nfunc-mcp 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +121 -379
  2. package/dist/index.js +4 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/mappers/a11yDedupe.js +38 -7
  5. package/dist/mappers/a11yDedupe.js.map +1 -1
  6. package/dist/mappers/defectFormatter.d.ts +9 -1
  7. package/dist/mappers/defectFormatter.js +53 -13
  8. package/dist/mappers/defectFormatter.js.map +1 -1
  9. package/dist/mappers/labFieldComparator.d.ts +62 -0
  10. package/dist/mappers/labFieldComparator.js +134 -0
  11. package/dist/mappers/labFieldComparator.js.map +1 -0
  12. package/dist/mappers/priorityMapper.d.ts +42 -0
  13. package/dist/mappers/priorityMapper.js +58 -0
  14. package/dist/mappers/priorityMapper.js.map +1 -1
  15. package/dist/mappers/psiAggregator.d.ts +130 -0
  16. package/dist/mappers/psiAggregator.js +293 -0
  17. package/dist/mappers/psiAggregator.js.map +1 -0
  18. package/dist/mappers/runComparator.d.ts +85 -0
  19. package/dist/mappers/runComparator.js +165 -0
  20. package/dist/mappers/runComparator.js.map +1 -0
  21. package/dist/mappers/wcagLevels.d.ts +73 -0
  22. package/dist/mappers/wcagLevels.js +320 -0
  23. package/dist/mappers/wcagLevels.js.map +1 -0
  24. package/dist/mappers/webVitalsMapper.d.ts +52 -0
  25. package/dist/mappers/webVitalsMapper.js +131 -0
  26. package/dist/mappers/webVitalsMapper.js.map +1 -0
  27. package/dist/tools/accessibility.d.ts +1 -0
  28. package/dist/tools/accessibility.js +488 -63
  29. package/dist/tools/accessibility.js.map +1 -1
  30. package/dist/tools/lighthouse.js +370 -102
  31. package/dist/tools/lighthouse.js.map +1 -1
  32. package/dist/tools/performanceAudit.d.ts +2 -0
  33. package/dist/tools/performanceAudit.js +446 -0
  34. package/dist/tools/performanceAudit.js.map +1 -0
  35. package/dist/tools/performanceAuditPlan.d.ts +2 -0
  36. package/dist/tools/performanceAuditPlan.js +438 -0
  37. package/dist/tools/performanceAuditPlan.js.map +1 -0
  38. package/dist/utils/batchState.d.ts +75 -0
  39. package/dist/utils/batchState.js +128 -0
  40. package/dist/utils/batchState.js.map +1 -0
  41. package/dist/utils/csvReader.d.ts +20 -0
  42. package/dist/utils/csvReader.js +172 -0
  43. package/dist/utils/csvReader.js.map +1 -0
  44. package/dist/utils/httpClient.d.ts +84 -0
  45. package/dist/utils/httpClient.js +171 -0
  46. package/dist/utils/httpClient.js.map +1 -0
  47. package/dist/utils/outputParsers.js +26 -30
  48. package/dist/utils/outputParsers.js.map +1 -1
  49. package/dist/utils/psiAuth.d.ts +26 -0
  50. package/dist/utils/psiAuth.js +36 -0
  51. package/dist/utils/psiAuth.js.map +1 -0
  52. package/dist/utils/psiParser.d.ts +135 -0
  53. package/dist/utils/psiParser.js +200 -0
  54. package/dist/utils/psiParser.js.map +1 -0
  55. package/dist/utils/publicUrl.d.ts +17 -0
  56. package/dist/utils/publicUrl.js +115 -0
  57. package/dist/utils/publicUrl.js.map +1 -0
  58. package/dist/utils/sitemapReader.d.ts +27 -0
  59. package/dist/utils/sitemapReader.js +272 -0
  60. package/dist/utils/sitemapReader.js.map +1 -0
  61. package/dist/utils/urlClassifier.d.ts +45 -0
  62. package/dist/utils/urlClassifier.js +267 -0
  63. package/dist/utils/urlClassifier.js.map +1 -0
  64. package/dist/utils/urlInput.d.ts +30 -0
  65. package/dist/utils/urlInput.js +130 -0
  66. package/dist/utils/urlInput.js.map +1 -0
  67. package/docs/manual.md +769 -0
  68. package/docs/psi-report-spec.md +174 -0
  69. package/package.json +13 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batchState.js","sourceRoot":"","sources":["../../src/utils/batchState.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * URL extraction from a CSV.
