@rivi-software/agentic-seo-check 0.1.0 → 0.2.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.
Files changed (144) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +124 -18
  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.js +174 -86
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/aeo/audit.d.ts +44 -0
  9. package/dist/core/aeo/audit.js +310 -0
  10. package/dist/core/aeo/audit.js.map +1 -0
  11. package/dist/core/aeo/bots.d.ts +30 -0
  12. package/dist/core/aeo/bots.js +299 -0
  13. package/dist/core/aeo/bots.js.map +1 -0
  14. package/dist/core/aeo/llms-txt.d.ts +8 -0
  15. package/dist/core/aeo/llms-txt.js +88 -0
  16. package/dist/core/aeo/llms-txt.js.map +1 -0
  17. package/dist/core/aeo/page.d.ts +53 -0
  18. package/dist/core/aeo/page.js +312 -0
  19. package/dist/core/aeo/page.js.map +1 -0
  20. package/dist/core/aeo/probe.d.ts +10 -0
  21. package/dist/core/aeo/probe.js +53 -0
  22. package/dist/core/aeo/probe.js.map +1 -0
  23. package/dist/core/aeo/site-rules.d.ts +13 -0
  24. package/dist/core/aeo/site-rules.js +178 -0
  25. package/dist/core/aeo/site-rules.js.map +1 -0
  26. package/dist/core/audit.d.ts +37 -16
  27. package/dist/core/audit.js +389 -249
  28. package/dist/core/audit.js.map +1 -1
  29. package/dist/core/concurrency.d.ts +4 -4
  30. package/dist/core/concurrency.js +33 -23
  31. package/dist/core/concurrency.js.map +1 -1
  32. package/dist/core/crawl.d.ts +9 -5
  33. package/dist/core/crawl.js +271 -123
  34. package/dist/core/crawl.js.map +1 -1
  35. package/dist/core/fetch.d.ts +5 -5
  36. package/dist/core/fetch.js +151 -100
  37. package/dist/core/fetch.js.map +1 -1
  38. package/dist/core/html/answer.d.ts +4 -0
  39. package/dist/core/html/answer.js +198 -0
  40. package/dist/core/html/answer.js.map +1 -0
  41. package/dist/core/html/dom.d.ts +17 -0
  42. package/dist/core/html/dom.js +83 -0
  43. package/dist/core/html/dom.js.map +1 -0
  44. package/dist/core/html/extract.d.ts +2 -2
  45. package/dist/core/html/extract.js +102 -111
  46. package/dist/core/html/extract.js.map +1 -1
  47. package/dist/core/html/structured-data.d.ts +16 -6
  48. package/dist/core/html/structured-data.js +180 -145
  49. package/dist/core/html/structured-data.js.map +1 -1
  50. package/dist/core/html/text.d.ts +14 -14
  51. package/dist/core/html/text.js +110 -107
  52. package/dist/core/html/text.js.map +1 -1
  53. package/dist/core/lighthouse.d.ts +26 -25
  54. package/dist/core/lighthouse.js +181 -105
  55. package/dist/core/lighthouse.js.map +1 -1
  56. package/dist/core/performance-rules.d.ts +2 -2
  57. package/dist/core/performance-rules.js +105 -33
  58. package/dist/core/performance-rules.js.map +1 -1
  59. package/dist/core/render-rules.d.ts +5 -4
  60. package/dist/core/render-rules.js +129 -62
  61. package/dist/core/render-rules.js.map +1 -1
  62. package/dist/core/render.d.ts +2 -2
  63. package/dist/core/render.js +29 -23
  64. package/dist/core/render.js.map +1 -1
  65. package/dist/core/report/markdown.d.ts +4 -2
  66. package/dist/core/report/markdown.js +210 -129
  67. package/dist/core/report/markdown.js.map +1 -1
  68. package/dist/core/robots.d.ts +12 -5
  69. package/dist/core/robots.js +43 -17
  70. package/dist/core/robots.js.map +1 -1
  71. package/dist/core/rules/aeo.d.ts +11 -0
  72. package/dist/core/rules/aeo.js +423 -0
  73. package/dist/core/rules/aeo.js.map +1 -0
  74. package/dist/core/rules/canonical.d.ts +1 -1
  75. package/dist/core/rules/canonical.js +93 -57
  76. package/dist/core/rules/canonical.js.map +1 -1
  77. package/dist/core/rules/content.js +40 -26
  78. package/dist/core/rules/content.js.map +1 -1
  79. package/dist/core/rules/document.js +64 -41
  80. package/dist/core/rules/document.js.map +1 -1
  81. package/dist/core/rules/headings.js +58 -38
  82. package/dist/core/rules/headings.js.map +1 -1
  83. package/dist/core/rules/hreflang.js +70 -38
  84. package/dist/core/rules/hreflang.js.map +1 -1
  85. package/dist/core/rules/images.js +44 -26
  86. package/dist/core/rules/images.js.map +1 -1
  87. package/dist/core/rules/index.d.ts +10 -6
  88. package/dist/core/rules/index.js +43 -36
  89. package/dist/core/rules/index.js.map +1 -1
  90. package/dist/core/rules/indexing.d.ts +2 -2
  91. package/dist/core/rules/indexing.js +48 -26
  92. package/dist/core/rules/indexing.js.map +1 -1
  93. package/dist/core/rules/keyword.js +84 -47
  94. package/dist/core/rules/keyword.js.map +1 -1
  95. package/dist/core/rules/links.js +72 -37
  96. package/dist/core/rules/links.js.map +1 -1
  97. package/dist/core/rules/meta.js +55 -34
  98. package/dist/core/rules/meta.js.map +1 -1
  99. package/dist/core/rules/response.js +75 -41
  100. package/dist/core/rules/response.js.map +1 -1
  101. package/dist/core/rules/rule.d.ts +6 -6
  102. package/dist/core/rules/rule.js +36 -36
  103. package/dist/core/rules/rule.js.map +1 -1
  104. package/dist/core/rules/social.js +86 -45
  105. package/dist/core/rules/social.js.map +1 -1
  106. package/dist/core/rules/structured-data.d.ts +6 -4
  107. package/dist/core/rules/structured-data.js +314 -102
  108. package/dist/core/rules/structured-data.js.map +1 -1
  109. package/dist/core/rules/title.js +48 -33
  110. package/dist/core/rules/title.js.map +1 -1
  111. package/dist/core/score.d.ts +5 -3
  112. package/dist/core/score.js +11 -9
  113. package/dist/core/score.js.map +1 -1
  114. package/dist/core/site-rules.d.ts +3 -3
  115. package/dist/core/site-rules.js +409 -198
  116. package/dist/core/site-rules.js.map +1 -1
  117. package/dist/core/sitemap.d.ts +5 -5
  118. package/dist/core/sitemap.js +109 -64
  119. package/dist/core/sitemap.js.map +1 -1
  120. package/dist/core/static.d.ts +2 -2
  121. package/dist/core/static.js +210 -112
  122. package/dist/core/static.js.map +1 -1
  123. package/dist/core/summary.d.ts +2 -2
  124. package/dist/core/summary.js +11 -6
  125. package/dist/core/summary.js.map +1 -1
  126. package/dist/core/types.d.ts +189 -14
  127. package/dist/core/types.js +15 -8
  128. package/dist/core/types.js.map +1 -1
  129. package/dist/core/url.d.ts +11 -11
  130. package/dist/core/url.js +122 -70
  131. package/dist/core/url.js.map +1 -1
  132. package/dist/index.d.ts +15 -11
  133. package/dist/index.js +11 -9
  134. package/dist/index.js.map +1 -1
  135. package/dist/mcp-bin.js +7 -4
  136. package/dist/mcp-bin.js.map +1 -1
  137. package/dist/mcp.d.ts +3 -3
  138. package/dist/mcp.js +437 -121
  139. package/dist/mcp.js.map +1 -1
  140. package/dist/version.d.ts +0 -1
  141. package/dist/version.js +4 -2
  142. package/dist/version.js.map +1 -1
  143. package/package.json +32 -10
  144. package/skills/seo-audit/SKILL.md +8 -2
