@diegovelasquezweb/a11y-engine 0.1.8 → 0.2.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 CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - **Programmatic API** — 7 exported functions accessible via `import { ... } from "@diegovelasquezweb/a11y-engine"`:
13
+ - `getEnrichedFindings(input, options?)` — normalizes raw findings, canonicalizes pa11y rules, enriches with fix intelligence, infers effort, sorts by severity. Accepts a full scan payload or a raw findings array. Supports `screenshotUrlBuilder` callback for consumer-specific screenshot URLs.
14
+ - `getAuditSummary(findings, payload?)` — computes severity totals, compliance score, grade label, WCAG pass/fail status, persona impact groups, quick wins, target URL, and detected stack from metadata.
15
+ - `getPDFReport(payload, options?)` — generates a formal A4 PDF compliance report via Playwright. Returns `{ buffer, contentType }`.
16
+ - `getChecklist(options?)` — generates a standalone manual accessibility testing checklist as HTML. Returns `{ html, contentType }`.
17
+ - `getHTMLReport(payload, options?)` — generates an interactive HTML audit dashboard with severity filters and fix guidance. Supports embedded base64 screenshots via `screenshotsDir`. Returns `{ html, contentType }`.
18
+ - `getRemediationGuide(payload, options?)` — generates a Markdown remediation guide optimized for AI agents. Supports optional `patternFindings` from source scanner. Returns `{ markdown, contentType }`.
19
+ - `getSourcePatterns(projectDir, options?)` — scans project source code for accessibility patterns not detectable by axe-core. Returns `{ findings, summary }`.
20
+ - **TypeScript type declarations** shipped with the package (`scripts/index.d.mts`):
21
+ - `Finding` — raw finding with all snake_case fields
22
+ - `EnrichedFinding` — extends Finding with camelCase aliases and enriched fields
23
+ - `AuditSummary` — full audit summary including totals, score, personas, quick wins, detected stack
24
+ - `SeverityTotals`, `PersonaGroup`, `DetectedStack`, `ComplianceScore`
25
+ - `ScanPayload`, `EnrichmentOptions`, `ReportOptions`
26
+ - `PDFReport`, `HTMLReport`, `ChecklistReport`, `RemediationGuide`
27
+ - `SourcePatternFinding`, `SourcePatternResult`, `SourcePatternOptions`
28
+ - `exports` and `main` fields in `package.json` pointing to `scripts/index.mjs`
29
+ - `--axe-tags` CLI flag passthrough from `audit.mjs` to `dom-scanner.mjs`
30
+ - `resolveScanDirs` exported from `source-scanner.mjs` for programmatic use
31
+
32
+ ### Changed
33
+
34
+ - `getEnrichedFindings` always creates camelCase aliases (`fixDescription`, `fixCode`, `screenshotPath`, `wcagCriterionId`, `impactedUsers`, etc.) regardless of whether the finding already has fix data — fixes bug where camelCase fields were `undefined` when snake_case data existed
35
+ - `getEnrichedFindings` infers `effort` field after intelligence enrichment: findings with `fixCode` default to `"low"`, others to `"high"` — unless an explicit effort value already exists
36
+ - `getEnrichedFindings` normalizes raw findings internally — consumers no longer need to pre-process the findings array
37
+ - `getEnrichedFindings` sorts findings by severity (Critical > Serious > Moderate > Minor) then by ID
38
+ - `getAuditSummary` now includes `quickWins` (top 3 Critical/Serious findings with fix code), `targetUrl` (extracted from metadata with fallbacks), and `detectedStack` (framework/CMS/libraries from project context)
39
+ - CLI (`audit.mjs`) continues to work standalone — the programmatic API is additive
40
+
10
41
  ---
11
42
 
12
43
  ## [0.1.3] — 2026-03-14
package/README.md CHANGED
@@ -1,176 +1,218 @@
1
1
  # @diegovelasquezweb/a11y-engine
2
2
 
3
- Multi-engine WCAG 2.2 AA accessibility audit engine. Combines three scanning engines (axe-core, Chrome DevTools Protocol, and pa11y), merges and deduplicates their findings, enriches results with fix intelligence, and produces structured artifacts for developers, agents, and stakeholders.
3
+ Multi-engine WCAG 2.2 accessibility audit engine. Combines three scanning engines (axe-core, Chrome DevTools Protocol, and pa11y), merges and deduplicates their findings, enriches results with fix intelligence, and produces structured artifacts for developers, agents, and stakeholders.
4
4
 
