svelte-firekit 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,476 @@
1
+ /**
2
+ * @fileoverview FirekitAnalytics - Google Analytics Service for SvelteKit with Firebase Analytics
3
+ * @module FirekitAnalytics
4
+ * @version 1.0.0
5
+ */
6
+ import { logEvent, setUserId, setUserProperties, setAnalyticsCollectionEnabled } from 'firebase/analytics';
7
+ import { firebaseService } from '../firebase.js';
8
+ import { firekitAuth } from './auth.js';
9
+ /**
10
+ * Comprehensive Firebase Analytics service for SvelteKit applications.
11
+ * Provides a complete analytics solution with automatic user tracking,
12
+ * e-commerce tracking, and SvelteKit-specific features.
13
+ *
14
+ * @class FirekitAnalytics
15
+ * @example
16
+ * ```typescript
17
+ * import { firekitAnalytics } from 'svelte-firekit';
18
+ *
19
+ * // Track a custom event
20
+ * firekitAnalytics.trackEvent('button_click', { button_name: 'signup' });
21
+ *
22
+ * // Track a purchase
23
+ * firekitAnalytics.trackPurchase({
24
+ * transaction_id: 'T12345',
25
+ * value: 29.99,
26
+ * currency: 'USD',
27
+ * items: [{ item_id: 'prod_123', item_name: 'Premium Plan' }]
28
+ * });
29
+ * ```
30
+ */
31
+ class FirekitAnalytics {
32
+ static instance;
33
+ analytics = null;
34
+ _initialized = false;
35
+ customParameters = {};
36
+ debugMode = false;
37
+ constructor() {
38
+ if (typeof window !== 'undefined') {
39
+ this.initialize();
40
+ }
41
+ }
42
+ /**
43
+ * Gets singleton instance of FirekitAnalytics
44
+ * @returns {FirekitAnalytics} The FirekitAnalytics instance
45
+ */
46
+ static getInstance() {
47
+ if (!FirekitAnalytics.instance) {
48
+ FirekitAnalytics.instance = new FirekitAnalytics();
49
+ }
50
+ return FirekitAnalytics.instance;
51
+ }
52
+ /**
53
+ * Initializes the analytics service
54
+ */
55
+ initialize() {
56
+ if (this._initialized || typeof window === 'undefined')
57
+ return;
58
+ try {
59
+ this.analytics = firebaseService.getAnalyticsInstance();
60
+ this._initialized = true;
61
+ console.log('FirekitAnalytics initialized successfully');
62
+ }
63
+ catch (error) {
64
+ console.error('Failed to initialize FirekitAnalytics:', error);
65
+ this._initialized = false;
66
+ }
67
+ }
68
+ /**
69
+ * Checks if analytics is available
70
+ * @private
71
+ */
72
+ isAnalyticsAvailable() {
73
+ return this._initialized && this.analytics !== null;
74
+ }
75
+ /**
76
+ * Gets the analytics instance
77
+ * @returns {Analytics | null} Firebase Analytics instance
78
+ */
79
+ getAnalyticsInstance() {
80
+ return this.analytics;
81
+ }
82
+ /**
83
+ * Checks if analytics is initialized
84
+ * @returns {boolean} True if analytics is initialized
85
+ */
86
+ isInitialized() {
87
+ return this._initialized;
88
+ }
89
+ // ========================================
90
+ // CORE TRACKING FUNCTIONS
91
+ // ========================================
92
+ /**
93
+ * Logs a custom analytics event
94
+ * @param {string} eventName Name of the event
95
+ * @param {Record<string, any>} parameters Event parameters
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * firekitAnalytics.trackEvent('button_click', {
100
+ * button_name: 'signup',
101
+ * page_location: '/home'
102
+ * });
103
+ * ```
104
+ */
105
+ trackEvent(eventName, parameters = {}) {
106
+ if (!this.isAnalyticsAvailable()) {
107
+ console.warn('Analytics not available, event not tracked:', eventName);
108
+ return;
109
+ }
110
+ try {
111
+ const mergedParameters = { ...this.customParameters, ...parameters };
112
+ logEvent(this.analytics, eventName, mergedParameters);
113
+ if (this.debugMode) {
114
+ console.log('Analytics Event:', eventName, mergedParameters);
115
+ }
116
+ }
117
+ catch (error) {
118
+ console.error('Failed to track event:', eventName, error);
119
+ }
120
+ }
121
+ /**
122
+ * Tracks page view events
123
+ * @param {PageViewEvent} pageView Page view data
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * firekitAnalytics.trackPageView({
128
+ * page_path: '/products',
129
+ * page_title: 'Products Page'
130
+ * });
131
+ * ```
132
+ */
133
+ trackPageView(pageView) {
134
+ this.trackEvent('page_view', {
135
+ page_path: pageView.page_path,
136
+ page_title: pageView.page_title || document.title,
137
+ page_location: pageView.page_location || window.location.href,
138
+ page_referrer: pageView.page_referrer || document.referrer
139
+ });
140
+ }
141
+ /**
142
+ * Sets user ID for analytics
143
+ * @param {string | null} userId User ID or null to clear
144
+ */
145
+ setUserId(userId) {
146
+ if (!this.isAnalyticsAvailable())
147
+ return;
148
+ try {
149
+ setUserId(this.analytics, userId);
150
+ }
151
+ catch (error) {
152
+ console.error('Failed to set user ID:', error);
153
+ }
154
+ }
155
+ /**
156
+ * Sets user properties for analytics
157
+ * @param {Record<string, any>} properties User properties
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * firekitAnalytics.setUserProperties({
162
+ * user_type: 'premium',
163
+ * subscription_plan: 'pro'
164
+ * });
165
+ * ```
166
+ */
167
+ setUserProperties(properties) {
168
+ if (!this.isAnalyticsAvailable())
169
+ return;
170
+ try {
171
+ setUserProperties(this.analytics, properties);
172
+ }
173
+ catch (error) {
174
+ console.error('Failed to set user properties:', error);
175
+ }
176
+ }
177
+ // ========================================
178
+ // E-COMMERCE TRACKING
179
+ // ========================================
180
+ /**
181
+ * Tracks purchase events
182
+ * @param {PurchaseEvent} purchase Purchase data
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * firekitAnalytics.trackPurchase({
187
+ * transaction_id: 'T12345',
188
+ * value: 29.99,
189
+ * currency: 'USD',
190
+ * items: [{
191
+ * item_id: 'prod_123',
192
+ * item_name: 'Premium Plan',
193
+ * price: 29.99
194
+ * }]
195
+ * });
196
+ * ```
197
+ */
198
+ trackPurchase(purchase) {
199
+ const parameters = {
200
+ transaction_id: purchase.transaction_id,
201
+ value: purchase.value,
202
+ currency: purchase.currency || 'USD'
203
+ };
204
+ if (purchase.tax !== undefined)
205
+ parameters.tax = purchase.tax;
206
+ if (purchase.shipping !== undefined)
207
+ parameters.shipping = purchase.shipping;
208
+ if (purchase.items)
209
+ parameters.items = purchase.items;
210
+ this.trackEvent('purchase', parameters);
211
+ }
212
+ /**
213
+ * Tracks add to cart events
214
+ * @param {EcommerceItem} item Item being added to cart
215
+ * @param {number} value Total value of the cart
216
+ * @param {string} currency Currency code
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * firekitAnalytics.trackAddToCart({
221
+ * item_id: 'prod_123',
222
+ * item_name: 'Premium Plan',
223
+ * price: 29.99
224
+ * }, 29.99, 'USD');
225
+ * ```
226
+ */
227
+ trackAddToCart(item, value, currency = 'USD') {
228
+ this.trackEvent('add_to_cart', {
229
+ currency,
230
+ value,
231
+ items: [item]
232
+ });
233
+ }
234
+ /**
235
+ * Tracks remove from cart events
236
+ * @param {EcommerceItem} item Item being removed from cart
237
+ * @param {number} value Total value of the cart
238
+ * @param {string} currency Currency code
239
+ */
240
+ trackRemoveFromCart(item, value, currency = 'USD') {
241
+ this.trackEvent('remove_from_cart', {
242
+ currency,
243
+ value,
244
+ items: [item]
245
+ });
246
+ }
247
+ /**
248
+ * Tracks view item events
249
+ * @param {EcommerceItem} item Item being viewed
250
+ */
251
+ trackViewItem(item) {
252
+ this.trackEvent('view_item', {
253
+ items: [item]
254
+ });
255
+ }
256
+ /**
257
+ * Tracks begin checkout events
258
+ * @param {EcommerceItem[]} items Items in cart
259
+ * @param {number} value Total value
260
+ * @param {string} currency Currency code
261
+ */
262
+ trackBeginCheckout(items, value, currency = 'USD') {
263
+ this.trackEvent('begin_checkout', {
264
+ currency,
265
+ value,
266
+ items
267
+ });
268
+ }
269
+ // ========================================
270
+ // ENGAGEMENT TRACKING
271
+ // ========================================
272
+ /**
273
+ * Tracks form submission events
274
+ * @param {FormSubmissionEvent} formSubmission Form submission data
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * firekitAnalytics.trackFormSubmission({
279
+ * form_name: 'contact_form',
280
+ * success: true
281
+ * });
282
+ * ```
283
+ */
284
+ trackFormSubmission(formSubmission) {
285
+ const parameters = {
286
+ form_name: formSubmission.form_name
287
+ };
288
+ if (formSubmission.form_id)
289
+ parameters.form_id = formSubmission.form_id;
290
+ if (formSubmission.success !== undefined)
291
+ parameters.success = formSubmission.success;
292
+ if (formSubmission.error_message)
293
+ parameters.error_message = formSubmission.error_message;
294
+ this.trackEvent('form_submit', parameters);
295
+ }
296
+ /**
297
+ * Tracks search events
298
+ * @param {SearchEvent} search Search data
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * firekitAnalytics.trackSearch({
303
+ * search_term: 'premium features',
304
+ * results_count: 15
305
+ * });
306
+ * ```
307
+ */
308
+ trackSearch(search) {
309
+ const parameters = {
310
+ search_term: search.search_term
311
+ };
312
+ if (search.results_count !== undefined)
313
+ parameters.results_count = search.results_count;
314
+ if (search.category)
315
+ parameters.category = search.category;
316
+ this.trackEvent('search', parameters);
317
+ }
318
+ /**
319
+ * Tracks custom conversion events
320
+ * @param {string} conversionName Name of the conversion
321
+ * @param {number} value Conversion value
322
+ * @param {string} currency Currency code
323
+ */
324
+ trackConversion(conversionName, value, currency = 'USD') {
325
+ const parameters = {};
326
+ if (value !== undefined) {
327
+ parameters.value = value;
328
+ parameters.currency = currency;
329
+ }
330
+ this.trackEvent(conversionName, parameters);
331
+ }
332
+ /**
333
+ * Tracks user engagement events
334
+ * @param {EngagementEvent} engagement Engagement data
335
+ */
336
+ trackEngagement(engagement) {
337
+ const parameters = {};
338
+ if (engagement.engagement_time_msec)
339
+ parameters.engagement_time_msec = engagement.engagement_time_msec;
340
+ if (engagement.session_id)
341
+ parameters.session_id = engagement.session_id;
342
+ this.trackEvent('user_engagement', parameters);
343
+ }
344
+ // ========================================
345
+ // SVELTEKIT-SPECIFIC FUNCTIONS
346
+ // ========================================
347
+ /**
348
+ * Initializes automatic page tracking for SvelteKit
349
+ * @param {boolean} trackInitialPage Whether to track the initial page load
350
+ *
351
+ * @example
352
+ * ```typescript
353
+ * // In your app.html or layout
354
+ * firekitAnalytics.initPageTracking();
355
+ * ```
356
+ */
357
+ initPageTracking(trackInitialPage = true) {
358
+ if (typeof window === 'undefined')
359
+ return;
360
+ // Track initial page load
361
+ if (trackInitialPage) {
362
+ this.trackPageView({
363
+ page_path: window.location.pathname,
364
+ page_title: document.title
365
+ });
366
+ }
367
+ // Listen for SvelteKit navigation events
368
+ window.addEventListener('sveltekit:navigation-end', () => {
369
+ this.trackPageView({
370
+ page_path: window.location.pathname,
371
+ page_title: document.title
372
+ });
373
+ });
374
+ // Fallback for other navigation events
375
+ window.addEventListener('popstate', () => {
376
+ this.trackPageView({
377
+ page_path: window.location.pathname,
378
+ page_title: document.title
379
+ });
380
+ });
381
+ }
382
+ /**
383
+ * Tracks route changes manually (useful for custom routing)
384
+ * @param {string} route Route path
385
+ * @param {string} title Page title
386
+ */
387
+ trackRouteChange(route, title) {
388
+ this.trackPageView({
389
+ page_path: route,
390
+ page_title: title || document.title
391
+ });
392
+ }
393
+ // ========================================
394
+ // UTILITY FUNCTIONS
395
+ // ========================================
396
+ /**
397
+ * Sets custom parameters that will be included in all subsequent events
398
+ * @param {Record<string, any>} parameters Custom parameters
399
+ */
400
+ setCustomParameters(parameters) {
401
+ this.customParameters = { ...this.customParameters, ...parameters };
402
+ }
403
+ /**
404
+ * Clears custom parameters
405
+ */
406
+ clearCustomParameters() {
407
+ this.customParameters = {};
408
+ }
409
+ /**
410
+ * Tracks multiple events in batch
411
+ * @param {AnalyticsEvent[]} events Array of events to track
412
+ */
413
+ trackEvents(events) {
414
+ events.forEach((event) => {
415
+ this.trackEvent(event.name, event.parameters);
416
+ });
417
+ }
418
+ /**
419
+ * Enables or disables analytics collection
420
+ * @param {boolean} enabled Whether to enable analytics collection
421
+ */
422
+ setAnalyticsEnabled(enabled) {
423
+ if (!this.isAnalyticsAvailable())
424
+ return;
425
+ try {
426
+ setAnalyticsCollectionEnabled(this.analytics, enabled);
427
+ }
428
+ catch (error) {
429
+ console.error('Failed to set analytics collection enabled:', error);
430
+ }
431
+ }
432
+ /**
433
+ * Sets debug mode for analytics
434
+ * @param {boolean} enabled Whether to enable debug mode
435
+ */
436
+ setDebugMode(enabled) {
437
+ this.debugMode = enabled;
438
+ }
439
+ /**
440
+ * Gets current debug mode status
441
+ * @returns {boolean} Current debug mode status
442
+ */
443
+ getDebugMode() {
444
+ return this.debugMode;
445
+ }
446
+ /**
447
+ * Cleans up analytics resources
448
+ */
449
+ cleanup() {
450
+ this.customParameters = {};
451
+ this.debugMode = false;
452
+ }
453
+ }
454
+ /**
455
+ * Pre-initialized singleton instance of FirekitAnalytics.
456
+ * This is the main export that should be used throughout your application.
457
+ *
458
+ * @example
459
+ * ```typescript
460
+ * import { firekitAnalytics } from 'svelte-firekit';
461
+ *
462
+ * // Track a custom event
463
+ * firekitAnalytics.trackEvent('button_click', { button_name: 'signup' });
464
+ *
465
+ * // Initialize page tracking
466
+ * firekitAnalytics.initPageTracking();
467
+ *
468
+ * // Track a purchase
469
+ * firekitAnalytics.trackPurchase({
470
+ * transaction_id: 'T12345',
471
+ * value: 29.99,
472
+ * currency: 'USD'
473
+ * });
474
+ * ```
475
+ */
476
+ export const firekitAnalytics = FirekitAnalytics.getInstance();
@@ -4,7 +4,7 @@
4
4
  * @version 1.0.0
