@umituz/web-firebase 1.0.5 → 2.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 (69) hide show
  1. package/README.md +555 -0
  2. package/dist/application/index.d.mts +273 -0
  3. package/dist/application/index.d.ts +273 -0
  4. package/dist/application/index.js +490 -0
  5. package/dist/application/index.mjs +19 -0
  6. package/dist/chunk-34DL2QWQ.mjs +87 -0
  7. package/dist/chunk-4FP2ELQ5.mjs +96 -0
  8. package/dist/chunk-7TX3OU3O.mjs +721 -0
  9. package/dist/chunk-I6WGBPFB.mjs +439 -0
  10. package/dist/chunk-RZ4QR6TB.mjs +96 -0
  11. package/dist/chunk-U2XI4MGO.mjs +397 -0
  12. package/dist/domain/index.d.mts +325 -0
  13. package/dist/domain/index.d.ts +325 -0
  14. package/dist/domain/index.js +662 -0
  15. package/dist/domain/index.mjs +36 -0
  16. package/dist/file.repository.interface-v5vHgVsZ.d.mts +241 -0
  17. package/dist/file.repository.interface-v5vHgVsZ.d.ts +241 -0
  18. package/dist/firebase.entity-xvfEPjXZ.d.mts +15 -0
  19. package/dist/firebase.entity-xvfEPjXZ.d.ts +15 -0
  20. package/dist/index.d.mts +14 -96
  21. package/dist/index.d.ts +14 -96
  22. package/dist/index.js +1717 -78
  23. package/dist/index.mjs +88 -175
  24. package/dist/infrastructure/index.d.mts +170 -0
  25. package/dist/infrastructure/index.d.ts +170 -0
  26. package/dist/infrastructure/index.js +856 -0
  27. package/dist/infrastructure/index.mjs +46 -0
  28. package/dist/presentation/index.d.mts +25 -0
  29. package/dist/presentation/index.d.ts +25 -0
  30. package/dist/presentation/index.js +105 -0
  31. package/dist/presentation/index.mjs +6 -0
  32. package/dist/user.repository.interface-DS74TsJ5.d.mts +298 -0
  33. package/dist/user.repository.interface-DS74TsJ5.d.ts +298 -0
  34. package/package.json +37 -11
  35. package/src/application/dto/auth.dto.ts +69 -0
  36. package/src/application/dto/index.ts +7 -0
  37. package/src/application/dto/user.dto.ts +64 -0
  38. package/src/application/index.ts +7 -0
  39. package/src/application/use-cases/auth/reset-password.use-case.ts +66 -0
  40. package/src/application/use-cases/auth/sign-in-with-google.use-case.ts +86 -0
  41. package/src/application/use-cases/auth/sign-in.use-case.ts +77 -0
  42. package/src/application/use-cases/auth/sign-out.use-case.ts +22 -0
  43. package/src/application/use-cases/auth/sign-up.use-case.ts +99 -0
  44. package/src/application/use-cases/index.ts +12 -0
  45. package/src/application/use-cases/user/delete-account.use-case.ts +77 -0
  46. package/src/application/use-cases/user/update-profile.use-case.ts +98 -0
  47. package/src/domain/entities/file.entity.ts +151 -0
  48. package/src/domain/entities/timestamp.entity.ts +116 -0
  49. package/src/domain/entities/user.entity.ts +193 -0
  50. package/src/domain/errors/auth.errors.ts +115 -0
  51. package/src/domain/errors/repository.errors.ts +121 -0
  52. package/src/domain/index.ts +25 -2
  53. package/src/domain/interfaces/auth.repository.interface.ts +83 -0
  54. package/src/domain/interfaces/file.repository.interface.ts +143 -0
  55. package/src/domain/interfaces/user.repository.interface.ts +75 -0
  56. package/src/domain/value-objects/email.vo.ts +105 -0
  57. package/src/domain/value-objects/file-path.vo.ts +184 -0
  58. package/src/domain/value-objects/user-id.vo.ts +87 -0
  59. package/src/index.ts +19 -4
  60. package/src/infrastructure/firebase/auth.adapter.ts +220 -0
  61. package/src/infrastructure/firebase/client.ts +141 -0
  62. package/src/infrastructure/firebase/firestore.adapter.ts +190 -0
  63. package/src/infrastructure/firebase/storage.adapter.ts +323 -0
  64. package/src/infrastructure/index.ts +10 -5
  65. package/src/infrastructure/utils/storage.util.ts +3 -3
  66. package/src/presentation/hooks/useAuth.ts +153 -0
  67. package/src/presentation/hooks/useFirestore.ts +125 -0
  68. package/src/presentation/hooks/useStorage.ts +141 -0
  69. package/src/presentation/providers/FirebaseProvider.tsx +40 -0
