@archpilotlabs/archpilot 0.2.2 → 0.2.3
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/dist/archpilot-cli.js +516 -105
- package/dist/cli/src/archpilot-cli.js +5523 -0
- package/dist/cli/src/archpilot-cli.js.map +1 -0
- package/dist/core/src/adr/adrLoader.js +97 -0
- package/dist/core/src/adr/adrLoader.js.map +1 -0
- package/dist/core/src/adr/adrParser.js +77 -0
- package/dist/core/src/adr/adrParser.js.map +1 -0
- package/dist/core/src/adr/adrTemplate.js +45 -0
- package/dist/core/src/adr/adrTemplate.js.map +1 -0
- package/dist/core/src/adr/adrTypes.js +3 -0
- package/dist/core/src/adr/adrTypes.js.map +1 -0
- package/dist/core/src/adr/adrValidation.js +160 -0
- package/dist/core/src/adr/adrValidation.js.map +1 -0
- package/dist/core/src/adr/index.js +9 -0
- package/dist/core/src/adr/index.js.map +1 -0
- package/dist/core/src/adrManagement.js +174 -0
- package/dist/core/src/adrManagement.js.map +1 -0
- package/dist/core/src/adrRequirementStatus.js +62 -0
- package/dist/core/src/adrRequirementStatus.js.map +1 -0
- package/dist/core/src/adrScaffoldGeneration.js +325 -0
- package/dist/core/src/adrScaffoldGeneration.js.map +1 -0
- package/dist/core/src/apiDesignAnalysis.js +869 -0
- package/dist/core/src/apiDesignAnalysis.js.map +1 -0
- package/dist/core/src/applicationLayeringAnalysis.js +920 -0
- package/dist/core/src/applicationLayeringAnalysis.js.map +1 -0
- package/dist/core/src/applyRulePack.js +117 -0
- package/dist/core/src/applyRulePack.js.map +1 -0
- package/dist/core/src/architectureBaseline.js +271 -0
- package/dist/core/src/architectureBaseline.js.map +1 -0
- package/dist/core/src/architectureComponentDiscoveryDefaults.js +139 -0
- package/dist/core/src/architectureComponentDiscoveryDefaults.js.map +1 -0
- package/dist/core/src/architectureDependencyContext.js +316 -0
- package/dist/core/src/architectureDependencyContext.js.map +1 -0
- package/dist/core/src/architectureDriftDetection.js +96 -0
- package/dist/core/src/architectureDriftDetection.js.map +1 -0
- package/dist/core/src/architectureDriftTimeline.js +221 -0
- package/dist/core/src/architectureDriftTimeline.js.map +1 -0
- package/dist/core/src/architectureGovernanceBaseline.js +469 -0
- package/dist/core/src/architectureGovernanceBaseline.js.map +1 -0
- package/dist/core/src/architectureHealthHistory.js +208 -0
- package/dist/core/src/architectureHealthHistory.js.map +1 -0
- package/dist/core/src/architectureHealthScore.js +657 -0
- package/dist/core/src/architectureHealthScore.js.map +1 -0
- package/dist/core/src/architectureInit.js +826 -0
- package/dist/core/src/architectureInit.js.map +1 -0
- package/dist/core/src/architectureMap.js +727 -0
- package/dist/core/src/architectureMap.js.map +1 -0
- package/dist/core/src/architectureMigrationPreview.js +467 -0
- package/dist/core/src/architectureMigrationPreview.js.map +1 -0
- package/dist/core/src/architectureModelRefresh.js +209 -0
- package/dist/core/src/architectureModelRefresh.js.map +1 -0
- package/dist/core/src/architectureReportWorkflow.js +139 -0
- package/dist/core/src/architectureReportWorkflow.js.map +1 -0
- package/dist/core/src/architectureValidation.js +3879 -0
- package/dist/core/src/architectureValidation.js.map +1 -0
- package/dist/core/src/architectureWorkspaceModel.js +166 -0
- package/dist/core/src/architectureWorkspaceModel.js.map +1 -0
- package/dist/core/src/authorizationTenantAnalysis.js +652 -0
- package/dist/core/src/authorizationTenantAnalysis.js.map +1 -0
- package/dist/core/src/bootstrapArchitectureConfig.js +150 -0
- package/dist/core/src/bootstrapArchitectureConfig.js.map +1 -0
- package/dist/core/src/changeRisk/changeRiskAssessment.js +251 -0
- package/dist/core/src/changeRisk/changeRiskAssessment.js.map +1 -0
- package/dist/core/src/changeRisk/changeRiskTypes.js +3 -0
- package/dist/core/src/changeRisk/changeRiskTypes.js.map +1 -0
- package/dist/core/src/changeRisk/index.js +6 -0
- package/dist/core/src/changeRisk/index.js.map +1 -0
- package/dist/core/src/ci/githubInlineAnnotations.js +245 -0
- package/dist/core/src/ci/githubInlineAnnotations.js.map +1 -0
- package/dist/core/src/ci/githubPrCommentAdapter.js +232 -0
- package/dist/core/src/ci/githubPrCommentAdapter.js.map +1 -0
- package/dist/core/src/ciWorkflowTemplates.js +1042 -0
- package/dist/core/src/ciWorkflowTemplates.js.map +1 -0
- package/dist/core/src/compliance/complianceExportBuilder.js +122 -0
- package/dist/core/src/compliance/complianceExportBuilder.js.map +1 -0
- package/dist/core/src/compliance/complianceExportTypes.js +3 -0
- package/dist/core/src/compliance/complianceExportTypes.js.map +1 -0
- package/dist/core/src/compliance/complianceExportWriter.js +48 -0
- package/dist/core/src/compliance/complianceExportWriter.js.map +1 -0
- package/dist/core/src/compliance/complianceRulePackResolver.js +62 -0
- package/dist/core/src/compliance/complianceRulePackResolver.js.map +1 -0
- package/dist/core/src/config/index.js +17 -0
- package/dist/core/src/config/index.js.map +1 -0
- package/dist/core/src/config/projectConfig.js +224 -0
- package/dist/core/src/config/projectConfig.js.map +1 -0
- package/dist/core/src/config/projectIdentity.js +160 -0
- package/dist/core/src/config/projectIdentity.js.map +1 -0
- package/dist/core/src/dataQueryRiskDetection.js +1203 -0
- package/dist/core/src/dataQueryRiskDetection.js.map +1 -0
- package/dist/core/src/dependencyValidation.js +2557 -0
- package/dist/core/src/dependencyValidation.js.map +1 -0
- package/dist/core/src/discoverAdrs.js +15 -0
- package/dist/core/src/discoverAdrs.js.map +1 -0
- package/dist/core/src/drift/driftComparator.js +49 -0
- package/dist/core/src/drift/driftComparator.js.map +1 -0
- package/dist/core/src/drift/driftSnapshotLoader.js +136 -0
- package/dist/core/src/drift/driftSnapshotLoader.js.map +1 -0
- package/dist/core/src/drift/driftSnapshotTypes.js +3 -0
- package/dist/core/src/drift/driftSnapshotTypes.js.map +1 -0
- package/dist/core/src/drift/driftSnapshotWriter.js +48 -0
- package/dist/core/src/drift/driftSnapshotWriter.js.map +1 -0
- package/dist/core/src/drift/index.js +6 -0
- package/dist/core/src/drift/index.js.map +1 -0
- package/dist/core/src/eventArchitectureAnalysis.js +925 -0
- package/dist/core/src/eventArchitectureAnalysis.js.map +1 -0
- package/dist/core/src/findingTargetResolution.js +117 -0
- package/dist/core/src/findingTargetResolution.js.map +1 -0
- package/dist/core/src/generateArchitectureDiagrams.js +57 -0
- package/dist/core/src/generateArchitectureDiagrams.js.map +1 -0
- package/dist/core/src/generateImpactAnalysis.js +366 -0
- package/dist/core/src/generateImpactAnalysis.js.map +1 -0
- package/dist/core/src/generateOnboardingDocs.js +262 -0
- package/dist/core/src/generateOnboardingDocs.js.map +1 -0
- package/dist/core/src/governance/cloudConnection.js +203 -0
- package/dist/core/src/governance/cloudConnection.js.map +1 -0
- package/dist/core/src/governance/governanceContracts.js +129 -0
- package/dist/core/src/governance/governanceContracts.js.map +1 -0
- package/dist/core/src/governance/governanceGitMetadata.js +78 -0
- package/dist/core/src/governance/governanceGitMetadata.js.map +1 -0
- package/dist/core/src/governance/governanceUploadClient.js +119 -0
- package/dist/core/src/governance/governanceUploadClient.js.map +1 -0
- package/dist/core/src/governance/governanceUploadLimitErrorFormatting.js +155 -0
- package/dist/core/src/governance/governanceUploadLimitErrorFormatting.js.map +1 -0
- package/dist/core/src/governance/governanceUploadPayload.js +439 -0
- package/dist/core/src/governance/governanceUploadPayload.js.map +1 -0
- package/dist/core/src/governance/runGovernanceUpload.js +103 -0
- package/dist/core/src/governance/runGovernanceUpload.js.map +1 -0
- package/dist/core/src/impactAnalysisEngine.js +376 -0
- package/dist/core/src/impactAnalysisEngine.js.map +1 -0
- package/dist/core/src/initializationState.js +99 -0
- package/dist/core/src/initializationState.js.map +1 -0
- package/dist/core/src/managedArtifacts.js +151 -0
- package/dist/core/src/managedArtifacts.js.map +1 -0
- package/dist/core/src/moduleContractValidation.js +266 -0
- package/dist/core/src/moduleContractValidation.js.map +1 -0
- package/dist/core/src/moduleContractsGenerator.js +337 -0
- package/dist/core/src/moduleContractsGenerator.js.map +1 -0
- package/dist/core/src/moduleDiscovery.js +416 -0
- package/dist/core/src/moduleDiscovery.js.map +1 -0
- package/dist/core/src/moduleScope.js +426 -0
- package/dist/core/src/moduleScope.js.map +1 -0
- package/dist/core/src/onboardingGuidance.js +74 -0
- package/dist/core/src/onboardingGuidance.js.map +1 -0
- package/dist/core/src/operationalLogging.js +7 -0
- package/dist/core/src/operationalLogging.js.map +1 -0
- package/dist/core/src/packageVersion.js +52 -0
- package/dist/core/src/packageVersion.js.map +1 -0
- package/dist/core/src/policy/effectivePolicyResolver.js +91 -0
- package/dist/core/src/policy/effectivePolicyResolver.js.map +1 -0
- package/dist/core/src/policy/exceptionLoader.js +285 -0
- package/dist/core/src/policy/exceptionLoader.js.map +1 -0
- package/dist/core/src/policy/remotePolicyLoader.js +227 -0
- package/dist/core/src/policy/remotePolicyLoader.js.map +1 -0
- package/dist/core/src/policy/remotePolicyTypes.js +3 -0
- package/dist/core/src/policy/remotePolicyTypes.js.map +1 -0
- package/dist/core/src/policy/riskPolicyConfig.js +206 -0
- package/dist/core/src/policy/riskPolicyConfig.js.map +1 -0
- package/dist/core/src/policy/riskPolicyEnforcement.js +203 -0
- package/dist/core/src/policy/riskPolicyEnforcement.js.map +1 -0
- package/dist/core/src/policy/riskPolicyExitCode.js +16 -0
- package/dist/core/src/policy/riskPolicyExitCode.js.map +1 -0
- package/dist/core/src/policy/riskPolicyInitializer.js +90 -0
- package/dist/core/src/policy/riskPolicyInitializer.js.map +1 -0
- package/dist/core/src/quickFix/quickFixEngine.js +132 -0
- package/dist/core/src/quickFix/quickFixEngine.js.map +1 -0
- package/dist/core/src/quickFix/quickFixMessaging.js +15 -0
- package/dist/core/src/quickFix/quickFixMessaging.js.map +1 -0
- package/dist/core/src/quickFix/quickFixProviders.js +87 -0
- package/dist/core/src/quickFix/quickFixProviders.js.map +1 -0
- package/dist/core/src/quickFix/quickFixTypes.js +3 -0
- package/dist/core/src/quickFix/quickFixTypes.js.map +1 -0
- package/dist/core/src/remediation/fileHelpers.js +86 -0
- package/dist/core/src/remediation/fileHelpers.js.map +1 -0
- package/dist/core/src/remediation/fixRegistry.js +645 -0
- package/dist/core/src/remediation/fixRegistry.js.map +1 -0
- package/dist/core/src/remediation/index.js +8 -0
- package/dist/core/src/remediation/index.js.map +1 -0
- package/dist/core/src/remediation/remediationEngine.js +207 -0
- package/dist/core/src/remediation/remediationEngine.js.map +1 -0
- package/dist/core/src/reporting/adrReporter.js +59 -0
- package/dist/core/src/reporting/adrReporter.js.map +1 -0
- package/dist/core/src/reporting/architectureBaselineReporter.js +64 -0
- package/dist/core/src/reporting/architectureBaselineReporter.js.map +1 -0
- package/dist/core/src/reporting/architectureDiagramReporter.js +148 -0
- package/dist/core/src/reporting/architectureDiagramReporter.js.map +1 -0
- package/dist/core/src/reporting/architectureDriftTimelineReporter.js +44 -0
- package/dist/core/src/reporting/architectureDriftTimelineReporter.js.map +1 -0
- package/dist/core/src/reporting/architectureHealthHistoryReporter.js +51 -0
- package/dist/core/src/reporting/architectureHealthHistoryReporter.js.map +1 -0
- package/dist/core/src/reporting/architectureMapReporter.js +228 -0
- package/dist/core/src/reporting/architectureMapReporter.js.map +1 -0
- package/dist/core/src/reporting/architecturePrCommentReporter.js +324 -0
- package/dist/core/src/reporting/architecturePrCommentReporter.js.map +1 -0
- package/dist/core/src/reporting/architecturePrReviewReporter.js +389 -0
- package/dist/core/src/reporting/architecturePrReviewReporter.js.map +1 -0
- package/dist/core/src/reporting/architectureReportArtifacts.js +166 -0
- package/dist/core/src/reporting/architectureReportArtifacts.js.map +1 -0
- package/dist/core/src/reporting/architectureReviewReportGenerator.js +382 -0
- package/dist/core/src/reporting/architectureReviewReportGenerator.js.map +1 -0
- package/dist/core/src/reporting/architectureReviewReporter.js +1633 -0
- package/dist/core/src/reporting/architectureReviewReporter.js.map +1 -0
- package/dist/core/src/reporting/architectureRiskAssessment.js +101 -0
- package/dist/core/src/reporting/architectureRiskAssessment.js.map +1 -0
- package/dist/core/src/reporting/architectureScoreBadge.js +177 -0
- package/dist/core/src/reporting/architectureScoreBadge.js.map +1 -0
- package/dist/core/src/reporting/architectureSupplementalReporters.js +124 -0
- package/dist/core/src/reporting/architectureSupplementalReporters.js.map +1 -0
- package/dist/core/src/reporting/changeRiskAssessment.js +6 -0
- package/dist/core/src/reporting/changeRiskAssessment.js.map +1 -0
- package/dist/core/src/reporting/changeRiskTypes.js +3 -0
- package/dist/core/src/reporting/changeRiskTypes.js.map +1 -0
- package/dist/core/src/reporting/impactAnalysisReporter.js +111 -0
- package/dist/core/src/reporting/impactAnalysisReporter.js.map +1 -0
- package/dist/core/src/reporting/onboardingDocsReporter.js +113 -0
- package/dist/core/src/reporting/onboardingDocsReporter.js.map +1 -0
- package/dist/core/src/reporting/rulePackReporter.js +61 -0
- package/dist/core/src/reporting/rulePackReporter.js.map +1 -0
- package/dist/core/src/reporting/safetyControlsDetection.js +343 -0
- package/dist/core/src/reporting/safetyControlsDetection.js.map +1 -0
- package/dist/core/src/resolveRuleAdrs.js +31 -0
- package/dist/core/src/resolveRuleAdrs.js.map +1 -0
- package/dist/core/src/reviewGenerationWorkflow.js +220 -0
- package/dist/core/src/reviewGenerationWorkflow.js.map +1 -0
- package/dist/core/src/rulePacks/index.js +17 -0
- package/dist/core/src/rulePacks/index.js.map +1 -0
- package/dist/core/src/rulePacks/layeredMonolithRulePack.js +45 -0
- package/dist/core/src/rulePacks/layeredMonolithRulePack.js.map +1 -0
- package/dist/core/src/rulePacks/modularMonolithRulePack.js +41 -0
- package/dist/core/src/rulePacks/modularMonolithRulePack.js.map +1 -0
- package/dist/core/src/rulePacks/types.js +3 -0
- package/dist/core/src/rulePacks/types.js.map +1 -0
- package/dist/core/src/setupNextSteps.js +939 -0
- package/dist/core/src/setupNextSteps.js.map +1 -0
- package/dist/core/src/setupRefresh.js +152 -0
- package/dist/core/src/setupRefresh.js.map +1 -0
- package/dist/core/src/smartInit/apiDbHintDetection.js +836 -0
- package/dist/core/src/smartInit/apiDbHintDetection.js.map +1 -0
- package/dist/core/src/smartInit/architectureProposalBuilder.js +1215 -0
- package/dist/core/src/smartInit/architectureProposalBuilder.js.map +1 -0
- package/dist/core/src/smartInit/architectureStyleDetection.js +472 -0
- package/dist/core/src/smartInit/architectureStyleDetection.js.map +1 -0
- package/dist/core/src/smartInit/archpilotIgnore.js +139 -0
- package/dist/core/src/smartInit/archpilotIgnore.js.map +1 -0
- package/dist/core/src/smartInit/componentDetection.js +1084 -0
- package/dist/core/src/smartInit/componentDetection.js.map +1 -0
- package/dist/core/src/smartInit/implementationProfileResolution.js +53 -0
- package/dist/core/src/smartInit/implementationProfileResolution.js.map +1 -0
- package/dist/core/src/smartInit/index.js +31 -0
- package/dist/core/src/smartInit/index.js.map +1 -0
- package/dist/core/src/smartInit/moduleDetection.js +982 -0
- package/dist/core/src/smartInit/moduleDetection.js.map +1 -0
- package/dist/core/src/smartInit/projectKindDetection.js +437 -0
- package/dist/core/src/smartInit/projectKindDetection.js.map +1 -0
- package/dist/core/src/smartInit/repoTopologyDetection.js +470 -0
- package/dist/core/src/smartInit/repoTopologyDetection.js.map +1 -0
- package/dist/core/src/smartInit/runContext.js +112 -0
- package/dist/core/src/smartInit/runContext.js.map +1 -0
- package/dist/core/src/smartInit/smartInitEngine.js +94 -0
- package/dist/core/src/smartInit/smartInitEngine.js.map +1 -0
- package/dist/core/src/smartInit/stackDetection.js +503 -0
- package/dist/core/src/smartInit/stackDetection.js.map +1 -0
- package/dist/core/src/smartInit/toolingHintDetection.js +345 -0
- package/dist/core/src/smartInit/toolingHintDetection.js.map +1 -0
- package/dist/core/src/sourceScopeClassification.js +83 -0
- package/dist/core/src/sourceScopeClassification.js.map +1 -0
- package/dist/core/src/stackAdapters.js +2475 -0
- package/dist/core/src/stackAdapters.js.map +1 -0
- package/dist/core/src/stackMetadata.js +157 -0
- package/dist/core/src/stackMetadata.js.map +1 -0
- package/dist/core/src/suppressions/index.js +3 -0
- package/dist/core/src/suppressions/index.js.map +1 -0
- package/dist/core/src/suppressions/suppressionReporter.js +38 -0
- package/dist/core/src/suppressions/suppressionReporter.js.map +1 -0
- package/dist/core/src/suppressions/suppressionTypes.js +3 -0
- package/dist/core/src/suppressions/suppressionTypes.js.map +1 -0
- package/dist/core/src/suppressions/suppressionValidator.js +119 -0
- package/dist/core/src/suppressions/suppressionValidator.js.map +1 -0
- package/dist/core/src/transactionDomainAnalysis.js +1416 -0
- package/dist/core/src/transactionDomainAnalysis.js.map +1 -0
- package/dist/core/src/validation/changedFilesResolver.js +165 -0
- package/dist/core/src/validation/changedFilesResolver.js.map +1 -0
- package/dist/core/src/validationProfiling.js +33 -0
- package/dist/core/src/validationProfiling.js.map +1 -0
- package/dist/core/src/validationRuleExplanations.js +69 -0
- package/dist/core/src/validationRuleExplanations.js.map +1 -0
- package/dist/core/src/validationSourceInventory.js +200 -0
- package/dist/core/src/validationSourceInventory.js.map +1 -0
- package/dist/core/src/validationSuppressions.js +231 -0
- package/dist/core/src/validationSuppressions.js.map +1 -0
- package/dist/core/src/validationTypeScriptAstCache.js +82 -0
- package/dist/core/src/validationTypeScriptAstCache.js.map +1 -0
- package/dist/core/src/validationWorkflow.js +222 -0
- package/dist/core/src/validationWorkflow.js.map +1 -0
- package/dist/core/src/workspaceRoot.js +68 -0
- package/dist/core/src/workspaceRoot.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.detectApiDbHints = detectApiDbHints;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const projectKindDetection_1 = require("./projectKindDetection");
|
|
40
|
+
const ignoredDirectoryNames = new Set([
|
|
41
|
+
'.git',
|
|
42
|
+
'.archpilot',
|
|
43
|
+
'.archpilot_',
|
|
44
|
+
'.vscode-test',
|
|
45
|
+
'tests',
|
|
46
|
+
'test',
|
|
47
|
+
'__tests__',
|
|
48
|
+
'fixtures',
|
|
49
|
+
'smoke',
|
|
50
|
+
'docs',
|
|
51
|
+
'doc',
|
|
52
|
+
'reports',
|
|
53
|
+
'report',
|
|
54
|
+
'badges',
|
|
55
|
+
'examples',
|
|
56
|
+
'node_modules',
|
|
57
|
+
'dist',
|
|
58
|
+
'build',
|
|
59
|
+
'out',
|
|
60
|
+
'coverage',
|
|
61
|
+
'.next',
|
|
62
|
+
'target',
|
|
63
|
+
'vendor',
|
|
64
|
+
'.venv',
|
|
65
|
+
'venv',
|
|
66
|
+
'__pycache__',
|
|
67
|
+
]);
|
|
68
|
+
function normalizePath(value) {
|
|
69
|
+
return value.replaceAll('\\', '/');
|
|
70
|
+
}
|
|
71
|
+
function uniqueSorted(values) {
|
|
72
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
73
|
+
}
|
|
74
|
+
function uniqueSortedDatabaseKinds(values) {
|
|
75
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
76
|
+
}
|
|
77
|
+
function addEvidence(evidence, value) {
|
|
78
|
+
if (!evidence.includes(value)) {
|
|
79
|
+
evidence.push(value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function isIgnoredPath(ignoreMatcher, relativePath) {
|
|
83
|
+
return ignoreMatcher?.isIgnored(normalizePath(relativePath)) ?? false;
|
|
84
|
+
}
|
|
85
|
+
function safeReadDirEntries(directoryPath) {
|
|
86
|
+
try {
|
|
87
|
+
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function safeReadText(filePath, maxChars = 80_000) {
|
|
94
|
+
try {
|
|
95
|
+
const content = fs.readFileSync(filePath, { encoding: 'utf8' });
|
|
96
|
+
return content.slice(0, maxChars);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function scanWorkspace(workspaceRoot, maxDepth = 7, ignoreMatcher) {
|
|
103
|
+
const files = [];
|
|
104
|
+
const directories = [];
|
|
105
|
+
const queue = [
|
|
106
|
+
{ abs: workspaceRoot, rel: '', depth: 0 },
|
|
107
|
+
];
|
|
108
|
+
while (queue.length > 0) {
|
|
109
|
+
const current = queue.shift();
|
|
110
|
+
if (!current) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const entries = safeReadDirEntries(current.abs)
|
|
114
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
const absPath = path.join(current.abs, entry.name);
|
|
117
|
+
const relPath = current.rel.length > 0 ? `${current.rel}/${entry.name}` : entry.name;
|
|
118
|
+
if (isIgnoredPath(ignoreMatcher, relPath)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
if (current.depth >= maxDepth || ignoredDirectoryNames.has(entry.name)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
directories.push(normalizePath(relPath));
|
|
126
|
+
queue.push({ abs: absPath, rel: relPath, depth: current.depth + 1 });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (entry.isFile()) {
|
|
130
|
+
files.push(normalizePath(relPath));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
files: uniqueSorted(files),
|
|
136
|
+
directories: uniqueSorted(directories),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function parsePackageJsonSummaries(workspaceRoot, files) {
|
|
140
|
+
const packageJsonPaths = files.filter((entry) => entry === 'package.json' || entry.endsWith('/package.json'));
|
|
141
|
+
const summaries = [];
|
|
142
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
143
|
+
const absolutePath = path.join(workspaceRoot, ...packageJsonPath.split('/'));
|
|
144
|
+
const contents = safeReadText(absolutePath);
|
|
145
|
+
if (!contents) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const parsed = JSON.parse(contents);
|
|
150
|
+
const dependencies = new Set();
|
|
151
|
+
for (const fieldName of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) {
|
|
152
|
+
const value = parsed[fieldName];
|
|
153
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
for (const name of Object.keys(value)) {
|
|
157
|
+
dependencies.add(name);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
summaries.push({ path: packageJsonPath, dependencies });
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Ignore invalid package.json files for detection.
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return summaries;
|
|
167
|
+
}
|
|
168
|
+
function parseComposerJsonSummaries(workspaceRoot, files) {
|
|
169
|
+
const composerJsonPaths = files.filter((entry) => entry === 'composer.json' || entry.endsWith('/composer.json'));
|
|
170
|
+
const summaries = [];
|
|
171
|
+
for (const composerJsonPath of composerJsonPaths) {
|
|
172
|
+
const absolutePath = path.join(workspaceRoot, ...composerJsonPath.split('/'));
|
|
173
|
+
const contents = safeReadText(absolutePath);
|
|
174
|
+
if (!contents) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const parsed = JSON.parse(contents);
|
|
179
|
+
const dependencies = new Set();
|
|
180
|
+
for (const fieldName of ['require', 'require-dev']) {
|
|
181
|
+
const value = parsed[fieldName];
|
|
182
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
for (const name of Object.keys(value)) {
|
|
186
|
+
dependencies.add(name.toLowerCase());
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
summaries.push({ path: composerJsonPath, dependencies });
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Ignore invalid composer.json files for detection.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return summaries;
|
|
196
|
+
}
|
|
197
|
+
function fileExists(files, name) {
|
|
198
|
+
return files.some((entry) => entry === name || entry.endsWith(`/${name}`));
|
|
199
|
+
}
|
|
200
|
+
function directoryExists(directories, name) {
|
|
201
|
+
return directories.some((entry) => entry === name || entry.endsWith(`/${name}`));
|
|
202
|
+
}
|
|
203
|
+
function dependencyExists(packageJsonSummaries, name) {
|
|
204
|
+
return packageJsonSummaries.some((summary) => summary.dependencies.has(name));
|
|
205
|
+
}
|
|
206
|
+
function composerDependencyExists(composerJsonSummaries, name) {
|
|
207
|
+
const normalized = name.toLowerCase();
|
|
208
|
+
return composerJsonSummaries.some((summary) => summary.dependencies.has(normalized));
|
|
209
|
+
}
|
|
210
|
+
function anyFileExists(files, names) {
|
|
211
|
+
return names.some((name) => fileExists(files, name));
|
|
212
|
+
}
|
|
213
|
+
function getFileExtension(filePath) {
|
|
214
|
+
const normalized = normalizePath(filePath);
|
|
215
|
+
return path.posix.extname(normalized).toLowerCase();
|
|
216
|
+
}
|
|
217
|
+
function getFileNameWithoutExtension(filePath) {
|
|
218
|
+
const normalized = normalizePath(filePath);
|
|
219
|
+
const parsed = path.posix.parse(normalized);
|
|
220
|
+
return parsed.name.toLowerCase();
|
|
221
|
+
}
|
|
222
|
+
function hasDirectorySegment(filePath, segment) {
|
|
223
|
+
const normalized = normalizePath(filePath);
|
|
224
|
+
return normalized.split('/').some((part) => part.toLowerCase() === segment.toLowerCase());
|
|
225
|
+
}
|
|
226
|
+
function detectApiHints(workspaceRoot, files, directories, packageJsonSummaries, composerJsonSummaries) {
|
|
227
|
+
const evidence = [];
|
|
228
|
+
let restScore = 0;
|
|
229
|
+
let graphqlScore = 0;
|
|
230
|
+
const addRest = (points, note) => {
|
|
231
|
+
restScore += points;
|
|
232
|
+
addEvidence(evidence, note);
|
|
233
|
+
};
|
|
234
|
+
const addGraphql = (points, note) => {
|
|
235
|
+
graphqlScore += points;
|
|
236
|
+
addEvidence(evidence, note);
|
|
237
|
+
};
|
|
238
|
+
for (const openApiFile of ['contracts/openapi.yaml', 'contracts/openapi.yml', 'openapi.yaml', 'openapi.yml']) {
|
|
239
|
+
if (fileExists(files, openApiFile)) {
|
|
240
|
+
addRest(4, `Found ${openApiFile}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (const swaggerFile of ['swagger.yaml', 'swagger.yml', 'swagger.json']) {
|
|
244
|
+
if (fileExists(files, swaggerFile)) {
|
|
245
|
+
addRest(4, `Found ${swaggerFile}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (const directoryName of ['controllers', 'routes', 'router']) {
|
|
249
|
+
if (directoryExists(directories, directoryName)) {
|
|
250
|
+
addRest(2, `Found ${directoryName} directory`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (directoryExists(directories, 'app/api') || directoryExists(directories, 'src/app/api')) {
|
|
254
|
+
addRest(2, 'Found Next.js app/api route directory');
|
|
255
|
+
}
|
|
256
|
+
if (files.some((entry) => /(^|\/)route\.[jt]s$/u.test(entry))) {
|
|
257
|
+
addRest(2, 'Found route.ts/route.js file');
|
|
258
|
+
}
|
|
259
|
+
const laravelApiRoutePath = files.find((entry) => /(^|\/)routes\/api\.php$/iu.test(entry));
|
|
260
|
+
if (laravelApiRoutePath) {
|
|
261
|
+
const content = safeReadText(path.join(workspaceRoot, ...laravelApiRoutePath.split('/')), 30_000);
|
|
262
|
+
if (content &&
|
|
263
|
+
(/\bRoute::(?:get|post|put|patch|delete|apiResource|resource)\s*\(\s*['"][^'"]*[A-Za-z0-9_{]/u.test(content) ||
|
|
264
|
+
/\bRoute::middleware\s*\(\s*['"]api['"]\s*\)/u.test(content) ||
|
|
265
|
+
/\bRoute::prefix\s*\(\s*['"]api(?:\/|['"])/u.test(content))) {
|
|
266
|
+
addRest(4, `Found meaningful Laravel API routes in ${laravelApiRoutePath}`);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
addEvidence(evidence, `Found ${laravelApiRoutePath} without strong API route evidence`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const dependency of ['express', 'fastify', '@nestjs/core', 'koa', 'hapi']) {
|
|
273
|
+
if (dependencyExists(packageJsonSummaries, dependency)) {
|
|
274
|
+
addRest(1, `Found package.json with ${dependency} dependency`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
for (const dependency of ['@apollo/server', 'apollo-server', 'graphql', 'graphql-yoga']) {
|
|
278
|
+
if (dependencyExists(packageJsonSummaries, dependency)) {
|
|
279
|
+
addGraphql(1, `Found package.json with ${dependency} dependency`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (composerDependencyExists(composerJsonSummaries, 'laravel/framework')) {
|
|
283
|
+
addEvidence(evidence, 'Found Laravel framework dependency without treating framework presence as API evidence');
|
|
284
|
+
}
|
|
285
|
+
for (const schemaFile of ['schema.graphql', 'schema.gql']) {
|
|
286
|
+
if (fileExists(files, schemaFile)) {
|
|
287
|
+
addGraphql(4, `Found ${schemaFile}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (directoryExists(directories, 'graphql') || directoryExists(directories, 'resolvers')) {
|
|
291
|
+
addGraphql(2, 'Found graphql/ or resolvers/ directory');
|
|
292
|
+
}
|
|
293
|
+
// Lightweight code-content checks for framework hints.
|
|
294
|
+
const apiCodeExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.java', '.kt', '.php']);
|
|
295
|
+
const apiFileNamePattern = /(?:^|[-_.])(controller|route|router|resolver|graphql|endpoint|rest)(?:[-_.]|$)/iu;
|
|
296
|
+
const candidateFiles = files
|
|
297
|
+
.filter((entry) => apiCodeExtensions.has(getFileExtension(entry)))
|
|
298
|
+
.filter((entry) => {
|
|
299
|
+
const fileName = getFileNameWithoutExtension(entry);
|
|
300
|
+
return (apiFileNamePattern.test(fileName) ||
|
|
301
|
+
hasDirectorySegment(entry, 'controllers') ||
|
|
302
|
+
hasDirectorySegment(entry, 'controller') ||
|
|
303
|
+
hasDirectorySegment(entry, 'routes') ||
|
|
304
|
+
hasDirectorySegment(entry, 'route') ||
|
|
305
|
+
hasDirectorySegment(entry, 'router') ||
|
|
306
|
+
hasDirectorySegment(entry, 'resolvers') ||
|
|
307
|
+
hasDirectorySegment(entry, 'resolver') ||
|
|
308
|
+
hasDirectorySegment(entry, 'graphql') ||
|
|
309
|
+
hasDirectorySegment(entry, 'api'));
|
|
310
|
+
})
|
|
311
|
+
.slice(0, 220);
|
|
312
|
+
for (const relativePath of candidateFiles) {
|
|
313
|
+
const absolutePath = path.join(workspaceRoot, ...relativePath.split('/'));
|
|
314
|
+
const text = safeReadText(absolutePath, 30_000);
|
|
315
|
+
if (!text) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const extension = getFileExtension(relativePath);
|
|
319
|
+
const isJavaLike = extension === '.java' || extension === '.kt';
|
|
320
|
+
const isTypeScriptLike = extension === '.ts' || extension === '.tsx' || extension === '.js' || extension === '.jsx';
|
|
321
|
+
const isPhp = extension === '.php';
|
|
322
|
+
if (isJavaLike && /@RestController\b/u.test(text)) {
|
|
323
|
+
addRest(2, `Found @RestController usage in ${relativePath}`);
|
|
324
|
+
}
|
|
325
|
+
if (isTypeScriptLike && /@(Controller|Get|Post|Put|Patch|Delete)\s*\(/u.test(text)) {
|
|
326
|
+
addRest(2, `Found HTTP controller/route decorators in ${relativePath}`);
|
|
327
|
+
}
|
|
328
|
+
if (isPhp && (/\bresponse\s*\(\s*\)\s*->\s*json\s*\(/u.test(text) || /\bJsonResource\b/u.test(text))) {
|
|
329
|
+
addRest(3, `Found Laravel JSON response/resource usage in ${relativePath}`);
|
|
330
|
+
}
|
|
331
|
+
if (/\btypeDefs\b/u.test(text) || /\bresolvers?\b/u.test(text)) {
|
|
332
|
+
addGraphql(1, `Found GraphQL typeDefs/resolver hints in ${relativePath}`);
|
|
333
|
+
}
|
|
334
|
+
if (isJavaLike && (/@Query\b/u.test(text) || /@Mutation\b/u.test(text))) {
|
|
335
|
+
addGraphql(2, `Found GraphQL Query/Mutation annotations in ${relativePath}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
let apiStyle = 'unknown';
|
|
339
|
+
let confidence = 'low';
|
|
340
|
+
if (restScore === 0 && graphqlScore === 0) {
|
|
341
|
+
apiStyle = 'unknown';
|
|
342
|
+
confidence = 'low';
|
|
343
|
+
}
|
|
344
|
+
else if (restScore >= graphqlScore && restScore >= 3) {
|
|
345
|
+
apiStyle = 'rest';
|
|
346
|
+
confidence = restScore >= 4 ? 'high' : 'medium';
|
|
347
|
+
if (graphqlScore > 0) {
|
|
348
|
+
addEvidence(evidence, 'Detected both REST and GraphQL signals; REST evidence is stronger');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else if (graphqlScore >= 3) {
|
|
352
|
+
apiStyle = 'graphql';
|
|
353
|
+
confidence = graphqlScore >= 4 ? 'high' : 'medium';
|
|
354
|
+
if (restScore > 0) {
|
|
355
|
+
addEvidence(evidence, 'Detected both REST and GraphQL signals; GraphQL evidence is stronger');
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
apiStyle = 'unknown';
|
|
360
|
+
confidence = 'low';
|
|
361
|
+
addEvidence(evidence, 'API evidence was present but not strong enough to select an API style');
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
result: {
|
|
365
|
+
apiStyle,
|
|
366
|
+
confidence,
|
|
367
|
+
evidence: uniqueSorted(evidence),
|
|
368
|
+
},
|
|
369
|
+
restScore,
|
|
370
|
+
graphqlScore,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const primaryDatabaseKinds = new Set([
|
|
374
|
+
'postgresql',
|
|
375
|
+
'mysql',
|
|
376
|
+
'mariadb',
|
|
377
|
+
'mongodb',
|
|
378
|
+
'sqlite',
|
|
379
|
+
'sqlserver',
|
|
380
|
+
'oracle',
|
|
381
|
+
'cassandra',
|
|
382
|
+
'dynamodb',
|
|
383
|
+
'cosmosdb',
|
|
384
|
+
'firestore',
|
|
385
|
+
'neo4j',
|
|
386
|
+
'snowflake',
|
|
387
|
+
'bigquery',
|
|
388
|
+
]);
|
|
389
|
+
const databaseAliases = [
|
|
390
|
+
{ database: 'postgresql', aliases: ['pgsql', 'postgres', 'postgresql', 'jdbc:postgresql'] },
|
|
391
|
+
{ database: 'mysql', aliases: ['mysql', 'jdbc:mysql'] },
|
|
392
|
+
{ database: 'mariadb', aliases: ['mariadb', 'jdbc:mariadb'] },
|
|
393
|
+
{ database: 'mongodb', aliases: ['mongodb', 'mongo', 'mongodb+srv', 'mongodb://', 'mongodb+srv://'] },
|
|
394
|
+
{ database: 'sqlite', aliases: ['sqlite', 'sqlite3', '.sqlite', '.sqlite3'] },
|
|
395
|
+
{ database: 'sqlserver', aliases: ['sqlsrv', 'sqlserver', 'mssql', 'jdbc:sqlserver', 'pdo_sqlsrv'] },
|
|
396
|
+
{ database: 'oracle', aliases: ['oracle', 'oci8', 'pdo_oci', 'jdbc:oracle', 'SERVICE_NAME', 'TNS'] },
|
|
397
|
+
{ database: 'redis', aliases: ['redis', 'redis://', 'rediss://'] },
|
|
398
|
+
{ database: 'cassandra', aliases: ['cassandra', 'cql'] },
|
|
399
|
+
{ database: 'elasticsearch', aliases: ['elasticsearch', 'elastic.co'] },
|
|
400
|
+
{ database: 'opensearch', aliases: ['opensearch'] },
|
|
401
|
+
{ database: 'dynamodb', aliases: ['dynamodb', 'dynamo_db', 'aws_dynamodb'] },
|
|
402
|
+
{ database: 'cosmosdb', aliases: ['cosmosdb', 'cosmos_db', 'azure-cosmos', 'documentdb'] },
|
|
403
|
+
{ database: 'firestore', aliases: ['firestore', 'firebase-admin'] },
|
|
404
|
+
{ database: 'neo4j', aliases: ['neo4j', 'bolt://', 'neo4j://'] },
|
|
405
|
+
{ database: 'snowflake', aliases: ['snowflake'] },
|
|
406
|
+
{ database: 'bigquery', aliases: ['bigquery', 'big_query'] },
|
|
407
|
+
];
|
|
408
|
+
const packageDependencyDatabaseHints = [
|
|
409
|
+
{ database: 'postgresql', dependencies: ['pg', 'postgres', 'postgresql', 'typeorm', 'sequelize', 'knex', 'drizzle-orm'], note: 'SQL database' },
|
|
410
|
+
{ database: 'mysql', dependencies: ['mysql', 'mysql2'] },
|
|
411
|
+
{ database: 'mariadb', dependencies: ['mariadb'] },
|
|
412
|
+
{ database: 'mongodb', dependencies: ['mongoose', 'mongodb'] },
|
|
413
|
+
{ database: 'sqlite', dependencies: ['sqlite3', 'better-sqlite3'] },
|
|
414
|
+
{ database: 'sqlserver', dependencies: ['mssql', 'tedious'] },
|
|
415
|
+
{ database: 'oracle', dependencies: ['oracledb'] },
|
|
416
|
+
{ database: 'redis', dependencies: ['redis', 'ioredis'] },
|
|
417
|
+
{ database: 'cassandra', dependencies: ['cassandra-driver'] },
|
|
418
|
+
{ database: 'elasticsearch', dependencies: ['@elastic/elasticsearch', 'elasticsearch'] },
|
|
419
|
+
{ database: 'opensearch', dependencies: ['@opensearch-project/opensearch'] },
|
|
420
|
+
{ database: 'dynamodb', dependencies: ['@aws-sdk/client-dynamodb', 'aws-sdk', 'dynamodb'] },
|
|
421
|
+
{ database: 'cosmosdb', dependencies: ['@azure/cosmos'] },
|
|
422
|
+
{ database: 'firestore', dependencies: ['@google-cloud/firestore', 'firebase-admin'] },
|
|
423
|
+
{ database: 'neo4j', dependencies: ['neo4j-driver'] },
|
|
424
|
+
{ database: 'snowflake', dependencies: ['snowflake-sdk'] },
|
|
425
|
+
{ database: 'bigquery', dependencies: ['@google-cloud/bigquery'] },
|
|
426
|
+
];
|
|
427
|
+
const composerDependencyDatabaseHints = [
|
|
428
|
+
{ database: 'sqlserver', dependencies: ['ext-sqlsrv', 'ext-pdo_sqlsrv'] },
|
|
429
|
+
{ database: 'oracle', dependencies: ['ext-oci8', 'ext-pdo_oci', 'yajra/laravel-oci8'] },
|
|
430
|
+
{ database: 'mongodb', dependencies: ['mongodb/mongodb', 'ext-mongodb'] },
|
|
431
|
+
{ database: 'redis', dependencies: ['predis/predis', 'ext-redis'] },
|
|
432
|
+
{ database: 'postgresql', dependencies: ['ext-pgsql', 'ext-pdo_pgsql'] },
|
|
433
|
+
{ database: 'mysql', dependencies: ['ext-mysqli', 'ext-pdo_mysql'] },
|
|
434
|
+
{ database: 'sqlite', dependencies: ['ext-sqlite3', 'ext-pdo_sqlite'] },
|
|
435
|
+
];
|
|
436
|
+
const textDependencyDatabaseHints = [
|
|
437
|
+
{ database: 'postgresql', pattern: /\b(psycopg2|asyncpg|pg8000|org\.postgresql|Npgsql|gem ['"]pg['"])/iu, label: 'PostgreSQL dependency' },
|
|
438
|
+
{ database: 'mysql', pattern: /\b(mysqlclient|pymysql|mysql-connector|mysql2|mysql-connector-java|MySqlConnector|Pomelo\.EntityFrameworkCore\.MySql)\b/iu, label: 'MySQL dependency' },
|
|
439
|
+
{ database: 'mariadb', pattern: /\b(mariadb|mariadb-java-client)\b/iu, label: 'MariaDB dependency' },
|
|
440
|
+
{ database: 'mongodb', pattern: /\b(pymongo|motor|MongoDB\.Driver|mongoid|mongoose|mongodb)\b/iu, label: 'MongoDB dependency' },
|
|
441
|
+
{ database: 'sqlite', pattern: /\b(sqlite3|Microsoft\.Data\.Sqlite|System\.Data\.SQLite|sqlite)\b/iu, label: 'SQLite dependency' },
|
|
442
|
+
{ database: 'sqlserver', pattern: /\b(mssql-jdbc|sqljdbc|Microsoft\.Data\.SqlClient|System\.Data\.SqlClient|pyodbc|pymssql|tiny_tds)\b/iu, label: 'SQL Server dependency' },
|
|
443
|
+
{ database: 'oracle', pattern: /\b(ojdbc|oracledb|cx_Oracle|Oracle\.ManagedDataAccess|ruby-oci8)\b/iu, label: 'Oracle dependency' },
|
|
444
|
+
{ database: 'redis', pattern: /\b(redis|StackExchange\.Redis|Jedis|lettuce-core)\b/iu, label: 'Redis dependency' },
|
|
445
|
+
{ database: 'cassandra', pattern: /\b(cassandra-driver|cassandra|DataStax|CassandraCSharpDriver)\b/iu, label: 'Cassandra dependency' },
|
|
446
|
+
{ database: 'elasticsearch', pattern: /\b(elasticsearch|Elastic\.Clients\.Elasticsearch|Nest)\b/iu, label: 'Elasticsearch dependency' },
|
|
447
|
+
{ database: 'opensearch', pattern: /\b(opensearch|OpenSearch\.Client)\b/iu, label: 'OpenSearch dependency' },
|
|
448
|
+
{ database: 'dynamodb', pattern: /\b(dynamodb|DynamoDB|software\.amazon\.awssdk:dynamodb)\b/u, label: 'DynamoDB dependency' },
|
|
449
|
+
{ database: 'cosmosdb', pattern: /\b(azure-cosmos|Microsoft\.Azure\.Cosmos|com\.azure:azure-cosmos)\b/iu, label: 'Cosmos DB dependency' },
|
|
450
|
+
{ database: 'firestore', pattern: /\b(firestore|firebase-admin|google-cloud-firestore)\b/iu, label: 'Firestore dependency' },
|
|
451
|
+
{ database: 'neo4j', pattern: /\b(neo4j-driver|neo4j|org\.neo4j\.driver)\b/iu, label: 'Neo4j dependency' },
|
|
452
|
+
{ database: 'snowflake', pattern: /\b(snowflake-connector|snowflake-sqlalchemy|snowflake-jdbc|Snowflake\.Data|snowflake-sdk)\b/iu, label: 'Snowflake dependency' },
|
|
453
|
+
{ database: 'bigquery', pattern: /\b(bigquery|google-cloud-bigquery|Google\.Cloud\.BigQuery)\b/iu, label: 'BigQuery dependency' },
|
|
454
|
+
];
|
|
455
|
+
const ormToolingPatterns = [
|
|
456
|
+
/\b(typeorm|sequelize|drizzle-orm|@mikro-orm\/core|prisma|@prisma\/client)\b/iu,
|
|
457
|
+
/\b(hibernate-core|org\.hibernate|spring-boot-starter-data-jpa|JpaRepository|CrudRepository|jakarta\.persistence|javax\.persistence)\b/u,
|
|
458
|
+
/\b(Microsoft\.EntityFrameworkCore|DbContext|DbSet<)\b/u,
|
|
459
|
+
/\b(SQLAlchemy|sqlalchemy|alembic|Alembic)\b/u,
|
|
460
|
+
/\b(doctrine\/orm|illuminate\/database)\b/iu,
|
|
461
|
+
];
|
|
462
|
+
function normalizeConnectionName(value) {
|
|
463
|
+
const normalized = value.trim().toLowerCase().replace(/['"]/gu, '');
|
|
464
|
+
for (const entry of databaseAliases) {
|
|
465
|
+
if (entry.aliases.some((alias) => normalized === alias.toLowerCase())) {
|
|
466
|
+
return entry.database;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return undefined;
|
|
470
|
+
}
|
|
471
|
+
function detectDatabasesInText(content) {
|
|
472
|
+
const found = [];
|
|
473
|
+
const lower = content.toLowerCase();
|
|
474
|
+
for (const entry of databaseAliases) {
|
|
475
|
+
if (entry.aliases.some((alias) => lower.includes(alias.toLowerCase()))) {
|
|
476
|
+
found.push(entry.database);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (/\bserver\s*=[^;\n]+;\s*database\s*=/iu.test(content)) {
|
|
480
|
+
found.push('sqlserver');
|
|
481
|
+
}
|
|
482
|
+
if (/\bdata source\s*=[^;\n]+;\s*(initial catalog|database)\s*=/iu.test(content)) {
|
|
483
|
+
found.push('sqlserver');
|
|
484
|
+
}
|
|
485
|
+
if (/\bData Source\s*=\s*[^;\n]+\.(sqlite|sqlite3|db)\b/u.test(content)) {
|
|
486
|
+
found.push('sqlite');
|
|
487
|
+
}
|
|
488
|
+
return uniqueSortedDatabaseKinds(found);
|
|
489
|
+
}
|
|
490
|
+
function detectDatabaseHints(workspaceRoot, files, directories, packageJsonSummaries, composerJsonSummaries) {
|
|
491
|
+
const evidence = [];
|
|
492
|
+
let hasUnknownDatabaseEvidence = false;
|
|
493
|
+
const scores = {
|
|
494
|
+
postgresql: 0,
|
|
495
|
+
mysql: 0,
|
|
496
|
+
mariadb: 0,
|
|
497
|
+
mongodb: 0,
|
|
498
|
+
sqlite: 0,
|
|
499
|
+
sqlserver: 0,
|
|
500
|
+
oracle: 0,
|
|
501
|
+
redis: 0,
|
|
502
|
+
cassandra: 0,
|
|
503
|
+
elasticsearch: 0,
|
|
504
|
+
opensearch: 0,
|
|
505
|
+
dynamodb: 0,
|
|
506
|
+
cosmosdb: 0,
|
|
507
|
+
firestore: 0,
|
|
508
|
+
neo4j: 0,
|
|
509
|
+
snowflake: 0,
|
|
510
|
+
bigquery: 0,
|
|
511
|
+
};
|
|
512
|
+
const addDb = (database, points, note) => {
|
|
513
|
+
scores[database] += points;
|
|
514
|
+
addEvidence(evidence, note);
|
|
515
|
+
};
|
|
516
|
+
// Prisma
|
|
517
|
+
const prismaSchemaPath = files.find((entry) => entry === 'prisma/schema.prisma' || entry.endsWith('/prisma/schema.prisma'));
|
|
518
|
+
if (prismaSchemaPath) {
|
|
519
|
+
const content = safeReadText(path.join(workspaceRoot, ...prismaSchemaPath.split('/')));
|
|
520
|
+
if (content) {
|
|
521
|
+
if (/provider\s*=\s*["']postgresql["']/iu.test(content)) {
|
|
522
|
+
addDb('postgresql', 4, 'Found Prisma datasource provider postgresql');
|
|
523
|
+
}
|
|
524
|
+
if (/provider\s*=\s*["']mysql["']/iu.test(content)) {
|
|
525
|
+
addDb('mysql', 4, 'Found Prisma datasource provider mysql');
|
|
526
|
+
}
|
|
527
|
+
if (/provider\s*=\s*["']mariadb["']/iu.test(content)) {
|
|
528
|
+
addDb('mariadb', 4, 'Found Prisma datasource provider mariadb');
|
|
529
|
+
}
|
|
530
|
+
if (/provider\s*=\s*["']mongodb["']/iu.test(content)) {
|
|
531
|
+
addDb('mongodb', 4, 'Found Prisma datasource provider mongodb');
|
|
532
|
+
}
|
|
533
|
+
if (/provider\s*=\s*["']sqlite["']/iu.test(content)) {
|
|
534
|
+
addDb('sqlite', 4, 'Found Prisma datasource provider sqlite');
|
|
535
|
+
}
|
|
536
|
+
if (/provider\s*=\s*["']sqlserver["']/iu.test(content)) {
|
|
537
|
+
addDb('sqlserver', 4, 'Found Prisma datasource provider sqlserver');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
// Generic datasource configs / content hints
|
|
542
|
+
const configCandidateFiles = files.filter((entry) => /(^|\/)(application\.(properties|ya?ml)|appsettings(\.[A-Za-z0-9_-]+)?\.json|settings\.py|database\.ya?ml|database\.yml|schema\.prisma|ormconfig\.(json|js|ts)|knexfile\.(js|ts)|sequelize\.(js|ts)|sequelize\/config\.(js|ts)|config\/database\.php|\.env(\.[a-z0-9_-]+)?)$/iu.test(entry) ||
|
|
543
|
+
/(^|\/)(docker-compose|compose)(\.[A-Za-z0-9_-]+)?\.ya?ml$/iu.test(entry) ||
|
|
544
|
+
/(^|\/)(package\.json|composer\.json|pom\.xml|build\.gradle(\.kts)?|requirements\.txt|pyproject\.toml|Gemfile|[^/]+\.csproj)$/iu.test(entry));
|
|
545
|
+
for (const relativePath of configCandidateFiles.slice(0, 120)) {
|
|
546
|
+
const content = safeReadText(path.join(workspaceRoot, ...relativePath.split('/')), 50_000);
|
|
547
|
+
if (!content) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const isLaravelDatabaseConfig = /(^|\/)config\/database\.php$/iu.test(relativePath);
|
|
551
|
+
const dbConnectionMatch = content.match(/(?:^|\n)\s*DB_CONNECTION\s*=\s*([A-Za-z0-9_-]+)/iu);
|
|
552
|
+
const laravelDefaultConnectionMatch = content.match(/['"]default['"]\s*=>\s*env\s*\(\s*['"]DB_CONNECTION['"]\s*,\s*['"]([^'"]+)['"]\s*\)/iu);
|
|
553
|
+
const laravelDefaultConnection = (laravelDefaultConnectionMatch?.[1] ?? '').toLowerCase();
|
|
554
|
+
const shouldTrustLaravelDefaultConnection = isLaravelDatabaseConfig &&
|
|
555
|
+
['sqlsrv', 'sqlserver', 'mssql', 'oracle', 'oci8'].includes(laravelDefaultConnection);
|
|
556
|
+
const explicitConnection = (dbConnectionMatch?.[1] ??
|
|
557
|
+
(shouldTrustLaravelDefaultConnection ? laravelDefaultConnection : '') ??
|
|
558
|
+
'').toLowerCase();
|
|
559
|
+
const normalizedExplicitConnection = normalizeConnectionName(explicitConnection);
|
|
560
|
+
if (normalizedExplicitConnection) {
|
|
561
|
+
addDb(normalizedExplicitConnection, 4, `Found explicit ${normalizedExplicitConnection} DB connection in ${relativePath}`);
|
|
562
|
+
}
|
|
563
|
+
else if (dbConnectionMatch?.[1]) {
|
|
564
|
+
hasUnknownDatabaseEvidence = true;
|
|
565
|
+
addEvidence(evidence, `Found explicit unmapped DB connection in ${relativePath}`);
|
|
566
|
+
}
|
|
567
|
+
if (explicitConnection === 'pgsql' || explicitConnection === 'postgres' || explicitConnection === 'postgresql') {
|
|
568
|
+
addDb('postgresql', 4, `Found explicit PostgreSQL DB connection in ${relativePath}`);
|
|
569
|
+
}
|
|
570
|
+
if (explicitConnection === 'mysql') {
|
|
571
|
+
addDb('mysql', 4, `Found explicit MySQL DB connection in ${relativePath}`);
|
|
572
|
+
}
|
|
573
|
+
if (explicitConnection === 'mariadb') {
|
|
574
|
+
addDb('mariadb', 4, `Found explicit MariaDB DB connection in ${relativePath}`);
|
|
575
|
+
}
|
|
576
|
+
if (explicitConnection === 'sqlite') {
|
|
577
|
+
addDb('sqlite', 4, `Found explicit SQLite DB connection in ${relativePath}`);
|
|
578
|
+
}
|
|
579
|
+
if (explicitConnection === 'sqlsrv' || explicitConnection === 'sqlserver' || explicitConnection === 'mssql') {
|
|
580
|
+
addDb('sqlserver', 4, `Found explicit SQL Server DB connection in ${relativePath}`);
|
|
581
|
+
}
|
|
582
|
+
if (explicitConnection === 'oracle' || explicitConnection === 'oci8') {
|
|
583
|
+
addDb('oracle', 4, `Found explicit Oracle DB connection in ${relativePath}`);
|
|
584
|
+
}
|
|
585
|
+
if (isLaravelDatabaseConfig) {
|
|
586
|
+
if (/\b(sqlsrv|pdo_sqlsrv|mssql|sqlserver)\b/iu.test(content)) {
|
|
587
|
+
addEvidence(evidence, `Found SQL Server-capable Laravel database config in ${relativePath}`);
|
|
588
|
+
}
|
|
589
|
+
if (/\b(oci8|pdo_oci|oracle|SERVICE_NAME|SID|TNS)\b/u.test(content)) {
|
|
590
|
+
addEvidence(evidence, `Found Oracle-capable Laravel database config in ${relativePath}`);
|
|
591
|
+
}
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
const envLikeMatches = content.matchAll(/(?:^|\n)\s*([A-Z0-9_]*(?:DATABASE_URL|DB_URL|DB_HOST|DB_PORT|DB_DATABASE)|SQLSERVER_[A-Z0-9_]+|MSSQL_[A-Z0-9_]+|ORACLE_[A-Z0-9_]+|MONGO(?:DB)?_[A-Z0-9_]+|REDIS_[A-Z0-9_]+|CASSANDRA_[A-Z0-9_]+|ELASTICSEARCH_[A-Z0-9_]+|OPENSEARCH_[A-Z0-9_]+|DYNAMODB_[A-Z0-9_]+|COSMOS(?:DB)?_[A-Z0-9_]+|FIRESTORE_[A-Z0-9_]+|NEO4J_[A-Z0-9_]+|SNOWFLAKE_[A-Z0-9_]+|BIGQUERY_[A-Z0-9_]+)\s*=\s*([^\n#]+)/giu);
|
|
595
|
+
for (const match of envLikeMatches) {
|
|
596
|
+
const databases = detectDatabasesInText(`${match[1]}=${match[2]}`);
|
|
597
|
+
if (databases.length === 0 && /(?:DATABASE_URL|DB_URL|DB_CONNECTION)/iu.test(match[1])) {
|
|
598
|
+
hasUnknownDatabaseEvidence = true;
|
|
599
|
+
addEvidence(evidence, `Found unmapped database environment/config variable in ${relativePath}`);
|
|
600
|
+
}
|
|
601
|
+
for (const database of databases) {
|
|
602
|
+
addDb(database, 3, `Found ${database} environment/config variable in ${relativePath}`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (/jdbc:postgresql:\/\//iu.test(content) || /\bpostgres(?:ql)?\b/iu.test(content)) {
|
|
606
|
+
addDb('postgresql', 2, `Found PostgreSQL datasource/config hints in ${relativePath}`);
|
|
607
|
+
}
|
|
608
|
+
if (/jdbc:mysql:\/\//iu.test(content) || /\bmysql\b/iu.test(content)) {
|
|
609
|
+
addDb('mysql', 2, `Found MySQL datasource/config hints in ${relativePath}`);
|
|
610
|
+
}
|
|
611
|
+
if (/jdbc:mariadb:\/\//iu.test(content) || /\bmariadb\b/iu.test(content)) {
|
|
612
|
+
addDb('mariadb', 2, `Found MariaDB datasource/config hints in ${relativePath}`);
|
|
613
|
+
}
|
|
614
|
+
if (/mongodb(\+srv)?:\/\//iu.test(content) || /\bmongodb\b/iu.test(content)) {
|
|
615
|
+
addDb('mongodb', 2, `Found MongoDB datasource/config hints in ${relativePath}`);
|
|
616
|
+
}
|
|
617
|
+
if (/\bsqlite\b/iu.test(content) || /\.sqlite\b/iu.test(content)) {
|
|
618
|
+
addDb('sqlite', 2, `Found SQLite datasource/config hints in ${relativePath}`);
|
|
619
|
+
}
|
|
620
|
+
if (/jdbc:sqlserver:\/\//iu.test(content) || /\b(sqlsrv|pdo_sqlsrv|mssql|sqlserver)\b/iu.test(content)) {
|
|
621
|
+
addDb('sqlserver', 2, `Found SQL Server datasource/config hints in ${relativePath}`);
|
|
622
|
+
}
|
|
623
|
+
if (/jdbc:oracle:thin:@/iu.test(content) || /\b(oci8|pdo_oci|oracle|SERVICE_NAME|SID|TNS)\b/u.test(content)) {
|
|
624
|
+
addDb('oracle', 2, `Found Oracle datasource/config hints in ${relativePath}`);
|
|
625
|
+
}
|
|
626
|
+
for (const database of detectDatabasesInText(content)) {
|
|
627
|
+
addDb(database, 1, `Found ${database} connection/config text in ${relativePath}`);
|
|
628
|
+
}
|
|
629
|
+
for (const dependencyHint of textDependencyDatabaseHints) {
|
|
630
|
+
if (dependencyHint.pattern.test(content)) {
|
|
631
|
+
addDb(dependencyHint.database, 2, `Found ${dependencyHint.label} in ${relativePath}`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
for (const dependency of ['pg', 'postgres', 'postgresql']) {
|
|
636
|
+
if (dependencyExists(packageJsonSummaries, dependency)) {
|
|
637
|
+
addDb('postgresql', 1, `Found package.json dependency hint (${dependency})`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
for (const dependency of ['mysql', 'mysql2']) {
|
|
641
|
+
if (dependencyExists(packageJsonSummaries, dependency)) {
|
|
642
|
+
addDb('mysql', 1, `Found package.json dependency hint (${dependency})`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
for (const dependency of ['mongoose', 'mongodb']) {
|
|
646
|
+
if (dependencyExists(packageJsonSummaries, dependency)) {
|
|
647
|
+
addDb('mongodb', 1, `Found package.json dependency hint (${dependency})`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
for (const dependency of ['sqlite3', 'better-sqlite3']) {
|
|
651
|
+
if (dependencyExists(packageJsonSummaries, dependency)) {
|
|
652
|
+
addDb('sqlite', 1, `Found package.json dependency hint (${dependency})`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (dependencyExists(packageJsonSummaries, 'mssql')) {
|
|
656
|
+
addDb('sqlserver', 1, 'Found package.json dependency hint (mssql)');
|
|
657
|
+
}
|
|
658
|
+
if (dependencyExists(packageJsonSummaries, 'oracledb')) {
|
|
659
|
+
addDb('oracle', 1, 'Found package.json dependency hint (oracledb)');
|
|
660
|
+
}
|
|
661
|
+
for (const databaseHint of packageDependencyDatabaseHints) {
|
|
662
|
+
for (const dependency of databaseHint.dependencies) {
|
|
663
|
+
if (dependencyExists(packageJsonSummaries, dependency)) {
|
|
664
|
+
addDb(databaseHint.database, 1, databaseHint.note
|
|
665
|
+
? `Found package.json dependency hint (${dependency}) for ${databaseHint.note}`
|
|
666
|
+
: `Found package.json dependency hint (${dependency})`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
for (const dependency of ['ext-sqlsrv', 'ext-pdo_sqlsrv']) {
|
|
671
|
+
if (composerDependencyExists(composerJsonSummaries, dependency)) {
|
|
672
|
+
addDb('sqlserver', 3, `Found composer.json dependency hint (${dependency})`);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
for (const dependency of ['ext-oci8', 'ext-pdo_oci', 'yajra/laravel-oci8']) {
|
|
676
|
+
if (composerDependencyExists(composerJsonSummaries, dependency)) {
|
|
677
|
+
addDb('oracle', 3, `Found composer.json dependency hint (${dependency})`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
for (const databaseHint of composerDependencyDatabaseHints) {
|
|
681
|
+
for (const dependency of databaseHint.dependencies) {
|
|
682
|
+
if (composerDependencyExists(composerJsonSummaries, dependency)) {
|
|
683
|
+
addDb(databaseHint.database, 2, `Found composer.json dependency hint (${dependency})`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const hasRelationalOrmEvidence = ['typeorm', 'sequelize', '@sequelize/core', 'knex', 'drizzle-orm', '@mikro-orm/core', 'mikro-orm', 'prisma', '@prisma/client']
|
|
688
|
+
.some((dependency) => dependencyExists(packageJsonSummaries, dependency)) ||
|
|
689
|
+
['doctrine/orm', 'illuminate/database'].some((dependency) => composerDependencyExists(composerJsonSummaries, dependency)) ||
|
|
690
|
+
configCandidateFiles.some((relativePath) => {
|
|
691
|
+
const content = safeReadText(path.join(workspaceRoot, ...relativePath.split('/')), 50_000);
|
|
692
|
+
return content ? ormToolingPatterns.some((pattern) => pattern.test(content)) : false;
|
|
693
|
+
});
|
|
694
|
+
for (const relativePath of configCandidateFiles.slice(0, 120)) {
|
|
695
|
+
const content = safeReadText(path.join(workspaceRoot, ...relativePath.split('/')), 50_000);
|
|
696
|
+
if (!content) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
if (/\bNpgsql\.EntityFrameworkCore\.PostgreSQL\b/u.test(content)) {
|
|
700
|
+
addDb('postgresql', 4, `Found Entity Framework PostgreSQL provider in ${relativePath}`);
|
|
701
|
+
}
|
|
702
|
+
if (/\bMicrosoft\.EntityFrameworkCore\.SqlServer\b/u.test(content)) {
|
|
703
|
+
addDb('sqlserver', 4, `Found Entity Framework SQL Server provider in ${relativePath}`);
|
|
704
|
+
}
|
|
705
|
+
if (/\b(Pomelo\.EntityFrameworkCore\.MySql|MySql\.EntityFrameworkCore)\b/u.test(content)) {
|
|
706
|
+
addDb('mysql', 4, `Found Entity Framework MySQL provider in ${relativePath}`);
|
|
707
|
+
}
|
|
708
|
+
if (/\bSqlite\b/u.test(content) && /\bMicrosoft\.EntityFrameworkCore\b/u.test(content)) {
|
|
709
|
+
addDb('sqlite', 3, `Found Entity Framework SQLite provider in ${relativePath}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (hasRelationalOrmEvidence) {
|
|
713
|
+
const primaryScores = Object.entries(scores)
|
|
714
|
+
.filter(([database, score]) => primaryDatabaseKinds.has(database) && score > 0);
|
|
715
|
+
const onlyPrimaryScore = primaryScores.length === 1 ? primaryScores[0] : undefined;
|
|
716
|
+
if (onlyPrimaryScore) {
|
|
717
|
+
addDb(onlyPrimaryScore[0], 1, `Found ORM/data-mapper evidence supporting ${onlyPrimaryScore[0]} database selection`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (directoryExists(directories, 'db') || directoryExists(directories, 'migrations')) {
|
|
721
|
+
addEvidence(evidence, 'Found db/ or migrations/ directory');
|
|
722
|
+
}
|
|
723
|
+
const ranked = Object.entries(scores)
|
|
724
|
+
.sort((left, right) => {
|
|
725
|
+
if (right[1] !== left[1]) {
|
|
726
|
+
return right[1] - left[1];
|
|
727
|
+
}
|
|
728
|
+
return left[0].localeCompare(right[0]);
|
|
729
|
+
});
|
|
730
|
+
const [topDatabase, topScore] = ranked[0] ?? ['postgresql', 0];
|
|
731
|
+
const secondScore = ranked[1]?.[1] ?? 0;
|
|
732
|
+
const databaseHints = ranked
|
|
733
|
+
.filter((entry) => entry[1] > 0)
|
|
734
|
+
.map((entry) => entry[0]);
|
|
735
|
+
let database = 'unknown';
|
|
736
|
+
let confidence = 'low';
|
|
737
|
+
if (topScore <= 0) {
|
|
738
|
+
database = 'unknown';
|
|
739
|
+
confidence = 'low';
|
|
740
|
+
}
|
|
741
|
+
else if (!primaryDatabaseKinds.has(topDatabase)) {
|
|
742
|
+
database = 'unknown';
|
|
743
|
+
confidence = topScore >= 3 ? 'medium' : 'low';
|
|
744
|
+
addEvidence(evidence, `Detected ${topDatabase} as a data-store/search/cache hint, not a primary database selection`);
|
|
745
|
+
}
|
|
746
|
+
else if (secondScore > 0 && topScore < secondScore + 2) {
|
|
747
|
+
database = 'unknown';
|
|
748
|
+
confidence = 'medium';
|
|
749
|
+
addEvidence(evidence, 'Detected multiple competing database hints; leaving primary database unselected');
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
database = topDatabase;
|
|
753
|
+
if (topScore >= 4 && topScore >= secondScore + 1) {
|
|
754
|
+
confidence = 'high';
|
|
755
|
+
}
|
|
756
|
+
else if (topScore >= 2) {
|
|
757
|
+
confidence = 'medium';
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
confidence = 'low';
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return {
|
|
764
|
+
result: {
|
|
765
|
+
database,
|
|
766
|
+
...(databaseHints.length > 0 ? { databaseHints } : {}),
|
|
767
|
+
...(hasUnknownDatabaseEvidence ? { hasUnknownDatabaseEvidence: true } : {}),
|
|
768
|
+
confidence,
|
|
769
|
+
evidence: uniqueSorted(evidence),
|
|
770
|
+
},
|
|
771
|
+
scores,
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
function detectApiDbHints(workspaceRoot, options) {
|
|
775
|
+
const scan = scanWorkspace(workspaceRoot, 7, options?.ignoreMatcher);
|
|
776
|
+
const packageJsonSummaries = parsePackageJsonSummaries(workspaceRoot, scan.files);
|
|
777
|
+
const composerJsonSummaries = parseComposerJsonSummaries(workspaceRoot, scan.files);
|
|
778
|
+
const apiDetection = detectApiHints(workspaceRoot, scan.files, scan.directories, packageJsonSummaries, composerJsonSummaries);
|
|
779
|
+
const dbDetection = detectDatabaseHints(workspaceRoot, scan.files, scan.directories, packageJsonSummaries, composerJsonSummaries);
|
|
780
|
+
const projectKinds = options?.projectKinds ?? (0, projectKindDetection_1.detectProjectKinds)(workspaceRoot);
|
|
781
|
+
const apiResult = { ...apiDetection.result };
|
|
782
|
+
const dbResult = { ...dbDetection.result };
|
|
783
|
+
const likelyNoPublicApi = projectKinds.primaryKind === 'library' || projectKinds.primaryKind === 'cli';
|
|
784
|
+
if (apiResult.apiStyle === 'unknown' && likelyNoPublicApi) {
|
|
785
|
+
apiResult.apiStyle = 'none';
|
|
786
|
+
apiResult.confidence = 'medium';
|
|
787
|
+
addEvidence(apiResult.evidence, 'Detected library/cli-oriented repository with no API artifacts');
|
|
788
|
+
}
|
|
789
|
+
const likelyNoDatabase = projectKinds.primaryKind === 'frontend' ||
|
|
790
|
+
projectKinds.primaryKind === 'library' ||
|
|
791
|
+
projectKinds.primaryKind === 'cli';
|
|
792
|
+
if (dbResult.database === 'unknown' && likelyNoDatabase) {
|
|
793
|
+
dbResult.database = 'none';
|
|
794
|
+
dbResult.confidence = 'medium';
|
|
795
|
+
addEvidence(dbResult.evidence, 'Detected frontend/library/cli-oriented repository with no database artifacts');
|
|
796
|
+
}
|
|
797
|
+
const overallEvidence = [];
|
|
798
|
+
if (apiResult.apiStyle === 'rest') {
|
|
799
|
+
addEvidence(overallEvidence, 'Detected REST API signals');
|
|
800
|
+
}
|
|
801
|
+
if (apiResult.apiStyle === 'graphql') {
|
|
802
|
+
addEvidence(overallEvidence, 'Detected GraphQL API signals');
|
|
803
|
+
}
|
|
804
|
+
if (dbResult.database !== 'unknown' && dbResult.database !== 'none') {
|
|
805
|
+
addEvidence(overallEvidence, `Detected ${dbResult.database} database signals`);
|
|
806
|
+
}
|
|
807
|
+
if (apiResult.apiStyle === 'none') {
|
|
808
|
+
addEvidence(overallEvidence, 'No API artifacts detected for current project profile');
|
|
809
|
+
}
|
|
810
|
+
if (dbResult.database === 'none') {
|
|
811
|
+
addEvidence(overallEvidence, 'No database artifacts detected for current project profile');
|
|
812
|
+
}
|
|
813
|
+
if (overallEvidence.length === 0) {
|
|
814
|
+
addEvidence(overallEvidence, 'API and database hints remain unknown from current repository evidence');
|
|
815
|
+
}
|
|
816
|
+
let confidence = 'low';
|
|
817
|
+
if (apiResult.confidence === 'high' || dbResult.confidence === 'high') {
|
|
818
|
+
confidence = 'high';
|
|
819
|
+
}
|
|
820
|
+
else if (apiResult.confidence === 'medium' || dbResult.confidence === 'medium') {
|
|
821
|
+
confidence = 'medium';
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
api: {
|
|
825
|
+
...apiResult,
|
|
826
|
+
evidence: uniqueSorted(apiResult.evidence),
|
|
827
|
+
},
|
|
828
|
+
database: {
|
|
829
|
+
...dbResult,
|
|
830
|
+
evidence: uniqueSorted(dbResult.evidence),
|
|
831
|
+
},
|
|
832
|
+
confidence,
|
|
833
|
+
evidence: uniqueSorted(overallEvidence),
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
//# sourceMappingURL=apiDbHintDetection.js.map
|