@grafana/faro-react-native 1.3.1 → 1.4.1
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/makeRNConfig.js +5 -5
- package/dist/cjs/config/makeRNConfig.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/makeRNConfig.js +5 -5
- package/dist/esm/config/makeRNConfig.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/makeRNConfig.ts +10 -9
- 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/initialize.test.ts +63 -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":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseInstrumentation, dateNow, EVENT_SESSION_START, genShortID, VERSION } from '@grafana/faro-core';\nimport type { Config, Meta, MetaSession, TransportItem } from '@grafana/faro-core';\n\nimport type { ReactNativeFullConfig, ReactNativeSessionTrackingConfig } from '../../config/types';\n\nimport { minimalSessionDeviceAttributes, type SessionAttributes } from './sessionAttributes';\nimport { type FaroUserSession, getSessionManagerByConfig, isSampled } from './sessionManager';\nimport { MAX_SESSION_PERSISTENCE_TIME } from './sessionManager/sessionConstants';\nimport { createUserSessionObject, isUserSessionValid } from './sessionManager/sessionManagerUtils';\nimport type { SessionManager } from './sessionManager/types';\n\n/**\n * Session instrumentation for React Native\n * Manages persistent or volatile sessions with expiration and inactivity tracking\n */\nexport class SessionInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-session';\n readonly version = VERSION;\n\n // previously notified session, to ensure we don't send session start\n // event twice for the same session\n private notifiedSession: MetaSession | undefined;\n private sessionManagerInstance: InstanceType<SessionManager> | undefined;\n\n private getDefaultSessionDeviceAttributes(): SessionAttributes {\n const cfg = this.config as ReactNativeFullConfig;\n if (cfg.preloadedSessionDeviceAttributes != null) {\n return cfg.preloadedSessionDeviceAttributes;\n }\n return minimalSessionDeviceAttributes();\n }\n\n private sendSessionStartEvent(meta: Meta): void {\n const session = meta.session;\n\n if (session && session.id !== this.notifiedSession?.id) {\n this.notifiedSession = session;\n // no need to add attributes and session id, they are included as part of meta\n // automatically\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n }\n\n private createInitialSession(\n SessionManagerClass: SessionManager,\n sessionsConfig: Required<Config>['sessionTracking']\n ): {\n initialSession: FaroUserSession;\n emitSessionStartOnInit: boolean;\n } {\n let storedUserSession = SessionManagerClass.fetchUserSession();\n\n const sessionsConfigTyped = sessionsConfig as ReactNativeSessionTrackingConfig;\n const maxPersistenceMs = sessionsConfigTyped.maxSessionPersistenceTime ?? MAX_SESSION_PERSISTENCE_TIME;\n\n if (sessionsConfig.persistent && storedUserSession) {\n const now = dateNow();\n const shouldClearPersistentSession = storedUserSession.lastActivity < now - maxPersistenceMs;\n\n if (shouldClearPersistentSession) {\n SessionManagerClass.removeUserSession();\n storedUserSession = null;\n }\n }\n\n const defaultAttributes = this.getDefaultSessionDeviceAttributes();\n\n let emitSessionStartOnInit: boolean;\n let initialSession: FaroUserSession;\n\n if (isUserSessionValid(storedUserSession)) {\n const sessionId = storedUserSession?.sessionId;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: storedUserSession?.isSampled || false,\n started: storedUserSession?.started,\n });\n\n const storedUserSessionMeta = storedUserSession?.sessionMeta;\n\n // For resumed sessions we want to merge the previous overrides with the configured ones.\n // If the same key is present in both, the new one will override the old one.\n const overrides = { ...sessionsConfig.session?.overrides, ...storedUserSessionMeta?.overrides };\n\n initialSession.sessionMeta = {\n ...sessionsConfig.session,\n id: sessionId,\n attributes: {\n // Start with custom attributes from config\n ...sessionsConfig.session?.attributes,\n // Merge with stored attributes\n ...storedUserSessionMeta?.attributes,\n // Default attributes take precedence (matching Flutter SDK behavior)\n ...defaultAttributes,\n // For valid resumed sessions we do not want to recalculate the sampling decision on each init phase.\n isSampled: initialSession.isSampled.toString(),\n },\n overrides,\n };\n\n emitSessionStartOnInit = false;\n } else {\n const sessionId = sessionsConfig.session?.id ?? genShortID();\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: isSampled(),\n });\n\n const overrides = sessionsConfig.session?.overrides;\n\n initialSession.sessionMeta = {\n id: sessionId,\n attributes: {\n isSampled: initialSession.isSampled.toString(),\n // Start with custom attributes from config\n ...sessionsConfig.session?.attributes,\n // Default attributes take precedence (matching Flutter SDK behavior)\n ...defaultAttributes,\n },\n // new session we don't care about previous overrides\n ...(overrides ? { overrides } : {}),\n };\n\n emitSessionStartOnInit = true;\n }\n\n return { initialSession, emitSessionStartOnInit };\n }\n\n private registerBeforeSendHook(SessionManagerClass: SessionManager) {\n const { updateSession } = new SessionManagerClass();\n\n this.transports?.addBeforeSendHooks((item: TransportItem) => {\n updateSession();\n\n const attributes = item.meta.session?.attributes;\n\n // Only filter out items when session is explicitly NOT sampled (isSampled='false')\n // If isSampled='true', remove the attribute before sending (it's internal)\n // If no isSampled attribute, pass through the item unchanged\n if (attributes?.['isSampled'] === 'false') {\n // Session is not sampled - drop this item\n return null;\n }\n\n if (attributes?.['isSampled'] === 'true') {\n // Session is sampled - remove internal isSampled attribute before sending\n let newItem: TransportItem = JSON.parse(JSON.stringify(item));\n\n const newAttributes = newItem.meta.session?.attributes;\n delete newAttributes?.['isSampled'];\n\n if (Object.keys(newAttributes ?? {}).length === 0) {\n delete newItem.meta.session?.attributes;\n }\n\n return newItem;\n }\n\n // No isSampled attribute or other value - pass through unchanged\n return item;\n });\n }\n\n initialize(): void {\n const sessionTrackingConfig = this.config.sessionTracking;\n\n if (!sessionTrackingConfig?.enabled) {\n this.metas.addListener(this.sendSessionStartEvent.bind(this));\n return;\n }\n\n const SessionManagerClass = getSessionManagerByConfig(sessionTrackingConfig);\n\n this.registerBeforeSendHook(SessionManagerClass);\n\n const { initialSession, emitSessionStartOnInit } = this.createInitialSession(\n SessionManagerClass,\n sessionTrackingConfig\n );\n SessionManagerClass.storeUserSession(initialSession);\n\n const initialSessionMeta = initialSession.sessionMeta;\n this.notifiedSession = initialSessionMeta;\n this.api.setSession(initialSessionMeta);\n\n this.sessionManagerInstance = new SessionManagerClass();\n\n if (emitSessionStartOnInit) {\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n\n this.metas.addListener(this.sendSessionStartEvent.bind(this));\n }\n\n /**\n * Clean up session manager listeners\n */\n unpatch(): void {\n if (this.sessionManagerInstance && 'unpatch' in this.sessionManagerInstance) {\n this.sessionManagerInstance.unpatch();\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseInstrumentation, dateNow, EVENT_SESSION_START, faro, VERSION } from '@grafana/faro-core';\nimport type {\n API,\n BeforeSendHook,\n Config,\n Meta,\n Metas,\n MetaSession,\n TransportItem,\n Transports,\n UserActionInternalInterface,\n} from '@grafana/faro-core';\n\nimport type { ReactNativeFullConfig, ReactNativeSessionTrackingConfig } from '../../config/types';\n\nimport { clearDirectSessionActivityHandler, registerDirectSessionActivityHandler } from './directSessionActivity';\nimport { classifySessionActivity, isRecoveredCrashItem, SessionActivityKind } from './sessionActivity';\nimport { minimalSessionDeviceAttributes, type SessionAttributes } from './sessionAttributes';\nimport { type FaroUserSession, getSessionManagerByConfig, isSampled } from './sessionManager';\nimport { MAX_SESSION_PERSISTENCE_TIME } from './sessionManager/sessionConstants';\nimport { createUserSessionObject, isUserSessionValid } from './sessionManager/sessionManagerUtils';\nimport type { SessionManager, SessionManagerInstance } from './sessionManager/types';\n\nconst SESSION_INSTRUMENTATION_NAME = '@grafana/faro-react-native:instrumentation-session';\n\n// Track the current session hook per transport so superseded hooks yield to a\n// replacement. The shared fallback preserves sampling decisions captured on\n// queued items after the owning instrumentation has detached.\nconst activeSessionBeforeSendHooks = new WeakMap<Transports, BeforeSendHook>();\nconst sessionSamplingFallbackHooks = new WeakMap<Transports, BeforeSendHook>();\n\nfunction applySessionSampling(item: TransportItem, fallbackSamplingDecision?: boolean): TransportItem | null {\n const attributes = item.meta.session?.attributes;\n const samplingAttribute = attributes?.['isSampled'] ?? fallbackSamplingDecision?.toString();\n\n if (samplingAttribute === 'false') {\n return null;\n }\n\n if (samplingAttribute !== 'true') {\n return item;\n }\n\n const sampledItem: TransportItem = {\n ...item,\n meta: {\n ...item.meta,\n session: item.meta.session\n ? {\n ...item.meta.session,\n attributes: item.meta.session.attributes ? { ...item.meta.session.attributes } : undefined,\n }\n : undefined,\n },\n };\n\n const sampledAttributes = sampledItem.meta.session?.attributes;\n delete sampledAttributes?.['isSampled'];\n\n if (Object.keys(sampledAttributes ?? {}).length === 0) {\n delete sampledItem.meta.session?.attributes;\n }\n\n return sampledItem;\n}\n\nfunction ensureSessionSamplingFallback(transports: Transports): void {\n if (sessionSamplingFallbackHooks.has(transports)) {\n return;\n }\n\n const fallbackHook: BeforeSendHook = (item) =>\n activeSessionBeforeSendHooks.has(transports) ? item : applySessionSampling(item);\n\n transports.addBeforeSendHooks(fallbackHook);\n sessionSamplingFallbackHooks.set(transports, fallbackHook);\n}\n\nfunction removeInternalSamplingAttribute(session: MetaSession | undefined): MetaSession | undefined {\n if (session?.attributes?.['isSampled'] == null) {\n return session;\n }\n\n const attributes = { ...session.attributes };\n delete attributes['isSampled'];\n\n return {\n ...session,\n attributes: Object.keys(attributes).length === 0 ? undefined : attributes,\n };\n}\n\n/**\n * Starts a new linked session at an application-defined boundary.\n *\n * Call this after changing or clearing the current user for logout, account\n * changes, or another boundary that must not share a session. Calls made\n * before Faro initializes, after teardown, or while session tracking is\n * disabled have no effect.\n */\nexport function startNewSession(): void {\n const instrumentation = faro?.instrumentations?.instrumentations.find(\n ({ name }) => name === SESSION_INSTRUMENTATION_NAME\n );\n\n if (\n instrumentation != null &&\n 'startNewSession' in instrumentation &&\n typeof instrumentation.startNewSession === 'function'\n ) {\n instrumentation.startNewSession();\n }\n}\n\n/**\n * Session instrumentation for React Native\n * Manages persistent or volatile sessions with expiration and inactivity tracking\n */\nexport class SessionInstrumentation extends BaseInstrumentation {\n readonly name = SESSION_INSTRUMENTATION_NAME;\n readonly version = VERSION;\n\n // previously notified session, to ensure we don't send session start\n // event twice for the same session\n private notifiedSession: MetaSession | undefined;\n private sessionManagerInstance: SessionManagerInstance | undefined;\n private isResettingSession = false;\n private unregisterDirectSessionActivity: (() => void) | undefined;\n private sessionMetaListenerMetas: Metas | undefined;\n private beforeSendHookApi: API | undefined;\n private beforeSendHook: BeforeSendHook | undefined;\n private beforeSendHookTransports: Transports | undefined;\n\n private getDefaultSessionDeviceAttributes(): SessionAttributes {\n const cfg = this.config as ReactNativeFullConfig;\n if (cfg.preloadedSessionDeviceAttributes != null) {\n return cfg.preloadedSessionDeviceAttributes;\n }\n return minimalSessionDeviceAttributes();\n }\n\n private readonly sendSessionStartEvent = (meta: Meta): void => {\n const session = meta.session;\n\n if (session && session.id !== this.notifiedSession?.id) {\n this.notifiedSession = session;\n // no need to add attributes and session id, they are included as part of meta\n // automatically\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n };\n\n private registerSessionMetaListener(): void {\n this.metas.addListener(this.sendSessionStartEvent);\n this.sessionMetaListenerMetas = this.metas;\n }\n\n private createInitialSession(\n SessionManagerClass: SessionManager,\n sessionsConfig: Required<Config>['sessionTracking']\n ): {\n initialSession: FaroUserSession;\n emitSessionStartOnInit: boolean;\n } {\n let storedUserSession = SessionManagerClass.fetchUserSession();\n\n const sessionsConfigTyped = sessionsConfig as ReactNativeSessionTrackingConfig;\n const maxPersistenceMs = sessionsConfigTyped.maxSessionPersistenceTime ?? MAX_SESSION_PERSISTENCE_TIME;\n\n if (sessionsConfig.persistent && storedUserSession) {\n const now = dateNow();\n const shouldClearPersistentSession =\n storedUserSession.started > now ||\n storedUserSession.lastActivity > now ||\n storedUserSession.lastActivity <= now - maxPersistenceMs;\n\n if (shouldClearPersistentSession) {\n SessionManagerClass.removeUserSession();\n storedUserSession = null;\n }\n }\n\n const defaultAttributes = this.getDefaultSessionDeviceAttributes();\n\n let emitSessionStartOnInit: boolean;\n let initialSession: FaroUserSession;\n\n if (!sessionsConfig.persistent && isUserSessionValid(storedUserSession)) {\n const sessionId = storedUserSession?.sessionId;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: storedUserSession?.isSampled || false,\n started: storedUserSession?.started,\n });\n\n const storedUserSessionMeta = storedUserSession?.sessionMeta;\n\n // For resumed sessions we want to merge the previous overrides with the configured ones.\n // If the same key is present in both, the new one will override the old one.\n const overrides = { ...sessionsConfig.session?.overrides, ...storedUserSessionMeta?.overrides };\n\n initialSession.sessionMeta = {\n ...sessionsConfig.session,\n id: sessionId,\n attributes: {\n // Start with custom attributes from config\n ...sessionsConfig.session?.attributes,\n // Merge with stored attributes\n ...storedUserSessionMeta?.attributes,\n // Default attributes take precedence\n ...defaultAttributes,\n // For valid resumed sessions we do not want to recalculate the sampling decision on each init phase.\n isSampled: initialSession.isSampled.toString(),\n },\n overrides,\n };\n\n emitSessionStartOnInit = false;\n } else {\n initialSession = createUserSessionObject({\n sessionId: sessionsConfig.session?.id,\n isSampled: isSampled(),\n });\n\n const sessionId = initialSession.sessionId;\n const previousSessionId = storedUserSession?.sessionId === sessionId ? undefined : storedUserSession?.sessionId;\n const overrides = sessionsConfig.session?.overrides;\n\n initialSession.sessionMeta = {\n id: sessionId,\n attributes: {\n // Start with custom attributes from config\n ...sessionsConfig.session?.attributes,\n // Default attributes take precedence\n ...defaultAttributes,\n isSampled: initialSession.isSampled.toString(),\n ...(previousSessionId == null ? {} : { previousSession: previousSessionId }),\n },\n // new session we don't care about previous overrides\n ...(overrides ? { overrides } : {}),\n };\n\n emitSessionStartOnInit = true;\n }\n\n return { initialSession, emitSessionStartOnInit };\n }\n\n private registerBeforeSendHook(SessionManagerClass: SessionManager): void {\n const registeredTransports = this.transports;\n ensureSessionSamplingFallback(registeredTransports);\n\n const beforeSendHook: BeforeSendHook = (item: TransportItem) => {\n const activeBeforeSendHook = activeSessionBeforeSendHooks.get(registeredTransports);\n if (activeBeforeSendHook != null && activeBeforeSendHook !== beforeSendHook) {\n return item;\n }\n\n let nextItem = item;\n let fallbackSamplingDecision: boolean | undefined;\n const sessionManager = this.beforeSendHook === beforeSendHook ? this.sessionManagerInstance : undefined;\n\n if (sessionManager != null) {\n const storedSession = SessionManagerClass.fetchUserSession();\n const recoveredCrash = isRecoveredCrashItem(item, storedSession?.sessionId);\n // Ending an active action can send telemetry synchronously. Keep that\n // telemetry on the current session until the explicit reset completes.\n const checkedSession = recoveredCrash\n ? null\n : this.isResettingSession\n ? storedSession\n : sessionManager.checkSession(classifySessionActivity(item), storedSession);\n\n if (checkedSession?.sessionMeta != null && item.meta.session?.id !== checkedSession.sessionId) {\n nextItem = {\n ...item,\n meta: {\n ...item.meta,\n session: checkedSession.sessionMeta,\n },\n };\n }\n\n // New crash records carry the crash-time decision. Older records may not,\n // so retain the previous behavior and use the live session's stable\n // decision rather than implicitly sampling every recovered crash.\n fallbackSamplingDecision = recoveredCrash ? storedSession?.isSampled : checkedSession?.isSampled;\n }\n\n return applySessionSampling(nextItem, fallbackSamplingDecision);\n };\n\n this.beforeSendHook = beforeSendHook;\n this.beforeSendHookApi = this.api;\n this.beforeSendHookTransports = registeredTransports;\n registeredTransports.addBeforeSendHooks(beforeSendHook);\n activeSessionBeforeSendHooks.set(registeredTransports, beforeSendHook);\n }\n\n initialize(): void {\n // BaseInstrumentation instances can be registered more than once. Detach\n // every resource from the previous Faro instance before binding the next.\n this.unpatch();\n\n const sessionTrackingConfig = this.config.sessionTracking;\n\n // A new initialization owns direct-activity routing, including when\n // session tracking is disabled.\n clearDirectSessionActivityHandler();\n\n if (!sessionTrackingConfig?.enabled) {\n this.registerSessionMetaListener();\n return;\n }\n\n const SessionManagerClass = getSessionManagerByConfig(sessionTrackingConfig);\n\n // Constructing a manager registers an AppState subscription and a metas\n // listener, so exactly one instance is created and reused. It is also the\n // instance `unpatch()` cleans up.\n this.sessionManagerInstance = new SessionManagerClass(this.metas);\n\n this.registerBeforeSendHook(SessionManagerClass);\n\n const { initialSession, emitSessionStartOnInit } = this.createInitialSession(\n SessionManagerClass,\n sessionTrackingConfig\n );\n SessionManagerClass.storeUserSession(initialSession);\n\n const initialSessionMeta = initialSession.sessionMeta;\n this.notifiedSession = initialSessionMeta;\n this.api.setSession(initialSessionMeta);\n\n if (emitSessionStartOnInit) {\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n\n this.unregisterDirectSessionActivity = registerDirectSessionActivityHandler(() => {\n this.sessionManagerInstance?.checkSession(SessionActivityKind.Meaningful);\n });\n\n this.registerSessionMetaListener();\n }\n\n /** Starts a new linked session immediately. */\n startNewSession(): void {\n if (this.sessionManagerInstance == null || this.isResettingSession) {\n return;\n }\n\n this.isResettingSession = true;\n try {\n try {\n const activeAction = this.api.getActiveUserAction?.() as UserActionInternalInterface | undefined;\n activeAction?.end();\n } catch (error) {\n this.logWarn('Failed to end the active user action before resetting the session:', error);\n }\n\n this.sessionManagerInstance.resetSession();\n } finally {\n this.isResettingSession = false;\n }\n }\n\n /**\n * Clean up session manager listeners\n */\n unpatch(): void {\n this.unregisterDirectSessionActivity?.();\n this.unregisterDirectSessionActivity = undefined;\n\n const beforeSendHook = this.beforeSendHook;\n const beforeSendHookApi = this.beforeSendHookApi;\n const beforeSendHookTransports = this.beforeSendHookTransports;\n const ownedActiveHook =\n beforeSendHook != null &&\n beforeSendHookTransports != null &&\n activeSessionBeforeSendHooks.get(beforeSendHookTransports) === beforeSendHook;\n\n // Stop the manager's metas synchronization before removing the internal\n // sampling marker. Keep the session hook active during that update so any\n // surviving metas listener that emits telemetry is still attributed and\n // filtered with the current session.\n this.sessionManagerInstance?.unpatch();\n\n try {\n if (ownedActiveHook && beforeSendHookApi) {\n const session = beforeSendHookApi.getSession();\n const sessionWithoutSamplingAttribute = removeInternalSamplingAttribute(session);\n if (sessionWithoutSamplingAttribute !== session) {\n this.isResettingSession = true;\n try {\n beforeSendHookApi.setSession(sessionWithoutSamplingAttribute);\n } finally {\n this.isResettingSession = false;\n }\n }\n }\n } finally {\n this.beforeSendHook = undefined;\n this.beforeSendHookApi = undefined;\n this.beforeSendHookTransports = undefined;\n if (beforeSendHook && beforeSendHookTransports) {\n if (ownedActiveHook) {\n activeSessionBeforeSendHooks.delete(beforeSendHookTransports);\n }\n beforeSendHookTransports.removeBeforeSendHooks(beforeSendHook);\n }\n\n this.sessionManagerInstance = undefined;\n\n const sessionMetaListenerMetas = this.sessionMetaListenerMetas;\n this.sessionMetaListenerMetas = undefined;\n if (sessionMetaListenerMetas) {\n sessionMetaListenerMetas.removeListener(this.sendSessionStartEvent);\n }\n\n this.notifiedSession = undefined;\n this.isResettingSession = false;\n }\n }\n\n destroy(): void {\n this.unpatch();\n }\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { EVENT_VIEW_CHANGED, TransportItemType } from '@grafana/faro-core';
|
|
2
|
+
import { EVENT_NAVIGATION } from '../../navigation/utils';
|
|
3
|
+
import { EVENT_APP_STATE_CHANGED } from '../appState';
|
|
4
|
+
export var SessionActivityKind;
|
|
5
|
+
(function (SessionActivityKind) {
|
|
6
|
+
SessionActivityKind["Meaningful"] = "meaningful";
|
|
7
|
+
SessionActivityKind["Passive"] = "passive";
|
|
8
|
+
})(SessionActivityKind || (SessionActivityKind = {}));
|
|
9
|
+
/** Classifies how a transport item affects the session inactivity window. */
|
|
10
|
+
export function classifySessionActivity(item) {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
const payload = item.payload;
|
|
13
|
+
if (payload.action != null) {
|
|
14
|
+
return SessionActivityKind.Meaningful;
|
|
15
|
+
}
|
|
16
|
+
if (item.type !== TransportItemType.EVENT) {
|
|
17
|
+
return SessionActivityKind.Passive;
|
|
18
|
+
}
|
|
19
|
+
if (payload.name === EVENT_VIEW_CHANGED || payload.name === EVENT_NAVIGATION) {
|
|
20
|
+
return SessionActivityKind.Meaningful;
|
|
21
|
+
}
|
|
22
|
+
if (payload.name === EVENT_APP_STATE_CHANGED &&
|
|
23
|
+
((_a = payload.attributes) === null || _a === void 0 ? void 0 : _a['fromState']) === 'background' &&
|
|
24
|
+
((_b = payload.attributes) === null || _b === void 0 ? void 0 : _b['toState']) === 'active') {
|
|
25
|
+
return SessionActivityKind.Meaningful;
|
|
26
|
+
}
|
|
27
|
+
return SessionActivityKind.Passive;
|
|
28
|
+
}
|
|
29
|
+
/** Recovered crashes keep the session captured by the previous process. */
|
|
30
|
+
export function isRecoveredCrashItem(item, currentSessionId) {
|
|
31
|
+
var _a;
|
|
32
|
+
if (item.type !== TransportItemType.EXCEPTION || ((_a = item.meta.session) === null || _a === void 0 ? void 0 : _a.id) == null) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const payload = item.payload;
|
|
36
|
+
return payload.type === 'crash' && payload.fatal === true && item.meta.session.id !== currentSessionId;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=sessionActivity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionActivity.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/sessionActivity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,CAAN,IAAY,mBAGX;AAHD,WAAY,mBAAmB;IAC7B,gDAAyB,CAAA;IACzB,0CAAmB,CAAA;AACrB,CAAC,EAHW,mBAAmB,KAAnB,mBAAmB,QAG9B;AAQD,6EAA6E;AAC7E,MAAM,UAAU,uBAAuB,CAAC,IAAmB;;IACzD,MAAM,OAAO,GAAG,IAAI,CAAC,OAA+B,CAAC;IAErD,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QAC3B,OAAO,mBAAmB,CAAC,UAAU,CAAC;IACxC,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC1C,OAAO,mBAAmB,CAAC,OAAO,CAAC;IACrC,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,KAAK,kBAAkB,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QAC7E,OAAO,mBAAmB,CAAC,UAAU,CAAC;IACxC,CAAC;IAED,IACE,OAAO,CAAC,IAAI,KAAK,uBAAuB;QACxC,CAAA,MAAA,OAAO,CAAC,UAAU,0CAAG,WAAW,CAAC,MAAK,YAAY;QAClD,CAAA,MAAA,OAAO,CAAC,UAAU,0CAAG,SAAS,CAAC,MAAK,QAAQ,EAC5C,CAAC;QACD,OAAO,mBAAmB,CAAC,UAAU,CAAC;IACxC,CAAC;IAED,OAAO,mBAAmB,CAAC,OAAO,CAAC;AACrC,CAAC;AAOD,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB,CAAC,IAAmB,EAAE,gBAAoC;;IAC5F,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,CAAC,SAAS,IAAI,CAAA,MAAA,IAAI,CAAC,IAAI,CAAC,OAAO,0CAAE,EAAE,KAAI,IAAI,EAAE,CAAC;QAC/E,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAuB,CAAC;IAC7C,OAAO,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,gBAAgB,CAAC;AACzG,CAAC","sourcesContent":["import { EVENT_VIEW_CHANGED, TransportItemType } from '@grafana/faro-core';\nimport type { EventAttributes, TransportItem } from '@grafana/faro-core';\n\nimport { EVENT_NAVIGATION } from '../../navigation/utils';\nimport { EVENT_APP_STATE_CHANGED } from '../appState';\n\nexport enum SessionActivityKind {\n Meaningful = 'meaningful',\n Passive = 'passive',\n}\n\ntype ActivityEventPayload = {\n action?: unknown;\n attributes?: EventAttributes;\n name?: string;\n};\n\n/** Classifies how a transport item affects the session inactivity window. */\nexport function classifySessionActivity(item: TransportItem): SessionActivityKind {\n const payload = item.payload as ActivityEventPayload;\n\n if (payload.action != null) {\n return SessionActivityKind.Meaningful;\n }\n\n if (item.type !== TransportItemType.EVENT) {\n return SessionActivityKind.Passive;\n }\n\n if (payload.name === EVENT_VIEW_CHANGED || payload.name === EVENT_NAVIGATION) {\n return SessionActivityKind.Meaningful;\n }\n\n if (\n payload.name === EVENT_APP_STATE_CHANGED &&\n payload.attributes?.['fromState'] === 'background' &&\n payload.attributes?.['toState'] === 'active'\n ) {\n return SessionActivityKind.Meaningful;\n }\n\n return SessionActivityKind.Passive;\n}\n\ntype CrashPayload = {\n fatal?: unknown;\n type?: unknown;\n};\n\n/** Recovered crashes keep the session captured by the previous process. */\nexport function isRecoveredCrashItem(item: TransportItem, currentSessionId: string | undefined): boolean {\n if (item.type !== TransportItemType.EXCEPTION || item.meta.session?.id == null) {\n return false;\n }\n\n const payload = item.payload as CrashPayload;\n return payload.type === 'crash' && payload.fatal === true && item.meta.session.id !== currentSessionId;\n}\n"]}
|
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { Platform } from 'react-native';
|
|
11
11
|
import DeviceInfo from 'react-native-device-info';
|
|
12
12
|
import { getInstallationId } from './installationId';
|
|
13
|
+
import { getSessionProcessInfo } from './sessionProcess';
|
|
13
14
|
/**
|
|
14
15
|
* React Native framework version from `Platform.constants` (host app runtime), not `@grafana/faro-react-native` semver.
|
|
15
16
|
*/
|
|
@@ -61,7 +62,7 @@ function getMobileDeviceType(isTablet) {
|
|
|
61
62
|
return undefined;
|
|
62
63
|
}
|
|
63
64
|
/**
|
|
64
|
-
* Get OS detail string
|
|
65
|
+
* Get OS detail string for session attributes.
|
|
65
66
|
* iOS: "iOS 17.0"
|
|
66
67
|
* Android: "Android 15 (SDK 35)"
|
|
67
68
|
*/
|
|
@@ -99,16 +100,16 @@ function getDeviceOsBuildId() {
|
|
|
99
100
|
* No synchronous DeviceInfo reads — use {@link getSessionAttributes} or the package async `initializeFaro`.
|
|
100
101
|
*/
|
|
101
102
|
export function minimalSessionDeviceAttributes() {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
};
|
|
103
|
+
const processInfo = getSessionProcessInfo();
|
|
104
|
+
return Object.assign({ react_native_version: getReactNativeVersion() }, (processInfo == null ? {} : { process_name: processInfo.identifier }));
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
107
|
* Get all session attributes
|
|
108
108
|
* These attributes are automatically included with every telemetry event
|
|
109
109
|
*
|
|
110
|
-
* Core attributes
|
|
110
|
+
* Core session attributes:
|
|
111
111
|
* - react_native_version (RN framework in the host app)
|
|
112
|
+
* - process_name (Android process name or iOS bundle identifier)
|
|
112
113
|
* - device_os, device_os_version, device_os_detail
|
|
113
114
|
* - device_manufacturer, device_model, device_model_name
|
|
114
115
|
* - device_brand, device_is_physical, device_id
|
|
@@ -146,7 +147,6 @@ function collectMobileMeta() {
|
|
|
146
147
|
// Memory info
|
|
147
148
|
const totalMemory = DeviceInfo.getTotalMemorySync();
|
|
148
149
|
const usedMemory = DeviceInfo.getUsedMemorySync();
|
|
149
|
-
const reactNativeVersion = getReactNativeVersion();
|
|
150
150
|
// Try to get async device info (battery, carrier)
|
|
151
151
|
let batteryLevel;
|
|
152
152
|
let isCharging;
|
|
@@ -184,7 +184,7 @@ function collectMobileMeta() {
|
|
|
184
184
|
catch (_error) {
|
|
185
185
|
// Carrier not available
|
|
186
186
|
}
|
|
187
|
-
const attributes = Object.assign(Object.assign(Object.assign({
|
|
187
|
+
const attributes = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, minimalSessionDeviceAttributes()), { device_os: systemName, device_os_version: systemVersion, device_os_detail: deviceOsDetail, device_manufacturer: manufacturer.toLowerCase(), device_model: model, device_model_name: deviceName, device_brand: brand, device_is_physical: String(!isEmulator) }), (installationId ? { device_id: installationId } : {})), (deviceType ? { device_type: deviceType } : {})), { device_memory_total: String(totalMemory), device_memory_used: String(usedMemory), device_battery_level: batteryLevel, device_is_charging: isCharging, device_low_power_mode: lowPowerMode, device_carrier: carrier });
|
|
188
188
|
const appMeta = installationId ? { installationId } : {};
|
|
189
189
|
const structuredDeviceBrand = getStructuredDeviceBrand(model, brand);
|
|
190
190
|
const structuredDeviceManufacturer = getStructuredDeviceManufacturer(manufacturer);
|
|
@@ -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":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAIlD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AA0EzD;;GAEG;AACH,SAAS,qBAAqB;IAC5B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,kBAAkB,CAAC;QACtD,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC3C,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,OAK3C,CAAC;YACF,IAAI,aAAa,GAAG,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;YACjD,IAAI,UAAU,EAAE,CAAC;gBACf,aAAa,IAAI,OAAO,UAAU,EAAE,CAAC;YACvC,CAAC;YACD,OAAO,aAAa,CAAC;QACvB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAa,EAAE,KAAa;IAC5D,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClE,CAAC;AAED,SAAS,+BAA+B,CAAC,YAAoB;IAC3D,OAAO,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;AAC3E,CAAC;AAED,SAAS,kCAAkC,CAAC,KAAa;IACvD,8EAA8E;IAC9E,oBAAoB;IACpB,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,wEAAwE;IACxE,uEAAuE;IACvE,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC,WAAW,EAAE,CAAC;IAClC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAiB;IAC5C,2EAA2E;IAC3E,gEAAgE;IAChE,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IACxC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,SAAe,iBAAiB;;QAC9B,MAAM,UAAU,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,aAAa,GAAG,UAAU,CAAC,gBAAgB,EAAE,CAAC;QAEpD,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,CAAC;gBAChD,OAAO,GAAG,UAAU,IAAI,aAAa,SAAS,QAAQ,GAAG,CAAC;YAC5D,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,OAAO,GAAG,UAAU,IAAI,aAAa,EAAE,CAAC;YAC1C,CAAC;QACH,CAAC;QAED,OAAO,GAAG,UAAU,IAAI,aAAa,EAAE,CAAC;IAC1C,CAAC;CAAA;AAED,SAAe,kBAAkB;;QAC/B,IAAI,CAAC;YACH,uEAAuE;YACvE,iEAAiE;YACjE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,UAAU,EAAE,CAAC;YAC9C,OAAO,OAAO,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAChE,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B;IAC5C,MAAM,WAAW,GAAG,qBAAqB,EAAE,CAAC;IAC5C,uBACE,oBAAoB,EAAE,qBAAqB,EAAE,IAC1C,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,WAAW,CAAC,UAAU,EAAE,CAAC,EACxE;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAgB,oBAAoB;;QACxC,OAAO,CAAC,MAAM,iBAAiB,EAAE,CAAC,CAAC,iBAAiB,CAAC;IACvD,CAAC;CAAA;AAED,SAAe,iBAAiB;;QAC9B,IAAI,CAAC;YACH,MAAM,CAAC,cAAc,EAAE,eAAe,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC1E,iBAAiB,EAAE;gBACnB,kBAAkB,EAAE;gBACpB,iBAAiB,EAAE;aACpB,CAAC,CAAC;YAEH,8BAA8B;YAC9B,MAAM,UAAU,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC;YAC9C,MAAM,aAAa,GAAG,UAAU,CAAC,gBAAgB,EAAE,CAAC;YACpD,MAAM,YAAY,GAAG,UAAU,CAAC,mBAAmB,EAAE,CAAC;YACtD,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,eAAe,GAAG,kCAAkC,CAAC,KAAK,CAAC,CAAC;YAClE,MAAM,UAAU,GAAG,UAAU,CAAC,iBAAiB,EAAE,CAAC;YAClD,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,UAAU,CAAC,cAAc,EAAE,CAAC;YAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,UAAU,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YAEjD,cAAc;YACd,MAAM,WAAW,GAAG,UAAU,CAAC,kBAAkB,EAAE,CAAC;YACpD,MAAM,UAAU,GAAG,UAAU,CAAC,iBAAiB,EAAE,CAAC;YAElD,kDAAkD;YAClD,IAAI,YAAgC,CAAC;YACrC,IAAI,UAA8B,CAAC;YACnC,IAAI,YAAgC,CAAC;YACrC,IAAI,OAA2B,CAAC;YAEhC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,eAAe,EAAE,CAAC;gBACnD,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;oBACjB,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,6BAA6B;YAC/B,CAAC;YAED,IAAI,CAAC;gBACH,UAAU,GAAG,MAAM,CAAC,MAAM,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;YAC5D,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,gCAAgC;YAClC,CAAC;YAED,IAAI,CAAC;gBACH,IAAI,iBAAiB,IAAI,UAAU,EAAE,CAAC;oBACpC,YAAY,GAAG,MAAM,CACnB,MAAO,UAA8E,CAAC,eAAe,EAAE,CACxG,CAAC;gBACJ,CAAC;YACH,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,+BAA+B;YACjC,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,UAAU,EAAE,CAAC;gBAClD,IAAI,WAAW,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;oBAC7C,OAAO,GAAG,WAAW,CAAC;gBACxB,CAAC;YACH,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,wBAAwB;YAC1B,CAAC;YAED,MAAM,UAAU,6EACX,8BAA8B,EAAE,KACnC,SAAS,EAAE,UAAU,EACrB,iBAAiB,EAAE,aAAa,EAChC,gBAAgB,EAAE,cAAc,EAChC,mBAAmB,EAAE,YAAY,CAAC,WAAW,EAAE,EAC/C,YAAY,EAAE,KAAK,EACnB,iBAAiB,EAAE,UAAU,EAC7B,YAAY,EAAE,KAAK,EACnB,kBAAkB,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,KACpC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GACrD,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAClD,mBAAmB,EAAE,MAAM,CAAC,WAAW,CAAC,EACxC,kBAAkB,EAAE,MAAM,CAAC,UAAU,CAAC,EACtC,oBAAoB,EAAE,YAAY,EAClC,kBAAkB,EAAE,UAAU,EAC9B,qBAAqB,EAAE,YAAY,EACnC,cAAc,EAAE,OAAO,GACxB,CAAC;YACF,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,MAAM,qBAAqB,GAAG,wBAAwB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACrE,MAAM,4BAA4B,GAAG,+BAA+B,CAAC,YAAY,CAAC,CAAC;YACnF,MAAM,MAAM,mCACP,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACzD,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,aAAa,GACvB,CAAC;YAEF,OAAO;gBACL,iBAAiB,EAAE,UAAU;gBAC7B,IAAI,EAAE;oBACJ,GAAG,EAAE,OAAO;oBACZ,MAAM,8CACJ,KAAK,EAAE,qBAAqB,EAC5B,WAAW,EAAE,CAAC,UAAU,EACxB,YAAY,EAAE,4BAA4B,IACvC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACjE,UAAU,EAAE,KAAK,KACd,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC5C;oBACD,EAAE,EAAE,MAAM;iBACX;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,MAAM,cAAc,GAAG,MAAM,iBAAiB,EAAE,CAAC;YAEjD,OAAO;gBACL,iBAAiB,EAAE,8BAA8B,EAAE;gBACnD,IAAI,EAAE;oBACJ,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE;iBAC9C;aACF,CAAC;QACJ,CAAC;IACH,CAAC;CAAA;AAED;;GAEG;AACH,MAAM,UAAgB,qBAAqB;;QACzC,OAAO,iBAAiB,EAAE,CAAC;IAC7B,CAAC;CAAA;AAED;;;GAGG;AACH,MAAM,UAAgB,kCAAkC;;QACtD,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,qBAAqB,EAAE,CAAC,CAAC,iBAAiB,CAAC;QAC3D,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,8BAA8B,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;CAAA","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,43 +1,157 @@
|
|
|
1
|
-
import { AppState } from 'react-native';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { AppState, Platform } from 'react-native';
|
|
2
|
+
import { dateNow, faro } from '@grafana/faro-core';
|
|
3
|
+
import { SessionActivityKind } from '../sessionActivity';
|
|
4
|
+
import { claimSessionPersistenceStorageId, getSessionProcessInfo, releaseSessionPersistenceOwnership, } from '../sessionProcess';
|
|
5
|
+
import { parsePersistentSession, serializePersistentSession } from './persistentSessionRecord';
|
|
4
6
|
import { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';
|
|
5
|
-
import { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';
|
|
7
|
+
import { getSessionMetaUpdateHandler, getUserSessionResetter, getUserSessionUpdater, toStorableSessionMeta, } from './sessionManagerUtils';
|
|
8
|
+
function errorMessage(error) {
|
|
9
|
+
return error instanceof Error ? error.message : String(error);
|
|
10
|
+
}
|
|
11
|
+
function loadMmkvModule() {
|
|
12
|
+
try {
|
|
13
|
+
return require('react-native-mmkv');
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
throw new Error(`react-native-mmkv could not be loaded. Install it and rebuild the native project. ${errorMessage(error)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function mmkvConfiguration(mmkv, storageId, isIosExtension) {
|
|
20
|
+
var _a;
|
|
21
|
+
if (!isIosExtension) {
|
|
22
|
+
return { id: storageId };
|
|
23
|
+
}
|
|
24
|
+
if (typeof mmkv.createMMKV === 'function') {
|
|
25
|
+
return { id: storageId, mode: 'multi-process' };
|
|
26
|
+
}
|
|
27
|
+
const legacyMultiProcessMode = (_a = mmkv.Mode) === null || _a === void 0 ? void 0 : _a.MULTI_PROCESS;
|
|
28
|
+
if (typeof legacyMultiProcessMode !== 'number') {
|
|
29
|
+
throw new Error('Persisting sessions in an iOS extension requires react-native-mmkv v3 or newer.');
|
|
30
|
+
}
|
|
31
|
+
return { id: storageId, mode: legacyMultiProcessMode };
|
|
32
|
+
}
|
|
6
33
|
function createMmkvInstance() {
|
|
34
|
+
// Load MMKV before taking native ownership so a missing dependency cannot
|
|
35
|
+
// leave the process-wide claim occupied.
|
|
36
|
+
const mmkv = loadMmkvModule();
|
|
37
|
+
const processInfo = getSessionProcessInfo();
|
|
38
|
+
const storageId = claimSessionPersistenceStorageId();
|
|
39
|
+
if (storageId == null || processInfo == null) {
|
|
40
|
+
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.');
|
|
41
|
+
}
|
|
7
42
|
try {
|
|
8
|
-
const
|
|
9
|
-
|
|
43
|
+
const configuration = mmkvConfiguration(mmkv, storageId, Platform.OS === 'ios' && !processInfo.isMain);
|
|
44
|
+
// react-native-mmkv v4 was rewritten to Nitro and removed the `new MMKV()`
|
|
45
|
+
// class constructor in favor of a `createMMKV()` factory (see the v4 upgrade
|
|
46
|
+
// guide). Support both so v2/v3 (class) and v4+ (factory) work.
|
|
47
|
+
if (typeof mmkv.createMMKV === 'function') {
|
|
48
|
+
return mmkv.createMMKV(configuration);
|
|
49
|
+
}
|
|
50
|
+
if (typeof mmkv.MMKV !== 'function') {
|
|
51
|
+
throw new Error('The installed react-native-mmkv package does not expose a supported storage API.');
|
|
52
|
+
}
|
|
53
|
+
return new mmkv.MMKV(configuration);
|
|
10
54
|
}
|
|
11
|
-
catch (
|
|
12
|
-
|
|
55
|
+
catch (error) {
|
|
56
|
+
releaseSessionPersistenceOwnership();
|
|
57
|
+
throw new Error(`Session persistence could not be initialized. ${errorMessage(error)}`);
|
|
13
58
|
}
|
|
14
59
|
}
|
|
60
|
+
// undefined means MMKV has not been resolved; null means it is unavailable.
|
|
15
61
|
let mmkvSingleton;
|
|
16
|
-
|
|
17
|
-
|
|
62
|
+
// undefined means storage has not been read, null means no persisted state is
|
|
63
|
+
// available, and a session is the live value for this JavaScript runtime.
|
|
64
|
+
let runtimeSession;
|
|
65
|
+
function tryGetMmkv() {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
if (mmkvSingleton !== undefined) {
|
|
68
|
+
return mmkvSingleton;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
18
71
|
mmkvSingleton = createMmkvInstance();
|
|
19
72
|
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
mmkvSingleton = null;
|
|
75
|
+
(_b = (_a = 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);
|
|
76
|
+
}
|
|
20
77
|
return mmkvSingleton;
|
|
21
78
|
}
|
|
79
|
+
function removeMmkvValue(mmkv, key) {
|
|
80
|
+
const compatibleMmkv = mmkv;
|
|
81
|
+
if (typeof compatibleMmkv.remove === 'function') {
|
|
82
|
+
compatibleMmkv.remove(key);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (typeof compatibleMmkv.delete === 'function') {
|
|
86
|
+
compatibleMmkv.delete(key);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
throw new Error('The installed react-native-mmkv version cannot remove stored values.');
|
|
90
|
+
}
|
|
22
91
|
/** @internal */
|
|
23
92
|
export function resetMmkvSingletonForTests() {
|
|
24
93
|
mmkvSingleton = undefined;
|
|
94
|
+
runtimeSession = undefined;
|
|
25
95
|
}
|
|
26
96
|
/**
|
|
27
97
|
* Persistent session storage backed by MMKV (synchronous reads/writes).
|
|
28
98
|
* Used when `sessionTracking.persistent` is true.
|
|
29
99
|
*/
|
|
30
100
|
export class MmkvPersistentSessionsManager {
|
|
31
|
-
constructor() {
|
|
101
|
+
constructor(metas = faro.metas) {
|
|
32
102
|
this.appStateSubscription = null;
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
103
|
+
this.activityWriteTimer = null;
|
|
104
|
+
this.hasPendingActivityWrite = false;
|
|
105
|
+
this.lastActivityWrite = 0;
|
|
106
|
+
this.wasBackgrounded = AppState.currentState === 'background';
|
|
107
|
+
this.metaUpdateHandler = null;
|
|
108
|
+
this.flushActivityWrite = () => {
|
|
109
|
+
if (this.activityWriteTimer != null) {
|
|
110
|
+
clearTimeout(this.activityWriteTimer);
|
|
111
|
+
this.activityWriteTimer = null;
|
|
112
|
+
}
|
|
113
|
+
if (!this.hasPendingActivityWrite) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const session = MmkvPersistentSessionsManager.fetchUserSession();
|
|
117
|
+
this.hasPendingActivityWrite = false;
|
|
118
|
+
if (session == null) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
MmkvPersistentSessionsManager.persistSession(session);
|
|
122
|
+
this.lastActivityWrite = dateNow();
|
|
123
|
+
};
|
|
124
|
+
this.recordUserSessionActivity = (session) => {
|
|
125
|
+
// Keep expiry checks exact in memory while limiting native storage writes.
|
|
126
|
+
MmkvPersistentSessionsManager.setRuntimeSession(session);
|
|
127
|
+
this.hasPendingActivityWrite = true;
|
|
128
|
+
const elapsed = dateNow() - this.lastActivityWrite;
|
|
129
|
+
if (this.lastActivityWrite === 0 || elapsed < 0 || elapsed >= STORAGE_UPDATE_DELAY) {
|
|
130
|
+
this.flushActivityWrite();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (this.activityWriteTimer == null) {
|
|
134
|
+
this.activityWriteTimer = setTimeout(this.flushActivityWrite, STORAGE_UPDATE_DELAY - elapsed);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
35
137
|
this.handleAppStateChange = (nextAppState) => {
|
|
36
|
-
if (nextAppState === '
|
|
37
|
-
this.
|
|
138
|
+
if (nextAppState === 'background') {
|
|
139
|
+
this.wasBackgrounded = true;
|
|
140
|
+
this.flushActivityWrite();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (nextAppState === 'active' && this.wasBackgrounded) {
|
|
144
|
+
this.wasBackgrounded = false;
|
|
145
|
+
this.checkSession(SessionActivityKind.Meaningful);
|
|
38
146
|
}
|
|
39
147
|
};
|
|
148
|
+
this.metas = metas;
|
|
40
149
|
this.updateUserSession = getUserSessionUpdater({
|
|
150
|
+
fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,
|
|
151
|
+
recordUserSessionActivity: this.recordUserSessionActivity,
|
|
152
|
+
storeUserSession: MmkvPersistentSessionsManager.storeUserSession,
|
|
153
|
+
});
|
|
154
|
+
this.resetUserSession = getUserSessionResetter({
|
|
41
155
|
fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,
|
|
42
156
|
storeUserSession: MmkvPersistentSessionsManager.storeUserSession,
|
|
43
157
|
});
|
|
@@ -45,17 +159,36 @@ export class MmkvPersistentSessionsManager {
|
|
|
45
159
|
}
|
|
46
160
|
static removeUserSession() {
|
|
47
161
|
var _a, _b;
|
|
162
|
+
runtimeSession = null;
|
|
163
|
+
const mmkv = tryGetMmkv();
|
|
164
|
+
if (mmkv == null) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
48
167
|
try {
|
|
49
|
-
|
|
168
|
+
removeMmkvValue(mmkv, STORAGE_KEY);
|
|
50
169
|
}
|
|
51
170
|
catch (error) {
|
|
52
171
|
(_b = (_a = 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);
|
|
53
172
|
}
|
|
54
173
|
}
|
|
55
174
|
static storeUserSession(session) {
|
|
175
|
+
MmkvPersistentSessionsManager.setRuntimeSession(session);
|
|
176
|
+
MmkvPersistentSessionsManager.persistSession(session);
|
|
177
|
+
}
|
|
178
|
+
static setRuntimeSession(session) {
|
|
179
|
+
runtimeSession =
|
|
180
|
+
session.sessionMeta == null
|
|
181
|
+
? session
|
|
182
|
+
: Object.assign(Object.assign({}, session), { sessionMeta: toStorableSessionMeta(session.sessionMeta) });
|
|
183
|
+
}
|
|
184
|
+
static persistSession(session) {
|
|
56
185
|
var _a, _b;
|
|
186
|
+
const mmkv = tryGetMmkv();
|
|
187
|
+
if (mmkv == null) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
57
190
|
try {
|
|
58
|
-
|
|
191
|
+
mmkv.set(STORAGE_KEY, serializePersistentSession(session));
|
|
59
192
|
}
|
|
60
193
|
catch (error) {
|
|
61
194
|
(_b = (_a = 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);
|
|
@@ -63,34 +196,64 @@ export class MmkvPersistentSessionsManager {
|
|
|
63
196
|
}
|
|
64
197
|
static fetchUserSession() {
|
|
65
198
|
var _a, _b;
|
|
199
|
+
if (runtimeSession !== undefined) {
|
|
200
|
+
return runtimeSession;
|
|
201
|
+
}
|
|
202
|
+
const mmkv = tryGetMmkv();
|
|
203
|
+
if (mmkv == null) {
|
|
204
|
+
runtimeSession = null;
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
66
207
|
try {
|
|
67
|
-
const storedSession =
|
|
68
|
-
if (storedSession) {
|
|
69
|
-
|
|
208
|
+
const storedSession = mmkv.getString(STORAGE_KEY);
|
|
209
|
+
if (storedSession == null) {
|
|
210
|
+
runtimeSession = null;
|
|
211
|
+
return null;
|
|
70
212
|
}
|
|
71
|
-
|
|
213
|
+
runtimeSession = parsePersistentSession(storedSession);
|
|
214
|
+
if (runtimeSession == null) {
|
|
215
|
+
removeMmkvValue(mmkv, STORAGE_KEY);
|
|
216
|
+
}
|
|
217
|
+
return runtimeSession;
|
|
72
218
|
}
|
|
73
219
|
catch (error) {
|
|
220
|
+
runtimeSession = null;
|
|
74
221
|
(_b = (_a = 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);
|
|
75
222
|
return null;
|
|
76
223
|
}
|
|
77
224
|
}
|
|
225
|
+
checkSession(activity, currentSession) {
|
|
226
|
+
return this.updateUserSession(activity, currentSession);
|
|
227
|
+
}
|
|
228
|
+
resetSession() {
|
|
229
|
+
if (this.activityWriteTimer != null) {
|
|
230
|
+
clearTimeout(this.activityWriteTimer);
|
|
231
|
+
this.activityWriteTimer = null;
|
|
232
|
+
}
|
|
233
|
+
this.hasPendingActivityWrite = false;
|
|
234
|
+
const session = this.resetUserSession();
|
|
235
|
+
this.lastActivityWrite = dateNow();
|
|
236
|
+
return session;
|
|
237
|
+
}
|
|
78
238
|
init() {
|
|
79
239
|
this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);
|
|
80
|
-
const
|
|
240
|
+
const metaUpdateHandler = getSessionMetaUpdateHandler({
|
|
81
241
|
fetchUserSession: MmkvPersistentSessionsManager.fetchUserSession,
|
|
82
242
|
storeUserSession: MmkvPersistentSessionsManager.storeUserSession,
|
|
83
|
-
})
|
|
84
|
-
this.
|
|
243
|
+
});
|
|
244
|
+
this.metas.addListener(metaUpdateHandler);
|
|
245
|
+
this.metaUpdateHandler = metaUpdateHandler;
|
|
85
246
|
}
|
|
86
247
|
unpatch() {
|
|
248
|
+
this.flushActivityWrite();
|
|
87
249
|
if (this.appStateSubscription) {
|
|
88
250
|
this.appStateSubscription.remove();
|
|
89
251
|
this.appStateSubscription = null;
|
|
90
252
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
253
|
+
const metaUpdateHandler = this.metaUpdateHandler;
|
|
254
|
+
this.metaUpdateHandler = null;
|
|
255
|
+
if (metaUpdateHandler) {
|
|
256
|
+
this.metas.removeListener(metaUpdateHandler);
|
|
94
257
|
}
|
|
95
258
|
}
|
|
96
259
|
}
|