@debugbundle/sdk-react-native 1.3.0 → 3.0.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/CHANGELOG.md +22 -2
- package/DebugBundleReactNative.podspec +2 -2
- package/MIGRATION-3.0.md +9 -0
- package/README.md +8 -3
- package/android/build.gradle +3 -3
- package/android/src/main/java/com/debugbundle/reactnative/DebugBundleReactNativeModule.java +2 -2
- package/app.plugin.js +1 -1
- package/dist/before-send.d.ts.map +1 -1
- package/dist/before-send.js +6 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +238 -61
- package/dist/console.d.ts +4 -2
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +5 -0
- package/dist/privacy-keys.d.ts +3 -0
- package/dist/privacy-keys.d.ts.map +1 -0
- package/dist/privacy-keys.js +35 -0
- package/dist/privacy-telemetry.d.ts +19 -0
- package/dist/privacy-telemetry.d.ts.map +1 -0
- package/dist/privacy-telemetry.js +164 -0
- package/dist/privacy-text.d.ts +3 -0
- package/dist/privacy-text.d.ts.map +1 -0
- package/dist/privacy-text.js +85 -0
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +29 -45
- package/ios/DebugBundleReactNative.swift +2 -2
- package/package.json +6 -5
- package/src/before-send.ts +6 -0
- package/src/client.ts +226 -70
- package/src/console.ts +6 -1
- package/src/privacy-keys.ts +39 -0
- package/src/privacy-telemetry.ts +168 -0
- package/src/privacy-text.ts +86 -0
- package/src/redaction.ts +26 -46
- package/android/src/test/java/com/debugbundle/reactnative/DebugBundleReactNativeModuleTest.java +0 -474
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [3.0.0] - 2026-09-25
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Bound pending native probe and context calls with the shared event bridge budgets, contain throwing capture context accessors, and sanitize context field names before retaining or handing off values. Context keeps at most 50 fields with 128-character keys; saturated probes and context updates are best-effort drops.
|
|
12
|
+
|
|
13
|
+
- Reject locally disabled or below-threshold logs and disabled request capture before JavaScript context redaction and `beforeSend`; filtered console records skip argument formatting when the SDK client exposes its eligibility check.
|
|
14
|
+
- Limit unresolved native bridge event calls to 256 and 4 MiB, with 32 calls and 1 MiB reserved for errors and exceptions. Individual retained events are capped at 64 KiB; calls above capacity are discarded so a stalled bridge cannot retain an unbounded promise backlog.
|
|
15
|
+
|
|
16
|
+
## [2.0.0] - 2026-09-21
|
|
17
|
+
|
|
18
|
+
### Security
|
|
19
|
+
|
|
20
|
+
- Enforce mandatory JavaScript capture protection before and after `beforeSend` and before native bridge calls, including the legacy bridge fallback.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Pin the protected Android 2.0.0 and Swift 2.0 release lines and fail the release when any wrapper, bridge, podspec, Android, or smoke-test version drifts.
|
|
25
|
+
- Require published-native Android 17/API 37.0 and Xcode 27/iOS 27 app-delivery gates before npm publication.
|
|
26
|
+
|
|
5
27
|
## [1.3.0] - 2026-09-12
|
|
6
28
|
|
|
7
29
|
### Changed
|
|
@@ -10,8 +32,6 @@ All notable changes to this project will be documented in this file.
|
|
|
10
32
|
- Use Android1.3.1 with complete packaged Apache licenses and the Apache-2.0 Swift SDK1.3.0.
|
|
11
33
|
- Publish npm releases through GitHub Actions trusted publishing without an npm token.
|
|
12
34
|
|
|
13
|
-
## Unreleased
|
|
14
|
-
|
|
15
35
|
## 1.2.0 - 2026-07-29
|
|
16
36
|
|
|
17
37
|
- Preserve React Native SDK identity, original JavaScript error fields, service, correlation, context, probes, and mobile metadata through additive Android and Swift external-event APIs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Pod::Spec.new do |s|
|
|
2
2
|
s.name = "DebugBundleReactNative"
|
|
3
|
-
s.version = "
|
|
3
|
+
s.version = "3.0.0"
|
|
4
4
|
s.summary = "React Native wrapper for the DebugBundle mobile SDKs."
|
|
5
5
|
s.license = { :type => "Apache-2.0", :file => "LICENSE" }
|
|
6
6
|
s.author = { "DebugBundle" => "support@debugbundle.com" }
|
|
@@ -12,5 +12,5 @@ Pod::Spec.new do |s|
|
|
|
12
12
|
|
|
13
13
|
s.dependency "React-Core"
|
|
14
14
|
install_modules_dependencies(s) if defined?(install_modules_dependencies)
|
|
15
|
-
s.dependency "DebugBundle", "~>
|
|
15
|
+
s.dependency "DebugBundle", "~> 3.0"
|
|
16
16
|
end
|
package/MIGRATION-3.0.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# React Native 3.0 migration
|
|
2
|
+
|
|
3
|
+
React Native 2.0 remains the published compatibility baseline. Version 3 uses the hardened Swift3 and Android3 native SDKs with the same installation flow and bridge APIs; no collector or extra service is needed. Install/rebuild the native binary when upgrading and verify both native dependency versions before publishing the wrapper.
|
|
4
|
+
|
|
5
|
+
Optional `beforeSend` callbacks run after capture returns on the JavaScript event loop. They must return promptly: closures retain application state and cannot be transparently moved to isolated workers. Hook side effects are no longer synchronous with capture. Valid replacements, null drops and protected-original fallback for thrown/invalid hooks remain supported. Final values receive privacy protection and level checks again before native handoff.
|
|
6
|
+
|
|
7
|
+
Privacy-safe pending preparation and native calls share a 256-call/4-MiB cap. Low-priority work may use224 calls/3MiB, reserving space for errors and exceptions. Both the protected original and final bridge snapshot stay charged until the bridge settles. Under pressure a pending hook or valid expanded replacement can be discarded; it never causes a return to pre-hook application content. SDK-owned queues stay bounded if the native bridge stalls. Explicit flush lets prior deferred preparations reach the bridge before requesting the native flush.
|
|
8
|
+
|
|
9
|
+
The existing canonical and legacy native bridge methods remain supported. Context, probes, JavaScript error identity and native offline/ACK behavior remain available. Swift3 and Android3 document background persistence and safe custom-exception projection separately.
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ Published implementation:
|
|
|
15
15
|
- React Navigation breadcrumb helpers.
|
|
16
16
|
- `fetch` and `XMLHttpRequest` instrumentation with target-scoped `X-DebugBundle-Trace-Id`.
|
|
17
17
|
- JS-side bounded serialization and redaction before native queue persistence.
|
|
18
|
+
- Local log level, log capture, and request capture switches are checked before event construction or `beforeSend`. A stalled native bridge accepts at most 224 lower-priority event calls / 3 MiB and reserves 32 more calls / 1 MiB for errors and exceptions. Each event is capped at 64 KiB; excess calls are discarded.
|
|
18
19
|
- Android and iOS native wrappers that delegate queueing, config/status, request capture, crash/error capture, flushing, and probe trigger activation to the native SDK foundations.
|
|
19
20
|
- Repository-local Android Java, Swift, and Objective-C++ wrapper tests with an
|
|
20
21
|
enforced 80% line-coverage floor for every handwritten native bridge source.
|
|
@@ -25,9 +26,9 @@ Published implementation:
|
|
|
25
26
|
| Lane | Support |
|
|
26
27
|
| --- | --- |
|
|
27
28
|
| Minimum compatibility | React Native 0.76+, React 18.2+, iOS 15+, Android minSdk 23 |
|
|
28
|
-
| Recommended production | Current stable React Native 0.
|
|
29
|
+
| Recommended production | Current stable React Native 0.87.x with Hermes and the New Architecture enabled where your app supports it |
|
|
29
30
|
| Installed-base compatibility | React Native 0.76 through current stable, including legacy bridge apps |
|
|
30
|
-
| Rolling CI | TypeScript/package smoke, Android bridge compile on RN 0.76.9, 0.82.1,
|
|
31
|
+
| Rolling CI | TypeScript/package smoke, Android bridge compile on RN 0.76.9, 0.82.1, 0.85.3, and 0.87.1, bare iOS compatibility/current-installed-base lanes, plus Expo SDK 57 / RN 0.86 Android and iOS development builds |
|
|
31
32
|
| Expo | Expo development builds and prebuild; Expo Go is degraded because it cannot load custom native modules |
|
|
32
33
|
|
|
33
34
|
JSC compatibility is best-effort where the selected React Native lane still supports it. Hermes is the primary tested JavaScript engine.
|
|
@@ -141,4 +142,8 @@ checks.
|
|
|
141
142
|
|
|
142
143
|
## Release
|
|
143
144
|
|
|
144
|
-
The package publishes to npm from `v*` tags through GitHub Actions. Configure an npm GitHub Actions trusted publisher for `debugbundle/debugbundle-react-native` and `release.yml`, with a blank environment and direct `npm publish` enabled. Publishing uses OIDC without a long-lived npm token. Make sure the tag matches `package.json` exactly
|
|
145
|
+
The package publishes to npm from `v*` tags through GitHub Actions. Configure an npm GitHub Actions trusted publisher for `debugbundle/debugbundle-react-native` and `release.yml`, with a blank environment and direct `npm publish` enabled. Publishing uses OIDC without a long-lived npm token. Make sure the tag matches `package.json` exactly. The next protected release requires a matching 2.x wrapper/Swift pod line and a published Android 2.x BOM/module pin. `make check-protected-native-pins` refuses the current 1.x source line; update the wrapper version, native pins, and embedded SDK versions together only after the exact Android and Swift artifacts are available to Maven Central and CocoaPods consumers. The release workflow then compiles and runs clean apps against those published artifacts on Android API 37 and Xcode 27 before npm publication.
|
|
146
|
+
|
|
147
|
+
Probe and context native calls share the event bridge's pending count and byte limits, including reserved exception capacity. Saturated auxiliary calls are discarded without evaluating probe suppliers. Probe labels are limited to 128 characters. Persistent JS context retains at most 50 fields with keys up to 128 characters, and field-sensitive privacy runs before local retention and native handoff. Throwing capture context accessors are withheld without escaping into application code. These bounds do not move native SDK persistence off its caller thread.
|
|
148
|
+
|
|
149
|
+
Version 3 defers optional `beforeSend` callbacks until capture returns. Callbacks run on the JavaScript event loop and must return promptly. Pending preparation and native delivery share bounded count/byte limits; valid expanded replacements may be dropped under pressure. See [the version3 migration guide](MIGRATION-3.0.md).
|
package/android/build.gradle
CHANGED
|
@@ -11,7 +11,7 @@ if (isReactNativeHostBuild) {
|
|
|
11
11
|
def hostKotlinVersion = rootProject.ext.has("kotlinVersion")
|
|
12
12
|
? rootProject.ext.get("kotlinVersion")
|
|
13
13
|
: "2.1.20"
|
|
14
|
-
def debugBundleAndroidVersion = rootProject.findProperty("debugBundleAndroidVersion") ?: "
|
|
14
|
+
def debugBundleAndroidVersion = rootProject.findProperty("debugBundleAndroidVersion") ?: "3.0.0"
|
|
15
15
|
|
|
16
16
|
android {
|
|
17
17
|
namespace "com.debugbundle.reactnative"
|
|
@@ -32,7 +32,7 @@ android {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
dependencies {
|
|
35
|
-
compileOnly "com.facebook.react:react-android:${rootProject.findProperty("reactNativeVersion") ?: "0.
|
|
35
|
+
compileOnly "com.facebook.react:react-android:${rootProject.findProperty("reactNativeVersion") ?: "0.87.1"}"
|
|
36
36
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:${hostKotlinVersion}"
|
|
37
37
|
implementation platform("com.debugbundle:debugbundle-android-bom:${debugBundleAndroidVersion}")
|
|
38
38
|
implementation("com.debugbundle:debugbundle-android") {
|
|
@@ -40,7 +40,7 @@ dependencies {
|
|
|
40
40
|
exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib-jdk7"
|
|
41
41
|
exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib-jdk8"
|
|
42
42
|
}
|
|
43
|
-
testImplementation "com.facebook.react:react-android:${rootProject.findProperty("reactNativeVersion") ?: "0.
|
|
43
|
+
testImplementation "com.facebook.react:react-android:${rootProject.findProperty("reactNativeVersion") ?: "0.87.1"}"
|
|
44
44
|
testImplementation "junit:junit:4.13.2"
|
|
45
45
|
testImplementation "org.mockito:mockito-core:5.14.2"
|
|
46
46
|
}
|
|
@@ -199,12 +199,12 @@ public final class DebugBundleReactNativeModule extends ReactContextBaseJavaModu
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
private String currentSdkVersion = "
|
|
202
|
+
private String currentSdkVersion = "3.0.0";
|
|
203
203
|
private String currentService = "react-native-app";
|
|
204
204
|
private String currentEnvironment = "production";
|
|
205
205
|
|
|
206
206
|
private DebugBundleConfig createConfig(ReadableMap config) {
|
|
207
|
-
currentSdkVersion = stringOrDefault(config, "sdkVersion", "
|
|
207
|
+
currentSdkVersion = stringOrDefault(config, "sdkVersion", "3.0.0");
|
|
208
208
|
currentService = stringOrDefault(config, "service", "react-native-app");
|
|
209
209
|
currentEnvironment = stringOrDefault(config, "environment", "production");
|
|
210
210
|
return DebugBundleConfig.create(
|
package/app.plugin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"before-send.d.ts","sourceRoot":"","sources":["../src/before-send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AA2ClF,wBAAgB,eAAe,CAC7B,KAAK,EAAE,wBAAwB,EAC/B,IAAI,EAAE,qBAAqB,GAAG,IAAI,GACjC,wBAAwB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"before-send.d.ts","sourceRoot":"","sources":["../src/before-send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AA2ClF,wBAAgB,eAAe,CAC7B,KAAK,EAAE,wBAAwB,EAC/B,IAAI,EAAE,qBAAqB,GAAG,IAAI,GACjC,wBAAwB,GAAG,IAAI,CAmBjC"}
|
package/dist/before-send.js
CHANGED
|
@@ -42,6 +42,12 @@ export function applyBeforeSend(event, hook) {
|
|
|
42
42
|
}
|
|
43
43
|
try {
|
|
44
44
|
const result = hook(cloneEvent(event));
|
|
45
|
+
// Hooks have synchronous return contracts. Contain mistaken async failures
|
|
46
|
+
// before treating their Promise as invalid and keeping the protected original.
|
|
47
|
+
if (result instanceof Promise) {
|
|
48
|
+
void result.catch(() => undefined);
|
|
49
|
+
return event;
|
|
50
|
+
}
|
|
45
51
|
if (result === null) {
|
|
46
52
|
return null;
|
|
47
53
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare class DebugBundleReactNativeClient implements DebugBundleClient {
|
|
|
7
7
|
private breadcrumbs;
|
|
8
8
|
private probeBuffers;
|
|
9
9
|
private jsProbeActivationExpiresAt;
|
|
10
|
+
private pendingNativeCalls;
|
|
11
|
+
private pendingNativeBytes;
|
|
10
12
|
constructor(config?: DebugBundleConfig);
|
|
11
13
|
get status(): DebugBundleStatus;
|
|
12
14
|
get lastEventAt(): number | null;
|
|
@@ -14,15 +16,21 @@ export declare class DebugBundleReactNativeClient implements DebugBundleClient {
|
|
|
14
16
|
captureException(error: unknown, context?: DebugBundleCaptureContext): void;
|
|
15
17
|
captureError(error: unknown, context?: DebugBundleCaptureContext): void;
|
|
16
18
|
captureLog(message: string, level?: DebugBundleLogLevel, context?: DebugBundleCaptureContext): void;
|
|
19
|
+
isLogEnabled(level: DebugBundleLogLevel): boolean;
|
|
17
20
|
captureRequest(request: DebugBundleRequestInfo, response: DebugBundleResponseInfo, context?: DebugBundleCaptureContext): void;
|
|
18
21
|
captureMessage(message: string, level?: DebugBundleLogLevel, context?: DebugBundleCaptureContext): void;
|
|
19
22
|
setContext(key: string, value: unknown): void;
|
|
20
23
|
probe(label: string, data: unknown | (() => unknown), options?: DebugBundleProbeOptions): void;
|
|
24
|
+
private enqueueNativeAuxiliary;
|
|
21
25
|
flush(): Promise<void>;
|
|
22
26
|
activateProbeTriggerToken(token: string): Promise<boolean>;
|
|
23
27
|
recordBreadcrumb(type: string, data?: Record<string, unknown>): void;
|
|
24
28
|
recordScreen(screenName: string, previousScreen?: string | null, source?: string): void;
|
|
25
29
|
private enqueue;
|
|
30
|
+
private sendAdmittedEvent;
|
|
31
|
+
private prepareAdmittedEvent;
|
|
32
|
+
private hasNativeCapacity;
|
|
33
|
+
private releaseNativeCapacity;
|
|
26
34
|
private canCapture;
|
|
27
35
|
private mergeContext;
|
|
28
36
|
private snapshotProbeData;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EAEjB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EAIjB,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAoDpB,qBAAa,4BAA6B,YAAW,iBAAiB;IACpE,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,WAAW,CAAkE;IACrF,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,YAAY,CAAqD;IACzE,OAAO,CAAC,0BAA0B,CAAK;IACvC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,kBAAkB,CAAK;gBAEnB,MAAM,GAAE,iBAAsB;IAO1C,IAAI,MAAM,IAAI,iBAAiB,CAE9B;IAED,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IA8BrC,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,yBAA8B,GAAG,IAAI;IAgB/E,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,yBAA8B,GAAG,IAAI;IAI3E,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,mBAA+B,EAAE,OAAO,GAAE,yBAA8B,GAAG,IAAI;IAalH,YAAY,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO;IAMjD,cAAc,CACZ,OAAO,EAAE,sBAAsB,EAC/B,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE,yBAA8B,GACtC,IAAI;IA6BP,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,mBAA+B,EAAE,OAAO,GAAE,yBAA8B,GAAG,IAAI;IAQtH,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAqB7C,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,EAAE,OAAO,GAAE,uBAA4B,GAAG,IAAI;IAuBlG,OAAO,CAAC,sBAAsB;IASxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAatB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWhE,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAcxE,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,GAAE,MAAM,GAAG,IAAW,EAAE,MAAM,SAAqB,GAAG,IAAI;IAWzG,OAAO,CAAC,OAAO;IA4Df,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,0BAA0B;CAInC;AAiBD,wBAAgB,uBAAuB,CAAC,MAAM,GAAE,iBAAsB,GAAG,4BAA4B,CAEpG;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,yBAAyB,CAsClF"}
|
package/dist/client.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { getNativeModule, degradedNativeState, safeNativeCall } from "./native.js";
|
|
2
2
|
import { applyBeforeSend } from "./before-send.js";
|
|
3
3
|
import { defaultRedactFields, DEFAULT_HEADER_ALLOWLIST, sanitizeHeaders, sanitizeValue } from "./redaction.js";
|
|
4
|
+
import { sanitizeTelemetry } from "./privacy-telemetry.js";
|
|
4
5
|
const SDK_NAME = "@debugbundle/sdk-react-native";
|
|
5
|
-
const DEFAULT_SDK_VERSION = "
|
|
6
|
+
const DEFAULT_SDK_VERSION = "3.0.0";
|
|
7
|
+
const MAX_PENDING_NATIVE_CALLS = 256;
|
|
8
|
+
const MAX_PENDING_LOW_PRIORITY_CALLS = 224;
|
|
9
|
+
const MAX_PENDING_NATIVE_BYTES = 4 * 1024 * 1024;
|
|
10
|
+
const MAX_PENDING_LOW_PRIORITY_BYTES = 3 * 1024 * 1024;
|
|
11
|
+
const MAX_NATIVE_EVENT_BYTES = 64 * 1024;
|
|
12
|
+
const MAX_CONTEXT_FIELDS = 50;
|
|
13
|
+
const MAX_CONTEXT_KEY_LENGTH = 128;
|
|
6
14
|
const LOG_LEVELS = {
|
|
7
15
|
debug: 10,
|
|
8
16
|
info: 20,
|
|
@@ -10,6 +18,37 @@ const LOG_LEVELS = {
|
|
|
10
18
|
error: 40,
|
|
11
19
|
critical: 50
|
|
12
20
|
};
|
|
21
|
+
function protectEventFields(event, additionalKeys) {
|
|
22
|
+
// Preserve typed identities only when their scalar values pass the mandatory policy.
|
|
23
|
+
for (const value of [event.schema_version, event.sdk_name, event.sdk_version,
|
|
24
|
+
...Object.values(event.correlation ?? {})]) {
|
|
25
|
+
if (typeof value !== "string")
|
|
26
|
+
continue;
|
|
27
|
+
const checked = sanitizeTelemetry(value, { additionalKeys });
|
|
28
|
+
if (!checked.ok || checked.value !== value)
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const result = sanitizeTelemetry({
|
|
32
|
+
service: event.service,
|
|
33
|
+
payload: event.payload,
|
|
34
|
+
...(event.context ? { context: event.context } : {})
|
|
35
|
+
}, { additionalKeys });
|
|
36
|
+
if (!result.ok || !result.value || typeof result.value !== "object" || Array.isArray(result.value)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const safeFields = result.value;
|
|
40
|
+
if (!safeFields.service || !safeFields.payload ||
|
|
41
|
+
typeof safeFields.service !== "object" || Array.isArray(safeFields.service) ||
|
|
42
|
+
typeof safeFields.payload !== "object" || Array.isArray(safeFields.payload)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
...event,
|
|
47
|
+
service: safeFields.service,
|
|
48
|
+
payload: safeFields.payload,
|
|
49
|
+
...(event.context ? { context: safeFields.context } : {})
|
|
50
|
+
};
|
|
51
|
+
}
|
|
13
52
|
export class DebugBundleReactNativeClient {
|
|
14
53
|
config;
|
|
15
54
|
nativeModule = null;
|
|
@@ -18,6 +57,8 @@ export class DebugBundleReactNativeClient {
|
|
|
18
57
|
breadcrumbs = [];
|
|
19
58
|
probeBuffers = new Map();
|
|
20
59
|
jsProbeActivationExpiresAt = 0;
|
|
60
|
+
pendingNativeCalls = 0;
|
|
61
|
+
pendingNativeBytes = 0;
|
|
21
62
|
constructor(config = {}) {
|
|
22
63
|
this.config = resolveConfig(config);
|
|
23
64
|
if (Object.keys(config).length > 0) {
|
|
@@ -62,6 +103,9 @@ export class DebugBundleReactNativeClient {
|
|
|
62
103
|
}
|
|
63
104
|
}
|
|
64
105
|
captureException(error, context = {}) {
|
|
106
|
+
if (!this.config.captureErrors || !this.canCapture("frontend_exception", false) ||
|
|
107
|
+
!this.hasNativeCapacity(true))
|
|
108
|
+
return;
|
|
65
109
|
const mergedContext = this.mergeContext(context);
|
|
66
110
|
const sanitizedError = asRecord(sanitizeValue(error, { redactFields: this.config.redactFields }));
|
|
67
111
|
this.enqueue("frontend_exception", {
|
|
@@ -78,16 +122,26 @@ export class DebugBundleReactNativeClient {
|
|
|
78
122
|
this.captureException(error, context);
|
|
79
123
|
}
|
|
80
124
|
captureLog(message, level = "warning", context = {}) {
|
|
125
|
+
if (!this.isLogEnabled(level))
|
|
126
|
+
return;
|
|
127
|
+
const mergedContext = this.mergeContext(context);
|
|
81
128
|
this.enqueue("log_event", {
|
|
82
129
|
level,
|
|
83
130
|
message: sanitizeValue(message, { redactFields: this.config.redactFields }),
|
|
84
131
|
attributes: {
|
|
85
|
-
...asRecord(sanitizeValue(
|
|
132
|
+
...asRecord(sanitizeValue(mergedContext, { redactFields: this.config.redactFields })),
|
|
86
133
|
source: "javascript"
|
|
87
134
|
}
|
|
88
|
-
}, stringValue(
|
|
135
|
+
}, stringValue(mergedContext.trace_id), true);
|
|
136
|
+
}
|
|
137
|
+
isLogEnabled(level) {
|
|
138
|
+
const rank = LOG_LEVELS[level];
|
|
139
|
+
return rank !== undefined && this.config.captureLogs && rank >= LOG_LEVELS[this.config.logLevel] &&
|
|
140
|
+
this.canCapture("log_event", true) && this.hasNativeCapacity(rank >= LOG_LEVELS.error);
|
|
89
141
|
}
|
|
90
142
|
captureRequest(request, response, context = {}) {
|
|
143
|
+
if (!this.config.captureNetwork || !this.canCapture("request_event", true) || !this.hasNativeCapacity(false))
|
|
144
|
+
return;
|
|
91
145
|
const mergedContext = this.mergeContext(context);
|
|
92
146
|
const traceId = request.traceId ?? stringValue(mergedContext.trace_id);
|
|
93
147
|
const parsedUrl = parseRequestUrl(request.url);
|
|
@@ -105,10 +159,8 @@ export class DebugBundleReactNativeClient {
|
|
|
105
159
|
}),
|
|
106
160
|
duration_ms: response.durationMillis ?? 0
|
|
107
161
|
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
this.enqueue("request_event", payload, traceId, true, asRecord(sanitizeValue(mergedContext, { redactFields: this.config.redactFields })), this.config.captureNetwork);
|
|
162
|
+
this.recordBreadcrumb("network_request", payload);
|
|
163
|
+
this.enqueue("request_event", payload, traceId, true, asRecord(sanitizeValue(mergedContext, { redactFields: this.config.redactFields })));
|
|
112
164
|
}
|
|
113
165
|
captureMessage(message, level = "warning", context = {}) {
|
|
114
166
|
this.recordBreadcrumb("message", {
|
|
@@ -118,39 +170,66 @@ export class DebugBundleReactNativeClient {
|
|
|
118
170
|
});
|
|
119
171
|
}
|
|
120
172
|
setContext(key, value) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
173
|
+
if (typeof key !== "string" || !key || key.length > MAX_CONTEXT_KEY_LENGTH ||
|
|
174
|
+
!this.hasNativeCapacity(false) ||
|
|
175
|
+
!Object.hasOwn(this.context, key) && Object.keys(this.context).length >= MAX_CONTEXT_FIELDS)
|
|
176
|
+
return;
|
|
177
|
+
try {
|
|
178
|
+
// Include the field name in mandatory privacy before local or native ownership.
|
|
179
|
+
const fields = asRecord(sanitizeValue({ [key]: value }, { redactFields: this.config.redactFields }));
|
|
180
|
+
if (!Object.hasOwn(fields, key))
|
|
181
|
+
return;
|
|
182
|
+
const entry = { value: fields[key] };
|
|
183
|
+
this.context = { ...this.context, [key]: entry.value };
|
|
184
|
+
const nativeModule = this.nativeModule;
|
|
185
|
+
if (nativeModule?.setContextValue) {
|
|
186
|
+
this.enqueueNativeAuxiliary({ key, entry }, () => nativeModule.setContextValue(key, entry));
|
|
187
|
+
}
|
|
188
|
+
else if (nativeModule?.setContext) {
|
|
189
|
+
this.enqueueNativeAuxiliary({ key, entry }, () => nativeModule.setContext(key, entry.value));
|
|
190
|
+
}
|
|
124
191
|
}
|
|
125
|
-
|
|
126
|
-
|
|
192
|
+
catch {
|
|
193
|
+
// Context accessors and optional native integrations cannot escape into the host.
|
|
127
194
|
}
|
|
128
195
|
}
|
|
129
196
|
probe(label, data, options = {}) {
|
|
130
|
-
if (!label ||
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
const nativeProbeActive = this.nativeModule?.isProbeActive?.(label) ?? false;
|
|
134
|
-
if (options.heavy && !nativeProbeActive && !this.hasActiveJsProbeActivation()) {
|
|
197
|
+
if (!label || label.length > MAX_CONTEXT_KEY_LENGTH || !this.hasNativeCapacity(false) ||
|
|
198
|
+
this.probeBuffers.size >= this.config.maxProbeLabels && !this.probeBuffers.has(label))
|
|
135
199
|
return;
|
|
200
|
+
try {
|
|
201
|
+
const nativeModule = this.nativeModule;
|
|
202
|
+
const nativeProbeActive = nativeModule?.isProbeActive?.(label) ?? false;
|
|
203
|
+
if (options.heavy && !nativeProbeActive && !this.hasActiveJsProbeActivation())
|
|
204
|
+
return;
|
|
205
|
+
const value = typeof data === "function" ? safeInvoke(data) : data;
|
|
206
|
+
const sanitizedData = objectWrap(sanitizeValue(value, { redactFields: this.config.redactFields }));
|
|
207
|
+
const occurredAt = new Date().toISOString();
|
|
208
|
+
const entry = { label, data: sanitizedData, timestamp: occurredAt, activation_id: null };
|
|
209
|
+
const entries = this.probeBuffers.get(label) ?? [];
|
|
210
|
+
entries.push(entry);
|
|
211
|
+
this.probeBuffers.set(label, entries.slice(-this.config.maxProbeEntriesPerLabel));
|
|
212
|
+
if (nativeModule?.captureProbe) {
|
|
213
|
+
this.enqueueNativeAuxiliary({ label, data: sanitizedData, occurredAt }, () => nativeModule.captureProbe(label, sanitizedData, occurredAt));
|
|
214
|
+
}
|
|
136
215
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const occurredAt = new Date().toISOString();
|
|
140
|
-
const entry = {
|
|
141
|
-
label,
|
|
142
|
-
data: sanitizedData,
|
|
143
|
-
timestamp: occurredAt,
|
|
144
|
-
activation_id: null
|
|
145
|
-
};
|
|
146
|
-
const entries = this.probeBuffers.get(label) ?? [];
|
|
147
|
-
entries.push(entry);
|
|
148
|
-
this.probeBuffers.set(label, entries.slice(-this.config.maxProbeEntriesPerLabel));
|
|
149
|
-
if (this.nativeModule?.captureProbe) {
|
|
150
|
-
void safeNativeCall(() => this.nativeModule.captureProbe(label, sanitizedData, occurredAt), false);
|
|
216
|
+
catch {
|
|
217
|
+
// Probe options, native activation readers and data access cannot throw into the host.
|
|
151
218
|
}
|
|
152
219
|
}
|
|
220
|
+
enqueueNativeAuxiliary(payload, operation) {
|
|
221
|
+
if (!this.hasNativeCapacity(false))
|
|
222
|
+
return;
|
|
223
|
+
const bytes = utf8ByteLength(JSON.stringify(payload));
|
|
224
|
+
if (bytes > MAX_NATIVE_EVENT_BYTES || this.pendingNativeBytes + bytes > MAX_PENDING_LOW_PRIORITY_BYTES)
|
|
225
|
+
return;
|
|
226
|
+
this.pendingNativeCalls += 1;
|
|
227
|
+
this.pendingNativeBytes += bytes;
|
|
228
|
+
void safeNativeCall(operation, undefined).finally(() => this.releaseNativeCapacity(bytes));
|
|
229
|
+
}
|
|
153
230
|
async flush() {
|
|
231
|
+
// Hook preparation queued by earlier capture calls must reach the bridge first.
|
|
232
|
+
await Promise.resolve();
|
|
154
233
|
if (!this.nativeModule) {
|
|
155
234
|
return;
|
|
156
235
|
}
|
|
@@ -194,43 +273,117 @@ export class DebugBundleReactNativeClient {
|
|
|
194
273
|
});
|
|
195
274
|
}
|
|
196
275
|
enqueue(eventType, payload, traceId, countTowardSession, context, localPolicyAllows = true) {
|
|
197
|
-
if (!
|
|
276
|
+
if (!localPolicyAllows)
|
|
198
277
|
return;
|
|
199
|
-
|
|
200
|
-
const authoredEvent = {
|
|
201
|
-
schema_version: "2026-03-01",
|
|
202
|
-
event_id: generateId(),
|
|
203
|
-
event_type: eventType,
|
|
204
|
-
sdk_name: SDK_NAME,
|
|
205
|
-
sdk_version: this.config.sdkVersion,
|
|
206
|
-
service: {
|
|
207
|
-
name: this.config.service,
|
|
208
|
-
environment: this.config.environment,
|
|
209
|
-
runtime: "react-native",
|
|
210
|
-
framework: "react-native"
|
|
211
|
-
},
|
|
212
|
-
occurred_at: new Date().toISOString(),
|
|
213
|
-
...(traceId ? { correlation: { trace_id: traceId } } : {}),
|
|
214
|
-
...(context && Object.keys(context).length > 0 ? { context } : {}),
|
|
215
|
-
payload: this.nativeState.device
|
|
216
|
-
? { ...payload, device: canonicalDevice(this.nativeState.device, this.config) }
|
|
217
|
-
: payload
|
|
218
|
-
};
|
|
219
|
-
const event = applyBeforeSend(authoredEvent, this.config.beforeSend);
|
|
220
|
-
if (!event || !localPolicyAllows) {
|
|
278
|
+
if (!this.canCapture(eventType, countTowardSession)) {
|
|
221
279
|
return;
|
|
222
280
|
}
|
|
223
|
-
|
|
224
|
-
|
|
281
|
+
const highPriority = eventType === "frontend_exception" ||
|
|
282
|
+
eventType === "log_event" && (payload.level === "error" || payload.level === "critical");
|
|
283
|
+
if (!this.hasNativeCapacity(highPriority))
|
|
225
284
|
return;
|
|
285
|
+
this.pendingNativeCalls += 1;
|
|
286
|
+
let handedOff = false;
|
|
287
|
+
let reservedBytes = 0;
|
|
288
|
+
try {
|
|
289
|
+
const authoredEvent = {
|
|
290
|
+
schema_version: "2026-03-01",
|
|
291
|
+
event_id: generateId(),
|
|
292
|
+
event_type: eventType,
|
|
293
|
+
sdk_name: SDK_NAME,
|
|
294
|
+
sdk_version: this.config.sdkVersion,
|
|
295
|
+
service: {
|
|
296
|
+
name: this.config.service,
|
|
297
|
+
environment: this.config.environment,
|
|
298
|
+
runtime: "react-native",
|
|
299
|
+
framework: "react-native"
|
|
300
|
+
},
|
|
301
|
+
occurred_at: new Date().toISOString(),
|
|
302
|
+
...(traceId ? { correlation: { trace_id: traceId } } : {}),
|
|
303
|
+
...(context && Object.keys(context).length > 0 ? { context } : {}),
|
|
304
|
+
payload: this.nativeState.device
|
|
305
|
+
? { ...payload, device: canonicalDevice(this.nativeState.device, this.config) }
|
|
306
|
+
: payload
|
|
307
|
+
};
|
|
308
|
+
const initial = protectEventFields(authoredEvent, this.config.redactFields);
|
|
309
|
+
if (!initial)
|
|
310
|
+
return;
|
|
311
|
+
const config = this.config;
|
|
312
|
+
const nativeModule = this.nativeModule;
|
|
313
|
+
const initialBytes = utf8ByteLength(JSON.stringify(initial));
|
|
314
|
+
const byteLimit = highPriority ? MAX_PENDING_NATIVE_BYTES : MAX_PENDING_LOW_PRIORITY_BYTES;
|
|
315
|
+
if (initialBytes > MAX_NATIVE_EVENT_BYTES || this.pendingNativeBytes + initialBytes > byteLimit)
|
|
316
|
+
return;
|
|
317
|
+
this.pendingNativeBytes += initialBytes;
|
|
318
|
+
reservedBytes = initialBytes;
|
|
319
|
+
if (!nativeModule) {
|
|
320
|
+
this.nativeState = degradedNativeState("native_module_unavailable");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
handedOff = true;
|
|
324
|
+
this.sendAdmittedEvent(initial, config, nativeModule, reservedBytes);
|
|
226
325
|
}
|
|
227
|
-
|
|
228
|
-
|
|
326
|
+
catch {
|
|
327
|
+
this.nativeState = degradedNativeState("event_preparation_failed");
|
|
229
328
|
}
|
|
230
|
-
|
|
231
|
-
|
|
329
|
+
finally {
|
|
330
|
+
if (!handedOff)
|
|
331
|
+
this.releaseNativeCapacity(reservedBytes);
|
|
232
332
|
}
|
|
233
333
|
}
|
|
334
|
+
sendAdmittedEvent(initial, config, nativeModule, reservedBytes) {
|
|
335
|
+
// This activation receives only a protected snapshot: deferred closures must
|
|
336
|
+
// not retain the raw capture payload/context through their lexical scope.
|
|
337
|
+
const prepareAndSend = async () => {
|
|
338
|
+
if (this.config !== config)
|
|
339
|
+
return;
|
|
340
|
+
const safeEvent = this.prepareAdmittedEvent(initial, config, nativeModule.enqueueCanonicalEvent !== undefined);
|
|
341
|
+
if (safeEvent === null)
|
|
342
|
+
return;
|
|
343
|
+
const finalPriority = safeEvent.event_type === "frontend_exception" ||
|
|
344
|
+
safeEvent.event_type === "log_event" && ["error", "critical"].includes(String(safeEvent.payload.level));
|
|
345
|
+
const eventBytes = utf8ByteLength(JSON.stringify(safeEvent));
|
|
346
|
+
const finalLimit = finalPriority ? MAX_PENDING_NATIVE_BYTES : MAX_PENDING_LOW_PRIORITY_BYTES;
|
|
347
|
+
// The callback closure can retain the protected original until the bridge
|
|
348
|
+
// settles. Charge both it and the final snapshot instead of forgetting it.
|
|
349
|
+
if (eventBytes > MAX_NATIVE_EVENT_BYTES || this.pendingNativeBytes + eventBytes > finalLimit)
|
|
350
|
+
return;
|
|
351
|
+
this.pendingNativeBytes += eventBytes;
|
|
352
|
+
reservedBytes += eventBytes;
|
|
353
|
+
if (nativeModule.enqueueCanonicalEvent)
|
|
354
|
+
await nativeModule.enqueueCanonicalEvent(safeEvent);
|
|
355
|
+
else
|
|
356
|
+
await nativeModule.enqueueEvent(safeEvent);
|
|
357
|
+
};
|
|
358
|
+
const operation = config.beforeSend === null
|
|
359
|
+
? safeNativeCall(prepareAndSend, undefined)
|
|
360
|
+
: Promise.resolve().then(() => safeNativeCall(prepareAndSend, undefined));
|
|
361
|
+
void operation.finally(() => { this.releaseNativeCapacity(reservedBytes); });
|
|
362
|
+
}
|
|
363
|
+
prepareAdmittedEvent(initial, config, canonical) {
|
|
364
|
+
// Exit this synchronous activation before awaiting the bridge. An application
|
|
365
|
+
// replacement may be huge before projection and must not survive suspension.
|
|
366
|
+
const event = applyBeforeSend(initial, config.beforeSend);
|
|
367
|
+
if (!event || this.config !== config)
|
|
368
|
+
return null;
|
|
369
|
+
const safeEvent = protectEventFields(event, config.redactFields);
|
|
370
|
+
if (!safeEvent)
|
|
371
|
+
return null;
|
|
372
|
+
if (safeEvent.event_type === "log_event" &&
|
|
373
|
+
(!config.captureLogs || !(LOG_LEVELS[safeEvent.payload.level] >= LOG_LEVELS[config.logLevel])))
|
|
374
|
+
return null;
|
|
375
|
+
if (safeEvent.event_type === "frontend_exception" && !config.captureErrors)
|
|
376
|
+
return null;
|
|
377
|
+
return canonical ? safeEvent : toLegacyNativeEvent(safeEvent);
|
|
378
|
+
}
|
|
379
|
+
hasNativeCapacity(highPriority) {
|
|
380
|
+
return this.pendingNativeCalls < (highPriority ? MAX_PENDING_NATIVE_CALLS : MAX_PENDING_LOW_PRIORITY_CALLS) &&
|
|
381
|
+
this.pendingNativeBytes < (highPriority ? MAX_PENDING_NATIVE_BYTES : MAX_PENDING_LOW_PRIORITY_BYTES);
|
|
382
|
+
}
|
|
383
|
+
releaseNativeCapacity(bytes) {
|
|
384
|
+
this.pendingNativeCalls -= 1;
|
|
385
|
+
this.pendingNativeBytes -= bytes;
|
|
386
|
+
}
|
|
234
387
|
canCapture(eventType, countTowardSession) {
|
|
235
388
|
if (!this.config.enabled || !this.config.projectToken) {
|
|
236
389
|
return false;
|
|
@@ -240,7 +393,13 @@ export class DebugBundleReactNativeClient {
|
|
|
240
393
|
return true;
|
|
241
394
|
}
|
|
242
395
|
mergeContext(context) {
|
|
243
|
-
|
|
396
|
+
try {
|
|
397
|
+
return { ...this.context, ...context };
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
// A hostile caller accessor cannot interrupt error reporting or application work.
|
|
401
|
+
return { ...this.context };
|
|
402
|
+
}
|
|
244
403
|
}
|
|
245
404
|
snapshotProbeData() {
|
|
246
405
|
if (!this.config.probeFlushOnError) {
|
|
@@ -255,6 +414,24 @@ export class DebugBundleReactNativeClient {
|
|
|
255
414
|
return this.jsProbeActivationExpiresAt > Date.now();
|
|
256
415
|
}
|
|
257
416
|
}
|
|
417
|
+
function utf8ByteLength(value) {
|
|
418
|
+
let bytes = 0;
|
|
419
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
420
|
+
const code = value.charCodeAt(index);
|
|
421
|
+
if (code < 0x80)
|
|
422
|
+
bytes += 1;
|
|
423
|
+
else if (code < 0x800)
|
|
424
|
+
bytes += 2;
|
|
425
|
+
else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length &&
|
|
426
|
+
value.charCodeAt(index + 1) >= 0xdc00 && value.charCodeAt(index + 1) <= 0xdfff) {
|
|
427
|
+
bytes += 4;
|
|
428
|
+
index += 1;
|
|
429
|
+
}
|
|
430
|
+
else
|
|
431
|
+
bytes += 3;
|
|
432
|
+
}
|
|
433
|
+
return bytes;
|
|
434
|
+
}
|
|
258
435
|
export function createDebugBundleClient(config = {}) {
|
|
259
436
|
return new DebugBundleReactNativeClient(config);
|
|
260
437
|
}
|
package/dist/console.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import type { DebugBundleClient } from "./types.js";
|
|
2
|
-
export declare function captureDebugBundleConsole(client: Pick<DebugBundleClient, "captureLog">
|
|
1
|
+
import type { DebugBundleClient, DebugBundleLogLevel } from "./types.js";
|
|
2
|
+
export declare function captureDebugBundleConsole(client: Pick<DebugBundleClient, "captureLog"> & {
|
|
3
|
+
isLogEnabled?: (level: DebugBundleLogLevel) => boolean;
|
|
4
|
+
}): () => void;
|
|
3
5
|
//# sourceMappingURL=console.d.ts.map
|
package/dist/console.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAIzE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,GAAG;IACxF,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC;CACxD,GAAG,MAAM,IAAI,CAoCb"}
|
package/dist/console.js
CHANGED
|
@@ -26,8 +26,13 @@ export function captureDebugBundleConsole(client) {
|
|
|
26
26
|
}
|
|
27
27
|
inCapture = true;
|
|
28
28
|
try {
|
|
29
|
+
if (client.isLogEnabled?.(level) === false)
|
|
30
|
+
return;
|
|
29
31
|
client.captureLog(args.map(String).join(" "), level, { source: "console" });
|
|
30
32
|
}
|
|
33
|
+
catch {
|
|
34
|
+
// Console instrumentation must not change host logging behavior.
|
|
35
|
+
}
|
|
31
36
|
finally {
|
|
32
37
|
inCapture = false;
|
|
33
38
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const DEFAULT_SENSITIVE_KEYS: readonly ["password", "secret", "token", "api_key", "apikey", "access_token", "refresh_token", "private_key", "authorization", "bearer", "cookie", "session_id", "passwd", "ssn", "credit_card", "card_number", "cvv", "cvc", "pin", "expiry", "phone", "otp", "verification_code"];
|
|
2
|
+
export declare function isSensitiveKey(key: string, sensitiveKeys: readonly string[]): boolean;
|
|
3
|
+
//# sourceMappingURL=privacy-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy-keys.d.ts","sourceRoot":"","sources":["../src/privacy-keys.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,qRAKzB,CAAC;AAeX,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAkBrF"}
|