@kb-labs/qa-core 0.6.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 +120 -0
- package/dist/baseline/index.d.ts +27 -0
- package/dist/baseline/index.js +762 -0
- package/dist/baseline/index.js.map +1 -0
- package/dist/categories/index.d.ts +13 -0
- package/dist/categories/index.js +39 -0
- package/dist/categories/index.js.map +1 -0
- package/dist/history/index.d.ts +47 -0
- package/dist/history/index.js +324 -0
- package/dist/history/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1550 -0
- package/dist/index.js.map +1 -0
- package/dist/last-run-store-CQ_6ai40.d.ts +78 -0
- package/dist/report/index.d.ts +51 -0
- package/dist/report/index.js +423 -0
- package/dist/report/index.js.map +1 -0
- package/dist/runner/index.d.ts +43 -0
- package/dist/runner/index.js +713 -0
- package/dist/runner/index.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { getCheckIcon, getCheckLabel } from '@kb-labs/qa-contracts';
|
|
2
|
+
|
|
3
|
+
// src/report/json-reporter.ts
|
|
4
|
+
function buildJsonReport(results, diff) {
|
|
5
|
+
const hasFailures = Object.values(results).some((r) => r.failed.length > 0);
|
|
6
|
+
const summary = {};
|
|
7
|
+
const failures = {};
|
|
8
|
+
const errors = {};
|
|
9
|
+
for (const ct of Object.keys(results)) {
|
|
10
|
+
const r = results[ct];
|
|
11
|
+
const total = r.passed.length + r.failed.length + r.skipped.length;
|
|
12
|
+
summary[ct] = {
|
|
13
|
+
total,
|
|
14
|
+
passed: r.passed.length,
|
|
15
|
+
failed: r.failed.length,
|
|
16
|
+
skipped: r.skipped.length
|
|
17
|
+
};
|
|
18
|
+
failures[ct] = [...r.failed];
|
|
19
|
+
errors[ct] = { ...r.errors };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
status: hasFailures ? "failed" : "passed",
|
|
23
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24
|
+
summary,
|
|
25
|
+
failures,
|
|
26
|
+
errors,
|
|
27
|
+
baseline: diff ?? null
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function buildDetailedJsonReport(results, grouped, diff) {
|
|
31
|
+
const base = buildJsonReport(results, diff);
|
|
32
|
+
return { ...base, grouped };
|
|
33
|
+
}
|
|
34
|
+
function icon(ct) {
|
|
35
|
+
return getCheckIcon(ct);
|
|
36
|
+
}
|
|
37
|
+
function label(ct) {
|
|
38
|
+
return getCheckLabel(ct);
|
|
39
|
+
}
|
|
40
|
+
function buildBaselineDiffLines(diff) {
|
|
41
|
+
const lines = [];
|
|
42
|
+
for (const ct of Object.keys(diff)) {
|
|
43
|
+
const d = diff[ct];
|
|
44
|
+
if (d.newFailures.length > 0) {
|
|
45
|
+
lines.push(`${icon(ct)} ${label(ct)}: +${d.newFailures.length} new failures`);
|
|
46
|
+
for (const pkg of d.newFailures) {
|
|
47
|
+
lines.push(` - ${pkg}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (d.fixed.length > 0) {
|
|
51
|
+
lines.push(`${icon(ct)} ${label(ct)}: -${d.fixed.length} fixed`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return lines;
|
|
55
|
+
}
|
|
56
|
+
function buildRunReport(results, diff) {
|
|
57
|
+
const sections = [];
|
|
58
|
+
const summaryLines = [];
|
|
59
|
+
let totalPassed = 0;
|
|
60
|
+
let totalFailed = 0;
|
|
61
|
+
let totalSkipped = 0;
|
|
62
|
+
for (const ct of Object.keys(results)) {
|
|
63
|
+
const r = results[ct];
|
|
64
|
+
const total = r.passed.length + r.failed.length + r.skipped.length;
|
|
65
|
+
const pct = total > 0 ? Math.round(r.passed.length / total * 100) : 100;
|
|
66
|
+
const status = r.failed.length === 0 ? "PASS" : "FAIL";
|
|
67
|
+
summaryLines.push(`${status} ${icon(ct)} ${label(ct).padEnd(12)} ${r.passed.length}/${total} passed (${pct}%)`);
|
|
68
|
+
if (r.failed.length > 0) {
|
|
69
|
+
for (const pkg of r.failed.slice(0, 5)) {
|
|
70
|
+
summaryLines.push(` - ${pkg}`);
|
|
71
|
+
}
|
|
72
|
+
if (r.failed.length > 5) {
|
|
73
|
+
summaryLines.push(` ... and ${r.failed.length - 5} more`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
totalPassed += r.passed.length;
|
|
77
|
+
totalFailed += r.failed.length;
|
|
78
|
+
totalSkipped += r.skipped.length;
|
|
79
|
+
}
|
|
80
|
+
sections.push({ header: "QA Summary Report", lines: summaryLines });
|
|
81
|
+
if (diff) {
|
|
82
|
+
const diffLines = buildBaselineDiffLines(diff);
|
|
83
|
+
if (diffLines.length > 0) {
|
|
84
|
+
sections.push({ header: "Baseline Comparison", lines: diffLines });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
sections.push({
|
|
88
|
+
header: "Totals",
|
|
89
|
+
lines: [`Total: ${totalPassed} passed, ${totalFailed} failed, ${totalSkipped} skipped`]
|
|
90
|
+
});
|
|
91
|
+
return sections;
|
|
92
|
+
}
|
|
93
|
+
function buildHistoryTable(history, limit = 20) {
|
|
94
|
+
const entries = history.slice(-limit);
|
|
95
|
+
const lines = [];
|
|
96
|
+
for (const entry of entries) {
|
|
97
|
+
const date = new Date(entry.timestamp).toLocaleDateString();
|
|
98
|
+
const status = entry.status === "passed" ? "PASS" : "FAIL";
|
|
99
|
+
const summary = Object.keys(entry.summary).map((ct) => {
|
|
100
|
+
const s = entry.summary[ct];
|
|
101
|
+
return `${icon(ct)} ${s.failed}F`;
|
|
102
|
+
}).join(" ");
|
|
103
|
+
lines.push(`${date} ${entry.git.commit} ${status} ${summary} ${entry.git.message.slice(0, 40)}`);
|
|
104
|
+
}
|
|
105
|
+
return [{ header: `QA History (last ${entries.length})`, lines }];
|
|
106
|
+
}
|
|
107
|
+
function buildTrendsReport(trends, history) {
|
|
108
|
+
if (trends.length === 0) {
|
|
109
|
+
return [{ header: "QA Trends", lines: ["Not enough history (need at least 2 entries)"] }];
|
|
110
|
+
}
|
|
111
|
+
const lines = [];
|
|
112
|
+
for (const t of trends) {
|
|
113
|
+
const arrow = t.delta > 0 ? `+${t.delta} (regression)` : t.delta < 0 ? `${t.delta} (improvement)` : "\u2192 no change";
|
|
114
|
+
lines.push(`${icon(t.checkType)} ${label(t.checkType).padEnd(12)} ${t.previous} \u2192 ${t.current} ${arrow}`);
|
|
115
|
+
}
|
|
116
|
+
if (history.length >= 2) {
|
|
117
|
+
const first = history[Math.max(0, history.length - 10)];
|
|
118
|
+
const last = history[history.length - 1];
|
|
119
|
+
lines.push("");
|
|
120
|
+
lines.push(`Period: ${new Date(first.timestamp).toLocaleDateString()} \u2192 ${new Date(last.timestamp).toLocaleDateString()}`);
|
|
121
|
+
}
|
|
122
|
+
return [{ header: "QA Trends", lines }];
|
|
123
|
+
}
|
|
124
|
+
function buildRegressionsReport(result, history) {
|
|
125
|
+
if (history.length < 2) {
|
|
126
|
+
return [{ header: "Regression Detection", lines: ["Not enough history (need at least 2 entries)"] }];
|
|
127
|
+
}
|
|
128
|
+
const prev = history[history.length - 2];
|
|
129
|
+
const curr = history[history.length - 1];
|
|
130
|
+
const lines = [
|
|
131
|
+
`Comparing: ${prev.git.commit} \u2192 ${curr.git.commit}`,
|
|
132
|
+
""
|
|
133
|
+
];
|
|
134
|
+
if (!result.hasRegressions) {
|
|
135
|
+
lines.push("No regressions detected.");
|
|
136
|
+
return [{ header: "Regression Detection", lines }];
|
|
137
|
+
}
|
|
138
|
+
for (const r of result.regressions) {
|
|
139
|
+
lines.push(`${r.checkType}: +${r.newFailures.length} new failures`);
|
|
140
|
+
for (const pkg of r.newFailures) {
|
|
141
|
+
lines.push(` - ${pkg}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
lines.push("");
|
|
145
|
+
lines.push("REGRESSIONS DETECTED!");
|
|
146
|
+
return [{ header: "Regression Detection", lines }];
|
|
147
|
+
}
|
|
148
|
+
function buildBaselineReport(baseline) {
|
|
149
|
+
if (!baseline) {
|
|
150
|
+
return [{ header: "Baseline Status", lines: ["No baseline captured yet. Run baseline:update first."] }];
|
|
151
|
+
}
|
|
152
|
+
const lines = [
|
|
153
|
+
`Captured: ${new Date(baseline.timestamp).toLocaleString()}`,
|
|
154
|
+
`Git: ${baseline.git.commit} (${baseline.git.branch})`,
|
|
155
|
+
""
|
|
156
|
+
];
|
|
157
|
+
for (const ct of Object.keys(baseline.results)) {
|
|
158
|
+
const r = baseline.results[ct];
|
|
159
|
+
lines.push(`${icon(ct)} ${label(ct).padEnd(12)} ${r.passed} passed, ${r.failed} failed`);
|
|
160
|
+
if (r.failedPackages.length > 0) {
|
|
161
|
+
const shown = r.failedPackages.slice(0, 3);
|
|
162
|
+
for (const pkg of shown) {
|
|
163
|
+
lines.push(` - ${pkg}`);
|
|
164
|
+
}
|
|
165
|
+
if (r.failedPackages.length > 3) {
|
|
166
|
+
lines.push(` ... and ${r.failedPackages.length - 3} more`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return [{ header: "Baseline Status", lines }];
|
|
171
|
+
}
|
|
172
|
+
function checkTag(status, ct) {
|
|
173
|
+
const short = ct === "typeCheck" ? "types" : ct;
|
|
174
|
+
if (status === "failed") {
|
|
175
|
+
return short.toUpperCase();
|
|
176
|
+
}
|
|
177
|
+
if (status === "skipped") {
|
|
178
|
+
return `-${short}-`;
|
|
179
|
+
}
|
|
180
|
+
return short;
|
|
181
|
+
}
|
|
182
|
+
function getErrorPreview(raw) {
|
|
183
|
+
const errLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
184
|
+
for (const el of errLines) {
|
|
185
|
+
const cleaned = el.replace(/^Command failed: .*/, "").trim();
|
|
186
|
+
if (cleaned.length > 0) {
|
|
187
|
+
return cleaned.replace(/\/[^\s]*\/kb-labs\//g, "").slice(0, 100);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
function renderPackageLines(pkg, lines) {
|
|
193
|
+
const hasFail = Object.values(pkg.checks).some((v) => v === "failed");
|
|
194
|
+
const status = hasFail ? "FAIL" : "PASS";
|
|
195
|
+
const tags = Object.keys(pkg.checks).map((ct) => checkTag(pkg.checks[ct], ct)).join(" ");
|
|
196
|
+
lines.push(` ${status} ${pkg.name.padEnd(40)} ${tags}`);
|
|
197
|
+
if (hasFail) {
|
|
198
|
+
for (const ct of Object.keys(pkg.checks)) {
|
|
199
|
+
if (pkg.checks[ct] === "failed") {
|
|
200
|
+
const preview = getErrorPreview((pkg.errors[ct] ?? "").trim());
|
|
201
|
+
lines.push(` ${ct}: ${preview || "failed"}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function renderCategoryLines(catKey, grouped) {
|
|
207
|
+
const cat = grouped.categories[catKey];
|
|
208
|
+
const lines = [`PASS ${cat.summary.passed} | FAIL ${cat.summary.failed}`, ""];
|
|
209
|
+
for (const repoKey of Object.keys(cat.repos).sort()) {
|
|
210
|
+
const repo = cat.repos[repoKey];
|
|
211
|
+
lines.push(` ${repoKey} (${repo.summary.total} packages)`);
|
|
212
|
+
const sorted = [...repo.packages].sort((a, b) => {
|
|
213
|
+
const aFail = Object.values(a.checks).some((v) => v === "failed") ? 0 : 1;
|
|
214
|
+
const bFail = Object.values(b.checks).some((v) => v === "failed") ? 0 : 1;
|
|
215
|
+
if (aFail !== bFail) {
|
|
216
|
+
return aFail - bFail;
|
|
217
|
+
}
|
|
218
|
+
return a.name.localeCompare(b.name);
|
|
219
|
+
});
|
|
220
|
+
for (const pkg of sorted) {
|
|
221
|
+
renderPackageLines(pkg, lines);
|
|
222
|
+
}
|
|
223
|
+
lines.push("");
|
|
224
|
+
}
|
|
225
|
+
return lines;
|
|
226
|
+
}
|
|
227
|
+
function buildDetailedRunReport(grouped, diff) {
|
|
228
|
+
const sections = [];
|
|
229
|
+
const categoryKeys = Object.keys(grouped.categories).sort((a, b) => {
|
|
230
|
+
if (a === "uncategorized") {
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
if (b === "uncategorized") {
|
|
234
|
+
return -1;
|
|
235
|
+
}
|
|
236
|
+
return a.localeCompare(b);
|
|
237
|
+
});
|
|
238
|
+
for (const catKey of categoryKeys) {
|
|
239
|
+
const cat = grouped.categories[catKey];
|
|
240
|
+
sections.push({ header: `${cat.label} (${cat.summary.total} packages)`, lines: renderCategoryLines(catKey, grouped) });
|
|
241
|
+
}
|
|
242
|
+
if (diff) {
|
|
243
|
+
const diffLines = buildBaselineDiffLines(diff);
|
|
244
|
+
if (diffLines.length > 0) {
|
|
245
|
+
sections.push({ header: "Baseline Comparison", lines: diffLines });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
let totalPassed = 0;
|
|
249
|
+
let totalFailed = 0;
|
|
250
|
+
for (const catKey of categoryKeys) {
|
|
251
|
+
totalPassed += grouped.categories[catKey].summary.passed;
|
|
252
|
+
totalFailed += grouped.categories[catKey].summary.failed;
|
|
253
|
+
}
|
|
254
|
+
sections.push({
|
|
255
|
+
header: "Totals",
|
|
256
|
+
lines: [`Total: ${totalPassed} passed, ${totalFailed} failed (${categoryKeys.length} categories)`]
|
|
257
|
+
});
|
|
258
|
+
return sections;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/report/grouped-reporter.ts
|
|
262
|
+
function emptyGroupSummary(checkTypes) {
|
|
263
|
+
const checks = {};
|
|
264
|
+
for (const ct of checkTypes) {
|
|
265
|
+
checks[ct] = { passed: 0, failed: 0, skipped: 0 };
|
|
266
|
+
}
|
|
267
|
+
return { total: 0, passed: 0, failed: 0, checks };
|
|
268
|
+
}
|
|
269
|
+
function resolveCheckStatus(pkgName, ct, results) {
|
|
270
|
+
const r = results[ct];
|
|
271
|
+
if (!r) {
|
|
272
|
+
return "skipped";
|
|
273
|
+
}
|
|
274
|
+
if (r.failed.includes(pkgName)) {
|
|
275
|
+
return "failed";
|
|
276
|
+
}
|
|
277
|
+
if (r.passed.includes(pkgName)) {
|
|
278
|
+
return "passed";
|
|
279
|
+
}
|
|
280
|
+
return "skipped";
|
|
281
|
+
}
|
|
282
|
+
function buildPackageStatus(pkg, results, category) {
|
|
283
|
+
const checks = {};
|
|
284
|
+
const errors = {};
|
|
285
|
+
for (const ct of Object.keys(results)) {
|
|
286
|
+
checks[ct] = resolveCheckStatus(pkg.name, ct, results);
|
|
287
|
+
if (checks[ct] === "failed" && results[ct]?.errors[pkg.name]) {
|
|
288
|
+
errors[ct] = results[ct].errors[pkg.name];
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
name: pkg.name,
|
|
293
|
+
repo: pkg.repo,
|
|
294
|
+
category,
|
|
295
|
+
checks,
|
|
296
|
+
errors
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function addToSummary(summary, status) {
|
|
300
|
+
summary.total++;
|
|
301
|
+
const hasFail = Object.values(status.checks).some((v) => v === "failed");
|
|
302
|
+
if (hasFail) {
|
|
303
|
+
summary.failed++;
|
|
304
|
+
} else {
|
|
305
|
+
summary.passed++;
|
|
306
|
+
}
|
|
307
|
+
for (const ct of Object.keys(status.checks)) {
|
|
308
|
+
const s = status.checks[ct];
|
|
309
|
+
if (!summary.checks[ct]) {
|
|
310
|
+
summary.checks[ct] = { passed: 0, failed: 0, skipped: 0 };
|
|
311
|
+
}
|
|
312
|
+
if (s === "passed") {
|
|
313
|
+
summary.checks[ct].passed++;
|
|
314
|
+
} else if (s === "failed") {
|
|
315
|
+
summary.checks[ct].failed++;
|
|
316
|
+
} else {
|
|
317
|
+
summary.checks[ct].skipped++;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function groupResults(results, packages, categoryMap, config) {
|
|
322
|
+
const checkTypes = Object.keys(results);
|
|
323
|
+
const grouped = { categories: {} };
|
|
324
|
+
for (const pkg of packages) {
|
|
325
|
+
const categoryKeys = categoryMap.get(pkg.name) ?? ["uncategorized"];
|
|
326
|
+
for (const categoryKey of categoryKeys) {
|
|
327
|
+
const status = buildPackageStatus(pkg, results, categoryKey);
|
|
328
|
+
if (!grouped.categories[categoryKey]) {
|
|
329
|
+
const label2 = categoryKey === "uncategorized" ? "Uncategorized" : config?.categories?.[categoryKey]?.label ?? categoryKey;
|
|
330
|
+
grouped.categories[categoryKey] = {
|
|
331
|
+
label: label2,
|
|
332
|
+
repos: {},
|
|
333
|
+
summary: emptyGroupSummary(checkTypes)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const categoryGroup = grouped.categories[categoryKey];
|
|
337
|
+
if (!categoryGroup.repos[pkg.repo]) {
|
|
338
|
+
categoryGroup.repos[pkg.repo] = {
|
|
339
|
+
packages: [],
|
|
340
|
+
summary: emptyGroupSummary(checkTypes)
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const repoGroup = categoryGroup.repos[pkg.repo];
|
|
344
|
+
repoGroup.packages.push(status);
|
|
345
|
+
addToSummary(repoGroup.summary, status);
|
|
346
|
+
addToSummary(categoryGroup.summary, status);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return grouped;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/report/error-grouping.ts
|
|
353
|
+
function extractPattern(errorText, checkType) {
|
|
354
|
+
if (checkType === "lint") {
|
|
355
|
+
const ruleMatch = errorText.match(/(\S+\/[\w-]+|no-[\w-]+)/);
|
|
356
|
+
if (ruleMatch?.[1]) {
|
|
357
|
+
return ruleMatch[1];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (checkType === "typeCheck") {
|
|
361
|
+
const tsMatch = errorText.match(/TS(\d{4,5})/);
|
|
362
|
+
if (tsMatch) {
|
|
363
|
+
return `TS${tsMatch[1]}`;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (checkType === "test") {
|
|
367
|
+
const failMatch = errorText.match(/FAIL\s+(\S+)/);
|
|
368
|
+
if (failMatch) {
|
|
369
|
+
return `FAIL: ${failMatch[1]}`;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (checkType === "build") {
|
|
373
|
+
if (errorText.includes("Cannot find module")) {
|
|
374
|
+
return "Cannot find module";
|
|
375
|
+
}
|
|
376
|
+
if (errorText.includes("Module not found")) {
|
|
377
|
+
return "Module not found";
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const firstLine = errorText.split("\n").find((l) => l.trim().length > 0)?.trim() ?? "";
|
|
381
|
+
return firstLine.slice(0, 100) || "Unknown error";
|
|
382
|
+
}
|
|
383
|
+
function groupErrors(results) {
|
|
384
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
385
|
+
let ungrouped = 0;
|
|
386
|
+
for (const ct of Object.keys(results)) {
|
|
387
|
+
const check = results[ct];
|
|
388
|
+
if (!check.errors) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
for (const [pkgName, errorText] of Object.entries(check.errors)) {
|
|
392
|
+
const pattern = extractPattern(errorText, ct);
|
|
393
|
+
const key = `${ct}::${pattern}`;
|
|
394
|
+
const existing = groupMap.get(key);
|
|
395
|
+
if (existing) {
|
|
396
|
+
existing.count++;
|
|
397
|
+
existing.packages.push(pkgName);
|
|
398
|
+
} else {
|
|
399
|
+
groupMap.set(key, {
|
|
400
|
+
pattern,
|
|
401
|
+
count: 1,
|
|
402
|
+
packages: [pkgName],
|
|
403
|
+
checkType: ct,
|
|
404
|
+
example: errorText.slice(0, 200)
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const groups = [];
|
|
410
|
+
for (const group of groupMap.values()) {
|
|
411
|
+
if (group.count === 1) {
|
|
412
|
+
ungrouped++;
|
|
413
|
+
} else {
|
|
414
|
+
groups.push(group);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
groups.sort((a, b) => b.count - a.count);
|
|
418
|
+
return { groups, ungrouped };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export { buildBaselineReport, buildDetailedJsonReport, buildDetailedRunReport, buildHistoryTable, buildJsonReport, buildRegressionsReport, buildRunReport, buildTrendsReport, groupErrors, groupResults };
|
|
422
|
+
//# sourceMappingURL=index.js.map
|
|
423
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/report/json-reporter.ts","../../src/report/text-reporter.ts","../../src/report/grouped-reporter.ts","../../src/report/error-grouping.ts"],"names":["label"],"mappings":"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","file":"index.js","sourcesContent":["import type { QAResults, QAReport, BaselineDiff, GroupedResults } from '@kb-labs/qa-contracts';\n\n/**\n * Build a structured JSON report from QA results.\n */\nexport function buildJsonReport(\n results: QAResults,\n diff?: BaselineDiff | null,\n): QAReport {\n const hasFailures = Object.values(results).some((r) => r.failed.length > 0);\n\n const summary = {} as QAReport['summary'];\n const failures = {} as QAReport['failures'];\n const errors = {} as QAReport['errors'];\n\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n const total = r.passed.length + r.failed.length + r.skipped.length;\n summary[ct] = {\n total,\n passed: r.passed.length,\n failed: r.failed.length,\n skipped: r.skipped.length,\n };\n failures[ct] = [...r.failed];\n errors[ct] = { ...r.errors };\n }\n\n return {\n status: hasFailures ? 'failed' : 'passed',\n timestamp: new Date().toISOString(),\n summary,\n failures,\n errors,\n baseline: diff ?? null,\n };\n}\n\n/**\n * Build a detailed JSON report with grouped results.\n */\nexport function buildDetailedJsonReport(\n results: QAResults,\n grouped: GroupedResults,\n diff?: BaselineDiff | null,\n): QAReport & { grouped: GroupedResults } {\n const base = buildJsonReport(results, diff);\n return { ...base, grouped };\n}\n","import type { QAResults, BaselineDiff, HistoryEntry, TrendResult, RegressionResult, BaselineSnapshot, GroupedResults } from '@kb-labs/qa-contracts';\nimport { getCheckLabel, getCheckIcon } from '@kb-labs/qa-contracts';\n\nexport interface ReportSection {\n header: string;\n lines: string[];\n}\n\nfunction icon(ct: string): string {\n return getCheckIcon(ct);\n}\n\nfunction label(ct: string): string {\n return getCheckLabel(ct);\n}\n\n/**\n * Build text report for a QA run.\n * Returns structured sections — CLI layer adds ANSI colors.\n */\nfunction buildBaselineDiffLines(diff: BaselineDiff): string[] {\n const lines: string[] = [];\n for (const ct of Object.keys(diff)) {\n const d = diff[ct]!;\n if (d.newFailures.length > 0) {\n lines.push(`${icon(ct)} ${label(ct)}: +${d.newFailures.length} new failures`);\n for (const pkg of d.newFailures) { lines.push(` - ${pkg}`); }\n }\n if (d.fixed.length > 0) {\n lines.push(`${icon(ct)} ${label(ct)}: -${d.fixed.length} fixed`);\n }\n }\n return lines;\n}\n\nexport function buildRunReport(results: QAResults, diff?: BaselineDiff | null): ReportSection[] {\n const sections: ReportSection[] = [];\n\n const summaryLines: string[] = [];\n let totalPassed = 0;\n let totalFailed = 0;\n let totalSkipped = 0;\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n const total = r.passed.length + r.failed.length + r.skipped.length;\n const pct = total > 0 ? Math.round((r.passed.length / total) * 100) : 100;\n const status = r.failed.length === 0 ? 'PASS' : 'FAIL';\n summaryLines.push(`${status} ${icon(ct)} ${label(ct).padEnd(12)} ${r.passed.length}/${total} passed (${pct}%)`);\n if (r.failed.length > 0) {\n for (const pkg of r.failed.slice(0, 5)) { summaryLines.push(` - ${pkg}`); }\n if (r.failed.length > 5) { summaryLines.push(` ... and ${r.failed.length - 5} more`); }\n }\n totalPassed += r.passed.length;\n totalFailed += r.failed.length;\n totalSkipped += r.skipped.length;\n }\n sections.push({ header: 'QA Summary Report', lines: summaryLines });\n\n if (diff) {\n const diffLines = buildBaselineDiffLines(diff);\n if (diffLines.length > 0) { sections.push({ header: 'Baseline Comparison', lines: diffLines }); }\n }\n\n sections.push({\n header: 'Totals',\n lines: [`Total: ${totalPassed} passed, ${totalFailed} failed, ${totalSkipped} skipped`],\n });\n\n return sections;\n}\n\n/**\n * Build history table for display.\n */\nexport function buildHistoryTable(history: HistoryEntry[], limit: number = 20): ReportSection[] {\n const entries = history.slice(-limit);\n const lines: string[] = [];\n\n for (const entry of entries) {\n const date = new Date(entry.timestamp).toLocaleDateString();\n const status = entry.status === 'passed' ? 'PASS' : 'FAIL';\n const summary = Object.keys(entry.summary).map((ct) => {\n const s = entry.summary[ct]!;\n return `${icon(ct)} ${s.failed}F`;\n }).join(' ');\n\n lines.push(`${date} ${entry.git.commit} ${status} ${summary} ${entry.git.message.slice(0, 40)}`);\n }\n\n return [{ header: `QA History (last ${entries.length})`, lines }];\n}\n\n/**\n * Build trends report.\n */\nexport function buildTrendsReport(trends: TrendResult[], history: HistoryEntry[]): ReportSection[] {\n if (trends.length === 0) {\n return [{ header: 'QA Trends', lines: ['Not enough history (need at least 2 entries)'] }];\n }\n\n const lines: string[] = [];\n for (const t of trends) {\n const arrow = t.delta > 0 ? `+${t.delta} (regression)` : t.delta < 0 ? `${t.delta} (improvement)` : '→ no change';\n lines.push(`${icon(t.checkType)} ${label(t.checkType).padEnd(12)} ${t.previous} → ${t.current} ${arrow}`);\n }\n\n if (history.length >= 2) {\n const first = history[Math.max(0, history.length - 10)]!;\n const last = history[history.length - 1]!;\n lines.push('');\n lines.push(`Period: ${new Date(first.timestamp).toLocaleDateString()} → ${new Date(last.timestamp).toLocaleDateString()}`);\n }\n\n return [{ header: 'QA Trends', lines }];\n}\n\n/**\n * Build regressions report.\n */\nexport function buildRegressionsReport(result: RegressionResult, history: HistoryEntry[]): ReportSection[] {\n if (history.length < 2) {\n return [{ header: 'Regression Detection', lines: ['Not enough history (need at least 2 entries)'] }];\n }\n\n const prev = history[history.length - 2]!;\n const curr = history[history.length - 1]!;\n const lines: string[] = [\n `Comparing: ${prev.git.commit} → ${curr.git.commit}`,\n '',\n ];\n\n if (!result.hasRegressions) {\n lines.push('No regressions detected.');\n return [{ header: 'Regression Detection', lines }];\n }\n\n for (const r of result.regressions) {\n lines.push(`${r.checkType}: +${r.newFailures.length} new failures`);\n for (const pkg of r.newFailures) {\n lines.push(` - ${pkg}`);\n }\n }\n\n lines.push('');\n lines.push('REGRESSIONS DETECTED!');\n\n return [{ header: 'Regression Detection', lines }];\n}\n\n/**\n * Build baseline status report.\n */\nexport function buildBaselineReport(baseline: BaselineSnapshot | null): ReportSection[] {\n if (!baseline) {\n return [{ header: 'Baseline Status', lines: ['No baseline captured yet. Run baseline:update first.'] }];\n }\n\n const lines: string[] = [\n `Captured: ${new Date(baseline.timestamp).toLocaleString()}`,\n `Git: ${baseline.git.commit} (${baseline.git.branch})`,\n '',\n ];\n\n for (const ct of Object.keys(baseline.results)) {\n const r = baseline.results[ct]!;\n lines.push(`${icon(ct)} ${label(ct).padEnd(12)} ${r.passed} passed, ${r.failed} failed`);\n if (r.failedPackages.length > 0) {\n const shown = r.failedPackages.slice(0, 3);\n for (const pkg of shown) {\n lines.push(` - ${pkg}`);\n }\n if (r.failedPackages.length > 3) {\n lines.push(` ... and ${r.failedPackages.length - 3} more`);\n }\n }\n }\n\n return [{ header: 'Baseline Status', lines }];\n}\n\n/**\n * Format a check status tag. Failed checks are UPPERCASED.\n */\nfunction checkTag(status: 'passed' | 'failed' | 'skipped', ct: string): string {\n const short = ct === 'typeCheck' ? 'types' : ct;\n if (status === 'failed') {return short.toUpperCase();}\n if (status === 'skipped') {return `-${short}-`;}\n return short;\n}\n\n/**\n * Build a detailed report grouped by category → repo → packages.\n */\ntype PackageEntry = GroupedResults['categories'][string]['repos'][string]['packages'][number];\n\nfunction getErrorPreview(raw: string): string {\n const errLines = raw.split('\\n').filter((l) => l.trim().length > 0);\n for (const el of errLines) {\n const cleaned = el.replace(/^Command failed: .*/, '').trim();\n if (cleaned.length > 0) {\n return cleaned.replace(/\\/[^\\s]*\\/kb-labs\\//g, '').slice(0, 100);\n }\n }\n return '';\n}\n\nfunction renderPackageLines(pkg: PackageEntry, lines: string[]): void {\n const hasFail = Object.values(pkg.checks).some((v) => v === 'failed');\n const status = hasFail ? 'FAIL' : 'PASS';\n const tags = Object.keys(pkg.checks).map((ct) => checkTag(pkg.checks[ct]!, ct)).join(' ');\n lines.push(` ${status} ${pkg.name.padEnd(40)} ${tags}`);\n if (hasFail) {\n for (const ct of Object.keys(pkg.checks)) {\n if (pkg.checks[ct] === 'failed') {\n const preview = getErrorPreview((pkg.errors[ct] ?? '').trim());\n lines.push(` ${ct}: ${preview || 'failed'}`);\n }\n }\n }\n}\n\nfunction renderCategoryLines(catKey: string, grouped: GroupedResults): string[] {\n const cat = grouped.categories[catKey]!;\n const lines: string[] = [`PASS ${cat.summary.passed} | FAIL ${cat.summary.failed}`, ''];\n\n for (const repoKey of Object.keys(cat.repos).sort()) {\n const repo = cat.repos[repoKey]!;\n lines.push(` ${repoKey} (${repo.summary.total} packages)`);\n\n const sorted = [...repo.packages].sort((a, b) => {\n const aFail = Object.values(a.checks).some((v) => v === 'failed') ? 0 : 1;\n const bFail = Object.values(b.checks).some((v) => v === 'failed') ? 0 : 1;\n if (aFail !== bFail) { return aFail - bFail; }\n return a.name.localeCompare(b.name);\n });\n\n for (const pkg of sorted) { renderPackageLines(pkg, lines); }\n lines.push('');\n }\n return lines;\n}\n\nexport function buildDetailedRunReport(grouped: GroupedResults, diff?: BaselineDiff | null): ReportSection[] {\n const sections: ReportSection[] = [];\n\n const categoryKeys = Object.keys(grouped.categories).sort((a, b) => {\n if (a === 'uncategorized') { return 1; }\n if (b === 'uncategorized') { return -1; }\n return a.localeCompare(b);\n });\n\n for (const catKey of categoryKeys) {\n const cat = grouped.categories[catKey]!;\n sections.push({ header: `${cat.label} (${cat.summary.total} packages)`, lines: renderCategoryLines(catKey, grouped) });\n }\n\n if (diff) {\n const diffLines = buildBaselineDiffLines(diff);\n if (diffLines.length > 0) { sections.push({ header: 'Baseline Comparison', lines: diffLines }); }\n }\n\n let totalPassed = 0;\n let totalFailed = 0;\n for (const catKey of categoryKeys) {\n totalPassed += grouped.categories[catKey]!.summary.passed;\n totalFailed += grouped.categories[catKey]!.summary.failed;\n }\n sections.push({\n header: 'Totals',\n lines: [`Total: ${totalPassed} passed, ${totalFailed} failed (${categoryKeys.length} categories)`],\n });\n\n return sections;\n}\n","import type {\n QAResults,\n WorkspacePackage,\n QAPluginConfig,\n PackageStatus,\n GroupedResults,\n GroupSummary,\n} from '@kb-labs/qa-contracts';\n\nfunction emptyGroupSummary(checkTypes: string[]): GroupSummary {\n const checks = {} as GroupSummary['checks'];\n for (const ct of checkTypes) {\n checks[ct] = { passed: 0, failed: 0, skipped: 0 };\n }\n return { total: 0, passed: 0, failed: 0, checks };\n}\n\nfunction resolveCheckStatus(\n pkgName: string,\n ct: string,\n results: QAResults,\n): 'passed' | 'failed' | 'skipped' {\n const r = results[ct];\n if (!r) {return 'skipped';}\n if (r.failed.includes(pkgName)) {return 'failed';}\n if (r.passed.includes(pkgName)) {return 'passed';}\n return 'skipped';\n}\n\n/**\n * Build per-package status from QA results.\n */\nfunction buildPackageStatus(\n pkg: WorkspacePackage,\n results: QAResults,\n category: string,\n): PackageStatus {\n const checks = {} as PackageStatus['checks'];\n const errors: Record<string, string> = {};\n\n for (const ct of Object.keys(results)) {\n checks[ct] = resolveCheckStatus(pkg.name, ct, results);\n if (checks[ct] === 'failed' && results[ct]?.errors[pkg.name]) {\n errors[ct] = results[ct]!.errors[pkg.name]!;\n }\n }\n\n return {\n name: pkg.name,\n repo: pkg.repo,\n category,\n checks,\n errors,\n };\n}\n\n/**\n * Add a PackageStatus to a GroupSummary.\n */\nfunction addToSummary(summary: GroupSummary, status: PackageStatus): void {\n summary.total++;\n\n const hasFail = Object.values(status.checks).some((v) => v === 'failed');\n if (hasFail) {\n summary.failed++;\n } else {\n summary.passed++;\n }\n\n for (const ct of Object.keys(status.checks)) {\n const s = status.checks[ct];\n if (!summary.checks[ct]) {\n summary.checks[ct] = { passed: 0, failed: 0, skipped: 0 };\n }\n if (s === 'passed') {summary.checks[ct].passed++;}\n else if (s === 'failed') {summary.checks[ct].failed++;}\n else {summary.checks[ct].skipped++;}\n }\n}\n\n/**\n * Group QA results by category → repo → packages.\n *\n * A package can appear in multiple categories if its repo is listed\n * in multiple category configs (e.g., kb-labs-cli in both \"core\" and \"hosts\").\n */\nexport function groupResults(\n results: QAResults,\n packages: WorkspacePackage[],\n categoryMap: Map<string, string[]>,\n config?: QAPluginConfig,\n): GroupedResults {\n const checkTypes = Object.keys(results);\n const grouped: GroupedResults = { categories: {} };\n\n for (const pkg of packages) {\n const categoryKeys = categoryMap.get(pkg.name) ?? ['uncategorized'];\n\n for (const categoryKey of categoryKeys) {\n const status = buildPackageStatus(pkg, results, categoryKey);\n\n // Ensure category exists\n if (!grouped.categories[categoryKey]) {\n const label =\n categoryKey === 'uncategorized'\n ? 'Uncategorized'\n : config?.categories?.[categoryKey]?.label ?? categoryKey;\n\n grouped.categories[categoryKey] = {\n label,\n repos: {},\n summary: emptyGroupSummary(checkTypes),\n };\n }\n\n const categoryGroup = grouped.categories[categoryKey]!;\n\n // Ensure repo exists within category\n if (!categoryGroup.repos[pkg.repo]) {\n categoryGroup.repos[pkg.repo] = {\n packages: [],\n summary: emptyGroupSummary(checkTypes),\n };\n }\n\n const repoGroup = categoryGroup.repos[pkg.repo]!;\n\n repoGroup.packages.push(status);\n addToSummary(repoGroup.summary, status);\n addToSummary(categoryGroup.summary, status);\n }\n }\n\n return grouped;\n}\n","import type { QAResults } from '@kb-labs/qa-contracts';\nimport type { ErrorGroup, QAErrorGroupsResponse } from '@kb-labs/qa-contracts';\n\n/**\n * Extract an error pattern from raw error text.\n * Tries to identify ESLint rules, TS error codes, or common patterns.\n */\nfunction extractPattern(errorText: string, checkType: string): string {\n if (checkType === 'lint') {\n // ESLint: look for rule names like \"@typescript-eslint/no-explicit-any\"\n const ruleMatch = errorText.match(/(\\S+\\/[\\w-]+|no-[\\w-]+)/);\n if (ruleMatch?.[1]) {return ruleMatch[1];}\n }\n\n if (checkType === 'typeCheck') {\n // TypeScript: look for error codes like \"TS2345\" or \"error TS2345\"\n const tsMatch = errorText.match(/TS(\\d{4,5})/);\n if (tsMatch) {return `TS${tsMatch[1]}`;}\n }\n\n if (checkType === 'test') {\n // Test failures: look for \"FAIL\" + test file pattern\n const failMatch = errorText.match(/FAIL\\s+(\\S+)/);\n if (failMatch) {return `FAIL: ${failMatch[1]}`;}\n }\n\n if (checkType === 'build') {\n // Build: \"Module not found\", \"Cannot find module\"\n if (errorText.includes('Cannot find module')) {return 'Cannot find module';}\n if (errorText.includes('Module not found')) {return 'Module not found';}\n }\n\n // Fallback: first meaningful line (skip empty lines, trim)\n const firstLine = errorText.split('\\n').find((l) => l.trim().length > 0)?.trim() ?? '';\n return firstLine.slice(0, 100) || 'Unknown error';\n}\n\n/**\n * Group errors from QA results by error pattern.\n * Packages with the same error pattern (e.g., same ESLint rule) are grouped together.\n */\nexport function groupErrors(results: QAResults): QAErrorGroupsResponse {\n const groupMap = new Map<string, ErrorGroup>();\n let ungrouped = 0;\n\n for (const ct of Object.keys(results)) {\n const check = results[ct]!;\n if (!check.errors) {continue;}\n\n for (const [pkgName, errorText] of Object.entries(check.errors)) {\n const pattern = extractPattern(errorText, ct);\n const key = `${ct}::${pattern}`;\n\n const existing = groupMap.get(key);\n if (existing) {\n existing.count++;\n existing.packages.push(pkgName);\n } else {\n groupMap.set(key, {\n pattern,\n count: 1,\n packages: [pkgName],\n checkType: ct,\n example: errorText.slice(0, 200),\n });\n }\n }\n }\n\n // Groups with count=1 are \"ungrouped\" (unique errors)\n const groups: ErrorGroup[] = [];\n for (const group of groupMap.values()) {\n if (group.count === 1) {\n ungrouped++;\n } else {\n groups.push(group);\n }\n }\n\n // Sort by count descending\n groups.sort((a, b) => b.count - a.count);\n\n return { groups, ungrouped };\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export { L as LastRunData, c as collectSubmoduleInfo, a as getSubmoduleInfo, g as getWorkspacePackages, l as loadLastRun, b as runLintCheck, r as runQA, e as runTestCheck, d as runTypeCheck, s as saveLastRun } from '../last-run-store-CQ_6ai40.js';
|
|
2
|
+
import { WorkspacePackage, CheckResult } from '@kb-labs/qa-contracts';
|
|
3
|
+
|
|
4
|
+
interface CacheEntry {
|
|
5
|
+
hash: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
}
|
|
8
|
+
type PackageCache = Record<string, CacheEntry>;
|
|
9
|
+
/**
|
|
10
|
+
* Load package hash cache from disk.
|
|
11
|
+
*/
|
|
12
|
+
declare function loadCache(rootDir: string): PackageCache;
|
|
13
|
+
/**
|
|
14
|
+
* Save package hash cache to disk.
|
|
15
|
+
*/
|
|
16
|
+
declare function saveCache(rootDir: string, cache: PackageCache): void;
|
|
17
|
+
/**
|
|
18
|
+
* Compute SHA256 hash for a package based on its source files and package.json.
|
|
19
|
+
*/
|
|
20
|
+
declare function computePackageHash(pkgDir: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a package has changed since last cached hash.
|
|
23
|
+
*/
|
|
24
|
+
declare function hasPackageChanged(pkgDir: string, pkgName: string, cache: PackageCache): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Update cache entry for a package.
|
|
27
|
+
*/
|
|
28
|
+
declare function updateCacheEntry(pkgDir: string, pkgName: string, cache: PackageCache): PackageCache;
|
|
29
|
+
|
|
30
|
+
interface BuildRunnerOptions {
|
|
31
|
+
rootDir: string;
|
|
32
|
+
packages: WorkspacePackage[];
|
|
33
|
+
noCache?: boolean;
|
|
34
|
+
onProgress?: (pkg: string, status: 'pass' | 'fail' | 'skip', durationMs?: number) => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run build checks across all packages.
|
|
38
|
+
* Uses incremental builds — only rebuilds packages where src/ is newer than dist/.
|
|
39
|
+
* Builds in dependency order (topological sort) so DTS files are available for downstream packages.
|
|
40
|
+
*/
|
|
41
|
+
declare function runBuildCheck(options: BuildRunnerOptions): CheckResult;
|
|
42
|
+
|
|
43
|
+
export { computePackageHash, hasPackageChanged, loadCache, runBuildCheck, saveCache, updateCacheEntry };
|