5
5
  */
6
6
  import { type User } from 'firebase/auth';
7
- import { type UserProfile, type AuthState, type PasswordUpdateResult, type AccountDeletionResult, type PhoneVerificationResult } from '../types/auth.js';
7
+ import { type UserProfile, type AuthState, type PasswordUpdateResult, type AccountDeletionResult, type PhoneVerificationResult, type SignInResult, type RegistrationResult, type OAuthSignInResult } from '../types/auth.js';
8
8
  /**
9
9
  * Comprehensive Firebase Authentication service for Svelte applications.
10
10
  * Provides a complete authentication solution with automatic Firestore integration,
@@ -101,30 +101,33 @@ declare class FirekitAuth {
101
101
  * Signs in user with email and password
102
102
  * @param {string} email User's email address
103
103
  * @param {string} password User's password
104
- * @returns {Promise<UserProfile>} Promise resolving to user profile
104
+ * @returns {Promise<SignInResult>} Promise resolving to sign-in result
105
105
  * @throws {FirekitAuthError} If sign-in fails
106
106
  *
107
107
  * @example
108
108
  * ```typescript
109
109
  * try {
110
- * const user = await firekitAuth.signInWithEmail("user@example.com", "password123");
111
- * console.log("Signed in:", user.displayName);
110
+ * const result = await firekitAuth.signInWithEmail("user@example.com", "password123");
111
+ * console.log("Signed in:", result.user.displayName);
112
+ * console.log("Is new user:", result.isNewUser);
112
113
  * } catch (error) {
113
114
  * console.error("Sign-in failed:", error.message);
114
115
  * }
115
116
  * ```
116
117
  */
