svelte-firekit 0.0.25 → 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.
Files changed (95) hide show
  1. package/README.md +445 -213
  2. package/dist/components/Collection.svelte +150 -0
  3. package/dist/components/Collection.svelte.d.ts +27 -0
  4. package/dist/components/Ddoc.svelte +131 -0
  5. package/dist/components/Ddoc.svelte.d.ts +28 -0
  6. package/dist/components/Node.svelte +97 -0
  7. package/dist/components/Node.svelte.d.ts +23 -0
  8. package/dist/components/auth-guard.svelte +89 -0
  9. package/dist/components/auth-guard.svelte.d.ts +26 -0
  10. package/dist/components/custom-guard.svelte +122 -0
  11. package/dist/components/custom-guard.svelte.d.ts +31 -0
  12. package/dist/components/download-url.svelte +92 -0
  13. package/dist/components/download-url.svelte.d.ts +19 -0
  14. package/dist/components/firebase-app.svelte +30 -0
  15. package/dist/components/firebase-app.svelte.d.ts +7 -0
  16. package/dist/components/node-list.svelte +102 -0
  17. package/dist/components/node-list.svelte.d.ts +27 -0
  18. package/dist/components/signed-in.svelte +42 -0
  19. package/dist/components/signed-in.svelte.d.ts +11 -0
  20. package/dist/components/signed-out.svelte +42 -0
  21. package/dist/components/signed-out.svelte.d.ts +11 -0
  22. package/dist/components/storage-list.svelte +97 -0
  23. package/dist/components/storage-list.svelte.d.ts +26 -0
  24. package/dist/components/upload-task.svelte +108 -0
  25. package/dist/components/upload-task.svelte.d.ts +24 -0
  26. package/dist/config.js +17 -39
  27. package/dist/firebase.d.ts +43 -21
  28. package/dist/firebase.js +121 -35
  29. package/dist/index.d.ts +21 -13
  30. package/dist/index.js +27 -15
  31. package/dist/services/auth.d.ts +397 -0
  32. package/dist/services/auth.js +882 -0
  33. package/dist/services/collection.svelte.d.ts +286 -0
  34. package/dist/services/collection.svelte.js +871 -0
  35. package/dist/services/document.svelte.d.ts +288 -0
  36. package/dist/services/document.svelte.js +555 -0
  37. package/dist/services/mutations.d.ts +336 -0
  38. package/dist/services/mutations.js +1079 -0
  39. package/dist/services/presence.svelte.d.ts +141 -0
  40. package/dist/services/presence.svelte.js +727 -0
  41. package/dist/{realtime → services}/realtime.svelte.d.ts +3 -1
  42. package/dist/{realtime → services}/realtime.svelte.js +13 -7
  43. package/dist/services/storage.svelte.d.ts +257 -0
  44. package/dist/services/storage.svelte.js +374 -0
  45. package/dist/services/user.svelte.d.ts +296 -0
  46. package/dist/services/user.svelte.js +609 -0
  47. package/dist/types/auth.d.ts +158 -0
  48. package/dist/types/auth.js +106 -0
  49. package/dist/types/collection.d.ts +360 -0
  50. package/dist/types/collection.js +167 -0
  51. package/dist/types/document.d.ts +342 -0
  52. package/dist/types/document.js +148 -0
  53. package/dist/types/firebase.d.ts +44 -0
  54. package/dist/types/firebase.js +33 -0
  55. package/dist/types/index.d.ts +6 -0
  56. package/dist/types/index.js +4 -0
  57. package/dist/types/mutations.d.ts +387 -0
  58. package/dist/types/mutations.js +205 -0
  59. package/dist/types/presence.d.ts +282 -0
  60. package/dist/types/presence.js +80 -0
  61. package/dist/utils/errors.d.ts +21 -0
  62. package/dist/utils/errors.js +35 -0
  63. package/dist/utils/firestore.d.ts +9 -0
  64. package/dist/utils/firestore.js +33 -0
  65. package/dist/utils/index.d.ts +4 -0
  66. package/dist/utils/index.js +8 -0
  67. package/dist/utils/providers.d.ts +16 -0
  68. package/dist/utils/providers.js +30 -0
  69. package/dist/utils/user.d.ts +8 -0
  70. package/dist/utils/user.js +29 -0
  71. package/package.json +64 -64
  72. package/dist/auth/auth.d.ts +0 -117
  73. package/dist/auth/auth.js +0 -194
  74. package/dist/auth/presence.svelte.d.ts +0 -139
  75. package/dist/auth/presence.svelte.js +0 -373
  76. package/dist/auth/user.svelte.d.ts +0 -112
  77. package/dist/auth/user.svelte.js +0 -155
  78. package/dist/firestore/awaitable-doc.svelte.d.ts +0 -141
  79. package/dist/firestore/awaitable-doc.svelte.js +0 -183
  80. package/dist/firestore/batch-mutations.svelte.d.ts +0 -140
  81. package/dist/firestore/batch-mutations.svelte.js +0 -218
  82. package/dist/firestore/collection-group.svelte.d.ts +0 -78
  83. package/dist/firestore/collection-group.svelte.js +0 -120
  84. package/dist/firestore/collection.svelte.d.ts +0 -96
  85. package/dist/firestore/collection.svelte.js +0 -137
  86. package/dist/firestore/doc.svelte.d.ts +0 -90
  87. package/dist/firestore/doc.svelte.js +0 -131
  88. package/dist/firestore/document-mutations.svelte.d.ts +0 -164
  89. package/dist/firestore/document-mutations.svelte.js +0 -273
  90. package/dist/storage/download-url.svelte.d.ts +0 -83
  91. package/dist/storage/download-url.svelte.js +0 -114
  92. package/dist/storage/storage-list.svelte.d.ts +0 -89
  93. package/dist/storage/storage-list.svelte.js +0 -123
  94. package/dist/storage/upload-task.svelte.d.ts +0 -94
  95. package/dist/storage/upload-task.svelte.js +0 -138
