@rejourneyco/react-native 1.0.8 → 1.0.10

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 (52) hide show
  1. package/README.md +77 -3
  2. package/android/src/main/AndroidManifest.xml +6 -0
  3. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +143 -8
  4. package/android/src/main/java/com/rejourney/RejourneyOkHttpInitProvider.kt +68 -0
  5. package/android/src/main/java/com/rejourney/engine/DeviceRegistrar.kt +21 -3
  6. package/android/src/main/java/com/rejourney/engine/RejourneyImpl.kt +69 -17
  7. package/android/src/main/java/com/rejourney/recording/AnrSentinel.kt +27 -2
  8. package/android/src/main/java/com/rejourney/recording/InteractionRecorder.kt +3 -1
  9. package/android/src/main/java/com/rejourney/recording/RejourneyNetworkInterceptor.kt +93 -0
  10. package/android/src/main/java/com/rejourney/recording/ReplayOrchestrator.kt +226 -146
  11. package/android/src/main/java/com/rejourney/recording/SegmentDispatcher.kt +7 -0
  12. package/android/src/main/java/com/rejourney/recording/StabilityMonitor.kt +3 -0
  13. package/android/src/main/java/com/rejourney/recording/TelemetryPipeline.kt +39 -0
  14. package/android/src/main/java/com/rejourney/recording/ViewHierarchyScanner.kt +8 -0
  15. package/android/src/main/java/com/rejourney/recording/VisualCapture.kt +95 -21
  16. package/android/src/main/java/com/rejourney/utility/DataCompression.kt +14 -2
  17. package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +14 -0
  18. package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +18 -0
  19. package/ios/Engine/DeviceRegistrar.swift +13 -3
  20. package/ios/Engine/RejourneyImpl.swift +204 -115
  21. package/ios/Recording/AnrSentinel.swift +58 -25
  22. package/ios/Recording/InteractionRecorder.swift +1 -0
  23. package/ios/Recording/RejourneyURLProtocol.swift +216 -0
  24. package/ios/Recording/ReplayOrchestrator.swift +207 -144
  25. package/ios/Recording/SegmentDispatcher.swift +8 -0
  26. package/ios/Recording/StabilityMonitor.swift +40 -32
  27. package/ios/Recording/TelemetryPipeline.swift +45 -2
  28. package/ios/Recording/ViewHierarchyScanner.swift +1 -0
  29. package/ios/Recording/VisualCapture.swift +79 -29
  30. package/ios/Rejourney.mm +27 -8
  31. package/ios/Utility/DataCompression.swift +2 -2
  32. package/ios/Utility/ImageBlur.swift +0 -1
  33. package/lib/commonjs/expoRouterTracking.js +137 -0
  34. package/lib/commonjs/index.js +204 -34
  35. package/lib/commonjs/sdk/autoTracking.js +262 -100
  36. package/lib/commonjs/sdk/networkInterceptor.js +84 -4
  37. package/lib/module/expoRouterTracking.js +135 -0
  38. package/lib/module/index.js +203 -28
  39. package/lib/module/sdk/autoTracking.js +260 -100
  40. package/lib/module/sdk/networkInterceptor.js +84 -4
  41. package/lib/typescript/NativeRejourney.d.ts +5 -2
  42. package/lib/typescript/expoRouterTracking.d.ts +14 -0
  43. package/lib/typescript/index.d.ts +2 -2
  44. package/lib/typescript/sdk/autoTracking.d.ts +14 -1
  45. package/lib/typescript/types/index.d.ts +56 -5
  46. package/package.json +23 -3
  47. package/src/NativeRejourney.ts +8 -5
  48. package/src/expoRouterTracking.ts +167 -0
  49. package/src/index.ts +221 -35
  50. package/src/sdk/autoTracking.ts +286 -114
  51. package/src/sdk/networkInterceptor.ts +110 -1
  52. package/src/types/index.ts +58 -6
