@rivi-software/agentic-seo-check 0.0.0-stage → 0.1.1

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -2
  3. package/dist/brand.d.ts +11 -0
  4. package/dist/brand.js +22 -0
  5. package/dist/brand.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +194 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/core/audit.d.ts +72 -0
  10. package/dist/core/audit.js +452 -0
  11. package/dist/core/audit.js.map +1 -0
  12. package/dist/core/concurrency.d.ts +4 -0
  13. package/dist/core/concurrency.js +39 -0
  14. package/dist/core/concurrency.js.map +1 -0
  15. package/dist/core/crawl.d.ts +40 -0
  16. package/dist/core/crawl.js +324 -0
  17. package/dist/core/crawl.js.map +1 -0
  18. package/dist/core/fetch.d.ts +27 -0
  19. package/dist/core/fetch.js +186 -0
  20. package/dist/core/fetch.js.map +1 -0
  21. package/dist/core/html/dom.d.ts +13 -0
  22. package/dist/core/html/dom.js +17 -0
  23. package/dist/core/html/dom.js.map +1 -0
  24. package/dist/core/html/extract.d.ts +10 -0
  25. package/dist/core/html/extract.js +207 -0
  26. package/dist/core/html/extract.js.map +1 -0
  27. package/dist/core/html/structured-data.d.ts +21 -0
  28. package/dist/core/html/structured-data.js +220 -0
  29. package/dist/core/html/structured-data.js.map +1 -0
  30. package/dist/core/html/text.d.ts +23 -0
  31. package/dist/core/html/text.js +152 -0
  32. package/dist/core/html/text.js.map +1 -0
  33. package/dist/core/lighthouse.d.ts +50 -0
  34. package/dist/core/lighthouse.js +216 -0
  35. package/dist/core/lighthouse.js.map +1 -0
  36. package/dist/core/performance-rules.d.ts +3 -0
  37. package/dist/core/performance-rules.js +125 -0
  38. package/dist/core/performance-rules.js.map +1 -0
  39. package/dist/core/render-rules.d.ts +8 -0
  40. package/dist/core/render-rules.js +144 -0
  41. package/dist/core/render-rules.js.map +1 -0
  42. package/dist/core/render.d.ts +22 -0
  43. package/dist/core/render.js +79 -0
  44. package/dist/core/render.js.map +1 -0
  45. package/dist/core/report/markdown.d.ts +2 -0
  46. package/dist/core/report/markdown.js +131 -0
  47. package/dist/core/report/markdown.js.map +1 -0
  48. package/dist/core/robots.d.ts +11 -0
  49. package/dist/core/robots.js +48 -0
  50. package/dist/core/robots.js.map +1 -0
  51. package/dist/core/rules/canonical.d.ts +2 -0
  52. package/dist/core/rules/canonical.js +141 -0
  53. package/dist/core/rules/canonical.js.map +1 -0
  54. package/dist/core/rules/content.d.ts +1 -0
  55. package/dist/core/rules/content.js +55 -0
  56. package/dist/core/rules/content.js.map +1 -0
  57. package/dist/core/rules/document.d.ts +1 -0
  58. package/dist/core/rules/document.js +83 -0
  59. package/dist/core/rules/document.js.map +1 -0
  60. package/dist/core/rules/headings.d.ts +1 -0
  61. package/dist/core/rules/headings.js +81 -0
  62. package/dist/core/rules/headings.js.map +1 -0
  63. package/dist/core/rules/hreflang.d.ts +1 -0
  64. package/dist/core/rules/hreflang.js +100 -0
  65. package/dist/core/rules/hreflang.js.map +1 -0
  66. package/dist/core/rules/images.d.ts +1 -0
  67. package/dist/core/rules/images.js +77 -0
  68. package/dist/core/rules/images.js.map +1 -0
  69. package/dist/core/rules/index.d.ts +14 -0
  70. package/dist/core/rules/index.js +77 -0
  71. package/dist/core/rules/index.js.map +1 -0
  72. package/dist/core/rules/indexing.d.ts +10 -0
  73. package/dist/core/rules/indexing.js +75 -0
  74. package/dist/core/rules/indexing.js.map +1 -0
  75. package/dist/core/rules/keyword.d.ts +1 -0
  76. package/dist/core/rules/keyword.js +111 -0
  77. package/dist/core/rules/keyword.js.map +1 -0
  78. package/dist/core/rules/links.d.ts +1 -0
  79. package/dist/core/rules/links.js +107 -0
  80. package/dist/core/rules/links.js.map +1 -0
  81. package/dist/core/rules/meta.d.ts +1 -0
  82. package/dist/core/rules/meta.js +81 -0
  83. package/dist/core/rules/meta.js.map +1 -0
  84. package/dist/core/rules/response.d.ts +1 -0
  85. package/dist/core/rules/response.js +93 -0
  86. package/dist/core/rules/response.js.map +1 -0
  87. package/dist/core/rules/rule.d.ts +42 -0
  88. package/dist/core/rules/rule.js +51 -0
  89. package/dist/core/rules/rule.js.map +1 -0
  90. package/dist/core/rules/social.d.ts +1 -0
  91. package/dist/core/rules/social.js +115 -0
  92. package/dist/core/rules/social.js.map +1 -0
  93. package/dist/core/rules/structured-data.d.ts +25 -0
  94. package/dist/core/rules/structured-data.js +381 -0
  95. package/dist/core/rules/structured-data.js.map +1 -0
  96. package/dist/core/rules/title.d.ts +1 -0
  97. package/dist/core/rules/title.js +74 -0
  98. package/dist/core/rules/title.js.map +1 -0
  99. package/dist/core/score.d.ts +3 -0
  100. package/dist/core/score.js +17 -0
  101. package/dist/core/score.js.map +1 -0
  102. package/dist/core/site-rules.d.ts +64 -0
  103. package/dist/core/site-rules.js +462 -0
  104. package/dist/core/site-rules.js.map +1 -0
  105. package/dist/core/sitemap.d.ts +13 -0
  106. package/dist/core/sitemap.js +140 -0
  107. package/dist/core/sitemap.js.map +1 -0
  108. package/dist/core/static.d.ts +9 -0
  109. package/dist/core/static.js +281 -0
  110. package/dist/core/static.js.map +1 -0
  111. package/dist/core/summary.d.ts +2 -0
  112. package/dist/core/summary.js +33 -0
  113. package/dist/core/summary.js.map +1 -0
  114. package/dist/core/types.d.ts +296 -0
  115. package/dist/core/types.js +33 -0
  116. package/dist/core/types.js.map +1 -0
  117. package/dist/core/url.d.ts +20 -0
  118. package/dist/core/url.js +191 -0
  119. package/dist/core/url.js.map +1 -0
  120. package/dist/index.d.ts +11 -0
  121. package/dist/index.js +10 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/mcp-bin.d.ts +2 -0
  124. package/dist/mcp-bin.js +10 -0
  125. package/dist/mcp-bin.js.map +1 -0
  126. package/dist/mcp.d.ts +5 -0
  127. package/dist/mcp.js +380 -0
  128. package/dist/mcp.js.map +1 -0
  129. package/dist/version.d.ts +1 -0
  130. package/dist/version.js +6 -0
  131. package/dist/version.js.map +1 -0
  132. package/package.json +107 -3
  133. package/skills/seo-audit/SKILL.md +53 -0
