@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
@@ -492,7 +492,7 @@ var SDK_VERSION;
492
492
  var init_version = __esm({
493
493
  "lib/module/core/constants/version.js"() {
494
494
  "use strict";
495
- SDK_VERSION = "1.3.3";
495
+ SDK_VERSION = "1.5.0";
496
496
  }
497
497
  });
498
498
 
@@ -542,6 +542,10 @@ function mergeDeviceContext(configured, detected = staticDeviceContext()) {
542
542
  ...detected
543
543
  };
544
544
  }
545
+ function resolveEventPlatform() {
546
+ const os = resolvePlatformOS();
547
+ return os === "ios" || os === "android" || os === "web" ? os : "react_native";
548
+ }
545
549
  var init_device = __esm({
546
550
  "lib/module/core/context/device.js"() {
547
551
  "use strict";
@@ -5327,6 +5331,10 @@ function enableDebug(config) {
5327
5331
  __DEV__ && logger.debug("Debug already enabled, skipping");
5328
5332
  return;
5329
5333
  }
5334
+ if (!__DEV__ && !config.allowInRelease) {
5335
+ 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.");
5336
+ return;
5337
+ }
5330
5338
  if (!isDevToolsBundled()) {
5331
5339
  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.");
5332
5340
  return;
@@ -5421,7 +5429,7 @@ function enableDebug(config) {
5421
5429
  journeyEnabled: _journeyConfig.enabled,
5422
5430
  canScreenshot: true,
5423
5431
  navMode: _autoScreenDetector ? "auto" : "beacon",
5424
- sdkVersion: "1.0.0"
5432
+ sdkVersion: SDK_VERSION
5425
5433
  });
5426
5434
  __DEV__ && logger.debug("Emitted journey:debug_ready event to desktop");
5427
5435
  } catch {
@@ -5556,6 +5564,7 @@ var init_bootstrap = __esm({
5556
5564
  "use strict";
5557
5565
  import_transport = __toESM(require_devtools_stub());
5558
5566
  init_timings();
5567
+ init_version();
5559
5568
  import_stream = __toESM(require_devtools_stub());
5560
5569
  import_commands = __toESM(require_devtools_stub());
5561
5570
  init_internalLogger();
@@ -5849,89 +5858,6 @@ var init_NativeScaleBunPerformance = __esm({
5849
5858
  }
5850
5859
  });
5851
5860
 
5852
- // lib/module/analytics/batching.js
5853
- function chunkEvents(events, size = MAX_BATCH_EVENTS) {
5854
- const cap = Math.max(1, Math.min(size, MAX_BATCH_EVENTS));
5855
- if (events.length <= cap) return events.length ? [events.slice()] : [];
5856
- const out = [];
5857
- for (let i = 0; i < events.length; i += cap) {
5858
- out.push(events.slice(i, i + cap));
5859
- }
5860
- return out;
5861
- }
5862
- function clampBatchSize(configured, fallback) {
5863
- const n = typeof configured === "number" && configured > 0 ? configured : fallback;
5864
- return Math.max(1, Math.min(n, MAX_BATCH_EVENTS));
5865
- }
5866
- function batchIdempotencyKey(eventIds) {
5867
- let h = 2166136261;
5868
- const joined = eventIds.join("");
5869
- for (let i = 0; i < joined.length; i++) {
5870
- h ^= joined.charCodeAt(i);
5871
- h = Math.imul(h, 16777619) >>> 0;
5872
- }
5873
- return `sdk-${eventIds.length}-${h.toString(16).padStart(8, "0")}`;
5874
- }
5875
- var MAX_BATCH_EVENTS;
5876
- var init_batching = __esm({
5877
- "lib/module/analytics/batching.js"() {
5878
- "use strict";
5879
- MAX_BATCH_EVENTS = 500;
5880
- }
5881
- });
5882
-
5883
- // lib/module/analytics/automaticEvents.js
5884
- function compactProperties(properties) {
5885
- const out = {
5886
- capture_source: "automatic"
5887
- };
5888
- for (const [key, value] of Object.entries(properties ?? {})) {
5889
- if (value !== void 0) out[key] = value;
5890
- }
5891
- return out;
5892
- }
5893
- function emitAutomaticEvent(name, properties) {
5894
- const event = {
5895
- name,
5896
- properties: compactProperties(properties)
5897
- };
5898
- if (listeners.size === 0) {
5899
- pending3.push(event);
5900
- if (pending3.length > MAX_PENDING2) pending3.shift();
5901
- return;
5902
- }
5903
- for (const listener of listeners) {
5904
- try {
5905
- listener(event);
5906
- } catch {
5907
- }
5908
- }
5909
- }
5910
- function subscribeAutomaticEvents(listener) {
5911
- listeners.add(listener);
5912
- if (pending3.length > 0) {
5913
- const buffered = pending3.splice(0, pending3.length);
5914
- for (const event of buffered) {
5915
- try {
5916
- listener(event);
5917
- } catch {
5918
- }
5919
- }
5920
- }
5921
- return () => {
5922
- listeners.delete(listener);
5923
- };
5924
- }
5925
- var listeners, pending3, MAX_PENDING2;
5926
- var init_automaticEvents = __esm({
5927
- "lib/module/analytics/automaticEvents.js"() {
5928
- "use strict";
5929
- listeners = /* @__PURE__ */ new Set();
5930
- pending3 = [];
5931
- MAX_PENDING2 = 50;
5932
- }
5933
- });
5934
-
5935
5861
  // lib/module/crypto/hmacSha256.js
5936
5862
  function utf8(str3) {
5937
5863
  const out = [];
@@ -6048,6 +5974,89 @@ var init_hmacSha256 = __esm({
6048
5974
  }
6049
5975
  });
6050
5976
 
5977
+ // lib/module/analytics/batching.js
5978
+ function chunkEvents(events, size = MAX_BATCH_EVENTS) {
5979
+ const cap = Math.max(1, Math.min(size, MAX_BATCH_EVENTS));
5980
+ if (events.length <= cap) return events.length ? [events.slice()] : [];
5981
+ const out = [];
5982
+ for (let i = 0; i < events.length; i += cap) {
5983
+ out.push(events.slice(i, i + cap));
5984
+ }
5985
+ return out;
5986
+ }
5987
+ function clampBatchSize(configured, fallback) {
5988
+ const n = typeof configured === "number" && configured > 0 ? configured : fallback;
5989
+ return Math.max(1, Math.min(n, MAX_BATCH_EVENTS));
5990
+ }
5991
+ function batchIdempotencyKey(eventIds) {
5992
+ let h = 2166136261;
5993
+ const joined = eventIds.join("");
5994
+ for (let i = 0; i < joined.length; i++) {
5995
+ h ^= joined.charCodeAt(i);
5996
+ h = Math.imul(h, 16777619) >>> 0;
5997
+ }
5998
+ return `sdk-${eventIds.length}-${h.toString(16).padStart(8, "0")}`;
5999
+ }
6000
+ var MAX_BATCH_EVENTS;
6001
+ var init_batching = __esm({
6002
+ "lib/module/analytics/batching.js"() {
6003
+ "use strict";
6004
+ MAX_BATCH_EVENTS = 500;
6005
+ }
6006
+ });
6007
+
6008
+ // lib/module/analytics/automaticEvents.js
6009
+ function compactProperties(properties) {
6010
+ const out = {
6011
+ capture_source: "automatic"
6012
+ };
6013
+ for (const [key, value] of Object.entries(properties ?? {})) {
6014
+ if (value !== void 0) out[key] = value;
6015
+ }
6016
+ return out;
6017
+ }
6018
+ function emitAutomaticEvent(name, properties) {
6019
+ const event = {
6020
+ name,
6021
+ properties: compactProperties(properties)
6022
+ };
6023
+ if (listeners.size === 0) {
6024
+ pending3.push(event);
6025
+ if (pending3.length > MAX_PENDING2) pending3.shift();
6026
+ return;
6027
+ }
6028
+ for (const listener of listeners) {
6029
+ try {
6030
+ listener(event);
6031
+ } catch {
6032
+ }
6033
+ }
6034
+ }
6035
+ function subscribeAutomaticEvents(listener) {
6036
+ listeners.add(listener);
6037
+ if (pending3.length > 0) {
6038
+ const buffered = pending3.splice(0, pending3.length);
6039
+ for (const event of buffered) {
6040
+ try {
6041
+ listener(event);
6042
+ } catch {
6043
+ }
6044
+ }
6045
+ }
6046
+ return () => {
6047
+ listeners.delete(listener);
6048
+ };
6049
+ }
6050
+ var listeners, pending3, MAX_PENDING2;
6051
+ var init_automaticEvents = __esm({
6052
+ "lib/module/analytics/automaticEvents.js"() {
6053
+ "use strict";
6054
+ listeners = /* @__PURE__ */ new Set();
6055
+ pending3 = [];
6056
+ MAX_PENDING2 = 50;
6057
+ }
6058
+ });
6059
+
6051
6060
  // lib/module/analytics/EventTracker.js
6052
6061
  function uuid() {
6053
6062
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
@@ -6061,7 +6070,9 @@ var init_EventTracker = __esm({
6061
6070
  "lib/module/analytics/EventTracker.js"() {
6062
6071
  "use strict";
6063
6072
  init_StorageBackend();
6073
+ init_device();
6064
6074
  init_deviceId();
6075
+ init_version();
6065
6076
  init_internalLogger();
6066
6077
  init_nativeCrashBridge();
6067
6078
  init_hmacSha256();
@@ -6090,7 +6101,7 @@ var init_EventTracker = __esm({
6090
6101
  constructor(config) {
6091
6102
  this.storage = createStorageBackend();
6092
6103
  this.cfg = {
6093
- platform: "react_native",
6104
+ platform: resolveEventPlatform(),
6094
6105
  flushIntervalMs: DEFAULT_FLUSH_MS,
6095
6106
  batchSize: DEFAULT_BATCH,
6096
6107
  autoLifecycleEvents: true,
@@ -6316,7 +6327,7 @@ var init_EventTracker = __esm({
6316
6327
  event_name: eventName,
6317
6328
  event_time: Date.now(),
6318
6329
  app_id: this.cfg.appId,
6319
- platform: this.cfg.platform ?? "react_native",
6330
+ platform: this.cfg.platform ?? resolveEventPlatform(),
6320
6331
  installation_id: this.installationId,
6321
6332
  anonymous_id: this.anonymousId,
6322
6333
  session_id: canonicalSessionId || this.sessionId,
@@ -6327,7 +6338,7 @@ var init_EventTracker = __esm({
6327
6338
  ...properties ?? {}
6328
6339
  },
6329
6340
  context: {
6330
- sdk_version: ctx.sdk_version ?? "0.1.0",
6341
+ sdk_version: ctx.sdk_version ?? SDK_VERSION,
6331
6342
  ...ctx
6332
6343
  }
6333
6344
  };
@@ -6411,6 +6422,38 @@ var init_EventTracker = __esm({
6411
6422
  }
6412
6423
  });
6413
6424
 
6425
+ // lib/module/core/id/installationId.js
6426
+ function resolveInstallationId() {
6427
+ try {
6428
+ const storage3 = createStorageBackend();
6429
+ const existing = storage3.get(K_INSTALLATION_ID);
6430
+ if (existing) return existing;
6431
+ const id = `inst-${randomSegment()}${randomSegment()}`;
6432
+ storage3.set(K_INSTALLATION_ID, id);
6433
+ return id;
6434
+ } catch {
6435
+ return `inst-ephemeral-${randomSegment()}`;
6436
+ }
6437
+ }
6438
+ function randomSegment() {
6439
+ try {
6440
+ const g = globalThis;
6441
+ if (g.crypto?.getRandomValues) {
6442
+ const bytes = g.crypto.getRandomValues(new Uint8Array(6));
6443
+ return Array.from(bytes).map((b) => b.toString(36).padStart(2, "0")).join("").slice(0, 8);
6444
+ }
6445
+ } catch {
6446
+ }
6447
+ return Math.random().toString(36).slice(2, 10).padEnd(8, "0");
6448
+ }
6449
+ var init_installationId = __esm({
6450
+ "lib/module/core/id/installationId.js"() {
6451
+ "use strict";
6452
+ init_StorageBackend();
6453
+ init_EventTracker();
6454
+ }
6455
+ });
6456
+
6414
6457
  // lib/module/features/engage/engageSignals.js
6415
6458
  var EngageSignalBus, engageSignals;
6416
6459
  var init_engageSignals = __esm({
@@ -6870,18 +6913,6 @@ function subscribeNativeProgress(onTick) {
6870
6913
  };
6871
6914
  }
6872
6915
  }
6873
- function resolveInstallationId() {
6874
- try {
6875
- const storage3 = createStorageBackend();
6876
- const existing = storage3.get(K_INSTALLATION_ID);
6877
- if (existing) return existing;
6878
- const id = `inst-${Math.random().toString(36).slice(2, 10)}${Math.random().toString(36).slice(2, 10)}`;
6879
- storage3.set(K_INSTALLATION_ID, id);
6880
- return id;
6881
- } catch {
6882
- return `inst-ephemeral-${Math.random().toString(36).slice(2, 10)}`;
6883
- }
6884
- }
6885
6916
  async function deliverOtaEvents(params) {
6886
6917
  const events = otaEventEmitter.flush();
6887
6918
  if (!events.length) return;
@@ -6931,7 +6962,7 @@ var init_OtaOrchestrator = __esm({
6931
6962
  init_OtaEventEmitter();
6932
6963
  init_geoCountry();
6933
6964
  init_deviceAttributes();
6934
- init_EventTracker();
6965
+ init_installationId();
6935
6966
  init_signature();
6936
6967
  init_environment();
6937
6968
  init_retry();
@@ -8053,6 +8084,7 @@ var init_journeyManager = __esm({
8053
8084
  "use strict";
8054
8085
  init_journeyTypes();
8055
8086
  init_gestureBuffer();
8087
+ init_version();
8056
8088
  init_gestureDetector();
8057
8089
  init_navDetector();
8058
8090
  init_screenshotHelper();
@@ -8409,7 +8441,7 @@ var init_journeyManager = __esm({
8409
8441
  journeyEnabled: this.config.enabled,
8410
8442
  canScreenshot: this.screenshotHelper.canCapture,
8411
8443
  navMode: this.navDetector.navMode,
8412
- sdkVersion: "0.1.0"
8444
+ sdkVersion: SDK_VERSION
8413
8445
  };
8414
8446
  this._emitEvent(JOURNEY_EVENTS.DEBUG_READY, payload);
8415
8447
  }
@@ -10491,7 +10523,7 @@ var PERF_SCHEMA_VERSION = 1;
10491
10523
 
10492
10524
  // lib/module/features/performance/collectors/JsStallCollector.js
10493
10525
  var MAX_STALL_RECORDS = 100;
10494
- var JsStallCollector = class {
10526
+ var JsStallCollector = class _JsStallCollector {
10495
10527
  pollTimer = null;
10496
10528
  reportTimer = null;
10497
10529
  lastTick = 0;
@@ -10501,6 +10533,11 @@ var JsStallCollector = class {
10501
10533
  maxStallMs = 0;
10502
10534
  totalStallMs = 0;
10503
10535
  recentStalls = [];
10536
+ /** Per-run caps on INDIVIDUAL stall events — see the emit site. Summaries are uncapped. */
10537
+ static EMITTED_STALLS_MAX = 200;
10538
+ static CRITICAL_OVERFLOW_MAX = 100;
10539
+ emittedStalls = 0;
10540
+ emittedCriticalOverflow = 0;
10504
10541
  constructor(config, onStall, onSummary, contextProvider) {
10505
10542
  this.config = config;
10506
10543
  this.onStall = onStall;
@@ -10561,6 +10598,13 @@ var JsStallCollector = class {
10561
10598
  }
10562
10599
  if (durationMs > 100) {
10563
10600
  const severity = this._classifySeverity(durationMs);
10601
+ if (this.emittedStalls >= _JsStallCollector.EMITTED_STALLS_MAX) {
10602
+ if (severity !== "critical") return;
10603
+ if (this.emittedCriticalOverflow >= _JsStallCollector.CRITICAL_OVERFLOW_MAX) return;
10604
+ this.emittedCriticalOverflow++;
10605
+ } else {
10606
+ this.emittedStalls++;
10607
+ }
10564
10608
  const event = {
10565
10609
  id: `stall-${timestamp}-${Math.random().toString(36).substring(2, 8)}`,
10566
10610
  type: "js_stall",
@@ -10948,13 +10992,27 @@ var AutoScreenLoadCollector = class {
10948
10992
 
10949
10993
  // lib/module/features/performance/collectors/FrameMetricsCollector.js
10950
10994
  var import_react_native12 = require("react-native");
10951
- var FrameMetricsCollector = class {
10995
+ var FrameMetricsCollector = class _FrameMetricsCollector {
10952
10996
  eventSubscription = null;
10953
10997
  running = false;
10954
10998
  /** AppState subscription — see `_installAppStateGate`. */
10955
10999
  appStateSubscription = null;
10956
11000
  /** True while the native sampler is actually running (i.e. armed AND foregrounded). */
10957
11001
  sampling = false;
11002
+ /** Timestamp of the last emitted window — drives the healthy-window heartbeat below. */
11003
+ lastEmitTs = 0;
11004
+ /**
11005
+ * HEALTHY windows emit at a heartbeat, not per report interval.
11006
+ *
11007
+ * The native sampler reports every `reportIntervalMs` (5 s), and every window became a stored
11008
+ * row — on a session hours long that is 720 rows/hour of "still 60 fps", which is how real
11009
+ * sessions hit the dashboard's 2,000-row display cap with nothing to say. A window that shows
11010
+ * ANY jank (dropped/frozen frames, meaningful slow-frame count, an FPS dip) always emits; a
11011
+ * healthy window emits only when the heartbeat has elapsed, keeping the FPS series alive for
11012
+ * the charts at 1/12th the volume. Capture cadence is unchanged — only identical-looking
11013
+ * healthy rows are elided, and the first window after ANY emit gap states the recovery.
11014
+ */
11015
+ static HEALTHY_HEARTBEAT_MS = 6e4;
10958
11016
  constructor(config, onFrameMetrics, contextProvider) {
10959
11017
  this.config = config;
10960
11018
  this.onFrameMetrics = onFrameMetrics;
@@ -11057,10 +11115,17 @@ var FrameMetricsCollector = class {
11057
11115
  const expectedFrames = Math.round(windowMs / 16.67);
11058
11116
  const actualFrames = Math.max(0, totalFrames - droppedFrames);
11059
11117
  const estimatedFps = expectedFrames > 0 ? Math.round(actualFrames / expectedFrames * 60) : 60;
11118
+ const clampedFps = Math.min(60, Math.max(0, estimatedFps));
11119
+ const now2 = Date.now();
11120
+ const healthy = droppedFrames === 0 && frozenFrames === 0 && slowFrames <= 2 && clampedFps >= 55;
11121
+ if (healthy && this.lastEmitTs !== 0 && now2 - this.lastEmitTs < _FrameMetricsCollector.HEALTHY_HEARTBEAT_MS) {
11122
+ return;
11123
+ }
11124
+ this.lastEmitTs = now2;
11060
11125
  const event = {
11061
- id: `frames-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`,
11126
+ id: `frames-${now2}-${Math.random().toString(36).substring(2, 8)}`,
11062
11127
  type: "frame_metrics",
11063
- timestamp: Date.now(),
11128
+ timestamp: now2,
11064
11129
  schemaVersion: PERF_SCHEMA_VERSION,
11065
11130
  context: this.contextProvider(),
11066
11131
  windowMs,
@@ -11068,7 +11133,7 @@ var FrameMetricsCollector = class {
11068
11133
  droppedFrames,
11069
11134
  slowFrames,
11070
11135
  frozenFrames,
11071
- estimatedFps: Math.min(60, Math.max(0, estimatedFps)),
11136
+ estimatedFps: clampedFps,
11072
11137
  maxFrameTimeMs
11073
11138
  };
11074
11139
  this.onFrameMetrics(event);
@@ -12131,6 +12196,7 @@ function decodeManual(base64) {
12131
12196
  }
12132
12197
 
12133
12198
  // lib/module/features/session/BackendSessionAdapter.js
12199
+ init_installationId();
12134
12200
  init_version();
12135
12201
  init_bridgeAdapter();
12136
12202
  init_nativeModule();
@@ -12266,6 +12332,13 @@ var BackendSessionAdapter = class _BackendSessionAdapter {
12266
12332
  const payload = {
12267
12333
  sessionId: session.sessionId,
12268
12334
  deviceId: session.deviceId,
12335
+ // The canonical per-install id — the SAME value OTA events are
12336
+ // reported under, and a DIFFERENT value from deviceId. The
12337
+ // backend stores it on the Device so release health can join a
12338
+ // bundle's installs to that device's sessions. Without it, crash
12339
+ // impact for a release resolves to "no data" for every device
12340
+ // that never registered for push.
12341
+ installationId: resolveInstallationId(),
12269
12342
  startedAt: session.startedAt,
12270
12343
  // Link the recording row to the always-on analytics row so the
12271
12344
  // dashboard can join them. track() events land on the analytics
@@ -13813,6 +13886,10 @@ function isSessionLaneEvent(name) {
13813
13886
  function resolveEventLane(name) {
13814
13887
  return isSessionLaneEvent(name) ? "session" : "analytics";
13815
13888
  }
13889
+ var INTERNAL_ONLY_SIGNALS = /* @__PURE__ */ new Set(["$network_request"]);
13890
+ function isUploadedSessionEvent(name) {
13891
+ return !INTERNAL_ONLY_SIGNALS.has(name);
13892
+ }
13816
13893
 
13817
13894
  // lib/module/config/ConfigManager.js
13818
13895
  init_StorageBackend();
@@ -16296,7 +16373,8 @@ var ScaleBunFacade = class {
16296
16373
  appId,
16297
16374
  clientKey,
16298
16375
  apiBaseUrl,
16299
- platform: rawConfig?.platform ?? "react_native",
16376
+ // Auto-detected (Platform.OS) unless the integration overrides it — see resolveEventPlatform.
16377
+ platform: rawConfig?.platform ?? resolveEventPlatform(),
16300
16378
  appVersion: rawConfig?.appVersion,
16301
16379
  osVersion: rawConfig?.osVersion,
16302
16380
  autoLifecycleEvents: rawConfig?.autoLifecycleEvents !== false,
@@ -17106,7 +17184,8 @@ var ScaleBunFacade = class {
17106
17184
  __DEV__ && logger.debug("Tracking event:", name);
17107
17185
  engageSignals.emit(name, payload);
17108
17186
  const adapter = SessionManager.getExistingInstance()?.getBackendTransport();
17109
- if (this._clientKey && adapter) {
17187
+ if (!isUploadedSessionEvent(name)) {
17188
+ } else if (this._clientKey && adapter) {
17110
17189
  adapter.trackEvent({
17111
17190
  eventId: generateEventId(),
17112
17191
  sessionId: adapter.analyticsSessionId ?? "",