@detiq/crawler 1.1.9 → 1.1.10

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.
Files changed (82) hide show
  1. package/dist/accessibility-auditor.js +54 -74
  2. package/dist/ai-login.js +227 -249
  3. package/dist/auth-token-rotator.js +57 -67
  4. package/dist/auth.js +299 -336
  5. package/dist/browser-extension.js +104 -142
  6. package/dist/captcha-solver.js +98 -125
  7. package/dist/color-contrast.js +77 -89
  8. package/dist/content-chunker.js +126 -126
  9. package/dist/content-dedup.js +54 -71
  10. package/dist/cookie-consent.js +68 -83
  11. package/dist/cookie-jar.js +53 -60
  12. package/dist/crawler.js +4494 -5353
  13. package/dist/custom-js-injector.js +45 -61
  14. package/dist/dark-mode-capture.js +67 -81
  15. package/dist/dead-link-detector.js +47 -55
  16. package/dist/element-hash.js +8 -12
  17. package/dist/email-otp.js +154 -195
  18. package/dist/engine-router.js +87 -98
  19. package/dist/export/bulk-export.js +70 -55
  20. package/dist/export/vector-export.js +77 -105
  21. package/dist/extractors/advanced.js +249 -266
  22. package/dist/extractors/artifact-envelope.js +59 -58
  23. package/dist/failure-analysis-agent.js +37 -33
  24. package/dist/feature-flag-detector.js +51 -66
  25. package/dist/form-detector.js +55 -75
  26. package/dist/frontier.js +76 -101
  27. package/dist/gap-pass.js +68 -89
  28. package/dist/graphql-detector.js +62 -72
  29. package/dist/http-engine.js +282 -330
  30. package/dist/incremental-diff.js +54 -87
  31. package/dist/index.js +67 -28
  32. package/dist/input-fuzzer.js +93 -97
  33. package/dist/interactive-probe.js +715 -827
  34. package/dist/job-queue.js +2151 -2169
  35. package/dist/js-coverage.js +52 -56
  36. package/dist/js-router-detector.js +86 -117
  37. package/dist/language-detector.js +51 -63
  38. package/dist/link-classifier.js +32 -50
  39. package/dist/live-view.js +91 -108
  40. package/dist/locator-extractor.js +151 -187
  41. package/dist/logger.js +9 -7
  42. package/dist/markdown-extract.js +48 -61
  43. package/dist/native-crawler.js +780 -946
  44. package/dist/network-observer.js +135 -156
  45. package/dist/network-throttle.js +29 -40
  46. package/dist/openapi-generator.js +77 -92
  47. package/dist/pdf-extractor.js +72 -90
  48. package/dist/performance-metrics.js +80 -102
  49. package/dist/phash.js +125 -144
  50. package/dist/pii-scrubber.js +65 -68
  51. package/dist/playwright-recorder.js +70 -58
  52. package/dist/policies/crawl-safety.js +31 -34
  53. package/dist/profiles/domain-profile.js +130 -125
  54. package/dist/proxy.js +146 -172
  55. package/dist/rate-limiter.js +58 -65
  56. package/dist/redirect-tracker.js +60 -64
  57. package/dist/redis-frontier.js +116 -136
  58. package/dist/redis-queue.js +157 -144
  59. package/dist/response-cache.js +127 -140
  60. package/dist/responsive-detector.js +42 -46
  61. package/dist/retry-policy.js +46 -50
  62. package/dist/robots-txt.js +69 -83
  63. package/dist/screen-reader.js +45 -70
  64. package/dist/screenshot-masker.js +59 -77
  65. package/dist/seo-checker.js +88 -107
  66. package/dist/session-capture.js +192 -212
  67. package/dist/session-export.js +107 -133
  68. package/dist/sitemap-writer.js +38 -41
  69. package/dist/sitemap.js +141 -202
  70. package/dist/skyvern.js +123 -148
  71. package/dist/sqs-queue.js +42 -42
  72. package/dist/sso-token.js +74 -90
  73. package/dist/stealth/human-behavior.js +156 -173
  74. package/dist/telemetry.js +68 -82
  75. package/dist/test-executor.js +309 -335
  76. package/dist/tls-fingerprint.js +77 -88
  77. package/dist/url-pattern-completer.js +71 -90
  78. package/dist/visual-diff-verdict.js +23 -38
  79. package/dist/webhook-notifier.js +71 -69
  80. package/dist/websocket-detector.js +56 -68
  81. package/dist/worker.js +29 -38
  82. package/package.json +1 -1
