@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,1915 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.BackendSessionAdapter = void 0;
7
+ var _internalLogger = require("../../core/logger/internalLogger");
8
+ var _base = require("../../core/encoding/base64");
9
+ var _endpoints = require("../../transport/http/endpoints");
10
+ var _installationId = require("../../core/id/installationId");
11
+ var _version = require("../../core/constants/version");
12
+ var _bridgeAdapter = require("../replay/bridge/adapters/bridgeAdapter");
13
+ var _nativeModule = require("../replay/bridge/nativeModule");
14
+ var _batching = require("../../analytics/batching");
15
+ var _quotaState = require("../../config/quotaState");
16
+ var _nativeQuota = require("../replay/bridge/nativeQuota");
17
+ /**
18
+ * ScaleBun SDK — Backend Session Adapter
19
+ *
20
+ * Implements the SessionTransport interface (System A) and bridges
21
+ * session/frame/event data to the backend API via HTTP.
22
+ *
23
+ * This is the ADAPTER that connects the existing SessionManager
24
+ * (which only has DesktopSessionTransport) to the backend.
25
+ *
26
+ * Design:
27
+ * - Uses the same endpoint contract as BackendReplayTransport
28
+ * (POST /v1/replay/sessions, /frames, /events, /finalize)
29
+ * - Batches frames and events with auto-flush
30
+ * - Never throws — all errors are caught and logged
31
+ * - Behind config flag: only constructed when backendUpload is enabled
32
+ *
33
+ * This does NOT duplicate the existing BackendReplayTransport.
34
+ * It exists because SessionManager uses a different interface
35
+ * (SessionTransport from sessionTypes.ts) than ReplayTransport
36
+ * (from features/replay/transport/ReplayTransport.ts).
37
+ * The payload shapes differ, so this adapter maps between them.
38
+ *
39
+ * MVP/dev-only: base64 frames are sent inline in JSON body.
40
+ */
41
+
42
+ // Reused rather than reimplemented: `chunkEvents` is generic, order-preserving, and already carries
43
+ // a contract test proving it drops and duplicates nothing (analytics/__tests__/batchChunking.test.ts).
44
+ // Its internal ceiling (MAX_BATCH_EVENTS = 500) is well above the 100-frame cap passed here.
45
+
46
+ // ─── Config ─────────────────────────────────────────────────────────────────
47
+
48
+ /**
49
+ * One platform-agnostic log line for the logs lane. The queue forwards whatever it is
50
+ * given — `source`/`platform` are neutral string hints set by the PRODUCER (console
51
+ * tags 'console'/'js'; a future native producer tags its own values onto this SAME
52
+ * shape). `level` is free-text. Nothing here is JS-specific.
53
+ */
54
+
55
+ // ─── Adapter ────────────────────────────────────────────────────────────────
56
+
57
+ class BackendSessionAdapter {
58
+ /** Durable store for cross-kill frame recovery (null → recovery disabled). */
59
+
60
+ /** Storage key for the snapshotted unflushed frame buffer (see persistPendingFrames). */
61
+ static PENDING_FRAMES_KEY = 'scalebun_replay_pending_frames';
62
+ /**
63
+ * Byte budget for the cross-kill frame snapshot (see `_selectFramesToPersist`).
64
+ *
65
+ * Replaces a flat 10-frame cap. A count could not bound the KV write at all — these frames carry
66
+ * base64 screenshots, so ten of them is anywhere from ~200 KB to several MB depending on device
67
+ * resolution, and an oversized value is exactly what fails on the background transition where
68
+ * this write happens.
69
+ *
70
+ * 2 MB is deliberately modest: this is a last-resort snapshot for frames that never reached the
71
+ * network, not a mirror of the session. Enough for a keyframe plus a run of deltas on any device;
72
+ * small enough that writing it cannot itself be the reason backgrounding stalls.
73
+ */
74
+ static MAX_PERSIST_BYTES = 2 * 1024 * 1024;
75
+ /**
76
+ * Frames the backend accepts in ONE request — `'Max 100 frames per batch'`
77
+ * (`IngestionService.uploadFrames`). Exceeding it is a 4xx, which this SDK classifies as
78
+ * permanent.
79
+ *
80
+ * This has to exist alongside the byte budget, not instead of it. The budget alone cannot bound
81
+ * the COUNT, and the count is what the server checks: measured on real traffic, mobile keyframes
82
+ * average ~16 KB, so a 2 MB snapshot is around 128 frames — over the cap by construction. The old
83
+ * flat 10-frame rule was under it by construction, which is why this failure could not happen
84
+ * before and can now.
85
+ */
86
+ static MAX_FRAMES_PER_BATCH = 100;
87
+ // Per-session toggle: cleared to false if the backend reports storage disabled.
88
+
89
+ // True when telemetry should divert to the durable native outbox (both flags on).
90
+ // Native module availability is re-checked per call so a missing/old binary falls
91
+ // back to the JS path transparently.
92
+
93
+ // Cached one-shot native-outbox bootstrap promise (see _ensureNativeOutbox).
94
+ _nativeOutboxReady = null;
95
+ currentSessionId = null;
96
+ // §11 session_start ordering: a recording sessionId is added here ONLY after its
97
+ // INGESTION_START row is confirmed created on the backend (either via sendSessionStart's
98
+ // own POST, or via startAnalyticsSession when recording adopts the analytics id).
99
+ // Child telemetry lanes (frames/events/network/errors/perf/logs) refuse to flush until
100
+ // the row exists — otherwise the backend 404s the child write. Data stays buffered and
101
+ // the flush timer retries, so nothing is lost to a start/child race. The durable native
102
+ // outbox path has its own ordering; this guards only the JS in-memory fallback.
103
+ _startedSessionIds = new Set();
104
+ // §11 offline-start recovery: INGESTION_START payloads whose POST failed (e.g. the app
105
+ // launched with no connectivity). Without this, a failed START was logged and forgotten —
106
+ // the row-ready gate never opened, every child lane re-buffered forever, and the session
107
+ // stayed stuck until an app restart even after the network returned. Each flush tick
108
+ // re-attempts these until acked (network errors keep retrying; permanent 4xx gives up).
109
+ _pendingStartPayloads = new Map();
110
+ _startRetryInFlight = false;
111
+ pendingFrames = [];
112
+ pendingEvents = [];
113
+ pendingNetworkRequests = [];
114
+ pendingErrors = [];
115
+ pendingPerformanceMetrics = [];
116
+ // Platform-agnostic logs lane. Neutral by construction — holds whatever LogQueueItems
117
+ // it's handed (console today; native later) and forwards them untouched.
118
+ pendingLogs = [];
119
+ flushTimer = null;
120
+ destroyed = false;
121
+ /**
122
+ * Latch for `sdk_replay_capture_failed` — reported once per adapter, not once per rejected batch.
123
+ *
124
+ * A session whose row was never created rejects EVERY frame batch, so an unlatched report would
125
+ * emit one analytics event per flush for as long as the app stays open, on the same lane it is
126
+ * trying to report through.
127
+ */
128
+ _captureFailureReported = false;
129
+
130
+ // ── Analytics lane (always-on, foreground-scoped; isolated from recording) ──
131
+ // Fully separate from currentSessionId/pendingEvents/_flushEvents, which serve
132
+ // recording. This lane carries user track() events and never touches recording
133
+ // state. One queue doubles as the pre-ready buffer (bounded, drop-oldest) and
134
+ // the outgoing queue; sessionId is stamped at flush so endpoint id == body id.
135
+ static ANALYTICS_BUFFER_CAP = 100;
136
+ /** Logs lane: queue length that triggers an eager flush (mirrors the network lane). */
137
+ static LOGS_BATCH_SIZE = 20;
138
+ /** Logs lane: max rows posted per flush (server caps at 200/batch). */
139
+ static LOGS_FLUSH_MAX = 50;
140
+ _analyticsSessionId = null;
141
+ pendingAnalyticsEvents = [];
142
+ analyticsFlushTimer = null;
143
+ // iOS finalize reconciliation: the id of a backgrounded analytics session whose
144
+ // INGESTION_FINALIZE has NOT been confirmed delivered. Set synchronously in
145
+ // finalizeAnalyticsSession before any await (so it survives an iOS suspension of
146
+ // the awaited fetch), cleared only on a confirmed finalize ACK. On the next
147
+ // foreground the listener calls reconcilePendingFinalize() to close it for real.
148
+ // In-memory is sufficient: the JS context survives background→foreground; only a
149
+ // true process kill drops it (covered by the server-side idle-close backstop).
150
+ _pendingFinalizeId = null;
151
+
152
+ // ── Per-feature quota (shared contract v1) ──
153
+ /** Unsubscribe from quotaState (set in the constructor, released in destroy). */
154
+ _quotaUnsub = null;
155
+ /**
156
+ * Sessions whose INGESTION_START was refused with `quota_exceeded` (feature `sessions`). No row
157
+ * will ever exist for them, so every child lane for that id is dropped rather than buffered
158
+ * behind a row-ready gate that can never open.
159
+ */
160
+ _refusedSessionIds = new Set();
161
+ /** Unregister this adapter as the native sessions-refusal handler. */
162
+ _nativeRefusedUnsub = null;
163
+ /** Host hook (SessionManager) to end a recording whose session start was refused. */
164
+ _onSessionRefused = null;
165
+ constructor(config) {
166
+ this.config = {
167
+ batchSize: 5,
168
+ flushIntervalMs: 3000,
169
+ timeoutMs: 15_000,
170
+ useObjectStorage: false,
171
+ enableNativeFileOutbox: false,
172
+ nativeOutboxUploadEnabled: false,
173
+ nativeOutboxTuning: {},
174
+ idleThresholdMs: 60_000,
175
+ ...config,
176
+ clientKey: config.clientKey
177
+ };
178
+ this._frameStore = config.storage ?? null;
179
+ this.objectStorageActive = this.config.useObjectStorage;
180
+ this.nativeOutboxEnabled = this.config.enableNativeFileOutbox && this.config.nativeOutboxUploadEnabled;
181
+ // Quota: purge/park a lane the moment its block starts, un-park it when it ends. Applies to
182
+ // blocks from /config AND from refusals seen on any lane (including another lane's 402).
183
+ this._quotaUnsub = _quotaState.quotaState.subscribe((feature, blocked) => this._onQuotaChange(feature, blocked));
184
+ // The native lane mirror (re-asserted on EVERY quota sync) is registered once per process by
185
+ // subscribeNativeQuotaEvents(), before whichever native init builds an uploader. A session the
186
+ // native uploader saw refused is dropped here too.
187
+ this._nativeRefusedUnsub = (0, _nativeQuota.setNativeSessionRefusedHandler)(id => {
188
+ if (!this.destroyed) this._refuseSession(id);
189
+ });
190
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] backendUpload enabled`);
191
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] backendBaseUrl: ${this.config.endpoint}`);
192
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] transport fan-out active: desktop yes, backend yes`);
193
+ }
194
+
195
+ // ─── SessionTransport Interface ─────────────────────────────────────
196
+
197
+ /**
198
+ * §11 gate: true only when the current recording session's INGESTION_START row is
199
+ * confirmed on the backend. Child telemetry lanes consult this before flushing so they
200
+ * never race ahead of session creation (which would 404). Null id → not ready.
201
+ */
202
+ _sessionRowReady() {
203
+ return !!this.currentSessionId && this._startedSessionIds.has(this.currentSessionId);
204
+ }
205
+
206
+ /**
207
+ * The session live errors go to: the recording session when one runs, else the always-on
208
+ * analytics session.
209
+ *
210
+ * Errors used to require `currentSessionId` — the RECORDING lane — so on any device where replay
211
+ * was not recording (sampled out, `record: false`, between recordings) not one error left the
212
+ * phone, and a recovered crash drained at launch waited for a recording that might never start.
213
+ * The analytics session has a backend row from the moment it opens, which is all an error needs.
214
+ */
215
+ _errorsSessionId() {
216
+ return this.currentSessionId ?? this._analyticsSessionId;
217
+ }
218
+
219
+ /** Lazy: nativeCrashBridge → CrashReporter → SessionManager → this file would be a cycle. */
220
+ _publishNativeSession(sessionId) {
221
+ try {
222
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
223
+ const {
224
+ setNativeActiveSession
225
+ } = require('../crash/nativeCrashBridge');
226
+ void setNativeActiveSession(sessionId);
227
+ } catch {
228
+ // Losing this degrades a recovered crash to "current session" — never fail a start.
229
+ }
230
+ }
231
+
232
+ // ─── Quota (shared contract v1) ─────────────────────────────────────
233
+
234
+ /** True when the current recording session's START was refused for quota (no row, ever). */
235
+ _currentSessionRefused() {
236
+ return !!this.currentSessionId && this._refusedSessionIds.has(this.currentSessionId);
237
+ }
238
+
239
+ /** Register the host's reaction to a refused session start (SessionManager ends the recording). */
240
+ setSessionRefusedHandler(fn) {
241
+ this._onSessionRefused = fn;
242
+ }
243
+
244
+ /**
245
+ * A limited feature's block started or ended.
246
+ *
247
+ * Started: everything queued for it is PURGED (in memory, the cross-kill frame snapshot, and the
248
+ * native outbox lane), because the server will refuse it and a retry is exactly what the contract
249
+ * forbids. Capture itself is stopped by the owners of capture (SessionManager for frames,
250
+ * NetworkFeature/EventTracker gate at their entry points); this adapter also refuses new items at
251
+ * its own entry points, so a producer that was not told still cannot leak through.
252
+ *
253
+ * Ended: nothing is replayed — what was purged is gone. The native lanes follow via the sync
254
+ * listener registered in the constructor (it fires after every transition too).
255
+ */
256
+ _onQuotaChange(feature, blocked) {
257
+ if (this.destroyed) return;
258
+ if (blocked) {
259
+ if (feature === 'frames') {
260
+ this.pendingFrames = [];
261
+ this._clearPersistedFrames();
262
+ } else if (feature === 'networkRequests') {
263
+ this.pendingNetworkRequests = [];
264
+ } else if (feature === 'events') {
265
+ this.pendingEvents = [];
266
+ this.pendingAnalyticsEvents = [];
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * A session START came back 402 `quota_exceeded` (feature `sessions`). Drop the session: no row
273
+ * will be created, so nothing may queue behind it and the start is never retried.
274
+ */
275
+ _refuseSession(sessionId) {
276
+ this._refusedSessionIds.add(sessionId);
277
+ this._pendingStartPayloads.delete(sessionId);
278
+ if (sessionId === this._analyticsSessionId) {
279
+ this._analyticsSessionId = null;
280
+ this._stopAnalyticsFlushTimer();
281
+ this.pendingAnalyticsEvents = [];
282
+ }
283
+ if (sessionId === this.currentSessionId) {
284
+ this.pendingFrames = [];
285
+ this.pendingEvents = [];
286
+ this.pendingNetworkRequests = [];
287
+ this.pendingPerformanceMetrics = [];
288
+ this.pendingLogs = [];
289
+ try {
290
+ this._onSessionRefused?.(sessionId);
291
+ } catch {/* no-throw */}
292
+ }
293
+ _internalLogger.logger.warn(`[ScaleBun Replay] session start refused (quota_exceeded: sessions) — session dropped`);
294
+ }
295
+
296
+ /** True when an HTTP error carried a recorded quota refusal. */
297
+ static _isQuotaRefusal(err, feature) {
298
+ const f = err?.quotaFeature;
299
+ return !!f && (feature === undefined || f === feature);
300
+ }
301
+ sendSessionStart(session) {
302
+ this.currentSessionId = session.sessionId;
303
+ this._startFlushTimer();
304
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] session start requested`);
305
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] sessionId: ${session.sessionId}`);
306
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] deviceId: ${session.deviceId}`);
307
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] recordingSessionId: ${session.sessionId}`);
308
+
309
+ // One-row merge: when recording adopted the always-on analytics id
310
+ // (SessionManager.startSession), the INGESTION_START row already exists.
311
+ // Do NOT post a second one — just keep the flush timer running so frames/
312
+ // journey/network/errors/perf flush to INGESTION_*(analyticsId). Legacy
313
+ // mode (no clientKey, distinct minted id) still posts /replay/sessions.
314
+ if (this.config.clientKey && session.sessionId === this._analyticsSessionId) {
315
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] recording attached to analytics session ${session.sessionId} (no new row)`);
316
+ return;
317
+ }
318
+
319
+ // Quota: a `sessions` block stops NEW sessions. SessionManager already refuses to start one
320
+ // (it only adopts an analytics session that exists), so this is the transport's own backstop:
321
+ // mark the id refused — every child lane for it then drops instead of buffering forever.
322
+ if (this.config.clientKey && _quotaState.quotaState.isBlocked('sessions')) {
323
+ this._refusedSessionIds.add(session.sessionId);
324
+ _internalLogger.logger.warn(`[ScaleBun Replay] session start skipped — sessions quota exceeded`);
325
+ // Deferred: we are still inside SessionManager.startSession, which must finish before the
326
+ // recording it started can be ended.
327
+ const refusedId = session.sessionId;
328
+ Promise.resolve().then(() => {
329
+ try {
330
+ this._onSessionRefused?.(refusedId);
331
+ } catch {/* no-throw */}
332
+ });
333
+ return;
334
+ }
335
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] backend session upload started`);
336
+ const startEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_START : '/replay/sessions';
337
+
338
+ // Fetch native device info to enrich the session payload.
339
+ // Falls back to metadata-only if bridge unavailable (old native binary).
340
+ _bridgeAdapter.bridgeAdapter.getDeviceInfo().then(nativeInfo => {
341
+ const m = session.metadata;
342
+ this._cachedAppVersion = nativeInfo?.appVersion ?? m?.appVersion ?? null;
343
+ const payload = {
344
+ sessionId: session.sessionId,
345
+ deviceId: session.deviceId,
346
+ // The canonical per-install id — the SAME value OTA events are
347
+ // reported under, and a DIFFERENT value from deviceId. The
348
+ // backend stores it on the Device so release health can join a
349
+ // bundle's installs to that device's sessions. Without it, crash
350
+ // impact for a release resolves to "no data" for every device
351
+ // that never registered for push.
352
+ installationId: (0, _installationId.resolveInstallationId)(),
353
+ startedAt: session.startedAt,
354
+ // Link the recording row to the always-on analytics row so the
355
+ // dashboard can join them. track() events land on the analytics
356
+ // id; frames/journey land on this recording id. Without this
357
+ // link the two appear as separate sessions (one looks "empty").
358
+ // BACKEND DEPENDENCY: the dashboard must read/join on this field
359
+ // (or treat the analytics row as canonical) to fully merge them.
360
+ analyticsSessionId: this._analyticsSessionId,
361
+ // Carry the idle threshold on the recording lane too: it may
362
+ // create the row before the analytics lane does. Backend
363
+ // upsert leaves it untouched if already set.
364
+ idleThresholdMs: this.config.idleThresholdMs,
365
+ device: {
366
+ platform: nativeInfo?.platform ?? m?.platform,
367
+ osVersion: nativeInfo?.osVersion ?? m?.osVersion,
368
+ deviceModel: nativeInfo?.deviceModel ?? m?.deviceModel,
369
+ screenWidth: m?.screenWidth,
370
+ screenHeight: m?.screenHeight,
371
+ appName: nativeInfo?.appName ?? m?.appName,
372
+ appVersion: nativeInfo?.appVersion ?? m?.appVersion,
373
+ bundleId: nativeInfo?.bundleId ?? m?.bundleId,
374
+ sdkVersion: _version.SDK_VERSION,
375
+ releaseStage: m?.buildType,
376
+ // Backend ingestion source not in this repo; field name
377
+ // unconfirmed. Send both so whichever it reads is populated.
378
+ environment: m?.buildType
379
+ }
380
+ };
381
+ __DEV__ && _internalLogger.logger.debug(`[BackendSessionAdapter] POST session start: ${session.sessionId}`);
382
+ return this._post(startEndpoint, payload).then(() => {
383
+ // §11: row now exists — release the child-lane flush gate for this id.
384
+ this._startedSessionIds.add(session.sessionId);
385
+ this._pendingStartPayloads.delete(session.sessionId);
386
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] backend session upload success`);
387
+ }, err => {
388
+ // Quota: 402 quota_exceeded(sessions) — no row, never retried.
389
+ if (BackendSessionAdapter._isQuotaRefusal(err, 'sessions')) {
390
+ this._refuseSession(session.sessionId);
391
+ return;
392
+ }
393
+ // Offline-start recovery: keep the payload; the flush timer re-attempts it
394
+ // until the row is acked. A permanent 4xx (rejected key) is not retried —
395
+ // the same payload can never succeed.
396
+ if (!BackendSessionAdapter._isPermanentHttpError(err)) {
397
+ this._pendingStartPayloads.set(session.sessionId, payload);
398
+ }
399
+ _internalLogger.logger.info(`[ScaleBun Replay] backend session upload failure`);
400
+ });
401
+ }).catch(() => _internalLogger.logger.info(`[ScaleBun Replay] backend session upload failure`));
402
+ }
403
+ sendSessionEnd(session) {
404
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] finalize started`);
405
+ // Flush all pending data first, then finalize
406
+ Promise.all([this._flushFrames(), this._flushEvents(), this._flushNetwork(), this.flushErrors(), this._flushPerformance()]).then(() => {
407
+ // One-row merge: if this recording adopted the always-on analytics id,
408
+ // the analytics lane owns the session's lifecycle and finalizes it on
409
+ // background (finalizeAnalyticsSession). Finalizing here would close the
410
+ // shared row mid-foreground and orphan later track() events — so flush
411
+ // (above) but skip finalize. Legacy/distinct-id sessions finalize normally.
412
+ if (this.config.clientKey && session.sessionId === this._analyticsSessionId) {
413
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] recording ended; analytics session ${session.sessionId} stays open`);
414
+ return;
415
+ }
416
+ // Quota: a session whose start was refused has no row — nothing to finalize.
417
+ if (this._refusedSessionIds.has(session.sessionId)) return;
418
+ const finalizeEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_FINALIZE(session.sessionId) : `/replay/sessions/${session.sessionId}/finalize`;
419
+ return this._post(finalizeEndpoint, {
420
+ endedAt: session.endedAt ?? Date.now(),
421
+ durationMs: session.endedAt ? session.endedAt - session.startedAt : Date.now() - session.startedAt,
422
+ totalFrames: session.artifactIndex?.frameRefs?.length ?? 0,
423
+ totalEvents: session.artifactIndex?.eventRefs?.length ?? 0,
424
+ finalState: session.status === 'timeout' ? 'timeout' : 'completed'
425
+ });
426
+ }).then(() => {
427
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] finalize success`);
428
+ }).catch(() => {
429
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] finalize failure`);
430
+ }).finally(() => {
431
+ this._stopFlushTimer();
432
+ // Keep the row-ready gate OPEN for a merged analytics session. The analytics
433
+ // lane still owns it (not finalized on background — the session spans the
434
+ // app-switch), so a foreground restart re-adopts the SAME id and must keep
435
+ // flushing frames/events. Dropping the gate here would 404-buffer everything
436
+ // after the first background cycle. Only clear it for a distinct/legacy
437
+ // recording id that was actually finalized above.
438
+ if (!(this.config.clientKey && session.sessionId === this._analyticsSessionId)) {
439
+ this._startedSessionIds.delete(session.sessionId);
440
+ }
441
+ // Session end: drain any durably-stored telemetry from the native outbox.
442
+ this._triggerNativeFlush();
443
+ __DEV__ && _internalLogger.logger.debug(`[BackendSessionAdapter] Session finalized: ${session.sessionId}`);
444
+ });
445
+ }
446
+
447
+ // ─── Analytics Lane (isolated from recording) ───────────────────────
448
+
449
+ /** Public: the always-on analytics session id, or null before it opens. */
450
+ get analyticsSessionId() {
451
+ return this._analyticsSessionId;
452
+ }
453
+
454
+ /**
455
+ * Open the always-on analytics ingestion session. Sets the analytics id,
456
+ * starts the analytics flush timer, replays any pre-ready buffered events,
457
+ * and POSTs INGESTION_START. Mirrors sendSessionStart's body builder but
458
+ * never touches currentSessionId (recording lane).
459
+ */
460
+ async startAnalyticsSession(session) {
461
+ if (this.destroyed) return;
462
+ // Quota: a `sessions` block stops NEW sessions. No id is opened, so track() events buffer
463
+ // (bounded, drop-oldest) and recording does not start; a session that is ALREADY open is
464
+ // untouched and keeps its other lanes. SDKBootstrapper opens one when the block lifts.
465
+ if (_quotaState.quotaState.isBlocked('sessions')) {
466
+ _internalLogger.logger.warn(`[ScaleBun Analytics] session not started — sessions quota exceeded`);
467
+ return;
468
+ }
469
+ this._analyticsSessionId = session.sessionId;
470
+ // Native crash records must name a session the backend has a row for. This is that
471
+ // session; publishing anything else (the event tracker's local `sess-` id did) makes
472
+ // every recovered crash 404 and get dropped.
473
+ this._publishNativeSession(session.sessionId);
474
+ // A new session gets its own capture-failure report. The latch bounds reports WITHIN a
475
+ // session; left standing across sessions it silenced every session after the first in the
476
+ // app process, which is the one place this differs from the web SDK — that pipeline is
477
+ // constructed per page and its session id is readonly, so its latch cannot outlive a session.
478
+ this._captureFailureReported = false;
479
+ this._startAnalyticsFlushTimer();
480
+
481
+ // Replay pre-ready buffered events now that the id exists. sessionId is
482
+ // stamped at flush time, so buffered events need no rewrite.
483
+ this._flushAnalyticsEvents().catch(() => {});
484
+ const startEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_START : '/replay/sessions';
485
+ try {
486
+ const nativeInfo = await _bridgeAdapter.bridgeAdapter.getDeviceInfo();
487
+ const m = session.metadata;
488
+ this._cachedAppVersion = nativeInfo?.appVersion ?? m?.appVersion ?? null;
489
+ const payload = {
490
+ sessionId: session.sessionId,
491
+ deviceId: session.deviceId,
492
+ startedAt: session.startedAt,
493
+ idleThresholdMs: session.idleThresholdMs,
494
+ device: {
495
+ platform: nativeInfo?.platform ?? m?.platform,
496
+ osVersion: nativeInfo?.osVersion ?? m?.osVersion,
497
+ deviceModel: nativeInfo?.deviceModel ?? m?.deviceModel,
498
+ screenWidth: m?.screenWidth,
499
+ screenHeight: m?.screenHeight,
500
+ appName: nativeInfo?.appName ?? m?.appName,
501
+ appVersion: nativeInfo?.appVersion ?? m?.appVersion,
502
+ bundleId: nativeInfo?.bundleId ?? m?.bundleId,
503
+ sdkVersion: _version.SDK_VERSION,
504
+ releaseStage: m?.buildType,
505
+ environment: m?.buildType
506
+ }
507
+ };
508
+ try {
509
+ await this._post(startEndpoint, payload);
510
+ } catch (err) {
511
+ // Quota: 402 quota_exceeded(sessions) — drop the session, never retry the start.
512
+ if (BackendSessionAdapter._isQuotaRefusal(err, 'sessions')) {
513
+ this._refuseSession(session.sessionId);
514
+ throw err;
515
+ }
516
+ // Offline-start recovery: keep the payload for the flush-tick retry loop.
517
+ // Without this a session that started with no connectivity NEVER opened its
518
+ // row-ready gate — frames/events re-buffered for the life of the process and
519
+ // only an app restart recovered it, even after the network came back.
520
+ if (!BackendSessionAdapter._isPermanentHttpError(err)) {
521
+ this._pendingStartPayloads.set(session.sessionId, payload);
522
+ }
523
+ throw err;
524
+ }
525
+ // §11: row exists. When recording adopts this analytics id (one-row merge),
526
+ // this also releases the recording child-lane gate (_sessionRowReady).
527
+ this._startedSessionIds.add(session.sessionId);
528
+ this._pendingStartPayloads.delete(session.sessionId);
529
+ // Row is confirmed — replay any events buffered while START was in flight.
530
+ // The pre-ack flush at the top of this method is gated off until now, so
531
+ // this is what actually drains the pre-ready buffer (no premature 404).
532
+ this._flushAnalyticsEvents().catch(() => {});
533
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Analytics] session start upload success: ${session.sessionId}`);
534
+ } catch {
535
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Analytics] session start upload failure`);
536
+ }
537
+ }
538
+
539
+ /**
540
+ * Re-attempt any INGESTION_START whose POST failed (offline app launch). Runs on every
541
+ * flush tick of BOTH lanes — cheap no-op when the map is empty, single-flight guarded.
542
+ * On ack: opens the row-ready gate and drains whichever lane the id belongs to, which is
543
+ * exactly what un-sticks a replay that started offline once connectivity returns.
544
+ */
545
+ _retryPendingStarts() {
546
+ if (this.destroyed || this._startRetryInFlight || this._pendingStartPayloads.size === 0) return;
547
+ // A start re-attempted while `sessions` is blocked would only be refused; keep it for later.
548
+ if (_quotaState.quotaState.isBlocked('sessions')) return;
549
+ const entry = this._pendingStartPayloads.entries().next().value;
550
+ if (!entry) return;
551
+ const [sessionId, payload] = entry;
552
+ // A stale pending start for a session that already ended AND is not the open analytics
553
+ // session would create a row nothing will ever finalize — drop it instead.
554
+ const stillRelevant = sessionId === this.currentSessionId || sessionId === this._analyticsSessionId;
555
+ if (!stillRelevant) {
556
+ this._pendingStartPayloads.delete(sessionId);
557
+ return;
558
+ }
559
+ this._startRetryInFlight = true;
560
+ const startEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_START : '/replay/sessions';
561
+ this._post(startEndpoint, payload).then(() => {
562
+ this._startedSessionIds.add(sessionId);
563
+ this._pendingStartPayloads.delete(sessionId);
564
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] deferred session start delivered: ${sessionId}`);
565
+ if (sessionId === this._analyticsSessionId) {
566
+ this._flushAnalyticsEvents().catch(() => {});
567
+ }
568
+ }).catch(err => {
569
+ if (BackendSessionAdapter._isQuotaRefusal(err, 'sessions')) {
570
+ this._refuseSession(sessionId);
571
+ } else if (BackendSessionAdapter._isPermanentHttpError(err)) {
572
+ this._pendingStartPayloads.delete(sessionId); // will never succeed — stop retrying
573
+ }
574
+ }).finally(() => {
575
+ this._startRetryInFlight = false;
576
+ });
577
+ }
578
+
579
+ /**
580
+ * Enqueue a user track() event. Buffers (bounded, drop-oldest) when the
581
+ * analytics session is not yet open; flushes at >=20 once it is.
582
+ */
583
+ trackEvent(event) {
584
+ if (this.destroyed) return;
585
+ // Quota: an `events` block stops capture at the source — the server would refuse it.
586
+ if (_quotaState.quotaState.isBlocked('events')) return;
587
+ this.pendingAnalyticsEvents.push(event);
588
+ const overflow = this.pendingAnalyticsEvents.length - BackendSessionAdapter.ANALYTICS_BUFFER_CAP;
589
+ if (overflow > 0) {
590
+ this.pendingAnalyticsEvents.splice(0, overflow); // drop oldest
591
+ }
592
+ if (this._analyticsSessionId && this.pendingAnalyticsEvents.length >= 20) {
593
+ this._flushAnalyticsEvents().catch(() => {});
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Emit an app-lifecycle boundary marker (APP_BACKGROUND / APP_FOREGROUND) onto
599
+ * the analytics lane so the dashboard can shade the backgrounded span on the
600
+ * replay timeline and exclude it from the active duration. Timestamp is captured
601
+ * at the transition (accurate even if delivery is deferred until the next
602
+ * foreground on iOS). No-op when no analytics session is open. The sessionId is
603
+ * (re)stamped at flush time, so passing the current id here is only a placeholder.
604
+ */
605
+ emitLifecycleMarker(type, payload) {
606
+ if (this.destroyed || !this._analyticsSessionId) return;
607
+ this.trackEvent({
608
+ eventId: this._clientId(),
609
+ sessionId: this._analyticsSessionId,
610
+ ts: Date.now(),
611
+ type,
612
+ source: 'sdk',
613
+ ...(payload ? {
614
+ payload
615
+ } : {})
616
+ });
617
+ }
618
+
619
+ /**
620
+ * Public: force-flush queued analytics events immediately. Called on
621
+ * APP_BACKGROUND so the lifecycle marker (and any buffered track events) reach
622
+ * the backend before the OS suspends the flush timer. Best-effort on iOS, where
623
+ * the POST may not resolve until the next foreground — the events stay buffered
624
+ * with accurate timestamps and flush then.
625
+ */
626
+ flushAnalytics() {
627
+ return this._flushAnalyticsEvents();
628
+ }
629
+
630
+ /**
631
+ * Flush queued analytics events. Mirrors _flushEvents (recording) byte for
632
+ * byte on the wire map, but gates on _analyticsSessionId and STAMPS that id
633
+ * into every event so endpoint id == body sessionId by construction.
634
+ */
635
+ async _flushAnalyticsEvents(forSessionId) {
636
+ // finalize clears _analyticsSessionId synchronously, then passes the
637
+ // captured id here so the final drain still targets the right session.
638
+ const sessionId = forSessionId ?? this._analyticsSessionId;
639
+ if (this.pendingAnalyticsEvents.length === 0 || !sessionId) return;
640
+
641
+ // Quota, send-time: anything queued before the block (or for a refused session) is purged,
642
+ // never sent — a refused batch must not be retried.
643
+ if (_quotaState.quotaState.isBlocked('events') || this._refusedSessionIds.has(sessionId)) {
644
+ this.pendingAnalyticsEvents = [];
645
+ return;
646
+ }
647
+
648
+ // Row-confirmed gate (mirrors the recording lanes' _sessionRowReady). The
649
+ // INGESTION_START create is async and can fail/lag; flushing events before
650
+ // the row exists 404s the child write and re-buffers forever. Keep events
651
+ // queued (bounded, drop-oldest via trackEvent) until START is acked.
652
+ if (!this._startedSessionIds.has(sessionId)) return;
653
+ const batch = this.pendingAnalyticsEvents.splice(0, 20);
654
+ const events = batch.map(e => this._leanEvent(e, sessionId));
655
+ try {
656
+ const eventsEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_EVENTS(sessionId) : `/replay/sessions/${sessionId}/events`;
657
+ await this._post(eventsEndpoint, {
658
+ events
659
+ });
660
+ __DEV__ && _internalLogger.logger.debug(`[BackendSessionAdapter] Flushed ${batch.length} analytics event(s)`);
661
+ } catch (err) {
662
+ if (!BackendSessionAdapter._isPermanentHttpError(err)) this.pendingAnalyticsEvents.unshift(...batch);
663
+ }
664
+ }
665
+
666
+ /** Flush remaining analytics events, finalize the session, stop its timer. */
667
+ async finalizeAnalyticsSession() {
668
+ const sessionId = this._analyticsSessionId;
669
+ if (!sessionId) return;
670
+
671
+ // Clear the id and stop the timer SYNCHRONOUSLY, before any await. On iOS
672
+ // the app is suspended on background and the finalize fetch below never
673
+ // resolves until the next foreground, so the old `finally` that nulled the
674
+ // id never ran before the resume guard (SDKBootstrapper: `state==='active'
675
+ // && !adapter.analyticsSessionId`) evaluated → guard stayed false → no new
676
+ // session opened. Clearing up front makes the guard always see null after
677
+ // background, so resume re-opens a fresh analytics session every cycle.
678
+ this._analyticsSessionId = null;
679
+ this._stopAnalyticsFlushTimer();
680
+
681
+ // Mark as pending-finalize BEFORE any await. On iOS the app suspends right
682
+ // after backgrounding, so the awaited _post below may never resolve. Setting
683
+ // the marker synchronously guarantees the next foreground can reconcile-close
684
+ // this id even though the background POST was suspended. Cleared only on a
685
+ // confirmed ACK (below), so a suspended POST leaves the marker in place.
686
+ this._pendingFinalizeId = sessionId;
687
+ try {
688
+ // Pass the captured id explicitly — the field is already cleared.
689
+ await this._flushAnalyticsEvents(sessionId);
690
+ const finalizeEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_FINALIZE(sessionId) : `/replay/sessions/${sessionId}/finalize`;
691
+ await this._post(finalizeEndpoint, {
692
+ endedAt: Date.now()
693
+ });
694
+ // Confirmed delivered → drop the marker so the foreground reconciler does
695
+ // NOT re-finalize this id (first half of the no-double-finalize guard).
696
+ if (this._pendingFinalizeId === sessionId) this._pendingFinalizeId = null;
697
+ __DEV__ && _internalLogger.logger.debug(`[BackendSessionAdapter] Analytics session finalized: ${sessionId}`);
698
+ } catch {
699
+ // Leave the marker set — foreground reconciliation will retry the close.
700
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Analytics] finalize failure (will reconcile on foreground): ${sessionId}`);
701
+ }
702
+ }
703
+
704
+ /**
705
+ * Close any analytics session left open by a suspended background finalize.
706
+ * Called on the NEXT foreground (SDKBootstrapper app-state 'active') BEFORE a new
707
+ * analytics session is opened. JS is live on foreground, so this POST completes.
708
+ * No-op when there is no pending id. Idempotent with the background finalize: the
709
+ * marker is cleared on a confirmed background ACK, so this only fires when the
710
+ * background POST was NOT confirmed; if that suspended POST later also lands, the
711
+ * server must treat a repeat INGESTION_FINALIZE for the same id as a no-op.
712
+ */
713
+ async reconcilePendingFinalize() {
714
+ const sessionId = this._pendingFinalizeId;
715
+ if (!sessionId) return;
716
+ if (this._refusedSessionIds.has(sessionId)) {
717
+ this._pendingFinalizeId = null;
718
+ return;
719
+ }
720
+ try {
721
+ const finalizeEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_FINALIZE(sessionId) : `/replay/sessions/${sessionId}/finalize`;
722
+ await this._post(finalizeEndpoint, {
723
+ endedAt: Date.now()
724
+ });
725
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Analytics] reconciled finalize on foreground: ${sessionId}`);
726
+ } catch {
727
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Analytics] foreground reconcile finalize failed: ${sessionId}`);
728
+ } finally {
729
+ // Clear after a foreground attempt regardless of outcome: JS is live here
730
+ // so a transient network error is rare, and the server-side idle-close
731
+ // backstop covers any still-orphaned row. Bounds total finalizes per id to
732
+ // at most 2 (suspended-bg + foreground) — never a per-foreground loop.
733
+ if (this._pendingFinalizeId === sessionId) this._pendingFinalizeId = null;
734
+ }
735
+ }
736
+ _startAnalyticsFlushTimer() {
737
+ this._stopAnalyticsFlushTimer();
738
+ this.analyticsFlushTimer = setInterval(() => {
739
+ this._retryPendingStarts();
740
+ this._flushAnalyticsEvents().catch(() => {});
741
+ // Errors ride the analytics session when nothing is recording; without this tick they
742
+ // waited on the recording timer, which does not run when replay is off.
743
+ if (!this.currentSessionId) this.flushErrors().catch(() => {});
744
+ }, this.config.flushIntervalMs);
745
+ }
746
+ _stopAnalyticsFlushTimer() {
747
+ if (this.analyticsFlushTimer) {
748
+ clearInterval(this.analyticsFlushTimer);
749
+ this.analyticsFlushTimer = null;
750
+ }
751
+ }
752
+ sendEvent(event) {
753
+ if (this.destroyed) return;
754
+ // Quota: journey events are `events` (same /events endpoint as track()).
755
+ if (_quotaState.quotaState.isBlocked('events') || this._currentSessionRefused()) return;
756
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] event recorded`);
757
+ __DEV__ && _internalLogger.logger.info(` eventId: ${event.eventId}`);
758
+ __DEV__ && _internalLogger.logger.info(` type: ${event.type}`);
759
+ __DEV__ && _internalLogger.logger.info(` timestamp: ${event.ts}`);
760
+ __DEV__ && _internalLogger.logger.info(` sessionId: ${event.sessionId}`);
761
+ this.pendingEvents.push(event);
762
+ if (this.pendingEvents.length >= 20) {
763
+ this._flushEvents().catch(() => {});
764
+ }
765
+ }
766
+ sendFrame(frame, imageData) {
767
+ if (this.destroyed) return;
768
+ // Quota: SessionManager stops capture on a `frames` block; this refuses any frame that was
769
+ // already in flight when the block arrived.
770
+ if (_quotaState.quotaState.isBlocked('frames') || this._currentSessionRefused()) return;
771
+ __DEV__ && _internalLogger.logger.info(`[ScaleBun Replay] frame captured`);
772
+ __DEV__ && _internalLogger.logger.info(` frameId: ${frame.frameId}`);
773
+ __DEV__ && _internalLogger.logger.info(` timestamp: ${frame.ts}`);
774
+ __DEV__ && _internalLogger.logger.info(` sessionId: ${frame.sessionId}`);
775
+ __DEV__ && _internalLogger.logger.info(` width/height: ${frame.width}x${frame.height}`);
776
+ this.pendingFrames.push({
777
+ frame,
778
+ imageData
779
+ });
780
+ if (this.pendingFrames.length >= this.config.batchSize) {
781
+ this._flushFrames().catch(() => {});
782
+ }
783
+ }
784
+
785
+ // ─── SaaS Ingestion Queues ──────────────────────────────────────────
786
+
787
+ queueNetworkRequest(data) {
788
+ if (this.destroyed || !this.config.clientKey) return;
789
+ // Quota: `networkRequests` block (NetworkFeature also gates before calling this).
790
+ if (_quotaState.quotaState.isBlocked('networkRequests') || this._currentSessionRefused()) return;
791
+ // clientId: stable per-row idempotency key so a mobile retry of the same
792
+ // batch upserts instead of inserting duplicates server-side.
793
+ this.pendingNetworkRequests.push({
794
+ ...data,
795
+ clientId: this._clientId(),
796
+ timestamp: new Date().toISOString()
797
+ });
798
+ if (this.pendingNetworkRequests.length >= 20) {
799
+ this._flushNetwork().catch(() => {});
800
+ }
801
+ }
802
+
803
+ // D8/D9/D12: this signature was {type, message, stack, screenName} — four
804
+ // fields — while the backend row and every dashboard surface were already
805
+ // built for the rest. The consequences of the four-field bottleneck:
806
+ // regression detection permanently false (no appVersion on any mobile
807
+ // error), Issue.primaryPlatform NULL for every RN crash (no platform), and
808
+ // native crash signal/thread/exception-type discarded one call before the
809
+ // wire (no metadata). The receiver needed nothing; only this signature did.
810
+ /** App version resolved at session start; errors stamp it so regression
811
+ * detection has a version to work with (D8). Null until a session starts. */
812
+ _cachedAppVersion = null;
813
+ _appVersion() {
814
+ return this._cachedAppVersion ?? undefined;
815
+ }
816
+ queueError(data) {
817
+ if (this.destroyed || !this.config.clientKey) return;
818
+ this.pendingErrors.push({
819
+ ...data,
820
+ // The session already knows the app version — never make a crash
821
+ // reporter's callers supply what the transport can fill itself.
822
+ appVersion: data.appVersion ?? this._appVersion(),
823
+ // A recovered crash brings its OWN clientId — the stable id written when the crash was
824
+ // first persisted. Generating a fresh one here would mint a new identity on every retry
825
+ // and defeat the unique constraint the backend dedupes with.
826
+ clientId: data.clientId ?? this._clientId(),
827
+ timestamp: new Date().toISOString()
828
+ });
829
+ if (this.pendingErrors.length >= 10) {
830
+ this.flushErrors().catch(() => {});
831
+ }
832
+ }
833
+
834
+ /**
835
+ * Public: force-flush queued errors immediately (handled-error delivery).
836
+ *
837
+ * @returns whether the batch was DURABLY accepted. Crash recovery deletes its on-disk record on
838
+ * this answer, so it must reflect the backend and not merely that the call returned.
839
+ */
840
+ flushErrors() {
841
+ /*
842
+ * SERIALISED, and an empty queue answers with the flush that emptied it.
843
+ *
844
+ * BackendErrorsSink.deliver() queues a crash and immediately calls flushErrors(); the crash
845
+ * drain then calls flushErrors() again to ask whether the crash was delivered. The first
846
+ * call had already taken the batch (the splice is synchronous), so the second found an
847
+ * empty queue and answered `false` — every recovered crash stayed on disk, the drain stopped
848
+ * after one record, and the crash was re-sent every launch forever. Chaining the calls means
849
+ * the question waits for the flush actually carrying the crash and gets ITS answer.
850
+ *
851
+ * Nothing can be delivered without a clientKey (queueError drops it), so that is never
852
+ * "delivered", however empty the queue.
853
+ */
854
+ const run = this._errorsFlushTail.then(previousOk => {
855
+ if (this.destroyed || !this.config.clientKey) return false;
856
+ if (this.pendingErrors.length === 0) return previousOk;
857
+ return this._flushErrors();
858
+ });
859
+ this._errorsFlushTail = run.catch(() => false);
860
+ return run;
861
+ }
862
+
863
+ /** The last errors flush — the answer an empty queue gives. Starts true: nothing owed. */
864
+ _errorsFlushTail = Promise.resolve(true);
865
+
866
+ /**
867
+ * Public: flush ALL recording-lane queues immediately. Called on APP_BACKGROUND
868
+ * so the server-side reaper computes endedAt from the freshest data even when
869
+ * the OS suspends the flush timer right after backgrounding.
870
+ */
871
+ async flushAll() {
872
+ await Promise.all([this._flushFrames(), this._flushEvents(), this._flushNetwork(), this.flushErrors(), this._flushPerformance(), this._flushLogs()]);
873
+ // Background trigger: close native segments + drain the native uploader so a
874
+ // suspended JS context doesn't strand durably-stored telemetry.
875
+ this._triggerNativeFlush();
876
+ }
877
+ queuePerformanceMetric(data) {
878
+ if (this.destroyed || !this.config.clientKey) {
879
+ _internalLogger.logger.warn('[perf.queue] dropped — no clientKey or adapter destroyed', {
880
+ type: data.type,
881
+ destroyed: this.destroyed,
882
+ hasClientKey: !!this.config.clientKey
883
+ });
884
+ return;
885
+ }
886
+ if (this._currentSessionRefused()) return; // no row will ever exist for this session
887
+ this.pendingPerformanceMetrics.push({
888
+ ...data,
889
+ clientId: this._clientId(),
890
+ timestamp: new Date().toISOString()
891
+ });
892
+ _internalLogger.logger.debug('[perf.trace] queue enqueued performance metric', {
893
+ type: data.type,
894
+ pending: this.pendingPerformanceMetrics.length
895
+ });
896
+ if (this.pendingPerformanceMetrics.length >= 20) {
897
+ this._flushPerformance().catch(() => {});
898
+ }
899
+ }
900
+
901
+ /**
902
+ * Queue one log line onto the platform-agnostic logs lane. Mirrors
903
+ * queueNetworkRequest: drop when not in SaaS mode (no clientKey) or destroyed,
904
+ * batch-flush at LOGS_BATCH_SIZE. The producer supplies source/platform/level —
905
+ * this method makes NO assumptions about them, so a native producer reuses it as-is.
906
+ */
907
+ queueLog(data) {
908
+ if (this.destroyed || !this.config.clientKey) return;
909
+ if (this._currentSessionRefused()) return; // no row will ever exist for this session
910
+ this.pendingLogs.push({
911
+ level: data.level,
912
+ message: data.message,
913
+ source: data.source,
914
+ platform: data.platform,
915
+ timestamp: data.timestamp ?? new Date().toISOString(),
916
+ ...(data.metadata ? {
917
+ metadata: data.metadata
918
+ } : {})
919
+ });
920
+ if (this.pendingLogs.length >= BackendSessionAdapter.LOGS_BATCH_SIZE) {
921
+ this._flushLogs().catch(() => {});
922
+ }
923
+ }
924
+
925
+ /** Stable client-generated idempotency id (matches journeyManager id scheme). */
926
+ _clientId() {
927
+ return `${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 10)}`;
928
+ }
929
+
930
+ // ─── Native outbox routing ──────────────────────────────────────────
931
+
932
+ /**
933
+ * Hand a telemetry batch to the durable native outbox. Returns true if native
934
+ * accepted it (native uploader now owns delivery — caller must NOT also POST).
935
+ * Returns false on any miss (flag off, no session, old binary, native error) so
936
+ * the caller falls back to the JS HTTP path with the batch intact.
937
+ */
938
+ async _enqueueNative(lane, records,
939
+ /**
940
+ * Session to enqueue UNDER. Defaults to the current one, which is right for every live lane.
941
+ *
942
+ * A recovered native crash must override it. Without this parameter the outbox stamped
943
+ * `currentSessionId` on everything, so with the outbox enabled a recovered crash was filed
944
+ * against the session created after the restart — the exact defect the routing above exists
945
+ * to prevent, reappearing through a path that routing never touched.
946
+ */
947
+ targetSessionId) {
948
+ const sessionId = targetSessionId || this.currentSessionId;
949
+ if (!this.nativeOutboxEnabled || !sessionId) return false;
950
+ const native = (0, _nativeModule.getReplaySdkNative)();
951
+ if (!native?.enqueueOutboxRecords) return false;
952
+ if (!(await this._ensureNativeOutbox(native))) return false;
953
+ try {
954
+ return await native.enqueueOutboxRecords(lane, sessionId, records);
955
+ } catch {
956
+ return false;
957
+ }
958
+ }
959
+
960
+ /**
961
+ * Bootstrap the native outbox once. In SaaS mode the native module's full
962
+ * `initialize()` (which builds a SessionManager) is never called, so the
963
+ * NativeOutbox/uploader would not exist. `initOutbox` constructs only the
964
+ * durable outbox from clientKey/apiBaseUrl/flags. Idempotent + cached so it
965
+ * runs at most once; resolves false on old binaries (caller falls back to JS).
966
+ */
967
+ _ensureNativeOutbox(native) {
968
+ if (this._nativeOutboxReady) return this._nativeOutboxReady;
969
+ if (!native.initOutbox) return Promise.resolve(false);
970
+ // Quota, BEFORE initOutbox: initOutbox builds the uploader and drains immediately. The module
971
+ // remembers lane blocks set before the uploader exists and applies them when it is built, and
972
+ // bridge calls to one module are processed in order — so persisted blocks are in place before
973
+ // the first drain. The 402 listener is attached first for the same reason.
974
+ (0, _nativeQuota.subscribeNativeQuotaEvents)();
975
+ (0, _nativeQuota.syncNativeQuotaLanes)();
976
+ this._nativeOutboxReady = native.initOutbox({
977
+ ...this.config.nativeOutboxTuning,
978
+ clientKey: this.config.clientKey,
979
+ apiBaseUrl: this.config.endpoint,
980
+ enableNativeFileOutbox: true,
981
+ nativeOutboxUploadEnabled: true,
982
+ useObjectStorageForFrames: this.config.useObjectStorage === true
983
+ }).catch(() => false);
984
+ return this._nativeOutboxReady;
985
+ }
986
+
987
+ /**
988
+ * Public readiness gate for the native frame outbox path (used by the frame
989
+ * capture adapter). Resolves true only when both outbox flags are on, a session
990
+ * exists, the native binary exposes the bridge, and `initOutbox` succeeded.
991
+ * Resolves false otherwise so the frame pipeline falls back to `captureScreenshot`.
992
+ */
993
+ ensureNativeOutboxReady() {
994
+ if (!this.nativeOutboxEnabled || !this.currentSessionId) return Promise.resolve(false);
995
+ const native = (0, _nativeModule.getReplaySdkNative)();
996
+ if (!native?.captureScreenshotToFile) return Promise.resolve(false);
997
+ return this._ensureNativeOutbox(native);
998
+ }
999
+
1000
+ /** Best-effort: close native segments and drain the native uploader. Never throws. */
1001
+ _triggerNativeFlush() {
1002
+ if (!this.nativeOutboxEnabled) return;
1003
+ try {
1004
+ (0, _nativeModule.getReplaySdkNative)()?.flushNativeOutbox?.().catch(() => {});
1005
+ } catch {
1006
+ /* old binary / unavailable — ignore */
1007
+ }
1008
+ }
1009
+
1010
+ // ─── Lifecycle ──────────────────────────────────────────────────────
1011
+
1012
+ destroy() {
1013
+ this.destroyed = true;
1014
+ this._quotaUnsub?.();
1015
+ this._quotaUnsub = null;
1016
+ this._nativeRefusedUnsub?.();
1017
+ this._nativeRefusedUnsub = null;
1018
+ this._refusedSessionIds.clear();
1019
+ this._onSessionRefused = null;
1020
+ this._stopFlushTimer();
1021
+ this._stopAnalyticsFlushTimer();
1022
+ this.pendingFrames = [];
1023
+ this.pendingEvents = [];
1024
+ this.pendingAnalyticsEvents = [];
1025
+ this.pendingNetworkRequests = [];
1026
+ this.pendingErrors = [];
1027
+ this.pendingPerformanceMetrics = [];
1028
+ this.currentSessionId = null;
1029
+ this._analyticsSessionId = null;
1030
+ this._pendingFinalizeId = null;
1031
+ this._startedSessionIds.clear();
1032
+ this._pendingStartPayloads.clear();
1033
+ }
1034
+
1035
+ // ─── Internal: Flush ────────────────────────────────────────────────
1036
+
1037
+ /** Wire metadata for a frame — real format/sizeBytes from native (no hardcoded literals). */
1038
+ _frameMeta(b) {
1039
+ const f = b.frame;
1040
+ const format = f.format ?? 'jpeg';
1041
+ // P2-5: a JSON frame-delta carries an application content type, not an image MIME.
1042
+ const isJsonDelta = f.frameType === 'delta' && format === 'scalebun-framedelta';
1043
+ const contentType = isJsonDelta ? 'application/x-scalebun-framedelta+json' : `image/${format}`;
1044
+ return {
1045
+ frameId: f.frameId,
1046
+ sessionId: f.sessionId,
1047
+ timestamp: f.ts,
1048
+ // P2-0: real monotonic per-session sequence (was hardcoded 0 server-side).
1049
+ sequenceNumber: f.sequenceNumber,
1050
+ // P2-5: keyframe|delta + base reference for dashboard reconstruction.
1051
+ frameType: f.frameType,
1052
+ baseFrameId: f.baseFrameId,
1053
+ screenName: f.screen ?? '',
1054
+ trigger: f.captureReason ?? '',
1055
+ format,
1056
+ contentType,
1057
+ width: f.width,
1058
+ height: f.height,
1059
+ sizeBytes: f.byteSize ?? 0,
1060
+ masked: f.redactionState === 'full',
1061
+ // Visible scroll containers at capture time, sanitized at the native boundary. Omitted
1062
+ // (not sent as []) when not measured, so the server stores null rather than "none".
1063
+ ...(f.scrollState ? {
1064
+ scrollState: f.scrollState
1065
+ } : {})
1066
+ };
1067
+ }
1068
+
1069
+ /**
1070
+ * P2-4: build the lean (v2) wire payload for one event. Drops redundant/empty
1071
+ * fields at the source while NEVER dropping fields the dashboard/analytics depend
1072
+ * on (eventId, type, timestamp, sessionId). `label` is omitted when it equals
1073
+ * `type` (the server defaults it back). `screenName` omitted when empty. `data`
1074
+ * is pruned of null/''/undefined leaves (one level). The `v:2` marker lets the
1075
+ * backend/dashboard pick the lean decoder; legacy (v1/no-marker) events still
1076
+ * decode unchanged. See P2_CONTRACT.md §4.
1077
+ */
1078
+ _leanEvent(e, sessionId) {
1079
+ const out = {
1080
+ v: 2,
1081
+ eventId: e.eventId,
1082
+ sessionId,
1083
+ type: e.type,
1084
+ timestamp: e.ts
1085
+ };
1086
+ const screen = e.screen ?? '';
1087
+ if (screen) out.screenName = screen;
1088
+ const label = e.subtype ?? e.type;
1089
+ if (label && label !== e.type) out.label = label;
1090
+ /**
1091
+ * THIS MAPPER ENUMERATES. Every field the wire carries has to be named here, so a field
1092
+ * added to JourneyEvent and not added here is dropped silently — no error, no warning, and
1093
+ * nothing downstream can tell the difference between "the SDK did not send it" and "the
1094
+ * user never produced it". `frameId` was exactly that case: the column, the DTO field and
1095
+ * the ingest write all existed already, and the value died on this line.
1096
+ *
1097
+ * Omitted when absent rather than sent as null, matching how screenName and label behave
1098
+ * above — the v2 lean shape drops empty fields and the server reconstructs them.
1099
+ */
1100
+ if (e.frameId) out.frameId = e.frameId;
1101
+ const data = this._pruneEmpty(e.payload);
1102
+ if (data && Object.keys(data).length > 0) out.data = data;
1103
+ return out;
1104
+ }
1105
+
1106
+ /** Shallow-prune null/undefined/'' leaves from a payload object. */
1107
+ _pruneEmpty(payload) {
1108
+ if (!payload || typeof payload !== 'object') return undefined;
1109
+ const src = payload;
1110
+ const out = {};
1111
+ for (const k of Object.keys(src)) {
1112
+ const v = src[k];
1113
+ if (v === null || v === undefined || v === '') continue;
1114
+ out[k] = v;
1115
+ }
1116
+ return out;
1117
+ }
1118
+ async _flushFrames() {
1119
+ if (this.pendingFrames.length === 0 || !this.currentSessionId) return;
1120
+ // Quota, send-time: frames queued before a `frames` block are DISCARDED — including the
1121
+ // final flush on session end — and the cross-kill snapshot with them.
1122
+ if (_quotaState.quotaState.isBlocked('frames')) {
1123
+ this.pendingFrames = [];
1124
+ this._clearPersistedFrames();
1125
+ return;
1126
+ }
1127
+ if (this._currentSessionRefused()) {
1128
+ this.pendingFrames = [];
1129
+ return;
1130
+ }
1131
+ // §11: hold frames until the session_start row is confirmed (else 404). Data stays
1132
+ // buffered; the flush timer retries once the row lands.
1133
+ if (!this._sessionRowReady()) return;
1134
+ const sessionId = this.currentSessionId;
1135
+ const batch = this.pendingFrames.splice(0, this.config.batchSize);
1136
+ /**
1137
+ * Chunked, because `config.batchSize` is HOST-CONFIGURABLE and the server's 100-frame cap is
1138
+ * not. A host raising it past 100 previously turned every frame flush into a permanent 4xx —
1139
+ * silent, total replay loss for that app, from a config value that looks like a tuning knob.
1140
+ *
1141
+ * A permanent rejection is reported and abandoned inside `_postFramesChunked`; what comes
1142
+ * back is only what is still worth another attempt.
1143
+ */
1144
+ const remaining = await this._postFramesChunked(sessionId, batch);
1145
+ if (remaining.length) {
1146
+ _internalLogger.logger.warn(`[replay.sdk.error] backend frame upload failure`, {
1147
+ count: remaining.length
1148
+ });
1149
+ this.pendingFrames.unshift(...remaining);
1150
+ return;
1151
+ }
1152
+ __DEV__ && _internalLogger.logger.debug(`[BackendSessionAdapter] Flushed ${batch.length} frame(s)`);
1153
+ // Delivered — the durable snapshot (if any) is now redundant. Clear it once
1154
+ // the in-memory buffer is fully drained so a later kill can't replay
1155
+ // already-uploaded frames.
1156
+ if (this.pendingFrames.length === 0) this._clearPersistedFrames();
1157
+ }
1158
+
1159
+ /**
1160
+ * Report frames the SDK gave up on, on the analytics lane.
1161
+ *
1162
+ * SAME EVENT NAME AS THE WEB SDK (`sdk_replay_capture_failed`) with platform-specific `reason`
1163
+ * values, so one dashboard breakdown reads both platforms without a concept-map alias — the two
1164
+ * SDKs share no event vocabulary otherwise (`$pageview` vs `screen_viewed`), and adding a second
1165
+ * name here would need an entry in `event-concepts.ts` for no benefit.
1166
+ *
1167
+ * Latched to once per session: a session whose row was never created rejects every batch, and an
1168
+ * unlatched report would emit one event per flush for as long as the app is open.
1169
+ *
1170
+ * Best-effort and never throws — a diagnostic must not be able to break the capture path it
1171
+ * describes.
1172
+ */
1173
+ _reportCaptureFailure(stage, err, frameCount) {
1174
+ if (this._captureFailureReported) return;
1175
+ this._captureFailureReported = true;
1176
+ try {
1177
+ if (!this._analyticsSessionId) return; // no lane open — nothing to attach it to
1178
+ const status = err?.status;
1179
+ this.trackEvent({
1180
+ eventId: this._clientId(),
1181
+ sessionId: this._analyticsSessionId,
1182
+ ts: Date.now(),
1183
+ type: 'sdk_replay_capture_failed',
1184
+ source: 'sdk',
1185
+ payload: {
1186
+ stage,
1187
+ // Platform-specific vocabulary. The HTTP status is the honest reason on this path:
1188
+ // RN posts to the per-lane endpoints, which surface a real status — unlike the web
1189
+ // batch fan-out, whose 200 says nothing about what it stored.
1190
+ reason: status ? `http_${status}` : 'upload_failed',
1191
+ frames: frameCount,
1192
+ platform: 'react-native'
1193
+ }
1194
+ });
1195
+ } catch {
1196
+ // Diagnostics must never break the capture path.
1197
+ }
1198
+ }
1199
+
1200
+ /**
1201
+ * Post frames in BACKEND-SAFE CHUNKS, and return the ones that were not durably accepted.
1202
+ *
1203
+ * WHY THIS EXISTS. `_postFrameBatch` issues exactly one request for whatever array it is handed,
1204
+ * and both callers could hand it more than the server's 100-frame cap — the live flush via
1205
+ * `config.batchSize`, and cross-kill recovery via the 2 MB persistence budget (~128 frames at the
1206
+ * measured 16 KB average). The result was a 4xx, classified permanent, and on the recovery path
1207
+ * that meant `_clearPersistedFrames()` — the ENTIRE cross-kill snapshot deleted because it was
1208
+ * one frame too big to send in a single request it never needed to be sent in.
1209
+ *
1210
+ * THE RETURN VALUE IS THE POINT. The caller must be able to delete exactly what landed and keep
1211
+ * exactly what did not, so this reports the survivors rather than throwing:
1212
+ * - chunk accepted → dropped from the result (it is durably stored)
1213
+ * - chunk PERMANENTLY refused → dropped, and reported once. Re-sending identical bytes cannot
1214
+ * succeed, and holding it would wedge every future launch on the
1215
+ * same poison chunk. Only that chunk is abandoned; the rest still
1216
+ * gets its chance, which is the whole difference from before.
1217
+ * - chunk TRANSIENTLY failed → that chunk AND every chunk after it are kept, in order. We stop
1218
+ * on the first transient failure rather than pressing on, because
1219
+ * the usual cause is the network being gone and the rest would
1220
+ * fail identically.
1221
+ */
1222
+ async _postFramesChunked(sessionId, items) {
1223
+ const chunks = (0, _batching.chunkEvents)(items, BackendSessionAdapter.MAX_FRAMES_PER_BATCH);
1224
+ for (let i = 0; i < chunks.length; i++) {
1225
+ // Re-checked per chunk: a block can arrive (from any lane or /config) mid-loop.
1226
+ if (_quotaState.quotaState.isBlocked('frames')) return [];
1227
+ try {
1228
+ await this._postFrameBatch(sessionId, chunks[i]);
1229
+ } catch (err) {
1230
+ // Quota: a refusal (402 on presign or frames) is not a capture defect and nothing
1231
+ // after it will be accepted either — abandon the whole remainder, report nothing.
1232
+ // quotaState is already set, and its listener has purged the lane.
1233
+ if (BackendSessionAdapter._isQuotaRefusal(err)) return [];
1234
+ if (BackendSessionAdapter._isPermanentHttpError(err)) {
1235
+ this._reportCaptureFailure('upload-rejected', err, chunks[i].length);
1236
+ continue;
1237
+ }
1238
+ return chunks.slice(i).flat();
1239
+ }
1240
+ }
1241
+ return [];
1242
+ }
1243
+
1244
+ /**
1245
+ * POST one frame batch under an EXPLICIT sessionId (object-storage path when
1246
+ * active, else legacy inline base64). Used by both the live flush and cross-kill
1247
+ * recovery — recovery passes the PRIOR session's id so orphaned frames relink to
1248
+ * it. Throws on transient error so the caller can re-queue; frame writes are
1249
+ * idempotent server-side (upsert by frameId), so a double-send is harmless.
1250
+ */
1251
+ async _postFrameBatch(sessionId, batch) {
1252
+ if (batch.length === 0) return;
1253
+ __DEV__ && _internalLogger.logger.info(`[replay.sdk.frame] backend frame upload started`, {
1254
+ count: batch.length
1255
+ });
1256
+
1257
+ // Object-storage path: presign → PUT bytes → post key-only metadata.
1258
+ if (this.objectStorageActive && this.config.clientKey) {
1259
+ const result = await this._uploadFramesViaStorage(sessionId, batch);
1260
+ if (result === 'sent') {
1261
+ __DEV__ && _internalLogger.logger.info(`[replay.sdk.frame] backend frame upload success (object storage)`);
1262
+ return;
1263
+ }
1264
+ // 'disabled' → storage not configured server-side; fall through to inline.
1265
+ }
1266
+
1267
+ // Legacy inline path: base64 in JSON body.
1268
+ const frames = batch.map(b => ({
1269
+ ...this._frameMeta(b),
1270
+ imageData: b.imageData
1271
+ }));
1272
+ const framesEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_FRAMES(sessionId) : `/replay/sessions/${sessionId}/frames`;
1273
+ await this._post(framesEndpoint, {
1274
+ frames
1275
+ });
1276
+ __DEV__ && _internalLogger.logger.info(`[replay.sdk.frame] backend frame upload success`);
1277
+ }
1278
+
1279
+ // ─── Cross-kill frame recovery ──────────────────────────────────────
1280
+ //
1281
+ // Frames buffer in-memory (pendingFrames) and only reach the backend on flush.
1282
+ // A process kill (swipe-away, OOM) between captures loses the last frames the
1283
+ // user saw — the iOS/Android background flush may be suspended before its POST
1284
+ // resolves. To close that gap we snapshot the unflushed buffer to the durable
1285
+ // KV store on background (survives kill), then re-upload it on the NEXT launch
1286
+ // linked to the PRIOR session id. Best-effort and never throws.
1287
+
1288
+ /**
1289
+ * Snapshot the current unflushed frame buffer to durable storage so it survives
1290
+ * a process kill. Called on APP_BACKGROUND before the (possibly-suspended)
1291
+ * network flush. Synchronous mirror write — reaches disk on the native store's
1292
+ * next coalesced flush. No-op if recovery storage is absent or nothing pending.
1293
+ */
1294
+ persistPendingFrames() {
1295
+ if (!this._frameStore || this.destroyed || !this.currentSessionId) return;
1296
+ if (this.pendingFrames.length === 0) return;
1297
+ // Quota: never snapshot frames that can never be uploaded.
1298
+ if (_quotaState.quotaState.isBlocked('frames') || this._currentSessionRefused()) return;
1299
+ try {
1300
+ const items = BackendSessionAdapter._selectFramesToPersist(this.pendingFrames);
1301
+ if (items.length === 0) return;
1302
+ this._frameStore.set(BackendSessionAdapter.PENDING_FRAMES_KEY, JSON.stringify({
1303
+ sessionId: this.currentSessionId,
1304
+ items
1305
+ }));
1306
+ } catch {
1307
+ // Serialization/storage failure must never break backgrounding.
1308
+ }
1309
+ }
1310
+
1311
+ /**
1312
+ * Choose which unflushed frames survive a process kill.
1313
+ *
1314
+ * WHY THIS IS NOT `slice(-10)`.
1315
+ *
1316
+ * The old rule kept the ten most RECENT frames, which is the wrong end of the buffer for replay.
1317
+ * A delta reconstructs against its base keyframe, so ten trailing deltas whose keyframe was
1318
+ * dropped are unplayable on their own — they arrive as `orphaned`, the state the dashboard flags
1319
+ * as a capture defect. Keeping the keyframe and fewer deltas yields a SHORTER replay; keeping the
1320
+ * deltas and no keyframe yields NO replay. That asymmetry is the whole decision.
1321
+ *
1322
+ * So: every keyframe is kept, then the most recent deltas fill whatever byte budget is left. This
1323
+ * mirrors the web SDK's `capBytes` policy (which sheds deltas first and never the keyframe), so
1324
+ * the two platforms lose the same thing under pressure instead of two different things.
1325
+ *
1326
+ * BYTES, not a frame count. A count is a poor proxy here: these carry base64 screenshots, so ten
1327
+ * frames may be 200 KB or 4 MB depending on device resolution — and this write goes to a KV store
1328
+ * on the background transition, where an oversized value is the thing most likely to fail and
1329
+ * lose the snapshot entirely. `imageData.length` is the base64 length, which is within ~1% of the
1330
+ * serialized cost and needs no extra pass.
1331
+ */
1332
+ static _selectFramesToPersist(pending) {
1333
+ const isKeyframe = b => b.frame.frameType !== 'delta';
1334
+ const size = b => b.imageData?.length ?? 0;
1335
+ const keep = [];
1336
+ let budget = BackendSessionAdapter.MAX_PERSIST_BYTES;
1337
+ /**
1338
+ * A COUNT cap as well as a byte budget.
1339
+ *
1340
+ * The byte budget is the right bound for the KV write; it is the wrong bound for the upload,
1341
+ * because the server checks a count. Recovery now chunks, so exceeding this is no longer
1342
+ * destructive — but persisting more than can ever be sent in one pass just guarantees leftover
1343
+ * work on every launch, and the frames past this point are the least valuable ones anyway.
1344
+ */
1345
+ const maxCount = BackendSessionAdapter.MAX_FRAMES_PER_BATCH;
1346
+
1347
+ // Keyframes first, newest back to oldest — a later keyframe supersedes an earlier one, so if
1348
+ // only one fits it should be the one closest to what the user last saw.
1349
+ for (let i = pending.length - 1; i >= 0; i--) {
1350
+ const b = pending[i];
1351
+ if (!isKeyframe(b)) continue;
1352
+ const cost = size(b);
1353
+ if (cost > budget || keep.length >= maxCount) continue;
1354
+ budget -= cost;
1355
+ keep.push(b);
1356
+ }
1357
+ // Then the most recent deltas with what remains.
1358
+ for (let i = pending.length - 1; i >= 0; i--) {
1359
+ const b = pending[i];
1360
+ if (isKeyframe(b)) continue;
1361
+ const cost = size(b);
1362
+ if (cost > budget || keep.length >= maxCount) continue;
1363
+ budget -= cost;
1364
+ keep.push(b);
1365
+ }
1366
+
1367
+ // Restore capture order: the backend orders frames by `sequenceNumber`, and the recovery POST
1368
+ // re-sends this array as-is.
1369
+ return keep.sort((a, b) => (a.frame.sequenceNumber ?? 0) - (b.frame.sequenceNumber ?? 0));
1370
+ }
1371
+
1372
+ /** Drop the persisted frame snapshot (after successful delivery). Never throws. */
1373
+ _clearPersistedFrames() {
1374
+ if (!this._frameStore) return;
1375
+ try {
1376
+ this._frameStore.delete(BackendSessionAdapter.PENDING_FRAMES_KEY);
1377
+ } catch {
1378
+ /* non-fatal */
1379
+ }
1380
+ }
1381
+
1382
+ /**
1383
+ * On cold launch: if a prior run left an unflushed frame snapshot, re-upload it
1384
+ * linked to that PRIOR session, then clear it. Runs independently of the new
1385
+ * session (uses the stored id + a direct POST, bypassing the row-ready gate —
1386
+ * the prior session row already exists server-side). Best-effort; on transient
1387
+ * failure the snapshot is kept for the next launch.
1388
+ */
1389
+ async recoverPersistedFrames() {
1390
+ if (!this._frameStore) return;
1391
+ // Quota: this path bypasses the live flush, so it must check the block itself. A snapshot
1392
+ // restored while `frames` is blocked is deleted, not kept for a later launch — it was
1393
+ // captured under a period whose allowance is spent.
1394
+ if (_quotaState.quotaState.isBlocked('frames')) {
1395
+ this._clearPersistedFrames();
1396
+ return;
1397
+ }
1398
+ let raw;
1399
+ try {
1400
+ raw = this._frameStore.get(BackendSessionAdapter.PENDING_FRAMES_KEY);
1401
+ } catch {
1402
+ return;
1403
+ }
1404
+ if (!raw) return;
1405
+ let parsed;
1406
+ try {
1407
+ parsed = JSON.parse(raw);
1408
+ } catch {
1409
+ // Corrupt snapshot — discard so it can't wedge every launch.
1410
+ this._clearPersistedFrames();
1411
+ return;
1412
+ }
1413
+ const sessionId = parsed.sessionId;
1414
+ const items = parsed.items;
1415
+ if (!sessionId || !Array.isArray(items) || items.length === 0) {
1416
+ this._clearPersistedFrames();
1417
+ return;
1418
+ }
1419
+ __DEV__ && _internalLogger.logger.info(`[replay.sdk.frame] recovering ${items.length} frame(s) for prior session ${sessionId}`);
1420
+ /**
1421
+ * DELETE ONLY WHAT WAS ACCEPTED.
1422
+ *
1423
+ * This posted every recovered frame in ONE request and, on any permanent rejection, deleted
1424
+ * the whole snapshot. Since the persistence budget is 2 MB of ~16 KB frames, that request was
1425
+ * routinely over the server's 100-frame cap — so the common outcome was a 4xx followed by the
1426
+ * deletion of a cross-kill recovery snapshot in which every frame was individually fine. The
1427
+ * one thing a recovery buffer must never do.
1428
+ *
1429
+ * Now the send is chunked and only the unaccepted remainder survives. A chunk the server will
1430
+ * never take is abandoned inside `_postFramesChunked` (and reported), so a single poison chunk
1431
+ * cannot wedge every future launch — but a transient failure keeps its frames for next time.
1432
+ */
1433
+ const remaining = await this._postFramesChunked(sessionId, items);
1434
+ if (remaining.length === 0) {
1435
+ this._clearPersistedFrames();
1436
+ return;
1437
+ }
1438
+ _internalLogger.logger.warn(`[replay.sdk.frame] prior-session frame recovery deferred`, {
1439
+ kept: remaining.length
1440
+ });
1441
+ try {
1442
+ this._frameStore.set(BackendSessionAdapter.PENDING_FRAMES_KEY, JSON.stringify({
1443
+ sessionId,
1444
+ items: remaining
1445
+ }));
1446
+ } catch {
1447
+ // Re-writing the trimmed snapshot is best-effort; the original is still on disk.
1448
+ }
1449
+ }
1450
+
1451
+ /**
1452
+ * Upload a batch via object storage. Returns 'sent' on success, 'disabled' if
1453
+ * the backend reports storage is not configured (caller falls back to inline).
1454
+ * Throws on transient error so the caller re-queues the batch.
1455
+ */
1456
+ async _uploadFramesViaStorage(sessionId, batch) {
1457
+ // Decode ONCE: the exact byte count we will PUT is declared as `sizeBytes`, and the backend
1458
+ // signs that Content-Length into the URL — the PUT body must be these very bytes.
1459
+ const decoded = batch.map(b => ({
1460
+ b,
1461
+ bytes: (0, _base.base64ToBytes)(this._stripDataUri(b.imageData))
1462
+ }));
1463
+ const presignReq = decoded.map(({
1464
+ b,
1465
+ bytes
1466
+ }) => {
1467
+ const m = this._frameMeta(b);
1468
+ return {
1469
+ frameId: m.frameId,
1470
+ contentType: m.contentType,
1471
+ format: m.format,
1472
+ sizeBytes: bytes.length
1473
+ };
1474
+ });
1475
+ const resp = await this._postReturning(_endpoints.ENDPOINTS.INGESTION_FRAMES_PRESIGN(sessionId), {
1476
+ frames: presignReq
1477
+ });
1478
+ if (!resp || resp.enabled === false) {
1479
+ // Latch off for the rest of the session so we don't presign every batch.
1480
+ this.objectStorageActive = false;
1481
+ return 'disabled';
1482
+ }
1483
+ const byFrameId = new Map((resp.frames ?? []).map(p => [p.frameId, p]));
1484
+
1485
+ // PARTIAL GRANT: the backend issues URLs only up to the remaining frames allowance (the
1486
+ // response's `quota` block — already recorded by _postReturning — blocks `frames` from here
1487
+ // on). A frame WITHOUT a URL was refused: it is dropped, never thrown for and never retried.
1488
+ const granted = decoded.filter(({
1489
+ b
1490
+ }) => byFrameId.has(b.frame.frameId));
1491
+ if (granted.length < decoded.length) {
1492
+ _internalLogger.logger.warn(`[replay.sdk.frame] presign granted ${granted.length}/${decoded.length} frame(s); the rest are dropped (quota)`);
1493
+ }
1494
+ if (granted.length === 0) return 'sent';
1495
+
1496
+ // Upload bytes directly to storage (backend never sees image bytes here). URLs live 120 s.
1497
+ await Promise.all(granted.map(async ({
1498
+ b,
1499
+ bytes
1500
+ }) => {
1501
+ const p = byFrameId.get(b.frame.frameId);
1502
+ await this._putBytes(p.uploadUrl, bytes, p.contentType);
1503
+ }));
1504
+
1505
+ // Persist key-only metadata (no base64 in the body) — granted frames only.
1506
+ const frames = granted.map(({
1507
+ b
1508
+ }) => {
1509
+ const p = byFrameId.get(b.frame.frameId);
1510
+ return {
1511
+ ...this._frameMeta(b),
1512
+ storageKey: p.storageKey,
1513
+ contentType: p.contentType
1514
+ };
1515
+ });
1516
+ await this._post(_endpoints.ENDPOINTS.INGESTION_FRAMES(sessionId), {
1517
+ frames
1518
+ });
1519
+ return 'sent';
1520
+ }
1521
+ async _flushEvents() {
1522
+ if (this.pendingEvents.length === 0 || !this.currentSessionId) return;
1523
+ if (_quotaState.quotaState.isBlocked('events') || this._currentSessionRefused()) {
1524
+ this.pendingEvents = [];
1525
+ return;
1526
+ }
1527
+ const batch = this.pendingEvents.splice(0, 20);
1528
+ const events = batch.map(e => this._leanEvent(e, e.sessionId));
1529
+ try {
1530
+ __DEV__ && _internalLogger.logger.info(`[replay.sdk.event] backend event upload started`, {
1531
+ count: batch.length
1532
+ });
1533
+ // Durable native outbox owns delivery when active (no duplicate JS POST).
1534
+ // The native enqueue is NOT gated on row-readiness: it writes to disk and its
1535
+ // uploader handles ordering/retry, so it must accept data even while offline.
1536
+ if (await this._enqueueNative('events', events)) return;
1537
+ // §11: JS-direct POST only after the session_start row exists; else re-buffer.
1538
+ if (!this._sessionRowReady()) {
1539
+ this.pendingEvents.unshift(...batch);
1540
+ return;
1541
+ }
1542
+ const eventsEndpoint = this.config.clientKey ? _endpoints.ENDPOINTS.INGESTION_EVENTS(this.currentSessionId) : `/replay/sessions/${this.currentSessionId}/events`;
1543
+ await this._post(eventsEndpoint, {
1544
+ events
1545
+ });
1546
+ __DEV__ && _internalLogger.logger.info(`[replay.sdk.event] backend event upload success`);
1547
+ __DEV__ && _internalLogger.logger.debug(`[BackendSessionAdapter] Flushed ${batch.length} event(s)`);
1548
+ } catch (err) {
1549
+ if (BackendSessionAdapter._isPermanentHttpError(err)) return;
1550
+ _internalLogger.logger.warn(`[replay.sdk.error] backend event upload failure`, {
1551
+ count: batch.length
1552
+ });
1553
+ this.pendingEvents.unshift(...batch);
1554
+ }
1555
+ }
1556
+ async _flushNetwork() {
1557
+ if (this.pendingNetworkRequests.length === 0 || !this.currentSessionId || !this.config.clientKey) return;
1558
+ if (_quotaState.quotaState.isBlocked('networkRequests') || this._currentSessionRefused()) {
1559
+ this.pendingNetworkRequests = [];
1560
+ return;
1561
+ }
1562
+ const batch = this.pendingNetworkRequests.splice(0, 50);
1563
+ try {
1564
+ if (await this._enqueueNative('network', batch)) return;
1565
+ if (!this._sessionRowReady()) {
1566
+ this.pendingNetworkRequests.unshift(...batch);
1567
+ return;
1568
+ }
1569
+ await this._post(_endpoints.ENDPOINTS.INGESTION_NETWORK(this.currentSessionId), {
1570
+ requests: batch
1571
+ });
1572
+ } catch (err) {
1573
+ if (!BackendSessionAdapter._isPermanentHttpError(err)) this.pendingNetworkRequests.unshift(...batch);
1574
+ }
1575
+ }
1576
+
1577
+ /**
1578
+ * D11: deliver a bug report to the SAME ingest the web SDK uses
1579
+ * (`POST /ingestion/sessions/:id/bug-reports`). Fire-and-forget with one
1580
+ * retry deferral: RN previously had NO path to this endpoint at all —
1581
+ * `reportBug()` emitted an analytics event no handler consumed, so the
1582
+ * bug-reports page was empty forever for every RN app.
1583
+ */
1584
+ submitBugReport(report) {
1585
+ if (this.destroyed || !this.config.clientKey || !this.currentSessionId) return;
1586
+ void this._post(`/ingestion/sessions/${this.currentSessionId}/bug-reports`, {
1587
+ ...report,
1588
+ timestamp: Date.now()
1589
+ }).catch(() => {
1590
+ /* best-effort; the $bug_report analytics breadcrumb still records intent */
1591
+ });
1592
+ }
1593
+
1594
+ /**
1595
+ * @returns TRUE only when every group in this batch was DURABLY accepted — an HTTP 2xx, or a
1596
+ * handoff to the native outbox, which is itself a persistent retrying queue.
1597
+ *
1598
+ * The return value is not cosmetic: `drainNativeCrash` deletes the on-disk crash record on the
1599
+ * strength of it. This method used to return `void` and swallow its own errors in the catch
1600
+ * below, so "the promise resolved" meant nothing at all — a failed POST looked exactly like a
1601
+ * successful one, and the caller would have deleted the only durable copy of a crash that never
1602
+ * arrived. Anything less than a real acknowledgement here reintroduces the loss this whole
1603
+ * change exists to prevent.
1604
+ */
1605
+ async _flushErrors() {
1606
+ const live = this._errorsSessionId();
1607
+ if (this.pendingErrors.length === 0 || !live || !this.config.clientKey) return false;
1608
+ const batch = this.pendingErrors.splice(0, 20);
1609
+ try {
1610
+ if (!this._startedSessionIds.has(live)) {
1611
+ this.pendingErrors.unshift(...batch);
1612
+ return false;
1613
+ }
1614
+ /**
1615
+ * ROUTED BY THE ERROR'S OWN SESSION, not by whichever session is current.
1616
+ *
1617
+ * A recovered native crash carries the id of the session it actually happened in — that
1618
+ * process is gone, so the current session is the wrong owner. Sending it under
1619
+ * `currentSessionId` is what marked a healthy session crashed and left the crashed one
1620
+ * looking clean.
1621
+ *
1622
+ * The URL still carries a session because that is the established ingest shape; the
1623
+ * error's own `sessionId` simply chooses WHICH one. Errors are grouped by their target
1624
+ * so a batch mixing a recovered crash with live errors cannot drag the live ones onto
1625
+ * the dead session.
1626
+ */
1627
+ const byTarget = new Map();
1628
+ for (const e of batch) {
1629
+ const target = e.sessionId || live;
1630
+ byTarget.set(target, [...(byTarget.get(target) ?? []), e]);
1631
+ }
1632
+ let allAccepted = true;
1633
+ for (const [target, errors] of byTarget) {
1634
+ // `sessionId` is a routing instruction, not payload — strip it so the stored row is
1635
+ // byte-identical to one sent the ordinary way.
1636
+ const cleaned = errors.map(({
1637
+ sessionId: _s,
1638
+ ...rest
1639
+ }) => rest);
1640
+
1641
+ // The outbox is enqueued PER TARGET, so a recovered crash keeps its own session.
1642
+ if (await this._enqueueNative('errors', cleaned, target)) continue;
1643
+ try {
1644
+ await this._post(_endpoints.ENDPOINTS.INGESTION_ERRORS(target), {
1645
+ errors: cleaned
1646
+ });
1647
+ } catch (err) {
1648
+ /*
1649
+ * 404 on a session that is not the live one: the backend has no row for the
1650
+ * session this crash names. Records written before the native session id was
1651
+ * fixed name the event tracker's local `sess-` id, which never had a row, and
1652
+ * a session can also be purged by retention before its crash is drained.
1653
+ * Dropping it as "permanent" is what lost every such crash. File it under the
1654
+ * live session instead, and SAY so — a crash attributed by fallback must never
1655
+ * read as one observed in that session.
1656
+ */
1657
+ if (err?.status === 404 && target !== live) {
1658
+ const moved = cleaned.map(e => ({
1659
+ ...e,
1660
+ metadata: {
1661
+ ...(e.metadata ?? {}),
1662
+ attribution: 'fallback',
1663
+ originalSessionId: target
1664
+ }
1665
+ }));
1666
+ try {
1667
+ await this._post(_endpoints.ENDPOINTS.INGESTION_ERRORS(live), {
1668
+ errors: moved
1669
+ });
1670
+ continue;
1671
+ } catch (retryErr) {
1672
+ err = retryErr;
1673
+ }
1674
+ }
1675
+ allAccepted = false;
1676
+ // A permanent error (4xx) will never succeed, so re-queuing it would spin
1677
+ // forever; anything else is worth another attempt.
1678
+ if (!BackendSessionAdapter._isPermanentHttpError(err)) {
1679
+ this.pendingErrors.unshift(...errors);
1680
+ }
1681
+ }
1682
+ }
1683
+ return allAccepted;
1684
+ } catch (err) {
1685
+ if (!BackendSessionAdapter._isPermanentHttpError(err)) this.pendingErrors.unshift(...batch);
1686
+ return false;
1687
+ }
1688
+ }
1689
+ async _flushPerformance() {
1690
+ if (this.pendingPerformanceMetrics.length === 0) return;
1691
+ if (!this.currentSessionId || !this.config.clientKey) {
1692
+ _internalLogger.logger.debug('[perf.trace] flush waiting — not deliverable yet', {
1693
+ pending: this.pendingPerformanceMetrics.length,
1694
+ hasSession: !!this.currentSessionId,
1695
+ hasClientKey: !!this.config.clientKey
1696
+ });
1697
+ return;
1698
+ }
1699
+ if (this._currentSessionRefused()) {
1700
+ this.pendingPerformanceMetrics = [];
1701
+ return;
1702
+ }
1703
+ const batch = this.pendingPerformanceMetrics.splice(0, 50);
1704
+ try {
1705
+ if (await this._enqueueNative('performance', batch)) {
1706
+ _internalLogger.logger.debug('[perf.trace] flush handed to native outbox', {
1707
+ count: batch.length
1708
+ });
1709
+ return;
1710
+ }
1711
+ if (!this._sessionRowReady()) {
1712
+ _internalLogger.logger.debug('[perf.trace] flush session row not ready — re-buffering', {
1713
+ count: batch.length
1714
+ });
1715
+ this.pendingPerformanceMetrics.unshift(...batch);
1716
+ return;
1717
+ }
1718
+ await this._post(_endpoints.ENDPOINTS.INGESTION_PERFORMANCE(this.currentSessionId), {
1719
+ metrics: batch
1720
+ });
1721
+ _internalLogger.logger.debug('[perf.trace] flush POST performance metrics ok', {
1722
+ count: batch.length
1723
+ });
1724
+ } catch (err) {
1725
+ const permanent = BackendSessionAdapter._isPermanentHttpError(err);
1726
+ _internalLogger.logger.warn('[perf.flush] POST performance metrics failed', {
1727
+ count: batch.length,
1728
+ permanent
1729
+ });
1730
+ if (!permanent) this.pendingPerformanceMetrics.unshift(...batch);
1731
+ }
1732
+ }
1733
+ async _flushLogs() {
1734
+ if (this.pendingLogs.length === 0 || !this.currentSessionId || !this.config.clientKey) return;
1735
+ if (this._currentSessionRefused()) {
1736
+ this.pendingLogs = [];
1737
+ return;
1738
+ }
1739
+ const batch = this.pendingLogs.splice(0, BackendSessionAdapter.LOGS_FLUSH_MAX);
1740
+ try {
1741
+ if (await this._enqueueNative('logs', batch)) return;
1742
+ if (!this._sessionRowReady()) {
1743
+ this.pendingLogs.unshift(...batch);
1744
+ return;
1745
+ }
1746
+ await this._post(_endpoints.ENDPOINTS.INGESTION_LOGS(this.currentSessionId), {
1747
+ logs: batch
1748
+ });
1749
+ } catch (err) {
1750
+ if (!BackendSessionAdapter._isPermanentHttpError(err)) this.pendingLogs.unshift(...batch);
1751
+ }
1752
+ }
1753
+
1754
+ // ─── Internal: Timer ────────────────────────────────────────────────
1755
+
1756
+ _startFlushTimer() {
1757
+ this._stopFlushTimer();
1758
+ this.flushTimer = setInterval(() => {
1759
+ this._retryPendingStarts();
1760
+ this._flushFrames().catch(() => {});
1761
+ this._flushEvents().catch(() => {});
1762
+ this._flushNetwork().catch(() => {});
1763
+ this.flushErrors().catch(() => {});
1764
+ this._flushPerformance().catch(() => {});
1765
+ this._flushLogs().catch(() => {});
1766
+ this._triggerNativeFlush();
1767
+ }, this.config.flushIntervalMs);
1768
+ }
1769
+ _stopFlushTimer() {
1770
+ if (this.flushTimer) {
1771
+ clearInterval(this.flushTimer);
1772
+ this.flushTimer = null;
1773
+ }
1774
+ }
1775
+
1776
+ // ─── Internal: HTTP ─────────────────────────────────────────────────
1777
+
1778
+ // A 4xx (except 408 Request Timeout / 429 Too Many Requests) is permanent:
1779
+ // the same payload will never succeed, so requeuing it retries forever and
1780
+ // floods the logs (e.g. a rejected clientKey → HTTP 403 on every flush tick).
1781
+ // Drop the batch on these; keep retrying 5xx / timeouts / network errors.
1782
+ static _isPermanentHttpError(err) {
1783
+ const status = err?.status;
1784
+ return typeof status === 'number' && status >= 400 && status < 500 && status !== 408 && status !== 429;
1785
+ }
1786
+ async _post(path, body) {
1787
+ const url = `${this.config.endpoint}${path}`;
1788
+ try {
1789
+ const controller = new AbortController();
1790
+ const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);
1791
+ try {
1792
+ const headers = {
1793
+ 'Content-Type': 'application/json'
1794
+ };
1795
+ if (this.config.clientKey) {
1796
+ headers['x-scalebun-client-key'] = this.config.clientKey;
1797
+ }
1798
+ const response = await fetch(url, {
1799
+ method: 'POST',
1800
+ headers,
1801
+ body: JSON.stringify(body),
1802
+ signal: controller.signal
1803
+ });
1804
+ if (!response.ok) {
1805
+ _internalLogger.logger.warn(`[BackendSessionAdapter] HTTP ${response.status} from ${path}`);
1806
+ const httpErr = new Error(`HTTP ${response.status}`);
1807
+ httpErr.status = response.status;
1808
+ httpErr.quotaFeature = await BackendSessionAdapter._noteQuota(response);
1809
+ throw httpErr;
1810
+ }
1811
+ // A 2xx may carry a partial grant (`quota.refused > 0`): the allowance is now spent.
1812
+ await BackendSessionAdapter._noteQuota(response);
1813
+ } finally {
1814
+ clearTimeout(timeout);
1815
+ }
1816
+ } catch (err) {
1817
+ if (err?.name === 'AbortError') {
1818
+ _internalLogger.logger.warn(`[BackendSessionAdapter] Timeout on ${path}`);
1819
+ } else {
1820
+ _internalLogger.logger.warn(`[BackendSessionAdapter] Failed ${path}: ${err?.message}`);
1821
+ }
1822
+ throw err;
1823
+ }
1824
+ }
1825
+
1826
+ /**
1827
+ * Read an ingestion response for a quota refusal (contract v1 §2a) and record it in quotaState.
1828
+ * Only a 402 or a 2xx can carry one; anything else is not read. Never throws.
1829
+ */
1830
+ static async _noteQuota(response) {
1831
+ const status = response?.status;
1832
+ if (status !== 402 && !(status >= 200 && status < 300)) return null;
1833
+ if (typeof response?.json !== 'function') return null;
1834
+ try {
1835
+ const body = await response.json();
1836
+ return (0, _quotaState.noteQuotaResponse)(status, body, response.headers);
1837
+ } catch {
1838
+ return null; // empty / non-JSON body
1839
+ }
1840
+ }
1841
+
1842
+ /**
1843
+ * POST that parses and returns the JSON response body. Mirrors _post's headers
1844
+ * and timeout handling; used by the object-storage presign request, which needs
1845
+ * the returned upload URLs (the void _post discards the body).
1846
+ */
1847
+ async _postReturning(path, body) {
1848
+ const url = `${this.config.endpoint}${path}`;
1849
+ const controller = new AbortController();
1850
+ const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);
1851
+ try {
1852
+ const headers = {
1853
+ 'Content-Type': 'application/json'
1854
+ };
1855
+ if (this.config.clientKey) {
1856
+ headers['x-scalebun-client-key'] = this.config.clientKey;
1857
+ }
1858
+ const response = await fetch(url, {
1859
+ method: 'POST',
1860
+ headers,
1861
+ body: JSON.stringify(body),
1862
+ signal: controller.signal
1863
+ });
1864
+ if (!response.ok) {
1865
+ _internalLogger.logger.warn(`[BackendSessionAdapter] HTTP ${response.status} from ${path}`);
1866
+ const httpErr = new Error(`HTTP ${response.status}`);
1867
+ httpErr.status = response.status;
1868
+ httpErr.quotaFeature = await BackendSessionAdapter._noteQuota(response);
1869
+ throw httpErr;
1870
+ }
1871
+ const parsed = await response.json();
1872
+ // A 2xx can carry a partial grant (`quota.refused > 0`) — record it (frames presign).
1873
+ try {
1874
+ (0, _quotaState.noteQuotaResponse)(response.status, parsed, response.headers);
1875
+ } catch {/* no-throw */}
1876
+ return parsed;
1877
+ } finally {
1878
+ clearTimeout(timeout);
1879
+ }
1880
+ }
1881
+
1882
+ /**
1883
+ * PUT raw image bytes directly to a presigned storage URL. The caller decodes the base64 frame
1884
+ * payload once (RN-safe: a Uint8Array body — the `fetch('data:…').blob()` round-trip fails under
1885
+ * the new architecture) so the bytes PUT are exactly the `sizeBytes` declared at presign, whose
1886
+ * Content-Length the URL is signed for. No client key header — the URL carries its own auth.
1887
+ */
1888
+ async _putBytes(uploadUrl, bytes, contentType) {
1889
+ const controller = new AbortController();
1890
+ const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);
1891
+ try {
1892
+ const response = await fetch(uploadUrl, {
1893
+ method: 'PUT',
1894
+ headers: {
1895
+ 'Content-Type': contentType
1896
+ },
1897
+ body: bytes,
1898
+ signal: controller.signal
1899
+ });
1900
+ if (!response.ok) {
1901
+ throw new Error(`HTTP ${response.status}`);
1902
+ }
1903
+ } finally {
1904
+ clearTimeout(timeout);
1905
+ }
1906
+ }
1907
+
1908
+ /** Strip a leading `data:<mime>;base64,` prefix if the native layer included one. */
1909
+ _stripDataUri(s) {
1910
+ const comma = s.indexOf(',');
1911
+ return s.startsWith('data:') && comma !== -1 ? s.slice(comma + 1) : s;
1912
+ }
1913
+ }
1914
+ exports.BackendSessionAdapter = BackendSessionAdapter;
1915
+ //# sourceMappingURL=BackendSessionAdapter.js.map