@debugbundle/sdk-react-native 3.0.1 → 3.1.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 +16 -0
- package/DebugBundleReactNative.podspec +2 -2
- package/MIGRATION-3.1.md +116 -0
- package/README.md +37 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/debugbundle/reactnative/DebugBundleReactNativeModule.java +34 -3
- package/app.plugin.js +1 -1
- package/dist/NativeDebugBundleReactNative.d.ts +1 -0
- package/dist/NativeDebugBundleReactNative.d.ts.map +1 -1
- package/dist/before-send.d.ts.map +1 -1
- package/dist/before-send.js +9 -131
- package/dist/canonical-schema.d.ts +15 -0
- package/dist/canonical-schema.d.ts.map +1 -0
- package/dist/canonical-schema.js +139 -0
- package/dist/client-values.d.ts +17 -0
- package/dist/client-values.d.ts.map +1 -0
- package/dist/client-values.js +185 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +102 -205
- package/dist/diagnostic-budget.d.ts +16 -0
- package/dist/diagnostic-budget.d.ts.map +1 -0
- package/dist/diagnostic-budget.js +120 -0
- package/dist/event-schema-data.d.ts +2 -0
- package/dist/event-schema-data.d.ts.map +1 -0
- package/dist/event-schema-data.js +49 -0
- package/dist/evidence-capabilities.d.ts +5 -0
- package/dist/evidence-capabilities.d.ts.map +1 -0
- package/dist/evidence-capabilities.js +21 -0
- package/dist/evidence-controller.d.ts +24 -0
- package/dist/evidence-controller.d.ts.map +1 -0
- package/dist/evidence-controller.js +113 -0
- package/dist/evidence-types.d.ts +33 -0
- package/dist/evidence-types.d.ts.map +1 -0
- package/dist/evidence-types.js +1 -0
- package/dist/evidence-wire.d.ts +7 -0
- package/dist/evidence-wire.d.ts.map +1 -0
- package/dist/evidence-wire.js +132 -0
- package/dist/exception-evidence.d.ts +25 -0
- package/dist/exception-evidence.d.ts.map +1 -0
- package/dist/exception-evidence.js +175 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/owned-json.d.ts +11 -0
- package/dist/owned-json.d.ts.map +1 -0
- package/dist/owned-json.js +85 -0
- package/dist/privacy-keys.d.ts +2 -0
- package/dist/privacy-keys.d.ts.map +1 -1
- package/dist/privacy-keys.js +10 -2
- package/dist/privacy-telemetry.js +7 -7
- package/dist/privacy-text.d.ts +2 -2
- package/dist/privacy-text.d.ts.map +1 -1
- package/dist/privacy-text.js +35 -33
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +9 -7
- package/dist/safe-error-fields.d.ts +4 -0
- package/dist/safe-error-fields.d.ts.map +1 -0
- package/dist/safe-error-fields.js +54 -0
- package/dist/types.d.ts +21 -6
- package/dist/types.d.ts.map +1 -1
- package/ios/DebugBundleReactNative.swift +58 -4
- package/ios/DebugBundleReactNativeBridge.mm +4 -0
- package/package.json +3 -2
- package/src/NativeDebugBundleReactNative.ts +1 -0
- package/src/before-send.ts +10 -174
- package/src/canonical-schema.ts +108 -0
- package/src/client-values.ts +196 -0
- package/src/client.ts +92 -213
- package/src/diagnostic-budget.ts +90 -0
- package/src/event-schema-data.ts +49 -0
- package/src/evidence-capabilities.ts +19 -0
- package/src/evidence-controller.ts +89 -0
- package/src/evidence-types.ts +14 -0
- package/src/evidence-wire.ts +100 -0
- package/src/exception-evidence.ts +112 -0
- package/src/index.ts +6 -0
- package/src/owned-json.ts +60 -0
- package/src/privacy-keys.ts +12 -2
- package/src/privacy-telemetry.ts +9 -9
- package/src/privacy-text.ts +32 -31
- package/src/redaction.ts +8 -7
- package/src/safe-error-fields.ts +46 -0
- package/src/types.ts +23 -5
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type LegacyError = {
|
|
2
|
+
name: string;
|
|
3
|
+
message: string;
|
|
4
|
+
stack: string;
|
|
5
|
+
traceAvailable?: boolean;
|
|
6
|
+
};
|
|
7
|
+
type Frame = {
|
|
8
|
+
function: string | null;
|
|
9
|
+
file: string | null;
|
|
10
|
+
line: number | null;
|
|
11
|
+
column: number | null;
|
|
12
|
+
classification: "unknown" | "framework" | "sdk";
|
|
13
|
+
};
|
|
14
|
+
export declare function snapshotError(error: unknown, fields: string[]): LegacyError;
|
|
15
|
+
export declare function parseMobileStack(stack: string, maximum?: number): {
|
|
16
|
+
frames: Frame[];
|
|
17
|
+
status: string;
|
|
18
|
+
truncated: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** New cause traversal requires the engine's native Error brand. Hermes/JSC
|
|
21
|
+
* without it retain the outer error and explicitly report unsupported traversal.
|
|
22
|
+
* Portable JS cannot prove aggregate arrays are not proxies: no iterator is run. */
|
|
23
|
+
export declare function snapshotExceptionEvidence(error: unknown, legacy: LegacyError, fields: string[], fatal?: boolean, brand?: ((value: unknown) => boolean) | undefined): Record<string, unknown> | undefined;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=exception-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exception-evidence.d.ts","sourceRoot":"","sources":["../src/exception-evidence.ts"],"names":[],"mappings":"AAIA,KAAK,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAC9F,KAAK,KAAK,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,SAAS,GAAG,WAAW,GAAG,KAAK,CAAA;CAAE,CAAC;AAI3J,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAc3E;AAUD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CA0BrH;AAUD;;oFAEoF;AACpF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,UAAQ,EAC5G,KAAK,4CAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAsC1D"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { errorField, safeErrorStack, nativeBrand } from "./safe-error-fields.js";
|
|
2
|
+
import { sanitizeValue } from "./redaction.js";
|
|
3
|
+
import { utf8Bytes } from "./owned-json.js";
|
|
4
|
+
const text = (value, fallback) => typeof value === "string" && value ? value : fallback;
|
|
5
|
+
export function snapshotError(error, fields) {
|
|
6
|
+
let name = "Error", message = "Unknown error", stack;
|
|
7
|
+
try {
|
|
8
|
+
if (typeof error === "string")
|
|
9
|
+
message = error;
|
|
10
|
+
else if (error !== null && typeof error === "object") {
|
|
11
|
+
name = text(errorField(error, "name"), name);
|
|
12
|
+
message = text(errorField(error, "message"), message);
|
|
13
|
+
stack = safeErrorStack(error);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch { /* Legacy outer evidence is best effort; no application accessor is required. */ }
|
|
17
|
+
return {
|
|
18
|
+
name: text(sanitizeValue(name, { redactFields: fields }), "Error"),
|
|
19
|
+
message: text(sanitizeValue(message, { redactFields: fields }), "Unknown error"),
|
|
20
|
+
stack: text(sanitizeValue(text(stack, `${name}: ${message}`), { redactFields: fields, maxStringLength: 8192 }), `${name}: ${message}`),
|
|
21
|
+
traceAvailable: typeof stack === "string" && stack.length > 0
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function clip(text, maximum) {
|
|
25
|
+
let result = "", bytes = 0;
|
|
26
|
+
for (const character of text.slice(0, maximum)) {
|
|
27
|
+
const cost = utf8Bytes(character);
|
|
28
|
+
if (bytes + cost > maximum)
|
|
29
|
+
break;
|
|
30
|
+
result += character;
|
|
31
|
+
bytes += cost;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
export function parseMobileStack(stack, maximum = 32) {
|
|
36
|
+
const frames = [];
|
|
37
|
+
const lines = stack.slice(0, 16_384).split(/\r?\n/, 129);
|
|
38
|
+
let partial = false, truncated = stack.length > 16_384 || lines.length > 128;
|
|
39
|
+
for (const input of lines.slice(0, 128)) {
|
|
40
|
+
if (frames.length >= maximum) {
|
|
41
|
+
truncated = true;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
if (input.length > 2048)
|
|
45
|
+
truncated = true;
|
|
46
|
+
const line = input.trim().slice(0, 2048);
|
|
47
|
+
const named = /^at\s+(.+?)\s*\((?:address at )?(.+?):(\d+)(?::(\d+))?\)$/.exec(line);
|
|
48
|
+
const anonymous = named ? null : /^at\s+(.+?):(\d+)(?::(\d+))?$/.exec(line);
|
|
49
|
+
const webkit = named || anonymous ? null : /^(.*?)@(.+?):(\d+)(?::(\d+))?$/.exec(line);
|
|
50
|
+
const match = named ?? anonymous ?? webkit;
|
|
51
|
+
if (!match) {
|
|
52
|
+
if (/^at\s|@\S+:\d/.test(line))
|
|
53
|
+
partial = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
let file = named || webkit ? match[2] : match[1];
|
|
57
|
+
if (/^https?:\/\//i.test(file)) {
|
|
58
|
+
try {
|
|
59
|
+
const url = new URL(file);
|
|
60
|
+
file = `${url.protocol}//${url.host}${url.pathname}`;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
partial = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
file = file.split(/[?#]/, 1)[0];
|
|
69
|
+
const row = Number(named || webkit ? match[3] : match[2]);
|
|
70
|
+
const rawColumn = named || webkit ? match[4] : match[3];
|
|
71
|
+
const column = rawColumn === undefined ? null : Number(rawColumn);
|
|
72
|
+
if (!Number.isSafeInteger(row) || row <= 0 || column !== null && (!Number.isSafeInteger(column) || column < 0)) {
|
|
73
|
+
partial = true;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const fn = named || webkit ? clip(match[1].trim(), 256) || null : null;
|
|
77
|
+
frames.push({ function: fn, file: clip(file, 2048), line: row, column,
|
|
78
|
+
classification: /node_modules[/\\]@debugbundle[/\\]/.test(file) ? "sdk" : /node_modules[/\\]/.test(file) ? "framework" : "unknown" });
|
|
79
|
+
}
|
|
80
|
+
return { frames, status: truncated ? "truncated" : !frames.length ? "unparsed" : partial ? "partial" : "parsed", truncated };
|
|
81
|
+
}
|
|
82
|
+
function stackLinks(stack) {
|
|
83
|
+
return stack.replace(/https?:\/\/[^\s)]+/gi, source => {
|
|
84
|
+
const coordinate = /(:\d+(?::\d+)?)$/.exec(source)?.[1] ?? "";
|
|
85
|
+
const location = coordinate ? source.slice(0, -coordinate.length) : source;
|
|
86
|
+
try {
|
|
87
|
+
const url = new URL(location);
|
|
88
|
+
return `${url.protocol}//${url.host}${url.pathname}${coordinate}`;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return "[unavailable URL]";
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** New cause traversal requires the engine's native Error brand. Hermes/JSC
|
|
96
|
+
* without it retain the outer error and explicitly report unsupported traversal.
|
|
97
|
+
* Portable JS cannot prove aggregate arrays are not proxies: no iterator is run. */
|
|
98
|
+
export function snapshotExceptionEvidence(error, legacy, fields, fatal = false, brand = nativeBrand) {
|
|
99
|
+
const nodes = [], seen = new Map(), active = new Set();
|
|
100
|
+
let unsupported = brand === undefined, unsafe = false, truncated = false, totalFrames = 0;
|
|
101
|
+
const visit = (value, depth, root = false) => {
|
|
102
|
+
if (!root && (!brand || !brand(value))) {
|
|
103
|
+
unsupported = true;
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (typeof value === "object" && value !== null && active.has(value)) {
|
|
107
|
+
truncated = true;
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
if (depth > (fatal ? 1 : 4) || nodes.length >= (fatal ? 2 : 8)) {
|
|
111
|
+
truncated = true;
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (!root && seen.has(value))
|
|
115
|
+
return seen.get(value);
|
|
116
|
+
const snapshot = root ? legacy : snapshotError(value, fields);
|
|
117
|
+
const name = clip(snapshot.name, 256), message = clip(snapshot.message, fatal ? 512 : 2048);
|
|
118
|
+
const stack = stackLinks(clip(snapshot.stack, fatal ? 512 : 16_384));
|
|
119
|
+
const parsed = parseMobileStack(stack, fatal ? Math.min(4, 8 - totalFrames) : 32), frames = parsed.frames;
|
|
120
|
+
totalFrames += frames.length;
|
|
121
|
+
truncated ||= parsed.truncated;
|
|
122
|
+
const trace = snapshot.traceAvailable !== false;
|
|
123
|
+
const id = nodes.length;
|
|
124
|
+
const node = { id, name, message, stack: trace ? stack : null, frames: trace ? frames : [],
|
|
125
|
+
trace_origin: trace ? "runtime_reported" : "unavailable", parsing_status: trace ? parsed.status : "unavailable", relationships: [] };
|
|
126
|
+
nodes.push(node);
|
|
127
|
+
if (!brand || !brand(value)) {
|
|
128
|
+
unsupported = true;
|
|
129
|
+
return id;
|
|
130
|
+
}
|
|
131
|
+
seen.set(value, id);
|
|
132
|
+
active.add(value);
|
|
133
|
+
const cause = Object.getOwnPropertyDescriptor(value, "cause");
|
|
134
|
+
if (cause && !("value" in cause))
|
|
135
|
+
unsafe = true;
|
|
136
|
+
else if (cause?.value !== undefined) {
|
|
137
|
+
const target = visit(cause.value, depth + 1);
|
|
138
|
+
if (target !== null)
|
|
139
|
+
node.relationships.push({ kind: "cause", target_id: target });
|
|
140
|
+
}
|
|
141
|
+
const aggregate = Object.getOwnPropertyDescriptor(value, "errors");
|
|
142
|
+
if (aggregate) {
|
|
143
|
+
if (!("value" in aggregate))
|
|
144
|
+
unsafe = true;
|
|
145
|
+
else
|
|
146
|
+
unsupported = true;
|
|
147
|
+
}
|
|
148
|
+
active.delete(value);
|
|
149
|
+
return id;
|
|
150
|
+
};
|
|
151
|
+
try {
|
|
152
|
+
visit(error, 0, true);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
unsafe = true;
|
|
156
|
+
}
|
|
157
|
+
if (!nodes.length)
|
|
158
|
+
return undefined;
|
|
159
|
+
const graph = { version: 1, runtime: "react_native", root_id: 0, nodes, truncated,
|
|
160
|
+
unavailable_reason: unsafe ? "unsafe" : truncated ? "budget_exceeded" : unsupported ? "unsupported" : null };
|
|
161
|
+
const maximum = fatal ? 3584 : 31_000;
|
|
162
|
+
if (utf8Bytes(JSON.stringify(graph)) > maximum) {
|
|
163
|
+
graph.truncated = true;
|
|
164
|
+
graph.unavailable_reason = "budget_exceeded";
|
|
165
|
+
for (const node of nodes) {
|
|
166
|
+
node.stack = null;
|
|
167
|
+
node.frames = node.frames.slice(0, fatal ? 2 : 4);
|
|
168
|
+
node.parsing_status = "truncated";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (utf8Bytes(JSON.stringify(graph)) > maximum)
|
|
172
|
+
for (const node of nodes)
|
|
173
|
+
node.frames = [];
|
|
174
|
+
return utf8Bytes(JSON.stringify(graph)) <= maximum ? graph : undefined;
|
|
175
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,12 @@ import { DebugBundleReactNativeClient, createDebugBundleClient } from "./client.
|
|
|
2
2
|
import { captureDebugBundleConsole } from "./console.js";
|
|
3
3
|
import { installDebugBundleErrorHandlers } from "./errors.js";
|
|
4
4
|
import type { DebugBundleCaptureContext, DebugBundleBeforeSend, DebugBundleClient, DebugBundleConfig, DebugBundleLogLevel, DebugBundleProbeOptions, DebugBundleRequestInfo, DebugBundleResponseInfo, DebugBundleStatus } from "./types.js";
|
|
5
|
+
import type { EvidenceCapabilities, OperationScope, RecoveryObservation } from "./evidence-types.js";
|
|
5
6
|
export declare const DebugBundle: {
|
|
7
|
+
beginOperation(): OperationScope;
|
|
8
|
+
withOperationContext<T>(operation: (scope: OperationScope) => T): T;
|
|
9
|
+
captureRecovery(observation: RecoveryObservation): void;
|
|
10
|
+
readonly evidenceCapabilities: EvidenceCapabilities;
|
|
6
11
|
init(config: DebugBundleConfig): void;
|
|
7
12
|
captureException(error: unknown, context?: DebugBundleCaptureContext): void;
|
|
8
13
|
captureError(error: unknown, context?: DebugBundleCaptureContext): void;
|
|
@@ -21,5 +26,6 @@ export declare const DebugBundle: {
|
|
|
21
26
|
export { createDebugBundleClient, DebugBundleReactNativeClient };
|
|
22
27
|
export { captureDebugBundleConsole };
|
|
23
28
|
export { installDebugBundleErrorHandlers };
|
|
29
|
+
export type { EvidenceCapabilities, EvidenceFeature, EvidenceScope, OperationScope, RecoveryObservation } from "./evidence-types.js";
|
|
24
30
|
export type { DebugBundleCaptureContext, DebugBundleBeforeSend, DebugBundleClient, DebugBundleConfig, DebugBundleLogLevel, DebugBundleProbeOptions, DebugBundleRequestInfo, DebugBundleResponseInfo, DebugBundleStatus };
|
|
25
31
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EACV,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EACV,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAIrG,eAAO,MAAM,WAAW;sBACJ,cAAc;yBACX,CAAC,aAAa,CAAC,KAAK,EAAE,cAAc,KAAK,CAAC,GAAG,CAAC;iCACtC,mBAAmB,GAAG,IAAI;mCAC3B,oBAAoB;iBACnC,iBAAiB,GAAG,IAAI;4BAWb,OAAO,YAAY,yBAAyB,GAAG,IAAI;wBAGvD,OAAO,YAAY,yBAAyB,GAAG,IAAI;wBAGnD,MAAM,UAAU,mBAAmB,YAAY,yBAAyB,GAAG,IAAI;4BAG3E,sBAAsB,YAAY,uBAAuB,YAAY,yBAAyB,GAAG,IAAI;4BAGrG,MAAM,UAAU,mBAAmB,YAAY,yBAAyB,GAAG,IAAI;oBAGvF,MAAM,SAAS,OAAO,GAAG,IAAI;iBAGhC,MAAM,QAAQ,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,YAAY,uBAAuB,GAAG,IAAI;aAGrF,OAAO,CAAC,IAAI,CAAC;qCAGW,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;2BAGnC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;6BAG3C,MAAM,mBAAmB,MAAM,GAAG,IAAI,WAAW,MAAM,GAAG,IAAI;qBAGzE,iBAAiB;0BAGZ,MAAM,GAAG,IAAI;CAGjC,CAAC;AAEF,OAAO,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,CAAC;AACrC,OAAO,EAAE,+BAA+B,EAAE,CAAC;AAC3C,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACrI,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EAClB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,10 @@ import { captureDebugBundleConsole } from "./console.js";
|
|
|
3
3
|
import { installDebugBundleErrorHandlers } from "./errors.js";
|
|
4
4
|
const singleton = new DebugBundleReactNativeClient();
|
|
5
5
|
export const DebugBundle = {
|
|
6
|
+
beginOperation() { return singleton.beginOperation(); },
|
|
7
|
+
withOperationContext(operation) { return singleton.withOperationContext(operation); },
|
|
8
|
+
captureRecovery(observation) { singleton.captureRecovery(observation); },
|
|
9
|
+
get evidenceCapabilities() { return singleton.evidenceCapabilities; },
|
|
6
10
|
init(config) {
|
|
7
11
|
singleton.init(config);
|
|
8
12
|
if (config.captureErrors !== false) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type OwnedJSON = null | boolean | number | string | OwnedJSON[] | {
|
|
2
|
+
[key: string]: OwnedJSON;
|
|
3
|
+
};
|
|
4
|
+
/** Exhausted ownership cannot safely fall back to pre-hook application data. */
|
|
5
|
+
export declare class EventValidationUnavailable extends Error {
|
|
6
|
+
}
|
|
7
|
+
export declare function utf8Bytes(value: string): number;
|
|
8
|
+
/** Bounded plain JSON copy; no toJSON, property getters, foreign iterators or formatters. */
|
|
9
|
+
export declare function ownJSON(input: unknown): OwnedJSON;
|
|
10
|
+
export declare function jsonObject(value: unknown): Record<string, OwnedJSON> | null;
|
|
11
|
+
//# sourceMappingURL=owned-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owned-json.d.ts","sourceRoot":"","sources":["../src/owned-json.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtG,gFAAgF;AAChF,qBAAa,0BAA2B,SAAQ,KAAK;CAAG;AAExD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW/C;AAED,6FAA6F;AAC7F,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAoCjD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAI,CAE3E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Exhausted ownership cannot safely fall back to pre-hook application data. */
|
|
2
|
+
export class EventValidationUnavailable extends Error {
|
|
3
|
+
}
|
|
4
|
+
export function utf8Bytes(value) {
|
|
5
|
+
let bytes = 0;
|
|
6
|
+
for (let index = 0; index < value.length; index++) {
|
|
7
|
+
const code = value.charCodeAt(index);
|
|
8
|
+
if (code < 0x80)
|
|
9
|
+
bytes++;
|
|
10
|
+
else if (code < 0x800)
|
|
11
|
+
bytes += 2;
|
|
12
|
+
else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length &&
|
|
13
|
+
value.charCodeAt(index + 1) >= 0xdc00 && value.charCodeAt(index + 1) <= 0xdfff) {
|
|
14
|
+
bytes += 4;
|
|
15
|
+
index++;
|
|
16
|
+
}
|
|
17
|
+
else
|
|
18
|
+
bytes += 3;
|
|
19
|
+
}
|
|
20
|
+
return bytes;
|
|
21
|
+
}
|
|
22
|
+
/** Bounded plain JSON copy; no toJSON, property getters, foreign iterators or formatters. */
|
|
23
|
+
export function ownJSON(input) {
|
|
24
|
+
let nodes = 4096, remaining = 8 * 1024 * 1024;
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
const use = (amount) => { remaining -= amount; if (remaining < 0)
|
|
27
|
+
throw new EventValidationUnavailable(); };
|
|
28
|
+
const visit = (value, depth) => {
|
|
29
|
+
if (--nodes < 0 || depth > 16)
|
|
30
|
+
throw new EventValidationUnavailable();
|
|
31
|
+
if (value === null || typeof value === "boolean") {
|
|
32
|
+
use(5);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
36
|
+
use(24);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
if (value.length > remaining)
|
|
41
|
+
throw new EventValidationUnavailable();
|
|
42
|
+
use(utf8Bytes(value) + 2);
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
if (typeof value !== "object" || value === null || seen.has(value))
|
|
46
|
+
throw new TypeError("invalid JSON");
|
|
47
|
+
seen.add(value);
|
|
48
|
+
try {
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
if (value.length > 256)
|
|
51
|
+
throw new EventValidationUnavailable();
|
|
52
|
+
use(value.length + 2);
|
|
53
|
+
return Array.from({ length: value.length }, (_, index) => {
|
|
54
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
55
|
+
if (!descriptor || !("value" in descriptor))
|
|
56
|
+
throw new TypeError("invalid JSON");
|
|
57
|
+
return visit(descriptor.value, depth + 1);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
61
|
+
throw new TypeError("invalid JSON");
|
|
62
|
+
const keys = Object.keys(value);
|
|
63
|
+
if (keys.length > 256)
|
|
64
|
+
throw new EventValidationUnavailable();
|
|
65
|
+
const output = Object.create(null);
|
|
66
|
+
for (const key of keys) {
|
|
67
|
+
if (key.length > remaining)
|
|
68
|
+
throw new EventValidationUnavailable();
|
|
69
|
+
use(utf8Bytes(key) + 4);
|
|
70
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
71
|
+
if (!descriptor || !("value" in descriptor))
|
|
72
|
+
throw new TypeError("invalid JSON");
|
|
73
|
+
output[key] = visit(descriptor.value, depth + 1);
|
|
74
|
+
}
|
|
75
|
+
return output;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
seen.delete(value);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return visit(input, 0);
|
|
82
|
+
}
|
|
83
|
+
export function jsonObject(value) {
|
|
84
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
85
|
+
}
|
package/dist/privacy-keys.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
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
2
|
export declare function isSensitiveKey(key: string, sensitiveKeys: readonly string[]): boolean;
|
|
3
|
+
/** Operation-owned lookup: bounded caller policy never enters a global cache. */
|
|
4
|
+
export declare function createSensitiveKeyMatcher(sensitiveKeys: readonly string[]): (key: string) => boolean;
|
|
3
5
|
//# sourceMappingURL=privacy-keys.d.ts.map
|
|
@@ -1 +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,
|
|
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,CAErF;AAED,iFAAiF;AACjF,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAIpG"}
|
package/dist/privacy-keys.js
CHANGED
|
@@ -16,11 +16,19 @@ function splitKeyIntoSegments(key) {
|
|
|
16
16
|
.filter((segment) => segment.length > 0);
|
|
17
17
|
}
|
|
18
18
|
export function isSensitiveKey(key, sensitiveKeys) {
|
|
19
|
+
return createSensitiveKeyMatcher(sensitiveKeys)(key);
|
|
20
|
+
}
|
|
21
|
+
/** Operation-owned lookup: bounded caller policy never enters a global cache. */
|
|
22
|
+
export function createSensitiveKeyMatcher(sensitiveKeys) {
|
|
23
|
+
const exact = new Set(sensitiveKeys);
|
|
24
|
+
const canonicalSensitiveKeys = new Set(sensitiveKeys.map(canonicalizeSensitiveKey));
|
|
25
|
+
return key => matchesSensitiveKey(key, exact, canonicalSensitiveKeys);
|
|
26
|
+
}
|
|
27
|
+
function matchesSensitiveKey(key, exact, canonicalSensitiveKeys) {
|
|
19
28
|
const normalized = key.trim().toLowerCase();
|
|
20
|
-
if (
|
|
29
|
+
if (exact.has(normalized)) {
|
|
21
30
|
return true;
|
|
22
31
|
}
|
|
23
|
-
const canonicalSensitiveKeys = new Set(sensitiveKeys.map(canonicalizeSensitiveKey));
|
|
24
32
|
const segments = splitKeyIntoSegments(key);
|
|
25
33
|
for (let start = 0; start < segments.length; start += 1) {
|
|
26
34
|
let combined = "";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_SENSITIVE_KEYS,
|
|
2
|
-
import {
|
|
1
|
+
import { DEFAULT_SENSITIVE_KEYS, createSensitiveKeyMatcher } from "./privacy-keys.js";
|
|
2
|
+
import { createCredentialTextScrubber } from "./privacy-text.js";
|
|
3
3
|
const REDACTED = "[REDACTED]";
|
|
4
4
|
const MAX_DEPTH = 16;
|
|
5
5
|
const MAX_NODES = 4096;
|
|
@@ -56,7 +56,7 @@ function sanitizeString(value, state, allowStructured) {
|
|
|
56
56
|
// Malformed structured content with a recognizable sensitive label is withheld below.
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
const result =
|
|
59
|
+
const result = state.scrubText(value);
|
|
60
60
|
if (result !== value)
|
|
61
61
|
state.redactionCount += 1;
|
|
62
62
|
if ((value.startsWith("{") || value.startsWith("[")) && result === value &&
|
|
@@ -108,7 +108,7 @@ function sanitizeValue(value, state, depth, allowStructured) {
|
|
|
108
108
|
state.textBytes += byteLength(key);
|
|
109
109
|
if (state.textBytes > state.maxTotalBytes)
|
|
110
110
|
throw new BudgetExceeded();
|
|
111
|
-
const safeKey =
|
|
111
|
+
const safeKey = state.scrubText(key);
|
|
112
112
|
if (safeKey !== key) {
|
|
113
113
|
state.redactionCount += 1;
|
|
114
114
|
continue;
|
|
@@ -116,7 +116,7 @@ function sanitizeValue(value, state, depth, allowStructured) {
|
|
|
116
116
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
117
117
|
if (descriptor === undefined || !("value" in descriptor))
|
|
118
118
|
throw new TypeError("unsafe_input");
|
|
119
|
-
const safeValue =
|
|
119
|
+
const safeValue = state.sensitiveKey(key)
|
|
120
120
|
? mask(state)
|
|
121
121
|
: sanitizeValue(descriptor.value, state, depth + 1, allowStructured);
|
|
122
122
|
Object.defineProperty(result, key, {
|
|
@@ -148,8 +148,8 @@ export function sanitizeTelemetry(value, options = {}) {
|
|
|
148
148
|
textBytes: 0,
|
|
149
149
|
redactionCount: 0,
|
|
150
150
|
seen: new WeakSet(),
|
|
151
|
-
|
|
152
|
-
extra,
|
|
151
|
+
sensitiveKey: createSensitiveKeyMatcher([...DEFAULT_SENSITIVE_KEYS, ...extra]),
|
|
152
|
+
scrubText: createCredentialTextScrubber(extra),
|
|
153
153
|
maxTotalBytes
|
|
154
154
|
};
|
|
155
155
|
const sanitized = sanitizeValue(value, state, 0, true);
|
package/dist/privacy-text.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export declare function
|
|
1
|
+
/** Compile each caller rule once per bounded protection operation, not per scalar. */
|
|
2
|
+
export declare function createCredentialTextScrubber(additionalKeys: readonly string[]): (input: string, scanUrls?: boolean) => string;
|
|
3
3
|
//# sourceMappingURL=privacy-text.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy-text.d.ts","sourceRoot":"","sources":["../src/privacy-text.ts"],"names":[],"mappings":"AAyDA,
|
|
1
|
+
{"version":3,"file":"privacy-text.d.ts","sourceRoot":"","sources":["../src/privacy-text.ts"],"names":[],"mappings":"AAyDA,sFAAsF;AACtF,wBAAgB,4BAA4B,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,MAAM,CA4B7H"}
|
package/dist/privacy-text.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_SENSITIVE_KEYS,
|
|
1
|
+
import { DEFAULT_SENSITIVE_KEYS, createSensitiveKeyMatcher } from "./privacy-keys.js";
|
|
2
2
|
const REDACTED = "[REDACTED]";
|
|
3
3
|
const BUILTIN_LABELS = [...DEFAULT_SENSITIVE_KEYS, "client_secret", "x_api_key",
|
|
4
4
|
"accessToken", "refreshToken", "privateKey", "clientSecret"];
|
|
@@ -29,7 +29,7 @@ function isValidCard(value) {
|
|
|
29
29
|
}
|
|
30
30
|
return sum % 10 === 0;
|
|
31
31
|
}
|
|
32
|
-
function scrubUrl(raw,
|
|
32
|
+
function scrubUrl(raw, scrub, sensitive) {
|
|
33
33
|
try {
|
|
34
34
|
const url = new URL(raw);
|
|
35
35
|
if (url.username || url.password) {
|
|
@@ -38,9 +38,9 @@ function scrubUrl(raw, keys) {
|
|
|
38
38
|
}
|
|
39
39
|
const nextQuery = new URLSearchParams();
|
|
40
40
|
for (const [key, value] of url.searchParams) {
|
|
41
|
-
if (key.length > 128 ||
|
|
41
|
+
if (key.length > 128 || scrub(key, false) !== key)
|
|
42
42
|
continue;
|
|
43
|
-
nextQuery.append(key,
|
|
43
|
+
nextQuery.append(key, sensitive(key) || scrub(value, false) !== value ? REDACTED : value);
|
|
44
44
|
}
|
|
45
45
|
url.search = nextQuery.toString();
|
|
46
46
|
url.hash = "";
|
|
@@ -51,35 +51,37 @@ function scrubUrl(raw, keys) {
|
|
|
51
51
|
return REDACTED;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
/**
|
|
55
|
-
export function
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
54
|
+
/** Compile each caller rule once per bounded protection operation, not per scalar. */
|
|
55
|
+
export function createCredentialTextScrubber(additionalKeys) {
|
|
56
|
+
const sensitive = createSensitiveKeyMatcher([...DEFAULT_SENSITIVE_KEYS, ...additionalKeys]);
|
|
57
|
+
const patterns = additionalKeys.map(key => {
|
|
58
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
59
|
+
return new RegExp(`\\b(${escaped})\\b(["']?\\s*[:=]\\s*)(?:"[^"]*"|'[^']*'|[^\\s&,;]+)`, "gi");
|
|
60
|
+
});
|
|
61
|
+
const scrub = (input, scanUrls = true) => {
|
|
62
|
+
if (PEM_START.test(input) && !PEM_COMPLETE.test(input))
|
|
65
63
|
return REDACTED;
|
|
64
|
+
let output = input;
|
|
65
|
+
if (/(?:password|token|secret|authorization|cookie)%3[ad]/i.test(output)) {
|
|
66
|
+
try {
|
|
67
|
+
output = decodeURIComponent(output);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return REDACTED;
|
|
71
|
+
}
|
|
66
72
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return output.replace(URL_TEXT, (matched) => {
|
|
82
|
-
const trailing = matched.match(/[).,;]+$/)?.[0] ?? "";
|
|
83
|
-
return scrubUrl(matched.slice(0, matched.length - trailing.length), keys) + trailing;
|
|
84
|
-
});
|
|
73
|
+
output = output.replace(PEM, REDACTED).replace(HEADER, "$1: [REDACTED]");
|
|
74
|
+
output = output.replace(BEARER, "$1 [REDACTED]").replace(DEBUGBUNDLE_TOKEN, REDACTED);
|
|
75
|
+
output = output.replace(ASSIGNMENT, "$1$2[REDACTED]");
|
|
76
|
+
for (const pattern of patterns)
|
|
77
|
+
output = output.replace(pattern, "$1$2[REDACTED]");
|
|
78
|
+
output = output.replace(CARD, (candidate) => isValidCard(candidate) ? REDACTED : candidate);
|
|
79
|
+
if (!scanUrls)
|
|
80
|
+
return output;
|
|
81
|
+
return output.replace(URL_TEXT, (matched) => {
|
|
82
|
+
const trailing = matched.match(/[).,;]+$/)?.[0] ?? "";
|
|
83
|
+
return scrubUrl(matched.slice(0, matched.length - trailing.length), scrub, sensitive) + trailing;
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
return scrub;
|
|
85
87
|
}
|
package/dist/redaction.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,wBAAwB,UAQpC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAUpF;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC3C,SAAS,GAAE,MAAM,EAA6B,EAC9C,OAAO,GAAE,eAAoB,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAaxB"}
|
package/dist/redaction.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_SENSITIVE_KEYS, isSensitiveKey as privacySensitiveKey } from "./privacy-keys.js";
|
|
2
2
|
import { sanitizeTelemetry } from "./privacy-telemetry.js";
|
|
3
|
+
import { errorField, safeErrorStack } from "./safe-error-fields.js";
|
|
3
4
|
const DEFAULT_REDACT_FIELDS = [
|
|
4
5
|
...DEFAULT_SENSITIVE_KEYS
|
|
5
6
|
];
|
|
@@ -69,9 +70,6 @@ function sanitize(value, options, depth, visited, key) {
|
|
|
69
70
|
if (typeof value === "function" || typeof value === "symbol") {
|
|
70
71
|
return `[${typeof value}]`;
|
|
71
72
|
}
|
|
72
|
-
if (value instanceof Error) {
|
|
73
|
-
return sanitizeError(value, options, depth, visited);
|
|
74
|
-
}
|
|
75
73
|
if (depth >= options.maxDepth) {
|
|
76
74
|
return "[Truncated: depth]";
|
|
77
75
|
}
|
|
@@ -80,6 +78,8 @@ function sanitize(value, options, depth, visited, key) {
|
|
|
80
78
|
return "[Circular]";
|
|
81
79
|
}
|
|
82
80
|
visited.add(value);
|
|
81
|
+
if (value instanceof Error)
|
|
82
|
+
return sanitizeError(value, options, depth, visited);
|
|
83
83
|
if (Array.isArray(value)) {
|
|
84
84
|
return value
|
|
85
85
|
.slice(0, options.maxArrayLength)
|
|
@@ -94,11 +94,13 @@ function sanitize(value, options, depth, visited, key) {
|
|
|
94
94
|
return String(value);
|
|
95
95
|
}
|
|
96
96
|
function sanitizeError(error, options, depth, visited) {
|
|
97
|
+
const name = errorField(error, "name"), message = errorField(error, "message");
|
|
98
|
+
const stack = safeErrorStack(error), cause = Object.getOwnPropertyDescriptor(error, "cause");
|
|
97
99
|
return {
|
|
98
|
-
name: truncate(
|
|
99
|
-
message: truncate(
|
|
100
|
-
stack: typeof
|
|
101
|
-
cause:
|
|
100
|
+
name: truncate(typeof name === "string" && name ? name : "Error", options.maxStringLength, options.redactFields),
|
|
101
|
+
message: truncate(typeof message === "string" ? message : "", options.maxStringLength, options.redactFields),
|
|
102
|
+
stack: typeof stack === "string" ? truncate(stack, options.maxStringLength * 4, options.redactFields) : null,
|
|
103
|
+
cause: cause && "value" in cause ? sanitize(cause.value, options, depth + 1, visited, "cause") : null
|
|
102
104
|
};
|
|
103
105
|
}
|
|
104
106
|
function truncate(value, maxLength, fields) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-error-fields.d.ts","sourceRoot":"","sources":["../src/safe-error-fields.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,WACa,OAAO,KAAK,OAAO,aAAY,CAAC;AAGrE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAW9D;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAyBrD"}
|