@rejourneyco/react-native 1.0.0 → 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 (49) hide show
  1. package/README.md +29 -0
  2. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +47 -30
  3. package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +25 -1
  4. package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +70 -32
  5. package/android/src/main/java/com/rejourney/core/Constants.kt +4 -4
  6. package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +14 -0
  7. package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +9 -0
  8. package/ios/Capture/RJCaptureEngine.m +72 -34
  9. package/ios/Capture/RJCaptureHeuristics.h +7 -5
  10. package/ios/Capture/RJCaptureHeuristics.m +138 -112
  11. package/ios/Capture/RJVideoEncoder.m +0 -26
  12. package/ios/Core/Rejourney.mm +64 -102
  13. package/ios/Utils/RJPerfTiming.m +0 -5
  14. package/ios/Utils/RJWindowUtils.m +0 -1
  15. package/lib/commonjs/components/Mask.js +1 -6
  16. package/lib/commonjs/index.js +12 -101
  17. package/lib/commonjs/sdk/autoTracking.js +55 -353
  18. package/lib/commonjs/sdk/constants.js +2 -13
  19. package/lib/commonjs/sdk/errorTracking.js +1 -29
  20. package/lib/commonjs/sdk/metricsTracking.js +3 -24
  21. package/lib/commonjs/sdk/navigation.js +3 -42
  22. package/lib/commonjs/sdk/networkInterceptor.js +7 -49
  23. package/lib/commonjs/sdk/utils.js +0 -5
  24. package/lib/module/components/Mask.js +1 -6
  25. package/lib/module/index.js +11 -105
  26. package/lib/module/sdk/autoTracking.js +55 -354
  27. package/lib/module/sdk/constants.js +2 -13
  28. package/lib/module/sdk/errorTracking.js +1 -29
  29. package/lib/module/sdk/index.js +0 -2
  30. package/lib/module/sdk/metricsTracking.js +3 -24
  31. package/lib/module/sdk/navigation.js +3 -42
  32. package/lib/module/sdk/networkInterceptor.js +7 -49
  33. package/lib/module/sdk/utils.js +0 -5
  34. package/lib/typescript/NativeRejourney.d.ts +2 -0
  35. package/lib/typescript/sdk/autoTracking.d.ts +5 -6
  36. package/lib/typescript/types/index.d.ts +0 -1
  37. package/package.json +11 -3
  38. package/src/NativeRejourney.ts +4 -0
  39. package/src/components/Mask.tsx +0 -3
  40. package/src/index.ts +11 -88
  41. package/src/sdk/autoTracking.ts +72 -331
  42. package/src/sdk/constants.ts +13 -13
  43. package/src/sdk/errorTracking.ts +1 -17
  44. package/src/sdk/index.ts +0 -2
  45. package/src/sdk/metricsTracking.ts +5 -33
  46. package/src/sdk/navigation.ts +8 -29
  47. package/src/sdk/networkInterceptor.ts +9 -33
  48. package/src/sdk/utils.ts +0 -5
  49. package/src/types/index.ts +0 -29
