svelte-firekit 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Collection.svelte +49 -75
- package/dist/components/Doc.svelte +56 -38
- package/dist/firebase.d.ts +9 -0
- package/dist/firebase.js +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/services/analytics.d.ts +272 -0
- package/dist/services/analytics.js +476 -0
- package/dist/services/auth.d.ts +26 -21
- package/dist/services/auth.js +93 -22
- package/dist/services/collection.svelte.d.ts +1 -14
- package/dist/services/collection.svelte.js +10 -85
- package/dist/services/index.d.ts +9 -0
- package/dist/services/index.js +10 -0
- package/dist/services/mutations.d.ts +5 -37
- package/dist/services/mutations.js +43 -140
- package/dist/services/presence.svelte.d.ts +1 -22
- package/dist/services/presence.svelte.js +1 -101
- package/dist/types/analytics.d.ts +84 -0
- package/dist/types/analytics.js +6 -0
- package/dist/types/auth.d.ts +65 -7
- package/dist/types/collection.d.ts +0 -25
- package/dist/types/firebase.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/mutations.d.ts +0 -22
- package/dist/types/presence.d.ts +0 -20
- package/package.json +1 -1
|
@@ -31,15 +31,9 @@ import { CacheSource } from '../types/document.js';
|
|
|
31
31
|
* { type: 'create', path: 'users', data: userData },
|
|
32
32
|
* { type: 'update', path: 'profiles/123', data: profileUpdate }
|
|
33
33
|
* ]);
|
|
34
|
-
*
|
|
35
|
-
* // Listen to mutation events
|
|
36
|
-
* const unsubscribe = firekitDocMutations.addEventListener((event) => {
|
|
37
|
-
* console.log('Mutation event:', event.type, event.data);
|
|
38
|
-
* });
|
|
39
34
|
* ```
|
|
40
35
|
*/
|
|
41
36
|
class FirekitDocumentMutations {
|
|
42
|
-
eventListeners = new Set();
|
|
43
37
|
analytics = this.initializeAnalytics();
|
|
44
38
|
defaultOptions = {
|
|
45
39
|
timestamps: true,
|
|
@@ -132,13 +126,6 @@ class FirekitDocumentMutations {
|
|
|
132
126
|
// Update analytics
|
|
133
127
|
this.analytics.failedMutations++;
|
|
134
128
|
this.updateErrorAnalytics(mutationError);
|
|
135
|
-
// Emit error event
|
|
136
|
-
this.emitEvent({
|
|
137
|
-
type: 'mutation_error',
|
|
138
|
-
error: mutationError,
|
|
139
|
-
timestamp: new Date(),
|
|
140
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
141
|
-
});
|
|
142
129
|
console.error('FirekitDocumentMutations error:', mutationError);
|
|
143
130
|
return mutationError;
|
|
144
131
|
}
|
|
@@ -200,19 +187,6 @@ class FirekitDocumentMutations {
|
|
|
200
187
|
// Sort by count
|
|
201
188
|
this.analytics.commonErrors.sort((a, b) => b.count - a.count);
|
|
202
189
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Emit event to all listeners
|
|
205
|
-
*/
|
|
206
|
-
emitEvent(event) {
|
|
207
|
-
this.eventListeners.forEach((callback) => {
|
|
208
|
-
try {
|
|
209
|
-
callback(event);
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
console.error('Error in mutation event listener:', error);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
190
|
/**
|
|
217
191
|
* Execute operation with retry logic
|
|
218
192
|
*/
|
|
@@ -223,12 +197,6 @@ class FirekitDocumentMutations {
|
|
|
223
197
|
while (attempt < retryConfig.maxAttempts) {
|
|
224
198
|
try {
|
|
225
199
|
if (attempt > 0) {
|
|
226
|
-
this.emitEvent({
|
|
227
|
-
type: 'mutation_retry',
|
|
228
|
-
data: { attempt, maxAttempts: retryConfig.maxAttempts, operation: operationName, path },
|
|
229
|
-
timestamp: new Date(),
|
|
230
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
231
|
-
});
|
|
232
200
|
this.analytics.retryStats.totalRetries++;
|
|
233
201
|
}
|
|
234
202
|
const result = await operation();
|
|
@@ -288,13 +256,6 @@ class FirekitDocumentMutations {
|
|
|
288
256
|
const startTime = Date.now();
|
|
289
257
|
const mergedOptions = { ...this.defaultOptions, ...options };
|
|
290
258
|
try {
|
|
291
|
-
// Emit start event
|
|
292
|
-
this.emitEvent({
|
|
293
|
-
type: 'mutation_start',
|
|
294
|
-
data: { operation: 'create', path: collectionPath, data },
|
|
295
|
-
timestamp: new Date(),
|
|
296
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
297
|
-
});
|
|
298
259
|
// Validate data if requested
|
|
299
260
|
if (mergedOptions.validate) {
|
|
300
261
|
const validation = this.validateData(data, mergedOptions.validator);
|
|
@@ -340,13 +301,6 @@ class FirekitDocumentMutations {
|
|
|
340
301
|
this.analytics.totalMutations++;
|
|
341
302
|
this.analytics.successfulMutations++;
|
|
342
303
|
this.updateOperationAnalytics(MutationOperationType.CREATE, Date.now() - startTime, true);
|
|
343
|
-
// Emit success event
|
|
344
|
-
this.emitEvent({
|
|
345
|
-
type: 'mutation_success',
|
|
346
|
-
data: { operation: 'create', path: collectionPath, result },
|
|
347
|
-
timestamp: new Date(),
|
|
348
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
349
|
-
});
|
|
350
304
|
return result;
|
|
351
305
|
}
|
|
352
306
|
catch (error) {
|
|
@@ -392,12 +346,6 @@ class FirekitDocumentMutations {
|
|
|
392
346
|
const startTime = Date.now();
|
|
393
347
|
const mergedOptions = { ...this.defaultOptions, ...options };
|
|
394
348
|
try {
|
|
395
|
-
this.emitEvent({
|
|
396
|
-
type: 'mutation_start',
|
|
397
|
-
data: { operation: 'set', path, data },
|
|
398
|
-
timestamp: new Date(),
|
|
399
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
400
|
-
});
|
|
401
349
|
if (mergedOptions.validate) {
|
|
402
350
|
const validation = this.validateData(data, mergedOptions.validator);
|
|
403
351
|
if (!validation.valid) {
|
|
@@ -432,12 +380,6 @@ class FirekitDocumentMutations {
|
|
|
432
380
|
this.analytics.totalMutations++;
|
|
433
381
|
this.analytics.successfulMutations++;
|
|
434
382
|
this.updateOperationAnalytics(MutationOperationType.SET, Date.now() - startTime, true);
|
|
435
|
-
this.emitEvent({
|
|
436
|
-
type: 'mutation_success',
|
|
437
|
-
data: { operation: 'set', path, result },
|
|
438
|
-
timestamp: new Date(),
|
|
439
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
440
|
-
});
|
|
441
383
|
return result;
|
|
442
384
|
}
|
|
443
385
|
catch (error) {
|
|
@@ -480,12 +422,6 @@ class FirekitDocumentMutations {
|
|
|
480
422
|
const startTime = Date.now();
|
|
481
423
|
const mergedOptions = { ...this.defaultOptions, ...options };
|
|
482
424
|
try {
|
|
483
|
-
this.emitEvent({
|
|
484
|
-
type: 'mutation_start',
|
|
485
|
-
data: { operation: 'update', path, data },
|
|
486
|
-
timestamp: new Date(),
|
|
487
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
488
|
-
});
|
|
489
425
|
if (mergedOptions.validate) {
|
|
490
426
|
const validation = this.validateData(data, mergedOptions.validator);
|
|
491
427
|
if (!validation.valid) {
|
|
@@ -502,6 +438,7 @@ class FirekitDocumentMutations {
|
|
|
502
438
|
...data,
|
|
503
439
|
...(mergedOptions.timestamps && this.getTimestampData(false))
|
|
504
440
|
};
|
|
441
|
+
// @ts-ignore
|
|
505
442
|
await updateDoc(docRef, dataToUpdate);
|
|
506
443
|
return {
|
|
507
444
|
success: true,
|
|
@@ -519,12 +456,6 @@ class FirekitDocumentMutations {
|
|
|
519
456
|
this.analytics.totalMutations++;
|
|
520
457
|
this.analytics.successfulMutations++;
|
|
521
458
|
this.updateOperationAnalytics(MutationOperationType.UPDATE, Date.now() - startTime, true);
|
|
522
|
-
this.emitEvent({
|
|
523
|
-
type: 'mutation_success',
|
|
524
|
-
data: { operation: 'update', path, result },
|
|
525
|
-
timestamp: new Date(),
|
|
526
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
527
|
-
});
|
|
528
459
|
return result;
|
|
529
460
|
}
|
|
530
461
|
catch (error) {
|
|
@@ -565,12 +496,6 @@ class FirekitDocumentMutations {
|
|
|
565
496
|
const startTime = Date.now();
|
|
566
497
|
const mergedOptions = { ...this.defaultOptions, ...options };
|
|
567
498
|
try {
|
|
568
|
-
this.emitEvent({
|
|
569
|
-
type: 'mutation_start',
|
|
570
|
-
data: { operation: 'delete', path },
|
|
571
|
-
timestamp: new Date(),
|
|
572
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
573
|
-
});
|
|
574
499
|
const result = await this.executeWithRetry(async () => {
|
|
575
500
|
const firestore = firebaseService.getDbInstance();
|
|
576
501
|
if (!firestore) {
|
|
@@ -593,12 +518,6 @@ class FirekitDocumentMutations {
|
|
|
593
518
|
this.analytics.totalMutations++;
|
|
594
519
|
this.analytics.successfulMutations++;
|
|
595
520
|
this.updateOperationAnalytics(MutationOperationType.DELETE, Date.now() - startTime, true);
|
|
596
|
-
this.emitEvent({
|
|
597
|
-
type: 'mutation_success',
|
|
598
|
-
data: { operation: 'delete', path, result },
|
|
599
|
-
timestamp: new Date(),
|
|
600
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
601
|
-
});
|
|
602
521
|
return result;
|
|
603
522
|
}
|
|
604
523
|
catch (error) {
|
|
@@ -696,17 +615,12 @@ class FirekitDocumentMutations {
|
|
|
696
615
|
async batch(operations, config = {}) {
|
|
697
616
|
const startTime = Date.now();
|
|
698
617
|
const batchConfig = {
|
|
699
|
-
batchSize: 500,
|
|
700
618
|
parallel: false,
|
|
701
619
|
failFast: true,
|
|
702
|
-
...config
|
|
620
|
+
...config,
|
|
621
|
+
// Enforce Firestore limit of 500 operations per batch
|
|
622
|
+
batchSize: Math.min(config.batchSize || 500, 500)
|
|
703
623
|
};
|
|
704
|
-
this.emitEvent({
|
|
705
|
-
type: 'batch_start',
|
|
706
|
-
data: { operationCount: operations.length, config: batchConfig },
|
|
707
|
-
timestamp: new Date(),
|
|
708
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
709
|
-
});
|
|
710
624
|
try {
|
|
711
625
|
const firestore = firebaseService.getDbInstance();
|
|
712
626
|
if (!firestore) {
|
|
@@ -754,15 +668,7 @@ class FirekitDocumentMutations {
|
|
|
754
668
|
successCount++;
|
|
755
669
|
});
|
|
756
670
|
results.push(...batchResults);
|
|
757
|
-
|
|
758
|
-
const completed = (i + 1) * batchConfig.batchSize;
|
|
759
|
-
this.emitEvent({
|
|
760
|
-
type: 'batch_progress',
|
|
761
|
-
data: { completed: Math.min(completed, operations.length), total: operations.length },
|
|
762
|
-
timestamp: new Date(),
|
|
763
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
764
|
-
});
|
|
765
|
-
batchConfig.onProgress?.(Math.min(completed, operations.length), operations.length);
|
|
671
|
+
batchConfig.onProgress?.(Math.min((i + 1) * batchConfig.batchSize, operations.length), operations.length);
|
|
766
672
|
}
|
|
767
673
|
const batchResult = {
|
|
768
674
|
success: failureCount === 0,
|
|
@@ -778,12 +684,6 @@ class FirekitDocumentMutations {
|
|
|
778
684
|
strategy: batchConfig.parallel ? 'parallel' : 'sequential'
|
|
779
685
|
}
|
|
780
686
|
};
|
|
781
|
-
this.emitEvent({
|
|
782
|
-
type: 'batch_complete',
|
|
783
|
-
data: batchResult,
|
|
784
|
-
timestamp: new Date(),
|
|
785
|
-
userId: firekitUser.uid ?? 'anonymous'
|
|
786
|
-
});
|
|
787
687
|
return batchResult;
|
|
788
688
|
}
|
|
789
689
|
catch (error) {
|
|
@@ -800,7 +700,32 @@ class FirekitDocumentMutations {
|
|
|
800
700
|
throw new Error('Firestore not available');
|
|
801
701
|
const options = { ...this.defaultOptions, ...operation.options };
|
|
802
702
|
switch (operation.type) {
|
|
803
|
-
case 'create':
|
|
703
|
+
case 'create': {
|
|
704
|
+
let data = operation.data || {};
|
|
705
|
+
if (options.timestamps) {
|
|
706
|
+
data = { ...data, ...this.getTimestampData() };
|
|
707
|
+
}
|
|
708
|
+
if (options.validate && operation.data) {
|
|
709
|
+
const validation = this.validateData(operation.data, options.validator);
|
|
710
|
+
if (!validation.valid) {
|
|
711
|
+
throw new MutationError(MutationErrorCode.VALIDATION_FAILED, validation.message || 'Validation failed', operation.type, operation.path);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
// For CREATE operations, use addDoc if no custom ID, otherwise use setDoc
|
|
715
|
+
if (options.customId) {
|
|
716
|
+
// Use setDoc with custom ID
|
|
717
|
+
const docRef = doc(firestore, operation.path, options.customId);
|
|
718
|
+
batch.set(docRef, { ...data, id: options.customId }, { merge: options.merge });
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
// Use addDoc to let Firestore generate the ID
|
|
722
|
+
// Note: We can't use addDoc in a batch, so we need to generate an ID
|
|
723
|
+
const docId = this.generateDocumentId();
|
|
724
|
+
const docRef = doc(firestore, operation.path, docId);
|
|
725
|
+
batch.set(docRef, { ...data, id: docId }, { merge: options.merge });
|
|
726
|
+
}
|
|
727
|
+
break;
|
|
728
|
+
}
|
|
804
729
|
case 'set': {
|
|
805
730
|
const docRef = doc(firestore, operation.path);
|
|
806
731
|
let data = operation.data || {};
|
|
@@ -840,6 +765,18 @@ class FirekitDocumentMutations {
|
|
|
840
765
|
throw new MutationError(MutationErrorCode.UNIMPLEMENTED, `Operation type ${operation.type} not supported in batch`);
|
|
841
766
|
}
|
|
842
767
|
}
|
|
768
|
+
/**
|
|
769
|
+
* Generate a unique document ID (Firestore-style)
|
|
770
|
+
*/
|
|
771
|
+
generateDocumentId() {
|
|
772
|
+
// Generate a Firestore-style document ID (20 characters, alphanumeric)
|
|
773
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
774
|
+
let result = '';
|
|
775
|
+
for (let i = 0; i < 20; i++) {
|
|
776
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
777
|
+
}
|
|
778
|
+
return result;
|
|
779
|
+
}
|
|
843
780
|
/**
|
|
844
781
|
* Chunk array into smaller arrays
|
|
845
782
|
*/
|
|
@@ -974,35 +911,6 @@ class FirekitDocumentMutations {
|
|
|
974
911
|
(this.analytics.averageDuration * (this.analytics.totalMutations - 1) + duration) /
|
|
975
912
|
this.analytics.totalMutations;
|
|
976
913
|
}
|
|
977
|
-
// ========================================
|
|
978
|
-
// EVENT MANAGEMENT
|
|
979
|
-
// ========================================
|
|
980
|
-
/**
|
|
981
|
-
* Add event listener for mutation events
|
|
982
|
-
*
|
|
983
|
-
* @param callback Event callback function
|
|
984
|
-
* @returns Cleanup function to remove listener
|
|
985
|
-
*
|
|
986
|
-
* @example
|
|
987
|
-
* ```typescript
|
|
988
|
-
* const unsubscribe = firekitDocMutations.addEventListener((event) => {
|
|
989
|
-
* console.log('Mutation event:', event.type, event.data);
|
|
990
|
-
* });
|
|
991
|
-
*
|
|
992
|
-
* // Clean up when done
|
|
993
|
-
* unsubscribe();
|
|
994
|
-
* ```
|
|
995
|
-
*/
|
|
996
|
-
addEventListener(callback) {
|
|
997
|
-
this.eventListeners.add(callback);
|
|
998
|
-
return () => this.eventListeners.delete(callback);
|
|
999
|
-
}
|
|
1000
|
-
/**
|
|
1001
|
-
* Remove all event listeners
|
|
1002
|
-
*/
|
|
1003
|
-
clearEventListeners() {
|
|
1004
|
-
this.eventListeners.clear();
|
|
1005
|
-
}
|
|
1006
914
|
/**
|
|
1007
915
|
* Get current mutation analytics
|
|
1008
916
|
*
|
|
@@ -1069,11 +977,6 @@ class FirekitDocumentMutations {
|
|
|
1069
977
|
* { type: 'create', path: 'users', data: userData },
|
|
1070
978
|
* { type: 'update', path: 'profiles/123', data: updateData }
|
|
1071
979
|
* ]);
|
|
1072
|
-
*
|
|
1073
|
-
* // Listen to events
|
|
1074
|
-
* const unsubscribe = firekitDocMutations.addEventListener((event) => {
|
|
1075
|
-
* console.log('Mutation:', event.type, event.data);
|
|
1076
|
-
* });
|
|
1077
980
|
* ```
|
|
1078
981
|
*/
|
|
1079
982
|
export const firekitDocMutations = new FirekitDocumentMutations();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module FirekitPresence
|
|
4
4
|
* @version 1.0.0
|
|
5
5
|
*/
|
|
6
|
-
import { type PresenceConfig, type Location, type SessionData, type
|
|
6
|
+
import { type PresenceConfig, type Location, type SessionData, type PresenceStats, PresenceError } from '../types/presence.js';
|
|
7
7
|
/**
|
|
8
8
|
* Main presence tracking service
|
|
9
9
|
*
|
|
@@ -18,11 +18,6 @@ import { type PresenceConfig, type Location, type SessionData, type PresenceEven
|
|
|
18
18
|
* sessionTTL: 30 * 60 * 1000
|
|
19
19
|
* });
|
|
20
20
|
*
|
|
21
|
-
* // Listen to events
|
|
22
|
-
* const unsubscribe = firekitPresence.addEventListener((event) => {
|
|
23
|
-
* console.log('Presence event:', event);
|
|
24
|
-
* });
|
|
25
|
-
*
|
|
26
21
|
* // Access reactive state
|
|
27
22
|
* $: console.log('Status:', firekitPresence.status);
|
|
28
23
|
* ```
|
|
@@ -32,7 +27,6 @@ declare class FirekitPresence {
|
|
|
32
27
|
private config;
|
|
33
28
|
private geolocationService;
|
|
34
29
|
private connectedListener;
|
|
35
|
-
private eventListeners;
|
|
36
30
|
private currentUser;
|
|
37
31
|
private _initialized;
|
|
38
32
|
private _status;
|
|
@@ -103,16 +97,6 @@ declare class FirekitPresence {
|
|
|
103
97
|
* Force refresh presence data
|
|
104
98
|
*/
|
|
105
99
|
refresh(): Promise<void>;
|
|
106
|
-
/**
|
|
107
|
-
* Add event listener
|
|
108
|
-
* @param callback Event callback function
|
|
109
|
-
* @returns Cleanup function to remove listener
|
|
110
|
-
*/
|
|
111
|
-
addEventListener(callback: PresenceEventCallback): () => void;
|
|
112
|
-
/**
|
|
113
|
-
* Emit event to all listeners
|
|
114
|
-
*/
|
|
115
|
-
private emitEvent;
|
|
116
100
|
/**
|
|
117
101
|
* Dispose of all resources and cleanup
|
|
118
102
|
*/
|
|
@@ -130,11 +114,6 @@ declare class FirekitPresence {
|
|
|
130
114
|
* geolocation: { enabled: true, type: 'browser' },
|
|
131
115
|
* sessionTTL: 30 * 60 * 1000
|
|
132
116
|
* });
|
|
133
|
-
*
|
|
134
|
-
* // Listen to events
|
|
135
|
-
* const unsubscribe = firekitPresence.addEventListener((event) => {
|
|
136
|
-
* console.log('Presence event:', event.type, event.data);
|
|
137
|
-
* });
|
|
138
117
|
* ```
|
|
139
118
|
*/
|
|
140
119
|
export declare const firekitPresence: FirekitPresence;
|
|
@@ -241,11 +241,6 @@ class DeviceInfoService {
|
|
|
241
241
|
* sessionTTL: 30 * 60 * 1000
|
|
242
242
|
* });
|
|
243
243
|
*
|
|
244
|
-
* // Listen to events
|
|
245
|
-
* const unsubscribe = firekitPresence.addEventListener((event) => {
|
|
246
|
-
* console.log('Presence event:', event);
|
|
247
|
-
* });
|
|
248
|
-
*
|
|
249
244
|
* // Access reactive state
|
|
250
245
|
* $: console.log('Status:', firekitPresence.status);
|
|
251
246
|
* ```
|
|
@@ -259,7 +254,6 @@ class FirekitPresence {
|
|
|
259
254
|
};
|
|
260
255
|
geolocationService = null;
|
|
261
256
|
connectedListener = null;
|
|
262
|
-
eventListeners = new Set();
|
|
263
257
|
currentUser = null;
|
|
264
258
|
// Reactive state using Svelte 5 runes
|
|
265
259
|
_initialized = $state(false);
|
|
@@ -333,12 +327,7 @@ class FirekitPresence {
|
|
|
333
327
|
if (this.config.geolocation?.enabled) {
|
|
334
328
|
this.geolocationService = new GeolocationService(this.config.geolocation);
|
|
335
329
|
if (this.config.geolocation.requireConsent) {
|
|
336
|
-
this.emitEvent({ type: 'consent_requested', timestamp: Date.now() });
|
|
337
330
|
const hasConsent = await this.geolocationService.requestConsent();
|
|
338
|
-
this.emitEvent({
|
|
339
|
-
type: hasConsent ? 'consent_granted' : 'consent_denied',
|
|
340
|
-
timestamp: Date.now()
|
|
341
|
-
});
|
|
342
331
|
}
|
|
343
332
|
// Start location tracking if consent granted
|
|
344
333
|
if (!this.config.geolocation.requireConsent || this.geolocationService.hasConsent) {
|
|
@@ -348,22 +337,12 @@ class FirekitPresence {
|
|
|
348
337
|
// Set up Firebase connection monitoring
|
|
349
338
|
await this.setupConnectionMonitoring();
|
|
350
339
|
this._initialized = true;
|
|
351
|
-
this.emitEvent({
|
|
352
|
-
type: 'init',
|
|
353
|
-
data: { userId: user.uid, config: this.config },
|
|
354
|
-
timestamp: Date.now()
|
|
355
|
-
});
|
|
356
340
|
}
|
|
357
341
|
catch (error) {
|
|
358
342
|
this._error =
|
|
359
343
|
error instanceof PresenceError
|
|
360
344
|
? error
|
|
361
345
|
: new PresenceError(PresenceErrorCode.INITIALIZATION_FAILED, `Failed to initialize presence service: ${error.message}`, error);
|
|
362
|
-
this.emitEvent({
|
|
363
|
-
type: 'error',
|
|
364
|
-
error: this._error,
|
|
365
|
-
timestamp: Date.now()
|
|
366
|
-
});
|
|
367
346
|
throw this._error;
|
|
368
347
|
}
|
|
369
348
|
finally {
|
|
@@ -455,13 +434,6 @@ class FirekitPresence {
|
|
|
455
434
|
...(device && { device }),
|
|
456
435
|
...(this.config.customMetadata && { metadata: this.config.customMetadata })
|
|
457
436
|
};
|
|
458
|
-
this.emitEvent({
|
|
459
|
-
type: 'session_created',
|
|
460
|
-
data: { session },
|
|
461
|
-
timestamp: Date.now(),
|
|
462
|
-
sessionId: session.id,
|
|
463
|
-
userId: session.userId
|
|
464
|
-
});
|
|
465
437
|
}
|
|
466
438
|
else {
|
|
467
439
|
// Update existing session
|
|
@@ -472,13 +444,6 @@ class FirekitPresence {
|
|
|
472
444
|
lastActivity: new Date().toISOString(),
|
|
473
445
|
...(location && { location })
|
|
474
446
|
};
|
|
475
|
-
this.emitEvent({
|
|
476
|
-
type: 'session_updated',
|
|
477
|
-
data: { session, previousStatus: this._currentSession.status },
|
|
478
|
-
timestamp: Date.now(),
|
|
479
|
-
sessionId: session.id,
|
|
480
|
-
userId: session.userId
|
|
481
|
-
});
|
|
482
447
|
}
|
|
483
448
|
// Save session to Firebase
|
|
484
449
|
const sessionPath = this.config.sessionPath || 'presence';
|
|
@@ -489,34 +454,12 @@ class FirekitPresence {
|
|
|
489
454
|
this._status = status;
|
|
490
455
|
// Load and update all sessions
|
|
491
456
|
await this.loadSessions();
|
|
492
|
-
this.emitEvent({
|
|
493
|
-
type: 'status_change',
|
|
494
|
-
data: { status, session, location },
|
|
495
|
-
timestamp: Date.now(),
|
|
496
|
-
sessionId: session.id,
|
|
497
|
-
userId: session.userId
|
|
498
|
-
});
|
|
499
|
-
// Emit location update if location changed
|
|
500
|
-
if (location) {
|
|
501
|
-
this.emitEvent({
|
|
502
|
-
type: 'location_update',
|
|
503
|
-
data: { location, session },
|
|
504
|
-
timestamp: Date.now(),
|
|
505
|
-
sessionId: session.id,
|
|
506
|
-
userId: session.userId
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
457
|
}
|
|
510
458
|
catch (error) {
|
|
511
459
|
this._error =
|
|
512
460
|
error instanceof PresenceError
|
|
513
461
|
? error
|
|
514
462
|
: new PresenceError(PresenceErrorCode.DATABASE_ERROR, `Failed to set presence: ${error.message}`, error);
|
|
515
|
-
this.emitEvent({
|
|
516
|
-
type: 'error',
|
|
517
|
-
error: this._error,
|
|
518
|
-
timestamp: Date.now()
|
|
519
|
-
});
|
|
520
463
|
throw this._error;
|
|
521
464
|
}
|
|
522
465
|
}
|
|
@@ -541,17 +484,10 @@ class FirekitPresence {
|
|
|
541
484
|
const cutoffTime = new Date(Date.now() - this.config.sessionTTL).toISOString();
|
|
542
485
|
const expiredSessions = sessions.filter((session) => session.lastSeen < cutoffTime);
|
|
543
486
|
sessions = sessions.filter((session) => session.lastSeen >= cutoffTime);
|
|
544
|
-
// Remove stale sessions from database
|
|
487
|
+
// Remove stale sessions from database
|
|
545
488
|
for (const expiredSession of expiredSessions) {
|
|
546
489
|
const staleSessionRef = ref(db, `${sessionPath}/${this.currentUser.uid}/sessions/${expiredSession.id}`);
|
|
547
490
|
await set(staleSessionRef, null);
|
|
548
|
-
this.emitEvent({
|
|
549
|
-
type: 'session_expired',
|
|
550
|
-
data: { session: expiredSession },
|
|
551
|
-
timestamp: Date.now(),
|
|
552
|
-
sessionId: expiredSession.id,
|
|
553
|
-
userId: expiredSession.userId
|
|
554
|
-
});
|
|
555
491
|
}
|
|
556
492
|
}
|
|
557
493
|
this._sessions = sessions;
|
|
@@ -643,31 +579,6 @@ class FirekitPresence {
|
|
|
643
579
|
}
|
|
644
580
|
}
|
|
645
581
|
// ========================================
|
|
646
|
-
// EVENT MANAGEMENT
|
|
647
|
-
// ========================================
|
|
648
|
-
/**
|
|
649
|
-
* Add event listener
|
|
650
|
-
* @param callback Event callback function
|
|
651
|
-
* @returns Cleanup function to remove listener
|
|
652
|
-
*/
|
|
653
|
-
addEventListener(callback) {
|
|
654
|
-
this.eventListeners.add(callback);
|
|
655
|
-
return () => this.eventListeners.delete(callback);
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Emit event to all listeners
|
|
659
|
-
*/
|
|
660
|
-
emitEvent(event) {
|
|
661
|
-
this.eventListeners.forEach((callback) => {
|
|
662
|
-
try {
|
|
663
|
-
callback(event);
|
|
664
|
-
}
|
|
665
|
-
catch (error) {
|
|
666
|
-
console.error('Error in presence event listener:', error);
|
|
667
|
-
}
|
|
668
|
-
});
|
|
669
|
-
}
|
|
670
|
-
// ========================================
|
|
671
582
|
// CLEANUP
|
|
672
583
|
// ========================================
|
|
673
584
|
/**
|
|
@@ -690,8 +601,6 @@ class FirekitPresence {
|
|
|
690
601
|
this.connectedListener();
|
|
691
602
|
this.connectedListener = null;
|
|
692
603
|
}
|
|
693
|
-
// Clear event listeners
|
|
694
|
-
this.eventListeners.clear();
|
|
695
604
|
// Reset state
|
|
696
605
|
this._initialized = false;
|
|
697
606
|
this._status = 'offline';
|
|
@@ -699,10 +608,6 @@ class FirekitPresence {
|
|
|
699
608
|
this._error = null;
|
|
700
609
|
this._currentSession = null;
|
|
701
610
|
this._sessions = [];
|
|
702
|
-
this.emitEvent({
|
|
703
|
-
type: 'disconnect',
|
|
704
|
-
timestamp: Date.now()
|
|
705
|
-
});
|
|
706
611
|
}
|
|
707
612
|
}
|
|
708
613
|
/**
|
|
@@ -717,11 +622,6 @@ class FirekitPresence {
|
|
|
717
622
|
* geolocation: { enabled: true, type: 'browser' },
|
|
718
623
|
* sessionTTL: 30 * 60 * 1000
|
|
719
624
|
* });
|
|
720
|
-
*
|
|
721
|
-
* // Listen to events
|
|
722
|
-
* const unsubscribe = firekitPresence.addEventListener((event) => {
|
|
723
|
-
* console.log('Presence event:', event.type, event.data);
|
|
724
|
-
* });
|
|
725
625
|
* ```
|
|
726
626
|
*/
|
|
727
627
|
export const firekitPresence = FirekitPresence.getInstance();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Analytics types and interfaces for FirekitAnalytics
|
|
3
|
+
* @module AnalyticsTypes
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Analytics event interface
|
|
8
|
+
*/
|
|
9
|
+
export interface AnalyticsEvent {
|
|
10
|
+
name: string;
|
|
11
|
+
parameters?: Record<string, any>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* E-commerce item interface
|
|
15
|
+
*/
|
|
16
|
+
export interface EcommerceItem {
|
|
17
|
+
item_id: string;
|
|
18
|
+
item_name: string;
|
|
19
|
+
item_category?: string;
|
|
20
|
+
item_variant?: string;
|
|
21
|
+
item_brand?: string;
|
|
22
|
+
price?: number;
|
|
23
|
+
quantity?: number;
|
|
24
|
+
currency?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Purchase event interface
|
|
28
|
+
*/
|
|
29
|
+
export interface PurchaseEvent {
|
|
30
|
+
transaction_id: string;
|
|
31
|
+
value: number;
|
|
32
|
+
currency?: string;
|
|
33
|
+
tax?: number;
|
|
34
|
+
shipping?: number;
|
|
35
|
+
items?: EcommerceItem[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Form submission event interface
|
|
39
|
+
*/
|
|
40
|
+
export interface FormSubmissionEvent {
|
|
41
|
+
form_name: string;
|
|
42
|
+
form_id?: string;
|
|
43
|
+
success?: boolean;
|
|
44
|
+
error_message?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Search event interface
|
|
48
|
+
*/
|
|
49
|
+
export interface SearchEvent {
|
|
50
|
+
search_term: string;
|
|
51
|
+
results_count?: number;
|
|
52
|
+
category?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Page view event interface
|
|
56
|
+
*/
|
|
57
|
+
export interface PageViewEvent {
|
|
58
|
+
page_path: string;
|
|
59
|
+
page_title?: string;
|
|
60
|
+
page_location?: string;
|
|
61
|
+
page_referrer?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* User engagement event interface
|
|
65
|
+
*/
|
|
66
|
+
export interface EngagementEvent {
|
|
67
|
+
engagement_time_msec?: number;
|
|
68
|
+
session_id?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Analytics configuration interface
|
|
72
|
+
*/
|
|
73
|
+
export interface AnalyticsConfig {
|
|
74
|
+
/** Enable debug mode for analytics */
|
|
75
|
+
debugMode?: boolean;
|
|
76
|
+
/** Enable automatic page tracking */
|
|
77
|
+
enablePageTracking?: boolean;
|
|
78
|
+
/** Enable automatic user tracking */
|
|
79
|
+
enableUserTracking?: boolean;
|
|
80
|
+
/** Custom parameters to include in all events */
|
|
81
|
+
customParameters?: Record<string, any>;
|
|
82
|
+
/** Enable analytics collection */
|
|
83
|
+
enabled?: boolean;
|
|
84
|
+
}
|