@diegovelasquezweb/a11y-engine 0.8.3 → 0.8.4
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 +8 -24
- package/docs/api-reference.md +371 -101
- package/docs/engine-manifest.md +8 -0
- package/package.json +1 -1
- package/src/ai/claude.mjs +16 -10
- package/src/index.d.mts +18 -9
- package/src/index.mjs +9 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @diegovelasquezweb/a11y-engine
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@diegovelasquezweb/a11y-engine)
|
|
4
|
+
|
|
3
5
|
Accessibility automation engine for web applications. It orchestrates multi engine scanning, stack aware enrichment, and report generation for apps and services through a stable API.
|
|
4
6
|
|
|
5
7
|
## What it does
|
|
@@ -12,7 +14,7 @@ Accessibility automation engine for web applications. It orchestrates multi engi
|
|
|
12
14
|
| **Fix intelligence** | Enriches each finding with WCAG mapping, fix code snippets, framework and CMS specific notes, UI library ownership hints, effort estimates, and persona impact |
|
|
13
15
|
| **AI enrichment** | Optional Claude-powered analysis that adds contextual fix suggestions based on detected stack, repo structure, and finding patterns |
|
|
14
16
|
| **Report generation** | Produces HTML dashboard, PDF compliance report, manual testing checklist, and Markdown remediation guide |
|
|
15
|
-
| **Source code scanning** | Static regex analysis of project source for accessibility patterns that runtime engines cannot detect
|
|
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 |
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
18
20
|
|
|
@@ -58,13 +60,13 @@ const payload = await runAudit({
|
|
|
58
60
|
maxRoutes: 5,
|
|
59
61
|
axeTags: ["wcag2a", "wcag2aa", "best-practice"],
|
|
60
62
|
engines: { axe: true, cdp: true, pa11y: true },
|
|
61
|
-
repoUrl: "https://github.com/owner/repo", // optional
|
|
62
|
-
githubToken: process.env.GH_TOKEN, // optional
|
|
63
|
+
repoUrl: "https://github.com/owner/repo", // optional, enables source pattern scan and stack detection
|
|
64
|
+
githubToken: process.env.GH_TOKEN, // optional, required for private repos
|
|
63
65
|
ai: {
|
|
64
66
|
enabled: true,
|
|
65
67
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
66
68
|
githubToken: process.env.GH_TOKEN,
|
|
67
|
-
systemPrompt: "Custom prompt...", // optional
|
|
69
|
+
systemPrompt: "Custom prompt...", // optional, overrides default Claude system prompt
|
|
68
70
|
},
|
|
69
71
|
onProgress: (step, status, extra) => console.log(`${step}: ${status}`, extra),
|
|
70
72
|
});
|
|
@@ -135,25 +137,7 @@ See [API Reference](docs/api-reference.md) for exact options and return types.
|
|
|
135
137
|
|
|
136
138
|
## CLI
|
|
137
139
|
|
|
138
|
-
The package exposes an `a11y-audit` binary for terminal execution.
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
# Basic scan
|
|
142
|
-
pnpm exec a11y-audit --base-url https://example.com
|
|
143
|
-
|
|
144
|
-
# With source code pattern scanning via GitHub API (no clone)
|
|
145
|
-
pnpm exec a11y-audit --base-url https://example.com \
|
|
146
|
-
--repo-url https://github.com/owner/repo \
|
|
147
|
-
--github-token ghp_...
|
|
148
|
-
|
|
149
|
-
# With AI enrichment (set ANTHROPIC_API_KEY env var)
|
|
150
|
-
ANTHROPIC_API_KEY=sk-ant-... pnpm exec a11y-audit --base-url https://example.com
|
|
151
|
-
|
|
152
|
-
# With custom AI system prompt
|
|
153
|
-
AI_SYSTEM_PROMPT="You are..." ANTHROPIC_API_KEY=sk-ant-... pnpm exec a11y-audit --base-url https://example.com
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
See the [CLI Handbook](docs/cli-handbook.md) for all flags and examples.
|
|
140
|
+
The package exposes an `a11y-audit` binary for terminal execution. See the [CLI Handbook](docs/cli-handbook.md) for all flags, env vars, and examples.
|
|
157
141
|
|
|
158
142
|
## AI enrichment
|
|
159
143
|
|
|
@@ -163,7 +147,7 @@ When `ANTHROPIC_API_KEY` is set, the engine runs a post-scan enrichment step tha
|
|
|
163
147
|
- A production-quality code snippet in the correct framework syntax
|
|
164
148
|
- Context-aware suggestions when repo source files are available
|
|
165
149
|
|
|
166
|
-
AI output is stored in separate fields (`ai_fix_description`, `ai_fix_code`)
|
|
150
|
+
AI output is stored in separate fields (`ai_fix_description`, `ai_fix_code`). The original engine fixes are always preserved. Findings improved by AI are flagged with `aiEnhanced: true`.
|
|
167
151
|
|
|
168
152
|
The system prompt is fully customizable via `options.ai.systemPrompt` (programmatic API) or the `AI_SYSTEM_PROMPT` env var (CLI).
|
|
169
153
|
|
package/docs/api-reference.md
CHANGED
|
@@ -4,174 +4,444 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Import](#import)
|
|
11
|
+
- [End-to-end example](#end-to-end-example)
|
|
12
|
+
- [Core API](#core-api)
|
|
13
|
+
- [runAudit](#runauditoptions)
|
|
14
|
+
- [getFindings](#getfindingsinput-options)
|
|
15
|
+
- [getOverview](#getoverviewfindings-payload)
|
|
16
|
+
- [Output API](#output-api)
|
|
17
|
+
- [getPDFReport](#getpdfreportpayload-options)
|
|
18
|
+
- [getHTMLReport](#gethtmlreportpayload-options)
|
|
19
|
+
- [getChecklist](#getchecklistoptions)
|
|
20
|
+
- [getRemediationGuide](#getremediationguidepayload-options)
|
|
21
|
+
- [getSourcePatterns](#getsourcepatternsprojdir-options)
|
|
22
|
+
- [Knowledge API](#knowledge-api)
|
|
23
|
+
- [getKnowledge](#getknowledgeoptions)
|
|
24
|
+
- [getScannerHelp](#getscannerhelpoptions)
|
|
25
|
+
- [getPersonaReference](#getpersonareferenceoptions)
|
|
26
|
+
- [getUiHelp](#getuihelpoptions)
|
|
27
|
+
- [getConformanceLevels](#getconformancelevelsoptions)
|
|
28
|
+
- [getWcagPrinciples](#getwcagprinciplesoptions)
|
|
29
|
+
- [getSeverityLevels](#getseveritylevelsoptions)
|
|
8
30
|
|
|
9
|
-
|
|
31
|
+
---
|
|
10
32
|
|
|
11
|
-
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @diegovelasquezweb/a11y-engine
|
|
37
|
+
npx playwright install chromium
|
|
38
|
+
npx puppeteer browsers install chrome
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Import
|
|
42
|
+
|
|
43
|
+
All functions are named exports from the package root:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import {
|
|
47
|
+
runAudit,
|
|
48
|
+
getFindings,
|
|
49
|
+
getOverview,
|
|
50
|
+
getPDFReport,
|
|
51
|
+
getHTMLReport,
|
|
52
|
+
getChecklist,
|
|
53
|
+
getRemediationGuide,
|
|
54
|
+
getSourcePatterns,
|
|
55
|
+
getKnowledge,
|
|
56
|
+
getScannerHelp,
|
|
57
|
+
getPersonaReference,
|
|
58
|
+
getUiHelp,
|
|
59
|
+
getConformanceLevels,
|
|
60
|
+
getWcagPrinciples,
|
|
61
|
+
getSeverityLevels,
|
|
62
|
+
} from "@diegovelasquezweb/a11y-engine";
|
|
63
|
+
```
|
|
12
64
|
|
|
13
|
-
|
|
65
|
+
---
|
|
14
66
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
67
|
+
## End-to-end example
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { runAudit, getFindings, getOverview } from "@diegovelasquezweb/a11y-engine";
|
|
71
|
+
|
|
72
|
+
// 1. Run the scan
|
|
73
|
+
const payload = await runAudit({
|
|
74
|
+
baseUrl: "https://example.com",
|
|
75
|
+
maxRoutes: 5,
|
|
76
|
+
engines: { axe: true, cdp: true, pa11y: true },
|
|
77
|
+
onProgress: (step, status) => console.log(`[${step}] ${status}`),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 2. Get enriched findings
|
|
81
|
+
const findings = getFindings(payload);
|
|
82
|
+
|
|
83
|
+
// 3. Get compliance summary
|
|
84
|
+
const { score, scoreLabel, wcagStatus, totals, quickWins } = getOverview(findings, payload);
|
|
85
|
+
|
|
86
|
+
console.log(`Score: ${score}/100 (${scoreLabel})`);
|
|
87
|
+
console.log(`WCAG Status: ${wcagStatus}`);
|
|
88
|
+
console.log(`Critical: ${totals.Critical}, Serious: ${totals.Serious}`);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Core API
|
|
94
|
+
|
|
95
|
+
### `runAudit(options)`
|
|
96
|
+
|
|
97
|
+
Runs the full audit pipeline: route discovery → axe/CDP/pa11y scan → merge/dedup → analyzer enrichment → optional source pattern scanning → optional AI enrichment.
|
|
98
|
+
|
|
99
|
+
Returns a `ScanPayload` object consumed by all other functions.
|
|
100
|
+
|
|
101
|
+
**Options:**
|
|
102
|
+
|
|
103
|
+
| Option | Type | Default | Range / Values | Description |
|
|
104
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
105
|
+
| `baseUrl` | `string` | — | Required | Starting URL including protocol (`https://` or `http://`) |
|
|
106
|
+
| `maxRoutes` | `number` | `10` | 1 – 50 | Maximum unique pages to discover and scan |
|
|
107
|
+
| `crawlDepth` | `number` | `2` | 1 – 3 | BFS link-follow depth from `baseUrl`. Has no effect when `routes` is set |
|
|
108
|
+
| `routes` | `string` | — | CSV paths | Explicit paths to scan (e.g. `"/,/about,/contact"`). Overrides auto-discovery entirely |
|
|
109
|
+
| `waitUntil` | `string` | `"domcontentloaded"` | `"domcontentloaded"` \| `"load"` \| `"networkidle"` | Page load strategy. Use `"networkidle"` for SPAs that render after `DOMContentLoaded` |
|
|
110
|
+
| `waitMs` | `number` | `2000` | 0 – 10000 | Fixed delay (ms) after page load before engines run |
|
|
111
|
+
| `timeoutMs` | `number` | `30000` | 5000 – 120000 | Network timeout per page (ms) |
|
|
112
|
+
| `headless` | `boolean` | `true` | — | Set `false` to open a visible browser for debugging |
|
|
113
|
+
| `viewport` | `object` | `{ width: 1280, height: 800 }` | width: 320–2560, height: 320–2560 | Browser viewport dimensions in pixels |
|
|
114
|
+
| `colorScheme` | `string` | `"light"` | `"light"` \| `"dark"` | Emulates `prefers-color-scheme` media query |
|
|
115
|
+
| `engines` | `object` | all `true` | `{ axe?, cdp?, pa11y? }` | Which engines to run. At least one must be enabled |
|
|
116
|
+
| `axeTags` | `string[]` | WCAG 2.x A+AA | See below | axe-core rule tag filter. Also determines pa11y standard |
|
|
117
|
+
| `onlyRule` | `string` | — | axe rule ID | Run a single axe rule only (e.g. `"color-contrast"`) |
|
|
118
|
+
| `ignoreFindings` | `string[]` | — | axe rule IDs | Suppress specific rules from output entirely |
|
|
119
|
+
| `excludeSelectors` | `string[]` | — | CSS selectors | Skip elements matching these selectors during axe scan |
|
|
120
|
+
| `framework` | `string` | auto-detected | See below | Override framework detection for fix notes and source boundaries |
|
|
121
|
+
| `projectDir` | `string` | — | local path | Local project source directory. Enables source pattern scanning and package.json stack detection |
|
|
122
|
+
| `repoUrl` | `string` | — | GitHub URL | Remote repo URL. Enables source pattern scanning via GitHub API — no clone required |
|
|
123
|
+
| `githubToken` | `string` | — | GitHub PAT | Increases GitHub API rate limit from 60 to 5,000 req/hr. Required for private repos |
|
|
124
|
+
| `skipPatterns` | `boolean` | `false` | — | Disable source pattern scanning even when `projectDir` or `repoUrl` is set |
|
|
125
|
+
| `screenshotsDir` | `string` | `.audit/screenshots` | dir path | Directory where element screenshots are saved |
|
|
126
|
+
| `ai.enabled` | `boolean` | `false` | — | Enable Claude AI enrichment for Critical and Serious findings |
|
|
127
|
+
| `ai.apiKey` | `string` | — | Anthropic API key | Required when `ai.enabled` is `true` |
|
|
128
|
+
| `ai.githubToken` | `string` | — | GitHub PAT | Used to fetch source files from the repo for AI context |
|
|
129
|
+
| `ai.model` | `string` | `"claude-haiku-4-5-20251001"` | Anthropic model ID | Claude model to use |
|
|
130
|
+
| `ai.systemPrompt` | `string` | Built-in prompt | — | Overrides the default Claude system prompt for the entire scan |
|
|
131
|
+
| `onProgress` | `function` | — | — | Callback fired at each pipeline step |
|
|
132
|
+
|
|
133
|
+
**`axeTags` common values:**
|
|
134
|
+
|
|
135
|
+
| Tag | Covers |
|
|
44
136
|
| :--- | :--- |
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
|
|
137
|
+
| `wcag2a` | WCAG 2.0 Level A |
|
|
138
|
+
| `wcag2aa` | WCAG 2.0 Level AA |
|
|
139
|
+
| `wcag21a` | WCAG 2.1 Level A additions |
|
|
140
|
+
| `wcag21aa` | WCAG 2.1 Level AA additions |
|
|
141
|
+
| `wcag22a` | WCAG 2.2 Level A additions |
|
|
142
|
+
| `wcag22aa` | WCAG 2.2 Level AA additions |
|
|
143
|
+
| `wcag2aaa` | WCAG 2.0 Level AAA |
|
|
144
|
+
| `best-practice` | Non-WCAG best practices |
|
|
145
|
+
|
|
146
|
+
**Supported `framework` values:** `nextjs`, `gatsby`, `react`, `nuxt`, `vue`, `angular`, `astro`, `svelte`, `remix`, `shopify`, `wordpress`, `drupal`
|
|
147
|
+
|
|
148
|
+
**`onProgress` callback:**
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
onProgress: (step, status, extra) => {
|
|
152
|
+
// step: "page" | "axe" | "cdp" | "pa11y" | "merge" | "intelligence" | "repo" | "patterns" | "ai"
|
|
153
|
+
// status: "running" | "done" | "error" | "skipped"
|
|
154
|
+
// extra: { found?: number, merged?: number, ... } — step-specific data
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const payload = await runAudit({
|
|
160
|
+
baseUrl: "https://example.com",
|
|
161
|
+
maxRoutes: 5,
|
|
162
|
+
engines: { axe: true, cdp: true, pa11y: true },
|
|
163
|
+
repoUrl: "https://github.com/owner/repo",
|
|
164
|
+
githubToken: process.env.GH_TOKEN,
|
|
165
|
+
ai: { enabled: true, apiKey: process.env.ANTHROPIC_API_KEY },
|
|
166
|
+
onProgress: (step, status) => console.log(`[${step}] ${status}`),
|
|
167
|
+
});
|
|
168
|
+
```
|
|
54
169
|
|
|
55
170
|
Returns: `Promise<ScanPayload>`
|
|
56
171
|
|
|
57
|
-
> **`ai_enriched_findings` fast path**: When AI enrichment runs,
|
|
172
|
+
> **`ai_enriched_findings` fast path**: When AI enrichment runs, `getFindings()` uses `payload.ai_enriched_findings` directly instead of re-normalizing the raw findings array.
|
|
58
173
|
|
|
59
|
-
|
|
174
|
+
---
|
|
60
175
|
|
|
61
|
-
|
|
176
|
+
### `getFindings(input, options?)`
|
|
62
177
|
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
178
|
+
Normalizes raw scan results into enriched, UI-ready findings sorted by severity.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { getFindings } from "@diegovelasquezweb/a11y-engine";
|
|
182
|
+
|
|
183
|
+
const findings = getFindings(payload, {
|
|
184
|
+
// Optional: rewrite internal screenshot paths to app URLs
|
|
185
|
+
screenshotUrlBuilder: (rawPath) =>
|
|
186
|
+
`/api/scan/${scanId}/screenshot?path=${encodeURIComponent(rawPath)}`,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// findings[0] example:
|
|
190
|
+
// {
|
|
191
|
+
// id: "A11Y-001",
|
|
192
|
+
// ruleId: "color-contrast",
|
|
193
|
+
// title: "Elements must meet minimum color contrast ratio thresholds",
|
|
194
|
+
// severity: "Serious",
|
|
195
|
+
// wcag: "1.4.3",
|
|
196
|
+
// selector: ".hero-text",
|
|
197
|
+
// actual: "Element has insufficient color contrast of 2.5:1 ...",
|
|
198
|
+
// expected: "Text contrast ratio must be at least 4.5:1 ...",
|
|
199
|
+
// fixDescription: "Increase the foreground color contrast ...",
|
|
200
|
+
// fixCode: "/* Change #aaa to #767676 */",
|
|
201
|
+
// effort: "low",
|
|
202
|
+
// aiEnhanced: true, // present when AI ran
|
|
203
|
+
// aiFixDescription: "...", // Claude-generated (more specific)
|
|
204
|
+
// aiFixCode: "...", // Claude-generated code snippet
|
|
205
|
+
// }
|
|
206
|
+
```
|
|
66
207
|
|
|
67
208
|
Returns: `EnrichedFinding[]`
|
|
68
209
|
|
|
69
|
-
|
|
210
|
+
---
|
|
70
211
|
|
|
71
|
-
|
|
212
|
+
### `getOverview(findings, payload?)`
|
|
72
213
|
|
|
73
|
-
|
|
74
|
-
|
|
214
|
+
Computes the compliance score, WCAG status, severity totals, persona groups, and quick wins from enriched findings.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { getFindings, getOverview } from "@diegovelasquezweb/a11y-engine";
|
|
218
|
+
|
|
219
|
+
const findings = getFindings(payload);
|
|
220
|
+
const overview = getOverview(findings, payload);
|
|
221
|
+
|
|
222
|
+
// overview example:
|
|
223
|
+
// {
|
|
224
|
+
// score: 72, // 0–100. Formula: 100 - (Critical×15) - (Serious×5) - (Moderate×2) - (Minor×0.5)
|
|
225
|
+
// label: "Fair", // "Excellent" (90–100) | "Good" (75–89) | "Fair" (55–74) | "Poor" (35–54) | "Critical" (0–34)
|
|
226
|
+
// wcagStatus: "Fail", // "Pass" | "Conditional Pass" | "Fail"
|
|
227
|
+
// totals: { Critical: 1, Serious: 3, Moderate: 5, Minor: 2 },
|
|
228
|
+
// personaGroups: {
|
|
229
|
+
// screenReader: { count: 4, findings: [...] },
|
|
230
|
+
// keyboard: { count: 2, findings: [...] },
|
|
231
|
+
// vision: { count: 3, findings: [...] },
|
|
232
|
+
// cognitive: { count: 1, findings: [...] },
|
|
233
|
+
// },
|
|
234
|
+
// quickWins: [...], // top Critical/Serious findings with fix code ready
|
|
235
|
+
// targetUrl: "https://example.com",
|
|
236
|
+
// detectedStack: { framework: "nextjs", cms: null, uiLibraries: ["radix-ui"] },
|
|
237
|
+
// }
|
|
238
|
+
```
|
|
75
239
|
|
|
76
240
|
Returns: `AuditSummary`
|
|
77
241
|
|
|
242
|
+
---
|
|
243
|
+
|
|
78
244
|
## Output API
|
|
79
245
|
|
|
80
246
|
### `getPDFReport(payload, options?)`
|
|
81
247
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
248
|
+
Generates a formal A4 PDF compliance report.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { getPDFReport } from "@diegovelasquezweb/a11y-engine";
|
|
252
|
+
|
|
253
|
+
const { buffer, contentType } = await getPDFReport(payload, {
|
|
254
|
+
baseUrl: "https://example.com",
|
|
255
|
+
target: "WCAG 2.2 AA",
|
|
256
|
+
});
|
|
86
257
|
|
|
87
|
-
|
|
258
|
+
// In a Next.js API route:
|
|
259
|
+
return new Response(buffer, { headers: { "Content-Type": contentType } });
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Returns: `Promise<{ buffer: Buffer, contentType: string }>`
|
|
263
|
+
|
|
264
|
+
---
|
|
88
265
|
|
|
89
266
|
### `getHTMLReport(payload, options?)`
|
|
90
267
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- `screenshotsDir?: string`
|
|
268
|
+
Generates an interactive HTML audit dashboard with finding cards, score gauge, and persona breakdown.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { getHTMLReport } from "@diegovelasquezweb/a11y-engine";
|
|
96
272
|
|
|
97
|
-
|
|
273
|
+
const { html, contentType } = await getHTMLReport(payload, {
|
|
274
|
+
baseUrl: "https://example.com",
|
|
275
|
+
screenshotsDir: "/path/to/screenshots",
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Returns: `Promise<{ html: string, contentType: string }>`
|
|
280
|
+
|
|
281
|
+
---
|
|
98
282
|
|
|
99
283
|
### `getChecklist(options?)`
|
|
100
284
|
|
|
101
|
-
|
|
102
|
-
|
|
285
|
+
Generates an interactive HTML manual testing checklist with 41 WCAG checks.
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import { getChecklist } from "@diegovelasquezweb/a11y-engine";
|
|
103
289
|
|
|
104
|
-
|
|
290
|
+
const { html, contentType } = await getChecklist({
|
|
291
|
+
baseUrl: "https://example.com",
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Returns: `Promise<{ html: string, contentType: string }>`
|
|
296
|
+
|
|
297
|
+
---
|
|
105
298
|
|
|
106
299
|
### `getRemediationGuide(payload, options?)`
|
|
107
300
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
301
|
+
Generates a Markdown remediation guide optimized for AI agents and developers. Includes finding details, fix code, verify commands, and source pattern findings.
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
import { getRemediationGuide } from "@diegovelasquezweb/a11y-engine";
|
|
305
|
+
|
|
306
|
+
const { markdown, contentType } = await getRemediationGuide(payload, {
|
|
307
|
+
baseUrl: "https://example.com",
|
|
308
|
+
patternFindings: payload.patternFindings ?? null,
|
|
309
|
+
});
|
|
113
310
|
|
|
114
|
-
|
|
311
|
+
// Write to disk or return as download
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Returns: `Promise<{ markdown: string, contentType: string }>`
|
|
315
|
+
|
|
316
|
+
---
|
|
115
317
|
|
|
116
318
|
### `getSourcePatterns(projectDir, options?)`
|
|
117
319
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
320
|
+
Scans a local project directory for source code accessibility patterns that runtime engines cannot detect.
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { getSourcePatterns } from "@diegovelasquezweb/a11y-engine";
|
|
324
|
+
|
|
325
|
+
const result = await getSourcePatterns("./", {
|
|
326
|
+
framework: "nextjs", // optional — scopes scan to framework source dirs
|
|
327
|
+
onlyPattern: "placeholder-only-label", // optional — run a single pattern
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// result example:
|
|
331
|
+
// {
|
|
332
|
+
// findings: [
|
|
333
|
+
// {
|
|
334
|
+
// id: "PAT-a1b2c3",
|
|
335
|
+
// pattern_id: "placeholder-only-label",
|
|
336
|
+
// title: "Input uses placeholder as its only label",
|
|
337
|
+
// severity: "Critical",
|
|
338
|
+
// status: "confirmed",
|
|
339
|
+
// file: "src/components/SearchBar.tsx",
|
|
340
|
+
// line: 12,
|
|
341
|
+
// match: ' <input placeholder="Search..." />',
|
|
342
|
+
// context: "...",
|
|
343
|
+
// fix_description: "Add an aria-label or visible <label> element",
|
|
344
|
+
// }
|
|
345
|
+
// ],
|
|
346
|
+
// summary: { total: 3, confirmed: 2, potential: 1 }
|
|
347
|
+
// }
|
|
348
|
+
```
|
|
122
349
|
|
|
123
350
|
Returns: `Promise<SourcePatternResult>`
|
|
124
351
|
|
|
352
|
+
---
|
|
353
|
+
|
|
125
354
|
## Knowledge API
|
|
126
355
|
|
|
356
|
+
These functions expose engine-owned content for UIs and agents to render. All accept an optional `{ locale?: string }` option (default: `"en"`).
|
|
357
|
+
|
|
358
|
+
### `getKnowledge(options?)`
|
|
359
|
+
|
|
360
|
+
Returns the full knowledge pack — combines all knowledge functions into one call. Useful for pre-loading UI help content.
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
import { getKnowledge } from "@diegovelasquezweb/a11y-engine";
|
|
364
|
+
|
|
365
|
+
const knowledge = getKnowledge({ locale: "en" });
|
|
366
|
+
|
|
367
|
+
// knowledge.scanner → scan options help and engine descriptions
|
|
368
|
+
// knowledge.personas → persona labels, icons, descriptions
|
|
369
|
+
// knowledge.concepts → UI concept definitions
|
|
370
|
+
// knowledge.glossary → accessibility glossary
|
|
371
|
+
// knowledge.conformanceLevels → WCAG A/AA/AAA definitions with axe tags
|
|
372
|
+
// knowledge.wcagPrinciples → the 4 WCAG principles
|
|
373
|
+
// knowledge.severityLevels → Critical/Serious/Moderate/Minor definitions
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Returns: `EngineKnowledge`
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
127
380
|
### `getScannerHelp(options?)`
|
|
128
381
|
|
|
129
|
-
|
|
130
|
-
- `locale?: string`
|
|
382
|
+
Returns scan option descriptions, allowed values, and engine metadata — used to render Advanced Settings UI.
|
|
131
383
|
|
|
132
|
-
|
|
384
|
+
```ts
|
|
385
|
+
const help = getScannerHelp();
|
|
386
|
+
// help.engines → [{ id: "axe", label: "axe-core", description: "..." }, ...]
|
|
387
|
+
// help.options → [{ id: "maxRoutes", label: "Max Routes", type: "number", ... }, ...]
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
133
391
|
|
|
134
392
|
### `getPersonaReference(options?)`
|
|
135
393
|
|
|
136
|
-
|
|
137
|
-
- `locale?: string`
|
|
394
|
+
Returns persona labels, descriptions, and disability group definitions.
|
|
138
395
|
|
|
139
|
-
|
|
396
|
+
```ts
|
|
397
|
+
const ref = getPersonaReference();
|
|
398
|
+
// ref.personas → [{ id: "screenReader", label: "Screen Readers", icon: "...", description: "..." }, ...]
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
140
402
|
|
|
141
403
|
### `getUiHelp(options?)`
|
|
142
404
|
|
|
143
|
-
|
|
144
|
-
- `locale?: string`
|
|
405
|
+
Returns shared concept definitions and a glossary of accessibility terms.
|
|
145
406
|
|
|
146
|
-
|
|
407
|
+
```ts
|
|
408
|
+
const ui = getUiHelp();
|
|
409
|
+
// ui.concepts → { wcag: "...", aria: "...", ... }
|
|
410
|
+
// ui.glossary → [{ term: "ARIA", definition: "..." }, ...]
|
|
411
|
+
```
|
|
147
412
|
|
|
148
|
-
|
|
413
|
+
---
|
|
149
414
|
|
|
150
|
-
|
|
151
|
-
- `locale?: string`
|
|
415
|
+
### `getConformanceLevels(options?)`
|
|
152
416
|
|
|
153
|
-
Returns
|
|
417
|
+
Returns WCAG conformance level definitions with their corresponding axe-core tag sets.
|
|
154
418
|
|
|
155
|
-
|
|
419
|
+
```ts
|
|
420
|
+
const { conformanceLevels } = getConformanceLevels();
|
|
421
|
+
// conformanceLevels[0] → { id: "AA", label: "WCAG 2.2 AA", axeTags: ["wcag2a", "wcag2aa", ...] }
|
|
422
|
+
```
|
|
156
423
|
|
|
157
|
-
|
|
158
|
-
- `locale?: string`
|
|
424
|
+
---
|
|
159
425
|
|
|
160
|
-
|
|
426
|
+
### `getWcagPrinciples(options?)`
|
|
161
427
|
|
|
162
|
-
|
|
428
|
+
Returns the four WCAG principles (Perceivable, Operable, Understandable, Robust) with criterion prefix patterns.
|
|
163
429
|
|
|
164
|
-
|
|
165
|
-
|
|
430
|
+
```ts
|
|
431
|
+
const { wcagPrinciples } = getWcagPrinciples();
|
|
432
|
+
// wcagPrinciples[0] → { id: "perceivable", label: "Perceivable", prefix: "1.", description: "..." }
|
|
433
|
+
```
|
|
166
434
|
|
|
167
|
-
|
|
435
|
+
---
|
|
168
436
|
|
|
169
|
-
### `
|
|
437
|
+
### `getSeverityLevels(options?)`
|
|
170
438
|
|
|
171
|
-
|
|
172
|
-
- `locale?: string`
|
|
439
|
+
Returns severity level definitions with labels, descriptions, and ordering.
|
|
173
440
|
|
|
174
|
-
|
|
441
|
+
```ts
|
|
442
|
+
const { severityLevels } = getSeverityLevels();
|
|
443
|
+
// severityLevels[0] → { id: "Critical", label: "Critical", order: 0, description: "..." }
|
|
444
|
+
```
|
|
175
445
|
|
|
176
446
|
---
|
|
177
447
|
|
package/docs/engine-manifest.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Source Modules](#1-source-modules)
|
|
10
|
+
- [Asset Modules](#2-asset-modules)
|
|
11
|
+
- [Test Suite](#3-test-suite)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
7
15
|
This document is the current technical inventory of the engine package.
|
|
8
16
|
|
|
9
17
|
## 1) Source Modules
|
package/package.json
CHANGED
package/src/ai/claude.mjs
CHANGED
|
@@ -25,18 +25,10 @@ const MAX_AI_FINDINGS = 20; // cap to control cost
|
|
|
25
25
|
* @param {object} context
|
|
26
26
|
* @returns {string}
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
const { framework, cms, uiLibraries } = context.stack || {};
|
|
30
|
-
|
|
31
|
-
let stackInfo = "";
|
|
32
|
-
if (framework) stackInfo += `Framework: ${framework}\n`;
|
|
33
|
-
if (cms) stackInfo += `CMS: ${cms}\n`;
|
|
34
|
-
if (uiLibraries?.length) stackInfo += `UI Libraries: ${uiLibraries.join(", ")}\n`;
|
|
35
|
-
|
|
36
|
-
return `You are an expert web accessibility engineer specializing in WCAG 2.2 AA remediation.
|
|
28
|
+
export const DEFAULT_AI_SYSTEM_PROMPT = `You are an expert web accessibility engineer specializing in WCAG 2.2 AA remediation.
|
|
37
29
|
|
|
38
30
|
Your task is to provide a developer-friendly AI hint for each accessibility finding — something MORE USEFUL than the generic automated fix already provided.
|
|
39
|
-
|
|
31
|
+
|
|
40
32
|
For each finding, provide:
|
|
41
33
|
1. fixDescription: A 2-3 sentence explanation that goes BEYOND the generic fix. Explain WHY this matters for real users, WHAT specifically to look for in the codebase, and HOW to verify the fix works. Be specific to the selector and actual violation data provided.
|
|
42
34
|
2. fixCode: A ready-to-use, production-quality code snippet in the correct syntax for the stack. Do NOT copy the existing fix code — write a BETTER, more complete example that a developer can use directly.
|
|
@@ -48,6 +40,20 @@ Rules:
|
|
|
48
40
|
- Reference the actual selector or element from the finding when possible
|
|
49
41
|
- If the violation data contains specific values (colors, ratios, labels), use them in your response
|
|
50
42
|
- Respond in JSON only — no markdown, no explanation outside the JSON structure`;
|
|
43
|
+
|
|
44
|
+
function buildSystemPrompt(context) {
|
|
45
|
+
const { framework, cms, uiLibraries } = context.stack || {};
|
|
46
|
+
|
|
47
|
+
let stackInfo = "";
|
|
48
|
+
if (framework) stackInfo += `Framework: ${framework}\n`;
|
|
49
|
+
if (cms) stackInfo += `CMS: ${cms}\n`;
|
|
50
|
+
if (uiLibraries?.length) stackInfo += `UI Libraries: ${uiLibraries.join(", ")}\n`;
|
|
51
|
+
|
|
52
|
+
const base = DEFAULT_AI_SYSTEM_PROMPT;
|
|
53
|
+
return stackInfo ? base.replace(
|
|
54
|
+
"For each finding, provide:",
|
|
55
|
+
`Project context:\n${stackInfo}\nFor each finding, provide:`
|
|
56
|
+
) : base;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
/**
|
package/src/index.d.mts
CHANGED
|
@@ -403,15 +403,6 @@ export interface RunAuditOptions {
|
|
|
403
403
|
onProgress?: (step: string, status: string, extra?: Record<string, unknown>) => void;
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
export interface AiOptions {
|
|
407
|
-
enabled?: boolean;
|
|
408
|
-
apiKey?: string;
|
|
409
|
-
githubToken?: string;
|
|
410
|
-
model?: string;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
406
|
|
|
416
407
|
|
|
417
408
|
export interface EnrichmentOptions {
|
|
@@ -471,3 +462,21 @@ export function getWcagPrinciples(options?: KnowledgeOptions): WcagPrinciplesRes
|
|
|
471
462
|
export function getSeverityLevels(options?: KnowledgeOptions): SeverityLevelsResult;
|
|
472
463
|
|
|
473
464
|
export function getKnowledge(options?: KnowledgeOptions): EngineKnowledge;
|
|
465
|
+
|
|
466
|
+
export const DEFAULT_AI_SYSTEM_PROMPT: string;
|
|
467
|
+
|
|
468
|
+
export interface ViewportPreset {
|
|
469
|
+
label: string;
|
|
470
|
+
width: number;
|
|
471
|
+
height: number;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export const VIEWPORT_PRESETS: ViewportPreset[];
|
|
475
|
+
|
|
476
|
+
export interface AiOptions {
|
|
477
|
+
enabled?: boolean;
|
|
478
|
+
apiKey?: string;
|
|
479
|
+
githubToken?: string;
|
|
480
|
+
model?: string;
|
|
481
|
+
systemPrompt?: string;
|
|
482
|
+
}
|
package/src/index.mjs
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { ASSET_PATHS, loadAssetJson } from "./core/asset-loader.mjs";
|
|
8
|
+
export { DEFAULT_AI_SYSTEM_PROMPT } from "./ai/claude.mjs";
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// Lazy-loaded asset cache
|
|
@@ -238,7 +239,7 @@ export function getFindings(input, options = {}) {
|
|
|
238
239
|
recommendedFix: finding.recommended_fix,
|
|
239
240
|
evidence: finding.evidence,
|
|
240
241
|
totalInstances: finding.total_instances,
|
|
241
|
-
effort: finding.effort,
|
|
242
|
+
effort: finding.effort ?? (finding.fix_code ? "low" : "high"),
|
|
242
243
|
relatedRules: finding.related_rules,
|
|
243
244
|
screenshotPath: finding.screenshot_path,
|
|
244
245
|
falsePositiveRisk: finding.false_positive_risk,
|
|
@@ -583,6 +584,13 @@ export function getSeverityLevels(options = {}) {
|
|
|
583
584
|
};
|
|
584
585
|
}
|
|
585
586
|
|
|
587
|
+
export const VIEWPORT_PRESETS = [
|
|
588
|
+
{ label: "Desktop", width: 1280, height: 800 },
|
|
589
|
+
{ label: "Laptop", width: 1440, height: 900 },
|
|
590
|
+
{ label: "Tablet", width: 768, height: 1024 },
|
|
591
|
+
{ label: "Mobile", width: 375, height: 812 },
|
|
592
|
+
];
|
|
593
|
+
|
|
586
594
|
export function getKnowledge(options = {}) {
|
|
587
595
|
const scanner = getScannerHelp(options);
|
|
588
596
|
const personas = getPersonaReference(options);
|