@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.
Files changed (65) hide show
  1. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +72 -391
  2. package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +11 -113
  3. package/android/src/main/java/com/rejourney/capture/SegmentUploader.kt +1 -15
  4. package/android/src/main/java/com/rejourney/capture/VideoEncoder.kt +1 -61
  5. package/android/src/main/java/com/rejourney/capture/ViewHierarchyScanner.kt +3 -1
  6. package/android/src/main/java/com/rejourney/lifecycle/SessionLifecycleService.kt +1 -22
  7. package/android/src/main/java/com/rejourney/network/DeviceAuthManager.kt +3 -26
  8. package/android/src/main/java/com/rejourney/network/NetworkMonitor.kt +0 -2
  9. package/android/src/main/java/com/rejourney/network/UploadManager.kt +7 -93
  10. package/android/src/main/java/com/rejourney/network/UploadWorker.kt +5 -41
  11. package/android/src/main/java/com/rejourney/privacy/PrivacyMask.kt +2 -58
  12. package/android/src/main/java/com/rejourney/touch/TouchInterceptor.kt +4 -4
  13. package/android/src/main/java/com/rejourney/utils/EventBuffer.kt +36 -7
  14. package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +7 -0
  15. package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +9 -0
  16. package/ios/Capture/RJCaptureEngine.m +3 -34
  17. package/ios/Capture/RJVideoEncoder.m +0 -26
  18. package/ios/Capture/RJViewHierarchyScanner.m +68 -51
  19. package/ios/Core/RJLifecycleManager.m +0 -14
  20. package/ios/Core/Rejourney.mm +53 -129
  21. package/ios/Network/RJDeviceAuthManager.m +0 -2
  22. package/ios/Network/RJUploadManager.h +8 -0
  23. package/ios/Network/RJUploadManager.m +45 -0
  24. package/ios/Privacy/RJPrivacyMask.m +5 -31
  25. package/ios/Rejourney.h +0 -14
  26. package/ios/Touch/RJTouchInterceptor.m +21 -15
  27. package/ios/Utils/RJEventBuffer.m +57 -69
  28. package/ios/Utils/RJPerfTiming.m +0 -5
  29. package/ios/Utils/RJWindowUtils.m +87 -87
  30. package/lib/commonjs/components/Mask.js +1 -6
  31. package/lib/commonjs/index.js +46 -117
  32. package/lib/commonjs/sdk/autoTracking.js +39 -313
  33. package/lib/commonjs/sdk/constants.js +2 -13
  34. package/lib/commonjs/sdk/errorTracking.js +1 -29
  35. package/lib/commonjs/sdk/metricsTracking.js +3 -24
  36. package/lib/commonjs/sdk/navigation.js +3 -42
  37. package/lib/commonjs/sdk/networkInterceptor.js +7 -60
  38. package/lib/commonjs/sdk/utils.js +73 -19
  39. package/lib/module/components/Mask.js +1 -6
  40. package/lib/module/index.js +45 -121
  41. package/lib/module/sdk/autoTracking.js +39 -314
  42. package/lib/module/sdk/constants.js +2 -13
  43. package/lib/module/sdk/errorTracking.js +1 -29
  44. package/lib/module/sdk/index.js +0 -2
  45. package/lib/module/sdk/metricsTracking.js +3 -24
  46. package/lib/module/sdk/navigation.js +3 -42
  47. package/lib/module/sdk/networkInterceptor.js +7 -60
  48. package/lib/module/sdk/utils.js +73 -19
  49. package/lib/typescript/NativeRejourney.d.ts +1 -0
  50. package/lib/typescript/sdk/autoTracking.d.ts +4 -4
  51. package/lib/typescript/sdk/utils.d.ts +31 -1
  52. package/lib/typescript/types/index.d.ts +0 -1
  53. package/package.json +17 -11
  54. package/src/NativeRejourney.ts +2 -0
  55. package/src/components/Mask.tsx +0 -3
  56. package/src/index.ts +43 -92
  57. package/src/sdk/autoTracking.ts +51 -284
  58. package/src/sdk/constants.ts +13 -13
  59. package/src/sdk/errorTracking.ts +1 -17
  60. package/src/sdk/index.ts +0 -2
  61. package/src/sdk/metricsTracking.ts +5 -33
  62. package/src/sdk/navigation.ts +8 -29
  63. package/src/sdk/networkInterceptor.ts +9 -42
  64. package/src/sdk/utils.ts +76 -19
  65. package/src/types/index.ts +0 -29