117
- signInWithEmail(email: string, password: string): Promise<UserProfile>;
118
+ signInWithEmail(email: string, password: string): Promise<SignInResult>;
118
119
  /**
119
120
  * Signs in user with Google popup
120
- * @returns {Promise<UserProfile>} Promise resolving to user profile
121
+ * @returns {Promise<OAuthSignInResult>} Promise resolving to OAuth sign-in result
121
122
  * @throws {FirekitAuthError} If sign-in fails
122
123
  *
123
124
  * @example
124
125
  * ```typescript
125
126
  * try {
126
- * const user = await firekitAuth.signInWithGoogle();
127
- * console.log("Signed in with Google:", user.email);
127
+ * const result = await firekitAuth.signInWithGoogle();
128
+ * console.log("Signed in with Google:", result.user.email);
129
+ * console.log("Is new user:", result.isNewUser);
130
+ * console.log("Access token:", result.accessToken);
128
131
  * } catch (error) {
129
132
  * if (error.code === 'auth/popup-closed-by-user') {
130
133
  * console.log("User cancelled sign-in");
@@ -132,31 +135,32 @@ declare class FirekitAuth {
132
135
  * }
133
136
  * ```
134
137
  */
135
- signInWithGoogle(): Promise<UserProfile>;
138
+ signInWithGoogle(): Promise<OAuthSignInResult>;
136
139
  /**
137
140
  * Signs in user with Facebook popup
138
- * @returns {Promise<UserProfile>} Promise resolving to user profile
141
+ * @returns {Promise<OAuthSignInResult>} Promise resolving to OAuth sign-in result
139
142
  * @throws {FirekitAuthError} If sign-in fails
140
143
  */