@@ -0,0 +1,324 @@
1
+ import { mapConcurrent, sleep } from "./concurrency.js";
2
+ import { DEFAULT_USER_AGENT, fetchPage, isHtmlResponse, probeUrl, toResponseInfo, } from "./fetch.js";
3
+ import { extractPage } from "./html/extract.js";
4
+ import { loadRobotsTxt } from "./robots.js";
5
+ import { isLocalHost } from "./rules/canonical.js";
6
+ import { loadSitemaps } from "./sitemap.js";
7
+ import { hostOf, isSameSite, looksLikeHtml, normalizeUrl, stripWww, } from "./url.js";
8
+ /**
9
+ * Works through a queue that grows while it runs: each crawled page can add the links it found.
10
+ * At most `concurrency` items run at once, and with `delayMs` each slot waits that long after its
11
+ * item before it takes the next one. An item that throws is left out; the rest go on.
12
+ */
13
+ const runQueue = async (queue, concurrency, delayMs, work) => {
14
+ const limit = Math.max(1, concurrency);
15
+ const running = new Set();
16
+ const launch = (item) => {
17
+ const task = (async () => {
18
+ try {
19
+ await work(item);
20
+ }
21
+ catch {
22
+ // The page is left out of the crawl; the rest of the queue goes on.
23
+ }
24
+ if (delayMs > 0) {
25
+ await sleep(delayMs);
26
+ }
27
+ })().finally(() => running.delete(task));
28
+ running.add(task);
29
+ };
30
+ for (;;) {
31
+ while (running.size < limit) {
32
+ const item = queue.shift();
33
+ if (!item) {
34
+ break;
35
+ }
36
+ launch(item);
37
+ }
38
+ if (running.size === 0) {
39
+ return;
40
+ }
41
+ // oxlint-disable-next-line eslint/no-await-in-loop -- the queue grows as pages finish, so what to start next is only known once a slot frees
42
+ await Promise.race(running);
43
+ }
44
+ };
45
+ /** Adds `source` to the set of pages that link to `target`. */
46
+ const addSource = (sources, target, source) => {
47
+ let set = sources.get(target);
48
+ if (!set) {
49
+ set = new Set();
50
+ sources.set(target, set);
51
+ }
52
+ set.add(source);
53
+ };
54
+ const runProbes = async (startUrl, fetchOpts) => {
55
+ const probes = {};
56
+ const url = new URL(startUrl);
57
+ const soft404Url = new URL(`/agentic-seo-probe-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, url.origin).href;
58
+ const soft = await probeUrl(soft404Url, fetchOpts);
59
+ probes.soft404 = soft.error ? null : { url: soft404Url, status: soft.status };
60
+ if (isLocalHost(startUrl)) {
61
+ return probes;
62
+ }
63
+ if (url.protocol === "https:") {
64
+ const httpUrl = `http://${url.host}/`;
65
+ const res = await probeUrl(httpUrl, { ...fetchOpts, timeoutMs: 10_000 });
66
+ probes.httpRedirect = res.error
67
+ ? null
68
+ : {
69
+ url: httpUrl,
70
+ status: res.status,
71
+ finalUrl: res.finalUrl,
72
+ redirectsToHttps: res.finalUrl.startsWith("https://"),
73
+ };
74
+ }
75
+ const host = url.hostname;
76
+ const variantHost = host.startsWith("www.") ? stripWww(host) : `www.${host}`;
77
+ const variantUrl = `${url.protocol}//${variantHost}/`;
78
+ const res = await probeUrl(variantUrl, { ...fetchOpts, timeoutMs: 10_000 });
79
+ probes.hostVariant = res.error
80
+ ? null
81
+ : {
82
+ url: variantUrl,
83
+ status: res.status,
84
+ finalUrl: res.finalUrl,
85
+ redirectsToMain: (hostOf(res.finalUrl) ?? "") === host,
86
+ };
87
+ return probes;
88
+ };
89
+ /** The progress callback of a caller that does not listen for progress. */
90
+ const ignoreProgress = () => {
91
+ // Nobody to tell.
92
+ };
93
+ export const crawlSite = async (startInput, options) => {
94
+ const started = performance.now();
95
+ const progress = options.onProgress ?? ignoreProgress;
96
+ const startUrl = normalizeUrl(startInput) ?? startInput;
97
+ const { origin } = new URL(startUrl);
98
+ const startHost = hostOf(startUrl) ?? "";
99
+ const userAgent = options.fetch.userAgent ?? DEFAULT_USER_AGENT;
100
+ progress({ phase: "robots", message: `Fetching ${origin}/robots.txt` });
101
+ const robots = await loadRobotsTxt(origin, userAgent, options.fetch);
102
+ const allows = options.respectRobots ? robots.allows : () => true;
103
+ progress({ phase: "sitemap", message: "Fetching sitemaps" });
104
+ const sitemapCandidates = [
105
+ ...new Set([...robots.info.sitemaps, new URL("/sitemap.xml", origin).href]),
106
+ ];
107
+ const sitemaps = await loadSitemaps(sitemapCandidates, options.fetch);
108
+ const visited = new Set([startUrl]);
109
+ const discovered = new Set([startUrl]);
110
+ const linkChecks = new Map();
111
+ const linkSources = new Map();
112
+ const externalSources = new Map();
113
+ const records = [];
114
+ const blocked = [];
115
+ let limitSkips = 0;
116
+ const accepts = (url) => {
117
+ if (options.includePatterns.length &&
118
+ !options.includePatterns.some((re) => re.test(url))) {
119
+ return false;
120
+ }
121
+ if (options.excludePatterns.some((re) => re.test(url))) {
122
+ return false;
123
+ }
124
+ return true;
125
+ };
126
+ const queue = [{ url: startUrl, depth: 0 }];
127
+ /** Queues the internal links a crawled page has, and remembers every link's sources. */
128
+ const followLinks = (links, from, depth) => {
129
+ for (const link of links) {
130
+ const target = link.url ? normalizeUrl(link.url) : null;
131
+ if (!target) {
132
+ continue;
133
+ }
134
+ if (!link.internal) {
135
+ addSource(externalSources, target, from);
136
+ continue;
137
+ }
138
+ addSource(linkSources, target, from);
139
+ if (!looksLikeHtml(target) ||
140
+ !accepts(target) ||
141
+ discovered.has(target)) {
142
+ continue;
143
+ }
144
+ discovered.add(target);
145
+ if (depth + 1 > options.maxDepth) {
146
+ continue;
147
+ }
148
+ if (visited.size >= options.maxPages) {
149
+ limitSkips += 1;
150
+ continue;
151
+ }
152
+ visited.add(target);
153
+ queue.push({ url: target, depth: depth + 1 });
154
+ }
155
+ };
156
+ /**
157
+ * Records where a redirected request ended. False when there is nothing more to crawl there:
158
+ * the redirect led to a page crawled already, or off the site.
159
+ */
160
+ const keepsRedirectTarget = (res, finalKey) => {
161
+ if (!linkChecks.has(finalKey)) {
162
+ linkChecks.set(finalKey, {
163
+ url: res.finalUrl,
164
+ status: res.status,
165
+ finalUrl: res.finalUrl,
166
+ redirectChain: [],
167
+ error: res.error,
168
+ });
169
+ }
170
+ if (visited.has(finalKey) && records.some((r) => r.url === finalKey)) {
171
+ return false;
172
+ }
173
+ visited.add(finalKey);
174
+ return isSameSite(finalKey, startHost, options.includeSubdomains);
175
+ };
176
+ /** A response that is no HTML page: an asset is skipped, an error page may still be recorded. */
177
+ const recordUnreadable = (item, res, finalKey) => {
178
+ const isAsset = res.status !== null &&
179
+ res.status < 400 &&
180
+ !res.error &&
181
+ res.body !== null &&
182
+ !isHtmlResponse(res);
183
+ // Record error pages so page-level findings (response.error-status) appear for the start URL.
184
+ if (isAsset || (item.depth !== 0 && res.status !== null)) {
185
+ return;
186
+ }
187
+ const page = extractPage(res.body ?? "", {
188
+ url: res.finalUrl,
189
+ siteHost: startHost,
190
+ includeSubdomains: options.includeSubdomains,
191
+ });
192
+ records.push({
193
+ url: finalKey,
194
+ requestedUrl: item.url,
195
+ page,
196
+ response: toResponseInfo(res),
197
+ depth: item.depth,
198
+ });
199
+ };
200
+ const processItem = async (item) => {
201
+ if (!allows(item.url)) {
202
+ blocked.push(item.url);
203
+ return;
204
+ }
205
+ const res = await fetchPage(item.url, options.fetch);
206
+ const requestedKey = item.url;
207
+ const finalKey = normalizeUrl(res.finalUrl) ?? res.finalUrl;
208
+ linkChecks.set(requestedKey, {
209
+ url: item.url,
210
+ status: res.status,
211
+ finalUrl: res.finalUrl,
212
+ redirectChain: res.redirectChain,
213
+ error: res.error,
214
+ });
215
+ if (finalKey !== requestedKey && !keepsRedirectTarget(res, finalKey)) {
216
+ return;
217
+ }
218
+ progress({
219
+ phase: "crawl",
220
+ message: `${res.status ?? "ERR"} ${res.finalUrl}`,
221
+ crawled: records.length + 1,
222
+ discovered: discovered.size,
223
+ url: res.finalUrl,
224
+ });
225
+ if (res.error || res.body === null || !isHtmlResponse(res)) {
226
+ recordUnreadable(item, res, finalKey);
227
+ return;
228
+ }
229
+ const page = extractPage(res.body, {
230
+ url: res.finalUrl,
231
+ siteHost: startHost,
232
+ includeSubdomains: options.includeSubdomains,
233
+ });
234
+ records.push({
235
+ url: finalKey,
236
+ requestedUrl: item.url,
237
+ page,
238
+ response: toResponseInfo(res),
239
+ depth: item.depth,
240
+ });
241
+ followLinks(page.links, finalKey, item.depth);
242
+ };
243
+ await runQueue(queue, options.concurrency, options.delayMs, processItem);
244
+ /* Check internal link targets that were discovered but not crawled (limits, depth, non-HTML). */
245
+ progress({ phase: "links", message: "Checking internal link targets" });
246
+ const toCheck = [...linkSources.keys()]
247
+ .filter((u) => !linkChecks.has(u) && allows(u))
248
+ .slice(0, options.maxLinkChecks);
249
+ const sitemapExtra = sitemaps.urls
250
+ .map((u) => normalizeUrl(u) ?? u)
251
+ .filter((u) => isSameSite(u, startHost, options.includeSubdomains) &&
252
+ !linkChecks.has(u) &&
253
+ !toCheck.includes(u) &&
254
+ allows(u))
255
+ .slice(0, Math.max(0, options.maxLinkChecks - toCheck.length));
256
+ for (const target of [...linkSources.keys()].filter((u) => !allows(u))) {
257
+ if (!blocked.includes(target)) {
258
+ blocked.push(target);
259
+ }
260
+ }
261
+ await mapConcurrent([...toCheck, ...sitemapExtra], options.concurrency, async (u) => {
262
+ const res = await probeUrl(u, options.fetch);
263
+ linkChecks.set(u, {
264
+ url: u,
265
+ status: res.status,
266
+ finalUrl: res.finalUrl,
267
+ redirectChain: res.redirectChain,
268
+ error: res.error,
269
+ });
270
+ if (options.delayMs) {
271
+ await sleep(options.delayMs);
272
+ }
273
+ });
274
+ let externalChecks;
275
+ if (options.checkExternalLinks) {
276
+ progress({
277
+ phase: "links",
278
+ message: `Checking ${Math.min(externalSources.size, options.maxLinkChecks)} external links`,
279
+ });
280
+ const checks = new Map();
281
+ externalChecks = checks;
282
+ const targets = [...externalSources.keys()].slice(0, options.maxLinkChecks);
283
+ await mapConcurrent(targets, Math.min(options.concurrency, 4), async (u) => {
284
+ const res = await probeUrl(u, {
285
+ ...options.fetch,
286
+ timeoutMs: Math.min(options.fetch.timeoutMs ?? 15_000, 10_000),
287
+ });
288
+ checks.set(u, {
289
+ url: u,
290
+ status: res.status,
291
+ finalUrl: res.finalUrl,
292
+ redirectChain: res.redirectChain,
293
+ error: res.error,
294
+ });
295
+ });
296
+ }
297
+ progress({ phase: "probes", message: "Probing soft-404 and host redirects" });
298
+ const probes = await runProbes(startUrl, options.fetch);
299
+ const stats = {
300
+ pagesCrawled: records.length,
301
+ pagesDiscovered: discovered.size,
302
+ linksChecked: linkChecks.size + (externalChecks?.size ?? 0),
303
+ maxPages: options.maxPages,
304
+ maxDepth: options.maxDepth,
305
+ hitPageLimit: limitSkips > 0,
306
+ durationMs: Math.round(performance.now() - started),
307
+ respectedRobots: options.respectRobots,
308
+ };
309
+ return {
310
+ startUrl,
311
+ records,
312
+ linkChecks,
313
+ linkSources,
314
+ externalChecks,
315
+ externalSources: options.checkExternalLinks ? externalSources : undefined,
316
+ robots: robots.info,
317
+ robotsAllows: robots.info.found ? robots.allows : null,
318
+ blockedByRobots: blocked,
319
+ sitemaps,
320
+ probes,
321
+ stats,
322
+ };
323
+ };
324
+ //# sourceMappingURL=crawl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawl.js","sourceRoot":"","sources":["../../src/core/crawl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { RedirectHop, ResponseInfo } from "./types.js";
2
+ export declare const DEFAULT_USER_AGENT: string;
3
+ export interface FetchOptions {
4
+ timeoutMs?: number;
5
+ userAgent?: string;
6
+ headers?: Record<string, string>;
7
+ maxRedirects?: number;
8
+ /** Stop reading the body after this many bytes. */
9
+ maxBodyBytes?: number;
10
+ }
11
+ export interface FetchResult {
12
+ requestedUrl: string;
13
+ finalUrl: string;
14
+ status: number | null;
15
+ headers: Record<string, string>;
16
+ body: string | null;
17
+ redirectChain: RedirectHop[];
18
+ timeMs: number;
19
+ contentType: string | null;
20
+ error?: string;
21
+ }
22
+ /** GET a URL, following redirects manually so the chain is recorded. */
23
+ export declare const fetchPage: (url: string, options?: FetchOptions, readBody?: boolean) => Promise<FetchResult>;
24
+ /** Status/redirect check without downloading the body. */
25
+ export declare const probeUrl: (url: string, options?: FetchOptions) => Promise<FetchResult>;
26
+ export declare const toResponseInfo: (r: FetchResult) => ResponseInfo;
27
+ export declare const isHtmlResponse: (r: FetchResult) => boolean;
@@ -0,0 +1,186 @@
1
+ import { VERSION } from "../version.js";
2
+ export const DEFAULT_USER_AGENT = `Mozilla/5.0 (compatible; agentic-seo-check/${VERSION}; +https://github.com/rivi-software/agentic-seo-check)`;
3
+ const KEPT_HEADERS = [
4
+ "content-type",
5
+ "x-robots-tag",
6
+ "link",
7
+ "content-language",
8
+ "cache-control",
9
+ "last-modified",
10
+ "location",
11
+ "content-length",
12
+ "vary",
13
+ ];
14
+ const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
15
+ const pickHeaders = (headers) => {
16
+ const out = {};
17
+ for (const name of KEPT_HEADERS) {
18
+ const value = headers.get(name);
19
+ if (value !== null) {
20
+ out[name] = value;
21
+ }
22
+ }
23
+ return out;
24
+ };
25
+ const result = (requestedUrl, finalUrl, status, headers, body, chain, started, error) => {
26
+ const r = {
27
+ requestedUrl,
28
+ finalUrl,
29
+ status,
30
+ headers,
31
+ body,
32
+ redirectChain: chain,
33
+ timeMs: Math.round(performance.now() - started),
34
+ contentType: headers["content-type"] ?? null,
35
+ };
36
+ if (error) {
37
+ r.error = error;
38
+ }
39
+ return r;
40
+ };
41
+ const readLimited = async (res, maxBytes) => {
42
+ if (!res.body) {
43
+ return "";
44
+ }
45
+ const reader = res.body.getReader();
46
+ const chunks = [];
47
+ let total = 0;
48
+ let truncated = false;
49
+ for (;;) {
50
+ // oxlint-disable-next-line eslint/no-await-in-loop -- a stream hands over its chunks one after another
51
+ const { done, value } = await reader.read();
52
+ if (done) {
53
+ break;
54
+ }
55
+ if (value) {
56
+ chunks.push(value);
57
+ total += value.byteLength;
58
+ if (total >= maxBytes) {
59
+ truncated = true;
60
+ break;
61
+ }
62
+ }
63
+ }
64
+ if (truncated) {
65
+ await reader.cancel().catch(() => null);
66
+ }
67
+ const charset = /charset=(?<charset>[\w-]+)/iu.exec(res.headers.get("content-type") ?? "")?.groups?.charset;
68
+ try {
69
+ return new TextDecoder(charset ?? "utf-8").decode(Buffer.concat(chunks));
70
+ }
71
+ catch {
72
+ return new TextDecoder("utf-8").decode(Buffer.concat(chunks));
73
+ }
74
+ };
75
+ const fetchHop = async (walk, current) => {
76
+ const { url, options, readBody, timeoutMs, started, chain } = walk;
77
+ const controller = new AbortController();
78
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
79
+ try {
80
+ const res = await fetch(current, {
81
+ method: "GET",
82
+ redirect: "manual",
83
+ signal: controller.signal,
84
+ headers: {
85
+ "user-agent": options.userAgent ?? DEFAULT_USER_AGENT,
86
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
87
+ "accept-language": "en,*;q=0.5",
88
+ ...options.headers,
89
+ },
90
+ });
91
+ const headers = pickHeaders(res.headers);
92
+ if (REDIRECT_STATUSES.has(res.status) && headers["location"]) {
93
+ chain.push({ url: current, status: res.status });
94
+ let nextUrl;
95
+ try {
96
+ nextUrl = new URL(headers["location"], current).href;
97
+ }
98
+ catch {
99
+ return {
100
+ done: result(url, current, res.status, headers, null, chain, started, `Invalid Location header: ${headers["location"]}`),
101
+ };
102
+ }
103
+ await res.body?.cancel().catch(() => null);
104
+ if (chain.some((h) => h.url === nextUrl) || nextUrl === current) {
105
+ return {
106
+ done: result(url, nextUrl, res.status, headers, null, chain, started, "Redirect loop"),
107
+ };
108
+ }
109
+ return { next: nextUrl };
110
+ }
111
+ let body = null;
112
+ if (readBody) {
113
+ body = await readLimited(res, options.maxBodyBytes ?? 5_000_000);
114
+ }
115
+ else {
116
+ await res.body?.cancel().catch(() => null);
117
+ }
118
+ return {
119
+ done: result(url, current, res.status, headers, body, chain, started),
120
+ };
121
+ }
122
+ catch (error) {
123
+ let message = String(error);
124
+ if (error instanceof Error) {
125
+ message =
126
+ error.name === "AbortError"
127
+ ? `Timed out after ${timeoutMs} ms`
128
+ : error.message;
129
+ }
130
+ return {
131
+ done: result(url, current, null, {}, null, chain, started, message),
132
+ };
133
+ }
134
+ finally {
135
+ clearTimeout(timer);
136
+ }
137
+ };
138
+ /** GET a URL, following redirects manually so the chain is recorded. */
139
+ export const fetchPage = async (url, options = {}, readBody = true) => {
140
+ const maxRedirects = options.maxRedirects ?? 10;
141
+ const walk = {
142
+ url,
143
+ options,
144
+ readBody,
145
+ timeoutMs: options.timeoutMs ?? 15_000,
146
+ started: performance.now(),
147
+ chain: [],
148
+ };
149
+ let current = url;
150
+ for (let hop = 0; hop <= maxRedirects; hop += 1) {
151
+ // oxlint-disable-next-line eslint/no-await-in-loop -- each request goes where the one before it redirected
152
+ const outcome = await fetchHop(walk, current);
153
+ if ("done" in outcome) {
154
+ return outcome.done;
155
+ }
156
+ current = outcome.next;
157
+ }
158
+ return result(url, current, null, {}, null, walk.chain, walk.started, `More than ${maxRedirects} redirects`);
159
+ };
160
+ /** Status/redirect check without downloading the body. */
161
+ export const probeUrl = (url, options = {}) => fetchPage(url, options, false);
162
+ export const toResponseInfo = (r) => {
163
+ const info = {
164
+ status: r.status,
165
+ finalUrl: r.finalUrl,
166
+ redirectChain: r.redirectChain,
167
+ timeMs: r.timeMs,
168
+ contentType: r.contentType,
169
+ headers: r.headers,
170
+ };
171
+ if (r.error) {
172
+ info.error = r.error;
173
+ }
174
+ return info;
175
+ };
176
+ export const isHtmlResponse = (r) => {
177
+ const ct = r.contentType?.toLowerCase() ?? "";
178
+ if (ct) {
179
+ return ct.includes("text/html") || ct.includes("application/xhtml");
180
+ }
181
+ if (!r.body) {
182
+ return false;
183
+ }
184
+ return /<html|<!doctype/iu.test(r.body.slice(0, 2000));
185
+ };
186
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/core/fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { AnyNode, Document, Element } from "domhandler";
2
+ export type { AnyNode, Document, Element } from "domhandler";
3
+ /** Parse a full HTML document with the WHATWG algorithm (parse5), scripting enabled like a browser. */
4
+ export declare const parseDocument: (html: string) => Document;
5
+ /** Descendants of `root` matching a CSS selector, in document order. */
6
+ export declare const queryAll: (root: AnyNode, selector: string) => Element[];
7
+ /** First descendant of `root` matching a CSS selector. */
8
+ export declare const queryOne: (root: AnyNode, selector: string) => Element | null;
9
+ export declare const attr: (el: Element, name: string) => string | undefined;
10
+ export declare const hasAttr: (el: Element, name: string) => boolean;
11
+ /** DOM `textContent`: concatenated text of all descendants, comments excluded. */
12
+ export declare const text: (node: AnyNode | AnyNode[]) => string;
13
+ export declare const childElements: (el: Element) => Element[];
@@ -0,0 +1,17 @@
1
+ import { selectAll, selectOne } from "css-select";
2
+ import { isTag } from "domhandler";
3
+ import { textContent } from "domutils";
4
+ import { parse } from "parse5";
5
+ import { adapter } from "parse5-htmlparser2-tree-adapter";
6
+ /** Parse a full HTML document with the WHATWG algorithm (parse5), scripting enabled like a browser. */
7
+ export const parseDocument = (html) => parse(html, { scriptingEnabled: true, treeAdapter: adapter });
8
+ /** Descendants of `root` matching a CSS selector, in document order. */
9
+ export const queryAll = (root, selector) => selectAll(selector, root);
10
+ /** First descendant of `root` matching a CSS selector. */
11
+ export const queryOne = (root, selector) => selectOne(selector, root);
12
+ export const attr = (el, name) => Object.hasOwn(el.attribs, name) ? el.attribs[name] : undefined;
13
+ export const hasAttr = (el, name) => Object.hasOwn(el.attribs, name);
14
+ /** DOM `textContent`: concatenated text of all descendants, comments excluded. */
15
+ export const text = (node) => textContent(node);
16
+ export const childElements = (el) => el.children.filter(isTag);
17
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../../src/core/html/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAI1D,uGAAuG;AACvG,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAY,EAAE,CACtD,KAAK,CAAC,IAAI,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;AAEhE,wEAAwE;AACxE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAa,EAAE,QAAgB,EAAa,EAAE,CACrE,SAAS,CAAmB,QAAQ,EAAE,IAAI,CAAC,CAAC;AAE9C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAa,EAAE,QAAgB,EAAkB,EAAE,CAC1E,SAAS,CAAmB,QAAQ,EAAE,IAAI,CAAC,CAAC;AAE9C,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,EAAW,EAAE,IAAY,EAAsB,EAAE,CACpE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAEjE,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EAAW,EAAE,IAAY,EAAW,EAAE,CAC5D,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAElC,kFAAkF;AAClF,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAAyB,EAAU,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AAE7E,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAW,EAAa,EAAE,CACtD,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ExtractedPage } from "../types.js";
2
+ export interface ExtractOptions {
3
+ /** Absolute URL of the document; relative links are resolved against it (or against <base href>). */
4
+ url: string;
5
+ /** Treat subdomains of the site host as internal. */
6
+ includeSubdomains?: boolean;
7
+ /** Host used to classify links as internal. Defaults to the host of `url`. */
8
+ siteHost?: string;
9
+ }
10
+ export declare const extractPage: (html: string, options: ExtractOptions) => ExtractedPage;