@scalebun/react-native 1.3.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/android/src/main/java/com/scalebun/rn/ota/BsPatch.kt +182 -0
  2. package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +88 -13
  3. package/android/src/oldarch/java/com/scalebun/rn/crash/ScaleBunCrashSpec.kt +16 -0
  4. package/bin/scalebun.js +55 -3
  5. package/dist/scalebun.full.js +190 -110
  6. package/dist/scalebun.full.js.map +1 -1
  7. package/dist/scalebun.slim.js +188 -109
  8. package/dist/scalebun.slim.js.map +1 -1
  9. package/ios/Ota/BsPatch.swift +180 -0
  10. package/ios/Ota/OtaSlotManager.swift +59 -2
  11. package/ios/Ota/ScaleBunOtaModule.swift +89 -10
  12. package/lib/commonjs/analytics/EventTracker.js +26 -4
  13. package/lib/commonjs/analytics/EventTracker.js.map +1 -1
  14. package/lib/commonjs/analytics/eventLane.js +23 -0
  15. package/lib/commonjs/analytics/eventLane.js.map +1 -1
  16. package/lib/commonjs/core/constants/version.js +1 -1
  17. package/lib/commonjs/core/context/device.js +18 -0
  18. package/lib/commonjs/core/context/device.js.map +1 -1
  19. package/lib/commonjs/core/id/installationId.js +55 -0
  20. package/lib/commonjs/core/id/installationId.js.map +1 -0
  21. package/lib/commonjs/debug/bootstrap.js +14 -1
  22. package/lib/commonjs/debug/bootstrap.js.map +1 -1
  23. package/lib/commonjs/debug/transport.js +2 -1
  24. package/lib/commonjs/debug/transport.js.map +1 -1
  25. package/lib/commonjs/features/journey/journeyManager.js +2 -1
  26. package/lib/commonjs/features/journey/journeyManager.js.map +1 -1
  27. package/lib/commonjs/features/ota/OtaOrchestrator.js +2 -30
  28. package/lib/commonjs/features/ota/OtaOrchestrator.js.map +1 -1
  29. package/lib/commonjs/features/performance/collectors/FrameMetricsCollector.js +27 -3
  30. package/lib/commonjs/features/performance/collectors/FrameMetricsCollector.js.map +1 -1
  31. package/lib/commonjs/features/performance/collectors/JsStallCollector.js +20 -1
  32. package/lib/commonjs/features/performance/collectors/JsStallCollector.js.map +1 -1
  33. package/lib/commonjs/features/session/BackendSessionAdapter.js +8 -0
  34. package/lib/commonjs/features/session/BackendSessionAdapter.js.map +1 -1
  35. package/lib/commonjs/public/ScaleBunFacade.js +10 -2
  36. package/lib/commonjs/public/ScaleBunFacade.js.map +1 -1
  37. package/lib/module/analytics/EventTracker.js +26 -4
  38. package/lib/module/analytics/EventTracker.js.map +1 -1
  39. package/lib/module/analytics/eventLane.js +22 -0
  40. package/lib/module/analytics/eventLane.js.map +1 -1
  41. package/lib/module/core/constants/version.js +1 -1
  42. package/lib/module/core/context/device.js +17 -0
  43. package/lib/module/core/context/device.js.map +1 -1
  44. package/lib/module/core/id/installationId.js +50 -0
  45. package/lib/module/core/id/installationId.js.map +1 -0
  46. package/lib/module/debug/bootstrap.js +14 -1
  47. package/lib/module/debug/bootstrap.js.map +1 -1
  48. package/lib/module/debug/transport.js +2 -1
  49. package/lib/module/debug/transport.js.map +1 -1
  50. package/lib/module/features/journey/journeyManager.js +2 -1
  51. package/lib/module/features/journey/journeyManager.js.map +1 -1
  52. package/lib/module/features/ota/OtaOrchestrator.js +1 -29
  53. package/lib/module/features/ota/OtaOrchestrator.js.map +1 -1
  54. package/lib/module/features/performance/collectors/FrameMetricsCollector.js +27 -3
  55. package/lib/module/features/performance/collectors/FrameMetricsCollector.js.map +1 -1
  56. package/lib/module/features/performance/collectors/JsStallCollector.js +20 -1
  57. package/lib/module/features/performance/collectors/JsStallCollector.js.map +1 -1
  58. package/lib/module/features/session/BackendSessionAdapter.js +8 -0
  59. package/lib/module/features/session/BackendSessionAdapter.js.map +1 -1
  60. package/lib/module/public/ScaleBunFacade.js +12 -4
  61. package/lib/module/public/ScaleBunFacade.js.map +1 -1
  62. package/lib/typescript/analytics/EventTracker.d.ts.map +1 -1
  63. package/lib/typescript/analytics/eventLane.d.ts +2 -0
  64. package/lib/typescript/analytics/eventLane.d.ts.map +1 -1
  65. package/lib/typescript/core/constants/version.d.ts +1 -1
  66. package/lib/typescript/core/context/device.d.ts +13 -0
  67. package/lib/typescript/core/context/device.d.ts.map +1 -1
  68. package/lib/typescript/core/id/installationId.d.ts +20 -0
  69. package/lib/typescript/core/id/installationId.d.ts.map +1 -0
  70. package/lib/typescript/debug/bootstrap.d.ts +18 -0
  71. package/lib/typescript/debug/bootstrap.d.ts.map +1 -1
  72. package/lib/typescript/debug/transport.d.ts.map +1 -1
  73. package/lib/typescript/features/journey/journeyManager.d.ts.map +1 -1
  74. package/lib/typescript/features/ota/OtaOrchestrator.d.ts.map +1 -1
  75. package/lib/typescript/features/performance/collectors/FrameMetricsCollector.d.ts +14 -0
  76. package/lib/typescript/features/performance/collectors/FrameMetricsCollector.d.ts.map +1 -1
  77. package/lib/typescript/features/performance/collectors/JsStallCollector.d.ts +5 -0
  78. package/lib/typescript/features/performance/collectors/JsStallCollector.d.ts.map +1 -1
  79. package/lib/typescript/features/session/BackendSessionAdapter.d.ts.map +1 -1
  80. package/lib/typescript/public/ScaleBunFacade.d.ts.map +1 -1
  81. package/lib/typescript/public/types.d.ts +1 -1
  82. package/lib/typescript/public/types.d.ts.map +1 -1
  83. package/package.json +3 -2
  84. package/src/analytics/EventTracker.ts +26 -4
  85. package/src/analytics/eventLane.ts +22 -0
  86. package/src/core/constants/version.ts +1 -1
  87. package/src/core/context/device.ts +17 -0
  88. package/src/core/id/installationId.ts +52 -0
  89. package/src/debug/bootstrap.ts +38 -1
  90. package/src/debug/transport.ts +2 -1
  91. package/src/features/journey/journeyManager.ts +2 -1
  92. package/src/features/ota/OtaOrchestrator.ts +1 -30
  93. package/src/features/performance/collectors/FrameMetricsCollector.ts +32 -3
  94. package/src/features/performance/collectors/JsStallCollector.ts +20 -1
  95. package/src/features/session/BackendSessionAdapter.ts +8 -0
  96. package/src/public/ScaleBunFacade.ts +12 -4
  97. package/src/public/types.ts +1 -1
