@rejourneyco/react-native 1.0.1 → 1.0.2
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/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +35 -29
- package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +7 -0
- package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +9 -0
- package/ios/Capture/RJCaptureEngine.m +3 -34
- package/ios/Capture/RJVideoEncoder.m +0 -26
- package/ios/Core/Rejourney.mm +32 -95
- package/ios/Utils/RJPerfTiming.m +0 -5
- package/ios/Utils/RJWindowUtils.m +0 -1
- package/lib/commonjs/components/Mask.js +1 -6
- package/lib/commonjs/index.js +4 -87
- package/lib/commonjs/sdk/autoTracking.js +39 -310
- package/lib/commonjs/sdk/constants.js +2 -13
- package/lib/commonjs/sdk/errorTracking.js +1 -29
- package/lib/commonjs/sdk/metricsTracking.js +3 -24
- package/lib/commonjs/sdk/navigation.js +3 -42
- package/lib/commonjs/sdk/networkInterceptor.js +7 -49
- package/lib/commonjs/sdk/utils.js +0 -5
- package/lib/module/components/Mask.js +1 -6
- package/lib/module/index.js +3 -91
- package/lib/module/sdk/autoTracking.js +39 -311
- package/lib/module/sdk/constants.js +2 -13
- package/lib/module/sdk/errorTracking.js +1 -29
- package/lib/module/sdk/index.js +0 -2
- package/lib/module/sdk/metricsTracking.js +3 -24
- package/lib/module/sdk/navigation.js +3 -42
- package/lib/module/sdk/networkInterceptor.js +7 -49
- package/lib/module/sdk/utils.js +0 -5
- package/lib/typescript/NativeRejourney.d.ts +1 -0
- package/lib/typescript/sdk/autoTracking.d.ts +4 -4
- package/lib/typescript/types/index.d.ts +0 -1
- package/package.json +2 -8
- package/src/NativeRejourney.ts +2 -0
- package/src/components/Mask.tsx +0 -3
- package/src/index.ts +3 -73
- package/src/sdk/autoTracking.ts +51 -282
- package/src/sdk/constants.ts +13 -13
- package/src/sdk/errorTracking.ts +1 -17
- package/src/sdk/index.ts +0 -2
- package/src/sdk/metricsTracking.ts +5 -33
- package/src/sdk/navigation.ts +8 -29
- package/src/sdk/networkInterceptor.ts +9 -33
- package/src/sdk/utils.ts +0 -5
- package/src/types/index.ts +0 -29
|
@@ -14,35 +14,25 @@ const SDK_VERSION = exports.SDK_VERSION = '1.0.0';
|
|
|
14
14
|
const DEFAULT_CONFIG = exports.DEFAULT_CONFIG = {
|
|
15
15
|
enabled: true,
|
|
16
16
|
captureFPS: 0.5,
|
|
17
|
-
// Every 2 seconds (only used in timer mode)
|
|
18
17
|
captureOnEvents: true,
|
|
19
|
-
// Event-driven capture (not time-based)
|
|
20
18
|
maxSessionDuration: 10 * 60 * 1000,
|
|
21
|
-
// 10 minutes (project-level configurable, clamped 1–10)
|
|
22
19
|
maxStorageSize: 50 * 1024 * 1024,
|
|
23
|
-
// 50MB
|
|
24
20
|
autoScreenTracking: true,
|
|
25
21
|
autoGestureTracking: true,
|
|
26
22
|
privacyOcclusion: true,
|
|
27
23
|
enableCompression: true,
|
|
28
24
|
inactivityThreshold: 5000,
|
|
29
|
-
// 5 seconds
|
|
30
25
|
disableInDev: false,
|
|
31
26
|
detectRageTaps: true,
|
|
32
27
|
rageTapThreshold: 3,
|
|
33
28
|
rageTapTimeWindow: 1000,
|
|
34
|
-
// 1 second
|
|
35
29
|
debug: false,
|
|
36
30
|
autoStartRecording: true,
|
|
37
31
|
collectDeviceInfo: true,
|
|
38
|
-
// Collect detailed device information
|
|
39
32
|
collectGeoLocation: true,
|
|
40
|
-
// Collect IP address and geolocation
|
|
41
33
|
postNavigationDelay: 300,
|
|
42
|
-
// 300ms - allow navigation animations to complete
|
|
43
34
|
postGestureDelay: 200,
|
|
44
|
-
|
|
45
|
-
postModalDelay: 400 // 400ms - ensure modals/alerts are fully rendered
|
|
35
|
+
postModalDelay: 400
|
|
46
36
|
};
|
|
47
37
|
|
|
48
38
|
/** Event type constants */
|
|
@@ -78,8 +68,7 @@ const CAPTURE_SETTINGS = exports.CAPTURE_SETTINGS = {
|
|
|
78
68
|
MIN_FPS: 0.1,
|
|
79
69
|
MAX_FPS: 2,
|
|
80
70
|
CAPTURE_SCALE: 0.25,
|
|
81
|
-
|
|
82
|
-
MIN_CAPTURE_DELTA_TIME: 0.5 // Minimum 0.5s between captures (rate limiting)
|
|
71
|
+
MIN_CAPTURE_DELTA_TIME: 0.5
|
|
83
72
|
};
|
|
84
73
|
|
|
85
74
|
/** Memory management settings */
|
|
@@ -15,20 +15,11 @@ exports.setupErrorTracking = setupErrorTracking;
|
|
|
15
15
|
* Split from autoTracking.ts for better code organization.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
// Type declarations for browser globals (only used in hybrid apps where DOM is available)
|
|
19
|
-
|
|
20
|
-
// Cast globalThis to work with both RN and hybrid scenarios
|
|
21
18
|
const _globalThis = globalThis;
|
|
22
|
-
|
|
23
|
-
// Original error handlers (for restoration)
|
|
24
19
|
let originalErrorHandler;
|
|
25
20
|
let originalOnError = null;
|
|
26
21
|
let originalOnUnhandledRejection = null;
|
|
27
|
-
|
|
28
|
-
// Callbacks
|
|
29
22
|
let onErrorCallback = null;
|
|
30
|
-
|
|
31
|
-
// Metrics
|
|
32
23
|
let errorCount = 0;
|
|
33
24
|
|
|
34
25
|
/**
|
|
@@ -37,18 +28,12 @@ let errorCount = 0;
|
|
|
37
28
|
function setupErrorTracking(config, onError) {
|
|
38
29
|
onErrorCallback = onError;
|
|
39
30
|
errorCount = 0;
|
|
40
|
-
|
|
41
|
-
// Track React Native errors
|
|
42
31
|
if (config.trackReactNativeErrors !== false) {
|
|
43
32
|
setupReactNativeErrorHandler();
|
|
44
33
|
}
|
|
45
|
-
|
|
46
|
-
// Track JavaScript errors (only works in web/debug)
|
|
47
34
|
if (config.trackJSErrors !== false && typeof _globalThis !== 'undefined') {
|
|
48
35
|
setupJSErrorHandler();
|
|
49
36
|
}
|
|
50
|
-
|
|
51
|
-
// Track unhandled promise rejections
|
|
52
37
|
if (config.trackPromiseRejections !== false && typeof _globalThis !== 'undefined') {
|
|
53
38
|
setupPromiseRejectionHandler();
|
|
54
39
|
}
|
|
@@ -58,7 +43,6 @@ function setupErrorTracking(config, onError) {
|
|
|
58
43
|
* Cleanup error tracking and restore original handlers
|
|
59
44
|
*/
|
|
60
45
|
function cleanupErrorTracking() {
|
|
61
|
-
// Restore React Native handler
|
|
62
46
|
if (originalErrorHandler) {
|
|
63
47
|
try {
|
|
64
48
|
const ErrorUtils = _globalThis.ErrorUtils;
|
|
@@ -70,14 +54,10 @@ function cleanupErrorTracking() {
|
|
|
70
54
|
}
|
|
71
55
|
originalErrorHandler = undefined;
|
|
72
56
|
}
|
|
73
|
-
|
|
74
|
-
// Restore global onerror
|
|
75
57
|
if (originalOnError !== null) {
|
|
76
58
|
_globalThis.onerror = originalOnError;
|
|
77
59
|
originalOnError = null;
|
|
78
60
|
}
|
|
79
|
-
|
|
80
|
-
// Remove promise rejection handler
|
|
81
61
|
if (originalOnUnhandledRejection && typeof _globalThis.removeEventListener !== 'undefined') {
|
|
82
62
|
_globalThis.removeEventListener('unhandledrejection', originalOnUnhandledRejection);
|
|
83
63
|
originalOnUnhandledRejection = null;
|
|
@@ -129,11 +109,7 @@ function setupReactNativeErrorHandler() {
|
|
|
129
109
|
try {
|
|
130
110
|
const ErrorUtils = _globalThis.ErrorUtils;
|
|
131
111
|
if (!ErrorUtils) return;
|
|
132
|
-
|
|
133
|
-
// Store original handler
|
|
134
112
|
originalErrorHandler = ErrorUtils.getGlobalHandler();
|
|
135
|
-
|
|
136
|
-
// Set new handler
|
|
137
113
|
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
138
114
|
trackError({
|
|
139
115
|
type: 'error',
|
|
@@ -142,14 +118,12 @@ function setupReactNativeErrorHandler() {
|
|
|
142
118
|
stack: error.stack,
|
|
143
119
|
name: error.name || 'Error'
|
|
144
120
|
});
|
|
145
|
-
|
|
146
|
-
// Call original handler
|
|
147
121
|
if (originalErrorHandler) {
|
|
148
122
|
originalErrorHandler(error, isFatal);
|
|
149
123
|
}
|
|
150
124
|
});
|
|
151
125
|
} catch {
|
|
152
|
-
//
|
|
126
|
+
// Ignore
|
|
153
127
|
}
|
|
154
128
|
}
|
|
155
129
|
|
|
@@ -167,8 +141,6 @@ function setupJSErrorHandler() {
|
|
|
167
141
|
stack: error?.stack || `${source}:${lineno}:${colno}`,
|
|
168
142
|
name: error?.name || 'Error'
|
|
169
143
|
});
|
|
170
|
-
|
|
171
|
-
// Call original handler
|
|
172
144
|
if (originalOnError) {
|
|
173
145
|
return originalOnError(message, source, lineno, colno, error);
|
|
174
146
|
}
|
|
@@ -27,10 +27,9 @@ exports.trackAPIMetrics = trackAPIMetrics;
|
|
|
27
27
|
* Session metrics structure
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
// Metrics state
|
|
31
30
|
let metrics = createEmptyMetrics();
|
|
32
31
|
let sessionStartTime = 0;
|
|
33
|
-
let maxSessionDurationMs = 10 * 60 * 1000;
|
|
32
|
+
let maxSessionDurationMs = 10 * 60 * 1000;
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
35
|
* Create empty metrics object
|
|
@@ -78,11 +77,8 @@ function initMetrics() {
|
|
|
78
77
|
* Get current session metrics with calculated scores
|
|
79
78
|
*/
|
|
80
79
|
function getSessionMetrics() {
|
|
81
|
-
// Calculate duration, clamped to max session duration
|
|
82
80
|
const rawDuration = Date.now() - sessionStartTime;
|
|
83
81
|
const durationMs = Math.min(rawDuration, maxSessionDurationMs);
|
|
84
|
-
|
|
85
|
-
// Calculate scores
|
|
86
82
|
const interactionScore = calculateInteractionScore(durationMs);
|
|
87
83
|
const explorationScore = calculateExplorationScore();
|
|
88
84
|
const uxScore = calculateUXScore();
|
|
@@ -104,9 +100,6 @@ function setMaxSessionDurationMinutes(minutes) {
|
|
|
104
100
|
maxSessionDurationMs = clampedMinutes * 60 * 1000;
|
|
105
101
|
}
|
|
106
102
|
}
|
|
107
|
-
|
|
108
|
-
// ==================== Metric Increment Methods ====================
|
|
109
|
-
|
|
110
103
|
function incrementTouchCount() {
|
|
111
104
|
metrics.touchCount++;
|
|
112
105
|
metrics.totalEvents++;
|
|
@@ -146,8 +139,6 @@ function trackAPIMetrics(success, durationMs = 0, responseBytes = 0) {
|
|
|
146
139
|
}
|
|
147
140
|
}
|
|
148
141
|
|
|
149
|
-
// ==================== Score Calculations ====================
|
|
150
|
-
|
|
151
142
|
/**
|
|
152
143
|
* Calculate interaction score based on engagement with app
|
|
153
144
|
* Higher = more engaged (more interactions per minute)
|
|
@@ -156,16 +147,12 @@ function calculateInteractionScore(durationMs) {
|
|
|
156
147
|
if (durationMs <= 0) return 100;
|
|
157
148
|
const durationMinutes = durationMs / 60000;
|
|
158
149
|
const interactionsPerMinute = metrics.touchCount / Math.max(0.5, durationMinutes);
|
|
159
|
-
|
|
160
|
-
// Ideal: 10-30 interactions per minute
|
|
161
|
-
// Low (< 5): user passive/confused
|
|
162
|
-
// Very high (> 60): rage tapping
|
|
163
150
|
if (interactionsPerMinute < 2) return 20;
|
|
164
151
|
if (interactionsPerMinute < 5) return 50;
|
|
165
152
|
if (interactionsPerMinute < 10) return 70;
|
|
166
153
|
if (interactionsPerMinute <= 30) return 100;
|
|
167
154
|
if (interactionsPerMinute <= 60) return 80;
|
|
168
|
-
return 50;
|
|
155
|
+
return 50;
|
|
169
156
|
}
|
|
170
157
|
|
|
171
158
|
/**
|
|
@@ -174,14 +161,12 @@ function calculateInteractionScore(durationMs) {
|
|
|
174
161
|
*/
|
|
175
162
|
function calculateExplorationScore() {
|
|
176
163
|
const uniqueScreens = metrics.uniqueScreensCount;
|
|
177
|
-
|
|
178
|
-
// More unique screens = better exploration
|
|
179
164
|
if (uniqueScreens >= 10) return 100;
|
|
180
165
|
if (uniqueScreens >= 7) return 90;
|
|
181
166
|
if (uniqueScreens >= 5) return 80;
|
|
182
167
|
if (uniqueScreens >= 3) return 60;
|
|
183
168
|
if (uniqueScreens >= 2) return 40;
|
|
184
|
-
return 20;
|
|
169
|
+
return 20;
|
|
185
170
|
}
|
|
186
171
|
|
|
187
172
|
/**
|
|
@@ -190,14 +175,8 @@ function calculateExplorationScore() {
|
|
|
190
175
|
*/
|
|
191
176
|
function calculateUXScore() {
|
|
192
177
|
let score = 100;
|
|
193
|
-
|
|
194
|
-
// Deduct for errors
|
|
195
178
|
score -= metrics.errorCount * 10;
|
|
196
|
-
|
|
197
|
-
// Deduct heavily for rage taps
|
|
198
179
|
score -= metrics.rageTapCount * 20;
|
|
199
|
-
|
|
200
|
-
// Deduct for API errors (less severe)
|
|
201
180
|
score -= metrics.apiErrorCount * 5;
|
|
202
181
|
return Math.max(0, Math.min(100, score));
|
|
203
182
|
}
|
|
@@ -26,46 +26,28 @@ exports.normalizeScreenName = normalizeScreenName;
|
|
|
26
26
|
function normalizeScreenName(raw) {
|
|
27
27
|
if (!raw) return 'Unknown';
|
|
28
28
|
let name = raw;
|
|
29
|
-
|
|
30
|
-
// 1. Remove non-printable characters and weird symbols (often from icons)
|
|
31
29
|
name = name.replace(/[^\x20-\x7E\s]/g, '');
|
|
32
|
-
|
|
33
|
-
// 2. Handle hyphens: my-recipes -> My Recipes
|
|
34
30
|
name = name.split(/[-_]/).map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
|
|
35
|
-
|
|
36
|
-
// 3. Remove common suffixes
|
|
37
31
|
const suffixes = ['Screen', 'Page', 'View', 'Controller', 'ViewController', 'VC'];
|
|
38
32
|
for (const suffix of suffixes) {
|
|
39
33
|
if (name.endsWith(suffix) && name.length > suffix.length) {
|
|
40
34
|
name = name.slice(0, -suffix.length).trim();
|
|
41
35
|
}
|
|
42
36
|
}
|
|
43
|
-
|
|
44
|
-
// 4. Remove common prefixes (React Native internals)
|
|
45
37
|
const prefixes = ['RNS', 'RCT', 'RN', 'UI'];
|
|
46
38
|
for (const prefix of prefixes) {
|
|
47
39
|
if (name.startsWith(prefix) && name.length > prefix.length + 2) {
|
|
48
40
|
name = name.slice(prefix.length).trim();
|
|
49
41
|
}
|
|
50
42
|
}
|
|
51
|
-
|
|
52
|
-
// 5. Handle dynamic route params: [id] -> (omit), [userId] -> "User"
|
|
53
43
|
name = name.replace(/\[([a-zA-Z]+)(?:Id)?\]/g, (_, param) => {
|
|
54
44
|
const clean = param.replace(/Id$/i, '');
|
|
55
45
|
if (clean.length < 2) return '';
|
|
56
46
|
return clean.charAt(0).toUpperCase() + clean.slice(1);
|
|
57
47
|
});
|
|
58
|
-
|
|
59
|
-
// 6. Remove leftover brackets
|
|
60
48
|
name = name.replace(/\[\]/g, '');
|
|
61
|
-
|
|
62
|
-
// 7. Convert camelCase/PascalCase to Title Case with spaces
|
|
63
49
|
name = name.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
64
|
-
|
|
65
|
-
// 8. Clean up multiple spaces and trim
|
|
66
50
|
name = name.replace(/\s+/g, ' ').trim();
|
|
67
|
-
|
|
68
|
-
// 9. Capitalize first letter
|
|
69
51
|
if (name.length > 0) {
|
|
70
52
|
name = name.charAt(0).toUpperCase() + name.slice(1);
|
|
71
53
|
}
|
|
@@ -80,23 +62,17 @@ function normalizeScreenName(raw) {
|
|
|
80
62
|
* @returns Human-readable screen name
|
|
81
63
|
*/
|
|
82
64
|
function getScreenNameFromPath(pathname, segments) {
|
|
83
|
-
// Use segments for cleaner names if available
|
|
84
65
|
if (segments.length > 0) {
|
|
85
|
-
// Filter out group markers like (tabs), (auth), etc.
|
|
86
66
|
const cleanSegments = segments.filter(s => !s.startsWith('(') && !s.endsWith(')'));
|
|
87
67
|
if (cleanSegments.length > 0) {
|
|
88
|
-
// Process each segment
|
|
89
68
|
const processedSegments = cleanSegments.map(s => {
|
|
90
|
-
// Handle dynamic params like [id]
|
|
91
69
|
if (s.startsWith('[') && s.endsWith(']')) {
|
|
92
70
|
const param = s.slice(1, -1);
|
|
93
|
-
// Skip pure ID params, keep meaningful ones
|
|
94
71
|
if (param === 'id' || param === 'slug') return null;
|
|
95
|
-
|
|
72
|
+
if (param === 'id' || param === 'slug') return null;
|
|
96
73
|
const clean = param.replace(/Id$/i, '');
|
|
97
74
|
return clean.charAt(0).toUpperCase() + clean.slice(1);
|
|
98
75
|
}
|
|
99
|
-
// Regular segment - capitalize
|
|
100
76
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
101
77
|
}).filter(Boolean);
|
|
102
78
|
if (processedSegments.length > 0) {
|
|
@@ -104,30 +80,17 @@ function getScreenNameFromPath(pathname, segments) {
|
|
|
104
80
|
}
|
|
105
81
|
}
|
|
106
82
|
}
|
|
107
|
-
|
|
108
|
-
// Fall back to pathname
|
|
109
83
|
if (!pathname || pathname === '/') {
|
|
110
84
|
return 'Home';
|
|
111
85
|
}
|
|
112
|
-
|
|
113
|
-
// Clean up the path
|
|
114
|
-
let cleanPath = pathname.replace(/^\/(tabs)?/, '') // Remove leading slash and (tabs)
|
|
115
|
-
.replace(/\([^)]+\)/g, '') // Remove all group markers like (settings)
|
|
116
|
-
.replace(/\[([^\]]+)\]/g, (_, param) => {
|
|
117
|
-
// Handle dynamic params in path
|
|
86
|
+
const cleanPath = pathname.replace(/^\/(tabs)?/, '').replace(/\([^)]+\)/g, '').replace(/\[([^\]]+)\]/g, (_, param) => {
|
|
118
87
|
if (param === 'id' || param === 'slug') return '';
|
|
119
88
|
const clean = param.replace(/Id$/i, '');
|
|
120
89
|
return clean.charAt(0).toUpperCase() + clean.slice(1);
|
|
121
|
-
}).replace(/\/+/g, '/')
|
|
122
|
-
.replace(/^\//, '') // Remove leading slash
|
|
123
|
-
.replace(/\/$/, '') // Remove trailing slash
|
|
124
|
-
.replace(/\//g, ' > ') // Replace slashes with arrows
|
|
125
|
-
.trim();
|
|
90
|
+
}).replace(/\/+/g, '/').replace(/^\//, '').replace(/\/$/, '').replace(/\//g, ' > ').trim();
|
|
126
91
|
if (!cleanPath) {
|
|
127
92
|
return 'Home';
|
|
128
93
|
}
|
|
129
|
-
|
|
130
|
-
// Capitalize first letter of each word
|
|
131
94
|
return cleanPath.split(' > ').map(s => s.charAt(0).toUpperCase() + s.slice(1)).filter(s => s.length > 0).join(' > ') || 'Home';
|
|
132
95
|
}
|
|
133
96
|
|
|
@@ -141,8 +104,6 @@ function getCurrentRouteFromState(state) {
|
|
|
141
104
|
if (!state || !state.routes) return null;
|
|
142
105
|
const route = state.routes[state.index ?? state.routes.length - 1];
|
|
143
106
|
if (!route) return null;
|
|
144
|
-
|
|
145
|
-
// If nested state, recurse
|
|
146
107
|
if (route.state) {
|
|
147
108
|
return getCurrentRouteFromState(route.state);
|
|
148
109
|
}
|
|
@@ -24,41 +24,26 @@ exports.restoreNetworkInterceptor = restoreNetworkInterceptor;
|
|
|
24
24
|
* - PII Scrubbing for query parameters
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
// Store original implementations
|
|
28
27
|
let originalFetch = null;
|
|
29
28
|
let originalXHROpen = null;
|
|
30
29
|
let originalXHRSend = null;
|
|
31
|
-
|
|
32
|
-
// Callback to log network requests (called asynchronously)
|
|
33
30
|
let logCallback = null;
|
|
34
|
-
|
|
35
|
-
// Pending requests buffer (circular buffer for memory efficiency)
|
|
36
31
|
const MAX_PENDING_REQUESTS = 100;
|
|
37
32
|
const pendingRequests = new Array(MAX_PENDING_REQUESTS).fill(null);
|
|
38
33
|
let pendingHead = 0;
|
|
39
34
|
let pendingTail = 0;
|
|
40
35
|
let pendingCount = 0;
|
|
41
|
-
|
|
42
|
-
// Flush timer
|
|
43
36
|
let flushTimer = null;
|
|
44
|
-
const FLUSH_INTERVAL = 500;
|
|
45
|
-
|
|
46
|
-
// Sampling for high-frequency endpoints
|
|
37
|
+
const FLUSH_INTERVAL = 500;
|
|
47
38
|
const endpointCounts = new Map();
|
|
48
|
-
const SAMPLE_WINDOW = 10000;
|
|
49
|
-
const MAX_PER_ENDPOINT = 20;
|
|
50
|
-
|
|
51
|
-
// Configuration
|
|
39
|
+
const SAMPLE_WINDOW = 10000;
|
|
40
|
+
const MAX_PER_ENDPOINT = 20;
|
|
52
41
|
const config = {
|
|
53
42
|
enabled: true,
|
|
54
43
|
ignorePatterns: [],
|
|
55
|
-
// Simple string patterns for fast matching
|
|
56
44
|
maxUrlLength: 300,
|
|
57
|
-
|
|
58
|
-
captureSizes: false // Disabled by default for performance
|
|
45
|
+
captureSizes: false
|
|
59
46
|
};
|
|
60
|
-
|
|
61
|
-
// PII Scrubbing - Sensitive keys to look for in query params
|
|
62
47
|
const SENSITIVE_KEYS = ['token', 'key', 'secret', 'password', 'auth', 'access_token', 'api_key'];
|
|
63
48
|
|
|
64
49
|
/**
|
|
@@ -66,7 +51,6 @@ const SENSITIVE_KEYS = ['token', 'key', 'secret', 'password', 'auth', 'access_to
|
|
|
66
51
|
*/
|
|
67
52
|
function scrubUrl(url) {
|
|
68
53
|
try {
|
|
69
|
-
// Fast check if URL might contain params
|
|
70
54
|
if (url.indexOf('?') === -1) return url;
|
|
71
55
|
const urlObj = new URL(url);
|
|
72
56
|
let modified = false;
|
|
@@ -76,13 +60,10 @@ function scrubUrl(url) {
|
|
|
76
60
|
modified = true;
|
|
77
61
|
}
|
|
78
62
|
});
|
|
79
|
-
|
|
80
|
-
// Also scan for partial matches (case-insensitive) if strict scrubbing needed
|
|
81
|
-
// But for performance, we stick to exact keys or common variations
|
|
82
|
-
|
|
83
63
|
return modified ? urlObj.toString() : url;
|
|
84
64
|
} catch {
|
|
85
|
-
//
|
|
65
|
+
// Ignore error, fallback to primitive scrubbing
|
|
66
|
+
|
|
86
67
|
let scrubbed = url;
|
|
87
68
|
SENSITIVE_KEYS.forEach(key => {
|
|
88
69
|
const regex = new RegExp(`([?&])${key}=[^&]*`, 'gi');
|
|
@@ -130,14 +111,10 @@ function queueRequest(request) {
|
|
|
130
111
|
pendingHead = (pendingHead + 1) % MAX_PENDING_REQUESTS;
|
|
131
112
|
pendingCount--;
|
|
132
113
|
}
|
|
133
|
-
|
|
134
|
-
// Scrub URL before queuing
|
|
135
114
|
request.url = scrubUrl(request.url);
|
|
136
115
|
pendingRequests[pendingTail] = request;
|
|
137
116
|
pendingTail = (pendingTail + 1) % MAX_PENDING_REQUESTS;
|
|
138
117
|
pendingCount++;
|
|
139
|
-
|
|
140
|
-
// Schedule flush if not already scheduled
|
|
141
118
|
if (!flushTimer) {
|
|
142
119
|
flushTimer = setTimeout(flushPendingRequests, FLUSH_INTERVAL);
|
|
143
120
|
}
|
|
@@ -160,7 +137,7 @@ function flushPendingRequests() {
|
|
|
160
137
|
try {
|
|
161
138
|
logCallback(request);
|
|
162
139
|
} catch {
|
|
163
|
-
// Ignore
|
|
140
|
+
// Ignore
|
|
164
141
|
}
|
|
165
142
|
}
|
|
166
143
|
}
|
|
@@ -173,12 +150,9 @@ function parseUrlFast(url) {
|
|
|
173
150
|
// Fast path for common patterns
|
|
174
151
|
let hostEnd = -1;
|
|
175
152
|
let pathStart = -1;
|
|
176
|
-
|
|
177
|
-
// Find ://
|
|
178
153
|
const protoEnd = url.indexOf('://');
|
|
179
154
|
if (protoEnd !== -1) {
|
|
180
155
|
const afterProto = protoEnd + 3;
|
|
181
|
-
// Find end of host (first / after ://)
|
|
182
156
|
const slashPos = url.indexOf('/', afterProto);
|
|
183
157
|
if (slashPos !== -1) {
|
|
184
158
|
hostEnd = slashPos;
|
|
@@ -192,8 +166,6 @@ function parseUrlFast(url) {
|
|
|
192
166
|
path: pathStart < url.length ? url.substring(pathStart) : '/'
|
|
193
167
|
};
|
|
194
168
|
}
|
|
195
|
-
|
|
196
|
-
// Relative URL
|
|
197
169
|
return {
|
|
198
170
|
host: '',
|
|
199
171
|
path: url
|
|
@@ -208,15 +180,10 @@ function interceptFetch() {
|
|
|
208
180
|
if (originalFetch) return;
|
|
209
181
|
originalFetch = globalThis.fetch;
|
|
210
182
|
globalThis.fetch = function optimizedFetch(input, init) {
|
|
211
|
-
// Fast path: if disabled or no callback, skip entirely
|
|
212
183
|
if (!config.enabled || !logCallback) {
|
|
213
184
|
return originalFetch(input, init);
|
|
214
185
|
}
|
|
215
|
-
|
|
216
|
-
// Extract URL string (minimal work)
|
|
217
186
|
const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
|
|
218
|
-
|
|
219
|
-
// Fast ignore check
|
|
220
187
|
if (shouldIgnoreUrl(url)) {
|
|
221
188
|
return originalFetch(input, init);
|
|
222
189
|
}
|
|
@@ -248,7 +215,6 @@ function interceptFetch() {
|
|
|
248
215
|
});
|
|
249
216
|
return response;
|
|
250
217
|
}, error => {
|
|
251
|
-
// Error - queue the log asynchronously
|
|
252
218
|
queueRequest({
|
|
253
219
|
requestId: `f${startTime}`,
|
|
254
220
|
method,
|
|
@@ -275,8 +241,6 @@ function interceptXHR() {
|
|
|
275
241
|
originalXHRSend = XMLHttpRequest.prototype.send;
|
|
276
242
|
XMLHttpRequest.prototype.open = function (method, url, async = true, username, password) {
|
|
277
243
|
const urlString = typeof url === 'string' ? url : url.toString();
|
|
278
|
-
|
|
279
|
-
// Store minimal info
|
|
280
244
|
this.__rj = {
|
|
281
245
|
m: method.toUpperCase(),
|
|
282
246
|
u: urlString,
|
|
@@ -312,9 +276,6 @@ function interceptXHR() {
|
|
|
312
276
|
errorMessage: this.status === 0 ? 'Network error' : undefined
|
|
313
277
|
});
|
|
314
278
|
};
|
|
315
|
-
|
|
316
|
-
// Use load/error events (more efficient than readystatechange)
|
|
317
|
-
// Note: We use basic addEventListener without options for RN compatibility
|
|
318
279
|
this.addEventListener('load', onComplete);
|
|
319
280
|
this.addEventListener('error', onComplete);
|
|
320
281
|
this.addEventListener('abort', onComplete);
|
|
@@ -327,11 +288,8 @@ function interceptXHR() {
|
|
|
327
288
|
*/
|
|
328
289
|
function initNetworkInterceptor(callback, options) {
|
|
329
290
|
logCallback = callback;
|
|
330
|
-
|
|
331
|
-
// Convert patterns to simple strings for fast matching
|
|
332
291
|
if (options?.ignoreUrls) {
|
|
333
292
|
config.ignorePatterns = options.ignoreUrls.filter(p => typeof p === 'string');
|
|
334
|
-
// Note: RegExp patterns are not supported in optimized version for performance
|
|
335
293
|
}
|
|
336
294
|
if (options?.captureSizes !== undefined) {
|
|
337
295
|
config.captureSizes = options.captureSizes;
|
|
@@ -301,11 +301,6 @@ class Logger {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
305
|
-
// Lifecycle Logs - Industry standard minimal logging
|
|
306
|
-
// These are the only logs integrators will see in debug builds
|
|
307
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
308
|
-
|
|
309
304
|
/**
|
|
310
305
|
* Log SDK initialization success.
|
|
311
306
|
* Only shown in development builds - this is the minimal "SDK started" log.
|
|
@@ -24,7 +24,6 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
|
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
26
|
import React from 'react';
|
|
27
|
-
// Lazy-loaded React Native modules
|
|
28
27
|
let _RN = null;
|
|
29
28
|
function getRN() {
|
|
30
29
|
if (_RN) return _RN;
|
|
@@ -48,8 +47,6 @@ export const Mask = ({
|
|
|
48
47
|
...props
|
|
49
48
|
}) => {
|
|
50
49
|
const RN = getRN();
|
|
51
|
-
|
|
52
|
-
// If RN isn't loaded yet (shouldn't happen in practice), render children directly
|
|
53
50
|
if (!RN) {
|
|
54
51
|
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
|
|
55
52
|
}
|
|
@@ -58,9 +55,7 @@ export const Mask = ({
|
|
|
58
55
|
StyleSheet
|
|
59
56
|
} = RN;
|
|
60
57
|
const styles = StyleSheet.create({
|
|
61
|
-
container: {
|
|
62
|
-
// Minimal container style - doesn't affect layout
|
|
63
|
-
}
|
|
58
|
+
container: {}
|
|
64
59
|
});
|
|
65
60
|
return /*#__PURE__*/React.createElement(View, _extends({}, props, {
|
|
66
61
|
style: [styles.container, style],
|