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.
@@ -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 current user to get updated email verification status
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 current user with email/password
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 and associated data
299
- * @param {string} [currentPassword] Current password for reauthentication (required for email/password accounts)
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 primary email address
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 UID
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 all resources
360
- * @returns {Promise<void>} Promise that resolves when cleanup is complete
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
  }
@@ -32,8 +32,9 @@ import { mapFirebaseUserToProfile, updateUserInFirestore, createGoogleProvider,
32
32
  */
33
33
  class FirekitAuth {
34
34
  static instance;
35
- auth = firebaseService.getAuthInstance();
36
- firestore = firebaseService.getDbInstance();
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
- if (!this.auth) {
46
- throw new Error('Firebase Auth instance not available');
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
- return this.auth.currentUser;
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 (code) => {
397
+ confirm: async (verificationCode) => {
343
398
  try {
344
- const result = await confirmationResult.confirm(code);
345
- await this.updateUserInFirestore(result.user);
346
- return this.mapFirebaseUserToProfile(result.user);
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
- if (!this.auth.currentUser) {
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
- if (!this.auth.currentUser) {
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
- if (!this.auth.currentUser) {
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
- if (!this.auth.currentUser) {
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 current user to get updated email verification status
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
- if (!this.auth.currentUser) {
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
- if (!this.auth.currentUser) {
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 current user with email/password
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
- if (!this.auth.currentUser || !this.auth.currentUser.email) {
634
- throw new FirekitAuthError('auth/no-current-user', 'No authenticated user or email unavailable.');
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
- throw new FirekitAuthError(error.code || 'auth/reauthentication-failed', `Reauthentication failed: ${error.message || 'Unknown error occurred.'}`);
706
+ this.handleAuthError(error);
642
707
  }
643
708
  }
644
709
  /**
645
- * Deletes user account and associated data
646
- * @param {string} [currentPassword] Current password for reauthentication (required for email/password accounts)
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
- if (!this.auth.currentUser) {
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 (required for email/password accounts)
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.error('Failed to mark user as deleted in Firestore:', firestoreError);
744
+ console.warn('Failed to update Firestore before account deletion:', firestoreError);
677
745
  }
678
- // Delete the Firebase Auth account
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
- this.authState.loading = true;
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
- return !!this.authState.user && !this.authState.user.isAnonymous;
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
- return !!this.authState.user?.isAnonymous;
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
- return !!this.authState.user?.emailVerified;
810
+ this.initializeServices();
811
+ return this.authState.user?.emailVerified ?? false;
753
812
  }
754
813
  /**
755
- * Gets user's primary email address
814
+ * Gets user's email address
756
815
  * @returns {string | null} User's email or null
757
816
  */
758
817
  getUserEmail() {
759
- return this.authState.user?.email || null;
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
- return this.authState.user?.displayName || null;
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
- return this.authState.user?.photoURL || null;
834
+ this.initializeServices();
835
+ return this.authState.user?.photoURL ?? null;
774
836
  }
775
837
  /**
776
- * Gets user's UID
838
+ * Gets user's unique ID
777
839
  * @returns {string | null} User's UID or null
778
840
  */
779
841
  getUserId() {
780
- return this.authState.user?.uid || null;
842
+ this.initializeServices();
843
+ return this.authState.user?.uid ?? null;
781
844
  }
782
845
  /**
783
- * Cleans up all resources
784
- * @returns {Promise<void>} Promise that resolves when cleanup is complete
846
+ * Cleans up resources and listeners
847
+ * @returns {Promise<void>} Promise that resolves when cleanup completes
785
848
  */
786
849
  async cleanup() {
787
- // Clear all listeners
788
- this.stateListeners.clear();
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
- // Reset state
793
- this.authState = {
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 = firebaseService.getAuthInstance();
35
- firestore = firebaseService.getDbInstance();
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
- if (!this.auth) {
68
- throw new Error('Firebase Auth instance not available');
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
- if (!this._user?.uid) {
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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-firekit",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -62,4 +62,4 @@
62
62
  "dependencies": {
63
63
  "firebase": "^11.9.1"
64
64
  }
65
- }
65
+ }