qualyx 0.3.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.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +523 -0
  3. package/dist/cli/commands/init.d.ts +6 -0
  4. package/dist/cli/commands/init.d.ts.map +1 -0
  5. package/dist/cli/commands/init.js +124 -0
  6. package/dist/cli/commands/init.js.map +1 -0
  7. package/dist/cli/commands/list.d.ts +3 -0
  8. package/dist/cli/commands/list.d.ts.map +1 -0
  9. package/dist/cli/commands/list.js +122 -0
  10. package/dist/cli/commands/list.js.map +1 -0
  11. package/dist/cli/commands/run.d.ts +12 -0
  12. package/dist/cli/commands/run.d.ts.map +1 -0
  13. package/dist/cli/commands/run.js +160 -0
  14. package/dist/cli/commands/run.js.map +1 -0
  15. package/dist/cli/commands/schedule.d.ts +19 -0
  16. package/dist/cli/commands/schedule.d.ts.map +1 -0
  17. package/dist/cli/commands/schedule.js +240 -0
  18. package/dist/cli/commands/schedule.js.map +1 -0
  19. package/dist/cli/commands/validate.d.ts +3 -0
  20. package/dist/cli/commands/validate.d.ts.map +1 -0
  21. package/dist/cli/commands/validate.js +47 -0
  22. package/dist/cli/commands/validate.js.map +1 -0
  23. package/dist/cli/index.d.ts +3 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +194 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/core/claude-runner.d.ts +23 -0
  28. package/dist/core/claude-runner.d.ts.map +1 -0
  29. package/dist/core/claude-runner.js +196 -0
  30. package/dist/core/claude-runner.js.map +1 -0
  31. package/dist/core/config-loader.d.ts +137 -0
  32. package/dist/core/config-loader.d.ts.map +1 -0
  33. package/dist/core/config-loader.js +239 -0
  34. package/dist/core/config-loader.js.map +1 -0
  35. package/dist/core/executor.d.ts +75 -0
  36. package/dist/core/executor.d.ts.map +1 -0
  37. package/dist/core/executor.js +337 -0
  38. package/dist/core/executor.js.map +1 -0
  39. package/dist/core/index.d.ts +6 -0
  40. package/dist/core/index.d.ts.map +1 -0
  41. package/dist/core/index.js +7 -0
  42. package/dist/core/index.js.map +1 -0
  43. package/dist/core/prompt-builder.d.ts +24 -0
  44. package/dist/core/prompt-builder.d.ts.map +1 -0
  45. package/dist/core/prompt-builder.js +145 -0
  46. package/dist/core/prompt-builder.js.map +1 -0
  47. package/dist/core/retry-handler.d.ts +42 -0
  48. package/dist/core/retry-handler.d.ts.map +1 -0
  49. package/dist/core/retry-handler.js +126 -0
  50. package/dist/core/retry-handler.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +16 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/integrations/email.d.ts +38 -0
  56. package/dist/integrations/email.d.ts.map +1 -0
  57. package/dist/integrations/email.js +216 -0
  58. package/dist/integrations/email.js.map +1 -0
  59. package/dist/integrations/index.d.ts +5 -0
  60. package/dist/integrations/index.d.ts.map +1 -0
  61. package/dist/integrations/index.js +5 -0
  62. package/dist/integrations/index.js.map +1 -0
  63. package/dist/integrations/jira.d.ts +68 -0
  64. package/dist/integrations/jira.d.ts.map +1 -0
  65. package/dist/integrations/jira.js +288 -0
  66. package/dist/integrations/jira.js.map +1 -0
  67. package/dist/integrations/slack.d.ts +66 -0
  68. package/dist/integrations/slack.d.ts.map +1 -0
  69. package/dist/integrations/slack.js +192 -0
  70. package/dist/integrations/slack.js.map +1 -0
  71. package/dist/integrations/teams.d.ts +72 -0
  72. package/dist/integrations/teams.d.ts.map +1 -0
  73. package/dist/integrations/teams.js +197 -0
  74. package/dist/integrations/teams.js.map +1 -0
  75. package/dist/reporters/console.d.ts +83 -0
  76. package/dist/reporters/console.d.ts.map +1 -0
  77. package/dist/reporters/console.js +299 -0
  78. package/dist/reporters/console.js.map +1 -0
  79. package/dist/reporters/html.d.ts +29 -0
  80. package/dist/reporters/html.d.ts.map +1 -0
  81. package/dist/reporters/html.js +105 -0
  82. package/dist/reporters/html.js.map +1 -0
  83. package/dist/storage/results.d.ts +61 -0
  84. package/dist/storage/results.d.ts.map +1 -0
  85. package/dist/storage/results.js +111 -0
  86. package/dist/storage/results.js.map +1 -0
  87. package/dist/storage/sqlite.d.ts +70 -0
  88. package/dist/storage/sqlite.d.ts.map +1 -0
  89. package/dist/storage/sqlite.js +240 -0
  90. package/dist/storage/sqlite.js.map +1 -0
  91. package/dist/types/index.d.ts +1239 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/index.js +105 -0
  94. package/dist/types/index.js.map +1 -0
  95. package/package.json +75 -0
  96. package/templates/crontab.hbs +24 -0
  97. package/templates/github-schedule.yml.hbs +153 -0
  98. package/templates/prompt.md.hbs +147 -0
  99. package/templates/report.html.hbs +423 -0
  100. package/templates/slack-message.json.hbs +93 -0
