code-auditor-mcp 2.6.1 → 3.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/README.md +319 -147
- package/dist/analyzers/analyzerUtils.d.ts +13 -11
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +22 -56
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/dataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/dataAccessAnalyzer.js +50 -24
- package/dist/analyzers/dataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +4 -1
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +210 -111
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/dryAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/dryAnalyzer.js +190 -58
- package/dist/analyzers/dryAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts +20 -0
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/invariantsAnalyzer.js +148 -0
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +2 -2
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/schemaAnalyzer.d.ts +2 -0
- package/dist/analyzers/schemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/schemaAnalyzer.js +306 -147
- package/dist/analyzers/schemaAnalyzer.js.map +1 -1
- package/dist/analyzers/solidAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/solidAnalyzer.js +145 -119
- package/dist/analyzers/solidAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +3 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +41 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +8 -8
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +6 -6
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/api/index.js +1 -0
- package/dist/api/index.js.map +1 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +367 -24
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +740 -18
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB-enhanced.d.ts +35 -13
- package/dist/codeIndexDB-enhanced.d.ts.map +1 -1
- package/dist/codeIndexDB-enhanced.js +311 -214
- package/dist/codeIndexDB-enhanced.js.map +1 -1
- package/dist/codeIndexDB.d.ts +222 -0
- package/dist/codeIndexDB.d.ts.map +1 -0
- package/dist/codeIndexDB.js +2042 -0
- package/dist/codeIndexDB.js.map +1 -0
- package/dist/codeIndexService.d.ts.map +1 -1
- package/dist/codeIndexService.js.map +1 -1
- package/dist/componentScanner.d.ts +14 -8
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +136 -113
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +10 -7
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/fingerprint.d.ts +15 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +32 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/functionScanner.d.ts +5 -0
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +417 -341
- package/dist/functionScanner.js.map +1 -1
- package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AiderConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.js.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
- package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
- package/dist/generators/JetBrainsConfigGenerator.d.ts.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
- package/dist/grammars/tree-sitter-go.wasm +0 -0
- package/dist/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/invariants/index.d.ts +9 -0
- package/dist/invariants/index.d.ts.map +1 -0
- package/dist/invariants/index.js +4 -0
- package/dist/invariants/index.js.map +1 -0
- package/dist/invariants/invariant-rules.schema.json +137 -0
- package/dist/invariants/ruleEngine.d.ts +32 -0
- package/dist/invariants/ruleEngine.d.ts.map +1 -0
- package/dist/invariants/ruleEngine.js +536 -0
- package/dist/invariants/ruleEngine.js.map +1 -0
- package/dist/invariants/ruleValidator.d.ts +17 -0
- package/dist/invariants/ruleValidator.d.ts.map +1 -0
- package/dist/invariants/ruleValidator.js +264 -0
- package/dist/invariants/ruleValidator.js.map +1 -0
- package/dist/invariants/types.d.ts +78 -0
- package/dist/invariants/types.d.ts.map +1 -0
- package/dist/invariants/types.js +5 -0
- package/dist/invariants/types.js.map +1 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/LanguageRegistry.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +1 -1
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +102 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -0
- package/dist/languages/adapterBridge.js +382 -0
- package/dist/languages/adapterBridge.js.map +1 -0
- package/dist/languages/go/GoAdapter.d.ts +37 -26
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +560 -281
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/index.d.ts +5 -2
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +11 -9
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/tree-sitter/converter.d.ts +43 -0
- package/dist/languages/tree-sitter/converter.d.ts.map +1 -0
- package/dist/languages/tree-sitter/converter.js +150 -0
- package/dist/languages/tree-sitter/converter.js.map +1 -0
- package/dist/languages/tree-sitter/parser.d.ts +36 -0
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -0
- package/dist/languages/tree-sitter/parser.js +114 -0
- package/dist/languages/tree-sitter/parser.js.map +1 -0
- package/dist/languages/types.d.ts +11 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +92 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +951 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts +80 -11
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +279 -13
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +21 -21
- package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +130 -126
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.js +1 -1
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +1455 -1132
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +4 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +49 -2
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpDiagnostics.d.ts.map +1 -1
- package/dist/mcpDiagnostics.js +8 -5
- package/dist/mcpDiagnostics.js.map +1 -1
- package/dist/mcpToolErrors.d.ts.map +1 -1
- package/dist/mcpToolErrors.js.map +1 -1
- package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
- package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
- package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
- package/dist/native-bootstrap.d.ts +15 -0
- package/dist/native-bootstrap.d.ts.map +1 -0
- package/dist/native-bootstrap.js +75 -0
- package/dist/native-bootstrap.js.map +1 -0
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.js +4 -0
- package/dist/reporting/reportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts +39 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -0
- package/dist/reporting/sarifReportGenerator.js +229 -0
- package/dist/reporting/sarifReportGenerator.js.map +1 -0
- package/dist/search/QueryParser.d.ts +41 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +200 -19
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +4 -4
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/services/ProjectTaskRepository.d.ts +34 -9
- package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
- package/dist/services/ProjectTaskRepository.js +385 -10
- package/dist/services/ProjectTaskRepository.js.map +1 -1
- package/dist/services/SchemaParser.d.ts.map +1 -1
- package/dist/services/SchemaParser.js +5 -8
- package/dist/services/SchemaParser.js.map +1 -1
- package/dist/services/auditJobService.d.ts +1 -1
- package/dist/services/projectTaskHelpers.js.map +1 -1
- package/dist/services/whitelistService.d.ts.map +1 -1
- package/dist/services/whitelistService.js +1 -1
- package/dist/services/whitelistService.js.map +1 -1
- package/dist/tool-registry.d.ts +79 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +139 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/types/projectTask.d.ts +31 -0
- package/dist/types/projectTask.d.ts.map +1 -1
- package/dist/types.d.ts +26 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ui/InteractivePrompts.d.ts.map +1 -1
- package/dist/ui/InteractivePrompts.js.map +1 -1
- package/dist/utils/astParser.d.ts +31 -38
- package/dist/utils/astParser.d.ts.map +1 -1
- package/dist/utils/astParser.js +138 -196
- package/dist/utils/astParser.js.map +1 -1
- package/dist/utils/astUtils.d.ts +87 -58
- package/dist/utils/astUtils.d.ts.map +1 -1
- package/dist/utils/astUtils.js +852 -581
- package/dist/utils/astUtils.js.map +1 -1
- package/dist/utils/componentPatterns.js.map +1 -1
- package/dist/utils/componentResponsibility.d.ts +13 -9
- package/dist/utils/componentResponsibility.d.ts.map +1 -1
- package/dist/utils/componentResponsibility.js +236 -206
- package/dist/utils/componentResponsibility.js.map +1 -1
- package/dist/utils/dependencyExtractor.d.ts +11 -6
- package/dist/utils/dependencyExtractor.d.ts.map +1 -1
- package/dist/utils/dependencyExtractor.js +200 -119
- package/dist/utils/dependencyExtractor.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +1 -1
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/jsDocParser.d.ts +27 -30
- package/dist/utils/jsDocParser.d.ts.map +1 -1
- package/dist/utils/jsDocParser.js +271 -207
- package/dist/utils/jsDocParser.js.map +1 -1
- package/dist/utils/reactDetection.d.ts +27 -12
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +331 -219
- package/dist/utils/reactDetection.js.map +1 -1
- package/dist/workers/auditWorker.js +17 -3
- package/dist/workers/auditWorker.js.map +1 -1
- package/package.json +54 -51
- package/dist/adapters/GoAdapter.d.ts +0 -53
- package/dist/adapters/GoAdapter.d.ts.map +0 -1
- package/dist/adapters/GoAdapter.js +0 -611
- package/dist/adapters/GoAdapter.js.map +0 -1
- package/dist/adapters/GoConfig.d.ts +0 -35
- package/dist/adapters/GoConfig.d.ts.map +0 -1
- package/dist/adapters/GoConfig.js +0 -155
- package/dist/adapters/GoConfig.js.map +0 -1
- package/dist/adapters/LanguageAdapter.d.ts +0 -135
- package/dist/adapters/LanguageAdapter.d.ts.map +0 -1
- package/dist/adapters/LanguageAdapter.js +0 -47
- package/dist/adapters/LanguageAdapter.js.map +0 -1
- package/dist/adapters/TypeScriptAdapter.d.ts +0 -43
- package/dist/adapters/TypeScriptAdapter.d.ts.map +0 -1
- package/dist/adapters/TypeScriptAdapter.js +0 -386
- package/dist/adapters/TypeScriptAdapter.js.map +0 -1
- package/dist/adapters/UniversalAnalyzer.d.ts +0 -42
- package/dist/adapters/UniversalAnalyzer.d.ts.map +0 -1
- package/dist/adapters/UniversalAnalyzer.js +0 -103
- package/dist/adapters/UniversalAnalyzer.js.map +0 -1
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts +0 -48
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts.map +0 -1
- package/dist/adapters/UniversalSOLIDAnalyzer.js +0 -178
- package/dist/adapters/UniversalSOLIDAnalyzer.js.map +0 -1
- package/dist/adapters/solidAnalyzerCompat.d.ts +0 -21
- package/dist/adapters/solidAnalyzerCompat.d.ts.map +0 -1
- package/dist/adapters/solidAnalyzerCompat.js +0 -78
- package/dist/adapters/solidAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/dataAccessAnalyzerCompat.js +0 -59
- package/dist/analyzers/dataAccessAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/documentationAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/documentationAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/documentationAnalyzerCompat.js +0 -52
- package/dist/analyzers/documentationAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/dryAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/dryAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/dryAnalyzerCompat.js +0 -44
- package/dist/analyzers/dryAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/schemaAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/schemaAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/schemaAnalyzerCompat.js +0 -69
- package/dist/analyzers/schemaAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/solidAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/solidAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/solidAnalyzerCompat.js +0 -78
- package/dist/analyzers/solidAnalyzerCompat.js.map +0 -1
- package/dist/codeIndexDb.d.ts +0 -394
- package/dist/codeIndexDb.d.ts.map +0 -1
- package/dist/codeIndexDb.js +0 -2077
- package/dist/codeIndexDb.js.map +0 -1
- package/dist/languages/typescript/TypeScriptAdapter.d.ts +0 -108
- package/dist/languages/typescript/TypeScriptAdapter.d.ts.map +0 -1
- package/dist/languages/typescript/TypeScriptAdapter.js +0 -567
- package/dist/languages/typescript/TypeScriptAdapter.js.map +0 -1
- package/dist/languages/typescript/test-extraction.d.ts +0 -2
- package/dist/languages/typescript/test-extraction.d.ts.map +0 -1
- package/dist/languages/typescript/test-extraction.js +0 -36
- package/dist/languages/typescript/test-extraction.js.map +0 -1
- package/dist/mcp-hybrid.d.ts +0 -10
- package/dist/mcp-hybrid.d.ts.map +0 -1
- package/dist/mcp-hybrid.js +0 -41
- package/dist/mcp-hybrid.js.map +0 -1
- package/dist/mcp-index.d.ts +0 -13
- package/dist/mcp-index.d.ts.map +0 -1
- package/dist/mcp-index.js +0 -123
- package/dist/mcp-index.js.map +0 -1
- package/dist/mcp-standalone.d.ts +0 -3
- package/dist/mcp-standalone.d.ts.map +0 -1
- package/dist/mcp-standalone.js +0 -1201
- package/dist/mcp-standalone.js.map +0 -1
- package/dist/mcp-ui-server.d.ts +0 -27
- package/dist/mcp-ui-server.d.ts.map +0 -1
- package/dist/mcp-ui-server.js +0 -484
- package/dist/mcp-ui-server.js.map +0 -1
- package/dist/test-multi-strategy-search.d.ts +0 -3
- package/dist/test-multi-strategy-search.d.ts.map +0 -1
- package/dist/test-multi-strategy-search.js +0 -170
- package/dist/test-multi-strategy-search.js.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Schema Analyzer
|
|
3
3
|
* Analyzes code against loaded database schemas to find violations and patterns
|
|
4
|
+
*
|
|
5
|
+
* Uses tree-sitter AST patterns instead of the TypeScript compiler API.
|
|
4
6
|
*/
|
|
5
7
|
import { CodeIndexDB } from '../codeIndexDB.js';
|
|
8
|
+
import { parseFile, getLineAndColumn, getNodeName } from '../languages/adapterBridge.js';
|
|
6
9
|
import { promises as fs } from 'fs';
|
|
7
10
|
import path from 'path';
|
|
8
|
-
import ts from 'typescript';
|
|
9
11
|
const DEFAULT_CONFIG = {
|
|
10
12
|
enableTableUsageTracking: true,
|
|
11
13
|
checkMissingReferences: true,
|
|
@@ -16,6 +18,30 @@ const DEFAULT_CONFIG = {
|
|
|
16
18
|
allowedQueryPatterns: ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
|
|
17
19
|
requiredSchemas: []
|
|
18
20
|
};
|
|
21
|
+
const SQL_KEYWORDS = ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP'];
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/** Get the raw source text from an ASTNode's underlying tree-sitter node. */
|
|
26
|
+
function rawText(node) {
|
|
27
|
+
return node.raw?.text ?? '';
|
|
28
|
+
}
|
|
29
|
+
/** Strip surrounding single or double quotes from a string. */
|
|
30
|
+
function stripQuotes(text) {
|
|
31
|
+
if ((text.startsWith("'") && text.endsWith("'")) ||
|
|
32
|
+
(text.startsWith('"') && text.endsWith('"')) ||
|
|
33
|
+
(text.startsWith('`') && text.endsWith('`'))) {
|
|
34
|
+
return text.slice(1, -1);
|
|
35
|
+
}
|
|
36
|
+
return text;
|
|
37
|
+
}
|
|
38
|
+
/** Find the first direct child with a given type. */
|
|
39
|
+
function findChild(node, type) {
|
|
40
|
+
return node.children?.find(c => c.type === type);
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Main analyzer
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
19
45
|
export const analyzeSchema = async (files, config = {}, options = {}, progressCallback) => {
|
|
20
46
|
const finalConfig = { ...DEFAULT_CONFIG, ...config };
|
|
21
47
|
const violations = [];
|
|
@@ -88,117 +114,141 @@ export const analyzeSchema = async (files, config = {}, options = {}, progressCa
|
|
|
88
114
|
};
|
|
89
115
|
async function analyzeFile(filePath, content, allTables, tableToSchema, config, db) {
|
|
90
116
|
const violations = [];
|
|
91
|
-
// Parse
|
|
92
|
-
const
|
|
117
|
+
// Parse with tree-sitter
|
|
118
|
+
const ast = parseFile(filePath, content);
|
|
119
|
+
if (!ast) {
|
|
120
|
+
return violations;
|
|
121
|
+
}
|
|
93
122
|
// Track schema usage for this file
|
|
94
123
|
const schemaUsages = [];
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Walk the AST while tracking the enclosing function name.
|
|
126
|
+
* Matches the original pattern: the visitor fires for the current node
|
|
127
|
+
* using the *parent* function context, then computes a new context for
|
|
128
|
+
* the node's children.
|
|
129
|
+
*/
|
|
130
|
+
walkWithContext(ast.root, (node, currentFunction) => {
|
|
131
|
+
// ---------------------------------------------------------------
|
|
132
|
+
// 1. Drizzle ORM – Functional table definitions
|
|
133
|
+
// pgTable('table_name', { ... }), mysqlTable, sqliteTable
|
|
134
|
+
// ---------------------------------------------------------------
|
|
135
|
+
if (node.type === 'call_expression') {
|
|
136
|
+
const identNode = findChild(node, 'identifier');
|
|
137
|
+
if (identNode) {
|
|
138
|
+
const funcName = rawText(identNode);
|
|
139
|
+
if (['pgTable', 'mysqlTable', 'sqliteTable'].includes(funcName)) {
|
|
140
|
+
const tableName = extractTableNameFromDrizzle(node);
|
|
141
|
+
if (tableName) {
|
|
142
|
+
const { line, column } = getLineAndColumn(node);
|
|
143
|
+
schemaUsages.push({
|
|
144
|
+
tableName,
|
|
145
|
+
functionName: currentFunction || 'schema-definition',
|
|
146
|
+
usageType: 'reference',
|
|
147
|
+
line,
|
|
148
|
+
column,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
112
151
|
}
|
|
113
152
|
}
|
|
114
153
|
}
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
154
|
+
// ---------------------------------------------------------------
|
|
155
|
+
// 2. TypeORM – Entity decorators
|
|
156
|
+
// @Entity('table_name') or @Entity({ name: 'table_name' })
|
|
157
|
+
// ---------------------------------------------------------------
|
|
158
|
+
if (node.type === 'decorator') {
|
|
159
|
+
const callExpr = findChild(node, 'call_expression');
|
|
160
|
+
if (callExpr) {
|
|
161
|
+
const decoratorName = getDecoratorName(callExpr);
|
|
162
|
+
if (decoratorName === 'Entity') {
|
|
163
|
+
const tableName = extractTableNameFromTypeORM(callExpr);
|
|
164
|
+
if (tableName) {
|
|
165
|
+
const { line, column } = getLineAndColumn(node);
|
|
166
|
+
schemaUsages.push({
|
|
167
|
+
tableName,
|
|
168
|
+
functionName: currentFunction || 'entity-definition',
|
|
169
|
+
usageType: 'reference',
|
|
170
|
+
line,
|
|
171
|
+
column,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
130
174
|
}
|
|
131
175
|
}
|
|
132
176
|
}
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
177
|
+
// ---------------------------------------------------------------
|
|
178
|
+
// 3. Mongoose – Schema constructors
|
|
179
|
+
// new Schema({ field: { type: String, ref: 'OtherModel' } })
|
|
180
|
+
// ---------------------------------------------------------------
|
|
181
|
+
if (node.type === 'new_expression') {
|
|
182
|
+
const exprIdent = findChild(node, 'identifier');
|
|
183
|
+
if (exprIdent && rawText(exprIdent) === 'Schema') {
|
|
137
184
|
analyzeMongooseSchema(node, currentFunction);
|
|
138
185
|
}
|
|
139
186
|
}
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
187
|
+
// ---------------------------------------------------------------
|
|
188
|
+
// 4. Prisma Client – Table access patterns
|
|
189
|
+
// prisma.user.findMany()
|
|
190
|
+
// ---------------------------------------------------------------
|
|
191
|
+
if (node.type === 'member_expression') {
|
|
192
|
+
const propIdent = findChild(node, 'property_identifier');
|
|
193
|
+
if (propIdent) {
|
|
194
|
+
const propertyName = rawText(propIdent);
|
|
195
|
+
// Prisma-specific check (prisma.user, db.user, client.user)
|
|
196
|
+
if (isPrismaBoundProperty(node)) {
|
|
197
|
+
const { line, column } = getLineAndColumn(node);
|
|
198
|
+
schemaUsages.push({
|
|
199
|
+
tableName: propertyName,
|
|
200
|
+
functionName: currentFunction || 'unknown',
|
|
201
|
+
usageType: 'query',
|
|
202
|
+
line,
|
|
203
|
+
column,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
153
206
|
}
|
|
154
207
|
}
|
|
155
|
-
//
|
|
156
|
-
|
|
208
|
+
// ---------------------------------------------------------------
|
|
209
|
+
// 5. Detect SQL queries and table references in string/template literals
|
|
210
|
+
// ---------------------------------------------------------------
|
|
211
|
+
if (node.type === 'string' ||
|
|
212
|
+
node.type === 'template_string' ||
|
|
213
|
+
node.type === 'template_literal') {
|
|
157
214
|
const queryText = getQueryText(node);
|
|
158
215
|
if (queryText) {
|
|
159
216
|
const queryAnalysis = analyzeQuery(queryText, allTables, filePath);
|
|
160
217
|
violations.push(...queryAnalysis.violations);
|
|
161
|
-
// Record schema usage
|
|
162
218
|
for (const table of queryAnalysis.tables) {
|
|
163
|
-
const
|
|
219
|
+
const { line, column } = getLineAndColumn(node);
|
|
164
220
|
schemaUsages.push({
|
|
165
221
|
tableName: table,
|
|
166
222
|
functionName: currentFunction || 'unknown',
|
|
167
223
|
usageType: queryAnalysis.type,
|
|
168
|
-
line
|
|
169
|
-
column
|
|
170
|
-
rawQuery: queryText
|
|
224
|
+
line,
|
|
225
|
+
column,
|
|
226
|
+
rawQuery: queryText,
|
|
171
227
|
});
|
|
172
228
|
}
|
|
173
229
|
}
|
|
174
230
|
}
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
funcName = node.name.text;
|
|
194
|
-
}
|
|
195
|
-
else if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
196
|
-
funcName = node.name.text;
|
|
231
|
+
// ---------------------------------------------------------------
|
|
232
|
+
// 6. General ORM / model references
|
|
233
|
+
// Any member expression whose property matches a known table name
|
|
234
|
+
// ---------------------------------------------------------------
|
|
235
|
+
if (node.type === 'member_expression') {
|
|
236
|
+
const propIdent = findChild(node, 'property_identifier');
|
|
237
|
+
if (propIdent) {
|
|
238
|
+
const propertyName = rawText(propIdent);
|
|
239
|
+
if (allTables.has(propertyName)) {
|
|
240
|
+
const { line, column } = getLineAndColumn(node);
|
|
241
|
+
schemaUsages.push({
|
|
242
|
+
tableName: propertyName,
|
|
243
|
+
functionName: currentFunction || 'unknown',
|
|
244
|
+
usageType: 'reference',
|
|
245
|
+
line,
|
|
246
|
+
column,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
197
249
|
}
|
|
198
250
|
}
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
visit(sourceFile);
|
|
251
|
+
});
|
|
202
252
|
// Record schema usages in database
|
|
203
253
|
if (config.enableTableUsageTracking) {
|
|
204
254
|
for (const usage of schemaUsages) {
|
|
@@ -209,7 +259,7 @@ async function analyzeFile(filePath, content, allTables, tableToSchema, config,
|
|
|
209
259
|
usageType: usage.usageType,
|
|
210
260
|
line: usage.line,
|
|
211
261
|
column: usage.column,
|
|
212
|
-
rawQuery: usage.rawQuery
|
|
262
|
+
rawQuery: usage.rawQuery,
|
|
213
263
|
});
|
|
214
264
|
}
|
|
215
265
|
}
|
|
@@ -222,27 +272,61 @@ async function analyzeFile(filePath, content, allTables, tableToSchema, config,
|
|
|
222
272
|
}
|
|
223
273
|
return violations;
|
|
224
274
|
}
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// AST walker with function-context tracking
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
/**
|
|
279
|
+
* Walk an AST depth-first while tracking the name of the enclosing function.
|
|
280
|
+
* The visitor receives the current node and the *parent's* function name,
|
|
281
|
+
* then computes an updated name for the node's children.
|
|
282
|
+
*/
|
|
283
|
+
function walkWithContext(node, visitor, currentFunction) {
|
|
284
|
+
// Invoke visitor with the parent's context
|
|
285
|
+
visitor(node, currentFunction);
|
|
286
|
+
// Determine the function context for children
|
|
287
|
+
let funcName = currentFunction;
|
|
288
|
+
if (node.type === 'function_declaration' ||
|
|
289
|
+
node.type === 'method_definition' ||
|
|
290
|
+
node.type === 'arrow_function') {
|
|
291
|
+
const name = getNodeName(node);
|
|
292
|
+
if (name) {
|
|
293
|
+
funcName = name;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Recurse into children
|
|
297
|
+
if (node.children) {
|
|
298
|
+
for (const child of node.children) {
|
|
299
|
+
walkWithContext(child, visitor, funcName);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
// Query text detection
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
225
306
|
function getQueryText(node) {
|
|
226
|
-
if (
|
|
227
|
-
const text = node
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return node.text;
|
|
307
|
+
if (node.type === 'string') {
|
|
308
|
+
const text = rawText(node);
|
|
309
|
+
const unquoted = stripQuotes(text);
|
|
310
|
+
const upper = unquoted.toUpperCase();
|
|
311
|
+
if (SQL_KEYWORDS.some(kw => upper.includes(kw))) {
|
|
312
|
+
return unquoted;
|
|
233
313
|
}
|
|
234
314
|
}
|
|
235
|
-
if (
|
|
236
|
-
//
|
|
237
|
-
const
|
|
238
|
-
if (
|
|
239
|
-
headText
|
|
240
|
-
|
|
241
|
-
|
|
315
|
+
if (node.type === 'template_string' || node.type === 'template_literal') {
|
|
316
|
+
// The first string_fragment contains the text before the first substitution
|
|
317
|
+
const headNode = node.children?.find(c => c.type === 'string_fragment');
|
|
318
|
+
if (headNode) {
|
|
319
|
+
const headText = rawText(headNode).toUpperCase();
|
|
320
|
+
if (SQL_KEYWORDS.some(kw => headText.includes(kw))) {
|
|
321
|
+
return rawText(headNode) + ' ...';
|
|
322
|
+
}
|
|
242
323
|
}
|
|
243
324
|
}
|
|
244
325
|
return null;
|
|
245
326
|
}
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
// SQL query analysis
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
246
330
|
function analyzeQuery(query, allTables, filePath) {
|
|
247
331
|
const violations = [];
|
|
248
332
|
const tables = [];
|
|
@@ -256,7 +340,7 @@ function analyzeQuery(query, allTables, filePath) {
|
|
|
256
340
|
type = 'update';
|
|
257
341
|
else if (queryUpper.includes('DELETE'))
|
|
258
342
|
type = 'delete';
|
|
259
|
-
// Extract table names (basic regex
|
|
343
|
+
// Extract table names (basic regex — could be enhanced)
|
|
260
344
|
const tableMatches = query.match(/(?:FROM|JOIN|INTO|UPDATE)\s+([a-zA-Z_][a-zA-Z0-9_]*)/gi);
|
|
261
345
|
if (tableMatches) {
|
|
262
346
|
for (const match of tableMatches) {
|
|
@@ -269,7 +353,7 @@ function analyzeQuery(query, allTables, filePath) {
|
|
|
269
353
|
schemaType: 'missing-reference',
|
|
270
354
|
tableName,
|
|
271
355
|
snippet: query.substring(0, 100),
|
|
272
|
-
suggestion: 'Load the appropriate database schema or check table name spelling'
|
|
356
|
+
suggestion: 'Load the appropriate database schema or check table name spelling',
|
|
273
357
|
});
|
|
274
358
|
}
|
|
275
359
|
else if (tableName) {
|
|
@@ -279,6 +363,9 @@ function analyzeQuery(query, allTables, filePath) {
|
|
|
279
363
|
}
|
|
280
364
|
return { violations, tables, type };
|
|
281
365
|
}
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// Validation checks
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
282
369
|
function checkMissingTableReferences(usages, allTables, filePath) {
|
|
283
370
|
const violations = [];
|
|
284
371
|
for (const usage of usages) {
|
|
@@ -290,7 +377,7 @@ function checkMissingTableReferences(usages, allTables, filePath) {
|
|
|
290
377
|
message: `Reference to unknown table '${usage.tableName}'`,
|
|
291
378
|
schemaType: 'missing-reference',
|
|
292
379
|
tableName: usage.tableName,
|
|
293
|
-
suggestion: 'Load the database schema that contains this table'
|
|
380
|
+
suggestion: 'Load the database schema that contains this table',
|
|
294
381
|
});
|
|
295
382
|
}
|
|
296
383
|
}
|
|
@@ -313,85 +400,157 @@ function validateQueryPatterns(usages, config, filePath) {
|
|
|
313
400
|
severity: 'suggestion',
|
|
314
401
|
message: `Function '${funcName}' has ${count} database queries (max recommended: ${config.maxQueriesPerFunction})`,
|
|
315
402
|
schemaType: 'missing-reference',
|
|
316
|
-
suggestion: 'Consider consolidating queries or extracting to a data access layer'
|
|
403
|
+
suggestion: 'Consider consolidating queries or extracting to a data access layer',
|
|
317
404
|
});
|
|
318
405
|
}
|
|
319
406
|
}
|
|
320
407
|
return violations;
|
|
321
408
|
}
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
322
410
|
// ORM-specific helper functions
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
/**
|
|
413
|
+
* Extract the table name from a Drizzle ORM call.
|
|
414
|
+
* Drizzle: pgTable('table_name', { ... })
|
|
415
|
+
*/
|
|
323
416
|
function extractTableNameFromDrizzle(node) {
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
417
|
+
// call_expression children: [expression, arguments]
|
|
418
|
+
const argsNode = findChild(node, 'arguments');
|
|
419
|
+
if (!argsNode)
|
|
420
|
+
return null;
|
|
421
|
+
// The first child of arguments that is a string literal
|
|
422
|
+
const stringChild = argsNode.children?.find(c => c.type === 'string');
|
|
423
|
+
if (stringChild) {
|
|
424
|
+
return stripQuotes(rawText(stringChild));
|
|
327
425
|
}
|
|
328
426
|
return null;
|
|
329
427
|
}
|
|
428
|
+
/**
|
|
429
|
+
* Get the decorator name from a call_expression inside a decorator.
|
|
430
|
+
* e.g. @Entity(...) → 'Entity'
|
|
431
|
+
*/
|
|
330
432
|
function getDecoratorName(node) {
|
|
331
|
-
|
|
332
|
-
|
|
433
|
+
// node is a call_expression inside a decorator
|
|
434
|
+
const identNode = findChild(node, 'identifier');
|
|
435
|
+
if (identNode) {
|
|
436
|
+
return rawText(identNode);
|
|
333
437
|
}
|
|
334
438
|
return null;
|
|
335
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Extract the table name from a TypeORM Entity decorator call.
|
|
442
|
+
* @Entity('table_name') or @Entity({ name: 'table_name' })
|
|
443
|
+
*/
|
|
336
444
|
function extractTableNameFromTypeORM(node) {
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
445
|
+
// node is a call_expression
|
|
446
|
+
const argsNode = findChild(node, 'arguments');
|
|
447
|
+
if (!argsNode)
|
|
448
|
+
return null;
|
|
449
|
+
// First non-bracket argument
|
|
450
|
+
const firstArg = argsNode.children?.find(c => c.type !== '(' && c.type !== ')');
|
|
451
|
+
if (!firstArg)
|
|
452
|
+
return null;
|
|
453
|
+
// @Entity('table_name')
|
|
454
|
+
if (firstArg.type === 'string') {
|
|
455
|
+
return stripQuotes(rawText(firstArg));
|
|
456
|
+
}
|
|
457
|
+
// @Entity({ name: 'table_name' })
|
|
458
|
+
if (firstArg.type === 'object') {
|
|
459
|
+
for (const prop of firstArg.children ?? []) {
|
|
460
|
+
if (prop.type === 'pair') {
|
|
461
|
+
// pair children: [key, :, value]
|
|
462
|
+
const keyNode = prop.children?.[0];
|
|
463
|
+
if (keyNode &&
|
|
464
|
+
(keyNode.type === 'identifier' || keyNode.type === 'string' || keyNode.type === 'property_identifier')) {
|
|
465
|
+
const keyText = stripQuotes(rawText(keyNode));
|
|
466
|
+
if (keyText === 'name') {
|
|
467
|
+
const valueNode = prop.children?.find(c => c.type === 'string');
|
|
468
|
+
if (valueNode) {
|
|
469
|
+
return stripQuotes(rawText(valueNode));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
351
472
|
}
|
|
352
473
|
}
|
|
353
474
|
}
|
|
354
475
|
}
|
|
355
476
|
return null;
|
|
356
477
|
}
|
|
478
|
+
/**
|
|
479
|
+
* Analyze a Mongoose Schema constructor for field-level references.
|
|
480
|
+
* Mongoose: new Schema({ field: { type: String, ref: 'OtherModel' } })
|
|
481
|
+
*/
|
|
357
482
|
function analyzeMongooseSchema(node, currentFunction) {
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
483
|
+
// node is a new_expression: children [new, expression, arguments]
|
|
484
|
+
const argsNode = findChild(node, 'arguments');
|
|
485
|
+
if (!argsNode)
|
|
486
|
+
return;
|
|
487
|
+
const schemaArg = argsNode.children?.find(c => c.type === 'object');
|
|
488
|
+
if (schemaArg) {
|
|
489
|
+
analyzeObjectForReferences(schemaArg, currentFunction);
|
|
365
490
|
}
|
|
366
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Recurse into an object literal looking for { ref: 'ModelName' } patterns.
|
|
494
|
+
*/
|
|
367
495
|
function analyzeObjectForReferences(obj, currentFunction) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
496
|
+
// obj is an 'object' literal
|
|
497
|
+
for (const prop of obj.children ?? []) {
|
|
498
|
+
if (prop.type !== 'pair')
|
|
499
|
+
continue;
|
|
500
|
+
// pair children: [key, :, value]
|
|
501
|
+
const valueNode = prop.children?.find(c => c.type === 'object');
|
|
502
|
+
if (!valueNode)
|
|
503
|
+
continue;
|
|
504
|
+
// Look for a nested 'ref' property
|
|
505
|
+
for (const nestedProp of valueNode.children ?? []) {
|
|
506
|
+
if (nestedProp.type !== 'pair')
|
|
507
|
+
continue;
|
|
508
|
+
const keyNode = nestedProp.children?.[0];
|
|
509
|
+
if (!keyNode)
|
|
510
|
+
continue;
|
|
511
|
+
const keyText = stripQuotes(rawText(keyNode));
|
|
512
|
+
if (keyText === 'ref') {
|
|
513
|
+
const refValueNode = nestedProp.children?.find(c => c.type === 'string');
|
|
514
|
+
if (refValueNode) {
|
|
515
|
+
const referencedModel = stripQuotes(rawText(refValueNode));
|
|
516
|
+
// NOTE: referencedModel is currently not added to schemaUsages
|
|
517
|
+
// because this helper does not have access to the parent scope's array.
|
|
518
|
+
// Future enhancement: track Mongoose ref relationships.
|
|
519
|
+
void referencedModel;
|
|
380
520
|
}
|
|
381
521
|
}
|
|
382
522
|
}
|
|
383
523
|
}
|
|
384
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Check if a member_expression looks like a Prisma client table access.
|
|
527
|
+
* prisma.user.findMany() — the top-level object is 'prisma'/'db'/'client'.
|
|
528
|
+
*/
|
|
385
529
|
function isPrismaBoundProperty(node) {
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
530
|
+
// node is a member_expression: children [object, ., property_identifier]
|
|
531
|
+
// For prisma.user.findMany(), the outer member_expression's object is another
|
|
532
|
+
// member_expression (prisma.user), whose object is the identifier 'prisma'.
|
|
533
|
+
// Find the nested member_expression that is the object of this one
|
|
534
|
+
const nestedME = node.children?.find(c => c.type === 'member_expression');
|
|
535
|
+
if (nestedME) {
|
|
536
|
+
// The object of the nested member_expression is the base identifier
|
|
537
|
+
const baseId = findChild(nestedME, 'identifier');
|
|
538
|
+
if (baseId) {
|
|
539
|
+
const baseName = rawText(baseId).toLowerCase();
|
|
540
|
+
return ['prisma', 'db', 'client'].includes(baseName);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// Also handle direct access like prisma.$queryRaw
|
|
544
|
+
const directId = findChild(node, 'identifier');
|
|
545
|
+
if (directId) {
|
|
546
|
+
const baseName = rawText(directId).toLowerCase();
|
|
547
|
+
return ['prisma', 'db', 'client'].includes(baseName);
|
|
392
548
|
}
|
|
393
549
|
return false;
|
|
394
550
|
}
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
552
|
+
// Analyzer Definition
|
|
553
|
+
// ---------------------------------------------------------------------------
|
|
395
554
|
/**
|
|
396
555
|
* Schema Analyzer Definition
|
|
397
556
|
*/
|