@rejourneyco/react-native 1.0.2 → 1.0.3
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 +38 -363
- package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +11 -113
- package/android/src/main/java/com/rejourney/capture/SegmentUploader.kt +1 -15
- package/android/src/main/java/com/rejourney/capture/VideoEncoder.kt +1 -61
- package/android/src/main/java/com/rejourney/capture/ViewHierarchyScanner.kt +3 -1
- package/android/src/main/java/com/rejourney/lifecycle/SessionLifecycleService.kt +1 -22
- package/android/src/main/java/com/rejourney/network/DeviceAuthManager.kt +3 -26
- package/android/src/main/java/com/rejourney/network/NetworkMonitor.kt +0 -2
- package/android/src/main/java/com/rejourney/network/UploadManager.kt +7 -93
- package/android/src/main/java/com/rejourney/network/UploadWorker.kt +5 -41
- package/android/src/main/java/com/rejourney/privacy/PrivacyMask.kt +2 -58
- package/android/src/main/java/com/rejourney/touch/TouchInterceptor.kt +4 -4
- package/android/src/main/java/com/rejourney/utils/EventBuffer.kt +36 -7
- package/ios/Capture/RJViewHierarchyScanner.m +68 -51
- package/ios/Core/RJLifecycleManager.m +0 -14
- package/ios/Core/Rejourney.mm +24 -37
- package/ios/Network/RJDeviceAuthManager.m +0 -2
- package/ios/Network/RJUploadManager.h +8 -0
- package/ios/Network/RJUploadManager.m +45 -0
- package/ios/Privacy/RJPrivacyMask.m +5 -31
- package/ios/Rejourney.h +0 -14
- package/ios/Touch/RJTouchInterceptor.m +21 -15
- package/ios/Utils/RJEventBuffer.m +57 -69
- package/ios/Utils/RJWindowUtils.m +87 -86
- package/lib/commonjs/index.js +42 -30
- package/lib/commonjs/sdk/autoTracking.js +0 -3
- package/lib/commonjs/sdk/networkInterceptor.js +0 -11
- package/lib/commonjs/sdk/utils.js +73 -14
- package/lib/module/index.js +42 -30
- package/lib/module/sdk/autoTracking.js +0 -3
- package/lib/module/sdk/networkInterceptor.js +0 -11
- package/lib/module/sdk/utils.js +73 -14
- package/lib/typescript/sdk/utils.d.ts +31 -1
- package/package.json +16 -4
- package/src/index.ts +40 -19
- package/src/sdk/autoTracking.ts +0 -2
- package/src/sdk/constants.ts +13 -13
- package/src/sdk/networkInterceptor.ts +0 -9
- package/src/sdk/utils.ts +76 -14
|
@@ -23,110 +23,111 @@
|
|
|
23
23
|
|
|
24
24
|
@implementation RJWindowUtils
|
|
25
25
|
|
|
26
|
+
static __weak UIWindow *_cachedKeyWindow = nil;
|
|
27
|
+
static NSTimeInterval _lastCacheTime = 0;
|
|
28
|
+
static const NSTimeInterval kKeyWindowCacheTTL = 0.5; // Cache for 500ms
|
|
29
|
+
|
|
26
30
|
+ (UIWindow *)keyWindow {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
CGFloat bestKeyAppLevel = -CGFLOAT_MAX;
|
|
46
|
-
UIWindow *bestApp = nil;
|
|
47
|
-
CGFloat bestAppLevel = -CGFLOAT_MAX;
|
|
48
|
-
UIWindow *anyKey = nil;
|
|
49
|
-
|
|
50
|
-
for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
|
|
51
|
-
if (![scene isKindOfClass:[UIWindowScene class]]) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
|
55
|
-
if (windowScene.activationState != UISceneActivationStateForegroundActive) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
for (UIWindow *window in windowScene.windows) {
|
|
60
|
-
if (!window || window.isHidden || window.alpha <= 0.01) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
NSString *cls = NSStringFromClass([window class]);
|
|
65
|
-
BOOL isSystemInputWindow =
|
|
66
|
-
([cls containsString:@"Keyboard"] ||
|
|
67
|
-
[cls containsString:@"TextEffects"] ||
|
|
68
|
-
[cls containsString:@"InputWindow"] ||
|
|
69
|
-
[cls containsString:@"RemoteKeyboard"]);
|
|
70
|
-
|
|
71
|
-
BOOL hasRoot = (window.rootViewController != nil);
|
|
72
|
-
BOOL isAppCandidate = (!isSystemInputWindow && hasRoot);
|
|
73
|
-
CGFloat level = window.windowLevel;
|
|
74
|
-
|
|
75
|
-
if (window.isKeyWindow) {
|
|
76
|
-
if (!anyKey) {
|
|
77
|
-
anyKey = window;
|
|
78
|
-
}
|
|
79
|
-
if (isAppCandidate && level > bestKeyAppLevel) {
|
|
80
|
-
bestKeyAppLevel = level;
|
|
81
|
-
bestKeyApp = window;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (isAppCandidate && level > bestAppLevel) {
|
|
86
|
-
bestAppLevel = level;
|
|
87
|
-
bestApp = window;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
31
|
+
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
|
|
32
|
+
if (_cachedKeyWindow && (now - _lastCacheTime < kKeyWindowCacheTTL)) {
|
|
33
|
+
return _cachedKeyWindow;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
UIWindow *window = [self findKeyWindowInternal];
|
|
37
|
+
_cachedKeyWindow = window;
|
|
38
|
+
_lastCacheTime = now;
|
|
39
|
+
return window;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
+ (UIWindow *)findKeyWindowInternal {
|
|
43
|
+
if (@available(iOS 13.0, *)) {
|
|
44
|
+
UIWindow *bestKeyApp = nil;
|
|
45
|
+
CGFloat bestKeyAppLevel = -CGFLOAT_MAX;
|
|
46
|
+
UIWindow *bestApp = nil;
|
|
47
|
+
CGFloat bestAppLevel = -CGFLOAT_MAX;
|
|
48
|
+
UIWindow *anyKey = nil;
|
|
91
49
|
|
|
92
|
-
|
|
93
|
-
|
|
50
|
+
for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
|
|
51
|
+
if (![scene isKindOfClass:[UIWindowScene class]]) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
|
55
|
+
if (windowScene.activationState !=
|
|
56
|
+
UISceneActivationStateForegroundActive) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (UIWindow *window in windowScene.windows) {
|
|
61
|
+
if (!window || window.isHidden || window.alpha <= 0.01) {
|
|
62
|
+
continue;
|
|
94
63
|
}
|
|
95
|
-
|
|
96
|
-
|
|
64
|
+
|
|
65
|
+
NSString *cls = NSStringFromClass([window class]);
|
|
66
|
+
BOOL isSystemInputWindow = ([cls containsString:@"Keyboard"] ||
|
|
67
|
+
[cls containsString:@"TextEffects"] ||
|
|
68
|
+
[cls containsString:@"InputWindow"] ||
|
|
69
|
+
[cls containsString:@"RemoteKeyboard"]);
|
|
70
|
+
|
|
71
|
+
BOOL hasRoot = (window.rootViewController != nil);
|
|
72
|
+
BOOL isAppCandidate = (!isSystemInputWindow && hasRoot);
|
|
73
|
+
CGFloat level = window.windowLevel;
|
|
74
|
+
|
|
75
|
+
if (window.isKeyWindow) {
|
|
76
|
+
if (!anyKey) {
|
|
77
|
+
anyKey = window;
|
|
78
|
+
}
|
|
79
|
+
if (isAppCandidate && level > bestKeyAppLevel) {
|
|
80
|
+
bestKeyAppLevel = level;
|
|
81
|
+
bestKeyApp = window;
|
|
82
|
+
}
|
|
97
83
|
}
|
|
98
|
-
|
|
99
|
-
|
|
84
|
+
|
|
85
|
+
if (isAppCandidate && level > bestAppLevel) {
|
|
86
|
+
bestAppLevel = level;
|
|
87
|
+
bestApp = window;
|
|
100
88
|
}
|
|
101
|
-
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (bestKeyApp) {
|
|
93
|
+
return bestKeyApp;
|
|
94
|
+
}
|
|
95
|
+
if (bestApp) {
|
|
96
|
+
return bestApp;
|
|
97
|
+
}
|
|
98
|
+
if (anyKey) {
|
|
99
|
+
return anyKey;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
102
|
#pragma clang diagnostic push
|
|
103
103
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
104
|
-
|
|
104
|
+
return [UIApplication sharedApplication].keyWindow;
|
|
105
105
|
#pragma clang diagnostic pop
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
}
|
|
107
|
+
return nil;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
+ (NSString *)accessibilityLabelForView:(UIView *)view {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
current = current.superview;
|
|
111
|
+
UIView *current = view;
|
|
112
|
+
while (current) {
|
|
113
|
+
if (current.accessibilityLabel.length > 0) {
|
|
114
|
+
return current.accessibilityLabel;
|
|
117
115
|
}
|
|
118
|
-
|
|
116
|
+
current = current.superview;
|
|
117
|
+
}
|
|
118
|
+
return nil;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
+ (NSString *)generateSessionId {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
|
|
123
|
+
NSString *timestampStr =
|
|
124
|
+
[NSString stringWithFormat:@"%.0f", timestamp * 1000];
|
|
125
|
+
NSString *randomHex = [NSString stringWithFormat:@"%08X", arc4random()];
|
|
126
|
+
return [NSString stringWithFormat:@"session_%@_%@", timestampStr, randomHex];
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
+ (NSTimeInterval)currentTimestampMillis {
|
|
129
|
-
|
|
130
|
+
return [[NSDate date] timeIntervalSince1970] * 1000;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
@end
|
package/lib/commonjs/index.js
CHANGED
|
@@ -194,7 +194,12 @@ function getLogger() {
|
|
|
194
194
|
logRecordingStart: () => {},
|
|
195
195
|
logRecordingRemoteDisabled: () => {},
|
|
196
196
|
logInvalidProjectKey: () => {},
|
|
197
|
-
logPackageMismatch: () => {}
|
|
197
|
+
logPackageMismatch: () => {},
|
|
198
|
+
logNetworkRequest: () => {},
|
|
199
|
+
logFrustration: () => {},
|
|
200
|
+
logError: () => {},
|
|
201
|
+
logUploadStats: () => {},
|
|
202
|
+
logLifecycleEvent: () => {}
|
|
198
203
|
};
|
|
199
204
|
}
|
|
200
205
|
try {
|
|
@@ -219,7 +224,12 @@ function getLogger() {
|
|
|
219
224
|
logRecordingStart: () => {},
|
|
220
225
|
logRecordingRemoteDisabled: () => {},
|
|
221
226
|
logInvalidProjectKey: () => {},
|
|
222
|
-
logPackageMismatch: () => {}
|
|
227
|
+
logPackageMismatch: () => {},
|
|
228
|
+
logNetworkRequest: () => {},
|
|
229
|
+
logFrustration: () => {},
|
|
230
|
+
logError: () => {},
|
|
231
|
+
logUploadStats: () => {},
|
|
232
|
+
logLifecycleEvent: () => {}
|
|
223
233
|
};
|
|
224
234
|
}
|
|
225
235
|
}
|
|
@@ -276,6 +286,7 @@ function getAutoTracking() {
|
|
|
276
286
|
let _isInitialized = false;
|
|
277
287
|
let _isRecording = false;
|
|
278
288
|
let _initializationFailed = false;
|
|
289
|
+
let _metricsInterval = null;
|
|
279
290
|
let _appStateSubscription = null;
|
|
280
291
|
let _authErrorSubscription = null;
|
|
281
292
|
let _currentAppState = 'active'; // Default to active, will be updated on init
|
|
@@ -478,18 +489,12 @@ const Rejourney = {
|
|
|
478
489
|
const apiUrl = _storedConfig.apiUrl || 'https://api.rejourney.co';
|
|
479
490
|
const publicKey = _storedConfig.publicRouteKey || '';
|
|
480
491
|
getLogger().debug(`Calling native startSession (apiUrl=${apiUrl})`);
|
|
481
|
-
|
|
482
|
-
// Use user identity if set, otherwise use anonymous device ID
|
|
483
492
|
const deviceId = await getAutoTracking().ensurePersistentAnonymousId();
|
|
484
|
-
|
|
485
|
-
// Try to load persisted user identity if not already set in memory
|
|
486
493
|
if (!_userIdentity) {
|
|
487
494
|
_userIdentity = await loadPersistedUserIdentity();
|
|
488
495
|
}
|
|
489
496
|
const userId = _userIdentity || deviceId;
|
|
490
497
|
getLogger().debug(`userId=${userId.substring(0, 8)}...`);
|
|
491
|
-
|
|
492
|
-
// Start native session
|
|
493
498
|
const result = await nativeModule.startSession(userId, apiUrl, publicKey);
|
|
494
499
|
getLogger().debug('Native startSession returned:', JSON.stringify(result));
|
|
495
500
|
if (!result?.success) {
|
|
@@ -502,10 +507,27 @@ const Rejourney = {
|
|
|
502
507
|
}
|
|
503
508
|
_isRecording = true;
|
|
504
509
|
getLogger().debug(`✅ Session started: ${result.sessionId}`);
|
|
505
|
-
// Use lifecycle log for session start - only shown in dev builds
|
|
506
510
|
getLogger().logSessionStart(result.sessionId);
|
|
507
|
-
|
|
508
|
-
|
|
511
|
+
// Start polling for upload stats in dev mode
|
|
512
|
+
if (__DEV__) {
|
|
513
|
+
_metricsInterval = setInterval(async () => {
|
|
514
|
+
if (!_isRecording) {
|
|
515
|
+
if (_metricsInterval) clearInterval(_metricsInterval);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
try {
|
|
519
|
+
const native = getRejourneyNative();
|
|
520
|
+
if (native) {
|
|
521
|
+
const metrics = await native.getSDKMetrics();
|
|
522
|
+
if (metrics) {
|
|
523
|
+
getLogger().logUploadStats(metrics);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
} catch (e) {
|
|
527
|
+
getLogger().debug('Failed to fetch metrics:', e);
|
|
528
|
+
}
|
|
529
|
+
}, 10000); // Poll more frequently in dev (10s) for better feedback
|
|
530
|
+
}
|
|
509
531
|
getAutoTracking().initAutoTracking({
|
|
510
532
|
rageTapThreshold: _storedConfig?.rageTapThreshold ?? 3,
|
|
511
533
|
rageTapTimeWindow: _storedConfig?.rageTapTimeWindow ?? 500,
|
|
@@ -523,7 +545,7 @@ const Rejourney = {
|
|
|
523
545
|
x,
|
|
524
546
|
y
|
|
525
547
|
});
|
|
526
|
-
|
|
548
|
+
getLogger().logFrustration(`Rage tap (${count} taps)`);
|
|
527
549
|
},
|
|
528
550
|
// Error callback - log as error event
|
|
529
551
|
onError: error => {
|
|
@@ -532,17 +554,10 @@ const Rejourney = {
|
|
|
532
554
|
stack: error.stack,
|
|
533
555
|
name: error.name
|
|
534
556
|
});
|
|
535
|
-
|
|
557
|
+
getLogger().logError(error.message);
|
|
536
558
|
},
|
|
537
|
-
|
|
538
|
-
onScreen: (_screenName, _previousScreen) => {
|
|
539
|
-
// Native module already handles screen changes
|
|
540
|
-
// This is just for metrics tracking
|
|
541
|
-
// logger.debug(`Screen changed: ${previousScreen} -> ${screenName}`);
|
|
542
|
-
}
|
|
559
|
+
onScreen: (_screenName, _previousScreen) => {}
|
|
543
560
|
});
|
|
544
|
-
|
|
545
|
-
// Collect and log device info
|
|
546
561
|
if (_storedConfig?.collectDeviceInfo !== false) {
|
|
547
562
|
try {
|
|
548
563
|
const deviceInfo = await getAutoTracking().collectDeviceInfo();
|
|
@@ -551,13 +566,10 @@ const Rejourney = {
|
|
|
551
566
|
getLogger().warn('Failed to collect device info:', deviceError);
|
|
552
567
|
}
|
|
553
568
|
}
|
|
554
|
-
|
|
555
|
-
// Setup automatic network interception
|
|
556
569
|
if (_storedConfig?.autoTrackNetwork !== false) {
|
|
557
570
|
try {
|
|
558
571
|
const ignoreUrls = [apiUrl, '/api/ingest/presign', '/api/ingest/batch/complete', '/api/ingest/session/end', ...(_storedConfig?.networkIgnoreUrls || [])];
|
|
559
572
|
getNetworkInterceptor().initNetworkInterceptor(request => {
|
|
560
|
-
this.logNetworkRequest(request);
|
|
561
573
|
getAutoTracking().trackAPIRequest(request.success || false, request.statusCode, request.duration || 0, request.responseBodySize || 0);
|
|
562
574
|
}, {
|
|
563
575
|
ignoreUrls,
|
|
@@ -589,6 +601,10 @@ const Rejourney = {
|
|
|
589
601
|
getAutoTracking().cleanupAutoTracking();
|
|
590
602
|
getAutoTracking().resetMetrics();
|
|
591
603
|
await safeNativeCall('stopSession', () => getRejourneyNative().stopSession(), undefined);
|
|
604
|
+
if (_metricsInterval) {
|
|
605
|
+
clearInterval(_metricsInterval);
|
|
606
|
+
_metricsInterval = null;
|
|
607
|
+
}
|
|
592
608
|
_isRecording = false;
|
|
593
609
|
getLogger().logSessionEnd('current');
|
|
594
610
|
} catch (error) {
|
|
@@ -605,7 +621,6 @@ const Rejourney = {
|
|
|
605
621
|
*/
|
|
606
622
|
logEvent(name, properties) {
|
|
607
623
|
safeNativeCallSync('logEvent', () => {
|
|
608
|
-
// Fire and forget - don't await
|
|
609
624
|
getRejourneyNative().logEvent(name, properties || {}).catch(() => {});
|
|
610
625
|
}, undefined);
|
|
611
626
|
},
|
|
@@ -733,7 +748,6 @@ const Rejourney = {
|
|
|
733
748
|
* @returns Path to export file (not implemented)
|
|
734
749
|
*/
|
|
735
750
|
async exportSession(_sessionId) {
|
|
736
|
-
// Return empty string - actual export should be done from dashboard server
|
|
737
751
|
getLogger().warn('exportSession not implemented - export from dashboard server');
|
|
738
752
|
return '';
|
|
739
753
|
},
|
|
@@ -955,8 +969,6 @@ const Rejourney = {
|
|
|
955
969
|
errorMessage: request.errorMessage,
|
|
956
970
|
cached: request.cached
|
|
957
971
|
};
|
|
958
|
-
|
|
959
|
-
// Fire and forget - don't await, this is low priority
|
|
960
972
|
getRejourneyNative().logEvent('network_request', networkEvent).catch(() => {});
|
|
961
973
|
}, undefined);
|
|
962
974
|
},
|
|
@@ -1053,10 +1065,10 @@ function handleAppStateChange(nextAppState) {
|
|
|
1053
1065
|
try {
|
|
1054
1066
|
if (_currentAppState.match(/active/) && nextAppState === 'background') {
|
|
1055
1067
|
// App going to background - native module handles this automatically
|
|
1056
|
-
getLogger().
|
|
1068
|
+
getLogger().logLifecycleEvent('App moving to background');
|
|
1057
1069
|
} else if (_currentAppState.match(/inactive|background/) && nextAppState === 'active') {
|
|
1058
1070
|
// App coming back to foreground
|
|
1059
|
-
getLogger().
|
|
1071
|
+
getLogger().logLifecycleEvent('App returning to foreground');
|
|
1060
1072
|
}
|
|
1061
1073
|
_currentAppState = nextAppState;
|
|
1062
1074
|
} catch (error) {
|
|
@@ -479,9 +479,6 @@ function setupNavigationTracking() {
|
|
|
479
479
|
if (__DEV__) {
|
|
480
480
|
_utils.logger.debug('Setting up navigation tracking...');
|
|
481
481
|
}
|
|
482
|
-
|
|
483
|
-
// Delay to ensure navigation is initialized - Expo Router needs more time
|
|
484
|
-
// We retry a few times with increasing delays
|
|
485
482
|
let attempts = 0;
|
|
486
483
|
const maxAttempts = 5;
|
|
487
484
|
const trySetup = () => {
|
|
@@ -126,8 +126,6 @@ function queueRequest(request) {
|
|
|
126
126
|
function flushPendingRequests() {
|
|
127
127
|
flushTimer = null;
|
|
128
128
|
if (!logCallback || pendingCount === 0) return;
|
|
129
|
-
|
|
130
|
-
// Process all pending requests
|
|
131
129
|
while (pendingCount > 0) {
|
|
132
130
|
const request = pendingRequests[pendingHead];
|
|
133
131
|
pendingRequests[pendingHead] = null; // Allow GC
|
|
@@ -195,14 +193,9 @@ function interceptFetch() {
|
|
|
195
193
|
if (!shouldSampleRequest(path)) {
|
|
196
194
|
return originalFetch(input, init);
|
|
197
195
|
}
|
|
198
|
-
|
|
199
|
-
// Capture start time (only synchronous work)
|
|
200
196
|
const startTime = Date.now();
|
|
201
197
|
const method = (init?.method || 'GET').toUpperCase();
|
|
202
|
-
|
|
203
|
-
// Call original fetch
|
|
204
198
|
return originalFetch(input, init).then(response => {
|
|
205
|
-
// Success - queue the log asynchronously
|
|
206
199
|
queueRequest({
|
|
207
200
|
requestId: `f${startTime}`,
|
|
208
201
|
method,
|
|
@@ -253,8 +246,6 @@ function interceptXHR() {
|
|
|
253
246
|
if (!config.enabled || !logCallback || !data || shouldIgnoreUrl(data.u)) {
|
|
254
247
|
return originalXHRSend.call(this, body);
|
|
255
248
|
}
|
|
256
|
-
|
|
257
|
-
// Check sampling
|
|
258
249
|
const {
|
|
259
250
|
path
|
|
260
251
|
} = parseUrlFast(data.u);
|
|
@@ -303,8 +294,6 @@ function initNetworkInterceptor(callback, options) {
|
|
|
303
294
|
*/
|
|
304
295
|
function disableNetworkInterceptor() {
|
|
305
296
|
config.enabled = false;
|
|
306
|
-
|
|
307
|
-
// Flush any pending requests
|
|
308
297
|
if (flushTimer) {
|
|
309
298
|
clearTimeout(flushTimer);
|
|
310
299
|
flushTimer = null;
|
|
@@ -243,7 +243,6 @@ let LogLevel = exports.LogLevel = /*#__PURE__*/function (LogLevel) {
|
|
|
243
243
|
*/
|
|
244
244
|
class Logger {
|
|
245
245
|
prefix = '[Rejourney]';
|
|
246
|
-
debugMode = false;
|
|
247
246
|
|
|
248
247
|
/**
|
|
249
248
|
* Minimum log level to display.
|
|
@@ -264,7 +263,6 @@ class Logger {
|
|
|
264
263
|
this.minimumLogLevel = level;
|
|
265
264
|
}
|
|
266
265
|
setDebugMode(enabled) {
|
|
267
|
-
this.debugMode = enabled;
|
|
268
266
|
this.minimumLogLevel = enabled ? LogLevel.DEBUG : typeof __DEV__ !== 'undefined' && __DEV__ ? LogLevel.ERROR : LogLevel.SILENT;
|
|
269
267
|
}
|
|
270
268
|
|
|
@@ -285,14 +283,26 @@ class Logger {
|
|
|
285
283
|
/** Log a warning message */
|
|
286
284
|
warn(...args) {
|
|
287
285
|
if (this.minimumLogLevel <= LogLevel.WARNING) {
|
|
288
|
-
|
|
286
|
+
if (this.minimumLogLevel <= LogLevel.DEBUG) {
|
|
287
|
+
// Explicit Debug Mode: Show YellowBox
|
|
288
|
+
console.warn(this.prefix, ...args);
|
|
289
|
+
} else {
|
|
290
|
+
// Default Dev Mode: Log to console only, avoid YellowBox
|
|
291
|
+
console.log(this.prefix, '[WARN]', ...args);
|
|
292
|
+
}
|
|
289
293
|
}
|
|
290
294
|
}
|
|
291
295
|
|
|
292
296
|
/** Log an error message */
|
|
293
297
|
error(...args) {
|
|
294
298
|
if (this.minimumLogLevel <= LogLevel.ERROR) {
|
|
295
|
-
|
|
299
|
+
if (this.minimumLogLevel <= LogLevel.DEBUG) {
|
|
300
|
+
// Explicit Debug Mode: Show RedBox
|
|
301
|
+
console.error(this.prefix, ...args);
|
|
302
|
+
} else {
|
|
303
|
+
// Default Dev Mode: Log to console only, avoid RedBox
|
|
304
|
+
console.log(this.prefix, '[ERROR]', ...args);
|
|
305
|
+
}
|
|
296
306
|
}
|
|
297
307
|
}
|
|
298
308
|
notice(...args) {
|
|
@@ -306,9 +316,7 @@ class Logger {
|
|
|
306
316
|
* Only shown in development builds - this is the minimal "SDK started" log.
|
|
307
317
|
*/
|
|
308
318
|
logInitSuccess(version) {
|
|
309
|
-
|
|
310
|
-
this.info(`✓ SDK initialized (v${version})`);
|
|
311
|
-
}
|
|
319
|
+
this.notice(`✓ SDK initialized (v${version})`);
|
|
312
320
|
}
|
|
313
321
|
|
|
314
322
|
/**
|
|
@@ -324,9 +332,7 @@ class Logger {
|
|
|
324
332
|
* Only shown in development builds.
|
|
325
333
|
*/
|
|
326
334
|
logSessionStart(sessionId) {
|
|
327
|
-
|
|
328
|
-
this.info(`Session started: ${sessionId}`);
|
|
329
|
-
}
|
|
335
|
+
this.notice(`Session started: ${sessionId}`);
|
|
330
336
|
}
|
|
331
337
|
|
|
332
338
|
/**
|
|
@@ -334,12 +340,10 @@ class Logger {
|
|
|
334
340
|
* Only shown in development builds.
|
|
335
341
|
*/
|
|
336
342
|
logSessionEnd(sessionId) {
|
|
337
|
-
|
|
338
|
-
this.info(`Session ended: ${sessionId}`);
|
|
339
|
-
}
|
|
343
|
+
this.notice(`Session ended: ${sessionId}`);
|
|
340
344
|
}
|
|
341
345
|
logObservabilityStart() {
|
|
342
|
-
this.notice('Starting Rejourney observability');
|
|
346
|
+
this.notice('💧 Starting Rejourney observability');
|
|
343
347
|
}
|
|
344
348
|
logRecordingStart() {
|
|
345
349
|
this.notice('Starting recording');
|
|
@@ -353,6 +357,61 @@ class Logger {
|
|
|
353
357
|
logPackageMismatch() {
|
|
354
358
|
this.notice('Bundle ID / package name mismatch');
|
|
355
359
|
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Log network request details
|
|
363
|
+
*/
|
|
364
|
+
logNetworkRequest(request) {
|
|
365
|
+
const statusIcon = request.error || request.statusCode && request.statusCode >= 400 ? '🔴' : '🟢';
|
|
366
|
+
const method = request.method || 'GET';
|
|
367
|
+
// Shorten URL to just path if possible
|
|
368
|
+
let url = request.url || '';
|
|
369
|
+
try {
|
|
370
|
+
if (url.startsWith('http')) {
|
|
371
|
+
const urlObj = new URL(url);
|
|
372
|
+
url = urlObj.pathname;
|
|
373
|
+
}
|
|
374
|
+
} catch {
|
|
375
|
+
// Keep full URL if parsing fails
|
|
376
|
+
}
|
|
377
|
+
const duration = request.duration ? `(${Math.round(request.duration)}ms)` : '';
|
|
378
|
+
const status = request.statusCode ? `${request.statusCode}` : 'ERR';
|
|
379
|
+
this.notice(`${statusIcon} [NET] ${status} ${method} ${url} ${duration} ${request.error ? `Error: ${request.error}` : ''}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Log frustration event (rage taps, etc)
|
|
384
|
+
*/
|
|
385
|
+
logFrustration(kind) {
|
|
386
|
+
this.notice(`🤬 Frustration detected: ${kind}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Log error captured by SDK
|
|
391
|
+
*/
|
|
392
|
+
logError(message) {
|
|
393
|
+
this.notice(`X Error captured: ${message}`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Log lifecycle event (Background/Foreground)
|
|
398
|
+
* Visible in development builds.
|
|
399
|
+
*/
|
|
400
|
+
logLifecycleEvent(event) {
|
|
401
|
+
this.notice(`🔄 Lifecycle: ${event}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Log upload statistics
|
|
406
|
+
*/
|
|
407
|
+
logUploadStats(metrics) {
|
|
408
|
+
const success = metrics.uploadSuccessCount;
|
|
409
|
+
const failed = metrics.uploadFailureCount;
|
|
410
|
+
const bytes = formatBytes(metrics.totalBytesUploaded);
|
|
411
|
+
|
|
412
|
+
// Always show in dev mode for reassurance, even if 0
|
|
413
|
+
this.notice(`📡 Upload Stats: ${success} success, ${failed} failed (${bytes} uploaded)`);
|
|
414
|
+
}
|
|
356
415
|
}
|
|
357
416
|
const logger = exports.logger = new Logger();
|
|
358
417
|
//# sourceMappingURL=utils.js.map
|