@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.
Files changed (39) hide show
  1. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +38 -363
  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/ios/Capture/RJViewHierarchyScanner.m +68 -51
  15. package/ios/Core/RJLifecycleManager.m +0 -14
  16. package/ios/Core/Rejourney.mm +24 -37
  17. package/ios/Network/RJDeviceAuthManager.m +0 -2
  18. package/ios/Network/RJUploadManager.h +8 -0
  19. package/ios/Network/RJUploadManager.m +45 -0
  20. package/ios/Privacy/RJPrivacyMask.m +5 -31
  21. package/ios/Rejourney.h +0 -14
  22. package/ios/Touch/RJTouchInterceptor.m +21 -15
  23. package/ios/Utils/RJEventBuffer.m +57 -69
  24. package/ios/Utils/RJWindowUtils.m +87 -86
  25. package/lib/commonjs/index.js +42 -30
  26. package/lib/commonjs/sdk/autoTracking.js +0 -3
  27. package/lib/commonjs/sdk/networkInterceptor.js +0 -11
  28. package/lib/commonjs/sdk/utils.js +73 -14
  29. package/lib/module/index.js +42 -30
  30. package/lib/module/sdk/autoTracking.js +0 -3
  31. package/lib/module/sdk/networkInterceptor.js +0 -11
  32. package/lib/module/sdk/utils.js +73 -14
  33. package/lib/typescript/sdk/utils.d.ts +31 -1
  34. package/package.json +16 -4
  35. package/src/index.ts +40 -19
  36. package/src/sdk/autoTracking.ts +0 -2
  37. package/src/sdk/constants.ts +13 -13
  38. package/src/sdk/networkInterceptor.ts +0 -9
  39. package/src/sdk/utils.ts +76 -14
@@ -86,7 +86,12 @@ function getLogger() {
86
86
  logRecordingStart: () => {},
87
87
  logRecordingRemoteDisabled: () => {},
88
88
  logInvalidProjectKey: () => {},
89
- logPackageMismatch: () => {}
89
+ logPackageMismatch: () => {},
90
+ logNetworkRequest: () => {},
91
+ logFrustration: () => {},
92
+ logError: () => {},
93
+ logUploadStats: () => {},
94
+ logLifecycleEvent: () => {}
90
95
  };
91
96
  }
92
97
  try {
@@ -111,7 +116,12 @@ function getLogger() {
111
116
  logRecordingStart: () => {},
112
117
  logRecordingRemoteDisabled: () => {},
113
118
  logInvalidProjectKey: () => {},
114
- logPackageMismatch: () => {}
119
+ logPackageMismatch: () => {},
120
+ logNetworkRequest: () => {},
121
+ logFrustration: () => {},
122
+ logError: () => {},
123
+ logUploadStats: () => {},
124
+ logLifecycleEvent: () => {}
115
125
  };
116
126
  }
117
127
  }