@@ -0,0 +1,299 @@
1
+ import chalk from 'chalk';
2
+ /**
3
+ * Format duration in a human-readable format.
4
+ */
5
+ function formatDuration(ms) {
6
+ if (ms < 1000) {
7
+ return `${ms}ms`;
8
+ }
9
+ if (ms < 60000) {
10
+ return `${(ms / 1000).toFixed(1)}s`;
11
+ }
12
+ const minutes = Math.floor(ms / 60000);
13
+ const seconds = Math.floor((ms % 60000) / 1000);
14
+ return `${minutes}m ${seconds}s`;
15
+ }
16
+ /**
17
+ * Get the status icon for a test status.
18
+ */
19
+ function getStatusIcon(status) {
20
+ switch (status) {
21
+ case 'passed':
22
+ return chalk.green('✓');
23
+ case 'failed':
24
+ return chalk.red('✗');
25
+ case 'skipped':
26
+ return chalk.yellow('○');
27
+ case 'pending':
28
+ return chalk.gray('◌');
29
+ default:
30
+ return chalk.gray('?');
31
+ }
32
+ }
33
+ /**
34
+ * Get the severity badge.
35
+ */
36
+ function getSeverityBadge(severity) {
37
+ switch (severity) {
38
+ case 'critical':
39
+ return chalk.bgRed.white(` ${severity.toUpperCase()} `);
40
+ case 'high':
41
+ return chalk.bgYellow.black(` ${severity.toUpperCase()} `);
42
+ case 'medium':
43
+ return chalk.bgBlue.white(` ${severity.toUpperCase()} `);
44
+ case 'low':
45
+ return chalk.bgGray.white(` ${severity.toUpperCase()} `);
46
+ default:
47
+ return chalk.bgGray.white(` ${severity.toUpperCase()} `);
48
+ }
49
+ }
50
+ /**
51
+ * Console reporter that provides colored terminal output.
52
+ */
53
+ export class ConsoleReporter {
54
+ options;
55
+ startTime = 0;
56
+ totalTests = 0;
57
+ completedTests = 0;
58
+ constructor(options = {}) {
59
+ this.options = options;
60
+ }
61
+ /**
62
+ * Get executor callbacks for live reporting.
63
+ */
64
+ getCallbacks() {
65
+ return {
66
+ onRunStart: this.onRunStart.bind(this),
67
+ onTestStart: this.onTestStart.bind(this),
68
+ onTestComplete: this.onTestComplete.bind(this),
69
+ onTestRetry: this.onTestRetry.bind(this),
70
+ onRunComplete: this.onRunComplete.bind(this),
71
+ onSetupStart: this.onSetupStart.bind(this),
72
+ onSetupComplete: this.onSetupComplete.bind(this),
73
+ };
74
+ }
75
+ /**
76
+ * Called when setup starts for an app.
77
+ */
78
+ onSetupStart(app) {
79
+ console.log(chalk.cyan(` Setting up: ${app.name}`));
80
+ }
81
+ /**
82
+ * Called when setup completes for an app.
83
+ */
84
+ onSetupComplete(app, success, error) {
85
+ if (success) {
86
+ console.log(chalk.green(` ✓ Setup complete: ${app.name}`));
87
+ }
88
+ else {
89
+ console.log(chalk.red(` ✗ Setup failed: ${app.name}`));
90
+ if (error) {
91
+ console.log(chalk.red(` Error: ${error}`));
92
+ }
93
+ }
94
+ console.log();
95
+ }
96
+ /**
97
+ * Called when a test run starts.
98
+ */
99
+ onRunStart(totalTests) {
100
+ this.startTime = Date.now();
101
+ this.totalTests = totalTests;
102
+ this.completedTests = 0;
103
+ console.log();
104
+ console.log(chalk.bold.cyan(' Qualyx Test Runner'));
105
+ console.log(chalk.gray(` Running ${totalTests} test${totalTests !== 1 ? 's' : ''}...`));
106
+ console.log();
107
+ }
108
+ /**
109
+ * Called when a test starts.
110
+ */
111
+ onTestStart(app, rule) {
112
+ if (this.options.verbose) {
113
+ console.log(chalk.gray(` Starting: ${app.name} / ${rule.name}`));
114
+ }
115
+ }
116
+ /**
117
+ * Called when a test completes.
118
+ */
119
+ onTestComplete(result) {
120
+ this.completedTests++;
121
+ const icon = getStatusIcon(result.status);
122
+ const severity = getSeverityBadge(result.severity);
123
+ const duration = chalk.gray(`(${formatDuration(result.duration)})`);
124
+ console.log(` ${icon} ${result.appName} / ${result.ruleName} ${severity} ${duration}`);
125
+ if (result.retryCount > 0) {
126
+ console.log(chalk.gray(` Retried ${result.retryCount} time(s)`));
127
+ }
128
+ if (result.status === 'failed' && result.error) {
129
+ console.log(chalk.red(` Error: ${result.error}`));
130
+ }
131
+ if (this.options.verbose && result.steps.length > 0) {
132
+ console.log(chalk.gray(' Steps:'));
133
+ for (const step of result.steps) {
134
+ const stepIcon = getStatusIcon(step.status);
135
+ console.log(chalk.gray(` ${stepIcon} ${step.step}`));
136
+ if (step.error) {
137
+ console.log(chalk.red(` ${step.error}`));
138
+ }
139
+ }
140
+ }
141
+ if (this.options.verbose && result.validations.length > 0) {
142
+ console.log(chalk.gray(' Validations:'));
143
+ for (const validation of result.validations) {
144
+ const validationIcon = validation.passed ? chalk.green('✓') : chalk.red('✗');
145
+ console.log(chalk.gray(` ${validationIcon} ${validation.validation}`));
146
+ }
147
+ }
148
+ }
149
+ /**
150
+ * Called when a test is being retried.
151
+ */
152
+ onTestRetry(app, rule, attempt, maxRetries) {
153
+ console.log(chalk.yellow(` Retrying (${attempt}/${maxRetries}): ${app.name} / ${rule.name}`));
154
+ }
155
+ /**
156
+ * Called when a test run completes.
157
+ */
158
+ onRunComplete(result) {
159
+ console.log();
160
+ this.printSummary(result);
161
+ }
162
+ /**
163
+ * Print the final summary.
164
+ */
165
+ printSummary(result) {
166
+ const { totalTests, passed, failed, skipped, duration } = result;
167
+ console.log(chalk.bold(' Summary'));
168
+ console.log(chalk.gray(' ─'.repeat(30)));
169
+ // Stats
170
+ console.log(` Total: ${totalTests}`);
171
+ console.log(` ${chalk.green('Passed:')} ${passed}`);
172
+ console.log(` ${chalk.red('Failed:')} ${failed}`);
173
+ if (skipped > 0) {
174
+ console.log(` ${chalk.yellow('Skipped:')} ${skipped}`);
175
+ }
176
+ console.log(` Duration: ${formatDuration(duration)}`);
177
+ // Pass rate
178
+ if (totalTests > 0) {
179
+ const passRate = ((passed / totalTests) * 100).toFixed(1);
180
+ console.log(` Pass Rate: ${passRate}%`);
181
+ }
182
+ console.log();
183
+ // Final status
184
+ if (failed === 0) {
185
+ console.log(chalk.green.bold(' ✓ All tests passed!'));
186
+ }
187
+ else {
188
+ console.log(chalk.red.bold(` ✗ ${failed} test${failed !== 1 ? 's' : ''} failed`));
189
+ }
190
+ console.log();
191
+ }
192
+ /**
193
+ * Print detailed results (for verbose mode or separate report).
194
+ */
195
+ printDetailedResults(result) {
196
+ console.log();
197
+ console.log(chalk.bold(' Detailed Results'));
198
+ console.log(chalk.gray(' ─'.repeat(30)));
199
+ for (const test of result.results) {
200
+ console.log();
201
+ const icon = getStatusIcon(test.status);
202
+ const severity = getSeverityBadge(test.severity);
203
+ console.log(` ${icon} ${test.ruleName} ${severity}`);
204
+ console.log(chalk.gray(` App: ${test.appName}`));
205
+ console.log(chalk.gray(` ID: ${test.ruleId}`));
206
+ console.log(chalk.gray(` Duration: ${formatDuration(test.duration)}`));
207
+ if (test.steps.length > 0) {
208
+ console.log(chalk.gray(' Steps:'));
209
+ for (const step of test.steps) {
210
+ const stepIcon = getStatusIcon(step.status);
211
+ console.log(chalk.gray(` ${stepIcon} ${step.step}`));
212
+ }
213
+ }
214
+ if (test.validations.length > 0) {
215
+ console.log(chalk.gray(' Validations:'));
216
+ for (const validation of test.validations) {
217
+ const validationIcon = validation.passed ? chalk.green('✓') : chalk.red('✗');
218
+ console.log(chalk.gray(` ${validationIcon} ${validation.validation}`));
219
+ }
220
+ }
221
+ if (test.error) {
222
+ console.log(chalk.red(` Error: ${test.error}`));
223
+ }
224
+ }
225
+ console.log();
226
+ }
227
+ }
228
+ /**
229
+ * Print a preview of tests (for dry-run mode).
230
+ */
231
+ export function printDryRunPreview(previews, showPrompts = false) {
232
+ console.log();
233
+ console.log(chalk.bold.cyan(' Qualyx Dry Run Preview'));
234
+ console.log(chalk.gray(` ${previews.length} test${previews.length !== 1 ? 's' : ''} would be executed`));
235
+ console.log();
236
+ for (const preview of previews) {
237
+ const severity = getSeverityBadge(preview.severity);
238
+ console.log(` ${chalk.gray('○')} ${preview.app} / ${preview.rule} ${severity}`);
239
+ console.log(chalk.gray(` Steps: ${preview.steps}`));
240
+ if (showPrompts) {
241
+ console.log();
242
+ console.log(chalk.gray(' Prompt:'));
243
+ console.log(chalk.gray(' ─'.repeat(20)));
244
+ const lines = preview.prompt.split('\n');
245
+ for (const line of lines) {
246
+ console.log(chalk.gray(` ${line}`));
247
+ }
248
+ console.log(chalk.gray(' ─'.repeat(20)));
249
+ console.log();
250
+ }
251
+ }
252
+ console.log();
253
+ console.log(chalk.yellow(' No tests were executed (dry-run mode)'));
254
+ console.log();
255
+ }
256
+ /**
257
+ * Print validation results (for qualyx validate command).
258
+ */
259
+ export function printValidationResult(filePath, valid, errors, warnings) {
260
+ console.log();
261
+ if (valid) {
262
+ console.log(chalk.green(` ✓ Configuration is valid: ${filePath}`));
263
+ }
264
+ else {
265
+ console.log(chalk.red(` ✗ Configuration is invalid: ${filePath}`));
266
+ for (const error of errors) {
267
+ console.log(chalk.red(` - ${error}`));
268
+ }
269
+ }
270
+ if (warnings.length > 0) {
271
+ console.log();
272
+ console.log(chalk.yellow(' Warnings:'));
273
+ for (const warning of warnings) {
274
+ console.log(chalk.yellow(` - ${warning}`));
275
+ }
276
+ }
277
+ console.log();
278
+ }
279
+ /**
280
+ * Print a list of apps and rules (for qualyx list command).
281
+ */
282
+ export function printList(apps) {
283
+ console.log();
284
+ console.log(chalk.bold.cyan(' Qualyx Configuration'));
285
+ console.log();
286
+ for (const app of apps) {
287
+ console.log(chalk.bold(` ${app.name}`));
288
+ console.log(chalk.gray(` URL: ${app.url}`));
289
+ console.log(chalk.gray(` Rules: ${app.rules.length}`));
290
+ console.log();
291
+ for (const rule of app.rules) {
292
+ const severity = getSeverityBadge(rule.severity);
293
+ console.log(` ${chalk.gray('•')} ${rule.name} ${severity}`);
294
+ console.log(chalk.gray(` ID: ${rule.id}`));
295
+ }
296
+ console.log();
297
+ }
298
+ }
299
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/reporters/console.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { RunResult } from '../types/index.js';
2
+ export interface HtmlReporterOptions {
3
+ outputDir: string;
4
+ fileName?: string;
5
+ }
6
+ /**
7
+ * HTML reporter that generates visual test reports.
8
+ */
9
+ export declare class HtmlReporter {
10
+ private options;
11
+ constructor(options?: Partial<HtmlReporterOptions>);
12
+ /**
13
+ * Generate an HTML report from run results.
14
+ */
15
+ generate(result: RunResult, organization?: string): string;
16
+ /**
17
+ * Generate and save the HTML report to a file.
18
+ */
19
+ save(result: RunResult, organization?: string): string;
20
+ /**
21
+ * Generate a timestamped report filename.
22
+ */
23
+ static generateFileName(result: RunResult): string;
24
+ }
25
+ /**
26
+ * Quick function to generate and save an HTML report.
27
+ */
28
+ export declare function generateHtmlReport(result: RunResult, options?: Partial<HtmlReporterOptions>, organization?: string): string;
29
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/reporters/html.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AA4CnD,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAOD;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAsB;gBAEzB,OAAO,GAAE,OAAO,CAAC,mBAAmB,CAAM;IAItD;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,GAAE,MAAiB,GAAG,MAAM;IAqBpE;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,GAAE,MAAiB,GAAG,MAAM;IAkBhE;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM;CAKnD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,OAAO,CAAC,mBAAmB,CAAM,EAC1C,YAAY,GAAE,MAAiB,GAC9B,MAAM,CAGR"}
@@ -0,0 +1,105 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
2
+ import { resolve, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import Handlebars from 'handlebars';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+ // Load and compile the HTML report template
8
+ const templatePath = resolve(__dirname, '../../templates/report.html.hbs');
9
+ let reportTemplate = null;
10
+ function getReportTemplate() {
11
+ if (!reportTemplate) {
12
+ const templateSource = readFileSync(templatePath, 'utf-8');
13
+ reportTemplate = Handlebars.compile(templateSource);
14
+ }
15
+ return reportTemplate;
16
+ }
17
+ // Register Handlebars helpers for HTML reports
18
+ Handlebars.registerHelper('formatDate', (dateString) => {
19
+ const date = new Date(dateString);
20
+ return date.toLocaleString('en-US', {
21
+ year: 'numeric',
22
+ month: 'short',
23
+ day: 'numeric',
24
+ hour: '2-digit',
25
+ minute: '2-digit',
26
+ second: '2-digit',
27
+ });
28
+ });
29
+ Handlebars.registerHelper('formatDuration', (ms) => {
30
+ if (ms < 1000) {
31
+ return `${ms}ms`;
32
+ }
33
+ if (ms < 60000) {
34
+ return `${(ms / 1000).toFixed(1)}s`;
35
+ }
36
+ const minutes = Math.floor(ms / 60000);
37
+ const seconds = Math.floor((ms % 60000) / 1000);
38
+ return `${minutes}m ${seconds}s`;
39
+ });
40
+ Handlebars.registerHelper('eq', (a, b) => a === b);
41
+ const DEFAULT_OPTIONS = {
42
+ outputDir: './qualyx-reports',
43
+ fileName: 'report.html',
44
+ };
45
+ /**
46
+ * HTML reporter that generates visual test reports.
47
+ */
48
+ export class HtmlReporter {
49
+ options;
50
+ constructor(options = {}) {
51
+ this.options = { ...DEFAULT_OPTIONS, ...options };
52
+ }
53
+ /**
54
+ * Generate an HTML report from run results.
55
+ */
56
+ generate(result, organization = 'Qualyx') {
57
+ const template = getReportTemplate();
58
+ // Calculate percentages for progress bar
59
+ const total = result.totalTests || 1; // Avoid division by zero
60
+ const passedPercent = (result.passed / total) * 100;
61
+ const failedPercent = (result.failed / total) * 100;
62
+ const skippedPercent = (result.skipped / total) * 100;
63
+ const templateData = {
64
+ run: result,
65
+ organization,
66
+ passedPercent: passedPercent.toFixed(1),
67
+ failedPercent: failedPercent.toFixed(1),
68
+ skippedPercent: skippedPercent.toFixed(1),
69
+ version: '0.1.0',
70
+ };
71
+ return template(templateData);
72
+ }
73
+ /**
74
+ * Generate and save the HTML report to a file.
75
+ */
76
+ save(result, organization = 'Qualyx') {
77
+ const html = this.generate(result, organization);
78
+ // Ensure output directory exists
79
+ const outputDir = resolve(this.options.outputDir);
80
+ if (!existsSync(outputDir)) {
81
+ mkdirSync(outputDir, { recursive: true });
82
+ }
83
+ // Generate filename with timestamp if not specified
84
+ const fileName = this.options.fileName || `report-${result.runId.slice(0, 8)}.html`;
85
+ const filePath = resolve(outputDir, fileName);
86
+ writeFileSync(filePath, html, 'utf-8');
87
+ return filePath;
88
+ }
89
+ /**
90
+ * Generate a timestamped report filename.
91
+ */
92
+ static generateFileName(result) {
93
+ const date = new Date(result.startedAt);
94
+ const timestamp = date.toISOString().replace(/[:.]/g, '-').slice(0, 19);
95
+ return `report-${timestamp}.html`;
96
+ }
97
+ }
98
+ /**
99
+ * Quick function to generate and save an HTML report.
100
+ */
101
+ export function generateHtmlReport(result, options = {}, organization = 'Qualyx') {
102
+ const reporter = new HtmlReporter(options);
103
+ return reporter.save(result, organization);
104
+ }
105
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/reporters/html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { QualyxConfig, RunResult, TestResult } from '../types/index.js';
2
+ /**
3
+ * Generate a hash of the configuration for change detection.
4
+ */
5
+ export declare function hashConfig(config: QualyxConfig): string;
6
+ /**
7
+ * Save a run result to storage.
8
+ */
9
+ export declare function saveRunResult(result: RunResult, config?: QualyxConfig): void;
10
+ /**
11
+ * Get the latest run result.
12
+ */
13
+ export declare function getLatestRunResult(): RunResult | null;
14
+ /**
15
+ * Get a run result by ID.
16
+ */
17
+ export declare function getRunResult(runId: string): RunResult | null;
18
+ /**
19
+ * Get recent run results.
20
+ */
21
+ export declare function getRecentRunResults(limit?: number): RunResult[];
22
+ /**
23
+ * Get runs that had failures.
24
+ */
25
+ export declare function getFailedRunResults(limit?: number): RunResult[];
26
+ /**
27
+ * Get history for a specific test rule.
28
+ */
29
+ export declare function getTestHistory(ruleId: string, appName: string, limit?: number): TestResult[];
30
+ /**
31
+ * Get statistics for a test rule.
32
+ */
33
+ export declare function getTestStats(ruleId: string, appName: string): {
34
+ total: number;
35
+ passed: number;
36
+ failed: number;
37
+ passRate: number;
38
+ };
39
+ /**
40
+ * Clean up old run data.
41
+ */
42
+ export declare function cleanupOldRuns(daysToKeep?: number): number;
43
+ /**
44
+ * Analyze trends for a test rule.
45
+ */
46
+ export declare function analyzeTestTrend(ruleId: string, appName: string, limit?: number): {
47
+ ruleId: string;
48
+ appName: string;
49
+ totalRuns: number;
50
+ passed: number;
51
+ failed: number;
52
+ passRate: number;
53
+ avgDuration: number;
54
+ trend: "improving" | "degrading" | "stable";
55
+ history: {
56
+ status: import("../types/index.js").TestStatus;
57
+ duration: number;
58
+ startedAt: string;
59
+ }[];
60
+ } | null;
61
+ //# sourceMappingURL=results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/storage/results.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG7E;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAGvD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAI5E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,GAAG,IAAI,CAGrD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAG5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,GAAE,MAAW,GAAG,SAAS,EAAE,CAGnE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,GAAE,MAAW,GAAG,SAAS,EAAE,CAGnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE,CAGhG;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;EAG3D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,GAAE,MAAW,GAAG,MAAM,CAG9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW;;;;;;;;;;;;;;SA6CnF"}
@@ -0,0 +1,111 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { getStorage } from './sqlite.js';
3
+ /**
4
+ * Generate a hash of the configuration for change detection.
5
+ */
6
+ export function hashConfig(config) {
7
+ const content = JSON.stringify(config);
8
+ return createHash('sha256').update(content).digest('hex').slice(0, 16);
9
+ }
10
+ /**
11
+ * Save a run result to storage.
12
+ */
13
+ export function saveRunResult(result, config) {
14
+ const storage = getStorage();
15
+ const configHash = config ? hashConfig(config) : undefined;
16
+ storage.saveRun(result, configHash);
17
+ }
18
+ /**
19
+ * Get the latest run result.
20
+ */
21
+ export function getLatestRunResult() {
22
+ const storage = getStorage();
23
+ return storage.getLatestRun();
24
+ }
25
+ /**
26
+ * Get a run result by ID.
27
+ */
28
+ export function getRunResult(runId) {
29
+ const storage = getStorage();
30
+ return storage.getRun(runId);
31
+ }
32
+ /**
33
+ * Get recent run results.
34
+ */
35
+ export function getRecentRunResults(limit = 10) {
36
+ const storage = getStorage();
37
+ return storage.getRecentRuns(limit);
38
+ }
39
+ /**
40
+ * Get runs that had failures.
41
+ */
42
+ export function getFailedRunResults(limit = 10) {
43
+ const storage = getStorage();
44
+ return storage.getFailedRuns(limit);
45
+ }
46
+ /**
47
+ * Get history for a specific test rule.
48
+ */
49
+ export function getTestHistory(ruleId, appName, limit = 10) {
50
+ const storage = getStorage();
51
+ return storage.getTestHistory(ruleId, appName, limit);
52
+ }
53
+ /**
54
+ * Get statistics for a test rule.
55
+ */
56
+ export function getTestStats(ruleId, appName) {
57
+ const storage = getStorage();
58
+ return storage.getRuleStats(ruleId, appName);
59
+ }
60
+ /**
61
+ * Clean up old run data.
62
+ */
63
+ export function cleanupOldRuns(daysToKeep = 30) {
64
+ const storage = getStorage();
65
+ return storage.cleanupOldRuns(daysToKeep);
66
+ }
67
+ /**
68
+ * Analyze trends for a test rule.
69
+ */
70
+ export function analyzeTestTrend(ruleId, appName, limit = 20) {
71
+ const history = getTestHistory(ruleId, appName, limit);
72
+ if (history.length === 0) {
73
+ return null;
74
+ }
75
+ // Calculate metrics
76
+ const totalRuns = history.length;
77
+ const passed = history.filter((t) => t.status === 'passed').length;
78
+ const failed = history.filter((t) => t.status === 'failed').length;
79
+ const avgDuration = history.reduce((sum, t) => sum + t.duration, 0) / totalRuns;
80
+ // Determine trend direction
81
+ const recent = history.slice(0, Math.ceil(history.length / 2));
82
+ const older = history.slice(Math.ceil(history.length / 2));
83
+ const recentPassRate = recent.filter((t) => t.status === 'passed').length / recent.length;
84
+ const olderPassRate = older.length > 0 ? older.filter((t) => t.status === 'passed').length / older.length : recentPassRate;
85
+ let trend;
86
+ if (recentPassRate > olderPassRate + 0.1) {
87
+ trend = 'improving';
88
+ }
89
+ else if (recentPassRate < olderPassRate - 0.1) {
90
+ trend = 'degrading';
91
+ }
92
+ else {
93
+ trend = 'stable';
94
+ }
95
+ return {
96
+ ruleId,
97
+ appName,
98
+ totalRuns,
99
+ passed,
100
+ failed,
101
+ passRate: (passed / totalRuns) * 100,
102
+ avgDuration,
103
+ trend,
104
+ history: history.map((t) => ({
105
+ status: t.status,
106
+ duration: t.duration,
107
+ startedAt: t.startedAt,
108
+ })),
109
+ };
110
+ }
111
+ //# sourceMappingURL=results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.js","sourceRoot":"","sources":["../../src/storage/results.ts"],"names":[],"mappings":"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"}