@@ -0,0 +1,298 @@
1
+ import { UserCredential, User as User$1 } from 'firebase/auth';
2
+ import { QueryConstraint } from 'firebase/firestore';
3
+
4
+ /**
5
+ * User Domain Entities
6
+ * @description Core user-related entities following DDD principles
7
+ * Migrated from: /Users/umituz/Desktop/github/umituz/apps/web/app-growth-factory/src/domains/firebase/types/index.ts
8
+ */
9
+ /**
10
+ * User Profile Value Object
11
+ * Immutable user profile data
12
+ */
13
+ interface UserProfile {
14
+ readonly id: string;
15
+ readonly email: string;
16
+ readonly displayName: string;
17
+ readonly photoURL?: string;
18
+ readonly phoneNumber?: string;
19
+ readonly createdAt: number;
20
+ readonly updatedAt: number;
21
+ readonly lastLoginAt: number;
22
+ readonly emailVerified: boolean;
23
+ }
24
+ /**
25
+ * User Notifications Settings
26
+ */
27
+ interface UserNotifications {
28
+ email: boolean;
29
+ push: boolean;
30
+ marketing: boolean;
31
+ security: boolean;
32
+ weeklyDigest: boolean;
33
+ }
34
+ /**
35
+ * User Privacy Settings
36
+ */
37
+ interface UserPrivacy {
38
+ profileVisibility: 'public' | 'private';
39
+ showEmail: boolean;
40
+ showPhone: boolean;
41
+ dataSharing: boolean;
42
+ }
43
+ /**
44
+ * User Settings Value Object
45
+ */
46
+ interface UserSettings {
47
+ theme: 'light' | 'dark' | 'system';
48
+ language: string;
49
+ timezone: string;
50
+ currency: string;
51
+ notifications: UserNotifications;
52
+ privacy: UserPrivacy;
53
+ }
54
+ /**
55
+ * User Subscription Value Object
56
+ */
57
+ interface UserSubscription {
58
+ plan: 'free' | 'standard' | 'professional' | 'business';
59
+ status: 'active' | 'inactive' | 'canceled' | 'past_due';
60
+ polarCustomerId?: string;
61
+ polarSubscriptionId?: string;
62
+ currentPeriodStart?: number;
63
+ currentPeriodEnd?: number;
64
+ cancelAtPeriodEnd: boolean;
65
+ readonly createdAt: number;
66
+ readonly updatedAt: number;
67
+ }
68
+ /**
69
+ * Account Metrics Value Object
70
+ */
71
+ interface AccountMetrics {
72
+ followers: number;
73
+ following: number;
74
+ posts: number;
75
+ engagement: number;
76
+ lastSyncedAt: number;
77
+ }
78
+ /**
79
+ * User Connected Account Entity
80
+ */
81
+ interface UserConnectedAccount {
82
+ platform: 'twitter' | 'facebook' | 'instagram' | 'linkedin' | 'tiktok' | 'youtube' | 'pinterest' | 'reddit' | 'threads' | 'discord' | 'telegram' | 'mastodon' | 'medium';
83
+ connected: boolean;
84
+ readonly connectedAt: number;
85
+ username?: string;
86
+ profileId?: string;
87
+ accessToken?: string;
88
+ refreshToken?: string;
89
+ tokenExpiresAt?: number;
90
+ metrics?: AccountMetrics;
91
+ }
92
+ /**
93
+ * User Content Entity
94
+ */
95
+ interface UserContent {
96
+ readonly id: string;
97
+ type: 'post' | 'article' | 'video' | 'image' | 'story' | 'reel';
98
+ title: string;
99
+ content: string;
100
+ platforms: string[];
101
+ status: 'draft' | 'scheduled' | 'published' | 'failed';
102
+ scheduledFor?: number;
103
+ publishedAt?: number;
104
+ readonly createdAt: number;
105
+ readonly updatedAt: number;
106
+ mediaUrls?: string[];
107
+ metadata?: Record<string, unknown>;
108
+ }
109
+ /**
110
+ * Platform Breakdown for Analytics
111
+ */
112
+ interface PlatformBreakdown {
113
+ [platform: string]: {
114
+ posts: number;
115
+ engagement: number;
116
+ reach: number;
117
+ };
118
+ }
119
+ /**
120
+ * User Analytics Value Object
121
+ */
122
+ interface UserAnalytics {
123
+ totalPosts: number;
124
+ totalEngagement: number;
125
+ totalReach: number;
126
+ topPerformingPosts: string[];
127
+ platformBreakdown: PlatformBreakdown;
128
+ lastCalculatedAt: number;
129
+ }
130
+ /**
131
+ * User Credits Value Object
132
+ */
133
+ interface UserCredits {
134
+ standard: number;
135
+ professional: number;
136
+ total: number;
137
+ resetAt: number;
138
+ lastResetAt: number;
139
+ }
140
+ /**
141
+ * User Aggregate Root
142
+ * Main user document containing all user-related data
143
+ * This is the aggregate root for the User domain
144
+ */
145
+ interface User {
146
+ readonly profile: UserProfile;
147
+ settings: UserSettings;
148
+ subscription: UserSubscription;
149
+ connectedAccounts: UserConnectedAccount[];
150
+ content: UserContent[];
151
+ analytics: UserAnalytics;
152
+ credits: UserCredits;
153
+ }
154
+ /**
155
+ * Collection Constants
156
+ */
157
+ declare const USER_COLLECTIONS: {
158
+ readonly USERS: "users";
159
+ readonly CONTENT: "content";
160
+ readonly ANALYTICS: "analytics";
161
+ readonly CONNECTED_ACCOUNTS: "connectedAccounts";
162
+ };
163
+ declare const USER_SUBCOLLECTIONS: {
164
+ readonly CONTENT: "content";
165
+ readonly ANALYTICS: "analytics";
166
+ readonly SCHEDULED: "scheduled";
167
+ readonly PUBLISHED: "published";
168
+ readonly DRAFTS: "drafts";
169
+ };
170
+
171
+ /**
172
+ * Authentication Repository Interface
173
+ * @description Defines contract for authentication operations
174
+ */
175
+
176
+ /**
177
+ * Authentication Repository Interface
178
+ * Defines operations for user authentication and management
179
+ */
180
+ interface IAuthRepository {
181
+ /**
182
+ * Sign in with email and password
183
+ */
184
+ signIn(email: string, password: string): Promise<UserCredential>;
185
+ /**
186
+ * Sign up with email, password, and display name
187
+ */
188
+ signUp(email: string, password: string, displayName: string): Promise<UserCredential>;
189
+ /**
190
+ * Sign in with Google OAuth
191
+ */
192
+ signInWithGoogle(): Promise<UserCredential>;
193
+ /**
194
+ * Sign out current user
195
+ */
196
+ signOut(): Promise<void>;
197
+ /**
198
+ * Send password reset email
199
+ */
200
+ sendPasswordReset(email: string): Promise<void>;
201
+ /**
202
+ * Resend email verification
203
+ */
204
+ resendEmailVerification(): Promise<void>;
205
+ /**
206
+ * Update user profile (displayName, photoURL)
207
+ */
208
+ updateProfile(updates: Partial<Pick<User['profile'], 'displayName' | 'photoURL'>>): Promise<void>;
209
+ /**
210
+ * Update user email (requires password)
211
+ */
212
+ updateEmail(newEmail: string, password: string): Promise<void>;
213
+ /**
214
+ * Update user password (requires current password)
215
+ */
216
+ updatePassword(currentPassword: string, newPassword: string): Promise<void>;
217
+ /**
218
+ * Delete user account (requires password)
219
+ */
220
+ deleteAccount(password: string): Promise<void>;
221
+ /**
222
+ * Get current authenticated user
223
+ */
224
+ getCurrentUser(): User$1 | null;
225
+ /**
226
+ * Subscribe to auth state changes
227
+ */
228
+ onAuthStateChanged(callback: (user: User$1 | null) => void): () => void;
229
+ /**
230
+ * Create user document in Firestore
231
+ */
232
+ createUserDocument(userId: string, data: Partial<Omit<User, 'profile'>> & {
233
+ email: string;
234
+ displayName: string;
235
+ }): Promise<void>;
236
+ /**
237
+ * Update last login timestamp
238
+ */
239
+ updateLastLogin(userId: string): Promise<void>;
240
+ }
241
+
242
+ /**
243
+ * User Repository Interface
244
+ * @description Defines contract for user data operations
245
+ */
246
+
247
+ /**
248
+ * User Repository Interface
249
+ * Defines operations for user data management
250
+ */
251
+ interface IUserRepository {
252
+ /**
253
+ * Get user by ID
254
+ */
255
+ getUser(userId: string): Promise<User | null>;
256
+ /**
257
+ * Get user by email
258
+ */
259
+ getUserByEmail(email: string): Promise<User | null>;
260
+ /**
261
+ * Create user
262
+ */
263
+ createUser(userId: string, data: Partial<User>): Promise<void>;
264
+ /**
265
+ * Update user
266
+ */
267
+ updateUser(userId: string, data: Partial<User>): Promise<void>;
268
+ /**
269
+ * Delete user
270
+ */
271
+ deleteUser(userId: string): Promise<void>;
272
+ /**
273
+ * Update user profile
274
+ */
275
+ updateProfile(userId: string, updates: Partial<Pick<User['profile'], 'displayName' | 'photoURL' | 'phoneNumber'>>): Promise<void>;
276
+ /**
277
+ * Update user settings
278
+ */
279
+ updateSettings(userId: string, settings: Partial<User['settings']>): Promise<void>;
280
+ /**
281
+ * Update user subscription
282
+ */
283
+ updateSubscription(userId: string, subscription: Partial<User['subscription']>): Promise<void>;
284
+ /**
285
+ * Update last login timestamp
286
+ */
287
+ updateLastLogin(userId: string): Promise<void>;
288
+ /**
289
+ * Query users with constraints
290
+ */
291
+ queryUsers(constraints: QueryConstraint[]): Promise<User[]>;
292
+ /**
293
+ * Subscribe to user document changes
294
+ */
295
+ subscribeToUser(userId: string, callback: (user: User | null) => void, onError?: (error: Error) => void): () => void;
296
+ }
297
+
298
+ export { type AccountMetrics as A, type IAuthRepository as I, type PlatformBreakdown as P, USER_COLLECTIONS as U, type IUserRepository as a, USER_SUBCOLLECTIONS as b, type User as c, type UserAnalytics as d, type UserConnectedAccount as e, type UserContent as f, type UserCredits as g, type UserNotifications as h, type UserPrivacy as i, type UserProfile as j, type UserSettings as k, type UserSubscription as l };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@umituz/web-firebase",
3
- "version": "1.0.5",
4
- "description": "Universal Firebase utilities for web applications",
3
+ "version": "2.0.1",
4
+ "description": "Comprehensive Firebase integration with DDD architecture for web applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -15,17 +15,34 @@
15
15
  "import": "./dist/index.mjs",
