@nahisaho/musubix-core 1.0.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 (214) hide show
  1. package/bin/musubix.js +18 -0
  2. package/dist/__tests__/index.test.d.ts +2 -0
  3. package/dist/__tests__/index.test.d.ts.map +1 -0
  4. package/dist/__tests__/index.test.js +27 -0
  5. package/dist/__tests__/index.test.js.map +1 -0
  6. package/dist/auth/auth-manager.d.ts +320 -0
  7. package/dist/auth/auth-manager.d.ts.map +1 -0
  8. package/dist/auth/auth-manager.js +580 -0
  9. package/dist/auth/auth-manager.js.map +1 -0
  10. package/dist/cli/base.d.ts +58 -0
  11. package/dist/cli/base.d.ts.map +1 -0
  12. package/dist/cli/base.js +93 -0
  13. package/dist/cli/base.js.map +1 -0
  14. package/dist/cli/commands/help.d.ts +17 -0
  15. package/dist/cli/commands/help.d.ts.map +1 -0
  16. package/dist/cli/commands/help.js +228 -0
  17. package/dist/cli/commands/help.js.map +1 -0
  18. package/dist/cli/commands/index.d.ts +14 -0
  19. package/dist/cli/commands/index.d.ts.map +1 -0
  20. package/dist/cli/commands/index.js +25 -0
  21. package/dist/cli/commands/index.js.map +1 -0
  22. package/dist/cli/commands/init.d.ts +38 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -0
  24. package/dist/cli/commands/init.js +258 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/cli/index.d.ts +9 -0
  27. package/dist/cli/index.d.ts.map +1 -0
  28. package/dist/cli/index.js +9 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/codegen/coding-standards.d.ts +250 -0
  31. package/dist/codegen/coding-standards.d.ts.map +1 -0
  32. package/dist/codegen/coding-standards.js +976 -0
  33. package/dist/codegen/coding-standards.js.map +1 -0
  34. package/dist/codegen/coverage-reporter.d.ts +264 -0
  35. package/dist/codegen/coverage-reporter.d.ts.map +1 -0
  36. package/dist/codegen/coverage-reporter.js +697 -0
  37. package/dist/codegen/coverage-reporter.js.map +1 -0
  38. package/dist/codegen/dependency-analyzer.d.ts +271 -0
  39. package/dist/codegen/dependency-analyzer.d.ts.map +1 -0
  40. package/dist/codegen/dependency-analyzer.js +661 -0
  41. package/dist/codegen/dependency-analyzer.js.map +1 -0
  42. package/dist/codegen/generator.d.ts +275 -0
  43. package/dist/codegen/generator.d.ts.map +1 -0
  44. package/dist/codegen/generator.js +781 -0
  45. package/dist/codegen/generator.js.map +1 -0
  46. package/dist/codegen/index.d.ts +18 -0
  47. package/dist/codegen/index.d.ts.map +1 -0
  48. package/dist/codegen/index.js +27 -0
  49. package/dist/codegen/index.js.map +1 -0
  50. package/dist/codegen/integration-test-generator.d.ts +312 -0
  51. package/dist/codegen/integration-test-generator.d.ts.map +1 -0
  52. package/dist/codegen/integration-test-generator.js +765 -0
  53. package/dist/codegen/integration-test-generator.js.map +1 -0
  54. package/dist/codegen/pattern-conformance.d.ts +309 -0
  55. package/dist/codegen/pattern-conformance.d.ts.map +1 -0
  56. package/dist/codegen/pattern-conformance.js +590 -0
  57. package/dist/codegen/pattern-conformance.js.map +1 -0
  58. package/dist/codegen/quality-metrics.d.ts +235 -0
  59. package/dist/codegen/quality-metrics.d.ts.map +1 -0
  60. package/dist/codegen/quality-metrics.js +439 -0
  61. package/dist/codegen/quality-metrics.js.map +1 -0
  62. package/dist/codegen/security-scanner.d.ts +179 -0
  63. package/dist/codegen/security-scanner.d.ts.map +1 -0
  64. package/dist/codegen/security-scanner.js +495 -0
  65. package/dist/codegen/security-scanner.js.map +1 -0
  66. package/dist/codegen/static-analyzer.d.ts +188 -0
  67. package/dist/codegen/static-analyzer.d.ts.map +1 -0
  68. package/dist/codegen/static-analyzer.js +490 -0
  69. package/dist/codegen/static-analyzer.js.map +1 -0
  70. package/dist/codegen/unit-test-generator.d.ts +289 -0
  71. package/dist/codegen/unit-test-generator.d.ts.map +1 -0
  72. package/dist/codegen/unit-test-generator.js +634 -0
  73. package/dist/codegen/unit-test-generator.js.map +1 -0
  74. package/dist/design/adr-generator.d.ts +227 -0
  75. package/dist/design/adr-generator.d.ts.map +1 -0
  76. package/dist/design/adr-generator.js +423 -0
  77. package/dist/design/adr-generator.js.map +1 -0
  78. package/dist/design/c4-generator.d.ts +267 -0
  79. package/dist/design/c4-generator.d.ts.map +1 -0
  80. package/dist/design/c4-generator.js +453 -0
  81. package/dist/design/c4-generator.js.map +1 -0
  82. package/dist/design/framework-optimizer.d.ts +190 -0
  83. package/dist/design/framework-optimizer.d.ts.map +1 -0
  84. package/dist/design/framework-optimizer.js +589 -0
  85. package/dist/design/framework-optimizer.js.map +1 -0
  86. package/dist/design/index.d.ts +12 -0
  87. package/dist/design/index.d.ts.map +1 -0
  88. package/dist/design/index.js +13 -0
  89. package/dist/design/index.js.map +1 -0
  90. package/dist/design/pattern-detector.d.ts +270 -0
  91. package/dist/design/pattern-detector.d.ts.map +1 -0
  92. package/dist/design/pattern-detector.js +621 -0
  93. package/dist/design/pattern-detector.js.map +1 -0
  94. package/dist/design/solid-validator.d.ts +188 -0
  95. package/dist/design/solid-validator.d.ts.map +1 -0
  96. package/dist/design/solid-validator.js +579 -0
  97. package/dist/design/solid-validator.js.map +1 -0
  98. package/dist/error/data-persistence.d.ts +311 -0
  99. package/dist/error/data-persistence.d.ts.map +1 -0
  100. package/dist/error/data-persistence.js +586 -0
  101. package/dist/error/data-persistence.js.map +1 -0
  102. package/dist/error/graceful-degradation.d.ts +309 -0
  103. package/dist/error/graceful-degradation.d.ts.map +1 -0
  104. package/dist/error/graceful-degradation.js +510 -0
  105. package/dist/error/graceful-degradation.js.map +1 -0
  106. package/dist/error/index.d.ts +11 -0
  107. package/dist/error/index.d.ts.map +1 -0
  108. package/dist/error/index.js +19 -0
  109. package/dist/error/index.js.map +1 -0
  110. package/dist/explanation/explanation-generator.d.ts +228 -0
  111. package/dist/explanation/explanation-generator.d.ts.map +1 -0
  112. package/dist/explanation/explanation-generator.js +662 -0
  113. package/dist/explanation/explanation-generator.js.map +1 -0
  114. package/dist/explanation/index.d.ts +11 -0
  115. package/dist/explanation/index.d.ts.map +1 -0
  116. package/dist/explanation/index.js +19 -0
  117. package/dist/explanation/index.js.map +1 -0
  118. package/dist/explanation/reasoning-chain.d.ts +314 -0
  119. package/dist/explanation/reasoning-chain.d.ts.map +1 -0
  120. package/dist/explanation/reasoning-chain.js +414 -0
  121. package/dist/explanation/reasoning-chain.js.map +1 -0
  122. package/dist/explanation/visual-explanation.d.ts +315 -0
  123. package/dist/explanation/visual-explanation.d.ts.map +1 -0
  124. package/dist/explanation/visual-explanation.js +667 -0
  125. package/dist/explanation/visual-explanation.js.map +1 -0
  126. package/dist/index.d.ts +33 -0
  127. package/dist/index.d.ts.map +1 -0
  128. package/dist/index.js +47 -0
  129. package/dist/index.js.map +1 -0
  130. package/dist/requirements/decomposer.d.ts +235 -0
  131. package/dist/requirements/decomposer.d.ts.map +1 -0
  132. package/dist/requirements/decomposer.js +587 -0
  133. package/dist/requirements/decomposer.js.map +1 -0
  134. package/dist/requirements/related-finder.d.ts +261 -0
  135. package/dist/requirements/related-finder.d.ts.map +1 -0
  136. package/dist/requirements/related-finder.js +629 -0
  137. package/dist/requirements/related-finder.js.map +1 -0
  138. package/dist/traceability/impact.d.ts +196 -0
  139. package/dist/traceability/impact.d.ts.map +1 -0
  140. package/dist/traceability/impact.js +438 -0
  141. package/dist/traceability/impact.js.map +1 -0
  142. package/dist/traceability/index.d.ts +9 -0
  143. package/dist/traceability/index.d.ts.map +1 -0
  144. package/dist/traceability/index.js +10 -0
  145. package/dist/traceability/index.js.map +1 -0
  146. package/dist/traceability/manager.d.ts +266 -0
  147. package/dist/traceability/manager.d.ts.map +1 -0
  148. package/dist/traceability/manager.js +412 -0
  149. package/dist/traceability/manager.js.map +1 -0
  150. package/dist/types/common.d.ts +294 -0
  151. package/dist/types/common.d.ts.map +1 -0
  152. package/dist/types/common.js +15 -0
  153. package/dist/types/common.js.map +1 -0
  154. package/dist/types/ears.d.ts +158 -0
  155. package/dist/types/ears.d.ts.map +1 -0
  156. package/dist/types/ears.js +33 -0
  157. package/dist/types/ears.js.map +1 -0
  158. package/dist/types/errors.d.ts +176 -0
  159. package/dist/types/errors.d.ts.map +1 -0
  160. package/dist/types/errors.js +55 -0
  161. package/dist/types/errors.js.map +1 -0
  162. package/dist/types/index.d.ts +10 -0
  163. package/dist/types/index.d.ts.map +1 -0
  164. package/dist/types/index.js +10 -0
  165. package/dist/types/index.js.map +1 -0
  166. package/dist/utils/data-protector.d.ts +122 -0
  167. package/dist/utils/data-protector.d.ts.map +1 -0
  168. package/dist/utils/data-protector.js +275 -0
  169. package/dist/utils/data-protector.js.map +1 -0
  170. package/dist/utils/error-handler.d.ts +101 -0
  171. package/dist/utils/error-handler.d.ts.map +1 -0
  172. package/dist/utils/error-handler.js +324 -0
  173. package/dist/utils/error-handler.js.map +1 -0
  174. package/dist/utils/i18n-manager.d.ts +259 -0
  175. package/dist/utils/i18n-manager.d.ts.map +1 -0
  176. package/dist/utils/i18n-manager.js +554 -0
  177. package/dist/utils/i18n-manager.js.map +1 -0
  178. package/dist/utils/index.d.ts +10 -0
  179. package/dist/utils/index.d.ts.map +1 -0
  180. package/dist/utils/index.js +10 -0
  181. package/dist/utils/index.js.map +1 -0
  182. package/dist/utils/logger.d.ts +120 -0
  183. package/dist/utils/logger.d.ts.map +1 -0
  184. package/dist/utils/logger.js +237 -0
  185. package/dist/utils/logger.js.map +1 -0
  186. package/dist/utils/performance-profiler.d.ts +251 -0
  187. package/dist/utils/performance-profiler.d.ts.map +1 -0
  188. package/dist/utils/performance-profiler.js +458 -0
  189. package/dist/utils/performance-profiler.js.map +1 -0
  190. package/dist/utils/scalability-optimizer.d.ts +294 -0
  191. package/dist/utils/scalability-optimizer.d.ts.map +1 -0
  192. package/dist/utils/scalability-optimizer.js +606 -0
  193. package/dist/utils/scalability-optimizer.js.map +1 -0
  194. package/dist/utils/structured-logger.d.ts +294 -0
  195. package/dist/utils/structured-logger.d.ts.map +1 -0
  196. package/dist/utils/structured-logger.js +630 -0
  197. package/dist/utils/structured-logger.js.map +1 -0
  198. package/dist/utils/version-compatibility.d.ts +217 -0
  199. package/dist/utils/version-compatibility.d.ts.map +1 -0
  200. package/dist/utils/version-compatibility.js +443 -0
  201. package/dist/utils/version-compatibility.js.map +1 -0
  202. package/dist/validators/ears-validator.d.ts +182 -0
  203. package/dist/validators/ears-validator.d.ts.map +1 -0
  204. package/dist/validators/ears-validator.js +357 -0
  205. package/dist/validators/ears-validator.js.map +1 -0
  206. package/dist/validators/index.d.ts +8 -0
  207. package/dist/validators/index.d.ts.map +1 -0
  208. package/dist/validators/index.js +9 -0
  209. package/dist/validators/index.js.map +1 -0
  210. package/dist/version.d.ts +8 -0
  211. package/dist/version.d.ts.map +1 -0
  212. package/dist/version.js +8 -0
  213. package/dist/version.js.map +1 -0
  214. package/package.json +100 -0
