@rivi-software/agentic-seo-check 0.1.1 → 0.3.0
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/README.md +67 -7
- package/dist/cli.js +69 -26
- package/dist/cli.js.map +1 -1
- package/dist/core/aeo/audit.d.ts +44 -0
- package/dist/core/aeo/audit.js +310 -0
- package/dist/core/aeo/audit.js.map +1 -0
- package/dist/core/aeo/bots.d.ts +30 -0
- package/dist/core/aeo/bots.js +299 -0
- package/dist/core/aeo/bots.js.map +1 -0
- package/dist/core/aeo/llms-txt.d.ts +8 -0
- package/dist/core/aeo/llms-txt.js +88 -0
- package/dist/core/aeo/llms-txt.js.map +1 -0
- package/dist/core/aeo/page.d.ts +43 -0
- package/dist/core/aeo/page.js +284 -0
- package/dist/core/aeo/page.js.map +1 -0
- package/dist/core/aeo/probe.d.ts +10 -0
- package/dist/core/aeo/probe.js +53 -0
- package/dist/core/aeo/probe.js.map +1 -0
- package/dist/core/aeo/site-rules.d.ts +13 -0
- package/dist/core/aeo/site-rules.js +178 -0
- package/dist/core/aeo/site-rules.js.map +1 -0
- package/dist/core/audit.d.ts +23 -2
- package/dist/core/audit.js +50 -22
- package/dist/core/audit.js.map +1 -1
- package/dist/core/crawl.d.ts +7 -1
- package/dist/core/crawl.js +85 -9
- package/dist/core/crawl.js.map +1 -1
- package/dist/core/fetch.d.ts +9 -0
- package/dist/core/fetch.js +28 -12
- package/dist/core/fetch.js.map +1 -1
- package/dist/core/html/answer.d.ts +4 -0
- package/dist/core/html/answer.js +198 -0
- package/dist/core/html/answer.js.map +1 -0
- package/dist/core/html/dom.d.ts +6 -0
- package/dist/core/html/dom.js +92 -1
- package/dist/core/html/dom.js.map +1 -1
- package/dist/core/html/extract.js +21 -72
- package/dist/core/html/extract.js.map +1 -1
- package/dist/core/html/head.d.ts +10 -0
- package/dist/core/html/head.js +206 -0
- package/dist/core/html/head.js.map +1 -0
- package/dist/core/html/resources.d.ts +17 -0
- package/dist/core/html/resources.js +172 -0
- package/dist/core/html/resources.js.map +1 -0
- package/dist/core/html/structured-data.d.ts +2 -0
- package/dist/core/html/structured-data.js +2 -0
- package/dist/core/html/structured-data.js.map +1 -1
- package/dist/core/report/markdown.d.ts +3 -1
- package/dist/core/report/markdown.js +102 -0
- package/dist/core/report/markdown.js.map +1 -1
- package/dist/core/robots.d.ts +7 -0
- package/dist/core/robots.js +13 -1
- package/dist/core/robots.js.map +1 -1
- package/dist/core/rules/aeo.d.ts +11 -0
- package/dist/core/rules/aeo.js +423 -0
- package/dist/core/rules/aeo.js.map +1 -0
- package/dist/core/rules/canonical.d.ts +3 -0
- package/dist/core/rules/canonical.js +54 -2
- package/dist/core/rules/canonical.js.map +1 -1
- package/dist/core/rules/document.js +4 -2
- package/dist/core/rules/document.js.map +1 -1
- package/dist/core/rules/head.d.ts +1 -0
- package/dist/core/rules/head.js +116 -0
- package/dist/core/rules/head.js.map +1 -0
- package/dist/core/rules/index.d.ts +4 -0
- package/dist/core/rules/index.js +15 -1
- package/dist/core/rules/index.js.map +1 -1
- package/dist/core/rules/indexing.d.ts +10 -0
- package/dist/core/rules/indexing.js +171 -9
- package/dist/core/rules/indexing.js.map +1 -1
- package/dist/core/rules/loading.d.ts +1 -0
- package/dist/core/rules/loading.js +138 -0
- package/dist/core/rules/loading.js.map +1 -0
- package/dist/core/rules/rule.d.ts +2 -0
- package/dist/core/rules/rule.js +2 -0
- package/dist/core/rules/rule.js.map +1 -1
- package/dist/core/rules/structured-data-values.d.ts +3 -0
- package/dist/core/rules/structured-data-values.js +316 -0
- package/dist/core/rules/structured-data-values.js.map +1 -0
- package/dist/core/rules/structured-data.js +50 -20
- package/dist/core/rules/structured-data.js.map +1 -1
- package/dist/core/rules/urls.d.ts +1 -0
- package/dist/core/rules/urls.js +293 -0
- package/dist/core/rules/urls.js.map +1 -0
- package/dist/core/score.d.ts +4 -2
- package/dist/core/score.js.map +1 -1
- package/dist/core/site-probes.d.ts +33 -0
- package/dist/core/site-probes.js +144 -0
- package/dist/core/site-probes.js.map +1 -0
- package/dist/core/site-rules.d.ts +18 -0
- package/dist/core/site-rules.js +479 -5
- package/dist/core/site-rules.js.map +1 -1
- package/dist/core/sitemap.d.ts +38 -1
- package/dist/core/sitemap.js +177 -43
- package/dist/core/sitemap.js.map +1 -1
- package/dist/core/static.d.ts +6 -0
- package/dist/core/static.js +122 -12
- package/dist/core/static.js.map +1 -1
- package/dist/core/types.d.ts +268 -2
- package/dist/core/types.js.map +1 -1
- package/dist/core/url.d.ts +2 -0
- package/dist/core/url.js +8 -0
- package/dist/core/url.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +129 -3
- package/dist/mcp.js.map +1 -1
- package/package.json +6 -2
- package/skills/seo-audit/SKILL.md +9 -2
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { hostOf, resolveUrl } from "../url.js";
|
|
2
|
+
import { isLocalHost } from "./canonical.js";
|
|
3
|
+
import { cap, defineRule, GOOGLE_DOCS } from "./rule.js";
|
|
4
|
+
/** Query parameters that only tell analytics where a click came from. */
|
|
5
|
+
const TRACKING_PARAMETERS = new Set([
|
|
6
|
+
"gclid",
|
|
7
|
+
"gbraid",
|
|
8
|
+
"wbraid",
|
|
9
|
+
"dclid",
|
|
10
|
+
"fbclid",
|
|
11
|
+
"msclkid",
|
|
12
|
+
"yclid",
|
|
13
|
+
"twclid",
|
|
14
|
+
"ttclid",
|
|
15
|
+
"igshid",
|
|
16
|
+
"li_fat_id",
|
|
17
|
+
"mc_cid",
|
|
18
|
+
"mc_eid",
|
|
19
|
+
"_ga",
|
|
20
|
+
"_gl",
|
|
21
|
+
"_hsenc",
|
|
22
|
+
"_hsmi",
|
|
23
|
+
]);
|
|
24
|
+
const isTrackingParameter = (name) => {
|
|
25
|
+
const key = name.toLowerCase();
|
|
26
|
+
return key.startsWith("utm_") || TRACKING_PARAMETERS.has(key);
|
|
27
|
+
};
|
|
28
|
+
/** Parameter names servers use for session ids, and the shortest value that looks like one. */
|
|
29
|
+
const SESSION_PARAMETERS = new Map([
|
|
30
|
+
["jsessionid", 8],
|
|
31
|
+
["phpsessid", 8],
|
|
32
|
+
["sessionid", 8],
|
|
33
|
+
["session_id", 8],
|
|
34
|
+
["sessid", 8],
|
|
35
|
+
["cfid", 4],
|
|
36
|
+
["cftoken", 8],
|
|
37
|
+
["zenid", 8],
|
|
38
|
+
["oscsid", 8],
|
|
39
|
+
// "sid" also stands for a site or store id; only a long token is a session.
|
|
40
|
+
["sid", 16],
|
|
41
|
+
]);
|
|
42
|
+
/** A session id in the URL's query or as a `;jsessionid=` path parameter, or null. */
|
|
43
|
+
const sessionParameter = (url) => {
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = new URL(url);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (/;jsessionid=/iu.test(parsed.pathname)) {
|
|
52
|
+
return "jsessionid";
|
|
53
|
+
}
|
|
54
|
+
for (const [name, value] of parsed.searchParams) {
|
|
55
|
+
const key = name.toLowerCase();
|
|
56
|
+
const minLength = key.startsWith("aspsessionid")
|
|
57
|
+
? 8
|
|
58
|
+
: SESSION_PARAMETERS.get(key);
|
|
59
|
+
if (minLength !== undefined && value.length >= minLength) {
|
|
60
|
+
return name;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
65
|
+
/** The page URL's path, or null when the URL is not one a visitor can request. */
|
|
66
|
+
const pagePath = (page, source) => {
|
|
67
|
+
if (source === "html") {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
return new URL(page.url).pathname;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* A locale segment such as en-US, en-us, pt_BR, es-419, zh-Hans or zh-Hant-TW, whose case and
|
|
79
|
+
* underscore are conventions: a lowercase language, then an optional Title-case script and an
|
|
80
|
+
* optional two-letter or three-digit region. "Our-Team" and "how_to_cook" do not match.
|
|
81
|
+
*/
|
|
82
|
+
const LOCALE_SEGMENT = /^[a-z]{2,3}(?:[-_][A-Z][a-z]{3})?(?:[-_](?:[A-Za-z]{2}|\d{3}))?$/u;
|
|
83
|
+
/** The path without locale segments, which are spelled the way the locale is. */
|
|
84
|
+
const pathWithoutLocales = (path) => path
|
|
85
|
+
.split("/")
|
|
86
|
+
.filter((segment) => !LOCALE_SEGMENT.test(segment))
|
|
87
|
+
.join("/");
|
|
88
|
+
const KEEP_EXISTING_URLS = "Do not rename URLs that are already indexed or linked to only for this: a rename needs a 301 redirect from every old URL and resets what search engines know about the page. Apply it to new URLs.";
|
|
89
|
+
/** Private networks: an address there resolves only inside one office or one machine. */
|
|
90
|
+
const isPrivateAddress = (host) => /^(?:10|127)\.\d{1,3}\.\d{1,3}\.\d{1,3}$/u.test(host) ||
|
|
91
|
+
/^192\.168\.\d{1,3}\.\d{1,3}$/u.test(host) ||
|
|
92
|
+
/^172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}$/u.test(host);
|
|
93
|
+
const isDevHost = (url) => {
|
|
94
|
+
const host = hostOf(url);
|
|
95
|
+
return host !== null && (isLocalHost(url) || isPrivateAddress(host));
|
|
96
|
+
};
|
|
97
|
+
/** Every URL the page links to or loads from, once, with the first place it is written. */
|
|
98
|
+
const urlsOnPage = (page) => {
|
|
99
|
+
const out = new Map();
|
|
100
|
+
const add = (raw, where) => {
|
|
101
|
+
const url = raw ? resolveUrl(raw, page.url) : null;
|
|
102
|
+
if (url && !out.has(url)) {
|
|
103
|
+
out.set(url, where);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
add(page.canonical, '<link rel="canonical">');
|
|
107
|
+
for (const h of page.hreflang) {
|
|
108
|
+
add(h.url, `<link rel="alternate" hreflang="${h.lang}">`);
|
|
109
|
+
}
|
|
110
|
+
add(page.og["url"], "og:url");
|
|
111
|
+
add(page.og["image"], "og:image");
|
|
112
|
+
for (const link of page.links) {
|
|
113
|
+
add(link.url, "<a href>");
|
|
114
|
+
}
|
|
115
|
+
for (const image of page.images) {
|
|
116
|
+
add(image.src, "<img src>");
|
|
117
|
+
}
|
|
118
|
+
for (const resource of page.resources) {
|
|
119
|
+
add(resource.url, resource.kind);
|
|
120
|
+
}
|
|
121
|
+
return [...out].map(([url, where]) => ({ url, where }));
|
|
122
|
+
};
|
|
123
|
+
export const urlRules = [
|
|
124
|
+
defineRule({
|
|
125
|
+
id: "url.uppercase",
|
|
126
|
+
category: "links",
|
|
127
|
+
severity: "info",
|
|
128
|
+
description: "The page's URL path has uppercase letters. Google treats /Page and /page as two URLs, so links that differ in case split signals or end in a 404.",
|
|
129
|
+
recommendation: `Use lowercase paths for new pages, and redirect (301) other spellings of the path to the one you keep. ${KEEP_EXISTING_URLS}`,
|
|
130
|
+
docs: GOOGLE_DOCS.urlStructure,
|
|
131
|
+
}, ({ page, source }, make) => {
|
|
132
|
+
const path = pagePath(page, source);
|
|
133
|
+
// Percent-escapes such as %C3%A9 are uppercase by convention, not part of the path's spelling.
|
|
134
|
+
return path &&
|
|
135
|
+
/[A-Z]/u.test(pathWithoutLocales(path).replaceAll(/%[\dA-F]{2}/giu, ""))
|
|
136
|
+
? make(`The URL path ${path} has uppercase letters.`, {
|
|
137
|
+
evidence: { path },
|
|
138
|
+
})
|
|
139
|
+
: null;
|
|
140
|
+
}),
|
|
141
|
+
defineRule({
|
|
142
|
+
id: "url.underscores",
|
|
143
|
+
category: "links",
|
|
144
|
+
severity: "info",
|
|
145
|
+
description: "The page's URL path separates words with underscores; Google recommends hyphens.",
|
|
146
|
+
recommendation: `Separate words in new URLs with hyphens (/blue-widgets, not /blue_widgets). ${KEEP_EXISTING_URLS}`,
|
|
147
|
+
docs: GOOGLE_DOCS.urlStructure,
|
|
148
|
+
}, ({ page, source }, make) => {
|
|
149
|
+
const path = pagePath(page, source);
|
|
150
|
+
// Underscores in a word (/blue_widgets), not as a leading marker (/_next/, /_assets/).
|
|
151
|
+
return path && /[\p{L}\p{N}]_[\p{L}\p{N}]/u.test(pathWithoutLocales(path))
|
|
152
|
+
? make(`The URL path ${path} uses underscores between words.`, {
|
|
153
|
+
evidence: { path },
|
|
154
|
+
})
|
|
155
|
+
: null;
|
|
156
|
+
}),
|
|
157
|
+
defineRule({
|
|
158
|
+
id: "url.session-id",
|
|
159
|
+
category: "links",
|
|
160
|
+
severity: "warning",
|
|
161
|
+
description: "The page's URL or its internal links carry a session id (jsessionid, PHPSESSID, sid), so every visit creates new URLs for the same content.",
|
|
162
|
+
recommendation: "Keep sessions in a cookie, not in the URL. In Java servlet containers, set the session tracking mode to COOKIE; in PHP, set session.use_trans_sid=0 and session.use_only_cookies=1.",
|
|
163
|
+
docs: GOOGLE_DOCS.urlStructure,
|
|
164
|
+
}, ({ page, source }, make) => {
|
|
165
|
+
const own = pagePath(page, source) ? sessionParameter(page.url) : null;
|
|
166
|
+
const links = page.links.filter((l) => l.internal && l.url && sessionParameter(l.url));
|
|
167
|
+
if (!own && links.length === 0) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const parameter = own ?? (links[0]?.url ? sessionParameter(links[0].url) : null);
|
|
171
|
+
return make(own
|
|
172
|
+
? `The page URL carries a session id (${own}).`
|
|
173
|
+
: `${links.length} internal link(s) carry a session id (${parameter}).`, {
|
|
174
|
+
evidence: {
|
|
175
|
+
parameter,
|
|
176
|
+
pageUrl: own ? page.url : null,
|
|
177
|
+
links: links.length,
|
|
178
|
+
examples: cap(links.map((l) => l.url)),
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}),
|
|
182
|
+
defineRule({
|
|
183
|
+
id: "links.tracking-parameters",
|
|
184
|
+
category: "links",
|
|
185
|
+
severity: "info",
|
|
186
|
+
description: "Internal links carry campaign tracking parameters (utm_*, gclid, fbclid), which make crawlers fetch duplicate URLs and overwrite the visit's real traffic source in analytics.",
|
|
187
|
+
recommendation: "Link to the clean URL. Campaign parameters belong on links from emails, ads and other sites, not on links between your own pages; to measure which banner or menu item was clicked, use an analytics event instead.",
|
|
188
|
+
docs: GOOGLE_DOCS.urlStructure,
|
|
189
|
+
}, ({ page }, make) => {
|
|
190
|
+
const tagged = page.links.filter((l) => {
|
|
191
|
+
if (!l.internal || !l.url) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
return [...new URL(l.url).searchParams.keys()].some(isTrackingParameter);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return tagged.length > 0
|
|
202
|
+
? make(`${tagged.length} internal link(s) carry tracking parameters.`, {
|
|
203
|
+
evidence: {
|
|
204
|
+
count: tagged.length,
|
|
205
|
+
examples: cap(tagged.map((l) => l.url)),
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
: null;
|
|
209
|
+
}),
|
|
210
|
+
defineRule({
|
|
211
|
+
id: "links.local-host",
|
|
212
|
+
category: "links",
|
|
213
|
+
severity: "error",
|
|
214
|
+
description: "A public page's canonical, og:url, og:image, hreflang, images or scripts point to localhost, a .local or .test host, or a private network address, which work only on the developer's machine (error). Only <a> links there is info: a setup guide may mean them.",
|
|
215
|
+
recommendation: "Build these URLs from the production site URL (an environment variable such as SITE_URL) instead of the development server's, or make them relative. Check the canonical, og:url and og:image first: a canonical on localhost points search engines at a page they cannot reach.",
|
|
216
|
+
}, ({ page, source }, make) => {
|
|
217
|
+
if (source === "html" || isDevHost(page.url)) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const local = urlsOnPage(page).filter((u) => isDevHost(u.url));
|
|
221
|
+
if (local.length === 0) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const places = [...new Set(local.map((u) => u.where))];
|
|
225
|
+
// A tutorial may link to http://localhost:3000 on purpose, for the reader's own machine.
|
|
226
|
+
const onlyAnchors = places.every((w) => w === "<a href>");
|
|
227
|
+
return make(`${local.length} URL(s) on the page point to a development host (${cap(places, 4).join(", ")}).`, {
|
|
228
|
+
severity: onlyAnchors ? "info" : "error",
|
|
229
|
+
recommendation: onlyAnchors
|
|
230
|
+
? "If these links are meant for the reader's own machine (a setup guide), leave them. Otherwise build them from the production site URL or make them relative."
|
|
231
|
+
: undefined,
|
|
232
|
+
evidence: {
|
|
233
|
+
count: local.length,
|
|
234
|
+
examples: cap(local.map((u) => `${u.where}: ${u.url}`)),
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
}),
|
|
238
|
+
defineRule({
|
|
239
|
+
id: "links.missing-fragment",
|
|
240
|
+
category: "links",
|
|
241
|
+
severity: "info",
|
|
242
|
+
description: 'Same-page links (href="#name") point to an id that no element on the page has, so the link goes nowhere.',
|
|
243
|
+
recommendation: 'Give the target element the id the link names, or fix the link. If JavaScript handles the click (a menu or modal toggle), use a <button> instead of <a href="#...">.',
|
|
244
|
+
}, ({ page }, make) => page.missingFragments.length > 0
|
|
245
|
+
? make(`${page.missingFragments.length} same-page link(s) point to ids that are not on the page: ${cap(page.missingFragments, 5).join(", ")}.`, {
|
|
246
|
+
selector: cap(page.missingFragments, 5)
|
|
247
|
+
.map((href) => `a[href="${href.replaceAll('"', '\\"')}"]`)
|
|
248
|
+
.join(", "),
|
|
249
|
+
evidence: { fragments: cap(page.missingFragments) },
|
|
250
|
+
})
|
|
251
|
+
: null),
|
|
252
|
+
defineRule({
|
|
253
|
+
id: "https.mixed-content",
|
|
254
|
+
category: "document",
|
|
255
|
+
severity: "warning",
|
|
256
|
+
description: "An https page loads resources over http. Browsers block scripts, stylesheets, frames and responsive images (warning) and upgrade plain images, audio and video to https, which fails when the https URL does not exist (info). With a Content-Security-Policy of upgrade-insecure-requests, every resource is upgraded (info).",
|
|
257
|
+
recommendation: "Load every resource over https: change http:// URLs to https:// (or make them relative), and host or replace any resource that has no https version. A Content-Security-Policy of upgrade-insecure-requests upgrades the rest.",
|
|
258
|
+
docs: "https://web.dev/articles/what-is-mixed-content",
|
|
259
|
+
}, ({ page, source, response }, make) => {
|
|
260
|
+
if (source !== "url" || !page.url.startsWith("https://")) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const insecure = page.resources.filter((r) => r.url.startsWith("http://"));
|
|
264
|
+
// upgrade-insecure-requests (meta tag or header) has the browser request all of them over https.
|
|
265
|
+
const upgradesAll = page.upgradesInsecureRequests ||
|
|
266
|
+
// Several CSP headers arrive joined with ", ", and a comma separates policies.
|
|
267
|
+
/(?:^|[;,])\s*upgrade-insecure-requests\s*(?:[;,]|$)/iu.test(response?.headers["content-security-policy"] ?? "");
|
|
268
|
+
const isUpgraded = (r) => upgradesAll || r.kind === "image" || r.kind === "media";
|
|
269
|
+
const blocked = insecure.filter((r) => !isUpgraded(r));
|
|
270
|
+
const upgraded = insecure.filter(isUpgraded);
|
|
271
|
+
const examples = (list) => cap(list.map((r) => `${r.kind}: ${r.url}`));
|
|
272
|
+
return [
|
|
273
|
+
blocked.length > 0
|
|
274
|
+
? make(`Browsers block ${blocked.length} resource(s) the page loads over http (${[...new Set(blocked.map((r) => r.kind))].join(", ")}).`, {
|
|
275
|
+
evidence: {
|
|
276
|
+
count: blocked.length,
|
|
277
|
+
examples: examples(blocked),
|
|
278
|
+
},
|
|
279
|
+
})
|
|
280
|
+
: null,
|
|
281
|
+
upgraded.length > 0
|
|
282
|
+
? make(`${upgraded.length} resource URL(s) use http; browsers upgrade them to https, which fails where the https URL does not exist.`, {
|
|
283
|
+
severity: "info",
|
|
284
|
+
evidence: {
|
|
285
|
+
count: upgraded.length,
|
|
286
|
+
examples: examples(upgraded),
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
: null,
|
|
290
|
+
].filter((f) => f !== null);
|
|
291
|
+
}),
|
|
292
|
+
];
|
|
293
|
+
//# sourceMappingURL=urls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../src/core/rules/urls.ts"],"names":[],"mappings":"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"}
|
package/dist/core/score.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import type { Finding,
|
|
1
|
+
import type { Finding, SeverityCounts } from "./types.js";
|
|
2
2
|
export declare const pageScore: (counts: SeverityCounts) => number;
|
|
3
|
-
export declare const siteScore: (pages:
|
|
3
|
+
export declare const siteScore: (pages: {
|
|
4
|
+
score: number;
|
|
5
|
+
}[], siteFindings: Finding[]) => number;
|
package/dist/core/score.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/core/score.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAW,CAAC;AACjE,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAW,CAAC;AAEnE,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAsB,EAAU,EAAE;IAC1D,MAAM,SAAS,GACb,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC,KAAK;QACjC,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,OAAO;QACrC,MAAM,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;IAElC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/core/score.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAW,CAAC;AACjE,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAW,CAAC;AAEnE,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAsB,EAAU,EAAE;IAC1D,MAAM,SAAS,GACb,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC,KAAK;QACjC,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,OAAO;QACrC,MAAM,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;IAElC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,KAA0B,EAC1B,YAAuB,EACf,EAAE;IACV,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM;QACtB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM;QAC3D,CAAC,CAAC,GAAG,CAAC;IAER,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CACnC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,EAC1C,CAAC,CACF,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FetchOptions } from "./fetch.js";
|
|
2
|
+
import type { LinkCheck, PageRecord, UrlVariantProbe } from "./site-rules.js";
|
|
3
|
+
export interface ProbeOptions {
|
|
4
|
+
fetch: FetchOptions;
|
|
5
|
+
concurrency: number;
|
|
6
|
+
delayMs: number;
|
|
7
|
+
/** robots.txt lets the crawler fetch the URL. */
|
|
8
|
+
allows: (url: string) => boolean;
|
|
9
|
+
/** The crawl's start URL: images on its site are checked, and only its origin gets `fetch.headers`. */
|
|
10
|
+
startUrl: string;
|
|
11
|
+
includeSubdomains: boolean;
|
|
12
|
+
/** Also check images on other hosts, as `checkExternalLinks` does for links. */
|
|
13
|
+
checkExternal: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Pages with fewer words in their HTML (an app shell) give no text to compare. */
|
|
16
|
+
export declare const MIN_COMPARABLE_WORDS = 50;
|
|
17
|
+
export interface ImageCheckResult {
|
|
18
|
+
imageChecks: Map<string, LinkCheck>;
|
|
19
|
+
/** For every image URL, the pages that show it. */
|
|
20
|
+
imageSources: Map<string, Set<string>>;
|
|
21
|
+
}
|
|
22
|
+
/** Image URLs the crawled pages show, with the pages showing each, in crawl order. */
|
|
23
|
+
export declare const imageSourcesOf: (records: PageRecord[]) => Map<string, Set<string>>;
|
|
24
|
+
/** Request up to `limit` image URLs, the ones on most pages first, without reading their bodies. */
|
|
25
|
+
export declare const checkImages: (records: PageRecord[], limit: number, options: ProbeOptions) => Promise<ImageCheckResult>;
|
|
26
|
+
/** Shared shingles from which two pages count as the same text, as in site.duplicate-content. */
|
|
27
|
+
export declare const SAME_TEXT = 0.9;
|
|
28
|
+
/**
|
|
29
|
+
* Request a trailing-slash and an uppercase variant of a few crawled pages. A variant that answers
|
|
30
|
+
* 200 itself, without a redirect, and names no canonical that points back is a second URL for the
|
|
31
|
+
* same page. Local dev servers often answer both forms where production does not, so they are left out.
|
|
32
|
+
*/
|
|
33
|
+
export declare const probeUrlVariants: (records: PageRecord[], startUrl: string, options: ProbeOptions) => Promise<UrlVariantProbe[]>;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { mapConcurrent, sleep } from "./concurrency.js";
|
|
2
|
+
import { fetchPage, isHtmlResponse, probeUrl } from "./fetch.js";
|
|
3
|
+
import { extractPage } from "./html/extract.js";
|
|
4
|
+
import { jaccard, shingles } from "./html/text.js";
|
|
5
|
+
import { isLocalHost } from "./rules/canonical.js";
|
|
6
|
+
import { robotsDirectives } from "./rules/indexing.js";
|
|
7
|
+
import { hostOf, isSameSite, looksLikeHtml, normalizeUrl } from "./url.js";
|
|
8
|
+
/** Pages with fewer words in their HTML (an app shell) give no text to compare. */
|
|
9
|
+
export const MIN_COMPARABLE_WORDS = 50;
|
|
10
|
+
/** Image URLs the crawled pages show, with the pages showing each, in crawl order. */
|
|
11
|
+
export const imageSourcesOf = (records) => {
|
|
12
|
+
const sources = new Map();
|
|
13
|
+
// The resources were resolved against the page's <base href>, as the browser resolves them.
|
|
14
|
+
for (const record of records) {
|
|
15
|
+
for (const resource of record.page.resources) {
|
|
16
|
+
if (resource.kind !== "image" && resource.kind !== "responsive-image") {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const set = sources.get(resource.url) ?? new Set();
|
|
20
|
+
set.add(record.url);
|
|
21
|
+
sources.set(resource.url, set);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return sources;
|
|
25
|
+
};
|
|
26
|
+
/** Request up to `limit` image URLs, the ones on most pages first, without reading their bodies. */
|
|
27
|
+
export const checkImages = async (records, limit, options) => {
|
|
28
|
+
const imageSources = imageSourcesOf(records);
|
|
29
|
+
const imageChecks = new Map();
|
|
30
|
+
const startHost = hostOf(options.startUrl) ?? "";
|
|
31
|
+
const startOrigin = new URL(options.startUrl).origin;
|
|
32
|
+
// A page can point an <img> anywhere; the crawl requests other hosts only when asked to.
|
|
33
|
+
const targets = [...imageSources]
|
|
34
|
+
.toSorted((a, b) => b[1].size - a[1].size)
|
|
35
|
+
.flatMap(([url]) => options.allows(url) &&
|
|
36
|
+
(options.checkExternal ||
|
|
37
|
+
isSameSite(url, startHost, options.includeSubdomains))
|
|
38
|
+
? [url]
|
|
39
|
+
: [])
|
|
40
|
+
.slice(0, limit);
|
|
41
|
+
await mapConcurrent(targets, options.concurrency, async (url) => {
|
|
42
|
+
// The user's headers (a staging password, a cookie) go to the audited origin only.
|
|
43
|
+
const { headers, ...rest } = options.fetch;
|
|
44
|
+
const fetchOptions = {
|
|
45
|
+
...rest,
|
|
46
|
+
timeoutMs: Math.min(options.fetch.timeoutMs ?? 15_000, 10_000),
|
|
47
|
+
};
|
|
48
|
+
if (headers && new URL(url).origin === startOrigin) {
|
|
49
|
+
fetchOptions.headers = headers;
|
|
50
|
+
}
|
|
51
|
+
const res = await probeUrl(url, fetchOptions);
|
|
52
|
+
imageChecks.set(url, {
|
|
53
|
+
url,
|
|
54
|
+
status: res.status,
|
|
55
|
+
finalUrl: res.finalUrl,
|
|
56
|
+
redirectChain: res.redirectChain,
|
|
57
|
+
error: res.error,
|
|
58
|
+
});
|
|
59
|
+
if (options.delayMs) {
|
|
60
|
+
await sleep(options.delayMs);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return { imageChecks, imageSources };
|
|
64
|
+
};
|
|
65
|
+
/** Shared shingles from which two pages count as the same text, as in site.duplicate-content. */
|
|
66
|
+
export const SAME_TEXT = 0.9;
|
|
67
|
+
/** Pages whose slash and case variants are requested; a few tell how the server treats them all. */
|
|
68
|
+
const VARIANT_SAMPLE = 3;
|
|
69
|
+
/** The same URL with the trailing slash added or removed, and with the path in uppercase. */
|
|
70
|
+
const variantsOf = (url) => {
|
|
71
|
+
const parsed = new URL(url);
|
|
72
|
+
const { pathname } = parsed;
|
|
73
|
+
const out = [];
|
|
74
|
+
const slash = new URL(parsed.href);
|
|
75
|
+
slash.pathname = pathname.endsWith("/")
|
|
76
|
+
? pathname.slice(0, -1)
|
|
77
|
+
: `${pathname}/`;
|
|
78
|
+
out.push({ kind: "trailing-slash", variant: slash.href });
|
|
79
|
+
// Percent-escapes keep their case; only letters in the path change.
|
|
80
|
+
const upper = pathname.replaceAll(/%[\dA-F]{2}|[a-z]+/giu, (part) => part.startsWith("%") ? part : part.toUpperCase());
|
|
81
|
+
if (upper !== pathname) {
|
|
82
|
+
const cased = new URL(parsed.href);
|
|
83
|
+
cased.pathname = upper;
|
|
84
|
+
out.push({ kind: "case", variant: cased.href });
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
};
|
|
88
|
+
const isIndexablePage = (record) => record.response?.status === 200 &&
|
|
89
|
+
!robotsDirectives(record.page, record.response).noindex;
|
|
90
|
+
/**
|
|
91
|
+
* Request a trailing-slash and an uppercase variant of a few crawled pages. A variant that answers
|
|
92
|
+
* 200 itself, without a redirect, and names no canonical that points back is a second URL for the
|
|
93
|
+
* same page. Local dev servers often answer both forms where production does not, so they are left out.
|
|
94
|
+
*/
|
|
95
|
+
export const probeUrlVariants = async (records, startUrl, options) => {
|
|
96
|
+
if (isLocalHost(startUrl)) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const sample = records
|
|
100
|
+
.filter((r) => {
|
|
101
|
+
const { pathname } = new URL(r.url);
|
|
102
|
+
return (pathname !== "/" &&
|
|
103
|
+
r.page.wordCount >= MIN_COMPARABLE_WORDS &&
|
|
104
|
+
isIndexablePage(r) &&
|
|
105
|
+
looksLikeHtml(r.url) &&
|
|
106
|
+
!/\.[a-z\d]{2,5}$/iu.test(pathname));
|
|
107
|
+
})
|
|
108
|
+
.slice(0, VARIANT_SAMPLE);
|
|
109
|
+
const requests = sample.flatMap((record) => variantsOf(record.url)
|
|
110
|
+
.filter(({ variant }) => options.allows(variant))
|
|
111
|
+
.map((v) => ({ record, ...v })));
|
|
112
|
+
const probes = [];
|
|
113
|
+
await mapConcurrent(requests, options.concurrency, async ({ record, kind, variant }) => {
|
|
114
|
+
const res = await fetchPage(variant, options.fetch);
|
|
115
|
+
const answered = res.status === 200 &&
|
|
116
|
+
res.redirectChain.length === 0 &&
|
|
117
|
+
res.body !== null &&
|
|
118
|
+
isHtmlResponse(res);
|
|
119
|
+
const variantPage = answered
|
|
120
|
+
? extractPage(res.body ?? "", { url: variant })
|
|
121
|
+
: null;
|
|
122
|
+
const canonical = variantPage?.canonical ?? null;
|
|
123
|
+
// The page's own canonical, or the page itself, is where both URLs should consolidate.
|
|
124
|
+
const target = normalizeUrl(record.page.canonical ?? record.url);
|
|
125
|
+
const named = normalizeUrl(canonical ?? variant);
|
|
126
|
+
// An app shell or catch-all page answering every unknown path is a soft 404, not this page.
|
|
127
|
+
const samePage = variantPage !== null &&
|
|
128
|
+
jaccard(shingles(record.page.text), shingles(variantPage.text)) >=
|
|
129
|
+
SAME_TEXT;
|
|
130
|
+
probes.push({
|
|
131
|
+
kind,
|
|
132
|
+
url: record.url,
|
|
133
|
+
variant,
|
|
134
|
+
status: res.status,
|
|
135
|
+
canonical,
|
|
136
|
+
duplicate: samePage && named !== target,
|
|
137
|
+
});
|
|
138
|
+
if (options.delayMs) {
|
|
139
|
+
await sleep(options.delayMs);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return probes;
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=site-probes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-probes.js","sourceRoot":"","sources":["../../src/core/site-probes.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,18 @@ export interface LinkCheck {
|
|
|
20
20
|
}[];
|
|
21
21
|
error?: string;
|
|
22
22
|
}
|
|
23
|
+
/** A crawled page requested again with its trailing slash toggled, or with its path in uppercase. */
|
|
24
|
+
export interface UrlVariantProbe {
|
|
25
|
+
kind: "trailing-slash" | "case";
|
|
26
|
+
/** The crawled page. */
|
|
27
|
+
url: string;
|
|
28
|
+
variant: string;
|
|
29
|
+
status: number | null;
|
|
30
|
+
/** The canonical the variant names, when it answered with a page. */
|
|
31
|
+
canonical: string | null;
|
|
32
|
+
/** The variant answered 200 itself and names no canonical that points back to the page. */
|
|
33
|
+
duplicate: boolean;
|
|
34
|
+
}
|
|
23
35
|
export interface ProbeResults {
|
|
24
36
|
soft404?: {
|
|
25
37
|
url: string;
|
|
@@ -37,6 +49,7 @@ export interface ProbeResults {
|
|
|
37
49
|
finalUrl: string | null;
|
|
38
50
|
redirectsToMain: boolean;
|
|
39
51
|
} | null;
|
|
52
|
+
urlVariants?: UrlVariantProbe[];
|
|
40
53
|
}
|
|
41
54
|
export interface SiteContext {
|
|
42
55
|
startUrl: string;
|
|
@@ -47,6 +60,8 @@ export interface SiteContext {
|
|
|
47
60
|
/** For every internal target URL, the pages linking to it. */
|
|
48
61
|
linkSources: Map<string, Set<string>>;
|
|
49
62
|
robots: RobotsTxtInfo | null;
|
|
63
|
+
/** robots.txt up to 500 KiB, for checks per user agent (`robots.text` stops at 20,000 characters). */
|
|
64
|
+
robotsText?: string | null;
|
|
50
65
|
robotsAllows: ((url: string) => boolean) | null;
|
|
51
66
|
blockedByRobots: string[];
|
|
52
67
|
sitemaps: SitemapInfo;
|
|
@@ -60,5 +75,8 @@ export interface SiteContext {
|
|
|
60
75
|
externalSources?: Map<string, Set<string>>;
|
|
61
76
|
/** Directory audits: HTML files nobody links to. */
|
|
62
77
|
unlinkedPages?: string[];
|
|
78
|
+
/** Image URLs the pages show that were requested (or looked up as files), keyed by URL. */
|
|
79
|
+
imageChecks?: Map<string, LinkCheck>;
|
|
80
|
+
imageSources?: Map<string, Set<string>>;
|
|
63
81
|
}
|
|
64
82
|
export declare const runSiteRules: (ctx: SiteContext) => Finding[];
|