package/src/index.ts CHANGED
@@ -79,13 +79,11 @@ function getReactNative(): typeof import('react-native') | null {
79
79
  }
80
80
  }
81
81
 
82
- // Lazy-loaded logger
83
82
  let _logger: typeof import('./sdk/utils').logger | null = null;
84
83
 
85
84
  function getLogger() {
86
85
  if (_logger) return _logger;
87
86
  if (_sdkDisabled) {
88
- // Return a no-op logger if SDK is disabled
89
87
  return {
90
88
  debug: () => { },
91
89
  info: console.log.bind(console, '[Rejourney]'),
@@ -102,6 +100,11 @@ function getLogger() {
102
100
  logRecordingRemoteDisabled: () => { },
103
101
  logInvalidProjectKey: () => { },
104
102
  logPackageMismatch: () => { },
103
+ logNetworkRequest: () => { },
104
+ logFrustration: () => { },
105
+ logError: () => { },
106
+ logUploadStats: () => { },
107
+ logLifecycleEvent: () => { },
105
108
  };
106
109
  }
107
110
 
@@ -128,6 +131,11 @@ function getLogger() {
128
131
  logRecordingRemoteDisabled: () => { },
129
132
  logInvalidProjectKey: () => { },
130
133
  logPackageMismatch: () => { },
134
+ logNetworkRequest: () => { },
135
+ logFrustration: () => { },
136
+ logError: () => { },
137
+ logUploadStats: () => { },
138
+ logLifecycleEvent: () => { },
131
139
  };
132
140
  }
133
141
  }
@@ -175,7 +183,7 @@ const noopAutoTracking = {
175
183
  notifyStateChange: () => { },
176
184
  getSessionMetrics: () => ({}),
177
185
  resetMetrics: () => { },
178
- collectDeviceInfo: () => ({}),
186
+ collectDeviceInfo: async () => ({} as any),
179
187
  ensurePersistentAnonymousId: async () => 'anonymous',
180
188
  };
181
189
 
@@ -196,6 +204,7 @@ function getAutoTracking() {
196
204
  let _isInitialized = false;
197
205
  let _isRecording = false;
198
206
  let _initializationFailed = false;
207
+ let _metricsInterval: ReturnType<typeof setInterval> | null = null;
199
208
  let _appStateSubscription: { remove: () => void } | null = null;
200
209
  let _authErrorSubscription: { remove: () => void } | null = null;
201
210
  let _currentAppState: string = 'active'; // Default to active, will be updated on init
@@ -219,7 +228,7 @@ async function loadPersistedUserIdentity(): Promise<string | null> {
219
228
 
220
229
  // NATIVE STORAGE: Read directly from SharedPreferences/NSUserDefaults
221
230
  return await nativeModule.getUserIdentity();
222
- } catch (e) {
231
+ } catch {
223
232
  return null;
224
233
  }
225
234
  }
