@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/session-export.js
CHANGED
|
@@ -1,117 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a Playwright storageState (the format ZeTa already captures via
|
|
3
|
+
* session-capture.ts/live-view.ts and stores per-project) into snippets for
|
|
4
|
+
* other automation frameworks. Playwright's storageState is the de-facto
|
|
5
|
+
* standard for browser session export — see docs/UNIVERSAL-SESSION-CAPTURE-
|
|
6
|
+
* STRATEGY.md — so every other framework's import mechanism is a small,
|
|
7
|
+
* mechanical conversion away, not a new capture flow.
|
|
8
|
+
*
|
|
9
|
+
* Pure, dependency-free functions — no I/O, no network, fully unit-testable.
|
|
10
|
+
*/
|
|
1
11
|
function jsStringLiteral(s) {
|
|
2
|
-
|
|
12
|
+
return JSON.stringify(s);
|
|
3
13
|
}
|
|
14
|
+
/** Python dict-literal repr — JSON.stringify's `true`/`false`/`null` aren't
|
|
15
|
+
* valid Python (Python needs `True`/`False`/`None`), so a plain
|
|
16
|
+
* JSON.stringify would emit syntactically invalid Python for any cookie
|
|
17
|
+
* with a boolean field. */
|
|
4
18
|
function pythonDictLiteral(obj) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
const parts = Object.entries(obj).map(([k, v]) => {
|
|
20
|
+
let repr;
|
|
21
|
+
if (typeof v === 'boolean')
|
|
22
|
+
repr = v ? 'True' : 'False';
|
|
23
|
+
else if (v === null || v === undefined)
|
|
24
|
+
repr = 'None';
|
|
25
|
+
else if (typeof v === 'number')
|
|
26
|
+
repr = String(v);
|
|
27
|
+
else
|
|
28
|
+
repr = JSON.stringify(v);
|
|
29
|
+
return `${JSON.stringify(k)}: ${repr}`;
|
|
30
|
+
});
|
|
31
|
+
return `{${parts.join(', ')}}`;
|
|
14
32
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
${
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
/** cy.setCookie() + localStorage.setItem() inside a reusable custom command,
|
|
34
|
+
* following Cypress's documented cy.session() pattern (see references in
|
|
35
|
+
* docs/UNIVERSAL-SESSION-CAPTURE-STRATEGY.md). */
|
|
36
|
+
export function toCypressSnippet(state) {
|
|
37
|
+
const cookieLines = state.cookies.map((c) => {
|
|
38
|
+
const opts = [`domain: ${jsStringLiteral(c.domain)}`, `path: ${jsStringLiteral(c.path)}`];
|
|
39
|
+
if (c.secure)
|
|
40
|
+
opts.push('secure: true');
|
|
41
|
+
if (c.httpOnly)
|
|
42
|
+
opts.push('httpOnly: true');
|
|
43
|
+
if (c.sameSite)
|
|
44
|
+
opts.push(`sameSite: ${jsStringLiteral(c.sameSite.toLowerCase())}`);
|
|
45
|
+
return ` cy.setCookie(${jsStringLiteral(c.name)}, ${jsStringLiteral(c.value)}, { ${opts.join(', ')} });`;
|
|
46
|
+
});
|
|
47
|
+
const localStorageBlocks = state.origins
|
|
48
|
+
.filter((o) => o.localStorage.length > 0)
|
|
49
|
+
.map((o) => {
|
|
50
|
+
const sets = o.localStorage
|
|
51
|
+
.map((kv) => ` window.localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`)
|
|
52
|
+
.join('\n');
|
|
53
|
+
return ` cy.visit(${jsStringLiteral(o.origin)});\n cy.window().then((window) => {\n${sets}\n });`;
|
|
54
|
+
});
|
|
55
|
+
return [
|
|
56
|
+
'// Generated by ZeTa-AI — restores a captured browser session in Cypress.',
|
|
57
|
+
'// Usage: import this file and call restoreZetaSession() inside cy.session(), e.g.:',
|
|
58
|
+
"// cy.session('zeta-session', restoreZetaSession)",
|
|
59
|
+
"Cypress.Commands.add('restoreZetaSession', () => {",
|
|
60
|
+
...cookieLines,
|
|
61
|
+
...localStorageBlocks,
|
|
62
|
+
'});',
|
|
63
|
+
].join('\n');
|
|
39
64
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"# Selenium requires the driver to already be on a matching domain before",
|
|
50
|
-
"# add_cookie() will accept a cookie for it \u2014 navigate first, then restore.",
|
|
51
|
-
"def restore_zeta_session(driver):"
|
|
52
|
-
];
|
|
53
|
-
for (const [domain, cookies] of byDomain) {
|
|
54
|
-
lines.push(` driver.get(${JSON.stringify(`https://${domain.replace(/^\./, "")}`)})`);
|
|
55
|
-
for (const c of cookies) {
|
|
56
|
-
const cookieDict = { name: c.name, value: c.value, path: c.path };
|
|
57
|
-
if (c.expires && c.expires > 0) cookieDict.expiry = Math.floor(c.expires);
|
|
58
|
-
if (c.secure) cookieDict.secure = true;
|
|
59
|
-
lines.push(` driver.add_cookie(${pythonDictLiteral(cookieDict)})`);
|
|
65
|
+
/** driver.add_cookie() (Selenium requires the browser already be on the
|
|
66
|
+
* cookie's domain before adding it) + execute_script() for localStorage,
|
|
67
|
+
* per Selenium's documented Cookie API. */
|
|
68
|
+
export function toSeleniumPythonDict(state) {
|
|
69
|
+
const byDomain = new Map();
|
|
70
|
+
for (const c of state.cookies) {
|
|
71
|
+
const list = byDomain.get(c.domain) ?? [];
|
|
72
|
+
list.push(c);
|
|
73
|
+
byDomain.set(c.domain, list);
|
|
60
74
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
const lines = [
|
|
76
|
+
'# Generated by ZeTa-AI — restores a captured browser session in Selenium.',
|
|
77
|
+
'# Selenium requires the driver to already be on a matching domain before',
|
|
78
|
+
'# add_cookie() will accept a cookie for it — navigate first, then restore.',
|
|
79
|
+
'def restore_zeta_session(driver):',
|
|
80
|
+
];
|
|
81
|
+
for (const [domain, cookies] of byDomain) {
|
|
82
|
+
lines.push(` driver.get(${JSON.stringify(`https://${domain.replace(/^\./, '')}`)})`);
|
|
83
|
+
for (const c of cookies) {
|
|
84
|
+
const cookieDict = { name: c.name, value: c.value, path: c.path };
|
|
85
|
+
if (c.expires && c.expires > 0)
|
|
86
|
+
cookieDict.expiry = Math.floor(c.expires);
|
|
87
|
+
if (c.secure)
|
|
88
|
+
cookieDict.secure = true;
|
|
89
|
+
lines.push(` driver.add_cookie(${pythonDictLiteral(cookieDict)})`);
|
|
90
|
+
}
|
|
69
91
|
}
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
for (const o of state.origins) {
|
|
93
|
+
if (o.localStorage.length === 0)
|
|
94
|
+
continue;
|
|
95
|
+
lines.push(` driver.get(${JSON.stringify(o.origin)})`);
|
|
96
|
+
for (const kv of o.localStorage) {
|
|
97
|
+
lines.push(` driver.execute_script(${JSON.stringify(`window.localStorage.setItem(arguments[0], arguments[1]);`)}, ${JSON.stringify(kv.name)}, ${JSON.stringify(kv.value)})`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return lines.join('\n');
|
|
72
101
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
/** browser.setCookies() accepts an array shape very close to Playwright's own
|
|
103
|
+
* cookie objects (per WebdriverIO's documented API) — closest 1:1 mapping
|
|
104
|
+
* of the three target formats. */
|
|
105
|
+
export function toWebdriverIOSnippet(state) {
|
|
106
|
+
const cookies = state.cookies.map((c) => ({
|
|
107
|
+
name: c.name,
|
|
108
|
+
value: c.value,
|
|
109
|
+
domain: c.domain,
|
|
110
|
+
path: c.path,
|
|
111
|
+
secure: !!c.secure,
|
|
112
|
+
httpOnly: !!c.httpOnly,
|
|
113
|
+
...(c.expires && c.expires > 0 ? { expiry: Math.floor(c.expires) } : {}),
|
|
114
|
+
}));
|
|
115
|
+
const localStorageSets = state.origins
|
|
116
|
+
.filter((o) => o.localStorage.length > 0)
|
|
117
|
+
.map((o) => {
|
|
118
|
+
const sets = o.localStorage
|
|
119
|
+
.map((kv) => ` localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`)
|
|
120
|
+
.join('\n');
|
|
121
|
+
return ` await browser.url(${jsStringLiteral(o.origin)});\n await browser.execute(() => {\n${sets}\n });`;
|
|
122
|
+
});
|
|
123
|
+
return [
|
|
124
|
+
'// Generated by ZeTa-AI — restores a captured browser session in WebdriverIO.',
|
|
125
|
+
'async function restoreZetaSession(browser) {',
|
|
126
|
+
` await browser.setCookies(${JSON.stringify(cookies, null, 2).split('\n').join('\n ')});`,
|
|
127
|
+
...localStorageSets,
|
|
128
|
+
'}',
|
|
129
|
+
'module.exports = { restoreZetaSession };',
|
|
130
|
+
].join('\n');
|
|
98
131
|
}
|
|
99
|
-
function exportSessionAs(format, state) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
132
|
+
export function exportSessionAs(format, state) {
|
|
133
|
+
switch (format) {
|
|
134
|
+
case 'cypress':
|
|
135
|
+
return { content: toCypressSnippet(state), filename: 'zeta-session.cy-commands.js', mimeType: 'application/javascript' };
|
|
136
|
+
case 'selenium-python':
|
|
137
|
+
return { content: toSeleniumPythonDict(state), filename: 'zeta_session.py', mimeType: 'text/x-python' };
|
|
138
|
+
case 'webdriverio':
|
|
139
|
+
return { content: toWebdriverIOSnippet(state), filename: 'zeta-session.wdio.js', mimeType: 'application/javascript' };
|
|
140
|
+
default:
|
|
141
|
+
throw new Error(`Unknown session export format: ${format}`);
|
|
142
|
+
}
|
|
110
143
|
}
|
|
111
|
-
export {
|
|
112
|
-
exportSessionAs,
|
|
113
|
-
toCypressSnippet,
|
|
114
|
-
toSeleniumPythonDict,
|
|
115
|
-
toWebdriverIOSnippet
|
|
116
|
-
};
|
|
117
|
-
//# sourceMappingURL=session-export.js.map
|
package/dist/sitemap-writer.js
CHANGED
|
@@ -1,47 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Sitemap writer — generate XML sitemap from crawled screens.
|
|
3
|
+
*
|
|
4
|
+
* Compliant with sitemaps.org protocol (sitemap index + individual sitemap files).
|
|
5
|
+
* Splits into 50k-URL files per the spec when needed.
|
|
6
|
+
* Used for: SEO audit export, future crawl seed, and crawler/robots.txt comparison.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
3
10
|
const XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
4
11
|
const SITEMAP_NS = 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
|
|
5
12
|
function escape(s) {
|
|
6
|
-
|
|
13
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
7
14
|
}
|
|
8
15
|
function buildSitemapXml(urls) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
${entries}
|
|
19
|
-
</urlset>`;
|
|
16
|
+
const entries = urls.map(u => [
|
|
17
|
+
' <url>',
|
|
18
|
+
` <loc>${escape(u.loc)}</loc>`,
|
|
19
|
+
u.lastmod ? ` <lastmod>${u.lastmod}</lastmod>` : '',
|
|
20
|
+
u.changefreq ? ` <changefreq>${u.changefreq}</changefreq>` : '',
|
|
21
|
+
u.priority !== undefined ? ` <priority>${u.priority.toFixed(1)}</priority>` : '',
|
|
22
|
+
' </url>',
|
|
23
|
+
].filter(Boolean).join('\n')).join('\n');
|
|
24
|
+
return `${XML_HEADER}<urlset ${SITEMAP_NS}>\n${entries}\n</urlset>`;
|
|
20
25
|
}
|
|
21
|
-
async function writeSitemap(urls, outputPath) {
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
export async function writeSitemap(urls, outputPath) {
|
|
27
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
28
|
+
await fs.writeFile(outputPath, buildSitemapXml(urls), 'utf8');
|
|
24
29
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
/** Convert crawled screen records to SitemapUrl objects */
|
|
31
|
+
export function screensToSitemapUrls(screens) {
|
|
32
|
+
return screens
|
|
33
|
+
.filter(s => {
|
|
34
|
+
try {
|
|
35
|
+
return new URL(s.url).protocol.startsWith('http');
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.map(s => ({
|
|
42
|
+
loc: s.url,
|
|
43
|
+
lastmod: s.updatedAt ? s.updatedAt.toISOString().split('T')[0] : undefined,
|
|
44
|
+
changefreq: 'weekly',
|
|
45
|
+
priority: s.depth === 0 ? 1.0 : s.depth === 1 ? 0.8 : 0.6,
|
|
46
|
+
}));
|
|
38
47
|
}
|
|
39
|
-
function buildSitemapString(urls) {
|
|
40
|
-
|
|
48
|
+
export function buildSitemapString(urls) {
|
|
49
|
+
return buildSitemapXml(urls);
|
|
41
50
|
}
|
|
42
|
-
export {
|
|
43
|
-
buildSitemapString,
|
|
44
|
-
screensToSitemapUrls,
|
|
45
|
-
writeSitemap
|
|
46
|
-
};
|
|
47
|
-
//# sourceMappingURL=sitemap-writer.js.map
|