5
5
  ## What it is
6
6
 
7
- A Node.js CLI and programmatic engine that:
7
+ A Node.js package that works two ways:
8
8
 
9
- 1. Crawls a target URL and discovers routes automatically
10
- 2. Runs three independent accessibility engines against each page:
11
- - **axe-core** — industry-standard WCAG rule engine, injected into the live page via Playwright
12
- - **CDP** (Chrome DevTools Protocol) — queries the browser's accessibility tree directly for issues axe may miss (missing accessible names, aria-hidden on focusable elements)
13
- - **pa11y** (HTML CodeSniffer) — catches WCAG violations around heading hierarchy, link purpose, and form associations
14
- 3. Merges and deduplicates findings across all three engines
15
- 4. Optionally scans project source code for patterns no runtime engine can detect
16
- 5. Enriches each finding with stack-aware fix guidance, selectors, and verification commands
17
- 6. Produces a full artifact set: JSON data, Markdown remediation guide, HTML dashboard, PDF compliance report, and manual testing checklist
9
+ 1. **CLI** — run `npx a11y-audit --base-url <url>` to scan a site and generate reports
10
+ 2. **Programmatic API** import functions directly to normalize findings, compute scores, and generate reports in your own application
18
11
 
19
- ## Why use this engine
12
+ ## Programmatic API
20
13
 
21
- | Capability | With this engine | Without |
14
+ ```bash
15
+ npm install @diegovelasquezweb/a11y-engine
16
+ ```
17
+
18
+ ```ts
19
+ import {
20
+ getEnrichedFindings,
21
+ getAuditSummary,
22
+ getPDFReport,
23
+ getChecklist,
24
+ getHTMLReport,
25
+ getRemediationGuide,
26
+ getSourcePatterns,
27
+ } from "@diegovelasquezweb/a11y-engine";
28
+ ```
29
+
30
+ ### getEnrichedFindings
31
+
32
+ Normalizes raw scan findings, canonicalizes pa11y rules to axe equivalents, enriches with fix intelligence, infers effort, and sorts by severity.
33
+
34
+ ```ts
35
+ const findings = getEnrichedFindings(scanPayload, {
36
+ screenshotUrlBuilder: (path) => `/api/screenshot?path=${encodeURIComponent(path)}`,
37
+ });
38
+ ```
39
+
40
+ | Parameter | Type | Description |
22
41
  | :--- | :--- | :--- |
23
- | **Multi-engine scanning** | axe-core + CDP accessibility tree + pa11y (HTML CodeSniffer) with cross-engine deduplication | Single engine higher false-negative rate |
24
- | **Full WCAG 2.2 Coverage** | Three runtime engines + source code pattern scanner | Runtime scan only misses structural and source-level issues |
25
- | **Fix Intelligence** | Stack-aware patches with code snippets tailored to detected framework | Raw rule violations with no remediation context |
26
- | **Structured Artifacts** | JSON + Markdown + HTML + PDF + Checklist — ready to consume or forward | Findings exist only in the terminal session |
27
- | **CI/Agent Integration** | Deterministic exit codes, stdout-parseable output paths, JSON schema | Requires wrapper scripting |
42
+ | `input` | `ScanPayload \| Finding[] \| Record<string, unknown>[]` | Raw scan output or findings array |
43
+ | `options.screenshotUrlBuilder` | `(rawPath: string) => string` | Transforms screenshot file paths into consumer-specific URLs |
28
44
 
29
- ## How the scan pipeline works
45
+ **Returns**: `EnrichedFinding[]` normalized, enriched, sorted findings with both snake_case and camelCase fields.
46
+
47
+ ### getAuditSummary
48
+
49
+ Computes a complete audit summary from enriched findings.
30
50
 
51
+ ```ts
52
+ const summary = getAuditSummary(findings, scanPayload);
53
+ // summary.score → 72
54
+ // summary.label → "Good"
55
+ // summary.wcagStatus → "Fail"
56
+ // summary.totals → { Critical: 1, Serious: 3, Moderate: 5, Minor: 2 }
57
+ // summary.personaGroups → { screenReader: {...}, keyboard: {...}, ... }
58
+ // summary.quickWins → [top 3 fixable Critical/Serious findings]
59
+ // summary.targetUrl → "https://example.com"
60
+ // summary.detectedStack → { framework: "nextjs", cms: null, uiLibraries: [] }
31
61
  ```
