@rejourneyco/react-native 1.0.0

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 (152) hide show
  1. package/android/build.gradle.kts +135 -0
  2. package/android/consumer-rules.pro +10 -0
  3. package/android/proguard-rules.pro +1 -0
  4. package/android/src/main/AndroidManifest.xml +15 -0
  5. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +2981 -0
  6. package/android/src/main/java/com/rejourney/capture/ANRHandler.kt +206 -0
  7. package/android/src/main/java/com/rejourney/capture/ActivityTracker.kt +98 -0
  8. package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +1553 -0
  9. package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +375 -0
  10. package/android/src/main/java/com/rejourney/capture/CrashHandler.kt +153 -0
  11. package/android/src/main/java/com/rejourney/capture/MotionEvent.kt +215 -0
  12. package/android/src/main/java/com/rejourney/capture/SegmentUploader.kt +512 -0
  13. package/android/src/main/java/com/rejourney/capture/VideoEncoder.kt +773 -0
  14. package/android/src/main/java/com/rejourney/capture/ViewHierarchyScanner.kt +633 -0
  15. package/android/src/main/java/com/rejourney/capture/ViewSerializer.kt +286 -0
  16. package/android/src/main/java/com/rejourney/core/Constants.kt +117 -0
  17. package/android/src/main/java/com/rejourney/core/Logger.kt +93 -0
  18. package/android/src/main/java/com/rejourney/core/Types.kt +124 -0
  19. package/android/src/main/java/com/rejourney/lifecycle/SessionLifecycleService.kt +162 -0
  20. package/android/src/main/java/com/rejourney/network/DeviceAuthManager.kt +747 -0
  21. package/android/src/main/java/com/rejourney/network/HttpClientProvider.kt +16 -0
  22. package/android/src/main/java/com/rejourney/network/NetworkMonitor.kt +272 -0
  23. package/android/src/main/java/com/rejourney/network/UploadManager.kt +1363 -0
  24. package/android/src/main/java/com/rejourney/network/UploadWorker.kt +492 -0
  25. package/android/src/main/java/com/rejourney/privacy/PrivacyMask.kt +645 -0
  26. package/android/src/main/java/com/rejourney/touch/GestureClassifier.kt +233 -0
  27. package/android/src/main/java/com/rejourney/touch/KeyboardTracker.kt +158 -0
  28. package/android/src/main/java/com/rejourney/touch/TextInputTracker.kt +181 -0
  29. package/android/src/main/java/com/rejourney/touch/TouchInterceptor.kt +591 -0
  30. package/android/src/main/java/com/rejourney/utils/EventBuffer.kt +284 -0
  31. package/android/src/main/java/com/rejourney/utils/OEMDetector.kt +154 -0
  32. package/android/src/main/java/com/rejourney/utils/PerfTiming.kt +235 -0
  33. package/android/src/main/java/com/rejourney/utils/Telemetry.kt +297 -0
  34. package/android/src/main/java/com/rejourney/utils/WindowUtils.kt +84 -0
  35. package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +187 -0
  36. package/android/src/newarch/java/com/rejourney/RejourneyPackage.kt +40 -0
  37. package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +218 -0
  38. package/android/src/oldarch/java/com/rejourney/RejourneyPackage.kt +23 -0
  39. package/ios/Capture/RJANRHandler.h +42 -0
  40. package/ios/Capture/RJANRHandler.m +328 -0
  41. package/ios/Capture/RJCaptureEngine.h +275 -0
  42. package/ios/Capture/RJCaptureEngine.m +2062 -0
  43. package/ios/Capture/RJCaptureHeuristics.h +80 -0
  44. package/ios/Capture/RJCaptureHeuristics.m +903 -0
  45. package/ios/Capture/RJCrashHandler.h +46 -0
  46. package/ios/Capture/RJCrashHandler.m +313 -0
  47. package/ios/Capture/RJMotionEvent.h +183 -0
  48. package/ios/Capture/RJMotionEvent.m +183 -0
  49. package/ios/Capture/RJPerformanceManager.h +100 -0
  50. package/ios/Capture/RJPerformanceManager.m +373 -0
  51. package/ios/Capture/RJPixelBufferDownscaler.h +42 -0
  52. package/ios/Capture/RJPixelBufferDownscaler.m +85 -0
  53. package/ios/Capture/RJSegmentUploader.h +146 -0
  54. package/ios/Capture/RJSegmentUploader.m +778 -0
  55. package/ios/Capture/RJVideoEncoder.h +247 -0
  56. package/ios/Capture/RJVideoEncoder.m +1036 -0
  57. package/ios/Capture/RJViewControllerTracker.h +73 -0
  58. package/ios/Capture/RJViewControllerTracker.m +508 -0
  59. package/ios/Capture/RJViewHierarchyScanner.h +215 -0
  60. package/ios/Capture/RJViewHierarchyScanner.m +1464 -0
  61. package/ios/Capture/RJViewSerializer.h +119 -0
  62. package/ios/Capture/RJViewSerializer.m +498 -0
  63. package/ios/Core/RJConstants.h +124 -0
  64. package/ios/Core/RJConstants.m +88 -0
  65. package/ios/Core/RJLifecycleManager.h +85 -0
  66. package/ios/Core/RJLifecycleManager.m +308 -0
  67. package/ios/Core/RJLogger.h +61 -0
  68. package/ios/Core/RJLogger.m +211 -0
  69. package/ios/Core/RJTypes.h +176 -0
  70. package/ios/Core/RJTypes.m +66 -0
  71. package/ios/Core/Rejourney.h +64 -0
  72. package/ios/Core/Rejourney.mm +2495 -0
  73. package/ios/Network/RJDeviceAuthManager.h +94 -0
  74. package/ios/Network/RJDeviceAuthManager.m +967 -0
  75. package/ios/Network/RJNetworkMonitor.h +68 -0
  76. package/ios/Network/RJNetworkMonitor.m +267 -0
  77. package/ios/Network/RJRetryManager.h +73 -0
  78. package/ios/Network/RJRetryManager.m +325 -0
  79. package/ios/Network/RJUploadManager.h +267 -0
  80. package/ios/Network/RJUploadManager.m +2296 -0
  81. package/ios/Privacy/RJPrivacyMask.h +163 -0
  82. package/ios/Privacy/RJPrivacyMask.m +922 -0
  83. package/ios/Rejourney.h +63 -0
  84. package/ios/Touch/RJGestureClassifier.h +130 -0
  85. package/ios/Touch/RJGestureClassifier.m +333 -0
  86. package/ios/Touch/RJTouchInterceptor.h +169 -0
  87. package/ios/Touch/RJTouchInterceptor.m +772 -0
  88. package/ios/Utils/RJEventBuffer.h +112 -0
  89. package/ios/Utils/RJEventBuffer.m +358 -0
  90. package/ios/Utils/RJGzipUtils.h +33 -0
  91. package/ios/Utils/RJGzipUtils.m +89 -0
  92. package/ios/Utils/RJKeychainManager.h +48 -0
  93. package/ios/Utils/RJKeychainManager.m +111 -0
  94. package/ios/Utils/RJPerfTiming.h +209 -0
  95. package/ios/Utils/RJPerfTiming.m +264 -0
  96. package/ios/Utils/RJTelemetry.h +92 -0
  97. package/ios/Utils/RJTelemetry.m +320 -0
  98. package/ios/Utils/RJWindowUtils.h +66 -0
  99. package/ios/Utils/RJWindowUtils.m +133 -0
  100. package/lib/commonjs/NativeRejourney.js +40 -0
  101. package/lib/commonjs/components/Mask.js +79 -0
  102. package/lib/commonjs/index.js +1381 -0
  103. package/lib/commonjs/sdk/autoTracking.js +1259 -0
  104. package/lib/commonjs/sdk/constants.js +151 -0
  105. package/lib/commonjs/sdk/errorTracking.js +199 -0
  106. package/lib/commonjs/sdk/index.js +50 -0
  107. package/lib/commonjs/sdk/metricsTracking.js +204 -0
  108. package/lib/commonjs/sdk/navigation.js +151 -0
  109. package/lib/commonjs/sdk/networkInterceptor.js +412 -0
  110. package/lib/commonjs/sdk/utils.js +363 -0
  111. package/lib/commonjs/types/expo-router.d.js +2 -0
  112. package/lib/commonjs/types/index.js +2 -0
  113. package/lib/module/NativeRejourney.js +38 -0
  114. package/lib/module/components/Mask.js +72 -0
  115. package/lib/module/index.js +1284 -0
  116. package/lib/module/sdk/autoTracking.js +1233 -0
  117. package/lib/module/sdk/constants.js +145 -0
  118. package/lib/module/sdk/errorTracking.js +189 -0
  119. package/lib/module/sdk/index.js +12 -0
  120. package/lib/module/sdk/metricsTracking.js +187 -0
  121. package/lib/module/sdk/navigation.js +143 -0
  122. package/lib/module/sdk/networkInterceptor.js +401 -0
  123. package/lib/module/sdk/utils.js +342 -0
  124. package/lib/module/types/expo-router.d.js +2 -0
  125. package/lib/module/types/index.js +2 -0
  126. package/lib/typescript/NativeRejourney.d.ts +147 -0
  127. package/lib/typescript/components/Mask.d.ts +39 -0
  128. package/lib/typescript/index.d.ts +117 -0
  129. package/lib/typescript/sdk/autoTracking.d.ts +204 -0
  130. package/lib/typescript/sdk/constants.d.ts +120 -0
  131. package/lib/typescript/sdk/errorTracking.d.ts +32 -0
  132. package/lib/typescript/sdk/index.d.ts +9 -0
  133. package/lib/typescript/sdk/metricsTracking.d.ts +58 -0
  134. package/lib/typescript/sdk/navigation.d.ts +33 -0
  135. package/lib/typescript/sdk/networkInterceptor.d.ts +47 -0
  136. package/lib/typescript/sdk/utils.d.ts +148 -0
  137. package/lib/typescript/types/index.d.ts +624 -0
  138. package/package.json +102 -0
  139. package/rejourney.podspec +21 -0
  140. package/src/NativeRejourney.ts +165 -0
  141. package/src/components/Mask.tsx +80 -0
  142. package/src/index.ts +1459 -0
  143. package/src/sdk/autoTracking.ts +1373 -0
  144. package/src/sdk/constants.ts +134 -0
  145. package/src/sdk/errorTracking.ts +231 -0
  146. package/src/sdk/index.ts +11 -0
  147. package/src/sdk/metricsTracking.ts +232 -0
  148. package/src/sdk/navigation.ts +157 -0
  149. package/src/sdk/networkInterceptor.ts +440 -0
  150. package/src/sdk/utils.ts +369 -0
  151. package/src/types/expo-router.d.ts +7 -0
  152. package/src/types/index.ts +739 -0
