@detiq/crawler 1.1.9 → 1.1.11

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 (83) 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
  83. package/src/job-queue.ts +136 -0
@@ -1,143 +1,117 @@
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
- */
11
1
  function jsStringLiteral(s) {
12
- return JSON.stringify(s);
2
+ return JSON.stringify(s);
13
3
  }
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. */
18
4
  function pythonDictLiteral(obj) {
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(', ')}}`;
5
+ const parts = Object.entries(obj).map(([k, v]) => {
6
+ let repr;
7
+ if (typeof v === "boolean") repr = v ? "True" : "False";
8
+ else if (v === null || v === void 0) repr = "None";
9
+ else if (typeof v === "number") repr = String(v);
10
+ else repr = JSON.stringify(v);
11
+ return `${JSON.stringify(k)}: ${repr}`;
12
+ });
13
+ return `{${parts.join(", ")}}`;
32
14
  }
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');
15
+ function toCypressSnippet(state) {
16
+ const cookieLines = state.cookies.map((c) => {
17
+ const opts = [`domain: ${jsStringLiteral(c.domain)}`, `path: ${jsStringLiteral(c.path)}`];
18
+ if (c.secure) opts.push("secure: true");
19
+ if (c.httpOnly) opts.push("httpOnly: true");
20
+ if (c.sameSite) opts.push(`sameSite: ${jsStringLiteral(c.sameSite.toLowerCase())}`);
21
+ return ` cy.setCookie(${jsStringLiteral(c.name)}, ${jsStringLiteral(c.value)}, { ${opts.join(", ")} });`;
22
+ });
23
+ const localStorageBlocks = state.origins.filter((o) => o.localStorage.length > 0).map((o) => {
24
+ const sets = o.localStorage.map((kv) => ` window.localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`).join("\n");
25
+ return ` cy.visit(${jsStringLiteral(o.origin)});
26
+ cy.window().then((window) => {
27
+ ${sets}
28
+ });`;
29
+ });
30
+ return [
31
+ "// Generated by ZeTa-AI \u2014 restores a captured browser session in Cypress.",
32
+ "// Usage: import this file and call restoreZetaSession() inside cy.session(), e.g.:",
33
+ "// cy.session('zeta-session', restoreZetaSession)",
34
+ "Cypress.Commands.add('restoreZetaSession', () => {",
35
+ ...cookieLines,
36
+ ...localStorageBlocks,
37
+ "});"
38
+ ].join("\n");
64
39
  }
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);
40
+ function toSeleniumPythonDict(state) {
41
+ const byDomain = /* @__PURE__ */ new Map();
42
+ for (const c of state.cookies) {
43
+ const list = byDomain.get(c.domain) ?? [];
44
+ list.push(c);
45
+ byDomain.set(c.domain, list);
46
+ }
47
+ const lines = [
48
+ "# Generated by ZeTa-AI \u2014 restores a captured browser session in Selenium.",
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)})`);
74
60
  }
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
- }
61
+ }
62
+ for (const o of state.origins) {
63
+ if (o.localStorage.length === 0) continue;
64
+ lines.push(` driver.get(${JSON.stringify(o.origin)})`);
65
+ for (const kv of o.localStorage) {
66
+ lines.push(
67
+ ` driver.execute_script(${JSON.stringify(`window.localStorage.setItem(arguments[0], arguments[1]);`)}, ${JSON.stringify(kv.name)}, ${JSON.stringify(kv.value)})`
68
+ );
91
69
  }
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');
70
+ }
71
+ return lines.join("\n");
101
72
  }
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');
73
+ function toWebdriverIOSnippet(state) {
74
+ const cookies = state.cookies.map((c) => ({
75
+ name: c.name,
76
+ value: c.value,
77
+ domain: c.domain,
78
+ path: c.path,
79
+ secure: !!c.secure,
80
+ httpOnly: !!c.httpOnly,
81
+ ...c.expires && c.expires > 0 ? { expiry: Math.floor(c.expires) } : {}
82
+ }));
83
+ const localStorageSets = state.origins.filter((o) => o.localStorage.length > 0).map((o) => {
84
+ const sets = o.localStorage.map((kv) => ` localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`).join("\n");
85
+ return ` await browser.url(${jsStringLiteral(o.origin)});
86
+ await browser.execute(() => {
87
+ ${sets}
88
+ });`;
89
+ });
90
+ return [
91
+ "// Generated by ZeTa-AI \u2014 restores a captured browser session in WebdriverIO.",
92
+ "async function restoreZetaSession(browser) {",
93
+ ` await browser.setCookies(${JSON.stringify(cookies, null, 2).split("\n").join("\n ")});`,
94
+ ...localStorageSets,
95
+ "}",
96
+ "module.exports = { restoreZetaSession };"
97
+ ].join("\n");
131
98
  }
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
- }
99
+ function exportSessionAs(format, state) {
100
+ switch (format) {
101
+ case "cypress":
102
+ return { content: toCypressSnippet(state), filename: "zeta-session.cy-commands.js", mimeType: "application/javascript" };
103
+ case "selenium-python":
104
+ return { content: toSeleniumPythonDict(state), filename: "zeta_session.py", mimeType: "text/x-python" };
105
+ case "webdriverio":
106
+ return { content: toWebdriverIOSnippet(state), filename: "zeta-session.wdio.js", mimeType: "application/javascript" };
107
+ default:
108
+ throw new Error(`Unknown session export format: ${format}`);
109
+ }
143
110
  }
