@scalebun/react-native 1.2.2 → 1.3.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 (32) hide show
  1. package/android/src/main/java/com/scalebun/replaysdk/ReplaySdkModule.kt +25 -0
  2. package/android/src/main/java/com/scalebun/replaysdk/session/SessionManager.kt +41 -0
  3. package/dist/scalebun.full.js +35 -1
  4. package/dist/scalebun.full.js.map +1 -1
  5. package/dist/scalebun.slim.js +35 -1
  6. package/dist/scalebun.slim.js.map +1 -1
  7. package/ios/ReplaySdk.swift +13 -0
  8. package/ios/Session/SessionManager.swift +43 -0
  9. package/lib/commonjs/bootstrap/SDKBootstrapper.js +12 -0
  10. package/lib/commonjs/bootstrap/SDKBootstrapper.js.map +1 -1
  11. package/lib/commonjs/core/config/schema.js +6 -0
  12. package/lib/commonjs/core/config/schema.js.map +1 -1
  13. package/lib/commonjs/public/ScaleBunFacade.js +21 -1
  14. package/lib/commonjs/public/ScaleBunFacade.js.map +1 -1
  15. package/lib/module/bootstrap/SDKBootstrapper.js +12 -0
  16. package/lib/module/bootstrap/SDKBootstrapper.js.map +1 -1
  17. package/lib/module/core/config/schema.js +6 -0
  18. package/lib/module/core/config/schema.js.map +1 -1
  19. package/lib/module/public/ScaleBunFacade.js +21 -1
  20. package/lib/module/public/ScaleBunFacade.js.map +1 -1
  21. package/lib/typescript/bootstrap/SDKBootstrapper.d.ts.map +1 -1
  22. package/lib/typescript/core/config/schema.d.ts +2 -0
  23. package/lib/typescript/core/config/schema.d.ts.map +1 -1
  24. package/lib/typescript/public/ScaleBunFacade.d.ts +8 -1
  25. package/lib/typescript/public/ScaleBunFacade.d.ts.map +1 -1
  26. package/lib/typescript/public/types.d.ts +12 -0
  27. package/lib/typescript/public/types.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/bootstrap/SDKBootstrapper.ts +12 -0
  30. package/src/core/config/schema.ts +8 -0
  31. package/src/public/ScaleBunFacade.ts +19 -1
  32. package/src/public/types.ts +13 -0