@@ -1,59 +1,93 @@
1
- import { isLocalHost } from './canonical.js';
2
- import { defineRule, GOOGLE_DOCS } from './rule.js';
1
+ import { isLocalHost } from "./canonical.js";
2
+ import { defineRule, GOOGLE_DOCS } from "./rule.js";
3
3
  export const responseRules = [
4
4
  defineRule({
5
- id: 'response.error-status',
6
- category: 'response',
7
- severity: 'error',
8
- description: 'The URL returned a 4xx or 5xx status code.',
9
- recommendation: 'Fix the route or server error. If the page was removed on purpose, return 410 (or 404) and remove internal links and sitemap entries pointing to it.',
10
- }, ({ response }, make) => (response?.status && response.status >= 400 ? make(`URL returned HTTP ${response.status}.`, { evidence: { status: response.status, finalUrl: response.finalUrl } }) : null)),
5
+ id: "response.error-status",
6
+ category: "response",
7
+ severity: "error",
8
+ description: "The URL returned a 4xx or 5xx status code.",
9
+ recommendation: "Fix the route or server error. If the page was removed on purpose, return 410 (or 404) and remove internal links and sitemap entries pointing to it.",
10
+ }, ({ response }, make) => response?.status && response.status >= 400
11
+ ? make(`URL returned HTTP ${response.status}.`, {
12
+ evidence: { status: response.status, finalUrl: response.finalUrl },
13
+ })
14
+ : null),
11
15
  defineRule({
12
- id: 'response.redirected',
13
- category: 'response',
14
- severity: 'info',
15
- description: 'The requested URL redirected to another URL.',
16
- recommendation: 'Link directly to the final URL (internal links, sitemap, canonical) so crawlers and users skip the redirect.',
16
+ id: "response.redirected",
17
+ category: "response",
18
+ severity: "info",
19
+ description: "The requested URL redirected to another URL.",
20
+ recommendation: "Link directly to the final URL (internal links, sitemap, canonical) so crawlers and users skip the redirect.",
17
21
  docs: GOOGLE_DOCS.redirects,
18
- }, ({ response }, make) => response && response.redirectChain.length === 1
19
- ? make(`Request redirected (${response.redirectChain[0].status}) to ${response.finalUrl}.`, { evidence: { chain: response.redirectChain, finalUrl: response.finalUrl } })
20
- : null),
22
+ }, ({ response }, make) => {
23
+ const [hop] = response?.redirectChain ?? [];
24
+ return response && hop && response.redirectChain.length === 1
25
+ ? make(`Request redirected (${hop.status}) to ${response.finalUrl}.`, {
26
+ evidence: {
27
+ chain: response.redirectChain,
28
+ finalUrl: response.finalUrl,
29
+ },
30
+ })
31
+ : null;
32
+ }),
21
33
  defineRule({
22
- id: 'response.redirect-chain',
23
- category: 'response',
24
- severity: 'warning',
25
- description: 'The URL goes through two or more redirects before reaching the final page.',
26
- recommendation: 'Collapse the chain so the first URL redirects straight to the final destination. Each hop costs crawl budget and loses some signal.',
34
+ id: "response.redirect-chain",
35
+ category: "response",
36
+ severity: "warning",
37
+ description: "The URL goes through two or more redirects before reaching the final page.",
38
+ recommendation: "Collapse the chain so the first URL redirects straight to the final destination. Each hop costs crawl budget and loses some signal.",
27
39
  docs: GOOGLE_DOCS.redirects,
28
40
  }, ({ response }, make) => response && response.redirectChain.length >= 2
29
- ? make(`Redirect chain of ${response.redirectChain.length} hops ends at ${response.finalUrl}.`, { evidence: { chain: response.redirectChain, finalUrl: response.finalUrl } })
41
+ ? make(`Redirect chain of ${response.redirectChain.length} hops ends at ${response.finalUrl}.`, {
42
+ evidence: {
43
+ chain: response.redirectChain,
44
+ finalUrl: response.finalUrl,
45
+ },
46
+ })
30
47
  : null),