16
16
  "require": "./dist/index.js"
17
17
  },
18
- "./domain": "./src/domain/index.ts",
19
- "./infrastructure": "./src/infrastructure/index.ts",
20
- "./presentation": "./src/presentation/index.ts"
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
+ }
21
38
  },
22
39
  "files": [
23
40
  "dist",
24
41
  "src"
25
42
  ],
26
43
  "scripts": {
27
- "build": "tsup src/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",
28
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch --external react --external firebase",
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",
29
46
  "lint": "tsc --noEmit"
30
47
  },
31
48
  "keywords": [
@@ -33,18 +50,27 @@
33
50
  "firestore",
34
51
  "auth",
35
52
  "storage",
53
+ "functions",
54
+ "analytics",
36
55
  "react",
37
- "web"
56
+ "web",
57
+ "typescript",
58
+ "ddd",
59
+ "domain-driven-design",
60
+ "clean-architecture",
61
+ "repository-pattern",
62
+ "use-cases",
63
+ "hexagonal-architecture"
38
64
  ],
39
- "author": "Antigravity",
65
+ "author": "umituz",
40
66
  "license": "MIT",
41
67
  "peerDependencies": {
42
- "firebase": ">=10",
68
+ "firebase": ">=12",
43
69
  "react": ">=18"
44
70
  },
