@devaccel-labs/app-comments-toolbar 1.1.0 → 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/README.md +148 -30
- package/dist/react.js +417 -64
- package/dist/server.js +342 -14
- package/dist/types/capture/api.d.ts +30 -0
- package/dist/types/capture/editor.d.ts +10 -0
- package/dist/types/capture/ignore.d.ts +19 -0
- package/dist/types/capture/index.d.ts +1 -0
- package/dist/types/capture/picker.d.ts +9 -0
- package/dist/types/capture/picture.d.ts +29 -0
- package/dist/types/capture/surface.d.ts +39 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/resolve.d.ts +2 -1
- package/dist/types/core/types.d.ts +11 -0
- package/dist/types/debug-context.d.ts +7 -0
- package/dist/types/dictation.d.ts +9 -0
- package/dist/types/embed/controller.d.ts +61 -0
- package/dist/types/embed/index.d.ts +1 -0
- package/dist/types/embed/options.d.ts +35 -0
- package/dist/types/help/index.d.ts +1 -0
- package/dist/types/help/panel-index.d.ts +1 -0
- package/dist/types/help/panel-styles.d.ts +1 -0
- package/dist/types/help/panel.d.ts +4 -0
- package/dist/types/help/pill-styles.d.ts +11 -0
- package/dist/types/help/pill.d.ts +44 -0
- package/dist/types/help/react.d.ts +12 -0
- package/dist/types/help/requests.d.ts +2 -0
- package/dist/types/help/types.d.ts +119 -0
- package/dist/types/help/wire.d.ts +61 -0
- package/dist/types/native-wire.d.ts +63 -0
- package/dist/types/native.d.ts +43 -0
- package/dist/types/react.d.ts +7 -0
- package/dist/types/replay.d.ts +6 -0
- package/dist/types/runtime/api.d.ts +250 -0
- package/dist/types/runtime/attachment-model.d.ts +16 -0
- package/dist/types/runtime/attachments.d.ts +43 -7
- package/dist/types/runtime/boot.d.ts +39 -0
- package/dist/types/runtime/browser-label.d.ts +5 -0
- package/dist/types/runtime/build-id.d.ts +1 -0
- package/dist/types/runtime/capture-ui.d.ts +45 -21
- package/dist/types/runtime/clarity-model.d.ts +260 -0
- package/dist/types/runtime/clarity-ui.d.ts +62 -0
- package/dist/types/runtime/client.d.ts +6 -197
- package/dist/types/runtime/composer-size.d.ts +30 -0
- package/dist/types/runtime/dashboard.d.ts +17 -2
- package/dist/types/runtime/debug-context-core.d.ts +88 -0
- package/dist/types/runtime/debug-context-host.d.ts +39 -0
- package/dist/types/runtime/debug-context-model.d.ts +352 -0
- package/dist/types/runtime/debug-context-redact.d.ts +48 -0
- package/dist/types/runtime/debug-context.d.ts +36 -0
- package/dist/types/runtime/details-model.d.ts +7 -0
- package/dist/types/runtime/details.d.ts +7 -5
- package/dist/types/runtime/dictation-engine.d.ts +98 -0
- package/dist/types/runtime/dictation-model.d.ts +28 -0
- package/dist/types/runtime/dictation-ui.d.ts +56 -0
- package/dist/types/runtime/dictation.d.ts +66 -0
- package/dist/types/runtime/draft-dock.d.ts +32 -0
- package/dist/types/runtime/draft-model.d.ts +104 -0
- package/dist/types/runtime/draft-store.d.ts +15 -0
- package/dist/types/runtime/edits.d.ts +1 -1
- package/dist/types/runtime/font.d.ts +16 -0
- package/dist/types/runtime/format.d.ts +30 -1
- package/dist/types/runtime/host-watch.d.ts +20 -0
- package/dist/types/runtime/icons.d.ts +16 -2
- package/dist/types/runtime/index.d.ts +2 -0
- package/dist/types/runtime/ink.d.ts +8 -0
- package/dist/types/runtime/kind-model.d.ts +14 -0
- package/dist/types/runtime/live-model.d.ts +25 -0
- package/dist/types/runtime/loader.d.ts +5 -3
- package/dist/types/runtime/markup-editor.d.ts +54 -0
- package/dist/types/runtime/markup-model.d.ts +126 -0
- package/dist/types/runtime/markup.d.ts +69 -0
- package/dist/types/runtime/mention-parse.d.ts +18 -0
- package/dist/types/runtime/mentions.d.ts +4 -4
- package/dist/types/runtime/page-background.d.ts +9 -0
- package/dist/types/runtime/page-image.d.ts +64 -11
- package/dist/types/runtime/placement.d.ts +6 -3
- package/dist/types/runtime/record.d.ts +18 -2
- package/dist/types/runtime/related.d.ts +1 -1
- package/dist/types/runtime/replay-model.d.ts +80 -0
- package/dist/types/runtime/replay.d.ts +5 -38
- package/dist/types/runtime/screen.d.ts +47 -0
- package/dist/types/runtime/screenshot.d.ts +60 -8
- package/dist/types/runtime/session-source.d.ts +45 -0
- package/dist/types/runtime/session.d.ts +20 -18
- package/dist/types/runtime/shield.d.ts +9 -0
- package/dist/types/runtime/shot.d.ts +45 -0
- package/dist/types/runtime/sketch.d.ts +26 -0
- package/dist/types/runtime/styles.d.ts +1 -1
- package/dist/types/runtime/sync.d.ts +1 -0
- package/dist/types/runtime/thread-model.d.ts +18 -0
- package/dist/types/runtime/ticket-form-model.d.ts +57 -0
- package/dist/types/runtime/ticket-form.d.ts +9 -0
- package/dist/types/runtime/toolbar.d.ts +37 -1
- package/dist/types/runtime/ui-scale.d.ts +17 -0
- package/dist/types/runtime/update.d.ts +32 -8
- package/dist/types/runtime/use-debug-context.d.ts +27 -0
- package/dist/types/server/index.d.ts +22 -0
- package/dist/types/server/support.d.ts +20 -0
- package/dist/types/server.d.ts +2 -0
- package/dist/types/vite/plugin.d.ts +15 -0
- package/dist/vite.js +121 -41
- package/package.json +24 -11
- package/src/capture/api.ts +128 -0
- package/src/capture/editor.tsx +178 -0
- package/src/capture/ignore.ts +38 -0
- package/src/capture/index.ts +27 -0
- package/src/capture/picker.tsx +171 -0
- package/src/capture/picture.ts +90 -0
- package/src/capture/surface.tsx +185 -0
- package/src/compiler/annotate.ts +202 -0
- package/src/compiler/babel.d.ts +11 -0
- package/src/compiler/enabled.ts +55 -0
- package/src/compiler/index.ts +2 -0
- package/src/core/index.ts +8 -0
- package/src/core/resolve.ts +307 -0
- package/src/core/types.ts +58 -0
- package/src/debug-context.ts +47 -0
- package/src/dictation.ts +17 -0
- package/src/embed/controller.ts +215 -0
- package/src/embed/index.ts +68 -0
- package/src/embed/options.ts +100 -0
- package/src/help/index.ts +38 -0
- package/src/help/panel-index.ts +17 -0
- package/src/help/panel-styles.ts +123 -0
- package/src/help/panel.ts +840 -0
- package/src/help/pill-styles.ts +75 -0
- package/src/help/pill.ts +338 -0
- package/src/help/react.tsx +143 -0
- package/src/help/requests.ts +111 -0
- package/src/help/types.ts +124 -0
- package/src/help/wire.ts +55 -0
- package/src/native-wire.ts +124 -0
- package/src/native.ts +282 -0
- package/src/react.ts +27 -0
- package/src/replay.ts +13 -0
- package/src/runtime/api.ts +531 -0
- package/src/runtime/attachment-model.ts +42 -0
- package/src/runtime/attachments.tsx +1260 -0
- package/src/runtime/boot.ts +95 -0
- package/src/runtime/browser-label.ts +31 -0
- package/src/runtime/build-id.ts +10 -0
- package/src/runtime/capture-ui.tsx +306 -0
- package/src/runtime/capture.ts +508 -0
- package/src/runtime/clarity-model.ts +634 -0
- package/src/runtime/clarity-ui.tsx +454 -0
- package/src/runtime/client.ts +151 -0
- package/src/runtime/composer-size.tsx +146 -0
- package/src/runtime/dashboard.tsx +1130 -0
- package/src/runtime/debug-context-core.ts +681 -0
- package/src/runtime/debug-context-host.ts +56 -0
- package/src/runtime/debug-context-model.ts +1319 -0
- package/src/runtime/debug-context-redact.ts +259 -0
- package/src/runtime/debug-context.ts +613 -0
- package/src/runtime/details-model.ts +30 -0
- package/src/runtime/details.tsx +395 -0
- package/src/runtime/dictation-engine.ts +616 -0
- package/src/runtime/dictation-model.ts +79 -0
- package/src/runtime/dictation-ui.tsx +363 -0
- package/src/runtime/dictation.ts +271 -0
- package/src/runtime/dock.ts +108 -0
- package/src/runtime/draft-dock.tsx +124 -0
- package/src/runtime/draft-model.ts +251 -0
- package/src/runtime/draft-store.ts +149 -0
- package/src/runtime/edits.tsx +165 -0
- package/src/runtime/font.ts +42 -0
- package/src/runtime/format.ts +129 -0
- package/src/runtime/freshness.ts +23 -0
- package/src/runtime/handoff.ts +76 -0
- package/src/runtime/host-watch.ts +64 -0
- package/src/runtime/icons.tsx +295 -0
- package/src/runtime/idle.ts +20 -0
- package/src/runtime/index.ts +11 -0
- package/src/runtime/ink.ts +6 -0
- package/src/runtime/kind-model.ts +20 -0
- package/src/runtime/live-model.ts +32 -0
- package/src/runtime/loader.tsx +87 -0
- package/src/runtime/markup-editor.tsx +1333 -0
- package/src/runtime/markup-model.ts +485 -0
- package/src/runtime/markup.ts +382 -0
- package/src/runtime/members.ts +58 -0
- package/src/runtime/mention-parse.ts +56 -0
- package/src/runtime/mentions.tsx +205 -0
- package/src/runtime/page-background.ts +51 -0
- package/src/runtime/page-image.ts +1592 -0
- package/src/runtime/placement.ts +336 -0
- package/src/runtime/record.ts +402 -0
- package/src/runtime/redact.ts +30 -0
- package/src/runtime/related.ts +34 -0
- package/src/runtime/render-page.ts +2 -0
- package/src/runtime/replay-model.ts +232 -0
- package/src/runtime/replay.ts +173 -0
- package/src/runtime/runtime-version.ts +7 -0
- package/src/runtime/runtime.tsx +51 -0
- package/src/runtime/screen.ts +182 -0
- package/src/runtime/screenshot.ts +260 -0
- package/src/runtime/session-source.ts +112 -0
- package/src/runtime/session.ts +108 -0
- package/src/runtime/shield.ts +34 -0
- package/src/runtime/shot.tsx +422 -0
- package/src/runtime/sketch.ts +139 -0
- package/src/runtime/styles.ts +1541 -0
- package/src/runtime/sync.ts +300 -0
- package/src/runtime/thread-model.ts +71 -0
- package/src/runtime/ticket-form-model.ts +128 -0
- package/src/runtime/ticket-form.tsx +342 -0
- package/src/runtime/toolbar.tsx +4427 -0
- package/src/runtime/ui-scale.ts +75 -0
- package/src/runtime/update.ts +98 -0
- package/src/runtime/use-debug-context.ts +81 -0
- package/src/server/index.ts +133 -0
- package/src/server/support.ts +499 -0
- package/src/server.ts +4 -0
- package/src/vite/plugin.ts +102 -0
- package/src/vite.ts +1 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction for the debug context file. DOM-free: the toolbar runs it in the browser before upload, the
|
|
3
|
+
* service runs it again on every stored file, and native clients share it.
|
|
4
|
+
*
|
|
5
|
+
* Over-redact rather than under-redact. Every rule here is applied to text an app wrote (console lines, error
|
|
6
|
+
* messages, response excerpts, store values), so it has to catch secrets and personal data nobody labelled.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** A JSON object whose keys are not known ahead: store state, parsed bodies. Values are redacted before use. */
|
|
10
|
+
export type JsonBag = { [key: string]: unknown };
|
|
11
|
+
|
|
12
|
+
/** Counts what was taken out, so a reader knows something is missing instead of assuming it never happened. */
|
|
13
|
+
export type RedactionTally = { masked: number; dropped: number };
|
|
14
|
+
|
|
15
|
+
export function newTally(): RedactionTally {
|
|
16
|
+
return { masked: 0, dropped: 0 };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type Rule = readonly [RegExp, string];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Secrets first, then personal data. Each pattern is global; order matters where one could match inside
|
|
23
|
+
* another (a JWT before a long token, a card number before a phone number).
|
|
24
|
+
*/
|
|
25
|
+
const RULES: readonly Rule[] = [
|
|
26
|
+
[/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g, "[secret]"],
|
|
27
|
+
[/\bbearer\s+[a-z0-9._~+/=-]{8,}/gi, "Bearer [secret]"],
|
|
28
|
+
[/\bbasic\s+[a-z0-9+/=]{12,}/gi, "Basic [secret]"],
|
|
29
|
+
[/\beyJ[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\b/g, "[secret]"],
|
|
30
|
+
[/\b(?:sk|pk|rk)_(?:live|test)_[a-zA-Z0-9]{8,}\b/g, "[secret]"],
|
|
31
|
+
[/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, "[secret]"],
|
|
32
|
+
[/\bgh[pousr]_[A-Za-z0-9]{30,}\b/g, "[secret]"],
|
|
33
|
+
[/\bgithub_pat_[A-Za-z0-9_]{30,}\b/g, "[secret]"],
|
|
34
|
+
[/\bxox[abposr]-[A-Za-z0-9-]{10,}\b/g, "[secret]"],
|
|
35
|
+
[/\bAIza[0-9A-Za-z_-]{35}\b/g, "[secret]"],
|
|
36
|
+
[/\b(?:ct|ctc|ctk)_[A-Za-z0-9_]{12,}\b/g, "[secret]"],
|
|
37
|
+
[/\bsk-(?:proj-|ant-)?[A-Za-z0-9_-]{20,}\b/g, "[secret]"],
|
|
38
|
+
// `password=hunter2`, `"api_key": "abc"`, `token: abc` inside free text.
|
|
39
|
+
[
|
|
40
|
+
/(["']?\b(?:pass(?:word|wd|phrase)?|pwd|secret|token|api[-_ ]?key|access[-_ ]?key|private[-_ ]?key|client[-_ ]?secret|auth(?:orization)?|session(?:id)?|cookie|otp|cvv|cvc|ssn)\b["']?\s*[:=]\s*)(["']?)(?!\[(?:secret|redacted)\])[^\s"'&,;}]{1,512}\2/gi,
|
|
41
|
+
"$1$2[secret]$2",
|
|
42
|
+
],
|
|
43
|
+
// user:pass@ in a URL.
|
|
44
|
+
[/(\b[a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, "$1[secret]@"],
|
|
45
|
+
[/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[email]"],
|
|
46
|
+
[/\b\d{3}-\d{2}-\d{4}\b/g, "[ssn]"],
|
|
47
|
+
[/\b[A-Z]{2}\d{2}(?: ?[A-Z0-9]{4}){3,7}(?: ?[A-Z0-9]{1,3})?\b/g, "[iban]"],
|
|
48
|
+
// Octets only up to 255, so a four-part version number such as `142.0.7444.12` survives.
|
|
49
|
+
[/(?<![\d.])(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)(?![\d.])/g, "[ip]"],
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/** Card-like digit runs, checked with Luhn so order numbers and timestamps survive. */
|
|
53
|
+
const CARD = /\b(?:\d[ -]?){12,18}\d\b/g;
|
|
54
|
+
/** International or North American phone numbers with separators. Bare digit runs are left (ids). */
|
|
55
|
+
const PHONE = /(?:\+\d{1,3}[\s.-]?)?(?:\(\d{2,4}\)[\s.-]?|\b\d{2,4}[\s.-])\d{3,4}[\s.-]\d{3,4}\b/g;
|
|
56
|
+
|
|
57
|
+
function luhn(digits: string) {
|
|
58
|
+
let sum = 0;
|
|
59
|
+
let double = false;
|
|
60
|
+
for (let index = digits.length - 1; index >= 0; index -= 1) {
|
|
61
|
+
let value = digits.charCodeAt(index) - 48;
|
|
62
|
+
if (double) {
|
|
63
|
+
value *= 2;
|
|
64
|
+
if (value > 9) value -= 9;
|
|
65
|
+
}
|
|
66
|
+
sum += value;
|
|
67
|
+
double = !double;
|
|
68
|
+
}
|
|
69
|
+
return sum % 10 === 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Control characters other than tab and newline. */
|
|
73
|
+
const CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f]/g;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `value` with secrets and personal data replaced, control characters removed and cut at `max`. Placeholders
|
|
77
|
+
* (`[secret]`, `[email]`, …) are stable, so running it twice changes nothing.
|
|
78
|
+
*/
|
|
79
|
+
export function scrubText(value: string, max: number, tally?: RedactionTally): string {
|
|
80
|
+
let text = value.length > max * 4 ? value.slice(0, max * 4) : value;
|
|
81
|
+
text = text.replace(CONTROL, "");
|
|
82
|
+
for (const [pattern, replacement] of RULES) {
|
|
83
|
+
text = text.replace(pattern, (...match: string[]) => {
|
|
84
|
+
if (tally) tally.masked += 1;
|
|
85
|
+
return replacement.replace(/\$(\d)/g, (_, group: string) => match[Number(group)] ?? "");
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
text = text.replace(CARD, (found) => {
|
|
89
|
+
const digits = found.replace(/[ -]/g, "");
|
|
90
|
+
if (digits.length < 13 || !luhn(digits)) return found;
|
|
91
|
+
if (tally) tally.masked += 1;
|
|
92
|
+
return "[card]";
|
|
93
|
+
});
|
|
94
|
+
text = text.replace(PHONE, (found) => {
|
|
95
|
+
if (found.replace(/\D/g, "").length < 9) return found;
|
|
96
|
+
if (tally) tally.masked += 1;
|
|
97
|
+
return "[phone]";
|
|
98
|
+
});
|
|
99
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Object keys whose values are never kept, at any depth. */
|
|
103
|
+
export const SENSITIVE_KEY =
|
|
104
|
+
/pass(?:word|wd|phrase|code)?|pwd|secret|token|credential|api[-_ ]?key|access[-_ ]?key|private[-_ ]?key|auth|session|cookie|csrf|xsrf|\bssn\b|social.?security|\bpin\b|\botp\b|cvv|cvc|card.?number|\bpan\b|iban|account.?number|routing|signature|\bdob\b|birth|^email$|e-?mail|phone|mobile|address|postcode|zip.?code/i;
|
|
105
|
+
|
|
106
|
+
/** Query keys whose values are never kept. `s` is Comment Tool's own signed-file signature. */
|
|
107
|
+
const SECRET_PARAM =
|
|
108
|
+
/^s$|token|key|secret|auth|session|password|passwd|sig|code|credential|email|phone|otp/i;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A URL with its userinfo removed, secret-looking query values replaced and the rest scrubbed as text. Same-
|
|
112
|
+
* origin URLs keep only path and query. Unparseable input is scrubbed as text.
|
|
113
|
+
*/
|
|
114
|
+
export function scrubUrl(
|
|
115
|
+
value: string,
|
|
116
|
+
max: number,
|
|
117
|
+
base?: string,
|
|
118
|
+
tally?: RedactionTally,
|
|
119
|
+
): string {
|
|
120
|
+
// Userinfo is cut from the text itself: React Native's URL cannot write `username` or `password`.
|
|
121
|
+
const bare = value.replace(/^([a-z][a-z0-9+.-]*:\/\/)[^/?#@\s]*@/i, (_, scheme: string) => {
|
|
122
|
+
if (tally) tally.masked += 1;
|
|
123
|
+
return scheme;
|
|
124
|
+
});
|
|
125
|
+
let url: URL;
|
|
126
|
+
try {
|
|
127
|
+
url = new URL(bare, base);
|
|
128
|
+
} catch {
|
|
129
|
+
return scrubText(bare, max, tally);
|
|
130
|
+
}
|
|
131
|
+
const params = [...url.searchParams.keys()];
|
|
132
|
+
const query = new URLSearchParams();
|
|
133
|
+
for (const name of params) {
|
|
134
|
+
for (const item of url.searchParams.getAll(name)) {
|
|
135
|
+
if (SECRET_PARAM.test(name)) {
|
|
136
|
+
query.append(name, "[redacted]");
|
|
137
|
+
if (tally) tally.masked += 1;
|
|
138
|
+
} else query.append(name, item);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const search = query.toString()
|
|
142
|
+
? `?${query.toString().replaceAll("%5Bredacted%5D", "[redacted]")}`
|
|
143
|
+
: "";
|
|
144
|
+
let sameOrigin = false;
|
|
145
|
+
if (base) {
|
|
146
|
+
try {
|
|
147
|
+
sameOrigin = new URL(base).origin === url.origin;
|
|
148
|
+
} catch {
|
|
149
|
+
sameOrigin = false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// A native app's screen URL (`<scheme>://app/<route>`) has no web origin; it keeps its scheme and host.
|
|
153
|
+
const head = sameOrigin
|
|
154
|
+
? ""
|
|
155
|
+
: /^https?:$/.test(url.protocol)
|
|
156
|
+
? url.origin
|
|
157
|
+
: url.host
|
|
158
|
+
? `${url.protocol}//${url.host}`
|
|
159
|
+
: "";
|
|
160
|
+
return scrubText(`${head}${url.pathname}${search}`, max, tally);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type ScrubOptions = {
|
|
164
|
+
/** Deepest level kept; anything below becomes "[…]". */
|
|
165
|
+
depth?: number;
|
|
166
|
+
/** Longest string kept. */
|
|
167
|
+
text?: number;
|
|
168
|
+
/** Most entries kept per object or array. */
|
|
169
|
+
entries?: number;
|
|
170
|
+
/** Keep only the shape: types and sizes, no values. */
|
|
171
|
+
shapeOnly?: boolean;
|
|
172
|
+
tally?: RedactionTally;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/** Any JSON-like value, redacted at every depth: sensitive keys dropped, strings scrubbed, sizes bounded. */
|
|
176
|
+
export function scrubValue(value: unknown, options: ScrubOptions = {}, level = 0): unknown {
|
|
177
|
+
const depth = options.depth ?? 6;
|
|
178
|
+
const max = options.text ?? 300;
|
|
179
|
+
const entries = options.entries ?? 50;
|
|
180
|
+
const tally = options.tally;
|
|
181
|
+
if (value === null || value === undefined) return null;
|
|
182
|
+
if (typeof value === "string")
|
|
183
|
+
return options.shapeOnly ? `string(${value.length})` : scrubText(value, max, tally);
|
|
184
|
+
if (typeof value === "number")
|
|
185
|
+
return options.shapeOnly ? "number" : Number.isFinite(value) ? value : null;
|
|
186
|
+
if (typeof value === "boolean") return options.shapeOnly ? "boolean" : value;
|
|
187
|
+
if (typeof value === "bigint") return options.shapeOnly ? "bigint" : value.toString();
|
|
188
|
+
if (typeof value !== "object") return options.shapeOnly ? typeof value : null;
|
|
189
|
+
if (level >= depth) {
|
|
190
|
+
if (tally) tally.dropped += 1;
|
|
191
|
+
return "[…]";
|
|
192
|
+
}
|
|
193
|
+
if (value instanceof Date) return options.shapeOnly ? "date" : value.toISOString();
|
|
194
|
+
if (value instanceof Map) return scrubValue(Object.fromEntries(value), options, level);
|
|
195
|
+
if (value instanceof Set) return scrubValue([...value], options, level);
|
|
196
|
+
if (Array.isArray(value)) {
|
|
197
|
+
const kept = value.slice(0, entries).map((item) => scrubValue(item, options, level + 1));
|
|
198
|
+
if (value.length > entries) {
|
|
199
|
+
kept.push(`[+${value.length - entries} more]`);
|
|
200
|
+
if (tally) tally.dropped += 1;
|
|
201
|
+
}
|
|
202
|
+
return options.shapeOnly ? { array: value.length, of: kept[0] ?? null } : kept;
|
|
203
|
+
}
|
|
204
|
+
const out: JsonBag = {};
|
|
205
|
+
let count = 0;
|
|
206
|
+
for (const key of Object.keys(value)) {
|
|
207
|
+
if (count >= entries) {
|
|
208
|
+
out["[more]"] = Object.keys(value).length - entries;
|
|
209
|
+
if (tally) tally.dropped += 1;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
const safeKey = scrubText(key, 80, tally);
|
|
213
|
+
if (SENSITIVE_KEY.test(key)) {
|
|
214
|
+
out[safeKey] = "[redacted]";
|
|
215
|
+
if (tally) tally.masked += 1;
|
|
216
|
+
} else {
|
|
217
|
+
let item: unknown;
|
|
218
|
+
try {
|
|
219
|
+
item = (value as JsonBag)[key];
|
|
220
|
+
} catch {
|
|
221
|
+
item = null;
|
|
222
|
+
}
|
|
223
|
+
if (typeof item === "function" || typeof item === "symbol") continue;
|
|
224
|
+
out[safeKey] = scrubValue(item, options, level + 1);
|
|
225
|
+
}
|
|
226
|
+
count += 1;
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Response headers whose values are ids that join a request with backend logs. Values are opaque. */
|
|
232
|
+
export const ID_HEADERS: readonly string[] = [
|
|
233
|
+
"x-request-id",
|
|
234
|
+
"x-correlation-id",
|
|
235
|
+
"traceparent",
|
|
236
|
+
"sentry-trace",
|
|
237
|
+
"cf-ray",
|
|
238
|
+
"x-amzn-requestid",
|
|
239
|
+
"x-amz-request-id",
|
|
240
|
+
"x-cloud-trace-context",
|
|
241
|
+
"x-trace-id",
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
const HEADER_NAME = /^[a-z0-9-]{1,64}$/;
|
|
245
|
+
|
|
246
|
+
/** The id headers to read: the defaults plus a host's own, lowercased, invalid names dropped. */
|
|
247
|
+
export function idHeaderNames(extra: readonly string[] = []): string[] {
|
|
248
|
+
const names = new Set(ID_HEADERS);
|
|
249
|
+
for (const name of extra) {
|
|
250
|
+
const lower = typeof name === "string" ? name.trim().toLowerCase() : "";
|
|
251
|
+
if (HEADER_NAME.test(lower) && !SENSITIVE_KEY.test(lower)) names.add(lower);
|
|
252
|
+
}
|
|
253
|
+
return [...names];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** An id header's value: opaque, so only control characters and obvious secrets are removed. */
|
|
257
|
+
export function scrubId(value: string): string {
|
|
258
|
+
return scrubText(value.trim(), 200);
|
|
259
|
+
}
|