@facet-coverage/core 0.1.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 (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +422 -0
  3. package/bin/facet-coverage.js +3 -0
  4. package/dist/cli/commands/analyze.d.ts +13 -0
  5. package/dist/cli/commands/analyze.d.ts.map +1 -0
  6. package/dist/cli/commands/analyze.js +161 -0
  7. package/dist/cli/commands/analyze.js.map +1 -0
  8. package/dist/cli/commands/generate.d.ts +10 -0
  9. package/dist/cli/commands/generate.d.ts.map +1 -0
  10. package/dist/cli/commands/generate.js +67 -0
  11. package/dist/cli/commands/generate.js.map +1 -0
  12. package/dist/cli/commands/validate.d.ts +11 -0
  13. package/dist/cli/commands/validate.d.ts.map +1 -0
  14. package/dist/cli/commands/validate.js +120 -0
  15. package/dist/cli/commands/validate.js.map +1 -0
  16. package/dist/cli/commands/watch.d.ts +10 -0
  17. package/dist/cli/commands/watch.d.ts.map +1 -0
  18. package/dist/cli/commands/watch.js +151 -0
  19. package/dist/cli/commands/watch.js.map +1 -0
  20. package/dist/cli/index.d.ts +2 -0
  21. package/dist/cli/index.d.ts.map +1 -0
  22. package/dist/cli/index.js +47 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/core/CoverageCalculator.d.ts +34 -0
  25. package/dist/core/CoverageCalculator.d.ts.map +1 -0
  26. package/dist/core/CoverageCalculator.js +167 -0
  27. package/dist/core/CoverageCalculator.js.map +1 -0
  28. package/dist/core/FacetParser.d.ts +39 -0
  29. package/dist/core/FacetParser.d.ts.map +1 -0
  30. package/dist/core/FacetParser.js +114 -0
  31. package/dist/core/FacetParser.js.map +1 -0
  32. package/dist/core/StructureReader.d.ts +37 -0
  33. package/dist/core/StructureReader.d.ts.map +1 -0
  34. package/dist/core/StructureReader.js +126 -0
  35. package/dist/core/StructureReader.js.map +1 -0
  36. package/dist/core/TestScanner.d.ts +36 -0
  37. package/dist/core/TestScanner.d.ts.map +1 -0
  38. package/dist/core/TestScanner.js +192 -0
  39. package/dist/core/TestScanner.js.map +1 -0
  40. package/dist/core/Validator.d.ts +33 -0
  41. package/dist/core/Validator.d.ts.map +1 -0
  42. package/dist/core/Validator.js +183 -0
  43. package/dist/core/Validator.js.map +1 -0
  44. package/dist/core/index.d.ts +6 -0
  45. package/dist/core/index.d.ts.map +1 -0
  46. package/dist/core/index.js +14 -0
  47. package/dist/core/index.js.map +1 -0
  48. package/dist/index.d.ts +6 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +23 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/integrations/playwright.d.ts +95 -0
  53. package/dist/integrations/playwright.d.ts.map +1 -0
  54. package/dist/integrations/playwright.js +168 -0
  55. package/dist/integrations/playwright.js.map +1 -0
  56. package/dist/reporters/HtmlReporter.d.ts +53 -0
  57. package/dist/reporters/HtmlReporter.d.ts.map +1 -0
  58. package/dist/reporters/HtmlReporter.js +484 -0
  59. package/dist/reporters/HtmlReporter.js.map +1 -0
  60. package/dist/reporters/JsonReporter.d.ts +21 -0
  61. package/dist/reporters/JsonReporter.d.ts.map +1 -0
  62. package/dist/reporters/JsonReporter.js +58 -0
  63. package/dist/reporters/JsonReporter.js.map +1 -0
  64. package/dist/reporters/MarkdownReporter.d.ts +25 -0
  65. package/dist/reporters/MarkdownReporter.d.ts.map +1 -0
  66. package/dist/reporters/MarkdownReporter.js +141 -0
  67. package/dist/reporters/MarkdownReporter.js.map +1 -0
  68. package/dist/reporters/index.d.ts +4 -0
  69. package/dist/reporters/index.d.ts.map +1 -0
  70. package/dist/reporters/index.js +10 -0
  71. package/dist/reporters/index.js.map +1 -0
  72. package/dist/types.d.ts +205 -0
  73. package/dist/types.d.ts.map +1 -0
  74. package/dist/types.js +28 -0
  75. package/dist/types.js.map +1 -0
  76. package/package.json +88 -0
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCommand = generateCommand;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const FacetParser_js_1 = require("../../core/FacetParser.js");
7
+ /**
8
+ * Generate structure.json from facet markdown files
9
+ */
10
+ async function generateCommand(dir, options = {}) {
11
+ const facetsDir = (0, path_1.resolve)(process.cwd(), dir);
12
+ if (!(0, fs_1.existsSync)(facetsDir)) {
13
+ console.error(`❌ Directory not found: ${facetsDir}`);
14
+ process.exit(1);
15
+ }
16
+ console.log(`💎 Generating structure from: ${facetsDir}`);
17
+ // Find all markdown files in the directory
18
+ const files = (0, fs_1.readdirSync)(facetsDir).filter(f => f.endsWith('.md'));
19
+ if (files.length === 0) {
20
+ console.error(`❌ No markdown files found in: ${facetsDir}`);
21
+ process.exit(1);
22
+ }
23
+ const parser = new FacetParser_js_1.FacetParser();
24
+ const facets = [];
25
+ for (const file of files) {
26
+ const filePath = (0, path_1.join)(facetsDir, file);
27
+ const parsed = parser.parseFile(filePath);
28
+ // Determine facet type from filename (e.g., business.md -> business)
29
+ const type = options.type || (0, path_1.basename)(file, '.md');
30
+ console.log(` 📄 ${file}`);
31
+ for (const section of parsed.sections) {
32
+ // Only include top-level sections (h2 or the first h1 if no h2s)
33
+ if (section.level <= 2) {
34
+ const facetId = FacetParser_js_1.FacetParser.generateFacetId(file, section.slug);
35
+ facets.push({
36
+ id: facetId,
37
+ source: {
38
+ file: `facets/${file}`,
39
+ section: section.slug,
40
+ },
41
+ type,
42
+ title: section.title,
43
+ });
44
+ console.log(` ✓ ${facetId}`);
45
+ }
46
+ }
47
+ }
48
+ // Determine feature name from parent directory
49
+ const featureDir = (0, path_1.dirname)(facetsDir);
50
+ const featureName = (0, path_1.basename)(featureDir);
51
+ // Build structure object
52
+ const structure = {
53
+ feature: featureName,
54
+ facets,
55
+ };
56
+ // Determine output path
57
+ const outputDir = options.output || (0, path_1.join)(featureDir, '.facet');
58
+ const outputPath = (0, path_1.join)(outputDir, 'structure.json');
59
+ // Ensure output directory exists
60
+ (0, fs_1.mkdirSync)(outputDir, { recursive: true });
61
+ // Write structure file
62
+ (0, fs_1.writeFileSync)(outputPath, JSON.stringify(structure, null, 2));
63
+ console.log(`\n✅ Generated: ${outputPath}`);
64
+ console.log(` Feature: ${featureName}`);
65
+ console.log(` Facets: ${facets.length}`);
66
+ }
67
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ interface ValidateOptions {
2
+ config?: string;
3
+ strict?: boolean;
4
+ json?: boolean;
5
+ }
6
+ /**
7
+ * Validate facet structure and test links
8
+ */
9
+ export declare function validateCommand(options?: ValidateOptions): Promise<void>;
10
+ export {};
11
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BlF"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateCommand = validateCommand;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const Validator_js_1 = require("../../core/Validator.js");
7
+ const types_js_1 = require("../../types.js");
8
+ /**
9
+ * Validate facet structure and test links
10
+ */
11
+ async function validateCommand(options = {}) {
12
+ const cwd = process.cwd();
13
+ // Load config
14
+ const config = await loadConfig(options.config, cwd);
15
+ // Enable strict mode if requested
16
+ if (options.strict) {
17
+ config.validation.requireAllTestsLinked = true;
18
+ }
19
+ console.log('💎 Validating Facet Coverage...\n');
20
+ const validator = new Validator_js_1.Validator(config);
21
+ const result = await validator.validate(cwd);
22
+ if (options.json) {
23
+ console.log(JSON.stringify(result, null, 2));
24
+ }
25
+ else {
26
+ printValidationResult(result);
27
+ }
28
+ // Exit with error code if validation failed
29
+ if (!result.valid) {
30
+ process.exit(1);
31
+ }
32
+ }
33
+ /**
34
+ * Load configuration from file or use defaults
35
+ */
36
+ async function loadConfig(configPath, cwd) {
37
+ const configFiles = [
38
+ configPath,
39
+ 'facet.config.js',
40
+ 'facet.config.mjs',
41
+ 'facet.config.json',
42
+ ].filter(Boolean);
43
+ for (const file of configFiles) {
44
+ const fullPath = (0, path_1.resolve)(cwd, file);
45
+ if ((0, fs_1.existsSync)(fullPath)) {
46
+ if (file.endsWith('.json')) {
47
+ const content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
48
+ return { ...types_js_1.defaultConfig, ...JSON.parse(content) };
49
+ }
50
+ else {
51
+ try {
52
+ const imported = await import(fullPath);
53
+ return { ...types_js_1.defaultConfig, ...(imported.default || imported) };
54
+ }
55
+ catch (error) {
56
+ console.warn(`Warning: Could not load config from ${file}`);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ return types_js_1.defaultConfig;
62
+ }
63
+ /**
64
+ * Print validation results
65
+ */
66
+ function printValidationResult(result) {
67
+ if (result.valid && result.warnings.length === 0) {
68
+ console.log('✅ Validation passed! No issues found.\n');
69
+ return;
70
+ }
71
+ // Print errors
72
+ if (result.errors.length > 0) {
73
+ console.log(`❌ Errors (${result.errors.length}):\n`);
74
+ for (const error of result.errors) {
75
+ const location = error.file ? ` in ${error.file}` : '';
76
+ const line = error.line ? `:${error.line}` : '';
77
+ const facet = error.facetId ? ` [${error.facetId}]` : '';
78
+ console.log(` ${getErrorIcon(error.type)} ${error.message}${location}${line}${facet}`);
79
+ }
80
+ console.log('');
81
+ }
82
+ // Print warnings
83
+ if (result.warnings.length > 0) {
84
+ console.log(`⚠️ Warnings (${result.warnings.length}):\n`);
85
+ for (const warning of result.warnings) {
86
+ const location = warning.file ? ` in ${warning.file}` : '';
87
+ const line = warning.line ? `:${warning.line}` : '';
88
+ const facet = warning.facetId ? ` [${warning.facetId}]` : '';
89
+ console.log(` ⚠️ ${warning.message}${location}${line}${facet}`);
90
+ }
91
+ console.log('');
92
+ }
93
+ // Summary
94
+ if (result.valid) {
95
+ console.log('✅ Validation passed with warnings.');
96
+ }
97
+ else {
98
+ console.log('❌ Validation failed. Please fix the errors above.');
99
+ }
100
+ }
101
+ /**
102
+ * Get icon for error type
103
+ */
104
+ function getErrorIcon(type) {
105
+ switch (type) {
106
+ case 'missing-source':
107
+ return '📄';
108
+ case 'missing-section':
109
+ return '📑';
110
+ case 'invalid-facet-id':
111
+ return '🏷️';
112
+ case 'orphan-test':
113
+ return '🔗';
114
+ case 'duplicate-id':
115
+ return '🔄';
116
+ default:
117
+ return '❌';
118
+ }
119
+ }
120
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ interface WatchOptions {
2
+ config?: string;
3
+ validate?: boolean;
4
+ }
5
+ /**
6
+ * Watch for changes and re-run analysis
7
+ */
8
+ export declare function watchCommand(options?: WatchOptions): Promise<void>;
9
+ export {};
10
+ //# sourceMappingURL=watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/watch.ts"],"names":[],"mappings":"AAWA,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD5E"}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.watchCommand = watchCommand;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const chokidar_1 = require("chokidar");
7
+ const CoverageCalculator_js_1 = require("../../core/CoverageCalculator.js");
8
+ const Validator_js_1 = require("../../core/Validator.js");
9
+ const JsonReporter_js_1 = require("../../reporters/JsonReporter.js");
10
+ const HtmlReporter_js_1 = require("../../reporters/HtmlReporter.js");
11
+ const MarkdownReporter_js_1 = require("../../reporters/MarkdownReporter.js");
12
+ const types_js_1 = require("../../types.js");
13
+ /**
14
+ * Watch for changes and re-run analysis
15
+ */
16
+ async function watchCommand(options = {}) {
17
+ const cwd = process.cwd();
18
+ // Load config
19
+ const config = await loadConfig(options.config, cwd);
20
+ console.log('💎 Facet Coverage Watch Mode\n');
21
+ console.log('Watching for changes...\n');
22
+ // Initial run
23
+ await runAnalysis(config, cwd, options.validate);
24
+ // Watch patterns
25
+ const watchPatterns = [
26
+ ...config.structureFiles,
27
+ 'features/**/facets/**/*.md',
28
+ `${config.testDir}/**/*.{ts,js,tsx,jsx}`,
29
+ ];
30
+ // Create watcher
31
+ const watcher = (0, chokidar_1.watch)(watchPatterns, {
32
+ cwd,
33
+ ignoreInitial: true,
34
+ persistent: true,
35
+ });
36
+ // Debounce timer
37
+ let debounceTimer = null;
38
+ watcher.on('all', (event, path) => {
39
+ // Clear existing timer
40
+ if (debounceTimer) {
41
+ clearTimeout(debounceTimer);
42
+ }
43
+ // Debounce to avoid multiple rapid runs
44
+ debounceTimer = setTimeout(async () => {
45
+ console.log(`\n📝 Change detected: ${path} (${event})`);
46
+ console.log('Re-analyzing...\n');
47
+ await runAnalysis(config, cwd, options.validate);
48
+ }, 300);
49
+ });
50
+ // Handle shutdown
51
+ process.on('SIGINT', () => {
52
+ console.log('\n\n👋 Stopping watch mode...');
53
+ watcher.close();
54
+ process.exit(0);
55
+ });
56
+ // Keep process alive
57
+ console.log('Press Ctrl+C to stop.\n');
58
+ }
59
+ /**
60
+ * Run analysis
61
+ */
62
+ async function runAnalysis(config, cwd, validate = false) {
63
+ const startTime = Date.now();
64
+ try {
65
+ // Optionally validate first
66
+ if (validate) {
67
+ const validator = new Validator_js_1.Validator(config);
68
+ const validationResult = await validator.validate(cwd);
69
+ if (!validationResult.valid) {
70
+ console.log('❌ Validation failed:');
71
+ for (const error of validationResult.errors) {
72
+ console.log(` - ${error.message}`);
73
+ }
74
+ console.log('');
75
+ return;
76
+ }
77
+ }
78
+ // Calculate coverage
79
+ const calculator = new CoverageCalculator_js_1.CoverageCalculator(config);
80
+ const report = await calculator.calculateCoverage(cwd);
81
+ // Generate reports
82
+ if (config.output.formats.includes('json')) {
83
+ const reporter = new JsonReporter_js_1.JsonReporter(config);
84
+ reporter.write(report, cwd);
85
+ }
86
+ if (config.output.formats.includes('html')) {
87
+ const reporter = new HtmlReporter_js_1.HtmlReporter(config);
88
+ reporter.write(report, cwd);
89
+ }
90
+ if (config.output.formats.includes('markdown')) {
91
+ const reporter = new MarkdownReporter_js_1.MarkdownReporter(config);
92
+ reporter.write(report, cwd);
93
+ }
94
+ // Print summary
95
+ const duration = Date.now() - startTime;
96
+ const icon = report.summary.percentage >= 80 ? '✅' : report.summary.percentage >= 50 ? '🟡' : '❌';
97
+ console.log('┌─────────────────────────────────────────────┐');
98
+ console.log(`│ ${icon} Coverage: ${String(report.summary.percentage + '%').padEnd(6)} (${report.summary.coveredFacets}/${report.summary.totalFacets} facets)`.padEnd(45) + ' │');
99
+ console.log('└─────────────────────────────────────────────┘');
100
+ // Show type breakdown
101
+ if (report.byType.length > 0) {
102
+ for (const type of report.byType) {
103
+ const typeIcon = type.percentage === 100 ? '✅' : type.percentage >= 75 ? '🟡' : '❌';
104
+ console.log(` ${typeIcon} ${type.type}: ${type.percentage}%`);
105
+ }
106
+ }
107
+ console.log(`\n⏱️ Analysis completed in ${duration}ms`);
108
+ // Check thresholds
109
+ const thresholdResult = calculator.checkThresholds(report);
110
+ if (!thresholdResult.passed) {
111
+ console.log('\n⚠️ Thresholds not met:');
112
+ for (const failure of thresholdResult.failures) {
113
+ console.log(` - ${failure}`);
114
+ }
115
+ }
116
+ }
117
+ catch (error) {
118
+ console.error('❌ Error during analysis:', error);
119
+ }
120
+ }
121
+ /**
122
+ * Load configuration from file or use defaults
123
+ */
124
+ async function loadConfig(configPath, cwd) {
125
+ const configFiles = [
126
+ configPath,
127
+ 'facet.config.js',
128
+ 'facet.config.mjs',
129
+ 'facet.config.json',
130
+ ].filter(Boolean);
131
+ for (const file of configFiles) {
132
+ const fullPath = (0, path_1.resolve)(cwd, file);
133
+ if ((0, fs_1.existsSync)(fullPath)) {
134
+ if (file.endsWith('.json')) {
135
+ const content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
136
+ return { ...types_js_1.defaultConfig, ...JSON.parse(content) };
137
+ }
138
+ else {
139
+ try {
140
+ const imported = await import(fullPath);
141
+ return { ...types_js_1.defaultConfig, ...(imported.default || imported) };
142
+ }
143
+ catch (error) {
144
+ console.warn(`Warning: Could not load config from ${file}`);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return types_js_1.defaultConfig;
150
+ }
151
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../../src/cli/commands/watch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const commander_1 = require("commander");
4
+ const generate_js_1 = require("./commands/generate.js");
5
+ const analyze_js_1 = require("./commands/analyze.js");
6
+ const validate_js_1 = require("./commands/validate.js");
7
+ const watch_js_1 = require("./commands/watch.js");
8
+ const program = new commander_1.Command();
9
+ program
10
+ .name('facet')
11
+ .description('💎 Test every facet of your features - natural specifications with rigorous coverage tracking')
12
+ .version('0.1.0');
13
+ // Generate command
14
+ program
15
+ .command('generate <dir>')
16
+ .description('Generate structure.json from facet markdown files')
17
+ .option('-o, --output <path>', 'Output directory for structure.json')
18
+ .option('-t, --type <type>', 'Override facet type (default: derived from filename)')
19
+ .action(generate_js_1.generateCommand);
20
+ // Analyze command
21
+ program
22
+ .command('analyze')
23
+ .description('Analyze facet coverage')
24
+ .option('-c, --config <path>', 'Path to config file')
25
+ .option('-f, --format <format>', 'Output format (json, html, markdown)')
26
+ .option('-t, --threshold <number>', 'Coverage threshold percentage')
27
+ .option('--json', 'Output results as JSON')
28
+ .option('--silent', 'Suppress console output')
29
+ .action(analyze_js_1.analyzeCommand);
30
+ // Validate command
31
+ program
32
+ .command('validate')
33
+ .description('Validate facet structure and test links')
34
+ .option('-c, --config <path>', 'Path to config file')
35
+ .option('--strict', 'Enable strict validation (require all tests linked)')
36
+ .option('--json', 'Output results as JSON')
37
+ .action(validate_js_1.validateCommand);
38
+ // Watch command
39
+ program
40
+ .command('watch')
41
+ .description('Watch for changes and re-run analysis')
42
+ .option('-c, --config <path>', 'Path to config file')
43
+ .option('-v, --validate', 'Run validation before analysis')
44
+ .action(watch_js_1.watchCommand);
45
+ // Parse arguments
46
+ program.parse();
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,wDAAyD;AACzD,sDAAuD;AACvD,wDAAyD;AACzD,kDAAmD;AAEnD,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,+FAA+F,CAAC;KAC5G,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,mBAAmB;AACnB,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,qCAAqC,CAAC;KACpE,MAAM,CAAC,mBAAmB,EAAE,sDAAsD,CAAC;KACnF,MAAM,CAAC,6BAAe,CAAC,CAAC;AAE3B,kBAAkB;AAClB,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,wBAAwB,CAAC;KACrC,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KACpD,MAAM,CAAC,uBAAuB,EAAE,sCAAsC,CAAC;KACvE,MAAM,CAAC,0BAA0B,EAAE,+BAA+B,CAAC;KACnE,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,UAAU,EAAE,yBAAyB,CAAC;KAC7C,MAAM,CAAC,2BAAc,CAAC,CAAC;AAE1B,mBAAmB;AACnB,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KACpD,MAAM,CAAC,UAAU,EAAE,qDAAqD,CAAC;KACzE,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,6BAAe,CAAC,CAAC;AAE3B,gBAAgB;AAChB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;KACpD,MAAM,CAAC,gBAAgB,EAAE,gCAAgC,CAAC;KAC1D,MAAM,CAAC,uBAAY,CAAC,CAAC;AAExB,kBAAkB;AAClB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { FacetConfig, FacetCoverage, CoverageReport } from '../types.js';
2
+ /**
3
+ * Calculates coverage metrics for facets
4
+ */
5
+ export declare class CoverageCalculator {
6
+ private config;
7
+ private structureReader;
8
+ private testScanner;
9
+ constructor(config?: Partial<FacetConfig>);
10
+ /**
11
+ * Calculate complete coverage report
12
+ */
13
+ calculateCoverage(cwd?: string): Promise<CoverageReport>;
14
+ /**
15
+ * Calculate coverage for a single feature
16
+ */
17
+ private calculateFeatureCoverage;
18
+ /**
19
+ * Calculate coverage breakdown by facet type
20
+ */
21
+ private calculateTypeCoverage;
22
+ /**
23
+ * Check if coverage meets configured thresholds
24
+ */
25
+ checkThresholds(report: CoverageReport): {
26
+ passed: boolean;
27
+ failures: string[];
28
+ };
29
+ /**
30
+ * Get coverage for a specific facet
31
+ */
32
+ getFacetCoverage(facetId: string, cwd?: string): Promise<FacetCoverage | null>;
33
+ }
34
+ //# sourceMappingURL=CoverageCalculator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CoverageCalculator.d.ts","sourceRoot":"","sources":["../../src/core/CoverageCalculator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EAIX,aAAa,EAGb,cAAc,EACf,MAAM,aAAa,CAAC;AAKrB;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;gBAErB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM;IAM7C;;OAEG;IACG,iBAAiB,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,cAAc,CAAC;IA+D7E;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAsChC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA6B7B;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG;QACvC,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB;IA0BD;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;CAYpG"}
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CoverageCalculator = void 0;
4
+ const path_1 = require("path");
5
+ const types_js_1 = require("../types.js");
6
+ const StructureReader_js_1 = require("./StructureReader.js");
7
+ const TestScanner_js_1 = require("./TestScanner.js");
8
+ /**
9
+ * Calculates coverage metrics for facets
10
+ */
11
+ class CoverageCalculator {
12
+ config;
13
+ structureReader;
14
+ testScanner;
15
+ constructor(config = {}) {
16
+ this.config = { ...types_js_1.defaultConfig, ...config };
17
+ this.structureReader = new StructureReader_js_1.StructureReader(this.config);
18
+ this.testScanner = new TestScanner_js_1.TestScanner(this.config);
19
+ }
20
+ /**
21
+ * Calculate complete coverage report
22
+ */
23
+ async calculateCoverage(cwd = process.cwd()) {
24
+ // Get all structures and test links
25
+ const structures = await this.structureReader.readAllStructures(cwd);
26
+ const testLinks = await this.testScanner.scanAllTests(cwd);
27
+ // Build facet ID to test links map
28
+ const facetToTests = new Map();
29
+ for (const link of testLinks) {
30
+ for (const facetId of link.facetIds) {
31
+ const existing = facetToTests.get(facetId) || [];
32
+ existing.push(link);
33
+ facetToTests.set(facetId, existing);
34
+ }
35
+ }
36
+ // Calculate feature-level coverage
37
+ const features = [];
38
+ const allFacets = [];
39
+ const uncovered = [];
40
+ for (const [structureFile, structure] of structures) {
41
+ const featureCoverage = this.calculateFeatureCoverage(structure, structureFile, facetToTests);
42
+ features.push(featureCoverage);
43
+ // Collect all facet coverage data
44
+ allFacets.push(...featureCoverage.facets);
45
+ // Collect uncovered facets
46
+ for (const fc of featureCoverage.facets) {
47
+ if (!fc.covered) {
48
+ uncovered.push(fc.facet);
49
+ }
50
+ }
51
+ }
52
+ // Calculate overall statistics
53
+ const totalFacets = allFacets.length;
54
+ const coveredFacets = allFacets.filter(f => f.covered).length;
55
+ const uncoveredFacets = totalFacets - coveredFacets;
56
+ const percentage = totalFacets > 0 ? Math.round((coveredFacets / totalFacets) * 100) : 100;
57
+ // Calculate coverage by type
58
+ const byType = this.calculateTypeCoverage(allFacets);
59
+ return {
60
+ timestamp: new Date().toISOString(),
61
+ summary: {
62
+ totalFacets,
63
+ coveredFacets,
64
+ uncoveredFacets,
65
+ percentage,
66
+ },
67
+ byType,
68
+ features,
69
+ tests: testLinks,
70
+ uncovered,
71
+ };
72
+ }
73
+ /**
74
+ * Calculate coverage for a single feature
75
+ */
76
+ calculateFeatureCoverage(structure, structureFile, facetToTests) {
77
+ const facetCoverages = [];
78
+ for (const facet of structure.facets) {
79
+ const coveredBy = facetToTests.get(facet.id) || [];
80
+ facetCoverages.push({
81
+ facet,
82
+ covered: coveredBy.length > 0,
83
+ coveredBy,
84
+ });
85
+ }
86
+ const totalFacets = facetCoverages.length;
87
+ const coveredFacets = facetCoverages.filter(f => f.covered).length;
88
+ const percentage = totalFacets > 0 ? Math.round((coveredFacets / totalFacets) * 100) : 100;
89
+ // Calculate by type for this feature
90
+ const byType = this.calculateTypeCoverage(facetCoverages);
91
+ // Get feature path (parent of .facet directory)
92
+ const facetDir = (0, path_1.dirname)(structureFile);
93
+ const featurePath = (0, path_1.dirname)(facetDir);
94
+ return {
95
+ feature: structure.feature,
96
+ path: featurePath,
97
+ totalFacets,
98
+ coveredFacets,
99
+ percentage,
100
+ byType,
101
+ facets: facetCoverages,
102
+ };
103
+ }
104
+ /**
105
+ * Calculate coverage breakdown by facet type
106
+ */
107
+ calculateTypeCoverage(facetCoverages) {
108
+ const typeMap = new Map();
109
+ for (const fc of facetCoverages) {
110
+ const type = fc.facet.type;
111
+ const existing = typeMap.get(type) || { total: 0, covered: 0 };
112
+ existing.total++;
113
+ if (fc.covered) {
114
+ existing.covered++;
115
+ }
116
+ typeMap.set(type, existing);
117
+ }
118
+ const result = [];
119
+ for (const [type, stats] of typeMap) {
120
+ result.push({
121
+ type,
122
+ total: stats.total,
123
+ covered: stats.covered,
124
+ percentage: stats.total > 0 ? Math.round((stats.covered / stats.total) * 100) : 100,
125
+ });
126
+ }
127
+ // Sort by type name
128
+ result.sort((a, b) => a.type.localeCompare(b.type));
129
+ return result;
130
+ }
131
+ /**
132
+ * Check if coverage meets configured thresholds
133
+ */
134
+ checkThresholds(report) {
135
+ const failures = [];
136
+ // Check global threshold
137
+ if (report.summary.percentage < this.config.thresholds.global) {
138
+ failures.push(`Global coverage ${report.summary.percentage}% is below threshold of ${this.config.thresholds.global}%`);
139
+ }
140
+ // Check per-type thresholds
141
+ for (const [type, threshold] of Object.entries(this.config.thresholds.byType)) {
142
+ const typeCoverage = report.byType.find(t => t.type === type);
143
+ if (typeCoverage && typeCoverage.percentage < threshold) {
144
+ failures.push(`${type} coverage ${typeCoverage.percentage}% is below threshold of ${threshold}%`);
145
+ }
146
+ }
147
+ return {
148
+ passed: failures.length === 0,
149
+ failures,
150
+ };
151
+ }
152
+ /**
153
+ * Get coverage for a specific facet
154
+ */
155
+ async getFacetCoverage(facetId, cwd = process.cwd()) {
156
+ const report = await this.calculateCoverage(cwd);
157
+ for (const feature of report.features) {
158
+ const facetCoverage = feature.facets.find(f => f.facet.id === facetId);
159
+ if (facetCoverage) {
160
+ return facetCoverage;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ }
166
+ exports.CoverageCalculator = CoverageCalculator;
167
+ //# sourceMappingURL=CoverageCalculator.js.map