@scalebun/react-native 1.0.2 → 1.0.4

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 (1694) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +65 -65
  3. package/android/build.gradle +86 -86
  4. package/android/consumer-rules.pro +28 -28
  5. package/android/src/main/AndroidManifest.xml +14 -14
  6. package/android/src/main/java/com/scalebun/core/crash/CrashStorage.kt +155 -41
  7. package/android/src/main/java/com/scalebun/core/crash/NativeCrashHandler.kt +93 -79
  8. package/android/src/main/java/com/scalebun/core/performance/ANRWatchdog.kt +100 -100
  9. package/android/src/main/java/com/scalebun/core/performance/AppStartCollector.kt +132 -132
  10. package/android/src/main/java/com/scalebun/core/performance/FrameMetricsCollector.kt +230 -230
  11. package/android/src/main/java/com/scalebun/core/performance/PerformanceCore.kt +165 -165
  12. package/android/src/main/java/com/scalebun/profiler/ProfilerModule.kt +398 -398
  13. package/android/src/main/java/com/scalebun/profiler/ProfilerPackage.kt +24 -24
  14. package/android/src/main/java/com/scalebun/profiler/collectors/CpuCollector.kt +161 -161
  15. package/android/src/main/java/com/scalebun/profiler/collectors/IncidentDetector.kt +223 -223
  16. package/android/src/main/java/com/scalebun/profiler/collectors/MemoryCollector.kt +157 -157
  17. package/android/src/main/java/com/scalebun/profiler/collectors/RenderCollector.kt +194 -194
  18. package/android/src/main/java/com/scalebun/profiler/collectors/SamplerScheduler.kt +218 -218
  19. package/android/src/main/java/com/scalebun/profiler/collectors/ThreadCollector.kt +142 -142
  20. package/android/src/main/java/com/scalebun/replaysdk/ReplaySdkModule.kt +982 -966
  21. package/android/src/main/java/com/scalebun/replaysdk/ReplaySdkPackage.kt +24 -24
  22. package/android/src/main/java/com/scalebun/replaysdk/capture/CaptureReason.kt +47 -47
  23. package/android/src/main/java/com/scalebun/replaysdk/capture/FrameCaptureManager.kt +508 -508
  24. package/android/src/main/java/com/scalebun/replaysdk/capture/FrameCaptureScheduler.kt +187 -187
  25. package/android/src/main/java/com/scalebun/replaysdk/capture/UiStabilityDetector.kt +214 -214
  26. package/android/src/main/java/com/scalebun/replaysdk/core/ReplayConfig.kt +144 -144
  27. package/android/src/main/java/com/scalebun/replaysdk/core/ReplayLogger.kt +31 -31
  28. package/android/src/main/java/com/scalebun/replaysdk/core/SessionIdGenerator.kt +15 -15
  29. package/android/src/main/java/com/scalebun/replaysdk/models/ReplayModels.kt +88 -88
  30. package/android/src/main/java/com/scalebun/replaysdk/outbox/NativeOutbox.kt +541 -541
  31. package/android/src/main/java/com/scalebun/replaysdk/outbox/OutboxUploader.kt +407 -407
  32. package/android/src/main/java/com/scalebun/replaysdk/privacy/PrivacyMaskProcessor.kt +126 -126
  33. package/android/src/main/java/com/scalebun/replaysdk/session/SessionManager.kt +281 -281
  34. package/android/src/main/java/com/scalebun/replaysdk/storage/ReplayStorage.kt +136 -136
  35. package/android/src/main/java/com/scalebun/replaysdk/timeline/TimelineCollector.kt +28 -28
  36. package/android/src/main/java/com/scalebun/replaysdk/tracking/InteractionTracker.kt +419 -419
  37. package/android/src/main/java/com/scalebun/replaysdk/tracking/ScreenTracker.kt +20 -20
  38. package/android/src/main/java/com/scalebun/replaysdk/transport/ReplayTransport.kt +99 -99
  39. package/android/src/main/java/com/scalebun/replaysdk/voice/VoiceRecorder.kt +121 -121
  40. package/android/src/main/java/com/scalebun/replaysdk/voice/VoiceTranscriber.kt +170 -104
  41. package/android/src/main/java/com/scalebun/rn/ScaleBunPackage.kt +113 -113
  42. package/android/src/main/java/com/scalebun/rn/crash/ScaleBunCrashModule.kt +164 -100
  43. package/android/src/main/java/com/scalebun/rn/crash/ScaleBunCrashPackage.kt +22 -22
  44. package/android/src/main/java/com/scalebun/rn/engage/ScaleBunEngageModule.kt +579 -579
  45. package/android/src/main/java/com/scalebun/rn/engage/ScaleBunEngagePackage.kt +22 -22
  46. package/android/src/main/java/com/scalebun/rn/engage/ScaleBunNotificationPresenter.kt +110 -110
  47. package/android/src/main/java/com/scalebun/rn/ota/BundleDownloader.kt +170 -0
  48. package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +284 -136
  49. package/android/src/main/java/com/scalebun/rn/ota/SlotManager.kt +311 -285
  50. package/android/src/main/java/com/scalebun/rn/performance/PerformanceModule.kt +206 -206
  51. package/android/src/main/java/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule.kt +77 -77
  52. package/android/src/main/java/com/scalebun/rn/storage/ScaleBunStorageModule.kt +68 -68
  53. package/android/src/main/java/com/scalebun/storage/KvFileStore.kt +174 -174
  54. package/android/src/newarch/java/com/scalebun/profiler/ProfilerSpec.kt +12 -12
  55. package/android/src/newarch/java/com/scalebun/replaysdk/ReplaySdkSpec.kt +12 -12
  56. package/android/src/newarch/java/com/scalebun/rn/crash/ScaleBunCrashSpec.kt +15 -15
  57. package/android/src/newarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +16 -16
  58. package/android/src/newarch/java/com/scalebun/rn/performance/PerformanceSpec.kt +12 -12
  59. package/android/src/newarch/java/com/scalebun/rn/referrer/ScaleBunInstallReferrerSpec.kt +14 -14
  60. package/android/src/newarch/java/com/scalebun/rn/storage/ScaleBunStorageSpec.kt +16 -16
  61. package/android/src/oldarch/java/com/scalebun/profiler/ProfilerSpec.kt +34 -34
  62. package/android/src/oldarch/java/com/scalebun/replaysdk/ReplaySdkSpec.kt +76 -76
  63. package/android/src/oldarch/java/com/scalebun/rn/crash/ScaleBunCrashSpec.kt +21 -21
  64. package/android/src/oldarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +31 -25
  65. package/android/src/oldarch/java/com/scalebun/rn/performance/PerformanceSpec.kt +27 -27
  66. package/android/src/oldarch/java/com/scalebun/rn/referrer/ScaleBunInstallReferrerSpec.kt +18 -18
  67. package/android/src/oldarch/java/com/scalebun/rn/storage/ScaleBunStorageSpec.kt +26 -26
  68. package/bin/lib/iosCodemod.js +266 -266
  69. package/bin/scalebun.js +640 -640
  70. package/ios/Capture/InteractionTracker.swift +191 -191
  71. package/ios/Capture/PrivacyMaskProcessor.swift +158 -158
  72. package/ios/Capture/ScreenshotCaptureManager.swift +315 -315
  73. package/ios/Capture/WindowResolver.swift +63 -63
  74. package/ios/Core/ReplayConfig.swift +100 -100
  75. package/ios/Core/ReplayLogger.swift +22 -22
  76. package/ios/Core/SessionIdGenerator.swift +18 -18
  77. package/ios/Crash/ScaleBunCrashBridge.mm +69 -63
  78. package/ios/Crash/ScaleBunCrashHandler.swift +80 -67
  79. package/ios/Crash/ScaleBunCrashModule.swift +83 -69
  80. package/ios/Crash/ScaleBunCrashSignalBridge.h +49 -18
  81. package/ios/Crash/ScaleBunCrashSignalBridge.m +296 -131
  82. package/ios/Crash/ScaleBunCrashSignalStress.c +137 -0
  83. package/ios/Crash/ScaleBunCrashStorage.swift +121 -46
  84. package/ios/Engage/ScaleBunEngageBridge.m +32 -32
  85. package/ios/Engage/ScaleBunEngageModule.swift +250 -250
  86. package/ios/Engage/ScaleBunPushNotificationCenter.swift +185 -185
  87. package/ios/Ota/BundleDownloader.swift +182 -0
  88. package/ios/Ota/OtaSlotManager.swift +290 -264
  89. package/ios/Ota/ScaleBunOtaBridge.mm +77 -68
  90. package/ios/Ota/ScaleBunOtaEventsModule.swift +61 -0
  91. package/ios/Ota/ScaleBunOtaModule.swift +176 -123
  92. package/ios/Outbox/NativeOutbox.swift +598 -598
  93. package/ios/Outbox/OutboxUploader.swift +431 -431
  94. package/ios/Performance/ScaleBunPerformanceBridge.m +32 -32
  95. package/ios/Performance/ScaleBunPerformanceModule.swift +290 -290
  96. package/ios/PrivacyInfo.xcprivacy +97 -97
  97. package/ios/Profiler/ScaleBunProfilerBridge.m +55 -55
  98. package/ios/Profiler/ScaleBunProfilerModule.swift +972 -972
  99. package/ios/ReplaySdk-Bridging-Header.h +2 -2
  100. package/ios/ReplaySdk.swift +608 -608
  101. package/ios/ReplaySdkBridge.m +94 -94
  102. package/ios/Session/SessionManager.swift +203 -203
  103. package/ios/Skan/ScaleBunSkanBridge.m +14 -14
  104. package/ios/Skan/ScaleBunSkanModule.swift +61 -61
  105. package/ios/Storage/ReplayStorage.swift +61 -61
  106. package/ios/Storage/ScaleBunKvFileStore.swift +125 -125
  107. package/ios/Storage/ScaleBunStorageBridge.mm +72 -72
  108. package/ios/Storage/ScaleBunStorageModule.swift +57 -57
  109. package/ios/Transport/ReplayTransport.swift +70 -70
  110. package/ios/VoiceRecorder.swift +145 -145
  111. package/lib/commonjs/analytics/EventTracker.js +64 -36
  112. package/lib/commonjs/analytics/EventTracker.js.map +1 -1
  113. package/lib/commonjs/analytics/automaticEvents.js +7 -7
  114. package/lib/commonjs/analytics/automaticEvents.js.map +1 -1
  115. package/lib/commonjs/analytics/batching.js +24 -24
  116. package/lib/commonjs/analytics/batching.js.map +1 -1
  117. package/lib/commonjs/analytics/eventLane.js +13 -13
  118. package/lib/commonjs/analytics/eventLane.js.map +1 -1
  119. package/lib/commonjs/bootstrap/FeatureRegistry.js +17 -17
  120. package/lib/commonjs/bootstrap/FeatureRegistry.js.map +1 -1
  121. package/lib/commonjs/bootstrap/SDKBootstrapper.js +83 -56
  122. package/lib/commonjs/bootstrap/SDKBootstrapper.js.map +1 -1
  123. package/lib/commonjs/bucketing/fnv1a32.js +10 -10
  124. package/lib/commonjs/bucketing/fnv1a32.js.map +1 -1
  125. package/lib/commonjs/compat/codepush.js +6 -6
  126. package/lib/commonjs/compat/codepush.js.map +1 -1
  127. package/lib/commonjs/config/ConfigManager.js +20 -20
  128. package/lib/commonjs/config/ConfigManager.js.map +1 -1
  129. package/lib/commonjs/config/configTypes.js.map +1 -1
  130. package/lib/commonjs/core/architecture.js +23 -23
  131. package/lib/commonjs/core/architecture.js.map +1 -1
  132. package/lib/commonjs/core/clock/now.js +2 -2
  133. package/lib/commonjs/core/clock/now.js.map +1 -1
  134. package/lib/commonjs/core/config/defaults.js.map +1 -1
  135. package/lib/commonjs/core/config/schema.js +112 -104
  136. package/lib/commonjs/core/config/schema.js.map +1 -1
  137. package/lib/commonjs/core/constants/endpoints.js +7 -7
  138. package/lib/commonjs/core/constants/endpoints.js.map +1 -1
  139. package/lib/commonjs/core/constants/timings.js +5 -5
  140. package/lib/commonjs/core/constants/timings.js.map +1 -1
  141. package/lib/commonjs/core/constants/version.js +4 -4
  142. package/lib/commonjs/core/constants/version.js.map +1 -1
  143. package/lib/commonjs/core/context/app.js.map +1 -1
  144. package/lib/commonjs/core/context/device.js +130 -0
  145. package/lib/commonjs/core/context/device.js.map +1 -1
  146. package/lib/commonjs/core/context/session.js.map +1 -1
  147. package/lib/commonjs/core/context/user.js.map +1 -1
  148. package/lib/commonjs/core/contracts/IFeature.js.map +1 -1
  149. package/lib/commonjs/core/di/container.js.map +1 -1
  150. package/lib/commonjs/core/encoding/base64.js +9 -9
  151. package/lib/commonjs/core/encoding/base64.js.map +1 -1
  152. package/lib/commonjs/core/id/deviceId.js +4 -4
  153. package/lib/commonjs/core/id/deviceId.js.map +1 -1
  154. package/lib/commonjs/core/id/sessionId.js.map +1 -1
  155. package/lib/commonjs/core/lifecycle/appLifecycle.js.map +1 -1
  156. package/lib/commonjs/core/lifecycle/crashSafe.js +3 -3
  157. package/lib/commonjs/core/lifecycle/crashSafe.js.map +1 -1
  158. package/lib/commonjs/core/logger/errorClassification.js +30 -30
  159. package/lib/commonjs/core/logger/errorClassification.js.map +1 -1
  160. package/lib/commonjs/core/logger/internalLogger.js.map +1 -1
  161. package/lib/commonjs/core/logger/levels.js.map +1 -1
  162. package/lib/commonjs/crypto/hmacSha256.js +6 -6
  163. package/lib/commonjs/crypto/hmacSha256.js.map +1 -1
  164. package/lib/commonjs/debug/bootstrap.js +26 -26
  165. package/lib/commonjs/debug/bootstrap.js.map +1 -1
  166. package/lib/commonjs/debug/commands.js +10 -10
  167. package/lib/commonjs/debug/commands.js.map +1 -1
  168. package/lib/commonjs/debug/configBuilder.js +6 -6
  169. package/lib/commonjs/debug/configBuilder.js.map +1 -1
  170. package/lib/commonjs/debug/exportBridge.js +17 -17
  171. package/lib/commonjs/debug/exportBridge.js.map +1 -1
  172. package/lib/commonjs/debug/hostResolver.js +10 -10
  173. package/lib/commonjs/debug/hostResolver.js.map +1 -1
  174. package/lib/commonjs/debug/index.js.map +1 -1
  175. package/lib/commonjs/debug/metrics.js.map +1 -1
  176. package/lib/commonjs/debug/perf.js +11 -11
  177. package/lib/commonjs/debug/perf.js.map +1 -1
  178. package/lib/commonjs/debug/perfWiring.js +10 -10
  179. package/lib/commonjs/debug/perfWiring.js.map +1 -1
  180. package/lib/commonjs/debug/protocol.js.map +1 -1
  181. package/lib/commonjs/debug/redaction.js +26 -26
  182. package/lib/commonjs/debug/redaction.js.map +1 -1
  183. package/lib/commonjs/debug/replayWiring.js +13 -12
  184. package/lib/commonjs/debug/replayWiring.js.map +1 -1
  185. package/lib/commonjs/debug/screenTracking.js +7 -7
  186. package/lib/commonjs/debug/screenTracking.js.map +1 -1
  187. package/lib/commonjs/debug/sessionWiring.js +8 -8
  188. package/lib/commonjs/debug/sessionWiring.js.map +1 -1
  189. package/lib/commonjs/debug/stream.js +26 -26
  190. package/lib/commonjs/debug/stream.js.map +1 -1
  191. package/lib/commonjs/debug/transport.js +24 -24
  192. package/lib/commonjs/debug/transport.js.map +1 -1
  193. package/lib/commonjs/features/bugreport/BugReportFeature.js +112 -0
  194. package/lib/commonjs/features/bugreport/BugReportFeature.js.map +1 -0
  195. package/lib/commonjs/features/bugreport/buildPayload.js +97 -0
  196. package/lib/commonjs/features/bugreport/buildPayload.js.map +1 -0
  197. package/lib/commonjs/features/bugreport/index.js.map +1 -1
  198. package/lib/commonjs/features/crash/CrashFeature.js +27 -9
  199. package/lib/commonjs/features/crash/CrashFeature.js.map +1 -1
  200. package/lib/commonjs/features/crash/CrashReporter.js +53 -29
  201. package/lib/commonjs/features/crash/CrashReporter.js.map +1 -1
  202. package/lib/commonjs/features/crash/NativeCrashFeature.js +7 -7
  203. package/lib/commonjs/features/crash/NativeCrashFeature.js.map +1 -1
  204. package/lib/commonjs/features/crash/capture.js +27 -9
  205. package/lib/commonjs/features/crash/capture.js.map +1 -1
  206. package/lib/commonjs/features/crash/globalHandler.js +11 -11
  207. package/lib/commonjs/features/crash/globalHandler.js.map +1 -1
  208. package/lib/commonjs/features/crash/index.js.map +1 -1
  209. package/lib/commonjs/features/crash/nativeCrashBridge.js +179 -44
  210. package/lib/commonjs/features/crash/nativeCrashBridge.js.map +1 -1
  211. package/lib/commonjs/features/crash/rejectionHandler.js +123 -0
  212. package/lib/commonjs/features/crash/rejectionHandler.js.map +1 -0
  213. package/lib/commonjs/features/engage/EngageAnchor.js +3 -3
  214. package/lib/commonjs/features/engage/EngageAnchor.js.map +1 -1
  215. package/lib/commonjs/features/engage/EngageArchetypeRenderers.js +19 -19
  216. package/lib/commonjs/features/engage/EngageArchetypeRenderers.js.map +1 -1
  217. package/lib/commonjs/features/engage/EngageInAppView.js +35 -35
  218. package/lib/commonjs/features/engage/EngageInAppView.js.map +1 -1
  219. package/lib/commonjs/features/engage/EngageInlinePlacement.js +7 -7
  220. package/lib/commonjs/features/engage/EngageInlinePlacement.js.map +1 -1
  221. package/lib/commonjs/features/engage/EngagePromptProvider.js +124 -88
  222. package/lib/commonjs/features/engage/EngagePromptProvider.js.map +1 -1
  223. package/lib/commonjs/features/engage/EngagePromptView.js +65 -32
  224. package/lib/commonjs/features/engage/EngagePromptView.js.map +1 -1
  225. package/lib/commonjs/features/engage/EngageTransport.js +79 -79
  226. package/lib/commonjs/features/engage/EngageTransport.js.map +1 -1
  227. package/lib/commonjs/features/engage/EngageVariantContent.js +10 -10
  228. package/lib/commonjs/features/engage/EngageVariantContent.js.map +1 -1
  229. package/lib/commonjs/features/engage/__test-support__/reactNativeStub.js +7 -7
  230. package/lib/commonjs/features/engage/__test-support__/reactNativeStub.js.map +1 -1
  231. package/lib/commonjs/features/engage/attachmentCapture.js +12 -12
  232. package/lib/commonjs/features/engage/attachmentCapture.js.map +1 -1
  233. package/lib/commonjs/features/engage/captureVoiceAttachment.js +20 -20
  234. package/lib/commonjs/features/engage/captureVoiceAttachment.js.map +1 -1
  235. package/lib/commonjs/features/engage/engageCoachmarkTour.js +4 -4
  236. package/lib/commonjs/features/engage/engageCoachmarkTour.js.map +1 -1
  237. package/lib/commonjs/features/engage/engageGameLogic.js.map +1 -1
  238. package/lib/commonjs/features/engage/engageMediaSizing.js +15 -15
  239. package/lib/commonjs/features/engage/engageMediaSizing.js.map +1 -1
  240. package/lib/commonjs/features/engage/engageMultiStepSheet.js +6 -6
  241. package/lib/commonjs/features/engage/engageMultiStepSheet.js.map +1 -1
  242. package/lib/commonjs/features/engage/engageOutcome.js +6 -6
  243. package/lib/commonjs/features/engage/engageOutcome.js.map +1 -1
  244. package/lib/commonjs/features/engage/engagePushTokenBridge.js +20 -20
  245. package/lib/commonjs/features/engage/engagePushTokenBridge.js.map +1 -1
  246. package/lib/commonjs/features/engage/engageShakeBridge.js +16 -16
  247. package/lib/commonjs/features/engage/engageShakeBridge.js.map +1 -1
  248. package/lib/commonjs/features/engage/engageSignals.js.map +1 -1
  249. package/lib/commonjs/features/engage/engageStoreReviewBridge.js +19 -19
  250. package/lib/commonjs/features/engage/engageStoreReviewBridge.js.map +1 -1
  251. package/lib/commonjs/features/engage/engageTestSeenStore.js +29 -29
  252. package/lib/commonjs/features/engage/engageTestSeenStore.js.map +1 -1
  253. package/lib/commonjs/features/engage/engageThrottle.js +117 -86
  254. package/lib/commonjs/features/engage/engageThrottle.js.map +1 -1
  255. package/lib/commonjs/features/engage/engageTriggerEngine.js +17 -7
  256. package/lib/commonjs/features/engage/engageTriggerEngine.js.map +1 -1
  257. package/lib/commonjs/features/engage/engageTypes.js +33 -33
  258. package/lib/commonjs/features/engage/engageTypes.js.map +1 -1
  259. package/lib/commonjs/features/engage/engageVariantResolver.js +13 -13
  260. package/lib/commonjs/features/engage/engageVariantResolver.js.map +1 -1
  261. package/lib/commonjs/features/engage/engageWindowInsets.js +10 -10
  262. package/lib/commonjs/features/engage/engageWindowInsets.js.map +1 -1
  263. package/lib/commonjs/features/engage/gestureTriggerDetector.js.map +1 -1
  264. package/lib/commonjs/features/engage/transcription/TranscriptionProvider.js +16 -16
  265. package/lib/commonjs/features/engage/transcription/TranscriptionProvider.js.map +1 -1
  266. package/lib/commonjs/features/install-referrer/installReferrer.js +26 -26
  267. package/lib/commonjs/features/install-referrer/installReferrer.js.map +1 -1
  268. package/lib/commonjs/features/journey/ScaleBunDebugRoot.js +30 -29
  269. package/lib/commonjs/features/journey/ScaleBunDebugRoot.js.map +1 -1
  270. package/lib/commonjs/features/journey/ScaleBunFlatList.js +15 -15
  271. package/lib/commonjs/features/journey/ScaleBunFlatList.js.map +1 -1
  272. package/lib/commonjs/features/journey/ScaleBunImpression.js +22 -22
  273. package/lib/commonjs/features/journey/ScaleBunImpression.js.map +1 -1
  274. package/lib/commonjs/features/journey/ScaleBunScrollView.js +20 -20
  275. package/lib/commonjs/features/journey/ScaleBunScrollView.js.map +1 -1
  276. package/lib/commonjs/features/journey/ScaleBunSectionList.js +15 -15
  277. package/lib/commonjs/features/journey/ScaleBunSectionList.js.map +1 -1
  278. package/lib/commonjs/features/journey/calibrationContext.js +12 -12
  279. package/lib/commonjs/features/journey/calibrationContext.js.map +1 -1
  280. package/lib/commonjs/features/journey/gestureBuffer.js +9 -9
  281. package/lib/commonjs/features/journey/gestureBuffer.js.map +1 -1
  282. package/lib/commonjs/features/journey/gestureDetector.js +10 -10
  283. package/lib/commonjs/features/journey/gestureDetector.js.map +1 -1
  284. package/lib/commonjs/features/journey/impression.js +18 -18
  285. package/lib/commonjs/features/journey/impression.js.map +1 -1
  286. package/lib/commonjs/features/journey/journeyManager.js +6 -6
  287. package/lib/commonjs/features/journey/journeyManager.js.map +1 -1
  288. package/lib/commonjs/features/journey/journeyTypes.js +5 -5
  289. package/lib/commonjs/features/journey/journeyTypes.js.map +1 -1
  290. package/lib/commonjs/features/journey/navDetector.js +7 -7
  291. package/lib/commonjs/features/journey/navDetector.js.map +1 -1
  292. package/lib/commonjs/features/journey/screenshotHelper.js +7 -7
  293. package/lib/commonjs/features/journey/screenshotHelper.js.map +1 -1
  294. package/lib/commonjs/features/journey/targetRegistry.js +17 -17
  295. package/lib/commonjs/features/journey/targetRegistry.js.map +1 -1
  296. package/lib/commonjs/features/journey/touchTarget.js +29 -29
  297. package/lib/commonjs/features/journey/touchTarget.js.map +1 -1
  298. package/lib/commonjs/features/navigation/AutoScreenDetector.js +114 -71
  299. package/lib/commonjs/features/navigation/AutoScreenDetector.js.map +1 -1
  300. package/lib/commonjs/features/network/NetworkFeature.js +23 -23
  301. package/lib/commonjs/features/network/NetworkFeature.js.map +1 -1
  302. package/lib/commonjs/features/network/index.js +5 -5
  303. package/lib/commonjs/features/network/index.js.map +1 -1
  304. package/lib/commonjs/features/ota/OtaEventEmitter.js +21 -21
  305. package/lib/commonjs/features/ota/OtaEventEmitter.js.map +1 -1
  306. package/lib/commonjs/features/ota/OtaOrchestrator.js +595 -42
  307. package/lib/commonjs/features/ota/OtaOrchestrator.js.map +1 -1
  308. package/lib/commonjs/features/ota/OtaTypes.js.map +1 -1
  309. package/lib/commonjs/features/ota/deviceAttributes.js +48 -0
  310. package/lib/commonjs/features/ota/deviceAttributes.js.map +1 -0
  311. package/lib/commonjs/features/ota/environment.js +172 -0
  312. package/lib/commonjs/features/ota/environment.js.map +1 -0
  313. package/lib/commonjs/features/ota/geoCountry.js +93 -0
  314. package/lib/commonjs/features/ota/geoCountry.js.map +1 -0
  315. package/lib/commonjs/features/ota/retry.js +96 -0
  316. package/lib/commonjs/features/ota/retry.js.map +1 -0
  317. package/lib/commonjs/features/ota/signature.js +115 -0
  318. package/lib/commonjs/features/ota/signature.js.map +1 -0
  319. package/lib/commonjs/features/ota/useOtaUpdate.js +18 -15
  320. package/lib/commonjs/features/ota/useOtaUpdate.js.map +1 -1
  321. package/lib/commonjs/features/performance/PerformanceFeature.js +49 -35
  322. package/lib/commonjs/features/performance/PerformanceFeature.js.map +1 -1
  323. package/lib/commonjs/features/performance/collectors/AppLaunchCollector.js +13 -13
  324. package/lib/commonjs/features/performance/collectors/AppLaunchCollector.js.map +1 -1
  325. package/lib/commonjs/features/performance/collectors/AutoScreenLoadCollector.js +32 -32
  326. package/lib/commonjs/features/performance/collectors/AutoScreenLoadCollector.js.map +1 -1
  327. package/lib/commonjs/features/performance/collectors/CustomTraceCollector.js +13 -13
  328. package/lib/commonjs/features/performance/collectors/CustomTraceCollector.js.map +1 -1
  329. package/lib/commonjs/features/performance/collectors/FrameMetricsCollector.js +80 -20
  330. package/lib/commonjs/features/performance/collectors/FrameMetricsCollector.js.map +1 -1
  331. package/lib/commonjs/features/performance/collectors/JsStallCollector.js +12 -12
  332. package/lib/commonjs/features/performance/collectors/JsStallCollector.js.map +1 -1
  333. package/lib/commonjs/features/performance/collectors/NetworkPerfCollector.js +10 -10
  334. package/lib/commonjs/features/performance/collectors/NetworkPerfCollector.js.map +1 -1
  335. package/lib/commonjs/features/performance/collectors/ScreenLoadCollector.js +11 -11
  336. package/lib/commonjs/features/performance/collectors/ScreenLoadCollector.js.map +1 -1
  337. package/lib/commonjs/features/performance/collectors/UiHangCollector.js +5 -5
  338. package/lib/commonjs/features/performance/collectors/UiHangCollector.js.map +1 -1
  339. package/lib/commonjs/features/performance/config.js +11 -11
  340. package/lib/commonjs/features/performance/config.js.map +1 -1
  341. package/lib/commonjs/features/performance/getNativePerformanceModule.js +11 -11
  342. package/lib/commonjs/features/performance/getNativePerformanceModule.js.map +1 -1
  343. package/lib/commonjs/features/performance/index.js.map +1 -1
  344. package/lib/commonjs/features/performance/metrics/MetricsEngine.js +13 -13
  345. package/lib/commonjs/features/performance/metrics/MetricsEngine.js.map +1 -1
  346. package/lib/commonjs/features/performance/models.js +8 -8
  347. package/lib/commonjs/features/performance/models.js.map +1 -1
  348. package/lib/commonjs/features/performance/transport/PerformanceTransport.js +31 -6
  349. package/lib/commonjs/features/performance/transport/PerformanceTransport.js.map +1 -1
  350. package/lib/commonjs/features/profiler/ProfilerFeature.js +53 -53
  351. package/lib/commonjs/features/profiler/ProfilerFeature.js.map +1 -1
  352. package/lib/commonjs/features/profiler/config.js +15 -15
  353. package/lib/commonjs/features/profiler/config.js.map +1 -1
  354. package/lib/commonjs/features/profiler/contracts.js +22 -22
  355. package/lib/commonjs/features/profiler/contracts.js.map +1 -1
  356. package/lib/commonjs/features/profiler/index.js.map +1 -1
  357. package/lib/commonjs/features/profiler/models.js +44 -44
  358. package/lib/commonjs/features/profiler/models.js.map +1 -1
  359. package/lib/commonjs/features/profiler/transport/ProfilerTransport.js +9 -9
  360. package/lib/commonjs/features/profiler/transport/ProfilerTransport.js.map +1 -1
  361. package/lib/commonjs/features/replay/bridge/adapters/bridgeAdapter.js +17 -17
  362. package/lib/commonjs/features/replay/bridge/adapters/bridgeAdapter.js.map +1 -1
  363. package/lib/commonjs/features/replay/bridge/eventEmitter.js +8 -8
  364. package/lib/commonjs/features/replay/bridge/eventEmitter.js.map +1 -1
  365. package/lib/commonjs/features/replay/bridge/nativeModule.js +20 -20
  366. package/lib/commonjs/features/replay/bridge/nativeModule.js.map +1 -1
  367. package/lib/commonjs/features/replay/core/clock/clock.js +6 -6
  368. package/lib/commonjs/features/replay/core/clock/clock.js.map +1 -1
  369. package/lib/commonjs/features/replay/core/config/defaults.js +2 -2
  370. package/lib/commonjs/features/replay/core/config/defaults.js.map +1 -1
  371. package/lib/commonjs/features/replay/core/config/validator.js +5 -5
  372. package/lib/commonjs/features/replay/core/config/validator.js.map +1 -1
  373. package/lib/commonjs/features/replay/core/env/environmentDetector.js +10 -10
  374. package/lib/commonjs/features/replay/core/env/environmentDetector.js.map +1 -1
  375. package/lib/commonjs/features/replay/core/errors/safeCall.js +4 -4
  376. package/lib/commonjs/features/replay/core/errors/safeCall.js.map +1 -1
  377. package/lib/commonjs/features/replay/core/ids/sessionId.js +5 -5
  378. package/lib/commonjs/features/replay/core/ids/sessionId.js.map +1 -1
  379. package/lib/commonjs/features/replay/core/logger/logger.js +5 -5
  380. package/lib/commonjs/features/replay/core/logger/logger.js.map +1 -1
  381. package/lib/commonjs/features/replay/core/privacy/privacyTypes.js +4 -4
  382. package/lib/commonjs/features/replay/core/privacy/privacyTypes.js.map +1 -1
  383. package/lib/commonjs/features/replay/core/queue/boundedQueue.js +5 -5
  384. package/lib/commonjs/features/replay/core/queue/boundedQueue.js.map +1 -1
  385. package/lib/commonjs/features/replay/integrations/alert/alertInstrumentation.js +59 -59
  386. package/lib/commonjs/features/replay/integrations/alert/alertInstrumentation.js.map +1 -1
  387. package/lib/commonjs/features/replay/integrations/logs/consoleIntegration.js +19 -19
  388. package/lib/commonjs/features/replay/integrations/logs/consoleIntegration.js.map +1 -1
  389. package/lib/commonjs/features/replay/integrations/network/networkAdapter.js +30 -30
  390. package/lib/commonjs/features/replay/integrations/network/networkAdapter.js.map +1 -1
  391. package/lib/commonjs/features/replay/integrations/react-navigation/navigationIntegration.js +19 -19
  392. package/lib/commonjs/features/replay/integrations/react-navigation/navigationIntegration.js.map +1 -1
  393. package/lib/commonjs/features/replay/integrations/touch/ReplayRoot.js +16 -16
  394. package/lib/commonjs/features/replay/integrations/touch/ReplayRoot.js.map +1 -1
  395. package/lib/commonjs/features/replay/pipeline/batching/batchManager.js +5 -5
  396. package/lib/commonjs/features/replay/pipeline/batching/batchManager.js.map +1 -1
  397. package/lib/commonjs/features/replay/pipeline/envelope/envelopeBuilder.js +5 -5
  398. package/lib/commonjs/features/replay/pipeline/envelope/envelopeBuilder.js.map +1 -1
  399. package/lib/commonjs/features/replay/pipeline/retry/retryPolicy.js +4 -4
  400. package/lib/commonjs/features/replay/pipeline/retry/retryPolicy.js.map +1 -1
  401. package/lib/commonjs/features/replay/pipeline/transport/transportTypes.js +7 -7
  402. package/lib/commonjs/features/replay/pipeline/transport/transportTypes.js.map +1 -1
  403. package/lib/commonjs/features/replay/public/api.js +21 -21
  404. package/lib/commonjs/features/replay/public/api.js.map +1 -1
  405. package/lib/commonjs/features/replay/public/enums.js +5 -5
  406. package/lib/commonjs/features/replay/public/enums.js.map +1 -1
  407. package/lib/commonjs/features/replay/public/types.js.map +1 -1
  408. package/lib/commonjs/features/replay/replay/breadcrumbs/breadcrumbCollector.js +5 -5
  409. package/lib/commonjs/features/replay/replay/breadcrumbs/breadcrumbCollector.js.map +1 -1
  410. package/lib/commonjs/features/replay/replay/frames/frameModels.js +4 -4
  411. package/lib/commonjs/features/replay/replay/frames/frameModels.js.map +1 -1
  412. package/lib/commonjs/features/replay/replay/session/sessionOrchestrator.js +10 -10
  413. package/lib/commonjs/features/replay/replay/session/sessionOrchestrator.js.map +1 -1
  414. package/lib/commonjs/features/replay/replay/timeline/timelineModels.js +4 -4
  415. package/lib/commonjs/features/replay/replay/timeline/timelineModels.js.map +1 -1
  416. package/lib/commonjs/features/replay/transport/BackendReplayTransport.js +13 -13
  417. package/lib/commonjs/features/replay/transport/BackendReplayTransport.js.map +1 -1
  418. package/lib/commonjs/features/replay/transport/CompositeReplayTransport.js +8 -8
  419. package/lib/commonjs/features/replay/transport/CompositeReplayTransport.js.map +1 -1
  420. package/lib/commonjs/features/replay/transport/DesktopReplayTransport.js +14 -14
  421. package/lib/commonjs/features/replay/transport/DesktopReplayTransport.js.map +1 -1
  422. package/lib/commonjs/features/replay/transport/ReplayTransport.js.map +1 -1
  423. package/lib/commonjs/features/session/BackendSessionAdapter.js +486 -164
  424. package/lib/commonjs/features/session/BackendSessionAdapter.js.map +1 -1
  425. package/lib/commonjs/features/session/DesktopSessionTransport.js +14 -14
  426. package/lib/commonjs/features/session/DesktopSessionTransport.js.map +1 -1
  427. package/lib/commonjs/features/session/JourneyEventPipeline.js +20 -19
  428. package/lib/commonjs/features/session/JourneyEventPipeline.js.map +1 -1
  429. package/lib/commonjs/features/session/ReplayCaptureManager.js +169 -123
  430. package/lib/commonjs/features/session/ReplayCaptureManager.js.map +1 -1
  431. package/lib/commonjs/features/session/ScrollTracker.js +17 -17
  432. package/lib/commonjs/features/session/ScrollTracker.js.map +1 -1
  433. package/lib/commonjs/features/session/SessionManager.js +185 -155
  434. package/lib/commonjs/features/session/SessionManager.js.map +1 -1
  435. package/lib/commonjs/features/session/SyncDecisionEngine.js +23 -23
  436. package/lib/commonjs/features/session/SyncDecisionEngine.js.map +1 -1
  437. package/lib/commonjs/features/session/index.js.map +1 -1
  438. package/lib/commonjs/features/session/nativeCapture.js +9 -9
  439. package/lib/commonjs/features/session/nativeCapture.js.map +1 -1
  440. package/lib/commonjs/features/session/outboxFrameCapture.js +18 -17
  441. package/lib/commonjs/features/session/outboxFrameCapture.js.map +1 -1
  442. package/lib/commonjs/features/session/sessionTypes.js +44 -44
  443. package/lib/commonjs/features/session/sessionTypes.js.map +1 -1
  444. package/lib/commonjs/features/skan/skanBridge.js +11 -11
  445. package/lib/commonjs/features/skan/skanBridge.js.map +1 -1
  446. package/lib/commonjs/features/skan/skanConversionManager.js +23 -23
  447. package/lib/commonjs/features/skan/skanConversionManager.js.map +1 -1
  448. package/lib/commonjs/index.js +20 -0
  449. package/lib/commonjs/index.js.map +1 -1
  450. package/lib/commonjs/integrations/fetch/fetchInterceptor.js +21 -21
  451. package/lib/commonjs/integrations/fetch/fetchInterceptor.js.map +1 -1
  452. package/lib/commonjs/integrations/navigation/screenTracker.js.map +1 -1
  453. package/lib/commonjs/integrations/network/bodyCapture.js +69 -69
  454. package/lib/commonjs/integrations/network/bodyCapture.js.map +1 -1
  455. package/lib/commonjs/integrations/xhr/xhrInterceptor.js +24 -24
  456. package/lib/commonjs/integrations/xhr/xhrInterceptor.js.map +1 -1
  457. package/lib/commonjs/metro/index.js +37 -37
  458. package/lib/commonjs/metro/index.js.map +1 -1
  459. package/lib/commonjs/metro/optionalDependencyStub.js.map +1 -1
  460. package/lib/commonjs/metro/optionalModules.js +16 -16
  461. package/lib/commonjs/metro/optionalModules.js.map +1 -1
  462. package/lib/commonjs/pipeline/dispatcher/dispatcher.js.map +1 -1
  463. package/lib/commonjs/pipeline/envelope/envelope.js.map +1 -1
  464. package/lib/commonjs/pipeline/envelope/serializer.js.map +1 -1
  465. package/lib/commonjs/pipeline/processors/dedupe.js.map +1 -1
  466. package/lib/commonjs/pipeline/processors/enrich.js.map +1 -1
  467. package/lib/commonjs/pipeline/processors/sampling.js.map +1 -1
  468. package/lib/commonjs/pipeline/processors/sanitize.js.map +1 -1
  469. package/lib/commonjs/pipeline/queue/flushQueue.js +4 -4
  470. package/lib/commonjs/pipeline/queue/flushQueue.js.map +1 -1
  471. package/lib/commonjs/pipeline/queue/memoryQueue.js.map +1 -1
  472. package/lib/commonjs/pipeline/queue/persistentQueue.js +80 -80
  473. package/lib/commonjs/pipeline/queue/persistentQueue.js.map +1 -1
  474. package/lib/commonjs/pipeline/queue/queuePolicy.js.map +1 -1
  475. package/lib/commonjs/pipeline/scheduler/flushScheduler.js.map +1 -1
  476. package/lib/commonjs/pipeline/scheduler/retryPolicy.js +2 -2
  477. package/lib/commonjs/pipeline/scheduler/retryPolicy.js.map +1 -1
  478. package/lib/commonjs/public/ScaleBunErrorBoundary.js +79 -0
  479. package/lib/commonjs/public/ScaleBunErrorBoundary.js.map +1 -0
  480. package/lib/commonjs/public/ScaleBunFacade.js +424 -276
  481. package/lib/commonjs/public/ScaleBunFacade.js.map +1 -1
  482. package/lib/commonjs/public/ScaleBunProvider.js +3 -3
  483. package/lib/commonjs/public/ScaleBunProvider.js.map +1 -1
  484. package/lib/commonjs/public/typedTracker.js +36 -36
  485. package/lib/commonjs/public/typedTracker.js.map +1 -1
  486. package/lib/commonjs/public/types.js.map +1 -1
  487. package/lib/commonjs/push/PushManager.js +21 -21
  488. package/lib/commonjs/push/PushManager.js.map +1 -1
  489. package/lib/commonjs/push/adapters/ManualAdapter.js +14 -14
  490. package/lib/commonjs/push/adapters/ManualAdapter.js.map +1 -1
  491. package/lib/commonjs/push/adapters/NativeBridgeAdapter.js +17 -17
  492. package/lib/commonjs/push/adapters/NativeBridgeAdapter.js.map +1 -1
  493. package/lib/commonjs/push/adapters/NoopAdapter.js +5 -5
  494. package/lib/commonjs/push/adapters/NoopAdapter.js.map +1 -1
  495. package/lib/commonjs/push/adapters/RNFirebaseMessagingAdapter.js +17 -17
  496. package/lib/commonjs/push/adapters/RNFirebaseMessagingAdapter.js.map +1 -1
  497. package/lib/commonjs/push/adapters/loadMessaging.js +12 -12
  498. package/lib/commonjs/push/adapters/loadMessaging.js.map +1 -1
  499. package/lib/commonjs/push/adapters/loadNotifee.js +12 -12
  500. package/lib/commonjs/push/adapters/loadNotifee.js.map +1 -1
  501. package/lib/commonjs/push/adapters/selectAdapter.js +14 -14
  502. package/lib/commonjs/push/adapters/selectAdapter.js.map +1 -1
  503. package/lib/commonjs/push/detect/capabilities.js +19 -19
  504. package/lib/commonjs/push/detect/capabilities.js.map +1 -1
  505. package/lib/commonjs/push/index.js.map +1 -1
  506. package/lib/commonjs/push/native/nativeNotifications.js +32 -32
  507. package/lib/commonjs/push/native/nativeNotifications.js.map +1 -1
  508. package/lib/commonjs/push/types.js.map +1 -1
  509. package/lib/commonjs/specs/NativeReplaySdk.js +18 -18
  510. package/lib/commonjs/specs/NativeReplaySdk.js.map +1 -1
  511. package/lib/commonjs/specs/NativeScaleBunCrash.js +19 -19
  512. package/lib/commonjs/specs/NativeScaleBunCrash.js.map +1 -1
  513. package/lib/commonjs/specs/NativeScaleBunInstallReferrer.js +14 -14
  514. package/lib/commonjs/specs/NativeScaleBunInstallReferrer.js.map +1 -1
  515. package/lib/commonjs/specs/NativeScaleBunOta.js +21 -21
  516. package/lib/commonjs/specs/NativeScaleBunOta.js.map +1 -1
  517. package/lib/commonjs/specs/NativeScaleBunPerformance.js +15 -15
  518. package/lib/commonjs/specs/NativeScaleBunPerformance.js.map +1 -1
  519. package/lib/commonjs/specs/NativeScaleBunProfiler.js +16 -16
  520. package/lib/commonjs/specs/NativeScaleBunProfiler.js.map +1 -1
  521. package/lib/commonjs/specs/NativeScaleBunStorage.js +29 -29
  522. package/lib/commonjs/specs/NativeScaleBunStorage.js.map +1 -1
  523. package/lib/commonjs/storage/StorageBackend.js +83 -83
  524. package/lib/commonjs/storage/StorageBackend.js.map +1 -1
  525. package/lib/commonjs/storage/db/sqlite.js.map +1 -1
  526. package/lib/commonjs/storage/files/fileStore.js.map +1 -1
  527. package/lib/commonjs/transport/auth/sdkSession.js.map +1 -1
  528. package/lib/commonjs/transport/backoff/exponentialJitter.js +2 -2
  529. package/lib/commonjs/transport/backoff/exponentialJitter.js.map +1 -1
  530. package/lib/commonjs/transport/http/endpoints.js.map +1 -1
  531. package/lib/commonjs/transport/http/httpClient.js +3 -3
  532. package/lib/commonjs/transport/http/httpClient.js.map +1 -1
  533. package/lib/commonjs/transport/rateLimit/retryAfter.js.map +1 -1
  534. package/lib/commonjs/transport/signing/requestSigner.js.map +1 -1
  535. package/lib/commonjs/vendor/protocol/index.js +74 -74
  536. package/lib/commonjs/vendor/protocol/index.js.map +1 -1
  537. package/lib/commonjs/vendor/protocol/internal/validation.js +23 -23
  538. package/lib/commonjs/vendor/protocol/internal/validation.js.map +1 -1
  539. package/lib/commonjs/vendor/sdk-contracts/bridge.js.map +1 -1
  540. package/lib/commonjs/vendor/sdk-contracts/config.js.map +1 -1
  541. package/lib/commonjs/vendor/sdk-contracts/events.js.map +1 -1
  542. package/lib/commonjs/vendor/sdk-contracts/features.js.map +1 -1
  543. package/lib/commonjs/vendor/sdk-contracts/index.js.map +1 -1
  544. package/lib/commonjs/vendor/sdk-contracts/platform.js.map +1 -1
  545. package/lib/commonjs/vendor/sdk-contracts/session-boundary.js.map +1 -1
  546. package/lib/commonjs/vendor/sdk-contracts/session.js.map +1 -1
  547. package/lib/commonjs/vendor/vendor.manifest +10 -10
  548. package/lib/module/analytics/EventTracker.js +63 -35
  549. package/lib/module/analytics/EventTracker.js.map +1 -1
  550. package/lib/module/analytics/automaticEvents.js +7 -7
  551. package/lib/module/analytics/automaticEvents.js.map +1 -1
  552. package/lib/module/analytics/batching.js +24 -24
  553. package/lib/module/analytics/batching.js.map +1 -1
  554. package/lib/module/analytics/eventLane.js +13 -13
  555. package/lib/module/analytics/eventLane.js.map +1 -1
  556. package/lib/module/bootstrap/FeatureRegistry.js +17 -17
  557. package/lib/module/bootstrap/FeatureRegistry.js.map +1 -1
  558. package/lib/module/bootstrap/SDKBootstrapper.js +81 -54
  559. package/lib/module/bootstrap/SDKBootstrapper.js.map +1 -1
  560. package/lib/module/bucketing/fnv1a32.js +10 -10
  561. package/lib/module/bucketing/fnv1a32.js.map +1 -1
  562. package/lib/module/compat/codepush.js +6 -6
  563. package/lib/module/compat/codepush.js.map +1 -1
  564. package/lib/module/config/ConfigManager.js +20 -20
  565. package/lib/module/config/ConfigManager.js.map +1 -1
  566. package/lib/module/config/configTypes.js.map +1 -1
  567. package/lib/module/core/architecture.js +23 -23
  568. package/lib/module/core/architecture.js.map +1 -1
  569. package/lib/module/core/clock/now.js +2 -2
  570. package/lib/module/core/clock/now.js.map +1 -1
  571. package/lib/module/core/config/defaults.js.map +1 -1
  572. package/lib/module/core/config/schema.js +112 -104
  573. package/lib/module/core/config/schema.js.map +1 -1
  574. package/lib/module/core/constants/endpoints.js +7 -7
  575. package/lib/module/core/constants/endpoints.js.map +1 -1
  576. package/lib/module/core/constants/timings.js +5 -5
  577. package/lib/module/core/constants/timings.js.map +1 -1
  578. package/lib/module/core/constants/version.js +4 -4
  579. package/lib/module/core/constants/version.js.map +1 -1
  580. package/lib/module/core/context/app.js.map +1 -1
  581. package/lib/module/core/context/device.js +119 -0
  582. package/lib/module/core/context/device.js.map +1 -1
  583. package/lib/module/core/context/session.js.map +1 -1
  584. package/lib/module/core/context/user.js.map +1 -1
  585. package/lib/module/core/contracts/IFeature.js.map +1 -1
  586. package/lib/module/core/di/container.js.map +1 -1
  587. package/lib/module/core/encoding/base64.js +9 -9
  588. package/lib/module/core/encoding/base64.js.map +1 -1
  589. package/lib/module/core/id/deviceId.js +4 -4
  590. package/lib/module/core/id/deviceId.js.map +1 -1
  591. package/lib/module/core/id/sessionId.js.map +1 -1
  592. package/lib/module/core/lifecycle/appLifecycle.js.map +1 -1
  593. package/lib/module/core/lifecycle/crashSafe.js +3 -3
  594. package/lib/module/core/lifecycle/crashSafe.js.map +1 -1
  595. package/lib/module/core/logger/errorClassification.js +30 -30
  596. package/lib/module/core/logger/errorClassification.js.map +1 -1
  597. package/lib/module/core/logger/internalLogger.js.map +1 -1
  598. package/lib/module/core/logger/levels.js.map +1 -1
  599. package/lib/module/crypto/hmacSha256.js +6 -6
  600. package/lib/module/crypto/hmacSha256.js.map +1 -1
  601. package/lib/module/debug/bootstrap.js +26 -26
  602. package/lib/module/debug/bootstrap.js.map +1 -1
  603. package/lib/module/debug/commands.js +10 -10
  604. package/lib/module/debug/commands.js.map +1 -1
  605. package/lib/module/debug/configBuilder.js +6 -6
  606. package/lib/module/debug/configBuilder.js.map +1 -1
  607. package/lib/module/debug/exportBridge.js +17 -17
  608. package/lib/module/debug/exportBridge.js.map +1 -1
  609. package/lib/module/debug/hostResolver.js +10 -10
  610. package/lib/module/debug/hostResolver.js.map +1 -1
  611. package/lib/module/debug/index.js +5 -5
  612. package/lib/module/debug/index.js.map +1 -1
  613. package/lib/module/debug/metrics.js.map +1 -1
  614. package/lib/module/debug/perf.js +11 -11
  615. package/lib/module/debug/perf.js.map +1 -1
  616. package/lib/module/debug/perfWiring.js +10 -10
  617. package/lib/module/debug/perfWiring.js.map +1 -1
  618. package/lib/module/debug/protocol.js +11 -11
  619. package/lib/module/debug/protocol.js.map +1 -1
  620. package/lib/module/debug/redaction.js +26 -26
  621. package/lib/module/debug/redaction.js.map +1 -1
  622. package/lib/module/debug/replayWiring.js +13 -12
  623. package/lib/module/debug/replayWiring.js.map +1 -1
  624. package/lib/module/debug/screenTracking.js +7 -7
  625. package/lib/module/debug/screenTracking.js.map +1 -1
  626. package/lib/module/debug/sessionWiring.js +8 -8
  627. package/lib/module/debug/sessionWiring.js.map +1 -1
  628. package/lib/module/debug/stream.js +26 -26
  629. package/lib/module/debug/stream.js.map +1 -1
  630. package/lib/module/debug/transport.js +24 -24
  631. package/lib/module/debug/transport.js.map +1 -1
  632. package/lib/module/features/bugreport/BugReportFeature.js +105 -0
  633. package/lib/module/features/bugreport/BugReportFeature.js.map +1 -0
  634. package/lib/module/features/bugreport/buildPayload.js +91 -0
  635. package/lib/module/features/bugreport/buildPayload.js.map +1 -0
  636. package/lib/module/features/bugreport/index.js.map +1 -1
  637. package/lib/module/features/crash/CrashFeature.js +27 -9
  638. package/lib/module/features/crash/CrashFeature.js.map +1 -1
  639. package/lib/module/features/crash/CrashReporter.js +53 -29
  640. package/lib/module/features/crash/CrashReporter.js.map +1 -1
  641. package/lib/module/features/crash/NativeCrashFeature.js +7 -7
  642. package/lib/module/features/crash/NativeCrashFeature.js.map +1 -1
  643. package/lib/module/features/crash/capture.js +28 -9
  644. package/lib/module/features/crash/capture.js.map +1 -1
  645. package/lib/module/features/crash/globalHandler.js +11 -11
  646. package/lib/module/features/crash/globalHandler.js.map +1 -1
  647. package/lib/module/features/crash/index.js.map +1 -1
  648. package/lib/module/features/crash/nativeCrashBridge.js +177 -44
  649. package/lib/module/features/crash/nativeCrashBridge.js.map +1 -1
  650. package/lib/module/features/crash/rejectionHandler.js +116 -0
  651. package/lib/module/features/crash/rejectionHandler.js.map +1 -0
  652. package/lib/module/features/engage/EngageAnchor.js +3 -3
  653. package/lib/module/features/engage/EngageAnchor.js.map +1 -1
  654. package/lib/module/features/engage/EngageArchetypeRenderers.js +19 -19
  655. package/lib/module/features/engage/EngageArchetypeRenderers.js.map +1 -1
  656. package/lib/module/features/engage/EngageInAppView.js +35 -35
  657. package/lib/module/features/engage/EngageInAppView.js.map +1 -1
  658. package/lib/module/features/engage/EngageInlinePlacement.js +7 -7
  659. package/lib/module/features/engage/EngageInlinePlacement.js.map +1 -1
  660. package/lib/module/features/engage/EngagePromptProvider.js +124 -88
  661. package/lib/module/features/engage/EngagePromptProvider.js.map +1 -1
  662. package/lib/module/features/engage/EngagePromptView.js +65 -32
  663. package/lib/module/features/engage/EngagePromptView.js.map +1 -1
  664. package/lib/module/features/engage/EngageTransport.js +79 -79
  665. package/lib/module/features/engage/EngageTransport.js.map +1 -1
  666. package/lib/module/features/engage/EngageVariantContent.js +10 -10
  667. package/lib/module/features/engage/EngageVariantContent.js.map +1 -1
  668. package/lib/module/features/engage/__test-support__/reactNativeStub.js +7 -7
  669. package/lib/module/features/engage/__test-support__/reactNativeStub.js.map +1 -1
  670. package/lib/module/features/engage/attachmentCapture.js +12 -12
  671. package/lib/module/features/engage/attachmentCapture.js.map +1 -1
  672. package/lib/module/features/engage/captureVoiceAttachment.js +20 -20
  673. package/lib/module/features/engage/captureVoiceAttachment.js.map +1 -1
  674. package/lib/module/features/engage/engageCoachmarkTour.js +4 -4
  675. package/lib/module/features/engage/engageCoachmarkTour.js.map +1 -1
  676. package/lib/module/features/engage/engageGameLogic.js.map +1 -1
  677. package/lib/module/features/engage/engageMediaSizing.js +15 -15
  678. package/lib/module/features/engage/engageMediaSizing.js.map +1 -1
  679. package/lib/module/features/engage/engageMultiStepSheet.js +6 -6
  680. package/lib/module/features/engage/engageMultiStepSheet.js.map +1 -1
  681. package/lib/module/features/engage/engageOutcome.js +6 -6
  682. package/lib/module/features/engage/engageOutcome.js.map +1 -1
  683. package/lib/module/features/engage/engagePushTokenBridge.js +20 -20
  684. package/lib/module/features/engage/engagePushTokenBridge.js.map +1 -1
  685. package/lib/module/features/engage/engageShakeBridge.js +16 -16
  686. package/lib/module/features/engage/engageShakeBridge.js.map +1 -1
  687. package/lib/module/features/engage/engageSignals.js.map +1 -1
  688. package/lib/module/features/engage/engageStoreReviewBridge.js +19 -19
  689. package/lib/module/features/engage/engageStoreReviewBridge.js.map +1 -1
  690. package/lib/module/features/engage/engageTestSeenStore.js +29 -29
  691. package/lib/module/features/engage/engageTestSeenStore.js.map +1 -1
  692. package/lib/module/features/engage/engageThrottle.js +117 -86
  693. package/lib/module/features/engage/engageThrottle.js.map +1 -1
  694. package/lib/module/features/engage/engageTriggerEngine.js +17 -7
  695. package/lib/module/features/engage/engageTriggerEngine.js.map +1 -1
  696. package/lib/module/features/engage/engageTypes.js +33 -33
  697. package/lib/module/features/engage/engageTypes.js.map +1 -1
  698. package/lib/module/features/engage/engageVariantResolver.js +13 -13
  699. package/lib/module/features/engage/engageVariantResolver.js.map +1 -1
  700. package/lib/module/features/engage/engageWindowInsets.js +10 -10
  701. package/lib/module/features/engage/engageWindowInsets.js.map +1 -1
  702. package/lib/module/features/engage/gestureTriggerDetector.js.map +1 -1
  703. package/lib/module/features/engage/transcription/TranscriptionProvider.js +16 -16
  704. package/lib/module/features/engage/transcription/TranscriptionProvider.js.map +1 -1
  705. package/lib/module/features/install-referrer/installReferrer.js +26 -26
  706. package/lib/module/features/install-referrer/installReferrer.js.map +1 -1
  707. package/lib/module/features/journey/ScaleBunDebugRoot.js +30 -29
  708. package/lib/module/features/journey/ScaleBunDebugRoot.js.map +1 -1
  709. package/lib/module/features/journey/ScaleBunFlatList.js +15 -15
  710. package/lib/module/features/journey/ScaleBunFlatList.js.map +1 -1
  711. package/lib/module/features/journey/ScaleBunImpression.js +22 -22
  712. package/lib/module/features/journey/ScaleBunImpression.js.map +1 -1
  713. package/lib/module/features/journey/ScaleBunScrollView.js +20 -20
  714. package/lib/module/features/journey/ScaleBunScrollView.js.map +1 -1
  715. package/lib/module/features/journey/ScaleBunSectionList.js +15 -15
  716. package/lib/module/features/journey/ScaleBunSectionList.js.map +1 -1
  717. package/lib/module/features/journey/calibrationContext.js +12 -12
  718. package/lib/module/features/journey/calibrationContext.js.map +1 -1
  719. package/lib/module/features/journey/gestureBuffer.js +9 -9
  720. package/lib/module/features/journey/gestureBuffer.js.map +1 -1
  721. package/lib/module/features/journey/gestureDetector.js +10 -10
  722. package/lib/module/features/journey/gestureDetector.js.map +1 -1
  723. package/lib/module/features/journey/impression.js +18 -18
  724. package/lib/module/features/journey/impression.js.map +1 -1
  725. package/lib/module/features/journey/journeyManager.js +6 -6
  726. package/lib/module/features/journey/journeyManager.js.map +1 -1
  727. package/lib/module/features/journey/journeyTypes.js +5 -5
  728. package/lib/module/features/journey/journeyTypes.js.map +1 -1
  729. package/lib/module/features/journey/navDetector.js +7 -7
  730. package/lib/module/features/journey/navDetector.js.map +1 -1
  731. package/lib/module/features/journey/screenshotHelper.js +7 -7
  732. package/lib/module/features/journey/screenshotHelper.js.map +1 -1
  733. package/lib/module/features/journey/targetRegistry.js +17 -17
  734. package/lib/module/features/journey/targetRegistry.js.map +1 -1
  735. package/lib/module/features/journey/touchTarget.js +29 -29
  736. package/lib/module/features/journey/touchTarget.js.map +1 -1
  737. package/lib/module/features/navigation/AutoScreenDetector.js +114 -71
  738. package/lib/module/features/navigation/AutoScreenDetector.js.map +1 -1
  739. package/lib/module/features/network/NetworkFeature.js +23 -23
  740. package/lib/module/features/network/NetworkFeature.js.map +1 -1
  741. package/lib/module/features/network/index.js +5 -5
  742. package/lib/module/features/network/index.js.map +1 -1
  743. package/lib/module/features/ota/OtaEventEmitter.js +21 -21
  744. package/lib/module/features/ota/OtaEventEmitter.js.map +1 -1
  745. package/lib/module/features/ota/OtaOrchestrator.js +596 -43
  746. package/lib/module/features/ota/OtaOrchestrator.js.map +1 -1
  747. package/lib/module/features/ota/OtaTypes.js.map +1 -1
  748. package/lib/module/features/ota/deviceAttributes.js +43 -0
  749. package/lib/module/features/ota/deviceAttributes.js.map +1 -0
  750. package/lib/module/features/ota/environment.js +165 -0
  751. package/lib/module/features/ota/environment.js.map +1 -0
  752. package/lib/module/features/ota/geoCountry.js +86 -0
  753. package/lib/module/features/ota/geoCountry.js.map +1 -0
  754. package/lib/module/features/ota/retry.js +87 -0
  755. package/lib/module/features/ota/retry.js.map +1 -0
  756. package/lib/module/features/ota/signature.js +109 -0
  757. package/lib/module/features/ota/signature.js.map +1 -0
  758. package/lib/module/features/ota/useOtaUpdate.js +18 -15
  759. package/lib/module/features/ota/useOtaUpdate.js.map +1 -1
  760. package/lib/module/features/performance/PerformanceFeature.js +49 -35
  761. package/lib/module/features/performance/PerformanceFeature.js.map +1 -1
  762. package/lib/module/features/performance/collectors/AppLaunchCollector.js +13 -13
  763. package/lib/module/features/performance/collectors/AppLaunchCollector.js.map +1 -1
  764. package/lib/module/features/performance/collectors/AutoScreenLoadCollector.js +32 -32
  765. package/lib/module/features/performance/collectors/AutoScreenLoadCollector.js.map +1 -1
  766. package/lib/module/features/performance/collectors/CustomTraceCollector.js +13 -13
  767. package/lib/module/features/performance/collectors/CustomTraceCollector.js.map +1 -1
  768. package/lib/module/features/performance/collectors/FrameMetricsCollector.js +81 -21
  769. package/lib/module/features/performance/collectors/FrameMetricsCollector.js.map +1 -1
  770. package/lib/module/features/performance/collectors/JsStallCollector.js +12 -12
  771. package/lib/module/features/performance/collectors/JsStallCollector.js.map +1 -1
  772. package/lib/module/features/performance/collectors/NetworkPerfCollector.js +10 -10
  773. package/lib/module/features/performance/collectors/NetworkPerfCollector.js.map +1 -1
  774. package/lib/module/features/performance/collectors/ScreenLoadCollector.js +11 -11
  775. package/lib/module/features/performance/collectors/ScreenLoadCollector.js.map +1 -1
  776. package/lib/module/features/performance/collectors/UiHangCollector.js +5 -5
  777. package/lib/module/features/performance/collectors/UiHangCollector.js.map +1 -1
  778. package/lib/module/features/performance/config.js +11 -11
  779. package/lib/module/features/performance/config.js.map +1 -1
  780. package/lib/module/features/performance/getNativePerformanceModule.js +11 -11
  781. package/lib/module/features/performance/getNativePerformanceModule.js.map +1 -1
  782. package/lib/module/features/performance/index.js +2 -2
  783. package/lib/module/features/performance/index.js.map +1 -1
  784. package/lib/module/features/performance/metrics/MetricsEngine.js +13 -13
  785. package/lib/module/features/performance/metrics/MetricsEngine.js.map +1 -1
  786. package/lib/module/features/performance/models.js +8 -8
  787. package/lib/module/features/performance/models.js.map +1 -1
  788. package/lib/module/features/performance/transport/PerformanceTransport.js +31 -6
  789. package/lib/module/features/performance/transport/PerformanceTransport.js.map +1 -1
  790. package/lib/module/features/profiler/ProfilerFeature.js +53 -53
  791. package/lib/module/features/profiler/ProfilerFeature.js.map +1 -1
  792. package/lib/module/features/profiler/config.js +15 -15
  793. package/lib/module/features/profiler/config.js.map +1 -1
  794. package/lib/module/features/profiler/contracts.js +22 -22
  795. package/lib/module/features/profiler/contracts.js.map +1 -1
  796. package/lib/module/features/profiler/index.js +4 -4
  797. package/lib/module/features/profiler/index.js.map +1 -1
  798. package/lib/module/features/profiler/models.js +44 -44
  799. package/lib/module/features/profiler/models.js.map +1 -1
  800. package/lib/module/features/profiler/transport/ProfilerTransport.js +9 -9
  801. package/lib/module/features/profiler/transport/ProfilerTransport.js.map +1 -1
  802. package/lib/module/features/replay/bridge/adapters/bridgeAdapter.js +17 -17
  803. package/lib/module/features/replay/bridge/adapters/bridgeAdapter.js.map +1 -1
  804. package/lib/module/features/replay/bridge/eventEmitter.js +8 -8
  805. package/lib/module/features/replay/bridge/eventEmitter.js.map +1 -1
  806. package/lib/module/features/replay/bridge/nativeModule.js +20 -20
  807. package/lib/module/features/replay/bridge/nativeModule.js.map +1 -1
  808. package/lib/module/features/replay/core/clock/clock.js +6 -6
  809. package/lib/module/features/replay/core/clock/clock.js.map +1 -1
  810. package/lib/module/features/replay/core/config/defaults.js +2 -2
  811. package/lib/module/features/replay/core/config/defaults.js.map +1 -1
  812. package/lib/module/features/replay/core/config/validator.js +5 -5
  813. package/lib/module/features/replay/core/config/validator.js.map +1 -1
  814. package/lib/module/features/replay/core/env/environmentDetector.js +10 -10
  815. package/lib/module/features/replay/core/env/environmentDetector.js.map +1 -1
  816. package/lib/module/features/replay/core/errors/safeCall.js +4 -4
  817. package/lib/module/features/replay/core/errors/safeCall.js.map +1 -1
  818. package/lib/module/features/replay/core/ids/sessionId.js +5 -5
  819. package/lib/module/features/replay/core/ids/sessionId.js.map +1 -1
  820. package/lib/module/features/replay/core/logger/logger.js +5 -5
  821. package/lib/module/features/replay/core/logger/logger.js.map +1 -1
  822. package/lib/module/features/replay/core/privacy/privacyTypes.js +4 -4
  823. package/lib/module/features/replay/core/privacy/privacyTypes.js.map +1 -1
  824. package/lib/module/features/replay/core/queue/boundedQueue.js +5 -5
  825. package/lib/module/features/replay/core/queue/boundedQueue.js.map +1 -1
  826. package/lib/module/features/replay/integrations/alert/alertInstrumentation.js +59 -59
  827. package/lib/module/features/replay/integrations/alert/alertInstrumentation.js.map +1 -1
  828. package/lib/module/features/replay/integrations/logs/consoleIntegration.js +19 -19
  829. package/lib/module/features/replay/integrations/logs/consoleIntegration.js.map +1 -1
  830. package/lib/module/features/replay/integrations/network/networkAdapter.js +30 -30
  831. package/lib/module/features/replay/integrations/network/networkAdapter.js.map +1 -1
  832. package/lib/module/features/replay/integrations/react-navigation/navigationIntegration.js +19 -19
  833. package/lib/module/features/replay/integrations/react-navigation/navigationIntegration.js.map +1 -1
  834. package/lib/module/features/replay/integrations/touch/ReplayRoot.js +16 -16
  835. package/lib/module/features/replay/integrations/touch/ReplayRoot.js.map +1 -1
  836. package/lib/module/features/replay/pipeline/batching/batchManager.js +5 -5
  837. package/lib/module/features/replay/pipeline/batching/batchManager.js.map +1 -1
  838. package/lib/module/features/replay/pipeline/envelope/envelopeBuilder.js +5 -5
  839. package/lib/module/features/replay/pipeline/envelope/envelopeBuilder.js.map +1 -1
  840. package/lib/module/features/replay/pipeline/retry/retryPolicy.js +4 -4
  841. package/lib/module/features/replay/pipeline/retry/retryPolicy.js.map +1 -1
  842. package/lib/module/features/replay/pipeline/transport/transportTypes.js +7 -7
  843. package/lib/module/features/replay/pipeline/transport/transportTypes.js.map +1 -1
  844. package/lib/module/features/replay/public/api.js +21 -21
  845. package/lib/module/features/replay/public/api.js.map +1 -1
  846. package/lib/module/features/replay/public/enums.js +5 -5
  847. package/lib/module/features/replay/public/enums.js.map +1 -1
  848. package/lib/module/features/replay/public/types.js.map +1 -1
  849. package/lib/module/features/replay/replay/breadcrumbs/breadcrumbCollector.js +5 -5
  850. package/lib/module/features/replay/replay/breadcrumbs/breadcrumbCollector.js.map +1 -1
  851. package/lib/module/features/replay/replay/frames/frameModels.js +4 -4
  852. package/lib/module/features/replay/replay/frames/frameModels.js.map +1 -1
  853. package/lib/module/features/replay/replay/session/sessionOrchestrator.js +10 -10
  854. package/lib/module/features/replay/replay/session/sessionOrchestrator.js.map +1 -1
  855. package/lib/module/features/replay/replay/timeline/timelineModels.js +4 -4
  856. package/lib/module/features/replay/replay/timeline/timelineModels.js.map +1 -1
  857. package/lib/module/features/replay/transport/BackendReplayTransport.js +13 -13
  858. package/lib/module/features/replay/transport/BackendReplayTransport.js.map +1 -1
  859. package/lib/module/features/replay/transport/CompositeReplayTransport.js +8 -8
  860. package/lib/module/features/replay/transport/CompositeReplayTransport.js.map +1 -1
  861. package/lib/module/features/replay/transport/DesktopReplayTransport.js +14 -14
  862. package/lib/module/features/replay/transport/DesktopReplayTransport.js.map +1 -1
  863. package/lib/module/features/replay/transport/ReplayTransport.js.map +1 -1
  864. package/lib/module/features/session/BackendSessionAdapter.js +485 -164
  865. package/lib/module/features/session/BackendSessionAdapter.js.map +1 -1
  866. package/lib/module/features/session/DesktopSessionTransport.js +14 -14
  867. package/lib/module/features/session/DesktopSessionTransport.js.map +1 -1
  868. package/lib/module/features/session/JourneyEventPipeline.js +20 -19
  869. package/lib/module/features/session/JourneyEventPipeline.js.map +1 -1
  870. package/lib/module/features/session/ReplayCaptureManager.js +169 -123
  871. package/lib/module/features/session/ReplayCaptureManager.js.map +1 -1
  872. package/lib/module/features/session/ScrollTracker.js +17 -17
  873. package/lib/module/features/session/ScrollTracker.js.map +1 -1
  874. package/lib/module/features/session/SessionManager.js +185 -155
  875. package/lib/module/features/session/SessionManager.js.map +1 -1
  876. package/lib/module/features/session/SyncDecisionEngine.js +23 -23
  877. package/lib/module/features/session/SyncDecisionEngine.js.map +1 -1
  878. package/lib/module/features/session/index.js +5 -5
  879. package/lib/module/features/session/index.js.map +1 -1
  880. package/lib/module/features/session/nativeCapture.js +9 -10
  881. package/lib/module/features/session/nativeCapture.js.map +1 -1
  882. package/lib/module/features/session/outboxFrameCapture.js +18 -17
  883. package/lib/module/features/session/outboxFrameCapture.js.map +1 -1
  884. package/lib/module/features/session/sessionTypes.js +44 -44
  885. package/lib/module/features/session/sessionTypes.js.map +1 -1
  886. package/lib/module/features/skan/skanBridge.js +11 -11
  887. package/lib/module/features/skan/skanBridge.js.map +1 -1
  888. package/lib/module/features/skan/skanConversionManager.js +23 -23
  889. package/lib/module/features/skan/skanConversionManager.js.map +1 -1
  890. package/lib/module/index.js +19 -10
  891. package/lib/module/index.js.map +1 -1
  892. package/lib/module/integrations/fetch/fetchInterceptor.js +21 -21
  893. package/lib/module/integrations/fetch/fetchInterceptor.js.map +1 -1
  894. package/lib/module/integrations/navigation/screenTracker.js.map +1 -1
  895. package/lib/module/integrations/network/bodyCapture.js +69 -69
  896. package/lib/module/integrations/network/bodyCapture.js.map +1 -1
  897. package/lib/module/integrations/xhr/xhrInterceptor.js +24 -24
  898. package/lib/module/integrations/xhr/xhrInterceptor.js.map +1 -1
  899. package/lib/module/metro/index.js +37 -37
  900. package/lib/module/metro/index.js.map +1 -1
  901. package/lib/module/metro/optionalDependencyStub.js +11 -11
  902. package/lib/module/metro/optionalDependencyStub.js.map +1 -1
  903. package/lib/module/metro/optionalModules.js +16 -16
  904. package/lib/module/metro/optionalModules.js.map +1 -1
  905. package/lib/module/pipeline/dispatcher/dispatcher.js.map +1 -1
  906. package/lib/module/pipeline/envelope/envelope.js.map +1 -1
  907. package/lib/module/pipeline/envelope/serializer.js.map +1 -1
  908. package/lib/module/pipeline/processors/dedupe.js.map +1 -1
  909. package/lib/module/pipeline/processors/enrich.js.map +1 -1
  910. package/lib/module/pipeline/processors/sampling.js.map +1 -1
  911. package/lib/module/pipeline/processors/sanitize.js.map +1 -1
  912. package/lib/module/pipeline/queue/flushQueue.js +4 -4
  913. package/lib/module/pipeline/queue/flushQueue.js.map +1 -1
  914. package/lib/module/pipeline/queue/memoryQueue.js.map +1 -1
  915. package/lib/module/pipeline/queue/persistentQueue.js +80 -80
  916. package/lib/module/pipeline/queue/persistentQueue.js.map +1 -1
  917. package/lib/module/pipeline/queue/queuePolicy.js.map +1 -1
  918. package/lib/module/pipeline/scheduler/flushScheduler.js.map +1 -1
  919. package/lib/module/pipeline/scheduler/retryPolicy.js +2 -2
  920. package/lib/module/pipeline/scheduler/retryPolicy.js.map +1 -1
  921. package/lib/module/public/ScaleBunErrorBoundary.js +70 -0
  922. package/lib/module/public/ScaleBunErrorBoundary.js.map +1 -0
  923. package/lib/module/public/ScaleBunFacade.js +424 -276
  924. package/lib/module/public/ScaleBunFacade.js.map +1 -1
  925. package/lib/module/public/ScaleBunProvider.js +3 -3
  926. package/lib/module/public/ScaleBunProvider.js.map +1 -1
  927. package/lib/module/public/typedTracker.js +36 -36
  928. package/lib/module/public/typedTracker.js.map +1 -1
  929. package/lib/module/public/types.js.map +1 -1
  930. package/lib/module/push/PushManager.js +21 -21
  931. package/lib/module/push/PushManager.js.map +1 -1
  932. package/lib/module/push/adapters/ManualAdapter.js +14 -14
  933. package/lib/module/push/adapters/ManualAdapter.js.map +1 -1
  934. package/lib/module/push/adapters/NativeBridgeAdapter.js +17 -17
  935. package/lib/module/push/adapters/NativeBridgeAdapter.js.map +1 -1
  936. package/lib/module/push/adapters/NoopAdapter.js +5 -5
  937. package/lib/module/push/adapters/NoopAdapter.js.map +1 -1
  938. package/lib/module/push/adapters/RNFirebaseMessagingAdapter.js +17 -17
  939. package/lib/module/push/adapters/RNFirebaseMessagingAdapter.js.map +1 -1
  940. package/lib/module/push/adapters/loadMessaging.js +12 -12
  941. package/lib/module/push/adapters/loadMessaging.js.map +1 -1
  942. package/lib/module/push/adapters/loadNotifee.js +12 -12
  943. package/lib/module/push/adapters/loadNotifee.js.map +1 -1
  944. package/lib/module/push/adapters/selectAdapter.js +14 -14
  945. package/lib/module/push/adapters/selectAdapter.js.map +1 -1
  946. package/lib/module/push/detect/capabilities.js +19 -19
  947. package/lib/module/push/detect/capabilities.js.map +1 -1
  948. package/lib/module/push/index.js +3 -3
  949. package/lib/module/push/index.js.map +1 -1
  950. package/lib/module/push/native/nativeNotifications.js +32 -32
  951. package/lib/module/push/native/nativeNotifications.js.map +1 -1
  952. package/lib/module/push/types.js.map +1 -1
  953. package/lib/module/specs/NativeReplaySdk.js +18 -18
  954. package/lib/module/specs/NativeReplaySdk.js.map +1 -1
  955. package/lib/module/specs/NativeScaleBunCrash.js +19 -19
  956. package/lib/module/specs/NativeScaleBunCrash.js.map +1 -1
  957. package/lib/module/specs/NativeScaleBunInstallReferrer.js +14 -14
  958. package/lib/module/specs/NativeScaleBunInstallReferrer.js.map +1 -1
  959. package/lib/module/specs/NativeScaleBunOta.js +21 -21
  960. package/lib/module/specs/NativeScaleBunOta.js.map +1 -1
  961. package/lib/module/specs/NativeScaleBunPerformance.js +15 -15
  962. package/lib/module/specs/NativeScaleBunPerformance.js.map +1 -1
  963. package/lib/module/specs/NativeScaleBunProfiler.js +16 -16
  964. package/lib/module/specs/NativeScaleBunProfiler.js.map +1 -1
  965. package/lib/module/specs/NativeScaleBunStorage.js +29 -29
  966. package/lib/module/specs/NativeScaleBunStorage.js.map +1 -1
  967. package/lib/module/storage/StorageBackend.js +83 -83
  968. package/lib/module/storage/StorageBackend.js.map +1 -1
  969. package/lib/module/storage/db/sqlite.js.map +1 -1
  970. package/lib/module/storage/files/fileStore.js.map +1 -1
  971. package/lib/module/transport/auth/sdkSession.js.map +1 -1
  972. package/lib/module/transport/backoff/exponentialJitter.js +2 -2
  973. package/lib/module/transport/backoff/exponentialJitter.js.map +1 -1
  974. package/lib/module/transport/http/endpoints.js.map +1 -1
  975. package/lib/module/transport/http/httpClient.js +3 -3
  976. package/lib/module/transport/http/httpClient.js.map +1 -1
  977. package/lib/module/transport/rateLimit/retryAfter.js.map +1 -1
  978. package/lib/module/transport/signing/requestSigner.js.map +1 -1
  979. package/lib/module/vendor/protocol/index.js +74 -74
  980. package/lib/module/vendor/protocol/index.js.map +1 -1
  981. package/lib/module/vendor/protocol/internal/validation.js +23 -23
  982. package/lib/module/vendor/protocol/internal/validation.js.map +1 -1
  983. package/lib/module/vendor/sdk-contracts/bridge.js.map +1 -1
  984. package/lib/module/vendor/sdk-contracts/config.js.map +1 -1
  985. package/lib/module/vendor/sdk-contracts/events.js.map +1 -1
  986. package/lib/module/vendor/sdk-contracts/features.js.map +1 -1
  987. package/lib/module/vendor/sdk-contracts/index.js.map +1 -1
  988. package/lib/module/vendor/sdk-contracts/platform.js.map +1 -1
  989. package/lib/module/vendor/sdk-contracts/session-boundary.js.map +1 -1
  990. package/lib/module/vendor/sdk-contracts/session.js.map +1 -1
  991. package/lib/module/vendor/vendor.manifest +10 -10
  992. package/lib/typescript/analytics/EventTracker.d.ts +20 -0
  993. package/lib/typescript/analytics/EventTracker.d.ts.map +1 -1
  994. package/lib/typescript/bootstrap/SDKBootstrapper.d.ts.map +1 -1
  995. package/lib/typescript/core/config/schema.d.ts +1 -1
  996. package/lib/typescript/core/config/schema.d.ts.map +1 -1
  997. package/lib/typescript/core/context/device.d.ts +46 -0
  998. package/lib/typescript/core/context/device.d.ts.map +1 -1
  999. package/lib/typescript/debug/replayWiring.d.ts.map +1 -1
  1000. package/lib/typescript/features/bugreport/BugReportFeature.d.ts +59 -0
  1001. package/lib/typescript/features/bugreport/BugReportFeature.d.ts.map +1 -0
  1002. package/lib/typescript/features/bugreport/buildPayload.d.ts +51 -0
  1003. package/lib/typescript/features/bugreport/buildPayload.d.ts.map +1 -0
  1004. package/lib/typescript/features/crash/CrashFeature.d.ts.map +1 -1
  1005. package/lib/typescript/features/crash/CrashReporter.d.ts +20 -1
  1006. package/lib/typescript/features/crash/CrashReporter.d.ts.map +1 -1
  1007. package/lib/typescript/features/crash/capture.d.ts +21 -2
  1008. package/lib/typescript/features/crash/capture.d.ts.map +1 -1
  1009. package/lib/typescript/features/crash/nativeCrashBridge.d.ts +16 -3
  1010. package/lib/typescript/features/crash/nativeCrashBridge.d.ts.map +1 -1
  1011. package/lib/typescript/features/crash/rejectionHandler.d.ts +46 -0
  1012. package/lib/typescript/features/crash/rejectionHandler.d.ts.map +1 -0
  1013. package/lib/typescript/features/engage/EngagePromptProvider.d.ts.map +1 -1
  1014. package/lib/typescript/features/engage/EngagePromptView.d.ts.map +1 -1
  1015. package/lib/typescript/features/engage/engageThrottle.d.ts +24 -4
  1016. package/lib/typescript/features/engage/engageThrottle.d.ts.map +1 -1
  1017. package/lib/typescript/features/engage/engageTriggerEngine.d.ts.map +1 -1
  1018. package/lib/typescript/features/engage/engageTypes.d.ts +21 -0
  1019. package/lib/typescript/features/engage/engageTypes.d.ts.map +1 -1
  1020. package/lib/typescript/features/journey/ScaleBunDebugRoot.d.ts.map +1 -1
  1021. package/lib/typescript/features/navigation/AutoScreenDetector.d.ts +9 -0
  1022. package/lib/typescript/features/navigation/AutoScreenDetector.d.ts.map +1 -1
  1023. package/lib/typescript/features/ota/OtaOrchestrator.d.ts +75 -3
  1024. package/lib/typescript/features/ota/OtaOrchestrator.d.ts.map +1 -1
  1025. package/lib/typescript/features/ota/OtaTypes.d.ts +42 -0
  1026. package/lib/typescript/features/ota/OtaTypes.d.ts.map +1 -1
  1027. package/lib/typescript/features/ota/deviceAttributes.d.ts +18 -0
  1028. package/lib/typescript/features/ota/deviceAttributes.d.ts.map +1 -0
  1029. package/lib/typescript/features/ota/environment.d.ts +99 -0
  1030. package/lib/typescript/features/ota/environment.d.ts.map +1 -0
  1031. package/lib/typescript/features/ota/geoCountry.d.ts +14 -0
  1032. package/lib/typescript/features/ota/geoCountry.d.ts.map +1 -0
  1033. package/lib/typescript/features/ota/retry.d.ts +53 -0
  1034. package/lib/typescript/features/ota/retry.d.ts.map +1 -0
  1035. package/lib/typescript/features/ota/signature.d.ts +66 -0
  1036. package/lib/typescript/features/ota/signature.d.ts.map +1 -0
  1037. package/lib/typescript/features/ota/useOtaUpdate.d.ts +20 -2
  1038. package/lib/typescript/features/ota/useOtaUpdate.d.ts.map +1 -1
  1039. package/lib/typescript/features/performance/PerformanceFeature.d.ts +0 -1
  1040. package/lib/typescript/features/performance/PerformanceFeature.d.ts.map +1 -1
  1041. package/lib/typescript/features/performance/collectors/FrameMetricsCollector.d.ts +33 -0
  1042. package/lib/typescript/features/performance/collectors/FrameMetricsCollector.d.ts.map +1 -1
  1043. package/lib/typescript/features/performance/transport/PerformanceTransport.d.ts.map +1 -1
  1044. package/lib/typescript/features/session/BackendSessionAdapter.d.ts +135 -4
  1045. package/lib/typescript/features/session/BackendSessionAdapter.d.ts.map +1 -1
  1046. package/lib/typescript/features/session/JourneyEventPipeline.d.ts +2 -0
  1047. package/lib/typescript/features/session/JourneyEventPipeline.d.ts.map +1 -1
  1048. package/lib/typescript/features/session/ReplayCaptureManager.d.ts +38 -1
  1049. package/lib/typescript/features/session/ReplayCaptureManager.d.ts.map +1 -1
  1050. package/lib/typescript/features/session/SessionManager.d.ts.map +1 -1
  1051. package/lib/typescript/features/session/nativeCapture.d.ts +2 -1
  1052. package/lib/typescript/features/session/nativeCapture.d.ts.map +1 -1
  1053. package/lib/typescript/features/session/outboxFrameCapture.d.ts.map +1 -1
  1054. package/lib/typescript/features/session/sessionTypes.d.ts +20 -3
  1055. package/lib/typescript/features/session/sessionTypes.d.ts.map +1 -1
  1056. package/lib/typescript/index.d.ts +7 -0
  1057. package/lib/typescript/index.d.ts.map +1 -1
  1058. package/lib/typescript/public/ScaleBunErrorBoundary.d.ts +44 -0
  1059. package/lib/typescript/public/ScaleBunErrorBoundary.d.ts.map +1 -0
  1060. package/lib/typescript/public/ScaleBunFacade.d.ts +58 -0
  1061. package/lib/typescript/public/ScaleBunFacade.d.ts.map +1 -1
  1062. package/lib/typescript/specs/NativeScaleBunCrash.d.ts +14 -0
  1063. package/lib/typescript/specs/NativeScaleBunCrash.d.ts.map +1 -1
  1064. package/lib/typescript/specs/NativeScaleBunOta.d.ts +11 -0
  1065. package/lib/typescript/specs/NativeScaleBunOta.d.ts.map +1 -1
  1066. package/lib/typescript/vendor/protocol/index.d.ts +17 -17
  1067. package/package.json +12 -14
  1068. package/react-native.config.js +24 -24
  1069. package/scalebun-react-native.podspec +37 -37
  1070. package/src/analytics/EventTracker.ts +489 -462
  1071. package/src/analytics/automaticEvents.ts +105 -105
  1072. package/src/analytics/batching.ts +54 -54
  1073. package/src/analytics/eventLane.ts +26 -26
  1074. package/src/bootstrap/FeatureRegistry.ts +81 -81
  1075. package/src/bootstrap/SDKBootstrapper.ts +610 -585
  1076. package/src/bucketing/fnv1a32.ts +40 -40
  1077. package/src/compat/codepush.ts +118 -118
  1078. package/src/config/ConfigManager.ts +167 -167
  1079. package/src/config/configTypes.ts +32 -32
  1080. package/src/core/architecture.ts +39 -39
  1081. package/src/core/clock/now.ts +6 -6
  1082. package/src/core/config/schema.ts +239 -231
  1083. package/src/core/constants/endpoints.ts +9 -9
  1084. package/src/core/constants/timings.ts +23 -23
  1085. package/src/core/constants/version.ts +6 -6
  1086. package/src/core/context/device.ts +146 -0
  1087. package/src/core/context/user.ts +26 -26
  1088. package/src/core/contracts/IFeature.ts +53 -53
  1089. package/src/core/di/container.ts +26 -26
  1090. package/src/core/encoding/base64.ts +46 -46
  1091. package/src/core/id/deviceId.ts +51 -51
  1092. package/src/core/lifecycle/appLifecycle.ts +33 -33
  1093. package/src/core/lifecycle/crashSafe.ts +29 -29
  1094. package/src/core/logger/errorClassification.ts +108 -108
  1095. package/src/core/logger/internalLogger.ts +65 -65
  1096. package/src/crypto/hmacSha256.ts +106 -106
  1097. package/src/debug/bootstrap.ts +366 -366
  1098. package/src/debug/commands.ts +597 -597
  1099. package/src/debug/configBuilder.ts +56 -56
  1100. package/src/debug/exportBridge.ts +153 -153
  1101. package/src/debug/hostResolver.ts +34 -34
  1102. package/src/debug/index.ts +25 -25
  1103. package/src/debug/metrics.ts +29 -29
  1104. package/src/debug/perf.ts +212 -212
  1105. package/src/debug/perfWiring.ts +119 -119
  1106. package/src/debug/protocol.ts +58 -58
  1107. package/src/debug/redaction.ts +290 -290
  1108. package/src/debug/replayWiring.ts +148 -147
  1109. package/src/debug/screenTracking.ts +55 -55
  1110. package/src/debug/sessionWiring.ts +59 -59
  1111. package/src/debug/stream.ts +561 -561
  1112. package/src/debug/transport.ts +439 -439
  1113. package/src/features/bugreport/BugReportFeature.ts +128 -0
  1114. package/src/features/bugreport/buildPayload.ts +116 -0
  1115. package/src/features/crash/CrashFeature.ts +71 -52
  1116. package/src/features/crash/CrashReporter.ts +179 -140
  1117. package/src/features/crash/NativeCrashFeature.ts +42 -42
  1118. package/src/features/crash/capture.ts +48 -21
  1119. package/src/features/crash/globalHandler.ts +39 -39
  1120. package/src/features/crash/index.ts +8 -8
  1121. package/src/features/crash/nativeCrashBridge.ts +266 -132
  1122. package/src/features/crash/rejectionHandler.ts +134 -0
  1123. package/src/features/engage/EngageAnchor.tsx +159 -159
  1124. package/src/features/engage/EngageArchetypeRenderers.tsx +3716 -3716
  1125. package/src/features/engage/EngageInAppView.tsx +2678 -2678
  1126. package/src/features/engage/EngageInlinePlacement.tsx +169 -169
  1127. package/src/features/engage/EngagePromptProvider.tsx +1318 -1270
  1128. package/src/features/engage/EngagePromptView.tsx +1400 -1364
  1129. package/src/features/engage/EngageTransport.ts +668 -668
  1130. package/src/features/engage/EngageVariantContent.tsx +1483 -1483
  1131. package/src/features/engage/__test-support__/reactNativeStub.js +40 -40
  1132. package/src/features/engage/attachmentCapture.ts +45 -45
  1133. package/src/features/engage/captureVoiceAttachment.ts +81 -81
  1134. package/src/features/engage/engageCoachmarkTour.ts +120 -120
  1135. package/src/features/engage/engageGameLogic.ts +347 -347
  1136. package/src/features/engage/engageMediaSizing.ts +76 -76
  1137. package/src/features/engage/engageMultiStepSheet.ts +53 -53
  1138. package/src/features/engage/engageOutcome.ts +157 -157
  1139. package/src/features/engage/engagePushTokenBridge.ts +84 -84
  1140. package/src/features/engage/engageShakeBridge.ts +67 -67
  1141. package/src/features/engage/engageSignals.ts +40 -40
  1142. package/src/features/engage/engageStoreReviewBridge.ts +48 -48
  1143. package/src/features/engage/engageTestSeenStore.ts +95 -95
  1144. package/src/features/engage/engageThrottle.ts +478 -440
  1145. package/src/features/engage/engageTriggerEngine.ts +350 -335
  1146. package/src/features/engage/engageTypes.ts +712 -691
  1147. package/src/features/engage/engageVariantResolver.ts +364 -364
  1148. package/src/features/engage/engageWindowInsets.ts +108 -108
  1149. package/src/features/engage/gestureTriggerDetector.ts +83 -83
  1150. package/src/features/engage/transcription/TranscriptionProvider.ts +68 -68
  1151. package/src/features/install-referrer/installReferrer.ts +112 -112
  1152. package/src/features/journey/ScaleBunDebugRoot.tsx +727 -726
  1153. package/src/features/journey/ScaleBunFlatList.tsx +39 -39
  1154. package/src/features/journey/ScaleBunImpression.tsx +109 -109
  1155. package/src/features/journey/ScaleBunScrollView.tsx +72 -72
  1156. package/src/features/journey/ScaleBunSectionList.tsx +39 -39
  1157. package/src/features/journey/calibrationContext.ts +53 -53
  1158. package/src/features/journey/gestureBuffer.ts +59 -59
  1159. package/src/features/journey/gestureDetector.ts +162 -162
  1160. package/src/features/journey/impression.ts +92 -92
  1161. package/src/features/journey/journeyManager.ts +466 -466
  1162. package/src/features/journey/journeyTypes.ts +215 -215
  1163. package/src/features/journey/navDetector.ts +149 -149
  1164. package/src/features/journey/screenshotHelper.ts +137 -137
  1165. package/src/features/journey/targetRegistry.ts +43 -43
  1166. package/src/features/journey/touchTarget.ts +128 -128
  1167. package/src/features/navigation/AutoScreenDetector.ts +503 -448
  1168. package/src/features/network/NetworkFeature.ts +144 -144
  1169. package/src/features/network/index.ts +31 -31
  1170. package/src/features/ota/OtaEventEmitter.ts +128 -128
  1171. package/src/features/ota/OtaOrchestrator.ts +963 -323
  1172. package/src/features/ota/OtaTypes.ts +118 -62
  1173. package/src/features/ota/deviceAttributes.ts +46 -0
  1174. package/src/features/ota/environment.ts +199 -0
  1175. package/src/features/ota/geoCountry.ts +85 -0
  1176. package/src/features/ota/retry.ts +123 -0
  1177. package/src/features/ota/signature.ts +123 -0
  1178. package/src/features/ota/useOtaUpdate.ts +152 -134
  1179. package/src/features/performance/PerformanceFeature.ts +597 -584
  1180. package/src/features/performance/collectors/AppLaunchCollector.ts +117 -117
  1181. package/src/features/performance/collectors/AutoScreenLoadCollector.ts +258 -258
  1182. package/src/features/performance/collectors/CustomTraceCollector.ts +116 -116
  1183. package/src/features/performance/collectors/FrameMetricsCollector.ts +181 -119
  1184. package/src/features/performance/collectors/JsStallCollector.ts +165 -165
  1185. package/src/features/performance/collectors/NetworkPerfCollector.ts +69 -69
  1186. package/src/features/performance/collectors/ScreenLoadCollector.ts +95 -95
  1187. package/src/features/performance/collectors/UiHangCollector.ts +98 -98
  1188. package/src/features/performance/config.ts +136 -136
  1189. package/src/features/performance/getNativePerformanceModule.ts +38 -38
  1190. package/src/features/performance/index.ts +28 -28
  1191. package/src/features/performance/metrics/MetricsEngine.ts +152 -152
  1192. package/src/features/performance/models.ts +235 -235
  1193. package/src/features/performance/transport/PerformanceTransport.ts +161 -143
  1194. package/src/features/profiler/ProfilerFeature.ts +661 -661
  1195. package/src/features/profiler/config.ts +230 -230
  1196. package/src/features/profiler/contracts.ts +176 -176
  1197. package/src/features/profiler/index.ts +45 -45
  1198. package/src/features/profiler/models.ts +430 -430
  1199. package/src/features/profiler/transport/ProfilerTransport.ts +93 -93
  1200. package/src/features/replay/bridge/adapters/bridgeAdapter.ts +230 -230
  1201. package/src/features/replay/bridge/eventEmitter.ts +176 -176
  1202. package/src/features/replay/bridge/nativeModule.ts +241 -241
  1203. package/src/features/replay/core/clock/clock.ts +38 -38
  1204. package/src/features/replay/core/config/defaults.ts +66 -66
  1205. package/src/features/replay/core/config/validator.ts +113 -113
  1206. package/src/features/replay/core/env/environmentDetector.ts +21 -21
  1207. package/src/features/replay/core/errors/safeCall.ts +34 -34
  1208. package/src/features/replay/core/ids/sessionId.ts +44 -44
  1209. package/src/features/replay/core/logger/logger.ts +40 -40
  1210. package/src/features/replay/core/privacy/privacyTypes.ts +41 -41
  1211. package/src/features/replay/core/queue/boundedQueue.ts +71 -71
  1212. package/src/features/replay/integrations/alert/alertInstrumentation.ts +277 -277
  1213. package/src/features/replay/integrations/logs/consoleIntegration.ts +116 -116
  1214. package/src/features/replay/integrations/network/networkAdapter.ts +128 -128
  1215. package/src/features/replay/integrations/react-navigation/navigationIntegration.ts +89 -89
  1216. package/src/features/replay/integrations/touch/ReplayRoot.tsx +114 -114
  1217. package/src/features/replay/pipeline/batching/batchManager.ts +133 -133
  1218. package/src/features/replay/pipeline/envelope/envelopeBuilder.ts +66 -66
  1219. package/src/features/replay/pipeline/retry/retryPolicy.ts +62 -62
  1220. package/src/features/replay/pipeline/transport/transportTypes.ts +85 -85
  1221. package/src/features/replay/public/api.ts +364 -364
  1222. package/src/features/replay/public/enums.ts +78 -78
  1223. package/src/features/replay/public/types.ts +297 -297
  1224. package/src/features/replay/replay/breadcrumbs/breadcrumbCollector.ts +115 -115
  1225. package/src/features/replay/replay/frames/frameModels.ts +45 -45
  1226. package/src/features/replay/replay/session/sessionOrchestrator.ts +286 -286
  1227. package/src/features/replay/replay/timeline/timelineModels.ts +129 -129
  1228. package/src/features/replay/transport/BackendReplayTransport.ts +215 -215
  1229. package/src/features/replay/transport/CompositeReplayTransport.ts +82 -82
  1230. package/src/features/replay/transport/DesktopReplayTransport.ts +140 -140
  1231. package/src/features/replay/transport/ReplayTransport.ts +84 -84
  1232. package/src/features/session/BackendSessionAdapter.ts +1568 -1233
  1233. package/src/features/session/DesktopSessionTransport.ts +128 -128
  1234. package/src/features/session/JourneyEventPipeline.ts +276 -273
  1235. package/src/features/session/ReplayCaptureManager.ts +998 -946
  1236. package/src/features/session/ScrollTracker.ts +166 -166
  1237. package/src/features/session/SessionManager.ts +1248 -1209
  1238. package/src/features/session/SyncDecisionEngine.ts +117 -117
  1239. package/src/features/session/index.ts +88 -88
  1240. package/src/features/session/nativeCapture.ts +39 -38
  1241. package/src/features/session/outboxFrameCapture.ts +75 -74
  1242. package/src/features/session/sessionTypes.ts +514 -491
  1243. package/src/features/skan/skanBridge.ts +61 -61
  1244. package/src/features/skan/skanConversionManager.ts +173 -173
  1245. package/src/index.ts +200 -191
  1246. package/src/integrations/fetch/fetchInterceptor.ts +182 -182
  1247. package/src/integrations/network/bodyCapture.ts +328 -328
  1248. package/src/integrations/xhr/xhrInterceptor.ts +231 -231
  1249. package/src/metro/index.ts +164 -164
  1250. package/src/metro/optionalDependencyStub.ts +13 -13
  1251. package/src/metro/optionalModules.ts +27 -27
  1252. package/src/pipeline/dispatcher/dispatcher.ts +22 -22
  1253. package/src/pipeline/envelope/envelope.ts +31 -31
  1254. package/src/pipeline/envelope/serializer.ts +14 -14
  1255. package/src/pipeline/processors/enrich.ts +11 -11
  1256. package/src/pipeline/processors/sanitize.ts +23 -23
  1257. package/src/pipeline/queue/flushQueue.ts +144 -144
  1258. package/src/pipeline/queue/memoryQueue.ts +31 -31
  1259. package/src/pipeline/queue/persistentQueue.ts +414 -414
  1260. package/src/pipeline/scheduler/flushScheduler.ts +47 -47
  1261. package/src/pipeline/scheduler/retryPolicy.ts +8 -8
  1262. package/src/public/ScaleBunErrorBoundary.tsx +79 -0
  1263. package/src/public/ScaleBunFacade.ts +1630 -1479
  1264. package/src/public/ScaleBunProvider.tsx +91 -91
  1265. package/src/public/typedTracker.ts +123 -123
  1266. package/src/public/types.ts +312 -312
  1267. package/src/push/PushManager.ts +318 -318
  1268. package/src/push/adapters/ManualAdapter.ts +100 -100
  1269. package/src/push/adapters/NativeBridgeAdapter.ts +142 -142
  1270. package/src/push/adapters/NoopAdapter.ts +46 -46
  1271. package/src/push/adapters/RNFirebaseMessagingAdapter.ts +271 -271
  1272. package/src/push/adapters/loadMessaging.ts +24 -24
  1273. package/src/push/adapters/loadNotifee.ts +29 -29
  1274. package/src/push/adapters/selectAdapter.ts +176 -176
  1275. package/src/push/detect/capabilities.ts +124 -124
  1276. package/src/push/index.ts +22 -22
  1277. package/src/push/native/nativeNotifications.ts +216 -216
  1278. package/src/push/types.ts +159 -159
  1279. package/src/specs/NativeReplaySdk.ts +99 -99
  1280. package/src/specs/NativeScaleBunCrash.ts +52 -36
  1281. package/src/specs/NativeScaleBunInstallReferrer.ts +29 -29
  1282. package/src/specs/NativeScaleBunOta.ts +97 -85
  1283. package/src/specs/NativeScaleBunPerformance.ts +34 -34
  1284. package/src/specs/NativeScaleBunProfiler.ts +41 -41
  1285. package/src/specs/NativeScaleBunStorage.ts +59 -59
  1286. package/src/storage/StorageBackend.ts +272 -272
  1287. package/src/transport/auth/sdkSession.ts +27 -27
  1288. package/src/transport/backoff/exponentialJitter.ts +9 -9
  1289. package/src/transport/http/endpoints.ts +33 -33
  1290. package/src/transport/http/httpClient.ts +73 -73
  1291. package/src/vendor/protocol/index.ts +391 -391
  1292. package/src/vendor/protocol/internal/validation.ts +497 -497
  1293. package/src/vendor/sdk-contracts/bridge.ts +162 -162
  1294. package/src/vendor/sdk-contracts/config.ts +192 -192
  1295. package/src/vendor/sdk-contracts/events.ts +50 -50
  1296. package/src/vendor/sdk-contracts/features.ts +42 -42
  1297. package/src/vendor/sdk-contracts/index.ts +98 -98
  1298. package/src/vendor/sdk-contracts/platform.ts +42 -42
  1299. package/src/vendor/sdk-contracts/session-boundary.ts +55 -55
  1300. package/src/vendor/sdk-contracts/session.ts +225 -225
  1301. package/src/vendor/vendor.manifest +10 -10
  1302. package/android/build/generated/source/buildConfig/debug/com/scalebun/sdk/BuildConfig.java +0 -12
  1303. package/android/build/generated/source/codegen/java/com/scalebun/spec/NativeReplaySdkSpec.java +0 -208
  1304. package/android/build/generated/source/codegen/java/com/scalebun/spec/NativeScaleBunCrashSpec.java +0 -46
  1305. package/android/build/generated/source/codegen/java/com/scalebun/spec/NativeScaleBunInstallReferrerSpec.java +0 -38
  1306. package/android/build/generated/source/codegen/java/com/scalebun/spec/NativeScaleBunOtaSpec.java +0 -63
  1307. package/android/build/generated/source/codegen/java/com/scalebun/spec/NativeScaleBunPerformanceSpec.java +0 -70
  1308. package/android/build/generated/source/codegen/java/com/scalebun/spec/NativeScaleBunProfilerSpec.java +0 -96
  1309. package/android/build/generated/source/codegen/java/com/scalebun/spec/NativeScaleBunStorageSpec.java +0 -50
  1310. package/android/build/generated/source/codegen/jni/CMakeLists.txt +0 -28
  1311. package/android/build/generated/source/codegen/jni/ScaleBunSpec-generated.cpp +0 -554
  1312. package/android/build/generated/source/codegen/jni/ScaleBunSpec.h +0 -79
  1313. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/ComponentDescriptors.cpp +0 -22
  1314. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/ComponentDescriptors.h +0 -24
  1315. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/EventEmitters.cpp +0 -16
  1316. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/EventEmitters.h +0 -17
  1317. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/Props.cpp +0 -19
  1318. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/Props.h +0 -18
  1319. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/ScaleBunSpecJSI-generated.cpp +0 -595
  1320. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/ScaleBunSpecJSI.h +0 -1070
  1321. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/ShadowNodes.cpp +0 -17
  1322. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/ShadowNodes.h +0 -23
  1323. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/States.cpp +0 -16
  1324. package/android/build/generated/source/codegen/jni/react/renderer/components/ScaleBunSpec/States.h +0 -20
  1325. package/android/build/generated/source/codegen/schema.json +0 -1
  1326. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +0 -20
  1327. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +0 -18
  1328. package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +0 -6
  1329. package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +0 -1
  1330. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  1331. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  1332. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
  1333. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +0 -1
  1334. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +0 -2
  1335. package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +0 -2
  1336. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +0 -2
  1337. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +0 -2
  1338. package/android/build/intermediates/java_res/debug/processDebugJavaRes/out/META-INF/scalebun_react-native_debug.kotlin_module +0 -0
  1339. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/sdk/BuildConfig.class +0 -0
  1340. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/spec/NativeReplaySdkSpec.class +0 -0
  1341. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/spec/NativeScaleBunCrashSpec.class +0 -0
  1342. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/spec/NativeScaleBunInstallReferrerSpec.class +0 -0
  1343. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/spec/NativeScaleBunOtaSpec.class +0 -0
  1344. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/spec/NativeScaleBunPerformanceSpec.class +0 -0
  1345. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/spec/NativeScaleBunProfilerSpec.class +0 -0
  1346. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/scalebun/spec/NativeScaleBunStorageSpec.class +0 -0
  1347. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +0 -2
  1348. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +0 -24
  1349. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +0 -20
  1350. package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +0 -1
  1351. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +0 -1
  1352. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/META-INF/scalebun_react-native_debug.kotlin_module +0 -0
  1353. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/crash/CrashStorage.class +0 -0
  1354. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/crash/NativeCrashHandler$Companion.class +0 -0
  1355. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/crash/NativeCrashHandler.class +0 -0
  1356. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/ANRWatchdog$HangInfo.class +0 -0
  1357. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/ANRWatchdog.class +0 -0
  1358. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/AppStartCollector$AppStartInfo.class +0 -0
  1359. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/AppStartCollector$Breakdown.class +0 -0
  1360. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/AppStartCollector$start$1.class +0 -0
  1361. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/AppStartCollector.class +0 -0
  1362. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/FrameMetricsCollector$Companion.class +0 -0
  1363. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/FrameMetricsCollector$FrameMetricsSnapshot.class +0 -0
  1364. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/FrameMetricsCollector$start$1.class +0 -0
  1365. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/FrameMetricsCollector$startChoreographerFallback$1.class +0 -0
  1366. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/FrameMetricsCollector.class +0 -0
  1367. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/PerformanceCore$Listener.class +0 -0
  1368. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/core/performance/PerformanceCore.class +0 -0
  1369. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/ProfilerModule$Companion.class +0 -0
  1370. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/ProfilerModule.class +0 -0
  1371. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/ProfilerPackage.class +0 -0
  1372. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/ProfilerSpec.class +0 -0
  1373. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/CpuCollector.class +0 -0
  1374. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/IncidentDetector.class +0 -0
  1375. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/MemoryCollector.class +0 -0
  1376. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/RenderCollector$frameCallback$1.class +0 -0
  1377. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/RenderCollector.class +0 -0
  1378. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/SamplerScheduler.class +0 -0
  1379. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/SamplerSchedulerKt.class +0 -0
  1380. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/ThreadCollector$WhenMappings.class +0 -0
  1381. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/profiler/collectors/ThreadCollector.class +0 -0
  1382. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/ReplaySdkModule$Companion.class +0 -0
  1383. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/ReplaySdkModule$registerConnectivity$cb$1.class +0 -0
  1384. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/ReplaySdkModule.class +0 -0
  1385. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/ReplaySdkPackage.class +0 -0
  1386. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/ReplaySdkSpec.class +0 -0
  1387. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/CaptureReason$Companion.class +0 -0
  1388. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/CaptureReason.class +0 -0
  1389. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/FrameCaptureManager$startFrequencyTimer$1.class +0 -0
  1390. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/FrameCaptureManager.class +0 -0
  1391. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/FrameCaptureScheduler$Companion.class +0 -0
  1392. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/FrameCaptureScheduler$FrameRequest.class +0 -0
  1393. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/FrameCaptureScheduler.class +0 -0
  1394. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/UiStabilityDetector$Companion.class +0 -0
  1395. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/UiStabilityDetector$SettleWatch.class +0 -0
  1396. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/UiStabilityDetector$StabilityWatch.class +0 -0
  1397. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/capture/UiStabilityDetector.class +0 -0
  1398. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/CaptureMode$Companion.class +0 -0
  1399. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/CaptureMode.class +0 -0
  1400. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/ReplayConfig$Companion.class +0 -0
  1401. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/ReplayConfig.class +0 -0
  1402. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/ReplayLogger.class +0 -0
  1403. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/ScreenshotQuality$Companion.class +0 -0
  1404. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/ScreenshotQuality.class +0 -0
  1405. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/SessionIdGenerator.class +0 -0
  1406. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/core/SessionState.class +0 -0
  1407. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/models/ReplayFrame.class +0 -0
  1408. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/models/ReplaySession.class +0 -0
  1409. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/models/TimelineEvent.class +0 -0
  1410. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/NativeOutbox$Companion.class +0 -0
  1411. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/NativeOutbox$enforceLimits$lambda$37$$inlined$sortedBy$1.class +0 -0
  1412. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/NativeOutbox.class +0 -0
  1413. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/OutboxUploader$NetResult$NetworkError.class +0 -0
  1414. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/OutboxUploader$NetResult$Response.class +0 -0
  1415. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/OutboxUploader$NetResult.class +0 -0
  1416. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/OutboxUploader$Outcome.class +0 -0
  1417. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/OutboxUploader$WhenMappings.class +0 -0
  1418. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/outbox/OutboxUploader.class +0 -0
  1419. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/privacy/PrivacyMaskProcessor.class +0 -0
  1420. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/session/SessionManager.class +0 -0
  1421. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/storage/ReplayStorage$enforceStorageLimit$$inlined$sortedBy$1.class +0 -0
  1422. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/storage/ReplayStorage.class +0 -0
  1423. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/timeline/TimelineCollector.class +0 -0
  1424. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/tracking/DialogTouchObserver$start$runnable$1.class +0 -0
  1425. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/tracking/DialogTouchObserver.class +0 -0
  1426. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/tracking/InteractionTracker.class +0 -0
  1427. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/tracking/NativeTouchInterceptor.class +0 -0
  1428. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/tracking/ScreenTracker.class +0 -0
  1429. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/transport/ReplayTransport$UploadResult.class +0 -0
  1430. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/transport/ReplayTransport$upload$2.class +0 -0
  1431. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/transport/ReplayTransport.class +0 -0
  1432. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/voice/VoiceRecorder$Companion.class +0 -0
  1433. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/voice/VoiceRecorder$Result.class +0 -0
  1434. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/voice/VoiceRecorder.class +0 -0
  1435. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/voice/VoiceTranscriber$TranscriptResult.class +0 -0
  1436. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/voice/VoiceTranscriber$listener$1.class +0 -0
  1437. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/replaysdk/voice/VoiceTranscriber.class +0 -0
  1438. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/ScaleBunPackage$Companion.class +0 -0
  1439. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/ScaleBunPackage.class +0 -0
  1440. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/crash/ScaleBunCrashModule$Companion.class +0 -0
  1441. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/crash/ScaleBunCrashModule.class +0 -0
  1442. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/crash/ScaleBunCrashPackage.class +0 -0
  1443. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/crash/ScaleBunCrashSpec.class +0 -0
  1444. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/engage/ScaleBunEngageModule$Companion.class +0 -0
  1445. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/engage/ScaleBunEngageModule$WindowInsetEdges.class +0 -0
  1446. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/engage/ScaleBunEngageModule$shakeListener$1.class +0 -0
  1447. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/engage/ScaleBunEngageModule.class +0 -0
  1448. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/engage/ScaleBunEngagePackage.class +0 -0
  1449. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/engage/ScaleBunNotificationPresenter.class +0 -0
  1450. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/ota/ScaleBunOtaModule$Companion.class +0 -0
  1451. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/ota/ScaleBunOtaModule.class +0 -0
  1452. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/ota/ScaleBunOtaSpec.class +0 -0
  1453. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/ota/SlotManager$Companion.class +0 -0
  1454. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/ota/SlotManager.class +0 -0
  1455. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/performance/PerformanceModule$Companion.class +0 -0
  1456. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/performance/PerformanceModule$coreListener$1.class +0 -0
  1457. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/performance/PerformanceModule.class +0 -0
  1458. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/performance/PerformanceSpec.class +0 -0
  1459. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule$Companion.class +0 -0
  1460. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule$getInstallReferrer$1.class +0 -0
  1461. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule.class +0 -0
  1462. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/referrer/ScaleBunInstallReferrerSpec.class +0 -0
  1463. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/storage/ScaleBunStorageModule$Companion.class +0 -0
  1464. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/storage/ScaleBunStorageModule.class +0 -0
  1465. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/rn/storage/ScaleBunStorageSpec.class +0 -0
  1466. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/sdk/BuildConfig.class +0 -0
  1467. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/spec/NativeReplaySdkSpec.class +0 -0
  1468. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/spec/NativeScaleBunCrashSpec.class +0 -0
  1469. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/spec/NativeScaleBunInstallReferrerSpec.class +0 -0
  1470. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/spec/NativeScaleBunOtaSpec.class +0 -0
  1471. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/spec/NativeScaleBunPerformanceSpec.class +0 -0
  1472. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/spec/NativeScaleBunProfilerSpec.class +0 -0
  1473. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/spec/NativeScaleBunStorageSpec.class +0 -0
  1474. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/storage/KvFileStore$Companion.class +0 -0
  1475. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/scalebun/storage/KvFileStore.class +0 -0
  1476. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  1477. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +0 -1
  1478. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  1479. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  1480. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  1481. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  1482. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  1483. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  1484. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
  1485. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  1486. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  1487. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  1488. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  1489. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  1490. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  1491. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
  1492. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  1493. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  1494. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  1495. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  1496. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  1497. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  1498. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
  1499. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
  1500. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
  1501. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
  1502. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
  1503. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  1504. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
  1505. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i.len +0 -0
  1506. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  1507. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  1508. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  1509. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  1510. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  1511. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  1512. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
  1513. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab +0 -0
  1514. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.keystream +0 -0
  1515. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.keystream.len +0 -0
  1516. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.len +0 -0
  1517. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.values.at +0 -0
  1518. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab_i +0 -0
  1519. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab_i.len +0 -0
  1520. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  1521. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  1522. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  1523. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  1524. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values +0 -0
  1525. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  1526. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.s +0 -1
  1527. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  1528. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
  1529. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  1530. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  1531. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  1532. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  1533. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  1534. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  1535. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
  1536. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  1537. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
  1538. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
  1539. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
  1540. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  1541. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
  1542. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
  1543. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  1544. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  1545. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  1546. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
  1547. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  1548. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  1549. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
  1550. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +0 -2
  1551. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  1552. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  1553. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  1554. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  1555. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  1556. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  1557. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
  1558. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  1559. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  1560. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  1561. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  1562. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  1563. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  1564. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
  1565. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  1566. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  1567. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  1568. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  1569. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values +0 -0
  1570. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  1571. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.s +0 -1
  1572. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  1573. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
  1574. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  1575. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  1576. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  1577. package/android/build/outputs/logs/manifest-merger-debug-report.txt +0 -25
  1578. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  1579. package/android/build/tmp/kotlin-classes/debug/META-INF/scalebun_react-native_debug.kotlin_module +0 -0
  1580. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/crash/CrashStorage.class +0 -0
  1581. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/crash/NativeCrashHandler$Companion.class +0 -0
  1582. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/crash/NativeCrashHandler.class +0 -0
  1583. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/ANRWatchdog$HangInfo.class +0 -0
  1584. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/ANRWatchdog.class +0 -0
  1585. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/AppStartCollector$AppStartInfo.class +0 -0
  1586. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/AppStartCollector$Breakdown.class +0 -0
  1587. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/AppStartCollector$start$1.class +0 -0
  1588. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/AppStartCollector.class +0 -0
  1589. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/FrameMetricsCollector$Companion.class +0 -0
  1590. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/FrameMetricsCollector$FrameMetricsSnapshot.class +0 -0
  1591. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/FrameMetricsCollector$start$1.class +0 -0
  1592. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/FrameMetricsCollector$startChoreographerFallback$1.class +0 -0
  1593. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/FrameMetricsCollector.class +0 -0
  1594. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/PerformanceCore$Listener.class +0 -0
  1595. package/android/build/tmp/kotlin-classes/debug/com/scalebun/core/performance/PerformanceCore.class +0 -0
  1596. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/ProfilerModule$Companion.class +0 -0
  1597. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/ProfilerModule.class +0 -0
  1598. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/ProfilerPackage.class +0 -0
  1599. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/ProfilerSpec.class +0 -0
  1600. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/CpuCollector.class +0 -0
  1601. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/IncidentDetector.class +0 -0
  1602. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/MemoryCollector.class +0 -0
  1603. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/RenderCollector$frameCallback$1.class +0 -0
  1604. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/RenderCollector.class +0 -0
  1605. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/SamplerScheduler.class +0 -0
  1606. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/SamplerSchedulerKt.class +0 -0
  1607. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/ThreadCollector$WhenMappings.class +0 -0
  1608. package/android/build/tmp/kotlin-classes/debug/com/scalebun/profiler/collectors/ThreadCollector.class +0 -0
  1609. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/ReplaySdkModule$Companion.class +0 -0
  1610. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/ReplaySdkModule$registerConnectivity$cb$1.class +0 -0
  1611. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/ReplaySdkModule.class +0 -0
  1612. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/ReplaySdkPackage.class +0 -0
  1613. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/ReplaySdkSpec.class +0 -0
  1614. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/CaptureReason$Companion.class +0 -0
  1615. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/CaptureReason.class +0 -0
  1616. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/FrameCaptureManager$startFrequencyTimer$1.class +0 -0
  1617. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/FrameCaptureManager.class +0 -0
  1618. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/FrameCaptureScheduler$Companion.class +0 -0
  1619. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/FrameCaptureScheduler$FrameRequest.class +0 -0
  1620. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/FrameCaptureScheduler.class +0 -0
  1621. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/UiStabilityDetector$Companion.class +0 -0
  1622. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/UiStabilityDetector$SettleWatch.class +0 -0
  1623. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/UiStabilityDetector$StabilityWatch.class +0 -0
  1624. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/capture/UiStabilityDetector.class +0 -0
  1625. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/CaptureMode$Companion.class +0 -0
  1626. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/CaptureMode.class +0 -0
  1627. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/ReplayConfig$Companion.class +0 -0
  1628. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/ReplayConfig.class +0 -0
  1629. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/ReplayLogger.class +0 -0
  1630. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/ScreenshotQuality$Companion.class +0 -0
  1631. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/ScreenshotQuality.class +0 -0
  1632. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/SessionIdGenerator.class +0 -0
  1633. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/core/SessionState.class +0 -0
  1634. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/models/ReplayFrame.class +0 -0
  1635. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/models/ReplaySession.class +0 -0
  1636. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/models/TimelineEvent.class +0 -0
  1637. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/NativeOutbox$Companion.class +0 -0
  1638. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/NativeOutbox$enforceLimits$lambda$37$$inlined$sortedBy$1.class +0 -0
  1639. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/NativeOutbox.class +0 -0
  1640. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/OutboxUploader$NetResult$NetworkError.class +0 -0
  1641. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/OutboxUploader$NetResult$Response.class +0 -0
  1642. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/OutboxUploader$NetResult.class +0 -0
  1643. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/OutboxUploader$Outcome.class +0 -0
  1644. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/OutboxUploader$WhenMappings.class +0 -0
  1645. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/outbox/OutboxUploader.class +0 -0
  1646. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/privacy/PrivacyMaskProcessor.class +0 -0
  1647. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/session/SessionManager.class +0 -0
  1648. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/storage/ReplayStorage$enforceStorageLimit$$inlined$sortedBy$1.class +0 -0
  1649. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/storage/ReplayStorage.class +0 -0
  1650. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/timeline/TimelineCollector.class +0 -0
  1651. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/tracking/DialogTouchObserver$start$runnable$1.class +0 -0
  1652. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/tracking/DialogTouchObserver.class +0 -0
  1653. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/tracking/InteractionTracker.class +0 -0
  1654. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/tracking/NativeTouchInterceptor.class +0 -0
  1655. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/tracking/ScreenTracker.class +0 -0
  1656. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/transport/ReplayTransport$UploadResult.class +0 -0
  1657. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/transport/ReplayTransport$upload$2.class +0 -0
  1658. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/transport/ReplayTransport.class +0 -0
  1659. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/voice/VoiceRecorder$Companion.class +0 -0
  1660. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/voice/VoiceRecorder$Result.class +0 -0
  1661. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/voice/VoiceRecorder.class +0 -0
  1662. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/voice/VoiceTranscriber$TranscriptResult.class +0 -0
  1663. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/voice/VoiceTranscriber$listener$1.class +0 -0
  1664. package/android/build/tmp/kotlin-classes/debug/com/scalebun/replaysdk/voice/VoiceTranscriber.class +0 -0
  1665. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/ScaleBunPackage$Companion.class +0 -0
  1666. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/ScaleBunPackage.class +0 -0
  1667. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/crash/ScaleBunCrashModule$Companion.class +0 -0
  1668. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/crash/ScaleBunCrashModule.class +0 -0
  1669. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/crash/ScaleBunCrashPackage.class +0 -0
  1670. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/crash/ScaleBunCrashSpec.class +0 -0
  1671. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/engage/ScaleBunEngageModule$Companion.class +0 -0
  1672. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/engage/ScaleBunEngageModule$WindowInsetEdges.class +0 -0
  1673. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/engage/ScaleBunEngageModule$shakeListener$1.class +0 -0
  1674. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/engage/ScaleBunEngageModule.class +0 -0
  1675. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/engage/ScaleBunEngagePackage.class +0 -0
  1676. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/engage/ScaleBunNotificationPresenter.class +0 -0
  1677. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/ota/ScaleBunOtaModule$Companion.class +0 -0
  1678. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/ota/ScaleBunOtaModule.class +0 -0
  1679. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/ota/ScaleBunOtaSpec.class +0 -0
  1680. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/ota/SlotManager$Companion.class +0 -0
  1681. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/ota/SlotManager.class +0 -0
  1682. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/performance/PerformanceModule$Companion.class +0 -0
  1683. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/performance/PerformanceModule$coreListener$1.class +0 -0
  1684. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/performance/PerformanceModule.class +0 -0
  1685. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/performance/PerformanceSpec.class +0 -0
  1686. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule$Companion.class +0 -0
  1687. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule$getInstallReferrer$1.class +0 -0
  1688. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/referrer/ScaleBunInstallReferrerModule.class +0 -0
  1689. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/referrer/ScaleBunInstallReferrerSpec.class +0 -0
  1690. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/storage/ScaleBunStorageModule$Companion.class +0 -0
  1691. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/storage/ScaleBunStorageModule.class +0 -0
  1692. package/android/build/tmp/kotlin-classes/debug/com/scalebun/rn/storage/ScaleBunStorageSpec.class +0 -0
  1693. package/android/build/tmp/kotlin-classes/debug/com/scalebun/storage/KvFileStore$Companion.class +0 -0
  1694. package/android/build/tmp/kotlin-classes/debug/com/scalebun/storage/KvFileStore.class +0 -0