@@ -168,6 +178,7 @@ function getAutoTracking() {
168
178
  let _isInitialized = false;
169
179
  let _isRecording = false;
170
180
  let _initializationFailed = false;
181
+ let _metricsInterval = null;
171
182
  let _appStateSubscription = null;
172
183
  let _authErrorSubscription = null;
173
184
  let _currentAppState = 'active'; // Default to active, will be updated on init
@@ -370,18 +381,12 @@ const Rejourney = {
370
381
  const apiUrl = _storedConfig.apiUrl || 'https://api.rejourney.co';
371
382
  const publicKey = _storedConfig.publicRouteKey || '';
372
383
  getLogger().debug(`Calling native startSession (apiUrl=${apiUrl})`);
373
-
374
- // Use user identity if set, otherwise use anonymous device ID
375
384
  const deviceId = await getAutoTracking().ensurePersistentAnonymousId();
376
-
377
- // Try to load persisted user identity if not already set in memory
378
385
  if (!_userIdentity) {
379
386
  _userIdentity = await loadPersistedUserIdentity();
380
387
  }
381
388
  const userId = _userIdentity || deviceId;
382
389
  getLogger().debug(`userId=${userId.substring(0, 8)}...`);
383
-
384
- // Start native session
385
390
  const result = await nativeModule.startSession(userId, apiUrl, publicKey);
386
391
  getLogger().debug('Native startSession returned:', JSON.stringify(result));
387
392
  if (!result?.success) {
@@ -394,10 +399,27 @@ const Rejourney = {
394
399
  }
395
400
  _isRecording = true;
396
401
  getLogger().debug(`✅ Session started: ${result.sessionId}`);
397
- // Use lifecycle log for session start - only shown in dev builds
398
402
  getLogger().logSessionStart(result.sessionId);
399
-
400
- // Initialize auto tracking features
403
+ // Start polling for upload stats in dev mode
404
+ if (__DEV__) {
405
+ _metricsInterval = setInterval(async () => {
406
+ if (!_isRecording) {
407
+ if (_metricsInterval) clearInterval(_metricsInterval);
408
+ return;
409
+ }
410
+ try {
411
+ const native = getRejourneyNative();
412
+ if (native) {
413
+ const metrics = await native.getSDKMetrics();
414
+ if (metrics) {
415
+ getLogger().logUploadStats(metrics);
416
+ }
417
+ }
418
+ } catch (e) {
419
+ getLogger().debug('Failed to fetch metrics:', e);
420
+ }
421
+ }, 10000); // Poll more frequently in dev (10s) for better feedback
422
+ }
401
423
  getAutoTracking().initAutoTracking({
402
424
  rageTapThreshold: _storedConfig?.rageTapThreshold ?? 3,
403
425
  rageTapTimeWindow: _storedConfig?.rageTapTimeWindow ?? 500,
@@ -415,7 +437,7 @@ const Rejourney = {
415
437
  x,
416
438
  y
417
439
  });
418
- // logger.debug(`Rage tap detected: ${count} taps at (${x}, ${y})`);
440
+ getLogger().logFrustration(`Rage tap (${count} taps)`);
419
441
  },
420
442
  // Error callback - log as error event
421
443
  onError: error => {
@@ -424,17 +446,10 @@ const Rejourney = {
424
446
  stack: error.stack,
425
447
  name: error.name
426
448
  });
427
- // logger.debug(`Error captured: ${error.message}`);
449
+ getLogger().logError(error.message);
428
450
  },
429
- // Screen change callback - log screen change
430
- onScreen: (_screenName, _previousScreen) => {
431
- // Native module already handles screen changes
432
- // This is just for metrics tracking
433
- // logger.debug(`Screen changed: ${previousScreen} -> ${screenName}`);
434
- }
451
+ onScreen: (_screenName, _previousScreen) => {}
435
452
  });
