@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,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, "&")
|
|
21
|
+
.replace(/</g, "<")
|
|
22
|
+
.replace(/>/g, ">")
|
|
23
|
+
.replace(/"/g, """)
|
|
24
|
+
.replace(/'/g, "'");
|
|
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"}
|