@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.
- package/android/src/main/java/com/scalebun/replaysdk/ReplaySdkModule.kt +25 -0
- package/android/src/main/java/com/scalebun/replaysdk/session/SessionManager.kt +41 -0
- package/dist/scalebun.full.js +35 -1
- package/dist/scalebun.full.js.map +1 -1
- package/dist/scalebun.slim.js +35 -1
- package/dist/scalebun.slim.js.map +1 -1
- package/ios/ReplaySdk.swift +13 -0
- package/ios/Session/SessionManager.swift +43 -0
- package/lib/commonjs/bootstrap/SDKBootstrapper.js +12 -0
- package/lib/commonjs/bootstrap/SDKBootstrapper.js.map +1 -1
- package/lib/commonjs/core/config/schema.js +6 -0
- package/lib/commonjs/core/config/schema.js.map +1 -1
- package/lib/commonjs/public/ScaleBunFacade.js +21 -1
- package/lib/commonjs/public/ScaleBunFacade.js.map +1 -1
- package/lib/module/bootstrap/SDKBootstrapper.js +12 -0
- package/lib/module/bootstrap/SDKBootstrapper.js.map +1 -1
- package/lib/module/core/config/schema.js +6 -0
- package/lib/module/core/config/schema.js.map +1 -1
- package/lib/module/public/ScaleBunFacade.js +21 -1
- package/lib/module/public/ScaleBunFacade.js.map +1 -1
- package/lib/typescript/bootstrap/SDKBootstrapper.d.ts.map +1 -1
- package/lib/typescript/core/config/schema.d.ts +2 -0
- package/lib/typescript/core/config/schema.d.ts.map +1 -1
- package/lib/typescript/public/ScaleBunFacade.d.ts +8 -1
- package/lib/typescript/public/ScaleBunFacade.d.ts.map +1 -1
- package/lib/typescript/public/types.d.ts +12 -0
- package/lib/typescript/public/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/bootstrap/SDKBootstrapper.ts +12 -0
- package/src/core/config/schema.ts +8 -0
- package/src/public/ScaleBunFacade.ts +19 -1
- 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()
|
package/dist/scalebun.full.js
CHANGED
|
@@ -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
|
-
/**
|
|
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,
|