436
-
437
- // Collect and log device info
438
453
  if (_storedConfig?.collectDeviceInfo !== false) {
439
454
  try {
440
455
  const deviceInfo = await getAutoTracking().collectDeviceInfo();
@@ -443,13 +458,10 @@ const Rejourney = {
443
458
  getLogger().warn('Failed to collect device info:', deviceError);
444
459
  }
445
460
  }
446
-
447
- // Setup automatic network interception
448
461
  if (_storedConfig?.autoTrackNetwork !== false) {
449
462
  try {
450
463
  const ignoreUrls = [apiUrl, '/api/ingest/presign', '/api/ingest/batch/complete', '/api/ingest/session/end', ...(_storedConfig?.networkIgnoreUrls || [])];
451
464
  getNetworkInterceptor().initNetworkInterceptor(request => {
452
- this.logNetworkRequest(request);
453
465
  getAutoTracking().trackAPIRequest(request.success || false, request.statusCode, request.duration || 0, request.responseBodySize || 0);
454
466
  }, {
455
467
  ignoreUrls,
@@ -481,6 +493,10 @@ const Rejourney = {
481
493
  getAutoTracking().cleanupAutoTracking();
482
494
  getAutoTracking().resetMetrics();
483
495
  await safeNativeCall('stopSession', () => getRejourneyNative().stopSession(), undefined);
496
+ if (_metricsInterval) {
497
+ clearInterval(_metricsInterval);
498
+ _metricsInterval = null;
499
+ }
484
500
  _isRecording = false;
485
501
  getLogger().logSessionEnd('current');
486
502
  } catch (error) {
@@ -497,7 +513,6 @@ const Rejourney = {
497
513
  */
498
514
  logEvent(name, properties) {
499
515
  safeNativeCallSync('logEvent', () => {
500
- // Fire and forget - don't await
501
516
  getRejourneyNative().logEvent(name, properties || {}).catch(() => {});
502
517
  }, undefined);
503
518
  },
@@ -625,7 +640,6 @@ const Rejourney = {
625
640
  * @returns Path to export file (not implemented)
626
641
  */
627
642
  async exportSession(_sessionId) {
628
- // Return empty string - actual export should be done from dashboard server
629
643
  getLogger().warn('exportSession not implemented - export from dashboard server');
630
644
  return '';
631
645
  },
@@ -847,8 +861,6 @@ const Rejourney = {
847
861
  errorMessage: request.errorMessage,
848
862
  cached: request.cached
849
863
  };
850
-
851
- // Fire and forget - don't await, this is low priority
852
864
  getRejourneyNative().logEvent('network_request', networkEvent).catch(() => {});
853
865
  }, undefined);
854
866
  },
@@ -945,10 +957,10 @@ function handleAppStateChange(nextAppState) {
945
957
  try {
946
958
  if (_currentAppState.match(/active/) && nextAppState === 'background') {
947
959
  // App going to background - native module handles this automatically
948
- getLogger().debug('App moving to background');
960
+ getLogger().logLifecycleEvent('App moving to background');
949
961
  } else if (_currentAppState.match(/inactive|background/) && nextAppState === 'active') {
950
962
  // App coming back to foreground
951
- getLogger().debug('App returning to foreground');
963
+ getLogger().logLifecycleEvent('App returning to foreground');
952
964
  }
953
965
  _currentAppState = nextAppState;
954
966
  } catch (error) {
@@ -452,9 +452,6 @@ function setupNavigationTracking() {
452
452
  if (__DEV__) {
453
453
  logger.debug('Setting up navigation tracking...');
454
454
  }
455
-
456
- // Delay to ensure navigation is initialized - Expo Router needs more time
457
- // We retry a few times with increasing delays
458
455
  let attempts = 0;
459
456
  const maxAttempts = 5;
460
457
  const trySetup = () => {
@@ -115,8 +115,6 @@ function queueRequest(request) {
115
115
  function flushPendingRequests() {
116
116
  flushTimer = null;
117
117
  if (!logCallback || pendingCount === 0) return;
118
-
119
- // Process all pending requests
120
118
  while (pendingCount > 0) {
121
119
  const request = pendingRequests[pendingHead];
122
120
  pendingRequests[pendingHead] = null; // Allow GC
@@ -184,14 +182,9 @@ function interceptFetch() {
184
182
  if (!shouldSampleRequest(path)) {
185
183
  return originalFetch(input, init);
186
184
  }
187
-
188
- // Capture start time (only synchronous work)
189
185
  const startTime = Date.now();
190
186
  const method = (init?.method || 'GET').toUpperCase();
191
-
192
- // Call original fetch
193
187
  return originalFetch(input, init).then(response => {
194
- // Success - queue the log asynchronously
195
188
  queueRequest({
196
189
  requestId: `f${startTime}`,
197
190
  method,
@@ -242,8 +235,6 @@ function interceptXHR() {
242
235
  if (!config.enabled || !logCallback || !data || shouldIgnoreUrl(data.u)) {
243
236
  return originalXHRSend.call(this, body);
244
237
  }
245
-
246
- // Check sampling
247
238
  const {
248
239
  path
249
240
  } = parseUrlFast(data.u);
@@ -292,8 +283,6 @@ export function initNetworkInterceptor(callback, options) {
292
283
  */
293
284
  export function disableNetworkInterceptor() {
294
285
  config.enabled = false;
295
-
296
- // Flush any pending requests
297
286
  if (flushTimer) {
298
287
  clearTimeout(flushTimer);
299
288
  flushTimer = null;
@@ -222,7 +222,6 @@ export let LogLevel = /*#__PURE__*/function (LogLevel) {
222
222
  */
223
223
  class Logger {
224
224
  prefix = '[Rejourney]';
225
- debugMode = false;
226
225
 
227
226
  /**
228
227
  * Minimum log level to display.
@@ -243,7 +242,6 @@ class Logger {
243
242
  this.minimumLogLevel = level;
244
243
  }
245
244
  setDebugMode(enabled) {
246
- this.debugMode = enabled;
247
245
  this.minimumLogLevel = enabled ? LogLevel.DEBUG : typeof __DEV__ !== 'undefined' && __DEV__ ? LogLevel.ERROR : LogLevel.SILENT;
248
246
  }
249
247
 
@@ -264,14 +262,26 @@ class Logger {
264
262
  /** Log a warning message */
265
263
  warn(...args) {
266
264
  if (this.minimumLogLevel <= LogLevel.WARNING) {
267
- console.warn(this.prefix, ...args);
265
+ if (this.minimumLogLevel <= LogLevel.DEBUG) {
266
+ // Explicit Debug Mode: Show YellowBox
267
+ console.warn(this.prefix, ...args);
268
+ } else {
269
+ // Default Dev Mode: Log to console only, avoid YellowBox
270
+ console.log(this.prefix, '[WARN]', ...args);
271
+ }
268
272
  }
269
273
  }
270
274
 
271
275
  /** Log an error message */
272
276
  error(...args) {
273
277
  if (this.minimumLogLevel <= LogLevel.ERROR) {
274
- console.error(this.prefix, ...args);
278
+ if (this.minimumLogLevel <= LogLevel.DEBUG) {
279
+ // Explicit Debug Mode: Show RedBox
280
+ console.error(this.prefix, ...args);
281
+ } else {
282
+ // Default Dev Mode: Log to console only, avoid RedBox
283
+ console.log(this.prefix, '[ERROR]', ...args);
284
+ }
275
285
  }
276
286
  }
277
287
  notice(...args) {
@@ -285,9 +295,7 @@ class Logger {
285
295
  * Only shown in development builds - this is the minimal "SDK started" log.
286
296
  */
287
297
  logInitSuccess(version) {
288
- if (this.debugMode) {
289
- this.info(`✓ SDK initialized (v${version})`);
290
- }
298
+ this.notice(`✓ SDK initialized (v${version})`);
291
299
  }
292
300
 
293
301
  /**
@@ -303,9 +311,7 @@ class Logger {
303
311
  * Only shown in development builds.
304
312
  */
305
313
  logSessionStart(sessionId) {
306
- if (this.debugMode) {
307
- this.info(`Session started: ${sessionId}`);
308
- }
314
+ this.notice(`Session started: ${sessionId}`);
309
315
  }
310
316
 
311
317
  /**
@@ -313,12 +319,10 @@ class Logger {
313
319
  * Only shown in development builds.
314
320
  */
315
321
  logSessionEnd(sessionId) {
316
- if (this.debugMode) {
317
- this.info(`Session ended: ${sessionId}`);
318
- }
322
+ this.notice(`Session ended: ${sessionId}`);
319
323
  }
320
324
  logObservabilityStart() {
321
- this.notice('Starting Rejourney observability');
325
+ this.notice('💧 Starting Rejourney observability');
322
326
  }
323
327
  logRecordingStart() {
324
328
  this.notice('Starting recording');
@@ -332,6 +336,61 @@ class Logger {
332
336
  logPackageMismatch() {
333
337
  this.notice('Bundle ID / package name mismatch');
334
338
  }
339
+
340
+ /**
341
+ * Log network request details
342
+ */
343
+ logNetworkRequest(request) {
344
+ const statusIcon = request.error || request.statusCode && request.statusCode >= 400 ? '🔴' : '🟢';
345
+ const method = request.method || 'GET';
346
+ // Shorten URL to just path if possible
347
+ let url = request.url || '';
348
+ try {
349
+ if (url.startsWith('http')) {
350
+ const urlObj = new URL(url);
351
+ url = urlObj.pathname;
352
+ }
353
+ } catch {
354
+ // Keep full URL if parsing fails
355
+ }
356
+ const duration = request.duration ? `(${Math.round(request.duration)}ms)` : '';
357
+ const status = request.statusCode ? `${request.statusCode}` : 'ERR';
358
+ this.notice(`${statusIcon} [NET] ${status} ${method} ${url} ${duration} ${request.error ? `Error: ${request.error}` : ''}`);
359
+ }
360
+
361
+ /**
362
+ * Log frustration event (rage taps, etc)
363
+ */
364
+ logFrustration(kind) {
365
+ this.notice(`🤬 Frustration detected: ${kind}`);
366
+ }
367
+
368
+ /**
369
+ * Log error captured by SDK
370
+ */
371
+ logError(message) {
372
+ this.notice(`X Error captured: ${message}`);
373
+ }
374
+
375
+ /**
376
+ * Log lifecycle event (Background/Foreground)
377
+ * Visible in development builds.
378
+ */
379
+ logLifecycleEvent(event) {
380
+ this.notice(`🔄 Lifecycle: ${event}`);
381
+ }
382
+
383
+ /**
384
+ * Log upload statistics
385
+ */
386
+ logUploadStats(metrics) {
387
+ const success = metrics.uploadSuccessCount;
388
+ const failed = metrics.uploadFailureCount;
389
+ const bytes = formatBytes(metrics.totalBytesUploaded);
390
+
391
+ // Always show in dev mode for reassurance, even if 0
392
+ this.notice(`📡 Upload Stats: ${success} success, ${failed} failed (${bytes} uploaded)`);
393
+ }
335
394
  }
336
395
  export const logger = new Logger();
337
396
  //# sourceMappingURL=utils.js.map
@@ -91,7 +91,6 @@ export declare enum LogLevel {
91
91
  */
92
92
  declare class Logger {
93
93
  private prefix;
94
- private debugMode;
95
94
  /**
96
95
  * Minimum log level to display.
97
96
  *
@@ -142,6 +141,37 @@ declare class Logger {
142
141
  logRecordingRemoteDisabled(): void;
143
142
  logInvalidProjectKey(): void;
144
143
  logPackageMismatch(): void;
144
+ /**
145
+ * Log network request details
146
+ */
147
+ logNetworkRequest(request: {
148
+ method?: string;
149
+ url?: string;
150
+ statusCode?: number;
151
+ duration?: number;
152
+ error?: string;
153
+ }): void;
154
+ /**
155
+ * Log frustration event (rage taps, etc)
156
+ */
157
+ logFrustration(kind: string): void;
158
+ /**
159
+ * Log error captured by SDK
160
+ */
161
+ logError(message: string): void;
162
+ /**
163
+ * Log lifecycle event (Background/Foreground)
164
+ * Visible in development builds.
165
+ */
166
+ logLifecycleEvent(event: string): void;
167
+ /**
168
+ * Log upload statistics
169
+ */
170
+ logUploadStats(metrics: {
171
+ uploadSuccessCount: number;
172
+ uploadFailureCount: number;
173
+ totalBytesUploaded: number;
174
+ }): void;
145
175
  }
146
176
  export declare const logger: Logger;
147
177
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rejourneyco/react-native",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Rejourney Session Recording SDK for React Native",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -47,9 +47,21 @@
47
47
  },
48
48
  "keywords": [
49
49
  "react-native",
50
- "session-recording",
51
- "analytics",
52
- "replay"
50
+ "session-replay",
51
+ "observability",
52
+ "error-tracking",
53
+ "crash-reporting",
54
+ "anr-reporting",
55
+ "api-monitoring",
56
+ "alerting",
57
+ "mobile-analytics",
58
+ "mobile-observability",
59
+ "mobile-error-tracking",
60
+ "mobile-crash-reporting",
61
+ "mobile-anr-reporting",
62
+ "mobile-api-monitoring",
63
+ "mobile-performance-monitoring",
64
+ "mobile-user-behavior-analytics"
53
65
  ],
54
66
  "repository": {
55
67
  "type": "git",
package/src/index.ts CHANGED
@@ -100,6 +100,11 @@ function getLogger() {
100
100
  logRecordingRemoteDisabled: () => { },
101
101
  logInvalidProjectKey: () => { },
102
102
  logPackageMismatch: () => { },
103
+ logNetworkRequest: () => { },
104
+ logFrustration: () => { },
105
+ logError: () => { },
106
+ logUploadStats: () => { },
107
+ logLifecycleEvent: () => { },
103
108
  };
104
109
  }
105
110
 
@@ -126,6 +131,11 @@ function getLogger() {
126
131
  logRecordingRemoteDisabled: () => { },
127
132
  logInvalidProjectKey: () => { },
128
133
  logPackageMismatch: () => { },
134
+ logNetworkRequest: () => { },
135
+ logFrustration: () => { },
136
+ logError: () => { },
137
+ logUploadStats: () => { },
138
+ logLifecycleEvent: () => { },
129
139
  };
130
140
  }
131
141
  }
@@ -194,6 +204,7 @@ function getAutoTracking() {
194
204
  let _isInitialized = false;
195
205
  let _isRecording = false;
196
206
  let _initializationFailed = false;
207
+ let _metricsInterval: ReturnType<typeof setInterval> | null = null;
197
208
  let _appStateSubscription: { remove: () => void } | null = null;
198
209
  let _authErrorSubscription: { remove: () => void } | null = null;
199
210
  let _currentAppState: string = 'active'; // Default to active, will be updated on init
@@ -415,10 +426,8 @@ const Rejourney: RejourneyAPI = {
415
426
 
416
427
  getLogger().debug(`Calling native startSession (apiUrl=${apiUrl})`);
417
428
 
418
- // Use user identity if set, otherwise use anonymous device ID
419
429
  const deviceId = await getAutoTracking().ensurePersistentAnonymousId();
420
430
 
421
- // Try to load persisted user identity if not already set in memory
422
431
  if (!_userIdentity) {
423
432
  _userIdentity = await loadPersistedUserIdentity();
424
433
  }
@@ -426,7 +435,6 @@ const Rejourney: RejourneyAPI = {
426
435
  const userId = _userIdentity || deviceId;
427
436
  getLogger().debug(`userId=${userId.substring(0, 8)}...`);
428
437
 
429
- // Start native session
430
438
  const result = await nativeModule.startSession(userId, apiUrl, publicKey);
431
439
  getLogger().debug('Native startSession returned:', JSON.stringify(result));
432
440
 
@@ -441,10 +449,28 @@ const Rejourney: RejourneyAPI = {
441
449
 
442
450
  _isRecording = true;
443
451
  getLogger().debug(`✅ Session started: ${result.sessionId}`);
444
- // Use lifecycle log for session start - only shown in dev builds
445
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
+ }
446
473
 
447
- // Initialize auto tracking features
448
474
  getAutoTracking().initAutoTracking(
449
475
  {
450
476
  rageTapThreshold: _storedConfig?.rageTapThreshold ?? 3,
@@ -464,7 +490,7 @@ const Rejourney: RejourneyAPI = {
464
490
  x,
465
491
  y,
466
492
  });
467
- // logger.debug(`Rage tap detected: ${count} taps at (${x}, ${y})`);
493
+ getLogger().logFrustration(`Rage tap (${count} taps)`);
468
494
  },
469
495
  // Error callback - log as error event
470
496
  onError: (error: { message: string; stack?: string; name?: string }) => {
@@ -473,18 +499,13 @@ const Rejourney: RejourneyAPI = {
473
499
  stack: error.stack,
474
500
  name: error.name,
475
501
  });
476
- // logger.debug(`Error captured: ${error.message}`);
502
+ getLogger().logError(error.message);
477
503
  },
478
- // Screen change callback - log screen change
479
504
  onScreen: (_screenName: string, _previousScreen?: string) => {
480
- // Native module already handles screen changes
481
- // This is just for metrics tracking
482
- // logger.debug(`Screen changed: ${previousScreen} -> ${screenName}`);
483
505
  },
484
506
  }
485
507
  );
486
508
 
487
- // Collect and log device info
488
509
  if (_storedConfig?.collectDeviceInfo !== false) {
489
510
  try {
490
511
  const deviceInfo = await getAutoTracking().collectDeviceInfo();
@@ -494,7 +515,6 @@ const Rejourney: RejourneyAPI = {
494
515
  }
495
516
  }
496
517
 
497
- // Setup automatic network interception
498
518
  if (_storedConfig?.autoTrackNetwork !== false) {
499
519
  try {
500
520
  const ignoreUrls: (string | RegExp)[] = [
@@ -507,7 +527,6 @@ const Rejourney: RejourneyAPI = {
507
527
 
508
528
  getNetworkInterceptor().initNetworkInterceptor(
509
529
  (request: NetworkRequestParams) => {
510
- this.logNetworkRequest(request);
511
530
  getAutoTracking().trackAPIRequest(
512
531
  request.success || false,
513
532
  request.statusCode,
@@ -554,6 +573,11 @@ const Rejourney: RejourneyAPI = {
554
573
 
555
574
  await safeNativeCall('stopSession', () => getRejourneyNative()!.stopSession(), undefined);
556
575
 
576
+ if (_metricsInterval) {
577
+ clearInterval(_metricsInterval);
578
+ _metricsInterval = null;
579
+ }
580
+
557
581
  _isRecording = false;
558
582
  getLogger().logSessionEnd('current');
559
583
  } catch (error) {
@@ -573,7 +597,6 @@ const Rejourney: RejourneyAPI = {
573
597
  safeNativeCallSync(
574
598
  'logEvent',
575
599
  () => {
576
- // Fire and forget - don't await
577
600
  getRejourneyNative()!.logEvent(name, properties || {}).catch(() => { });
578
601
  },
579
602
  undefined
@@ -719,7 +742,6 @@ const Rejourney: RejourneyAPI = {
719
742
  * @returns Path to export file (not implemented)
720
743
  */
721
744
  async exportSession(_sessionId: string): Promise<string> {
722
- // Return empty string - actual export should be done from dashboard server
723
745
  getLogger().warn('exportSession not implemented - export from dashboard server');
724
746
  return '';
725
747
  },
@@ -975,7 +997,6 @@ const Rejourney: RejourneyAPI = {
975
997
  cached: request.cached,
976
998
  };
977
999
 
978
- // Fire and forget - don't await, this is low priority
979
1000
  getRejourneyNative()!.logEvent('network_request', networkEvent).catch(() => { });
980
1001
  },
981
1002
  undefined
@@ -1095,10 +1116,10 @@ function handleAppStateChange(nextAppState: string): void {
1095
1116
  try {
1096
1117
  if (_currentAppState.match(/active/) && nextAppState === 'background') {
1097
1118
  // App going to background - native module handles this automatically
1098
- getLogger().debug('App moving to background');
1119
+ getLogger().logLifecycleEvent('App moving to background');
1099
1120
  } else if (_currentAppState.match(/inactive|background/) && nextAppState === 'active') {
1100
1121
  // App coming back to foreground
1101
- getLogger().debug('App returning to foreground');
1122
+ getLogger().logLifecycleEvent('App returning to foreground');
1102
1123
  }
1103
1124
  _currentAppState = nextAppState;
1104
1125
  } catch (error) {
@@ -572,8 +572,6 @@ function setupNavigationTracking(): void {
572
572
  logger.debug('Setting up navigation tracking...');
573
573
  }
574
574
 
575
- // Delay to ensure navigation is initialized - Expo Router needs more time
576
- // We retry a few times with increasing delays
577
575
  let attempts = 0;
578
576
  const maxAttempts = 5;
579
577
 
@@ -7,26 +7,26 @@ export const SDK_VERSION = '1.0.0';
7
7
  /** Default configuration values */
8
8
  export const DEFAULT_CONFIG = {
9
9
  enabled: true,
10
- captureFPS: 0.5,
11
- captureOnEvents: true,
12
- maxSessionDuration: 10 * 60 * 1000,
13
- maxStorageSize: 50 * 1024 * 1024,
10
+ captureFPS: 0.5,
11
+ captureOnEvents: true,
12
+ maxSessionDuration: 10 * 60 * 1000,
13
+ maxStorageSize: 50 * 1024 * 1024,
14
14
  autoScreenTracking: true,
15
15
  autoGestureTracking: true,
16
16
  privacyOcclusion: true,
17
17
  enableCompression: true,
18
- inactivityThreshold: 5000,
18
+ inactivityThreshold: 5000,
19
19
  disableInDev: false,
20
20
  detectRageTaps: true,
21
21
  rageTapThreshold: 3,
22
- rageTapTimeWindow: 1000,
22
+ rageTapTimeWindow: 1000,
23
23
  debug: false,
24
24
  autoStartRecording: true,
25
- collectDeviceInfo: true,
26
- collectGeoLocation: true,
27
- postNavigationDelay: 300,
28
- postGestureDelay: 200,
29
- postModalDelay: 400,
25
+ collectDeviceInfo: true,
26
+ collectGeoLocation: true,
27
+ postNavigationDelay: 300,
28
+ postGestureDelay: 200,
29
+ postModalDelay: 400,
30
30
  } as const;
31
31
 
32
32
  /** Event type constants */
@@ -61,8 +61,8 @@ export const CAPTURE_SETTINGS = {
61
61
  DEFAULT_FPS: 0.5,
62
62
  MIN_FPS: 0.1,
63
63
  MAX_FPS: 2,
64
- CAPTURE_SCALE: 0.25,
65
- MIN_CAPTURE_DELTA_TIME: 0.5,
64
+ CAPTURE_SCALE: 0.25,
65
+ MIN_CAPTURE_DELTA_TIME: 0.5,
66
66
  } as const;
67
67
 
68
68
  /** Memory management settings */