@@ -1,78 +1,75 @@
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
8
1
  const PATTERNS = [
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 },
2
+ // Email — RFC 5321 simplified
3
+ { type: "email", re: /\b[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}\b/g },
4
+ // US SSN — 9 digits with dashes or spaces
5
+ { type: "ssn", re: /\b(?!000|666|9\d\d)\d{3}[-\s](?!00)\d{2}[-\s](?!0000)\d{4}\b/g },
6
+ // Credit card — Visa/MC/Amex/Discover with optional spaces/dashes
7
+ { 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 },
8
+ // IBAN — international bank account (15-34 chars)
9
+ { type: "iban", re: /\b[A-Z]{2}\d{2}[A-Z0-9]{1,30}\b/g },
10
+ // Phone — international or US formats
11
+ { 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 },
12
+ // Date of birth — common formats
13
+ { type: "dob", re: /\b(?:0?[1-9]|1[0-2])\/(?:0?[1-9]|[12]\d|3[01])\/(?:19|20)\d{2}\b/g },
14
+ // IPv4 private/internal addresses — only flag private-looking ones in content
15
+ { 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 }
23
16
  ];
24
17
  const REDACT_MAP = {
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]',
18
+ email: "[EMAIL]",
19
+ phone: "[PHONE]",
20
+ ssn: "[SSN]",
21
+ "credit-card": "[CARD]",
22
+ "ip-address": "[IP]",
23
+ passport: "[PASSPORT]",
24
+ iban: "[IBAN]",
25
+ dob: "[DOB]"
33
26
  };
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
- }
27
+ function detectPii(text) {
28
+ const matches = [];
29
+ const seen = /* @__PURE__ */ new Set();
30
+ for (const { type, re } of PATTERNS) {
31
+ const r = new RegExp(re.source, re.flags.includes("g") ? re.flags : re.flags + "g");
32
+ let m;
33
+ while ((m = r.exec(text)) !== null) {
34
+ const key = `${type}:${m.index}`;
35
+ if (!seen.has(key)) {
36
+ seen.add(key);
37
+ matches.push({ type, value: m[0], start: m.index, end: m.index + m[0].length });
38
+ }
47
39
  }
48
- return matches.sort((a, b) => a.start - b.start);
40
+ }
41
+ return matches.sort((a, b) => a.start - b.start);
49
42
  }
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 };
43
+ function scrubPii(text) {
44
+ const matches = detectPii(text);
45
+ if (matches.length === 0) return { text, matches: [], scrubbed: false };
46
+ let scrubbed = text;
47
+ for (let i = matches.length - 1; i >= 0; i--) {
48
+ const { type, start, end } = matches[i];
49
+ scrubbed = scrubbed.slice(0, start) + REDACT_MAP[type] + scrubbed.slice(end);
50
+ }
51
+ return { text: scrubbed, matches, scrubbed: true };
61
52
  }
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;
53
+ function hasPii(text) {
54
+ for (const { re } of PATTERNS) {
55
+ const r = new RegExp(re.source, re.flags);
56
+ if (r.test(text)) return true;
57
+ }
58
+ return false;
69
59
  }
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
- }
60
+ function scrubObject(obj, fields) {
61
+ const result = { ...obj };
62
+ for (const field of fields) {
63
+ if (typeof result[field] === "string") {
64
+ result[field] = scrubPii(result[field]).text;
76
65
  }
77
- return result;
66
+ }
67
+ return result;
78
68
  }
