@rejourneyco/react-native 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 (152) hide show
  1. package/android/build.gradle.kts +135 -0
  2. package/android/consumer-rules.pro +10 -0
  3. package/android/proguard-rules.pro +1 -0
  4. package/android/src/main/AndroidManifest.xml +15 -0
  5. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +2981 -0
  6. package/android/src/main/java/com/rejourney/capture/ANRHandler.kt +206 -0
  7. package/android/src/main/java/com/rejourney/capture/ActivityTracker.kt +98 -0
  8. package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +1553 -0
  9. package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +375 -0
  10. package/android/src/main/java/com/rejourney/capture/CrashHandler.kt +153 -0
  11. package/android/src/main/java/com/rejourney/capture/MotionEvent.kt +215 -0
  12. package/android/src/main/java/com/rejourney/capture/SegmentUploader.kt +512 -0
  13. package/android/src/main/java/com/rejourney/capture/VideoEncoder.kt +773 -0
  14. package/android/src/main/java/com/rejourney/capture/ViewHierarchyScanner.kt +633 -0
  15. package/android/src/main/java/com/rejourney/capture/ViewSerializer.kt +286 -0
  16. package/android/src/main/java/com/rejourney/core/Constants.kt +117 -0
  17. package/android/src/main/java/com/rejourney/core/Logger.kt +93 -0
  18. package/android/src/main/java/com/rejourney/core/Types.kt +124 -0
  19. package/android/src/main/java/com/rejourney/lifecycle/SessionLifecycleService.kt +162 -0
  20. package/android/src/main/java/com/rejourney/network/DeviceAuthManager.kt +747 -0
  21. package/android/src/main/java/com/rejourney/network/HttpClientProvider.kt +16 -0
  22. package/android/src/main/java/com/rejourney/network/NetworkMonitor.kt +272 -0
  23. package/android/src/main/java/com/rejourney/network/UploadManager.kt +1363 -0
  24. package/android/src/main/java/com/rejourney/network/UploadWorker.kt +492 -0
  25. package/android/src/main/java/com/rejourney/privacy/PrivacyMask.kt +645 -0
  26. package/android/src/main/java/com/rejourney/touch/GestureClassifier.kt +233 -0
  27. package/android/src/main/java/com/rejourney/touch/KeyboardTracker.kt +158 -0
  28. package/android/src/main/java/com/rejourney/touch/TextInputTracker.kt +181 -0
  29. package/android/src/main/java/com/rejourney/touch/TouchInterceptor.kt +591 -0
  30. package/android/src/main/java/com/rejourney/utils/EventBuffer.kt +284 -0
  31. package/android/src/main/java/com/rejourney/utils/OEMDetector.kt +154 -0
  32. package/android/src/main/java/com/rejourney/utils/PerfTiming.kt +235 -0
  33. package/android/src/main/java/com/rejourney/utils/Telemetry.kt +297 -0
  34. package/android/src/main/java/com/rejourney/utils/WindowUtils.kt +84 -0
  35. package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +187 -0
  36. package/android/src/newarch/java/com/rejourney/RejourneyPackage.kt +40 -0
  37. package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +218 -0
  38. package/android/src/oldarch/java/com/rejourney/RejourneyPackage.kt +23 -0
  39. package/ios/Capture/RJANRHandler.h +42 -0
  40. package/ios/Capture/RJANRHandler.m +328 -0
  41. package/ios/Capture/RJCaptureEngine.h +275 -0
  42. package/ios/Capture/RJCaptureEngine.m +2062 -0
  43. package/ios/Capture/RJCaptureHeuristics.h +80 -0
  44. package/ios/Capture/RJCaptureHeuristics.m +903 -0
  45. package/ios/Capture/RJCrashHandler.h +46 -0
  46. package/ios/Capture/RJCrashHandler.m +313 -0
  47. package/ios/Capture/RJMotionEvent.h +183 -0
  48. package/ios/Capture/RJMotionEvent.m +183 -0
  49. package/ios/Capture/RJPerformanceManager.h +100 -0
  50. package/ios/Capture/RJPerformanceManager.m +373 -0
  51. package/ios/Capture/RJPixelBufferDownscaler.h +42 -0
  52. package/ios/Capture/RJPixelBufferDownscaler.m +85 -0
  53. package/ios/Capture/RJSegmentUploader.h +146 -0
  54. package/ios/Capture/RJSegmentUploader.m +778 -0
  55. package/ios/Capture/RJVideoEncoder.h +247 -0
  56. package/ios/Capture/RJVideoEncoder.m +1036 -0
  57. package/ios/Capture/RJViewControllerTracker.h +73 -0
  58. package/ios/Capture/RJViewControllerTracker.m +508 -0
  59. package/ios/Capture/RJViewHierarchyScanner.h +215 -0
  60. package/ios/Capture/RJViewHierarchyScanner.m +1464 -0
  61. package/ios/Capture/RJViewSerializer.h +119 -0
  62. package/ios/Capture/RJViewSerializer.m +498 -0
  63. package/ios/Core/RJConstants.h +124 -0
  64. package/ios/Core/RJConstants.m +88 -0
  65. package/ios/Core/RJLifecycleManager.h +85 -0
  66. package/ios/Core/RJLifecycleManager.m +308 -0
  67. package/ios/Core/RJLogger.h +61 -0
  68. package/ios/Core/RJLogger.m +211 -0
  69. package/ios/Core/RJTypes.h +176 -0
  70. package/ios/Core/RJTypes.m +66 -0
  71. package/ios/Core/Rejourney.h +64 -0
  72. package/ios/Core/Rejourney.mm +2495 -0
  73. package/ios/Network/RJDeviceAuthManager.h +94 -0
  74. package/ios/Network/RJDeviceAuthManager.m +967 -0
  75. package/ios/Network/RJNetworkMonitor.h +68 -0
  76. package/ios/Network/RJNetworkMonitor.m +267 -0
  77. package/ios/Network/RJRetryManager.h +73 -0
  78. package/ios/Network/RJRetryManager.m +325 -0
  79. package/ios/Network/RJUploadManager.h +267 -0
  80. package/ios/Network/RJUploadManager.m +2296 -0
  81. package/ios/Privacy/RJPrivacyMask.h +163 -0
  82. package/ios/Privacy/RJPrivacyMask.m +922 -0
  83. package/ios/Rejourney.h +63 -0
  84. package/ios/Touch/RJGestureClassifier.h +130 -0
  85. package/ios/Touch/RJGestureClassifier.m +333 -0
  86. package/ios/Touch/RJTouchInterceptor.h +169 -0
  87. package/ios/Touch/RJTouchInterceptor.m +772 -0
  88. package/ios/Utils/RJEventBuffer.h +112 -0
  89. package/ios/Utils/RJEventBuffer.m +358 -0
  90. package/ios/Utils/RJGzipUtils.h +33 -0
  91. package/ios/Utils/RJGzipUtils.m +89 -0
  92. package/ios/Utils/RJKeychainManager.h +48 -0
  93. package/ios/Utils/RJKeychainManager.m +111 -0
  94. package/ios/Utils/RJPerfTiming.h +209 -0
  95. package/ios/Utils/RJPerfTiming.m +264 -0
  96. package/ios/Utils/RJTelemetry.h +92 -0
  97. package/ios/Utils/RJTelemetry.m +320 -0
  98. package/ios/Utils/RJWindowUtils.h +66 -0
  99. package/ios/Utils/RJWindowUtils.m +133 -0
  100. package/lib/commonjs/NativeRejourney.js +40 -0
  101. package/lib/commonjs/components/Mask.js +79 -0
  102. package/lib/commonjs/index.js +1381 -0
  103. package/lib/commonjs/sdk/autoTracking.js +1259 -0
  104. package/lib/commonjs/sdk/constants.js +151 -0
  105. package/lib/commonjs/sdk/errorTracking.js +199 -0
  106. package/lib/commonjs/sdk/index.js +50 -0
  107. package/lib/commonjs/sdk/metricsTracking.js +204 -0
  108. package/lib/commonjs/sdk/navigation.js +151 -0
  109. package/lib/commonjs/sdk/networkInterceptor.js +412 -0
  110. package/lib/commonjs/sdk/utils.js +363 -0
  111. package/lib/commonjs/types/expo-router.d.js +2 -0
  112. package/lib/commonjs/types/index.js +2 -0
  113. package/lib/module/NativeRejourney.js +38 -0
  114. package/lib/module/components/Mask.js +72 -0
  115. package/lib/module/index.js +1284 -0
  116. package/lib/module/sdk/autoTracking.js +1233 -0
  117. package/lib/module/sdk/constants.js +145 -0
  118. package/lib/module/sdk/errorTracking.js +189 -0
  119. package/lib/module/sdk/index.js +12 -0
  120. package/lib/module/sdk/metricsTracking.js +187 -0
  121. package/lib/module/sdk/navigation.js +143 -0
  122. package/lib/module/sdk/networkInterceptor.js +401 -0
  123. package/lib/module/sdk/utils.js +342 -0
  124. package/lib/module/types/expo-router.d.js +2 -0
  125. package/lib/module/types/index.js +2 -0
  126. package/lib/typescript/NativeRejourney.d.ts +147 -0
  127. package/lib/typescript/components/Mask.d.ts +39 -0
  128. package/lib/typescript/index.d.ts +117 -0
  129. package/lib/typescript/sdk/autoTracking.d.ts +204 -0
  130. package/lib/typescript/sdk/constants.d.ts +120 -0
  131. package/lib/typescript/sdk/errorTracking.d.ts +32 -0
  132. package/lib/typescript/sdk/index.d.ts +9 -0
  133. package/lib/typescript/sdk/metricsTracking.d.ts +58 -0
  134. package/lib/typescript/sdk/navigation.d.ts +33 -0
  135. package/lib/typescript/sdk/networkInterceptor.d.ts +47 -0
  136. package/lib/typescript/sdk/utils.d.ts +148 -0
  137. package/lib/typescript/types/index.d.ts +624 -0
  138. package/package.json +102 -0
  139. package/rejourney.podspec +21 -0
  140. package/src/NativeRejourney.ts +165 -0
  141. package/src/components/Mask.tsx +80 -0
  142. package/src/index.ts +1459 -0
  143. package/src/sdk/autoTracking.ts +1373 -0
  144. package/src/sdk/constants.ts +134 -0
  145. package/src/sdk/errorTracking.ts +231 -0
  146. package/src/sdk/index.ts +11 -0
  147. package/src/sdk/metricsTracking.ts +232 -0
  148. package/src/sdk/navigation.ts +157 -0
  149. package/src/sdk/networkInterceptor.ts +440 -0
  150. package/src/sdk/utils.ts +369 -0
  151. package/src/types/expo-router.d.ts +7 -0
  152. package/src/types/index.ts +739 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Rejourney SDK Constants
