@a11y-oracle/audit-formatter 1.0.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.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @module selector
3
+ *
4
+ * Utilities for generating CSS selectors and HTML snippets from
5
+ * A11y-Oracle element data structures.
6
+ *
7
+ * Selector priority: `#id` > `tag.class1.class2` > `tag[role="..."]` > `tag`
8
+ */
9
+ import type { A11yFocusedElement } from '@a11y-oracle/core-engine';
10
+ import type { TabOrderEntry } from '@a11y-oracle/focus-analyzer';
11
+ /**
12
+ * Build a CSS selector from an A11yFocusedElement.
13
+ * Priority: `#id` > `tag.class1.class2` > `tag`
14
+ */
15
+ export declare function selectorFromFocusedElement(el: A11yFocusedElement): string;
16
+ /**
17
+ * Build a CSS selector from a TabOrderEntry.
18
+ * Priority: `#id` > `tag[role="..."]` > `tag`
19
+ *
20
+ * TabOrderEntry does not have `className`, so we use `role` as a fallback
21
+ * to produce a more specific selector when no id is present.
22
+ */
23
+ export declare function selectorFromTabOrderEntry(entry: TabOrderEntry): string;
24
+ /**
25
+ * Build a minimal HTML snippet from an A11yFocusedElement.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * htmlSnippetFromFocusedElement(el)
30
+ * // => '<button id="submit" class="btn primary">Submit</button>'
31
+ * ```
32
+ */
33
+ export declare function htmlSnippetFromFocusedElement(el: A11yFocusedElement): string;
34
+ /**
35
+ * Build a minimal HTML snippet from a TabOrderEntry.
36
+ */
37
+ export declare function htmlSnippetFromTabOrderEntry(entry: TabOrderEntry): string;
38
+ //# sourceMappingURL=selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/lib/selector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAsBjE;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,kBAAkB,GACrB,MAAM,CAcR;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,aAAa,GACnB,MAAM,CASR;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,EAAE,EAAE,kBAAkB,GACrB,MAAM,CAWR;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,aAAa,GACnB,MAAM,CASR"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @module selector
3
+ *
4
+ * Utilities for generating CSS selectors and HTML snippets from
5
+ * A11y-Oracle element data structures.
6
+ *
7
+ * Selector priority: `#id` > `tag.class1.class2` > `tag[role="..."]` > `tag`
8
+ */
9
+ /** Escape HTML special characters in attribute values and text content. */
10
+ function escapeHtml(str) {
11
+ return str
12
+ .replace(/&/g, '&amp;')
13
+ .replace(/</g, '&lt;')
14
+ .replace(/>/g, '&gt;')
15
+ .replace(/"/g, '&quot;')
16
+ .replace(/'/g, '&#39;');
17
+ }
18
+ /** Escape characters that break CSS attribute selectors. */
19
+ function escapeCssAttr(str) {
20
+ return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
21
+ }
22
+ /** Escape an ID for use in a CSS `#id` selector. */
23
+ function escapeCssId(id) {
24
+ return id.replace(/([^\w-])/g, '\\$1');
25
+ }
26
+ /**
27
+ * Build a CSS selector from an A11yFocusedElement.
28
+ * Priority: `#id` > `tag.class1.class2` > `tag`
29
+ */
30
+ export function selectorFromFocusedElement(el) {
31
+ if (el.id) {
32
+ return `#${escapeCssId(el.id)}`;
33
+ }
34
+ const tag = el.tag.toLowerCase();
35
+ if (el.className) {
36
+ const classes = el.className
37
+ .trim()
38
+ .split(/\s+/)
39
+ .map((c) => `.${c}`)
40
+ .join('');
41
+ return `${tag}${classes}`;
42
+ }
43
+ return tag;
44
+ }
45
+ /**
46
+ * Build a CSS selector from a TabOrderEntry.
47
+ * Priority: `#id` > `tag[role="..."]` > `tag`
48
+ *
49
+ * TabOrderEntry does not have `className`, so we use `role` as a fallback
50
+ * to produce a more specific selector when no id is present.
51
+ */
52
+ export function selectorFromTabOrderEntry(entry) {
53
+ if (entry.id) {
54
+ return `#${escapeCssId(entry.id)}`;
55
+ }
56
+ const tag = entry.tag.toLowerCase();
57
+ if (entry.role) {
58
+ return `${tag}[role="${escapeCssAttr(entry.role)}"]`;
59
+ }
60
+ return tag;
61
+ }
62
+ /**
63
+ * Build a minimal HTML snippet from an A11yFocusedElement.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * htmlSnippetFromFocusedElement(el)
68
+ * // => '<button id="submit" class="btn primary">Submit</button>'
69
+ * ```
70
+ */
71
+ export function htmlSnippetFromFocusedElement(el) {
72
+ const tag = el.tag.toLowerCase();
73
+ const attrs = [];
74
+ if (el.id)
75
+ attrs.push(`id="${escapeHtml(el.id)}"`);
76
+ if (el.className)
77
+ attrs.push(`class="${escapeHtml(el.className)}"`);
78
+ if (el.role)
79
+ attrs.push(`role="${escapeHtml(el.role)}"`);
80
+ if (el.ariaLabel)
81
+ attrs.push(`aria-label="${escapeHtml(el.ariaLabel)}"`);
82
+ const attrStr = attrs.length > 0 ? ` ${attrs.join(' ')}` : '';
83
+ const content = el.textContent ? escapeHtml(el.textContent.slice(0, 80)) : '';
84
+ return `<${tag}${attrStr}>${content}</${tag}>`;
85
+ }
86
+ /**
87
+ * Build a minimal HTML snippet from a TabOrderEntry.
88
+ */
89
+ export function htmlSnippetFromTabOrderEntry(entry) {
90
+ const tag = entry.tag.toLowerCase();
91
+ const attrs = [];
92
+ if (entry.id)
93
+ attrs.push(`id="${escapeHtml(entry.id)}"`);
94
+ if (entry.role)
95
+ attrs.push(`role="${escapeHtml(entry.role)}"`);
96
+ const attrStr = attrs.length > 0 ? ` ${attrs.join(' ')}` : '';
97
+ const content = entry.textContent ? escapeHtml(entry.textContent.slice(0, 80)) : '';
98
+ return `<${tag}${attrStr}>${content}</${tag}>`;
99
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @module types
3
+ *
4
+ * Axe-core-compatible issue types for A11y-Oracle findings.
5
+ *
6
+ * These types are structurally compatible with Beacon's AxeIssue interface
7
+ * but defined locally so audit-formatter has zero dependencies on Beacon.
8
+ *
9
+ * Fields that Beacon assigns during ingestion (id, testRunId, fingerprint,
10
+ * status, triageNotes) are omitted — the formatter only outputs the issue
11
+ * data itself.
12
+ */
13
+ /** Impact levels matching axe-core severity. */
14
+ export type OracleImpact = 'minor' | 'moderate' | 'serious' | 'critical';
15
+ /**
16
+ * Result type discriminator.
17
+ * - `'violation'` / `'incomplete'` match axe-core conventions.
18
+ * - `'oracle'` identifies findings from A11y-Oracle analysis.
19
+ */
20
+ export type OracleResultType = 'violation' | 'incomplete' | 'oracle';
21
+ /**
22
+ * A single check within an axe-core node result.
23
+ * Simplified for Oracle findings — `data` carries rule-specific context
24
+ * (e.g., `{ contrastRatio: 2.1 }` for focus contrast issues).
25
+ */
26
+ export interface OracleCheck {
27
+ /** Check identifier (typically the ruleId). */
28
+ id: string;
29
+ /** Check-specific data, e.g. `{ contrastRatio: 2.1 }`. */
30
+ data: unknown;
31
+ /** Related DOM nodes (empty for Oracle findings). */
32
+ relatedNodes: unknown[];
33
+ /** Impact level of this check. */
34
+ impact: string;
35
+ /** Human-readable description of the check result. */
36
+ message: string;
37
+ }
38
+ /**
39
+ * A single DOM node result, compatible with axe-core's AxeNode shape.
40
+ */
41
+ export interface OracleNode {
42
+ /** Node-level impact override. */
43
+ impact?: string;
44
+ /** HTML snippet of the problematic element. */
45
+ html: string;
46
+ /** CSS selector path for the element. */
47
+ target: string[];
48
+ /** Checks where any one passing would fix the issue. */
49
+ any: OracleCheck[];
50
+ /** Checks where all must pass. */
51
+ all: OracleCheck[];
52
+ /** Checks where none should be present. */
53
+ none: OracleCheck[];
54
+ /** Human-readable summary of the failure. */
55
+ failureSummary: string;
56
+ }
57
+ /**
58
+ * An accessibility issue in axe-core-compatible format.
59
+ *
60
+ * Structurally compatible with Beacon's `AxeIssue` interface for the
61
+ * fields that the formatter is responsible for producing. Fields assigned
62
+ * by the consumer (id, testRunId, fingerprint, status, triageNotes)
63
+ * are omitted.
64
+ *
65
+ * `resultType` is always `'oracle'` to distinguish from axe-core findings.
66
+ * `ruleId` is prefixed with `oracle/` (e.g., `oracle/focus-not-visible`).
67
+ */
68
+ export interface OracleIssue {
69
+ /** Rule identifier, e.g. `oracle/focus-not-visible`. */
70
+ ruleId: string;
71
+ /** Severity: minor | moderate | serious | critical. */
72
+ impact: OracleImpact;
73
+ /** Human-readable description of what the rule checks. */
74
+ description: string;
75
+ /** Short help text for the issue. */
76
+ help: string;
77
+ /** Summary of the failure for remediation. */
78
+ failureSummary: string;
79
+ /** HTML snippet of the offending element. */
80
+ htmlSnippet: string;
81
+ /** CSS selector targeting the element. */
82
+ selector: string;
83
+ /** Name of the test spec that produced this finding. */
84
+ specName: string;
85
+ /** Project name for issue attribution. */
86
+ project: string;
87
+ /** URL to WCAG documentation. */
88
+ helpUrl: string;
89
+ /** WCAG tags, e.g. `['wcag2a', 'wcag212', 'cat.keyboard', 'oracle']`. */
90
+ tags: string[];
91
+ /** Axe-compatible node results. */
92
+ nodes: OracleNode[];
93
+ /** Result type discriminator. Always `'oracle'` for formatter output. */
94
+ resultType: OracleResultType;
95
+ }
96
+ /**
97
+ * WCAG conformance standard for filtering rules.
98
+ *
99
+ * Values follow axe-core's tag convention (no decimals).
100
+ * Each standard includes all rules from earlier versions at the same
101
+ * or lower level. For example, `'wcag21aa'` includes WCAG 2.0 Level A,
102
+ * WCAG 2.0 Level AA, WCAG 2.1 Level A, and WCAG 2.1 Level AA rules.
103
+ */
104
+ export type WcagLevel = 'wcag2a' | 'wcag2aa' | 'wcag21a' | 'wcag21aa' | 'wcag22a' | 'wcag22aa';
105
+ /**
106
+ * Context required by all formatter functions.
107
+ * Provided by the test runner or OracleAuditor.
108
+ */
109
+ export interface AuditContext {
110
+ /** Project name for issue attribution. */
111
+ project: string;
112
+ /** Name of the spec/test file producing these findings. */
113
+ specName: string;
114
+ /** Target WCAG standard. Default 'wcag22aa' (all rules). */
115
+ wcagLevel?: WcagLevel;
116
+ /** Rule IDs to suppress (e.g. ['oracle/positive-tabindex']). */
117
+ disabledRules?: string[];
118
+ }
119
+ /**
120
+ * Metadata for a single Oracle audit rule.
121
+ */
122
+ export interface OracleRule {
123
+ /** Rule ID, e.g. `oracle/focus-not-visible`. */
124
+ ruleId: string;
125
+ /** Short help text, used as the issue `help` field. */
126
+ help: string;
127
+ /** Longer description, used as the issue `description` field. */
128
+ description: string;
129
+ /** Impact severity. */
130
+ impact: OracleImpact;
131
+ /** WCAG tags. */
132
+ tags: string[];
133
+ /** URL to WCAG criterion documentation. */
134
+ helpUrl: string;
135
+ /** Failure summary template. */
136
+ failureSummary: string;
137
+ }
138
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,gDAAgD;AAChD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,0DAA0D;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,qDAAqD;IACrD,YAAY,EAAE,OAAO,EAAE,CAAC;IACxB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wDAAwD;IACxD,GAAG,EAAE,WAAW,EAAE,CAAC;IACnB,kCAAkC;IAClC,GAAG,EAAE,WAAW,EAAE,CAAC;IACnB,2CAA2C;IAC3C,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,MAAM,EAAE,YAAY,CAAC;IACrB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,mCAAmC;IACnC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,yEAAyE;IACzE,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,SAAS,GACT,SAAS,GACT,UAAU,GACV,SAAS,GACT,UAAU,CAAC;AAEf;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,iBAAiB;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;CACxB"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @module types
3
+ *
4
+ * Axe-core-compatible issue types for A11y-Oracle findings.
5
+ *
6
+ * These types are structurally compatible with Beacon's AxeIssue interface
7
+ * but defined locally so audit-formatter has zero dependencies on Beacon.
8
+ *
9
+ * Fields that Beacon assigns during ingestion (id, testRunId, fingerprint,
10
+ * status, triageNotes) are omitted — the formatter only outputs the issue
11
+ * data itself.
12
+ */
13
+ export {};
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@a11y-oracle/audit-formatter",
3
+ "version": "1.0.0",
4
+ "description": "Converts accessibility findings to axe-core-compatible OracleIssue objects with WCAG rule metadata",
5
+ "license": "MIT",
6
+ "author": "a11y-oracle",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/a11y-oracle/a11y-oracle.git",
10
+ "directory": "libs/audit-formatter"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/a11y-oracle/a11y-oracle/issues"
14
+ },
15
+ "homepage": "https://github.com/a11y-oracle/a11y-oracle/tree/main/libs/audit-formatter",
16
+ "keywords": [
17
+ "accessibility",
18
+ "a11y",
19
+ "audit",
20
+ "wcag",
21
+ "axe-core",
22
+ "testing",
23
+ "formatter"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "type": "module",
29
+ "main": "./dist/index.js",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ "./package.json": "./package.json",
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "import": "./dist/index.js",
37
+ "default": "./dist/index.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "!**/*.tsbuildinfo"
43
+ ],
44
+ "dependencies": {
45
+ "@a11y-oracle/core-engine": "1.0.0",
46
+ "@a11y-oracle/focus-analyzer": "1.0.0",
47
+ "tslib": "^2.3.0"
48
+ }
49
+ }