@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,381 @@
1
+ import { isJsonObject, isString, typesOf } from "../html/structured-data.js";
2
+ import { cap, defineRule, GOOGLE_DOCS } from "./rule.js";
3
+ const G = "https://developers.google.com/search/docs/appearance/structured-data/";
4
+ const ARTICLE = {
5
+ required: [],
6
+ recommended: ["headline", "image", "datePublished", "dateModified", "author"],
7
+ docs: `${G}article`,
8
+ };
9
+ // oxlint-disable-next-line anti-slop/no-known-value-widening -- the table is read with whatever @type a page names, so it needs an index type that lets a lookup miss
10
+ export const SCHEMA_SPECS = {
11
+ Article: ARTICLE,
12
+ NewsArticle: ARTICLE,
13
+ BlogPosting: ARTICLE,
14
+ TechArticle: ARTICLE,
15
+ Product: {
16
+ required: ["name"],
17
+ recommended: ["image", "description", "brand", "sku", "offers"],
18
+ oneOf: [["offers", "review", "aggregateRating"]],
19
+ docs: `${G}product`,
20
+ },
21
+ Offer: {
22
+ required: ["price", "priceCurrency"],
23
+ recommended: ["availability", "url", "priceValidUntil"],
24
+ docs: `${G}product`,
25
+ },
26
+ AggregateOffer: {
27
+ required: ["lowPrice", "priceCurrency"],
28
+ recommended: ["highPrice", "offerCount"],
29
+ docs: `${G}product`,
30
+ },
31
+ Organization: {
32
+ required: ["name"],
33
+ recommended: ["url", "logo", "sameAs"],
34
+ docs: `${G}organization`,
35
+ },
36
+ WebSite: {
37
+ required: ["name", "url"],
38
+ recommended: [],
39
+ docs: `${G}sitelinks-searchbox`,
40
+ },
41
+ WebPage: { required: [], recommended: ["name"] },
42
+ BreadcrumbList: {
43
+ required: ["itemListElement"],
44
+ recommended: [],
45
+ docs: `${G}breadcrumb`,
46
+ },
47
+ ListItem: {
48
+ required: ["position"],
49
+ recommended: ["name", "item"],
50
+ docs: `${G}breadcrumb`,
51
+ },
52
+ ItemList: {
53
+ required: ["itemListElement"],
54
+ recommended: [],
55
+ docs: `${G}carousel`,
56
+ },
57
+ FAQPage: { required: ["mainEntity"], recommended: [], docs: `${G}faqpage` },
58
+ Question: {
59
+ required: ["name", "acceptedAnswer"],
60
+ recommended: [],
61
+ docs: `${G}faqpage`,
62
+ },
63
+ Answer: { required: ["text"], recommended: [], docs: `${G}faqpage` },
64
+ LocalBusiness: {
65
+ required: ["name", "address"],
66
+ recommended: [
67
+ "telephone",
68
+ "url",
69
+ "image",
70
+ "openingHoursSpecification",
71
+ "priceRange",
72
+ "geo",
73
+ ],
74
+ docs: `${G}local-business`,
75
+ },
76
+ Event: {
77
+ required: ["name", "startDate", "location"],
78
+ recommended: [
79
+ "endDate",
80
+ "description",
81
+ "image",
82
+ "offers",
83
+ "performer",
84
+ "organizer",
85
+ "eventStatus",
86
+ "eventAttendanceMode",
87
+ ],
88
+ docs: `${G}event`,
89
+ },
90
+ Recipe: {
91
+ required: ["name", "image"],
92
+ recommended: [
93
+ "author",
94
+ "datePublished",
95
+ "description",
96
+ "prepTime",
97
+ "cookTime",
98
+ "totalTime",
99
+ "recipeYield",
100
+ "recipeIngredient",
101
+ "recipeInstructions",
102
+ "aggregateRating",
103
+ ],
104
+ docs: `${G}recipe`,
105
+ },
106
+ VideoObject: {
107
+ required: ["name", "thumbnailUrl", "uploadDate"],
108
+ recommended: ["description", "duration", "contentUrl", "embedUrl"],
109
+ docs: `${G}video`,
110
+ },
111
+ Person: { required: ["name"], recommended: ["url", "sameAs", "jobTitle"] },
112
+ JobPosting: {
113
+ required: [
114
+ "title",
115
+ "description",
116
+ "datePosted",
117
+ "hiringOrganization",
118
+ "jobLocation",
119
+ ],
120
+ recommended: ["validThrough", "employmentType", "baseSalary", "identifier"],
121
+ docs: `${G}job-posting`,
122
+ },
123
+ HowTo: {
124
+ required: ["name", "step"],
125
+ recommended: ["image", "totalTime", "estimatedCost", "supply", "tool"],
126
+ docs: `${G}how-to`,
127
+ },
128
+ Review: {
129
+ required: ["itemReviewed", "reviewRating", "author"],
130
+ recommended: ["datePublished", "reviewBody"],
131
+ docs: `${G}review-snippet`,
132
+ },
133
+ Rating: {
134
+ required: ["ratingValue"],
135
+ recommended: ["bestRating", "worstRating"],
136
+ docs: `${G}review-snippet`,
137
+ },
138
+ AggregateRating: {
139
+ required: ["ratingValue"],
140
+ recommended: ["bestRating"],
141
+ oneOf: [["ratingCount", "reviewCount"]],
142
+ docs: `${G}review-snippet`,
143
+ },
144
+ SoftwareApplication: {
145
+ required: ["name"],
146
+ recommended: [
147
+ "applicationCategory",
148
+ "operatingSystem",
149
+ "offers",
150
+ "aggregateRating",
151
+ ],
152
+ oneOf: [["offers", "aggregateRating"]],
153
+ docs: `${G}software-app`,
154
+ },
155
+ Course: {
156
+ required: ["name", "description", "provider"],
157
+ recommended: [],
158
+ docs: `${G}course`,
159
+ },
160
+ Dataset: {
161
+ required: ["name", "description"],
162
+ recommended: ["url", "creator", "license", "distribution"],
163
+ docs: `${G}dataset`,
164
+ },
165
+ ImageObject: {
166
+ required: [],
167
+ recommended: ["contentUrl", "license", "creator"],
168
+ oneOf: [["contentUrl", "url"]],
169
+ docs: `${G}image-license-metadata`,
170
+ },
171
+ PostalAddress: {
172
+ required: [],
173
+ recommended: [
174
+ "streetAddress",
175
+ "addressLocality",
176
+ "postalCode",
177
+ "addressCountry",
178
+ ],
179
+ },
180
+ };
181
+ const DATE_PROPS = new Set([
182
+ "datePublished",
183
+ "dateModified",
184
+ "startDate",
185
+ "endDate",
186
+ "uploadDate",
187
+ "datePosted",
188
+ "validThrough",
189
+ "dateCreated",
190
+ "expires",
191
+ ]);
192
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/u;
193
+ const isPresent = (value) => {
194
+ if (value === null || value === undefined) {
195
+ return false;
196
+ }
197
+ if (isString(value)) {
198
+ return value.trim().length > 0;
199
+ }
200
+ if (Array.isArray(value)) {
201
+ return value.length > 0;
202
+ }
203
+ return true;
204
+ };
205
+ /** The properties a typed object lacks: required ones (and oneOf groups it fills none of), then recommended ones. */
206
+ const missingProperties = (obj, spec) => {
207
+ const required = spec.required.filter((p) => !isPresent(obj[p]));
208
+ for (const group of spec.oneOf ?? []) {
209
+ if (!group.some((p) => isPresent(obj[p]))) {
210
+ required.push(`one of: ${group.join(" | ")}`);
211
+ }
212
+ }
213
+ const recommended = spec.recommended.filter((p) => !isPresent(obj[p]));
214
+ return { required, recommended };
215
+ };
216
+ /** Walk one structured-data item (and nested typed objects) collecting spec violations. */
217
+ export const validateItem = (item) => {
218
+ const required = [];
219
+ const recommended = [];
220
+ const badDates = [];
221
+ const unknownTypes = [];
222
+ const visited = new Set();
223
+ const walk = (node, path, depth) => {
224
+ if (depth > 6 ||
225
+ !(Array.isArray(node) || isJsonObject(node)) ||
226
+ visited.has(node)) {
227
+ return;
228
+ }
229
+ visited.add(node);
230
+ if (Array.isArray(node)) {
231
+ for (const [i, n] of node.entries()) {
232
+ walk(n, `${path}[${i}]`, depth);
233
+ }
234
+ return;
235
+ }
236
+ for (const type of typesOf(node["@type"])) {
237
+ const spec = SCHEMA_SPECS[type];
238
+ if (!spec) {
239
+ unknownTypes.push(type);
240
+ continue;
241
+ }
242
+ const missing = missingProperties(node, spec);
243
+ if (missing.required.length) {
244
+ required.push({ type, missing: missing.required, path });
245
+ }
246
+ if (missing.recommended.length) {
247
+ recommended.push({ type, missing: missing.recommended, path });
248
+ }
249
+ }
250
+ for (const [key, value] of Object.entries(node)) {
251
+ if (DATE_PROPS.has(key) &&
252
+ isString(value) &&
253
+ !ISO_DATE.test(value.trim())) {
254
+ badDates.push(`${path ? `${path}.` : ""}${key}="${value}"`);
255
+ }
256
+ if (Array.isArray(value) || isJsonObject(value)) {
257
+ walk(value, path ? `${path}.${key}` : key, depth + 1);
258
+ }
259
+ }
260
+ };
261
+ // SAFETY: extractStructuredData builds every item's data from JSON.parse or from microdata, and both hold JSON values alone.
262
+ walk(item.data, "", 0);
263
+ return { required, recommended, badDates, unknownTypes };
264
+ };
265
+ export const structuredDataRules = [
266
+ defineRule({
267
+ id: "structured-data.invalid-json",
268
+ category: "structured-data",
269
+ severity: "error",
270
+ description: "A JSON-LD <script> block contains invalid JSON and will be ignored entirely.",
271
+ recommendation: "Fix the JSON syntax (trailing commas, unescaped quotes, HTML comments inside the script). Serialise with JSON.stringify instead of hand-writing it.",
272
+ docs: GOOGLE_DOCS.structuredData,
273
+ }, ({ page }, make) => page.structuredDataErrors.map((e) => make(`Invalid JSON-LD: ${e.message}`, {
274
+ selector: 'script[type="application/ld+json"]',
275
+ evidence: { snippet: e.snippet },
276
+ }))),
277
+ defineRule({
278
+ id: "structured-data.missing",
279
+ category: "structured-data",
280
+ severity: "info",
281
+ description: "The page has no structured data (JSON-LD or microdata).",
282
+ recommendation: "Add JSON-LD for the page type (Article, Product, Organization, BreadcrumbList, FAQPage, ...) to qualify for rich results.",
283
+ docs: GOOGLE_DOCS.structuredData,
284
+ }, ({ page }, make) => page.structuredData.length === 0 && page.structuredDataErrors.length === 0
285
+ ? make("Page has no structured data.")
286
+ : null),
287
+ defineRule({
288
+ id: "structured-data.missing-context",
289
+ category: "structured-data",
290
+ severity: "warning",
291
+ description: "A JSON-LD item has no @context or a context other than schema.org.",
292
+ recommendation: 'Set "@context": "https://schema.org" on every top-level JSON-LD object.',
293
+ docs: GOOGLE_DOCS.structuredData,
294
+ }, ({ page }, make) => {
295
+ const bad = page.structuredData.filter((i) => i.source === "json-ld" &&
296
+ !(i.context ?? "").toLowerCase().includes("schema.org"));
297
+ return bad.length
298
+ ? make(`${bad.length} JSON-LD item(s) lack a schema.org @context.`, {
299
+ selector: 'script[type="application/ld+json"]',
300
+ evidence: {
301
+ contexts: cap(bad.map((i) => i.context)),
302
+ types: cap(bad.flatMap((i) => i.types)),
303
+ },
304
+ })
305
+ : null;
306
+ }),
307
+ defineRule({
308
+ id: "structured-data.missing-type",
309
+ category: "structured-data",
310
+ severity: "warning",
311
+ description: "A structured data item has no @type (or itemtype).",
312
+ recommendation: 'Add "@type" (e.g. "Article") to every structured data object.',
313
+ docs: GOOGLE_DOCS.structuredData,
314
+ }, ({ page }, make) => {
315
+ const bad = page.structuredData.filter((i) => i.types.length === 0);
316
+ return bad.length
317
+ ? make(`${bad.length} structured data item(s) have no @type.`, {
318
+ evidence: {
319
+ sources: cap(bad.map((i) => i.source)),
320
+ keys: cap(bad.map((i) => Object.keys(i.data).slice(0, 8))),
321
+ },
322
+ })
323
+ : null;
324
+ }),
325
+ defineRule({
326
+ id: "structured-data.missing-required",
327
+ category: "structured-data",
328
+ severity: "warning",
329
+ description: "A structured data item of a known type is missing properties Google requires for rich results.",
330
+ recommendation: "Add the missing properties listed in evidence. Validate with https://search.google.com/test/rich-results after fixing.",
331
+ docs: GOOGLE_DOCS.structuredData,
332
+ }, ({ page }, make) => page.structuredData.flatMap((item) => {
333
+ const { required } = validateItem(item);
334
+ return required.map((iss) => make(`${iss.type} is missing required: ${iss.missing.join(", ")}.`, {
335
+ selector: item.source === "json-ld"
336
+ ? 'script[type="application/ld+json"]'
337
+ : `[itemtype*="${iss.type}"]`,
338
+ evidence: {
339
+ type: iss.type,
340
+ missing: iss.missing,
341
+ path: iss.path || "(root)",
342
+ source: item.source,
343
+ docs: SCHEMA_SPECS[iss.type]?.docs,
344
+ },
345
+ }));
346
+ })),
347
+ defineRule({
348
+ id: "structured-data.missing-recommended",
349
+ category: "structured-data",
350
+ severity: "info",
351
+ description: "A structured data item is missing properties Google recommends for richer results.",
352
+ recommendation: "Add the recommended properties where the data exists; they unlock additional rich-result features.",
353
+ docs: GOOGLE_DOCS.structuredData,
354
+ }, ({ page }, make) => page.structuredData.flatMap((item) => {
355
+ const { recommended } = validateItem(item);
356
+ return recommended.map((iss) => make(`${iss.type} is missing recommended: ${iss.missing.join(", ")}.`, {
357
+ evidence: {
358
+ type: iss.type,
359
+ missing: iss.missing,
360
+ path: iss.path || "(root)",
361
+ source: item.source,
362
+ },
363
+ }));
364
+ })),
365
+ defineRule({
366
+ id: "structured-data.invalid-date",
367
+ category: "structured-data",
368
+ severity: "warning",
369
+ description: "Date properties in structured data that are not ISO 8601.",
370
+ recommendation: "Use ISO 8601 dates such as 2026-03-14 or 2026-03-14T09:30:00+01:00.",
371
+ docs: GOOGLE_DOCS.structuredData,
372
+ }, ({ page }, make) => {
373
+ const bad = page.structuredData.flatMap((i) => validateItem(i).badDates);
374
+ return bad.length
375
+ ? make(`${bad.length} structured data date(s) are not ISO 8601.`, {
376
+ evidence: { examples: cap(bad) },
377
+ })
378
+ : null;
379
+ }),
380
+ ];
381
+ //# sourceMappingURL=structured-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-data.js","sourceRoot":"","sources":["../../../src/core/rules/structured-data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const titleRules: import("./rule.js").Rule[];
@@ -0,0 +1,74 @@
1
+ import { isGenericTitle } from "../html/text.js";
2
+ import { defineRule, GOOGLE_DOCS } from "./rule.js";
3
+ export const titleRules = [
4
+ defineRule({
5
+ id: "title.missing",
6
+ category: "title",
7
+ severity: "error",
8
+ description: "The page has no <title> element or it is empty.",
9
+ recommendation: "Add a unique, descriptive <title> in <head> (about 30-60 characters) that states what the page is about.",
10
+ docs: GOOGLE_DOCS.titles,
11
+ }, ({ page }, make) => page.title
12
+ ? null
13
+ : make("Page has no <title> or the title is empty.", {
14
+ selector: "head > title",
15
+ })),
16
+ defineRule({
17
+ id: "title.multiple",
18
+ category: "title",
19
+ severity: "warning",
20
+ description: "More than one <title> element is present.",
21
+ recommendation: "Keep exactly one <title>. Browsers and crawlers may pick different ones when several exist.",
22
+ docs: GOOGLE_DOCS.titles,
23
+ }, ({ page }, make) => page.titles.length > 1
24
+ ? make(`Page has ${page.titles.length} <title> elements.`, {
25
+ selector: "title",
26
+ evidence: { titles: page.titles },
27
+ })
28
+ : null),
29
+ defineRule({
30
+ id: "title.too-long",
31
+ category: "title",
32
+ severity: "warning",
33
+ description: "Title is longer than the configured maximum (default 60 characters) and will be truncated in search results.",
34
+ recommendation: "Shorten the title to roughly 60 characters or fewer, keeping the most important words first.",
35
+ docs: GOOGLE_DOCS.titles,
36
+ }, ({ page, thresholds }, make) => page.title && page.title.length > thresholds.titleMaxChars
37
+ ? make(`Title is ${page.title.length} characters (max ${thresholds.titleMaxChars}).`, {
38
+ selector: "head > title",
39
+ evidence: {
40
+ title: page.title,
41
+ length: page.title.length,
42
+ max: thresholds.titleMaxChars,
43
+ },
44
+ })
45
+ : null),
46
+ defineRule({
47
+ id: "title.too-short",
48
+ category: "title",
49
+ severity: "info",
50
+ description: "Title is shorter than the configured minimum (default 30 characters).",
51
+ recommendation: "Consider a more descriptive title that includes the page topic and, where sensible, the brand.",
52
+ docs: GOOGLE_DOCS.titles,
53
+ }, ({ page, thresholds }, make) => page.title && page.title.length < thresholds.titleMinChars
54
+ ? make(`Title is only ${page.title.length} characters (recommended at least ${thresholds.titleMinChars}).`, {
55
+ selector: "head > title",
56
+ evidence: {
57
+ title: page.title,
58
+ length: page.title.length,
59
+ min: thresholds.titleMinChars,
60
+ },
61
+ })
62
+ : null),
63
+ defineRule({
64
+ id: "title.generic",
65
+ category: "title",
66
+ severity: "warning",
67
+ description: 'Title is a framework default or placeholder such as "React App", "Create Next App", "Home" or "Untitled".',
68
+ recommendation: "Replace the placeholder with a title that describes this page. In frameworks, set it per route (metadata export, <Head>, useHead, <svelte:head>, etc.).",
69
+ docs: GOOGLE_DOCS.titles,
70
+ }, ({ page }, make) => page.title && isGenericTitle(page.title)
71
+ ? make(`Title "${page.title}" looks like a placeholder or framework default.`, { selector: "head > title", evidence: { title: page.title } })
72
+ : null),
73
+ ];
74
+ //# sourceMappingURL=title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title.js","sourceRoot":"","sources":["../../../src/core/rules/title.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Finding, PageAudit, SeverityCounts } from "./types.js";
2
+ export declare const pageScore: (counts: SeverityCounts) => number;
3
+ export declare const siteScore: (pages: PageAudit[], siteFindings: Finding[]) => number;
@@ -0,0 +1,17 @@
1
+ const PAGE_WEIGHTS = { error: 15, warning: 5, info: 1 };
2
+ const SITE_WEIGHTS = { error: 10, warning: 3, info: 0.5 };
3
+ const clamp = (n) => Math.max(0, Math.min(100, n));
4
+ export const pageScore = (counts) => {
5
+ const deduction = counts.error * PAGE_WEIGHTS.error +
6
+ counts.warning * PAGE_WEIGHTS.warning +
7
+ counts.info * PAGE_WEIGHTS.info;
8
+ return clamp(Math.round(100 - deduction));
9
+ };
10
+ export const siteScore = (pages, siteFindings) => {
11
+ const avg = pages.length
12
+ ? pages.reduce((sum, p) => sum + p.score, 0) / pages.length
13
+ : 100;
14
+ const deduction = siteFindings.reduce((sum, f) => sum + SITE_WEIGHTS[f.severity], 0);
15
+ return clamp(Math.round(avg - deduction));
16
+ };
17
+ //# sourceMappingURL=score.js.map
@@ -0,0 +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,KAAkB,EAClB,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,64 @@
1
+ import type { ExtractedPage, Finding, ResponseInfo, RobotsTxtInfo, RuleMeta, SitemapInfo, Thresholds, CrawlStats } from "./types.js";
2
+ export declare const SITE_RULES: RuleMeta[];
3
+ export interface PageRecord {
4
+ /** Normalized final URL (or synthetic URL for files). */
5
+ url: string;
6
+ requestedUrl: string;
7
+ page: ExtractedPage;
8
+ response?: ResponseInfo;
9
+ depth: number;
10
+ /** File path for directory audits. */
11
+ file?: string;
12
+ }
13
+ export interface LinkCheck {
14
+ url: string;
15
+ status: number | null;
16
+ finalUrl: string;
17
+ redirectChain: {
18
+ url: string;
19
+ status: number;
20
+ }[];
21
+ error?: string;
22
+ }
23
+ export interface ProbeResults {
24
+ soft404?: {
25
+ url: string;
26
+ status: number | null;
27
+ } | null;
28
+ httpRedirect?: {
29
+ url: string;
30
+ status: number | null;
31
+ finalUrl: string | null;
32
+ redirectsToHttps: boolean;
33
+ } | null;
34
+ hostVariant?: {
35
+ url: string;
36
+ status: number | null;
37
+ finalUrl: string | null;
38
+ redirectsToMain: boolean;
39
+ } | null;
40
+ }
41
+ export interface SiteContext {
42
+ startUrl: string;
43
+ source: "crawl" | "directory";
44
+ pages: PageRecord[];
45
+ /** Status of every internal URL that was fetched or probed, keyed by normalized URL. */
46
+ linkChecks: Map<string, LinkCheck>;
47
+ /** For every internal target URL, the pages linking to it. */
48
+ linkSources: Map<string, Set<string>>;
49
+ robots: RobotsTxtInfo | null;
50
+ robotsAllows: ((url: string) => boolean) | null;
51
+ blockedByRobots: string[];
52
+ sitemaps: SitemapInfo;
53
+ probes: ProbeResults;
54
+ crawl: CrawlStats;
55
+ thresholds: Thresholds;
56
+ /** Compare URLs by path only (directory audits, where hosts are synthetic). */
57
+ pathOnly: boolean;
58
+ /** External link targets that were probed, keyed by URL. */
59
+ externalChecks?: Map<string, LinkCheck>;
60
+ externalSources?: Map<string, Set<string>>;
61
+ /** Directory audits: HTML files nobody links to. */
62
+ unlinkedPages?: string[];
63
+ }
64
+ export declare const runSiteRules: (ctx: SiteContext) => Finding[];