@@ -0,0 +1,320 @@
1
+ //
2
+ // RJTelemetry.m
3
+ // Rejourney SDK Telemetry
4
+ //
5
+ // Provides observability metrics for SDK health monitoring.
6
+ //
7
+
8
+ #import "RJTelemetry.h"
9
+ #import "../Core/RJLogger.h"
10
+
11
+ static void *kRJTelemetryQueueKey = &kRJTelemetryQueueKey;
12
+
13
+ #pragma mark - RJTelemetryMetrics Implementation
14
+
15
+ @interface RJTelemetryMetrics ()
16
+
17
+ @property(nonatomic, assign, readwrite) NSInteger uploadSuccessCount;
18
+ @property(nonatomic, assign, readwrite) NSInteger uploadFailureCount;
19
+ @property(nonatomic, assign, readwrite) NSInteger retryAttemptCount;
20
+ @property(nonatomic, assign, readwrite) NSInteger circuitBreakerOpenCount;
21
+ @property(nonatomic, assign, readwrite) NSInteger memoryEvictionCount;
22
+ @property(nonatomic, assign, readwrite) NSInteger offlinePersistCount;
23
+ @property(nonatomic, assign, readwrite) NSInteger sessionStartCount;
24
+ @property(nonatomic, assign, readwrite) NSInteger crashCount;
25
+ @property(nonatomic, assign, readwrite) NSInteger anrCount;
26
+ @property(nonatomic, assign, readwrite) double uploadSuccessRate;
27
+ @property(nonatomic, assign, readwrite) NSTimeInterval avgUploadDurationMs;
28
+ @property(nonatomic, assign, readwrite) NSInteger currentQueueDepth;
29
+ @property(nonatomic, strong, readwrite) NSDate *lastUploadTime;
30
+ @property(nonatomic, strong, readwrite) NSDate *lastRetryTime;
31
+
32
+ @end
33
+
34
+ @implementation RJTelemetryMetrics
35
+ @end
36
+
37
+ #pragma mark - RJTelemetry Implementation
38
+
39
+ @interface RJTelemetry ()
40
+
41
+ @property(nonatomic, strong) dispatch_queue_t metricsQueue;
42
+ @property(nonatomic, strong) RJTelemetryMetrics *metrics;
43
+ @property(nonatomic, assign) NSInteger totalUploadCount;
44
+ @property(nonatomic, assign) NSTimeInterval totalUploadDurationMs;
45
+ @property(nonatomic, assign) NSUInteger totalBytesUploaded;
46
+ @property(nonatomic, assign) NSUInteger totalBytesEvicted;
47
+
48
+ @end
49
+
50
+ @implementation RJTelemetry
51
+
52
+ + (instancetype)sharedInstance {
53
+ static RJTelemetry *instance = nil;
54
+ static dispatch_once_t onceToken;
55
+ dispatch_once(&onceToken, ^{
56
+ instance = [[RJTelemetry alloc] init];
57
+ });
58
+ return instance;
59
+ }
60
+
61
+ - (instancetype)init {
62
+ self = [super init];
63
+ if (self) {
64
+ _metricsQueue =
65
+ dispatch_queue_create("com.rejourney.telemetry", DISPATCH_QUEUE_SERIAL);
66
+ dispatch_queue_set_specific(_metricsQueue, kRJTelemetryQueueKey,
67
+ kRJTelemetryQueueKey, NULL);
68
+ _metrics = [[RJTelemetryMetrics alloc] init];
69
+ [self resetMetrics];
70
+ }
71
+ return self;
72
+ }
73
+
74
+ - (void)recordEvent:(RJTelemetryEventType)eventType {
75
+ [self recordEvent:eventType metadata:nil];
76
+ }
77
+
78
+ - (void)recordEvent:(RJTelemetryEventType)eventType
79
+ metadata:(nullable NSDictionary<NSString *, id> *)metadata {
80
+ dispatch_async(self.metricsQueue, ^{
81
+ switch (eventType) {
82
+ case RJTelemetryEventUploadSuccess:
83
+ self.metrics.uploadSuccessCount++;
84
+ self.metrics.lastUploadTime = [NSDate date];
85
+ break;
86
+
87
+ case RJTelemetryEventUploadFailure:
88
+ self.metrics.uploadFailureCount++;
89
+ break;
90
+
91
+ case RJTelemetryEventRetryAttempt:
92
+ self.metrics.retryAttemptCount++;
93
+ self.metrics.lastRetryTime = [NSDate date];
94
+ break;
95
+
96
+ case RJTelemetryEventCircuitBreakerOpen:
97
+ self.metrics.circuitBreakerOpenCount++;
98
+ RJLogWarning(@"[Telemetry] Circuit breaker opened (total: %ld)",
99
+ (long)self.metrics.circuitBreakerOpenCount);
100
+ break;
101
+
102
+ case RJTelemetryEventCircuitBreakerClose:
103
+ RJLogDebug(@"[Telemetry] Circuit breaker closed");
104
+ break;
105
+
106
+ case RJTelemetryEventMemoryPressureEviction:
107
+ self.metrics.memoryEvictionCount++;
108
+ break;
109
+
110
+ case RJTelemetryEventOfflineQueuePersist:
111
+ self.metrics.offlinePersistCount++;
112
+ RJLogDebug(@"[Telemetry] Offline queue persisted (total: %ld)",
113
+ (long)self.metrics.offlinePersistCount);
114
+ break;
115
+
116
+ case RJTelemetryEventOfflineQueueRestore:
117
+ RJLogDebug(@"[Telemetry] Offline queue restored");
118
+ break;
119
+
120
+ case RJTelemetryEventSessionStart:
121
+ self.metrics.sessionStartCount++;
122
+ break;
123
+
124
+ case RJTelemetryEventSessionEnd:
125
+
126
+ [self logCurrentMetricsInternal];
127
+ break;
128
+
129
+ case RJTelemetryEventCrashDetected:
130
+ self.metrics.crashCount++;
131
+ RJLogWarning(@"[Telemetry] Crash detected (total: %ld)",
132
+ (long)self.metrics.crashCount);
133
+ break;
134
+
135
+ case RJTelemetryEventTokenRefresh:
136
+ RJLogDebug(@"[Telemetry] Token refresh triggered");
137
+ break;
138
+ }
139
+
140
+
141
+ [self updateSuccessRate];
142
+ });
143
+ }
144
+
145
+ - (void)recordUploadDuration:(NSTimeInterval)durationMs
146
+ success:(BOOL)success
147
+ byteCount:(NSUInteger)bytes {
148
+ dispatch_async(self.metricsQueue, ^{
149
+ self.totalUploadCount++;
150
+ self.totalUploadDurationMs += durationMs;
151
+ self.metrics.avgUploadDurationMs =
152
+ self.totalUploadDurationMs / (double)self.totalUploadCount;
153
+
154
+ if (success) {
155
+ self.totalBytesUploaded += bytes;
156
+ self.metrics.uploadSuccessCount++;
157
+ self.metrics.lastUploadTime = [NSDate date];
158
+ } else {
159
+ self.metrics.uploadFailureCount++;
160
+ }
161
+
162
+ [self updateSuccessRate];
163
+ });
164
+ }
165
+
166
+ - (void)recordFrameEviction:(NSUInteger)bytesEvicted
167
+ frameCount:(NSInteger)count {
168
+ dispatch_async(self.metricsQueue, ^{
169
+ self.metrics.memoryEvictionCount += count;
170
+ self.totalBytesEvicted += bytesEvicted;
171
+
172
+ RJLogWarning(
173
+ @"[Telemetry] Memory eviction: %ld frames, %.1f KB total evicted",
174
+ (long)count, self.totalBytesEvicted / 1024.0);
175
+ });
176
+ }
177
+
178
+ - (void)recordQueueDepth:(NSInteger)depth {
179
+ dispatch_async(self.metricsQueue, ^{
180
+ self.metrics.currentQueueDepth = depth;
181
+ });
182
+ }
183
+
184
+ - (void)recordANR {
185
+ dispatch_async(self.metricsQueue, ^{
186
+ self.metrics.anrCount++;
187
+ RJLogWarning(@"[Telemetry] ANR detected (total: %ld)",
188
+ (long)self.metrics.anrCount);
189
+ });
190
+ }
191
+
192
+ - (void)updateSuccessRate {
193
+ NSInteger total =
194
+ self.metrics.uploadSuccessCount + self.metrics.uploadFailureCount;
195
+ if (total > 0) {
196
+ self.metrics.uploadSuccessRate =
197
+ (double)self.metrics.uploadSuccessCount / (double)total;
198
+ } else {
199
+ self.metrics.uploadSuccessRate = 1.0;
200
+ }
201
+ }
202
+
203
+ - (RJTelemetryMetrics *)currentMetrics {
204
+ RJTelemetryMetrics *snapshot = [[RJTelemetryMetrics alloc] init];
205
+ void (^capture)(void) = ^{
206
+ snapshot.uploadSuccessCount = self.metrics.uploadSuccessCount;
207
+ snapshot.uploadFailureCount = self.metrics.uploadFailureCount;
208
+ snapshot.retryAttemptCount = self.metrics.retryAttemptCount;
209
+ snapshot.circuitBreakerOpenCount = self.metrics.circuitBreakerOpenCount;
210
+ snapshot.memoryEvictionCount = self.metrics.memoryEvictionCount;
211
+ snapshot.offlinePersistCount = self.metrics.offlinePersistCount;
212
+ snapshot.sessionStartCount = self.metrics.sessionStartCount;
213
+ snapshot.crashCount = self.metrics.crashCount;
214
+ snapshot.anrCount = self.metrics.anrCount;
215
+ snapshot.uploadSuccessRate = self.metrics.uploadSuccessRate;
216
+ snapshot.avgUploadDurationMs = self.metrics.avgUploadDurationMs;
217
+ snapshot.currentQueueDepth = self.metrics.currentQueueDepth;
218
+ snapshot.lastUploadTime = self.metrics.lastUploadTime;
219
+ snapshot.lastRetryTime = self.metrics.lastRetryTime;
220
+ };
221
+
222
+ if (dispatch_get_specific(kRJTelemetryQueueKey)) {
223
+ capture();
224
+ } else {
225
+ dispatch_sync(self.metricsQueue, capture);
226
+ }
227
+
228
+ return snapshot;
229
+ }
230
+
231
+ - (NSDictionary<NSString *, id> *)metricsAsDictionary {
232
+ RJTelemetryMetrics *m = [self currentMetrics];
233
+ __block NSUInteger totalBytesUploaded = 0;
234
+ __block NSUInteger totalBytesEvicted = 0;
235
+ void (^captureTotals)(void) = ^{
236
+ totalBytesUploaded = self.totalBytesUploaded;
237
+ totalBytesEvicted = self.totalBytesEvicted;
238
+ };
239
+ if (dispatch_get_specific(kRJTelemetryQueueKey)) {
240
+ captureTotals();
241
+ } else {
242
+ dispatch_sync(self.metricsQueue, captureTotals);
243
+ }
244
+ return @{
245
+ @"uploadSuccessCount" : @(m.uploadSuccessCount),
246
+ @"uploadFailureCount" : @(m.uploadFailureCount),
247
+ @"retryAttemptCount" : @(m.retryAttemptCount),
248
+ @"circuitBreakerOpenCount" : @(m.circuitBreakerOpenCount),
249
+ @"memoryEvictionCount" : @(m.memoryEvictionCount),
250
+ @"offlinePersistCount" : @(m.offlinePersistCount),
251
+ @"sessionStartCount" : @(m.sessionStartCount),
252
+ @"crashCount" : @(m.crashCount),
253
+ @"anrCount" : @(m.anrCount),
254
+ @"uploadSuccessRate" : @(m.uploadSuccessRate),
255
+ @"avgUploadDurationMs" : @(m.avgUploadDurationMs),
256
+ @"currentQueueDepth" : @(m.currentQueueDepth),
257
+ @"lastUploadTime" : m.lastUploadTime
258
+ ? @([m.lastUploadTime timeIntervalSince1970] * 1000)
259
+ : [NSNull null],
260
+ @"lastRetryTime" : m.lastRetryTime
261
+ ? @([m.lastRetryTime timeIntervalSince1970] * 1000)
262
+ : [NSNull null],
263
+ @"totalBytesUploaded" : @(totalBytesUploaded),
264
+ @"totalBytesEvicted" : @(totalBytesEvicted),
265
+ };
266
+ }
267
+
268
+ - (void)resetMetrics {
269
+ dispatch_async(self.metricsQueue, ^{
270
+ self.metrics.uploadSuccessCount = 0;
271
+ self.metrics.uploadFailureCount = 0;
272
+ self.metrics.retryAttemptCount = 0;
273
+ self.metrics.circuitBreakerOpenCount = 0;
274
+ self.metrics.memoryEvictionCount = 0;
275
+ self.metrics.offlinePersistCount = 0;
276
+ self.metrics.sessionStartCount = 0;
277
+ self.metrics.crashCount = 0;
278
+ self.metrics.anrCount = 0;
279
+ self.metrics.uploadSuccessRate = 1.0;
280
+ self.metrics.avgUploadDurationMs = 0;
281
+ self.metrics.currentQueueDepth = 0;
282
+ self.metrics.lastUploadTime = nil;
283
+ self.metrics.lastRetryTime = nil;
284
+ self.totalUploadCount = 0;
285
+ self.totalUploadDurationMs = 0;
286
+ self.totalBytesUploaded = 0;
287
+ self.totalBytesEvicted = 0;
288
+ });
289
+ }
290
+
291
+ - (void)logCurrentMetrics {
292
+ #ifdef DEBUG
293
+ dispatch_async(self.metricsQueue, ^{
294
+ [self logCurrentMetricsInternal];
295
+ });
296
+ #endif
297
+ }
298
+
299
+ - (void)logCurrentMetricsInternal {
300
+ RJLogDebug(@"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
301
+ RJLogDebug(@" SDK Telemetry Summary");
302
+ RJLogDebug(@" Uploads: %ld success, %ld failed (%.1f%% success rate)",
303
+ (long)self.metrics.uploadSuccessCount,
304
+ (long)self.metrics.uploadFailureCount,
305
+ self.metrics.uploadSuccessRate * 100);
306
+ RJLogDebug(@" Avg upload latency: %.1f ms",
307
+ self.metrics.avgUploadDurationMs);
308
+ RJLogDebug(@" Retries: %ld attempts", (long)self.metrics.retryAttemptCount);
309
+ RJLogDebug(@" Circuit breaker opens: %ld",
310
+ (long)self.metrics.circuitBreakerOpenCount);
311
+ RJLogDebug(@" Memory evictions: %ld frames (%.1f KB)",
312
+ (long)self.metrics.memoryEvictionCount,
313
+ self.totalBytesEvicted / 1024.0);
314
+ RJLogDebug(@" Offline persists: %ld",
315
+ (long)self.metrics.offlinePersistCount);
316
+ RJLogDebug(@" Data uploaded: %.1f KB", self.totalBytesUploaded / 1024.0);
317
+ RJLogDebug(@"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
318
+ }
319
+
320
+ @end
@@ -0,0 +1,66 @@
1
+ //
2
+ // RJWindowUtils.h
3
+ // Rejourney
4
+ //
5
+ // Window and view utility functions.
6
+ //
7
+ // Licensed under the Apache License, Version 2.0 (the "License");
8
+ // you may not use this file except in compliance with the License.
9
+ // You may obtain a copy of the License at
10
+ //
11
+ // http://www.apache.org/licenses/LICENSE-2.0
12
+ //
13
+ // Unless required by applicable law or agreed to in writing, software
14
+ // distributed under the License is distributed on an "AS IS" BASIS,
15
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ // See the License for the specific language governing permissions and
17
+ // limitations under the License.
18
+ //
19
+ // Copyright (c) 2026 Rejourney
20
+ //
21
+
22
+ #import <Foundation/Foundation.h>
23
+ #import <UIKit/UIKit.h>
24
+
25
+ NS_ASSUME_NONNULL_BEGIN
26
+
27
+ /**
28
+ * Utility class for window and view operations.
29
+ */
30
+ @interface RJWindowUtils : NSObject
31
+
32
+ /**
33
+ * Returns the current key window.
34
+ * Handles iOS 13+ window scene architecture.
35
+ *
36
+ * @return The key window, or nil if none is available.
37
+ */
38
+ + (nullable UIWindow *)keyWindow;
39
+
40
+ /**
41
+ * Finds the accessibility label for a view or its ancestors.
42
+ *
43
+ * @param view The view to start searching from.
44
+ * @return The first accessibility label found, or nil.
45
+ */
46
+ + (nullable NSString *)accessibilityLabelForView:(nullable UIView *)view;
47
+
48
+ /**
49
+ * Generates a unique session ID.
50
+ *
51
+ * Format: session_{timestamp}_{random_hex}
52
+ *
53
+ * @return A unique session identifier.
54
+ */
55
+ + (NSString *)generateSessionId;
56
+
57
+ /**
58
+ * Returns the current timestamp in milliseconds.
59
+ *
60
+ * @return Current time as milliseconds since Unix epoch.
61
+ */
62
+ + (NSTimeInterval)currentTimestampMillis;
63
+
64
+ @end
65
+
66
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,133 @@
1
+ //
2
+ // RJWindowUtils.m
3
+ // Rejourney
4
+ //
5
+ // Window and view utility functions implementation.
6
+ //
7
+ // Licensed under the Apache License, Version 2.0 (the "License");
8
+ // you may not use this file except in compliance with the License.
9
+ // You may obtain a copy of the License at
10
+ //
11
+ // http://www.apache.org/licenses/LICENSE-2.0
12
+ //
13
+ // Unless required by applicable law or agreed to in writing, software
14
+ // distributed under the License is distributed on an "AS IS" BASIS,
15
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ // See the License for the specific language governing permissions and
17
+ // limitations under the License.
18
+ //
19
+ // Copyright (c) 2026 Rejourney
20
+ //
21
+
22
+ #import "RJWindowUtils.h"
23
+
24
+ @implementation RJWindowUtils
25
+
26
+ + (UIWindow *)keyWindow {
27
+ if (@available(iOS 13.0, *)) {
28
+ // IMPORTANT:
29
+ // "Key window" can temporarily become a system window such as:
30
+ // - UITextEffectsWindow
31
+ // - UIRemoteKeyboardWindow / UIInputWindowController-hosted windows
32
+ // Capturing those can trigger keyboard autolayout warnings/spikes even if the
33
+ // user never focused a text field. Prefer a normal-level app window.
34
+ //
35
+ // ALSO IMPORTANT:
36
+ // Some frameworks (including React Native/Expo modals/overlays) may present
37
+ // content in a separate UIWindow with a windowLevel > UIWindowLevelNormal.
38
+ // If we only consider "normal" level, we may capture the wrong window and
39
+ // miss TextInputs entirely (privacy masking failure).
40
+ //
41
+ // We therefore prefer the TOP-MOST visible non-system window, while still
42
+ // excluding keyboard/text-effects windows.
43
+
44
+ UIWindow *bestKeyApp = nil;
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
+ }
92
+
93
+ if (bestKeyApp) {
94
+ return bestKeyApp;
95
+ }
96
+ if (bestApp) {
97
+ return bestApp;
98
+ }
99
+ if (anyKey) {
100
+ return anyKey;
101
+ }
102
+ } else {
103
+ #pragma clang diagnostic push
104
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
105
+ return [UIApplication sharedApplication].keyWindow;
106
+ #pragma clang diagnostic pop
107
+ }
108
+ return nil;
109
+ }
110
+
111
+ + (NSString *)accessibilityLabelForView:(UIView *)view {
112
+ UIView *current = view;
113
+ while (current) {
114
+ if (current.accessibilityLabel.length > 0) {
115
+ return current.accessibilityLabel;
116
+ }
117
+ current = current.superview;
118
+ }
119
+ return nil;
120
+ }
121
+
122
+ + (NSString *)generateSessionId {
123
+ NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
124
+ NSString *timestampStr = [NSString stringWithFormat:@"%.0f", timestamp * 1000];
125
+ NSString *randomHex = [NSString stringWithFormat:@"%08X", arc4random()];
126
+ return [NSString stringWithFormat:@"session_%@_%@", timestampStr, randomHex];
127
+ }
128
+
129
+ + (NSTimeInterval)currentTimestampMillis {
130
+ return [[NSDate date] timeIntervalSince1970] * 1000;
131
+ }
132
+
133
+ @end
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _reactNative = require("react-native");
8
+ /**
9
+ * TurboModule spec for Rejourney SDK
10
+ *
11
+ * This file defines the native module interface for React Native's New Architecture.
12
+ * It follows the official React Native TurboModules pattern for Codegen compatibility.
13
+ *
14
+ * IMPORTANT: This spec file is used by Codegen to generate native bindings.
15
+ * The default export MUST be a direct TurboModuleRegistry.get() call.
16
+ *
17
+ * @see https://reactnative.dev/docs/turbo-native-modules-introduction
18
+ */
19
+ /**
20
+ * SDK telemetry metrics for observability
21
+ */
22
+ /**
23
+ * Native Rejourney module specification for TurboModules (New Architecture)
24
+ *
25
+ * This interface defines all methods exposed by the native module.
26
+ * Codegen uses this to generate:
27
+ * - iOS: RejourneySpec.h (protocol) and RejourneySpec-generated.mm (JSI bindings)
28
+ * - Android: NativeRejourneySpec.java (interface)
29
+ */
30
+ /**
31
+ * Default export for Codegen.
32
+ *
33
+ * CRITICAL: This MUST be a direct TurboModuleRegistry.get() call.
34
+ * Codegen parses this file statically and requires this exact pattern.
35
+ *
36
+ * Using getEnforcing() would throw if module not found.
37
+ * Using get() returns null, which is safer during development/testing.
38
+ */
39
+ var _default = exports.default = _reactNative.TurboModuleRegistry.get('Rejourney');
40
+ //# sourceMappingURL=NativeRejourney.js.map
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.Mask = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /**
10
+ * Mask Component
11
+ *
12
+ * Wrapper component to mask sensitive content in session replays.
13
+ * All children wrapped in this component will be obscured in recordings.
14
+ *
15
+ * IMPORTANT: This file uses lazy loading to avoid "PlatformConstants could not be found"
16
+ * errors on React Native 0.81+ with New Architecture (Bridgeless).
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * import { Mask } from 'rejourney';
21
+ *
22
+ * // Mask sensitive user ID
23
+ * <Mask>
24
+ * <Text>User ID: {user.id}</Text>
25
+ * </Mask>
26
+ *
27
+ * // Mask credit card info
28
+ * <Mask>
29
+ * <CreditCardDisplay card={card} />
30
+ * </Mask>
31
+ * ```
32
+ */
33
+ // Lazy-loaded React Native modules
34
+ let _RN = null;
35
+ function getRN() {
36
+ if (_RN) return _RN;
37
+ try {
38
+ _RN = require('react-native');
39
+ return _RN;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ /**
45
+ * Wrapper component to mask sensitive content in session replays.
46
+ * All children will be obscured in recordings.
47
+ *
48
+ * Uses accessibilityHint to signal to the native capture engine
49
+ * that this view and its contents should be masked.
50
+ */
51
+ const Mask = ({
52
+ children,
53
+ style,
54
+ ...props
55
+ }) => {
56
+ const RN = getRN();
57
+
58
+ // If RN isn't loaded yet (shouldn't happen in practice), render children directly
59
+ if (!RN) {
60
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children);
61
+ }
62
+ const {
63
+ View,
64
+ StyleSheet
65
+ } = RN;
66
+ const styles = StyleSheet.create({
67
+ container: {
68
+ // Minimal container style - doesn't affect layout
69
+ }
70
+ });
71
+ return /*#__PURE__*/_react.default.createElement(View, _extends({}, props, {
72
+ style: [styles.container, style],
73
+ accessibilityHint: "rejourney_occlude",
74
+ collapsable: false
75
+ }), children);
76
+ };
77
+ exports.Mask = Mask;
78
+ var _default = exports.default = Mask;
79
+ //# sourceMappingURL=Mask.js.map