141
- signInWithFacebook(): Promise<UserProfile>;
144
+ signInWithFacebook(): Promise<OAuthSignInResult>;
142
145
  /**
143
146
  * Signs in user with Apple popup
144
- * @returns {Promise<UserProfile>} Promise resolving to user profile
147
+ * @returns {Promise<OAuthSignInResult>} Promise resolving to OAuth sign-in result
145
148
  * @throws {FirekitAuthError} If sign-in fails
146
149
  */
147
- signInWithApple(): Promise<UserProfile>;
150
+ signInWithApple(): Promise<OAuthSignInResult>;
148
151
  /**
149
152
  * Signs in user anonymously
150
- * @returns {Promise<UserProfile>} Promise resolving to user profile
153
+ * @returns {Promise<SignInResult>} Promise resolving to sign-in result
151
154
  * @throws {FirekitAuthError} If sign-in fails
152
155
  *
153
156
  * @example
154
157
  * ```typescript
155
- * const user = await firekitAuth.signInAnonymously();
156
- * console.log("Anonymous user:", user.uid);
158
+ * const result = await firekitAuth.signInAnonymously();
159
+ * console.log("Anonymous user:", result.user.uid);
160
+ * console.log("Is new user:", result.isNewUser);
157
161
  * ```
158
162
  */
