@rejourneyco/react-native 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +35 -29
  2. package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +7 -0
  3. package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +9 -0
  4. package/ios/Capture/RJCaptureEngine.m +3 -34
  5. package/ios/Capture/RJVideoEncoder.m +0 -26
  6. package/ios/Core/Rejourney.mm +32 -95
  7. package/ios/Utils/RJPerfTiming.m +0 -5
  8. package/ios/Utils/RJWindowUtils.m +0 -1
  9. package/lib/commonjs/components/Mask.js +1 -6
  10. package/lib/commonjs/index.js +4 -87
  11. package/lib/commonjs/sdk/autoTracking.js +39 -310
  12. package/lib/commonjs/sdk/constants.js +2 -13
  13. package/lib/commonjs/sdk/errorTracking.js +1 -29
  14. package/lib/commonjs/sdk/metricsTracking.js +3 -24
  15. package/lib/commonjs/sdk/navigation.js +3 -42
  16. package/lib/commonjs/sdk/networkInterceptor.js +7 -49
  17. package/lib/commonjs/sdk/utils.js +0 -5
  18. package/lib/module/components/Mask.js +1 -6
  19. package/lib/module/index.js +3 -91
  20. package/lib/module/sdk/autoTracking.js +39 -311
  21. package/lib/module/sdk/constants.js +2 -13
  22. package/lib/module/sdk/errorTracking.js +1 -29
  23. package/lib/module/sdk/index.js +0 -2
  24. package/lib/module/sdk/metricsTracking.js +3 -24
  25. package/lib/module/sdk/navigation.js +3 -42
  26. package/lib/module/sdk/networkInterceptor.js +7 -49
  27. package/lib/module/sdk/utils.js +0 -5
  28. package/lib/typescript/NativeRejourney.d.ts +1 -0
  29. package/lib/typescript/sdk/autoTracking.d.ts +4 -4
  30. package/lib/typescript/types/index.d.ts +0 -1
  31. package/package.json +2 -8
  32. package/src/NativeRejourney.ts +2 -0
  33. package/src/components/Mask.tsx +0 -3
  34. package/src/index.ts +3 -73
  35. package/src/sdk/autoTracking.ts +51 -282
  36. package/src/sdk/constants.ts +13 -13
  37. package/src/sdk/errorTracking.ts +1 -17
  38. package/src/sdk/index.ts +0 -2
  39. package/src/sdk/metricsTracking.ts +5 -33
  40. package/src/sdk/navigation.ts +8 -29
  41. package/src/sdk/networkInterceptor.ts +9 -33
  42. package/src/sdk/utils.ts +0 -5
  43. package/src/types/index.ts +0 -29
@@ -46,6 +46,7 @@
46
46
  #import <UIKit/UIKit.h>
47
47
  #import <mach/mach_time.h>
48
48
  #import <sys/sysctl.h>
49
+ #import <sys/utsname.h>
49
50
 
50
51
  static uint64_t _rj_constructorMachTime = 0;
51
52
  static NSTimeInterval _rj_constructorWallTimeMs = 0;
@@ -608,6 +609,34 @@ RCT_EXPORT_METHOD(debugTriggerANR : (double)durationMs) {
608
609
  });
609
610
  }
610
611
 