32
- URL
33
- |
34
- v
35
- [1. Crawl & Discover] sitemap.xml / BFS link crawl / explicit --routes
36
- |
37
- v
38
- [2. Navigate] Playwright opens each route in Chromium
39
- |
40
- +---> [axe-core] Injects axe into the page, runs WCAG tag checks
41
- |
42
- +---> [CDP] Opens a CDP session, reads the full accessibility tree
43
- |
44
- +---> [pa11y] Launches HTML CodeSniffer via Puppeteer Chrome
45
- |
46
- v
47
- [3. Merge & Dedup] Combines findings, removes cross-engine duplicates
48
- |
49
- v
50
- [4. Analyze] Enriches with WCAG mapping, severity, fix code, framework hints
51
- |
52
- v
53
- [5. Reports] HTML dashboard, PDF, checklist, Markdown remediation
62
+
63
+ | Parameter | Type | Description |
64
+ | :--- | :--- | :--- |
65
+ | `findings` | `EnrichedFinding[]` | Output from `getEnrichedFindings` |
66
+ | `payload` | `ScanPayload \| null` | Original scan payload for metadata extraction |
67
+
68
+ **Returns**: `AuditSummary`
69
+
70
+ ### getPDFReport
71
+
72
+ Generates a formal A4 PDF compliance report using Playwright.
73
+
74
+ ```ts
75
+ const { buffer, contentType } = await getPDFReport(scanPayload, {
76
+ baseUrl: "https://example.com",
77
+ });
78
+ fs.writeFileSync("report.pdf", buffer);
54
79
  ```
55
80
 
56
- ## Installation
81
+ **Returns**: `Promise<PDFReport>` — `{ buffer: Buffer, contentType: "application/pdf" }`
57
82
 
58
- ```bash
59
- npm install @diegovelasquezweb/a11y-engine
60
- npx playwright install chromium
61
- npx puppeteer browsers install chrome
83
+ ### getHTMLReport
84
+
85
+ Generates an interactive HTML audit dashboard with severity filters, persona impact, and fix guidance.
86
+
87
+ ```ts
88
+ const { html, contentType } = await getHTMLReport(scanPayload, {
89
+ baseUrl: "https://example.com",
90
+ screenshotsDir: "/path/to/.audit/screenshots",
91
+ });
62
92
  ```
63
93
 
64
- ```bash
65
- pnpm add @diegovelasquezweb/a11y-engine
66
- pnpm exec playwright install chromium
67
- npx puppeteer browsers install chrome
94
+ **Returns**: `Promise<HTMLReport>` — `{ html: string, contentType: "text/html" }`
95
+
96
+ ### getChecklist
97
+
98
+ Generates a standalone manual accessibility testing checklist.
99
+
100
+ ```ts
101
+ const { html, contentType } = await getChecklist({
102
+ baseUrl: "https://example.com",
103
+ });
68
104
  ```
69
105
 
70
- > **Two browsers are required:**
71
- > - **Playwright Chromium** — used by axe-core and CDP checks
72
- > - **Puppeteer Chrome** — used by pa11y (HTML CodeSniffer)
73
- >
74
- > These are separate browser installations. If Puppeteer Chrome is missing, pa11y checks fail silently (non-fatal) and the scan continues with axe + CDP only.
106
+ **Returns**: `Promise<ChecklistReport>` `{ html: string, contentType: "text/html" }`
107
+
108
+ ### getRemediationGuide
109
+
110
+ Generates a Markdown remediation guide optimized for AI agents.
111
+
112
+ ```ts
113
+ const { markdown, contentType } = await getRemediationGuide(scanPayload, {
114
+ baseUrl: "https://example.com",
115
+ patternFindings: sourcePatternResult,
116
+ });
117
+ ```
118
+
119
+ **Returns**: `Promise<RemediationGuide>` — `{ markdown: string, contentType: "text/markdown" }`
120
+
121
+ ### getSourcePatterns
122
+
123
+ Scans project source code for accessibility patterns not detectable by axe-core at runtime.
124
+
125
+ ```ts
126
+ const { findings, summary } = await getSourcePatterns("/path/to/project", {
127
+ framework: "nextjs",
128
+ });
129
+ // summary → { total: 12, confirmed: 10, potential: 2 }
130
+ ```
131
+
132
+ **Returns**: `Promise<SourcePatternResult>` — `{ findings: SourcePatternFinding[], summary: { total, confirmed, potential } }`
133
+
134
+ ## CLI usage
75
135
 