45
71
  "devDependencies": {
46
72
  "@types/react": "^18.2.0",
47
- "firebase": "^11.0.0",
73
+ "firebase": "^12.11.0",
48
74
  "tsup": "^8.0.0",
49
75
  "typescript": "^5.0.0"
50
76
  }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Authentication DTOs
3
+ * @description Data Transfer Objects for authentication operations
4
+ */
5
+
6
+ import type { UserCredential } from 'firebase/auth'
7
+
8
+ /**
9
+ * Sign In DTO
10
+ */
11
+ export interface SignInDTO {
12
+ email: string
13
+ password: string
14
+ }
15
+
16
+ /**
17
+ * Sign Up DTO
18
+ */
19
+ export interface SignUpDTO {
20
+ email: string
21
+ password: string
22
+ displayName: string
23
+ }
24
+
25
+ /**
26
+ * Sign Up Result
27
+ */
28
+ export interface SignUpResult extends UserCredential {
29
+ userId: string
30
+ emailVerified: boolean
31
+ }
32
+
33
+ /**
34
+ * Reset Password DTO
35
+ */
36
+ export interface ResetPasswordDTO {
37
+ email: string
38
+ }
39
+
40
+ /**
41
+ * Update Profile DTO
42
+ */
43
+ export interface UpdateProfileDTO {
44
+ displayName?: string
45
+ photoURL?: string
46
+ }
47
+
48
+ /**
49
+ * Update Email DTO
50
+ */
51
+ export interface UpdateEmailDTO {
52
+ newEmail: string
53
+ currentPassword: string
54
+ }
55
+
56
+ /**
57
+ * Update Password DTO
58
+ */
59
+ export interface UpdatePasswordDTO {
60
+ currentPassword: string
61
+ newPassword: string
62
+ }
63
+
64
+ /**
65
+ * Delete Account DTO
66
+ */
67
+ export interface DeleteAccountDTO {
68
+ password: string
69
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Application Layer DTOs
3
+ * @description Data Transfer Objects for use cases
4
+ */
5
+
6
+ export * from './auth.dto'
7
+ export * from './user.dto'
@@ -0,0 +1,64 @@
1
+ /**
2
+ * User DTOs
3
+ * @description Data Transfer Objects for user operations
4
+ */
5
+
6
+ import type { User } from '../../domain/entities/user.entity'
7
+
8
+ /**
9
+ * Create User DTO
10
+ */
11
+ export interface CreateUserDTO {
12
+ id: string
13
+ email: string
14
+ displayName: string
15
+ photoURL?: string
16
+ phoneNumber?: string
17
+ emailVerified: boolean
18
+ }
19
+
20
+ /**
21
+ * Update User DTO
22
+ */
23
+ export interface UpdateUserDTO {
24
+ displayName?: string
25
+ photoURL?: string
26
+ phoneNumber?: string
27
+ theme?: 'light' | 'dark' | 'system'
28
+ language?: string
29
+ timezone?: string
30
+ }
31
+
32
+ /**
33
+ * Update User Settings DTO
34
+ */
35
+ export interface UpdateUserSettingsDTO {
36
+ theme?: 'light' | 'dark' | 'system'
37
+ language?: string
38
+ timezone?: string
39
+ currency?: string
40
+ notifications?: Partial<User['settings']['notifications']>
41
+ privacy?: Partial<User['settings']['privacy']>
42
+ }
43
+
44
+ /**
45
+ * Update User Subscription DTO
46
+ */
47
+ export interface UpdateUserSubscriptionDTO {
48
+ plan?: User['subscription']['plan']
49
+ status?: User['subscription']['status']
50
+ polarCustomerId?: string
51
+ polarSubscriptionId?: string
52
+ currentPeriodStart?: number
53
+ currentPeriodEnd?: number
54
+ cancelAtPeriodEnd?: boolean
55
+ }
56
+
57
+ /**
58
+ * User Query Result
59
+ */
60
+ export interface UserQueryResult {
61
+ user: User | null
62
+ lastLoginAt?: number
63
+ createdAt?: number
64
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Application Layer Public API
3
+ * @description Exports all use cases and DTOs
4
+ */
5
+
6
+ export * from './dto'
7
+ export * from './use-cases'
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Reset Password Use Case
3
+ * @description Handles password reset flow
4
+ */
5
+
6
+ import type { IAuthRepository } from '../../../domain/interfaces/auth.repository.interface'
7
+ import type { ResetPasswordDTO } from '../../dto/auth.dto'
8
+ import { createAuthError, AuthErrorCode } from '../../../domain/errors/auth.errors'
9
+
10
+ export class ResetPasswordUseCase {
11
+ constructor(private readonly authRepository: IAuthRepository) {}
12
+
13
+ /**
14
+ * Execute password reset use case
15
+ */
16
+ async execute(dto: ResetPasswordDTO): Promise<void> {
17
+ try {
18
+ // Validate input
19
+ this.validateDTO(dto)
20
+
21
+ // Send password reset email
22
+ await this.authRepository.sendPasswordReset(dto.email)
23
+ } catch (error) {
24
+ throw this.handleError(error)
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Validate DTO
30
+ */
31
+ private validateDTO(dto: ResetPasswordDTO): void {
32
+ if (!dto.email) {
33
+ throw createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Email is required')
34
+ }
35
+
36
+ // Email validation
37
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
38
+ if (!emailRegex.test(dto.email)) {
39
+ throw createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Invalid email format')
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Handle errors
45
+ */
46
+ private handleError(error: unknown): Error {
47
+ if (error instanceof Error && 'code' in error) {
48
+ const code = (error as { code: string }).code
49
+
50
+ if (code === 'auth/invalid-email') {
51
+ return createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Invalid email', error)
52
+ }
53
+
54
+ if (code === 'auth/user-not-found') {
55
+ // Don't reveal if user exists
56
+ return createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Password reset email sent if account exists')
57
+ }
58
+
59
+ if (code === 'auth/too-many-requests') {
60
+ return createAuthError(AuthErrorCode.TOO_MANY_REQUESTS, 'Too many requests', error)
61
+ }
62
+ }
63
+
64
+ return createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Password reset failed', error)
65
+ }
66
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Sign In With Google Use Case
3
+ * @description Handles Google OAuth sign in/sign up
4
+ */
5
+
6
+ import type { UserCredential } from 'firebase/auth'
7
+ import type { IAuthRepository } from '../../../domain/interfaces/auth.repository.interface'
8
+ import type { IUserRepository } from '../../../domain/interfaces/user.repository.interface'
9
+ import type { CreateUserDTO } from '../../dto/user.dto'
10
+ import { createAuthError, AuthErrorCode } from '../../../domain/errors/auth.errors'
11
+
12
+ export class SignInWithGoogleUseCase {
13
+ constructor(
14
+ private readonly authRepository: IAuthRepository,
15
+ private readonly userRepository: IUserRepository
16
+ ) {}
17
+
18
+ /**
19
+ * Execute Google sign in use case
20
+ */
21
+ async execute(): Promise<UserCredential> {
22
+ try {
23
+ // Perform Google sign in
24
+ const result = await this.authRepository.signInWithGoogle()
25
+
26
+ // Check if user needs onboarding
27
+ const existingUser = await this.userRepository.getUser(result.user.uid)
28
+ if (!existingUser) {
29
+ // New user - create user document
30
+ const createUserDTO: CreateUserDTO = {
31
+ id: result.user.uid,
32
+ email: result.user.email || '',
33
+ displayName: result.user.displayName || '',
34
+ photoURL: result.user.photoURL || undefined,
35
+ phoneNumber: result.user.phoneNumber || undefined,
36
+ emailVerified: result.user.emailVerified,
37
+ }
38
+
39
+ await this.authRepository.createUserDocument(result.user.uid, createUserDTO)
40
+ }
41
+
42
+ return result
43
+ } catch (error) {
44
+ throw this.handleError(error)
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Handle errors
50
+ */
51
+ private handleError(error: unknown): Error {
52
+ if (error instanceof Error && 'code' in error) {
53
+ const code = (error as { code: string }).code
54
+
55
+ if (code === 'auth/popup-closed-by-user') {
56
+ return createAuthError(AuthErrorCode.OAUTH_CANCELLED, 'Google sign in cancelled', error)
57
+ }
58
+
59
+ if (code === 'auth/popup-blocked') {
60
+ return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Popup blocked by browser', error)
61
+ }
62
+
63
+ if (code === 'auth/account-exists-with-different-credential') {
64
+ return createAuthError(
65
+ AuthErrorCode.OAUTH_ACCOUNT_EXISTS,
66
+ 'Account already exists with different credential',
67
+ error
68
+ )
69
+ }
70
+
71
+ if (code === 'auth/auth-domain-policy-required') {
72
+ return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Auth domain not configured', error)
73
+ }
74
+
75
+ if (code === 'auth/unauthorized-domain') {
76
+ return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Unauthorized domain', error)
77
+ }
78
+
79
+ if (code === 'auth/cancelled-popup-request') {
80
+ return createAuthError(AuthErrorCode.OAUTH_CANCELLED, 'Sign in cancelled', error)
81
+ }
82
+ }
83
+
84
+ return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Google sign in failed', error)
85
+ }
86
+ }