code-auditor-mcp 4.1.1 → 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 +338 -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.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +23 -35
- 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 +1538 -1033
- 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 +969 -821
- 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 +132 -271
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +365 -2729
- 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 +15 -0
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +15 -16
- 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 +16 -14
- 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/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 +18 -3
- package/dist/daemon/resolve.d.ts.map +1 -1
- package/dist/daemon/resolve.js +25 -4
- 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 +17 -8
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +16 -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 +827 -850
- 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 +1144 -984
- 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 +101 -71
- 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 +11 -5
- 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
|
@@ -18,28 +18,19 @@
|
|
|
18
18
|
* provenance — strict provenance only, never consults name lists
|
|
19
19
|
* names — legacy English-only name matching (opt-in escape hatch)
|
|
20
20
|
*/
|
|
21
|
+
import { dialectForPackage } from '../languages/sql/dialectDetection.js';
|
|
22
|
+
import { DB_PACKAGES, handleTypesForPackage } from './tsEcosystem.js';
|
|
23
|
+
import { buildBindingEnv, classifyRootIdentifier, extractInterfaceFields, resolveReceiverRoot, findEnclosingClassHeritage, } from './receiverRoot.js';
|
|
24
|
+
import { identifyHandle } from './handleIdentification.js';
|
|
25
|
+
import { stripSqlQuotes } from './sqlLiteral.js';
|
|
21
26
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
|
-
// Constants —
|
|
27
|
+
// Constants — validator packages
|
|
28
|
+
//
|
|
29
|
+
// The DB ecosystem (DB_PACKAGES / ORM_METHODS) was TypeScript/npm-only and
|
|
30
|
+
// moved behind the resolution interface to `tsEcosystem.ts`
|
|
31
|
+
// (correction-seams-not-placement §4). What remains here is the
|
|
32
|
+
// validator-package vocabulary, a distinct concern.
|
|
23
33
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
24
|
-
/** Database packages — spec R1.1 */
|
|
25
|
-
export const DB_PACKAGES = new Set([
|
|
26
|
-
'better-sqlite3',
|
|
27
|
-
'drizzle-orm',
|
|
28
|
-
'@prisma/client',
|
|
29
|
-
'pg',
|
|
30
|
-
'mysql2',
|
|
31
|
-
'postgres',
|
|
32
|
-
'kysely',
|
|
33
|
-
'knex',
|
|
34
|
-
'mongodb',
|
|
35
|
-
'mongoose',
|
|
36
|
-
'@libsql/client',
|
|
37
|
-
'@planetscale/database',
|
|
38
|
-
'@neondatabase/serverless',
|
|
39
|
-
'@vercel/postgres',
|
|
40
|
-
'bun:sqlite',
|
|
41
|
-
'node:sqlite',
|
|
42
|
-
]);
|
|
43
34
|
/** Validator packages — spec R4.1 */
|
|
44
35
|
export const VALIDATOR_PACKAGES = new Set([
|
|
45
36
|
'zod',
|
|
@@ -52,79 +43,6 @@ export const VALIDATOR_PACKAGES = new Set([
|
|
|
52
43
|
'@sinclair/typebox',
|
|
53
44
|
'class-validator',
|
|
54
45
|
]);
|
|
55
|
-
/** Known DB type names — spec R1.1 (propagation rule 8) */
|
|
56
|
-
export const DB_TYPES = new Set([
|
|
57
|
-
'D1Database',
|
|
58
|
-
'D1PreparedStatement',
|
|
59
|
-
'D1Result',
|
|
60
|
-
'Database',
|
|
61
|
-
'Pool',
|
|
62
|
-
'PrismaClient',
|
|
63
|
-
'Kysely',
|
|
64
|
-
'Connection',
|
|
65
|
-
'SqliteDatabase',
|
|
66
|
-
'BetterSQLite3Database',
|
|
67
|
-
]);
|
|
68
|
-
/**
|
|
69
|
-
* DB call methods — the fixed API surface (language-invariant).
|
|
70
|
-
*
|
|
71
|
-
* Spec 33 Item 11 FP category 5: the bare-identifier hybrid fallback in
|
|
72
|
-
* `isDBProvenanced` treated any `get(...)` / `each(...)` / `values(...)` call
|
|
73
|
-
* as a DB query, flagging lodash-style object accessors (e.g.
|
|
74
|
-
* `@directus/utils`'s `get(item, ...)`) as sql-injection. Those three names
|
|
75
|
-
* are also common non-DB methods (lodash `get`, jQuery/iterator
|
|
76
|
-
* `each`, Map/WebSocket `.values()`), so they are removed from the fallback —
|
|
77
|
-
* mirroring the `get`/`each` trim in CHANGELOG 3.4.9 (DB_CALL_METHOD_NAMES).
|
|
78
|
-
*
|
|
79
|
-
* `query` is deliberately RETAINED: it is a genuine query-execution method on
|
|
80
|
-
* mysql2, pg, node-postgres, D1 and Planetscale (`.query(...)`), and the
|
|
81
|
-
* spec-19 data-access fixtures exercise it as a canonical DB entry point.
|
|
82
|
-
* Removing it would turn real SQL-injection positives into false negatives.
|
|
83
|
-
*
|
|
84
|
-
* `raw` is deliberately retained: it is a genuine raw-execution method on
|
|
85
|
-
* D1 prepared statements, Knex, and Kysely, and the Item-6 taint-tracking
|
|
86
|
-
* fixtures exercise it as the canonical raw-SQL entry point.
|
|
87
|
-
*/
|
|
88
|
-
export const DB_CALL_METHODS = new Set([
|
|
89
|
-
'exec',
|
|
90
|
-
'prepare',
|
|
91
|
-
'batch',
|
|
92
|
-
'run',
|
|
93
|
-
'all',
|
|
94
|
-
'first',
|
|
95
|
-
'query',
|
|
96
|
-
'raw',
|
|
97
|
-
]);
|
|
98
|
-
/** ORM method patterns — fixed API surface for ORM recognition (Spec 21 R1) */
|
|
99
|
-
export const ORM_METHODS = new Set([
|
|
100
|
-
'find',
|
|
101
|
-
'findOne',
|
|
102
|
-
'findMany',
|
|
103
|
-
'findFirst',
|
|
104
|
-
'findUnique',
|
|
105
|
-
'select',
|
|
106
|
-
'insert',
|
|
107
|
-
'insertMany',
|
|
108
|
-
'update',
|
|
109
|
-
'updateOne',
|
|
110
|
-
'updateMany',
|
|
111
|
-
'delete',
|
|
112
|
-
'deleteOne',
|
|
113
|
-
'deleteMany',
|
|
114
|
-
'from',
|
|
115
|
-
'where',
|
|
116
|
-
'join',
|
|
117
|
-
'leftJoin',
|
|
118
|
-
'rightJoin',
|
|
119
|
-
'innerJoin',
|
|
120
|
-
'create',
|
|
121
|
-
'createMany',
|
|
122
|
-
'aggregate',
|
|
123
|
-
'count',
|
|
124
|
-
'distinct',
|
|
125
|
-
'execute',
|
|
126
|
-
'query',
|
|
127
|
-
]);
|
|
128
46
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
129
47
|
// Package matching
|
|
130
48
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -132,6 +50,20 @@ export const ORM_METHODS = new Set([
|
|
|
132
50
|
function matchesDBPackage(specifier) {
|
|
133
51
|
return [...DB_PACKAGES].some((pkg) => specifier === pkg || specifier.startsWith(pkg + '/'));
|
|
134
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* The base npm package name of a module specifier — `pg` for `pg`/`pg/lib`,
|
|
55
|
+
* `@neondatabase/serverless` for `@neondatabase/serverless` and its subpaths,
|
|
56
|
+
* `mysql2` for `mysql2/promise`. Used to stamp {@link ProvenanceEvidence.packageName}
|
|
57
|
+
* with the exact key `dialectForPackage` maps, so a subpath import still resolves
|
|
58
|
+
* to its driver's dialect.
|
|
59
|
+
*/
|
|
60
|
+
function basePackageName(specifier) {
|
|
61
|
+
if (specifier.startsWith('@')) {
|
|
62
|
+
const parts = specifier.split('/');
|
|
63
|
+
return parts.slice(0, 2).join('/');
|
|
64
|
+
}
|
|
65
|
+
return specifier.split('/')[0];
|
|
66
|
+
}
|
|
135
67
|
/** Check if a module specifier matches a validator package. */
|
|
136
68
|
function matchesValidatorPackage(specifier) {
|
|
137
69
|
return [...VALIDATOR_PACKAGES].some((pkg) => specifier === pkg || specifier.startsWith(pkg + '/'));
|
|
@@ -144,19 +76,38 @@ function matchesValidatorPackage(specifier) {
|
|
|
144
76
|
* An import like `import Database from 'better-sqlite3'` produces `Database` as
|
|
145
77
|
* provenanced with reason "package". Shared by the DB and validator variants,
|
|
146
78
|
* which differ only in the package predicate.
|
|
79
|
+
*
|
|
80
|
+
* `handleTypesFor` is the Decision A type filter. It maps a matched specifier to
|
|
81
|
+
* the package's handle-name set, or `undefined` when the caller wants no type
|
|
82
|
+
* filter (the validator variant — a validator package's every export is a
|
|
83
|
+
* validator). When the set is present, a *named* import seeds only if its
|
|
84
|
+
* original export name (`spec.name`, not the local alias) is in the set; a
|
|
85
|
+
* default or namespace import seeds unconditionally, because a DB package's
|
|
86
|
+
* default/namespace *is* its handle and tree-sitter records no original export
|
|
87
|
+
* name for a default import to match. This is what stops
|
|
88
|
+
* `import { KVNamespace } from '@cloudflare/workers-types'` from seeding while
|
|
89
|
+
* `import { D1Database }` still does.
|
|
90
|
+
*
|
|
147
91
|
* @param ast
|
|
148
92
|
* @param adapter
|
|
149
93
|
* @param matchesPackage
|
|
94
|
+
* @param handleTypesFor
|
|
150
95
|
* @returns
|
|
151
96
|
*/
|
|
152
|
-
function extractProvenancedImports(ast, adapter, matchesPackage) {
|
|
97
|
+
function extractProvenancedImports(ast, adapter, matchesPackage, handleTypesFor = () => undefined) {
|
|
153
98
|
const seedMap = new Map();
|
|
154
99
|
const imports = adapter.extractImports(ast);
|
|
155
100
|
for (const imp of imports) {
|
|
156
101
|
const specifier = imp.source;
|
|
157
102
|
if (!matchesPackage(specifier))
|
|
158
103
|
continue;
|
|
104
|
+
const handleTypes = handleTypesFor(specifier);
|
|
159
105
|
for (const spec of imp.specifiers) {
|
|
106
|
+
// Decision A: a named import seeds only when the package's manifest lists
|
|
107
|
+
// the original export name as a handle. Default/namespace imports seed
|
|
108
|
+
// unconditionally (see the doc above).
|
|
109
|
+
if (handleTypes && !spec.isDefault && !spec.isNamespace && !handleTypes.has(spec.name))
|
|
110
|
+
continue;
|
|
160
111
|
const localName = spec.alias ?? spec.name;
|
|
161
112
|
const label = spec.isDefault
|
|
162
113
|
? `default import from ${specifier}`
|
|
@@ -170,6 +121,7 @@ function extractProvenancedImports(ast, adapter, matchesPackage) {
|
|
|
170
121
|
reason: 'package',
|
|
171
122
|
source: label,
|
|
172
123
|
chain: [],
|
|
124
|
+
packageName: basePackageName(specifier),
|
|
173
125
|
});
|
|
174
126
|
}
|
|
175
127
|
}
|
|
@@ -184,7 +136,7 @@ function extractProvenancedImports(ast, adapter, matchesPackage) {
|
|
|
184
136
|
* @returns a map of local identifier → DB-provenance evidence
|
|
185
137
|
*/
|
|
186
138
|
export function extractDBProvenancedImports(ast, adapter) {
|
|
187
|
-
return extractProvenancedImports(ast, adapter, matchesDBPackage);
|
|
139
|
+
return extractProvenancedImports(ast, adapter, matchesDBPackage, handleTypesForPackage);
|
|
188
140
|
}
|
|
189
141
|
/**
|
|
190
142
|
* Extract all validator-provenanced identifiers from a file's imports.
|
|
@@ -204,40 +156,17 @@ export function extractValidatorProvenancedImports(ast, adapter) {
|
|
|
204
156
|
* Returns true if any new identifier was added.
|
|
205
157
|
*/
|
|
206
158
|
function propagateVariableDeclaration(node, ctx) {
|
|
207
|
-
const { nameNode, valueNode
|
|
159
|
+
const { nameNode, valueNode } = splitVariableDeclarator(node, ctx.adapter);
|
|
208
160
|
if (!nameNode)
|
|
209
161
|
return false;
|
|
210
|
-
let mutated = false;
|
|
211
|
-
// Rule 8: type annotation — let x: D1Database
|
|
212
|
-
if (typeAnnotationNode) {
|
|
213
|
-
mutated = propagateFromTypeAnnotation(nameNode, typeAnnotationNode, ctx);
|
|
214
|
-
}
|
|
215
162
|
if (valueNode) {
|
|
216
|
-
|
|
163
|
+
return propagateFromValue(nameNode, valueNode, ctx);
|
|
217
164
|
}
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
/** Rule 8: type annotation — `let x: D1Database`. */
|
|
221
|
-
function propagateFromTypeAnnotation(nameNode, typeAnnotationNode, ctx) {
|
|
222
|
-
if (nameNode.type !== 'identifier')
|
|
223
|
-
return false;
|
|
224
|
-
const typeText = ctx.adapter.getNodeText(typeAnnotationNode, ctx.sourceCode).trim();
|
|
225
|
-
if (!DB_TYPES.has(typeText))
|
|
226
|
-
return false;
|
|
227
|
-
const name = ctx.adapter.getNodeText(nameNode, ctx.sourceCode);
|
|
228
|
-
if (ctx.provenanceMap.has(name))
|
|
229
|
-
return false;
|
|
230
|
-
ctx.provenanceMap.set(name, {
|
|
231
|
-
identifier: name,
|
|
232
|
-
reason: 'type',
|
|
233
|
-
source: `type annotation ${typeText}`,
|
|
234
|
-
chain: [],
|
|
235
|
-
});
|
|
236
|
-
return true;
|
|
165
|
+
return false;
|
|
237
166
|
}
|
|
238
167
|
/** Propagate provenance from a declarator's value expression into its names. */
|
|
239
168
|
function propagateFromValue(nameNode, valueNode, ctx) {
|
|
240
|
-
const propagated = tryPropagateFromExpression(valueNode, ctx.adapter, ctx.sourceCode, ctx.provenanceMap);
|
|
169
|
+
const propagated = tryPropagateFromExpression(valueNode, ctx.adapter, ctx.sourceCode, ctx.provenanceMap, ctx.localFunctions);
|
|
241
170
|
if (!propagated)
|
|
242
171
|
return false;
|
|
243
172
|
let mutated = false;
|
|
@@ -248,6 +177,7 @@ function propagateFromValue(nameNode, valueNode, ctx) {
|
|
|
248
177
|
reason: 'propagation',
|
|
249
178
|
source: propagated.source,
|
|
250
179
|
chain: [...propagated.chain, propagated.identifier],
|
|
180
|
+
packageName: propagated.packageName,
|
|
251
181
|
});
|
|
252
182
|
mutated = true;
|
|
253
183
|
}
|
|
@@ -258,7 +188,7 @@ function propagateFromValue(nameNode, valueNode, ctx) {
|
|
|
258
188
|
* Propagate provenance through a default parameter (rule 6).
|
|
259
189
|
* Returns true if a new identifier was added.
|
|
260
190
|
*/
|
|
261
|
-
function propagateDefaultParameter(node, adapter, sourceCode, provenanceMap) {
|
|
191
|
+
function propagateDefaultParameter(node, adapter, sourceCode, provenanceMap, localFunctions) {
|
|
262
192
|
const children = adapter.getChildren(node);
|
|
263
193
|
// assignment_pattern has [left, right]
|
|
264
194
|
if (children.length < 2)
|
|
@@ -268,13 +198,14 @@ function propagateDefaultParameter(node, adapter, sourceCode, provenanceMap) {
|
|
|
268
198
|
if (leftNode.type !== 'identifier')
|
|
269
199
|
return false;
|
|
270
200
|
const paramName = adapter.getNodeText(leftNode, sourceCode);
|
|
271
|
-
const propagated = tryPropagateFromExpression(rightNode, adapter, sourceCode, provenanceMap);
|
|
201
|
+
const propagated = tryPropagateFromExpression(rightNode, adapter, sourceCode, provenanceMap, localFunctions);
|
|
272
202
|
if (propagated && !provenanceMap.has(paramName)) {
|
|
273
203
|
provenanceMap.set(paramName, {
|
|
274
204
|
identifier: paramName,
|
|
275
205
|
reason: 'propagation',
|
|
276
206
|
source: `default parameter = ${propagated.source}`,
|
|
277
207
|
chain: [...propagated.chain, propagated.identifier],
|
|
208
|
+
packageName: propagated.packageName,
|
|
278
209
|
});
|
|
279
210
|
return true;
|
|
280
211
|
}
|
|
@@ -284,33 +215,81 @@ function propagateDefaultParameter(node, adapter, sourceCode, provenanceMap) {
|
|
|
284
215
|
* Propagate provenance through a class field initialization (rule 7).
|
|
285
216
|
* Returns true if a new identifier was added.
|
|
286
217
|
*/
|
|
287
|
-
function propagateClassField(node, adapter, sourceCode, provenanceMap) {
|
|
218
|
+
function propagateClassField(node, adapter, sourceCode, provenanceMap, localFunctions) {
|
|
288
219
|
const children = adapter.getChildren(node);
|
|
289
|
-
// Typically [name, value] or [decorators..., name, value]
|
|
220
|
+
// Typically [name, value], [modifier, name, type], or [decorators..., name, value].
|
|
290
221
|
const nameChild = children.find((c) => c.type === 'property_identifier');
|
|
222
|
+
if (!nameChild)
|
|
223
|
+
return false;
|
|
224
|
+
const fieldName = adapter.getNodeText(nameChild, sourceCode);
|
|
225
|
+
// Rule 7 — class field initialized with a DB value (`private db = new Database()`).
|
|
226
|
+
// `accessibility_modifier` and `type_annotation` are excluded so the value is
|
|
227
|
+
// found even on a modified or annotated field.
|
|
291
228
|
const valueChild = children.find((c) => c.type !== 'property_identifier' &&
|
|
292
229
|
c.type !== 'decorator' &&
|
|
230
|
+
c.type !== 'accessibility_modifier' &&
|
|
293
231
|
c.type !== 'private' &&
|
|
294
232
|
c.type !== 'public' &&
|
|
295
233
|
c.type !== 'protected' &&
|
|
296
234
|
c.type !== 'static' &&
|
|
297
235
|
c.type !== 'readonly' &&
|
|
298
|
-
c.type !== 'abstract'
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const propagated = tryPropagateFromExpression(valueChild, adapter, sourceCode, provenanceMap);
|
|
236
|
+
c.type !== 'abstract' &&
|
|
237
|
+
c.type !== 'type_annotation');
|
|
238
|
+
if (valueChild) {
|
|
239
|
+
const propagated = tryPropagateFromExpression(valueChild, adapter, sourceCode, provenanceMap, localFunctions);
|
|
302
240
|
if (propagated && !provenanceMap.has(fieldName)) {
|
|
303
241
|
provenanceMap.set(fieldName, {
|
|
304
242
|
identifier: fieldName,
|
|
305
243
|
reason: 'propagation',
|
|
306
244
|
source: `class field initialized from ${propagated.source}`,
|
|
307
245
|
chain: [...propagated.chain, propagated.identifier],
|
|
246
|
+
packageName: propagated.packageName,
|
|
308
247
|
});
|
|
309
248
|
return true;
|
|
310
249
|
}
|
|
311
250
|
}
|
|
312
251
|
return false;
|
|
313
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* S5b (form 4) — propagate provenance through a member assignment
|
|
255
|
+
* (`this.db = new Database()`). The RHS is a DB-provenanced expression (a
|
|
256
|
+
* `new X()` where X is an in-repo class that resolves, or a provenanced
|
|
257
|
+
* identifier), so the assigned field is a DB handle. This is the `new X()`
|
|
258
|
+
* binding form for the `this.<field> = …` assignment shape that no
|
|
259
|
+
* `variable_declarator`/`field_definition` rule covers.
|
|
260
|
+
*/
|
|
261
|
+
function propagateMemberAssignment(node, ctx) {
|
|
262
|
+
const { adapter, sourceCode, provenanceMap } = ctx;
|
|
263
|
+
const children = adapter.getChildren(node);
|
|
264
|
+
// children: [member_expression (this.db), <RHS expression>].
|
|
265
|
+
const lhs = children.find((c) => c.type === 'member_expression' || c.type === 'selector_expression');
|
|
266
|
+
if (!lhs)
|
|
267
|
+
return false;
|
|
268
|
+
const lhsChildren = adapter.getChildren(lhs);
|
|
269
|
+
const obj = lhsChildren.find((c) => c.type === 'this' || c.type === 'super');
|
|
270
|
+
if (!obj)
|
|
271
|
+
return false;
|
|
272
|
+
const prop = lhsChildren.find((c) => c.type === 'property_identifier' || c.type === 'field_identifier');
|
|
273
|
+
if (!prop)
|
|
274
|
+
return false;
|
|
275
|
+
const fieldName = adapter.getNodeText(prop, sourceCode);
|
|
276
|
+
if (!fieldName || provenanceMap.has(fieldName))
|
|
277
|
+
return false;
|
|
278
|
+
const rhs = children[children.length - 1];
|
|
279
|
+
if (!rhs || rhs.type === 'member_expression' || rhs.type === 'selector_expression')
|
|
280
|
+
return false;
|
|
281
|
+
const propagated = tryPropagateFromExpression(rhs, adapter, sourceCode, provenanceMap, ctx.localFunctions);
|
|
282
|
+
if (!propagated)
|
|
283
|
+
return false;
|
|
284
|
+
provenanceMap.set(fieldName, {
|
|
285
|
+
identifier: fieldName,
|
|
286
|
+
reason: 'propagation',
|
|
287
|
+
source: `this.${fieldName} assigned from ${propagated.source}`,
|
|
288
|
+
chain: [...propagated.chain, propagated.identifier],
|
|
289
|
+
packageName: propagated.packageName,
|
|
290
|
+
});
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
314
293
|
function applyPropagationRule(node, parent, ctx) {
|
|
315
294
|
const { adapter, sourceCode, provenanceMap } = ctx;
|
|
316
295
|
if (node.type === 'variable_declarator') {
|
|
@@ -318,19 +297,22 @@ function applyPropagationRule(node, parent, ctx) {
|
|
|
318
297
|
}
|
|
319
298
|
if (node.type === 'assignment_pattern' &&
|
|
320
299
|
parent?.type === 'formal_parameters') {
|
|
321
|
-
return propagateDefaultParameter(node, adapter, sourceCode, provenanceMap);
|
|
300
|
+
return propagateDefaultParameter(node, adapter, sourceCode, provenanceMap, ctx.localFunctions);
|
|
301
|
+
}
|
|
302
|
+
if (node.type === 'public_field_definition') {
|
|
303
|
+
return propagateClassField(node, adapter, sourceCode, provenanceMap, ctx.localFunctions);
|
|
322
304
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return
|
|
305
|
+
// S5b (form 4) — `this.db = new Database()` / `this.db = <provenanced>`.
|
|
306
|
+
if (node.type === 'assignment_expression') {
|
|
307
|
+
return propagateMemberAssignment(node, ctx);
|
|
326
308
|
}
|
|
327
309
|
return false;
|
|
328
310
|
}
|
|
329
311
|
/**
|
|
330
|
-
* Propagate provenance through assignments, destructuring,
|
|
331
|
-
*
|
|
312
|
+
* Propagate provenance through assignments, destructuring, default
|
|
313
|
+
* parameters, and class-field initializers within a single file.
|
|
332
314
|
*
|
|
333
|
-
* The
|
|
315
|
+
* The 7 single-file propagation rules (spec R1):
|
|
334
316
|
* 1. new Expression → variable
|
|
335
317
|
* 2. DB-provenanced call return → variable
|
|
336
318
|
* 3. member expression on DB receiver → variable
|
|
@@ -338,7 +320,6 @@ function applyPropagationRule(node, parent, ctx) {
|
|
|
338
320
|
* 5. array destructuring from DB source
|
|
339
321
|
* 6. default parameter with DB value
|
|
340
322
|
* 7. class field initialized with DB value
|
|
341
|
-
* 8. type annotation with known DB type
|
|
342
323
|
* @param adapter
|
|
343
324
|
* @param ast
|
|
344
325
|
* @param seedMap
|
|
@@ -348,7 +329,12 @@ function applyPropagationRule(node, parent, ctx) {
|
|
|
348
329
|
export function propagateProvenance(ast, adapter, sourceCode, seedMap) {
|
|
349
330
|
// Work on a copy so we can add newly-provenanced identifiers during the walk
|
|
350
331
|
const provenanceMap = new Map(seedMap);
|
|
351
|
-
const ctx = {
|
|
332
|
+
const ctx = {
|
|
333
|
+
adapter,
|
|
334
|
+
sourceCode,
|
|
335
|
+
provenanceMap,
|
|
336
|
+
localFunctions: collectLocalFunctionNames(ast, adapter, sourceCode),
|
|
337
|
+
};
|
|
352
338
|
// Keep iterating until no new identifiers are discovered (handles chains)
|
|
353
339
|
let changed = true;
|
|
354
340
|
let iterations = 0;
|
|
@@ -364,6 +350,37 @@ export function propagateProvenance(ast, adapter, sourceCode, seedMap) {
|
|
|
364
350
|
}
|
|
365
351
|
return provenanceMap;
|
|
366
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* Names of functions *declared in this file* — named function declarations,
|
|
355
|
+
* method definitions, and arrow/function expressions bound to a variable.
|
|
356
|
+
* S5f consults this to refuse to forward a provenanced argument through a local
|
|
357
|
+
* function: the local function's own return is resolved by
|
|
358
|
+
* `detectDbReturningFunctions` (returned construction), not by
|
|
359
|
+
* the "it takes a handle, so it must return one" heuristic that is only sound
|
|
360
|
+
* for *external* factories like `enhancePrisma(PrismaClient)`.
|
|
361
|
+
*/
|
|
362
|
+
function collectLocalFunctionNames(ast, adapter, sourceCode) {
|
|
363
|
+
const names = new Set();
|
|
364
|
+
walkAST(ast.root, (node) => {
|
|
365
|
+
if (FUNCTION_NODE_TYPES.has(node.type)) {
|
|
366
|
+
const name = adapter.getNodeName(node);
|
|
367
|
+
if (name)
|
|
368
|
+
names.add(name);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (node.type === 'variable_declarator') {
|
|
372
|
+
const { nameNode, valueNode } = splitVariableDeclarator(node, adapter);
|
|
373
|
+
if (!nameNode || !valueNode)
|
|
374
|
+
return;
|
|
375
|
+
const vt = valueNode.type;
|
|
376
|
+
if (vt === 'arrow_function' || vt === 'function_expression' || vt === 'generator_function') {
|
|
377
|
+
for (const n of extractPatternNames(nameNode, adapter, sourceCode))
|
|
378
|
+
names.add(n);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
return names;
|
|
383
|
+
}
|
|
367
384
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
368
385
|
// Propagation helpers
|
|
369
386
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -371,7 +388,22 @@ export function propagateProvenance(ast, adapter, sourceCode, seedMap) {
|
|
|
371
388
|
* Given a value expression node, check if it indicates DB provenance
|
|
372
389
|
* and return the evidence of the provenanced source if so.
|
|
373
390
|
*/
|
|
374
|
-
function tryPropagateFromExpression(node, adapter, sourceCode, provenanceMap) {
|
|
391
|
+
function tryPropagateFromExpression(node, adapter, sourceCode, provenanceMap, localFunctions) {
|
|
392
|
+
// ── Rule 0: await x — unwrap the await and propagate from its operand ──
|
|
393
|
+
// Mirrors getCallExpressionCallee, which already recurses through
|
|
394
|
+
// await_expression. `await` is a significant anonymous child (converter.ts
|
|
395
|
+
// SIGNIFICANT_ANONYMOUS_TYPES) and is skipped; the operand carries the
|
|
396
|
+
// provenance (e.g. `const x = await factory()`).
|
|
397
|
+
if (node.type === 'await_expression') {
|
|
398
|
+
for (const child of adapter.getChildren(node)) {
|
|
399
|
+
if (child.type === 'await')
|
|
400
|
+
continue;
|
|
401
|
+
const result = tryPropagateFromExpression(child, adapter, sourceCode, provenanceMap, localFunctions);
|
|
402
|
+
if (result)
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
375
407
|
// ── Rule 1: new Database(...) ──
|
|
376
408
|
if (node.type === 'new_expression') {
|
|
377
409
|
const constructorNode = findChildOfType(node, [
|
|
@@ -388,7 +420,7 @@ function tryPropagateFromExpression(node, adapter, sourceCode, provenanceMap) {
|
|
|
388
420
|
// ── Rule 2: drizzle(env.DB) — call where callee is DB-provenanced ──
|
|
389
421
|
// ── Rule 3: db.prepare(sql) — member expression call on DB receiver ──
|
|
390
422
|
if (node.type === 'call_expression') {
|
|
391
|
-
const viaCall = tryCallProvenance(node, adapter, sourceCode, provenanceMap);
|
|
423
|
+
const viaCall = tryCallProvenance(node, adapter, sourceCode, provenanceMap, localFunctions);
|
|
392
424
|
if (viaCall)
|
|
393
425
|
return viaCall;
|
|
394
426
|
}
|
|
@@ -423,7 +455,7 @@ function lookupEvidence(provenanceMap, name) {
|
|
|
423
455
|
* identifier (e.g. `drizzle(...)`) or a member expression on a DB receiver
|
|
424
456
|
* (e.g. `db.prepare(sql)`).
|
|
425
457
|
*/
|
|
426
|
-
function tryCallProvenance(node, adapter, sourceCode, provenanceMap) {
|
|
458
|
+
function tryCallProvenance(node, adapter, sourceCode, provenanceMap, localFunctions) {
|
|
427
459
|
const calleeNode = getCallExpressionCallee(node, adapter);
|
|
428
460
|
if (!calleeNode)
|
|
429
461
|
return null;
|
|
@@ -433,6 +465,12 @@ function tryCallProvenance(node, adapter, sourceCode, provenanceMap) {
|
|
|
433
465
|
const evidence = lookupEvidence(provenanceMap, name);
|
|
434
466
|
if (evidence)
|
|
435
467
|
return evidence;
|
|
468
|
+
// S5f — higher-order wrapper: a plain-identifier call whose callee is NOT
|
|
469
|
+
// provenanced but forwards a provenanced DB-handle argument
|
|
470
|
+
// (`enhancePrisma(PrismaClient)`). The call result is treated as a DB handle.
|
|
471
|
+
const argEvidence = tryProvenancedArgument(node, adapter, sourceCode, provenanceMap, localFunctions);
|
|
472
|
+
if (argEvidence)
|
|
473
|
+
return argEvidence;
|
|
436
474
|
}
|
|
437
475
|
// Case: member expression — db.prepare(...)
|
|
438
476
|
if (calleeNode.type === 'member_expression') {
|
|
@@ -444,14 +482,55 @@ function tryCallProvenance(node, adapter, sourceCode, provenanceMap) {
|
|
|
444
482
|
return null;
|
|
445
483
|
}
|
|
446
484
|
/**
|
|
447
|
-
*
|
|
448
|
-
*
|
|
485
|
+
* S5f — a call whose callee is unprovenanced but whose *argument* is a
|
|
486
|
+
* DB-provenanced expression (`enhancePrisma(PrismaClient)`) forwards the handle,
|
|
487
|
+
* so the call result is a DB handle (a wrapper constructor / factory). Restricted
|
|
488
|
+
* to a plain-identifier callee so member-expression calls like `JSON.stringify(db)`
|
|
489
|
+
* or `console.log(db)` — whose result is a string / undefined, not a handle — are
|
|
490
|
+
* not swept in.
|
|
491
|
+
*/
|
|
492
|
+
function tryProvenancedArgument(node, adapter, sourceCode, provenanceMap, localFunctions) {
|
|
493
|
+
// S5f is only sound for *external* factories (`enhancePrisma(PrismaClient)`).
|
|
494
|
+
// A local function that merely takes a handle does not therefore return one —
|
|
495
|
+
// e.g. `loadCallGraphData(indexHandle)` returns an in-memory graph, not a
|
|
496
|
+
// handle — and its return is resolved by `detectDbReturningFunctions`. Refusing
|
|
497
|
+
// to forward through a locally-declared function breaks the cascade that labels
|
|
498
|
+
// plain Maps/Sets as DB handles (the source of the index-code N+1 false
|
|
499
|
+
// positives). `localFunctions` is undefined in the `detectDbReturningFunctions`
|
|
500
|
+
// return-check, where S5f's canonical imported-factory case must still fire.
|
|
501
|
+
if (localFunctions) {
|
|
502
|
+
const calleeNode = getCallExpressionCallee(node, adapter);
|
|
503
|
+
if (calleeNode?.type === 'identifier') {
|
|
504
|
+
const calleeName = adapter.getNodeText(calleeNode, sourceCode);
|
|
505
|
+
if (calleeName && localFunctions.has(calleeName))
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const argsNode = adapter.getChildren(node).find((c) => c.type === 'arguments');
|
|
510
|
+
if (!argsNode)
|
|
511
|
+
return null;
|
|
512
|
+
for (const arg of adapter.getChildren(argsNode)) {
|
|
513
|
+
if (arg.type === '(' || arg.type === ')' || arg.type === ',')
|
|
514
|
+
continue;
|
|
515
|
+
const evidence = tryPropagateFromExpression(arg, adapter, sourceCode, provenanceMap, localFunctions);
|
|
516
|
+
if (evidence) {
|
|
517
|
+
return {
|
|
518
|
+
identifier: evidence.identifier,
|
|
519
|
+
reason: 'wrapper',
|
|
520
|
+
source: `higher-order wrapper forwards DB handle \`${evidence.identifier}\``,
|
|
521
|
+
chain: evidence.chain,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Split a variable_declarator into its name and value child nodes.
|
|
449
529
|
*/
|
|
450
530
|
function splitVariableDeclarator(node, adapter) {
|
|
451
531
|
const children = adapter.getChildren(node);
|
|
452
532
|
let nameNode = null;
|
|
453
533
|
let valueNode = null;
|
|
454
|
-
let typeAnnotationNode = null;
|
|
455
534
|
let pastEquals = false;
|
|
456
535
|
for (const child of children) {
|
|
457
536
|
if (child.type === '=' || child.type === 'equals') {
|
|
@@ -460,10 +539,8 @@ function splitVariableDeclarator(node, adapter) {
|
|
|
460
539
|
}
|
|
461
540
|
if (child.type === ':')
|
|
462
541
|
continue;
|
|
463
|
-
if (child.type === 'type_annotation')
|
|
464
|
-
typeAnnotationNode = child;
|
|
542
|
+
if (child.type === 'type_annotation')
|
|
465
543
|
continue;
|
|
466
|
-
}
|
|
467
544
|
if (!pastEquals && !nameNode) {
|
|
468
545
|
// First non-syntax child is the name/pattern
|
|
469
546
|
if (child.type === 'identifier' ||
|
|
@@ -482,7 +559,7 @@ function splitVariableDeclarator(node, adapter) {
|
|
|
482
559
|
}
|
|
483
560
|
}
|
|
484
561
|
}
|
|
485
|
-
return { nameNode, valueNode
|
|
562
|
+
return { nameNode, valueNode };
|
|
486
563
|
}
|
|
487
564
|
/**
|
|
488
565
|
* Extract all variable names from a destructuring or identifier pattern.
|
|
@@ -562,8 +639,21 @@ function collectChildren(node, adapter, sourceCode, names) {
|
|
|
562
639
|
* `env.DB.prepare` yields "env.DB", not "env"); a `this`/`super` child returns
|
|
563
640
|
* `thisSuperResult` (null for the receiver walker, 'this' for the root walker);
|
|
564
641
|
* any other child returns null.
|
|
642
|
+
*
|
|
643
|
+
* A `call_expression` child is the fluent/builder-chain case (`db.selectFrom(…)
|
|
644
|
+
* .selectAll().execute()` — the object of `.execute` is a call): descend into
|
|
645
|
+
* the call's *callee* and resolve that, so the root receiver is reached through
|
|
646
|
+
* the chain rather than dropped. Without this, every ORM whose queries are
|
|
647
|
+
* builder chains (kysely, drizzle, knex, prisma fluent API) resolves to `null`
|
|
648
|
+
* at the first call boundary.
|
|
565
649
|
*/
|
|
566
650
|
function resolveReceiverText(firstChild, adapter, sourceCode, thisSuperResult) {
|
|
651
|
+
if (firstChild.type === 'call_expression') {
|
|
652
|
+
const callee = getCallExpressionCallee(firstChild, adapter);
|
|
653
|
+
if (!callee)
|
|
654
|
+
return null;
|
|
655
|
+
return resolveReceiverText(callee, adapter, sourceCode, thisSuperResult);
|
|
656
|
+
}
|
|
567
657
|
if (firstChild.type === 'identifier' ||
|
|
568
658
|
firstChild.type === 'member_expression' ||
|
|
569
659
|
firstChild.type === 'selector_expression') {
|
|
@@ -578,8 +668,13 @@ function resolveReceiverText(firstChild, adapter, sourceCode, thisSuperResult) {
|
|
|
578
668
|
* Extract the "receiver" identifier from a member expression chain.
|
|
579
669
|
* For `db.prepare` → "db"
|
|
580
670
|
* For `this.db.prepare` → "db" (walk to the deepest non-member identifier)
|
|
671
|
+
*
|
|
672
|
+
* @param node the member/selector expression node
|
|
673
|
+
* @param adapter the language adapter used to walk child nodes
|
|
674
|
+
* @param sourceCode the file source text for reading node text
|
|
675
|
+
* @returns the receiver identifier text, or `null`
|
|
581
676
|
*/
|
|
582
|
-
function getMemberExpressionReceiver(node, adapter, sourceCode) {
|
|
677
|
+
export function getMemberExpressionReceiver(node, adapter, sourceCode) {
|
|
583
678
|
// Walk down the member expression chain to find the root object
|
|
584
679
|
let current = node;
|
|
585
680
|
while (current.type === 'member_expression' ||
|
|
@@ -604,6 +699,9 @@ function getMemberExpressionReceiver(node, adapter, sourceCode) {
|
|
|
604
699
|
* guards must agree on: a bare `identifier`, a `member_expression`/`selector`,
|
|
605
700
|
* and — for `await fn<T>(...)` — an `await_expression` wrapping the callee plus
|
|
606
701
|
* a `type_arguments` child (skipped, not mistaken for the callee).
|
|
702
|
+
* @param node The call-expression node whose callee to extract.
|
|
703
|
+
* @param adapter The language adapter for child traversal.
|
|
704
|
+
* @returns The callee node, or null when none is found.
|
|
607
705
|
*/
|
|
608
706
|
export function getCallExpressionCallee(node, adapter) {
|
|
609
707
|
for (const child of adapter.getChildren(node)) {
|
|
@@ -675,104 +773,297 @@ export function buildProvenanceContext(ast, adapter, sourceCode, options) {
|
|
|
675
773
|
const mode = options.mode;
|
|
676
774
|
// 1. Extract seed identifiers from imports
|
|
677
775
|
const dbSeeds = extractDBProvenancedImports(ast, adapter);
|
|
678
|
-
const validatorSeeds =
|
|
679
|
-
|
|
680
|
-
|
|
776
|
+
const validatorSeeds = extractValidatorProvenancedImports(ast, adapter);
|
|
777
|
+
// 1a. Merge cross-file resolution seeds (declaration-based, not name-based).
|
|
778
|
+
// These replace the deleted name-list fallback (Spec 69 §10): the
|
|
779
|
+
// `seedProvenance` map is the per-file DB-provenanced identifier set
|
|
780
|
+
// produced by `computeReceiverProvenance` for this file.
|
|
781
|
+
if (options.seedProvenance) {
|
|
782
|
+
for (const [name, evidence] of options.seedProvenance) {
|
|
783
|
+
if (!dbSeeds.has(name))
|
|
784
|
+
dbSeeds.set(name, evidence);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
681
787
|
// 2. Propagate provenance through assignments
|
|
682
788
|
let dbProvenanced = propagateProvenance(ast, adapter, sourceCode, dbSeeds);
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
//
|
|
687
|
-
//
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
// 4.
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
789
|
+
const validatorProvenanced = propagateProvenance(ast, adapter, sourceCode, validatorSeeds);
|
|
790
|
+
// 2a. DB-shaped activity — receiver roots of DB/ORM call sites, proven or
|
|
791
|
+
// unproven. This is the file-gate signal, not a handle decision: it must
|
|
792
|
+
// run before the R3 dialect gate so a type-annotated handle with a dynamic
|
|
793
|
+
// SQL argument (unproven under criterion 9, no dialect to parse) still
|
|
794
|
+
// passes the gate and reaches the analyzers instead of being dropped.
|
|
795
|
+
const dbActivity = collectDbActivity(ast, adapter, sourceCode, dbProvenanced);
|
|
796
|
+
// 3. R3 — a call whose argument parses as SQL proves its receiver a handle
|
|
797
|
+
// (criterion 8). This is the replacement for the type-name handle tests
|
|
798
|
+
// criterion 9 deleted: `const db: D1Database = getDb()` no longer proves `db`
|
|
799
|
+
// by its annotation, but `db.query('SELECT …')` still does — by the parsed
|
|
800
|
+
// argument, not the name. Runs before wrapper detection so a wrapper body's
|
|
801
|
+
// `db.prepare('SELECT …')` is already visible as a DB call to
|
|
802
|
+
// `detectDbWrappers`. Runs even without a named dialect: `identifyHandle`
|
|
803
|
+
// parses the literal under DEFAULT_SQL_DIALECT when the dialect is null
|
|
804
|
+
// (Spec 70 R2), so a sibling's parseable literal still proves the receiver.
|
|
805
|
+
dbProvenanced = applySqlArgumentInference(ast, adapter, sourceCode, dbProvenanced, options.sqlDialect ?? null);
|
|
806
|
+
// 4. Wrapper detection (structural, not name-based): learn DB-wrapper function
|
|
807
|
+
// names from function bodies — a body that constructs/calls a DB driver.
|
|
808
|
+
// A bare call like `d1(sql)` then resolves as DB-provenanced and its SQL
|
|
809
|
+
// reaches the table rules instead of bypassing them.
|
|
810
|
+
dbProvenanced = detectDbWrappers(ast, adapter, sourceCode, dbProvenanced);
|
|
704
811
|
return {
|
|
705
812
|
dbProvenanced,
|
|
706
813
|
validatorProvenanced,
|
|
707
814
|
mode,
|
|
815
|
+
dbActivity,
|
|
708
816
|
};
|
|
709
817
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
818
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
819
|
+
// R3 — sql-argument handle inference (criterion 8)
|
|
820
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
821
|
+
/**
|
|
822
|
+
* Build the TS receiver-classification env each local fold constructs. The three
|
|
823
|
+
* receiver folds (`collectDbActivity`, `applySqlArgumentInference`,
|
|
824
|
+
* `delegatesToProvenanced`) once each hand-built a near-identical `env` literal;
|
|
825
|
+
* this constructor keeps the classifier's inputs in one place. `interfaceFields`
|
|
826
|
+
* is omitted by the bare-root callers that classify a receiver with no member
|
|
827
|
+
* chain (the B3 arm reads it only for `env.DB`-shaped chains); `delegatesToProvenanced`
|
|
828
|
+
* passes an empty provenance map so the receiver's own declaration — not its
|
|
829
|
+
* propagated provenance — decides its disposition.
|
|
830
|
+
*/
|
|
831
|
+
function buildRootEnv(adapter, sourceCode, provenance, bindings, interfaceFields) {
|
|
832
|
+
return { provenance, bindings, interfaceFields, adapter, sourceCode };
|
|
717
833
|
}
|
|
718
|
-
/**
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
834
|
+
/**
|
|
835
|
+
* Collect the receiver roots of DB-shaped call sites (a member/selector call
|
|
836
|
+
* whose first argument is a static SQL string literal, or whose receiver root
|
|
837
|
+
* resolves handle/unproven), *proven or unproven*. This is the file-gate signal
|
|
838
|
+
* — see `ProvenanceContext.dbActivity` — not a handle decision (that is
|
|
839
|
+
* `identifyHandle`'s job alone, criterion 2).
|
|
840
|
+
*
|
|
841
|
+
* The discriminator is the package discriminant — the receiver root's
|
|
842
|
+
* disposition — not the method name: `join` is `Array.prototype.join` and also
|
|
843
|
+
* `SQL JOIN`. A static SQL literal proves DB shape outright; a dynamic SQL
|
|
844
|
+
* argument (`db.exec('DROP ' + x)`, `db.prepare(sql)`) is admitted by the
|
|
845
|
+
* receiver's resolution (`db` → handle/unproven) while `array.join(',')` is
|
|
846
|
+
* dropped because `array` resolves to a JS global (not-handle). Unlike R3
|
|
847
|
+
* (`applySqlArgumentInference`) it needs no dialect. Runs unconditionally,
|
|
848
|
+
* before R3's dialect gate.
|
|
849
|
+
* @param ast - The file's parsed AST.
|
|
850
|
+
* @param adapter - The language adapter (used for the callee walk and Go skip).
|
|
851
|
+
* @param sourceCode - The file's source text (for node-text reads).
|
|
852
|
+
* @param dbProvenanced - the propagated DB-provenanced map the receiver
|
|
853
|
+
* discriminant reads as its provenance seed.
|
|
854
|
+
* @returns The set of DB-shaped receiver roots, or an empty set for Go.
|
|
855
|
+
*/
|
|
856
|
+
export function collectDbActivity(ast, adapter, sourceCode, dbProvenanced = new Map()) {
|
|
857
|
+
const activity = new Set();
|
|
858
|
+
// Go resolves cross-file; a member-receiver root has no local binding to key
|
|
859
|
+
// the gate on, and Go files are gated by their own import resolution instead.
|
|
860
|
+
if (adapter.name === 'go')
|
|
861
|
+
return activity;
|
|
862
|
+
const env = buildRootEnv(adapter, sourceCode, dbProvenanced, buildBindingEnv(ast, adapter, sourceCode), extractInterfaceFields(ast, adapter, sourceCode));
|
|
863
|
+
walkAST(ast.root, (node) => {
|
|
864
|
+
if (node.type !== 'call_expression')
|
|
865
|
+
return;
|
|
866
|
+
const callee = getCallExpressionCallee(node, adapter);
|
|
867
|
+
if (!callee)
|
|
868
|
+
return;
|
|
869
|
+
if (callee.type !== 'member_expression' && callee.type !== 'selector_expression')
|
|
870
|
+
return;
|
|
871
|
+
const root = resolveReceiverRoot(callee, adapter, sourceCode);
|
|
872
|
+
if (root === null)
|
|
873
|
+
return;
|
|
874
|
+
// A static SQL literal proves DB shape regardless of the receiver's package
|
|
875
|
+
// (`db.query('SELECT …')`). A dynamic SQL argument is admitted by the
|
|
876
|
+
// package discriminant: the receiver's disposition — not the method name —
|
|
877
|
+
// distinguishes `db.prepare(sql)` (handle/unproven) from `array.join(',')`
|
|
878
|
+
// (root `Array` → not-handle).
|
|
879
|
+
if (extractStaticSqlArgument(node, adapter, sourceCode) !== null) {
|
|
880
|
+
activity.add(root);
|
|
881
|
+
return;
|
|
730
882
|
}
|
|
731
|
-
|
|
883
|
+
if (classifyRootIdentifier(root, env) !== 'not-handle')
|
|
884
|
+
activity.add(root);
|
|
885
|
+
});
|
|
886
|
+
return activity;
|
|
732
887
|
}
|
|
733
|
-
/**
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
888
|
+
/**
|
|
889
|
+
* R3: a call whose argument parses as SQL proves its receiver a DB handle.
|
|
890
|
+
* Folds the one entry point `identifyHandle` (criterion 2: no second decision
|
|
891
|
+
* path) over DB-shaped call sites and adds every receiver it proves a handle to
|
|
892
|
+
* `dbProvenanced`. This is the replacement for the type-name handle tests
|
|
893
|
+
* criterion 9 deleted: a receiver typed `D1Database` / `MockDB` is no longer
|
|
894
|
+
* proven by its annotation, but `db.query('SELECT …')` still is — by the parsed
|
|
895
|
+
* argument, not the name.
|
|
896
|
+
*
|
|
897
|
+
* The declaration-resolution arm of `identifyHandle` re-runs here, but it is
|
|
898
|
+
* harmless and already the same evidence `propagateProvenance` produced: for a
|
|
899
|
+
* type-annotated receiver it now returns `unproven` (criterion 9), and
|
|
900
|
+
* `combineVerdicts`' `handle > not-handle > unproven` precedence lets the
|
|
901
|
+
* sql-argument proof win. A receiver already in the map is left untouched.
|
|
902
|
+
*
|
|
903
|
+
* @param ast - the parsed file AST whose call sites R3 folds over.
|
|
904
|
+
* @param adapter - the language adapter (name, extraction helpers) for the file.
|
|
905
|
+
* @param sourceCode - the raw file source, for callee/method text extraction.
|
|
906
|
+
* @param dbProvenanced - the running receiver→evidence map to extend in place.
|
|
907
|
+
* @param sqlDialect - the corpus's named SQL dialect (or null), threaded to the SQL parser.
|
|
908
|
+
* @returns the same `dbProvenanced` map, now also carrying every receiver R3 proved.
|
|
909
|
+
*/
|
|
910
|
+
export function applySqlArgumentInference(ast, adapter, sourceCode, dbProvenanced, sqlDialect) {
|
|
911
|
+
if (adapter.name === 'go')
|
|
912
|
+
return dbProvenanced; // Go resolves cross-file; no TS env here.
|
|
913
|
+
const env = buildRootEnv(adapter, sourceCode, dbProvenanced, buildBindingEnv(ast, adapter, sourceCode), extractInterfaceFields(ast, adapter, sourceCode));
|
|
914
|
+
walkAST(ast.root, (node) => {
|
|
915
|
+
if (node.type !== 'call_expression')
|
|
916
|
+
return;
|
|
917
|
+
const callee = getCallExpressionCallee(node, adapter);
|
|
918
|
+
if (!callee)
|
|
919
|
+
return;
|
|
920
|
+
// Only member-expression calls carry the receiver whose handle-ness R3
|
|
921
|
+
// proves. A bare-identifier call (`query('…')`) has no receiver to add.
|
|
922
|
+
if (callee.type !== 'member_expression' && callee.type !== 'selector_expression')
|
|
923
|
+
return;
|
|
924
|
+
const method = extractMemberExpressionProperty(callee, adapter, sourceCode);
|
|
925
|
+
if (!method)
|
|
926
|
+
return;
|
|
927
|
+
// The SQL argument — not the method name — is the admission proof. R3 folds
|
|
928
|
+
// `identifyHandle` over every member call; a null static literal simply can't
|
|
929
|
+
// prove a handle, so it falls through. No method-name set is consulted.
|
|
930
|
+
const root = resolveReceiverRoot(callee, adapter, sourceCode);
|
|
931
|
+
if (root === null)
|
|
932
|
+
return;
|
|
933
|
+
const receiver = getMemberExpressionReceiver(callee, adapter, sourceCode) ?? root;
|
|
934
|
+
const sqlArgument = extractStaticSqlArgument(node, adapter, sourceCode);
|
|
935
|
+
if (sqlArgument === null)
|
|
936
|
+
return;
|
|
937
|
+
const thisField = receiverRootIsThis(callee, adapter);
|
|
938
|
+
const thisHeritage = thisField
|
|
939
|
+
? findEnclosingClassHeritage(ast, adapter, node, sourceCode)
|
|
940
|
+
: null;
|
|
941
|
+
const verdict = identifyHandle({
|
|
942
|
+
format: 'typescript',
|
|
943
|
+
root,
|
|
944
|
+
receiver,
|
|
945
|
+
method,
|
|
946
|
+
sqlArgument,
|
|
947
|
+
thisField,
|
|
948
|
+
thisHeritage,
|
|
949
|
+
}, {
|
|
950
|
+
imports: new Map(),
|
|
951
|
+
typeAnnotations: new Map(),
|
|
952
|
+
bindings: new Map(),
|
|
953
|
+
withinFileProvenance: new Map(),
|
|
954
|
+
sqlDialect,
|
|
955
|
+
resolution: { dialect: 'ts', env },
|
|
956
|
+
});
|
|
957
|
+
if (verdict.kind === 'handle' && !dbProvenanced.has(root)) {
|
|
958
|
+
dbProvenanced.set(root, {
|
|
959
|
+
identifier: root,
|
|
960
|
+
reason: 'sql-argument',
|
|
961
|
+
source: 'SQL argument parses as a statement (Spec 70 R3)',
|
|
753
962
|
chain: [],
|
|
754
963
|
});
|
|
755
964
|
}
|
|
756
|
-
}
|
|
965
|
+
});
|
|
966
|
+
return dbProvenanced;
|
|
757
967
|
}
|
|
758
968
|
/**
|
|
759
|
-
*
|
|
969
|
+
* Extract the R3 sites of a TS-family file — the member calls whose static SQL
|
|
970
|
+
* argument proves their receiver a handle (Spec 70 criterion 8). This is the
|
|
971
|
+
* provenance-free extraction half of {@link applySqlArgumentInference}: the same
|
|
972
|
+
* candidate filter (DB/ORM shape) and the same static-argument test, but it
|
|
973
|
+
* records the site instead of folding `identifyHandle`. The corpus-side
|
|
974
|
+
* `applyR3FromSites` mirror re-runs the verdict once the seeded provenance and
|
|
975
|
+
* bindings are known.
|
|
976
|
+
* @param ast - The file's parsed AST.
|
|
977
|
+
* @param adapter - The language adapter (used for the callee walk and Go skip).
|
|
978
|
+
* @param sourceCode - The file's source text (for static-argument reads).
|
|
979
|
+
* @returns The recorded R3 sites, or `[]` for Go.
|
|
760
980
|
*/
|
|
761
|
-
function
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
981
|
+
export function extractR3Sites(ast, adapter, sourceCode) {
|
|
982
|
+
if (adapter.name === 'go')
|
|
983
|
+
return []; // Go resolves cross-file; no TS env here.
|
|
984
|
+
const sites = [];
|
|
985
|
+
walkAST(ast.root, (node) => {
|
|
986
|
+
if (node.type !== 'call_expression')
|
|
987
|
+
return;
|
|
988
|
+
const callee = getCallExpressionCallee(node, adapter);
|
|
989
|
+
if (!callee)
|
|
990
|
+
return;
|
|
991
|
+
// Only member-expression calls carry the receiver whose handle-ness R3
|
|
992
|
+
// proves. A bare-identifier call (`query('…')`) has no receiver to add.
|
|
993
|
+
if (callee.type !== 'member_expression' && callee.type !== 'selector_expression')
|
|
994
|
+
return;
|
|
995
|
+
const method = extractMemberExpressionProperty(callee, adapter, sourceCode);
|
|
996
|
+
if (!method)
|
|
997
|
+
return;
|
|
998
|
+
// The SQL argument — not the method name — is the admission proof. R3 folds
|
|
999
|
+
// `identifyHandle` over every member call; a null static literal simply can't
|
|
1000
|
+
// prove a handle, so it falls through. No method-name set is consulted.
|
|
1001
|
+
const root = resolveReceiverRoot(callee, adapter, sourceCode);
|
|
1002
|
+
if (root === null)
|
|
1003
|
+
return;
|
|
1004
|
+
const receiver = getMemberExpressionReceiver(callee, adapter, sourceCode) ?? root;
|
|
1005
|
+
const sqlArgument = extractStaticSqlArgument(node, adapter, sourceCode);
|
|
1006
|
+
if (sqlArgument === null)
|
|
1007
|
+
return;
|
|
1008
|
+
const thisField = receiverRootIsThis(callee, adapter);
|
|
1009
|
+
const thisHeritage = thisField
|
|
1010
|
+
? findEnclosingClassHeritage(ast, adapter, node, sourceCode)
|
|
1011
|
+
: null;
|
|
1012
|
+
sites.push({
|
|
1013
|
+
root,
|
|
1014
|
+
receiver,
|
|
1015
|
+
method,
|
|
1016
|
+
sqlArgument,
|
|
1017
|
+
thisField,
|
|
1018
|
+
thisHeritage,
|
|
1019
|
+
});
|
|
766
1020
|
});
|
|
1021
|
+
return sites;
|
|
767
1022
|
}
|
|
768
|
-
/**
|
|
769
|
-
function
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
1023
|
+
/** True when a member/selector chain bottoms out at `this`/`super`. */
|
|
1024
|
+
function receiverRootIsThis(callee, adapter) {
|
|
1025
|
+
let current = callee;
|
|
1026
|
+
while (current.type === 'member_expression' || current.type === 'selector_expression') {
|
|
1027
|
+
const children = adapter.getChildren(current);
|
|
1028
|
+
const object = children.find((c) => c.type !== '.' && c.type !== 'property_identifier' && c.type !== 'field_identifier');
|
|
1029
|
+
if (!object)
|
|
1030
|
+
return false;
|
|
1031
|
+
current = object;
|
|
1032
|
+
}
|
|
1033
|
+
return current.type === 'this' || current.type === 'super';
|
|
773
1034
|
}
|
|
774
|
-
|
|
775
|
-
|
|
1035
|
+
/**
|
|
1036
|
+
* The statically-present SQL text of a call's first argument (string/template
|
|
1037
|
+
* literal), unquoted — or null when the first argument is not a literal or the
|
|
1038
|
+
* template carries a `${…}` substitution (dynamic). Mirrors the literal-argument
|
|
1039
|
+
* slice of the data-access analyzer's `extractStaticSql`; the tagged-template
|
|
1040
|
+
* and variable-assignment cases are not R3's concern here because a member
|
|
1041
|
+
* receiver already names the handle.
|
|
1042
|
+
*/
|
|
1043
|
+
function extractStaticSqlArgument(node, adapter, sourceCode) {
|
|
1044
|
+
const children = adapter.getChildren(node);
|
|
1045
|
+
const args = children.find((c) => {
|
|
1046
|
+
const t = adapter.getNodeType(c);
|
|
1047
|
+
return t === 'arguments' || t === 'argument_list';
|
|
1048
|
+
});
|
|
1049
|
+
if (!args)
|
|
1050
|
+
return null;
|
|
1051
|
+
for (const arg of adapter.getChildren(args)) {
|
|
1052
|
+
const t = adapter.getNodeType(arg);
|
|
1053
|
+
if (t === '(' || t === ')' || t === ',')
|
|
1054
|
+
continue;
|
|
1055
|
+
if (t === 'string' || t === 'template_string') {
|
|
1056
|
+
if (t === 'template_string') {
|
|
1057
|
+
const kids = adapter.getChildren(arg) ?? [];
|
|
1058
|
+
if (kids.some((c) => adapter.getNodeType(c) === 'template_substitution'))
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
const raw = adapter.getNodeText(arg, sourceCode) ?? '';
|
|
1062
|
+
return stripSqlQuotes(raw);
|
|
1063
|
+
}
|
|
1064
|
+
return null; // first non-trivia argument is not a literal → cannot parse.
|
|
1065
|
+
}
|
|
1066
|
+
return null;
|
|
776
1067
|
}
|
|
777
1068
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
778
1069
|
// Wrapper detection — learn DB-wrapper function names from function bodies
|
|
@@ -784,62 +1075,211 @@ const FUNCTION_NODE_TYPES = new Set([
|
|
|
784
1075
|
'arrow_function',
|
|
785
1076
|
'method_definition',
|
|
786
1077
|
'generator_function_declaration',
|
|
787
|
-
'
|
|
1078
|
+
'generator_function',
|
|
1079
|
+
]);
|
|
1080
|
+
/** Learn DB-wrapper names from nodes of `spec.nodeTypes` whose bodies wrap a DB
|
|
1081
|
+
* operation, merging each learned name into `dbProvenanced`. */
|
|
1082
|
+
function learnWrapperNames(ast, adapter, sourceCode, dbProvenanced, spec, bindings) {
|
|
1083
|
+
const nodes = adapter.findNodes(ast, {
|
|
1084
|
+
custom: (node) => spec.nodeTypes.has(node.type),
|
|
1085
|
+
});
|
|
1086
|
+
for (const node of nodes) {
|
|
1087
|
+
const name = adapter.getNodeName(node);
|
|
1088
|
+
if (!name)
|
|
1089
|
+
continue;
|
|
1090
|
+
if (dbProvenanced.has(name))
|
|
1091
|
+
continue;
|
|
1092
|
+
if (!spec.isWrapper(node, adapter, sourceCode, dbProvenanced, bindings))
|
|
1093
|
+
continue;
|
|
1094
|
+
dbProvenanced.set(name, {
|
|
1095
|
+
identifier: name,
|
|
1096
|
+
reason: 'wrapper',
|
|
1097
|
+
source: spec.source,
|
|
1098
|
+
chain: [],
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
return dbProvenanced;
|
|
1102
|
+
}
|
|
1103
|
+
/** Class-node types that may wrap a DB handle (Spec 69 §10 — cross-file anchor). */
|
|
1104
|
+
const WRAPPER_CLASS_TYPES = new Set([
|
|
1105
|
+
'class_declaration',
|
|
1106
|
+
'abstract_class_declaration',
|
|
1107
|
+
'class',
|
|
788
1108
|
]);
|
|
789
1109
|
/**
|
|
790
|
-
* Learn DB-wrapper
|
|
791
|
-
* provenance map.
|
|
1110
|
+
* Learn DB-wrapper names and merge them into the provenance map.
|
|
792
1111
|
*
|
|
793
|
-
* A wrapper is a named function whose own body performs a DB operation —
|
|
794
|
-
* a `fetch` to the Cloudflare D1 HTTP query API, or a call that delegates
|
|
795
|
-
* already DB-provenanced identifier/receiver. Adding the wrapper's name to
|
|
1112
|
+
* A wrapper is a named function/class whose own body performs a DB operation —
|
|
1113
|
+
* either a `fetch` to the Cloudflare D1 HTTP query API, or a call that delegates
|
|
1114
|
+
* to an already DB-provenanced identifier/receiver. Adding the wrapper's name to
|
|
796
1115
|
* `dbProvenanced` lets `isDBProvenanced` treat a bare call like `d1(sql)` as a
|
|
797
1116
|
* DB call, so its SQL argument reaches the table rules (unknown-table /
|
|
798
1117
|
* stale-table-reference) and the data-access rules instead of bypassing them.
|
|
799
1118
|
*
|
|
800
|
-
*
|
|
801
|
-
*
|
|
802
|
-
*
|
|
1119
|
+
* The class form (`options.classes`) is the object-form of the function form: its
|
|
1120
|
+
* body (fields + methods + constructor) constructs or calls a DB driver, so the
|
|
1121
|
+
* class *is* a DB handle — e.g. `export class Database { constructor() {
|
|
1122
|
+
* this.sql = neon(url) } query(t) { return this.sql(t) } }`. Class bodies are
|
|
1123
|
+
* scanned *through* method bodies (the driver call lives in the constructor) but
|
|
1124
|
+
* never into nested classes or nested function declarations — an inner named
|
|
1125
|
+
* function's DB op is not mis-attributed to the outer class.
|
|
1126
|
+
*
|
|
1127
|
+
* This is structural evidence, not name matching, so it is only run in hybrid
|
|
1128
|
+
* mode (alongside the name fallbacks), never strict `provenance`/`names` modes.
|
|
803
1129
|
*
|
|
804
|
-
* @param ast
|
|
805
|
-
* @param adapter
|
|
806
|
-
* @param sourceCode
|
|
807
|
-
* @param dbProvenanced
|
|
808
|
-
* @
|
|
1130
|
+
* @param ast the parsed file AST
|
|
1131
|
+
* @param adapter the language adapter used to walk nodes
|
|
1132
|
+
* @param sourceCode the file source text for reading node text
|
|
1133
|
+
* @param dbProvenanced the provenance map to merge learned wrapper names into
|
|
1134
|
+
* @param options when `classes` is true, also scan wrapper classes
|
|
1135
|
+
* @returns the provenance map with wrapper names added
|
|
809
1136
|
*/
|
|
810
|
-
function
|
|
1137
|
+
export function detectDbWrappers(ast, adapter, sourceCode, dbProvenanced, options) {
|
|
1138
|
+
const bindings = buildBindingEnv(ast, adapter, sourceCode);
|
|
1139
|
+
learnWrapperNames(ast, adapter, sourceCode, dbProvenanced, {
|
|
1140
|
+
nodeTypes: FUNCTION_NODE_TYPES,
|
|
1141
|
+
isWrapper: isDbWrapperBody,
|
|
1142
|
+
source: 'function body performs a DB operation',
|
|
1143
|
+
}, bindings);
|
|
1144
|
+
if (options?.classes) {
|
|
1145
|
+
learnWrapperNames(ast, adapter, sourceCode, dbProvenanced, {
|
|
1146
|
+
nodeTypes: WRAPPER_CLASS_TYPES,
|
|
1147
|
+
isWrapper: isDbWrapperClass,
|
|
1148
|
+
source: 'class body constructs or calls a DB driver',
|
|
1149
|
+
}, bindings);
|
|
1150
|
+
}
|
|
1151
|
+
return dbProvenanced;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Form-5 (Spec 69 §10 S5f) — function return resolution.
|
|
1155
|
+
*
|
|
1156
|
+
* A call to an in-repo function resolves to its *returned construction*,
|
|
1157
|
+
* transitively: `export function getDB() { return new Database() }` proves
|
|
1158
|
+
* `getDB` is a DB handle because the returned expression is already provenanced —
|
|
1159
|
+
* the identifier's name is irrelevant. It is the binding form the two
|
|
1160
|
+
* `cli-integration` fixtures need: `const db = getDB()` in an importing file
|
|
1161
|
+
* resolves through the export fixed point (a provenanced `getDB` export is seeded
|
|
1162
|
+
* in importers) and then propagates via the existing rule 2 (call to a provenanced
|
|
1163
|
+
* identifier).
|
|
1164
|
+
*
|
|
1165
|
+
* This deliberately does NOT learn to accept mocks: a `getDB(): MockDB` with no
|
|
1166
|
+
* provenanced client anywhere in its chain is honestly not a handle, and stays
|
|
1167
|
+
* unprovenanced.
|
|
1168
|
+
*
|
|
1169
|
+
* @param ast the parsed file AST
|
|
1170
|
+
* @param adapter the language adapter used to find function nodes
|
|
1171
|
+
* @param sourceCode the file source text for reading return expressions
|
|
1172
|
+
* @param dbProvenanced the provenance map to merge learned names into
|
|
1173
|
+
* @returns the provenance map with DB-returning function names added
|
|
1174
|
+
*/
|
|
1175
|
+
export function detectDbReturningFunctions(ast, adapter, sourceCode, dbProvenanced) {
|
|
811
1176
|
const functionNodes = adapter.findNodes(ast, {
|
|
812
1177
|
custom: (node) => FUNCTION_NODE_TYPES.has(node.type),
|
|
813
1178
|
});
|
|
814
1179
|
for (const fn of functionNodes) {
|
|
815
1180
|
const name = adapter.getNodeName(fn);
|
|
816
|
-
if (!name)
|
|
817
|
-
continue;
|
|
818
|
-
if (dbProvenanced.has(name))
|
|
819
|
-
continue;
|
|
820
|
-
if (!isDbWrapperBody(fn, adapter, sourceCode, dbProvenanced))
|
|
1181
|
+
if (!name || dbProvenanced.has(name))
|
|
821
1182
|
continue;
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1183
|
+
// A `return` whose expression is an already-provenanced construction
|
|
1184
|
+
// (`return new Database(...)`, `return db`, `return drizzle(...)`).
|
|
1185
|
+
if (functionReturnsProvenanced(fn, adapter, sourceCode, dbProvenanced)) {
|
|
1186
|
+
dbProvenanced.set(name, {
|
|
1187
|
+
identifier: name,
|
|
1188
|
+
reason: 'propagation',
|
|
1189
|
+
source: 'returns a DB-provenanced construction',
|
|
1190
|
+
chain: [],
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
828
1193
|
}
|
|
829
1194
|
return dbProvenanced;
|
|
830
1195
|
}
|
|
1196
|
+
/**
|
|
1197
|
+
* True when any `return` statement in a function body returns an expression that
|
|
1198
|
+
* is itself a DB-provenanced construction — a provenanced identifier, a
|
|
1199
|
+
* `new`/call whose constructor/callee is provenanced, or a member expression on a
|
|
1200
|
+
* provenanced receiver.
|
|
1201
|
+
*/
|
|
1202
|
+
function functionReturnsProvenanced(fn, adapter, sourceCode, dbProvenanced) {
|
|
1203
|
+
let found = false;
|
|
1204
|
+
walkAST(fn, (node) => {
|
|
1205
|
+
if (found || node.type !== 'return_statement')
|
|
1206
|
+
return;
|
|
1207
|
+
const arg = adapter.getChildren(node).find((c) => c.type !== 'return');
|
|
1208
|
+
if (!arg)
|
|
1209
|
+
return;
|
|
1210
|
+
if (tryPropagateFromExpression(arg, adapter, sourceCode, dbProvenanced)) {
|
|
1211
|
+
found = true;
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
return found;
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Decide whether a class's own body performs a DB operation: a D1 REST fetch, a
|
|
1218
|
+
* call delegating to an already DB-provenanced identifier/receiver, or a
|
|
1219
|
+
* `new`/call to a DB-provenanced constructor/function (the driver a wrapper
|
|
1220
|
+
* class *owns*). Method bodies are included (the constructor is where the driver
|
|
1221
|
+
* call lives); nested classes and nested function declarations are excluded.
|
|
1222
|
+
*/
|
|
1223
|
+
function isDbWrapperClass(cls, adapter, sourceCode, dbProvenanced, bindings) {
|
|
1224
|
+
for (const expr of collectClassCallExpressions(cls, adapter)) {
|
|
1225
|
+
if (expr.type === 'new_expression') {
|
|
1226
|
+
const ctor = findChildOfType(expr, ['identifier', 'member_expression']);
|
|
1227
|
+
if (ctor) {
|
|
1228
|
+
const ctorName = extractIdentifierName(ctor, adapter, sourceCode);
|
|
1229
|
+
if (ctorName && dbProvenanced.has(ctorName))
|
|
1230
|
+
return true;
|
|
1231
|
+
}
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
if (isD1RestCall(expr, adapter, sourceCode))
|
|
1235
|
+
return true;
|
|
1236
|
+
if (delegatesToProvenanced(expr, adapter, sourceCode, dbProvenanced, bindings))
|
|
1237
|
+
return true;
|
|
1238
|
+
}
|
|
1239
|
+
return false;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Collect `call_expression`/`new_expression` nodes within a class body, descending
|
|
1243
|
+
* through method bodies but never into nested classes or nested function
|
|
1244
|
+
* declarations (so an inner function's DB op doesn't make the class a wrapper).
|
|
1245
|
+
*/
|
|
1246
|
+
function collectClassCallExpressions(cls, adapter) {
|
|
1247
|
+
const expressions = [];
|
|
1248
|
+
const NESTED_SKIP = new Set([
|
|
1249
|
+
'class_declaration',
|
|
1250
|
+
'abstract_class_declaration',
|
|
1251
|
+
'class',
|
|
1252
|
+
'function_declaration',
|
|
1253
|
+
'function_expression',
|
|
1254
|
+
'arrow_function',
|
|
1255
|
+
'generator_function_declaration',
|
|
1256
|
+
'generator_function',
|
|
1257
|
+
]);
|
|
1258
|
+
const walk = (node) => {
|
|
1259
|
+
for (const child of adapter.getChildren(node)) {
|
|
1260
|
+
if (NESTED_SKIP.has(child.type))
|
|
1261
|
+
continue;
|
|
1262
|
+
if (child.type === 'call_expression' || child.type === 'new_expression') {
|
|
1263
|
+
expressions.push(child);
|
|
1264
|
+
}
|
|
1265
|
+
walk(child);
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
walk(cls);
|
|
1269
|
+
return expressions;
|
|
1270
|
+
}
|
|
831
1271
|
/**
|
|
832
1272
|
* Decide whether a function's own body performs a DB operation: a D1 REST fetch,
|
|
833
1273
|
* or a call delegating to an already DB-provenanced receiver/identifier.
|
|
834
1274
|
* Nested function bodies are excluded so an inner function's DB op is not
|
|
835
1275
|
* mis-attributed to an outer wrapper.
|
|
836
1276
|
*/
|
|
837
|
-
function isDbWrapperBody(fn, adapter, sourceCode, dbProvenanced) {
|
|
1277
|
+
function isDbWrapperBody(fn, adapter, sourceCode, dbProvenanced, bindings) {
|
|
838
1278
|
const calls = collectOwnCallExpressions(fn, adapter);
|
|
839
1279
|
for (const call of calls) {
|
|
840
1280
|
if (isD1RestCall(call, adapter, sourceCode))
|
|
841
1281
|
return true;
|
|
842
|
-
if (delegatesToProvenanced(call, adapter, sourceCode, dbProvenanced))
|
|
1282
|
+
if (delegatesToProvenanced(call, adapter, sourceCode, dbProvenanced, bindings))
|
|
843
1283
|
return true;
|
|
844
1284
|
}
|
|
845
1285
|
return false;
|
|
@@ -867,22 +1307,56 @@ function collectOwnCallExpressions(fn, adapter) {
|
|
|
867
1307
|
* True when `call` is a `fetch(...)` to the Cloudflare D1 HTTP query API
|
|
868
1308
|
* (`…/d1/database/<id>/query`). That endpoint is specific enough that a match
|
|
869
1309
|
* is conclusive evidence the surrounding function is a D1 wrapper.
|
|
1310
|
+
*
|
|
1311
|
+
* @param call the candidate call-expression node
|
|
1312
|
+
* @param adapter the language adapter used to read the callee and arguments
|
|
1313
|
+
* @param sourceCode the file source text for reading node text
|
|
1314
|
+
* @returns true when the call names `fetch` with a literal `/d1/database/` URL
|
|
870
1315
|
*/
|
|
871
|
-
function isD1RestCall(call, adapter, sourceCode) {
|
|
1316
|
+
export function isD1RestCall(call, adapter, sourceCode) {
|
|
1317
|
+
if (adapter.getNodeType(call) !== 'call_expression')
|
|
1318
|
+
return false;
|
|
872
1319
|
const callee = getCallExpressionCallee(call, adapter);
|
|
873
1320
|
if (!callee)
|
|
874
1321
|
return false;
|
|
875
|
-
|
|
876
|
-
|
|
1322
|
+
// The callee must *name* fetch: a bare `fetch(…)` identifier, or a member
|
|
1323
|
+
// `….fetch(…)` whose property node is `fetch`. Reading the property node (not
|
|
1324
|
+
// the callee's whole text) keeps a string inside a subscript/argument from
|
|
1325
|
+
// being read as the method name.
|
|
1326
|
+
const calleeType = adapter.getNodeType(callee);
|
|
1327
|
+
if (calleeType === 'identifier') {
|
|
1328
|
+
if (adapter.getNodeText(callee, sourceCode) !== 'fetch')
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
else if (calleeType === 'member_expression' || calleeType === 'selector_expression') {
|
|
1332
|
+
if (extractMemberExpressionProperty(callee, adapter, sourceCode) !== 'fetch')
|
|
1333
|
+
return false;
|
|
1334
|
+
}
|
|
1335
|
+
else {
|
|
877
1336
|
return false;
|
|
878
|
-
|
|
879
|
-
|
|
1337
|
+
}
|
|
1338
|
+
// The URL is the first string/template-literal argument. A non-literal first
|
|
1339
|
+
// argument (`fetch(makeUrl(…))`) abstains — the value fetch receives is the
|
|
1340
|
+
// *result* of that call, not a literal, so there is no URL to read.
|
|
1341
|
+
const args = adapter.getChildren(call).find((c) => adapter.getNodeType(c) === 'arguments');
|
|
1342
|
+
if (!args)
|
|
1343
|
+
return false;
|
|
1344
|
+
for (const arg of adapter.getChildren(args)) {
|
|
1345
|
+
const t = adapter.getNodeType(arg);
|
|
1346
|
+
if (t === '(' || t === ')' || t === ',')
|
|
1347
|
+
continue;
|
|
1348
|
+
if (t === 'string' || t === 'template_string') {
|
|
1349
|
+
return (adapter.getNodeText(arg, sourceCode) ?? '').includes('/d1/database/');
|
|
1350
|
+
}
|
|
1351
|
+
break; // first argument is not a literal — abstain
|
|
1352
|
+
}
|
|
1353
|
+
return false;
|
|
880
1354
|
}
|
|
881
1355
|
/**
|
|
882
1356
|
* True when `call` delegates to an identifier or member-receiver that is already
|
|
883
1357
|
* DB-provenanced (e.g. `db.prepare(...)` inside a `d1Query(sql)` wrapper).
|
|
884
1358
|
*/
|
|
885
|
-
function delegatesToProvenanced(call, adapter, sourceCode, dbProvenanced) {
|
|
1359
|
+
function delegatesToProvenanced(call, adapter, sourceCode, dbProvenanced, bindings) {
|
|
886
1360
|
const callee = getCallExpressionCallee(call, adapter);
|
|
887
1361
|
if (!callee)
|
|
888
1362
|
return false;
|
|
@@ -894,159 +1368,84 @@ function delegatesToProvenanced(call, adapter, sourceCode, dbProvenanced) {
|
|
|
894
1368
|
const receiver = getMemberExpressionReceiver(callee, adapter, sourceCode);
|
|
895
1369
|
if (!receiver)
|
|
896
1370
|
return false;
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1371
|
+
const receiverProvenanced = dbProvenanced.has(receiver) ||
|
|
1372
|
+
// Compound receivers ("env.DB", "db.users") — match any dotted segment.
|
|
1373
|
+
receiver.split('.').some((part) => dbProvenanced.has(part));
|
|
1374
|
+
if (!receiverProvenanced)
|
|
1375
|
+
return false;
|
|
1376
|
+
// The receiver root's declaration — not the method name — decides whether
|
|
1377
|
+
// this is a DB operation (`db.prepare(…)`) or an in-memory mutation on a
|
|
1378
|
+
// provenanced *result* (`provenancedMap.set(k, v)`, `rows.map(…)`):
|
|
1379
|
+
// `join` is both `Array.prototype.join` and `SQL JOIN`, so a method name
|
|
1380
|
+
// cannot discriminate. Provenance is deliberately left out of the env so the
|
|
1381
|
+
// receiver's own declaration (an array/Map/Set/JS-global/primitive →
|
|
1382
|
+
// not-handle) is consulted rather than its propagated provenance.
|
|
1383
|
+
const root = resolveReceiverRoot(callee, adapter, sourceCode);
|
|
1384
|
+
if (root === null)
|
|
1385
|
+
return false;
|
|
1386
|
+
const env = buildRootEnv(adapter, sourceCode, new Map(), bindings);
|
|
1387
|
+
return classifyRootIdentifier(root, env) !== 'not-handle';
|
|
901
1388
|
}
|
|
902
1389
|
return false;
|
|
903
1390
|
}
|
|
1391
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1392
|
+
// Per-call-site dialect (Spec 70)
|
|
1393
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1394
|
+
/** The SQL dialect a DB-driver package's evidence names, or null when the
|
|
1395
|
+
* evidence carries no package or the package names no single dialect. */
|
|
1396
|
+
function dialectForEvidence(evidence) {
|
|
1397
|
+
if (!evidence?.packageName)
|
|
1398
|
+
return null;
|
|
1399
|
+
return dialectForPackage(evidence.packageName);
|
|
1400
|
+
}
|
|
904
1401
|
/**
|
|
905
|
-
*
|
|
1402
|
+
* Resolve the SQL dialect a call site should parse its SQL argument under, from
|
|
1403
|
+
* the package its receiver traces to (Spec 70 — per-site dialect). The dialect
|
|
1404
|
+
* is a property of the *located fact*, not the repo: `pool.query(…)` where
|
|
1405
|
+
* `pool` resolves to `pg` is postgres, `conn.query(…)` where `conn` resolves to
|
|
1406
|
+
* `mysql2` is mysql. A repo with two drivers is therefore not "ambiguous" for a
|
|
1407
|
+
* site whose receiver resolves to one of them — that site names its dialect and
|
|
1408
|
+
* parses honestly.
|
|
906
1409
|
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
1410
|
+
* Returns null when the receiver carries no package (a wrapper, a type
|
|
1411
|
+
* annotation, an in-repo module, a cross-dialect ORM like `knex`) or when that
|
|
1412
|
+
* package names no single dialect — the caller then falls back to repo-level
|
|
1413
|
+
* detection (`detectDialect`), and abstains only when that is also null.
|
|
909
1414
|
*
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
*
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
* @param node the call-expression node whose callee is under test
|
|
917
|
-
* @param query bundled adapter, source, provenance context, and DB methods
|
|
918
|
-
* @returns true if the call's callee resolves to a DB-provenanced target
|
|
1415
|
+
* @param node The call-expression node whose callee is under test.
|
|
1416
|
+
* @param adapter The language adapter for child traversal.
|
|
1417
|
+
* @param sourceCode The file source text (for reading node text).
|
|
1418
|
+
* @param provenanceContext The resolved provenance context for this file.
|
|
1419
|
+
* @returns The receiver's dialect, or null when the receiver doesn't resolve to one.
|
|
919
1420
|
*/
|
|
920
|
-
export function
|
|
921
|
-
if (
|
|
922
|
-
return
|
|
923
|
-
const { adapter, sourceCode, context, methods } = query;
|
|
1421
|
+
export function resolveSiteDialect(node, adapter, sourceCode, provenanceContext) {
|
|
1422
|
+
if (!provenanceContext)
|
|
1423
|
+
return null;
|
|
924
1424
|
const calleeNode = getCallExpressionCallee(node, adapter);
|
|
925
1425
|
if (!calleeNode)
|
|
926
|
-
return
|
|
927
|
-
//
|
|
1426
|
+
return null;
|
|
1427
|
+
// Bare-identifier call (a wrapper like `d1(sql)`) → the identifier's package.
|
|
928
1428
|
if (calleeNode.type === 'identifier') {
|
|
929
1429
|
const name = adapter.getNodeText(calleeNode, sourceCode);
|
|
930
|
-
if (name
|
|
931
|
-
return
|
|
932
|
-
|
|
933
|
-
// Hybrid fallback: standalone calls to dbCallMethods (query, execute, etc.)
|
|
934
|
-
// qualify as likely DB calls when provenance couldn't resolve the import.
|
|
935
|
-
// This catches patterns like `import { query } from './db'` where './db' is
|
|
936
|
-
// a local re-export of a known package — provenance can't see through it,
|
|
937
|
-
// but the method name is strong evidence.
|
|
938
|
-
if (context.mode === 'hybrid' && name !== null && methods.has(name)) {
|
|
939
|
-
return true;
|
|
940
|
-
}
|
|
941
|
-
return false;
|
|
1430
|
+
if (!name)
|
|
1431
|
+
return null;
|
|
1432
|
+
return dialectForEvidence(provenanceContext.dbProvenanced.get(name));
|
|
942
1433
|
}
|
|
943
|
-
//
|
|
1434
|
+
// Member-expression call (`pool.query(...)`) → the receiver's package. Match the
|
|
1435
|
+
// full receiver text and each dotted segment, mirroring
|
|
1436
|
+
// `isMemberExpressionDBProvenanced` (`env.DB` / `db.users` / `this.db`).
|
|
944
1437
|
if (calleeNode.type === 'member_expression' || calleeNode.type === 'selector_expression') {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
* Traverses the member chain to find the root receiver,
|
|
953
|
-
* checks if it's in the provenance context, and verifies
|
|
954
|
-
* the method matches the DB call/ORM API.
|
|
955
|
-
*/
|
|
956
|
-
function isMemberExpressionDBProvenanced(node, query) {
|
|
957
|
-
const { adapter, sourceCode, context } = query;
|
|
958
|
-
const rootReceiver = findRootReceiver(node, adapter, sourceCode);
|
|
959
|
-
if (!rootReceiver)
|
|
960
|
-
return false;
|
|
961
|
-
// In names mode, check the method name directly
|
|
962
|
-
if (context.mode === 'names') {
|
|
963
|
-
return isDBMethodCall(node, query);
|
|
964
|
-
}
|
|
965
|
-
// Check if the root receiver is DB-provenanced.
|
|
966
|
-
// Compound receivers (e.g. "db.users" or "env.DB") need to match both
|
|
967
|
-
// the full text (for bindings like "env.DB") and each sub-identifier
|
|
968
|
-
// (for receivers like "db.users" where "db" is in dbReceiverNames).
|
|
969
|
-
const matchesProvenance = (r) => {
|
|
970
|
-
if (context.dbProvenanced.has(r))
|
|
971
|
-
return true;
|
|
972
|
-
for (const part of r.split('.')) {
|
|
973
|
-
if (context.dbProvenanced.has(part))
|
|
974
|
-
return true;
|
|
1438
|
+
const receiver = getMemberExpressionReceiver(calleeNode, adapter, sourceCode);
|
|
1439
|
+
if (!receiver)
|
|
1440
|
+
return null;
|
|
1441
|
+
for (const candidate of [receiver, ...receiver.split('.')]) {
|
|
1442
|
+
const dialect = dialectForEvidence(provenanceContext.dbProvenanced.get(candidate));
|
|
1443
|
+
if (dialect)
|
|
1444
|
+
return dialect;
|
|
975
1445
|
}
|
|
976
|
-
return false;
|
|
977
|
-
};
|
|
978
|
-
if (!matchesProvenance(rootReceiver)) {
|
|
979
|
-
if (rootReceiver !== 'this')
|
|
980
|
-
return false;
|
|
981
|
-
// For `this.xxx`, check if the method chain itself suggests DB usage
|
|
982
|
-
return isDBMethodOnThis(node, query);
|
|
983
|
-
}
|
|
984
|
-
// Check that the method is in the DB call/ORM API
|
|
985
|
-
return isDBMethodCall(node, query);
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Walk a member/selector expression chain to its root receiver text.
|
|
989
|
-
* Returns `'this'` for `this.xxx` chains and `null` when no root is found.
|
|
990
|
-
*/
|
|
991
|
-
function findRootReceiver(node, adapter, sourceCode) {
|
|
992
|
-
let current = node;
|
|
993
|
-
while (current.type === 'member_expression' || current.type === 'selector_expression') {
|
|
994
|
-
const children = adapter.getChildren(current);
|
|
995
|
-
const firstChild = children[0];
|
|
996
|
-
if (!firstChild)
|
|
997
|
-
break;
|
|
998
|
-
return resolveReceiverText(firstChild, adapter, sourceCode, 'this');
|
|
999
1446
|
}
|
|
1000
1447
|
return null;
|
|
1001
1448
|
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Walk a member/selector-expression chain, returning true when any property in
|
|
1004
|
-
* the chain is a DB or ORM method. Shared by isDBMethodCall and
|
|
1005
|
-
* isDBMethodOnThis, which differ only in how they read a node's children.
|
|
1006
|
-
*/
|
|
1007
|
-
function dbMethodInMemberChain(node, query, getChildren) {
|
|
1008
|
-
const { adapter, sourceCode, methods } = query;
|
|
1009
|
-
let current = node;
|
|
1010
|
-
while (current.type === 'member_expression' ||
|
|
1011
|
-
current.type === 'selector_expression') {
|
|
1012
|
-
const children = getChildren(current);
|
|
1013
|
-
// The property is typically the second or third child
|
|
1014
|
-
for (const child of children) {
|
|
1015
|
-
if (child.type === 'property_identifier' ||
|
|
1016
|
-
child.type === 'field_identifier') {
|
|
1017
|
-
const propName = adapter.getNodeText(child, sourceCode);
|
|
1018
|
-
const lower = propName.toLowerCase();
|
|
1019
|
-
if (methods.has(lower) || ORM_METHODS.has(lower)) {
|
|
1020
|
-
return true;
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
// Go deeper if there's a nested member/selector expression
|
|
1025
|
-
const firstChild = children[0];
|
|
1026
|
-
if (firstChild?.type === 'member_expression' ||
|
|
1027
|
-
firstChild?.type === 'selector_expression') {
|
|
1028
|
-
current = firstChild;
|
|
1029
|
-
}
|
|
1030
|
-
else {
|
|
1031
|
-
break;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
return false;
|
|
1035
|
-
}
|
|
1036
|
-
/**
|
|
1037
|
-
* Check if a call through a member expression uses a DB method
|
|
1038
|
-
* (exec, prepare, all, etc.) or an ORM method (find, insert, etc.).
|
|
1039
|
-
*/
|
|
1040
|
-
function isDBMethodCall(node, query) {
|
|
1041
|
-
return dbMethodInMemberChain(node, query, (n) => n.children ?? []);
|
|
1042
|
-
}
|
|
1043
|
-
/**
|
|
1044
|
-
* For `this.xxx.method()` calls — check if the method chain suggests DB access.
|
|
1045
|
-
* Used when the receiver is `this` (not directly DB-provenanced).
|
|
1046
|
-
*/
|
|
1047
|
-
function isDBMethodOnThis(node, query) {
|
|
1048
|
-
return dbMethodInMemberChain(node, query, (n) => query.adapter.getChildren(n));
|
|
1049
|
-
}
|
|
1050
1449
|
/**
|
|
1051
1450
|
* Check if an identifier is validator-provenanced (R4 infrastructure).
|
|
1052
1451
|
*
|
|
@@ -1097,7 +1496,7 @@ function inferReceiverFromCall(node, ctx) {
|
|
|
1097
1496
|
if (!callee || (callee.type !== 'member_expression' && callee.type !== 'selector_expression'))
|
|
1098
1497
|
return;
|
|
1099
1498
|
const methodName = extractMemberExpressionProperty(callee, adapter, sourceCode);
|
|
1100
|
-
if (!methodName ||
|
|
1499
|
+
if (!methodName || extractStaticSqlArgument(node, adapter, sourceCode) === null)
|
|
1101
1500
|
return;
|
|
1102
1501
|
const receiver = getMemberExpressionReceiver(callee, adapter, sourceCode);
|
|
1103
1502
|
if (!receiver)
|
|
@@ -1106,7 +1505,7 @@ function inferReceiverFromCall(node, ctx) {
|
|
|
1106
1505
|
if (provenancedSet.has(receiver) || seen.has(receiver))
|
|
1107
1506
|
return;
|
|
1108
1507
|
// Step 3: Trace receiver through assignment chain to a provenanced source
|
|
1109
|
-
const traced = traceAssignmentChain(receiver, assignmentGraph, provenancedSet);
|
|
1508
|
+
const traced = traceAssignmentChain(receiver, assignmentGraph, provenancedSet, adapter, sourceCode);
|
|
1110
1509
|
if (traced.found) {
|
|
1111
1510
|
seen.add(receiver);
|
|
1112
1511
|
inferred.identifiers.push(receiver);
|
|
@@ -1118,7 +1517,8 @@ function inferReceiverFromCall(node, ctx) {
|
|
|
1118
1517
|
}
|
|
1119
1518
|
/**
|
|
1120
1519
|
* Build a reverse assignment graph from the AST.
|
|
1121
|
-
* Maps variable name → the
|
|
1520
|
+
* Maps variable name → the *node* of its initializer expression (not its text,
|
|
1521
|
+
* so the trace can walk it structurally rather than regex over a string).
|
|
1122
1522
|
* Handles: const/let/var declarations, parameter defaults, class fields.
|
|
1123
1523
|
*/
|
|
1124
1524
|
function buildAssignmentGraph(ast, adapter, sourceCode) {
|
|
@@ -1142,10 +1542,9 @@ function recordVariableDeclarator(node, ctx) {
|
|
|
1142
1542
|
if (!nameNode || !valueNode)
|
|
1143
1543
|
return;
|
|
1144
1544
|
const names = extractPatternNames(nameNode, ctx.adapter, ctx.sourceCode);
|
|
1145
|
-
const valueText = ctx.adapter.getNodeText(valueNode, ctx.sourceCode);
|
|
1146
1545
|
for (const name of names) {
|
|
1147
1546
|
if (!ctx.graph.has(name))
|
|
1148
|
-
ctx.graph.set(name,
|
|
1547
|
+
ctx.graph.set(name, valueNode);
|
|
1149
1548
|
}
|
|
1150
1549
|
}
|
|
1151
1550
|
/** Record parameter defaults (`function foo(x = <expr>)`) into the graph. */
|
|
@@ -1161,14 +1560,12 @@ function recordParameterDefault(node, parent, ctx) {
|
|
|
1161
1560
|
if (leftNode.type !== 'identifier')
|
|
1162
1561
|
return;
|
|
1163
1562
|
const paramName = ctx.adapter.getNodeText(leftNode, ctx.sourceCode);
|
|
1164
|
-
const valueText = ctx.adapter.getNodeText(rightNode, ctx.sourceCode);
|
|
1165
1563
|
if (!ctx.graph.has(paramName))
|
|
1166
|
-
ctx.graph.set(paramName,
|
|
1564
|
+
ctx.graph.set(paramName, rightNode);
|
|
1167
1565
|
}
|
|
1168
1566
|
/** Record class-field initializers (`fieldName = <expr>`) into the graph. */
|
|
1169
1567
|
function recordClassField(node, ctx) {
|
|
1170
|
-
if (node.type !== 'public_field_definition'
|
|
1171
|
-
node.type !== 'field_definition') {
|
|
1568
|
+
if (node.type !== 'public_field_definition') {
|
|
1172
1569
|
return;
|
|
1173
1570
|
}
|
|
1174
1571
|
const children = ctx.adapter.getChildren(node);
|
|
@@ -1185,15 +1582,14 @@ function recordClassField(node, ctx) {
|
|
|
1185
1582
|
if (!nameChild || !valueChild)
|
|
1186
1583
|
return;
|
|
1187
1584
|
const fieldName = ctx.adapter.getNodeText(nameChild, ctx.sourceCode);
|
|
1188
|
-
const valueText = ctx.adapter.getNodeText(valueChild, ctx.sourceCode);
|
|
1189
1585
|
if (!ctx.graph.has(fieldName))
|
|
1190
|
-
ctx.graph.set(fieldName,
|
|
1586
|
+
ctx.graph.set(fieldName, valueChild);
|
|
1191
1587
|
}
|
|
1192
1588
|
/**
|
|
1193
1589
|
* Trace a receiver identifier through the assignment graph to see if it
|
|
1194
1590
|
* ultimately resolves to a provenanced source.
|
|
1195
1591
|
*/
|
|
1196
|
-
function traceAssignmentChain(receiver, assignmentGraph, provenancedSet) {
|
|
1592
|
+
function traceAssignmentChain(receiver, assignmentGraph, provenancedSet, adapter, sourceCode) {
|
|
1197
1593
|
const visited = new Set();
|
|
1198
1594
|
const chain = [receiver];
|
|
1199
1595
|
let current = receiver;
|
|
@@ -1208,9 +1604,9 @@ function traceAssignmentChain(receiver, assignmentGraph, provenancedSet) {
|
|
|
1208
1604
|
reason: provenancedSet.get(current).source,
|
|
1209
1605
|
};
|
|
1210
1606
|
}
|
|
1211
|
-
const
|
|
1212
|
-
if (
|
|
1213
|
-
const idents = extractTopLevelIdentifiers(
|
|
1607
|
+
const initNode = assignmentGraph.get(current);
|
|
1608
|
+
if (initNode) {
|
|
1609
|
+
const idents = extractTopLevelIdentifiers(initNode, adapter, sourceCode);
|
|
1214
1610
|
for (const ident of idents) {
|
|
1215
1611
|
if (provenancedSet.has(ident)) {
|
|
1216
1612
|
chain.push(current);
|
|
@@ -1222,10 +1618,10 @@ function traceAssignmentChain(receiver, assignmentGraph, provenancedSet) {
|
|
|
1222
1618
|
}
|
|
1223
1619
|
}
|
|
1224
1620
|
}
|
|
1225
|
-
if (!
|
|
1621
|
+
if (!initNode || visited.has(initNode))
|
|
1226
1622
|
break;
|
|
1227
|
-
visited.add(
|
|
1228
|
-
const next = findNextTraceIdentifier(
|
|
1623
|
+
visited.add(initNode);
|
|
1624
|
+
const next = findNextTraceIdentifier(initNode, assignmentGraph, visited, adapter, sourceCode);
|
|
1229
1625
|
if (!next)
|
|
1230
1626
|
break;
|
|
1231
1627
|
chain.push(next);
|
|
@@ -1235,33 +1631,93 @@ function traceAssignmentChain(receiver, assignmentGraph, provenancedSet) {
|
|
|
1235
1631
|
return { found: false, chains: [], reason: '' };
|
|
1236
1632
|
}
|
|
1237
1633
|
/** Find the next assignment-graph identifier to trace, if any. */
|
|
1238
|
-
function findNextTraceIdentifier(
|
|
1239
|
-
const idents = extractTopLevelIdentifiers(
|
|
1634
|
+
function findNextTraceIdentifier(initNode, assignmentGraph, visited, adapter, sourceCode) {
|
|
1635
|
+
const idents = extractTopLevelIdentifiers(initNode, adapter, sourceCode);
|
|
1240
1636
|
for (const ident of idents) {
|
|
1241
|
-
|
|
1637
|
+
const nextNode = assignmentGraph.get(ident);
|
|
1638
|
+
if (nextNode && !visited.has(nextNode)) {
|
|
1242
1639
|
return ident;
|
|
1243
1640
|
}
|
|
1244
1641
|
}
|
|
1245
1642
|
return null;
|
|
1246
1643
|
}
|
|
1247
1644
|
/**
|
|
1248
|
-
* Extract top-level identifier
|
|
1249
|
-
*
|
|
1645
|
+
* Extract the top-level identifier name from an initializer-expression node.
|
|
1646
|
+
* Walks the node structurally to its root identifier — through `await`,
|
|
1647
|
+
* parentheses, calls, member access, and subscripts — so `await db.connect()`
|
|
1648
|
+
* yields `db` rather than `await`, and `(db.prepare(sql))` yields `db`.
|
|
1649
|
+
* E.g., `db.prepare(sql)` → ["db"], `getConnection()` → ["getConnection"].
|
|
1650
|
+
*
|
|
1651
|
+
* @param node the initializer-expression node to walk from
|
|
1652
|
+
* @param adapter the language adapter used to walk child nodes
|
|
1653
|
+
* @param sourceCode the file source text for reading node text
|
|
1654
|
+
* @returns the root identifier name, or an empty array when there is none
|
|
1655
|
+
*/
|
|
1656
|
+
export function extractTopLevelIdentifiers(node, adapter, sourceCode) {
|
|
1657
|
+
const root = rootIdentifierNode(node, adapter);
|
|
1658
|
+
if (!root)
|
|
1659
|
+
return [];
|
|
1660
|
+
const name = adapter.getNodeText(root, sourceCode);
|
|
1661
|
+
return name ? [name] : [];
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* The root identifier a value node resolves to, or null when the value is not a
|
|
1665
|
+
* bare identifier chain (e.g. a binary expression or literal). Descends through
|
|
1666
|
+
* `await_expression`, `parenthesized_expression`, `call_expression`/
|
|
1667
|
+
* `new_expression` (via their callee), `member_expression`/`selector_expression`
|
|
1668
|
+
* (via their receiver), and `subscript_expression` (via their object).
|
|
1250
1669
|
*/
|
|
1251
|
-
function
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1670
|
+
function rootIdentifierNode(node, adapter) {
|
|
1671
|
+
let current = node;
|
|
1672
|
+
for (let depth = 0; depth < 64; depth++) {
|
|
1673
|
+
const t = adapter.getNodeType(current);
|
|
1674
|
+
if (t === 'identifier')
|
|
1675
|
+
return current;
|
|
1676
|
+
const children = adapter.getChildren(current) ?? [];
|
|
1677
|
+
let next = null;
|
|
1678
|
+
if (t === 'await_expression') {
|
|
1679
|
+
next = children.find((c) => adapter.getNodeType(c) !== 'await') ?? null;
|
|
1680
|
+
}
|
|
1681
|
+
else if (t === 'parenthesized_expression') {
|
|
1682
|
+
next = children.find((c) => {
|
|
1683
|
+
const ct = adapter.getNodeType(c);
|
|
1684
|
+
return ct !== '(' && ct !== ')';
|
|
1685
|
+
}) ?? null;
|
|
1686
|
+
}
|
|
1687
|
+
else if (t === 'call_expression' || t === 'new_expression') {
|
|
1688
|
+
next = getCallExpressionCallee(current, adapter);
|
|
1689
|
+
}
|
|
1690
|
+
else if (t === 'member_expression' || t === 'selector_expression') {
|
|
1691
|
+
next = children.find((c) => {
|
|
1692
|
+
const ct = adapter.getNodeType(c);
|
|
1693
|
+
return ct !== '.' && ct !== 'property_identifier' && ct !== 'field_identifier';
|
|
1694
|
+
}) ?? null;
|
|
1695
|
+
}
|
|
1696
|
+
else if (t === 'subscript_expression') {
|
|
1697
|
+
next = children.find((c) => {
|
|
1698
|
+
const ct = adapter.getNodeType(c);
|
|
1699
|
+
return ct !== '[' && ct !== ']';
|
|
1700
|
+
}) ?? null;
|
|
1701
|
+
}
|
|
1702
|
+
else {
|
|
1703
|
+
break;
|
|
1704
|
+
}
|
|
1705
|
+
if (!next)
|
|
1706
|
+
break;
|
|
1707
|
+
current = next;
|
|
1257
1708
|
}
|
|
1258
|
-
return
|
|
1709
|
+
return adapter.getNodeType(current) === 'identifier' ? current : null;
|
|
1259
1710
|
}
|
|
1260
1711
|
/**
|
|
1261
1712
|
* Extract the property name from a member expression node.
|
|
1262
1713
|
* For `db.prepare` → "prepare", for `db.sql.prepare` → "prepare"
|
|
1714
|
+
*
|
|
1715
|
+
* @param node the member/selector expression node
|
|
1716
|
+
* @param adapter the language adapter used to read children
|
|
1717
|
+
* @param sourceCode the file source text for reading node text
|
|
1718
|
+
* @returns the property identifier text, or `null`
|
|
1263
1719
|
*/
|
|
1264
|
-
function extractMemberExpressionProperty(node, adapter, sourceCode) {
|
|
1720
|
+
export function extractMemberExpressionProperty(node, adapter, sourceCode) {
|
|
1265
1721
|
const children = adapter.getChildren(node);
|
|
1266
1722
|
// The property is the last non-dot child
|
|
1267
1723
|
for (let i = children.length - 1; i >= 0; i--) {
|
|
@@ -1272,4 +1728,228 @@ function extractMemberExpressionProperty(node, adapter, sourceCode) {
|
|
|
1272
1728
|
}
|
|
1273
1729
|
return null;
|
|
1274
1730
|
}
|
|
1731
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1732
|
+
// TS within-file-provenance split — the extract arm (Spec 70 Item 4 / Item 3)
|
|
1733
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1734
|
+
//
|
|
1735
|
+
// `extractTsWithinFileProvenance` projects everything `computeTsWithinFileProvenance`
|
|
1736
|
+
// reads from a single file's AST into a serializable `TsWithinFileProvenanceExtract`
|
|
1737
|
+
// (see tsExpressionDescriptor.ts). The classify arm re-runs the fixed point over the
|
|
1738
|
+
// projection with no AST. The two are pinned byte-identical by the parity spec.
|
|
1739
|
+
//
|
|
1740
|
+
// Every helper below is the *structural* half — provenance-free — so it may run at
|
|
1741
|
+
// extract time. The semantic half (which name resolves to which evidence) is
|
|
1742
|
+
// deferred to classify, where the provenance map is known.
|
|
1743
|
+
/** Project an `await` expression: the first non-`await` child carries the value. */
|
|
1744
|
+
function describeAwaitExpression(node, adapter, sourceCode) {
|
|
1745
|
+
for (const child of adapter.getChildren(node)) {
|
|
1746
|
+
if (child.type === 'await')
|
|
1747
|
+
continue;
|
|
1748
|
+
return { kind: 'await', operand: describeTsExpression(child, adapter, sourceCode) };
|
|
1749
|
+
}
|
|
1750
|
+
return { kind: 'await', operand: null };
|
|
1751
|
+
}
|
|
1752
|
+
/** Project a `new` expression: the leftmost identifier of its constructor. */
|
|
1753
|
+
function describeNewExpression(node, adapter, sourceCode) {
|
|
1754
|
+
const ctor = findChildOfType(node, ['identifier', 'member_expression']);
|
|
1755
|
+
return { kind: 'new', ctorName: ctor ? extractIdentifierName(ctor, adapter, sourceCode) : null };
|
|
1756
|
+
}
|
|
1757
|
+
/** Project a call expression: the callee (via `getCallExpressionCallee`) + its args. */
|
|
1758
|
+
function describeCallExpression(node, adapter, sourceCode) {
|
|
1759
|
+
const calleeNode = getCallExpressionCallee(node, adapter);
|
|
1760
|
+
const callee = calleeNode ? describeTsExpression(calleeNode, adapter, sourceCode) : null;
|
|
1761
|
+
const argsNode = adapter.getChildren(node).find((c) => c.type === 'arguments');
|
|
1762
|
+
const args = [];
|
|
1763
|
+
if (argsNode) {
|
|
1764
|
+
for (const arg of adapter.getChildren(argsNode)) {
|
|
1765
|
+
if (arg.type === '(' || arg.type === ')' || arg.type === ',')
|
|
1766
|
+
continue;
|
|
1767
|
+
args.push(describeTsExpression(arg, adapter, sourceCode));
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
return { kind: 'call', callee, args };
|
|
1771
|
+
}
|
|
1772
|
+
/** Project a member expression: receiver (object) + property + full source text. */
|
|
1773
|
+
function describeMemberExpression(node, adapter, sourceCode) {
|
|
1774
|
+
const children = adapter.getChildren(node);
|
|
1775
|
+
const receiver = children[0] ? describeTsExpression(children[0], adapter, sourceCode) : null;
|
|
1776
|
+
let property = null;
|
|
1777
|
+
for (const child of children) {
|
|
1778
|
+
if (child.type === 'property_identifier' || child.type === 'field_identifier') {
|
|
1779
|
+
property = adapter.getNodeText(child, sourceCode);
|
|
1780
|
+
break;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
return { kind: 'member', receiver, property, text: adapter.getNodeText(node, sourceCode) };
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Project a value-expression node into a {@link TsExpressionDescriptor}. Mirrors
|
|
1787
|
+
* the *structural* dispatch of `tryPropagateFromExpression`: `await` → `new` →
|
|
1788
|
+
* `call` → `identifier` → `member`, and anything else collapses to `unproven`.
|
|
1789
|
+
*/
|
|
1790
|
+
function describeTsExpression(node, adapter, sourceCode) {
|
|
1791
|
+
switch (node.type) {
|
|
1792
|
+
case 'await_expression':
|
|
1793
|
+
return describeAwaitExpression(node, adapter, sourceCode);
|
|
1794
|
+
case 'new_expression':
|
|
1795
|
+
return describeNewExpression(node, adapter, sourceCode);
|
|
1796
|
+
case 'call_expression':
|
|
1797
|
+
return describeCallExpression(node, adapter, sourceCode);
|
|
1798
|
+
case 'identifier':
|
|
1799
|
+
return { kind: 'identifier', name: adapter.getNodeText(node, sourceCode) };
|
|
1800
|
+
case 'member_expression':
|
|
1801
|
+
return describeMemberExpression(node, adapter, sourceCode);
|
|
1802
|
+
default:
|
|
1803
|
+
return { kind: 'unproven' };
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
/** Child types excluded when locating a class-field initializer (mirrors `propagateClassField`). */
|
|
1807
|
+
const CLASS_FIELD_EXCLUDED = new Set([
|
|
1808
|
+
'property_identifier',
|
|
1809
|
+
'decorator',
|
|
1810
|
+
'accessibility_modifier',
|
|
1811
|
+
'private',
|
|
1812
|
+
'public',
|
|
1813
|
+
'protected',
|
|
1814
|
+
'static',
|
|
1815
|
+
'readonly',
|
|
1816
|
+
'abstract',
|
|
1817
|
+
'type_annotation',
|
|
1818
|
+
]);
|
|
1819
|
+
/**
|
|
1820
|
+
* Project a single file's AST into the serializable {@link TsWithinFileProvenanceExtract}
|
|
1821
|
+
* that `classifyTsWithinFileProvenance` re-runs the provenance fixed point over.
|
|
1822
|
+
*
|
|
1823
|
+
* Everything here is provenance-free (structural reads of the AST + source text),
|
|
1824
|
+
* so it can run once at parse time and be carried as a file fact. The semantic
|
|
1825
|
+
* resolution — which name a value resolves to under the current provenance map —
|
|
1826
|
+
* is deferred to the classify arm.
|
|
1827
|
+
* @param ast - The file's parsed AST.
|
|
1828
|
+
* @param adapter - The language adapter (drives the walk + node-text reads).
|
|
1829
|
+
* @param sourceCode - The file's source text (for structural reads).
|
|
1830
|
+
* @returns The provenance-free `TsWithinFileProvenanceExtract` projection.
|
|
1831
|
+
*/
|
|
1832
|
+
export function extractTsWithinFileProvenance(ast, adapter, sourceCode) {
|
|
1833
|
+
const seeds = extractDBProvenancedImports(ast, adapter);
|
|
1834
|
+
const localFunctions = collectLocalFunctionNames(ast, adapter, sourceCode);
|
|
1835
|
+
const bindings = buildBindingEnv(ast, adapter, sourceCode);
|
|
1836
|
+
const interfaceFields = extractInterfaceFields(ast, adapter, sourceCode);
|
|
1837
|
+
// ── Propagation rules, in `walkAST` pre-order ─────────────────────────────
|
|
1838
|
+
const propagationRules = [];
|
|
1839
|
+
walkAST(ast.root, (node, parent) => {
|
|
1840
|
+
if (node.type === 'variable_declarator') {
|
|
1841
|
+
const { nameNode, valueNode } = splitVariableDeclarator(node, adapter);
|
|
1842
|
+
if (!nameNode)
|
|
1843
|
+
return;
|
|
1844
|
+
const names = extractPatternNames(nameNode, adapter, sourceCode);
|
|
1845
|
+
const value = valueNode ? describeTsExpression(valueNode, adapter, sourceCode) : null;
|
|
1846
|
+
propagationRules.push({ kind: 'variable-declarator', names, value });
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
if (node.type === 'assignment_pattern' && parent?.type === 'formal_parameters') {
|
|
1850
|
+
const children = adapter.getChildren(node);
|
|
1851
|
+
if (children.length < 2 || children[0].type !== 'identifier')
|
|
1852
|
+
return;
|
|
1853
|
+
propagationRules.push({
|
|
1854
|
+
kind: 'default-parameter',
|
|
1855
|
+
name: adapter.getNodeText(children[0], sourceCode),
|
|
1856
|
+
value: describeTsExpression(children[1], adapter, sourceCode),
|
|
1857
|
+
});
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
if (node.type === 'public_field_definition') {
|
|
1861
|
+
const children = adapter.getChildren(node);
|
|
1862
|
+
const nameChild = children.find((c) => c.type === 'property_identifier');
|
|
1863
|
+
if (!nameChild)
|
|
1864
|
+
return;
|
|
1865
|
+
const valueChild = children.find((c) => !CLASS_FIELD_EXCLUDED.has(c.type));
|
|
1866
|
+
if (!valueChild)
|
|
1867
|
+
return;
|
|
1868
|
+
propagationRules.push({
|
|
1869
|
+
kind: 'class-field',
|
|
1870
|
+
field: adapter.getNodeText(nameChild, sourceCode),
|
|
1871
|
+
value: describeTsExpression(valueChild, adapter, sourceCode),
|
|
1872
|
+
});
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
if (node.type === 'assignment_expression') {
|
|
1876
|
+
const children = adapter.getChildren(node);
|
|
1877
|
+
const lhs = children.find((c) => c.type === 'member_expression' || c.type === 'selector_expression');
|
|
1878
|
+
if (!lhs)
|
|
1879
|
+
return;
|
|
1880
|
+
const lhsChildren = adapter.getChildren(lhs);
|
|
1881
|
+
const obj = lhsChildren.find((c) => c.type === 'this' || c.type === 'super');
|
|
1882
|
+
if (!obj)
|
|
1883
|
+
return;
|
|
1884
|
+
const prop = lhsChildren.find((c) => c.type === 'property_identifier' || c.type === 'field_identifier');
|
|
1885
|
+
if (!prop)
|
|
1886
|
+
return;
|
|
1887
|
+
const fieldName = adapter.getNodeText(prop, sourceCode);
|
|
1888
|
+
if (!fieldName)
|
|
1889
|
+
return;
|
|
1890
|
+
const rhs = children[children.length - 1];
|
|
1891
|
+
if (!rhs || rhs.type === 'member_expression' || rhs.type === 'selector_expression')
|
|
1892
|
+
return;
|
|
1893
|
+
propagationRules.push({
|
|
1894
|
+
kind: 'member-assignment',
|
|
1895
|
+
field: fieldName,
|
|
1896
|
+
value: describeTsExpression(rhs, adapter, sourceCode),
|
|
1897
|
+
});
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
// ── Wrapper functions, wrapper classes, returning functions ────────────────
|
|
1902
|
+
// One `findNodes` pass for functions (wrappers + returning) and one for classes,
|
|
1903
|
+
// matching the order `detectDbWrappers` / `detectDbReturningFunctions` consume.
|
|
1904
|
+
const wrapperFunctions = [];
|
|
1905
|
+
const returningFunctions = [];
|
|
1906
|
+
const functionNodes = adapter.findNodes(ast, {
|
|
1907
|
+
custom: (n) => FUNCTION_NODE_TYPES.has(n.type),
|
|
1908
|
+
});
|
|
1909
|
+
for (const fn of functionNodes) {
|
|
1910
|
+
const name = adapter.getNodeName(fn);
|
|
1911
|
+
if (!name)
|
|
1912
|
+
continue;
|
|
1913
|
+
const ownCalls = collectOwnCallExpressions(fn, adapter).map((call) => ({
|
|
1914
|
+
callee: getCallExpressionCallee(call, adapter)
|
|
1915
|
+
? describeTsExpression(getCallExpressionCallee(call, adapter), adapter, sourceCode)
|
|
1916
|
+
: null,
|
|
1917
|
+
isD1Rest: isD1RestCall(call, adapter, sourceCode),
|
|
1918
|
+
}));
|
|
1919
|
+
wrapperFunctions.push({ name, ownCalls });
|
|
1920
|
+
const returnExprs = [];
|
|
1921
|
+
walkAST(fn, (n) => {
|
|
1922
|
+
if (n.type !== 'return_statement')
|
|
1923
|
+
return;
|
|
1924
|
+
const arg = adapter.getChildren(n).find((c) => c.type !== 'return');
|
|
1925
|
+
if (!arg)
|
|
1926
|
+
return;
|
|
1927
|
+
returnExprs.push(describeTsExpression(arg, adapter, sourceCode));
|
|
1928
|
+
});
|
|
1929
|
+
returningFunctions.push({ name, returnExprs });
|
|
1930
|
+
}
|
|
1931
|
+
const wrapperClasses = [];
|
|
1932
|
+
const classNodes = adapter.findNodes(ast, {
|
|
1933
|
+
custom: (n) => WRAPPER_CLASS_TYPES.has(n.type),
|
|
1934
|
+
});
|
|
1935
|
+
for (const cls of classNodes) {
|
|
1936
|
+
const name = adapter.getNodeName(cls);
|
|
1937
|
+
if (!name)
|
|
1938
|
+
continue;
|
|
1939
|
+
const classCalls = collectClassCallExpressions(cls, adapter).map((expr) => {
|
|
1940
|
+
if (expr.type === 'new_expression') {
|
|
1941
|
+
const ctor = findChildOfType(expr, ['identifier', 'member_expression']);
|
|
1942
|
+
return { kind: 'new', ctorName: ctor ? extractIdentifierName(ctor, adapter, sourceCode) : null };
|
|
1943
|
+
}
|
|
1944
|
+
const calleeNode = getCallExpressionCallee(expr, adapter);
|
|
1945
|
+
return {
|
|
1946
|
+
kind: 'call',
|
|
1947
|
+
callee: calleeNode ? describeTsExpression(calleeNode, adapter, sourceCode) : null,
|
|
1948
|
+
isD1Rest: isD1RestCall(expr, adapter, sourceCode),
|
|
1949
|
+
};
|
|
1950
|
+
});
|
|
1951
|
+
wrapperClasses.push({ name, classCalls });
|
|
1952
|
+
}
|
|
1953
|
+
return { seeds, bindings, localFunctions, propagationRules, wrapperFunctions, wrapperClasses, returningFunctions, interfaceFields };
|
|
1954
|
+
}
|
|
1275
1955
|
//# sourceMappingURL=provenance.js.map
|