ally-a11y 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.
Files changed (84) hide show
  1. package/ACCESSIBILITY.md +205 -0
  2. package/LICENSE +21 -0
  3. package/README.md +940 -0
  4. package/dist/cli.d.ts +7 -0
  5. package/dist/cli.js +528 -0
  6. package/dist/commands/audit-palette.d.ts +18 -0
  7. package/dist/commands/audit-palette.js +613 -0
  8. package/dist/commands/auto-pr.d.ts +19 -0
  9. package/dist/commands/auto-pr.js +434 -0
  10. package/dist/commands/badge.d.ts +11 -0
  11. package/dist/commands/badge.js +143 -0
  12. package/dist/commands/completion.d.ts +4 -0
  13. package/dist/commands/completion.js +185 -0
  14. package/dist/commands/crawl.d.ts +12 -0
  15. package/dist/commands/crawl.js +249 -0
  16. package/dist/commands/doctor.d.ts +5 -0
  17. package/dist/commands/doctor.js +233 -0
  18. package/dist/commands/explain.d.ts +12 -0
  19. package/dist/commands/explain.js +233 -0
  20. package/dist/commands/fix.d.ts +13 -0
  21. package/dist/commands/fix.js +668 -0
  22. package/dist/commands/health.d.ts +11 -0
  23. package/dist/commands/health.js +367 -0
  24. package/dist/commands/history.d.ts +10 -0
  25. package/dist/commands/history.js +191 -0
  26. package/dist/commands/init.d.ts +9 -0
  27. package/dist/commands/init.js +164 -0
  28. package/dist/commands/learn.d.ts +8 -0
  29. package/dist/commands/learn.js +592 -0
  30. package/dist/commands/pr-check.d.ts +12 -0
  31. package/dist/commands/pr-check.js +270 -0
  32. package/dist/commands/report.d.ts +11 -0
  33. package/dist/commands/report.js +375 -0
  34. package/dist/commands/scan-storybook.d.ts +18 -0
  35. package/dist/commands/scan-storybook.js +402 -0
  36. package/dist/commands/scan.d.ts +25 -0
  37. package/dist/commands/scan.js +673 -0
  38. package/dist/commands/stats.d.ts +5 -0
  39. package/dist/commands/stats.js +137 -0
  40. package/dist/commands/tree.d.ts +12 -0
  41. package/dist/commands/tree.js +635 -0
  42. package/dist/commands/triage.d.ts +13 -0
  43. package/dist/commands/triage.js +327 -0
  44. package/dist/commands/watch.d.ts +17 -0
  45. package/dist/commands/watch.js +302 -0
  46. package/dist/types/index.d.ts +60 -0
  47. package/dist/types/index.js +4 -0
  48. package/dist/utils/baseline.d.ts +62 -0
  49. package/dist/utils/baseline.js +169 -0
  50. package/dist/utils/browser.d.ts +78 -0
  51. package/dist/utils/browser.js +239 -0
  52. package/dist/utils/cache.d.ts +76 -0
  53. package/dist/utils/cache.js +178 -0
  54. package/dist/utils/config.d.ts +102 -0
  55. package/dist/utils/config.js +237 -0
  56. package/dist/utils/converters.d.ts +77 -0
  57. package/dist/utils/converters.js +200 -0
  58. package/dist/utils/copilot.d.ts +36 -0
  59. package/dist/utils/copilot.js +139 -0
  60. package/dist/utils/detect.d.ts +22 -0
  61. package/dist/utils/detect.js +197 -0
  62. package/dist/utils/enhanced-errors.d.ts +46 -0
  63. package/dist/utils/enhanced-errors.js +295 -0
  64. package/dist/utils/errors.d.ts +31 -0
  65. package/dist/utils/errors.js +149 -0
  66. package/dist/utils/fix-patterns.d.ts +56 -0
  67. package/dist/utils/fix-patterns.js +529 -0
  68. package/dist/utils/history-tracking.d.ts +94 -0
  69. package/dist/utils/history-tracking.js +230 -0
  70. package/dist/utils/history.d.ts +42 -0
  71. package/dist/utils/history.js +255 -0
  72. package/dist/utils/impact-scores.d.ts +44 -0
  73. package/dist/utils/impact-scores.js +257 -0
  74. package/dist/utils/retry.d.ts +24 -0
  75. package/dist/utils/retry.js +76 -0
  76. package/dist/utils/scanner.d.ts +74 -0
  77. package/dist/utils/scanner.js +606 -0
  78. package/dist/utils/scanner.test.d.ts +4 -0
  79. package/dist/utils/scanner.test.js +162 -0
  80. package/dist/utils/ui.d.ts +44 -0
  81. package/dist/utils/ui.js +276 -0
  82. package/mcp-server/dist/index.d.ts +8 -0
  83. package/mcp-server/dist/index.js +1923 -0
  84. package/package.json +88 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Caching utilities for incremental scans
