@diegovelasquezweb/a11y-engine 0.8.4 → 0.9.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @diegovelasquezweb/a11y-engine
1
+ # a11y Engine
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@diegovelasquezweb/a11y-engine)](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 |
18
19
 
19
20
  ## Installation
20
21
 
@@ -40,12 +41,6 @@ import {
40
41
  getChecklist,
41
42
  getRemediationGuide,
42
43
  getSourcePatterns,
43
- getScannerHelp,
44
- getPersonaReference,
45
- getUiHelp,
46
- getConformanceLevels,
47
- getWcagPrinciples,
48
- getSeverityLevels,
49
44
  getKnowledge,
50
45
  } from "@diegovelasquezweb/a11y-engine";
51
46
  ```
@@ -121,19 +116,21 @@ These functions render final artifacts from scan payload data.
121
116
 
122
117
  ### Knowledge API
123
118
 
124
- These functions expose scanner help content, persona explanations, conformance levels, and UI copy so frontends or agents can render guidance from engine-owned data.
119
+ Returns all accessibility knowledge in a single call: scanner help, persona profiles, concepts, glossary, documentation, conformance levels, WCAG principles, and severity definitions.
125
120
 
126
- | Function | Returns | Description |
127
- | :--- | :--- | :--- |
128
- | `getScannerHelp(options?)` | `{ locale, version, title, engines, options }` | Scanner option and engine help metadata |
129
- | `getPersonaReference(options?)` | `{ locale, version, personas }` | Persona labels, descriptions, and mapping hints |
130
- | `getUiHelp(options?)` | `{ locale, version, concepts, glossary }` | Shared concept definitions and glossary entries |
131
- | `getConformanceLevels(options?)` | `{ locale, version, conformanceLevels }` | WCAG conformance level definitions with axe tag mappings |
132
- | `getWcagPrinciples(options?)` | `{ locale, version, wcagPrinciples }` | The four WCAG principles with criterion prefix patterns |
133
- | `getSeverityLevels(options?)` | `{ locale, version, severityLevels }` | Severity level definitions with labels and ordering |
134
- | `getKnowledge(options?)` | Full knowledge pack | Combines all knowledge APIs into a single response for UI or agent flows |
135
-
136
- See [API Reference](docs/api-reference.md) for exact options and return types.
121
+ ```ts
122
+ const knowledge = getKnowledge({ locale: "en" });
123
+ // knowledge.scanner → engine and option descriptions
124
+ // knowledge.personas persona labels and descriptions
125
+ // knowledge.concepts concept definitions
126
+ // knowledge.glossary → accessibility glossary
127
+ // knowledge.docs → documentation articles
128
+ // knowledge.conformanceLevels A/AA/AAA with axe tag mappings
129
+ // knowledge.wcagPrinciples → the four WCAG principles
130
+ // knowledge.severityLevels → Critical/Serious/Moderate/Minor definitions
131
+ ```
132
+
133
+ See [API Reference](docs/api-reference.md) for the full `EngineKnowledge` shape.
137
134
 
138
135
  ## CLI
139
136
 
@@ -353,95 +353,28 @@ Returns: `Promise<SourcePatternResult>`
353
353
 
354
354
  ## Knowledge API
355
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
356
  ### `getKnowledge(options?)`
359
357
 
360
- Returns the full knowledge pack combines all knowledge functions into one call. Useful for pre-loading UI help content.
358
+ Returns all accessibility knowledge in a single call. Accepts an optional `{ locale?: string }` option (default: `"en"`).
361
359
 
362
360
  ```ts
363
361
  import { getKnowledge } from "@diegovelasquezweb/a11y-engine";
364
362
 
365
363
  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
-
380
- ### `getScannerHelp(options?)`
381
-
382
- Returns scan option descriptions, allowed values, and engine metadata — used to render Advanced Settings UI.
383
-
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
- ---
391
-
392
- ### `getPersonaReference(options?)`
393
-
394
- Returns persona labels, descriptions, and disability group definitions.
395
-
396
- ```ts
397
- const ref = getPersonaReference();
398
- // ref.personas → [{ id: "screenReader", label: "Screen Readers", icon: "...", description: "..." }, ...]
399
- ```
400
-
401
- ---
402
-
403
- ### `getUiHelp(options?)`
404
-
405
- Returns shared concept definitions and a glossary of accessibility terms.
406
-
407
- ```ts
408
- const ui = getUiHelp();
409
- // ui.concepts → { wcag: "...", aria: "...", ... }
410
- // ui.glossary → [{ term: "ARIA", definition: "..." }, ...]
411
- ```
412
-
413
- ---
414
-
415
- ### `getConformanceLevels(options?)`
416
-
417
- Returns WCAG conformance level definitions with their corresponding axe-core tag sets.
418
-
419
- ```ts
420
- const { conformanceLevels } = getConformanceLevels();
421
- // conformanceLevels[0] → { id: "AA", label: "WCAG 2.2 AA", axeTags: ["wcag2a", "wcag2aa", ...] }
422
- ```
423
-
424
- ---
425
-
426
- ### `getWcagPrinciples(options?)`
427
-
428
- Returns the four WCAG principles (Perceivable, Operable, Understandable, Robust) with criterion prefix patterns.
429
-
430
- ```ts
431
- const { wcagPrinciples } = getWcagPrinciples();
432
- // wcagPrinciples[0] → { id: "perceivable", label: "Perceivable", prefix: "1.", description: "..." }
433
364
  ```
434
365
 
435
- ---
436
-
437
- ### `getSeverityLevels(options?)`
438
-
439
- Returns severity level definitions with labels, descriptions, and ordering.
440
-
441
- ```ts
442
- const { severityLevels } = getSeverityLevels();
443
- // severityLevels[0] { id: "Critical", label: "Critical", order: 0, description: "..." }
444
- ```
366
+ **Returns:** `EngineKnowledge`
367
+
368
+ | Field | Type | Description |
369
+ | :--- | :--- | :--- |
370
+ | `scanner` | `{ title, engines, options }` | Scan option descriptions, allowed values, and engine metadata |
371
+ | `personas` | `PersonaReferenceItem[]` | Persona labels, icons, descriptions, and mapped rules |
372
+ | `concepts` | `Record<string, ConceptEntry>` | Concept definitions with title, body, and context |
373
+ | `glossary` | `GlossaryEntry[]` | Accessibility term definitions |
374
+ | `docs` | `KnowledgeDocs` | Documentation articles organized by section and group |
375
+ | `conformanceLevels` | `ConformanceLevel[]` | WCAG A/AA/AAA definitions with axe-core tag mappings |
376
+ | `wcagPrinciples` | `WcagPrinciple[]` | The four WCAG principles with criterion prefix patterns |
377
+ | `severityLevels` | `SeverityLevel[]` | Critical/Serious/Moderate/Minor definitions with ordering |
445
378
 
446
379
  ---
447
380
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegovelasquezweb/a11y-engine",
3
- "version": "0.8.4",
3
+ "version": "0.9.0",
4
4
  "description": "WCAG 2.2 accessibility audit engine — scanner, analyzer, and report builders",
5
5
  "type": "module",
6
6
  "license": "MIT",
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 { /* non-fatal */ }
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); // non-fatal — never block the pipeline
77
+ process.exit(0);
78
78
  });
