@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.
- package/README.md +345 -0
- package/dist/auth/ci-token.d.ts +49 -0
- package/dist/auth/ci-token.d.ts.map +1 -0
- package/dist/auth/ci-token.js +83 -0
- package/dist/auth/device-flow.d.ts +66 -0
- package/dist/auth/device-flow.d.ts.map +1 -0
- package/dist/auth/device-flow.js +156 -0
- package/dist/auth/token-store.d.ts +53 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +78 -0
- package/dist/baseline/diff.d.ts +57 -0
- package/dist/baseline/diff.d.ts.map +1 -0
- package/dist/baseline/diff.js +152 -0
- package/dist/baseline/hash.d.ts +54 -0
- package/dist/baseline/hash.d.ts.map +1 -0
- package/dist/baseline/hash.js +66 -0
- package/dist/baseline/manager.d.ts +89 -0
- package/dist/baseline/manager.d.ts.map +1 -0
- package/dist/baseline/manager.js +157 -0
- package/dist/cache/index.d.ts +8 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +7 -0
- package/dist/cache/route-cache.d.ts +119 -0
- package/dist/cache/route-cache.d.ts.map +1 -0
- package/dist/cache/route-cache.js +213 -0
- package/dist/ci/changed-routes.d.ts +95 -0
- package/dist/ci/changed-routes.d.ts.map +1 -0
- package/dist/ci/changed-routes.js +304 -0
- package/dist/ci/github-api.d.ts +68 -0
- package/dist/ci/github-api.d.ts.map +1 -0
- package/dist/ci/github-api.js +138 -0
- package/dist/ci/gitlab-api.d.ts +75 -0
- package/dist/ci/gitlab-api.d.ts.map +1 -0
- package/dist/ci/gitlab-api.js +180 -0
- package/dist/ci/index.d.ts +6 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +4 -0
- package/dist/commands/audit.d.ts +58 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +862 -0
- package/dist/commands/baseline.d.ts +22 -0
- package/dist/commands/baseline.d.ts.map +1 -0
- package/dist/commands/baseline.js +210 -0
- package/dist/commands/comment.d.ts +14 -0
- package/dist/commands/comment.d.ts.map +1 -0
- package/dist/commands/comment.js +363 -0
- package/dist/commands/diff.d.ts +24 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +196 -0
- package/dist/commands/doctor.d.ts +58 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +338 -0
- package/dist/commands/download.d.ts +12 -0
- package/dist/commands/download.d.ts.map +1 -0
- package/dist/commands/download.js +183 -0
- package/dist/commands/explain.d.ts +62 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +302 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +212 -0
- package/dist/commands/login.d.ts +14 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +222 -0
- package/dist/commands/policy.d.ts +13 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +347 -0
- package/dist/commands/upload.d.ts +12 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +158 -0
- package/dist/config/defaults.d.ts +21 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +49 -0
- package/dist/config/loader.d.ts +66 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +167 -0
- package/dist/config/schema.d.ts +55 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +6 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1090 -0
- package/dist/interactive/fix-wizard.d.ts +44 -0
- package/dist/interactive/fix-wizard.d.ts.map +1 -0
- package/dist/interactive/fix-wizard.js +286 -0
- package/dist/interactive/init-wizard.d.ts +32 -0
- package/dist/interactive/init-wizard.d.ts.map +1 -0
- package/dist/interactive/init-wizard.js +193 -0
- package/dist/interactive/prompts.d.ts +62 -0
- package/dist/interactive/prompts.d.ts.map +1 -0
- package/dist/interactive/prompts.js +78 -0
- package/dist/monorepo/detector.d.ts +70 -0
- package/dist/monorepo/detector.d.ts.map +1 -0
- package/dist/monorepo/detector.js +278 -0
- package/dist/monorepo/index.d.ts +9 -0
- package/dist/monorepo/index.d.ts.map +1 -0
- package/dist/monorepo/index.js +8 -0
- package/dist/monorepo/workspace.d.ts +142 -0
- package/dist/monorepo/workspace.d.ts.map +1 -0
- package/dist/monorepo/workspace.js +171 -0
- package/dist/output/envelope.d.ts +21 -0
- package/dist/output/envelope.d.ts.map +1 -0
- package/dist/output/envelope.js +27 -0
- package/dist/output/factory.d.ts +73 -0
- package/dist/output/factory.d.ts.map +1 -0
- package/dist/output/factory.js +60 -0
- package/dist/output/formats.d.ts +11 -0
- package/dist/output/formats.d.ts.map +1 -0
- package/dist/output/formats.js +41 -0
- package/dist/output/html.d.ts +45 -0
- package/dist/output/html.d.ts.map +1 -0
- package/dist/output/html.js +607 -0
- package/dist/output/human.d.ts +41 -0
- package/dist/output/human.d.ts.map +1 -0
- package/dist/output/human.js +274 -0
- package/dist/output/json.d.ts +42 -0
- package/dist/output/json.d.ts.map +1 -0
- package/dist/output/json.js +37 -0
- package/dist/output/junit.d.ts +56 -0
- package/dist/output/junit.d.ts.map +1 -0
- package/dist/output/junit.js +135 -0
- package/dist/output/markdown.d.ts +77 -0
- package/dist/output/markdown.d.ts.map +1 -0
- package/dist/output/markdown.js +411 -0
- package/dist/output/sarif.d.ts +160 -0
- package/dist/output/sarif.d.ts.map +1 -0
- package/dist/output/sarif.js +207 -0
- package/dist/policy/evaluator.d.ts +111 -0
- package/dist/policy/evaluator.d.ts.map +1 -0
- package/dist/policy/evaluator.js +362 -0
- package/dist/policy/index.d.ts +15 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +11 -0
- package/dist/policy/loader.d.ts +97 -0
- package/dist/policy/loader.d.ts.map +1 -0
- package/dist/policy/loader.js +281 -0
- package/dist/policy/schema.d.ts +297 -0
- package/dist/policy/schema.d.ts.map +1 -0
- package/dist/policy/schema.js +230 -0
- package/dist/quality-gate/evaluator.d.ts +58 -0
- package/dist/quality-gate/evaluator.d.ts.map +1 -0
- package/dist/quality-gate/evaluator.js +274 -0
- package/dist/quality-gate/index.d.ts +10 -0
- package/dist/quality-gate/index.d.ts.map +1 -0
- package/dist/quality-gate/index.js +7 -0
- package/dist/quality-gate/types.d.ts +103 -0
- package/dist/quality-gate/types.d.ts.map +1 -0
- package/dist/quality-gate/types.js +23 -0
- package/dist/templates/azure-devops.d.ts +25 -0
- package/dist/templates/azure-devops.d.ts.map +1 -0
- package/dist/templates/azure-devops.js +109 -0
- package/dist/templates/circleci.d.ts +28 -0
- package/dist/templates/circleci.d.ts.map +1 -0
- package/dist/templates/circleci.js +86 -0
- package/dist/templates/github-actions.d.ts +81 -0
- package/dist/templates/github-actions.d.ts.map +1 -0
- package/dist/templates/github-actions.js +393 -0
- package/dist/templates/gitlab-ci.d.ts +26 -0
- package/dist/templates/gitlab-ci.d.ts.map +1 -0
- package/dist/templates/gitlab-ci.js +70 -0
- package/dist/templates/index.d.ts +72 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +112 -0
- package/dist/templates/jenkins.d.ts +26 -0
- package/dist/templates/jenkins.d.ts.map +1 -0
- package/dist/templates/jenkins.js +110 -0
- package/dist/ui/banner.d.ts +31 -0
- package/dist/ui/banner.d.ts.map +1 -0
- package/dist/ui/banner.js +84 -0
- package/dist/ui/diagnostics.d.ts +39 -0
- package/dist/ui/diagnostics.d.ts.map +1 -0
- package/dist/ui/diagnostics.js +153 -0
- package/dist/ui/spinner.d.ts +61 -0
- package/dist/ui/spinner.d.ts.map +1 -0
- package/dist/ui/spinner.js +101 -0
- package/dist/ui/table.d.ts +63 -0
- package/dist/ui/table.d.ts.map +1 -0
- package/dist/ui/table.js +236 -0
- package/dist/utils/client.d.ts +82 -0
- package/dist/utils/client.d.ts.map +1 -0
- package/dist/utils/client.js +128 -0
- package/dist/utils/detect-env.d.ts +59 -0
- package/dist/utils/detect-env.d.ts.map +1 -0
- package/dist/utils/detect-env.js +115 -0
- package/dist/utils/exit-codes.d.ts +47 -0
- package/dist/utils/exit-codes.d.ts.map +1 -0
- package/dist/utils/exit-codes.js +61 -0
- package/dist/utils/logger.d.ts +87 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +185 -0
- package/dist/utils/sanitize.d.ts +36 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +64 -0
- package/dist/utils/validators.d.ts +41 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +123 -0
- package/package.json +63 -0
- package/schemas/vertaaux.config.schema.json +103 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML output formatter for CLI.
|
|
3
|
+
*
|
|
4
|
+
* Generates standalone HTML reports with embedded CSS and JS.
|
|
5
|
+
* Reports are print-friendly and don't require external dependencies.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Normalize issues from various API response formats.
|
|
9
|
+
*/
|
|
10
|
+
function normalizeIssues(issues) {
|
|
11
|
+
if (Array.isArray(issues))
|
|
12
|
+
return issues;
|
|
13
|
+
if (issues && typeof issues === "object") {
|
|
14
|
+
const values = Object.values(issues);
|
|
15
|
+
return values.flatMap((value) => Array.isArray(value) ? value : []);
|
|
16
|
+
}
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get overall score from scores object.
|
|
21
|
+
*/
|
|
22
|
+
function getOverallScore(scores) {
|
|
23
|
+
if (!scores)
|
|
24
|
+
return null;
|
|
25
|
+
const direct = scores.overall ?? scores.ux ?? scores.total;
|
|
26
|
+
if (typeof direct === "number" && Number.isFinite(direct))
|
|
27
|
+
return direct;
|
|
28
|
+
const numeric = Object.values(scores)
|
|
29
|
+
.filter((v) => typeof v === "number" && Number.isFinite(v));
|
|
30
|
+
if (numeric.length === 0)
|
|
31
|
+
return null;
|
|
32
|
+
return Math.round(numeric.reduce((a, b) => a + b, 0) / numeric.length);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Escape HTML special characters.
|
|
36
|
+
*/
|
|
37
|
+
function escapeHtml(text) {
|
|
38
|
+
return text
|
|
39
|
+
.replace(/&/g, "&")
|
|
40
|
+
.replace(/</g, "<")
|
|
41
|
+
.replace(/>/g, ">")
|
|
42
|
+
.replace(/"/g, """)
|
|
43
|
+
.replace(/'/g, "'");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get score color class based on value.
|
|
47
|
+
*/
|
|
48
|
+
function getScoreColor(score) {
|
|
49
|
+
if (score >= 90)
|
|
50
|
+
return "score-good";
|
|
51
|
+
if (score >= 70)
|
|
52
|
+
return "score-warning";
|
|
53
|
+
return "score-bad";
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get severity color class.
|
|
57
|
+
*/
|
|
58
|
+
function getSeverityClass(severity) {
|
|
59
|
+
const normalized = severity.toLowerCase();
|
|
60
|
+
if (normalized === "critical" || normalized === "error")
|
|
61
|
+
return "severity-critical";
|
|
62
|
+
if (normalized === "serious" || normalized === "warning")
|
|
63
|
+
return "severity-serious";
|
|
64
|
+
if (normalized === "minor" || normalized === "moderate")
|
|
65
|
+
return "severity-minor";
|
|
66
|
+
return "severity-info";
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get WCAG link from reference string.
|
|
70
|
+
*/
|
|
71
|
+
function getWcagLink(reference) {
|
|
72
|
+
if (!reference)
|
|
73
|
+
return "";
|
|
74
|
+
// Extract WCAG criteria like "2.1.1" or "4.1.2"
|
|
75
|
+
const match = reference.match(/(\d+\.\d+\.\d+)/);
|
|
76
|
+
if (match) {
|
|
77
|
+
const criteria = match[1];
|
|
78
|
+
return `https://www.w3.org/WAI/WCAG21/Understanding/${criteria.replace(/\./g, "")}`;
|
|
79
|
+
}
|
|
80
|
+
// Return reference as-is if it looks like a URL
|
|
81
|
+
if (reference.startsWith("http")) {
|
|
82
|
+
return reference;
|
|
83
|
+
}
|
|
84
|
+
return "";
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Generate the CSS styles for the report.
|
|
88
|
+
*/
|
|
89
|
+
function generateStyles() {
|
|
90
|
+
return `
|
|
91
|
+
:root {
|
|
92
|
+
--color-bg: #0a0f14;
|
|
93
|
+
--color-surface: #121920;
|
|
94
|
+
--color-border: #1e2a36;
|
|
95
|
+
--color-text: #e8eaed;
|
|
96
|
+
--color-text-dim: #8a9299;
|
|
97
|
+
--color-brand: #78FFB4;
|
|
98
|
+
--color-good: #4ade80;
|
|
99
|
+
--color-warning: #fbbf24;
|
|
100
|
+
--color-bad: #f87171;
|
|
101
|
+
--color-info: #60a5fa;
|
|
102
|
+
--font-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
* {
|
|
106
|
+
box-sizing: border-box;
|
|
107
|
+
margin: 0;
|
|
108
|
+
padding: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
body {
|
|
112
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
113
|
+
background: var(--color-bg);
|
|
114
|
+
color: var(--color-text);
|
|
115
|
+
line-height: 1.6;
|
|
116
|
+
padding: 2rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.container {
|
|
120
|
+
max-width: 1200px;
|
|
121
|
+
margin: 0 auto;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Header */
|
|
125
|
+
header {
|
|
126
|
+
border-bottom: 1px solid var(--color-border);
|
|
127
|
+
padding-bottom: 1.5rem;
|
|
128
|
+
margin-bottom: 2rem;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.brand {
|
|
132
|
+
display: flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
gap: 0.75rem;
|
|
135
|
+
margin-bottom: 1rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.brand-icon {
|
|
139
|
+
font-size: 2rem;
|
|
140
|
+
color: var(--color-brand);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.brand-name {
|
|
144
|
+
font-size: 1.5rem;
|
|
145
|
+
font-weight: 700;
|
|
146
|
+
color: var(--color-brand);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.audit-url {
|
|
150
|
+
font-family: var(--font-mono);
|
|
151
|
+
font-size: 0.9rem;
|
|
152
|
+
color: var(--color-text-dim);
|
|
153
|
+
word-break: break-all;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.audit-meta {
|
|
157
|
+
display: flex;
|
|
158
|
+
gap: 1.5rem;
|
|
159
|
+
margin-top: 0.75rem;
|
|
160
|
+
font-size: 0.875rem;
|
|
161
|
+
color: var(--color-text-dim);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Score Cards */
|
|
165
|
+
.scores {
|
|
166
|
+
display: grid;
|
|
167
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
168
|
+
gap: 1rem;
|
|
169
|
+
margin-bottom: 2rem;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.score-card {
|
|
173
|
+
background: var(--color-surface);
|
|
174
|
+
border: 1px solid var(--color-border);
|
|
175
|
+
border-radius: 0.5rem;
|
|
176
|
+
padding: 1.25rem;
|
|
177
|
+
text-align: center;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.score-card.score-overall {
|
|
181
|
+
grid-column: span 2;
|
|
182
|
+
border-color: var(--color-brand);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.score-value {
|
|
186
|
+
font-size: 2.5rem;
|
|
187
|
+
font-weight: 700;
|
|
188
|
+
line-height: 1.2;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.score-label {
|
|
192
|
+
font-size: 0.875rem;
|
|
193
|
+
color: var(--color-text-dim);
|
|
194
|
+
text-transform: uppercase;
|
|
195
|
+
letter-spacing: 0.05em;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.score-good .score-value { color: var(--color-good); }
|
|
199
|
+
.score-warning .score-value { color: var(--color-warning); }
|
|
200
|
+
.score-bad .score-value { color: var(--color-bad); }
|
|
201
|
+
|
|
202
|
+
/* Issues Section */
|
|
203
|
+
.issues-section {
|
|
204
|
+
margin-bottom: 2rem;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.section-header {
|
|
208
|
+
display: flex;
|
|
209
|
+
justify-content: space-between;
|
|
210
|
+
align-items: center;
|
|
211
|
+
margin-bottom: 1rem;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.section-title {
|
|
215
|
+
font-size: 1.25rem;
|
|
216
|
+
font-weight: 600;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.issue-count {
|
|
220
|
+
background: var(--color-surface);
|
|
221
|
+
padding: 0.25rem 0.75rem;
|
|
222
|
+
border-radius: 1rem;
|
|
223
|
+
font-size: 0.875rem;
|
|
224
|
+
color: var(--color-text-dim);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* Issues Table */
|
|
228
|
+
.issues-table {
|
|
229
|
+
width: 100%;
|
|
230
|
+
border-collapse: collapse;
|
|
231
|
+
background: var(--color-surface);
|
|
232
|
+
border-radius: 0.5rem;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.issues-table th,
|
|
237
|
+
.issues-table td {
|
|
238
|
+
padding: 0.75rem 1rem;
|
|
239
|
+
text-align: left;
|
|
240
|
+
border-bottom: 1px solid var(--color-border);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.issues-table th {
|
|
244
|
+
background: rgba(120, 255, 180, 0.05);
|
|
245
|
+
font-weight: 600;
|
|
246
|
+
font-size: 0.875rem;
|
|
247
|
+
text-transform: uppercase;
|
|
248
|
+
letter-spacing: 0.05em;
|
|
249
|
+
color: var(--color-text-dim);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.issues-table tr:last-child td {
|
|
253
|
+
border-bottom: none;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.issues-table tr:hover {
|
|
257
|
+
background: rgba(255, 255, 255, 0.02);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Severity Badges */
|
|
261
|
+
.severity-badge {
|
|
262
|
+
display: inline-block;
|
|
263
|
+
padding: 0.25rem 0.5rem;
|
|
264
|
+
border-radius: 0.25rem;
|
|
265
|
+
font-size: 0.75rem;
|
|
266
|
+
font-weight: 600;
|
|
267
|
+
text-transform: uppercase;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.severity-critical {
|
|
271
|
+
background: rgba(248, 113, 113, 0.15);
|
|
272
|
+
color: var(--color-bad);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.severity-serious {
|
|
276
|
+
background: rgba(251, 191, 36, 0.15);
|
|
277
|
+
color: var(--color-warning);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.severity-minor {
|
|
281
|
+
background: rgba(96, 165, 250, 0.15);
|
|
282
|
+
color: var(--color-info);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.severity-info {
|
|
286
|
+
background: rgba(138, 146, 153, 0.15);
|
|
287
|
+
color: var(--color-text-dim);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* Issue Details */
|
|
291
|
+
.issue-id {
|
|
292
|
+
font-family: var(--font-mono);
|
|
293
|
+
font-size: 0.875rem;
|
|
294
|
+
color: var(--color-brand);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.issue-description {
|
|
298
|
+
max-width: 400px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.issue-selector {
|
|
302
|
+
font-family: var(--font-mono);
|
|
303
|
+
font-size: 0.75rem;
|
|
304
|
+
color: var(--color-text-dim);
|
|
305
|
+
max-width: 200px;
|
|
306
|
+
overflow: hidden;
|
|
307
|
+
text-overflow: ellipsis;
|
|
308
|
+
white-space: nowrap;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.wcag-link {
|
|
312
|
+
color: var(--color-info);
|
|
313
|
+
text-decoration: none;
|
|
314
|
+
font-size: 0.875rem;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.wcag-link:hover {
|
|
318
|
+
text-decoration: underline;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* Collapsible Details */
|
|
322
|
+
.issue-row {
|
|
323
|
+
cursor: pointer;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.issue-details {
|
|
327
|
+
display: none;
|
|
328
|
+
background: rgba(0, 0, 0, 0.2);
|
|
329
|
+
padding: 1rem;
|
|
330
|
+
border-bottom: 1px solid var(--color-border);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.issue-details.expanded {
|
|
334
|
+
display: block;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.detail-label {
|
|
338
|
+
font-size: 0.75rem;
|
|
339
|
+
text-transform: uppercase;
|
|
340
|
+
color: var(--color-text-dim);
|
|
341
|
+
margin-bottom: 0.25rem;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.detail-value {
|
|
345
|
+
margin-bottom: 1rem;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* Footer */
|
|
349
|
+
footer {
|
|
350
|
+
border-top: 1px solid var(--color-border);
|
|
351
|
+
padding-top: 1.5rem;
|
|
352
|
+
margin-top: 2rem;
|
|
353
|
+
display: flex;
|
|
354
|
+
justify-content: space-between;
|
|
355
|
+
align-items: center;
|
|
356
|
+
font-size: 0.875rem;
|
|
357
|
+
color: var(--color-text-dim);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.footer-brand {
|
|
361
|
+
display: flex;
|
|
362
|
+
align-items: center;
|
|
363
|
+
gap: 0.5rem;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.footer-brand a {
|
|
367
|
+
color: var(--color-brand);
|
|
368
|
+
text-decoration: none;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* Print Styles */
|
|
372
|
+
@media print {
|
|
373
|
+
body {
|
|
374
|
+
background: white;
|
|
375
|
+
color: #1a1a1a;
|
|
376
|
+
padding: 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.score-card {
|
|
380
|
+
background: #f5f5f5;
|
|
381
|
+
border-color: #ddd;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.score-good .score-value { color: #22c55e; }
|
|
385
|
+
.score-warning .score-value { color: #d97706; }
|
|
386
|
+
.score-bad .score-value { color: #dc2626; }
|
|
387
|
+
|
|
388
|
+
.issues-table {
|
|
389
|
+
background: white;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.issues-table th {
|
|
393
|
+
background: #f5f5f5;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.severity-critical { background: #fef2f2; color: #dc2626; }
|
|
397
|
+
.severity-serious { background: #fffbeb; color: #d97706; }
|
|
398
|
+
.severity-minor { background: #eff6ff; color: #2563eb; }
|
|
399
|
+
|
|
400
|
+
footer {
|
|
401
|
+
page-break-inside: avoid;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
`;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Generate interactive JavaScript for the report.
|
|
408
|
+
*/
|
|
409
|
+
function generateScript() {
|
|
410
|
+
return `
|
|
411
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
412
|
+
// Toggle issue details
|
|
413
|
+
document.querySelectorAll('.issue-row').forEach(function(row) {
|
|
414
|
+
row.addEventListener('click', function() {
|
|
415
|
+
const details = this.nextElementSibling;
|
|
416
|
+
if (details && details.classList.contains('issue-details')) {
|
|
417
|
+
details.classList.toggle('expanded');
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Sort table by column
|
|
423
|
+
document.querySelectorAll('.issues-table th[data-sortable]').forEach(function(th) {
|
|
424
|
+
th.style.cursor = 'pointer';
|
|
425
|
+
th.addEventListener('click', function() {
|
|
426
|
+
const table = this.closest('table');
|
|
427
|
+
const tbody = table.querySelector('tbody');
|
|
428
|
+
const rows = Array.from(tbody.querySelectorAll('tr.issue-row'));
|
|
429
|
+
const colIndex = Array.from(th.parentNode.children).indexOf(th);
|
|
430
|
+
const isAsc = th.getAttribute('data-sort') !== 'asc';
|
|
431
|
+
|
|
432
|
+
rows.sort(function(a, b) {
|
|
433
|
+
const aVal = a.children[colIndex].textContent.trim();
|
|
434
|
+
const bVal = b.children[colIndex].textContent.trim();
|
|
435
|
+
return isAsc ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
th.setAttribute('data-sort', isAsc ? 'asc' : 'desc');
|
|
439
|
+
|
|
440
|
+
// Re-insert sorted rows (with their details)
|
|
441
|
+
rows.forEach(function(row) {
|
|
442
|
+
const details = row.nextElementSibling;
|
|
443
|
+
tbody.appendChild(row);
|
|
444
|
+
if (details && details.classList.contains('issue-details')) {
|
|
445
|
+
tbody.appendChild(details);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
`;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Generate score cards HTML.
|
|
455
|
+
*/
|
|
456
|
+
function generateScoreCards(scores) {
|
|
457
|
+
if (!scores)
|
|
458
|
+
return "";
|
|
459
|
+
const overall = getOverallScore(scores);
|
|
460
|
+
const cards = [];
|
|
461
|
+
// Overall score card
|
|
462
|
+
if (overall !== null) {
|
|
463
|
+
cards.push(`
|
|
464
|
+
<div class="score-card score-overall ${getScoreColor(overall)}">
|
|
465
|
+
<div class="score-value">${overall}</div>
|
|
466
|
+
<div class="score-label">Overall Score</div>
|
|
467
|
+
</div>
|
|
468
|
+
`);
|
|
469
|
+
}
|
|
470
|
+
// Category scores
|
|
471
|
+
for (const [key, value] of Object.entries(scores)) {
|
|
472
|
+
if (key === "overall" || typeof value !== "number")
|
|
473
|
+
continue;
|
|
474
|
+
const score = value;
|
|
475
|
+
cards.push(`
|
|
476
|
+
<div class="score-card ${getScoreColor(score)}">
|
|
477
|
+
<div class="score-value">${score}</div>
|
|
478
|
+
<div class="score-label">${escapeHtml(key)}</div>
|
|
479
|
+
</div>
|
|
480
|
+
`);
|
|
481
|
+
}
|
|
482
|
+
return `<div class="scores">${cards.join("")}</div>`;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Generate issues table HTML.
|
|
486
|
+
*/
|
|
487
|
+
function generateIssuesTable(issues, interactive) {
|
|
488
|
+
if (issues.length === 0) {
|
|
489
|
+
return `<p style="color: var(--color-good);">No issues found!</p>`;
|
|
490
|
+
}
|
|
491
|
+
const rows = issues.map((issue) => {
|
|
492
|
+
const severity = issue.severity || "info";
|
|
493
|
+
const wcagLink = getWcagLink(issue.wcag_reference);
|
|
494
|
+
const recommendation = issue.recommendation || issue.recommended_fix || "";
|
|
495
|
+
const mainRow = `
|
|
496
|
+
<tr class="issue-row">
|
|
497
|
+
<td>
|
|
498
|
+
<span class="severity-badge ${getSeverityClass(severity)}">
|
|
499
|
+
${escapeHtml(severity)}
|
|
500
|
+
</span>
|
|
501
|
+
</td>
|
|
502
|
+
<td class="issue-id">${escapeHtml(issue.id || "-")}</td>
|
|
503
|
+
<td class="issue-description">${escapeHtml(issue.description || issue.title || "-")}</td>
|
|
504
|
+
<td class="issue-selector" title="${escapeHtml(issue.selector || "")}">${escapeHtml(issue.selector || "-")}</td>
|
|
505
|
+
<td>
|
|
506
|
+
${wcagLink ? `<a class="wcag-link" href="${wcagLink}" target="_blank">${escapeHtml(issue.wcag_reference || "")}</a>` : escapeHtml(issue.wcag_reference || "-")}
|
|
507
|
+
</td>
|
|
508
|
+
</tr>
|
|
509
|
+
`;
|
|
510
|
+
const detailsRow = interactive && recommendation ? `
|
|
511
|
+
<tr class="issue-details">
|
|
512
|
+
<td colspan="5">
|
|
513
|
+
<div class="detail-label">Recommendation</div>
|
|
514
|
+
<div class="detail-value">${escapeHtml(recommendation)}</div>
|
|
515
|
+
${issue.selector ? `
|
|
516
|
+
<div class="detail-label">Selector</div>
|
|
517
|
+
<div class="detail-value" style="font-family: var(--font-mono); font-size: 0.875rem;">${escapeHtml(issue.selector)}</div>
|
|
518
|
+
` : ""}
|
|
519
|
+
</td>
|
|
520
|
+
</tr>
|
|
521
|
+
` : "";
|
|
522
|
+
return mainRow + detailsRow;
|
|
523
|
+
});
|
|
524
|
+
return `
|
|
525
|
+
<table class="issues-table">
|
|
526
|
+
<thead>
|
|
527
|
+
<tr>
|
|
528
|
+
<th data-sortable>Severity</th>
|
|
529
|
+
<th data-sortable>ID</th>
|
|
530
|
+
<th>Description</th>
|
|
531
|
+
<th>Selector</th>
|
|
532
|
+
<th>WCAG</th>
|
|
533
|
+
</tr>
|
|
534
|
+
</thead>
|
|
535
|
+
<tbody>
|
|
536
|
+
${rows.join("")}
|
|
537
|
+
</tbody>
|
|
538
|
+
</table>
|
|
539
|
+
`;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Format audit result as standalone HTML report.
|
|
543
|
+
*
|
|
544
|
+
* @param result - Audit result object
|
|
545
|
+
* @param options - Formatting options
|
|
546
|
+
* @returns Complete HTML document string
|
|
547
|
+
*/
|
|
548
|
+
export function formatAuditHtml(result, options = {}) {
|
|
549
|
+
const { title = "VertaaUX Audit Report", interactive = true } = options;
|
|
550
|
+
const issues = normalizeIssues(result.issues);
|
|
551
|
+
const timestamp = new Date().toISOString();
|
|
552
|
+
const auditDate = result.completed_at
|
|
553
|
+
? new Date(result.completed_at).toLocaleDateString()
|
|
554
|
+
: new Date().toLocaleDateString();
|
|
555
|
+
// Count issues by severity
|
|
556
|
+
const severityCounts = {};
|
|
557
|
+
for (const issue of issues) {
|
|
558
|
+
const sev = issue.severity?.toLowerCase() || "info";
|
|
559
|
+
severityCounts[sev] = (severityCounts[sev] || 0) + 1;
|
|
560
|
+
}
|
|
561
|
+
return `<!DOCTYPE html>
|
|
562
|
+
<html lang="en">
|
|
563
|
+
<head>
|
|
564
|
+
<meta charset="UTF-8">
|
|
565
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
566
|
+
<title>${escapeHtml(title)}</title>
|
|
567
|
+
<style>${generateStyles()}</style>
|
|
568
|
+
</head>
|
|
569
|
+
<body>
|
|
570
|
+
<div class="container">
|
|
571
|
+
<header>
|
|
572
|
+
<div class="brand">
|
|
573
|
+
<span class="brand-icon">V</span>
|
|
574
|
+
<span class="brand-name">VertaaUX</span>
|
|
575
|
+
</div>
|
|
576
|
+
<div class="audit-url">${escapeHtml(result.url || "Unknown URL")}</div>
|
|
577
|
+
<div class="audit-meta">
|
|
578
|
+
<span>Mode: ${escapeHtml(result.mode || "basic")}</span>
|
|
579
|
+
<span>Date: ${auditDate}</span>
|
|
580
|
+
${result.job_id ? `<span>Job: ${escapeHtml(result.job_id)}</span>` : ""}
|
|
581
|
+
</div>
|
|
582
|
+
</header>
|
|
583
|
+
|
|
584
|
+
${generateScoreCards(result.scores)}
|
|
585
|
+
|
|
586
|
+
<section class="issues-section">
|
|
587
|
+
<div class="section-header">
|
|
588
|
+
<h2 class="section-title">Issues Found</h2>
|
|
589
|
+
<span class="issue-count">${issues.length} total</span>
|
|
590
|
+
</div>
|
|
591
|
+
${generateIssuesTable(issues, interactive)}
|
|
592
|
+
</section>
|
|
593
|
+
|
|
594
|
+
<footer>
|
|
595
|
+
<div class="footer-brand">
|
|
596
|
+
Generated by <a href="https://vertaaux.ai">VertaaUX</a>
|
|
597
|
+
</div>
|
|
598
|
+
<div class="footer-timestamp">
|
|
599
|
+
${timestamp}
|
|
600
|
+
</div>
|
|
601
|
+
</footer>
|
|
602
|
+
</div>
|
|
603
|
+
|
|
604
|
+
${interactive ? `<script>${generateScript()}</script>` : ""}
|
|
605
|
+
</body>
|
|
606
|
+
</html>`;
|
|
607
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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 { type FormatTableOptions } from "../ui/table.js";
|
|
8
|
+
export interface AuditResult {
|
|
9
|
+
job_id?: string;
|
|
10
|
+
status?: string;
|
|
11
|
+
url?: string;
|
|
12
|
+
mode?: string;
|
|
13
|
+
progress?: number;
|
|
14
|
+
created_at?: string;
|
|
15
|
+
started_at?: string;
|
|
16
|
+
completed_at?: string;
|
|
17
|
+
scores?: Record<string, unknown>;
|
|
18
|
+
issues?: unknown;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface FormatHumanOptions {
|
|
22
|
+
/** How to group issues: severity (default), category, route */
|
|
23
|
+
groupBy?: "severity" | "category" | "route";
|
|
24
|
+
/** Whether to show detailed scores table */
|
|
25
|
+
showScores?: boolean;
|
|
26
|
+
/** Whether to show the summary at the end */
|
|
27
|
+
showSummary?: boolean;
|
|
28
|
+
/** Maximum issues to display per group */
|
|
29
|
+
maxIssuesPerGroup?: number;
|
|
30
|
+
/** Table formatting options */
|
|
31
|
+
tableOptions?: FormatTableOptions;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Format audit result as human-readable string.
|
|
35
|
+
*
|
|
36
|
+
* @param result - Audit result object
|
|
37
|
+
* @param options - Formatting options
|
|
38
|
+
* @returns Formatted string for terminal display
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatAuditHuman(result: AuditResult, options?: FormatHumanOptions): string;
|
|
41
|
+
//# sourceMappingURL=human.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human.d.ts","sourceRoot":"","sources":["../../src/output/human.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAGxB,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,kBAAkB;IACjC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,4CAA4C;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+BAA+B;IAC/B,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAmRD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAkDR"}
|