612
+ RCT_EXPORT_METHOD(getDeviceInfo : (RCTPromiseResolveBlock)
613
+ resolve reject : (RCTPromiseRejectBlock)reject) {
614
+ NSMutableDictionary *info = [NSMutableDictionary new];
615
+
616
+ // Model
617
+ struct utsname systemInfo;
618
+ uname(&systemInfo);
619
+ NSString *modelCode = [NSString stringWithCString:systemInfo.machine
620
+ encoding:NSUTF8StringEncoding];
621
+ info[@"model"] = modelCode ?: [[UIDevice currentDevice] model];
622
+
623
+ info[@"brand"] = @"Apple";
624
+ info[@"systemName"] = [[UIDevice currentDevice] systemName];
625
+ info[@"systemVersion"] = [[UIDevice currentDevice] systemVersion];
626
+ info[@"bundleId"] = [[NSBundle mainBundle] bundleIdentifier] ?: @"";
627
+ info[@"appVersion"] =
628
+ [[NSBundle mainBundle]
629
+ objectForInfoDictionaryKey:@"CFBundleShortVersionString"]
630
+ ?: @"";
631
+ info[@"buildNumber"] =
632
+ [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]
633
+ ?: @"";
634
+ info[@"isTablet"] = @([[UIDevice currentDevice] userInterfaceIdiom] ==
635
+ UIUserInterfaceIdiomPad);
636
+
637
+ resolve(info);
638
+ }
639
+
611
640
  RCT_EXPORT_METHOD(getSessionId : (RCTPromiseResolveBlock)
612
641
  resolve reject : (RCTPromiseRejectBlock)reject) {
613
642
  NSString *sessionId = self.currentSessionId;
@@ -947,7 +976,6 @@ RCT_EXPORT_METHOD(setUserIdentity : (NSString *)userId resolve : (
947
976
  @try {
948
977
  NSString *safeUserId = userId.length > 0 ? userId : @"anonymous";
949
978
 
950
- // KEY CHANGE: Persist directly to NSUserDefaults (Native Storage)
951
979
  [[NSUserDefaults standardUserDefaults] setObject:safeUserId
952
980
  forKey:@"rj_user_identity"];
953
981
  [[NSUserDefaults standardUserDefaults] synchronize];
@@ -960,13 +988,11 @@ RCT_EXPORT_METHOD(setUserIdentity : (NSString *)userId resolve : (
960
988
  RJLogDebug(@"User identity updated and persisted: %@", safeUserId);
961
989
 
962
990
  if (self.isRecording) {
963
- // Log event for tracking
964
991
  NSMutableDictionary *event = [NSMutableDictionary new];
965
992
  event[@"type"] = @"user_identity_changed";
966
993
  event[@"timestamp"] = @([RJWindowUtils currentTimestampMillis]);
967
994
  event[@"userId"] = safeUserId;
968
995
 
969
- // Helper to log event safely
970
996
  if (self.eventBuffer) {
971
997
  [self.eventBuffer appendEvent:event];
972
998
  }
@@ -1008,17 +1034,15 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1008
1034
  return;
1009
1035
  }
1010
1036
 
1011
- // Throttle scroll events to avoid spamming the main thread/logs
1012
1037
  if ([gestureType hasPrefix:@"scroll"]) {
1013
1038
  static NSTimeInterval lastScrollLogTime = 0;
1014
1039
  NSTimeInterval now = CACurrentMediaTime();
1015
- if (now - lastScrollLogTime < 0.5) { // 500ms throttle
1040
+ if (now - lastScrollLogTime < 0.5) {
1016
1041
  return;
1017
1042
  }
1018
1043
  lastScrollLogTime = now;
1019
1044
  }
1020
1045
 
1021
- // Move ALL processing to the background state queue to unblock Main Thread
1022
1046
  dispatch_async(self.stateQueue, ^{
1023
1047
  @try {
1024
1048
  NSMutableDictionary *details =
@@ -1030,18 +1054,8 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1030
1054
  @"targetLabel" : targetLabel ?: [NSNull null]
1031
1055
  }];
1032
1056
 
1033
- // Log internal event (already on stateQueue, so safe)
1034
- // We call a simpler version that assumes we are already on background or
1035
- // handles it Actually logEventInternal dispatches TO stateQueue. Since we
1036
- // are ON stateQueue, we can call a direct helper or just logEventInternal
1037
- // (it will just dispatch_async again which is fine) But to be cleaner,
1038
- // let's just use logEventInternal but ensuring we don't do main thread
1039
- // dictionary work above. We moved the dictionary creation HERE
1040
- // (background).
1041
-
1042
1057
  [self logEventInternal:RJEventTypeGesture details:[details copy]];
1043
1058
 
1044
- // Notify engine on main thread (it's lightweight)
1045
1059
  dispatch_async(dispatch_get_main_queue(), ^{
1046
1060
  if (self.captureEngine) {
1047
1061
  [self.captureEngine notifyGesture:gestureType];
@@ -1123,11 +1137,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1123
1137
  RJLogError(@"Device registration failed: %@",
1124
1138
  error);
1125
1139
 
1126
- // SECURITY: Handle specific error codes
1127
- // 403 = Bundle ID mismatch or forbidden - PERMANENT
1128
- // failure 404 = Project not found - could be
1129
- // temporary, retry Other = Network/transient -
1130
- // retry
1131
1140
  if (error.code == 403) {
1132
1141
  RJLogError(@"SECURITY: Bundle ID mismatch or "
1133
1142
  @"access forbidden. "
@@ -1136,9 +1145,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1136
1145
  strongSelf.authPermanentlyFailed = YES;
1137
1146
  [strongSelf handleAuthenticationFailure:error];
1138
1147
  } else {
1139
- // For 404 and other errors, schedule retry with
1140
- // exponential backoff Recording continues locally
1141
- // - events queued for later upload
1142
1148
  [strongSelf scheduleAuthRetryWithError:error
1143
1149
  publicKey:publicKey
1144
1150
  apiUrl:apiUrl];
@@ -1146,7 +1152,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1146
1152
  } else {
1147
1153
  RJLogDebug(@"Device registered: %@", credId);
1148
1154
 
1149
- // Auth succeeded - reset retry state
1150
1155
  [strongSelf resetAuthRetryState];
1151
1156
 
1152
1157
  [strongSelf
@@ -1161,21 +1166,16 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1161
1166
  - (void)handleAuthenticationFailure:(NSError *)error {
1162
1167
  RJLogError(@"Authentication failure - stopping recording. Error: %@", error);
1163
1168
 
1164
- // Stop recording to prevent data accumulation that can't be uploaded
1165
1169
  dispatch_async(dispatch_get_main_queue(), ^{
1166
1170
  if (self.isRecording) {
1167
1171
  self.isRecording = NO;
1168
1172
 
1169
- // Stop capture engine
1170
1173
  if (self.captureEngine) {
1171
1174
  [self.captureEngine stopSession];
1172
1175
  }
1173
1176
 
1174
- // Clear auth data so next attempt starts fresh
1175
1177
  [[RJDeviceAuthManager sharedManager] clearAllAuthData];
1176
1178
 
1177
- // Notify JS layer about the failure (if bridge is available)
1178
- // This allows the app to handle the error (e.g., show user message)
1179
1179
  @try {
1180
1180
  if (self.bridge) {
1181
1181
  [self.bridge enqueueJSCall:@"RCTDeviceEventEmitter"
@@ -1202,22 +1202,18 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1202
1202
  - (void)scheduleAuthRetryWithError:(NSError *)error
1203
1203
  publicKey:(NSString *)publicKey
1204
1204
  apiUrl:(NSString *)apiUrl {
1205
- // Check if already permanently failed (403 security error)
1206
1205
  if (self.authPermanentlyFailed) {
1207
1206
  RJLogWarning(@"Auth permanently failed - not scheduling retry");
1208
1207
  return;
1209
1208
  }
1210
1209
 
1211
- // Increment retry count
1212
1210
  self.authRetryCount++;
1213
1211
 
1214
- // Check max retries
1215
1212
  if (self.authRetryCount > RJ_MAX_AUTH_RETRIES) {
1216
1213
  RJLogError(@"Auth failed after %ld retries. Recording continues locally, "
1217
1214
  @"events will be uploaded when auth succeeds.",
1218
1215
  (long)RJ_MAX_AUTH_RETRIES);
1219
1216
 
1220
- // Notify JS but DON'T stop recording - events queue locally
1221
1217
  dispatch_async(dispatch_get_main_queue(), ^{
1222
1218
  @try {
1223
1219
  if (self.bridge) {
@@ -1234,18 +1230,15 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1234
1230
  completion:nil];
1235
1231
  }
1236
1232
  } @catch (NSException *exception) {
1237
- // Ignore JS notification failures
1238
1233
  }
1239
1234
  });
1240
1235
 
1241
- // Schedule a much longer retry (5 minutes) to try again later
1242
1236
  [self scheduleBackgroundAuthRetryAfter:300.0
1243
1237
  publicKey:publicKey
1244
1238
  apiUrl:apiUrl];
1245
1239
  return;
1246
1240
  }
1247
1241
 
1248
- // Calculate exponential backoff delay: 2, 4, 8, 16, 32... capped at 60s
1249
1242
  NSTimeInterval delay =
1250
1243
  MIN(RJ_AUTH_RETRY_BASE_DELAY * pow(2, self.authRetryCount - 1),
1251
1244
  RJ_AUTH_RETRY_MAX_DELAY);
@@ -1263,7 +1256,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1263
1256
  - (void)scheduleBackgroundAuthRetryAfter:(NSTimeInterval)delay
1264
1257
  publicKey:(NSString *)publicKey
1265
1258
  apiUrl:(NSString *)apiUrl {
1266
- // Cancel any existing retry timer
1267
1259
  if (self.authRetryTimer) {
1268
1260
  [self.authRetryTimer invalidate];
1269
1261
  self.authRetryTimer = nil;
@@ -1296,14 +1288,11 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1296
1288
 
1297
1289
  RJLogInfo(@"Retrying auth (attempt %ld)...", (long)(self.authRetryCount + 1));
1298
1290
 
1299
- // After 2 failed attempts, clear cached auth data and re-register fresh
1300
- // This handles expired/corrupted tokens or server-side revocations
1301
1291
  if (self.authRetryCount >= 2) {
1302
1292
  RJLogInfo(@"Clearing cached auth data and re-registering fresh...");
1303
1293
  [[RJDeviceAuthManager sharedManager] clearAllAuthData];
1304
1294
  }
1305
1295
 
1306
- // Re-attempt device auth setup
1307
1296
  [self setupDeviceAuthWithPublicKey:publicKey apiUrl:apiUrl];
1308
1297
  }
1309
1298
 
@@ -1372,8 +1361,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1372
1361
  apiUrl
1373
1362
  isRetry:YES];
1374
1363
  } else {
1375
- // Re-registration also failed - this is a
1376
- // security error
1377
1364
  RJLogError(@"Re-registration failed: %@",
1378
1365
  retryError);
1379
1366
  if (retryError.code == 403 ||
@@ -1384,12 +1371,10 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1384
1371
  }
1385
1372
  }];
1386
1373
  } else {
1387
- // Already retried - this is a persistent security error
1388
1374
  RJLogError(@"Token fetch failed after retry: %@", error);
1389
1375
  [strongSelf handleAuthenticationFailure:error];
1390
1376
  }
1391
1377
  } else {
1392
- // Network or other transient errors
1393
1378
  RJLogWarning(@"Failed to get upload token (transient): %@", error);
1394
1379
  }
1395
1380
  }];
@@ -1557,7 +1542,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1557
1542
  if (self.isShuttingDown || !eventType)
1558
1543
  return;
1559
1544
 
1560
- // Log gesture events specifically for debugging
1561
1545
  if ([eventType isEqualToString:@"gesture"]) {
1562
1546
  NSString *gestureType = details[@"gestureType"] ?: @"unknown";
1563
1547
  RJLogInfo(
@@ -1774,9 +1758,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1774
1758
  });
1775
1759
  }
1776
1760
 
1777
- /// Flush data with option to end session or keep it alive for resumption.
1778
- /// @param isFinal If YES, ends the session on backend. If NO, just uploads
1779
- /// pending data.
1780
1761
  - (void)flushDataWithCompletion:(RJCompletionHandler)completion
1781
1762
  isFinal:(BOOL)isFinal {
1782
1763
 
@@ -1834,7 +1815,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1834
1815
  return;
1835
1816
  }
1836
1817
 
1837
- // For non-final flushes, skip promotion evaluation and just upload
1838
1818
  if (!isFinal) {
1839
1819
  RJLogInfo(
1840
1820
  @"[RJ-FLUSH] Non-final background flush - uploading %lu "
@@ -1915,13 +1895,10 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1915
1895
  }
1916
1896
  }
1917
1897
 
1918
- /// Convenience method for final flush (ends session).
1919
1898
  - (void)flushAllDataWithCompletion:(RJCompletionHandler)completion {
1920
1899
  [self flushDataWithCompletion:completion isFinal:YES];
1921
1900
  }
1922
1901
 
1923
- /// Flush pending data for background transition without ending the session.
1924
- /// Used when app goes to background but may return quickly.
1925
1902
  - (void)flushDataForBackgroundWithCompletion:(RJCompletionHandler)completion {
1926
1903
  [self flushDataWithCompletion:completion isFinal:NO];
1927
1904
  }
@@ -1960,7 +1937,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
1960
1937
  if (self.isRecording) {
1961
1938
  RJLogDebug(@"[KEYBOARD] Keyboard shown (height=%.0f)",
1962
1939
  keyboardFrame.size.height);
1963
- // Include keyboard height so web UI can render overlay
1964
1940
  [self logEventInternal:RJEventTypeKeyboardShow
1965
1941
  details:@{
1966
1942
  @"keyboardHeight" : @(keyboardFrame.size.height),
@@ -2020,9 +1996,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2020
1996
  @try {
2021
1997
  [self stopBatchUploadTimer];
2022
1998
 
2023
- // CRITICAL: Sync accumulated background time to uploadManager BEFORE any
2024
- // flush This ensures if the session ends during background, the correct
2025
- // time is included
2026
1999
  if (self.uploadManager && self.lifecycleManager) {
2027
2000
  NSTimeInterval currentBgTime =
2028
2001
  self.lifecycleManager.totalBackgroundTimeMs;
@@ -2032,14 +2005,10 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2032
2005
  currentBgTime);
2033
2006
  }
2034
2007
 
2035
- // Log background event FIRST and SYNCHRONOUSLY so it's included in the
2036
- // flush This must happen before we pause capture or start the flush
2037
2008
  NSMutableDictionary *bgEvent =
2038
2009
  [NSMutableDictionary dictionaryWithCapacity:3];
2039
2010
  bgEvent[@"type"] = RJEventTypeAppBackground;
2040
2011
  bgEvent[@"timestamp"] = @([RJWindowUtils currentTimestampMillis]);
2041
-
2042
- // Add directly to session events synchronously
2043
2012
  [self performStateSync:^{
2044
2013
  if (self.sessionEvents) {
2045
2014
  [self.sessionEvents addObject:bgEvent];
@@ -2049,17 +2018,12 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2049
2018
  }
2050
2019
  }];
2051
2020
 
2052
- // Also add to event buffer for persistence
2053
2021
  if (self.eventBuffer) {
2054
2022
  [self.eventBuffer appendEvent:bgEvent];
2055
2023
  }
2056
2024
 
2057
2025
  if (self.captureEngine) {
2058
2026
  @try {
2059
- // Use ASYNCHRONOUS pause for backgrounding.
2060
- // The encoder now implements internal UIBackgroundTask protection, so
2061
- // it will finish finalizing the MP4 on a background queue without
2062
- // locking the UI.
2063
2027
  RJLogInfo(@"[RJ-VIDEO] Pausing video capture for background (ASYNC)");
2064
2028
  [self.captureEngine pauseVideoCapture];
2065
2029
  RJLogInfo(@"[RJ-VIDEO] Video capture pause initiated");
@@ -2074,9 +2038,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2074
2038
  beginBackgroundTaskWithName:@"RejourneySessionFlush"];
2075
2039
  }
2076
2040
 
2077
- // Use non-final flush for background - session may resume if user returns
2078
- // quickly This avoids calling session/end which would prevent frame capture
2079
- // after returning
2080
2041
  RJLogInfo(@"[RJ-FLUSH] Starting non-final background flush (session will "
2081
2042
  @"resume if user returns)");
2082
2043
  [self flushDataForBackgroundWithCompletion:^(BOOL success) {
@@ -2129,13 +2090,9 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2129
2090
  @try {
2130
2091
  [self stopBatchUploadTimer];
2131
2092
 
2132
- // CRITICAL: Calculate and sync background time before ending session
2133
- // If we're being terminated while in background, we need to include
2134
- // the current background duration in the session's total background time
2135
2093
  if (self.uploadManager && self.lifecycleManager) {
2136
2094
  NSTimeInterval totalBgTime = self.lifecycleManager.totalBackgroundTimeMs;
2137
2095
 
2138
- // If we're currently in background, add the ongoing duration
2139
2096
  if (self.lifecycleManager.isInBackground &&
2140
2097
  self.lifecycleManager.backgroundEntryTime > 0) {
2141
2098
  NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
@@ -2155,15 +2112,12 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2155
2112
  }
2156
2113
 
2157
2114
  if (self.captureEngine) {
2158
- // Use SYNCHRONOUS stop to ensure segment is finished and upload is
2159
- // triggered before we try to upload events. This prevents video segments
2160
- // from being lost when app is terminated.
2115
+
2161
2116
  RJLogInfo(@"[RJ-TERMINATE] Stopping capture engine synchronously");
2162
2117
  [self.captureEngine stopSessionSync];
2163
2118
  RJLogInfo(@"[RJ-TERMINATE] Capture engine stopped");
2164
2119
  }
2165
2120
 
2166
- // Add terminated event SYNCHRONOUSLY before we copy events
2167
2121
  NSMutableDictionary *terminateEvent =
2168
2122
  [NSMutableDictionary dictionaryWithCapacity:3];
2169
2123
  terminateEvent[@"type"] = RJEventTypeAppTerminated;
@@ -2219,7 +2173,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2219
2173
  @try {
2220
2174
  NSTimeInterval bgTimeMs = self.lifecycleManager.totalBackgroundTimeMs;
2221
2175
 
2222
- // Keep local state in sync so stopSession/flush paths can rely on it.
2223
2176
  self.totalBackgroundTimeMs = bgTimeMs;
2224
2177
 
2225
2178
  if (self.uploadManager) {
@@ -2259,10 +2212,7 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2259
2212
  [self handleSessionTimeout:backgroundDuration currentTime:currentTime];
2260
2213
  }
2261
2214
 
2262
- /// Handle session timeout after extended background period.
2263
- /// This cleanly ends the old session and starts a fresh one.
2264
- ///
2265
- /// Flow:
2215
+
2266
2216
  /// 1. Capture final background time from lifecycle manager
2267
2217
  /// 2. Stop timers and capture engine for old session
2268
2218
  /// 3. Synchronously end the old session with correct background time
@@ -2326,7 +2276,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2326
2276
  if (finalEvents.count > 0) {
2327
2277
  [self.uploadManager synchronousUploadWithEvents:finalEvents];
2328
2278
  } else {
2329
- // Even with no events, end the session to record background time
2330
2279
  [self.uploadManager endSessionSync];
2331
2280
  }
2332
2281
 
@@ -2343,8 +2292,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2343
2292
  self.sessionStartTime = currentTime;
2344
2293
  self.totalBackgroundTimeMs = 0;
2345
2294
  [self.sessionEvents removeAllObjects];
2346
-
2347
- // Persist for crash recovery
2348
2295
  [[NSUserDefaults standardUserDefaults]
2349
2296
  setObject:newSessionId
2350
2297
  forKey:@"rj_current_session_id"];
@@ -2353,7 +2300,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2353
2300
 
2354
2301
  RJLogInfo(@"[RJ-SESSION-TIMEOUT] New session ID: %@", newSessionId);
2355
2302
 
2356
- // Reset upload manager for new session
2357
2303
  if (self.uploadManager) {
2358
2304
  @try {
2359
2305
  [self.uploadManager resetForNewSession];
@@ -2365,7 +2311,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2365
2311
  self.uploadManager.sessionStartTime = currentTime;
2366
2312
  self.uploadManager.totalBackgroundTimeMs = 0;
2367
2313
 
2368
- // Preserve user identity
2369
2314
  if (!self.userId) {
2370
2315
  self.userId = [[NSUserDefaults standardUserDefaults]
2371
2316
  stringForKey:@"rj_user_identity"];
@@ -2373,17 +2318,14 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2373
2318
  self.uploadManager.userId = self.userId ?: @"anonymous";
2374
2319
  }
2375
2320
 
2376
- // Reset lifecycle manager background tracking
2377
2321
  if (self.lifecycleManager) {
2378
2322
  [self.lifecycleManager resetBackgroundTime];
2379
2323
  self.lifecycleManager.isRecording = YES;
2380
2324
  }
2381
2325
 
2382
- // Reset event buffer for new session - recreate since sessionId is readonly
2383
2326
  if (self.eventBuffer) {
2384
2327
  [self.eventBuffer clearAllEvents];
2385
2328
  }
2386
- // Create new event buffer for new session (sessionId is readonly)
2387
2329
  NSString *pendingPath = self.eventBuffer.pendingRootPath;
2388
2330
  if (pendingPath.length == 0) {
2389
2331
  pendingPath =
@@ -2417,15 +2359,11 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2417
2359
  }
2418
2360
 
2419
2361
  self.isRecording = YES;
2420
-
2421
- // Verify touch tracking
2422
2362
  RJTouchInterceptor *touchInterceptor = [RJTouchInterceptor sharedInstance];
2423
2363
  if (touchInterceptor && !touchInterceptor.isTrackingEnabled) {
2424
2364
  RJLogInfo(@"[RJ-SESSION-TIMEOUT] Re-enabling touch tracking");
2425
2365
  [self setupTouchTracking];
2426
2366
  }
2427
-
2428
- // Start timers for new session
2429
2367
  [self startBatchUploadTimer];
2430
2368
  [self startDurationLimitTimer];
2431
2369
 
@@ -2447,7 +2385,6 @@ RCT_EXPORT_METHOD(getUserIdentity : (RCTPromiseResolveBlock)
2447
2385
  [self.eventBuffer appendEvent:sessionStartEvent];
2448
2386
  }
2449
2387
 
2450
- // Immediate upload to register session with backend
2451
2388
  dispatch_after(
2452
2389
  dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
2453
2390
  dispatch_get_main_queue(), ^{
@@ -68,14 +68,9 @@ void rj_perf_record(RJPerfMetric metric, uint64_t start, uint64_t end) {
68
68
 
69
69
  double ms = rj_ms(start, end);
70
70
 
71
- // IMMEDIATE LOGGING (Request by user)
72
- // Pinpoints main thread blockers
73
71
  BOOL isMain = [NSThread isMainThread];
74
72
  const char *threadName = isMain ? "MAIN" : "BG";
75
73
 
76
- // Log critical main thread hitches (> 4ms) visibly
77
- // Or just log everything as requested ("intense logging")
78
- // Using specific emoji to highlight potential issues
79
74
  if (isMain && ms > 4.0) {
80
75
  RJLogInfo(@"[RJ-PERF] ⚠️ [%s] %s: %.2fms", threadName, rj_metric_names[metric],
81
76
  ms);
@@ -73,7 +73,6 @@
73
73
  CGFloat level = window.windowLevel;
74
74
 
75
75
  if (window.isKeyWindow) {
76
- // Keep as a last-ditch fallback (even if it's a system window).
77
76
  if (!anyKey) {
78
77
  anyKey = window;
79
78
  }
@@ -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],