code-auditor-mcp 4.1.0 → 5.0.1
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +374 -0
- package/README.md +4 -4
- package/configs/hhra-compat.json +0 -8
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +7 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +2 -8
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +4 -4
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +2 -2
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +1 -1
- package/dist/analyzers/applicability.d.ts +42 -0
- package/dist/analyzers/applicability.d.ts.map +1 -1
- package/dist/analyzers/applicability.js +74 -0
- package/dist/analyzers/applicability.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +18 -24
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +30 -23
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +113 -76
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +2 -0
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +8 -3
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +210 -148
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +23 -28
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/handleIdentification.d.ts +356 -0
- package/dist/analyzers/handleIdentification.d.ts.map +1 -0
- package/dist/analyzers/handleIdentification.js +424 -0
- package/dist/analyzers/handleIdentification.js.map +1 -0
- package/dist/analyzers/manifestStaleness.d.ts +64 -0
- package/dist/analyzers/manifestStaleness.d.ts.map +1 -0
- package/dist/analyzers/manifestStaleness.js +115 -0
- package/dist/analyzers/manifestStaleness.js.map +1 -0
- package/dist/analyzers/orgFilterTiers.d.ts +73 -1
- package/dist/analyzers/orgFilterTiers.d.ts.map +1 -1
- package/dist/analyzers/orgFilterTiers.js +166 -1
- package/dist/analyzers/orgFilterTiers.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +251 -57
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +1109 -429
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +2 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +2 -0
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/receiverResolution.d.ts +183 -0
- package/dist/analyzers/receiverResolution.d.ts.map +1 -0
- package/dist/analyzers/receiverResolution.js +540 -0
- package/dist/analyzers/receiverResolution.js.map +1 -0
- package/dist/analyzers/receiverRoot.d.ts +328 -0
- package/dist/analyzers/receiverRoot.d.ts.map +1 -0
- package/dist/analyzers/receiverRoot.js +990 -0
- package/dist/analyzers/receiverRoot.js.map +1 -0
- package/dist/analyzers/ruleRegistry.d.ts +12 -68
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +32 -342
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +10 -0
- package/dist/analyzers/ruleTiming.d.ts.map +1 -1
- package/dist/analyzers/ruleTiming.js +20 -0
- package/dist/analyzers/ruleTiming.js.map +1 -1
- package/dist/analyzers/sqlLiteral.d.ts +76 -0
- package/dist/analyzers/sqlLiteral.d.ts.map +1 -0
- package/dist/analyzers/sqlLiteral.js +419 -0
- package/dist/analyzers/sqlLiteral.js.map +1 -0
- package/dist/analyzers/tsEcosystem.d.ts +115 -0
- package/dist/analyzers/tsEcosystem.d.ts.map +1 -0
- package/dist/analyzers/tsEcosystem.js +231 -0
- package/dist/analyzers/tsEcosystem.js.map +1 -0
- package/dist/analyzers/tsExpressionDescriptor.d.ts +156 -0
- package/dist/analyzers/tsExpressionDescriptor.d.ts.map +1 -0
- package/dist/analyzers/tsExpressionDescriptor.js +374 -0
- package/dist/analyzers/tsExpressionDescriptor.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +6 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +107 -44
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +1 -12
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +24 -101
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +287 -49
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +2130 -512
- 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 +2 -18
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -33
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +20 -143
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +4 -3
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +36 -13
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.js +6 -0
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts +15 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js +25 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +21 -32
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +166 -196
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/functionConcerns.d.ts +3 -0
- package/dist/analyzers/universal/functionConcerns.d.ts.map +1 -1
- package/dist/analyzers/universal/functionConcerns.js +4 -1
- package/dist/analyzers/universal/functionConcerns.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +310 -69
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js +709 -383
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
- package/dist/analyzers/universal/schema/config.d.ts +3 -14
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/config.js +3 -16
- package/dist/analyzers/universal/schema/config.js.map +1 -1
- package/dist/analyzers/universal/schema/discovery.d.ts +22 -1
- package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/discovery.js +36 -20
- package/dist/analyzers/universal/schema/discovery.js.map +1 -1
- package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/jsonSchema.js +13 -3
- package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -1
- package/dist/analyzers/universal/schema/migrations.d.ts +187 -27
- package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/migrations.js +443 -186
- package/dist/analyzers/universal/schema/migrations.js.map +1 -1
- package/dist/analyzers/universal/schema/types.d.ts +17 -3
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/violations.js +1 -1
- package/dist/analyzers/universal/schema/violations.js.map +1 -1
- package/dist/analyzers/universal/styleScale.d.ts +52 -0
- package/dist/analyzers/universal/styleScale.d.ts.map +1 -0
- package/dist/analyzers/universal/styleScale.js +95 -0
- package/dist/analyzers/universal/styleScale.js.map +1 -0
- package/dist/applyDataDirEnv.js +1 -1
- package/dist/applyDataDirEnv.js.map +1 -1
- package/dist/auditRouter.d.ts +26 -30
- package/dist/auditRouter.d.ts.map +1 -1
- package/dist/auditRouter.js +373 -143
- package/dist/auditRouter.js.map +1 -1
- package/dist/auditRunner.d.ts +64 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +1537 -1051
- package/dist/auditRunner.js.map +1 -1
- package/dist/baseline.d.ts +30 -3
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +70 -9
- package/dist/baseline.js.map +1 -1
- package/dist/cli.js +997 -812
- package/dist/cli.js.map +1 -1
- package/dist/codeIndex/analyzerConfig.d.ts +60 -0
- package/dist/codeIndex/analyzerConfig.d.ts.map +1 -0
- package/dist/codeIndex/analyzerConfig.js +98 -0
- package/dist/codeIndex/analyzerConfig.js.map +1 -0
- package/dist/codeIndex/auditResults.d.ts +39 -0
- package/dist/codeIndex/auditResults.d.ts.map +1 -0
- package/dist/codeIndex/auditResults.js +99 -0
- package/dist/codeIndex/auditResults.js.map +1 -0
- package/dist/codeIndex/codeMap.d.ts +59 -0
- package/dist/codeIndex/codeMap.d.ts.map +1 -0
- package/dist/codeIndex/codeMap.js +72 -0
- package/dist/codeIndex/codeMap.js.map +1 -0
- package/dist/codeIndex/conventions.d.ts +33 -0
- package/dist/codeIndex/conventions.d.ts.map +1 -0
- package/dist/codeIndex/conventions.js +84 -0
- package/dist/codeIndex/conventions.js.map +1 -0
- package/dist/codeIndex/coverage.d.ts +78 -0
- package/dist/codeIndex/coverage.d.ts.map +1 -0
- package/dist/codeIndex/coverage.js +164 -0
- package/dist/codeIndex/coverage.js.map +1 -0
- package/dist/codeIndex/dependencyGraph.d.ts +97 -0
- package/dist/codeIndex/dependencyGraph.d.ts.map +1 -0
- package/dist/codeIndex/dependencyGraph.js +241 -0
- package/dist/codeIndex/dependencyGraph.js.map +1 -0
- package/dist/codeIndex/functionIndex.d.ts +111 -0
- package/dist/codeIndex/functionIndex.d.ts.map +1 -0
- package/dist/codeIndex/functionIndex.js +306 -0
- package/dist/codeIndex/functionIndex.js.map +1 -0
- package/dist/codeIndex/inferredReceivers.d.ts +32 -0
- package/dist/codeIndex/inferredReceivers.d.ts.map +1 -0
- package/dist/codeIndex/inferredReceivers.js +41 -0
- package/dist/codeIndex/inferredReceivers.js.map +1 -0
- package/dist/codeIndex/meta.d.ts +30 -0
- package/dist/codeIndex/meta.d.ts.map +1 -0
- package/dist/codeIndex/meta.js +37 -0
- package/dist/codeIndex/meta.js.map +1 -0
- package/dist/codeIndex/migrations.d.ts +96 -0
- package/dist/codeIndex/migrations.d.ts.map +1 -0
- package/dist/codeIndex/migrations.js +1302 -0
- package/dist/codeIndex/migrations.js.map +1 -0
- package/dist/codeIndex/projectTasks.d.ts +54 -0
- package/dist/codeIndex/projectTasks.d.ts.map +1 -0
- package/dist/codeIndex/projectTasks.js +81 -0
- package/dist/codeIndex/projectTasks.js.map +1 -0
- package/dist/codeIndex/provenance.d.ts +47 -0
- package/dist/codeIndex/provenance.d.ts.map +1 -0
- package/dist/codeIndex/provenance.js +48 -0
- package/dist/codeIndex/provenance.js.map +1 -0
- package/dist/codeIndex/rawSql.d.ts +54 -0
- package/dist/codeIndex/rawSql.d.ts.map +1 -0
- package/dist/codeIndex/rawSql.js +71 -0
- package/dist/codeIndex/rawSql.js.map +1 -0
- package/dist/codeIndex/schema.d.ts +99 -0
- package/dist/codeIndex/schema.d.ts.map +1 -0
- package/dist/codeIndex/schema.js +174 -0
- package/dist/codeIndex/schema.js.map +1 -0
- package/dist/codeIndex/search.d.ts +40 -0
- package/dist/codeIndex/search.d.ts.map +1 -0
- package/dist/codeIndex/search.js +316 -0
- package/dist/codeIndex/search.js.map +1 -0
- package/dist/codeIndex/shared.d.ts +27 -0
- package/dist/codeIndex/shared.d.ts.map +1 -0
- package/dist/codeIndex/shared.js +46 -0
- package/dist/codeIndex/shared.js.map +1 -0
- package/dist/codeIndex/sqliteCollection.d.ts +81 -0
- package/dist/codeIndex/sqliteCollection.d.ts.map +1 -0
- package/dist/codeIndex/sqliteCollection.js +250 -0
- package/dist/codeIndex/sqliteCollection.js.map +1 -0
- package/dist/codeIndex/whitelist.d.ts +59 -0
- package/dist/codeIndex/whitelist.d.ts.map +1 -0
- package/dist/codeIndex/whitelist.js +188 -0
- package/dist/codeIndex/whitelist.js.map +1 -0
- package/dist/codeIndexDB.d.ts +130 -277
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +364 -2748
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/codeIndexService.d.ts +87 -12
- package/dist/codeIndexService.d.ts.map +1 -1
- package/dist/codeIndexService.js +110 -34
- package/dist/codeIndexService.js.map +1 -1
- package/dist/componentScanner.d.ts +35 -0
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +114 -80
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configGeneratePath.d.ts +4 -0
- package/dist/config/configGeneratePath.d.ts.map +1 -1
- package/dist/config/configGeneratePath.js +4 -0
- package/dist/config/configGeneratePath.js.map +1 -1
- package/dist/config/configLoader.d.ts +16 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +26 -23
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +11 -7
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +21 -20
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +17 -2
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +32 -25
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/config/lintConfigReader.d.ts +6 -0
- package/dist/config/lintConfigReader.d.ts.map +1 -1
- package/dist/config/lintConfigReader.js +6 -0
- package/dist/config/lintConfigReader.js.map +1 -1
- package/dist/config/staticObjectExtract.d.ts +4 -0
- package/dist/config/staticObjectExtract.d.ts.map +1 -1
- package/dist/config/staticObjectExtract.js +7 -3
- package/dist/config/staticObjectExtract.js.map +1 -1
- package/dist/config/thresholdRationales.d.ts +2 -0
- package/dist/config/thresholdRationales.d.ts.map +1 -1
- package/dist/config/thresholdRationales.js +5 -2
- package/dist/config/thresholdRationales.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -5
- package/dist/constants.js.map +1 -1
- package/dist/conventions/conventionMiner.d.ts +105 -2
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +334 -259
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/coverage/coverageService.js +4 -4
- package/dist/coverage/coverageService.js.map +1 -1
- package/dist/daemon/args.d.ts +3 -0
- package/dist/daemon/args.d.ts.map +1 -1
- package/dist/daemon/args.js +3 -0
- package/dist/daemon/args.js.map +1 -1
- package/dist/daemon/core.d.ts +33 -27
- package/dist/daemon/core.d.ts.map +1 -1
- package/dist/daemon/core.js +198 -162
- package/dist/daemon/core.js.map +1 -1
- package/dist/daemon/lspServer.d.ts +8 -0
- package/dist/daemon/lspServer.d.ts.map +1 -1
- package/dist/daemon/lspServer.js +8 -0
- package/dist/daemon/lspServer.js.map +1 -1
- package/dist/daemon/main.d.ts +2 -0
- package/dist/daemon/main.d.ts.map +1 -1
- package/dist/daemon/main.js +2 -0
- package/dist/daemon/main.js.map +1 -1
- package/dist/daemon/resolve.d.ts +22 -7
- package/dist/daemon/resolve.d.ts.map +1 -1
- package/dist/daemon/resolve.js +29 -8
- package/dist/daemon/resolve.js.map +1 -1
- package/dist/daemon/socketClient.d.ts +10 -1
- package/dist/daemon/socketClient.d.ts.map +1 -1
- package/dist/daemon/socketClient.js +10 -1
- package/dist/daemon/socketClient.js.map +1 -1
- package/dist/daemon/socketServer.d.ts +13 -0
- package/dist/daemon/socketServer.d.ts.map +1 -1
- package/dist/daemon/socketServer.js +13 -0
- package/dist/daemon/socketServer.js.map +1 -1
- package/dist/dataPaths.d.ts +27 -8
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +28 -44
- package/dist/dataPaths.js.map +1 -1
- package/dist/dismissals.d.ts +22 -1
- package/dist/dismissals.d.ts.map +1 -1
- package/dist/dismissals.js +22 -1
- package/dist/dismissals.js.map +1 -1
- package/dist/enforcement/gate.d.ts +1 -0
- package/dist/enforcement/gate.d.ts.map +1 -1
- package/dist/enforcement/gate.js +1 -0
- package/dist/enforcement/gate.js.map +1 -1
- package/dist/fingerprint.d.ts +6 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +46 -1
- package/dist/fingerprint.js.map +1 -1
- package/dist/functionScanner.d.ts +30 -1
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +258 -256
- package/dist/functionScanner.js.map +1 -1
- package/dist/generators/AWSQConfigGenerator.d.ts +23 -0
- package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AWSQConfigGenerator.js +23 -0
- package/dist/generators/AWSQConfigGenerator.js.map +1 -1
- package/dist/generators/BaseConfigGenerator.d.ts +7 -0
- package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.js +7 -0
- package/dist/generators/BaseConfigGenerator.js.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.d.ts +23 -0
- package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.js +23 -0
- package/dist/generators/ClaudeConfigGenerator.js.map +1 -1
- package/dist/generators/ClineConfigGenerator.d.ts +23 -0
- package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClineConfigGenerator.js +23 -0
- package/dist/generators/ClineConfigGenerator.js.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.d.ts +23 -0
- package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.js +23 -0
- package/dist/generators/CodeiumConfigGenerator.js.map +1 -1
- package/dist/generators/CodexConfigGenerator.d.ts +23 -0
- package/dist/generators/CodexConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodexConfigGenerator.js +23 -0
- package/dist/generators/CodexConfigGenerator.js.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.d.ts +9 -0
- package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.js +9 -0
- package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
- package/dist/generators/ContinueConfigGenerator.d.ts +23 -0
- package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ContinueConfigGenerator.js +23 -0
- package/dist/generators/ContinueConfigGenerator.js.map +1 -1
- package/dist/generators/CopilotConfigGenerator.d.ts +23 -0
- package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CopilotConfigGenerator.js +23 -0
- package/dist/generators/CopilotConfigGenerator.js.map +1 -1
- package/dist/generators/CursorConfigGenerator.d.ts +23 -0
- package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CursorConfigGenerator.js +23 -0
- package/dist/generators/CursorConfigGenerator.js.map +1 -1
- package/dist/generators/GeminiConfigGenerator.d.ts +23 -0
- package/dist/generators/GeminiConfigGenerator.d.ts.map +1 -1
- package/dist/generators/GeminiConfigGenerator.js +23 -0
- package/dist/generators/GeminiConfigGenerator.js.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.d.ts +23 -0
- package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.js +23 -0
- package/dist/generators/VSCodeConfigGenerator.js.map +1 -1
- package/dist/generators/ZCodeConfigGenerator.d.ts +23 -0
- package/dist/generators/ZCodeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ZCodeConfigGenerator.js +23 -0
- package/dist/generators/ZCodeConfigGenerator.js.map +1 -1
- package/dist/graph/blastRadius.d.ts.map +1 -1
- package/dist/graph/blastRadius.js +12 -7
- package/dist/graph/blastRadius.js.map +1 -1
- package/dist/graph/callGraph.d.ts +31 -0
- package/dist/graph/callGraph.d.ts.map +1 -1
- package/dist/graph/callGraph.js +62 -32
- package/dist/graph/callGraph.js.map +1 -1
- package/dist/graph/importClassification.d.ts +28 -0
- package/dist/graph/importClassification.d.ts.map +1 -1
- package/dist/graph/importClassification.js +57 -0
- package/dist/graph/importClassification.js.map +1 -1
- package/dist/graph/importGraph.d.ts +38 -0
- package/dist/graph/importGraph.d.ts.map +1 -1
- package/dist/graph/importGraph.js +38 -0
- package/dist/graph/importGraph.js.map +1 -1
- package/dist/graph/outputFormatter.d.ts +12 -4
- package/dist/graph/outputFormatter.d.ts.map +1 -1
- package/dist/graph/outputFormatter.js +12 -62
- package/dist/graph/outputFormatter.js.map +1 -1
- package/dist/hooks/codex.d.ts +8 -1
- package/dist/hooks/codex.d.ts.map +1 -1
- package/dist/hooks/codex.js +7 -0
- package/dist/hooks/codex.js.map +1 -1
- package/dist/hooks/core.d.ts +6 -0
- package/dist/hooks/core.d.ts.map +1 -1
- package/dist/hooks/core.js +6 -1
- package/dist/hooks/core.js.map +1 -1
- package/dist/hooks/cursor.d.ts +11 -1
- package/dist/hooks/cursor.d.ts.map +1 -1
- package/dist/hooks/cursor.js +10 -0
- package/dist/hooks/cursor.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/installConfig.d.ts +23 -5
- package/dist/installConfig.d.ts.map +1 -1
- package/dist/installConfig.js +23 -5
- package/dist/installConfig.js.map +1 -1
- package/dist/installer.d.ts +6 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +6 -0
- package/dist/installer.js.map +1 -1
- package/dist/invariants/ruleEngine.d.ts +3 -0
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +156 -119
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/invariants/ruleValidator.d.ts +3 -0
- package/dist/invariants/ruleValidator.d.ts.map +1 -1
- package/dist/invariants/ruleValidator.js +185 -178
- package/dist/invariants/ruleValidator.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +4 -1
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +8 -19
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/RuntimeManager.d.ts +8 -0
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +55 -5
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +1 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +4 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +37 -3
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +11 -14
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/go/analyzer-darwin-amd64 +0 -0
- package/dist/languages/go/analyzer-darwin-arm64 +0 -0
- package/dist/languages/go/analyzer-linux-amd64 +0 -0
- package/dist/languages/go/analyzer-linux-arm64 +0 -0
- package/dist/languages/go/analyzer-src/analyzer.go +2 -0
- package/dist/languages/go/analyzer-src/dataaccess.go +388 -0
- package/dist/languages/go/analyzer-src/types.go +8 -0
- package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
- package/dist/languages/go/database-packages.json +8 -0
- package/dist/languages/go/goResolution.d.ts +229 -0
- package/dist/languages/go/goResolution.d.ts.map +1 -0
- package/dist/languages/go/goResolution.js +799 -0
- package/dist/languages/go/goResolution.js.map +1 -0
- package/dist/languages/go/stdlib-packages.json +353 -0
- package/dist/languages/go/tenantInputs.d.ts +37 -0
- package/dist/languages/go/tenantInputs.d.ts.map +1 -0
- package/dist/languages/go/tenantInputs.js +121 -0
- package/dist/languages/go/tenantInputs.js.map +1 -0
- package/dist/languages/index.d.ts +1 -0
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +5 -0
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/json/JsonAdapter.d.ts +177 -0
- package/dist/languages/json/JsonAdapter.d.ts.map +1 -0
- package/dist/languages/json/JsonAdapter.js +531 -0
- package/dist/languages/json/JsonAdapter.js.map +1 -0
- package/dist/languages/locationIndex.d.ts +41 -0
- package/dist/languages/locationIndex.d.ts.map +1 -0
- package/dist/languages/locationIndex.js +42 -0
- package/dist/languages/locationIndex.js.map +1 -0
- package/dist/languages/sql/dialectDetection.d.ts +57 -0
- package/dist/languages/sql/dialectDetection.d.ts.map +1 -0
- package/dist/languages/sql/dialectDetection.js +135 -0
- package/dist/languages/sql/dialectDetection.js.map +1 -0
- package/dist/languages/sql/sqlAst.d.ts +494 -0
- package/dist/languages/sql/sqlAst.d.ts.map +1 -0
- package/dist/languages/sql/sqlAst.js +1817 -0
- package/dist/languages/sql/sqlAst.js.map +1 -0
- package/dist/languages/testConventions.d.ts +12 -1
- package/dist/languages/testConventions.d.ts.map +1 -1
- package/dist/languages/testConventions.js +13 -1
- package/dist/languages/testConventions.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/converter.js +2 -2
- package/dist/languages/tree-sitter/converter.js.map +1 -1
- package/dist/languages/tree-sitter/parser.d.ts +3 -4
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +10 -4
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/tree-sitter/rawNode.d.ts +3 -0
- package/dist/languages/tree-sitter/rawNode.d.ts.map +1 -1
- package/dist/languages/tree-sitter/rawNode.js +3 -0
- package/dist/languages/tree-sitter/rawNode.js.map +1 -1
- package/dist/languages/types.d.ts +7 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +36 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +168 -45
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/languages/typescript/database-packages.json +19 -0
- package/dist/ledger.d.ts +118 -2
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +167 -45
- package/dist/ledger.js.map +1 -1
- package/dist/legacyIndexNotice.d.ts +32 -0
- package/dist/legacyIndexNotice.d.ts.map +1 -0
- package/dist/legacyIndexNotice.js +89 -0
- package/dist/legacyIndexNotice.js.map +1 -0
- package/dist/mcp-tools/discoveryQueries.d.ts +55 -0
- package/dist/mcp-tools/discoveryQueries.d.ts.map +1 -0
- package/dist/mcp-tools/discoveryQueries.js +157 -0
- package/dist/mcp-tools/discoveryQueries.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts +9 -123
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +180 -170
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +20 -0
- package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
- package/dist/mcp-tools/whitelistTools.js +20 -0
- package/dist/mcp-tools/whitelistTools.js.map +1 -1
- package/dist/mcp-tools/workflowGuide.d.ts +9 -0
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +13 -5
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts +78 -0
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +203 -340
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.d.ts +2 -0
- package/dist/mcp-ui-simple.d.ts.map +1 -1
- package/dist/mcp-ui-simple.js +12 -5
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +6 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +832 -851
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +29 -33
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +102 -791
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpAutoIndex.d.ts +5 -0
- package/dist/mcpAutoIndex.d.ts.map +1 -1
- package/dist/mcpAutoIndex.js +7 -2
- package/dist/mcpAutoIndex.js.map +1 -1
- package/dist/mcpDiagnostics.d.ts +7 -0
- package/dist/mcpDiagnostics.d.ts.map +1 -1
- package/dist/mcpDiagnostics.js +34 -9
- package/dist/mcpDiagnostics.js.map +1 -1
- package/dist/mcpToolErrors.d.ts +16 -0
- package/dist/mcpToolErrors.d.ts.map +1 -1
- package/dist/mcpToolErrors.js +16 -0
- package/dist/mcpToolErrors.js.map +1 -1
- package/dist/nextFile.d.ts +6 -0
- package/dist/nextFile.d.ts.map +1 -1
- package/dist/nextFile.js +6 -0
- package/dist/nextFile.js.map +1 -1
- package/dist/nextFileIncremental.d.ts +44 -4
- package/dist/nextFileIncremental.d.ts.map +1 -1
- package/dist/nextFileIncremental.js +47 -7
- package/dist/nextFileIncremental.js.map +1 -1
- package/dist/phase/batchFunctions.d.ts +38 -0
- package/dist/phase/batchFunctions.d.ts.map +1 -0
- package/dist/phase/batchFunctions.js +64 -0
- package/dist/phase/batchFunctions.js.map +1 -0
- package/dist/phase/checks.d.ts +39 -0
- package/dist/phase/checks.d.ts.map +1 -0
- package/dist/phase/checks.js +48 -0
- package/dist/phase/checks.js.map +1 -0
- package/dist/phase/codeBlocks.d.ts +25 -0
- package/dist/phase/codeBlocks.d.ts.map +1 -0
- package/dist/phase/codeBlocks.js +268 -0
- package/dist/phase/codeBlocks.js.map +1 -0
- package/dist/phase/colorMath.d.ts +41 -0
- package/dist/phase/colorMath.d.ts.map +1 -0
- package/dist/phase/colorMath.js +109 -0
- package/dist/phase/colorMath.js.map +1 -0
- package/dist/phase/config.d.ts +29 -0
- package/dist/phase/config.d.ts.map +1 -0
- package/dist/phase/config.js +54 -0
- package/dist/phase/config.js.map +1 -0
- package/dist/phase/consumed.d.ts +47 -0
- package/dist/phase/consumed.d.ts.map +1 -0
- package/dist/phase/consumed.js +22 -0
- package/dist/phase/consumed.js.map +1 -0
- package/dist/phase/conventionMining.d.ts +59 -0
- package/dist/phase/conventionMining.d.ts.map +1 -0
- package/dist/phase/conventionMining.js +152 -0
- package/dist/phase/conventionMining.js.map +1 -0
- package/dist/phase/coverage.d.ts +65 -0
- package/dist/phase/coverage.d.ts.map +1 -0
- package/dist/phase/coverage.js +121 -0
- package/dist/phase/coverage.js.map +1 -0
- package/dist/phase/dag.d.ts +49 -0
- package/dist/phase/dag.d.ts.map +1 -0
- package/dist/phase/dag.js +120 -0
- package/dist/phase/dag.js.map +1 -0
- package/dist/phase/dataAccessCalls.d.ts +33 -0
- package/dist/phase/dataAccessCalls.d.ts.map +1 -0
- package/dist/phase/dataAccessCalls.js +42 -0
- package/dist/phase/dataAccessCalls.js.map +1 -0
- package/dist/phase/dataAccessCallsCandidates.d.ts +25 -0
- package/dist/phase/dataAccessCallsCandidates.d.ts.map +1 -0
- package/dist/phase/dataAccessCallsCandidates.js +27 -0
- package/dist/phase/dataAccessCallsCandidates.js.map +1 -0
- package/dist/phase/dynamicSql.d.ts +18 -0
- package/dist/phase/dynamicSql.d.ts.map +1 -0
- package/dist/phase/dynamicSql.js +20 -0
- package/dist/phase/dynamicSql.js.map +1 -0
- package/dist/phase/exportForm.d.ts +23 -0
- package/dist/phase/exportForm.d.ts.map +1 -0
- package/dist/phase/exportForm.js +29 -0
- package/dist/phase/exportForm.js.map +1 -0
- package/dist/phase/exportSymbols.d.ts +21 -0
- package/dist/phase/exportSymbols.d.ts.map +1 -0
- package/dist/phase/exportSymbols.js +28 -0
- package/dist/phase/exportSymbols.js.map +1 -0
- package/dist/phase/fileHeader.d.ts +15 -0
- package/dist/phase/fileHeader.d.ts.map +1 -0
- package/dist/phase/fileHeader.js +33 -0
- package/dist/phase/fileHeader.js.map +1 -0
- package/dist/phase/fileImports.d.ts +39 -0
- package/dist/phase/fileImports.d.ts.map +1 -0
- package/dist/phase/fileImports.js +138 -0
- package/dist/phase/fileImports.js.map +1 -0
- package/dist/phase/fileSymbols.d.ts +25 -0
- package/dist/phase/fileSymbols.d.ts.map +1 -0
- package/dist/phase/fileSymbols.js +302 -0
- package/dist/phase/fileSymbols.js.map +1 -0
- package/dist/phase/functionBodies.d.ts +28 -0
- package/dist/phase/functionBodies.d.ts.map +1 -0
- package/dist/phase/functionBodies.js +67 -0
- package/dist/phase/functionBodies.js.map +1 -0
- package/dist/phase/functionIndex.d.ts +28 -0
- package/dist/phase/functionIndex.d.ts.map +1 -0
- package/dist/phase/functionIndex.js +152 -0
- package/dist/phase/functionIndex.js.map +1 -0
- package/dist/phase/goFunctionAnalysis.d.ts +47 -0
- package/dist/phase/goFunctionAnalysis.d.ts.map +1 -0
- package/dist/phase/goFunctionAnalysis.js +291 -0
- package/dist/phase/goFunctionAnalysis.js.map +1 -0
- package/dist/phase/goFunctions.d.ts +38 -0
- package/dist/phase/goFunctions.d.ts.map +1 -0
- package/dist/phase/goFunctions.js +154 -0
- package/dist/phase/goFunctions.js.map +1 -0
- package/dist/phase/goImports.d.ts +29 -0
- package/dist/phase/goImports.d.ts.map +1 -0
- package/dist/phase/goImports.js +68 -0
- package/dist/phase/goImports.js.map +1 -0
- package/dist/phase/goPackageBindings.d.ts +22 -0
- package/dist/phase/goPackageBindings.d.ts.map +1 -0
- package/dist/phase/goPackageBindings.js +30 -0
- package/dist/phase/goPackageBindings.js.map +1 -0
- package/dist/phase/goSwitches.d.ts +28 -0
- package/dist/phase/goSwitches.d.ts.map +1 -0
- package/dist/phase/goSwitches.js +52 -0
- package/dist/phase/goSwitches.js.map +1 -0
- package/dist/phase/importForm.d.ts +20 -0
- package/dist/phase/importForm.d.ts.map +1 -0
- package/dist/phase/importForm.js +27 -0
- package/dist/phase/importForm.js.map +1 -0
- package/dist/phase/importSpecifiers.d.ts +20 -0
- package/dist/phase/importSpecifiers.d.ts.map +1 -0
- package/dist/phase/importSpecifiers.js +30 -0
- package/dist/phase/importSpecifiers.js.map +1 -0
- package/dist/phase/imports.d.ts +18 -0
- package/dist/phase/imports.d.ts.map +1 -0
- package/dist/phase/imports.js +25 -0
- package/dist/phase/imports.js.map +1 -0
- package/dist/phase/jsonDocument.d.ts +26 -0
- package/dist/phase/jsonDocument.d.ts.map +1 -0
- package/dist/phase/jsonDocument.js +36 -0
- package/dist/phase/jsonDocument.js.map +1 -0
- package/dist/phase/localBinding.d.ts +33 -0
- package/dist/phase/localBinding.d.ts.map +1 -0
- package/dist/phase/localBinding.js +208 -0
- package/dist/phase/localBinding.js.map +1 -0
- package/dist/phase/loopQueries.d.ts +31 -0
- package/dist/phase/loopQueries.d.ts.map +1 -0
- package/dist/phase/loopQueries.js +44 -0
- package/dist/phase/loopQueries.js.map +1 -0
- package/dist/phase/loopQueryCandidates.d.ts +26 -0
- package/dist/phase/loopQueryCandidates.d.ts.map +1 -0
- package/dist/phase/loopQueryCandidates.js +28 -0
- package/dist/phase/loopQueryCandidates.js.map +1 -0
- package/dist/phase/oracles.d.ts +216 -0
- package/dist/phase/oracles.d.ts.map +1 -0
- package/dist/phase/oracles.js +484 -0
- package/dist/phase/oracles.js.map +1 -0
- package/dist/phase/phaseModel.d.ts +190 -0
- package/dist/phase/phaseModel.d.ts.map +1 -0
- package/dist/phase/phaseModel.js +468 -0
- package/dist/phase/phaseModel.js.map +1 -0
- package/dist/phase/producers.d.ts +450 -0
- package/dist/phase/producers.d.ts.map +1 -0
- package/dist/phase/producers.js +1027 -0
- package/dist/phase/producers.js.map +1 -0
- package/dist/phase/querySiteCandidates.d.ts +27 -0
- package/dist/phase/querySiteCandidates.d.ts.map +1 -0
- package/dist/phase/querySiteCandidates.js +66 -0
- package/dist/phase/querySiteCandidates.js.map +1 -0
- package/dist/phase/querySites.d.ts +30 -0
- package/dist/phase/querySites.d.ts.map +1 -0
- package/dist/phase/querySites.js +93 -0
- package/dist/phase/querySites.js.map +1 -0
- package/dist/phase/reachability.d.ts +61 -0
- package/dist/phase/reachability.d.ts.map +1 -0
- package/dist/phase/reachability.js +104 -0
- package/dist/phase/reachability.js.map +1 -0
- package/dist/phase/reactComponents.d.ts +33 -0
- package/dist/phase/reactComponents.d.ts.map +1 -0
- package/dist/phase/reactComponents.js +47 -0
- package/dist/phase/reactComponents.js.map +1 -0
- package/dist/phase/receiverActivity.d.ts +35 -0
- package/dist/phase/receiverActivity.d.ts.map +1 -0
- package/dist/phase/receiverActivity.js +66 -0
- package/dist/phase/receiverActivity.js.map +1 -0
- package/dist/phase/receiverConsumers.d.ts +200 -0
- package/dist/phase/receiverConsumers.d.ts.map +1 -0
- package/dist/phase/receiverConsumers.js +1025 -0
- package/dist/phase/receiverConsumers.js.map +1 -0
- package/dist/phase/receiverProvenance.d.ts +97 -0
- package/dist/phase/receiverProvenance.d.ts.map +1 -0
- package/dist/phase/receiverProvenance.js +337 -0
- package/dist/phase/receiverProvenance.js.map +1 -0
- package/dist/phase/routing.d.ts +47 -0
- package/dist/phase/routing.d.ts.map +1 -0
- package/dist/phase/routing.js +62 -0
- package/dist/phase/routing.js.map +1 -0
- package/dist/phase/rules/conventions.d.ts +61 -0
- package/dist/phase/rules/conventions.d.ts.map +1 -0
- package/dist/phase/rules/conventions.js +403 -0
- package/dist/phase/rules/conventions.js.map +1 -0
- package/dist/phase/rules/crossDomain.d.ts +102 -0
- package/dist/phase/rules/crossDomain.d.ts.map +1 -0
- package/dist/phase/rules/crossDomain.js +918 -0
- package/dist/phase/rules/crossDomain.js.map +1 -0
- package/dist/phase/rules/dataAccess.d.ts +62 -0
- package/dist/phase/rules/dataAccess.d.ts.map +1 -0
- package/dist/phase/rules/dataAccess.js +480 -0
- package/dist/phase/rules/dataAccess.js.map +1 -0
- package/dist/phase/rules/dependencyGraph.d.ts +44 -0
- package/dist/phase/rules/dependencyGraph.d.ts.map +1 -0
- package/dist/phase/rules/dependencyGraph.js +260 -0
- package/dist/phase/rules/dependencyGraph.js.map +1 -0
- package/dist/phase/rules/documentation.d.ts +45 -0
- package/dist/phase/rules/documentation.d.ts.map +1 -0
- package/dist/phase/rules/documentation.js +340 -0
- package/dist/phase/rules/documentation.js.map +1 -0
- package/dist/phase/rules/dry.d.ts +109 -0
- package/dist/phase/rules/dry.d.ts.map +1 -0
- package/dist/phase/rules/dry.js +803 -0
- package/dist/phase/rules/dry.js.map +1 -0
- package/dist/phase/rules/fileDocumentation.d.ts +36 -0
- package/dist/phase/rules/fileDocumentation.d.ts.map +1 -0
- package/dist/phase/rules/fileDocumentation.js +134 -0
- package/dist/phase/rules/fileDocumentation.js.map +1 -0
- package/dist/phase/rules/functionBodies.d.ts +33 -0
- package/dist/phase/rules/functionBodies.d.ts.map +1 -0
- package/dist/phase/rules/functionBodies.js +71 -0
- package/dist/phase/rules/functionBodies.js.map +1 -0
- package/dist/phase/rules/goRules.d.ts +49 -0
- package/dist/phase/rules/goRules.d.ts.map +1 -0
- package/dist/phase/rules/goRules.js +209 -0
- package/dist/phase/rules/goRules.js.map +1 -0
- package/dist/phase/rules/querySites.d.ts +29 -0
- package/dist/phase/rules/querySites.d.ts.map +1 -0
- package/dist/phase/rules/querySites.js +94 -0
- package/dist/phase/rules/querySites.js.map +1 -0
- package/dist/phase/rules/react.d.ts +38 -0
- package/dist/phase/rules/react.d.ts.map +1 -0
- package/dist/phase/rules/react.js +202 -0
- package/dist/phase/rules/react.js.map +1 -0
- package/dist/phase/rules/registry.d.ts +170 -0
- package/dist/phase/rules/registry.d.ts.map +1 -0
- package/dist/phase/rules/registry.js +103 -0
- package/dist/phase/rules/registry.js.map +1 -0
- package/dist/phase/rules/schema.d.ts +79 -0
- package/dist/phase/rules/schema.d.ts.map +1 -0
- package/dist/phase/rules/schema.js +273 -0
- package/dist/phase/rules/schema.js.map +1 -0
- package/dist/phase/rules/schemaJson.d.ts +32 -0
- package/dist/phase/rules/schemaJson.d.ts.map +1 -0
- package/dist/phase/rules/schemaJson.js +81 -0
- package/dist/phase/rules/schemaJson.js.map +1 -0
- package/dist/phase/rules/schemaValidator.d.ts +38 -0
- package/dist/phase/rules/schemaValidator.d.ts.map +1 -0
- package/dist/phase/rules/schemaValidator.js +107 -0
- package/dist/phase/rules/schemaValidator.js.map +1 -0
- package/dist/phase/rules/secrets.d.ts +34 -0
- package/dist/phase/rules/secrets.d.ts.map +1 -0
- package/dist/phase/rules/secrets.js +208 -0
- package/dist/phase/rules/secrets.js.map +1 -0
- package/dist/phase/rules/security.d.ts +30 -0
- package/dist/phase/rules/security.d.ts.map +1 -0
- package/dist/phase/rules/security.js +85 -0
- package/dist/phase/rules/security.js.map +1 -0
- package/dist/phase/rules/securityDefects.d.ts +37 -0
- package/dist/phase/rules/securityDefects.d.ts.map +1 -0
- package/dist/phase/rules/securityDefects.js +167 -0
- package/dist/phase/rules/securityDefects.js.map +1 -0
- package/dist/phase/rules/solid.d.ts +61 -0
- package/dist/phase/rules/solid.d.ts.map +1 -0
- package/dist/phase/rules/solid.js +562 -0
- package/dist/phase/rules/solid.js.map +1 -0
- package/dist/phase/rules/styles.d.ts +77 -0
- package/dist/phase/rules/styles.d.ts.map +1 -0
- package/dist/phase/rules/styles.js +868 -0
- package/dist/phase/rules/styles.js.map +1 -0
- package/dist/phase/rules/unreferencedModule.d.ts +26 -0
- package/dist/phase/rules/unreferencedModule.d.ts.map +1 -0
- package/dist/phase/rules/unreferencedModule.js +61 -0
- package/dist/phase/rules/unreferencedModule.js.map +1 -0
- package/dist/phase/runner.d.ts +549 -0
- package/dist/phase/runner.d.ts.map +1 -0
- package/dist/phase/runner.js +1098 -0
- package/dist/phase/runner.js.map +1 -0
- package/dist/phase/schemaCode.d.ts +34 -0
- package/dist/phase/schemaCode.d.ts.map +1 -0
- package/dist/phase/schemaCode.js +53 -0
- package/dist/phase/schemaCode.js.map +1 -0
- package/dist/phase/schemaObjects.d.ts +37 -0
- package/dist/phase/schemaObjects.d.ts.map +1 -0
- package/dist/phase/schemaObjects.js +139 -0
- package/dist/phase/schemaObjects.js.map +1 -0
- package/dist/phase/schemaUsage.d.ts +30 -0
- package/dist/phase/schemaUsage.d.ts.map +1 -0
- package/dist/phase/schemaUsage.js +99 -0
- package/dist/phase/schemaUsage.js.map +1 -0
- package/dist/phase/schemaUsageCandidates.d.ts +32 -0
- package/dist/phase/schemaUsageCandidates.d.ts.map +1 -0
- package/dist/phase/schemaUsageCandidates.js +165 -0
- package/dist/phase/schemaUsageCandidates.js.map +1 -0
- package/dist/phase/schemaValidations.d.ts +34 -0
- package/dist/phase/schemaValidations.d.ts.map +1 -0
- package/dist/phase/schemaValidations.js +53 -0
- package/dist/phase/schemaValidations.js.map +1 -0
- package/dist/phase/secretCandidates.d.ts +28 -0
- package/dist/phase/secretCandidates.d.ts.map +1 -0
- package/dist/phase/secretCandidates.js +132 -0
- package/dist/phase/secretCandidates.js.map +1 -0
- package/dist/phase/securityCandidates.d.ts +29 -0
- package/dist/phase/securityCandidates.d.ts.map +1 -0
- package/dist/phase/securityCandidates.js +469 -0
- package/dist/phase/securityCandidates.js.map +1 -0
- package/dist/phase/seeded-defects.d.ts +18 -0
- package/dist/phase/seeded-defects.d.ts.map +1 -0
- package/dist/phase/seeded-defects.js +61 -0
- package/dist/phase/seeded-defects.js.map +1 -0
- package/dist/phase/stringLiterals.d.ts +24 -0
- package/dist/phase/stringLiterals.d.ts.map +1 -0
- package/dist/phase/stringLiterals.js +35 -0
- package/dist/phase/stringLiterals.js.map +1 -0
- package/dist/phase/stylesCss.d.ts +25 -0
- package/dist/phase/stylesCss.d.ts.map +1 -0
- package/dist/phase/stylesCss.js +34 -0
- package/dist/phase/stylesCss.js.map +1 -0
- package/dist/phase/stylesMarkup.d.ts +31 -0
- package/dist/phase/stylesMarkup.d.ts.map +1 -0
- package/dist/phase/stylesMarkup.js +48 -0
- package/dist/phase/stylesMarkup.js.map +1 -0
- package/dist/phase/stylesSource.d.ts +36 -0
- package/dist/phase/stylesSource.d.ts.map +1 -0
- package/dist/phase/stylesSource.js +50 -0
- package/dist/phase/stylesSource.js.map +1 -0
- package/dist/phase/typeDeclarations.d.ts +33 -0
- package/dist/phase/typeDeclarations.d.ts.map +1 -0
- package/dist/phase/typeDeclarations.js +76 -0
- package/dist/phase/typeDeclarations.js.map +1 -0
- package/dist/phase/types.d.ts +2191 -0
- package/dist/phase/types.d.ts.map +1 -0
- package/dist/phase/types.js +27 -0
- package/dist/phase/types.js.map +1 -0
- package/dist/phase/withinFileProvenance.d.ts +50 -0
- package/dist/phase/withinFileProvenance.d.ts.map +1 -0
- package/dist/phase/withinFileProvenance.js +112 -0
- package/dist/phase/withinFileProvenance.js.map +1 -0
- package/dist/pipeline.d.ts +37 -30
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +131 -326
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +104 -21
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +1145 -988
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/pipelineTypes.d.ts +21 -0
- package/dist/pipelineTypes.d.ts.map +1 -1
- package/dist/pipelineTypes.js +21 -10
- package/dist/pipelineTypes.js.map +1 -1
- package/dist/presets/presets.d.ts +9 -2
- package/dist/presets/presets.d.ts.map +1 -1
- package/dist/presets/presets.js +15 -26
- package/dist/presets/presets.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts +14 -3
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +14 -3
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts +5 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +5 -1
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts +9 -2
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +19 -5
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/reportGenerator.d.ts +9 -2
- package/dist/reporting/reportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.js +9 -2
- package/dist/reporting/reportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts +14 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +14 -0
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/reporting/sizeDistribution.d.ts +19 -2
- package/dist/reporting/sizeDistribution.d.ts.map +1 -1
- package/dist/reporting/sizeDistribution.js +23 -5
- package/dist/reporting/sizeDistribution.js.map +1 -1
- package/dist/ruleAliases.d.ts +6 -8
- package/dist/ruleAliases.d.ts.map +1 -1
- package/dist/ruleAliases.js +29 -4
- package/dist/ruleAliases.js.map +1 -1
- package/dist/search/QueryParser.d.ts +1 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +32 -91
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts +19 -20
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +214 -194
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/services/ProjectTaskRepository.d.ts +65 -3
- package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
- package/dist/services/ProjectTaskRepository.js +94 -25
- package/dist/services/ProjectTaskRepository.js.map +1 -1
- package/dist/services/auditJobService.d.ts +17 -1
- package/dist/services/auditJobService.d.ts.map +1 -1
- package/dist/services/auditJobService.js +16 -0
- package/dist/services/auditJobService.js.map +1 -1
- package/dist/services/fileAccounting.d.ts +43 -5
- package/dist/services/fileAccounting.d.ts.map +1 -1
- package/dist/services/fileAccounting.js +48 -9
- package/dist/services/fileAccounting.js.map +1 -1
- package/dist/services/projectTaskHelpers.d.ts +45 -0
- package/dist/services/projectTaskHelpers.d.ts.map +1 -1
- package/dist/services/projectTaskHelpers.js +45 -0
- package/dist/services/projectTaskHelpers.js.map +1 -1
- package/dist/services/whitelistService.d.ts +40 -5
- package/dist/services/whitelistService.d.ts.map +1 -1
- package/dist/services/whitelistService.js +87 -46
- package/dist/services/whitelistService.js.map +1 -1
- package/dist/sqlite/betterSqlite3.d.ts +35 -1
- package/dist/sqlite/betterSqlite3.d.ts.map +1 -1
- package/dist/sqlite/betterSqlite3.js +35 -1
- package/dist/sqlite/betterSqlite3.js.map +1 -1
- package/dist/sqlite/driver.d.ts +23 -0
- package/dist/sqlite/driver.d.ts.map +1 -1
- package/dist/sqlite/driver.js +23 -0
- package/dist/sqlite/driver.js.map +1 -1
- package/dist/sqlite/nodeSqlite.d.ts +35 -1
- package/dist/sqlite/nodeSqlite.d.ts.map +1 -1
- package/dist/sqlite/nodeSqlite.js +35 -1
- package/dist/sqlite/nodeSqlite.js.map +1 -1
- package/dist/structuralSignature.d.ts +9 -0
- package/dist/structuralSignature.d.ts.map +1 -1
- package/dist/structuralSignature.js +9 -0
- package/dist/structuralSignature.js.map +1 -1
- package/dist/styles/cssAstExtractor.d.ts +15 -0
- package/dist/styles/cssAstExtractor.d.ts.map +1 -1
- package/dist/styles/cssAstExtractor.js +15 -0
- package/dist/styles/cssAstExtractor.js.map +1 -1
- package/dist/styles/normalizer.d.ts +6 -0
- package/dist/styles/normalizer.d.ts.map +1 -1
- package/dist/styles/normalizer.js +6 -0
- package/dist/styles/normalizer.js.map +1 -1
- package/dist/styles/styleExtractor.d.ts +29 -2
- package/dist/styles/styleExtractor.d.ts.map +1 -1
- package/dist/styles/styleExtractor.js +67 -48
- package/dist/styles/styleExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts +8 -0
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +135 -138
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindConfigLoader.d.ts +12 -0
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
- package/dist/styles/tailwindConfigLoader.js +12 -0
- package/dist/styles/tailwindConfigLoader.js.map +1 -1
- package/dist/styles/tailwindExpander.d.ts +3 -0
- package/dist/styles/tailwindExpander.d.ts.map +1 -1
- package/dist/styles/tailwindExpander.js +8 -4
- package/dist/styles/tailwindExpander.js.map +1 -1
- package/dist/styles/tailwindProbe.d.ts +9 -0
- package/dist/styles/tailwindProbe.d.ts.map +1 -1
- package/dist/styles/tailwindProbe.js +181 -166
- package/dist/styles/tailwindProbe.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts +13 -0
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +13 -0
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/symbols.d.ts +13 -11
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +14 -21
- package/dist/symbols.js.map +1 -1
- package/dist/telemetry.d.ts +31 -3
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +31 -3
- package/dist/telemetry.js.map +1 -1
- package/dist/tool-registry.d.ts +37 -1
- package/dist/tool-registry.d.ts.map +1 -1
- package/dist/tool-registry.js +37 -1
- package/dist/tool-registry.js.map +1 -1
- package/dist/types.d.ts +106 -92
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -16
- package/dist/types.js.map +1 -1
- package/dist/ui/InteractivePrompts.d.ts +12 -0
- package/dist/ui/InteractivePrompts.d.ts.map +1 -1
- package/dist/ui/InteractivePrompts.js +12 -0
- package/dist/ui/InteractivePrompts.js.map +1 -1
- package/dist/utils/astParser.d.ts +15 -0
- package/dist/utils/astParser.d.ts.map +1 -1
- package/dist/utils/astParser.js +15 -0
- package/dist/utils/astParser.js.map +1 -1
- package/dist/utils/astUtils.d.ts +60 -0
- package/dist/utils/astUtils.d.ts.map +1 -1
- package/dist/utils/astUtils.js +350 -362
- package/dist/utils/astUtils.js.map +1 -1
- package/dist/utils/contentHash.d.ts +22 -0
- package/dist/utils/contentHash.d.ts.map +1 -0
- package/dist/utils/contentHash.js +26 -0
- package/dist/utils/contentHash.js.map +1 -0
- package/dist/utils/dependencyExtractor.d.ts +28 -0
- package/dist/utils/dependencyExtractor.d.ts.map +1 -1
- package/dist/utils/dependencyExtractor.js +28 -1
- package/dist/utils/dependencyExtractor.js.map +1 -1
- package/dist/utils/errorMessage.d.ts +10 -0
- package/dist/utils/errorMessage.d.ts.map +1 -0
- package/dist/utils/errorMessage.js +12 -0
- package/dist/utils/errorMessage.js.map +1 -0
- package/dist/utils/fileDiscovery.d.ts +61 -2
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +131 -23
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/reactDetection.d.ts +34 -5
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +73 -52
- package/dist/utils/reactDetection.js.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/workers/auditWorker.js +7 -3
- package/dist/workers/auditWorker.js.map +1 -1
- package/examples/.auditrc.example.json +0 -8
- package/examples/.auditrc.json +0 -11
- package/examples/.code-auditor.json +0 -2
- package/examples/nextjs.auditrc.json +0 -8
- package/examples/node-api.auditrc.json +0 -7
- package/examples/react.auditrc.json +0 -6
- package/examples/tolerant-solid.auditrc.json +0 -5
- package/package.json +14 -8
- package/plugin/.claude-plugin/manifest.json +16 -0
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +6 -2
- package/plugin/scripts/hook-audit.sh +41 -14
- package/plugin/scripts/hook-common.sh +50 -5
- package/plugin/scripts/hook-drift-check.sh +72 -0
- package/plugin/scripts/hook-self-audit.sh +17 -12
- package/plugin/skills/code-auditor/SKILL.md +1 -1
- package/server.json +2 -2
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
* identical to the original class method (minus `this`). Violations are built
|
|
9
9
|
* with `createSchemaViolation` instead of `this.createViolation`.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { getCallExpressionCallee, getMemberExpressionReceiver, extractMemberExpressionProperty, resolveSiteDialect, } from '../../provenance.js';
|
|
12
|
+
import { identifyHandle } from '../../handleIdentification.js';
|
|
13
|
+
import { resolveReceiverRoot, buildBindingEnv } from '../../receiverRoot.js';
|
|
12
14
|
import { OrmAdapterRegistry } from '../../orm/index.js';
|
|
13
|
-
import { SQL_TAG_NAMES,
|
|
15
|
+
import { SQL_TAG_NAMES, DEFAULT_SCHEMA_CONFIG } from './config.js';
|
|
14
16
|
import { createSchemaViolation } from './violations.js';
|
|
15
17
|
import { isTestOrSpecPath } from '../../../languages/testConventions.js';
|
|
18
|
+
import { parseSqlProgramTolerant, collectTypedRelations, DEFAULT_SQL_DIALECT } from '../../../languages/sql/sqlAst.js';
|
|
16
19
|
/**
|
|
17
20
|
* Extract table references from a TypeScript/JavaScript AST.
|
|
18
21
|
*
|
|
@@ -28,30 +31,38 @@ import { isTestOrSpecPath } from '../../../languages/testConventions.js';
|
|
|
28
31
|
export function findTableReferences(ast, adapter, sourceCode, ctx) {
|
|
29
32
|
const references = [];
|
|
30
33
|
const unresolved = [];
|
|
34
|
+
const unparseable = [];
|
|
31
35
|
// (1) Tagged template SQL — e.g. sql`SELECT * FROM heroes`
|
|
32
|
-
|
|
36
|
+
const taggedRefs = extractTaggedTemplateRefs(ast, adapter, sourceCode, ctx);
|
|
37
|
+
references.push(...taggedRefs.references);
|
|
38
|
+
unparseable.push(...taggedRefs.unparseable);
|
|
33
39
|
// (2) DB-call patterns — e.g. db.exec("SELECT * FROM heroes")
|
|
34
40
|
const dbRefs = extractDbCallRefs(ast, adapter, sourceCode, ctx);
|
|
35
41
|
references.push(...dbRefs.references);
|
|
36
42
|
unresolved.push(...dbRefs.unresolved);
|
|
43
|
+
unparseable.push(...dbRefs.unparseable);
|
|
37
44
|
// (3) .sql files — scan the entire source (the whole file IS SQL).
|
|
38
45
|
if (ast.filePath.endsWith('.sql')) {
|
|
39
|
-
const fileRefs = parseSqlTables(sourceCode, { line: 1, column: 1 }, sourceCode, ctx.allTables);
|
|
40
|
-
references.push(...fileRefs);
|
|
46
|
+
const fileRefs = parseSqlTables(sourceCode, { line: 1, column: 1 }, sourceCode, ctx.allTables, ctx.config.sqlDialect ?? null, ctx.config.sqlDialectReason ?? null);
|
|
47
|
+
references.push(...fileRefs.references);
|
|
48
|
+
unparseable.push(...fileRefs.unparseable);
|
|
41
49
|
}
|
|
42
50
|
// (4) Spec 15 R2 — ORM-aware extraction (Drizzle + Prisma)
|
|
43
51
|
references.push(...extractOrmRefs(ast, adapter, sourceCode));
|
|
44
52
|
// (5) knex-style query-builder reads — db('table').select(...) / .where(...) / .first(...)
|
|
45
53
|
references.push(...extractQueryBuilderRefs(ast, adapter, sourceCode, ctx));
|
|
46
|
-
|
|
54
|
+
// (6) table-backed collection-facade constructors — new SqliteCollectionAdapter(db, 't')
|
|
55
|
+
references.push(...extractCollectionAdapterRefs(ast, adapter, sourceCode));
|
|
56
|
+
return { references, unresolved, unparseable };
|
|
47
57
|
}
|
|
48
58
|
/**
|
|
49
59
|
* Strategy (1): tagged-template SQL — e.g. sql`SELECT * FROM heroes`.
|
|
50
60
|
* This is a syntax feature, not a naming convention — keep the sqlTagNames gate.
|
|
51
61
|
*/
|
|
52
62
|
function extractTaggedTemplateRefs(ast, adapter, sourceCode, ctx) {
|
|
53
|
-
const { config, allTables } = ctx;
|
|
63
|
+
const { config, allTables, provenanceContext } = ctx;
|
|
54
64
|
const references = [];
|
|
65
|
+
const unparseable = [];
|
|
55
66
|
const sqlTags = config.sqlTagNames ?? [...SQL_TAG_NAMES];
|
|
56
67
|
const taggedTemplates = adapter.findNodes(ast, {
|
|
57
68
|
custom: (node) => {
|
|
@@ -70,80 +81,173 @@ function extractTaggedTemplateRefs(ast, adapter, sourceCode, ctx) {
|
|
|
70
81
|
if (!templateText)
|
|
71
82
|
continue;
|
|
72
83
|
const location = getCallLocation(callNode);
|
|
73
|
-
|
|
84
|
+
// Spec 70 (per-site dialect) — a tag imported from `postgres` (the `postgres`
|
|
85
|
+
// package's `sql\`…\``) is postgres, not whatever the repo-level detection
|
|
86
|
+
// names. Resolves from the tag identifier's package, else falls back.
|
|
87
|
+
const siteDialect = resolveSiteDialect(callNode, adapter, sourceCode, provenanceContext);
|
|
88
|
+
const parsed = parseSqlTables(templateText, location, sourceCode, allTables, siteDialect ?? config.sqlDialect ?? null, siteDialect ? null : (config.sqlDialectReason ?? null));
|
|
89
|
+
references.push(...parsed.references);
|
|
90
|
+
unparseable.push(...parsed.unparseable);
|
|
74
91
|
}
|
|
75
|
-
return references;
|
|
92
|
+
return { references, unparseable };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Spec 70 R1.2 — the schema family's DB-call admission funnels through
|
|
96
|
+
* `identifyHandle` (the one entry point) instead of the boolean
|
|
97
|
+
* `isDBProvenanced`. A `handle` or `unproven` verdict admits the call — an
|
|
98
|
+
* `unproven` receiver whose type annotation is no longer a handle test
|
|
99
|
+
* (criterion 9) stays visible so `unresolved-query` / `unparseable` report its
|
|
100
|
+
* SQL as unreadable rather than silently clean; `not-handle` drops it. `null`
|
|
101
|
+
* means "not a query-shaped call" (a non-DB/ORM method, or a name with no DB
|
|
102
|
+
* signal at all). Admission is tri-state, not boolean.
|
|
103
|
+
*/
|
|
104
|
+
function dbCallVerdict(node, ast, adapter, sourceCode, ctx) {
|
|
105
|
+
const { provenanceContext, config, handleEnv } = ctx;
|
|
106
|
+
if (!provenanceContext || provenanceContext.mode === 'names')
|
|
107
|
+
return null;
|
|
108
|
+
const callee = getCallExpressionCallee(node, adapter);
|
|
109
|
+
if (!callee)
|
|
110
|
+
return null;
|
|
111
|
+
// The declaration-resolution env `identifyHandle` reads. Production threads it
|
|
112
|
+
// (`handleEnv`); a direct caller that seeded only `dbProvenanced` (unit tests)
|
|
113
|
+
// falls back to a freshly-built env over that provenance — the same evidence the
|
|
114
|
+
// boolean `isDBProvenanced` gate this replaced consumed, so a seeded-provenanced
|
|
115
|
+
// receiver still admits and its SQL still reaches `parseSqlTables`.
|
|
116
|
+
const env = handleEnv ?? {
|
|
117
|
+
provenance: provenanceContext.dbProvenanced,
|
|
118
|
+
bindings: buildBindingEnv(ast, adapter, sourceCode),
|
|
119
|
+
adapter,
|
|
120
|
+
sourceCode,
|
|
121
|
+
};
|
|
122
|
+
const dialect = resolveSiteDialect(node, adapter, sourceCode, provenanceContext) ??
|
|
123
|
+
config.sqlDialect ?? null;
|
|
124
|
+
const facts = () => ({
|
|
125
|
+
imports: new Map(),
|
|
126
|
+
typeAnnotations: new Map(),
|
|
127
|
+
bindings: new Map(),
|
|
128
|
+
withinFileProvenance: new Map(),
|
|
129
|
+
sqlDialect: dialect,
|
|
130
|
+
resolution: { dialect: 'ts', env },
|
|
131
|
+
});
|
|
132
|
+
// Bare-identifier call (`query(…)` / `d1(…)`): a provenanced wrapper or a
|
|
133
|
+
// type-annotated handle — the seam decides. A truly unbound name carries no DB
|
|
134
|
+
// signal, so it is rejected before its SQL argument could prove handle-ness.
|
|
135
|
+
if (callee.type === 'identifier') {
|
|
136
|
+
const name = adapter.getNodeText(callee, sourceCode);
|
|
137
|
+
if (!name)
|
|
138
|
+
return null;
|
|
139
|
+
const binding = env.bindings.get(name);
|
|
140
|
+
const isProvenanced = provenanceContext.dbProvenanced.has(name);
|
|
141
|
+
const isTypeAnnotated = !!binding && !!binding.typeText;
|
|
142
|
+
if (!isProvenanced && !isTypeAnnotated)
|
|
143
|
+
return null;
|
|
144
|
+
return identifyHandle({
|
|
145
|
+
format: 'typescript',
|
|
146
|
+
root: name,
|
|
147
|
+
receiver: name,
|
|
148
|
+
method: name,
|
|
149
|
+
sqlArgument: getFirstStringArgument(node, adapter, sourceCode),
|
|
150
|
+
thisField: false,
|
|
151
|
+
}, facts());
|
|
152
|
+
}
|
|
153
|
+
if (callee.type !== 'member_expression' && callee.type !== 'selector_expression') {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const method = extractMemberExpressionProperty(callee, adapter, sourceCode);
|
|
157
|
+
if (!method)
|
|
158
|
+
return null;
|
|
159
|
+
// The package discriminant — not the method name — decides handle-ness
|
|
160
|
+
// (`identifyHandle` below). No method-name set gates admission here.
|
|
161
|
+
const root = resolveReceiverRoot(callee, adapter, sourceCode);
|
|
162
|
+
if (root === null)
|
|
163
|
+
return null;
|
|
164
|
+
const receiver = getMemberExpressionReceiver(callee, adapter, sourceCode) ?? root;
|
|
165
|
+
return identifyHandle({
|
|
166
|
+
format: 'typescript',
|
|
167
|
+
root,
|
|
168
|
+
receiver,
|
|
169
|
+
method,
|
|
170
|
+
sqlArgument: getFirstStringArgument(node, adapter, sourceCode),
|
|
171
|
+
thisField: receiverIsThisRootedLocal(callee, adapter),
|
|
172
|
+
}, facts());
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Walk a member chain to see whether it bottoms out at `this`/`super`.
|
|
176
|
+
* @param callee - The member/selector expression to walk.
|
|
177
|
+
* @param adapter - The language adapter (drives child access).
|
|
178
|
+
* @returns True when the chain's root is `this` or `super`.
|
|
179
|
+
*/
|
|
180
|
+
export function receiverIsThisRootedLocal(callee, adapter) {
|
|
181
|
+
let current = callee;
|
|
182
|
+
while (current.type === 'member_expression' || current.type === 'selector_expression') {
|
|
183
|
+
const children = adapter.getChildren(current);
|
|
184
|
+
const firstChild = children.find((c) => c.type !== '.' && c.type !== 'property_identifier' && c.type !== 'field_identifier');
|
|
185
|
+
if (!firstChild)
|
|
186
|
+
return false;
|
|
187
|
+
current = firstChild;
|
|
188
|
+
}
|
|
189
|
+
return current.type === 'this' || current.type === 'super';
|
|
76
190
|
}
|
|
77
191
|
/**
|
|
78
192
|
* Strategy (2): DB-call patterns — e.g. db.exec("SELECT * FROM heroes").
|
|
79
|
-
* Spec
|
|
80
|
-
*
|
|
193
|
+
* Spec 70 R1.2: admission is decided once by {@link dbCallVerdict} (via
|
|
194
|
+
* `identifyHandle`); a `handle` or `unproven` receiver is a DB call, a
|
|
195
|
+
* `not-handle` receiver is not. There is no name-list fallback.
|
|
81
196
|
*/
|
|
82
197
|
function extractDbCallRefs(ast, adapter, sourceCode, ctx) {
|
|
83
|
-
const {
|
|
198
|
+
const { provenanceContext, allTables, config } = ctx;
|
|
84
199
|
const references = [];
|
|
85
200
|
const unresolved = [];
|
|
201
|
+
const unparseable = [];
|
|
86
202
|
const dbCalls = adapter.findNodes(ast, {
|
|
87
203
|
custom: (node) => {
|
|
88
204
|
if (node.type !== 'call_expression')
|
|
89
205
|
return false;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return isDBProvenanced(node, { adapter, sourceCode, context: provenanceContext, methods: DB_CALL_METHODS });
|
|
93
|
-
}
|
|
94
|
-
// Legacy name-based check for names mode / no context
|
|
95
|
-
const callee = getCallee(node, adapter, sourceCode);
|
|
96
|
-
if (!callee)
|
|
97
|
-
return false;
|
|
98
|
-
const dbMethods = config.dbCallMethods ?? [...DB_CALL_METHOD_NAMES];
|
|
99
|
-
const dbReceivers = config.dbReceiverNames ?? [...DB_RECEIVER_NAMES];
|
|
100
|
-
return isDbMemberCall(callee, dbMethods, dbReceivers);
|
|
206
|
+
const verdict = dbCallVerdict(node, ast, adapter, sourceCode, ctx);
|
|
207
|
+
return verdict !== null && verdict.kind !== 'not-handle';
|
|
101
208
|
},
|
|
102
209
|
});
|
|
103
210
|
for (const callNode of dbCalls) {
|
|
104
211
|
// A string/template argument is always SQL text, for *every* DB-call method:
|
|
105
212
|
// node-sqlite3's `db.all(sql, cb)` / `db.run(sql, cb)` pass SQL as a literal
|
|
106
|
-
// just as D1's `db.prepare(sql)` does.
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
const methodName = extractDbCallMethodName(callNode, adapter, sourceCode);
|
|
111
|
-
const resolveIdentifier = methodName === null ||
|
|
112
|
-
SQL_CARRYING_METHOD_NAMES.includes(methodName);
|
|
213
|
+
// just as D1's `db.prepare(sql)` does. "Is this argument SQL" is answered by
|
|
214
|
+
// the resolved value's shape, not by a method name list (Spec 70): a string
|
|
215
|
+
// literal is SQL, an array/object literal is a statements array / parameter
|
|
216
|
+
// object (not SQL), and anything unreadable stays `unresolved`.
|
|
113
217
|
const location = getCallLocation(callNode);
|
|
114
|
-
const resolved = resolveQuerySql(callNode, ast, adapter, sourceCode
|
|
218
|
+
const resolved = resolveQuerySql(callNode, ast, adapter, sourceCode);
|
|
115
219
|
if (resolved.sqlText !== null) {
|
|
116
|
-
|
|
220
|
+
// §13 — a static SQL argument in a SQL position (provenanced receiver) is
|
|
221
|
+
// parsed, not regex-scanned. A named dialect that cannot parse it, or no
|
|
222
|
+
// dialect at all, is `cannot-fire` — surfaced by `parseSqlTables` as an
|
|
223
|
+
// unreadable statement rather than a silent empty reference set that
|
|
224
|
+
// `unknown-table` / `stale-table-reference` would read as clean.
|
|
225
|
+
// Spec 70 (per-site dialect) — the dialect is a property of the call site's
|
|
226
|
+
// receiver, not the repo: `pool.query(…)` where `pool` → `pg` parses as
|
|
227
|
+
// postgres even in a repo that also names `mysql2`. Falls back to the
|
|
228
|
+
// repo-level detection result only when the receiver doesn't resolve to a
|
|
229
|
+
// package (or resolves to a cross-dialect ORM).
|
|
230
|
+
const siteDialect = resolveSiteDialect(callNode, adapter, sourceCode, provenanceContext);
|
|
231
|
+
const parsed = parseSqlTables(resolved.sqlText, location, sourceCode, allTables, siteDialect ?? config.sqlDialect ?? null, siteDialect ? null : (config.sqlDialectReason ?? null));
|
|
232
|
+
references.push(...parsed.references);
|
|
233
|
+
unparseable.push(...parsed.unparseable);
|
|
117
234
|
}
|
|
118
235
|
else if (resolved.unresolved !== null) {
|
|
119
236
|
unresolved.push(resolved.unresolved);
|
|
120
237
|
}
|
|
121
238
|
}
|
|
122
|
-
return { references, unresolved };
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Extract the method name from a DB call's callee, returning the final
|
|
126
|
-
* identifier segment stripped of any chained call / type-argument text.
|
|
127
|
-
* Handles bare identifiers (`query(...)`) and member expressions, including
|
|
128
|
-
* chained ones (`db.prepare(sql).run()` → `run`, `env.DB.prepare(...)` →
|
|
129
|
-
* `prepare`). Returns null only when the callee text cannot be read.
|
|
130
|
-
*/
|
|
131
|
-
function extractDbCallMethodName(node, adapter, sourceCode) {
|
|
132
|
-
const callee = getCallee(node, adapter, sourceCode);
|
|
133
|
-
if (!callee)
|
|
134
|
-
return null;
|
|
135
|
-
const dotIdx = callee.lastIndexOf('.');
|
|
136
|
-
const segment = dotIdx === -1 ? callee : callee.substring(dotIdx + 1);
|
|
137
|
-
// Strip a chained argument list (`run(...)`) or generic type args (`all<Row>`).
|
|
138
|
-
const name = segment.replace(/[<(].*$/, '').trim();
|
|
139
|
-
return name.length > 0 ? name : null;
|
|
239
|
+
return { references, unresolved, unparseable };
|
|
140
240
|
}
|
|
141
241
|
/**
|
|
142
242
|
* Strategy (4): ORM-aware extraction (Drizzle + Prisma) via the registered
|
|
143
243
|
* adapter. Complements raw-SQL extraction by picking up ORM-specific patterns
|
|
144
244
|
* like db.select().from(users) and prisma.user.findMany().
|
|
245
|
+
* @param ast - The file's parsed AST.
|
|
246
|
+
* @param adapter - The language adapter (for ORM adapter dispatch).
|
|
247
|
+
* @param sourceCode - The file's source text.
|
|
248
|
+
* @returns The ORM-extracted table references (empty when no ORM adapter applies).
|
|
145
249
|
*/
|
|
146
|
-
function extractOrmRefs(ast, adapter, sourceCode) {
|
|
250
|
+
export function extractOrmRefs(ast, adapter, sourceCode) {
|
|
147
251
|
const references = [];
|
|
148
252
|
const ormRegistry = OrmAdapterRegistry.getInstance();
|
|
149
253
|
const ormAdapter = ormRegistry.getAdapterForFile(ast.filePath);
|
|
@@ -194,14 +298,22 @@ function extractOrmRefs(ast, adapter, sourceCode) {
|
|
|
194
298
|
* schema violation, and feeding it into unknown-table floods a test-heavy corpus
|
|
195
299
|
* and flips the fail-open ratio.
|
|
196
300
|
*/
|
|
197
|
-
/**
|
|
301
|
+
/** Knex/transaction receiver names for the fluent `receiver('table').method(` form.
|
|
302
|
+
* Spec 69 §10: this is a structural knex idiom, not a name-list fallback — the
|
|
303
|
+
* generic `db`/`database`/`sql`/`stmt` names are provenanced by declaration only. */
|
|
198
304
|
const QUERY_BUILDER_RECEIVER_NAMES = ['trx', 'knex'];
|
|
199
|
-
|
|
305
|
+
/** Strategy (5): fluent query-builder reads — `trx('table').method(` and the
|
|
306
|
+
* `knex` receiver. Records the *read* only, tagged `origin: 'query-builder'`,
|
|
307
|
+
* so a scratch/test table name carried in a fluent builder call is not a schema
|
|
308
|
+
* violation and does not flood the naming/unknown-table/lifecycle checks.
|
|
309
|
+
* @param ast - The file's parsed AST.
|
|
310
|
+
* @param adapter - The language adapter (drives node discovery + text reads).
|
|
311
|
+
* @param sourceCode - The file's source text.
|
|
312
|
+
* @param ctx - Shared table-reference extraction context.
|
|
313
|
+
* @returns The query-builder table references (read-only, `origin: 'query-builder'`). */
|
|
314
|
+
export function extractQueryBuilderRefs(ast, adapter, sourceCode, ctx) {
|
|
200
315
|
const references = [];
|
|
201
|
-
const receivers = new Set([
|
|
202
|
-
...(ctx.config.dbReceiverNames ?? [...DB_RECEIVER_NAMES]),
|
|
203
|
-
...QUERY_BUILDER_RECEIVER_NAMES,
|
|
204
|
-
]);
|
|
316
|
+
const receivers = new Set([...QUERY_BUILDER_RECEIVER_NAMES]);
|
|
205
317
|
const calls = adapter.findNodes(ast, { type: 'call_expression' });
|
|
206
318
|
for (const call of calls) {
|
|
207
319
|
// Only the outermost call of a query chain carries the complete text and the
|
|
@@ -236,6 +348,64 @@ function extractQueryBuilderRefs(ast, adapter, sourceCode, ctx) {
|
|
|
236
348
|
function normalizeQueryBuilderTable(table) {
|
|
237
349
|
return table.split(/\s+as\s+/i)[0].trim();
|
|
238
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Strategy (6): table-backed collection-facade constructors — e.g.
|
|
353
|
+
* `new SqliteCollectionAdapter(db, 'project_tasks')`.
|
|
354
|
+
*
|
|
355
|
+
* `SqliteCollectionAdapter` (src/codeIndex/sqliteCollection.ts) is a
|
|
356
|
+
* LokiJS-`Collection`-compatible facade over a SQLite table. Every statement it
|
|
357
|
+
* issues interpolates the table from an instance field — `SELECT * FROM
|
|
358
|
+
* "${this.tableName}"`, `INSERT INTO "${this.tableName}" (…)`, `UPDATE
|
|
359
|
+
* "${this.tableName}" …`, `DELETE FROM "${this.tableName}" …` — so the keyword-
|
|
360
|
+
* anchored `parseSqlTables` never sees the table name and the facade's reads and
|
|
361
|
+
* writes are both invisible to the schema-usage fact. The one place the table
|
|
362
|
+
* name is a string literal is the constructor call. Recognizing it records the
|
|
363
|
+
* *read* (the `find`/`findOne` SELECT) so a table seeded by a migration and then
|
|
364
|
+
* read through the facade is not flagged `written-never-read` — the
|
|
365
|
+
* `project_tasks` case: `new SqliteCollectionAdapter(db, 'project_tasks')` with
|
|
366
|
+
* reads through `this.tasksAdapter.find(...)`.
|
|
367
|
+
*
|
|
368
|
+
* Only the read is recorded, mirroring strategy (5)'s read-only choice: the
|
|
369
|
+
* facade's write verbs (`insert`/`update`/`remove`/`clear`) back tables that
|
|
370
|
+
* migrations already `CREATE`/`INSERT` seed, so the write is already in the fact
|
|
371
|
+
* and recording a second, unsourceable write from the constructor would
|
|
372
|
+
* double-count. The read is the missing half of the lifecycle pair. References
|
|
373
|
+
* are tagged `origin: 'collection-adapter'` — a real schema table, so naming/
|
|
374
|
+
* unknown-table checks still run on it (unlike 'query-builder'), but the
|
|
375
|
+
* discriminator lets the cross-domain analyzer recognise a table that is *only*
|
|
376
|
+
* seen through a facade.
|
|
377
|
+
*/
|
|
378
|
+
const COLLECTION_ADAPTER_NAMES = new Set(['SqliteCollectionAdapter']);
|
|
379
|
+
/** Strategy (6): table-backed collection-facade constructors — `new
|
|
380
|
+
* SqliteCollectionAdapter(db, 'table')`. Records the *read* the facade issues
|
|
381
|
+
* (tagged `origin: 'collection-adapter'`) so a migration-seeded table read
|
|
382
|
+
* through a facade is not flagged `written-never-read`; the facade's writes are
|
|
383
|
+
* already seeded by migrations and left unrecorded to avoid double-counting.
|
|
384
|
+
* @param ast - The file's parsed AST.
|
|
385
|
+
* @param adapter - The language adapter (drives `new_expression` discovery).
|
|
386
|
+
* @param sourceCode - The file's source text.
|
|
387
|
+
* @returns The collection-facade table references (`origin: 'collection-adapter'`). */
|
|
388
|
+
export function extractCollectionAdapterRefs(ast, adapter, sourceCode) {
|
|
389
|
+
const references = [];
|
|
390
|
+
const constructors = adapter.findNodes(ast, { type: 'new_expression' });
|
|
391
|
+
for (const node of constructors) {
|
|
392
|
+
const text = adapter.getNodeText(node, sourceCode).trim();
|
|
393
|
+
// `new SqliteCollectionAdapter(db, 'project_tasks')` — the first argument is
|
|
394
|
+
// the shared handle, the second is the literal table name the facade backs.
|
|
395
|
+
// `[^,]+` for the handle tolerates `this.db` / `db` / any receiver expression.
|
|
396
|
+
const m = /^new\s+([A-Za-z_$][\w$]*)\s*\(\s*[^,]+,\s*['"]([^'"]+)['"]/.exec(text);
|
|
397
|
+
if (!m || !COLLECTION_ADAPTER_NAMES.has(m[1]))
|
|
398
|
+
continue;
|
|
399
|
+
references.push({
|
|
400
|
+
table: m[2],
|
|
401
|
+
type: 'select',
|
|
402
|
+
location: node.location.start,
|
|
403
|
+
context: `new ${m[1]}(…, '${m[2]}')`,
|
|
404
|
+
origin: 'collection-adapter',
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
return references;
|
|
408
|
+
}
|
|
239
409
|
/** True when `call` is the object of a parent member_expression (it is chained
|
|
240
410
|
* into a further `.method(...)`), so it is not the outermost call of its chain. */
|
|
241
411
|
function isChainedFurther(call, adapter) {
|
|
@@ -249,193 +419,106 @@ function isChainedFurther(call, adapter) {
|
|
|
249
419
|
return object === call;
|
|
250
420
|
}
|
|
251
421
|
/**
|
|
252
|
-
* SQL
|
|
422
|
+
* Parse SQL table names from a SQL text string via the node-sql-parser AST
|
|
423
|
+
* (Spec 70 §13). This is the schema family's successor to the keyword-regex
|
|
424
|
+
* scanner: relations come from a typed statement walk (`collectTypedRelations`),
|
|
425
|
+
* so a `FROM`/`JOIN`/`INSERT INTO`/`UPDATE`/`DELETE`/`CREATE TABLE` is read
|
|
426
|
+
* exactly where the grammar says it is, and a CTE name or an `ALTER … RENAME`
|
|
427
|
+
* target is subtracted from the walk instead of regex-guessed.
|
|
253
428
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
// exclude `DELETE FROM` — a write context whose table is already captured
|
|
263
|
-
// by the DELETE pattern below. Without the gate, `DELETE FROM users` is
|
|
264
|
-
// also tagged `select`, misclassifying a delete-only table as read.
|
|
265
|
-
{ regex: /\bFROM\s+([`"']?)([\p{L}_][\p{L}\p{N}_]*)\1/giu, type: 'select', fromKeyword: true },
|
|
266
|
-
{ regex: /\bJOIN\s+([`"']?)([\p{L}_][\p{L}\p{N}_]*)\1/giu, type: 'select' },
|
|
267
|
-
// Spec 52 R2 — the four D1/SQLite upsert forms are writes. MySQL's
|
|
268
|
-
// `INSERT IGNORE INTO` (no OR) and `ON DUPLICATE KEY UPDATE` are
|
|
269
|
-
// deliberately out of scope: the classifier targets the D1/Workers SQLite
|
|
270
|
-
// dialect, and `ON DUPLICATE KEY UPDATE col = …` (no `SET`) would otherwise
|
|
271
|
-
// misfire the UPDATE pattern below onto the column name.
|
|
272
|
-
{ regex: /\b(?:INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?|REPLACE)\s+INTO\s+([`"']?)([\p{L}_][\p{L}\p{N}_]*)\1/giu, type: 'insert' },
|
|
273
|
-
{ regex: /\bUPDATE\s+([`"']?)([\p{L}_][\p{L}\p{N}_]*)\1/giu, type: 'update' },
|
|
274
|
-
{ regex: /\bDELETE\s+FROM\s+([`"']?)([\p{L}_][\p{L}\p{N}_]*)\1/giu, type: 'delete' },
|
|
275
|
-
{ regex: /\bCREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?([`"']?)([\p{L}_][\p{L}\p{N}_]*)\1/giu, type: 'create' },
|
|
276
|
-
];
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Run the keyword-anchored SQL patterns over `cleaned`, skipping system
|
|
280
|
-
* tables, table-valued functions, module-specifier FROMs, short CTE/alias
|
|
281
|
-
* identifiers, and SQL keywords.
|
|
282
|
-
*/
|
|
283
|
-
function matchSqlPatterns(ctx) {
|
|
284
|
-
const { sqlText, cleaned, baseLocation, sourceCode, allTables } = ctx;
|
|
285
|
-
const references = [];
|
|
286
|
-
for (const { regex, type, fromKeyword } of sqlTablePatterns()) {
|
|
287
|
-
// Create fresh regex since we might consume with exec
|
|
288
|
-
const re = new RegExp(regex.source, regex.flags);
|
|
289
|
-
let match;
|
|
290
|
-
while ((match = re.exec(cleaned)) !== null) {
|
|
291
|
-
const table = match[2]; // The table name (capture group 2)
|
|
292
|
-
if (!table || isSystemTable(table) || isTableValuedFunction(table))
|
|
293
|
-
continue;
|
|
294
|
-
// Skip JS/TS module specifiers (`import x from 'mod'`) that step (3)'s
|
|
295
|
-
// full-source scan of migration `.ts` files otherwise captures as tables.
|
|
296
|
-
if (isModuleImportFrom(cleaned, match.index))
|
|
297
|
-
continue;
|
|
298
|
-
// `DELETE FROM` is a write, not a read — the DELETE pattern below already
|
|
299
|
-
// classifies the table, so the generic FROM pattern must not also tag it
|
|
300
|
-
// `select` (Spec 56 R4 finding: a delete-only table was misread as
|
|
301
|
-
// also-read, suppressing written-never-read).
|
|
302
|
-
if (fromKeyword && isDeleteFrom(cleaned, match.index))
|
|
303
|
-
continue;
|
|
304
|
-
// Skip very short identifiers (likely CTE names / bare aliases like 'x',
|
|
305
|
-
// 't', 'o', 'c') unless they are known table names — the guard catches
|
|
306
|
-
// false positives from single-char CTE/alias identifiers.
|
|
307
|
-
if (!isSqlKeyword(table) && table.length < 3 && !allTables?.has(table.toLowerCase()))
|
|
308
|
-
continue;
|
|
309
|
-
// Skip common false positives: common variable names, keywords
|
|
310
|
-
if (isSqlKeyword(table))
|
|
311
|
-
continue;
|
|
312
|
-
// Calculate position in original source. Anchor against `sqlText` — the
|
|
313
|
-
// original SQL, which appears verbatim in the source — NOT `cleaned`, which
|
|
314
|
-
// carries `__TMPL__` sentinels for `${…}` substitutions that never appear
|
|
315
|
-
// in the source. `sourceCode.indexOf(cleaned)` returns -1 for any
|
|
316
|
-
// interpolated template, collapsing every anchor to a plausible-but-wrong
|
|
317
|
-
// line. An `indexOf` that returns -1 is a bug, not a fallback — throw
|
|
318
|
-
// rather than mis-place (same guard the severity lookup uses for `undefined`).
|
|
319
|
-
if (match[0].includes('__TMPL__')) {
|
|
320
|
-
// The match overlaps a template substitution (a templated table name);
|
|
321
|
-
// there is no verbatim anchor to point at, and the reference is filtered
|
|
322
|
-
// out downstream — skip it rather than fabricate a location.
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
const sqlAbs = sourceCode.indexOf(sqlText);
|
|
326
|
-
if (sqlAbs < 0) {
|
|
327
|
-
throw new Error(`parseSqlTables: SQL text not found in source — a table reference that cannot be anchored is a bug, not something to mis-place.`);
|
|
328
|
-
}
|
|
329
|
-
const offset = sqlText.indexOf(match[0]);
|
|
330
|
-
if (offset < 0) {
|
|
331
|
-
throw new Error(`parseSqlTables: match "${match[0]}" not found in SQL text — a table reference that cannot be anchored is a bug, not something to mis-place.`);
|
|
332
|
-
}
|
|
333
|
-
const location = offsetToLocation(sourceCode, sqlAbs + offset, baseLocation);
|
|
334
|
-
references.push({
|
|
335
|
-
table,
|
|
336
|
-
type,
|
|
337
|
-
location,
|
|
338
|
-
context: match[0].trim(),
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return references;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Parse a SQL string (possibly with template expressions) into table references.
|
|
429
|
+
* Contract (Spec 70): a named dialect that cannot parse the SQL is *unreadable*,
|
|
430
|
+
* not "no tables". A null dialect does not skip the parse (R2: parsing a literal
|
|
431
|
+
* does not require proving the site's dialect first) — the SQL is attempted under
|
|
432
|
+
* {@link DEFAULT_SQL_DIALECT}; when it parses, the tables are derived; when it
|
|
433
|
+
* fails, the record is `cannot-fire` naming **both** the parse failure and the
|
|
434
|
+
* undetermined dialect (the failure may be dialect-specific syntax). Either way
|
|
435
|
+
* a failure is returned as an `unparseable` record, never a silent empty
|
|
436
|
+
* reference set that `unknown-table` / `stale-table-reference` would read clean.
|
|
346
437
|
*
|
|
347
438
|
* @param sqlText The SQL text to parse.
|
|
348
|
-
* @param baseLocation
|
|
349
|
-
* @param sourceCode The full source
|
|
350
|
-
* @param allTables
|
|
351
|
-
* @
|
|
439
|
+
* @param baseLocation The line/column of the SQL text's start in `sourceCode`.
|
|
440
|
+
* @param sourceCode The full source text (for offset-to-location mapping).
|
|
441
|
+
* @param allTables Known-table catalog used to keep short (1–2 char) identifiers.
|
|
442
|
+
* @param dialect The corpus's named dialect, or null when detection named none.
|
|
443
|
+
* @param dialectReason Why `dialect` is null (for the cannot-fire message).
|
|
444
|
+
* @returns Table references parsed from the SQL, plus unparseable statements.
|
|
352
445
|
*/
|
|
353
|
-
export function parseSqlTables(sqlText, baseLocation, sourceCode, allTables) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
446
|
+
export function parseSqlTables(sqlText, baseLocation, sourceCode, allTables, dialect, dialectReason) {
|
|
447
|
+
const references = [];
|
|
448
|
+
const unparseable = [];
|
|
449
|
+
const trimmed = sqlText.trim();
|
|
450
|
+
if (trimmed.length === 0)
|
|
451
|
+
return { references, unparseable };
|
|
452
|
+
const program = parseSqlProgramTolerant(sqlText, dialect ?? DEFAULT_SQL_DIALECT, false);
|
|
453
|
+
for (const failure of program.failures) {
|
|
454
|
+
unparseable.push({
|
|
455
|
+
sqlText,
|
|
456
|
+
location: baseLocation,
|
|
457
|
+
reason: dialect === null
|
|
458
|
+
? `${dialectReason ?? 'dialect undetermined (no database driver in package.json or wrangler.toml)'} — and the SQL does not parse under the default ${DEFAULT_SQL_DIALECT} grammar: ${failure.reason}`
|
|
459
|
+
: failure.reason,
|
|
460
|
+
kind: dialect === null ? 'dialect-undetermined' : 'parse-failure',
|
|
461
|
+
});
|
|
369
462
|
}
|
|
370
|
-
|
|
463
|
+
if (program.statements.length === 0)
|
|
464
|
+
return { references, unparseable };
|
|
465
|
+
// Anchor the SQL text against the source exactly once. The raw `sqlText`
|
|
466
|
+
// (still carrying `${…}`) appears verbatim in the source; the normalized
|
|
467
|
+
// parse input does not. An absent match is a bug, not a fallback — throw
|
|
468
|
+
// rather than mis-place every reference onto a wrong line.
|
|
469
|
+
const sqlAbs = sourceCode.indexOf(sqlText);
|
|
470
|
+
if (sqlAbs < 0) {
|
|
471
|
+
throw new Error(`parseSqlTables: SQL text not found in source — a table reference that cannot be anchored is a bug, not something to mis-place.`);
|
|
472
|
+
}
|
|
473
|
+
references.push(...extractSqlTables(collectTypedRelations(program.statements, new Set(program.truncatedConflictIndices)), sqlText, sqlAbs, baseLocation, sourceCode, allTables));
|
|
474
|
+
return { references, unparseable };
|
|
371
475
|
}
|
|
372
476
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
* so they can be filtered from table-references in parseSqlTables().
|
|
377
|
-
* Without this, "JOIN t.posts" captures t via the JOIN regex when t is
|
|
378
|
-
* an alias for the real table x.
|
|
379
|
-
*
|
|
380
|
-
* @param sqlText The SQL text to scan for alias identifiers.
|
|
381
|
-
* @returns Lowercased alias identifiers to filter from table references.
|
|
477
|
+
* Convert typed AST relations into `TableReference`s, applying the same guards
|
|
478
|
+
* the regex scanner applied (system tables, table-valued functions, SQL
|
|
479
|
+
* keywords, short 1–2-char identifiers) and anchoring each to its source line.
|
|
382
480
|
*/
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
aliases
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
// Pattern: keyword + table + word — the third word is the alias if it's
|
|
410
|
-
// not a SQL keyword and not followed by '.' (table.column reference).
|
|
411
|
-
const bareRe = /\b(?:FROM|JOIN)\s+([\p{L}_][\p{L}\p{N}_]*)\s+([\p{L}_][\p{L}\p{N}_]*)\b/giu;
|
|
412
|
-
while ((m = bareRe.exec(sqlText)) !== null) {
|
|
413
|
-
const alias = m[2];
|
|
414
|
-
// Don't add if it looks like a keyword or is followed by '.' (table ref)
|
|
415
|
-
if (!isSqlKeyword(alias)) {
|
|
416
|
-
const afterMatch = sqlText.substring(m.index + m[0].length);
|
|
417
|
-
if (!/^\s*\./.test(afterMatch)) {
|
|
418
|
-
aliases.add(alias.toLowerCase());
|
|
419
|
-
}
|
|
420
|
-
}
|
|
481
|
+
function extractSqlTables(relations, sqlText, sqlAbs, baseLocation, sourceCode, allTables) {
|
|
482
|
+
const references = [];
|
|
483
|
+
const lowerSql = sqlText.toLowerCase();
|
|
484
|
+
for (const rel of relations) {
|
|
485
|
+
const table = rel.table;
|
|
486
|
+
if (isSystemTable(table))
|
|
487
|
+
continue;
|
|
488
|
+
if (rel.db !== null && isSystemTable(`${rel.db}.${table}`))
|
|
489
|
+
continue;
|
|
490
|
+
if (isTableValuedFunction(table))
|
|
491
|
+
continue;
|
|
492
|
+
if (isSqlKeyword(table))
|
|
493
|
+
continue;
|
|
494
|
+
// Short 1–2-char identifiers are almost always CTE names / bare aliases
|
|
495
|
+
// (`x`, `t`, `o`) rather than real tables; keep one only when the catalog
|
|
496
|
+
// names it. (CTE names are already subtracted by collectTypedRelations.)
|
|
497
|
+
if (table.length < 3 && !allTables?.has(table.toLowerCase()))
|
|
498
|
+
continue;
|
|
499
|
+
const qualified = rel.db !== null ? `${rel.db}.${table}` : table;
|
|
500
|
+
references.push({
|
|
501
|
+
table,
|
|
502
|
+
type: rel.type,
|
|
503
|
+
location: anchorTable(table, lowerSql, sqlAbs, baseLocation, sourceCode),
|
|
504
|
+
context: `${rel.type} ${qualified}`,
|
|
505
|
+
...(rel.conflictClauseTruncated ? { conflictClauseTruncated: true } : {}),
|
|
506
|
+
});
|
|
421
507
|
}
|
|
422
|
-
return
|
|
508
|
+
return references;
|
|
423
509
|
}
|
|
424
510
|
/**
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
* capture group instead of the alias group, and is never denylisted.
|
|
432
|
-
*
|
|
433
|
-
* `__TMPL__` keeps the token boundaries intact so alias extraction
|
|
434
|
-
* correctly identifies `t` as the alias. `__TMPL__` table references
|
|
435
|
-
* are filtered in parseSqlTables().
|
|
511
|
+
* Anchor a table name to its source location. The node-sql-parser AST is
|
|
512
|
+
* location-free, so the anchor is the first case-insensitive occurrence of the
|
|
513
|
+
* bare name within the SQL text — a line-level attribution good enough for the
|
|
514
|
+
* lifecycle rules' per-function re-homing. When the name is not found (a quoted
|
|
515
|
+
* identifier the parser normalized, or a name only present via a substitution),
|
|
516
|
+
* fall back to the SQL text's own start.
|
|
436
517
|
*/
|
|
437
|
-
|
|
438
|
-
|
|
518
|
+
function anchorTable(table, lowerSql, sqlAbs, baseLocation, sourceCode) {
|
|
519
|
+
const idx = lowerSql.indexOf(table.toLowerCase());
|
|
520
|
+
const rel = idx >= 0 ? idx : 0;
|
|
521
|
+
return offsetToLocation(sourceCode, sqlAbs + rel, baseLocation);
|
|
439
522
|
}
|
|
440
523
|
/**
|
|
441
524
|
* Return known table names within edit distance ≤ maxDist.
|
|
@@ -561,21 +644,28 @@ const DANGEROUS_SQL_PATTERNS = [
|
|
|
561
644
|
];
|
|
562
645
|
/**
|
|
563
646
|
* Evaluate a single dangerous-pattern match: skip parameterized queries and
|
|
564
|
-
* taint-safe dynamic strings, else
|
|
647
|
+
* taint-safe dynamic strings, else produce a dynamic-sql-construction candidate.
|
|
565
648
|
*/
|
|
566
|
-
function
|
|
567
|
-
const { ast, adapter, sourceCode, symbolOrdinals
|
|
568
|
-
// Parameterized queries pass a bound-params argument (`query(sql, params)`).
|
|
569
|
-
// When the matched literal is followed by `, params` the interpolated
|
|
570
|
-
// `${...}` segments are compile-time clauses whose `?` placeholders are
|
|
571
|
-
// bound by that argument — not an injection vector. The data-access
|
|
572
|
-
// analyzer's checkQuerySecurity applies the same signal.
|
|
573
|
-
const afterMatch = sourceCode.slice(match.index + match[0].length);
|
|
574
|
-
if (/^\s*,/.test(afterMatch))
|
|
575
|
-
return;
|
|
649
|
+
function collectInjectionMatch(ctx, match) {
|
|
650
|
+
const { ast, adapter, sourceCode, symbolOrdinals } = ctx;
|
|
576
651
|
const location = offsetToLocation(sourceCode, match.index, { line: 1, column: 1 });
|
|
577
652
|
// Find enclosing function from the AST at this position
|
|
578
653
|
const node = findClosestNodeAt(ast.root, location, adapter);
|
|
654
|
+
const callNode = findEnclosingCallExpression(node, adapter);
|
|
655
|
+
// Parameterized queries pass a bound-params argument (`query(sql, params)`).
|
|
656
|
+
// When the matched literal's enclosing call carries a second argument, the
|
|
657
|
+
// interpolated `${...}` segments are compile-time clauses whose `?`
|
|
658
|
+
// placeholders are bound by that argument — not an injection vector. This is
|
|
659
|
+
// checked at the *call* level (two source forms below), not by looking for a
|
|
660
|
+
// comma immediately after the match: a *nested* template fragment inside the
|
|
661
|
+
// first argument — `query(\`...\${scope ? \` WHERE \${scope.clause}\` : ''}\`,
|
|
662
|
+
// scope?.params)` — ends the regex match at the inner template's backtick, so
|
|
663
|
+
// the `,` lands several characters later and a bare `^\s*,` lookahead misses
|
|
664
|
+
// it. The data-access analyzer's checkQuerySecurity applies the same signal.
|
|
665
|
+
const afterMatch = sourceCode.slice(match.index + match[0].length);
|
|
666
|
+
if (/^\s*,/.test(afterMatch) || (callNode && callHasBindParams(callNode, adapter))) {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
579
669
|
// Taint-aware safety check (Spec 33 Item 11a): clear the finding when the
|
|
580
670
|
// query argument's dynamic parts are all provably safe. The naive regex
|
|
581
671
|
// cannot tell trusted-DDL interpolation (`query(\`CREATE TABLE ${name}...\`)`
|
|
@@ -583,18 +673,15 @@ function checkInjectionMatch(ctx, match) {
|
|
|
583
673
|
// distinction is delegated to the adapter's dynamic-string safety analysis
|
|
584
674
|
// (isSafeInterpolation / resolveLocalConstant) — the same signal the
|
|
585
675
|
// data-access analyzer already trusts for sql-injection-risk.
|
|
586
|
-
const callNode = findEnclosingCallExpression(node, adapter);
|
|
587
676
|
if (callNode && isAllDynamicPartsSafe(callNode, ast, adapter, sourceCode)) {
|
|
588
|
-
return;
|
|
677
|
+
return null;
|
|
589
678
|
}
|
|
590
679
|
const enclosingFn = node ? functionIdentityLabel(findEnclosingFunctionIdentity(node, adapter, ast.filePath)) : 'top-level';
|
|
591
680
|
const baseSymbol = `${enclosingFn}:dynamic-sql-construction`;
|
|
592
681
|
const ordinal = (symbolOrdinals.get(baseSymbol) ?? 0) + 1;
|
|
593
682
|
symbolOrdinals.set(baseSymbol, ordinal);
|
|
594
683
|
const symbol = ordinal > 1 ? `${baseSymbol}:${ordinal}` : baseSymbol;
|
|
595
|
-
|
|
596
|
-
// User-controlled SQL built by interpolation is injectable now — critical.
|
|
597
|
-
{ severity: 'critical', rule: 'dynamic-sql-construction', symbol }));
|
|
684
|
+
return { file: ast.filePath, line: location.line, column: location.column, enclosingFn, symbol };
|
|
598
685
|
}
|
|
599
686
|
/**
|
|
600
687
|
* Detect potential SQL injection in query/execute calls.
|
|
@@ -604,24 +691,42 @@ function checkInjectionMatch(ctx, match) {
|
|
|
604
691
|
* @param sourceCode The raw source text.
|
|
605
692
|
* @returns SQL-injection violations.
|
|
606
693
|
*/
|
|
607
|
-
export function
|
|
608
|
-
const
|
|
694
|
+
export function collectDynamicSqlCandidates(ast, adapter, sourceCode) {
|
|
695
|
+
const candidates = [];
|
|
609
696
|
const ctx = {
|
|
610
697
|
ast,
|
|
611
698
|
adapter,
|
|
612
699
|
sourceCode,
|
|
613
700
|
symbolOrdinals: new Map(),
|
|
614
|
-
violations,
|
|
615
701
|
};
|
|
616
702
|
for (const pattern of DANGEROUS_SQL_PATTERNS) {
|
|
617
703
|
// Clone regex to reset state (global regexes track lastIndex)
|
|
618
704
|
const re = new RegExp(pattern.source, pattern.flags);
|
|
619
705
|
let match;
|
|
620
706
|
while ((match = re.exec(sourceCode)) !== null) {
|
|
621
|
-
|
|
707
|
+
const candidate = collectInjectionMatch(ctx, match);
|
|
708
|
+
if (candidate)
|
|
709
|
+
candidates.push(candidate);
|
|
622
710
|
}
|
|
623
711
|
}
|
|
624
|
-
return
|
|
712
|
+
return candidates;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Detect potential SQL injection in query/execute calls (legacy emission form).
|
|
716
|
+
*
|
|
717
|
+
* Maps the collected candidates — the exact set `collectDynamicSqlCandidates`
|
|
718
|
+
* returns — to schema violations, so the legacy path and the phase path cannot
|
|
719
|
+
* diverge (parity by construction).
|
|
720
|
+
*
|
|
721
|
+
* @param ast The parsed file AST.
|
|
722
|
+
* @param adapter The language adapter for the file's syntax.
|
|
723
|
+
* @param sourceCode The raw source text.
|
|
724
|
+
* @returns SQL-injection violations.
|
|
725
|
+
*/
|
|
726
|
+
export function checkSQLInjection(ast, adapter, sourceCode) {
|
|
727
|
+
return collectDynamicSqlCandidates(ast, adapter, sourceCode).map((c) => createSchemaViolation(c.file, { line: c.line, column: c.column }, `SQL query built via string interpolation or concatenation in ${c.enclosingFn}; use parameterized queries.`,
|
|
728
|
+
// User-controlled SQL built by interpolation is injectable now — critical.
|
|
729
|
+
{ severity: 'critical', rule: 'dynamic-sql-construction', symbol: c.symbol }));
|
|
625
730
|
}
|
|
626
731
|
/**
|
|
627
732
|
* Walk up from a node to the enclosing call_expression, if any. Used by
|
|
@@ -637,6 +742,16 @@ function findEnclosingCallExpression(node, adapter) {
|
|
|
637
742
|
}
|
|
638
743
|
return null;
|
|
639
744
|
}
|
|
745
|
+
/** True when the call expression carries a second argument — the bound-params
|
|
746
|
+
* argument of a parameterized `query(sql, params)` / `execute(sql, params)`.
|
|
747
|
+
* Mirrors the argument-count signal `isWrapperFunctionWithBindParams` uses. */
|
|
748
|
+
function callHasBindParams(callNode, adapter) {
|
|
749
|
+
const args = adapter.getChildren(callNode).find(c => adapter.getNodeType(c) === 'arguments');
|
|
750
|
+
if (!args)
|
|
751
|
+
return false;
|
|
752
|
+
const realArgs = adapter.getChildren(args).filter(c => !['(', ')', ','].includes(adapter.getNodeType(c)));
|
|
753
|
+
return realArgs.length >= 2;
|
|
754
|
+
}
|
|
640
755
|
/**
|
|
641
756
|
* True when a dynamic query/execute string argument is provably safe to embed
|
|
642
757
|
* in SQL — every interpolated sub-part is a compile-time constant, quote-escaped
|
|
@@ -747,7 +862,7 @@ export function hasTemplateArgument(node, adapter) {
|
|
|
747
862
|
return false;
|
|
748
863
|
for (const child of node.children) {
|
|
749
864
|
const type = adapter.getNodeType(child);
|
|
750
|
-
if (type === 'template_string'
|
|
865
|
+
if (type === 'template_string') {
|
|
751
866
|
return true;
|
|
752
867
|
}
|
|
753
868
|
}
|
|
@@ -766,8 +881,16 @@ export function getTemplateText(node, adapter, sourceCode) {
|
|
|
766
881
|
return null;
|
|
767
882
|
for (const child of node.children) {
|
|
768
883
|
const type = adapter.getNodeType(child);
|
|
769
|
-
if (type === 'template_string'
|
|
770
|
-
|
|
884
|
+
if (type === 'template_string') {
|
|
885
|
+
const text = adapter.getNodeText(child, sourceCode).trim();
|
|
886
|
+
// A template literal's source text includes its surrounding backticks
|
|
887
|
+
// (`` `SELECT …` ``). node-sql-parser rejects a statement wrapped in
|
|
888
|
+
// backticks, so strip them before handing the SQL to the AST parser —
|
|
889
|
+
// the same unwrap `getFirstStringArgument` already does for string args.
|
|
890
|
+
if (text.startsWith('`') && text.endsWith('`')) {
|
|
891
|
+
return text.slice(1, -1);
|
|
892
|
+
}
|
|
893
|
+
return text;
|
|
771
894
|
}
|
|
772
895
|
}
|
|
773
896
|
return null;
|
|
@@ -790,8 +913,7 @@ export function getFirstStringArgument(node, adapter, sourceCode) {
|
|
|
790
913
|
for (const arg of child.children) {
|
|
791
914
|
const argType = adapter.getNodeType(arg);
|
|
792
915
|
if (argType === 'string' ||
|
|
793
|
-
argType === 'template_string'
|
|
794
|
-
argType === 'template_literal') {
|
|
916
|
+
argType === 'template_string') {
|
|
795
917
|
const text = adapter.getNodeText(arg, sourceCode).trim();
|
|
796
918
|
// Strip surrounding quotes from string literals
|
|
797
919
|
if ((text.startsWith("'") && text.endsWith("'")) ||
|
|
@@ -844,6 +966,16 @@ function unquoteLiteral(text) {
|
|
|
844
966
|
}
|
|
845
967
|
return null;
|
|
846
968
|
}
|
|
969
|
+
/**
|
|
970
|
+
* True when an initializer's source text is a structured literal — an array
|
|
971
|
+
* (`[...]`) or object (`{...}`) — rather than a string, call result, or
|
|
972
|
+
* identifier. A DB-call first argument that resolves to a structured literal
|
|
973
|
+
* is a statements array / bound-parameter object, not SQL text.
|
|
974
|
+
*/
|
|
975
|
+
function isStructuredLiteral(text) {
|
|
976
|
+
const t = text.trim();
|
|
977
|
+
return t.startsWith('[') || t.startsWith('{');
|
|
978
|
+
}
|
|
847
979
|
/**
|
|
848
980
|
* Resolve the SQL text held by a DB call's first argument.
|
|
849
981
|
*
|
|
@@ -851,30 +983,29 @@ function unquoteLiteral(text) {
|
|
|
851
983
|
* identifier argument is resolved via the adapter's `resolveLocalConstant`
|
|
852
984
|
* capability — a same-module `const` bound to a string/template literal yields
|
|
853
985
|
* its SQL text (template substitutions are left in place; `parseSqlTables`
|
|
854
|
-
* resolves them).
|
|
855
|
-
*
|
|
856
|
-
*
|
|
986
|
+
* resolves them).
|
|
987
|
+
*
|
|
988
|
+
* "Is this argument SQL" is answered by the resolved value's shape, not by a
|
|
989
|
+
* method name list (Spec 70): a string/template literal is SQL; an array/object
|
|
990
|
+
* literal is a statements array or parameter object (not SQL, so it is skipped
|
|
991
|
+
* rather than reported as unresolvable); anything unreadable (imported constant,
|
|
992
|
+
* reassigned/concatenated expression, call result, parameter, unsupported
|
|
993
|
+
* language) is reported as `unresolved` so the query is not silently treated as
|
|
994
|
+
* table-free.
|
|
857
995
|
*
|
|
858
996
|
* @param callNode The DB call_expression node.
|
|
859
997
|
* @param ast The full AST (for local-constant scope traversal).
|
|
860
998
|
* @param adapter The language adapter for the file's syntax.
|
|
861
999
|
* @param sourceCode The raw source text.
|
|
862
|
-
* @param resolveIdentifier When false, skip identifier resolution entirely (the
|
|
863
|
-
* call's first argument is not SQL — e.g. `db.batch(stmts)`).
|
|
864
1000
|
* @returns The resolved SQL text, an unresolved-query record, or neither.
|
|
865
1001
|
*/
|
|
866
|
-
function resolveQuerySql(callNode, ast, adapter, sourceCode
|
|
1002
|
+
export function resolveQuerySql(callNode, ast, adapter, sourceCode) {
|
|
867
1003
|
// Direct string/template argument — the common, fully-static path. This runs
|
|
868
1004
|
// for every DB-call method (node-sqlite3 `db.all(sql)` / `db.run(sql)` pass
|
|
869
1005
|
// SQL as a literal, not just `prepare`/`exec`).
|
|
870
1006
|
const direct = getFirstStringArgument(callNode, adapter, sourceCode);
|
|
871
1007
|
if (direct !== null)
|
|
872
1008
|
return { sqlText: direct, unresolved: null };
|
|
873
|
-
// Identifier resolution is method-gated by the caller: `batch`/`run`/`all`/
|
|
874
|
-
// `first` take statements/parameters, not SQL, so a bare identifier argument
|
|
875
|
-
// there is not SQL and must not be reported as unresolvable.
|
|
876
|
-
if (!resolveIdentifier)
|
|
877
|
-
return { sqlText: null, unresolved: null };
|
|
878
1009
|
// Bare identifier argument — resolve the local constant.
|
|
879
1010
|
const identNode = getFirstIdentifierArgument(callNode, adapter);
|
|
880
1011
|
if (!identNode)
|
|
@@ -894,8 +1025,13 @@ function resolveQuerySql(callNode, ast, adapter, sourceCode, resolveIdentifier =
|
|
|
894
1025
|
}
|
|
895
1026
|
const sqlText = unquoteLiteral(resolved.initText);
|
|
896
1027
|
if (sqlText === null) {
|
|
897
|
-
// initText is not a string literal
|
|
898
|
-
//
|
|
1028
|
+
// initText is not a string literal. A structured literal (array/object) is
|
|
1029
|
+
// a statements array / parameter object, not SQL — skip it. Anything else
|
|
1030
|
+
// (call result, binary expr, chained identifier `const B = A`) cannot be
|
|
1031
|
+
// statically read, so the query is genuinely unseen.
|
|
1032
|
+
if (isStructuredLiteral(resolved.initText)) {
|
|
1033
|
+
return { sqlText: null, unresolved: null };
|
|
1034
|
+
}
|
|
899
1035
|
return { sqlText: null, unresolved: { identifier, location } };
|
|
900
1036
|
}
|
|
901
1037
|
return { sqlText, unresolved: null };
|
|
@@ -907,6 +1043,9 @@ function resolveQuerySql(callNode, ast, adapter, sourceCode, resolveIdentifier =
|
|
|
907
1043
|
* file whose DB access is only partly visible. These are diagnostics — "the
|
|
908
1044
|
* tool could not see this query" — not findings that the code is wrong, so they
|
|
909
1045
|
* are non-blocking (see CoverageDiagnostic).
|
|
1046
|
+
* @param unresolved The unresolved-query records to report.
|
|
1047
|
+
* @param filePath The file path the diagnostics belong to.
|
|
1048
|
+
* @returns Coverage diagnostics for each unresolved query.
|
|
910
1049
|
*/
|
|
911
1050
|
export function checkUnresolvedQueries(unresolved, filePath) {
|
|
912
1051
|
return unresolved.map((u) => ({
|
|
@@ -919,21 +1058,145 @@ export function checkUnresolvedQueries(unresolved, filePath) {
|
|
|
919
1058
|
}));
|
|
920
1059
|
}
|
|
921
1060
|
/**
|
|
922
|
-
*
|
|
1061
|
+
* Build `cannot-fire` coverage diagnostics for DB-call SQL whose static string
|
|
1062
|
+
* argument a named dialect cannot parse (`PRAGMA table_info(x)`, `VACUUM`,
|
|
1063
|
+
* `ANALYZE`, and other grammar gaps). These are the SQL-position siblings of the
|
|
1064
|
+
* unresolvable-identifier `unresolved-query` diagnostic: the argument *is* read,
|
|
1065
|
+
* but the parser cannot answer "which tables", so the table-read/write facts are
|
|
1066
|
+
* unreadable rather than "no tables". Reporting (rather than letting
|
|
1067
|
+
* `unknown-table` / `stale-table-reference` read an empty reference set as clean)
|
|
1068
|
+
* is what keeps an unparseable statement visibly unreadable (Spec 70 R2).
|
|
923
1069
|
*
|
|
924
|
-
* @param
|
|
925
|
-
* @param
|
|
926
|
-
* @
|
|
927
|
-
* @returns True when the callee is a permitted receiver.method DB call.
|
|
1070
|
+
* @param unparseable The unparseable-SQL records to report.
|
|
1071
|
+
* @param filePath The file path the diagnostics belong to.
|
|
1072
|
+
* @returns One `cannot-fire` diagnostic per unparseable SQL argument.
|
|
928
1073
|
*/
|
|
929
|
-
export function
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1074
|
+
export function checkUnparseableSql(unparseable, filePath) {
|
|
1075
|
+
return unparseable.map((u) => {
|
|
1076
|
+
const why = u.kind === 'parse-failure'
|
|
1077
|
+
? `cannot be parsed ('${u.reason}')`
|
|
1078
|
+
: `is ${u.reason}`;
|
|
1079
|
+
return {
|
|
1080
|
+
analyzerName: 'schema',
|
|
1081
|
+
kind: 'cannot-fire',
|
|
1082
|
+
message: `SQL statement ${why} — its table read/write status is unreadable, so cross-domain lifecycle rules (unknown-table, stale-table-reference) may be unreliable for this file.`,
|
|
1083
|
+
file: filePath,
|
|
1084
|
+
line: u.location.line,
|
|
1085
|
+
details: { sql: u.sqlText, reason: u.reason, kind: u.kind },
|
|
1086
|
+
};
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Build `cannot-fire` coverage diagnostics for DB-looking imports whose
|
|
1091
|
+
* specifier could not be resolved to an in-repo file. This is the Spec 69 §10
|
|
1092
|
+
* coverage-parity signal: the deleted `DB_RECEIVER_NAMES` name-list fallback
|
|
1093
|
+
* used to mark a receiver named `db`/`database`/`sql`/`stmt` as DB-provenanced
|
|
1094
|
+
* even without a declaration. With that gone, a receiver imported from a
|
|
1095
|
+
* non-resolvable `./db`-style specifier can no longer be provenanced by
|
|
1096
|
+
* declaration, so any DB access through it is unseen. Reporting that as
|
|
1097
|
+
* `cannot-fire` (rather than letting the affected rules report `clean`) is what
|
|
1098
|
+
* prevents a silent regression — the coverage count must rise by exactly the
|
|
1099
|
+
* amount the finding count falls.
|
|
1100
|
+
*
|
|
1101
|
+
* @param unresolved The unresolved-import records for this file (already
|
|
1102
|
+
* filtered to the current file by the caller).
|
|
1103
|
+
* @param filePath The file path the diagnostics belong to.
|
|
1104
|
+
* @returns One `cannot-fire` diagnostic per unresolved DB-looking import.
|
|
1105
|
+
*/
|
|
1106
|
+
export function checkUnresolvedReceiverImports(unresolved, filePath) {
|
|
1107
|
+
return unresolved.map((u) => {
|
|
1108
|
+
const reason = `import of ${u.names.length > 0 ? u.names.map((n) => `'${n}'`).join(', ') : 'a DB receiver'} ` +
|
|
1109
|
+
`from '${u.source}' does not resolve to an in-repo file, so its DB provenance cannot be ` +
|
|
1110
|
+
`established by declaration`;
|
|
1111
|
+
return {
|
|
1112
|
+
analyzerName: 'schema',
|
|
1113
|
+
kind: 'cannot-fire',
|
|
1114
|
+
message: `${reason}. DB access through this receiver is unseen: table-reference ` +
|
|
1115
|
+
`and data-access rules may report clean on access they could not observe.`,
|
|
1116
|
+
file: filePath,
|
|
1117
|
+
line: 0,
|
|
1118
|
+
// `reason` carries the same "why" the call-site shape (`checkUnprovenQueryReceivers`)
|
|
1119
|
+
// carries in `details.reason` — a consumer reading `details.reason` must never
|
|
1120
|
+
// see `undefined` for an unproven receiver, whichever emission path produced it.
|
|
1121
|
+
details: { source: u.source, names: u.names, reason },
|
|
1122
|
+
};
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Build `cannot-fire` coverage diagnostics for query-shaped call sites whose
|
|
1127
|
+
* receiver is unproven (Spec 69 §10 S5a — disposition replaces step-failure).
|
|
1128
|
+
*
|
|
1129
|
+
* The §10 guard is keyed to *the question being unanswered*, not to a resolution
|
|
1130
|
+
* step failing: an unproven receiver — whether an unresolved import, a runtime
|
|
1131
|
+
* binding (`env.DB`), an unannotated parameter, or a `new <wrapper-class>()` the
|
|
1132
|
+
* resolution does not trace — must report `cannot-fire` with a reason, never
|
|
1133
|
+
* `clean`. This is the per-call-site complement to
|
|
1134
|
+
* {@link checkUnresolvedReceiverImports} (which is keyed to the import alone).
|
|
1135
|
+
*
|
|
1136
|
+
* @param unproven The unproven-query-receiver records for this file.
|
|
1137
|
+
* @param filePath The file path the diagnostics belong to.
|
|
1138
|
+
* @returns One `cannot-fire` diagnostic per unproven query receiver.
|
|
1139
|
+
*/
|
|
1140
|
+
export function checkUnprovenQueryReceivers(unproven, filePath) {
|
|
1141
|
+
return unproven.map((u) => ({
|
|
1142
|
+
analyzerName: 'schema',
|
|
1143
|
+
kind: 'cannot-fire',
|
|
1144
|
+
message: `Query-shaped call \`.${u.method}()\` on ${u.receiver}: ${u.reason}`,
|
|
1145
|
+
file: filePath,
|
|
1146
|
+
line: u.line,
|
|
1147
|
+
details: { receiver: u.receiver, method: u.method, reason: u.reason },
|
|
1148
|
+
}));
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Deduplicate `cannot-fire` diagnostics so one receiver gets one disposition
|
|
1152
|
+
* (Spec 69 §10 Q3).
|
|
1153
|
+
*
|
|
1154
|
+
* Two emission paths can both report the same unanswered question — "is this
|
|
1155
|
+
* receiver a DB handle?" — for one receiver:
|
|
1156
|
+
*
|
|
1157
|
+
* - {@link checkUnresolvedReceiverImports} — keyed to the *import* (`line: 0`,
|
|
1158
|
+
* `details.names`) for a receiver imported from an unresolvable `./db`-style
|
|
1159
|
+
* specifier.
|
|
1160
|
+
* - {@link checkUnprovenQueryReceivers} — keyed to the *call site* (real line,
|
|
1161
|
+
* `details.receiver`) for a query-shaped call through an unproven receiver.
|
|
1162
|
+
*
|
|
1163
|
+
* A receiver imported from an unresolvable specifier *and* called at a query
|
|
1164
|
+
* site therefore produced two diagnostics for one unanswered question, doubling
|
|
1165
|
+
* the coverage count and making the §10 finding↔coverage arithmetic reconcile
|
|
1166
|
+
* when it should not.
|
|
1167
|
+
*
|
|
1168
|
+
* The call-site diagnostic wins: it carries the precise line and method. The
|
|
1169
|
+
* import-level diagnostic is dropped for each name the call-site path already
|
|
1170
|
+
* dispositioned (compared by bare receiver name — `this.db` → `db`), and
|
|
1171
|
+
* survives only for names imported but never called, where it is the sole
|
|
1172
|
+
* signal that a receiver is unseen.
|
|
1173
|
+
*
|
|
1174
|
+
* @param diagnostics The merged `cannot-fire` diagnostics for one file.
|
|
1175
|
+
* @returns The same diagnostics, with redundant import-level signals removed.
|
|
1176
|
+
*/
|
|
1177
|
+
export function dedupeCannotFireByReceiver(diagnostics) {
|
|
1178
|
+
const callSiteReceivers = new Set();
|
|
1179
|
+
for (const d of diagnostics) {
|
|
1180
|
+
const receiver = d.details?.receiver;
|
|
1181
|
+
if (typeof receiver !== 'string' || receiver.length === 0)
|
|
1182
|
+
continue;
|
|
1183
|
+
const bare = receiver.split('.').pop() ?? receiver;
|
|
1184
|
+
callSiteReceivers.add(`${d.file}${bare}`);
|
|
1185
|
+
}
|
|
1186
|
+
const out = [];
|
|
1187
|
+
for (const d of diagnostics) {
|
|
1188
|
+
const names = d.details?.names;
|
|
1189
|
+
if (Array.isArray(names) && d.line === 0) {
|
|
1190
|
+
const remaining = names.filter((n) => typeof n === 'string' && !callSiteReceivers.has(`${d.file}${n}`));
|
|
1191
|
+
if (remaining.length === 0)
|
|
1192
|
+
continue; // fully dispositioned by the call-site path
|
|
1193
|
+
out.push({ ...d, details: { ...d.details, names: remaining } });
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
out.push(d);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
return out;
|
|
937
1200
|
}
|
|
938
1201
|
/**
|
|
939
1202
|
* Get the line/column location of the call expression.
|
|
@@ -981,6 +1244,34 @@ export function isSystemTable(table) {
|
|
|
981
1244
|
];
|
|
982
1245
|
return systemTables.some(st => table.toLowerCase() === st || table.toLowerCase().startsWith(st + '.'));
|
|
983
1246
|
}
|
|
1247
|
+
const TABLE_VALUED_FUNCTIONS = new Set([
|
|
1248
|
+
// SQLite
|
|
1249
|
+
'json_each', 'json_tree',
|
|
1250
|
+
// PostgreSQL
|
|
1251
|
+
'unnest', 'generate_series', 'generate_subscripts',
|
|
1252
|
+
'json_array_elements', 'jsonb_array_elements',
|
|
1253
|
+
'json_array_elements_text', 'jsonb_array_elements_text',
|
|
1254
|
+
'json_each_text', 'jsonb_each_text', 'jsonb_each',
|
|
1255
|
+
'json_object_keys', 'jsonb_object_keys',
|
|
1256
|
+
'regexp_split_to_table', 'string_to_table',
|
|
1257
|
+
// DuckDB / ClickHouse-ish readers
|
|
1258
|
+
'read_csv', 'read_csv_auto', 'read_parquet', 'read_json', 'read_json_auto',
|
|
1259
|
+
'parquet_scan', 'csv_scan', 'glob', 'range',
|
|
1260
|
+
]);
|
|
1261
|
+
const SQL_KEYWORDS = new Set([
|
|
1262
|
+
'select', 'from', 'where', 'join', 'inner', 'outer', 'left', 'right',
|
|
1263
|
+
'full', 'cross', 'on', 'and', 'or', 'not', 'in', 'as', 'is', 'null',
|
|
1264
|
+
'like', 'between', 'order', 'group', 'by', 'having', 'limit', 'offset',
|
|
1265
|
+
'union', 'all', 'distinct', 'case', 'when', 'then', 'else', 'end',
|
|
1266
|
+
'insert', 'into', 'values', 'update', 'set', 'delete', 'create',
|
|
1267
|
+
'table', 'alter', 'drop', 'index', 'view', 'if', 'exists', 'primary',
|
|
1268
|
+
'key', 'foreign', 'references', 'constraint', 'default', 'unique',
|
|
1269
|
+
'check', 'asc', 'desc', 'count', 'sum', 'avg', 'min', 'max',
|
|
1270
|
+
'skip', 'locked', 'nowait',
|
|
1271
|
+
'integer', 'text', 'varchar', 'text', 'boolean', 'float', 'blob',
|
|
1272
|
+
'real', 'timestamp', 'date', 'time', 'datetime', 'serial', 'bigint',
|
|
1273
|
+
'the', 'a', 'an',
|
|
1274
|
+
]);
|
|
984
1275
|
/**
|
|
985
1276
|
* True when `name` is a SQL table-valued function rather than a real table.
|
|
986
1277
|
* These appear after FROM/JOIN (`FROM json_each(...)`, `FROM generate_series(...)`)
|
|
@@ -996,21 +1287,7 @@ export function isSystemTable(table) {
|
|
|
996
1287
|
* @returns True when `name` is a known table-valued function.
|
|
997
1288
|
*/
|
|
998
1289
|
export function isTableValuedFunction(name) {
|
|
999
|
-
|
|
1000
|
-
// SQLite
|
|
1001
|
-
'json_each', 'json_tree',
|
|
1002
|
-
// PostgreSQL
|
|
1003
|
-
'unnest', 'generate_series', 'generate_subscripts',
|
|
1004
|
-
'json_array_elements', 'jsonb_array_elements',
|
|
1005
|
-
'json_array_elements_text', 'jsonb_array_elements_text',
|
|
1006
|
-
'json_each_text', 'jsonb_each_text', 'jsonb_each',
|
|
1007
|
-
'json_object_keys', 'jsonb_object_keys',
|
|
1008
|
-
'regexp_split_to_table', 'string_to_table',
|
|
1009
|
-
// DuckDB / ClickHouse-ish readers
|
|
1010
|
-
'read_csv', 'read_csv_auto', 'read_parquet', 'read_json', 'read_json_auto',
|
|
1011
|
-
'parquet_scan', 'csv_scan', 'glob', 'range',
|
|
1012
|
-
]);
|
|
1013
|
-
return tvfs.has(name.toLowerCase());
|
|
1290
|
+
return TABLE_VALUED_FUNCTIONS.has(name.toLowerCase());
|
|
1014
1291
|
}
|
|
1015
1292
|
/**
|
|
1016
1293
|
* Common SQL keywords and identifiers that are not real table names.
|
|
@@ -1019,77 +1296,7 @@ export function isTableValuedFunction(name) {
|
|
|
1019
1296
|
* @returns True when `word` is a SQL keyword/reserved identifier.
|
|
1020
1297
|
*/
|
|
1021
1298
|
export function isSqlKeyword(word) {
|
|
1022
|
-
|
|
1023
|
-
'select', 'from', 'where', 'join', 'inner', 'outer', 'left', 'right',
|
|
1024
|
-
'full', 'cross', 'on', 'and', 'or', 'not', 'in', 'as', 'is', 'null',
|
|
1025
|
-
'like', 'between', 'order', 'group', 'by', 'having', 'limit', 'offset',
|
|
1026
|
-
'union', 'all', 'distinct', 'case', 'when', 'then', 'else', 'end',
|
|
1027
|
-
'insert', 'into', 'values', 'update', 'set', 'delete', 'create',
|
|
1028
|
-
'table', 'alter', 'drop', 'index', 'view', 'if', 'exists', 'primary',
|
|
1029
|
-
'key', 'foreign', 'references', 'constraint', 'default', 'unique',
|
|
1030
|
-
'check', 'asc', 'desc', 'count', 'sum', 'avg', 'min', 'max',
|
|
1031
|
-
'skip', 'locked', 'nowait',
|
|
1032
|
-
'integer', 'text', 'varchar', 'text', 'boolean', 'float', 'blob',
|
|
1033
|
-
'real', 'timestamp', 'date', 'time', 'datetime', 'serial', 'bigint',
|
|
1034
|
-
'the', 'a', 'an',
|
|
1035
|
-
]);
|
|
1036
|
-
return keywords.has(word.toLowerCase());
|
|
1037
|
-
}
|
|
1038
|
-
/**
|
|
1039
|
-
* True when the `from` keyword at `fromIndex` is a JavaScript/TypeScript
|
|
1040
|
-
* module specifier (`import x from 'mod'`, `import { a } from 'mod'`,
|
|
1041
|
-
* `export { a } from 'mod'`, `export * from 'mod'`) rather than a SQL
|
|
1042
|
-
* FROM clause.
|
|
1043
|
-
*
|
|
1044
|
-
* Step (3) of findTableReferences() full-source scans `.ts` files under
|
|
1045
|
-
* `migrations/`, where a case-insensitive `\bFROM\s+([`"']?)...` pattern
|
|
1046
|
-
* matches `from 'typeorm'` / `from 'path'` / `from 'dotenv'` and records the
|
|
1047
|
-
* module name as an unknown table. SQL never introduces FROM with
|
|
1048
|
-
* `import`/`export`, so requiring that introducer at a statement boundary is
|
|
1049
|
-
* unambiguous — and only checking the introducer at a line/`;` boundary
|
|
1050
|
-
* avoids misreading a SQL comment (`-- import data`) or a table named
|
|
1051
|
-
* `import_log` as a module statement.
|
|
1052
|
-
*
|
|
1053
|
-
* @param sqlText The source text being scanned for a FROM introducer.
|
|
1054
|
-
* @param fromIndex The character index of the `from` keyword within `sqlText`.
|
|
1055
|
-
* @returns True when the introducer preceding `fromIndex` is an import/export.
|
|
1056
|
-
*/
|
|
1057
|
-
export function isDeleteFrom(sqlText, fromIndex) {
|
|
1058
|
-
// `DELETE FROM` is a write: the table is captured by the DELETE pattern, and
|
|
1059
|
-
// the generic FROM pattern must not re-tag it as a read. Check whether the
|
|
1060
|
-
// word immediately preceding the FROM keyword (across any whitespace) is
|
|
1061
|
-
// `DELETE` — the only SQL keyword that introduces a write-FROM. `SELECT …
|
|
1062
|
-
// FROM`, `JOIN`, and `INSERT INTO … SELECT … FROM` all keep their read FROM.
|
|
1063
|
-
return /\bDELETE\s+$/iu.test(sqlText.slice(0, fromIndex));
|
|
1064
|
-
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Whether the `from` keyword at `fromIndex` is the FROM of a module
|
|
1067
|
-
* `import`/`export` statement rather than the read-FROM of a SQL `SELECT`/`JOIN`.
|
|
1068
|
-
*
|
|
1069
|
-
* Scans back from `fromIndex` to the enclosing statement boundary (a `;` or a
|
|
1070
|
-
* blank line — a module introducer never crosses either) and checks that the
|
|
1071
|
-
* prefix is introduced by `import`/`export`. This keeps `SELECT … FROM`,
|
|
1072
|
-
* `JOIN …`, and `INSERT … SELECT … FROM` out of the module-import count while
|
|
1073
|
-
* letting `IMPORT … FROM` and `EXPORT … FROM` through.
|
|
1074
|
-
*
|
|
1075
|
-
* @param sqlText The source text being scanned.
|
|
1076
|
-
* @param fromIndex The character index of the `from` keyword within `sqlText`.
|
|
1077
|
-
* @returns True when the introducer preceding `fromIndex` is an import/export.
|
|
1078
|
-
*/
|
|
1079
|
-
export function isModuleImportFrom(sqlText, fromIndex) {
|
|
1080
|
-
let start = fromIndex;
|
|
1081
|
-
while (start > 0) {
|
|
1082
|
-
const prev = sqlText[start - 1];
|
|
1083
|
-
// Stop at a statement terminator or a blank line — a module statement's
|
|
1084
|
-
// `import`/`export` introducer never crosses these.
|
|
1085
|
-
if (prev === ';')
|
|
1086
|
-
break;
|
|
1087
|
-
if (prev === '\n' && start >= 2 && sqlText[start - 2] === '\n')
|
|
1088
|
-
break;
|
|
1089
|
-
start--;
|
|
1090
|
-
}
|
|
1091
|
-
const prefix = sqlText.slice(start, fromIndex);
|
|
1092
|
-
return /(?:^|[\n;])\s*(?:import|export)\b/m.test(prefix);
|
|
1299
|
+
return SQL_KEYWORDS.has(word.toLowerCase());
|
|
1093
1300
|
}
|
|
1094
1301
|
/**
|
|
1095
1302
|
* SQL statement keyword patterns that mark a query. Shared by the standalone
|
|
@@ -1100,21 +1307,74 @@ export function isModuleImportFrom(sqlText, fromIndex) {
|
|
|
1100
1307
|
* statement, not a second query (Spec 52 R2).
|
|
1101
1308
|
*/
|
|
1102
1309
|
const SQL_QUERY_PATTERNS = [
|
|
1103
|
-
/SELECT\s+/gi,
|
|
1104
|
-
/INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO|REPLACE\s+INTO/gi,
|
|
1105
|
-
/(?<!DO\s)(?<!KEY\s)UPDATE\s+/gi,
|
|
1106
|
-
/DELETE\s+FROM/gi,
|
|
1310
|
+
{ keyword: 'SELECT', pattern: /SELECT\s+/gi },
|
|
1311
|
+
{ keyword: 'INSERT', pattern: /INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO|REPLACE\s+INTO/gi },
|
|
1312
|
+
{ keyword: 'UPDATE', pattern: /(?<!DO\s)(?<!KEY\s)UPDATE\s+/gi },
|
|
1313
|
+
{ keyword: 'DELETE', pattern: /DELETE\s+FROM/gi },
|
|
1107
1314
|
];
|
|
1108
|
-
/** Number of SQL-statement keyword occurrences a text slice contains.
|
|
1109
|
-
|
|
1315
|
+
/** Number of SQL-statement keyword occurrences a text slice contains.
|
|
1316
|
+
*
|
|
1317
|
+
* @param text The text slice to count SQL keywords over.
|
|
1318
|
+
* @returns The number of SQL-statement keyword occurrences.
|
|
1319
|
+
*/
|
|
1320
|
+
export function countSqlKeywordOccurrences(text) {
|
|
1110
1321
|
let count = 0;
|
|
1111
|
-
for (const pattern of SQL_QUERY_PATTERNS) {
|
|
1322
|
+
for (const { pattern } of SQL_QUERY_PATTERNS) {
|
|
1112
1323
|
const matches = text.match(pattern);
|
|
1113
1324
|
if (matches)
|
|
1114
1325
|
count += matches.length;
|
|
1115
1326
|
}
|
|
1116
1327
|
return count;
|
|
1117
1328
|
}
|
|
1329
|
+
/** Located SQL-keyword occurrences — the offset form of
|
|
1330
|
+
* {@link countSqlKeywordOccurrences}, returning each keyword's start offset and
|
|
1331
|
+
* its label. The caller passes call-body-stripped text (same length as the
|
|
1332
|
+
* original, so offsets map 1:1 onto it). */
|
|
1333
|
+
function findSqlKeywordOffsets(text) {
|
|
1334
|
+
const sites = [];
|
|
1335
|
+
for (const { keyword, pattern } of SQL_QUERY_PATTERNS) {
|
|
1336
|
+
pattern.lastIndex = 0;
|
|
1337
|
+
let m;
|
|
1338
|
+
while ((m = pattern.exec(text)) !== null) {
|
|
1339
|
+
sites.push({ offset: m.index, method: keyword });
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return sites;
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Locate every DB-query site in a source slice — the located counterpart of
|
|
1346
|
+
* {@link countQueries}. The three components match `countQueries` exactly (the
|
|
1347
|
+
* eager execution-method call sites, the `.exec`-with-SQL sites, and the
|
|
1348
|
+
* standalone SQL-keyword sites over the call-body-stripped text), so the two can
|
|
1349
|
+
* never drift: `countQueries` is `extractQuerySiteOffsets(...).length`. Each
|
|
1350
|
+
* site's `offset` points at the method name (for a call) or keyword start (for a
|
|
1351
|
+
* standalone keyword), and `method` is the label the fact carries.
|
|
1352
|
+
*
|
|
1353
|
+
* @param text The source slice to scan.
|
|
1354
|
+
* @returns One located site per query the slice issues.
|
|
1355
|
+
*/
|
|
1356
|
+
export function extractQuerySiteOffsets(text) {
|
|
1357
|
+
const sites = [];
|
|
1358
|
+
// Eager execution-method call sites.
|
|
1359
|
+
const callRe = /\.(query|execute|run|first|raw|batch)\b[^()\n]*\(|(?<!Promise)\.(all)\b[^()\n]*\(/g;
|
|
1360
|
+
let m;
|
|
1361
|
+
while ((m = callRe.exec(text)) !== null) {
|
|
1362
|
+
sites.push({ offset: m.index + 1, method: m[1] ?? m[2] });
|
|
1363
|
+
}
|
|
1364
|
+
// `.exec(...)` sites whose balanced body carries a SQL keyword.
|
|
1365
|
+
const execRe = /\.exec\b[^()\n]*\(/g;
|
|
1366
|
+
while ((m = execRe.exec(text)) !== null) {
|
|
1367
|
+
const openParen = m.index + m[0].length;
|
|
1368
|
+
const i = scanBalancedParens(text, openParen);
|
|
1369
|
+
if (countSqlKeywordOccurrences(text.slice(openParen, i - 1)) > 0) {
|
|
1370
|
+
sites.push({ offset: m.index + 1, method: 'exec' });
|
|
1371
|
+
}
|
|
1372
|
+
execRe.lastIndex = i;
|
|
1373
|
+
}
|
|
1374
|
+
// Standalone SQL keywords outside recognized call bodies.
|
|
1375
|
+
sites.push(...findSqlKeywordOffsets(stripQueryCallBodies(text)));
|
|
1376
|
+
return sites;
|
|
1377
|
+
}
|
|
1118
1378
|
/**
|
|
1119
1379
|
* Count the number of DB queries a function body issues — by *call site*, not
|
|
1120
1380
|
* by SQL literal (defect #47: counting literals made parameterizing a query into
|
|
@@ -1142,11 +1402,7 @@ function countSqlKeywordOccurrences(text) {
|
|
|
1142
1402
|
* @returns The number of DB queries the function issues.
|
|
1143
1403
|
*/
|
|
1144
1404
|
export function countQueries(text) {
|
|
1145
|
-
|
|
1146
|
-
const execCount = countExecCallsWithSql(text);
|
|
1147
|
-
const bodyless = stripQueryCallBodies(text);
|
|
1148
|
-
const sqlCount = countSqlKeywordOccurrences(bodyless);
|
|
1149
|
-
return callCount + execCount + sqlCount;
|
|
1405
|
+
return extractQuerySiteOffsets(text).length;
|
|
1150
1406
|
}
|
|
1151
1407
|
/**
|
|
1152
1408
|
* Scan forward from just after an opening `(` (at `openParen`) to the matching
|
|
@@ -1277,7 +1533,7 @@ const FUNCTION_NODE_TYPES = new Set([
|
|
|
1277
1533
|
'function_declaration',
|
|
1278
1534
|
'function_expression',
|
|
1279
1535
|
'generator_function_declaration',
|
|
1280
|
-
'
|
|
1536
|
+
'generator_function',
|
|
1281
1537
|
'method_definition',
|
|
1282
1538
|
]);
|
|
1283
1539
|
/**
|
|
@@ -1290,6 +1546,10 @@ const FUNCTION_NODE_TYPES = new Set([
|
|
|
1290
1546
|
* than carrying a divergent copy (the old copies differed only in whether the
|
|
1291
1547
|
* walk started at `node` or `parent(node)`, which is equivalent for every real
|
|
1292
1548
|
* call site, all of which hand in a leaf or call node, never a function node).
|
|
1549
|
+
* @param node The AST node to start walking from.
|
|
1550
|
+
* @param adapter The language adapter for parent/type traversal.
|
|
1551
|
+
* @param filePath The file path recorded in the identity.
|
|
1552
|
+
* @returns The enclosing function's identity (or a top-level identity).
|
|
1293
1553
|
*/
|
|
1294
1554
|
export function findEnclosingFunctionIdentity(node, adapter, filePath) {
|
|
1295
1555
|
let current = node;
|
|
@@ -1324,4 +1584,70 @@ export function functionIdentityLabel(id) {
|
|
|
1324
1584
|
return 'top-level';
|
|
1325
1585
|
return id.name ?? `fn:${id.startLine}:${id.startColumn}`;
|
|
1326
1586
|
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Project every node `findEnclosingFunctionIdentity` would match — the six
|
|
1589
|
+
* `FUNCTION_NODE_TYPES` plus `adapter.isMethod` — into a `FunctionSpan`. The set
|
|
1590
|
+
* and the `adapter.getNodeName` text are byte-identical to the identity walk, so
|
|
1591
|
+
* a corpus-side "innermost span containing the location" reproduces the walk's
|
|
1592
|
+
* result (a nested function's start is inside its parent's span, hence later, so
|
|
1593
|
+
* the containing span with the latest start is the deepest one).
|
|
1594
|
+
*
|
|
1595
|
+
* @param ast The parsed AST whose enclosing functions are projected.
|
|
1596
|
+
* @param adapter The language adapter for type/name traversal.
|
|
1597
|
+
* @returns One `FunctionSpan` per enclosing-function node, in walk pre-order.
|
|
1598
|
+
*/
|
|
1599
|
+
export function extractEnclosingFunctionSpans(ast, adapter) {
|
|
1600
|
+
const spans = [];
|
|
1601
|
+
const walk = (node) => {
|
|
1602
|
+
const type = adapter.getNodeType(node);
|
|
1603
|
+
if (FUNCTION_NODE_TYPES.has(type) || adapter.isMethod(node)) {
|
|
1604
|
+
spans.push({
|
|
1605
|
+
startLine: node.location.start.line,
|
|
1606
|
+
startColumn: node.location.start.column,
|
|
1607
|
+
endLine: node.location.end.line,
|
|
1608
|
+
endColumn: node.location.end.column,
|
|
1609
|
+
name: adapter.getNodeName(node),
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
if (node.children) {
|
|
1613
|
+
for (const child of node.children)
|
|
1614
|
+
walk(child);
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
walk(ast.root);
|
|
1618
|
+
return spans;
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Project every `string_fragment` leaf into a `StringFragmentSpan`. The legacy
|
|
1622
|
+
* re-home's `findClosestNodeAt(ref.location)` returns the deepest node containing
|
|
1623
|
+
* the reference; for a table name parsed out of a SQL string that node is a
|
|
1624
|
+
* `string_fragment` (the `alias(…, $.string_fragment)` token, a named leaf). The
|
|
1625
|
+
* set and the start/end coordinates are byte-identical to that node, so a
|
|
1626
|
+
* corpus-side "innermost fragment containing the location" reproduces the
|
|
1627
|
+
* top-level coordinate (a reference is always inside exactly one fragment — the
|
|
1628
|
+
* fragments of one string are disjoint, and distinct strings are disjoint).
|
|
1629
|
+
*
|
|
1630
|
+
* @param ast The parsed AST whose string fragments are projected.
|
|
1631
|
+
* @param adapter The language adapter for type/child traversal.
|
|
1632
|
+
* @returns One `StringFragmentSpan` per `string_fragment` node, in walk pre-order.
|
|
1633
|
+
*/
|
|
1634
|
+
export function extractStringFragmentSpans(ast, adapter) {
|
|
1635
|
+
const spans = [];
|
|
1636
|
+
const walk = (node) => {
|
|
1637
|
+
if (adapter.getNodeType(node) === 'string_fragment') {
|
|
1638
|
+
spans.push({
|
|
1639
|
+
startLine: node.location.start.line,
|
|
1640
|
+
startColumn: node.location.start.column,
|
|
1641
|
+
endLine: node.location.end.line,
|
|
1642
|
+
endColumn: node.location.end.column,
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
if (node.children) {
|
|
1646
|
+
for (const child of node.children)
|
|
1647
|
+
walk(child);
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
walk(ast.root);
|
|
1651
|
+
return spans;
|
|
1652
|
+
}
|
|
1327
1653
|
//# sourceMappingURL=codeAnalysis.js.map
|