@@ -0,0 +1,882 @@
1
+ /**
2
+ * @fileoverview FirekitAuth - Complete Firebase Authentication Service for Svelte
3
+ * @module FirekitAuth
4
+ * @version 1.0.0
5
+ */
6
+ import { EmailAuthProvider, PhoneAuthProvider, RecaptchaVerifier, signInWithEmailAndPassword, signInWithPopup, signInWithPhoneNumber as firebaseSignInWithPhoneNumber, signInWithCredential, signInAnonymously as firebaseSignInAnonymously, createUserWithEmailAndPassword, signOut, sendPasswordResetEmail, confirmPasswordReset, sendEmailVerification, updateProfile, updateEmail, updatePassword, reauthenticateWithCredential, deleteUser, reload, getIdToken, onAuthStateChanged } from 'firebase/auth';
7
+ import { doc, setDoc, serverTimestamp } from 'firebase/firestore';
8
+ import { firebaseService } from '../firebase.js';
9
+ import { AuthErrorCode, FirekitAuthError } from '../types/auth.js';
10
+ import { mapFirebaseUserToProfile, updateUserInFirestore, createGoogleProvider, createFacebookProvider, createAppleProvider, handleAuthError } from '../utils/index.js';
11
+ /**
12
+ * Comprehensive Firebase Authentication service for Svelte applications.
13
+ * Provides a complete authentication solution with automatic Firestore integration,
14
+ * error handling, and support for all major authentication methods.
15
+ *
16
+ * @class FirekitAuth
17
+ * @example
18
+ * ```typescript
19
+ * import { firekitAuth } from 'svelte-firekit';
20
+ *
21
+ * // Sign in with Google
22
+ * await firekitAuth.signInWithGoogle();
23
+ *
24
+ * // Register new user
25
+ * await firekitAuth.registerWithEmail("user@example.com", "password123", "John Doe");
26
+ *
27
+ * // Listen to auth state changes
28
+ * const unsubscribe = firekitAuth.onAuthStateChanged((user) => {
29
+ * console.log('User:', user);
30
+ * });
31
+ * ```
32
+ */
33
+ class FirekitAuth {
34
+ static instance;
35
+ auth = null;
36
+ firestore = null;
37
+ _servicesInitialized = false;
38
+ authState = {
39
+ user: null,
40
+ loading: true,
41
+ initialized: false
42
+ };
43
+ stateListeners = new Set();
44
+ recaptchaVerifiers = new Map();
45
+ constructor() {
46
+ // Don't initialize Firebase services in constructor
47
+ // They will be initialized lazily when needed
48
+ }
49
+ /**
50
+ * Gets singleton instance of FirekitAuth
51
+ * @returns {FirekitAuth} The FirekitAuth instance
52
+ */
53
+ static getInstance() {
54
+ if (!FirekitAuth.instance) {
55
+ FirekitAuth.instance = new FirekitAuth();
56
+ }
57
+ return FirekitAuth.instance;
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
+ }
82
+ /**
83
+ * Initializes the authentication state listener
84
+ * @private
85
+ */
86
+ initializeAuthStateListener() {
87
+ if (!this.auth) {
88
+ console.error('Auth instance not available');
89
+ return;
90
+ }
91
+ onAuthStateChanged(this.auth, (user) => {
92
+ this.authState = {
93
+ user: user ? this.mapFirebaseUserToProfile(user) : null,
94
+ loading: false,
95
+ initialized: true
96
+ };
97
+ this.notifyStateListeners();
98
+ }, (error) => {
99
+ console.error('Auth state change error:', error);
100
+ this.authState = {
101
+ user: null,
102
+ loading: false,
103
+ initialized: true
104
+ };
105
+ this.notifyStateListeners();
106
+ });
107
+ }
108
+ /**
109
+ * Notifies all state listeners of auth state changes
110
+ * @private
111
+ */
112
+ notifyStateListeners() {
113
+ this.stateListeners.forEach((listener) => listener(this.authState));
114
+ }
115
+ /**
116
+ * Maps Firebase User to UserProfile interface
117
+ * @private
118
+ */
119
+ mapFirebaseUserToProfile(user) {
120
+ return mapFirebaseUserToProfile(user);
121
+ }
122
+ /**
123
+ * Updates user data in Firestore with comprehensive profile information
124
+ * @private
125
+ */
126
+ async updateUserInFirestore(user) {
127
+ if (!this.firestore) {
128
+ throw new Error('Firestore instance not available');
129
+ }
130
+ await updateUserInFirestore(this.firestore, user);
131
+ }
132
+ /**
133
+ * Handles Firebase authentication errors and throws FirekitAuthError
134
+ * @private
135
+ */
136
+ handleAuthError(error) {
137
+ handleAuthError(error);
138
+ }
139
+ /**
140
+ * Gets the current authentication state
141
+ * @returns {AuthState} Current authentication state
142
+ */
143
+ getState() {
144
+ this.initializeServices();
145
+ return { ...this.authState };
146
+ }
147
+ /**
148
+ * Gets the current authenticated user
149
+ * @returns {User | null} Current Firebase user or null
150
+ */
151
+ getCurrentUser() {
152
+ this.initializeServices();
153
+ return this.auth?.currentUser ?? null;
154
+ }
155
+ /**
156
+ * Gets the current user profile
157
+ * @returns {UserProfile | null} Current user profile or null
158
+ */
159
+ getCurrentUserProfile() {
160
+ this.initializeServices();
161
+ return this.authState.user;
162
+ }
163
+ /**
164
+ * Waits for auth initialization to complete
165
+ * @returns {Promise<UserProfile | null>} Promise that resolves when auth is initialized
166
+ */
167
+ async waitForAuth() {
168
+ this.initializeServices();
169
+ return new Promise((resolve) => {
170
+ if (this.authState.initialized) {
171
+ resolve(this.authState.user);
172
+ return;
173
+ }
174
+ const unsubscribe = this.onAuthStateChanged((state) => {
175
+ if (state.initialized) {
176
+ unsubscribe();
177
+ resolve(state.user);
178
+ }
179
+ });
180
+ });
181
+ }
182
+ /**
183
+ * Subscribes to authentication state changes
184
+ * @param {Function} callback Callback function to handle state changes
185
+ * @returns {Function} Unsubscribe function
186
+ */
187
+ onAuthStateChanged(callback) {
188
+ this.initializeServices();
189
+ this.stateListeners.add(callback);
190
+ // Immediately call with current state
191
+ callback(this.authState);
192
+ return () => {
193
+ this.stateListeners.delete(callback);
194
+ };
195
+ }
196
+ // ========================================
197
+ // SIGN IN METHODS
198
+ // ========================================
199
+ /**
200
+ * Signs in user with email and password
201
+ * @param {string} email User's email address
202
+ * @param {string} password User's password
203
+ * @returns {Promise<UserProfile>} Promise resolving to user profile
204
+ * @throws {FirekitAuthError} If sign-in fails
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * try {
209
+ * const user = await firekitAuth.signInWithEmail("user@example.com", "password123");
210
+ * console.log("Signed in:", user.displayName);
211
+ * } catch (error) {
212
+ * console.error("Sign-in failed:", error.message);
213
+ * }
214
+ * ```
215
+ */
216
+ async signInWithEmail(email, password) {
217
+ this.initializeServices();
218
+ if (!this.auth) {
219
+ throw new Error('Auth instance not available');
220
+ }
221
+ try {
222
+ this.authState.loading = true;
223
+ this.notifyStateListeners();
224
+ const userCredential = await signInWithEmailAndPassword(this.auth, email, password);
225
+ await this.updateUserInFirestore(userCredential.user);
226
+ return this.mapFirebaseUserToProfile(userCredential.user);
227
+ }
228
+ catch (error) {
229
+ this.handleAuthError(error);
230
+ }
231
+ finally {
232
+ this.authState.loading = false;
233
+ this.notifyStateListeners();
234
+ }
235
+ }
236
+ /**
237
+ * Signs in user with Google popup
238
+ * @returns {Promise<UserProfile>} Promise resolving to user profile
239
+ * @throws {FirekitAuthError} If sign-in fails
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * try {
244
+ * const user = await firekitAuth.signInWithGoogle();
245
+ * console.log("Signed in with Google:", user.email);
246
+ * } catch (error) {
247
+ * if (error.code === 'auth/popup-closed-by-user') {
248
+ * console.log("User cancelled sign-in");
249
+ * }
250
+ * }
251
+ * ```
252
+ */
253
+ async signInWithGoogle() {
254
+ this.initializeServices();
255
+ if (!this.auth) {
256
+ throw new Error('Auth instance not available');
257
+ }
258
+ try {
259
+ this.authState.loading = true;
260
+ this.notifyStateListeners();
261
+ const provider = createGoogleProvider();
262
+ const result = await signInWithPopup(this.auth, provider);
263
+ await this.updateUserInFirestore(result.user);
264
+ return this.mapFirebaseUserToProfile(result.user);
265
+ }
266
+ catch (error) {
267
+ this.handleAuthError(error);
268
+ }
269
+ finally {
270
+ this.authState.loading = false;
271
+ this.notifyStateListeners();
272
+ }
273
+ }
274
+ /**
275
+ * Signs in user with Facebook popup
276
+ * @returns {Promise<UserProfile>} Promise resolving to user profile
277
+ * @throws {FirekitAuthError} If sign-in fails
278
+ */
279
+ async signInWithFacebook() {
280
+ this.initializeServices();
281
+ if (!this.auth) {
282
+ throw new Error('Auth instance not available');
283
+ }
284
+ try {
285
+ this.authState.loading = true;
286
+ this.notifyStateListeners();
287
+ const provider = createFacebookProvider();
288
+ const result = await signInWithPopup(this.auth, provider);
289
+ await this.updateUserInFirestore(result.user);
290
+ return this.mapFirebaseUserToProfile(result.user);
291
+ }
292
+ catch (error) {
293
+ this.handleAuthError(error);
294
+ }
295
+ finally {
296
+ this.authState.loading = false;
297
+ this.notifyStateListeners();
298
+ }
299
+ }
300
+ /**
301
+ * Signs in user with Apple popup
302
+ * @returns {Promise<UserProfile>} Promise resolving to user profile
303
+ * @throws {FirekitAuthError} If sign-in fails
304
+ */
305
+ async signInWithApple() {
306
+ this.initializeServices();
307
+ if (!this.auth) {
308
+ throw new Error('Auth instance not available');
309
+ }
310
+ try {
311
+ this.authState.loading = true;
312
+ this.notifyStateListeners();
313
+ const provider = createAppleProvider();
314
+ const result = await signInWithPopup(this.auth, provider);
315
+ await this.updateUserInFirestore(result.user);
316
+ return this.mapFirebaseUserToProfile(result.user);
317
+ }
318
+ catch (error) {
319
+ this.handleAuthError(error);
320
+ }
321
+ finally {
322
+ this.authState.loading = false;
323
+ this.notifyStateListeners();
324
+ }
325
+ }
326
+ /**
327
+ * Signs in user anonymously
328
+ * @returns {Promise<UserProfile>} Promise resolving to user profile
329
+ * @throws {FirekitAuthError} If sign-in fails
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * const user = await firekitAuth.signInAnonymously();
334
+ * console.log("Anonymous user:", user.uid);
335
+ * ```
336
+ */
337
+ async signInAnonymously() {
338
+ this.initializeServices();
339
+ if (!this.auth) {
340
+ throw new Error('Auth instance not available');
341
+ }
342
+ try {
343
+ this.authState.loading = true;
344
+ this.notifyStateListeners();
345
+ const result = await firebaseSignInAnonymously(this.auth);
346
+ await this.updateUserInFirestore(result.user);
347
+ return this.mapFirebaseUserToProfile(result.user);
348
+ }
349
+ catch (error) {
350
+ this.handleAuthError(error);
351
+ }
352
+ finally {
353
+ this.authState.loading = false;
354
+ this.notifyStateListeners();
355
+ }
356
+ }
357
+ /**
358
+ * Initiates phone number sign-in process
359
+ * @param {string} phoneNumber Phone number in international format
360
+ * @param {string} recaptchaContainerId ID of the reCAPTCHA container element
361
+ * @returns {Promise<PhoneVerificationResult>} Promise resolving to verification result
362
+ * @throws {FirekitAuthError} If verification initiation fails
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const verification = await firekitAuth.signInWithPhoneNumber("+1234567890", "recaptcha-container");
367
+ * const user = await verification.confirm("123456");
368
+ * ```
369
+ */
370
+ async signInWithPhoneNumber(phoneNumber, recaptchaContainerId) {
371
+ this.initializeServices();
372
+ if (!this.auth) {
373
+ throw new Error('Auth instance not available');
374
+ }
375
+ try {
376
+ this.authState.loading = true;
377
+ this.notifyStateListeners();
378
+ // Clean up existing verifier if any
379
+ const existingVerifier = this.recaptchaVerifiers.get(recaptchaContainerId);
380
+ if (existingVerifier) {
381
+ existingVerifier.clear();
382
+ this.recaptchaVerifiers.delete(recaptchaContainerId);
383
+ }
384
+ const recaptchaVerifier = new RecaptchaVerifier(this.auth, recaptchaContainerId, {
385
+ size: 'normal',
386
+ callback: () => {
387
+ console.log('reCAPTCHA solved');
388
+ },
389
+ 'expired-callback': () => {
390
+ console.log('reCAPTCHA expired');
391
+ }
392
+ });
393
+ this.recaptchaVerifiers.set(recaptchaContainerId, recaptchaVerifier);
394
+ const confirmationResult = await firebaseSignInWithPhoneNumber(this.auth, phoneNumber, recaptchaVerifier);
395
+ return {
396
+ verificationId: confirmationResult.verificationId,
397
+ confirm: async (verificationCode) => {
398
+ try {
399
+ const userCredential = await confirmationResult.confirm(verificationCode);
400
+ await this.updateUserInFirestore(userCredential.user);
401
+ return this.mapFirebaseUserToProfile(userCredential.user);
402
+ }
403
+ catch (error) {
404
+ this.handleAuthError(error);
405
+ }
406
+ finally {
407
+ // Clean up verifier after use
408
+ recaptchaVerifier.clear();
409
+ this.recaptchaVerifiers.delete(recaptchaContainerId);
410
+ }
411
+ }
412
+ };
413
+ }
414
+ catch (error) {
415
+ this.handleAuthError(error);
416
+ }
417
+ finally {
418
+ this.authState.loading = false;
419
+ this.notifyStateListeners();
420
+ }
421
+ }
422
+ // ========================================
423
+ // REGISTRATION METHODS
424
+ // ========================================
425
+ /**
426
+ * Registers new user with email and password
427
+ * @param {string} email User's email address
428
+ * @param {string} password User's password
429
+ * @param {string} [displayName] User's display name
430
+ * @param {boolean} [sendVerification=true] Whether to send email verification
431
+ * @returns {Promise<UserProfile>} Promise resolving to user profile
432
+ * @throws {FirekitAuthError} If registration fails
433
+ *
434
+ * @example
435
+ * ```typescript
436
+ * const user = await firekitAuth.registerWithEmail(
437
+ * "user@example.com",
438
+ * "password123",
439
+ * "John Doe"
440
+ * );
441
+ * console.log("Registered:", user.displayName);
442
+ * ```
443
+ */
444
+ async registerWithEmail(email, password, displayName, sendVerification = true) {
445
+ this.initializeServices();
446
+ if (!this.auth) {
447
+ throw new Error('Auth instance not available');
448
+ }
449
+ try {
450
+ this.authState.loading = true;
451
+ this.notifyStateListeners();
452
+ const userCredential = await createUserWithEmailAndPassword(this.auth, email, password);
453
+ const user = userCredential.user;
454
+ // Update profile if displayName provided
455
+ if (displayName) {
456
+ await updateProfile(user, { displayName });
457
+ }
458
+ // Send email verification
459
+ if (sendVerification) {
460
+ await sendEmailVerification(user);
461
+ }
462
+ await this.updateUserInFirestore(user);
463
+ return this.mapFirebaseUserToProfile(user);
464
+ }
465
+ catch (error) {
466
+ this.handleAuthError(error);
467
+ }
468
+ finally {
469
+ this.authState.loading = false;
470
+ this.notifyStateListeners();
471
+ }
472
+ }
473
+ // ========================================
474
+ // PASSWORD METHODS
475
+ // ========================================
476
+ /**
477
+ * Sends password reset email
478
+ * @param {string} email User's email address
479
+ * @returns {Promise<void>} Promise that resolves when email is sent
480
+ * @throws {FirekitAuthError} If sending fails
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * await firekitAuth.sendPasswordReset("user@example.com");
485
+ * console.log("Password reset email sent");
486
+ * ```
487
+ */
488
+ async sendPasswordReset(email) {
489
+ this.initializeServices();
490
+ if (!this.auth) {
491
+ throw new Error('Auth instance not available');
492
+ }
493
+ try {
494
+ await sendPasswordResetEmail(this.auth, email);
495
+ }
496
+ catch (error) {
497
+ this.handleAuthError(error);
498
+ }
499
+ }
500
+ /**
501
+ * Confirms password reset with code
502
+ * @param {string} code Password reset code from email
503
+ * @param {string} newPassword New password
504
+ * @returns {Promise<void>} Promise that resolves when password is reset
505
+ * @throws {FirekitAuthError} If reset fails
506
+ */
507
+ async confirmPasswordReset(code, newPassword) {
508
+ this.initializeServices();
509
+ if (!this.auth) {
510
+ throw new Error('Auth instance not available');
511
+ }
512
+ try {
513
+ await confirmPasswordReset(this.auth, code, newPassword);
514
+ }
515
+ catch (error) {
516
+ this.handleAuthError(error);
517
+ }
518
+ }
519
+ /**
520
+ * Updates user password with reauthentication
521
+ * @param {string} newPassword New password
522
+ * @param {string} currentPassword Current password for reauthentication
523
+ * @returns {Promise<PasswordUpdateResult>} Promise resolving to update result
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * const result = await firekitAuth.updatePassword("newPassword123", "oldPassword123");
528
+ * if (result.success) {
529
+ * console.log("Password updated successfully");
530
+ * } else {
531
+ * console.error("Update failed:", result.message);
532
+ * }
533
+ * ```
534
+ */
535
+ async updatePassword(newPassword, currentPassword) {
536
+ this.initializeServices();
537
+ if (!this.auth?.currentUser) {
538
+ return {
539
+ success: false,
540
+ message: 'No authenticated user found.',
541
+ code: 'auth/no-current-user'
542
+ };
543
+ }
544
+ try {
545
+ await this.reauthenticateUser(currentPassword);
546
+ await updatePassword(this.auth.currentUser, newPassword);
547
+ return {
548
+ success: true,
549
+ message: 'Password successfully updated.'
550
+ };
551
+ }
552
+ catch (error) {
553
+ const code = error.code;
554
+ if (code === AuthErrorCode.WRONG_PASSWORD) {
555
+ return {
556
+ success: false,
557
+ code,
558
+ message: 'Current password is incorrect.'
559
+ };
560
+ }
561
+ return {
562
+ success: false,
563
+ code: code || 'unknown_error',
564
+ message: error.message || 'Failed to update password.'
565
+ };
566
+ }
567
+ }
568
+ // ========================================
569
+ // PROFILE METHODS
570
+ // ========================================
571
+ /**
572
+ * Updates user profile
573
+ * @param {Object} profile Profile update data
574
+ * @param {string} [profile.displayName] New display name
575
+ * @param {string} [profile.photoURL] New photo URL
576
+ * @returns {Promise<void>} Promise that resolves when profile is updated
577
+ * @throws {FirekitAuthError} If update fails
578
+ *
579
+ * @example
580
+ * ```typescript
581
+ * await firekitAuth.updateUserProfile({
582
+ * displayName: "John Smith",
583
+ * photoURL: "https://example.com/photo.jpg"
584
+ * });
585
+ * ```
586
+ */
587
+ async updateUserProfile(profile) {
588
+ this.initializeServices();
589
+ if (!this.auth?.currentUser) {
590
+ throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
591
+ }
592
+ try {
593
+ await updateProfile(this.auth.currentUser, profile);
594
+ await this.updateUserInFirestore(this.auth.currentUser);
595
+ }
596
+ catch (error) {
597
+ this.handleAuthError(error);
598
+ }
599
+ }
600
+ /**
601
+ * Updates user email address
602
+ * @param {string} newEmail New email address
603
+ * @returns {Promise<void>} Promise that resolves when email is updated
604
+ * @throws {FirekitAuthError} If update fails
605
+ *
606
+ * @example
607
+ * ```typescript
608
+ * await firekitAuth.updateEmail("newemail@example.com");
609
+ * ```
610
+ */
611
+ async updateEmail(newEmail) {
612
+ this.initializeServices();
613
+ if (!this.auth?.currentUser) {
614
+ throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
615
+ }
616
+ try {
617
+ await updateEmail(this.auth.currentUser, newEmail);
618
+ await this.updateUserInFirestore(this.auth.currentUser);
619
+ }
620
+ catch (error) {
621
+ this.handleAuthError(error);
622
+ }
623
+ }
624
+ /**
625
+ * Sends email verification to current user
626
+ * @returns {Promise<void>} Promise that resolves when verification email is sent
627
+ * @throws {FirekitAuthError} If sending fails
628
+ *
629
+ * @example
630
+ * ```typescript
631
+ * await firekitAuth.sendEmailVerification();
632
+ * ```
633
+ */
634
+ async sendEmailVerification() {
635
+ this.initializeServices();
636
+ if (!this.auth?.currentUser) {
637
+ throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
638
+ }
639
+ try {
640
+ await sendEmailVerification(this.auth.currentUser);
641
+ }
642
+ catch (error) {
643
+ this.handleAuthError(error);
644
+ }
645
+ }
646
+ /**
647
+ * Reloads user to get updated data
648
+ * @returns {Promise<void>} Promise that resolves when user is reloaded
649
+ * @throws {FirekitAuthError} If reload fails
650
+ *
651
+ * @example
652
+ * ```typescript
653
+ * await firekitAuth.reloadUser();
654
+ * ```
655
+ */
656
+ async reloadUser() {
657
+ this.initializeServices();
658
+ if (!this.auth?.currentUser) {
659
+ throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
660
+ }
661
+ try {
662
+ await reload(this.auth.currentUser);
663
+ await this.updateUserInFirestore(this.auth.currentUser);
664
+ }
665
+ catch (error) {
666
+ this.handleAuthError(error);
667
+ }
668
+ }
669
+ /**
670
+ * Gets the current user's ID token
671
+ * @param {boolean} [forceRefresh=false] Whether to force token refresh
672
+ * @returns {Promise<string>} Promise resolving to ID token
673
+ * @throws {FirekitAuthError} If getting token fails
674
+ *
675
+ * @example
676
+ * ```typescript
677
+ * const token = await firekitAuth.getIdToken();
678
+ * ```
679
+ */
680
+ async getIdToken(forceRefresh = false) {
681
+ this.initializeServices();
682
+ if (!this.auth?.currentUser) {
683
+ throw new FirekitAuthError('auth/no-current-user', 'No authenticated user found.');
684
+ }
685
+ try {
686
+ return await getIdToken(this.auth.currentUser, forceRefresh);
687
+ }
688
+ catch (error) {
689
+ this.handleAuthError(error);
690
+ }
691
+ }
692
+ /**
693
+ * Reauthenticates user with current password
694
+ * @private
695
+ */
696
+ async reauthenticateUser(currentPassword) {
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.');
700
+ }
701
+ try {
702
+ const credential = EmailAuthProvider.credential(this.auth.currentUser.email, currentPassword);
703
+ await reauthenticateWithCredential(this.auth.currentUser, credential);
704
+ }
705
+ catch (error) {
706
+ this.handleAuthError(error);
707
+ }
708
+ }
709
+ /**
710
+ * Deletes user account
711
+ * @param {string} [currentPassword] Current password for reauthentication
712
+ * @returns {Promise<AccountDeletionResult>} Promise resolving to deletion result
713
+ *
714
+ * @example
715
+ * ```typescript
716
+ * const result = await firekitAuth.deleteAccount("currentPassword123");
717
+ * if (result.success) {
718
+ * console.log("Account deleted successfully");
719
+ * } else {
720
+ * console.error("Deletion failed:", result.message);
721
+ * }
722
+ * ```
723
+ */
724
+ async deleteAccount(currentPassword) {
725
+ this.initializeServices();
726
+ if (!this.auth?.currentUser) {
727
+ return {
728
+ success: false,
729
+ message: 'No authenticated user found.'
730
+ };
731
+ }
732
+ try {
733
+ const user = this.auth.currentUser;
734
+ // Reauthenticate if password provided
735
+ if (currentPassword) {
736
+ await this.reauthenticateUser(currentPassword);
737
+ }
738
+ // Delete user data from Firestore first
739
+ try {
740
+ const userRef = doc(this.firestore, 'users', user.uid);
741
+ await setDoc(userRef, { deleted: true, deletedAt: serverTimestamp() }, { merge: true });
742
+ }
743
+ catch (firestoreError) {
744
+ console.warn('Failed to update Firestore before account deletion:', firestoreError);
745
+ }
746
+ // Delete the user account
747
+ await deleteUser(user);
748
+ return {
749
+ success: true,
750
+ message: 'Account successfully deleted.'
751
+ };
752
+ }
753
+ catch (error) {
754
+ return {
755
+ success: false,
756
+ message: error.message || 'Failed to delete account.'
757
+ };
758
+ }
759
+ }
760
+ /**
761
+ * Signs out the current user
762
+ * @returns {Promise<void>} Promise that resolves when sign-out completes
763
+ * @throws {FirekitAuthError} If sign-out fails
764
+ *
765
+ * @example
766
+ * ```typescript
767
+ * await firekitAuth.signOut();
768
+ * console.log("User signed out");
769
+ * ```
770
+ */
771
+ async signOut() {
772
+ this.initializeServices();
773
+ if (!this.auth) {
774
+ throw new Error('Auth instance not available');
775
+ }
776
+ try {
777
+ // Clear reCAPTCHA verifiers
778
+ this.recaptchaVerifiers.forEach((verifier) => verifier.clear());
779
+ this.recaptchaVerifiers.clear();
780
+ await signOut(this.auth);
781
+ }
782
+ catch (error) {
783
+ this.handleAuthError(error);
784
+ }
785
+ }
786
+ // ========================================
787
+ // UTILITY METHODS
788
+ // ========================================
789
+ /**
790
+ * Checks if user is authenticated
791
+ * @returns {boolean} True if user is authenticated
792
+ */
793
+ isAuthenticated() {
794
+ this.initializeServices();
795
+ return this.authState.user !== null && !this.authState.user.isAnonymous;
796
+ }
797
+ /**
798
+ * Checks if user is anonymous
799
+ * @returns {boolean} True if user is anonymous
800
+ */
801
+ isAnonymous() {
802
+ this.initializeServices();
803
+ return this.authState.user?.isAnonymous ?? false;
804
+ }
805
+ /**
806
+ * Checks if user's email is verified
807
+ * @returns {boolean} True if email is verified
808
+ */
809
+ isEmailVerified() {
810
+ this.initializeServices();
811
+ return this.authState.user?.emailVerified ?? false;
812
+ }
813
+ /**
814
+ * Gets user's email address
815
+ * @returns {string | null} User's email or null
816
+ */
817
+ getUserEmail() {
818
+ this.initializeServices();
819
+ return this.authState.user?.email ?? null;
820
+ }
821
+ /**
822
+ * Gets user's display name
823
+ * @returns {string | null} User's display name or null
824
+ */
825
+ getUserDisplayName() {
826
+ this.initializeServices();
827
+ return this.authState.user?.displayName ?? null;
828
+ }
829
+ /**
830
+ * Gets user's photo URL
831
+ * @returns {string | null} User's photo URL or null
832
+ */
833
+ getUserPhotoURL() {
834
+ this.initializeServices();
835
+ return this.authState.user?.photoURL ?? null;
836
+ }
837
+ /**
838
+ * Gets user's unique ID
839
+ * @returns {string | null} User's UID or null
840
+ */
841
+ getUserId() {
842
+ this.initializeServices();
843
+ return this.authState.user?.uid ?? null;
844
+ }
845
+ /**
846
+ * Cleans up resources and listeners
847
+ * @returns {Promise<void>} Promise that resolves when cleanup completes
848
+ */
849
+ async cleanup() {
850
+ this.initializeServices();
851
+ // Clear reCAPTCHA verifiers
852
+ this.recaptchaVerifiers.forEach((verifier) => verifier.clear());
853
+ this.recaptchaVerifiers.clear();
854
+ // Clear state listeners
855
+ this.stateListeners.clear();
856
+ }
857
+ }
858
+ /**
859
+ * Pre-initialized singleton instance of FirekitAuth.
860
+ * This is the main export that should be used throughout your application.
861
+ *
862
+ * @example
863
+ * ```typescript
864
+ * import { firekitAuth } from 'svelte-firekit';
865
+ *
866
+ * // Sign in with email
867
+ * await firekitAuth.signInWithEmail("user@example.com", "password");
868
+ *
869
+ * // Listen to auth state
870
+ * const unsubscribe = firekitAuth.onAuthStateChanged((state) => {
871
+ * if (state.user) {
872
+ * console.log("User is signed in:", state.user.email);
873
+ * } else {
874
+ * console.log("User is signed out");
875
+ * }
876
+ * });
877
+ *
878
+ * // Clean up listener
879
+ * unsubscribe();
880
+ * ```
881
+ */
882
+ export const firekitAuth = FirekitAuth.getInstance();