@@ -611,6 +611,19 @@ class ReplaySdkModule(reactContext: ReactApplicationContext) : ReplaySdkSpec(rea
611
611
  @ReactMethod
612
612
  override fun setPrivacyOptions(options: ReadableMap, promise: Promise) {
613
613
  try {
614
+ // Was a no-op stub: the promise resolved but nothing propagated, so
615
+ // `setPrivacy({ disableScreenshots })` did nothing at runtime on Android and only a
616
+ // restart (which re-reads the init config) honoured it. Now each supplied field is
617
+ // merged onto the running config via SessionManager.updatePrivacy, which routes through
618
+ // updateConfig → the capture manager, so the next frame respects it. Absent keys are
619
+ // left null → unchanged (a partial setPrivacy call must not reset the others).
620
+ sessionManager?.updatePrivacy(
621
+ disableScreenshots = if (options.hasKey("disableScreenshots")) options.getBoolean("disableScreenshots") else null,
622
+ maskTextInputs = if (options.hasKey("maskTextInputs")) options.getBoolean("maskTextInputs") else null,
623
+ maskImages = if (options.hasKey("maskImages")) options.getBoolean("maskImages") else null,
624
+ ignoredScreens = if (options.hasKey("ignoredScreens")) readStringList(options.getArray("ignoredScreens")) else null,
625
+ maskedElementIds = if (options.hasKey("maskedElementIds")) readStringList(options.getArray("maskedElementIds")) else null,
626
+ )
614
627
  promise.resolve(null)
615
628
  } catch (e: Exception) {
616
629
  promise.reject("PRIVACY_ERROR", e.message, e)
@@ -620,6 +633,7 @@ class ReplaySdkModule(reactContext: ReactApplicationContext) : ReplaySdkSpec(rea
620
633
  @ReactMethod
621
634
  override fun maskElement(elementId: String, promise: Promise) {
622
635
  try {
636
+ sessionManager?.addMaskedElement(elementId)
623
637
  promise.resolve(null)
624
638
  } catch (e: Exception) {
625
639
  promise.reject("PRIVACY_ERROR", e.message, e)
@@ -629,12 +643,23 @@ class ReplaySdkModule(reactContext: ReactApplicationContext) : ReplaySdkSpec(rea
629
643
  @ReactMethod
630
644
  override fun unmaskElement(elementId: String, promise: Promise) {
631
645
  try {
646
+ sessionManager?.removeMaskedElement(elementId)
632
647
  promise.resolve(null)
633
648
  } catch (e: Exception) {
634
649
  promise.reject("PRIVACY_ERROR", e.message, e)
635
650
  }
636
651
  }
637
652
 
653
+ /** ReadableArray → List<String>, nulls and non-strings dropped. */
654
+ private fun readStringList(arr: ReadableArray?): List<String> {
655
+ if (arr == null) return emptyList()
656
+ val out = ArrayList<String>(arr.size())
657
+ for (i in 0 until arr.size()) {
658
+ arr.getString(i)?.let { out.add(it) }
659
+ }
660
+ return out
661
+ }
662
+
638
663
  // ─── User ───────────────────────────────────────────────────────────
639
664
 
640
665
  @ReactMethod
@@ -226,6 +226,47 @@ class SessionManager(
226
226
  ReplayLogger.verbose = newConfig.verboseLogging
227
227
  }
228
228
 
229
+ /**
230
+ * Runtime privacy update — the missing half of `ScaleBun.replay.setPrivacy(...)`.
231
+ *
232
+ * `captureFrame` gates every capture on `config.disableScreenshots` / `config.ignoredScreens`
233
+ * and passes `config` (with `maskedElementIds`) into `scheduleCapture`, all reading THIS
234
+ * object's `config`. So merging the new privacy fields onto the current config and routing
235
+ * through `updateConfig` makes a runtime toggle take effect on the very next frame — no
236
+ * restart. MERGE, not replace: only the fields the caller supplied change; a null argument
237
+ * leaves that field as it was (the native `setPrivacyOptions` was a no-op stub before this,
238
+ * so the toggle silently did nothing on Android).
239
+ */
240
+ fun updatePrivacy(
241
+ disableScreenshots: Boolean? = null,
242
+ maskTextInputs: Boolean? = null,
243
+ maskImages: Boolean? = null,
244
+ ignoredScreens: List<String>? = null,
245
+ maskedElementIds: List<String>? = null,
246
+ ) {
247
+ updateConfig(
248
+ config.copy(
249
+ disableScreenshots = disableScreenshots ?: config.disableScreenshots,
250
+ maskTextInputs = maskTextInputs ?: config.maskTextInputs,
251
+ maskImages = maskImages ?: config.maskImages,
252
+ ignoredScreens = ignoredScreens ?: config.ignoredScreens,
253
+ maskedElementIds = maskedElementIds ?: config.maskedElementIds,
254
+ )
255
+ )
256
+ }
257
+
258
+ /** Add one element id to the runtime mask set (idempotent). Backs `replay.maskElement(id)`. */
259
+ fun addMaskedElement(elementId: String) {
260
+ if (config.maskedElementIds.contains(elementId)) return
261
+ updateConfig(config.copy(maskedElementIds = config.maskedElementIds + elementId))
262
+ }
263
+
264
+ /** Remove one element id from the runtime mask set. Backs `replay.unmaskElement(id)`. */
265
+ fun removeMaskedElement(elementId: String) {
266
+ if (!config.maskedElementIds.contains(elementId)) return
267
+ updateConfig(config.copy(maskedElementIds = config.maskedElementIds - elementId))
268
+ }
269
+
229
270
  // ─── Query ──────────────────────────────────────────────────────────
230
271
 
231
272
  fun getSessionInfo(): Map<String, Any?>? = session?.toMap()
@@ -14031,6 +14031,12 @@ var SHAPE = {
14031
14031
  }
14032
14032
  },
14033
14033
  captureInteractionHeatmap: bool(true),
14034
+ // Opt-in: intercept console.* and ship the lines to the Diagnose → Logs lane in SaaS
14035
+ // mode. OFF by default — console output can carry PII/secrets and adds ingest volume,
14036
+ // so capturing it is the host's explicit choice, not a silent default. When true and a
14037
+ // clientKey is present, the bootstrapper arms the console integration (previously only
14038
+ // reachable through the desktop-debug tooling, so stock SaaS apps captured nothing).
14039
+ captureConsoleLogs: bool(false),
14034
14040
  ota: {
14035
14041
  kind: "obj",
14036
14042
  opt: true,
@@ -15283,6 +15289,9 @@ function uninstallAlertInstrumentation() {
15283
15289
  installed = false;
15284
15290
  }
15285
15291
 
15292
+ // lib/module/bootstrap/SDKBootstrapper.js
15293
+ init_consoleIntegration();
15294
+
15286
15295
  // lib/module/features/session/BackendSessionAdapter.js
15287
15296
  init_internalLogger();
15288
15297
 
@@ -16795,6 +16804,9 @@ var SDKBootstrapper = class {
16795
16804
  }
16796
16805
  });
16797
16806
  sessionManager.setBackendTransport(backendAdapter);
16807
+ if (this.config.clientKey && this.config.captureConsoleLogs) {
16808
+ installConsoleIntegration();
16809
+ }
16798
16810
  backendAdapter.recoverPersistedFrames().catch(() => {
16799
16811
  });
16800
16812
  if (this.config.clientKey) {
@@ -16981,6 +16993,7 @@ var SDKBootstrapper = class {
16981
16993
  this._deepLinkUnsubscribe?.();
16982
16994
  this._deepLinkUnsubscribe = null;
16983
16995
  uninstallAlertInstrumentation();
16996
+ uninstallConsoleIntegration();
16984
16997
  this.featureRegistry.teardownAll();
16985
16998
  }
16986
16999
  };
@@ -20321,8 +20334,29 @@ var ScaleBunFacade = class {
20321
20334
  }
20322
20335
  });
20323
20336
  }
20324
- /** Log a message (convenience wrapper over track) */
20337
+ /**
20338
+ * Log a message. Delivered to BOTH lanes:
20339
+ * - the Diagnose → Logs lane (tagged `source: 'sdk'`), so an explicit `log()` call shows
20340
+ * up where a developer looks for logs. The method name promised this, but it previously
20341
+ * only emitted a `$log` analytics EVENT and never reached the Logs page.
20342
+ * - the analytics `$log` event (unchanged), so anything already built on it keeps working.
20343
+ * `queueLog` self-gates on `clientKey`, so the Logs-lane write is a no-op outside SaaS mode.
20344
+ */
20325
20345
  log(level, message, data) {
20346
+ try {
20347
+ const transport = SessionManager.getExistingInstance()?.getBackendTransport();
20348
+ transport?.queueLog({
20349
+ level,
20350
+ message,
20351
+ source: "sdk",
20352
+ platform: "js",
20353
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
20354
+ ...data ? {
20355
+ metadata: data
20356
+ } : {}
20357
+ });
20358
+ } catch {
20359
+ }
20326
20360
  return this.track("$log", {
20327
20361
  level,
20328
20362
  message,