@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.
Files changed (43) hide show
  1. package/CONFIG.md +117 -0
  2. package/README.md +534 -0
  3. package/dist/client.d.ts +93 -0
  4. package/dist/client.js +416 -0
  5. package/dist/config.example.d.ts +8 -0
  6. package/dist/config.example.js +13 -0
  7. package/dist/configLoader.d.ts +10 -0
  8. package/dist/configLoader.js +22 -0
  9. package/dist/constants.d.ts +38 -0
  10. package/dist/constants.js +42 -0
  11. package/dist/context.d.ts +36 -0
  12. package/dist/context.js +216 -0
  13. package/dist/core.d.ts +9 -0
  14. package/dist/core.js +45 -0
  15. package/dist/hooks/useFeatureTracking.d.ts +34 -0
  16. package/dist/hooks/useFeatureTracking.js +60 -0
  17. package/dist/hooks/useOnboardingTracking.d.ts +85 -0
  18. package/dist/hooks/useOnboardingTracking.js +172 -0
  19. package/dist/hooks/useScreenTracking.d.ts +23 -0
  20. package/dist/hooks/useScreenTracking.js +52 -0
  21. package/dist/hooks/useScrollTracking.d.ts +39 -0
  22. package/dist/hooks/useScrollTracking.js +146 -0
  23. package/dist/index.d.ts +140 -0
  24. package/dist/index.js +171 -0
  25. package/dist/integrations/expoRouter.d.ts +42 -0
  26. package/dist/integrations/expoRouter.js +66 -0
  27. package/dist/integrations/reactNavigation.d.ts +27 -0
  28. package/dist/integrations/reactNavigation.js +101 -0
  29. package/dist/queue.d.ts +52 -0
  30. package/dist/queue.js +143 -0
  31. package/dist/session.d.ts +44 -0
  32. package/dist/session.js +139 -0
  33. package/dist/sessionAnalytics.d.ts +43 -0
  34. package/dist/sessionAnalytics.js +74 -0
  35. package/dist/storage.d.ts +67 -0
  36. package/dist/storage.js +210 -0
  37. package/dist/transport.d.ts +41 -0
  38. package/dist/transport.js +158 -0
  39. package/dist/types.d.ts +102 -0
  40. package/dist/types.js +5 -0
  41. package/dist/utils.d.ts +39 -0
  42. package/dist/utils.js +116 -0
  43. 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
+ }
@@ -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
+ }
@@ -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
+ }