@diegovelasquezweb/a11y-engine 0.8.4 → 0.8.5
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/README.md +2 -1
- package/package.json +1 -1
- package/src/ai/claude.mjs +1 -1
- package/src/ai/enrich.mjs +1 -1
- package/src/index.mjs +0 -31
- package/src/pipeline/dom-scanner.mjs +2 -7
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# a11y Engine
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@diegovelasquezweb/a11y-engine)
|
|
4
4
|
|
|
@@ -15,6 +15,7 @@ Accessibility automation engine for web applications. It orchestrates multi engi
|
|
|
15
15
|
| **AI enrichment** | Optional Claude-powered analysis that adds contextual fix suggestions based on detected stack, repo structure, and finding patterns |
|
|
16
16
|
| **Report generation** | Produces HTML dashboard, PDF compliance report, manual testing checklist, and Markdown remediation guide |
|
|
17
17
|
| **Source code scanning** | Static regex analysis of project source for accessibility patterns that runtime engines cannot detect. Works with local paths or remote GitHub repos |
|
|
18
|
+
| **Knowledge API** | Exposes WCAG conformance levels, severity definitions, persona profiles, glossary, scanner help, and documentation so frontends and agents can render guidance from engine-owned data |
|
|
18
19
|
|
|
19
20
|
## Installation
|
|
20
21
|
|
package/package.json
CHANGED
package/src/ai/claude.mjs
CHANGED
|
@@ -236,7 +236,7 @@ async function fetchSourceFilesForFindings(findings, repoUrl, githubToken) {
|
|
|
236
236
|
try {
|
|
237
237
|
const content = await fetchRepoFile(repoUrl, filePath, githubToken);
|
|
238
238
|
if (content) sourceFiles[filePath] = content;
|
|
239
|
-
} catch {
|
|
239
|
+
} catch { }
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
package/src/ai/enrich.mjs
CHANGED
|
@@ -74,6 +74,6 @@ async function main() {
|
|
|
74
74
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
75
75
|
main().catch((err) => {
|
|
76
76
|
log.warn(`AI enrichment failed (non-fatal): ${err.message}`);
|
|
77
|
-
process.exit(0);
|
|
77
|
+
process.exit(0);
|
|
78
78
|
});
|
|
79
79
|
}
|
package/src/index.mjs
CHANGED
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
import { ASSET_PATHS, loadAssetJson } from "./core/asset-loader.mjs";
|
|
8
8
|
export { DEFAULT_AI_SYSTEM_PROMPT } from "./ai/claude.mjs";
|
|
9
9
|
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
10
|
// Lazy-loaded asset cache
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
11
|
|
|
14
12
|
let _intelligence = null;
|
|
15
13
|
let _pa11yConfig = null;
|
|
@@ -59,9 +57,7 @@ function resolveKnowledgeLocale(locale = "en") {
|
|
|
59
57
|
return "en";
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
60
|
// Pa11y rule canonicalization (internal)
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
61
|
|
|
66
62
|
function normalizePa11yToken(value) {
|
|
67
63
|
return value
|
|
@@ -98,9 +94,7 @@ function mapPa11yRuleToCanonical(ruleId, sourceRuleId = null, checkData = null)
|
|
|
98
94
|
return ruleId;
|
|
99
95
|
}
|
|
100
96
|
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
97
|
// Raw finding normalization (internal)
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
98
|
|
|
105
99
|
const SEVERITY_ORDER = { Critical: 1, Serious: 2, Moderate: 3, Minor: 4 };
|
|
106
100
|
|
|
@@ -170,9 +164,7 @@ function normalizeSingleFinding(item, index, screenshotUrlBuilder) {
|
|
|
170
164
|
};
|
|
171
165
|
}
|
|
172
166
|
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
174
167
|
// Finding enrichment
|
|
175
|
-
// ---------------------------------------------------------------------------
|
|
176
168
|
|
|
177
169
|
/**
|
|
178
170
|
* Normalizes and enriches raw findings with intelligence data.
|
|
@@ -189,19 +181,16 @@ export function getFindings(input, options = {}) {
|
|
|
189
181
|
const { screenshotUrlBuilder = null } = options;
|
|
190
182
|
const rules = getIntelligence().rules || {};
|
|
191
183
|
|
|
192
|
-
// If AI enrichment ran, return those findings directly (already normalized + enriched)
|
|
193
184
|
if (input?.ai_enriched_findings?.length > 0 && !screenshotUrlBuilder) {
|
|
194
185
|
return input.ai_enriched_findings;
|
|
195
186
|
}
|
|
196
187
|
|
|
197
188
|
const rawFindings = input?.findings || [];
|
|
198
189
|
|
|
199
|
-
// Normalize raw findings
|
|
200
190
|
const normalized = rawFindings.map((item, index) =>
|
|
201
191
|
normalizeSingleFinding(item, index, screenshotUrlBuilder)
|
|
202
192
|
);
|
|
203
193
|
|
|
204
|
-
// Enrich with intelligence and output camelCase-only findings
|
|
205
194
|
const enriched = normalized.map((finding) => {
|
|
206
195
|
const canonical = mapPa11yRuleToCanonical(
|
|
207
196
|
finding.rule_id,
|
|
@@ -209,7 +198,6 @@ export function getFindings(input, options = {}) {
|
|
|
209
198
|
finding.check_data,
|
|
210
199
|
);
|
|
211
200
|
|
|
212
|
-
// Build camelCase-only enriched finding
|
|
213
201
|
const enrichedFinding = {
|
|
214
202
|
id: finding.id,
|
|
215
203
|
ruleId: canonical,
|
|
@@ -281,7 +269,6 @@ export function getFindings(input, options = {}) {
|
|
|
281
269
|
return enrichedFinding;
|
|
282
270
|
});
|
|
283
271
|
|
|
284
|
-
// Sort by severity then by ID
|
|
285
272
|
enriched.sort((a, b) => {
|
|
286
273
|
const sa = SEVERITY_ORDER[a.severity] ?? 99;
|
|
287
274
|
const sb = SEVERITY_ORDER[b.severity] ?? 99;
|
|
@@ -292,9 +279,7 @@ export function getFindings(input, options = {}) {
|
|
|
292
279
|
return enriched;
|
|
293
280
|
}
|
|
294
281
|
|
|
295
|
-
// ---------------------------------------------------------------------------
|
|
296
282
|
// Score computation (internal)
|
|
297
|
-
// ---------------------------------------------------------------------------
|
|
298
283
|
|
|
299
284
|
function getComplianceScore(totals) {
|
|
300
285
|
const config = getComplianceConfig();
|
|
@@ -325,9 +310,7 @@ function getComplianceScore(totals) {
|
|
|
325
310
|
return { score, label, wcagStatus };
|
|
326
311
|
}
|
|
327
312
|
|
|
328
|
-
// ---------------------------------------------------------------------------
|
|
329
313
|
// Persona grouping (internal)
|
|
330
|
-
// ---------------------------------------------------------------------------
|
|
331
314
|
|
|
332
315
|
function getPersonaGroups(findings) {
|
|
333
316
|
const ref = getWcagReference();
|
|
@@ -384,9 +367,7 @@ function getPersonaGroups(findings) {
|
|
|
384
367
|
return groups;
|
|
385
368
|
}
|
|
386
369
|
|
|
387
|
-
// ---------------------------------------------------------------------------
|
|
388
370
|
// Audit summary
|
|
389
|
-
// ---------------------------------------------------------------------------
|
|
390
371
|
|
|
391
372
|
/**
|
|
392
373
|
* Computes a complete audit summary from enriched findings: severity totals,
|
|
@@ -444,9 +425,7 @@ export function getOverview(findings, payload = null) {
|
|
|
444
425
|
};
|
|
445
426
|
}
|
|
446
427
|
|
|
447
|
-
// ---------------------------------------------------------------------------
|
|
448
428
|
// Knowledge APIs
|
|
449
|
-
// ---------------------------------------------------------------------------
|
|
450
429
|
|
|
451
430
|
/**
|
|
452
431
|
* Returns scanner-facing help metadata including engine descriptions,
|
|
@@ -619,9 +598,7 @@ export function getKnowledge(options = {}) {
|
|
|
619
598
|
};
|
|
620
599
|
}
|
|
621
600
|
|
|
622
|
-
// ---------------------------------------------------------------------------
|
|
623
601
|
// Full audit pipeline
|
|
624
|
-
// ---------------------------------------------------------------------------
|
|
625
602
|
|
|
626
603
|
/**
|
|
627
604
|
* Runs a complete accessibility audit: crawl + scan (axe + CDP + pa11y) + analyze.
|
|
@@ -826,9 +803,7 @@ export async function runAudit(options) {
|
|
|
826
803
|
return findingsPayload;
|
|
827
804
|
}
|
|
828
805
|
|
|
829
|
-
// ---------------------------------------------------------------------------
|
|
830
806
|
// Report generation
|
|
831
|
-
// ---------------------------------------------------------------------------
|
|
832
807
|
|
|
833
808
|
import {
|
|
834
809
|
normalizeFindings as normalizeForReports,
|
|
@@ -1011,9 +986,7 @@ export async function getChecklist(options = {}) {
|
|
|
1011
986
|
};
|
|
1012
987
|
}
|
|
1013
988
|
|
|
1014
|
-
// ---------------------------------------------------------------------------
|
|
1015
989
|
// HTML Report
|
|
1016
|
-
// ---------------------------------------------------------------------------
|
|
1017
990
|
|
|
1018
991
|
/**
|
|
1019
992
|
* Generates an interactive HTML audit dashboard from raw scan findings.
|
|
@@ -1143,9 +1116,7 @@ export async function getHTMLReport(payload, options = {}) {
|
|
|
1143
1116
|
};
|
|
1144
1117
|
}
|
|
1145
1118
|
|
|
1146
|
-
// ---------------------------------------------------------------------------
|
|
1147
1119
|
// Remediation Guide (Markdown)
|
|
1148
|
-
// ---------------------------------------------------------------------------
|
|
1149
1120
|
|
|
1150
1121
|
/**
|
|
1151
1122
|
* Generates a Markdown remediation guide from raw scan findings.
|
|
@@ -1171,9 +1142,7 @@ export async function getRemediationGuide(payload, options = {}) {
|
|
|
1171
1142
|
};
|
|
1172
1143
|
}
|
|
1173
1144
|
|
|
1174
|
-
// ---------------------------------------------------------------------------
|
|
1175
1145
|
// Source Pattern Scanner
|
|
1176
|
-
// ---------------------------------------------------------------------------
|
|
1177
1146
|
|
|
1178
1147
|
/**
|
|
1179
1148
|
* Scans a project's source code for accessibility patterns not detectable by axe-core.
|
|
@@ -935,17 +935,12 @@ function mergeViolations(axeViolations, cdpViolations, pa11yViolations) {
|
|
|
935
935
|
}
|
|
936
936
|
}
|
|
937
937
|
|
|
938
|
-
// Step 3: pa11y findings —
|
|
938
|
+
// Step 3: pa11y findings — only skip if same rule + same target already exists
|
|
939
939
|
for (const v of pa11yViolations) {
|
|
940
940
|
const target = v.nodes?.[0]?.target?.[0] || "";
|
|
941
941
|
const key = `${v.id}::${target}`;
|
|
942
942
|
|
|
943
|
-
|
|
944
|
-
const isAxeEquivDuplicate = v.id && seenRuleTargets.has(v.id) && target && seenRuleTargets.get(v.id).has(target);
|
|
945
|
-
// Also check if any existing finding covers this exact target (broader dedup)
|
|
946
|
-
const selectorCovered = target && [...seen].some((k) => k.endsWith(`::${target}`));
|
|
947
|
-
|
|
948
|
-
if (!seen.has(key) && !isAxeEquivDuplicate && (!selectorCovered || !target)) {
|
|
943
|
+
if (!seen.has(key)) {
|
|
949
944
|
seen.add(key);
|
|
950
945
|
if (!seenRuleTargets.has(v.id)) seenRuleTargets.set(v.id, new Set());
|
|
951
946
|
seenRuleTargets.get(v.id).add(target);
|