@@ -0,0 +1,697 @@
1
+ /**
2
+ * Coverage Reporter
3
+ *
4
+ * Generates and reports test coverage metrics
5
+ *
6
+ * @packageDocumentation
7
+ * @module codegen/coverage-reporter
8
+ *
9
+ * @see REQ-QUA-002 - Coverage Reporting
10
+ * @see Article VII - Quality Assurance Standards
11
+ */
12
+ /**
13
+ * Default configuration
14
+ */
15
+ export const DEFAULT_REPORTER_CONFIG = {
16
+ projectName: 'Project',
17
+ thresholds: [
18
+ { metric: 'statements', pass: 80, warn: 60 },
19
+ { metric: 'branches', pass: 80, warn: 60 },
20
+ { metric: 'functions', pass: 80, warn: 60 },
21
+ { metric: 'lines', pass: 80, warn: 60 },
22
+ ],
23
+ formats: ['text', 'json'],
24
+ outputDir: 'coverage',
25
+ includeFileDetails: true,
26
+ showUncoveredLines: true,
27
+ trackTrends: false,
28
+ };
29
+ /**
30
+ * Coverage Reporter
31
+ */
32
+ export class CoverageReporter {
33
+ config;
34
+ constructor(config) {
35
+ this.config = { ...DEFAULT_REPORTER_CONFIG, ...config };
36
+ }
37
+ /**
38
+ * Generate coverage report from raw data
39
+ */
40
+ generateReport(coverageData) {
41
+ const summary = this.calculateSummary(coverageData);
42
+ const thresholdResults = this.checkThresholds(summary);
43
+ const recommendations = this.generateRecommendations(coverageData, summary, thresholdResults);
44
+ const trends = this.config.trackTrends
45
+ ? this.calculateTrends(summary)
46
+ : undefined;
47
+ return {
48
+ timestamp: new Date(),
49
+ projectName: this.config.projectName,
50
+ summary,
51
+ files: coverageData,
52
+ thresholdResults,
53
+ trends,
54
+ recommendations,
55
+ };
56
+ }
57
+ /**
58
+ * Format report
59
+ */
60
+ formatReport(report, format) {
61
+ switch (format) {
62
+ case 'text':
63
+ return this.formatText(report);
64
+ case 'json':
65
+ return this.formatJson(report);
66
+ case 'html':
67
+ return this.formatHtml(report);
68
+ case 'lcov':
69
+ return this.formatLcov(report);
70
+ case 'cobertura':
71
+ return this.formatCobertura(report);
72
+ case 'markdown':
73
+ return this.formatMarkdown(report);
74
+ default:
75
+ return this.formatText(report);
76
+ }
77
+ }
78
+ /**
79
+ * Get all formatted reports
80
+ */
81
+ getAllFormattedReports(report) {
82
+ const reports = new Map();
83
+ for (const format of this.config.formats) {
84
+ reports.set(format, this.formatReport(report, format));
85
+ }
86
+ return reports;
87
+ }
88
+ /**
89
+ * Parse coverage from various formats
90
+ */
91
+ parseCoverage(data, format) {
92
+ switch (format) {
93
+ case 'istanbul':
94
+ return this.parseIstanbul(data);
95
+ case 'lcov':
96
+ return this.parseLcov(data);
97
+ case 'cobertura':
98
+ return this.parseCobertura(data);
99
+ default:
100
+ throw new Error(`Unknown format: ${format}`);
101
+ }
102
+ }
103
+ /**
104
+ * Calculate summary
105
+ */
106
+ calculateSummary(files) {
107
+ const totals = {
108
+ statements: { total: 0, covered: 0, skipped: 0 },
109
+ branches: { total: 0, covered: 0, skipped: 0 },
110
+ functions: { total: 0, covered: 0, skipped: 0 },
111
+ lines: { total: 0, covered: 0, skipped: 0 },
112
+ };
113
+ let coveredFiles = 0;
114
+ for (const file of files) {
115
+ // Aggregate metrics
116
+ totals.statements.total += file.statements.total;
117
+ totals.statements.covered += file.statements.covered;
118
+ totals.statements.skipped += file.statements.skipped;
119
+ totals.branches.total += file.branches.total;
120
+ totals.branches.covered += file.branches.covered;
121
+ totals.branches.skipped += file.branches.skipped;
122
+ totals.functions.total += file.functions.total;
123
+ totals.functions.covered += file.functions.covered;
124
+ totals.functions.skipped += file.functions.skipped;
125
+ totals.lines.total += file.lines.total;
126
+ totals.lines.covered += file.lines.covered;
127
+ totals.lines.skipped += file.lines.skipped;
128
+ // Check if file is covered
129
+ if (file.statements.percentage > 0) {
130
+ coveredFiles++;
131
+ }
132
+ }
133
+ const statements = this.createMetric(totals.statements);
134
+ const branches = this.createMetric(totals.branches);
135
+ const functions = this.createMetric(totals.functions);
136
+ const lines = this.createMetric(totals.lines);
137
+ const overallPercentage = (statements.percentage +
138
+ branches.percentage +
139
+ functions.percentage +
140
+ lines.percentage) / 4;
141
+ return {
142
+ totalFiles: files.length,
143
+ coveredFiles,
144
+ statements,
145
+ branches,
146
+ functions,
147
+ lines,
148
+ overallPercentage: Math.round(overallPercentage * 100) / 100,
149
+ };
150
+ }
151
+ /**
152
+ * Create metric from totals
153
+ */
154
+ createMetric(totals) {
155
+ const percentage = totals.total > 0
156
+ ? (totals.covered / totals.total) * 100
157
+ : 0;
158
+ return {
159
+ total: totals.total,
160
+ covered: totals.covered,
161
+ skipped: totals.skipped,
162
+ percentage: Math.round(percentage * 100) / 100,
163
+ };
164
+ }
165
+ /**
166
+ * Check thresholds
167
+ */
168
+ checkThresholds(summary) {
169
+ const results = [];
170
+ for (const threshold of this.config.thresholds) {
171
+ const current = summary[threshold.metric].percentage;
172
+ let status;
173
+ let gap;
174
+ if (current >= threshold.pass) {
175
+ status = 'pass';
176
+ }
177
+ else if (current >= threshold.warn) {
178
+ status = 'warn';
179
+ gap = threshold.pass - current;
180
+ }
181
+ else {
182
+ status = 'fail';
183
+ gap = threshold.pass - current;
184
+ }
185
+ results.push({
186
+ metric: threshold.metric,
187
+ current,
188
+ threshold,
189
+ status,
190
+ gap,
191
+ });
192
+ }
193
+ return results;
194
+ }
195
+ /**
196
+ * Calculate trends
197
+ */
198
+ calculateTrends(current) {
199
+ const trends = [];
200
+ const previous = this.config.previousCoverage;
201
+ const metrics = ['statements', 'branches', 'functions', 'lines'];
202
+ for (const metric of metrics) {
203
+ const currentValue = current[metric].percentage;
204
+ const previousValue = previous ? previous[metric].percentage : currentValue;
205
+ const change = currentValue - previousValue;
206
+ trends.push({
207
+ date: new Date(),
208
+ metric,
209
+ percentage: currentValue,
210
+ change: Math.round(change * 100) / 100,
211
+ });
212
+ }
213
+ return trends;
214
+ }
215
+ /**
216
+ * Generate recommendations
217
+ */
218
+ generateRecommendations(files, summary, thresholds) {
219
+ const recommendations = [];
220
+ // Threshold-based recommendations
221
+ const failingMetrics = thresholds.filter((t) => t.status === 'fail');
222
+ if (failingMetrics.length > 0) {
223
+ for (const metric of failingMetrics) {
224
+ recommendations.push(`Increase ${metric.metric} coverage by ${metric.gap?.toFixed(1)}% to meet threshold`);
225
+ }
226
+ }
227
+ // File-based recommendations
228
+ const lowCoverageFiles = files
229
+ .filter((f) => f.statements.percentage < 50)
230
+ .sort((a, b) => a.statements.percentage - b.statements.percentage)
231
+ .slice(0, 5);
232
+ if (lowCoverageFiles.length > 0) {
233
+ recommendations.push(`Focus on improving coverage in: ${lowCoverageFiles.map((f) => f.path).join(', ')}`);
234
+ }
235
+ // Branch-based recommendations
236
+ if (summary.branches.percentage < summary.statements.percentage - 20) {
237
+ recommendations.push('Branch coverage is significantly lower than statement coverage - add more conditional tests');
238
+ }
239
+ // Function-based recommendations
240
+ const filesWithUncoveredFunctions = files.filter((f) => f.uncoveredFunctions.length > 0);
241
+ if (filesWithUncoveredFunctions.length > 0) {
242
+ recommendations.push(`${filesWithUncoveredFunctions.length} files have uncovered functions - add tests for them`);
243
+ }
244
+ // Overall recommendations
245
+ if (summary.overallPercentage >= 80) {
246
+ recommendations.push('Coverage is good! Consider adding edge case tests');
247
+ }
248
+ else if (summary.overallPercentage >= 60) {
249
+ recommendations.push('Coverage is moderate. Prioritize critical paths');
250
+ }
251
+ else {
252
+ recommendations.push('Coverage is low. Start with unit tests for core functionality');
253
+ }
254
+ return recommendations;
255
+ }
256
+ /**
257
+ * Format as text
258
+ */
259
+ formatText(report) {
260
+ const lines = [];
261
+ const width = 80;
262
+ lines.push('='.repeat(width));
263
+ lines.push(`COVERAGE REPORT - ${report.projectName}`);
264
+ lines.push(`Generated: ${report.timestamp.toISOString()}`);
265
+ lines.push('='.repeat(width));
266
+ lines.push('');
267
+ // Summary
268
+ lines.push('SUMMARY');
269
+ lines.push('-'.repeat(width));
270
+ lines.push(`Files: ${report.summary.coveredFiles}/${report.summary.totalFiles}`);
271
+ lines.push(`Statements: ${this.formatMetricBar(report.summary.statements)}`);
272
+ lines.push(`Branches: ${this.formatMetricBar(report.summary.branches)}`);
273
+ lines.push(`Functions: ${this.formatMetricBar(report.summary.functions)}`);
274
+ lines.push(`Lines: ${this.formatMetricBar(report.summary.lines)}`);
275
+ lines.push('');
276
+ lines.push(`Overall: ${report.summary.overallPercentage}%`);
277
+ lines.push('');
278
+ // Threshold results
279
+ lines.push('THRESHOLD STATUS');
280
+ lines.push('-'.repeat(width));
281
+ for (const result of report.thresholdResults) {
282
+ const icon = result.status === 'pass' ? '✓' :
283
+ result.status === 'warn' ? '⚠' : '✗';
284
+ const status = result.status.toUpperCase().padEnd(4);
285
+ lines.push(`${icon} ${result.metric.padEnd(12)} ${result.current.toFixed(1)}% ${status}`);
286
+ }
287
+ lines.push('');
288
+ // Trends
289
+ if (report.trends) {
290
+ lines.push('TRENDS');
291
+ lines.push('-'.repeat(width));
292
+ for (const trend of report.trends) {
293
+ const arrow = trend.change > 0 ? '↑' : trend.change < 0 ? '↓' : '→';
294
+ const change = trend.change !== 0 ? `${trend.change > 0 ? '+' : ''}${trend.change.toFixed(1)}%` : '';
295
+ lines.push(`${trend.metric.padEnd(12)} ${trend.percentage.toFixed(1)}% ${arrow} ${change}`);
296
+ }
297
+ lines.push('');
298
+ }
299
+ // File details
300
+ if (this.config.includeFileDetails) {
301
+ lines.push('FILE COVERAGE');
302
+ lines.push('-'.repeat(width));
303
+ for (const file of report.files.slice(0, 20)) {
304
+ const pct = file.statements.percentage.toFixed(1).padStart(5);
305
+ lines.push(`${pct}% ${file.path}`);
306
+ if (this.config.showUncoveredLines && file.uncoveredLines.length > 0) {
307
+ const uncovered = file.uncoveredLines.slice(0, 10).join(', ');
308
+ const more = file.uncoveredLines.length > 10
309
+ ? ` (+${file.uncoveredLines.length - 10} more)`
310
+ : '';
311
+ lines.push(` Uncovered lines: ${uncovered}${more}`);
312
+ }
313
+ }
314
+ if (report.files.length > 20) {
315
+ lines.push(`... and ${report.files.length - 20} more files`);
316
+ }
317
+ lines.push('');
318
+ }
319
+ // Recommendations
320
+ lines.push('RECOMMENDATIONS');
321
+ lines.push('-'.repeat(width));
322
+ for (const rec of report.recommendations) {
323
+ lines.push(`• ${rec}`);
324
+ }
325
+ lines.push('');
326
+ lines.push('='.repeat(width));
327
+ return lines.join('\n');
328
+ }
329
+ /**
330
+ * Format metric as bar
331
+ */
332
+ formatMetricBar(metric) {
333
+ const barWidth = 30;
334
+ const filled = Math.round((metric.percentage / 100) * barWidth);
335
+ const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
336
+ return `${bar} ${metric.covered}/${metric.total} (${metric.percentage}%)`;
337
+ }
338
+ /**
339
+ * Format as JSON
340
+ */
341
+ formatJson(report) {
342
+ return JSON.stringify(report, null, 2);
343
+ }
344
+ /**
345
+ * Format as HTML
346
+ */
347
+ formatHtml(report) {
348
+ const statusColor = (status) => {
349
+ switch (status) {
350
+ case 'pass': return '#4caf50';
351
+ case 'warn': return '#ff9800';
352
+ case 'fail': return '#f44336';
353
+ }
354
+ };
355
+ return `<!DOCTYPE html>
356
+ <html>
357
+ <head>
358
+ <title>Coverage Report - ${report.projectName}</title>
359
+ <style>
360
+ body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; margin: 20px; }
361
+ h1 { color: #333; }
362
+ .summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin: 20px 0; }
363
+ .metric { background: #f5f5f5; padding: 20px; border-radius: 8px; }
364
+ .metric h3 { margin: 0 0 10px 0; }
365
+ .metric .value { font-size: 2em; font-weight: bold; }
366
+ .progress { background: #e0e0e0; height: 8px; border-radius: 4px; margin-top: 10px; }
367
+ .progress-bar { height: 100%; border-radius: 4px; }
368
+ .threshold { display: flex; align-items: center; padding: 10px; border-radius: 4px; margin: 5px 0; }
369
+ .threshold.pass { background: #e8f5e9; }
370
+ .threshold.warn { background: #fff3e0; }
371
+ .threshold.fail { background: #ffebee; }
372
+ table { width: 100%; border-collapse: collapse; margin: 20px 0; }
373
+ th, td { text-align: left; padding: 10px; border-bottom: 1px solid #ddd; }
374
+ th { background: #f5f5f5; }
375
+ .recommendation { padding: 10px; background: #e3f2fd; margin: 5px 0; border-radius: 4px; }
376
+ </style>
377
+ </head>
378
+ <body>
379
+ <h1>Coverage Report - ${report.projectName}</h1>
380
+ <p>Generated: ${report.timestamp.toISOString()}</p>
381
+
382
+ <div class="summary">
383
+ <div class="metric">
384
+ <h3>Statements</h3>
385
+ <div class="value">${report.summary.statements.percentage}%</div>
386
+ <div class="progress"><div class="progress-bar" style="width: ${report.summary.statements.percentage}%; background: #2196f3;"></div></div>
387
+ </div>
388
+ <div class="metric">
389
+ <h3>Branches</h3>
390
+ <div class="value">${report.summary.branches.percentage}%</div>
391
+ <div class="progress"><div class="progress-bar" style="width: ${report.summary.branches.percentage}%; background: #9c27b0;"></div></div>
392
+ </div>
393
+ <div class="metric">
394
+ <h3>Functions</h3>
395
+ <div class="value">${report.summary.functions.percentage}%</div>
396
+ <div class="progress"><div class="progress-bar" style="width: ${report.summary.functions.percentage}%; background: #ff9800;"></div></div>
397
+ </div>
398
+ <div class="metric">
399
+ <h3>Lines</h3>
400
+ <div class="value">${report.summary.lines.percentage}%</div>
401
+ <div class="progress"><div class="progress-bar" style="width: ${report.summary.lines.percentage}%; background: #4caf50;"></div></div>
402
+ </div>
403
+ </div>
404
+
405
+ <h2>Threshold Status</h2>
406
+ ${report.thresholdResults.map((r) => `
407
+ <div class="threshold ${r.status}">
408
+ <strong>${r.metric}</strong>: ${r.current}%
409
+ (threshold: ${r.threshold.pass}%) -
410
+ <span style="color: ${statusColor(r.status)}">${r.status.toUpperCase()}</span>
411
+ ${r.gap ? ` (${r.gap.toFixed(1)}% to pass)` : ''}
412
+ </div>
413
+ `).join('')}
414
+
415
+ <h2>File Coverage</h2>
416
+ <table>
417
+ <thead>
418
+ <tr>
419
+ <th>File</th>
420
+ <th>Statements</th>
421
+ <th>Branches</th>
422
+ <th>Functions</th>
423
+ <th>Lines</th>
424
+ </tr>
425
+ </thead>
426
+ <tbody>
427
+ ${report.files.map((f) => `
428
+ <tr>
429
+ <td>${f.path}</td>
430
+ <td>${f.statements.percentage}%</td>
431
+ <td>${f.branches.percentage}%</td>
432
+ <td>${f.functions.percentage}%</td>
433
+ <td>${f.lines.percentage}%</td>
434
+ </tr>
435
+ `).join('')}
436
+ </tbody>
437
+ </table>
438
+
439
+ <h2>Recommendations</h2>
440
+ ${report.recommendations.map((r) => `
441
+ <div class="recommendation">${r}</div>
442
+ `).join('')}
443
+ </body>
444
+ </html>`;
445
+ }
446
+ /**
447
+ * Format as LCOV
448
+ */
449
+ formatLcov(report) {
450
+ const lines = [];
451
+ for (const file of report.files) {
452
+ lines.push(`TN:`);
453
+ lines.push(`SF:${file.path}`);
454
+ // Function coverage
455
+ lines.push(`FNF:${file.functions.total}`);
456
+ lines.push(`FNH:${file.functions.covered}`);
457
+ // Line coverage
458
+ lines.push(`LF:${file.lines.total}`);
459
+ lines.push(`LH:${file.lines.covered}`);
460
+ // Branch coverage
461
+ lines.push(`BRF:${file.branches.total}`);
462
+ lines.push(`BRH:${file.branches.covered}`);
463
+ lines.push('end_of_record');
464
+ }
465
+ return lines.join('\n');
466
+ }
467
+ /**
468
+ * Format as Cobertura XML
469
+ */
470
+ formatCobertura(report) {
471
+ const lineRate = report.summary.lines.percentage / 100;
472
+ const branchRate = report.summary.branches.percentage / 100;
473
+ return `<?xml version="1.0" ?>
474
+ <!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
475
+ <coverage version="1.0" timestamp="${report.timestamp.getTime()}" lines-valid="${report.summary.lines.total}" lines-covered="${report.summary.lines.covered}" line-rate="${lineRate.toFixed(4)}" branches-valid="${report.summary.branches.total}" branches-covered="${report.summary.branches.covered}" branch-rate="${branchRate.toFixed(4)}">
476
+ <packages>
477
+ <package name="${report.projectName}" line-rate="${lineRate.toFixed(4)}" branch-rate="${branchRate.toFixed(4)}">
478
+ <classes>
479
+ ${report.files.map((f) => ` <class name="${f.path}" filename="${f.path}" line-rate="${(f.lines.percentage / 100).toFixed(4)}" branch-rate="${(f.branches.percentage / 100).toFixed(4)}">
480
+ <lines>
481
+ ${Array.from({ length: f.lines.total }, (_, i) => ` <line number="${i + 1}" hits="${f.uncoveredLines.includes(i + 1) ? 0 : 1}"/>`).join('\n')}
482
+ </lines>
483
+ </class>`).join('\n')}
484
+ </classes>
485
+ </package>
486
+ </packages>
487
+ </coverage>`;
488
+ }
489
+ /**
490
+ * Format as Markdown
491
+ */
492
+ formatMarkdown(report) {
493
+ const lines = [];
494
+ lines.push(`# Coverage Report - ${report.projectName}`);
495
+ lines.push('');
496
+ lines.push(`> Generated: ${report.timestamp.toISOString()}`);
497
+ lines.push('');
498
+ // Summary
499
+ lines.push('## Summary');
500
+ lines.push('');
501
+ lines.push('| Metric | Covered | Total | Percentage |');
502
+ lines.push('|--------|---------|-------|------------|');
503
+ lines.push(`| Statements | ${report.summary.statements.covered} | ${report.summary.statements.total} | ${report.summary.statements.percentage}% |`);
504
+ lines.push(`| Branches | ${report.summary.branches.covered} | ${report.summary.branches.total} | ${report.summary.branches.percentage}% |`);
505
+ lines.push(`| Functions | ${report.summary.functions.covered} | ${report.summary.functions.total} | ${report.summary.functions.percentage}% |`);
506
+ lines.push(`| Lines | ${report.summary.lines.covered} | ${report.summary.lines.total} | ${report.summary.lines.percentage}% |`);
507
+ lines.push('');
508
+ lines.push(`**Overall Coverage: ${report.summary.overallPercentage}%**`);
509
+ lines.push('');
510
+ // Thresholds
511
+ lines.push('## Threshold Status');
512
+ lines.push('');
513
+ for (const result of report.thresholdResults) {
514
+ const icon = result.status === 'pass' ? '✅' :
515
+ result.status === 'warn' ? '⚠️' : '❌';
516
+ lines.push(`- ${icon} **${result.metric}**: ${result.current}% (threshold: ${result.threshold.pass}%)`);
517
+ }
518
+ lines.push('');
519
+ // Trends
520
+ if (report.trends) {
521
+ lines.push('## Trends');
522
+ lines.push('');
523
+ lines.push('| Metric | Current | Change |');
524
+ lines.push('|--------|---------|--------|');
525
+ for (const trend of report.trends) {
526
+ const arrow = trend.change > 0 ? '📈' : trend.change < 0 ? '📉' : '➡️';
527
+ const change = trend.change !== 0
528
+ ? `${trend.change > 0 ? '+' : ''}${trend.change.toFixed(1)}%`
529
+ : 'No change';
530
+ lines.push(`| ${trend.metric} | ${trend.percentage}% | ${arrow} ${change} |`);
531
+ }
532
+ lines.push('');
533
+ }
534
+ // File Coverage
535
+ if (this.config.includeFileDetails) {
536
+ lines.push('## File Coverage');
537
+ lines.push('');
538
+ lines.push('| File | Statements | Branches | Functions | Lines |');
539
+ lines.push('|------|------------|----------|-----------|-------|');
540
+ for (const file of report.files) {
541
+ lines.push(`| ${file.path} | ${file.statements.percentage}% | ${file.branches.percentage}% | ${file.functions.percentage}% | ${file.lines.percentage}% |`);
542
+ }
543
+ lines.push('');
544
+ }
545
+ // Recommendations
546
+ lines.push('## Recommendations');
547
+ lines.push('');
548
+ for (const rec of report.recommendations) {
549
+ lines.push(`- ${rec}`);
550
+ }
551
+ return lines.join('\n');
552
+ }
553
+ /**
554
+ * Parse Istanbul JSON coverage
555
+ */
556
+ parseIstanbul(data) {
557
+ const json = JSON.parse(data);
558
+ const files = [];
559
+ for (const [path, coverage] of Object.entries(json)) {
560
+ const cov = coverage;
561
+ files.push(this.parseIstanbulFile(path, cov));
562
+ }
563
+ return files;
564
+ }
565
+ /**
566
+ * Parse Istanbul file coverage
567
+ */
568
+ parseIstanbulFile(path, coverage) {
569
+ const s = coverage.s;
570
+ const b = coverage.b;
571
+ const f = coverage.f;
572
+ const stmtTotal = Object.keys(s).length;
573
+ const stmtCovered = Object.values(s).filter((v) => v > 0).length;
574
+ const branchTotal = Object.values(b).flat().length;
575
+ const branchCovered = Object.values(b).flat().filter((v) => v > 0).length;
576
+ const fnTotal = Object.keys(f).length;
577
+ const fnCovered = Object.values(f).filter((v) => v > 0).length;
578
+ // Calculate line coverage from statement map
579
+ const statementMap = coverage.statementMap;
580
+ const lines = new Set();
581
+ const coveredLines = new Set();
582
+ for (const [key, value] of Object.entries(statementMap)) {
583
+ const line = value.start.line;
584
+ lines.add(line);
585
+ if (s[key] > 0) {
586
+ coveredLines.add(line);
587
+ }
588
+ }
589
+ return {
590
+ path,
591
+ statements: {
592
+ total: stmtTotal,
593
+ covered: stmtCovered,
594
+ skipped: 0,
595
+ percentage: stmtTotal > 0 ? Math.round((stmtCovered / stmtTotal) * 10000) / 100 : 0,
596
+ },
597
+ branches: {
598
+ total: branchTotal,
599
+ covered: branchCovered,
600
+ skipped: 0,
601
+ percentage: branchTotal > 0 ? Math.round((branchCovered / branchTotal) * 10000) / 100 : 0,
602
+ },
603
+ functions: {
604
+ total: fnTotal,
605
+ covered: fnCovered,
606
+ skipped: 0,
607
+ percentage: fnTotal > 0 ? Math.round((fnCovered / fnTotal) * 10000) / 100 : 0,
608
+ },
609
+ lines: {
610
+ total: lines.size,
611
+ covered: coveredLines.size,
612
+ skipped: 0,
613
+ percentage: lines.size > 0 ? Math.round((coveredLines.size / lines.size) * 10000) / 100 : 0,
614
+ },
615
+ uncoveredLines: [...lines].filter((l) => !coveredLines.has(l)),
616
+ uncoveredBranches: [],
617
+ uncoveredFunctions: [],
618
+ };
619
+ }
620
+ /**
621
+ * Parse LCOV format
622
+ */
623
+ parseLcov(data) {
624
+ const files = [];
625
+ const records = data.split('end_of_record');
626
+ for (const record of records) {
627
+ if (!record.trim())
628
+ continue;
629
+ const lines = record.trim().split('\n');
630
+ let path = '';
631
+ let linesTotal = 0, linesCovered = 0;
632
+ let fnTotal = 0, fnCovered = 0;
633
+ let branchTotal = 0, branchCovered = 0;
634
+ for (const line of lines) {
635
+ if (line.startsWith('SF:'))
636
+ path = line.substring(3);
637
+ else if (line.startsWith('LF:'))
638
+ linesTotal = parseInt(line.substring(3));
639
+ else if (line.startsWith('LH:'))
640
+ linesCovered = parseInt(line.substring(3));
641
+ else if (line.startsWith('FNF:'))
642
+ fnTotal = parseInt(line.substring(4));
643
+ else if (line.startsWith('FNH:'))
644
+ fnCovered = parseInt(line.substring(4));
645
+ else if (line.startsWith('BRF:'))
646
+ branchTotal = parseInt(line.substring(4));
647
+ else if (line.startsWith('BRH:'))
648
+ branchCovered = parseInt(line.substring(4));
649
+ }
650
+ if (path) {
651
+ files.push({
652
+ path,
653
+ statements: { total: linesTotal, covered: linesCovered, skipped: 0, percentage: linesTotal > 0 ? Math.round((linesCovered / linesTotal) * 10000) / 100 : 0 },
654
+ branches: { total: branchTotal, covered: branchCovered, skipped: 0, percentage: branchTotal > 0 ? Math.round((branchCovered / branchTotal) * 10000) / 100 : 0 },
655
+ functions: { total: fnTotal, covered: fnCovered, skipped: 0, percentage: fnTotal > 0 ? Math.round((fnCovered / fnTotal) * 10000) / 100 : 0 },
656
+ lines: { total: linesTotal, covered: linesCovered, skipped: 0, percentage: linesTotal > 0 ? Math.round((linesCovered / linesTotal) * 10000) / 100 : 0 },
657
+ uncoveredLines: [],
658
+ uncoveredBranches: [],
659
+ uncoveredFunctions: [],
660
+ });
661
+ }
662
+ }
663
+ return files;
664
+ }
665
+ /**
666
+ * Parse Cobertura XML
667
+ */
668
+ parseCobertura(data) {
669
+ // Simple XML parsing for Cobertura format
670
+ const files = [];
671
+ const classRegex = /<class name="([^"]+)" filename="([^"]+)" line-rate="([^"]+)" branch-rate="([^"]+)"/g;
672
+ let match;
673
+ while ((match = classRegex.exec(data)) !== null) {
674
+ const path = match[2];
675
+ const lineRate = parseFloat(match[3]) * 100;
676
+ const branchRate = parseFloat(match[4]) * 100;
677
+ files.push({
678
+ path,
679
+ statements: { total: 100, covered: Math.round(lineRate), skipped: 0, percentage: lineRate },
680
+ branches: { total: 100, covered: Math.round(branchRate), skipped: 0, percentage: branchRate },
681
+ functions: { total: 100, covered: Math.round(lineRate), skipped: 0, percentage: lineRate },
682
+ lines: { total: 100, covered: Math.round(lineRate), skipped: 0, percentage: lineRate },
683
+ uncoveredLines: [],
684
+ uncoveredBranches: [],
685
+ uncoveredFunctions: [],
686
+ });
687
+ }
688
+ return files;
689
+ }
690
+ }
691
+ /**
692
+ * Create coverage reporter instance
693
+ */
694
+ export function createCoverageReporter(config) {
695
+ return new CoverageReporter(config);
696
+ }
697
+ //# sourceMappingURL=coverage-reporter.js.map