69
+ export {
70
+ detectPii,
71
+ hasPii,
72
+ scrubObject,
73
+ scrubPii
74
+ };
75
+ //# sourceMappingURL=pii-scrubber.js.map
@@ -1,18 +1,6 @@
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
- */
13
1
  function buildSmartLocatorHelper() {
14
- return `
15
- // smartLocator — falls back to similarity search when primary selector fails.
2
+ return `
3
+ // smartLocator \u2014 falls back to similarity search when primary selector fails.
16
4
  // Generated by ZeTa Crawler. Do not edit manually.
17
5
  async function smartLocator(page: any, primary: string, ref: { tag?: string; text?: string; role?: string; placeholder?: string }) {
18
6
  try {
@@ -39,57 +27,76 @@ async function smartLocator(page: any, primary: string, ref: { tag?: string; tex
39
27
  }`.trim();
40
28
  }
41
29
  function sanitizeTestName(name) {
42
- return name.replace(/[^a-zA-Z0-9 _-]/g, '').trim().slice(0, 80) || 'Screen';
30
+ return name.replace(/[^a-zA-Z0-9 _-]/g, "").trim().slice(0, 80) || "Screen";
43
31
  }
44
32
  function escapeString(s) {
45
- return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/`/g, '\\`');
33
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/`/g, "\\`");
46
34
  }
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(), '');
35
+ function generatePlaywrightTest(screens, opts = {}) {
36
+ const {
37
+ includeConsoleCheck = false,
38
+ testTimeout = 3e4,
39
+ projectName = "ZeTa Crawl",
40
+ useSmartLocators = true
41
+ } = opts;
42
+ const needsSmartLocator = useSmartLocators && screens.some((s) => s.smartLocator != null);
43
+ const lines = [
44
+ `// Generated by ZeTa Crawler \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
45
+ `// Project: ${escapeString(projectName)}`,
46
+ `// Screens: ${screens.length}`,
47
+ `// Run: npx playwright test`,
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);`);
61
76
  }
62
- lines.push(`test.setTimeout(${testTimeout});`, '');
63
- if (includeConsoleCheck) {
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;`, `});`, '');
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
+ }
65
89
  }
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(`});`, '');
90
+ if (includeConsoleCheck) {
91
+ lines.push(` expect((page as any).__consoleErrors ?? []).toHaveLength(0);`);
87
92
  }
88
- return lines.join('\n');
93
+ lines.push(`});`, "");
94
+ }
95
+ return lines.join("\n");
89
96
  }
90
- export function generatePlaywrightConfig(opts = {}) {
91
- const { baseUrl = 'http://localhost:3000', reportDir = 'playwright-report' } = opts;
92
- return `// playwright.config.ts — generated by ZeTa Crawler
97
+ function generatePlaywrightConfig(opts = {}) {
98
+ const { baseUrl = "http://localhost:3000", reportDir = "playwright-report" } = opts;
99
+ return `// playwright.config.ts \u2014 generated by ZeTa Crawler
93
100
  import { defineConfig } from '@playwright/test';
94
101
 
95
102
  export default defineConfig({
@@ -109,3 +116,8 @@ export default defineConfig({
109
116
  });
110
117
  `;
111
118
  }
119
+ export {
120
+ generatePlaywrightConfig,
121
+ generatePlaywrightTest
122
+ };
123
+ //# sourceMappingURL=playwright-recorder.js.map
@@ -1,40 +1,37 @@
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
- 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' };
3
+ 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
- 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;
14
+ function redactSensitiveValue(value) {
15
+ let redacted = value.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, "[email]").replace(/\b(?:\+?\d[\d -]{8,}\d)\b/g, "[phone-or-id]").replace(/\b(?:\d[ -]*?){13,19}\b/g, "[card-like-number]").replace(/\b[A-Za-z0-9_-]{24,}\b/g, "[token-like-value]");
16
+ if (SECRET_LIKE.test(redacted)) redacted = redacted.replace(/:\s*[^,\s}]+/g, ": [redacted]");
17
+ return redacted;
23
18
  }
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;
19
+ function redactJson(value) {
20
+ if (typeof value === "string") return redactSensitiveValue(value);
21
+ if (Array.isArray(value)) return value.map(redactJson);
22
+ if (value && typeof value === "object") {
23
+ const out = {};
24
+ for (const [key, item] of Object.entries(value)) {
25
+ if (SECRET_LIKE.test(key)) out[key] = "[redacted]";
26
+ else out[key] = redactJson(item);
38
27
  }
39
- return value;
28
+ return out;
29
+ }
30
+ return value;
40
31
  }
32
+ export {
33
+ assessActionSafety,
34
+ redactJson,
35
+ redactSensitiveValue
36
+ };
37
+ //# sourceMappingURL=crawl-safety.js.map