@@ -241,9 +250,7 @@ function isRuntimeReady(): boolean {
241
250
  if (_runtimeReady) return true;
242
251
 
243
252
  try {
244
- // Try to access a core module to verify runtime is ready
245
253
  const RN = require('react-native');
246
- // If we can access NativeModules without error, runtime is ready
247
254
  if (RN.NativeModules) {
248
255
  _runtimeReady = true;
249
256
  return true;
@@ -325,12 +332,10 @@ function getRejourneyNative(): Spec | null {
325
332
  }
326
333
  }
327
334
  } catch (error) {
328
- // If any access fails, log and return null
329
335
  getLogger().warn('Rejourney: Failed to access native modules:', error);
330
336
  _rejourneyNative = null;
331
337
  }
332
338
 
333
- // Ensure we never return undefined - convert to null
334
339
  if (_rejourneyNative === undefined) {
335
340
  _rejourneyNative = null;
336
341
  }
@@ -421,10 +426,8 @@ const Rejourney: RejourneyAPI = {
421
426
 
422
427
  getLogger().debug(`Calling native startSession (apiUrl=${apiUrl})`);
423
428
 
424
- // Use user identity if set, otherwise use anonymous device ID
425
429
  const deviceId = await getAutoTracking().ensurePersistentAnonymousId();
426
430
 
427
- // Try to load persisted user identity if not already set in memory
428
431
  if (!_userIdentity) {
429
432
  _userIdentity = await loadPersistedUserIdentity();
430
433
  }
@@ -432,7 +435,6 @@ const Rejourney: RejourneyAPI = {
432
435
  const userId = _userIdentity || deviceId;
433
436
  getLogger().debug(`userId=${userId.substring(0, 8)}...`);
434
437
 
435
- // Start native session
436
438
  const result = await nativeModule.startSession(userId, apiUrl, publicKey);
437
439
  getLogger().debug('Native startSession returned:', JSON.stringify(result));
438
440
 
@@ -447,10 +449,28 @@ const Rejourney: RejourneyAPI = {
447
449
 
448
450
  _isRecording = true;
449
451
  getLogger().debug(`✅ Session started: ${result.sessionId}`);
450
- // Use lifecycle log for session start - only shown in dev builds
451
452
  getLogger().logSessionStart(result.sessionId);
453
+ // Start polling for upload stats in dev mode
454
+ if (__DEV__) {
455
+ _metricsInterval = setInterval(async () => {
456
+ if (!_isRecording) {
457
+ if (_metricsInterval) clearInterval(_metricsInterval);
458
+ return;
459
+ }
460
+ try {
461
+ const native = getRejourneyNative();
462
+ if (native) {
463
+ const metrics = await native.getSDKMetrics();
464
+ if (metrics) {
465
+ getLogger().logUploadStats(metrics);
466
+ }
467
+ }
468
+ } catch (e) {
469
+ getLogger().debug('Failed to fetch metrics:', e);
470
+ }
471
+ }, 10000); // Poll more frequently in dev (10s) for better feedback
472
+ }
452
473
 
453
- // Initialize auto tracking features
454
474
  getAutoTracking().initAutoTracking(
455
475
  {
456
476
  rageTapThreshold: _storedConfig?.rageTapThreshold ?? 3,
@@ -470,7 +490,7 @@ const Rejourney: RejourneyAPI = {
470
490
  x,
471
491
  y,
472
492
  });
473
- // logger.debug(`Rage tap detected: ${count} taps at (${x}, ${y})`);
493
+ getLogger().logFrustration(`Rage tap (${count} taps)`);
474
494
  },
475
495
  // Error callback - log as error event
476
496
  onError: (error: { message: string; stack?: string; name?: string }) => {
@@ -479,28 +499,22 @@ const Rejourney: RejourneyAPI = {
479
499
  stack: error.stack,
480
500
  name: error.name,
481
501
  });
482
- // logger.debug(`Error captured: ${error.message}`);
502
+ getLogger().logError(error.message);
483
503
  },
484
- // Screen change callback - log screen change
485
504
  onScreen: (_screenName: string, _previousScreen?: string) => {
486
- // Native module already handles screen changes
487
- // This is just for metrics tracking
488
- // logger.debug(`Screen changed: ${previousScreen} -> ${screenName}`);
489
505
  },
490
506
  }
491
507
  );
492
508
 
493
- // Collect and log device info
494
509
  if (_storedConfig?.collectDeviceInfo !== false) {
495
510
  try {
496
- const deviceInfo = getAutoTracking().collectDeviceInfo();
511
+ const deviceInfo = await getAutoTracking().collectDeviceInfo();
497
512
  this.logEvent('device_info', deviceInfo as unknown as Record<string, unknown>);
498
513
  } catch (deviceError) {
499
514
  getLogger().warn('Failed to collect device info:', deviceError);
500
515
  }
501
516
  }
502
517
 
503
- // Setup automatic network interception
504
518
  if (_storedConfig?.autoTrackNetwork !== false) {
505
519
  try {
506
520
  const ignoreUrls: (string | RegExp)[] = [
@@ -513,7 +527,6 @@ const Rejourney: RejourneyAPI = {
513
527
 
514
528
  getNetworkInterceptor().initNetworkInterceptor(
515
529
  (request: NetworkRequestParams) => {
516
- this.logNetworkRequest(request);
517
530
  getAutoTracking().trackAPIRequest(
518
531
  request.success || false,
519
532
  request.statusCode,
@@ -527,13 +540,11 @@ const Rejourney: RejourneyAPI = {
527
540
  }
528
541
  );
529
542
 
530
- // logger.debug('Network interception enabled');
531
543
  } catch (networkError) {
532
544
  getLogger().warn('Failed to setup network interception:', networkError);
533
545
  }
534
546
  }
535
547
 
536
- // logger.debug('Auto tracking enabled');
537
548
 
538
549
  return true;
539
550
  } catch (error) {
@@ -553,19 +564,21 @@ const Rejourney: RejourneyAPI = {
553
564
  }
554
565
 
555
566
  try {
556
- // Get session metrics before stopping
557
567
  const metrics = getAutoTracking().getSessionMetrics();
558
568
  this.logEvent('session_metrics', metrics as unknown as Record<string, unknown>);
559
569
 
560
- // Cleanup
561
570
  getNetworkInterceptor().disableNetworkInterceptor();
562
571
  getAutoTracking().cleanupAutoTracking();
563
572
  getAutoTracking().resetMetrics();
564
573
 
565
574
  await safeNativeCall('stopSession', () => getRejourneyNative()!.stopSession(), undefined);
566
575
 
576
+ if (_metricsInterval) {
577
+ clearInterval(_metricsInterval);
578
+ _metricsInterval = null;
579
+ }
580
+
567
581
  _isRecording = false;
568
- // Use lifecycle log for session end - only shown in dev builds
569
582
  getLogger().logSessionEnd('current');
570
583
  } catch (error) {
571
584
  getLogger().error('Failed to stop recording:', error);
@@ -584,7 +597,6 @@ const Rejourney: RejourneyAPI = {
584
597
  safeNativeCallSync(
585
598
  'logEvent',
586
599
  () => {
587
- // Fire and forget - don't await
588
600
  getRejourneyNative()!.logEvent(name, properties || {}).catch(() => { });
589
601
  },
590
602
  undefined
@@ -603,9 +615,7 @@ const Rejourney: RejourneyAPI = {
603
615
  setUserIdentity(userId: string): void {
604
616
  _userIdentity = userId;
605
617
  persistUserIdentity(userId).catch(() => { });
606
- // logger.debug(`User identity set: ${userId}`);
607
618
 
608
- // If recording is active, update the native module immediately
609
619
  if (_isRecording && getRejourneyNative()) {
610
620
  safeNativeCallSync(
611
621
  'setUserIdentity',
@@ -624,9 +634,7 @@ const Rejourney: RejourneyAPI = {
624
634
  clearUserIdentity(): void {
625
635
  _userIdentity = null;
626
636
  persistUserIdentity(null).catch(() => { });
627
- // logger.debug('User identity cleared');
628
637
 
629
- // If recording is active, update the native module immediately
630
638
  if (_isRecording && getRejourneyNative()) {
631
639
  safeNativeCallSync(
632
640
  'setUserIdentity',
@@ -645,10 +653,7 @@ const Rejourney: RejourneyAPI = {
645
653
  * @param params - Optional screen parameters
646
654
  */
647
655
  tagScreen(screenName: string, _params?: Record<string, unknown>): void {
648
- // Track screen for metrics and funnel tracking
649
656
  getAutoTracking().trackScreen(screenName);
650
-
651
- // Notify state change (kept for API compatibility)
652
657
  getAutoTracking().notifyStateChange();
653
658
 
654
659
  safeNativeCallSync(
@@ -737,7 +742,6 @@ const Rejourney: RejourneyAPI = {
737
742
  * @returns Path to export file (not implemented)
738
743
  */
739
744
  async exportSession(_sessionId: string): Promise<string> {
740
- // Return empty string - actual export should be done from dashboard server
741
745
  getLogger().warn('exportSession not implemented - export from dashboard server');
742
746
  return '';
743
747
  },
@@ -845,10 +849,6 @@ const Rejourney: RejourneyAPI = {
845
849
  );
846
850
  },
847
851
 
848
- // ========================================================================
849
- // OAuth / External URL Tracking
850
- // ========================================================================
851
-
852
852
  /**
853
853
  * Notify the SDK that an OAuth flow is starting
854
854
  *
@@ -997,17 +997,12 @@ const Rejourney: RejourneyAPI = {
997
997
  cached: request.cached,
998
998
  };
999
999
 
1000
- // Fire and forget - don't await, this is low priority
1001
1000
  getRejourneyNative()!.logEvent('network_request', networkEvent).catch(() => { });
1002
1001
  },
1003
1002
  undefined
1004
1003
  );
1005
1004
  },
1006
1005
 
1007
- // ========================================================================
1008
- // SDK Telemetry / Observability
1009
- // ========================================================================
1010
-
1011
1006
  /**
1012
1007
  * Get SDK telemetry metrics for observability
1013
1008
  *
@@ -1065,10 +1060,6 @@ const Rejourney: RejourneyAPI = {
1065
1060
  }
1066
1061
  },
1067
1062
 
1068
- // ========================================================================
1069
- // Privacy / View Masking
1070
- // ========================================================================
1071
-
1072
1063
  /**
1073
1064
  * Mask a view by its nativeID prop (will be occluded in recordings)
1074
1065
  *
@@ -1113,10 +1104,6 @@ const Rejourney: RejourneyAPI = {
1113
1104
  },
1114
1105
  };
1115
1106
 
1116
- // =============================================================================
1117
- // Automatic Lifecycle Management
1118
- // =============================================================================
1119
-
1120
1107
  /**
1121
1108
  * Handle app state changes for automatic session management
1122
1109
  * - Pauses recording when app goes to background
@@ -1129,10 +1116,10 @@ function handleAppStateChange(nextAppState: string): void {
1129
1116
  try {
1130
1117
  if (_currentAppState.match(/active/) && nextAppState === 'background') {
1131
1118
  // App going to background - native module handles this automatically
1132
- getLogger().debug('App moving to background');
1119
+ getLogger().logLifecycleEvent('App moving to background');
1133
1120
  } else if (_currentAppState.match(/inactive|background/) && nextAppState === 'active') {
1134
1121
  // App coming back to foreground
1135
- getLogger().debug('App returning to foreground');
1122
+ getLogger().logLifecycleEvent('App returning to foreground');
1136
1123
  }
1137
1124
  _currentAppState = nextAppState;
1138
1125
  } catch (error) {
@@ -1150,20 +1137,14 @@ function setupLifecycleManagement(): void {
1150
1137
  const RN = getReactNative();
1151
1138
  if (!RN) return;
1152
1139
 
1153
- // Remove any existing subscription
1154
1140
  if (_appStateSubscription) {
1155
1141
  _appStateSubscription.remove();
1156
1142
  _appStateSubscription = null;
1157
1143
  }
1158
1144
 
1159
1145
  try {
1160
- // Get current app state
1161
1146
  _currentAppState = RN.AppState.currentState || 'active';
1162
-
1163
- // Subscribe to app state changes
1164
1147
  _appStateSubscription = RN.AppState.addEventListener('change', handleAppStateChange);
1165
-
1166
- // Setup auth error listener from native module
1167
1148
  setupAuthErrorListener();
1168
1149
 
1169
1150
  getLogger().debug('Lifecycle management enabled');
@@ -1190,9 +1171,6 @@ function setupAuthErrorListener(): void {
1190
1171
  try {
1191
1172
  const nativeModule = getRejourneyNative();
1192
1173
  if (nativeModule) {
1193
- // RN warns if a non-null module is passed without addListener/removeListeners.
1194
- // Our native module may not implement these no-op methods yet, so only pass
1195
- // the module when those hooks exist; otherwise use the global emitter.
1196
1174
  const maybeAny = nativeModule as any;
1197
1175
  const hasEventEmitterHooks =
1198
1176
  typeof maybeAny?.addListener === 'function' && typeof maybeAny?.removeListeners === 'function';
@@ -1212,10 +1190,8 @@ function setupAuthErrorListener(): void {
1212
1190
  getLogger().logInvalidProjectKey();
1213
1191
  }
1214
1192
 
1215
- // Update SDK state - recording has been stopped by native
1216
1193
  _isRecording = false;
1217
1194
 
1218
- // Call user's error handler if provided
1219
1195
  if (_storedConfig?.onAuthError) {
1220
1196
  try {
1221
1197
  _storedConfig.onAuthError(error);
@@ -1227,7 +1203,6 @@ function setupAuthErrorListener(): void {
1227
1203
  );
1228
1204
  }
1229
1205
  } catch (error) {
1230
- // Event emitter not available on this platform - that's OK
1231
1206
  getLogger().debug('Auth error listener not available:', error);
1232
1207
  }
1233
1208
  }
@@ -1246,10 +1221,6 @@ function cleanupLifecycleManagement(): void {
1246
1221
  }
1247
1222
  }
1248
1223
 
1249
- // =============================================================================
1250
- // Simple Initialization API
1251
- // =============================================================================
1252
-
1253
1224
  /**
1254
1225
  * Initialize Rejourney SDK - STEP 1 of 3
1255
1226
  *
@@ -1282,14 +1253,12 @@ export function initRejourney(
1282
1253
  publicRouteKey: string,
1283
1254
  options?: Omit<RejourneyConfig, 'publicRouteKey'>
1284
1255
  ): void {
1285
- // Validate public route key
1286
1256
  if (!publicRouteKey || typeof publicRouteKey !== 'string') {
1287
1257
  getLogger().warn('Rejourney: Invalid public route key provided. SDK will be disabled.');
1288
1258
  _initializationFailed = true;
1289
1259
  return;
1290
1260
  }
1291
1261
 
1292
- // Store config for later use
1293
1262
  _storedConfig = {
1294
1263
  ...options,
1295
1264
  publicRouteKey,
@@ -1350,7 +1319,6 @@ export function startRejourney(): void {
1350
1319
  getLogger().logRecordingStart();
1351
1320
  getLogger().debug('Starting session...');
1352
1321
 
1353
- // Fire and forget - don't block the caller
1354
1322
  (async () => {
1355
1323
  try {
1356
1324
  const started = await Rejourney._startSession();
@@ -1384,11 +1352,8 @@ export function stopRejourney(): void {
1384
1352
 
1385
1353
  export default Rejourney;
1386
1354
 
1387
- // Export types
1388
1355
  export * from './types';
1389
1356
 
1390
- // Export auto tracking utilities for advanced usage
1391
- // These are used internally but can be called manually if needed
1392
1357
  export {
1393
1358
  trackTap,
1394
1359
  trackScroll,
@@ -1399,11 +1364,8 @@ export {
1399
1364
  getSessionMetrics,
1400
1365
  } from './sdk/autoTracking';
1401
1366
 
1402
- // Navigation
1403
1367
  export { trackNavigationState, useNavigationTracking } from './sdk/autoTracking';
1404
1368
 
1405
- // Re-export LogLevel enum from utils
1406
- // Note: This is safe because the enum itself doesn't trigger react-native imports
1407
1369
  export { LogLevel } from './sdk/utils';
1408
1370
 
1409
1371
  /**
@@ -1438,15 +1400,4 @@ export function setLogLevel(level: number): void {
1438
1400
  getLogger().setLogLevel(level);
1439
1401
  }
1440
1402
 
1441
- // Note: Components and hooks removed in new engine
1442
- // Session replay now handled by dashboard web UI
1443
- // export { RejourneyReplay } from './components/replay/RejourneyReplay';
1444
- // export { GestureTracker } from './components/GestureTracker';
1445
- // export { SessionList } from './components/SessionList';
1446
- // export { useRejourney } from './hooks/useRejourney';
1447
- // export { useReplay } from './hooks/useReplay';
1448
-
1449
- // Note: SDK managers removed in new engine - all functionality handled by native module
1450
-
1451
- // Export Mask component
1452
1403
  export { Mask } from './components/Mask';