159
- signInAnonymously(): Promise<UserProfile>;
163
+ signInAnonymously(): Promise<SignInResult>;
160
164
  /**
161
165
  * Initiates phone number sign-in process
162
166
  * @param {string} phoneNumber Phone number in international format
@@ -177,20 +181,21 @@ declare class FirekitAuth {
177
181
  * @param {string} password User's password
178
182
  * @param {string} [displayName] User's display name
179
183
  * @param {boolean} [sendVerification=true] Whether to send email verification
180
- * @returns {Promise<UserProfile>} Promise resolving to user profile
184
+ * @returns {Promise<RegistrationResult>} Promise resolving to registration result
181
185
  * @throws {FirekitAuthError} If registration fails
182
186
  *
183
187
  * @example
184
188
  * ```typescript
185
- * const user = await firekitAuth.registerWithEmail(
189
+ * const result = await firekitAuth.registerWithEmail(
186
190
  * "user@example.com",
187
191
  * "password123",
188
192
  * "John Doe"
189
193
  * );
190
- * console.log("Registered:", user.displayName);
194
+ * console.log("Registered:", result.user.displayName);
195
+ * console.log("Email verification sent:", result.emailVerificationSent);
191
196
  * ```
192
197
  */
193
- registerWithEmail(email: string, password: string, displayName?: string, sendVerification?: boolean): Promise<UserProfile>;
198
+ registerWithEmail(email: string, password: string, displayName?: string, sendVerification?: boolean): Promise<RegistrationResult>;
194
199
  /**
195
200
  * Sends password reset email
196
201
  * @param {string} email User's email address