@detiq/crawler 1.1.8 → 1.1.9
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/dist/accessibility-auditor.js +74 -54
- package/dist/ai-login.js +249 -227
- package/dist/auth-token-rotator.js +67 -57
- package/dist/auth.js +336 -299
- package/dist/browser-extension.js +142 -104
- package/dist/captcha-solver.js +125 -98
- package/dist/color-contrast.js +89 -77
- package/dist/content-chunker.js +126 -126
- package/dist/content-dedup.js +71 -54
- package/dist/cookie-consent.js +83 -68
- package/dist/cookie-jar.js +60 -53
- package/dist/crawler.js +5345 -4486
- package/dist/custom-js-injector.js +61 -45
- package/dist/dark-mode-capture.js +81 -67
- package/dist/dead-link-detector.js +55 -47
- package/dist/element-hash.js +12 -8
- package/dist/email-otp.js +195 -154
- package/dist/engine-router.js +98 -87
- package/dist/export/bulk-export.js +55 -70
- package/dist/export/vector-export.js +105 -77
- package/dist/extractors/advanced.js +266 -249
- package/dist/extractors/artifact-envelope.js +58 -59
- package/dist/failure-analysis-agent.js +33 -37
- package/dist/feature-flag-detector.js +66 -51
- package/dist/form-detector.js +75 -55
- package/dist/frontier.js +101 -76
- package/dist/gap-pass.js +89 -68
- package/dist/graphql-detector.js +72 -62
- package/dist/http-engine.js +330 -282
- package/dist/incremental-diff.js +87 -54
- package/dist/index.js +28 -67
- package/dist/input-fuzzer.js +97 -93
- package/dist/interactive-probe.js +827 -715
- package/dist/job-queue.js +2169 -2151
- package/dist/js-coverage.js +56 -52
- package/dist/js-router-detector.js +117 -86
- package/dist/language-detector.js +63 -51
- package/dist/link-classifier.js +50 -32
- package/dist/live-view.js +108 -91
- package/dist/locator-extractor.js +187 -151
- package/dist/logger.js +7 -9
- package/dist/markdown-extract.js +61 -48
- package/dist/native-crawler.js +946 -780
- package/dist/network-observer.js +156 -135
- package/dist/network-throttle.js +40 -29
- package/dist/openapi-generator.js +92 -77
- package/dist/pdf-extractor.js +90 -72
- package/dist/performance-metrics.js +102 -80
- package/dist/phash.js +144 -125
- package/dist/pii-scrubber.js +68 -65
- package/dist/playwright-recorder.js +58 -70
- package/dist/policies/crawl-safety.js +34 -31
- package/dist/profiles/domain-profile.js +125 -130
- package/dist/proxy.js +172 -146
- package/dist/rate-limiter.js +65 -58
- package/dist/redirect-tracker.js +64 -60
- package/dist/redis-frontier.js +136 -116
- package/dist/redis-queue.js +144 -157
- package/dist/response-cache.js +140 -127
- package/dist/responsive-detector.js +46 -42
- package/dist/retry-policy.js +50 -46
- package/dist/robots-txt.js +83 -69
- package/dist/screen-reader.js +70 -45
- package/dist/screenshot-masker.js +77 -59
- package/dist/seo-checker.js +107 -88
- package/dist/session-capture.js +212 -192
- package/dist/session-export.js +133 -107
- package/dist/sitemap-writer.js +41 -38
- package/dist/sitemap.js +202 -141
- package/dist/skyvern.js +148 -123
- package/dist/sqs-queue.js +42 -42
- package/dist/sso-token.js +90 -74
- package/dist/stealth/human-behavior.js +173 -156
- package/dist/telemetry.js +82 -68
- package/dist/test-executor.js +335 -309
- package/dist/tls-fingerprint.js +88 -77
- package/dist/url-pattern-completer.js +90 -71
- package/dist/visual-diff-verdict.js +38 -23
- package/dist/webhook-notifier.js +69 -71
- package/dist/websocket-detector.js +68 -56
- package/dist/worker.js +38 -29
- package/package.json +1 -1
package/dist/pii-scrubber.js
CHANGED
|
@@ -1,75 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PII scrubber — detects and redacts Personally Identifiable Information
|
|
3
|
+
* from crawled markdown, HTML, and extracted text before storage.
|
|
4
|
+
*
|
|
5
|
+
* GDPR Article 4(1): "any information relating to an identified or identifiable natural person"
|
|
6
|
+
*/
|
|
7
|
+
// Patterns — ordered from most-specific to least-specific
|
|
1
8
|
const PATTERNS = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
// Email — RFC 5321 simplified
|
|
10
|
+
{ type: 'email', re: /\b[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}\b/g },
|
|
11
|
+
// US SSN — 9 digits with dashes or spaces
|
|
12
|
+
{ type: 'ssn', re: /\b(?!000|666|9\d\d)\d{3}[-\s](?!00)\d{2}[-\s](?!0000)\d{4}\b/g },
|
|
13
|
+
// Credit card — Visa/MC/Amex/Discover with optional spaces/dashes
|
|
14
|
+
{ type: 'credit-card', re: /\b(?:4\d{3}|5[1-5]\d{2}|3[47]\d{2}|6(?:011|5\d{2}))[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{3,4}\b/g },
|
|
15
|
+
// IBAN — international bank account (15-34 chars)
|
|
16
|
+
{ type: 'iban', re: /\b[A-Z]{2}\d{2}[A-Z0-9]{1,30}\b/g },
|
|
17
|
+
// Phone — international or US formats
|
|
18
|
+
{ type: 'phone', re: /(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}\b|\+\d{1,3}[-.\s]\d{2,4}[-.\s]\d{3,4}[-.\s]\d{3,4}/g },
|
|
19
|
+
// Date of birth — common formats
|
|
20
|
+
{ type: 'dob', re: /\b(?:0?[1-9]|1[0-2])\/(?:0?[1-9]|[12]\d|3[01])\/(?:19|20)\d{2}\b/g },
|
|
21
|
+
// IPv4 private/internal addresses — only flag private-looking ones in content
|
|
22
|
+
{ type: 'ip-address', re: /\b(?:192\.168\.\d{1,3}\.\d{1,3}|10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b/g },
|
|
16
23
|
];
|
|
17
24
|
const REDACT_MAP = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
email: '[EMAIL]',
|
|
26
|
+
phone: '[PHONE]',
|
|
27
|
+
ssn: '[SSN]',
|
|
28
|
+
'credit-card': '[CARD]',
|
|
29
|
+
'ip-address': '[IP]',
|
|
30
|
+
passport: '[PASSPORT]',
|
|
31
|
+
iban: '[IBAN]',
|
|
32
|
+
dob: '[DOB]',
|
|
26
33
|
};
|
|
27
|
-
function detectPii(text) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
export function detectPii(text) {
|
|
35
|
+
const matches = [];
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
for (const { type, re } of PATTERNS) {
|
|
38
|
+
const r = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g');
|
|
39
|
+
let m;
|
|
40
|
+
while ((m = r.exec(text)) !== null) {
|
|
41
|
+
const key = `${type}:${m.index}`;
|
|
42
|
+
if (!seen.has(key)) {
|
|
43
|
+
seen.add(key);
|
|
44
|
+
matches.push({ type, value: m[0], start: m.index, end: m.index + m[0].length });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
return matches.sort((a, b) => a.start - b.start);
|
|
48
|
+
return matches.sort((a, b) => a.start - b.start);
|
|
42
49
|
}
|
|
43
|
-
function scrubPii(text) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
export function scrubPii(text) {
|
|
51
|
+
const matches = detectPii(text);
|
|
52
|
+
if (matches.length === 0)
|
|
53
|
+
return { text, matches: [], scrubbed: false };
|
|
54
|
+
// Replace from end to start so indices stay valid
|
|
55
|
+
let scrubbed = text;
|
|
56
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
57
|
+
const { type, start, end } = matches[i];
|
|
58
|
+
scrubbed = scrubbed.slice(0, start) + REDACT_MAP[type] + scrubbed.slice(end);
|
|
59
|
+
}
|
|
60
|
+
return { text: scrubbed, matches, scrubbed: true };
|
|
52
61
|
}
|
|
53
|
-
function hasPii(text) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
export function hasPii(text) {
|
|
63
|
+
for (const { re } of PATTERNS) {
|
|
64
|
+
const r = new RegExp(re.source, re.flags);
|
|
65
|
+
if (r.test(text))
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
59
69
|
}
|
|
60
|
-
function scrubObject(obj, fields) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
export function scrubObject(obj, fields) {
|
|
71
|
+
const result = { ...obj };
|
|
72
|
+
for (const field of fields) {
|
|
73
|
+
if (typeof result[field] === 'string') {
|
|
74
|
+
result[field] = scrubPii(result[field]).text;
|
|
75
|
+
}
|
|
65
76
|
}
|
|
66
|
-
|
|
67
|
-
return result;
|
|
77
|
+
return result;
|
|
68
78
|
}
|
|
69
|
-
export {
|
|
70
|
-
detectPii,
|
|
71
|
-
hasPii,
|
|
72
|
-
scrubObject,
|
|
73
|
-
scrubPii
|
|
74
|
-
};
|
|
75
|
-
//# sourceMappingURL=pii-scrubber.js.map
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright test recorder — generate Playwright test code from crawl data.
|
|
3
|
+
*
|
|
4
|
+
* Given a list of crawled screens, generates a minimal Playwright test file that:
|
|
5
|
+
* 1. Navigates to each screen URL
|
|
6
|
+
* 2. Verifies the page title
|
|
7
|
+
* 3. Checks key elements visible on the page
|
|
8
|
+
* 4. Verifies no console errors (optional)
|
|
9
|
+
*
|
|
10
|
+
* This is a "smoke test" generator — not a full interaction recorder.
|
|
11
|
+
* Output: valid TypeScript Playwright test file ready to run with npx playwright test.
|
|
12
|
+
*/
|
|
1
13
|
function buildSmartLocatorHelper() {
|
|
2
|
-
|
|
3
|
-
// smartLocator
|
|
14
|
+
return `
|
|
15
|
+
// smartLocator — falls back to similarity search when primary selector fails.
|
|
4
16
|
// Generated by ZeTa Crawler. Do not edit manually.
|
|
5
17
|
async function smartLocator(page: any, primary: string, ref: { tag?: string; text?: string; role?: string; placeholder?: string }) {
|
|
6
18
|
try {
|
|
@@ -27,76 +39,57 @@ async function smartLocator(page: any, primary: string, ref: { tag?: string; tex
|
|
|
27
39
|
}`.trim();
|
|
28
40
|
}
|
|
29
41
|
function sanitizeTestName(name) {
|
|
30
|
-
|
|
42
|
+
return name.replace(/[^a-zA-Z0-9 _-]/g, '').trim().slice(0, 80) || 'Screen';
|
|
31
43
|
}
|
|
32
44
|
function escapeString(s) {
|
|
33
|
-
|
|
45
|
+
return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/`/g, '\\`');
|
|
34
46
|
}
|
|
35
|
-
function generatePlaywrightTest(screens, opts = {}) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"import { test, expect } from '@playwright/test';",
|
|
50
|
-
""
|
|
51
|
-
];
|
|
52
|
-
if (needsSmartLocator) {
|
|
53
|
-
lines.push(buildSmartLocatorHelper(), "");
|
|
54
|
-
}
|
|
55
|
-
lines.push(`test.setTimeout(${testTimeout});`, "");
|
|
56
|
-
if (includeConsoleCheck) {
|
|
57
|
-
lines.push(
|
|
58
|
-
`test.beforeEach(async ({ page }) => {`,
|
|
59
|
-
` const errors: string[] = [];`,
|
|
60
|
-
` page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });`,
|
|
61
|
-
` page.on('pageerror', err => errors.push(err.message));`,
|
|
62
|
-
` (page as any).__consoleErrors = errors;`,
|
|
63
|
-
`});`,
|
|
64
|
-
""
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
for (const screen of screens) {
|
|
68
|
-
const testName = sanitizeTestName(screen.name);
|
|
69
|
-
lines.push(
|
|
70
|
-
`test('${escapeString(testName)}', async ({ page }) => {`,
|
|
71
|
-
` await page.goto('${escapeString(screen.url)}');`,
|
|
72
|
-
` await expect(page).toHaveURL(/${escapeString(screen.url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))}/);`
|
|
73
|
-
);
|
|
74
|
-
if (screen.title) {
|
|
75
|
-
lines.push(` await expect(page).toHaveTitle(/${escapeString(screen.title.slice(0, 40).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))}/i);`);
|
|
76
|
-
}
|
|
77
|
-
if (screen.heading) {
|
|
78
|
-
const h = escapeString(screen.heading.slice(0, 60));
|
|
79
|
-
if (needsSmartLocator && screen.smartLocator) {
|
|
80
|
-
const tag = escapeString(screen.smartLocator.tag);
|
|
81
|
-
const text = escapeString(screen.smartLocator.text ?? "");
|
|
82
|
-
lines.push(
|
|
83
|
-
` const headingEl = await smartLocator(page, 'h1, h2, h3', { tag: '${tag}', text: '${text}' });`,
|
|
84
|
-
` await expect(headingEl.first()).toContainText('${h}', { ignoreCase: true });`
|
|
85
|
-
);
|
|
86
|
-
} else {
|
|
87
|
-
lines.push(` await expect(page.locator('h1, h2, h3').first()).toContainText('${h}', { ignoreCase: true });`);
|
|
88
|
-
}
|
|
47
|
+
export function generatePlaywrightTest(screens, opts = {}) {
|
|
48
|
+
const { includeConsoleCheck = false, testTimeout = 30_000, projectName = 'ZeTa Crawl', useSmartLocators = true, } = opts;
|
|
49
|
+
const needsSmartLocator = useSmartLocators && screens.some(s => s.smartLocator != null);
|
|
50
|
+
const lines = [
|
|
51
|
+
`// Generated by ZeTa Crawler — ${new Date().toISOString()}`,
|
|
52
|
+
`// Project: ${escapeString(projectName)}`,
|
|
53
|
+
`// Screens: ${screens.length}`,
|
|
54
|
+
`// Run: npx playwright test`,
|
|
55
|
+
'',
|
|
56
|
+
"import { test, expect } from '@playwright/test';",
|
|
57
|
+
'',
|
|
58
|
+
];
|
|
59
|
+
if (needsSmartLocator) {
|
|
60
|
+
lines.push(buildSmartLocatorHelper(), '');
|
|
89
61
|
}
|
|
62
|
+
lines.push(`test.setTimeout(${testTimeout});`, '');
|
|
90
63
|
if (includeConsoleCheck) {
|
|
91
|
-
|
|
64
|
+
lines.push(`test.beforeEach(async ({ page }) => {`, ` const errors: string[] = [];`, ` page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });`, ` page.on('pageerror', err => errors.push(err.message));`, ` (page as any).__consoleErrors = errors;`, `});`, '');
|
|
92
65
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
66
|
+
for (const screen of screens) {
|
|
67
|
+
const testName = sanitizeTestName(screen.name);
|
|
68
|
+
lines.push(`test('${escapeString(testName)}', async ({ page }) => {`, ` await page.goto('${escapeString(screen.url)}');`, ` await expect(page).toHaveURL(/${escapeString(screen.url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))}/);`);
|
|
69
|
+
if (screen.title) {
|
|
70
|
+
lines.push(` await expect(page).toHaveTitle(/${escapeString(screen.title.slice(0, 40).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))}/i);`);
|
|
71
|
+
}
|
|
72
|
+
if (screen.heading) {
|
|
73
|
+
const h = escapeString(screen.heading.slice(0, 60));
|
|
74
|
+
if (needsSmartLocator && screen.smartLocator) {
|
|
75
|
+
const tag = escapeString(screen.smartLocator.tag);
|
|
76
|
+
const text = escapeString(screen.smartLocator.text ?? '');
|
|
77
|
+
lines.push(` const headingEl = await smartLocator(page, 'h1, h2, h3', { tag: '${tag}', text: '${text}' });`, ` await expect(headingEl.first()).toContainText('${h}', { ignoreCase: true });`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
lines.push(` await expect(page.locator('h1, h2, h3').first()).toContainText('${h}', { ignoreCase: true });`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (includeConsoleCheck) {
|
|
84
|
+
lines.push(` expect((page as any).__consoleErrors ?? []).toHaveLength(0);`);
|
|
85
|
+
}
|
|
86
|
+
lines.push(`});`, '');
|
|
87
|
+
}
|
|
88
|
+
return lines.join('\n');
|
|
96
89
|
}
|
|
97
|
-
function generatePlaywrightConfig(opts = {}) {
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
export function generatePlaywrightConfig(opts = {}) {
|
|
91
|
+
const { baseUrl = 'http://localhost:3000', reportDir = 'playwright-report' } = opts;
|
|
92
|
+
return `// playwright.config.ts — generated by ZeTa Crawler
|
|
100
93
|
import { defineConfig } from '@playwright/test';
|
|
101
94
|
|
|
102
95
|
export default defineConfig({
|
|
@@ -116,8 +109,3 @@ export default defineConfig({
|
|
|
116
109
|
});
|
|
117
110
|
`;
|
|
118
111
|
}
|
|
119
|
-
export {
|
|
120
|
-
generatePlaywrightConfig,
|
|
121
|
-
generatePlaywrightTest
|
|
122
|
-
};
|
|
123
|
-
//# sourceMappingURL=playwright-recorder.js.map
|
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
const DESTRUCTIVE_ACTION = /\b(delete|remove|destroy|deactivate|disable|cancel|refund|void|transfer|withdraw|pay now|place order|submit order|confirm payment|send money|publish|approve|reject|bulk update|drop)\b/i;
|
|
2
2
|
const SECRET_LIKE = /\b(api[_ -]?key|secret|token|password|authorization|cookie|set-cookie)\b/i;
|
|
3
|
-
function assessActionSafety(label, profile) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
export function assessActionSafety(label, profile) {
|
|
4
|
+
const riskyByProfile = profile.riskyActions.some((risk) => label.toLowerCase().includes(risk.toLowerCase()));
|
|
5
|
+
if (DESTRUCTIVE_ACTION.test(label) || riskyByProfile) {
|
|
6
|
+
return {
|
|
7
|
+
allowed: false,
|
|
8
|
+
severity: 'block',
|
|
9
|
+
reason: `Blocked risky action "${label}" for ${profile.industry} profile.`,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return { allowed: true, severity: 'info' };
|
|
13
13
|
}
|
|
14
|
-
function redactSensitiveValue(value) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
export function redactSensitiveValue(value) {
|
|
15
|
+
let redacted = value
|
|
16
|
+
.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, '[email]')
|
|
17
|
+
.replace(/\b(?:\+?\d[\d -]{8,}\d)\b/g, '[phone-or-id]')
|
|
18
|
+
.replace(/\b(?:\d[ -]*?){13,19}\b/g, '[card-like-number]')
|
|
19
|
+
.replace(/\b[A-Za-z0-9_-]{24,}\b/g, '[token-like-value]');
|
|
20
|
+
if (SECRET_LIKE.test(redacted))
|
|
21
|
+
redacted = redacted.replace(/:\s*[^,\s}]+/g, ': [redacted]');
|
|
22
|
+
return redacted;
|
|
18
23
|
}
|
|
19
|
-
function redactJson(value) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
export function redactJson(value) {
|
|
25
|
+
if (typeof value === 'string')
|
|
26
|
+
return redactSensitiveValue(value);
|
|
27
|
+
if (Array.isArray(value))
|
|
28
|
+
return value.map(redactJson);
|
|
29
|
+
if (value && typeof value === 'object') {
|
|
30
|
+
const out = {};
|
|
31
|
+
for (const [key, item] of Object.entries(value)) {
|
|
32
|
+
if (SECRET_LIKE.test(key))
|
|
33
|
+
out[key] = '[redacted]';
|
|
34
|
+
else
|
|
35
|
+
out[key] = redactJson(item);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
27
38
|
}
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
return value;
|
|
39
|
+
return value;
|
|
31
40
|
}
|
|
32
|
-
export {
|
|
33
|
-
assessActionSafety,
|
|
34
|
-
redactJson,
|
|
35
|
-
redactSensitiveValue
|
|
36
|
-
};
|
|
37
|
-
//# sourceMappingURL=crawl-safety.js.map
|