@rejourneyco/react-native 1.0.1 → 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 +72 -391
- 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/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/Capture/RJViewHierarchyScanner.m +68 -51
- package/ios/Core/RJLifecycleManager.m +0 -14
- package/ios/Core/Rejourney.mm +53 -129
- 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/RJPerfTiming.m +0 -5
- package/ios/Utils/RJWindowUtils.m +87 -87
- package/lib/commonjs/components/Mask.js +1 -6
- package/lib/commonjs/index.js +46 -117
- package/lib/commonjs/sdk/autoTracking.js +39 -313
- 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 -60
- package/lib/commonjs/sdk/utils.js +73 -19
- package/lib/module/components/Mask.js +1 -6
- package/lib/module/index.js +45 -121
- package/lib/module/sdk/autoTracking.js +39 -314
- 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 -60
- package/lib/module/sdk/utils.js +73 -19
- package/lib/typescript/NativeRejourney.d.ts +1 -0
- package/lib/typescript/sdk/autoTracking.d.ts +4 -4
- package/lib/typescript/sdk/utils.d.ts +31 -1
- package/lib/typescript/types/index.d.ts +0 -1
- package/package.json +17 -11
- package/src/NativeRejourney.ts +2 -0
- package/src/components/Mask.tsx +0 -3
- package/src/index.ts +43 -92
- package/src/sdk/autoTracking.ts +51 -284
- 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 -42
- package/src/sdk/utils.ts +76 -19
- package/src/types/index.ts +0 -29
|
@@ -23,111 +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
|
-
// Keep as a last-ditch fallback (even if it's a system window).
|
|
77
|
-
if (!anyKey) {
|
|
78
|
-
anyKey = window;
|
|
79
|
-
}
|
|
80
|
-
if (isAppCandidate && level > bestKeyAppLevel) {
|
|
81
|
-
bestKeyAppLevel = level;
|
|
82
|
-
bestKeyApp = window;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (isAppCandidate && level > bestAppLevel) {
|
|
87
|
-
bestAppLevel = level;
|
|
88
|
-
bestApp = window;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
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;
|
|
92
49
|
|
|
93
|
-
|
|
94
|
-
|
|
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;
|
|
95
63
|
}
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
}
|
|
98
83
|
}
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
|
|
85
|
+
if (isAppCandidate && level > bestAppLevel) {
|
|
86
|
+
bestAppLevel = level;
|
|
87
|
+
bestApp = window;
|
|
101
88
|
}
|
|
102
|
-
|
|
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 {
|
|
103
102
|
#pragma clang diagnostic push
|
|
104
103
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
105
|
-
|
|
104
|
+
return [UIApplication sharedApplication].keyWindow;
|
|
106
105
|
#pragma clang diagnostic pop
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
}
|
|
107
|
+
return nil;
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
+ (NSString *)accessibilityLabelForView:(UIView *)view {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
current = current.superview;
|
|
111
|
+
UIView *current = view;
|
|
112
|
+
while (current) {
|
|
113
|
+
if (current.accessibilityLabel.length > 0) {
|
|
114
|
+
return current.accessibilityLabel;
|
|
118
115
|
}
|
|
119
|
-
|
|
116
|
+
current = current.superview;
|
|
117
|
+
}
|
|
118
|
+
return nil;
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
+ (NSString *)generateSessionId {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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];
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
+ (NSTimeInterval)currentTimestampMillis {
|
|
130
|
-
|
|
130
|
+
return [[NSDate date] timeIntervalSince1970] * 1000;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
@end
|
|
@@ -30,7 +30,6 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
|
|
|
30
30
|
* </Mask>
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
|
-
// Lazy-loaded React Native modules
|
|
34
33
|
let _RN = null;
|
|
35
34
|
function getRN() {
|
|
36
35
|
if (_RN) return _RN;
|
|
@@ -54,8 +53,6 @@ const Mask = ({
|
|
|
54
53
|
...props
|
|
55
54
|
}) => {
|
|
56
55
|
const RN = getRN();
|
|
57
|
-
|
|
58
|
-
// If RN isn't loaded yet (shouldn't happen in practice), render children directly
|
|
59
56
|
if (!RN) {
|
|
60
57
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children);
|
|
61
58
|
}
|
|
@@ -64,9 +61,7 @@ const Mask = ({
|
|
|
64
61
|
StyleSheet
|
|
65
62
|
} = RN;
|
|
66
63
|
const styles = StyleSheet.create({
|
|
67
|
-
container: {
|
|
68
|
-
// Minimal container style - doesn't affect layout
|
|
69
|
-
}
|
|
64
|
+
container: {}
|
|
70
65
|
});
|
|
71
66
|
return /*#__PURE__*/_react.default.createElement(View, _extends({}, props, {
|
|
72
67
|
style: [styles.container, style],
|
package/lib/commonjs/index.js
CHANGED
|
@@ -175,13 +175,10 @@ function getReactNative() {
|
|
|
175
175
|
return null;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
// Lazy-loaded logger
|
|
180
178
|
let _logger = null;
|
|
181
179
|
function getLogger() {
|
|
182
180
|
if (_logger) return _logger;
|
|
183
181
|
if (_sdkDisabled) {
|
|
184
|
-
// Return a no-op logger if SDK is disabled
|
|
185
182
|
return {
|
|
186
183
|
debug: () => {},
|
|
187
184
|
info: console.log.bind(console, '[Rejourney]'),
|
|
@@ -197,7 +194,12 @@ function getLogger() {
|
|
|
197
194
|
logRecordingStart: () => {},
|
|
198
195
|
logRecordingRemoteDisabled: () => {},
|
|
199
196
|
logInvalidProjectKey: () => {},
|
|
200
|
-
logPackageMismatch: () => {}
|
|
197
|
+
logPackageMismatch: () => {},
|
|
198
|
+
logNetworkRequest: () => {},
|
|
199
|
+
logFrustration: () => {},
|
|
200
|
+
logError: () => {},
|
|
201
|
+
logUploadStats: () => {},
|
|
202
|
+
logLifecycleEvent: () => {}
|
|
201
203
|
};
|
|
202
204
|
}
|
|
203
205
|
try {
|
|
@@ -222,7 +224,12 @@ function getLogger() {
|
|
|
222
224
|
logRecordingStart: () => {},
|
|
223
225
|
logRecordingRemoteDisabled: () => {},
|
|
224
226
|
logInvalidProjectKey: () => {},
|
|
225
|
-
logPackageMismatch: () => {}
|
|
227
|
+
logPackageMismatch: () => {},
|
|
228
|
+
logNetworkRequest: () => {},
|
|
229
|
+
logFrustration: () => {},
|
|
230
|
+
logError: () => {},
|
|
231
|
+
logUploadStats: () => {},
|
|
232
|
+
logLifecycleEvent: () => {}
|
|
226
233
|
};
|
|
227
234
|
}
|
|
228
235
|
}
|
|
@@ -260,7 +267,7 @@ const noopAutoTracking = {
|
|
|
260
267
|
notifyStateChange: () => {},
|
|
261
268
|
getSessionMetrics: () => ({}),
|
|
262
269
|
resetMetrics: () => {},
|
|
263
|
-
collectDeviceInfo: () => ({}),
|
|
270
|
+
collectDeviceInfo: async () => ({}),
|
|
264
271
|
ensurePersistentAnonymousId: async () => 'anonymous'
|
|
265
272
|
};
|
|
266
273
|
function getAutoTracking() {
|
|
@@ -279,6 +286,7 @@ function getAutoTracking() {
|
|
|
279
286
|
let _isInitialized = false;
|
|
280
287
|
let _isRecording = false;
|
|
281
288
|
let _initializationFailed = false;
|
|
289
|
+
let _metricsInterval = null;
|
|
282
290
|
let _appStateSubscription = null;
|
|
283
291
|
let _authErrorSubscription = null;
|
|
284
292
|
let _currentAppState = 'active'; // Default to active, will be updated on init
|
|
@@ -301,7 +309,7 @@ async function loadPersistedUserIdentity() {
|
|
|
301
309
|
|
|
302
310
|
// NATIVE STORAGE: Read directly from SharedPreferences/NSUserDefaults
|
|
303
311
|
return await nativeModule.getUserIdentity();
|
|
304
|
-
} catch
|
|
312
|
+
} catch {
|
|
305
313
|
return null;
|
|
306
314
|
}
|
|
307
315
|
}
|
|
@@ -321,9 +329,7 @@ let _runtimeReady = false;
|
|
|
321
329
|
function isRuntimeReady() {
|
|
322
330
|
if (_runtimeReady) return true;
|
|
323
331
|
try {
|
|
324
|
-
// Try to access a core module to verify runtime is ready
|
|
325
332
|
const RN = require('react-native');
|
|
326
|
-
// If we can access NativeModules without error, runtime is ready
|
|
327
333
|
if (RN.NativeModules) {
|
|
328
334
|
_runtimeReady = true;
|
|
329
335
|
return true;
|
|
@@ -406,12 +412,9 @@ function getRejourneyNative() {
|
|
|
406
412
|
}
|
|
407
413
|
}
|
|
408
414
|
} catch (error) {
|
|
409
|
-
// If any access fails, log and return null
|
|
410
415
|
getLogger().warn('Rejourney: Failed to access native modules:', error);
|
|
411
416
|
_rejourneyNative = null;
|
|
412
417
|
}
|
|
413
|
-
|
|
414
|
-
// Ensure we never return undefined - convert to null
|
|
415
418
|
if (_rejourneyNative === undefined) {
|
|
416
419
|
_rejourneyNative = null;
|
|
417
420
|
}
|
|
@@ -486,18 +489,12 @@ const Rejourney = {
|
|
|
486
489
|
const apiUrl = _storedConfig.apiUrl || 'https://api.rejourney.co';
|
|
487
490
|
const publicKey = _storedConfig.publicRouteKey || '';
|
|
488
491
|
getLogger().debug(`Calling native startSession (apiUrl=${apiUrl})`);
|
|
489
|
-
|
|
490
|
-
// Use user identity if set, otherwise use anonymous device ID
|
|
491
492
|
const deviceId = await getAutoTracking().ensurePersistentAnonymousId();
|
|
492
|
-
|
|
493
|
-
// Try to load persisted user identity if not already set in memory
|
|
494
493
|
if (!_userIdentity) {
|
|
495
494
|
_userIdentity = await loadPersistedUserIdentity();
|
|
496
495
|
}
|
|
497
496
|
const userId = _userIdentity || deviceId;
|
|
498
497
|
getLogger().debug(`userId=${userId.substring(0, 8)}...`);
|
|
499
|
-
|
|
500
|
-
// Start native session
|
|
501
498
|
const result = await nativeModule.startSession(userId, apiUrl, publicKey);
|
|
502
499
|
getLogger().debug('Native startSession returned:', JSON.stringify(result));
|
|
503
500
|
if (!result?.success) {
|
|
@@ -510,10 +507,27 @@ const Rejourney = {
|
|
|
510
507
|
}
|
|
511
508
|
_isRecording = true;
|
|
512
509
|
getLogger().debug(`✅ Session started: ${result.sessionId}`);
|
|
513
|
-
// Use lifecycle log for session start - only shown in dev builds
|
|
514
510
|
getLogger().logSessionStart(result.sessionId);
|
|
515
|
-
|
|
516
|
-
|
|
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
|
+
}
|
|
517
531
|
getAutoTracking().initAutoTracking({
|
|
518
532
|
rageTapThreshold: _storedConfig?.rageTapThreshold ?? 3,
|
|
519
533
|
rageTapTimeWindow: _storedConfig?.rageTapTimeWindow ?? 500,
|
|
@@ -531,7 +545,7 @@ const Rejourney = {
|
|
|
531
545
|
x,
|
|
532
546
|
y
|
|
533
547
|
});
|
|
534
|
-
|
|
548
|
+
getLogger().logFrustration(`Rage tap (${count} taps)`);
|
|
535
549
|
},
|
|
536
550
|
// Error callback - log as error event
|
|
537
551
|
onError: error => {
|
|
@@ -540,46 +554,31 @@ const Rejourney = {
|
|
|
540
554
|
stack: error.stack,
|
|
541
555
|
name: error.name
|
|
542
556
|
});
|
|
543
|
-
|
|
557
|
+
getLogger().logError(error.message);
|
|
544
558
|
},
|
|
545
|
-
|
|
546
|
-
onScreen: (_screenName, _previousScreen) => {
|
|
547
|
-
// Native module already handles screen changes
|
|
548
|
-
// This is just for metrics tracking
|
|
549
|
-
// logger.debug(`Screen changed: ${previousScreen} -> ${screenName}`);
|
|
550
|
-
}
|
|
559
|
+
onScreen: (_screenName, _previousScreen) => {}
|
|
551
560
|
});
|
|
552
|
-
|
|
553
|
-
// Collect and log device info
|
|
554
561
|
if (_storedConfig?.collectDeviceInfo !== false) {
|
|
555
562
|
try {
|
|
556
|
-
const deviceInfo = getAutoTracking().collectDeviceInfo();
|
|
563
|
+
const deviceInfo = await getAutoTracking().collectDeviceInfo();
|
|
557
564
|
this.logEvent('device_info', deviceInfo);
|
|
558
565
|
} catch (deviceError) {
|
|
559
566
|
getLogger().warn('Failed to collect device info:', deviceError);
|
|
560
567
|
}
|
|
561
568
|
}
|
|
562
|
-
|
|
563
|
-
// Setup automatic network interception
|
|
564
569
|
if (_storedConfig?.autoTrackNetwork !== false) {
|
|
565
570
|
try {
|
|
566
571
|
const ignoreUrls = [apiUrl, '/api/ingest/presign', '/api/ingest/batch/complete', '/api/ingest/session/end', ...(_storedConfig?.networkIgnoreUrls || [])];
|
|
567
572
|
getNetworkInterceptor().initNetworkInterceptor(request => {
|
|
568
|
-
this.logNetworkRequest(request);
|
|
569
573
|
getAutoTracking().trackAPIRequest(request.success || false, request.statusCode, request.duration || 0, request.responseBodySize || 0);
|
|
570
574
|
}, {
|
|
571
575
|
ignoreUrls,
|
|
572
576
|
captureSizes: _storedConfig?.networkCaptureSizes !== false
|
|
573
577
|
});
|
|
574
|
-
|
|
575
|
-
// logger.debug('Network interception enabled');
|
|
576
578
|
} catch (networkError) {
|
|
577
579
|
getLogger().warn('Failed to setup network interception:', networkError);
|
|
578
580
|
}
|
|
579
581
|
}
|
|
580
|
-
|
|
581
|
-
// logger.debug('Auto tracking enabled');
|
|
582
|
-
|
|
583
582
|
return true;
|
|
584
583
|
} catch (error) {
|
|
585
584
|
getLogger().error('Failed to start recording:', error);
|
|
@@ -596,17 +595,17 @@ const Rejourney = {
|
|
|
596
595
|
return;
|
|
597
596
|
}
|
|
598
597
|
try {
|
|
599
|
-
// Get session metrics before stopping
|
|
600
598
|
const metrics = getAutoTracking().getSessionMetrics();
|
|
601
599
|
this.logEvent('session_metrics', metrics);
|
|
602
|
-
|
|
603
|
-
// Cleanup
|
|
604
600
|
getNetworkInterceptor().disableNetworkInterceptor();
|
|
605
601
|
getAutoTracking().cleanupAutoTracking();
|
|
606
602
|
getAutoTracking().resetMetrics();
|
|
607
603
|
await safeNativeCall('stopSession', () => getRejourneyNative().stopSession(), undefined);
|
|
604
|
+
if (_metricsInterval) {
|
|
605
|
+
clearInterval(_metricsInterval);
|
|
606
|
+
_metricsInterval = null;
|
|
607
|
+
}
|
|
608
608
|
_isRecording = false;
|
|
609
|
-
// Use lifecycle log for session end - only shown in dev builds
|
|
610
609
|
getLogger().logSessionEnd('current');
|
|
611
610
|
} catch (error) {
|
|
612
611
|
getLogger().error('Failed to stop recording:', error);
|
|
@@ -622,7 +621,6 @@ const Rejourney = {
|
|
|
622
621
|
*/
|
|
623
622
|
logEvent(name, properties) {
|
|
624
623
|
safeNativeCallSync('logEvent', () => {
|
|
625
|
-
// Fire and forget - don't await
|
|
626
624
|
getRejourneyNative().logEvent(name, properties || {}).catch(() => {});
|
|
627
625
|
}, undefined);
|
|
628
626
|
},
|
|
@@ -638,9 +636,6 @@ const Rejourney = {
|
|
|
638
636
|
setUserIdentity(userId) {
|
|
639
637
|
_userIdentity = userId;
|
|
640
638
|
persistUserIdentity(userId).catch(() => {});
|
|
641
|
-
// logger.debug(`User identity set: ${userId}`);
|
|
642
|
-
|
|
643
|
-
// If recording is active, update the native module immediately
|
|
644
639
|
if (_isRecording && getRejourneyNative()) {
|
|
645
640
|
safeNativeCallSync('setUserIdentity', () => {
|
|
646
641
|
getRejourneyNative().setUserIdentity(userId).catch(() => {});
|
|
@@ -654,9 +649,6 @@ const Rejourney = {
|
|
|
654
649
|
clearUserIdentity() {
|
|
655
650
|
_userIdentity = null;
|
|
656
651
|
persistUserIdentity(null).catch(() => {});
|
|
657
|
-
// logger.debug('User identity cleared');
|
|
658
|
-
|
|
659
|
-
// If recording is active, update the native module immediately
|
|
660
652
|
if (_isRecording && getRejourneyNative()) {
|
|
661
653
|
safeNativeCallSync('setUserIdentity', () => {
|
|
662
654
|
getRejourneyNative().setUserIdentity('anonymous').catch(() => {});
|
|
@@ -670,10 +662,7 @@ const Rejourney = {
|
|
|
670
662
|
* @param params - Optional screen parameters
|
|
671
663
|
*/
|
|
672
664
|
tagScreen(screenName, _params) {
|
|
673
|
-
// Track screen for metrics and funnel tracking
|
|
674
665
|
getAutoTracking().trackScreen(screenName);
|
|
675
|
-
|
|
676
|
-
// Notify state change (kept for API compatibility)
|
|
677
666
|
getAutoTracking().notifyStateChange();
|
|
678
667
|
safeNativeCallSync('tagScreen', () => {
|
|
679
668
|
getRejourneyNative().screenChanged(screenName).catch(() => {});
|
|
@@ -759,7 +748,6 @@ const Rejourney = {
|
|
|
759
748
|
* @returns Path to export file (not implemented)
|
|
760
749
|
*/
|
|
761
750
|
async exportSession(_sessionId) {
|
|
762
|
-
// Return empty string - actual export should be done from dashboard server
|
|
763
751
|
getLogger().warn('exportSession not implemented - export from dashboard server');
|
|
764
752
|
return '';
|
|
765
753
|
},
|
|
@@ -851,10 +839,6 @@ const Rejourney = {
|
|
|
851
839
|
getAutoTracking().trackScroll();
|
|
852
840
|
await safeNativeCall('onScroll', () => getRejourneyNative().onScroll(scrollOffset), undefined);
|
|
853
841
|
},
|
|
854
|
-
// ========================================================================
|
|
855
|
-
// OAuth / External URL Tracking
|
|
856
|
-
// ========================================================================
|
|
857
|
-
|
|
858
842
|
/**
|
|
859
843
|
* Notify the SDK that an OAuth flow is starting
|
|
860
844
|
*
|
|
@@ -985,15 +969,9 @@ const Rejourney = {
|
|
|
985
969
|
errorMessage: request.errorMessage,
|
|
986
970
|
cached: request.cached
|
|
987
971
|
};
|
|
988
|
-
|
|
989
|
-
// Fire and forget - don't await, this is low priority
|
|
990
972
|
getRejourneyNative().logEvent('network_request', networkEvent).catch(() => {});
|
|
991
973
|
}, undefined);
|
|
992
974
|
},
|
|
993
|
-
// ========================================================================
|
|
994
|
-
// SDK Telemetry / Observability
|
|
995
|
-
// ========================================================================
|
|
996
|
-
|
|
997
975
|
/**
|
|
998
976
|
* Get SDK telemetry metrics for observability
|
|
999
977
|
*
|
|
@@ -1041,10 +1019,6 @@ const Rejourney = {
|
|
|
1041
1019
|
getLogger().warn('debugTriggerANR is only available in development mode');
|
|
1042
1020
|
}
|
|
1043
1021
|
},
|
|
1044
|
-
// ========================================================================
|
|
1045
|
-
// Privacy / View Masking
|
|
1046
|
-
// ========================================================================
|
|
1047
|
-
|
|
1048
1022
|
/**
|
|
1049
1023
|
* Mask a view by its nativeID prop (will be occluded in recordings)
|
|
1050
1024
|
*
|
|
@@ -1080,10 +1054,6 @@ const Rejourney = {
|
|
|
1080
1054
|
}
|
|
1081
1055
|
};
|
|
1082
1056
|
|
|
1083
|
-
// =============================================================================
|
|
1084
|
-
// Automatic Lifecycle Management
|
|
1085
|
-
// =============================================================================
|
|
1086
|
-
|
|
1087
1057
|
/**
|
|
1088
1058
|
* Handle app state changes for automatic session management
|
|
1089
1059
|
* - Pauses recording when app goes to background
|
|
@@ -1095,10 +1065,10 @@ function handleAppStateChange(nextAppState) {
|
|
|
1095
1065
|
try {
|
|
1096
1066
|
if (_currentAppState.match(/active/) && nextAppState === 'background') {
|
|
1097
1067
|
// App going to background - native module handles this automatically
|
|
1098
|
-
getLogger().
|
|
1068
|
+
getLogger().logLifecycleEvent('App moving to background');
|
|
1099
1069
|
} else if (_currentAppState.match(/inactive|background/) && nextAppState === 'active') {
|
|
1100
1070
|
// App coming back to foreground
|
|
1101
|
-
getLogger().
|
|
1071
|
+
getLogger().logLifecycleEvent('App returning to foreground');
|
|
1102
1072
|
}
|
|
1103
1073
|
_currentAppState = nextAppState;
|
|
1104
1074
|
} catch (error) {
|
|
@@ -1114,20 +1084,13 @@ function setupLifecycleManagement() {
|
|
|
1114
1084
|
if (_sdkDisabled) return;
|
|
1115
1085
|
const RN = getReactNative();
|
|
1116
1086
|
if (!RN) return;
|
|
1117
|
-
|
|
1118
|
-
// Remove any existing subscription
|
|
1119
1087
|
if (_appStateSubscription) {
|
|
1120
1088
|
_appStateSubscription.remove();
|
|
1121
1089
|
_appStateSubscription = null;
|
|
1122
1090
|
}
|
|
1123
1091
|
try {
|
|
1124
|
-
// Get current app state
|
|
1125
1092
|
_currentAppState = RN.AppState.currentState || 'active';
|
|
1126
|
-
|
|
1127
|
-
// Subscribe to app state changes
|
|
1128
1093
|
_appStateSubscription = RN.AppState.addEventListener('change', handleAppStateChange);
|
|
1129
|
-
|
|
1130
|
-
// Setup auth error listener from native module
|
|
1131
1094
|
setupAuthErrorListener();
|
|
1132
1095
|
getLogger().debug('Lifecycle management enabled');
|
|
1133
1096
|
} catch (error) {
|
|
@@ -1150,9 +1113,6 @@ function setupAuthErrorListener() {
|
|
|
1150
1113
|
try {
|
|
1151
1114
|
const nativeModule = getRejourneyNative();
|
|
1152
1115
|
if (nativeModule) {
|
|
1153
|
-
// RN warns if a non-null module is passed without addListener/removeListeners.
|
|
1154
|
-
// Our native module may not implement these no-op methods yet, so only pass
|
|
1155
|
-
// the module when those hooks exist; otherwise use the global emitter.
|
|
1156
1116
|
const maybeAny = nativeModule;
|
|
1157
1117
|
const hasEventEmitterHooks = typeof maybeAny?.addListener === 'function' && typeof maybeAny?.removeListeners === 'function';
|
|
1158
1118
|
const eventEmitter = hasEventEmitterHooks ? new RN.NativeEventEmitter(maybeAny) : new RN.NativeEventEmitter();
|
|
@@ -1163,11 +1123,7 @@ function setupAuthErrorListener() {
|
|
|
1163
1123
|
} else if (error?.code === 404) {
|
|
1164
1124
|
getLogger().logInvalidProjectKey();
|
|
1165
1125
|
}
|
|
1166
|
-
|
|
1167
|
-
// Update SDK state - recording has been stopped by native
|
|
1168
1126
|
_isRecording = false;
|
|
1169
|
-
|
|
1170
|
-
// Call user's error handler if provided
|
|
1171
1127
|
if (_storedConfig?.onAuthError) {
|
|
1172
1128
|
try {
|
|
1173
1129
|
_storedConfig.onAuthError(error);
|
|
@@ -1178,7 +1134,6 @@ function setupAuthErrorListener() {
|
|
|
1178
1134
|
});
|
|
1179
1135
|
}
|
|
1180
1136
|
} catch (error) {
|
|
1181
|
-
// Event emitter not available on this platform - that's OK
|
|
1182
1137
|
getLogger().debug('Auth error listener not available:', error);
|
|
1183
1138
|
}
|
|
1184
1139
|
}
|
|
@@ -1197,10 +1152,6 @@ function cleanupLifecycleManagement() {
|
|
|
1197
1152
|
}
|
|
1198
1153
|
}
|
|
1199
1154
|
|
|
1200
|
-
// =============================================================================
|
|
1201
|
-
// Simple Initialization API
|
|
1202
|
-
// =============================================================================
|
|
1203
|
-
|
|
1204
1155
|
/**
|
|
1205
1156
|
* Initialize Rejourney SDK - STEP 1 of 3
|
|
1206
1157
|
*
|
|
@@ -1230,14 +1181,11 @@ function cleanupLifecycleManagement() {
|
|
|
1230
1181
|
* ```
|
|
1231
1182
|
*/
|
|
1232
1183
|
function initRejourney(publicRouteKey, options) {
|
|
1233
|
-
// Validate public route key
|
|
1234
1184
|
if (!publicRouteKey || typeof publicRouteKey !== 'string') {
|
|
1235
1185
|
getLogger().warn('Rejourney: Invalid public route key provided. SDK will be disabled.');
|
|
1236
1186
|
_initializationFailed = true;
|
|
1237
1187
|
return;
|
|
1238
1188
|
}
|
|
1239
|
-
|
|
1240
|
-
// Store config for later use
|
|
1241
1189
|
_storedConfig = {
|
|
1242
1190
|
...options,
|
|
1243
1191
|
publicRouteKey
|
|
@@ -1291,8 +1239,6 @@ function startRejourney() {
|
|
|
1291
1239
|
}
|
|
1292
1240
|
getLogger().logRecordingStart();
|
|
1293
1241
|
getLogger().debug('Starting session...');
|
|
1294
|
-
|
|
1295
|
-
// Fire and forget - don't block the caller
|
|
1296
1242
|
(async () => {
|
|
1297
1243
|
try {
|
|
1298
1244
|
const started = await Rejourney._startSession();
|
|
@@ -1323,12 +1269,7 @@ function stopRejourney() {
|
|
|
1323
1269
|
getLogger().warn('Error stopping Rejourney:', error);
|
|
1324
1270
|
}
|
|
1325
1271
|
}
|
|
1326
|
-
var _default = exports.default = Rejourney;
|
|
1327
|
-
// Export auto tracking utilities for advanced usage
|
|
1328
|
-
// These are used internally but can be called manually if needed
|
|
1329
|
-
// Navigation
|
|
1330
|
-
// Re-export LogLevel enum from utils
|
|
1331
|
-
// Note: This is safe because the enum itself doesn't trigger react-native imports
|
|
1272
|
+
var _default = exports.default = Rejourney;
|
|
1332
1273
|
/**
|
|
1333
1274
|
* Configure SDK log verbosity.
|
|
1334
1275
|
*
|
|
@@ -1360,16 +1301,4 @@ var _default = exports.default = Rejourney; // Export types
|
|
|
1360
1301
|
function setLogLevel(level) {
|
|
1361
1302
|
getLogger().setLogLevel(level);
|
|
1362
1303
|
}
|
|
1363
|
-
|
|
1364
|
-
// Note: Components and hooks removed in new engine
|
|
1365
|
-
// Session replay now handled by dashboard web UI
|
|
1366
|
-
// export { RejourneyReplay } from './components/replay/RejourneyReplay';
|
|
1367
|
-
// export { GestureTracker } from './components/GestureTracker';
|
|
1368
|
-
// export { SessionList } from './components/SessionList';
|
|
1369
|
-
// export { useRejourney } from './hooks/useRejourney';
|
|
1370
|
-
// export { useReplay } from './hooks/useReplay';
|
|
1371
|
-
|
|
1372
|
-
// Note: SDK managers removed in new engine - all functionality handled by native module
|
|
1373
|
-
|
|
1374
|
-
// Export Mask component
|
|
1375
1304
|
//# sourceMappingURL=index.js.map
|