@thymian/plugin-reporter 0.0.0-PLACEHOLDER

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 ADDED
@@ -0,0 +1,59 @@
1
+ <!-- HEADER:START - Do not remove or modify this section -->
2
+ <div align="center">
3
+ <img src="https://raw.githubusercontent.com/thymianofficial/thymian/main/astro-docs/src/assets/logo.svg" alt="Thymian Logo" width="200"/>
4
+
5
+ # Thymian
6
+
7
+ **Add resilience and HTTP conformance to your API development workflow**
8
+
9
+ [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL%203.0-green.svg)](https://github.com/thymianofficial/thymian/blob/main/LICENSE)
10
+ [![CI](https://github.com/thymianofficial/thymian/actions/workflows/ci.yaml/badge.svg)](https://github.com/thymianofficial/thymian/actions/workflows/ci.yaml)
11
+ [![Documentation](https://img.shields.io/badge/docs-thymian.dev-green.svg)](https://thymian.dev)
12
+ [![Discord](https://img.shields.io/discord/1440702693768429791?logo=discord&label=Discord&color=5865F2)](https://discord.gg/TRSwCxbz9f)
13
+ [![Reddit](https://img.shields.io/badge/Reddit-ThymianOfficial-FF4500?logo=reddit)](https://www.reddit.com/r/ThymianOfficial/)
14
+ [![Twitter](https://img.shields.io/badge/Twitter-@thymiandev-1DA1F2?logo=x)](https://x.com/thymiandev)
15
+
16
+ </div>
17
+ <!-- HEADER:END -->
18
+
19
+ ## @thymian/plugin-reporter
20
+
21
+ Formats and outputs Thymian validation results. Listens for rule violations and test outcomes emitted during a Thymian run, then generates structured reports for the console, CI pipelines, or custom integrations.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install @thymian/plugin-reporter
27
+ ```
28
+
29
+ > **Getting started with Thymian?** See the [main Thymian package](https://www.npmjs.com/package/thymian) for quick installation and first-run instructions.
30
+
31
+ <!-- FOOTER:START - Do not remove or modify this section -->
32
+
33
+ ## 📚 Documentation
34
+
35
+ - **[Getting Started](https://thymian.dev/introduction/getting-started)** - Set up Thymian in minutes
36
+ - **[Documentation](https://thymian.dev)** - Comprehensive guides and API reference
37
+ - **[CLI Reference](https://thymian.dev/references/cli)** - Complete CLI command documentation
38
+ - **[HTTP Rules](https://thymian.dev/guides/http-rules/how-to-use-rules)** - Learn about HTTP conformance validation
39
+
40
+ ## 🏢 Enterprise Support
41
+
42
+ Get professional consulting and dedicated support from the creators of Thymian. We offer:
43
+
44
+ - API design and governance strategies
45
+ - HTTP standards compliance auditing
46
+ - Custom plugin development
47
+ - Custom rule development
48
+ - Team training and workshops
49
+
50
+ **[Learn more about Enterprise Support](https://thymian.dev/enterprise)** | **Email: [support@thymian.dev](mailto:support@thymian.dev)**
51
+
52
+ ---
53
+
54
+ <div align="center">
55
+
56
+ **Shipped with 🌱 by [qupaya](https://qupaya.com)**
57
+
58
+ </div>
59
+ <!-- FOOTER:END -->
@@ -0,0 +1,18 @@
1
+ import type { ThymianReport, ThymianReportSeverity } from '@thymian/core';
2
+ export type ThymianReportStatistics = {
3
+ numberOfReports: number;
4
+ numberOfItems: number;
5
+ severityCounts: Record<ThymianReportSeverity, number>;
6
+ };
7
+ export type ReportAnalysis = {
8
+ reports: ThymianReport[];
9
+ statistics: ThymianReportStatistics;
10
+ };
11
+ export declare function analyze(reports: ThymianReport[]): ReportAnalysis;
12
+ export interface Formatter<Options = Record<PropertyKey, unknown>> {
13
+ options: Options;
14
+ init(options: Options): void | Promise<void>;
15
+ report(report: ThymianReport): void | Promise<void>;
16
+ flush(): string | undefined | Promise<string | undefined>;
17
+ }
18
+ //# sourceMappingURL=formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,uBAAuB,GAAG;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,EAAE,uBAAuB,CAAC;CACrC,CAAC;AAQF,wBAAgB,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,cAAc,CAsBhE;AAED,MAAM,WAAW,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC;IAC/D,OAAO,EAAE,OAAO,CAAC;IAEjB,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD,KAAK,IAAI,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC3D"}
@@ -0,0 +1,24 @@
1
+ function countItems(reports) {
2
+ return reports.flatMap((report) => report.sections?.flatMap((s) => s.items) ?? []);
3
+ }
4
+ export function analyze(reports) {
5
+ const items = countItems(reports);
6
+ const severityCounts = {
7
+ error: 0,
8
+ warn: 0,
9
+ hint: 0,
10
+ info: 0,
11
+ };
12
+ for (const item of items) {
13
+ severityCounts[item.severity]++;
14
+ }
15
+ return {
16
+ reports,
17
+ statistics: {
18
+ numberOfReports: reports.length,
19
+ numberOfItems: items.length,
20
+ severityCounts,
21
+ },
22
+ };
23
+ }
24
+ //# sourceMappingURL=formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAiBA,SAAS,UAAU,CAAC,OAAwB;IAC1C,OAAO,OAAO,CAAC,OAAO,CACpB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAwB;IAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAElC,MAAM,cAAc,GAA0C;QAC5D,KAAK,EAAE,CAAC;QACR,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,CAAC;KACR,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;IAClC,CAAC;IAED,OAAO;QACL,OAAO;QACP,UAAU,EAAE;YACV,eAAe,EAAE,OAAO,CAAC,MAAM;YAC/B,aAAa,EAAE,KAAK,CAAC,MAAM;YAC3B,cAAc;SACf;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Logger, ThymianReport } from '@thymian/core';
2
+ import type { Formatter } from '../formatter.js';
3
+ export type CsvFormatterOptions = {
4
+ path: string;
5
+ };
6
+ export declare class CsvFormatter implements Formatter<CsvFormatterOptions> {
7
+ private readonly logger;
8
+ private stream;
9
+ options: CsvFormatterOptions;
10
+ constructor(logger: Logger);
11
+ flush(): Promise<string | undefined>;
12
+ init(options: CsvFormatterOptions): Promise<void>;
13
+ report(report: ThymianReport): Promise<void>;
14
+ }
15
+ export declare function thymianReportToCsvLines(report: ThymianReport): string[];
16
+ export declare function csvSafe(str: string | undefined): string;
17
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/formatters/csv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,qBAAa,YAAa,YAAW,SAAS,CAAC,mBAAmB,CAAC;IAKrD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,MAAM,CAAe;IAE7B,OAAO,EAAG,mBAAmB,CAAC;gBAED,MAAM,EAAE,MAAM;IAE3C,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAUpC,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjD,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAmB7C;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAkBvE;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAavD"}
@@ -0,0 +1,74 @@
1
+ import { createWriteStream } from 'node:fs';
2
+ export class CsvFormatter {
3
+ logger;
4
+ stream;
5
+ options;
6
+ constructor(logger) {
7
+ this.logger = logger;
8
+ }
9
+ flush() {
10
+ return new Promise((resolve) => {
11
+ this.stream.end(() => {
12
+ this.logger.debug(`Wrote CSV report to ${this.options.path}`);
13
+ resolve(undefined);
14
+ });
15
+ });
16
+ }
17
+ init(options) {
18
+ this.options = options;
19
+ this.stream = createWriteStream(options.path, 'utf-8');
20
+ this.stream.on('error', (err) => {
21
+ this.logger.error(`Failed to write CSV report to ${this.options.path}: ${err.message}`);
22
+ });
23
+ return new Promise((resolve) => {
24
+ this.stream.on('ready', () => {
25
+ this.stream.write('source,section,severity,ruleName,message,details\n');
26
+ resolve();
27
+ });
28
+ });
29
+ }
30
+ report(report) {
31
+ return new Promise((resolve, reject) => {
32
+ const lines = thymianReportToCsvLines(report);
33
+ if (lines.length === 0) {
34
+ resolve();
35
+ return;
36
+ }
37
+ const data = lines.join('');
38
+ this.stream.write(data, (err) => {
39
+ if (err) {
40
+ reject(err);
41
+ }
42
+ else {
43
+ resolve();
44
+ }
45
+ });
46
+ });
47
+ }
48
+ }
49
+ export function thymianReportToCsvLines(report) {
50
+ const lines = [];
51
+ if (!report.sections || report.sections.length === 0) {
52
+ // Report with no sections — emit a single row with source and message
53
+ lines.push(`${csvSafe(report.source)},,,,${csvSafe(report.message)},\n`);
54
+ return lines;
55
+ }
56
+ for (const section of report.sections) {
57
+ for (const item of section.items) {
58
+ lines.push(`${csvSafe(report.source)},${csvSafe(section.heading)},${csvSafe(item.severity)},${csvSafe(item.ruleName)},${csvSafe(item.message)},${csvSafe(item.details)}\n`);
59
+ }
60
+ }
61
+ return lines;
62
+ }
63
+ export function csvSafe(str) {
64
+ if (!str) {
65
+ return '';
66
+ }
67
+ // Escape double quotes by doubling them, wrap in quotes if needed
68
+ const escaped = str.replaceAll('"', '""').replaceAll('\n', ' ');
69
+ if (escaped.includes(',') || escaped.includes('"') || escaped.includes(' ')) {
70
+ return `"${escaped}"`;
71
+ }
72
+ return escaped;
73
+ }
74
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.js","sourceRoot":"","sources":["../../src/formatters/csv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Logger, ThymianReport, ThymianReportSeverity } from '@thymian/core';
2
+ import { type Formatter } from '../formatter.js';
3
+ export declare const details: (text: string) => string;
4
+ export declare function mapSeverityToBadge(severity: ThymianReportSeverity): string;
5
+ export type MarkdownFormatterOptions = {
6
+ path: string;
7
+ };
8
+ export declare class MarkdownFormatter implements Formatter<MarkdownFormatterOptions> {
9
+ private readonly logger;
10
+ options: MarkdownFormatterOptions;
11
+ private readonly reports;
12
+ constructor(logger: Logger);
13
+ init(options: MarkdownFormatterOptions): void;
14
+ report(report: ThymianReport): void;
15
+ flush(): Promise<string | undefined>;
16
+ }
17
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/formatters/markdown.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EACb,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG1D,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,MAK1B,CAAC;AAEd,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAW1E;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,qBAAa,iBAAkB,YAAW,SAAS,CAAC,wBAAwB,CAAC;IAK/D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,EAAG,wBAAwB,CAAC;IAEnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAElB,MAAM,EAAE,MAAM;IAE3C,IAAI,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI;IAI7C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAI7B,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAuE3C"}
@@ -0,0 +1,85 @@
1
+ import * as path from 'node:path';
2
+ import { mkdir, writeFile } from 'fs/promises';
3
+ import { analyze } from '../formatter.js';
4
+ import { errorSymbol, hintSymbol, warnSymbol } from '../style.js';
5
+ export const details = (text) => `
6
+ <details>
7
+ <summary>More details</summary>
8
+ ${text}
9
+ </details>`;
10
+ export function mapSeverityToBadge(severity) {
11
+ if (severity === 'error') {
12
+ return `${errorSymbol} error`;
13
+ }
14
+ if (severity === 'warn') {
15
+ return `${warnSymbol} warning`;
16
+ }
17
+ if (severity === 'hint') {
18
+ return `${hintSymbol} hint`;
19
+ }
20
+ return 'info';
21
+ }
22
+ export class MarkdownFormatter {
23
+ logger;
24
+ options;
25
+ reports = [];
26
+ constructor(logger) {
27
+ this.logger = logger;
28
+ }
29
+ init(options) {
30
+ this.options = options;
31
+ }
32
+ report(report) {
33
+ this.reports.push(report);
34
+ }
35
+ async flush() {
36
+ if (this.reports.length === 0) {
37
+ return undefined;
38
+ }
39
+ const analysis = analyze(this.reports);
40
+ const lines = [];
41
+ lines.push('# Thymian Report');
42
+ const { numberOfReports, numberOfItems, severityCounts } = analysis.statistics;
43
+ lines.push(`A total of ${numberOfReports} reports with ${numberOfItems} items were found.`);
44
+ lines.push(`Of these there are ${severityCounts.error} errors, ${severityCounts.warn} warnings, ${severityCounts.hint} hints and ${severityCounts.info} infos.`);
45
+ lines.push('');
46
+ for (const report of analysis.reports) {
47
+ lines.push(`## ${report.source}`);
48
+ lines.push('');
49
+ if (report.message) {
50
+ lines.push(` ${report.message}`);
51
+ lines.push('');
52
+ lines.push('');
53
+ }
54
+ if (report.sections) {
55
+ for (const section of report.sections) {
56
+ lines.push(`### ${section.heading}`);
57
+ lines.push('');
58
+ for (const item of section.items) {
59
+ const sev = mapSeverityToBadge(item.severity);
60
+ lines.push(`- **${sev}**: ${item.message}`);
61
+ if (item.ruleName) {
62
+ lines.push(`<br/> *Rule: ${item.ruleName}*`);
63
+ }
64
+ if (item.details) {
65
+ lines.push(details(item.details));
66
+ }
67
+ if (item.links && item.links.length > 0) {
68
+ lines.push(' Related links:');
69
+ for (const link of item.links) {
70
+ lines.push(` - [${link.title ?? link.url}](${link.url})`);
71
+ }
72
+ }
73
+ }
74
+ lines.push('');
75
+ }
76
+ }
77
+ lines.push('');
78
+ }
79
+ await mkdir(path.dirname(this.options.path), { recursive: true });
80
+ await writeFile(this.options.path, lines.join('\n'), 'utf-8');
81
+ this.logger.debug(`Wrote Markdown report to ${this.options.path}.`);
82
+ return undefined;
83
+ }
84
+ }
85
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/formatters/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { ThymianReport, ThymianReportSeverity } from '@thymian/core';
2
+ import { type Formatter } from '../formatter.js';
3
+ export declare function formatSeverityPrefix(severity: ThymianReportSeverity): string;
4
+ export type TextFormatterOptions = {
5
+ summaryOnly: boolean;
6
+ path?: string;
7
+ };
8
+ export declare class TextFormatter implements Formatter<Partial<TextFormatterOptions>> {
9
+ options: TextFormatterOptions;
10
+ private readonly reports;
11
+ init(options: Partial<TextFormatterOptions>): void;
12
+ flush(): Promise<string | undefined>;
13
+ report(report: ThymianReport): void;
14
+ /**
15
+ * Ensure output is plain text (no ANSI escape sequences) when stdout is not a TTY.
16
+ *
17
+ * Chalk auto-detects TTY and sets `chalk.level = 0` for non-TTY, which prevents
18
+ * new styling. However, if chalk level was forced or if any ANSI codes slip through
19
+ * from other sources, this safety net strips them for non-TTY output.
20
+ */
21
+ private ensurePlainTextForNonTty;
22
+ }
23
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/formatters/text.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAI1E,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAsC1D,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAY5E;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAa,aAAc,YAAW,SAAS,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC5E,OAAO,EAAG,oBAAoB,CAAC;IAE/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI;IAO5C,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAuF1C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAInC;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;CAMjC"}
@@ -0,0 +1,140 @@
1
+ import * as path from 'node:path';
2
+ import { stripVTControlCharacters } from 'node:util';
3
+ import chalk from 'chalk';
4
+ import { mkdir, writeFile } from 'fs/promises';
5
+ import { analyze } from '../formatter.js';
6
+ import { errorSymbol, hintSymbol, successSymbol, warnSymbol, } from '../style.js';
7
+ /**
8
+ * Visible column width of a severity prefix in a monospace terminal.
9
+ *
10
+ * Unicode symbols (✖ U+2716, ⚠ U+26A0, ℹ U+2139) each occupy one terminal
11
+ * column in standard monospace fonts. We use hardcoded values instead of
12
+ * `.length` on template strings because JavaScript's string length counts
13
+ * UTF-16 code units, which may differ from visual column width for characters
14
+ * outside the BMP. For these specific BMP symbols the values happen to match.
15
+ *
16
+ * Widths: "✖ error: " = 9, "⚠ warning: " = 11, "ℹ hint: " = 8
17
+ */
18
+ function severityPrefixWidth(severity) {
19
+ switch (severity) {
20
+ case 'error':
21
+ return 9; // "✖ error: "
22
+ case 'warn':
23
+ return 11; // "⚠ warning: "
24
+ case 'hint':
25
+ return 8; // "ℹ hint: "
26
+ default:
27
+ return 0;
28
+ }
29
+ }
30
+ /**
31
+ * Regex matching severity-grouped section headings produced by the core report
32
+ * sorter in severity mode, e.g. "Errors (3)", "Warnings (1)", "Hints (2)", "Info (1)".
33
+ */
34
+ const SEVERITY_HEADING_RE = /^(Errors|Warnings|Hints|Info)\s*\(\d+\)$/;
35
+ export function formatSeverityPrefix(severity) {
36
+ if (severity === 'hint') {
37
+ return `${chalk.blue(`${hintSymbol} ${severity}`)}: `;
38
+ }
39
+ if (severity === 'warn') {
40
+ return `${chalk.yellow(`${warnSymbol} warning`)}: `;
41
+ }
42
+ if (severity === 'error') {
43
+ return `${chalk.red(`${errorSymbol} ${severity}`)}: `;
44
+ }
45
+ return '';
46
+ }
47
+ export class TextFormatter {
48
+ options;
49
+ reports = [];
50
+ init(options) {
51
+ this.options = {
52
+ summaryOnly: false,
53
+ ...options,
54
+ };
55
+ }
56
+ async flush() {
57
+ if (this.reports.length === 0) {
58
+ const message = `${chalk.green(successSymbol)} No problems found`;
59
+ if (this.options.path) {
60
+ const plainText = stripVTControlCharacters(message);
61
+ await mkdir(path.dirname(this.options.path), { recursive: true });
62
+ await writeFile(this.options.path, plainText, 'utf-8');
63
+ }
64
+ return this.ensurePlainTextForNonTty(message);
65
+ }
66
+ const analysis = analyze(this.reports);
67
+ const lines = [];
68
+ if (!this.options.summaryOnly) {
69
+ for (const report of analysis.reports) {
70
+ lines.push(`${report.source} ${Array.from({
71
+ length: Math.max(1, 80 - report.source.length),
72
+ })
73
+ .map(() => '─')
74
+ .join('')}`);
75
+ lines.push('');
76
+ if (report.message) {
77
+ lines.push(` ${report.message}`);
78
+ lines.push('');
79
+ lines.push('');
80
+ }
81
+ if (report.sections && report.sections.length > 0) {
82
+ for (const section of report.sections) {
83
+ const isSeverityGrouped = SEVERITY_HEADING_RE.test(section.heading);
84
+ lines.push(` ${chalk.bold(section.heading)}`);
85
+ lines.push('');
86
+ for (const item of section.items) {
87
+ // When grouped by severity the heading already conveys the
88
+ // severity level → skip the per-item severity prefix to
89
+ // avoid redundant "Errors (3) ✖ error: …" lines.
90
+ const prefix = isSeverityGrouped
91
+ ? ''
92
+ : formatSeverityPrefix(item.severity);
93
+ // 4 spaces base indentation + prefix + message
94
+ lines.push(` ${prefix}${item.message}`);
95
+ // When grouped by rule the heading already shows the rule
96
+ // name → skip the per-item rule name to avoid duplication.
97
+ const ruleRedundant = item.ruleName === section.heading;
98
+ if (item.ruleName && !ruleRedundant) {
99
+ // Align rule name under message text:
100
+ // 4 spaces base indent + prefix visible width
101
+ const indent = isSeverityGrouped
102
+ ? 4
103
+ : 4 + severityPrefixWidth(item.severity);
104
+ lines.push(`${' '.repeat(indent)}${chalk.dim(item.ruleName)}`);
105
+ }
106
+ }
107
+ lines.push('');
108
+ }
109
+ }
110
+ }
111
+ }
112
+ lines.push('');
113
+ const { error, warn, hint } = analysis.statistics.severityCounts;
114
+ lines.push(`Found ${chalk.red(`${error} ${error === 1 ? 'error' : 'errors'}`)}, ${chalk.yellow(`${warn} ${warn === 1 ? 'warning' : 'warnings'}`)} and ${chalk.blue(`${hint} ${hint === 1 ? 'hint' : 'hints'}`)}.`);
115
+ const coloredOutput = lines.join('\n');
116
+ if (this.options.path) {
117
+ const plainText = stripVTControlCharacters(coloredOutput);
118
+ await mkdir(path.dirname(this.options.path), { recursive: true });
119
+ await writeFile(this.options.path, plainText, 'utf-8');
120
+ }
121
+ return this.ensurePlainTextForNonTty(coloredOutput);
122
+ }
123
+ report(report) {
124
+ this.reports.push(report);
125
+ }
126
+ /**
127
+ * Ensure output is plain text (no ANSI escape sequences) when stdout is not a TTY.
128
+ *
129
+ * Chalk auto-detects TTY and sets `chalk.level = 0` for non-TTY, which prevents
130
+ * new styling. However, if chalk level was forced or if any ANSI codes slip through
131
+ * from other sources, this safety net strips them for non-TTY output.
132
+ */
133
+ ensurePlainTextForNonTty(output) {
134
+ if (!process.stdout.isTTY) {
135
+ return stripVTControlCharacters(output);
136
+ }
137
+ return output;
138
+ }
139
+ }
140
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/formatters/text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type Logger } from '@thymian/core';
2
+ import type { Formatter } from './formatter.js';
3
+ import { type CsvFormatterOptions } from './formatters/csv.js';
4
+ import { type MarkdownFormatterOptions } from './formatters/markdown.js';
5
+ import { type TextFormatterOptions } from './formatters/text.js';
6
+ export type Formatters = {
7
+ text: Partial<TextFormatterOptions>;
8
+ markdown: Partial<MarkdownFormatterOptions>;
9
+ csv: Partial<CsvFormatterOptions>;
10
+ };
11
+ export type ReporterPluginOptions = {
12
+ formatters?: Partial<Formatters>;
13
+ };
14
+ export type FormatterConstructor<T> = (logger: Logger) => Formatter<T>;
15
+ export type FormatterRegistryEntry<K extends keyof Formatters> = {
16
+ factory: FormatterConstructor<Formatters[K]>;
17
+ prepareOptions: <T = Formatters[K]>(options: Formatters[K], pluginOptions: {
18
+ cwd: string;
19
+ logger: Logger;
20
+ }) => Formatters[K] | T;
21
+ };
22
+ export declare const FORMATTER_REGISTRY: {
23
+ [K in keyof Formatters]: FormatterRegistryEntry<K>;
24
+ };
25
+ export declare function isValidFormatter(name: string): name is keyof Formatters;
26
+ export declare function getFormatters(config: ReporterPluginOptions['formatters'], cwd: string, logger: Logger): Promise<Formatter[]>;
27
+ //# sourceMappingURL=get-formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-formatters.d.ts","sourceRoot":"","sources":["../src/get-formatters.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,eAAe,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACpC,QAAQ,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC5C,GAAG,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC;AAEvE,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,MAAM,UAAU,IAAI;IAC/D,OAAO,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,cAAc,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAChC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EACtB,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC3C,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE;KAC9B,CAAC,IAAI,MAAM,UAAU,GAAG,sBAAsB,CAAC,CAAC,CAAC;CAoC1C,CAAC;AAEX,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,MAAM,UAAU,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,qBAAqB,CAAC,YAAY,CAAM,EAChD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,SAAS,EAAE,CAAC,CA+BtB"}
@@ -0,0 +1,61 @@
1
+ import { join } from 'node:path';
2
+ import { ThymianBaseError } from '@thymian/core';
3
+ import { CsvFormatter } from './formatters/csv.js';
4
+ import { MarkdownFormatter, } from './formatters/markdown.js';
5
+ import { TextFormatter } from './formatters/text.js';
6
+ export const FORMATTER_REGISTRY = {
7
+ text: {
8
+ factory: () => new TextFormatter(),
9
+ prepareOptions: (options, { cwd }) => ({
10
+ summaryOnly: false,
11
+ ...options,
12
+ ...(typeof options.path === 'string'
13
+ ? { path: join(cwd, options.path) }
14
+ : {}),
15
+ }),
16
+ },
17
+ markdown: {
18
+ factory: (logger) => new MarkdownFormatter(logger),
19
+ prepareOptions: (options, pluginOptions) => ({
20
+ path: join(pluginOptions.cwd, typeof options.path === 'string'
21
+ ? options.path
22
+ : '.thymian/reports/report.md'),
23
+ ...options,
24
+ }),
25
+ },
26
+ csv: {
27
+ factory: (logger) => new CsvFormatter(logger),
28
+ prepareOptions: (options, { cwd }) => ({
29
+ path: join(cwd, typeof options.path === 'string'
30
+ ? options.path
31
+ : '.thymian/reports/report.csv'),
32
+ ...options,
33
+ }),
34
+ },
35
+ };
36
+ export function isValidFormatter(name) {
37
+ return Object.hasOwn(FORMATTER_REGISTRY, name);
38
+ }
39
+ export async function getFormatters(config = {}, cwd, logger) {
40
+ return Promise.all(Object.entries(config).map(async ([name, options]) => {
41
+ if (!isValidFormatter(name)) {
42
+ throw new ThymianBaseError(`Unknown formatter "${name}". Available formatters: ${Object.keys(FORMATTER_REGISTRY).join(', ')}.`, {
43
+ name: 'UnknownFormatterError',
44
+ ref: 'https://thymian.dev/references/errors/unknown-formatter-error/',
45
+ suggestions: [
46
+ 'If you want to add your own formatter, implement a new plugin and listen on the `core.report` event and/or open a Github issue.',
47
+ ],
48
+ });
49
+ }
50
+ const formatterConfig = FORMATTER_REGISTRY[name];
51
+ const formatter = formatterConfig.factory(logger);
52
+ const preparedOptions = formatterConfig.prepareOptions(options, {
53
+ cwd,
54
+ logger,
55
+ });
56
+ // we know that the options are valid, so we can safely cast them
57
+ await formatter.init(preparedOptions);
58
+ return formatter;
59
+ }));
60
+ }
61
+ //# sourceMappingURL=get-formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-formatters.js","sourceRoot":"","sources":["../src/get-formatters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type ThymianPlugin } from '@thymian/core';
2
+ import { type Formatters } from './get-formatters.js';
3
+ export type ReporterPluginOptions = {
4
+ formatters?: Partial<Formatters>;
5
+ };
6
+ export declare const reporterPlugin: ThymianPlugin<ReporterPluginOptions>;
7
+ export default reporterPlugin;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAsB,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,KAAK,UAAU,EAAiB,MAAM,qBAAqB,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,qBAAqB,CAgH/D,CAAC;AAEF,eAAe,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,100 @@
1
+ import {} from '@thymian/core';
2
+ import { getFormatters } from './get-formatters.js';
3
+ export const reporterPlugin = {
4
+ name: '@thymian/plugin-reporter',
5
+ options: {
6
+ type: 'object',
7
+ additionalProperties: false,
8
+ properties: {
9
+ formatters: {
10
+ nullable: true,
11
+ description: 'Configuration for different report formatters',
12
+ type: 'object',
13
+ properties: {
14
+ text: {
15
+ nullable: true,
16
+ description: 'Configuration for the text (console) formatter',
17
+ type: 'object',
18
+ properties: {
19
+ summaryOnly: {
20
+ description: 'When true, only shows the summary without detailed reports',
21
+ type: 'boolean',
22
+ nullable: true,
23
+ },
24
+ path: {
25
+ description: 'File path where the plain text report will be saved (ANSI escape codes are stripped)',
26
+ type: 'string',
27
+ nullable: true,
28
+ },
29
+ },
30
+ additionalProperties: false,
31
+ },
32
+ markdown: {
33
+ description: 'Configuration for the Markdown formatter',
34
+ nullable: true,
35
+ type: 'object',
36
+ properties: {
37
+ path: {
38
+ description: 'File path where the markdown report will be saved',
39
+ type: 'string',
40
+ nullable: true,
41
+ },
42
+ },
43
+ additionalProperties: false,
44
+ },
45
+ csv: {
46
+ description: 'Configuration for the CSV formatter',
47
+ nullable: true,
48
+ type: 'object',
49
+ properties: {
50
+ path: {
51
+ description: 'File path where the CSV report will be saved',
52
+ type: 'string',
53
+ nullable: true,
54
+ },
55
+ },
56
+ additionalProperties: false,
57
+ },
58
+ },
59
+ additionalProperties: false,
60
+ },
61
+ },
62
+ },
63
+ version: '0.x',
64
+ events: {
65
+ listensOn: ['core.report'],
66
+ },
67
+ actions: {
68
+ listensOn: ['core.report.flush', 'core.close'],
69
+ },
70
+ async plugin(emitter, logger, { formatters: userFormatters, cwd }) {
71
+ const formatters = Object.fromEntries(Object.entries({
72
+ text: {},
73
+ ...(userFormatters ?? {}),
74
+ }).filter(([, options]) => options != null));
75
+ let hasFlushed = false;
76
+ const flushReporters = async () => {
77
+ if (hasFlushed) {
78
+ return {};
79
+ }
80
+ hasFlushed = true;
81
+ const results = await Promise.all(reporters.map(async (r) => r.flush()));
82
+ // Collect text output from formatters that return strings (e.g. TextFormatter)
83
+ const textParts = results.filter((r) => typeof r === 'string');
84
+ return textParts.length > 0 ? { text: textParts.join('\n') } : {};
85
+ };
86
+ const reporters = await getFormatters(formatters, cwd, logger);
87
+ emitter.on('core.report', async (report) => {
88
+ reporters.forEach((r) => r.report(report));
89
+ });
90
+ emitter.onAction('core.report.flush', async (_event, ctx) => {
91
+ ctx.reply(await flushReporters());
92
+ });
93
+ emitter.onAction('core.close', async (_event, ctx) => {
94
+ await flushReporters();
95
+ ctx.reply();
96
+ });
97
+ },
98
+ };
99
+ export default reporterPlugin;
100
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare const errorSymbol = "\u2716";
2
+ export declare const warnSymbol = "\u26A0";
3
+ export declare const hintSymbol = "\u2139";
4
+ export declare const successSymbol = "\u2713";
5
+ //# sourceMappingURL=style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,WAAM,CAAC;AAC/B,eAAO,MAAM,UAAU,WAAM,CAAC;AAC9B,eAAO,MAAM,UAAU,WAAM,CAAC;AAC9B,eAAO,MAAM,aAAa,WAAM,CAAC"}
package/dist/style.js ADDED
@@ -0,0 +1,5 @@
1
+ export const errorSymbol = '✖';
2
+ export const warnSymbol = '⚠';
3
+ export const hintSymbol = 'ℹ';
4
+ export const successSymbol = '✓';
5
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC/B,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC;AAC9B,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function createList(list: string[]): string;
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAgBjD"}
package/dist/utils.js ADDED
@@ -0,0 +1,17 @@
1
+ export function createList(list) {
2
+ if (list.length === 0) {
3
+ return '';
4
+ }
5
+ else if (list.length === 1) {
6
+ return `${list[0]}`;
7
+ }
8
+ else {
9
+ // length >= 2
10
+ const last = list.at(-1);
11
+ return (list
12
+ .slice(0, list.length - 1)
13
+ .map((el) => `${el}`)
14
+ .join(', ') + ` and ${last}`);
15
+ }
16
+ }
17
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,cAAc;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAW,CAAC;QAEnC,OAAO,CACL,IAAI;aACD,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACzB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;aACpB,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,EAAE,CAC/B,CAAC;IACJ,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@thymian/plugin-reporter",
3
+ "version": "0.0.0-PLACEHOLDER",
4
+ "description": "Thymian plugin for formatting and outputting conformance analysis reports",
5
+ "license": "AGPL-3.0-only",
6
+ "bugs": {
7
+ "url": "https://github.com/thymianofficial/thymian/issues"
8
+ },
9
+ "homepage": "https://thymian.dev",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/thymianofficial/thymian.git",
13
+ "directory": "packages/plugin-reporter"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "keywords": [
19
+ "thymian",
20
+ "api-reports",
21
+ "ci",
22
+ "reporter",
23
+ "reporting",
24
+ "test-reporter"
25
+ ],
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ "./package.json": "./package.json",
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "default": "./dist/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "!**/*.tsbuildinfo"
41
+ ],
42
+ "dependencies": {
43
+ "chalk": "^4.1.2",
44
+ "tslib": "^2.3.0",
45
+ "@thymian/core": "0.0.0-PLACEHOLDER"
46
+ }
47
+ }