@scalebun-release/react-native 2.5.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.
Files changed (1203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/android/build.gradle +94 -0
  4. package/android/consumer-rules.pro +28 -0
  5. package/android/src/androidTest/java/com/scalebun/rn/ota/ScaleBunOtaVerifierInstrumentedTest.kt +112 -0
  6. package/android/src/main/AndroidManifest.xml +14 -0
  7. package/android/src/main/java/com/scalebun/core/crash/CrashStorage.kt +155 -0
  8. package/android/src/main/java/com/scalebun/core/crash/NativeCrashHandler.kt +93 -0
  9. package/android/src/main/java/com/scalebun/core/performance/ANRWatchdog.kt +100 -0
  10. package/android/src/main/java/com/scalebun/core/performance/AppStartCollector.kt +180 -0
  11. package/android/src/main/java/com/scalebun/core/performance/FrameMetricsCollector.kt +230 -0
  12. package/android/src/main/java/com/scalebun/core/performance/PerformanceCore.kt +165 -0
  13. package/android/src/main/java/com/scalebun/profiler/ProfilerModule.kt +398 -0
  14. package/android/src/main/java/com/scalebun/profiler/ProfilerPackage.kt +24 -0
  15. package/android/src/main/java/com/scalebun/profiler/collectors/CpuCollector.kt +161 -0
  16. package/android/src/main/java/com/scalebun/profiler/collectors/IncidentDetector.kt +223 -0
  17. package/android/src/main/java/com/scalebun/profiler/collectors/MemoryCollector.kt +157 -0
  18. package/android/src/main/java/com/scalebun/profiler/collectors/RenderCollector.kt +194 -0
  19. package/android/src/main/java/com/scalebun/profiler/collectors/SamplerScheduler.kt +218 -0
  20. package/android/src/main/java/com/scalebun/profiler/collectors/ThreadCollector.kt +142 -0
  21. package/android/src/main/java/com/scalebun/replaysdk/ReplaySdkModule.kt +1117 -0
  22. package/android/src/main/java/com/scalebun/replaysdk/ReplaySdkPackage.kt +24 -0
  23. package/android/src/main/java/com/scalebun/replaysdk/capture/CaptureReason.kt +47 -0
  24. package/android/src/main/java/com/scalebun/replaysdk/capture/FrameCaptureManager.kt +574 -0
  25. package/android/src/main/java/com/scalebun/replaysdk/capture/FrameCaptureScheduler.kt +187 -0
  26. package/android/src/main/java/com/scalebun/replaysdk/capture/UiStabilityDetector.kt +214 -0
  27. package/android/src/main/java/com/scalebun/replaysdk/core/ReplayConfig.kt +144 -0
  28. package/android/src/main/java/com/scalebun/replaysdk/core/ReplayLogger.kt +31 -0
  29. package/android/src/main/java/com/scalebun/replaysdk/core/SessionIdGenerator.kt +15 -0
  30. package/android/src/main/java/com/scalebun/replaysdk/models/ReplayModels.kt +94 -0
  31. package/android/src/main/java/com/scalebun/replaysdk/outbox/NativeOutbox.kt +574 -0
  32. package/android/src/main/java/com/scalebun/replaysdk/outbox/OutboxUploader.kt +559 -0
  33. package/android/src/main/java/com/scalebun/replaysdk/privacy/PrivacyMaskProcessor.kt +126 -0
  34. package/android/src/main/java/com/scalebun/replaysdk/session/SessionManager.kt +322 -0
  35. package/android/src/main/java/com/scalebun/replaysdk/storage/ReplayStorage.kt +136 -0
  36. package/android/src/main/java/com/scalebun/replaysdk/timeline/TimelineCollector.kt +28 -0
  37. package/android/src/main/java/com/scalebun/replaysdk/tracking/InteractionTracker.kt +470 -0
  38. package/android/src/main/java/com/scalebun/replaysdk/tracking/ScreenTracker.kt +20 -0
  39. package/android/src/main/java/com/scalebun/replaysdk/tracking/ScrollContextProbe.kt +188 -0
  40. package/android/src/main/java/com/scalebun/replaysdk/transport/ReplayTransport.kt +99 -0
  41. package/android/src/main/java/com/scalebun/replaysdk/voice/VoiceRecorder.kt +121 -0
  42. package/android/src/main/java/com/scalebun/replaysdk/voice/VoiceTranscriber.kt +170 -0
  43. package/android/src/main/java/com/scalebun/rn/ScaleBunPackage.kt +113 -0
  44. package/android/src/main/java/com/scalebun/rn/crash/ScaleBunCrashModule.kt +188 -0
  45. package/android/src/main/java/com/scalebun/rn/crash/ScaleBunCrashPackage.kt +22 -0
  46. package/android/src/main/java/com/scalebun/rn/engage/ScaleBunEngageModule.kt +579 -0
  47. package/android/src/main/java/com/scalebun/rn/engage/ScaleBunEngagePackage.kt +22 -0
  48. package/android/src/main/java/com/scalebun/rn/engage/ScaleBunNotificationPresenter.kt +110 -0
  49. package/android/src/main/java/com/scalebun/rn/ota/BsPatch.kt +240 -0
  50. package/android/src/main/java/com/scalebun/rn/ota/BundleDownloader.kt +195 -0
  51. package/android/src/main/java/com/scalebun/rn/ota/DeviceIntegrity.kt +81 -0
  52. package/android/src/main/java/com/scalebun/rn/ota/DownloadFailure.kt +55 -0
  53. package/android/src/main/java/com/scalebun/rn/ota/OtaProtocol.kt +244 -0
  54. package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaKeyRegistry.kt +100 -0
  55. package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +683 -0
  56. package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaReleaseVerifier.kt +136 -0
  57. package/android/src/main/java/com/scalebun/rn/ota/SlotManager.kt +578 -0
  58. package/android/src/main/java/com/scalebun/rn/ota/TlsPinning.kt +118 -0
  59. package/android/src/main/java/com/scalebun/rn/performance/PerformanceModule.kt +217 -0
  60. package/android/src/main/java/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule.kt +77 -0
  61. package/android/src/main/java/com/scalebun/rn/storage/ScaleBunStorageModule.kt +68 -0
  62. package/android/src/main/java/com/scalebun/storage/KvFileStore.kt +174 -0
  63. package/android/src/newarch/java/com/scalebun/profiler/ProfilerSpec.kt +12 -0
  64. package/android/src/newarch/java/com/scalebun/replaysdk/ReplaySdkSpec.kt +12 -0
  65. package/android/src/newarch/java/com/scalebun/rn/crash/ScaleBunCrashSpec.kt +15 -0
  66. package/android/src/newarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +16 -0
  67. package/android/src/newarch/java/com/scalebun/rn/performance/PerformanceSpec.kt +12 -0
  68. package/android/src/newarch/java/com/scalebun/rn/referrer/ScaleBunInstallReferrerSpec.kt +14 -0
  69. package/android/src/newarch/java/com/scalebun/rn/storage/ScaleBunStorageSpec.kt +16 -0
  70. package/android/src/oldarch/java/com/scalebun/profiler/ProfilerSpec.kt +34 -0
  71. package/android/src/oldarch/java/com/scalebun/replaysdk/ReplaySdkSpec.kt +77 -0
  72. package/android/src/oldarch/java/com/scalebun/rn/crash/ScaleBunCrashSpec.kt +40 -0
  73. package/android/src/oldarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +42 -0
  74. package/android/src/oldarch/java/com/scalebun/rn/performance/PerformanceSpec.kt +27 -0
  75. package/android/src/oldarch/java/com/scalebun/rn/referrer/ScaleBunInstallReferrerSpec.kt +18 -0
  76. package/android/src/oldarch/java/com/scalebun/rn/storage/ScaleBunStorageSpec.kt +26 -0
  77. package/bin/lib/androidCodemod.js +370 -0
  78. package/bin/lib/iosCodemod.js +343 -0
  79. package/bin/scalebun.js +1004 -0
  80. package/dist/scalebun.full.js +37348 -0
  81. package/dist/scalebun.slim.js +34137 -0
  82. package/ios/Capture/InteractionTracker.swift +237 -0
  83. package/ios/Capture/PrivacyMaskProcessor.swift +158 -0
  84. package/ios/Capture/ScreenshotCaptureManager.swift +398 -0
  85. package/ios/Capture/WindowResolver.swift +63 -0
  86. package/ios/Core/DeviceIdentity.swift +50 -0
  87. package/ios/Core/ReplayConfig.swift +100 -0
  88. package/ios/Core/ReplayLogger.swift +22 -0
  89. package/ios/Core/SessionIdGenerator.swift +18 -0
  90. package/ios/Crash/ScaleBunCrashBridge.mm +74 -0
  91. package/ios/Crash/ScaleBunCrashHandler.swift +84 -0
  92. package/ios/Crash/ScaleBunCrashModule.swift +98 -0
  93. package/ios/Crash/ScaleBunCrashSignalBridge.h +49 -0
  94. package/ios/Crash/ScaleBunCrashSignalBridge.m +296 -0
  95. package/ios/Crash/ScaleBunCrashSignalStress.c +137 -0
  96. package/ios/Crash/ScaleBunCrashStorage.swift +121 -0
  97. package/ios/Crash/ScaleBunExceptionChain.swift +71 -0
  98. package/ios/CrashTests/ScaleBunExceptionChainTests.swift +144 -0
  99. package/ios/Engage/ScaleBunEngageBridge.m +32 -0
  100. package/ios/Engage/ScaleBunEngageModule.swift +250 -0
  101. package/ios/Engage/ScaleBunPushNotificationCenter.swift +185 -0
  102. package/ios/Ota/BsPatch.swift +203 -0
  103. package/ios/Ota/BundleDownloader.swift +249 -0
  104. package/ios/Ota/DeviceIntegrity.swift +112 -0
  105. package/ios/Ota/DownloadFailure.swift +56 -0
  106. package/ios/Ota/OtaProtocol.swift +246 -0
  107. package/ios/Ota/OtaSlotManager.swift +613 -0
  108. package/ios/Ota/ScaleBunOtaBridge.mm +92 -0
  109. package/ios/Ota/ScaleBunOtaEventsModule.swift +61 -0
  110. package/ios/Ota/ScaleBunOtaKeyRegistry.swift +147 -0
  111. package/ios/Ota/ScaleBunOtaModule.swift +587 -0
  112. package/ios/Ota/ScaleBunOtaReleaseVerifier.swift +119 -0
  113. package/ios/Ota/TlsPinning.swift +222 -0
  114. package/ios/OtaTests/DownloadFailureTests.swift +78 -0
  115. package/ios/OtaTests/ScaleBunOtaVerifierTests.swift +548 -0
  116. package/ios/Outbox/NativeOutbox.swift +632 -0
  117. package/ios/Outbox/OutboxUploader.swift +578 -0
  118. package/ios/Performance/ScaleBunPerformanceBridge.m +32 -0
  119. package/ios/Performance/ScaleBunPerformanceModule.swift +290 -0
  120. package/ios/PrivacyInfo.xcprivacy +97 -0
  121. package/ios/Profiler/ScaleBunProfilerBridge.m +55 -0
  122. package/ios/Profiler/ScaleBunProfilerModule.swift +966 -0
  123. package/ios/ReplaySdk-Bridging-Header.h +2 -0
  124. package/ios/ReplaySdk.swift +793 -0
  125. package/ios/ReplaySdkBridge.m +97 -0
  126. package/ios/Session/SessionManager.swift +273 -0
  127. package/ios/Skan/ScaleBunSkanBridge.m +14 -0
  128. package/ios/Skan/ScaleBunSkanModule.swift +61 -0
  129. package/ios/Storage/ReplayStorage.swift +61 -0
  130. package/ios/Storage/ScaleBunKvFileStore.swift +125 -0
  131. package/ios/Storage/ScaleBunStorageBridge.mm +72 -0
  132. package/ios/Storage/ScaleBunStorageModule.swift +57 -0
  133. package/ios/Transport/ReplayTransport.swift +70 -0
  134. package/ios/VoiceRecorder.swift +145 -0
  135. package/lib/commonjs/analytics/EventTracker.js +598 -0
  136. package/lib/commonjs/analytics/automaticEvents.js +71 -0
  137. package/lib/commonjs/analytics/batching.js +64 -0
  138. package/lib/commonjs/analytics/eventLane.js +55 -0
  139. package/lib/commonjs/analytics/revenue.js +125 -0
  140. package/lib/commonjs/analytics/subscription.js +94 -0
  141. package/lib/commonjs/bootstrap/FeatureRegistry.js +86 -0
  142. package/lib/commonjs/bootstrap/SDKBootstrapper.js +748 -0
  143. package/lib/commonjs/bucketing/fnv1a32.js +45 -0
  144. package/lib/commonjs/compat/codepush.js +153 -0
  145. package/lib/commonjs/config/ConfigManager.js +238 -0
  146. package/lib/commonjs/config/configTypes.js +16 -0
  147. package/lib/commonjs/config/otaPolicyState.js +85 -0
  148. package/lib/commonjs/config/quotaState.js +435 -0
  149. package/lib/commonjs/core/architecture.js +45 -0
  150. package/lib/commonjs/core/clock/now.js +13 -0
  151. package/lib/commonjs/core/config/defaults.js +2 -0
  152. package/lib/commonjs/core/config/schema.js +512 -0
  153. package/lib/commonjs/core/constants/endpoints.js +19 -0
  154. package/lib/commonjs/core/constants/protocol.js +31 -0
  155. package/lib/commonjs/core/constants/timings.js +39 -0
  156. package/lib/commonjs/core/constants/version.js +18 -0
  157. package/lib/commonjs/core/context/app.js +2 -0
  158. package/lib/commonjs/core/context/device.js +155 -0
  159. package/lib/commonjs/core/context/session.js +2 -0
  160. package/lib/commonjs/core/context/user.js +2 -0
  161. package/lib/commonjs/core/contracts/IFeature.js +2 -0
  162. package/lib/commonjs/core/di/container.js +29 -0
  163. package/lib/commonjs/core/encoding/base64.js +50 -0
  164. package/lib/commonjs/core/id/deviceId.js +52 -0
  165. package/lib/commonjs/core/id/installationId.js +55 -0
  166. package/lib/commonjs/core/id/sessionId.js +2 -0
  167. package/lib/commonjs/core/lifecycle/appLifecycle.js +31 -0
  168. package/lib/commonjs/core/lifecycle/crashSafe.js +33 -0
  169. package/lib/commonjs/core/logger/errorClassification.js +107 -0
  170. package/lib/commonjs/core/logger/internalLogger.js +63 -0
  171. package/lib/commonjs/core/logger/levels.js +2 -0
  172. package/lib/commonjs/crypto/hmacSha256.js +132 -0
  173. package/lib/commonjs/debug/bootstrap.js +429 -0
  174. package/lib/commonjs/debug/commands.js +732 -0
  175. package/lib/commonjs/debug/configBuilder.js +70 -0
  176. package/lib/commonjs/debug/exportBridge.js +155 -0
  177. package/lib/commonjs/debug/hostResolver.js +42 -0
  178. package/lib/commonjs/debug/index.js +141 -0
  179. package/lib/commonjs/debug/metrics.js +39 -0
  180. package/lib/commonjs/debug/perf.js +186 -0
  181. package/lib/commonjs/debug/perfWiring.js +109 -0
  182. package/lib/commonjs/debug/protocol.js +121 -0
  183. package/lib/commonjs/debug/redaction.js +422 -0
  184. package/lib/commonjs/debug/replayWiring.js +149 -0
  185. package/lib/commonjs/debug/screenTracking.js +60 -0
  186. package/lib/commonjs/debug/sessionWiring.js +48 -0
  187. package/lib/commonjs/debug/stream.js +542 -0
  188. package/lib/commonjs/debug/transport.js +368 -0
  189. package/lib/commonjs/features/bugreport/BugReportFeature.js +112 -0
  190. package/lib/commonjs/features/bugreport/buildPayload.js +97 -0
  191. package/lib/commonjs/features/bugreport/index.js +2 -0
  192. package/lib/commonjs/features/crash/CrashFeature.js +86 -0
  193. package/lib/commonjs/features/crash/CrashReporter.js +160 -0
  194. package/lib/commonjs/features/crash/NativeCrashFeature.js +42 -0
  195. package/lib/commonjs/features/crash/capture.js +48 -0
  196. package/lib/commonjs/features/crash/globalHandler.js +41 -0
  197. package/lib/commonjs/features/crash/index.js +14 -0
  198. package/lib/commonjs/features/crash/nativeCrashBridge.js +361 -0
  199. package/lib/commonjs/features/crash/rejectionHandler.js +180 -0
  200. package/lib/commonjs/features/crash/scrubCrash.js +158 -0
  201. package/lib/commonjs/features/engage/EngageAnchor.js +132 -0
  202. package/lib/commonjs/features/engage/EngageArchetypeRenderers.js +4292 -0
  203. package/lib/commonjs/features/engage/EngageInAppView.js +2670 -0
  204. package/lib/commonjs/features/engage/EngageInlinePlacement.js +133 -0
  205. package/lib/commonjs/features/engage/EngagePromptProvider.js +1063 -0
  206. package/lib/commonjs/features/engage/EngagePromptView.js +1252 -0
  207. package/lib/commonjs/features/engage/EngageTransport.js +608 -0
  208. package/lib/commonjs/features/engage/EngageVariantContent.js +1778 -0
  209. package/lib/commonjs/features/engage/__test-support__/reactNativeStub.js +54 -0
  210. package/lib/commonjs/features/engage/attachmentCapture.js +48 -0
  211. package/lib/commonjs/features/engage/captureVoiceAttachment.js +88 -0
  212. package/lib/commonjs/features/engage/engageCoachmarkTour.js +106 -0
  213. package/lib/commonjs/features/engage/engageGameLogic.js +308 -0
  214. package/lib/commonjs/features/engage/engageMediaSizing.js +60 -0
  215. package/lib/commonjs/features/engage/engageMultiStepSheet.js +43 -0
  216. package/lib/commonjs/features/engage/engageOutcome.js +118 -0
  217. package/lib/commonjs/features/engage/engagePushTokenBridge.js +72 -0
  218. package/lib/commonjs/features/engage/engageShakeBridge.js +62 -0
  219. package/lib/commonjs/features/engage/engageSignals.js +39 -0
  220. package/lib/commonjs/features/engage/engageStoreReviewBridge.js +49 -0
  221. package/lib/commonjs/features/engage/engageTestSeenStore.js +99 -0
  222. package/lib/commonjs/features/engage/engageThrottle.js +404 -0
  223. package/lib/commonjs/features/engage/engageTriggerEngine.js +365 -0
  224. package/lib/commonjs/features/engage/engageTypes.js +116 -0
  225. package/lib/commonjs/features/engage/engageVariantResolver.js +143 -0
  226. package/lib/commonjs/features/engage/engageWindowInsets.js +98 -0
  227. package/lib/commonjs/features/engage/gestureTriggerDetector.js +85 -0
  228. package/lib/commonjs/features/engage/transcription/TranscriptionProvider.js +55 -0
  229. package/lib/commonjs/features/install-referrer/installReferrer.js +113 -0
  230. package/lib/commonjs/features/journey/ScaleBunDebugRoot.js +1038 -0
  231. package/lib/commonjs/features/journey/ScaleBunFlatList.js +39 -0
  232. package/lib/commonjs/features/journey/ScaleBunImpression.js +113 -0
  233. package/lib/commonjs/features/journey/ScaleBunScrollView.js +102 -0
  234. package/lib/commonjs/features/journey/ScaleBunSectionList.js +39 -0
  235. package/lib/commonjs/features/journey/autoInstrumentScroll.js +161 -0
  236. package/lib/commonjs/features/journey/calibrationContext.js +42 -0
  237. package/lib/commonjs/features/journey/gestureBuffer.js +63 -0
  238. package/lib/commonjs/features/journey/gestureDetector.js +176 -0
  239. package/lib/commonjs/features/journey/impression.js +93 -0
  240. package/lib/commonjs/features/journey/interactionProtocol.js +168 -0
  241. package/lib/commonjs/features/journey/journeyManager.js +466 -0
  242. package/lib/commonjs/features/journey/journeyTypes.js +55 -0
  243. package/lib/commonjs/features/journey/nativeScroll.js +85 -0
  244. package/lib/commonjs/features/journey/navDetector.js +161 -0
  245. package/lib/commonjs/features/journey/screenshotHelper.js +136 -0
  246. package/lib/commonjs/features/journey/scrollContext.js +154 -0
  247. package/lib/commonjs/features/journey/targetGeometry.js +185 -0
  248. package/lib/commonjs/features/journey/targetRegistry.js +53 -0
  249. package/lib/commonjs/features/journey/touchTarget.js +249 -0
  250. package/lib/commonjs/features/journey/uiState.js +188 -0
  251. package/lib/commonjs/features/navigation/AutoScreenDetector.js +607 -0
  252. package/lib/commonjs/features/network/NetworkFeature.js +216 -0
  253. package/lib/commonjs/features/network/index.js +23 -0
  254. package/lib/commonjs/features/network/thirdParty.js +151 -0
  255. package/lib/commonjs/features/ota/OtaEventEmitter.js +129 -0
  256. package/lib/commonjs/features/ota/OtaOrchestrator.js +1309 -0
  257. package/lib/commonjs/features/ota/OtaTypes.js +6 -0
  258. package/lib/commonjs/features/ota/deviceAttributes.js +48 -0
  259. package/lib/commonjs/features/ota/environment.js +172 -0
  260. package/lib/commonjs/features/ota/geoCountry.js +93 -0
  261. package/lib/commonjs/features/ota/retry.js +96 -0
  262. package/lib/commonjs/features/ota/signedDownload.js +187 -0
  263. package/lib/commonjs/features/ota/useOtaUpdate.js +106 -0
  264. package/lib/commonjs/features/performance/PerformanceFeature.js +649 -0
  265. package/lib/commonjs/features/performance/collectors/AppLaunchCollector.js +122 -0
  266. package/lib/commonjs/features/performance/collectors/AutoScreenLoadCollector.js +232 -0
  267. package/lib/commonjs/features/performance/collectors/CustomTraceCollector.js +99 -0
  268. package/lib/commonjs/features/performance/collectors/FrameMetricsCollector.js +184 -0
  269. package/lib/commonjs/features/performance/collectors/JsStallCollector.js +165 -0
  270. package/lib/commonjs/features/performance/collectors/NetworkPerfCollector.js +59 -0
  271. package/lib/commonjs/features/performance/collectors/ScreenLoadCollector.js +78 -0
  272. package/lib/commonjs/features/performance/collectors/UiHangCollector.js +78 -0
  273. package/lib/commonjs/features/performance/config.js +58 -0
  274. package/lib/commonjs/features/performance/getNativePerformanceModule.js +44 -0
  275. package/lib/commonjs/features/performance/index.js +58 -0
  276. package/lib/commonjs/features/performance/metrics/MetricsEngine.js +148 -0
  277. package/lib/commonjs/features/performance/models.js +48 -0
  278. package/lib/commonjs/features/performance/transport/PerformanceTransport.js +168 -0
  279. package/lib/commonjs/features/profiler/ProfilerFeature.js +600 -0
  280. package/lib/commonjs/features/profiler/config.js +151 -0
  281. package/lib/commonjs/features/profiler/contracts.js +62 -0
  282. package/lib/commonjs/features/profiler/index.js +40 -0
  283. package/lib/commonjs/features/profiler/models.js +112 -0
  284. package/lib/commonjs/features/profiler/transport/ProfilerTransport.js +94 -0
  285. package/lib/commonjs/features/replay/bridge/adapters/bridgeAdapter.js +203 -0
  286. package/lib/commonjs/features/replay/bridge/eventEmitter.js +128 -0
  287. package/lib/commonjs/features/replay/bridge/nativeModule.js +95 -0
  288. package/lib/commonjs/features/replay/bridge/nativeQuota.js +121 -0
  289. package/lib/commonjs/features/replay/core/clock/clock.js +47 -0
  290. package/lib/commonjs/features/replay/core/config/defaults.js +88 -0
  291. package/lib/commonjs/features/replay/core/config/validator.js +109 -0
  292. package/lib/commonjs/features/replay/core/env/environmentDetector.js +28 -0
  293. package/lib/commonjs/features/replay/core/errors/safeCall.js +42 -0
  294. package/lib/commonjs/features/replay/core/ids/sessionId.js +53 -0
  295. package/lib/commonjs/features/replay/core/logger/logger.js +36 -0
  296. package/lib/commonjs/features/replay/core/privacy/privacyTypes.js +34 -0
  297. package/lib/commonjs/features/replay/core/queue/boundedQueue.js +71 -0
  298. package/lib/commonjs/features/replay/integrations/alert/alertInstrumentation.js +259 -0
  299. package/lib/commonjs/features/replay/integrations/logs/consoleIntegration.js +139 -0
  300. package/lib/commonjs/features/replay/integrations/network/networkAdapter.js +77 -0
  301. package/lib/commonjs/features/replay/integrations/react-navigation/navigationIntegration.js +88 -0
  302. package/lib/commonjs/features/replay/integrations/touch/ReplayRoot.js +103 -0
  303. package/lib/commonjs/features/replay/pipeline/batching/batchManager.js +119 -0
  304. package/lib/commonjs/features/replay/pipeline/envelope/envelopeBuilder.js +52 -0
  305. package/lib/commonjs/features/replay/pipeline/retry/retryPolicy.js +53 -0
  306. package/lib/commonjs/features/replay/pipeline/transport/transportTypes.js +67 -0
  307. package/lib/commonjs/features/replay/public/api.js +336 -0
  308. package/lib/commonjs/features/replay/public/enums.js +85 -0
  309. package/lib/commonjs/features/replay/public/types.js +6 -0
  310. package/lib/commonjs/features/replay/replay/breadcrumbs/breadcrumbCollector.js +119 -0
  311. package/lib/commonjs/features/replay/replay/frames/frameModels.js +40 -0
  312. package/lib/commonjs/features/replay/replay/session/sessionOrchestrator.js +279 -0
  313. package/lib/commonjs/features/replay/replay/timeline/timelineModels.js +120 -0
  314. package/lib/commonjs/features/replay/transport/BackendReplayTransport.js +175 -0
  315. package/lib/commonjs/features/replay/transport/CompositeReplayTransport.js +75 -0
  316. package/lib/commonjs/features/replay/transport/DesktopReplayTransport.js +124 -0
  317. package/lib/commonjs/features/replay/transport/ReplayTransport.js +2 -0
  318. package/lib/commonjs/features/session/BackendSessionAdapter.js +1915 -0
  319. package/lib/commonjs/features/session/DesktopSessionTransport.js +124 -0
  320. package/lib/commonjs/features/session/JourneyEventPipeline.js +244 -0
  321. package/lib/commonjs/features/session/ReplayCaptureManager.js +1253 -0
  322. package/lib/commonjs/features/session/ScrollTracker.js +147 -0
  323. package/lib/commonjs/features/session/SessionManager.js +1448 -0
  324. package/lib/commonjs/features/session/SyncDecisionEngine.js +95 -0
  325. package/lib/commonjs/features/session/frameLink.js +36 -0
  326. package/lib/commonjs/features/session/frameScrollState.js +41 -0
  327. package/lib/commonjs/features/session/index.js +84 -0
  328. package/lib/commonjs/features/session/nativeCapture.js +53 -0
  329. package/lib/commonjs/features/session/outboxFrameCapture.js +83 -0
  330. package/lib/commonjs/features/session/sessionTypes.js +230 -0
  331. package/lib/commonjs/features/session/viewportPlausibility.js +143 -0
  332. package/lib/commonjs/features/skan/skanBridge.js +54 -0
  333. package/lib/commonjs/features/skan/skanConversionManager.js +134 -0
  334. package/lib/commonjs/index.js +267 -0
  335. package/lib/commonjs/integrations/fetch/fetchInterceptor.js +174 -0
  336. package/lib/commonjs/integrations/navigation/screenTracker.js +2 -0
  337. package/lib/commonjs/integrations/network/bodyCapture.js +336 -0
  338. package/lib/commonjs/integrations/xhr/xhrInterceptor.js +224 -0
  339. package/lib/commonjs/metro/composeSourceMap.js +105 -0
  340. package/lib/commonjs/metro/featureModules.js +131 -0
  341. package/lib/commonjs/metro/index.js +230 -0
  342. package/lib/commonjs/metro/optionalDependencyStub.js +6 -0
  343. package/lib/commonjs/metro/optionalModules.js +31 -0
  344. package/lib/commonjs/metro/serializerCompose.js +161 -0
  345. package/lib/commonjs/pipeline/dispatcher/dispatcher.js +28 -0
  346. package/lib/commonjs/pipeline/envelope/envelope.js +17 -0
  347. package/lib/commonjs/pipeline/envelope/serializer.js +14 -0
  348. package/lib/commonjs/pipeline/processors/dedupe.js +2 -0
  349. package/lib/commonjs/pipeline/processors/enrich.js +16 -0
  350. package/lib/commonjs/pipeline/processors/sampling.js +2 -0
  351. package/lib/commonjs/pipeline/processors/sanitize.js +28 -0
  352. package/lib/commonjs/pipeline/queue/flushQueue.js +107 -0
  353. package/lib/commonjs/pipeline/queue/memoryQueue.js +31 -0
  354. package/lib/commonjs/pipeline/queue/persistentQueue.js +342 -0
  355. package/lib/commonjs/pipeline/queue/queuePolicy.js +2 -0
  356. package/lib/commonjs/pipeline/scheduler/flushScheduler.js +45 -0
  357. package/lib/commonjs/pipeline/scheduler/retryPolicy.js +15 -0
  358. package/lib/commonjs/public/ScaleBunErrorBoundary.js +91 -0
  359. package/lib/commonjs/public/ScaleBunFacade.js +2044 -0
  360. package/lib/commonjs/public/ScaleBunProvider.js +62 -0
  361. package/lib/commonjs/public/typedTracker.js +79 -0
  362. package/lib/commonjs/public/types.js +6 -0
  363. package/lib/commonjs/push/PushManager.js +270 -0
  364. package/lib/commonjs/push/adapters/ManualAdapter.js +86 -0
  365. package/lib/commonjs/push/adapters/NativeBridgeAdapter.js +131 -0
  366. package/lib/commonjs/push/adapters/NoopAdapter.js +45 -0
  367. package/lib/commonjs/push/adapters/RNFirebaseMessagingAdapter.js +248 -0
  368. package/lib/commonjs/push/adapters/loadMessaging.js +29 -0
  369. package/lib/commonjs/push/adapters/loadNotifee.js +36 -0
  370. package/lib/commonjs/push/adapters/selectAdapter.js +153 -0
  371. package/lib/commonjs/push/detect/capabilities.js +136 -0
  372. package/lib/commonjs/push/index.js +33 -0
  373. package/lib/commonjs/push/native/nativeNotifications.js +168 -0
  374. package/lib/commonjs/push/types.js +2 -0
  375. package/lib/commonjs/specs/NativeReplaySdk.js +28 -0
  376. package/lib/commonjs/specs/NativeScaleBunCrash.js +30 -0
  377. package/lib/commonjs/specs/NativeScaleBunInstallReferrer.js +24 -0
  378. package/lib/commonjs/specs/NativeScaleBunOta.js +32 -0
  379. package/lib/commonjs/specs/NativeScaleBunPerformance.js +25 -0
  380. package/lib/commonjs/specs/NativeScaleBunProfiler.js +26 -0
  381. package/lib/commonjs/specs/NativeScaleBunStorage.js +40 -0
  382. package/lib/commonjs/storage/StorageBackend.js +240 -0
  383. package/lib/commonjs/storage/db/sqlite.js +2 -0
  384. package/lib/commonjs/storage/files/fileStore.js +2 -0
  385. package/lib/commonjs/transport/auth/sdkSession.js +28 -0
  386. package/lib/commonjs/transport/backoff/exponentialJitter.js +16 -0
  387. package/lib/commonjs/transport/http/endpoints.js +46 -0
  388. package/lib/commonjs/transport/http/httpClient.js +72 -0
  389. package/lib/commonjs/transport/rateLimit/retryAfter.js +2 -0
  390. package/lib/commonjs/transport/signing/requestSigner.js +2 -0
  391. package/lib/commonjs/vendor/protocol/index.js +327 -0
  392. package/lib/commonjs/vendor/protocol/internal/validation.js +429 -0
  393. package/lib/commonjs/vendor/sdk-contracts/bridge.js +2 -0
  394. package/lib/commonjs/vendor/sdk-contracts/config.js +2 -0
  395. package/lib/commonjs/vendor/sdk-contracts/events.js +2 -0
  396. package/lib/commonjs/vendor/sdk-contracts/features.js +2 -0
  397. package/lib/commonjs/vendor/sdk-contracts/index.js +2 -0
  398. package/lib/commonjs/vendor/sdk-contracts/platform.js +2 -0
  399. package/lib/commonjs/vendor/sdk-contracts/session-boundary.js +6 -0
  400. package/lib/commonjs/vendor/sdk-contracts/session.js +2 -0
  401. package/lib/commonjs/vendor/vendor.manifest +10 -0
  402. package/lib/module/analytics/EventTracker.js +592 -0
  403. package/lib/module/analytics/automaticEvents.js +63 -0
  404. package/lib/module/analytics/batching.js +55 -0
  405. package/lib/module/analytics/eventLane.js +47 -0
  406. package/lib/module/analytics/revenue.js +117 -0
  407. package/lib/module/analytics/subscription.js +86 -0
  408. package/lib/module/bootstrap/FeatureRegistry.js +79 -0
  409. package/lib/module/bootstrap/SDKBootstrapper.js +742 -0
  410. package/lib/module/bucketing/fnv1a32.js +37 -0
  411. package/lib/module/compat/codepush.js +147 -0
  412. package/lib/module/config/ConfigManager.js +230 -0
  413. package/lib/module/config/configTypes.js +10 -0
  414. package/lib/module/config/otaPolicyState.js +76 -0
  415. package/lib/module/config/quotaState.js +423 -0
  416. package/lib/module/core/architecture.js +38 -0
  417. package/lib/module/core/clock/now.js +7 -0
  418. package/lib/module/core/config/defaults.js +2 -0
  419. package/lib/module/core/config/schema.js +506 -0
  420. package/lib/module/core/constants/endpoints.js +13 -0
  421. package/lib/module/core/constants/protocol.js +25 -0
  422. package/lib/module/core/constants/timings.js +26 -0
  423. package/lib/module/core/constants/version.js +12 -0
  424. package/lib/module/core/context/app.js +2 -0
  425. package/lib/module/core/context/device.js +143 -0
  426. package/lib/module/core/context/session.js +2 -0
  427. package/lib/module/core/context/user.js +2 -0
  428. package/lib/module/core/contracts/IFeature.js +2 -0
  429. package/lib/module/core/di/container.js +22 -0
  430. package/lib/module/core/encoding/base64.js +44 -0
  431. package/lib/module/core/id/deviceId.js +45 -0
  432. package/lib/module/core/id/installationId.js +50 -0
  433. package/lib/module/core/id/sessionId.js +2 -0
  434. package/lib/module/core/lifecycle/appLifecycle.js +25 -0
  435. package/lib/module/core/lifecycle/crashSafe.js +26 -0
  436. package/lib/module/core/logger/errorClassification.js +96 -0
  437. package/lib/module/core/logger/internalLogger.js +59 -0
  438. package/lib/module/core/logger/levels.js +2 -0
  439. package/lib/module/crypto/hmacSha256.js +126 -0
  440. package/lib/module/debug/bootstrap.js +393 -0
  441. package/lib/module/debug/commands.js +726 -0
  442. package/lib/module/debug/configBuilder.js +63 -0
  443. package/lib/module/debug/exportBridge.js +148 -0
  444. package/lib/module/debug/hostResolver.js +37 -0
  445. package/lib/module/debug/index.js +13 -0
  446. package/lib/module/debug/metrics.js +33 -0
  447. package/lib/module/debug/perf.js +180 -0
  448. package/lib/module/debug/perfWiring.js +102 -0
  449. package/lib/module/debug/protocol.js +19 -0
  450. package/lib/module/debug/redaction.js +411 -0
  451. package/lib/module/debug/replayWiring.js +139 -0
  452. package/lib/module/debug/screenTracking.js +53 -0
  453. package/lib/module/debug/sessionWiring.js +43 -0
  454. package/lib/module/debug/stream.js +536 -0
  455. package/lib/module/debug/transport.js +362 -0
  456. package/lib/module/features/bugreport/BugReportFeature.js +105 -0
  457. package/lib/module/features/bugreport/buildPayload.js +91 -0
  458. package/lib/module/features/bugreport/index.js +2 -0
  459. package/lib/module/features/crash/CrashFeature.js +79 -0
  460. package/lib/module/features/crash/CrashReporter.js +151 -0
  461. package/lib/module/features/crash/NativeCrashFeature.js +36 -0
  462. package/lib/module/features/crash/capture.js +42 -0
  463. package/lib/module/features/crash/globalHandler.js +36 -0
  464. package/lib/module/features/crash/index.js +8 -0
  465. package/lib/module/features/crash/nativeCrashBridge.js +350 -0
  466. package/lib/module/features/crash/rejectionHandler.js +171 -0
  467. package/lib/module/features/crash/scrubCrash.js +149 -0
  468. package/lib/module/features/engage/EngageAnchor.js +121 -0
  469. package/lib/module/features/engage/EngageArchetypeRenderers.js +4264 -0
  470. package/lib/module/features/engage/EngageInAppView.js +2656 -0
  471. package/lib/module/features/engage/EngageInlinePlacement.js +123 -0
  472. package/lib/module/features/engage/EngagePromptProvider.js +1054 -0
  473. package/lib/module/features/engage/EngagePromptView.js +1243 -0
  474. package/lib/module/features/engage/EngageTransport.js +602 -0
  475. package/lib/module/features/engage/EngageVariantContent.js +1771 -0
  476. package/lib/module/features/engage/__test-support__/reactNativeStub.js +52 -0
  477. package/lib/module/features/engage/attachmentCapture.js +42 -0
  478. package/lib/module/features/engage/captureVoiceAttachment.js +80 -0
  479. package/lib/module/features/engage/engageCoachmarkTour.js +94 -0
  480. package/lib/module/features/engage/engageGameLogic.js +287 -0
  481. package/lib/module/features/engage/engageMediaSizing.js +51 -0
  482. package/lib/module/features/engage/engageMultiStepSheet.js +35 -0
  483. package/lib/module/features/engage/engageOutcome.js +108 -0
  484. package/lib/module/features/engage/engagePushTokenBridge.js +65 -0
  485. package/lib/module/features/engage/engageShakeBridge.js +56 -0
  486. package/lib/module/features/engage/engageSignals.js +34 -0
  487. package/lib/module/features/engage/engageStoreReviewBridge.js +43 -0
  488. package/lib/module/features/engage/engageTestSeenStore.js +92 -0
  489. package/lib/module/features/engage/engageThrottle.js +390 -0
  490. package/lib/module/features/engage/engageTriggerEngine.js +360 -0
  491. package/lib/module/features/engage/engageTypes.js +110 -0
  492. package/lib/module/features/engage/engageVariantResolver.js +135 -0
  493. package/lib/module/features/engage/engageWindowInsets.js +89 -0
  494. package/lib/module/features/engage/gestureTriggerDetector.js +80 -0
  495. package/lib/module/features/engage/transcription/TranscriptionProvider.js +46 -0
  496. package/lib/module/features/install-referrer/installReferrer.js +104 -0
  497. package/lib/module/features/journey/ScaleBunDebugRoot.js +1030 -0
  498. package/lib/module/features/journey/ScaleBunFlatList.js +32 -0
  499. package/lib/module/features/journey/ScaleBunImpression.js +105 -0
  500. package/lib/module/features/journey/ScaleBunScrollView.js +95 -0
  501. package/lib/module/features/journey/ScaleBunSectionList.js +32 -0
  502. package/lib/module/features/journey/autoInstrumentScroll.js +155 -0
  503. package/lib/module/features/journey/calibrationContext.js +35 -0
  504. package/lib/module/features/journey/gestureBuffer.js +56 -0
  505. package/lib/module/features/journey/gestureDetector.js +169 -0
  506. package/lib/module/features/journey/impression.js +84 -0
  507. package/lib/module/features/journey/interactionProtocol.js +158 -0
  508. package/lib/module/features/journey/journeyManager.js +459 -0
  509. package/lib/module/features/journey/journeyTypes.js +49 -0
  510. package/lib/module/features/journey/nativeScroll.js +77 -0
  511. package/lib/module/features/journey/navDetector.js +154 -0
  512. package/lib/module/features/journey/screenshotHelper.js +129 -0
  513. package/lib/module/features/journey/scrollContext.js +142 -0
  514. package/lib/module/features/journey/targetGeometry.js +175 -0
  515. package/lib/module/features/journey/targetRegistry.js +44 -0
  516. package/lib/module/features/journey/touchTarget.js +241 -0
  517. package/lib/module/features/journey/uiState.js +176 -0
  518. package/lib/module/features/navigation/AutoScreenDetector.js +601 -0
  519. package/lib/module/features/network/NetworkFeature.js +209 -0
  520. package/lib/module/features/network/index.js +17 -0
  521. package/lib/module/features/network/thirdParty.js +142 -0
  522. package/lib/module/features/ota/OtaEventEmitter.js +123 -0
  523. package/lib/module/features/ota/OtaOrchestrator.js +1302 -0
  524. package/lib/module/features/ota/OtaTypes.js +2 -0
  525. package/lib/module/features/ota/deviceAttributes.js +43 -0
  526. package/lib/module/features/ota/environment.js +165 -0
  527. package/lib/module/features/ota/geoCountry.js +86 -0
  528. package/lib/module/features/ota/retry.js +87 -0
  529. package/lib/module/features/ota/signedDownload.js +174 -0
  530. package/lib/module/features/ota/useOtaUpdate.js +100 -0
  531. package/lib/module/features/performance/PerformanceFeature.js +643 -0
  532. package/lib/module/features/performance/collectors/AppLaunchCollector.js +115 -0
  533. package/lib/module/features/performance/collectors/AutoScreenLoadCollector.js +225 -0
  534. package/lib/module/features/performance/collectors/CustomTraceCollector.js +92 -0
  535. package/lib/module/features/performance/collectors/FrameMetricsCollector.js +177 -0
  536. package/lib/module/features/performance/collectors/JsStallCollector.js +158 -0
  537. package/lib/module/features/performance/collectors/NetworkPerfCollector.js +52 -0
  538. package/lib/module/features/performance/collectors/ScreenLoadCollector.js +71 -0
  539. package/lib/module/features/performance/collectors/UiHangCollector.js +71 -0
  540. package/lib/module/features/performance/config.js +51 -0
  541. package/lib/module/features/performance/getNativePerformanceModule.js +39 -0
  542. package/lib/module/features/performance/index.js +9 -0
  543. package/lib/module/features/performance/metrics/MetricsEngine.js +139 -0
  544. package/lib/module/features/performance/models.js +42 -0
  545. package/lib/module/features/performance/transport/PerformanceTransport.js +162 -0
  546. package/lib/module/features/profiler/ProfilerFeature.js +594 -0
  547. package/lib/module/features/profiler/config.js +143 -0
  548. package/lib/module/features/profiler/contracts.js +56 -0
  549. package/lib/module/features/profiler/index.js +11 -0
  550. package/lib/module/features/profiler/models.js +106 -0
  551. package/lib/module/features/profiler/transport/ProfilerTransport.js +87 -0
  552. package/lib/module/features/replay/bridge/adapters/bridgeAdapter.js +198 -0
  553. package/lib/module/features/replay/bridge/eventEmitter.js +122 -0
  554. package/lib/module/features/replay/bridge/nativeModule.js +89 -0
  555. package/lib/module/features/replay/bridge/nativeQuota.js +109 -0
  556. package/lib/module/features/replay/core/clock/clock.js +37 -0
  557. package/lib/module/features/replay/core/config/defaults.js +82 -0
  558. package/lib/module/features/replay/core/config/validator.js +103 -0
  559. package/lib/module/features/replay/core/env/environmentDetector.js +22 -0
  560. package/lib/module/features/replay/core/errors/safeCall.js +35 -0
  561. package/lib/module/features/replay/core/ids/sessionId.js +44 -0
  562. package/lib/module/features/replay/core/logger/logger.js +29 -0
  563. package/lib/module/features/replay/core/privacy/privacyTypes.js +27 -0
  564. package/lib/module/features/replay/core/queue/boundedQueue.js +64 -0
  565. package/lib/module/features/replay/integrations/alert/alertInstrumentation.js +252 -0
  566. package/lib/module/features/replay/integrations/logs/consoleIntegration.js +131 -0
  567. package/lib/module/features/replay/integrations/network/networkAdapter.js +70 -0
  568. package/lib/module/features/replay/integrations/react-navigation/navigationIntegration.js +82 -0
  569. package/lib/module/features/replay/integrations/touch/ReplayRoot.js +96 -0
  570. package/lib/module/features/replay/pipeline/batching/batchManager.js +113 -0
  571. package/lib/module/features/replay/pipeline/envelope/envelopeBuilder.js +43 -0
  572. package/lib/module/features/replay/pipeline/retry/retryPolicy.js +45 -0
  573. package/lib/module/features/replay/pipeline/transport/transportTypes.js +60 -0
  574. package/lib/module/features/replay/public/api.js +332 -0
  575. package/lib/module/features/replay/public/enums.js +85 -0
  576. package/lib/module/features/replay/public/types.js +2 -0
  577. package/lib/module/features/replay/replay/breadcrumbs/breadcrumbCollector.js +113 -0
  578. package/lib/module/features/replay/replay/frames/frameModels.js +33 -0
  579. package/lib/module/features/replay/replay/session/sessionOrchestrator.js +272 -0
  580. package/lib/module/features/replay/replay/timeline/timelineModels.js +108 -0
  581. package/lib/module/features/replay/transport/BackendReplayTransport.js +169 -0
  582. package/lib/module/features/replay/transport/CompositeReplayTransport.js +68 -0
  583. package/lib/module/features/replay/transport/DesktopReplayTransport.js +119 -0
  584. package/lib/module/features/replay/transport/ReplayTransport.js +2 -0
  585. package/lib/module/features/session/BackendSessionAdapter.js +1908 -0
  586. package/lib/module/features/session/DesktopSessionTransport.js +118 -0
  587. package/lib/module/features/session/JourneyEventPipeline.js +239 -0
  588. package/lib/module/features/session/ReplayCaptureManager.js +1248 -0
  589. package/lib/module/features/session/ScrollTracker.js +141 -0
  590. package/lib/module/features/session/SessionManager.js +1443 -0
  591. package/lib/module/features/session/SyncDecisionEngine.js +88 -0
  592. package/lib/module/features/session/frameLink.js +29 -0
  593. package/lib/module/features/session/frameScrollState.js +34 -0
  594. package/lib/module/features/session/index.js +35 -0
  595. package/lib/module/features/session/nativeCapture.js +45 -0
  596. package/lib/module/features/session/outboxFrameCapture.js +77 -0
  597. package/lib/module/features/session/sessionTypes.js +226 -0
  598. package/lib/module/features/session/viewportPlausibility.js +137 -0
  599. package/lib/module/features/skan/skanBridge.js +48 -0
  600. package/lib/module/features/skan/skanConversionManager.js +128 -0
  601. package/lib/module/index.js +80 -0
  602. package/lib/module/integrations/fetch/fetchInterceptor.js +169 -0
  603. package/lib/module/integrations/navigation/screenTracker.js +2 -0
  604. package/lib/module/integrations/network/bodyCapture.js +320 -0
  605. package/lib/module/integrations/xhr/xhrInterceptor.js +219 -0
  606. package/lib/module/metro/composeSourceMap.js +97 -0
  607. package/lib/module/metro/featureModules.js +123 -0
  608. package/lib/module/metro/index.js +218 -0
  609. package/lib/module/metro/optionalDependencyStub.js +14 -0
  610. package/lib/module/metro/optionalModules.js +25 -0
  611. package/lib/module/metro/serializerCompose.js +152 -0
  612. package/lib/module/pipeline/dispatcher/dispatcher.js +21 -0
  613. package/lib/module/pipeline/envelope/envelope.js +11 -0
  614. package/lib/module/pipeline/envelope/serializer.js +8 -0
  615. package/lib/module/pipeline/processors/dedupe.js +2 -0
  616. package/lib/module/pipeline/processors/enrich.js +10 -0
  617. package/lib/module/pipeline/processors/sampling.js +2 -0
  618. package/lib/module/pipeline/processors/sanitize.js +22 -0
  619. package/lib/module/pipeline/queue/flushQueue.js +102 -0
  620. package/lib/module/pipeline/queue/memoryQueue.js +24 -0
  621. package/lib/module/pipeline/queue/persistentQueue.js +336 -0
  622. package/lib/module/pipeline/queue/queuePolicy.js +2 -0
  623. package/lib/module/pipeline/scheduler/flushScheduler.js +38 -0
  624. package/lib/module/pipeline/scheduler/retryPolicy.js +9 -0
  625. package/lib/module/public/ScaleBunErrorBoundary.js +82 -0
  626. package/lib/module/public/ScaleBunFacade.js +2038 -0
  627. package/lib/module/public/ScaleBunProvider.js +54 -0
  628. package/lib/module/public/typedTracker.js +72 -0
  629. package/lib/module/public/types.js +2 -0
  630. package/lib/module/push/PushManager.js +263 -0
  631. package/lib/module/push/adapters/ManualAdapter.js +79 -0
  632. package/lib/module/push/adapters/NativeBridgeAdapter.js +124 -0
  633. package/lib/module/push/adapters/NoopAdapter.js +38 -0
  634. package/lib/module/push/adapters/RNFirebaseMessagingAdapter.js +241 -0
  635. package/lib/module/push/adapters/loadMessaging.js +23 -0
  636. package/lib/module/push/adapters/loadNotifee.js +30 -0
  637. package/lib/module/push/adapters/selectAdapter.js +147 -0
  638. package/lib/module/push/detect/capabilities.js +130 -0
  639. package/lib/module/push/index.js +9 -0
  640. package/lib/module/push/native/nativeNotifications.js +155 -0
  641. package/lib/module/push/types.js +2 -0
  642. package/lib/module/specs/NativeReplaySdk.js +24 -0
  643. package/lib/module/specs/NativeScaleBunCrash.js +26 -0
  644. package/lib/module/specs/NativeScaleBunInstallReferrer.js +20 -0
  645. package/lib/module/specs/NativeScaleBunOta.js +28 -0
  646. package/lib/module/specs/NativeScaleBunPerformance.js +21 -0
  647. package/lib/module/specs/NativeScaleBunProfiler.js +22 -0
  648. package/lib/module/specs/NativeScaleBunStorage.js +36 -0
  649. package/lib/module/storage/StorageBackend.js +231 -0
  650. package/lib/module/storage/db/sqlite.js +2 -0
  651. package/lib/module/storage/files/fileStore.js +2 -0
  652. package/lib/module/transport/auth/sdkSession.js +21 -0
  653. package/lib/module/transport/backoff/exponentialJitter.js +10 -0
  654. package/lib/module/transport/http/endpoints.js +40 -0
  655. package/lib/module/transport/http/httpClient.js +64 -0
  656. package/lib/module/transport/rateLimit/retryAfter.js +2 -0
  657. package/lib/module/transport/signing/requestSigner.js +2 -0
  658. package/lib/module/vendor/protocol/index.js +312 -0
  659. package/lib/module/vendor/protocol/internal/validation.js +423 -0
  660. package/lib/module/vendor/sdk-contracts/bridge.js +2 -0
  661. package/lib/module/vendor/sdk-contracts/config.js +2 -0
  662. package/lib/module/vendor/sdk-contracts/events.js +2 -0
  663. package/lib/module/vendor/sdk-contracts/features.js +2 -0
  664. package/lib/module/vendor/sdk-contracts/index.js +2 -0
  665. package/lib/module/vendor/sdk-contracts/platform.js +2 -0
  666. package/lib/module/vendor/sdk-contracts/session-boundary.js +2 -0
  667. package/lib/module/vendor/sdk-contracts/session.js +2 -0
  668. package/lib/module/vendor/vendor.manifest +10 -0
  669. package/lib/typescript/analytics/EventTracker.d.ts +197 -0
  670. package/lib/typescript/analytics/automaticEvents.d.ts +30 -0
  671. package/lib/typescript/analytics/batching.d.ts +33 -0
  672. package/lib/typescript/analytics/eventLane.d.ts +22 -0
  673. package/lib/typescript/analytics/revenue.d.ts +94 -0
  674. package/lib/typescript/analytics/subscription.d.ts +35 -0
  675. package/lib/typescript/bootstrap/FeatureRegistry.d.ts +36 -0
  676. package/lib/typescript/bootstrap/SDKBootstrapper.d.ts +82 -0
  677. package/lib/typescript/bucketing/fnv1a32.d.ts +20 -0
  678. package/lib/typescript/compat/codepush.d.ts +76 -0
  679. package/lib/typescript/config/ConfigManager.d.ts +82 -0
  680. package/lib/typescript/config/configTypes.d.ts +73 -0
  681. package/lib/typescript/config/otaPolicyState.d.ts +55 -0
  682. package/lib/typescript/config/quotaState.d.ts +155 -0
  683. package/lib/typescript/core/architecture.d.ts +29 -0
  684. package/lib/typescript/core/clock/now.d.ts +5 -0
  685. package/lib/typescript/core/config/defaults.d.ts +1 -0
  686. package/lib/typescript/core/config/schema.d.ts +107 -0
  687. package/lib/typescript/core/constants/endpoints.d.ts +13 -0
  688. package/lib/typescript/core/constants/protocol.d.ts +23 -0
  689. package/lib/typescript/core/constants/timings.d.ts +22 -0
  690. package/lib/typescript/core/constants/version.d.ts +12 -0
  691. package/lib/typescript/core/context/app.d.ts +1 -0
  692. package/lib/typescript/core/context/device.d.ts +61 -0
  693. package/lib/typescript/core/context/session.d.ts +1 -0
  694. package/lib/typescript/core/context/user.d.ts +24 -0
  695. package/lib/typescript/core/contracts/IFeature.d.ts +44 -0
  696. package/lib/typescript/core/di/container.d.ts +9 -0
  697. package/lib/typescript/core/encoding/base64.d.ts +13 -0
  698. package/lib/typescript/core/id/deviceId.d.ts +9 -0
  699. package/lib/typescript/core/id/installationId.d.ts +20 -0
  700. package/lib/typescript/core/id/sessionId.d.ts +1 -0
  701. package/lib/typescript/core/lifecycle/appLifecycle.d.ts +14 -0
  702. package/lib/typescript/core/lifecycle/crashSafe.d.ts +7 -0
  703. package/lib/typescript/core/logger/errorClassification.d.ts +46 -0
  704. package/lib/typescript/core/logger/internalLogger.d.ts +24 -0
  705. package/lib/typescript/core/logger/levels.d.ts +1 -0
  706. package/lib/typescript/crypto/hmacSha256.d.ts +10 -0
  707. package/lib/typescript/debug/bootstrap.d.ts +148 -0
  708. package/lib/typescript/debug/commands.d.ts +77 -0
  709. package/lib/typescript/debug/configBuilder.d.ts +13 -0
  710. package/lib/typescript/debug/exportBridge.d.ts +27 -0
  711. package/lib/typescript/debug/hostResolver.d.ts +14 -0
  712. package/lib/typescript/debug/index.d.ts +12 -0
  713. package/lib/typescript/debug/metrics.d.ts +2 -0
  714. package/lib/typescript/debug/perf.d.ts +43 -0
  715. package/lib/typescript/debug/perfWiring.d.ts +33 -0
  716. package/lib/typescript/debug/protocol.d.ts +15 -0
  717. package/lib/typescript/debug/redaction.d.ts +69 -0
  718. package/lib/typescript/debug/replayWiring.d.ts +25 -0
  719. package/lib/typescript/debug/screenTracking.d.ts +16 -0
  720. package/lib/typescript/debug/sessionWiring.d.ts +16 -0
  721. package/lib/typescript/debug/stream.d.ts +77 -0
  722. package/lib/typescript/debug/transport.d.ts +110 -0
  723. package/lib/typescript/features/bugreport/BugReportFeature.d.ts +59 -0
  724. package/lib/typescript/features/bugreport/buildPayload.d.ts +51 -0
  725. package/lib/typescript/features/bugreport/index.d.ts +1 -0
  726. package/lib/typescript/features/crash/CrashFeature.d.ts +21 -0
  727. package/lib/typescript/features/crash/CrashReporter.d.ts +107 -0
  728. package/lib/typescript/features/crash/NativeCrashFeature.d.ts +17 -0
  729. package/lib/typescript/features/crash/capture.d.ts +22 -0
  730. package/lib/typescript/features/crash/globalHandler.d.ts +14 -0
  731. package/lib/typescript/features/crash/index.d.ts +2 -0
  732. package/lib/typescript/features/crash/nativeCrashBridge.d.ts +43 -0
  733. package/lib/typescript/features/crash/rejectionHandler.d.ts +65 -0
  734. package/lib/typescript/features/crash/scrubCrash.d.ts +35 -0
  735. package/lib/typescript/features/engage/EngageAnchor.d.ts +36 -0
  736. package/lib/typescript/features/engage/EngageArchetypeRenderers.d.ts +66 -0
  737. package/lib/typescript/features/engage/EngageInAppView.d.ts +164 -0
  738. package/lib/typescript/features/engage/EngageInlinePlacement.d.ts +42 -0
  739. package/lib/typescript/features/engage/EngagePromptProvider.d.ts +233 -0
  740. package/lib/typescript/features/engage/EngagePromptView.d.ts +97 -0
  741. package/lib/typescript/features/engage/EngageTransport.d.ts +158 -0
  742. package/lib/typescript/features/engage/EngageVariantContent.d.ts +12 -0
  743. package/lib/typescript/features/engage/attachmentCapture.d.ts +17 -0
  744. package/lib/typescript/features/engage/captureVoiceAttachment.d.ts +31 -0
  745. package/lib/typescript/features/engage/engageCoachmarkTour.d.ts +18 -0
  746. package/lib/typescript/features/engage/engageGameLogic.d.ts +34 -0
  747. package/lib/typescript/features/engage/engageMediaSizing.d.ts +23 -0
  748. package/lib/typescript/features/engage/engageMultiStepSheet.d.ts +25 -0
  749. package/lib/typescript/features/engage/engageOutcome.d.ts +38 -0
  750. package/lib/typescript/features/engage/engagePushTokenBridge.d.ts +30 -0
  751. package/lib/typescript/features/engage/engageShakeBridge.d.ts +20 -0
  752. package/lib/typescript/features/engage/engageSignals.d.ts +13 -0
  753. package/lib/typescript/features/engage/engageStoreReviewBridge.d.ts +23 -0
  754. package/lib/typescript/features/engage/engageTestSeenStore.d.ts +46 -0
  755. package/lib/typescript/features/engage/engageThrottle.d.ts +180 -0
  756. package/lib/typescript/features/engage/engageTriggerEngine.d.ts +16 -0
  757. package/lib/typescript/features/engage/engageTypes.d.ts +576 -0
  758. package/lib/typescript/features/engage/engageVariantResolver.d.ts +46 -0
  759. package/lib/typescript/features/engage/engageWindowInsets.d.ts +25 -0
  760. package/lib/typescript/features/engage/gestureTriggerDetector.d.ts +20 -0
  761. package/lib/typescript/features/engage/transcription/TranscriptionProvider.d.ts +51 -0
  762. package/lib/typescript/features/install-referrer/installReferrer.d.ts +39 -0
  763. package/lib/typescript/features/journey/ScaleBunDebugRoot.d.ts +49 -0
  764. package/lib/typescript/features/journey/ScaleBunFlatList.d.ts +20 -0
  765. package/lib/typescript/features/journey/ScaleBunImpression.d.ts +30 -0
  766. package/lib/typescript/features/journey/ScaleBunScrollView.d.ts +25 -0
  767. package/lib/typescript/features/journey/ScaleBunSectionList.d.ts +20 -0
  768. package/lib/typescript/features/journey/autoInstrumentScroll.d.ts +46 -0
  769. package/lib/typescript/features/journey/calibrationContext.d.ts +26 -0
  770. package/lib/typescript/features/journey/gestureBuffer.d.ts +27 -0
  771. package/lib/typescript/features/journey/gestureDetector.d.ts +40 -0
  772. package/lib/typescript/features/journey/impression.d.ts +48 -0
  773. package/lib/typescript/features/journey/interactionProtocol.d.ts +159 -0
  774. package/lib/typescript/features/journey/journeyManager.d.ts +62 -0
  775. package/lib/typescript/features/journey/journeyTypes.d.ts +161 -0
  776. package/lib/typescript/features/journey/nativeScroll.d.ts +47 -0
  777. package/lib/typescript/features/journey/navDetector.d.ts +42 -0
  778. package/lib/typescript/features/journey/screenshotHelper.d.ts +33 -0
  779. package/lib/typescript/features/journey/scrollContext.d.ts +49 -0
  780. package/lib/typescript/features/journey/targetGeometry.d.ts +126 -0
  781. package/lib/typescript/features/journey/targetRegistry.d.ts +28 -0
  782. package/lib/typescript/features/journey/touchTarget.d.ts +174 -0
  783. package/lib/typescript/features/journey/uiState.d.ts +93 -0
  784. package/lib/typescript/features/navigation/AutoScreenDetector.d.ts +196 -0
  785. package/lib/typescript/features/network/NetworkFeature.d.ts +43 -0
  786. package/lib/typescript/features/network/index.d.ts +46 -0
  787. package/lib/typescript/features/network/thirdParty.d.ts +95 -0
  788. package/lib/typescript/features/ota/OtaEventEmitter.d.ts +93 -0
  789. package/lib/typescript/features/ota/OtaOrchestrator.d.ts +287 -0
  790. package/lib/typescript/features/ota/OtaTypes.d.ts +193 -0
  791. package/lib/typescript/features/ota/deviceAttributes.d.ts +18 -0
  792. package/lib/typescript/features/ota/environment.d.ts +99 -0
  793. package/lib/typescript/features/ota/geoCountry.d.ts +14 -0
  794. package/lib/typescript/features/ota/retry.d.ts +53 -0
  795. package/lib/typescript/features/ota/signedDownload.d.ts +81 -0
  796. package/lib/typescript/features/ota/useOtaUpdate.d.ts +72 -0
  797. package/lib/typescript/features/performance/PerformanceFeature.d.ts +132 -0
  798. package/lib/typescript/features/performance/collectors/AppLaunchCollector.d.ts +27 -0
  799. package/lib/typescript/features/performance/collectors/AutoScreenLoadCollector.d.ts +70 -0
  800. package/lib/typescript/features/performance/collectors/CustomTraceCollector.d.ts +35 -0
  801. package/lib/typescript/features/performance/collectors/FrameMetricsCollector.d.ts +74 -0
  802. package/lib/typescript/features/performance/collectors/JsStallCollector.d.ts +45 -0
  803. package/lib/typescript/features/performance/collectors/NetworkPerfCollector.d.ts +23 -0
  804. package/lib/typescript/features/performance/collectors/ScreenLoadCollector.d.ts +30 -0
  805. package/lib/typescript/features/performance/collectors/UiHangCollector.d.ts +21 -0
  806. package/lib/typescript/features/performance/config.d.ts +73 -0
  807. package/lib/typescript/features/performance/getNativePerformanceModule.d.ts +2 -0
  808. package/lib/typescript/features/performance/index.d.ts +10 -0
  809. package/lib/typescript/features/performance/metrics/MetricsEngine.d.ts +69 -0
  810. package/lib/typescript/features/performance/models.d.ts +169 -0
  811. package/lib/typescript/features/performance/transport/PerformanceTransport.d.ts +40 -0
  812. package/lib/typescript/features/profiler/ProfilerFeature.d.ts +92 -0
  813. package/lib/typescript/features/profiler/config.d.ts +85 -0
  814. package/lib/typescript/features/profiler/contracts.d.ts +142 -0
  815. package/lib/typescript/features/profiler/index.d.ts +13 -0
  816. package/lib/typescript/features/profiler/models.d.ts +332 -0
  817. package/lib/typescript/features/profiler/transport/ProfilerTransport.d.ts +30 -0
  818. package/lib/typescript/features/replay/bridge/adapters/bridgeAdapter.d.ts +52 -0
  819. package/lib/typescript/features/replay/bridge/eventEmitter.d.ts +100 -0
  820. package/lib/typescript/features/replay/bridge/nativeModule.d.ts +166 -0
  821. package/lib/typescript/features/replay/bridge/nativeQuota.d.ts +26 -0
  822. package/lib/typescript/features/replay/core/clock/clock.d.ts +16 -0
  823. package/lib/typescript/features/replay/core/config/defaults.d.ts +28 -0
  824. package/lib/typescript/features/replay/core/config/validator.d.ts +13 -0
  825. package/lib/typescript/features/replay/core/env/environmentDetector.d.ts +14 -0
  826. package/lib/typescript/features/replay/core/errors/safeCall.d.ts +12 -0
  827. package/lib/typescript/features/replay/core/ids/sessionId.d.ts +15 -0
  828. package/lib/typescript/features/replay/core/logger/logger.d.ts +15 -0
  829. package/lib/typescript/features/replay/core/privacy/privacyTypes.d.ts +24 -0
  830. package/lib/typescript/features/replay/core/queue/boundedQueue.d.ts +29 -0
  831. package/lib/typescript/features/replay/integrations/alert/alertInstrumentation.d.ts +48 -0
  832. package/lib/typescript/features/replay/integrations/logs/consoleIntegration.d.ts +18 -0
  833. package/lib/typescript/features/replay/integrations/network/networkAdapter.d.ts +31 -0
  834. package/lib/typescript/features/replay/integrations/react-navigation/navigationIntegration.d.ts +25 -0
  835. package/lib/typescript/features/replay/integrations/touch/ReplayRoot.d.ts +26 -0
  836. package/lib/typescript/features/replay/pipeline/batching/batchManager.d.ts +47 -0
  837. package/lib/typescript/features/replay/pipeline/envelope/envelopeBuilder.d.ts +12 -0
  838. package/lib/typescript/features/replay/pipeline/retry/retryPolicy.d.ts +17 -0
  839. package/lib/typescript/features/replay/pipeline/transport/transportTypes.d.ts +40 -0
  840. package/lib/typescript/features/replay/public/api.d.ts +83 -0
  841. package/lib/typescript/features/replay/public/enums.d.ts +73 -0
  842. package/lib/typescript/features/replay/public/types.d.ts +249 -0
  843. package/lib/typescript/features/replay/replay/breadcrumbs/breadcrumbCollector.d.ts +26 -0
  844. package/lib/typescript/features/replay/replay/frames/frameModels.d.ts +23 -0
  845. package/lib/typescript/features/replay/replay/session/sessionOrchestrator.d.ts +53 -0
  846. package/lib/typescript/features/replay/replay/timeline/timelineModels.d.ts +29 -0
  847. package/lib/typescript/features/replay/transport/BackendReplayTransport.d.ts +45 -0
  848. package/lib/typescript/features/replay/transport/CompositeReplayTransport.d.ts +22 -0
  849. package/lib/typescript/features/replay/transport/DesktopReplayTransport.d.ts +35 -0
  850. package/lib/typescript/features/replay/transport/ReplayTransport.d.ts +72 -0
  851. package/lib/typescript/features/session/BackendSessionAdapter.d.ts +528 -0
  852. package/lib/typescript/features/session/DesktopSessionTransport.d.ts +28 -0
  853. package/lib/typescript/features/session/JourneyEventPipeline.d.ts +74 -0
  854. package/lib/typescript/features/session/ReplayCaptureManager.d.ts +391 -0
  855. package/lib/typescript/features/session/ScrollTracker.d.ts +48 -0
  856. package/lib/typescript/features/session/SessionManager.d.ts +414 -0
  857. package/lib/typescript/features/session/SyncDecisionEngine.d.ts +33 -0
  858. package/lib/typescript/features/session/frameLink.d.ts +27 -0
  859. package/lib/typescript/features/session/frameScrollState.d.ts +27 -0
  860. package/lib/typescript/features/session/index.d.ts +23 -0
  861. package/lib/typescript/features/session/nativeCapture.d.ts +18 -0
  862. package/lib/typescript/features/session/outboxFrameCapture.d.ts +20 -0
  863. package/lib/typescript/features/session/sessionTypes.d.ts +362 -0
  864. package/lib/typescript/features/session/viewportPlausibility.d.ts +72 -0
  865. package/lib/typescript/features/skan/skanBridge.d.ts +18 -0
  866. package/lib/typescript/features/skan/skanConversionManager.d.ts +69 -0
  867. package/lib/typescript/index.d.ts +59 -0
  868. package/lib/typescript/integrations/fetch/fetchInterceptor.d.ts +41 -0
  869. package/lib/typescript/integrations/navigation/screenTracker.d.ts +1 -0
  870. package/lib/typescript/integrations/network/bodyCapture.d.ts +101 -0
  871. package/lib/typescript/integrations/xhr/xhrInterceptor.d.ts +46 -0
  872. package/lib/typescript/metro/composeSourceMap.d.ts +26 -0
  873. package/lib/typescript/metro/featureModules.d.ts +49 -0
  874. package/lib/typescript/metro/index.d.ts +99 -0
  875. package/lib/typescript/metro/optionalDependencyStub.d.ts +14 -0
  876. package/lib/typescript/metro/optionalModules.d.ts +20 -0
  877. package/lib/typescript/metro/serializerCompose.d.ts +46 -0
  878. package/lib/typescript/pipeline/dispatcher/dispatcher.d.ts +7 -0
  879. package/lib/typescript/pipeline/envelope/envelope.d.ts +16 -0
  880. package/lib/typescript/pipeline/envelope/serializer.d.ts +7 -0
  881. package/lib/typescript/pipeline/processors/dedupe.d.ts +1 -0
  882. package/lib/typescript/pipeline/processors/enrich.d.ts +3 -0
  883. package/lib/typescript/pipeline/processors/sampling.d.ts +1 -0
  884. package/lib/typescript/pipeline/processors/sanitize.d.ts +3 -0
  885. package/lib/typescript/pipeline/queue/flushQueue.d.ts +12 -0
  886. package/lib/typescript/pipeline/queue/memoryQueue.d.ts +12 -0
  887. package/lib/typescript/pipeline/queue/persistentQueue.d.ts +120 -0
  888. package/lib/typescript/pipeline/queue/queuePolicy.d.ts +1 -0
  889. package/lib/typescript/pipeline/scheduler/flushScheduler.d.ts +9 -0
  890. package/lib/typescript/pipeline/scheduler/retryPolicy.d.ts +5 -0
  891. package/lib/typescript/public/ScaleBunErrorBoundary.d.ts +44 -0
  892. package/lib/typescript/public/ScaleBunFacade.d.ts +800 -0
  893. package/lib/typescript/public/ScaleBunProvider.d.ts +23 -0
  894. package/lib/typescript/public/typedTracker.d.ts +88 -0
  895. package/lib/typescript/public/types.d.ts +316 -0
  896. package/lib/typescript/push/PushManager.d.ts +69 -0
  897. package/lib/typescript/push/adapters/ManualAdapter.d.ts +40 -0
  898. package/lib/typescript/push/adapters/NativeBridgeAdapter.d.ts +37 -0
  899. package/lib/typescript/push/adapters/NoopAdapter.d.ts +21 -0
  900. package/lib/typescript/push/adapters/RNFirebaseMessagingAdapter.d.ts +39 -0
  901. package/lib/typescript/push/adapters/loadMessaging.d.ts +17 -0
  902. package/lib/typescript/push/adapters/loadNotifee.d.ts +15 -0
  903. package/lib/typescript/push/adapters/selectAdapter.d.ts +20 -0
  904. package/lib/typescript/push/detect/capabilities.d.ts +15 -0
  905. package/lib/typescript/push/index.d.ts +10 -0
  906. package/lib/typescript/push/native/nativeNotifications.d.ts +65 -0
  907. package/lib/typescript/push/types.d.ts +143 -0
  908. package/lib/typescript/specs/NativeReplaySdk.d.ts +69 -0
  909. package/lib/typescript/specs/NativeScaleBunCrash.d.ts +58 -0
  910. package/lib/typescript/specs/NativeScaleBunInstallReferrer.d.ts +28 -0
  911. package/lib/typescript/specs/NativeScaleBunOta.d.ts +141 -0
  912. package/lib/typescript/specs/NativeScaleBunPerformance.d.ts +31 -0
  913. package/lib/typescript/specs/NativeScaleBunProfiler.d.ts +38 -0
  914. package/lib/typescript/specs/NativeScaleBunStorage.d.ts +55 -0
  915. package/lib/typescript/storage/StorageBackend.d.ts +131 -0
  916. package/lib/typescript/storage/db/sqlite.d.ts +1 -0
  917. package/lib/typescript/storage/files/fileStore.d.ts +1 -0
  918. package/lib/typescript/transport/auth/sdkSession.d.ts +10 -0
  919. package/lib/typescript/transport/backoff/exponentialJitter.d.ts +5 -0
  920. package/lib/typescript/transport/http/endpoints.d.ts +26 -0
  921. package/lib/typescript/transport/http/httpClient.d.ts +23 -0
  922. package/lib/typescript/transport/rateLimit/retryAfter.d.ts +1 -0
  923. package/lib/typescript/transport/signing/requestSigner.d.ts +1 -0
  924. package/lib/typescript/vendor/protocol/index.d.ts +345 -0
  925. package/lib/typescript/vendor/protocol/internal/validation.d.ts +114 -0
  926. package/lib/typescript/vendor/sdk-contracts/bridge.d.ts +104 -0
  927. package/lib/typescript/vendor/sdk-contracts/config.d.ts +156 -0
  928. package/lib/typescript/vendor/sdk-contracts/events.d.ts +42 -0
  929. package/lib/typescript/vendor/sdk-contracts/features.d.ts +38 -0
  930. package/lib/typescript/vendor/sdk-contracts/index.d.ts +15 -0
  931. package/lib/typescript/vendor/sdk-contracts/platform.d.ts +35 -0
  932. package/lib/typescript/vendor/sdk-contracts/session-boundary.d.ts +42 -0
  933. package/lib/typescript/vendor/sdk-contracts/session.d.ts +157 -0
  934. package/package.json +153 -0
  935. package/react-native.config.js +24 -0
  936. package/scalebun-react-native.podspec +44 -0
  937. package/src/analytics/EventTracker.ts +654 -0
  938. package/src/analytics/automaticEvents.ts +109 -0
  939. package/src/analytics/batching.ts +54 -0
  940. package/src/analytics/eventLane.ts +48 -0
  941. package/src/analytics/revenue.ts +143 -0
  942. package/src/analytics/subscription.ts +145 -0
  943. package/src/bootstrap/FeatureRegistry.ts +81 -0
  944. package/src/bootstrap/SDKBootstrapper.ts +769 -0
  945. package/src/bucketing/fnv1a32.ts +40 -0
  946. package/src/compat/codepush.ts +189 -0
  947. package/src/config/ConfigManager.ts +233 -0
  948. package/src/config/configTypes.ts +62 -0
  949. package/src/config/otaPolicyState.ts +79 -0
  950. package/src/config/quotaState.ts +484 -0
  951. package/src/core/architecture.ts +39 -0
  952. package/src/core/clock/now.ts +6 -0
  953. package/src/core/config/defaults.ts +0 -0
  954. package/src/core/config/schema.ts +455 -0
  955. package/src/core/constants/endpoints.ts +12 -0
  956. package/src/core/constants/protocol.ts +24 -0
  957. package/src/core/constants/timings.ts +25 -0
  958. package/src/core/constants/version.ts +11 -0
  959. package/src/core/context/app.ts +0 -0
  960. package/src/core/context/device.ts +169 -0
  961. package/src/core/context/session.ts +0 -0
  962. package/src/core/context/user.ts +26 -0
  963. package/src/core/contracts/IFeature.ts +53 -0
  964. package/src/core/di/container.ts +26 -0
  965. package/src/core/encoding/base64.ts +46 -0
  966. package/src/core/id/deviceId.ts +51 -0
  967. package/src/core/id/installationId.ts +52 -0
  968. package/src/core/id/sessionId.ts +0 -0
  969. package/src/core/lifecycle/appLifecycle.ts +33 -0
  970. package/src/core/lifecycle/crashSafe.ts +29 -0
  971. package/src/core/logger/errorClassification.ts +108 -0
  972. package/src/core/logger/internalLogger.ts +65 -0
  973. package/src/core/logger/levels.ts +0 -0
  974. package/src/crypto/hmacSha256.ts +106 -0
  975. package/src/debug/bootstrap.ts +436 -0
  976. package/src/debug/commands.ts +597 -0
  977. package/src/debug/configBuilder.ts +56 -0
  978. package/src/debug/exportBridge.ts +155 -0
  979. package/src/debug/hostResolver.ts +34 -0
  980. package/src/debug/index.ts +25 -0
  981. package/src/debug/metrics.ts +29 -0
  982. package/src/debug/perf.ts +212 -0
  983. package/src/debug/perfWiring.ts +119 -0
  984. package/src/debug/protocol.ts +58 -0
  985. package/src/debug/redaction.ts +483 -0
  986. package/src/debug/replayWiring.ts +148 -0
  987. package/src/debug/screenTracking.ts +55 -0
  988. package/src/debug/sessionWiring.ts +59 -0
  989. package/src/debug/stream.ts +561 -0
  990. package/src/debug/transport.ts +440 -0
  991. package/src/features/bugreport/BugReportFeature.ts +128 -0
  992. package/src/features/bugreport/buildPayload.ts +116 -0
  993. package/src/features/bugreport/index.ts +0 -0
  994. package/src/features/crash/CrashFeature.ts +87 -0
  995. package/src/features/crash/CrashReporter.ts +189 -0
  996. package/src/features/crash/NativeCrashFeature.ts +42 -0
  997. package/src/features/crash/capture.ts +48 -0
  998. package/src/features/crash/globalHandler.ts +39 -0
  999. package/src/features/crash/index.ts +8 -0
  1000. package/src/features/crash/nativeCrashBridge.ts +352 -0
  1001. package/src/features/crash/rejectionHandler.ts +196 -0
  1002. package/src/features/crash/scrubCrash.ts +175 -0
  1003. package/src/features/engage/EngageAnchor.tsx +159 -0
  1004. package/src/features/engage/EngageArchetypeRenderers.tsx +3705 -0
  1005. package/src/features/engage/EngageInAppView.tsx +2678 -0
  1006. package/src/features/engage/EngageInlinePlacement.tsx +169 -0
  1007. package/src/features/engage/EngagePromptProvider.tsx +1318 -0
  1008. package/src/features/engage/EngagePromptView.tsx +1400 -0
  1009. package/src/features/engage/EngageTransport.ts +668 -0
  1010. package/src/features/engage/EngageVariantContent.tsx +1483 -0
  1011. package/src/features/engage/__test-support__/reactNativeStub.js +40 -0
  1012. package/src/features/engage/attachmentCapture.ts +45 -0
  1013. package/src/features/engage/captureVoiceAttachment.ts +81 -0
  1014. package/src/features/engage/engageCoachmarkTour.ts +120 -0
  1015. package/src/features/engage/engageGameLogic.ts +347 -0
  1016. package/src/features/engage/engageMediaSizing.ts +76 -0
  1017. package/src/features/engage/engageMultiStepSheet.ts +53 -0
  1018. package/src/features/engage/engageOutcome.ts +157 -0
  1019. package/src/features/engage/engagePushTokenBridge.ts +84 -0
  1020. package/src/features/engage/engageShakeBridge.ts +67 -0
  1021. package/src/features/engage/engageSignals.ts +40 -0
  1022. package/src/features/engage/engageStoreReviewBridge.ts +48 -0
  1023. package/src/features/engage/engageTestSeenStore.ts +95 -0
  1024. package/src/features/engage/engageThrottle.ts +478 -0
  1025. package/src/features/engage/engageTriggerEngine.ts +350 -0
  1026. package/src/features/engage/engageTypes.ts +712 -0
  1027. package/src/features/engage/engageVariantResolver.ts +364 -0
  1028. package/src/features/engage/engageWindowInsets.ts +108 -0
  1029. package/src/features/engage/gestureTriggerDetector.ts +83 -0
  1030. package/src/features/engage/transcription/TranscriptionProvider.ts +68 -0
  1031. package/src/features/install-referrer/installReferrer.ts +112 -0
  1032. package/src/features/journey/ScaleBunDebugRoot.tsx +1045 -0
  1033. package/src/features/journey/ScaleBunFlatList.tsx +39 -0
  1034. package/src/features/journey/ScaleBunImpression.tsx +109 -0
  1035. package/src/features/journey/ScaleBunScrollView.tsx +101 -0
  1036. package/src/features/journey/ScaleBunSectionList.tsx +39 -0
  1037. package/src/features/journey/autoInstrumentScroll.ts +155 -0
  1038. package/src/features/journey/calibrationContext.ts +44 -0
  1039. package/src/features/journey/gestureBuffer.ts +59 -0
  1040. package/src/features/journey/gestureDetector.ts +162 -0
  1041. package/src/features/journey/impression.ts +92 -0
  1042. package/src/features/journey/interactionProtocol.ts +243 -0
  1043. package/src/features/journey/journeyManager.ts +469 -0
  1044. package/src/features/journey/journeyTypes.ts +215 -0
  1045. package/src/features/journey/nativeScroll.ts +94 -0
  1046. package/src/features/journey/navDetector.ts +149 -0
  1047. package/src/features/journey/screenshotHelper.ts +137 -0
  1048. package/src/features/journey/scrollContext.ts +146 -0
  1049. package/src/features/journey/targetGeometry.ts +197 -0
  1050. package/src/features/journey/targetRegistry.ts +43 -0
  1051. package/src/features/journey/touchTarget.ts +339 -0
  1052. package/src/features/journey/uiState.ts +182 -0
  1053. package/src/features/navigation/AutoScreenDetector.ts +617 -0
  1054. package/src/features/network/NetworkFeature.ts +232 -0
  1055. package/src/features/network/index.ts +59 -0
  1056. package/src/features/network/thirdParty.ts +160 -0
  1057. package/src/features/ota/OtaEventEmitter.ts +187 -0
  1058. package/src/features/ota/OtaOrchestrator.ts +1513 -0
  1059. package/src/features/ota/OtaTypes.ts +230 -0
  1060. package/src/features/ota/deviceAttributes.ts +46 -0
  1061. package/src/features/ota/environment.ts +199 -0
  1062. package/src/features/ota/geoCountry.ts +85 -0
  1063. package/src/features/ota/retry.ts +123 -0
  1064. package/src/features/ota/signedDownload.ts +202 -0
  1065. package/src/features/ota/useOtaUpdate.ts +161 -0
  1066. package/src/features/performance/PerformanceFeature.ts +729 -0
  1067. package/src/features/performance/collectors/AppLaunchCollector.ts +131 -0
  1068. package/src/features/performance/collectors/AutoScreenLoadCollector.ts +258 -0
  1069. package/src/features/performance/collectors/CustomTraceCollector.ts +116 -0
  1070. package/src/features/performance/collectors/FrameMetricsCollector.ts +210 -0
  1071. package/src/features/performance/collectors/JsStallCollector.ts +184 -0
  1072. package/src/features/performance/collectors/NetworkPerfCollector.ts +69 -0
  1073. package/src/features/performance/collectors/ScreenLoadCollector.ts +95 -0
  1074. package/src/features/performance/collectors/UiHangCollector.ts +98 -0
  1075. package/src/features/performance/config.ts +136 -0
  1076. package/src/features/performance/getNativePerformanceModule.ts +38 -0
  1077. package/src/features/performance/index.ts +28 -0
  1078. package/src/features/performance/metrics/MetricsEngine.ts +152 -0
  1079. package/src/features/performance/models.ts +235 -0
  1080. package/src/features/performance/transport/PerformanceTransport.ts +161 -0
  1081. package/src/features/profiler/ProfilerFeature.ts +661 -0
  1082. package/src/features/profiler/config.ts +230 -0
  1083. package/src/features/profiler/contracts.ts +176 -0
  1084. package/src/features/profiler/index.ts +45 -0
  1085. package/src/features/profiler/models.ts +430 -0
  1086. package/src/features/profiler/transport/ProfilerTransport.ts +93 -0
  1087. package/src/features/replay/bridge/adapters/bridgeAdapter.ts +235 -0
  1088. package/src/features/replay/bridge/eventEmitter.ts +176 -0
  1089. package/src/features/replay/bridge/nativeModule.ts +252 -0
  1090. package/src/features/replay/bridge/nativeQuota.ts +111 -0
  1091. package/src/features/replay/core/clock/clock.ts +38 -0
  1092. package/src/features/replay/core/config/defaults.ts +66 -0
  1093. package/src/features/replay/core/config/validator.ts +113 -0
  1094. package/src/features/replay/core/env/environmentDetector.ts +21 -0
  1095. package/src/features/replay/core/errors/safeCall.ts +34 -0
  1096. package/src/features/replay/core/ids/sessionId.ts +44 -0
  1097. package/src/features/replay/core/logger/logger.ts +36 -0
  1098. package/src/features/replay/core/privacy/privacyTypes.ts +41 -0
  1099. package/src/features/replay/core/queue/boundedQueue.ts +71 -0
  1100. package/src/features/replay/integrations/alert/alertInstrumentation.ts +277 -0
  1101. package/src/features/replay/integrations/logs/consoleIntegration.ts +136 -0
  1102. package/src/features/replay/integrations/network/networkAdapter.ts +73 -0
  1103. package/src/features/replay/integrations/react-navigation/navigationIntegration.ts +89 -0
  1104. package/src/features/replay/integrations/touch/ReplayRoot.tsx +114 -0
  1105. package/src/features/replay/pipeline/batching/batchManager.ts +133 -0
  1106. package/src/features/replay/pipeline/envelope/envelopeBuilder.ts +66 -0
  1107. package/src/features/replay/pipeline/retry/retryPolicy.ts +62 -0
  1108. package/src/features/replay/pipeline/transport/transportTypes.ts +85 -0
  1109. package/src/features/replay/public/api.ts +364 -0
  1110. package/src/features/replay/public/enums.ts +78 -0
  1111. package/src/features/replay/public/types.ts +297 -0
  1112. package/src/features/replay/replay/breadcrumbs/breadcrumbCollector.ts +115 -0
  1113. package/src/features/replay/replay/frames/frameModels.ts +45 -0
  1114. package/src/features/replay/replay/session/sessionOrchestrator.ts +286 -0
  1115. package/src/features/replay/replay/timeline/timelineModels.ts +129 -0
  1116. package/src/features/replay/transport/BackendReplayTransport.ts +215 -0
  1117. package/src/features/replay/transport/CompositeReplayTransport.ts +82 -0
  1118. package/src/features/replay/transport/DesktopReplayTransport.ts +140 -0
  1119. package/src/features/replay/transport/ReplayTransport.ts +84 -0
  1120. package/src/features/session/BackendSessionAdapter.ts +1977 -0
  1121. package/src/features/session/DesktopSessionTransport.ts +130 -0
  1122. package/src/features/session/JourneyEventPipeline.ts +278 -0
  1123. package/src/features/session/ReplayCaptureManager.ts +1401 -0
  1124. package/src/features/session/ScrollTracker.ts +166 -0
  1125. package/src/features/session/SessionManager.ts +1687 -0
  1126. package/src/features/session/SyncDecisionEngine.ts +117 -0
  1127. package/src/features/session/frameLink.ts +41 -0
  1128. package/src/features/session/frameScrollState.ts +36 -0
  1129. package/src/features/session/index.ts +88 -0
  1130. package/src/features/session/nativeCapture.ts +43 -0
  1131. package/src/features/session/outboxFrameCapture.ts +79 -0
  1132. package/src/features/session/sessionTypes.ts +528 -0
  1133. package/src/features/session/viewportPlausibility.ts +134 -0
  1134. package/src/features/skan/skanBridge.ts +53 -0
  1135. package/src/features/skan/skanConversionManager.ts +173 -0
  1136. package/src/index.ts +206 -0
  1137. package/src/integrations/fetch/fetchInterceptor.ts +201 -0
  1138. package/src/integrations/navigation/screenTracker.ts +0 -0
  1139. package/src/integrations/network/bodyCapture.ts +328 -0
  1140. package/src/integrations/xhr/xhrInterceptor.ts +259 -0
  1141. package/src/metro/composeSourceMap.ts +104 -0
  1142. package/src/metro/featureModules.ts +143 -0
  1143. package/src/metro/index.ts +336 -0
  1144. package/src/metro/optionalDependencyStub.ts +13 -0
  1145. package/src/metro/optionalModules.ts +27 -0
  1146. package/src/metro/serializerCompose.ts +193 -0
  1147. package/src/pipeline/dispatcher/dispatcher.ts +22 -0
  1148. package/src/pipeline/envelope/envelope.ts +31 -0
  1149. package/src/pipeline/envelope/serializer.ts +14 -0
  1150. package/src/pipeline/processors/dedupe.ts +0 -0
  1151. package/src/pipeline/processors/enrich.ts +11 -0
  1152. package/src/pipeline/processors/sampling.ts +0 -0
  1153. package/src/pipeline/processors/sanitize.ts +23 -0
  1154. package/src/pipeline/queue/flushQueue.ts +144 -0
  1155. package/src/pipeline/queue/memoryQueue.ts +31 -0
  1156. package/src/pipeline/queue/persistentQueue.ts +385 -0
  1157. package/src/pipeline/queue/queuePolicy.ts +0 -0
  1158. package/src/pipeline/scheduler/flushScheduler.ts +47 -0
  1159. package/src/pipeline/scheduler/retryPolicy.ts +8 -0
  1160. package/src/public/ScaleBunErrorBoundary.tsx +91 -0
  1161. package/src/public/ScaleBunFacade.ts +2129 -0
  1162. package/src/public/ScaleBunProvider.tsx +91 -0
  1163. package/src/public/typedTracker.ts +123 -0
  1164. package/src/public/types.ts +362 -0
  1165. package/src/push/PushManager.ts +318 -0
  1166. package/src/push/adapters/ManualAdapter.ts +100 -0
  1167. package/src/push/adapters/NativeBridgeAdapter.ts +142 -0
  1168. package/src/push/adapters/NoopAdapter.ts +46 -0
  1169. package/src/push/adapters/RNFirebaseMessagingAdapter.ts +271 -0
  1170. package/src/push/adapters/loadMessaging.ts +24 -0
  1171. package/src/push/adapters/loadNotifee.ts +29 -0
  1172. package/src/push/adapters/selectAdapter.ts +176 -0
  1173. package/src/push/detect/capabilities.ts +124 -0
  1174. package/src/push/index.ts +22 -0
  1175. package/src/push/native/nativeNotifications.ts +207 -0
  1176. package/src/push/types.ts +159 -0
  1177. package/src/specs/NativeReplaySdk.ts +101 -0
  1178. package/src/specs/NativeScaleBunCrash.ts +64 -0
  1179. package/src/specs/NativeScaleBunInstallReferrer.ts +29 -0
  1180. package/src/specs/NativeScaleBunOta.ts +154 -0
  1181. package/src/specs/NativeScaleBunPerformance.ts +34 -0
  1182. package/src/specs/NativeScaleBunProfiler.ts +41 -0
  1183. package/src/specs/NativeScaleBunStorage.ts +59 -0
  1184. package/src/storage/StorageBackend.ts +272 -0
  1185. package/src/storage/db/sqlite.ts +0 -0
  1186. package/src/storage/files/fileStore.ts +0 -0
  1187. package/src/transport/auth/sdkSession.ts +27 -0
  1188. package/src/transport/backoff/exponentialJitter.ts +9 -0
  1189. package/src/transport/http/endpoints.ts +33 -0
  1190. package/src/transport/http/httpClient.ts +73 -0
  1191. package/src/transport/rateLimit/retryAfter.ts +0 -0
  1192. package/src/transport/signing/requestSigner.ts +0 -0
  1193. package/src/vendor/protocol/index.ts +391 -0
  1194. package/src/vendor/protocol/internal/validation.ts +497 -0
  1195. package/src/vendor/sdk-contracts/bridge.ts +162 -0
  1196. package/src/vendor/sdk-contracts/config.ts +192 -0
  1197. package/src/vendor/sdk-contracts/events.ts +50 -0
  1198. package/src/vendor/sdk-contracts/features.ts +42 -0
  1199. package/src/vendor/sdk-contracts/index.ts +98 -0
  1200. package/src/vendor/sdk-contracts/platform.ts +42 -0
  1201. package/src/vendor/sdk-contracts/session-boundary.ts +55 -0
  1202. package/src/vendor/sdk-contracts/session.ts +225 -0
  1203. package/src/vendor/vendor.manifest +10 -0
@@ -0,0 +1,1687 @@
1
+ /**
2
+ * ScaleBun SDK — Session Manager
3
+ *
4
+ * Central orchestrator for the unified session system.
5
+ * Manages session lifecycle, wires pipeline + capture + transport + sync.
6
+ *
7
+ * Architecture:
8
+ * - Singleton per debug session
9
+ * - Creates and owns JourneyEventPipeline, ReplayCaptureManager,
10
+ * DesktopSessionTransport, SyncDecisionEngine
11
+ * - Receives events from ScaleBunDebugRoot (nav, interactions), DebugEventStream
12
+ * (network, logs), and breadcrumbs
13
+ * - Coordinates crash anchoring across events AND frames
14
+ * - Auto-timeout via maxDurationMs
15
+ */
16
+
17
+ import { frameLinkVerdict } from './frameLink';
18
+ import { logger } from '../../core/logger/internalLogger';
19
+ import type { DebugTransport } from '../../debug/transport';
20
+ import type {
21
+ Session,
22
+ SessionMetadata,
23
+ SessionReplayConfig,
24
+ JourneyEventType,
25
+ JourneyEventSeverity,
26
+ JourneyEventSource,
27
+ CrashAnchor,
28
+ NativeSyncMetadata,
29
+ SyncDecisionCallback,
30
+ } from './sessionTypes';
31
+ import { DEFAULT_SESSION_REPLAY_CONFIG } from './sessionTypes';
32
+ import { JourneyEventPipeline } from './JourneyEventPipeline';
33
+ import { ReplayCaptureManager, type NativeCaptureFn, type OutboxFrameCapture } from './ReplayCaptureManager';
34
+ import { makeOutboxFrameCapture } from './outboxFrameCapture';
35
+ import { DesktopSessionTransport } from './DesktopSessionTransport';
36
+ import { SyncDecisionEngine } from './SyncDecisionEngine';
37
+ import { ScrollTracker, type ScrollMetadata } from './ScrollTracker';
38
+ import type { BackendSessionAdapter } from './BackendSessionAdapter';
39
+ import type { JourneyEvent } from './sessionTypes';
40
+ import { generateEventId } from '../replay/core/ids/sessionId';
41
+ import { bridgeAdapter } from '../replay/bridge/adapters/bridgeAdapter';
42
+ import { redactBody } from '../../debug/redaction';
43
+ import { consumeCalibrationTarget } from '../journey/calibrationContext';
44
+ import { resolvePlatformOS } from '../../core/context/device';
45
+ import { emitAutomaticEvent } from '../../analytics/automaticEvents';
46
+ import { checkViewport } from './viewportPlausibility';
47
+ import { quotaState } from '../../config/quotaState';
48
+ import {
49
+ INTERACTION_PROTOCOL_VERSION,
50
+ automaticInteractionProperties,
51
+ generateInteractionId,
52
+ type InteractionStateStatus,
53
+ } from '../journey/interactionProtocol';
54
+
55
+ // ─── Types ──────────────────────────────────────────────────────────────────
56
+
57
+ export interface SessionManagerConfig {
58
+ sessionReplay?: Partial<SessionReplayConfig>;
59
+ syncPolicy?: SyncDecisionCallback;
60
+ /**
61
+ * Feature #4 (heatmaps): when true, tap/gesture coordinates are ALSO routed
62
+ * to the always-on analytics lane so coords are captured even when no replay
63
+ * recording is active. Additive — does NOT change replay-recording capture.
64
+ * Unset → false at this layer (safety floor for direct construction); the
65
+ * product default (true) is applied by ConfigSchema and passed in by the
66
+ * bootstrapper. See ConfigSchema.captureInteractionHeatmap.
67
+ */
68
+ captureInteractionHeatmap?: boolean;
69
+ }
70
+
71
+ // ─── Singleton ──────────────────────────────────────────────────────────────
72
+
73
+ let _instance: SessionManager | null = null;
74
+
75
+ // ─── Session Manager ────────────────────────────────────────────────────────
76
+
77
+ export class SessionManager {
78
+ private desktopTransport: DesktopSessionTransport | null = null;
79
+ private pipeline: JourneyEventPipeline;
80
+ private captureManager: ReplayCaptureManager;
81
+ private syncEngine: SyncDecisionEngine;
82
+ private scrollTracker: ScrollTracker | null = null;
83
+ private _backendTransport: BackendSessionAdapter | null = null;
84
+ private config: SessionReplayConfig;
85
+ /**
86
+ * Feature #4 (heatmaps): route tap/gesture coords to the always-on analytics
87
+ * lane even when no replay recording is active. Additive, opt-in (default off).
88
+ */
89
+ private _captureInteractionHeatmap = false;
90
+ private session: Session | null = null;
91
+ private active = false;
92
+ private timeoutTimer: ReturnType<typeof setTimeout> | null = null;
93
+ private crashAnchors: CrashAnchor[] = [];
94
+ /** Tracks loading-start timestamps per screen for duration computation */
95
+ private loadingStartTimes = new Map<string, number>();
96
+ /**
97
+ * Always-tracked current screen name.
98
+ * Updated by onScreenChange even when session is inactive, so that when
99
+ * startSession is called we know what screen the user is currently on and
100
+ * can emit the initial SCREEN_VIEW + navigation capture immediately.
101
+ * This is read-only metadata — does NOT produce events or frames while inactive.
102
+ */
103
+ private _lastKnownScreen: string | null = null;
104
+ /** When the current screen was entered — the basis for SCREEN_EXIT's measured dwellMs. */
105
+ private _screenEnteredAt: number | null = null;
106
+
107
+ /**
108
+ * Last screenshot-base dimensions reported by the native interaction tracker.
109
+ * Stored in JS-coordinate units (dp on Android, pt on iOS) — both are the same
110
+ * unit RN uses for pageX/pageY, so JS-fallback taps can divide directly without
111
+ * additional conversion. This is the EXACT base the native screenshot is captured
112
+ * in, and avoids using Dimensions.get('window') (which excludes system insets
113
+ * differently across devices and pushes markers ~10% off vertically).
114
+ */
115
+ private _lastNativeViewportJsUnits: { width: number; height: number; space: string } | null = null;
116
+
117
+ /**
118
+ * Exact-duplicate gesture suppression (QA: one physical tap recorded 2–3×).
119
+ *
120
+ * Measured in production data: the native tracker re-emitted the SAME tap — identical
121
+ * gestureType and identical coordinates to the SUB-PIXEL (e.g. x=466.962890625) — at
122
+ * 160–240ms gaps, sometimes twice. A human re-tap cannot reproduce a sub-pixel float
123
+ * exactly, so exact coordinate equality within the window is a safe discriminator: real
124
+ * double-taps land on at least slightly different pixels and pass through untouched.
125
+ * The pipeline's own 100ms dedup was too narrow for these; this window covers what was
126
+ * actually observed with margin. Sits at the ONE entry point every gesture source funnels
127
+ * through, so recording and analytics lanes are protected equally.
128
+ */
129
+ private _lastGestureSig: string | null = null;
130
+ private _lastGestureTs = 0;
131
+ private static readonly GESTURE_DEDUP_WINDOW_MS = 400;
132
+
133
+ private _nativeCapture: NativeCaptureFn;
134
+ private _rawTransport: DebugTransport | null;
135
+ /** Memoized native file-outbox frame adapter (gated by the backend transport). */
136
+ private _outboxFrameCapture: OutboxFrameCapture | null = null;
137
+ /** Unsubscribe from quotaState (released in resetInstance). */
138
+ private _quotaUnsub: (() => void) | null = null;
139
+ /** True while frame capture is paused by a `frames` quota block inside an active session. */
140
+ private _framesQuotaPaused = false;
141
+ /**
142
+ * Whether capture has run at all in the CURRENT session. Decides start() vs resume() when a
143
+ * frames block lifts: resume() continues the session's sequence, which is only right if this
144
+ * session already produced frames; a session that began blocked gets a clean start().
145
+ */
146
+ private _captureStartedThisSession = false;
147
+
148
+ constructor(
149
+ debugTransport: DebugTransport | null,
150
+ nativeCapture: NativeCaptureFn,
151
+ managerConfig?: SessionManagerConfig,
152
+ ) {
153
+ this._rawTransport = debugTransport;
154
+ this._nativeCapture = nativeCapture;
155
+
156
+ // Merge config with defaults
157
+ this.config = {
158
+ ...DEFAULT_SESSION_REPLAY_CONFIG,
159
+ ...managerConfig?.sessionReplay,
160
+ privacyPolicy: {
161
+ ...DEFAULT_SESSION_REPLAY_CONFIG.privacyPolicy,
162
+ ...managerConfig?.sessionReplay?.privacyPolicy,
163
+ },
164
+ };
165
+
166
+ // Create subsystems
167
+ this.desktopTransport = debugTransport ? new DesktopSessionTransport(debugTransport) : null;
168
+
169
+ this.pipeline = new JourneyEventPipeline('', {
170
+ maxEventCount: this.config.maxEventCount,
171
+ });
172
+
173
+ this.captureManager = new ReplayCaptureManager(
174
+ '',
175
+ this.config,
176
+ nativeCapture,
177
+ this._getOutboxFrameCapture(),
178
+ );
179
+
180
+ this._captureInteractionHeatmap = managerConfig?.captureInteractionHeatmap === true;
181
+
182
+ this.syncEngine = new SyncDecisionEngine(managerConfig?.syncPolicy);
183
+
184
+ // Wire pipeline → transport (desktop + optional backend)
185
+ this.pipeline.setEmitCallback((event) => {
186
+ this.desktopTransport?.sendEvent(event);
187
+ this._backendTransport?.sendEvent(event);
188
+ });
189
+
190
+ // Wire capture → transport (desktop + optional backend)
191
+ this.captureManager.setFrameEmitCallback((frame, imageData) => {
192
+ this.desktopTransport?.sendFrame(frame, imageData);
193
+ this._backendTransport?.sendFrame(frame, imageData);
194
+ });
195
+
196
+ this._wireLoadingIntervals();
197
+
198
+ // Per-feature quota (contract v1): a `frames` block stops capture NOW (not just upload), and
199
+ // its end resumes capture in the SAME session starting from a fresh keyframe.
200
+ this._quotaUnsub = quotaState.subscribe((feature, blocked) => {
201
+ if (feature === 'frames') this._onFramesQuota(blocked);
202
+ });
203
+ }
204
+
205
+ /**
206
+ * Stop or resume frame capture for a `frames` quota block.
207
+ *
208
+ * Stopping goes through `captureManager.stop()` — every pending capture timer is cancelled and
209
+ * the generation bump discards captures already in flight. The adapter purges what was queued.
210
+ * The session itself keeps running: events, network and errors are governed by their own quotas.
211
+ */
212
+ private _onFramesQuota(blocked: boolean): void {
213
+ try {
214
+ if (blocked) {
215
+ if (this.active && !this._framesQuotaPaused) {
216
+ this.captureManager.stop('quota_pause');
217
+ this._framesQuotaPaused = true;
218
+ logger.warn('[SessionManager] Replay frames quota exceeded — frame capture stopped');
219
+ }
220
+ return;
221
+ }
222
+ if (!this._framesQuotaPaused) return;
223
+ this._framesQuotaPaused = false;
224
+ if (!this.active) return;
225
+ if (this._captureStartedThisSession) {
226
+ this.captureManager.resume();
227
+ } else {
228
+ this.captureManager.start(); // fresh per-session state: sequence 0, keyframe first
229
+ this._captureStartedThisSession = true;
230
+ }
231
+ // A keyframe right away: the frames before the pause were discarded, so without one the
232
+ // replay would show nothing until the next navigation/interaction capture.
233
+ this.captureManager.captureManual('manual').catch(() => { });
234
+ __DEV__ && logger.info('[SessionManager] Replay frames quota lifted — capture resumed with a keyframe');
235
+ } catch (err) {
236
+ logger.warn('[SessionManager] frames quota transition failed:', err);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * A wait the capture manager sat through becomes a pair of journey events.
242
+ *
243
+ * The frame it eventually takes shows the LOADED screen, which leaves the span between the
244
+ * tap and that frame unaccounted for — and the player resolves an unaccounted playhead to the
245
+ * previous frame, so it would show the screen the visitor had already left. These two events
246
+ * are what lets the timeline say "this screen was loading, for this long" instead, at roughly
247
+ * 1 KB against the ~56 KB a second screenshot would cost.
248
+ *
249
+ * `reason` rides on the payload rather than becoming a third event type: `timeout` and
250
+ * `network_idle` are the same interval with different endings, and a consumer that does not
251
+ * know about the distinction should still see the band.
252
+ */
253
+ private _wireLoadingIntervals(): void {
254
+ this.captureManager.setLoadingIntervalCallback((phase, screen, detail) => {
255
+ if (phase === 'start') {
256
+ this.emitEvent('LOADING_STARTED', {
257
+ screen,
258
+ payload: { screen, source: 'replay-hold' },
259
+ source: 'sdk',
260
+ });
261
+ return;
262
+ }
263
+ this.emitEvent('LOADING_COMPLETED', {
264
+ screen,
265
+ payload: {
266
+ screen,
267
+ durationMs: detail.durationMs,
268
+ reason: detail.reason,
269
+ source: 'replay-hold',
270
+ },
271
+ source: 'sdk',
272
+ });
273
+ });
274
+ }
275
+
276
+ // ─── Singleton Access ───────────────────────────────────────────────
277
+
278
+ static getInstance(
279
+ debugTransport: DebugTransport | null,
280
+ nativeCapture: NativeCaptureFn,
281
+ config?: SessionManagerConfig,
282
+ ): SessionManager {
283
+ if (!_instance) {
284
+ _instance = new SessionManager(debugTransport, nativeCapture, config);
285
+ }
286
+ return _instance;
287
+ }
288
+
289
+ static getExistingInstance(): SessionManager | null {
290
+ return _instance;
291
+ }
292
+
293
+ static resetInstance(): void {
294
+ if (_instance) {
295
+ _instance.endSession();
296
+ _instance._quotaUnsub?.();
297
+ _instance._quotaUnsub = null;
298
+ }
299
+ _instance = null;
300
+ }
301
+
302
+ /**
303
+ * Attach a backend transport for uploading replay data to the backend API.
304
+ * This is additive — the existing desktop transport remains active if present.
305
+ */
306
+ setBackendTransport(transport: BackendSessionAdapter): void {
307
+ this._backendTransport = transport;
308
+ // A session whose START the backend refused for quota has no row and never will: end the
309
+ // recording instead of capturing into a transport that drops everything for it.
310
+ transport.setSessionRefusedHandler?.((sessionId) => {
311
+ if (this.active && this.currentSessionId === sessionId) this.endSession();
312
+ });
313
+ __DEV__ && logger.info('[SessionManager] Backend transport attached (replay → backend upload enabled)');
314
+ }
315
+
316
+ /** Expose the backend transport so features can queue network/errors/performance. */
317
+ getBackendTransport(): BackendSessionAdapter | null {
318
+ return this._backendTransport;
319
+ }
320
+
321
+ /**
322
+ * Memoized native file-outbox frame adapter. Always injected into the capture
323
+ * manager; its readiness gate (backed by the backend transport) resolves false
324
+ * when the outbox flags are off / the binary is old, so capture transparently
325
+ * falls back to the legacy `captureScreenshot` path. The closure reads the
326
+ * backend transport lazily at capture time (it attaches after construction).
327
+ */
328
+ private _getOutboxFrameCapture(): OutboxFrameCapture {
329
+ if (!this._outboxFrameCapture) {
330
+ this._outboxFrameCapture = makeOutboxFrameCapture(
331
+ () => this._backendTransport?.ensureNativeOutboxReady() ?? Promise.resolve(false),
332
+ );
333
+ }
334
+ return this._outboxFrameCapture;
335
+ }
336
+
337
+ /**
338
+ * Attach a desktop transport dynamically.
339
+ * Called when debug mode connects after SDK initialization.
340
+ */
341
+ setDesktopTransport(debugTransport: DebugTransport): void {
342
+ this.desktopTransport = new DesktopSessionTransport(debugTransport);
343
+ __DEV__ && logger.info('[SessionManager] Desktop transport attached');
344
+ }
345
+
346
+ // ─── Session Lifecycle ──────────────────────────────────────────────
347
+
348
+ get isActive(): boolean {
349
+ return this.active;
350
+ }
351
+
352
+ get currentSessionId(): string | null {
353
+ return this.session?.sessionId ?? null;
354
+ }
355
+
356
+ get currentSessionMetadata(): SessionMetadata | null {
357
+ return this.session?.metadata ?? null;
358
+ }
359
+
360
+ /**
361
+ * The active RECORDING session ID (`ses-xxx`), or null when no recording
362
+ * is in progress.
363
+ *
364
+ * This is the canonical field that DebugEventStream stamps into every
365
+ * outgoing event's `data.recordingSessionId`. It is the single source of
366
+ * truth for correlating network and performance events with the session
367
+ * that was active when they were captured.
368
+ *
369
+ * Distinct from the TRANSPORT session ID (`debug-xxx`) which lives on
370
+ * the wire envelope's top-level `sessionId` field and never changes.
371
+ */
372
+ get recordingSessionId(): string | null {
373
+ return this.active ? (this.session?.sessionId ?? null) : null;
374
+ }
375
+
376
+ /**
377
+ * Update the native capture function at runtime.
378
+ * Called by ScaleBunDebugRoot once the root View ref is ready for ViewShot.
379
+ * Also updates the active session's capture manager if one exists.
380
+ */
381
+ setNativeCapture(captureFn: NativeCaptureFn): void {
382
+ this._nativeCapture = captureFn;
383
+ // Hot-swap into active capture manager if session is running
384
+ if (this.active && this.captureManager) {
385
+ (this.captureManager as any).nativeCapture = captureFn;
386
+ __DEV__ && logger.info('[SessionManager] Capture function updated (hot-swap)');
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Start a new session. Idempotent — no-op if already active.
392
+ */
393
+ startSession(metadata?: Partial<SessionMetadata>): void {
394
+ try {
395
+ if (this.active) {
396
+ __DEV__ && logger.debug('[SessionManager] Session already active, skipping start');
397
+ return;
398
+ }
399
+
400
+ // Quota (contract v1): a `sessions` block stops NEW sessions. Adopting an analytics
401
+ // session that is already open is not a new session, so that path stays allowed; minting
402
+ // a fresh recording id to post a new INGESTION_START is not.
403
+ if (this._backendTransport && !this._backendTransport.analyticsSessionId && quotaState.isBlocked('sessions')) {
404
+ logger.warn('[SessionManager] Session not started — sessions quota exceeded');
405
+ return;
406
+ }
407
+
408
+ // One-row merge: adopt the always-on analytics ingestion id as this
409
+ // recording session's id when the backend (SaaS) lane has one open.
410
+ // This threads the analytics id through the pipeline + capture manager
411
+ // below, so every frame/event body carries it AND it becomes
412
+ // currentSessionId at sendSessionStart — keeping endpoint id == body id
413
+ // while attaching frames/journey to the single analytics row (no second
414
+ // INGESTION_START). Legacy/no-clientKey mode has no analytics id → mint
415
+ // a fresh recording id and post to /replay/... as before.
416
+ const sessionId = this._backendTransport?.analyticsSessionId ?? generateSessionId();
417
+
418
+ // Create fresh subsystems for this session
419
+ this.pipeline = new JourneyEventPipeline(sessionId, {
420
+ maxEventCount: this.config.maxEventCount,
421
+ });
422
+ this.pipeline.setEmitCallback((event) => {
423
+ this.desktopTransport?.sendEvent(event);
424
+ this._backendTransport?.sendEvent(event);
425
+ });
426
+
427
+ this.captureManager = new ReplayCaptureManager(
428
+ sessionId,
429
+ this.config,
430
+ this._nativeCapture,
431
+ this._getOutboxFrameCapture(),
432
+ );
433
+ this.captureManager.setFrameEmitCallback((frame, imageData) => {
434
+ this.desktopTransport?.sendFrame(frame, imageData);
435
+ this._backendTransport?.sendFrame(frame, imageData);
436
+ });
437
+
438
+ // A NEW capture manager starts with no callbacks. Frame emission is re-wired above;
439
+ // loading intervals have to be re-wired here for the same reason, or every session
440
+ // after the first records frames and silently stops recording the waits between them.
441
+ this._wireLoadingIntervals();
442
+
443
+ // Create scroll tracker — wired to emit events + trigger capture on settle
444
+ this.scrollTracker = new ScrollTracker((scrollMeta: ScrollMetadata) => {
445
+ if (!this.active) return;
446
+
447
+ // Synthesize representative coordinates for the scroll.
448
+ // ScrollTracker only knows content deltas — it has no absolute touch
449
+ // point — but the dashboard CorrelationEngine DROPS any interaction
450
+ // without x/y, and the replay overlay draws scroll/swipe as an arrow
451
+ // that needs normalizedEnd coords. Without this, ScaleBunScrollView scrolls
452
+ // were invisible AND uncounted. Anchor the gesture at screen center and
453
+ // extend by the finger displacement (opposite the content offset delta),
454
+ // matching the finger-path convention native swipes already use.
455
+ const vw = this.session?.metadata?.screenWidth ?? 0;
456
+ const vh = this.session?.metadata?.screenHeight ?? 0;
457
+ const coords: Record<string, number | boolean> = {};
458
+ if (vw > 0 && vh > 0) {
459
+ const cx = vw / 2;
460
+ const cy = vh / 2;
461
+ const ex = Math.max(0, Math.min(vw, cx - scrollMeta.totalDeltaX));
462
+ const ey = Math.max(0, Math.min(vh, cy - scrollMeta.totalDeltaY));
463
+ coords.x = cx;
464
+ coords.y = cy;
465
+ coords.endX = ex;
466
+ coords.endY = ey;
467
+ coords.normalizedX = cx / vw;
468
+ coords.normalizedY = cy / vh;
469
+ coords.normalizedEndX = ex / vw;
470
+ coords.normalizedEndY = ey / vh;
471
+ coords.viewportWidth = vw;
472
+ coords.viewportHeight = vh;
473
+ coords.normalizedPrecomputed = true;
474
+ }
475
+
476
+ // Emit gesture:scroll event.
477
+ // emitEvent() triggers captureManager.onScrollSettle() internally
478
+ // for USER_ACTION events with gestureType='scroll' — no explicit call needed.
479
+ this.emitEvent('USER_ACTION', {
480
+ subtype: 'gesture:scroll',
481
+ payload: {
482
+ gestureType: 'scroll',
483
+ totalDeltaX: scrollMeta.totalDeltaX,
484
+ totalDeltaY: scrollMeta.totalDeltaY,
485
+ dominantDirection: scrollMeta.dominantDirection,
486
+ direction: scrollMeta.dominantDirection,
487
+ durationMs: scrollMeta.durationMs,
488
+ startTs: scrollMeta.startTs,
489
+ endTs: scrollMeta.endTs,
490
+ confidence: 'high',
491
+ ...coords,
492
+ },
493
+ source: 'user',
494
+ });
495
+ });
496
+
497
+ this.crashAnchors = [];
498
+ this.loadingStartTimes.clear();
499
+
500
+ // Build session object
501
+ this.session = {
502
+ sessionId,
503
+ deviceId: this._rawTransport?.deviceId ?? 'unknown',
504
+ startedAt: Date.now(),
505
+ status: 'active',
506
+ metadata: {
507
+ // The real Platform.OS. This used to be a ternary whose else-branch was 'android'
508
+ // and whose catch was ALSO 'android' — so a macOS session, a Windows session, and
509
+ // a failure to load react-native at all were reported identically, as Android.
510
+ // Enriched by native device info when the bridge answers.
511
+ platform: resolvePlatformOS(),
512
+ buildType: __DEV__ ? 'debug' : 'release',
513
+ ...metadata,
514
+ // Capture window dp dims here so emitEvent() can normalize coords
515
+ // without re-querying Dimensions on every gesture event.
516
+ ...(() => {
517
+ if (metadata?.screenWidth && metadata?.screenHeight) return {};
518
+ try {
519
+ const { Dimensions } = require('react-native');
520
+ const dims = Dimensions.get('window');
521
+ if (dims.width > 0 && dims.height > 0) {
522
+ return { screenWidth: dims.width, screenHeight: dims.height };
523
+ }
524
+ } catch { /* no-throw */ }
525
+ return {};
526
+ })(),
527
+ },
528
+ transportState: 'local',
529
+ syncState: 'pending-evaluation',
530
+ artifactIndex: {
531
+ frameRefs: [],
532
+ eventRefs: [],
533
+ networkRefs: [],
534
+ logRefs: [],
535
+ crashAnchors: [],
536
+ },
537
+ };
538
+
539
+ this.active = true;
540
+
541
+ // Enrich the live session metadata with native device info (appVersion,
542
+ // deviceModel, osVersion, …) so synchronous consumers — e.g. the engage
543
+ // response builder reading `currentSessionMetadata.appVersion` — see real
544
+ // values rather than the platform-only subset known at create time. The
545
+ // session-start metadata above is the fallback when the bridge is
546
+ // unavailable (old native binary). Fire-and-forget, never throws.
547
+ Promise.resolve()
548
+ .then(() => bridgeAdapter.getDeviceInfo())
549
+ .then(info => {
550
+ const s = this.session;
551
+ if (!info || !s) return;
552
+ const m = s.metadata;
553
+ s.metadata = {
554
+ ...m,
555
+ appVersion: info.appVersion ?? m.appVersion,
556
+ appName: info.appName ?? m.appName,
557
+ bundleId: info.bundleId ?? m.bundleId,
558
+ osVersion: info.osVersion ?? m.osVersion,
559
+ deviceModel: info.deviceModel ?? m.deviceModel,
560
+ };
561
+ })
562
+ .catch(() => {
563
+ /* no-throw: metadata-only fallback */
564
+ });
565
+
566
+ // Notify transport (desktop + optional backend)
567
+ this.desktopTransport?.sendSessionStart(this.session);
568
+ this._backendTransport?.sendSessionStart(this.session);
569
+
570
+ // Emit SESSION_STARTED event
571
+ this.pipeline.emit('SESSION_STARTED', {
572
+ payload: { sessionId },
573
+ source: 'system',
574
+ });
575
+
576
+ // Start capture manager — unless the `frames` quota is spent, in which case the session
577
+ // runs without frame capture and resumes it (with a keyframe) when the block lifts.
578
+ if (quotaState.isBlocked('frames')) {
579
+ this._framesQuotaPaused = true;
580
+ this._captureStartedThisSession = false;
581
+ logger.warn('[SessionManager] Replay frames quota exceeded — session started without frame capture');
582
+ } else {
583
+ this._framesQuotaPaused = false;
584
+ this._captureStartedThisSession = true;
585
+ this.captureManager.start();
586
+ }
587
+
588
+ // Set timeout
589
+ if (this.config.maxDurationMs > 0) {
590
+ this.timeoutTimer = setTimeout(() => {
591
+ __DEV__ && logger.debug('[SessionManager] Session timeout reached');
592
+ this.endSession('timeout');
593
+ }, this.config.maxDurationMs);
594
+ }
595
+
596
+ __DEV__ && logger.info(`[SessionManager] Session started: ${sessionId}`);
597
+
598
+ // ── Replay initial screen ──────────────────────────────────────
599
+ // If the user was already on a screen before pressing Record
600
+ // (typical: app mounted → screen hook fired → screen tracked),
601
+ // emit the initial SCREEN_VIEW now so the session captures the
602
+ // starting screen and triggers the first navigation frame.
603
+ if (this._lastKnownScreen) {
604
+ this.emitEvent('SCREEN_VIEW', {
605
+ screen: this._lastKnownScreen,
606
+ subtype: this._lastKnownScreen,
607
+ source: 'sdk',
608
+ });
609
+ }
610
+ } catch (err) {
611
+ logger.error('[SessionManager] Failed to start session:', err);
612
+ }
613
+ }
614
+
615
+ /**
616
+ * End the current session.
617
+ */
618
+ endSession(reason: 'ended' | 'timeout' = 'ended'): void {
619
+ // Fully idempotent — safe to call multiple times
620
+ if (!this.active) {
621
+ return;
622
+ }
623
+
624
+ // Disable collection FIRST to prevent any re-entrant emission during teardown
625
+ this.active = false;
626
+
627
+ // Guard: session must exist (should always be true if active was true)
628
+ if (!this.session) return;
629
+
630
+ try {
631
+
632
+ // Clear timeout
633
+ if (this.timeoutTimer) {
634
+ clearTimeout(this.timeoutTimer);
635
+ this.timeoutTimer = null;
636
+ }
637
+
638
+ // Dispose scroll tracker — cancels pending settle timer
639
+ if (this.scrollTracker) {
640
+ this.scrollTracker.dispose();
641
+ this.scrollTracker = null;
642
+ }
643
+
644
+ // Clear loading state
645
+ this.loadingStartTimes.clear();
646
+
647
+ // Emit SESSION_ENDED event
648
+ this.pipeline.emit('SESSION_ENDED', {
649
+ payload: { reason },
650
+ source: 'system',
651
+ });
652
+
653
+ // Stop capture — this cancels ALL pending timers and increments generation
654
+ this.captureManager.stop();
655
+
656
+ // Build final artifact index
657
+ const eventRefs = this.pipeline.buildEventRefs();
658
+ this.session.artifactIndex = {
659
+ ...eventRefs,
660
+ frameRefs: this.captureManager.getFrameRefs(),
661
+ crashAnchors: this.crashAnchors,
662
+ };
663
+
664
+ // Finalize session
665
+ this.session.endedAt = Date.now();
666
+ this.session.status = reason === 'timeout' ? 'timeout' : 'ended';
667
+
668
+ // Run sync evaluation
669
+ const hasCrash = this.crashAnchors.length > 0;
670
+ const hasError = this.pipeline.getEvents().some(
671
+ e => e.type === 'ERROR_CAPTURED',
672
+ );
673
+
674
+ const candidate = SyncDecisionEngine.buildCandidate(
675
+ this.session.sessionId,
676
+ this.session.metadata,
677
+ this.pipeline.count,
678
+ this.captureManager.count,
679
+ (this.session.endedAt - this.session.startedAt),
680
+ hasCrash,
681
+ hasError,
682
+ );
683
+
684
+ const decision = this.syncEngine.evaluate(candidate);
685
+ this.session.syncState = decision.shouldSync ? 'eligible' : 'ineligible';
686
+
687
+ // Notify transport (desktop + optional backend)
688
+ this.desktopTransport?.sendSessionEnd(this.session);
689
+ this._backendTransport?.sendSessionEnd(this.session);
690
+
691
+ // Clean up
692
+ this.pipeline.clear();
693
+ this.captureManager.clear();
694
+ // (this.active already set to false above)
695
+
696
+ __DEV__ && logger.info(
697
+ `[SessionManager] Session ended: ${this.session.sessionId} ` +
698
+ `(${reason}, sync=${decision.shouldSync}, priority=${decision.priority})`,
699
+ );
700
+ } catch (err) {
701
+ logger.error('[SessionManager] Failed to end session:', err);
702
+ // active is already false — no further cleanup needed
703
+ }
704
+ }
705
+
706
+ // ─── Event Emission ─────────────────────────────────────────────────
707
+
708
+ /**
709
+ * Resolve the dp-space base used to normalize JS-fallback tap coordinates.
710
+ *
711
+ * The screenshot is captured by native in the content-view at its physical
712
+ * pixel size. Normalized coords must divide by that SAME region (converted
713
+ * to dp, since pageX/pageY are dp). Using Dimensions.get('window') (or
714
+ * session.metadata, which is seeded from it) excludes system insets and is
715
+ * ~10% smaller than the content-view — pushing markers off. Preference order:
716
+ * native content-view (px→dp) → session metadata → payload → Dimensions.
717
+ */
718
+ private _resolveNormalizationBaseDp(
719
+ p: Record<string, any>,
720
+ ): { width: number; height: number; space: string } | null {
721
+ // Native screenshot-base size in JS-coordinate units (dp on Android, pt on iOS).
722
+ // This is the EXACT region the native screenshot was captured in, so JS-fallback
723
+ // taps (pageX/pageY in dp/pt) divide directly into the same coord space the
724
+ // marker overlay uses on the dashboard. Highest priority — session metadata
725
+ // and Dimensions.get('window') report the app window which excludes system
726
+ // insets differently across devices and pushes markers off vertically.
727
+ if (this._lastNativeViewportJsUnits) {
728
+ return {
729
+ width: this._lastNativeViewportJsUnits.width,
730
+ height: this._lastNativeViewportJsUnits.height,
731
+ space: this._lastNativeViewportJsUnits.space,
732
+ };
733
+ }
734
+
735
+ const mw = this.session?.metadata?.screenWidth;
736
+ const mh = this.session?.metadata?.screenHeight;
737
+ if (mw && mh) return { width: mw, height: mh, space: 'session-metadata-dp' };
738
+
739
+ if (typeof p.viewportWidth === 'number' && p.viewportWidth > 0
740
+ && typeof p.viewportHeight === 'number' && p.viewportHeight > 0) {
741
+ return { width: p.viewportWidth, height: p.viewportHeight, space: 'payload-viewport-dp' };
742
+ }
743
+
744
+ try {
745
+ const { Dimensions } = require('react-native');
746
+ const dims = Dimensions.get('window');
747
+ if (dims.width > 0 && dims.height > 0) {
748
+ return { width: dims.width, height: dims.height, space: 'root-view-dp' };
749
+ }
750
+ } catch { /* no-throw */ }
751
+
752
+ return null;
753
+ }
754
+
755
+ /**
756
+ * Inject/resolve normalized tap coordinates into a USER_ACTION payload, in place.
757
+ *
758
+ * Mutates `p` to carry normalizedX/Y (+ end coords for swipes/scrolls) so the
759
+ * heatmap can overlay them across viewport sizes. Returns true when the source
760
+ * already provided high-confidence / pre-computed normalized coords (native or
761
+ * JS-root-measured), false when this method JS-estimated them.
762
+ *
763
+ * Extracted from emitEvent so BOTH the replay-recording lane AND the always-on
764
+ * analytics lane (Feature #4) normalize identically — keeping the tap payload
765
+ * keys byte-for-byte the same on both routes.
766
+ */
767
+ private _normalizeInteractionPayload(p: Record<string, unknown>): boolean {
768
+ // If native tracker already provided high-confidence normalized coords,
769
+ // OR the JS path measured the root-view rect and precomputed normalized
770
+ // coords against it, do not overwrite them with JS estimation.
771
+ const hasNormalizedXY = typeof p.normalizedX === 'number'
772
+ && typeof p.normalizedY === 'number';
773
+ const alreadyNormalized = hasNormalizedXY
774
+ && (p.confidence === 'high' || p.normalizedPrecomputed === true);
775
+
776
+ if (alreadyNormalized) {
777
+ // Cache the native screenshot base in JS-coordinate units (dp/pt)
778
+ // so subsequent JS-fallback taps normalize against the same region
779
+ // the screenshot was captured from. Android sends px (suffix -px),
780
+ // iOS sends pt (suffix -pt). Both are converted to the unit RN
781
+ // pageX/pageY use, so JS taps divide directly with no extra math.
782
+ const cs = typeof p.coordinateSpace === 'string' ? p.coordinateSpace : '';
783
+
784
+ /**
785
+ * TRUST, BUT CHECK. `confidence: 'high'` means the NATIVE layer measured it — not that
786
+ * the number is right. One production device reports a content view of 288x448 px while
787
+ * telling us its layout width is 434.7dp, an implied density of 0.663 that no screen
788
+ * has. Cached unchecked, that wrong base then normalised every tap in the session and
789
+ * became the screenshot's coordinate space too.
790
+ *
791
+ * `windowWidth` and `density` are attached here so the check has both sides of the
792
+ * invariant and so the BACKEND can re-derive the same verdict from the stored payload —
793
+ * see `viewportPlausibility.ts` for the rule and the 56-device measurement behind it.
794
+ */
795
+ let density = 0;
796
+ let windowWidth = 0;
797
+ try {
798
+ const { PixelRatio, Dimensions } = require('react-native');
799
+ density = PixelRatio.get();
800
+ windowWidth = Dimensions.get('window').width;
801
+ } catch { /* no-throw */ }
802
+
803
+ const check = checkViewport({
804
+ viewportWidth: typeof p.viewportWidth === 'number' ? p.viewportWidth : null,
805
+ viewportHeight: typeof p.viewportHeight === 'number' ? p.viewportHeight : null,
806
+ windowWidth: windowWidth > 0 ? windowWidth : null,
807
+ coordinateSpace: cs,
808
+ density: density > 0 ? density : null,
809
+ });
810
+ p.viewportCheck = check.verdict;
811
+ if (check.reason) p.viewportCheckReason = check.reason;
812
+ if (check.impliedDensity != null) p.impliedDensity = check.impliedDensity;
813
+
814
+ if (typeof p.viewportWidth === 'number' && p.viewportWidth > 0
815
+ && typeof p.viewportHeight === 'number' && p.viewportHeight > 0
816
+ && check.verdict !== 'implausible') {
817
+ if (cs.endsWith('-px')) {
818
+ if (density > 0) {
819
+ this._lastNativeViewportJsUnits = {
820
+ width: p.viewportWidth / density,
821
+ height: p.viewportHeight / density,
822
+ space: 'native-content-view-dp',
823
+ };
824
+ }
825
+ } else if (cs.endsWith('-pt')) {
826
+ // iOS already in pt — same unit as RN pageX/pageY
827
+ this._lastNativeViewportJsUnits = {
828
+ width: p.viewportWidth,
829
+ height: p.viewportHeight,
830
+ space: 'native-window-pt',
831
+ };
832
+ }
833
+ } else if (check.verdict === 'implausible') {
834
+ /**
835
+ * The native base is unusable, so fall back to the window and RE-NORMALISE.
836
+ *
837
+ * The coordinates already on the payload were divided by the bad base, so leaving
838
+ * them would keep every tap in the wrong place — the point of rejecting the base is
839
+ * to stop using it, including for the numbers already computed from it. The
840
+ * original is kept under `rejectedViewportW/H` so the device stays diagnosable
841
+ * from stored data instead of needing the handset.
842
+ */
843
+ p.rejectedViewportWidth = p.viewportWidth;
844
+ p.rejectedViewportHeight = p.viewportHeight;
845
+ this._lastNativeViewportJsUnits = null;
846
+
847
+ /**
848
+ * The window, taken DIRECTLY — not via `_resolveNormalizationBaseDp`.
849
+ *
850
+ * That helper would walk its priority list and land straight back on the rejected
851
+ * number: its third branch reads `p.viewportWidth` off this very payload, and its
852
+ * second reads `session.metadata.screenWidth`, which on a device that misreports
853
+ * its content view has no reason to be any more trustworthy. Rejecting a source and
854
+ * then re-selecting it through a helper is how a fix becomes a no-op.
855
+ *
856
+ * `Dimensions.get('window')` is the measurement the invariant just CONFIRMED is
857
+ * sound — on the failing device its 434.7dp is exactly right for a 1440px screen —
858
+ * so it is the one source that has earned being the fallback here.
859
+ */
860
+ let winW = 0;
861
+ let winH = 0;
862
+ try {
863
+ const { Dimensions } = require('react-native');
864
+ const d = Dimensions.get('window');
865
+ winW = d.width;
866
+ winH = d.height;
867
+ } catch { /* no-throw */ }
868
+
869
+ const rx = typeof p.x === 'number' ? p.x : undefined;
870
+ const ry = typeof p.y === 'number' ? p.y : undefined;
871
+ if (winW > 0 && winH > 0) {
872
+ // `x`/`y` arrived in the REJECTED space (px), so they have to be converted into
873
+ // the window's unit before dividing, or the normalisation is wrong a second time.
874
+ const rejW = typeof p.rejectedViewportWidth === 'number' ? p.rejectedViewportWidth : 0;
875
+ const rejH = typeof p.rejectedViewportHeight === 'number' ? p.rejectedViewportHeight : 0;
876
+ if (rx !== undefined && ry !== undefined && rejW > 0 && rejH > 0) {
877
+ // Scale-free: the tap's POSITION within the rejected frame is still the best
878
+ // estimate of where the finger was, even though the frame's absolute size was
879
+ // nonsense. Re-express it against the window instead of re-dividing raw px.
880
+ const nx = Math.max(0, Math.min(1, rx / rejW));
881
+ const ny = Math.max(0, Math.min(1, ry / rejH));
882
+ p.normalizedX = nx;
883
+ p.normalizedY = ny;
884
+ p.x = nx * winW;
885
+ p.y = ny * winH;
886
+ }
887
+ p.viewportWidth = winW;
888
+ p.viewportHeight = winH;
889
+ p.coordinateSpace = 'window-fallback-dp';
890
+ p.confidence = 'derived';
891
+ }
892
+ }
893
+ } else {
894
+ // Resolve the normalization base (in dp). Priority:
895
+ // 1. session metadata screen size
896
+ // 2. native content-view size (px → dp via PixelRatio) — matches screenshot
897
+ // 3. payload viewport
898
+ // 4. Dimensions.get('window') — last resort (excludes insets, ~10% off)
899
+ const base = this._resolveNormalizationBaseDp(p);
900
+
901
+ const x = typeof p.x === 'number' ? p.x : undefined;
902
+ const y = typeof p.y === 'number' ? p.y : undefined;
903
+ if (x !== undefined && y !== undefined && base) {
904
+ p.normalizedX = Math.max(0, Math.min(1, x / base.width));
905
+ p.normalizedY = Math.max(0, Math.min(1, y / base.height));
906
+ p.viewportWidth = base.width;
907
+ p.viewportHeight = base.height;
908
+ p.coordinateSpace = base.space;
909
+ }
910
+
911
+ const endX = typeof p.endX === 'number' ? p.endX : undefined;
912
+ const endY = typeof p.endY === 'number' ? p.endY : undefined;
913
+ if (endX !== undefined && endY !== undefined && base) {
914
+ p.normalizedEndX = Math.max(0, Math.min(1, endX / base.width));
915
+ p.normalizedEndY = Math.max(0, Math.min(1, endY / base.height));
916
+ }
917
+ }
918
+
919
+ return alreadyNormalized;
920
+ }
921
+
922
+ /**
923
+ * Feature #4 (heatmaps) — additive analytics emission.
924
+ *
925
+ * Routes a tap/gesture to the always-on ANALYTICS lane (BackendSessionAdapter
926
+ * .trackEvent) so coords are captured even when no replay recording is active.
927
+ *
928
+ * Strictly additive and isolated:
929
+ * - Only runs when `_captureInteractionHeatmap` is ON (opt-in flag) AND a
930
+ * backend analytics transport is attached AND no recording is active
931
+ * (when active, the recording lane already emits the SAME USER_ACTION —
932
+ * emitting here too would duplicate it).
933
+ * - Reuses the EXACT recording payload shape (USER_ACTION + the same keys:
934
+ * normalizedX, normalizedY, gestureType, screen, …) so the backend
935
+ * aggregate reads them identically (no protocol/schema change).
936
+ * - Normalizes via the same _normalizeInteractionPayload used by recording.
937
+ * - Respects redaction: payload is passed through redactBody so any caller-
938
+ * injected sensitive keys (e.g. a label) are stripped before transport.
939
+ * - Never throws.
940
+ */
941
+ private _emitInteractionToAnalytics(
942
+ gestureType: string,
943
+ payload: Record<string, unknown>,
944
+ occurredAt: number,
945
+ screenName?: string,
946
+ ): boolean {
947
+ if (!this._captureInteractionHeatmap) return false;
948
+ if (this.active) return false; // recording lane already carries this tap
949
+ const adapter = this._backendTransport;
950
+ if (!adapter) return false;
951
+
952
+ try {
953
+ // Normalize coords in place (same logic as the recording lane).
954
+ this._normalizeInteractionPayload(payload);
955
+
956
+ // Respect redaction before handing to transport. redactBody returns a
957
+ // new object and strips known-sensitive keys; coords/gesture survive.
958
+ const safePayload = redactBody(payload) as Record<string, unknown>;
959
+
960
+ /**
961
+ * NO `frameId` HERE, and it is not an oversight.
962
+ *
963
+ * This method is guarded by `if (this.active) return;` — it runs ONLY when no replay
964
+ * recording is active, which is precisely when no screenshots are being captured.
965
+ * There is no frame on screen to point at, so an analytics-lane tap can never be
966
+ * attributed to a visual state.
967
+ *
968
+ * That makes it a permanent property of this lane rather than a gap to close later,
969
+ * and the dashboard has to account for it: these taps live in the heatmap grid
970
+ * alongside recorded ones, so a state filter necessarily covers a subset of the
971
+ * traffic. `heat:screen-coverage` reports that subset's size per app for exactly this
972
+ * reason.
973
+ */
974
+ const event: JourneyEvent = {
975
+ eventId: generateEventId(),
976
+ // sessionId is (re)stamped by the analytics lane at flush time.
977
+ sessionId: adapter.analyticsSessionId ?? '',
978
+ ts: occurredAt,
979
+ type: 'USER_ACTION',
980
+ subtype: `gesture:${gestureType}`,
981
+ screen: screenName ?? this._lastKnownScreen ?? undefined,
982
+ payload: safePayload,
983
+ source: 'user',
984
+ };
985
+ adapter.trackEvent(event);
986
+ return true;
987
+ } catch (err) {
988
+ logger.error('[SessionManager] heatmap analytics emit failed:', err);
989
+ return false;
990
+ }
991
+ }
992
+
993
+ /**
994
+ * Emit a canonical event into the session pipeline.
995
+ * This is the primary entry point for all event sources.
996
+ */
997
+ emitEvent(
998
+ type: JourneyEventType,
999
+ opts?: {
1000
+ subtype?: string;
1001
+ severity?: JourneyEventSeverity;
1002
+ screen?: string;
1003
+ payload?: Record<string, unknown>;
1004
+ correlationId?: string;
1005
+ traceId?: string;
1006
+ source?: JourneyEventSource;
1007
+ journeyId?: string;
1008
+ /** Original observation time; interaction capture may resolve asynchronously. */
1009
+ timestamp?: number;
1010
+ },
1011
+ ): void {
1012
+ if (!this.active) return;
1013
+
1014
+ try {
1015
+ // Pre-process USER_ACTION events to inject normalized coordinates for heatmap readiness
1016
+ if (type === 'USER_ACTION' && opts?.payload) {
1017
+ const p = opts.payload;
1018
+
1019
+ const alreadyNormalized = this._normalizeInteractionPayload(p);
1020
+
1021
+ __DEV__ && logger.debug(
1022
+ `[TapAccuracy] sink platform=${p.platform ?? '?'} space=${p.coordinateSpace ?? '?'} ` +
1023
+ `source=${alreadyNormalized ? (p.confidence === 'high' ? 'native-high' : 'js-precomputed') : 'js-estimated'} ` +
1024
+ `norm=(${p.normalizedX},${p.normalizedY}) base=(${p.viewportWidth}x${p.viewportHeight})`,
1025
+ );
1026
+
1027
+ // ─── Diagnostic: merge calibration expected target (if a calibration
1028
+ // screen stamped it within the TTL) and emit ONE unified [TapDiag]
1029
+ // JSON object per gesture. Observability only — no math runs here.
1030
+ const cal = consumeCalibrationTarget();
1031
+ if (cal) {
1032
+ p.expectedTargetId = cal.targetId;
1033
+ p.expectedNX = cal.expectedNX;
1034
+ p.expectedNY = cal.expectedNY;
1035
+ }
1036
+
1037
+ try {
1038
+ const meta = this.session?.metadata as any | undefined;
1039
+ const diag = {
1040
+ tag: 'TapDiag',
1041
+ side: 'sdk',
1042
+ platform: p.platform ?? null,
1043
+ coordinateSource: alreadyNormalized
1044
+ ? (p.confidence === 'high' ? 'native-high' : 'js-precomputed')
1045
+ : 'js-estimated',
1046
+ coordinateSpace: p.coordinateSpace ?? null,
1047
+ targetId: p.expectedTargetId ?? p.target ?? null,
1048
+ expectedNX: p.expectedNX ?? null,
1049
+ expectedNY: p.expectedNY ?? null,
1050
+ rawX: p.x ?? null,
1051
+ rawY: p.y ?? null,
1052
+ pageX: p.pageX ?? null,
1053
+ pageY: p.pageY ?? null,
1054
+ locationX: p.locationX ?? null,
1055
+ locationY: p.locationY ?? null,
1056
+ normalizedX: p.normalizedX ?? null,
1057
+ normalizedY: p.normalizedY ?? null,
1058
+ viewportWidth: p.viewportWidth ?? null,
1059
+ viewportHeight: p.viewportHeight ?? null,
1060
+ rootViewWidth: p.rootViewWidth ?? null,
1061
+ rootViewHeight: p.rootViewHeight ?? null,
1062
+ rootViewLeft: p.rootViewLeft ?? null,
1063
+ rootViewTop: p.rootViewTop ?? null,
1064
+ screenWidth: p.screenWidth ?? meta?.screenWidth ?? null,
1065
+ screenHeight: p.screenHeight ?? meta?.screenHeight ?? null,
1066
+ windowWidth: p.windowWidth ?? null,
1067
+ windowHeight: p.windowHeight ?? null,
1068
+ statusBarHeight: p.statusBarHeight ?? null,
1069
+ safeAreaTop: p.safeAreaTop ?? null,
1070
+ timestamp: Date.now(),
1071
+ confidence: p.confidence ?? null,
1072
+ source: p.source ?? null,
1073
+ };
1074
+ // Dev-only diagnostic echo. Must NOT emit in release: the console
1075
+ // capture integration (captureConsoleLogs) would otherwise forward
1076
+ // one [TapDiag] line per tap into the customer-facing Logs lane and
1077
+ // bury their own logs. Sibling [TapAccuracy] log above is __DEV__-gated
1078
+ // for the same reason; the heatmap/replay data flows through the
1079
+ // interaction pipeline, not this echo, so gating it loses nothing.
1080
+ // eslint-disable-next-line no-console
1081
+ if (__DEV__) console.log('[TapDiag]', JSON.stringify(diag));
1082
+ } catch { /* no-throw */ }
1083
+ }
1084
+
1085
+ /**
1086
+ * Which frame was on screen when this happened.
1087
+ *
1088
+ * Resolved BEFORE `pipeline.emit`, and deliberately before the capture triggers
1089
+ * below: those capture the frame that shows the RESULT of the interaction, and a tap
1090
+ * belongs to the state it was made IN, not the one it produced. Reading after them
1091
+ * would attribute every tap to its own consequence.
1092
+ *
1093
+ * USER_ACTION only. A SCREEN_VIEW's frame is the screen being left, which is
1094
+ * misleading rather than useful, and no consumer asks for it.
1095
+ *
1096
+ * `currentKeyframe` returns null unless the frame is from this same screen and still
1097
+ * fresh — see its contract. Null is the common, correct answer and stays absent on
1098
+ * the event rather than becoming a default.
1099
+ */
1100
+ /**
1101
+ * The keyframe is LINKED only when it can show this tap: a tap made after the list was
1102
+ * scrolled is not a tap on the picture taken before it (frameLink.ts). The verdict and the
1103
+ * frame's age ride on the payload, so a missing or unverified link is visible, not silent.
1104
+ */
1105
+ let framedOpts: (typeof opts & { frameId?: string }) | undefined = opts;
1106
+ if (type === 'USER_ACTION') {
1107
+ const kf = this.captureManager?.currentKeyframe(opts?.screen ?? this._lastKnownScreen ?? undefined);
1108
+ const link = frameLinkVerdict(kf, opts?.payload?.['scroll_context']);
1109
+ const at = typeof opts?.timestamp === 'number' ? opts.timestamp : Date.now();
1110
+ framedOpts = {
1111
+ ...opts,
1112
+ frameId: kf && (link === 'linked' || link === 'unverified') ? kf.frameId : undefined,
1113
+ payload: {
1114
+ ...(opts?.payload ?? {}),
1115
+ frame_link: link,
1116
+ ...(kf ? { frame_age_ms: Math.max(0, at - kf.capturedAt) } : {}),
1117
+ },
1118
+ };
1119
+ }
1120
+
1121
+ const event = this.pipeline.emit(type, framedOpts);
1122
+
1123
+ // Trigger capture on screen view or interaction
1124
+ if (event) {
1125
+ if (type === 'SCREEN_VIEW' && opts?.screen) {
1126
+ this.captureManager.onScreenChange(opts.screen);
1127
+ } else if (type === 'USER_ACTION') {
1128
+ // Extract interaction coordinates from the gesture payload
1129
+ // so the captured frame carries tap location metadata.
1130
+ const p = opts?.payload;
1131
+ const x = typeof p?.x === 'number' ? p.x : undefined;
1132
+ const y = typeof p?.y === 'number' ? p.y : undefined;
1133
+ const gestureType = typeof p?.gestureType === 'string' ? p.gestureType : undefined;
1134
+
1135
+ // Scroll gestures use onScrollSettle (which debounces into a single
1136
+ // capture). Using onInteraction for scroll would produce a DUPLICATE
1137
+ // screenshot alongside the ScrollTracker's onScrollSettle call.
1138
+ if (gestureType === 'scroll') {
1139
+ this.captureManager.onScrollSettle(opts?.screen ?? this._lastKnownScreen ?? undefined);
1140
+ } else {
1141
+ let interactionPoint: import('./sessionTypes').ReplayFrame['interactionPoint'];
1142
+ if (x !== undefined && y !== undefined && gestureType) {
1143
+ interactionPoint = {
1144
+ x,
1145
+ y,
1146
+ normalizedX: typeof p?.normalizedX === 'number' ? p.normalizedX : undefined,
1147
+ normalizedY: typeof p?.normalizedY === 'number' ? p.normalizedY : undefined,
1148
+ screenWidth: this.session?.metadata?.screenWidth ?? 0,
1149
+ screenHeight: this.session?.metadata?.screenHeight ?? 0,
1150
+ viewportWidth: typeof p?.viewportWidth === 'number' ? p.viewportWidth : undefined,
1151
+ viewportHeight: typeof p?.viewportHeight === 'number' ? p.viewportHeight : undefined,
1152
+ gestureType,
1153
+ endX: typeof p?.endX === 'number' ? p.endX : undefined,
1154
+ endY: typeof p?.endY === 'number' ? p.endY : undefined,
1155
+ normalizedEndX: typeof p?.normalizedEndX === 'number' ? p.normalizedEndX : undefined,
1156
+ normalizedEndY: typeof p?.normalizedEndY === 'number' ? p.normalizedEndY : undefined,
1157
+ durationMs: typeof p?.durationMs === 'number' ? p.durationMs : undefined,
1158
+ direction: typeof p?.direction === 'string' ? p.direction : undefined,
1159
+ };
1160
+ }
1161
+
1162
+ this.captureManager.onInteraction(interactionPoint);
1163
+ }
1164
+ }
1165
+ }
1166
+ } catch (err) {
1167
+ logger.error('[SessionManager] emitEvent failed:', err);
1168
+ }
1169
+ }
1170
+
1171
+ /**
1172
+ * Anchor a crash in the session context.
1173
+ * Protects nearby events and frames from eviction.
1174
+ */
1175
+ anchorCrash(crashEventId: string): CrashAnchor | null {
1176
+ if (!this.active) return null;
1177
+
1178
+ try {
1179
+ // Anchor events
1180
+ const anchor = this.pipeline.anchorCrash(crashEventId);
1181
+
1182
+ // Anchor frames (protect frames near the crash timestamp)
1183
+ const crashEvent = this.pipeline.getEvents().find(
1184
+ e => e.eventId === crashEventId,
1185
+ );
1186
+ if (crashEvent) {
1187
+ const protectedFrameIds = this.captureManager.protectFramesNearCrash(crashEvent.ts);
1188
+ anchor.nearbyFrameIds = protectedFrameIds;
1189
+ }
1190
+
1191
+ // Capture an error-context frame
1192
+ this.captureManager.captureManual('error-context').catch(() => { });
1193
+
1194
+ this.crashAnchors.push(anchor);
1195
+
1196
+ __DEV__ && logger.debug(
1197
+ `[SessionManager] Crash anchored: ${crashEventId}, ` +
1198
+ `protected ${anchor.nearbyEventIds.length} events, ` +
1199
+ `${anchor.nearbyFrameIds.length} frames`,
1200
+ );
1201
+
1202
+ return anchor;
1203
+ } catch (err) {
1204
+ logger.error('[SessionManager] anchorCrash failed:', err);
1205
+ return null;
1206
+ }
1207
+ }
1208
+
1209
+ // ─── Navigation / Screen Integration ────────────────────────────────
1210
+
1211
+ /**
1212
+ * Notify of a screen change. Called by ScaleBunDebugRoot via NavDetector.
1213
+ */
1214
+ /** The screen currently on top, or null before the first navigation is observed. */
1215
+ getLastKnownScreen(): string | null {
1216
+ return this._lastKnownScreen;
1217
+ }
1218
+
1219
+ onScreenChange(screenName: string): void {
1220
+ // Always track the current screen — even when inactive.
1221
+ // This allows startSession to emit the initial SCREEN_VIEW for
1222
+ // the screen that was already mounted before Record was pressed.
1223
+ const previous = this._lastKnownScreen;
1224
+ const enteredAt = this._screenEnteredAt;
1225
+ this._lastKnownScreen = screenName;
1226
+ this._screenEnteredAt = Date.now();
1227
+
1228
+ if (!this.active) return; // Defense-in-depth: no emission when recording is OFF
1229
+
1230
+ // SCREEN_EXIT for the screen being left, carrying its dwell time.
1231
+ //
1232
+ // The exit was previously detectable ONLY inside the Engage trigger engine and never became an
1233
+ // event, so time-on-screen had to be inferred from the gap to the NEXT SCREEN_VIEW — which is
1234
+ // wrong whenever the app backgrounds in between, and impossible for the last screen of a
1235
+ // session. Emitting it here makes dwell a measured value rather than a derived guess.
1236
+ if (previous && previous !== screenName && enteredAt != null) {
1237
+ this.emitEvent('SCREEN_EXIT', {
1238
+ screen: previous,
1239
+ subtype: previous,
1240
+ source: 'sdk',
1241
+ payload: { dwellMs: Math.max(0, Date.now() - enteredAt), to: screenName },
1242
+ });
1243
+ }
1244
+
1245
+ this.emitEvent('SCREEN_VIEW', {
1246
+ screen: screenName,
1247
+ subtype: screenName,
1248
+ source: 'sdk',
1249
+ ...(previous && previous !== screenName ? { payload: { from: previous } } : {}),
1250
+ });
1251
+ }
1252
+
1253
+ /**
1254
+ * Notify of a user interaction. Called by ScaleBunDebugRoot touch handlers.
1255
+ * Also triggers frame capture for desktop-initiated recordings.
1256
+ */
1257
+ onUserAction(
1258
+ subtype: string,
1259
+ payload?: Record<string, unknown>,
1260
+ context?: { screen?: string; timestamp?: number },
1261
+ ): void {
1262
+ if (!this.active) return; // Defense-in-depth: no emission when recording is OFF
1263
+ this.emitEvent('USER_ACTION', {
1264
+ subtype,
1265
+ screen: context?.screen ?? this._lastKnownScreen ?? undefined,
1266
+ payload,
1267
+ source: 'user',
1268
+ timestamp: context?.timestamp,
1269
+ });
1270
+ // NOTE: Do NOT call captureManager.onInteraction() here.
1271
+ // emitEvent() already triggers onInteraction() for USER_ACTION events (line ~405).
1272
+ // Calling it again here caused DUPLICATE frame captures per gesture.
1273
+ }
1274
+
1275
+ /**
1276
+ * Detailed gesture detection — called by ScaleBunDebugRoot touch handlers.
1277
+ * Emits a USER_ACTION event with gesture-specific subtype and payload.
1278
+ */
1279
+ onGestureDetected(gestureType: string, details?: {
1280
+ x?: number;
1281
+ y?: number;
1282
+ endX?: number;
1283
+ endY?: number;
1284
+ duration?: number;
1285
+ durationMs?: number;
1286
+ direction?: string;
1287
+ target?: string;
1288
+ viewportWidth?: number;
1289
+ viewportHeight?: number;
1290
+ pageX?: number;
1291
+ pageY?: number;
1292
+ locationX?: number;
1293
+ locationY?: number;
1294
+ normalizedX?: number;
1295
+ normalizedY?: number;
1296
+ normalizedEndX?: number;
1297
+ normalizedEndY?: number;
1298
+ confidence?: string;
1299
+ coordinateSpace?: string;
1300
+ source?: string;
1301
+ // Pre-computed normalization flag — set by JS fallback when it normalized
1302
+ // against the measured root-view rect (the exact view the screenshot captures).
1303
+ // Without this flag, SessionManager.emitEvent would overwrite the pre-computed
1304
+ // values via _resolveNormalizationBaseDp using a potentially-different base.
1305
+ normalizedPrecomputed?: boolean;
1306
+ // Diagnostic fields used by the dashboard to verify capture-space alignment.
1307
+ statusBarHeight?: number;
1308
+ safeAreaTop?: number;
1309
+ rootViewTop?: number;
1310
+ rootViewLeft?: number;
1311
+ rootViewWidth?: number;
1312
+ rootViewHeight?: number;
1313
+ windowWidth?: number;
1314
+ windowHeight?: number;
1315
+ screenshotCropTop?: number;
1316
+ screenWidth?: number;
1317
+ screenHeight?: number;
1318
+ platform?: string;
1319
+ interactionId?: string;
1320
+ interactionProtocol?: number;
1321
+ occurredAt?: number;
1322
+ ui?: string;
1323
+ stateStatus?: InteractionStateStatus;
1324
+ targetId?: string;
1325
+ screenName?: string;
1326
+ emitAutomaticAnalytics?: boolean;
1327
+ /**
1328
+ * RN-6 — the native↔JS join's own verdict, and it MUST be declared here.
1329
+ *
1330
+ * This signature ENUMERATES its fields rather than spreading, and the only caller
1331
+ * (ScaleBunDebugRoot's native listener) invokes it through `(sm as any)`. So a field
1332
+ * added at the call site and not added here is dropped in silence, with no type
1333
+ * error anywhere — which is precisely how these three nearly shipped as a no-op.
1334
+ *
1335
+ * 'exact' = one JS start was clearly closest. 'ambiguous' = two were within
1336
+ * CORRELATION_MARGIN_MS of each other, so target/ui/screenName were deliberately
1337
+ * withheld rather than guessed. 'none' = no start within tolerance, meaning the JS
1338
+ * handler never fired. The last two are kept distinct because they have different
1339
+ * fixes and collapsing them would hide which is happening in production.
1340
+ */
1341
+ correlation?: 'exact' | 'ambiguous' | 'none';
1342
+ correlationDeltaMs?: number | null;
1343
+ correlationRunnerUpDeltaMs?: number | null;
1344
+ /**
1345
+ * RN-1/RN-2 — which kind of identity `targetId` is. Declared here for the same reason as
1346
+ * the correlation fields above: this signature enumerates, and its only caller goes
1347
+ * through `(sm as any)`, so an undeclared field is dropped in silence.
1348
+ */
1349
+ targetSource?: 'testID' | 'path' | 'component' | 'none';
1350
+ /** RN-8: where the screen name came from. Declared here because this signature enumerates. */
1351
+ screenSource?: 'manual' | 'navigation' | 'none';
1352
+ /**
1353
+ * RN-13: the target control's rectangle in capture-base fractions. Declared here because
1354
+ * this signature ENUMERATES — the sixth field to need it, after three correlation fields,
1355
+ * targetSource and screenSource.
1356
+ */
1357
+ targetRect?: { x: number; y: number; width: number; height: number };
1358
+ /**
1359
+ * RN-3: content offset of the nearest ancestor scroller. Declared here because this
1360
+ * signature ENUMERATES — the seventh field to need it.
1361
+ */
1362
+ scrollX?: number;
1363
+ scrollY?: number;
1364
+ scrollSource?: 'wrapper' | 'native' | 'none';
1365
+ /**
1366
+ * RN-4: list row identity. Declared here because this signature ENUMERATES — the eighth
1367
+ * and ninth fields to need it.
1368
+ */
1369
+ itemKey?: string;
1370
+ itemIndex?: number;
1371
+ /**
1372
+ * RN-15: tap position within the target control. Declared here because this signature
1373
+ * ENUMERATES — the tenth and eleventh fields to need it.
1374
+ */
1375
+ localU?: number;
1376
+ localV?: number;
1377
+ /**
1378
+ * Native scroll chain at touch-down (innermost first, capture pixels), the device pixel
1379
+ * density, and the tap-contract version. Declared here because this signature ENUMERATES.
1380
+ */
1381
+ scrollContext?: import('../journey/nativeScroll').NativeScrollEntry[];
1382
+ pixelDensity?: number;
1383
+ heatmapSchemaVersion?: number;
1384
+ }): void {
1385
+ // Exact-duplicate suppression — see _lastGestureSig. Signature is the gesture type plus
1386
+ // raw coordinates verbatim; String() keeps undefined coords distinct from 0 (a payload
1387
+ // with no coords never collides with a real origin tap).
1388
+ const suppliedInteractionId = details?.interactionId;
1389
+ const dedupSig = suppliedInteractionId
1390
+ ? `id:${suppliedInteractionId}`
1391
+ : `${gestureType}|${String(details?.x)}|${String(details?.y)}|${String(details?.endX)}|${String(details?.endY)}`;
1392
+ const nowTs = Date.now();
1393
+ if (
1394
+ dedupSig === this._lastGestureSig &&
1395
+ nowTs - this._lastGestureTs <= SessionManager.GESTURE_DEDUP_WINDOW_MS
1396
+ ) {
1397
+ this._lastGestureTs = nowTs; // a burst of 3 stays suppressed even if gaps chain past the window
1398
+ return;
1399
+ }
1400
+ this._lastGestureSig = dedupSig;
1401
+ this._lastGestureTs = nowTs;
1402
+
1403
+ // Build the canonical gesture payload ONCE so the recording lane and the
1404
+ // (additive) analytics lane carry byte-identical keys (normalizedX/Y,
1405
+ // gestureType, etc.). Same object shape that was previously inlined.
1406
+ const interactionId = suppliedInteractionId ?? generateInteractionId();
1407
+ const occurredAt = details?.occurredAt ?? Date.now();
1408
+ const stateStatus = details?.stateStatus ?? (details?.ui ? 'captured_nonempty' : 'not_captured');
1409
+ const payload: Record<string, unknown> = {
1410
+ interaction_id: interactionId,
1411
+ interaction_protocol: details?.interactionProtocol ?? INTERACTION_PROTOCOL_VERSION,
1412
+ state_status: stateStatus,
1413
+ ui: details?.ui,
1414
+ target_id: details?.targetId,
1415
+ /**
1416
+ * RN-6 — shipped with the row so the join is auditable instead of assumed.
1417
+ *
1418
+ * Without it, an interaction with no `target_id` is ambiguous in the worst way:
1419
+ * it could mean the control genuinely had no identity, or that the correlation
1420
+ * refused and the identity exists but could not be attributed. Those are
1421
+ * different defects with different owners, and the accounting lane cannot give
1422
+ * an honest unplaceable REASON without knowing which.
1423
+ *
1424
+ * Undefined on the JS-fallback path (no native event, so nothing was joined),
1425
+ * which is itself the correct answer there rather than a fabricated 'exact'.
1426
+ */
1427
+ correlation: details?.correlation,
1428
+ correlation_delta_ms: details?.correlationDeltaMs,
1429
+ correlation_runner_up_delta_ms: details?.correlationRunnerUpDeltaMs,
1430
+ /**
1431
+ * RN-1/RN-2 — stored alongside `target_id` so a reader can tell an authored
1432
+ * identifier from a structural inference. 'path' means the app authored no testID
1433
+ * and the identity was derived from component names, which is useful but will
1434
+ * change under refactoring; a dashboard that presents the two identically invites
1435
+ * someone to trust a key that is about to move.
1436
+ */
1437
+ target_source: details?.targetSource,
1438
+ /**
1439
+ * RN-8 — stored so "13.8% of interactions have a screen" becomes answerable.
1440
+ * 'none' means the app instrumented nothing the SDK could see; 'manual' means a
1441
+ * <ScaleBunScreen> beacon; 'navigation' means the React Navigation hook. Three
1442
+ * different owners behind one number that today has none.
1443
+ */
1444
+ screen_source: details?.screenSource,
1445
+ /**
1446
+ * RN-13 — what makes "the heat is on this control" checkable instead of assumed.
1447
+ * Absent when the control could not be measured synchronously, which is a fact worth
1448
+ * storing: an unmeasurable control and a mis-placed tap are different findings.
1449
+ */
1450
+ target_rect: details?.targetRect,
1451
+ /**
1452
+ * RN-3 — the backend already reads scrollX/scrollY off the payload into the fact
1453
+ * table's own columns (it has done since the web SDK, which sends `sx`/`sy`), so
1454
+ * these need no migration. Mobile simply never sent them.
1455
+ *
1456
+ * `scroll_source` says whether an offset was available at all: 'none' means the app
1457
+ * uses a plain <ScrollView> rather than the SDK's wrapper, which is a coverage gap in
1458
+ * the integration and not a tap at the top of a list.
1459
+ */
1460
+ scrollX: details?.scrollX,
1461
+ scrollY: details?.scrollY,
1462
+ scroll_source: details?.scrollSource,
1463
+ /**
1464
+ * Every scroll container under the finger at touch-down, innermost first, in the
1465
+ * capture view's pixel space (native probe). The single scrollX/Y above is the
1466
+ * innermost; the chain is what lets a nested list be placed without guessing.
1467
+ */
1468
+ scroll_context: details?.scrollContext,
1469
+ pixel_density: details?.pixelDensity,
1470
+ heatmap_schema_version: details?.heatmapSchemaVersion,
1471
+ /**
1472
+ * RN-4 — what stops a recycled native view merging two different rows into one
1473
+ * hotspot. The key is the app's own keyExtractor output, so it is stable across
1474
+ * scrolling and across sessions; the index is carried so a recycled slot (same index,
1475
+ * different key) can be proven rather than inferred.
1476
+ */
1477
+ item_key: details?.itemKey,
1478
+ item_index: details?.itemIndex,
1479
+ /**
1480
+ * RN-15 — where in the control, not just which control. Turns "they tapped Add to
1481
+ * Cart" into "they tapped its left edge", which is what tells you whether a hit area
1482
+ * is right. Outside 0..1 is a hitSlop tap and is kept as such.
1483
+ */
1484
+ local_u: details?.localU,
1485
+ local_v: details?.localV,
1486
+ gestureType,
1487
+ x: details?.x,
1488
+ y: details?.y,
1489
+ endX: details?.endX,
1490
+ endY: details?.endY,
1491
+ duration: details?.duration ?? details?.durationMs,
1492
+ durationMs: details?.durationMs ?? details?.duration,
1493
+ direction: details?.direction,
1494
+ target: details?.target,
1495
+ viewportWidth: details?.viewportWidth,
1496
+ viewportHeight: details?.viewportHeight,
1497
+ pageX: details?.pageX,
1498
+ pageY: details?.pageY,
1499
+ locationX: details?.locationX,
1500
+ locationY: details?.locationY,
1501
+ // Native / JS-precomputed coords — passed through without overwriting
1502
+ normalizedX: details?.normalizedX,
1503
+ normalizedY: details?.normalizedY,
1504
+ normalizedEndX: details?.normalizedEndX,
1505
+ normalizedEndY: details?.normalizedEndY,
1506
+ confidence: details?.confidence,
1507
+ coordinateSpace: details?.coordinateSpace,
1508
+ source: details?.source,
1509
+ normalizedPrecomputed: details?.normalizedPrecomputed,
1510
+ statusBarHeight: details?.statusBarHeight,
1511
+ safeAreaTop: details?.safeAreaTop,
1512
+ rootViewTop: details?.rootViewTop,
1513
+ rootViewLeft: details?.rootViewLeft,
1514
+ rootViewWidth: details?.rootViewWidth,
1515
+ rootViewHeight: details?.rootViewHeight,
1516
+ windowWidth: details?.windowWidth,
1517
+ windowHeight: details?.windowHeight,
1518
+ screenshotCropTop: details?.screenshotCropTop,
1519
+ screenWidth: details?.screenWidth,
1520
+ screenHeight: details?.screenHeight,
1521
+ platform: details?.platform,
1522
+ };
1523
+
1524
+ // ── Feature #4 (heatmaps): additive analytics emission ──
1525
+ // When NOT recording, route the tap to the always-on analytics lane so
1526
+ // heatmaps still get coords (opt-in via captureInteractionHeatmap). No-op
1527
+ // when the flag is OFF, no backend transport is attached, or recording is
1528
+ // active (the recording path below already carries this tap). Uses a fresh
1529
+ // payload clone so analytics normalization never mutates the recording one.
1530
+ const analyticsReplayCarrier = this._emitInteractionToAnalytics(
1531
+ gestureType,
1532
+ { ...payload },
1533
+ occurredAt,
1534
+ details?.screenName,
1535
+ );
1536
+ const replayCarrier = !!this._backendTransport && (this.active || analyticsReplayCarrier);
1537
+ if (details?.emitAutomaticAnalytics) {
1538
+ try {
1539
+ const safePayload = redactBody(payload) as Record<string, unknown>;
1540
+ emitAutomaticEvent(
1541
+ 'element_interacted',
1542
+ automaticInteractionProperties(
1543
+ safePayload,
1544
+ details?.screenName ?? this._lastKnownScreen ?? undefined,
1545
+ replayCarrier,
1546
+ ),
1547
+ occurredAt,
1548
+ );
1549
+ } catch { /* automatic projection must never affect interaction capture */ }
1550
+ }
1551
+
1552
+ if (!this.active) return; // Defense-in-depth: no emission when recording is OFF
1553
+ // The subtype becomes `ReplayEvent.label`, which is what the Events explorer GROUPS BY. Naming
1554
+ // the tapped control here is what splits taps per control instead of collapsing every tap in the
1555
+ // app into one `gesture:tap` row. Falls back to the bare gesture when nothing identified the
1556
+ // target, so behaviour is unchanged for taps that cannot be resolved.
1557
+ //
1558
+ // ⚠️ `gestureType` also travels in the payload, and the backend's `resolveGesture` reads THAT
1559
+ // first — so enriching the label cannot change heatmap gesture classification.
1560
+ const target = typeof details?.target === 'string' ? details.target.trim() : '';
1561
+ this.onUserAction(
1562
+ target ? `gesture:${gestureType} · ${target}` : `gesture:${gestureType}`,
1563
+ payload,
1564
+ { screen: details?.screenName, timestamp: occurredAt },
1565
+ );
1566
+ }
1567
+
1568
+ /**
1569
+ * Manual frame capture — triggered by Desktop "Capture Step" button.
1570
+ */
1571
+ async captureManualFrame(): Promise<{ frameId?: string } | null> {
1572
+ if (!this.active || !this.captureManager) return null; // No capture when recording is OFF
1573
+ const frame = await this.captureManager.captureManual('manual');
1574
+ return frame ? { frameId: frame.frameId } : null;
1575
+ }
1576
+
1577
+ /**
1578
+ * Notify of app lifecycle change.
1579
+ */
1580
+ onLifecycleChange(state: 'foreground' | 'background'): void {
1581
+ this.emitEvent(
1582
+ state === 'foreground' ? 'APP_FOREGROUND' : 'APP_BACKGROUND',
1583
+ { source: 'system' },
1584
+ );
1585
+ }
1586
+
1587
+ // ─── Scroll Integration ─────────────────────────────────────────────
1588
+
1589
+ /**
1590
+ * Notify of a scroll event from a scrollable surface (ScaleBunScrollView).
1591
+ * Delegates to ScrollTracker for accumulation and settle detection.
1592
+ */
1593
+ onScrollEvent(contentOffsetX: number, contentOffsetY: number): void {
1594
+ if (!this.active) return;
1595
+ this.scrollTracker?.onScrollEvent(contentOffsetX, contentOffsetY);
1596
+ }
1597
+
1598
+ // ─── Loading State Integration ──────────────────────────────────────
1599
+
1600
+ /**
1601
+ * Notify that a screen has started loading.
1602
+ * Explicit instrumentation — NOT auto-called on screen entry.
1603
+ * Call from app code: ScaleBun.markLoadingStarted('ScreenName')
1604
+ */
1605
+ onLoadingStarted(screenName: string): void {
1606
+ if (!this.active) return;
1607
+
1608
+ this.loadingStartTimes.set(screenName, Date.now());
1609
+
1610
+ this.emitEvent('LOADING_STARTED', {
1611
+ screen: screenName,
1612
+ subtype: screenName,
1613
+ payload: { screen: screenName },
1614
+ source: 'sdk',
1615
+ });
1616
+ }
1617
+
1618
+ /**
1619
+ * Notify that a screen has finished loading.
1620
+ * Explicit instrumentation — call from app code.
1621
+ * Triggers a 'screen_loaded' frame capture for the settled state.
1622
+ */
1623
+ onLoadingCompleted(screenName: string): void {
1624
+ if (!this.active) return;
1625
+
1626
+ const startTime = this.loadingStartTimes.get(screenName);
1627
+ const durationMs = startTime ? Date.now() - startTime : undefined;
1628
+ this.loadingStartTimes.delete(screenName);
1629
+
1630
+ this.emitEvent('LOADING_COMPLETED', {
1631
+ screen: screenName,
1632
+ subtype: screenName,
1633
+ payload: {
1634
+ screen: screenName,
1635
+ durationMs,
1636
+ },
1637
+ source: 'sdk',
1638
+ });
1639
+
1640
+ // Capture the settled loaded screen
1641
+ this.captureManager.onLoadingEnd(screenName);
1642
+ }
1643
+
1644
+ /**
1645
+ * App network activity, forwarded to replay capture so a frame can wait for the screen's
1646
+ * data instead of photographing the wait. Called by NetworkFeature, which has already
1647
+ * excluded this SDK's own uploads and third-party beacons.
1648
+ *
1649
+ * Deliberately NOT gated on `this.active`: the counter must stay balanced across a session
1650
+ * boundary. Dropping the `end` of a request whose `start` was counted would leave replay
1651
+ * believing the app is permanently loading, and a session can start or stop mid-request.
1652
+ * The capture manager ignores the signal when it is not capturing anyway.
1653
+ */
1654
+ onNetworkActivity(phase: 'start' | 'end'): void {
1655
+ try {
1656
+ this.captureManager.onNetworkActivity(phase);
1657
+ } catch {
1658
+ // Advisory signal — it must never take down a network request.
1659
+ }
1660
+ }
1661
+
1662
+ // ─── Sync Policy ────────────────────────────────────────────────────
1663
+
1664
+ /**
1665
+ * Override the sync policy. SDK-owned — native only provides metadata.
1666
+ */
1667
+ setSyncPolicy(policy: SyncDecisionCallback): void {
1668
+ this.syncEngine.setPolicy(policy);
1669
+ }
1670
+
1671
+ /**
1672
+ * Provide native-gathered sync metadata for the current session.
1673
+ * Does NOT make the sync decision — that stays in SDK's SyncDecisionEngine.
1674
+ */
1675
+ setNativeSyncMetadata(metadata: NativeSyncMetadata): void {
1676
+ // Stored for use at session end evaluation
1677
+ if (this.session) {
1678
+ (this.session as any)._nativeSyncMetadata = metadata;
1679
+ }
1680
+ }
1681
+ }
1682
+
1683
+ // ─── Helpers ────────────────────────────────────────────────────────────────
1684
+
1685
+ function generateSessionId(): string {
1686
+ return `ses-${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 8)}`;
1687
+ }