@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,119 +1,165 @@
1
- import { VERSION } from '../version.js';
1
+ import { VERSION } from "../version.js";
2
2
  export const DEFAULT_USER_AGENT = `Mozilla/5.0 (compatible; agentic-seo-check/${VERSION}; +https://github.com/rivi-software/agentic-seo-check)`;
3
- const KEPT_HEADERS = ['content-type', 'x-robots-tag', 'link', 'content-language', 'cache-control', 'last-modified', 'location', 'content-length', 'vary'];
3
+ const KEPT_HEADERS = [
4
+ "content-type",
5
+ "x-robots-tag",
6
+ "link",
7
+ "content-language",
8
+ "cache-control",
9
+ "last-modified",
10
+ "location",
11
+ "content-length",
12
+ "vary",
13
+ ];
4
14
  const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
5
- /** GET a URL, following redirects manually so the chain is recorded. */
6
- export async function fetchPage(url, options = {}, readBody = true) {
7
- const timeoutMs = options.timeoutMs ?? 15_000;
8
- const maxRedirects = options.maxRedirects ?? 10;
9
- const started = performance.now();
10
- const chain = [];
11
- let current = url;
12
- for (let hop = 0; hop <= maxRedirects; hop++) {
13
- const controller = new AbortController();
14
- const timer = setTimeout(() => controller.abort(), timeoutMs);
15
- try {
16
- const res = await fetch(current, {
17
- method: 'GET',
18
- redirect: 'manual',
19
- signal: controller.signal,
20
- headers: {
21
- 'user-agent': options.userAgent ?? DEFAULT_USER_AGENT,
22
- accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
23
- 'accept-language': 'en,*;q=0.5',
24
- ...(options.headers ?? {}),
25
- },
26
- });
27
- const headers = pickHeaders(res.headers);
28
- if (REDIRECT_STATUSES.has(res.status) && headers['location']) {
29
- chain.push({ url: current, status: res.status });
30
- let nextUrl;
31
- try {
32
- nextUrl = new URL(headers['location'], current).href;
33
- }
34
- catch {
35
- return result(url, current, res.status, headers, null, chain, started, `Invalid Location header: ${headers['location']}`);
36
- }
37
- await res.body?.cancel().catch(() => undefined);
38
- if (chain.some((h) => h.url === nextUrl) || nextUrl === current) {
39
- return result(url, nextUrl, res.status, headers, null, chain, started, 'Redirect loop');
40
- }
41
- current = nextUrl;
42
- continue;
43
- }
44
- let body = null;
45
- if (readBody)
46
- body = await readLimited(res, options.maxBodyBytes ?? 5_000_000);
47
- else
48
- await res.body?.cancel().catch(() => undefined);
49
- return result(url, current, res.status, headers, body, chain, started);
50
- }
51
- catch (err) {
52
- const message = err instanceof Error ? (err.name === 'AbortError' ? `Timed out after ${timeoutMs} ms` : err.message) : String(err);
53
- return result(url, current, null, {}, null, chain, started, message);
54
- }
55
- finally {
56
- clearTimeout(timer);
15
+ const pickHeaders = (headers) => {
16
+ const out = {};
17
+ for (const name of KEPT_HEADERS) {
18
+ const value = headers.get(name);
19
+ if (value !== null) {
20
+ out[name] = value;
57
21
  }
58
22
  }
59
- return result(url, current, null, {}, null, chain, started, `More than ${maxRedirects} redirects`);
60
- }
61
- /** Status/redirect check without downloading the body. */
62
- export function probeUrl(url, options = {}) {
63
- return fetchPage(url, options, false);
64
- }
65
- async function readLimited(res, maxBytes) {
66
- if (!res.body)
67
- return '';
23
+ return out;
24
+ };
25
+ const result = (requestedUrl, finalUrl, status, headers, body, chain, started, error) => {
26
+ const r = {
27
+ requestedUrl,
28
+ finalUrl,
29
+ status,
30
+ headers,
31
+ body,
32
+ redirectChain: chain,
33
+ timeMs: Math.round(performance.now() - started),
34
+ contentType: headers["content-type"] ?? null,
35
+ };
36
+ if (error) {
37
+ r.error = error;
38
+ }
39
+ return r;
40
+ };
41
+ const readLimited = async (res, maxBytes) => {
42
+ if (!res.body) {
43
+ return "";
44
+ }
68
45
  const reader = res.body.getReader();
69
46
  const chunks = [];
70
47
  let total = 0;
48
+ let truncated = false;
71
49
  for (;;) {
50
+ // oxlint-disable-next-line eslint/no-await-in-loop -- a stream hands over its chunks one after another
72
51
  const { done, value } = await reader.read();
73
- if (done)
52
+ if (done) {
74
53
  break;
54
+ }
75
55
  if (value) {
76
56
  chunks.push(value);
77
57
  total += value.byteLength;
78
58
  if (total >= maxBytes) {
79
- await reader.cancel().catch(() => undefined);
59
+ truncated = true;
80
60
  break;
81
61
  }
82
62
  }
83
63
  }
84
- const charset = /charset=([\w-]+)/i.exec(res.headers.get('content-type') ?? '')?.[1];
64
+ if (truncated) {
65
+ await reader.cancel().catch(() => null);
66
+ }
67
+ const charset = /charset=(?<charset>[\w-]+)/iu.exec(res.headers.get("content-type") ?? "")?.groups?.charset;
85
68
  try {
86
- return new TextDecoder(charset ?? 'utf-8').decode(Buffer.concat(chunks));
69
+ return new TextDecoder(charset ?? "utf-8").decode(Buffer.concat(chunks));
87
70
  }
88
71
  catch {
89
- return new TextDecoder('utf-8').decode(Buffer.concat(chunks));
72
+ return new TextDecoder("utf-8").decode(Buffer.concat(chunks));
90
73
  }
91
- }
92
- function pickHeaders(headers) {
93
- const out = {};
94
- for (const name of KEPT_HEADERS) {
95
- const value = headers.get(name);
96
- if (value !== null)
97
- out[name] = value;
74
+ };
75
+ const fetchHop = async (walk, current) => {
76
+ const { url, options, readBody, timeoutMs, started, chain } = walk;
77
+ const controller = new AbortController();
78
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
79
+ try {
80
+ const res = await fetch(current, {
81
+ method: "GET",
82
+ redirect: "manual",
83
+ signal: controller.signal,
84
+ headers: {
85
+ "user-agent": options.userAgent ?? DEFAULT_USER_AGENT,
86
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
87
+ "accept-language": "en,*;q=0.5",
88
+ ...options.headers,
89
+ },
90
+ });
91
+ const headers = pickHeaders(res.headers);
92
+ if (REDIRECT_STATUSES.has(res.status) && headers["location"]) {
93
+ chain.push({ url: current, status: res.status });
94
+ let nextUrl;
95
+ try {
96
+ nextUrl = new URL(headers["location"], current).href;
97
+ }
98
+ catch {
99
+ return {
100
+ done: result(url, current, res.status, headers, null, chain, started, `Invalid Location header: ${headers["location"]}`),
101
+ };
102
+ }
103
+ await res.body?.cancel().catch(() => null);
104
+ if (chain.some((h) => h.url === nextUrl) || nextUrl === current) {
105
+ return {
106
+ done: result(url, nextUrl, res.status, headers, null, chain, started, "Redirect loop"),
107
+ };
108
+ }
109
+ return { next: nextUrl };
110
+ }
111
+ let body = null;
112
+ if (readBody) {
113
+ body = await readLimited(res, options.maxBodyBytes ?? 5_000_000);
114
+ }
115
+ else {
116
+ await res.body?.cancel().catch(() => null);
117
+ }
118
+ return {
119
+ done: result(url, current, res.status, headers, body, chain, started),
120
+ };
98
121
  }
99
- return out;
100
- }
101
- function result(requestedUrl, finalUrl, status, headers, body, chain, started, error) {
102
- const r = {
103
- requestedUrl,
104
- finalUrl,
105
- status,
106
- headers,
107
- body,
108
- redirectChain: chain,
109
- timeMs: Math.round(performance.now() - started),
110
- contentType: headers['content-type'] ?? null,
122
+ catch (error) {
123
+ let message = String(error);
124
+ if (error instanceof Error) {
125
+ message =
126
+ error.name === "AbortError"
127
+ ? `Timed out after ${timeoutMs} ms`
128
+ : error.message;
129
+ }
130
+ return {
131
+ done: result(url, current, null, {}, null, chain, started, message),
132
+ };
133
+ }
134
+ finally {
135
+ clearTimeout(timer);
136
+ }
137
+ };
138
+ /** GET a URL, following redirects manually so the chain is recorded. */
139
+ export const fetchPage = async (url, options = {}, readBody = true) => {
140
+ const maxRedirects = options.maxRedirects ?? 10;
141
+ const walk = {
142
+ url,
143
+ options,
144
+ readBody,
145
+ timeoutMs: options.timeoutMs ?? 15_000,
146
+ started: performance.now(),
147
+ chain: [],
111
148
  };
112
- if (error)
113
- r.error = error;
114
- return r;
115
- }
116
- export function toResponseInfo(r) {
149
+ let current = url;
150
+ for (let hop = 0; hop <= maxRedirects; hop += 1) {
151
+ // oxlint-disable-next-line eslint/no-await-in-loop -- each request goes where the one before it redirected
152
+ const outcome = await fetchHop(walk, current);
153
+ if ("done" in outcome) {
154
+ return outcome.done;
155
+ }
156
+ current = outcome.next;
157
+ }
158
+ return result(url, current, null, {}, null, walk.chain, walk.started, `More than ${maxRedirects} redirects`);
159
+ };
160
+ /** Status/redirect check without downloading the body. */
161
+ export const probeUrl = (url, options = {}) => fetchPage(url, options, false);
162
+ export const toResponseInfo = (r) => {
117
163
  const info = {
118
164
  status: r.status,
119
165
  finalUrl: r.finalUrl,
@@ -122,14 +168,19 @@ export function toResponseInfo(r) {
122
168
  contentType: r.contentType,
123
169
  headers: r.headers,
124
170
  };
125
- if (r.error)
171
+ if (r.error) {
126
172
  info.error = r.error;
173
+ }
127
174
  return info;
128
- }
129
- export function isHtmlResponse(r) {
130
- const ct = r.contentType?.toLowerCase() ?? '';
131
- if (ct)
132
- return ct.includes('text/html') || ct.includes('application/xhtml');
133
- return typeof r.body === 'string' && /<html|<!doctype/i.test(r.body.slice(0, 2000));
134
- }
175
+ };
176
+ export const isHtmlResponse = (r) => {
177
+ const ct = r.contentType?.toLowerCase() ?? "";
178
+ if (ct) {
179
+ return ct.includes("text/html") || ct.includes("application/xhtml");
180
+ }
181
+ if (!r.body) {
182
+ return false;
183
+ }
184
+ return /<html|<!doctype/iu.test(r.body.slice(0, 2000));
185
+ };
135
186
  //# sourceMappingURL=fetch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/core/fetch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/core/fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { AnswerSignals } from "../types.js";
2
+ import type { Document } from "./dom.js";
3
+ /** Reads what the AEO rules need from a parsed document; `bodyText` is its visible body text. */
4
+ export declare const extractAnswerSignals: (doc: Document, bodyText: string) => AnswerSignals;
@@ -0,0 +1,198 @@
1
+ import { hasChildren, isTag, isText } from "domhandler";
2
+ import { attr, childElements, HIDDEN_ELEMENTS, queryAll, queryOne, text, visibleText, } from "./dom.js";
3
+ import { collapseWhitespace, wordCount } from "./text.js";
4
+ /** Meta names whose content holds robots directives for one crawler or for all of them. */
5
+ const ROBOTS_META_NAMES = new Set([
6
+ "robots",
7
+ "googlebot",
8
+ "googlebot-news",
9
+ "bingbot",
10
+ ]);
11
+ /** Containers FAQ widgets put a question in when it is not a heading. */
12
+ const QUESTION_CONTAINERS = new Set(["summary", "dt"]);
13
+ /** Elements an answer usually starts with. */
14
+ const ANSWER_BLOCKS = new Set([
15
+ "p",
16
+ "li",
17
+ "dd",
18
+ "blockquote",
19
+ "td",
20
+ "figcaption",
21
+ "pre",
22
+ ]);
23
+ /** Page chrome: lists and tables here are navigation, not content. */
24
+ const CHROME = new Set(["nav", "header", "footer", "aside"]);
25
+ /** Roots JavaScript frameworks render into; empty in the HTML when the page renders client-side. */
26
+ const APP_ROOTS = [
27
+ "#root",
28
+ "#app",
29
+ "#__next",
30
+ "#__nuxt",
31
+ "#___gatsby",
32
+ "#svelte",
33
+ "app-root",
34
+ "[data-reactroot]",
35
+ "[ng-app]",
36
+ ];
37
+ /*
38
+ * Written dates in the visible text. Every quantifier is bounded and every alternative is a fixed
39
+ * word, so each regular expression stays linear on long page text.
40
+ */
41
+ const DATE_PATTERNS = [
42
+ /\b\d{4}-\d{2}-\d{2}\b/u,
43
+ /\b\d{1,2}[./-]\d{1,2}[./-]\d{4}\b/u,
44
+ /\b(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|june?|july?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\.? \d{1,2}(?:st|nd|rd|th)?,? \d{4}\b/iu,
45
+ /\b\d{1,2}(?:st|nd|rd|th)?\.? (?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|june?|july?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\.?,? \d{4}\b/iu,
46
+ ];
47
+ const MAX_QUESTIONS = 100;
48
+ /** A question ends with a question mark; a long "heading" is a paragraph styled as one. */
49
+ const isQuestion = (value) => value.length > 0 && value.length <= 300 && /[??]$/u.test(value);
50
+ const isHeading = (el) => /^h[1-6]$/u.test(el.name);
51
+ const inChrome = (el) => {
52
+ for (let p = el.parent; p && isTag(p); p = p.parent) {
53
+ if (CHROME.has(p.name)) {
54
+ return true;
55
+ }
56
+ }
57
+ return false;
58
+ };
59
+ /**
60
+ * What a heading, `<summary>` or `<dt>` does to the question being read: "subsection" goes on with
61
+ * its section, "content" is ordinary text, a question starts a new section and null ends it.
62
+ */
63
+ const headingEffect = (el, current) => {
64
+ const heading = isHeading(el);
65
+ const label = collapseWhitespace(visibleText(el));
66
+ const question = isQuestion(label);
67
+ const level = heading ? Number(el.name.slice(1)) : 0;
68
+ if (!question) {
69
+ // "Why choose us?" answered by one card per reason, each under its own subheading, is answered.
70
+ if (heading && current && current.level > 0 && level > current.level) {
71
+ return "subsection";
72
+ }
73
+ // A <summary> or <dt> ends a section only when it asks a question itself.
74
+ return heading ? null : "content";
75
+ }
76
+ return {
77
+ level,
78
+ text: label.slice(0, 200),
79
+ sectionWords: 0,
80
+ firstBlockWords: null,
81
+ };
82
+ };
83
+ /** Question headings in document order, with how much text follows each before the next one. */
84
+ const findQuestions = (body) => {
85
+ const questions = [];
86
+ let current = null;
87
+ const walk = (nodes) => {
88
+ for (const node of nodes) {
89
+ if (isText(node)) {
90
+ if (current) {
91
+ current.sectionWords += wordCount(node.data);
92
+ }
93
+ continue;
94
+ }
95
+ if (!isTag(node)) {
96
+ if (hasChildren(node)) {
97
+ walk(node.children);
98
+ }
99
+ continue;
100
+ }
101
+ if (HIDDEN_ELEMENTS.has(node.name)) {
102
+ continue;
103
+ }
104
+ if (isHeading(node) || QUESTION_CONTAINERS.has(node.name)) {
105
+ const effect = headingEffect(node, current);
106
+ if (effect === "subsection") {
107
+ continue;
108
+ }
109
+ if (effect !== "content") {
110
+ current = effect;
111
+ if (current && questions.length < MAX_QUESTIONS) {
112
+ questions.push(current);
113
+ }
114
+ continue;
115
+ }
116
+ }
117
+ if (current &&
118
+ current.firstBlockWords === null &&
119
+ ANSWER_BLOCKS.has(node.name)) {
120
+ current.firstBlockWords = wordCount(visibleText(node));
121
+ }
122
+ walk(node.children);
123
+ }
124
+ };
125
+ walk(body.children);
126
+ return questions;
127
+ };
128
+ /** A short selector for a `data-nosnippet` element: its tag plus its id or first class. */
129
+ const nosnippetSelector = (el) => {
130
+ const id = attr(el, "id")?.trim();
131
+ const firstClass = attr(el, "class")?.trim().split(/\s+/u)[0];
132
+ let { name } = el;
133
+ if (id) {
134
+ name += `#${id}`;
135
+ }
136
+ else if (firstClass) {
137
+ name += `.${firstClass}`;
138
+ }
139
+ return `${name.slice(0, 80)}[data-nosnippet]`;
140
+ };
141
+ const hasNosnippetAncestor = (el) => {
142
+ for (let p = el.parent; p && isTag(p); p = p.parent) {
143
+ if (Object.hasOwn(p.attribs, "data-nosnippet")) {
144
+ return true;
145
+ }
146
+ }
147
+ return false;
148
+ };
149
+ const emptyAppRoot = (body) => {
150
+ for (const selector of APP_ROOTS) {
151
+ const el = queryOne(body, selector);
152
+ if (el && wordCount(visibleText(el)) === 0) {
153
+ return selector;
154
+ }
155
+ }
156
+ return null;
157
+ };
158
+ const emptySignals = (robotsMeta) => ({
159
+ robotsMeta,
160
+ questions: [],
161
+ lists: 0,
162
+ tables: 0,
163
+ nosnippetElements: 0,
164
+ nosnippetWords: 0,
165
+ nosnippetSelectors: [],
166
+ emptyAppRoot: null,
167
+ noscriptAsksForJavaScript: false,
168
+ timeElements: 0,
169
+ mentionsDate: false,
170
+ });
171
+ /** Reads what the AEO rules need from a parsed document; `bodyText` is its visible body text. */
172
+ export const extractAnswerSignals = (doc, bodyText) => {
173
+ const robotsMeta = queryAll(doc, "meta[name]").flatMap((el) => {
174
+ const name = (attr(el, "name") ?? "").trim().toLowerCase();
175
+ const content = collapseWhitespace(attr(el, "content") ?? "").toLowerCase();
176
+ return ROBOTS_META_NAMES.has(name) && content ? [{ name, content }] : [];
177
+ });
178
+ const body = queryOne(doc, "body");
179
+ if (!body) {
180
+ return emptySignals(robotsMeta);
181
+ }
182
+ const nosnippet = queryAll(body, "[data-nosnippet]").filter((el) => !hasNosnippetAncestor(el));
183
+ return {
184
+ robotsMeta,
185
+ questions: findQuestions(body),
186
+ lists: queryAll(body, "ul, ol").filter((el) => childElements(el).filter((c) => c.name === "li").length >= 2 &&
187
+ !inChrome(el)).length,
188
+ tables: queryAll(body, "table").filter((el) => queryAll(el, "tr").length >= 2 && !inChrome(el)).length,
189
+ nosnippetElements: nosnippet.length,
190
+ nosnippetWords: nosnippet.reduce((sum, el) => sum + wordCount(visibleText(el)), 0),
191
+ nosnippetSelectors: nosnippet.slice(0, 10).map(nosnippetSelector),
192
+ emptyAppRoot: emptyAppRoot(body),
193
+ noscriptAsksForJavaScript: queryAll(body, "noscript").some((el) => /javascript/iu.test(text(el))),
194
+ timeElements: queryAll(body, "time").length,
195
+ mentionsDate: DATE_PATTERNS.some((re) => re.test(bodyText)),
196
+ };
197
+ };
198
+ //# sourceMappingURL=answer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer.js","sourceRoot":"","sources":["../../../src/core/html/answer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { AnyNode, Document, Element } from "domhandler";
2
+ export type { AnyNode, Document, Element } from "domhandler";
3
+ /** Elements whose content is never visible page text. */
4
+ export declare const HIDDEN_ELEMENTS: Set<string>;
5
+ /** The text a reader sees under `root`: hidden elements left out, whitespace collapsed. */
6
+ export declare const visibleText: (root: AnyNode) => string;
7
+ /** Parse a full HTML document with the WHATWG algorithm (parse5), scripting enabled like a browser. */
8
+ export declare const parseDocument: (html: string) => Document;
9
+ /** Descendants of `root` matching a CSS selector, in document order. */
10
+ export declare const queryAll: (root: AnyNode, selector: string) => Element[];
11
+ /** First descendant of `root` matching a CSS selector. */
12
+ export declare const queryOne: (root: AnyNode, selector: string) => Element | null;
13
+ export declare const attr: (el: Element, name: string) => string | undefined;
14
+ export declare const hasAttr: (el: Element, name: string) => boolean;
15
+ /** DOM `textContent`: concatenated text of all descendants, comments excluded. */
16
+ export declare const text: (node: AnyNode | AnyNode[]) => string;
17
+ export declare const childElements: (el: Element) => Element[];
@@ -0,0 +1,83 @@
1
+ import { selectAll, selectOne } from "css-select";
2
+ import { hasChildren, isTag, isText } from "domhandler";
3
+ import { textContent } from "domutils";
4
+ import { parse } from "parse5";
5
+ import { adapter } from "parse5-htmlparser2-tree-adapter";
6
+ import { collapseWhitespace } from "./text.js";
7
+ /** Elements whose content is never visible page text. */
8
+ export const HIDDEN_ELEMENTS = new Set([
9
+ "script",
10
+ "style",
11
+ "noscript",
12
+ "template",
13
+ "svg",
14
+ "iframe",
15
+ "head",
16
+ ]);
17
+ /** Elements that break words apart; padded with spaces so adjacent words don't fuse. */
18
+ const BLOCK_ELEMENTS = new Set([
19
+ "p",
20
+ "div",
21
+ "li",
22
+ "h1",
23
+ "h2",
24
+ "h3",
25
+ "h4",
26
+ "h5",
27
+ "h6",
28
+ "br",
29
+ "tr",
30
+ "td",
31
+ "th",
32
+ "section",
33
+ "article",
34
+ "header",
35
+ "footer",
36
+ "nav",
37
+ "blockquote",
38
+ "pre",
39
+ "dd",
40
+ "dt",
41
+ "figcaption",
42
+ ]);
43
+ /** The text a reader sees under `root`: hidden elements left out, whitespace collapsed. */
44
+ export const visibleText = (root) => {
45
+ const parts = [];
46
+ const walk = (nodes) => {
47
+ for (const node of nodes) {
48
+ if (isText(node)) {
49
+ parts.push(node.data);
50
+ }
51
+ else if (isTag(node)) {
52
+ if (HIDDEN_ELEMENTS.has(node.name)) {
53
+ continue;
54
+ }
55
+ const block = BLOCK_ELEMENTS.has(node.name);
56
+ if (block) {
57
+ parts.push(" ");
58
+ }
59
+ walk(node.children);
60
+ if (block) {
61
+ parts.push(" ");
62
+ }
63
+ }
64
+ else if (hasChildren(node)) {
65
+ walk(node.children);
66
+ }
67
+ }
68
+ };
69
+ walk(hasChildren(root) ? root.children : [root]);
70
+ return collapseWhitespace(parts.join(""));
71
+ };
72
+ /** Parse a full HTML document with the WHATWG algorithm (parse5), scripting enabled like a browser. */
73
+ export const parseDocument = (html) => parse(html, { scriptingEnabled: true, treeAdapter: adapter });
74
+ /** Descendants of `root` matching a CSS selector, in document order. */
75
+ export const queryAll = (root, selector) => selectAll(selector, root);
76
+ /** First descendant of `root` matching a CSS selector. */
77
+ export const queryOne = (root, selector) => selectOne(selector, root);
78
+ export const attr = (el, name) => Object.hasOwn(el.attribs, name) ? el.attribs[name] : undefined;
79
+ export const hasAttr = (el, name) => Object.hasOwn(el.attribs, name);
80
+ /** DOM `textContent`: concatenated text of all descendants, comments excluded. */
81
+ export const text = (node) => textContent(node);
82
+ export const childElements = (el) => el.children.filter(isTag);
83
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../../src/core/html/dom.ts"],"names":[],"mappings":"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"}