@umituz/react-native-auth 3.4.29 → 3.4.31

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.
@@ -0,0 +1,293 @@
1
+ # Domain Layer
2
+
3
+ React Native Auth paketinin domain katmanı. İş kurallarını, domain entity'lerini ve value object'leri içerir.
4
+
5
+ ## Yapı
6
+
7
+ ```
8
+ domain/
9
+ ├── entities/
10
+ │ ├── AuthUser.ts # Authentication kullanıcısı
11
+ │ └── UserProfile.ts # Kullanıcı profili
12
+ ├── value-objects/
13
+ │ └── AuthConfig.ts # Auth konfigürasyonu
14
+ ├── errors/
15
+ │ └── AuthError.ts # Auth hataları
16
+ └── utils/
17
+ ├── anonymousNameGenerator.ts # Anonymous isim oluşturucu
18
+ └── migration.ts # Veri taşıma
19
+ ```
20
+
21
+ ## Entities
22
+
23
+ ### AuthUser
24
+
25
+ Authentication işlemleri için kullanıcı entity'si.
26
+
27
+ ```typescript
28
+ import type { AuthUser } from '@umituz/react-native-auth';
29
+
30
+ const user: AuthUser = {
31
+ uid: 'user-123',
32
+ email: 'user@example.com',
33
+ displayName: 'John Doe',
34
+ photoURL: 'https://...',
35
+ isAnonymous: false,
36
+ provider: 'google',
37
+ emailVerified: true,
38
+ };
39
+ ```
40
+
41
+ **Özellikler:**
42
+ - Firebase User ile uyumlu
43
+ - Provider bilgisi
44
+ - Anonymous kontrolü
45
+ - Email verification durumu
46
+
47
+ **Kullanım:**
48
+ ```typescript
49
+ function getUserDisplayName(user: AuthUser): string {
50
+ return user.displayName || user.email || 'Anonymous';
51
+ }
52
+
53
+ function isSocialLogin(user: AuthUser): boolean {
54
+ return user.provider === 'google' || user.provider === 'apple';
55
+ }
56
+ ```
57
+
58
+ ### UserProfile
59
+
60
+ Kullanıcı profili entity'si.
61
+
62
+ ```typescript
63
+ import type { UserProfile } from '@umituz/react-native-auth';
64
+
65
+ const profile: UserProfile = {
66
+ displayName: 'John Doe',
67
+ photoURL: 'https://...',
68
+ bio: 'Software developer',
69
+ location: 'Istanbul',
70
+ website: 'https://johndoe.com',
71
+ };
72
+ ```
73
+
74
+ **Güncelleme:**
75
+ ```typescript
76
+ async function updateProfile(userId: string, updates: UpdateProfileParams) {
77
+ await updateDoc(doc(db, 'users', userId), updates);
78
+ }
79
+ ```
80
+
81
+ ## Value Objects
82
+
83
+ ### AuthConfig
84
+
85
+ Authentication konfigürasyonu.
86
+
87
+ ```typescript
88
+ import { AuthConfig, DEFAULT_AUTH_CONFIG } from '@umituz/react-native-auth';
89
+
90
+ const config: AuthConfig = {
91
+ password: {
92
+ minLength: 8,
93
+ requireUppercase: true,
94
+ requireLowercase: true,
95
+ requireNumbers: true,
96
+ requireSpecialChars: true,
97
+ },
98
+ social: {
99
+ google: {
100
+ iosClientId: '...',
101
+ webClientId: '...',
102
+ },
103
+ apple: {
104
+ enabled: true,
105
+ },
106
+ },
107
+ };
108
+ ```
109
+
110
+ ## Errors
111
+
112
+ ### AuthError Hierarchy
113
+
114
+ ```typescript
115
+ import {
116
+ AuthError,
117
+ AuthInitializationError,
118
+ AuthConfigurationError,
119
+ AuthValidationError,
120
+ AuthNetworkError,
121
+ AuthUserNotFoundError,
122
+ AuthWrongPasswordError,
123
+ AuthEmailAlreadyInUseError,
124
+ AuthWeakPasswordError,
125
+ AuthInvalidEmailError,
126
+ } from '@umituz/react-native-auth';
127
+ ```
128
+
129
+ **Hata Yakalama:**
130
+ ```typescript
131
+ try {
132
+ await signIn({ email, password });
133
+ } catch (error) {
134
+ if (error instanceof AuthUserNotFoundError) {
135
+ // Kullanıcı bulunamadı
136
+ } else if (error instanceof AuthWrongPasswordError) {
137
+ // Şifre hatalı
138
+ } else if (error instanceof AuthNetworkError) {
139
+ // Network hatası
140
+ }
141
+ }
142
+ ```
143
+
144
+ ## Utils
145
+
146
+ ### Anonymous Name Generator
147
+
148
+ Anonymous kullanıcılar için rastgele isim oluşturur.
149
+
150
+ ```typescript
151
+ import {
152
+ generateAnonymousName,
153
+ getAnonymousDisplayName
154
+ } from '@umituz/react-native-auth';
155
+
156
+ const name1 = generateAnonymousName('user-123');
157
+ // "User_Witty_Badger_1234"
158
+
159
+ const name2 = generateAnonymousName('user-456', {
160
+ prefix: 'Guest',
161
+ adjectiveCount: 1,
162
+ nounCount: 1,
163
+ });
164
+ // "Guest_Clever_Fox_456"
165
+
166
+ const displayName = getAnonymousDisplayName('user-123');
167
+ // "Witty Badger"
168
+ ```
169
+
170
+ **Konfigürasyon:**
171
+ ```typescript
172
+ interface AnonymousNameConfig {
173
+ prefix?: string; // Varsayılan: 'User'
174
+ adjectiveCount?: number; // Varsayılan: 2
175
+ nounCount?: number; // Varsayılan: 1
176
+ showNumbers?: boolean; // Varsayılan: true
177
+ }
178
+ ```
179
+
180
+ ### Migration
181
+
182
+ Kullanıcı verilerini taşımak için utility.
183
+
184
+ ```typescript
185
+ import { migrateUserData, configureMigration } from '@umituz/react-native-auth';
186
+
187
+ // Migration konfigürasyonu
188
+ configureMigration({
189
+ from: 'legacy_users',
190
+ to: 'users',
191
+ transform: (legacyData) => ({
192
+ displayName: legacyData.name,
193
+ email: legacyData.email_address,
194
+ createdAt: legacyData.joined_at,
195
+ }),
196
+ });
197
+
198
+ // Migration çalıştırma
199
+ await migrateUserData(userId);
200
+ ```
201
+
202
+ **Tam Örnek:**
203
+ ```typescript
204
+ async function migrateLegacyUser(userId: string) {
205
+ configureMigration({
206
+ from: 'old_users_collection',
207
+ to: 'users',
208
+ transform: (old) => ({
209
+ displayName: old.full_name,
210
+ email: old.email_addr,
211
+ photoURL: old.profile_pic,
212
+ createdAt: old.created_at,
213
+ metadata: {
214
+ migratedFrom: 'legacy',
215
+ migratedAt: Date.now(),
216
+ },
217
+ }),
218
+ });
219
+
220
+ try {
221
+ await migrateUserData(userId);
222
+ console.log('Migration successful');
223
+ } catch (error) {
224
+ console.error('Migration failed:', error);
225
+ }
226
+ }
227
+ ```
228
+
229
+ ## Type Guards
230
+
231
+ Domain type'ları için guard'lar:
232
+
233
+ ```typescript
234
+ function isAuthenticatedUser(user: AuthUser | null): user is AuthUser {
235
+ return user !== null && !user.isAnonymous;
236
+ }
237
+
238
+ function isEmailVerified(user: AuthUser): boolean {
239
+ return !!user.emailVerified;
240
+ }
241
+
242
+ function hasProvider(user: AuthUser, provider: string): boolean {
243
+ return user.provider === provider;
244
+ }
245
+ ```
246
+
247
+ ## Domain Services
248
+
249
+ ### User Validation
250
+
251
+ ```typescript
252
+ function validateUserForRegistration(user: Partial<AuthUser>): ValidationResult {
253
+ const errors: string[] = [];
254
+
255
+ if (!user.email) {
256
+ errors.push('Email is required');
257
+ }
258
+
259
+ if (!user.displayName || user.displayName.length < 2) {
260
+ errors.push('Display name must be at least 2 characters');
261
+ }
262
+
263
+ return {
264
+ isValid: errors.length === 0,
265
+ errors,
266
+ };
267
+ }
268
+ ```
269
+
270
+ ## Domain Rules
271
+
272
+ 1. **AuthUser Rules:**
273
+ - Her kullanıcının unique bir `uid`'si olmalı
274
+ - Anonymous kullanıcıların `email`'i yoktur
275
+ - Social login kullanıcıları provider bilgisi taşır
276
+
277
+ 2. **UserProfile Rules:**
278
+ - `displayName` en az 2 karakter olmalı
279
+ - `email` geçerli formatta olmalı
280
+ - `photoURL` geçerli URL olmalı
281
+
282
+ 3. **Password Rules:**
283
+ - Minimum 8 karakter
284
+ - En az 1 büyük harf
285
+ - En az 1 küçük harf
286
+ - En az 1 rakam
287
+ - En az 1 özel karakter
288
+
289
+ ## İlgili Modüller
290
+
291
+ - **[Application](../application/README.md)** - Application ports
292
+ - **[Infrastructure](../infrastructure/README.md)** - Infrastructure implementation
293
+ - **[Presentation](../presentation/README.md)** - UI components ve hooks
@@ -0,0 +1,377 @@
1
+ # AuthUser Entity
2
+
3
+ Authentication için provider-agnostic kullanıcı entity'si. Firebase User ile uyumlu, ancak herhangi bir auth provider ile kullanılabilir.
4
+
5
+ ## Tip Tanımı
6
+
7
+ ```typescript
8
+ import type { AuthUser, AuthProviderType } from '@umituz/react-native-auth';
9
+
10
+ interface AuthUser {
11
+ uid: string; // Unique kullanıcı ID'si
12
+ email: string | null; // Email adresi (anonymous'da null)
13
+ displayName: string | null; // Görünen ad
14
+ isAnonymous: boolean; // Anonymous kullanıcı mı
15
+ emailVerified: boolean; // Email doğrulanmış mı
16
+ photoURL: string | null; // Profil fotoğrafı URL'si
17
+ provider: AuthProviderType; // Auth provider tipi
18
+ }
19
+
20
+ type AuthProviderType =
21
+ | "google.com" // Google ile giriş
22
+ | "apple.com" // Apple ile giriş
23
+ | "password" // Email/password ile giriş
24
+ | "anonymous" // Anonymous kullanıcı
25
+ | "unknown"; // Bilinmeyen provider
26
+ ```
27
+
28
+ ## Örnekler
29
+
30
+ ### Email/Password Kullanıcısı
31
+
32
+ ```typescript
33
+ const emailUser: AuthUser = {
34
+ uid: 'user-123',
35
+ email: 'john@example.com',
36
+ displayName: 'John Doe',
37
+ isAnonymous: false,
38
+ emailVerified: true,
39
+ photoURL: null,
40
+ provider: 'password',
41
+ };
42
+ ```
43
+
44
+ ### Google Kullanıcısı
45
+
46
+ ```typescript
47
+ const googleUser: AuthUser = {
48
+ uid: 'google-456',
49
+ email: 'jane@gmail.com',
50
+ displayName: 'Jane Smith',
51
+ isAnonymous: false,
52
+ emailVerified: true,
53
+ photoURL: 'https://lh3.googleusercontent.com/...',
54
+ provider: 'google.com',
55
+ };
56
+ ```
57
+
58
+ ### Apple Kullanıcısı
59
+
60
+ ```typescript
61
+ const appleUser: AuthUser = {
62
+ uid: 'apple-789',
63
+ email: 'user@icloud.com',
64
+ displayName: 'Apple User',
65
+ isAnonymous: false,
66
+ emailVerified: true,
67
+ photoURL: null,
68
+ provider: 'apple.com',
69
+ };
70
+ ```
71
+
72
+ ### Anonymous Kullanıcı
73
+
74
+ ```typescript
75
+ const anonymousUser: AuthUser = {
76
+ uid: 'anon-abc',
77
+ email: null,
78
+ displayName: null,
79
+ isAnonymous: true,
80
+ emailVerified: false,
81
+ photoURL: null,
82
+ provider: 'anonymous',
83
+ };
84
+ ```
85
+
86
+ ## Kullanım
87
+
88
+ ### Firebase User'dan AuthUser'a Dönüşüm
89
+
90
+ ```typescript
91
+ import { getAuth } from 'firebase/auth';
92
+ import type { AuthUser } from '@umituz/react-native-auth';
93
+
94
+ function firebaseUserToAuthUser(firebaseUser: FirebaseUser): AuthUser {
95
+ return {
96
+ uid: firebaseUser.uid,
97
+ email: firebaseUser.email,
98
+ displayName: firebaseUser.displayName,
99
+ isAnonymous: firebaseUser.isAnonymous,
100
+ emailVerified: firebaseUser.emailVerified,
101
+ photoURL: firebaseUser.photoURL,
102
+ provider: firebaseUser.providerData[0]?.providerId || 'unknown',
103
+ };
104
+ }
105
+
106
+ // Kullanım
107
+ const auth = getAuth();
108
+ const firebaseUser = auth.currentUser;
109
+ if (firebaseUser) {
110
+ const user: AuthUser = firebaseUserToAuthUser(firebaseUser);
111
+ console.log(user.displayName);
112
+ }
113
+ ```
114
+
115
+ ### DisplayName Alma
116
+
117
+ ```typescript
118
+ function getUserDisplayName(user: AuthUser): string {
119
+ // Önce displayName'i dene
120
+ if (user.displayName) {
121
+ return user.displayName;
122
+ }
123
+
124
+ // Yoksa email'i kullan
125
+ if (user.email) {
126
+ // Email'den isim çıkar
127
+ const name = user.email.split('@')[0];
128
+ return name.charAt(0).toUpperCase() + name.slice(1);
129
+ }
130
+
131
+ // Hiçbiri yoksa varsayılan
132
+ return 'Kullanıcı';
133
+ }
134
+
135
+ // Kullanım
136
+ const name = getUserDisplayName(user);
137
+ console.log(name); // "John" veya "Johndoe"
138
+ ```
139
+
140
+ ### Provider Kontrolü
141
+
142
+ ```typescript
143
+ function isSocialLogin(user: AuthUser): boolean {
144
+ return user.provider === 'google.com' || user.provider === 'apple.com';
145
+ }
146
+
147
+ function isEmailPasswordUser(user: AuthUser): boolean {
148
+ return user.provider === 'password';
149
+ }
150
+
151
+ function canChangePassword(user: AuthUser): boolean {
152
+ // Sadece email/password kullanıcıları şifre değiştirebilir
153
+ return user.provider === 'password';
154
+ }
155
+
156
+ // Kullanım
157
+ if (isSocialLogin(user)) {
158
+ console.log('Social login ile giriş yaptı');
159
+ }
160
+
161
+ if (canChangePassword(user)) {
162
+ // Şifre değiştirme butonunu göster
163
+ }
164
+ ```
165
+
166
+ ### Email Doğrulama Kontrolü
167
+
168
+ ```typescript
169
+ function requireEmailVerification(user: AuthUser): void {
170
+ if (user.provider === 'password' && !user.emailVerified) {
171
+ throw new Error('Email doğrulanması gerekiyor');
172
+ }
173
+ }
174
+
175
+ function shouldShowVerifyEmailBanner(user: AuthUser): boolean {
176
+ return (
177
+ !user.isAnonymous &&
178
+ user.provider === 'password' &&
179
+ !user.emailVerified
180
+ );
181
+ }
182
+ ```
183
+
184
+ ### Avatar URL Alma
185
+
186
+ ```typescript
187
+ function getUserAvatar(user: AuthUser): string | null {
188
+ // Önce photoURL'yi dene
189
+ if (user.photoURL) {
190
+ return user.photoURL;
191
+ }
192
+
193
+ // Anonymous ise null döndür
194
+ if (user.isAnonymous) {
195
+ return null;
196
+ }
197
+
198
+ // Default avatar oluştur (örn: UI Avatars)
199
+ if (user.email) {
200
+ return `https://ui-avatars.com/api/?name=${encodeURIComponent(user.email)}&background=random`;
201
+ }
202
+
203
+ return null;
204
+ }
205
+ ```
206
+
207
+ ### User İsim Oluşturma
208
+
209
+ ```typescript
210
+ function getUserInitials(user: AuthUser): string {
211
+ if (user.displayName) {
212
+ const names = user.displayName.trim().split(' ');
213
+ if (names.length >= 2) {
214
+ return (names[0][0] + names[names.length - 1][0]).toUpperCase();
215
+ }
216
+ return names[0][0].toUpperCase();
217
+ }
218
+
219
+ if (user.email) {
220
+ return user.email[0].toUpperCase();
221
+ }
222
+
223
+ return '?';
224
+ }
225
+
226
+ // Kullanım
227
+ const initials = getUserInitials(user);
228
+ console.log(initials); // "JD" veya "J" veya "?"
229
+ ```
230
+
231
+ ## Type Guards
232
+
233
+ ```typescript
234
+ function isAuthenticatedUser(user: AuthUser | null): user is AuthUser {
235
+ return user !== null && !user.isAnonymous;
236
+ }
237
+
238
+ function isAnonymousUser(user: AuthUser | null): user is AuthUser {
239
+ return user !== null && user.isAnonymous;
240
+ }
241
+
242
+ function hasEmail(user: AuthUser | null): user is AuthUser {
243
+ return user !== null && user.email !== null;
244
+ }
245
+
246
+ // Kullanım
247
+ if (isAuthenticatedUser(user)) {
248
+ // TypeScript burada user'ın AuthUser olduğunu bilir
249
+ console.log(user.email);
250
+ }
251
+ ```
252
+
253
+ ## Firebase ile Entegrasyon
254
+
255
+ ### Auth State Değişikliği Dinleme
256
+
257
+ ```typescript
258
+ import { getAuth, onAuthStateChanged } from 'firebase/auth';
259
+ import type { AuthUser } from '@umituz/react-native-auth';
260
+
261
+ function useAuthUser() {
262
+ const [user, setUser] = useState<AuthUser | null>(null);
263
+
264
+ useEffect(() => {
265
+ const auth = getAuth();
266
+ const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
267
+ if (firebaseUser) {
268
+ const authUser: AuthUser = {
269
+ uid: firebaseUser.uid,
270
+ email: firebaseUser.email,
271
+ displayName: firebaseUser.displayName,
272
+ isAnonymous: firebaseUser.isAnonymous,
273
+ emailVerified: firebaseUser.emailVerified,
274
+ photoURL: firebaseUser.photoURL,
275
+ provider: firebaseUser.providerData[0]?.providerId || 'unknown',
276
+ };
277
+ setUser(authUser);
278
+ } else {
279
+ setUser(null);
280
+ }
281
+ });
282
+
283
+ return unsubscribe;
284
+ }, []);
285
+
286
+ return user;
287
+ }
288
+ ```
289
+
290
+ ### User Meta Verileri
291
+
292
+ ```typescript
293
+ interface ExtendedAuthUser extends AuthUser {
294
+ createdAt?: number;
295
+ lastSignInAt?: number;
296
+ metadata?: {
297
+ lastSignInTime?: string;
298
+ creationTime?: string;
299
+ };
300
+ }
301
+
302
+ function enrichAuthUser(user: AuthUser, firebaseUser: FirebaseUser): ExtendedAuthUser {
303
+ return {
304
+ ...user,
305
+ metadata: {
306
+ lastSignInTime: firebaseUser.metadata.lastSignInTime,
307
+ creationTime: firebaseUser.metadata.creationTime,
308
+ },
309
+ };
310
+ }
311
+ ```
312
+
313
+ ## Validasyon
314
+
315
+ ### User Validasyonu
316
+
317
+ ```typescript
318
+ function validateAuthUser(user: AuthUser): { valid: boolean; errors: string[] } {
319
+ const errors: string[] = [];
320
+
321
+ if (!user.uid || user.uid.length === 0) {
322
+ errors.push('User ID is required');
323
+ }
324
+
325
+ if (!user.isAnonymous && !user.email) {
326
+ errors.push('Email is required for non-anonymous users');
327
+ }
328
+
329
+ if (user.email && !isValidEmail(user.email)) {
330
+ errors.push('Invalid email format');
331
+ }
332
+
333
+ return {
334
+ valid: errors.length === 0,
335
+ errors,
336
+ };
337
+ }
338
+
339
+ function isValidEmail(email: string): boolean {
340
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
341
+ }
342
+ ```
343
+
344
+ ## Testler
345
+
346
+ ### Mock User Oluşturma
347
+
348
+ ```typescript
349
+ function createMockAuthUser(overrides?: Partial<AuthUser>): AuthUser {
350
+ return {
351
+ uid: 'mock-user-123',
352
+ email: 'mock@example.com',
353
+ displayName: 'Mock User',
354
+ isAnonymous: false,
355
+ emailVerified: true,
356
+ photoURL: null,
357
+ provider: 'password',
358
+ ...overrides,
359
+ };
360
+ }
361
+
362
+ // Testlerde kullanım
363
+ const mockUser = createMockAuthUser({
364
+ provider: 'google.com',
365
+ photoURL: 'https://example.com/avatar.jpg',
366
+ });
367
+ ```
368
+
369
+ ## İlgili Entity'ler
370
+
371
+ - **[`UserProfile`](./UserProfile.md)** - Kullanıcı profili entity'si
372
+ - **[`AuthError`](../errors/AuthError.md)** - Auth hataları
373
+
374
+ ## İlgili Type'lar
375
+
376
+ - **[`AuthProviderType`](#tip-tanımı)** - Provider tipi
377
+ - **[`UpdateProfileParams`](../UserProfile.md)** - Profil güncelleme parametreleri