31
48
  defineRule({
32
- id: 'response.slow',
33
- category: 'response',
34
- severity: 'warning',
35
- description: 'The HTML response took longer than the configured threshold (default 2000 ms).',
36
- recommendation: 'Reduce server response time: cache the HTML, avoid blocking data fetches during render, use a CDN. Google recommends TTFB under 800 ms.',
49
+ id: "response.slow",
50
+ category: "response",
51
+ severity: "warning",
52
+ description: "The HTML response took longer than the configured threshold (default 2000 ms).",
53
+ recommendation: "Reduce server response time: cache the HTML, avoid blocking data fetches during render, use a CDN. Google recommends TTFB under 800 ms.",
37
54
  docs: GOOGLE_DOCS.cwv,
38
- }, ({ response, thresholds }, make) => response?.timeMs !== null && response?.timeMs !== undefined && response.timeMs > thresholds.slowResponseMs
55
+ }, ({ response, thresholds }, make) => response?.timeMs !== null &&
56
+ response?.timeMs !== undefined &&
57
+ response.timeMs > thresholds.slowResponseMs
39
58
  ? make(`HTML response took ${response.timeMs} ms (single measurement; threshold ${thresholds.slowResponseMs} ms).`, { evidence: { timeMs: response.timeMs } })
40
59
  : null),
