@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,207 @@
1
+ import { isTag, isText, hasChildren } from "domhandler";
2
+ import { isSameSite, resolveUrl, hostOf } from "../url.js";
3
+ import { attr, parseDocument, queryAll, queryOne, text } from "./dom.js";
4
+ import { extractStructuredData } from "./structured-data.js";
5
+ import { collapseWhitespace, wordCount } from "./text.js";
6
+ const relList = (rel) => (rel ?? "").toLowerCase().split(/\s+/u).filter(Boolean);
7
+ const attrOrNull = (value) => value?.trim() || null;
8
+ const firstNonEmpty = (values) => {
9
+ for (const v of values) {
10
+ if (v) {
11
+ return v;
12
+ }
13
+ }
14
+ return null;
15
+ };
16
+ const detectCharset = (doc) => {
17
+ const metaCharset = queryOne(doc, "meta[charset]");
18
+ const direct = metaCharset ? attr(metaCharset, "charset") : undefined;
19
+ if (direct) {
20
+ return direct.trim();
21
+ }
22
+ const httpEquiv = queryAll(doc, "meta[http-equiv]").find((el) => (attr(el, "http-equiv") ?? "").toLowerCase() === "content-type");
23
+ const content = httpEquiv ? attr(httpEquiv, "content") : undefined;
24
+ const match = content?.match(/charset=(?<charset>[\w-]+)/iu);
25
+ return match?.groups?.charset ?? null;
26
+ };
27
+ /** Elements whose content is never visible page text. */
28
+ const HIDDEN_ELEMENTS = new Set([
29
+ "script",
30
+ "style",
31
+ "noscript",
32
+ "template",
33
+ "svg",
34
+ "iframe",
35
+ "head",
36
+ ]);
37
+ /** Elements that break words apart; padded with spaces so adjacent words don't fuse. */
38
+ const BLOCK_ELEMENTS = new Set([
39
+ "p",
40
+ "div",
41
+ "li",
42
+ "h1",
43
+ "h2",
44
+ "h3",
45
+ "h4",
46
+ "h5",
47
+ "h6",
48
+ "br",
49
+ "tr",
50
+ "td",
51
+ "th",
52
+ "section",
53
+ "article",
54
+ "header",
55
+ "footer",
56
+ "nav",
57
+ "blockquote",
58
+ "pre",
59
+ "dd",
60
+ "dt",
61
+ "figcaption",
62
+ ]);
63
+ const visibleText = (doc) => {
64
+ const body = queryOne(doc, "body");
65
+ if (!body) {
66
+ return "";
67
+ }
68
+ const parts = [];
69
+ const walk = (nodes) => {
70
+ for (const node of nodes) {
71
+ if (isText(node)) {
72
+ parts.push(node.data);
73
+ }
74
+ else if (isTag(node)) {
75
+ if (HIDDEN_ELEMENTS.has(node.name)) {
76
+ continue;
77
+ }
78
+ const block = BLOCK_ELEMENTS.has(node.name);
79
+ if (block) {
80
+ parts.push(" ");
81
+ }
82
+ walk(node.children);
83
+ if (block) {
84
+ parts.push(" ");
85
+ }
86
+ }
87
+ else if (hasChildren(node)) {
88
+ walk(node.children);
89
+ }
90
+ }
91
+ };
92
+ walk(body.children);
93
+ return collapseWhitespace(parts.join(""));
94
+ };
95
+ export const extractPage = (html, options) => {
96
+ const doc = parseDocument(html);
97
+ const hasDoctype = /^\s*<!doctype\s+html/iu.test(html);
98
+ const baseEl = queryOne(doc, "base[href]");
99
+ const baseHref = baseEl ? attr(baseEl, "href") : undefined;
100
+ const base = (baseHref && resolveUrl(baseHref, options.url)) || options.url;
101
+ const siteHost = options.siteHost ?? hostOf(options.url) ?? "";
102
+ const titles = queryAll(doc, "title").map((el) => collapseWhitespace(text(el)));
103
+ const metaDescriptions = queryAll(doc, "meta[name]")
104
+ .filter((el) => (attr(el, "name") ?? "").trim().toLowerCase() === "description")
105
+ .map((el) => collapseWhitespace(attr(el, "content") ?? ""));
106
+ const metaRobots = queryAll(doc, "meta[name]")
107
+ .filter((el) => ["robots", "googlebot", "googlebot-news", "bingbot"].includes((attr(el, "name") ?? "").trim().toLowerCase()))
108
+ .map((el) => collapseWhitespace(attr(el, "content") ?? "").toLowerCase())
109
+ .filter(Boolean);
110
+ const canonicals = queryAll(doc, "link[rel]")
111
+ .filter((el) => relList(attr(el, "rel")).includes("canonical"))
112
+ .map((el) => (attr(el, "href") ?? "").trim());
113
+ const headings = queryAll(doc, "h1, h2, h3, h4, h5, h6").map((el) => ({
114
+ level: Number(el.name.slice(1)),
115
+ text: collapseWhitespace(text(el)),
116
+ }));
117
+ const og = {};
118
+ const twitter = {};
119
+ for (const el of queryAll(doc, "meta[property], meta[name]")) {
120
+ const key = (attr(el, "property") ?? attr(el, "name") ?? "")
121
+ .trim()
122
+ .toLowerCase();
123
+ const content = collapseWhitespace(attr(el, "content") ?? "");
124
+ if (!key) {
125
+ continue;
126
+ }
127
+ if (key.startsWith("og:") && !(key in og)) {
128
+ og[key.slice(3)] = content;
129
+ }
130
+ else if (key.startsWith("twitter:") && !(key in twitter)) {
131
+ twitter[key.slice(8)] = content;
132
+ }
133
+ }
134
+ const images = queryAll(doc, "img").map((el) => ({
135
+ src: attr(el, "src") ?? attr(el, "data-src") ?? null,
136
+ alt: attr(el, "alt") ?? null,
137
+ width: attr(el, "width") ?? null,
138
+ height: attr(el, "height") ?? null,
139
+ loading: attr(el, "loading") ?? null,
140
+ }));
141
+ const links = queryAll(doc, "a[href]").map((el) => {
142
+ const href = (attr(el, "href") ?? "").trim();
143
+ // Fragment-only and empty hrefs are not links to other documents.
144
+ const url = href === "" || href.startsWith("#") ? null : resolveUrl(href, base);
145
+ const rel = attr(el, "rel") ?? null;
146
+ const relTokens = relList(rel);
147
+ const linkText = collapseWhitespace(text(el));
148
+ const ariaLabel = collapseWhitespace(attr(el, "aria-label") ?? "");
149
+ const title = collapseWhitespace(attr(el, "title") ?? "");
150
+ const imgAlt = queryAll(el, "img[alt]")
151
+ .map((img) => collapseWhitespace(attr(img, "alt") ?? ""))
152
+ .find(Boolean);
153
+ return {
154
+ href,
155
+ url,
156
+ text: linkText,
157
+ rel,
158
+ internal: url
159
+ ? isSameSite(url, siteHost, options.includeSubdomains)
160
+ : false,
161
+ nofollow: relTokens.includes("nofollow"),
162
+ hasAccessibleName: Boolean(linkText || ariaLabel || title || imgAlt),
163
+ };
164
+ });
165
+ const hreflang = queryAll(doc, "link[rel]")
166
+ .filter((el) => relList(attr(el, "rel")).includes("alternate") && attr(el, "hreflang"))
167
+ .map((el) => {
168
+ const href = (attr(el, "href") ?? "").trim();
169
+ return {
170
+ lang: (attr(el, "hreflang") ?? "").trim(),
171
+ href,
172
+ url: resolveUrl(href, base),
173
+ };
174
+ });
175
+ const structured = extractStructuredData(doc);
176
+ const bodyText = visibleText(doc);
177
+ const htmlEl = queryOne(doc, "html");
178
+ const viewportEl = queryOne(doc, 'meta[name="viewport" i]');
179
+ return {
180
+ url: options.url,
181
+ hasDoctype,
182
+ lang: attrOrNull(htmlEl ? attr(htmlEl, "lang") : undefined),
183
+ charset: detectCharset(doc),
184
+ viewport: attrOrNull(viewportEl ? attr(viewportEl, "content") : undefined),
185
+ titles,
186
+ title: firstNonEmpty(titles),
187
+ metaDescriptions,
188
+ metaDescription: firstNonEmpty(metaDescriptions),
189
+ metaRobots,
190
+ canonicals,
191
+ canonical: canonicals[0] ? resolveUrl(canonicals[0], base) : null,
192
+ headings,
193
+ h1s: headings.flatMap((h) => (h.level === 1 ? [h.text] : [])),
194
+ og,
195
+ twitter,
196
+ images,
197
+ links,
198
+ hreflang,
199
+ structuredData: structured.items,
200
+ structuredDataErrors: structured.errors,
201
+ text: bodyText,
202
+ wordCount: wordCount(bodyText),
203
+ htmlBytes: Buffer.byteLength(html, "utf-8"),
204
+ hasBaseHref: Boolean(baseHref),
205
+ };
206
+ };
207
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/core/html/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { StructuredDataError, StructuredDataItem } from "../types.js";
2
+ import type { Document } from "./dom.js";
3
+ /** A value JSON.parse returns: all that a JSON-LD block or a microdata item can hold. */
4
+ export type JsonValue = string | number | boolean | null | JsonValue[] | JsonObject;
5
+ /** A JSON-LD node or a microdata item: whatever properties the page gave it. */
6
+ export interface JsonObject {
7
+ [property: string]: JsonValue;
8
+ }
9
+ /** Whether a structured-data value is a string. */
10
+ export declare const isString: (value: JsonValue | undefined) => value is string;
11
+ /** Whether a structured-data value is an object with properties, rather than a list or a scalar. */
12
+ export declare const isJsonObject: (value: JsonValue | undefined) => value is JsonObject;
13
+ export interface StructuredDataExtraction {
14
+ items: StructuredDataItem[];
15
+ errors: StructuredDataError[];
16
+ }
17
+ /** `https://schema.org/Article` -> `Article`, `schema:Article` -> `Article`. */
18
+ export declare const shortType: (type: string) => string;
19
+ export declare const contextString: (value: JsonValue | undefined) => string | null;
20
+ export declare const typesOf: (value: JsonValue | undefined) => string[];
21
+ export declare const extractStructuredData: (doc: Document) => StructuredDataExtraction;
@@ -0,0 +1,220 @@
1
+ import { isTag } from "domhandler";
2
+ import { attr, childElements, hasAttr, queryAll, text } from "./dom.js";
3
+ import { collapseWhitespace } from "./text.js";
4
+ /** Whether a structured-data value is a string. */
5
+ export const isString = (value) => typeof value === "string";
6
+ /** Whether a structured-data value is an object with properties, rather than a list or a scalar. */
7
+ export const isJsonObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
8
+ const stripCdata = (source) => source
9
+ .replace(/^\s*<!--/u, "")
10
+ .replace(/-->\s*$/u, "")
11
+ .replace(/^\s*\/\*\s*<!\[CDATA\[\s*\*\//u, "")
12
+ .replace(/\/\*\s*\]\]>\s*\*\/\s*$/u, "")
13
+ .replace(/^\s*<!\[CDATA\[/u, "")
14
+ .replace(/\]\]>\s*$/u, "");
15
+ /** `https://schema.org/Article` -> `Article`, `schema:Article` -> `Article`. */
16
+ export const shortType = (type) => {
17
+ const t = type.trim();
18
+ const slash = t.lastIndexOf("/");
19
+ const colon = t.lastIndexOf(":");
20
+ const cut = Math.max(slash, t.startsWith("http") ? -1 : colon);
21
+ return cut >= 0 ? t.slice(cut + 1) : t;
22
+ };
23
+ export const contextString = (value) => {
24
+ if (isString(value)) {
25
+ return value;
26
+ }
27
+ if (Array.isArray(value)) {
28
+ for (const v of value) {
29
+ const s = contextString(v);
30
+ if (s) {
31
+ return s;
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+ if (isJsonObject(value)) {
37
+ const vocab = value["@vocab"];
38
+ if (isString(vocab)) {
39
+ return vocab;
40
+ }
41
+ }
42
+ return null;
43
+ };
44
+ export const typesOf = (value) => {
45
+ if (isString(value)) {
46
+ return [shortType(value)];
47
+ }
48
+ if (Array.isArray(value)) {
49
+ return value.filter(isString).map(shortType);
50
+ }
51
+ return [];
52
+ };
53
+ const flattenJsonLd = (node, inheritedContext) => {
54
+ if (Array.isArray(node)) {
55
+ return node.flatMap((n) => flattenJsonLd(n, inheritedContext));
56
+ }
57
+ if (!isJsonObject(node)) {
58
+ return [];
59
+ }
60
+ const context = contextString(node["@context"]) ?? inheritedContext;
61
+ const graph = node["@graph"];
62
+ if (Array.isArray(graph)) {
63
+ return graph.flatMap((n) => flattenJsonLd(n, context));
64
+ }
65
+ return [
66
+ { source: "json-ld", types: typesOf(node["@type"]), context, data: node },
67
+ ];
68
+ };
69
+ /* ---------------------------------------------------------------------------
70
+ * Microdata
71
+ * ------------------------------------------------------------------------- */
72
+ /** Elements whose microdata value is always one attribute, empty when it is missing. */
73
+ const URL_VALUE_ATTRIBUTES = new Map([
74
+ ["meta", "content"],
75
+ ["img", "src"],
76
+ ["audio", "src"],
77
+ ["embed", "src"],
78
+ ["iframe", "src"],
79
+ ["source", "src"],
80
+ ["track", "src"],
81
+ ["video", "src"],
82
+ ["a", "href"],
83
+ ["area", "href"],
84
+ ["link", "href"],
85
+ ["object", "data"],
86
+ ]);
87
+ /** Elements whose microdata value is an attribute other than `content`, or else their text. */
88
+ const TEXT_VALUE_ATTRIBUTES = new Map([
89
+ ["data", "value"],
90
+ ["meter", "value"],
91
+ ["time", "datetime"],
92
+ ]);
93
+ const microdataValue = (el) => {
94
+ const tag = el.name.toLowerCase();
95
+ const urlAttribute = URL_VALUE_ATTRIBUTES.get(tag);
96
+ if (urlAttribute) {
97
+ return attr(el, urlAttribute) ?? "";
98
+ }
99
+ return (attr(el, TEXT_VALUE_ATTRIBUTES.get(tag) ?? "content") ??
100
+ collapseWhitespace(text(el)));
101
+ };
102
+ /** Walk descendants, stopping at nested itemscopes (they are added as a single property). */
103
+ const collectProps = (root, out) => {
104
+ for (const child of childElements(root)) {
105
+ if (hasAttr(child, "itemprop")) {
106
+ out.push(child);
107
+ }
108
+ if (!hasAttr(child, "itemscope")) {
109
+ collectProps(child, out);
110
+ }
111
+ }
112
+ };
113
+ /** Whether an element sits inside another itemscope, which makes it a property, not an item. */
114
+ const hasItemscopeAncestor = (el) => {
115
+ for (let p = el.parent; p && isTag(p); p = p.parent) {
116
+ if (hasAttr(p, "itemscope")) {
117
+ return true;
118
+ }
119
+ }
120
+ return false;
121
+ };
122
+ const microdataObject = (scope) => {
123
+ const obj = {};
124
+ const itemtype = attr(scope, "itemtype");
125
+ if (itemtype) {
126
+ const types = itemtype.split(/\s+/u).filter(Boolean);
127
+ const [first] = types;
128
+ obj["@type"] = types.length === 1 && first ? first : types;
129
+ if (first) {
130
+ try {
131
+ const u = new URL(first);
132
+ obj["@context"] = `${u.protocol}//${u.host}`;
133
+ }
134
+ catch {
135
+ /* not a URL */
136
+ }
137
+ }
138
+ }
139
+ const itemid = attr(scope, "itemid");
140
+ if (itemid) {
141
+ obj["@id"] = itemid;
142
+ }
143
+ const props = [];
144
+ collectProps(scope, props);
145
+ for (const el of props) {
146
+ const names = (attr(el, "itemprop") ?? "").split(/\s+/u).filter(Boolean);
147
+ const value = hasAttr(el, "itemscope")
148
+ ? microdataObject(el)
149
+ : microdataValue(el);
150
+ for (const name of names) {
151
+ const existing = obj[name];
152
+ if (existing === undefined) {
153
+ obj[name] = value;
154
+ }
155
+ else if (Array.isArray(existing)) {
156
+ existing.push(value);
157
+ }
158
+ else {
159
+ obj[name] = [existing, value];
160
+ }
161
+ }
162
+ }
163
+ return obj;
164
+ };
165
+ const extractMicrodata = (doc) => {
166
+ const items = [];
167
+ for (const el of queryAll(doc, "[itemscope]")) {
168
+ // Top-level scopes only: nested scopes become property values.
169
+ if (hasItemscopeAncestor(el)) {
170
+ continue;
171
+ }
172
+ const data = microdataObject(el);
173
+ items.push({
174
+ source: "microdata",
175
+ types: typesOf(data["@type"]),
176
+ context: contextString(data["@context"]),
177
+ data,
178
+ });
179
+ }
180
+ return items;
181
+ };
182
+ export const extractStructuredData = (doc) => {
183
+ const items = [];
184
+ const errors = [];
185
+ for (const el of queryAll(doc, "script[type]")) {
186
+ const type = (attr(el, "type") ?? "").trim().toLowerCase();
187
+ if (type !== "application/ld+json") {
188
+ continue;
189
+ }
190
+ const raw = text(el).trim();
191
+ if (!raw) {
192
+ errors.push({
193
+ source: "json-ld",
194
+ message: "Empty JSON-LD script block",
195
+ snippet: "",
196
+ });
197
+ continue;
198
+ }
199
+ let parsed;
200
+ try {
201
+ parsed = JSON.parse(stripCdata(raw));
202
+ }
203
+ catch (error) {
204
+ errors.push({
205
+ source: "json-ld",
206
+ message: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
207
+ snippet: raw.slice(0, 200),
208
+ });
209
+ continue;
210
+ }
211
+ for (const node of flattenJsonLd(parsed, null)) {
212
+ items.push(node);
213
+ }
214
+ }
215
+ for (const item of extractMicrodata(doc)) {
216
+ items.push(item);
217
+ }
218
+ return { items, errors };
219
+ };
220
+ //# sourceMappingURL=structured-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-data.js","sourceRoot":"","sources":["../../../src/core/html/structured-data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** Text analysis helpers: word counts, readability, placeholder detection. */
2
+ export declare const words: (text: string) => string[];
3
+ export declare const wordCount: (text: string) => number;
4
+ export declare const collapseWhitespace: (text: string) => string;
5
+ export declare const sentenceCount: (text: string) => number;
6
+ /** English syllable heuristic: vowel groups minus silent trailing e, min 1. */
7
+ export declare const syllables: (word: string) => number;
8
+ /**
9
+ * Flesch Reading Ease (English formula). Returns null when there is not enough text.
10
+ * 90-100 very easy, 60-70 plain English, 30-50 difficult, <30 very difficult.
11
+ */
12
+ export declare const fleschReadingEase: (text: string) => number | null;
13
+ export declare const hasPlaceholderText: (text: string) => boolean;
14
+ export declare const GENERIC_TITLES: Set<string>;
15
+ export declare const isGenericTitle: (title: string) => boolean;
16
+ export declare const GENERIC_ANCHORS: Set<string>;
17
+ export declare const isGenericAnchor: (text: string) => boolean;
18
+ export declare const normalizeForComparison: (text: string) => string;
19
+ export declare const includesKeyword: (haystack: string | null | undefined, keyword: string) => boolean;
20
+ export declare const keywordOccurrences: (text: string, keyword: string) => number;
21
+ /** 5-word shingles for near-duplicate detection. */
22
+ export declare const shingles: (text: string, size?: number) => Set<string>;
23
+ export declare const jaccard: (a: Set<string>, b: Set<string>) => number;
@@ -0,0 +1,152 @@
1
+ /** Text analysis helpers: word counts, readability, placeholder detection. */
2
+ const WORD_RE = /[\p{L}\p{N}][\p{L}\p{N}'’-]*/gu;
3
+ export const words = (text) => text.match(WORD_RE) ?? [];
4
+ export const wordCount = (text) => words(text).length;
5
+ export const collapseWhitespace = (text) => text.replaceAll(/\s+/gu, " ").trim();
6
+ export const sentenceCount = (text) => {
7
+ const parts = text
8
+ .split(/[.!?]+(?:\s+|$)/u)
9
+ .filter((s) => s.trim().length > 0);
10
+ return Math.max(1, parts.length);
11
+ };
12
+ /** English syllable heuristic: vowel groups minus silent trailing e, min 1. */
13
+ export const syllables = (word) => {
14
+ const w = word.toLowerCase().replaceAll(/[^a-z]/gu, "");
15
+ if (!w) {
16
+ return 0;
17
+ }
18
+ if (w.length <= 3) {
19
+ return 1;
20
+ }
21
+ let count = (w.replace(/e$/u, "").match(/[aeiouy]+/gu) ?? []).length;
22
+ if (w.endsWith("le") && !/[aeiouy]le$/u.test(w)) {
23
+ count += 1;
24
+ }
25
+ return Math.max(1, count);
26
+ };
27
+ /**
28
+ * Flesch Reading Ease (English formula). Returns null when there is not enough text.
29
+ * 90-100 very easy, 60-70 plain English, 30-50 difficult, <30 very difficult.
30
+ */
31
+ export const fleschReadingEase = (text) => {
32
+ const ws = words(text);
33
+ if (ws.length < 100) {
34
+ return null;
35
+ }
36
+ const sentences = sentenceCount(text);
37
+ const syl = ws.reduce((sum, w) => sum + syllables(w), 0);
38
+ const score = 206.835 - 1.015 * (ws.length / sentences) - 84.6 * (syl / ws.length);
39
+ return Math.round(score * 10) / 10;
40
+ };
41
+ const PLACEHOLDER_RE = /lorem ipsum|dolor sit amet|consectetur adipiscing/iu;
42
+ export const hasPlaceholderText = (text) => PLACEHOLDER_RE.test(text);
43
+ export const GENERIC_TITLES = new Set([
44
+ "home",
45
+ "homepage",
46
+ "index",
47
+ "untitled",
48
+ "untitled page",
49
+ "untitled document",
50
+ "document",
51
+ "new page",
52
+ "page",
53
+ "title",
54
+ "welcome",
55
+ "react app",
56
+ "vite app",
57
+ "vite + react",
58
+ "vite + react + ts",
59
+ "vite + vue",
60
+ "vite + svelte",
61
+ "create next app",
62
+ "next app",
63
+ "nuxt app",
64
+ "nuxt",
65
+ "astro",
66
+ "my app",
67
+ "my site",
68
+ "my website",
69
+ "website",
70
+ "default",
71
+ "test",
72
+ "hello world",
73
+ "svelte app",
74
+ "angular app",
75
+ "gatsby default starter",
76
+ "remix app",
77
+ "tanstack start starter",
78
+ "example",
79
+ ]);
80
+ export const isGenericTitle = (title) => GENERIC_TITLES.has(collapseWhitespace(title).toLowerCase());
81
+ export const GENERIC_ANCHORS = new Set([
82
+ "click here",
83
+ "here",
84
+ "read more",
85
+ "learn more",
86
+ "more",
87
+ "link",
88
+ "this",
89
+ "this page",
90
+ "continue",
91
+ "continue reading",
92
+ "details",
93
+ "more info",
94
+ "info",
95
+ "go",
96
+ "page",
97
+ "article",
98
+ "website",
99
+ "url",
100
+ "www",
101
+ ]);
102
+ export const isGenericAnchor = (text) => {
103
+ const t = collapseWhitespace(text)
104
+ .toLowerCase()
105
+ .replace(/[.!…»›>]+$/u, "")
106
+ .trim();
107
+ return GENERIC_ANCHORS.has(t);
108
+ };
109
+ export const normalizeForComparison = (text) => collapseWhitespace(text).toLowerCase();
110
+ export const includesKeyword = (haystack, keyword) => {
111
+ if (!haystack) {
112
+ return false;
113
+ }
114
+ return normalizeForComparison(haystack).includes(normalizeForComparison(keyword));
115
+ };
116
+ export const keywordOccurrences = (text, keyword) => {
117
+ const hay = normalizeForComparison(text);
118
+ const needle = normalizeForComparison(keyword);
119
+ if (!needle) {
120
+ return 0;
121
+ }
122
+ let count = 0;
123
+ let idx = hay.indexOf(needle);
124
+ while (idx !== -1) {
125
+ count += 1;
126
+ idx = hay.indexOf(needle, idx + needle.length);
127
+ }
128
+ return count;
129
+ };
130
+ /** 5-word shingles for near-duplicate detection. */
131
+ export const shingles = (text, size = 5) => {
132
+ const ws = words(normalizeForComparison(text));
133
+ const out = new Set();
134
+ for (let i = 0; i + size <= ws.length; i += 1) {
135
+ out.add(ws.slice(i, i + size).join(" "));
136
+ }
137
+ return out;
138
+ };
139
+ export const jaccard = (a, b) => {
140
+ if (a.size === 0 && b.size === 0) {
141
+ return 1;
142
+ }
143
+ let inter = 0;
144
+ const [small, large] = a.size < b.size ? [a, b] : [b, a];
145
+ for (const s of small) {
146
+ if (large.has(s)) {
147
+ inter += 1;
148
+ }
149
+ }
150
+ return inter / (a.size + b.size - inter);
151
+ };
152
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/core/html/text.ts"],"names":[],"mappings":"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"}