@scalebun/react-native 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/android/src/main/java/com/scalebun/core/crash/CrashStorage.kt +115 -1
  2. package/android/src/main/java/com/scalebun/core/crash/NativeCrashHandler.kt +14 -0
  3. package/android/src/main/java/com/scalebun/replaysdk/ReplaySdkModule.kt +25 -9
  4. package/android/src/main/java/com/scalebun/replaysdk/voice/VoiceTranscriber.kt +68 -2
  5. package/android/src/main/java/com/scalebun/rn/crash/ScaleBunCrashModule.kt +66 -2
  6. package/android/src/main/java/com/scalebun/rn/ota/SlotManager.kt +70 -4
  7. package/ios/Crash/ScaleBunCrashBridge.mm +6 -0
  8. package/ios/Crash/ScaleBunCrashHandler.swift +14 -1
  9. package/ios/Crash/ScaleBunCrashModule.swift +14 -0
  10. package/ios/Crash/ScaleBunCrashSignalBridge.h +32 -1
  11. package/ios/Crash/ScaleBunCrashSignalBridge.m +172 -7
  12. package/ios/Crash/ScaleBunCrashSignalStress.c +137 -0
  13. package/ios/Crash/ScaleBunCrashStorage.swift +75 -0
  14. package/lib/commonjs/analytics/EventTracker.js +30 -2
  15. package/lib/commonjs/analytics/EventTracker.js.map +1 -1
  16. package/lib/commonjs/bootstrap/SDKBootstrapper.js +31 -23
  17. package/lib/commonjs/bootstrap/SDKBootstrapper.js.map +1 -1
  18. package/lib/commonjs/compat/codepush.js +78 -15
  19. package/lib/commonjs/compat/codepush.js.map +1 -1
  20. package/lib/commonjs/core/config/schema.js +26 -5
  21. package/lib/commonjs/core/config/schema.js.map +1 -1
  22. package/lib/commonjs/core/constants/endpoints.js +9 -6
  23. package/lib/commonjs/core/constants/endpoints.js.map +1 -1
  24. package/lib/commonjs/core/context/device.js +130 -0
  25. package/lib/commonjs/core/context/device.js.map +1 -1
  26. package/lib/commonjs/debug/replayWiring.js +2 -1
  27. package/lib/commonjs/debug/replayWiring.js.map +1 -1
  28. package/lib/commonjs/features/bugreport/BugReportFeature.js +112 -0
  29. package/lib/commonjs/features/bugreport/BugReportFeature.js.map +1 -0
  30. package/lib/commonjs/features/bugreport/buildPayload.js +97 -0
  31. package/lib/commonjs/features/bugreport/buildPayload.js.map +1 -0
  32. package/lib/commonjs/features/crash/CrashReporter.js +25 -1
  33. package/lib/commonjs/features/crash/CrashReporter.js.map +1 -1
  34. package/lib/commonjs/features/crash/capture.js +27 -9
  35. package/lib/commonjs/features/crash/capture.js.map +1 -1
  36. package/lib/commonjs/features/crash/nativeCrashBridge.js +163 -28
  37. package/lib/commonjs/features/crash/nativeCrashBridge.js.map +1 -1
  38. package/lib/commonjs/features/engage/EngagePromptProvider.js +36 -2
  39. package/lib/commonjs/features/engage/EngagePromptProvider.js.map +1 -1
  40. package/lib/commonjs/features/engage/EngagePromptView.js +47 -14
  41. package/lib/commonjs/features/engage/EngagePromptView.js.map +1 -1
  42. package/lib/commonjs/features/engage/engageTriggerEngine.js +10 -0
  43. package/lib/commonjs/features/engage/engageTriggerEngine.js.map +1 -1
  44. package/lib/commonjs/features/engage/engageTypes.js.map +1 -1
  45. package/lib/commonjs/features/journey/ScaleBunDebugRoot.js +2 -1
  46. package/lib/commonjs/features/journey/ScaleBunDebugRoot.js.map +1 -1
  47. package/lib/commonjs/features/ota/OtaOrchestrator.js +72 -5
  48. package/lib/commonjs/features/ota/OtaOrchestrator.js.map +1 -1
  49. package/lib/commonjs/features/ota/deviceAttributes.js +48 -0
  50. package/lib/commonjs/features/ota/deviceAttributes.js.map +1 -0
  51. package/lib/commonjs/features/ota/geoCountry.js +93 -0
  52. package/lib/commonjs/features/ota/geoCountry.js.map +1 -0
  53. package/lib/commonjs/features/ota/useOtaUpdate.js +4 -1
  54. package/lib/commonjs/features/ota/useOtaUpdate.js.map +1 -1
  55. package/lib/commonjs/features/performance/PerformanceFeature.js +23 -9
  56. package/lib/commonjs/features/performance/PerformanceFeature.js.map +1 -1
  57. package/lib/commonjs/features/performance/collectors/FrameMetricsCollector.js +69 -9
  58. package/lib/commonjs/features/performance/collectors/FrameMetricsCollector.js.map +1 -1
  59. package/lib/commonjs/features/performance/transport/PerformanceTransport.js +25 -0
  60. package/lib/commonjs/features/performance/transport/PerformanceTransport.js.map +1 -1
  61. package/lib/commonjs/features/session/BackendSessionAdapter.js +128 -11
  62. package/lib/commonjs/features/session/BackendSessionAdapter.js.map +1 -1
  63. package/lib/commonjs/features/session/JourneyEventPipeline.js +1 -0
  64. package/lib/commonjs/features/session/JourneyEventPipeline.js.map +1 -1
  65. package/lib/commonjs/features/session/ReplayCaptureManager.js +46 -0
  66. package/lib/commonjs/features/session/ReplayCaptureManager.js.map +1 -1
  67. package/lib/commonjs/features/session/SessionManager.js +42 -12
  68. package/lib/commonjs/features/session/SessionManager.js.map +1 -1
  69. package/lib/commonjs/features/session/nativeCapture.js +4 -4
  70. package/lib/commonjs/features/session/nativeCapture.js.map +1 -1
  71. package/lib/commonjs/features/session/outboxFrameCapture.js +3 -2
  72. package/lib/commonjs/features/session/outboxFrameCapture.js.map +1 -1
  73. package/lib/commonjs/features/session/sessionTypes.js.map +1 -1
  74. package/lib/commonjs/index.js +13 -0
  75. package/lib/commonjs/index.js.map +1 -1
  76. package/lib/commonjs/public/ScaleBunFacade.js +199 -2
  77. package/lib/commonjs/public/ScaleBunFacade.js.map +1 -1
  78. package/lib/commonjs/specs/NativeScaleBunCrash.js.map +1 -1
  79. package/lib/module/analytics/EventTracker.js +29 -1
  80. package/lib/module/analytics/EventTracker.js.map +1 -1
  81. package/lib/module/bootstrap/SDKBootstrapper.js +29 -21
  82. package/lib/module/bootstrap/SDKBootstrapper.js.map +1 -1
  83. package/lib/module/compat/codepush.js +77 -14
  84. package/lib/module/compat/codepush.js.map +1 -1
  85. package/lib/module/core/config/schema.js +26 -5
  86. package/lib/module/core/config/schema.js.map +1 -1
  87. package/lib/module/core/constants/endpoints.js +9 -6
  88. package/lib/module/core/constants/endpoints.js.map +1 -1
  89. package/lib/module/core/context/device.js +119 -0
  90. package/lib/module/core/context/device.js.map +1 -1
  91. package/lib/module/debug/replayWiring.js +2 -1
  92. package/lib/module/debug/replayWiring.js.map +1 -1
  93. package/lib/module/features/bugreport/BugReportFeature.js +105 -0
  94. package/lib/module/features/bugreport/BugReportFeature.js.map +1 -0
  95. package/lib/module/features/bugreport/buildPayload.js +91 -0
  96. package/lib/module/features/bugreport/buildPayload.js.map +1 -0
  97. package/lib/module/features/crash/CrashReporter.js +25 -1
  98. package/lib/module/features/crash/CrashReporter.js.map +1 -1
  99. package/lib/module/features/crash/capture.js +28 -9
  100. package/lib/module/features/crash/capture.js.map +1 -1
  101. package/lib/module/features/crash/nativeCrashBridge.js +161 -28
  102. package/lib/module/features/crash/nativeCrashBridge.js.map +1 -1
  103. package/lib/module/features/engage/EngagePromptProvider.js +36 -2
  104. package/lib/module/features/engage/EngagePromptProvider.js.map +1 -1
  105. package/lib/module/features/engage/EngagePromptView.js +47 -14
  106. package/lib/module/features/engage/EngagePromptView.js.map +1 -1
  107. package/lib/module/features/engage/engageTriggerEngine.js +10 -0
  108. package/lib/module/features/engage/engageTriggerEngine.js.map +1 -1
  109. package/lib/module/features/engage/engageTypes.js.map +1 -1
  110. package/lib/module/features/journey/ScaleBunDebugRoot.js +2 -1
  111. package/lib/module/features/journey/ScaleBunDebugRoot.js.map +1 -1
  112. package/lib/module/features/ota/OtaOrchestrator.js +72 -5
  113. package/lib/module/features/ota/OtaOrchestrator.js.map +1 -1
  114. package/lib/module/features/ota/deviceAttributes.js +43 -0
  115. package/lib/module/features/ota/deviceAttributes.js.map +1 -0
  116. package/lib/module/features/ota/geoCountry.js +86 -0
  117. package/lib/module/features/ota/geoCountry.js.map +1 -0
  118. package/lib/module/features/ota/useOtaUpdate.js +4 -1
  119. package/lib/module/features/ota/useOtaUpdate.js.map +1 -1
  120. package/lib/module/features/performance/PerformanceFeature.js +23 -9
  121. package/lib/module/features/performance/PerformanceFeature.js.map +1 -1
  122. package/lib/module/features/performance/collectors/FrameMetricsCollector.js +70 -10
  123. package/lib/module/features/performance/collectors/FrameMetricsCollector.js.map +1 -1
  124. package/lib/module/features/performance/transport/PerformanceTransport.js +25 -0
  125. package/lib/module/features/performance/transport/PerformanceTransport.js.map +1 -1
  126. package/lib/module/features/session/BackendSessionAdapter.js +128 -11
  127. package/lib/module/features/session/BackendSessionAdapter.js.map +1 -1
  128. package/lib/module/features/session/JourneyEventPipeline.js +1 -0
  129. package/lib/module/features/session/JourneyEventPipeline.js.map +1 -1
  130. package/lib/module/features/session/ReplayCaptureManager.js +46 -0
  131. package/lib/module/features/session/ReplayCaptureManager.js.map +1 -1
  132. package/lib/module/features/session/SessionManager.js +42 -12
  133. package/lib/module/features/session/SessionManager.js.map +1 -1
  134. package/lib/module/features/session/nativeCapture.js +4 -5
  135. package/lib/module/features/session/nativeCapture.js.map +1 -1
  136. package/lib/module/features/session/outboxFrameCapture.js +3 -2
  137. package/lib/module/features/session/outboxFrameCapture.js.map +1 -1
  138. package/lib/module/features/session/sessionTypes.js.map +1 -1
  139. package/lib/module/index.js +4 -1
  140. package/lib/module/index.js.map +1 -1
  141. package/lib/module/public/ScaleBunFacade.js +199 -2
  142. package/lib/module/public/ScaleBunFacade.js.map +1 -1
  143. package/lib/module/specs/NativeScaleBunCrash.js.map +1 -1
  144. package/lib/typescript/analytics/EventTracker.d.ts +20 -0
  145. package/lib/typescript/analytics/EventTracker.d.ts.map +1 -1
  146. package/lib/typescript/bootstrap/SDKBootstrapper.d.ts.map +1 -1
  147. package/lib/typescript/compat/codepush.d.ts +27 -4
  148. package/lib/typescript/compat/codepush.d.ts.map +1 -1
  149. package/lib/typescript/core/config/schema.d.ts +4 -1
  150. package/lib/typescript/core/config/schema.d.ts.map +1 -1
  151. package/lib/typescript/core/constants/endpoints.d.ts +9 -6
  152. package/lib/typescript/core/constants/endpoints.d.ts.map +1 -1
  153. package/lib/typescript/core/context/device.d.ts +46 -0
  154. package/lib/typescript/core/context/device.d.ts.map +1 -1
  155. package/lib/typescript/debug/replayWiring.d.ts.map +1 -1
  156. package/lib/typescript/features/bugreport/BugReportFeature.d.ts +59 -0
  157. package/lib/typescript/features/bugreport/BugReportFeature.d.ts.map +1 -0
  158. package/lib/typescript/features/bugreport/buildPayload.d.ts +51 -0
  159. package/lib/typescript/features/bugreport/buildPayload.d.ts.map +1 -0
  160. package/lib/typescript/features/crash/CrashReporter.d.ts +20 -1
  161. package/lib/typescript/features/crash/CrashReporter.d.ts.map +1 -1
  162. package/lib/typescript/features/crash/capture.d.ts +21 -2
  163. package/lib/typescript/features/crash/capture.d.ts.map +1 -1
  164. package/lib/typescript/features/crash/nativeCrashBridge.d.ts +16 -3
  165. package/lib/typescript/features/crash/nativeCrashBridge.d.ts.map +1 -1
  166. package/lib/typescript/features/engage/EngagePromptProvider.d.ts.map +1 -1
  167. package/lib/typescript/features/engage/EngagePromptView.d.ts.map +1 -1
  168. package/lib/typescript/features/engage/EngageTransport.d.ts +1 -1
  169. package/lib/typescript/features/engage/EngageTransport.d.ts.map +1 -1
  170. package/lib/typescript/features/engage/engageTriggerEngine.d.ts.map +1 -1
  171. package/lib/typescript/features/engage/engageTypes.d.ts +15 -0
  172. package/lib/typescript/features/engage/engageTypes.d.ts.map +1 -1
  173. package/lib/typescript/features/journey/ScaleBunDebugRoot.d.ts.map +1 -1
  174. package/lib/typescript/features/ota/OtaOrchestrator.d.ts +14 -2
  175. package/lib/typescript/features/ota/OtaOrchestrator.d.ts.map +1 -1
  176. package/lib/typescript/features/ota/deviceAttributes.d.ts +18 -0
  177. package/lib/typescript/features/ota/deviceAttributes.d.ts.map +1 -0
  178. package/lib/typescript/features/ota/geoCountry.d.ts +14 -0
  179. package/lib/typescript/features/ota/geoCountry.d.ts.map +1 -0
  180. package/lib/typescript/features/ota/useOtaUpdate.d.ts +20 -2
  181. package/lib/typescript/features/ota/useOtaUpdate.d.ts.map +1 -1
  182. package/lib/typescript/features/performance/PerformanceFeature.d.ts +0 -1
  183. package/lib/typescript/features/performance/PerformanceFeature.d.ts.map +1 -1
  184. package/lib/typescript/features/performance/collectors/FrameMetricsCollector.d.ts +33 -0
  185. package/lib/typescript/features/performance/collectors/FrameMetricsCollector.d.ts.map +1 -1
  186. package/lib/typescript/features/performance/transport/PerformanceTransport.d.ts.map +1 -1
  187. package/lib/typescript/features/session/BackendSessionAdapter.d.ts +42 -2
  188. package/lib/typescript/features/session/BackendSessionAdapter.d.ts.map +1 -1
  189. package/lib/typescript/features/session/JourneyEventPipeline.d.ts +2 -0
  190. package/lib/typescript/features/session/JourneyEventPipeline.d.ts.map +1 -1
  191. package/lib/typescript/features/session/ReplayCaptureManager.d.ts +38 -1
  192. package/lib/typescript/features/session/ReplayCaptureManager.d.ts.map +1 -1
  193. package/lib/typescript/features/session/SessionManager.d.ts.map +1 -1
  194. package/lib/typescript/features/session/nativeCapture.d.ts +2 -1
  195. package/lib/typescript/features/session/nativeCapture.d.ts.map +1 -1
  196. package/lib/typescript/features/session/outboxFrameCapture.d.ts.map +1 -1
  197. package/lib/typescript/features/session/sessionTypes.d.ts +20 -3
  198. package/lib/typescript/features/session/sessionTypes.d.ts.map +1 -1
  199. package/lib/typescript/index.d.ts +1 -0
  200. package/lib/typescript/index.d.ts.map +1 -1
  201. package/lib/typescript/public/ScaleBunFacade.d.ts +69 -0
  202. package/lib/typescript/public/ScaleBunFacade.d.ts.map +1 -1
  203. package/lib/typescript/public/types.d.ts +34 -8
  204. package/lib/typescript/public/types.d.ts.map +1 -1
  205. package/lib/typescript/specs/NativeScaleBunCrash.d.ts +14 -0
  206. package/lib/typescript/specs/NativeScaleBunCrash.d.ts.map +1 -1
  207. package/lib/typescript/vendor/protocol/index.d.ts +17 -17
  208. package/package.json +1 -1
  209. package/src/analytics/EventTracker.ts +28 -1
  210. package/src/bootstrap/SDKBootstrapper.ts +27 -23
  211. package/src/compat/codepush.ts +87 -16
  212. package/src/core/config/schema.ts +26 -5
  213. package/src/core/constants/endpoints.ts +9 -6
  214. package/src/core/context/device.ts +146 -0
  215. package/src/debug/replayWiring.ts +2 -1
  216. package/src/features/bugreport/BugReportFeature.ts +128 -0
  217. package/src/features/bugreport/buildPayload.ts +116 -0
  218. package/src/features/crash/CrashReporter.ts +40 -1
  219. package/src/features/crash/capture.ts +36 -9
  220. package/src/features/crash/nativeCrashBridge.ts +156 -22
  221. package/src/features/engage/EngagePromptProvider.tsx +48 -2
  222. package/src/features/engage/EngagePromptView.tsx +51 -15
  223. package/src/features/engage/EngageTransport.ts +1 -1
  224. package/src/features/engage/engageTriggerEngine.ts +15 -0
  225. package/src/features/engage/engageTypes.ts +15 -0
  226. package/src/features/journey/ScaleBunDebugRoot.tsx +2 -1
  227. package/src/features/ota/OtaOrchestrator.ts +84 -6
  228. package/src/features/ota/deviceAttributes.ts +46 -0
  229. package/src/features/ota/geoCountry.ts +85 -0
  230. package/src/features/ota/useOtaUpdate.ts +20 -2
  231. package/src/features/performance/PerformanceFeature.ts +22 -9
  232. package/src/features/performance/collectors/FrameMetricsCollector.ts +72 -10
  233. package/src/features/performance/transport/PerformanceTransport.ts +18 -0
  234. package/src/features/session/BackendSessionAdapter.ts +137 -10
  235. package/src/features/session/JourneyEventPipeline.ts +3 -0
  236. package/src/features/session/ReplayCaptureManager.ts +53 -1
  237. package/src/features/session/SessionManager.ts +46 -7
  238. package/src/features/session/nativeCapture.ts +3 -2
  239. package/src/features/session/outboxFrameCapture.ts +3 -2
  240. package/src/features/session/sessionTypes.ts +26 -3
  241. package/src/index.ts +4 -1
  242. package/src/public/ScaleBunFacade.ts +198 -2
  243. package/src/public/types.ts +35 -8
  244. package/src/specs/NativeScaleBunCrash.ts +16 -0
