qualink 0.5.0 → 0.6.1
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 +2 -2
- package/dist/cli/command-factory.js +7 -0
- package/dist/cli/commands/{coverage-dotnet.d.ts → coverage-cobertura.d.ts} +1 -1
- package/dist/cli/commands/coverage-cobertura.js +12 -0
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/index.js +13 -2
- package/dist/cli/multi-collect/patterns.d.ts +1 -1
- package/dist/cli/multi-collect/patterns.js +2 -2
- package/dist/cli/multi-collect/resolve-metadata.js +1 -1
- package/dist/cli/multi-collect/run-collector.js +4 -4
- package/dist/cli/send-to-sink.js +16 -1
- package/dist/collectors/coverage-cobertura.d.ts +2 -0
- package/dist/collectors/{coverage-dotnet.js → coverage-cobertura.js} +3 -3
- package/dist/collectors/index.d.ts +1 -1
- package/dist/collectors/index.js +1 -1
- package/dist/sinks/elastic.d.ts +4 -3
- package/dist/sinks/elastic.js +6 -5
- package/dist/sinks/loki.d.ts +2 -2
- package/dist/sinks/loki.js +3 -2
- package/dist/sinks/stdout.d.ts +2 -2
- package/dist/sinks/stdout.js +1 -0
- package/dist/sinks/types.d.ts +4 -1
- package/dist/types.d.ts +4 -4
- package/dist/utils/format.d.ts +1 -0
- package/dist/utils/format.js +11 -0
- package/package.json +9 -9
- package/dist/cli/commands/coverage-dotnet.js +0 -12
- package/dist/collectors/coverage-dotnet.d.ts +0 -2
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ qualink collect <collector> --input <path> --sink elastic [flags]
|
|
|
110
110
|
```bash
|
|
111
111
|
qualink collect eslint --input eslint-report.json --sink elastic --repo frontend-mono --category frontend --tags frontend,web
|
|
112
112
|
qualink collect sarif --input analyzers.sarif --sink elastic --repo backend-api --category backend --tags backend,api
|
|
113
|
-
qualink collect coverage-
|
|
113
|
+
qualink collect coverage-cobertura --input coverage.cobertura.xml --sink elastic --repo backend-api
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
### Multi-collect
|
|
@@ -160,7 +160,7 @@ Collectors:
|
|
|
160
160
|
- `lighthouse` (Lighthouse JSON)
|
|
161
161
|
- `coverage-js` (Istanbul/Vitest JSON)
|
|
162
162
|
- `sarif` (Roslyn or generic SARIF JSON)
|
|
163
|
-
- `coverage-
|
|
163
|
+
- `coverage-cobertura` (Cobertura/OpenCover XML)
|
|
164
164
|
- `junit` (JUnit XML)
|
|
165
165
|
|
|
166
166
|
ESLint file-level options (optional):
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
1
2
|
import { defineCommand } from "citty";
|
|
3
|
+
import { formatBytes } from "../utils/format.js";
|
|
2
4
|
import { CliError } from "./cli-error.js";
|
|
3
5
|
import { commonArgs, isDryRun } from "./common-args.js";
|
|
4
6
|
import { parseCommonMetadata } from "./parse-metadata.js";
|
|
@@ -16,6 +18,11 @@ export function createCollectorCommand(config) {
|
|
|
16
18
|
async run({ args }) {
|
|
17
19
|
try {
|
|
18
20
|
const parsedArgs = args;
|
|
21
|
+
const inputPath = typeof parsedArgs.input === "string" ? parsedArgs.input : undefined;
|
|
22
|
+
if (inputPath) {
|
|
23
|
+
const fileStat = await stat(inputPath);
|
|
24
|
+
process.stderr.write(` read: ${inputPath} (${formatBytes(fileStat.size)})\n`);
|
|
25
|
+
}
|
|
19
26
|
const metadata = parseCommonMetadata(parsedArgs);
|
|
20
27
|
const { metricType, documents } = await config.collect(parsedArgs, metadata);
|
|
21
28
|
await sendToSink(metricType, parsedArgs, documents);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { collectCoverageCobertura } from "../../collectors/coverage-cobertura.js";
|
|
2
|
+
import { createCollectorCommand } from "../command-factory.js";
|
|
3
|
+
import { loadTextInput } from "../load-input.js";
|
|
4
|
+
export const coverageCoberturaCommand = createCollectorCommand({
|
|
5
|
+
name: "coverage-cobertura",
|
|
6
|
+
description: "Collect Cobertura XML coverage metrics and relay them",
|
|
7
|
+
async collect(args, metadata) {
|
|
8
|
+
const input = await loadTextInput(args);
|
|
9
|
+
const documents = collectCoverageCobertura(input, metadata);
|
|
10
|
+
return { metricType: "coverage-cobertura", documents };
|
|
11
|
+
},
|
|
12
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { biomeCommand } from "./biome.js";
|
|
2
|
-
export {
|
|
2
|
+
export { coverageCoberturaCommand } from "./coverage-cobertura.js";
|
|
3
3
|
export { coverageJsCommand } from "./coverage-js.js";
|
|
4
4
|
export { eslintCommand } from "./eslint.js";
|
|
5
5
|
export { junitCommand } from "./junit.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { biomeCommand } from "./biome.js";
|
|
2
|
-
export {
|
|
2
|
+
export { coverageCoberturaCommand } from "./coverage-cobertura.js";
|
|
3
3
|
export { coverageJsCommand } from "./coverage-js.js";
|
|
4
4
|
export { eslintCommand } from "./eslint.js";
|
|
5
5
|
export { junitCommand } from "./junit.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { relative } from "node:path";
|
|
1
2
|
import { defineCommand, runMain } from "citty";
|
|
2
3
|
import { CliError } from "./cli-error.js";
|
|
3
|
-
import { biomeCommand,
|
|
4
|
+
import { biomeCommand, coverageCoberturaCommand, coverageJsCommand, eslintCommand, junitCommand, lighthouseCommand, metaCommand, pipelineCommand, sarifCommand, } from "./commands/index.js";
|
|
4
5
|
import { commonArgs, isDryRun } from "./common-args.js";
|
|
5
6
|
import { parseConfig, resolveConfig } from "./multi-collect/config.js";
|
|
6
7
|
import { discoverFiles } from "./multi-collect/discover.js";
|
|
@@ -32,7 +33,7 @@ const collectCommand = defineCommand({
|
|
|
32
33
|
lighthouse: lighthouseCommand,
|
|
33
34
|
"coverage-js": coverageJsCommand,
|
|
34
35
|
sarif: sarifCommand,
|
|
35
|
-
"coverage-
|
|
36
|
+
"coverage-cobertura": coverageCoberturaCommand,
|
|
36
37
|
junit: junitCommand,
|
|
37
38
|
},
|
|
38
39
|
async run({ args }) {
|
|
@@ -54,6 +55,11 @@ const collectCommand = defineCommand({
|
|
|
54
55
|
async function runDirMode(dir, args) {
|
|
55
56
|
const metadata = parseCommonMetadata(args);
|
|
56
57
|
const discovered = await discoverFiles(dir);
|
|
58
|
+
for (const [collectorKey, files] of discovered) {
|
|
59
|
+
for (const filePath of files) {
|
|
60
|
+
process.stderr.write(` scan: ${relative(dir, filePath)} → ${collectorKey}\n`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
57
63
|
const accumulated = new Map();
|
|
58
64
|
const counts = new Map();
|
|
59
65
|
for (const [collectorKey, files] of discovered) {
|
|
@@ -78,6 +84,11 @@ async function runConfigMode(configValue, args) {
|
|
|
78
84
|
const metadata = parseCommonMetadata(args);
|
|
79
85
|
const entries = await parseConfig(configValue);
|
|
80
86
|
const resolved = await resolveConfig(entries, ".");
|
|
87
|
+
for (const entry of resolved) {
|
|
88
|
+
for (const filePath of entry.files) {
|
|
89
|
+
process.stderr.write(` scan: ${filePath} → ${entry.type}\n`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
81
92
|
const accumulated = new Map();
|
|
82
93
|
const counts = new Map();
|
|
83
94
|
for (const entry of resolved) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MetricType } from "../../types.js";
|
|
2
|
-
export type CollectorKey = Extract<MetricType, "eslint" | "biome" | "coverage-js" | "coverage-
|
|
2
|
+
export type CollectorKey = Extract<MetricType, "eslint" | "biome" | "coverage-js" | "coverage-cobertura" | "sarif" | "lighthouse" | "junit">;
|
|
3
3
|
export declare const COLLECTOR_KEYS: readonly CollectorKey[];
|
|
4
4
|
export interface FilePattern {
|
|
5
5
|
/** Match against basename only */
|
|
@@ -2,7 +2,7 @@ export const COLLECTOR_KEYS = [
|
|
|
2
2
|
"eslint",
|
|
3
3
|
"biome",
|
|
4
4
|
"coverage-js",
|
|
5
|
-
"coverage-
|
|
5
|
+
"coverage-cobertura",
|
|
6
6
|
"sarif",
|
|
7
7
|
"lighthouse",
|
|
8
8
|
"junit",
|
|
@@ -11,7 +11,7 @@ export const COLLECTOR_PATTERNS = {
|
|
|
11
11
|
eslint: [{ basename: "eslint-report.json" }],
|
|
12
12
|
biome: [{ basename: "biome-report.json" }],
|
|
13
13
|
"coverage-js": [{ basename: "coverage-summary.json" }],
|
|
14
|
-
"coverage-
|
|
14
|
+
"coverage-cobertura": [
|
|
15
15
|
{ basename: "coverage.cobertura.xml" },
|
|
16
16
|
{ basename: "cobertura-coverage.xml" },
|
|
17
17
|
],
|
|
@@ -116,7 +116,7 @@ export function resolveFileMetadata(filePath, collectorKey) {
|
|
|
116
116
|
const stopAt = gitRoot ?? fileDir;
|
|
117
117
|
const overrides = {};
|
|
118
118
|
// Detect project name
|
|
119
|
-
if (collectorKey === "coverage-
|
|
119
|
+
if (collectorKey === "coverage-cobertura" || collectorKey === "sarif") {
|
|
120
120
|
overrides.projectName = findNearestProjectName(fileDir, stopAt) ?? null;
|
|
121
121
|
}
|
|
122
122
|
else {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { collectBiome } from "../../collectors/biome.js";
|
|
2
|
-
import {
|
|
2
|
+
import { collectCoverageCobertura } from "../../collectors/coverage-cobertura.js";
|
|
3
3
|
import { collectCoverageJs } from "../../collectors/coverage-js.js";
|
|
4
4
|
import { collectEslint } from "../../collectors/eslint.js";
|
|
5
5
|
import { collectJunit } from "../../collectors/junit.js";
|
|
@@ -53,10 +53,10 @@ export async function runCollector(key, filePath, metadata, urlOverride) {
|
|
|
53
53
|
const documents = collectCoverageJs(input, metadata);
|
|
54
54
|
return { metricType: "coverage-js", documents };
|
|
55
55
|
}
|
|
56
|
-
case "coverage-
|
|
56
|
+
case "coverage-cobertura": {
|
|
57
57
|
const input = await readTextFile(filePath);
|
|
58
|
-
const documents =
|
|
59
|
-
return { metricType: "coverage-
|
|
58
|
+
const documents = collectCoverageCobertura(input, metadata);
|
|
59
|
+
return { metricType: "coverage-cobertura", documents };
|
|
60
60
|
}
|
|
61
61
|
case "sarif": {
|
|
62
62
|
const input = await readJsonFile(filePath);
|
package/dist/cli/send-to-sink.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { INDEX_BY_TYPE } from "../sinks/elastic.js";
|
|
1
2
|
import { createSink } from "../sinks/index.js";
|
|
2
3
|
import { CliError } from "./cli-error.js";
|
|
3
4
|
import { argValue, envOrArg, isDryRun } from "./common-args.js";
|
|
@@ -65,8 +66,22 @@ export async function sendToSink(metricType, args, documents) {
|
|
|
65
66
|
sinkConfig = sinkConfigBase;
|
|
66
67
|
}
|
|
67
68
|
const sink = createSink(sinkConfig);
|
|
68
|
-
await sink.send({
|
|
69
|
+
const { durationMs } = await sink.send({
|
|
69
70
|
metricType,
|
|
70
71
|
documents,
|
|
71
72
|
});
|
|
73
|
+
const count = documents.length;
|
|
74
|
+
const ms = Math.round(durationMs);
|
|
75
|
+
if (sinkKind === "elastic") {
|
|
76
|
+
const index = INDEX_BY_TYPE[metricType];
|
|
77
|
+
const url = sinkConfig.elasticUrl ?? "";
|
|
78
|
+
process.stderr.write(` sent: ${count} document(s) → elastic ${index} (${url}) ${ms}ms\n`);
|
|
79
|
+
}
|
|
80
|
+
else if (sinkKind === "loki") {
|
|
81
|
+
const url = sinkConfig.lokiUrl ?? "";
|
|
82
|
+
process.stderr.write(` sent: ${count} document(s) → loki (${url}) ${ms}ms\n`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
process.stderr.write(` sent: ${count} document(s) → stdout\n`);
|
|
86
|
+
}
|
|
72
87
|
}
|
|
@@ -39,7 +39,7 @@ function parseOpenCover(root) {
|
|
|
39
39
|
branchesCovered: readNumber(summary, "@_visitedBranchPoints"),
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
export function
|
|
42
|
+
export function collectCoverageCobertura(xmlInput, metadata) {
|
|
43
43
|
const parser = new XMLParser({
|
|
44
44
|
ignoreAttributes: false,
|
|
45
45
|
attributeNamePrefix: "@_",
|
|
@@ -76,8 +76,8 @@ export function collectCoverageDotnet(xmlInput, metadata) {
|
|
|
76
76
|
const functionsCovered = 0;
|
|
77
77
|
const doc = {
|
|
78
78
|
...baseDocument({
|
|
79
|
-
metricType: "coverage-
|
|
80
|
-
tool: "
|
|
79
|
+
metricType: "coverage-cobertura",
|
|
80
|
+
tool: "cobertura",
|
|
81
81
|
languages: ["csharp"],
|
|
82
82
|
metadata,
|
|
83
83
|
}),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { collectBiome } from "./biome.js";
|
|
2
|
-
export {
|
|
2
|
+
export { collectCoverageCobertura } from "./coverage-cobertura.js";
|
|
3
3
|
export { collectCoverageJs } from "./coverage-js.js";
|
|
4
4
|
export { collectEslint } from "./eslint.js";
|
|
5
5
|
export { collectLighthouse } from "./lighthouse.js";
|
package/dist/collectors/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { collectBiome } from "./biome.js";
|
|
2
|
-
export {
|
|
2
|
+
export { collectCoverageCobertura } from "./coverage-cobertura.js";
|
|
3
3
|
export { collectCoverageJs } from "./coverage-js.js";
|
|
4
4
|
export { collectEslint } from "./eslint.js";
|
|
5
5
|
export { collectLighthouse } from "./lighthouse.js";
|
package/dist/sinks/elastic.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { NormalizedDocument } from "../types.js";
|
|
2
|
-
import type { SendInput, Sink } from "./types.js";
|
|
1
|
+
import type { MetricType, NormalizedDocument } from "../types.js";
|
|
2
|
+
import type { SendInput, SendResult, Sink } from "./types.js";
|
|
3
3
|
interface ElasticSinkOptions {
|
|
4
4
|
url: string;
|
|
5
5
|
apiKey: string;
|
|
6
6
|
retryMax: number;
|
|
7
7
|
retryBackoffMs: number;
|
|
8
8
|
}
|
|
9
|
+
export declare const INDEX_BY_TYPE: Record<MetricType, string>;
|
|
9
10
|
export declare function buildBulkBody(indexName: string, documents: NormalizedDocument[]): string;
|
|
10
11
|
export declare class ElasticSink implements Sink {
|
|
11
12
|
private readonly url;
|
|
@@ -13,6 +14,6 @@ export declare class ElasticSink implements Sink {
|
|
|
13
14
|
private readonly retryMax;
|
|
14
15
|
private readonly retryBackoffMs;
|
|
15
16
|
constructor(options: ElasticSinkOptions);
|
|
16
|
-
send(input: SendInput): Promise<
|
|
17
|
+
send(input: SendInput): Promise<SendResult>;
|
|
17
18
|
}
|
|
18
19
|
export {};
|
package/dist/sinks/elastic.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { isRecord } from "../utils/guards.js";
|
|
2
|
-
const INDEX_BY_TYPE = {
|
|
2
|
+
export const INDEX_BY_TYPE = {
|
|
3
3
|
biome: "codequality-biome",
|
|
4
4
|
eslint: "codequality-eslint",
|
|
5
5
|
lighthouse: "codequality-lighthouse",
|
|
6
6
|
"coverage-js": "codequality-coverage-js",
|
|
7
7
|
sarif: "codequality-sarif",
|
|
8
|
-
"coverage-
|
|
8
|
+
"coverage-cobertura": "codequality-coverage-cobertura",
|
|
9
9
|
junit: "codequality-junit",
|
|
10
10
|
meta: "codequality-meta",
|
|
11
11
|
pipeline: "codequality-pipeline",
|
|
@@ -63,8 +63,9 @@ export class ElasticSink {
|
|
|
63
63
|
}
|
|
64
64
|
async send(input) {
|
|
65
65
|
if (input.documents.length === 0) {
|
|
66
|
-
return;
|
|
66
|
+
return { durationMs: 0 };
|
|
67
67
|
}
|
|
68
|
+
const start = performance.now();
|
|
68
69
|
const indexName = INDEX_BY_TYPE[input.metricType];
|
|
69
70
|
let documents = input.documents;
|
|
70
71
|
let attempt = 0;
|
|
@@ -99,7 +100,7 @@ export class ElasticSink {
|
|
|
99
100
|
if (nonRetryableErrors.length > 0) {
|
|
100
101
|
throw new Error(`Elastic bulk request completed with ${nonRetryableErrors.length} non-retryable item error(s)`);
|
|
101
102
|
}
|
|
102
|
-
return;
|
|
103
|
+
return { durationMs: performance.now() - start };
|
|
103
104
|
}
|
|
104
105
|
if (attempt > this.retryMax) {
|
|
105
106
|
const deadLetterBody = buildBulkBody(indexName, retryable);
|
|
@@ -110,7 +111,7 @@ export class ElasticSink {
|
|
|
110
111
|
await sleep(this.retryBackoffMs * attempt);
|
|
111
112
|
continue;
|
|
112
113
|
}
|
|
113
|
-
return;
|
|
114
|
+
return { durationMs: performance.now() - start };
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
}
|
package/dist/sinks/loki.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NormalizedDocument } from "../types.js";
|
|
2
|
-
import type { SendInput, Sink } from "./types.js";
|
|
2
|
+
import type { SendInput, SendResult, Sink } from "./types.js";
|
|
3
3
|
export interface LokiSinkOptions {
|
|
4
4
|
url: string;
|
|
5
5
|
username?: string | undefined;
|
|
@@ -25,6 +25,6 @@ export declare class LokiSink implements Sink {
|
|
|
25
25
|
private readonly retryMax;
|
|
26
26
|
private readonly retryBackoffMs;
|
|
27
27
|
constructor(options: LokiSinkOptions);
|
|
28
|
-
send(input: SendInput): Promise<
|
|
28
|
+
send(input: SendInput): Promise<SendResult>;
|
|
29
29
|
}
|
|
30
30
|
export {};
|
package/dist/sinks/loki.js
CHANGED
|
@@ -48,8 +48,9 @@ export class LokiSink {
|
|
|
48
48
|
}
|
|
49
49
|
async send(input) {
|
|
50
50
|
if (input.documents.length === 0) {
|
|
51
|
-
return;
|
|
51
|
+
return { durationMs: 0 };
|
|
52
52
|
}
|
|
53
|
+
const start = performance.now();
|
|
53
54
|
const payload = buildLokiPayload(input.documents);
|
|
54
55
|
const body = JSON.stringify(payload);
|
|
55
56
|
const headers = {
|
|
@@ -71,7 +72,7 @@ export class LokiSink {
|
|
|
71
72
|
body,
|
|
72
73
|
});
|
|
73
74
|
if (response.status === 204 || response.ok) {
|
|
74
|
-
return;
|
|
75
|
+
return { durationMs: performance.now() - start };
|
|
75
76
|
}
|
|
76
77
|
const responseText = await response.text();
|
|
77
78
|
if (!isRetryableStatus(response.status) || attempt > this.retryMax) {
|
package/dist/sinks/stdout.d.ts
CHANGED
package/dist/sinks/stdout.js
CHANGED
package/dist/sinks/types.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export interface SendInput {
|
|
|
3
3
|
metricType: MetricType;
|
|
4
4
|
documents: NormalizedDocument[];
|
|
5
5
|
}
|
|
6
|
+
export interface SendResult {
|
|
7
|
+
durationMs: number;
|
|
8
|
+
}
|
|
6
9
|
export interface Sink {
|
|
7
|
-
send(input: SendInput): Promise<
|
|
10
|
+
send(input: SendInput): Promise<SendResult>;
|
|
8
11
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type Language = "js" | "ts" | "csharp" | (string & {});
|
|
2
2
|
export type Environment = "dev" | "test" | "prod" | "ci";
|
|
3
|
-
export type MetricType = "biome" | "eslint" | "lighthouse" | "coverage-js" | "sarif" | "coverage-
|
|
3
|
+
export type MetricType = "biome" | "eslint" | "lighthouse" | "coverage-js" | "sarif" | "coverage-cobertura" | "junit" | "meta" | "pipeline";
|
|
4
4
|
export interface BaseMetricDocument {
|
|
5
5
|
"@timestamp": string;
|
|
6
6
|
metric_type: MetricType;
|
|
@@ -102,8 +102,8 @@ export interface BiomeFileIssue {
|
|
|
102
102
|
fixable_errors: number;
|
|
103
103
|
fixable_warnings: number;
|
|
104
104
|
}
|
|
105
|
-
export interface
|
|
106
|
-
metric_type: "coverage-
|
|
105
|
+
export interface CoberturaCoverageMetricDocument extends CoverageMetricDocument {
|
|
106
|
+
metric_type: "coverage-cobertura";
|
|
107
107
|
coverage_format: "cobertura" | "opencover" | (string & {});
|
|
108
108
|
}
|
|
109
109
|
export interface JunitMetricDocument extends BaseMetricDocument {
|
|
@@ -128,7 +128,7 @@ export interface PipelineMetricDocument extends BaseMetricDocument {
|
|
|
128
128
|
start_time: string | null;
|
|
129
129
|
stage_name: string | null;
|
|
130
130
|
}
|
|
131
|
-
export type NormalizedDocument = BiomeMetricDocument | EslintMetricDocument | LighthouseMetricDocument | CoverageJsMetricDocument | SarifMetricDocument |
|
|
131
|
+
export type NormalizedDocument = BiomeMetricDocument | EslintMetricDocument | LighthouseMetricDocument | CoverageJsMetricDocument | SarifMetricDocument | CoberturaCoverageMetricDocument | JunitMetricDocument | MetaMetricDocument | PipelineMetricDocument;
|
|
132
132
|
export interface CommonMetadata {
|
|
133
133
|
repo: string;
|
|
134
134
|
category: string | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatBytes(bytes: number): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const UNITS = ["B", "kB", "MB", "GB"];
|
|
2
|
+
export function formatBytes(bytes) {
|
|
3
|
+
let value = bytes;
|
|
4
|
+
let unitIndex = 0;
|
|
5
|
+
while (value >= 1000 && unitIndex < UNITS.length - 1) {
|
|
6
|
+
value /= 1000;
|
|
7
|
+
unitIndex++;
|
|
8
|
+
}
|
|
9
|
+
const formatted = unitIndex === 0 ? value.toString() : value.toFixed(1).replace(/\.0$/, "");
|
|
10
|
+
return `${formatted} ${UNITS[unitIndex]}`;
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qualink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Collect, normalize, and relay code quality metrics from CI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
"node": ">=22"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"citty": "^0.2.
|
|
21
|
-
"fast-xml-parser": "^5.
|
|
20
|
+
"citty": "^0.2.2",
|
|
21
|
+
"fast-xml-parser": "^5.7.3"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@biomejs/biome": "^2.4.
|
|
25
|
-
"@types/node": "^25.
|
|
26
|
-
"@vitest/coverage-v8": "^4.
|
|
27
|
-
"git-cliff": "^2.
|
|
24
|
+
"@biomejs/biome": "^2.4.14",
|
|
25
|
+
"@types/node": "^25.6.0",
|
|
26
|
+
"@vitest/coverage-v8": "^4.1.5",
|
|
27
|
+
"git-cliff": "^2.13.1",
|
|
28
28
|
"ts-node": "^10.9.2",
|
|
29
|
-
"typescript": "^
|
|
30
|
-
"vitest": "^4.
|
|
29
|
+
"typescript": "^6.0.3",
|
|
30
|
+
"vitest": "^4.1.5"
|
|
31
31
|
},
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { collectCoverageDotnet } from "../../collectors/coverage-dotnet.js";
|
|
2
|
-
import { createCollectorCommand } from "../command-factory.js";
|
|
3
|
-
import { loadTextInput } from "../load-input.js";
|
|
4
|
-
export const coverageDotnetCommand = createCollectorCommand({
|
|
5
|
-
name: "coverage-dotnet",
|
|
6
|
-
description: "Collect .NET coverage metrics and relay them",
|
|
7
|
-
async collect(args, metadata) {
|
|
8
|
-
const input = await loadTextInput(args);
|
|
9
|
-
const documents = collectCoverageDotnet(input, metadata);
|
|
10
|
-
return { metricType: "coverage-dotnet", documents };
|
|
11
|
-
},
|
|
12
|
-
});
|