3
+ *
4
+ * Caches scan results by file hash to avoid re-scanning unchanged files.
5
+ */
6
+ import { createHash } from 'crypto';
7
+ import { readFile, writeFile, mkdir, stat } from 'fs/promises';
8
+ import { existsSync } from 'fs';
9
+ import { resolve, dirname, relative } from 'path';
10
+ const CACHE_VERSION = 1;
11
+ const CACHE_DIR = '.ally/cache';
12
+ const CACHE_INDEX_FILE = '.ally/cache/index.json';
13
+ /**
14
+ * Compute SHA-256 hash of file content
15
+ */
16
+ export async function hashFile(filePath) {
17
+ const content = await readFile(filePath);
18
+ return createHash('sha256').update(content).digest('hex');
19
+ }
20
+ /**
21
+ * Compute hash of content string
22
+ */
23
+ export function hashContent(content) {
24
+ return createHash('sha256').update(content).digest('hex');
25
+ }
26
+ /**
27
+ * Load cache index from disk
28
+ */
29
+ export async function loadCacheIndex() {
30
+ const indexPath = resolve(CACHE_INDEX_FILE);
31
+ if (!existsSync(indexPath)) {
32
+ return {
33
+ version: CACHE_VERSION,
34
+ entries: {},
35
+ updatedAt: Date.now(),
36
+ };
37
+ }
38
+ try {
39
+ const content = await readFile(indexPath, 'utf-8');
40
+ const index = JSON.parse(content);
41
+ // Check version compatibility
42
+ if (index.version !== CACHE_VERSION) {
43
+ return {
44
+ version: CACHE_VERSION,
45
+ entries: {},
46
+ updatedAt: Date.now(),
47
+ };
48
+ }
49
+ return index;
50
+ }
51
+ catch {
52
+ return {
53
+ version: CACHE_VERSION,
54
+ entries: {},
55
+ updatedAt: Date.now(),
56
+ };
57
+ }
58
+ }
59
+ /**
60
+ * Save cache index to disk
61
+ */
62
+ export async function saveCacheIndex(index) {
63
+ const indexPath = resolve(CACHE_INDEX_FILE);
64
+ const cacheDir = dirname(indexPath);
65
+ if (!existsSync(cacheDir)) {
66
+ await mkdir(cacheDir, { recursive: true });
67
+ }
68
+ index.updatedAt = Date.now();
69
+ await writeFile(indexPath, JSON.stringify(index, null, 2));
70
+ }
71
+ /**
72
+ * Get cached result for a file if valid
73
+ */
74
+ export async function getCachedResult(filePath, standard) {
75
+ const index = await loadCacheIndex();
76
+ const relativePath = relative(process.cwd(), filePath);
77
+ const entry = index.entries[relativePath];
78
+ if (!entry) {
79
+ return null;
80
+ }
81
+ // Check if standard matches
82
+ if (entry.standard !== standard) {
83
+ return null;
84
+ }
85
+ try {
86
+ // Check if file has been modified
87
+ const stats = await stat(filePath);
88
+ const mtime = stats.mtimeMs;
89
+ if (mtime !== entry.mtime) {
90
+ // File modified, verify with hash
91
+ const hash = await hashFile(filePath);
92
+ if (hash !== entry.hash) {
93
+ return null;
94
+ }
95
+ // Hash matches, update mtime in cache
96
+ entry.mtime = mtime;
97
+ await saveCacheIndex(index);
98
+ }
99
+ return entry.result;
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ /**
106
+ * Cache a scan result for a file
107
+ */
108
+ export async function cacheResult(filePath, result, standard) {
109
+ const index = await loadCacheIndex();
110
+ const relativePath = relative(process.cwd(), filePath);
111
+ try {
112
+ const stats = await stat(filePath);
113
+ const hash = await hashFile(filePath);
114
+ index.entries[relativePath] = {
115
+ path: relativePath,
116
+ hash,
117
+ mtime: stats.mtimeMs,
118
+ result,
119
+ standard,
120
+ cachedAt: Date.now(),
121
+ };
122
+ await saveCacheIndex(index);
123
+ }
124
+ catch {
125
+ // Silently fail if caching fails
126
+ }
127
+ }
128
+ /**
129
+ * Clear all cached results
130
+ */
131
+ export async function clearCache() {
132
+ const index = {
133
+ version: CACHE_VERSION,
134
+ entries: {},
135
+ updatedAt: Date.now(),
136
+ };
137
+ await saveCacheIndex(index);
138
+ }
139
+ /**
140
+ * Get cache statistics
141
+ */
142
+ export async function getCacheStats() {
143
+ const index = await loadCacheIndex();
144
+ const entries = Object.values(index.entries);
145
+ if (entries.length === 0) {
146
+ return {
147
+ entries: 0,
148
+ size: 0,
149
+ oldestEntry: null,
150
+ newestEntry: null,
151
+ };
152
+ }
153
+ const timestamps = entries.map((e) => e.cachedAt);
154
+ return {
155
+ entries: entries.length,
156
+ size: JSON.stringify(index).length,
157
+ oldestEntry: Math.min(...timestamps),
158
+ newestEntry: Math.max(...timestamps),
159
+ };
160
+ }
161
+ /**
162
+ * Remove stale cache entries (files that no longer exist)
163
+ */
164
+ export async function pruneCache() {
165
+ const index = await loadCacheIndex();
166
+ let removed = 0;
167
+ for (const path of Object.keys(index.entries)) {
168
+ const fullPath = resolve(path);
169
+ if (!existsSync(fullPath)) {
170
+ delete index.entries[path];
171
+ removed++;
172
+ }
173
+ }
174
+ if (removed > 0) {
175
+ await saveCacheIndex(index);
176
+ }
177
+ return removed;
178
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Configuration file support for Ally A11y CLI
3
+ *
4
+ * Supports loading config from:
5
+ * - .allyrc.json
6
+ * - .allyrc (JSON format)
7
+ * - ally.config.js (CommonJS module)
8
+ *
9
+ * Searches current directory and parent directories (like eslint)
10
+ */
11
+ import type { Severity } from '../types/index.js';
12
+ import type { WcagStandard } from './scanner.js';
13
+ /**
14
+ * Configuration options for the scan command
15
+ */
16
+ export interface ScanConfig {
17
+ /** Paths to scan (relative to config file or cwd) */
18
+ paths?: string[];
19
+ /** Glob patterns to ignore */
20
+ ignore?: string[];
21
+ /** Violation threshold for CI (exit with error if exceeded) */
22
+ threshold?: number;
23
+ /** WCAG standard to test against */
24
+ standard?: WcagStandard;
25
+ /** Only fail on these severity levels */
26
+ failOn?: Severity[];
27
+ }
28
+ /**
29
+ * Configuration options for report generation
30
+ */
31
+ export interface ReportConfig {
32
+ /** Output format: json, sarif, markdown, html */
33
+ format?: string;
34
+ /** Output directory or file path */
35
+ output?: string;
36
+ }
37
+ /**
38
+ * Configuration options for the fix command
39
+ */
40
+ export interface FixConfig {
41
+ /** Show what would be fixed without making changes */
42
+ dryRun?: boolean;
43
+ /** Auto-approve fixes for these violation types */
44
+ autoApprove?: string[];
45
+ }
46
+ /**
47
+ * Complete Ally configuration
48
+ */
49
+ export interface AllyConfig {
50
+ scan?: ScanConfig;
51
+ report?: ReportConfig;
52
+ fix?: FixConfig;
53
+ }
54
+ /** Result from loading config */
55
+ export interface ConfigResult {
56
+ config: AllyConfig;
57
+ configPath: string | null;
58
+ }
59
+ /**
60
+ * Load configuration from .allyrc.json or similar config files
61
+ *
62
+ * Searches current directory and parent directories for config files.
63
+ * Returns an empty config if no file is found.
64
+ *
65
+ * @param startDir - Directory to start searching from (defaults to cwd)
66
+ * @returns Loaded configuration and path to config file (if found)
67
+ */
68
+ export declare function loadConfig(startDir?: string): Promise<ConfigResult>;
69
+ /**
70
+ * Get the path to the config file that would be used
71
+ *
72
+ * Useful for debugging to see which config file is being loaded.
73
+ *
74
+ * @param startDir - Directory to start searching from (defaults to cwd)
75
+ * @returns Path to config file or null if none found
76
+ */
77
+ export declare function getConfigPath(startDir?: string): Promise<string | null>;
78
+ /**
79
+ * Merge CLI options with config file options
80
+ *
81
+ * CLI options take precedence over config file options.
82
+ *
83
+ * @param cliOptions - Options from command line
84
+ * @param config - Options from config file
85
+ * @returns Merged options
86
+ */
87
+ export declare function mergeOptions<T extends Record<string, unknown>>(cliOptions: T, configOptions: Partial<T>): T;
88
+ /** Result from loading ignore patterns */
89
+ export interface IgnoreResult {
90
+ patterns: string[];
91
+ ignorePath: string | null;
92
+ }
93
+ /**
94
+ * Load ignore patterns from .allyignore file
95
+ *
96
+ * Searches current directory and parent directories for ignore file.
97
+ * Returns empty patterns array if no file is found.
98
+ *
99
+ * @param startDir - Directory to start searching from (defaults to cwd)
100
+ * @returns Loaded ignore patterns and path to ignore file (if found)
101
+ */
102
+ export declare function loadIgnorePatterns(startDir?: string): Promise<IgnoreResult>;
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Configuration file support for Ally A11y CLI
3
+ *
4
+ * Supports loading config from:
5
+ * - .allyrc.json
6
+ * - .allyrc (JSON format)
7
+ * - ally.config.js (CommonJS module)
8
+ *
9
+ * Searches current directory and parent directories (like eslint)
10
+ */
11
+ import { existsSync } from 'fs';
12
+ import { readFile } from 'fs/promises';
13
+ import { resolve, dirname, join } from 'path';
14
+ /** Ignore file names to search for (in order of priority) */
15
+ const IGNORE_FILES = ['.allyignore', '.ally-ignore'];
16
+ /** Config file names to search for (in order of priority) */
17
+ const CONFIG_FILES = [
18
+ '.allyrc.json',
19
+ '.allyrc',
20
+ 'ally.config.js',
21
+ 'ally.config.cjs',
22
+ ];
23
+ /**
24
+ * Search for config file starting from directory and moving up to root
25
+ */
26
+ async function findConfigFile(startDir) {
27
+ let currentDir = resolve(startDir);
28
+ const root = dirname(currentDir);
29
+ // Keep searching until we hit the root
30
+ while (currentDir !== root) {
31
+ for (const configFile of CONFIG_FILES) {
32
+ const configPath = join(currentDir, configFile);
33
+ if (existsSync(configPath)) {
34
+ return configPath;
35
+ }
36
+ }
37
+ currentDir = dirname(currentDir);
38
+ }
39
+ // Check root as well
40
+ for (const configFile of CONFIG_FILES) {
41
+ const configPath = join(currentDir, configFile);
42
+ if (existsSync(configPath)) {
43
+ return configPath;
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Load and parse a config file
50
+ */
51
+ async function parseConfigFile(configPath) {
52
+ const ext = configPath.split('.').pop();
53
+ if (ext === 'js' || ext === 'cjs') {
54
+ // Load JavaScript config (CommonJS)
55
+ try {
56
+ const configModule = await import(configPath);
57
+ return configModule.default || configModule;
58
+ }
59
+ catch (error) {
60
+ throw new Error(`Failed to load config from ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
61
+ }
62
+ }
63
+ // Load JSON config
64
+ try {
65
+ const content = await readFile(configPath, 'utf-8');
66
+ return JSON.parse(content);
67
+ }
68
+ catch (error) {
69
+ if (error instanceof SyntaxError) {
70
+ throw new Error(`Invalid JSON in config file ${configPath}: ${error.message}`);
71
+ }
72
+ throw new Error(`Failed to read config file ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
73
+ }
74
+ }
75
+ /**
76
+ * Validate config structure and values
77
+ */
78
+ function validateConfig(config, configPath) {
79
+ const validStandards = [
80
+ 'wcag2a', 'wcag2aa', 'wcag2aaa',
81
+ 'wcag21a', 'wcag21aa', 'wcag21aaa',
82
+ 'wcag22aa', 'section508', 'best-practice'
83
+ ];
84
+ const validSeverities = ['critical', 'serious', 'moderate', 'minor'];
85
+ if (config.scan?.standard && !validStandards.includes(config.scan.standard)) {
86
+ throw new Error(`Invalid standard "${config.scan.standard}" in ${configPath}. ` +
87
+ `Valid options: ${validStandards.join(', ')}`);
88
+ }
89
+ if (config.scan?.failOn) {
90
+ for (const severity of config.scan.failOn) {
91
+ if (!validSeverities.includes(severity)) {
92
+ throw new Error(`Invalid severity "${severity}" in failOn in ${configPath}. ` +
93
+ `Valid options: ${validSeverities.join(', ')}`);
94
+ }
95
+ }
96
+ }
97
+ if (config.scan?.threshold !== undefined) {
98
+ if (typeof config.scan.threshold !== 'number' || config.scan.threshold < 0) {
99
+ throw new Error(`Invalid threshold "${config.scan.threshold}" in ${configPath}. ` +
100
+ `Must be a non-negative number.`);
101
+ }
102
+ }
103
+ const validFormats = ['json', 'sarif', 'markdown', 'html'];
104
+ if (config.report?.format && !validFormats.includes(config.report.format)) {
105
+ throw new Error(`Invalid report format "${config.report.format}" in ${configPath}. ` +
106
+ `Valid options: ${validFormats.join(', ')}`);
107
+ }
108
+ }
109
+ /**
110
+ * Load configuration from .allyrc.json or similar config files
111
+ *
112
+ * Searches current directory and parent directories for config files.
113
+ * Returns an empty config if no file is found.
114
+ *
115
+ * @param startDir - Directory to start searching from (defaults to cwd)
116
+ * @returns Loaded configuration and path to config file (if found)
117
+ */
118
+ export async function loadConfig(startDir = process.cwd()) {
119
+ try {
120
+ const configPath = await findConfigFile(startDir);
121
+ if (!configPath) {
122
+ return { config: {}, configPath: null };
123
+ }
124
+ const config = await parseConfigFile(configPath);
125
+ validateConfig(config, configPath);
126
+ return { config, configPath };
127
+ }
128
+ catch (error) {
129
+ // Re-throw validation/parsing errors
130
+ if (error instanceof Error && error.message.includes('config')) {
131
+ throw error;
132
+ }
133
+ // Wrap other errors
134
+ throw new Error(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
135
+ }
136
+ }
137
+ /**
138
+ * Get the path to the config file that would be used
139
+ *
140
+ * Useful for debugging to see which config file is being loaded.
141
+ *
142
+ * @param startDir - Directory to start searching from (defaults to cwd)
143
+ * @returns Path to config file or null if none found
144
+ */
145
+ export async function getConfigPath(startDir = process.cwd()) {
146
+ return findConfigFile(startDir);
147
+ }
148
+ /**
149
+ * Merge CLI options with config file options
150
+ *
151
+ * CLI options take precedence over config file options.
152
+ *
153
+ * @param cliOptions - Options from command line
154
+ * @param config - Options from config file
155
+ * @returns Merged options
156
+ */
157
+ export function mergeOptions(cliOptions, configOptions) {
158
+ const merged = { ...configOptions };
159
+ // CLI options override config options (only if explicitly set)
160
+ for (const [key, value] of Object.entries(cliOptions)) {
161
+ if (value !== undefined) {
162
+ merged[key] = value;
163
+ }
164
+ }
165
+ return merged;
166
+ }
167
+ /**
168
+ * Search for .allyignore file starting from directory and moving up to root
169
+ */
170
+ async function findIgnoreFile(startDir) {
171
+ let currentDir = resolve(startDir);
172
+ const root = dirname(currentDir);
173
+ while (currentDir !== root) {
174
+ for (const ignoreFile of IGNORE_FILES) {
175
+ const ignorePath = join(currentDir, ignoreFile);
176
+ if (existsSync(ignorePath)) {
177
+ return ignorePath;
178
+ }
179
+ }
180
+ currentDir = dirname(currentDir);
181
+ }
182
+ // Check root as well
183
+ for (const ignoreFile of IGNORE_FILES) {
184
+ const ignorePath = join(currentDir, ignoreFile);
185
+ if (existsSync(ignorePath)) {
186
+ return ignorePath;
187
+ }
188
+ }
189
+ return null;
190
+ }
191
+ /**
192
+ * Parse ignore file content into glob patterns
193
+ *
194
+ * Supports glob patterns, directory patterns (ending with /),
195
+ * comments (lines starting with #), and blank lines.
196
+ */
197
+ function parseIgnoreFile(content) {
198
+ return content
199
+ .split('\n')
200
+ .map((line) => line.trim())
201
+ .filter((line) => line && !line.startsWith('#'))
202
+ .map((pattern) => {
203
+ // Convert directory patterns to glob patterns
204
+ if (pattern.endsWith('/')) {
205
+ return `**/${pattern}**`;
206
+ }
207
+ // Ensure patterns without slashes match anywhere
208
+ if (!pattern.includes('/') && !pattern.startsWith('**/')) {
209
+ return `**/${pattern}`;
210
+ }
211
+ return pattern;
212
+ });
213
+ }
214
+ /**
215
+ * Load ignore patterns from .allyignore file
216
+ *
217
+ * Searches current directory and parent directories for ignore file.
218
+ * Returns empty patterns array if no file is found.
219
+ *
220
+ * @param startDir - Directory to start searching from (defaults to cwd)
221
+ * @returns Loaded ignore patterns and path to ignore file (if found)
222
+ */
223
+ export async function loadIgnorePatterns(startDir = process.cwd()) {
224
+ const ignorePath = await findIgnoreFile(startDir);
225
+ if (!ignorePath) {
226
+ return { patterns: [], ignorePath: null };
227
+ }
228
+ try {
229
+ const content = await readFile(ignorePath, 'utf-8');
230
+ const patterns = parseIgnoreFile(content);
231
+ return { patterns, ignorePath };
232
+ }
233
+ catch {
234
+ // If we can't read the file, return empty patterns
235
+ return { patterns: [], ignorePath: null };
236
+ }
237
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Format converters for accessibility reports
3
+ * Converts AllyReport to various output formats (SARIF, JUnit, CSV)
4
+ */
5
+ import type { AllyReport } from '../types/index.js';
6
+ export interface SarifReport {
7
+ $schema: string;
8
+ version: string;
9
+ runs: SarifRun[];
10
+ }
11
+ interface SarifRun {
12
+ tool: {
13
+ driver: {
14
+ name: string;
15
+ version: string;
16
+ informationUri: string;
17
+ rules: SarifRule[];
18
+ };
19
+ };
20
+ results: SarifResult[];
21
+ }
22
+ interface SarifRule {
23
+ id: string;
24
+ name: string;
25
+ shortDescription: {
26
+ text: string;
27
+ };
28
+ fullDescription: {
29
+ text: string;
30
+ };
31
+ helpUri: string;
32
+ defaultConfiguration: {
33
+ level: 'error' | 'warning' | 'note';
34
+ };
35
+ properties?: {
36
+ tags?: string[];
37
+ };
38
+ }
39
+ interface SarifResult {
40
+ ruleId: string;
41
+ ruleIndex: number;
42
+ level: 'error' | 'warning' | 'note';
43
+ message: {
44
+ text: string;
45
+ };
46
+ locations: SarifLocation[];
47
+ }
48
+ interface SarifLocation {
49
+ physicalLocation: {
50
+ artifactLocation: {
51
+ uri: string;
52
+ uriBaseId?: string;
53
+ };
54
+ region?: {
55
+ startLine?: number;
56
+ startColumn?: number;
57
+ snippet?: {
58
+ text: string;
59
+ };
60
+ };
61
+ };
62
+ }
63
+ /**
64
+ * Convert AllyReport to SARIF 2.1.0 format for GitHub Code Scanning integration
65
+ */
66
+ export declare function convertToSarif(report: AllyReport): SarifReport;
67
+ /**
68
+ * Convert AllyReport to JUnit XML format for CI/CD tools (Jenkins, GitLab, etc.)
69
+ * Each violation is represented as a test failure
70
+ */
71
+ export declare function convertToJunit(report: AllyReport): string;
72
+ /**
73
+ * Convert AllyReport to CSV format
74
+ * Headers: file,violation_id,impact,description,selector,wcag,help_url
75
+ */
76
+ export declare function convertToCsv(report: AllyReport): string;
77
+ export {};