code-auditor-mcp 4.1.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +374 -0
- package/README.md +4 -4
- package/configs/hhra-compat.json +0 -8
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +7 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +2 -8
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +4 -4
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +2 -2
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +1 -1
- package/dist/analyzers/applicability.d.ts +42 -0
- package/dist/analyzers/applicability.d.ts.map +1 -1
- package/dist/analyzers/applicability.js +74 -0
- package/dist/analyzers/applicability.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +18 -24
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +30 -23
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +113 -76
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +2 -0
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +8 -3
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +210 -148
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +23 -28
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/handleIdentification.d.ts +356 -0
- package/dist/analyzers/handleIdentification.d.ts.map +1 -0
- package/dist/analyzers/handleIdentification.js +424 -0
- package/dist/analyzers/handleIdentification.js.map +1 -0
- package/dist/analyzers/manifestStaleness.d.ts +64 -0
- package/dist/analyzers/manifestStaleness.d.ts.map +1 -0
- package/dist/analyzers/manifestStaleness.js +115 -0
- package/dist/analyzers/manifestStaleness.js.map +1 -0
- package/dist/analyzers/orgFilterTiers.d.ts +73 -1
- package/dist/analyzers/orgFilterTiers.d.ts.map +1 -1
- package/dist/analyzers/orgFilterTiers.js +166 -1
- package/dist/analyzers/orgFilterTiers.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +251 -57
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +1109 -429
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +2 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +2 -0
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/receiverResolution.d.ts +183 -0
- package/dist/analyzers/receiverResolution.d.ts.map +1 -0
- package/dist/analyzers/receiverResolution.js +540 -0
- package/dist/analyzers/receiverResolution.js.map +1 -0
- package/dist/analyzers/receiverRoot.d.ts +328 -0
- package/dist/analyzers/receiverRoot.d.ts.map +1 -0
- package/dist/analyzers/receiverRoot.js +990 -0
- package/dist/analyzers/receiverRoot.js.map +1 -0
- package/dist/analyzers/ruleRegistry.d.ts +12 -68
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +32 -342
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +10 -0
- package/dist/analyzers/ruleTiming.d.ts.map +1 -1
- package/dist/analyzers/ruleTiming.js +20 -0
- package/dist/analyzers/ruleTiming.js.map +1 -1
- package/dist/analyzers/sqlLiteral.d.ts +76 -0
- package/dist/analyzers/sqlLiteral.d.ts.map +1 -0
- package/dist/analyzers/sqlLiteral.js +419 -0
- package/dist/analyzers/sqlLiteral.js.map +1 -0
- package/dist/analyzers/tsEcosystem.d.ts +115 -0
- package/dist/analyzers/tsEcosystem.d.ts.map +1 -0
- package/dist/analyzers/tsEcosystem.js +231 -0
- package/dist/analyzers/tsEcosystem.js.map +1 -0
- package/dist/analyzers/tsExpressionDescriptor.d.ts +156 -0
- package/dist/analyzers/tsExpressionDescriptor.d.ts.map +1 -0
- package/dist/analyzers/tsExpressionDescriptor.js +374 -0
- package/dist/analyzers/tsExpressionDescriptor.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +6 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +107 -44
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +1 -12
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +24 -101
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +287 -49
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +2130 -512
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +2 -18
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -33
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +20 -143
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +4 -3
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +36 -13
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.js +6 -0
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts +15 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js +25 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +21 -32
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +166 -196
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/functionConcerns.d.ts +3 -0
- package/dist/analyzers/universal/functionConcerns.d.ts.map +1 -1
- package/dist/analyzers/universal/functionConcerns.js +4 -1
- package/dist/analyzers/universal/functionConcerns.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +310 -69
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js +709 -383
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
- package/dist/analyzers/universal/schema/config.d.ts +3 -14
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/config.js +3 -16
- package/dist/analyzers/universal/schema/config.js.map +1 -1
- package/dist/analyzers/universal/schema/discovery.d.ts +22 -1
- package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/discovery.js +36 -20
- package/dist/analyzers/universal/schema/discovery.js.map +1 -1
- package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/jsonSchema.js +13 -3
- package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -1
- package/dist/analyzers/universal/schema/migrations.d.ts +187 -27
- package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/migrations.js +443 -186
- package/dist/analyzers/universal/schema/migrations.js.map +1 -1
- package/dist/analyzers/universal/schema/types.d.ts +17 -3
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/violations.js +1 -1
- package/dist/analyzers/universal/schema/violations.js.map +1 -1
- package/dist/analyzers/universal/styleScale.d.ts +52 -0
- package/dist/analyzers/universal/styleScale.d.ts.map +1 -0
- package/dist/analyzers/universal/styleScale.js +95 -0
- package/dist/analyzers/universal/styleScale.js.map +1 -0
- package/dist/applyDataDirEnv.js +1 -1
- package/dist/applyDataDirEnv.js.map +1 -1
- package/dist/auditRouter.d.ts +26 -30
- package/dist/auditRouter.d.ts.map +1 -1
- package/dist/auditRouter.js +373 -143
- package/dist/auditRouter.js.map +1 -1
- package/dist/auditRunner.d.ts +64 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +1537 -1051
- package/dist/auditRunner.js.map +1 -1
- package/dist/baseline.d.ts +30 -3
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +70 -9
- package/dist/baseline.js.map +1 -1
- package/dist/cli.js +997 -812
- package/dist/cli.js.map +1 -1
- package/dist/codeIndex/analyzerConfig.d.ts +60 -0
- package/dist/codeIndex/analyzerConfig.d.ts.map +1 -0
- package/dist/codeIndex/analyzerConfig.js +98 -0
- package/dist/codeIndex/analyzerConfig.js.map +1 -0
- package/dist/codeIndex/auditResults.d.ts +39 -0
- package/dist/codeIndex/auditResults.d.ts.map +1 -0
- package/dist/codeIndex/auditResults.js +99 -0
- package/dist/codeIndex/auditResults.js.map +1 -0
- package/dist/codeIndex/codeMap.d.ts +59 -0
- package/dist/codeIndex/codeMap.d.ts.map +1 -0
- package/dist/codeIndex/codeMap.js +72 -0
- package/dist/codeIndex/codeMap.js.map +1 -0
- package/dist/codeIndex/conventions.d.ts +33 -0
- package/dist/codeIndex/conventions.d.ts.map +1 -0
- package/dist/codeIndex/conventions.js +84 -0
- package/dist/codeIndex/conventions.js.map +1 -0
- package/dist/codeIndex/coverage.d.ts +78 -0
- package/dist/codeIndex/coverage.d.ts.map +1 -0
- package/dist/codeIndex/coverage.js +164 -0
- package/dist/codeIndex/coverage.js.map +1 -0
- package/dist/codeIndex/dependencyGraph.d.ts +97 -0
- package/dist/codeIndex/dependencyGraph.d.ts.map +1 -0
- package/dist/codeIndex/dependencyGraph.js +241 -0
- package/dist/codeIndex/dependencyGraph.js.map +1 -0
- package/dist/codeIndex/functionIndex.d.ts +111 -0
- package/dist/codeIndex/functionIndex.d.ts.map +1 -0
- package/dist/codeIndex/functionIndex.js +306 -0
- package/dist/codeIndex/functionIndex.js.map +1 -0
- package/dist/codeIndex/inferredReceivers.d.ts +32 -0
- package/dist/codeIndex/inferredReceivers.d.ts.map +1 -0
- package/dist/codeIndex/inferredReceivers.js +41 -0
- package/dist/codeIndex/inferredReceivers.js.map +1 -0
- package/dist/codeIndex/meta.d.ts +30 -0
- package/dist/codeIndex/meta.d.ts.map +1 -0
- package/dist/codeIndex/meta.js +37 -0
- package/dist/codeIndex/meta.js.map +1 -0
- package/dist/codeIndex/migrations.d.ts +96 -0
- package/dist/codeIndex/migrations.d.ts.map +1 -0
- package/dist/codeIndex/migrations.js +1302 -0
- package/dist/codeIndex/migrations.js.map +1 -0
- package/dist/codeIndex/projectTasks.d.ts +54 -0
- package/dist/codeIndex/projectTasks.d.ts.map +1 -0
- package/dist/codeIndex/projectTasks.js +81 -0
- package/dist/codeIndex/projectTasks.js.map +1 -0
- package/dist/codeIndex/provenance.d.ts +47 -0
- package/dist/codeIndex/provenance.d.ts.map +1 -0
- package/dist/codeIndex/provenance.js +48 -0
- package/dist/codeIndex/provenance.js.map +1 -0
- package/dist/codeIndex/rawSql.d.ts +54 -0
- package/dist/codeIndex/rawSql.d.ts.map +1 -0
- package/dist/codeIndex/rawSql.js +71 -0
- package/dist/codeIndex/rawSql.js.map +1 -0
- package/dist/codeIndex/schema.d.ts +99 -0
- package/dist/codeIndex/schema.d.ts.map +1 -0
- package/dist/codeIndex/schema.js +174 -0
- package/dist/codeIndex/schema.js.map +1 -0
- package/dist/codeIndex/search.d.ts +40 -0
- package/dist/codeIndex/search.d.ts.map +1 -0
- package/dist/codeIndex/search.js +316 -0
- package/dist/codeIndex/search.js.map +1 -0
- package/dist/codeIndex/shared.d.ts +27 -0
- package/dist/codeIndex/shared.d.ts.map +1 -0
- package/dist/codeIndex/shared.js +46 -0
- package/dist/codeIndex/shared.js.map +1 -0
- package/dist/codeIndex/sqliteCollection.d.ts +81 -0
- package/dist/codeIndex/sqliteCollection.d.ts.map +1 -0
- package/dist/codeIndex/sqliteCollection.js +250 -0
- package/dist/codeIndex/sqliteCollection.js.map +1 -0
- package/dist/codeIndex/whitelist.d.ts +59 -0
- package/dist/codeIndex/whitelist.d.ts.map +1 -0
- package/dist/codeIndex/whitelist.js +188 -0
- package/dist/codeIndex/whitelist.js.map +1 -0
- package/dist/codeIndexDB.d.ts +130 -277
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +364 -2748
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/codeIndexService.d.ts +87 -12
- package/dist/codeIndexService.d.ts.map +1 -1
- package/dist/codeIndexService.js +110 -34
- package/dist/codeIndexService.js.map +1 -1
- package/dist/componentScanner.d.ts +35 -0
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +114 -80
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configGeneratePath.d.ts +4 -0
- package/dist/config/configGeneratePath.d.ts.map +1 -1
- package/dist/config/configGeneratePath.js +4 -0
- package/dist/config/configGeneratePath.js.map +1 -1
- package/dist/config/configLoader.d.ts +16 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +26 -23
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +11 -7
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +21 -20
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +17 -2
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +32 -25
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/config/lintConfigReader.d.ts +6 -0
- package/dist/config/lintConfigReader.d.ts.map +1 -1
- package/dist/config/lintConfigReader.js +6 -0
- package/dist/config/lintConfigReader.js.map +1 -1
- package/dist/config/staticObjectExtract.d.ts +4 -0
- package/dist/config/staticObjectExtract.d.ts.map +1 -1
- package/dist/config/staticObjectExtract.js +7 -3
- package/dist/config/staticObjectExtract.js.map +1 -1
- package/dist/config/thresholdRationales.d.ts +2 -0
- package/dist/config/thresholdRationales.d.ts.map +1 -1
- package/dist/config/thresholdRationales.js +5 -2
- package/dist/config/thresholdRationales.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -5
- package/dist/constants.js.map +1 -1
- package/dist/conventions/conventionMiner.d.ts +105 -2
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +334 -259
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/coverage/coverageService.js +4 -4
- package/dist/coverage/coverageService.js.map +1 -1
- package/dist/daemon/args.d.ts +3 -0
- package/dist/daemon/args.d.ts.map +1 -1
- package/dist/daemon/args.js +3 -0
- package/dist/daemon/args.js.map +1 -1
- package/dist/daemon/core.d.ts +33 -27
- package/dist/daemon/core.d.ts.map +1 -1
- package/dist/daemon/core.js +198 -162
- package/dist/daemon/core.js.map +1 -1
- package/dist/daemon/lspServer.d.ts +8 -0
- package/dist/daemon/lspServer.d.ts.map +1 -1
- package/dist/daemon/lspServer.js +8 -0
- package/dist/daemon/lspServer.js.map +1 -1
- package/dist/daemon/main.d.ts +2 -0
- package/dist/daemon/main.d.ts.map +1 -1
- package/dist/daemon/main.js +2 -0
- package/dist/daemon/main.js.map +1 -1
- package/dist/daemon/resolve.d.ts +22 -7
- package/dist/daemon/resolve.d.ts.map +1 -1
- package/dist/daemon/resolve.js +29 -8
- package/dist/daemon/resolve.js.map +1 -1
- package/dist/daemon/socketClient.d.ts +10 -1
- package/dist/daemon/socketClient.d.ts.map +1 -1
- package/dist/daemon/socketClient.js +10 -1
- package/dist/daemon/socketClient.js.map +1 -1
- package/dist/daemon/socketServer.d.ts +13 -0
- package/dist/daemon/socketServer.d.ts.map +1 -1
- package/dist/daemon/socketServer.js +13 -0
- package/dist/daemon/socketServer.js.map +1 -1
- package/dist/dataPaths.d.ts +27 -8
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +28 -44
- package/dist/dataPaths.js.map +1 -1
- package/dist/dismissals.d.ts +22 -1
- package/dist/dismissals.d.ts.map +1 -1
- package/dist/dismissals.js +22 -1
- package/dist/dismissals.js.map +1 -1
- package/dist/enforcement/gate.d.ts +1 -0
- package/dist/enforcement/gate.d.ts.map +1 -1
- package/dist/enforcement/gate.js +1 -0
- package/dist/enforcement/gate.js.map +1 -1
- package/dist/fingerprint.d.ts +6 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +46 -1
- package/dist/fingerprint.js.map +1 -1
- package/dist/functionScanner.d.ts +30 -1
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +258 -256
- package/dist/functionScanner.js.map +1 -1
- package/dist/generators/AWSQConfigGenerator.d.ts +23 -0
- package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AWSQConfigGenerator.js +23 -0
- package/dist/generators/AWSQConfigGenerator.js.map +1 -1
- package/dist/generators/BaseConfigGenerator.d.ts +7 -0
- package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.js +7 -0
- package/dist/generators/BaseConfigGenerator.js.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.d.ts +23 -0
- package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.js +23 -0
- package/dist/generators/ClaudeConfigGenerator.js.map +1 -1
- package/dist/generators/ClineConfigGenerator.d.ts +23 -0
- package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClineConfigGenerator.js +23 -0
- package/dist/generators/ClineConfigGenerator.js.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.d.ts +23 -0
- package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.js +23 -0
- package/dist/generators/CodeiumConfigGenerator.js.map +1 -1
- package/dist/generators/CodexConfigGenerator.d.ts +23 -0
- package/dist/generators/CodexConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodexConfigGenerator.js +23 -0
- package/dist/generators/CodexConfigGenerator.js.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.d.ts +9 -0
- package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.js +9 -0
- package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
- package/dist/generators/ContinueConfigGenerator.d.ts +23 -0
- package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ContinueConfigGenerator.js +23 -0
- package/dist/generators/ContinueConfigGenerator.js.map +1 -1
- package/dist/generators/CopilotConfigGenerator.d.ts +23 -0
- package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CopilotConfigGenerator.js +23 -0
- package/dist/generators/CopilotConfigGenerator.js.map +1 -1
- package/dist/generators/CursorConfigGenerator.d.ts +23 -0
- package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CursorConfigGenerator.js +23 -0
- package/dist/generators/CursorConfigGenerator.js.map +1 -1
- package/dist/generators/GeminiConfigGenerator.d.ts +23 -0
- package/dist/generators/GeminiConfigGenerator.d.ts.map +1 -1
- package/dist/generators/GeminiConfigGenerator.js +23 -0
- package/dist/generators/GeminiConfigGenerator.js.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.d.ts +23 -0
- package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.js +23 -0
- package/dist/generators/VSCodeConfigGenerator.js.map +1 -1
- package/dist/generators/ZCodeConfigGenerator.d.ts +23 -0
- package/dist/generators/ZCodeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ZCodeConfigGenerator.js +23 -0
- package/dist/generators/ZCodeConfigGenerator.js.map +1 -1
- package/dist/graph/blastRadius.d.ts.map +1 -1
- package/dist/graph/blastRadius.js +12 -7
- package/dist/graph/blastRadius.js.map +1 -1
- package/dist/graph/callGraph.d.ts +31 -0
- package/dist/graph/callGraph.d.ts.map +1 -1
- package/dist/graph/callGraph.js +62 -32
- package/dist/graph/callGraph.js.map +1 -1
- package/dist/graph/importClassification.d.ts +28 -0
- package/dist/graph/importClassification.d.ts.map +1 -1
- package/dist/graph/importClassification.js +57 -0
- package/dist/graph/importClassification.js.map +1 -1
- package/dist/graph/importGraph.d.ts +38 -0
- package/dist/graph/importGraph.d.ts.map +1 -1
- package/dist/graph/importGraph.js +38 -0
- package/dist/graph/importGraph.js.map +1 -1
- package/dist/graph/outputFormatter.d.ts +12 -4
- package/dist/graph/outputFormatter.d.ts.map +1 -1
- package/dist/graph/outputFormatter.js +12 -62
- package/dist/graph/outputFormatter.js.map +1 -1
- package/dist/hooks/codex.d.ts +8 -1
- package/dist/hooks/codex.d.ts.map +1 -1
- package/dist/hooks/codex.js +7 -0
- package/dist/hooks/codex.js.map +1 -1
- package/dist/hooks/core.d.ts +6 -0
- package/dist/hooks/core.d.ts.map +1 -1
- package/dist/hooks/core.js +6 -1
- package/dist/hooks/core.js.map +1 -1
- package/dist/hooks/cursor.d.ts +11 -1
- package/dist/hooks/cursor.d.ts.map +1 -1
- package/dist/hooks/cursor.js +10 -0
- package/dist/hooks/cursor.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/installConfig.d.ts +23 -5
- package/dist/installConfig.d.ts.map +1 -1
- package/dist/installConfig.js +23 -5
- package/dist/installConfig.js.map +1 -1
- package/dist/installer.d.ts +6 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +6 -0
- package/dist/installer.js.map +1 -1
- package/dist/invariants/ruleEngine.d.ts +3 -0
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +156 -119
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/invariants/ruleValidator.d.ts +3 -0
- package/dist/invariants/ruleValidator.d.ts.map +1 -1
- package/dist/invariants/ruleValidator.js +185 -178
- package/dist/invariants/ruleValidator.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +4 -1
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +8 -19
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/RuntimeManager.d.ts +8 -0
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +55 -5
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +1 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +4 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +37 -3
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +11 -14
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/go/analyzer-darwin-amd64 +0 -0
- package/dist/languages/go/analyzer-darwin-arm64 +0 -0
- package/dist/languages/go/analyzer-linux-amd64 +0 -0
- package/dist/languages/go/analyzer-linux-arm64 +0 -0
- package/dist/languages/go/analyzer-src/analyzer.go +2 -0
- package/dist/languages/go/analyzer-src/dataaccess.go +388 -0
- package/dist/languages/go/analyzer-src/types.go +8 -0
- package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
- package/dist/languages/go/database-packages.json +8 -0
- package/dist/languages/go/goResolution.d.ts +229 -0
- package/dist/languages/go/goResolution.d.ts.map +1 -0
- package/dist/languages/go/goResolution.js +799 -0
- package/dist/languages/go/goResolution.js.map +1 -0
- package/dist/languages/go/stdlib-packages.json +353 -0
- package/dist/languages/go/tenantInputs.d.ts +37 -0
- package/dist/languages/go/tenantInputs.d.ts.map +1 -0
- package/dist/languages/go/tenantInputs.js +121 -0
- package/dist/languages/go/tenantInputs.js.map +1 -0
- package/dist/languages/index.d.ts +1 -0
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +5 -0
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/json/JsonAdapter.d.ts +177 -0
- package/dist/languages/json/JsonAdapter.d.ts.map +1 -0
- package/dist/languages/json/JsonAdapter.js +531 -0
- package/dist/languages/json/JsonAdapter.js.map +1 -0
- package/dist/languages/locationIndex.d.ts +41 -0
- package/dist/languages/locationIndex.d.ts.map +1 -0
- package/dist/languages/locationIndex.js +42 -0
- package/dist/languages/locationIndex.js.map +1 -0
- package/dist/languages/sql/dialectDetection.d.ts +57 -0
- package/dist/languages/sql/dialectDetection.d.ts.map +1 -0
- package/dist/languages/sql/dialectDetection.js +135 -0
- package/dist/languages/sql/dialectDetection.js.map +1 -0
- package/dist/languages/sql/sqlAst.d.ts +494 -0
- package/dist/languages/sql/sqlAst.d.ts.map +1 -0
- package/dist/languages/sql/sqlAst.js +1817 -0
- package/dist/languages/sql/sqlAst.js.map +1 -0
- package/dist/languages/testConventions.d.ts +12 -1
- package/dist/languages/testConventions.d.ts.map +1 -1
- package/dist/languages/testConventions.js +13 -1
- package/dist/languages/testConventions.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/converter.js +2 -2
- package/dist/languages/tree-sitter/converter.js.map +1 -1
- package/dist/languages/tree-sitter/parser.d.ts +3 -4
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +10 -4
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/tree-sitter/rawNode.d.ts +3 -0
- package/dist/languages/tree-sitter/rawNode.d.ts.map +1 -1
- package/dist/languages/tree-sitter/rawNode.js +3 -0
- package/dist/languages/tree-sitter/rawNode.js.map +1 -1
- package/dist/languages/types.d.ts +7 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +36 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +168 -45
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/languages/typescript/database-packages.json +19 -0
- package/dist/ledger.d.ts +118 -2
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +167 -45
- package/dist/ledger.js.map +1 -1
- package/dist/legacyIndexNotice.d.ts +32 -0
- package/dist/legacyIndexNotice.d.ts.map +1 -0
- package/dist/legacyIndexNotice.js +89 -0
- package/dist/legacyIndexNotice.js.map +1 -0
- package/dist/mcp-tools/discoveryQueries.d.ts +55 -0
- package/dist/mcp-tools/discoveryQueries.d.ts.map +1 -0
- package/dist/mcp-tools/discoveryQueries.js +157 -0
- package/dist/mcp-tools/discoveryQueries.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts +9 -123
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +180 -170
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +20 -0
- package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
- package/dist/mcp-tools/whitelistTools.js +20 -0
- package/dist/mcp-tools/whitelistTools.js.map +1 -1
- package/dist/mcp-tools/workflowGuide.d.ts +9 -0
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +13 -5
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts +78 -0
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +203 -340
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.d.ts +2 -0
- package/dist/mcp-ui-simple.d.ts.map +1 -1
- package/dist/mcp-ui-simple.js +12 -5
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +6 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +832 -851
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +29 -33
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +102 -791
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpAutoIndex.d.ts +5 -0
- package/dist/mcpAutoIndex.d.ts.map +1 -1
- package/dist/mcpAutoIndex.js +7 -2
- package/dist/mcpAutoIndex.js.map +1 -1
- package/dist/mcpDiagnostics.d.ts +7 -0
- package/dist/mcpDiagnostics.d.ts.map +1 -1
- package/dist/mcpDiagnostics.js +34 -9
- package/dist/mcpDiagnostics.js.map +1 -1
- package/dist/mcpToolErrors.d.ts +16 -0
- package/dist/mcpToolErrors.d.ts.map +1 -1
- package/dist/mcpToolErrors.js +16 -0
- package/dist/mcpToolErrors.js.map +1 -1
- package/dist/nextFile.d.ts +6 -0
- package/dist/nextFile.d.ts.map +1 -1
- package/dist/nextFile.js +6 -0
- package/dist/nextFile.js.map +1 -1
- package/dist/nextFileIncremental.d.ts +44 -4
- package/dist/nextFileIncremental.d.ts.map +1 -1
- package/dist/nextFileIncremental.js +47 -7
- package/dist/nextFileIncremental.js.map +1 -1
- package/dist/phase/batchFunctions.d.ts +38 -0
- package/dist/phase/batchFunctions.d.ts.map +1 -0
- package/dist/phase/batchFunctions.js +64 -0
- package/dist/phase/batchFunctions.js.map +1 -0
- package/dist/phase/checks.d.ts +39 -0
- package/dist/phase/checks.d.ts.map +1 -0
- package/dist/phase/checks.js +48 -0
- package/dist/phase/checks.js.map +1 -0
- package/dist/phase/codeBlocks.d.ts +25 -0
- package/dist/phase/codeBlocks.d.ts.map +1 -0
- package/dist/phase/codeBlocks.js +268 -0
- package/dist/phase/codeBlocks.js.map +1 -0
- package/dist/phase/colorMath.d.ts +41 -0
- package/dist/phase/colorMath.d.ts.map +1 -0
- package/dist/phase/colorMath.js +109 -0
- package/dist/phase/colorMath.js.map +1 -0
- package/dist/phase/config.d.ts +29 -0
- package/dist/phase/config.d.ts.map +1 -0
- package/dist/phase/config.js +54 -0
- package/dist/phase/config.js.map +1 -0
- package/dist/phase/consumed.d.ts +47 -0
- package/dist/phase/consumed.d.ts.map +1 -0
- package/dist/phase/consumed.js +22 -0
- package/dist/phase/consumed.js.map +1 -0
- package/dist/phase/conventionMining.d.ts +59 -0
- package/dist/phase/conventionMining.d.ts.map +1 -0
- package/dist/phase/conventionMining.js +152 -0
- package/dist/phase/conventionMining.js.map +1 -0
- package/dist/phase/coverage.d.ts +65 -0
- package/dist/phase/coverage.d.ts.map +1 -0
- package/dist/phase/coverage.js +121 -0
- package/dist/phase/coverage.js.map +1 -0
- package/dist/phase/dag.d.ts +49 -0
- package/dist/phase/dag.d.ts.map +1 -0
- package/dist/phase/dag.js +120 -0
- package/dist/phase/dag.js.map +1 -0
- package/dist/phase/dataAccessCalls.d.ts +33 -0
- package/dist/phase/dataAccessCalls.d.ts.map +1 -0
- package/dist/phase/dataAccessCalls.js +42 -0
- package/dist/phase/dataAccessCalls.js.map +1 -0
- package/dist/phase/dataAccessCallsCandidates.d.ts +25 -0
- package/dist/phase/dataAccessCallsCandidates.d.ts.map +1 -0
- package/dist/phase/dataAccessCallsCandidates.js +27 -0
- package/dist/phase/dataAccessCallsCandidates.js.map +1 -0
- package/dist/phase/dynamicSql.d.ts +18 -0
- package/dist/phase/dynamicSql.d.ts.map +1 -0
- package/dist/phase/dynamicSql.js +20 -0
- package/dist/phase/dynamicSql.js.map +1 -0
- package/dist/phase/exportForm.d.ts +23 -0
- package/dist/phase/exportForm.d.ts.map +1 -0
- package/dist/phase/exportForm.js +29 -0
- package/dist/phase/exportForm.js.map +1 -0
- package/dist/phase/exportSymbols.d.ts +21 -0
- package/dist/phase/exportSymbols.d.ts.map +1 -0
- package/dist/phase/exportSymbols.js +28 -0
- package/dist/phase/exportSymbols.js.map +1 -0
- package/dist/phase/fileHeader.d.ts +15 -0
- package/dist/phase/fileHeader.d.ts.map +1 -0
- package/dist/phase/fileHeader.js +33 -0
- package/dist/phase/fileHeader.js.map +1 -0
- package/dist/phase/fileImports.d.ts +39 -0
- package/dist/phase/fileImports.d.ts.map +1 -0
- package/dist/phase/fileImports.js +138 -0
- package/dist/phase/fileImports.js.map +1 -0
- package/dist/phase/fileSymbols.d.ts +25 -0
- package/dist/phase/fileSymbols.d.ts.map +1 -0
- package/dist/phase/fileSymbols.js +302 -0
- package/dist/phase/fileSymbols.js.map +1 -0
- package/dist/phase/functionBodies.d.ts +28 -0
- package/dist/phase/functionBodies.d.ts.map +1 -0
- package/dist/phase/functionBodies.js +67 -0
- package/dist/phase/functionBodies.js.map +1 -0
- package/dist/phase/functionIndex.d.ts +28 -0
- package/dist/phase/functionIndex.d.ts.map +1 -0
- package/dist/phase/functionIndex.js +152 -0
- package/dist/phase/functionIndex.js.map +1 -0
- package/dist/phase/goFunctionAnalysis.d.ts +47 -0
- package/dist/phase/goFunctionAnalysis.d.ts.map +1 -0
- package/dist/phase/goFunctionAnalysis.js +291 -0
- package/dist/phase/goFunctionAnalysis.js.map +1 -0
- package/dist/phase/goFunctions.d.ts +38 -0
- package/dist/phase/goFunctions.d.ts.map +1 -0
- package/dist/phase/goFunctions.js +154 -0
- package/dist/phase/goFunctions.js.map +1 -0
- package/dist/phase/goImports.d.ts +29 -0
- package/dist/phase/goImports.d.ts.map +1 -0
- package/dist/phase/goImports.js +68 -0
- package/dist/phase/goImports.js.map +1 -0
- package/dist/phase/goPackageBindings.d.ts +22 -0
- package/dist/phase/goPackageBindings.d.ts.map +1 -0
- package/dist/phase/goPackageBindings.js +30 -0
- package/dist/phase/goPackageBindings.js.map +1 -0
- package/dist/phase/goSwitches.d.ts +28 -0
- package/dist/phase/goSwitches.d.ts.map +1 -0
- package/dist/phase/goSwitches.js +52 -0
- package/dist/phase/goSwitches.js.map +1 -0
- package/dist/phase/importForm.d.ts +20 -0
- package/dist/phase/importForm.d.ts.map +1 -0
- package/dist/phase/importForm.js +27 -0
- package/dist/phase/importForm.js.map +1 -0
- package/dist/phase/importSpecifiers.d.ts +20 -0
- package/dist/phase/importSpecifiers.d.ts.map +1 -0
- package/dist/phase/importSpecifiers.js +30 -0
- package/dist/phase/importSpecifiers.js.map +1 -0
- package/dist/phase/imports.d.ts +18 -0
- package/dist/phase/imports.d.ts.map +1 -0
- package/dist/phase/imports.js +25 -0
- package/dist/phase/imports.js.map +1 -0
- package/dist/phase/jsonDocument.d.ts +26 -0
- package/dist/phase/jsonDocument.d.ts.map +1 -0
- package/dist/phase/jsonDocument.js +36 -0
- package/dist/phase/jsonDocument.js.map +1 -0
- package/dist/phase/localBinding.d.ts +33 -0
- package/dist/phase/localBinding.d.ts.map +1 -0
- package/dist/phase/localBinding.js +208 -0
- package/dist/phase/localBinding.js.map +1 -0
- package/dist/phase/loopQueries.d.ts +31 -0
- package/dist/phase/loopQueries.d.ts.map +1 -0
- package/dist/phase/loopQueries.js +44 -0
- package/dist/phase/loopQueries.js.map +1 -0
- package/dist/phase/loopQueryCandidates.d.ts +26 -0
- package/dist/phase/loopQueryCandidates.d.ts.map +1 -0
- package/dist/phase/loopQueryCandidates.js +28 -0
- package/dist/phase/loopQueryCandidates.js.map +1 -0
- package/dist/phase/oracles.d.ts +216 -0
- package/dist/phase/oracles.d.ts.map +1 -0
- package/dist/phase/oracles.js +484 -0
- package/dist/phase/oracles.js.map +1 -0
- package/dist/phase/phaseModel.d.ts +190 -0
- package/dist/phase/phaseModel.d.ts.map +1 -0
- package/dist/phase/phaseModel.js +468 -0
- package/dist/phase/phaseModel.js.map +1 -0
- package/dist/phase/producers.d.ts +450 -0
- package/dist/phase/producers.d.ts.map +1 -0
- package/dist/phase/producers.js +1027 -0
- package/dist/phase/producers.js.map +1 -0
- package/dist/phase/querySiteCandidates.d.ts +27 -0
- package/dist/phase/querySiteCandidates.d.ts.map +1 -0
- package/dist/phase/querySiteCandidates.js +66 -0
- package/dist/phase/querySiteCandidates.js.map +1 -0
- package/dist/phase/querySites.d.ts +30 -0
- package/dist/phase/querySites.d.ts.map +1 -0
- package/dist/phase/querySites.js +93 -0
- package/dist/phase/querySites.js.map +1 -0
- package/dist/phase/reachability.d.ts +61 -0
- package/dist/phase/reachability.d.ts.map +1 -0
- package/dist/phase/reachability.js +104 -0
- package/dist/phase/reachability.js.map +1 -0
- package/dist/phase/reactComponents.d.ts +33 -0
- package/dist/phase/reactComponents.d.ts.map +1 -0
- package/dist/phase/reactComponents.js +47 -0
- package/dist/phase/reactComponents.js.map +1 -0
- package/dist/phase/receiverActivity.d.ts +35 -0
- package/dist/phase/receiverActivity.d.ts.map +1 -0
- package/dist/phase/receiverActivity.js +66 -0
- package/dist/phase/receiverActivity.js.map +1 -0
- package/dist/phase/receiverConsumers.d.ts +200 -0
- package/dist/phase/receiverConsumers.d.ts.map +1 -0
- package/dist/phase/receiverConsumers.js +1025 -0
- package/dist/phase/receiverConsumers.js.map +1 -0
- package/dist/phase/receiverProvenance.d.ts +97 -0
- package/dist/phase/receiverProvenance.d.ts.map +1 -0
- package/dist/phase/receiverProvenance.js +337 -0
- package/dist/phase/receiverProvenance.js.map +1 -0
- package/dist/phase/routing.d.ts +47 -0
- package/dist/phase/routing.d.ts.map +1 -0
- package/dist/phase/routing.js +62 -0
- package/dist/phase/routing.js.map +1 -0
- package/dist/phase/rules/conventions.d.ts +61 -0
- package/dist/phase/rules/conventions.d.ts.map +1 -0
- package/dist/phase/rules/conventions.js +403 -0
- package/dist/phase/rules/conventions.js.map +1 -0
- package/dist/phase/rules/crossDomain.d.ts +102 -0
- package/dist/phase/rules/crossDomain.d.ts.map +1 -0
- package/dist/phase/rules/crossDomain.js +918 -0
- package/dist/phase/rules/crossDomain.js.map +1 -0
- package/dist/phase/rules/dataAccess.d.ts +62 -0
- package/dist/phase/rules/dataAccess.d.ts.map +1 -0
- package/dist/phase/rules/dataAccess.js +480 -0
- package/dist/phase/rules/dataAccess.js.map +1 -0
- package/dist/phase/rules/dependencyGraph.d.ts +44 -0
- package/dist/phase/rules/dependencyGraph.d.ts.map +1 -0
- package/dist/phase/rules/dependencyGraph.js +260 -0
- package/dist/phase/rules/dependencyGraph.js.map +1 -0
- package/dist/phase/rules/documentation.d.ts +45 -0
- package/dist/phase/rules/documentation.d.ts.map +1 -0
- package/dist/phase/rules/documentation.js +340 -0
- package/dist/phase/rules/documentation.js.map +1 -0
- package/dist/phase/rules/dry.d.ts +109 -0
- package/dist/phase/rules/dry.d.ts.map +1 -0
- package/dist/phase/rules/dry.js +803 -0
- package/dist/phase/rules/dry.js.map +1 -0
- package/dist/phase/rules/fileDocumentation.d.ts +36 -0
- package/dist/phase/rules/fileDocumentation.d.ts.map +1 -0
- package/dist/phase/rules/fileDocumentation.js +134 -0
- package/dist/phase/rules/fileDocumentation.js.map +1 -0
- package/dist/phase/rules/functionBodies.d.ts +33 -0
- package/dist/phase/rules/functionBodies.d.ts.map +1 -0
- package/dist/phase/rules/functionBodies.js +71 -0
- package/dist/phase/rules/functionBodies.js.map +1 -0
- package/dist/phase/rules/goRules.d.ts +49 -0
- package/dist/phase/rules/goRules.d.ts.map +1 -0
- package/dist/phase/rules/goRules.js +209 -0
- package/dist/phase/rules/goRules.js.map +1 -0
- package/dist/phase/rules/querySites.d.ts +29 -0
- package/dist/phase/rules/querySites.d.ts.map +1 -0
- package/dist/phase/rules/querySites.js +94 -0
- package/dist/phase/rules/querySites.js.map +1 -0
- package/dist/phase/rules/react.d.ts +38 -0
- package/dist/phase/rules/react.d.ts.map +1 -0
- package/dist/phase/rules/react.js +202 -0
- package/dist/phase/rules/react.js.map +1 -0
- package/dist/phase/rules/registry.d.ts +170 -0
- package/dist/phase/rules/registry.d.ts.map +1 -0
- package/dist/phase/rules/registry.js +103 -0
- package/dist/phase/rules/registry.js.map +1 -0
- package/dist/phase/rules/schema.d.ts +79 -0
- package/dist/phase/rules/schema.d.ts.map +1 -0
- package/dist/phase/rules/schema.js +273 -0
- package/dist/phase/rules/schema.js.map +1 -0
- package/dist/phase/rules/schemaJson.d.ts +32 -0
- package/dist/phase/rules/schemaJson.d.ts.map +1 -0
- package/dist/phase/rules/schemaJson.js +81 -0
- package/dist/phase/rules/schemaJson.js.map +1 -0
- package/dist/phase/rules/schemaValidator.d.ts +38 -0
- package/dist/phase/rules/schemaValidator.d.ts.map +1 -0
- package/dist/phase/rules/schemaValidator.js +107 -0
- package/dist/phase/rules/schemaValidator.js.map +1 -0
- package/dist/phase/rules/secrets.d.ts +34 -0
- package/dist/phase/rules/secrets.d.ts.map +1 -0
- package/dist/phase/rules/secrets.js +208 -0
- package/dist/phase/rules/secrets.js.map +1 -0
- package/dist/phase/rules/security.d.ts +30 -0
- package/dist/phase/rules/security.d.ts.map +1 -0
- package/dist/phase/rules/security.js +85 -0
- package/dist/phase/rules/security.js.map +1 -0
- package/dist/phase/rules/securityDefects.d.ts +37 -0
- package/dist/phase/rules/securityDefects.d.ts.map +1 -0
- package/dist/phase/rules/securityDefects.js +167 -0
- package/dist/phase/rules/securityDefects.js.map +1 -0
- package/dist/phase/rules/solid.d.ts +61 -0
- package/dist/phase/rules/solid.d.ts.map +1 -0
- package/dist/phase/rules/solid.js +562 -0
- package/dist/phase/rules/solid.js.map +1 -0
- package/dist/phase/rules/styles.d.ts +77 -0
- package/dist/phase/rules/styles.d.ts.map +1 -0
- package/dist/phase/rules/styles.js +868 -0
- package/dist/phase/rules/styles.js.map +1 -0
- package/dist/phase/rules/unreferencedModule.d.ts +26 -0
- package/dist/phase/rules/unreferencedModule.d.ts.map +1 -0
- package/dist/phase/rules/unreferencedModule.js +61 -0
- package/dist/phase/rules/unreferencedModule.js.map +1 -0
- package/dist/phase/runner.d.ts +549 -0
- package/dist/phase/runner.d.ts.map +1 -0
- package/dist/phase/runner.js +1098 -0
- package/dist/phase/runner.js.map +1 -0
- package/dist/phase/schemaCode.d.ts +34 -0
- package/dist/phase/schemaCode.d.ts.map +1 -0
- package/dist/phase/schemaCode.js +53 -0
- package/dist/phase/schemaCode.js.map +1 -0
- package/dist/phase/schemaObjects.d.ts +37 -0
- package/dist/phase/schemaObjects.d.ts.map +1 -0
- package/dist/phase/schemaObjects.js +139 -0
- package/dist/phase/schemaObjects.js.map +1 -0
- package/dist/phase/schemaUsage.d.ts +30 -0
- package/dist/phase/schemaUsage.d.ts.map +1 -0
- package/dist/phase/schemaUsage.js +99 -0
- package/dist/phase/schemaUsage.js.map +1 -0
- package/dist/phase/schemaUsageCandidates.d.ts +32 -0
- package/dist/phase/schemaUsageCandidates.d.ts.map +1 -0
- package/dist/phase/schemaUsageCandidates.js +165 -0
- package/dist/phase/schemaUsageCandidates.js.map +1 -0
- package/dist/phase/schemaValidations.d.ts +34 -0
- package/dist/phase/schemaValidations.d.ts.map +1 -0
- package/dist/phase/schemaValidations.js +53 -0
- package/dist/phase/schemaValidations.js.map +1 -0
- package/dist/phase/secretCandidates.d.ts +28 -0
- package/dist/phase/secretCandidates.d.ts.map +1 -0
- package/dist/phase/secretCandidates.js +132 -0
- package/dist/phase/secretCandidates.js.map +1 -0
- package/dist/phase/securityCandidates.d.ts +29 -0
- package/dist/phase/securityCandidates.d.ts.map +1 -0
- package/dist/phase/securityCandidates.js +469 -0
- package/dist/phase/securityCandidates.js.map +1 -0
- package/dist/phase/seeded-defects.d.ts +18 -0
- package/dist/phase/seeded-defects.d.ts.map +1 -0
- package/dist/phase/seeded-defects.js +61 -0
- package/dist/phase/seeded-defects.js.map +1 -0
- package/dist/phase/stringLiterals.d.ts +24 -0
- package/dist/phase/stringLiterals.d.ts.map +1 -0
- package/dist/phase/stringLiterals.js +35 -0
- package/dist/phase/stringLiterals.js.map +1 -0
- package/dist/phase/stylesCss.d.ts +25 -0
- package/dist/phase/stylesCss.d.ts.map +1 -0
- package/dist/phase/stylesCss.js +34 -0
- package/dist/phase/stylesCss.js.map +1 -0
- package/dist/phase/stylesMarkup.d.ts +31 -0
- package/dist/phase/stylesMarkup.d.ts.map +1 -0
- package/dist/phase/stylesMarkup.js +48 -0
- package/dist/phase/stylesMarkup.js.map +1 -0
- package/dist/phase/stylesSource.d.ts +36 -0
- package/dist/phase/stylesSource.d.ts.map +1 -0
- package/dist/phase/stylesSource.js +50 -0
- package/dist/phase/stylesSource.js.map +1 -0
- package/dist/phase/typeDeclarations.d.ts +33 -0
- package/dist/phase/typeDeclarations.d.ts.map +1 -0
- package/dist/phase/typeDeclarations.js +76 -0
- package/dist/phase/typeDeclarations.js.map +1 -0
- package/dist/phase/types.d.ts +2191 -0
- package/dist/phase/types.d.ts.map +1 -0
- package/dist/phase/types.js +27 -0
- package/dist/phase/types.js.map +1 -0
- package/dist/phase/withinFileProvenance.d.ts +50 -0
- package/dist/phase/withinFileProvenance.d.ts.map +1 -0
- package/dist/phase/withinFileProvenance.js +112 -0
- package/dist/phase/withinFileProvenance.js.map +1 -0
- package/dist/pipeline.d.ts +37 -30
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +131 -326
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +104 -21
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +1145 -988
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/pipelineTypes.d.ts +21 -0
- package/dist/pipelineTypes.d.ts.map +1 -1
- package/dist/pipelineTypes.js +21 -10
- package/dist/pipelineTypes.js.map +1 -1
- package/dist/presets/presets.d.ts +9 -2
- package/dist/presets/presets.d.ts.map +1 -1
- package/dist/presets/presets.js +15 -26
- package/dist/presets/presets.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts +14 -3
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +14 -3
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts +5 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +5 -1
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts +9 -2
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +19 -5
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/reportGenerator.d.ts +9 -2
- package/dist/reporting/reportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.js +9 -2
- package/dist/reporting/reportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts +14 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +14 -0
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/reporting/sizeDistribution.d.ts +19 -2
- package/dist/reporting/sizeDistribution.d.ts.map +1 -1
- package/dist/reporting/sizeDistribution.js +23 -5
- package/dist/reporting/sizeDistribution.js.map +1 -1
- package/dist/ruleAliases.d.ts +6 -8
- package/dist/ruleAliases.d.ts.map +1 -1
- package/dist/ruleAliases.js +29 -4
- package/dist/ruleAliases.js.map +1 -1
- package/dist/search/QueryParser.d.ts +1 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +32 -91
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts +19 -20
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +214 -194
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/services/ProjectTaskRepository.d.ts +65 -3
- package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
- package/dist/services/ProjectTaskRepository.js +94 -25
- package/dist/services/ProjectTaskRepository.js.map +1 -1
- package/dist/services/auditJobService.d.ts +17 -1
- package/dist/services/auditJobService.d.ts.map +1 -1
- package/dist/services/auditJobService.js +16 -0
- package/dist/services/auditJobService.js.map +1 -1
- package/dist/services/fileAccounting.d.ts +43 -5
- package/dist/services/fileAccounting.d.ts.map +1 -1
- package/dist/services/fileAccounting.js +48 -9
- package/dist/services/fileAccounting.js.map +1 -1
- package/dist/services/projectTaskHelpers.d.ts +45 -0
- package/dist/services/projectTaskHelpers.d.ts.map +1 -1
- package/dist/services/projectTaskHelpers.js +45 -0
- package/dist/services/projectTaskHelpers.js.map +1 -1
- package/dist/services/whitelistService.d.ts +40 -5
- package/dist/services/whitelistService.d.ts.map +1 -1
- package/dist/services/whitelistService.js +87 -46
- package/dist/services/whitelistService.js.map +1 -1
- package/dist/sqlite/betterSqlite3.d.ts +35 -1
- package/dist/sqlite/betterSqlite3.d.ts.map +1 -1
- package/dist/sqlite/betterSqlite3.js +35 -1
- package/dist/sqlite/betterSqlite3.js.map +1 -1
- package/dist/sqlite/driver.d.ts +23 -0
- package/dist/sqlite/driver.d.ts.map +1 -1
- package/dist/sqlite/driver.js +23 -0
- package/dist/sqlite/driver.js.map +1 -1
- package/dist/sqlite/nodeSqlite.d.ts +35 -1
- package/dist/sqlite/nodeSqlite.d.ts.map +1 -1
- package/dist/sqlite/nodeSqlite.js +35 -1
- package/dist/sqlite/nodeSqlite.js.map +1 -1
- package/dist/structuralSignature.d.ts +9 -0
- package/dist/structuralSignature.d.ts.map +1 -1
- package/dist/structuralSignature.js +9 -0
- package/dist/structuralSignature.js.map +1 -1
- package/dist/styles/cssAstExtractor.d.ts +15 -0
- package/dist/styles/cssAstExtractor.d.ts.map +1 -1
- package/dist/styles/cssAstExtractor.js +15 -0
- package/dist/styles/cssAstExtractor.js.map +1 -1
- package/dist/styles/normalizer.d.ts +6 -0
- package/dist/styles/normalizer.d.ts.map +1 -1
- package/dist/styles/normalizer.js +6 -0
- package/dist/styles/normalizer.js.map +1 -1
- package/dist/styles/styleExtractor.d.ts +29 -2
- package/dist/styles/styleExtractor.d.ts.map +1 -1
- package/dist/styles/styleExtractor.js +67 -48
- package/dist/styles/styleExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts +8 -0
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +135 -138
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindConfigLoader.d.ts +12 -0
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
- package/dist/styles/tailwindConfigLoader.js +12 -0
- package/dist/styles/tailwindConfigLoader.js.map +1 -1
- package/dist/styles/tailwindExpander.d.ts +3 -0
- package/dist/styles/tailwindExpander.d.ts.map +1 -1
- package/dist/styles/tailwindExpander.js +8 -4
- package/dist/styles/tailwindExpander.js.map +1 -1
- package/dist/styles/tailwindProbe.d.ts +9 -0
- package/dist/styles/tailwindProbe.d.ts.map +1 -1
- package/dist/styles/tailwindProbe.js +181 -166
- package/dist/styles/tailwindProbe.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts +13 -0
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +13 -0
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/symbols.d.ts +13 -11
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +14 -21
- package/dist/symbols.js.map +1 -1
- package/dist/telemetry.d.ts +31 -3
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +31 -3
- package/dist/telemetry.js.map +1 -1
- package/dist/tool-registry.d.ts +37 -1
- package/dist/tool-registry.d.ts.map +1 -1
- package/dist/tool-registry.js +37 -1
- package/dist/tool-registry.js.map +1 -1
- package/dist/types.d.ts +106 -92
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -16
- package/dist/types.js.map +1 -1
- package/dist/ui/InteractivePrompts.d.ts +12 -0
- package/dist/ui/InteractivePrompts.d.ts.map +1 -1
- package/dist/ui/InteractivePrompts.js +12 -0
- package/dist/ui/InteractivePrompts.js.map +1 -1
- package/dist/utils/astParser.d.ts +15 -0
- package/dist/utils/astParser.d.ts.map +1 -1
- package/dist/utils/astParser.js +15 -0
- package/dist/utils/astParser.js.map +1 -1
- package/dist/utils/astUtils.d.ts +60 -0
- package/dist/utils/astUtils.d.ts.map +1 -1
- package/dist/utils/astUtils.js +350 -362
- package/dist/utils/astUtils.js.map +1 -1
- package/dist/utils/contentHash.d.ts +22 -0
- package/dist/utils/contentHash.d.ts.map +1 -0
- package/dist/utils/contentHash.js +26 -0
- package/dist/utils/contentHash.js.map +1 -0
- package/dist/utils/dependencyExtractor.d.ts +28 -0
- package/dist/utils/dependencyExtractor.d.ts.map +1 -1
- package/dist/utils/dependencyExtractor.js +28 -1
- package/dist/utils/dependencyExtractor.js.map +1 -1
- package/dist/utils/errorMessage.d.ts +10 -0
- package/dist/utils/errorMessage.d.ts.map +1 -0
- package/dist/utils/errorMessage.js +12 -0
- package/dist/utils/errorMessage.js.map +1 -0
- package/dist/utils/fileDiscovery.d.ts +61 -2
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +131 -23
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/reactDetection.d.ts +34 -5
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +73 -52
- package/dist/utils/reactDetection.js.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/workers/auditWorker.js +7 -3
- package/dist/workers/auditWorker.js.map +1 -1
- package/examples/.auditrc.example.json +0 -8
- package/examples/.auditrc.json +0 -11
- package/examples/.code-auditor.json +0 -2
- package/examples/nextjs.auditrc.json +0 -8
- package/examples/node-api.auditrc.json +0 -7
- package/examples/react.auditrc.json +0 -6
- package/examples/tolerant-solid.auditrc.json +0 -5
- package/package.json +14 -8
- package/plugin/.claude-plugin/manifest.json +16 -0
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +6 -2
- package/plugin/scripts/hook-audit.sh +41 -14
- package/plugin/scripts/hook-common.sh +50 -5
- package/plugin/scripts/hook-drift-check.sh +72 -0
- package/plugin/scripts/hook-self-audit.sh +17 -12
- package/plugin/skills/code-auditor/SKILL.md +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,1817 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL as a parsed format — the AST seam.
|
|
3
|
+
*
|
|
4
|
+
* Spec 70 R1: a SQL grammar joins the shipped grammars, SQL strings reaching a
|
|
5
|
+
* data-access call site are parsed into an AST like any other format, and the
|
|
6
|
+
* dialect is a *closed decision* — one named grammar, not a fallback ladder.
|
|
7
|
+
* Here that grammar is `node-sql-parser`; the dialect is a required parameter
|
|
8
|
+
* (the corpus's named {@link Dialect} discriminant, from the discovery-query
|
|
9
|
+
* registry), never a hardcoded constant — guessing `sqlite` on a Postgres repo
|
|
10
|
+
* would be a silent wrong answer. It is pure JavaScript (no WASM, no two-phase
|
|
11
|
+
* init), so it loads synchronously through the ordinary dependency path rather
|
|
12
|
+
* than the `initParsers()` WASM loader.
|
|
13
|
+
*
|
|
14
|
+
* The seam is {@link parseSql}: `{ ok: true, ast }` or `{ ok: false, reason }`.
|
|
15
|
+
* Every rule that reads SQL facts gets an AST or it gets nothing. There is no
|
|
16
|
+
* regex path to fall back to — a string the grammar cannot parse is a parse
|
|
17
|
+
* failure, and the caller turns that into `cannot-fire` with the reason. The
|
|
18
|
+
* twelve regex sites in the security family convert by consuming the helpers
|
|
19
|
+
* below (statement kind, relations, where facts, tenant predicate, insert
|
|
20
|
+
* columns, DDL facts), each of which walks the AST rather than scanning text.
|
|
21
|
+
*
|
|
22
|
+
* `node-sql-parser` is a CommonJS module; the default import is required in ESM.
|
|
23
|
+
*/
|
|
24
|
+
import pkg from 'node-sql-parser';
|
|
25
|
+
const { Parser } = pkg;
|
|
26
|
+
const parser = new Parser();
|
|
27
|
+
/**
|
|
28
|
+
* The named default dialect a caller parses under when detection names none.
|
|
29
|
+
*
|
|
30
|
+
* Spec 70 R2: parsing a SQL literal does not require *proving* the site's
|
|
31
|
+
* dialect first — one grammar is named, with its dialect stated, and the parse
|
|
32
|
+
* is attempted under it. When {@link detectDialect} returns null (a repo names
|
|
33
|
+
* no driver, or names several), the caller still attempts the parse under this
|
|
34
|
+
* default rather than abstaining before it has looked. The default is sqlite:
|
|
35
|
+
* node-sql-parser's sqlite grammar is the most permissive, and the rest of this
|
|
36
|
+
* module already standardizes on "the sqlite grammar" as the baseline (see the
|
|
37
|
+
* `truncateConflictClause` / `ON CONFLICT` notes below).
|
|
38
|
+
*
|
|
39
|
+
* The honesty rule is not weakened, it moves: a literal that *parses* under the
|
|
40
|
+
* default yields facts; a literal that *fails* is reported `cannot-fire` with a
|
|
41
|
+
* reason naming **both** the parse failure and the fact that the dialect is
|
|
42
|
+
* undetermined (so the failure may be dialect-specific syntax), never a silent
|
|
43
|
+
* empty fact set.
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_SQL_DIALECT = 'sqlite';
|
|
46
|
+
/** Map a node-sql-parser statement `type` to the closed {@link SqlStatementKind}. */
|
|
47
|
+
function statementKind(type) {
|
|
48
|
+
switch (type) {
|
|
49
|
+
case 'select': return 'select';
|
|
50
|
+
case 'insert': return 'insert';
|
|
51
|
+
case 'replace': return 'replace';
|
|
52
|
+
case 'update': return 'update';
|
|
53
|
+
case 'delete': return 'delete';
|
|
54
|
+
case 'create': return 'create';
|
|
55
|
+
case 'alter': return 'alter';
|
|
56
|
+
case 'drop': return 'drop';
|
|
57
|
+
case 'use': return 'use';
|
|
58
|
+
default: return 'other';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** DDL statement kinds — schema definition (CREATE/ALTER/DROP/USE), not a
|
|
62
|
+
* row-level data-access query. Schema is extracted separately by the `.sql`
|
|
63
|
+
* visitor and Durable Object DDL (`pipelineAdapters.ts`); a data-access call is
|
|
64
|
+
* a DML query or an ORM operation, and a DDL literal must not surface as one. */
|
|
65
|
+
export function isDdlStatementKind(kind) {
|
|
66
|
+
return kind === 'create' || kind === 'alter' || kind === 'drop' || kind === 'use';
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The standalone transaction-control statements the sqlite grammar does not
|
|
70
|
+
* accept, recognized — not parsed — as SQL. A `BEGIN`/`COMMIT`/`ROLLBACK` is
|
|
71
|
+
* unambiguous SQL with no data-access facts (no relations, no WHERE, not a
|
|
72
|
+
* write), so it proves its receiver is a handle and contributes nothing to any
|
|
73
|
+
* table/filter fact. Recognized via a closed set, case-insensitively, with an
|
|
74
|
+
* optional trailing `;`.
|
|
75
|
+
*/
|
|
76
|
+
const TRANSACTION_CONTROL = new Set([
|
|
77
|
+
'begin',
|
|
78
|
+
'begin transaction',
|
|
79
|
+
'begin deferred',
|
|
80
|
+
'begin immediate',
|
|
81
|
+
'begin exclusive',
|
|
82
|
+
'commit',
|
|
83
|
+
'commit transaction',
|
|
84
|
+
'end',
|
|
85
|
+
'end transaction',
|
|
86
|
+
'rollback',
|
|
87
|
+
'rollback transaction',
|
|
88
|
+
]);
|
|
89
|
+
/** A synthetic statement for recognized transaction control. The AST walkers
|
|
90
|
+
* treat an unknown statement type as "no relations / no where / not a write",
|
|
91
|
+
* which is exactly the facts a BEGIN/COMMIT/ROLLBACK has. */
|
|
92
|
+
const TRANSACTION_MARKER = { type: 'transaction' };
|
|
93
|
+
/** True when `trimmed` is a standalone transaction-control statement. */
|
|
94
|
+
function isStandaloneTransactionControl(trimmed) {
|
|
95
|
+
const t = trimmed.replace(/;\s*$/, '').trim().toLowerCase();
|
|
96
|
+
return TRANSACTION_CONTROL.has(t);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Migration-file statements that name no *stored* table — recognized, not parsed,
|
|
100
|
+
* as SQL. A `PRAGMA` / `DROP INDEX` / `DROP VIEW` / `DROP TRIGGER` / `CREATE VIEW`
|
|
101
|
+
* / `CREATE TRIGGER` declares no table the rules track (an index/view/trigger is
|
|
102
|
+
* not a stored table, and a PRAGMA is a connection setting), so a migration's
|
|
103
|
+
* `PRAGMA foreign_keys=OFF` contributes nothing to any table/filter fact — the
|
|
104
|
+
* same "recognized, no facts" move as transaction control. Recognition is
|
|
105
|
+
* DDL-path only (in {@link parseSqlProgramTolerant}), never in {@link parseSql}:
|
|
106
|
+
* the per-call-site path must NOT recognize a `PRAGMA`, because
|
|
107
|
+
* `PRAGMA table_info(x)` names `x` and a caller reading "no tables" would take
|
|
108
|
+
* that as clean (an unknown/stale-table miss). Of these, some the sqlite grammar
|
|
109
|
+
* *rejects* (`PRAGMA`, `DROP INDEX`, `DROP TRIGGER`, a compound
|
|
110
|
+
* `CREATE VIEW … UNION ALL`) and recognition avoids a spurious `cannot-fire`; the
|
|
111
|
+
* rest (`DROP VIEW`, a simple `CREATE VIEW`, `CREATE TRIGGER`) actually parse,
|
|
112
|
+
* and are recognized anyway so the DDL table walker never reads a view/trigger
|
|
113
|
+
* name as a stored table. `DROP TABLE` is deliberately absent: it *does* name a
|
|
114
|
+
* stored table the lifecycle rules track, and the grammar parses it. Recognized
|
|
115
|
+
* via a closed keyword set, case-insensitively, with an optional trailing `;`.
|
|
116
|
+
*/
|
|
117
|
+
const NO_FACTS_DDL_PREFIXES = ['pragma', 'drop index', 'drop view', 'drop trigger', 'create view', 'create trigger'];
|
|
118
|
+
/** A synthetic statement for recognized no-fact DDL. The AST walkers treat an
|
|
119
|
+
* unknown statement type as "no relations / no where / not a write", which is
|
|
120
|
+
* exactly the facts a PRAGMA / DROP INDEX / DROP VIEW / DROP TRIGGER has. */
|
|
121
|
+
const NO_FACTS_MARKER = { type: 'no-facts' };
|
|
122
|
+
function isNoTableFactsDdl(trimmed) {
|
|
123
|
+
const t = trimmed.replace(/;\s*$/, '').trim().toLowerCase();
|
|
124
|
+
for (const p of NO_FACTS_DDL_PREFIXES) {
|
|
125
|
+
if (!t.startsWith(p))
|
|
126
|
+
continue;
|
|
127
|
+
const after = t[p.length] ?? '';
|
|
128
|
+
if (t.length === p.length || /\s/.test(after))
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Rewrite D1-style numbered positional parameters (`?1`, `?2`, …) to the bare
|
|
135
|
+
* `?` the sqlite grammar accepts. Parameter numbering is an ordering hint, not a
|
|
136
|
+
* fact: it does not affect which tables are read or whether a tenant predicate
|
|
137
|
+
* exists, so the rewrite is lossless for every consumer of this module. A `?`
|
|
138
|
+
* inside a single-quoted string literal is left untouched (that `?` is literal
|
|
139
|
+
* text, not a placeholder); quoted identifiers (`"…"`, `` `…` ``) are also
|
|
140
|
+
* skipped so an identifier containing `?` is not rewritten. A doubled quote
|
|
141
|
+
* (`''` inside a string) stays inside the region.
|
|
142
|
+
* @param sql The SQL text to rewrite.
|
|
143
|
+
* @returns The SQL with D1-style numbered parameters (`?1`) rewritten to bare `?`.
|
|
144
|
+
*/
|
|
145
|
+
export function normalizePositionalParams(sql) {
|
|
146
|
+
let out = '';
|
|
147
|
+
let i = 0;
|
|
148
|
+
let quote = null;
|
|
149
|
+
while (i < sql.length) {
|
|
150
|
+
const ch = sql[i];
|
|
151
|
+
if (quote !== null) {
|
|
152
|
+
if (ch === quote) {
|
|
153
|
+
if (sql[i + 1] === quote) {
|
|
154
|
+
out += ch + ch;
|
|
155
|
+
i += 2;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
quote = null;
|
|
159
|
+
}
|
|
160
|
+
out += ch;
|
|
161
|
+
i++;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
165
|
+
quote = ch;
|
|
166
|
+
out += ch;
|
|
167
|
+
i++;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (ch === '?' && /\d/.test(sql[i + 1] ?? '')) {
|
|
171
|
+
let j = i + 1;
|
|
172
|
+
while (j < sql.length && /\d/.test(sql[j]))
|
|
173
|
+
j++;
|
|
174
|
+
out += '?';
|
|
175
|
+
i = j;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
out += ch;
|
|
179
|
+
i++;
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Rewrite a JavaScript template substitution (`${…}`) to the SQL placeholder
|
|
185
|
+
* `?`. A `${…}` in the SQL text is a host-language interpolation — a value,
|
|
186
|
+
* identifier, or table name filled in at query-composition time — which the SQL
|
|
187
|
+
* grammar cannot tokenize (`${` is not a SQL spelling). A value-position
|
|
188
|
+
* substitution (`WHERE id = ${id}`, `SELECT ${cols} …`) is exactly a parameter,
|
|
189
|
+
* so `?` is a lossless rewrite: it preserves every table/filter fact the AST
|
|
190
|
+
* walk reads. A *table-position* substitution (`FROM ${table}`) rewrites to
|
|
191
|
+
* `FROM ?`, which the grammar rejects — the honest `cannot-fire`, because a
|
|
192
|
+
* dynamic table name is genuinely unreadable. A `${…}` inside a quoted string
|
|
193
|
+
* or quoted identifier is literal text the grammar already accepts, so it is
|
|
194
|
+
* left untouched (rewriting it would corrupt a string literal that happens to
|
|
195
|
+
* contain `$`).
|
|
196
|
+
* @param sql The SQL text to rewrite.
|
|
197
|
+
* @returns The SQL with `${…}` template substitutions rewritten to `?` placeholders.
|
|
198
|
+
*/
|
|
199
|
+
export function normalizeTemplateSubstitutions(sql) {
|
|
200
|
+
let out = '';
|
|
201
|
+
let i = 0;
|
|
202
|
+
let quote = null;
|
|
203
|
+
while (i < sql.length) {
|
|
204
|
+
const ch = sql[i];
|
|
205
|
+
if (quote !== null) {
|
|
206
|
+
if (ch === quote) {
|
|
207
|
+
if (sql[i + 1] === quote) {
|
|
208
|
+
out += ch + ch;
|
|
209
|
+
i += 2;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
quote = null;
|
|
213
|
+
}
|
|
214
|
+
out += ch;
|
|
215
|
+
i++;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
219
|
+
quote = ch;
|
|
220
|
+
out += ch;
|
|
221
|
+
i++;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (ch === '$' && sql[i + 1] === '{') {
|
|
225
|
+
// Skip to the matching `}` (no nested braces in a substitution).
|
|
226
|
+
let j = i + 2;
|
|
227
|
+
while (j < sql.length && sql[j] !== '}')
|
|
228
|
+
j++;
|
|
229
|
+
out += '?';
|
|
230
|
+
i = j < sql.length ? j + 1 : sql.length;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
out += ch;
|
|
234
|
+
i++;
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Rewrite sqlite's length-qualified `TEXT(N)` type spelling to `VARCHAR(N)`.
|
|
240
|
+
* Drizzle's sqlite dialect emits `text(N)` for string columns, and
|
|
241
|
+
* node-sql-parser's sqlite grammar accepts the length form only for `varchar(N)`
|
|
242
|
+
* — it rejects `text(N)` even though sqlite itself accepts it. SQLite gives
|
|
243
|
+
* `TEXT(N)` and `VARCHAR(N)` the same TEXT affinity, so the rewrite is lossless
|
|
244
|
+
* for every fact an AST walk reads (table/column names, types, declared lengths)
|
|
245
|
+
* and recovers a statement that would otherwise report a spurious `cannot-fire`.
|
|
246
|
+
* Only the `text` keyword *immediately followed by `(`* is rewritten — a bare
|
|
247
|
+
* `text` (no length), a longer identifier (`textual`), and any occurrence inside
|
|
248
|
+
* a string literal or quoted identifier are left intact.
|
|
249
|
+
* @param sql The SQL text to rewrite.
|
|
250
|
+
* @returns The SQL with length-qualified `text(N)` rewritten to `varchar(N)`.
|
|
251
|
+
*/
|
|
252
|
+
export function normalizeSqliteTextLength(sql) {
|
|
253
|
+
let out = '';
|
|
254
|
+
let i = 0;
|
|
255
|
+
let quote = null;
|
|
256
|
+
while (i < sql.length) {
|
|
257
|
+
const ch = sql[i];
|
|
258
|
+
if (quote !== null) {
|
|
259
|
+
if (ch === quote) {
|
|
260
|
+
if (sql[i + 1] === quote) {
|
|
261
|
+
out += ch + ch;
|
|
262
|
+
i += 2;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
quote = null;
|
|
266
|
+
}
|
|
267
|
+
out += ch;
|
|
268
|
+
i++;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
272
|
+
quote = ch;
|
|
273
|
+
out += ch;
|
|
274
|
+
i++;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
// `text` as a whole word, followed by optional whitespace then `(`.
|
|
278
|
+
if (ch === 't' || ch === 'T') {
|
|
279
|
+
const word = sql.slice(i, i + 4);
|
|
280
|
+
if (word.toLowerCase() === 'text') {
|
|
281
|
+
const prev = i === 0 ? '' : sql[i - 1];
|
|
282
|
+
const next = sql[i + 4] ?? '';
|
|
283
|
+
const wordStart = i === 0 || !/[a-zA-Z0-9_$]/.test(prev);
|
|
284
|
+
const wordEnd = !/[a-zA-Z0-9_$]/.test(next);
|
|
285
|
+
let j = i + 4;
|
|
286
|
+
while (j < sql.length && /\s/.test(sql[j]))
|
|
287
|
+
j++;
|
|
288
|
+
if (wordStart && wordEnd && sql[j] === '(') {
|
|
289
|
+
out += 'varchar';
|
|
290
|
+
i += 4;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
out += ch;
|
|
296
|
+
i++;
|
|
297
|
+
}
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Rewrite a string literal whose entire content is one backslash (`'\'`) to the
|
|
302
|
+
* doubled spelling (`'\\'`). SQLite spells "the escape character is a single
|
|
303
|
+
* backslash" as `'\'`, but node-sql-parser's lexer reads the bare `'\'` as an
|
|
304
|
+
* unterminated escaped-quote (`\'` with no closing quote) and rejects the whole
|
|
305
|
+
* statement — so a `LIKE … ESCAPE '\'` clause (recall-protocol's FTS-escape
|
|
306
|
+
* spelling) fails to parse. Doubling the backslash is the same value spelled the
|
|
307
|
+
* way the grammar accepts (`'\\'` is one literal backslash), so the rewrite is
|
|
308
|
+
* lossless: the escape character is still one backslash, and no fact a rule
|
|
309
|
+
* reads (relations, filters, statement kind) changes. Only the exact three-char
|
|
310
|
+
* sequence `'\'` is rewritten, and only where it opens a string literal (never
|
|
311
|
+
* inside a quoted identifier or a longer string), so a `'\''` (backslash then
|
|
312
|
+
* escaped quote) or a double-quoted identifier containing `'\'` is left intact.
|
|
313
|
+
* @param sql The SQL text to rewrite.
|
|
314
|
+
* @returns The SQL with a lone-backslash string literal rewritten to `'\\'`.
|
|
315
|
+
*/
|
|
316
|
+
export function normalizeEscapeClause(sql) {
|
|
317
|
+
let out = '';
|
|
318
|
+
let i = 0;
|
|
319
|
+
let quote = null;
|
|
320
|
+
while (i < sql.length) {
|
|
321
|
+
const ch = sql[i];
|
|
322
|
+
if (quote !== null) {
|
|
323
|
+
if (ch === quote) {
|
|
324
|
+
if (sql[i + 1] === quote) {
|
|
325
|
+
out += ch + ch;
|
|
326
|
+
i += 2;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
quote = null;
|
|
330
|
+
}
|
|
331
|
+
out += ch;
|
|
332
|
+
i++;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
336
|
+
if (ch === "'" && sql[i + 1] === '\\' && sql[i + 2] === "'" && sql[i + 3] !== "'") {
|
|
337
|
+
out += "'\\\\'";
|
|
338
|
+
i += 3;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
quote = ch;
|
|
342
|
+
out += ch;
|
|
343
|
+
i++;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
out += ch;
|
|
347
|
+
i++;
|
|
348
|
+
}
|
|
349
|
+
return out;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Rewrite a bare `?` placeholder in a `LIMIT`/`OFFSET` clause to a literal
|
|
353
|
+
* (`LIMIT 1` / `OFFSET 0`). node-sql-parser's sqlite grammar accepts `LIMIT 5`
|
|
354
|
+
* but rejects `LIMIT ?` (a bound-parameter limit) even though sqlite itself
|
|
355
|
+
* accepts it — so a parameterized `SELECT … LIMIT ? OFFSET ?` (recall-protocol's
|
|
356
|
+
* paging spelling) fails to parse. The fact a rule reads from a LIMIT/OFFSET
|
|
357
|
+
* clause is *presence*, not the value (see `whereFacts`), so substituting a
|
|
358
|
+
* concrete literal is lossless: `LIMIT ?` still reports "has a limit". The
|
|
359
|
+
* rewrite fires only on a bare `?` immediately after the keyword (whitespace
|
|
360
|
+
* optional), and skips string literals and quoted identifiers.
|
|
361
|
+
* @param sql The SQL text to rewrite.
|
|
362
|
+
* @returns The SQL with `LIMIT ?` → `LIMIT 1` and `OFFSET ?` → `OFFSET 0`.
|
|
363
|
+
*/
|
|
364
|
+
export function normalizeLimitOffsetPlaceholders(sql) {
|
|
365
|
+
let out = '';
|
|
366
|
+
let i = 0;
|
|
367
|
+
let quote = null;
|
|
368
|
+
while (i < sql.length) {
|
|
369
|
+
const ch = sql[i];
|
|
370
|
+
if (quote !== null) {
|
|
371
|
+
if (ch === quote) {
|
|
372
|
+
if (sql[i + 1] === quote) {
|
|
373
|
+
out += ch + ch;
|
|
374
|
+
i += 2;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
quote = null;
|
|
378
|
+
}
|
|
379
|
+
out += ch;
|
|
380
|
+
i++;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
384
|
+
quote = ch;
|
|
385
|
+
out += ch;
|
|
386
|
+
i++;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
let keyword = null;
|
|
390
|
+
let value = null;
|
|
391
|
+
const lower6 = sql.slice(i, i + 6).toLowerCase();
|
|
392
|
+
if (lower6.startsWith('limit')) {
|
|
393
|
+
keyword = sql.slice(i, i + 5);
|
|
394
|
+
value = '1';
|
|
395
|
+
}
|
|
396
|
+
else if (lower6.startsWith('offset')) {
|
|
397
|
+
keyword = sql.slice(i, i + 6);
|
|
398
|
+
value = '0';
|
|
399
|
+
}
|
|
400
|
+
if (keyword !== null && value !== null) {
|
|
401
|
+
const prev = i === 0 ? '' : sql[i - 1];
|
|
402
|
+
const after = sql[i + keyword.length] ?? '';
|
|
403
|
+
if (!/[a-zA-Z0-9_$]/.test(prev) && !/[a-zA-Z0-9_$]/.test(after)) {
|
|
404
|
+
let j = i + keyword.length;
|
|
405
|
+
while (j < sql.length && /\s/.test(sql[j]))
|
|
406
|
+
j++;
|
|
407
|
+
if (sql[j] === '?') {
|
|
408
|
+
out += keyword + ' ' + value;
|
|
409
|
+
i = j + 1;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
out += ch;
|
|
415
|
+
i++;
|
|
416
|
+
}
|
|
417
|
+
return out;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Rewrite a `CREATE VIRTUAL TABLE name USING fts5(cols…)` declaration to the
|
|
421
|
+
* `CREATE TABLE name (cols…)` the sqlite grammar accepts. node-sql-parser's
|
|
422
|
+
* sqlite grammar has no `CREATE VIRTUAL TABLE`, so a full-text-search table
|
|
423
|
+
* (recall-protocol's `strategies_fts`, declared `USING fts5(...)`) fails to parse
|
|
424
|
+
* and blanks the table — even though a virtual table is a first-class queryable
|
|
425
|
+
* relation the lifecycle rules track exactly like a stored table. The facts a
|
|
426
|
+
* rule reads from the declaration are the table name and its column names; the
|
|
427
|
+
* FTS5 module wrapper (`USING fts5`, a column's `UNINDEXED`, the trailing
|
|
428
|
+
* `tokenize = '…'` option) is module configuration, not a table fact. So the
|
|
429
|
+
* rewrite drops the module syntax and keeps the columns, recovering the same
|
|
430
|
+
* table/column facts a `CREATE TABLE` would carry. Only a `CREATE VIRTUAL TABLE`
|
|
431
|
+
* (word-boundary matched, case-insensitive) whose module argument list parses to
|
|
432
|
+
* at least one bare column is rewritten; anything else — a module option list
|
|
433
|
+
* with no columns, an unexpected shape — is left untouched and reports an honest
|
|
434
|
+
* parse failure rather than a fabricated table.
|
|
435
|
+
* @param sql The SQL text to rewrite.
|
|
436
|
+
* @returns The SQL with `CREATE VIRTUAL TABLE … USING <module>(cols)` rewritten to `CREATE TABLE … (cols)`.
|
|
437
|
+
*/
|
|
438
|
+
export function normalizeVirtualTable(sql) {
|
|
439
|
+
let out = '';
|
|
440
|
+
let i = 0;
|
|
441
|
+
let quote = null;
|
|
442
|
+
const HEADER = 'create virtual table';
|
|
443
|
+
while (i < sql.length) {
|
|
444
|
+
const ch = sql[i];
|
|
445
|
+
if (quote !== null) {
|
|
446
|
+
if (ch === quote) {
|
|
447
|
+
if (sql[i + 1] === quote) {
|
|
448
|
+
out += ch + ch;
|
|
449
|
+
i += 2;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
quote = null;
|
|
453
|
+
}
|
|
454
|
+
out += ch;
|
|
455
|
+
i++;
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
459
|
+
quote = ch;
|
|
460
|
+
out += ch;
|
|
461
|
+
i++;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const beforeOk = i === 0 || !/[a-zA-Z0-9_$]/.test(sql[i - 1]);
|
|
465
|
+
const afterHeader = sql[i + HEADER.length] ?? '';
|
|
466
|
+
const afterOk = !/[a-zA-Z0-9_$]/.test(afterHeader);
|
|
467
|
+
if (beforeOk && afterOk && sql.slice(i, i + HEADER.length).toLowerCase() === HEADER) {
|
|
468
|
+
// `CREATE VIRTUAL TABLE <name> USING <module>( <args> )`
|
|
469
|
+
let j = i + HEADER.length;
|
|
470
|
+
while (j < sql.length && /\s/.test(sql[j]))
|
|
471
|
+
j++;
|
|
472
|
+
const nameStart = j;
|
|
473
|
+
while (j < sql.length && !/\s/.test(sql[j]) && sql[j] !== '(')
|
|
474
|
+
j++;
|
|
475
|
+
const name = sql.slice(nameStart, j);
|
|
476
|
+
while (j < sql.length && /\s/.test(sql[j]))
|
|
477
|
+
j++;
|
|
478
|
+
if (sql.slice(j, j + 5).toLowerCase() === 'using') {
|
|
479
|
+
let k = j + 5;
|
|
480
|
+
while (k < sql.length && /\s/.test(sql[k]))
|
|
481
|
+
k++;
|
|
482
|
+
while (k < sql.length && /[a-zA-Z0-9_$]/.test(sql[k]))
|
|
483
|
+
k++; // module name
|
|
484
|
+
while (k < sql.length && /\s/.test(sql[k]))
|
|
485
|
+
k++;
|
|
486
|
+
if (sql[k] === '(') {
|
|
487
|
+
// find the module argument list's matching `)` (nested parens + quotes)
|
|
488
|
+
let m = k + 1;
|
|
489
|
+
let depth = 1;
|
|
490
|
+
let q = null;
|
|
491
|
+
while (m < sql.length && depth > 0) {
|
|
492
|
+
const c = sql[m];
|
|
493
|
+
if (q !== null) {
|
|
494
|
+
if (c === q) {
|
|
495
|
+
if (sql[m + 1] === q) {
|
|
496
|
+
m += 2;
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
q = null;
|
|
500
|
+
}
|
|
501
|
+
m++;
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
505
|
+
q = c;
|
|
506
|
+
m++;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (c === '(')
|
|
510
|
+
depth++;
|
|
511
|
+
else if (c === ')')
|
|
512
|
+
depth--;
|
|
513
|
+
m++;
|
|
514
|
+
}
|
|
515
|
+
const cols = extractFtsColumnNames(sql.slice(k + 1, m - 1));
|
|
516
|
+
if (cols.length > 0 && name.length > 0) {
|
|
517
|
+
out += `CREATE TABLE ${name} (${cols.join(', ')})`;
|
|
518
|
+
i = m;
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
out += ch;
|
|
524
|
+
i++;
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
out += ch;
|
|
528
|
+
i++;
|
|
529
|
+
}
|
|
530
|
+
return out;
|
|
531
|
+
}
|
|
532
|
+
/** Split an FTS5 module argument list into its bare column names, dropping
|
|
533
|
+
* `UNINDEXED`/`NOT INDEXED` modifiers and `name = '…'` option entries. */
|
|
534
|
+
function extractFtsColumnNames(args) {
|
|
535
|
+
const parts = [];
|
|
536
|
+
let i = 0;
|
|
537
|
+
let start = 0;
|
|
538
|
+
let depth = 0;
|
|
539
|
+
let quote = null;
|
|
540
|
+
while (i < args.length) {
|
|
541
|
+
const ch = args[i];
|
|
542
|
+
if (quote !== null) {
|
|
543
|
+
if (ch === quote) {
|
|
544
|
+
if (args[i + 1] === quote) {
|
|
545
|
+
i += 2;
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
quote = null;
|
|
549
|
+
}
|
|
550
|
+
i++;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
554
|
+
quote = ch;
|
|
555
|
+
i++;
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
if (ch === '(')
|
|
559
|
+
depth++;
|
|
560
|
+
else if (ch === ')')
|
|
561
|
+
depth--;
|
|
562
|
+
else if (ch === ',' && depth === 0) {
|
|
563
|
+
parts.push(args.slice(start, i).trim());
|
|
564
|
+
start = i + 1;
|
|
565
|
+
}
|
|
566
|
+
i++;
|
|
567
|
+
}
|
|
568
|
+
parts.push(args.slice(start).trim());
|
|
569
|
+
const cols = [];
|
|
570
|
+
for (const part of parts) {
|
|
571
|
+
if (part === '')
|
|
572
|
+
continue;
|
|
573
|
+
if (part.includes('='))
|
|
574
|
+
continue; // `tokenize = '…'`, `prefix = '…'`, `content = …`
|
|
575
|
+
const name = part.split(/\s+/)[0];
|
|
576
|
+
if (name)
|
|
577
|
+
cols.push(name);
|
|
578
|
+
}
|
|
579
|
+
return cols;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Apply the declared input normalizations for a dialect: D1 numbered parameters
|
|
583
|
+
* `?n` → `?`, template substitutions `${…}` → `?`, `LIMIT ?`/`OFFSET ?` → a
|
|
584
|
+
* concrete literal, `'\'` → `'\\'`, `CREATE VIRTUAL TABLE … USING fts5(…)` →
|
|
585
|
+
* `CREATE TABLE …`, and — sqlite only, since it is a sqlite-specific spelling —
|
|
586
|
+
* length-qualified `text(N)` → `varchar(N)`.
|
|
587
|
+
*/
|
|
588
|
+
function applyDeclaredNormalizations(trimmed, dialect) {
|
|
589
|
+
// Strip comments first. Every normalizer below tracks quoted strings to avoid
|
|
590
|
+
// rewriting inside them, but none of them models `--` / `/* … */` comments. An
|
|
591
|
+
// apostrophe in a comment — `don't` in a migration's header block — opens a
|
|
592
|
+
// quote that swallows the rest of the file and silently disables every rewrite
|
|
593
|
+
// after it (`text(N)` → `varchar(N)` and the rest). Comments are semantically
|
|
594
|
+
// inert, so dropping them before the quote-tracking rewrites is lossless and
|
|
595
|
+
// keeps the normalizers' quote state honest.
|
|
596
|
+
const uncommented = stripComments(trimmed);
|
|
597
|
+
const positional = normalizePositionalParams(uncommented);
|
|
598
|
+
const templated = normalizeTemplateSubstitutions(positional);
|
|
599
|
+
const limited = normalizeLimitOffsetPlaceholders(templated);
|
|
600
|
+
const escaped = normalizeEscapeClause(limited);
|
|
601
|
+
const tabled = normalizeVirtualTable(escaped);
|
|
602
|
+
return dialect === 'sqlite' ? normalizeSqliteTextLength(tabled) : tabled;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Split a (possibly multi-statement) SQL string on top-level `;`. Semicolons
|
|
606
|
+
* inside single/double-quoted strings and backtick identifiers are not statement
|
|
607
|
+
* boundaries, and empty fragments (a trailing `;`) are dropped. A single string
|
|
608
|
+
* literal carrying several DDL statements (`DROP TABLE a; DROP TABLE b;`) parses
|
|
609
|
+
* as its statements instead of failing "expected a single statement".
|
|
610
|
+
*
|
|
611
|
+
* A `CREATE TRIGGER … BEGIN … END` body carries `;` between its own statements
|
|
612
|
+
* that are not statement boundaries, so the trigger's `BEGIN … END` block is
|
|
613
|
+
* tracked and its internal `;` kept inside the statement — otherwise a trigger
|
|
614
|
+
* would be mid-body-split into a dangling header and a bare `END`. The tracking
|
|
615
|
+
* is scoped to a statement whose leading text is `CREATE TRIGGER`, so a
|
|
616
|
+
* transaction's `BEGIN;` or a stray `CASE`/`END` in ordinary SQL is split exactly
|
|
617
|
+
* as before. A `CASE … END` inside the body is tracked so its `END` is not
|
|
618
|
+
* mistaken for the trigger's closing `END`.
|
|
619
|
+
* @param sql The SQL text to split.
|
|
620
|
+
* @returns The top-level statements, in order, with empty fragments dropped.
|
|
621
|
+
*/
|
|
622
|
+
export function splitSqlStatements(sql) {
|
|
623
|
+
const parts = [];
|
|
624
|
+
let current = '';
|
|
625
|
+
let i = 0;
|
|
626
|
+
let quote = null;
|
|
627
|
+
let beginDepth = 0;
|
|
628
|
+
let caseDepth = 0;
|
|
629
|
+
const wordBoundary = (c) => c === undefined || !/[a-zA-Z0-9_$]/.test(c);
|
|
630
|
+
while (i < sql.length) {
|
|
631
|
+
const ch = sql[i];
|
|
632
|
+
if (quote !== null) {
|
|
633
|
+
if (ch === quote) {
|
|
634
|
+
if (sql[i + 1] === quote) {
|
|
635
|
+
current += ch + ch;
|
|
636
|
+
i += 2;
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
quote = null;
|
|
640
|
+
}
|
|
641
|
+
current += ch;
|
|
642
|
+
i++;
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
// Skip comments before quote tracking: an apostrophe inside `-- don't` must
|
|
646
|
+
// not open a string literal and swallow every later `;` in the file, and a
|
|
647
|
+
// `;` inside a comment is prose, not a statement boundary. Mirrors
|
|
648
|
+
// `truncateConflictClause`'s comment handling. The comment text is dropped
|
|
649
|
+
// (semantically inert — the parser never needs it to extract ops).
|
|
650
|
+
if (ch === '-' && sql[i + 1] === '-') {
|
|
651
|
+
while (i < sql.length && sql[i] !== '\n')
|
|
652
|
+
i++;
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (ch === '/' && sql[i + 1] === '*') {
|
|
656
|
+
i += 2;
|
|
657
|
+
while (i < sql.length && !(sql[i] === '*' && sql[i + 1] === '/'))
|
|
658
|
+
i++;
|
|
659
|
+
i += 2;
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
663
|
+
quote = ch;
|
|
664
|
+
current += ch;
|
|
665
|
+
i++;
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
if (/[a-zA-Z]/.test(ch)) {
|
|
669
|
+
let j = i;
|
|
670
|
+
while (j < sql.length && /[a-zA-Z0-9_$]/.test(sql[j]))
|
|
671
|
+
j++;
|
|
672
|
+
const word = sql.slice(i, j).toLowerCase();
|
|
673
|
+
const prev = i === 0 ? undefined : sql[i - 1];
|
|
674
|
+
const after = sql[j];
|
|
675
|
+
if (wordBoundary(prev) && wordBoundary(after)) {
|
|
676
|
+
if (word === 'begin') {
|
|
677
|
+
if (current.trimStart().toLowerCase().startsWith('create trigger'))
|
|
678
|
+
beginDepth++;
|
|
679
|
+
}
|
|
680
|
+
else if (word === 'case') {
|
|
681
|
+
if (beginDepth > 0)
|
|
682
|
+
caseDepth++;
|
|
683
|
+
}
|
|
684
|
+
else if (word === 'end') {
|
|
685
|
+
if (caseDepth > 0)
|
|
686
|
+
caseDepth--;
|
|
687
|
+
else if (beginDepth > 0)
|
|
688
|
+
beginDepth--;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
current += sql.slice(i, j);
|
|
692
|
+
i = j;
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if (ch === ';') {
|
|
696
|
+
if (beginDepth === 0 && caseDepth === 0) {
|
|
697
|
+
const part = current.trim();
|
|
698
|
+
if (part.length > 0)
|
|
699
|
+
parts.push(part);
|
|
700
|
+
current = '';
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
current += ch;
|
|
704
|
+
}
|
|
705
|
+
i++;
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
current += ch;
|
|
709
|
+
i++;
|
|
710
|
+
}
|
|
711
|
+
const tail = current.trim();
|
|
712
|
+
if (tail.length > 0)
|
|
713
|
+
parts.push(tail);
|
|
714
|
+
return parts;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Truncate a SQL string at its top-level `ON CONFLICT` clause, returning the
|
|
718
|
+
* prefix and a flag recording that a conflict clause was present but not parsed.
|
|
719
|
+
*
|
|
720
|
+
* Spec 70 (ON CONFLICT disposition): node-sql-parser's `sqlite` grammar has no
|
|
721
|
+
* `ON CONFLICT`, so a D1 `INSERT … ON CONFLICT (…) DO UPDATE` upsert is a parse
|
|
722
|
+
* failure — and the failure took the whole statement down, blanking the target
|
|
723
|
+
* table / write-status facts the prefix already answers. Those facts (target
|
|
724
|
+
* table, write status, predicate presence) are all answered by the
|
|
725
|
+
* `INSERT INTO x (cols) VALUES (…)` prefix, so the clause is cut at a *top-level*
|
|
726
|
+
* `ON CONFLICT` (not inside a string literal, comment, or parenthesized
|
|
727
|
+
* subexpression) and the prefix is parsed. The flag keeps "an upsert is present"
|
|
728
|
+
* distinct from "the write is a plain INSERT" — a future rule reading the update
|
|
729
|
+
* action sees "present but unread" rather than absent. This is a truncation, not
|
|
730
|
+
* a normalization: the clause's update action is recorded as dropped, never
|
|
731
|
+
* silently re-written into an equivalent spelling.
|
|
732
|
+
* @param sql The SQL text to scan.
|
|
733
|
+
* @returns The prefix before a top-level `ON CONFLICT` clause, plus a flag recording that a clause was truncated.
|
|
734
|
+
*/
|
|
735
|
+
export function truncateConflictClause(sql) {
|
|
736
|
+
let i = 0;
|
|
737
|
+
let quote = null;
|
|
738
|
+
let depth = 0;
|
|
739
|
+
while (i < sql.length) {
|
|
740
|
+
const ch = sql[i];
|
|
741
|
+
if (quote !== null) {
|
|
742
|
+
if (ch === quote) {
|
|
743
|
+
if (sql[i + 1] === quote) {
|
|
744
|
+
i += 2;
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
quote = null;
|
|
748
|
+
}
|
|
749
|
+
i++;
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
753
|
+
quote = ch;
|
|
754
|
+
i++;
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
if (ch === '-' && sql[i + 1] === '-') {
|
|
758
|
+
while (i < sql.length && sql[i] !== '\n')
|
|
759
|
+
i++;
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (ch === '/' && sql[i + 1] === '*') {
|
|
763
|
+
i += 2;
|
|
764
|
+
while (i < sql.length && !(sql[i] === '*' && sql[i + 1] === '/'))
|
|
765
|
+
i++;
|
|
766
|
+
i += 2;
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
if (ch === '(') {
|
|
770
|
+
depth++;
|
|
771
|
+
i++;
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
if (ch === ')') {
|
|
775
|
+
depth--;
|
|
776
|
+
i++;
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (depth === 0 && (ch === 'o' || ch === 'O')) {
|
|
780
|
+
const before = i > 0 ? sql[i - 1] : '';
|
|
781
|
+
const beforeOk = !/[a-z0-9_$]/i.test(before);
|
|
782
|
+
if (beforeOk && sql.slice(i, i + 11).toLowerCase() === 'on conflict') {
|
|
783
|
+
const after = sql[i + 11];
|
|
784
|
+
const afterOk = after === undefined || !/[a-z0-9_]/i.test(after);
|
|
785
|
+
if (afterOk) {
|
|
786
|
+
return { text: sql.slice(0, i).trim(), truncated: true };
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
i++;
|
|
791
|
+
}
|
|
792
|
+
return { text: sql, truncated: false };
|
|
793
|
+
}
|
|
794
|
+
/** Parse one already-normalized statement string into a single-statement result. */
|
|
795
|
+
function parseSingleStatement(text, dialect) {
|
|
796
|
+
const { text: toParse, truncated } = truncateConflictClause(text);
|
|
797
|
+
let astified;
|
|
798
|
+
try {
|
|
799
|
+
astified = parser.astify(toParse, { database: dialect });
|
|
800
|
+
}
|
|
801
|
+
catch (error) {
|
|
802
|
+
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
803
|
+
}
|
|
804
|
+
const conflictClauseTruncated = truncated || undefined;
|
|
805
|
+
if (Array.isArray(astified)) {
|
|
806
|
+
if (astified.length === 0) {
|
|
807
|
+
// The grammar consumed the input but produced no statement (e.g. a
|
|
808
|
+
// comment-only fragment) — an honest cannot-fire, not a crash.
|
|
809
|
+
return { ok: false, reason: 'SQL did not produce a statement' };
|
|
810
|
+
}
|
|
811
|
+
if (astified.length === 1) {
|
|
812
|
+
return { ok: true, ast: astified[0], kind: statementKind(astified[0].type), conflictClauseTruncated };
|
|
813
|
+
}
|
|
814
|
+
// A `;` the splitter missed — parse all of them and report the program.
|
|
815
|
+
return { ok: true, ast: astified[0], kind: statementKind(astified[0].type), statements: astified, conflictClauseTruncated };
|
|
816
|
+
}
|
|
817
|
+
if (astified === null || typeof astified !== 'object') {
|
|
818
|
+
return { ok: false, reason: 'SQL did not produce a statement' };
|
|
819
|
+
}
|
|
820
|
+
return { ok: true, ast: astified, kind: statementKind(astified.type), conflictClauseTruncated };
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Parse a SQL string — one statement, or several separated by `;`. Returns
|
|
824
|
+
* `{ ok: false, reason }` on an empty string, a parse error (after the declared
|
|
825
|
+
* normalization below), or a program where any statement fails to parse. A
|
|
826
|
+
* multi-statement string that fully parses is `ok` with `statements` holding
|
|
827
|
+
* every AST.
|
|
828
|
+
*
|
|
829
|
+
* Normalization, applied before the grammar: (a) D1 numbered parameters `?n` →
|
|
830
|
+
* `?`; (b) a multi-statement string is split on top-level `;` and each statement
|
|
831
|
+
* parsed individually; (c) a standalone transaction-control statement is
|
|
832
|
+
* recognized as SQL with no facts. Anything still failing after those is a
|
|
833
|
+
* genuine `cannot-fire` (e.g. `ON CONFLICT … DO UPDATE`, a Postgres `::` cast).
|
|
834
|
+
*
|
|
835
|
+
* @param text The SQL text to parse.
|
|
836
|
+
* @param dialect The dialect to parse under — the corpus's named dialect, or
|
|
837
|
+
* {@link DEFAULT_SQL_DIALECT} when detection named none. The caller is still
|
|
838
|
+
* responsible for the honesty: when it supplied the default because the dialect
|
|
839
|
+
* is undetermined, a parse failure must be reported naming both the failure and
|
|
840
|
+
* the undetermined dialect.
|
|
841
|
+
* @returns `{ ok: true, ast, kind }` on success (with `statements` for a multi-statement string), or `{ ok: false, reason }` on an empty string, parse error, or unparseable program.
|
|
842
|
+
*/
|
|
843
|
+
export function parseSql(text, dialect) {
|
|
844
|
+
const trimmed = text.trim();
|
|
845
|
+
if (trimmed.length === 0) {
|
|
846
|
+
return { ok: false, reason: 'empty SQL string' };
|
|
847
|
+
}
|
|
848
|
+
if (isStandaloneTransactionControl(trimmed)) {
|
|
849
|
+
return { ok: true, ast: TRANSACTION_MARKER, kind: 'other' };
|
|
850
|
+
}
|
|
851
|
+
const normalized = applyDeclaredNormalizations(trimmed, dialect);
|
|
852
|
+
const statements = splitSqlStatements(normalized);
|
|
853
|
+
if (statements.length === 0) {
|
|
854
|
+
// Only top-level `;` (or a substitution that normalized to nothing) — no
|
|
855
|
+
// statement text survives to parse.
|
|
856
|
+
return { ok: false, reason: 'SQL contained no statements' };
|
|
857
|
+
}
|
|
858
|
+
if (statements.length > 1) {
|
|
859
|
+
const asts = [];
|
|
860
|
+
let anyTruncated = false;
|
|
861
|
+
for (const statement of statements) {
|
|
862
|
+
const result = parseSingleStatement(statement, dialect);
|
|
863
|
+
if (!result.ok) {
|
|
864
|
+
return { ok: false, reason: `statement ${asts.length + 1} failed: ${result.reason}` };
|
|
865
|
+
}
|
|
866
|
+
asts.push(result.ast);
|
|
867
|
+
if (result.conflictClauseTruncated)
|
|
868
|
+
anyTruncated = true;
|
|
869
|
+
}
|
|
870
|
+
return { ok: true, ast: asts[0], kind: statementKind(asts[0].type), statements: asts, conflictClauseTruncated: anyTruncated || undefined };
|
|
871
|
+
}
|
|
872
|
+
return parseSingleStatement(statements[0], dialect);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* A template placeholder (`{{VERSION}}`, `{{.MigrationName}}`). A migration that
|
|
876
|
+
* carries one is not SQL until it is rendered at deploy time, so it is classified
|
|
877
|
+
* "not SQL", not "unparseable SQL" — a distinct failure reason a caller can
|
|
878
|
+
* bucket separately from dialect gaps.
|
|
879
|
+
*/
|
|
880
|
+
const TEMPLATE_PLACEHOLDER = /\{\{[^}]*\}\}/;
|
|
881
|
+
/**
|
|
882
|
+
* Remove `--` line comments and `/* … *\/` block comments (respecting quoted
|
|
883
|
+
* strings), so a `{{…}}` that appears only in a comment — prose naming a
|
|
884
|
+
* placeholder — is not mistaken for a template marker. A template placeholder
|
|
885
|
+
* that changes the SQL is inline in the statement text; one in a comment does not
|
|
886
|
+
* affect whether the file parses.
|
|
887
|
+
*/
|
|
888
|
+
function stripComments(sql) {
|
|
889
|
+
let out = '';
|
|
890
|
+
let i = 0;
|
|
891
|
+
let quote = null;
|
|
892
|
+
while (i < sql.length) {
|
|
893
|
+
const ch = sql[i];
|
|
894
|
+
const next = sql[i + 1];
|
|
895
|
+
if (quote !== null) {
|
|
896
|
+
if (ch === quote) {
|
|
897
|
+
if (next === quote) {
|
|
898
|
+
out += ch + ch;
|
|
899
|
+
i += 2;
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
quote = null;
|
|
903
|
+
}
|
|
904
|
+
out += ch;
|
|
905
|
+
i++;
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
909
|
+
quote = ch;
|
|
910
|
+
out += ch;
|
|
911
|
+
i++;
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (ch === '-' && next === '-') {
|
|
915
|
+
while (i < sql.length && sql[i] !== '\n')
|
|
916
|
+
i++;
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (ch === '/' && next === '*') {
|
|
920
|
+
i += 2;
|
|
921
|
+
while (i < sql.length && !(sql[i] === '*' && sql[i + 1] === '/'))
|
|
922
|
+
i++;
|
|
923
|
+
i += 2;
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
out += ch;
|
|
927
|
+
i++;
|
|
928
|
+
}
|
|
929
|
+
return out;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Parse a `.sql` file's full text into its statements. Used by the DDL
|
|
933
|
+
* discovery path (which reads whole migration files), not by the per-call-site
|
|
934
|
+
* parse. Empty text is an empty program, not a failure; a templated migration
|
|
935
|
+
* (`{{…}}`) is "not SQL until rendered"; a syntax error anywhere is a failure of
|
|
936
|
+
* the whole file (the same "parse or nothing" contract as {@link parseSql}).
|
|
937
|
+
*
|
|
938
|
+
* D1 numbered parameters (`?n` → `?`) are normalized before parsing, as in
|
|
939
|
+
* {@link parseSql}.
|
|
940
|
+
*
|
|
941
|
+
* @param text The `.sql` file's full text.
|
|
942
|
+
* @param dialect The corpus's named dialect.
|
|
943
|
+
* @returns A parsed program, or a failure reason (templated migration / syntax error).
|
|
944
|
+
*/
|
|
945
|
+
export function parseSqlProgram(text, dialect) {
|
|
946
|
+
const trimmed = text.trim();
|
|
947
|
+
if (trimmed.length === 0)
|
|
948
|
+
return { ok: true, statements: [] };
|
|
949
|
+
if (TEMPLATE_PLACEHOLDER.test(stripComments(trimmed))) {
|
|
950
|
+
return { ok: false, reason: 'templated migration (contains {{…}} placeholder — not SQL until rendered)' };
|
|
951
|
+
}
|
|
952
|
+
const normalized = applyDeclaredNormalizations(trimmed, dialect);
|
|
953
|
+
let astified;
|
|
954
|
+
try {
|
|
955
|
+
astified = parser.astify(normalized, { database: dialect });
|
|
956
|
+
}
|
|
957
|
+
catch (error) {
|
|
958
|
+
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
959
|
+
}
|
|
960
|
+
if (Array.isArray(astified))
|
|
961
|
+
return { ok: true, statements: astified };
|
|
962
|
+
if (astified === null || typeof astified !== 'object')
|
|
963
|
+
return { ok: true, statements: [] };
|
|
964
|
+
return { ok: true, statements: [astified] };
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Parse a `.sql` migration file tolerantly — statement by statement. The
|
|
968
|
+
* `parseSqlProgram` all-or-nothing contract is right for a single call-site
|
|
969
|
+
* query string (a query either parses or it is `cannot-fire`), but a migration
|
|
970
|
+
* file is a *sequence* of DDL statements, and one statement the grammar rejects
|
|
971
|
+
* (an `UPDATE … FROM`, a Drizzle `ALTER COLUMN "a" TO "a" <type>`) must not
|
|
972
|
+
* discard the well-formed `CREATE TABLE` beside it. Each statement is split on a
|
|
973
|
+
* top-level `;` and parsed individually; a `PRAGMA` / `DROP INDEX` / `DROP VIEW`
|
|
974
|
+
* / `DROP TRIGGER` / `CREATE VIEW` / `CREATE TRIGGER` is *recognized* as no-fact
|
|
975
|
+
* DDL (not parsed, not a failure), the successes are walked for DDL facts, and
|
|
976
|
+
* the remaining failures are reported so the caller can account for what it did
|
|
977
|
+
* not read. This is the honest successor to the regex scanners, which silently
|
|
978
|
+
* skipped whatever they did not match.
|
|
979
|
+
*
|
|
980
|
+
* @param text The `.sql` file's full text.
|
|
981
|
+
* @param dialect The corpus's named dialect.
|
|
982
|
+
* @param recognizeNoFactsDdl When true (a migration file), a `PRAGMA` /
|
|
983
|
+
* `DROP INDEX` / `DROP VIEW` / `DROP TRIGGER` / `CREATE VIEW` / `CREATE TRIGGER`
|
|
984
|
+
* is recognized as no-fact DDL rather than a parse failure. When false (a code
|
|
985
|
+
* SQL argument — see {@link parseSqlTables}), it is left to the grammar, so a
|
|
986
|
+
* `PRAGMA table_info(x)` surfaces as a parse failure (it names `x`) rather than
|
|
987
|
+
* reading "no tables" as clean.
|
|
988
|
+
* @returns The statements that parsed, the failures with reasons, and the indices of statements whose `ON CONFLICT` clause was truncated.
|
|
989
|
+
*/
|
|
990
|
+
export function parseSqlProgramTolerant(text, dialect, recognizeNoFactsDdl = true) {
|
|
991
|
+
const trimmed = text.trim();
|
|
992
|
+
if (trimmed.length === 0)
|
|
993
|
+
return { statements: [], failures: [], truncatedConflictIndices: [] };
|
|
994
|
+
if (TEMPLATE_PLACEHOLDER.test(stripComments(trimmed))) {
|
|
995
|
+
return { statements: [], failures: [{ index: 0, reason: 'templated migration (contains {{…}} placeholder — not SQL until rendered)' }], truncatedConflictIndices: [] };
|
|
996
|
+
}
|
|
997
|
+
const normalized = applyDeclaredNormalizations(trimmed, dialect);
|
|
998
|
+
const parts = splitSqlStatements(normalized);
|
|
999
|
+
const statements = [];
|
|
1000
|
+
const failures = [];
|
|
1001
|
+
const truncatedConflictIndices = [];
|
|
1002
|
+
parts.forEach((part, index) => {
|
|
1003
|
+
if (recognizeNoFactsDdl && isNoTableFactsDdl(part)) {
|
|
1004
|
+
statements.push(NO_FACTS_MARKER);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
const result = parseSingleStatement(part, dialect);
|
|
1008
|
+
if (result.ok) {
|
|
1009
|
+
if (result.conflictClauseTruncated)
|
|
1010
|
+
truncatedConflictIndices.push(statements.length);
|
|
1011
|
+
// A part can still parse to several statements (a `;` the splitter missed,
|
|
1012
|
+
// e.g. inside a comment-stripped span or a construct the splitter does not
|
|
1013
|
+
// model). `result.ast` is only the first of them — keep the rest, or the
|
|
1014
|
+
// producer silently drops every statement after the first.
|
|
1015
|
+
statements.push(...(result.statements ?? [result.ast]));
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
failures.push({ index, reason: result.reason });
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
return { statements, failures, truncatedConflictIndices };
|
|
1022
|
+
}
|
|
1023
|
+
function relationName(entry) {
|
|
1024
|
+
if (entry === null || typeof entry !== 'object')
|
|
1025
|
+
return null;
|
|
1026
|
+
const table = entry.table;
|
|
1027
|
+
if (typeof table !== 'string' || table.length === 0)
|
|
1028
|
+
return null;
|
|
1029
|
+
return table;
|
|
1030
|
+
}
|
|
1031
|
+
function pushRelation(refs, entry, via) {
|
|
1032
|
+
const name = relationName(entry);
|
|
1033
|
+
if (name !== null)
|
|
1034
|
+
refs.push({ name, via });
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Collect every relation referenced by a statement — the raw FROM/JOIN/INTO/
|
|
1038
|
+
* target/rename/drop relations, *without* the CTE-alias filter. This is the
|
|
1039
|
+
* AST replacement for site #1's `extractTables` plus its bolted-on alias filter:
|
|
1040
|
+
* the CTE names are subtracted in {@link extractTableNames}, because a CTE name
|
|
1041
|
+
* is a named result set, not a stored table.
|
|
1042
|
+
*
|
|
1043
|
+
* The walk descends into CTE bodies (`WITH x AS (SELECT … FROM orders)`) and
|
|
1044
|
+
* into `FROM (SELECT …)` subqueries, so a real table referenced only inside a
|
|
1045
|
+
* CTE or subquery is still collected — the flat text scan saw those, and an AST
|
|
1046
|
+
* walk that did not would silently under-count.
|
|
1047
|
+
* @param ast The parsed statement (or program) to walk.
|
|
1048
|
+
* @returns Every relation referenced, with the clause it came from, before CTE filtering.
|
|
1049
|
+
*/
|
|
1050
|
+
export function collectRelations(ast) {
|
|
1051
|
+
const refs = [];
|
|
1052
|
+
collectRelationsInto(ast, refs);
|
|
1053
|
+
return refs;
|
|
1054
|
+
}
|
|
1055
|
+
function collectRelationsInto(ast, refs) {
|
|
1056
|
+
switch (ast.type) {
|
|
1057
|
+
case 'select': {
|
|
1058
|
+
// CTE bodies reference real tables; walk them before the outer FROM.
|
|
1059
|
+
if (Array.isArray(ast.with)) {
|
|
1060
|
+
for (const cte of ast.with) {
|
|
1061
|
+
const inner = cte?.stmt?.ast;
|
|
1062
|
+
if (inner && typeof inner === 'object')
|
|
1063
|
+
collectRelationsInto(inner, refs);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
const from = ast.from;
|
|
1067
|
+
if (Array.isArray(from))
|
|
1068
|
+
for (const entry of from)
|
|
1069
|
+
collectFromEntry(entry, refs);
|
|
1070
|
+
else
|
|
1071
|
+
collectFromEntry(from, refs);
|
|
1072
|
+
break;
|
|
1073
|
+
}
|
|
1074
|
+
case 'insert':
|
|
1075
|
+
case 'replace':
|
|
1076
|
+
case 'update':
|
|
1077
|
+
case 'alter': {
|
|
1078
|
+
const table = ast.table;
|
|
1079
|
+
if (Array.isArray(table))
|
|
1080
|
+
for (const entry of table)
|
|
1081
|
+
pushRelation(refs, entry, 'target');
|
|
1082
|
+
else
|
|
1083
|
+
pushRelation(refs, table, 'target');
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
case 'delete': {
|
|
1087
|
+
const table = ast.table;
|
|
1088
|
+
if (Array.isArray(table))
|
|
1089
|
+
for (const entry of table)
|
|
1090
|
+
pushRelation(refs, entry, 'target');
|
|
1091
|
+
else
|
|
1092
|
+
pushRelation(refs, table, 'target');
|
|
1093
|
+
if (Array.isArray(ast.from))
|
|
1094
|
+
for (const entry of ast.from)
|
|
1095
|
+
pushRelation(refs, entry, 'from');
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
case 'create': {
|
|
1099
|
+
const table = ast.table;
|
|
1100
|
+
if (Array.isArray(table))
|
|
1101
|
+
for (const entry of table)
|
|
1102
|
+
pushRelation(refs, entry, 'target');
|
|
1103
|
+
else
|
|
1104
|
+
pushRelation(refs, table, 'target');
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1107
|
+
case 'drop': {
|
|
1108
|
+
if (Array.isArray(ast.name))
|
|
1109
|
+
for (const entry of ast.name)
|
|
1110
|
+
pushRelation(refs, entry, 'drop');
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
default:
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function collectFromEntry(entry, refs) {
|
|
1118
|
+
if (entry === null || typeof entry !== 'object')
|
|
1119
|
+
return;
|
|
1120
|
+
const e = entry;
|
|
1121
|
+
if (e.type === 'dual')
|
|
1122
|
+
return;
|
|
1123
|
+
// A subquery in FROM (`FROM (SELECT …)`) has no relation name of its own; its
|
|
1124
|
+
// inner SELECT references the real tables.
|
|
1125
|
+
if ('expr' in e && e.expr && typeof e.expr === 'object') {
|
|
1126
|
+
const inner = e.expr.ast;
|
|
1127
|
+
if (inner && typeof inner === 'object')
|
|
1128
|
+
collectRelationsInto(inner, refs);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
const via = 'join' in e ? 'join' : 'from';
|
|
1132
|
+
pushRelation(refs, entry, via);
|
|
1133
|
+
}
|
|
1134
|
+
/** The names of the CTEs declared by a `WITH` clause, lowercased.
|
|
1135
|
+
* @param ast The parsed statement.
|
|
1136
|
+
* @returns The lowercased CTE names. */
|
|
1137
|
+
export function cteNames(ast) {
|
|
1138
|
+
const names = new Set();
|
|
1139
|
+
if (ast.type !== 'select' || !Array.isArray(ast.with))
|
|
1140
|
+
return names;
|
|
1141
|
+
for (const cte of ast.with) {
|
|
1142
|
+
const value = cte?.name?.value;
|
|
1143
|
+
if (typeof value === 'string' && value.length > 0)
|
|
1144
|
+
names.add(value.toLowerCase());
|
|
1145
|
+
}
|
|
1146
|
+
return names;
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* The set of table names a statement references, CTE aliases excluded. This is
|
|
1150
|
+
* the AST replacement for `extractTables`: walk FROM/JOIN/INTO/target clauses,
|
|
1151
|
+
* collect relation nodes, then drop the CTE names declared in `WITH`. A CTE
|
|
1152
|
+
* name and a stored table of the same name resolve to the CTE, so a CTE
|
|
1153
|
+
* declaration wins.
|
|
1154
|
+
* @param ast The parsed statement.
|
|
1155
|
+
* @returns The referenced table names, de-duplicated, with CTE aliases excluded.
|
|
1156
|
+
*/
|
|
1157
|
+
export function extractTableNames(ast) {
|
|
1158
|
+
const cte = cteNames(ast);
|
|
1159
|
+
const seen = new Set();
|
|
1160
|
+
const names = [];
|
|
1161
|
+
for (const ref of collectRelations(ast)) {
|
|
1162
|
+
const lower = ref.name.toLowerCase();
|
|
1163
|
+
if (cte.has(lower))
|
|
1164
|
+
continue;
|
|
1165
|
+
if (seen.has(lower))
|
|
1166
|
+
continue;
|
|
1167
|
+
seen.add(lower);
|
|
1168
|
+
names.push(ref.name);
|
|
1169
|
+
}
|
|
1170
|
+
return names;
|
|
1171
|
+
}
|
|
1172
|
+
/** node-sql-parser statement `type` values (AST nodes that are statements, not
|
|
1173
|
+
* expressions). A nested statement is any object carrying one of these. */
|
|
1174
|
+
const STATEMENT_TYPES = new Set([
|
|
1175
|
+
'select', 'insert', 'replace', 'update', 'delete', 'create', 'alter', 'drop', 'use',
|
|
1176
|
+
]);
|
|
1177
|
+
function isStatementNode(node) {
|
|
1178
|
+
if (node === null || typeof node !== 'object' || Array.isArray(node))
|
|
1179
|
+
return false;
|
|
1180
|
+
const type = node.type;
|
|
1181
|
+
return typeof type === 'string' && STATEMENT_TYPES.has(type);
|
|
1182
|
+
}
|
|
1183
|
+
/** Yield every statement reachable from `node`, excluding `node` itself. */
|
|
1184
|
+
function* nestedStatements(node) {
|
|
1185
|
+
if (node === null || typeof node !== 'object')
|
|
1186
|
+
return;
|
|
1187
|
+
if (Array.isArray(node)) {
|
|
1188
|
+
for (const item of node)
|
|
1189
|
+
yield* nestedStatements(item);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
for (const value of Object.values(node)) {
|
|
1193
|
+
if (value === null || typeof value !== 'object')
|
|
1194
|
+
continue;
|
|
1195
|
+
if (isStatementNode(value))
|
|
1196
|
+
yield value;
|
|
1197
|
+
else
|
|
1198
|
+
yield* nestedStatements(value);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/** Read a relation entry's bare table name and schema qualifier. */
|
|
1202
|
+
function relationParts(entry) {
|
|
1203
|
+
if (entry === null || typeof entry !== 'object')
|
|
1204
|
+
return null;
|
|
1205
|
+
const table = entry.table;
|
|
1206
|
+
if (typeof table !== 'string' || table.length === 0)
|
|
1207
|
+
return null;
|
|
1208
|
+
const db = entry.db;
|
|
1209
|
+
return { table, db: typeof db === 'string' && db.length > 0 ? db : null };
|
|
1210
|
+
}
|
|
1211
|
+
function pushTypedRelation(out, entry, type, conflictClauseTruncated = false) {
|
|
1212
|
+
const parts = relationParts(entry);
|
|
1213
|
+
if (parts !== null) {
|
|
1214
|
+
out.push({ table: parts.table, db: parts.db, type, ...(conflictClauseTruncated ? { conflictClauseTruncated: true } : {}) });
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
/** Collect CTE names across a statement and its nested statements into `names`
|
|
1218
|
+
* (lowercased). */
|
|
1219
|
+
function collectCteNamesRecursive(node, names) {
|
|
1220
|
+
if (node.type === 'select' && Array.isArray(node.with)) {
|
|
1221
|
+
for (const cte of node.with) {
|
|
1222
|
+
const value = cte?.name?.value;
|
|
1223
|
+
if (typeof value === 'string' && value.length > 0)
|
|
1224
|
+
names.add(value.toLowerCase());
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
for (const nested of nestedStatements(node))
|
|
1228
|
+
collectCteNamesRecursive(nested, names);
|
|
1229
|
+
}
|
|
1230
|
+
/** Collect `ALTER TABLE … RENAME TO` targets (transient names, not stored
|
|
1231
|
+
* tables) across a statement and its nested statements. */
|
|
1232
|
+
function collectRenameTargetsRecursive(node, names) {
|
|
1233
|
+
if (node.type === 'alter') {
|
|
1234
|
+
const expr = node.expr;
|
|
1235
|
+
if (Array.isArray(expr)) {
|
|
1236
|
+
for (const op of expr) {
|
|
1237
|
+
if (op && typeof op === 'object' && op.action === 'rename') {
|
|
1238
|
+
const target = op.table;
|
|
1239
|
+
if (typeof target === 'string' && target.length > 0)
|
|
1240
|
+
names.add(target.toLowerCase());
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
for (const nested of nestedStatements(node))
|
|
1246
|
+
collectRenameTargetsRecursive(nested, names);
|
|
1247
|
+
}
|
|
1248
|
+
/** Emit the relations a statement names directly, with the statement's verb. A
|
|
1249
|
+
* DELETE's `from` duplicates its `table` (node-sql-parser carries both), so only
|
|
1250
|
+
* `table` is emitted as the write; a `CREATE` is emitted only for `CREATE TABLE`
|
|
1251
|
+
* (a `CREATE INDEX`/`CREATE VIEW` names no stored table the rules track). */
|
|
1252
|
+
function emitDirectRelations(ast, out, conflictClauseTruncated) {
|
|
1253
|
+
const table = ast.table;
|
|
1254
|
+
switch (ast.type) {
|
|
1255
|
+
case 'select': {
|
|
1256
|
+
const from = ast.from;
|
|
1257
|
+
if (Array.isArray(from))
|
|
1258
|
+
for (const entry of from)
|
|
1259
|
+
pushTypedRelation(out, entry, 'select', conflictClauseTruncated);
|
|
1260
|
+
else
|
|
1261
|
+
pushTypedRelation(out, from, 'select', conflictClauseTruncated);
|
|
1262
|
+
break;
|
|
1263
|
+
}
|
|
1264
|
+
case 'insert':
|
|
1265
|
+
case 'replace': {
|
|
1266
|
+
if (Array.isArray(table))
|
|
1267
|
+
for (const entry of table)
|
|
1268
|
+
pushTypedRelation(out, entry, 'insert', conflictClauseTruncated);
|
|
1269
|
+
else
|
|
1270
|
+
pushTypedRelation(out, table, 'insert', conflictClauseTruncated);
|
|
1271
|
+
break;
|
|
1272
|
+
}
|
|
1273
|
+
case 'update': {
|
|
1274
|
+
if (Array.isArray(table))
|
|
1275
|
+
for (const entry of table)
|
|
1276
|
+
pushTypedRelation(out, entry, 'update', conflictClauseTruncated);
|
|
1277
|
+
else
|
|
1278
|
+
pushTypedRelation(out, table, 'update', conflictClauseTruncated);
|
|
1279
|
+
break;
|
|
1280
|
+
}
|
|
1281
|
+
case 'delete': {
|
|
1282
|
+
if (Array.isArray(table))
|
|
1283
|
+
for (const entry of table)
|
|
1284
|
+
pushTypedRelation(out, entry, 'delete', conflictClauseTruncated);
|
|
1285
|
+
else
|
|
1286
|
+
pushTypedRelation(out, table, 'delete', conflictClauseTruncated);
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
case 'create': {
|
|
1290
|
+
if (ast.keyword === 'table') {
|
|
1291
|
+
if (Array.isArray(table))
|
|
1292
|
+
for (const entry of table)
|
|
1293
|
+
pushTypedRelation(out, entry, 'create', conflictClauseTruncated);
|
|
1294
|
+
else
|
|
1295
|
+
pushTypedRelation(out, table, 'create', conflictClauseTruncated);
|
|
1296
|
+
}
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
default:
|
|
1300
|
+
break;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function walkTyped(ast, out, conflictClauseTruncated) {
|
|
1304
|
+
// The truncation flag applies to the *top-level* statement only — a nested
|
|
1305
|
+
// statement (CTE body, subquery) carries no `ON CONFLICT` of its own.
|
|
1306
|
+
emitDirectRelations(ast, out, conflictClauseTruncated);
|
|
1307
|
+
for (const nested of nestedStatements(ast))
|
|
1308
|
+
walkTyped(nested, out, false);
|
|
1309
|
+
}
|
|
1310
|
+
/** The typed relations of a parsed SQL program (one or more statements), with
|
|
1311
|
+
* CTE names and ALTER RENAME targets subtracted and nested statements descended.
|
|
1312
|
+
* `truncatedConflictIndices` names the statements whose text carried a top-level
|
|
1313
|
+
* `ON CONFLICT` clause (truncated before parsing); their relations are flagged
|
|
1314
|
+
* `conflictClauseTruncated` so a write is recorded as "upsert present but
|
|
1315
|
+
* unread" rather than a plain INSERT.
|
|
1316
|
+
* @param statements The parsed statements of the program.
|
|
1317
|
+
* @param truncatedConflictIndices Indices into `statements` whose `ON CONFLICT` clause was truncated.
|
|
1318
|
+
* @returns The typed relations, with CTE names and ALTER RENAME targets subtracted. */
|
|
1319
|
+
export function collectTypedRelations(statements, truncatedConflictIndices = new Set()) {
|
|
1320
|
+
const cte = new Set();
|
|
1321
|
+
const rename = new Set();
|
|
1322
|
+
for (const stmt of statements) {
|
|
1323
|
+
collectCteNamesRecursive(stmt, cte);
|
|
1324
|
+
collectRenameTargetsRecursive(stmt, rename);
|
|
1325
|
+
}
|
|
1326
|
+
const out = [];
|
|
1327
|
+
statements.forEach((stmt, index) => {
|
|
1328
|
+
walkTyped(stmt, out, truncatedConflictIndices.has(index));
|
|
1329
|
+
});
|
|
1330
|
+
return out.filter((r) => !cte.has(r.table.toLowerCase()) && !rename.has(r.table.toLowerCase()));
|
|
1331
|
+
}
|
|
1332
|
+
/** True when an expression is a literal `1` (the `1=1` tautology operand). */
|
|
1333
|
+
function isLiteralOne(node) {
|
|
1334
|
+
if (node === null || typeof node !== 'object')
|
|
1335
|
+
return false;
|
|
1336
|
+
const n = node;
|
|
1337
|
+
return n.type === 'number' && n.value === 1;
|
|
1338
|
+
}
|
|
1339
|
+
/** True when a WHERE expression reduces to "always true" (nothing is limited). */
|
|
1340
|
+
function isTautologyExpr(node) {
|
|
1341
|
+
if (node === null || typeof node !== 'object')
|
|
1342
|
+
return false;
|
|
1343
|
+
const n = node;
|
|
1344
|
+
if (n.type === 'bool')
|
|
1345
|
+
return n.value === true;
|
|
1346
|
+
if (n.type !== 'binary_expr')
|
|
1347
|
+
return false;
|
|
1348
|
+
const op = typeof n.operator === 'string' ? n.operator.toUpperCase() : '';
|
|
1349
|
+
if (op === 'AND')
|
|
1350
|
+
return isTautologyExpr(n.left) && isTautologyExpr(n.right);
|
|
1351
|
+
if (op === '=')
|
|
1352
|
+
return isLiteralOne(n.left) && isLiteralOne(n.right);
|
|
1353
|
+
return false;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Row-limiting facts about a statement — the AST replacement for site #2's
|
|
1357
|
+
* `hasQueryFilter` + `whereClauseIsTautology`. A WHERE carrying a real predicate
|
|
1358
|
+
* (any non-tautology), a HAVING, or a LIMIT limits rows; a bare `WHERE 1=1` does
|
|
1359
|
+
* not.
|
|
1360
|
+
* @param ast The parsed statement.
|
|
1361
|
+
* @returns Row-limiting facts: presence of WHERE/HAVING/LIMIT and whether the WHERE is tautological.
|
|
1362
|
+
*/
|
|
1363
|
+
export function whereFacts(ast) {
|
|
1364
|
+
const where = ast.where ?? null;
|
|
1365
|
+
const having = ast.having ?? null;
|
|
1366
|
+
const limit = ast.limit ?? null;
|
|
1367
|
+
return {
|
|
1368
|
+
hasWhere: where !== null && where !== undefined,
|
|
1369
|
+
hasHaving: having !== null && having !== undefined,
|
|
1370
|
+
// node-sql-parser emits `limit: { seperator: '', value: [] }` for a SELECT
|
|
1371
|
+
// with no LIMIT clause — a present-but-empty object, not `null`. Reading the
|
|
1372
|
+
// bare `limit != null` would mark every query "limited" and silence
|
|
1373
|
+
// `unfiltered-query`'s filterless-read/write halves. An empty `value` array
|
|
1374
|
+
// is "no limit"; a non-empty one (or a scalar/array form a dialect may emit)
|
|
1375
|
+
// is a real LIMIT.
|
|
1376
|
+
hasLimit: hasLimitClause(limit),
|
|
1377
|
+
whereIsTautology: where !== null && where !== undefined && isTautologyExpr(where),
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
/** True when a parsed statement carries a real LIMIT. node-sql-parser's
|
|
1381
|
+
* `limit` is `{ seperator, value[] }`; `value.length === 0` means the SELECT
|
|
1382
|
+
* has no LIMIT clause, so the object alone does not prove one. */
|
|
1383
|
+
function hasLimitClause(limit) {
|
|
1384
|
+
if (limit === null || limit === undefined)
|
|
1385
|
+
return false;
|
|
1386
|
+
if (Array.isArray(limit))
|
|
1387
|
+
return limit.length > 0;
|
|
1388
|
+
if (typeof limit === 'object') {
|
|
1389
|
+
const value = limit.value;
|
|
1390
|
+
if (Array.isArray(value))
|
|
1391
|
+
return value.length > 0;
|
|
1392
|
+
return value !== null && value !== undefined;
|
|
1393
|
+
}
|
|
1394
|
+
return true;
|
|
1395
|
+
}
|
|
1396
|
+
// ─── Write verbs ─────────────────────────────────────────────────────────────
|
|
1397
|
+
/**
|
|
1398
|
+
* True when a statement is a write (INSERT/DELETE/UPDATE/REPLACE) — the AST
|
|
1399
|
+
* replacement for site #3's `hasWriteVerb`, which had to regex `REPLACE INTO`
|
|
1400
|
+
* as a two-word clause to avoid the `REPLACE()` string function. The statement
|
|
1401
|
+
* kind is that distinction, structurally.
|
|
1402
|
+
*/
|
|
1403
|
+
export function isWriteStatement(ast) {
|
|
1404
|
+
return ast.type === 'insert' || ast.type === 'delete' || ast.type === 'update' || ast.type === 'replace';
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* True when a statement mass-mutates existing rows (`UPDATE … SET`), the AST
|
|
1408
|
+
* replacement for `hasMassWriteVerb`. `DELETE` is deliberately excluded (Spec 68
|
|
1409
|
+
* disposition (a)); `INSERT`/`REPLACE` target specific rows.
|
|
1410
|
+
*/
|
|
1411
|
+
export function isMassWriteStatement(ast) {
|
|
1412
|
+
return ast.type === 'update';
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* True when a statement is an upsert — a `REPLACE`, or an `INSERT` carrying an
|
|
1416
|
+
* `ON CONFLICT` / `ON DUPLICATE KEY` clause — the AST replacement for
|
|
1417
|
+
* `isUpsertForm`. An upsert is keyed by construction, so it never reads as an
|
|
1418
|
+
* "unfiltered" mass write.
|
|
1419
|
+
* @param ast The parsed statement.
|
|
1420
|
+
* @returns True when the statement is an upsert (`REPLACE`, or `INSERT` with an `OR` / `ON DUPLICATE KEY` clause).
|
|
1421
|
+
*/
|
|
1422
|
+
export function isUpsertStatement(ast) {
|
|
1423
|
+
if (ast.type === 'replace')
|
|
1424
|
+
return true;
|
|
1425
|
+
if (ast.type !== 'insert')
|
|
1426
|
+
return false;
|
|
1427
|
+
// SQLite upsert spelling is `INSERT OR IGNORE/REPLACE/ABORT/FAIL/ROLLBACK`,
|
|
1428
|
+
// carried on the `or` field; the MySQL `ON DUPLICATE KEY UPDATE` spelling lands
|
|
1429
|
+
// on `on_duplicate_update`. Postgres `ON CONFLICT` does not parse under the
|
|
1430
|
+
// sqlite dialect and is a parse failure, which is the honest result.
|
|
1431
|
+
const raw = ast;
|
|
1432
|
+
const hasOr = raw.or !== null && raw.or !== undefined;
|
|
1433
|
+
const hasOnDuplicate = raw.on_duplicate_update !== null && raw.on_duplicate_update !== undefined;
|
|
1434
|
+
return hasOr || hasOnDuplicate;
|
|
1435
|
+
}
|
|
1436
|
+
// ─── Insert columns ──────────────────────────────────────────────────────────
|
|
1437
|
+
/**
|
|
1438
|
+
* The column list of an INSERT/REPLACE — the AST replacement for site #6's
|
|
1439
|
+
* `rawInsertColumnList`. `null` columns (an `INSERT … VALUES` with no column
|
|
1440
|
+
* list) yields an empty array, which is a distinct, honest result.
|
|
1441
|
+
* @param ast The parsed statement.
|
|
1442
|
+
* @returns The INSERT/REPLACE column list, or an empty array when there is none.
|
|
1443
|
+
*/
|
|
1444
|
+
export function insertColumns(ast) {
|
|
1445
|
+
if (ast.type !== 'insert' && ast.type !== 'replace')
|
|
1446
|
+
return [];
|
|
1447
|
+
const columns = ast.columns;
|
|
1448
|
+
if (!Array.isArray(columns))
|
|
1449
|
+
return [];
|
|
1450
|
+
return columns.filter((c) => typeof c === 'string');
|
|
1451
|
+
}
|
|
1452
|
+
// ─── Tenant predicate (org filter) ───────────────────────────────────────────
|
|
1453
|
+
/**
|
|
1454
|
+
* The column references that appear as *predicate operands* in a statement's
|
|
1455
|
+
* WHERE tree — the AST replacement for site #5's four regex families, collapsed
|
|
1456
|
+
* to one walk. A column name in the SELECT list, an INSERT column list, or a
|
|
1457
|
+
* comment is *not* an operand, so a projection can no longer masquerade as a
|
|
1458
|
+
* filter (the Spec 68 Thing 2 `sample_ownership` defect). A quoted identifier on
|
|
1459
|
+
* the left of a comparison (`"org_id" = ?`, the knex/Postgres form) is collected
|
|
1460
|
+
* too, because under a PostgreSQL-style dialect it is a column, not a string
|
|
1461
|
+
* literal.
|
|
1462
|
+
* @param ast The parsed statement.
|
|
1463
|
+
* @returns The lowercased column names that appear as WHERE-tree predicate operands.
|
|
1464
|
+
*/
|
|
1465
|
+
export function whereColumnRefs(ast) {
|
|
1466
|
+
const refs = new Set();
|
|
1467
|
+
const where = ast.where;
|
|
1468
|
+
collectWhereColumnRefs(where, refs);
|
|
1469
|
+
return refs;
|
|
1470
|
+
}
|
|
1471
|
+
function collectWhereColumnRefs(node, refs) {
|
|
1472
|
+
if (node === null || typeof node !== 'object')
|
|
1473
|
+
return;
|
|
1474
|
+
if (Array.isArray(node)) {
|
|
1475
|
+
for (const item of node)
|
|
1476
|
+
collectWhereColumnRefs(item, refs);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const n = node;
|
|
1480
|
+
if (n.type === 'column_ref') {
|
|
1481
|
+
const column = n.column;
|
|
1482
|
+
if (typeof column === 'string' && column.length > 0)
|
|
1483
|
+
refs.add(column.toLowerCase());
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
// A double-quoted identifier on the left of a comparison is a column under
|
|
1487
|
+
// PostgreSQL/knex; its node-sql-parser shape is `double_quote_string`.
|
|
1488
|
+
if (n.type === 'double_quote_string') {
|
|
1489
|
+
const value = n.value;
|
|
1490
|
+
if (typeof value === 'string' && value.length > 0)
|
|
1491
|
+
refs.add(value.toLowerCase());
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
// Recurse into binary/boolean/logical expressions and their operands.
|
|
1495
|
+
for (const key of ['left', 'right', 'expr', 'args', 'value', 'columns']) {
|
|
1496
|
+
const child = n[key];
|
|
1497
|
+
if (child !== null && typeof child === 'object')
|
|
1498
|
+
collectWhereColumnRefs(child, refs);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* True when a statement's WHERE clause applies an org/tenant-scoping predicate —
|
|
1503
|
+
* a `col op value` shape where `col` is in the tenant column set. This is the
|
|
1504
|
+
* AST form of `hasOrganizationFilter`; the tenant vocabulary is passed in (the
|
|
1505
|
+
* same vocabulary discovery uses), so the predicate and the tier set can never
|
|
1506
|
+
* read two different vocabularies.
|
|
1507
|
+
*
|
|
1508
|
+
* @param ast The parsed statement.
|
|
1509
|
+
* @param tenantColumns The tenant column names (lowercased) that count as scope.
|
|
1510
|
+
* @returns True when any WHERE predicate operand is a tenant column.
|
|
1511
|
+
*/
|
|
1512
|
+
export function hasTenantPredicate(ast, tenantColumns) {
|
|
1513
|
+
if (tenantColumns.size === 0)
|
|
1514
|
+
return false;
|
|
1515
|
+
const refs = whereColumnRefs(ast);
|
|
1516
|
+
for (const ref of refs) {
|
|
1517
|
+
if (tenantColumns.has(ref))
|
|
1518
|
+
return true;
|
|
1519
|
+
}
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
// ─── DDL facts ───────────────────────────────────────────────────────────────
|
|
1523
|
+
/** True when a statement is table DDL (`CREATE TABLE` / `DROP TABLE` / `ALTER TABLE`).
|
|
1524
|
+
* @param ast The parsed statement.
|
|
1525
|
+
* @returns True when the statement is `CREATE TABLE`, `DROP TABLE`, or `ALTER TABLE`. */
|
|
1526
|
+
export function isDdlStatement(ast) {
|
|
1527
|
+
if (ast.type === 'drop')
|
|
1528
|
+
return ast.keyword === 'table';
|
|
1529
|
+
if (ast.type === 'alter')
|
|
1530
|
+
return true;
|
|
1531
|
+
if (ast.type === 'create')
|
|
1532
|
+
return ast.keyword === 'table';
|
|
1533
|
+
return false;
|
|
1534
|
+
}
|
|
1535
|
+
/** The table names a DDL statement names (create target, drop names, alter target + rename).
|
|
1536
|
+
* @param ast The parsed statement.
|
|
1537
|
+
* @returns The table names the DDL statement names (create target, drop names, alter target + rename). */
|
|
1538
|
+
export function ddlTableNames(ast) {
|
|
1539
|
+
const names = new Set();
|
|
1540
|
+
for (const ref of collectRelations(ast))
|
|
1541
|
+
names.add(ref.name);
|
|
1542
|
+
if (ast.type === 'alter') {
|
|
1543
|
+
// `ALTER TABLE t RENAME TO newname` — the rename target is a table name.
|
|
1544
|
+
const expr = ast.expr;
|
|
1545
|
+
if (Array.isArray(expr)) {
|
|
1546
|
+
for (const op of expr) {
|
|
1547
|
+
if (op && typeof op === 'object' && op.action === 'rename') {
|
|
1548
|
+
const target = op.table;
|
|
1549
|
+
if (typeof target === 'string' && target.length > 0)
|
|
1550
|
+
names.add(target);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
return Array.from(names);
|
|
1556
|
+
}
|
|
1557
|
+
/** True when a constraint-flag field (`primary_key`, `unique`) is present and on. */
|
|
1558
|
+
function flagIsSet(value) {
|
|
1559
|
+
return value !== undefined && value !== null && value !== false;
|
|
1560
|
+
}
|
|
1561
|
+
/** The `not null` flag of a column definition — `nullable: { type: 'not null' }`
|
|
1562
|
+
* (or, on some dialects, a bare string). */
|
|
1563
|
+
function isNotNull(def) {
|
|
1564
|
+
const nullable = def.nullable;
|
|
1565
|
+
if (nullable === null || nullable === undefined)
|
|
1566
|
+
return false;
|
|
1567
|
+
if (typeof nullable === 'string')
|
|
1568
|
+
return nullable.toLowerCase() === 'not null';
|
|
1569
|
+
if (typeof nullable === 'object') {
|
|
1570
|
+
const t = nullable.type;
|
|
1571
|
+
return typeof t === 'string' && t.toLowerCase() === 'not null';
|
|
1572
|
+
}
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Extract every column defined by a `CREATE TABLE` (or `ALTER TABLE … ADD
|
|
1577
|
+
* COLUMN`) statement — the AST replacement for site #9's
|
|
1578
|
+
* `extractDdlTableColumns` + `leadingColumnName` + `splitColumnDefs`, which
|
|
1579
|
+
* hand-rolled paren-depth and quote tracking. The column name and its
|
|
1580
|
+
* constraints (primary-key / not-null / unique) come straight off the
|
|
1581
|
+
* definition nodes.
|
|
1582
|
+
* @param ast The parsed `CREATE TABLE` or `ALTER TABLE … ADD COLUMN` statement.
|
|
1583
|
+
* @returns Each column with its primary-key / not-null / unique flags and data type.
|
|
1584
|
+
*/
|
|
1585
|
+
export function ddlColumnDefinitions(ast) {
|
|
1586
|
+
const result = [];
|
|
1587
|
+
const tables = ddlTableNames(ast);
|
|
1588
|
+
const table = tables[0] ?? '';
|
|
1589
|
+
const emit = (d) => {
|
|
1590
|
+
const column = columnNameOf(d);
|
|
1591
|
+
if (column === null)
|
|
1592
|
+
return;
|
|
1593
|
+
const definition = d.definition;
|
|
1594
|
+
result.push({
|
|
1595
|
+
table,
|
|
1596
|
+
column,
|
|
1597
|
+
primaryKey: flagIsSet(d.primary_key),
|
|
1598
|
+
notNull: isNotNull(d),
|
|
1599
|
+
unique: flagIsSet(d.unique),
|
|
1600
|
+
dataType: typeof definition?.dataType === 'string' ? definition.dataType : null,
|
|
1601
|
+
});
|
|
1602
|
+
};
|
|
1603
|
+
if (ast.type === 'create') {
|
|
1604
|
+
const defs = ast.create_definitions;
|
|
1605
|
+
if (Array.isArray(defs)) {
|
|
1606
|
+
for (const def of defs)
|
|
1607
|
+
emit(def);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
else if (ast.type === 'alter') {
|
|
1611
|
+
const expr = ast.expr;
|
|
1612
|
+
if (Array.isArray(expr)) {
|
|
1613
|
+
for (const op of expr) {
|
|
1614
|
+
const d = op;
|
|
1615
|
+
if (d.action !== 'add')
|
|
1616
|
+
continue;
|
|
1617
|
+
emit(d);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
return result;
|
|
1622
|
+
}
|
|
1623
|
+
function columnNameOf(def) {
|
|
1624
|
+
if (def === null || typeof def !== 'object')
|
|
1625
|
+
return null;
|
|
1626
|
+
const column = def.column;
|
|
1627
|
+
if (column === null || typeof column !== 'object')
|
|
1628
|
+
return null;
|
|
1629
|
+
const name = column.column;
|
|
1630
|
+
return typeof name === 'string' && name.length > 0 ? name : null;
|
|
1631
|
+
}
|
|
1632
|
+
/** The column name of a `column_ref` node (`{ type: 'column_ref', column: 'a' }`). */
|
|
1633
|
+
function columnRefNameOf(node) {
|
|
1634
|
+
if (node === null || typeof node !== 'object')
|
|
1635
|
+
return null;
|
|
1636
|
+
const column = node.column;
|
|
1637
|
+
return typeof column === 'string' && column.length > 0 ? column : null;
|
|
1638
|
+
}
|
|
1639
|
+
/** The referenced table name of a `reference_definition`. */
|
|
1640
|
+
function referenceTableOf(ref) {
|
|
1641
|
+
if (ref === null || typeof ref !== 'object')
|
|
1642
|
+
return '';
|
|
1643
|
+
const table = ref.table;
|
|
1644
|
+
const entry = Array.isArray(table) ? table[0] : table;
|
|
1645
|
+
const name = entry?.table;
|
|
1646
|
+
return typeof name === 'string' ? name : '';
|
|
1647
|
+
}
|
|
1648
|
+
/** The referenced column names of a `reference_definition`. */
|
|
1649
|
+
function referenceColumnsOf(ref) {
|
|
1650
|
+
if (ref === null || typeof ref !== 'object')
|
|
1651
|
+
return [];
|
|
1652
|
+
const definition = ref.definition;
|
|
1653
|
+
if (!Array.isArray(definition))
|
|
1654
|
+
return [];
|
|
1655
|
+
const names = [];
|
|
1656
|
+
for (const rc of definition) {
|
|
1657
|
+
const name = columnRefNameOf(rc);
|
|
1658
|
+
if (name !== null)
|
|
1659
|
+
names.push(name);
|
|
1660
|
+
}
|
|
1661
|
+
return names;
|
|
1662
|
+
}
|
|
1663
|
+
/** The local column names of a table-level constraint's `definition` list. */
|
|
1664
|
+
function constraintColumnNames(def) {
|
|
1665
|
+
if (def === null || typeof def !== 'object')
|
|
1666
|
+
return [];
|
|
1667
|
+
const definition = def.definition;
|
|
1668
|
+
if (!Array.isArray(definition))
|
|
1669
|
+
return [];
|
|
1670
|
+
const names = [];
|
|
1671
|
+
for (const c of definition) {
|
|
1672
|
+
const name = columnRefNameOf(c);
|
|
1673
|
+
if (name !== null)
|
|
1674
|
+
names.push(name);
|
|
1675
|
+
}
|
|
1676
|
+
return names;
|
|
1677
|
+
}
|
|
1678
|
+
/** The normalized (`lower`) `constraint_type` of a constraint definition, or null. */
|
|
1679
|
+
function constraintTypeOf(def) {
|
|
1680
|
+
if (def === null || typeof def !== 'object')
|
|
1681
|
+
return null;
|
|
1682
|
+
const raw = def.constraint_type;
|
|
1683
|
+
return typeof raw === 'string' && raw.length > 0 ? raw.toLowerCase() : null;
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Extract the foreign-key constraints of a `CREATE TABLE` or `ALTER TABLE … ADD
|
|
1687
|
+
* CONSTRAINT … FOREIGN KEY` statement — the AST replacement for site #10's FK
|
|
1688
|
+
* regexes. Covers the three DDL spellings: column-level `REFERENCES`, table-level
|
|
1689
|
+
* `FOREIGN KEY (…) REFERENCES`, and ALTER `ADD CONSTRAINT … FOREIGN KEY`. The
|
|
1690
|
+
* referenced table and columns come straight off the `reference_definition` node.
|
|
1691
|
+
* @param ast The parsed `CREATE TABLE` or `ALTER TABLE` statement.
|
|
1692
|
+
* @returns The foreign-key constraints, with local columns and the referenced table/columns.
|
|
1693
|
+
*/
|
|
1694
|
+
export function ddlForeignKeys(ast) {
|
|
1695
|
+
if (ast.type !== 'create' && ast.type !== 'alter')
|
|
1696
|
+
return [];
|
|
1697
|
+
const result = [];
|
|
1698
|
+
const tables = ddlTableNames(ast);
|
|
1699
|
+
const table = tables[0] ?? '';
|
|
1700
|
+
const emit = (def) => {
|
|
1701
|
+
const ref = def.reference_definition;
|
|
1702
|
+
if (ref === null || typeof ref !== 'object' || ref === undefined)
|
|
1703
|
+
return;
|
|
1704
|
+
const localColumn = columnNameOf(def);
|
|
1705
|
+
const localColumns = constraintColumnNames(def);
|
|
1706
|
+
const columns = localColumn !== null ? [localColumn] : (localColumns.length > 0 ? localColumns : []);
|
|
1707
|
+
result.push({
|
|
1708
|
+
table,
|
|
1709
|
+
columns,
|
|
1710
|
+
referencesTable: referenceTableOf(ref),
|
|
1711
|
+
referencesColumns: referenceColumnsOf(ref),
|
|
1712
|
+
});
|
|
1713
|
+
};
|
|
1714
|
+
if (ast.type === 'create') {
|
|
1715
|
+
const defs = ast.create_definitions;
|
|
1716
|
+
if (Array.isArray(defs)) {
|
|
1717
|
+
for (const def of defs)
|
|
1718
|
+
emit(def);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
else {
|
|
1722
|
+
const expr = ast.expr;
|
|
1723
|
+
if (Array.isArray(expr)) {
|
|
1724
|
+
for (const op of expr) {
|
|
1725
|
+
const d = op;
|
|
1726
|
+
if (d.action !== 'add')
|
|
1727
|
+
continue;
|
|
1728
|
+
// `ALTER TABLE … ADD CONSTRAINT … FOREIGN KEY (…) REFERENCES …` carries the
|
|
1729
|
+
// constraint on `create_definitions` of the `add` op, not on the op itself.
|
|
1730
|
+
const cd = d.create_definitions;
|
|
1731
|
+
if (cd !== null && typeof cd === 'object' && cd !== undefined) {
|
|
1732
|
+
emit(cd);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return result;
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* The ordered CREATE / DROP / RENAME ops of a table-DDL statement — the AST
|
|
1741
|
+
* replacement for site #7's `DDL_RE` (which had to regex the whole
|
|
1742
|
+
* create/drop/alter-rename vocabulary as one alternation). The statement kind and
|
|
1743
|
+
* its target/rename nodes are read directly.
|
|
1744
|
+
* @param ast The parsed table-DDL statement.
|
|
1745
|
+
* @returns The ordered CREATE / DROP / RENAME ops.
|
|
1746
|
+
*/
|
|
1747
|
+
export function ddlMigrationOps(ast) {
|
|
1748
|
+
if (ast.type === 'create' && ast.keyword === 'table') {
|
|
1749
|
+
const tables = ddlTableNames(ast);
|
|
1750
|
+
return [{ op: 'CREATE', table: tables[0] ?? '' }];
|
|
1751
|
+
}
|
|
1752
|
+
if (ast.type === 'drop' && ast.keyword === 'table') {
|
|
1753
|
+
return ddlTableNames(ast).map((t) => ({ op: 'DROP', table: t }));
|
|
1754
|
+
}
|
|
1755
|
+
if (ast.type === 'alter') {
|
|
1756
|
+
const tables = ddlTableNames(ast);
|
|
1757
|
+
const target = tables[0] ?? '';
|
|
1758
|
+
const expr = ast.expr;
|
|
1759
|
+
if (Array.isArray(expr)) {
|
|
1760
|
+
for (const op of expr) {
|
|
1761
|
+
if (op && typeof op === 'object' && op.action === 'rename') {
|
|
1762
|
+
const newTable = op.table;
|
|
1763
|
+
if (typeof newTable === 'string' && newTable.length > 0) {
|
|
1764
|
+
return [{ op: 'RENAME', table: target, newTable }];
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return [];
|
|
1770
|
+
}
|
|
1771
|
+
return [];
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* The columns of a table-level or ALTER-added UNIQUE / PRIMARY KEY constraint —
|
|
1775
|
+
* the AST replacement for the table-level halves of `extractDdlUniqueColumns` and
|
|
1776
|
+
* `extractDdlPrimaryKeyColumns`, which had to hand-roll paren matching to find
|
|
1777
|
+
* the column list inside `UNIQUE (…)`. Column-level `UNIQUE` / `PRIMARY KEY` is
|
|
1778
|
+
* already carried by {@link ddlColumnDefinitions}.
|
|
1779
|
+
* @param ast The parsed `CREATE TABLE` or `ALTER TABLE` statement.
|
|
1780
|
+
* @param constraintType `'unique'` or `'primary key'`.
|
|
1781
|
+
* @returns The lowercased columns of table-level / ALTER-added constraints of that kind.
|
|
1782
|
+
*/
|
|
1783
|
+
export function ddlConstraintColumns(ast, constraintType) {
|
|
1784
|
+
const names = [];
|
|
1785
|
+
const collect = (def) => {
|
|
1786
|
+
if (def === null || typeof def !== 'object')
|
|
1787
|
+
return;
|
|
1788
|
+
if (constraintTypeOf(def) !== constraintType)
|
|
1789
|
+
return;
|
|
1790
|
+
for (const name of constraintColumnNames(def))
|
|
1791
|
+
names.push(name.toLowerCase());
|
|
1792
|
+
};
|
|
1793
|
+
if (ast.type === 'create') {
|
|
1794
|
+
const defs = ast.create_definitions;
|
|
1795
|
+
if (Array.isArray(defs))
|
|
1796
|
+
for (const def of defs)
|
|
1797
|
+
collect(def);
|
|
1798
|
+
}
|
|
1799
|
+
else if (ast.type === 'alter') {
|
|
1800
|
+
const expr = ast.expr;
|
|
1801
|
+
if (Array.isArray(expr)) {
|
|
1802
|
+
for (const op of expr) {
|
|
1803
|
+
const d = op;
|
|
1804
|
+
if (d.action !== 'add')
|
|
1805
|
+
continue;
|
|
1806
|
+
collect(d.create_definitions);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
return names;
|
|
1811
|
+
}
|
|
1812
|
+
// ─── Statement kind convenience ──────────────────────────────────────────────
|
|
1813
|
+
/** The {@link SqlStatementKind} of a parsed statement. */
|
|
1814
|
+
export function kindOf(ast) {
|
|
1815
|
+
return statementKind(ast.type);
|
|
1816
|
+
}
|
|
1817
|
+
//# sourceMappingURL=sqlAst.js.map
|