@@ -489,7 +489,7 @@ var SDK_VERSION;
489
489
  var init_version = __esm({
490
490
  "lib/module/core/constants/version.js"() {
491
491
  "use strict";
492
- SDK_VERSION = "1.3.3";
492
+ SDK_VERSION = "1.5.0";
493
493
  }
494
494
  });
495
495
 
@@ -539,6 +539,10 @@ function mergeDeviceContext(configured, detected = staticDeviceContext()) {
539
539
  ...detected
540
540
  };
541
541
  }
542
+ function resolveEventPlatform() {
543
+ const os = resolvePlatformOS();
544
+ return os === "ios" || os === "android" || os === "web" ? os : "react_native";
545
+ }
542
546
  var init_device = __esm({
543
547
  "lib/module/core/context/device.js"() {
544
548
  "use strict";
@@ -1235,6 +1239,7 @@ var init_transport = __esm({
1235
1239
  "use strict";
1236
1240
  init_protocol2();
1237
1241
  init_timings();
1242
+ init_version();
1238
1243
  DebugTransport = class {
1239
1244
  ws = null;
1240
1245
  state = "disconnected";
@@ -1406,7 +1411,7 @@ var init_transport = __esm({
1406
1411
  timestamp: Date.now(),
1407
1412
  sequence: this.nextSequence(),
1408
1413
  payload: {
1409
- sdkVersion: "0.1.0",
1414
+ sdkVersion: SDK_VERSION,
1410
1415
  appName: this.config.appName,
1411
1416
  appVersion: this.config.appVersion,
1412
1417
  bundleId: this.config.bundleId,
@@ -7329,7 +7334,7 @@ var init_JsStallCollector = __esm({
7329
7334
  "use strict";
7330
7335
  init_models();
7331
7336
  MAX_STALL_RECORDS = 100;
7332
- JsStallCollector = class {
7337
+ JsStallCollector = class _JsStallCollector {
7333
7338
  pollTimer = null;
7334
7339
  reportTimer = null;
7335
7340
  lastTick = 0;
@@ -7339,6 +7344,11 @@ var init_JsStallCollector = __esm({
7339
7344
  maxStallMs = 0;
7340
7345
  totalStallMs = 0;
7341
7346
  recentStalls = [];
7347
+ /** Per-run caps on INDIVIDUAL stall events — see the emit site. Summaries are uncapped. */
7348
+ static EMITTED_STALLS_MAX = 200;
7349
+ static CRITICAL_OVERFLOW_MAX = 100;
7350
+ emittedStalls = 0;
7351
+ emittedCriticalOverflow = 0;
7342
7352
  constructor(config, onStall, onSummary, contextProvider) {
7343
7353
  this.config = config;
7344
7354
  this.onStall = onStall;
@@ -7399,6 +7409,13 @@ var init_JsStallCollector = __esm({
7399
7409
  }
7400
7410
  if (durationMs > 100) {
7401
7411
  const severity = this._classifySeverity(durationMs);
7412
+ if (this.emittedStalls >= _JsStallCollector.EMITTED_STALLS_MAX) {
7413
+ if (severity !== "critical") return;
7414
+ if (this.emittedCriticalOverflow >= _JsStallCollector.CRITICAL_OVERFLOW_MAX) return;
7415
+ this.emittedCriticalOverflow++;
7416
+ } else {
7417
+ this.emittedStalls++;
7418
+ }
7402
7419
  const event = {
7403
7420
  id: `stall-${timestamp}-${Math.random().toString(36).substring(2, 8)}`,
7404
7421
  type: "js_stall",
@@ -7835,13 +7852,27 @@ var init_FrameMetricsCollector = __esm({
7835
7852
  import_react_native9 = require("react-native");
7836
7853
  init_getNativePerformanceModule();
7837
7854
  init_models();
7838
- FrameMetricsCollector = class {
7855
+ FrameMetricsCollector = class _FrameMetricsCollector {
7839
7856
  eventSubscription = null;
7840
7857
  running = false;
7841
7858
  /** AppState subscription — see `_installAppStateGate`. */
7842
7859
  appStateSubscription = null;
7843
7860
  /** True while the native sampler is actually running (i.e. armed AND foregrounded). */
7844
7861
  sampling = false;
7862
+ /** Timestamp of the last emitted window — drives the healthy-window heartbeat below. */
7863
+ lastEmitTs = 0;
7864
+ /**
7865
+ * HEALTHY windows emit at a heartbeat, not per report interval.
7866
+ *
7867
+ * The native sampler reports every `reportIntervalMs` (5 s), and every window became a stored
7868
+ * row — on a session hours long that is 720 rows/hour of "still 60 fps", which is how real
7869
+ * sessions hit the dashboard's 2,000-row display cap with nothing to say. A window that shows
7870
+ * ANY jank (dropped/frozen frames, meaningful slow-frame count, an FPS dip) always emits; a
7871
+ * healthy window emits only when the heartbeat has elapsed, keeping the FPS series alive for
7872
+ * the charts at 1/12th the volume. Capture cadence is unchanged — only identical-looking
7873
+ * healthy rows are elided, and the first window after ANY emit gap states the recovery.
7874
+ */
7875
+ static HEALTHY_HEARTBEAT_MS = 6e4;
7845
7876
  constructor(config, onFrameMetrics, contextProvider) {
7846
7877
  this.config = config;
7847
7878
  this.onFrameMetrics = onFrameMetrics;
@@ -7944,10 +7975,17 @@ var init_FrameMetricsCollector = __esm({
7944
7975
  const expectedFrames = Math.round(windowMs / 16.67);
7945
7976
  const actualFrames = Math.max(0, totalFrames - droppedFrames);
7946
7977
  const estimatedFps = expectedFrames > 0 ? Math.round(actualFrames / expectedFrames * 60) : 60;
7978
+ const clampedFps = Math.min(60, Math.max(0, estimatedFps));
7979
+ const now2 = Date.now();
7980
+ const healthy = droppedFrames === 0 && frozenFrames === 0 && slowFrames <= 2 && clampedFps >= 55;
7981
+ if (healthy && this.lastEmitTs !== 0 && now2 - this.lastEmitTs < _FrameMetricsCollector.HEALTHY_HEARTBEAT_MS) {
7982
+ return;
7983
+ }
7984
+ this.lastEmitTs = now2;
7947
7985
  const event = {
7948
- id: `frames-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`,
7986
+ id: `frames-${now2}-${Math.random().toString(36).substring(2, 8)}`,
7949
7987
  type: "frame_metrics",
7950
- timestamp: Date.now(),
7988
+ timestamp: now2,
7951
7989
  schemaVersion: PERF_SCHEMA_VERSION,
7952
7990
  context: this.contextProvider(),
7953
7991
  windowMs,
@@ -7955,7 +7993,7 @@ var init_FrameMetricsCollector = __esm({
7955
7993
  droppedFrames,
7956
7994
  slowFrames,
7957
7995
  frozenFrames,
7958
- estimatedFps: Math.min(60, Math.max(0, estimatedFps)),
7996
+ estimatedFps: clampedFps,
7959
7997
  maxFrameTimeMs
7960
7998
  };
7961
7999
  this.onFrameMetrics(event);
@@ -10003,6 +10041,10 @@ function enableDebug(config) {
10003
10041
  __DEV__ && logger.debug("Debug already enabled, skipping");
10004
10042
  return;
10005
10043
  }
10044
+ if (!__DEV__ && !config.allowInRelease) {
10045
+ logger.warn("[ScaleBun] Debug connection refused in a release build. The debug transport is unencrypted (ws://) and carries captured traffic and the shared secret in cleartext, so it is development-only by default. If you genuinely need it in a release build, set `allowInRelease: true` \u2014 and do not ship that to users.");
10046
+ return;
10047
+ }
10006
10048
  if (!isDevToolsBundled()) {
10007
10049
  logger.warn("[ScaleBun] debugConnection was configured, but the desktop-debugger tooling is not in this bundle. It is excluded by default (the ScaleBun desktop app is not yet released); opt in with `withScaleBun(config, { features: { devTools: true } })` in metro.config.js. All other SDK behaviour is unaffected.");
10008
10050
  return;
@@ -10097,7 +10139,7 @@ function enableDebug(config) {
10097
10139
  journeyEnabled: _journeyConfig.enabled,
10098
10140
  canScreenshot: true,
10099
10141
  navMode: _autoScreenDetector ? "auto" : "beacon",
10100
- sdkVersion: "1.0.0"
10142
+ sdkVersion: SDK_VERSION
10101
10143
  });
10102
10144
  __DEV__ && logger.debug("Emitted journey:debug_ready event to desktop");
10103
10145
  } catch {
@@ -10232,6 +10274,7 @@ var init_bootstrap = __esm({
10232
10274
  "use strict";
10233
10275
  init_transport();
10234
10276
  init_timings();
10277
+ init_version();
10235
10278
  init_stream();
10236
10279
  init_commands();
10237
10280
  init_internalLogger();
@@ -10511,89 +10554,6 @@ var init_nativeCrashBridge = __esm({
10511
10554
  }
10512
10555
  });
10513
10556
 
10514
- // lib/module/analytics/batching.js
10515
- function chunkEvents(events, size = MAX_BATCH_EVENTS) {
10516
- const cap = Math.max(1, Math.min(size, MAX_BATCH_EVENTS));
10517
- if (events.length <= cap) return events.length ? [events.slice()] : [];
10518
- const out = [];
10519
- for (let i = 0; i < events.length; i += cap) {
10520
- out.push(events.slice(i, i + cap));
10521
- }
10522
- return out;
10523
- }
10524
- function clampBatchSize(configured, fallback) {
10525
- const n = typeof configured === "number" && configured > 0 ? configured : fallback;
10526
- return Math.max(1, Math.min(n, MAX_BATCH_EVENTS));
10527
- }
10528
- function batchIdempotencyKey(eventIds) {
10529
- let h = 2166136261;
10530
- const joined = eventIds.join("");
10531
- for (let i = 0; i < joined.length; i++) {
10532
- h ^= joined.charCodeAt(i);
10533
- h = Math.imul(h, 16777619) >>> 0;
10534
- }
10535
- return `sdk-${eventIds.length}-${h.toString(16).padStart(8, "0")}`;
10536
- }
10537
- var MAX_BATCH_EVENTS;
10538
- var init_batching = __esm({
10539
- "lib/module/analytics/batching.js"() {
10540
- "use strict";
10541
- MAX_BATCH_EVENTS = 500;
10542
- }
10543
- });
10544
-
10545
- // lib/module/analytics/automaticEvents.js
10546
- function compactProperties(properties) {
10547
- const out = {
10548
- capture_source: "automatic"
10549
- };
10550
- for (const [key, value] of Object.entries(properties ?? {})) {
10551
- if (value !== void 0) out[key] = value;
10552
- }
10553
- return out;
10554
- }
10555
- function emitAutomaticEvent(name, properties) {
10556
- const event = {
10557
- name,
10558
- properties: compactProperties(properties)
10559
- };
10560
- if (listeners.size === 0) {
10561
- pending3.push(event);
10562
- if (pending3.length > MAX_PENDING2) pending3.shift();
10563
- return;
10564
- }
10565
- for (const listener of listeners) {
10566
- try {
10567
- listener(event);
10568
- } catch {
10569
- }
10570
- }
10571
- }
10572
- function subscribeAutomaticEvents(listener) {
10573
- listeners.add(listener);
10574
- if (pending3.length > 0) {
10575
- const buffered = pending3.splice(0, pending3.length);
10576
- for (const event of buffered) {
10577
- try {
10578
- listener(event);
10579
- } catch {
10580
- }
10581
- }
10582
- }
10583
- return () => {
10584
- listeners.delete(listener);
10585
- };
10586
- }
10587
- var listeners, pending3, MAX_PENDING2;
10588
- var init_automaticEvents = __esm({
10589
- "lib/module/analytics/automaticEvents.js"() {
10590
- "use strict";
10591
- listeners = /* @__PURE__ */ new Set();
10592
- pending3 = [];
10593
- MAX_PENDING2 = 50;
10594
- }
10595
- });
10596
-
10597
10557
  // lib/module/crypto/hmacSha256.js
10598
10558
  function utf8(str3) {
10599
10559
  const out = [];
@@ -10710,6 +10670,89 @@ var init_hmacSha256 = __esm({
10710
10670
  }
10711
10671
  });
10712
10672
 
10673
+ // lib/module/analytics/batching.js
10674
+ function chunkEvents(events, size = MAX_BATCH_EVENTS) {
10675
+ const cap = Math.max(1, Math.min(size, MAX_BATCH_EVENTS));
10676
+ if (events.length <= cap) return events.length ? [events.slice()] : [];
10677
+ const out = [];
10678
+ for (let i = 0; i < events.length; i += cap) {
10679
+ out.push(events.slice(i, i + cap));
10680
+ }
10681
+ return out;
10682
+ }
10683
+ function clampBatchSize(configured, fallback) {
10684
+ const n = typeof configured === "number" && configured > 0 ? configured : fallback;
10685
+ return Math.max(1, Math.min(n, MAX_BATCH_EVENTS));
10686
+ }
10687
+ function batchIdempotencyKey(eventIds) {
10688
+ let h = 2166136261;
10689
+ const joined = eventIds.join("");
10690
+ for (let i = 0; i < joined.length; i++) {
10691
+ h ^= joined.charCodeAt(i);
10692
+ h = Math.imul(h, 16777619) >>> 0;
10693
+ }
10694
+ return `sdk-${eventIds.length}-${h.toString(16).padStart(8, "0")}`;
10695
+ }
10696
+ var MAX_BATCH_EVENTS;
10697
+ var init_batching = __esm({
10698
+ "lib/module/analytics/batching.js"() {
10699
+ "use strict";
10700
+ MAX_BATCH_EVENTS = 500;
10701
+ }
10702
+ });
10703
+
10704
+ // lib/module/analytics/automaticEvents.js
10705
+ function compactProperties(properties) {
10706
+ const out = {
10707
+ capture_source: "automatic"
10708
+ };
10709
+ for (const [key, value] of Object.entries(properties ?? {})) {
10710
+ if (value !== void 0) out[key] = value;
10711
+ }
10712
+ return out;
10713
+ }
10714
+ function emitAutomaticEvent(name, properties) {
10715
+ const event = {
10716
+ name,
10717
+ properties: compactProperties(properties)
10718
+ };
10719
+ if (listeners.size === 0) {
10720
+ pending3.push(event);
10721
+ if (pending3.length > MAX_PENDING2) pending3.shift();
10722
+ return;
10723
+ }
10724
+ for (const listener of listeners) {
10725
+ try {
10726
+ listener(event);
10727
+ } catch {
10728
+ }
10729
+ }
10730
+ }
10731
+ function subscribeAutomaticEvents(listener) {
10732
+ listeners.add(listener);
10733
+ if (pending3.length > 0) {
10734
+ const buffered = pending3.splice(0, pending3.length);
10735
+ for (const event of buffered) {
10736
+ try {
10737
+ listener(event);
10738
+ } catch {
10739
+ }
10740
+ }
10741
+ }
10742
+ return () => {
10743
+ listeners.delete(listener);
10744
+ };
10745
+ }
10746
+ var listeners, pending3, MAX_PENDING2;
10747
+ var init_automaticEvents = __esm({
10748
+ "lib/module/analytics/automaticEvents.js"() {
10749
+ "use strict";
10750
+ listeners = /* @__PURE__ */ new Set();
10751
+ pending3 = [];
10752
+ MAX_PENDING2 = 50;
10753
+ }
10754
+ });
10755
+
10713
10756
  // lib/module/analytics/EventTracker.js
10714
10757
  function uuid() {
10715
10758
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
@@ -10723,7 +10766,9 @@ var init_EventTracker = __esm({
10723
10766
  "lib/module/analytics/EventTracker.js"() {
10724
10767
  "use strict";
10725
10768
  init_StorageBackend();
10769
+ init_device();
10726
10770
  init_deviceId();
10771
+ init_version();
10727
10772
  init_internalLogger();
10728
10773
  init_nativeCrashBridge();
10729
10774
  init_hmacSha256();
@@ -10752,7 +10797,7 @@ var init_EventTracker = __esm({
10752
10797
  constructor(config) {
10753
10798
  this.storage = createStorageBackend();
10754
10799
  this.cfg = {
10755
- platform: "react_native",
10800
+ platform: resolveEventPlatform(),
10756
10801
  flushIntervalMs: DEFAULT_FLUSH_MS,
10757
10802
  batchSize: DEFAULT_BATCH,
10758
10803
  autoLifecycleEvents: true,
@@ -10978,7 +11023,7 @@ var init_EventTracker = __esm({
10978
11023
  event_name: eventName,
10979
11024
  event_time: Date.now(),
10980
11025
  app_id: this.cfg.appId,
10981
- platform: this.cfg.platform ?? "react_native",
11026
+ platform: this.cfg.platform ?? resolveEventPlatform(),
10982
11027
  installation_id: this.installationId,
10983
11028
  anonymous_id: this.anonymousId,
10984
11029
  session_id: canonicalSessionId || this.sessionId,
@@ -10989,7 +11034,7 @@ var init_EventTracker = __esm({
10989
11034
  ...properties ?? {}
10990
11035
  },
10991
11036
  context: {
10992
- sdk_version: ctx.sdk_version ?? "0.1.0",
11037
+ sdk_version: ctx.sdk_version ?? SDK_VERSION,
10993
11038
  ...ctx
10994
11039
  }
10995
11040
  };
@@ -11073,6 +11118,38 @@ var init_EventTracker = __esm({
11073
11118
  }
11074
11119
  });
11075
11120
 
11121
+ // lib/module/core/id/installationId.js
11122
+ function resolveInstallationId() {
11123
+ try {
11124
+ const storage3 = createStorageBackend();
11125
+ const existing = storage3.get(K_INSTALLATION_ID);
11126
+ if (existing) return existing;
11127
+ const id = `inst-${randomSegment()}${randomSegment()}`;
11128
+ storage3.set(K_INSTALLATION_ID, id);
11129
+ return id;
11130
+ } catch {
11131
+ return `inst-ephemeral-${randomSegment()}`;
11132
+ }
11133
+ }
11134
+ function randomSegment() {
11135
+ try {
11136
+ const g = globalThis;
11137
+ if (g.crypto?.getRandomValues) {
11138
+ const bytes = g.crypto.getRandomValues(new Uint8Array(6));
11139
+ return Array.from(bytes).map((b) => b.toString(36).padStart(2, "0")).join("").slice(0, 8);
11140
+ }
11141
+ } catch {
11142
+ }
11143
+ return Math.random().toString(36).slice(2, 10).padEnd(8, "0");
11144
+ }
11145
+ var init_installationId = __esm({
11146
+ "lib/module/core/id/installationId.js"() {
11147
+ "use strict";
11148
+ init_StorageBackend();
11149
+ init_EventTracker();
11150
+ }
11151
+ });
11152
+
11076
11153
  // lib/module/features/engage/engageSignals.js
11077
11154
  var EngageSignalBus, engageSignals;
11078
11155
  var init_engageSignals = __esm({
@@ -11532,18 +11609,6 @@ function subscribeNativeProgress(onTick) {
11532
11609
  };
11533
11610
  }
11534
11611
  }
11535
- function resolveInstallationId() {
11536
- try {
11537
- const storage3 = createStorageBackend();
11538
- const existing = storage3.get(K_INSTALLATION_ID);
11539
- if (existing) return existing;
11540
- const id = `inst-${Math.random().toString(36).slice(2, 10)}${Math.random().toString(36).slice(2, 10)}`;
11541
- storage3.set(K_INSTALLATION_ID, id);
11542
- return id;
11543
- } catch {
11544
- return `inst-ephemeral-${Math.random().toString(36).slice(2, 10)}`;
11545
- }
11546
- }
11547
11612
  async function deliverOtaEvents(params) {
11548
11613
  const events = otaEventEmitter.flush();
11549
11614
  if (!events.length) return;
@@ -11593,7 +11658,7 @@ var init_OtaOrchestrator = __esm({
11593
11658
  init_OtaEventEmitter();
11594
11659
  init_geoCountry();
11595
11660
  init_deviceAttributes();
11596
- init_EventTracker();
11661
+ init_installationId();
11597
11662
  init_signature();
11598
11663
  init_environment();
11599
11664
  init_retry();
@@ -12715,6 +12780,7 @@ var init_journeyManager = __esm({
12715
12780
  "use strict";
12716
12781
  init_journeyTypes();
12717
12782
  init_gestureBuffer();
12783
+ init_version();
12718
12784
  init_gestureDetector();
12719
12785
  init_navDetector();
12720
12786
  init_screenshotHelper();
@@ -13071,7 +13137,7 @@ var init_journeyManager = __esm({
13071
13137
  journeyEnabled: this.config.enabled,
13072
13138
  canScreenshot: this.screenshotHelper.canCapture,
13073
13139
  navMode: this.navDetector.navMode,
13074
- sdkVersion: "0.1.0"
13140
+ sdkVersion: SDK_VERSION
13075
13141
  };
13076
13142
  this._emitEvent(JOURNEY_EVENTS.DEBUG_READY, payload);
13077
13143
  }
@@ -15332,6 +15398,7 @@ function decodeManual(base64) {
15332
15398
  }
15333
15399
 
15334
15400
  // lib/module/features/session/BackendSessionAdapter.js
15401
+ init_installationId();
15335
15402
  init_version();
15336
15403
  init_bridgeAdapter();
15337
15404
  init_nativeModule();
@@ -15467,6 +15534,13 @@ var BackendSessionAdapter = class _BackendSessionAdapter {
15467
15534
  const payload = {
15468
15535
  sessionId: session.sessionId,
15469
15536
  deviceId: session.deviceId,
15537
+ // The canonical per-install id — the SAME value OTA events are
15538
+ // reported under, and a DIFFERENT value from deviceId. The
15539
+ // backend stores it on the Device so release health can join a
15540
+ // bundle's installs to that device's sessions. Without it, crash
15541
+ // impact for a release resolves to "no data" for every device
15542
+ // that never registered for push.
15543
+ installationId: resolveInstallationId(),
15470
15544
  startedAt: session.startedAt,
15471
15545
  // Link the recording row to the always-on analytics row so the
15472
15546
  // dashboard can join them. track() events land on the analytics
@@ -17014,6 +17088,10 @@ function isSessionLaneEvent(name) {
17014
17088
  function resolveEventLane(name) {
17015
17089
  return isSessionLaneEvent(name) ? "session" : "analytics";
17016
17090
  }
17091
+ var INTERNAL_ONLY_SIGNALS = /* @__PURE__ */ new Set(["$network_request"]);
17092
+ function isUploadedSessionEvent(name) {
17093
+ return !INTERNAL_ONLY_SIGNALS.has(name);
17094
+ }
17017
17095
 
17018
17096
  // lib/module/config/ConfigManager.js
17019
17097
  init_StorageBackend();
@@ -19498,7 +19576,8 @@ var ScaleBunFacade = class {
19498
19576
  appId,
19499
19577
  clientKey,
19500
19578
  apiBaseUrl,
19501
- platform: rawConfig?.platform ?? "react_native",
19579
+ // Auto-detected (Platform.OS) unless the integration overrides it — see resolveEventPlatform.
19580
+ platform: rawConfig?.platform ?? resolveEventPlatform(),
19502
19581
  appVersion: rawConfig?.appVersion,
19503
19582
  osVersion: rawConfig?.osVersion,
19504
19583
  autoLifecycleEvents: rawConfig?.autoLifecycleEvents !== false,
@@ -20308,7 +20387,8 @@ var ScaleBunFacade = class {
20308
20387
  __DEV__ && logger.debug("Tracking event:", name);
20309
20388
  engageSignals.emit(name, payload);
20310
20389
  const adapter = SessionManager.getExistingInstance()?.getBackendTransport();
20311
- if (this._clientKey && adapter) {
20390
+ if (!isUploadedSessionEvent(name)) {
20391
+ } else if (this._clientKey && adapter) {
20312
20392
  adapter.trackEvent({
20313
20393
  eventId: generateEventId(),
20314
20394
  sessionId: adapter.analyticsSessionId ?? "",