76
- ## Quick start
136
+ The CLI runs the full scan pipeline: crawl, scan with 3 engines, merge, analyze, and generate reports.
77
137
 
78
138
  ```bash
79
- # Minimal scan — produces remediation.md in .audit/
139
+ # Minimal scan
80
140
  npx a11y-audit --base-url https://example.com
81
141
 
82
142
  # Full audit with all reports
83
143
  npx a11y-audit --base-url https://example.com --with-reports --output ./audit/report.html
84
144
 
85
- # Scan with source code intelligence (for stack-aware fix guidance)
145
+ # Scan with source code intelligence
86
146
  npx a11y-audit --base-url http://localhost:3000 --project-dir . --with-reports --output ./audit/report.html
87
147
  ```
88
148
 
89
- ## CLI usage
90
-
91
- ```
92
- a11y-audit --base-url <url> [options]
93
- ```
94
-
95
- ### Targeting & scope
149
+ ### Targeting and scope
96
150
 
97
151
  | Flag | Argument | Default | Description |
98
152
  | :--- | :--- | :--- | :--- |
99
- | `--base-url` | `<url>` | (Required) | Starting URL for the audit. |
100
- | `--max-routes` | `<num>` | `10` | Max routes to discover and scan. |
101
- | `--crawl-depth` | `<num>` | `2` | BFS link-follow depth during discovery (1-3). |
102
- | `--routes` | `<csv>` | — | Explicit path list, bypasses auto-discovery. |
103
- | `--project-dir` | `<path>` | — | Path to project source. Enables source pattern scanner and framework auto-detection. |
153
+ | `--base-url` | `<url>` | (Required) | Starting URL for the audit |
154
+ | `--max-routes` | `<num>` | `10` | Max routes to discover and scan |
155
+ | `--crawl-depth` | `<num>` | `2` | BFS link-follow depth during discovery (1-3) |
156
+ | `--routes` | `<csv>` | — | Explicit path list, bypasses auto-discovery |
157
+ | `--project-dir` | `<path>` | — | Path to project source for stack-aware fixes and source pattern scanning |
104
158
 
105
159
  ### Audit intelligence
106
160
 
107
161
  | Flag | Argument | Default | Description |
108
162
  | :--- | :--- | :--- | :--- |
109
- | `--target` | `<text>` | `WCAG 2.2 AA` | Compliance target label in reports. |
110
- | `--only-rule` | `<id>` | | Run a single axe rule (e.g. `color-contrast`). |
111
- | `--ignore-findings` | `<csv>` | — | Rule IDs to exclude from output. |
112
- | `--exclude-selectors` | `<csv>` | — | CSS selectors to skip during DOM scan. |
113
- | `--axe-tags` | `<csv>` | `wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22a,wcag22aa` | axe-core WCAG tag filter. |
114
- | `--framework` | `<name>` | — | Override auto-detected stack. Supported: `nextjs`, `gatsby`, `react`, `nuxt`, `vue`, `angular`, `astro`, `svelte`, `shopify`, `wordpress`, `drupal`. |
163
+ | `--target` | `<text>` | `WCAG 2.2 AA` | Compliance target label in reports |
164
+ | `--axe-tags` | `<csv>` | `wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22a,wcag22aa` | axe-core WCAG tag filter |
165
+ | `--only-rule` | `<id>` | — | Run a single axe rule (e.g. `color-contrast`) |
166
+ | `--ignore-findings` | `<csv>` | — | Rule IDs to exclude from output |
167
+ | `--exclude-selectors` | `<csv>` | | CSS selectors to skip during DOM scan |
168
+ | `--framework` | `<name>` | — | Override auto-detected stack (`nextjs`, `react`, `vue`, `angular`, `svelte`, `shopify`, `wordpress`, etc.) |
115
169
 