111
+ export {
112
+ exportSessionAs,
113
+ toCypressSnippet,
114
+ toSeleniumPythonDict,
115
+ toWebdriverIOSnippet
116
+ };
117
+ //# sourceMappingURL=session-export.js.map
@@ -1,50 +1,47 @@
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';
1
+ import * as fs from "fs/promises";
2
+ import * as path from "path";
10
3
  const XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>\n';
11
4
  const SITEMAP_NS = 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
12
5
  function escape(s) {
13
- return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
6
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
14
7
  }
15
8
  function buildSitemapXml(urls) {
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>`;
9
+ const entries = urls.map((u) => [
10
+ " <url>",
11
+ ` <loc>${escape(u.loc)}</loc>`,
12
+ u.lastmod ? ` <lastmod>${u.lastmod}</lastmod>` : "",
13
+ u.changefreq ? ` <changefreq>${u.changefreq}</changefreq>` : "",
14
+ u.priority !== void 0 ? ` <priority>${u.priority.toFixed(1)}</priority>` : "",
15
+ " </url>"
16
+ ].filter(Boolean).join("\n")).join("\n");
17
+ return `${XML_HEADER}<urlset ${SITEMAP_NS}>
18
+ ${entries}
19
+ </urlset>`;
25
20
  }
26
- export async function writeSitemap(urls, outputPath) {
27
- await fs.mkdir(path.dirname(outputPath), { recursive: true });
28
- await fs.writeFile(outputPath, buildSitemapXml(urls), 'utf8');
21
+ async function writeSitemap(urls, outputPath) {
22
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
23
+ await fs.writeFile(outputPath, buildSitemapXml(urls), "utf8");
29
24
  }
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
- }));
25
+ function screensToSitemapUrls(screens) {
26
+ return screens.filter((s) => {
27
+ try {
28
+ return new URL(s.url).protocol.startsWith("http");
29
+ } catch {
30
+ return false;
31
+ }
32
+ }).map((s) => ({
33
+ loc: s.url,
34
+ lastmod: s.updatedAt ? s.updatedAt.toISOString().split("T")[0] : void 0,
35
+ changefreq: "weekly",
36
+ priority: s.depth === 0 ? 1 : s.depth === 1 ? 0.8 : 0.6
37
+ }));
47
38
  }
48
- export function buildSitemapString(urls) {
49
- return buildSitemapXml(urls);
39
+ function buildSitemapString(urls) {
40
+ return buildSitemapXml(urls);
50
41
  }
42
+ export {
43
+ buildSitemapString,
44
+ screensToSitemapUrls,
45
+ writeSitemap
46
+ };
47
+ //# sourceMappingURL=sitemap-writer.js.map