sicua
Version:
A tool for analyzing project structure and dependencies
583 lines (582 loc) • 22.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataValidator = void 0;
/**
* Validation severity levels
*/
var ValidationSeverity;
(function (ValidationSeverity) {
ValidationSeverity["ERROR"] = "error";
ValidationSeverity["WARNING"] = "warning";
ValidationSeverity["INFO"] = "info";
})(ValidationSeverity || (ValidationSeverity = {}));
/**
* Validation issue categories
*/
var ValidationCategory;
(function (ValidationCategory) {
ValidationCategory["REQUIRED_FIELD"] = "required_field";
ValidationCategory["TYPE_MISMATCH"] = "type_mismatch";
ValidationCategory["FORMAT_INVALID"] = "format_invalid";
ValidationCategory["DATA_QUALITY"] = "data_quality";
ValidationCategory["CONSISTENCY"] = "consistency";
ValidationCategory["COMPLETENESS"] = "completeness";
})(ValidationCategory || (ValidationCategory = {}));
/**
* Utility class for validating extracted function data
*/
class DataValidator {
constructor(config = {}) {
this.config = {
strictMode: false,
allowEmptyBodies: true,
allowUnknownTypes: true,
maxParameterCount: 20,
maxBodyLength: 50000,
requireReturnType: false,
validateTypeNames: true,
checkNameConventions: true,
...config,
};
}
/**
* Validates a single function data object
* @param functionData The function data to validate
* @returns Validation result
*/
validateFunctionData(functionData) {
const issues = [];
try {
// Validate required fields
this.validateRequiredFields(functionData, issues);
// Validate field types
this.validateFieldTypes(functionData, issues);
// Validate data formats
this.validateDataFormats(functionData, issues);
// Validate data quality
this.validateDataQuality(functionData, issues);
// Validate consistency
this.validateConsistency(functionData, issues);
// Calculate scores
const errors = issues.filter((issue) => issue.severity === ValidationSeverity.ERROR).length;
const warnings = issues.filter((issue) => issue.severity === ValidationSeverity.WARNING).length;
const score = this.calculateQualityScore(functionData, issues);
return {
isValid: errors === 0,
issues,
score,
warnings,
errors,
};
}
catch (error) {
return {
isValid: false,
issues: [
{
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "validation",
message: `Validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
value: functionData,
},
],
score: 0,
warnings: 0,
errors: 1,
};
}
}
/**
* Validates a batch of function data objects
* @param functionDataList Array of function data to validate
* @returns Batch validation result
*/
validateBatch(functionDataList) {
const functionResults = new Map();
let totalScore = 0;
let validCount = 0;
const issuesByCategory = {
[ValidationCategory.REQUIRED_FIELD]: 0,
[ValidationCategory.TYPE_MISMATCH]: 0,
[ValidationCategory.FORMAT_INVALID]: 0,
[ValidationCategory.DATA_QUALITY]: 0,
[ValidationCategory.CONSISTENCY]: 0,
[ValidationCategory.COMPLETENESS]: 0,
};
const issuesBySeverity = {
[ValidationSeverity.ERROR]: 0,
[ValidationSeverity.WARNING]: 0,
[ValidationSeverity.INFO]: 0,
};
functionDataList.forEach((functionData, index) => {
const key = `${functionData.componentName}.${functionData.functionName}` ||
`function_${index}`;
const result = this.validateFunctionData(functionData);
functionResults.set(key, result);
totalScore += result.score;
if (result.isValid) {
validCount++;
}
// Aggregate issue counts
result.issues.forEach((issue) => {
issuesByCategory[issue.category]++;
issuesBySeverity[issue.severity]++;
});
});
return {
totalFunctions: functionDataList.length,
validFunctions: validCount,
invalidFunctions: functionDataList.length - validCount,
overallScore: functionDataList.length > 0 ? totalScore / functionDataList.length : 0,
issuesByCategory,
issuesBySeverity,
functionResults,
};
}
/**
* Validates required fields
*/
validateRequiredFields(functionData, issues) {
const requiredFields = [
"componentName",
"functionName",
"params",
"returnType",
"body",
"dependencies",
"calledFunctions",
"isAsync",
];
requiredFields.forEach((field) => {
const value = functionData[field];
if (value === undefined || value === null) {
issues.push({
category: ValidationCategory.REQUIRED_FIELD,
severity: ValidationSeverity.ERROR,
field,
message: `Required field '${field}' is missing`,
value,
suggestion: `Ensure '${field}' is properly extracted during analysis`,
});
}
});
}
/**
* Validates field types
*/
validateFieldTypes(functionData, issues) {
// Component name validation
if (typeof functionData.componentName !== "string") {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "componentName",
message: "Component name must be a string",
value: functionData.componentName,
suggestion: "Check component name extraction logic",
});
}
// Function name validation
if (typeof functionData.functionName !== "string") {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "functionName",
message: "Function name must be a string",
value: functionData.functionName,
suggestion: "Check function name resolution logic",
});
}
// Parameters validation
if (!Array.isArray(functionData.params)) {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "params",
message: "Parameters must be an array",
value: functionData.params,
suggestion: "Ensure parameter extraction returns an array",
});
}
else {
functionData.params.forEach((param, index) => {
if (typeof param !== "string") {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.WARNING,
field: `params[${index}]`,
message: `Parameter at index ${index} must be a string`,
value: param,
suggestion: "Check parameter parsing logic",
});
}
});
}
// Return type validation
if (typeof functionData.returnType !== "string") {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "returnType",
message: "Return type must be a string",
value: functionData.returnType,
suggestion: "Check return type resolution logic",
});
}
// Body validation
if (typeof functionData.body !== "string") {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "body",
message: "Function body must be a string",
value: functionData.body,
suggestion: "Check body extraction logic",
});
}
// Dependencies validation
if (!Array.isArray(functionData.dependencies)) {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "dependencies",
message: "Dependencies must be an array",
value: functionData.dependencies,
suggestion: "Ensure dependency extraction returns an array",
});
}
// Called functions validation
if (!Array.isArray(functionData.calledFunctions)) {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "calledFunctions",
message: "Called functions must be an array",
value: functionData.calledFunctions,
suggestion: "Ensure called function extraction returns an array",
});
}
// isAsync validation
if (typeof functionData.isAsync !== "boolean") {
issues.push({
category: ValidationCategory.TYPE_MISMATCH,
severity: ValidationSeverity.ERROR,
field: "isAsync",
message: "isAsync must be a boolean",
value: functionData.isAsync,
suggestion: "Check async detection logic",
});
}
}
/**
* Validates data formats
*/
validateDataFormats(functionData, issues) {
// Component name format
if (functionData.componentName &&
typeof functionData.componentName === "string") {
if (functionData.componentName.trim() === "") {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.WARNING,
field: "componentName",
message: "Component name is empty",
value: functionData.componentName,
suggestion: "Ensure component name is properly extracted",
});
}
}
// Function name format
if (functionData.functionName &&
typeof functionData.functionName === "string") {
if (functionData.functionName.trim() === "") {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.ERROR,
field: "functionName",
message: "Function name is empty",
value: functionData.functionName,
});
}
if (this.config.checkNameConventions &&
!this.isValidJavaScriptIdentifier(functionData.functionName)) {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.WARNING,
field: "functionName",
message: "Function name is not a valid JavaScript identifier",
value: functionData.functionName,
suggestion: "Consider normalizing function names",
});
}
}
// Return type format
if (functionData.returnType &&
typeof functionData.returnType === "string") {
if (this.config.requireReturnType &&
functionData.returnType === "unknown") {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.WARNING,
field: "returnType",
message: "Return type is unknown",
value: functionData.returnType,
suggestion: "Improve type resolution or enable type checker",
});
}
if (!this.config.allowUnknownTypes &&
functionData.returnType === "unknown") {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.ERROR,
field: "returnType",
message: "Unknown return types not allowed in strict mode",
value: functionData.returnType,
});
}
}
// Body format
if (functionData.body && typeof functionData.body === "string") {
if (!this.config.allowEmptyBodies && functionData.body.trim() === "") {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.WARNING,
field: "body",
message: "Function body is empty",
value: functionData.body,
suggestion: "Check if empty bodies should be excluded",
});
}
if (functionData.body.length > this.config.maxBodyLength) {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.WARNING,
field: "body",
message: `Function body exceeds maximum length (${functionData.body.length} > ${this.config.maxBodyLength})`,
value: functionData.body.length,
suggestion: "Consider splitting large functions or increasing limit",
});
}
}
// Parameters format
if (Array.isArray(functionData.params)) {
if (functionData.params.length > this.config.maxParameterCount) {
issues.push({
category: ValidationCategory.FORMAT_INVALID,
severity: ValidationSeverity.WARNING,
field: "params",
message: `Too many parameters (${functionData.params.length} > ${this.config.maxParameterCount})`,
value: functionData.params.length,
suggestion: "Consider refactoring functions with many parameters",
});
}
}
}
/**
* Validates data quality
*/
validateDataQuality(functionData, issues) {
// Check for placeholder/fallback values
const placeholderPatterns = [
"Anonymous Function",
"Unknown Function",
"ErrorFunction",
"Fallback",
"DefaultExport",
"// Error parsing",
];
placeholderPatterns.forEach((pattern) => {
if (functionData.functionName?.includes(pattern)) {
issues.push({
category: ValidationCategory.DATA_QUALITY,
severity: ValidationSeverity.WARNING,
field: "functionName",
message: `Function name appears to be a placeholder: ${pattern}`,
value: functionData.functionName,
suggestion: "Improve function name resolution",
});
}
if (functionData.body?.includes(pattern)) {
issues.push({
category: ValidationCategory.DATA_QUALITY,
severity: ValidationSeverity.INFO,
field: "body",
message: `Function body contains placeholder text: ${pattern}`,
value: pattern,
suggestion: "Check body extraction logic",
});
}
});
// Check for suspicious patterns
if (functionData.dependencies &&
functionData.dependencies.length === 0 &&
functionData.calledFunctions &&
functionData.calledFunctions.length === 0 &&
functionData.body &&
functionData.body.length > 100) {
issues.push({
category: ValidationCategory.DATA_QUALITY,
severity: ValidationSeverity.INFO,
field: "dependencies",
message: "Large function with no detected dependencies or called functions",
value: functionData.body.length,
suggestion: "Verify dependency and function call extraction",
});
}
}
/**
* Validates internal consistency
*/
validateConsistency(functionData, issues) {
// Async consistency
if (functionData.isAsync &&
functionData.returnType &&
!functionData.returnType.toLowerCase().includes("promise") &&
functionData.returnType !== "unknown") {
issues.push({
category: ValidationCategory.CONSISTENCY,
severity: ValidationSeverity.WARNING,
field: "isAsync",
message: "Function marked as async but return type does not indicate Promise",
value: {
isAsync: functionData.isAsync,
returnType: functionData.returnType,
},
suggestion: "Verify async detection logic",
});
}
// Body and parameters consistency
if (Array.isArray(functionData.params) && functionData.body) {
const bodyText = functionData.body.toLowerCase();
functionData.params.forEach((param) => {
const paramName = this.extractParameterName(param);
if (paramName &&
paramName.length > 2 &&
!bodyText.includes(paramName.toLowerCase())) {
issues.push({
category: ValidationCategory.CONSISTENCY,
severity: ValidationSeverity.INFO,
field: "params",
message: `Parameter '${paramName}' not found in function body`,
value: paramName,
suggestion: "Parameter might be unused or body extraction incomplete",
});
}
});
}
}
/**
* Calculates quality score (0-100)
*/
calculateQualityScore(functionData, issues) {
let score = 100;
// Deduct points for issues
issues.forEach((issue) => {
switch (issue.severity) {
case ValidationSeverity.ERROR:
score -= 20;
break;
case ValidationSeverity.WARNING:
score -= 10;
break;
case ValidationSeverity.INFO:
score -= 2;
break;
}
});
// Bonus points for completeness
if (functionData.returnType && functionData.returnType !== "unknown") {
score += 5;
}
if (Array.isArray(functionData.dependencies) &&
functionData.dependencies.length > 0) {
score += 3;
}
if (Array.isArray(functionData.calledFunctions) &&
functionData.calledFunctions.length > 0) {
score += 3;
}
if (functionData.body &&
functionData.body.trim().length > 0 &&
!functionData.body.includes("Error parsing")) {
score += 5;
}
return Math.max(0, Math.min(100, score));
}
/**
* Checks if a string is a valid JavaScript identifier
*/
isValidJavaScriptIdentifier(name) {
const identifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
return identifierRegex.test(name) && !this.isReservedWord(name);
}
/**
* Checks if a string is a JavaScript reserved word
*/
isReservedWord(word) {
const reservedWords = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"return",
"super",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"yield",
];
return reservedWords.includes(word.toLowerCase());
}
/**
* Extracts parameter name from parameter string
*/
extractParameterName(param) {
// Handle destructuring, types, defaults, etc.
const cleaned = param.trim();
// Simple case: just identifier
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(cleaned)) {
return cleaned;
}
// Extract from destructuring or typed parameters
const match = cleaned.match(/^(?:\.\.\.)?([a-zA-Z_$][a-zA-Z0-9_$]*)/);
return match ? match[1] : "";
}
/**
* Updates validation configuration
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
/**
* Gets current validation configuration
*/
getConfig() {
return { ...this.config };
}
}
exports.DataValidator = DataValidator;