@ticktockbent/charlotte 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/dist/browser/browser-manager.d.ts +14 -0
- package/dist/browser/browser-manager.d.ts.map +1 -0
- package/dist/browser/browser-manager.js +72 -0
- package/dist/browser/browser-manager.js.map +1 -0
- package/dist/browser/cdp-session.d.ts +7 -0
- package/dist/browser/cdp-session.d.ts.map +1 -0
- package/dist/browser/cdp-session.js +35 -0
- package/dist/browser/cdp-session.js.map +1 -0
- package/dist/browser/page-manager.d.ts +30 -0
- package/dist/browser/page-manager.d.ts.map +1 -0
- package/dist/browser/page-manager.js +123 -0
- package/dist/browser/page-manager.js.map +1 -0
- package/dist/dev/auditor.d.ts +39 -0
- package/dist/dev/auditor.d.ts.map +1 -0
- package/dist/dev/auditor.js +474 -0
- package/dist/dev/auditor.js.map +1 -0
- package/dist/dev/dev-mode-state.d.ts +24 -0
- package/dist/dev/dev-mode-state.d.ts.map +1 -0
- package/dist/dev/dev-mode-state.js +93 -0
- package/dist/dev/dev-mode-state.js.map +1 -0
- package/dist/dev/file-watcher.d.ts +20 -0
- package/dist/dev/file-watcher.d.ts.map +1 -0
- package/dist/dev/file-watcher.js +78 -0
- package/dist/dev/file-watcher.js.map +1 -0
- package/dist/dev/static-server.d.ts +18 -0
- package/dist/dev/static-server.d.ts.map +1 -0
- package/dist/dev/static-server.js +73 -0
- package/dist/dev/static-server.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/renderer/accessibility-extractor.d.ts +19 -0
- package/dist/renderer/accessibility-extractor.d.ts.map +1 -0
- package/dist/renderer/accessibility-extractor.js +138 -0
- package/dist/renderer/accessibility-extractor.js.map +1 -0
- package/dist/renderer/content-extractor.d.ts +6 -0
- package/dist/renderer/content-extractor.d.ts.map +1 -0
- package/dist/renderer/content-extractor.js +150 -0
- package/dist/renderer/content-extractor.js.map +1 -0
- package/dist/renderer/dom-path.d.ts +4 -0
- package/dist/renderer/dom-path.d.ts.map +1 -0
- package/dist/renderer/dom-path.js +34 -0
- package/dist/renderer/dom-path.js.map +1 -0
- package/dist/renderer/element-id-generator.d.ts +19 -0
- package/dist/renderer/element-id-generator.d.ts.map +1 -0
- package/dist/renderer/element-id-generator.js +73 -0
- package/dist/renderer/element-id-generator.js.map +1 -0
- package/dist/renderer/interactive-extractor.d.ts +13 -0
- package/dist/renderer/interactive-extractor.d.ts.map +1 -0
- package/dist/renderer/interactive-extractor.js +161 -0
- package/dist/renderer/interactive-extractor.js.map +1 -0
- package/dist/renderer/layout-extractor.d.ts +8 -0
- package/dist/renderer/layout-extractor.d.ts.map +1 -0
- package/dist/renderer/layout-extractor.js +48 -0
- package/dist/renderer/layout-extractor.js.map +1 -0
- package/dist/renderer/renderer-pipeline.d.ts +26 -0
- package/dist/renderer/renderer-pipeline.d.ts.map +1 -0
- package/dist/renderer/renderer-pipeline.js +163 -0
- package/dist/renderer/renderer-pipeline.js.map +1 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +39 -0
- package/dist/server.js.map +1 -0
- package/dist/state/differ.d.ts +9 -0
- package/dist/state/differ.d.ts.map +1 -0
- package/dist/state/differ.js +295 -0
- package/dist/state/differ.js.map +1 -0
- package/dist/state/snapshot-store.d.ts +52 -0
- package/dist/state/snapshot-store.d.ts.map +1 -0
- package/dist/state/snapshot-store.js +98 -0
- package/dist/state/snapshot-store.js.map +1 -0
- package/dist/tools/dev-mode.d.ts +4 -0
- package/dist/tools/dev-mode.d.ts.map +1 -0
- package/dist/tools/dev-mode.js +160 -0
- package/dist/tools/dev-mode.js.map +1 -0
- package/dist/tools/evaluate.d.ts +10 -0
- package/dist/tools/evaluate.d.ts.map +1 -0
- package/dist/tools/evaluate.js +109 -0
- package/dist/tools/evaluate.js.map +1 -0
- package/dist/tools/interaction.d.ts +4 -0
- package/dist/tools/interaction.d.ts.map +1 -0
- package/dist/tools/interaction.js +680 -0
- package/dist/tools/interaction.js.map +1 -0
- package/dist/tools/navigation.d.ts +4 -0
- package/dist/tools/navigation.d.ts.map +1 -0
- package/dist/tools/navigation.js +136 -0
- package/dist/tools/navigation.js.map +1 -0
- package/dist/tools/observation.d.ts +4 -0
- package/dist/tools/observation.d.ts.map +1 -0
- package/dist/tools/observation.js +278 -0
- package/dist/tools/observation.js.map +1 -0
- package/dist/tools/session.d.ts +4 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +372 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/tool-helpers.d.ts +89 -0
- package/dist/tools/tool-helpers.d.ts.map +1 -0
- package/dist/tools/tool-helpers.js +127 -0
- package/dist/tools/tool-helpers.js.map +1 -0
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/element-id.d.ts +8 -0
- package/dist/types/element-id.d.ts.map +1 -0
- package/dist/types/element-id.js +19 -0
- package/dist/types/element-id.js.map +1 -0
- package/dist/types/errors.d.ts +22 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +30 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/page-representation.d.ts +84 -0
- package/dist/types/page-representation.d.ts.map +1 -0
- package/dist/types/page-representation.js +2 -0
- package/dist/types/page-representation.js.map +1 -0
- package/dist/types/snapshot.d.ts +22 -0
- package/dist/types/snapshot.d.ts.map +1 -0
- package/dist/types/snapshot.js +2 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +6 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +31 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/wait.d.ts +21 -0
- package/dist/utils/wait.d.ts.map +1 -0
- package/dist/utils/wait.js +55 -0
- package/dist/utils/wait.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { logger } from "../utils/logger.js";
|
|
2
|
+
const ALL_CATEGORIES = [
|
|
3
|
+
"a11y",
|
|
4
|
+
"performance",
|
|
5
|
+
"seo",
|
|
6
|
+
"contrast",
|
|
7
|
+
"links",
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Compute relative luminance of a color per WCAG 2.1.
|
|
11
|
+
* Input: r, g, b in 0-255 range.
|
|
12
|
+
*/
|
|
13
|
+
export function relativeLuminance(r, g, b) {
|
|
14
|
+
const [rs, gs, bs] = [r / 255, g / 255, b / 255].map((channel) => channel <= 0.03928
|
|
15
|
+
? channel / 12.92
|
|
16
|
+
: Math.pow((channel + 0.055) / 1.055, 2.4));
|
|
17
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Compute WCAG 2.1 contrast ratio between two colors.
|
|
21
|
+
* Returns a ratio >= 1.
|
|
22
|
+
*/
|
|
23
|
+
export function contrastRatio(foregroundRgb, backgroundRgb) {
|
|
24
|
+
const luminanceForeground = relativeLuminance(...foregroundRgb);
|
|
25
|
+
const luminanceBackground = relativeLuminance(...backgroundRgb);
|
|
26
|
+
const lighter = Math.max(luminanceForeground, luminanceBackground);
|
|
27
|
+
const darker = Math.min(luminanceForeground, luminanceBackground);
|
|
28
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse a CSS color string like "rgb(255, 0, 0)" or "rgba(255, 0, 0, 1)" to [r, g, b].
|
|
32
|
+
* Returns null if unparseable.
|
|
33
|
+
*/
|
|
34
|
+
export function parseRgbColor(colorString) {
|
|
35
|
+
const rgbMatch = colorString.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
|
|
36
|
+
if (rgbMatch) {
|
|
37
|
+
return [
|
|
38
|
+
parseInt(rgbMatch[1], 10),
|
|
39
|
+
parseInt(rgbMatch[2], 10),
|
|
40
|
+
parseInt(rgbMatch[3], 10),
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function buildSummary(findings) {
|
|
46
|
+
if (findings.length === 0) {
|
|
47
|
+
return "No issues found.";
|
|
48
|
+
}
|
|
49
|
+
const errorCount = findings.filter((f) => f.severity === "error").length;
|
|
50
|
+
const warningCount = findings.filter((f) => f.severity === "warning").length;
|
|
51
|
+
const infoCount = findings.filter((f) => f.severity === "info").length;
|
|
52
|
+
const parts = [];
|
|
53
|
+
if (errorCount > 0)
|
|
54
|
+
parts.push(`${errorCount} error${errorCount > 1 ? "s" : ""}`);
|
|
55
|
+
if (warningCount > 0)
|
|
56
|
+
parts.push(`${warningCount} warning${warningCount > 1 ? "s" : ""}`);
|
|
57
|
+
if (infoCount > 0)
|
|
58
|
+
parts.push(`${infoCount} info`);
|
|
59
|
+
return `${findings.length} finding${findings.length > 1 ? "s" : ""}: ${parts.join(", ")}`;
|
|
60
|
+
}
|
|
61
|
+
export class Auditor {
|
|
62
|
+
async audit(page, session, categories) {
|
|
63
|
+
const categoriesToCheck = categories && categories.length > 0
|
|
64
|
+
? categories
|
|
65
|
+
: ALL_CATEGORIES;
|
|
66
|
+
const allFindings = [];
|
|
67
|
+
for (const category of categoriesToCheck) {
|
|
68
|
+
try {
|
|
69
|
+
const categoryFindings = await this.runCategoryAudit(category, page, session);
|
|
70
|
+
allFindings.push(...categoryFindings);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.warn(`Audit category '${category}' failed`, error);
|
|
74
|
+
allFindings.push({
|
|
75
|
+
category,
|
|
76
|
+
severity: "warning",
|
|
77
|
+
message: `Audit check failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
78
|
+
recommendation: "Check console for details and retry.",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
categories_checked: categoriesToCheck,
|
|
84
|
+
findings: allFindings,
|
|
85
|
+
summary: buildSummary(allFindings),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async runCategoryAudit(category, page, session) {
|
|
89
|
+
switch (category) {
|
|
90
|
+
case "a11y":
|
|
91
|
+
return this.auditAccessibility(session);
|
|
92
|
+
case "performance":
|
|
93
|
+
return this.auditPerformance(session);
|
|
94
|
+
case "seo":
|
|
95
|
+
return this.auditSeo(page);
|
|
96
|
+
case "contrast":
|
|
97
|
+
return this.auditContrast(page, session);
|
|
98
|
+
case "links":
|
|
99
|
+
return this.auditLinks(page);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async auditAccessibility(session) {
|
|
103
|
+
const findings = [];
|
|
104
|
+
const { nodes } = await session.send("Accessibility.getFullAXTree");
|
|
105
|
+
const headingLevels = [];
|
|
106
|
+
for (const node of nodes) {
|
|
107
|
+
const role = node.role?.value;
|
|
108
|
+
const name = node.name?.value;
|
|
109
|
+
const nameIsEmpty = !name || name.trim() === "";
|
|
110
|
+
// Images without alt text (AX role is "image", not "img")
|
|
111
|
+
if ((role === "image" || role === "img") && nameIsEmpty) {
|
|
112
|
+
findings.push({
|
|
113
|
+
category: "a11y",
|
|
114
|
+
severity: "error",
|
|
115
|
+
message: "Image has no accessible name (missing alt text).",
|
|
116
|
+
element: `backendNodeId:${node.backendDOMNodeId}`,
|
|
117
|
+
recommendation: "Add an alt attribute describing the image, or alt=\"\" for decorative images.",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Buttons without labels
|
|
121
|
+
if (role === "button" && nameIsEmpty) {
|
|
122
|
+
findings.push({
|
|
123
|
+
category: "a11y",
|
|
124
|
+
severity: "error",
|
|
125
|
+
message: "Button has no accessible name.",
|
|
126
|
+
element: `backendNodeId:${node.backendDOMNodeId}`,
|
|
127
|
+
recommendation: "Add text content, aria-label, or aria-labelledby to the button.",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// Links without labels
|
|
131
|
+
if (role === "link" && nameIsEmpty) {
|
|
132
|
+
findings.push({
|
|
133
|
+
category: "a11y",
|
|
134
|
+
severity: "error",
|
|
135
|
+
message: "Link has no accessible name.",
|
|
136
|
+
element: `backendNodeId:${node.backendDOMNodeId}`,
|
|
137
|
+
recommendation: "Add text content, aria-label, or aria-labelledby to the link.",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// Form inputs without labels
|
|
141
|
+
if ((role === "textbox" ||
|
|
142
|
+
role === "combobox" ||
|
|
143
|
+
role === "checkbox" ||
|
|
144
|
+
role === "radio" ||
|
|
145
|
+
role === "spinbutton" ||
|
|
146
|
+
role === "slider") &&
|
|
147
|
+
nameIsEmpty) {
|
|
148
|
+
findings.push({
|
|
149
|
+
category: "a11y",
|
|
150
|
+
severity: "error",
|
|
151
|
+
message: `Form input (${role}) has no accessible name.`,
|
|
152
|
+
element: `backendNodeId:${node.backendDOMNodeId}`,
|
|
153
|
+
recommendation: "Add a <label> element, aria-label, or aria-labelledby.",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Track heading levels for hierarchy check
|
|
157
|
+
if (role === "heading") {
|
|
158
|
+
const level = node.properties?.find((p) => p.name === "level")?.value?.value;
|
|
159
|
+
if (typeof level === "number") {
|
|
160
|
+
headingLevels.push(level);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Check heading hierarchy
|
|
165
|
+
for (let i = 1; i < headingLevels.length; i++) {
|
|
166
|
+
const previousLevel = headingLevels[i - 1];
|
|
167
|
+
const currentLevel = headingLevels[i];
|
|
168
|
+
if (currentLevel > previousLevel + 1) {
|
|
169
|
+
findings.push({
|
|
170
|
+
category: "a11y",
|
|
171
|
+
severity: "warning",
|
|
172
|
+
message: `Heading level skipped: h${previousLevel} followed by h${currentLevel}.`,
|
|
173
|
+
recommendation: `Use sequential heading levels (h${previousLevel} → h${previousLevel + 1}).`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return findings;
|
|
178
|
+
}
|
|
179
|
+
async auditPerformance(session) {
|
|
180
|
+
const findings = [];
|
|
181
|
+
await session.send("Performance.enable");
|
|
182
|
+
const { metrics } = await session.send("Performance.getMetrics");
|
|
183
|
+
await session.send("Performance.disable");
|
|
184
|
+
const metricMap = new Map();
|
|
185
|
+
for (const metric of metrics) {
|
|
186
|
+
metricMap.set(metric.name, metric.value);
|
|
187
|
+
}
|
|
188
|
+
// DOM node count
|
|
189
|
+
const domNodeCount = metricMap.get("Nodes");
|
|
190
|
+
if (domNodeCount !== undefined) {
|
|
191
|
+
if (domNodeCount > 3000) {
|
|
192
|
+
findings.push({
|
|
193
|
+
category: "performance",
|
|
194
|
+
severity: "error",
|
|
195
|
+
message: `DOM has ${domNodeCount} nodes (exceeds 3000).`,
|
|
196
|
+
recommendation: "Reduce DOM complexity by removing unnecessary elements or using virtualization.",
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else if (domNodeCount > 1500) {
|
|
200
|
+
findings.push({
|
|
201
|
+
category: "performance",
|
|
202
|
+
severity: "warning",
|
|
203
|
+
message: `DOM has ${domNodeCount} nodes (exceeds 1500).`,
|
|
204
|
+
recommendation: "Consider simplifying the DOM structure for better performance.",
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Heap usage
|
|
209
|
+
const heapUsed = metricMap.get("JSHeapUsedSize");
|
|
210
|
+
const heapTotal = metricMap.get("JSHeapTotalSize");
|
|
211
|
+
if (heapUsed !== undefined && heapTotal !== undefined && heapTotal > 0) {
|
|
212
|
+
const heapPercent = (heapUsed / heapTotal) * 100;
|
|
213
|
+
if (heapPercent > 80) {
|
|
214
|
+
findings.push({
|
|
215
|
+
category: "performance",
|
|
216
|
+
severity: "warning",
|
|
217
|
+
message: `JS heap usage is ${heapPercent.toFixed(1)}% (${(heapUsed / 1024 / 1024).toFixed(1)}MB / ${(heapTotal / 1024 / 1024).toFixed(1)}MB).`,
|
|
218
|
+
recommendation: "Investigate memory usage and potential memory leaks.",
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Layout and style recalc counts
|
|
223
|
+
const layoutCount = metricMap.get("LayoutCount");
|
|
224
|
+
const recalcStyleCount = metricMap.get("RecalcStyleCount");
|
|
225
|
+
const scriptDuration = metricMap.get("ScriptDuration");
|
|
226
|
+
const taskDuration = metricMap.get("TaskDuration");
|
|
227
|
+
const performanceStats = [];
|
|
228
|
+
if (layoutCount !== undefined)
|
|
229
|
+
performanceStats.push(`layouts: ${layoutCount}`);
|
|
230
|
+
if (recalcStyleCount !== undefined)
|
|
231
|
+
performanceStats.push(`style recalcs: ${recalcStyleCount}`);
|
|
232
|
+
if (scriptDuration !== undefined)
|
|
233
|
+
performanceStats.push(`script: ${(scriptDuration * 1000).toFixed(0)}ms`);
|
|
234
|
+
if (taskDuration !== undefined)
|
|
235
|
+
performanceStats.push(`total tasks: ${(taskDuration * 1000).toFixed(0)}ms`);
|
|
236
|
+
if (performanceStats.length > 0) {
|
|
237
|
+
findings.push({
|
|
238
|
+
category: "performance",
|
|
239
|
+
severity: "info",
|
|
240
|
+
message: `Performance metrics: ${performanceStats.join(", ")}.`,
|
|
241
|
+
recommendation: "Use these metrics as a baseline for performance comparisons.",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return findings;
|
|
245
|
+
}
|
|
246
|
+
async auditSeo(page) {
|
|
247
|
+
const findings = [];
|
|
248
|
+
const seoData = await page.evaluate(() => {
|
|
249
|
+
const title = document.title;
|
|
250
|
+
const metaDescription = document
|
|
251
|
+
.querySelector('meta[name="description"]')
|
|
252
|
+
?.getAttribute("content");
|
|
253
|
+
const metaViewport = document
|
|
254
|
+
.querySelector('meta[name="viewport"]')
|
|
255
|
+
?.getAttribute("content");
|
|
256
|
+
const htmlLang = document.documentElement.getAttribute("lang");
|
|
257
|
+
const h1Elements = document.querySelectorAll("h1");
|
|
258
|
+
const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")).map((heading) => parseInt(heading.tagName.substring(1), 10));
|
|
259
|
+
return {
|
|
260
|
+
title: title || "",
|
|
261
|
+
metaDescription: metaDescription ?? null,
|
|
262
|
+
metaViewport: metaViewport ?? null,
|
|
263
|
+
htmlLang: htmlLang ?? null,
|
|
264
|
+
h1Count: h1Elements.length,
|
|
265
|
+
headingLevels: headings,
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
if (!seoData.title) {
|
|
269
|
+
findings.push({
|
|
270
|
+
category: "seo",
|
|
271
|
+
severity: "error",
|
|
272
|
+
message: "Page has no <title> tag or title is empty.",
|
|
273
|
+
recommendation: "Add a descriptive <title> element in the <head>.",
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (seoData.metaDescription === null) {
|
|
277
|
+
findings.push({
|
|
278
|
+
category: "seo",
|
|
279
|
+
severity: "warning",
|
|
280
|
+
message: "Page has no <meta name=\"description\"> tag.",
|
|
281
|
+
recommendation: "Add a meta description for search engine summaries.",
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
else if (seoData.metaDescription.trim() === "") {
|
|
285
|
+
findings.push({
|
|
286
|
+
category: "seo",
|
|
287
|
+
severity: "warning",
|
|
288
|
+
message: "Meta description is empty.",
|
|
289
|
+
recommendation: "Add meaningful content to the meta description.",
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (seoData.metaViewport === null) {
|
|
293
|
+
findings.push({
|
|
294
|
+
category: "seo",
|
|
295
|
+
severity: "warning",
|
|
296
|
+
message: "Page has no <meta name=\"viewport\"> tag.",
|
|
297
|
+
recommendation: 'Add <meta name="viewport" content="width=device-width, initial-scale=1.0">.',
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (seoData.htmlLang === null) {
|
|
301
|
+
findings.push({
|
|
302
|
+
category: "seo",
|
|
303
|
+
severity: "warning",
|
|
304
|
+
message: "HTML element has no lang attribute.",
|
|
305
|
+
recommendation: 'Add lang attribute to the <html> element, e.g. <html lang="en">.',
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (seoData.h1Count === 0) {
|
|
309
|
+
findings.push({
|
|
310
|
+
category: "seo",
|
|
311
|
+
severity: "warning",
|
|
312
|
+
message: "Page has no <h1> element.",
|
|
313
|
+
recommendation: "Add a single <h1> element for the page's main topic.",
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else if (seoData.h1Count > 1) {
|
|
317
|
+
findings.push({
|
|
318
|
+
category: "seo",
|
|
319
|
+
severity: "warning",
|
|
320
|
+
message: `Page has ${seoData.h1Count} <h1> elements.`,
|
|
321
|
+
recommendation: "Use only one <h1> per page for clarity.",
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
// Check heading hierarchy
|
|
325
|
+
for (let i = 1; i < seoData.headingLevels.length; i++) {
|
|
326
|
+
const previousLevel = seoData.headingLevels[i - 1];
|
|
327
|
+
const currentLevel = seoData.headingLevels[i];
|
|
328
|
+
if (currentLevel > previousLevel + 1) {
|
|
329
|
+
findings.push({
|
|
330
|
+
category: "seo",
|
|
331
|
+
severity: "info",
|
|
332
|
+
message: `Heading hierarchy skip: h${previousLevel} → h${currentLevel}.`,
|
|
333
|
+
recommendation: "Use sequential heading levels for better SEO structure.",
|
|
334
|
+
});
|
|
335
|
+
break; // Only report the first skip
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return findings;
|
|
339
|
+
}
|
|
340
|
+
async auditContrast(page, session) {
|
|
341
|
+
const findings = [];
|
|
342
|
+
const MAX_TEXT_ELEMENTS_TO_CHECK = 50;
|
|
343
|
+
// Get text elements with their computed styles via page.evaluate
|
|
344
|
+
const textElementData = await page.evaluate((maxElements) => {
|
|
345
|
+
const elements = [];
|
|
346
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
|
|
347
|
+
acceptNode: (node) => {
|
|
348
|
+
const text = node.textContent?.trim();
|
|
349
|
+
if (!text || text.length === 0)
|
|
350
|
+
return NodeFilter.FILTER_REJECT;
|
|
351
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
let count = 0;
|
|
355
|
+
while (walker.nextNode() && count < maxElements) {
|
|
356
|
+
const textNode = walker.currentNode;
|
|
357
|
+
const parentElement = textNode.parentElement;
|
|
358
|
+
if (!parentElement)
|
|
359
|
+
continue;
|
|
360
|
+
const computedStyle = window.getComputedStyle(parentElement);
|
|
361
|
+
const color = computedStyle.color;
|
|
362
|
+
const backgroundColor = computedStyle.backgroundColor;
|
|
363
|
+
// Skip transparent backgrounds (they inherit from parent)
|
|
364
|
+
if (backgroundColor === "rgba(0, 0, 0, 0)" ||
|
|
365
|
+
backgroundColor === "transparent") {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
elements.push({
|
|
369
|
+
text: (textNode.textContent?.trim() ?? "").substring(0, 50),
|
|
370
|
+
color,
|
|
371
|
+
backgroundColor,
|
|
372
|
+
fontSize: computedStyle.fontSize,
|
|
373
|
+
fontWeight: computedStyle.fontWeight,
|
|
374
|
+
selector: parentElement.tagName.toLowerCase() +
|
|
375
|
+
(parentElement.id ? `#${parentElement.id}` : "") +
|
|
376
|
+
(parentElement.className
|
|
377
|
+
? `.${parentElement.className.split(" ").join(".")}`
|
|
378
|
+
: ""),
|
|
379
|
+
});
|
|
380
|
+
count++;
|
|
381
|
+
}
|
|
382
|
+
return elements;
|
|
383
|
+
}, MAX_TEXT_ELEMENTS_TO_CHECK);
|
|
384
|
+
for (const elementData of textElementData) {
|
|
385
|
+
const foregroundRgb = parseRgbColor(elementData.color);
|
|
386
|
+
const backgroundRgb = parseRgbColor(elementData.backgroundColor);
|
|
387
|
+
if (!foregroundRgb || !backgroundRgb)
|
|
388
|
+
continue;
|
|
389
|
+
const ratio = contrastRatio(foregroundRgb, backgroundRgb);
|
|
390
|
+
// Determine if text is "large" per WCAG
|
|
391
|
+
const fontSizePx = parseFloat(elementData.fontSize);
|
|
392
|
+
const fontWeightNumeric = elementData.fontWeight === "bold"
|
|
393
|
+
? 700
|
|
394
|
+
: parseInt(elementData.fontWeight, 10) || 400;
|
|
395
|
+
const isLargeText = fontSizePx >= 24 || (fontSizePx >= 18.66 && fontWeightNumeric >= 700);
|
|
396
|
+
const minimumRatio = isLargeText ? 3 : 4.5;
|
|
397
|
+
if (ratio < minimumRatio) {
|
|
398
|
+
findings.push({
|
|
399
|
+
category: "contrast",
|
|
400
|
+
severity: ratio < 3 ? "error" : "warning",
|
|
401
|
+
message: `Low contrast ratio ${ratio.toFixed(2)}:1 (minimum ${minimumRatio}:1 for ${isLargeText ? "large" : "normal"} text). Text: "${elementData.text}".`,
|
|
402
|
+
element: elementData.selector,
|
|
403
|
+
recommendation: `Increase contrast between text color (${elementData.color}) and background (${elementData.backgroundColor}).`,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return findings;
|
|
408
|
+
}
|
|
409
|
+
async auditLinks(page) {
|
|
410
|
+
const findings = [];
|
|
411
|
+
const MAX_LINKS_TO_CHECK = 50;
|
|
412
|
+
const LINK_TIMEOUT_MS = 3000;
|
|
413
|
+
// Collect all link hrefs from the page
|
|
414
|
+
const linkData = await page.evaluate(() => {
|
|
415
|
+
const anchors = document.querySelectorAll("a[href]");
|
|
416
|
+
return Array.from(anchors).map((anchor) => ({
|
|
417
|
+
href: anchor.href,
|
|
418
|
+
text: anchor.textContent?.trim().substring(0, 50) || "(no text)",
|
|
419
|
+
}));
|
|
420
|
+
});
|
|
421
|
+
// Filter to checkable links and deduplicate
|
|
422
|
+
const checkedUrls = new Set();
|
|
423
|
+
const linksToCheck = [];
|
|
424
|
+
for (const link of linkData) {
|
|
425
|
+
if (linksToCheck.length >= MAX_LINKS_TO_CHECK)
|
|
426
|
+
break;
|
|
427
|
+
// Skip non-HTTP links
|
|
428
|
+
if (!link.href.startsWith("http://") &&
|
|
429
|
+
!link.href.startsWith("https://")) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
// Skip already-checked URLs
|
|
433
|
+
if (checkedUrls.has(link.href))
|
|
434
|
+
continue;
|
|
435
|
+
checkedUrls.add(link.href);
|
|
436
|
+
linksToCheck.push(link);
|
|
437
|
+
}
|
|
438
|
+
// Check all links concurrently
|
|
439
|
+
const checkResults = await Promise.allSettled(linksToCheck.map(async (link) => {
|
|
440
|
+
const controller = new AbortController();
|
|
441
|
+
const timeoutId = setTimeout(() => controller.abort(), LINK_TIMEOUT_MS);
|
|
442
|
+
try {
|
|
443
|
+
const response = await fetch(link.href, {
|
|
444
|
+
method: "HEAD",
|
|
445
|
+
signal: controller.signal,
|
|
446
|
+
redirect: "follow",
|
|
447
|
+
});
|
|
448
|
+
return { link, status: response.status, ok: response.ok };
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
return { link, status: 0, ok: false, timeout: true };
|
|
452
|
+
}
|
|
453
|
+
finally {
|
|
454
|
+
clearTimeout(timeoutId);
|
|
455
|
+
}
|
|
456
|
+
}));
|
|
457
|
+
for (const result of checkResults) {
|
|
458
|
+
if (result.status !== "fulfilled")
|
|
459
|
+
continue;
|
|
460
|
+
const { link, status, ok } = result.value;
|
|
461
|
+
if (!ok && status >= 400) {
|
|
462
|
+
findings.push({
|
|
463
|
+
category: "links",
|
|
464
|
+
severity: status >= 500 ? "error" : "warning",
|
|
465
|
+
message: `Broken link (HTTP ${status}): ${link.href}`,
|
|
466
|
+
element: `"${link.text}"`,
|
|
467
|
+
recommendation: "Fix or remove the broken link.",
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return findings;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=auditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditor.js","sourceRoot":"","sources":["../../src/dev/auditor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PageManager } from "../browser/page-manager.js";
|
|
2
|
+
import type { ReloadEvent } from "../types/page-representation.js";
|
|
3
|
+
import { type StaticServerInfo } from "./static-server.js";
|
|
4
|
+
export interface DevServeOptions {
|
|
5
|
+
directoryPath: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
watch: boolean;
|
|
8
|
+
pageManager: PageManager;
|
|
9
|
+
/** Use polling for file watching. Slower but avoids inotify limits. */
|
|
10
|
+
usePolling?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class DevModeState {
|
|
13
|
+
private staticServer;
|
|
14
|
+
private fileWatcher;
|
|
15
|
+
private pendingReloadEvent;
|
|
16
|
+
private reloadInProgress;
|
|
17
|
+
startServing(options: DevServeOptions): Promise<StaticServerInfo>;
|
|
18
|
+
stopAll(): Promise<void>;
|
|
19
|
+
consumePendingReloadEvent(): ReloadEvent | null;
|
|
20
|
+
isServing(): boolean;
|
|
21
|
+
getServerInfo(): StaticServerInfo | null;
|
|
22
|
+
private handleFilesChanged;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=dev-mode-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-mode-state.d.ts","sourceRoot":"","sources":["../../src/dev/dev-mode-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAIzE,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,WAAW,CAAC;IACzB,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,gBAAgB,CAA8B;IAEhD,YAAY,CAChB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,gBAAgB,CAAC;IAsBtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,yBAAyB,IAAI,WAAW,GAAG,IAAI;IAM/C,SAAS,IAAI,OAAO;IAIpB,aAAa,IAAI,gBAAgB,GAAG,IAAI;IAIxC,OAAO,CAAC,kBAAkB;CAiD3B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { StaticServer } from "./static-server.js";
|
|
2
|
+
import { FileWatcher } from "./file-watcher.js";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
export class DevModeState {
|
|
5
|
+
staticServer = new StaticServer();
|
|
6
|
+
fileWatcher = new FileWatcher();
|
|
7
|
+
pendingReloadEvent = null;
|
|
8
|
+
reloadInProgress = null;
|
|
9
|
+
async startServing(options) {
|
|
10
|
+
// Stop any existing serving session first
|
|
11
|
+
await this.stopAll();
|
|
12
|
+
const serverInfo = await this.staticServer.start({
|
|
13
|
+
directoryPath: options.directoryPath,
|
|
14
|
+
port: options.port,
|
|
15
|
+
});
|
|
16
|
+
if (options.watch) {
|
|
17
|
+
await this.fileWatcher.start({
|
|
18
|
+
directoryPath: options.directoryPath,
|
|
19
|
+
onFilesChanged: (changedFiles) => {
|
|
20
|
+
this.handleFilesChanged(changedFiles, options.pageManager);
|
|
21
|
+
},
|
|
22
|
+
usePolling: options.usePolling,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return serverInfo;
|
|
26
|
+
}
|
|
27
|
+
async stopAll() {
|
|
28
|
+
if (this.fileWatcher.isWatching()) {
|
|
29
|
+
await this.fileWatcher.stop();
|
|
30
|
+
}
|
|
31
|
+
if (this.staticServer.isRunning()) {
|
|
32
|
+
await this.staticServer.stop();
|
|
33
|
+
}
|
|
34
|
+
this.pendingReloadEvent = null;
|
|
35
|
+
this.reloadInProgress = null;
|
|
36
|
+
}
|
|
37
|
+
consumePendingReloadEvent() {
|
|
38
|
+
const event = this.pendingReloadEvent;
|
|
39
|
+
this.pendingReloadEvent = null;
|
|
40
|
+
return event;
|
|
41
|
+
}
|
|
42
|
+
isServing() {
|
|
43
|
+
return this.staticServer.isRunning();
|
|
44
|
+
}
|
|
45
|
+
getServerInfo() {
|
|
46
|
+
return this.staticServer.getInfo();
|
|
47
|
+
}
|
|
48
|
+
handleFilesChanged(changedFiles, pageManager) {
|
|
49
|
+
// Merge with any existing pending event (accumulate files)
|
|
50
|
+
if (this.pendingReloadEvent) {
|
|
51
|
+
const existingFiles = new Set(this.pendingReloadEvent.files_changed);
|
|
52
|
+
for (const file of changedFiles) {
|
|
53
|
+
existingFiles.add(file);
|
|
54
|
+
}
|
|
55
|
+
this.pendingReloadEvent = {
|
|
56
|
+
trigger: "file_change",
|
|
57
|
+
files_changed: [...existingFiles],
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.pendingReloadEvent = {
|
|
63
|
+
trigger: "file_change",
|
|
64
|
+
files_changed: changedFiles,
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// Reload the active page if not already reloading
|
|
69
|
+
if (this.reloadInProgress) {
|
|
70
|
+
logger.debug("Reload already in progress, skipping duplicate reload");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!pageManager.hasPages()) {
|
|
74
|
+
logger.warn("No active page to reload after file change");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const page = pageManager.getActivePage();
|
|
78
|
+
this.reloadInProgress = page
|
|
79
|
+
.reload({ waitUntil: "load" })
|
|
80
|
+
.then(() => {
|
|
81
|
+
logger.info("Page reloaded after file change", {
|
|
82
|
+
files: changedFiles,
|
|
83
|
+
});
|
|
84
|
+
})
|
|
85
|
+
.catch((error) => {
|
|
86
|
+
logger.warn("Failed to reload page after file change", error);
|
|
87
|
+
})
|
|
88
|
+
.finally(() => {
|
|
89
|
+
this.reloadInProgress = null;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=dev-mode-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-mode-state.js","sourceRoot":"","sources":["../../src/dev/dev-mode-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface FileWatcherOptions {
|
|
2
|
+
directoryPath: string;
|
|
3
|
+
onFilesChanged: (changedFiles: string[]) => void;
|
|
4
|
+
debounceMs?: number;
|
|
5
|
+
/** Use polling instead of native file system events. Slower but avoids inotify limits. */
|
|
6
|
+
usePolling?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class FileWatcher {
|
|
9
|
+
private watcher;
|
|
10
|
+
private pendingChanges;
|
|
11
|
+
private debounceTimer;
|
|
12
|
+
private directoryPath;
|
|
13
|
+
private onFilesChanged;
|
|
14
|
+
private debounceMs;
|
|
15
|
+
start(options: FileWatcherOptions): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
17
|
+
isWatching(): boolean;
|
|
18
|
+
private flushPendingChanges;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=file-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/dev/file-watcher.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAmD;IACzE,OAAO,CAAC,UAAU,CAAuB;IAEnC,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+CjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B,UAAU,IAAI,OAAO;IAIrB,OAAO,CAAC,mBAAmB;CAY5B"}
|