@axyle/expo-sdk 1.0.0
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/CONFIG.md +117 -0
- package/README.md +534 -0
- package/dist/client.d.ts +93 -0
- package/dist/client.js +416 -0
- package/dist/config.example.d.ts +8 -0
- package/dist/config.example.js +13 -0
- package/dist/configLoader.d.ts +10 -0
- package/dist/configLoader.js +22 -0
- package/dist/constants.d.ts +38 -0
- package/dist/constants.js +42 -0
- package/dist/context.d.ts +36 -0
- package/dist/context.js +216 -0
- package/dist/core.d.ts +9 -0
- package/dist/core.js +45 -0
- package/dist/hooks/useFeatureTracking.d.ts +34 -0
- package/dist/hooks/useFeatureTracking.js +60 -0
- package/dist/hooks/useOnboardingTracking.d.ts +85 -0
- package/dist/hooks/useOnboardingTracking.js +172 -0
- package/dist/hooks/useScreenTracking.d.ts +23 -0
- package/dist/hooks/useScreenTracking.js +52 -0
- package/dist/hooks/useScrollTracking.d.ts +39 -0
- package/dist/hooks/useScrollTracking.js +146 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.js +171 -0
- package/dist/integrations/expoRouter.d.ts +42 -0
- package/dist/integrations/expoRouter.js +66 -0
- package/dist/integrations/reactNavigation.d.ts +27 -0
- package/dist/integrations/reactNavigation.js +101 -0
- package/dist/queue.d.ts +52 -0
- package/dist/queue.js +143 -0
- package/dist/session.d.ts +44 -0
- package/dist/session.js +139 -0
- package/dist/sessionAnalytics.d.ts +43 -0
- package/dist/sessionAnalytics.js +74 -0
- package/dist/storage.d.ts +67 -0
- package/dist/storage.js +210 -0
- package/dist/transport.d.ts +41 -0
- package/dist/transport.js +158 -0
- package/dist/types.d.ts +102 -0
- package/dist/types.js +5 -0
- package/dist/utils.d.ts +39 -0
- package/dist/utils.js +116 -0
- package/package.json +44 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* React Navigation integration for automatic screen tracking
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createNavigationTracker = createNavigationTracker;
|
|
7
|
+
exports.trackNavigationReady = trackNavigationReady;
|
|
8
|
+
exports.trackNavigationStateChange = trackNavigationStateChange;
|
|
9
|
+
const core_1 = require("../core");
|
|
10
|
+
/**
|
|
11
|
+
* Get the current route name from navigation state
|
|
12
|
+
*/
|
|
13
|
+
function getActiveRouteName(state) {
|
|
14
|
+
if (!state || typeof state.index !== 'number') {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const route = state.routes[state.index];
|
|
18
|
+
if (route.state) {
|
|
19
|
+
return getActiveRouteName(route.state);
|
|
20
|
+
}
|
|
21
|
+
return route.name;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a navigation state change handler for React Navigation
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* import { NavigationContainer } from '@react-navigation/native';
|
|
29
|
+
* import { createNavigationTracker } from '@axyle/expo-sdk';
|
|
30
|
+
*
|
|
31
|
+
* const navigationTracker = createNavigationTracker();
|
|
32
|
+
*
|
|
33
|
+
* function App() {
|
|
34
|
+
* return (
|
|
35
|
+
* <NavigationContainer
|
|
36
|
+
* onStateChange={navigationTracker}
|
|
37
|
+
* >
|
|
38
|
+
* {/* Your navigators *\/}
|
|
39
|
+
* </NavigationContainer>
|
|
40
|
+
* );
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function createNavigationTracker() {
|
|
45
|
+
let previousRouteName;
|
|
46
|
+
return (state) => {
|
|
47
|
+
const currentRouteName = getActiveRouteName(state);
|
|
48
|
+
if (currentRouteName && currentRouteName !== previousRouteName) {
|
|
49
|
+
// Track screen view
|
|
50
|
+
(0, core_1.track)('Screen Viewed', {
|
|
51
|
+
screen: currentRouteName,
|
|
52
|
+
previousScreen: previousRouteName || null,
|
|
53
|
+
});
|
|
54
|
+
previousRouteName = currentRouteName;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Alternative: Get navigation ref and track manually
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```tsx
|
|
63
|
+
* import { NavigationContainer } from '@react-navigation/native';
|
|
64
|
+
* import { trackNavigationReady, trackNavigationStateChange } from '@axyle/expo-sdk';
|
|
65
|
+
*
|
|
66
|
+
* const navigationRef = createNavigationContainerRef();
|
|
67
|
+
*
|
|
68
|
+
* function App() {
|
|
69
|
+
* return (
|
|
70
|
+
* <NavigationContainer
|
|
71
|
+
* ref={navigationRef}
|
|
72
|
+
* onReady={() => trackNavigationReady(navigationRef)}
|
|
73
|
+
* onStateChange={() => trackNavigationStateChange(navigationRef)}
|
|
74
|
+
* >
|
|
75
|
+
* {/* Your navigators *\/}
|
|
76
|
+
* </NavigationContainer>
|
|
77
|
+
* );
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
let currentRouteName;
|
|
82
|
+
function trackNavigationReady(navigationRef) {
|
|
83
|
+
currentRouteName = navigationRef.current?.getCurrentRoute()?.name;
|
|
84
|
+
if (currentRouteName) {
|
|
85
|
+
(0, core_1.track)('Screen Viewed', {
|
|
86
|
+
screen: currentRouteName,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function trackNavigationStateChange(navigationRef) {
|
|
91
|
+
const previousRouteName = currentRouteName;
|
|
92
|
+
const route = navigationRef.current?.getCurrentRoute();
|
|
93
|
+
currentRouteName = route?.name;
|
|
94
|
+
if (currentRouteName && currentRouteName !== previousRouteName) {
|
|
95
|
+
(0, core_1.track)('Screen Viewed', {
|
|
96
|
+
screen: currentRouteName,
|
|
97
|
+
previousScreen: previousRouteName || null,
|
|
98
|
+
params: route?.params || {},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
package/dist/queue.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event queue manager
|
|
3
|
+
* Handles queuing, batching, and flushing events
|
|
4
|
+
*/
|
|
5
|
+
import { AnalyticsEvent } from './types';
|
|
6
|
+
import { Transport } from './transport';
|
|
7
|
+
export interface QueueConfig {
|
|
8
|
+
maxQueueSize: number;
|
|
9
|
+
flushInterval: number;
|
|
10
|
+
}
|
|
11
|
+
export declare class EventQueue {
|
|
12
|
+
private config;
|
|
13
|
+
private transport;
|
|
14
|
+
private logger;
|
|
15
|
+
private flushTimer;
|
|
16
|
+
private isFlushing;
|
|
17
|
+
constructor(config: QueueConfig, transport: Transport, logger: any);
|
|
18
|
+
/**
|
|
19
|
+
* Initialize the queue
|
|
20
|
+
* Starts the flush timer and attempts to flush any persisted events
|
|
21
|
+
*/
|
|
22
|
+
initialize(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Add event to queue
|
|
25
|
+
*/
|
|
26
|
+
enqueue(event: AnalyticsEvent): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Flush all queued events
|
|
29
|
+
*/
|
|
30
|
+
flush(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Start periodic flush timer
|
|
33
|
+
*/
|
|
34
|
+
private startFlushTimer;
|
|
35
|
+
/**
|
|
36
|
+
* Stop periodic flush timer
|
|
37
|
+
*/
|
|
38
|
+
private stopFlushTimer;
|
|
39
|
+
/**
|
|
40
|
+
* Get queue size
|
|
41
|
+
*/
|
|
42
|
+
getQueueSize(): Promise<number>;
|
|
43
|
+
/**
|
|
44
|
+
* Clear all queued events
|
|
45
|
+
*/
|
|
46
|
+
clear(): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Shutdown the queue
|
|
49
|
+
* Flushes remaining events and stops timer
|
|
50
|
+
*/
|
|
51
|
+
shutdown(): Promise<void>;
|
|
52
|
+
}
|
package/dist/queue.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Event queue manager
|
|
4
|
+
* Handles queuing, batching, and flushing events
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.EventQueue = void 0;
|
|
8
|
+
const storage_1 = require("./storage");
|
|
9
|
+
class EventQueue {
|
|
10
|
+
constructor(config, transport, logger) {
|
|
11
|
+
this.flushTimer = null;
|
|
12
|
+
this.isFlushing = false;
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.transport = transport;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the queue
|
|
19
|
+
* Starts the flush timer and attempts to flush any persisted events
|
|
20
|
+
*/
|
|
21
|
+
async initialize() {
|
|
22
|
+
// Start periodic flush
|
|
23
|
+
this.startFlushTimer();
|
|
24
|
+
// Try to flush any events that were persisted from previous session
|
|
25
|
+
await this.flush();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Add event to queue
|
|
29
|
+
*/
|
|
30
|
+
async enqueue(event) {
|
|
31
|
+
try {
|
|
32
|
+
// Add to persistent storage
|
|
33
|
+
await storage_1.Storage.addEventToQueue(event);
|
|
34
|
+
this.logger.log(`Event queued: ${event.name}`);
|
|
35
|
+
// Check if we should flush
|
|
36
|
+
const queuedEvents = await storage_1.Storage.getQueuedEvents();
|
|
37
|
+
this.logger.log(`Current queue size: ${queuedEvents.length}/${this.config.maxQueueSize}`);
|
|
38
|
+
if (queuedEvents.length >= this.config.maxQueueSize) {
|
|
39
|
+
this.logger.log('Queue size limit reached, flushing...');
|
|
40
|
+
await this.flush();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
this.logger.error('Failed to enqueue event:', error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Flush all queued events
|
|
49
|
+
*/
|
|
50
|
+
async flush() {
|
|
51
|
+
// Prevent concurrent flushes
|
|
52
|
+
if (this.isFlushing) {
|
|
53
|
+
this.logger.log('Flush already in progress, skipping');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.isFlushing = true;
|
|
57
|
+
try {
|
|
58
|
+
const events = await storage_1.Storage.getQueuedEvents();
|
|
59
|
+
if (events.length === 0) {
|
|
60
|
+
this.logger.log('No events to flush');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
this.logger.log(`Flushing ${events.length} events...`);
|
|
64
|
+
// Send events via transport
|
|
65
|
+
const sentEventIds = await this.transport.sendBatch(events);
|
|
66
|
+
// Remove successfully sent events from queue
|
|
67
|
+
if (sentEventIds.length > 0) {
|
|
68
|
+
await storage_1.Storage.removeEventsFromQueue(sentEventIds);
|
|
69
|
+
this.logger.log(`Successfully flushed ${sentEventIds.length} events`);
|
|
70
|
+
}
|
|
71
|
+
// Check if there are failed events remaining
|
|
72
|
+
const remainingEvents = await storage_1.Storage.getQueuedEvents();
|
|
73
|
+
if (remainingEvents.length > 0) {
|
|
74
|
+
this.logger.warn(`${remainingEvents.length} events failed to send, will retry later`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
this.logger.error('Error during flush:', error);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
this.isFlushing = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Start periodic flush timer
|
|
86
|
+
*/
|
|
87
|
+
startFlushTimer() {
|
|
88
|
+
if (this.flushTimer) {
|
|
89
|
+
clearInterval(this.flushTimer);
|
|
90
|
+
}
|
|
91
|
+
this.flushTimer = setInterval(() => {
|
|
92
|
+
this.flush().catch((error) => {
|
|
93
|
+
this.logger.error('Error in periodic flush:', error);
|
|
94
|
+
});
|
|
95
|
+
}, this.config.flushInterval);
|
|
96
|
+
this.logger.log(`Started flush timer with interval: ${this.config.flushInterval}ms`);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Stop periodic flush timer
|
|
100
|
+
*/
|
|
101
|
+
stopFlushTimer() {
|
|
102
|
+
if (this.flushTimer) {
|
|
103
|
+
clearInterval(this.flushTimer);
|
|
104
|
+
this.flushTimer = null;
|
|
105
|
+
this.logger.log('Stopped flush timer');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get queue size
|
|
110
|
+
*/
|
|
111
|
+
async getQueueSize() {
|
|
112
|
+
try {
|
|
113
|
+
const events = await storage_1.Storage.getQueuedEvents();
|
|
114
|
+
return events.length;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.logger.error('Failed to get queue size:', error);
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Clear all queued events
|
|
123
|
+
*/
|
|
124
|
+
async clear() {
|
|
125
|
+
try {
|
|
126
|
+
await storage_1.Storage.clearQueue();
|
|
127
|
+
this.logger.log('Queue cleared');
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
this.logger.error('Failed to clear queue:', error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Shutdown the queue
|
|
135
|
+
* Flushes remaining events and stops timer
|
|
136
|
+
*/
|
|
137
|
+
async shutdown() {
|
|
138
|
+
this.logger.log('Shutting down event queue...');
|
|
139
|
+
this.stopFlushTimer();
|
|
140
|
+
await this.flush();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.EventQueue = EventQueue;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management with 30-minute timeout
|
|
3
|
+
*/
|
|
4
|
+
export declare class SessionManager {
|
|
5
|
+
private sessionTimeout;
|
|
6
|
+
private currentSession;
|
|
7
|
+
private logger;
|
|
8
|
+
private onSessionStart?;
|
|
9
|
+
private onSessionEnd?;
|
|
10
|
+
constructor(sessionTimeout: number, logger: any, callbacks?: {
|
|
11
|
+
onSessionStart?: (sessionId: string) => void;
|
|
12
|
+
onSessionEnd?: (sessionId: string, duration: number) => void;
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Initialize session manager
|
|
16
|
+
* Loads existing session or creates new one
|
|
17
|
+
*/
|
|
18
|
+
initialize(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Get current session ID
|
|
21
|
+
*/
|
|
22
|
+
getSessionId(): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Update last activity time
|
|
25
|
+
* Checks if session should be renewed
|
|
26
|
+
*/
|
|
27
|
+
updateActivity(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Start a new session
|
|
30
|
+
*/
|
|
31
|
+
private startNewSession;
|
|
32
|
+
/**
|
|
33
|
+
* End a session
|
|
34
|
+
*/
|
|
35
|
+
private endSession;
|
|
36
|
+
/**
|
|
37
|
+
* Force end current session
|
|
38
|
+
*/
|
|
39
|
+
forceEndSession(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Reset session (start fresh)
|
|
42
|
+
*/
|
|
43
|
+
reset(): Promise<void>;
|
|
44
|
+
}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session management with 30-minute timeout
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SessionManager = void 0;
|
|
7
|
+
const storage_1 = require("./storage");
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
class SessionManager {
|
|
10
|
+
constructor(sessionTimeout, logger, callbacks) {
|
|
11
|
+
this.currentSession = null;
|
|
12
|
+
this.sessionTimeout = sessionTimeout;
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
this.onSessionStart = callbacks?.onSessionStart;
|
|
15
|
+
this.onSessionEnd = callbacks?.onSessionEnd;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize session manager
|
|
19
|
+
* Loads existing session or creates new one
|
|
20
|
+
*/
|
|
21
|
+
async initialize() {
|
|
22
|
+
try {
|
|
23
|
+
const savedSession = await storage_1.Storage.getSessionData();
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
if (savedSession) {
|
|
26
|
+
// Check if session is still valid
|
|
27
|
+
const timeSinceLastActivity = now - savedSession.lastActivityTime;
|
|
28
|
+
if (timeSinceLastActivity < this.sessionTimeout) {
|
|
29
|
+
// Resume existing session
|
|
30
|
+
this.currentSession = savedSession;
|
|
31
|
+
this.logger.log('Resumed existing session:', savedSession.sessionId);
|
|
32
|
+
await this.updateActivity();
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Session expired, end it and start new one
|
|
36
|
+
this.logger.log('Session expired, starting new session');
|
|
37
|
+
await this.endSession(savedSession);
|
|
38
|
+
await this.startNewSession();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// No existing session, start new one
|
|
43
|
+
await this.startNewSession();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
this.logger.error('Failed to initialize session:', error);
|
|
48
|
+
// Start new session as fallback
|
|
49
|
+
await this.startNewSession();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get current session ID
|
|
54
|
+
*/
|
|
55
|
+
getSessionId() {
|
|
56
|
+
if (!this.currentSession) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return this.currentSession.sessionId;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Update last activity time
|
|
63
|
+
* Checks if session should be renewed
|
|
64
|
+
*/
|
|
65
|
+
async updateActivity() {
|
|
66
|
+
if (!this.currentSession)
|
|
67
|
+
return;
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
const timeSinceLastActivity = now - this.currentSession.lastActivityTime;
|
|
70
|
+
// If session expired, end it and start new one
|
|
71
|
+
if (timeSinceLastActivity >= this.sessionTimeout) {
|
|
72
|
+
this.logger.log('Session timeout detected, starting new session');
|
|
73
|
+
await this.endSession(this.currentSession);
|
|
74
|
+
await this.startNewSession();
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Update activity time
|
|
78
|
+
this.currentSession.lastActivityTime = now;
|
|
79
|
+
await storage_1.Storage.saveSessionData(this.currentSession);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Start a new session
|
|
84
|
+
*/
|
|
85
|
+
async startNewSession() {
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
const sessionId = `session_${now}_${(0, utils_1.generateUUID)()}`;
|
|
88
|
+
this.currentSession = {
|
|
89
|
+
sessionId,
|
|
90
|
+
startTime: now,
|
|
91
|
+
lastActivityTime: now,
|
|
92
|
+
};
|
|
93
|
+
await storage_1.Storage.saveSessionData(this.currentSession);
|
|
94
|
+
this.logger.log('Started new session:', sessionId);
|
|
95
|
+
// Trigger callback
|
|
96
|
+
if (this.onSessionStart) {
|
|
97
|
+
try {
|
|
98
|
+
this.onSessionStart(sessionId);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
this.logger.error('Error in onSessionStart callback:', error);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* End a session
|
|
107
|
+
*/
|
|
108
|
+
async endSession(session) {
|
|
109
|
+
const duration = session.lastActivityTime - session.startTime;
|
|
110
|
+
this.logger.log(`Ended session ${session.sessionId}, duration: ${duration}ms`);
|
|
111
|
+
// Trigger callback
|
|
112
|
+
if (this.onSessionEnd) {
|
|
113
|
+
try {
|
|
114
|
+
this.onSessionEnd(session.sessionId, duration);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.logger.error('Error in onSessionEnd callback:', error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Force end current session
|
|
123
|
+
*/
|
|
124
|
+
async forceEndSession() {
|
|
125
|
+
if (this.currentSession) {
|
|
126
|
+
await this.endSession(this.currentSession);
|
|
127
|
+
await storage_1.Storage.clearSessionData();
|
|
128
|
+
this.currentSession = null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Reset session (start fresh)
|
|
133
|
+
*/
|
|
134
|
+
async reset() {
|
|
135
|
+
await this.forceEndSession();
|
|
136
|
+
await this.startNewSession();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.SessionManager = SessionManager;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Analytics Counter
|
|
3
|
+
* Tracks and counts analytics events locally for the current session
|
|
4
|
+
*/
|
|
5
|
+
import { AnalyticsEvent } from "./types";
|
|
6
|
+
export interface SessionAnalyticsStats {
|
|
7
|
+
totalEvents: number;
|
|
8
|
+
eventsByType: Record<string, number>;
|
|
9
|
+
eventsByScreen: Record<string, number>;
|
|
10
|
+
sessionStartTime: number;
|
|
11
|
+
sessionDuration: number;
|
|
12
|
+
lastEventTime: number | null;
|
|
13
|
+
}
|
|
14
|
+
export declare class SessionAnalytics {
|
|
15
|
+
private events;
|
|
16
|
+
private sessionStartTime;
|
|
17
|
+
private lastEventTime;
|
|
18
|
+
constructor(sessionStartTime: number);
|
|
19
|
+
/**
|
|
20
|
+
* Add an event to the session analytics
|
|
21
|
+
*/
|
|
22
|
+
addEvent(event: AnalyticsEvent): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get current session statistics
|
|
25
|
+
*/
|
|
26
|
+
getStats(): SessionAnalyticsStats;
|
|
27
|
+
/**
|
|
28
|
+
* Get all events in the session
|
|
29
|
+
*/
|
|
30
|
+
getEvents(): AnalyticsEvent[];
|
|
31
|
+
/**
|
|
32
|
+
* Get events by type
|
|
33
|
+
*/
|
|
34
|
+
getEventsByType(eventName: string): AnalyticsEvent[];
|
|
35
|
+
/**
|
|
36
|
+
* Clear all events (for new session)
|
|
37
|
+
*/
|
|
38
|
+
clear(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Reset session start time
|
|
41
|
+
*/
|
|
42
|
+
resetSession(sessionStartTime: number): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Analytics Counter
|
|
4
|
+
* Tracks and counts analytics events locally for the current session
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SessionAnalytics = void 0;
|
|
8
|
+
class SessionAnalytics {
|
|
9
|
+
constructor(sessionStartTime) {
|
|
10
|
+
this.events = [];
|
|
11
|
+
this.lastEventTime = null;
|
|
12
|
+
this.sessionStartTime = sessionStartTime;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Add an event to the session analytics
|
|
16
|
+
*/
|
|
17
|
+
addEvent(event) {
|
|
18
|
+
this.events.push(event);
|
|
19
|
+
this.lastEventTime = Date.now();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get current session statistics
|
|
23
|
+
*/
|
|
24
|
+
getStats() {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const eventsByType = {};
|
|
27
|
+
const eventsByScreen = {};
|
|
28
|
+
// Count events by type and screen
|
|
29
|
+
for (const event of this.events) {
|
|
30
|
+
// Count by event name
|
|
31
|
+
eventsByType[event.name] = (eventsByType[event.name] || 0) + 1;
|
|
32
|
+
// Count by screen if available
|
|
33
|
+
const screen = event.properties?.screen;
|
|
34
|
+
if (screen) {
|
|
35
|
+
eventsByScreen[screen] = (eventsByScreen[screen] || 0) + 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
totalEvents: this.events.length,
|
|
40
|
+
eventsByType,
|
|
41
|
+
eventsByScreen,
|
|
42
|
+
sessionStartTime: this.sessionStartTime,
|
|
43
|
+
sessionDuration: now - this.sessionStartTime,
|
|
44
|
+
lastEventTime: this.lastEventTime,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get all events in the session
|
|
49
|
+
*/
|
|
50
|
+
getEvents() {
|
|
51
|
+
return [...this.events];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get events by type
|
|
55
|
+
*/
|
|
56
|
+
getEventsByType(eventName) {
|
|
57
|
+
return this.events.filter((event) => event.name === eventName);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Clear all events (for new session)
|
|
61
|
+
*/
|
|
62
|
+
clear() {
|
|
63
|
+
this.events = [];
|
|
64
|
+
this.lastEventTime = null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Reset session start time
|
|
68
|
+
*/
|
|
69
|
+
resetSession(sessionStartTime) {
|
|
70
|
+
this.sessionStartTime = sessionStartTime;
|
|
71
|
+
this.clear();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.SessionAnalytics = SessionAnalytics;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage layer using AsyncStorage for persistence
|
|
3
|
+
* Handles event queue, user IDs, session data, and preferences
|
|
4
|
+
*/
|
|
5
|
+
import { AnalyticsEvent, SessionData } from './types';
|
|
6
|
+
export declare class Storage {
|
|
7
|
+
/**
|
|
8
|
+
* Get all queued events
|
|
9
|
+
*/
|
|
10
|
+
static getQueuedEvents(): Promise<AnalyticsEvent[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Save events to queue
|
|
13
|
+
*/
|
|
14
|
+
static saveQueuedEvents(events: AnalyticsEvent[]): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Add event to queue
|
|
17
|
+
*/
|
|
18
|
+
static addEventToQueue(event: AnalyticsEvent): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Remove events from queue
|
|
21
|
+
*/
|
|
22
|
+
static removeEventsFromQueue(eventIds: string[]): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Clear all queued events
|
|
25
|
+
*/
|
|
26
|
+
static clearQueue(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get anonymous ID (create if doesn't exist)
|
|
29
|
+
*/
|
|
30
|
+
static getAnonymousId(): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Get user ID
|
|
33
|
+
*/
|
|
34
|
+
static getUserId(): Promise<string | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Set user ID
|
|
37
|
+
*/
|
|
38
|
+
static setUserId(userId: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Clear user ID (on reset/logout)
|
|
41
|
+
*/
|
|
42
|
+
static clearUserId(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Get session data
|
|
45
|
+
*/
|
|
46
|
+
static getSessionData(): Promise<SessionData | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Save session data
|
|
49
|
+
*/
|
|
50
|
+
static saveSessionData(session: SessionData): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Clear session data
|
|
53
|
+
*/
|
|
54
|
+
static clearSessionData(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Check if user has opted out
|
|
57
|
+
*/
|
|
58
|
+
static isOptedOut(): Promise<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Set opt-out status
|
|
61
|
+
*/
|
|
62
|
+
static setOptOut(optOut: boolean): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Clear all analytics data
|
|
65
|
+
*/
|
|
66
|
+
static clearAll(): Promise<void>;
|
|
67
|
+
}
|