@detiq/crawler 1.1.7 → 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.d.ts +10 -0
- 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.d.ts +2 -0
- package/dist/index.js +28 -63
- package/dist/index.js.map +2 -2
- package/dist/input-fuzzer.js +97 -93
- package/dist/interactive-probe.d.ts +36 -0
- 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.d.ts +37 -0
- package/dist/locator-extractor.js +199 -0
- package/dist/locator-extractor.js.map +7 -0
- 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/src/index.ts +2 -0
- package/src/locator-extractor.ts +226 -0
- package/types/index.d.ts +2 -0
- package/types/locator-extractor.d.ts +37 -0
package/dist/cookie-consent.js
CHANGED
|
@@ -1,78 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie consent auto-acceptor.
|
|
3
|
+
*
|
|
4
|
+
* Detects and clicks "Accept" on common cookie consent banners before the
|
|
5
|
+
* crawler proceeds. Without this, many EU sites show a consent wall that
|
|
6
|
+
* blocks content or shows a different layout, making crawl results unreliable.
|
|
7
|
+
*
|
|
8
|
+
* Supports: Cookiebot, OneTrust, TrustArc, Usercentrics, GDPR Cookie Compliance,
|
|
9
|
+
* generic "Accept All" / "Accept Cookies" buttons.
|
|
10
|
+
*
|
|
11
|
+
* Strategy: try known selectors first, then fall back to text-based button search.
|
|
12
|
+
* Non-blocking — if no banner is found or click fails, crawl proceeds normally.
|
|
13
|
+
*/
|
|
14
|
+
// Known consent platform selectors
|
|
1
15
|
const CONSENT_SELECTORS = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
// Cookiebot
|
|
17
|
+
'#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
|
|
18
|
+
'#CybotCookiebotDialogBodyButtonAccept',
|
|
19
|
+
// OneTrust
|
|
20
|
+
'#onetrust-accept-btn-handler',
|
|
21
|
+
'.ot-sdk-accept-btn',
|
|
22
|
+
// TrustArc
|
|
23
|
+
'.truste_popframe [title*="Accept"]',
|
|
24
|
+
'#truste-consent-button',
|
|
25
|
+
// Usercentrics
|
|
26
|
+
'[data-testid="uc-accept-all-button"]',
|
|
27
|
+
// Generic high-confidence
|
|
28
|
+
'[id*="accept"][id*="cookie"]',
|
|
29
|
+
'[class*="accept"][class*="cookie"]',
|
|
30
|
+
'[class*="cookie"][class*="accept"]',
|
|
31
|
+
// GDPR Cookie Compliance
|
|
32
|
+
'.gdpr-cookie-notice-accept',
|
|
33
|
+
// Quantcast
|
|
34
|
+
'.qc-cmp2-summary-buttons button:first-child',
|
|
21
35
|
];
|
|
36
|
+
// Fallback: button text matching
|
|
22
37
|
const ACCEPT_TEXT_PATTERNS = [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
/^accept all$/i,
|
|
39
|
+
/^accept all cookies$/i,
|
|
40
|
+
/^allow all$/i,
|
|
41
|
+
/^i accept$/i,
|
|
42
|
+
/^agree$/i,
|
|
43
|
+
/^ok$/i,
|
|
44
|
+
/^got it$/i,
|
|
45
|
+
/^allow cookies$/i,
|
|
31
46
|
];
|
|
32
|
-
async function acceptCookieConsent(page, opts = {}) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
export async function acceptCookieConsent(page, opts = {}) {
|
|
48
|
+
const { waitMs = 1_500 } = opts;
|
|
49
|
+
// Wait briefly for consent banners to appear
|
|
50
|
+
await page.waitForTimeout(waitMs).catch(() => { });
|
|
51
|
+
// Try known selectors first
|
|
52
|
+
for (const selector of CONSENT_SELECTORS) {
|
|
53
|
+
try {
|
|
54
|
+
const el = await page.$(selector);
|
|
55
|
+
if (el && await el.isVisible()) {
|
|
56
|
+
await el.click({ timeout: 2_000 });
|
|
57
|
+
await page.waitForTimeout(500);
|
|
58
|
+
return { found: true, clicked: true, method: `selector: ${selector}` };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch { /* try next */ }
|
|
45
62
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
// Fallback: find buttons with accept-like text
|
|
64
|
+
try {
|
|
65
|
+
const buttons = await page.$$('button, [role="button"], a[class*="cookie"]');
|
|
66
|
+
for (const btn of buttons) {
|
|
67
|
+
const text = ((await btn.textContent()) ?? '').trim();
|
|
68
|
+
if (ACCEPT_TEXT_PATTERNS.some(re => re.test(text))) {
|
|
69
|
+
if (await btn.isVisible()) {
|
|
70
|
+
await btn.click({ timeout: 2_000 });
|
|
71
|
+
await page.waitForTimeout(500);
|
|
72
|
+
return { found: true, clicked: true, method: `text: "${text}"` };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
56
75
|
}
|
|
57
|
-
}
|
|
58
76
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return { found: false, clicked: false };
|
|
77
|
+
catch { /* non-fatal */ }
|
|
78
|
+
return { found: false, clicked: false };
|
|
62
79
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
/** Check if a consent banner is still visible after attempted dismissal */
|
|
81
|
+
export async function isConsentBannerVisible(page) {
|
|
82
|
+
try {
|
|
83
|
+
for (const sel of CONSENT_SELECTORS.slice(0, 5)) {
|
|
84
|
+
const el = await page.$(sel);
|
|
85
|
+
if (el && await el.isVisible())
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
68
92
|
}
|
|
69
|
-
return false;
|
|
70
|
-
} catch {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
93
|
}
|
|
74
|
-
export {
|
|
75
|
-
acceptCookieConsent,
|
|
76
|
-
isConsentBannerVisible
|
|
77
|
-
};
|
|
78
|
-
//# sourceMappingURL=cookie-consent.js.map
|
package/dist/cookie-jar.js
CHANGED
|
@@ -1,61 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Cookie jar — persist and restore Playwright storageState cookies between
|
|
3
|
+
* crawl segments and restarts. Enables continuous auth across session boundaries
|
|
4
|
+
* (e.g. segment 1 logs in, segment 2–N inherit the session without re-login).
|
|
5
|
+
*
|
|
6
|
+
* Storage: JSON file per project in the crawl cache dir.
|
|
7
|
+
* Format: Playwright's storageState shape (cookies + origins).
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs/promises';
|
|
10
|
+
import * as path from 'path';
|
|
3
11
|
function jarPath(cacheDir, projectId) {
|
|
4
|
-
|
|
12
|
+
return path.join(cacheDir, `cookie-jar-${projectId}.json`);
|
|
5
13
|
}
|
|
6
|
-
async function saveCookieJar(cacheDir, projectId, tenantId, storageState, domain) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export async function saveCookieJar(cacheDir, projectId, tenantId, storageState, domain) {
|
|
15
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
16
|
+
const entry = {
|
|
17
|
+
projectId,
|
|
18
|
+
tenantId,
|
|
19
|
+
storageState,
|
|
20
|
+
savedAt: new Date().toISOString(),
|
|
21
|
+
domain,
|
|
22
|
+
};
|
|
23
|
+
await fs.writeFile(jarPath(cacheDir, projectId), JSON.stringify(entry, null, 2), 'utf8');
|
|
16
24
|
}
|
|
17
|
-
async function loadCookieJar(cacheDir, projectId, maxAgeMs = 8 * 60 * 60 *
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
export async function loadCookieJar(cacheDir, projectId, maxAgeMs = 8 * 60 * 60 * 1000) {
|
|
26
|
+
try {
|
|
27
|
+
const raw = await fs.readFile(jarPath(cacheDir, projectId), 'utf8');
|
|
28
|
+
const entry = JSON.parse(raw);
|
|
29
|
+
const age = Date.now() - new Date(entry.savedAt).getTime();
|
|
30
|
+
if (age > maxAgeMs)
|
|
31
|
+
return null; // expired
|
|
32
|
+
return entry.storageState;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
27
37
|
}
|
|
28
|
-
async function clearCookieJar(cacheDir, projectId) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
export async function clearCookieJar(cacheDir, projectId) {
|
|
39
|
+
try {
|
|
40
|
+
await fs.unlink(jarPath(cacheDir, projectId));
|
|
41
|
+
}
|
|
42
|
+
catch { /* already gone */ }
|
|
33
43
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
/** Merge two storageState JSON strings — second takes priority for same-domain cookies. */
|
|
45
|
+
export function mergeStorageStates(base, overlay) {
|
|
46
|
+
try {
|
|
47
|
+
const a = JSON.parse(base);
|
|
48
|
+
const b = JSON.parse(overlay);
|
|
49
|
+
// Cookies: overlay replaces same-name+domain+path cookies from base
|
|
50
|
+
const cookies = [...(a.cookies ?? [])];
|
|
51
|
+
for (const ck of (b.cookies ?? [])) {
|
|
52
|
+
const idx = cookies.findIndex((c) => c.name === ck.name && c.domain === ck.domain && c.path === ck.path);
|
|
53
|
+
if (idx >= 0)
|
|
54
|
+
cookies[idx] = ck;
|
|
55
|
+
else
|
|
56
|
+
cookies.push(ck);
|
|
57
|
+
}
|
|
58
|
+
// Origins: merge origins, overlay wins on same-origin
|
|
59
|
+
const originsMap = new Map();
|
|
60
|
+
for (const o of [...(a.origins ?? []), ...(b.origins ?? [])]) {
|
|
61
|
+
originsMap.set(o.origin, o);
|
|
62
|
+
}
|
|
63
|
+
return JSON.stringify({ cookies, origins: [...originsMap.values()] });
|
|
45
64
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
originsMap.set(o.origin, o);
|
|
65
|
+
catch {
|
|
66
|
+
return overlay || base;
|
|
49
67
|
}
|
|
50
|
-
return JSON.stringify({ cookies, origins: [...originsMap.values()] });
|
|
51
|
-
} catch {
|
|
52
|
-
return overlay || base;
|
|
53
|
-
}
|
|
54
68
|
}
|
|
55
|
-
export {
|
|
56
|
-
clearCookieJar,
|
|
57
|
-
loadCookieJar,
|
|
58
|
-
mergeStorageStates,
|
|
59
|
-
saveCookieJar
|
|
60
|
-
};
|
|
61
|
-
//# sourceMappingURL=cookie-jar.js.map
|