@@ -55,6 +55,11 @@ class AnrSentinel private constructor() {
55
55
 
56
56
  fun activate() {
57
57
  if (isActive.getAndSet(true)) return
58
+
59
+ // Reset watchdog state on each activation to avoid stale timings from
60
+ // previous app background periods.
61
+ lastResponseTime.set(System.currentTimeMillis())
62
+ pongSequence.set(pingSequence.get())
58
63
 
59
64
  startWatchdog()
60
65
  }
@@ -87,7 +92,7 @@ class AnrSentinel private constructor() {
87
92
  val elapsed = System.currentTimeMillis() - lastResponseTime.get()
88
93
  val missedPongs = pingSequence.get() - pongSequence.get()
89
94
 
90
- if (elapsed > anrThresholdMs && missedPongs > 0) {
95
+ if (elapsed >= anrThresholdMs && missedPongs > 0) {
91
96
  captureAnr(elapsed)
92
97
 
93
98
  // Reset to avoid duplicate reports
@@ -113,12 +118,32 @@ class AnrSentinel private constructor() {
113
118
  "${element.className}.${element.methodName}(${element.fileName}:${element.lineNumber})"
114
119
  }
115
120
 
116
- ReplayOrchestrator.shared?.incrementFaultTally()
121
+ ReplayOrchestrator.shared?.incrementStalledTally()
117
122
 
118
123
  // Route ANR through TelemetryPipeline so it arrives in the events
119
124
  // batch and the backend ingest worker can insert it into the anrs table
120
125
  val stackStr = frames.joinToString("\n")
121
126
  TelemetryPipeline.shared?.recordAnrEvent(durationMs, stackStr)
127
+
128
+ // Persist ANR incident and send through /api/ingest/fault so ANRs survive
129
+ // process termination/background upload loss, similar to crash recovery.
130
+ val sessionId = StabilityMonitor.shared?.currentSessionId
131
+ ?: ReplayOrchestrator.shared?.replayId
132
+ ?: "unknown"
133
+ val incident = IncidentRecord(
134
+ sessionId = sessionId,
135
+ timestampMs = System.currentTimeMillis(),
136
+ category = "anr",
137
+ identifier = "MainThreadFrozen",
138
+ detail = "Main thread unresponsive for ${durationMs}ms",
139
+ frames = frames,
140
+ context = mapOf(
141
+ "durationMs" to durationMs.toString(),
142
+ "threadState" to "blocked"
143
+ )
144
+ )
145
+ StabilityMonitor.shared?.persistIncidentSync(incident)
146
+ StabilityMonitor.shared?.transmitStoredReport()
122
147
 
123
148
  DiagnosticLog.fault("ANR detected: ${durationMs}ms hang")
124
149
 
@@ -133,8 +133,10 @@ class InteractionRecorder private constructor(private val context: Context) {
133
133
  // Notify SpecialCases about touch phases for touch-based
134
134
  // map idle detection (Mapbox v10+ fallback).
135
135
  when (event.actionMasked) {
136
- MotionEvent.ACTION_DOWN ->
136
+ MotionEvent.ACTION_DOWN -> {
137
+ VisualCapture.shared?.invalidateMaskCache()
137
138
  SpecialCases.shared.notifyTouchBegan()
139
+ }
138
140
  MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL ->
139
141
  SpecialCases.shared.notifyTouchEnded()
140
142
  }
@@ -0,0 +1,93 @@
1
+ package com.rejourney.recording
2
+
3
+ import okhttp3.Interceptor
4
+ import okhttp3.Response
5
+ import java.io.IOException
6
+ import java.util.UUID
7
+
8
+ /**
9
+ * Native OkHttp Interceptor for Rejourney
10
+ *
11
+ * Captures native network traffic and routes it to the Rejourney TelemetryPipeline.
12
+ * To use, add this interceptor to your native OkHttpClient:
13
+ * val client = OkHttpClient.Builder()
14
+ * .addInterceptor(RejourneyNetworkInterceptor())
15
+ * .build()
16
+ */
17
+ class RejourneyNetworkInterceptor : Interceptor {
18
+
19
+ @Throws(IOException::class)
20
+ override fun intercept(chain: Interceptor.Chain): Response {
21
+ val request = chain.request()
22
+ val startMs = System.currentTimeMillis()
23
+
24
+ var response: Response? = null
25
+ var error: Exception? = null
26
+
27
+ try {
28
+ response = chain.proceed(request)
29
+ } catch (e: Exception) {
30
+ error = e
31
+ throw e
32
+ } finally {
33
+ try {
34
+ val endMs = System.currentTimeMillis()
35
+ val duration = endMs - startMs
36
+
37
+ val isSuccess = response?.isSuccessful == true
38
+ val statusCode = response?.code ?: 0
39
+
40
+ val urlStr = request.url.toString()
41
+ val pathStr = request.url.encodedPath
42
+
43
+ var maxUrlStr = urlStr
44
+ if (maxUrlStr.length > 300) {
45
+ maxUrlStr = maxUrlStr.substring(0, 300)
46
+ }
47
+
48
+ val reqSize = request.body?.contentLength()?.takeIf { it >= 0 }?.toInt() ?: 0
49
+ val resSize = response?.body?.contentLength()?.takeIf { it >= 0 }?.toInt() ?: 0
50
+
51
+ val event = mutableMapOf<String, Any>(
52
+ "requestId" to "n_${UUID.randomUUID()}",
53
+ "method" to request.method,
54
+ "url" to maxUrlStr,
55
+ "urlPath" to pathStr,
56
+ "urlHost" to request.url.host,
57
+ "statusCode" to statusCode,
58
+ "duration" to duration,
59
+ "startTimestamp" to startMs,
60
+ "endTimestamp" to endMs,
61
+ "success" to isSuccess
62
+ )
63
+
64
+ if (reqSize > 0) {
65
+ event["requestBodySize"] = reqSize
66
+ }
67
+
68
+ request.body?.contentType()?.let {
69
+ event["requestContentType"] = it.toString()
70
+ }
71
+
72
+ if (resSize > 0) {
73
+ event["responseBodySize"] = resSize
74
+ }
75
+
76
+ response?.body?.contentType()?.let {
77
+ event["responseContentType"] = it.toString()
78
+ }
79
+
80
+ error?.let {
81
+ event["errorMessage"] = it.message ?: "Network error"
82
+ }
83
+
84
+ TelemetryPipeline.shared?.recordNetworkEvent(event)
85
+
86
+ } catch (ignore: Exception) {
87
+ // Ignore to avoid breaking the application network call
88
+ }
89
+ }
90
+
91
+ return response!!
92
+ }
93
+ }