mcp-web-validator 1.3.16 → 1.3.18
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/CHANGELOG.md +14 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +34 -9
- package/dist/index.js.map +1 -1
- package/dist/presentation.d.ts +5 -1
- package/dist/presentation.js +18 -9
- package/dist/presentation.js.map +1 -1
- package/dist/report.d.ts +9 -0
- package/dist/report.js +13 -4
- package/dist/report.js.map +1 -1
- package/dist/seo-auditor.js +16 -1
- package/dist/seo-auditor.js.map +1 -1
- package/dist/w3c-validator.d.ts +9 -3
- package/dist/w3c-validator.js +18 -8
- package/dist/w3c-validator.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to this project are documented here. The project follows [Se
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.3.18] - 2026-09-22
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Treated Nu HTML Checker `non-document-error` diagnostics as errors instead of informational messages in local validation, report scoring, and narration, matching the validator's documented JSON semantics and hosted behavior.
|
|
12
|
+
- Preserved full Nu HTML diagnostic totals and severity counts when local/package output is capped at 200 messages, with explicit truncation metadata/disclosure so summaries and heuristic scoring no longer undercount large result sets.
|
|
13
|
+
|
|
14
|
+
## [1.3.17] - 2026-09-22
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Kept known upstream CSS validator limitations separate from trusted CSS error counts in validation-report summaries while preserving the raw diagnostic and compatibility-limited score withholding.
|
|
19
|
+
- Rejected scalar and scalar-array top-level values in JSON-LD syntax checks so valid JSON that is not a JSON-LD document no longer passes cleanly.
|
|
20
|
+
|
|
7
21
|
## [1.3.16] - 2026-09-22
|
|
8
22
|
|
|
9
23
|
### Fixed
|
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,12 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { auditSeoMetadata, checkBrokenLinks, validateSchemaMarkup } from "./seo-auditor.js";
|
|
4
4
|
import { type ValidationReport } from "./report.js";
|
|
5
5
|
import { readTextFile } from "./network.js";
|
|
6
|
-
import { validateCssContent, validateHtmlContent } from "./w3c-validator.js";
|
|
6
|
+
import { validateCssContent, validateHtmlContent, validateHtmlContentDetailed } from "./w3c-validator.js";
|
|
7
7
|
export declare const SERVER_VERSION: string;
|
|
8
8
|
interface ValidationReportDependencies {
|
|
9
9
|
readTextFile: typeof readTextFile;
|
|
10
10
|
validateHtmlContent: typeof validateHtmlContent;
|
|
11
|
+
validateHtmlContentDetailed?: typeof validateHtmlContentDetailed;
|
|
11
12
|
validateCssContent: typeof validateCssContent;
|
|
12
13
|
auditSeoMetadata: typeof auditSeoMetadata;
|
|
13
14
|
validateSchemaMarkup: typeof validateSchemaMarkup;
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { createValidationReport, validationReportChecks, } from "./report.js";
|
|
|
10
10
|
import { fetchPublicText, getErrorMessage, readTextFile } from "./network.js";
|
|
11
11
|
import { cssValidationContent, failureContent, htmlValidationContent, linkCheckContent, reportContent, schemaValidationContent, screenshotCaptureContent, seoAuditContent, } from "./presentation.js";
|
|
12
12
|
import { PACKAGE_VERSION } from "./version.js";
|
|
13
|
-
import { MAX_CSS_VALIDATION_BYTES, validateCssContent, validateHtmlContent, } from "./w3c-validator.js";
|
|
13
|
+
import { getW3CMessageSeverity, MAX_CSS_VALIDATION_BYTES, validateCssContent, validateHtmlContent, validateHtmlContentDetailed, } from "./w3c-validator.js";
|
|
14
14
|
export const SERVER_VERSION = PACKAGE_VERSION;
|
|
15
15
|
const HTML_MAX_BYTES = 2_000_000;
|
|
16
16
|
const CSS_MAX_BYTES = MAX_CSS_VALIDATION_BYTES;
|
|
@@ -122,6 +122,8 @@ function failedReport(filePath, error) {
|
|
|
122
122
|
brokenLinks: 0,
|
|
123
123
|
},
|
|
124
124
|
htmlMessages: [],
|
|
125
|
+
htmlTotalMessages: 0,
|
|
126
|
+
htmlTruncated: false,
|
|
125
127
|
cssMessages: [],
|
|
126
128
|
seoIssues: [],
|
|
127
129
|
schemaIssues: [],
|
|
@@ -133,6 +135,7 @@ function failedReport(filePath, error) {
|
|
|
133
135
|
const validationReportDependencies = {
|
|
134
136
|
readTextFile,
|
|
135
137
|
validateHtmlContent,
|
|
138
|
+
validateHtmlContentDetailed,
|
|
136
139
|
validateCssContent,
|
|
137
140
|
auditSeoMetadata,
|
|
138
141
|
validateSchemaMarkup,
|
|
@@ -158,8 +161,16 @@ export async function generateValidationReport({ htmlFilePath, cssFilePath, base
|
|
|
158
161
|
recordFailure("css", "CSS validation", cause);
|
|
159
162
|
}
|
|
160
163
|
}
|
|
164
|
+
const htmlValidation = dependencies.validateHtmlContentDetailed
|
|
165
|
+
? dependencies.validateHtmlContentDetailed(html)
|
|
166
|
+
: dependencies.validateHtmlContent(html).then((messages) => {
|
|
167
|
+
const counts = { error: 0, warning: 0, info: 0 };
|
|
168
|
+
for (const message of messages)
|
|
169
|
+
counts[getW3CMessageSeverity(message)] += 1;
|
|
170
|
+
return { messages, total: messages.length, truncated: false, counts };
|
|
171
|
+
});
|
|
161
172
|
const [htmlResult, cssResult, seoResult, schemaResult, linksResult] = await Promise.allSettled([
|
|
162
|
-
|
|
173
|
+
htmlValidation,
|
|
163
174
|
css === undefined ? Promise.resolve([]) : dependencies.validateCssContent(css),
|
|
164
175
|
Promise.resolve().then(() => dependencies.auditSeoMetadata(html)),
|
|
165
176
|
Promise.resolve().then(() => dependencies.validateSchemaMarkup(html)),
|
|
@@ -178,7 +189,10 @@ export async function generateValidationReport({ htmlFilePath, cssFilePath, base
|
|
|
178
189
|
return createValidationReport({
|
|
179
190
|
htmlFilePath,
|
|
180
191
|
cssAudited: css !== undefined,
|
|
181
|
-
htmlMessages: htmlResult.status === "fulfilled" ? htmlResult.value : [],
|
|
192
|
+
htmlMessages: htmlResult.status === "fulfilled" ? htmlResult.value.messages : [],
|
|
193
|
+
htmlTotalMessages: htmlResult.status === "fulfilled" ? htmlResult.value.total : 0,
|
|
194
|
+
htmlTruncated: htmlResult.status === "fulfilled" ? htmlResult.value.truncated : false,
|
|
195
|
+
htmlCounts: htmlResult.status === "fulfilled" ? htmlResult.value.counts : undefined,
|
|
182
196
|
cssMessages: cssResult.status === "fulfilled" ? cssResult.value : [],
|
|
183
197
|
seoIssues: seoResult.status === "fulfilled" ? seoResult.value.slice(0, 200) : [],
|
|
184
198
|
schemaIssues: schemaResult.status === "fulfilled" ? schemaResult.value.slice(0, 200) : [],
|
|
@@ -199,18 +213,20 @@ export function createServer() {
|
|
|
199
213
|
},
|
|
200
214
|
outputSchema: {
|
|
201
215
|
errors: z.array(w3cMessageSchema),
|
|
216
|
+
totalMessages: z.number().int().nonnegative(),
|
|
217
|
+
truncated: z.boolean(),
|
|
202
218
|
error: z.string().optional(),
|
|
203
219
|
},
|
|
204
220
|
annotations: externalReadOnlyAnnotations,
|
|
205
221
|
}, async ({ filePath }) => {
|
|
206
222
|
try {
|
|
207
223
|
const html = await readTextFile(filePath, HTML_MAX_BYTES);
|
|
208
|
-
const
|
|
209
|
-
return result({ errors }, htmlValidationContent(
|
|
224
|
+
const validation = await validateHtmlContentDetailed(html);
|
|
225
|
+
return result({ errors: validation.messages, totalMessages: validation.total, truncated: validation.truncated }, htmlValidationContent(validation.messages, undefined, validation.total, validation.counts));
|
|
210
226
|
}
|
|
211
227
|
catch (cause) {
|
|
212
228
|
const error = getErrorMessage(cause);
|
|
213
|
-
return result({ errors: [], error }, failureContent("HTML validation", error, "Confirm the file exists and is readable, then retry. If the W3C service is unavailable, try again later."), true);
|
|
229
|
+
return result({ errors: [], totalMessages: 0, truncated: false, error }, failureContent("HTML validation", error, "Confirm the file exists and is readable, then retry. If the W3C service is unavailable, try again later."), true);
|
|
214
230
|
}
|
|
215
231
|
});
|
|
216
232
|
server.registerTool("html.url", {
|
|
@@ -221,6 +237,8 @@ export function createServer() {
|
|
|
221
237
|
},
|
|
222
238
|
outputSchema: {
|
|
223
239
|
errors: z.array(w3cMessageSchema),
|
|
240
|
+
totalMessages: z.number().int().nonnegative(),
|
|
241
|
+
truncated: z.boolean(),
|
|
224
242
|
fetchedUrl: z.string().optional(),
|
|
225
243
|
error: z.string().optional(),
|
|
226
244
|
},
|
|
@@ -240,12 +258,17 @@ export function createServer() {
|
|
|
240
258
|
if (fetched.status < 200 || fetched.status >= 300) {
|
|
241
259
|
throw new Error(`Target URL returned HTTP ${fetched.status}.`);
|
|
242
260
|
}
|
|
243
|
-
const
|
|
244
|
-
return result({
|
|
261
|
+
const validation = await validateHtmlContentDetailed(fetched.text);
|
|
262
|
+
return result({
|
|
263
|
+
errors: validation.messages,
|
|
264
|
+
totalMessages: validation.total,
|
|
265
|
+
truncated: validation.truncated,
|
|
266
|
+
fetchedUrl: fetched.url,
|
|
267
|
+
}, htmlValidationContent(validation.messages, fetched.url, validation.total, validation.counts));
|
|
245
268
|
}
|
|
246
269
|
catch (cause) {
|
|
247
270
|
const error = getErrorMessage(cause);
|
|
248
|
-
return result({ errors: [], error }, failureContent("URL validation", error, "Confirm the URL is public, uses HTTP or HTTPS on a standard port, and returns HTML, then retry."), true);
|
|
271
|
+
return result({ errors: [], totalMessages: 0, truncated: false, error }, failureContent("URL validation", error, "Confirm the URL is public, uses HTTP or HTTPS on a standard port, and returns HTML, then retry."), true);
|
|
249
272
|
}
|
|
250
273
|
});
|
|
251
274
|
server.registerTool("css.local", {
|
|
@@ -363,6 +386,8 @@ export function createServer() {
|
|
|
363
386
|
report: z.string(),
|
|
364
387
|
summary: reportSummarySchema,
|
|
365
388
|
htmlMessages: z.array(w3cMessageSchema),
|
|
389
|
+
htmlTotalMessages: z.number().int().nonnegative(),
|
|
390
|
+
htmlTruncated: z.boolean(),
|
|
366
391
|
cssMessages: z.array(cssMessageSchema),
|
|
367
392
|
seoIssues: z.array(seoIssueSchema),
|
|
368
393
|
schemaIssues: z.array(seoIssueSchema),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { captureScreenshots } from \"./screenshot.js\";\nimport {\n auditSeoMetadata,\n auditSeoMetadataDetailed,\n checkBrokenLinks,\n validateSchemaMarkup,\n validateSchemaMarkupDetailed,\n} from \"./seo-auditor.js\";\nimport {\n createValidationReport,\n type ValidationReport,\n type ValidationReportCheck,\n validationReportChecks,\n} from \"./report.js\";\nimport { fetchPublicText, getErrorMessage, readTextFile } from \"./network.js\";\nimport {\n cssValidationContent,\n failureContent,\n htmlValidationContent,\n linkCheckContent,\n reportContent,\n schemaValidationContent,\n screenshotCaptureContent,\n seoAuditContent,\n} from \"./presentation.js\";\nimport { PACKAGE_VERSION } from \"./version.js\";\nimport {\n MAX_CSS_VALIDATION_BYTES,\n validateCssContent,\n validateHtmlContent,\n} from \"./w3c-validator.js\";\n\nexport const SERVER_VERSION = PACKAGE_VERSION;\n\nconst HTML_MAX_BYTES = 2_000_000;\nconst CSS_MAX_BYTES = MAX_CSS_VALIDATION_BYTES;\nconst PATH_MAX_LENGTH = 4_096;\nconst MAX_VIEWPORTS = 8;\n\nconst filePathSchema = z.string().trim().min(1).max(PATH_MAX_LENGTH);\nconst htmlContentSchema = z.string().min(1).max(HTML_MAX_BYTES);\nconst publicUrlSchema = z\n .string()\n .url()\n .refine((value) => {\n const protocol = new URL(value).protocol;\n return protocol === \"http:\" || protocol === \"https:\";\n }, \"URL must use HTTP or HTTPS.\");\n\nconst w3cMessageSchema = z.object({\n type: z.string(),\n subType: z.string().optional(),\n message: z.string(),\n lastLine: z.number().int().optional(),\n lastColumn: z.number().int().optional(),\n firstLine: z.number().int().optional(),\n firstColumn: z.number().int().optional(),\n extract: z.string().optional(),\n});\n\nconst cssMessageSchema = z.object({\n line: z.number().int(),\n type: z.string(),\n message: z.string(),\n context: z.string().optional(),\n compatibility: z.literal(\"known-validator-limitation\").optional(),\n});\n\nconst seoIssueSchema = z.object({\n code: z.string().describe(\"Stable machine-readable audit rule identifier.\"),\n severity: z.enum([\"error\", \"warning\", \"info\"]),\n category: z.enum([\"SEO\", \"Schema\", \"BrokenLinks\", \"Accessibility\"]),\n message: z.string(),\n element: z.string().optional(),\n});\n\nconst linkStatusSchema = z.object({\n url: z.string(),\n status: z.union([z.number().int(), z.enum([\"blocked\", \"failed\"])]),\n ok: z.boolean(),\n message: z.string().optional(),\n});\n\nconst screenshotSchema = z.object({\n viewportName: z.string(),\n width: z.number().int(),\n height: z.number().int(),\n outputPath: z.string(),\n});\n\nconst reportSummarySchema = z.object({\n overallScore: z.number().int().min(0).max(100).nullable(),\n htmlScore: z.number().int().min(0).max(100).nullable(),\n cssScore: z.number().int().min(0).max(100).nullable(),\n seoScore: z.number().int().min(0).max(100).nullable(),\n linkScore: z.number().int().min(0).max(100).nullable(),\n htmlErrors: z.number().int().nonnegative(),\n htmlWarnings: z.number().int().nonnegative(),\n cssErrors: z.number().int().nonnegative(),\n cssCompatibilityLimitations: z.number().int().nonnegative(),\n seoErrors: z.number().int().nonnegative(),\n seoWarnings: z.number().int().nonnegative(),\n schemaErrors: z.number().int().nonnegative(),\n linksChecked: z.number().int().nonnegative(),\n brokenLinks: z.number().int().nonnegative(),\n});\n\nconst externalReadOnlyAnnotations = {\n // These calls contact external recipients but do not modify external state.\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n} as const;\n\nconst localReadOnlyAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n} as const;\n\nfunction result<T extends object>(\n structuredContent: T,\n content: string,\n isError = false,\n): CallToolResult {\n return {\n structuredContent: structuredContent as Record<string, unknown>,\n content: [\n {\n type: \"text\",\n text: content,\n },\n ],\n ...(isError ? { isError: true } : {}),\n };\n}\n\nfunction failedReport(filePath: string, error: string): ValidationReport {\n return {\n report: `Validation report could not be generated: ${error}`,\n summary: {\n overallScore: null,\n htmlScore: null,\n cssScore: null,\n seoScore: null,\n linkScore: null,\n htmlErrors: 0,\n htmlWarnings: 0,\n cssErrors: 0,\n cssCompatibilityLimitations: 0,\n seoErrors: 0,\n seoWarnings: 0,\n schemaErrors: 0,\n linksChecked: 0,\n brokenLinks: 0,\n },\n htmlMessages: [],\n cssMessages: [],\n seoIssues: [],\n schemaIssues: [],\n links: [],\n failedChecks: [\"input\"],\n errors: [`${path.basename(filePath || \"document\")}: ${error}`],\n };\n}\n\ninterface ValidationReportDependencies {\n readTextFile: typeof readTextFile;\n validateHtmlContent: typeof validateHtmlContent;\n validateCssContent: typeof validateCssContent;\n auditSeoMetadata: typeof auditSeoMetadata;\n validateSchemaMarkup: typeof validateSchemaMarkup;\n checkBrokenLinks: typeof checkBrokenLinks;\n}\n\nconst validationReportDependencies: ValidationReportDependencies = {\n readTextFile,\n validateHtmlContent,\n validateCssContent,\n auditSeoMetadata,\n validateSchemaMarkup,\n checkBrokenLinks,\n};\n\nexport interface GenerateValidationReportOptions {\n htmlFilePath: string;\n cssFilePath?: string;\n baseUrl?: string;\n}\n\n/** Runs independent validation checks without discarding successful results when another check fails. */\nexport async function generateValidationReport(\n { htmlFilePath, cssFilePath, baseUrl }: GenerateValidationReportOptions,\n dependencies: ValidationReportDependencies = validationReportDependencies,\n): Promise<ValidationReport> {\n const html = await dependencies.readTextFile(htmlFilePath, HTML_MAX_BYTES);\n const failedChecks: ValidationReportCheck[] = [];\n const errors: string[] = [];\n const recordFailure = (check: ValidationReportCheck, label: string, cause: unknown): void => {\n if (!failedChecks.includes(check)) {\n failedChecks.push(check);\n }\n errors.push(`${label} was unavailable: ${getErrorMessage(cause)}`);\n };\n\n let css: string | undefined;\n if (cssFilePath) {\n try {\n css = await dependencies.readTextFile(cssFilePath, CSS_MAX_BYTES);\n } catch (cause) {\n recordFailure(\"css\", \"CSS validation\", cause);\n }\n }\n\n const [htmlResult, cssResult, seoResult, schemaResult, linksResult] = await Promise.allSettled([\n dependencies.validateHtmlContent(html),\n css === undefined ? Promise.resolve([]) : dependencies.validateCssContent(css),\n Promise.resolve().then(() => dependencies.auditSeoMetadata(html)),\n Promise.resolve().then(() => dependencies.validateSchemaMarkup(html)),\n dependencies.checkBrokenLinks(html, baseUrl, 25),\n ]);\n\n if (htmlResult.status === \"rejected\") recordFailure(\"html\", \"HTML validation\", htmlResult.reason);\n if (cssResult.status === \"rejected\") recordFailure(\"css\", \"CSS validation\", cssResult.reason);\n if (seoResult.status === \"rejected\") recordFailure(\"seo\", \"SEO analysis\", seoResult.reason);\n if (schemaResult.status === \"rejected\") recordFailure(\"schema\", \"JSON-LD analysis\", schemaResult.reason);\n if (linksResult.status === \"rejected\") recordFailure(\"links\", \"Link checking\", linksResult.reason);\n\n return createValidationReport({\n htmlFilePath,\n cssAudited: css !== undefined,\n htmlMessages: htmlResult.status === \"fulfilled\" ? htmlResult.value : [],\n cssMessages: cssResult.status === \"fulfilled\" ? cssResult.value : [],\n seoIssues: seoResult.status === \"fulfilled\" ? seoResult.value.slice(0, 200) : [],\n schemaIssues: schemaResult.status === \"fulfilled\" ? schemaResult.value.slice(0, 200) : [],\n links: linksResult.status === \"fulfilled\" ? linksResult.value : [],\n failedChecks,\n errors,\n });\n}\n\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: \"mcp-web-validator\", version: SERVER_VERSION },\n {\n instructions:\n \"Validate only files, markup, and public URLs the user owns or is authorized to inspect. HTML and CSS validation send supplied content to W3C-operated validators. Link checks contact public links without following redirects. Screenshot capture executes page content in a sandboxed local browser and writes PNG files.\",\n },\n );\n\n server.registerTool(\n \"html.local\",\n {\n title: \"Validate local HTML\",\n description:\n \"Reads a bounded local HTML file and sends its markup to the W3C Nu HTML Checker. Use only files the user is authorized to share.\",\n inputSchema: {\n filePath: filePathSchema.describe(\"Absolute or workspace-relative path to an HTML file.\"),\n },\n outputSchema: {\n errors: z.array(w3cMessageSchema),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ filePath }) => {\n try {\n const html = await readTextFile(filePath, HTML_MAX_BYTES);\n const errors = await validateHtmlContent(html);\n return result({ errors }, htmlValidationContent(errors));\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { errors: [], error },\n failureContent(\n \"HTML validation\",\n error,\n \"Confirm the file exists and is readable, then retry. If the W3C service is unavailable, try again later.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"html.url\",\n {\n title: \"Validate a public URL\",\n description:\n \"Fetches a bounded public HTTP(S) page, then sends its markup to the W3C Nu HTML Checker. Private, reserved, credentialed, and nonstandard-port destinations are rejected.\",\n inputSchema: {\n url: publicUrlSchema.describe(\"Public HTTP(S) URL on port 80 or 443.\"),\n },\n outputSchema: {\n errors: z.array(w3cMessageSchema),\n fetchedUrl: z.string().optional(),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ url }) => {\n try {\n const fetched = await fetchPublicText(url, {\n maxBytes: HTML_MAX_BYTES,\n timeoutMs: 15_000,\n maxRedirects: 3,\n acceptedContentTypes: [\"text/html\", \"application/xhtml+xml\"],\n headers: {\n accept: \"text/html,application/xhtml+xml;q=0.9\",\n \"user-agent\": `DigestSEO-Web-Validator/${SERVER_VERSION} (+https://digestseo.com/validator-mcp/)`,\n },\n });\n if (fetched.status < 200 || fetched.status >= 300) {\n throw new Error(`Target URL returned HTTP ${fetched.status}.`);\n }\n const errors = await validateHtmlContent(fetched.text);\n return result(\n { errors, fetchedUrl: fetched.url },\n htmlValidationContent(errors, fetched.url),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { errors: [], error },\n failureContent(\n \"URL validation\",\n error,\n \"Confirm the URL is public, uses HTTP or HTTPS on a standard port, and returns HTML, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"css.local\",\n {\n title: \"Validate local CSS\",\n description:\n \"Reads a bounded local CSS file and sends it to the W3C Jigsaw CSS Validator. Use only files the user is authorized to share.\",\n inputSchema: {\n filePath: filePathSchema.describe(\"Absolute or workspace-relative path to a CSS file.\"),\n },\n outputSchema: {\n errors: z.array(cssMessageSchema),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ filePath }) => {\n try {\n const css = await readTextFile(filePath, CSS_MAX_BYTES);\n const errors = await validateCssContent(css);\n return result({ errors }, cssValidationContent(errors));\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { errors: [], error },\n failureContent(\n \"CSS validation\",\n error,\n \"Confirm the file exists, is readable, and is within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"seo.metadata\",\n {\n title: \"Audit SEO metadata\",\n description:\n \"Analyzes supplied HTML locally for metadata, heading structure, viewport configuration, image alternatives, and Open Graph fields.\",\n inputSchema: {\n htmlContent: htmlContentSchema.describe(\"Raw HTML markup to inspect locally.\"),\n },\n outputSchema: {\n issues: z.array(seoIssueSchema),\n totalIssues: z.number().int().nonnegative(),\n truncated: z.boolean(),\n error: z.string().optional(),\n },\n annotations: localReadOnlyAnnotations,\n },\n async ({ htmlContent }) => {\n try {\n const { issues, totalIssues, truncated } = auditSeoMetadataDetailed(htmlContent);\n return result(\n { issues, totalIssues, truncated },\n seoAuditContent(issues, totalIssues, truncated),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { issues: [], totalIssues: 0, truncated: false, error },\n failureContent(\n \"SEO audit\",\n error,\n \"Confirm the supplied HTML is complete and within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"links.broken\",\n {\n title: \"Check public links\",\n description:\n \"Resolves and checks up to 25 public HTTP(S) links in supplied HTML. Redirects are reported but not followed, and response bodies are discarded.\",\n inputSchema: {\n htmlContent: htmlContentSchema.describe(\"Raw HTML markup containing links to check.\"),\n baseUrl: publicUrlSchema\n .optional()\n .describe(\"Optional public HTTP(S) base URL used to resolve relative links.\"),\n maxLinks: z\n .number()\n .int()\n .min(1)\n .max(25)\n .default(25)\n .describe(\"Maximum number of public HTTP(S) links to check, from 1 to 25.\"),\n },\n outputSchema: {\n links: z.array(linkStatusSchema),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ htmlContent, baseUrl, maxLinks }) => {\n try {\n const links = await checkBrokenLinks(htmlContent, baseUrl, maxLinks);\n return result({ links }, linkCheckContent(links, baseUrl));\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { links: [], error },\n failureContent(\n \"Link check\",\n error,\n \"Confirm the base URL is public and the supplied HTML is within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"schema.markup\",\n {\n title: \"Validate JSON-LD syntax\",\n description:\n \"Parses JSON-LD blocks in supplied HTML locally and reports empty blocks or JSON syntax errors. It does not validate vocabulary semantics.\",\n inputSchema: {\n htmlContent: htmlContentSchema.describe(\"Raw HTML containing JSON-LD script blocks.\"),\n },\n outputSchema: {\n issues: z.array(seoIssueSchema),\n totalIssues: z.number().int().nonnegative(),\n truncated: z.boolean(),\n error: z.string().optional(),\n },\n annotations: localReadOnlyAnnotations,\n },\n async ({ htmlContent }) => {\n try {\n const { issues, totalIssues, truncated } = validateSchemaMarkupDetailed(htmlContent);\n return result(\n { issues, totalIssues, truncated },\n schemaValidationContent(issues, totalIssues, truncated, htmlContent),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { issues: [], totalIssues: 0, truncated: false, error },\n failureContent(\n \"JSON-LD syntax\",\n error,\n \"Confirm the supplied HTML is complete and within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"report.validation\",\n {\n title: \"Generate a validation report\",\n description:\n \"Combines W3C HTML/CSS validation, local SEO/accessibility checks, JSON-LD syntax checks, and a bounded public-link check into a Markdown and structured report.\",\n inputSchema: {\n htmlFilePath: filePathSchema.describe(\"Absolute or workspace-relative HTML file path.\"),\n cssFilePath: filePathSchema\n .optional()\n .describe(\"Optional absolute or workspace-relative CSS file path.\"),\n baseUrl: publicUrlSchema\n .optional()\n .describe(\"Optional public HTTP(S) base URL used to resolve relative links.\"),\n },\n outputSchema: {\n report: z.string(),\n summary: reportSummarySchema,\n htmlMessages: z.array(w3cMessageSchema),\n cssMessages: z.array(cssMessageSchema),\n seoIssues: z.array(seoIssueSchema),\n schemaIssues: z.array(seoIssueSchema),\n links: z.array(linkStatusSchema),\n failedChecks: z.array(z.enum(validationReportChecks)),\n errors: z.array(z.string()).optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ htmlFilePath, cssFilePath, baseUrl }) => {\n try {\n const reportData = await generateValidationReport({\n htmlFilePath,\n cssFilePath,\n baseUrl,\n });\n return result(reportData, reportContent(reportData));\n } catch (cause) {\n const error = getErrorMessage(cause);\n const reportData = failedReport(htmlFilePath, error);\n return result(\n reportData,\n failureContent(\n \"Validation report\",\n error,\n \"Confirm the input paths are readable and any base URL is public, then regenerate the report.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"screenshot.capture\",\n {\n title: \"Capture responsive screenshots\",\n description:\n \"Renders a local HTML file or HTTP(S) URL in a sandboxed local Chromium browser and writes PNG screenshots to the requested directory. Downloads the pinned headless browser on first use if it is not already cached. Existing matching files may be replaced.\",\n inputSchema: {\n targetPath: z\n .string()\n .trim()\n .min(1)\n .max(PATH_MAX_LENGTH)\n .describe(\"Local HTML path or HTTP(S) URL to render.\"),\n outputDir: filePathSchema\n .optional()\n .default(\".mcp-validator/screenshots\")\n .describe(\"Directory where PNG screenshots will be written.\"),\n viewports: z\n .array(\n z.object({\n name: z\n .string()\n .regex(/^[A-Za-z0-9_-]{1,50}$/)\n .describe(\"Safe filename label for this screenshot viewport.\"),\n width: z\n .number()\n .int()\n .min(200)\n .max(3_840)\n .describe(\"Viewport width in CSS pixels, from 200 to 3840.\"),\n height: z\n .number()\n .int()\n .min(200)\n .max(4_320)\n .describe(\"Viewport height in CSS pixels, from 200 to 4320.\"),\n }),\n )\n .min(1)\n .max(MAX_VIEWPORTS)\n .optional()\n .describe(\"Optional viewport definitions; defaults to desktop, tablet, and mobile sizes.\"),\n },\n outputSchema: {\n screenshots: z.array(screenshotSchema),\n outputDirectory: z.string(),\n error: z.string().optional(),\n },\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n },\n async ({ targetPath, outputDir, viewports }) => {\n const outputDirectory = path.resolve(outputDir);\n try {\n const screenshots = await captureScreenshots(targetPath, outputDirectory, viewports);\n return result(\n { screenshots, outputDirectory },\n screenshotCaptureContent(screenshots.length, outputDirectory),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { screenshots: [], outputDirectory, error },\n failureContent(\n \"Screenshot capture\",\n error,\n \"Confirm the target is reachable or readable and the output directory is writable, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n return server;\n}\n\nexport async function run(): Promise<void> {\n const server = createServer();\n await server.connect(new StdioServerTransport());\n console.error(`mcp-web-validator ${SERVER_VERSION} started on stdio`);\n}\n\nconst invokedPath = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : undefined;\nif (invokedPath === import.meta.url) {\n run().catch((cause: unknown) => {\n console.error(\"Fatal error starting mcp-web-validator:\", getErrorMessage(cause));\n process.exitCode = 1;\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { captureScreenshots } from \"./screenshot.js\";\nimport {\n auditSeoMetadata,\n auditSeoMetadataDetailed,\n checkBrokenLinks,\n validateSchemaMarkup,\n validateSchemaMarkupDetailed,\n} from \"./seo-auditor.js\";\nimport {\n createValidationReport,\n type ValidationReport,\n type ValidationReportCheck,\n validationReportChecks,\n} from \"./report.js\";\nimport { fetchPublicText, getErrorMessage, readTextFile } from \"./network.js\";\nimport {\n cssValidationContent,\n failureContent,\n htmlValidationContent,\n linkCheckContent,\n reportContent,\n schemaValidationContent,\n screenshotCaptureContent,\n seoAuditContent,\n} from \"./presentation.js\";\nimport { PACKAGE_VERSION } from \"./version.js\";\nimport {\n getW3CMessageSeverity,\n MAX_CSS_VALIDATION_BYTES,\n validateCssContent,\n validateHtmlContent,\n validateHtmlContentDetailed,\n} from \"./w3c-validator.js\";\n\nexport const SERVER_VERSION = PACKAGE_VERSION;\n\nconst HTML_MAX_BYTES = 2_000_000;\nconst CSS_MAX_BYTES = MAX_CSS_VALIDATION_BYTES;\nconst PATH_MAX_LENGTH = 4_096;\nconst MAX_VIEWPORTS = 8;\n\nconst filePathSchema = z.string().trim().min(1).max(PATH_MAX_LENGTH);\nconst htmlContentSchema = z.string().min(1).max(HTML_MAX_BYTES);\nconst publicUrlSchema = z\n .string()\n .url()\n .refine((value) => {\n const protocol = new URL(value).protocol;\n return protocol === \"http:\" || protocol === \"https:\";\n }, \"URL must use HTTP or HTTPS.\");\n\nconst w3cMessageSchema = z.object({\n type: z.string(),\n subType: z.string().optional(),\n message: z.string(),\n lastLine: z.number().int().optional(),\n lastColumn: z.number().int().optional(),\n firstLine: z.number().int().optional(),\n firstColumn: z.number().int().optional(),\n extract: z.string().optional(),\n});\n\nconst cssMessageSchema = z.object({\n line: z.number().int(),\n type: z.string(),\n message: z.string(),\n context: z.string().optional(),\n compatibility: z.literal(\"known-validator-limitation\").optional(),\n});\n\nconst seoIssueSchema = z.object({\n code: z.string().describe(\"Stable machine-readable audit rule identifier.\"),\n severity: z.enum([\"error\", \"warning\", \"info\"]),\n category: z.enum([\"SEO\", \"Schema\", \"BrokenLinks\", \"Accessibility\"]),\n message: z.string(),\n element: z.string().optional(),\n});\n\nconst linkStatusSchema = z.object({\n url: z.string(),\n status: z.union([z.number().int(), z.enum([\"blocked\", \"failed\"])]),\n ok: z.boolean(),\n message: z.string().optional(),\n});\n\nconst screenshotSchema = z.object({\n viewportName: z.string(),\n width: z.number().int(),\n height: z.number().int(),\n outputPath: z.string(),\n});\n\nconst reportSummarySchema = z.object({\n overallScore: z.number().int().min(0).max(100).nullable(),\n htmlScore: z.number().int().min(0).max(100).nullable(),\n cssScore: z.number().int().min(0).max(100).nullable(),\n seoScore: z.number().int().min(0).max(100).nullable(),\n linkScore: z.number().int().min(0).max(100).nullable(),\n htmlErrors: z.number().int().nonnegative(),\n htmlWarnings: z.number().int().nonnegative(),\n cssErrors: z.number().int().nonnegative(),\n cssCompatibilityLimitations: z.number().int().nonnegative(),\n seoErrors: z.number().int().nonnegative(),\n seoWarnings: z.number().int().nonnegative(),\n schemaErrors: z.number().int().nonnegative(),\n linksChecked: z.number().int().nonnegative(),\n brokenLinks: z.number().int().nonnegative(),\n});\n\nconst externalReadOnlyAnnotations = {\n // These calls contact external recipients but do not modify external state.\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n} as const;\n\nconst localReadOnlyAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n} as const;\n\nfunction result<T extends object>(\n structuredContent: T,\n content: string,\n isError = false,\n): CallToolResult {\n return {\n structuredContent: structuredContent as Record<string, unknown>,\n content: [\n {\n type: \"text\",\n text: content,\n },\n ],\n ...(isError ? { isError: true } : {}),\n };\n}\n\nfunction failedReport(filePath: string, error: string): ValidationReport {\n return {\n report: `Validation report could not be generated: ${error}`,\n summary: {\n overallScore: null,\n htmlScore: null,\n cssScore: null,\n seoScore: null,\n linkScore: null,\n htmlErrors: 0,\n htmlWarnings: 0,\n cssErrors: 0,\n cssCompatibilityLimitations: 0,\n seoErrors: 0,\n seoWarnings: 0,\n schemaErrors: 0,\n linksChecked: 0,\n brokenLinks: 0,\n },\n htmlMessages: [],\n htmlTotalMessages: 0,\n htmlTruncated: false,\n cssMessages: [],\n seoIssues: [],\n schemaIssues: [],\n links: [],\n failedChecks: [\"input\"],\n errors: [`${path.basename(filePath || \"document\")}: ${error}`],\n };\n}\n\ninterface ValidationReportDependencies {\n readTextFile: typeof readTextFile;\n validateHtmlContent: typeof validateHtmlContent;\n validateHtmlContentDetailed?: typeof validateHtmlContentDetailed;\n validateCssContent: typeof validateCssContent;\n auditSeoMetadata: typeof auditSeoMetadata;\n validateSchemaMarkup: typeof validateSchemaMarkup;\n checkBrokenLinks: typeof checkBrokenLinks;\n}\n\nconst validationReportDependencies: ValidationReportDependencies = {\n readTextFile,\n validateHtmlContent,\n validateHtmlContentDetailed,\n validateCssContent,\n auditSeoMetadata,\n validateSchemaMarkup,\n checkBrokenLinks,\n};\n\nexport interface GenerateValidationReportOptions {\n htmlFilePath: string;\n cssFilePath?: string;\n baseUrl?: string;\n}\n\n/** Runs independent validation checks without discarding successful results when another check fails. */\nexport async function generateValidationReport(\n { htmlFilePath, cssFilePath, baseUrl }: GenerateValidationReportOptions,\n dependencies: ValidationReportDependencies = validationReportDependencies,\n): Promise<ValidationReport> {\n const html = await dependencies.readTextFile(htmlFilePath, HTML_MAX_BYTES);\n const failedChecks: ValidationReportCheck[] = [];\n const errors: string[] = [];\n const recordFailure = (check: ValidationReportCheck, label: string, cause: unknown): void => {\n if (!failedChecks.includes(check)) {\n failedChecks.push(check);\n }\n errors.push(`${label} was unavailable: ${getErrorMessage(cause)}`);\n };\n\n let css: string | undefined;\n if (cssFilePath) {\n try {\n css = await dependencies.readTextFile(cssFilePath, CSS_MAX_BYTES);\n } catch (cause) {\n recordFailure(\"css\", \"CSS validation\", cause);\n }\n }\n\n const htmlValidation = dependencies.validateHtmlContentDetailed\n ? dependencies.validateHtmlContentDetailed(html)\n : dependencies.validateHtmlContent(html).then((messages) => {\n const counts = { error: 0, warning: 0, info: 0 };\n for (const message of messages) counts[getW3CMessageSeverity(message)] += 1;\n return { messages, total: messages.length, truncated: false, counts };\n });\n const [htmlResult, cssResult, seoResult, schemaResult, linksResult] = await Promise.allSettled([\n htmlValidation,\n css === undefined ? Promise.resolve([]) : dependencies.validateCssContent(css),\n Promise.resolve().then(() => dependencies.auditSeoMetadata(html)),\n Promise.resolve().then(() => dependencies.validateSchemaMarkup(html)),\n dependencies.checkBrokenLinks(html, baseUrl, 25),\n ]);\n\n if (htmlResult.status === \"rejected\") recordFailure(\"html\", \"HTML validation\", htmlResult.reason);\n if (cssResult.status === \"rejected\") recordFailure(\"css\", \"CSS validation\", cssResult.reason);\n if (seoResult.status === \"rejected\") recordFailure(\"seo\", \"SEO analysis\", seoResult.reason);\n if (schemaResult.status === \"rejected\") recordFailure(\"schema\", \"JSON-LD analysis\", schemaResult.reason);\n if (linksResult.status === \"rejected\") recordFailure(\"links\", \"Link checking\", linksResult.reason);\n\n return createValidationReport({\n htmlFilePath,\n cssAudited: css !== undefined,\n htmlMessages: htmlResult.status === \"fulfilled\" ? htmlResult.value.messages : [],\n htmlTotalMessages: htmlResult.status === \"fulfilled\" ? htmlResult.value.total : 0,\n htmlTruncated: htmlResult.status === \"fulfilled\" ? htmlResult.value.truncated : false,\n htmlCounts: htmlResult.status === \"fulfilled\" ? htmlResult.value.counts : undefined,\n cssMessages: cssResult.status === \"fulfilled\" ? cssResult.value : [],\n seoIssues: seoResult.status === \"fulfilled\" ? seoResult.value.slice(0, 200) : [],\n schemaIssues: schemaResult.status === \"fulfilled\" ? schemaResult.value.slice(0, 200) : [],\n links: linksResult.status === \"fulfilled\" ? linksResult.value : [],\n failedChecks,\n errors,\n });\n}\n\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: \"mcp-web-validator\", version: SERVER_VERSION },\n {\n instructions:\n \"Validate only files, markup, and public URLs the user owns or is authorized to inspect. HTML and CSS validation send supplied content to W3C-operated validators. Link checks contact public links without following redirects. Screenshot capture executes page content in a sandboxed local browser and writes PNG files.\",\n },\n );\n\n server.registerTool(\n \"html.local\",\n {\n title: \"Validate local HTML\",\n description:\n \"Reads a bounded local HTML file and sends its markup to the W3C Nu HTML Checker. Use only files the user is authorized to share.\",\n inputSchema: {\n filePath: filePathSchema.describe(\"Absolute or workspace-relative path to an HTML file.\"),\n },\n outputSchema: {\n errors: z.array(w3cMessageSchema),\n totalMessages: z.number().int().nonnegative(),\n truncated: z.boolean(),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ filePath }) => {\n try {\n const html = await readTextFile(filePath, HTML_MAX_BYTES);\n const validation = await validateHtmlContentDetailed(html);\n return result(\n { errors: validation.messages, totalMessages: validation.total, truncated: validation.truncated },\n htmlValidationContent(validation.messages, undefined, validation.total, validation.counts),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { errors: [], totalMessages: 0, truncated: false, error },\n failureContent(\n \"HTML validation\",\n error,\n \"Confirm the file exists and is readable, then retry. If the W3C service is unavailable, try again later.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"html.url\",\n {\n title: \"Validate a public URL\",\n description:\n \"Fetches a bounded public HTTP(S) page, then sends its markup to the W3C Nu HTML Checker. Private, reserved, credentialed, and nonstandard-port destinations are rejected.\",\n inputSchema: {\n url: publicUrlSchema.describe(\"Public HTTP(S) URL on port 80 or 443.\"),\n },\n outputSchema: {\n errors: z.array(w3cMessageSchema),\n totalMessages: z.number().int().nonnegative(),\n truncated: z.boolean(),\n fetchedUrl: z.string().optional(),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ url }) => {\n try {\n const fetched = await fetchPublicText(url, {\n maxBytes: HTML_MAX_BYTES,\n timeoutMs: 15_000,\n maxRedirects: 3,\n acceptedContentTypes: [\"text/html\", \"application/xhtml+xml\"],\n headers: {\n accept: \"text/html,application/xhtml+xml;q=0.9\",\n \"user-agent\": `DigestSEO-Web-Validator/${SERVER_VERSION} (+https://digestseo.com/validator-mcp/)`,\n },\n });\n if (fetched.status < 200 || fetched.status >= 300) {\n throw new Error(`Target URL returned HTTP ${fetched.status}.`);\n }\n const validation = await validateHtmlContentDetailed(fetched.text);\n return result(\n {\n errors: validation.messages,\n totalMessages: validation.total,\n truncated: validation.truncated,\n fetchedUrl: fetched.url,\n },\n htmlValidationContent(validation.messages, fetched.url, validation.total, validation.counts),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { errors: [], totalMessages: 0, truncated: false, error },\n failureContent(\n \"URL validation\",\n error,\n \"Confirm the URL is public, uses HTTP or HTTPS on a standard port, and returns HTML, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"css.local\",\n {\n title: \"Validate local CSS\",\n description:\n \"Reads a bounded local CSS file and sends it to the W3C Jigsaw CSS Validator. Use only files the user is authorized to share.\",\n inputSchema: {\n filePath: filePathSchema.describe(\"Absolute or workspace-relative path to a CSS file.\"),\n },\n outputSchema: {\n errors: z.array(cssMessageSchema),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ filePath }) => {\n try {\n const css = await readTextFile(filePath, CSS_MAX_BYTES);\n const errors = await validateCssContent(css);\n return result({ errors }, cssValidationContent(errors));\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { errors: [], error },\n failureContent(\n \"CSS validation\",\n error,\n \"Confirm the file exists, is readable, and is within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"seo.metadata\",\n {\n title: \"Audit SEO metadata\",\n description:\n \"Analyzes supplied HTML locally for metadata, heading structure, viewport configuration, image alternatives, and Open Graph fields.\",\n inputSchema: {\n htmlContent: htmlContentSchema.describe(\"Raw HTML markup to inspect locally.\"),\n },\n outputSchema: {\n issues: z.array(seoIssueSchema),\n totalIssues: z.number().int().nonnegative(),\n truncated: z.boolean(),\n error: z.string().optional(),\n },\n annotations: localReadOnlyAnnotations,\n },\n async ({ htmlContent }) => {\n try {\n const { issues, totalIssues, truncated } = auditSeoMetadataDetailed(htmlContent);\n return result(\n { issues, totalIssues, truncated },\n seoAuditContent(issues, totalIssues, truncated),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { issues: [], totalIssues: 0, truncated: false, error },\n failureContent(\n \"SEO audit\",\n error,\n \"Confirm the supplied HTML is complete and within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"links.broken\",\n {\n title: \"Check public links\",\n description:\n \"Resolves and checks up to 25 public HTTP(S) links in supplied HTML. Redirects are reported but not followed, and response bodies are discarded.\",\n inputSchema: {\n htmlContent: htmlContentSchema.describe(\"Raw HTML markup containing links to check.\"),\n baseUrl: publicUrlSchema\n .optional()\n .describe(\"Optional public HTTP(S) base URL used to resolve relative links.\"),\n maxLinks: z\n .number()\n .int()\n .min(1)\n .max(25)\n .default(25)\n .describe(\"Maximum number of public HTTP(S) links to check, from 1 to 25.\"),\n },\n outputSchema: {\n links: z.array(linkStatusSchema),\n error: z.string().optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ htmlContent, baseUrl, maxLinks }) => {\n try {\n const links = await checkBrokenLinks(htmlContent, baseUrl, maxLinks);\n return result({ links }, linkCheckContent(links, baseUrl));\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { links: [], error },\n failureContent(\n \"Link check\",\n error,\n \"Confirm the base URL is public and the supplied HTML is within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"schema.markup\",\n {\n title: \"Validate JSON-LD syntax\",\n description:\n \"Parses JSON-LD blocks in supplied HTML locally and reports empty blocks or JSON syntax errors. It does not validate vocabulary semantics.\",\n inputSchema: {\n htmlContent: htmlContentSchema.describe(\"Raw HTML containing JSON-LD script blocks.\"),\n },\n outputSchema: {\n issues: z.array(seoIssueSchema),\n totalIssues: z.number().int().nonnegative(),\n truncated: z.boolean(),\n error: z.string().optional(),\n },\n annotations: localReadOnlyAnnotations,\n },\n async ({ htmlContent }) => {\n try {\n const { issues, totalIssues, truncated } = validateSchemaMarkupDetailed(htmlContent);\n return result(\n { issues, totalIssues, truncated },\n schemaValidationContent(issues, totalIssues, truncated, htmlContent),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { issues: [], totalIssues: 0, truncated: false, error },\n failureContent(\n \"JSON-LD syntax\",\n error,\n \"Confirm the supplied HTML is complete and within the size limit, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"report.validation\",\n {\n title: \"Generate a validation report\",\n description:\n \"Combines W3C HTML/CSS validation, local SEO/accessibility checks, JSON-LD syntax checks, and a bounded public-link check into a Markdown and structured report.\",\n inputSchema: {\n htmlFilePath: filePathSchema.describe(\"Absolute or workspace-relative HTML file path.\"),\n cssFilePath: filePathSchema\n .optional()\n .describe(\"Optional absolute or workspace-relative CSS file path.\"),\n baseUrl: publicUrlSchema\n .optional()\n .describe(\"Optional public HTTP(S) base URL used to resolve relative links.\"),\n },\n outputSchema: {\n report: z.string(),\n summary: reportSummarySchema,\n htmlMessages: z.array(w3cMessageSchema),\n htmlTotalMessages: z.number().int().nonnegative(),\n htmlTruncated: z.boolean(),\n cssMessages: z.array(cssMessageSchema),\n seoIssues: z.array(seoIssueSchema),\n schemaIssues: z.array(seoIssueSchema),\n links: z.array(linkStatusSchema),\n failedChecks: z.array(z.enum(validationReportChecks)),\n errors: z.array(z.string()).optional(),\n },\n annotations: externalReadOnlyAnnotations,\n },\n async ({ htmlFilePath, cssFilePath, baseUrl }) => {\n try {\n const reportData = await generateValidationReport({\n htmlFilePath,\n cssFilePath,\n baseUrl,\n });\n return result(reportData, reportContent(reportData));\n } catch (cause) {\n const error = getErrorMessage(cause);\n const reportData = failedReport(htmlFilePath, error);\n return result(\n reportData,\n failureContent(\n \"Validation report\",\n error,\n \"Confirm the input paths are readable and any base URL is public, then regenerate the report.\",\n ),\n true,\n );\n }\n },\n );\n\n server.registerTool(\n \"screenshot.capture\",\n {\n title: \"Capture responsive screenshots\",\n description:\n \"Renders a local HTML file or HTTP(S) URL in a sandboxed local Chromium browser and writes PNG screenshots to the requested directory. Downloads the pinned headless browser on first use if it is not already cached. Existing matching files may be replaced.\",\n inputSchema: {\n targetPath: z\n .string()\n .trim()\n .min(1)\n .max(PATH_MAX_LENGTH)\n .describe(\"Local HTML path or HTTP(S) URL to render.\"),\n outputDir: filePathSchema\n .optional()\n .default(\".mcp-validator/screenshots\")\n .describe(\"Directory where PNG screenshots will be written.\"),\n viewports: z\n .array(\n z.object({\n name: z\n .string()\n .regex(/^[A-Za-z0-9_-]{1,50}$/)\n .describe(\"Safe filename label for this screenshot viewport.\"),\n width: z\n .number()\n .int()\n .min(200)\n .max(3_840)\n .describe(\"Viewport width in CSS pixels, from 200 to 3840.\"),\n height: z\n .number()\n .int()\n .min(200)\n .max(4_320)\n .describe(\"Viewport height in CSS pixels, from 200 to 4320.\"),\n }),\n )\n .min(1)\n .max(MAX_VIEWPORTS)\n .optional()\n .describe(\"Optional viewport definitions; defaults to desktop, tablet, and mobile sizes.\"),\n },\n outputSchema: {\n screenshots: z.array(screenshotSchema),\n outputDirectory: z.string(),\n error: z.string().optional(),\n },\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n },\n async ({ targetPath, outputDir, viewports }) => {\n const outputDirectory = path.resolve(outputDir);\n try {\n const screenshots = await captureScreenshots(targetPath, outputDirectory, viewports);\n return result(\n { screenshots, outputDirectory },\n screenshotCaptureContent(screenshots.length, outputDirectory),\n );\n } catch (cause) {\n const error = getErrorMessage(cause);\n return result(\n { screenshots: [], outputDirectory, error },\n failureContent(\n \"Screenshot capture\",\n error,\n \"Confirm the target is reachable or readable and the output directory is writable, then retry.\",\n ),\n true,\n );\n }\n },\n );\n\n return server;\n}\n\nexport async function run(): Promise<void> {\n const server = createServer();\n await server.connect(new StdioServerTransport());\n console.error(`mcp-web-validator ${SERVER_VERSION} started on stdio`);\n}\n\nconst invokedPath = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : undefined;\nif (invokedPath === import.meta.url) {\n run().catch((cause: unknown) => {\n console.error(\"Fatal error starting mcp-web-validator:\", getErrorMessage(cause));\n process.exitCode = 1;\n });\n}\n"]}
|
package/dist/presentation.d.ts
CHANGED
|
@@ -4,7 +4,11 @@ import { type CSSMessage, type W3CMessage } from "./w3c-validator.js";
|
|
|
4
4
|
interface ValidationReportResult extends ValidationReport {
|
|
5
5
|
errors?: string[];
|
|
6
6
|
}
|
|
7
|
-
export declare function htmlValidationContent(messages: W3CMessage[], source?: string
|
|
7
|
+
export declare function htmlValidationContent(messages: W3CMessage[], source?: string, totalMessages?: number, counts?: {
|
|
8
|
+
error: number;
|
|
9
|
+
warning: number;
|
|
10
|
+
info: number;
|
|
11
|
+
}): string;
|
|
8
12
|
export declare function cssValidationContent(messages: CSSMessage[]): string;
|
|
9
13
|
export declare function seoAuditContent(issues: SEOIssue[], totalIssues: number, truncated: boolean): string;
|
|
10
14
|
export declare function schemaValidationContent(issues: SEOIssue[], totalIssues: number, truncated: boolean, htmlContent: string): string;
|
package/dist/presentation.js
CHANGED
|
@@ -77,12 +77,14 @@ function toolContent(options) {
|
|
|
77
77
|
sections.push(`**Next step:** ${collapseWhitespace(options.nextStep, 400)}`);
|
|
78
78
|
return sections.join("\n\n");
|
|
79
79
|
}
|
|
80
|
-
export function htmlValidationContent(messages, source) {
|
|
81
|
-
const errorCount =
|
|
82
|
-
|
|
83
|
-
const
|
|
80
|
+
export function htmlValidationContent(messages, source, totalMessages = messages.length, counts) {
|
|
81
|
+
const errorCount = counts?.error
|
|
82
|
+
?? messages.filter((message) => getW3CMessageSeverity(message) === "error").length;
|
|
83
|
+
const warningCount = counts?.warning
|
|
84
|
+
?? messages.filter((message) => getW3CMessageSeverity(message) === "warning").length;
|
|
85
|
+
const infoCount = counts?.info ?? Math.max(0, totalMessages - errorCount - warningCount);
|
|
84
86
|
const sourceText = source ? ` for ${markdownCode(source, 180)}` : "";
|
|
85
|
-
if (
|
|
87
|
+
if (totalMessages === 0) {
|
|
86
88
|
return toolContent({
|
|
87
89
|
title: "HTML validation",
|
|
88
90
|
status: "clean",
|
|
@@ -103,6 +105,9 @@ export function htmlValidationContent(messages, source) {
|
|
|
103
105
|
nextStep: hasActionableDiagnostics
|
|
104
106
|
? "Fix errors first, then warnings, and rerun HTML validation to confirm the markup is clean."
|
|
105
107
|
: "Review the informational diagnostics, then rerun validation after relevant markup changes.",
|
|
108
|
+
note: totalMessages > messages.length
|
|
109
|
+
? `Showing the first ${messages.length} of ${totalMessages} HTML diagnostics in structured output; severity totals include all returned Nu diagnostics.`
|
|
110
|
+
: undefined,
|
|
106
111
|
});
|
|
107
112
|
}
|
|
108
113
|
export function cssValidationContent(messages) {
|
|
@@ -320,7 +325,7 @@ export function reportContent(reportData) {
|
|
|
320
325
|
? reportData.failedChecks.includes("css")
|
|
321
326
|
? "CSS validation unavailable"
|
|
322
327
|
: compatibilityLimited
|
|
323
|
-
? `${countLabel(summary.cssErrors, "CSS error")}
|
|
328
|
+
? `${countLabel(summary.cssErrors, "CSS error")}; ${countLabel(summary.cssCompatibilityLimitations, "known validator limitation")}`
|
|
324
329
|
: "CSS not audited"
|
|
325
330
|
: countLabel(summary.cssErrors, "CSS error");
|
|
326
331
|
const redirects = reportData.links.filter(isRedirect).length;
|
|
@@ -340,6 +345,10 @@ export function reportContent(reportData) {
|
|
|
340
345
|
const unavailableChecks = reportData.failedChecks
|
|
341
346
|
.filter((check) => check !== "input")
|
|
342
347
|
.map((check) => checkLabels[check]);
|
|
348
|
+
const scoreNote = "The score is a triage heuristic based on these checks, not a Lighthouse score or a search-ranking prediction.";
|
|
349
|
+
const truncationNote = reportData.htmlTruncated
|
|
350
|
+
? `Showing the first ${reportData.htmlMessages.length} of ${reportData.htmlTotalMessages} HTML diagnostics; HTML summary counts and scoring include all returned Nu diagnostics.`
|
|
351
|
+
: undefined;
|
|
343
352
|
return toolContent({
|
|
344
353
|
title: "Validation report",
|
|
345
354
|
status,
|
|
@@ -357,10 +366,10 @@ export function reportContent(reportData) {
|
|
|
357
366
|
? "Use the full Markdown report as the audit record and rerun it after meaningful page changes."
|
|
358
367
|
: "Work through these priorities, then regenerate the report to compare the heuristic score.",
|
|
359
368
|
note: partial
|
|
360
|
-
? "No overall score is shown while one or more checks are unavailable."
|
|
369
|
+
? ["No overall score is shown while one or more checks are unavailable.", truncationNote].filter(Boolean).join(" ")
|
|
361
370
|
: compatibilityLimited
|
|
362
|
-
? "CSS and overall scores are withheld while a known Jigsaw parser limitation is present; the original upstream diagnostic remains visible."
|
|
363
|
-
:
|
|
371
|
+
? ["CSS and overall scores are withheld while a known Jigsaw parser limitation is present; the original upstream diagnostic remains visible.", truncationNote].filter(Boolean).join(" ")
|
|
372
|
+
: [scoreNote, truncationNote].filter(Boolean).join(" "),
|
|
364
373
|
});
|
|
365
374
|
}
|
|
366
375
|
export function screenshotCaptureContent(count, outputDirectory) {
|
package/dist/presentation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation.js","sourceRoot":"","sources":["../src/presentation.ts"],"names":[],"mappings":"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","sourcesContent":["import * as cheerio from \"cheerio\";\nimport type { ValidationReport } from \"./report.js\";\nimport type { LinkStatus, SEOIssue } from \"./seo-auditor.js\";\nimport {\n getW3CMessageSeverity,\n type CSSMessage,\n type W3CMessage,\n} from \"./w3c-validator.js\";\n\ntype ActionPriority = 0 | 1 | 2;\n\ninterface ActionItem {\n message: string;\n priority: ActionPriority;\n location?: string;\n}\n\ninterface ValidationReportResult extends ValidationReport {\n errors?: string[];\n}\n\nfunction collapseWhitespace(value: string, maxLength = 240): string {\n const collapsed = value.replace(/\\s+/g, \" \").trim();\n if (collapsed.length <= maxLength) {\n return collapsed;\n }\n return `${collapsed.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\n/** Escapes untrusted text while leaving the surrounding, controlled Markdown intact. */\nfunction markdownText(value: string, maxLength = 240): string {\n return collapseWhitespace(value, maxLength)\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/([`*_[\\]{}()#+\\-.!|~])/g, \"\\\\$1\");\n}\n\n/** Wraps untrusted identifiers and paths in a code span that cannot be closed by their content. */\nfunction markdownCode(value: string, maxLength = 240): string {\n const collapsed = collapseWhitespace(value, maxLength);\n const longestBacktickRun = Math.max(\n 0,\n ...Array.from(collapsed.matchAll(/`+/g), (match) => match[0].length),\n );\n const delimiter = \"`\".repeat(longestBacktickRun + 1);\n const content = collapsed.startsWith(\"`\") || collapsed.endsWith(\"`\")\n ? ` ${collapsed} `\n : collapsed;\n return `${delimiter}${content}${delimiter}`;\n}\n\nfunction countLabel(count: number, singular: string, plural = `${singular}s`): string {\n return `${count} ${count === 1 ? singular : plural}`;\n}\n\nfunction priorityForSeverity(severity: string): ActionPriority {\n if (severity.toLowerCase() === \"error\") return 0;\n if (severity.toLowerCase() === \"warning\") return 1;\n return 2;\n}\n\nfunction formatLocation(line?: number, column?: number): string | undefined {\n if (line === undefined && column === undefined) return undefined;\n if (line === undefined) return `column ${column}`;\n if (column === undefined) return `line ${line}`;\n return `line ${line}, column ${column}`;\n}\n\nfunction formatActions(items: ActionItem[]): string | undefined {\n const prioritized = items\n .map((item, index) => ({ item, index }))\n .sort((left, right) => left.item.priority - right.item.priority || left.index - right.index)\n .slice(0, 3)\n .map(({ item }) => {\n const label = item.priority === 0 ? \"Error\" : item.priority === 1 ? \"Warning\" : \"Check\";\n const location = item.location\n ? ` · ${markdownCode(item.location, 120)}`\n : \"\";\n return `- **${label}**${location}: ${markdownText(item.message)}`;\n });\n\n return prioritized.length > 0 ? prioritized.join(\"\\n\") : undefined;\n}\n\nfunction toolContent(options: {\n title: string;\n status: string;\n outcome: string;\n nextStep: string;\n actions?: ActionItem[];\n note?: string;\n}): string {\n const sections = [\n `### ${options.title}: ${options.status}`,\n collapseWhitespace(options.outcome, 500),\n ];\n if (options.note) {\n sections.push(collapseWhitespace(options.note, 500));\n }\n const actionItems = options.actions ?? [];\n const actions = formatActions(actionItems);\n if (actions) {\n const hasActionableItem = actionItems.some((item) => item.priority < 2);\n const actionHeading = options.status === \"review suggested\" || !hasActionableItem ? \"Review\" : \"Fix first\";\n sections.push(`**${actionHeading}**\\n${actions}`);\n }\n sections.push(`**Next step:** ${collapseWhitespace(options.nextStep, 400)}`);\n return sections.join(\"\\n\\n\");\n}\n\nexport function htmlValidationContent(messages: W3CMessage[], source?: string): string {\n const errorCount = messages.filter((message) => getW3CMessageSeverity(message) === \"error\").length;\n const warningCount = messages.filter((message) => getW3CMessageSeverity(message) === \"warning\").length;\n const infoCount = messages.length - errorCount - warningCount;\n const sourceText = source ? ` for ${markdownCode(source, 180)}` : \"\";\n if (messages.length === 0) {\n return toolContent({\n title: \"HTML validation\",\n status: \"clean\",\n outcome: `The W3C validator returned no HTML diagnostics${sourceText}.`,\n nextStep: \"Keep this result as a baseline and validate again after the next markup change.\",\n });\n }\n\n const hasActionableDiagnostics = errorCount > 0 || warningCount > 0;\n return toolContent({\n title: \"HTML validation\",\n status: hasActionableDiagnostics ? \"attention needed\" : \"review suggested\",\n outcome: `The W3C validator returned ${countLabel(errorCount, \"error\")}, ${countLabel(warningCount, \"warning\")}, and ${countLabel(infoCount, \"informational diagnostic\")}${sourceText}.`,\n actions: messages.map((message) => ({\n priority: priorityForSeverity(getW3CMessageSeverity(message)),\n message: message.message,\n location: formatLocation(message.lastLine ?? message.firstLine, message.lastColumn ?? message.firstColumn),\n })),\n nextStep: hasActionableDiagnostics\n ? \"Fix errors first, then warnings, and rerun HTML validation to confirm the markup is clean.\"\n : \"Review the informational diagnostics, then rerun validation after relevant markup changes.\",\n });\n}\n\nexport function cssValidationContent(messages: CSSMessage[]): string {\n if (messages.length === 0) {\n return toolContent({\n title: \"CSS validation\",\n status: \"clean\",\n outcome: \"The W3C validator returned no CSS errors.\",\n nextStep: \"Validate again after the next stylesheet change.\",\n });\n }\n\n const compatibilityLimitations = messages.filter(\n (message) => message.compatibility === \"known-validator-limitation\",\n );\n const actionableErrors = messages.length - compatibilityLimitations.length;\n\n return toolContent({\n title: \"CSS validation\",\n status: actionableErrors > 0 ? \"attention needed\" : \"review suggested\",\n outcome: compatibilityLimitations.length > 0\n ? `The W3C validator returned ${countLabel(messages.length, \"CSS error\")}; ${countLabel(compatibilityLimitations.length, \"diagnostic\")} ${compatibilityLimitations.length === 1 ? \"matches\" : \"match\"} a known validator limitation.`\n : `The W3C validator returned ${countLabel(messages.length, \"CSS error\")}.`,\n actions: messages.map((message) => ({\n priority: message.compatibility === \"known-validator-limitation\" ? 2 : 0,\n message: message.context ? `${message.message} Context: ${message.context}` : message.message,\n location: formatLocation(message.line),\n })),\n nextStep: actionableErrors > 0\n ? \"Correct the actionable errors, then rerun CSS validation because one syntax issue can cause later diagnostics.\"\n : \"Review the marked @container diagnostic against current CSS specifications; do not treat it as invalid CSS by itself.\",\n note: compatibilityLimitations.length > 0\n ? \"Jigsaw currently does not recognize the standards-defined @container rule. The upstream diagnostic is preserved and marked as a known validator limitation.\"\n : undefined,\n });\n}\n\nexport function seoAuditContent(issues: SEOIssue[], totalIssues: number, truncated: boolean): string {\n const errors = issues.filter((issue) => issue.severity === \"error\").length;\n const warnings = issues.filter((issue) => issue.severity === \"warning\").length;\n const info = issues.filter((issue) => issue.severity === \"info\").length;\n if (totalIssues === 0) {\n return toolContent({\n title: \"SEO audit\",\n status: \"clean within this audit\",\n outcome: \"This focused rules-based audit found no SEO or accessibility issues in the supplied HTML.\",\n nextStep: \"Keep the metadata current and rerun the audit whenever the page template changes.\",\n note: \"This result does not replace a crawl, performance test, or Search Console review.\",\n });\n }\n\n const hasActionableFindings = errors > 0 || warnings > 0;\n return toolContent({\n title: \"SEO audit\",\n status: hasActionableFindings ? \"attention needed\" : \"review suggested\",\n outcome: `The audit found ${countLabel(errors, \"error\")}, ${countLabel(warnings, \"warning\")}, and ${countLabel(info, \"suggestion\")}.`,\n actions: issues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: issue.message,\n location: issue.element ? collapseWhitespace(issue.element, 120) : undefined,\n })),\n nextStep: hasActionableFindings\n ? \"Address errors first, then warnings, and rerun the audit after updating the page.\"\n : \"Review the suggestions that apply to this page, then rerun the audit after relevant template changes.\",\n note: truncated\n ? `Showing the first ${issues.length} of ${totalIssues} findings in structured output.`\n : undefined,\n });\n}\n\nfunction countJsonLdBlocks(htmlContent: string): number {\n const $ = cheerio.load(htmlContent);\n return $(\"script[type]\").filter((_, element) => {\n const type = $(element).attr(\"type\");\n return type?.split(\";\", 1)[0].trim().toLowerCase() === \"application/ld+json\";\n }).length;\n}\n\nexport function schemaValidationContent(\n issues: SEOIssue[],\n totalIssues: number,\n truncated: boolean,\n htmlContent: string,\n): string {\n const blockCount = countJsonLdBlocks(htmlContent);\n if (blockCount === 0) {\n return toolContent({\n title: \"JSON-LD syntax\",\n status: \"not present\",\n outcome: \"No JSON-LD script blocks were found, so there was no structured data to parse.\",\n nextStep: \"Add JSON-LD only when it accurately describes visible page content, then validate it again.\",\n });\n }\n if (totalIssues === 0) {\n return toolContent({\n title: \"JSON-LD syntax\",\n status: \"clean\",\n outcome: `${countLabel(blockCount, \"JSON-LD block\")} parsed without syntax errors.`,\n nextStep: \"Verify the properties against the relevant Schema.org type and search-engine requirements.\",\n note: \"This check covers JSON syntax only; it does not validate vocabulary semantics or rich-result eligibility.\",\n });\n }\n\n return toolContent({\n title: \"JSON-LD syntax\",\n status: \"attention needed\",\n outcome: `${countLabel(totalIssues, \"syntax issue\")} ${totalIssues === 1 ? \"was\" : \"were\"} found across ${countLabel(blockCount, \"JSON-LD block\")}.`,\n actions: issues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: issue.message,\n })),\n nextStep: \"Repair the invalid or empty blocks, then rerun this syntax check before testing rich-result eligibility.\",\n note: truncated\n ? `Showing the first ${issues.length} of ${totalIssues} findings in structured output.`\n : undefined,\n });\n}\n\nfunction linkPriority(link: LinkStatus): ActionPriority {\n if (isRedirect(link)) return 1;\n return 0;\n}\n\nfunction isRedirect(link: LinkStatus): boolean {\n return typeof link.status === \"number\" && link.status >= 300 && link.status < 400;\n}\n\nfunction linkActionMessage(link: LinkStatus): string {\n if (isRedirect(link)) {\n return `${link.status} redirect; destination was not followed`;\n }\n return `Link returned ${typeof link.status === \"number\" ? `HTTP ${link.status}` : link.status}${link.message ? ` — ${link.message}` : \"\"}`;\n}\n\nexport function linkCheckContent(links: LinkStatus[], baseUrl?: string): string {\n if (links.length === 0) {\n return toolContent({\n title: \"Link check\",\n status: \"nothing checked\",\n outcome: \"No eligible public HTTP(S) links were found, so no link requests were made.\",\n nextStep: baseUrl\n ? \"Confirm the HTML contains reachable anchor URLs, then run the check again.\"\n : \"If the page uses relative links, provide its public base URL and run the check again.\",\n });\n }\n\n const unreachable = links.filter((link) => !link.ok);\n const redirects = links.filter(isRedirect);\n if (unreachable.length === 0 && redirects.length === 0) {\n return toolContent({\n title: \"Link check\",\n status: \"clean\",\n outcome: links.length === 1\n ? \"The checked link returned a successful response.\"\n : `All ${links.length} checked links returned a successful response.`,\n nextStep: \"Recheck periodically because external link availability can change.\",\n });\n }\n\n const actions = [\n ...unreachable,\n ...redirects,\n ].map((link) => ({\n priority: linkPriority(link),\n location: link.url,\n message: linkActionMessage(link),\n }));\n\n const linkOutcome = [\n unreachable.length > 0\n ? `${countLabel(unreachable.length, \"link\")} ${unreachable.length === 1 ? \"is\" : \"are\"} broken or unreachable`\n : \"no checked links are broken or unreachable\",\n redirects.length > 0\n ? `${countLabel(redirects.length, \"redirect\")} ${redirects.length === 1 ? \"needs\" : \"need\"} review`\n : undefined,\n ].filter((item): item is string => item !== undefined).join(\"; \");\n\n return toolContent({\n title: \"Link check\",\n status: unreachable.length > 0 ? \"attention needed\" : \"review suggested\",\n outcome: `${linkOutcome} of ${links.length} checked. Redirect destinations are not followed.`,\n actions,\n nextStep: \"Update failed destinations and review redirects, then rerun the link check.\",\n });\n}\n\nfunction reportActionItems(reportData: ValidationReportResult): ActionItem[] {\n return [\n ...(reportData.errors ?? []).map((message) => ({\n priority: 0 as const,\n message,\n })),\n ...reportData.htmlMessages.map((message) => ({\n priority: priorityForSeverity(getW3CMessageSeverity(message)),\n message: `HTML: ${message.message}`,\n location: formatLocation(message.lastLine ?? message.firstLine, message.lastColumn ?? message.firstColumn),\n })),\n ...reportData.cssMessages.map((message) => ({\n priority: message.compatibility === \"known-validator-limitation\" ? 2 as const : 0 as const,\n message: `CSS: ${message.message}`,\n location: formatLocation(message.line),\n })),\n ...reportData.seoIssues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: `${issue.category}: ${issue.message}`,\n location: issue.element ? collapseWhitespace(issue.element, 120) : undefined,\n })),\n ...reportData.schemaIssues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: `JSON-LD: ${issue.message}`,\n })),\n ...reportData.links\n .filter((link) => !link.ok || isRedirect(link))\n .map((link) => ({\n priority: linkPriority(link),\n message: linkActionMessage(link),\n location: link.url,\n })),\n ];\n}\n\nexport function reportContent(reportData: ValidationReportResult): string {\n const { summary } = reportData;\n const actions = reportActionItems(reportData);\n const partial = reportData.failedChecks.length > 0;\n const compatibilityLimited = summary.cssCompatibilityLimitations > 0;\n const hasActionableFinding = actions.some((action) => action.priority < 2);\n const status = partial\n ? \"partial\"\n : compatibilityLimited\n ? \"compatibility-limited\"\n : actions.length === 0\n ? \"clean across completed checks\"\n : hasActionableFinding\n ? \"attention needed\"\n : \"review suggested\";\n const cssSummary = summary.cssScore === null\n ? reportData.failedChecks.includes(\"css\")\n ? \"CSS validation unavailable\"\n : compatibilityLimited\n ? `${countLabel(summary.cssErrors, \"CSS error\")}, including ${countLabel(summary.cssCompatibilityLimitations, \"known validator limitation\")}`\n : \"CSS not audited\"\n : countLabel(summary.cssErrors, \"CSS error\");\n const redirects = reportData.links.filter(isRedirect).length;\n const linkSummary = summary.linkScore === null\n ? reportData.failedChecks.includes(\"links\")\n ? \"link checking unavailable\"\n : \"no eligible links checked\"\n : `${summary.brokenLinks} broken or unreachable and ${countLabel(redirects, \"redirect\")} to review of ${countLabel(summary.linksChecked, \"link\")}`;\n const checkLabels = {\n input: \"input file\",\n html: \"HTML validation\",\n css: \"CSS validation\",\n seo: \"SEO analysis\",\n schema: \"JSON-LD analysis\",\n links: \"link checking\",\n } as const;\n const unavailableChecks = reportData.failedChecks\n .filter((check) => check !== \"input\")\n .map((check) => checkLabels[check]);\n return toolContent({\n title: \"Validation report\",\n status,\n outcome: partial\n ? `Partial validation report: ${unavailableChecks.join(\", \")} ${unavailableChecks.length === 1 ? \"was\" : \"were\"} unavailable; remaining checks completed. HTML has ${countLabel(summary.htmlErrors, \"error\")}; ${cssSummary}; SEO has ${countLabel(summary.seoErrors, \"error\")}; JSON-LD has ${countLabel(summary.schemaErrors, \"syntax error\")}; ${linkSummary}.`\n : compatibilityLimited\n ? `The overall heuristic score is withheld because CSS validation includes ${countLabel(summary.cssCompatibilityLimitations, \"known validator limitation\")}. HTML has ${countLabel(summary.htmlErrors, \"error\")} and ${countLabel(summary.htmlWarnings, \"warning\")}; ${cssSummary}; SEO has ${countLabel(summary.seoErrors, \"error\")} and ${countLabel(summary.seoWarnings, \"warning\")}; JSON-LD has ${countLabel(summary.schemaErrors, \"syntax error\")}; ${linkSummary}.`\n : `The report's heuristic overall score is **${summary.overallScore}/100**. HTML has ${countLabel(summary.htmlErrors, \"error\")} and ${countLabel(summary.htmlWarnings, \"warning\")}; ${cssSummary}; SEO has ${countLabel(summary.seoErrors, \"error\")} and ${countLabel(summary.seoWarnings, \"warning\")}; JSON-LD has ${countLabel(summary.schemaErrors, \"syntax error\")}; ${linkSummary}.`,\n actions,\n nextStep: partial\n ? \"Review the completed findings, then retry the unavailable checks.\"\n : compatibilityLimited\n ? \"Review actionable findings normally, and treat the marked @container diagnostic as an upstream validator limitation rather than proof of invalid CSS.\"\n : actions.length === 0\n ? \"Use the full Markdown report as the audit record and rerun it after meaningful page changes.\"\n : \"Work through these priorities, then regenerate the report to compare the heuristic score.\",\n note: partial\n ? \"No overall score is shown while one or more checks are unavailable.\"\n : compatibilityLimited\n ? \"CSS and overall scores are withheld while a known Jigsaw parser limitation is present; the original upstream diagnostic remains visible.\"\n : \"The score is a triage heuristic based on these checks, not a Lighthouse score or a search-ranking prediction.\",\n });\n}\n\nexport function screenshotCaptureContent(count: number, outputDirectory: string): string {\n return toolContent({\n title: \"Screenshot capture\",\n status: \"complete\",\n outcome: `Saved ${countLabel(count, \"PNG screenshot\")} to ${markdownCode(outputDirectory)}.`,\n nextStep: \"Open the PNG files and compare the rendered layouts at each requested viewport.\",\n });\n}\n\nexport function failureContent(title: string, error: string, nextStep: string): string {\n return toolContent({\n title,\n status: \"could not finish\",\n outcome: markdownText(error, 500),\n nextStep,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"presentation.js","sourceRoot":"","sources":["../src/presentation.ts"],"names":[],"mappings":"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","sourcesContent":["import * as cheerio from \"cheerio\";\nimport type { ValidationReport } from \"./report.js\";\nimport type { LinkStatus, SEOIssue } from \"./seo-auditor.js\";\nimport {\n getW3CMessageSeverity,\n type CSSMessage,\n type W3CMessage,\n} from \"./w3c-validator.js\";\n\ntype ActionPriority = 0 | 1 | 2;\n\ninterface ActionItem {\n message: string;\n priority: ActionPriority;\n location?: string;\n}\n\ninterface ValidationReportResult extends ValidationReport {\n errors?: string[];\n}\n\nfunction collapseWhitespace(value: string, maxLength = 240): string {\n const collapsed = value.replace(/\\s+/g, \" \").trim();\n if (collapsed.length <= maxLength) {\n return collapsed;\n }\n return `${collapsed.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\n/** Escapes untrusted text while leaving the surrounding, controlled Markdown intact. */\nfunction markdownText(value: string, maxLength = 240): string {\n return collapseWhitespace(value, maxLength)\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/([`*_[\\]{}()#+\\-.!|~])/g, \"\\\\$1\");\n}\n\n/** Wraps untrusted identifiers and paths in a code span that cannot be closed by their content. */\nfunction markdownCode(value: string, maxLength = 240): string {\n const collapsed = collapseWhitespace(value, maxLength);\n const longestBacktickRun = Math.max(\n 0,\n ...Array.from(collapsed.matchAll(/`+/g), (match) => match[0].length),\n );\n const delimiter = \"`\".repeat(longestBacktickRun + 1);\n const content = collapsed.startsWith(\"`\") || collapsed.endsWith(\"`\")\n ? ` ${collapsed} `\n : collapsed;\n return `${delimiter}${content}${delimiter}`;\n}\n\nfunction countLabel(count: number, singular: string, plural = `${singular}s`): string {\n return `${count} ${count === 1 ? singular : plural}`;\n}\n\nfunction priorityForSeverity(severity: string): ActionPriority {\n if (severity.toLowerCase() === \"error\") return 0;\n if (severity.toLowerCase() === \"warning\") return 1;\n return 2;\n}\n\nfunction formatLocation(line?: number, column?: number): string | undefined {\n if (line === undefined && column === undefined) return undefined;\n if (line === undefined) return `column ${column}`;\n if (column === undefined) return `line ${line}`;\n return `line ${line}, column ${column}`;\n}\n\nfunction formatActions(items: ActionItem[]): string | undefined {\n const prioritized = items\n .map((item, index) => ({ item, index }))\n .sort((left, right) => left.item.priority - right.item.priority || left.index - right.index)\n .slice(0, 3)\n .map(({ item }) => {\n const label = item.priority === 0 ? \"Error\" : item.priority === 1 ? \"Warning\" : \"Check\";\n const location = item.location\n ? ` · ${markdownCode(item.location, 120)}`\n : \"\";\n return `- **${label}**${location}: ${markdownText(item.message)}`;\n });\n\n return prioritized.length > 0 ? prioritized.join(\"\\n\") : undefined;\n}\n\nfunction toolContent(options: {\n title: string;\n status: string;\n outcome: string;\n nextStep: string;\n actions?: ActionItem[];\n note?: string;\n}): string {\n const sections = [\n `### ${options.title}: ${options.status}`,\n collapseWhitespace(options.outcome, 500),\n ];\n if (options.note) {\n sections.push(collapseWhitespace(options.note, 500));\n }\n const actionItems = options.actions ?? [];\n const actions = formatActions(actionItems);\n if (actions) {\n const hasActionableItem = actionItems.some((item) => item.priority < 2);\n const actionHeading = options.status === \"review suggested\" || !hasActionableItem ? \"Review\" : \"Fix first\";\n sections.push(`**${actionHeading}**\\n${actions}`);\n }\n sections.push(`**Next step:** ${collapseWhitespace(options.nextStep, 400)}`);\n return sections.join(\"\\n\\n\");\n}\n\nexport function htmlValidationContent(\n messages: W3CMessage[],\n source?: string,\n totalMessages = messages.length,\n counts?: { error: number; warning: number; info: number },\n): string {\n const errorCount = counts?.error\n ?? messages.filter((message) => getW3CMessageSeverity(message) === \"error\").length;\n const warningCount = counts?.warning\n ?? messages.filter((message) => getW3CMessageSeverity(message) === \"warning\").length;\n const infoCount = counts?.info ?? Math.max(0, totalMessages - errorCount - warningCount);\n const sourceText = source ? ` for ${markdownCode(source, 180)}` : \"\";\n if (totalMessages === 0) {\n return toolContent({\n title: \"HTML validation\",\n status: \"clean\",\n outcome: `The W3C validator returned no HTML diagnostics${sourceText}.`,\n nextStep: \"Keep this result as a baseline and validate again after the next markup change.\",\n });\n }\n\n const hasActionableDiagnostics = errorCount > 0 || warningCount > 0;\n return toolContent({\n title: \"HTML validation\",\n status: hasActionableDiagnostics ? \"attention needed\" : \"review suggested\",\n outcome: `The W3C validator returned ${countLabel(errorCount, \"error\")}, ${countLabel(warningCount, \"warning\")}, and ${countLabel(infoCount, \"informational diagnostic\")}${sourceText}.`,\n actions: messages.map((message) => ({\n priority: priorityForSeverity(getW3CMessageSeverity(message)),\n message: message.message,\n location: formatLocation(message.lastLine ?? message.firstLine, message.lastColumn ?? message.firstColumn),\n })),\n nextStep: hasActionableDiagnostics\n ? \"Fix errors first, then warnings, and rerun HTML validation to confirm the markup is clean.\"\n : \"Review the informational diagnostics, then rerun validation after relevant markup changes.\",\n note: totalMessages > messages.length\n ? `Showing the first ${messages.length} of ${totalMessages} HTML diagnostics in structured output; severity totals include all returned Nu diagnostics.`\n : undefined,\n });\n}\n\nexport function cssValidationContent(messages: CSSMessage[]): string {\n if (messages.length === 0) {\n return toolContent({\n title: \"CSS validation\",\n status: \"clean\",\n outcome: \"The W3C validator returned no CSS errors.\",\n nextStep: \"Validate again after the next stylesheet change.\",\n });\n }\n\n const compatibilityLimitations = messages.filter(\n (message) => message.compatibility === \"known-validator-limitation\",\n );\n const actionableErrors = messages.length - compatibilityLimitations.length;\n\n return toolContent({\n title: \"CSS validation\",\n status: actionableErrors > 0 ? \"attention needed\" : \"review suggested\",\n outcome: compatibilityLimitations.length > 0\n ? `The W3C validator returned ${countLabel(messages.length, \"CSS error\")}; ${countLabel(compatibilityLimitations.length, \"diagnostic\")} ${compatibilityLimitations.length === 1 ? \"matches\" : \"match\"} a known validator limitation.`\n : `The W3C validator returned ${countLabel(messages.length, \"CSS error\")}.`,\n actions: messages.map((message) => ({\n priority: message.compatibility === \"known-validator-limitation\" ? 2 : 0,\n message: message.context ? `${message.message} Context: ${message.context}` : message.message,\n location: formatLocation(message.line),\n })),\n nextStep: actionableErrors > 0\n ? \"Correct the actionable errors, then rerun CSS validation because one syntax issue can cause later diagnostics.\"\n : \"Review the marked @container diagnostic against current CSS specifications; do not treat it as invalid CSS by itself.\",\n note: compatibilityLimitations.length > 0\n ? \"Jigsaw currently does not recognize the standards-defined @container rule. The upstream diagnostic is preserved and marked as a known validator limitation.\"\n : undefined,\n });\n}\n\nexport function seoAuditContent(issues: SEOIssue[], totalIssues: number, truncated: boolean): string {\n const errors = issues.filter((issue) => issue.severity === \"error\").length;\n const warnings = issues.filter((issue) => issue.severity === \"warning\").length;\n const info = issues.filter((issue) => issue.severity === \"info\").length;\n if (totalIssues === 0) {\n return toolContent({\n title: \"SEO audit\",\n status: \"clean within this audit\",\n outcome: \"This focused rules-based audit found no SEO or accessibility issues in the supplied HTML.\",\n nextStep: \"Keep the metadata current and rerun the audit whenever the page template changes.\",\n note: \"This result does not replace a crawl, performance test, or Search Console review.\",\n });\n }\n\n const hasActionableFindings = errors > 0 || warnings > 0;\n return toolContent({\n title: \"SEO audit\",\n status: hasActionableFindings ? \"attention needed\" : \"review suggested\",\n outcome: `The audit found ${countLabel(errors, \"error\")}, ${countLabel(warnings, \"warning\")}, and ${countLabel(info, \"suggestion\")}.`,\n actions: issues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: issue.message,\n location: issue.element ? collapseWhitespace(issue.element, 120) : undefined,\n })),\n nextStep: hasActionableFindings\n ? \"Address errors first, then warnings, and rerun the audit after updating the page.\"\n : \"Review the suggestions that apply to this page, then rerun the audit after relevant template changes.\",\n note: truncated\n ? `Showing the first ${issues.length} of ${totalIssues} findings in structured output.`\n : undefined,\n });\n}\n\nfunction countJsonLdBlocks(htmlContent: string): number {\n const $ = cheerio.load(htmlContent);\n return $(\"script[type]\").filter((_, element) => {\n const type = $(element).attr(\"type\");\n return type?.split(\";\", 1)[0].trim().toLowerCase() === \"application/ld+json\";\n }).length;\n}\n\nexport function schemaValidationContent(\n issues: SEOIssue[],\n totalIssues: number,\n truncated: boolean,\n htmlContent: string,\n): string {\n const blockCount = countJsonLdBlocks(htmlContent);\n if (blockCount === 0) {\n return toolContent({\n title: \"JSON-LD syntax\",\n status: \"not present\",\n outcome: \"No JSON-LD script blocks were found, so there was no structured data to parse.\",\n nextStep: \"Add JSON-LD only when it accurately describes visible page content, then validate it again.\",\n });\n }\n if (totalIssues === 0) {\n return toolContent({\n title: \"JSON-LD syntax\",\n status: \"clean\",\n outcome: `${countLabel(blockCount, \"JSON-LD block\")} parsed without syntax errors.`,\n nextStep: \"Verify the properties against the relevant Schema.org type and search-engine requirements.\",\n note: \"This check covers JSON syntax only; it does not validate vocabulary semantics or rich-result eligibility.\",\n });\n }\n\n return toolContent({\n title: \"JSON-LD syntax\",\n status: \"attention needed\",\n outcome: `${countLabel(totalIssues, \"syntax issue\")} ${totalIssues === 1 ? \"was\" : \"were\"} found across ${countLabel(blockCount, \"JSON-LD block\")}.`,\n actions: issues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: issue.message,\n })),\n nextStep: \"Repair the invalid or empty blocks, then rerun this syntax check before testing rich-result eligibility.\",\n note: truncated\n ? `Showing the first ${issues.length} of ${totalIssues} findings in structured output.`\n : undefined,\n });\n}\n\nfunction linkPriority(link: LinkStatus): ActionPriority {\n if (isRedirect(link)) return 1;\n return 0;\n}\n\nfunction isRedirect(link: LinkStatus): boolean {\n return typeof link.status === \"number\" && link.status >= 300 && link.status < 400;\n}\n\nfunction linkActionMessage(link: LinkStatus): string {\n if (isRedirect(link)) {\n return `${link.status} redirect; destination was not followed`;\n }\n return `Link returned ${typeof link.status === \"number\" ? `HTTP ${link.status}` : link.status}${link.message ? ` — ${link.message}` : \"\"}`;\n}\n\nexport function linkCheckContent(links: LinkStatus[], baseUrl?: string): string {\n if (links.length === 0) {\n return toolContent({\n title: \"Link check\",\n status: \"nothing checked\",\n outcome: \"No eligible public HTTP(S) links were found, so no link requests were made.\",\n nextStep: baseUrl\n ? \"Confirm the HTML contains reachable anchor URLs, then run the check again.\"\n : \"If the page uses relative links, provide its public base URL and run the check again.\",\n });\n }\n\n const unreachable = links.filter((link) => !link.ok);\n const redirects = links.filter(isRedirect);\n if (unreachable.length === 0 && redirects.length === 0) {\n return toolContent({\n title: \"Link check\",\n status: \"clean\",\n outcome: links.length === 1\n ? \"The checked link returned a successful response.\"\n : `All ${links.length} checked links returned a successful response.`,\n nextStep: \"Recheck periodically because external link availability can change.\",\n });\n }\n\n const actions = [\n ...unreachable,\n ...redirects,\n ].map((link) => ({\n priority: linkPriority(link),\n location: link.url,\n message: linkActionMessage(link),\n }));\n\n const linkOutcome = [\n unreachable.length > 0\n ? `${countLabel(unreachable.length, \"link\")} ${unreachable.length === 1 ? \"is\" : \"are\"} broken or unreachable`\n : \"no checked links are broken or unreachable\",\n redirects.length > 0\n ? `${countLabel(redirects.length, \"redirect\")} ${redirects.length === 1 ? \"needs\" : \"need\"} review`\n : undefined,\n ].filter((item): item is string => item !== undefined).join(\"; \");\n\n return toolContent({\n title: \"Link check\",\n status: unreachable.length > 0 ? \"attention needed\" : \"review suggested\",\n outcome: `${linkOutcome} of ${links.length} checked. Redirect destinations are not followed.`,\n actions,\n nextStep: \"Update failed destinations and review redirects, then rerun the link check.\",\n });\n}\n\nfunction reportActionItems(reportData: ValidationReportResult): ActionItem[] {\n return [\n ...(reportData.errors ?? []).map((message) => ({\n priority: 0 as const,\n message,\n })),\n ...reportData.htmlMessages.map((message) => ({\n priority: priorityForSeverity(getW3CMessageSeverity(message)),\n message: `HTML: ${message.message}`,\n location: formatLocation(message.lastLine ?? message.firstLine, message.lastColumn ?? message.firstColumn),\n })),\n ...reportData.cssMessages.map((message) => ({\n priority: message.compatibility === \"known-validator-limitation\" ? 2 as const : 0 as const,\n message: `CSS: ${message.message}`,\n location: formatLocation(message.line),\n })),\n ...reportData.seoIssues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: `${issue.category}: ${issue.message}`,\n location: issue.element ? collapseWhitespace(issue.element, 120) : undefined,\n })),\n ...reportData.schemaIssues.map((issue) => ({\n priority: priorityForSeverity(issue.severity),\n message: `JSON-LD: ${issue.message}`,\n })),\n ...reportData.links\n .filter((link) => !link.ok || isRedirect(link))\n .map((link) => ({\n priority: linkPriority(link),\n message: linkActionMessage(link),\n location: link.url,\n })),\n ];\n}\n\nexport function reportContent(reportData: ValidationReportResult): string {\n const { summary } = reportData;\n const actions = reportActionItems(reportData);\n const partial = reportData.failedChecks.length > 0;\n const compatibilityLimited = summary.cssCompatibilityLimitations > 0;\n const hasActionableFinding = actions.some((action) => action.priority < 2);\n const status = partial\n ? \"partial\"\n : compatibilityLimited\n ? \"compatibility-limited\"\n : actions.length === 0\n ? \"clean across completed checks\"\n : hasActionableFinding\n ? \"attention needed\"\n : \"review suggested\";\n const cssSummary = summary.cssScore === null\n ? reportData.failedChecks.includes(\"css\")\n ? \"CSS validation unavailable\"\n : compatibilityLimited\n ? `${countLabel(summary.cssErrors, \"CSS error\")}; ${countLabel(summary.cssCompatibilityLimitations, \"known validator limitation\")}`\n : \"CSS not audited\"\n : countLabel(summary.cssErrors, \"CSS error\");\n const redirects = reportData.links.filter(isRedirect).length;\n const linkSummary = summary.linkScore === null\n ? reportData.failedChecks.includes(\"links\")\n ? \"link checking unavailable\"\n : \"no eligible links checked\"\n : `${summary.brokenLinks} broken or unreachable and ${countLabel(redirects, \"redirect\")} to review of ${countLabel(summary.linksChecked, \"link\")}`;\n const checkLabels = {\n input: \"input file\",\n html: \"HTML validation\",\n css: \"CSS validation\",\n seo: \"SEO analysis\",\n schema: \"JSON-LD analysis\",\n links: \"link checking\",\n } as const;\n const unavailableChecks = reportData.failedChecks\n .filter((check) => check !== \"input\")\n .map((check) => checkLabels[check]);\n const scoreNote = \"The score is a triage heuristic based on these checks, not a Lighthouse score or a search-ranking prediction.\";\n const truncationNote = reportData.htmlTruncated\n ? `Showing the first ${reportData.htmlMessages.length} of ${reportData.htmlTotalMessages} HTML diagnostics; HTML summary counts and scoring include all returned Nu diagnostics.`\n : undefined;\n return toolContent({\n title: \"Validation report\",\n status,\n outcome: partial\n ? `Partial validation report: ${unavailableChecks.join(\", \")} ${unavailableChecks.length === 1 ? \"was\" : \"were\"} unavailable; remaining checks completed. HTML has ${countLabel(summary.htmlErrors, \"error\")}; ${cssSummary}; SEO has ${countLabel(summary.seoErrors, \"error\")}; JSON-LD has ${countLabel(summary.schemaErrors, \"syntax error\")}; ${linkSummary}.`\n : compatibilityLimited\n ? `The overall heuristic score is withheld because CSS validation includes ${countLabel(summary.cssCompatibilityLimitations, \"known validator limitation\")}. HTML has ${countLabel(summary.htmlErrors, \"error\")} and ${countLabel(summary.htmlWarnings, \"warning\")}; ${cssSummary}; SEO has ${countLabel(summary.seoErrors, \"error\")} and ${countLabel(summary.seoWarnings, \"warning\")}; JSON-LD has ${countLabel(summary.schemaErrors, \"syntax error\")}; ${linkSummary}.`\n : `The report's heuristic overall score is **${summary.overallScore}/100**. HTML has ${countLabel(summary.htmlErrors, \"error\")} and ${countLabel(summary.htmlWarnings, \"warning\")}; ${cssSummary}; SEO has ${countLabel(summary.seoErrors, \"error\")} and ${countLabel(summary.seoWarnings, \"warning\")}; JSON-LD has ${countLabel(summary.schemaErrors, \"syntax error\")}; ${linkSummary}.`,\n actions,\n nextStep: partial\n ? \"Review the completed findings, then retry the unavailable checks.\"\n : compatibilityLimited\n ? \"Review actionable findings normally, and treat the marked @container diagnostic as an upstream validator limitation rather than proof of invalid CSS.\"\n : actions.length === 0\n ? \"Use the full Markdown report as the audit record and rerun it after meaningful page changes.\"\n : \"Work through these priorities, then regenerate the report to compare the heuristic score.\",\n note: partial\n ? [\"No overall score is shown while one or more checks are unavailable.\", truncationNote].filter(Boolean).join(\" \")\n : compatibilityLimited\n ? [\"CSS and overall scores are withheld while a known Jigsaw parser limitation is present; the original upstream diagnostic remains visible.\", truncationNote].filter(Boolean).join(\" \")\n : [scoreNote, truncationNote].filter(Boolean).join(\" \"),\n });\n}\n\nexport function screenshotCaptureContent(count: number, outputDirectory: string): string {\n return toolContent({\n title: \"Screenshot capture\",\n status: \"complete\",\n outcome: `Saved ${countLabel(count, \"PNG screenshot\")} to ${markdownCode(outputDirectory)}.`,\n nextStep: \"Open the PNG files and compare the rendered layouts at each requested viewport.\",\n });\n}\n\nexport function failureContent(title: string, error: string, nextStep: string): string {\n return toolContent({\n title,\n status: \"could not finish\",\n outcome: markdownText(error, 500),\n nextStep,\n });\n}\n"]}
|
package/dist/report.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface ValidationReport {
|
|
|
22
22
|
report: string;
|
|
23
23
|
summary: ValidationReportSummary;
|
|
24
24
|
htmlMessages: W3CMessage[];
|
|
25
|
+
htmlTotalMessages: number;
|
|
26
|
+
htmlTruncated: boolean;
|
|
25
27
|
cssMessages: CSSMessage[];
|
|
26
28
|
seoIssues: SEOIssue[];
|
|
27
29
|
schemaIssues: SEOIssue[];
|
|
@@ -33,6 +35,13 @@ export interface ValidationReportInput {
|
|
|
33
35
|
htmlFilePath: string;
|
|
34
36
|
cssAudited: boolean;
|
|
35
37
|
htmlMessages: W3CMessage[];
|
|
38
|
+
htmlTotalMessages?: number;
|
|
39
|
+
htmlTruncated?: boolean;
|
|
40
|
+
htmlCounts?: {
|
|
41
|
+
error: number;
|
|
42
|
+
warning: number;
|
|
43
|
+
info: number;
|
|
44
|
+
};
|
|
36
45
|
cssMessages: CSSMessage[];
|
|
37
46
|
seoIssues: SEOIssue[];
|
|
38
47
|
schemaIssues: SEOIssue[];
|
package/dist/report.js
CHANGED
|
@@ -28,10 +28,14 @@ function isRedirect(link) {
|
|
|
28
28
|
export function createValidationReport(input) {
|
|
29
29
|
const failedChecks = input.failedChecks ?? [];
|
|
30
30
|
const checkFailed = (check) => failedChecks.includes(check);
|
|
31
|
-
const htmlErrors = input.
|
|
32
|
-
|
|
33
|
-
const
|
|
31
|
+
const htmlErrors = input.htmlCounts?.error
|
|
32
|
+
?? input.htmlMessages.filter((message) => getW3CMessageSeverity(message) === "error").length;
|
|
33
|
+
const htmlWarnings = input.htmlCounts?.warning
|
|
34
|
+
?? input.htmlMessages.filter((message) => getW3CMessageSeverity(message) === "warning").length;
|
|
35
|
+
const htmlTotalMessages = input.htmlTotalMessages ?? input.htmlMessages.length;
|
|
36
|
+
const htmlTruncated = input.htmlTruncated ?? htmlTotalMessages > input.htmlMessages.length;
|
|
34
37
|
const cssCompatibilityLimitations = input.cssMessages.filter((message) => message.compatibility === "known-validator-limitation").length;
|
|
38
|
+
const cssErrors = input.cssMessages.length - cssCompatibilityLimitations;
|
|
35
39
|
const seoErrors = input.seoIssues.filter((issue) => issue.severity === "error").length;
|
|
36
40
|
const seoWarnings = input.seoIssues.filter((issue) => issue.severity === "warning").length;
|
|
37
41
|
const schemaErrors = input.schemaIssues.filter((issue) => issue.severity === "error").length;
|
|
@@ -88,7 +92,7 @@ export function createValidationReport(input) {
|
|
|
88
92
|
`- JSON-LD syntax: ${schemaErrors} error(s)`,
|
|
89
93
|
`- Links: ${brokenLinks} broken or unreachable, ${redirectLinks} redirect${redirectLinks === 1 ? "" : "s"} to review of ${input.links.length} checked`,
|
|
90
94
|
"",
|
|
91
|
-
`## HTML diagnostics (${input.htmlMessages.length})`,
|
|
95
|
+
`## HTML diagnostics (${htmlTruncated ? `${input.htmlMessages.length} shown of ${htmlTotalMessages}` : htmlTotalMessages})`,
|
|
92
96
|
];
|
|
93
97
|
if (failedChecks.length > 0) {
|
|
94
98
|
report.splice(2, 0, "", "## Partial validation report", "The following checks were unavailable: " + failedChecks.join(", ") + ". Remaining checks completed.");
|
|
@@ -97,6 +101,9 @@ export function createValidationReport(input) {
|
|
|
97
101
|
report.push("No HTML validation diagnostics were returned.");
|
|
98
102
|
}
|
|
99
103
|
else {
|
|
104
|
+
if (htmlTruncated) {
|
|
105
|
+
report.push(`The structured HTML diagnostics are capped; showing the first ${input.htmlMessages.length} of ${htmlTotalMessages}. Summary counts and scoring use all returned Nu diagnostics.`);
|
|
106
|
+
}
|
|
100
107
|
report.push("", "| Line | Column | Severity | Message | Extract |", "| :---: | :---: | :--- | :--- | :--- |");
|
|
101
108
|
for (const message of input.htmlMessages) {
|
|
102
109
|
report.push(`| ${message.lastLine ?? "N/A"} | ${message.lastColumn ?? "N/A"} | ${markdownCell(getW3CMessageSeverity(message))} | ${markdownCell(message.message)} | ${markdownCell(message.extract ?? "N/A")} |`);
|
|
@@ -142,6 +149,8 @@ export function createValidationReport(input) {
|
|
|
142
149
|
report: report.join("\n"),
|
|
143
150
|
summary,
|
|
144
151
|
htmlMessages: input.htmlMessages,
|
|
152
|
+
htmlTotalMessages,
|
|
153
|
+
htmlTruncated,
|
|
145
154
|
cssMessages: input.cssMessages,
|
|
146
155
|
seoIssues: input.seoIssues,
|
|
147
156
|
schemaIssues: input.schemaIssues,
|
package/dist/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from \"node:path\";\nimport {\n getW3CMessageSeverity,\n type CSSMessage,\n type W3CMessage,\n} from \"./w3c-validator.js\";\nimport type { LinkStatus, SEOIssue } from \"./seo-auditor.js\";\n\nexport const validationReportChecks = [\"input\", \"html\", \"css\", \"seo\", \"schema\", \"links\"] as const;\nexport type ValidationReportCheck = (typeof validationReportChecks)[number];\n\nexport interface ValidationReportSummary {\n overallScore: number | null;\n htmlScore: number | null;\n cssScore: number | null;\n seoScore: number | null;\n linkScore: number | null;\n htmlErrors: number;\n htmlWarnings: number;\n cssErrors: number;\n cssCompatibilityLimitations: number;\n seoErrors: number;\n seoWarnings: number;\n schemaErrors: number;\n linksChecked: number;\n brokenLinks: number;\n}\n\nexport interface ValidationReport {\n report: string;\n summary: ValidationReportSummary;\n htmlMessages: W3CMessage[];\n cssMessages: CSSMessage[];\n seoIssues: SEOIssue[];\n schemaIssues: SEOIssue[];\n links: LinkStatus[];\n failedChecks: ValidationReportCheck[];\n errors?: string[];\n}\n\nexport interface ValidationReportInput {\n htmlFilePath: string;\n cssAudited: boolean;\n htmlMessages: W3CMessage[];\n cssMessages: CSSMessage[];\n seoIssues: SEOIssue[];\n schemaIssues: SEOIssue[];\n links: LinkStatus[];\n failedChecks?: ValidationReportCheck[];\n errors?: string[];\n}\n\nfunction clampScore(score: number): number {\n return Math.max(0, Math.min(100, score));\n}\n\nfunction scoreIndicator(score: number | null): string {\n if (score === null) return \"Unavailable\";\n if (score >= 90) return \"🟢\";\n if (score >= 50) return \"🟠\";\n return \"🔴\";\n}\n\nfunction markdownCell(value: unknown): string {\n return String(value ?? \"\")\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\|/g, \"\\\\|\")\n .replace(/`/g, \"\\\\`\")\n .replace(/[\\r\\n]+/g, \" \")\n .trim();\n}\n\nfunction isRedirect(link: LinkStatus): boolean {\n return typeof link.status === \"number\" && link.status >= 300 && link.status < 400;\n}\n\n/** Builds the human-readable report and its machine-readable equivalent. */\nexport function createValidationReport(input: ValidationReportInput): ValidationReport {\n const failedChecks = input.failedChecks ?? [];\n const checkFailed = (check: ValidationReportCheck): boolean => failedChecks.includes(check);\n const htmlErrors = input.htmlMessages.filter((message) => getW3CMessageSeverity(message) === \"error\").length;\n const htmlWarnings = input.htmlMessages.filter((message) => getW3CMessageSeverity(message) === \"warning\").length;\n const cssErrors = input.cssMessages.length;\n const cssCompatibilityLimitations = input.cssMessages.filter(\n (message) => message.compatibility === \"known-validator-limitation\",\n ).length;\n const seoErrors = input.seoIssues.filter((issue) => issue.severity === \"error\").length;\n const seoWarnings = input.seoIssues.filter((issue) => issue.severity === \"warning\").length;\n const schemaErrors = input.schemaIssues.filter((issue) => issue.severity === \"error\").length;\n const brokenLinks = input.links.filter((link) => !link.ok).length;\n const redirectLinks = input.links.filter(isRedirect).length;\n\n const htmlScore = checkFailed(\"html\") ? null : clampScore(100 - htmlErrors * 15 - htmlWarnings * 2);\n const cssScore = checkFailed(\"css\") || !input.cssAudited || cssCompatibilityLimitations > 0\n ? null\n : clampScore(100 - cssErrors * 20);\n const seoScore = checkFailed(\"seo\") || checkFailed(\"schema\")\n ? null\n : clampScore(100 - seoErrors * 15 - seoWarnings * 4 - schemaErrors * 15);\n const linkScore = checkFailed(\"links\") || input.links.length === 0\n ? null\n : clampScore(100 - brokenLinks * 25);\n const auditedScores = [htmlScore, seoScore, cssScore, linkScore].filter(\n (score): score is number => score !== null,\n );\n const overallScore = failedChecks.length > 0 || cssCompatibilityLimitations > 0 || auditedScores.length === 0\n ? null\n : Math.round(auditedScores.reduce((total, score) => total + score, 0) / auditedScores.length);\n\n const summary: ValidationReportSummary = {\n overallScore,\n htmlScore,\n cssScore,\n seoScore,\n linkScore,\n htmlErrors,\n htmlWarnings,\n cssErrors,\n cssCompatibilityLimitations,\n seoErrors,\n seoWarnings,\n schemaErrors,\n linksChecked: input.links.length,\n brokenLinks,\n };\n\n const report: string[] = [\n `# Web Validation & SEO Audit Report — ${overallScore === null ? \"Partial\" : `${scoreIndicator(overallScore)} **${overallScore}**/100`}`,\n `*Generated for: \\`${markdownCell(path.basename(input.htmlFilePath))}\\`*`,\n \"\",\n \"## Page health scores\",\n \"\",\n \"| Audit | Status | Score |\",\n \"| :--- | :---: | :---: |\",\n `| W3C HTML validation | ${htmlScore === null ? \"Unavailable\" : scoreIndicator(htmlScore)} | ${htmlScore === null ? \"N/A\" : `**${htmlScore}** / 100`} |`,\n `| CSS validation | ${cssScore === null ? (checkFailed(\"css\") ? \"Unavailable\" : cssCompatibilityLimitations > 0 ? \"Compatibility-limited\" : \"Not audited\") : scoreIndicator(cssScore)} | ${cssScore === null ? \"N/A\" : `**${cssScore}** / 100`} |`,\n `| SEO and accessibility | ${seoScore === null ? \"Unavailable\" : scoreIndicator(seoScore)} | ${seoScore === null ? \"N/A\" : `**${seoScore}** / 100`} |`,\n `| Link integrity | ${linkScore === null ? (checkFailed(\"links\") ? \"Unavailable\" : \"No links checked\") : scoreIndicator(linkScore)} | ${linkScore === null ? \"N/A\" : `**${linkScore}** / 100`} |`,\n \"\",\n \"## Summary\",\n \"\",\n `- HTML: ${htmlErrors} error(s), ${htmlWarnings} warning(s)` ,\n `- CSS: ${input.cssAudited ? `${cssErrors} error(s)${cssCompatibilityLimitations > 0 ? `, ${cssCompatibilityLimitations} known validator limitation(s)` : \"\"}` : \"not audited\"}`,\n `- SEO and accessibility: ${seoErrors} error(s), ${seoWarnings} warning(s)`,\n `- JSON-LD syntax: ${schemaErrors} error(s)`,\n `- Links: ${brokenLinks} broken or unreachable, ${redirectLinks} redirect${redirectLinks === 1 ? \"\" : \"s\"} to review of ${input.links.length} checked`,\n \"\",\n `## HTML diagnostics (${input.htmlMessages.length})`,\n ];\n\n if (failedChecks.length > 0) {\n report.splice(2, 0, \"\", \"## Partial validation report\", \"The following checks were unavailable: \" + failedChecks.join(\", \") + \". Remaining checks completed.\");\n }\n\n if (input.htmlMessages.length === 0) {\n report.push(\"No HTML validation diagnostics were returned.\");\n } else {\n report.push(\"\", \"| Line | Column | Severity | Message | Extract |\", \"| :---: | :---: | :--- | :--- | :--- |\");\n for (const message of input.htmlMessages) {\n report.push(\n `| ${message.lastLine ?? \"N/A\"} | ${message.lastColumn ?? \"N/A\"} | ${markdownCell(getW3CMessageSeverity(message))} | ${markdownCell(message.message)} | ${markdownCell(message.extract ?? \"N/A\")} |`,\n );\n }\n }\n\n if (input.cssAudited) {\n report.push(\"\", `## CSS diagnostics (${input.cssMessages.length})`);\n if (input.cssMessages.length === 0) {\n report.push(\"No CSS validation errors were returned.\");\n } else {\n report.push(\"\", \"| Line | Context | Message |\", \"| :---: | :--- | :--- |\");\n for (const message of input.cssMessages) {\n report.push(\n `| ${message.line} | ${markdownCell(message.context ?? \"N/A\")} | ${markdownCell(message.message)} |`,\n );\n }\n if (cssCompatibilityLimitations > 0) {\n report.push(\n \"\",\n `${cssCompatibilityLimitations} known validator limitation(s) match Jigsaw's current parser gap for the standards-defined \\`@container\\` rule. The original Jigsaw diagnostic is preserved, but CSS and overall scores are withheld because this upstream limitation can report valid modern CSS as invalid.`,\n );\n }\n }\n }\n\n const combinedIssues = [...input.seoIssues, ...input.schemaIssues];\n report.push(\"\", `## SEO, accessibility, and JSON-LD findings (${combinedIssues.length})`);\n if (combinedIssues.length === 0) {\n report.push(\"No SEO, accessibility, or JSON-LD syntax findings were returned.\");\n } else {\n report.push(\"\", \"| Category | Severity | Message | Element |\", \"| :--- | :--- | :--- | :--- |\");\n for (const issue of combinedIssues) {\n report.push(\n `| ${markdownCell(issue.category)} | ${markdownCell(issue.severity)} | ${markdownCell(issue.message)} | ${markdownCell(issue.element ?? \"N/A\")} |`,\n );\n }\n }\n\n report.push(\"\", `## Link health (${input.links.length} checked)`);\n if (input.links.length === 0) {\n report.push(\"No public HTTP(S) links were checked.\");\n } else {\n report.push(\"\", \"| URL | Status | Reachable | Details |\", \"| :--- | :---: | :---: | :--- |\");\n for (const link of input.links) {\n report.push(\n `| ${markdownCell(link.url)} | ${markdownCell(link.status)} | ${link.ok ? \"Yes\" : \"No\"} | ${markdownCell(link.message ?? \"Accessible\")} |`,\n );\n }\n }\n\n return {\n report: report.join(\"\\n\"),\n summary,\n htmlMessages: input.htmlMessages,\n cssMessages: input.cssMessages,\n seoIssues: input.seoIssues,\n schemaIssues: input.schemaIssues,\n links: input.links,\n failedChecks,\n ...(input.errors && input.errors.length > 0 ? { errors: input.errors } : {}),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EACL,qBAAqB,GAGtB,MAAM,oBAAoB,CAAC;AAG5B,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAiDlG,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,cAAc,CAAC,KAAoB;IAC1C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,aAAa,CAAC;IACzC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,IAAgB;IAClC,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;AACpF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,sBAAsB,CAAC,KAA4B;IACjE,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;IAC9C,MAAM,WAAW,GAAG,CAAC,KAA4B,EAAW,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5F,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,EAAE,KAAK;WACrC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,qBAAqB,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC/F,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,EAAE,OAAO;WACzC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,qBAAqB,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACjG,MAAM,iBAAiB,GAAG,KAAK,CAAC,iBAAiB,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;IAC/E,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;IAC3F,MAAM,2BAA2B,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAC1D,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,KAAK,4BAA4B,CACpE,CAAC,MAAM,CAAC;IACT,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,2BAA2B,CAAC;IACzE,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACvF,MAAM,WAAW,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAC3F,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC7F,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;IAClE,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;IAE5D,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,UAAU,GAAG,EAAE,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC;IACpG,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,2BAA2B,GAAG,CAAC;QACzF,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC;QAC1D,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,SAAS,GAAG,EAAE,GAAG,WAAW,GAAG,CAAC,GAAG,YAAY,GAAG,EAAE,CAAC,CAAC;IAC3E,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAChE,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,WAAW,GAAG,EAAE,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,MAAM,CACrE,CAAC,KAAK,EAAmB,EAAE,CAAC,KAAK,KAAK,IAAI,CAC3C,CAAC;IACF,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,2BAA2B,GAAG,CAAC,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;QAC3G,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAEhG,MAAM,OAAO,GAA4B;QACvC,YAAY;QACZ,SAAS;QACT,QAAQ;QACR,QAAQ;QACR,SAAS;QACT,UAAU;QACV,YAAY;QACZ,SAAS;QACT,2BAA2B;QAC3B,SAAS;QACT,WAAW;QACX,YAAY;QACZ,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;QAChC,WAAW;KACZ,CAAC;IAEF,MAAM,MAAM,GAAa;QACvB,yCAAyC,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,YAAY,CAAC,MAAM,YAAY,QAAQ,EAAE;QACxI,qBAAqB,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,KAAK;QACzE,EAAE;QACF,uBAAuB;QACvB,EAAE;QACF,4BAA4B;QAC5B,0BAA0B;QAC1B,2BAA2B,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,UAAU,IAAI;QACxJ,sBAAsB,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,2BAA2B,GAAG,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,UAAU,IAAI;QAClP,6BAA6B,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,UAAU,IAAI;QACtJ,sBAAsB,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,UAAU,IAAI;QACjM,EAAE;QACF,YAAY;QACZ,EAAE;QACF,WAAW,UAAU,cAAc,YAAY,aAAa;QAC5D,UAAU,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,YAAY,2BAA2B,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,2BAA2B,gCAAgC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE;QAChL,4BAA4B,SAAS,cAAc,WAAW,aAAa;QAC3E,qBAAqB,YAAY,WAAW;QAC5C,YAAY,WAAW,2BAA2B,aAAa,YAAY,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,iBAAiB,KAAK,CAAC,KAAK,CAAC,MAAM,UAAU;QACtJ,EAAE;QACF,wBAAwB,aAAa,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,aAAa,iBAAiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,GAAG;KAC5H,CAAC;IAEF,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,8BAA8B,EAAE,yCAAyC,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,+BAA+B,CAAC,CAAC;IACjK,CAAC;IAED,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAC/D,CAAC;SAAM,CAAC;QACN,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,iEAAiE,KAAK,CAAC,YAAY,CAAC,MAAM,OAAO,iBAAiB,+DAA+D,CAAC,CAAC;QACjM,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,kDAAkD,EAAE,wCAAwC,CAAC,CAAC;QAC9G,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CACT,KAAK,OAAO,CAAC,QAAQ,IAAI,KAAK,MAAM,OAAO,CAAC,UAAU,IAAI,KAAK,MAAM,YAAY,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CACrM,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,uBAAuB,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;QACpE,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,8BAA8B,EAAE,yBAAyB,CAAC,CAAC;YAC3E,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;gBACxC,MAAM,CAAC,IAAI,CACT,KAAK,OAAO,CAAC,IAAI,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CACrG,CAAC;YACJ,CAAC;YACD,IAAI,2BAA2B,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,CAAC,IAAI,CACT,EAAE,EACF,GAAG,2BAA2B,+QAA+Q,CAC9S,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,gDAAgD,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1F,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;IAClF,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,6CAA6C,EAAE,+BAA+B,CAAC,CAAC;QAChG,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CACT,KAAK,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,YAAY,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CACnJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,mBAAmB,KAAK,CAAC,KAAK,CAAC,MAAM,WAAW,CAAC,CAAC;IAClE,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,wCAAwC,EAAE,iCAAiC,CAAC,CAAC;QAC7F,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CACT,KAAK,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,MAAM,YAAY,CAAC,IAAI,CAAC,OAAO,IAAI,YAAY,CAAC,IAAI,CAC3I,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,OAAO;QACP,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,iBAAiB;QACjB,aAAa;QACb,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,YAAY;QACZ,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7E,CAAC;AACJ,CAAC","sourcesContent":["import * as path from \"node:path\";\nimport {\n getW3CMessageSeverity,\n type CSSMessage,\n type W3CMessage,\n} from \"./w3c-validator.js\";\nimport type { LinkStatus, SEOIssue } from \"./seo-auditor.js\";\n\nexport const validationReportChecks = [\"input\", \"html\", \"css\", \"seo\", \"schema\", \"links\"] as const;\nexport type ValidationReportCheck = (typeof validationReportChecks)[number];\n\nexport interface ValidationReportSummary {\n overallScore: number | null;\n htmlScore: number | null;\n cssScore: number | null;\n seoScore: number | null;\n linkScore: number | null;\n htmlErrors: number;\n htmlWarnings: number;\n cssErrors: number;\n cssCompatibilityLimitations: number;\n seoErrors: number;\n seoWarnings: number;\n schemaErrors: number;\n linksChecked: number;\n brokenLinks: number;\n}\n\nexport interface ValidationReport {\n report: string;\n summary: ValidationReportSummary;\n htmlMessages: W3CMessage[];\n htmlTotalMessages: number;\n htmlTruncated: boolean;\n cssMessages: CSSMessage[];\n seoIssues: SEOIssue[];\n schemaIssues: SEOIssue[];\n links: LinkStatus[];\n failedChecks: ValidationReportCheck[];\n errors?: string[];\n}\n\nexport interface ValidationReportInput {\n htmlFilePath: string;\n cssAudited: boolean;\n htmlMessages: W3CMessage[];\n htmlTotalMessages?: number;\n htmlTruncated?: boolean;\n htmlCounts?: { error: number; warning: number; info: number };\n cssMessages: CSSMessage[];\n seoIssues: SEOIssue[];\n schemaIssues: SEOIssue[];\n links: LinkStatus[];\n failedChecks?: ValidationReportCheck[];\n errors?: string[];\n}\n\nfunction clampScore(score: number): number {\n return Math.max(0, Math.min(100, score));\n}\n\nfunction scoreIndicator(score: number | null): string {\n if (score === null) return \"Unavailable\";\n if (score >= 90) return \"🟢\";\n if (score >= 50) return \"🟠\";\n return \"🔴\";\n}\n\nfunction markdownCell(value: unknown): string {\n return String(value ?? \"\")\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\|/g, \"\\\\|\")\n .replace(/`/g, \"\\\\`\")\n .replace(/[\\r\\n]+/g, \" \")\n .trim();\n}\n\nfunction isRedirect(link: LinkStatus): boolean {\n return typeof link.status === \"number\" && link.status >= 300 && link.status < 400;\n}\n\n/** Builds the human-readable report and its machine-readable equivalent. */\nexport function createValidationReport(input: ValidationReportInput): ValidationReport {\n const failedChecks = input.failedChecks ?? [];\n const checkFailed = (check: ValidationReportCheck): boolean => failedChecks.includes(check);\n const htmlErrors = input.htmlCounts?.error\n ?? input.htmlMessages.filter((message) => getW3CMessageSeverity(message) === \"error\").length;\n const htmlWarnings = input.htmlCounts?.warning\n ?? input.htmlMessages.filter((message) => getW3CMessageSeverity(message) === \"warning\").length;\n const htmlTotalMessages = input.htmlTotalMessages ?? input.htmlMessages.length;\n const htmlTruncated = input.htmlTruncated ?? htmlTotalMessages > input.htmlMessages.length;\n const cssCompatibilityLimitations = input.cssMessages.filter(\n (message) => message.compatibility === \"known-validator-limitation\",\n ).length;\n const cssErrors = input.cssMessages.length - cssCompatibilityLimitations;\n const seoErrors = input.seoIssues.filter((issue) => issue.severity === \"error\").length;\n const seoWarnings = input.seoIssues.filter((issue) => issue.severity === \"warning\").length;\n const schemaErrors = input.schemaIssues.filter((issue) => issue.severity === \"error\").length;\n const brokenLinks = input.links.filter((link) => !link.ok).length;\n const redirectLinks = input.links.filter(isRedirect).length;\n\n const htmlScore = checkFailed(\"html\") ? null : clampScore(100 - htmlErrors * 15 - htmlWarnings * 2);\n const cssScore = checkFailed(\"css\") || !input.cssAudited || cssCompatibilityLimitations > 0\n ? null\n : clampScore(100 - cssErrors * 20);\n const seoScore = checkFailed(\"seo\") || checkFailed(\"schema\")\n ? null\n : clampScore(100 - seoErrors * 15 - seoWarnings * 4 - schemaErrors * 15);\n const linkScore = checkFailed(\"links\") || input.links.length === 0\n ? null\n : clampScore(100 - brokenLinks * 25);\n const auditedScores = [htmlScore, seoScore, cssScore, linkScore].filter(\n (score): score is number => score !== null,\n );\n const overallScore = failedChecks.length > 0 || cssCompatibilityLimitations > 0 || auditedScores.length === 0\n ? null\n : Math.round(auditedScores.reduce((total, score) => total + score, 0) / auditedScores.length);\n\n const summary: ValidationReportSummary = {\n overallScore,\n htmlScore,\n cssScore,\n seoScore,\n linkScore,\n htmlErrors,\n htmlWarnings,\n cssErrors,\n cssCompatibilityLimitations,\n seoErrors,\n seoWarnings,\n schemaErrors,\n linksChecked: input.links.length,\n brokenLinks,\n };\n\n const report: string[] = [\n `# Web Validation & SEO Audit Report — ${overallScore === null ? \"Partial\" : `${scoreIndicator(overallScore)} **${overallScore}**/100`}`,\n `*Generated for: \\`${markdownCell(path.basename(input.htmlFilePath))}\\`*`,\n \"\",\n \"## Page health scores\",\n \"\",\n \"| Audit | Status | Score |\",\n \"| :--- | :---: | :---: |\",\n `| W3C HTML validation | ${htmlScore === null ? \"Unavailable\" : scoreIndicator(htmlScore)} | ${htmlScore === null ? \"N/A\" : `**${htmlScore}** / 100`} |`,\n `| CSS validation | ${cssScore === null ? (checkFailed(\"css\") ? \"Unavailable\" : cssCompatibilityLimitations > 0 ? \"Compatibility-limited\" : \"Not audited\") : scoreIndicator(cssScore)} | ${cssScore === null ? \"N/A\" : `**${cssScore}** / 100`} |`,\n `| SEO and accessibility | ${seoScore === null ? \"Unavailable\" : scoreIndicator(seoScore)} | ${seoScore === null ? \"N/A\" : `**${seoScore}** / 100`} |`,\n `| Link integrity | ${linkScore === null ? (checkFailed(\"links\") ? \"Unavailable\" : \"No links checked\") : scoreIndicator(linkScore)} | ${linkScore === null ? \"N/A\" : `**${linkScore}** / 100`} |`,\n \"\",\n \"## Summary\",\n \"\",\n `- HTML: ${htmlErrors} error(s), ${htmlWarnings} warning(s)` ,\n `- CSS: ${input.cssAudited ? `${cssErrors} error(s)${cssCompatibilityLimitations > 0 ? `, ${cssCompatibilityLimitations} known validator limitation(s)` : \"\"}` : \"not audited\"}`,\n `- SEO and accessibility: ${seoErrors} error(s), ${seoWarnings} warning(s)`,\n `- JSON-LD syntax: ${schemaErrors} error(s)`,\n `- Links: ${brokenLinks} broken or unreachable, ${redirectLinks} redirect${redirectLinks === 1 ? \"\" : \"s\"} to review of ${input.links.length} checked`,\n \"\",\n `## HTML diagnostics (${htmlTruncated ? `${input.htmlMessages.length} shown of ${htmlTotalMessages}` : htmlTotalMessages})`,\n ];\n\n if (failedChecks.length > 0) {\n report.splice(2, 0, \"\", \"## Partial validation report\", \"The following checks were unavailable: \" + failedChecks.join(\", \") + \". Remaining checks completed.\");\n }\n\n if (input.htmlMessages.length === 0) {\n report.push(\"No HTML validation diagnostics were returned.\");\n } else {\n if (htmlTruncated) {\n report.push(`The structured HTML diagnostics are capped; showing the first ${input.htmlMessages.length} of ${htmlTotalMessages}. Summary counts and scoring use all returned Nu diagnostics.`);\n }\n report.push(\"\", \"| Line | Column | Severity | Message | Extract |\", \"| :---: | :---: | :--- | :--- | :--- |\");\n for (const message of input.htmlMessages) {\n report.push(\n `| ${message.lastLine ?? \"N/A\"} | ${message.lastColumn ?? \"N/A\"} | ${markdownCell(getW3CMessageSeverity(message))} | ${markdownCell(message.message)} | ${markdownCell(message.extract ?? \"N/A\")} |`,\n );\n }\n }\n\n if (input.cssAudited) {\n report.push(\"\", `## CSS diagnostics (${input.cssMessages.length})`);\n if (input.cssMessages.length === 0) {\n report.push(\"No CSS validation errors were returned.\");\n } else {\n report.push(\"\", \"| Line | Context | Message |\", \"| :---: | :--- | :--- |\");\n for (const message of input.cssMessages) {\n report.push(\n `| ${message.line} | ${markdownCell(message.context ?? \"N/A\")} | ${markdownCell(message.message)} |`,\n );\n }\n if (cssCompatibilityLimitations > 0) {\n report.push(\n \"\",\n `${cssCompatibilityLimitations} known validator limitation(s) match Jigsaw's current parser gap for the standards-defined \\`@container\\` rule. The original Jigsaw diagnostic is preserved, but CSS and overall scores are withheld because this upstream limitation can report valid modern CSS as invalid.`,\n );\n }\n }\n }\n\n const combinedIssues = [...input.seoIssues, ...input.schemaIssues];\n report.push(\"\", `## SEO, accessibility, and JSON-LD findings (${combinedIssues.length})`);\n if (combinedIssues.length === 0) {\n report.push(\"No SEO, accessibility, or JSON-LD syntax findings were returned.\");\n } else {\n report.push(\"\", \"| Category | Severity | Message | Element |\", \"| :--- | :--- | :--- | :--- |\");\n for (const issue of combinedIssues) {\n report.push(\n `| ${markdownCell(issue.category)} | ${markdownCell(issue.severity)} | ${markdownCell(issue.message)} | ${markdownCell(issue.element ?? \"N/A\")} |`,\n );\n }\n }\n\n report.push(\"\", `## Link health (${input.links.length} checked)`);\n if (input.links.length === 0) {\n report.push(\"No public HTTP(S) links were checked.\");\n } else {\n report.push(\"\", \"| URL | Status | Reachable | Details |\", \"| :--- | :---: | :---: | :--- |\");\n for (const link of input.links) {\n report.push(\n `| ${markdownCell(link.url)} | ${markdownCell(link.status)} | ${link.ok ? \"Yes\" : \"No\"} | ${markdownCell(link.message ?? \"Accessible\")} |`,\n );\n }\n }\n\n return {\n report: report.join(\"\\n\"),\n summary,\n htmlMessages: input.htmlMessages,\n htmlTotalMessages,\n htmlTruncated,\n cssMessages: input.cssMessages,\n seoIssues: input.seoIssues,\n schemaIssues: input.schemaIssues,\n links: input.links,\n failedChecks,\n ...(input.errors && input.errors.length > 0 ? { errors: input.errors } : {}),\n };\n}\n"]}
|
package/dist/seo-auditor.js
CHANGED
|
@@ -32,6 +32,12 @@ function createIssueCollector() {
|
|
|
32
32
|
function isJsonLdScriptType(type) {
|
|
33
33
|
return type?.split(";", 1)[0].trim().toLowerCase() === "application/ld+json";
|
|
34
34
|
}
|
|
35
|
+
function hasJsonLdDocumentShape(value) {
|
|
36
|
+
if (Array.isArray(value)) {
|
|
37
|
+
return value.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
|
|
38
|
+
}
|
|
39
|
+
return typeof value === "object" && value !== null;
|
|
40
|
+
}
|
|
35
41
|
/**
|
|
36
42
|
* Audits technical SEO and accessibility basics on HTML content using Cheerio
|
|
37
43
|
*/
|
|
@@ -231,7 +237,16 @@ export function validateSchemaMarkupDetailed(htmlContent) {
|
|
|
231
237
|
return;
|
|
232
238
|
}
|
|
233
239
|
try {
|
|
234
|
-
JSON.parse(scriptText);
|
|
240
|
+
const parsed = JSON.parse(scriptText);
|
|
241
|
+
if (!hasJsonLdDocumentShape(parsed)) {
|
|
242
|
+
add({
|
|
243
|
+
code: "schema.jsonld.invalid_document",
|
|
244
|
+
severity: "error",
|
|
245
|
+
category: "Schema",
|
|
246
|
+
message: "Invalid JSON-LD document shape: the top level must be an object or an array of objects.",
|
|
247
|
+
element: `<script type="application/ld+json">...</script>`,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
235
250
|
}
|
|
236
251
|
catch (error) {
|
|
237
252
|
add({
|
package/dist/seo-auditor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo-auditor.js","sourceRoot":"","sources":["../src/seo-auditor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AACpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AACpC,MAAM,qBAAqB,GAAG,qBAAqB,eAAe,0CAA0C,CAAC;AAuB7G,SAAS,QAAQ,CAAC,MAAkB,EAAE,KAAe;IACnD,IAAI,MAAM,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB;IAK3B,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,OAAO;QACL,MAAM;QACN,GAAG,CAAC,KAAK;YACP,WAAW,IAAI,CAAC,CAAC;YACjB,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO;YACL,OAAO;gBACL,MAAM;gBACN,WAAW;gBACX,SAAS,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM;aACvC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAwB;IAClD,OAAO,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,qBAAqB,CAAC;AAC/E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,WAAmB;IAC1D,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,oBAAoB,EAAE,CAAC;IACzC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC;IAE1B,2BAA2B;IAC3B,MAAM,QAAQ,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IACnC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC;YACF,IAAI,EAAE,4BAA4B;YAClC,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,qGAAqG;SAC/G,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QACzC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC;gBACF,IAAI,EAAE,4BAA4B;gBAClC,QAAQ,EAAE,OAAO;gBACjB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,2BAA2B;aACrC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACjC,GAAG,CAAC;gBACF,IAAI,EAAE,kBAAkB;gBACxB,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,YAAY,SAAS,CAAC,MAAM,qHAAqH;gBAC1J,OAAO,EAAE,UAAU,SAAS,UAAU;aACvC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACjC,GAAG,CAAC;gBACF,IAAI,EAAE,kBAAkB;gBACxB,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,YAAY,SAAS,CAAC,MAAM,wJAAwJ;gBAC7L,OAAO,EAAE,UAAU,SAAS,UAAU;aACvC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,MAAM,eAAe,GAAG,CAAC,CAAC,mCAAmC,CAAC,CAAC;IAC/D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC;YACF,IAAI,EAAE,uCAAuC;YAC7C,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,mJAAmJ;SAC7J,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,GAAG,CAAC;gBACF,IAAI,EAAE,uCAAuC;gBAC7C,QAAQ,EAAE,OAAO;gBACjB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,8CAA8C;aACxD,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACjC,GAAG,CAAC;gBACF,IAAI,EAAE,6BAA6B;gBACnC,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,uBAAuB,QAAQ,CAAC,MAAM,yHAAyH;gBACxK,OAAO,EAAE,qCAAqC,QAAQ,IAAI;aAC3D,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACjC,GAAG,CAAC;gBACF,IAAI,EAAE,6BAA6B;gBACnC,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,uBAAuB,QAAQ,CAAC,MAAM,6IAA6I;gBAC5L,OAAO,EAAE,qCAAqC,QAAQ,IAAI;aAC3D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,cAAc,GAAG,CAAC,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE;QACjE,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACzC,OAAO,GAAG;aACP,IAAI,EAAE;aACN,KAAK,CAAC,cAAc,CAAC;aACrB,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IACH,MAAM,eAAe,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACxG,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,GAAG,CAAC;YACF,IAAI,EAAE,uBAAuB;YAC7B,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,0HAA0H;SACpI,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,GAAG,CAAC;YACF,IAAI,EAAE,uBAAuB;YAC7B,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,+GAA+G;SACzH,CAAC,CAAC;IACL,CAAC;IAED,oDAAoD;IACpD,MAAM,QAAQ,GAAG,CAAC,CAAC,gCAAgC,CAAC,CAAC;IACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC;YACF,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,mIAAmI;SAC7I,CAAC,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,GAAG,CAAC;YACF,IAAI,EAAE,gBAAgB;YACtB,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,6GAA6G;SACvH,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC;YACF,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,mBAAmB,MAAM,CAAC,MAAM,+IAA+I;SACzL,CAAC,CAAC;IACL,CAAC;IAED,gDAAgD;IAChD,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE;QAC3B,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC;QAChD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE5B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,GAAG,CAAC;gBACF,IAAI,EAAE,iCAAiC;gBACvC,QAAQ,EAAE,OAAO;gBACjB,QAAQ,EAAE,eAAe;gBACzB,OAAO,EAAE,iFAAiF;gBAC1F,OAAO,EAAE,aAAa,GAAG,IAAI;aAC9B,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC7B,0EAA0E;YAC1E,GAAG,CAAC;gBACF,IAAI,EAAE,+BAA+B;gBACrC,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,eAAe;gBACzB,OAAO,EAAE,sGAAsG;gBAC/G,OAAO,EAAE,aAAa,GAAG,WAAW;aACrC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,mCAAmC;IACnC,MAAM,OAAO,GAAG,CAAC,CAAC,kCAAkC,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,CAAC,CAAC,kCAAkC,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC;YACF,IAAI,EAAE,wBAAwB;YAC9B,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,gIAAgI;SAC1I,CAAC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC,OAAO,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,wBAAwB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC;AACtD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,4BAA4B,CAAC,WAAmB;IAC9D,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,oBAAoB,EAAE,CAAC;IACzC,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC;IAC1B,MAAM,MAAM,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAErG,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC7B,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;QAC3C,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC7B,GAAG,CAAC;gBACF,IAAI,EAAE,qBAAqB;gBAC3B,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,kBAAkB,KAAK,GAAG,CAAC,YAAY;aACjD,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,GAAG,CAAC;gBACF,IAAI,EAAE,4BAA4B;gBAClC,QAAQ,EAAE,OAAO;gBACjB,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,kCAAkC,eAAe,CAAC,KAAK,CAAC,EAAE;gBACnE,OAAO,EAAE,iDAAiD;aAC3D,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC,OAAO,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,OAAO,4BAA4B,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAAmB,EACnB,OAAgB,EAChB,QAAQ,GAAG,kBAAkB;IAE7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IACzD,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,IAAI,aAAa,GAAG,eAAe,CAAC;IACpC,MAAM,gBAAgB,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACnC,IAAI,oBAAqC,CAAC;QAC1C,IAAI,CAAC;YACH,oBAAoB,GAAG,eAAe;gBACpC,CAAC,CAAC,IAAI,GAAG,CAAC,gBAAgB,EAAE,eAAe,CAAC;gBAC5C,CAAC,CAAC,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,aAAa,GAAG,SAAS,CAAC;QAC5B,CAAC;QACD,IAAI,oBAAoB,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,aAAa,GAAG,MAAM,mBAAmB,CAAC,oBAAoB,CAAC,CAAC;YAClE,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,IAAI,CAAC,CAAC,KAAK,YAAY,cAAc,CAAC,EAAE,CAAC;oBACvC,MAAM,KAAK,CAAC;gBACd,CAAC;gBACD,aAAa,GAAG,SAAS,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE;QACzB,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,IAAI,WAAgB,CAAC;QACrB,IAAI,CAAC;YACH,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,wFAAwF;YACxF,OAAO;QACT,CAAC;QAED,IAAI,WAAW,CAAC,QAAQ,KAAK,OAAO,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1E,OAAO;QACT,CAAC;QAED,WAAW,CAAC,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,aAAa,GAAG,WAAW,CAAC,IAAI,CAAC;QACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YACjC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,KAAK,CAAa,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,UAAU,SAAS,CAAC,GAAW;QAClC,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE;gBAC5C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,YAAY,EAAE,qBAAqB,EAAE;gBAChD,SAAS,EAAE,qBAAqB;gBAChC,YAAY,EAAE,CAAC;aAChB,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC9C,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;YACzC,MAAM,kBAAkB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAE9C,yFAAyF;YACzF,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;gBACnE,IAAI,SAAS,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE;oBACzC,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE;wBACP,KAAK,EAAE,WAAW;wBAClB,YAAY,EAAE,qBAAqB;qBACpC;oBACD,SAAS,EAAE,qBAAqB;oBAChC,YAAY,EAAE,CAAC;iBAChB,CAAC,CAAC;gBACH,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBACtC,MAAM,kBAAkB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;oBAC7C,SAAS,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE;wBACrC,MAAM,EAAE,KAAK;wBACb,OAAO,EAAE,EAAE,YAAY,EAAE,qBAAqB,EAAE;wBAChD,SAAS,EAAE,qBAAqB;wBAChC,YAAY,EAAE,CAAC;qBAChB,CAAC,CAAC;gBACL,CAAC;gBAED,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACzC,MAAM,EAAE,GAAG,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC;gBACzC,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;gBACpC,MAAM,kBAAkB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;gBAC7C,OAAO;oBACL,GAAG;oBACH,MAAM;oBACN,EAAE;oBACF,OAAO,EACL,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG;wBAC3B,CAAC,CAAC,uBAAuB;wBACzB,CAAC,CAAC,QAAQ,KAAK,GAAG;4BAChB,CAAC,CAAC,iBAAiB,QAAQ,EAAE;4BAC7B,CAAC,CAAC,SAAS;iBAClB,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,GAAG;gBACH,MAAM,EAAE,UAAU;gBAClB,EAAE,EAAE,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG;gBACzC,OAAO,EACL,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG;oBACnC,CAAC,CAAC,uBAAuB;oBACzB,CAAC,CAAC,YAAY,KAAK,GAAG;wBACpB,CAAC,CAAC,iBAAiB,YAAY,EAAE;wBACjC,CAAC,CAAC,SAAS;aAClB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,OAAO;gBACL,GAAG;gBACH,MAAM,EAAE,KAAK,YAAY,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;gBAC9D,EAAE,EAAE,KAAK;gBACT,OAAO,EAAE,eAAe,CAAC,KAAK,CAAC;aAChC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,UAAU,MAAM;QACnB,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,SAAS,CAAC;YACxB,SAAS,IAAI,CAAC,CAAC;YACf,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import * as cheerio from \"cheerio\";\nimport {\n assertPublicHttpUrl,\n cancelResponseBody,\n fetchPublicHttp,\n getErrorMessage,\n PublicUrlError,\n} from \"./network.js\";\nimport { PACKAGE_VERSION } from \"./version.js\";\n\nexport const MAX_AUDIT_ISSUES = 200;\nexport const MAX_LINKS_TO_CHECK = 25;\nconst LINK_CHECK_CONCURRENCY = 5;\nconst LINK_CHECK_TIMEOUT_MS = 5_000;\nconst LINK_CHECK_USER_AGENT = `mcp-web-validator/${PACKAGE_VERSION} (+https://digestseo.com/validator-mcp/)`;\n\nexport interface SEOIssue {\n code: string;\n severity: \"error\" | \"warning\" | \"info\";\n category: \"SEO\" | \"Schema\" | \"BrokenLinks\" | \"Accessibility\";\n message: string;\n element?: string;\n}\n\nexport interface LinkStatus {\n url: string;\n status: number | string;\n ok: boolean;\n message?: string;\n}\n\nexport interface AuditDetails {\n issues: SEOIssue[];\n totalIssues: number;\n truncated: boolean;\n}\n\nfunction addIssue(issues: SEOIssue[], issue: SEOIssue): void {\n if (issues.length < MAX_AUDIT_ISSUES) {\n issues.push(issue);\n }\n}\n\nfunction createIssueCollector(): {\n issues: SEOIssue[];\n add: (issue: SEOIssue) => void;\n details: () => AuditDetails;\n} {\n const issues: SEOIssue[] = [];\n let totalIssues = 0;\n return {\n issues,\n add(issue) {\n totalIssues += 1;\n addIssue(issues, issue);\n },\n details() {\n return {\n issues,\n totalIssues,\n truncated: totalIssues > issues.length,\n };\n },\n };\n}\n\nfunction isJsonLdScriptType(type: string | undefined): boolean {\n return type?.split(\";\", 1)[0].trim().toLowerCase() === \"application/ld+json\";\n}\n\n/**\n * Audits technical SEO and accessibility basics on HTML content using Cheerio\n */\nexport function auditSeoMetadataDetailed(htmlContent: string): AuditDetails {\n const $ = cheerio.load(htmlContent);\n const collector = createIssueCollector();\n const { add } = collector;\n\n // --- Title Tag Audits ---\n const titleTag = $(\"head > title\");\n if (titleTag.length === 0) {\n add({\n code: \"seo.title.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Missing <title> tag. Add a concise, descriptive title to help represent the page in search results.\",\n });\n } else {\n const titleText = titleTag.text().trim();\n if (titleText.length === 0) {\n add({\n code: \"seo.title.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"The <title> tag is empty.\",\n });\n } else if (titleText.length < 30) {\n add({\n code: \"seo.title.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Title is ${titleText.length} characters. This is shorter than the audit's common editorial range; review whether it describes the page clearly.`,\n element: `<title>${titleText}</title>`,\n });\n } else if (titleText.length > 60) {\n add({\n code: \"seo.title.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Title is ${titleText.length} characters. This is longer than the audit's common editorial range; Google title links may be shortened or rewritten depending on context and device.`,\n element: `<title>${titleText}</title>`,\n });\n }\n }\n\n // --- Meta Description Audits ---\n const metaDescription = $('head > meta[name=\"description\" i]');\n if (metaDescription.length === 0) {\n add({\n code: \"seo.meta_description.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Missing <meta name=\\\"description\\\">. Add a concise, accurate page summary; Google may use page content or this description to generate a snippet.\",\n });\n } else {\n const descText = metaDescription.attr(\"content\")?.trim() || \"\";\n if (descText.length === 0) {\n add({\n code: \"seo.meta_description.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Meta description content attribute is empty.\",\n });\n } else if (descText.length < 120) {\n add({\n code: \"seo.meta_description.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Meta description is ${descText.length} characters. This is shorter than the audit's common editorial range; review whether it provides a useful page summary.`,\n element: `<meta name=\"description\" content=\"${descText}\">`,\n });\n } else if (descText.length > 160) {\n add({\n code: \"seo.meta_description.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Meta description is ${descText.length} characters. This is longer than the audit's common editorial range; displayed snippets may be shortened depending on the query and device.`,\n element: `<meta name=\"description\" content=\"${descText}\">`,\n });\n }\n }\n\n // --- Canonical Link ---\n const canonicalLinks = $(\"head > link[rel]\").filter((_, element) => {\n const rel = $(element).attr(\"rel\") ?? \"\";\n return rel\n .trim()\n .split(/[\\t\\n\\f\\r ]+/)\n .some((token) => token.toLowerCase() === \"canonical\");\n });\n const usableCanonical = canonicalLinks.filter((_, element) => Boolean($(element).attr(\"href\")?.trim()));\n if (canonicalLinks.length === 0) {\n add({\n code: \"seo.canonical.missing\",\n severity: \"info\",\n category: \"SEO\",\n message: \"No rel=\\\"canonical\\\" preference is declared. This is optional unless the page needs an explicit canonicalization signal.\",\n });\n } else if (usableCanonical.length === 0) {\n add({\n code: \"seo.canonical.missing\",\n severity: \"warning\",\n category: \"SEO\",\n message: \"Canonical link is present but its href is empty. Provide a usable canonical target or remove the declaration.\",\n });\n }\n\n // --- Viewport Meta Tag (Mobile Responsiveness) ---\n const viewport = $('head > meta[name=\"viewport\" i]');\n if (viewport.length === 0) {\n add({\n code: \"seo.viewport.missing\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Missing <meta name=\\\"viewport\\\"> tag. Review mobile rendering; this tag helps browsers size and scale the page on mobile devices.\",\n });\n }\n\n // --- Heading Structure ---\n const h1Tags = $(\"h1\");\n if (h1Tags.length === 0) {\n add({\n code: \"seo.h1.missing\",\n severity: \"warning\",\n category: \"SEO\",\n message: \"No <h1> heading found. Review whether the page has a clear main heading and a meaningful heading hierarchy.\",\n });\n } else if (h1Tags.length > 1) {\n add({\n code: \"seo.h1.multiple\",\n severity: \"info\",\n category: \"SEO\",\n message: `Found multiple (${h1Tags.length}) <h1> headings. Multiple H1s are not inherently an SEO error; ensure the heading hierarchy is meaningful and the main visual title is clear.`,\n });\n }\n\n // --- Images Alt Tags (SEO + Accessibility) ---\n $(\"img\").each((_, element) => {\n const img = $(element);\n const src = img.attr(\"src\") || \"unknown-source\";\n const alt = img.attr(\"alt\");\n\n if (alt === undefined) {\n add({\n code: \"accessibility.image_alt.missing\",\n severity: \"error\",\n category: \"Accessibility\",\n message: \"Missing 'alt' attribute on image. This makes it inaccessible to screen readers.\",\n element: `<img src=\"${src}\">`,\n });\n } else if (alt.trim() === \"\") {\n // Empty alt is acceptable for purely decorative images, but worth warning\n add({\n code: \"accessibility.image_alt.empty\",\n severity: \"info\",\n category: \"Accessibility\",\n message: \"Empty 'alt' attribute found. Ensure this image is purely decorative, otherwise add descriptive text.\",\n element: `<img src=\"${src}\" alt=\"\">`,\n });\n }\n });\n\n // --- Open Graph / Social Tags ---\n const ogTitle = $('head > meta[property=\"og:title\"]');\n const ogImage = $('head > meta[property=\"og:image\"]');\n if (ogTitle.length === 0 || ogImage.length === 0) {\n add({\n code: \"seo.open_graph.missing\",\n severity: \"info\",\n category: \"SEO\",\n message: \"Missing Open Graph social metadata (og:title / og:image). Add these to control preview cards on platforms like LinkedIn and X.\",\n });\n }\n\n return collector.details();\n}\n\nexport function auditSeoMetadata(htmlContent: string): SEOIssue[] {\n return auditSeoMetadataDetailed(htmlContent).issues;\n}\n\n/**\n * Parses and validates JSON-LD Schema markup\n */\nexport function validateSchemaMarkupDetailed(htmlContent: string): AuditDetails {\n const $ = cheerio.load(htmlContent);\n const collector = createIssueCollector();\n const { add } = collector;\n const blocks = $(\"script[type]\").filter((_, element) => isJsonLdScriptType($(element).attr(\"type\")));\n\n blocks.each((index, element) => {\n const scriptText = $(element).html() || \"\";\n if (scriptText.trim() === \"\") {\n add({\n code: \"schema.jsonld.empty\",\n severity: \"warning\",\n category: \"Schema\",\n message: `JSON-LD block #${index + 1} is empty.`,\n });\n return;\n }\n\n try {\n JSON.parse(scriptText);\n } catch (error: unknown) {\n add({\n code: \"schema.jsonld.invalid_json\",\n severity: \"error\",\n category: \"Schema\",\n message: `Invalid JSON-LD schema syntax: ${getErrorMessage(error)}`,\n element: `<script type=\"application/ld+json\">...</script>`,\n });\n }\n });\n\n return collector.details();\n}\n\nexport function validateSchemaMarkup(htmlContent: string): SEOIssue[] {\n return validateSchemaMarkupDetailed(htmlContent).issues;\n}\n\n/**\n * Extracts and tests links, treating 3xx responses as reachable redirects and 4xx/5xx as broken.\n */\nexport async function checkBrokenLinks(\n htmlContent: string,\n baseUrl?: string,\n maxLinks = MAX_LINKS_TO_CHECK,\n): Promise<LinkStatus[]> {\n if (!Number.isSafeInteger(maxLinks) || maxLinks <= 0) {\n throw new Error(\"maxLinks must be a positive integer\");\n }\n const linkLimit = Math.min(maxLinks, MAX_LINKS_TO_CHECK);\n const $ = cheerio.load(htmlContent);\n const fallbackBaseUrl = baseUrl ? await assertPublicHttpUrl(baseUrl) : undefined;\n let parsedBaseUrl = fallbackBaseUrl;\n const documentBaseHref = $(\"base[href]\").first().attr(\"href\");\n if (documentBaseHref !== undefined) {\n let resolvedDocumentBase: URL | undefined;\n try {\n resolvedDocumentBase = fallbackBaseUrl\n ? new URL(documentBaseHref, fallbackBaseUrl)\n : new URL(documentBaseHref);\n } catch {\n parsedBaseUrl = undefined;\n }\n if (resolvedDocumentBase) {\n try {\n parsedBaseUrl = await assertPublicHttpUrl(resolvedDocumentBase);\n } catch (error: unknown) {\n if (!(error instanceof PublicUrlError)) {\n throw error;\n }\n parsedBaseUrl = undefined;\n }\n }\n }\n const urls: string[] = [];\n const seenUrls = new Set<string>();\n\n $(\"a\").each((_, element) => {\n if (urls.length >= linkLimit) {\n return false;\n }\n\n const href = $(element).attr(\"href\")?.trim();\n if (!href || href.startsWith(\"#\")) {\n return;\n }\n\n let resolvedUrl: URL;\n try {\n resolvedUrl = parsedBaseUrl ? new URL(href, parsedBaseUrl) : new URL(href);\n } catch {\n // Relative links require a public base URL; unsupported or malformed links are skipped.\n return;\n }\n\n if (resolvedUrl.protocol !== \"http:\" && resolvedUrl.protocol !== \"https:\") {\n return;\n }\n\n resolvedUrl.hash = \"\";\n const normalizedUrl = resolvedUrl.href;\n if (!seenUrls.has(normalizedUrl)) {\n seenUrls.add(normalizedUrl);\n urls.push(normalizedUrl);\n }\n });\n\n const results = new Array<LinkStatus>(urls.length);\n let nextIndex = 0;\n\n async function checkLink(url: string): Promise<LinkStatus> {\n try {\n const headResult = await fetchPublicHttp(url, {\n method: \"HEAD\",\n headers: { \"User-Agent\": LINK_CHECK_USER_AGENT },\n timeoutMs: LINK_CHECK_TIMEOUT_MS,\n maxRedirects: 0,\n });\n const headStatus = headResult.response.status;\n const headFinalUrl = headResult.url.href;\n await cancelResponseBody(headResult.response);\n\n // Some sites reject or do not implement HEAD even when the linked resource is available.\n if (headStatus === 405 || headStatus === 403 || headStatus === 501) {\n let getResult = await fetchPublicHttp(url, {\n method: \"GET\",\n headers: {\n Range: \"bytes=0-0\",\n \"User-Agent\": LINK_CHECK_USER_AGENT,\n },\n timeoutMs: LINK_CHECK_TIMEOUT_MS,\n maxRedirects: 0,\n });\n if (getResult.response.status === 416) {\n await cancelResponseBody(getResult.response);\n getResult = await fetchPublicHttp(url, {\n method: \"GET\",\n headers: { \"User-Agent\": LINK_CHECK_USER_AGENT },\n timeoutMs: LINK_CHECK_TIMEOUT_MS,\n maxRedirects: 0,\n });\n }\n\n const status = getResult.response.status;\n const ok = status >= 200 && status < 400;\n const finalUrl = getResult.url.href;\n await cancelResponseBody(getResult.response);\n return {\n url,\n status,\n ok,\n message:\n status >= 300 && status < 400\n ? \"Redirect not followed\"\n : finalUrl !== url\n ? `Redirected to ${finalUrl}`\n : undefined,\n };\n }\n\n return {\n url,\n status: headStatus,\n ok: headStatus >= 200 && headStatus < 400,\n message:\n headStatus >= 300 && headStatus < 400\n ? \"Redirect not followed\"\n : headFinalUrl !== url\n ? `Redirected to ${headFinalUrl}`\n : undefined,\n };\n } catch (error: unknown) {\n return {\n url,\n status: error instanceof PublicUrlError ? \"blocked\" : \"failed\",\n ok: false,\n message: getErrorMessage(error),\n };\n }\n }\n\n async function worker(): Promise<void> {\n while (true) {\n const index = nextIndex;\n nextIndex += 1;\n if (index >= urls.length) {\n return;\n }\n results[index] = await checkLink(urls[index]);\n }\n }\n\n const workerCount = Math.min(LINK_CHECK_CONCURRENCY, urls.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"seo-auditor.js","sourceRoot":"","sources":["../src/seo-auditor.ts"],"names":[],"mappings":"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","sourcesContent":["import * as cheerio from \"cheerio\";\nimport {\n assertPublicHttpUrl,\n cancelResponseBody,\n fetchPublicHttp,\n getErrorMessage,\n PublicUrlError,\n} from \"./network.js\";\nimport { PACKAGE_VERSION } from \"./version.js\";\n\nexport const MAX_AUDIT_ISSUES = 200;\nexport const MAX_LINKS_TO_CHECK = 25;\nconst LINK_CHECK_CONCURRENCY = 5;\nconst LINK_CHECK_TIMEOUT_MS = 5_000;\nconst LINK_CHECK_USER_AGENT = `mcp-web-validator/${PACKAGE_VERSION} (+https://digestseo.com/validator-mcp/)`;\n\nexport interface SEOIssue {\n code: string;\n severity: \"error\" | \"warning\" | \"info\";\n category: \"SEO\" | \"Schema\" | \"BrokenLinks\" | \"Accessibility\";\n message: string;\n element?: string;\n}\n\nexport interface LinkStatus {\n url: string;\n status: number | string;\n ok: boolean;\n message?: string;\n}\n\nexport interface AuditDetails {\n issues: SEOIssue[];\n totalIssues: number;\n truncated: boolean;\n}\n\nfunction addIssue(issues: SEOIssue[], issue: SEOIssue): void {\n if (issues.length < MAX_AUDIT_ISSUES) {\n issues.push(issue);\n }\n}\n\nfunction createIssueCollector(): {\n issues: SEOIssue[];\n add: (issue: SEOIssue) => void;\n details: () => AuditDetails;\n} {\n const issues: SEOIssue[] = [];\n let totalIssues = 0;\n return {\n issues,\n add(issue) {\n totalIssues += 1;\n addIssue(issues, issue);\n },\n details() {\n return {\n issues,\n totalIssues,\n truncated: totalIssues > issues.length,\n };\n },\n };\n}\n\nfunction isJsonLdScriptType(type: string | undefined): boolean {\n return type?.split(\";\", 1)[0].trim().toLowerCase() === \"application/ld+json\";\n}\n\nfunction hasJsonLdDocumentShape(value: unknown): boolean {\n if (Array.isArray(value)) {\n return value.every((entry) => typeof entry === \"object\" && entry !== null && !Array.isArray(entry));\n }\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Audits technical SEO and accessibility basics on HTML content using Cheerio\n */\nexport function auditSeoMetadataDetailed(htmlContent: string): AuditDetails {\n const $ = cheerio.load(htmlContent);\n const collector = createIssueCollector();\n const { add } = collector;\n\n // --- Title Tag Audits ---\n const titleTag = $(\"head > title\");\n if (titleTag.length === 0) {\n add({\n code: \"seo.title.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Missing <title> tag. Add a concise, descriptive title to help represent the page in search results.\",\n });\n } else {\n const titleText = titleTag.text().trim();\n if (titleText.length === 0) {\n add({\n code: \"seo.title.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"The <title> tag is empty.\",\n });\n } else if (titleText.length < 30) {\n add({\n code: \"seo.title.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Title is ${titleText.length} characters. This is shorter than the audit's common editorial range; review whether it describes the page clearly.`,\n element: `<title>${titleText}</title>`,\n });\n } else if (titleText.length > 60) {\n add({\n code: \"seo.title.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Title is ${titleText.length} characters. This is longer than the audit's common editorial range; Google title links may be shortened or rewritten depending on context and device.`,\n element: `<title>${titleText}</title>`,\n });\n }\n }\n\n // --- Meta Description Audits ---\n const metaDescription = $('head > meta[name=\"description\" i]');\n if (metaDescription.length === 0) {\n add({\n code: \"seo.meta_description.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Missing <meta name=\\\"description\\\">. Add a concise, accurate page summary; Google may use page content or this description to generate a snippet.\",\n });\n } else {\n const descText = metaDescription.attr(\"content\")?.trim() || \"\";\n if (descText.length === 0) {\n add({\n code: \"seo.meta_description.missing_or_empty\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Meta description content attribute is empty.\",\n });\n } else if (descText.length < 120) {\n add({\n code: \"seo.meta_description.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Meta description is ${descText.length} characters. This is shorter than the audit's common editorial range; review whether it provides a useful page summary.`,\n element: `<meta name=\"description\" content=\"${descText}\">`,\n });\n } else if (descText.length > 160) {\n add({\n code: \"seo.meta_description.length\",\n severity: \"info\",\n category: \"SEO\",\n message: `Meta description is ${descText.length} characters. This is longer than the audit's common editorial range; displayed snippets may be shortened depending on the query and device.`,\n element: `<meta name=\"description\" content=\"${descText}\">`,\n });\n }\n }\n\n // --- Canonical Link ---\n const canonicalLinks = $(\"head > link[rel]\").filter((_, element) => {\n const rel = $(element).attr(\"rel\") ?? \"\";\n return rel\n .trim()\n .split(/[\\t\\n\\f\\r ]+/)\n .some((token) => token.toLowerCase() === \"canonical\");\n });\n const usableCanonical = canonicalLinks.filter((_, element) => Boolean($(element).attr(\"href\")?.trim()));\n if (canonicalLinks.length === 0) {\n add({\n code: \"seo.canonical.missing\",\n severity: \"info\",\n category: \"SEO\",\n message: \"No rel=\\\"canonical\\\" preference is declared. This is optional unless the page needs an explicit canonicalization signal.\",\n });\n } else if (usableCanonical.length === 0) {\n add({\n code: \"seo.canonical.missing\",\n severity: \"warning\",\n category: \"SEO\",\n message: \"Canonical link is present but its href is empty. Provide a usable canonical target or remove the declaration.\",\n });\n }\n\n // --- Viewport Meta Tag (Mobile Responsiveness) ---\n const viewport = $('head > meta[name=\"viewport\" i]');\n if (viewport.length === 0) {\n add({\n code: \"seo.viewport.missing\",\n severity: \"error\",\n category: \"SEO\",\n message: \"Missing <meta name=\\\"viewport\\\"> tag. Review mobile rendering; this tag helps browsers size and scale the page on mobile devices.\",\n });\n }\n\n // --- Heading Structure ---\n const h1Tags = $(\"h1\");\n if (h1Tags.length === 0) {\n add({\n code: \"seo.h1.missing\",\n severity: \"warning\",\n category: \"SEO\",\n message: \"No <h1> heading found. Review whether the page has a clear main heading and a meaningful heading hierarchy.\",\n });\n } else if (h1Tags.length > 1) {\n add({\n code: \"seo.h1.multiple\",\n severity: \"info\",\n category: \"SEO\",\n message: `Found multiple (${h1Tags.length}) <h1> headings. Multiple H1s are not inherently an SEO error; ensure the heading hierarchy is meaningful and the main visual title is clear.`,\n });\n }\n\n // --- Images Alt Tags (SEO + Accessibility) ---\n $(\"img\").each((_, element) => {\n const img = $(element);\n const src = img.attr(\"src\") || \"unknown-source\";\n const alt = img.attr(\"alt\");\n\n if (alt === undefined) {\n add({\n code: \"accessibility.image_alt.missing\",\n severity: \"error\",\n category: \"Accessibility\",\n message: \"Missing 'alt' attribute on image. This makes it inaccessible to screen readers.\",\n element: `<img src=\"${src}\">`,\n });\n } else if (alt.trim() === \"\") {\n // Empty alt is acceptable for purely decorative images, but worth warning\n add({\n code: \"accessibility.image_alt.empty\",\n severity: \"info\",\n category: \"Accessibility\",\n message: \"Empty 'alt' attribute found. Ensure this image is purely decorative, otherwise add descriptive text.\",\n element: `<img src=\"${src}\" alt=\"\">`,\n });\n }\n });\n\n // --- Open Graph / Social Tags ---\n const ogTitle = $('head > meta[property=\"og:title\"]');\n const ogImage = $('head > meta[property=\"og:image\"]');\n if (ogTitle.length === 0 || ogImage.length === 0) {\n add({\n code: \"seo.open_graph.missing\",\n severity: \"info\",\n category: \"SEO\",\n message: \"Missing Open Graph social metadata (og:title / og:image). Add these to control preview cards on platforms like LinkedIn and X.\",\n });\n }\n\n return collector.details();\n}\n\nexport function auditSeoMetadata(htmlContent: string): SEOIssue[] {\n return auditSeoMetadataDetailed(htmlContent).issues;\n}\n\n/**\n * Parses and validates JSON-LD Schema markup\n */\nexport function validateSchemaMarkupDetailed(htmlContent: string): AuditDetails {\n const $ = cheerio.load(htmlContent);\n const collector = createIssueCollector();\n const { add } = collector;\n const blocks = $(\"script[type]\").filter((_, element) => isJsonLdScriptType($(element).attr(\"type\")));\n\n blocks.each((index, element) => {\n const scriptText = $(element).html() || \"\";\n if (scriptText.trim() === \"\") {\n add({\n code: \"schema.jsonld.empty\",\n severity: \"warning\",\n category: \"Schema\",\n message: `JSON-LD block #${index + 1} is empty.`,\n });\n return;\n }\n\n try {\n const parsed = JSON.parse(scriptText) as unknown;\n if (!hasJsonLdDocumentShape(parsed)) {\n add({\n code: \"schema.jsonld.invalid_document\",\n severity: \"error\",\n category: \"Schema\",\n message: \"Invalid JSON-LD document shape: the top level must be an object or an array of objects.\",\n element: `<script type=\"application/ld+json\">...</script>`,\n });\n }\n } catch (error: unknown) {\n add({\n code: \"schema.jsonld.invalid_json\",\n severity: \"error\",\n category: \"Schema\",\n message: `Invalid JSON-LD schema syntax: ${getErrorMessage(error)}`,\n element: `<script type=\"application/ld+json\">...</script>`,\n });\n }\n });\n\n return collector.details();\n}\n\nexport function validateSchemaMarkup(htmlContent: string): SEOIssue[] {\n return validateSchemaMarkupDetailed(htmlContent).issues;\n}\n\n/**\n * Extracts and tests links, treating 3xx responses as reachable redirects and 4xx/5xx as broken.\n */\nexport async function checkBrokenLinks(\n htmlContent: string,\n baseUrl?: string,\n maxLinks = MAX_LINKS_TO_CHECK,\n): Promise<LinkStatus[]> {\n if (!Number.isSafeInteger(maxLinks) || maxLinks <= 0) {\n throw new Error(\"maxLinks must be a positive integer\");\n }\n const linkLimit = Math.min(maxLinks, MAX_LINKS_TO_CHECK);\n const $ = cheerio.load(htmlContent);\n const fallbackBaseUrl = baseUrl ? await assertPublicHttpUrl(baseUrl) : undefined;\n let parsedBaseUrl = fallbackBaseUrl;\n const documentBaseHref = $(\"base[href]\").first().attr(\"href\");\n if (documentBaseHref !== undefined) {\n let resolvedDocumentBase: URL | undefined;\n try {\n resolvedDocumentBase = fallbackBaseUrl\n ? new URL(documentBaseHref, fallbackBaseUrl)\n : new URL(documentBaseHref);\n } catch {\n parsedBaseUrl = undefined;\n }\n if (resolvedDocumentBase) {\n try {\n parsedBaseUrl = await assertPublicHttpUrl(resolvedDocumentBase);\n } catch (error: unknown) {\n if (!(error instanceof PublicUrlError)) {\n throw error;\n }\n parsedBaseUrl = undefined;\n }\n }\n }\n const urls: string[] = [];\n const seenUrls = new Set<string>();\n\n $(\"a\").each((_, element) => {\n if (urls.length >= linkLimit) {\n return false;\n }\n\n const href = $(element).attr(\"href\")?.trim();\n if (!href || href.startsWith(\"#\")) {\n return;\n }\n\n let resolvedUrl: URL;\n try {\n resolvedUrl = parsedBaseUrl ? new URL(href, parsedBaseUrl) : new URL(href);\n } catch {\n // Relative links require a public base URL; unsupported or malformed links are skipped.\n return;\n }\n\n if (resolvedUrl.protocol !== \"http:\" && resolvedUrl.protocol !== \"https:\") {\n return;\n }\n\n resolvedUrl.hash = \"\";\n const normalizedUrl = resolvedUrl.href;\n if (!seenUrls.has(normalizedUrl)) {\n seenUrls.add(normalizedUrl);\n urls.push(normalizedUrl);\n }\n });\n\n const results = new Array<LinkStatus>(urls.length);\n let nextIndex = 0;\n\n async function checkLink(url: string): Promise<LinkStatus> {\n try {\n const headResult = await fetchPublicHttp(url, {\n method: \"HEAD\",\n headers: { \"User-Agent\": LINK_CHECK_USER_AGENT },\n timeoutMs: LINK_CHECK_TIMEOUT_MS,\n maxRedirects: 0,\n });\n const headStatus = headResult.response.status;\n const headFinalUrl = headResult.url.href;\n await cancelResponseBody(headResult.response);\n\n // Some sites reject or do not implement HEAD even when the linked resource is available.\n if (headStatus === 405 || headStatus === 403 || headStatus === 501) {\n let getResult = await fetchPublicHttp(url, {\n method: \"GET\",\n headers: {\n Range: \"bytes=0-0\",\n \"User-Agent\": LINK_CHECK_USER_AGENT,\n },\n timeoutMs: LINK_CHECK_TIMEOUT_MS,\n maxRedirects: 0,\n });\n if (getResult.response.status === 416) {\n await cancelResponseBody(getResult.response);\n getResult = await fetchPublicHttp(url, {\n method: \"GET\",\n headers: { \"User-Agent\": LINK_CHECK_USER_AGENT },\n timeoutMs: LINK_CHECK_TIMEOUT_MS,\n maxRedirects: 0,\n });\n }\n\n const status = getResult.response.status;\n const ok = status >= 200 && status < 400;\n const finalUrl = getResult.url.href;\n await cancelResponseBody(getResult.response);\n return {\n url,\n status,\n ok,\n message:\n status >= 300 && status < 400\n ? \"Redirect not followed\"\n : finalUrl !== url\n ? `Redirected to ${finalUrl}`\n : undefined,\n };\n }\n\n return {\n url,\n status: headStatus,\n ok: headStatus >= 200 && headStatus < 400,\n message:\n headStatus >= 300 && headStatus < 400\n ? \"Redirect not followed\"\n : headFinalUrl !== url\n ? `Redirected to ${headFinalUrl}`\n : undefined,\n };\n } catch (error: unknown) {\n return {\n url,\n status: error instanceof PublicUrlError ? \"blocked\" : \"failed\",\n ok: false,\n message: getErrorMessage(error),\n };\n }\n }\n\n async function worker(): Promise<void> {\n while (true) {\n const index = nextIndex;\n nextIndex += 1;\n if (index >= urls.length) {\n return;\n }\n results[index] = await checkLink(urls[index]);\n }\n }\n\n const workerCount = Math.min(LINK_CHECK_CONCURRENCY, urls.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\n"]}
|
package/dist/w3c-validator.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export interface W3CMessage {
|
|
|
9
9
|
extract?: string;
|
|
10
10
|
}
|
|
11
11
|
export type W3CMessageSeverity = "error" | "warning" | "info";
|
|
12
|
+
export interface HtmlValidationResult {
|
|
13
|
+
messages: W3CMessage[];
|
|
14
|
+
total: number;
|
|
15
|
+
truncated: boolean;
|
|
16
|
+
counts: Record<W3CMessageSeverity, number>;
|
|
17
|
+
}
|
|
12
18
|
export declare function getW3CMessageSeverity(message: Pick<W3CMessage, "type" | "subType">): W3CMessageSeverity;
|
|
13
19
|
export interface CSSMessage {
|
|
14
20
|
line: number;
|
|
@@ -19,9 +25,9 @@ export interface CSSMessage {
|
|
|
19
25
|
}
|
|
20
26
|
export declare const MAX_CSS_VALIDATION_BYTES = 128000;
|
|
21
27
|
export declare const MAX_VALIDATION_MESSAGES = 200;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
*/
|
|
28
|
+
/** Validates HTML using the W3C Nu HTML Checker API and retains bounded-output metadata. */
|
|
29
|
+
export declare function validateHtmlContentDetailed(htmlContent: string): Promise<HtmlValidationResult>;
|
|
30
|
+
/** Backwards-compatible convenience API returning capped diagnostics only. */
|
|
25
31
|
export declare function validateHtmlContent(htmlContent: string): Promise<W3CMessage[]>;
|
|
26
32
|
/**
|
|
27
33
|
* Validates CSS using the W3C Jigsaw CSS Validator API
|
package/dist/w3c-validator.js
CHANGED
|
@@ -2,7 +2,7 @@ import { cancelResponseBody, getErrorMessage, readResponseText, } from "./networ
|
|
|
2
2
|
import { PACKAGE_VERSION } from "./version.js";
|
|
3
3
|
export function getW3CMessageSeverity(message) {
|
|
4
4
|
const type = message.type.trim().toLowerCase();
|
|
5
|
-
if (type === "error")
|
|
5
|
+
if (type === "error" || type === "non-document-error")
|
|
6
6
|
return "error";
|
|
7
7
|
if (type === "warning" || (type === "info" && message.subType?.trim().toLowerCase() === "warning")) {
|
|
8
8
|
return "warning";
|
|
@@ -52,10 +52,8 @@ function normalizeW3CMessage(message) {
|
|
|
52
52
|
}
|
|
53
53
|
return normalized;
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
*/
|
|
58
|
-
export async function validateHtmlContent(htmlContent) {
|
|
55
|
+
/** Validates HTML using the W3C Nu HTML Checker API and retains bounded-output metadata. */
|
|
56
|
+
export async function validateHtmlContentDetailed(htmlContent) {
|
|
59
57
|
const url = "https://validator.w3.org/nu/?out=json";
|
|
60
58
|
try {
|
|
61
59
|
assertContentSize(htmlContent, MAX_HTML_BYTES, "HTML content");
|
|
@@ -81,14 +79,26 @@ export async function validateHtmlContent(htmlContent) {
|
|
|
81
79
|
if (!Array.isArray(data.messages)) {
|
|
82
80
|
throw new Error("W3C HTML validator returned an invalid response shape");
|
|
83
81
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
const normalized = data.messages.map(normalizeW3CMessage);
|
|
83
|
+
const counts = { error: 0, warning: 0, info: 0 };
|
|
84
|
+
for (const message of normalized) {
|
|
85
|
+
counts[getW3CMessageSeverity(message)] += 1;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
messages: normalized.slice(0, MAX_VALIDATION_MESSAGES),
|
|
89
|
+
total: normalized.length,
|
|
90
|
+
truncated: normalized.length > MAX_VALIDATION_MESSAGES,
|
|
91
|
+
counts,
|
|
92
|
+
};
|
|
87
93
|
}
|
|
88
94
|
catch (error) {
|
|
89
95
|
throw new Error(`HTML validation failed: ${getErrorMessage(error)}`);
|
|
90
96
|
}
|
|
91
97
|
}
|
|
98
|
+
/** Backwards-compatible convenience API returning capped diagnostics only. */
|
|
99
|
+
export async function validateHtmlContent(htmlContent) {
|
|
100
|
+
return (await validateHtmlContentDetailed(htmlContent)).messages;
|
|
101
|
+
}
|
|
92
102
|
/**
|
|
93
103
|
* Validates CSS using the W3C Jigsaw CSS Validator API
|
|
94
104
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"w3c-validator.js","sourceRoot":"","sources":["../src/w3c-validator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n cancelResponseBody,\n getErrorMessage,\n readResponseText,\n} from \"./network.js\";\nimport { PACKAGE_VERSION } from \"./version.js\";\n\nexport interface W3CMessage {\n type: string;\n subType?: string;\n lastLine?: number;\n lastColumn?: number;\n firstLine?: number;\n firstColumn?: number;\n message: string;\n extract?: string;\n}\n\nexport type W3CMessageSeverity = \"error\" | \"warning\" | \"info\";\n\nexport function getW3CMessageSeverity(\n message: Pick<W3CMessage, \"type\" | \"subType\">,\n): W3CMessageSeverity {\n const type = message.type.trim().toLowerCase();\n if (type === \"error\") return \"error\";\n if (type === \"warning\" || (type === \"info\" && message.subType?.trim().toLowerCase() === \"warning\")) {\n return \"warning\";\n }\n return \"info\";\n}\n\nexport interface CSSMessage {\n line: number;\n type: string;\n message: string;\n context?: string;\n compatibility?: \"known-validator-limitation\";\n}\n\nconst VALIDATOR_TIMEOUT_MS = 20_000;\nconst MAX_HTML_BYTES = 2_000_000;\nexport const MAX_CSS_VALIDATION_BYTES = 128_000;\nconst MAX_VALIDATOR_RESPONSE_BYTES = 5_000_000;\nconst USER_AGENT = `mcp-web-validator/${PACKAGE_VERSION} (+https://digestseo.com/validator-mcp/)`;\nexport const MAX_VALIDATION_MESSAGES = 200;\n\nfunction isKnownCssValidatorLimitation(type: string | undefined, message: string): boolean {\n if (type?.trim().toLowerCase() !== \"at-rule\") return false;\n const normalized = message.trim().toLowerCase().replace(/[“”\"'‘’]/g, \"\");\n return normalized === \"unrecognized at-rule @container\";\n}\n\nfunction assertContentSize(content: string, maxBytes: number, label: string): void {\n const size = Buffer.byteLength(content, \"utf8\");\n if (size > maxBytes) {\n throw new Error(`${label} exceeds the ${maxBytes}-byte validation limit`);\n }\n}\n\nfunction normalizeW3CMessage(message: unknown): W3CMessage {\n if (typeof message !== \"object\" || message === null) {\n throw new Error(\"W3C HTML validator returned a malformed message\");\n }\n\n const candidate = message as Record<string, unknown>;\n if (typeof candidate.type !== \"string\" || typeof candidate.message !== \"string\") {\n throw new Error(\"W3C HTML validator returned a malformed message\");\n }\n\n const normalized: W3CMessage = {\n type: candidate.type,\n message: candidate.message,\n };\n\n for (const field of [\"lastLine\", \"lastColumn\", \"firstLine\", \"firstColumn\"] as const) {\n if (typeof candidate[field] === \"number\" && Number.isInteger(candidate[field])) {\n normalized[field] = candidate[field];\n }\n }\n\n if (typeof candidate.extract === \"string\") {\n normalized.extract = candidate.extract;\n }\n\n if (typeof candidate.subType === \"string\") {\n normalized.subType = candidate.subType;\n }\n\n return normalized;\n}\n\n/**\n * Validates HTML using the W3C Nu HTML Checker API\n */\nexport async function validateHtmlContent(htmlContent: string): Promise<W3CMessage[]> {\n const url = \"https://validator.w3.org/nu/?out=json\";\n\n try {\n assertContentSize(htmlContent, MAX_HTML_BYTES, \"HTML content\");\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"User-Agent\": USER_AGENT,\n },\n body: htmlContent,\n redirect: \"error\",\n signal: AbortSignal.timeout(VALIDATOR_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n await cancelResponseBody(response);\n throw new Error(`W3C HTML validator returned HTTP status ${response.status}`);\n }\n\n const text = await readResponseText(response, MAX_VALIDATOR_RESPONSE_BYTES);\n const data = JSON.parse(text) as { messages?: unknown };\n if (data.messages === undefined) {\n throw new Error(\"W3C HTML validator returned an invalid response shape\");\n }\n if (!Array.isArray(data.messages)) {\n throw new Error(\"W3C HTML validator returned an invalid response shape\");\n }\n return data.messages\n .map(normalizeW3CMessage)\n .slice(0, MAX_VALIDATION_MESSAGES);\n } catch (error: unknown) {\n throw new Error(`HTML validation failed: ${getErrorMessage(error)}`);\n }\n}\n\n/**\n * Validates CSS using the W3C Jigsaw CSS Validator API\n */\nexport async function validateCssContent(cssContent: string): Promise<CSSMessage[]> {\n const url = \"https://jigsaw.w3.org/css-validator/validator\";\n\n try {\n assertContentSize(cssContent, MAX_CSS_VALIDATION_BYTES, \"CSS content\");\n const form = new FormData();\n form.set(\"text\", cssContent);\n form.set(\"output\", \"json\");\n form.set(\"warning\", \"0\");\n form.set(\"profile\", \"css3svg\");\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"User-Agent\": USER_AGENT,\n },\n body: form,\n redirect: \"error\",\n signal: AbortSignal.timeout(VALIDATOR_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n await cancelResponseBody(response);\n throw new Error(`W3C CSS validator returned HTTP status ${response.status}`);\n }\n\n const text = await readResponseText(response, MAX_VALIDATOR_RESPONSE_BYTES);\n\n // An empty upstream response is indeterminate and must never be presented as a clean result.\n if (!text || text.trim() === \"\") {\n throw new Error(\"W3C CSS validator returned an empty response\");\n }\n\n const data = JSON.parse(text) as {\n cssvalidation?: {\n errors?: Array<{\n line: number;\n message: string;\n context?: string;\n type?: string;\n }>;\n warnings?: Array<{\n line: number;\n message: string;\n context?: string;\n type?: string;\n }>;\n };\n };\n\n if (!data.cssvalidation || typeof data.cssvalidation !== \"object\") {\n throw new Error(\"W3C CSS validator returned an invalid response shape\");\n }\n\n const errors = data.cssvalidation.errors || [];\n return errors.slice(0, MAX_VALIDATION_MESSAGES).map((err) => {\n const message = err.message ? err.message.trim() : \"Unknown CSS validation error\";\n return {\n line: err.line || 0,\n type: \"error\",\n message,\n context: err.context || undefined,\n ...(isKnownCssValidatorLimitation(err.type, message)\n ? { compatibility: \"known-validator-limitation\" as const }\n : {}),\n };\n });\n } catch (error: unknown) {\n throw new Error(`CSS validation failed: ${getErrorMessage(error)}`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"w3c-validator.js","sourceRoot":"","sources":["../src/w3c-validator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n cancelResponseBody,\n getErrorMessage,\n readResponseText,\n} from \"./network.js\";\nimport { PACKAGE_VERSION } from \"./version.js\";\n\nexport interface W3CMessage {\n type: string;\n subType?: string;\n lastLine?: number;\n lastColumn?: number;\n firstLine?: number;\n firstColumn?: number;\n message: string;\n extract?: string;\n}\n\nexport type W3CMessageSeverity = \"error\" | \"warning\" | \"info\";\n\nexport interface HtmlValidationResult {\n messages: W3CMessage[];\n total: number;\n truncated: boolean;\n counts: Record<W3CMessageSeverity, number>;\n}\n\nexport function getW3CMessageSeverity(\n message: Pick<W3CMessage, \"type\" | \"subType\">,\n): W3CMessageSeverity {\n const type = message.type.trim().toLowerCase();\n if (type === \"error\" || type === \"non-document-error\") return \"error\";\n if (type === \"warning\" || (type === \"info\" && message.subType?.trim().toLowerCase() === \"warning\")) {\n return \"warning\";\n }\n return \"info\";\n}\n\nexport interface CSSMessage {\n line: number;\n type: string;\n message: string;\n context?: string;\n compatibility?: \"known-validator-limitation\";\n}\n\nconst VALIDATOR_TIMEOUT_MS = 20_000;\nconst MAX_HTML_BYTES = 2_000_000;\nexport const MAX_CSS_VALIDATION_BYTES = 128_000;\nconst MAX_VALIDATOR_RESPONSE_BYTES = 5_000_000;\nconst USER_AGENT = `mcp-web-validator/${PACKAGE_VERSION} (+https://digestseo.com/validator-mcp/)`;\nexport const MAX_VALIDATION_MESSAGES = 200;\n\nfunction isKnownCssValidatorLimitation(type: string | undefined, message: string): boolean {\n if (type?.trim().toLowerCase() !== \"at-rule\") return false;\n const normalized = message.trim().toLowerCase().replace(/[“”\"'‘’]/g, \"\");\n return normalized === \"unrecognized at-rule @container\";\n}\n\nfunction assertContentSize(content: string, maxBytes: number, label: string): void {\n const size = Buffer.byteLength(content, \"utf8\");\n if (size > maxBytes) {\n throw new Error(`${label} exceeds the ${maxBytes}-byte validation limit`);\n }\n}\n\nfunction normalizeW3CMessage(message: unknown): W3CMessage {\n if (typeof message !== \"object\" || message === null) {\n throw new Error(\"W3C HTML validator returned a malformed message\");\n }\n\n const candidate = message as Record<string, unknown>;\n if (typeof candidate.type !== \"string\" || typeof candidate.message !== \"string\") {\n throw new Error(\"W3C HTML validator returned a malformed message\");\n }\n\n const normalized: W3CMessage = {\n type: candidate.type,\n message: candidate.message,\n };\n\n for (const field of [\"lastLine\", \"lastColumn\", \"firstLine\", \"firstColumn\"] as const) {\n if (typeof candidate[field] === \"number\" && Number.isInteger(candidate[field])) {\n normalized[field] = candidate[field];\n }\n }\n\n if (typeof candidate.extract === \"string\") {\n normalized.extract = candidate.extract;\n }\n\n if (typeof candidate.subType === \"string\") {\n normalized.subType = candidate.subType;\n }\n\n return normalized;\n}\n\n/** Validates HTML using the W3C Nu HTML Checker API and retains bounded-output metadata. */\nexport async function validateHtmlContentDetailed(htmlContent: string): Promise<HtmlValidationResult> {\n const url = \"https://validator.w3.org/nu/?out=json\";\n\n try {\n assertContentSize(htmlContent, MAX_HTML_BYTES, \"HTML content\");\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"User-Agent\": USER_AGENT,\n },\n body: htmlContent,\n redirect: \"error\",\n signal: AbortSignal.timeout(VALIDATOR_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n await cancelResponseBody(response);\n throw new Error(`W3C HTML validator returned HTTP status ${response.status}`);\n }\n\n const text = await readResponseText(response, MAX_VALIDATOR_RESPONSE_BYTES);\n const data = JSON.parse(text) as { messages?: unknown };\n if (data.messages === undefined) {\n throw new Error(\"W3C HTML validator returned an invalid response shape\");\n }\n if (!Array.isArray(data.messages)) {\n throw new Error(\"W3C HTML validator returned an invalid response shape\");\n }\n const normalized = data.messages.map(normalizeW3CMessage);\n const counts: HtmlValidationResult[\"counts\"] = { error: 0, warning: 0, info: 0 };\n for (const message of normalized) {\n counts[getW3CMessageSeverity(message)] += 1;\n }\n return {\n messages: normalized.slice(0, MAX_VALIDATION_MESSAGES),\n total: normalized.length,\n truncated: normalized.length > MAX_VALIDATION_MESSAGES,\n counts,\n };\n } catch (error: unknown) {\n throw new Error(`HTML validation failed: ${getErrorMessage(error)}`);\n }\n}\n\n/** Backwards-compatible convenience API returning capped diagnostics only. */\nexport async function validateHtmlContent(htmlContent: string): Promise<W3CMessage[]> {\n return (await validateHtmlContentDetailed(htmlContent)).messages;\n}\n\n/**\n * Validates CSS using the W3C Jigsaw CSS Validator API\n */\nexport async function validateCssContent(cssContent: string): Promise<CSSMessage[]> {\n const url = \"https://jigsaw.w3.org/css-validator/validator\";\n\n try {\n assertContentSize(cssContent, MAX_CSS_VALIDATION_BYTES, \"CSS content\");\n const form = new FormData();\n form.set(\"text\", cssContent);\n form.set(\"output\", \"json\");\n form.set(\"warning\", \"0\");\n form.set(\"profile\", \"css3svg\");\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"User-Agent\": USER_AGENT,\n },\n body: form,\n redirect: \"error\",\n signal: AbortSignal.timeout(VALIDATOR_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n await cancelResponseBody(response);\n throw new Error(`W3C CSS validator returned HTTP status ${response.status}`);\n }\n\n const text = await readResponseText(response, MAX_VALIDATOR_RESPONSE_BYTES);\n\n // An empty upstream response is indeterminate and must never be presented as a clean result.\n if (!text || text.trim() === \"\") {\n throw new Error(\"W3C CSS validator returned an empty response\");\n }\n\n const data = JSON.parse(text) as {\n cssvalidation?: {\n errors?: Array<{\n line: number;\n message: string;\n context?: string;\n type?: string;\n }>;\n warnings?: Array<{\n line: number;\n message: string;\n context?: string;\n type?: string;\n }>;\n };\n };\n\n if (!data.cssvalidation || typeof data.cssvalidation !== \"object\") {\n throw new Error(\"W3C CSS validator returned an invalid response shape\");\n }\n\n const errors = data.cssvalidation.errors || [];\n return errors.slice(0, MAX_VALIDATION_MESSAGES).map((err) => {\n const message = err.message ? err.message.trim() : \"Unknown CSS validation error\";\n return {\n line: err.line || 0,\n type: \"error\",\n message,\n context: err.context || undefined,\n ...(isKnownCssValidatorLimitation(err.type, message)\n ? { compatibility: \"known-validator-limitation\" as const }\n : {}),\n };\n });\n } catch (error: unknown) {\n throw new Error(`CSS validation failed: ${getErrorMessage(error)}`);\n }\n}\n"]}
|