116
- ### Execution & emulation
170
+ ### Execution and emulation
117
171
 
118
172
  | Flag | Argument | Default | Description |
119
173
  | :--- | :--- | :--- | :--- |
120
- | `--color-scheme` | `light\|dark` | `light` | Emulate `prefers-color-scheme`. |
121
- | `--wait-until` | `domcontentloaded\|load\|networkidle` | `domcontentloaded` | Playwright page load strategy. Use `networkidle` for SPAs. |
122
- | `--viewport` | `<WxH>` | — | Viewport size (e.g. `375x812`, `1440x900`). |
123
- | `--wait-ms` | `<num>` | `2000` | Delay after page load before running axe (ms). |
124
- | `--timeout-ms` | `<num>` | `30000` | Network timeout per page (ms). |
125
- | `--headed` | — | `false` | Run browser in visible mode. |
126
- | `--affected-only` | — | `false` | Re-scan only routes with previous violations. Requires a prior scan in `.audit/`. |
174
+ | `--color-scheme` | `light\|dark` | `light` | Emulate `prefers-color-scheme` |
175
+ | `--wait-until` | `domcontentloaded\|load\|networkidle` | `domcontentloaded` | Playwright page load strategy |
176
+ | `--viewport` | `<WxH>` | — | Viewport size (e.g. `375x812`) |
177
+ | `--wait-ms` | `<num>` | `2000` | Delay after page load before scanning (ms) |
178
+ | `--timeout-ms` | `<num>` | `30000` | Network timeout per page (ms) |
179
+ | `--headed` | — | `false` | Run browser in visible mode |
180
+ | `--affected-only` | — | `false` | Re-scan only routes with previous violations |
127
181
 
128
182
  ### Output generation
129
183
 
130
184
  | Flag | Argument | Default | Description |
131
185
  | :--- | :--- | :--- | :--- |
132
- | `--with-reports` | — | `false` | Generate HTML + PDF + Checklist reports. Requires `--output`. |
133
- | `--skip-reports` | | `true` | Skip visual report generation (default). |
134
- | `--output` | `<path>` | — | Output path for `report.html` (PDF and checklist derive from it). |
135
- | `--skip-patterns` | — | `false` | Disable source code pattern scanner even when `--project-dir` is set. |
136
-
137
- ## Common command patterns
138
-
139
- ```bash
140
- # Focused audit — one rule, one route
141
- a11y-audit --base-url https://example.com --only-rule color-contrast --routes /checkout --max-routes 1
142
-
143
- # Dark mode audit
144
- a11y-audit --base-url https://example.com --color-scheme dark
186
+ | `--with-reports` | — | `false` | Generate HTML + PDF + Checklist reports |
187
+ | `--output` | `<path>` | | Output path for `report.html` |
188
+ | `--skip-patterns` | — | `false` | Disable source code pattern scanner |
145
189
 
146
- # SPA with deferred rendering
147
- a11y-audit --base-url https://example.com --wait-until networkidle --wait-ms 3000
148
-
149
- # Mobile viewport
150
- a11y-audit --base-url https://example.com --viewport 375x812
151
-
152
- # Fast re-audit after fixes (skips clean pages)
153
- a11y-audit --base-url https://example.com --affected-only
190
+ ## How the scan pipeline works
154
191
 
155
- # Ignore known false positives
156
- a11y-audit --base-url https://example.com --ignore-findings color-contrast,frame-title
157
192
  ```