79
79
  }
package/src/index.d.mts CHANGED
@@ -449,18 +449,6 @@ export function getSourcePatterns(
449
449
  options?: SourcePatternOptions
450
450
  ): Promise<SourcePatternResult>;
451
451
 
452
- export function getScannerHelp(options?: KnowledgeOptions): ScannerHelp;
453
-
454
- export function getPersonaReference(options?: KnowledgeOptions): PersonaReference;
455
-
456
- export function getUiHelp(options?: KnowledgeOptions): UiHelp;
457
-
458
- export function getConformanceLevels(options?: KnowledgeOptions): ConformanceLevelsResult;
459
-
460
- export function getWcagPrinciples(options?: KnowledgeOptions): WcagPrinciplesResult;
461
-
462
- export function getSeverityLevels(options?: KnowledgeOptions): SeverityLevelsResult;
463
-
464
452
  export function getKnowledge(options?: KnowledgeOptions): EngineKnowledge;
465
453
 
466
454
  export const DEFAULT_AI_SYSTEM_PROMPT: string;
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,
@@ -455,7 +434,7 @@ export function getOverview(findings, payload = null) {
455
434
  * @param {{ locale?: string }} [options={}]
456
435
  * @returns {{ locale: string, version: string, title: string, engines: object[], options: object[] }}
457
436
  */
458
- export function getScannerHelp(options = {}) {
437
+ function getScannerHelp(options = {}) {
459
438
  const locale = resolveKnowledgeLocale(options.locale || "en");
460
439
  const payload = getKnowledgeData();
461
440
  const scanner = payload.locales[locale]?.scanner || { title: "Scanner Help", engines: [], options: [] };
@@ -476,7 +455,7 @@ export function getScannerHelp(options = {}) {
476
455
  * @param {{ locale?: string }} [options={}]
477
456
  * @returns {{ locale: string, version: string, personas: object[] }}
478
457
  */
479
- export function getPersonaReference(options = {}) {
458
+ function getPersonaReference(options = {}) {
480
459
  const locale = resolveKnowledgeLocale(options.locale || "en");
481
460
  const payload = getKnowledgeData();
482
461
  const wcagRef = getWcagReference();
@@ -513,7 +492,7 @@ export function getPersonaReference(options = {}) {
513
492
  * @param {{ locale?: string }} [options={}]
514
493
  * @returns {{ locale: string, version: string, tooltips: Record<string, object>, glossary: object[] }}
515
494
  */
516
- export function getUiHelp(options = {}) {
495
+ function getConceptsAndGlossary(options = {}) {
517
496
  const locale = resolveKnowledgeLocale(options.locale || "en");
518
497
  const payload = getKnowledgeData();
519
498
  const localePayload = payload.locales[locale] || {};
@@ -539,7 +518,7 @@ export function getUiHelp(options = {}) {
539
518
  * @param {{ locale?: string }} [options={}]
540
519
  * @returns {{ locale: string, version: string, conformanceLevels: object[] }}
541
520
  */
542
- export function getConformanceLevels(options = {}) {
521
+ function getConformanceLevels(options = {}) {
543
522
  const locale = resolveKnowledgeLocale(options.locale || "en");
544
523
  const payload = getKnowledgeData();
545
524
  const levels = payload.locales[locale]?.conformanceLevels || [];
@@ -556,7 +535,7 @@ export function getConformanceLevels(options = {}) {
556
535
  * @param {{ locale?: string }} [options={}]
557
536
  * @returns {{ locale: string, version: string, wcagPrinciples: object[] }}
558
537
  */
559
- export function getWcagPrinciples(options = {}) {
538
+ function getWcagPrinciples(options = {}) {
560
539
  const locale = resolveKnowledgeLocale(options.locale || "en");
561
540
  const payload = getKnowledgeData();
562
541
  const principles = payload.locales[locale]?.wcagPrinciples || [];
@@ -573,7 +552,7 @@ export function getWcagPrinciples(options = {}) {
573
552
  * @param {{ locale?: string }} [options={}]
574
553
  * @returns {{ locale: string, version: string, severityLevels: object[] }}
575
554
  */
576
- export function getSeverityLevels(options = {}) {
555
+ function getSeverityLevels(options = {}) {
577
556
  const locale = resolveKnowledgeLocale(options.locale || "en");
578
557
  const payload = getKnowledgeData();
579
558
  const levels = payload.locales[locale]?.severityLevels || [];
@@ -594,7 +573,7 @@ export const VIEWPORT_PRESETS = [
594
573
  export function getKnowledge(options = {}) {
595
574
  const scanner = getScannerHelp(options);
596
575
  const personas = getPersonaReference(options);
597
- const ui = getUiHelp(options);
576
+ const ui = getConceptsAndGlossary(options);
598
577
  const conformance = getConformanceLevels(options);
599
578
  const principles = getWcagPrinciples(options);
600
579
  const severity = getSeverityLevels(options);
@@ -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 — check via canonical rule ID (axe-equivalent) + selector
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
- // If pa11y was mapped to an axe rule ID, check if that rule already covers this target
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);