svelte-firekit 0.1.0 → 0.1.2

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.
@@ -36,8 +36,14 @@
36
36
  fallback?: Snippet<[]>;
37
37
  } = $props();
38
38
 
39
- // Get Firebase Auth instance
40
- const auth = firebaseService.getAuthInstance();
39
+ // Get Firebase Auth instance with error handling
40
+ let auth: Auth | null = null;
41
+ try {
42
+ auth = firebaseService.getAuthInstance();
43
+ } catch (error) {
44
+ console.warn('Firebase Auth not available:', error);
45
+ }
46
+
41
47
  if (!auth) {
42
48
  throw new Error('Firebase Auth instance not available');
43
49
  }
package/dist/firebase.js CHANGED
@@ -129,9 +129,21 @@ class FirebaseService {
129
129
  */
130
130
  getDbInstance() {
131
131
  if (!this.db) {
132
- this.getFirebaseApp();
133
- if (!this.db) {
134
- throw new FirebaseServiceError('Firestore instance not available', 'firestore');
132
+ try {
133
+ this.getFirebaseApp();
134
+ if (!this.db) {
135
+ // If we're not in a browser environment, Firestore won't be available
136
+ if (!this.isBrowser) {
137
+ throw new FirebaseServiceError('Firestore is not available in server environment', 'firestore');
138
+ }
139
+ throw new FirebaseServiceError('Firestore instance not available', 'firestore');
140
+ }
141
+ }
142
+ catch (error) {
143
+ if (error instanceof FirebaseServiceError) {
144
+ throw error;
145
+ }
146
+ throw new FirebaseServiceError('Failed to initialize Firestore', 'firestore');
135
147
  }
136
148
  }
137
149
  return this.db;
@@ -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,45 @@ 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
+ // Try to get Firestore instance, but don't fail if it's not available
69
+ try {
70
+ this.firestore = firebaseService.getDbInstance();
71
+ }
72
+ catch (firestoreError) {
73
+ console.warn('Firestore not available, continuing without Firestore integration:', firestoreError);
74
+ this.firestore = null;
75
+ }
76
+ this._servicesInitialized = true;
77
+ this.initializeAuthStateListener();
78
+ }
79
+ catch (error) {
80
+ console.error('Failed to initialize Firebase services:', error);
81
+ this.authState = {
82
+ user: null,
83
+ loading: false,
84
+ initialized: true
85
+ };
86
+ this.notifyStateListeners();
87
+ }
88
+ }
63
89
  /**
64
90
  * Initializes the authentication state listener
65
91
  * @private
66
92
  */
67
93
  initializeAuthStateListener() {
94
+ if (!this.auth) {
95
+ console.error('Auth instance not available');
96
+ return;
97
+ }
68
98
  onAuthStateChanged(this.auth, (user) => {
69
99
  this.authState = {
70
100
  user: user ? this.mapFirebaseUserToProfile(user) : null,
@@ -101,6 +131,10 @@ class FirekitAuth {
101
131
  * @private
102
132
  */
103
133
  async updateUserInFirestore(user) {
134
+ if (!this.firestore) {
135
+ console.warn('Firestore not available, skipping user update in Firestore');
136
+ return;
137
+ }
104
138
  await updateUserInFirestore(this.firestore, user);
105
139
  }
106
140
  /**
@@ -115,6 +149,7 @@ class FirekitAuth {
115
149
  * @returns {AuthState} Current authentication state
116
150
  */
117
151
  getState() {
152
+ this.initializeServices();
118
153
  return { ...this.authState };
119
154
  }
120
155
  /**
@@ -122,13 +157,15 @@ class FirekitAuth {
122
157
  * @returns {User | null} Current Firebase user or null
123
158
  */
124
159
  getCurrentUser() {
125
- return this.auth.currentUser;
160
+ this.initializeServices();
161
+ return this.auth?.currentUser ?? null;
126
162
  }
127
163
  /**
128
164
  * Gets the current user profile
129
165
  * @returns {UserProfile | null} Current user profile or null
130
166
  */
131
167
  getCurrentUserProfile() {
168
+ this.initializeServices();
132
169
  return this.authState.user;
133
170
  }
134
171
  /**
@@ -136,6 +173,7 @@ class FirekitAuth {
136
173
  * @returns {Promise<UserProfile | null>} Promise that resolves when auth is initialized
137
174
  */
138
175
  async waitForAuth() {
176
+ this.initializeServices();
139
177
  return new Promise((resolve) => {
140
178
  if (this.authState.initialized) {
141
179
  resolve(this.authState.user);
@@ -155,6 +193,7 @@ class FirekitAuth {
155
193
  * @returns {Function} Unsubscribe function
156
194
  */
157
195
  onAuthStateChanged(callback) {
196
+ this.initializeServices();
158
197
  this.stateListeners.add(callback);
159
198
  // Immediately call with current state
160
199
  callback(this.authState);
@@ -183,6 +222,10 @@ class FirekitAuth {
183
222
  * ```
184
223
  */
185
224
  async signInWithEmail(email, password) {
225
+ this.initializeServices();
226
+ if (!this.auth) {
227
+ throw new Error('Auth instance not available');
228
+ }
186
229
  try {
187
230
  this.authState.loading = true;
188
231
  this.notifyStateListeners();
@@ -216,6 +259,10 @@ class FirekitAuth {
216
259
  * ```
217
260
  */
218
261
  async signInWithGoogle() {
262
+ this.initializeServices();
263
+ if (!this.auth) {
264
+ throw new Error('Auth instance not available');
265
+ }
219
266
  try {
220
267
  this.authState.loading = true;
221
268
  this.notifyStateListeners();
@@ -238,6 +285,10 @@ class FirekitAuth {
238
285
  * @throws {FirekitAuthError} If sign-in fails
239
286
  */
240
287
  async signInWithFacebook() {
288
+ this.initializeServices();
289
+ if (!this.auth) {
290
+ throw new Error('Auth instance not available');
291
+ }
241
292
  try {
242
293
  this.authState.loading = true;
243
294
  this.notifyStateListeners();
@@ -260,6 +311,10 @@ class FirekitAuth {
260
311
  * @throws {FirekitAuthError} If sign-in fails
261
312
  */
262
313
  async signInWithApple() {
314
+ this.initializeServices();
315
+ if (!this.auth) {
316
+ throw new Error('Auth instance not available');
317
+ }
263
318
  try {
264
319
  this.authState.loading = true;
265
320
  this.notifyStateListeners();
@@ -288,6 +343,10 @@ class FirekitAuth {
288
343
  * ```
289
344
  */
290
345
  async signInAnonymously() {
346
+ this.initializeServices();
347
+ if (!this.auth) {
348
+ throw new Error('Auth instance not available');
349
+ }
291
350
  try {
292
351
  this.authState.loading = true;
293
352
  this.notifyStateListeners();
@@ -317,6 +376,10 @@ class FirekitAuth {
317
376
  * ```
318
377
  */
319
378
  async signInWithPhoneNumber(phoneNumber, recaptchaContainerId) {
379
+ this.initializeServices();
380
+ if (!this.auth) {
381
+ throw new Error('Auth instance not available');
382
+ }
320
383
  try {
321
384
  this.authState.loading = true;
322
385
  this.notifyStateListeners();
@@ -339,11 +402,11 @@ class FirekitAuth {
339
402
  const confirmationResult = await firebaseSignInWithPhoneNumber(this.auth, phoneNumber, recaptchaVerifier);
340
403
  return {
341
404
  verificationId: confirmationResult.verificationId,
342
- confirm: async (code) => {
405
+ confirm: async (verificationCode) => {
343
406
  try {
344
- const result = await confirmationResult.confirm(code);
345
- await this.updateUserInFirestore(result.user);
346
- return this.mapFirebaseUserToProfile(result.user);
407
+ const userCredential = await confirmationResult.confirm(verificationCode);
408
+ await this.updateUserInFirestore(userCredential.user);
409
+ return this.mapFirebaseUserToProfile(userCredential.user);
347
410
  }
348
411
  catch (error) {
349
412
  this.handleAuthError(error);
@@ -387,6 +450,10 @@ class FirekitAuth {
387
450
  * ```
388
451
  */
389
452
  async registerWithEmail(email, password, displayName, sendVerification = true) {
453
+ this.initializeServices();
454
+ if (!this.auth) {
455
+ throw new Error('Auth instance not available');
456
+ }
390
457
  try {
391
458
  this.authState.loading = true;
392
459
  this.notifyStateListeners();
@@ -427,6 +494,10 @@ class FirekitAuth {
427
494
  * ```
428
495
  */
429
496
  async sendPasswordReset(email) {
497
+ this.initializeServices();
498
+ if (!this.auth) {
499
+ throw new Error('Auth instance not available');
500
+ }
430
501
  try {
431
502
  await sendPasswordResetEmail(this.auth, email);
432
503
  }
@@ -442,6 +513,10 @@ class FirekitAuth {
442
513
  * @throws {FirekitAuthError} If reset fails
443
514
  */
444
515
  async confirmPasswordReset(code, newPassword) {
516
+ this.initializeServices();
517
+ if (!this.auth) {
518
+ throw new Error('Auth instance not available');
519
+ }
445
520
  try {
446
521
  await confirmPasswordReset(this.auth, code, newPassword);
447
522
  }
@@ -466,7 +541,8 @@ class FirekitAuth {
466
541
  * ```
467
542
  */
468
543
  async updatePassword(newPassword, currentPassword) {
469
- if (!this.auth.currentUser) {
544
+ this.initializeServices();
545
+ if (!this.auth?.currentUser) {
470
546
  return {
471
547
  success: false,
472
548
  message: 'No authenticated user found.',
@@ -517,7 +593,8 @@ class FirekitAuth {
517
593
  * ```
518
594
  */
519
595
  async updateUserProfile(profile) {
520
- if (!this.auth.currentUser) {
596
+ this.initializeServices();
597
+ if (!this.auth?.currentUser) {
521
598
  throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
522
599
  }
523
600
  try {
@@ -540,7 +617,8 @@ class FirekitAuth {
540
617
  * ```
541
618
  */
542
619
  async updateEmail(newEmail) {
543
- if (!this.auth.currentUser) {
620
+ this.initializeServices();
621
+ if (!this.auth?.currentUser) {
544
622
  throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
545
623
  }
546
624
  try {
@@ -551,9 +629,6 @@ class FirekitAuth {
551
629
  this.handleAuthError(error);
552
630
  }
553
631
  }
554
- // ========================================
555
- // EMAIL VERIFICATION METHODS
556
- // ========================================
557
632
  /**
558
633
  * Sends email verification to current user
559
634
  * @returns {Promise<void>} Promise that resolves when verification email is sent
@@ -562,11 +637,11 @@ class FirekitAuth {
562
637
  * @example
563
638
  * ```typescript
564
639
  * await firekitAuth.sendEmailVerification();
565
- * console.log("Verification email sent");
566
640
  * ```
567
641
  */
568
642
  async sendEmailVerification() {
569
- if (!this.auth.currentUser) {
643
+ this.initializeServices();
644
+ if (!this.auth?.currentUser) {
570
645
  throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
571
646
  }
572
647
  try {
@@ -577,12 +652,18 @@ class FirekitAuth {
577
652
  }
578
653
  }
579
654
  /**
580
- * Reloads current user to get updated email verification status
655
+ * Reloads user to get updated data
581
656
  * @returns {Promise<void>} Promise that resolves when user is reloaded
582
657
  * @throws {FirekitAuthError} If reload fails
658
+ *
659
+ * @example
660
+ * ```typescript
661
+ * await firekitAuth.reloadUser();
662
+ * ```
583
663
  */
584
664
  async reloadUser() {
585
- if (!this.auth.currentUser) {
665
+ this.initializeServices();
666
+ if (!this.auth?.currentUser) {
586
667
  throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
587
668
  }
588
669
  try {
@@ -593,9 +674,6 @@ class FirekitAuth {
593
674
  this.handleAuthError(error);
594
675
  }
595
676
  }
596
- // ========================================
597
- // TOKEN METHODS
598
- // ========================================
599
677
  /**
600
678
  * Gets the current user's ID token
601
679
  * @param {boolean} [forceRefresh=false] Whether to force token refresh
@@ -605,11 +683,11 @@ class FirekitAuth {
605
683
  * @example
606
684
  * ```typescript
607
685
  * const token = await firekitAuth.getIdToken();
608
- * // Use token for authenticated API calls
609
686
  * ```
610
687
  */
611
688
  async getIdToken(forceRefresh = false) {
612
- if (!this.auth.currentUser) {
689
+ this.initializeServices();
690
+ if (!this.auth?.currentUser) {
613
691
  throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
614
692
  }
615
693
  try {
@@ -619,31 +697,26 @@ class FirekitAuth {
619
697
  this.handleAuthError(error);
620
698
  }
621
699
  }
622
- // ========================================
623
- // ACCOUNT MANAGEMENT
624
- // ========================================
625
700
  /**
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
701
+ * Reauthenticates user with current password
630
702
  * @private
631
703
  */
632
704
  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.');
705
+ this.initializeServices();
706
+ if (!this.auth?.currentUser || !this.auth.currentUser.email) {
707
+ throw new FirekitAuthError('auth/no-current-user', 'No authenticated user with email found.');
635
708
  }
636
709
  try {
637
710
  const credential = EmailAuthProvider.credential(this.auth.currentUser.email, currentPassword);
638
711
  await reauthenticateWithCredential(this.auth.currentUser, credential);
639
712
  }
640
713
  catch (error) {
641
- throw new FirekitAuthError(error.code || 'auth/reauthentication-failed', `Reauthentication failed: ${error.message || 'Unknown error occurred.'}`);
714
+ this.handleAuthError(error);
642
715
  }
643
716
  }
644
717
  /**
645
- * Deletes user account and associated data
646
- * @param {string} [currentPassword] Current password for reauthentication (required for email/password accounts)
718
+ * Deletes user account
719
+ * @param {string} [currentPassword] Current password for reauthentication
647
720
  * @returns {Promise<AccountDeletionResult>} Promise resolving to deletion result
648
721
  *
649
722
  * @example
@@ -651,11 +724,14 @@ class FirekitAuth {
651
724
  * const result = await firekitAuth.deleteAccount("currentPassword123");
652
725
  * if (result.success) {
653
726
  * console.log("Account deleted successfully");
727
+ * } else {
728
+ * console.error("Deletion failed:", result.message);
654
729
  * }
655
730
  * ```
656
731
  */
657
732
  async deleteAccount(currentPassword) {
658
- if (!this.auth.currentUser) {
733
+ this.initializeServices();
734
+ if (!this.auth?.currentUser) {
659
735
  return {
660
736
  success: false,
661
737
  message: 'No authenticated user found.'
@@ -663,19 +739,21 @@ class FirekitAuth {
663
739
  }
664
740
  try {
665
741
  const user = this.auth.currentUser;
666
- // Reauthenticate if password provided (required for email/password accounts)
742
+ // Reauthenticate if password provided
667
743
  if (currentPassword) {
668
744
  await this.reauthenticateUser(currentPassword);
669
745
  }
670
- // Delete user data from Firestore first
671
- try {
672
- const userRef = doc(this.firestore, 'users', user.uid);
673
- await setDoc(userRef, { deleted: true, deletedAt: serverTimestamp() }, { merge: true });
674
- }
675
- catch (firestoreError) {
676
- console.error('Failed to mark user as deleted in Firestore:', firestoreError);
746
+ // Delete user data from Firestore first (if available)
747
+ if (this.firestore) {
748
+ try {
749
+ const userRef = doc(this.firestore, 'users', user.uid);
750
+ await setDoc(userRef, { deleted: true, deletedAt: serverTimestamp() }, { merge: true });
751
+ }
752
+ catch (firestoreError) {
753
+ console.warn('Failed to update Firestore before account deletion:', firestoreError);
754
+ }
677
755
  }
678
- // Delete the Firebase Auth account
756
+ // Delete the user account
679
757
  await deleteUser(user);
680
758
  return {
681
759
  success: true,
@@ -683,24 +761,14 @@ class FirekitAuth {
683
761
  };
684
762
  }
685
763
  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
764
  return {
694
765
  success: false,
695
766
  message: error.message || 'Failed to delete account.'
696
767
  };
697
768
  }
698
769
  }
699
- // ========================================
700
- // SIGN OUT
701
- // ========================================
702
770
  /**
703
- * Signs out current user
771
+ * Signs out the current user
704
772
  * @returns {Promise<void>} Promise that resolves when sign-out completes
705
773
  * @throws {FirekitAuthError} If sign-out fails
706
774
  *
@@ -711,10 +779,12 @@ class FirekitAuth {
711
779
  * ```
712
780
  */
713
781
  async signOut() {
782
+ this.initializeServices();
783
+ if (!this.auth) {
784
+ throw new Error('Auth instance not available');
785
+ }
714
786
  try {
715
- this.authState.loading = true;
716
- this.notifyStateListeners();
717
- // Clear all reCAPTCHA verifiers
787
+ // Clear reCAPTCHA verifiers
718
788
  this.recaptchaVerifiers.forEach((verifier) => verifier.clear());
719
789
  this.recaptchaVerifiers.clear();
720
790
  await signOut(this.auth);
@@ -722,10 +792,6 @@ class FirekitAuth {
722
792
  catch (error) {
723
793
  this.handleAuthError(error);
724
794
  }
725
- finally {
726
- this.authState.loading = false;
727
- this.notifyStateListeners();
728
- }
729
795
  }
730
796
  // ========================================
731
797
  // UTILITY METHODS
@@ -735,66 +801,68 @@ class FirekitAuth {
735
801
  * @returns {boolean} True if user is authenticated
736
802
  */
737
803
  isAuthenticated() {
738
- return !!this.authState.user && !this.authState.user.isAnonymous;
804
+ this.initializeServices();
805
+ return this.authState.user !== null && !this.authState.user.isAnonymous;
739
806
  }
740
807
  /**
741
808
  * Checks if user is anonymous
742
809
  * @returns {boolean} True if user is anonymous
743
810
  */
744
811
  isAnonymous() {
745
- return !!this.authState.user?.isAnonymous;
812
+ this.initializeServices();
813
+ return this.authState.user?.isAnonymous ?? false;
746
814
  }
747
815
  /**
748
816
  * Checks if user's email is verified
749
817
  * @returns {boolean} True if email is verified
750
818
  */
751
819
  isEmailVerified() {
752
- return !!this.authState.user?.emailVerified;
820
+ this.initializeServices();
821
+ return this.authState.user?.emailVerified ?? false;
753
822
  }
754
823
  /**
755
- * Gets user's primary email address
824
+ * Gets user's email address
756
825
  * @returns {string | null} User's email or null
757
826
  */
758
827
  getUserEmail() {
759
- return this.authState.user?.email || null;
828
+ this.initializeServices();
829
+ return this.authState.user?.email ?? null;
760
830
  }
761
831
  /**
762
832
  * Gets user's display name
763
833
  * @returns {string | null} User's display name or null
764
834
  */
765
835
  getUserDisplayName() {
766
- return this.authState.user?.displayName || null;
836
+ this.initializeServices();
837
+ return this.authState.user?.displayName ?? null;
767
838
  }
768
839
  /**
769
840
  * Gets user's photo URL
770
841
  * @returns {string | null} User's photo URL or null
771
842
  */
772
843
  getUserPhotoURL() {
773
- return this.authState.user?.photoURL || null;
844
+ this.initializeServices();
845
+ return this.authState.user?.photoURL ?? null;
774
846
  }
775
847
  /**
776
- * Gets user's UID
848
+ * Gets user's unique ID
777
849
  * @returns {string | null} User's UID or null
778
850
  */
779
851
  getUserId() {
780
- return this.authState.user?.uid || null;
852
+ this.initializeServices();
853
+ return this.authState.user?.uid ?? null;
781
854
  }
782
855
  /**
783
- * Cleans up all resources
784
- * @returns {Promise<void>} Promise that resolves when cleanup is complete
856
+ * Cleans up resources and listeners
857
+ * @returns {Promise<void>} Promise that resolves when cleanup completes
785
858
  */
786
859
  async cleanup() {
787
- // Clear all listeners
788
- this.stateListeners.clear();
789
- // Clear all reCAPTCHA verifiers
860
+ this.initializeServices();
861
+ // Clear reCAPTCHA verifiers
790
862
  this.recaptchaVerifiers.forEach((verifier) => verifier.clear());
791
863
  this.recaptchaVerifiers.clear();
792
- // Reset state
793
- this.authState = {
794
- user: null,
795
- loading: false,
796
- initialized: false
797
- };
864
+ // Clear state listeners
865
+ this.stateListeners.clear();
798
866
  }
799
867
  }
800
868
  /**
@@ -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.2",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev",