@umituz/react-native-auth 3.4.30 → 3.4.32
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/README.md +426 -0
- package/package.json +1 -3
- package/src/application/README.md +513 -0
- package/src/domain/ConfigAndErrors.md +545 -0
- package/src/domain/README.md +293 -0
- package/src/domain/entities/AuthUser.md +377 -0
- package/src/domain/entities/UserProfile.md +443 -0
- package/src/infrastructure/README.md +576 -0
- package/src/infrastructure/services/README.md +417 -0
- package/src/presentation/README.md +770 -0
- package/src/presentation/components/AuthBackground.tsx +21 -0
- package/src/presentation/components/AuthContainer.tsx +3 -3
- package/src/presentation/components/LoginForm.md +222 -0
- package/src/presentation/components/PasswordIndicators.md +260 -0
- package/src/presentation/components/ProfileComponents.md +575 -0
- package/src/presentation/components/README.md +117 -0
- package/src/presentation/components/SocialLoginButtons.md +340 -0
- package/src/presentation/hooks/README.md +122 -0
- package/src/presentation/hooks/useAccountManagement.md +386 -0
- package/src/presentation/hooks/useAuth.md +255 -0
- package/src/presentation/hooks/useAuthBottomSheet.md +414 -0
- package/src/presentation/hooks/useAuthRequired.md +248 -0
- package/src/presentation/hooks/useProfileUpdate.md +327 -0
- package/src/presentation/hooks/useSocialLogin.md +356 -0
- package/src/presentation/hooks/useUserProfile.md +230 -0
- package/src/presentation/screens/README.md +198 -0
- package/src/presentation/components/AuthGradientBackground.tsx +0 -33
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
# Application Layer
|
|
2
|
+
|
|
3
|
+
React Native Auth package application layer. Defines ports and interfaces for authentication operations.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
application/
|
|
9
|
+
└── ports/
|
|
10
|
+
├── IAuthService.ts # Authentication service interface
|
|
11
|
+
└── IAuthProvider.ts # Auth provider interface
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
The application layer defines the contracts (interfaces) for authentication operations. It follows the Hexagonal Architecture (Ports and Adapters) pattern:
|
|
17
|
+
|
|
18
|
+
- **Ports**: Interfaces that define what the application can do
|
|
19
|
+
- **Adapters**: Implementations that connect to external services (Firebase, etc.)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# IAuthService
|
|
24
|
+
|
|
25
|
+
Authentication service interface defining all auth operations.
|
|
26
|
+
|
|
27
|
+
## Interface
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import type { IAuthService, SignUpParams, SignInParams } from '@umituz/react-native-auth';
|
|
31
|
+
|
|
32
|
+
interface IAuthService {
|
|
33
|
+
// Sign up new user
|
|
34
|
+
signUp(params: SignUpParams): Promise<AuthUser>;
|
|
35
|
+
|
|
36
|
+
// Sign in existing user
|
|
37
|
+
signIn(params: SignInParams): Promise<AuthUser>;
|
|
38
|
+
|
|
39
|
+
// Sign out current user
|
|
40
|
+
signOut(): Promise<void>;
|
|
41
|
+
|
|
42
|
+
// Get current user
|
|
43
|
+
getCurrentUser(): AuthUser | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface SignUpParams {
|
|
47
|
+
email: string;
|
|
48
|
+
password: string;
|
|
49
|
+
displayName?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface SignInParams {
|
|
53
|
+
email: string;
|
|
54
|
+
password: string;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Usage Example
|
|
59
|
+
|
|
60
|
+
### Implementing IAuthService
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { IAuthService, SignUpParams, SignInParams } from '@umituz/react-native-auth';
|
|
64
|
+
|
|
65
|
+
class FirebaseAuthService implements IAuthService {
|
|
66
|
+
constructor(private firebaseAuth: Auth) {}
|
|
67
|
+
|
|
68
|
+
async signUp(params: SignUpParams): Promise<AuthUser> {
|
|
69
|
+
const userCredential = await createUserWithEmailAndPassword(
|
|
70
|
+
this.firebaseAuth,
|
|
71
|
+
params.email,
|
|
72
|
+
params.password
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Update profile if displayName provided
|
|
76
|
+
if (params.displayName) {
|
|
77
|
+
await updateProfile(userCredential.user, {
|
|
78
|
+
displayName: params.displayName,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return this.mapToAuthUser(userCredential.user);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async signIn(params: SignInParams): Promise<AuthUser> {
|
|
86
|
+
const userCredential = await signInWithEmailAndPassword(
|
|
87
|
+
this.firebaseAuth,
|
|
88
|
+
params.email,
|
|
89
|
+
params.password
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return this.mapToAuthUser(userCredential.user);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async signOut(): Promise<void> {
|
|
96
|
+
await signOut(this.firebaseAuth);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getCurrentUser(): AuthUser | null {
|
|
100
|
+
const user = this.firebaseAuth.currentUser;
|
|
101
|
+
return user ? this.mapToAuthUser(user) : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private mapToAuthUser(firebaseUser: FirebaseUser): AuthUser {
|
|
105
|
+
return {
|
|
106
|
+
uid: firebaseUser.uid,
|
|
107
|
+
email: firebaseUser.email,
|
|
108
|
+
displayName: firebaseUser.displayName,
|
|
109
|
+
isAnonymous: firebaseUser.isAnonymous,
|
|
110
|
+
emailVerified: firebaseUser.emailVerified,
|
|
111
|
+
photoURL: firebaseUser.photoURL,
|
|
112
|
+
provider: firebaseUser.providerData[0]?.providerId || 'unknown',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Using IAuthService
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
function LoginComponent({ authService }: { authService: IAuthService }) {
|
|
122
|
+
const [email, setEmail] = useState('');
|
|
123
|
+
const [password, setPassword] = useState('');
|
|
124
|
+
|
|
125
|
+
const handleSignIn = async () => {
|
|
126
|
+
try {
|
|
127
|
+
const user = await authService.signIn({ email, password });
|
|
128
|
+
console.log('Signed in:', user.displayName);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('Sign in failed:', error);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<View>
|
|
136
|
+
<TextInput value={email} onChangeText={setEmail} placeholder="Email" />
|
|
137
|
+
<TextInput
|
|
138
|
+
value={password}
|
|
139
|
+
onChangeText={setPassword}
|
|
140
|
+
placeholder="Password"
|
|
141
|
+
secureTextEntry
|
|
142
|
+
/>
|
|
143
|
+
<Button onPress={handleSignIn}>Sign In</Button>
|
|
144
|
+
</View>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
# IAuthProvider
|
|
152
|
+
|
|
153
|
+
Auth provider interface for different authentication methods (Firebase, custom backend, etc.).
|
|
154
|
+
|
|
155
|
+
## Interface
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import type { IAuthProvider, AuthCredentials, SignUpCredentials, SocialSignInResult } from '@umituz/react-native-auth';
|
|
159
|
+
|
|
160
|
+
interface IAuthProvider {
|
|
161
|
+
// Sign up with email/password
|
|
162
|
+
signUp(credentials: SignUpCredentials): Promise<AuthUser>;
|
|
163
|
+
|
|
164
|
+
// Sign in with email/password
|
|
165
|
+
signIn(credentials: AuthCredentials): Promise<AuthUser>;
|
|
166
|
+
|
|
167
|
+
// Sign in with social provider
|
|
168
|
+
signInWithSocial(provider: 'google' | 'apple'): Promise<SocialSignInResult>;
|
|
169
|
+
|
|
170
|
+
// Sign out
|
|
171
|
+
signOut(): Promise<void>;
|
|
172
|
+
|
|
173
|
+
// Get current user
|
|
174
|
+
getCurrentUser(): AuthUser | null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
interface AuthCredentials {
|
|
178
|
+
email: string;
|
|
179
|
+
password: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
interface SignUpCredentials extends AuthCredentials {
|
|
183
|
+
displayName?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
interface SocialSignInResult {
|
|
187
|
+
success: boolean;
|
|
188
|
+
user?: AuthUser;
|
|
189
|
+
error?: string;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Usage Example
|
|
194
|
+
|
|
195
|
+
### Firebase Provider Implementation
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { IAuthProvider } from '@umituz/react-native-auth';
|
|
199
|
+
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
|
|
200
|
+
|
|
201
|
+
class FirebaseAuthProvider implements IAuthProvider {
|
|
202
|
+
constructor(private firebaseAuth: Auth) {}
|
|
203
|
+
|
|
204
|
+
async signUp(credentials: SignUpCredentials): Promise<AuthUser> {
|
|
205
|
+
const userCredential = await createUserWithEmailAndPassword(
|
|
206
|
+
this.firebaseAuth,
|
|
207
|
+
credentials.email,
|
|
208
|
+
credentials.password
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (credentials.displayName) {
|
|
212
|
+
await updateProfile(userCredential.user, {
|
|
213
|
+
displayName: credentials.displayName,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return this.mapUser(userCredential.user);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async signIn(credentials: AuthCredentials): Promise<AuthUser> {
|
|
221
|
+
const userCredential = await signInWithEmailAndPassword(
|
|
222
|
+
this.firebaseAuth,
|
|
223
|
+
credentials.email,
|
|
224
|
+
credentials.password
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
return this.mapUser(userCredential.user);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async signInWithSocial(provider: 'google' | 'apple'): Promise<SocialSignInResult> {
|
|
231
|
+
try {
|
|
232
|
+
let authProvider: GoogleAuthProvider | OAuthProvider;
|
|
233
|
+
|
|
234
|
+
if (provider === 'google') {
|
|
235
|
+
authProvider = new GoogleAuthProvider();
|
|
236
|
+
} else {
|
|
237
|
+
authProvider = new OAuthProvider('apple.com');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const result = await signInWithPopup(this.firebaseAuth, authProvider);
|
|
241
|
+
const user = this.mapUser(result.user);
|
|
242
|
+
|
|
243
|
+
return { success: true, user };
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
error: error instanceof Error ? error.message : 'Social sign in failed',
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async signOut(): Promise<void> {
|
|
253
|
+
await signOut(this.firebaseAuth);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
getCurrentUser(): AuthUser | null {
|
|
257
|
+
const user = this.firebaseAuth.currentUser;
|
|
258
|
+
return user ? this.mapUser(user) : null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private mapUser(firebaseUser: FirebaseUser): AuthUser {
|
|
262
|
+
return {
|
|
263
|
+
uid: firebaseUser.uid,
|
|
264
|
+
email: firebaseUser.email,
|
|
265
|
+
displayName: firebaseUser.displayName,
|
|
266
|
+
isAnonymous: firebaseUser.isAnonymous,
|
|
267
|
+
emailVerified: firebaseUser.emailVerified,
|
|
268
|
+
photoURL: firebaseUser.photoURL,
|
|
269
|
+
provider: firebaseUser.providerData[0]?.providerId || 'unknown',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Custom Backend Provider
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import { IAuthProvider } from '@umituz/react-native-auth';
|
|
279
|
+
|
|
280
|
+
class BackendAuthProvider implements IAuthProvider {
|
|
281
|
+
constructor(private apiBaseUrl: string) {}
|
|
282
|
+
|
|
283
|
+
async signUp(credentials: SignUpCredentials): Promise<AuthUser> {
|
|
284
|
+
const response = await fetch(`${this.apiBaseUrl}/auth/signup`, {
|
|
285
|
+
method: 'POST',
|
|
286
|
+
headers: { 'Content-Type': 'application/json' },
|
|
287
|
+
body: JSON.stringify(credentials),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (!response.ok) {
|
|
291
|
+
throw new Error('Sign up failed');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const data = await response.json();
|
|
295
|
+
return data.user; // Assuming API returns { user: AuthUser }
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async signIn(credentials: AuthCredentials): Promise<AuthUser> {
|
|
299
|
+
const response = await fetch(`${this.apiBaseUrl}/auth/signin`, {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: { 'Content-Type': 'application/json' },
|
|
302
|
+
body: JSON.stringify(credentials),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
throw new Error('Sign in failed');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const data = await response.json();
|
|
310
|
+
return data.user;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async signInWithSocial(provider: 'google' | 'apple'): Promise<SocialSignInResult> {
|
|
314
|
+
// Implement social sign-in with your backend
|
|
315
|
+
const response = await fetch(`${this.apiBaseUrl}/auth/social/${provider}`, {
|
|
316
|
+
method: 'POST',
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const data = await response.json();
|
|
320
|
+
return data;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async signOut(): Promise<void> {
|
|
324
|
+
await fetch(`${this.apiBaseUrl}/auth/signout`, { method: 'POST' });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
getCurrentUser(): AuthUser | null {
|
|
328
|
+
// Return cached user or fetch from backend
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
# Dependency Injection
|
|
337
|
+
|
|
338
|
+
Using interfaces allows for easy dependency injection and testing.
|
|
339
|
+
|
|
340
|
+
## Provider Pattern
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
import { IAuthProvider } from '@umituz/react-native-auth';
|
|
344
|
+
|
|
345
|
+
interface AuthProviderContextType {
|
|
346
|
+
authProvider: IAuthProvider;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const AuthProviderContext = createContext<AuthProviderContextType | null>(null);
|
|
350
|
+
|
|
351
|
+
export function AuthProvider({ children, provider }: { children: ReactNode; provider: IAuthProvider }) {
|
|
352
|
+
return (
|
|
353
|
+
<AuthProviderContext.Provider value={{ authProvider: provider }}>
|
|
354
|
+
{children}
|
|
355
|
+
</AuthProviderContext.Provider>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function useAuthProvider(): IAuthProvider {
|
|
360
|
+
const context = useContext(AuthProviderContext);
|
|
361
|
+
if (!context) {
|
|
362
|
+
throw new Error('useAuthProvider must be used within AuthProvider');
|
|
363
|
+
}
|
|
364
|
+
return context.authProvider;
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## App Configuration
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import { FirebaseAuthProvider } from './providers/FirebaseAuthProvider';
|
|
372
|
+
import { BackendAuthProvider } from './providers/BackendAuthProvider';
|
|
373
|
+
|
|
374
|
+
// Choose provider based on environment
|
|
375
|
+
const authProvider = __DEV__
|
|
376
|
+
? new BackendAuthProvider('https://dev-api.example.com')
|
|
377
|
+
: new FirebaseAuthProvider(getAuth());
|
|
378
|
+
|
|
379
|
+
function App() {
|
|
380
|
+
return (
|
|
381
|
+
<AuthProvider provider={authProvider}>
|
|
382
|
+
<AppNavigator />
|
|
383
|
+
</AuthProvider>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
# Testing with Mocks
|
|
391
|
+
|
|
392
|
+
## Mock Implementation
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { IAuthProvider } from '@umituz/react-native-auth';
|
|
396
|
+
|
|
397
|
+
class MockAuthProvider implements IAuthProvider {
|
|
398
|
+
private mockUser: AuthUser | null = null;
|
|
399
|
+
|
|
400
|
+
async signUp(credentials: SignUpCredentials): Promise<AuthUser> {
|
|
401
|
+
this.mockUser = {
|
|
402
|
+
uid: 'mock-123',
|
|
403
|
+
email: credentials.email,
|
|
404
|
+
displayName: credentials.displayName || 'Mock User',
|
|
405
|
+
isAnonymous: false,
|
|
406
|
+
emailVerified: false,
|
|
407
|
+
photoURL: null,
|
|
408
|
+
provider: 'password',
|
|
409
|
+
};
|
|
410
|
+
return this.mockUser;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async signIn(credentials: AuthCredentials): Promise<AuthUser> {
|
|
414
|
+
if (credentials.email === 'test@example.com' && credentials.password === 'password') {
|
|
415
|
+
this.mockUser = {
|
|
416
|
+
uid: 'mock-123',
|
|
417
|
+
email: credentials.email,
|
|
418
|
+
displayName: 'Test User',
|
|
419
|
+
isAnonymous: false,
|
|
420
|
+
emailVerified: true,
|
|
421
|
+
photoURL: null,
|
|
422
|
+
provider: 'password',
|
|
423
|
+
};
|
|
424
|
+
return this.mockUser;
|
|
425
|
+
}
|
|
426
|
+
throw new Error('Invalid credentials');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async signInWithSocial(provider: 'google' | 'apple'): Promise<SocialSignInResult> {
|
|
430
|
+
this.mockUser = {
|
|
431
|
+
uid: `mock-${provider}-123`,
|
|
432
|
+
email: `${provider}@example.com`,
|
|
433
|
+
displayName: `${provider} User`,
|
|
434
|
+
isAnonymous: false,
|
|
435
|
+
emailVerified: true,
|
|
436
|
+
photoURL: null,
|
|
437
|
+
provider: provider === 'google' ? 'google.com' : 'apple.com',
|
|
438
|
+
};
|
|
439
|
+
return { success: true, user: this.mockUser };
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async signOut(): Promise<void> {
|
|
443
|
+
this.mockUser = null;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
getCurrentUser(): AuthUser | null {
|
|
447
|
+
return this.mockUser;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Usage in tests
|
|
452
|
+
const mockProvider = new MockAuthProvider();
|
|
453
|
+
render(
|
|
454
|
+
<AuthProvider provider={mockProvider}>
|
|
455
|
+
<LoginComponent />
|
|
456
|
+
</AuthProvider>
|
|
457
|
+
);
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
# Best Practices
|
|
463
|
+
|
|
464
|
+
## 1. Always Use Interfaces
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
// ✅ Good
|
|
468
|
+
function authenticateUser(authProvider: IAuthProvider, credentials: AuthCredentials) {
|
|
469
|
+
return authProvider.signIn(credentials);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ❌ Bad - couples to Firebase
|
|
473
|
+
function authenticateUser(auth: Auth, credentials: AuthCredentials) {
|
|
474
|
+
return signInWithEmailAndPassword(auth, credentials.email, credentials.password);
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## 2. Dependency Injection
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
// ✅ Good - injectable
|
|
482
|
+
class UserService {
|
|
483
|
+
constructor(private authProvider: IAuthProvider) {}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// ❌ Bad - tight coupling
|
|
487
|
+
import { getAuth } from 'firebase/auth';
|
|
488
|
+
class UserService {
|
|
489
|
+
private auth = getAuth();
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## 3. Error Handling
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
// ✅ Good - abstract errors
|
|
497
|
+
async function signUp(authProvider: IAuthProvider, params: SignUpParams) {
|
|
498
|
+
try {
|
|
499
|
+
return await authProvider.signUp(params);
|
|
500
|
+
} catch (error) {
|
|
501
|
+
if (error instanceof AuthEmailAlreadyInUseError) {
|
|
502
|
+
// Handle specific error
|
|
503
|
+
}
|
|
504
|
+
throw error;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Related Modules
|
|
510
|
+
|
|
511
|
+
- **[Domain](../domain/README.md)** - Domain entities and value objects
|
|
512
|
+
- **[Infrastructure](../infrastructure/README.md)** - Infrastructure implementations
|
|
513
|
+
- **[Presentation](../presentation/README.md)** - UI components and hooks
|