@vertaaux/cli 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.
Files changed (198) hide show
  1. package/README.md +345 -0
  2. package/dist/auth/ci-token.d.ts +49 -0
  3. package/dist/auth/ci-token.d.ts.map +1 -0
  4. package/dist/auth/ci-token.js +83 -0
  5. package/dist/auth/device-flow.d.ts +66 -0
  6. package/dist/auth/device-flow.d.ts.map +1 -0
  7. package/dist/auth/device-flow.js +156 -0
  8. package/dist/auth/token-store.d.ts +53 -0
  9. package/dist/auth/token-store.d.ts.map +1 -0
  10. package/dist/auth/token-store.js +78 -0
  11. package/dist/baseline/diff.d.ts +57 -0
  12. package/dist/baseline/diff.d.ts.map +1 -0
  13. package/dist/baseline/diff.js +152 -0
  14. package/dist/baseline/hash.d.ts +54 -0
  15. package/dist/baseline/hash.d.ts.map +1 -0
  16. package/dist/baseline/hash.js +66 -0
  17. package/dist/baseline/manager.d.ts +89 -0
  18. package/dist/baseline/manager.d.ts.map +1 -0
  19. package/dist/baseline/manager.js +157 -0
  20. package/dist/cache/index.d.ts +8 -0
  21. package/dist/cache/index.d.ts.map +1 -0
  22. package/dist/cache/index.js +7 -0
  23. package/dist/cache/route-cache.d.ts +119 -0
  24. package/dist/cache/route-cache.d.ts.map +1 -0
  25. package/dist/cache/route-cache.js +213 -0
  26. package/dist/ci/changed-routes.d.ts +95 -0
  27. package/dist/ci/changed-routes.d.ts.map +1 -0
  28. package/dist/ci/changed-routes.js +304 -0
  29. package/dist/ci/github-api.d.ts +68 -0
  30. package/dist/ci/github-api.d.ts.map +1 -0
  31. package/dist/ci/github-api.js +138 -0
  32. package/dist/ci/gitlab-api.d.ts +75 -0
  33. package/dist/ci/gitlab-api.d.ts.map +1 -0
  34. package/dist/ci/gitlab-api.js +180 -0
  35. package/dist/ci/index.d.ts +6 -0
  36. package/dist/ci/index.d.ts.map +1 -0
  37. package/dist/ci/index.js +4 -0
  38. package/dist/commands/audit.d.ts +58 -0
  39. package/dist/commands/audit.d.ts.map +1 -0
  40. package/dist/commands/audit.js +862 -0
  41. package/dist/commands/baseline.d.ts +22 -0
  42. package/dist/commands/baseline.d.ts.map +1 -0
  43. package/dist/commands/baseline.js +210 -0
  44. package/dist/commands/comment.d.ts +14 -0
  45. package/dist/commands/comment.d.ts.map +1 -0
  46. package/dist/commands/comment.js +363 -0
  47. package/dist/commands/diff.d.ts +24 -0
  48. package/dist/commands/diff.d.ts.map +1 -0
  49. package/dist/commands/diff.js +196 -0
  50. package/dist/commands/doctor.d.ts +58 -0
  51. package/dist/commands/doctor.d.ts.map +1 -0
  52. package/dist/commands/doctor.js +338 -0
  53. package/dist/commands/download.d.ts +12 -0
  54. package/dist/commands/download.d.ts.map +1 -0
  55. package/dist/commands/download.js +183 -0
  56. package/dist/commands/explain.d.ts +62 -0
  57. package/dist/commands/explain.d.ts.map +1 -0
  58. package/dist/commands/explain.js +302 -0
  59. package/dist/commands/init.d.ts +12 -0
  60. package/dist/commands/init.d.ts.map +1 -0
  61. package/dist/commands/init.js +212 -0
  62. package/dist/commands/login.d.ts +14 -0
  63. package/dist/commands/login.d.ts.map +1 -0
  64. package/dist/commands/login.js +222 -0
  65. package/dist/commands/policy.d.ts +13 -0
  66. package/dist/commands/policy.d.ts.map +1 -0
  67. package/dist/commands/policy.js +347 -0
  68. package/dist/commands/upload.d.ts +12 -0
  69. package/dist/commands/upload.d.ts.map +1 -0
  70. package/dist/commands/upload.js +158 -0
  71. package/dist/config/defaults.d.ts +21 -0
  72. package/dist/config/defaults.d.ts.map +1 -0
  73. package/dist/config/defaults.js +49 -0
  74. package/dist/config/loader.d.ts +66 -0
  75. package/dist/config/loader.d.ts.map +1 -0
  76. package/dist/config/loader.js +167 -0
  77. package/dist/config/schema.d.ts +55 -0
  78. package/dist/config/schema.d.ts.map +1 -0
  79. package/dist/config/schema.js +6 -0
  80. package/dist/index.d.ts +9 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +1090 -0
  83. package/dist/interactive/fix-wizard.d.ts +44 -0
  84. package/dist/interactive/fix-wizard.d.ts.map +1 -0
  85. package/dist/interactive/fix-wizard.js +286 -0
  86. package/dist/interactive/init-wizard.d.ts +32 -0
  87. package/dist/interactive/init-wizard.d.ts.map +1 -0
  88. package/dist/interactive/init-wizard.js +193 -0
  89. package/dist/interactive/prompts.d.ts +62 -0
  90. package/dist/interactive/prompts.d.ts.map +1 -0
  91. package/dist/interactive/prompts.js +78 -0
  92. package/dist/monorepo/detector.d.ts +70 -0
  93. package/dist/monorepo/detector.d.ts.map +1 -0
  94. package/dist/monorepo/detector.js +278 -0
  95. package/dist/monorepo/index.d.ts +9 -0
  96. package/dist/monorepo/index.d.ts.map +1 -0
  97. package/dist/monorepo/index.js +8 -0
  98. package/dist/monorepo/workspace.d.ts +142 -0
  99. package/dist/monorepo/workspace.d.ts.map +1 -0
  100. package/dist/monorepo/workspace.js +171 -0
  101. package/dist/output/envelope.d.ts +21 -0
  102. package/dist/output/envelope.d.ts.map +1 -0
  103. package/dist/output/envelope.js +27 -0
  104. package/dist/output/factory.d.ts +73 -0
  105. package/dist/output/factory.d.ts.map +1 -0
  106. package/dist/output/factory.js +60 -0
  107. package/dist/output/formats.d.ts +11 -0
  108. package/dist/output/formats.d.ts.map +1 -0
  109. package/dist/output/formats.js +41 -0
  110. package/dist/output/html.d.ts +45 -0
  111. package/dist/output/html.d.ts.map +1 -0
  112. package/dist/output/html.js +607 -0
  113. package/dist/output/human.d.ts +41 -0
  114. package/dist/output/human.d.ts.map +1 -0
  115. package/dist/output/human.js +274 -0
  116. package/dist/output/json.d.ts +42 -0
  117. package/dist/output/json.d.ts.map +1 -0
  118. package/dist/output/json.js +37 -0
  119. package/dist/output/junit.d.ts +56 -0
  120. package/dist/output/junit.d.ts.map +1 -0
  121. package/dist/output/junit.js +135 -0
  122. package/dist/output/markdown.d.ts +77 -0
  123. package/dist/output/markdown.d.ts.map +1 -0
  124. package/dist/output/markdown.js +411 -0
  125. package/dist/output/sarif.d.ts +160 -0
  126. package/dist/output/sarif.d.ts.map +1 -0
  127. package/dist/output/sarif.js +207 -0
  128. package/dist/policy/evaluator.d.ts +111 -0
  129. package/dist/policy/evaluator.d.ts.map +1 -0
  130. package/dist/policy/evaluator.js +362 -0
  131. package/dist/policy/index.d.ts +15 -0
  132. package/dist/policy/index.d.ts.map +1 -0
  133. package/dist/policy/index.js +11 -0
  134. package/dist/policy/loader.d.ts +97 -0
  135. package/dist/policy/loader.d.ts.map +1 -0
  136. package/dist/policy/loader.js +281 -0
  137. package/dist/policy/schema.d.ts +297 -0
  138. package/dist/policy/schema.d.ts.map +1 -0
  139. package/dist/policy/schema.js +230 -0
  140. package/dist/quality-gate/evaluator.d.ts +58 -0
  141. package/dist/quality-gate/evaluator.d.ts.map +1 -0
  142. package/dist/quality-gate/evaluator.js +274 -0
  143. package/dist/quality-gate/index.d.ts +10 -0
  144. package/dist/quality-gate/index.d.ts.map +1 -0
  145. package/dist/quality-gate/index.js +7 -0
  146. package/dist/quality-gate/types.d.ts +103 -0
  147. package/dist/quality-gate/types.d.ts.map +1 -0
  148. package/dist/quality-gate/types.js +23 -0
  149. package/dist/templates/azure-devops.d.ts +25 -0
  150. package/dist/templates/azure-devops.d.ts.map +1 -0
  151. package/dist/templates/azure-devops.js +109 -0
  152. package/dist/templates/circleci.d.ts +28 -0
  153. package/dist/templates/circleci.d.ts.map +1 -0
  154. package/dist/templates/circleci.js +86 -0
  155. package/dist/templates/github-actions.d.ts +81 -0
  156. package/dist/templates/github-actions.d.ts.map +1 -0
  157. package/dist/templates/github-actions.js +393 -0
  158. package/dist/templates/gitlab-ci.d.ts +26 -0
  159. package/dist/templates/gitlab-ci.d.ts.map +1 -0
  160. package/dist/templates/gitlab-ci.js +70 -0
  161. package/dist/templates/index.d.ts +72 -0
  162. package/dist/templates/index.d.ts.map +1 -0
  163. package/dist/templates/index.js +112 -0
  164. package/dist/templates/jenkins.d.ts +26 -0
  165. package/dist/templates/jenkins.d.ts.map +1 -0
  166. package/dist/templates/jenkins.js +110 -0
  167. package/dist/ui/banner.d.ts +31 -0
  168. package/dist/ui/banner.d.ts.map +1 -0
  169. package/dist/ui/banner.js +84 -0
  170. package/dist/ui/diagnostics.d.ts +39 -0
  171. package/dist/ui/diagnostics.d.ts.map +1 -0
  172. package/dist/ui/diagnostics.js +153 -0
  173. package/dist/ui/spinner.d.ts +61 -0
  174. package/dist/ui/spinner.d.ts.map +1 -0
  175. package/dist/ui/spinner.js +101 -0
  176. package/dist/ui/table.d.ts +63 -0
  177. package/dist/ui/table.d.ts.map +1 -0
  178. package/dist/ui/table.js +236 -0
  179. package/dist/utils/client.d.ts +82 -0
  180. package/dist/utils/client.d.ts.map +1 -0
  181. package/dist/utils/client.js +128 -0
  182. package/dist/utils/detect-env.d.ts +59 -0
  183. package/dist/utils/detect-env.d.ts.map +1 -0
  184. package/dist/utils/detect-env.js +115 -0
  185. package/dist/utils/exit-codes.d.ts +47 -0
  186. package/dist/utils/exit-codes.d.ts.map +1 -0
  187. package/dist/utils/exit-codes.js +61 -0
  188. package/dist/utils/logger.d.ts +87 -0
  189. package/dist/utils/logger.d.ts.map +1 -0
  190. package/dist/utils/logger.js +185 -0
  191. package/dist/utils/sanitize.d.ts +36 -0
  192. package/dist/utils/sanitize.d.ts.map +1 -0
  193. package/dist/utils/sanitize.js +64 -0
  194. package/dist/utils/validators.d.ts +41 -0
  195. package/dist/utils/validators.d.ts.map +1 -0
  196. package/dist/utils/validators.js +123 -0
  197. package/package.json +63 -0
  198. package/schemas/vertaaux.config.schema.json +103 -0
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Human-readable output formatter for CLI.
3
+ *
4
+ * Outputs audit results as colorful, formatted text for terminal display.
5
+ * Uses chalk for colors and cli-table3 for table formatting.
6
+ */
7
+ import chalk from "chalk";
8
+ import { formatIssuesTable, formatScoresTable, formatIssueSummary, groupBySeverity, groupByCategory, } from "../ui/table.js";
9
+ import { shouldUseColor, getTerminalWidth } from "../utils/detect-env.js";
10
+ /**
11
+ * Normalize issues from various API response formats.
12
+ */
13
+ function normalizeIssues(issues) {
14
+ if (Array.isArray(issues))
15
+ return issues;
16
+ if (issues && typeof issues === "object") {
17
+ const values = Object.values(issues);
18
+ return values.flatMap((value) => Array.isArray(value) ? value : []);
19
+ }
20
+ return [];
21
+ }
22
+ /**
23
+ * Get overall score from scores object.
24
+ */
25
+ function getOverallScore(scores) {
26
+ if (!scores)
27
+ return null;
28
+ const direct = scores.overall ?? scores.ux ?? scores.total;
29
+ if (typeof direct === "number" && Number.isFinite(direct))
30
+ return direct;
31
+ const numeric = Object.values(scores)
32
+ .filter((v) => typeof v === "number" && Number.isFinite(v));
33
+ if (numeric.length === 0)
34
+ return null;
35
+ return Math.round(numeric.reduce((a, b) => a + b, 0) / numeric.length);
36
+ }
37
+ /**
38
+ * Format a horizontal rule.
39
+ */
40
+ function hr() {
41
+ const width = Math.min(getTerminalWidth(), 80);
42
+ return shouldUseColor()
43
+ ? chalk.dim("-".repeat(width))
44
+ : "-".repeat(width);
45
+ }
46
+ /**
47
+ * Format the header section.
48
+ */
49
+ function formatHeader(result) {
50
+ const useColor = shouldUseColor();
51
+ const lines = [];
52
+ // Title
53
+ const title = useColor
54
+ ? chalk.bold.cyan("Audit Complete")
55
+ : "AUDIT COMPLETE";
56
+ lines.push(title);
57
+ // URL
58
+ if (result.url) {
59
+ const urlLine = useColor
60
+ ? chalk.dim("URL: ") + chalk.white(result.url)
61
+ : `URL: ${result.url}`;
62
+ lines.push(urlLine);
63
+ }
64
+ // Mode and Job ID
65
+ const meta = [];
66
+ if (result.mode)
67
+ meta.push(`Mode: ${result.mode}`);
68
+ if (result.job_id)
69
+ meta.push(`Job: ${result.job_id}`);
70
+ if (meta.length) {
71
+ lines.push(useColor ? chalk.dim(meta.join(" | ")) : meta.join(" | "));
72
+ }
73
+ return lines.join("\n");
74
+ }
75
+ /**
76
+ * Format the scores section.
77
+ */
78
+ function formatScoresSection(scores) {
79
+ const useColor = shouldUseColor();
80
+ const lines = [];
81
+ lines.push("");
82
+ lines.push(useColor ? chalk.bold("Scores") : "SCORES");
83
+ lines.push(hr());
84
+ if (!scores || Object.keys(scores).length === 0) {
85
+ lines.push(useColor ? chalk.dim("No scores available.") : "No scores available.");
86
+ return lines.join("\n");
87
+ }
88
+ // Overall score with color coding
89
+ const overall = getOverallScore(scores);
90
+ if (overall !== null) {
91
+ let scoreDisplay = String(overall);
92
+ if (useColor) {
93
+ if (overall >= 90) {
94
+ scoreDisplay = chalk.green.bold(scoreDisplay);
95
+ }
96
+ else if (overall >= 70) {
97
+ scoreDisplay = chalk.yellow.bold(scoreDisplay);
98
+ }
99
+ else {
100
+ scoreDisplay = chalk.red.bold(scoreDisplay);
101
+ }
102
+ }
103
+ lines.push(`Overall: ${scoreDisplay}/100`);
104
+ lines.push("");
105
+ }
106
+ // Detailed scores table
107
+ const numericScores = {};
108
+ for (const [key, value] of Object.entries(scores)) {
109
+ if (typeof value === "number" && key !== "overall") {
110
+ numericScores[key] = value;
111
+ }
112
+ }
113
+ if (Object.keys(numericScores).length > 0) {
114
+ lines.push(formatScoresTable(numericScores));
115
+ }
116
+ return lines.join("\n");
117
+ }
118
+ /**
119
+ * Format issues section grouped by severity.
120
+ */
121
+ function formatIssuesBySeverity(issues, maxPerGroup, tableOptions) {
122
+ const useColor = shouldUseColor();
123
+ const lines = [];
124
+ lines.push("");
125
+ lines.push(useColor ? chalk.bold("Issues") : "ISSUES");
126
+ lines.push(hr());
127
+ if (issues.length === 0) {
128
+ lines.push(useColor ? chalk.green("No issues found!") : "No issues found!");
129
+ return lines.join("\n");
130
+ }
131
+ // Group by severity
132
+ const groups = groupBySeverity(issues);
133
+ const severityOrder = ["critical", "error", "serious", "warning", "minor", "moderate", "info"];
134
+ for (const severity of severityOrder) {
135
+ const groupIssues = groups.get(severity);
136
+ if (!groupIssues || groupIssues.length === 0)
137
+ continue;
138
+ const displayIssues = maxPerGroup
139
+ ? groupIssues.slice(0, maxPerGroup)
140
+ : groupIssues;
141
+ // Section header
142
+ const label = severity.toUpperCase();
143
+ const count = groupIssues.length;
144
+ const header = useColor
145
+ ? chalk.bold(`${label} (${count})`)
146
+ : `${label} (${count})`;
147
+ lines.push("");
148
+ lines.push(header);
149
+ // Issues table for this severity
150
+ lines.push(formatIssuesTable(displayIssues, tableOptions));
151
+ // Show truncation message if needed
152
+ if (maxPerGroup && groupIssues.length > maxPerGroup) {
153
+ const remaining = groupIssues.length - maxPerGroup;
154
+ const msg = `...and ${remaining} more ${severity} issues`;
155
+ lines.push(useColor ? chalk.dim(msg) : msg);
156
+ }
157
+ }
158
+ return lines.join("\n");
159
+ }
160
+ /**
161
+ * Format issues section grouped by category.
162
+ */
163
+ function formatIssuesByCategory(issues, maxPerGroup, tableOptions) {
164
+ const useColor = shouldUseColor();
165
+ const lines = [];
166
+ lines.push("");
167
+ lines.push(useColor ? chalk.bold("Issues by Category") : "ISSUES BY CATEGORY");
168
+ lines.push(hr());
169
+ if (issues.length === 0) {
170
+ lines.push(useColor ? chalk.green("No issues found!") : "No issues found!");
171
+ return lines.join("\n");
172
+ }
173
+ // Group by category
174
+ const groups = groupByCategory(issues);
175
+ for (const [category, groupIssues] of groups) {
176
+ const displayIssues = maxPerGroup
177
+ ? groupIssues.slice(0, maxPerGroup)
178
+ : groupIssues;
179
+ // Section header
180
+ const header = useColor
181
+ ? chalk.bold(`${category} (${groupIssues.length})`)
182
+ : `${category} (${groupIssues.length})`;
183
+ lines.push("");
184
+ lines.push(header);
185
+ // Issues table for this category
186
+ lines.push(formatIssuesTable(displayIssues, tableOptions));
187
+ // Show truncation message if needed
188
+ if (maxPerGroup && groupIssues.length > maxPerGroup) {
189
+ const remaining = groupIssues.length - maxPerGroup;
190
+ const msg = `...and ${remaining} more issues`;
191
+ lines.push(useColor ? chalk.dim(msg) : msg);
192
+ }
193
+ }
194
+ return lines.join("\n");
195
+ }
196
+ /**
197
+ * Format the summary section.
198
+ */
199
+ function formatSummary(issues, scores, threshold) {
200
+ const useColor = shouldUseColor();
201
+ const lines = [];
202
+ lines.push("");
203
+ lines.push(hr());
204
+ // Summary line
205
+ const summary = formatIssueSummary(issues);
206
+ lines.push(summary);
207
+ // Pass/fail status
208
+ const overall = getOverallScore(scores);
209
+ const hasErrors = issues.some((i) => i.severity?.toLowerCase() === "critical" || i.severity?.toLowerCase() === "error");
210
+ let status;
211
+ let passed;
212
+ if (threshold !== undefined && overall !== null) {
213
+ passed = overall >= threshold && !hasErrors;
214
+ status = passed
215
+ ? `PASSED (score ${overall} >= ${threshold})`
216
+ : `FAILED (score ${overall} < ${threshold})`;
217
+ }
218
+ else if (overall !== null) {
219
+ passed = overall >= 70 && !hasErrors;
220
+ status = passed ? `PASSED (score: ${overall})` : `FAILED (score: ${overall})`;
221
+ }
222
+ else {
223
+ passed = !hasErrors;
224
+ status = passed ? "PASSED" : "FAILED";
225
+ }
226
+ if (useColor) {
227
+ status = passed ? chalk.green.bold(status) : chalk.red.bold(status);
228
+ }
229
+ lines.push(status);
230
+ return lines.join("\n");
231
+ }
232
+ /**
233
+ * Format audit result as human-readable string.
234
+ *
235
+ * @param result - Audit result object
236
+ * @param options - Formatting options
237
+ * @returns Formatted string for terminal display
238
+ */
239
+ export function formatAuditHuman(result, options = {}) {
240
+ const { groupBy = "severity", showScores = true, showSummary = true, maxIssuesPerGroup = 10, tableOptions, } = options;
241
+ const sections = [];
242
+ // Header
243
+ sections.push(formatHeader(result));
244
+ // Handle non-completed audits
245
+ if (result.status !== "completed") {
246
+ const useColor = shouldUseColor();
247
+ sections.push("");
248
+ sections.push(useColor
249
+ ? chalk.yellow(`Status: ${result.status || "unknown"}`)
250
+ : `Status: ${result.status || "unknown"}`);
251
+ if (typeof result.progress === "number") {
252
+ sections.push(`Progress: ${result.progress}%`);
253
+ }
254
+ return sections.join("\n");
255
+ }
256
+ // Scores section
257
+ if (showScores) {
258
+ sections.push(formatScoresSection(result.scores));
259
+ }
260
+ // Issues section
261
+ const issues = normalizeIssues(result.issues);
262
+ if (groupBy === "category") {
263
+ sections.push(formatIssuesByCategory(issues, maxIssuesPerGroup, tableOptions));
264
+ }
265
+ else {
266
+ // Default: group by severity
267
+ sections.push(formatIssuesBySeverity(issues, maxIssuesPerGroup, tableOptions));
268
+ }
269
+ // Summary
270
+ if (showSummary) {
271
+ sections.push(formatSummary(issues, result.scores));
272
+ }
273
+ return sections.join("\n") + "\n";
274
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * JSON output formatter for CLI.
3
+ *
4
+ * Outputs audit results as pretty-printed JSON.
5
+ * Used for piped output and machine parsing.
6
+ */
7
+ export interface AuditResult {
8
+ job_id?: string;
9
+ status?: string;
10
+ url?: string;
11
+ mode?: string;
12
+ progress?: number;
13
+ created_at?: string;
14
+ started_at?: string;
15
+ completed_at?: string;
16
+ scores?: Record<string, unknown>;
17
+ issues?: unknown;
18
+ error?: string;
19
+ }
20
+ export interface FormatJsonOptions {
21
+ /** Indentation spaces (default: 2) */
22
+ indent?: number;
23
+ /** Include only specific fields */
24
+ fields?: string[];
25
+ }
26
+ /**
27
+ * Format audit result as JSON string.
28
+ *
29
+ * @param result - Audit result object
30
+ * @param options - Formatting options
31
+ * @returns Pretty-printed JSON string
32
+ */
33
+ export declare function formatAuditJson(result: AuditResult, options?: FormatJsonOptions): string;
34
+ /**
35
+ * Format any data as JSON string.
36
+ *
37
+ * @param data - Data to format
38
+ * @param indent - Indentation spaces (default: 2)
39
+ * @returns Pretty-printed JSON string
40
+ */
41
+ export declare function formatJson(data: unknown, indent?: number): string;
42
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAeR;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,SAAI,GAAG,MAAM,CAE5D"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * JSON output formatter for CLI.
3
+ *
4
+ * Outputs audit results as pretty-printed JSON.
5
+ * Used for piped output and machine parsing.
6
+ */
7
+ /**
8
+ * Format audit result as JSON string.
9
+ *
10
+ * @param result - Audit result object
11
+ * @param options - Formatting options
12
+ * @returns Pretty-printed JSON string
13
+ */
14
+ export function formatAuditJson(result, options = {}) {
15
+ const { indent = 2, fields } = options;
16
+ // If specific fields requested, filter the result
17
+ if (fields && fields.length > 0) {
18
+ const filtered = {};
19
+ for (const field of fields) {
20
+ if (field in result) {
21
+ filtered[field] = result[field];
22
+ }
23
+ }
24
+ return JSON.stringify(filtered, null, indent);
25
+ }
26
+ return JSON.stringify(result, null, indent);
27
+ }
28
+ /**
29
+ * Format any data as JSON string.
30
+ *
31
+ * @param data - Data to format
32
+ * @param indent - Indentation spaces (default: 2)
33
+ * @returns Pretty-printed JSON string
34
+ */
35
+ export function formatJson(data, indent = 2) {
36
+ return JSON.stringify(data, null, indent);
37
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * JUnit XML output formatter for CLI.
3
+ *
4
+ * Generates JUnit XML format for integration with Jenkins, GitLab CI,
5
+ * and other CI systems that support JUnit test reports.
6
+ *
7
+ * @see https://github.com/testmoapp/junitxml
8
+ * @see https://docs.gitlab.com/ci/testing/unit_test_reports/
9
+ */
10
+ /**
11
+ * Options for JUnit formatting.
12
+ */
13
+ export interface JunitOptions {
14
+ /** Include passing checks as test cases */
15
+ includePassingChecks?: boolean;
16
+ /** Custom test suite name prefix */
17
+ suiteNamePrefix?: string;
18
+ }
19
+ /**
20
+ * Audit result structure (matches factory.ts).
21
+ */
22
+ export interface AuditResult {
23
+ job_id?: string;
24
+ status?: string;
25
+ url?: string;
26
+ mode?: string;
27
+ progress?: number;
28
+ created_at?: string;
29
+ started_at?: string;
30
+ completed_at?: string;
31
+ scores?: Record<string, unknown>;
32
+ issues?: unknown;
33
+ error?: string;
34
+ }
35
+ /**
36
+ * Format audit result as JUnit XML.
37
+ *
38
+ * @param result - Audit result from API
39
+ * @param options - Formatting options
40
+ * @returns JUnit XML string
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const junit = formatJunit(auditResult, {
45
+ * suiteNamePrefix: 'VertaaUX'
46
+ * });
47
+ * ```
48
+ */
49
+ export declare function formatJunit(result: AuditResult, options?: JunitOptions): string;
50
+ /**
51
+ * Format options for the JUnit formatter (compatible with FormatOptions pattern).
52
+ */
53
+ export interface FormatJunitOptions {
54
+ junit?: JunitOptions;
55
+ }
56
+ //# sourceMappingURL=junit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"junit.d.ts","sourceRoot":"","sources":["../../src/output/junit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,oCAAoC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqFD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CA+D/E;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * JUnit XML output formatter for CLI.
3
+ *
4
+ * Generates JUnit XML format for integration with Jenkins, GitLab CI,
5
+ * and other CI systems that support JUnit test reports.
6
+ *
7
+ * @see https://github.com/testmoapp/junitxml
8
+ * @see https://docs.gitlab.com/ci/testing/unit_test_reports/
9
+ */
10
+ /**
11
+ * Escape XML special characters.
12
+ *
13
+ * @param text - Text to escape
14
+ * @returns XML-safe text
15
+ */
16
+ function escapeXml(text) {
17
+ if (!text)
18
+ return "";
19
+ return text
20
+ .replace(/&/g, "&amp;")
21
+ .replace(/</g, "&lt;")
22
+ .replace(/>/g, "&gt;")
23
+ .replace(/"/g, "&quot;")
24
+ .replace(/'/g, "&apos;");
25
+ }
26
+ /**
27
+ * Normalize issues from various API response formats.
28
+ */
29
+ function normalizeIssues(issues) {
30
+ if (Array.isArray(issues))
31
+ return issues;
32
+ if (issues && typeof issues === "object") {
33
+ const values = Object.values(issues);
34
+ return values.flatMap((value) => Array.isArray(value) ? value : []);
35
+ }
36
+ return [];
37
+ }
38
+ /**
39
+ * Get rule ID from issue, checking multiple field names.
40
+ */
41
+ function getRuleId(issue) {
42
+ return issue.ruleId || issue.rule_id || issue.id || "unknown";
43
+ }
44
+ /**
45
+ * Group issues by category.
46
+ */
47
+ function groupByCategory(issues) {
48
+ const groups = new Map();
49
+ for (const issue of issues) {
50
+ const category = issue.category || "uncategorized";
51
+ const existing = groups.get(category) || [];
52
+ existing.push(issue);
53
+ groups.set(category, existing);
54
+ }
55
+ return groups;
56
+ }
57
+ /**
58
+ * Determine if an issue represents a failure.
59
+ * Issues with severity above 'info' are considered failures.
60
+ */
61
+ function isFailure(issue) {
62
+ const severity = (issue.severity || "info").toLowerCase();
63
+ return severity !== "info" && severity !== "minor";
64
+ }
65
+ /**
66
+ * Build failure element content.
67
+ */
68
+ function buildFailureContent(issue) {
69
+ const parts = [];
70
+ if (issue.recommendation || issue.recommended_fix) {
71
+ parts.push(`Recommendation: ${issue.recommendation || issue.recommended_fix}`);
72
+ }
73
+ if (issue.wcag_reference) {
74
+ parts.push(`WCAG Reference: ${issue.wcag_reference}`);
75
+ }
76
+ if (issue.selector) {
77
+ parts.push(`Selector: ${issue.selector}`);
78
+ }
79
+ return parts.join("\n");
80
+ }
81
+ /**
82
+ * Format audit result as JUnit XML.
83
+ *
84
+ * @param result - Audit result from API
85
+ * @param options - Formatting options
86
+ * @returns JUnit XML string
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const junit = formatJunit(auditResult, {
91
+ * suiteNamePrefix: 'VertaaUX'
92
+ * });
93
+ * ```
94
+ */
95
+ export function formatJunit(result, options) {
96
+ const issues = normalizeIssues(result.issues);
97
+ const groupedIssues = groupByCategory(issues);
98
+ const prefix = options?.suiteNamePrefix || "VertaaUX";
99
+ // Calculate totals
100
+ const totalTests = issues.length;
101
+ const totalFailures = issues.filter(isFailure).length;
102
+ // Build XML
103
+ const lines = [
104
+ '<?xml version="1.0" encoding="UTF-8"?>',
105
+ `<testsuites name="${escapeXml(`${prefix} Audit: ${result.url || "Unknown URL"}`)}" tests="${totalTests}" failures="${totalFailures}" errors="0" time="0">`,
106
+ ];
107
+ // Build testsuite for each category
108
+ for (const [category, categoryIssues] of groupedIssues) {
109
+ const suiteTests = categoryIssues.length;
110
+ const suiteFailures = categoryIssues.filter(isFailure).length;
111
+ lines.push(` <testsuite name="${escapeXml(category)}" tests="${suiteTests}" failures="${suiteFailures}" errors="0" time="0">`);
112
+ // Build testcase for each issue
113
+ for (const issue of categoryIssues) {
114
+ const ruleId = getRuleId(issue);
115
+ const description = issue.description || issue.title || "No description";
116
+ const classname = `${escapeXml(category)}.${escapeXml(ruleId)}`;
117
+ const name = escapeXml(description.slice(0, 100));
118
+ lines.push(` <testcase name="${name}" classname="${classname}" time="0">`);
119
+ if (isFailure(issue)) {
120
+ const severity = issue.severity || "warning";
121
+ const message = escapeXml(description);
122
+ const content = escapeXml(buildFailureContent(issue));
123
+ lines.push(` <failure message="${message}" type="${escapeXml(severity)}">${content}</failure>`);
124
+ }
125
+ lines.push(" </testcase>");
126
+ }
127
+ lines.push(" </testsuite>");
128
+ }
129
+ // Handle case where there are no issues (all passed)
130
+ if (groupedIssues.size === 0) {
131
+ lines.push(` <testsuite name="audit" tests="1" failures="0" errors="0" time="0">`, ` <testcase name="All checks passed" classname="audit.passed" time="0" />`, ` </testsuite>`);
132
+ }
133
+ lines.push("</testsuites>");
134
+ return lines.join("\n");
135
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Markdown formatter for PR comments.
3
+ *
4
+ * Generates GitHub/GitLab flavored markdown with:
5
+ * - Collapsible sections for grouping issues
6
+ * - Evidence links for each issue
7
+ * - New/fixed issue labeling from baseline comparison
8
+ * - Hidden identifier for sticky comment matching
9
+ */
10
+ import { type Issue } from "../baseline/hash.js";
11
+ import type { BaselineFile, BaselineIssue } from "../baseline/manager.js";
12
+ /**
13
+ * Markdown formatting options.
14
+ */
15
+ export interface MarkdownOptions {
16
+ /** Group issues by field */
17
+ groupBy: "severity" | "category" | "route" | "file" | "component";
18
+ /** Use collapsible sections */
19
+ collapse: boolean;
20
+ /** Collapse threshold - use details if more than N issues */
21
+ collapseThreshold: number;
22
+ /** Include evidence links */
23
+ includeEvidence: boolean;
24
+ /** Include fix suggestions inline */
25
+ includeFixes: boolean;
26
+ /** Base URL for evidence/report links */
27
+ baseUrl: string;
28
+ }
29
+ /**
30
+ * Data for comment generation.
31
+ */
32
+ export interface CommentData {
33
+ /** Audit ID for linking */
34
+ auditId?: string;
35
+ /** URL that was audited */
36
+ url?: string;
37
+ /** New issues (not in baseline) */
38
+ newIssues: Issue[];
39
+ /** Issues that were fixed (in baseline but not in current) */
40
+ fixedIssues: BaselineIssue[];
41
+ /** Issues still present (in both baseline and current) */
42
+ existingIssues: Issue[];
43
+ /** Audit scores */
44
+ scores?: {
45
+ overall?: number;
46
+ accessibility?: number;
47
+ };
48
+ }
49
+ /**
50
+ * Default markdown options.
51
+ */
52
+ export declare const DEFAULT_MARKDOWN_OPTIONS: MarkdownOptions;
53
+ /**
54
+ * Format markdown comment from audit data.
55
+ *
56
+ * Produces GitHub-flavored markdown with:
57
+ * - Hidden identifier for sticky comment matching
58
+ * - Status summary line
59
+ * - Issue counts table
60
+ * - Grouped and optionally collapsible issue sections
61
+ * - Evidence links for each issue
62
+ * - Footer with audit ID links
63
+ *
64
+ * @param data - Comment data with categorized issues
65
+ * @param options - Formatting options
66
+ * @returns Formatted markdown string
67
+ */
68
+ export declare function formatMarkdownComment(data: CommentData, options?: Partial<MarkdownOptions>): string;
69
+ /**
70
+ * Categorize issues into new/existing/fixed based on baseline.
71
+ *
72
+ * @param currentIssues - Issues from current audit
73
+ * @param baseline - Baseline for comparison (null = all issues are new)
74
+ * @returns Categorized issues for comment data
75
+ */
76
+ export declare function categorizeIssuesForComment(currentIssues: Issue[], baseline: BaselineFile | null): Pick<CommentData, "newIssues" | "fixedIssues" | "existingIssues">;
77
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/output/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAuB,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC;IAClE,+BAA+B;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6BAA6B;IAC7B,eAAe,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,YAAY,EAAE,OAAO,CAAC;IACtB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,SAAS,EAAE,KAAK,EAAE,CAAC;IACnB,8DAA8D;IAC9D,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,0DAA0D;IAC1D,cAAc,EAAE,KAAK,EAAE,CAAC;IACxB,mBAAmB;IACnB,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvD;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,eAOtC,CAAC;AA4TF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,OAAO,CAAC,eAAe,CAAM,GACrC,MAAM,CAiHR;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,KAAK,EAAE,EACtB,QAAQ,EAAE,YAAY,GAAG,IAAI,GAC5B,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,aAAa,GAAG,gBAAgB,CAAC,CAmCnE"}