@aaswe/codebase-ai
Version:
AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs
586 lines • 25.4 kB
JavaScript
"use strict";
/**
* Concrete Information Extractor
*
* Extracts concrete, actual information from code analysis results
* to generate TTL files with real class names, method signatures,
* dependencies, and business context indicators.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConcreteInformationExtractor = void 0;
const path = __importStar(require("path"));
const logger_1 = __importDefault(require("../../../utils/logger"));
/**
* Concrete Information Extractor
*
* Analyzes code structure to extract concrete, actionable information
* for generating comprehensive TTL files with real implementation details.
*/
class ConcreteInformationExtractor {
/**
* Extract concrete code structure from analysis result
*/
extractConcreteStructure(analysisResult) {
logger_1.default.debug('Extracting concrete code structure', {
filePath: analysisResult.filePath,
language: analysisResult.language,
classCount: analysisResult.classes.length,
functionCount: analysisResult.functions.length
});
try {
const structure = {
actualClasses: this.extractConcreteClasses(analysisResult),
actualFunctions: this.extractConcreteFunctions(analysisResult),
actualDependencies: this.extractConcreteDependencies(analysisResult),
actualExports: this.extractConcreteExports(analysisResult),
actualImports: this.extractConcreteImports(analysisResult),
architecturalPatterns: this.detectArchitecturalPatterns(analysisResult),
businessDomainIndicators: this.detectBusinessDomainIndicators(analysisResult),
qualityMetrics: this.calculateConcreteQualityMetrics(analysisResult)
};
logger_1.default.debug('Concrete structure extraction completed', {
classCount: structure.actualClasses.length,
functionCount: structure.actualFunctions.length,
dependencyCount: structure.actualDependencies.length,
patternCount: structure.architecturalPatterns.length,
domainIndicators: structure.businessDomainIndicators.length
});
return structure;
}
catch (error) {
logger_1.default.error('Failed to extract concrete structure', {
filePath: analysisResult.filePath,
error
});
throw error;
}
}
/**
* Extract concrete class information
*/
extractConcreteClasses(analysisResult) {
return analysisResult.classes.map(cls => ({
name: cls.name,
fullyQualifiedName: this.buildFullyQualifiedName(cls.name, analysisResult.filePath),
methods: this.extractConcreteMethods(cls),
properties: this.extractConcreteProperties(cls),
inheritance: {
extends: cls.extends,
implements: cls.implements
},
visibility: cls.visibility,
isAbstract: cls.isAbstract,
sourceLocation: {
file: cls.filePath,
startLine: cls.startLine,
endLine: cls.endLine,
startColumn: 0,
endColumn: 0
},
businessPurpose: this.inferBusinessPurpose(cls.name),
designPatterns: this.detectClassDesignPatterns(cls)
}));
}
/**
* Extract concrete method information from class
*/
extractConcreteMethods(cls) {
return cls.methods.map(method => ({
name: method.name,
signature: this.buildMethodSignature(method),
parameters: this.extractConcreteParameters(method.parameters),
returnType: method.returnType || 'void',
visibility: method.visibility,
isStatic: false, // Not available in current type
isAsync: method.isAsync,
complexity: method.complexity,
sourceLocation: {
file: method.filePath,
startLine: method.startLine,
endLine: method.endLine,
startColumn: 0,
endColumn: 0
},
businessPurpose: this.inferBusinessPurpose(method.name),
sideEffects: this.analyzeSideEffects(method)
}));
}
/**
* Extract concrete function information
*/
extractConcreteFunctions(analysisResult) {
return analysisResult.functions.map(func => ({
name: func.name,
signature: this.buildMethodSignature(func),
parameters: this.extractConcreteParameters(func.parameters),
returnType: func.returnType || 'void',
isAsync: func.isAsync,
isExported: func.isExported,
complexity: func.complexity,
sourceLocation: {
file: func.filePath,
startLine: func.startLine,
endLine: func.endLine,
startColumn: 0,
endColumn: 0
},
businessPurpose: this.inferBusinessPurpose(func.name),
dependencies: func.dependencies
}));
}
/**
* Extract concrete dependencies
*/
extractConcreteDependencies(analysisResult) {
const dependencies = [];
// Process imports to create dependencies
for (const importDecl of analysisResult.imports) {
const dependency = {
name: importDecl.source,
type: this.classifyDependencyType(importDecl.source),
usage: 'import',
usedSymbols: importDecl.imports.map(imp => imp.name),
businessJustification: this.inferDependencyJustification(importDecl.source)
};
dependencies.push(dependency);
}
// Add string dependencies
for (const dep of analysisResult.dependencies) {
if (!dependencies.some(d => d.name === dep)) {
dependencies.push({
name: dep,
type: this.classifyDependencyType(dep),
usage: 'require',
usedSymbols: [],
businessJustification: this.inferDependencyJustification(dep)
});
}
}
return dependencies;
}
/**
* Extract concrete exports
*/
extractConcreteExports(analysisResult) {
return analysisResult.exports.map(exp => ({
name: exp.name,
type: exp.type,
isDefault: exp.isDefault,
businessValue: this.inferBusinessValue(exp.name, exp.type)
}));
}
/**
* Extract concrete imports
*/
extractConcreteImports(analysisResult) {
return analysisResult.imports.map(imp => ({
source: imp.source,
imports: imp.imports.map(spec => ({
name: spec.name,
...(spec.alias ? { alias: spec.alias } : {}),
isDefault: spec.isDefault,
isNamespace: spec.isNamespace
})),
businessReason: this.inferImportReason(imp.source)
}));
}
/**
* Detect architectural patterns
*/
detectArchitecturalPatterns(analysisResult) {
const patterns = [];
// Analyze class and function names for patterns
const allNames = [
...analysisResult.classes.map(cls => cls.name),
...analysisResult.functions.map(func => func.name)
];
// Factory Pattern
const factoryEvidence = allNames.filter(name => name.toLowerCase().includes('factory') ||
name.toLowerCase().includes('create'));
if (factoryEvidence.length > 0) {
patterns.push({
name: 'Factory Pattern',
confidence: Math.min(factoryEvidence.length * 0.3, 1.0),
evidence: factoryEvidence,
businessBenefit: 'Provides flexible object creation and reduces coupling'
});
}
// Observer Pattern
const observerEvidence = allNames.filter(name => name.toLowerCase().includes('observer') ||
name.toLowerCase().includes('listener') ||
name.toLowerCase().includes('subscribe') ||
name.toLowerCase().includes('notify'));
if (observerEvidence.length > 0) {
patterns.push({
name: 'Observer Pattern',
confidence: Math.min(observerEvidence.length * 0.25, 1.0),
evidence: observerEvidence,
businessBenefit: 'Enables loose coupling and event-driven architecture'
});
}
// Repository Pattern
const repositoryEvidence = allNames.filter(name => name.toLowerCase().includes('repository') ||
name.toLowerCase().includes('dao') ||
name.toLowerCase().includes('store'));
if (repositoryEvidence.length > 0) {
patterns.push({
name: 'Repository Pattern',
confidence: Math.min(repositoryEvidence.length * 0.4, 1.0),
evidence: repositoryEvidence,
businessBenefit: 'Abstracts data access and improves testability'
});
}
// Service Pattern
const serviceEvidence = allNames.filter(name => name.toLowerCase().includes('service') ||
name.toLowerCase().includes('manager') ||
name.toLowerCase().includes('handler'));
if (serviceEvidence.length > 0) {
patterns.push({
name: 'Service Pattern',
confidence: Math.min(serviceEvidence.length * 0.2, 1.0),
evidence: serviceEvidence,
businessBenefit: 'Encapsulates business logic and promotes reusability'
});
}
return patterns;
}
/**
* Detect business domain indicators
*/
detectBusinessDomainIndicators(analysisResult) {
const indicators = [];
// Collect all identifiers
const allIdentifiers = [
...analysisResult.classes.map(cls => cls.name),
...analysisResult.functions.map(func => func.name),
...analysisResult.classes.flatMap(cls => cls.methods.map(m => m.name)),
...analysisResult.classes.flatMap(cls => cls.properties.map(p => p.name))
].map(name => name.toLowerCase());
// Domain keyword mappings
const domainKeywords = {
'E-commerce': {
keywords: ['order', 'cart', 'payment', 'product', 'customer', 'checkout', 'inventory', 'shipping'],
weight: 0.8
},
'Finance': {
keywords: ['account', 'transaction', 'balance', 'payment', 'invoice', 'billing', 'credit', 'debit'],
weight: 0.9
},
'Healthcare': {
keywords: ['patient', 'medical', 'diagnosis', 'treatment', 'doctor', 'appointment', 'prescription'],
weight: 0.9
},
'Education': {
keywords: ['student', 'course', 'grade', 'assignment', 'teacher', 'class', 'enrollment'],
weight: 0.8
},
'Authentication': {
keywords: ['user', 'login', 'auth', 'token', 'session', 'permission', 'role', 'access'],
weight: 0.7
},
'Content Management': {
keywords: ['article', 'post', 'content', 'publish', 'editor', 'media', 'page'],
weight: 0.7
},
'Communication': {
keywords: ['message', 'chat', 'notification', 'email', 'sms', 'channel', 'conversation'],
weight: 0.8
}
};
// Analyze each domain
for (const [domain, config] of Object.entries(domainKeywords)) {
const matches = config.keywords.filter(keyword => allIdentifiers.some(identifier => identifier.includes(keyword)));
if (matches.length >= 2) {
const confidence = Math.min((matches.length / config.keywords.length) * config.weight, 1.0);
indicators.push({
domain,
confidence,
evidence: matches,
keywords: config.keywords
});
}
}
return indicators.sort((a, b) => b.confidence - a.confidence);
}
/**
* Calculate concrete quality metrics
*/
calculateConcreteQualityMetrics(analysisResult) {
const totalComplexity = analysisResult.functions.reduce((sum, func) => sum + func.complexity, 0);
// Calculate average complexity for quality assessment
const avgComplexity = analysisResult.functions.length > 0 ? totalComplexity / analysisResult.functions.length : 0;
logger_1.default.debug('Calculated average function complexity', { avgComplexity });
return {
complexity: {
cyclomatic: analysisResult.complexity.cyclomaticComplexity,
cognitive: analysisResult.complexity.cognitiveComplexity,
maintainability: analysisResult.complexity.maintainabilityIndex
},
testability: {
score: this.calculateTestabilityScore(analysisResult),
factors: this.getTestabilityFactors(analysisResult)
},
reusability: {
score: this.calculateReusabilityScore(analysisResult),
factors: this.getReusabilityFactors(analysisResult)
},
documentation: {
coverage: this.calculateDocumentationCoverage(analysisResult),
quality: this.calculateDocumentationQuality(analysisResult)
}
};
}
// Helper methods
buildFullyQualifiedName(className, filePath) {
const moduleName = path.basename(filePath, path.extname(filePath));
return `${moduleName}.${className}`;
}
buildMethodSignature(method) {
const params = method.parameters.map(p => `${p.name}${p.optional ? '?' : ''}: ${p.type || 'any'}`).join(', ');
return `${method.name}(${params}): ${method.returnType || 'void'}`;
}
extractConcreteParameters(parameters) {
return parameters.map(param => ({
name: param.name,
type: param.type || 'any',
isOptional: param.optional || false,
defaultValue: param.defaultValue,
businessMeaning: this.inferBusinessMeaning(param.name)
}));
}
extractConcreteProperties(cls) {
return cls.properties.map(prop => ({
name: prop.name,
type: prop.type || 'any',
visibility: prop.visibility,
isStatic: prop.isStatic,
isReadonly: prop.isReadonly,
defaultValue: prop.defaultValue || undefined,
businessPurpose: this.inferBusinessPurpose(prop.name)
}));
}
classifyDependencyType(source) {
if (source.startsWith('.') || source.startsWith('/')) {
return 'internal';
}
if (['fs', 'path', 'http', 'crypto', 'util'].includes(source)) {
return 'builtin';
}
return 'external';
}
detectClassDesignPatterns(cls) {
const patterns = [];
const className = cls.name.toLowerCase();
if (className.includes('factory'))
patterns.push('Factory');
if (className.includes('builder'))
patterns.push('Builder');
if (className.includes('singleton'))
patterns.push('Singleton');
if (className.includes('observer'))
patterns.push('Observer');
if (className.includes('strategy'))
patterns.push('Strategy');
if (className.includes('adapter'))
patterns.push('Adapter');
return patterns;
}
analyzeSideEffects(method) {
const sideEffects = [];
const methodName = method.name.toLowerCase();
if (methodName.includes('save') || methodName.includes('create') || methodName.includes('update')) {
sideEffects.push('Database modification');
}
if (methodName.includes('delete') || methodName.includes('remove')) {
sideEffects.push('Data deletion');
}
if (methodName.includes('send') || methodName.includes('notify')) {
sideEffects.push('External communication');
}
if (methodName.includes('log') || methodName.includes('track')) {
sideEffects.push('Logging/tracking');
}
return sideEffects;
}
inferBusinessPurpose(name) {
const lowerName = name.toLowerCase();
if (lowerName.includes('user'))
return 'User management and authentication';
if (lowerName.includes('order'))
return 'Order processing and management';
if (lowerName.includes('payment'))
return 'Payment processing and billing';
if (lowerName.includes('product'))
return 'Product catalog and inventory';
if (lowerName.includes('customer'))
return 'Customer relationship management';
if (lowerName.includes('auth'))
return 'Authentication and authorization';
if (lowerName.includes('notification'))
return 'Communication and notifications';
if (lowerName.includes('report'))
return 'Reporting and analytics';
return 'Core business functionality';
}
inferBusinessMeaning(paramName) {
const lowerName = paramName.toLowerCase();
if (lowerName.includes('id'))
return 'Unique identifier';
if (lowerName.includes('name'))
return 'Display name or title';
if (lowerName.includes('email'))
return 'Email address for communication';
if (lowerName.includes('password'))
return 'Authentication credential';
if (lowerName.includes('amount'))
return 'Monetary or quantity value';
if (lowerName.includes('date'))
return 'Temporal information';
if (lowerName.includes('status'))
return 'State or condition indicator';
return 'Business data parameter';
}
inferDependencyJustification(source) {
if (source.includes('express'))
return 'Web server framework for API endpoints';
if (source.includes('react'))
return 'User interface component library';
if (source.includes('lodash'))
return 'Utility functions for data manipulation';
if (source.includes('axios'))
return 'HTTP client for external API communication';
if (source.includes('mongoose'))
return 'Database ORM for data persistence';
if (source.includes('jwt'))
return 'Authentication token management';
return 'Supporting library for business functionality';
}
inferBusinessValue(name, type) {
if (type === 'function' && name.toLowerCase().includes('api')) {
return 'Provides external interface for business operations';
}
if (type === 'class' && name.toLowerCase().includes('service')) {
return 'Encapsulates core business logic';
}
if (type === 'class' && name.toLowerCase().includes('model')) {
return 'Represents business domain entity';
}
return 'Supports business functionality and operations';
}
inferImportReason(source) {
return this.inferDependencyJustification(source);
}
calculateTestabilityScore(analysisResult) {
let score = 50; // Base score
// Bonus for pure functions (no side effects)
const pureFunctions = analysisResult.functions.filter(func => func.parameters.length > 0 && func.returnType && func.returnType !== 'void');
score += (pureFunctions.length / analysisResult.functions.length) * 30;
// Bonus for dependency injection patterns
const diPatterns = analysisResult.classes.filter(cls => cls.methods.some(method => method.parameters.length > 0));
score += (diPatterns.length / Math.max(analysisResult.classes.length, 1)) * 20;
return Math.min(score, 100);
}
getTestabilityFactors(analysisResult) {
const factors = [];
if (analysisResult.functions.some(f => f.parameters.length > 0)) {
factors.push('Functions accept parameters');
}
if (analysisResult.functions.some(f => f.returnType && f.returnType !== 'void')) {
factors.push('Functions return values');
}
if (analysisResult.classes.some(c => c.methods.length > 0)) {
factors.push('Classes have methods');
}
return factors;
}
calculateReusabilityScore(analysisResult) {
let score = 40; // Base score
// Bonus for exported functions/classes
const exportedItems = analysisResult.functions.filter(f => f.isExported).length +
analysisResult.classes.filter(c => c.isExported).length;
const totalItems = analysisResult.functions.length + analysisResult.classes.length;
if (totalItems > 0) {
score += (exportedItems / totalItems) * 40;
}
// Bonus for generic/utility functions
const utilityFunctions = analysisResult.functions.filter(func => func.name.toLowerCase().includes('util') ||
func.name.toLowerCase().includes('helper') ||
func.parameters.length > 0);
score += (utilityFunctions.length / Math.max(analysisResult.functions.length, 1)) * 20;
return Math.min(score, 100);
}
getReusabilityFactors(analysisResult) {
const factors = [];
if (analysisResult.functions.some(f => f.isExported)) {
factors.push('Exported functions available for reuse');
}
if (analysisResult.classes.some(c => c.isExported)) {
factors.push('Exported classes available for reuse');
}
if (analysisResult.functions.some(f => f.parameters.length > 0)) {
factors.push('Parameterized functions support different use cases');
}
return factors;
}
calculateDocumentationCoverage(analysisResult) {
// Since we don't have documentation in the types, use naming conventions
const totalItems = analysisResult.functions.length + analysisResult.classes.length;
if (totalItems === 0)
return 0;
const wellNamedItems = [
...analysisResult.functions.filter(f => f.name.length > 3 && !f.name.startsWith('_')),
...analysisResult.classes.filter(c => c.name.length > 3 && !c.name.startsWith('_'))
];
return Math.round((wellNamedItems.length / totalItems) * 100);
}
calculateDocumentationQuality(analysisResult) {
// Use meaningful naming as a proxy for documentation quality
const meaningfulNames = [
...analysisResult.functions.filter(f => f.name.includes('get') || f.name.includes('set') ||
f.name.includes('create') || f.name.includes('update') ||
f.name.includes('delete') || f.name.includes('find')),
...analysisResult.classes.filter(c => c.name.endsWith('Service') || c.name.endsWith('Manager') ||
c.name.endsWith('Controller') || c.name.endsWith('Repository'))
];
const totalItems = analysisResult.functions.length + analysisResult.classes.length;
return totalItems > 0 ? Math.round((meaningfulNames.length / totalItems) * 100) : 0;
}
}
exports.ConcreteInformationExtractor = ConcreteInformationExtractor;
//# sourceMappingURL=ConcreteInformationExtractor.js.map