158
-
159
- ## Output artifacts
160
-
161
- All artifacts are written to `.audit/` relative to the package root.
162
-
163
- | File | Always generated | Description |
164
- | :--- | :--- | :--- |
165
- | `a11y-scan-results.json` | Yes | Raw merged results from axe-core + CDP + pa11y per route |
166
- | `a11y-findings.json` | Yes | Enriched findings with fix intelligence, WCAG mapping, and severity |
167
- | `progress.json` | Yes | Real-time scan progress with per-engine step status and finding counts |
168
- | `remediation.md` | Yes | AI-agent-optimized remediation roadmap |
169
- | `report.html` | With `--with-reports` | Interactive HTML dashboard |
170
- | `report.pdf` | With `--with-reports` | Formal compliance PDF |
171
- | `checklist.html` | With `--with-reports` | Manual WCAG testing checklist |
172
-
173
- See [Output Artifacts](docs/outputs.md) for full schema reference.
193
+ URL
194
+ |
195
+ v
196
+ [1. Crawl & Discover] sitemap.xml / BFS link crawl / explicit --routes
197
+ |
198
+ v
199
+ [2. Navigate] Playwright opens each route in Chromium
200
+ |
201
+ +---> [axe-core] Injects axe into the page, runs WCAG tag checks
202
+ |
203
+ +---> [CDP] Opens a CDP session, reads the full accessibility tree
204
+ |
205
+ +---> [pa11y] Launches HTML CodeSniffer via Puppeteer Chrome
206
+ |
207
+ v
208
+ [3. Merge & Dedup] Combines findings, removes cross-engine duplicates
209
+ |
210
+ v
211
+ [4. Analyze] Enriches with WCAG mapping, severity, fix code, framework hints
212
+ |
213
+ v
214
+ [5. Reports] HTML dashboard, PDF, checklist, Markdown remediation
215
+ ```
174
216
 
175
217
  ## Scan engines
176
218
 
@@ -181,52 +223,48 @@ The primary engine. Runs Deque's axe-core rule set against the live DOM inside P
181
223
  ### CDP (Chrome DevTools Protocol)
182
224
 
183
225
  Queries the browser's full accessibility tree via a CDP session. Catches issues axe may miss:
184
- - Interactive elements (buttons, links, inputs) with no accessible name
226
+ - Interactive elements with no accessible name
185
227
  - Focusable elements hidden with `aria-hidden`
186
228
 
187
229
  ### pa11y (HTML CodeSniffer)
188
230
 
189
- Runs Squiz's HTML CodeSniffer via Puppeteer Chrome. Catches WCAG violations around:
190
- - Heading hierarchy
191
- - Link purpose
192
- - Form label associations
231
+ Runs Squiz's HTML CodeSniffer via Puppeteer Chrome. Catches WCAG violations around heading hierarchy, link purpose, and form label associations.
193
232
 
194
233
  Requires a separate Chrome installation (`npx puppeteer browsers install chrome`). If Chrome is missing, pa11y fails silently and the scan continues with axe + CDP.
195
234
 
196
- ### Merge & deduplication
197
-
198
- After all three engines run, findings are merged and deduplicated:
199
- - axe findings are added first (baseline)
200
- - CDP findings are checked against axe equivalents (e.g. `cdp-missing-accessible-name` vs `button-name`) to avoid duplicates
201
- - pa11y findings are checked against existing selectors to avoid triple-reporting the same element
202
-
203
- ## Troubleshooting
204
-
205
- **`Error: browserType.launch: Executable doesn't exist`**
206
- Run `npx playwright install chromium` (or `pnpm exec playwright install chromium`).
235
+ ## Output artifacts
207
236
 
208
- **`pa11y checks failed (non-fatal): Could not find Chrome`**
209
- pa11y requires Puppeteer's Chrome, which is separate from Playwright's Chromium. Install it with `npx puppeteer browsers install chrome`.
237
+ All artifacts are written to `.audit/` relative to the package root.
210
238
 
211
- **`Missing required argument: --base-url`**
212
- The flag is required. Provide a full URL including protocol: `--base-url https://example.com`.
239
+ | File | Always generated | Description |
240
+ | :--- | :--- | :--- |
241
+ | `a11y-scan-results.json` | Yes | Raw merged results from axe + CDP + pa11y per route |
242
+ | `a11y-findings.json` | Yes | Enriched findings with fix intelligence |
243
+ | `progress.json` | Yes | Real-time scan progress with per-engine step status |
244
+ | `remediation.md` | Yes | AI-agent-optimized remediation roadmap |
245
+ | `report.html` | With `--with-reports` | Interactive HTML dashboard |
246
+ | `report.pdf` | With `--with-reports` | Formal compliance PDF |
247
+ | `checklist.html` | With `--with-reports` | Manual WCAG testing checklist |
213
248
 
214
- **Scan returns 0 findings on an SPA**
215
- Use `--wait-until networkidle --wait-ms 3000` to let async content render before the engines run.
249
+ ## Installation
216
250
 
