@grafana/faro-react-native 1.3.1 → 1.4.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/README.md +176 -30
- package/android/build.gradle +17 -12
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashIds.kt +44 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +134 -38
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashSessionStore.kt +433 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +11 -2
- package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +128 -17
- package/android/src/main/java/com/grafana/faro/reactnative/FaroSessionProcess.kt +88 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FrameMonitor.kt +34 -42
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterBelowApi30Test.kt +21 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterTest.kt +59 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashSessionStoreTest.kt +268 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashTraceCacheTest.kt +52 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroSessionProcessTest.kt +127 -0
- package/dist/cjs/config/getRNInstrumentations.js +6 -8
- package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
- package/dist/cjs/config/sampling.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/dataCollection/DataCollectionPolicy.js +1 -2
- package/dist/cjs/dataCollection/DataCollectionPolicy.js.map +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/cjs/index.js +8 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/instrumentations/anr/instrumentation.js +10 -1
- package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/appState/index.js +8 -2
- package/dist/cjs/instrumentations/appState/index.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +19 -6
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +331 -0
- package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/cjs/instrumentations/errors/index.js +8 -3
- package/dist/cjs/instrumentations/errors/index.js.map +1 -1
- package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js +20 -31
- package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/frameMonitoring/types.js.map +1 -1
- package/dist/cjs/instrumentations/performance/index.js +2 -6
- package/dist/cjs/instrumentations/performance/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js +48 -0
- package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
- package/dist/cjs/instrumentations/session/directSessionActivity.js +37 -0
- package/dist/cjs/instrumentations/session/directSessionActivity.js.map +1 -0
- package/dist/cjs/instrumentations/session/index.js +214 -45
- package/dist/cjs/instrumentations/session/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionActivity.js +43 -0
- package/dist/cjs/instrumentations/session/sessionActivity.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionAttributes.js +8 -8
- package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +199 -24
- package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -13
- package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js +72 -0
- package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js +124 -96
- package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/types.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionProcess.js +96 -0
- package/dist/cjs/instrumentations/session/sessionProcess.js.map +1 -0
- package/dist/cjs/instrumentations/startup/index.js +2 -6
- package/dist/cjs/instrumentations/startup/index.js.map +1 -1
- package/dist/cjs/navigation/utils.js +3 -1
- package/dist/cjs/navigation/utils.js.map +1 -1
- package/dist/cjs/transports/fetch/index.js.map +1 -1
- package/dist/cjs/transports/fetch/transport.js +56 -25
- package/dist/cjs/transports/fetch/transport.js.map +1 -1
- package/dist/cjs/transports/fetch/types.js.map +1 -1
- package/dist/cjs/transports/offline/ConnectivityService.js +1 -1
- package/dist/cjs/transports/offline/ConnectivityService.js.map +1 -1
- package/dist/cjs/transports/offline/OfflineCache.js +2 -2
- package/dist/cjs/transports/offline/OfflineCache.js.map +1 -1
- package/dist/cjs/transports/offline/transport.js +3 -4
- package/dist/cjs/transports/offline/transport.js.map +1 -1
- package/dist/cjs/userPersistence/UserPersistence.js +1 -2
- package/dist/cjs/userPersistence/UserPersistence.js.map +1 -1
- package/dist/esm/config/getRNInstrumentations.js +6 -8
- package/dist/esm/config/getRNInstrumentations.js.map +1 -1
- package/dist/esm/config/sampling.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/dataCollection/DataCollectionPolicy.js +1 -2
- package/dist/esm/dataCollection/DataCollectionPolicy.js.map +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/instrumentations/anr/instrumentation.js +6 -1
- package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/appState/index.js +8 -2
- package/dist/esm/instrumentations/appState/index.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +15 -4
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +230 -0
- package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/esm/instrumentations/errors/index.js +8 -3
- package/dist/esm/instrumentations/errors/index.js.map +1 -1
- package/dist/esm/instrumentations/frameMonitoring/instrumentation.js +17 -32
- package/dist/esm/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/frameMonitoring/types.js.map +1 -1
- package/dist/esm/instrumentations/performance/index.js +2 -6
- package/dist/esm/instrumentations/performance/index.js.map +1 -1
- package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js +34 -0
- package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
- package/dist/esm/instrumentations/session/directSessionActivity.js +32 -0
- package/dist/esm/instrumentations/session/directSessionActivity.js.map +1 -0
- package/dist/esm/instrumentations/session/index.js +207 -46
- package/dist/esm/instrumentations/session/index.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionActivity.js +38 -0
- package/dist/esm/instrumentations/session/sessionActivity.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionAttributes.js +7 -7
- package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +190 -27
- package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -14
- package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js +56 -0
- package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js +1 -1
- package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js +120 -44
- package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/types.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionProcess.js +89 -0
- package/dist/esm/instrumentations/session/sessionProcess.js.map +1 -0
- package/dist/esm/instrumentations/startup/index.js +2 -6
- package/dist/esm/instrumentations/startup/index.js.map +1 -1
- package/dist/esm/navigation/utils.js +2 -1
- package/dist/esm/navigation/utils.js.map +1 -1
- package/dist/esm/transports/fetch/index.js.map +1 -1
- package/dist/esm/transports/fetch/transport.js +38 -14
- package/dist/esm/transports/fetch/transport.js.map +1 -1
- package/dist/esm/transports/fetch/types.js.map +1 -1
- package/dist/esm/transports/offline/ConnectivityService.js +1 -1
- package/dist/esm/transports/offline/ConnectivityService.js.map +1 -1
- package/dist/esm/transports/offline/OfflineCache.js +2 -2
- package/dist/esm/transports/offline/OfflineCache.js.map +1 -1
- package/dist/esm/transports/offline/transport.js +3 -4
- package/dist/esm/transports/offline/transport.js.map +1 -1
- package/dist/esm/userPersistence/UserPersistence.js +1 -2
- package/dist/esm/userPersistence/UserPersistence.js.map +1 -1
- package/dist/types/config/getRNInstrumentations.d.ts +1 -2
- package/dist/types/config/sampling.d.ts +0 -3
- package/dist/types/config/types.d.ts +5 -3
- package/dist/types/dataCollection/DataCollectionPolicy.d.ts +1 -2
- package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +14 -4
- package/dist/types/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.d.ts +27 -0
- package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +2 -2
- package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +2 -2
- package/dist/types/instrumentations/crashReporting/types.d.ts +6 -0
- package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +2 -2
- package/dist/types/instrumentations/frameMonitoring/types.d.ts +4 -6
- package/dist/types/instrumentations/performance/index.d.ts +0 -2
- package/dist/types/instrumentations/session/FaroSessionActivityBoundary.d.ts +8 -0
- package/dist/types/instrumentations/session/directSessionActivity.d.ts +13 -0
- package/dist/types/instrumentations/session/index.d.ts +20 -1
- package/dist/types/instrumentations/session/sessionActivity.d.ts +9 -0
- package/dist/types/instrumentations/session/sessionAttributes.d.ts +6 -3
- package/dist/types/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.d.ts +16 -3
- package/dist/types/instrumentations/session/sessionManager/VolatileSessionManager.d.ts +9 -3
- package/dist/types/instrumentations/session/sessionManager/persistentSessionRecord.d.ts +4 -0
- package/dist/types/instrumentations/session/sessionManager/sessionManagerUtils.d.ts +19 -7
- package/dist/types/instrumentations/session/sessionManager/types.d.ts +6 -0
- package/dist/types/instrumentations/session/sessionProcess.d.ts +17 -0
- package/dist/types/instrumentations/startup/index.d.ts +2 -6
- package/dist/types/navigation/utils.d.ts +1 -0
- package/dist/types/transports/fetch/index.d.ts +1 -1
- package/dist/types/transports/fetch/transport.d.ts +8 -1
- package/dist/types/transports/fetch/types.d.ts +5 -0
- package/dist/types/transports/offline/ConnectivityService.d.ts +1 -1
- package/dist/types/transports/offline/OfflineCache.d.ts +2 -2
- package/dist/types/transports/offline/transport.d.ts +3 -4
- package/dist/types/userPersistence/UserPersistence.d.ts +1 -2
- package/ios/FaroCrashReporter.swift +214 -21
- package/ios/FaroReactNative.swift +24 -0
- package/ios/FaroReactNativeModule.h +2 -1
- package/ios/FaroReactNativeModule.mm +121 -6
- package/ios/RefreshRateVitals.swift +34 -38
- package/package.json +7 -5
- package/src/config/getRNInstrumentations.ts +6 -8
- package/src/config/makeRNConfig.test.ts +6 -0
- package/src/config/sampling.ts +0 -3
- package/src/config/types.ts +5 -3
- package/src/dataCollection/DataCollectionPolicy.ts +1 -2
- package/src/generated/faroRNPackageMeta.ts +1 -1
- package/src/index.ts +8 -2
- package/src/instrumentations/anr/instrumentation.ts +8 -1
- package/src/instrumentations/appState/index.ts +10 -2
- package/src/instrumentations/crashReporting/BaseCrashReportingInstrumentation.ts +28 -8
- package/src/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.ts +277 -0
- package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +2 -2
- package/src/instrumentations/crashReporting/instrumentation.test.ts +433 -5
- package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +2 -2
- package/src/instrumentations/crashReporting/types.ts +7 -0
- package/src/instrumentations/errors/index.ts +12 -4
- package/src/instrumentations/errors/instrumentation.test.ts +35 -0
- package/src/instrumentations/frameMonitoring/instrumentation.test.ts +155 -0
- package/src/instrumentations/frameMonitoring/instrumentation.ts +19 -38
- package/src/instrumentations/frameMonitoring/types.ts +4 -6
- package/src/instrumentations/performance/index.ts +2 -6
- package/src/instrumentations/session/FaroSessionActivityBoundary.test.ts +60 -0
- package/src/instrumentations/session/FaroSessionActivityBoundary.tsx +41 -0
- package/src/instrumentations/session/beforeSendHook.test.ts +246 -0
- package/src/instrumentations/session/directSessionActivity.test.ts +158 -0
- package/src/instrumentations/session/directSessionActivity.ts +36 -0
- package/src/instrumentations/session/index.ts +269 -46
- package/src/instrumentations/session/persistentColdStart.test.ts +196 -0
- package/src/instrumentations/session/sessionActivity.test.ts +68 -0
- package/src/instrumentations/session/sessionActivity.ts +58 -0
- package/src/instrumentations/session/sessionAttributes.test.ts +37 -2
- package/src/instrumentations/session/sessionAttributes.ts +12 -6
- package/src/instrumentations/session/sessionCleanup.test.ts +410 -0
- package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.test.ts +552 -0
- package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.ts +249 -33
- package/src/instrumentations/session/sessionManager/VolatileSessionManager.ts +38 -18
- package/src/instrumentations/session/sessionManager/persistentSessionRecord.test.ts +103 -0
- package/src/instrumentations/session/sessionManager/persistentSessionRecord.ts +78 -0
- package/src/instrumentations/session/sessionManager/sampling.test.ts +1 -1
- package/src/instrumentations/session/sessionManager/sessionConstants.ts +1 -1
- package/src/instrumentations/session/sessionManager/sessionManagerUtils.test.ts +256 -18
- package/src/instrumentations/session/sessionManager/sessionManagerUtils.ts +180 -39
- package/src/instrumentations/session/sessionManager/types.ts +8 -0
- package/src/instrumentations/session/sessionManagerInstance.test.ts +65 -0
- package/src/instrumentations/session/sessionProcess.test.ts +136 -0
- package/src/instrumentations/session/sessionProcess.ts +114 -0
- package/src/instrumentations/session/startNewSession.test.ts +337 -0
- package/src/instrumentations/startup/index.ts +2 -6
- package/src/navigation/utils.ts +3 -1
- package/src/transports/fetch/index.ts +6 -1
- package/src/transports/fetch/transport.test.ts +86 -2
- package/src/transports/fetch/transport.ts +38 -16
- package/src/transports/fetch/types.ts +7 -0
- package/src/transports/offline/ConnectivityService.ts +1 -1
- package/src/transports/offline/OfflineCache.ts +2 -2
- package/src/transports/offline/transport.ts +3 -4
- package/src/userPersistence/UserPersistence.ts +1 -2
- package/dist/cjs/utils/throttle.js +0 -42
- package/dist/cjs/utils/throttle.js.map +0 -1
- package/dist/esm/utils/throttle.js +0 -34
- package/dist/esm/utils/throttle.js.map +0 -1
- package/dist/types/utils/throttle.d.ts +0 -9
- package/src/utils/throttle.test.ts +0 -141
- package/src/utils/throttle.ts +0 -34
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
package com.grafana.faro.reactnative
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.test.core.app.ApplicationProvider
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Assert.assertFalse
|
|
7
|
+
import org.junit.Assert.assertNull
|
|
8
|
+
import org.junit.Assert.assertTrue
|
|
9
|
+
import org.junit.Before
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
import org.junit.runner.RunWith
|
|
12
|
+
import org.robolectric.RobolectricTestRunner
|
|
13
|
+
import org.robolectric.annotation.Config
|
|
14
|
+
|
|
15
|
+
@RunWith(RobolectricTestRunner::class)
|
|
16
|
+
@Config(manifest = Config.NONE, sdk = [30])
|
|
17
|
+
class FaroCrashSessionStoreTest {
|
|
18
|
+
|
|
19
|
+
private lateinit var context: Context
|
|
20
|
+
|
|
21
|
+
@Before
|
|
22
|
+
fun setUp() {
|
|
23
|
+
context = ApplicationProvider.getApplicationContext()
|
|
24
|
+
context.getSharedPreferences(
|
|
25
|
+
"com.grafana.faro.crash_session_store",
|
|
26
|
+
Context.MODE_PRIVATE,
|
|
27
|
+
).edit().clear().commit()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Test
|
|
31
|
+
fun recordSessionContext_preservesFirstActivationForSessionUpdates() {
|
|
32
|
+
recordSession("session-a", activatedAtMs = 1_000L, nowMs = 1_000L)
|
|
33
|
+
recordSession("session-a", activatedAtMs = 2_000L, nowMs = 2_000L)
|
|
34
|
+
|
|
35
|
+
val contexts = FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 2_000L)
|
|
36
|
+
|
|
37
|
+
assertEquals(1, contexts.size)
|
|
38
|
+
assertEquals(1_000L, contexts.single().activatedAtMs)
|
|
39
|
+
assertEquals(2_000L, contexts.single().updatedAtMs)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun findMatchingContext_usesLatestSessionBeforeCrashInSameProcess() {
|
|
44
|
+
recordSession("session-a", activatedAtMs = 1_000L, nowMs = 1_000L)
|
|
45
|
+
recordSession("session-b", activatedAtMs = 3_000L, nowMs = 3_000L)
|
|
46
|
+
|
|
47
|
+
val sessionA = FaroCrashSessionStore.findMatchingContext(
|
|
48
|
+
context,
|
|
49
|
+
crashTimestampMs = 2_000L,
|
|
50
|
+
pid = 101,
|
|
51
|
+
processName = "com.example.app",
|
|
52
|
+
nowMs = 4_000L,
|
|
53
|
+
)
|
|
54
|
+
val sessionB = FaroCrashSessionStore.findMatchingContext(
|
|
55
|
+
context,
|
|
56
|
+
crashTimestampMs = 4_000L,
|
|
57
|
+
pid = 101,
|
|
58
|
+
processName = "com.example.app",
|
|
59
|
+
nowMs = 4_000L,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assertEquals("session-a", sessionA?.sessionId)
|
|
63
|
+
assertEquals("session-b", sessionB?.sessionId)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
fun findMatchingContext_doesNotCrossProcessBoundaries() {
|
|
68
|
+
recordSession(
|
|
69
|
+
sessionId = "session-a",
|
|
70
|
+
activatedAtMs = 1_000L,
|
|
71
|
+
nowMs = 1_000L,
|
|
72
|
+
pid = 101,
|
|
73
|
+
)
|
|
74
|
+
recordSession(
|
|
75
|
+
sessionId = "session-b",
|
|
76
|
+
activatedAtMs = 2_000L,
|
|
77
|
+
nowMs = 2_000L,
|
|
78
|
+
pid = 202,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
val match = FaroCrashSessionStore.findMatchingContext(
|
|
82
|
+
context,
|
|
83
|
+
crashTimestampMs = 3_000L,
|
|
84
|
+
pid = 101,
|
|
85
|
+
processName = "com.example.app",
|
|
86
|
+
nowMs = 3_000L,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
assertEquals("session-a", match?.sessionId)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Test
|
|
93
|
+
fun acknowledgeReports_removesOnlyItsOwnSessionContext() {
|
|
94
|
+
recordSession("session-a", activatedAtMs = 1_000L, nowMs = 1_000L, pid = 101)
|
|
95
|
+
recordSession("session-b", activatedAtMs = 2_000L, nowMs = 2_000L, pid = 202)
|
|
96
|
+
val contexts = FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 3_000L)
|
|
97
|
+
val sessionA = contexts.first { it.sessionId == "session-a" }
|
|
98
|
+
val sessionB = contexts.first { it.sessionId == "session-b" }
|
|
99
|
+
|
|
100
|
+
FaroCrashSessionStore.rememberPendingReport(
|
|
101
|
+
context,
|
|
102
|
+
reportId = "report-a",
|
|
103
|
+
crashTimestampMs = 1_500L,
|
|
104
|
+
sessionContext = sessionA,
|
|
105
|
+
usesPendingTrace = true,
|
|
106
|
+
nowMs = 3_000L,
|
|
107
|
+
)
|
|
108
|
+
FaroCrashSessionStore.rememberPendingReport(
|
|
109
|
+
context,
|
|
110
|
+
reportId = "report-b",
|
|
111
|
+
crashTimestampMs = 2_500L,
|
|
112
|
+
sessionContext = sessionB,
|
|
113
|
+
usesPendingTrace = false,
|
|
114
|
+
nowMs = 3_000L,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
val result = FaroCrashSessionStore.acknowledgeReports(
|
|
118
|
+
context,
|
|
119
|
+
reportIds = listOf("report-a"),
|
|
120
|
+
nowMs = 4_000L,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
val remaining = FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 4_000L)
|
|
124
|
+
assertTrue(result.clearPendingTrace)
|
|
125
|
+
assertTrue(FaroCrashSessionStore.isReportAcknowledged(context, "report-a", nowMs = 4_000L))
|
|
126
|
+
assertFalse(FaroCrashSessionStore.isReportAcknowledged(context, "report-b", nowMs = 4_000L))
|
|
127
|
+
assertEquals(listOf("session-b"), remaining.map { it.sessionId })
|
|
128
|
+
assertEquals(
|
|
129
|
+
"session-b",
|
|
130
|
+
FaroCrashSessionStore.contextForPendingReport(context, "report-b", nowMs = 4_000L)?.sessionId,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@Test
|
|
135
|
+
fun acknowledgeReports_keepsContextReferencedByAnotherPendingReport() {
|
|
136
|
+
recordSession("session-a", activatedAtMs = 1_000L, nowMs = 1_000L)
|
|
137
|
+
val sessionA = FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 2_000L).single()
|
|
138
|
+
FaroCrashSessionStore.rememberPendingReport(
|
|
139
|
+
context,
|
|
140
|
+
"report-a",
|
|
141
|
+
1_500L,
|
|
142
|
+
sessionA,
|
|
143
|
+
usesPendingTrace = false,
|
|
144
|
+
nowMs = 2_000L,
|
|
145
|
+
)
|
|
146
|
+
FaroCrashSessionStore.rememberPendingReport(
|
|
147
|
+
context,
|
|
148
|
+
"report-b",
|
|
149
|
+
1_600L,
|
|
150
|
+
sessionA,
|
|
151
|
+
usesPendingTrace = false,
|
|
152
|
+
nowMs = 2_000L,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
FaroCrashSessionStore.acknowledgeReports(context, listOf("report-a"), nowMs = 3_000L)
|
|
156
|
+
|
|
157
|
+
assertEquals(
|
|
158
|
+
"session-a",
|
|
159
|
+
FaroCrashSessionStore.contextForPendingReport(context, "report-b", nowMs = 3_000L)?.sessionId,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@Test
|
|
164
|
+
fun acknowledgeReports_clearsSharedTraceOnlyAfterLastReferencingReport() {
|
|
165
|
+
recordSession("session-a", activatedAtMs = 1_000L, nowMs = 1_000L)
|
|
166
|
+
val sessionA = FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 2_000L).single()
|
|
167
|
+
FaroCrashSessionStore.rememberPendingReport(
|
|
168
|
+
context,
|
|
169
|
+
"report-a",
|
|
170
|
+
1_500L,
|
|
171
|
+
sessionA,
|
|
172
|
+
usesPendingTrace = true,
|
|
173
|
+
nowMs = 2_000L,
|
|
174
|
+
)
|
|
175
|
+
FaroCrashSessionStore.rememberPendingReport(
|
|
176
|
+
context,
|
|
177
|
+
"report-b",
|
|
178
|
+
1_600L,
|
|
179
|
+
sessionA,
|
|
180
|
+
usesPendingTrace = true,
|
|
181
|
+
nowMs = 2_000L,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
val first = FaroCrashSessionStore.acknowledgeReports(context, listOf("report-a"), nowMs = 3_000L)
|
|
185
|
+
val second = FaroCrashSessionStore.acknowledgeReports(context, listOf("report-b"), nowMs = 4_000L)
|
|
186
|
+
|
|
187
|
+
assertTrue(first.persisted)
|
|
188
|
+
assertFalse(first.clearPendingTrace)
|
|
189
|
+
assertTrue(second.persisted)
|
|
190
|
+
assertTrue(second.clearPendingTrace)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@Test
|
|
194
|
+
fun acknowledgeReports_canTombstoneAnIgnoredReportWithoutPendingContext() {
|
|
195
|
+
val result = FaroCrashSessionStore.acknowledgeReports(
|
|
196
|
+
context,
|
|
197
|
+
reportIds = listOf("ignored-report"),
|
|
198
|
+
nowMs = 1_000L,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
assertTrue(result.persisted)
|
|
202
|
+
assertFalse(result.clearPendingTrace)
|
|
203
|
+
assertTrue(FaroCrashSessionStore.isReportAcknowledged(context, "ignored-report", nowMs = 1_000L))
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@Test
|
|
207
|
+
fun sessionContextExpiresOnlyAfterSevenDayBoundary() {
|
|
208
|
+
val sevenDays = FaroCrashSessionStore.MAX_CONTEXT_AGE_MS
|
|
209
|
+
recordSession("at-boundary", activatedAtMs = 1L, nowMs = 1_000L)
|
|
210
|
+
|
|
211
|
+
assertEquals(
|
|
212
|
+
1,
|
|
213
|
+
FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 1_000L + sevenDays).size,
|
|
214
|
+
)
|
|
215
|
+
assertTrue(
|
|
216
|
+
FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 1_001L + sevenDays).isEmpty(),
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@Test
|
|
221
|
+
fun malformedStorageDoesNotPreventNewContextFromBeingRecorded() {
|
|
222
|
+
context.getSharedPreferences(
|
|
223
|
+
"com.grafana.faro.crash_session_store",
|
|
224
|
+
Context.MODE_PRIVATE,
|
|
225
|
+
).edit().putString("session_contexts", "not-json").commit()
|
|
226
|
+
|
|
227
|
+
assertTrue(recordSession("session-a", activatedAtMs = 1_000L, nowMs = 1_000L))
|
|
228
|
+
assertEquals(
|
|
229
|
+
"session-a",
|
|
230
|
+
FaroCrashSessionStore.sessionContextsForTest(context, nowMs = 1_000L).single().sessionId,
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@Test
|
|
235
|
+
fun findMatchingContext_returnsNullWithoutUsableProcessIdentity() {
|
|
236
|
+
recordSession("session-a", activatedAtMs = 1_000L, nowMs = 1_000L)
|
|
237
|
+
|
|
238
|
+
val match = FaroCrashSessionStore.findMatchingContext(
|
|
239
|
+
context,
|
|
240
|
+
crashTimestampMs = 2_000L,
|
|
241
|
+
pid = 0,
|
|
242
|
+
processName = "",
|
|
243
|
+
nowMs = 2_000L,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
assertNull(match)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private fun recordSession(
|
|
250
|
+
sessionId: String,
|
|
251
|
+
activatedAtMs: Long,
|
|
252
|
+
nowMs: Long,
|
|
253
|
+
pid: Int = 101,
|
|
254
|
+
): Boolean {
|
|
255
|
+
return FaroCrashSessionStore.recordSessionContext(
|
|
256
|
+
context = context,
|
|
257
|
+
sessionId = sessionId,
|
|
258
|
+
activatedAtMs = activatedAtMs,
|
|
259
|
+
isSampled = true,
|
|
260
|
+
appVersion = "1.0.0",
|
|
261
|
+
appRelease = "42",
|
|
262
|
+
appBundleId = "com.example.app@42@1.0.0",
|
|
263
|
+
nowMs = nowMs,
|
|
264
|
+
pid = pid,
|
|
265
|
+
processName = "com.example.app",
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package com.grafana.faro.reactnative
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.test.core.app.ApplicationProvider
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Assert.assertNotNull
|
|
7
|
+
import org.junit.Assert.assertNull
|
|
8
|
+
import org.junit.Before
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
import org.junit.runner.RunWith
|
|
11
|
+
import org.robolectric.RobolectricTestRunner
|
|
12
|
+
import org.robolectric.annotation.Config
|
|
13
|
+
|
|
14
|
+
@RunWith(RobolectricTestRunner::class)
|
|
15
|
+
@Config(manifest = Config.NONE, sdk = [30])
|
|
16
|
+
class FaroCrashTraceCacheTest {
|
|
17
|
+
|
|
18
|
+
private lateinit var context: Context
|
|
19
|
+
|
|
20
|
+
@Before
|
|
21
|
+
fun setUp() {
|
|
22
|
+
context = ApplicationProvider.getApplicationContext()
|
|
23
|
+
context.getSharedPreferences(
|
|
24
|
+
"com.grafana.faro.crash_trace_cache",
|
|
25
|
+
Context.MODE_PRIVATE,
|
|
26
|
+
).edit().clear().commit()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun timestampMismatchDoesNotClearTraceForLaterMatchingReport() {
|
|
31
|
+
FaroCrashTraceCache.savePendingCrashTrace(context, "java.lang.RuntimeException", 20_000L)
|
|
32
|
+
val pending = FaroCrashTraceCache.peekPendingCrashTrace(context, 20_001L)
|
|
33
|
+
|
|
34
|
+
assertEquals("", FaroCrashTraceCache.traceForExitTimestamp(context, pending, 1_000L))
|
|
35
|
+
assertNotNull(FaroCrashTraceCache.peekPendingCrashTrace(context, 20_001L))
|
|
36
|
+
assertEquals(
|
|
37
|
+
"java.lang.RuntimeException",
|
|
38
|
+
FaroCrashTraceCache.traceForExitTimestamp(context, pending, 20_000L),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun traceExpiresAfterReplayWindow() {
|
|
44
|
+
val timestamp = 20_000L
|
|
45
|
+
val replayWindow = FaroCrashSessionStore.MAX_CONTEXT_AGE_MS
|
|
46
|
+
FaroCrashTraceCache.savePendingCrashTrace(context, "java.lang.RuntimeException", timestamp)
|
|
47
|
+
|
|
48
|
+
assertNotNull(FaroCrashTraceCache.peekPendingCrashTrace(context, timestamp + replayWindow))
|
|
49
|
+
assertNull(FaroCrashTraceCache.peekPendingCrashTrace(context, timestamp + replayWindow + 1L))
|
|
50
|
+
assertNull(FaroCrashTraceCache.peekPendingCrashTrace(context, timestamp + replayWindow + 1L))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
package com.grafana.faro.reactnative
|
|
2
|
+
|
|
3
|
+
import org.junit.After
|
|
4
|
+
import org.junit.Assert.assertEquals
|
|
5
|
+
import org.junit.Assert.assertFalse
|
|
6
|
+
import org.junit.Assert.assertNull
|
|
7
|
+
import org.junit.Assert.assertTrue
|
|
8
|
+
import org.junit.Before
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
|
|
11
|
+
class FaroSessionProcessTest {
|
|
12
|
+
|
|
13
|
+
@Before
|
|
14
|
+
fun setUp() {
|
|
15
|
+
FaroSessionProcess.resetPersistenceClaimForTest()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@After
|
|
19
|
+
fun tearDown() {
|
|
20
|
+
FaroSessionProcess.resetPersistenceClaimForTest()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Test
|
|
24
|
+
fun normalizeIdentifier_rejectsMissingProcessNames() {
|
|
25
|
+
assertNull(FaroSessionProcess.normalizeIdentifier(null))
|
|
26
|
+
assertNull(FaroSessionProcess.normalizeIdentifier(" "))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun normalizeIdentifier_trimsKnownProcessName() {
|
|
31
|
+
assertEquals(
|
|
32
|
+
"com.example.app:worker",
|
|
33
|
+
FaroSessionProcess.normalizeIdentifier(" com.example.app:worker "),
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Test
|
|
38
|
+
fun identifier_usesThePlatformApiOnAndroidPAndNewer() {
|
|
39
|
+
assertEquals(
|
|
40
|
+
"com.example.app:worker",
|
|
41
|
+
FaroSessionProcess.identifier(
|
|
42
|
+
sdkInt = 28,
|
|
43
|
+
currentProcessName = { "com.example.app:worker" },
|
|
44
|
+
legacyProcessName = { error("legacy lookup should not run") },
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
fun identifier_readsTheNullTerminatedCmdlineBeforeAndroidP() {
|
|
51
|
+
assertEquals(
|
|
52
|
+
"com.example.app:worker",
|
|
53
|
+
FaroSessionProcess.identifier(
|
|
54
|
+
sdkInt = 27,
|
|
55
|
+
currentProcessName = { error("platform lookup should not run") },
|
|
56
|
+
legacyProcessName = { "com.example.app:worker\u0000ignored" },
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Test
|
|
62
|
+
fun identifier_failsClosedWhenTheProcessNameCannotBeRead() {
|
|
63
|
+
assertNull(
|
|
64
|
+
FaroSessionProcess.identifier(
|
|
65
|
+
sdkInt = 27,
|
|
66
|
+
currentProcessName = { error("platform lookup should not run") },
|
|
67
|
+
legacyProcessName = { throw IllegalStateException("unavailable") },
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Test
|
|
73
|
+
fun isMainProcess_comparesCurrentProcessWithDeclaredMainProcess() {
|
|
74
|
+
assertEquals(
|
|
75
|
+
true,
|
|
76
|
+
FaroSessionProcess.isMainProcess(
|
|
77
|
+
currentName = "com.example.app",
|
|
78
|
+
declaredMainName = "com.example.app",
|
|
79
|
+
packageName = "com.example.app",
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
assertEquals(
|
|
83
|
+
false,
|
|
84
|
+
FaroSessionProcess.isMainProcess(
|
|
85
|
+
currentName = "com.example.app:worker",
|
|
86
|
+
declaredMainName = "com.example.app",
|
|
87
|
+
packageName = "com.example.app",
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Test
|
|
93
|
+
fun isMainProcess_usesPackageNameWhenMainProcessIsNotDeclared() {
|
|
94
|
+
assertEquals(
|
|
95
|
+
true,
|
|
96
|
+
FaroSessionProcess.isMainProcess(
|
|
97
|
+
currentName = "com.example.app",
|
|
98
|
+
declaredMainName = " ",
|
|
99
|
+
packageName = "com.example.app",
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Test
|
|
105
|
+
fun isMainProcess_failsClosedWithoutCurrentIdentity() {
|
|
106
|
+
assertNull(
|
|
107
|
+
FaroSessionProcess.isMainProcess(
|
|
108
|
+
currentName = null,
|
|
109
|
+
declaredMainName = "com.example.app",
|
|
110
|
+
packageName = "com.example.app",
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Test
|
|
116
|
+
fun claimPersistence_allowsOnlyOneOwnerAtATime() {
|
|
117
|
+
val firstOwner = Any()
|
|
118
|
+
val secondOwner = Any()
|
|
119
|
+
|
|
120
|
+
assertTrue(FaroSessionProcess.claimPersistence(firstOwner))
|
|
121
|
+
assertTrue(FaroSessionProcess.claimPersistence(firstOwner))
|
|
122
|
+
assertFalse(FaroSessionProcess.claimPersistence(secondOwner))
|
|
123
|
+
assertFalse(FaroSessionProcess.releasePersistence(secondOwner))
|
|
124
|
+
assertTrue(FaroSessionProcess.releasePersistence(firstOwner))
|
|
125
|
+
assertTrue(FaroSessionProcess.claimPersistence(secondOwner))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -22,8 +22,7 @@ function toRegExpArray(patterns) {
|
|
|
22
22
|
/**
|
|
23
23
|
* Returns the default set of instrumentations for React Native.
|
|
24
24
|
*
|
|
25
|
-
* Reads all options from the Faro config (single source of truth)
|
|
26
|
-
* Property names are aligned with Flutter SDK FaroConfig:
|
|
25
|
+
* Reads all options from the Faro config (single source of truth):
|
|
27
26
|
* - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals
|
|
28
27
|
* - enableCrashReporting, enableErrorReporting
|
|
29
28
|
* - fetchVitalsInterval, ignoreUrls
|
|
@@ -44,7 +43,6 @@ function toRegExpArray(patterns) {
|
|
|
44
43
|
*/
|
|
45
44
|
function getRNInstrumentations(config) {
|
|
46
45
|
if (config === void 0) { config = {}; }
|
|
47
|
-
// Aligned with Flutter SDK FaroConfig defaults
|
|
48
46
|
var
|
|
49
47
|
// Error & crash tracking
|
|
50
48
|
_a = config.enableErrorReporting,
|
|
@@ -67,21 +65,21 @@ function getRNInstrumentations(config) {
|
|
|
67
65
|
// Tracing
|
|
68
66
|
enableTracing = _p === void 0 ? false : _p, _q = config.tracingOptions, tracingOptions = _q === void 0 ? {} : _q;
|
|
69
67
|
var instrumentations = [];
|
|
70
|
-
// Error reporting
|
|
68
|
+
// Error reporting
|
|
71
69
|
if (enableErrorReporting) {
|
|
72
70
|
instrumentations.push(new errors_1.ErrorsInstrumentation({
|
|
73
71
|
releaseBundleFilename: config.releaseBundleFilename,
|
|
74
72
|
}));
|
|
75
73
|
}
|
|
76
|
-
// Console capture
|
|
74
|
+
// Console capture
|
|
77
75
|
if (enableConsoleCapture) {
|
|
78
76
|
instrumentations.push(new console_1.ConsoleInstrumentation());
|
|
79
77
|
}
|
|
80
|
-
// Sessions
|
|
78
|
+
// Sessions
|
|
81
79
|
instrumentations.push(new session_1.SessionInstrumentation());
|
|
82
|
-
// Views
|
|
80
|
+
// Views
|
|
83
81
|
instrumentations.push(new view_1.ViewInstrumentation());
|
|
84
|
-
// App state
|
|
82
|
+
// App state
|
|
85
83
|
instrumentations.push(new appState_1.AppStateInstrumentation());
|
|
86
84
|
// User actions - enabled by default, opt-out via enableUserActions
|
|
87
85
|
if (enableUserActions) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { Instrumentation, Patterns } from '@grafana/faro-core';\n\nimport { ANRInstrumentation } from '../instrumentations/anr';\nimport { AppStateInstrumentation } from '../instrumentations/appState';\nimport { ConsoleInstrumentation } from '../instrumentations/console';\nimport { CrashReportingInstrumentation } from '../instrumentations/crashReporting';\nimport { ErrorsInstrumentation } from '../instrumentations/errors';\nimport { FrameMonitoringInstrumentation } from '../instrumentations/frameMonitoring';\nimport { HttpInstrumentation } from '../instrumentations/http';\nimport { PerformanceInstrumentation } from '../instrumentations/performance';\nimport { SessionInstrumentation } from '../instrumentations/session';\nimport { StartupInstrumentation } from '../instrumentations/startup';\nimport { UserActionInstrumentation } from '../instrumentations/userActions';\nimport { ViewInstrumentation } from '../instrumentations/view';\nimport { XHRInstrumentation } from '../instrumentations/xhr';\n\nimport type { ReactNativeConfig } from './types';\n\n/** Convert Patterns (string | RegExp)[] to RegExp[] for instrumentations that require RegExp[]. */\nfunction toRegExpArray(patterns: Patterns): RegExp[] {\n return patterns.map((p) => (typeof p === 'string' ? new RegExp(p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')) : p));\n}\n\n/**\n * Returns the default set of instrumentations for React Native.\n *\n * Reads all options from the Faro config (single source of truth):\n * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals\n * - enableCrashReporting, enableErrorReporting\n * - fetchVitalsInterval, ignoreUrls\n *\n * @example\n * ```ts\n * const config: ReactNativeConfig = {\n * app: { name: 'my-app', version: '1.0.0' },\n * cpuUsageVitals: true,\n * memoryUsageVitals: true,\n * anrTracking: true,\n * refreshRateVitals: true,\n * enableCrashReporting: true,\n * fetchVitalsInterval: 30000,\n * instrumentations: getRNInstrumentations(config),\n * };\n * ```\n */\nexport function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {\n const {\n // Error & crash tracking\n enableErrorReporting = true,\n enableCrashReporting = false,\n anrTracking = false,\n anrOptions = {},\n\n // Performance vitals\n cpuUsageVitals = true,\n memoryUsageVitals = true,\n refreshRateVitals = false,\n fetchVitalsInterval = 30000,\n frameMonitoringOptions = {},\n\n // Network\n ignoreUrls = [],\n enableHttpInstrumentation = {},\n\n // Console and user actions\n enableConsoleCapture = true,\n enableUserActions = true,\n\n // Tracing\n enableTracing = false,\n tracingOptions = {},\n } = config;\n\n const instrumentations: Instrumentation[] = [];\n\n // Error reporting\n if (enableErrorReporting) {\n instrumentations.push(\n new ErrorsInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n // Console capture\n if (enableConsoleCapture) {\n instrumentations.push(new ConsoleInstrumentation());\n }\n\n // Sessions\n instrumentations.push(new SessionInstrumentation());\n\n // Views\n instrumentations.push(new ViewInstrumentation());\n\n // App state\n instrumentations.push(new AppStateInstrumentation());\n\n // User actions - enabled by default, opt-out via enableUserActions\n if (enableUserActions) {\n instrumentations.push(new UserActionInstrumentation());\n }\n\n // HTTP/XHR tracking: when tracing is enabled, TracingInstrumentation patches fetch and XHR.\n // When tracing is disabled, add HttpInstrumentation and/or XHRInstrumentation per enableHttpInstrumentation.\n if (!enableTracing) {\n const { fetch: enableFetch = true, xhr: enableXhr = true } = enableHttpInstrumentation;\n const ignoredUrlsRegExp = toRegExpArray(ignoreUrls);\n if (enableFetch) {\n instrumentations.push(new HttpInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n if (enableXhr) {\n instrumentations.push(new XHRInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n }\n\n // Performance vitals (CPU/memory) - only add if at least one metric is enabled\n if (cpuUsageVitals || memoryUsageVitals) {\n const perfInstrumentation = new PerformanceInstrumentation({\n memoryUsageVitals,\n cpuUsageVitals,\n fetchVitalsInterval,\n });\n instrumentations.push(perfInstrumentation);\n }\n\n // Startup tracking - always enabled\n instrumentations.push(new StartupInstrumentation());\n\n // Frame monitoring: enabled when refreshRateVitals is true\n // The instrumentation reads refreshRateVitals from faro.config (single source of truth)\n if (refreshRateVitals) {\n instrumentations.push(new FrameMonitoringInstrumentation(frameMonitoringOptions));\n }\n\n // ANR detection (Android only)\n if (anrTracking && Platform.OS === 'android') {\n instrumentations.push(new ANRInstrumentation(anrOptions));\n }\n\n // Crash reporting\n if (enableCrashReporting) {\n instrumentations.push(\n new CrashReportingInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n if (enableTracing) {\n try {\n const { TracingInstrumentation } = require('@grafana/faro-react-native-tracing');\n instrumentations.push(new TracingInstrumentation(tracingOptions));\n } catch {\n console.warn(\n '[Faro] enableTracing is true but @grafana/faro-react-native-tracing is not installed. ' +\n 'Add it to use tracing: yarn add @grafana/faro-react-native-tracing'\n );\n }\n }\n\n return instrumentations;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sampling.js","sourceRoot":"","sources":["../../../src/config/sampling.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"sampling.js","sourceRoot":"","sources":["../../../src/config/sampling.ts"],"names":[],"mappings":";;;AA4EA;;;;;;;;;;;;GAYG;AACH;IACE;;;;OAIG;IACH,sBAA4B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAE5C,8BAAO,GAAP,UAAQ,QAAyB;QAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC;IACH,mBAAC;AAAD,CAAC,AAXD,IAWC;AAXY,oCAAY;AAazB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH;IACE;;;;;OAKG;IACH,0BAA6B,EAAwC;QAAxC,OAAE,GAAF,EAAE,CAAsC;IAAG,CAAC;IAEzE,kCAAO,GAAP,UAAQ,OAAwB;QAC9B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IACH,uBAAC;AAAD,CAAC,AAZD,IAYC;AAZY,4CAAgB","sourcesContent":["import type { Meta } from '@grafana/faro-core';\n\n/**\n * Context passed to sampling implementations.\n *\n * Provides access to the current metadata state at SDK initialization time,\n * allowing sampling decisions based on session attributes, user info, app\n * environment, and other metadata.\n *\n * @example\n * ```ts\n * sampling: SamplingFunction((context) => {\n * // Sample all beta users\n * if (context.meta.user?.attributes?.['role'] === 'beta') {\n * return 1;\n * }\n * // Sample 10% of production sessions\n * if (context.meta.app?.environment === 'production') {\n * return 0.1;\n * }\n * return 1;\n * })\n * ```\n */\nexport interface SamplingContext {\n /** The current merged metadata state (session, user, app, sdk, view). */\n meta: Meta;\n}\n\n/**\n * Defines how sessions should be sampled.\n *\n * Implement this interface to provide custom sampling logic. Use one of the\n * built-in implementations:\n * - {@link SamplingRate} for a fixed sampling rate\n * - {@link SamplingFunction} for dynamic sampling based on context\n *\n * @example\n * ```ts\n * // Fixed 10% sampling\n * sessionTracking: {\n * sampling: new SamplingRate(0.1),\n * }\n *\n * // Dynamic sampling based on context\n * sessionTracking: {\n * sampling: new SamplingFunction((context) => {\n * if (context.meta.app?.environment === 'production') {\n * return 0.1;\n * }\n * return 1;\n * }),\n * }\n *\n * // Custom implementation\n * class BetaUserSampling implements Sampling {\n * resolve(context: SamplingContext): number {\n * return context.meta.user?.attributes?.['role'] === 'beta' ? 1 : 0.1;\n * }\n * }\n * sessionTracking: { sampling: new BetaUserSampling() }\n * ```\n */\nexport interface Sampling {\n /**\n * Resolves the sampling rate for the given context.\n *\n * @param context - The current metadata state\n * @returns A value between 0 and 1 (inclusive). Values are clamped to [0, 1].\n * - 1: Sample all sessions (100%)\n * - 0.5: Sample half of sessions (50%)\n * - 0: Sample no sessions (0%)\n */\n resolve(context: SamplingContext): number;\n}\n\n/**\n * Fixed sampling rate.\n *\n * Use this when you want a constant sampling probability regardless of\n * session context.\n *\n * @example\n * ```ts\n * sessionTracking: {\n * sampling: new SamplingRate(0.1), // 10% of sessions\n * }\n * ```\n */\nexport class SamplingRate implements Sampling {\n /**\n * Creates a fixed sampling rate.\n *\n * @param rate - The fixed sampling rate (0 to 1). Values are clamped to [0, 1].\n */\n constructor(public readonly rate: number) {}\n\n resolve(_context: SamplingContext): number {\n return Math.max(0, Math.min(1, this.rate));\n }\n}\n\n/**\n * Dynamic sampling based on context.\n *\n * Use this when you want to make sampling decisions based on session\n * metadata like user attributes, app environment, or other context.\n *\n * @example\n * ```ts\n * sessionTracking: {\n * sampling: new SamplingFunction((context) => {\n * // Sample all beta users\n * if (context.meta.user?.attributes?.['role'] === 'beta') {\n * return 1;\n * }\n * // Sample 10% of production sessions\n * if (context.meta.app?.environment === 'production') {\n * return 0.1;\n * }\n * return 1;\n * }),\n * }\n * ```\n */\nexport class SamplingFunction implements Sampling {\n /**\n * Creates a dynamic sampler with the given function.\n *\n * @param fn - The function that determines the rate based on context.\n * Receives a {@link SamplingContext} and should return a rate between 0 and 1.\n */\n constructor(private readonly fn: (context: SamplingContext) => number) {}\n\n resolve(context: SamplingContext): number {\n return Math.max(0, Math.min(1, this.fn(context)));\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Config } from '@grafana/faro-core';\n\nimport type { ANRInstrumentationOptions } from '../instrumentations/anr';\nimport type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';\nimport type { PreloadedMobileMeta, SessionAttributes } from '../instrumentations/session/sessionAttributes';\n\nimport type { Sampling } from './sampling';\n\n/**\n * React Native session tracking config.\n * Extends Config['sessionTracking'] with sampling
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Config } from '@grafana/faro-core';\n\nimport type { ANRInstrumentationOptions } from '../instrumentations/anr';\nimport type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';\nimport type { PreloadedMobileMeta, SessionAttributes } from '../instrumentations/session/sessionAttributes';\n\nimport type { Sampling } from './sampling';\n\n/**\n * React Native session tracking config.\n * Extends Config['sessionTracking'] with sampling and RN-specific props.\n * Excludes samplingRate and sampler in favor of sampling.\n *\n * Persistence defaults to true and stores a minimal, versioned record with\n * [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv) (synchronous).\n * Add `react-native-mmkv` to your app and rebuild native projects, or set\n * `persistent` to false to use in-memory sessions.\n */\nexport type ReactNativeSessionTrackingConfig = Omit<\n NonNullable<Config['sessionTracking']>,\n 'samplingRate' | 'sampler'\n> & {\n /**\n * Session sampling. Use {@link SamplingRate} for fixed rate or\n * {@link SamplingFunction} for dynamic sampling.\n */\n sampling?: Sampling;\n};\n\n/**\n * Flags for enabling optional built-in transports.\n * FetchTransport is always instantiated when url is provided.\n */\nexport interface EnableTransportsConfig {\n /** Enable OfflineTransport for caching when offline (default: false) */\n offline?: boolean;\n /** Enable ConsoleTransport to log telemetry to Metro console for debugging (default: false) */\n console?: boolean;\n}\n\n/** RN-specific config: overrides + new props. Omit faro-core props we replace with RN equivalents. */\ntype ReactNativeConfigOverrides = {\n /** Application metadata (required) */\n app: Config['app'];\n sessionTracking?: ReactNativeSessionTrackingConfig;\n url?: string;\n apiKey?: string;\n enableTransports?: EnableTransportsConfig;\n /**\n * Which HTTP instrumentations to enable. When tracing is disabled, these control\n * HttpInstrumentation (fetch) and XHRInstrumentation (xhr). Default: both true.\n */\n enableHttpInstrumentation?: {\n /** HttpInstrumentation - patches fetch API (default: true) */\n fetch?: boolean;\n /** XHRInstrumentation - patches XMLHttpRequest (default: true) */\n xhr?: boolean;\n };\n enableTracing?: boolean;\n /** Passed to TracingInstrumentation. See TracingInstrumentationOptions in @grafana/faro-react-native-tracing. */\n tracingOptions?: Record<string, unknown>;\n cpuUsageVitals?: boolean;\n memoryUsageVitals?: boolean;\n refreshRateVitals?: boolean;\n fetchVitalsInterval?: number;\n frameMonitoringOptions?: FrameMonitoringOptions;\n enableErrorReporting?: boolean;\n enableCrashReporting?: boolean;\n anrTracking?: boolean;\n anrOptions?: ANRInstrumentationOptions;\n enableConsoleCapture?: boolean;\n consoleCaptureOptions?: Config['consoleInstrumentation'];\n enableUserActions?: boolean;\n userActionsOptions?: Config['userActionsInstrumentation'];\n /**\n * Hermes / minified release stack lines omit a file path. This must match the Metro plugin\n * source map top-level `file` (default `bundle.js`, or your `sourceMapFile` option there).\n */\n releaseBundleFilename?: string;\n};\n\n/**\n * React Native config: Config (omit replaced props) & overrides.\n * Omit: sessionTracking, consoleInstrumentation, userActionsInstrumentation.\n * instrumentations and transports inherited from Config for extras (built-ins come from flags).\n */\nexport type ReactNativeConfig = Partial<\n Omit<Config, 'sessionTracking' | 'consoleInstrumentation' | 'userActionsInstrumentation'>\n> &\n ReactNativeConfigOverrides;\n\n/**\n * Internal full config returned by `makeRNConfig`.\n * Extends faro-core `Config` with RN-specific fields that instrumentations need at init time.\n */\nexport type ReactNativeFullConfig = Config & {\n preloadedMobileMeta?: PreloadedMobileMeta['meta'];\n preloadedSessionDeviceAttributes?: SessionAttributes;\n};\n"]}
|
|
@@ -55,8 +55,7 @@ var DEFAULT_STORAGE_KEY = 'faro_enable_data_collection';
|
|
|
55
55
|
* Controls whether telemetry data is collected and sent.
|
|
56
56
|
* The setting is persisted to AsyncStorage so it survives app restarts.
|
|
57
57
|
*
|
|
58
|
-
*
|
|
59
|
-
* - Uses AsyncStorage for persistence (matching Flutter's SharedPreferences)
|
|
58
|
+
* Uses AsyncStorage for persisting data collection policy state.
|
|
60
59
|
* - Provides enable/disable methods
|
|
61
60
|
* - Initializes from persisted value or default
|
|
62
61
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataCollectionPolicy.js","sourceRoot":"","sources":["../../../src/dataCollection/DataCollectionPolicy.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"DataCollectionPolicy.js","sourceRoot":"","sources":["../../../src/dataCollection/DataCollectionPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage';\n\nimport type { DataCollectionPolicy, DataCollectionPolicyOptions } from './types';\n\n/**\n * Default storage key for the data collection policy.\n */\nconst DEFAULT_STORAGE_KEY = 'faro_enable_data_collection';\n\n/**\n * DataCollectionPolicy implementation using AsyncStorage.\n *\n * Controls whether telemetry data is collected and sent.\n * The setting is persisted to AsyncStorage so it survives app restarts.\n *\n * Uses AsyncStorage for persisting data collection policy state.\n * - Provides enable/disable methods\n * - Initializes from persisted value or default\n *\n * @example\n * ```typescript\n * import { createDataCollectionPolicy } from '@grafana/faro-react-native';\n *\n * // Create and initialize the policy\n * const policy = await createDataCollectionPolicy();\n *\n * // Check if enabled\n * if (policy.isEnabled) {\n * // Data collection is allowed\n * }\n *\n * // User opts out\n * await policy.disable();\n *\n * // User opts back in\n * await policy.enable();\n * ```\n */\nexport class AsyncStorageDataCollectionPolicy implements DataCollectionPolicy {\n private _isEnabled: boolean;\n private readonly storageKey: string;\n private readonly subscribers: Set<(isEnabled: boolean) => void> = new Set();\n\n private constructor(options: DataCollectionPolicyOptions = {}) {\n this.storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;\n this._isEnabled = options.defaultEnabled ?? true;\n }\n\n /**\n * Create and initialize a DataCollectionPolicy.\n *\n * This async factory method loads the persisted value from AsyncStorage.\n */\n static async create(options: DataCollectionPolicyOptions = {}): Promise<AsyncStorageDataCollectionPolicy> {\n const policy = new AsyncStorageDataCollectionPolicy(options);\n await policy.initialize();\n return policy;\n }\n\n get isEnabled(): boolean {\n return this._isEnabled;\n }\n\n async enable(): Promise<void> {\n this._isEnabled = true;\n await this.persistSetting();\n this.notifySubscribers();\n }\n\n async disable(): Promise<void> {\n this._isEnabled = false;\n await this.persistSetting();\n this.notifySubscribers();\n }\n\n subscribe(callback: (isEnabled: boolean) => void): () => void {\n this.subscribers.add(callback);\n // Immediately notify subscriber of current state\n callback(this._isEnabled);\n\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n private async initialize(): Promise<void> {\n try {\n const storedValue = await AsyncStorage.getItem(this.storageKey);\n if (storedValue !== null) {\n this._isEnabled = storedValue === 'true';\n }\n } catch {\n // If loading fails, use default value\n }\n }\n\n private async persistSetting(): Promise<void> {\n try {\n await AsyncStorage.setItem(this.storageKey, String(this._isEnabled));\n } catch {\n // If persisting fails, log but don't throw\n }\n }\n\n private notifySubscribers(): void {\n this.subscribers.forEach((subscriber) => {\n try {\n subscriber(this._isEnabled);\n } catch {\n // Ignore subscriber errors\n }\n });\n }\n}\n\n/**\n * Factory function to create a DataCollectionPolicy.\n *\n * @example\n * ```typescript\n * const policy = await createDataCollectionPolicy();\n * ```\n */\nexport async function createDataCollectionPolicy(\n options: DataCollectionPolicyOptions = {}\n): Promise<DataCollectionPolicy> {\n return AsyncStorageDataCollectionPolicy.create(options);\n}\n\n/**\n * Global data collection policy instance.\n * Must be initialized before use.\n */\nlet globalPolicy: DataCollectionPolicy | null = null;\n\n/**\n * Initialize the global data collection policy.\n *\n * This should be called early in app initialization, before initializeFaro().\n *\n * @example\n * ```typescript\n * import { initializeDataCollectionPolicy, getDataCollectionPolicy } from '@grafana/faro-react-native';\n *\n * // Early in app startup\n * await initializeDataCollectionPolicy();\n *\n * // Later, check if collection is enabled\n * const policy = getDataCollectionPolicy();\n * if (policy?.isEnabled) {\n * // Initialize Faro\n * }\n * ```\n */\nexport async function initializeDataCollectionPolicy(\n options: DataCollectionPolicyOptions = {}\n): Promise<DataCollectionPolicy> {\n globalPolicy = await createDataCollectionPolicy(options);\n return globalPolicy;\n}\n\n/**\n * Get the global data collection policy.\n *\n * Returns null if not initialized.\n */\nexport function getDataCollectionPolicy(): DataCollectionPolicy | null {\n return globalPolicy;\n}\n\n/**\n * Set a custom data collection policy.\n *\n * Useful for testing or custom implementations.\n */\nexport function setDataCollectionPolicy(policy: DataCollectionPolicy | null): void {\n globalPolicy = policy;\n}\n"]}
|
|
@@ -5,5 +5,5 @@ exports.FARO_REACT_NATIVE_NPM_VERSION = exports.FARO_REACT_NATIVE_NPM_NAME = voi
|
|
|
5
5
|
* NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.
|
|
6
6
|
*/
|
|
7
7
|
exports.FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native';
|
|
8
|
-
exports.FARO_REACT_NATIVE_NPM_VERSION = '1.
|
|
8
|
+
exports.FARO_REACT_NATIVE_NPM_VERSION = '1.4.0';
|
|
9
9
|
//# sourceMappingURL=faroRNPackageMeta.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,0BAA0B,GAAG,4BAAqC,CAAC;AACnE,QAAA,6BAA6B,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.\n */\nexport const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native' as const;\nexport const FARO_REACT_NATIVE_NPM_VERSION = '1.
|
|
1
|
+
{"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,0BAA0B,GAAG,4BAAqC,CAAC;AACnE,QAAA,6BAA6B,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.\n */\nexport const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native' as const;\nexport const FARO_REACT_NATIVE_NPM_VERSION = '1.4.0' as const;\n"]}
|