@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.
- package/LICENSE +1 -1
- package/README.md +124 -18
- package/dist/brand.d.ts +11 -0
- package/dist/brand.js +22 -0
- package/dist/brand.js.map +1 -0
- package/dist/cli.js +174 -86
- 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 +53 -0
- package/dist/core/aeo/page.js +312 -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 +37 -16
- package/dist/core/audit.js +389 -249
- package/dist/core/audit.js.map +1 -1
- package/dist/core/concurrency.d.ts +4 -4
- package/dist/core/concurrency.js +33 -23
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/crawl.d.ts +9 -5
- package/dist/core/crawl.js +271 -123
- package/dist/core/crawl.js.map +1 -1
- package/dist/core/fetch.d.ts +5 -5
- package/dist/core/fetch.js +151 -100
- 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 +17 -0
- package/dist/core/html/dom.js +83 -0
- package/dist/core/html/dom.js.map +1 -0
- package/dist/core/html/extract.d.ts +2 -2
- package/dist/core/html/extract.js +102 -111
- package/dist/core/html/extract.js.map +1 -1
- package/dist/core/html/structured-data.d.ts +16 -6
- package/dist/core/html/structured-data.js +180 -145
- package/dist/core/html/structured-data.js.map +1 -1
- package/dist/core/html/text.d.ts +14 -14
- package/dist/core/html/text.js +110 -107
- package/dist/core/html/text.js.map +1 -1
- package/dist/core/lighthouse.d.ts +26 -25
- package/dist/core/lighthouse.js +181 -105
- package/dist/core/lighthouse.js.map +1 -1
- package/dist/core/performance-rules.d.ts +2 -2
- package/dist/core/performance-rules.js +105 -33
- package/dist/core/performance-rules.js.map +1 -1
- package/dist/core/render-rules.d.ts +5 -4
- package/dist/core/render-rules.js +129 -62
- package/dist/core/render-rules.js.map +1 -1
- package/dist/core/render.d.ts +2 -2
- package/dist/core/render.js +29 -23
- package/dist/core/render.js.map +1 -1
- package/dist/core/report/markdown.d.ts +4 -2
- package/dist/core/report/markdown.js +210 -129
- package/dist/core/report/markdown.js.map +1 -1
- package/dist/core/robots.d.ts +12 -5
- package/dist/core/robots.js +43 -17
- 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 +1 -1
- package/dist/core/rules/canonical.js +93 -57
- package/dist/core/rules/canonical.js.map +1 -1
- package/dist/core/rules/content.js +40 -26
- package/dist/core/rules/content.js.map +1 -1
- package/dist/core/rules/document.js +64 -41
- package/dist/core/rules/document.js.map +1 -1
- package/dist/core/rules/headings.js +58 -38
- package/dist/core/rules/headings.js.map +1 -1
- package/dist/core/rules/hreflang.js +70 -38
- package/dist/core/rules/hreflang.js.map +1 -1
- package/dist/core/rules/images.js +44 -26
- package/dist/core/rules/images.js.map +1 -1
- package/dist/core/rules/index.d.ts +10 -6
- package/dist/core/rules/index.js +43 -36
- package/dist/core/rules/index.js.map +1 -1
- package/dist/core/rules/indexing.d.ts +2 -2
- package/dist/core/rules/indexing.js +48 -26
- package/dist/core/rules/indexing.js.map +1 -1
- package/dist/core/rules/keyword.js +84 -47
- package/dist/core/rules/keyword.js.map +1 -1
- package/dist/core/rules/links.js +72 -37
- package/dist/core/rules/links.js.map +1 -1
- package/dist/core/rules/meta.js +55 -34
- package/dist/core/rules/meta.js.map +1 -1
- package/dist/core/rules/response.js +75 -41
- package/dist/core/rules/response.js.map +1 -1
- package/dist/core/rules/rule.d.ts +6 -6
- package/dist/core/rules/rule.js +36 -36
- package/dist/core/rules/rule.js.map +1 -1
- package/dist/core/rules/social.js +86 -45
- package/dist/core/rules/social.js.map +1 -1
- package/dist/core/rules/structured-data.d.ts +6 -4
- package/dist/core/rules/structured-data.js +314 -102
- package/dist/core/rules/structured-data.js.map +1 -1
- package/dist/core/rules/title.js +48 -33
- package/dist/core/rules/title.js.map +1 -1
- package/dist/core/score.d.ts +5 -3
- package/dist/core/score.js +11 -9
- package/dist/core/score.js.map +1 -1
- package/dist/core/site-rules.d.ts +3 -3
- package/dist/core/site-rules.js +409 -198
- package/dist/core/site-rules.js.map +1 -1
- package/dist/core/sitemap.d.ts +5 -5
- package/dist/core/sitemap.js +109 -64
- package/dist/core/sitemap.js.map +1 -1
- package/dist/core/static.d.ts +2 -2
- package/dist/core/static.js +210 -112
- package/dist/core/static.js.map +1 -1
- package/dist/core/summary.d.ts +2 -2
- package/dist/core/summary.js +11 -6
- package/dist/core/summary.js.map +1 -1
- package/dist/core/types.d.ts +189 -14
- package/dist/core/types.js +15 -8
- package/dist/core/types.js.map +1 -1
- package/dist/core/url.d.ts +11 -11
- package/dist/core/url.js +122 -70
- package/dist/core/url.js.map +1 -1
- package/dist/index.d.ts +15 -11
- package/dist/index.js +11 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +7 -4
- package/dist/mcp-bin.js.map +1 -1
- package/dist/mcp.d.ts +3 -3
- package/dist/mcp.js +437 -121
- package/dist/mcp.js.map +1 -1
- package/dist/version.d.ts +0 -1
- package/dist/version.js +4 -2
- package/dist/version.js.map +1 -1
- package/package.json +32 -10
- package/skills/seo-audit/SKILL.md +8 -2
|
@@ -1,59 +1,93 @@
|
|
|
1
|
-
import { isLocalHost } from
|
|
2
|
-
import { defineRule, GOOGLE_DOCS } from
|
|
1
|
+
import { isLocalHost } from "./canonical.js";
|
|
2
|
+
import { defineRule, GOOGLE_DOCS } from "./rule.js";
|
|
3
3
|
export const responseRules = [
|
|
4
4
|
defineRule({
|
|
5
|
-
id:
|
|
6
|
-
category:
|
|
7
|
-
severity:
|
|
8
|
-
description:
|
|
9
|
-
recommendation:
|
|
10
|
-
}, ({ response }, make) =>
|
|
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:
|
|
13
|
-
category:
|
|
14
|
-
severity:
|
|
15
|
-
description:
|
|
16
|
-
recommendation:
|
|
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) =>
|
|
19
|
-
|
|
20
|
-
|
|
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:
|
|
23
|
-
category:
|
|
24
|
-
severity:
|
|
25
|
-
description:
|
|
26
|
-
recommendation:
|
|
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}.`, {
|
|
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:
|
|
33
|
-
category:
|
|
34
|
-
severity:
|
|
35
|
-
description:
|
|
36
|
-
recommendation:
|
|
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 &&
|
|
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:
|
|
43
|
-
category:
|
|
44
|
-
severity:
|
|
45
|
-
description:
|
|
46
|
-
recommendation:
|
|
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
|
|
49
|
-
|
|
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:
|
|
53
|
-
category:
|
|
54
|
-
severity:
|
|
55
|
-
description:
|
|
56
|
-
recommendation:
|
|
57
|
-
}, ({ response, source }, make) => source ===
|
|
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":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,UAAU,CACR;QACE,EAAE,EAAE,uBAAuB;QAC3B,QAAQ,EAAE,UAAU;QACpB,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,4CAA4C;QACzD,cAAc,
|
|
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
|
|
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)
|
|
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?:
|
|
18
|
+
evidence?: Finding["evidence"];
|
|
19
19
|
}
|
|
20
20
|
/** Build a finding from rule metadata, overriding only what differs for this instance. */
|
|
21
|
-
export declare
|
|
22
|
-
export declare
|
|
23
|
-
export declare
|
|
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";
|
package/dist/core/rules/rule.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Build a finding from rule metadata, overriding only what differs for this instance. */
|
|
2
|
-
export
|
|
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
|
-
|
|
12
|
+
}
|
|
13
|
+
if (extras.evidence) {
|
|
13
14
|
f.evidence = extras.evidence;
|
|
14
|
-
|
|
15
|
+
}
|
|
16
|
+
if (meta.docs) {
|
|
15
17
|
f.docs = meta.docs;
|
|
18
|
+
}
|
|
16
19
|
return f;
|
|
17
|
-
}
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
check(ctx)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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:
|
|
34
|
-
snippets:
|
|
35
|
-
canonical:
|
|
36
|
-
robotsMeta:
|
|
37
|
-
robotsTxt:
|
|
38
|
-
sitemaps:
|
|
39
|
-
hreflang:
|
|
40
|
-
images:
|
|
41
|
-
links:
|
|
42
|
-
structuredData:
|
|
43
|
-
javascript:
|
|
44
|
-
cwv:
|
|
45
|
-
mobile:
|
|
46
|
-
redirects:
|
|
47
|
-
softs404:
|
|
48
|
-
og:
|
|
49
|
-
twitter:
|
|
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":"
|
|
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
|
|
2
|
-
import { defineRule, GOOGLE_DOCS } from
|
|
3
|
-
const OG_CORE = [
|
|
4
|
-
const OG_RECOMMENDED = [
|
|
5
|
-
|
|
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:
|
|
9
|
-
category:
|
|
10
|
-
severity:
|
|
11
|
-
description:
|
|
12
|
-
recommendation:
|
|
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) =>
|
|
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:
|
|
17
|
-
category:
|
|
18
|
-
severity:
|
|
19
|
-
description:
|
|
20
|
-
recommendation:
|
|
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
|
|
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:
|
|
30
|
-
category:
|
|
31
|
-
severity:
|
|
32
|
-
description:
|
|
33
|
-
recommendation:
|
|
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[
|
|
37
|
-
return img && !/^https?:\/\//
|
|
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:
|
|
41
|
-
category:
|
|
42
|
-
severity:
|
|
43
|
-
description:
|
|
44
|
-
recommendation:
|
|
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[
|
|
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
|
|
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:
|
|
56
|
-
category:
|
|
57
|
-
severity:
|
|
58
|
-
description:
|
|
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) =>
|
|
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:
|
|
64
|
-
category:
|
|
65
|
-
severity:
|
|
66
|
-
description:
|
|
67
|
-
recommendation:
|
|
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
|
|
71
|
-
return card && !TWITTER_CARDS.has(card.toLowerCase())
|
|
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":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAU,CAAC;
|
|
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
|
|
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
|
-
/**
|
|
16
|
-
export
|
|
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 {};
|