217
- **`--with-reports` exits without generating PDF**
218
- Ensure `--output` is also set and points to an `.html` file path: `--output ./audit/report.html`.
251
+ ```bash
252
+ npm install @diegovelasquezweb/a11y-engine
253
+ npx playwright install chromium
254
+ npx puppeteer browsers install chrome
255
+ ```
219
256
 
220
- **Chromium crashes in CI**
221
- Add `--no-sandbox` via the `PLAYWRIGHT_CHROMIUM_LAUNCH_OPTIONS` env var, or run Playwright with the `--with-deps` flag during browser installation.
257
+ > **Two browsers are required:**
258
+ > - **Playwright Chromium** used by axe-core and CDP checks
259
+ > - **Puppeteer Chrome** — used by pa11y (HTML CodeSniffer)
222
260
 
223
261
  ## Documentation
224
262
 
225
263
  | Resource | Description |
226
264
  | :--- | :--- |
227
- | [Architecture](https://github.com/diegovelasquezweb/a11y-engine/blob/main/docs/architecture.md) | How the multi-engine scanner pipeline works |
228
- | [CLI Handbook](https://github.com/diegovelasquezweb/a11y-engine/blob/main/docs/cli-handbook.md) | Full flag reference and usage patterns |
229
- | [Output Artifacts](https://github.com/diegovelasquezweb/a11y-engine/blob/main/docs/outputs.md) | Schema and structure of every generated file |
265
+ | [Architecture](docs/architecture.md) | How the multi-engine scanner pipeline works |
266
+ | [CLI Handbook](docs/cli-handbook.md) | Full flag reference and usage patterns |
267
+ | [Output Artifacts](docs/outputs.md) | Schema and structure of every generated file |
230
268
 
231
269
  ## License
232
270
 
@@ -205,6 +205,28 @@ Assets are static JSON files bundled with the package under `assets/`. They are
205
205
  | `remediation/axe-check-maps.json` | axe check-to-rule mapping |
206
206
  | `remediation/source-boundaries.json` | Framework-specific source file locations |
207
207
 
208
+ ## Programmatic API
209
+
210
+ In addition to the CLI pipeline, the engine exports 7 functions via `scripts/index.mjs` for direct consumption by Node.js applications (e.g. `a11y-scanner`). These functions reuse the same internal renderers, assets, and enrichment logic as the CLI — no duplication.
211
+
212
+ ```
213
+ scripts/index.mjs (public API)
214
+ ├── getEnrichedFindings() ← uses asset-loader, intelligence.json, pa11y-config.json
215
+ ├── getAuditSummary() ← uses compliance-config.json, wcag-reference.json
216
+ ├── getPDFReport() ← uses reports/renderers/pdf.mjs + Playwright
217
+ ├── getHTMLReport() ← uses reports/renderers/html.mjs + findings.mjs
218
+ ├── getChecklist() ← uses reports/renderers/html.mjs (manual checks)
219
+ ├── getRemediationGuide() ← uses reports/renderers/md.mjs
220
+ └── getSourcePatterns() ← uses engine/source-scanner.mjs
221
+ ```
222
+
223
+ ### Key design decisions
224
+
225
+ - **No filesystem output** — all API functions return data in memory (strings, Buffers, arrays). The consumer decides where to write.
226
+ - **Payload in, results out** — functions accept the raw `{ findings, metadata }` payload that `a11y-findings.json` contains. No need to resolve paths or read files.
227
+ - **`screenshotUrlBuilder` callback** — `getEnrichedFindings` accepts an optional function to transform raw screenshot paths (e.g. `screenshots/0-color-contrast.png`) into consumer-specific URLs (e.g. `/api/scan/{id}/screenshot?path=...`). This keeps URL construction out of the engine.
228
+ - **CLI unaffected** — the `audit.mjs` orchestrator and all CLI builders continue to work exactly as before. The API is additive.
229
+
208
230
  ## Execution model and timeouts
209
231
 
210
232
  `audit.mjs` spawns each stage as a child process via `node:child_process`. All child processes:
@@ -235,3 +235,7 @@ The engine never exits `1` just because findings were found. Exit `1` only indic
235
235
  REMEDIATION_PATH=<abs-path> # always printed on success
236
236
  REPORT_PATH=<abs-path> # only printed when --with-reports is set
237
237
  ```
238
+
239
+ ## Programmatic alternative
240
+
241
+ For applications that embed the engine as a dependency (e.g. web dashboards, CI pipelines), the engine also exports a programmatic API that processes scan data in memory without filesystem operations. See the [README](../README.md#programmatic-api) for full documentation.
package/docs/outputs.md CHANGED
@@ -257,40 +257,54 @@ Written to the same directory as `--output` as `checklist.html`.
257
257
 
258
258
  ## Consuming outputs programmatically
259
259
 
260
- ### Reading `a11y-findings.json` from an integration
261
-
262
- ```js
263
- import fs from "node:fs";
264
- import path from "node:path";
265
- import { createRequire } from "node:module";
266
-
267
- // Resolve real path (handles pnpm symlinks)
268
- const req = createRequire(import.meta.url);
269
- const auditScript = req.resolve("@diegovelasquezweb/a11y-engine/scripts/audit.mjs");
270
- const engineRoot = path.dirname(path.dirname(auditScript));
271
- const findingsPath = path.join(engineRoot, ".audit", "a11y-findings.json");
272
-
273
- const { findings, metadata } = JSON.parse(fs.readFileSync(findingsPath, "utf-8"));
260
+ ### Using the programmatic API (recommended)
261
+
262
+ The engine exports functions that process scan data directly in memory — no filesystem path resolution needed:
263
+
264
+ ```ts
265
+ import {
266
+ getEnrichedFindings,
267
+ getAuditSummary,
268
+ getPDFReport,
269
+ getHTMLReport,
270
+ getChecklist,
271
+ getRemediationGuide,
272
+ getSourcePatterns,
273
+ } from "@diegovelasquezweb/a11y-engine";
274
+
275
+ // After running audit.mjs via CLI, read the findings file
276
+ const payload = JSON.parse(fs.readFileSync(findingsPath, "utf-8"));
277
+
278
+ // Enrich findings with fix intelligence
279
+ const findings = getEnrichedFindings(payload, {
280
+ screenshotUrlBuilder: (path) => `/api/screenshot?path=${encodeURIComponent(path)}`,
281
+ });
282
+
283
+ // Get full audit summary
284
+ const summary = getAuditSummary(findings, payload);
285
+
286
+ // Generate reports
287
+ const pdf = await getPDFReport(payload, { baseUrl: "https://example.com" });
288
+ const html = await getHTMLReport(payload, { baseUrl: "https://example.com" });
289
+ const checklist = await getChecklist({ baseUrl: "https://example.com" });
290
+ const guide = await getRemediationGuide(payload, { baseUrl: "https://example.com" });
291
+
292
+ // Scan source code patterns
293
+ const patterns = await getSourcePatterns("/path/to/project", { framework: "nextjs" });
274
294
  ```
275
295
 
276
- > Note: `import.meta.url` may be mangled by bundlers (e.g. Next.js). In that case, use `fs.realpathSync` on the known symlink instead:
277
-
278
- ```js
279
- const symlinkBase = path.join(process.cwd(), "node_modules", "@diegovelasquezweb", "a11y-engine");
280
- const engineRoot = fs.realpathSync(symlinkBase);
281
- const findingsPath = path.join(engineRoot, ".audit", "a11y-findings.json");
282
- ```
296
+ See the [README](../README.md#programmatic-api) for full API documentation and type signatures.
283
297
 
284
298
  ### Reading `progress.json` for live UI updates
285
299
 
300
+ During CLI execution, `progress.json` is written to `.audit/` in real-time. This is relevant when using the CLI via `child_process` — the programmatic API does not write progress files.
301
+
286
302
  ```js
287
303
  const progressPath = path.join(engineRoot, ".audit", "progress.json");
288
304
 
289
- // Poll this file during scan execution
290
305
  if (fs.existsSync(progressPath)) {
291
306
  const progress = JSON.parse(fs.readFileSync(progressPath, "utf-8"));
292
307
  console.log(`Current step: ${progress.currentStep}`);
293
- console.log(`axe found: ${progress.steps?.axe?.found ?? "pending"}`);
294
308
  }
295
309
  ```
296
310
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegovelasquezweb/a11y-engine",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "WCAG 2.2 AA accessibility audit engine — scanner, analyzer, and report builders",
5
5
  "type": "module",
6
6
  "license": "MIT",