3
+ *
4
+ * People arrive with a spreadsheet — an SEO export, a analytics top-pages
5
+ * report, a hand-written list — and the URL column is never in the same place
6
+ * twice. This finds it rather than demanding a fixed format, because the
7
+ * alternative is a tool that rejects the file the user actually has.
8
+ *
9
+ * The parser is a small RFC4180 implementation instead of a split on commas:
10
+ * URLs contain commas inside quoted fields often enough that naive splitting
11
+ * silently truncates them.
12
+ */
13
+ export interface CsvUrlResult {
14
+ urls: string[];
15
+ /** Header name of the column used, or a positional description. */
16
+ column: string;
17
+ rowsRead: number;
18
+ warnings: string[];
19
+ }
20
+ export declare function readUrlsFromCsv(path: string): CsvUrlResult;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * URL extraction from a CSV.
3
+ *
4
+ * People arrive with a spreadsheet — an SEO export, a analytics top-pages
5
+ * report, a hand-written list — and the URL column is never in the same place
6
+ * twice. This finds it rather than demanding a fixed format, because the
7
+ * alternative is a tool that rejects the file the user actually has.
8
+ *
9
+ * The parser is a small RFC4180 implementation instead of a split on commas:
10
+ * URLs contain commas inside quoted fields often enough that naive splitting
11
+ * silently truncates them.
12
+ */
13
+ import { readFileSync } from "node:fs";
14
+ /** Delimiters worth guessing between, in preference order. */
15
+ const DELIMITERS = [",", ";", "\t", "|"];
16
+ /** Header names that mean "this is the URL column", lowercased. */
17
+ const URL_HEADERS = new Set([
18
+ "url", "urls", "page", "page url", "page_url", "address", "link", "loc",
19
+ "landing page", "landing_page", "full url", "final url", "request url",
20
+ ]);
21
+ function detectDelimiter(sample) {
22
+ let best = ",";
23
+ let bestCount = 0;
24
+ for (const d of DELIMITERS) {
25
+ // Count on the header line only; body rows can contain stray delimiters.
26
+ const count = (sample.split(d).length - 1);
27
+ if (count > bestCount) {
28
+ best = d;
29
+ bestCount = count;
30
+ }
31
+ }
32
+ return best;
33
+ }
34
+ /** RFC4180: quoted fields may contain the delimiter, newlines, and "" escapes. */
35
+ function parseCsv(text, delimiter) {
36
+ const rows = [];
37
+ let row = [];
38
+ let field = "";
39
+ let inQuotes = false;
40
+ for (let i = 0; i < text.length; i++) {
41
+ const ch = text[i];
42
+ if (inQuotes) {
43
+ if (ch === '"') {
44
+ if (text[i + 1] === '"') {
45
+ field += '"';
46
+ i++;
47
+ }
48
+ else {
49
+ inQuotes = false;
50
+ }
51
+ }
52
+ else {
53
+ field += ch;
54
+ }
55
+ continue;
56
+ }
57
+ if (ch === '"') {
58
+ inQuotes = true;
59
+ }
60
+ else if (ch === delimiter) {
61
+ row.push(field);
62
+ field = "";
63
+ }
64
+ else if (ch === "\n") {
65
+ row.push(field);
66
+ rows.push(row);
67
+ row = [];
68
+ field = "";
69
+ }
70
+ else if (ch !== "\r") {
71
+ field += ch;
72
+ }
73
+ }
74
+ if (field.length > 0 || row.length > 0) {
75
+ row.push(field);
76
+ rows.push(row);
77
+ }
78
+ return rows.filter((r) => r.some((c) => c.trim() !== ""));
79
+ }
80
+ function looksLikeUrl(value) {
81
+ const v = value.trim();
82
+ if (!/^https?:\/\//i.test(v))
83
+ return false;
84
+ try {
85
+ new URL(v);
86
+ return true;
87
+ }
88
+ catch {
89
+ return false;
90
+ }
91
+ }
92
+ /**
93
+ * Pick the URL column.
94
+ *
95
+ * Header match first, because a file that names its columns is telling us
96
+ * where to look. Falling back to content sniffing covers headerless exports
97
+ * and files whose URL column is called something unguessable — the column
98
+ * where the most cells parse as URLs is the column, regardless of its name.
99
+ */
100
+ function pickUrlColumn(rows) {
101
+ if (rows.length === 0)
102
+ return null;
103
+ const header = rows[0].map((h) => h.trim().toLowerCase());
104
+ const headerIsData = header.some(looksLikeUrl);
105
+ if (!headerIsData) {
106
+ const named = header.findIndex((h) => URL_HEADERS.has(h));
107
+ if (named >= 0) {
108
+ return { index: named, label: rows[0][named].trim(), hasHeader: true };
109
+ }
110
+ }
111
+ const body = headerIsData ? rows : rows.slice(1);
112
+ const width = Math.max(...rows.map((r) => r.length));
113
+ let bestIndex = -1;
114
+ let bestHits = 0;
115
+ for (let col = 0; col < width; col++) {
116
+ const hits = body.filter((r) => looksLikeUrl(r[col] ?? "")).length;
117
+ if (hits > bestHits) {
118
+ bestHits = hits;
119
+ bestIndex = col;
120
+ }
121
+ }
122
+ if (bestIndex < 0 || bestHits === 0)
123
+ return null;
124
+ return {
125
+ index: bestIndex,
126
+ label: headerIsData ? `column ${bestIndex + 1}` : (rows[0][bestIndex]?.trim() || `column ${bestIndex + 1}`),
127
+ hasHeader: !headerIsData,
128
+ };
129
+ }
130
+ export function readUrlsFromCsv(path) {
131
+ const warnings = [];
132
+ let text;
133
+ try {
134
+ text = readFileSync(path, "utf8");
135
+ }
136
+ catch (err) {
137
+ throw new Error(`Could not read CSV at ${path}: ${err.message}`);
138
+ }
139
+ // Strip a UTF-8 BOM; Excel adds one and it corrupts the first header name.
140
+ if (text.charCodeAt(0) === 0xfeff)
141
+ text = text.slice(1);
142
+ const firstLine = text.split(/\r?\n/, 1)[0] ?? "";
143
+ const rows = parseCsv(text, detectDelimiter(firstLine));
144
+ const column = pickUrlColumn(rows);
145
+ if (!column) {
146
+ throw new Error(`No URL column found in ${path}. Expected a column named one of ` +
147
+ `${[...URL_HEADERS].slice(0, 5).join(", ")}, or any column whose ` +
148
+ `values start with http:// or https://.`);
149
+ }
150
+ const body = column.hasHeader ? rows.slice(1) : rows;
151
+ const urls = [];
152
+ const seen = new Set();
153
+ let skipped = 0;
154
+ for (const row of body) {
155
+ const cell = (row[column.index] ?? "").trim();
156
+ if (!cell)
157
+ continue;
158
+ if (!looksLikeUrl(cell)) {
159
+ skipped++;
160
+ continue;
161
+ }
162
+ if (seen.has(cell))
163
+ continue;
164
+ seen.add(cell);
165
+ urls.push(cell);
166
+ }
167
+ if (skipped > 0) {
168
+ warnings.push(`Skipped ${skipped} row(s) in "${column.label}" that did not contain an http(s) URL.`);
169
+ }
170
+ return { urls, column: column.label, rowsRead: body.length, warnings };
171
+ }
172
+ //# sourceMappingURL=csvReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csvReader.js","sourceRoot":"","sources":["../../src/utils/csvReader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * HTTP counterpart to shellRunner.
3
+ *
4
+ * Every tool before this one reached the outside world by spawning a process,
5
+ * so `runShell` was the single choke point for timeout, error and duration
6
+ * handling. PSI is an HTTP API, and bending shellRunner around `curl` would
7
+ * trade a typed response for string parsing. This mirrors shellRunner's
8
+ * contract instead: never throws for a reachable-but-unhappy endpoint, always
9
+ * reports duration, and leaves the decision about what counts as failure to
10
+ * the caller.
11
+ *
12
+ * Node 18+ ships global fetch, so this adds no dependency.
13
+ */
14
+ export interface HttpResult {
15
+ /** True only for a 2xx. A 404 is a result, not an exception. */
16
+ ok: boolean;
17
+ /** 0 when the request never got a response (timeout, DNS, connection refused). */
18
+ status: number;
19
+ body: string;
20
+ /** Raw response bytes. Populated only when `raw` was requested. */
21
+ bytes?: Uint8Array;
22
+ durationMs: number;
23
+ /** Total attempts made, including the successful one. 1 means no retries. */
24
+ attempts: number;
25
+ /** Present only when `ok` is false. Always redacted. */
26
+ error?: string;
27
+ }
28
+ export interface HttpRequestOptions {
29
+ timeoutMs?: number;
30
+ /** Extra attempts after the first. 0 disables retrying. */
31
+ retries?: number;
32
+ /** Base delay for exponential backoff; doubled each attempt. */
33
+ retryDelayMs?: number;
34
+ headers?: Record<string, string>;
35
+ /**
36
+ * Return bytes instead of decoded text. Needed for `.xml.gz` sitemaps, which
37
+ * are served as an opaque gzip payload rather than with Content-Encoding, so
38
+ * fetch does not inflate them and decoding to a string corrupts them.
39
+ */
40
+ raw?: boolean;
41
+ /**
42
+ * Absolute ceiling across every attempt, including backoff. Where `timeoutMs`
43
+ * bounds one request, this bounds the whole call: each attempt is shortened
44
+ * to whatever is left, and retrying stops when nothing is. Without it,
45
+ * enabling `retryOnTimeout` silently doubles the worst case, which is exactly
46
+ * how a chunked runner overruns its budget.
47
+ */
48
+ totalBudgetMs?: number;
49
+ /**
50
+ * Retry after a timeout. Off by default: a timeout means the request already
51
+ * spent its entire budget, so retrying multiplies the wall clock by the
52
+ * attempt count for a request that is unlikely to be faster next time. A 429
53
+ * or 5xx is different — those fail fast and often succeed on retry.
54
+ */
55
+ retryOnTimeout?: boolean;
56
+ /**
57
+ * Literal strings to strip from every error message before it leaves this
58
+ * module — API keys, tokens, anything that would otherwise reach a log, a
59
+ * tool response, or a conversation transcript.
60
+ */
61
+ redact?: string[];
62
+ }
63
+ /**
64
+ * A URL safe to put in an error message.
65
+ *
66
+ * The PSI request URL carries the API key in the query string, so any error
67
+ * that interpolates the URL leaks the key into the tool response — and from
68
+ * there into the conversation transcript. Redacting at the point of formatting
69
+ * rather than asking every call site to remember is the only version of this
70
+ * that stays correct.
71
+ */
72
+ export declare function sanitizeUrl(url: string | URL): string;
73
+ export declare function httpGet(url: string | URL, options?: HttpRequestOptions): Promise<HttpResult>;
74
+ export interface HttpJsonResult<T> extends Omit<HttpResult, "body"> {
75
+ data: T | null;
76
+ /** Kept for error reporting; a caller that has `data` should not need it. */
77
+ body: string;
78
+ }
79
+ /**
80
+ * `httpGet` plus JSON parsing, with malformed JSON demoted to `ok: false`
81
+ * rather than a thrown SyntaxError. A 200 carrying truncated JSON is a failed
82
+ * request as far as every caller is concerned.
83
+ */
84
+ export declare function httpGetJson<T>(url: string | URL, options?: HttpRequestOptions): Promise<HttpJsonResult<T>>;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * HTTP counterpart to shellRunner.
3
+ *
4
+ * Every tool before this one reached the outside world by spawning a process,
5
+ * so `runShell` was the single choke point for timeout, error and duration
6
+ * handling. PSI is an HTTP API, and bending shellRunner around `curl` would
7
+ * trade a typed response for string parsing. This mirrors shellRunner's
8
+ * contract instead: never throws for a reachable-but-unhappy endpoint, always
9
+ * reports duration, and leaves the decision about what counts as failure to
10
+ * the caller.
11
+ *
12
+ * Node 18+ ships global fetch, so this adds no dependency.
13
+ */
14
+ /**
15
+ * Sites behind a bot wall reject the default Node fetch agent outright, which
16
+ * turns a healthy site into a 403 and a sitemap into a "not found". Naming
17
+ * ourselves honestly and looking like a browser gets through most of them;
18
+ * a caller can override it via `headers`.
19
+ */
20
+ const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " +
21
+ "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 nfunc-mcp/qa-audit";
22
+ /** Query parameters whose values are stripped before a URL is quoted anywhere. */
23
+ const SENSITIVE_PARAMS = new Set(["key", "api_key", "apikey", "token", "access_token"]);
24
+ /**
25
+ * A URL safe to put in an error message.
26
+ *
27
+ * The PSI request URL carries the API key in the query string, so any error
28
+ * that interpolates the URL leaks the key into the tool response — and from
29
+ * there into the conversation transcript. Redacting at the point of formatting
30
+ * rather than asking every call site to remember is the only version of this
31
+ * that stays correct.
32
+ */
33
+ export function sanitizeUrl(url) {
34
+ try {
35
+ const u = new URL(url);
36
+ for (const param of u.searchParams.keys()) {
37
+ if (SENSITIVE_PARAMS.has(param.toLowerCase())) {
38
+ u.searchParams.set(param, "REDACTED");
39
+ }
40
+ }
41
+ return u.toString();
42
+ }
43
+ catch {
44
+ return "[unparseable url]";
45
+ }
46
+ }
47
+ function redactAll(message, secrets) {
48
+ let out = message;
49
+ for (const secret of secrets) {
50
+ // A short "secret" would redact half the message; a real key is far longer.
51
+ if (secret && secret.length >= 8)
52
+ out = out.split(secret).join("REDACTED");
53
+ }
54
+ return out;
55
+ }
56
+ /** Retry only what a retry can plausibly fix. A 400 will be a 400 next time too. */
57
+ function isRetryable(status) {
58
+ return status === 408 || status === 429 || status >= 500;
59
+ }
60
+ /**
61
+ * `Retry-After` is either a delay in seconds or an HTTP date. Google sends
62
+ * seconds on 429s, but the date form is legal and cheap to support.
63
+ */
64
+ function retryAfterMs(header) {
65
+ if (!header)
66
+ return null;
67
+ const seconds = Number(header);
68
+ if (Number.isFinite(seconds) && seconds >= 0)
69
+ return seconds * 1000;
70
+ const date = Date.parse(header);
71
+ if (!Number.isNaN(date))
72
+ return Math.max(0, date - Date.now());
73
+ return null;
74
+ }
75
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
76
+ export async function httpGet(url, options = {}) {
77
+ const { timeoutMs = 60_000, retries = 2, retryDelayMs = 2_000, headers, raw = false, totalBudgetMs, retryOnTimeout = false, redact = [], } = options;
78
+ const start = Date.now();
79
+ const safeUrl = sanitizeUrl(url);
80
+ let lastError = "";
81
+ let lastStatus = 0;
82
+ const deadline = totalBudgetMs === undefined ? null : start + totalBudgetMs;
83
+ const remainingMs = () => (deadline === null ? Infinity : deadline - Date.now());
84
+ for (let attempt = 1; attempt <= retries + 1; attempt++) {
85
+ const budgetLeft = remainingMs();
86
+ if (budgetLeft <= 0) {
87
+ lastError = lastError || `Request to ${safeUrl} ran out of time budget`;
88
+ break;
89
+ }
90
+ try {
91
+ const response = await fetch(url, {
92
+ headers: { "user-agent": DEFAULT_USER_AGENT, ...headers },
93
+ signal: AbortSignal.timeout(Math.min(timeoutMs, budgetLeft)),
94
+ });
95
+ lastStatus = response.status;
96
+ if (response.ok) {
97
+ const bytes = new Uint8Array(await response.arrayBuffer());
98
+ return {
99
+ ok: true,
100
+ status: response.status,
101
+ body: raw ? "" : new TextDecoder().decode(bytes),
102
+ ...(raw ? { bytes } : {}),
103
+ durationMs: Date.now() - start,
104
+ attempts: attempt,
105
+ };
106
+ }
107
+ // Read the body before deciding — a non-2xx from PSI carries a JSON
108
+ // error object that says far more than the status code does.
109
+ const body = await response.text().catch(() => "");
110
+ lastError = `HTTP ${response.status} from ${safeUrl}${body ? `: ${body.slice(0, 300)}` : ""}`;
111
+ if (!isRetryable(response.status) || attempt > retries) {
112
+ return {
113
+ ok: false,
114
+ status: response.status,
115
+ body,
116
+ durationMs: Date.now() - start,
117
+ attempts: attempt,
118
+ error: redactAll(lastError, redact),
119
+ };
120
+ }
121
+ const serverAsked = retryAfterMs(response.headers.get("retry-after"));
122
+ const backoff = Math.min(serverAsked ?? retryDelayMs * 2 ** (attempt - 1), Math.max(0, remainingMs()));
123
+ await sleep(backoff);
124
+ }
125
+ catch (err) {
126
+ // Timeouts and network failures land here. AbortSignal.timeout raises a
127
+ // TimeoutError, which reads as an unexplained abort unless named.
128
+ const e = err;
129
+ const timedOut = e.name === "TimeoutError" || e.name === "AbortError";
130
+ lastStatus = 0;
131
+ lastError = timedOut
132
+ ? `Request to ${safeUrl} timed out after ${timeoutMs} ms`
133
+ : `Request to ${safeUrl} failed: ${e.message ?? "unknown network error"}`;
134
+ if (timedOut && !retryOnTimeout)
135
+ break;
136
+ if (attempt > retries)
137
+ break;
138
+ await sleep(Math.min(retryDelayMs * 2 ** (attempt - 1), Math.max(0, remainingMs())));
139
+ }
140
+ }
141
+ return {
142
+ ok: false,
143
+ status: lastStatus,
144
+ body: "",
145
+ durationMs: Date.now() - start,
146
+ attempts: retries + 1,
147
+ error: redactAll(lastError, redact),
148
+ };
149
+ }
150
+ /**
151
+ * `httpGet` plus JSON parsing, with malformed JSON demoted to `ok: false`
152
+ * rather than a thrown SyntaxError. A 200 carrying truncated JSON is a failed
153
+ * request as far as every caller is concerned.
154
+ */
155
+ export async function httpGetJson(url, options = {}) {
156
+ const result = await httpGet(url, options);
157
+ if (!result.ok)
158
+ return { ...result, data: null };
159
+ try {
160
+ return { ...result, data: JSON.parse(result.body) };
161
+ }
162
+ catch (err) {
163
+ return {
164
+ ...result,
165
+ ok: false,
166
+ data: null,
167
+ error: redactAll(`Response from ${sanitizeUrl(url)} was not valid JSON: ${err.message}`, options.redact ?? []),
168
+ };
169
+ }
170
+ }
171
+ //# sourceMappingURL=httpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpClient.js","sourceRoot":"","sources":["../../src/utils/httpClient.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { criterionFor } from "../mappers/wcagLevels.js";
1
2
  export function parseLighthouseJSON(rawJson) {
2
3
  const lhr = JSON.parse(rawJson);
3
4
  const categoryScores = {};
@@ -100,28 +101,21 @@ const WCAG_CRITERION_LEVELS = {
100
101
  "4.1.2": "A",
101
102
  "4.1.3": "AA",
102
103
  };
103
- function deriveWcagLevel(code) {
104
- // pa11y/HTMLCS code shape: WCAG2AA.Principle1.Guideline1_1.1_1_1.H37
105
- const segments = code.split(".");
106
- let criterion = null;
107
- for (const seg of segments) {
108
- if (/^\d+(_\d+)+$/.test(seg)) {
109
- criterion = seg.replace(/_/g, ".");
110
- break;
111
- }
112
- }
113
- if (criterion && WCAG_CRITERION_LEVELS[criterion]) {
114
- return { level: WCAG_CRITERION_LEVELS[criterion], criterion };
115
- }
116
- // Fallback: infer from standard prefix when criterion is unknown.
117
- const prefix = segments[0] ?? "";
118
- if (prefix === "WCAG2AAA")
119
- return { level: "AAA", criterion };
120
- if (prefix === "WCAG2AA")
121
- return { level: "AA", criterion };
122
- if (prefix === "WCAG2A")
123
- return { level: "A", criterion };
124
- return { level: "unknown", criterion };
104
+ /**
105
+ * Resolve a finding to its WCAG success criterion and level.
106
+ *
107
+ * Both engines are handled here now. htmlcs encodes the criterion in its code;
108
+ * axe does not expose WCAG tags through pa11y at all, so its rule ids go
109
+ * through a lookup table. The previous implementation inferred the level from
110
+ * the code's "WCAG2AA" prefix when the criterion was unknown — but that prefix
111
+ * names the *standard being tested against*, not the criterion's own level, so
112
+ * it labelled everything AA and made a level-based priority scheme impossible.
113
+ */
114
+ function deriveWcagLevel(code, runner) {
115
+ const found = criterionFor(code, runner);
116
+ if (found)
117
+ return { level: found.level, criterion: found.number };
118
+ return { level: "unknown", criterion: null };
125
119
  }
126
120
  function isAxeImpact(v) {
127
121
  return v === "critical" || v === "serious" || v === "moderate" || v === "minor";
@@ -187,12 +181,14 @@ export function parsePa11yJSON(rawJson) {
187
181
  const type = (issue.type ?? "error");
188
182
  const runner = issue.runner === "axe" ? "axe" : "htmlcs";
189
183
  if (runner === "axe") {
190
- // axe codes are bare rule ids with no WCAG path to parse, so the
191
- // criterion-derivation below cannot classify them it would return
192
- // level "unknown", which the priority mapper drops. Severity comes from
193
- // axe's own impact rating instead. (Skipping this branch silently
194
- // discarded every axe finding.)
184
+ // axe codes are bare rule ids with no WCAG path in them, so they go
185
+ // through the rule → criterion table rather than the code parser. This
186
+ // branch used to hardcode level "unknown" because that table did not
187
+ // exist; leaving it that way after adding the table made every axe
188
+ // finding read as a best-practice nit, including image-alt and
189
+ // color-contrast, which are 1.1.1 (A) and 1.4.3 (AA).
195
190
  const impact = issue.runnerExtras?.impact;
191
+ const axeCriterion = deriveWcagLevel(code, runner);
196
192
  violations.push({
197
193
  code,
198
194
  technique: code,
@@ -200,15 +196,15 @@ export function parsePa11yJSON(rawJson) {
200
196
  selector: issue.selector ?? "",
201
197
  context: issue.context ?? "",
202
198
  type,
203
- wcagLevel: "unknown",
204
- criterion: null,
199
+ wcagLevel: axeCriterion.level,
200
+ criterion: axeCriterion.criterion,
205
201
  runner,
206
202
  impact: isAxeImpact(impact) ? impact : undefined,
207
203
  needsReview: issue.runnerExtras?.needsFurtherReview === true,
208
204
  });
209
205
  continue;
210
206
  }
211
- const { level, criterion } = deriveWcagLevel(code);
207
+ const { level, criterion } = deriveWcagLevel(code, runner);
212
208
  violations.push({
213
209
  code,
214
210
  technique: extractTechnique(code),
@@ -1 +1 @@
1
- {"version":3,"file":"outputParsers.js","sourceRoot":"","sources":["../../src/utils/outputParsers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"outputParsers.js","sourceRoot":"","sources":["../../src/utils/outputParsers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * PSI API key resolution.
3
+ *
4
+ * Precedence: explicit tool input → PAGESPEED_API_KEY → keyless.
5
+ *
6
+ * The preferred setup is the `env` block in the MCP client config, because the
7
+ * client launches this server and we do not control its flags — `--env-file`
8
+ * is not available to us. An explicit input is supported for people who want
9
+ * to paste a key once, but it is the worst option: it lands in the
10
+ * conversation transcript and in any client-side logging, so it is never
11
+ * echoed back and every error that touches a request URL is redacted.
12
+ */
13
+ export type KeySource = "input" | "env" | "none";
14
+ export interface ResolvedKey {
15
+ key: string | null;
16
+ source: KeySource;
17
+ }
18
+ export declare const KEY_ENV_VAR = "PAGESPEED_API_KEY";
19
+ /**
20
+ * Keyless PSI is rate-limited hard enough that a batch will 429 partway
21
+ * through, leaving a half-finished audit and a confusing error. Refusing up
22
+ * front with instructions is a better failure than discovering it at run 12.
23
+ */
24
+ export declare const KEYLESS_RUN_CAP = 4;
25
+ export declare function resolveApiKey(explicit?: string): ResolvedKey;
26
+ export declare function keylessWarning(runs: number): string;