@@ -1,1270 +1,1318 @@
1
- /**
2
- * ScaleBun SDK — Engage Prompt Provider (campaign renderer core, D4)
3
- *
4
- * Wraps the host app and owns the campaign-render lifecycle for the shared
5
- * cross-platform JS overlay:
6
- * 1. on a trigger (app-controlled via `showEngagePrompt()` and/or the campaign's
7
- * own `trigger` from config), calls `facade.engage.fetchConfig()`,
8
- * 2. runs the throttle/decision core (`selectCampaignToRender`) to pick which
9
- * campaign — if any — is eligible, honoring frequency caps,
10
- * 3. mounts the parameterized `EngagePromptView` (Open/Closed: NPS now,
11
- * ratings/feedback/survey later via the renderer registry),
12
- * 4. on submit → `facade.engage.submitResponse({ surveyId, score, text })`
13
- * (transport stamps `responseId` UUID + `schemaVersion` per contract),
14
- * records the impression as completed, and emits an in-app dismiss/submit
15
- * tracking event,
16
- * 5. on dismiss → `facade.engage.trackEvent` with a `dismiss` event.
17
- *
18
- * The provider depends only on the `engage` namespace shape — NOT the whole
19
- * facade — so it stays decoupled and testable, and reuses the existing transport
20
- * (no second HTTP path).
21
- *
22
- * IN-APP MESSAGES (the second Engage family, §2.4/§2.5) are surfaced by this SAME
23
- * provider — NOT a parallel one. `showInAppMessage()` fetches the config, picks an
24
- * eligible message from `inAppMessages[]` (shared throttle store), and mounts the
25
- * config-driven `EngageInAppView` (banner/modal/overlay). Events: `impression` on
26
- * show, `click` on CTA tap (the resolved deep link is surfaced to the host via
27
- * `onInAppDeepLink` — the SDK never opens URLs), `dismiss` on close.
28
- *
29
- * PRECEDENCE (campaign vs in-app): a campaign prompt wins the screen. If a
30
- * campaign prompt is already visible, `showInAppMessage()` is a no-op (returns
31
- * false) and the mounted in-app view is hidden while a campaign is up, so the two
32
- * families never fight for the screen.
33
- */
34
-
35
- import React, {
36
- createContext,
37
- useContext,
38
- useCallback,
39
- useEffect,
40
- useMemo,
41
- useRef,
42
- useState,
43
- type ReactNode,
44
- type ReactElement,
45
- } from 'react';
46
- import { Linking, Platform } from 'react-native';
47
- import { logger } from '../../core/logger/internalLogger';
48
- import { installEngageTriggerEngine } from './engageTriggerEngine';
49
- import { appLifecycle } from '../../core/lifecycle/appLifecycle';
50
- import { getDeviceId } from '../../core/id/deviceId';
51
- import { ReplaySdk } from '../replay/public/api';
52
- import { SessionManager } from '../session/SessionManager';
53
- import {
54
- EngagePromptView,
55
- type EngagePromptResult,
56
- type EngageSubmitStatus,
57
- type RatingPromptConfig,
58
- } from './EngagePromptView';
59
- import { EngageInAppView, type InAppMechanicInteraction } from './EngageInAppView';
60
- import { EngageAnchorProvider } from './EngageAnchor';
61
- import {
62
- EngageInlinePlacementProvider,
63
- createEngageInlinePlacementRegistry,
64
- resolveEngageInlinePlacement,
65
- type EngageInlineDelivery,
66
- } from './EngageInlinePlacement';
67
- import { resolveInAppPresentation } from './engageVariantResolver';
68
- import { shouldTrackInAppCta } from './engageOutcome';
69
- import type { SubmitStage } from './EngageTransport';
70
- import {
71
- EngageThrottleStore,
72
- selectCampaignToRender,
73
- selectInAppToRender,
74
- selectInAppVariant,
75
- selectLatestUnseenTest,
76
- testRunIdOf,
77
- } from './engageThrottle';
78
- import { EngageTestSeenStore } from './engageTestSeenStore';
79
- import type {
80
- EngageAttachmentDraft,
81
- EngageCampaignConfig,
82
- EngageCampaignType,
83
- EngageConfigContext,
84
- EngageConfigResponse,
85
- EngageEventInput,
86
- InAppGameRequestHandler,
87
- EngageResponseInput,
88
- InAppMessageConfig,
89
- InAppRenderSpec,
90
- InAppVariantId,
91
- RatingFunnelEventDims,
92
- RatingFunnelEventInput,
93
- RatingFunnelEventType,
94
- SurveyAnswer,
95
- SurveyImpressionInput,
96
- } from './engageTypes';
97
-
98
- /** Client-generated UUID for app-rating idempotency (D8: `clientRatingId`). */
99
- function ratingUuid(): string {
100
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
101
- const r = (Math.random() * 16) | 0;
102
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
103
- return v.toString(16);
104
- });
105
- }
106
-
107
- /** Preserve a multi-question survey answer in the keyed wire shape (§1.4). */
108
- export function toWireAnswers(
109
- answers: SurveyAnswer[],
110
- ): { key: string; value: string | number | string[] }[] {
111
- return answers.map((a) => ({
112
- key: a.key,
113
- value: a.value,
114
- }));
115
- }
116
-
117
- /**
118
- * Resolve the canonical session id for response linkage (contract §1/§7).
119
- * Primary source is `SessionManager.currentSessionId` — the always-on analytics
120
- * session id (`rs-…`) that the replay recording attaches to (and that `/sessions/:id`
121
- * keys on). `ReplaySdk.sessionId` (the orchestrator's recording id) is only set
122
- * while a recording is mid-flight and reads `null` at submit time, so it is a
123
- * fallback, not the primary. Returns `undefined` (not `null`) so the optional wire
124
- * field is simply omitted when no session is active.
125
- */
126
- function currentSessionId(): string | undefined {
127
- return (
128
- SessionManager.getExistingInstance()?.currentSessionId ??
129
- ReplaySdk.sessionId ??
130
- undefined
131
- );
132
- }
133
-
134
- /**
135
- * The host app version for by-version analytics (contract §1). Read from the
136
- * SAME synchronous source the SDK's rating lane already uses
137
- * (`SessionManager.currentSessionMetadata.appVersion`, populated from native
138
- * device info) — NOT hardcoded, and NOT the SDK build version. `undefined` when
139
- * no session metadata is available yet, so the optional field is omitted.
140
- */
141
- function currentAppVersion(): string | undefined {
142
- return SessionManager.getExistingInstance()?.currentSessionMetadata?.appVersion ?? undefined;
143
- }
144
-
145
- /**
146
- * Device platform as the `'android' | 'ios'` wire union (contract §1). Shared JS
147
- * path — NOT platform-gated — so Android and iOS both stamp their own value.
148
- * `Platform.OS` also yields `'windows' | 'macos' | 'web'`; those are out of the
149
- * SDK's mobile scope and intentionally collapse to `undefined` (field omitted).
150
- */
151
- function currentPlatform(): 'android' | 'ios' | undefined {
152
- return Platform.OS === 'android' || Platform.OS === 'ios' ? Platform.OS : undefined;
153
- }
154
-
155
- /**
156
- * Build the config-serve context (contract §1.3 query). ALWAYS carries `deviceId`
157
- * — the backend keys per-device test previews on it, so omitting it would hide a
158
- * "Send Test" preview from the very device it was queued for (T1). `platform`/
159
- * `appVersion` ride along for server-side targeting; both honest, both omitted when
160
- * unresolved. A caller-supplied `context` (manual show) overrides field-by-field.
161
- */
162
- function buildConfigContext(
163
- engage: EngageRendererFacade,
164
- override?: EngageConfigContext,
165
- ): EngageConfigContext {
166
- const identifiedUserId = engage.getIdentifiedUserId?.();
167
- return {
168
- deviceId: getDeviceId(),
169
- ...(currentPlatform() ? { platform: currentPlatform() } : {}),
170
- ...(currentAppVersion() ? { appVersion: currentAppVersion() } : {}),
171
- ...(identifiedUserId ? { endUserId: identifiedUserId } : {}),
172
- ...override,
173
- };
174
- }
175
-
176
- /**
177
- * Build the rating-funnel breakdown dimensions (ratings-rebuild CONTRACT §1.2 `dims`)
178
- * from the SAME live session metadata the response builder reads — NOT hardcoded.
179
- * Only honest, present values are included; `country`/`locale`/`acquisitionSource`
180
- * are intentionally omitted here (no on-device source yet) so the backend reports
181
- * them as absent rather than fabricated.
182
- */
183
- function currentRatingDims(): RatingFunnelEventDims {
184
- const meta = SessionManager.getExistingInstance()?.currentSessionMetadata;
185
- const platform = currentPlatform();
186
- return {
187
- ...(platform ? { platform } : {}),
188
- ...(meta?.appVersion ? { appVersion: meta.appVersion } : {}),
189
- ...(meta?.osVersion ? { osVersion: meta.osVersion } : {}),
190
- ...(meta?.deviceModel ? { deviceModel: meta.deviceModel } : {}),
191
- };
192
- }
193
-
194
- /**
195
- * The slice of the SDK facade the renderer consumes (the `engage` namespace).
196
- * Matching this shape — rather than importing the facade — keeps the renderer a
197
- * pure consumer of the foundation transport and trivially testable.
198
- */
199
- export interface EngageRendererFacade {
200
- /** External user id last supplied through ScaleBun.identify(). */
201
- getIdentifiedUserId?: () => string | undefined;
202
- fetchConfig: (context?: EngageConfigContext) => Promise<EngageConfigResponse | null>;
203
- trackCoachmarkEvent?: (name: string, properties: Record<string, unknown>) => void;
204
- submitResponse: (input: EngageResponseInput) => void;
205
- /**
206
- * [P3-e] Submit a response with native attachments (screenshot/voice). Optional
207
- * so a host wired to an older facade still renders prompts (media silently
208
- * degrades to a plain response).
209
- */
210
- submitResponseWithAttachments?: (
211
- input: EngageResponseInput,
212
- attachments: EngageAttachmentDraft[],
213
- ) => void;
214
- /**
215
- * [Slice D5] CONFIRMED submit used by the reusable submitting→success→error state
216
- * machine: resolves only on real server persistence, rejects on failure, idempotent
217
- * on `responseId`. Optional so an older facade still renders prompts (falls back to
218
- * the fire-and-forget lanes, which resolve immediately).
219
- */
220
- submitResponseConfirmed?: (
221
- input: EngageResponseInput,
222
- attachments: EngageAttachmentDraft[],
223
- opts: { responseId: string; onStage?: (stage: SubmitStage) => void },
224
- ) => Promise<void>;
225
- trackEvent: (input: EngageEventInput) => void;
226
- trackSurveyImpression?: (input: SurveyImpressionInput) => void;
227
- /**
228
- * Force-flush queued in-app events immediately (bypassing the batch threshold/timer).
229
- * Called right after a TEST preview's impression/dismiss ACK so the backend consumes
230
- * the one-shot preview row before the app can be backgrounded/killed — otherwise a
231
- * still-`delivered` row re-serves on the next reopen. Optional: an older facade simply
232
- * relies on the periodic flush (eventual consistency).
233
- */
234
- flushEvents?: () => void;
235
- /**
236
- * Submit a 1–5 app-store rating via the EXISTING rating lane (NOT
237
- * submitResponse — D1 keeps app-ratings a distinct funnel). The provider
238
- * passes a client `clientRatingId` for idempotency (D8). Optional so a host
239
- * that never shows RATING_PROMPT need not supply it.
240
- */
241
- submitRating?: (input: {
242
- rating: number;
243
- comment?: string;
244
- source?: 'in_app' | 'prompt' | 'manual';
245
- clientRatingId?: string;
246
- }) => unknown;
247
- /**
248
- * Emit a rating-funnel tracking fact (ratings-rebuild CONTRACT §1.2) — eligible/
249
- * shown/dismissed/selected/redirect. NOT a campaign response (D1). Optional so an
250
- * older facade still renders rating prompts (the funnel just goes uninstrumented).
251
- */
252
- emitRatingEvent?: (input: RatingFunnelEventInput) => void;
253
- /**
254
- * Trigger the native in-app review sheet (D5). Resolves `true` only when the OS
255
- * flow ran/resolved after a positive route, so the provider emits
256
- * `store_redirect_clicked` solely on a `true` result. Optional/fail-soft.
257
- */
258
- requestStoreReview?: () => Promise<boolean>;
259
- }
260
-
261
- /**
262
- * Host-app RATING_PROMPT routing config. The store deeplink is NEVER hardcoded —
263
- * the host supplies either a static `storeUrl` or an `onHighRating` callback (e.g.
264
- * `Linking.openURL(...)` / native `requestReview()`), and an `onLowRating` for the
265
- * feedback hand-off. The provider only decides WHICH callback to invoke.
266
- */
267
- export interface EngageRatingRouting extends RatingPromptConfig {
268
- /** App-store deeplink opened on a high rating (host owns the navigation). */
269
- storeUrl?: string;
270
- /** Called on a high rating (>= threshold). Receives the resolved store URL if set. */
271
- onHighRating?: (info: { rating: number; campaign: EngageCampaignConfig; storeUrl?: string }) => void;
272
- /** Called on a low rating when routing to feedback (host opens its form). */
273
- onLowRating?: (info: { rating: number; campaign: EngageCampaignConfig }) => void;
274
- }
275
-
276
- export interface ShowEngagePromptOptions {
277
- /** Restrict to specific campaign types. Defaults to `['NPS']` (NPS-first slice). */
278
- types?: EngageCampaignType[];
279
- /** Match a campaign's `targetScreen` to the current screen. */
280
- screen?: string;
281
- /**
282
- * Restrict to campaigns whose `trigger` equals this value (behavioral-trigger
283
- * path, e.g. `'screen_exit'`). Omitted ⇒ trigger-agnostic (manual fire).
284
- */
285
- trigger?: string;
286
- /** Event name for `custom_event` / `after_purchase` (matches `triggerConfig.event`). */
287
- event?: string;
288
- /** Action label for `failed_action` (matches `triggerConfig.action`). */
289
- action?: string;
290
- /** Fired `time_in_app` threshold, used to select only that configured timer. */
291
- seconds?: number;
292
- /** App/user context forwarded to the config fetch for server-side targeting. */
293
- context?: EngageConfigContext;
294
- }
295
-
296
- export interface ShowInAppMessageOptions {
297
- /** Match a message's `targetScreen` to the current screen. */
298
- screen?: string;
299
- /**
300
- * Restrict to messages whose `trigger` equals this value (behavioral-trigger
301
- * path, e.g. `'session_start'`). Omitted ⇒ trigger-agnostic (manual host call).
302
- */
303
- trigger?: string;
304
- /** Fired business/custom event name, matched against `message.eventName`. */
305
- event?: string;
306
- /** Fired action value for forward-compatible action-specific messages. */
307
- action?: string;
308
- /** Fired `time_delay` threshold, used to select only that configured message. */
309
- seconds?: number;
310
- /** App/user context forwarded to the config fetch for server-side targeting. */
311
- context?: EngageConfigContext;
312
- }
313
-
314
- /**
315
- * The host's deep-link sink for an in-app CTA tap. The SDK NEVER opens a URL — it
316
- * hands the resolved `deepLink` (and full message/cta) to the host, which decides
317
- * how to navigate (e.g. `Linking.openURL` / a router push). Only fired for a CTA
318
- * whose `action` resolves to a link with a `deepLink`.
319
- */
320
- export type InAppDeepLinkHandler = (info: {
321
- message: InAppMessageConfig;
322
- cta: NonNullable<InAppRenderSpec['cta']>;
323
- deepLink: string;
324
- }) => void;
325
-
326
- /** Imperative controller a host app uses to trigger a prompt / in-app message. */
327
- export interface EngagePromptController {
328
- /** Fetch config, pick an eligible campaign, and show it. Returns whether one was shown. */
329
- showEngagePrompt: (options?: ShowEngagePromptOptions) => Promise<boolean>;
330
- /** Dismiss the currently visible prompt (no response submitted). */
331
- dismissEngagePrompt: () => void;
332
- /**
333
- * Fetch config, pick an eligible in-app message (`inAppMessages[]`), and show
334
- * it. Returns whether one was shown. PRECEDENCE: a campaign prompt already on
335
- * screen wins — an in-app message is NOT shown over it (see provider doc).
336
- */
337
- showInAppMessage: (options?: ShowInAppMessageOptions) => Promise<boolean>;
338
- /** Dismiss the currently visible in-app message (emits a `dismiss` event). */
339
- dismissInAppMessage: () => void;
340
- }
341
-
342
- const EngagePromptContext = createContext<EngagePromptController | null>(null);
343
-
344
- export interface EngagePromptProviderProps {
345
- /** The SDK facade's `engage` namespace (e.g. `ScaleBun.engage`). */
346
- engage: EngageRendererFacade;
347
- /** Default campaign types to consider. Defaults to NPS-first. */
348
- defaultTypes?: EngageCampaignType[];
349
- /**
350
- * RATING_PROMPT routing config (host-supplied). The app-store deeplink and
351
- * feedback hand-off are NEVER hardcoded in the SDK — supplied here.
352
- */
353
- ratingRouting?: EngageRatingRouting;
354
- /**
355
- * Host sink for an in-app message CTA deep link. The SDK never opens URLs —
356
- * it surfaces the resolved `deepLink` here for the host to navigate. Optional:
357
- * a host that never uses link-action CTAs need not supply it.
358
- */
359
- onInAppDeepLink?: InAppDeepLinkHandler;
360
- /**
361
- * Required when a production gamified campaign is used. The SDK calls this
362
- * only at play/load/check-in/quiz resolution time; the host's authenticated
363
- * backend owns eligibility, granting, and the returned result.
364
- */
365
- onInAppGameRequest?: InAppGameRequestHandler;
366
- /**
367
- * DEV/TEST ONLY — poll interval (ms) for queued test previews while the app is
368
- * already foregrounded. `checkForPreview` runs on mount + every AppState→active,
369
- * which covers "reopen the app". But if you press "Send Test" while the app is
370
- * ALREADY open and in the foreground, there is no background→foreground edge to
371
- * trigger a refetch. Setting e.g. `previewPollMs={4000}` makes a warm, foreground
372
- * app pick up a Send Test within a few seconds with no app switch. Leave UNSET in
373
- * production (0/undefined ⇒ no polling — the foreground/cold-start fetch is enough).
374
- */
375
- previewPollMs?: number;
376
- children: ReactNode;
377
- }
378
-
379
- const DEFAULT_TYPES: EngageCampaignType[] = ['NPS'];
380
-
381
- /**
382
- * Provider that mounts the shared overlay and exposes the imperative controller
383
- * via `useEngagePrompt()`.
384
- */
385
- export function EngagePromptProvider({
386
- engage,
387
- defaultTypes,
388
- ratingRouting,
389
- onInAppDeepLink,
390
- onInAppGameRequest,
391
- previewPollMs,
392
- children,
393
- }: EngagePromptProviderProps): ReactElement {
394
- const [active, setActive] = useState<EngageCampaignConfig | null>(null);
395
- const [activeInApp, setActiveInApp] = useState<InAppMessageConfig | null>(null);
396
- const [inlineFallbackMessageId, setInlineFallbackMessageId] = useState<string | null>(null);
397
- const [inlinePlacementRevision, setInlinePlacementRevision] = useState(0);
398
- const inlinePlacementRegistry = useMemo(createEngageInlinePlacementRegistry, []);
399
- const shownInAppMessageIdRef = useRef<string | null>(null);
400
- // Resolved A/B arm for the active in-app message (D8/J6). Held in a ref: it is
401
- // render-stable metadata (the chosen `spec` drives the view, the `variant` id
402
- // rides the lifecycle events) and must not perturb the precedence that keys off
403
- // `activeInApp`. `null` when no message is active (or it has no experiment).
404
- const activeInAppVariantRef = useRef<
405
- { variant: InAppVariantId; spec: InAppRenderSpec } | null
406
- >(null);
407
- // Show-time screen of the currently-active campaign prompt (contract §1
408
- // `screen`). Held in a ref alongside `active` — it is render-irrelevant
409
- // metadata read only at submit, so it needs no re-render and must not perturb
410
- // the campaign-vs-in-app precedence which keys off `active`/`activeInApp`.
411
- const activeScreenRef = useRef<string | undefined>(undefined);
412
- const surveyStartedRef = useRef<string | null>(null);
413
- // [T1] Test previews bypass the throttle store and the backend re-serves them on
414
- // EVERY config poll until TTL — great for "reopen the app and see it again", but it
415
- // means a dismissed preview would re-appear on the next warm poll. This session set
416
- // records previews the user already closed (dismiss/CTA) so we don't re-show the SAME
417
- // one. It is in-memory only: a cold restart clears it, so reopening the app still
418
- // shows the queued test (the intended QA behavior).
419
- const consumedPreviewIds = useRef<Set<string>>(new Set());
420
- // [T1] Persistent dedupe for ONE-SHOT test delivery — survives cold restarts (unlike
421
- // consumedPreviewIds above). A testRunId is recorded here AFTER it actually renders, so
422
- // reopening the app does NOT re-show a test the device already displayed. This is the
423
- // fix for "close/open and the old test re-appears, and they stack up". Separate from the
424
- // throttle store so production frequency caps never block a fresh test and vice-versa.
425
- const seenTestsRef = useRef<EngageTestSeenStore | null>(null);
426
- if (seenTestsRef.current === null) {
427
- seenTestsRef.current = new EngageTestSeenStore();
428
- }
429
- // One persistent throttle store for the provider lifetime — SHARED by campaigns
430
- // and in-app messages so both honor the same per-id frequency caps.
431
- const storeRef = useRef<EngageThrottleStore | null>(null);
432
- if (storeRef.current === null) {
433
- storeRef.current = new EngageThrottleStore();
434
- }
435
- const types = defaultTypes ?? DEFAULT_TYPES;
436
-
437
- // ─── Reusable submit state machine (Slice D5) ────────────────────────────
438
- // ONE machine for EVERY question type. `responseIdRef` is the idempotency key,
439
- // generated once per prompt and REUSED on retry so a re-send can't duplicate.
440
- // `submitCtxRef` holds the campaign+result so retry re-runs the same submit.
441
- const [submitStatus, setSubmitStatus] = useState<EngageSubmitStatus>({ phase: 'idle' });
442
- const responseIdRef = useRef<string | null>(null);
443
- const submitCtxRef = useRef<{ campaign: EngageCampaignConfig; result: EngagePromptResult } | null>(null);
444
- const successTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
445
-
446
- const resetSubmit = useCallback((): void => {
447
- if (successTimerRef.current) {
448
- clearTimeout(successTimerRef.current);
449
- successTimerRef.current = null;
450
- }
451
- responseIdRef.current = null;
452
- submitCtxRef.current = null;
453
- setSubmitStatus({ phase: 'idle' });
454
- }, []);
455
-
456
- // Single campaign tracking lane (mirrors trackInApp). `impression` on show,
457
- // `completion` on submit, `dismiss` on close — the §2.5 analytics events that
458
- // back the response-rate / completion KPIs (P3-e #2). Distinct from the local
459
- // throttle store, which only counts impressions for frequency caps.
460
- const trackCampaign = useCallback(
461
- (campaign: EngageCampaignConfig, eventType: 'impression' | 'completion' | 'dismiss') => {
462
- try {
463
- engage.trackEvent({
464
- campaignId: campaign.id,
465
- channel: 'inapp',
466
- type: eventType,
467
- deviceId: getDeviceId(),
468
- });
469
- } catch (err) {
470
- logger.debug('[Engage] trackEvent failed:', (err as Error)?.message);
471
- }
472
- },
473
- [engage],
474
- );
475
-
476
- const trackSurveyStage = useCallback(
477
- (campaign: EngageCampaignConfig, stage: 'shown' | 'started'): void => {
478
- if (campaign.type === 'RATING_PROMPT' || campaign.type === 'RATINGS_PROMPT') return;
479
- try {
480
- engage.trackSurveyImpression?.({
481
- surveyId: campaign.id,
482
- stage,
483
- sessionId: currentSessionId(),
484
- deviceId: getDeviceId(),
485
- platform: currentPlatform(),
486
- appVersion: currentAppVersion(),
487
- screen: activeScreenRef.current,
488
- });
489
- } catch (err) {
490
- logger.debug('[Engage] survey impression failed:', (err as Error)?.message);
491
- }
492
- },
493
- [engage],
494
- );
495
-
496
- const isRatingType = (type: EngageCampaignType): boolean =>
497
- type === 'RATING_PROMPT' || type === 'RATINGS_PROMPT';
498
-
499
- // Rating-funnel emit lane (ratings-rebuild CONTRACT §1.2). Distinct from
500
- // trackCampaign (the §2.5 in-app analytics lane) and from submitRating (the D1
501
- // app-rating lane): these are dimensioned FACTS feeding the Overview funnel and
502
- // are NEVER counted as responses. `stars` rides only on `rating_selected`.
503
- const emitRatingFunnel = useCallback(
504
- (
505
- campaign: EngageCampaignConfig,
506
- type: RatingFunnelEventType,
507
- extra?: { stars?: number },
508
- ): void => {
509
- try {
510
- engage.emitRatingEvent?.({
511
- type,
512
- sessionId: currentSessionId(),
513
- userId: engage.getIdentifiedUserId?.(),
514
- promptId: campaign.id,
515
- triggerId: campaign.trigger,
516
- ...(extra?.stars !== undefined ? { stars: extra.stars } : {}),
517
- dims: currentRatingDims(),
518
- });
519
- } catch (err) {
520
- logger.debug('[Engage] emitRatingEvent failed:', (err as Error)?.message);
521
- }
522
- },
523
- [engage],
524
- );
525
-
526
- const dismissEngagePrompt = useCallback((): void => {
527
- setActive((current) => {
528
- if (current) {
529
- trackCampaign(current, 'dismiss');
530
- // Rating funnel: a close without a selection is a funnel drop.
531
- if (isRatingType(current.type)) emitRatingFunnel(current, 'rating_prompt_dismissed');
532
- }
533
- return null;
534
- });
535
- activeScreenRef.current = undefined;
536
- surveyStartedRef.current = null;
537
- resetSubmit();
538
- }, [trackCampaign, resetSubmit, emitRatingFunnel]);
539
-
540
- // The actual submit run — used by both the first submit and a retry. Reads
541
- // `submitCtxRef` + `responseIdRef` so a retry re-sends with the SAME idempotency
542
- // key. Resolves to `success` ONLY on confirmed persistence; `error` on failure.
543
- const runSubmit = useCallback(async (): Promise<void> => {
544
- const ctx = submitCtxRef.current;
545
- if (!ctx) return;
546
- const { campaign, result } = ctx;
547
- const store = storeRef.current;
548
- const responseId = responseIdRef.current ?? (responseIdRef.current = ratingUuid());
549
-
550
- setSubmitStatus({ phase: 'submitting', stage: 'saving' });
551
- try {
552
- if (isRatingType(campaign.type)) {
553
- // RATING_PROMPT submits via the EXISTING app-rating lane (D1), NOT
554
- // submitResponse, with a client clientRatingId for idempotency. Awaited
555
- // so the loading state reflects the real round-trip.
556
- const rating = typeof result.score === 'number' ? result.score : 0;
557
- await Promise.resolve(
558
- engage.submitRating?.({ rating, source: 'prompt', clientRatingId: responseId }),
559
- );
560
- // Funnel: user picked 1–5★ (carries stars). Emitted regardless of route.
561
- emitRatingFunnel(campaign, 'rating_selected', { stars: rating });
562
- // Routing decided by the view (`route`), executed against host-supplied
563
- // callbacks/URL — the deeplink is never hardcoded.
564
- if (result.route === 'store') {
565
- // D5: prefer the genuinely-native in-app review sheet. `store_redirect_clicked`
566
- // fires ONLY when an actual store route resolves (native review ran, or a
567
- // host-supplied deeplink/callback executed) — never speculatively.
568
- let redirected = false;
569
- if (engage.requestStoreReview) {
570
- redirected = await engage.requestStoreReview();
571
- }
572
- const storeUrl = campaign.storeUrl ?? ratingRouting?.storeUrl;
573
- if (ratingRouting?.onHighRating) {
574
- ratingRouting.onHighRating({ rating, campaign, storeUrl });
575
- redirected = true;
576
- } else if (!redirected && storeUrl) {
577
- redirected = await Linking.openURL(storeUrl).then(() => true).catch(() => false);
578
- }
579
- if (redirected) emitRatingFunnel(campaign, 'store_redirect_clicked');
580
- } else if (result.route === 'feedback') {
581
- ratingRouting?.onLowRating?.({ rating, campaign });
582
- }
583
- } else {
584
- // [v2] response context (contract §1): session linkage + originating
585
- // screen + platform + app version. All optional; omitted when unresolved.
586
- // We send ONLY the new response fact + the sessionId; replay/errors/network
587
- // are NOT re-sent — the read side joins them by sessionId.
588
- const input: EngageResponseInput = Array.isArray(result.answers)
589
- ? {
590
- surveyId: campaign.id,
591
- text: result.reason,
592
- answers: toWireAnswers(result.answers),
593
- deviceId: getDeviceId(),
594
- sessionId: currentSessionId(),
595
- screen: activeScreenRef.current,
596
- platform: currentPlatform(),
597
- appVersion: currentAppVersion(),
598
- }
599
- : {
600
- surveyId: campaign.id,
601
- score: result.score,
602
- text: result.reason,
603
- tags: result.tags,
604
- deviceId: getDeviceId(),
605
- sessionId: currentSessionId(),
606
- screen: activeScreenRef.current,
607
- platform: currentPlatform(),
608
- appVersion: currentAppVersion(),
609
- };
610
-
611
- const drafts = result.attachments ?? [];
612
- if (engage.submitResponseConfirmed) {
613
- // Confirmed lane: awaits real persistence (presign+PUT bytes → response
614
- // POST), reports stages, idempotent on responseId, throws on failure.
615
- await engage.submitResponseConfirmed(input, drafts, {
616
- responseId,
617
- onStage: (stage) => setSubmitStatus({ phase: 'submitting', stage }),
618
- });
619
- } else if (drafts.length > 0 && engage.submitResponseWithAttachments) {
620
- // Fallback (older facade): fire-and-forget — resolves immediately.
621
- engage.submitResponseWithAttachments(input, drafts);
622
- } else {
623
- engage.submitResponse(input);
624
- }
625
- }
626
-
627
- store?.markCompleted(campaign.id);
628
- trackCampaign(campaign, 'completion');
629
- setSubmitStatus({ phase: 'success' });
630
- // Brief success beat, then close + reset.
631
- successTimerRef.current = setTimeout(() => {
632
- setActive(null);
633
- activeScreenRef.current = undefined;
634
- resetSubmit();
635
- }, 1100);
636
- } catch (err) {
637
- logger.warn('[Engage] submit failed:', (err as Error)?.message);
638
- setSubmitStatus({ phase: 'error', message: (err as Error)?.message });
639
- }
640
- }, [engage, trackCampaign, ratingRouting, resetSubmit, emitRatingFunnel]);
641
-
642
- const handleSubmit = useCallback(
643
- (campaign: EngageCampaignConfig, result: EngagePromptResult): void => {
644
- // Guard double-submit: ignore re-fires once a submit is in flight or done.
645
- if (submitCtxRef.current && responseIdRef.current) return;
646
- submitCtxRef.current = { campaign, result };
647
- void runSubmit();
648
- },
649
- [runSubmit],
650
- );
651
-
652
- const closeAfterSubmit = useCallback((): void => {
653
- setActive(null);
654
- activeScreenRef.current = undefined;
655
- resetSubmit();
656
- }, [resetSubmit]);
657
-
658
- const showEngagePrompt = useCallback(
659
- async (options?: ShowEngagePromptOptions): Promise<boolean> => {
660
- const store = storeRef.current;
661
- if (!store) return false;
662
- try {
663
- const config = await engage.fetchConfig(options?.context);
664
- if (!config || !Array.isArray(config.campaigns)) return false;
665
-
666
- const campaign = selectCampaignToRender(config.campaigns, store, {
667
- // A behavioral-trigger fire (`options.trigger`) is type-agnostic:
668
- // the trigger itself selects the campaign, so don't layer the
669
- // NPS-first default on top (it would drop FEEDBACK/CSAT/etc.).
670
- // Manual fires keep the provider default unless overridden.
671
- types: options?.types ?? (options?.trigger ? undefined : types),
672
- screen: options?.screen,
673
- trigger: options?.trigger,
674
- seconds: options?.seconds,
675
- event: options?.event,
676
- action: options?.action,
677
- });
678
- if (!campaign) {
679
- logger.debug('[Engage] no eligible campaign to render');
680
- return false;
681
- }
682
-
683
- // Record the impression up front so throttle counts it even if the
684
- // user backgrounds the app before submitting.
685
- store.recordImpression(campaign.id);
686
- // Emit the §2.5 impression event (P3-e #2) so the dashboard's
687
- // response-rate / completion KPIs have a real denominator — the
688
- // prompt path previously only counted impressions locally.
689
- trackCampaign(campaign, 'impression');
690
- // Rating funnel (CONTRACT §1.1): selection passed the throttle/anti-
691
- // annoyance gate (→ eligible) and we are about to render (→ shown).
692
- if (isRatingType(campaign.type)) {
693
- emitRatingFunnel(campaign, 'rating_prompt_eligible');
694
- emitRatingFunnel(campaign, 'rating_prompt_shown');
695
- }
696
- // Stamp the show-time screen alongside the active campaign so
697
- // handleSubmit can attribute the response to its originating screen.
698
- activeScreenRef.current = options?.screen;
699
- surveyStartedRef.current = null;
700
- trackSurveyStage(campaign, 'shown');
701
- resetSubmit();
702
- setActive(campaign);
703
- return true;
704
- } catch (err) {
705
- logger.warn('[Engage] showEngagePrompt failed:', (err as Error)?.message);
706
- return false;
707
- }
708
- },
709
- [engage, types, trackCampaign, trackSurveyStage, emitRatingFunnel],
710
- );
711
-
712
- // ─── In-app message lifecycle (the second Engage family, §2.4/§2.5) ──────
713
- // Reuses the SAME transport (`trackEvent`), the SAME throttle store, and the
714
- // SAME provider — NOT a parallel provider. Events: impression on show, click
715
- // on CTA tap, completion on a finished tour, dismiss on close/skip.
716
-
717
- const trackInApp = useCallback(
718
- (
719
- message: InAppMessageConfig,
720
- eventType:
721
- | 'impression'
722
- | 'completion'
723
- | 'click'
724
- | 'dismiss'
725
- | 'interaction'
726
- | 'render_degraded'
727
- | 'render_failed'
728
- | 'cta_open_failed',
729
- extraMeta?: Record<string, unknown>,
730
- ): void => {
731
- try {
732
- // A/B attribution (D8/J6): when the active message resolved to an
733
- // experiment arm, tag every lifecycle event with the chosen variant
734
- // via the existing optional `meta` field — NO event-schema change
735
- // (EngageEventUpload already carries `meta`; the backend EngageEvent
736
- // accepts it). Plain (non-experiment) messages send no variant key.
737
- const variant = activeInAppVariantRef.current?.variant;
738
- // [T1] Test-preview tagging: a preview-flagged message stamps
739
- // `previewId` + `isTest` + `source` so (a) the backend CONSUMES the
740
- // matching preview row on the impression and (b) production KPIs
741
- // exclude the traffic. Normal campaign messages omit all of it.
742
- const isPreview = message.preview === true || message.isTest === true;
743
- const meta = {
744
- ...(variant ? { variant } : {}),
745
- // testRunId correlates a specific "Send Test" run to its render/click/
746
- // dismiss in the dashboard; rides the existing meta field (no schema churn).
747
- ...(message.testRunId ? { testRunId: message.testRunId } : {}),
748
- ...(extraMeta ?? {}),
749
- };
750
- engage.trackEvent({
751
- campaignId: message.id,
752
- channel: 'inapp',
753
- type: eventType,
754
- deviceId: getDeviceId(),
755
- ...(message.previewId ? { previewId: message.previewId } : {}),
756
- ...(isPreview ? { isTest: true, source: 'test_preview' } : {}),
757
- ...(variant ? { variantId: variant } : {}),
758
- ...(Object.keys(meta).length > 0 ? { meta } : {}),
759
- });
760
- } catch (err) {
761
- logger.debug('[Engage] in-app trackEvent failed:', (err as Error)?.message);
762
- }
763
- },
764
- [engage],
765
- );
766
-
767
- // [T1] In-app message actually mounted on screen. The impression event IS the test
768
- // ACK to the backend (which consumes the preview row → 'rendered'). We ALSO persist the
769
- // testRunId locally so a cold restart can't re-show it before the ACK is processed
770
- // server-side — the one-shot guarantee survives a flaky/slow network.
771
- const handleInAppShow = useCallback(
772
- (message: InAppMessageConfig): void => {
773
- // A slot can be replaced or degrade to an overlay while the same message
774
- // remains active. Treat that as one mount lifecycle, not two impressions.
775
- if (shownInAppMessageIdRef.current === message.id) return;
776
- shownInAppMessageIdRef.current = message.id;
777
- trackInApp(message, 'impression');
778
- if (message.preview === true || message.isTest === true) {
779
- const runId = message.testRunId ?? message.previewId ?? message.id;
780
- seenTestsRef.current?.markSeen(runId);
781
- // Eager-flush so the backend consumes the one-shot preview row NOW — a fast
782
- // close→kill→reopen must not re-serve a still-`delivered` test.
783
- engage.flushEvents?.();
784
- logger.debug(`[Engage] test ${runId} render displayed ACK sent (impression), flushed`);
785
- }
786
- },
787
- [trackInApp, engage],
788
- );
789
-
790
- const finalizeInAppMessage = useCallback((eventType: 'dismiss' | 'completion'): void => {
791
- setActiveInApp((current) => {
792
- if (current) {
793
- const isTest = current.preview === true || current.isTest === true;
794
- const runId = current.testRunId ?? current.previewId ?? current.id;
795
- logger.debug(`[Engage] in-app ${eventType}${isTest ? ` (test ${runId})` : ''}`);
796
- trackInApp(current, eventType);
797
- if (isTest) {
798
- consumedPreviewIds.current.add(current.previewId ?? current.id);
799
- seenTestsRef.current?.markSeen(runId);
800
- engage.flushEvents?.();
801
- logger.debug(`[Engage] test ${runId} ${eventType} persisted + ACK flushed`);
802
- } else {
803
- storeRef.current?.markCompleted(current.id);
804
- engage.flushEvents?.();
805
- logger.debug(`[Engage] in-app ${current.id} ${eventType} — marked done on device`);
806
- }
807
- }
808
- return null;
809
- });
810
- // Clear the resolved A/B arm after the lifecycle event has read it.
811
- shownInAppMessageIdRef.current = null;
812
- activeInAppVariantRef.current = null;
813
- }, [trackInApp, engage]);
814
-
815
- const dismissInAppMessage = useCallback((): void => {
816
- finalizeInAppMessage('dismiss');
817
- }, [finalizeInAppMessage]);
818
-
819
- const completeInAppMessage = useCallback((): void => {
820
- finalizeInAppMessage('completion');
821
- }, [finalizeInAppMessage]);
822
-
823
- const handleInAppCta = useCallback(
824
- (message: InAppMessageConfig, cta: NonNullable<InAppRenderSpec['cta']>): void => {
825
- if (shouldTrackInAppCta(cta)) trackInApp(message, 'click');
826
- // Deep-link opening is the host's job — surface it, never open it here.
827
- if (cta.action === 'link') {
828
- if (!cta.deepLink) {
829
- // Misconfigured link CTA (no deepLink) — observable, not silent (T1 G17).
830
- logger.warn('[Engage] link CTA has no deepLink');
831
- trackInApp(message, 'cta_open_failed', { reason: 'missing_deeplink' });
832
- } else if (!onInAppDeepLink) {
833
- // Host wired no deep-link sink — the link can't open. Diagnose it.
834
- logger.warn('[Engage] link CTA but no onInAppDeepLink handler supplied');
835
- trackInApp(message, 'cta_open_failed', { reason: 'no_handler' });
836
- } else {
837
- try {
838
- onInAppDeepLink({ message, cta, deepLink: cta.deepLink });
839
- } catch (err) {
840
- logger.warn('[Engage] onInAppDeepLink threw:', (err as Error)?.message);
841
- trackInApp(message, 'cta_open_failed', {
842
- reason: (err as Error)?.message ?? 'handler_threw',
843
- });
844
- }
845
- }
846
- }
847
- // A test preview the user acted on is done — don't re-show it next poll AND
848
- // persist it durably (a CTA tap is as final as a close), then flush the ACK.
849
- if (message.preview === true || message.isTest === true) {
850
- const runId = message.testRunId ?? message.previewId ?? message.id;
851
- consumedPreviewIds.current.add(message.previewId ?? message.id);
852
- seenTestsRef.current?.markSeen(runId);
853
- engage.flushEvents?.();
854
- logger.debug(`[Engage] test ${runId} CTA — persisted locally + ACK flushed`);
855
- } else {
856
- // Production message: a CTA tap is as final as a close — don't re-show it.
857
- storeRef.current?.markCompleted(message.id);
858
- engage.flushEvents?.();
859
- logger.debug(`[Engage] in-app ${message.id} CTA tapped — marked done on device`);
860
- }
861
- setActiveInApp(null);
862
- // Any enabled click event above already read the arm; clear it now.
863
- shownInAppMessageIdRef.current = null;
864
- activeInAppVariantRef.current = null;
865
- },
866
- [trackInApp, onInAppDeepLink, engage],
867
- );
868
-
869
- // [T1] Pick the ONE test preview to render from a fetched config: drop any already
870
- // displayed (persistent seen store) or closed-this-session, then take the LATEST by
871
- // createdAt. One-shot + latest-only is what stops the "tests stack up 1,2,3…" bug.
872
- const pickLatestPreview = useCallback(
873
- (messages: InAppMessageConfig[]): InAppMessageConfig | null => {
874
- const latest = selectLatestUnseenTest(messages, {
875
- seen: (runId) => seenTestsRef.current?.has(runId) ?? false,
876
- consumed: (key) => consumedPreviewIds.current.has(key),
877
- });
878
- if (latest) logger.debug(`[Engage] selected latest test ${testRunIdOf(latest)}`);
879
- return latest;
880
- },
881
- [],
882
- );
883
-
884
- // [T1] Render a test preview IMMEDIATELY — bypassing trigger/throttle/schedule/
885
- // sample/variant. No throttle write (a preview must stay re-showable until it is
886
- // actually rendered + its impression consumes it server-side). Idempotent: if this
887
- // exact preview is already on screen, don't re-mount (avoids a duplicate impression).
888
- const showPreview = useCallback(
889
- (preview: InAppMessageConfig): boolean => {
890
- // PRECEDENCE: a campaign prompt owns the screen — defer (the backend re-serves
891
- // the preview on every poll until TTL, so the next foreground retries).
892
- if (active !== null) {
893
- logger.debug('[Engage] campaign prompt active — preview deferred');
894
- return false;
895
- }
896
- if (activeInApp?.id === preview.id) return true;
897
- const runId = preview.testRunId ?? preview.previewId ?? preview.id;
898
- // Already closed this preview this session — don't re-show on the next poll.
899
- if (consumedPreviewIds.current.has(preview.previewId ?? preview.id)) {
900
- return false;
901
- }
902
- // [T1] One-shot: already DISPLAYED on this device (persisted across restarts)
903
- // never re-show, even if the backend briefly re-serves a not-yet-ACKed row.
904
- if (seenTestsRef.current?.has(runId)) {
905
- logger.debug(`[Engage] test ${runId} already displayed on device — skip`);
906
- return false;
907
- }
908
- activeInAppVariantRef.current = null;
909
- setActiveInApp(preview);
910
- logger.debug(`[Engage] test ${runId} render started`);
911
- return true;
912
- },
913
- [active, activeInApp],
914
- );
915
-
916
- const showInAppMessage = useCallback(
917
- async (options?: ShowInAppMessageOptions): Promise<boolean> => {
918
- const store = storeRef.current;
919
- if (!store) return false;
920
- // PRECEDENCE: a campaign prompt on screen wins — never stack an in-app
921
- // message over an open survey/NPS/rating prompt.
922
- if (active !== null) {
923
- logger.debug('[Engage] campaign prompt active — in-app message deferred');
924
- return false;
925
- }
926
- try {
927
- const config = await engage.fetchConfig(buildConfigContext(engage, options?.context));
928
- if (!config || !Array.isArray(config.inAppMessages)) return false;
929
-
930
- // [T1] A queued test preview wins immediately, bypassing throttle/variant.
931
- // Pick the LATEST unseen one (one-shot dedupe) — never stack old tests.
932
- const preview = pickLatestPreview(config.inAppMessages);
933
- if (preview) return showPreview(preview);
934
-
935
- // Normal selection runs over NON-preview messages only a preview must
936
- // never enter the throttle store (it would falsely cap a real campaign id).
937
- const normal = config.inAppMessages.filter(
938
- (m) => !(m.preview === true || m.isTest === true),
939
- );
940
- const message = selectInAppToRender(normal, store, {
941
- screen: options?.screen,
942
- trigger: options?.trigger,
943
- seconds: options?.seconds,
944
- event: options?.event,
945
- action: options?.action,
946
- });
947
- if (!message) {
948
- logger.debug('[Engage] no eligible in-app message to render');
949
- return false;
950
- }
951
-
952
- // A/B variant selection (D8/J6): deterministically bucket this
953
- // device into arm A (base spec) or B (experiment.variantB) BEFORE
954
- // showing, so the impression event (and click/dismiss) attribute to
955
- // the arm the user actually saw. Only retained for attribution when
956
- // the message actually carries an enabled experiment; otherwise the
957
- // resolved spec is just the base spec and no variant key is emitted.
958
- const resolved = selectInAppVariant(message, getDeviceId());
959
- activeInAppVariantRef.current = message.experiment?.enabled
960
- ? resolved
961
- : null;
962
- // Record up front so the throttle counts it (impression also emitted
963
- // by the view's onShow → trackEvent for the analytics lane). Pass the
964
- // content revision so an edited campaign starts a fresh cap generation.
965
- store.recordImpression(message.id, Date.now(), message.revision);
966
- setActiveInApp(message);
967
- return true;
968
- } catch (err) {
969
- logger.warn('[Engage] showInAppMessage failed:', (err as Error)?.message);
970
- return false;
971
- }
972
- },
973
- [engage, active, showPreview, pickLatestPreview],
974
- );
975
-
976
- // ─── Cold-start + foreground preview check (T1) ──────────────────────────
977
- // "Send Test → reopen the app → see it immediately, EVERY time": the SDK fetches
978
- // config (deviceId in context, HTTP cache-bypassed) on mount AND on every
979
- // AppState→active, and renders any queued preview at once no trigger, no throttle,
980
- // no schedule. The backend keeps serving the preview on every poll until its TTL, so
981
- // a tester sees it on each reopen (rendering records lifecycle but does not consume).
982
- const checkForPreview = useCallback(async (): Promise<void> => {
983
- if (active !== null) return;
984
- try {
985
- const config = await engage.fetchConfig(buildConfigContext(engage));
986
- // One-shot + latest-only: render the newest test not already displayed.
987
- const preview = pickLatestPreview(config?.inAppMessages ?? []);
988
- if (preview) showPreview(preview);
989
- } catch (err) {
990
- logger.warn('[Engage] preview check failed:', (err as Error)?.message);
991
- }
992
- }, [engage, active, showPreview, pickLatestPreview]);
993
-
994
- useEffect(() => {
995
- // Drop stale seen-test entries (past TTL) so the persistent set stays bounded.
996
- seenTestsRef.current?.prune();
997
- void checkForPreview();
998
- const onLifecycle = (state: string): void => {
999
- if (state === 'active') void checkForPreview();
1000
- };
1001
- appLifecycle.addListener(onLifecycle);
1002
- return () => appLifecycle.removeListener(onLifecycle);
1003
- }, [checkForPreview]);
1004
-
1005
- // DEV/TEST warm-foreground poll (T1): covers "Send Test while the app is already
1006
- // open" no background→foreground edge fires, so without this you'd have to switch
1007
- // apps to see it. Only runs when the host opts in via `previewPollMs` (unset in prod).
1008
- useEffect(() => {
1009
- if (!previewPollMs || previewPollMs <= 0) return;
1010
- logger.debug(`[Engage] test-preview warm poll every ${previewPollMs}ms (dev)`);
1011
- const interval = setInterval(() => {
1012
- void checkForPreview();
1013
- }, previewPollMs);
1014
- return () => clearInterval(interval);
1015
- }, [previewPollMs, checkForPreview]);
1016
-
1017
- // ─── Always-on behavioral triggers (P3-e #3 / Slice E) ───────────────────
1018
- // The trigger engine wires every behavioral trigger (session_start, app_open,
1019
- // screen_view/exit, time_in_app, n_sessions, after_purchase, before_logout,
1020
- // custom_event, failed_action, after_crash/js_error, network_error, slow_screen,
1021
- // and the native gesture triggers) to the SDK signals the app already produces.
1022
- // A fire first tries the survey/rating campaign path (showEngagePrompt); if no
1023
- // campaign matches, the SAME trigger drives an in-app message (showInAppMessage).
1024
- // Both families share the throttle store, so a behavioral fire never double-shows.
1025
- // The engine buffers events fired before the first config arrives and replays
1026
- // them once it does (late binding), honoring each trigger's remaining delay.
1027
- useEffect(() => {
1028
- return installEngageTriggerEngine({
1029
- fire: (options) => {
1030
- void (async () => {
1031
- const shown = await showEngagePrompt(options);
1032
- if (!shown && options.trigger) {
1033
- await showInAppMessage({
1034
- trigger: options.trigger,
1035
- screen: options.screen,
1036
- event: options.event,
1037
- action: options.action,
1038
- seconds: options.seconds,
1039
- });
1040
- }
1041
- })();
1042
- },
1043
- fetchConfig: () => engage.fetchConfig(buildConfigContext(engage)),
1044
- });
1045
- }, [showEngagePrompt, showInAppMessage, engage]);
1046
-
1047
- const controller = useMemo<EngagePromptController>(
1048
- () => ({
1049
- showEngagePrompt,
1050
- dismissEngagePrompt,
1051
- showInAppMessage,
1052
- dismissInAppMessage,
1053
- }),
1054
- [showEngagePrompt, dismissEngagePrompt, showInAppMessage, dismissInAppMessage],
1055
- );
1056
-
1057
- const activeInAppSpec = activeInAppVariantRef.current?.spec ?? activeInApp?.spec;
1058
- const activeInAppPresentation = activeInApp && activeInAppSpec
1059
- ? resolveInAppPresentation({
1060
- layout: activeInApp.layout,
1061
- variantKey: activeInApp.variantKey,
1062
- spec: activeInAppSpec,
1063
- })
1064
- : null;
1065
- // Route by the canonical mount, not a variant allow-list. This covers every
1066
- // current/future inline variant, including smart_inbox_message.
1067
- const routesToInlineSlot = activeInAppPresentation?.mountModel === 'inline';
1068
- const inlinePlacement = resolveEngageInlinePlacement(activeInAppSpec);
1069
-
1070
- const handleActiveInAppShow = useCallback((): void => {
1071
- if (!activeInApp) return;
1072
- handleInAppShow(activeInApp);
1073
- const tourId = activeInApp.spec.ext?.coachTourId;
1074
- const first = Array.isArray(activeInApp.spec.ext?.steps) ? activeInApp.spec.ext?.steps[0] as Record<string, unknown> | undefined : undefined;
1075
- if (typeof tourId === 'string') {
1076
- engage.trackCoachmarkEvent?.('$coachmark_tour_started', { tourId });
1077
- engage.trackCoachmarkEvent?.('$coachmark_step_entered', {
1078
- tourId,
1079
- stepId: String(first?.id ?? first?.anchorKey ?? '1'),
1080
- });
1081
- }
1082
- }, [activeInApp, handleInAppShow, engage]);
1083
- const handleActiveInAppCta = useCallback((cta: NonNullable<InAppRenderSpec['cta']>): void => {
1084
- if (activeInApp) handleInAppCta(activeInApp, cta);
1085
- }, [activeInApp, handleInAppCta]);
1086
- const handleActiveInAppInteraction = useCallback((interaction: InAppMechanicInteraction): void => {
1087
- if (!activeInApp) return;
1088
- trackInApp(activeInApp, 'interaction', { interaction });
1089
- const tourId = activeInApp.spec.ext?.coachTourId;
1090
- if (typeof tourId !== 'string' || interaction.mechanic !== 'coachmark_spotlight') return;
1091
- const meta = interaction.meta ?? {};
1092
- const stepId = String(meta.anchorKey ?? meta.step ?? meta.toStep ?? 'unknown');
1093
- if (interaction.result === 'step_viewed' && interaction.phase !== 'started') {
1094
- engage.trackCoachmarkEvent?.('$coachmark_step_entered', { tourId, stepId });
1095
- } else if (interaction.phase === 'completed') {
1096
- engage.trackCoachmarkEvent?.('$coachmark_tour_completed', { tourId, stepId });
1097
- } else if (interaction.result === 'skipped') {
1098
- engage.trackCoachmarkEvent?.('$coachmark_tour_abandoned', { tourId, stepId });
1099
- }
1100
- }, [activeInApp, trackInApp, engage]);
1101
- const handleActiveInAppRenderError = useCallback((reason: string): void => {
1102
- if (activeInApp) {
1103
- trackInApp(activeInApp, 'render_failed', { reason });
1104
- const tourId = activeInApp.spec.ext?.coachTourId;
1105
- if (typeof tourId === 'string') {
1106
- const anchorKey = reason.startsWith('anchor_not_found:') ? reason.slice('anchor_not_found:'.length) : 'unknown';
1107
- engage.trackCoachmarkEvent?.('$coachmark_anchor_failed', {
1108
- tourId,
1109
- stepId: anchorKey,
1110
- reason: reason.startsWith('anchor_not_found:') ? 'anchor_not_found' : reason,
1111
- });
1112
- }
1113
- }
1114
- shownInAppMessageIdRef.current = null;
1115
- activeInAppVariantRef.current = null;
1116
- setActiveInApp(null);
1117
- }, [activeInApp, trackInApp, engage]);
1118
-
1119
- useEffect(() => {
1120
- if (!routesToInlineSlot || !inlinePlacement.placementKey) return;
1121
- return inlinePlacementRegistry.subscribe(inlinePlacement.placementKey, () => {
1122
- setInlinePlacementRevision((revision) => revision + 1);
1123
- });
1124
- }, [inlinePlacement.placementKey, inlinePlacementRegistry, routesToInlineSlot]);
1125
-
1126
- useEffect(() => {
1127
- if (!activeInApp || active !== null || !routesToInlineSlot) {
1128
- setInlineFallbackMessageId(null);
1129
- return;
1130
- }
1131
-
1132
- const messageId = activeInApp.id;
1133
- // Once this message has visibly degraded, keep it on that mount. Moving it
1134
- // into a late slot would create a second renderer and a misleading lifecycle.
1135
- if (inlineFallbackMessageId === messageId) return;
1136
- const reason = inlinePlacement.placementKey
1137
- ? `inline_placement_not_found:${inlinePlacement.placementKey}`
1138
- : 'missing_inline_placement_key';
1139
- const delivery: EngageInlineDelivery = {
1140
- message: activeInApp,
1141
- spec: activeInAppSpec,
1142
- onShow: handleActiveInAppShow,
1143
- onCta: handleActiveInAppCta,
1144
- onDismiss: dismissInAppMessage,
1145
- onComplete: completeInAppMessage,
1146
- onInteraction: handleActiveInAppInteraction,
1147
- onInAppGameRequest,
1148
- onRenderError: handleActiveInAppRenderError,
1149
- };
1150
- const delivered = inlinePlacementRegistry.deliver(
1151
- inlinePlacement.placementKey,
1152
- delivery,
1153
- );
1154
-
1155
- if (delivered) {
1156
- setInlineFallbackMessageId(null);
1157
- } else {
1158
- inlinePlacementRegistry.clear(inlinePlacement.placementKey, messageId);
1159
- if (inlinePlacement.fallback === 'skip') {
1160
- setInlineFallbackMessageId(null);
1161
- handleActiveInAppRenderError(reason);
1162
- } else {
1163
- // Preserve the legacy visible behavior, but make the degradation observable.
1164
- trackInApp(activeInApp, 'render_degraded', {
1165
- reason,
1166
- placementKey: inlinePlacement.placementKey,
1167
- fallback: 'overlay',
1168
- });
1169
- setInlineFallbackMessageId(messageId);
1170
- }
1171
- }
1172
-
1173
- return () => inlinePlacementRegistry.clear(inlinePlacement.placementKey, messageId);
1174
- }, [
1175
- active,
1176
- activeInApp,
1177
- activeInAppSpec,
1178
- dismissInAppMessage,
1179
- completeInAppMessage,
1180
- handleActiveInAppCta,
1181
- handleActiveInAppInteraction,
1182
- handleActiveInAppRenderError,
1183
- handleActiveInAppShow,
1184
- inlinePlacement.fallback,
1185
- inlinePlacement.placementKey,
1186
- inlinePlacementRegistry,
1187
- inlineFallbackMessageId,
1188
- inlinePlacementRevision,
1189
- onInAppGameRequest,
1190
- routesToInlineSlot,
1191
- trackInApp,
1192
- ]);
1193
-
1194
- const rootInAppVisible = activeInApp !== null &&
1195
- active === null &&
1196
- (!routesToInlineSlot || inlineFallbackMessageId === activeInApp.id);
1197
-
1198
- return (
1199
- <EngageAnchorProvider>
1200
- <EngageInlinePlacementProvider registry={inlinePlacementRegistry}>
1201
- <EngagePromptContext.Provider value={controller}>
1202
- {children}
1203
- <EngagePromptView
1204
- campaign={active}
1205
- visible={active !== null}
1206
- rating={ratingRouting}
1207
- submitStatus={submitStatus}
1208
- onRetrySubmit={() => void runSubmit()}
1209
- onCloseSubmit={closeAfterSubmit}
1210
- onSubmit={(result) => {
1211
- if (active) handleSubmit(active, result);
1212
- }}
1213
- onStart={() => {
1214
- if (active && surveyStartedRef.current !== active.id) {
1215
- surveyStartedRef.current = active.id;
1216
- trackSurveyStage(active, 'started');
1217
- }
1218
- }}
1219
- onDismiss={dismissEngagePrompt}
1220
- />
1221
- {/* In-app message overlay. PRECEDENCE: only visible when no campaign
1222
- prompt is on screen, so the two families never fight for the view. */}
1223
- <EngageInAppView
1224
- message={activeInApp}
1225
- visible={rootInAppVisible}
1226
- mountModelOverride={routesToInlineSlot && inlineFallbackMessageId === activeInApp?.id
1227
- ? 'overlay'
1228
- : undefined}
1229
- // Render the resolved A/B arm's content (D8/J6) when an experiment is
1230
- // active; undefined ⇒ the view falls back to the message's base spec.
1231
- spec={activeInAppVariantRef.current?.spec}
1232
- onShow={handleActiveInAppShow}
1233
- onCta={handleActiveInAppCta}
1234
- onDismiss={dismissInAppMessage}
1235
- onComplete={completeInAppMessage}
1236
- onInteraction={handleActiveInAppInteraction}
1237
- onInAppGameRequest={onInAppGameRequest}
1238
- onRenderError={handleActiveInAppRenderError}
1239
- />
1240
- </EngagePromptContext.Provider>
1241
- </EngageInlinePlacementProvider>
1242
- </EngageAnchorProvider>
1243
- );
1244
- }
1245
-
1246
- /**
1247
- * Hook a host app uses to trigger/dismiss the prompt:
1248
- * const { showEngagePrompt } = useEngagePrompt();
1249
- * showEngagePrompt({ types: ['NPS'] });
1250
- */
1251
- export function useEngagePrompt(): EngagePromptController {
1252
- const ctx = useContext(EngagePromptContext);
1253
- if (!ctx) {
1254
- // Fail-soft: a host that calls the hook outside the provider gets no-ops
1255
- // instead of a crash (matches the SDK's no-throw public-API style).
1256
- return {
1257
- showEngagePrompt: async () => {
1258
- logger.warn('[Engage] useEngagePrompt() called outside <EngagePromptProvider>');
1259
- return false;
1260
- },
1261
- dismissEngagePrompt: () => {},
1262
- showInAppMessage: async () => {
1263
- logger.warn('[Engage] useEngagePrompt() called outside <EngagePromptProvider>');
1264
- return false;
1265
- },
1266
- dismissInAppMessage: () => {},
1267
- };
1268
- }
1269
- return ctx;
1270
- }
1
+ /**
2
+ * ScaleBun SDK — Engage Prompt Provider (campaign renderer core, D4)
3
+ *
4
+ * Wraps the host app and owns the campaign-render lifecycle for the shared
5
+ * cross-platform JS overlay:
6
+ * 1. on a trigger (app-controlled via `showEngagePrompt()` and/or the campaign's
7
+ * own `trigger` from config), calls `facade.engage.fetchConfig()`,
8
+ * 2. runs the throttle/decision core (`selectCampaignToRender`) to pick which
9
+ * campaign — if any — is eligible, honoring frequency caps,
10
+ * 3. mounts the parameterized `EngagePromptView` (Open/Closed: NPS now,
11
+ * ratings/feedback/survey later via the renderer registry),
12
+ * 4. on submit → `facade.engage.submitResponse({ surveyId, score, text })`
13
+ * (transport stamps `responseId` UUID + `schemaVersion` per contract),
14
+ * records the impression as completed, and emits an in-app dismiss/submit
15
+ * tracking event,
16
+ * 5. on dismiss → `facade.engage.trackEvent` with a `dismiss` event.
17
+ *
18
+ * The provider depends only on the `engage` namespace shape — NOT the whole
19
+ * facade — so it stays decoupled and testable, and reuses the existing transport
20
+ * (no second HTTP path).
21
+ *
22
+ * IN-APP MESSAGES (the second Engage family, §2.4/§2.5) are surfaced by this SAME
23
+ * provider — NOT a parallel one. `showInAppMessage()` fetches the config, picks an
24
+ * eligible message from `inAppMessages[]` (shared throttle store), and mounts the
25
+ * config-driven `EngageInAppView` (banner/modal/overlay). Events: `impression` on
26
+ * show, `click` on CTA tap (the resolved deep link is surfaced to the host via
27
+ * `onInAppDeepLink` — the SDK never opens URLs), `dismiss` on close.
28
+ *
29
+ * PRECEDENCE (campaign vs in-app): a campaign prompt wins the screen. If a
30
+ * campaign prompt is already visible, `showInAppMessage()` is a no-op (returns
31
+ * false) and the mounted in-app view is hidden while a campaign is up, so the two
32
+ * families never fight for the screen.
33
+ */
34
+
35
+ import React, {
36
+ createContext,
37
+ useContext,
38
+ useCallback,
39
+ useEffect,
40
+ useMemo,
41
+ useRef,
42
+ useState,
43
+ type ReactNode,
44
+ type ReactElement,
45
+ } from 'react';
46
+ import { Linking, Platform } from 'react-native';
47
+ import { logger } from '../../core/logger/internalLogger';
48
+ import { installEngageTriggerEngine } from './engageTriggerEngine';
49
+ import { appLifecycle } from '../../core/lifecycle/appLifecycle';
50
+ import { getDeviceId } from '../../core/id/deviceId';
51
+ import { ReplaySdk } from '../replay/public/api';
52
+ import { SessionManager } from '../session/SessionManager';
53
+ import {
54
+ EngagePromptView,
55
+ type EngagePromptResult,
56
+ type EngageSubmitStatus,
57
+ type RatingPromptConfig,
58
+ } from './EngagePromptView';
59
+ import { EngageInAppView, type InAppMechanicInteraction } from './EngageInAppView';
60
+ import { EngageAnchorProvider } from './EngageAnchor';
61
+ import {
62
+ EngageInlinePlacementProvider,
63
+ createEngageInlinePlacementRegistry,
64
+ resolveEngageInlinePlacement,
65
+ type EngageInlineDelivery,
66
+ } from './EngageInlinePlacement';
67
+ import { resolveInAppPresentation } from './engageVariantResolver';
68
+ import { shouldTrackInAppCta } from './engageOutcome';
69
+ import type { SubmitStage } from './EngageTransport';
70
+ import {
71
+ EngageThrottleStore,
72
+ selectCampaignToRender,
73
+ selectInAppToRender,
74
+ selectInAppVariant,
75
+ selectLatestUnseenTest,
76
+ testRunIdOf,
77
+ } from './engageThrottle';
78
+ import { EngageTestSeenStore } from './engageTestSeenStore';
79
+ import type {
80
+ EngageAttachmentDraft,
81
+ EngageCampaignConfig,
82
+ EngageCampaignType,
83
+ EngageConfigContext,
84
+ EngageConfigResponse,
85
+ EngageEventInput,
86
+ InAppGameRequestHandler,
87
+ EngageResponseInput,
88
+ InAppMessageConfig,
89
+ InAppRenderSpec,
90
+ InAppVariantId,
91
+ RatingFunnelEventDims,
92
+ RatingFunnelEventInput,
93
+ RatingFunnelEventType,
94
+ SurveyAnswer,
95
+ SurveyImpressionInput,
96
+ } from './engageTypes';
97
+
98
+ /** Client-generated UUID for app-rating idempotency (D8: `clientRatingId`). */
99
+ function ratingUuid(): string {
100
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
101
+ const r = (Math.random() * 16) | 0;
102
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
103
+ return v.toString(16);
104
+ });
105
+ }
106
+
107
+ /** Preserve a multi-question survey answer in the keyed wire shape (§1.4). */
108
+ export function toWireAnswers(
109
+ answers: SurveyAnswer[],
110
+ ): { key: string; value: string | number | string[] }[] {
111
+ return answers.map((a) => ({
112
+ key: a.key,
113
+ value: a.value,
114
+ }));
115
+ }
116
+
117
+ /**
118
+ * Resolve the canonical session id for response linkage (contract §1/§7).
119
+ * Primary source is `SessionManager.currentSessionId` — the always-on analytics
120
+ * session id (`rs-…`) that the replay recording attaches to (and that `/sessions/:id`
121
+ * keys on). `ReplaySdk.sessionId` (the orchestrator's recording id) is only set
122
+ * while a recording is mid-flight and reads `null` at submit time, so it is a
123
+ * fallback, not the primary. Returns `undefined` (not `null`) so the optional wire
124
+ * field is simply omitted when no session is active.
125
+ */
126
+ function currentSessionId(): string | undefined {
127
+ return (
128
+ SessionManager.getExistingInstance()?.currentSessionId ??
129
+ ReplaySdk.sessionId ??
130
+ undefined
131
+ );
132
+ }
133
+
134
+ /**
135
+ * The host app version for by-version analytics (contract §1). Read from the
136
+ * SAME synchronous source the SDK's rating lane already uses
137
+ * (`SessionManager.currentSessionMetadata.appVersion`, populated from native
138
+ * device info) — NOT hardcoded, and NOT the SDK build version. `undefined` when
139
+ * no session metadata is available yet, so the optional field is omitted.
140
+ */
141
+ function currentAppVersion(): string | undefined {
142
+ return SessionManager.getExistingInstance()?.currentSessionMetadata?.appVersion ?? undefined;
143
+ }
144
+
145
+ /**
146
+ * Device platform as the `'android' | 'ios'` wire union (contract §1). Shared JS
147
+ * path — NOT platform-gated — so Android and iOS both stamp their own value.
148
+ * `Platform.OS` also yields `'windows' | 'macos' | 'web'`; those are out of the
149
+ * SDK's mobile scope and intentionally collapse to `undefined` (field omitted).
150
+ */
151
+ function currentPlatform(): 'android' | 'ios' | undefined {
152
+ return Platform.OS === 'android' || Platform.OS === 'ios' ? Platform.OS : undefined;
153
+ }
154
+
155
+ /**
156
+ * Build the config-serve context (contract §1.3 query). ALWAYS carries `deviceId`
157
+ * — the backend keys per-device test previews on it, so omitting it would hide a
158
+ * "Send Test" preview from the very device it was queued for (T1). `platform`/
159
+ * `appVersion` ride along for server-side targeting; both honest, both omitted when
160
+ * unresolved. A caller-supplied `context` (manual show) overrides field-by-field.
161
+ */
162
+ function buildConfigContext(
163
+ engage: EngageRendererFacade,
164
+ override?: EngageConfigContext,
165
+ ): EngageConfigContext {
166
+ const identifiedUserId = engage.getIdentifiedUserId?.();
167
+ return {
168
+ deviceId: getDeviceId(),
169
+ ...(currentPlatform() ? { platform: currentPlatform() } : {}),
170
+ ...(currentAppVersion() ? { appVersion: currentAppVersion() } : {}),
171
+ ...(identifiedUserId ? { endUserId: identifiedUserId } : {}),
172
+ ...override,
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Build the rating-funnel breakdown dimensions (ratings-rebuild CONTRACT §1.2 `dims`)
178
+ * from the SAME live session metadata the response builder reads — NOT hardcoded.
179
+ * Only honest, present values are included; `country`/`locale`/`acquisitionSource`
180
+ * are intentionally omitted here (no on-device source yet) so the backend reports
181
+ * them as absent rather than fabricated.
182
+ */
183
+ function currentRatingDims(): RatingFunnelEventDims {
184
+ const meta = SessionManager.getExistingInstance()?.currentSessionMetadata;
185
+ const platform = currentPlatform();
186
+ return {
187
+ ...(platform ? { platform } : {}),
188
+ ...(meta?.appVersion ? { appVersion: meta.appVersion } : {}),
189
+ ...(meta?.osVersion ? { osVersion: meta.osVersion } : {}),
190
+ ...(meta?.deviceModel ? { deviceModel: meta.deviceModel } : {}),
191
+ };
192
+ }
193
+
194
+ /**
195
+ * The slice of the SDK facade the renderer consumes (the `engage` namespace).
196
+ * Matching this shape — rather than importing the facade — keeps the renderer a
197
+ * pure consumer of the foundation transport and trivially testable.
198
+ */
199
+ export interface EngageRendererFacade {
200
+ /** External user id last supplied through ScaleBun.identify(). */
201
+ getIdentifiedUserId?: () => string | undefined;
202
+ fetchConfig: (context?: EngageConfigContext) => Promise<EngageConfigResponse | null>;
203
+ trackCoachmarkEvent?: (name: string, properties: Record<string, unknown>) => void;
204
+ submitResponse: (input: EngageResponseInput) => void;
205
+ /**
206
+ * [P3-e] Submit a response with native attachments (screenshot/voice). Optional
207
+ * so a host wired to an older facade still renders prompts (media silently
208
+ * degrades to a plain response).
209
+ */
210
+ submitResponseWithAttachments?: (
211
+ input: EngageResponseInput,
212
+ attachments: EngageAttachmentDraft[],
213
+ ) => void;
214
+ /**
215
+ * [Slice D5] CONFIRMED submit used by the reusable submitting→success→error state
216
+ * machine: resolves only on real server persistence, rejects on failure, idempotent
217
+ * on `responseId`. Optional so an older facade still renders prompts (falls back to
218
+ * the fire-and-forget lanes, which resolve immediately).
219
+ */
220
+ submitResponseConfirmed?: (
221
+ input: EngageResponseInput,
222
+ attachments: EngageAttachmentDraft[],
223
+ opts: { responseId: string; onStage?: (stage: SubmitStage) => void },
224
+ ) => Promise<void>;
225
+ trackEvent: (input: EngageEventInput) => void;
226
+ trackSurveyImpression?: (input: SurveyImpressionInput) => void;
227
+ /**
228
+ * Force-flush queued in-app events immediately (bypassing the batch threshold/timer).
229
+ * Called right after a TEST preview's impression/dismiss ACK so the backend consumes
230
+ * the one-shot preview row before the app can be backgrounded/killed — otherwise a
231
+ * still-`delivered` row re-serves on the next reopen. Optional: an older facade simply
232
+ * relies on the periodic flush (eventual consistency).
233
+ */
234
+ flushEvents?: () => void;
235
+ /**
236
+ * Submit a 1–5 app-store rating via the EXISTING rating lane (NOT
237
+ * submitResponse — D1 keeps app-ratings a distinct funnel). The provider
238
+ * passes a client `clientRatingId` for idempotency (D8). Optional so a host
239
+ * that never shows RATING_PROMPT need not supply it.
240
+ */
241
+ submitRating?: (input: {
242
+ rating: number;
243
+ comment?: string;
244
+ source?: 'in_app' | 'prompt' | 'manual';
245
+ clientRatingId?: string;
246
+ }) => unknown;
247
+ /**
248
+ * Emit a rating-funnel tracking fact (ratings-rebuild CONTRACT §1.2) — eligible/
249
+ * shown/dismissed/selected/redirect. NOT a campaign response (D1). Optional so an
250
+ * older facade still renders rating prompts (the funnel just goes uninstrumented).
251
+ */
252
+ emitRatingEvent?: (input: RatingFunnelEventInput) => void;
253
+ /**
254
+ * Trigger the native in-app review sheet (D5). Resolves `true` only when the OS
255
+ * flow ran/resolved after a positive route, so the provider emits
256
+ * `store_redirect_clicked` solely on a `true` result. Optional/fail-soft.
257
+ */
258
+ requestStoreReview?: () => Promise<boolean>;
259
+ }
260
+
261
+ /**
262
+ * Host-app RATING_PROMPT routing config. The store deeplink is NEVER hardcoded —
263
+ * the host supplies either a static `storeUrl` or an `onHighRating` callback (e.g.
264
+ * `Linking.openURL(...)` / native `requestReview()`), and an `onLowRating` for the
265
+ * feedback hand-off. The provider only decides WHICH callback to invoke.
266
+ */
267
+ export interface EngageRatingRouting extends RatingPromptConfig {
268
+ /** App-store deeplink opened on a high rating (host owns the navigation). */
269
+ storeUrl?: string;
270
+ /** Called on a high rating (>= threshold). Receives the resolved store URL if set. */
271
+ onHighRating?: (info: { rating: number; campaign: EngageCampaignConfig; storeUrl?: string }) => void;
272
+ /** Called on a low rating when routing to feedback (host opens its form). */
273
+ onLowRating?: (info: { rating: number; campaign: EngageCampaignConfig }) => void;
274
+ }
275
+
276
+ export interface ShowEngagePromptOptions {
277
+ /** Restrict to specific campaign types. Defaults to `['NPS']` (NPS-first slice). */
278
+ types?: EngageCampaignType[];
279
+ /** Match a campaign's `targetScreen` to the current screen. */
280
+ screen?: string;
281
+ /**
282
+ * Restrict to campaigns whose `trigger` equals this value (behavioral-trigger
283
+ * path, e.g. `'screen_exit'`). Omitted ⇒ trigger-agnostic (manual fire).
284
+ */
285
+ trigger?: string;
286
+ /** Event name for `custom_event` / `after_purchase` (matches `triggerConfig.event`). */
287
+ event?: string;
288
+ /** Action label for `failed_action` (matches `triggerConfig.action`). */
289
+ action?: string;
290
+ /** Fired `time_in_app` threshold, used to select only that configured timer. */
291
+ seconds?: number;
292
+ /** App/user context forwarded to the config fetch for server-side targeting. */
293
+ context?: EngageConfigContext;
294
+ }
295
+
296
+ export interface ShowInAppMessageOptions {
297
+ /** Match a message's `targetScreen` to the current screen. */
298
+ screen?: string;
299
+ /**
300
+ * Restrict to messages whose `trigger` equals this value (behavioral-trigger
301
+ * path, e.g. `'session_start'`). Omitted ⇒ trigger-agnostic (manual host call).
302
+ */
303
+ trigger?: string;
304
+ /** Fired business/custom event name, matched against `message.eventName`. */
305
+ event?: string;
306
+ /** Fired action value for forward-compatible action-specific messages. */
307
+ action?: string;
308
+ /** Fired `time_delay` threshold, used to select only that configured message. */
309
+ seconds?: number;
310
+ /** App/user context forwarded to the config fetch for server-side targeting. */
311
+ context?: EngageConfigContext;
312
+ }
313
+
314
+ /**
315
+ * The host's deep-link sink for an in-app CTA tap. The SDK NEVER opens a URL — it
316
+ * hands the resolved `deepLink` (and full message/cta) to the host, which decides
317
+ * how to navigate (e.g. `Linking.openURL` / a router push). Only fired for a CTA
318
+ * whose `action` resolves to a link with a `deepLink`.
319
+ */
320
+ export type InAppDeepLinkHandler = (info: {
321
+ message: InAppMessageConfig;
322
+ cta: NonNullable<InAppRenderSpec['cta']>;
323
+ deepLink: string;
324
+ }) => void;
325
+
326
+ /** Imperative controller a host app uses to trigger a prompt / in-app message. */
327
+ export interface EngagePromptController {
328
+ /** Fetch config, pick an eligible campaign, and show it. Returns whether one was shown. */
329
+ showEngagePrompt: (options?: ShowEngagePromptOptions) => Promise<boolean>;
330
+ /** Dismiss the currently visible prompt (no response submitted). */
331
+ dismissEngagePrompt: () => void;
332
+ /**
333
+ * Fetch config, pick an eligible in-app message (`inAppMessages[]`), and show
334
+ * it. Returns whether one was shown. PRECEDENCE: a campaign prompt already on
335
+ * screen wins — an in-app message is NOT shown over it (see provider doc).
336
+ */
337
+ showInAppMessage: (options?: ShowInAppMessageOptions) => Promise<boolean>;
338
+ /** Dismiss the currently visible in-app message (emits a `dismiss` event). */
339
+ dismissInAppMessage: () => void;
340
+ }
341
+
342
+ const EngagePromptContext = createContext<EngagePromptController | null>(null);
343
+
344
+ export interface EngagePromptProviderProps {
345
+ /** The SDK facade's `engage` namespace (e.g. `ScaleBun.engage`). */
346
+ engage: EngageRendererFacade;
347
+ /** Default campaign types to consider. Defaults to NPS-first. */
348
+ defaultTypes?: EngageCampaignType[];
349
+ /**
350
+ * RATING_PROMPT routing config (host-supplied). The app-store deeplink and
351
+ * feedback hand-off are NEVER hardcoded in the SDK — supplied here.
352
+ */
353
+ ratingRouting?: EngageRatingRouting;
354
+ /**
355
+ * Host sink for an in-app message CTA deep link. The SDK never opens URLs —
356
+ * it surfaces the resolved `deepLink` here for the host to navigate. Optional:
357
+ * a host that never uses link-action CTAs need not supply it.
358
+ */
359
+ onInAppDeepLink?: InAppDeepLinkHandler;
360
+ /**
361
+ * Required when a production gamified campaign is used. The SDK calls this
362
+ * only at play/load/check-in/quiz resolution time; the host's authenticated
363
+ * backend owns eligibility, granting, and the returned result.
364
+ */
365
+ onInAppGameRequest?: InAppGameRequestHandler;
366
+ /**
367
+ * DEV/TEST ONLY — poll interval (ms) for queued test previews while the app is
368
+ * already foregrounded. `checkForPreview` runs on mount + every AppState→active,
369
+ * which covers "reopen the app". But if you press "Send Test" while the app is
370
+ * ALREADY open and in the foreground, there is no background→foreground edge to
371
+ * trigger a refetch. Setting e.g. `previewPollMs={4000}` makes a warm, foreground
372
+ * app pick up a Send Test within a few seconds with no app switch. Leave UNSET in
373
+ * production (0/undefined ⇒ no polling — the foreground/cold-start fetch is enough).
374
+ */
375
+ previewPollMs?: number;
376
+ children: ReactNode;
377
+ }
378
+
379
+ const DEFAULT_TYPES: EngageCampaignType[] = ['NPS'];
380
+
381
+ /**
382
+ * Provider that mounts the shared overlay and exposes the imperative controller
383
+ * via `useEngagePrompt()`.
384
+ */
385
+ export function EngagePromptProvider({
386
+ engage,
387
+ defaultTypes,
388
+ ratingRouting,
389
+ onInAppDeepLink,
390
+ onInAppGameRequest,
391
+ previewPollMs,
392
+ children,
393
+ }: EngagePromptProviderProps): ReactElement {
394
+ const [active, setActive] = useState<EngageCampaignConfig | null>(null);
395
+ const [activeInApp, setActiveInApp] = useState<InAppMessageConfig | null>(null);
396
+ const [inlineFallbackMessageId, setInlineFallbackMessageId] = useState<string | null>(null);
397
+ const [inlinePlacementRevision, setInlinePlacementRevision] = useState(0);
398
+ const inlinePlacementRegistry = useMemo(createEngageInlinePlacementRegistry, []);
399
+ const shownInAppMessageIdRef = useRef<string | null>(null);
400
+ // Resolved A/B arm for the active in-app message (D8/J6). Held in a ref: it is
401
+ // render-stable metadata (the chosen `spec` drives the view, the `variant` id
402
+ // rides the lifecycle events) and must not perturb the precedence that keys off
403
+ // `activeInApp`. `null` when no message is active (or it has no experiment).
404
+ const activeInAppVariantRef = useRef<
405
+ { variant: InAppVariantId; spec: InAppRenderSpec } | null
406
+ >(null);
407
+ // Show-time screen of the currently-active campaign prompt (contract §1
408
+ // `screen`). Held in a ref alongside `active` — it is render-irrelevant
409
+ // metadata read only at submit, so it needs no re-render and must not perturb
410
+ // the campaign-vs-in-app precedence which keys off `active`/`activeInApp`.
411
+ const activeScreenRef = useRef<string | undefined>(undefined);
412
+ const surveyStartedRef = useRef<string | null>(null);
413
+ // [T1] Test previews bypass the throttle store and the backend re-serves them on
414
+ // EVERY config poll until TTL — great for "reopen the app and see it again", but it
415
+ // means a dismissed preview would re-appear on the next warm poll. This session set
416
+ // records previews the user already closed (dismiss/CTA) so we don't re-show the SAME
417
+ // one. It is in-memory only: a cold restart clears it, so reopening the app still
418
+ // shows the queued test (the intended QA behavior).
419
+ const consumedPreviewIds = useRef<Set<string>>(new Set());
420
+ // [T1] Persistent dedupe for ONE-SHOT test delivery — survives cold restarts (unlike
421
+ // consumedPreviewIds above). A testRunId is recorded here AFTER it actually renders, so
422
+ // reopening the app does NOT re-show a test the device already displayed. This is the
423
+ // fix for "close/open and the old test re-appears, and they stack up". Separate from the
424
+ // throttle store so production frequency caps never block a fresh test and vice-versa.
425
+ const seenTestsRef = useRef<EngageTestSeenStore | null>(null);
426
+ if (seenTestsRef.current === null) {
427
+ seenTestsRef.current = new EngageTestSeenStore();
428
+ }
429
+ // One persistent throttle store for the provider lifetime — SHARED by campaigns
430
+ // and in-app messages so both honor the same per-id frequency caps.
431
+ const storeRef = useRef<EngageThrottleStore | null>(null);
432
+ if (storeRef.current === null) {
433
+ storeRef.current = new EngageThrottleStore();
434
+ }
435
+ const types = defaultTypes ?? DEFAULT_TYPES;
436
+
437
+ // ─── Reusable submit state machine (Slice D5) ────────────────────────────
438
+ // ONE machine for EVERY question type. `responseIdRef` is the idempotency key,
439
+ // generated once per prompt and REUSED on retry so a re-send can't duplicate.
440
+ // `submitCtxRef` holds the campaign+result so retry re-runs the same submit.
441
+ const [submitStatus, setSubmitStatus] = useState<EngageSubmitStatus>({ phase: 'idle' });
442
+ const responseIdRef = useRef<string | null>(null);
443
+ const submitCtxRef = useRef<{ campaign: EngageCampaignConfig; result: EngagePromptResult } | null>(null);
444
+ const successTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
445
+
446
+ const resetSubmit = useCallback((): void => {
447
+ if (successTimerRef.current) {
448
+ clearTimeout(successTimerRef.current);
449
+ successTimerRef.current = null;
450
+ }
451
+ responseIdRef.current = null;
452
+ submitCtxRef.current = null;
453
+ setSubmitStatus({ phase: 'idle' });
454
+ }, []);
455
+
456
+ // Single campaign tracking lane (mirrors trackInApp). `impression` on show,
457
+ // `completion` on submit, `dismiss` on close — the §2.5 analytics events that
458
+ // back the response-rate / completion KPIs (P3-e #2). Distinct from the local
459
+ // throttle store, which only counts impressions for frequency caps.
460
+ const trackCampaign = useCallback(
461
+ (campaign: EngageCampaignConfig, eventType: 'impression' | 'completion' | 'dismiss') => {
462
+ try {
463
+ engage.trackEvent({
464
+ campaignId: campaign.id,
465
+ channel: 'inapp',
466
+ type: eventType,
467
+ deviceId: getDeviceId(),
468
+ });
469
+ } catch (err) {
470
+ logger.debug('[Engage] trackEvent failed:', (err as Error)?.message);
471
+ }
472
+ },
473
+ [engage],
474
+ );
475
+
476
+ const trackSurveyStage = useCallback(
477
+ (campaign: EngageCampaignConfig, stage: 'shown' | 'started'): void => {
478
+ if (campaign.type === 'RATING_PROMPT' || campaign.type === 'RATINGS_PROMPT') return;
479
+ try {
480
+ engage.trackSurveyImpression?.({
481
+ surveyId: campaign.id,
482
+ stage,
483
+ sessionId: currentSessionId(),
484
+ deviceId: getDeviceId(),
485
+ platform: currentPlatform(),
486
+ appVersion: currentAppVersion(),
487
+ screen: activeScreenRef.current,
488
+ });
489
+ } catch (err) {
490
+ logger.debug('[Engage] survey impression failed:', (err as Error)?.message);
491
+ }
492
+ },
493
+ [engage],
494
+ );
495
+
496
+ const isRatingType = (type: EngageCampaignType): boolean =>
497
+ type === 'RATING_PROMPT' || type === 'RATINGS_PROMPT';
498
+
499
+ // Rating-funnel emit lane (ratings-rebuild CONTRACT §1.2). Distinct from
500
+ // trackCampaign (the §2.5 in-app analytics lane) and from submitRating (the D1
501
+ // app-rating lane): these are dimensioned FACTS feeding the Overview funnel and
502
+ // are NEVER counted as responses. `stars` rides only on `rating_selected`.
503
+ const emitRatingFunnel = useCallback(
504
+ (
505
+ campaign: EngageCampaignConfig,
506
+ type: RatingFunnelEventType,
507
+ extra?: { stars?: number },
508
+ ): void => {
509
+ try {
510
+ engage.emitRatingEvent?.({
511
+ type,
512
+ sessionId: currentSessionId(),
513
+ userId: engage.getIdentifiedUserId?.(),
514
+ promptId: campaign.id,
515
+ triggerId: campaign.trigger,
516
+ ...(extra?.stars !== undefined ? { stars: extra.stars } : {}),
517
+ dims: currentRatingDims(),
518
+ });
519
+ } catch (err) {
520
+ logger.debug('[Engage] emitRatingEvent failed:', (err as Error)?.message);
521
+ }
522
+ },
523
+ [engage],
524
+ );
525
+
526
+ const dismissEngagePrompt = useCallback((): void => {
527
+ setActive((current) => {
528
+ if (current) {
529
+ trackCampaign(current, 'dismiss');
530
+ // Rating funnel: a close without a selection is a funnel drop.
531
+ if (isRatingType(current.type)) emitRatingFunnel(current, 'rating_prompt_dismissed');
532
+ }
533
+ return null;
534
+ });
535
+ activeScreenRef.current = undefined;
536
+ surveyStartedRef.current = null;
537
+ resetSubmit();
538
+ }, [trackCampaign, resetSubmit, emitRatingFunnel]);
539
+
540
+ // The actual submit run — used by both the first submit and a retry. Reads
541
+ // `submitCtxRef` + `responseIdRef` so a retry re-sends with the SAME idempotency
542
+ // key. Resolves to `success` ONLY on confirmed persistence; `error` on failure.
543
+ const runSubmit = useCallback(async (): Promise<void> => {
544
+ const ctx = submitCtxRef.current;
545
+ if (!ctx) return;
546
+ const { campaign, result } = ctx;
547
+ const store = storeRef.current;
548
+ const responseId = responseIdRef.current ?? (responseIdRef.current = ratingUuid());
549
+
550
+ setSubmitStatus({ phase: 'submitting', stage: 'saving' });
551
+ try {
552
+ if (isRatingType(campaign.type)) {
553
+ // RATING_PROMPT submits via the EXISTING app-rating lane (D1), NOT
554
+ // submitResponse, with a client clientRatingId for idempotency. Awaited
555
+ // so the loading state reflects the real round-trip.
556
+ const rating = typeof result.score === 'number' ? result.score : 0;
557
+ await Promise.resolve(
558
+ engage.submitRating?.({ rating, source: 'prompt', clientRatingId: responseId }),
559
+ );
560
+ // Funnel: user picked 1–5★ (carries stars). Emitted regardless of route.
561
+ emitRatingFunnel(campaign, 'rating_selected', { stars: rating });
562
+ // Routing decided by the view (`route`), executed against host-supplied
563
+ // callbacks/URL — the deeplink is never hardcoded.
564
+ if (result.route === 'store') {
565
+ // D5: prefer the genuinely-native in-app review sheet. `store_redirect_clicked`
566
+ // fires ONLY when an actual store route resolves (native review ran, or a
567
+ // host-supplied deeplink/callback executed) — never speculatively.
568
+ let redirected = false;
569
+ if (engage.requestStoreReview) {
570
+ redirected = await engage.requestStoreReview();
571
+ }
572
+ const storeUrl = campaign.storeUrl ?? ratingRouting?.storeUrl;
573
+ if (ratingRouting?.onHighRating) {
574
+ ratingRouting.onHighRating({ rating, campaign, storeUrl });
575
+ redirected = true;
576
+ } else if (!redirected && storeUrl) {
577
+ redirected = await Linking.openURL(storeUrl).then(() => true).catch(() => false);
578
+ }
579
+ if (redirected) emitRatingFunnel(campaign, 'store_redirect_clicked');
580
+ } else if (result.route === 'feedback') {
581
+ ratingRouting?.onLowRating?.({ rating, campaign });
582
+ }
583
+ } else {
584
+ // [v2] response context (contract §1): session linkage + originating
585
+ // screen + platform + app version. All optional; omitted when unresolved.
586
+ // We send ONLY the new response fact + the sessionId; replay/errors/network
587
+ // are NOT re-sent — the read side joins them by sessionId.
588
+ const input: EngageResponseInput = Array.isArray(result.answers)
589
+ ? {
590
+ surveyId: campaign.id,
591
+ text: result.reason,
592
+ answers: toWireAnswers(result.answers),
593
+ deviceId: getDeviceId(),
594
+ sessionId: currentSessionId(),
595
+ screen: activeScreenRef.current,
596
+ platform: currentPlatform(),
597
+ appVersion: currentAppVersion(),
598
+ }
599
+ : {
600
+ surveyId: campaign.id,
601
+ score: result.score,
602
+ text: result.reason,
603
+ tags: result.tags,
604
+ deviceId: getDeviceId(),
605
+ sessionId: currentSessionId(),
606
+ screen: activeScreenRef.current,
607
+ platform: currentPlatform(),
608
+ appVersion: currentAppVersion(),
609
+ };
610
+
611
+ const drafts = result.attachments ?? [];
612
+
613
+ /**
614
+ * Bug-report lane opt in per campaign via `fileAsBugReport`.
615
+ *
616
+ * A FEEDBACK campaign fired by `shake_device` is almost always "something is
617
+ * broken", not "how do you feel", and those are different products: a bug report
618
+ * gets a status, a priority and an assignee on /diagnose/bug-reports, while a survey
619
+ * response is counted in aggregate and cannot be triaged.
620
+ *
621
+ * Routed INSTEAD of submitResponse rather than in addition — a dual write would
622
+ * create two rows for one submission and double-count the campaign on the Engage
623
+ * side.
624
+ *
625
+ * `responseId` doubles as the idempotency key so a retried submit cannot file the
626
+ * same report twice (BugReport.clientReportId is @unique).
627
+ *
628
+ * Falls through to the normal lane when the host facade predates `reportBugDetailed`
629
+ * an older app bundling a newer config degrades to existing behaviour instead of
630
+ * dropping the user's report on the floor.
631
+ */
632
+ const bugLane =
633
+ campaign.fileAsBugReport && typeof (engage as any).reportBugDetailed === 'function'
634
+ ? ((engage as any).reportBugDetailed as (i: {
635
+ message: string;
636
+ title?: string;
637
+ clientReportId?: string;
638
+ }) => Promise<unknown>)
639
+ : null;
640
+
641
+ if (bugLane) {
642
+ const outcome = await bugLane({
643
+ message: result.reason ?? '',
644
+ title: campaign.name,
645
+ clientReportId: responseId,
646
+ });
647
+ // Surface a refusal as a throw so the existing retry/error UI handles it; a
648
+ // silent success here would tell the user their report was filed when it wasn't.
649
+ if (outcome && typeof outcome === 'object' && 'e' in (outcome as object)) {
650
+ throw new Error(`bug report failed: ${(outcome as { e: string }).e}`);
651
+ }
652
+ } else if (engage.submitResponseConfirmed) {
653
+ // Confirmed lane: awaits real persistence (presign+PUT bytes → response
654
+ // POST), reports stages, idempotent on responseId, throws on failure.
655
+ await engage.submitResponseConfirmed(input, drafts, {
656
+ responseId,
657
+ onStage: (stage) => setSubmitStatus({ phase: 'submitting', stage }),
658
+ });
659
+ } else if (drafts.length > 0 && engage.submitResponseWithAttachments) {
660
+ // Fallback (older facade): fire-and-forget — resolves immediately.
661
+ engage.submitResponseWithAttachments(input, drafts);
662
+ } else {
663
+ engage.submitResponse(input);
664
+ }
665
+ }
666
+
667
+ store?.markCompleted(campaign.id);
668
+ trackCampaign(campaign, 'completion');
669
+ setSubmitStatus({ phase: 'success' });
670
+ // Brief success beat, then close + reset.
671
+ successTimerRef.current = setTimeout(() => {
672
+ setActive(null);
673
+ activeScreenRef.current = undefined;
674
+ resetSubmit();
675
+ }, 1100);
676
+ } catch (err) {
677
+ logger.warn('[Engage] submit failed:', (err as Error)?.message);
678
+ setSubmitStatus({ phase: 'error', message: (err as Error)?.message });
679
+ }
680
+ }, [engage, trackCampaign, ratingRouting, resetSubmit, emitRatingFunnel]);
681
+
682
+ const handleSubmit = useCallback(
683
+ (campaign: EngageCampaignConfig, result: EngagePromptResult): void => {
684
+ // Guard double-submit: ignore re-fires once a submit is in flight or done.
685
+ if (submitCtxRef.current && responseIdRef.current) return;
686
+ submitCtxRef.current = { campaign, result };
687
+ void runSubmit();
688
+ },
689
+ [runSubmit],
690
+ );
691
+
692
+ const closeAfterSubmit = useCallback((): void => {
693
+ setActive(null);
694
+ activeScreenRef.current = undefined;
695
+ resetSubmit();
696
+ }, [resetSubmit]);
697
+
698
+ const showEngagePrompt = useCallback(
699
+ async (options?: ShowEngagePromptOptions): Promise<boolean> => {
700
+ const store = storeRef.current;
701
+ if (!store) return false;
702
+ try {
703
+ const config = await engage.fetchConfig(options?.context);
704
+ if (!config || !Array.isArray(config.campaigns)) return false;
705
+
706
+ const campaign = selectCampaignToRender(config.campaigns, store, {
707
+ // A behavioral-trigger fire (`options.trigger`) is type-agnostic:
708
+ // the trigger itself selects the campaign, so don't layer the
709
+ // NPS-first default on top (it would drop FEEDBACK/CSAT/etc.).
710
+ // Manual fires keep the provider default unless overridden.
711
+ types: options?.types ?? (options?.trigger ? undefined : types),
712
+ screen: options?.screen,
713
+ trigger: options?.trigger,
714
+ seconds: options?.seconds,
715
+ event: options?.event,
716
+ action: options?.action,
717
+ });
718
+ if (!campaign) {
719
+ // Name the inputs so a silent mismatch (wrong trigger name, throttle
720
+ // suppression, type filter) is diagnosable from device logs alone.
721
+ logger.debug(
722
+ `[Engage] no eligible campaign to render (trigger=${options?.trigger ?? ''} ` +
723
+ `candidates=${config.campaigns.length} ` +
724
+ `[${config.campaigns.map((c) => `${c.type}:${c.trigger ?? ''}`).join(', ')}])`,
725
+ );
726
+ return false;
727
+ }
728
+
729
+ // Record the impression up front so throttle counts it even if the
730
+ // user backgrounds the app before submitting.
731
+ store.recordImpression(campaign.id);
732
+ // Emit the §2.5 impression event (P3-e #2) so the dashboard's
733
+ // response-rate / completion KPIs have a real denominator the
734
+ // prompt path previously only counted impressions locally.
735
+ trackCampaign(campaign, 'impression');
736
+ // Rating funnel (CONTRACT §1.1): selection passed the throttle/anti-
737
+ // annoyance gate (→ eligible) and we are about to render (→ shown).
738
+ if (isRatingType(campaign.type)) {
739
+ emitRatingFunnel(campaign, 'rating_prompt_eligible');
740
+ emitRatingFunnel(campaign, 'rating_prompt_shown');
741
+ }
742
+ // Stamp the show-time screen alongside the active campaign so
743
+ // handleSubmit can attribute the response to its originating screen.
744
+ activeScreenRef.current = options?.screen;
745
+ surveyStartedRef.current = null;
746
+ trackSurveyStage(campaign, 'shown');
747
+ resetSubmit();
748
+ setActive(campaign);
749
+ return true;
750
+ } catch (err) {
751
+ logger.warn('[Engage] showEngagePrompt failed:', (err as Error)?.message);
752
+ return false;
753
+ }
754
+ },
755
+ [engage, types, trackCampaign, trackSurveyStage, emitRatingFunnel],
756
+ );
757
+
758
+ // ─── In-app message lifecycle (the second Engage family, §2.4/§2.5) ──────
759
+ // Reuses the SAME transport (`trackEvent`), the SAME throttle store, and the
760
+ // SAME provider — NOT a parallel provider. Events: impression on show, click
761
+ // on CTA tap, completion on a finished tour, dismiss on close/skip.
762
+
763
+ const trackInApp = useCallback(
764
+ (
765
+ message: InAppMessageConfig,
766
+ eventType:
767
+ | 'impression'
768
+ | 'completion'
769
+ | 'click'
770
+ | 'dismiss'
771
+ | 'interaction'
772
+ | 'render_degraded'
773
+ | 'render_failed'
774
+ | 'cta_open_failed',
775
+ extraMeta?: Record<string, unknown>,
776
+ ): void => {
777
+ try {
778
+ // A/B attribution (D8/J6): when the active message resolved to an
779
+ // experiment arm, tag every lifecycle event with the chosen variant
780
+ // via the existing optional `meta` field — NO event-schema change
781
+ // (EngageEventUpload already carries `meta`; the backend EngageEvent
782
+ // accepts it). Plain (non-experiment) messages send no variant key.
783
+ const variant = activeInAppVariantRef.current?.variant;
784
+ // [T1] Test-preview tagging: a preview-flagged message stamps
785
+ // `previewId` + `isTest` + `source` so (a) the backend CONSUMES the
786
+ // matching preview row on the impression and (b) production KPIs
787
+ // exclude the traffic. Normal campaign messages omit all of it.
788
+ const isPreview = message.preview === true || message.isTest === true;
789
+ const meta = {
790
+ ...(variant ? { variant } : {}),
791
+ // testRunId correlates a specific "Send Test" run to its render/click/
792
+ // dismiss in the dashboard; rides the existing meta field (no schema churn).
793
+ ...(message.testRunId ? { testRunId: message.testRunId } : {}),
794
+ ...(extraMeta ?? {}),
795
+ };
796
+ engage.trackEvent({
797
+ campaignId: message.id,
798
+ channel: 'inapp',
799
+ type: eventType,
800
+ deviceId: getDeviceId(),
801
+ ...(message.previewId ? { previewId: message.previewId } : {}),
802
+ ...(isPreview ? { isTest: true, source: 'test_preview' } : {}),
803
+ ...(variant ? { variantId: variant } : {}),
804
+ ...(Object.keys(meta).length > 0 ? { meta } : {}),
805
+ });
806
+ } catch (err) {
807
+ logger.debug('[Engage] in-app trackEvent failed:', (err as Error)?.message);
808
+ }
809
+ },
810
+ [engage],
811
+ );
812
+
813
+ // [T1] In-app message actually mounted on screen. The impression event IS the test
814
+ // ACK to the backend (which consumes the preview row → 'rendered'). We ALSO persist the
815
+ // testRunId locally so a cold restart can't re-show it before the ACK is processed
816
+ // server-side — the one-shot guarantee survives a flaky/slow network.
817
+ const handleInAppShow = useCallback(
818
+ (message: InAppMessageConfig): void => {
819
+ // A slot can be replaced or degrade to an overlay while the same message
820
+ // remains active. Treat that as one mount lifecycle, not two impressions.
821
+ if (shownInAppMessageIdRef.current === message.id) return;
822
+ shownInAppMessageIdRef.current = message.id;
823
+ trackInApp(message, 'impression');
824
+ if (message.preview === true || message.isTest === true) {
825
+ const runId = message.testRunId ?? message.previewId ?? message.id;
826
+ seenTestsRef.current?.markSeen(runId);
827
+ // Eager-flush so the backend consumes the one-shot preview row NOW — a fast
828
+ // close→kill→reopen must not re-serve a still-`delivered` test.
829
+ engage.flushEvents?.();
830
+ logger.debug(`[Engage] test ${runId} render displayed — ACK sent (impression), flushed`);
831
+ }
832
+ },
833
+ [trackInApp, engage],
834
+ );
835
+
836
+ const finalizeInAppMessage = useCallback((eventType: 'dismiss' | 'completion'): void => {
837
+ setActiveInApp((current) => {
838
+ if (current) {
839
+ const isTest = current.preview === true || current.isTest === true;
840
+ const runId = current.testRunId ?? current.previewId ?? current.id;
841
+ logger.debug(`[Engage] in-app ${eventType}${isTest ? ` (test ${runId})` : ''}`);
842
+ trackInApp(current, eventType);
843
+ if (isTest) {
844
+ consumedPreviewIds.current.add(current.previewId ?? current.id);
845
+ seenTestsRef.current?.markSeen(runId);
846
+ engage.flushEvents?.();
847
+ logger.debug(`[Engage] test ${runId} ${eventType} persisted + ACK flushed`);
848
+ } else {
849
+ // Stamp the SERVED revision: without it the completion can never be invalidated,
850
+ // and an edited campaign would never return to a device that closed the old one.
851
+ storeRef.current?.markCompleted(current.id, Date.now(), current.revision);
852
+ engage.flushEvents?.();
853
+ logger.debug(`[Engage] in-app ${current.id} ${eventType} — marked done on device`);
854
+ }
855
+ }
856
+ return null;
857
+ });
858
+ // Clear the resolved A/B arm after the lifecycle event has read it.
859
+ shownInAppMessageIdRef.current = null;
860
+ activeInAppVariantRef.current = null;
861
+ }, [trackInApp, engage]);
862
+
863
+ const dismissInAppMessage = useCallback((): void => {
864
+ finalizeInAppMessage('dismiss');
865
+ }, [finalizeInAppMessage]);
866
+
867
+ const completeInAppMessage = useCallback((): void => {
868
+ finalizeInAppMessage('completion');
869
+ }, [finalizeInAppMessage]);
870
+
871
+ const handleInAppCta = useCallback(
872
+ (message: InAppMessageConfig, cta: NonNullable<InAppRenderSpec['cta']>): void => {
873
+ if (shouldTrackInAppCta(cta)) trackInApp(message, 'click');
874
+ // Deep-link opening is the host's job — surface it, never open it here.
875
+ if (cta.action === 'link') {
876
+ if (!cta.deepLink) {
877
+ // Misconfigured link CTA (no deepLink) — observable, not silent (T1 G17).
878
+ logger.warn('[Engage] link CTA has no deepLink');
879
+ trackInApp(message, 'cta_open_failed', { reason: 'missing_deeplink' });
880
+ } else if (!onInAppDeepLink) {
881
+ // Host wired no deep-link sink — the link can't open. Diagnose it.
882
+ logger.warn('[Engage] link CTA but no onInAppDeepLink handler supplied');
883
+ trackInApp(message, 'cta_open_failed', { reason: 'no_handler' });
884
+ } else {
885
+ try {
886
+ onInAppDeepLink({ message, cta, deepLink: cta.deepLink });
887
+ } catch (err) {
888
+ logger.warn('[Engage] onInAppDeepLink threw:', (err as Error)?.message);
889
+ trackInApp(message, 'cta_open_failed', {
890
+ reason: (err as Error)?.message ?? 'handler_threw',
891
+ });
892
+ }
893
+ }
894
+ }
895
+ // A test preview the user acted on is done — don't re-show it next poll AND
896
+ // persist it durably (a CTA tap is as final as a close), then flush the ACK.
897
+ if (message.preview === true || message.isTest === true) {
898
+ const runId = message.testRunId ?? message.previewId ?? message.id;
899
+ consumedPreviewIds.current.add(message.previewId ?? message.id);
900
+ seenTestsRef.current?.markSeen(runId);
901
+ engage.flushEvents?.();
902
+ logger.debug(`[Engage] test ${runId} CTA persisted locally + ACK flushed`);
903
+ } else {
904
+ // Production message: a CTA tap is as final as a close — don't re-show it.
905
+ storeRef.current?.markCompleted(message.id, Date.now(), message.revision);
906
+ engage.flushEvents?.();
907
+ logger.debug(`[Engage] in-app ${message.id} CTA tapped — marked done on device`);
908
+ }
909
+ setActiveInApp(null);
910
+ // Any enabled click event above already read the arm; clear it now.
911
+ shownInAppMessageIdRef.current = null;
912
+ activeInAppVariantRef.current = null;
913
+ },
914
+ [trackInApp, onInAppDeepLink, engage],
915
+ );
916
+
917
+ // [T1] Pick the ONE test preview to render from a fetched config: drop any already
918
+ // displayed (persistent seen store) or closed-this-session, then take the LATEST by
919
+ // createdAt. One-shot + latest-only is what stops the "tests stack up 1,2,3…" bug.
920
+ const pickLatestPreview = useCallback(
921
+ (messages: InAppMessageConfig[]): InAppMessageConfig | null => {
922
+ const latest = selectLatestUnseenTest(messages, {
923
+ seen: (runId) => seenTestsRef.current?.has(runId) ?? false,
924
+ consumed: (key) => consumedPreviewIds.current.has(key),
925
+ });
926
+ if (latest) logger.debug(`[Engage] selected latest test ${testRunIdOf(latest)}`);
927
+ return latest;
928
+ },
929
+ [],
930
+ );
931
+
932
+ // [T1] Render a test preview IMMEDIATELY — bypassing trigger/throttle/schedule/
933
+ // sample/variant. No throttle write (a preview must stay re-showable until it is
934
+ // actually rendered + its impression consumes it server-side). Idempotent: if this
935
+ // exact preview is already on screen, don't re-mount (avoids a duplicate impression).
936
+ const showPreview = useCallback(
937
+ (preview: InAppMessageConfig): boolean => {
938
+ // PRECEDENCE: a campaign prompt owns the screen — defer (the backend re-serves
939
+ // the preview on every poll until TTL, so the next foreground retries).
940
+ if (active !== null) {
941
+ logger.debug('[Engage] campaign prompt active — preview deferred');
942
+ return false;
943
+ }
944
+ if (activeInApp?.id === preview.id) return true;
945
+ const runId = preview.testRunId ?? preview.previewId ?? preview.id;
946
+ // Already closed this preview this session — don't re-show on the next poll.
947
+ if (consumedPreviewIds.current.has(preview.previewId ?? preview.id)) {
948
+ return false;
949
+ }
950
+ // [T1] One-shot: already DISPLAYED on this device (persisted across restarts) —
951
+ // never re-show, even if the backend briefly re-serves a not-yet-ACKed row.
952
+ if (seenTestsRef.current?.has(runId)) {
953
+ logger.debug(`[Engage] test ${runId} already displayed on device skip`);
954
+ return false;
955
+ }
956
+ activeInAppVariantRef.current = null;
957
+ setActiveInApp(preview);
958
+ logger.debug(`[Engage] test ${runId} render started`);
959
+ return true;
960
+ },
961
+ [active, activeInApp],
962
+ );
963
+
964
+ const showInAppMessage = useCallback(
965
+ async (options?: ShowInAppMessageOptions): Promise<boolean> => {
966
+ const store = storeRef.current;
967
+ if (!store) return false;
968
+ // PRECEDENCE: a campaign prompt on screen wins — never stack an in-app
969
+ // message over an open survey/NPS/rating prompt.
970
+ if (active !== null) {
971
+ logger.debug('[Engage] campaign prompt active — in-app message deferred');
972
+ return false;
973
+ }
974
+ try {
975
+ const config = await engage.fetchConfig(buildConfigContext(engage, options?.context));
976
+ if (!config || !Array.isArray(config.inAppMessages)) return false;
977
+
978
+ // [T1] A queued test preview wins immediately, bypassing throttle/variant.
979
+ // Pick the LATEST unseen one (one-shot dedupe)never stack old tests.
980
+ const preview = pickLatestPreview(config.inAppMessages);
981
+ if (preview) return showPreview(preview);
982
+
983
+ // Normal selection runs over NON-preview messages only — a preview must
984
+ // never enter the throttle store (it would falsely cap a real campaign id).
985
+ const normal = config.inAppMessages.filter(
986
+ (m) => !(m.preview === true || m.isTest === true),
987
+ );
988
+ const message = selectInAppToRender(normal, store, {
989
+ screen: options?.screen,
990
+ trigger: options?.trigger,
991
+ seconds: options?.seconds,
992
+ event: options?.event,
993
+ action: options?.action,
994
+ });
995
+ if (!message) {
996
+ logger.debug('[Engage] no eligible in-app message to render');
997
+ return false;
998
+ }
999
+
1000
+ // A/B variant selection (D8/J6): deterministically bucket this
1001
+ // device into arm A (base spec) or B (experiment.variantB) BEFORE
1002
+ // showing, so the impression event (and click/dismiss) attribute to
1003
+ // the arm the user actually saw. Only retained for attribution when
1004
+ // the message actually carries an enabled experiment; otherwise the
1005
+ // resolved spec is just the base spec and no variant key is emitted.
1006
+ const resolved = selectInAppVariant(message, getDeviceId());
1007
+ activeInAppVariantRef.current = message.experiment?.enabled
1008
+ ? resolved
1009
+ : null;
1010
+ // Record up front so the throttle counts it (impression also emitted
1011
+ // by the view's onShow → trackEvent for the analytics lane). Pass the
1012
+ // content revision so an edited campaign starts a fresh cap generation.
1013
+ store.recordImpression(message.id, Date.now(), message.revision);
1014
+ setActiveInApp(message);
1015
+ return true;
1016
+ } catch (err) {
1017
+ logger.warn('[Engage] showInAppMessage failed:', (err as Error)?.message);
1018
+ return false;
1019
+ }
1020
+ },
1021
+ [engage, active, showPreview, pickLatestPreview],
1022
+ );
1023
+
1024
+ // ─── Cold-start + foreground preview check (T1) ──────────────────────────
1025
+ // "Send Test reopen the app see it immediately, EVERY time": the SDK fetches
1026
+ // config (deviceId in context, HTTP cache-bypassed) on mount AND on every
1027
+ // AppState→active, and renders any queued preview at once — no trigger, no throttle,
1028
+ // no schedule. The backend keeps serving the preview on every poll until its TTL, so
1029
+ // a tester sees it on each reopen (rendering records lifecycle but does not consume).
1030
+ const checkForPreview = useCallback(async (): Promise<void> => {
1031
+ if (active !== null) return;
1032
+ try {
1033
+ const config = await engage.fetchConfig(buildConfigContext(engage));
1034
+ // One-shot + latest-only: render the newest test not already displayed.
1035
+ const preview = pickLatestPreview(config?.inAppMessages ?? []);
1036
+ if (preview) showPreview(preview);
1037
+ } catch (err) {
1038
+ logger.warn('[Engage] preview check failed:', (err as Error)?.message);
1039
+ }
1040
+ }, [engage, active, showPreview, pickLatestPreview]);
1041
+
1042
+ useEffect(() => {
1043
+ // Drop stale seen-test entries (past TTL) so the persistent set stays bounded.
1044
+ seenTestsRef.current?.prune();
1045
+ void checkForPreview();
1046
+ const onLifecycle = (state: string): void => {
1047
+ if (state === 'active') void checkForPreview();
1048
+ };
1049
+ appLifecycle.addListener(onLifecycle);
1050
+ return () => appLifecycle.removeListener(onLifecycle);
1051
+ }, [checkForPreview]);
1052
+
1053
+ // DEV/TEST warm-foreground poll (T1): covers "Send Test while the app is already
1054
+ // open" — no background→foreground edge fires, so without this you'd have to switch
1055
+ // apps to see it. Only runs when the host opts in via `previewPollMs` (unset in prod).
1056
+ useEffect(() => {
1057
+ if (!previewPollMs || previewPollMs <= 0) return;
1058
+ logger.debug(`[Engage] test-preview warm poll every ${previewPollMs}ms (dev)`);
1059
+ const interval = setInterval(() => {
1060
+ void checkForPreview();
1061
+ }, previewPollMs);
1062
+ return () => clearInterval(interval);
1063
+ }, [previewPollMs, checkForPreview]);
1064
+
1065
+ // ─── Always-on behavioral triggers (P3-e #3 / Slice E) ───────────────────
1066
+ // The trigger engine wires every behavioral trigger (session_start, app_open,
1067
+ // screen_view/exit, time_in_app, n_sessions, after_purchase, before_logout,
1068
+ // custom_event, failed_action, after_crash/js_error, network_error, slow_screen,
1069
+ // and the native gesture triggers) to the SDK signals the app already produces.
1070
+ // A fire first tries the survey/rating campaign path (showEngagePrompt); if no
1071
+ // campaign matches, the SAME trigger drives an in-app message (showInAppMessage).
1072
+ // Both families share the throttle store, so a behavioral fire never double-shows.
1073
+ // The engine buffers events fired before the first config arrives and replays
1074
+ // them once it does (late binding), honoring each trigger's remaining delay.
1075
+ useEffect(() => {
1076
+ return installEngageTriggerEngine({
1077
+ fire: (options) => {
1078
+ void (async () => {
1079
+ const shown = await showEngagePrompt(options);
1080
+ if (!shown && options.trigger) {
1081
+ await showInAppMessage({
1082
+ trigger: options.trigger,
1083
+ screen: options.screen,
1084
+ event: options.event,
1085
+ action: options.action,
1086
+ seconds: options.seconds,
1087
+ });
1088
+ }
1089
+ })();
1090
+ },
1091
+ fetchConfig: () => engage.fetchConfig(buildConfigContext(engage)),
1092
+ });
1093
+ }, [showEngagePrompt, showInAppMessage, engage]);
1094
+
1095
+ const controller = useMemo<EngagePromptController>(
1096
+ () => ({
1097
+ showEngagePrompt,
1098
+ dismissEngagePrompt,
1099
+ showInAppMessage,
1100
+ dismissInAppMessage,
1101
+ }),
1102
+ [showEngagePrompt, dismissEngagePrompt, showInAppMessage, dismissInAppMessage],
1103
+ );
1104
+
1105
+ const activeInAppSpec = activeInAppVariantRef.current?.spec ?? activeInApp?.spec;
1106
+ const activeInAppPresentation = activeInApp && activeInAppSpec
1107
+ ? resolveInAppPresentation({
1108
+ layout: activeInApp.layout,
1109
+ variantKey: activeInApp.variantKey,
1110
+ spec: activeInAppSpec,
1111
+ })
1112
+ : null;
1113
+ // Route by the canonical mount, not a variant allow-list. This covers every
1114
+ // current/future inline variant, including smart_inbox_message.
1115
+ const routesToInlineSlot = activeInAppPresentation?.mountModel === 'inline';
1116
+ const inlinePlacement = resolveEngageInlinePlacement(activeInAppSpec);
1117
+
1118
+ const handleActiveInAppShow = useCallback((): void => {
1119
+ if (!activeInApp) return;
1120
+ handleInAppShow(activeInApp);
1121
+ const tourId = activeInApp.spec.ext?.coachTourId;
1122
+ const first = Array.isArray(activeInApp.spec.ext?.steps) ? activeInApp.spec.ext?.steps[0] as Record<string, unknown> | undefined : undefined;
1123
+ if (typeof tourId === 'string') {
1124
+ engage.trackCoachmarkEvent?.('$coachmark_tour_started', { tourId });
1125
+ engage.trackCoachmarkEvent?.('$coachmark_step_entered', {
1126
+ tourId,
1127
+ stepId: String(first?.id ?? first?.anchorKey ?? '1'),
1128
+ });
1129
+ }
1130
+ }, [activeInApp, handleInAppShow, engage]);
1131
+ const handleActiveInAppCta = useCallback((cta: NonNullable<InAppRenderSpec['cta']>): void => {
1132
+ if (activeInApp) handleInAppCta(activeInApp, cta);
1133
+ }, [activeInApp, handleInAppCta]);
1134
+ const handleActiveInAppInteraction = useCallback((interaction: InAppMechanicInteraction): void => {
1135
+ if (!activeInApp) return;
1136
+ trackInApp(activeInApp, 'interaction', { interaction });
1137
+ const tourId = activeInApp.spec.ext?.coachTourId;
1138
+ if (typeof tourId !== 'string' || interaction.mechanic !== 'coachmark_spotlight') return;
1139
+ const meta = interaction.meta ?? {};
1140
+ const stepId = String(meta.anchorKey ?? meta.step ?? meta.toStep ?? 'unknown');
1141
+ if (interaction.result === 'step_viewed' && interaction.phase !== 'started') {
1142
+ engage.trackCoachmarkEvent?.('$coachmark_step_entered', { tourId, stepId });
1143
+ } else if (interaction.phase === 'completed') {
1144
+ engage.trackCoachmarkEvent?.('$coachmark_tour_completed', { tourId, stepId });
1145
+ } else if (interaction.result === 'skipped') {
1146
+ engage.trackCoachmarkEvent?.('$coachmark_tour_abandoned', { tourId, stepId });
1147
+ }
1148
+ }, [activeInApp, trackInApp, engage]);
1149
+ const handleActiveInAppRenderError = useCallback((reason: string): void => {
1150
+ if (activeInApp) {
1151
+ trackInApp(activeInApp, 'render_failed', { reason });
1152
+ const tourId = activeInApp.spec.ext?.coachTourId;
1153
+ if (typeof tourId === 'string') {
1154
+ const anchorKey = reason.startsWith('anchor_not_found:') ? reason.slice('anchor_not_found:'.length) : 'unknown';
1155
+ engage.trackCoachmarkEvent?.('$coachmark_anchor_failed', {
1156
+ tourId,
1157
+ stepId: anchorKey,
1158
+ reason: reason.startsWith('anchor_not_found:') ? 'anchor_not_found' : reason,
1159
+ });
1160
+ }
1161
+ }
1162
+ shownInAppMessageIdRef.current = null;
1163
+ activeInAppVariantRef.current = null;
1164
+ setActiveInApp(null);
1165
+ }, [activeInApp, trackInApp, engage]);
1166
+
1167
+ useEffect(() => {
1168
+ if (!routesToInlineSlot || !inlinePlacement.placementKey) return;
1169
+ return inlinePlacementRegistry.subscribe(inlinePlacement.placementKey, () => {
1170
+ setInlinePlacementRevision((revision) => revision + 1);
1171
+ });
1172
+ }, [inlinePlacement.placementKey, inlinePlacementRegistry, routesToInlineSlot]);
1173
+
1174
+ useEffect(() => {
1175
+ if (!activeInApp || active !== null || !routesToInlineSlot) {
1176
+ setInlineFallbackMessageId(null);
1177
+ return;
1178
+ }
1179
+
1180
+ const messageId = activeInApp.id;
1181
+ // Once this message has visibly degraded, keep it on that mount. Moving it
1182
+ // into a late slot would create a second renderer and a misleading lifecycle.
1183
+ if (inlineFallbackMessageId === messageId) return;
1184
+ const reason = inlinePlacement.placementKey
1185
+ ? `inline_placement_not_found:${inlinePlacement.placementKey}`
1186
+ : 'missing_inline_placement_key';
1187
+ const delivery: EngageInlineDelivery = {
1188
+ message: activeInApp,
1189
+ spec: activeInAppSpec,
1190
+ onShow: handleActiveInAppShow,
1191
+ onCta: handleActiveInAppCta,
1192
+ onDismiss: dismissInAppMessage,
1193
+ onComplete: completeInAppMessage,
1194
+ onInteraction: handleActiveInAppInteraction,
1195
+ onInAppGameRequest,
1196
+ onRenderError: handleActiveInAppRenderError,
1197
+ };
1198
+ const delivered = inlinePlacementRegistry.deliver(
1199
+ inlinePlacement.placementKey,
1200
+ delivery,
1201
+ );
1202
+
1203
+ if (delivered) {
1204
+ setInlineFallbackMessageId(null);
1205
+ } else {
1206
+ inlinePlacementRegistry.clear(inlinePlacement.placementKey, messageId);
1207
+ if (inlinePlacement.fallback === 'skip') {
1208
+ setInlineFallbackMessageId(null);
1209
+ handleActiveInAppRenderError(reason);
1210
+ } else {
1211
+ // Preserve the legacy visible behavior, but make the degradation observable.
1212
+ trackInApp(activeInApp, 'render_degraded', {
1213
+ reason,
1214
+ placementKey: inlinePlacement.placementKey,
1215
+ fallback: 'overlay',
1216
+ });
1217
+ setInlineFallbackMessageId(messageId);
1218
+ }
1219
+ }
1220
+
1221
+ return () => inlinePlacementRegistry.clear(inlinePlacement.placementKey, messageId);
1222
+ }, [
1223
+ active,
1224
+ activeInApp,
1225
+ activeInAppSpec,
1226
+ dismissInAppMessage,
1227
+ completeInAppMessage,
1228
+ handleActiveInAppCta,
1229
+ handleActiveInAppInteraction,
1230
+ handleActiveInAppRenderError,
1231
+ handleActiveInAppShow,
1232
+ inlinePlacement.fallback,
1233
+ inlinePlacement.placementKey,
1234
+ inlinePlacementRegistry,
1235
+ inlineFallbackMessageId,
1236
+ inlinePlacementRevision,
1237
+ onInAppGameRequest,
1238
+ routesToInlineSlot,
1239
+ trackInApp,
1240
+ ]);
1241
+
1242
+ const rootInAppVisible = activeInApp !== null &&
1243
+ active === null &&
1244
+ (!routesToInlineSlot || inlineFallbackMessageId === activeInApp.id);
1245
+
1246
+ return (
1247
+ <EngageAnchorProvider>
1248
+ <EngageInlinePlacementProvider registry={inlinePlacementRegistry}>
1249
+ <EngagePromptContext.Provider value={controller}>
1250
+ {children}
1251
+ <EngagePromptView
1252
+ campaign={active}
1253
+ visible={active !== null}
1254
+ rating={ratingRouting}
1255
+ submitStatus={submitStatus}
1256
+ onRetrySubmit={() => void runSubmit()}
1257
+ onCloseSubmit={closeAfterSubmit}
1258
+ onSubmit={(result) => {
1259
+ if (active) handleSubmit(active, result);
1260
+ }}
1261
+ onStart={() => {
1262
+ if (active && surveyStartedRef.current !== active.id) {
1263
+ surveyStartedRef.current = active.id;
1264
+ trackSurveyStage(active, 'started');
1265
+ }
1266
+ }}
1267
+ onDismiss={dismissEngagePrompt}
1268
+ />
1269
+ {/* In-app message overlay. PRECEDENCE: only visible when no campaign
1270
+ prompt is on screen, so the two families never fight for the view. */}
1271
+ <EngageInAppView
1272
+ message={activeInApp}
1273
+ visible={rootInAppVisible}
1274
+ mountModelOverride={routesToInlineSlot && inlineFallbackMessageId === activeInApp?.id
1275
+ ? 'overlay'
1276
+ : undefined}
1277
+ // Render the resolved A/B arm's content (D8/J6) when an experiment is
1278
+ // active; undefined ⇒ the view falls back to the message's base spec.
1279
+ spec={activeInAppVariantRef.current?.spec}
1280
+ onShow={handleActiveInAppShow}
1281
+ onCta={handleActiveInAppCta}
1282
+ onDismiss={dismissInAppMessage}
1283
+ onComplete={completeInAppMessage}
1284
+ onInteraction={handleActiveInAppInteraction}
1285
+ onInAppGameRequest={onInAppGameRequest}
1286
+ onRenderError={handleActiveInAppRenderError}
1287
+ />
1288
+ </EngagePromptContext.Provider>
1289
+ </EngageInlinePlacementProvider>
1290
+ </EngageAnchorProvider>
1291
+ );
1292
+ }
1293
+
1294
+ /**
1295
+ * Hook a host app uses to trigger/dismiss the prompt:
1296
+ * const { showEngagePrompt } = useEngagePrompt();
1297
+ * showEngagePrompt({ types: ['NPS'] });
1298
+ */
1299
+ export function useEngagePrompt(): EngagePromptController {
1300
+ const ctx = useContext(EngagePromptContext);
1301
+ if (!ctx) {
1302
+ // Fail-soft: a host that calls the hook outside the provider gets no-ops
1303
+ // instead of a crash (matches the SDK's no-throw public-API style).
1304
+ return {
1305
+ showEngagePrompt: async () => {
1306
+ logger.warn('[Engage] useEngagePrompt() called outside <EngagePromptProvider>');
1307
+ return false;
1308
+ },
1309
+ dismissEngagePrompt: () => {},
1310
+ showInAppMessage: async () => {
1311
+ logger.warn('[Engage] useEngagePrompt() called outside <EngagePromptProvider>');
1312
+ return false;
1313
+ },
1314
+ dismissInAppMessage: () => {},
1315
+ };
1316
+ }
1317
+ return ctx;
1318
+ }