@rejourneyco/react-native 1.0.2 → 1.0.4
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/src/main/java/com/rejourney/RejourneyModuleImpl.kt +38 -363
- package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +11 -113
- package/android/src/main/java/com/rejourney/capture/SegmentUploader.kt +1 -15
- package/android/src/main/java/com/rejourney/capture/VideoEncoder.kt +1 -61
- package/android/src/main/java/com/rejourney/capture/ViewHierarchyScanner.kt +3 -1
- package/android/src/main/java/com/rejourney/lifecycle/SessionLifecycleService.kt +1 -22
- package/android/src/main/java/com/rejourney/network/DeviceAuthManager.kt +14 -27
- package/android/src/main/java/com/rejourney/network/NetworkMonitor.kt +0 -2
- package/android/src/main/java/com/rejourney/network/UploadManager.kt +7 -93
- package/android/src/main/java/com/rejourney/network/UploadWorker.kt +5 -41
- package/android/src/main/java/com/rejourney/privacy/PrivacyMask.kt +2 -58
- package/android/src/main/java/com/rejourney/touch/TouchInterceptor.kt +4 -4
- package/android/src/main/java/com/rejourney/utils/EventBuffer.kt +36 -7
- package/ios/Capture/RJCaptureEngine.m +9 -61
- package/ios/Capture/RJViewHierarchyScanner.m +68 -51
- package/ios/Core/RJLifecycleManager.m +0 -14
- package/ios/Core/Rejourney.mm +24 -37
- package/ios/Network/RJDeviceAuthManager.m +0 -2
- package/ios/Network/RJUploadManager.h +8 -0
- package/ios/Network/RJUploadManager.m +45 -0
- package/ios/Privacy/RJPrivacyMask.m +5 -31
- package/ios/Rejourney.h +0 -14
- package/ios/Touch/RJTouchInterceptor.m +21 -15
- package/ios/Utils/RJEventBuffer.m +57 -69
- package/ios/Utils/RJWindowUtils.m +87 -86
- package/lib/commonjs/index.js +44 -31
- package/lib/commonjs/sdk/autoTracking.js +0 -3
- package/lib/commonjs/sdk/constants.js +1 -1
- package/lib/commonjs/sdk/networkInterceptor.js +0 -11
- package/lib/commonjs/sdk/utils.js +73 -14
- package/lib/module/index.js +44 -31
- package/lib/module/sdk/autoTracking.js +0 -3
- package/lib/module/sdk/constants.js +1 -1
- package/lib/module/sdk/networkInterceptor.js +0 -11
- package/lib/module/sdk/utils.js +73 -14
- package/lib/typescript/sdk/constants.d.ts +1 -1
- package/lib/typescript/sdk/utils.d.ts +31 -1
- package/package.json +16 -4
- package/src/index.ts +42 -20
- package/src/sdk/autoTracking.ts +0 -2
- package/src/sdk/constants.ts +14 -14
- package/src/sdk/networkInterceptor.ts +0 -9
- package/src/sdk/utils.ts +76 -14
package/src/index.ts
CHANGED
|
@@ -100,6 +100,11 @@ function getLogger() {
|
|
|
100
100
|
logRecordingRemoteDisabled: () => { },
|
|
101
101
|
logInvalidProjectKey: () => { },
|
|
102
102
|
logPackageMismatch: () => { },
|
|
103
|
+
logNetworkRequest: () => { },
|
|
104
|
+
logFrustration: () => { },
|
|
105
|
+
logError: () => { },
|
|
106
|
+
logUploadStats: () => { },
|
|
107
|
+
logLifecycleEvent: () => { },
|
|
103
108
|
};
|
|
104
109
|
}
|
|
105
110
|
|
|
@@ -126,6 +131,11 @@ function getLogger() {
|
|
|
126
131
|
logRecordingRemoteDisabled: () => { },
|
|
127
132
|
logInvalidProjectKey: () => { },
|
|
128
133
|
logPackageMismatch: () => { },
|
|
134
|
+
logNetworkRequest: () => { },
|
|
135
|
+
logFrustration: () => { },
|
|
136
|
+
logError: () => { },
|
|
137
|
+
logUploadStats: () => { },
|
|
138
|
+
logLifecycleEvent: () => { },
|
|
129
139
|
};
|
|
130
140
|
}
|
|
131
141
|
}
|
|
@@ -194,6 +204,7 @@ function getAutoTracking() {
|
|
|
194
204
|
let _isInitialized = false;
|
|
195
205
|
let _isRecording = false;
|
|
196
206
|
let _initializationFailed = false;
|
|
207
|
+
let _metricsInterval: ReturnType<typeof setInterval> | null = null;
|
|
197
208
|
let _appStateSubscription: { remove: () => void } | null = null;
|
|
198
209
|
let _authErrorSubscription: { remove: () => void } | null = null;
|
|
199
210
|
let _currentAppState: string = 'active'; // Default to active, will be updated on init
|
|
@@ -415,10 +426,8 @@ const Rejourney: RejourneyAPI = {
|
|
|
415
426
|
|
|
416
427
|
getLogger().debug(`Calling native startSession (apiUrl=${apiUrl})`);
|
|
417
428
|
|
|
418
|
-
// Use user identity if set, otherwise use anonymous device ID
|
|
419
429
|
const deviceId = await getAutoTracking().ensurePersistentAnonymousId();
|
|
420
430
|
|
|
421
|
-
// Try to load persisted user identity if not already set in memory
|
|
422
431
|
if (!_userIdentity) {
|
|
423
432
|
_userIdentity = await loadPersistedUserIdentity();
|
|
424
433
|
}
|
|
@@ -426,7 +435,6 @@ const Rejourney: RejourneyAPI = {
|
|
|
426
435
|
const userId = _userIdentity || deviceId;
|
|
427
436
|
getLogger().debug(`userId=${userId.substring(0, 8)}...`);
|
|
428
437
|
|
|
429
|
-
// Start native session
|
|
430
438
|
const result = await nativeModule.startSession(userId, apiUrl, publicKey);
|
|
431
439
|
getLogger().debug('Native startSession returned:', JSON.stringify(result));
|
|
432
440
|
|
|
@@ -441,10 +449,28 @@ const Rejourney: RejourneyAPI = {
|
|
|
441
449
|
|
|
442
450
|
_isRecording = true;
|
|
443
451
|
getLogger().debug(`✅ Session started: ${result.sessionId}`);
|
|
444
|
-
// Use lifecycle log for session start - only shown in dev builds
|
|
445
452
|
getLogger().logSessionStart(result.sessionId);
|
|
453
|
+
// Start polling for upload stats in dev mode
|
|
454
|
+
if (__DEV__) {
|
|
455
|
+
_metricsInterval = setInterval(async () => {
|
|
456
|
+
if (!_isRecording) {
|
|
457
|
+
if (_metricsInterval) clearInterval(_metricsInterval);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
const native = getRejourneyNative();
|
|
462
|
+
if (native) {
|
|
463
|
+
const metrics = await native.getSDKMetrics();
|
|
464
|
+
if (metrics) {
|
|
465
|
+
getLogger().logUploadStats(metrics);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} catch (e) {
|
|
469
|
+
getLogger().debug('Failed to fetch metrics:', e);
|
|
470
|
+
}
|
|
471
|
+
}, 10000); // Poll more frequently in dev (10s) for better feedback
|
|
472
|
+
}
|
|
446
473
|
|
|
447
|
-
// Initialize auto tracking features
|
|
448
474
|
getAutoTracking().initAutoTracking(
|
|
449
475
|
{
|
|
450
476
|
rageTapThreshold: _storedConfig?.rageTapThreshold ?? 3,
|
|
@@ -464,7 +490,7 @@ const Rejourney: RejourneyAPI = {
|
|
|
464
490
|
x,
|
|
465
491
|
y,
|
|
466
492
|
});
|
|
467
|
-
|
|
493
|
+
getLogger().logFrustration(`Rage tap (${count} taps)`);
|
|
468
494
|
},
|
|
469
495
|
// Error callback - log as error event
|
|
470
496
|
onError: (error: { message: string; stack?: string; name?: string }) => {
|
|
@@ -473,18 +499,13 @@ const Rejourney: RejourneyAPI = {
|
|
|
473
499
|
stack: error.stack,
|
|
474
500
|
name: error.name,
|
|
475
501
|
});
|
|
476
|
-
|
|
502
|
+
getLogger().logError(error.message);
|
|
477
503
|
},
|
|
478
|
-
// Screen change callback - log screen change
|
|
479
504
|
onScreen: (_screenName: string, _previousScreen?: string) => {
|
|
480
|
-
// Native module already handles screen changes
|
|
481
|
-
// This is just for metrics tracking
|
|
482
|
-
// logger.debug(`Screen changed: ${previousScreen} -> ${screenName}`);
|
|
483
505
|
},
|
|
484
506
|
}
|
|
485
507
|
);
|
|
486
508
|
|
|
487
|
-
// Collect and log device info
|
|
488
509
|
if (_storedConfig?.collectDeviceInfo !== false) {
|
|
489
510
|
try {
|
|
490
511
|
const deviceInfo = await getAutoTracking().collectDeviceInfo();
|
|
@@ -494,7 +515,6 @@ const Rejourney: RejourneyAPI = {
|
|
|
494
515
|
}
|
|
495
516
|
}
|
|
496
517
|
|
|
497
|
-
// Setup automatic network interception
|
|
498
518
|
if (_storedConfig?.autoTrackNetwork !== false) {
|
|
499
519
|
try {
|
|
500
520
|
const ignoreUrls: (string | RegExp)[] = [
|
|
@@ -507,13 +527,13 @@ const Rejourney: RejourneyAPI = {
|
|
|
507
527
|
|
|
508
528
|
getNetworkInterceptor().initNetworkInterceptor(
|
|
509
529
|
(request: NetworkRequestParams) => {
|
|
510
|
-
this.logNetworkRequest(request);
|
|
511
530
|
getAutoTracking().trackAPIRequest(
|
|
512
531
|
request.success || false,
|
|
513
532
|
request.statusCode,
|
|
514
533
|
request.duration || 0,
|
|
515
534
|
request.responseBodySize || 0
|
|
516
535
|
);
|
|
536
|
+
Rejourney.logNetworkRequest(request);
|
|
517
537
|
},
|
|
518
538
|
{
|
|
519
539
|
ignoreUrls,
|
|
@@ -554,6 +574,11 @@ const Rejourney: RejourneyAPI = {
|
|
|
554
574
|
|
|
555
575
|
await safeNativeCall('stopSession', () => getRejourneyNative()!.stopSession(), undefined);
|
|
556
576
|
|
|
577
|
+
if (_metricsInterval) {
|
|
578
|
+
clearInterval(_metricsInterval);
|
|
579
|
+
_metricsInterval = null;
|
|
580
|
+
}
|
|
581
|
+
|
|
557
582
|
_isRecording = false;
|
|
558
583
|
getLogger().logSessionEnd('current');
|
|
559
584
|
} catch (error) {
|
|
@@ -573,7 +598,6 @@ const Rejourney: RejourneyAPI = {
|
|
|
573
598
|
safeNativeCallSync(
|
|
574
599
|
'logEvent',
|
|
575
600
|
() => {
|
|
576
|
-
// Fire and forget - don't await
|
|
577
601
|
getRejourneyNative()!.logEvent(name, properties || {}).catch(() => { });
|
|
578
602
|
},
|
|
579
603
|
undefined
|
|
@@ -689,7 +713,7 @@ const Rejourney: RejourneyAPI = {
|
|
|
689
713
|
eventCount: 0,
|
|
690
714
|
videoSegmentCount: 0,
|
|
691
715
|
storageSize: 0,
|
|
692
|
-
sdkVersion:
|
|
716
|
+
sdkVersion: SDK_VERSION,
|
|
693
717
|
isComplete: false,
|
|
694
718
|
},
|
|
695
719
|
events: [],
|
|
@@ -719,7 +743,6 @@ const Rejourney: RejourneyAPI = {
|
|
|
719
743
|
* @returns Path to export file (not implemented)
|
|
720
744
|
*/
|
|
721
745
|
async exportSession(_sessionId: string): Promise<string> {
|
|
722
|
-
// Return empty string - actual export should be done from dashboard server
|
|
723
746
|
getLogger().warn('exportSession not implemented - export from dashboard server');
|
|
724
747
|
return '';
|
|
725
748
|
},
|
|
@@ -975,7 +998,6 @@ const Rejourney: RejourneyAPI = {
|
|
|
975
998
|
cached: request.cached,
|
|
976
999
|
};
|
|
977
1000
|
|
|
978
|
-
// Fire and forget - don't await, this is low priority
|
|
979
1001
|
getRejourneyNative()!.logEvent('network_request', networkEvent).catch(() => { });
|
|
980
1002
|
},
|
|
981
1003
|
undefined
|
|
@@ -1095,10 +1117,10 @@ function handleAppStateChange(nextAppState: string): void {
|
|
|
1095
1117
|
try {
|
|
1096
1118
|
if (_currentAppState.match(/active/) && nextAppState === 'background') {
|
|
1097
1119
|
// App going to background - native module handles this automatically
|
|
1098
|
-
getLogger().
|
|
1120
|
+
getLogger().logLifecycleEvent('App moving to background');
|
|
1099
1121
|
} else if (_currentAppState.match(/inactive|background/) && nextAppState === 'active') {
|
|
1100
1122
|
// App coming back to foreground
|
|
1101
|
-
getLogger().
|
|
1123
|
+
getLogger().logLifecycleEvent('App returning to foreground');
|
|
1102
1124
|
}
|
|
1103
1125
|
_currentAppState = nextAppState;
|
|
1104
1126
|
} catch (error) {
|
package/src/sdk/autoTracking.ts
CHANGED
|
@@ -572,8 +572,6 @@ function setupNavigationTracking(): void {
|
|
|
572
572
|
logger.debug('Setting up navigation tracking...');
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
-
// Delay to ensure navigation is initialized - Expo Router needs more time
|
|
576
|
-
// We retry a few times with increasing delays
|
|
577
575
|
let attempts = 0;
|
|
578
576
|
const maxAttempts = 5;
|
|
579
577
|
|
package/src/sdk/constants.ts
CHANGED
|
@@ -2,31 +2,31 @@
|
|
|
2
2
|
* Rejourney SDK Constants
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export const SDK_VERSION = '1.0.
|
|
5
|
+
export const SDK_VERSION = '1.0.3';
|
|
6
6
|
|
|
7
7
|
/** Default configuration values */
|
|
8
8
|
export const DEFAULT_CONFIG = {
|
|
9
9
|
enabled: true,
|
|
10
|
-
captureFPS: 0.5,
|
|
11
|
-
captureOnEvents: true,
|
|
12
|
-
maxSessionDuration: 10 * 60 * 1000,
|
|
13
|
-
maxStorageSize: 50 * 1024 * 1024,
|
|
10
|
+
captureFPS: 0.5,
|
|
11
|
+
captureOnEvents: true,
|
|
12
|
+
maxSessionDuration: 10 * 60 * 1000,
|
|
13
|
+
maxStorageSize: 50 * 1024 * 1024,
|
|
14
14
|
autoScreenTracking: true,
|
|
15
15
|
autoGestureTracking: true,
|
|
16
16
|
privacyOcclusion: true,
|
|
17
17
|
enableCompression: true,
|
|
18
|
-
inactivityThreshold: 5000,
|
|
18
|
+
inactivityThreshold: 5000,
|
|
19
19
|
disableInDev: false,
|
|
20
20
|
detectRageTaps: true,
|
|
21
21
|
rageTapThreshold: 3,
|
|
22
|
-
rageTapTimeWindow: 1000,
|
|
22
|
+
rageTapTimeWindow: 1000,
|
|
23
23
|
debug: false,
|
|
24
24
|
autoStartRecording: true,
|
|
25
|
-
collectDeviceInfo: true,
|
|
26
|
-
collectGeoLocation: true,
|
|
27
|
-
postNavigationDelay: 300,
|
|
28
|
-
postGestureDelay: 200,
|
|
29
|
-
postModalDelay: 400,
|
|
25
|
+
collectDeviceInfo: true,
|
|
26
|
+
collectGeoLocation: true,
|
|
27
|
+
postNavigationDelay: 300,
|
|
28
|
+
postGestureDelay: 200,
|
|
29
|
+
postModalDelay: 400,
|
|
30
30
|
} as const;
|
|
31
31
|
|
|
32
32
|
/** Event type constants */
|
|
@@ -61,8 +61,8 @@ export const CAPTURE_SETTINGS = {
|
|
|
61
61
|
DEFAULT_FPS: 0.5,
|
|
62
62
|
MIN_FPS: 0.1,
|
|
63
63
|
MAX_FPS: 2,
|
|
64
|
-
CAPTURE_SCALE: 0.25,
|
|
65
|
-
MIN_CAPTURE_DELTA_TIME: 0.5,
|
|
64
|
+
CAPTURE_SCALE: 0.25,
|
|
65
|
+
MIN_CAPTURE_DELTA_TIME: 0.5,
|
|
66
66
|
} as const;
|
|
67
67
|
|
|
68
68
|
/** Memory management settings */
|
|
@@ -129,8 +129,6 @@ function flushPendingRequests(): void {
|
|
|
129
129
|
flushTimer = null;
|
|
130
130
|
|
|
131
131
|
if (!logCallback || pendingCount === 0) return;
|
|
132
|
-
|
|
133
|
-
// Process all pending requests
|
|
134
132
|
while (pendingCount > 0) {
|
|
135
133
|
const request = pendingRequests[pendingHead];
|
|
136
134
|
pendingRequests[pendingHead] = null; // Allow GC
|
|
@@ -209,14 +207,10 @@ function interceptFetch(): void {
|
|
|
209
207
|
return originalFetch!(input, init);
|
|
210
208
|
}
|
|
211
209
|
|
|
212
|
-
// Capture start time (only synchronous work)
|
|
213
210
|
const startTime = Date.now();
|
|
214
211
|
const method = ((init?.method || 'GET').toUpperCase()) as NetworkRequestParams['method'];
|
|
215
|
-
|
|
216
|
-
// Call original fetch
|
|
217
212
|
return originalFetch!(input, init).then(
|
|
218
213
|
(response) => {
|
|
219
|
-
// Success - queue the log asynchronously
|
|
220
214
|
queueRequest({
|
|
221
215
|
requestId: `f${startTime}`,
|
|
222
216
|
method,
|
|
@@ -281,8 +275,6 @@ function interceptXHR(): void {
|
|
|
281
275
|
if (!config.enabled || !logCallback || !data || shouldIgnoreUrl(data.u)) {
|
|
282
276
|
return originalXHRSend!.call(this, body);
|
|
283
277
|
}
|
|
284
|
-
|
|
285
|
-
// Check sampling
|
|
286
278
|
const { path } = parseUrlFast(data.u);
|
|
287
279
|
if (!shouldSampleRequest(path)) {
|
|
288
280
|
return originalXHRSend!.call(this, body);
|
|
@@ -344,7 +336,6 @@ export function initNetworkInterceptor(
|
|
|
344
336
|
export function disableNetworkInterceptor(): void {
|
|
345
337
|
config.enabled = false;
|
|
346
338
|
|
|
347
|
-
// Flush any pending requests
|
|
348
339
|
if (flushTimer) {
|
|
349
340
|
clearTimeout(flushTimer);
|
|
350
341
|
flushTimer = null;
|
package/src/sdk/utils.ts
CHANGED
|
@@ -236,7 +236,7 @@ export enum LogLevel {
|
|
|
236
236
|
*/
|
|
237
237
|
class Logger {
|
|
238
238
|
private prefix = '[Rejourney]';
|
|
239
|
-
|
|
239
|
+
|
|
240
240
|
|
|
241
241
|
/**
|
|
242
242
|
* Minimum log level to display.
|
|
@@ -260,7 +260,6 @@ class Logger {
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
setDebugMode(enabled: boolean): void {
|
|
263
|
-
this.debugMode = enabled;
|
|
264
263
|
this.minimumLogLevel = enabled
|
|
265
264
|
? LogLevel.DEBUG
|
|
266
265
|
: typeof __DEV__ !== 'undefined' && __DEV__
|
|
@@ -285,14 +284,26 @@ class Logger {
|
|
|
285
284
|
/** Log a warning message */
|
|
286
285
|
warn(...args: any[]): void {
|
|
287
286
|
if (this.minimumLogLevel <= LogLevel.WARNING) {
|
|
288
|
-
|
|
287
|
+
if (this.minimumLogLevel <= LogLevel.DEBUG) {
|
|
288
|
+
// Explicit Debug Mode: Show YellowBox
|
|
289
|
+
console.warn(this.prefix, ...args);
|
|
290
|
+
} else {
|
|
291
|
+
// Default Dev Mode: Log to console only, avoid YellowBox
|
|
292
|
+
console.log(this.prefix, '[WARN]', ...args);
|
|
293
|
+
}
|
|
289
294
|
}
|
|
290
295
|
}
|
|
291
296
|
|
|
292
297
|
/** Log an error message */
|
|
293
298
|
error(...args: any[]): void {
|
|
294
299
|
if (this.minimumLogLevel <= LogLevel.ERROR) {
|
|
295
|
-
|
|
300
|
+
if (this.minimumLogLevel <= LogLevel.DEBUG) {
|
|
301
|
+
// Explicit Debug Mode: Show RedBox
|
|
302
|
+
console.error(this.prefix, ...args);
|
|
303
|
+
} else {
|
|
304
|
+
// Default Dev Mode: Log to console only, avoid RedBox
|
|
305
|
+
console.log(this.prefix, '[ERROR]', ...args);
|
|
306
|
+
}
|
|
296
307
|
}
|
|
297
308
|
}
|
|
298
309
|
|
|
@@ -307,9 +318,7 @@ class Logger {
|
|
|
307
318
|
* Only shown in development builds - this is the minimal "SDK started" log.
|
|
308
319
|
*/
|
|
309
320
|
logInitSuccess(version: string): void {
|
|
310
|
-
|
|
311
|
-
this.info(`✓ SDK initialized (v${version})`);
|
|
312
|
-
}
|
|
321
|
+
this.notice(`✓ SDK initialized (v${version})`);
|
|
313
322
|
}
|
|
314
323
|
|
|
315
324
|
/**
|
|
@@ -325,9 +334,7 @@ class Logger {
|
|
|
325
334
|
* Only shown in development builds.
|
|
326
335
|
*/
|
|
327
336
|
logSessionStart(sessionId: string): void {
|
|
328
|
-
|
|
329
|
-
this.info(`Session started: ${sessionId}`);
|
|
330
|
-
}
|
|
337
|
+
this.notice(`Session started: ${sessionId}`);
|
|
331
338
|
}
|
|
332
339
|
|
|
333
340
|
/**
|
|
@@ -335,13 +342,11 @@ class Logger {
|
|
|
335
342
|
* Only shown in development builds.
|
|
336
343
|
*/
|
|
337
344
|
logSessionEnd(sessionId: string): void {
|
|
338
|
-
|
|
339
|
-
this.info(`Session ended: ${sessionId}`);
|
|
340
|
-
}
|
|
345
|
+
this.notice(`Session ended: ${sessionId}`);
|
|
341
346
|
}
|
|
342
347
|
|
|
343
348
|
logObservabilityStart(): void {
|
|
344
|
-
this.notice('Starting Rejourney observability');
|
|
349
|
+
this.notice('💧 Starting Rejourney observability');
|
|
345
350
|
}
|
|
346
351
|
|
|
347
352
|
logRecordingStart(): void {
|
|
@@ -359,6 +364,63 @@ class Logger {
|
|
|
359
364
|
logPackageMismatch(): void {
|
|
360
365
|
this.notice('Bundle ID / package name mismatch');
|
|
361
366
|
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Log network request details
|
|
370
|
+
*/
|
|
371
|
+
logNetworkRequest(request: { method?: string; url?: string; statusCode?: number; duration?: number; error?: string }): void {
|
|
372
|
+
const statusIcon = request.error || (request.statusCode && request.statusCode >= 400) ? '🔴' : '🟢';
|
|
373
|
+
const method = request.method || 'GET';
|
|
374
|
+
// Shorten URL to just path if possible
|
|
375
|
+
let url = request.url || '';
|
|
376
|
+
try {
|
|
377
|
+
if (url.startsWith('http')) {
|
|
378
|
+
const urlObj = new URL(url);
|
|
379
|
+
url = urlObj.pathname;
|
|
380
|
+
}
|
|
381
|
+
} catch {
|
|
382
|
+
// Keep full URL if parsing fails
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const duration = request.duration ? `(${Math.round(request.duration)}ms)` : '';
|
|
386
|
+
const status = request.statusCode ? `${request.statusCode}` : 'ERR';
|
|
387
|
+
|
|
388
|
+
this.notice(`${statusIcon} [NET] ${status} ${method} ${url} ${duration} ${request.error ? `Error: ${request.error}` : ''}`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Log frustration event (rage taps, etc)
|
|
393
|
+
*/
|
|
394
|
+
logFrustration(kind: string): void {
|
|
395
|
+
this.notice(`🤬 Frustration detected: ${kind}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Log error captured by SDK
|
|
400
|
+
*/
|
|
401
|
+
logError(message: string): void {
|
|
402
|
+
this.notice(`X Error captured: ${message}`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Log lifecycle event (Background/Foreground)
|
|
407
|
+
* Visible in development builds.
|
|
408
|
+
*/
|
|
409
|
+
logLifecycleEvent(event: string): void {
|
|
410
|
+
this.notice(`🔄 Lifecycle: ${event}`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Log upload statistics
|
|
415
|
+
*/
|
|
416
|
+
logUploadStats(metrics: { uploadSuccessCount: number; uploadFailureCount: number; totalBytesUploaded: number }): void {
|
|
417
|
+
const success = metrics.uploadSuccessCount;
|
|
418
|
+
const failed = metrics.uploadFailureCount;
|
|
419
|
+
const bytes = formatBytes(metrics.totalBytesUploaded);
|
|
420
|
+
|
|
421
|
+
// Always show in dev mode for reassurance, even if 0
|
|
422
|
+
this.notice(`📡 Upload Stats: ${success} success, ${failed} failed (${bytes} uploaded)`);
|
|
423
|
+
}
|
|
362
424
|
}
|
|
363
425
|
|
|
364
426
|
export const logger = new Logger();
|