codeguardian-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +348 -0
- package/dist/agent/agentTools.d.ts +26 -0
- package/dist/agent/agentTools.d.ts.map +1 -0
- package/dist/agent/agentTools.js +699 -0
- package/dist/agent/agentTools.js.map +1 -0
- package/dist/agent/autoValidator.d.ts +110 -0
- package/dist/agent/autoValidator.d.ts.map +1 -0
- package/dist/agent/autoValidator.js +964 -0
- package/dist/agent/autoValidator.js.map +1 -0
- package/dist/agent/fileWatcher.d.ts +28 -0
- package/dist/agent/fileWatcher.d.ts.map +1 -0
- package/dist/agent/fileWatcher.js +88 -0
- package/dist/agent/fileWatcher.js.map +1 -0
- package/dist/agent/guardianPersistence.d.ts +98 -0
- package/dist/agent/guardianPersistence.d.ts.map +1 -0
- package/dist/agent/guardianPersistence.js +296 -0
- package/dist/agent/guardianPersistence.js.map +1 -0
- package/dist/agent/mcpNotifications.d.ts +38 -0
- package/dist/agent/mcpNotifications.d.ts.map +1 -0
- package/dist/agent/mcpNotifications.js +81 -0
- package/dist/agent/mcpNotifications.js.map +1 -0
- package/dist/analyzers/aiPatterns.d.ts +16 -0
- package/dist/analyzers/aiPatterns.d.ts.map +1 -0
- package/dist/analyzers/aiPatterns.js +103 -0
- package/dist/analyzers/aiPatterns.js.map +1 -0
- package/dist/analyzers/antiPatterns.d.ts +60 -0
- package/dist/analyzers/antiPatterns.d.ts.map +1 -0
- package/dist/analyzers/antiPatterns.js +198 -0
- package/dist/analyzers/antiPatterns.js.map +1 -0
- package/dist/analyzers/builtinTypes.d.ts +18 -0
- package/dist/analyzers/builtinTypes.d.ts.map +1 -0
- package/dist/analyzers/builtinTypes.js +1275 -0
- package/dist/analyzers/builtinTypes.js.map +1 -0
- package/dist/analyzers/complexity.d.ts +14 -0
- package/dist/analyzers/complexity.d.ts.map +1 -0
- package/dist/analyzers/complexity.js +610 -0
- package/dist/analyzers/complexity.js.map +1 -0
- package/dist/analyzers/findingVerifier.d.ts +59 -0
- package/dist/analyzers/findingVerifier.d.ts.map +1 -0
- package/dist/analyzers/findingVerifier.js +1169 -0
- package/dist/analyzers/findingVerifier.js.map +1 -0
- package/dist/analyzers/impactAnalyzer.d.ts +53 -0
- package/dist/analyzers/impactAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/impactAnalyzer.js +152 -0
- package/dist/analyzers/impactAnalyzer.js.map +1 -0
- package/dist/analyzers/languageDetector.d.ts +48 -0
- package/dist/analyzers/languageDetector.d.ts.map +1 -0
- package/dist/analyzers/languageDetector.js +404 -0
- package/dist/analyzers/languageDetector.js.map +1 -0
- package/dist/analyzers/parsers/incrementalParser.d.ts +53 -0
- package/dist/analyzers/parsers/incrementalParser.d.ts.map +1 -0
- package/dist/analyzers/parsers/incrementalParser.js +193 -0
- package/dist/analyzers/parsers/incrementalParser.js.map +1 -0
- package/dist/analyzers/parsers/scopeResolver.d.ts +92 -0
- package/dist/analyzers/parsers/scopeResolver.d.ts.map +1 -0
- package/dist/analyzers/parsers/scopeResolver.js +324 -0
- package/dist/analyzers/parsers/scopeResolver.js.map +1 -0
- package/dist/analyzers/parsers/semanticIndex.d.ts +127 -0
- package/dist/analyzers/parsers/semanticIndex.d.ts.map +1 -0
- package/dist/analyzers/parsers/semanticIndex.js +429 -0
- package/dist/analyzers/parsers/semanticIndex.js.map +1 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.d.ts +42 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.js +233 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.js.map +1 -0
- package/dist/analyzers/parsers/treeSitterParser.d.ts +76 -0
- package/dist/analyzers/parsers/treeSitterParser.d.ts.map +1 -0
- package/dist/analyzers/parsers/treeSitterParser.js +709 -0
- package/dist/analyzers/parsers/treeSitterParser.js.map +1 -0
- package/dist/analyzers/relevanceScorer.d.ts +43 -0
- package/dist/analyzers/relevanceScorer.d.ts.map +1 -0
- package/dist/analyzers/relevanceScorer.js +200 -0
- package/dist/analyzers/relevanceScorer.js.map +1 -0
- package/dist/analyzers/standardLibrary.d.ts +22 -0
- package/dist/analyzers/standardLibrary.d.ts.map +1 -0
- package/dist/analyzers/standardLibrary.js +211 -0
- package/dist/analyzers/standardLibrary.js.map +1 -0
- package/dist/analyzers/symbolGraph.d.ts +30 -0
- package/dist/analyzers/symbolGraph.d.ts.map +1 -0
- package/dist/analyzers/symbolGraph.js +380 -0
- package/dist/analyzers/symbolGraph.js.map +1 -0
- package/dist/analyzers/symbolTable.d.ts +18 -0
- package/dist/analyzers/symbolTable.d.ts.map +1 -0
- package/dist/analyzers/symbolTable.js +176 -0
- package/dist/analyzers/symbolTable.js.map +1 -0
- package/dist/analyzers/typeChecker.d.ts +13 -0
- package/dist/analyzers/typeChecker.d.ts.map +1 -0
- package/dist/analyzers/typeChecker.js +580 -0
- package/dist/analyzers/typeChecker.js.map +1 -0
- package/dist/analyzers/usagePatterns.d.ts +42 -0
- package/dist/analyzers/usagePatterns.d.ts.map +1 -0
- package/dist/analyzers/usagePatterns.js +75 -0
- package/dist/analyzers/usagePatterns.js.map +1 -0
- package/dist/api-contract/context/backend.d.ts +19 -0
- package/dist/api-contract/context/backend.d.ts.map +1 -0
- package/dist/api-contract/context/backend.js +64 -0
- package/dist/api-contract/context/backend.js.map +1 -0
- package/dist/api-contract/context/contract.d.ts +34 -0
- package/dist/api-contract/context/contract.d.ts.map +1 -0
- package/dist/api-contract/context/contract.js +306 -0
- package/dist/api-contract/context/contract.js.map +1 -0
- package/dist/api-contract/context/frontend.d.ts +19 -0
- package/dist/api-contract/context/frontend.d.ts.map +1 -0
- package/dist/api-contract/context/frontend.js +64 -0
- package/dist/api-contract/context/frontend.js.map +1 -0
- package/dist/api-contract/detector.d.ts +28 -0
- package/dist/api-contract/detector.d.ts.map +1 -0
- package/dist/api-contract/detector.js +393 -0
- package/dist/api-contract/detector.js.map +1 -0
- package/dist/api-contract/extractors/python.d.ts +32 -0
- package/dist/api-contract/extractors/python.d.ts.map +1 -0
- package/dist/api-contract/extractors/python.js +521 -0
- package/dist/api-contract/extractors/python.js.map +1 -0
- package/dist/api-contract/extractors/pythonAstUtils.d.ts +44 -0
- package/dist/api-contract/extractors/pythonAstUtils.d.ts.map +1 -0
- package/dist/api-contract/extractors/pythonAstUtils.js +489 -0
- package/dist/api-contract/extractors/pythonAstUtils.js.map +1 -0
- package/dist/api-contract/extractors/tsAstUtils.d.ts +47 -0
- package/dist/api-contract/extractors/tsAstUtils.d.ts.map +1 -0
- package/dist/api-contract/extractors/tsAstUtils.js +173 -0
- package/dist/api-contract/extractors/tsAstUtils.js.map +1 -0
- package/dist/api-contract/extractors/typescript.d.ts +32 -0
- package/dist/api-contract/extractors/typescript.d.ts.map +1 -0
- package/dist/api-contract/extractors/typescript.js +666 -0
- package/dist/api-contract/extractors/typescript.js.map +1 -0
- package/dist/api-contract/index.d.ts +104 -0
- package/dist/api-contract/index.d.ts.map +1 -0
- package/dist/api-contract/index.js +232 -0
- package/dist/api-contract/index.js.map +1 -0
- package/dist/api-contract/types.d.ts +151 -0
- package/dist/api-contract/types.d.ts.map +1 -0
- package/dist/api-contract/types.js +19 -0
- package/dist/api-contract/types.js.map +1 -0
- package/dist/api-contract/validators/endpoint.d.ts +21 -0
- package/dist/api-contract/validators/endpoint.d.ts.map +1 -0
- package/dist/api-contract/validators/endpoint.js +224 -0
- package/dist/api-contract/validators/endpoint.js.map +1 -0
- package/dist/api-contract/validators/index.d.ts +40 -0
- package/dist/api-contract/validators/index.d.ts.map +1 -0
- package/dist/api-contract/validators/index.js +875 -0
- package/dist/api-contract/validators/index.js.map +1 -0
- package/dist/api-contract/validators/parameter.d.ts +17 -0
- package/dist/api-contract/validators/parameter.d.ts.map +1 -0
- package/dist/api-contract/validators/parameter.js +250 -0
- package/dist/api-contract/validators/parameter.js.map +1 -0
- package/dist/api-contract/validators/type.d.ts +38 -0
- package/dist/api-contract/validators/type.d.ts.map +1 -0
- package/dist/api-contract/validators/type.js +244 -0
- package/dist/api-contract/validators/type.js.map +1 -0
- package/dist/context/apiContract/complexTypeSupport.d.ts +83 -0
- package/dist/context/apiContract/complexTypeSupport.d.ts.map +1 -0
- package/dist/context/apiContract/complexTypeSupport.js +665 -0
- package/dist/context/apiContract/complexTypeSupport.js.map +1 -0
- package/dist/context/apiContract/graphqlSupport.d.ts +105 -0
- package/dist/context/apiContract/graphqlSupport.d.ts.map +1 -0
- package/dist/context/apiContract/graphqlSupport.js +671 -0
- package/dist/context/apiContract/graphqlSupport.js.map +1 -0
- package/dist/context/apiContract/index.d.ts +14 -0
- package/dist/context/apiContract/index.d.ts.map +1 -0
- package/dist/context/apiContract/index.js +17 -0
- package/dist/context/apiContract/index.js.map +1 -0
- package/dist/context/apiContract/webSocketSupport.d.ts +104 -0
- package/dist/context/apiContract/webSocketSupport.d.ts.map +1 -0
- package/dist/context/apiContract/webSocketSupport.js +465 -0
- package/dist/context/apiContract/webSocketSupport.js.map +1 -0
- package/dist/context/apiContractContext.d.ts +15 -0
- package/dist/context/apiContractContext.d.ts.map +1 -0
- package/dist/context/apiContractContext.js +979 -0
- package/dist/context/apiContractContext.js.map +1 -0
- package/dist/context/apiContractExtraction.d.ts +52 -0
- package/dist/context/apiContractExtraction.d.ts.map +1 -0
- package/dist/context/apiContractExtraction.js +438 -0
- package/dist/context/apiContractExtraction.js.map +1 -0
- package/dist/context/contextLineage.d.ts +79 -0
- package/dist/context/contextLineage.d.ts.map +1 -0
- package/dist/context/contextLineage.js +259 -0
- package/dist/context/contextLineage.js.map +1 -0
- package/dist/context/contextOrchestrator.d.ts +57 -0
- package/dist/context/contextOrchestrator.d.ts.map +1 -0
- package/dist/context/contextOrchestrator.js +162 -0
- package/dist/context/contextOrchestrator.js.map +1 -0
- package/dist/context/intentTracker.d.ts +73 -0
- package/dist/context/intentTracker.d.ts.map +1 -0
- package/dist/context/intentTracker.js +168 -0
- package/dist/context/intentTracker.js.map +1 -0
- package/dist/context/projectContext.d.ts +219 -0
- package/dist/context/projectContext.d.ts.map +1 -0
- package/dist/context/projectContext.js +1984 -0
- package/dist/context/projectContext.js.map +1 -0
- package/dist/prompts/index.d.ts +17 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +260 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/library.d.ts +51 -0
- package/dist/prompts/library.d.ts.map +1 -0
- package/dist/prompts/library.js +65 -0
- package/dist/prompts/library.js.map +1 -0
- package/dist/prompts/templates.d.ts +44 -0
- package/dist/prompts/templates.d.ts.map +1 -0
- package/dist/prompts/templates.js +97 -0
- package/dist/prompts/templates.js.map +1 -0
- package/dist/queue/jobPersistence.d.ts +46 -0
- package/dist/queue/jobPersistence.d.ts.map +1 -0
- package/dist/queue/jobPersistence.js +158 -0
- package/dist/queue/jobPersistence.js.map +1 -0
- package/dist/queue/jobQueue.d.ts +116 -0
- package/dist/queue/jobQueue.d.ts.map +1 -0
- package/dist/queue/jobQueue.js +275 -0
- package/dist/queue/jobQueue.js.map +1 -0
- package/dist/queue/validationJob.d.ts +69 -0
- package/dist/queue/validationJob.d.ts.map +1 -0
- package/dist/queue/validationJob.js +435 -0
- package/dist/queue/validationJob.js.map +1 -0
- package/dist/resources/index.d.ts +15 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +328 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/validationReportStore.d.ts +170 -0
- package/dist/resources/validationReportStore.d.ts.map +1 -0
- package/dist/resources/validationReportStore.js +515 -0
- package/dist/resources/validationReportStore.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +102 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/asyncValidation.d.ts +19 -0
- package/dist/tools/asyncValidation.d.ts.map +1 -0
- package/dist/tools/asyncValidation.js +346 -0
- package/dist/tools/asyncValidation.js.map +1 -0
- package/dist/tools/buildContext.d.ts +17 -0
- package/dist/tools/buildContext.d.ts.map +1 -0
- package/dist/tools/buildContext.js +188 -0
- package/dist/tools/buildContext.js.map +1 -0
- package/dist/tools/getDependencyGraph.d.ts +16 -0
- package/dist/tools/getDependencyGraph.d.ts.map +1 -0
- package/dist/tools/getDependencyGraph.js +436 -0
- package/dist/tools/getDependencyGraph.js.map +1 -0
- package/dist/tools/incrementalValidation.d.ts +71 -0
- package/dist/tools/incrementalValidation.d.ts.map +1 -0
- package/dist/tools/incrementalValidation.js +203 -0
- package/dist/tools/incrementalValidation.js.map +1 -0
- package/dist/tools/index.d.ts +24 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +106 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/validateCode.d.ts +17 -0
- package/dist/tools/validateCode.d.ts.map +1 -0
- package/dist/tools/validateCode.js +368 -0
- package/dist/tools/validateCode.js.map +1 -0
- package/dist/tools/validateCodeLite.d.ts +2 -0
- package/dist/tools/validateCodeLite.d.ts.map +1 -0
- package/dist/tools/validateCodeLite.js +2 -0
- package/dist/tools/validateCodeLite.js.map +1 -0
- package/dist/tools/validation/builtins.d.ts +92 -0
- package/dist/tools/validation/builtins.d.ts.map +1 -0
- package/dist/tools/validation/builtins.js +2184 -0
- package/dist/tools/validation/builtins.js.map +1 -0
- package/dist/tools/validation/contextualNaming.d.ts +99 -0
- package/dist/tools/validation/contextualNaming.d.ts.map +1 -0
- package/dist/tools/validation/contextualNaming.js +959 -0
- package/dist/tools/validation/contextualNaming.js.map +1 -0
- package/dist/tools/validation/deadCode.d.ts +115 -0
- package/dist/tools/validation/deadCode.d.ts.map +1 -0
- package/dist/tools/validation/deadCode.js +861 -0
- package/dist/tools/validation/deadCode.js.map +1 -0
- package/dist/tools/validation/extractors/index.d.ts +131 -0
- package/dist/tools/validation/extractors/index.d.ts.map +1 -0
- package/dist/tools/validation/extractors/index.js +233 -0
- package/dist/tools/validation/extractors/index.js.map +1 -0
- package/dist/tools/validation/extractors/javascript.d.ts +73 -0
- package/dist/tools/validation/extractors/javascript.d.ts.map +1 -0
- package/dist/tools/validation/extractors/javascript.js +1841 -0
- package/dist/tools/validation/extractors/javascript.js.map +1 -0
- package/dist/tools/validation/extractors/python.d.ts +93 -0
- package/dist/tools/validation/extractors/python.d.ts.map +1 -0
- package/dist/tools/validation/extractors/python.js +799 -0
- package/dist/tools/validation/extractors/python.js.map +1 -0
- package/dist/tools/validation/manifest.d.ts +45 -0
- package/dist/tools/validation/manifest.d.ts.map +1 -0
- package/dist/tools/validation/manifest.js +719 -0
- package/dist/tools/validation/manifest.js.map +1 -0
- package/dist/tools/validation/parser.d.ts +58 -0
- package/dist/tools/validation/parser.d.ts.map +1 -0
- package/dist/tools/validation/parser.js +232 -0
- package/dist/tools/validation/parser.js.map +1 -0
- package/dist/tools/validation/registry.d.ts +15 -0
- package/dist/tools/validation/registry.d.ts.map +1 -0
- package/dist/tools/validation/registry.js +169 -0
- package/dist/tools/validation/registry.js.map +1 -0
- package/dist/tools/validation/scoring.d.ts +54 -0
- package/dist/tools/validation/scoring.d.ts.map +1 -0
- package/dist/tools/validation/scoring.js +242 -0
- package/dist/tools/validation/scoring.js.map +1 -0
- package/dist/tools/validation/types.d.ts +120 -0
- package/dist/tools/validation/types.d.ts.map +1 -0
- package/dist/tools/validation/types.js +11 -0
- package/dist/tools/validation/types.js.map +1 -0
- package/dist/tools/validation/unusedLocals.d.ts +36 -0
- package/dist/tools/validation/unusedLocals.d.ts.map +1 -0
- package/dist/tools/validation/unusedLocals.js +333 -0
- package/dist/tools/validation/unusedLocals.js.map +1 -0
- package/dist/tools/validation/validation.d.ts +98 -0
- package/dist/tools/validation/validation.d.ts.map +1 -0
- package/dist/tools/validation/validation.js +1837 -0
- package/dist/tools/validation/validation.js.map +1 -0
- package/dist/types/codeGraph.d.ts +163 -0
- package/dist/types/codeGraph.d.ts.map +1 -0
- package/dist/types/codeGraph.js +9 -0
- package/dist/types/codeGraph.js.map +1 -0
- package/dist/types/symbolGraph.d.ts +68 -0
- package/dist/types/symbolGraph.d.ts.map +1 -0
- package/dist/types/symbolGraph.js +10 -0
- package/dist/types/symbolGraph.js.map +1 -0
- package/dist/types/tools.d.ts +43 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +7 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/utils/fileFilter.d.ts +37 -0
- package/dist/utils/fileFilter.d.ts.map +1 -0
- package/dist/utils/fileFilter.js +91 -0
- package/dist/utils/fileFilter.js.map +1 -0
- package/dist/utils/gitUtils.d.ts +28 -0
- package/dist/utils/gitUtils.d.ts.map +1 -0
- package/dist/utils/gitUtils.js +81 -0
- package/dist/utils/gitUtils.js.map +1 -0
- package/dist/utils/logger.d.ts +15 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +38 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/serialization.d.ts +25 -0
- package/dist/utils/serialization.d.ts.map +1 -0
- package/dist/utils/serialization.js +53 -0
- package/dist/utils/serialization.js.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Contract Guardian - TypeScript Extractor (AST-based)
|
|
3
|
+
*
|
|
4
|
+
* Extracts API service functions and TypeScript types/interfaces using AST.
|
|
5
|
+
*
|
|
6
|
+
* @format
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from "fs/promises";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import { glob } from "glob";
|
|
11
|
+
import { logger } from "../../utils/logger.js";
|
|
12
|
+
import { getParser } from "../../tools/validation/parser.js";
|
|
13
|
+
import { extractEndpointFromArguments as extractEndpointFromArgumentsTS, extractHttpMethodFromArguments as extractHttpMethodFromArgumentsTS, findEnclosingFunctionName, getNodeText, mapToHttpMethod, } from "./tsAstUtils.js";
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Service Extraction
|
|
16
|
+
// ============================================================================
|
|
17
|
+
/**
|
|
18
|
+
* Extract all API service functions from a project
|
|
19
|
+
*/
|
|
20
|
+
export async function extractServices(projectPath) {
|
|
21
|
+
const services = [];
|
|
22
|
+
// Find service files
|
|
23
|
+
const servicePatterns = [
|
|
24
|
+
`${projectPath}/**/services/**/*.ts`,
|
|
25
|
+
`${projectPath}/**/api/**/*.ts`,
|
|
26
|
+
`${projectPath}/**/clients/**/*.ts`,
|
|
27
|
+
`${projectPath}/src/lib/api.ts`,
|
|
28
|
+
`${projectPath}/lib/api.ts`,
|
|
29
|
+
];
|
|
30
|
+
const excludePatterns = [
|
|
31
|
+
"**/node_modules/**",
|
|
32
|
+
"**/*.test.ts",
|
|
33
|
+
"**/*.spec.ts",
|
|
34
|
+
"**/dist/**",
|
|
35
|
+
"**/build/**",
|
|
36
|
+
];
|
|
37
|
+
for (const pattern of servicePatterns) {
|
|
38
|
+
const files = await glob(pattern, {
|
|
39
|
+
ignore: excludePatterns,
|
|
40
|
+
nodir: true,
|
|
41
|
+
absolute: true,
|
|
42
|
+
});
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
try {
|
|
45
|
+
const content = await fs.readFile(file, "utf-8");
|
|
46
|
+
const fileServices = extractServicesFromFile(content, file);
|
|
47
|
+
services.push(...fileServices);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
logger.debug(`Failed to extract services from ${file}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
logger.info(`Extracted ${services.length} API services from ${projectPath}`);
|
|
55
|
+
return services;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Extract service functions from a single file using AST
|
|
59
|
+
*/
|
|
60
|
+
export function extractServicesFromFile(content, filePath) {
|
|
61
|
+
const services = [];
|
|
62
|
+
try {
|
|
63
|
+
const parser = getParser("typescript");
|
|
64
|
+
const tree = parser.parse(content);
|
|
65
|
+
// Traverse AST to find API calls
|
|
66
|
+
traverseNode(tree.rootNode, content, filePath, services);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
logger.debug(`AST parsing failed for ${filePath}, falling back to regex`);
|
|
70
|
+
return extractServicesFromFileRegex(content, filePath);
|
|
71
|
+
}
|
|
72
|
+
return services;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Recursively traverse AST nodes to find API calls
|
|
76
|
+
*/
|
|
77
|
+
function traverseNode(node, content, filePath, services) {
|
|
78
|
+
if (!node)
|
|
79
|
+
return;
|
|
80
|
+
// Look for call expressions (method calls)
|
|
81
|
+
if (node.type === "call_expression") {
|
|
82
|
+
const service = extractServiceFromCall(node, content, filePath);
|
|
83
|
+
if (service) {
|
|
84
|
+
services.push(service);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Recursively traverse children
|
|
88
|
+
for (const child of node.children || []) {
|
|
89
|
+
traverseNode(child, content, filePath, services);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Common patterns for direct API helper function names
|
|
94
|
+
*/
|
|
95
|
+
const API_HELPER_PATTERNS = [
|
|
96
|
+
/^fetch/i, // fetchApi, fetchData, fetchJson
|
|
97
|
+
/^api(?:Call|Request|Fetch)?$/i, // api, apiCall, apiRequest, apiFetch
|
|
98
|
+
/^(?:make|do|send)Request$/i, // makeRequest, doRequest, sendRequest
|
|
99
|
+
/^request$/i,
|
|
100
|
+
/^http(?:Client|Request)?$/i,
|
|
101
|
+
];
|
|
102
|
+
/**
|
|
103
|
+
* Extract service from a call expression node
|
|
104
|
+
*/
|
|
105
|
+
function extractServiceFromCall(node, content, filePath) {
|
|
106
|
+
// Get the function being called
|
|
107
|
+
const functionNode = node.childForFieldName("function");
|
|
108
|
+
if (!functionNode)
|
|
109
|
+
return null;
|
|
110
|
+
// Case 1: Method call like ApiService.post or api.get
|
|
111
|
+
if (functionNode.type === "member_expression") {
|
|
112
|
+
const objectNode = functionNode.childForFieldName("object");
|
|
113
|
+
const propertyNode = functionNode.childForFieldName("property");
|
|
114
|
+
if (!objectNode || !propertyNode)
|
|
115
|
+
return null;
|
|
116
|
+
const objectName = getNodeText(objectNode, content);
|
|
117
|
+
const methodName = getNodeText(propertyNode, content);
|
|
118
|
+
// Check if it's an API call pattern
|
|
119
|
+
const isApiCall = objectName === "ApiService" ||
|
|
120
|
+
objectName === "api" ||
|
|
121
|
+
objectName === "axios" ||
|
|
122
|
+
objectName === "client" ||
|
|
123
|
+
objectName.endsWith("Api") ||
|
|
124
|
+
objectName.endsWith("Service");
|
|
125
|
+
if (!isApiCall)
|
|
126
|
+
return null;
|
|
127
|
+
// Map method name to HTTP method
|
|
128
|
+
const httpMethod = mapToHttpMethod(methodName);
|
|
129
|
+
if (!httpMethod)
|
|
130
|
+
return null;
|
|
131
|
+
// Extract arguments
|
|
132
|
+
const argumentsNode = node.childForFieldName("arguments");
|
|
133
|
+
if (!argumentsNode)
|
|
134
|
+
return null;
|
|
135
|
+
const extracted = extractEndpointFromArgumentsTS(argumentsNode, content);
|
|
136
|
+
if (!extracted)
|
|
137
|
+
return null;
|
|
138
|
+
const endpoint = extracted.endpoint;
|
|
139
|
+
// Try to find the enclosing function/method name
|
|
140
|
+
const enclosingFunction = findEnclosingFunctionName(node, content);
|
|
141
|
+
// Extract request/response types from the enclosing function/method
|
|
142
|
+
const { requestType, responseType } = extractTypesFromEnclosingFunction(node, content, httpMethod);
|
|
143
|
+
return {
|
|
144
|
+
name: enclosingFunction || `${methodName}_${endpoint.replace(/[^a-zA-Z0-9]/g, "_")}`,
|
|
145
|
+
method: httpMethod,
|
|
146
|
+
endpoint,
|
|
147
|
+
requestType,
|
|
148
|
+
responseType,
|
|
149
|
+
queryParams: extracted.queryParams.length > 0 ? extracted.queryParams : undefined,
|
|
150
|
+
file: filePath,
|
|
151
|
+
line: node.startPosition.row + 1,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// Case 2: Direct function call like fetchApi('/endpoint', { method: 'POST' })
|
|
155
|
+
// This handles wrapper functions (fetchApi, apiCall, request, etc.)
|
|
156
|
+
if (functionNode.type === "identifier") {
|
|
157
|
+
const funcName = getNodeText(functionNode, content);
|
|
158
|
+
// Check if the function name matches common API helper patterns
|
|
159
|
+
const isApiHelper = API_HELPER_PATTERNS.some(p => p.test(funcName));
|
|
160
|
+
if (!isApiHelper)
|
|
161
|
+
return null;
|
|
162
|
+
// Extract arguments
|
|
163
|
+
const argumentsNode = node.childForFieldName("arguments");
|
|
164
|
+
if (!argumentsNode)
|
|
165
|
+
return null;
|
|
166
|
+
const extracted = extractEndpointFromArgumentsTS(argumentsNode, content);
|
|
167
|
+
if (!extracted)
|
|
168
|
+
return null;
|
|
169
|
+
const endpoint = extracted.endpoint;
|
|
170
|
+
// Try to detect HTTP method from the options argument (e.g., { method: 'POST' })
|
|
171
|
+
const httpMethod = extractHttpMethodFromArgumentsTS(argumentsNode, content) || "GET";
|
|
172
|
+
// Try to find the enclosing function/method name
|
|
173
|
+
const enclosingFunction = findEnclosingFunctionName(node, content);
|
|
174
|
+
const { requestType, responseType } = extractTypesFromEnclosingFunction(node, content, httpMethod);
|
|
175
|
+
return {
|
|
176
|
+
name: enclosingFunction || `${funcName}_${endpoint.replace(/[^a-zA-Z0-9]/g, "_")}`,
|
|
177
|
+
method: httpMethod,
|
|
178
|
+
endpoint,
|
|
179
|
+
requestType,
|
|
180
|
+
responseType,
|
|
181
|
+
queryParams: extracted.queryParams.length > 0 ? extracted.queryParams : undefined,
|
|
182
|
+
file: filePath,
|
|
183
|
+
line: node.startPosition.row + 1,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Best-effort request/response type extraction.
|
|
190
|
+
*
|
|
191
|
+
* This is intentionally heuristic: callers use it to enrich API contract mappings,
|
|
192
|
+
* not as a compiler-grade typechecker.
|
|
193
|
+
*/
|
|
194
|
+
function extractTypesFromEnclosingFunction(node, content, method) {
|
|
195
|
+
let current = node;
|
|
196
|
+
const result = {};
|
|
197
|
+
while (current) {
|
|
198
|
+
// Arrow function (common in React hooks/services)
|
|
199
|
+
if (current.type === "arrow_function") {
|
|
200
|
+
// Response type
|
|
201
|
+
const returnTypeNode = current.childForFieldName?.("return_type");
|
|
202
|
+
if (returnTypeNode) {
|
|
203
|
+
const returnTypeText = normalizeTypeText(getNodeText(returnTypeNode, content));
|
|
204
|
+
result.responseType = unwrapCommonGenerics(returnTypeText);
|
|
205
|
+
}
|
|
206
|
+
// Request type (first non-primitive typed param on write methods)
|
|
207
|
+
if (method === "POST" || method === "PUT" || method === "PATCH") {
|
|
208
|
+
const parametersNode = current.childForFieldName?.("parameters");
|
|
209
|
+
if (parametersNode) {
|
|
210
|
+
const params = collectTypedParameters(parametersNode, content);
|
|
211
|
+
for (const p of params) {
|
|
212
|
+
if (p.type && !isPrimitiveType(p.type)) {
|
|
213
|
+
result.requestType = unwrapCommonGenerics(p.type);
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
// Method definition (class services)
|
|
222
|
+
if (current.type === "method_definition") {
|
|
223
|
+
const returnTypeNode = current.childForFieldName?.("return_type");
|
|
224
|
+
if (returnTypeNode) {
|
|
225
|
+
const returnTypeText = normalizeTypeText(getNodeText(returnTypeNode, content));
|
|
226
|
+
result.responseType = unwrapCommonGenerics(returnTypeText);
|
|
227
|
+
}
|
|
228
|
+
if (method === "POST" || method === "PUT" || method === "PATCH") {
|
|
229
|
+
const parametersNode = current.childForFieldName?.("parameters");
|
|
230
|
+
if (parametersNode) {
|
|
231
|
+
const params = collectTypedParameters(parametersNode, content);
|
|
232
|
+
for (const p of params) {
|
|
233
|
+
if (p.type && !isPrimitiveType(p.type)) {
|
|
234
|
+
result.requestType = unwrapCommonGenerics(p.type);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
// Function declaration
|
|
243
|
+
if (current.type === "function_declaration") {
|
|
244
|
+
const returnTypeNode = current.childForFieldName?.("return_type");
|
|
245
|
+
if (returnTypeNode) {
|
|
246
|
+
const returnTypeText = normalizeTypeText(getNodeText(returnTypeNode, content));
|
|
247
|
+
result.responseType = unwrapCommonGenerics(returnTypeText);
|
|
248
|
+
}
|
|
249
|
+
if (method === "POST" || method === "PUT" || method === "PATCH") {
|
|
250
|
+
const parametersNode = current.childForFieldName?.("parameters");
|
|
251
|
+
if (parametersNode) {
|
|
252
|
+
const params = collectTypedParameters(parametersNode, content);
|
|
253
|
+
for (const p of params) {
|
|
254
|
+
if (p.type && !isPrimitiveType(p.type)) {
|
|
255
|
+
result.requestType = unwrapCommonGenerics(p.type);
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
current = current.parent;
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
function collectTypedParameters(parametersNode, content) {
|
|
268
|
+
const out = [];
|
|
269
|
+
for (const child of parametersNode.children || []) {
|
|
270
|
+
if (!child)
|
|
271
|
+
continue;
|
|
272
|
+
// tree-sitter-typescript: required_parameter / optional_parameter / identifier
|
|
273
|
+
if (child.type === "required_parameter" ||
|
|
274
|
+
child.type === "optional_parameter" ||
|
|
275
|
+
child.type === "identifier") {
|
|
276
|
+
const nameNode = child.childForFieldName?.("name") || child;
|
|
277
|
+
const typeNode = child.childForFieldName?.("type");
|
|
278
|
+
const name = nameNode ? getNodeText(nameNode, content) : "";
|
|
279
|
+
const typeText = typeNode
|
|
280
|
+
? normalizeTypeText(getNodeText(typeNode, content))
|
|
281
|
+
: undefined;
|
|
282
|
+
if (name)
|
|
283
|
+
out.push({ name, type: typeText });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
287
|
+
}
|
|
288
|
+
function normalizeTypeText(typeText) {
|
|
289
|
+
return typeText.replace(/^\s*:\s*/, "").trim();
|
|
290
|
+
}
|
|
291
|
+
function unwrapCommonGenerics(typeText) {
|
|
292
|
+
let t = typeText.trim();
|
|
293
|
+
// Strip trailing array syntax
|
|
294
|
+
while (t.endsWith("[]")) {
|
|
295
|
+
t = t.slice(0, -2).trim();
|
|
296
|
+
}
|
|
297
|
+
// Common wrappers
|
|
298
|
+
const wrappers = [
|
|
299
|
+
"Promise",
|
|
300
|
+
"AxiosResponse",
|
|
301
|
+
"ApiResponse",
|
|
302
|
+
"Response",
|
|
303
|
+
"Array",
|
|
304
|
+
"ReadonlyArray",
|
|
305
|
+
];
|
|
306
|
+
for (let i = 0; i < 5; i++) {
|
|
307
|
+
const m = t.match(/^([A-Za-z0-9_$.]+)\s*<\s*(.+)\s*>$/);
|
|
308
|
+
if (!m)
|
|
309
|
+
break;
|
|
310
|
+
const name = m[1];
|
|
311
|
+
const inner = m[2];
|
|
312
|
+
if (!wrappers.includes(name.split(".").pop() || name))
|
|
313
|
+
break;
|
|
314
|
+
t = inner.trim();
|
|
315
|
+
}
|
|
316
|
+
return t;
|
|
317
|
+
}
|
|
318
|
+
function isPrimitiveType(typeName) {
|
|
319
|
+
const raw = typeName.trim();
|
|
320
|
+
if (!raw)
|
|
321
|
+
return true;
|
|
322
|
+
// Literal types
|
|
323
|
+
if (/^['"`].*['"`]$/.test(raw))
|
|
324
|
+
return true;
|
|
325
|
+
if (/^[0-9]+(?:\.[0-9]+)?$/.test(raw))
|
|
326
|
+
return true;
|
|
327
|
+
// Union: only primitive if all parts are primitive
|
|
328
|
+
const unionParts = raw.split("|").map((p) => p.trim()).filter(Boolean);
|
|
329
|
+
if (unionParts.length > 1) {
|
|
330
|
+
return unionParts.every((p) => isPrimitiveType(p));
|
|
331
|
+
}
|
|
332
|
+
// Arrays / generics
|
|
333
|
+
const unwrapped = unwrapCommonGenerics(raw);
|
|
334
|
+
const base = unwrapped.replace(/\[\]$/g, "").trim();
|
|
335
|
+
const primitives = new Set([
|
|
336
|
+
"string",
|
|
337
|
+
"number",
|
|
338
|
+
"boolean",
|
|
339
|
+
"null",
|
|
340
|
+
"undefined",
|
|
341
|
+
"any",
|
|
342
|
+
"unknown",
|
|
343
|
+
"void",
|
|
344
|
+
"never",
|
|
345
|
+
"object",
|
|
346
|
+
"Record",
|
|
347
|
+
]);
|
|
348
|
+
const lower = base.toLowerCase();
|
|
349
|
+
if (primitives.has(lower))
|
|
350
|
+
return true;
|
|
351
|
+
// Sometimes TS nodes preserve casing (Record/Promise)
|
|
352
|
+
const head = base.split(/[<\s]/)[0];
|
|
353
|
+
if (primitives.has(head) || primitives.has(head.toLowerCase()))
|
|
354
|
+
return true;
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
// ============================================================================
|
|
358
|
+
// Fallback Regex Extraction
|
|
359
|
+
// ============================================================================
|
|
360
|
+
/**
|
|
361
|
+
* Fallback regex-based extraction when AST fails
|
|
362
|
+
*/
|
|
363
|
+
function extractServicesFromFileRegex(content, filePath) {
|
|
364
|
+
const services = [];
|
|
365
|
+
const lines = content.split("\n");
|
|
366
|
+
for (let i = 0; i < lines.length; i++) {
|
|
367
|
+
const line = lines[i];
|
|
368
|
+
const lineNum = i + 1;
|
|
369
|
+
// Pattern: ApiService.post('/endpoint', data)
|
|
370
|
+
// Pattern: api.get('/endpoint')
|
|
371
|
+
const apiCallMatch = line.match(/(?:ApiService|api|axios|client|[A-Za-z]+Api|[A-Za-z]+Service)\.(get|post|put|patch|delete)\s*\(\s*["'`]([^"'`]+)["'`]/i);
|
|
372
|
+
if (apiCallMatch) {
|
|
373
|
+
const method = apiCallMatch[1].toUpperCase();
|
|
374
|
+
const endpoint = apiCallMatch[2];
|
|
375
|
+
// Try to find function name
|
|
376
|
+
const funcMatch = line.match(/(?:export\s+)?(?:async\s+)?(?:function|const)\s+(\w+)/) ||
|
|
377
|
+
lines.slice(Math.max(0, i - 5), i).join(" ").match(/(\w+)\s*[:=]\s*(?:async\s*)?\(/);
|
|
378
|
+
const funcName = funcMatch ? funcMatch[1] : `api_${method.toLowerCase()}`;
|
|
379
|
+
services.push({
|
|
380
|
+
name: funcName,
|
|
381
|
+
method,
|
|
382
|
+
endpoint,
|
|
383
|
+
file: filePath,
|
|
384
|
+
line: lineNum,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return services;
|
|
389
|
+
}
|
|
390
|
+
// ============================================================================
|
|
391
|
+
// Type Extraction
|
|
392
|
+
// ============================================================================
|
|
393
|
+
/**
|
|
394
|
+
* Extract all TypeScript types/interfaces from a project
|
|
395
|
+
*/
|
|
396
|
+
export async function extractTypes(projectPath) {
|
|
397
|
+
const types = [];
|
|
398
|
+
// Find type definition files
|
|
399
|
+
const typePatterns = [
|
|
400
|
+
`${projectPath}/**/types/**/*.ts`,
|
|
401
|
+
`${projectPath}/**/interfaces/**/*.ts`,
|
|
402
|
+
`${projectPath}/**/models/**/*.ts`,
|
|
403
|
+
`${projectPath}/src/services/**/*.ts`, // Include services folder for inline types
|
|
404
|
+
`${projectPath}/src/types.ts`,
|
|
405
|
+
`${projectPath}/types.ts`,
|
|
406
|
+
];
|
|
407
|
+
const excludePatterns = [
|
|
408
|
+
"**/node_modules/**",
|
|
409
|
+
"**/*.test.ts",
|
|
410
|
+
"**/*.spec.ts",
|
|
411
|
+
"**/dist/**",
|
|
412
|
+
"**/build/**",
|
|
413
|
+
];
|
|
414
|
+
for (const pattern of typePatterns) {
|
|
415
|
+
const files = await glob(pattern, {
|
|
416
|
+
ignore: excludePatterns,
|
|
417
|
+
nodir: true,
|
|
418
|
+
absolute: true,
|
|
419
|
+
});
|
|
420
|
+
for (const file of files) {
|
|
421
|
+
try {
|
|
422
|
+
const content = await fs.readFile(file, "utf-8");
|
|
423
|
+
const fileTypes = extractTypesFromFile(content, file);
|
|
424
|
+
types.push(...fileTypes);
|
|
425
|
+
}
|
|
426
|
+
catch (err) {
|
|
427
|
+
logger.debug(`Failed to extract types from ${file}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
logger.info(`Extracted ${types.length} types from ${projectPath}`);
|
|
432
|
+
return types;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Extract types/interfaces from a single file using AST
|
|
436
|
+
*/
|
|
437
|
+
export function extractTypesFromFile(content, filePath) {
|
|
438
|
+
const types = [];
|
|
439
|
+
try {
|
|
440
|
+
const parser = getParser("typescript");
|
|
441
|
+
const tree = parser.parse(content);
|
|
442
|
+
traverseTypes(tree.rootNode, content, filePath, types);
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
logger.debug(`AST parsing failed for ${filePath}, falling back to regex`);
|
|
446
|
+
return extractTypesFromFileRegex(content, filePath);
|
|
447
|
+
}
|
|
448
|
+
return types;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Recursively traverse AST to find type definitions
|
|
452
|
+
*/
|
|
453
|
+
function traverseTypes(node, content, filePath, types) {
|
|
454
|
+
if (!node)
|
|
455
|
+
return;
|
|
456
|
+
// Interface declaration
|
|
457
|
+
if (node.type === "interface_declaration") {
|
|
458
|
+
const type = extractInterface(node, content, filePath);
|
|
459
|
+
if (type)
|
|
460
|
+
types.push(type);
|
|
461
|
+
}
|
|
462
|
+
// Type alias declaration
|
|
463
|
+
if (node.type === "type_alias_declaration") {
|
|
464
|
+
const type = extractTypeAlias(node, content, filePath);
|
|
465
|
+
if (type)
|
|
466
|
+
types.push(type);
|
|
467
|
+
}
|
|
468
|
+
// Recursively traverse children
|
|
469
|
+
for (const child of node.children || []) {
|
|
470
|
+
traverseTypes(child, content, filePath, types);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Extract interface definition
|
|
475
|
+
*/
|
|
476
|
+
function extractInterface(node, content, filePath) {
|
|
477
|
+
const nameNode = node.childForFieldName("name");
|
|
478
|
+
if (!nameNode)
|
|
479
|
+
return null;
|
|
480
|
+
const name = getNodeText(nameNode, content);
|
|
481
|
+
const bodyNode = node.childForFieldName("body");
|
|
482
|
+
if (!bodyNode)
|
|
483
|
+
return null;
|
|
484
|
+
const fields = extractFieldsFromBody(bodyNode, content);
|
|
485
|
+
return {
|
|
486
|
+
name,
|
|
487
|
+
fields,
|
|
488
|
+
file: filePath,
|
|
489
|
+
line: node.startPosition.row + 1,
|
|
490
|
+
kind: "interface",
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Extract type alias definition
|
|
495
|
+
*/
|
|
496
|
+
function extractTypeAlias(node, content, filePath) {
|
|
497
|
+
const nameNode = node.childForFieldName("name");
|
|
498
|
+
if (!nameNode)
|
|
499
|
+
return null;
|
|
500
|
+
const name = getNodeText(nameNode, content);
|
|
501
|
+
// For object type aliases
|
|
502
|
+
const valueNode = node.childForFieldName("value");
|
|
503
|
+
if (valueNode && valueNode.type === "object_type") {
|
|
504
|
+
const fields = extractFieldsFromBody(valueNode, content);
|
|
505
|
+
return {
|
|
506
|
+
name,
|
|
507
|
+
fields,
|
|
508
|
+
file: filePath,
|
|
509
|
+
line: node.startPosition.row + 1,
|
|
510
|
+
kind: "type",
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Extract fields from interface/type body
|
|
517
|
+
*/
|
|
518
|
+
function extractFieldsFromBody(bodyNode, content) {
|
|
519
|
+
const fields = [];
|
|
520
|
+
for (const child of bodyNode.children || []) {
|
|
521
|
+
if (child.type === "property_signature" || child.type === "field_definition") {
|
|
522
|
+
const nameNode = child.childForFieldName("name");
|
|
523
|
+
const typeNode = child.childForFieldName("type");
|
|
524
|
+
if (nameNode) {
|
|
525
|
+
const name = getNodeText(nameNode, content);
|
|
526
|
+
const type = typeNode ? getNodeText(typeNode, content) : "any";
|
|
527
|
+
// Check if optional
|
|
528
|
+
const isOptional = child.type === "property_signature" &&
|
|
529
|
+
child.children.some((c) => c.type === "?");
|
|
530
|
+
fields.push({
|
|
531
|
+
name,
|
|
532
|
+
type,
|
|
533
|
+
required: !isOptional,
|
|
534
|
+
optional: isOptional,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return fields;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Fallback regex-based type extraction
|
|
543
|
+
*/
|
|
544
|
+
function extractTypesFromFileRegex(content, filePath) {
|
|
545
|
+
const types = [];
|
|
546
|
+
const lines = content.split("\n");
|
|
547
|
+
let currentType = null;
|
|
548
|
+
for (let i = 0; i < lines.length; i++) {
|
|
549
|
+
const line = lines[i];
|
|
550
|
+
// Interface declaration
|
|
551
|
+
const interfaceMatch = line.match(/(?:export\s+)?interface\s+(\w+)(?:\s+extends\s+[^{]+)?\s*\{/);
|
|
552
|
+
if (interfaceMatch) {
|
|
553
|
+
if (currentType) {
|
|
554
|
+
types.push(currentType);
|
|
555
|
+
}
|
|
556
|
+
currentType = {
|
|
557
|
+
name: interfaceMatch[1],
|
|
558
|
+
kind: "interface",
|
|
559
|
+
fields: [],
|
|
560
|
+
file: filePath,
|
|
561
|
+
line: i + 1,
|
|
562
|
+
};
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
// Type alias declaration
|
|
566
|
+
const typeMatch = line.match(/(?:export\s+)?type\s+(\w+)\s*=\s*\{/);
|
|
567
|
+
if (typeMatch) {
|
|
568
|
+
if (currentType) {
|
|
569
|
+
types.push(currentType);
|
|
570
|
+
}
|
|
571
|
+
currentType = {
|
|
572
|
+
name: typeMatch[1],
|
|
573
|
+
kind: "type",
|
|
574
|
+
fields: [],
|
|
575
|
+
file: filePath,
|
|
576
|
+
line: i + 1,
|
|
577
|
+
};
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
// Extract field
|
|
581
|
+
if (currentType) {
|
|
582
|
+
const isIndented = line.startsWith(" ") || line.startsWith("\t");
|
|
583
|
+
const isEmpty = line.trim() === "";
|
|
584
|
+
const isComment = line.trim().startsWith("//");
|
|
585
|
+
if (!isIndented && !isEmpty && !isComment) {
|
|
586
|
+
types.push(currentType);
|
|
587
|
+
currentType = null;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
const fieldMatch = line.match(/(\w+)(\?)?:\s*([^;\[\{]+)(?:\[\])?(?:\s*;)?/);
|
|
591
|
+
if (fieldMatch) {
|
|
592
|
+
currentType.fields.push({
|
|
593
|
+
name: fieldMatch[1],
|
|
594
|
+
type: fieldMatch[3].trim(),
|
|
595
|
+
required: !fieldMatch[2],
|
|
596
|
+
optional: !!fieldMatch[2],
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (currentType) {
|
|
602
|
+
types.push(currentType);
|
|
603
|
+
}
|
|
604
|
+
return types;
|
|
605
|
+
}
|
|
606
|
+
// ============================================================================
|
|
607
|
+
// API Configuration Extraction
|
|
608
|
+
// ============================================================================
|
|
609
|
+
/**
|
|
610
|
+
* Extract API configuration from a project
|
|
611
|
+
*/
|
|
612
|
+
export async function extractApiConfig(projectPath) {
|
|
613
|
+
let apiBaseUrl = "/api";
|
|
614
|
+
let httpClient = "fetch";
|
|
615
|
+
// Check for API config files
|
|
616
|
+
const configPaths = [
|
|
617
|
+
path.join(projectPath, "src/config/api.ts"),
|
|
618
|
+
path.join(projectPath, "src/lib/api.ts"),
|
|
619
|
+
path.join(projectPath, "lib/api.ts"),
|
|
620
|
+
path.join(projectPath, "utils/api.ts"),
|
|
621
|
+
path.join(projectPath, "src/api/client.ts"),
|
|
622
|
+
path.join(projectPath, "src/services/api.ts"),
|
|
623
|
+
];
|
|
624
|
+
for (const configPath of configPaths) {
|
|
625
|
+
if (await fileExists(configPath)) {
|
|
626
|
+
try {
|
|
627
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
628
|
+
// Detect HTTP client
|
|
629
|
+
if (content.includes("axios")) {
|
|
630
|
+
httpClient = "axios";
|
|
631
|
+
}
|
|
632
|
+
else if (content.includes("react-query") || content.includes("useQuery")) {
|
|
633
|
+
httpClient = "react-query";
|
|
634
|
+
}
|
|
635
|
+
else if (content.includes("swr")) {
|
|
636
|
+
httpClient = "swr";
|
|
637
|
+
}
|
|
638
|
+
else if (content.includes("ApiService")) {
|
|
639
|
+
httpClient = "fetch"; // Custom service using fetch
|
|
640
|
+
}
|
|
641
|
+
// Extract base URL
|
|
642
|
+
const baseUrlMatch = content.match(/baseURL\s*[:=]\s*["']([^"']+)["']/) ||
|
|
643
|
+
content.match(/baseUrl\s*[:=]\s*["']([^"']+)["']/) ||
|
|
644
|
+
content.match(/API_BASE_URL\s*[:=]\s*["']([^"']+)["']/);
|
|
645
|
+
if (baseUrlMatch) {
|
|
646
|
+
apiBaseUrl = baseUrlMatch[1];
|
|
647
|
+
}
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
catch {
|
|
651
|
+
// Continue to next file
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return { apiBaseUrl, httpClient };
|
|
656
|
+
}
|
|
657
|
+
async function fileExists(filePath) {
|
|
658
|
+
try {
|
|
659
|
+
const stats = await fs.stat(filePath);
|
|
660
|
+
return stats.isFile();
|
|
661
|
+
}
|
|
662
|
+
catch {
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
//# sourceMappingURL=typescript.js.map
|