@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.
- package/android/build.gradle.kts +135 -0
- package/android/consumer-rules.pro +10 -0
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +15 -0
- package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +2981 -0
- package/android/src/main/java/com/rejourney/capture/ANRHandler.kt +206 -0
- package/android/src/main/java/com/rejourney/capture/ActivityTracker.kt +98 -0
- package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +1553 -0
- package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +375 -0
- package/android/src/main/java/com/rejourney/capture/CrashHandler.kt +153 -0
- package/android/src/main/java/com/rejourney/capture/MotionEvent.kt +215 -0
- package/android/src/main/java/com/rejourney/capture/SegmentUploader.kt +512 -0
- package/android/src/main/java/com/rejourney/capture/VideoEncoder.kt +773 -0
- package/android/src/main/java/com/rejourney/capture/ViewHierarchyScanner.kt +633 -0
- package/android/src/main/java/com/rejourney/capture/ViewSerializer.kt +286 -0
- package/android/src/main/java/com/rejourney/core/Constants.kt +117 -0
- package/android/src/main/java/com/rejourney/core/Logger.kt +93 -0
- package/android/src/main/java/com/rejourney/core/Types.kt +124 -0
- package/android/src/main/java/com/rejourney/lifecycle/SessionLifecycleService.kt +162 -0
- package/android/src/main/java/com/rejourney/network/DeviceAuthManager.kt +747 -0
- package/android/src/main/java/com/rejourney/network/HttpClientProvider.kt +16 -0
- package/android/src/main/java/com/rejourney/network/NetworkMonitor.kt +272 -0
- package/android/src/main/java/com/rejourney/network/UploadManager.kt +1363 -0
- package/android/src/main/java/com/rejourney/network/UploadWorker.kt +492 -0
- package/android/src/main/java/com/rejourney/privacy/PrivacyMask.kt +645 -0
- package/android/src/main/java/com/rejourney/touch/GestureClassifier.kt +233 -0
- package/android/src/main/java/com/rejourney/touch/KeyboardTracker.kt +158 -0
- package/android/src/main/java/com/rejourney/touch/TextInputTracker.kt +181 -0
- package/android/src/main/java/com/rejourney/touch/TouchInterceptor.kt +591 -0
- package/android/src/main/java/com/rejourney/utils/EventBuffer.kt +284 -0
- package/android/src/main/java/com/rejourney/utils/OEMDetector.kt +154 -0
- package/android/src/main/java/com/rejourney/utils/PerfTiming.kt +235 -0
- package/android/src/main/java/com/rejourney/utils/Telemetry.kt +297 -0
- package/android/src/main/java/com/rejourney/utils/WindowUtils.kt +84 -0
- package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +187 -0
- package/android/src/newarch/java/com/rejourney/RejourneyPackage.kt +40 -0
- package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +218 -0
- package/android/src/oldarch/java/com/rejourney/RejourneyPackage.kt +23 -0
- package/ios/Capture/RJANRHandler.h +42 -0
- package/ios/Capture/RJANRHandler.m +328 -0
- package/ios/Capture/RJCaptureEngine.h +275 -0
- package/ios/Capture/RJCaptureEngine.m +2062 -0
- package/ios/Capture/RJCaptureHeuristics.h +80 -0
- package/ios/Capture/RJCaptureHeuristics.m +903 -0
- package/ios/Capture/RJCrashHandler.h +46 -0
- package/ios/Capture/RJCrashHandler.m +313 -0
- package/ios/Capture/RJMotionEvent.h +183 -0
- package/ios/Capture/RJMotionEvent.m +183 -0
- package/ios/Capture/RJPerformanceManager.h +100 -0
- package/ios/Capture/RJPerformanceManager.m +373 -0
- package/ios/Capture/RJPixelBufferDownscaler.h +42 -0
- package/ios/Capture/RJPixelBufferDownscaler.m +85 -0
- package/ios/Capture/RJSegmentUploader.h +146 -0
- package/ios/Capture/RJSegmentUploader.m +778 -0
- package/ios/Capture/RJVideoEncoder.h +247 -0
- package/ios/Capture/RJVideoEncoder.m +1036 -0
- package/ios/Capture/RJViewControllerTracker.h +73 -0
- package/ios/Capture/RJViewControllerTracker.m +508 -0
- package/ios/Capture/RJViewHierarchyScanner.h +215 -0
- package/ios/Capture/RJViewHierarchyScanner.m +1464 -0
- package/ios/Capture/RJViewSerializer.h +119 -0
- package/ios/Capture/RJViewSerializer.m +498 -0
- package/ios/Core/RJConstants.h +124 -0
- package/ios/Core/RJConstants.m +88 -0
- package/ios/Core/RJLifecycleManager.h +85 -0
- package/ios/Core/RJLifecycleManager.m +308 -0
- package/ios/Core/RJLogger.h +61 -0
- package/ios/Core/RJLogger.m +211 -0
- package/ios/Core/RJTypes.h +176 -0
- package/ios/Core/RJTypes.m +66 -0
- package/ios/Core/Rejourney.h +64 -0
- package/ios/Core/Rejourney.mm +2495 -0
- package/ios/Network/RJDeviceAuthManager.h +94 -0
- package/ios/Network/RJDeviceAuthManager.m +967 -0
- package/ios/Network/RJNetworkMonitor.h +68 -0
- package/ios/Network/RJNetworkMonitor.m +267 -0
- package/ios/Network/RJRetryManager.h +73 -0
- package/ios/Network/RJRetryManager.m +325 -0
- package/ios/Network/RJUploadManager.h +267 -0
- package/ios/Network/RJUploadManager.m +2296 -0
- package/ios/Privacy/RJPrivacyMask.h +163 -0
- package/ios/Privacy/RJPrivacyMask.m +922 -0
- package/ios/Rejourney.h +63 -0
- package/ios/Touch/RJGestureClassifier.h +130 -0
- package/ios/Touch/RJGestureClassifier.m +333 -0
- package/ios/Touch/RJTouchInterceptor.h +169 -0
- package/ios/Touch/RJTouchInterceptor.m +772 -0
- package/ios/Utils/RJEventBuffer.h +112 -0
- package/ios/Utils/RJEventBuffer.m +358 -0
- package/ios/Utils/RJGzipUtils.h +33 -0
- package/ios/Utils/RJGzipUtils.m +89 -0
- package/ios/Utils/RJKeychainManager.h +48 -0
- package/ios/Utils/RJKeychainManager.m +111 -0
- package/ios/Utils/RJPerfTiming.h +209 -0
- package/ios/Utils/RJPerfTiming.m +264 -0
- package/ios/Utils/RJTelemetry.h +92 -0
- package/ios/Utils/RJTelemetry.m +320 -0
- package/ios/Utils/RJWindowUtils.h +66 -0
- package/ios/Utils/RJWindowUtils.m +133 -0
- package/lib/commonjs/NativeRejourney.js +40 -0
- package/lib/commonjs/components/Mask.js +79 -0
- package/lib/commonjs/index.js +1381 -0
- package/lib/commonjs/sdk/autoTracking.js +1259 -0
- package/lib/commonjs/sdk/constants.js +151 -0
- package/lib/commonjs/sdk/errorTracking.js +199 -0
- package/lib/commonjs/sdk/index.js +50 -0
- package/lib/commonjs/sdk/metricsTracking.js +204 -0
- package/lib/commonjs/sdk/navigation.js +151 -0
- package/lib/commonjs/sdk/networkInterceptor.js +412 -0
- package/lib/commonjs/sdk/utils.js +363 -0
- package/lib/commonjs/types/expo-router.d.js +2 -0
- package/lib/commonjs/types/index.js +2 -0
- package/lib/module/NativeRejourney.js +38 -0
- package/lib/module/components/Mask.js +72 -0
- package/lib/module/index.js +1284 -0
- package/lib/module/sdk/autoTracking.js +1233 -0
- package/lib/module/sdk/constants.js +145 -0
- package/lib/module/sdk/errorTracking.js +189 -0
- package/lib/module/sdk/index.js +12 -0
- package/lib/module/sdk/metricsTracking.js +187 -0
- package/lib/module/sdk/navigation.js +143 -0
- package/lib/module/sdk/networkInterceptor.js +401 -0
- package/lib/module/sdk/utils.js +342 -0
- package/lib/module/types/expo-router.d.js +2 -0
- package/lib/module/types/index.js +2 -0
- package/lib/typescript/NativeRejourney.d.ts +147 -0
- package/lib/typescript/components/Mask.d.ts +39 -0
- package/lib/typescript/index.d.ts +117 -0
- package/lib/typescript/sdk/autoTracking.d.ts +204 -0
- package/lib/typescript/sdk/constants.d.ts +120 -0
- package/lib/typescript/sdk/errorTracking.d.ts +32 -0
- package/lib/typescript/sdk/index.d.ts +9 -0
- package/lib/typescript/sdk/metricsTracking.d.ts +58 -0
- package/lib/typescript/sdk/navigation.d.ts +33 -0
- package/lib/typescript/sdk/networkInterceptor.d.ts +47 -0
- package/lib/typescript/sdk/utils.d.ts +148 -0
- package/lib/typescript/types/index.d.ts +624 -0
- package/package.json +102 -0
- package/rejourney.podspec +21 -0
- package/src/NativeRejourney.ts +165 -0
- package/src/components/Mask.tsx +80 -0
- package/src/index.ts +1459 -0
- package/src/sdk/autoTracking.ts +1373 -0
- package/src/sdk/constants.ts +134 -0
- package/src/sdk/errorTracking.ts +231 -0
- package/src/sdk/index.ts +11 -0
- package/src/sdk/metricsTracking.ts +232 -0
- package/src/sdk/navigation.ts +157 -0
- package/src/sdk/networkInterceptor.ts +440 -0
- package/src/sdk/utils.ts +369 -0
- package/src/types/expo-router.d.ts +7 -0
- 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
|