svelte-firekit 0.1.0 → 0.1.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.
- package/dist/services/auth.d.ts +22 -14
- package/dist/services/auth.js +141 -83
- package/dist/services/user.svelte.d.ts +6 -0
- package/dist/services/user.svelte.js +86 -10
- package/package.json +2 -2
package/dist/services/auth.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ declare class FirekitAuth {
|
|
|
31
31
|
private static instance;
|
|
32
32
|
private auth;
|
|
33
33
|
private firestore;
|
|
34
|
+
private _servicesInitialized;
|
|
34
35
|
private authState;
|
|
35
36
|
private stateListeners;
|
|
36
37
|
private recaptchaVerifiers;
|
|
@@ -40,6 +41,11 @@ declare class FirekitAuth {
|
|
|
40
41
|
* @returns {FirekitAuth} The FirekitAuth instance
|
|
41
42
|
*/
|
|
42
43
|
static getInstance(): FirekitAuth;
|
|
44
|
+
/**
|
|
45
|
+
* Initializes Firebase services and auth state listener
|
|
46
|
+
* @private
|
|
47
|
+
*/
|
|
48
|
+
private initializeServices;
|
|
43
49
|
/**
|
|
44
50
|
* Initializes the authentication state listener
|
|
45
51
|
* @private
|
|
@@ -263,14 +269,18 @@ declare class FirekitAuth {
|
|
|
263
269
|
* @example
|
|
264
270
|
* ```typescript
|
|
265
271
|
* await firekitAuth.sendEmailVerification();
|
|
266
|
-
* console.log("Verification email sent");
|
|
267
272
|
* ```
|
|
268
273
|
*/
|
|
269
274
|
sendEmailVerification(): Promise<void>;
|
|
270
275
|
/**
|
|
271
|
-
* Reloads
|
|
276
|
+
* Reloads user to get updated data
|
|
272
277
|
* @returns {Promise<void>} Promise that resolves when user is reloaded
|
|
273
278
|
* @throws {FirekitAuthError} If reload fails
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* await firekitAuth.reloadUser();
|
|
283
|
+
* ```
|
|
274
284
|
*/
|
|
275
285
|
reloadUser(): Promise<void>;
|
|
276
286
|
/**
|
|
@@ -282,21 +292,17 @@ declare class FirekitAuth {
|
|
|
282
292
|
* @example
|
|
283
293
|
* ```typescript
|
|
284
294
|
* const token = await firekitAuth.getIdToken();
|
|
285
|
-
* // Use token for authenticated API calls
|
|
286
295
|
* ```
|
|
287
296
|
*/
|
|
288
297
|
getIdToken(forceRefresh?: boolean): Promise<string>;
|
|
289
298
|
/**
|
|
290
|
-
* Reauthenticates
|
|
291
|
-
* @param {string} currentPassword Current password
|
|
292
|
-
* @returns {Promise<void>} Promise that resolves when reauthentication succeeds
|
|
293
|
-
* @throws {FirekitAuthError} If reauthentication fails
|
|
299
|
+
* Reauthenticates user with current password
|
|
294
300
|
* @private
|
|
295
301
|
*/
|
|
296
302
|
private reauthenticateUser;
|
|
297
303
|
/**
|
|
298
|
-
* Deletes user account
|
|
299
|
-
* @param {string} [currentPassword] Current password for reauthentication
|
|
304
|
+
* Deletes user account
|
|
305
|
+
* @param {string} [currentPassword] Current password for reauthentication
|
|
300
306
|
* @returns {Promise<AccountDeletionResult>} Promise resolving to deletion result
|
|
301
307
|
*
|
|
302
308
|
* @example
|
|
@@ -304,12 +310,14 @@ declare class FirekitAuth {
|
|
|
304
310
|
* const result = await firekitAuth.deleteAccount("currentPassword123");
|
|
305
311
|
* if (result.success) {
|
|
306
312
|
* console.log("Account deleted successfully");
|
|
313
|
+
* } else {
|
|
314
|
+
* console.error("Deletion failed:", result.message);
|
|
307
315
|
* }
|
|
308
316
|
* ```
|
|
309
317
|
*/
|
|
310
318
|
deleteAccount(currentPassword?: string): Promise<AccountDeletionResult>;
|
|
311
319
|
/**
|
|
312
|
-
* Signs out current user
|
|
320
|
+
* Signs out the current user
|
|
313
321
|
* @returns {Promise<void>} Promise that resolves when sign-out completes
|
|
314
322
|
* @throws {FirekitAuthError} If sign-out fails
|
|
315
323
|
*
|
|
@@ -336,7 +344,7 @@ declare class FirekitAuth {
|
|
|
336
344
|
*/
|
|
337
345
|
isEmailVerified(): boolean;
|
|
338
346
|
/**
|
|
339
|
-
* Gets user's
|
|
347
|
+
* Gets user's email address
|
|
340
348
|
* @returns {string | null} User's email or null
|
|
341
349
|
*/
|
|
342
350
|
getUserEmail(): string | null;
|
|
@@ -351,13 +359,13 @@ declare class FirekitAuth {
|
|
|
351
359
|
*/
|
|
352
360
|
getUserPhotoURL(): string | null;
|
|
353
361
|
/**
|
|
354
|
-
* Gets user's
|
|
362
|
+
* Gets user's unique ID
|
|
355
363
|
* @returns {string | null} User's UID or null
|
|
356
364
|
*/
|
|
357
365
|
getUserId(): string | null;
|
|
358
366
|
/**
|
|
359
|
-
* Cleans up
|
|
360
|
-
* @returns {Promise<void>} Promise that resolves when cleanup
|
|
367
|
+
* Cleans up resources and listeners
|
|
368
|
+
* @returns {Promise<void>} Promise that resolves when cleanup completes
|
|
361
369
|
*/
|
|
362
370
|
cleanup(): Promise<void>;
|
|
363
371
|
}
|
package/dist/services/auth.js
CHANGED
|
@@ -32,8 +32,9 @@ import { mapFirebaseUserToProfile, updateUserInFirestore, createGoogleProvider,
|
|
|
32
32
|
*/
|
|
33
33
|
class FirekitAuth {
|
|
34
34
|
static instance;
|
|
35
|
-
auth =
|
|
36
|
-
firestore =
|
|
35
|
+
auth = null;
|
|
36
|
+
firestore = null;
|
|
37
|
+
_servicesInitialized = false;
|
|
37
38
|
authState = {
|
|
38
39
|
user: null,
|
|
39
40
|
loading: true,
|
|
@@ -42,13 +43,8 @@ class FirekitAuth {
|
|
|
42
43
|
stateListeners = new Set();
|
|
43
44
|
recaptchaVerifiers = new Map();
|
|
44
45
|
constructor() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
if (!this.firestore) {
|
|
49
|
-
throw new Error('Firestore instance not available');
|
|
50
|
-
}
|
|
51
|
-
this.initializeAuthStateListener();
|
|
46
|
+
// Don't initialize Firebase services in constructor
|
|
47
|
+
// They will be initialized lazily when needed
|
|
52
48
|
}
|
|
53
49
|
/**
|
|
54
50
|
* Gets singleton instance of FirekitAuth
|
|
@@ -60,11 +56,38 @@ class FirekitAuth {
|
|
|
60
56
|
}
|
|
61
57
|
return FirekitAuth.instance;
|
|
62
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Initializes Firebase services and auth state listener
|
|
61
|
+
* @private
|
|
62
|
+
*/
|
|
63
|
+
initializeServices() {
|
|
64
|
+
if (this._servicesInitialized)
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
this.auth = firebaseService.getAuthInstance();
|
|
68
|
+
this.firestore = firebaseService.getDbInstance();
|
|
69
|
+
this._servicesInitialized = true;
|
|
70
|
+
this.initializeAuthStateListener();
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error('Failed to initialize Firebase services:', error);
|
|
74
|
+
this.authState = {
|
|
75
|
+
user: null,
|
|
76
|
+
loading: false,
|
|
77
|
+
initialized: true
|
|
78
|
+
};
|
|
79
|
+
this.notifyStateListeners();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
63
82
|
/**
|
|
64
83
|
* Initializes the authentication state listener
|
|
65
84
|
* @private
|
|
66
85
|
*/
|
|
67
86
|
initializeAuthStateListener() {
|
|
87
|
+
if (!this.auth) {
|
|
88
|
+
console.error('Auth instance not available');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
68
91
|
onAuthStateChanged(this.auth, (user) => {
|
|
69
92
|
this.authState = {
|
|
70
93
|
user: user ? this.mapFirebaseUserToProfile(user) : null,
|
|
@@ -101,6 +124,9 @@ class FirekitAuth {
|
|
|
101
124
|
* @private
|
|
102
125
|
*/
|
|
103
126
|
async updateUserInFirestore(user) {
|
|
127
|
+
if (!this.firestore) {
|
|
128
|
+
throw new Error('Firestore instance not available');
|
|
129
|
+
}
|
|
104
130
|
await updateUserInFirestore(this.firestore, user);
|
|
105
131
|
}
|
|
106
132
|
/**
|
|
@@ -115,6 +141,7 @@ class FirekitAuth {
|
|
|
115
141
|
* @returns {AuthState} Current authentication state
|
|
116
142
|
*/
|
|
117
143
|
getState() {
|
|
144
|
+
this.initializeServices();
|
|
118
145
|
return { ...this.authState };
|
|
119
146
|
}
|
|
120
147
|
/**
|
|
@@ -122,13 +149,15 @@ class FirekitAuth {
|
|
|
122
149
|
* @returns {User | null} Current Firebase user or null
|
|
123
150
|
*/
|
|
124
151
|
getCurrentUser() {
|
|
125
|
-
|
|
152
|
+
this.initializeServices();
|
|
153
|
+
return this.auth?.currentUser ?? null;
|
|
126
154
|
}
|
|
127
155
|
/**
|
|
128
156
|
* Gets the current user profile
|
|
129
157
|
* @returns {UserProfile | null} Current user profile or null
|
|
130
158
|
*/
|
|
131
159
|
getCurrentUserProfile() {
|
|
160
|
+
this.initializeServices();
|
|
132
161
|
return this.authState.user;
|
|
133
162
|
}
|
|
134
163
|
/**
|
|
@@ -136,6 +165,7 @@ class FirekitAuth {
|
|
|
136
165
|
* @returns {Promise<UserProfile | null>} Promise that resolves when auth is initialized
|
|
137
166
|
*/
|
|
138
167
|
async waitForAuth() {
|
|
168
|
+
this.initializeServices();
|
|
139
169
|
return new Promise((resolve) => {
|
|
140
170
|
if (this.authState.initialized) {
|
|
141
171
|
resolve(this.authState.user);
|
|
@@ -155,6 +185,7 @@ class FirekitAuth {
|
|
|
155
185
|
* @returns {Function} Unsubscribe function
|
|
156
186
|
*/
|
|
157
187
|
onAuthStateChanged(callback) {
|
|
188
|
+
this.initializeServices();
|
|
158
189
|
this.stateListeners.add(callback);
|
|
159
190
|
// Immediately call with current state
|
|
160
191
|
callback(this.authState);
|
|
@@ -183,6 +214,10 @@ class FirekitAuth {
|
|
|
183
214
|
* ```
|
|
184
215
|
*/
|
|
185
216
|
async signInWithEmail(email, password) {
|
|
217
|
+
this.initializeServices();
|
|
218
|
+
if (!this.auth) {
|
|
219
|
+
throw new Error('Auth instance not available');
|
|
220
|
+
}
|
|
186
221
|
try {
|
|
187
222
|
this.authState.loading = true;
|
|
188
223
|
this.notifyStateListeners();
|
|
@@ -216,6 +251,10 @@ class FirekitAuth {
|
|
|
216
251
|
* ```
|
|
217
252
|
*/
|
|
218
253
|
async signInWithGoogle() {
|
|
254
|
+
this.initializeServices();
|
|
255
|
+
if (!this.auth) {
|
|
256
|
+
throw new Error('Auth instance not available');
|
|
257
|
+
}
|
|
219
258
|
try {
|
|
220
259
|
this.authState.loading = true;
|
|
221
260
|
this.notifyStateListeners();
|
|
@@ -238,6 +277,10 @@ class FirekitAuth {
|
|
|
238
277
|
* @throws {FirekitAuthError} If sign-in fails
|
|
239
278
|
*/
|
|
240
279
|
async signInWithFacebook() {
|
|
280
|
+
this.initializeServices();
|
|
281
|
+
if (!this.auth) {
|
|
282
|
+
throw new Error('Auth instance not available');
|
|
283
|
+
}
|
|
241
284
|
try {
|
|
242
285
|
this.authState.loading = true;
|
|
243
286
|
this.notifyStateListeners();
|
|
@@ -260,6 +303,10 @@ class FirekitAuth {
|
|
|
260
303
|
* @throws {FirekitAuthError} If sign-in fails
|
|
261
304
|
*/
|
|
262
305
|
async signInWithApple() {
|
|
306
|
+
this.initializeServices();
|
|
307
|
+
if (!this.auth) {
|
|
308
|
+
throw new Error('Auth instance not available');
|
|
309
|
+
}
|
|
263
310
|
try {
|
|
264
311
|
this.authState.loading = true;
|
|
265
312
|
this.notifyStateListeners();
|
|
@@ -288,6 +335,10 @@ class FirekitAuth {
|
|
|
288
335
|
* ```
|
|
289
336
|
*/
|
|
290
337
|
async signInAnonymously() {
|
|
338
|
+
this.initializeServices();
|
|
339
|
+
if (!this.auth) {
|
|
340
|
+
throw new Error('Auth instance not available');
|
|
341
|
+
}
|
|
291
342
|
try {
|
|
292
343
|
this.authState.loading = true;
|
|
293
344
|
this.notifyStateListeners();
|
|
@@ -317,6 +368,10 @@ class FirekitAuth {
|
|
|
317
368
|
* ```
|
|
318
369
|
*/
|
|
319
370
|
async signInWithPhoneNumber(phoneNumber, recaptchaContainerId) {
|
|
371
|
+
this.initializeServices();
|
|
372
|
+
if (!this.auth) {
|
|
373
|
+
throw new Error('Auth instance not available');
|
|
374
|
+
}
|
|
320
375
|
try {
|
|
321
376
|
this.authState.loading = true;
|
|
322
377
|
this.notifyStateListeners();
|
|
@@ -339,11 +394,11 @@ class FirekitAuth {
|
|
|
339
394
|
const confirmationResult = await firebaseSignInWithPhoneNumber(this.auth, phoneNumber, recaptchaVerifier);
|
|
340
395
|
return {
|
|
341
396
|
verificationId: confirmationResult.verificationId,
|
|
342
|
-
confirm: async (
|
|
397
|
+
confirm: async (verificationCode) => {
|
|
343
398
|
try {
|
|
344
|
-
const
|
|
345
|
-
await this.updateUserInFirestore(
|
|
346
|
-
return this.mapFirebaseUserToProfile(
|
|
399
|
+
const userCredential = await confirmationResult.confirm(verificationCode);
|
|
400
|
+
await this.updateUserInFirestore(userCredential.user);
|
|
401
|
+
return this.mapFirebaseUserToProfile(userCredential.user);
|
|
347
402
|
}
|
|
348
403
|
catch (error) {
|
|
349
404
|
this.handleAuthError(error);
|
|
@@ -387,6 +442,10 @@ class FirekitAuth {
|
|
|
387
442
|
* ```
|
|
388
443
|
*/
|
|
389
444
|
async registerWithEmail(email, password, displayName, sendVerification = true) {
|
|
445
|
+
this.initializeServices();
|
|
446
|
+
if (!this.auth) {
|
|
447
|
+
throw new Error('Auth instance not available');
|
|
448
|
+
}
|
|
390
449
|
try {
|
|
391
450
|
this.authState.loading = true;
|
|
392
451
|
this.notifyStateListeners();
|
|
@@ -427,6 +486,10 @@ class FirekitAuth {
|
|
|
427
486
|
* ```
|
|
428
487
|
*/
|
|
429
488
|
async sendPasswordReset(email) {
|
|
489
|
+
this.initializeServices();
|
|
490
|
+
if (!this.auth) {
|
|
491
|
+
throw new Error('Auth instance not available');
|
|
492
|
+
}
|
|
430
493
|
try {
|
|
431
494
|
await sendPasswordResetEmail(this.auth, email);
|
|
432
495
|
}
|
|
@@ -442,6 +505,10 @@ class FirekitAuth {
|
|
|
442
505
|
* @throws {FirekitAuthError} If reset fails
|
|
443
506
|
*/
|
|
444
507
|
async confirmPasswordReset(code, newPassword) {
|
|
508
|
+
this.initializeServices();
|
|
509
|
+
if (!this.auth) {
|
|
510
|
+
throw new Error('Auth instance not available');
|
|
511
|
+
}
|
|
445
512
|
try {
|
|
446
513
|
await confirmPasswordReset(this.auth, code, newPassword);
|
|
447
514
|
}
|
|
@@ -466,7 +533,8 @@ class FirekitAuth {
|
|
|
466
533
|
* ```
|
|
467
534
|
*/
|
|
468
535
|
async updatePassword(newPassword, currentPassword) {
|
|
469
|
-
|
|
536
|
+
this.initializeServices();
|
|
537
|
+
if (!this.auth?.currentUser) {
|
|
470
538
|
return {
|
|
471
539
|
success: false,
|
|
472
540
|
message: 'No authenticated user found.',
|
|
@@ -517,7 +585,8 @@ class FirekitAuth {
|
|
|
517
585
|
* ```
|
|
518
586
|
*/
|
|
519
587
|
async updateUserProfile(profile) {
|
|
520
|
-
|
|
588
|
+
this.initializeServices();
|
|
589
|
+
if (!this.auth?.currentUser) {
|
|
521
590
|
throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
|
|
522
591
|
}
|
|
523
592
|
try {
|
|
@@ -540,7 +609,8 @@ class FirekitAuth {
|
|
|
540
609
|
* ```
|
|
541
610
|
*/
|
|
542
611
|
async updateEmail(newEmail) {
|
|
543
|
-
|
|
612
|
+
this.initializeServices();
|
|
613
|
+
if (!this.auth?.currentUser) {
|
|
544
614
|
throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
|
|
545
615
|
}
|
|
546
616
|
try {
|
|
@@ -551,9 +621,6 @@ class FirekitAuth {
|
|
|
551
621
|
this.handleAuthError(error);
|
|
552
622
|
}
|
|
553
623
|
}
|
|
554
|
-
// ========================================
|
|
555
|
-
// EMAIL VERIFICATION METHODS
|
|
556
|
-
// ========================================
|
|
557
624
|
/**
|
|
558
625
|
* Sends email verification to current user
|
|
559
626
|
* @returns {Promise<void>} Promise that resolves when verification email is sent
|
|
@@ -562,11 +629,11 @@ class FirekitAuth {
|
|
|
562
629
|
* @example
|
|
563
630
|
* ```typescript
|
|
564
631
|
* await firekitAuth.sendEmailVerification();
|
|
565
|
-
* console.log("Verification email sent");
|
|
566
632
|
* ```
|
|
567
633
|
*/
|
|
568
634
|
async sendEmailVerification() {
|
|
569
|
-
|
|
635
|
+
this.initializeServices();
|
|
636
|
+
if (!this.auth?.currentUser) {
|
|
570
637
|
throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
|
|
571
638
|
}
|
|
572
639
|
try {
|
|
@@ -577,12 +644,18 @@ class FirekitAuth {
|
|
|
577
644
|
}
|
|
578
645
|
}
|
|
579
646
|
/**
|
|
580
|
-
* Reloads
|
|
647
|
+
* Reloads user to get updated data
|
|
581
648
|
* @returns {Promise<void>} Promise that resolves when user is reloaded
|
|
582
649
|
* @throws {FirekitAuthError} If reload fails
|
|
650
|
+
*
|
|
651
|
+
* @example
|
|
652
|
+
* ```typescript
|
|
653
|
+
* await firekitAuth.reloadUser();
|
|
654
|
+
* ```
|
|
583
655
|
*/
|
|
584
656
|
async reloadUser() {
|
|
585
|
-
|
|
657
|
+
this.initializeServices();
|
|
658
|
+
if (!this.auth?.currentUser) {
|
|
586
659
|
throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
|
|
587
660
|
}
|
|
588
661
|
try {
|
|
@@ -593,9 +666,6 @@ class FirekitAuth {
|
|
|
593
666
|
this.handleAuthError(error);
|
|
594
667
|
}
|
|
595
668
|
}
|
|
596
|
-
// ========================================
|
|
597
|
-
// TOKEN METHODS
|
|
598
|
-
// ========================================
|
|
599
669
|
/**
|
|
600
670
|
* Gets the current user's ID token
|
|
601
671
|
* @param {boolean} [forceRefresh=false] Whether to force token refresh
|
|
@@ -605,11 +675,11 @@ class FirekitAuth {
|
|
|
605
675
|
* @example
|
|
606
676
|
* ```typescript
|
|
607
677
|
* const token = await firekitAuth.getIdToken();
|
|
608
|
-
* // Use token for authenticated API calls
|
|
609
678
|
* ```
|
|
610
679
|
*/
|
|
611
680
|
async getIdToken(forceRefresh = false) {
|
|
612
|
-
|
|
681
|
+
this.initializeServices();
|
|
682
|
+
if (!this.auth?.currentUser) {
|
|
613
683
|
throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
|
|
614
684
|
}
|
|
615
685
|
try {
|
|
@@ -619,31 +689,26 @@ class FirekitAuth {
|
|
|
619
689
|
this.handleAuthError(error);
|
|
620
690
|
}
|
|
621
691
|
}
|
|
622
|
-
// ========================================
|
|
623
|
-
// ACCOUNT MANAGEMENT
|
|
624
|
-
// ========================================
|
|
625
692
|
/**
|
|
626
|
-
* Reauthenticates
|
|
627
|
-
* @param {string} currentPassword Current password
|
|
628
|
-
* @returns {Promise<void>} Promise that resolves when reauthentication succeeds
|
|
629
|
-
* @throws {FirekitAuthError} If reauthentication fails
|
|
693
|
+
* Reauthenticates user with current password
|
|
630
694
|
* @private
|
|
631
695
|
*/
|
|
632
696
|
async reauthenticateUser(currentPassword) {
|
|
633
|
-
|
|
634
|
-
|
|
697
|
+
this.initializeServices();
|
|
698
|
+
if (!this.auth?.currentUser || !this.auth.currentUser.email) {
|
|
699
|
+
throw new FirekitAuthError('auth/no-current-user', 'No authenticated user with email found.');
|
|
635
700
|
}
|
|
636
701
|
try {
|
|
637
702
|
const credential = EmailAuthProvider.credential(this.auth.currentUser.email, currentPassword);
|
|
638
703
|
await reauthenticateWithCredential(this.auth.currentUser, credential);
|
|
639
704
|
}
|
|
640
705
|
catch (error) {
|
|
641
|
-
|
|
706
|
+
this.handleAuthError(error);
|
|
642
707
|
}
|
|
643
708
|
}
|
|
644
709
|
/**
|
|
645
|
-
* Deletes user account
|
|
646
|
-
* @param {string} [currentPassword] Current password for reauthentication
|
|
710
|
+
* Deletes user account
|
|
711
|
+
* @param {string} [currentPassword] Current password for reauthentication
|
|
647
712
|
* @returns {Promise<AccountDeletionResult>} Promise resolving to deletion result
|
|
648
713
|
*
|
|
649
714
|
* @example
|
|
@@ -651,11 +716,14 @@ class FirekitAuth {
|
|
|
651
716
|
* const result = await firekitAuth.deleteAccount("currentPassword123");
|
|
652
717
|
* if (result.success) {
|
|
653
718
|
* console.log("Account deleted successfully");
|
|
719
|
+
* } else {
|
|
720
|
+
* console.error("Deletion failed:", result.message);
|
|
654
721
|
* }
|
|
655
722
|
* ```
|
|
656
723
|
*/
|
|
657
724
|
async deleteAccount(currentPassword) {
|
|
658
|
-
|
|
725
|
+
this.initializeServices();
|
|
726
|
+
if (!this.auth?.currentUser) {
|
|
659
727
|
return {
|
|
660
728
|
success: false,
|
|
661
729
|
message: 'No authenticated user found.'
|
|
@@ -663,7 +731,7 @@ class FirekitAuth {
|
|
|
663
731
|
}
|
|
664
732
|
try {
|
|
665
733
|
const user = this.auth.currentUser;
|
|
666
|
-
// Reauthenticate if password provided
|
|
734
|
+
// Reauthenticate if password provided
|
|
667
735
|
if (currentPassword) {
|
|
668
736
|
await this.reauthenticateUser(currentPassword);
|
|
669
737
|
}
|
|
@@ -673,9 +741,9 @@ class FirekitAuth {
|
|
|
673
741
|
await setDoc(userRef, { deleted: true, deletedAt: serverTimestamp() }, { merge: true });
|
|
674
742
|
}
|
|
675
743
|
catch (firestoreError) {
|
|
676
|
-
console.
|
|
744
|
+
console.warn('Failed to update Firestore before account deletion:', firestoreError);
|
|
677
745
|
}
|
|
678
|
-
// Delete the
|
|
746
|
+
// Delete the user account
|
|
679
747
|
await deleteUser(user);
|
|
680
748
|
return {
|
|
681
749
|
success: true,
|
|
@@ -683,24 +751,14 @@ class FirekitAuth {
|
|
|
683
751
|
};
|
|
684
752
|
}
|
|
685
753
|
catch (error) {
|
|
686
|
-
const code = error.code;
|
|
687
|
-
if (code === AuthErrorCode.REQUIRES_RECENT_LOGIN) {
|
|
688
|
-
return {
|
|
689
|
-
success: false,
|
|
690
|
-
message: 'Please sign in again before deleting your account.'
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
754
|
return {
|
|
694
755
|
success: false,
|
|
695
756
|
message: error.message || 'Failed to delete account.'
|
|
696
757
|
};
|
|
697
758
|
}
|
|
698
759
|
}
|
|
699
|
-
// ========================================
|
|
700
|
-
// SIGN OUT
|
|
701
|
-
// ========================================
|
|
702
760
|
/**
|
|
703
|
-
* Signs out current user
|
|
761
|
+
* Signs out the current user
|
|
704
762
|
* @returns {Promise<void>} Promise that resolves when sign-out completes
|
|
705
763
|
* @throws {FirekitAuthError} If sign-out fails
|
|
706
764
|
*
|
|
@@ -711,10 +769,12 @@ class FirekitAuth {
|
|
|
711
769
|
* ```
|
|
712
770
|
*/
|
|
713
771
|
async signOut() {
|
|
772
|
+
this.initializeServices();
|
|
773
|
+
if (!this.auth) {
|
|
774
|
+
throw new Error('Auth instance not available');
|
|
775
|
+
}
|
|
714
776
|
try {
|
|
715
|
-
|
|
716
|
-
this.notifyStateListeners();
|
|
717
|
-
// Clear all reCAPTCHA verifiers
|
|
777
|
+
// Clear reCAPTCHA verifiers
|
|
718
778
|
this.recaptchaVerifiers.forEach((verifier) => verifier.clear());
|
|
719
779
|
this.recaptchaVerifiers.clear();
|
|
720
780
|
await signOut(this.auth);
|
|
@@ -722,10 +782,6 @@ class FirekitAuth {
|
|
|
722
782
|
catch (error) {
|
|
723
783
|
this.handleAuthError(error);
|
|
724
784
|
}
|
|
725
|
-
finally {
|
|
726
|
-
this.authState.loading = false;
|
|
727
|
-
this.notifyStateListeners();
|
|
728
|
-
}
|
|
729
785
|
}
|
|
730
786
|
// ========================================
|
|
731
787
|
// UTILITY METHODS
|
|
@@ -735,66 +791,68 @@ class FirekitAuth {
|
|
|
735
791
|
* @returns {boolean} True if user is authenticated
|
|
736
792
|
*/
|
|
737
793
|
isAuthenticated() {
|
|
738
|
-
|
|
794
|
+
this.initializeServices();
|
|
795
|
+
return this.authState.user !== null && !this.authState.user.isAnonymous;
|
|
739
796
|
}
|
|
740
797
|
/**
|
|
741
798
|
* Checks if user is anonymous
|
|
742
799
|
* @returns {boolean} True if user is anonymous
|
|
743
800
|
*/
|
|
744
801
|
isAnonymous() {
|
|
745
|
-
|
|
802
|
+
this.initializeServices();
|
|
803
|
+
return this.authState.user?.isAnonymous ?? false;
|
|
746
804
|
}
|
|
747
805
|
/**
|
|
748
806
|
* Checks if user's email is verified
|
|
749
807
|
* @returns {boolean} True if email is verified
|
|
750
808
|
*/
|
|
751
809
|
isEmailVerified() {
|
|
752
|
-
|
|
810
|
+
this.initializeServices();
|
|
811
|
+
return this.authState.user?.emailVerified ?? false;
|
|
753
812
|
}
|
|
754
813
|
/**
|
|
755
|
-
* Gets user's
|
|
814
|
+
* Gets user's email address
|
|
756
815
|
* @returns {string | null} User's email or null
|
|
757
816
|
*/
|
|
758
817
|
getUserEmail() {
|
|
759
|
-
|
|
818
|
+
this.initializeServices();
|
|
819
|
+
return this.authState.user?.email ?? null;
|
|
760
820
|
}
|
|
761
821
|
/**
|
|
762
822
|
* Gets user's display name
|
|
763
823
|
* @returns {string | null} User's display name or null
|
|
764
824
|
*/
|
|
765
825
|
getUserDisplayName() {
|
|
766
|
-
|
|
826
|
+
this.initializeServices();
|
|
827
|
+
return this.authState.user?.displayName ?? null;
|
|
767
828
|
}
|
|
768
829
|
/**
|
|
769
830
|
* Gets user's photo URL
|
|
770
831
|
* @returns {string | null} User's photo URL or null
|
|
771
832
|
*/
|
|
772
833
|
getUserPhotoURL() {
|
|
773
|
-
|
|
834
|
+
this.initializeServices();
|
|
835
|
+
return this.authState.user?.photoURL ?? null;
|
|
774
836
|
}
|
|
775
837
|
/**
|
|
776
|
-
* Gets user's
|
|
838
|
+
* Gets user's unique ID
|
|
777
839
|
* @returns {string | null} User's UID or null
|
|
778
840
|
*/
|
|
779
841
|
getUserId() {
|
|
780
|
-
|
|
842
|
+
this.initializeServices();
|
|
843
|
+
return this.authState.user?.uid ?? null;
|
|
781
844
|
}
|
|
782
845
|
/**
|
|
783
|
-
* Cleans up
|
|
784
|
-
* @returns {Promise<void>} Promise that resolves when cleanup
|
|
846
|
+
* Cleans up resources and listeners
|
|
847
|
+
* @returns {Promise<void>} Promise that resolves when cleanup completes
|
|
785
848
|
*/
|
|
786
849
|
async cleanup() {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
// Clear all reCAPTCHA verifiers
|
|
850
|
+
this.initializeServices();
|
|
851
|
+
// Clear reCAPTCHA verifiers
|
|
790
852
|
this.recaptchaVerifiers.forEach((verifier) => verifier.clear());
|
|
791
853
|
this.recaptchaVerifiers.clear();
|
|
792
|
-
//
|
|
793
|
-
this.
|
|
794
|
-
user: null,
|
|
795
|
-
loading: false,
|
|
796
|
-
initialized: false
|
|
797
|
-
};
|
|
854
|
+
// Clear state listeners
|
|
855
|
+
this.stateListeners.clear();
|
|
798
856
|
}
|
|
799
857
|
}
|
|
800
858
|
/**
|
|
@@ -48,6 +48,7 @@ declare class FirekitUserStore {
|
|
|
48
48
|
private static instance;
|
|
49
49
|
private auth;
|
|
50
50
|
private firestore;
|
|
51
|
+
private _servicesInitialized;
|
|
51
52
|
/** Current user profile state */
|
|
52
53
|
private _user;
|
|
53
54
|
/** Loading state indicator */
|
|
@@ -78,6 +79,11 @@ declare class FirekitUserStore {
|
|
|
78
79
|
* @returns {FirekitUserStore} The FirekitUserStore instance
|
|
79
80
|
*/
|
|
80
81
|
static getInstance(): FirekitUserStore;
|
|
82
|
+
/**
|
|
83
|
+
* Initializes Firebase services and auth state listener
|
|
84
|
+
* @private
|
|
85
|
+
*/
|
|
86
|
+
private initializeServices;
|
|
81
87
|
/**
|
|
82
88
|
* Initializes the authentication state listener
|
|
83
89
|
* @private
|
|
@@ -31,8 +31,9 @@ import { mapFirebaseUserToProfile, updateUserInFirestore, createAuthError, valid
|
|
|
31
31
|
*/
|
|
32
32
|
class FirekitUserStore {
|
|
33
33
|
static instance;
|
|
34
|
-
auth =
|
|
35
|
-
firestore =
|
|
34
|
+
auth = null;
|
|
35
|
+
firestore = null;
|
|
36
|
+
_servicesInitialized = false;
|
|
36
37
|
// ========================================
|
|
37
38
|
// REACTIVE STATE (Svelte 5 Runes)
|
|
38
39
|
// ========================================
|
|
@@ -64,13 +65,8 @@ class FirekitUserStore {
|
|
|
64
65
|
/** Derived: User's phone number */
|
|
65
66
|
_userPhoneNumber = $derived(this._user?.phoneNumber ?? null);
|
|
66
67
|
constructor() {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
if (!this.firestore) {
|
|
71
|
-
throw new Error('Firestore instance not available');
|
|
72
|
-
}
|
|
73
|
-
this.initializeAuthStateListener();
|
|
68
|
+
// Don't initialize Firebase services in constructor
|
|
69
|
+
// They will be initialized lazily when needed
|
|
74
70
|
}
|
|
75
71
|
/**
|
|
76
72
|
* Gets singleton instance of FirekitUserStore
|
|
@@ -82,11 +78,35 @@ class FirekitUserStore {
|
|
|
82
78
|
}
|
|
83
79
|
return FirekitUserStore.instance;
|
|
84
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Initializes Firebase services and auth state listener
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
initializeServices() {
|
|
86
|
+
if (this._servicesInitialized)
|
|
87
|
+
return;
|
|
88
|
+
try {
|
|
89
|
+
this.auth = firebaseService.getAuthInstance();
|
|
90
|
+
this.firestore = firebaseService.getDbInstance();
|
|
91
|
+
this._servicesInitialized = true;
|
|
92
|
+
this.initializeAuthStateListener();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('Failed to initialize Firebase services:', error);
|
|
96
|
+
this._error = error instanceof Error ? error : new Error(String(error));
|
|
97
|
+
this._loading = false;
|
|
98
|
+
this._initialized = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
85
101
|
/**
|
|
86
102
|
* Initializes the authentication state listener
|
|
87
103
|
* @private
|
|
88
104
|
*/
|
|
89
105
|
initializeAuthStateListener() {
|
|
106
|
+
if (!this.auth) {
|
|
107
|
+
console.error('Auth instance not available');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
90
110
|
onAuthStateChanged(this.auth, (firebaseUser) => {
|
|
91
111
|
this._user = firebaseUser ? this.mapFirebaseUserToProfile(firebaseUser) : null;
|
|
92
112
|
this._loading = false;
|
|
@@ -111,6 +131,9 @@ class FirekitUserStore {
|
|
|
111
131
|
* @private
|
|
112
132
|
*/
|
|
113
133
|
async updateUserInFirestore(user) {
|
|
134
|
+
if (!this.firestore) {
|
|
135
|
+
throw new Error('Firestore instance not available');
|
|
136
|
+
}
|
|
114
137
|
await updateUserInFirestore(this.firestore, user);
|
|
115
138
|
}
|
|
116
139
|
// ========================================
|
|
@@ -118,50 +141,62 @@ class FirekitUserStore {
|
|
|
118
141
|
// ========================================
|
|
119
142
|
/** Current user profile */
|
|
120
143
|
get user() {
|
|
144
|
+
this.initializeServices();
|
|
121
145
|
return this._user;
|
|
122
146
|
}
|
|
123
147
|
/** Current loading state */
|
|
124
148
|
get loading() {
|
|
149
|
+
this.initializeServices();
|
|
125
150
|
return this._loading;
|
|
126
151
|
}
|
|
127
152
|
/** Whether auth has been initialized */
|
|
128
153
|
get initialized() {
|
|
154
|
+
this.initializeServices();
|
|
129
155
|
return this._initialized;
|
|
130
156
|
}
|
|
131
157
|
/** Current error state */
|
|
132
158
|
get error() {
|
|
159
|
+
this.initializeServices();
|
|
133
160
|
return this._error;
|
|
134
161
|
}
|
|
135
162
|
/** Whether user is authenticated (not anonymous) */
|
|
136
163
|
get isAuthenticated() {
|
|
164
|
+
this.initializeServices();
|
|
137
165
|
return this._isAuthenticated;
|
|
138
166
|
}
|
|
139
167
|
/** Whether user is anonymous */
|
|
140
168
|
get isAnonymous() {
|
|
169
|
+
this.initializeServices();
|
|
141
170
|
return this._isAnonymous;
|
|
142
171
|
}
|
|
143
172
|
/** Whether user's email is verified */
|
|
144
173
|
get isEmailVerified() {
|
|
174
|
+
this.initializeServices();
|
|
145
175
|
return this._isEmailVerified;
|
|
146
176
|
}
|
|
147
177
|
/** User's email address */
|
|
148
178
|
get email() {
|
|
179
|
+
this.initializeServices();
|
|
149
180
|
return this._userEmail;
|
|
150
181
|
}
|
|
151
182
|
/** User's display name */
|
|
152
183
|
get displayName() {
|
|
184
|
+
this.initializeServices();
|
|
153
185
|
return this._userDisplayName;
|
|
154
186
|
}
|
|
155
187
|
/** User's photo URL */
|
|
156
188
|
get photoURL() {
|
|
189
|
+
this.initializeServices();
|
|
157
190
|
return this._userPhotoURL;
|
|
158
191
|
}
|
|
159
192
|
/** User's unique ID */
|
|
160
193
|
get uid() {
|
|
194
|
+
this.initializeServices();
|
|
161
195
|
return this._userId;
|
|
162
196
|
}
|
|
163
197
|
/** User's phone number */
|
|
164
198
|
get phoneNumber() {
|
|
199
|
+
this.initializeServices();
|
|
165
200
|
return this._userPhoneNumber;
|
|
166
201
|
}
|
|
167
202
|
// ========================================
|
|
@@ -179,6 +214,10 @@ class FirekitUserStore {
|
|
|
179
214
|
* ```
|
|
180
215
|
*/
|
|
181
216
|
async updateDisplayName(displayName) {
|
|
217
|
+
this.initializeServices();
|
|
218
|
+
if (!this.auth) {
|
|
219
|
+
throw new Error('Auth instance not available');
|
|
220
|
+
}
|
|
182
221
|
const currentUser = validateCurrentUser(this.auth);
|
|
183
222
|
try {
|
|
184
223
|
this._loading = true;
|
|
@@ -209,6 +248,10 @@ class FirekitUserStore {
|
|
|
209
248
|
* ```
|
|
210
249
|
*/
|
|
211
250
|
async updatePhotoURL(photoURL) {
|
|
251
|
+
this.initializeServices();
|
|
252
|
+
if (!this.auth) {
|
|
253
|
+
throw new Error('Auth instance not available');
|
|
254
|
+
}
|
|
212
255
|
const currentUser = validateCurrentUser(this.auth);
|
|
213
256
|
try {
|
|
214
257
|
this._loading = true;
|
|
@@ -242,6 +285,10 @@ class FirekitUserStore {
|
|
|
242
285
|
* ```
|
|
243
286
|
*/
|
|
244
287
|
async updateProfile(profileData) {
|
|
288
|
+
this.initializeServices();
|
|
289
|
+
if (!this.auth) {
|
|
290
|
+
throw new Error('Auth instance not available');
|
|
291
|
+
}
|
|
245
292
|
const currentUser = validateCurrentUser(this.auth);
|
|
246
293
|
try {
|
|
247
294
|
this._loading = true;
|
|
@@ -276,6 +323,10 @@ class FirekitUserStore {
|
|
|
276
323
|
* ```
|
|
277
324
|
*/
|
|
278
325
|
async updateEmail(newEmail) {
|
|
326
|
+
this.initializeServices();
|
|
327
|
+
if (!this.auth) {
|
|
328
|
+
throw new Error('Auth instance not available');
|
|
329
|
+
}
|
|
279
330
|
const currentUser = validateCurrentUser(this.auth);
|
|
280
331
|
try {
|
|
281
332
|
this._loading = true;
|
|
@@ -306,6 +357,10 @@ class FirekitUserStore {
|
|
|
306
357
|
* ```
|
|
307
358
|
*/
|
|
308
359
|
async updatePassword(newPassword) {
|
|
360
|
+
this.initializeServices();
|
|
361
|
+
if (!this.auth) {
|
|
362
|
+
throw new Error('Auth instance not available');
|
|
363
|
+
}
|
|
309
364
|
const currentUser = validateCurrentUser(this.auth);
|
|
310
365
|
try {
|
|
311
366
|
this._loading = true;
|
|
@@ -333,6 +388,10 @@ class FirekitUserStore {
|
|
|
333
388
|
* ```
|
|
334
389
|
*/
|
|
335
390
|
async sendEmailVerification() {
|
|
391
|
+
this.initializeServices();
|
|
392
|
+
if (!this.auth) {
|
|
393
|
+
throw new Error('Auth instance not available');
|
|
394
|
+
}
|
|
336
395
|
const currentUser = validateCurrentUser(this.auth);
|
|
337
396
|
try {
|
|
338
397
|
await sendEmailVerification(currentUser);
|
|
@@ -353,6 +412,10 @@ class FirekitUserStore {
|
|
|
353
412
|
* ```
|
|
354
413
|
*/
|
|
355
414
|
async reloadUser() {
|
|
415
|
+
this.initializeServices();
|
|
416
|
+
if (!this.auth) {
|
|
417
|
+
throw new Error('Auth instance not available');
|
|
418
|
+
}
|
|
356
419
|
const currentUser = validateCurrentUser(this.auth);
|
|
357
420
|
try {
|
|
358
421
|
this._loading = true;
|
|
@@ -384,6 +447,10 @@ class FirekitUserStore {
|
|
|
384
447
|
* ```
|
|
385
448
|
*/
|
|
386
449
|
async getIdToken(forceRefresh = false) {
|
|
450
|
+
this.initializeServices();
|
|
451
|
+
if (!this.auth) {
|
|
452
|
+
throw new Error('Auth instance not available');
|
|
453
|
+
}
|
|
387
454
|
const currentUser = validateCurrentUser(this.auth);
|
|
388
455
|
try {
|
|
389
456
|
return await getIdToken(currentUser, forceRefresh);
|
|
@@ -407,7 +474,8 @@ class FirekitUserStore {
|
|
|
407
474
|
* ```
|
|
408
475
|
*/
|
|
409
476
|
async getExtendedUserData() {
|
|
410
|
-
|
|
477
|
+
this.initializeServices();
|
|
478
|
+
if (!this._user?.uid || !this.firestore) {
|
|
411
479
|
return null;
|
|
412
480
|
}
|
|
413
481
|
try {
|
|
@@ -438,9 +506,13 @@ class FirekitUserStore {
|
|
|
438
506
|
* ```
|
|
439
507
|
*/
|
|
440
508
|
async updateExtendedUserData(data) {
|
|
509
|
+
this.initializeServices();
|
|
441
510
|
if (!this._user?.uid) {
|
|
442
511
|
throw new FirekitAuthError(AuthErrorCode.USER_NOT_FOUND, 'No authenticated user found.');
|
|
443
512
|
}
|
|
513
|
+
if (!this.firestore) {
|
|
514
|
+
throw new Error('Firestore instance not available');
|
|
515
|
+
}
|
|
444
516
|
try {
|
|
445
517
|
const userRef = doc(this.firestore, 'users', this._user.uid);
|
|
446
518
|
await setDoc(userRef, {
|
|
@@ -469,6 +541,7 @@ class FirekitUserStore {
|
|
|
469
541
|
* ```
|
|
470
542
|
*/
|
|
471
543
|
async waitForAuth() {
|
|
544
|
+
this.initializeServices();
|
|
472
545
|
return new Promise((resolve) => {
|
|
473
546
|
if (this._initialized) {
|
|
474
547
|
resolve(this._user);
|
|
@@ -508,6 +581,9 @@ class FirekitUserStore {
|
|
|
508
581
|
this._loading = true;
|
|
509
582
|
this._initialized = false;
|
|
510
583
|
this._error = null;
|
|
584
|
+
this._servicesInitialized = false;
|
|
585
|
+
this.auth = null;
|
|
586
|
+
this.firestore = null;
|
|
511
587
|
}
|
|
512
588
|
}
|
|
513
589
|
/**
|