41
60
  defineRule({
42
- id: 'response.not-html',
43
- category: 'response',
44
- severity: 'warning',
45
- description: 'The response Content-Type is not HTML.',
46
- recommendation: 'Serve pages with Content-Type: text/html; charset=utf-8.',
61
+ id: "response.not-html",
62
+ category: "response",
63
+ severity: "warning",
64
+ description: "The response Content-Type is not HTML.",
65
+ recommendation: "Serve pages with Content-Type: text/html; charset=utf-8.",
47
66
  }, ({ response }, make) => {
48
- const ct = response?.contentType?.toLowerCase();
49
- return ct && !ct.includes('html') && !ct.includes('xhtml') ? make(`Content-Type is "${response.contentType}", not HTML.`, { evidence: { contentType: response.contentType } }) : null;
67
+ const contentType = response?.contentType;
68
+ if (!contentType) {
69
+ return null;
70
+ }
71
+ const ct = contentType.toLowerCase();
72
+ return !ct.includes("html") && !ct.includes("xhtml")
73
+ ? make(`Content-Type is "${contentType}", not HTML.`, {
74
+ evidence: { contentType },
75
+ })
76
+ : null;
50
77
  }),
51
78
  defineRule({
52
- id: 'response.not-https',
53
- category: 'response',
54
- severity: 'warning',
55
- description: 'The final URL is served over plain http.',
56
- recommendation: 'Serve the site over https and redirect http to https. HTTPS is a (light) ranking signal and required for many browser features.',
57
- }, ({ response, source }, make) => source === 'url' && response && response.finalUrl.startsWith('http://') && !isLocalHost(response.finalUrl) ? make('Page is served over http:// instead of https://.', { evidence: { finalUrl: response.finalUrl } }) : null),
79
+ id: "response.not-https",
80
+ category: "response",
81
+ severity: "warning",
82
+ description: "The final URL is served over plain http.",
83
+ recommendation: "Serve the site over https and redirect http to https. HTTPS is a (light) ranking signal and required for many browser features.",
84
+ }, ({ response, source }, make) => source === "url" &&
85
+ response &&
86
+ response.finalUrl.startsWith("http://") &&
87
+ !isLocalHost(response.finalUrl)
88
+ ? make("Page is served over http:// instead of https://.", {
89
+ evidence: { finalUrl: response.finalUrl },
90
+ })
91
+ : null),
58
92
  ];
59
93
  //# sourceMappingURL=response.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/core/rules/response.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/core/rules/response.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ExtractedPage, Finding, PageSource, ResponseInfo, RuleMeta, Severity, Thresholds } from '../types.js';