@@ -61,6 +61,7 @@ typedef struct {
61
61
  @property(nonatomic, assign) NSInteger generation;
62
62
  @property(nonatomic, strong, nullable) RJViewHierarchyScanResult *scanResult;
63
63
  @property(nonatomic, copy, nullable) NSString *layoutSignature;
64
+ @property(nonatomic, assign) RJCaptureImportance importance;
64
65
 
65
66
  @end
66
67
 
@@ -168,6 +169,8 @@ typedef struct {
168
169
  @property(nonatomic, assign) CFRunLoopObserverRef runLoopObserver;
169
170
  @property(nonatomic, assign) BOOL runLoopCapturePending;
170
171
 
172
+ @property(nonatomic, assign) BOOL isWarmingUp;
173
+
171
174
  @end
172
175
 
173
176
  #pragma mark - Implementation
@@ -300,6 +303,7 @@ typedef struct {
300
303
  _isShuttingDown = NO;
301
304
  _uiReadyForCapture = NO;
302
305
  _framesSinceHierarchy = 0;
306
+ _isWarmingUp = NO;
303
307
 
304
308
  [self applyDefaultConfiguration];
305
309
 
@@ -494,7 +498,38 @@ typedef struct {
494
498
 
495
499
  - (void)appDidBecomeActive:(NSNotification *)notification {
496
500
  self.isInBackground = NO;
497
- RJLogDebug(@"CaptureEngine: App became active - warmup before capture");
501
+
502
+ // DEFENSIVE FIX: Warmup period
503
+ // When returning from background, the view hierarchy and layout may not be
504
+ // stable immediately. This can cause privacy masks to be drawn in the wrong
505
+ // position relative to the content (race condition). We impose a short
506
+ // "warmup" period where we skip capture to allow AutoLayout to settle.
507
+ self.isWarmingUp = YES;
508
+
509
+ // Clear stale caches to force fresh scan
510
+ if (self.lastCapturedPixelBuffer) {
511
+ CVPixelBufferRelease(self.lastCapturedPixelBuffer);
512
+ self.lastCapturedPixelBuffer = NULL;
513
+ }
514
+ self.lastMaskScanResult = nil;
515
+ self.lastSafeMaskScanResult = nil;
516
+
517
+ RJLogDebug(@"CaptureEngine: App became active - starting warmup (0.2s)");
518
+
519
+ __weak typeof(self) weakSelf = self;
520
+ dispatch_after(
521
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)),
522
+ dispatch_get_main_queue(), ^{
523
+ __strong typeof(weakSelf) strongSelf = weakSelf;
524
+ if (!strongSelf)
525
+ return;
526
+
527
+ strongSelf.isWarmingUp = NO;
528
+ RJLogDebug(@"CaptureEngine: Warmup complete - resuming capture");
529
+
530
+ // Trigger an immediate capture check
531
+ [strongSelf captureVideoFrame];
532
+ });
498
533
  }
499
534
 
500
535
  #pragma mark - System Monitoring
@@ -1087,14 +1122,31 @@ typedef struct {
1087
1122
  }
1088
1123
 
1089
1124
  - (void)captureVideoFrame {
1125
+ [self captureVideoFrameWithImportance:RJCaptureImportanceLow reason:@"timer"];
1126
+ }
1127
+
1128
+ - (void)captureVideoFrameWithImportance:(RJCaptureImportance)importance
1129
+ reason:(NSString *)reason {
1090
1130
  if (!self.internalIsRecording || self.isShuttingDown)
1091
1131
  return;
1132
+
1133
+ if (self.isWarmingUp) {
1134
+ return;
1135
+ }
1136
+
1137
+ // Critical events (like navigation) should bypass UI ready checks if
1138
+ // possible, but we still need a valid UI.
1092
1139
  if (!self.uiReadyForCapture) {
1093
1140
  if (self.framesSinceSessionStart % 60 == 0)
1094
1141
  RJLogDebug(@"Skipping capture: UI not ready");
1095
1142
  return;
1096
1143
  }
1097
- if (self.internalPerformanceLevel == RJPerformanceLevelPaused) {
1144
+
1145
+ // Critical events override paused state
1146
+ BOOL isCritical = (importance == RJCaptureImportanceCritical ||
1147
+ importance == RJCaptureImportanceHigh);
1148
+ if (self.internalPerformanceLevel == RJPerformanceLevelPaused &&
1149
+ !isCritical) {
1098
1150
  static NSInteger pauseSkipCount = 0;
1099
1151
  if (++pauseSkipCount % 60 == 0)
1100
1152
  RJLogDebug(@"Skipping capture: Performance Paused");
@@ -1109,12 +1161,19 @@ typedef struct {
1109
1161
 
1110
1162
  RJCapturePendingCapture *pending = [[RJCapturePendingCapture alloc] init];
1111
1163
  pending.wantedAt = now;
1164
+ pending.importance = importance;
1165
+
1112
1166
  NSTimeInterval grace = self.captureHeuristics.captureGraceSeconds;
1113
1167
  if (self.captureHeuristics.animationBlocking ||
1114
1168
  self.captureHeuristics.scrollActive ||
1115
1169
  self.captureHeuristics.keyboardAnimating) {
1116
1170
  grace = MIN(grace, 0.3);
1117
1171
  }
1172
+
1173
+ if (isCritical) {
1174
+ grace = MIN(grace, 0.1);
1175
+ }
1176
+
1118
1177
  pending.deadline = now + grace;
1119
1178
  pending.timestamp = [self currentTimestamp];
1120
1179
  pending.generation = ++self.pendingCaptureGeneration;
@@ -1207,7 +1266,8 @@ typedef struct {
1207
1266
  RJCaptureHeuristicsDecision *decision = [self.captureHeuristics
1208
1267
  decisionForSignature:pending.layoutSignature
1209
1268
  now:now
1210
- hasLastFrame:(self.lastCapturedPixelBuffer != NULL)];
1269
+ hasLastFrame:(self.lastCapturedPixelBuffer != NULL)
1270
+ importance:pending.importance];
1211
1271
 
1212
1272
  if (decision.action == RJCaptureHeuristicsActionRenderNow && !fullScan) {
1213
1273
  RJ_TIME_START_NAMED(viewScan);
@@ -1241,7 +1301,8 @@ typedef struct {
1241
1301
  decision = [self.captureHeuristics
1242
1302
  decisionForSignature:pending.layoutSignature
1243
1303
  now:now
1244
- hasLastFrame:(self.lastCapturedPixelBuffer != NULL)];
1304
+ hasLastFrame:(self.lastCapturedPixelBuffer != NULL)
1305
+ importance:pending.importance];
1245
1306
  }
1246
1307
 
1247
1308
  if (decision.action == RJCaptureHeuristicsActionDefer && fullScan) {
@@ -1342,7 +1403,10 @@ typedef struct {
1342
1403
  }
1343
1404
  self.pendingDefensiveCaptureTime = 0;
1344
1405
  self.lastIntentTime = CACurrentMediaTime();
1345
- [self captureVideoFrame];
1406
+ // Defensive capture triggered by heuristics (e.g.
1407
+ // navigation) is High importance
1408
+ [self captureVideoFrameWithImportance:RJCaptureImportanceHigh
1409
+ reason:reason];
1346
1410
  });
1347
1411
  }
1348
1412
 
@@ -1662,7 +1726,6 @@ typedef struct {
1662
1726
  }
1663
1727
 
1664
1728
  if (self.internalPerformanceLevel == RJPerformanceLevelMinimal) {
1665
- // Minimal mode trades quality for speed
1666
1729
  CGContextSetInterpolationQuality(context, kCGInterpolationNone);
1667
1730
  CGContextSetShouldAntialias(context, false);
1668
1731
  CGContextSetAllowsAntialiasing(context, false);
@@ -1672,17 +1735,13 @@ typedef struct {
1672
1735
  CGContextSetAllowsAntialiasing(context, true);
1673
1736
  }
1674
1737
 
1675
- // Set up context transform (flip for UIKit coordinates)
1676
1738
  CGContextScaleCTM(context, contextScale, -contextScale);
1677
1739
  CGContextTranslateCTM(context, 0, -sizePoints.height);
1678
1740
 
1679
- // Optimization #9: Fast Memset Clear (White = 0xFF)
1680
- // Much faster than CGContextFillRect
1681
1741
  memset(baseAddress, 0xFF, bytesPerRow * height);
1682
1742
 
1683
1743
  UIGraphicsPushContext(context);
1684
1744
 
1685
- // ===== RENDERING: ALWAYS USE drawViewHierarchyInRect =====
1686
1745
  RJ_TIME_START_NAMED(render);
1687
1746
  BOOL didDraw = NO;
1688
1747
  @try {
@@ -1703,10 +1762,6 @@ typedef struct {
1703
1762
  return NULL;
1704
1763
  }
1705
1764
 
1706
- // Recalculate effective scale so consumers (PrivacyMask) know the real
1707
- // mapping Used by caller to pass to applyToPixelBuffer Note: we don't need to
1708
- // return it, caller has it.
1709
-
1710
1765
  return pixelBuffer;
1711
1766
  }
1712
1767
 
@@ -1746,9 +1801,6 @@ typedef struct {
1746
1801
  }
1747
1802
 
1748
1803
  - (NSTimeInterval)currentTimestamp {
1749
- // Always use wall clock time for session timestamps
1750
- // CACurrentMediaTime optimization removed - it causes drift after
1751
- // background periods The ~1ms overhead is acceptable for 1fps capture
1752
1804
  return [[NSDate date] timeIntervalSince1970] * 1000.0;
1753
1805
  }
1754
1806
 
@@ -1760,8 +1812,6 @@ typedef struct {
1760
1812
  endTime:(NSTimeInterval)endTime
1761
1813
  frameCount:(NSInteger)frameCount {
1762
1814
 
1763
- // Ensure we are on our own encoding queue to protect hierarchySnapshots
1764
- // and maintain thread safety (callback comes from VideoEncoder queue)
1765
1815
  dispatch_async(self.encodingQueue, ^{
1766
1816
  RJLogDebug(@"CaptureEngine: videoEncoderDidFinishSegment: %@ (%ld frames, "
1767
1817
  @"%.1fs), sessionId=%@",
@@ -1803,9 +1853,6 @@ typedef struct {
1803
1853
 
1804
1854
  [self uploadCurrentHierarchySnapshots];
1805
1855
 
1806
- // NUCLEAR FIX: Do NOT call startSegmentWithSize here!
1807
- // The encoder's appendFrame method will auto-start a segment with the
1808
- // correct PIXEL dimensions when the next frame is captured.
1809
1856
  if (self.internalIsRecording && !self.isShuttingDown) {
1810
1857
  RJLogDebug(
1811
1858
  @"CaptureEngine: Segment finished, auto-start new on next frame");
@@ -1843,10 +1890,8 @@ typedef struct {
1843
1890
 
1844
1891
  RJLogInfo(@"CaptureEngine: Pausing video capture (sync=%d)", synchronous);
1845
1892
 
1846
- // Reset capture-in-progress flag immediately to prevent stuck state
1847
1893
  self.captureInProgress = NO;
1848
1894
 
1849
- // Invalidate timer synchronously if in sync mode
1850
1895
  if (synchronous) {
1851
1896
  [self teardownDisplayLink];
1852
1897
  } else {
@@ -1856,7 +1901,7 @@ typedef struct {
1856
1901
  }
1857
1902
 
1858
1903
  if (self.internalVideoEncoder) {
1859
- self.internalIsRecording = NO; // prevent any new frames from being enqueued
1904
+ self.internalIsRecording = NO;
1860
1905
 
1861
1906
  if (synchronous) {
1862
1907
  void (^finishSync)(void) = ^{
@@ -1895,19 +1940,15 @@ typedef struct {
1895
1940
  return;
1896
1941
  }
1897
1942
 
1898
- // Set recording back to YES to allow captureVideoFrame to proceed
1899
1943
  self.internalIsRecording = YES;
1900
1944
 
1901
1945
  RJLogInfo(@"CaptureEngine: Resuming video capture");
1902
1946
 
1903
- // Reset capture state to ensure clean resumption
1904
- // These flags may have been left in an inconsistent state when going to
1905
- // background
1906
1947
  self.captureInProgress = NO;
1907
- self.lastIntentTime = 0; // Allow immediate capture on resume
1948
+ self.lastIntentTime = 0;
1908
1949
 
1909
1950
  self.internalPerformanceLevel =
1910
- RJPerformanceLevelNormal; // Reset perf level on resume
1951
+ RJPerformanceLevelNormal;
1911
1952
 
1912
1953
  self.pendingCapture = nil;
1913
1954
  self.pendingCaptureGeneration = 0;
@@ -1926,7 +1967,6 @@ typedef struct {
1926
1967
  if (window && self.internalVideoEncoder) {
1927
1968
  RJLogInfo(@"CaptureEngine: Resuming capture...");
1928
1969
 
1929
- // Use the optimized Display Link
1930
1970
  [self setupDisplayLink];
1931
1971
 
1932
1972
  } else {
@@ -1943,14 +1983,12 @@ typedef struct {
1943
1983
  if (!self.internalIsRecording)
1944
1984
  return;
1945
1985
 
1946
- // Force update if screen changed
1947
1986
  if (![screenName isEqualToString:self.currentScreenName]) {
1948
1987
  NSTimeInterval now = CACurrentMediaTime();
1949
1988
  self.currentScreenName = screenName;
1950
1989
  RJLogDebug(@"Navigation to screen: %@ (forcing layout refresh)",
1951
1990
  screenName);
1952
1991
 
1953
- // Force layout change detection on next frame
1954
1992
  [self.captureHeuristics invalidateSignature];
1955
1993
  [self.captureHeuristics recordNavigationEventAtTime:now];
1956
1994
  self.lastSerializedSignature = nil;
@@ -10,6 +10,7 @@
10
10
  #import <Foundation/Foundation.h>
11
11
  #import <UIKit/UIKit.h>
12
12
 
13
+ #import "../Core/RJTypes.h"
13
14
  #import "RJViewHierarchyScanner.h"
14
15
 
15
16
  NS_ASSUME_NONNULL_BEGIN
@@ -59,7 +60,7 @@ typedef NS_ENUM(NSInteger, RJCaptureHeuristicsReason) {
59
60
  - (void)recordMapInteractionAtTime:(NSTimeInterval)time;
60
61
  - (void)recordNavigationEventAtTime:(NSTimeInterval)time;
61
62
  - (void)recordRenderedSignature:(nullable NSString *)signature
62
- atTime:(NSTimeInterval)time;
63
+ atTime:(NSTimeInterval)time;
63
64
 
64
65
  - (void)updateWithScanResult:(RJViewHierarchyScanResult *)scanResult
65
66
  window:(UIWindow *)window
@@ -68,10 +69,11 @@ typedef NS_ENUM(NSInteger, RJCaptureHeuristicsReason) {
68
69
  - (void)updateWithStabilityProbeForWindow:(UIWindow *)window
69
70
  now:(NSTimeInterval)now;
70
71
 
71
- - (RJCaptureHeuristicsDecision *)decisionForSignature:
72
- (nullable NSString *)signature
73
- now:(NSTimeInterval)now
74
- hasLastFrame:(BOOL)hasLastFrame;
72
+ - (RJCaptureHeuristicsDecision *)
73
+ decisionForSignature:(nullable NSString *)signature
74
+ now:(NSTimeInterval)now
75
+ hasLastFrame:(BOOL)hasLastFrame
76
+ importance:(RJCaptureImportance)importance;
75
77
 
76
78
  + (NSString *)stringForReason:(RJCaptureHeuristicsReason)reason;
77
79