@@ -13,10 +13,14 @@ import { noThrow, safeAsync } from '../core/lifecycle/crashSafe';
13
13
  import { logger } from '../core/logger/internalLogger';
14
14
  import { createEnvelope } from '../pipeline/envelope/envelope';
15
15
  import { generateEventId } from '../features/replay/core/ids/sessionId';
16
+ import { buildBugReportPayload } from '../features/bugreport/buildPayload';
17
+ import type { BugSubmitResult } from '../features/bugreport/BugReportFeature';
18
+ import { BugReportFeature } from '../features/bugreport/BugReportFeature';
16
19
  import { PersistentQueue } from '../pipeline/queue/persistentQueue';
17
20
  import { MemoryBackend, createStorageBackend } from '../storage/StorageBackend';
18
21
  import { HttpClient } from '../transport/http/httpClient';
19
22
  import { getDeviceId } from '../core/id/deviceId';
23
+ import { mergeDeviceContext } from '../core/context/device';
20
24
  import { coreContainer } from '../core/di/container';
21
25
  import { flushQueue } from '../pipeline/queue/flushQueue';
22
26
  import {
@@ -247,6 +251,13 @@ class ScaleBunFacade {
247
251
  // Continue — debug connection is independent of backend API
248
252
  }
249
253
 
254
+ // OTA from init config. Without this the whole `ota` config block was
255
+ // inert: `otaOrchestrator.init()` was reachable only by importing the
256
+ // orchestrator directly, so an app that followed the documented
257
+ // `ota: { enabled: true }` path silently got no updates AND no
258
+ // signature enforcement (sync() gates on the flag init() sets).
259
+ this._maybeStartOta(rawConfig);
260
+
250
261
  // Auto debug connection from init config — runs regardless of backend status
251
262
  const debugConfig = (rawConfig?.desktopDebug ?? rawConfig?.debug) as DebugConnectionConfig | undefined;
252
263
  if (debugConfig) {
@@ -260,6 +271,44 @@ class ScaleBunFacade {
260
271
  }
261
272
  }
262
273
 
274
+ /**
275
+ * Boot the OTA orchestrator when the init config asks for it.
276
+ *
277
+ * `publicSigningKey` is honoured here so signature enforcement is reachable
278
+ * from configuration alone. The SDK ships no ed25519 implementation, so a
279
+ * host that pins a key must also supply `ota.verifySignature`; pinning a key
280
+ * without a verifier is fail-CLOSED by design (signature.ts) — an update
281
+ * that cannot be verified is not installed. We say that out loud rather than
282
+ * letting the app discover it as a silent no-update condition.
283
+ */
284
+ private _maybeStartOta(rawConfig: any): void {
285
+ const ota = rawConfig?.ota;
286
+ if (!ota?.enabled) return;
287
+ try {
288
+ // Required lazily: hosts that never enable OTA should not pay for the
289
+ // module (or touch the native spec) at import time.
290
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
291
+ const { otaOrchestrator } = require('../features/ota/OtaOrchestrator');
292
+ const publicKey = ota.publicSigningKey;
293
+ const verifier = ota.verifySignature;
294
+ if (publicKey && typeof verifier !== 'function') {
295
+ logger.warn(
296
+ '[ScaleBun] ota.publicSigningKey is set but ota.verifySignature is not a function. ' +
297
+ 'Signature checking is fail-closed: updates will be REJECTED until a verifier is supplied.',
298
+ );
299
+ }
300
+ otaOrchestrator.init({
301
+ ...(publicKey ? { signature: { publicKey, verifier } } : {}),
302
+ ...(typeof ota.healthyTimeoutMs === 'number'
303
+ ? { healthyTimeoutMs: ota.healthyTimeoutMs }
304
+ : {}),
305
+ });
306
+ logger.info('[ScaleBun] OTA enabled from init config.');
307
+ } catch (err: any) {
308
+ logger.warn(`[ScaleBun] OTA init failed: ${err?.message ?? err}`);
309
+ }
310
+ }
311
+
263
312
  private _autoEnableDebug(debugConfig: DebugConnectionConfig): void {
264
313
  try {
265
314
  const dbgConfig = buildDebugConfigFromInitConfig(debugConfig);
@@ -278,7 +327,9 @@ class ScaleBunFacade {
278
327
  const appId = rawConfig?.appId;
279
328
  const enabled = rawConfig?.eventTracking !== false;
280
329
  const clientKey = this._clientKey ?? rawConfig?.clientKey;
281
- const apiBaseUrl = this._apiBaseUrl ?? rawConfig?.apiBaseUrl;
330
+ // apiBaseUrl is LOCKED use only the bootstrapped value, never fall back to raw
331
+ // integrator input, so the event-tracker/SKAdNetwork lane can't be repointed either.
332
+ const apiBaseUrl = this._apiBaseUrl;
282
333
  if (!enabled || !appId || !clientKey || !apiBaseUrl) {
283
334
  if (enabled && !appId) {
284
335
  logger.info('[ScaleBun] Envelope event tracking disabled — no appId in init config.');
@@ -307,7 +358,16 @@ class ScaleBunFacade {
307
358
  automaticEventTracking:
308
359
  rawConfig?.automaticEventTracking === true,
309
360
  flushIntervalMs: rawConfig?.flushIntervalMs,
310
- context: rawConfig?.context,
361
+ /**
362
+ * DETECTED, not configured.
363
+ *
364
+ * This was `rawConfig?.context` — whatever the app typed. Production carries
365
+ * `device_model: "android"` and `manufacturer: "android"` from a hand-written
366
+ * literal, while the replay lane was reading the real values off the native bridge
367
+ * the whole time. The SDK knows the device; the integrator is guessing at it.
368
+ * Everything outside the detected keys is still theirs.
369
+ */
370
+ context: mergeDeviceContext(rawConfig?.context),
311
371
  // Use the same foreground id as replay/journey capture. The
312
372
  // callback is resolved per event because the session subsystem
313
373
  // may start after this tracker is constructed.
@@ -597,6 +657,125 @@ class ScaleBunFacade {
597
657
  return null;
598
658
  }
599
659
 
660
+ // ─── Bug reports (F-BUG) ────────────────────────────────────────────
661
+
662
+ /**
663
+ * Thin feature object; the facade owns the HTTP, mirroring the web SDK split.
664
+ * Its only job here is raising the 'report' capture signal so replay flushes.
665
+ */
666
+ private readonly _bugReport = new BugReportFeature();
667
+
668
+ /**
669
+ * Submit a bug report against the CURRENT session.
670
+ *
671
+ * Mirrors the web SDK's `reportBugDetailed` ordering, which is load-bearing rather than
672
+ * incidental:
673
+ *
674
+ * 1. resolve the session — a report with no session cannot be joined to its evidence
675
+ * 2. signal 'report' — replay flushes its buffer so the report has visual context
676
+ * 3. snapshot the anchor — BEFORE the POST. A flush asks the recorder to deliver pending
677
+ * bytes; it cannot retroactively manufacture a frame, so an absent
678
+ * coordinate must stay absent rather than becoming a misleading
679
+ * "start of session" link in the dashboard
680
+ * 4. build + POST — bounds enforced by buildBugReportPayload against the server DTO
681
+ *
682
+ * Returns the report id, or `{ e }` naming which branch failed — 'init' (SDK not configured)
683
+ * reads differently from 'send' (network), and that difference is what tells an integrator
684
+ * whether to check their setup or their connection. It never throws: this is called from a UI
685
+ * the host app renders, usually from a user gesture, and an exception escaping into a React
686
+ * Native render tree is a redbox shown to the customer's user over a report they were sending us.
687
+ */
688
+ public async reportBugDetailed(input: {
689
+ message: string;
690
+ title?: string;
691
+ email?: string;
692
+ clientReportId?: string;
693
+ }): Promise<BugSubmitResult> {
694
+ if (!input || !input.message || input.message.trim().length === 0) {
695
+ logger.warn('[ScaleBun] reportBug: a message is required');
696
+ return { e: 'empty' };
697
+ }
698
+ if (!this._clientKey || !this._apiBaseUrl) {
699
+ logger.warn('[ScaleBun] reportBug: SDK is not configured (clientKey/apiBaseUrl)');
700
+ return { e: 'init' };
701
+ }
702
+
703
+ const sessionId = this.getActiveSessionId();
704
+ if (!sessionId) {
705
+ // No session means no row to attach to, and the ingestion route is session-scoped.
706
+ logger.warn('[ScaleBun] reportBug: no active session');
707
+ return { e: 'init' };
708
+ }
709
+
710
+ try {
711
+ // Flush replay into the session BEFORE reading the anchor below.
712
+ this._bugReport.signalReport();
713
+
714
+ const coord = this._replayCoord();
715
+ const payload = buildBugReportPayload(input, this._recentBreadcrumbs(), {
716
+ replayCoord: coord,
717
+ timestamp: Date.now(),
718
+ });
719
+
720
+ const res = await fetch(
721
+ `${this._apiBaseUrl}/ingestion/sessions/${sessionId}/bug-reports`,
722
+ {
723
+ method: 'POST',
724
+ headers: {
725
+ 'Content-Type': 'application/json',
726
+ 'x-scalebun-client-key': this._clientKey,
727
+ },
728
+ body: JSON.stringify(payload),
729
+ },
730
+ );
731
+ if (!res.ok) {
732
+ logger.warn('[ScaleBun] reportBug: server rejected the report', res.status);
733
+ return { e: 'send' };
734
+ }
735
+ const body = await res.json().catch(() => null);
736
+ return body?.id ?? { e: 'send' };
737
+ } catch (err: any) {
738
+ logger.warn('[ScaleBun] reportBug failed:', err?.message);
739
+ return { e: 'send' };
740
+ }
741
+ }
742
+
743
+ /**
744
+ * The replay coordinate at submit time, or null.
745
+ *
746
+ * Deliberately returns null rather than a zeroed coordinate when replay is disabled, paused, or
747
+ * has not produced a frame yet — see the anchor note on reportBug.
748
+ */
749
+ /**
750
+ * The replay coordinate at submit time.
751
+ *
752
+ * ALWAYS null today, deliberately and visibly.
753
+ *
754
+ * The web SDK reads this from its recorder, but the RN replay subsystem exposes no sequence or
755
+ * segment accessor on its public API (features/replay/public/api.ts) -- there is currently no
756
+ * supported way to ask it where playback is. Probing for it through an `any` cast would compile,
757
+ * miss, and leave every report anchor-less while looking wired: the same defect as the existing
758
+ * reportBug(), which emits an analytics event and looks like it files a report.
759
+ *
760
+ * Returning null keeps the payload HONEST -- buildBugReportPayload omits replaySeq entirely, so
761
+ * the dashboard shows no anchor rather than a false one pointing at the start of the session.
762
+ * To finish this: expose the current seq + segment id from the replay recorder, then read them
763
+ * here. The payload builder and its contract tests already handle a real coordinate, including
764
+ * a genuine seq of 0.
765
+ */
766
+ private _replayCoord(): { seq?: number | null; segmentId?: string | null } | null {
767
+ return null;
768
+ }
769
+ /** Recent console breadcrumbs for quick triage. The full set already travels with the session. */
770
+ private _recentBreadcrumbs(): readonly unknown[] {
771
+ try {
772
+ const f = (this as any)._loggerFeature ?? (this as any)._consoleFeature;
773
+ return f?.getRecent?.() ?? [];
774
+ } catch {
775
+ return [];
776
+ }
777
+ }
778
+
600
779
  // ─── Engage ─────────────────────────────────────────────────────────
601
780
 
602
781
  /**
@@ -1131,6 +1310,23 @@ class ScaleBunFacade {
1131
1310
 
1132
1311
  /** Report a bug (convenience wrapper over track) */
1133
1312
  public reportBug(description: string, metadata?: Record<string, unknown>) {
1313
+ // D11: this used to be ONLY `track('$bug_report')` — an analytics event
1314
+ // with zero consumers (no mapper, no handler, no BugReport row), so
1315
+ // /diagnose/bug-reports stayed empty forever for RN apps while the web
1316
+ // SDK used the real ingest. Route to the same endpoint the web SDK
1317
+ // uses; the analytics event is kept as a breadcrumb, not the delivery.
1318
+ try {
1319
+ const transport = SessionManager.getExistingInstance()?.getBackendTransport();
1320
+ transport?.submitBugReport({
1321
+ message: description,
1322
+ title:
1323
+ typeof metadata?.title === 'string' ? (metadata.title as string) : undefined,
1324
+ email:
1325
+ typeof metadata?.email === 'string' ? (metadata.email as string) : undefined,
1326
+ });
1327
+ } catch {
1328
+ /* delivery is best-effort; the track below still records intent */
1329
+ }
1134
1330
  return this.track('$bug_report', { description, ...metadata });
1135
1331
  }
1136
1332
 
@@ -78,14 +78,28 @@ export interface ScaleBunInitConfig {
78
78
  /** Publishable API key from dashboard */
79
79
  publishableKey: string;
80
80
  /**
81
- * Backend API base URL. Optional — defaults to the ScaleBun cloud.
82
- * Override only for staging or a self-hosted backend.
81
+ * @deprecated The endpoint is LOCKED to the ScaleBun cloud. Any value passed here is accepted
82
+ * (for backward compatibility) but ignored the bootstrapper always uses the fixed
83
+ * `DEFAULT_API_BASE_URL`. To target a different backend, change that constant at build time.
83
84
  */
84
85
  apiBaseUrl?: string;
85
86
  /** @deprecated Use desktopDebug instead */
86
87
  debug?: boolean;
87
- /** Enable session replay capture. Default: true */
88
+ /** Enable session replay capture (feature on/off). Default: true */
88
89
  sessionReplay?: boolean;
90
+ /**
91
+ * Session-replay capture options (SaaS mode).
92
+ * - `record` (default **true**): auto-start frame recording at cold launch so replays appear out
93
+ * of the box. Set `false` for events-only sessions — you can still record on demand with
94
+ * `ScaleBun.startSession()`.
95
+ * - `captureMode` / `intervalMs` / `quality`: tune capture cadence.
96
+ */
97
+ replay?: {
98
+ record?: boolean;
99
+ captureMode?: 'navigation' | 'interactions' | 'frequency';
100
+ intervalMs?: number;
101
+ quality?: 'high' | 'normal' | 'low' | 'grayscale';
102
+ };
89
103
  /** Event flush interval in ms. Default: 5000 */
90
104
  flushIntervalMs?: number;
91
105
  /** Max events per flush batch. Default: 1000 */
@@ -234,7 +248,6 @@ export interface ExportResult {
234
248
  * await ScaleBun.init({
235
249
  * projectId: 'proj_xxx',
236
250
  * publishableKey: 'pk_xxx',
237
- * apiBaseUrl: 'https://api.scalebun.com',
238
251
  * });
239
252
  * ```
240
253
  *
@@ -243,7 +256,6 @@ export interface ExportResult {
243
256
  * await ScaleBun.init({
244
257
  * projectId: 'proj_xxx',
245
258
  * publishableKey: 'pk_xxx',
246
- * apiBaseUrl: 'https://api.scalebun.com',
247
259
  * debug: true,
248
260
  * });
249
261
  * ```
@@ -254,8 +266,9 @@ export interface SimplifiedInitConfig {
254
266
  /** Publishable API key from dashboard */
255
267
  publishableKey: string;
256
268
  /**
257
- * Backend API base URL. Optional — defaults to the ScaleBun cloud.
258
- * Override only for staging or a self-hosted backend.
269
+ * @deprecated The endpoint is LOCKED to the ScaleBun cloud. Any value passed here is accepted
270
+ * (for backward compatibility) but ignored the bootstrapper always uses the fixed
271
+ * `DEFAULT_API_BASE_URL`. To target a different backend, change that constant at build time.
259
272
  */
260
273
  apiBaseUrl?: string;
261
274
 
@@ -271,9 +284,23 @@ export interface SimplifiedInitConfig {
271
284
  /** @deprecated Use desktopDebug instead. */
272
285
  debug?: DebugConnectionConfig;
273
286
 
274
- /** Enable session replay capture. Default: true */
287
+ /** Enable session replay capture (feature on/off). Default: true */
275
288
  sessionReplay?: boolean;
276
289
 
290
+ /**
291
+ * Session-replay capture options (SaaS mode).
292
+ * - `record` (default **true**): auto-start frame recording at cold launch so replays appear out
293
+ * of the box. Set `false` for events-only sessions — you can still record on demand with
294
+ * `ScaleBun.startSession()`.
295
+ * - `captureMode` / `intervalMs` / `quality`: tune capture cadence.
296
+ */
297
+ replay?: {
298
+ record?: boolean;
299
+ captureMode?: 'navigation' | 'interactions' | 'frequency';
300
+ intervalMs?: number;
301
+ quality?: 'high' | 'normal' | 'low' | 'grayscale';
302
+ };
303
+
277
304
  /** SDK-wide privacy settings */
278
305
  privacy?: PrivacyConfig;
279
306
 
@@ -23,8 +23,24 @@ export interface Spec extends TurboModule {
23
23
  /** DEV/TEST only: crash the process so the next launch can drain it. */
24
24
  crashNow(): Promise<boolean>;
25
25
 
26
+ /**
27
+ * Record which session is live so a crash can name it.
28
+ *
29
+ * Called once per session. Native writes it to crash-safe storage BEFORE any crash can happen —
30
+ * a fatal handler cannot call back into JS to ask.
31
+ */
32
+ setActiveSessionId(sessionId: string): Promise<boolean>;
33
+
26
34
  /** Return the previous run's crash record (or null), then clear it. */
27
35
  drainPendingCrash(): Promise<Object>;
36
+
37
+ /**
38
+ * Delete a delivered crash by its stable id.
39
+ *
40
+ * Called ONLY after the backend has accepted it. The read is non-destructive precisely so this
41
+ * can be the moment of deletion — a failed upload must leave the durable copy in place.
42
+ */
43
+ clearPendingCrash(crashId: string): Promise<boolean>;
28
44
  }
29
45
 
30
46
  /**