@grafana/faro-react-native 1.3.0 → 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 +20 -15
- 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/anr/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/appState/index.d.ts +1 -1
- package/dist/types/instrumentations/console/index.d.ts +1 -1
- package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +14 -4
- package/dist/types/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.d.ts +1 -1
- package/dist/types/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.d.ts +27 -0
- package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +3 -3
- package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +3 -3
- package/dist/types/instrumentations/crashReporting/types.d.ts +6 -0
- package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +3 -3
- package/dist/types/instrumentations/frameMonitoring/types.d.ts +4 -6
- package/dist/types/instrumentations/http/index.d.ts +1 -1
- package/dist/types/instrumentations/performance/index.d.ts +1 -3
- 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 +21 -2
- 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 +3 -7
- package/dist/types/instrumentations/userActions/index.d.ts +1 -1
- package/dist/types/instrumentations/view/index.d.ts +1 -1
- package/dist/types/instrumentations/xhr/index.d.ts +1 -1
- package/dist/types/navigation/utils.d.ts +1 -0
- package/dist/types/testUtils/mockFaroReactNativeTracing.d.ts +1 -1
- package/dist/types/transports/console/transport.d.ts +1 -1
- package/dist/types/transports/fetch/index.d.ts +1 -1
- package/dist/types/transports/fetch/transport.d.ts +9 -2
- 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 +4 -5
- 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 +8 -6
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionAttributes.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/sessionAttributes.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\nimport DeviceInfo from 'react-native-device-info';\n\nimport type { Meta } from '@grafana/faro-core';\n\nimport { getInstallationId } from './installationId';\n\n/**\n * Session attributes for React Native\n * These attributes are automatically included with every telemetry event\n *\n * Core attributes match Flutter SDK format, with additional mobile-specific\n * monitoring fields (memory, device type, battery, etc.)\n *\n * SDK name, core version, and npm adapter are on Faro meta `sdk` (`getSdkMeta` in `metas/sdk.ts`).\n *\n * `react_native_version` is the host app's React Native **framework** version from `Platform`, not the Faro package.\n */\nexport interface SessionAttributes {\n /** Host app's React Native framework version (e.g. \"0.75.1\") from `Platform.constants`. */\n react_native_version: string;\n\n /** Operating system (\"iOS\" or \"Android\") */\n device_os?: string;\n\n /** OS version (e.g., \"17.0\" for iOS, \"15\" for Android) */\n device_os_version?: string;\n\n /** Detailed OS info (e.g., \"iOS 17.0\" or \"Android 15 (SDK 35)\") */\n device_os_detail?: string;\n\n /** Device manufacturer (e.g., \"apple\", \"samsung\", \"Google\") */\n device_manufacturer?: string;\n\n /** Device model in flat session attributes (e.g., \"iPhone 15 Pro\", \"SM-A155F\") */\n device_model?: string;\n\n /** Human-readable model name (e.g., \"iPhone 15 Pro\") */\n device_model_name?: string;\n\n /** Device brand in flat session attributes (e.g., \"Apple\", \"samsung\") */\n device_brand?: string;\n\n /** Whether device is physical or emulator (\"true\" or \"false\") */\n device_is_physical?: string;\n\n /** Temporary flat attribute mirroring `app.installationId` during migration. */\n device_id?: string;\n\n /** Device type (\"mobile\" or \"tablet\") */\n device_type?: string;\n\n /** Total device memory in bytes */\n device_memory_total?: string;\n\n /** Currently used memory in bytes */\n device_memory_used?: string;\n\n /** Battery level percentage (e.g., \"85\") - empty if unavailable */\n device_battery_level?: string;\n\n /** Whether device is charging (\"true\" or \"false\") - empty if unavailable */\n device_is_charging?: string;\n\n /** Whether low power mode is enabled (\"true\" or \"false\") - empty if unavailable */\n device_low_power_mode?: string;\n\n /** Mobile carrier name (e.g., \"Verizon\") - empty if unavailable */\n device_carrier?: string;\n}\n\nexport interface PreloadedMobileMeta {\n sessionAttributes: SessionAttributes;\n meta: Pick<Meta, 'app' | 'device' | 'os'>;\n}\n\n/**\n * React Native framework version from `Platform.constants` (host app runtime), not `@grafana/faro-react-native` semver.\n */\nfunction getReactNativeVersion(): string {\n try {\n const version = Platform.constants.reactNativeVersion;\n if (version && typeof version === 'object') {\n const { major, minor, patch, prerelease } = version as {\n major: number;\n minor: number;\n patch: number;\n prerelease?: number;\n };\n let versionString = `${major}.${minor}.${patch}`;\n if (prerelease) {\n versionString += `-rc.${prerelease}`;\n }\n return versionString;\n }\n return 'unknown';\n } catch (_error) {\n return 'unknown';\n }\n}\n\nfunction getStructuredDeviceBrand(model: string, brand: string): string {\n if (Platform.OS !== 'ios') {\n return brand;\n }\n\n return model.toLowerCase().includes('ipad') ? 'iPad' : 'iPhone';\n}\n\nfunction getStructuredDeviceManufacturer(manufacturer: string): string {\n return Platform.OS === 'ios' ? manufacturer.toLowerCase() : manufacturer;\n}\n\nfunction getStructuredDeviceModelIdentifier(model: string): string | undefined {\n // Android getDeviceId() returns a board code, so use Build.MODEL for the Faro\n // model identifier.\n if (Platform.OS === 'android') {\n return model;\n }\n\n // React Native reports iPhone and iPadOS apps as Platform.OS === 'ios'.\n // react-native-device-info exposes the Apple hardware identifier here.\n if (Platform.OS === 'ios') {\n return DeviceInfo.getDeviceId();\n }\n\n return undefined;\n}\n\nfunction getMobileDeviceType(isTablet: boolean): 'mobile' | 'tablet' | undefined {\n // iPadOS is still reported as Platform.OS === 'ios'; DeviceInfo.isTablet()\n // distinguishes iPad/tablet form factor from phone form factor.\n if (Platform.OS === 'ios' || Platform.OS === 'android') {\n return isTablet ? 'tablet' : 'mobile';\n }\n\n return undefined;\n}\n\n/**\n * Get OS detail string matching Flutter SDK format\n * iOS: \"iOS 17.0\"\n * Android: \"Android 15 (SDK 35)\"\n */\nasync function getDeviceOsDetail(): Promise<string> {\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n\n if (Platform.OS === 'android') {\n try {\n const apiLevel = await DeviceInfo.getApiLevel();\n return `${systemName} ${systemVersion} (SDK ${apiLevel})`;\n } catch (_error) {\n return `${systemName} ${systemVersion}`;\n }\n }\n\n return `${systemName} ${systemVersion}`;\n}\n\nasync function getDeviceOsBuildId(): Promise<string | undefined> {\n try {\n // RN device-info exposes a useful OS build id on both Android and iOS.\n // Keep it omitted when unavailable instead of sending \"unknown\".\n const buildId = await DeviceInfo.getBuildId();\n return buildId && buildId !== 'unknown' ? buildId : undefined;\n } catch (_error) {\n return undefined;\n }\n}\n\n/**\n * Session attributes without device props when async collection or DeviceInfo is unavailable.\n * No synchronous DeviceInfo reads — use {@link getSessionAttributes} or the package async `initializeFaro`.\n */\nexport function minimalSessionDeviceAttributes(): SessionAttributes {\n return {\n react_native_version: getReactNativeVersion(),\n };\n}\n\n/**\n * Get all session attributes\n * These attributes are automatically included with every telemetry event\n *\n * Core attributes matching Flutter SDK:\n * - react_native_version (RN framework in the host app)\n * - device_os, device_os_version, device_os_detail\n * - device_manufacturer, device_model, device_model_name\n * - device_brand, device_is_physical, device_id\n *\n * Additional monitoring attributes:\n * - device_type (mobile/tablet)\n * - device_memory_total, device_memory_used\n * - device_battery_level, device_is_charging, device_low_power_mode\n * - device_carrier\n */\nexport async function getSessionAttributes(): Promise<SessionAttributes> {\n return (await collectMobileMeta()).sessionAttributes;\n}\n\nasync function collectMobileMeta(): Promise<PreloadedMobileMeta> {\n try {\n const [deviceOsDetail, deviceOsBuildId, installationId] = await Promise.all([\n getDeviceOsDetail(),\n getDeviceOsBuildId(),\n getInstallationId(),\n ]);\n\n // Get synchronous device info\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n const manufacturer = DeviceInfo.getManufacturerSync();\n const model = DeviceInfo.getModel();\n const modelIdentifier = getStructuredDeviceModelIdentifier(model);\n const deviceName = DeviceInfo.getDeviceNameSync();\n const brand = DeviceInfo.getBrand();\n const isEmulator = DeviceInfo.isEmulatorSync();\n const isTablet = DeviceInfo.isTablet();\n const deviceType = getMobileDeviceType(isTablet);\n\n // Memory info\n const totalMemory = DeviceInfo.getTotalMemorySync();\n const usedMemory = DeviceInfo.getUsedMemorySync();\n const reactNativeVersion = getReactNativeVersion();\n\n // Try to get async device info (battery, carrier)\n let batteryLevel: string | undefined;\n let isCharging: string | undefined;\n let lowPowerMode: string | undefined;\n let carrier: string | undefined;\n\n try {\n const battery = await DeviceInfo.getBatteryLevel();\n if (battery >= 0) {\n batteryLevel = String(Math.round(battery * 100));\n }\n } catch (_error) {\n // Battery info not available\n }\n\n try {\n isCharging = String(await DeviceInfo.isBatteryCharging());\n } catch (_error) {\n // Charging status not available\n }\n\n try {\n if ('isPowerSaveMode' in DeviceInfo) {\n lowPowerMode = String(\n await (DeviceInfo as typeof DeviceInfo & { isPowerSaveMode: () => Promise<boolean> }).isPowerSaveMode()\n );\n }\n } catch (_error) {\n // Low power mode not available\n }\n\n try {\n const carrierName = await DeviceInfo.getCarrier();\n if (carrierName && carrierName !== 'unknown') {\n carrier = carrierName;\n }\n } catch (_error) {\n // Carrier not available\n }\n\n const attributes: SessionAttributes = {\n react_native_version: reactNativeVersion,\n device_os: systemName,\n device_os_version: systemVersion,\n device_os_detail: deviceOsDetail,\n device_manufacturer: manufacturer.toLowerCase(),\n device_model: model,\n device_model_name: deviceName,\n device_brand: brand,\n device_is_physical: String(!isEmulator),\n ...(installationId ? { device_id: installationId } : {}),\n ...(deviceType ? { device_type: deviceType } : {}),\n device_memory_total: String(totalMemory),\n device_memory_used: String(usedMemory),\n device_battery_level: batteryLevel,\n device_is_charging: isCharging,\n device_low_power_mode: lowPowerMode,\n device_carrier: carrier,\n };\n const appMeta = installationId ? { installationId } : {};\n const structuredDeviceBrand = getStructuredDeviceBrand(model, brand);\n const structuredDeviceManufacturer = getStructuredDeviceManufacturer(manufacturer);\n const osMeta = {\n ...(deviceOsBuildId ? { build_id: deviceOsBuildId } : {}),\n detail: deviceOsDetail,\n name: systemName,\n version: systemVersion,\n };\n\n return {\n sessionAttributes: attributes,\n meta: {\n app: appMeta,\n device: {\n brand: structuredDeviceBrand,\n is_physical: !isEmulator,\n manufacturer: structuredDeviceManufacturer,\n ...(modelIdentifier ? { model_identifier: modelIdentifier } : {}),\n model_name: model,\n ...(deviceType ? { type: deviceType } : {}),\n },\n os: osMeta,\n },\n };\n } catch (_error) {\n const installationId = await getInstallationId();\n\n return {\n sessionAttributes: minimalSessionDeviceAttributes(),\n meta: {\n app: installationId ? { installationId } : {},\n },\n };\n }\n}\n\n/**\n * Get structured mobile meta plus flat session attributes for async `initializeFaro`.\n */\nexport async function loadMobileMetaForInit(): Promise<PreloadedMobileMeta> {\n return collectMobileMeta();\n}\n\n/**\n * Await full async session device attributes (battery, carrier, etc.), then fall back to\n * {@link minimalSessionDeviceAttributes} if anything throws. Used by async `initializeFaro`.\n */\nexport async function loadSessionDeviceAttributesForInit(): Promise<SessionAttributes> {\n try {\n return (await loadMobileMetaForInit()).sessionAttributes;\n } catch {\n return minimalSessionDeviceAttributes();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sessionAttributes.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/sessionAttributes.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\nimport DeviceInfo from 'react-native-device-info';\n\nimport type { Meta } from '@grafana/faro-core';\n\nimport { getInstallationId } from './installationId';\nimport { getSessionProcessInfo } from './sessionProcess';\n\n/**\n * Session attributes for React Native\n * These attributes are automatically included with every telemetry event\n *\n * Core mobile session attributes, with additional monitoring fields\n * (memory, device type, battery, etc.)\n *\n * SDK name, core version, and npm adapter are on Faro meta `sdk` (`getSdkMeta` in `metas/sdk.ts`).\n *\n * `react_native_version` is the host app's React Native **framework** version from `Platform`, not the Faro package.\n */\nexport interface SessionAttributes {\n /** Host app's React Native framework version (e.g. \"0.75.1\") from `Platform.constants`. */\n react_native_version: string;\n\n /** Stable Android process name or Apple bundle identifier for this runtime. */\n process_name?: string;\n\n /** Operating system (\"iOS\" or \"Android\") */\n device_os?: string;\n\n /** OS version (e.g., \"17.0\" for iOS, \"15\" for Android) */\n device_os_version?: string;\n\n /** Detailed OS info (e.g., \"iOS 17.0\" or \"Android 15 (SDK 35)\") */\n device_os_detail?: string;\n\n /** Device manufacturer (e.g., \"apple\", \"samsung\", \"Google\") */\n device_manufacturer?: string;\n\n /** Device model in flat session attributes (e.g., \"iPhone 15 Pro\", \"SM-A155F\") */\n device_model?: string;\n\n /** Human-readable model name (e.g., \"iPhone 15 Pro\") */\n device_model_name?: string;\n\n /** Device brand in flat session attributes (e.g., \"Apple\", \"samsung\") */\n device_brand?: string;\n\n /** Whether device is physical or emulator (\"true\" or \"false\") */\n device_is_physical?: string;\n\n /** Temporary flat attribute mirroring `app.installationId` during migration. */\n device_id?: string;\n\n /** Device type (\"mobile\" or \"tablet\") */\n device_type?: string;\n\n /** Total device memory in bytes */\n device_memory_total?: string;\n\n /** Currently used memory in bytes */\n device_memory_used?: string;\n\n /** Battery level percentage (e.g., \"85\") - empty if unavailable */\n device_battery_level?: string;\n\n /** Whether device is charging (\"true\" or \"false\") - empty if unavailable */\n device_is_charging?: string;\n\n /** Whether low power mode is enabled (\"true\" or \"false\") - empty if unavailable */\n device_low_power_mode?: string;\n\n /** Mobile carrier name (e.g., \"Verizon\") - empty if unavailable */\n device_carrier?: string;\n}\n\nexport interface PreloadedMobileMeta {\n sessionAttributes: SessionAttributes;\n meta: Pick<Meta, 'app' | 'device' | 'os'>;\n}\n\n/**\n * React Native framework version from `Platform.constants` (host app runtime), not `@grafana/faro-react-native` semver.\n */\nfunction getReactNativeVersion(): string {\n try {\n const version = Platform.constants.reactNativeVersion;\n if (version && typeof version === 'object') {\n const { major, minor, patch, prerelease } = version as {\n major: number;\n minor: number;\n patch: number;\n prerelease?: number;\n };\n let versionString = `${major}.${minor}.${patch}`;\n if (prerelease) {\n versionString += `-rc.${prerelease}`;\n }\n return versionString;\n }\n return 'unknown';\n } catch (_error) {\n return 'unknown';\n }\n}\n\nfunction getStructuredDeviceBrand(model: string, brand: string): string {\n if (Platform.OS !== 'ios') {\n return brand;\n }\n\n return model.toLowerCase().includes('ipad') ? 'iPad' : 'iPhone';\n}\n\nfunction getStructuredDeviceManufacturer(manufacturer: string): string {\n return Platform.OS === 'ios' ? manufacturer.toLowerCase() : manufacturer;\n}\n\nfunction getStructuredDeviceModelIdentifier(model: string): string | undefined {\n // Android getDeviceId() returns a board code, so use Build.MODEL for the Faro\n // model identifier.\n if (Platform.OS === 'android') {\n return model;\n }\n\n // React Native reports iPhone and iPadOS apps as Platform.OS === 'ios'.\n // react-native-device-info exposes the Apple hardware identifier here.\n if (Platform.OS === 'ios') {\n return DeviceInfo.getDeviceId();\n }\n\n return undefined;\n}\n\nfunction getMobileDeviceType(isTablet: boolean): 'mobile' | 'tablet' | undefined {\n // iPadOS is still reported as Platform.OS === 'ios'; DeviceInfo.isTablet()\n // distinguishes iPad/tablet form factor from phone form factor.\n if (Platform.OS === 'ios' || Platform.OS === 'android') {\n return isTablet ? 'tablet' : 'mobile';\n }\n\n return undefined;\n}\n\n/**\n * Get OS detail string for session attributes.\n * iOS: \"iOS 17.0\"\n * Android: \"Android 15 (SDK 35)\"\n */\nasync function getDeviceOsDetail(): Promise<string> {\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n\n if (Platform.OS === 'android') {\n try {\n const apiLevel = await DeviceInfo.getApiLevel();\n return `${systemName} ${systemVersion} (SDK ${apiLevel})`;\n } catch (_error) {\n return `${systemName} ${systemVersion}`;\n }\n }\n\n return `${systemName} ${systemVersion}`;\n}\n\nasync function getDeviceOsBuildId(): Promise<string | undefined> {\n try {\n // RN device-info exposes a useful OS build id on both Android and iOS.\n // Keep it omitted when unavailable instead of sending \"unknown\".\n const buildId = await DeviceInfo.getBuildId();\n return buildId && buildId !== 'unknown' ? buildId : undefined;\n } catch (_error) {\n return undefined;\n }\n}\n\n/**\n * Session attributes without device props when async collection or DeviceInfo is unavailable.\n * No synchronous DeviceInfo reads — use {@link getSessionAttributes} or the package async `initializeFaro`.\n */\nexport function minimalSessionDeviceAttributes(): SessionAttributes {\n const processInfo = getSessionProcessInfo();\n return {\n react_native_version: getReactNativeVersion(),\n ...(processInfo == null ? {} : { process_name: processInfo.identifier }),\n };\n}\n\n/**\n * Get all session attributes\n * These attributes are automatically included with every telemetry event\n *\n * Core session attributes:\n * - react_native_version (RN framework in the host app)\n * - process_name (Android process name or iOS bundle identifier)\n * - device_os, device_os_version, device_os_detail\n * - device_manufacturer, device_model, device_model_name\n * - device_brand, device_is_physical, device_id\n *\n * Additional monitoring attributes:\n * - device_type (mobile/tablet)\n * - device_memory_total, device_memory_used\n * - device_battery_level, device_is_charging, device_low_power_mode\n * - device_carrier\n */\nexport async function getSessionAttributes(): Promise<SessionAttributes> {\n return (await collectMobileMeta()).sessionAttributes;\n}\n\nasync function collectMobileMeta(): Promise<PreloadedMobileMeta> {\n try {\n const [deviceOsDetail, deviceOsBuildId, installationId] = await Promise.all([\n getDeviceOsDetail(),\n getDeviceOsBuildId(),\n getInstallationId(),\n ]);\n\n // Get synchronous device info\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n const manufacturer = DeviceInfo.getManufacturerSync();\n const model = DeviceInfo.getModel();\n const modelIdentifier = getStructuredDeviceModelIdentifier(model);\n const deviceName = DeviceInfo.getDeviceNameSync();\n const brand = DeviceInfo.getBrand();\n const isEmulator = DeviceInfo.isEmulatorSync();\n const isTablet = DeviceInfo.isTablet();\n const deviceType = getMobileDeviceType(isTablet);\n\n // Memory info\n const totalMemory = DeviceInfo.getTotalMemorySync();\n const usedMemory = DeviceInfo.getUsedMemorySync();\n\n // Try to get async device info (battery, carrier)\n let batteryLevel: string | undefined;\n let isCharging: string | undefined;\n let lowPowerMode: string | undefined;\n let carrier: string | undefined;\n\n try {\n const battery = await DeviceInfo.getBatteryLevel();\n if (battery >= 0) {\n batteryLevel = String(Math.round(battery * 100));\n }\n } catch (_error) {\n // Battery info not available\n }\n\n try {\n isCharging = String(await DeviceInfo.isBatteryCharging());\n } catch (_error) {\n // Charging status not available\n }\n\n try {\n if ('isPowerSaveMode' in DeviceInfo) {\n lowPowerMode = String(\n await (DeviceInfo as typeof DeviceInfo & { isPowerSaveMode: () => Promise<boolean> }).isPowerSaveMode()\n );\n }\n } catch (_error) {\n // Low power mode not available\n }\n\n try {\n const carrierName = await DeviceInfo.getCarrier();\n if (carrierName && carrierName !== 'unknown') {\n carrier = carrierName;\n }\n } catch (_error) {\n // Carrier not available\n }\n\n const attributes: SessionAttributes = {\n ...minimalSessionDeviceAttributes(),\n device_os: systemName,\n device_os_version: systemVersion,\n device_os_detail: deviceOsDetail,\n device_manufacturer: manufacturer.toLowerCase(),\n device_model: model,\n device_model_name: deviceName,\n device_brand: brand,\n device_is_physical: String(!isEmulator),\n ...(installationId ? { device_id: installationId } : {}),\n ...(deviceType ? { device_type: deviceType } : {}),\n device_memory_total: String(totalMemory),\n device_memory_used: String(usedMemory),\n device_battery_level: batteryLevel,\n device_is_charging: isCharging,\n device_low_power_mode: lowPowerMode,\n device_carrier: carrier,\n };\n const appMeta = installationId ? { installationId } : {};\n const structuredDeviceBrand = getStructuredDeviceBrand(model, brand);\n const structuredDeviceManufacturer = getStructuredDeviceManufacturer(manufacturer);\n const osMeta = {\n ...(deviceOsBuildId ? { build_id: deviceOsBuildId } : {}),\n detail: deviceOsDetail,\n name: systemName,\n version: systemVersion,\n };\n\n return {\n sessionAttributes: attributes,\n meta: {\n app: appMeta,\n device: {\n brand: structuredDeviceBrand,\n is_physical: !isEmulator,\n manufacturer: structuredDeviceManufacturer,\n ...(modelIdentifier ? { model_identifier: modelIdentifier } : {}),\n model_name: model,\n ...(deviceType ? { type: deviceType } : {}),\n },\n os: osMeta,\n },\n };\n } catch (_error) {\n const installationId = await getInstallationId();\n\n return {\n sessionAttributes: minimalSessionDeviceAttributes(),\n meta: {\n app: installationId ? { installationId } : {},\n },\n };\n }\n}\n\n/**\n * Get structured mobile meta plus flat session attributes for async `initializeFaro`.\n */\nexport async function loadMobileMetaForInit(): Promise<PreloadedMobileMeta> {\n return collectMobileMeta();\n}\n\n/**\n * Await full async session device attributes (battery, carrier, etc.), then fall back to\n * {@link minimalSessionDeviceAttributes} if anything throws. Used by async `initializeFaro`.\n */\nexport async function loadSessionDeviceAttributesForInit(): Promise<SessionAttributes> {\n try {\n return (await loadMobileMetaForInit()).sessionAttributes;\n } catch {\n return minimalSessionDeviceAttributes();\n }\n}\n"]}
|
|
@@ -1,48 +1,174 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
exports.MmkvPersistentSessionsManager = void 0;
|
|
4
15
|
exports.resetMmkvSingletonForTests = resetMmkvSingletonForTests;
|
|
5
16
|
var react_native_1 = require("react-native");
|
|
6
17
|
var faro_core_1 = require("@grafana/faro-core");
|
|
7
|
-
var
|
|
18
|
+
var sessionActivity_1 = require("../sessionActivity");
|
|
19
|
+
var sessionProcess_1 = require("../sessionProcess");
|
|
20
|
+
var persistentSessionRecord_1 = require("./persistentSessionRecord");
|
|
8
21
|
var sessionConstants_1 = require("./sessionConstants");
|
|
9
22
|
var sessionManagerUtils_1 = require("./sessionManagerUtils");
|
|
23
|
+
function errorMessage(error) {
|
|
24
|
+
return error instanceof Error ? error.message : String(error);
|
|
25
|
+
}
|
|
26
|
+
function loadMmkvModule() {
|
|
27
|
+
try {
|
|
28
|
+
return require('react-native-mmkv');
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw new Error("react-native-mmkv could not be loaded. Install it and rebuild the native project. ".concat(errorMessage(error)));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function mmkvConfiguration(mmkv, storageId, isIosExtension) {
|
|
35
|
+
var _a;
|
|
36
|
+
if (!isIosExtension) {
|
|
37
|
+
return { id: storageId };
|
|
38
|
+
}
|
|
39
|
+
if (typeof mmkv.createMMKV === 'function') {
|
|
40
|
+
return { id: storageId, mode: 'multi-process' };
|
|
41
|
+
}
|
|
42
|
+
var legacyMultiProcessMode = (_a = mmkv.Mode) === null || _a === void 0 ? void 0 : _a.MULTI_PROCESS;
|
|
43
|
+
if (typeof legacyMultiProcessMode !== 'number') {
|
|
44
|
+
throw new Error('Persisting sessions in an iOS extension requires react-native-mmkv v3 or newer.');
|
|
45
|
+
}
|
|
46
|
+
return { id: storageId, mode: legacyMultiProcessMode };
|
|
47
|
+
}
|
|
10
48
|
function createMmkvInstance() {
|
|
49
|
+
// Load MMKV before taking native ownership so a missing dependency cannot
|
|
50
|
+
// leave the process-wide claim occupied.
|
|
51
|
+
var mmkv = loadMmkvModule();
|
|
52
|
+
var processInfo = (0, sessionProcess_1.getSessionProcessInfo)();
|
|
53
|
+
var storageId = (0, sessionProcess_1.claimSessionPersistenceStorageId)();
|
|
54
|
+
if (storageId == null || processInfo == null) {
|
|
55
|
+
throw new Error('Native session-storage coordination is unavailable or already owned by another runtime. Rebuild the native app after upgrading Faro, or use sessionTracking.persistent=false in runtimes that cannot own storage.');
|
|
56
|
+
}
|
|
11
57
|
try {
|
|
12
|
-
var
|
|
13
|
-
|
|
58
|
+
var configuration = mmkvConfiguration(mmkv, storageId, react_native_1.Platform.OS === 'ios' && !processInfo.isMain);
|
|
59
|
+
// react-native-mmkv v4 was rewritten to Nitro and removed the `new MMKV()`
|
|
60
|
+
// class constructor in favor of a `createMMKV()` factory (see the v4 upgrade
|
|
61
|
+
// guide). Support both so v2/v3 (class) and v4+ (factory) work.
|
|
62
|
+
if (typeof mmkv.createMMKV === 'function') {
|
|
63
|
+
return mmkv.createMMKV(configuration);
|
|
64
|
+
}
|
|
65
|
+
if (typeof mmkv.MMKV !== 'function') {
|
|
66
|
+
throw new Error('The installed react-native-mmkv package does not expose a supported storage API.');
|
|
67
|
+
}
|
|
68
|
+
return new mmkv.MMKV(configuration);
|
|
14
69
|
}
|
|
15
|
-
catch (
|
|
16
|
-
|
|
70
|
+
catch (error) {
|
|
71
|
+
(0, sessionProcess_1.releaseSessionPersistenceOwnership)();
|
|
72
|
+
throw new Error("Session persistence could not be initialized. ".concat(errorMessage(error)));
|
|
17
73
|
}
|
|
18
74
|
}
|
|
75
|
+
// undefined means MMKV has not been resolved; null means it is unavailable.
|
|
19
76
|
var mmkvSingleton;
|
|
20
|
-
|
|
21
|
-
|
|
77
|
+
// undefined means storage has not been read, null means no persisted state is
|
|
78
|
+
// available, and a session is the live value for this JavaScript runtime.
|
|
79
|
+
var runtimeSession;
|
|
80
|
+
function tryGetMmkv() {
|
|
81
|
+
var _a, _b;
|
|
82
|
+
if (mmkvSingleton !== undefined) {
|
|
83
|
+
return mmkvSingleton;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
22
86
|
mmkvSingleton = createMmkvInstance();
|
|
23
87
|
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
mmkvSingleton = null;
|
|
90
|
+
(_b = (_a = faro_core_1.faro.unpatchedConsole) === null || _a === void 0 ? void 0 : _a.warn) === null || _b === void 0 ? void 0 : _b.call(_a, 'Session persistence is unavailable; using an in-memory session:', error);
|
|
91
|
+
}
|
|
24
92
|
return mmkvSingleton;
|
|
25
93
|
}
|
|
94
|
+
function removeMmkvValue(mmkv, key) {
|
|
95
|
+
var compatibleMmkv = mmkv;
|
|
96
|
+
if (typeof compatibleMmkv.remove === 'function') {
|
|
97
|
+
compatibleMmkv.remove(key);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (typeof compatibleMmkv.delete === 'function') {
|
|
101
|
+
compatibleMmkv.delete(key);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
throw new Error('The installed react-native-mmkv version cannot remove stored values.');
|
|
105
|
+
}
|
|
26
106
|
/** @internal */
|
|
27
107
|
function resetMmkvSingletonForTests() {
|
|
28
108
|
mmkvSingleton = undefined;
|
|
109
|
+
runtimeSession = undefined;
|
|
29
110
|
}
|
|
30
111
|
/**
|
|
31
112
|
* Persistent session storage backed by MMKV (synchronous reads/writes).
|
|
32
113
|
* Used when `sessionTracking.persistent` is true.
|
|
33
114
|
*/
|
|
34
115
|
var MmkvPersistentSessionsManager = /** @class */ (function () {
|
|
35
|
-
function MmkvPersistentSessionsManager() {
|
|
116
|
+
function MmkvPersistentSessionsManager(metas) {
|
|
117
|
+
if (metas === void 0) { metas = faro_core_1.faro.metas; }
|
|
36
118
|
var _this = this;
|
|
37
119
|
this.appStateSubscription = null;
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
120
|
+
this.activityWriteTimer = null;
|
|
121
|
+
this.hasPendingActivityWrite = false;
|
|
122
|
+
this.lastActivityWrite = 0;
|
|
123
|
+
this.wasBackgrounded = react_native_1.AppState.currentState === 'background';
|
|
124
|
+
this.metaUpdateHandler = null;
|
|
125
|
+
this.flushActivityWrite = function () {
|
|
126
|
+
if (_this.activityWriteTimer != null) {
|
|
127
|
+
clearTimeout(_this.activityWriteTimer);
|
|
128
|
+
_this.activityWriteTimer = null;
|
|
129
|
+
}
|
|
130
|
+
if (!_this.hasPendingActivityWrite) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
var session = MmkvPersistentSessionsManager.fetchUserSession();
|
|
134
|
+
_this.hasPendingActivityWrite = false;
|
|
135
|
+
if (session == null) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
MmkvPersistentSessionsManager.persistSession(session);
|
|
139
|
+
_this.lastActivityWrite = (0, faro_core_1.dateNow)();
|
|
140
|
+
};
|
|
141
|
+
this.recordUserSessionActivity = function (session) {
|
|
142
|
+
// Keep expiry checks exact in memory while limiting native storage writes.
|
|
143
|
+
MmkvPersistentSessionsManager.setRuntimeSession(session);
|
|
144
|
+
_this.hasPendingActivityWrite = true;
|
|
145
|
+
var elapsed = (0, faro_core_1.dateNow)() - _this.lastActivityWrite;
|
|
146
|
+
if (_this.lastActivityWrite === 0 || elapsed < 0 || elapsed >= sessionConstants_1.STORAGE_UPDATE_DELAY) {
|
|
147
|
+
_this.flushActivityWrite();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (_this.activityWriteTimer == null) {
|
|
151
|
+
_this.activityWriteTimer = setTimeout(_this.flushActivityWrite, sessionConstants_1.STORAGE_UPDATE_DELAY - elapsed);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
40
154
|
this.handleAppStateChange = function (nextAppState) {
|
|
41
|
-
if (nextAppState === '
|
|
42
|
-
_this.
|
|
155
|
+
if (nextAppState === 'background') {
|
|
156
|
+
_this.wasBackgrounded = true;
|
|
157
|
+
_this.flushActivityWrite();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (nextAppState === 'active' && _this.wasBackgrounded) {
|
|
161
|
+
_this.wasBackgrounded = false;
|
|
162
|
+
_this.checkSession(sessionActivity_1.SessionActivityKind.Meaningful);
|
|
43
163
|
}
|
|
44
164
|
};
|
|
165
|
+
this.metas = metas;
|
|
45
166
|
this.updateUserSession = (0, sessionManagerUtils_1.getUserSessionUpdater)({
|
|
167
|
+
fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,
|
|
168
|
+
recordUserSessionActivity: this.recordUserSessionActivity,
|
|
169
|
+
storeUserSession: MmkvPersistentSessionsManager.storeUserSession,
|
|
170
|
+
});
|
|
171
|
+
this.resetUserSession = (0, sessionManagerUtils_1.getUserSessionResetter)({
|
|
46
172
|
fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,
|
|
47
173
|
storeUserSession: MmkvPersistentSessionsManager.storeUserSession,
|
|
48
174
|
});
|
|
@@ -50,17 +176,36 @@ var MmkvPersistentSessionsManager = /** @class */ (function () {
|
|
|
50
176
|
}
|
|
51
177
|
MmkvPersistentSessionsManager.removeUserSession = function () {
|
|
52
178
|
var _a, _b;
|
|
179
|
+
runtimeSession = null;
|
|
180
|
+
var mmkv = tryGetMmkv();
|
|
181
|
+
if (mmkv == null) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
53
184
|
try {
|
|
54
|
-
|
|
185
|
+
removeMmkvValue(mmkv, sessionConstants_1.STORAGE_KEY);
|
|
55
186
|
}
|
|
56
187
|
catch (error) {
|
|
57
188
|
(_b = (_a = faro_core_1.faro.unpatchedConsole) === null || _a === void 0 ? void 0 : _a.warn) === null || _b === void 0 ? void 0 : _b.call(_a, 'Failed to remove session from MMKV:', error);
|
|
58
189
|
}
|
|
59
190
|
};
|
|
60
191
|
MmkvPersistentSessionsManager.storeUserSession = function (session) {
|
|
192
|
+
MmkvPersistentSessionsManager.setRuntimeSession(session);
|
|
193
|
+
MmkvPersistentSessionsManager.persistSession(session);
|
|
194
|
+
};
|
|
195
|
+
MmkvPersistentSessionsManager.setRuntimeSession = function (session) {
|
|
196
|
+
runtimeSession =
|
|
197
|
+
session.sessionMeta == null
|
|
198
|
+
? session
|
|
199
|
+
: __assign(__assign({}, session), { sessionMeta: (0, sessionManagerUtils_1.toStorableSessionMeta)(session.sessionMeta) });
|
|
200
|
+
};
|
|
201
|
+
MmkvPersistentSessionsManager.persistSession = function (session) {
|
|
61
202
|
var _a, _b;
|
|
203
|
+
var mmkv = tryGetMmkv();
|
|
204
|
+
if (mmkv == null) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
62
207
|
try {
|
|
63
|
-
|
|
208
|
+
mmkv.set(sessionConstants_1.STORAGE_KEY, (0, persistentSessionRecord_1.serializePersistentSession)(session));
|
|
64
209
|
}
|
|
65
210
|
catch (error) {
|
|
66
211
|
(_b = (_a = faro_core_1.faro.unpatchedConsole) === null || _a === void 0 ? void 0 : _a.warn) === null || _b === void 0 ? void 0 : _b.call(_a, 'Failed to store session in MMKV:', error);
|
|
@@ -68,34 +213,64 @@ var MmkvPersistentSessionsManager = /** @class */ (function () {
|
|
|
68
213
|
};
|
|
69
214
|
MmkvPersistentSessionsManager.fetchUserSession = function () {
|
|
70
215
|
var _a, _b;
|
|
216
|
+
if (runtimeSession !== undefined) {
|
|
217
|
+
return runtimeSession;
|
|
218
|
+
}
|
|
219
|
+
var mmkv = tryGetMmkv();
|
|
220
|
+
if (mmkv == null) {
|
|
221
|
+
runtimeSession = null;
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
71
224
|
try {
|
|
72
|
-
var storedSession =
|
|
73
|
-
if (storedSession) {
|
|
74
|
-
|
|
225
|
+
var storedSession = mmkv.getString(sessionConstants_1.STORAGE_KEY);
|
|
226
|
+
if (storedSession == null) {
|
|
227
|
+
runtimeSession = null;
|
|
228
|
+
return null;
|
|
75
229
|
}
|
|
76
|
-
|
|
230
|
+
runtimeSession = (0, persistentSessionRecord_1.parsePersistentSession)(storedSession);
|
|
231
|
+
if (runtimeSession == null) {
|
|
232
|
+
removeMmkvValue(mmkv, sessionConstants_1.STORAGE_KEY);
|
|
233
|
+
}
|
|
234
|
+
return runtimeSession;
|
|
77
235
|
}
|
|
78
236
|
catch (error) {
|
|
237
|
+
runtimeSession = null;
|
|
79
238
|
(_b = (_a = faro_core_1.faro.unpatchedConsole) === null || _a === void 0 ? void 0 : _a.warn) === null || _b === void 0 ? void 0 : _b.call(_a, 'Failed to fetch session from MMKV:', error);
|
|
80
239
|
return null;
|
|
81
240
|
}
|
|
82
241
|
};
|
|
242
|
+
MmkvPersistentSessionsManager.prototype.checkSession = function (activity, currentSession) {
|
|
243
|
+
return this.updateUserSession(activity, currentSession);
|
|
244
|
+
};
|
|
245
|
+
MmkvPersistentSessionsManager.prototype.resetSession = function () {
|
|
246
|
+
if (this.activityWriteTimer != null) {
|
|
247
|
+
clearTimeout(this.activityWriteTimer);
|
|
248
|
+
this.activityWriteTimer = null;
|
|
249
|
+
}
|
|
250
|
+
this.hasPendingActivityWrite = false;
|
|
251
|
+
var session = this.resetUserSession();
|
|
252
|
+
this.lastActivityWrite = (0, faro_core_1.dateNow)();
|
|
253
|
+
return session;
|
|
254
|
+
};
|
|
83
255
|
MmkvPersistentSessionsManager.prototype.init = function () {
|
|
84
256
|
this.appStateSubscription = react_native_1.AppState.addEventListener('change', this.handleAppStateChange);
|
|
85
|
-
var
|
|
257
|
+
var metaUpdateHandler = (0, sessionManagerUtils_1.getSessionMetaUpdateHandler)({
|
|
86
258
|
fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,
|
|
87
259
|
storeUserSession: MmkvPersistentSessionsManager.storeUserSession,
|
|
88
|
-
})
|
|
89
|
-
this.
|
|
260
|
+
});
|
|
261
|
+
this.metas.addListener(metaUpdateHandler);
|
|
262
|
+
this.metaUpdateHandler = metaUpdateHandler;
|
|
90
263
|
};
|
|
91
264
|
MmkvPersistentSessionsManager.prototype.unpatch = function () {
|
|
265
|
+
this.flushActivityWrite();
|
|
92
266
|
if (this.appStateSubscription) {
|
|
93
267
|
this.appStateSubscription.remove();
|
|
94
268
|
this.appStateSubscription = null;
|
|
95
269
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
270
|
+
var metaUpdateHandler = this.metaUpdateHandler;
|
|
271
|
+
this.metaUpdateHandler = null;
|
|
272
|
+
if (metaUpdateHandler) {
|
|
273
|
+
this.metas.removeListener(metaUpdateHandler);
|
|
99
274
|
}
|
|
100
275
|
};
|
|
101
276
|
return MmkvPersistentSessionsManager;
|
package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MmkvPersistentSessionsManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppState, type AppStateStatus } from 'react-native';\nimport type { MMKV } from 'react-native-mmkv';\n\nimport { faro, stringifyExternalJson } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils/throttle';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nfunction createMmkvInstance(): MMKV {\n try {\n const { MMKV } = require('react-native-mmkv');\n return new MMKV({ id: 'grafana-faro-react-native-session' });\n } catch {\n throw new Error(\n 'sessionTracking.persistent is true but react-native-mmkv could not be loaded. Install it: yarn add react-native-mmkv, then rebuild native projects.'\n );\n }\n}\n\nlet mmkvSingleton: MMKV | undefined;\n\nfunction getMmkv(): MMKV {\n if (mmkvSingleton == null) {\n mmkvSingleton = createMmkvInstance();\n }\n return mmkvSingleton;\n}\n\n/** @internal */\nexport function resetMmkvSingletonForTests(): void {\n mmkvSingleton = undefined;\n}\n\n/**\n * Persistent session storage backed by MMKV (synchronous reads/writes).\n * Used when `sessionTracking.persistent` is true.\n */\nexport class MmkvPersistentSessionsManager {\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n private appStateSubscription: ReturnType<typeof AppState.addEventListener> | null = null;\n private metaUnsubscribe: (() => void) | null = null;\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,\n storeUserSession: MmkvPersistentSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession(): void {\n try {\n getMmkv().remove(STORAGE_KEY);\n } catch (error) {\n faro.unpatchedConsole?.warn?.('Failed to remove session from MMKV:', error);\n }\n }\n\n static storeUserSession(session: FaroUserSession): void {\n try {\n getMmkv().set(STORAGE_KEY, stringifyExternalJson(session));\n } catch (error) {\n faro.unpatchedConsole?.warn?.('Failed to store session in MMKV:', error);\n }\n }\n\n static fetchUserSession(): FaroUserSession | null {\n try {\n const storedSession = getMmkv().getString(STORAGE_KEY);\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n return null;\n } catch (error) {\n faro.unpatchedConsole?.warn?.('Failed to fetch session from MMKV:', error);\n return null;\n }\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private handleAppStateChange = (nextAppState: AppStateStatus) => {\n if (nextAppState === 'active') {\n this.updateSession();\n }\n };\n\n private init(): void {\n this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);\n\n const unsubscribe = faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,\n storeUserSession: MmkvPersistentSessionsManager.storeUserSession,\n })\n );\n this.metaUnsubscribe = typeof unsubscribe === 'function' ? unsubscribe : null;\n }\n\n unpatch(): void {\n if (this.appStateSubscription) {\n this.appStateSubscription.remove();\n this.appStateSubscription = null;\n }\n\n if (this.metaUnsubscribe) {\n this.metaUnsubscribe();\n this.metaUnsubscribe = null;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MmkvPersistentSessionsManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppState, type AppStateStatus, Platform } from 'react-native';\nimport type { MMKV } from 'react-native-mmkv';\n\nimport { dateNow, faro } from '@grafana/faro-core';\nimport type { Metas } from '@grafana/faro-core';\n\nimport { SessionActivityKind } from '../sessionActivity';\nimport {\n claimSessionPersistenceStorageId,\n getSessionProcessInfo,\n releaseSessionPersistenceOwnership,\n} from '../sessionProcess';\n\nimport { parsePersistentSession, serializePersistentSession } from './persistentSessionRecord';\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport {\n getSessionMetaUpdateHandler,\n getUserSessionResetter,\n getUserSessionUpdater,\n toStorableSessionMeta,\n} from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\ntype MmkvConfiguration = {\n id: string;\n mode?: number | 'multi-process';\n};\n\ntype MmkvModule = {\n createMMKV?: (configuration: MmkvConfiguration) => MMKV;\n MMKV?: new (configuration: MmkvConfiguration) => MMKV;\n Mode?: { MULTI_PROCESS?: number };\n};\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction loadMmkvModule(): MmkvModule {\n try {\n return require('react-native-mmkv') as MmkvModule;\n } catch (error) {\n throw new Error(\n `react-native-mmkv could not be loaded. Install it and rebuild the native project. ${errorMessage(error)}`\n );\n }\n}\n\nfunction mmkvConfiguration(mmkv: MmkvModule, storageId: string, isIosExtension: boolean): MmkvConfiguration {\n if (!isIosExtension) {\n return { id: storageId };\n }\n\n if (typeof mmkv.createMMKV === 'function') {\n return { id: storageId, mode: 'multi-process' };\n }\n\n const legacyMultiProcessMode = mmkv.Mode?.MULTI_PROCESS;\n if (typeof legacyMultiProcessMode !== 'number') {\n throw new Error('Persisting sessions in an iOS extension requires react-native-mmkv v3 or newer.');\n }\n\n return { id: storageId, mode: legacyMultiProcessMode };\n}\n\nfunction createMmkvInstance(): MMKV {\n // Load MMKV before taking native ownership so a missing dependency cannot\n // leave the process-wide claim occupied.\n const mmkv = loadMmkvModule();\n const processInfo = getSessionProcessInfo();\n const storageId = claimSessionPersistenceStorageId();\n if (storageId == null || processInfo == null) {\n throw new Error(\n 'Native session-storage coordination is unavailable or already owned by another runtime. Rebuild the native app after upgrading Faro, or use sessionTracking.persistent=false in runtimes that cannot own storage.'\n );\n }\n\n try {\n const configuration = mmkvConfiguration(mmkv, storageId, Platform.OS === 'ios' && !processInfo.isMain);\n // react-native-mmkv v4 was rewritten to Nitro and removed the `new MMKV()`\n // class constructor in favor of a `createMMKV()` factory (see the v4 upgrade\n // guide). Support both so v2/v3 (class) and v4+ (factory) work.\n if (typeof mmkv.createMMKV === 'function') {\n return mmkv.createMMKV(configuration);\n }\n if (typeof mmkv.MMKV !== 'function') {\n throw new Error('The installed react-native-mmkv package does not expose a supported storage API.');\n }\n return new mmkv.MMKV(configuration);\n } catch (error) {\n releaseSessionPersistenceOwnership();\n throw new Error(`Session persistence could not be initialized. ${errorMessage(error)}`);\n }\n}\n\n// undefined means MMKV has not been resolved; null means it is unavailable.\nlet mmkvSingleton: MMKV | null | undefined;\n// undefined means storage has not been read, null means no persisted state is\n// available, and a session is the live value for this JavaScript runtime.\nlet runtimeSession: FaroUserSession | null | undefined;\n\nfunction tryGetMmkv(): MMKV | null {\n if (mmkvSingleton !== undefined) {\n return mmkvSingleton;\n }\n\n try {\n mmkvSingleton = createMmkvInstance();\n } catch (error) {\n mmkvSingleton = null;\n faro.unpatchedConsole?.warn?.('Session persistence is unavailable; using an in-memory session:', error);\n }\n\n return mmkvSingleton;\n}\n\nfunction removeMmkvValue(mmkv: MMKV, key: string): void {\n const compatibleMmkv = mmkv as MMKV & {\n delete?: (storageKey: string) => void;\n remove?: (storageKey: string) => void;\n };\n\n if (typeof compatibleMmkv.remove === 'function') {\n compatibleMmkv.remove(key);\n return;\n }\n if (typeof compatibleMmkv.delete === 'function') {\n compatibleMmkv.delete(key);\n return;\n }\n\n throw new Error('The installed react-native-mmkv version cannot remove stored values.');\n}\n\n/** @internal */\nexport function resetMmkvSingletonForTests(): void {\n mmkvSingleton = undefined;\n runtimeSession = undefined;\n}\n\n/**\n * Persistent session storage backed by MMKV (synchronous reads/writes).\n * Used when `sessionTracking.persistent` is true.\n */\nexport class MmkvPersistentSessionsManager {\n private resetUserSession: ReturnType<typeof getUserSessionResetter>;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n private appStateSubscription: ReturnType<typeof AppState.addEventListener> | null = null;\n private activityWriteTimer: ReturnType<typeof setTimeout> | null = null;\n private hasPendingActivityWrite = false;\n private lastActivityWrite = 0;\n private wasBackgrounded = AppState.currentState === 'background';\n private metaUpdateHandler: ReturnType<typeof getSessionMetaUpdateHandler> | null = null;\n private readonly metas: Metas;\n\n constructor(metas: Metas = faro.metas) {\n this.metas = metas;\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,\n recordUserSessionActivity: this.recordUserSessionActivity,\n storeUserSession: MmkvPersistentSessionsManager.storeUserSession,\n });\n this.resetUserSession = getUserSessionResetter({\n fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,\n storeUserSession: MmkvPersistentSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession(): void {\n runtimeSession = null;\n const mmkv = tryGetMmkv();\n if (mmkv == null) {\n return;\n }\n try {\n removeMmkvValue(mmkv, STORAGE_KEY);\n } catch (error) {\n faro.unpatchedConsole?.warn?.('Failed to remove session from MMKV:', error);\n }\n }\n\n static storeUserSession(session: FaroUserSession): void {\n MmkvPersistentSessionsManager.setRuntimeSession(session);\n MmkvPersistentSessionsManager.persistSession(session);\n }\n\n private static setRuntimeSession(session: FaroUserSession): void {\n runtimeSession =\n session.sessionMeta == null\n ? session\n : {\n ...session,\n sessionMeta: toStorableSessionMeta(session.sessionMeta),\n };\n }\n\n private static persistSession(session: FaroUserSession): void {\n const mmkv = tryGetMmkv();\n if (mmkv == null) {\n return;\n }\n try {\n mmkv.set(STORAGE_KEY, serializePersistentSession(session));\n } catch (error) {\n faro.unpatchedConsole?.warn?.('Failed to store session in MMKV:', error);\n }\n }\n\n static fetchUserSession(): FaroUserSession | null {\n if (runtimeSession !== undefined) {\n return runtimeSession;\n }\n\n const mmkv = tryGetMmkv();\n if (mmkv == null) {\n runtimeSession = null;\n return null;\n }\n\n try {\n const storedSession = mmkv.getString(STORAGE_KEY);\n if (storedSession == null) {\n runtimeSession = null;\n return null;\n }\n\n runtimeSession = parsePersistentSession(storedSession);\n if (runtimeSession == null) {\n removeMmkvValue(mmkv, STORAGE_KEY);\n }\n return runtimeSession;\n } catch (error) {\n runtimeSession = null;\n faro.unpatchedConsole?.warn?.('Failed to fetch session from MMKV:', error);\n return null;\n }\n }\n\n checkSession(activity: SessionActivityKind, currentSession?: FaroUserSession | null): FaroUserSession {\n return this.updateUserSession(activity, currentSession);\n }\n\n resetSession(): FaroUserSession {\n if (this.activityWriteTimer != null) {\n clearTimeout(this.activityWriteTimer);\n this.activityWriteTimer = null;\n }\n this.hasPendingActivityWrite = false;\n\n const session = this.resetUserSession();\n this.lastActivityWrite = dateNow();\n return session;\n }\n\n private flushActivityWrite = (): void => {\n if (this.activityWriteTimer != null) {\n clearTimeout(this.activityWriteTimer);\n this.activityWriteTimer = null;\n }\n\n if (!this.hasPendingActivityWrite) {\n return;\n }\n\n const session = MmkvPersistentSessionsManager.fetchUserSession();\n this.hasPendingActivityWrite = false;\n if (session == null) {\n return;\n }\n\n MmkvPersistentSessionsManager.persistSession(session);\n this.lastActivityWrite = dateNow();\n };\n\n private recordUserSessionActivity = (session: FaroUserSession): void => {\n // Keep expiry checks exact in memory while limiting native storage writes.\n MmkvPersistentSessionsManager.setRuntimeSession(session);\n this.hasPendingActivityWrite = true;\n\n const elapsed = dateNow() - this.lastActivityWrite;\n if (this.lastActivityWrite === 0 || elapsed < 0 || elapsed >= STORAGE_UPDATE_DELAY) {\n this.flushActivityWrite();\n return;\n }\n\n if (this.activityWriteTimer == null) {\n this.activityWriteTimer = setTimeout(this.flushActivityWrite, STORAGE_UPDATE_DELAY - elapsed);\n }\n };\n\n private handleAppStateChange = (nextAppState: AppStateStatus) => {\n if (nextAppState === 'background') {\n this.wasBackgrounded = true;\n this.flushActivityWrite();\n return;\n }\n\n if (nextAppState === 'active' && this.wasBackgrounded) {\n this.wasBackgrounded = false;\n this.checkSession(SessionActivityKind.Meaningful);\n }\n };\n\n private init(): void {\n this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);\n\n const metaUpdateHandler = getSessionMetaUpdateHandler({\n fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,\n storeUserSession: MmkvPersistentSessionsManager.storeUserSession,\n });\n this.metas.addListener(metaUpdateHandler);\n this.metaUpdateHandler = metaUpdateHandler;\n }\n\n unpatch(): void {\n this.flushActivityWrite();\n\n if (this.appStateSubscription) {\n this.appStateSubscription.remove();\n this.appStateSubscription = null;\n }\n\n const metaUpdateHandler = this.metaUpdateHandler;\n this.metaUpdateHandler = null;\n if (metaUpdateHandler) {\n this.metas.removeListener(metaUpdateHandler);\n }\n }\n}\n"]}
|
|
@@ -3,24 +3,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.VolatileSessionsManager = void 0;
|
|
4
4
|
var react_native_1 = require("react-native");
|
|
5
5
|
var faro_core_1 = require("@grafana/faro-core");
|
|
6
|
-
var
|
|
7
|
-
var sessionConstants_1 = require("./sessionConstants");
|
|
6
|
+
var sessionActivity_1 = require("../sessionActivity");
|
|
8
7
|
var sessionManagerUtils_1 = require("./sessionManagerUtils");
|
|
9
8
|
var VolatileSessionsManager = /** @class */ (function () {
|
|
10
|
-
function VolatileSessionsManager() {
|
|
9
|
+
function VolatileSessionsManager(metas) {
|
|
10
|
+
if (metas === void 0) { metas = faro_core_1.faro.metas; }
|
|
11
11
|
var _this = this;
|
|
12
12
|
this.appStateSubscription = null;
|
|
13
|
-
this.
|
|
14
|
-
this.
|
|
13
|
+
this.wasBackgrounded = react_native_1.AppState.currentState === 'background';
|
|
14
|
+
this.metaUpdateHandler = null;
|
|
15
15
|
this.handleAppStateChange = function (nextAppState) {
|
|
16
|
-
if (nextAppState === '
|
|
17
|
-
_this.
|
|
16
|
+
if (nextAppState === 'background') {
|
|
17
|
+
_this.wasBackgrounded = true;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (nextAppState === 'active' && _this.wasBackgrounded) {
|
|
21
|
+
_this.wasBackgrounded = false;
|
|
22
|
+
_this.checkSession(sessionActivity_1.SessionActivityKind.Meaningful);
|
|
18
23
|
}
|
|
19
24
|
};
|
|
25
|
+
this.metas = metas;
|
|
20
26
|
this.updateUserSession = (0, sessionManagerUtils_1.getUserSessionUpdater)({
|
|
21
27
|
fetchUserSession: VolatileSessionsManager.fetchUserSession,
|
|
22
28
|
storeUserSession: VolatileSessionsManager.storeUserSession,
|
|
23
29
|
});
|
|
30
|
+
this.resetUserSession = (0, sessionManagerUtils_1.getUserSessionResetter)({
|
|
31
|
+
fetchUserSession: VolatileSessionsManager.fetchUserSession,
|
|
32
|
+
storeUserSession: VolatileSessionsManager.storeUserSession,
|
|
33
|
+
});
|
|
24
34
|
this.init();
|
|
25
35
|
}
|
|
26
36
|
VolatileSessionsManager.removeUserSession = function () {
|
|
@@ -32,15 +42,22 @@ var VolatileSessionsManager = /** @class */ (function () {
|
|
|
32
42
|
VolatileSessionsManager.fetchUserSession = function () {
|
|
33
43
|
return VolatileSessionsManager.volatileStorage;
|
|
34
44
|
};
|
|
45
|
+
VolatileSessionsManager.prototype.checkSession = function (activity, currentSession) {
|
|
46
|
+
return this.updateUserSession(activity, currentSession);
|
|
47
|
+
};
|
|
48
|
+
VolatileSessionsManager.prototype.resetSession = function () {
|
|
49
|
+
return this.resetUserSession();
|
|
50
|
+
};
|
|
35
51
|
VolatileSessionsManager.prototype.init = function () {
|
|
36
52
|
// Listen to app state changes (equivalent to visibilitychange in web)
|
|
37
53
|
this.appStateSubscription = react_native_1.AppState.addEventListener('change', this.handleAppStateChange);
|
|
38
54
|
// Users can call the setSession() method, so we need to sync this with the in-memory session
|
|
39
|
-
var
|
|
55
|
+
var metaUpdateHandler = (0, sessionManagerUtils_1.getSessionMetaUpdateHandler)({
|
|
40
56
|
fetchUserSession: VolatileSessionsManager.fetchUserSession,
|
|
41
57
|
storeUserSession: VolatileSessionsManager.storeUserSession,
|
|
42
|
-
})
|
|
43
|
-
this.
|
|
58
|
+
});
|
|
59
|
+
this.metas.addListener(metaUpdateHandler);
|
|
60
|
+
this.metaUpdateHandler = metaUpdateHandler;
|
|
44
61
|
};
|
|
45
62
|
/**
|
|
46
63
|
* Clean up listeners when the instrumentation is unpatched
|
|
@@ -50,9 +67,10 @@ var VolatileSessionsManager = /** @class */ (function () {
|
|
|
50
67
|
this.appStateSubscription.remove();
|
|
51
68
|
this.appStateSubscription = null;
|
|
52
69
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
var metaUpdateHandler = this.metaUpdateHandler;
|
|
71
|
+
this.metaUpdateHandler = null;
|
|
72
|
+
if (metaUpdateHandler) {
|
|
73
|
+
this.metas.removeListener(metaUpdateHandler);
|
|
56
74
|
}
|
|
57
75
|
};
|
|
58
76
|
VolatileSessionsManager.volatileStorage = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VolatileSessionManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/VolatileSessionManager.ts"],"names":[],"mappings":";;;AAAA,6CAA6D;AAE7D,gDAA0C;
|
|
1
|
+
{"version":3,"file":"VolatileSessionManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/VolatileSessionManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppState, type AppStateStatus } from 'react-native';\n\nimport { faro } from '@grafana/faro-core';\nimport type { Metas } from '@grafana/faro-core';\n\nimport { SessionActivityKind } from '../sessionActivity';\n\nimport { getSessionMetaUpdateHandler, getUserSessionResetter, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class VolatileSessionsManager {\n private static volatileStorage: FaroUserSession | null = null;\n private resetUserSession: ReturnType<typeof getUserSessionResetter>;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n private appStateSubscription: ReturnType<typeof AppState.addEventListener> | null = null;\n private wasBackgrounded = AppState.currentState === 'background';\n private metaUpdateHandler: ReturnType<typeof getSessionMetaUpdateHandler> | null = null;\n private readonly metas: Metas;\n\n constructor(metas: Metas = faro.metas) {\n this.metas = metas;\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n });\n this.resetUserSession = getUserSessionResetter({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession(): void {\n VolatileSessionsManager.volatileStorage = null;\n }\n\n static storeUserSession(session: FaroUserSession): void {\n VolatileSessionsManager.volatileStorage = session;\n }\n\n static fetchUserSession(): FaroUserSession | null {\n return VolatileSessionsManager.volatileStorage;\n }\n\n checkSession(activity: SessionActivityKind, currentSession?: FaroUserSession | null): FaroUserSession {\n return this.updateUserSession(activity, currentSession);\n }\n\n resetSession(): FaroUserSession {\n return this.resetUserSession();\n }\n\n private handleAppStateChange = (nextAppState: AppStateStatus) => {\n if (nextAppState === 'background') {\n this.wasBackgrounded = true;\n return;\n }\n\n if (nextAppState === 'active' && this.wasBackgrounded) {\n this.wasBackgrounded = false;\n this.checkSession(SessionActivityKind.Meaningful);\n }\n };\n\n private init(): void {\n // Listen to app state changes (equivalent to visibilitychange in web)\n this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);\n\n // Users can call the setSession() method, so we need to sync this with the in-memory session\n const metaUpdateHandler = getSessionMetaUpdateHandler({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n });\n this.metas.addListener(metaUpdateHandler);\n this.metaUpdateHandler = metaUpdateHandler;\n }\n\n /**\n * Clean up listeners when the instrumentation is unpatched\n */\n unpatch(): void {\n if (this.appStateSubscription) {\n this.appStateSubscription.remove();\n this.appStateSubscription = null;\n }\n\n const metaUpdateHandler = this.metaUpdateHandler;\n this.metaUpdateHandler = null;\n if (metaUpdateHandler) {\n this.metas.removeListener(metaUpdateHandler);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.PERSISTED_SESSION_SCHEMA_VERSION = void 0;
|
|
15
|
+
exports.serializePersistentSession = serializePersistentSession;
|
|
16
|
+
exports.parsePersistentSession = parsePersistentSession;
|
|
17
|
+
exports.PERSISTED_SESSION_SCHEMA_VERSION = 1;
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return typeof value === 'object' && value != null && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
function isNonEmptyString(value) {
|
|
22
|
+
return typeof value === 'string' && value.length > 0;
|
|
23
|
+
}
|
|
24
|
+
function isValidTimestamp(value) {
|
|
25
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
26
|
+
}
|
|
27
|
+
function serializePersistentSession(session) {
|
|
28
|
+
var _a, _b;
|
|
29
|
+
var previousSession = (_b = (_a = session.sessionMeta) === null || _a === void 0 ? void 0 : _a.attributes) === null || _b === void 0 ? void 0 : _b['previousSession'];
|
|
30
|
+
var record = {
|
|
31
|
+
schemaVersion: exports.PERSISTED_SESSION_SCHEMA_VERSION,
|
|
32
|
+
currentSessionId: session.sessionId,
|
|
33
|
+
previousSessionId: isNonEmptyString(previousSession) ? previousSession : null,
|
|
34
|
+
startedAt: session.started,
|
|
35
|
+
// Preserve the record invariant if the device clock moves backwards after
|
|
36
|
+
// the session starts.
|
|
37
|
+
lastActivityAt: Math.max(session.started, session.lastActivity),
|
|
38
|
+
isSampled: session.isSampled,
|
|
39
|
+
};
|
|
40
|
+
return JSON.stringify(record);
|
|
41
|
+
}
|
|
42
|
+
function parsePersistentSession(serialized) {
|
|
43
|
+
var value;
|
|
44
|
+
try {
|
|
45
|
+
value = JSON.parse(serialized);
|
|
46
|
+
}
|
|
47
|
+
catch (_a) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (!isRecord(value) ||
|
|
51
|
+
value['schemaVersion'] !== exports.PERSISTED_SESSION_SCHEMA_VERSION ||
|
|
52
|
+
!isNonEmptyString(value['currentSessionId']) ||
|
|
53
|
+
!(value['previousSessionId'] === null || isNonEmptyString(value['previousSessionId'])) ||
|
|
54
|
+
!isValidTimestamp(value['startedAt']) ||
|
|
55
|
+
!isValidTimestamp(value['lastActivityAt']) ||
|
|
56
|
+
value['lastActivityAt'] < value['startedAt'] ||
|
|
57
|
+
typeof value['isSampled'] !== 'boolean') {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
var previousSessionId = value['previousSessionId'];
|
|
61
|
+
return {
|
|
62
|
+
sessionId: value['currentSessionId'],
|
|
63
|
+
started: value['startedAt'],
|
|
64
|
+
lastActivity: value['lastActivityAt'],
|
|
65
|
+
isSampled: value['isSampled'],
|
|
66
|
+
sessionMeta: {
|
|
67
|
+
id: value['currentSessionId'],
|
|
68
|
+
attributes: __assign({ isSampled: value['isSampled'].toString() }, (previousSessionId == null ? {} : { previousSession: previousSessionId })),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=persistentSessionRecord.js.map
|