api-tests-coverage 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/pipeline/confidence.d.ts +70 -0
- package/dist/src/pipeline/confidence.d.ts.map +1 -0
- package/dist/src/pipeline/confidence.js +198 -0
- package/dist/src/pipeline/graph.d.ts +58 -0
- package/dist/src/pipeline/graph.d.ts.map +1 -0
- package/dist/src/pipeline/graph.js +199 -0
- package/dist/src/pipeline/index.d.ts +24 -0
- package/dist/src/pipeline/index.d.ts.map +1 -0
- package/dist/src/pipeline/index.js +41 -0
- package/dist/src/pipeline/orchestrator.d.ts +42 -0
- package/dist/src/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/src/pipeline/orchestrator.js +115 -0
- package/dist/src/pipeline/stageInterface.d.ts +45 -0
- package/dist/src/pipeline/stageInterface.d.ts.map +1 -0
- package/dist/src/pipeline/stageInterface.js +17 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts +38 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/abstractLayerTraversal.js +203 -0
- package/dist/src/pipeline/stages/ast/astStage.d.ts +19 -0
- package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/astStage.js +238 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts +23 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/crossFileResolver.js +183 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts +15 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/graphBuilder.js +268 -0
- package/dist/src/pipeline/stages/ast/importResolver.d.ts +22 -0
- package/dist/src/pipeline/stages/ast/importResolver.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/importResolver.js +186 -0
- package/dist/src/pipeline/stages/ast/types.d.ts +85 -0
- package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/ast/types.js +5 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts +25 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/conflictEmitter.js +90 -0
- package/dist/src/pipeline/stages/dast/dastStage.d.ts +17 -0
- package/dist/src/pipeline/stages/dast/dastStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/dastStage.js +203 -0
- package/dist/src/pipeline/stages/dast/types.d.ts +49 -0
- package/dist/src/pipeline/stages/dast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/dast/types.js +9 -0
- package/dist/src/pipeline/stages/iast/iastStage.d.ts +17 -0
- package/dist/src/pipeline/stages/iast/iastStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/iast/iastStage.js +191 -0
- package/dist/src/pipeline/stages/iast/types.d.ts +48 -0
- package/dist/src/pipeline/stages/iast/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/iast/types.js +8 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +17 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/conflictDetector.js +60 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts +15 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +141 -0
- package/dist/src/pipeline/stages/merge/mergeRules.d.ts +39 -0
- package/dist/src/pipeline/stages/merge/mergeRules.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/mergeRules.js +90 -0
- package/dist/src/pipeline/stages/merge/mergeStage.d.ts +20 -0
- package/dist/src/pipeline/stages/merge/mergeStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/mergeStage.js +145 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.d.ts +11 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.d.ts.map +1 -0
- package/dist/src/pipeline/stages/merge/summaryComputer.js +46 -0
- package/dist/src/pipeline/stages/sca/ciDetector.d.ts +15 -0
- package/dist/src/pipeline/stages/sca/ciDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/ciDetector.js +87 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts +31 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/dependencyClassification.js +296 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts +25 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/dependencyDetector.js +416 -0
- package/dist/src/pipeline/stages/sca/scaStage.d.ts +21 -0
- package/dist/src/pipeline/stages/sca/scaStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/scaStage.js +208 -0
- package/dist/src/pipeline/stages/sca/types.d.ts +61 -0
- package/dist/src/pipeline/stages/sca/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/sca/types.js +9 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts +19 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +118 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts +20 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +238 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts +22 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/testEndpointMapper.js +134 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts +16 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/testLayerClassifier.js +191 -0
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts +20 -0
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/tiaStage.js +215 -0
- package/dist/src/pipeline/stages/tia/types.d.ts +52 -0
- package/dist/src/pipeline/stages/tia/types.d.ts.map +1 -0
- package/dist/src/pipeline/stages/tia/types.js +5 -0
- package/dist/src/pipeline/types.d.ts +128 -0
- package/dist/src/pipeline/types.d.ts.map +1 -0
- package/dist/src/pipeline/types.js +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AST Stage — Stage 2 of the coverage pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the existing AST analysis orchestrator and extends it with:
|
|
6
|
+
* - Multi-file analysis: parses all discovered files and builds a cross-file symbol table
|
|
7
|
+
* - Abstract layer traversal: resolves inherited assertions, helpers, fixtures
|
|
8
|
+
* - Graph building: converts analysis results into GraphNode[] and GraphEdge[]
|
|
9
|
+
*
|
|
10
|
+
* The AST stage reads the SCA output (from context.stageOutputs) to determine
|
|
11
|
+
* which languages and frameworks are present, enabling targeted analysis heuristics.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.AstStage = void 0;
|
|
48
|
+
const astAnalysisOrchestrator_1 = require("../../../ast/astAnalysisOrchestrator");
|
|
49
|
+
const crossFileResolver_1 = require("./crossFileResolver");
|
|
50
|
+
const abstractLayerTraversal_1 = require("./abstractLayerTraversal");
|
|
51
|
+
const graphBuilder_1 = require("./graphBuilder");
|
|
52
|
+
const fileClassifier_1 = require("../../../discovery/fileClassifier");
|
|
53
|
+
const projectDiscovery_1 = require("../../../discovery/projectDiscovery");
|
|
54
|
+
const parserRegistry_1 = require("../../../ast/parserRegistry");
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
class AstStage {
|
|
58
|
+
constructor() {
|
|
59
|
+
this.name = 'ast';
|
|
60
|
+
this.optional = false;
|
|
61
|
+
}
|
|
62
|
+
async execute(context) {
|
|
63
|
+
var _a;
|
|
64
|
+
const startTime = Date.now();
|
|
65
|
+
const filesScanned = [];
|
|
66
|
+
const filesSkipped = [];
|
|
67
|
+
// Ensure all language analyzers are registered
|
|
68
|
+
(0, astAnalysisOrchestrator_1.registerAllAnalyzers)();
|
|
69
|
+
// Get SCA output for language/framework context
|
|
70
|
+
const scaOutput = context.stageOutputs.get('sca');
|
|
71
|
+
// Build analysis context
|
|
72
|
+
const analysisContext = (0, astAnalysisOrchestrator_1.buildAnalysisContext)(context.config.astConfig);
|
|
73
|
+
// Discover all files
|
|
74
|
+
const artifacts = (0, projectDiscovery_1.discoverProject)({ rootDir: context.projectRoot });
|
|
75
|
+
const allSourceFiles = [
|
|
76
|
+
...artifacts.testFiles,
|
|
77
|
+
...artifacts.serviceFiles,
|
|
78
|
+
];
|
|
79
|
+
// Phase 1: Parse all files and build semantic models
|
|
80
|
+
const models = new Map();
|
|
81
|
+
const interactionsMap = new Map();
|
|
82
|
+
for (const filePath of allSourceFiles) {
|
|
83
|
+
// Enforce per-file timeout (RULE-16)
|
|
84
|
+
const fileStart = Date.now();
|
|
85
|
+
let content;
|
|
86
|
+
try {
|
|
87
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
filesSkipped.push({ file: filePath, reason: 'read-error' });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Detect language from file extension
|
|
94
|
+
const language = detectLanguage(filePath, scaOutput);
|
|
95
|
+
if (!language) {
|
|
96
|
+
filesSkipped.push({ file: filePath, reason: 'unsupported-language' });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// Check timeout
|
|
100
|
+
if (Date.now() - fileStart > context.config.fileTimeoutMs) {
|
|
101
|
+
filesSkipped.push({ file: filePath, reason: 'timeout' });
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
// Use existing AST analysis
|
|
106
|
+
const result = (0, astAnalysisOrchestrator_1.analyzeFileDetailed)(content, filePath, language, analysisContext);
|
|
107
|
+
interactionsMap.set(filePath, result.interactions);
|
|
108
|
+
// Build semantic model if we have an analyzer
|
|
109
|
+
const analyzer = (0, parserRegistry_1.getAnalyzer)(language, analysisContext.astConfig);
|
|
110
|
+
if (analyzer) {
|
|
111
|
+
const parsed = analyzer.parse(filePath, content);
|
|
112
|
+
if (!parsed.parseError) {
|
|
113
|
+
const model = analyzer.buildSemanticModel(parsed, analysisContext);
|
|
114
|
+
models.set(filePath, model);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// RULE-02: File not skipped on parse error — we already have fallback interactions
|
|
118
|
+
filesSkipped.push({ file: filePath, reason: 'parse-error' });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
filesScanned.push(filePath);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
// RULE-02: Never skip a file entirely on error
|
|
125
|
+
filesSkipped.push({
|
|
126
|
+
file: filePath,
|
|
127
|
+
reason: `analysis-error: ${err instanceof Error ? err.message : String(err)}`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Phase 2: Build cross-file symbol table
|
|
132
|
+
const crossFileTable = (0, crossFileResolver_1.buildCrossFileSymbolTable)(models, context.projectRoot);
|
|
133
|
+
// Phase 3: Run abstract layer traversal for test files
|
|
134
|
+
const traversalResults = new Map();
|
|
135
|
+
const depthCap = context.config.traversalDepthCap;
|
|
136
|
+
for (const [filePath] of models) {
|
|
137
|
+
const basename = path.basename(filePath);
|
|
138
|
+
if (!(0, fileClassifier_1.isTestFile)(basename))
|
|
139
|
+
continue;
|
|
140
|
+
// Try inheritance chain traversal for classes in this test file
|
|
141
|
+
for (const [className, classDecl] of crossFileTable.classes) {
|
|
142
|
+
if (classDecl.filePath === filePath && classDecl.extendsClass) {
|
|
143
|
+
const result = (0, abstractLayerTraversal_1.traverseInheritanceChain)(className, crossFileTable, depthCap);
|
|
144
|
+
traversalResults.set(filePath, result);
|
|
145
|
+
break; // One traversal result per test file
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// If no inheritance found, try resolving imported helpers
|
|
149
|
+
if (!traversalResults.has(filePath)) {
|
|
150
|
+
const model = models.get(filePath);
|
|
151
|
+
if (model) {
|
|
152
|
+
for (const [funcName, func] of model.functions) {
|
|
153
|
+
for (const calledFunc of func.calledFunctions) {
|
|
154
|
+
// Check if this is an imported function
|
|
155
|
+
const importedFiles = (_a = crossFileTable.importGraph.get(filePath)) !== null && _a !== void 0 ? _a : [];
|
|
156
|
+
for (const importedFile of importedFiles) {
|
|
157
|
+
const importedModel = models.get(importedFile);
|
|
158
|
+
if (importedModel === null || importedModel === void 0 ? void 0 : importedModel.functions.has(calledFunc)) {
|
|
159
|
+
const result = (0, abstractLayerTraversal_1.resolveImportedHelper)(calledFunc, importedFile, crossFileTable, depthCap);
|
|
160
|
+
if (result.resolvedAssertions.length > 0 || result.resolvedHttpCalls.length > 0) {
|
|
161
|
+
traversalResults.set(filePath, result);
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (traversalResults.has(filePath))
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
if (traversalResults.has(filePath))
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Phase 4: Build graph nodes and edges
|
|
176
|
+
const { nodes, edges } = (0, graphBuilder_1.buildAstGraph)(models, crossFileTable, traversalResults, interactionsMap);
|
|
177
|
+
// Add nodes and edges to the context graph
|
|
178
|
+
for (const node of nodes) {
|
|
179
|
+
if (!context.graph.hasNode(node.id)) {
|
|
180
|
+
context.graph.addNode(node);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (const edge of edges) {
|
|
184
|
+
if (!context.graph.hasEdge(edge.id)) {
|
|
185
|
+
context.graph.addEdge(edge);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Record diagnostics (RULE-12)
|
|
189
|
+
const diagnostics = {
|
|
190
|
+
stageName: 'ast',
|
|
191
|
+
filesScanned,
|
|
192
|
+
filesSkipped,
|
|
193
|
+
durationMs: Date.now() - startTime,
|
|
194
|
+
metadata: {
|
|
195
|
+
totalModels: models.size,
|
|
196
|
+
totalInteractions: Array.from(interactionsMap.values()).reduce((sum, arr) => sum + arr.length, 0),
|
|
197
|
+
totalClasses: crossFileTable.classes.size,
|
|
198
|
+
totalExportedSymbols: crossFileTable.exportedSymbols.size,
|
|
199
|
+
traversalResultCount: traversalResults.size,
|
|
200
|
+
graphNodesAdded: nodes.length,
|
|
201
|
+
graphEdgesAdded: edges.length,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
context.diagnostics.set('ast', diagnostics);
|
|
205
|
+
// Build output
|
|
206
|
+
const output = {
|
|
207
|
+
models,
|
|
208
|
+
crossFileTable,
|
|
209
|
+
traversalResults,
|
|
210
|
+
analyzedFiles: filesScanned,
|
|
211
|
+
skippedFiles: filesSkipped,
|
|
212
|
+
};
|
|
213
|
+
context.stageOutputs.set('ast', output);
|
|
214
|
+
return output;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.AstStage = AstStage;
|
|
218
|
+
/**
|
|
219
|
+
* Detect the language of a file from its extension.
|
|
220
|
+
*/
|
|
221
|
+
function detectLanguage(filePath, scaOutput) {
|
|
222
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
223
|
+
const extensionMap = {
|
|
224
|
+
'.ts': 'typescript',
|
|
225
|
+
'.tsx': 'typescript',
|
|
226
|
+
'.js': 'javascript',
|
|
227
|
+
'.jsx': 'javascript',
|
|
228
|
+
'.mjs': 'javascript',
|
|
229
|
+
'.cjs': 'javascript',
|
|
230
|
+
'.java': 'java',
|
|
231
|
+
'.kt': 'kotlin',
|
|
232
|
+
'.kts': 'kotlin',
|
|
233
|
+
'.py': 'python',
|
|
234
|
+
'.rb': 'ruby',
|
|
235
|
+
'.feature': 'cucumber',
|
|
236
|
+
};
|
|
237
|
+
return extensionMap[ext];
|
|
238
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-file resolver — builds a multi-file symbol table from all parsed semantic models.
|
|
3
|
+
*
|
|
4
|
+
* Aggregates per-file symbol tables, export declarations, class declarations,
|
|
5
|
+
* and import graphs into a unified `CrossFileSymbolTable` that enables
|
|
6
|
+
* cross-file constant resolution, inheritance chain traversal, and
|
|
7
|
+
* import-based helper resolution.
|
|
8
|
+
*/
|
|
9
|
+
import type { SemanticModel } from '../../../ast/astTypes';
|
|
10
|
+
import type { CrossFileSymbolTable } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Build a cross-file symbol table from all parsed semantic models.
|
|
13
|
+
*
|
|
14
|
+
* @param models - Map of filePath → SemanticModel
|
|
15
|
+
* @param projectRoot - The project root directory
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildCrossFileSymbolTable(models: Map<string, SemanticModel>, projectRoot: string): CrossFileSymbolTable;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a symbol name using the cross-file symbol table.
|
|
20
|
+
* Tries: direct lookup, qualified lookup (filePath::name), and import-chain following.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveSymbolCrossFile(symbolName: string, fromFile: string, table: CrossFileSymbolTable): string | undefined;
|
|
23
|
+
//# sourceMappingURL=crossFileResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crossFileResolver.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/crossFileResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAuC,MAAM,uBAAuB,CAAC;AAChG,OAAO,KAAK,EAAE,oBAAoB,EAAuC,MAAM,SAAS,CAAC;AAGzF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,oBAAoB,CAuDtB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,oBAAoB,GAC1B,MAAM,GAAG,SAAS,CA4BpB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-file resolver — builds a multi-file symbol table from all parsed semantic models.
|
|
4
|
+
*
|
|
5
|
+
* Aggregates per-file symbol tables, export declarations, class declarations,
|
|
6
|
+
* and import graphs into a unified `CrossFileSymbolTable` that enables
|
|
7
|
+
* cross-file constant resolution, inheritance chain traversal, and
|
|
8
|
+
* import-based helper resolution.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.buildCrossFileSymbolTable = buildCrossFileSymbolTable;
|
|
12
|
+
exports.resolveSymbolCrossFile = resolveSymbolCrossFile;
|
|
13
|
+
const importResolver_1 = require("./importResolver");
|
|
14
|
+
/**
|
|
15
|
+
* Build a cross-file symbol table from all parsed semantic models.
|
|
16
|
+
*
|
|
17
|
+
* @param models - Map of filePath → SemanticModel
|
|
18
|
+
* @param projectRoot - The project root directory
|
|
19
|
+
*/
|
|
20
|
+
function buildCrossFileSymbolTable(models, projectRoot) {
|
|
21
|
+
const exportedSymbols = new Map();
|
|
22
|
+
const classes = new Map();
|
|
23
|
+
const importGraph = new Map();
|
|
24
|
+
// Pass 1: Collect all exported symbols and class declarations
|
|
25
|
+
for (const [filePath, model] of models) {
|
|
26
|
+
// Collect constants as exported symbols
|
|
27
|
+
for (const [name, symbol] of model.constants) {
|
|
28
|
+
if (symbol.value !== undefined) {
|
|
29
|
+
exportedSymbols.set(`${filePath}::${name}`, { filePath, value: symbol.value });
|
|
30
|
+
// Also register as global name for simple lookups
|
|
31
|
+
if (!exportedSymbols.has(name)) {
|
|
32
|
+
exportedSymbols.set(name, { filePath, value: symbol.value });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Collect local variables that are const-like with string values
|
|
37
|
+
for (const [name, symbol] of model.localVariables) {
|
|
38
|
+
if (symbol.kind === 'const' && symbol.value !== undefined) {
|
|
39
|
+
exportedSymbols.set(`${filePath}::${name}`, { filePath, value: symbol.value });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Collect enum members as exported symbols
|
|
43
|
+
for (const [enumName, members] of model.enums) {
|
|
44
|
+
for (const [memberName, value] of members) {
|
|
45
|
+
const qualifiedName = `${enumName}.${memberName}`;
|
|
46
|
+
exportedSymbols.set(`${filePath}::${qualifiedName}`, { filePath, value });
|
|
47
|
+
if (!exportedSymbols.has(qualifiedName)) {
|
|
48
|
+
exportedSymbols.set(qualifiedName, { filePath, value });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Extract class declarations from functions (heuristic: capitalize name + has methods)
|
|
53
|
+
extractClassDeclarations(filePath, model, classes);
|
|
54
|
+
}
|
|
55
|
+
// Pass 2: Build import graph
|
|
56
|
+
for (const [filePath, model] of models) {
|
|
57
|
+
const imports = extractImportsFromModel(filePath, model, projectRoot);
|
|
58
|
+
const resolvedPaths = [];
|
|
59
|
+
for (const imp of imports) {
|
|
60
|
+
if (imp.resolvedPath) {
|
|
61
|
+
resolvedPaths.push(imp.resolvedPath);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
importGraph.set(filePath, resolvedPaths);
|
|
65
|
+
}
|
|
66
|
+
return { models, exportedSymbols, classes, importGraph };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve a symbol name using the cross-file symbol table.
|
|
70
|
+
* Tries: direct lookup, qualified lookup (filePath::name), and import-chain following.
|
|
71
|
+
*/
|
|
72
|
+
function resolveSymbolCrossFile(symbolName, fromFile, table) {
|
|
73
|
+
var _a;
|
|
74
|
+
// Try qualified lookup for the current file
|
|
75
|
+
const qualified = table.exportedSymbols.get(`${fromFile}::${symbolName}`);
|
|
76
|
+
if (qualified)
|
|
77
|
+
return qualified.value;
|
|
78
|
+
// Try global lookup
|
|
79
|
+
const global = table.exportedSymbols.get(symbolName);
|
|
80
|
+
if (global)
|
|
81
|
+
return global.value;
|
|
82
|
+
// Try following imports from the current file
|
|
83
|
+
const importedFiles = (_a = table.importGraph.get(fromFile)) !== null && _a !== void 0 ? _a : [];
|
|
84
|
+
for (const importedFile of importedFiles) {
|
|
85
|
+
const fromImported = table.exportedSymbols.get(`${importedFile}::${symbolName}`);
|
|
86
|
+
if (fromImported)
|
|
87
|
+
return fromImported.value;
|
|
88
|
+
}
|
|
89
|
+
// Try dotted name resolution (e.g. "Config.BASE_URL")
|
|
90
|
+
const dotParts = symbolName.split('.');
|
|
91
|
+
if (dotParts.length >= 2) {
|
|
92
|
+
// Try each possible combination
|
|
93
|
+
for (const [key, entry] of table.exportedSymbols) {
|
|
94
|
+
if (key.endsWith(`::${symbolName}`) || key === symbolName) {
|
|
95
|
+
return entry.value;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Extract import declarations from a semantic model.
|
|
103
|
+
* This is a heuristic extraction — real import extraction would come from the AST.
|
|
104
|
+
*/
|
|
105
|
+
function extractImportsFromModel(filePath, model, projectRoot) {
|
|
106
|
+
const imports = [];
|
|
107
|
+
// Use functions' calledFunctions to infer cross-file references
|
|
108
|
+
for (const [, func] of model.functions) {
|
|
109
|
+
for (const calledName of func.calledFunctions) {
|
|
110
|
+
// If the function name contains a dot, it might be an import reference
|
|
111
|
+
const dotIndex = calledName.indexOf('.');
|
|
112
|
+
if (dotIndex > 0) {
|
|
113
|
+
const modulePart = calledName.substring(0, dotIndex);
|
|
114
|
+
const resolvedPath = (0, importResolver_1.resolveImportPath)(`./${modulePart}`, filePath, projectRoot, model.language);
|
|
115
|
+
if (resolvedPath) {
|
|
116
|
+
imports.push({
|
|
117
|
+
source: modulePart,
|
|
118
|
+
resolvedPath,
|
|
119
|
+
importedNames: [calledName.substring(dotIndex + 1)],
|
|
120
|
+
isDefault: false,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return imports;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Extract class declarations from a semantic model (heuristic).
|
|
130
|
+
*
|
|
131
|
+
* Looks for functions that follow class patterns:
|
|
132
|
+
* - Capitalized names
|
|
133
|
+
* - Constructor patterns
|
|
134
|
+
* - Extends/implements annotations
|
|
135
|
+
*/
|
|
136
|
+
function extractClassDeclarations(filePath, model, classes) {
|
|
137
|
+
const classMethods = new Map();
|
|
138
|
+
for (const [name, func] of model.functions) {
|
|
139
|
+
// Look for constructor patterns to detect class names
|
|
140
|
+
if (name === 'constructor' || name.startsWith('__init__'))
|
|
141
|
+
continue;
|
|
142
|
+
// Methods prefixed with "ClassName." or "ClassName#"
|
|
143
|
+
const dotIndex = name.indexOf('.');
|
|
144
|
+
const hashIndex = name.indexOf('#');
|
|
145
|
+
const separator = dotIndex >= 0 ? dotIndex : hashIndex;
|
|
146
|
+
if (separator > 0) {
|
|
147
|
+
const className = name.substring(0, separator);
|
|
148
|
+
const methodName = name.substring(separator + 1);
|
|
149
|
+
if (!classMethods.has(className)) {
|
|
150
|
+
classMethods.set(className, []);
|
|
151
|
+
}
|
|
152
|
+
classMethods.get(className).push(methodName);
|
|
153
|
+
}
|
|
154
|
+
// Check annotations for class membership
|
|
155
|
+
if (func.annotations) {
|
|
156
|
+
for (const ann of func.annotations) {
|
|
157
|
+
// Spring/Java annotations like @Controller, @Service, @RestController
|
|
158
|
+
if (ann.match(/^@(Controller|Service|Repository|Component|RestController|SpringBootTest)/)) {
|
|
159
|
+
const className = extractClassNameFromAnnotation(name, func);
|
|
160
|
+
if (className && !classMethods.has(className)) {
|
|
161
|
+
classMethods.set(className, []);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Convert to ClassDeclaration objects
|
|
168
|
+
for (const [className, methods] of classMethods) {
|
|
169
|
+
if (!classes.has(className)) {
|
|
170
|
+
classes.set(className, {
|
|
171
|
+
name: className,
|
|
172
|
+
methods,
|
|
173
|
+
filePath,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function extractClassNameFromAnnotation(functionName, _func) {
|
|
179
|
+
const dotIndex = functionName.indexOf('.');
|
|
180
|
+
if (dotIndex > 0)
|
|
181
|
+
return functionName.substring(0, dotIndex);
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph builder — converts SemanticModels, cross-file results, and traversal
|
|
3
|
+
* results into GraphNode[] and GraphEdge[] for the Coverage Knowledge Graph.
|
|
4
|
+
*/
|
|
5
|
+
import type { SemanticModel, ResolvedHttpInteraction } from '../../../ast/astTypes';
|
|
6
|
+
import type { GraphNode, GraphEdge } from '../../types';
|
|
7
|
+
import type { CrossFileSymbolTable, TraversalResult } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* Build graph nodes and edges from parsed semantic models and analysis results.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildAstGraph(models: Map<string, SemanticModel>, crossFileTable: CrossFileSymbolTable, traversalResults: Map<string, TraversalResult>, interactions: Map<string, ResolvedHttpInteraction[]>): {
|
|
12
|
+
nodes: GraphNode[];
|
|
13
|
+
edges: GraphEdge[];
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=graphBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/graphBuilder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoB,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAIrE;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAClC,cAAc,EAAE,oBAAoB,EACpC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,GACnD;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CAsN5C"}
|