3
+ */
4
+
5
+ export const SDK_VERSION = '1.0.0';
6
+
7
+ /** Default configuration values */
8
+ export const DEFAULT_CONFIG = {
9
+ enabled: true,
10
+ captureFPS: 0.5, // Every 2 seconds (only used in timer mode)
11
+ captureOnEvents: true, // Event-driven capture (not time-based)
12
+ maxSessionDuration: 10 * 60 * 1000, // 10 minutes (project-level configurable, clamped 1–10)
13
+ maxStorageSize: 50 * 1024 * 1024, // 50MB
14
+ autoScreenTracking: true,
15
+ autoGestureTracking: true,
16
+ privacyOcclusion: true,
17
+ enableCompression: true,
18
+ inactivityThreshold: 5000, // 5 seconds
19
+ disableInDev: false,
20
+ detectRageTaps: true,
21
+ rageTapThreshold: 3,
22
+ rageTapTimeWindow: 1000, // 1 second
23
+ debug: false,
24
+ autoStartRecording: true,
25
+ collectDeviceInfo: true, // Collect detailed device information
26
+ collectGeoLocation: true, // Collect IP address and geolocation
27
+ postNavigationDelay: 300, // 300ms - allow navigation animations to complete
28
+ postGestureDelay: 200, // 200ms - show result of taps, not animations
29
+ postModalDelay: 400, // 400ms - ensure modals/alerts are fully rendered
30
+ } as const;
31
+
32
+ /** Event type constants */
33
+ export const EVENT_TYPES = {
34
+ GESTURE: 'gesture',
35
+ SCREEN_CHANGE: 'screen_change',
36
+ CUSTOM: 'custom',
37
+ APP_STATE: 'app_state',
38
+ FRUSTRATION: 'frustration',
39
+ ERROR: 'error',
40
+ } as const;
41
+
42
+ /** Gesture type constants */
43
+ export const GESTURE_TYPES = {
44
+ TAP: 'tap',
45
+ DOUBLE_TAP: 'double_tap',
46
+ LONG_PRESS: 'long_press',
47
+ SWIPE_LEFT: 'swipe_left',
48
+ SWIPE_RIGHT: 'swipe_right',
49
+ SWIPE_UP: 'swipe_up',
50
+ SWIPE_DOWN: 'swipe_down',
51
+ PINCH: 'pinch',
52
+ SCROLL: 'scroll',
53
+ RAGE_TAP: 'rage_tap',
54
+ } as const;
55
+
56
+ /** Playback speeds */
57
+ export const PLAYBACK_SPEEDS = [0.5, 1, 2, 4] as const;
58
+
59
+ /** Capture settings */
60
+ export const CAPTURE_SETTINGS = {
61
+ DEFAULT_FPS: 0.5,
62
+ MIN_FPS: 0.1,
63
+ MAX_FPS: 2,
64
+ CAPTURE_SCALE: 0.25, // 25% resolution for video segments
65
+ MIN_CAPTURE_DELTA_TIME: 0.5, // Minimum 0.5s between captures (rate limiting)
66
+ } as const;
67
+
68
+ /** Memory management settings */
69
+ export const MEMORY_SETTINGS = {
70
+ /** Maximum events to keep in memory before flushing */
71
+ MAX_EVENTS_IN_MEMORY: 100,
72
+ /** Memory warning threshold in MB (flush when exceeded) */
73
+ MEMORY_WARNING_THRESHOLD_MB: 100,
74
+ /** Enable aggressive memory cleanup during low memory */
75
+ AGGRESSIVE_CLEANUP_ENABLED: true,
76
+ /** Bitmap pool size for reusing bitmaps (Android) */
77
+ BITMAP_POOL_SIZE: 3,
78
+ } as const;
79
+
80
+ /** CPU throttling settings */
81
+ export const CPU_SETTINGS = {
82
+ /** Throttle captures when CPU usage exceeds this percentage */
83
+ CPU_THROTTLE_THRESHOLD: 80,
84
+ /** Minimum interval between captures when throttled (seconds) */
85
+ THROTTLED_MIN_INTERVAL: 2.0,
86
+ /** Skip captures when battery is below this level (0-100) */
87
+ LOW_BATTERY_THRESHOLD: 15,
88
+ /** Skip captures when device is thermally throttled */
89
+ THERMAL_THROTTLE_ENABLED: true,
90
+ /** Maximum consecutive captures before forced cooldown */
91
+ MAX_CONSECUTIVE_CAPTURES: 10,
92
+ /** Cooldown period after max consecutive captures (ms) */
93
+ CAPTURE_COOLDOWN_MS: 1000,
94
+ } as const;
95
+
96
+ /** Storage management settings */
97
+ export const STORAGE_SETTINGS = {
98
+ /** Maximum total storage for session data (bytes) */
99
+ MAX_STORAGE_SIZE: 50 * 1024 * 1024, // 50MB
100
+ /** Storage warning threshold - start cleanup at this level */
101
+ STORAGE_WARNING_THRESHOLD: 0.8, // 80% of max
102
+ /** Number of old sessions to keep */
103
+ MAX_SESSIONS_TO_KEEP: 5,
104
+ /** Auto-delete sessions older than this (hours) */
105
+ SESSION_EXPIRY_HOURS: 24,
106
+ /** Use efficient binary storage format */
107
+ USE_BINARY_FORMAT: true,
108
+ } as const;
109
+
110
+ /** Network/Upload settings */
111
+ export const UPLOAD_SETTINGS = {
112
+ /** Batch upload interval (ms) */
113
+ BATCH_INTERVAL_MS: 30000, // 30 seconds
114
+ /** Max retry attempts for failed uploads */
115
+ MAX_RETRY_ATTEMPTS: 3,
116
+ /** Retry delay multiplier (exponential backoff) */
117
+ RETRY_DELAY_MULTIPLIER: 2,
118
+ /** Initial retry delay (ms) */
119
+ INITIAL_RETRY_DELAY_MS: 1000,
120
+ /** Max events per upload batch */
121
+ MAX_EVENTS_PER_BATCH: 50,
122
+ /** Skip uploads when on cellular and battery is low */
123
+ CELLULAR_BATTERY_AWARE: true,
124
+ } as const;
125
+
126
+ /** Privacy constants */
127
+ export const PRIVACY = {
128
+ OCCLUSION_COLOR: '#808080',
129
+ SENSITIVE_COMPONENT_TYPES: [
130
+ 'TextInput',
131
+ 'SecureTextEntry',
132
+ 'PasswordField',
133
+ ],
134
+ } as const;
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Error Tracking Module for Rejourney SDK
3
+ *
4
+ * Handles JS error capture, React Native ErrorUtils, and unhandled promise rejections.
5
+ * Split from autoTracking.ts for better code organization.
6
+ */
7
+
8
+ import type { ErrorEvent } from '../types';
9
+
10
+ // Type declarations for browser globals (only used in hybrid apps where DOM is available)
11
+ type OnErrorEventHandler = ((
12
+ event: Event | string,
13
+ source?: string,
14
+ lineno?: number,
15
+ colno?: number,
16
+ error?: Error
17
+ ) => boolean | void) | null;
18
+
19
+ interface PromiseRejectionEvent {
20
+ reason?: any;
21
+ promise?: Promise<any>;
22
+ }
23
+
24
+ // Cast globalThis to work with both RN and hybrid scenarios
25
+ const _globalThis = globalThis as typeof globalThis & {
26
+ onerror?: OnErrorEventHandler;
27
+ addEventListener?: (type: string, handler: (event: any) => void) => void;
28
+ removeEventListener?: (type: string, handler: (event: any) => void) => void;
29
+ ErrorUtils?: {
30
+ getGlobalHandler: () => ((error: Error, isFatal: boolean) => void) | undefined;
31
+ setGlobalHandler: (handler: (error: Error, isFatal: boolean) => void) => void;
32
+ };
33
+ };
34
+
35
+ // Original error handlers (for restoration)
36
+ let originalErrorHandler: ((error: Error, isFatal: boolean) => void) | undefined;
37
+ let originalOnError: OnErrorEventHandler | null = null;
38
+ let originalOnUnhandledRejection: ((event: PromiseRejectionEvent) => void) | null = null;
39
+
40
+ // Callbacks
41
+ let onErrorCallback: ((error: ErrorEvent) => void) | null = null;
42
+
43
+ // Metrics
44
+ let errorCount = 0;
45
+
46
+ /**
47
+ * Setup error tracking with the given callback
48
+ */
49
+ export function setupErrorTracking(
50
+ config: {
51
+ trackJSErrors?: boolean;
52
+ trackPromiseRejections?: boolean;
53
+ trackReactNativeErrors?: boolean;
54
+ },
55
+ onError: (error: ErrorEvent) => void
56
+ ): void {
57
+ onErrorCallback = onError;
58
+ errorCount = 0;
59
+
60
+ // Track React Native errors
61
+ if (config.trackReactNativeErrors !== false) {
62
+ setupReactNativeErrorHandler();
63
+ }
64
+
65
+ // Track JavaScript errors (only works in web/debug)
66
+ if (config.trackJSErrors !== false && typeof _globalThis !== 'undefined') {
67
+ setupJSErrorHandler();
68
+ }
69
+
70
+ // Track unhandled promise rejections
71
+ if (config.trackPromiseRejections !== false && typeof _globalThis !== 'undefined') {
72
+ setupPromiseRejectionHandler();
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Cleanup error tracking and restore original handlers
78
+ */
79
+ export function cleanupErrorTracking(): void {
80
+ // Restore React Native handler
81
+ if (originalErrorHandler) {
82
+ try {
83
+ const ErrorUtils = _globalThis.ErrorUtils;
84
+ if (ErrorUtils) {
85
+ ErrorUtils.setGlobalHandler(originalErrorHandler);
86
+ }
87
+ } catch {
88
+ // Ignore
89
+ }
90
+ originalErrorHandler = undefined;
91
+ }
92
+
93
+ // Restore global onerror
94
+ if (originalOnError !== null) {
95
+ _globalThis.onerror = originalOnError;
96
+ originalOnError = null;
97
+ }
98
+
99
+ // Remove promise rejection handler
100
+ if (originalOnUnhandledRejection && typeof _globalThis.removeEventListener !== 'undefined') {
101
+ _globalThis.removeEventListener!('unhandledrejection', originalOnUnhandledRejection);
102
+ originalOnUnhandledRejection = null;
103
+ }
104
+
105
+ onErrorCallback = null;
106
+ }
107
+
108
+ /**
109
+ * Get current error count
110
+ */
111
+ export function getErrorCount(): number {
112
+ return errorCount;
113
+ }
114
+
115
+ /**
116
+ * Reset error count
117
+ */
118
+ export function resetErrorCount(): void {
119
+ errorCount = 0;
120
+ }
121
+
122
+ /**
123
+ * Manually capture an error
124
+ */
125
+ export function captureError(
126
+ message: string,
127
+ stack?: string,
128
+ name?: string
129
+ ): void {
130
+ trackError({
131
+ type: 'error',
132
+ timestamp: Date.now(),
133
+ message,
134
+ stack,
135
+ name: name || 'Error',
136
+ });
137
+ }
138
+
139
+ /**
140
+ * Track an error internally
141
+ */
142
+ function trackError(error: ErrorEvent): void {
143
+ errorCount++;
144
+
145
+ if (onErrorCallback) {
146
+ onErrorCallback(error);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Setup React Native ErrorUtils handler
152
+ */
153
+ function setupReactNativeErrorHandler(): void {
154
+ try {
155
+ const ErrorUtils = _globalThis.ErrorUtils;
156
+ if (!ErrorUtils) return;
157
+
158
+ // Store original handler
159
+ originalErrorHandler = ErrorUtils.getGlobalHandler();
160
+
161
+ // Set new handler
162
+ ErrorUtils.setGlobalHandler((error: Error, isFatal: boolean) => {
163
+ trackError({
164
+ type: 'error',
165
+ timestamp: Date.now(),
166
+ message: error.message || String(error),
167
+ stack: error.stack,
168
+ name: error.name || 'Error',
169
+ });
170
+
171
+ // Call original handler
172
+ if (originalErrorHandler) {
173
+ originalErrorHandler(error, isFatal);
174
+ }
175
+ });
176
+ } catch {
177
+ // ErrorUtils not available
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Setup global JS error handler
183
+ */
184
+ function setupJSErrorHandler(): void {
185
+ if (typeof _globalThis.onerror !== 'undefined') {
186
+ originalOnError = _globalThis.onerror;
187
+
188
+ _globalThis.onerror = (
189
+ message: string | Event,
190
+ source?: string,
191
+ lineno?: number,
192
+ colno?: number,
193
+ error?: Error
194
+ ) => {
195
+ trackError({
196
+ type: 'error',
197
+ timestamp: Date.now(),
198
+ message: typeof message === 'string' ? message : 'Unknown error',
199
+ stack: error?.stack || `${source}:${lineno}:${colno}`,
200
+ name: error?.name || 'Error',
201
+ });
202
+
203
+ // Call original handler
204
+ if (originalOnError) {
205
+ return originalOnError(message, source, lineno, colno, error);
206
+ }
207
+ return false;
208
+ };
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Setup unhandled promise rejection handler
214
+ */
215
+ function setupPromiseRejectionHandler(): void {
216
+ if (typeof _globalThis.addEventListener !== 'undefined') {
217
+ const handler = (event: PromiseRejectionEvent) => {
218
+ const reason = event.reason;
219
+ trackError({
220
+ type: 'error',
221
+ timestamp: Date.now(),
222
+ message: reason?.message || String(reason) || 'Unhandled Promise Rejection',
223
+ stack: reason?.stack,
224
+ name: reason?.name || 'UnhandledRejection',
225
+ });
226
+ };
227
+
228
+ originalOnUnhandledRejection = handler;
229
+ _globalThis.addEventListener!('unhandledrejection', handler);
230
+ }
231
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Rejourney SDK
3
+ * Session recording and replay for React Native
4
+ */
5
+
6
+ export * from './constants';
7
+ export * from './utils';
8
+ export * from './autoTracking';
9
+ export * from './networkInterceptor';
10
+ // Note: errorTracking and metricsTracking are internal modules
11
+ // Their exports are re-exported through autoTracking for backward compatibility
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Session Metrics Module for Rejourney SDK
3
+ *
4
+ * Tracks and calculates session metrics including interaction scores,
5
+ * API performance, and user engagement metrics.
6
+ * Split from autoTracking.ts for better code organization.
7
+ */
8
+
9
+ /**
10
+ * Session metrics structure
11
+ */
12
+ export interface SessionMetrics {
13
+ // Core counts
14
+ totalEvents: number;
15
+ touchCount: number;
16
+ scrollCount: number;
17
+ gestureCount: number;
18
+ inputCount: number;
19
+ navigationCount: number;
20
+
21
+ // Issue tracking
22
+ errorCount: number;
23
+ rageTapCount: number;
24
+
25
+ // API metrics
26
+ apiSuccessCount: number;
27
+ apiErrorCount: number;
28
+ apiTotalCount: number;
29
+
30
+ // Network timing (for avg calculation)
31
+ netTotalDurationMs: number; // Sum of all API durations
32
+ netTotalBytes: number; // Sum of all response bytes
33
+
34
+ // Screen tracking for funnels
35
+ screensVisited: string[];
36
+ uniqueScreensCount: number;
37
+
38
+ // Scores (0-100)
39
+ interactionScore: number;
40
+ explorationScore: number;
41
+ uxScore: number;
42
+ }
43
+
44
+ // Metrics state
45
+ let metrics: SessionMetrics = createEmptyMetrics();
46
+ let sessionStartTime = 0;
47
+ let maxSessionDurationMs = 10 * 60 * 1000; // 10 minutes default
48
+
49
+ /**
50
+ * Create empty metrics object
51
+ */
52
+ export function createEmptyMetrics(): SessionMetrics {
53
+ return {
54
+ totalEvents: 0,
55
+ touchCount: 0,
56
+ scrollCount: 0,
57
+ gestureCount: 0,
58
+ inputCount: 0,
59
+ navigationCount: 0,
60
+ errorCount: 0,
61
+ rageTapCount: 0,
62
+ apiSuccessCount: 0,
63
+ apiErrorCount: 0,
64
+ apiTotalCount: 0,
65
+ netTotalDurationMs: 0,
66
+ netTotalBytes: 0,
67
+ screensVisited: [],
68
+ uniqueScreensCount: 0,
69
+ interactionScore: 100,
70
+ explorationScore: 100,
71
+ uxScore: 100,
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Reset all metrics
77
+ */
78
+ export function resetMetrics(): void {
79
+ metrics = createEmptyMetrics();
80
+ sessionStartTime = 0;
81
+ }
82
+
83
+ /**
84
+ * Initialize metrics for new session
85
+ */
86
+ export function initMetrics(): void {
87
+ metrics = createEmptyMetrics();
88
+ sessionStartTime = Date.now();
89
+ }
90
+
91
+ /**
92
+ * Get current session metrics with calculated scores
93
+ */
94
+ export function getSessionMetrics(): SessionMetrics {
95
+ // Calculate duration, clamped to max session duration
96
+ const rawDuration = Date.now() - sessionStartTime;
97
+ const durationMs = Math.min(rawDuration, maxSessionDurationMs);
98
+
99
+ // Calculate scores
100
+ const interactionScore = calculateInteractionScore(durationMs);
101
+ const explorationScore = calculateExplorationScore();
102
+ const uxScore = calculateUXScore();
103
+
104
+ return {
105
+ ...metrics,
106
+ interactionScore,
107
+ explorationScore,
108
+ uxScore,
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Set max session duration (in minutes)
114
+ */
115
+ export function setMaxSessionDurationMinutes(minutes?: number): void {
116
+ if (minutes !== undefined && minutes > 0) {
117
+ // Clamp to 1-10 minutes
118
+ const clampedMinutes = Math.max(1, Math.min(10, minutes));
119
+ maxSessionDurationMs = clampedMinutes * 60 * 1000;
120
+ }
121
+ }
122
+
123
+ // ==================== Metric Increment Methods ====================
124
+
125
+ export function incrementTouchCount(): void {
126
+ metrics.touchCount++;
127
+ metrics.totalEvents++;
128
+ }
129
+
130
+ export function incrementScrollCount(): void {
131
+ metrics.scrollCount++;
132
+ metrics.totalEvents++;
133
+ }
134
+
135
+ export function incrementNavigationCount(): void {
136
+ metrics.navigationCount++;
137
+ metrics.totalEvents++;
138
+ }
139
+
140
+ export function incrementRageTapCount(): void {
141
+ metrics.rageTapCount++;
142
+ }
143
+
144
+ export function incrementErrorCount(): void {
145
+ metrics.errorCount++;
146
+ metrics.totalEvents++;
147
+ }
148
+
149
+ export function addScreenVisited(screenName: string): void {
150
+ metrics.screensVisited.push(screenName);
151
+ metrics.uniqueScreensCount = new Set(metrics.screensVisited).size;
152
+ }
153
+
154
+ export function trackAPIMetrics(
155
+ success: boolean,
156
+ durationMs: number = 0,
157
+ responseBytes: number = 0
158
+ ): void {
159
+ metrics.apiTotalCount++;
160
+
161
+ if (durationMs > 0) {
162
+ metrics.netTotalDurationMs += durationMs;
163
+ }
164
+ if (responseBytes > 0) {
165
+ metrics.netTotalBytes += responseBytes;
166
+ }
167
+
168
+ if (success) {
169
+ metrics.apiSuccessCount++;
170
+ } else {
171
+ metrics.apiErrorCount++;
172
+ metrics.errorCount++;
173
+ }
174
+ }
175
+
176
+ // ==================== Score Calculations ====================
177
+
178
+ /**
179
+ * Calculate interaction score based on engagement with app
180
+ * Higher = more engaged (more interactions per minute)
181
+ */
182
+ function calculateInteractionScore(durationMs: number): number {
183
+ if (durationMs <= 0) return 100;
184
+
185
+ const durationMinutes = durationMs / 60000;
186
+ const interactionsPerMinute = metrics.touchCount / Math.max(0.5, durationMinutes);
187
+
188
+ // Ideal: 10-30 interactions per minute
189
+ // Low (< 5): user passive/confused
190
+ // Very high (> 60): rage tapping
191
+ if (interactionsPerMinute < 2) return 20;
192
+ if (interactionsPerMinute < 5) return 50;
193
+ if (interactionsPerMinute < 10) return 70;
194
+ if (interactionsPerMinute <= 30) return 100;
195
+ if (interactionsPerMinute <= 60) return 80;
196
+ return 50; // Very high might indicate frustration
197
+ }
198
+
199
+ /**
200
+ * Calculate exploration score based on screens visited
201
+ * Higher = user explored more of the app
202
+ */
203
+ function calculateExplorationScore(): number {
204
+ const uniqueScreens = metrics.uniqueScreensCount;
205
+
206
+ // More unique screens = better exploration
207
+ if (uniqueScreens >= 10) return 100;
208
+ if (uniqueScreens >= 7) return 90;
209
+ if (uniqueScreens >= 5) return 80;
210
+ if (uniqueScreens >= 3) return 60;
211
+ if (uniqueScreens >= 2) return 40;
212
+ return 20; // Single screen visit
213
+ }
214
+
215
+ /**
216
+ * Calculate UX score based on errors and frustration
217
+ * Higher = better experience (fewer issues)
218
+ */
219
+ function calculateUXScore(): number {
220
+ let score = 100;
221
+
222
+ // Deduct for errors
223
+ score -= metrics.errorCount * 10;
224
+
225
+ // Deduct heavily for rage taps
226
+ score -= metrics.rageTapCount * 20;
227
+
228
+ // Deduct for API errors (less severe)
229
+ score -= metrics.apiErrorCount * 5;
230
+
231
+ return Math.max(0, Math.min(100, score));
232
+ }