1
+ import type { ExtractedPage, Finding, PageSource, ResponseInfo, RuleMeta, Severity, Thresholds } from "../types.js";
2
2
  export interface PageContext {
3
3
  page: ExtractedPage;
4
4
  source: PageSource;
@@ -9,18 +9,18 @@ export interface PageContext {
9
9
  }
10
10
  export interface Rule {
11
11
  meta: RuleMeta;
12
- check(ctx: PageContext): Finding[];
12
+ check: (ctx: PageContext) => Finding[];
13
13
  }
14
14
  export interface FindingExtras {
15
15
  severity?: Severity;
16
16
  recommendation?: string;
17
17
  selector?: string;
18
- evidence?: Record<string, unknown>;
18
+ evidence?: Finding["evidence"];
19
19
  }
20
20
  /** Build a finding from rule metadata, overriding only what differs for this instance. */
21
- export declare function finding(meta: RuleMeta, message: string, extras?: FindingExtras): Finding;
22
- export declare function defineRule(meta: RuleMeta, check: (ctx: PageContext, make: (message: string, extras?: FindingExtras) => Finding) => Finding[] | Finding | null | undefined | void): Rule;
23
- export declare function cap<T>(items: T[], max?: number): T[];
21
+ export declare const finding: (meta: RuleMeta, message: string, extras?: FindingExtras) => Finding;
22
+ export declare const defineRule: (meta: RuleMeta, check: (ctx: PageContext, make: (message: string, extras?: FindingExtras) => Finding) => Finding[] | Finding | null | undefined) => Rule;
23
+ export declare const cap: <T>(items: T[], max?: number) => T[];
24
24
  export declare const GOOGLE_DOCS: {
25
25
  readonly titles: "https://developers.google.com/search/docs/appearance/title-link";
26
26
  readonly snippets: "https://developers.google.com/search/docs/appearance/snippet";
@@ -1,5 +1,5 @@
1
1
  /** Build a finding from rule metadata, overriding only what differs for this instance. */
2
- export function finding(meta, message, extras = {}) {
2
+ export const finding = (meta, message, extras = {}) => {
3
3
  const f = {
4
4
  rule: meta.id,
5
5
  severity: extras.severity ?? meta.severity,
@@ -7,45 +7,45 @@ export function finding(meta, message, extras = {}) {
7
7
  message,
8
8
  recommendation: extras.recommendation ?? meta.recommendation,
9
9
  };
10
- if (extras.selector)
10
+ if (extras.selector) {
11
11
  f.selector = extras.selector;
12
- if (extras.evidence)
12
+ }
13
+ if (extras.evidence) {
13
14
  f.evidence = extras.evidence;
14
- if (meta.docs)
15
+ }
16
+ if (meta.docs) {
15
17
  f.docs = meta.docs;
18
+ }
16
19
  return f;
17
- }
18
- export function defineRule(meta, check) {
19
- return {
20
- meta,
21
- check(ctx) {
22
- const result = check(ctx, (message, extras) => finding(meta, message, extras));
23
- if (!result)
24
- return [];
25
- return Array.isArray(result) ? result : [result];
26
- },
27
- };
28
- }
29
- export function cap(items, max = 10) {
30
- return items.length > max ? items.slice(0, max) : items;
31
- }
20
+ };
21
+ export const defineRule = (meta, check) => ({
22
+ meta,
23
+ check(ctx) {
24
+ const result = check(ctx, (message, extras) => finding(meta, message, extras));
25
+ if (!result) {
26
+ return [];
27
+ }
28
+ return Array.isArray(result) ? result : [result];
29
+ },
30
+ });
31
+ export const cap = (items, max = 10) => items.length > max ? items.slice(0, max) : items;
32
32
  export const GOOGLE_DOCS = {
33
- titles: 'https://developers.google.com/search/docs/appearance/title-link',
34
- snippets: 'https://developers.google.com/search/docs/appearance/snippet',
35
- canonical: 'https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls',
36
- robotsMeta: 'https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag',
37
- robotsTxt: 'https://developers.google.com/search/docs/crawling-indexing/robots/intro',
38
- sitemaps: 'https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview',
39
- hreflang: 'https://developers.google.com/search/docs/specialty/international/localized-versions',
40
- images: 'https://developers.google.com/search/docs/appearance/google-images',
41
- links: 'https://developers.google.com/search/docs/crawling-indexing/links-crawlable',
42
- structuredData: 'https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data',
43
- javascript: 'https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics',
44
- cwv: 'https://web.dev/articles/vitals',
45
- mobile: 'https://developers.google.com/search/docs/crawling-indexing/mobile/mobile-sites-mobile-first-indexing',
46
- redirects: 'https://developers.google.com/search/docs/crawling-indexing/301-redirects',
47
- softs404: 'https://developers.google.com/search/docs/crawling-indexing/http-network-errors#soft-404-errors',
48
- og: 'https://ogp.me/',
49
- twitter: 'https://developer.x.com/en/docs/x-for-websites/cards/overview/markup',
33
+ titles: "https://developers.google.com/search/docs/appearance/title-link",
34
+ snippets: "https://developers.google.com/search/docs/appearance/snippet",
35
+ canonical: "https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls",
36
+ robotsMeta: "https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag",
37
+ robotsTxt: "https://developers.google.com/search/docs/crawling-indexing/robots/intro",
38
+ sitemaps: "https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview",
39
+ hreflang: "https://developers.google.com/search/docs/specialty/international/localized-versions",
40
+ images: "https://developers.google.com/search/docs/appearance/google-images",
41
+ links: "https://developers.google.com/search/docs/crawling-indexing/links-crawlable",
42
+ structuredData: "https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data",
43
+ javascript: "https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics",
44
+ cwv: "https://web.dev/articles/vitals",
45
+ mobile: "https://developers.google.com/search/docs/crawling-indexing/mobile/mobile-sites-mobile-first-indexing",
46
+ redirects: "https://developers.google.com/search/docs/crawling-indexing/301-redirects",
47
+ softs404: "https://developers.google.com/search/docs/crawling-indexing/http-network-errors#soft-404-errors",
48
+ og: "https://ogp.me/",
49
+ twitter: "https://developer.x.com/en/docs/x-for-websites/cards/overview/markup",
50
50
  };
51
51
  //# sourceMappingURL=rule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/core/rules/rule.ts"],"names":[],"mappings":"AAuBA,0FAA0F;AAC1F,MAAM,UAAU,OAAO,CAAC,IAAc,EAAE,OAAe,EAAE,SAAwB,EAAE;IACjF,MAAM,CAAC,GAAY;QACjB,IAAI,EAAE,IAAI,CAAC,EAAE;QACb,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ;QAC1C,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO;QACP,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc;KAC7D,CAAC;IACF,IAAI,MAAM,CAAC,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAClD,IAAI,MAAM,CAAC,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAClD,IAAI,IAAI,CAAC,IAAI;QAAE,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IAClC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,IAAc,EACd,KAAsI;IAEtI,OAAO;QACL,IAAI;QACJ,KAAK,CAAC,GAAG;YACP,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;YAC/E,IAAI,CAAC,MAAM;gBAAE,OAAO,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACnD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,CAAI,KAAU,EAAE,GAAG,GAAG,EAAE;IACzC,OAAO,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC1D,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM,EAAE,iEAAiE;IACzE,QAAQ,EAAE,8DAA8D;IACxE,SAAS,EAAE,wFAAwF;IACnG,UAAU,EAAE,6EAA6E;IACzF,SAAS,EAAE,0EAA0E;IACrF,QAAQ,EAAE,+EAA+E;IACzF,QAAQ,EAAE,sFAAsF;IAChG,MAAM,EAAE,oEAAoE;IAC5E,KAAK,EAAE,6EAA6E;IACpF,cAAc,EAAE,4FAA4F;IAC5G,UAAU,EAAE,8FAA8F;IAC1G,GAAG,EAAE,iCAAiC;IACtC,MAAM,EAAE,uGAAuG;IAC/G,SAAS,EAAE,2EAA2E;IACtF,QAAQ,EAAE,iGAAiG;IAC3G,EAAE,EAAE,iBAAiB;IACrB,OAAO,EAAE,sEAAsE;CACvE,CAAC"}
1
+ {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/core/rules/rule.ts"],"names":[],"mappings":"AA+BA,0FAA0F;AAC1F,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,IAAc,EACd,OAAe,EACf,MAAM,GAAkB,EAAE,EACjB,EAAE;IACX,MAAM,CAAC,GAAY;QACjB,IAAI,EAAE,IAAI,CAAC,EAAE;QACb,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ;QAC1C,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO;QACP,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc;KAC7D,CAAC;IAEF,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,OAAO,CAAC,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,IAAc,EACd,KAG2C,EACrC,EAAE,CAAC,CAAC;IACV,IAAI;IACJ,KAAK,CAAC,GAAG;QACP,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAC5C,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAC/B,CAAC;QAEF,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,GAAG,GAAG,CAAI,KAAU,EAAE,GAAG,GAAG,EAAE,EAAO,EAAE,CAClD,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAEnD,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM,EAAE,iEAAiE;IACzE,QAAQ,EAAE,8DAA8D;IACxE,SAAS,EACP,wFAAwF;IAC1F,UAAU,EACR,6EAA6E;IAC/E,SAAS,EACP,0EAA0E;IAC5E,QAAQ,EACN,+EAA+E;IACjF,QAAQ,EACN,sFAAsF;IACxF,MAAM,EAAE,oEAAoE;IAC5E,KAAK,EACH,6EAA6E;IAC/E,cAAc,EACZ,4FAA4F;IAC9F,UAAU,EACR,8FAA8F;IAChG,GAAG,EAAE,iCAAiC;IACtC,MAAM,EACJ,uGAAuG;IACzG,SAAS,EACP,2EAA2E;IAC7E,QAAQ,EACN,iGAAiG;IACnG,EAAE,EAAE,iBAAiB;IACrB,OAAO,EACL,sEAAsE;CAChE,CAAC"}
@@ -1,74 +1,115 @@
1
- import { normalizeUrl } from '../url.js';
2
- import { defineRule, GOOGLE_DOCS } from './rule.js';
3
- const OG_CORE = ['title', 'description', 'image'];
4
- const OG_RECOMMENDED = ['title', 'description', 'image', 'url', 'type'];
5
- const TWITTER_CARDS = new Set(['summary', 'summary_large_image', 'app', 'player']);
1
+ import { normalizeUrl } from "../url.js";
2
+ import { defineRule, GOOGLE_DOCS } from "./rule.js";
3
+ const OG_CORE = ["title", "description", "image"];
4
+ const OG_RECOMMENDED = [
5
+ "title",
6
+ "description",
7
+ "image",
8
+ "url",
9
+ "type",
10
+ ];
11
+ const TWITTER_CARDS = new Set([
12
+ "summary",
13
+ "summary_large_image",
14
+ "app",
15
+ "player",
16
+ ]);
6
17
  export const socialRules = [
7
18
  defineRule({
8
- id: 'og.missing',
9
- category: 'social',
10
- severity: 'warning',
11
- description: 'No Open Graph tags (og:title, og:description, og:image) are present.',
12
- recommendation: 'Add og:title, og:description, og:image (absolute URL, at least 1200x630), og:url and og:type so shares on social platforms and chat apps render a rich preview.',
19
+ id: "og.missing",
20
+ category: "social",
21
+ severity: "warning",
22
+ description: "No Open Graph tags (og:title, og:description, og:image) are present.",
23
+ recommendation: "Add og:title, og:description, og:image (absolute URL, at least 1200x630), og:url and og:type so shares on social platforms and chat apps render a rich preview.",
13
24
  docs: GOOGLE_DOCS.og,
14
- }, ({ page }, make) => (OG_CORE.some((k) => page.og[k]) ? null : make('Page has no Open Graph tags.', { selector: 'head > meta[property^="og:"]' }))),
25
+ }, ({ page }, make) => OG_CORE.some((k) => page.og[k])
26
+ ? null
27
+ : make("Page has no Open Graph tags.", {
28
+ selector: 'head > meta[property^="og:"]',
29
+ })),
15
30
  defineRule({
16
- id: 'og.incomplete',
17
- category: 'social',
18
- severity: 'info',
19
- description: 'Some Open Graph tags are present but one or more of og:title, og:description, og:image, og:url, og:type is missing.',
20
- recommendation: 'Fill in the missing Open Graph properties. og:image and og:description have the biggest effect on share previews.',
31
+ id: "og.incomplete",
32
+ category: "social",
33
+ severity: "info",
34
+ description: "Some Open Graph tags are present but one or more of og:title, og:description, og:image, og:url, og:type is missing.",
35
+ recommendation: "Fill in the missing Open Graph properties. og:image and og:description have the biggest effect on share previews.",
21
36
  docs: GOOGLE_DOCS.og,
22
37
  }, ({ page }, make) => {
23
- if (!OG_CORE.some((k) => page.og[k]))
38
+ if (!OG_CORE.some((k) => page.og[k])) {
24
39
  return null;
40
+ }
25
41
  const missing = OG_RECOMMENDED.filter((k) => !page.og[k]);
26
- return missing.length ? make(`Open Graph is missing: ${missing.map((m) => `og:${m}`).join(', ')}.`, { selector: 'head > meta[property^="og:"]', evidence: { missing, present: Object.keys(page.og) } }) : null;
42
+ return missing.length
43
+ ? make(`Open Graph is missing: ${missing.map((m) => `og:${m}`).join(", ")}.`, {
44
+ selector: 'head > meta[property^="og:"]',
45
+ evidence: { missing, present: Object.keys(page.og) },
46
+ })
47
+ : null;
27
48
  }),
28
49
  defineRule({
29
- id: 'og.image-not-absolute',
30
- category: 'social',
31
- severity: 'warning',
32
- description: 'og:image is not an absolute URL.',
33
- recommendation: 'Use a full https:// URL for og:image; most platforms ignore relative image paths.',
50
+ id: "og.image-not-absolute",
51
+ category: "social",
52
+ severity: "warning",
53
+ description: "og:image is not an absolute URL.",
54
+ recommendation: "Use a full https:// URL for og:image; most platforms ignore relative image paths.",
34
55
  docs: GOOGLE_DOCS.og,
35
56
  }, ({ page }, make) => {
36
- const img = page.og['image'];
37
- return img && !/^https?:\/\//i.test(img) ? make(`og:image "${img}" is not an absolute URL.`, { selector: 'head > meta[property="og:image"]', evidence: { image: img } }) : null;
57
+ const img = page.og["image"];
58
+ return img && !/^https?:\/\//iu.test(img)
59
+ ? make(`og:image "${img}" is not an absolute URL.`, {
60
+ selector: 'head > meta[property="og:image"]',
61
+ evidence: { image: img },
62
+ })
63
+ : null;
38
64
  }),
39
65
  defineRule({
40
- id: 'og.url-mismatch',
41
- category: 'social',
42
- severity: 'info',
43
- description: 'og:url differs from the canonical URL.',
44
- recommendation: 'Set og:url to the same URL as the canonical so share counts and previews consolidate.',
66
+ id: "og.url-mismatch",
67
+ category: "social",
68
+ severity: "info",
69
+ description: "og:url differs from the canonical URL.",
70
+ recommendation: "Set og:url to the same URL as the canonical so share counts and previews consolidate.",
45
71
  docs: GOOGLE_DOCS.og,
46
72
  }, ({ page }, make) => {
47
- const ogUrl = page.og['url'];
48
- if (!ogUrl || !page.canonical)
73
+ const ogUrl = page.og["url"];
74
+ if (!ogUrl || !page.canonical) {
49
75
  return null;
76
+ }
50
77
  const a = normalizeUrl(ogUrl);
51
78
  const b = normalizeUrl(page.canonical);
52
- return a && b && a !== b ? make('og:url does not match the canonical URL.', { selector: 'head > meta[property="og:url"]', evidence: { ogUrl, canonical: page.canonical } }) : null;
79
+ return a && b && a !== b
80
+ ? make("og:url does not match the canonical URL.", {
81
+ selector: 'head > meta[property="og:url"]',
82
+ evidence: { ogUrl, canonical: page.canonical },
83
+ })
84
+ : null;
53
85
  }),
54
86
  defineRule({
55
- id: 'twitter.card-missing',
56
- category: 'social',
57
- severity: 'info',
58
- description: 'No twitter:card meta tag is present.',
87
+ id: "twitter.card-missing",
88
+ category: "social",
89
+ severity: "info",
90
+ description: "No twitter:card meta tag is present.",
59
91
  recommendation: 'Add <meta name="twitter:card" content="summary_large_image">; X/Twitter falls back to Open Graph for the other fields.',
60
92
  docs: GOOGLE_DOCS.twitter,
61
- }, ({ page }, make) => (page.twitter['card'] ? null : make('Page has no twitter:card tag.', { selector: 'head > meta[name="twitter:card"]' }))),
93
+ }, ({ page }, make) => page.twitter["card"]
94
+ ? null
95
+ : make("Page has no twitter:card tag.", {
96
+ selector: 'head > meta[name="twitter:card"]',
97
+ })),
62
98
  defineRule({
63
- id: 'twitter.card-invalid',
64
- category: 'social',
65
- severity: 'warning',
66
- description: 'twitter:card has a value other than summary, summary_large_image, app or player.',
67
- recommendation: 'Use one of the supported card types, usually summary_large_image.',
99
+ id: "twitter.card-invalid",
100
+ category: "social",
101
+ severity: "warning",
102
+ description: "twitter:card has a value other than summary, summary_large_image, app or player.",
103
+ recommendation: "Use one of the supported card types, usually summary_large_image.",
68
104
  docs: GOOGLE_DOCS.twitter,
69
105
  }, ({ page }, make) => {
70
- const card = page.twitter['card'];
71
- return card && !TWITTER_CARDS.has(card.toLowerCase()) ? make(`twitter:card "${card}" is not a valid card type.`, { selector: 'head > meta[name="twitter:card"]', evidence: { card } }) : null;
106
+ const { card } = page.twitter;
107
+ return card && !TWITTER_CARDS.has(card.toLowerCase())
108
+ ? make(`twitter:card "${card}" is not a valid card type.`, {
109
+ selector: 'head > meta[name="twitter:card"]',
110
+ evidence: { card },
111
+ })
112
+ : null;
72
113
  }),
73
114
  ];
74
115
  //# sourceMappingURL=social.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"social.js","sourceRoot":"","sources":["../../../src/core/rules/social.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"social.js","sourceRoot":"","sources":["../../../src/core/rules/social.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { StructuredDataItem } from '../types.js';
1
+ import type { StructuredDataItem } from "../types.js";
2
2
  interface SchemaSpec {
3
3
  required: string[];
4
4
  recommended: string[];
@@ -12,12 +12,14 @@ interface Issue {
12
12
  missing: string[];
13
13
  path: string;
14
14
  }
15
- /** Walk one structured-data item (and nested typed objects) collecting spec violations. */
16
- export declare function validateItem(item: StructuredDataItem): {
15
+ /** What one structured-data item lacks or gets wrong, against the specs above. */
16
+ export interface ItemValidation {
17
17
  required: Issue[];
18
18
  recommended: Issue[];
19
19
  badDates: string[];
20
20
  unknownTypes: string[];
21
- };
21
+ }
22
+ /** Walk one structured-data item (and nested typed objects) collecting spec violations. */
23
+ export declare const validateItem: (item: StructuredDataItem) => ItemValidation;
22
24
  export declare const structuredDataRules: import("./rule.js").Rule[];
23
25
  export {};