@jterrats/smart-deployment 1.0.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/README.md +193 -0
- package/lib/ai/agentforce-error-handler.d.ts +81 -0
- package/lib/ai/agentforce-error-handler.js +196 -0
- package/lib/ai/agentforce-error-handler.js.map +1 -0
- package/lib/ai/agentforce-priority-service.d.ts +82 -0
- package/lib/ai/agentforce-priority-service.js +257 -0
- package/lib/ai/agentforce-priority-service.js.map +1 -0
- package/lib/ai/agentforce-service.d.ts +99 -0
- package/lib/ai/agentforce-service.js +300 -0
- package/lib/ai/agentforce-service.js.map +1 -0
- package/lib/ai/circuit-breaker.d.ts +115 -0
- package/lib/ai/circuit-breaker.js +277 -0
- package/lib/ai/circuit-breaker.js.map +1 -0
- package/lib/ai/dependency-inference-service.d.ts +76 -0
- package/lib/ai/dependency-inference-service.js +220 -0
- package/lib/ai/dependency-inference-service.js.map +1 -0
- package/lib/ai/llm-provider-factory.d.ts +15 -0
- package/lib/ai/llm-provider-factory.js +36 -0
- package/lib/ai/llm-provider-factory.js.map +1 -0
- package/lib/ai/llm-provider.d.ts +27 -0
- package/lib/ai/llm-provider.js +2 -0
- package/lib/ai/llm-provider.js.map +1 -0
- package/lib/ai/openai-service.d.ts +20 -0
- package/lib/ai/openai-service.js +89 -0
- package/lib/ai/openai-service.js.map +1 -0
- package/lib/ai/prompt-builder.d.ts +79 -0
- package/lib/ai/prompt-builder.js +180 -0
- package/lib/ai/prompt-builder.js.map +1 -0
- package/lib/ai/response-parser.d.ts +67 -0
- package/lib/ai/response-parser.js +234 -0
- package/lib/ai/response-parser.js.map +1 -0
- package/lib/ai/wave-validation-service.d.ts +111 -0
- package/lib/ai/wave-validation-service.js +381 -0
- package/lib/ai/wave-validation-service.js.map +1 -0
- package/lib/analysis/analysis-reporter.d.ts +56 -0
- package/lib/analysis/analysis-reporter.js +170 -0
- package/lib/analysis/analysis-reporter.js.map +1 -0
- package/lib/analytics/error-analytics.d.ts +80 -0
- package/lib/analytics/error-analytics.js +162 -0
- package/lib/analytics/error-analytics.js.map +1 -0
- package/lib/commands/analyze.d.ts +49 -0
- package/lib/commands/analyze.js +232 -0
- package/lib/commands/analyze.js.map +1 -0
- package/lib/commands/config.d.ts +42 -0
- package/lib/commands/config.js +219 -0
- package/lib/commands/config.js.map +1 -0
- package/lib/commands/resume.d.ts +26 -0
- package/lib/commands/resume.js +69 -0
- package/lib/commands/resume.js.map +1 -0
- package/lib/commands/start.d.ts +70 -0
- package/lib/commands/start.js +659 -0
- package/lib/commands/start.js.map +1 -0
- package/lib/commands/status.d.ts +37 -0
- package/lib/commands/status.js +69 -0
- package/lib/commands/status.js.map +1 -0
- package/lib/commands/validate.d.ts +33 -0
- package/lib/commands/validate.js +66 -0
- package/lib/commands/validate.js.map +1 -0
- package/lib/config/repo-config.d.ts +22 -0
- package/lib/config/repo-config.js +31 -0
- package/lib/config/repo-config.js.map +1 -0
- package/lib/constants/agentforce-limits.d.ts +174 -0
- package/lib/constants/agentforce-limits.js +262 -0
- package/lib/constants/agentforce-limits.js.map +1 -0
- package/lib/constants/api-version.d.ts +70 -0
- package/lib/constants/api-version.js +122 -0
- package/lib/constants/api-version.js.map +1 -0
- package/lib/constants/deployment-order.d.ts +68 -0
- package/lib/constants/deployment-order.js +162 -0
- package/lib/constants/deployment-order.js.map +1 -0
- package/lib/constants/salesforce-limits.d.ts +107 -0
- package/lib/constants/salesforce-limits.js +104 -0
- package/lib/constants/salesforce-limits.js.map +1 -0
- package/lib/dependencies/circular-dependency-detector.d.ts +137 -0
- package/lib/dependencies/circular-dependency-detector.js +329 -0
- package/lib/dependencies/circular-dependency-detector.js.map +1 -0
- package/lib/dependencies/cycle-remediation-planner.d.ts +50 -0
- package/lib/dependencies/cycle-remediation-planner.js +192 -0
- package/lib/dependencies/cycle-remediation-planner.js.map +1 -0
- package/lib/dependencies/dependency-cache.d.ts +134 -0
- package/lib/dependencies/dependency-cache.js +303 -0
- package/lib/dependencies/dependency-cache.js.map +1 -0
- package/lib/dependencies/dependency-depth-calculator.d.ts +145 -0
- package/lib/dependencies/dependency-depth-calculator.js +368 -0
- package/lib/dependencies/dependency-depth-calculator.js.map +1 -0
- package/lib/dependencies/dependency-graph-builder.d.ts +151 -0
- package/lib/dependencies/dependency-graph-builder.js +411 -0
- package/lib/dependencies/dependency-graph-builder.js.map +1 -0
- package/lib/dependencies/dependency-impact-analyzer.d.ts +145 -0
- package/lib/dependencies/dependency-impact-analyzer.js +330 -0
- package/lib/dependencies/dependency-impact-analyzer.js.map +1 -0
- package/lib/dependencies/dependency-merger.d.ts +122 -0
- package/lib/dependencies/dependency-merger.js +245 -0
- package/lib/dependencies/dependency-merger.js.map +1 -0
- package/lib/dependencies/dependency-resolver.d.ts +157 -0
- package/lib/dependencies/dependency-resolver.js +298 -0
- package/lib/dependencies/dependency-resolver.js.map +1 -0
- package/lib/dependencies/dependency-validator.d.ts +123 -0
- package/lib/dependencies/dependency-validator.js +291 -0
- package/lib/dependencies/dependency-validator.js.map +1 -0
- package/lib/dependencies/graph-visualizer.d.ts +110 -0
- package/lib/dependencies/graph-visualizer.js +262 -0
- package/lib/dependencies/graph-visualizer.js.map +1 -0
- package/lib/dependencies/heuristic-inference.d.ts +136 -0
- package/lib/dependencies/heuristic-inference.js +430 -0
- package/lib/dependencies/heuristic-inference.js.map +1 -0
- package/lib/deployment/cycle-source-editor.d.ts +34 -0
- package/lib/deployment/cycle-source-editor.js +121 -0
- package/lib/deployment/cycle-source-editor.js.map +1 -0
- package/lib/deployment/deployment-error-handler.d.ts +38 -0
- package/lib/deployment/deployment-error-handler.js +79 -0
- package/lib/deployment/deployment-error-handler.js.map +1 -0
- package/lib/deployment/deployment-reporter.d.ts +63 -0
- package/lib/deployment/deployment-reporter.js +150 -0
- package/lib/deployment/deployment-reporter.js.map +1 -0
- package/lib/deployment/deployment-state-summary.d.ts +38 -0
- package/lib/deployment/deployment-state-summary.js +209 -0
- package/lib/deployment/deployment-state-summary.js.map +1 -0
- package/lib/deployment/deployment-status-service.d.ts +36 -0
- package/lib/deployment/deployment-status-service.js +128 -0
- package/lib/deployment/deployment-status-service.js.map +1 -0
- package/lib/deployment/deployment-tracker.d.ts +42 -0
- package/lib/deployment/deployment-tracker.js +79 -0
- package/lib/deployment/deployment-tracker.js.map +1 -0
- package/lib/deployment/deployment-validation-service.d.ts +28 -0
- package/lib/deployment/deployment-validation-service.js +161 -0
- package/lib/deployment/deployment-validation-service.js.map +1 -0
- package/lib/deployment/retry-handler.d.ts +37 -0
- package/lib/deployment/retry-handler.js +86 -0
- package/lib/deployment/retry-handler.js.map +1 -0
- package/lib/deployment/sf-cli-integration.d.ts +42 -0
- package/lib/deployment/sf-cli-integration.js +105 -0
- package/lib/deployment/sf-cli-integration.js.map +1 -0
- package/lib/deployment/state-manager.d.ts +61 -0
- package/lib/deployment/state-manager.js +83 -0
- package/lib/deployment/state-manager.js.map +1 -0
- package/lib/deployment/test-executor.d.ts +41 -0
- package/lib/deployment/test-executor.js +87 -0
- package/lib/deployment/test-executor.js.map +1 -0
- package/lib/errors/base-error.d.ts +24 -0
- package/lib/errors/base-error.js +66 -0
- package/lib/errors/base-error.js.map +1 -0
- package/lib/errors/dependency-error.d.ts +37 -0
- package/lib/errors/dependency-error.js +76 -0
- package/lib/errors/dependency-error.js.map +1 -0
- package/lib/errors/deployment-error.d.ts +55 -0
- package/lib/errors/deployment-error.js +132 -0
- package/lib/errors/deployment-error.js.map +1 -0
- package/lib/errors/index.d.ts +45 -0
- package/lib/errors/index.js +71 -0
- package/lib/errors/index.js.map +1 -0
- package/lib/errors/network-error.d.ts +53 -0
- package/lib/errors/network-error.js +111 -0
- package/lib/errors/network-error.js.map +1 -0
- package/lib/errors/parsing-error.d.ts +41 -0
- package/lib/errors/parsing-error.js +69 -0
- package/lib/errors/parsing-error.js.map +1 -0
- package/lib/errors/validation-error-reporter.d.ts +34 -0
- package/lib/errors/validation-error-reporter.js +99 -0
- package/lib/errors/validation-error-reporter.js.map +1 -0
- package/lib/errors/validation-error.d.ts +58 -0
- package/lib/errors/validation-error.js +131 -0
- package/lib/errors/validation-error.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/monitoring/performance-monitor.d.ts +98 -0
- package/lib/monitoring/performance-monitor.js +260 -0
- package/lib/monitoring/performance-monitor.js.map +1 -0
- package/lib/parsers/apex-class-parser.d.ts +47 -0
- package/lib/parsers/apex-class-parser.js +368 -0
- package/lib/parsers/apex-class-parser.js.map +1 -0
- package/lib/parsers/apex-trigger-parser.d.ts +48 -0
- package/lib/parsers/apex-trigger-parser.js +229 -0
- package/lib/parsers/apex-trigger-parser.js.map +1 -0
- package/lib/parsers/aura-parser.d.ts +55 -0
- package/lib/parsers/aura-parser.js +229 -0
- package/lib/parsers/aura-parser.js.map +1 -0
- package/lib/parsers/bot-parser.d.ts +65 -0
- package/lib/parsers/bot-parser.js +225 -0
- package/lib/parsers/bot-parser.js.map +1 -0
- package/lib/parsers/custom-metadata-parser.d.ts +94 -0
- package/lib/parsers/custom-metadata-parser.js +199 -0
- package/lib/parsers/custom-metadata-parser.js.map +1 -0
- package/lib/parsers/custom-object-parser.d.ts +62 -0
- package/lib/parsers/custom-object-parser.js +297 -0
- package/lib/parsers/custom-object-parser.js.map +1 -0
- package/lib/parsers/email-template-parser.d.ts +64 -0
- package/lib/parsers/email-template-parser.js +238 -0
- package/lib/parsers/email-template-parser.js.map +1 -0
- package/lib/parsers/error-resilient-parser.d.ts +110 -0
- package/lib/parsers/error-resilient-parser.js +277 -0
- package/lib/parsers/error-resilient-parser.js.map +1 -0
- package/lib/parsers/flexipage-parser.d.ts +64 -0
- package/lib/parsers/flexipage-parser.js +196 -0
- package/lib/parsers/flexipage-parser.js.map +1 -0
- package/lib/parsers/flow-parser.d.ts +54 -0
- package/lib/parsers/flow-parser.js +287 -0
- package/lib/parsers/flow-parser.js.map +1 -0
- package/lib/parsers/genai-prompt-parser.d.ts +67 -0
- package/lib/parsers/genai-prompt-parser.js +160 -0
- package/lib/parsers/genai-prompt-parser.js.map +1 -0
- package/lib/parsers/layout-parser.d.ts +64 -0
- package/lib/parsers/layout-parser.js +267 -0
- package/lib/parsers/layout-parser.js.map +1 -0
- package/lib/parsers/lwc-parser.d.ts +60 -0
- package/lib/parsers/lwc-parser.js +264 -0
- package/lib/parsers/lwc-parser.js.map +1 -0
- package/lib/parsers/permission-set-parser.d.ts +86 -0
- package/lib/parsers/permission-set-parser.js +152 -0
- package/lib/parsers/permission-set-parser.js.map +1 -0
- package/lib/parsers/profile-parser.d.ts +81 -0
- package/lib/parsers/profile-parser.js +141 -0
- package/lib/parsers/profile-parser.js.map +1 -0
- package/lib/parsers/visualforce-parser.d.ts +47 -0
- package/lib/parsers/visualforce-parser.js +180 -0
- package/lib/parsers/visualforce-parser.js.map +1 -0
- package/lib/provisioning/data-provisioner.d.ts +88 -0
- package/lib/provisioning/data-provisioner.js +257 -0
- package/lib/provisioning/data-provisioner.js.map +1 -0
- package/lib/scanner/custom-structure-scanner.d.ts +66 -0
- package/lib/scanner/custom-structure-scanner.js +229 -0
- package/lib/scanner/custom-structure-scanner.js.map +1 -0
- package/lib/scanner/forceignore-parser.d.ts +69 -0
- package/lib/scanner/forceignore-parser.js +195 -0
- package/lib/scanner/forceignore-parser.js.map +1 -0
- package/lib/scanner/metadata-format-scanner.d.ts +77 -0
- package/lib/scanner/metadata-format-scanner.js +282 -0
- package/lib/scanner/metadata-format-scanner.js.map +1 -0
- package/lib/scanner/monorepo-scanner.d.ts +71 -0
- package/lib/scanner/monorepo-scanner.js +225 -0
- package/lib/scanner/monorepo-scanner.js.map +1 -0
- package/lib/scanner/project-validator.d.ts +55 -0
- package/lib/scanner/project-validator.js +235 -0
- package/lib/scanner/project-validator.js.map +1 -0
- package/lib/scanner/sfdx-project-detector.d.ts +86 -0
- package/lib/scanner/sfdx-project-detector.js +240 -0
- package/lib/scanner/sfdx-project-detector.js.map +1 -0
- package/lib/scanner/structure-validator.d.ts +64 -0
- package/lib/scanner/structure-validator.js +296 -0
- package/lib/scanner/structure-validator.js.map +1 -0
- package/lib/services/metadata-scanner-service.d.ts +64 -0
- package/lib/services/metadata-scanner-service.js +651 -0
- package/lib/services/metadata-scanner-service.js.map +1 -0
- package/lib/types/agentforce.d.ts +157 -0
- package/lib/types/agentforce.js +2 -0
- package/lib/types/agentforce.js.map +1 -0
- package/lib/types/dependency.d.ts +98 -0
- package/lib/types/dependency.js +5 -0
- package/lib/types/dependency.js.map +1 -0
- package/lib/types/deployment-plan.d.ts +81 -0
- package/lib/types/deployment-plan.js +6 -0
- package/lib/types/deployment-plan.js.map +1 -0
- package/lib/types/deployment.d.ts +88 -0
- package/lib/types/deployment.js +5 -0
- package/lib/types/deployment.js.map +1 -0
- package/lib/types/graph.d.ts +35 -0
- package/lib/types/graph.js +5 -0
- package/lib/types/graph.js.map +1 -0
- package/lib/types/index.d.ts +12 -0
- package/lib/types/index.js +17 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/metadata.d.ts +101 -0
- package/lib/types/metadata.js +13 -0
- package/lib/types/metadata.js.map +1 -0
- package/lib/types/project.d.ts +156 -0
- package/lib/types/project.js +56 -0
- package/lib/types/project.js.map +1 -0
- package/lib/types/salesforce/apex.d.ts +94 -0
- package/lib/types/salesforce/apex.js +6 -0
- package/lib/types/salesforce/apex.js.map +1 -0
- package/lib/types/salesforce/aura.d.ts +150 -0
- package/lib/types/salesforce/aura.js +6 -0
- package/lib/types/salesforce/aura.js.map +1 -0
- package/lib/types/salesforce/bot.d.ts +293 -0
- package/lib/types/salesforce/bot.js +6 -0
- package/lib/types/salesforce/bot.js.map +1 -0
- package/lib/types/salesforce/common.d.ts +15 -0
- package/lib/types/salesforce/common.js +5 -0
- package/lib/types/salesforce/common.js.map +1 -0
- package/lib/types/salesforce/custom-metadata.d.ts +92 -0
- package/lib/types/salesforce/custom-metadata.js +6 -0
- package/lib/types/salesforce/custom-metadata.js.map +1 -0
- package/lib/types/salesforce/email.d.ts +56 -0
- package/lib/types/salesforce/email.js +6 -0
- package/lib/types/salesforce/email.js.map +1 -0
- package/lib/types/salesforce/flexipage.d.ts +149 -0
- package/lib/types/salesforce/flexipage.js +6 -0
- package/lib/types/salesforce/flexipage.js.map +1 -0
- package/lib/types/salesforce/flow.d.ts +516 -0
- package/lib/types/salesforce/flow.js +6 -0
- package/lib/types/salesforce/flow.js.map +1 -0
- package/lib/types/salesforce/genai.d.ts +67 -0
- package/lib/types/salesforce/genai.js +6 -0
- package/lib/types/salesforce/genai.js.map +1 -0
- package/lib/types/salesforce/index.d.ts +27 -0
- package/lib/types/salesforce/index.js +43 -0
- package/lib/types/salesforce/index.js.map +1 -0
- package/lib/types/salesforce/layout.d.ts +236 -0
- package/lib/types/salesforce/layout.js +6 -0
- package/lib/types/salesforce/layout.js.map +1 -0
- package/lib/types/salesforce/lwc.d.ts +123 -0
- package/lib/types/salesforce/lwc.js +6 -0
- package/lib/types/salesforce/lwc.js.map +1 -0
- package/lib/types/salesforce/object.d.ts +427 -0
- package/lib/types/salesforce/object.js +6 -0
- package/lib/types/salesforce/object.js.map +1 -0
- package/lib/types/salesforce/parser-types.d.ts +79 -0
- package/lib/types/salesforce/parser-types.js +80 -0
- package/lib/types/salesforce/parser-types.js.map +1 -0
- package/lib/types/salesforce/permission.d.ts +289 -0
- package/lib/types/salesforce/permission.js +6 -0
- package/lib/types/salesforce/permission.js.map +1 -0
- package/lib/types/salesforce/resource.d.ts +93 -0
- package/lib/types/salesforce/resource.js +6 -0
- package/lib/types/salesforce/resource.js.map +1 -0
- package/lib/types/salesforce/visualforce.d.ts +70 -0
- package/lib/types/salesforce/visualforce.js +6 -0
- package/lib/types/salesforce/visualforce.js.map +1 -0
- package/lib/utils/cache-manager.d.ts +158 -0
- package/lib/utils/cache-manager.js +429 -0
- package/lib/utils/cache-manager.js.map +1 -0
- package/lib/utils/deployment-plan-manager.d.ts +40 -0
- package/lib/utils/deployment-plan-manager.js +183 -0
- package/lib/utils/deployment-plan-manager.js.map +1 -0
- package/lib/utils/error-aggregator.d.ts +117 -0
- package/lib/utils/error-aggregator.js +268 -0
- package/lib/utils/error-aggregator.js.map +1 -0
- package/lib/utils/file-system.d.ts +62 -0
- package/lib/utils/file-system.js +167 -0
- package/lib/utils/file-system.js.map +1 -0
- package/lib/utils/functional.d.ts +52 -0
- package/lib/utils/functional.js +61 -0
- package/lib/utils/functional.js.map +1 -0
- package/lib/utils/graph-algorithms.d.ts +53 -0
- package/lib/utils/graph-algorithms.js +177 -0
- package/lib/utils/graph-algorithms.js.map +1 -0
- package/lib/utils/logger.d.ts +154 -0
- package/lib/utils/logger.js +327 -0
- package/lib/utils/logger.js.map +1 -0
- package/lib/utils/network-handler.d.ts +64 -0
- package/lib/utils/network-handler.js +147 -0
- package/lib/utils/network-handler.js.map +1 -0
- package/lib/utils/performance.d.ts +148 -0
- package/lib/utils/performance.js +294 -0
- package/lib/utils/performance.js.map +1 -0
- package/lib/utils/string.d.ts +197 -0
- package/lib/utils/string.js +331 -0
- package/lib/utils/string.js.map +1 -0
- package/lib/utils/xml.d.ts +97 -0
- package/lib/utils/xml.js +227 -0
- package/lib/utils/xml.js.map +1 -0
- package/lib/validators/xml-metadata-validator.d.ts +106 -0
- package/lib/validators/xml-metadata-validator.js +509 -0
- package/lib/validators/xml-metadata-validator.js.map +1 -0
- package/lib/waves/priority-wave-generator-ai.d.ts +85 -0
- package/lib/waves/priority-wave-generator-ai.js +191 -0
- package/lib/waves/priority-wave-generator-ai.js.map +1 -0
- package/lib/waves/priority-wave-generator.d.ts +47 -0
- package/lib/waves/priority-wave-generator.js +88 -0
- package/lib/waves/priority-wave-generator.js.map +1 -0
- package/lib/waves/test-optimizer.d.ts +155 -0
- package/lib/waves/test-optimizer.js +290 -0
- package/lib/waves/test-optimizer.js.map +1 -0
- package/lib/waves/wave-builder.d.ts +147 -0
- package/lib/waves/wave-builder.js +286 -0
- package/lib/waves/wave-builder.js.map +1 -0
- package/lib/waves/wave-diff-generator.d.ts +17 -0
- package/lib/waves/wave-diff-generator.js +6 -0
- package/lib/waves/wave-diff-generator.js.map +1 -0
- package/lib/waves/wave-executor.d.ts +33 -0
- package/lib/waves/wave-executor.js +50 -0
- package/lib/waves/wave-executor.js.map +1 -0
- package/lib/waves/wave-merger.d.ts +96 -0
- package/lib/waves/wave-merger.js +181 -0
- package/lib/waves/wave-merger.js.map +1 -0
- package/lib/waves/wave-metadata-generator.d.ts +13 -0
- package/lib/waves/wave-metadata-generator.js +12 -0
- package/lib/waves/wave-metadata-generator.js.map +1 -0
- package/lib/waves/wave-splitter.d.ts +154 -0
- package/lib/waves/wave-splitter.js +307 -0
- package/lib/waves/wave-splitter.js.map +1 -0
- package/lib/waves/wave-validator.d.ts +17 -0
- package/lib/waves/wave-validator.js +15 -0
- package/lib/waves/wave-validator.js.map +1 -0
- package/messages/analyze.json +18 -0
- package/messages/config.json +29 -0
- package/messages/resume.json +9 -0
- package/messages/start.json +24 -0
- package/messages/status.json +8 -0
- package/messages/validate.json +9 -0
- package/npm-shrinkwrap.json +25676 -0
- package/oclif.lock +11988 -0
- package/oclif.manifest.json +589 -0
- package/package.json +224 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata Scanner Service
|
|
3
|
+
* Orchestrates project scanning, parsing, and dependency analysis
|
|
4
|
+
*
|
|
5
|
+
* Integrates:
|
|
6
|
+
* - SfdxProjectDetector
|
|
7
|
+
* - ForceIgnoreParser
|
|
8
|
+
* - Metadata parsers (all types)
|
|
9
|
+
* - DependencyGraphBuilder
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from 'node:fs/promises';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import { glob as globAsync } from 'glob';
|
|
14
|
+
import { DependencyGraphBuilder } from '../dependencies/dependency-graph-builder.js';
|
|
15
|
+
import { parseAura } from '../parsers/aura-parser.js';
|
|
16
|
+
import { parseApexClass } from '../parsers/apex-class-parser.js';
|
|
17
|
+
import { parseApexTrigger } from '../parsers/apex-trigger-parser.js';
|
|
18
|
+
import { parseBot } from '../parsers/bot-parser.js';
|
|
19
|
+
import { parseCustomMetadataType } from '../parsers/custom-metadata-parser.js';
|
|
20
|
+
import { parseCustomObject } from '../parsers/custom-object-parser.js';
|
|
21
|
+
import { parseEmailTemplate } from '../parsers/email-template-parser.js';
|
|
22
|
+
import { parseFlexiPage } from '../parsers/flexipage-parser.js';
|
|
23
|
+
import { parseFlow } from '../parsers/flow-parser.js';
|
|
24
|
+
import { parseGenAiPrompt } from '../parsers/genai-prompt-parser.js';
|
|
25
|
+
import { parseLayout } from '../parsers/layout-parser.js';
|
|
26
|
+
import { parseLWC } from '../parsers/lwc-parser.js';
|
|
27
|
+
import { parsePermissionSet } from '../parsers/permission-set-parser.js';
|
|
28
|
+
import { parseProfile } from '../parsers/profile-parser.js';
|
|
29
|
+
import { parseVisualforce } from '../parsers/visualforce-parser.js';
|
|
30
|
+
import { ForceIgnoreParser } from '../scanner/forceignore-parser.js';
|
|
31
|
+
import { SfdxProjectDetector } from '../scanner/sfdx-project-detector.js';
|
|
32
|
+
import { getLogger } from '../utils/logger.js';
|
|
33
|
+
const logger = getLogger('MetadataScannerService');
|
|
34
|
+
function toNodeIds(dependencies, defaultType) {
|
|
35
|
+
return new Set([...dependencies].map((dependency) => (dependency.includes(':') ? dependency : `${defaultType}:${dependency}`)));
|
|
36
|
+
}
|
|
37
|
+
function isDefined(value) {
|
|
38
|
+
return value !== undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Metadata file pattern matcher
|
|
42
|
+
* TODO: Use when implementing pattern-based metadata detection
|
|
43
|
+
*/
|
|
44
|
+
// interface MetadataPattern {
|
|
45
|
+
// type: MetadataType;
|
|
46
|
+
// patterns: string[];
|
|
47
|
+
// isContainer?: boolean; // For LWC, Aura, CustomObject bundles
|
|
48
|
+
// parser?: (filePath: string, content: string, metadataXml?: string) => Promise<MetadataComponent> | MetadataComponent;
|
|
49
|
+
// }
|
|
50
|
+
/**
|
|
51
|
+
* Metadata Scanner Service
|
|
52
|
+
* Scans project, parses metadata, and builds dependency graph
|
|
53
|
+
*/
|
|
54
|
+
export class MetadataScannerService {
|
|
55
|
+
forceIgnoreParser;
|
|
56
|
+
/**
|
|
57
|
+
* Scan project and analyze dependencies
|
|
58
|
+
*/
|
|
59
|
+
async scan(options = {}) {
|
|
60
|
+
const startTime = Date.now();
|
|
61
|
+
const errors = [];
|
|
62
|
+
const warnings = [];
|
|
63
|
+
// Detect project
|
|
64
|
+
const sourcePath = options.sourcePath ?? process.cwd();
|
|
65
|
+
const projectInfo = await SfdxProjectDetector.detect(sourcePath);
|
|
66
|
+
if (!projectInfo.detected) {
|
|
67
|
+
throw new Error(`SFDX project not found in ${sourcePath}`);
|
|
68
|
+
}
|
|
69
|
+
logger.info('Project detected', {
|
|
70
|
+
root: projectInfo.projectRoot,
|
|
71
|
+
packageDirs: projectInfo.packageDirectories.length,
|
|
72
|
+
});
|
|
73
|
+
// Parse .forceignore
|
|
74
|
+
const forceIgnorePath = path.join(projectInfo.projectRoot, '.forceignore');
|
|
75
|
+
if (await this.fileExists(forceIgnorePath)) {
|
|
76
|
+
this.forceIgnoreParser = new ForceIgnoreParser();
|
|
77
|
+
const loadResult = await this.forceIgnoreParser.load(projectInfo.projectRoot);
|
|
78
|
+
logger.info('Loaded .forceignore', {
|
|
79
|
+
rules: loadResult.totalRules,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Scan and parse metadata
|
|
83
|
+
const components = await this.scanMetadata(projectInfo.projectRoot, projectInfo.packageDirectories, errors, warnings);
|
|
84
|
+
// Build dependency graph
|
|
85
|
+
const graphBuilder = new DependencyGraphBuilder();
|
|
86
|
+
for (const component of components) {
|
|
87
|
+
graphBuilder.addComponent(component);
|
|
88
|
+
}
|
|
89
|
+
const dependencyResult = graphBuilder.build();
|
|
90
|
+
const executionTime = Date.now() - startTime;
|
|
91
|
+
logger.info('Scan complete', {
|
|
92
|
+
components: components.length,
|
|
93
|
+
dependencies: dependencyResult.stats.totalDependencies,
|
|
94
|
+
executionTime,
|
|
95
|
+
errors: errors.length,
|
|
96
|
+
warnings: warnings.length,
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
components,
|
|
100
|
+
dependencyResult,
|
|
101
|
+
projectRoot: projectInfo.projectRoot,
|
|
102
|
+
executionTime,
|
|
103
|
+
errors,
|
|
104
|
+
warnings,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Scan metadata files from package directories
|
|
109
|
+
*/
|
|
110
|
+
async scanMetadata(projectRoot, packageDirs, errors, warnings) {
|
|
111
|
+
const scannedPackages = await Promise.all(packageDirs.map(async (packageDir) => {
|
|
112
|
+
const packagePath = path.isAbsolute(packageDir) ? packageDir : path.join(projectRoot, packageDir);
|
|
113
|
+
if (!(await this.fileExists(packagePath))) {
|
|
114
|
+
logger.warn('Package directory not found', { packagePath });
|
|
115
|
+
warnings.push(`Package directory not found: ${packagePath}`);
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
return this.scanPackageDirectory(packagePath, errors, warnings);
|
|
119
|
+
}));
|
|
120
|
+
return scannedPackages.flat();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Scan a package directory for all metadata types
|
|
124
|
+
*/
|
|
125
|
+
async scanPackageDirectory(packagePath, errors, warnings) {
|
|
126
|
+
void warnings;
|
|
127
|
+
const components = [];
|
|
128
|
+
// Scan Apex Classes
|
|
129
|
+
const apexFiles = await this.findFiles(packagePath, '**/classes/**/*.cls');
|
|
130
|
+
const apexComponents = await Promise.all(apexFiles
|
|
131
|
+
.filter((filePath) => !this.shouldIgnore(filePath) && !filePath.endsWith('.cls-meta.xml'))
|
|
132
|
+
.map(async (filePath) => {
|
|
133
|
+
try {
|
|
134
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
135
|
+
const parsed = parseApexClass(filePath, content);
|
|
136
|
+
return {
|
|
137
|
+
name: parsed.className,
|
|
138
|
+
type: 'ApexClass',
|
|
139
|
+
filePath,
|
|
140
|
+
dependencies: toNodeIds(parsed.dependencies.map((d) => d.className), 'ApexClass'),
|
|
141
|
+
dependents: new Set(),
|
|
142
|
+
priorityBoost: 0,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
const errorMsg = `Failed to parse Apex class ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
147
|
+
logger.warn(errorMsg);
|
|
148
|
+
errors.push(errorMsg);
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
components.push(...apexComponents.filter(isDefined));
|
|
153
|
+
// Scan Apex Triggers
|
|
154
|
+
const triggerFiles = await this.findFiles(packagePath, '**/triggers/**/*.trigger');
|
|
155
|
+
const triggerComponents = await Promise.all(triggerFiles
|
|
156
|
+
.filter((filePath) => !this.shouldIgnore(filePath) && !filePath.endsWith('.trigger-meta.xml'))
|
|
157
|
+
.map(async (filePath) => {
|
|
158
|
+
try {
|
|
159
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
160
|
+
const parsed = parseApexTrigger(filePath, content);
|
|
161
|
+
return {
|
|
162
|
+
name: parsed.triggerName,
|
|
163
|
+
type: 'ApexTrigger',
|
|
164
|
+
filePath,
|
|
165
|
+
dependencies: toNodeIds(parsed.dependencies.map((d) => d.className), 'ApexClass'),
|
|
166
|
+
dependents: new Set(),
|
|
167
|
+
priorityBoost: 0,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
const errorMsg = `Failed to parse Apex trigger ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
172
|
+
logger.warn(errorMsg);
|
|
173
|
+
errors.push(errorMsg);
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
}));
|
|
177
|
+
components.push(...triggerComponents.filter(isDefined));
|
|
178
|
+
// Scan LWC Components (container-based)
|
|
179
|
+
const lwcDirs = await this.findDirectories(packagePath, '**/lwc/*');
|
|
180
|
+
const lwcComponents = await Promise.all(lwcDirs
|
|
181
|
+
.filter((lwcDir) => !this.shouldIgnore(lwcDir))
|
|
182
|
+
.map(async (lwcDir) => {
|
|
183
|
+
const componentName = path.basename(lwcDir);
|
|
184
|
+
const jsFile = path.join(lwcDir, `${componentName}.js`);
|
|
185
|
+
const tsFile = path.join(lwcDir, `${componentName}.ts`);
|
|
186
|
+
const metaFile = path.join(lwcDir, `${componentName}.js-meta.xml`);
|
|
187
|
+
const jsExists = await this.fileExists(jsFile);
|
|
188
|
+
const tsExists = jsExists ? false : await this.fileExists(tsFile);
|
|
189
|
+
const codeFile = jsExists ? jsFile : tsExists ? tsFile : null;
|
|
190
|
+
if (!codeFile) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const jsContent = await fs.readFile(codeFile, 'utf-8');
|
|
195
|
+
const metaContent = (await this.fileExists(metaFile)) ? await fs.readFile(metaFile, 'utf-8') : undefined;
|
|
196
|
+
const parsed = parseLWC(componentName, jsContent, metaContent);
|
|
197
|
+
return {
|
|
198
|
+
name: componentName,
|
|
199
|
+
type: 'LightningComponentBundle',
|
|
200
|
+
filePath: codeFile,
|
|
201
|
+
dependencies: new Set([...parsed.apexImports, ...parsed.lwcImports.map((imp) => `c:${imp}`)]),
|
|
202
|
+
dependents: new Set(),
|
|
203
|
+
priorityBoost: 0,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
const errorMsg = `Failed to parse LWC ${componentName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
208
|
+
logger.warn(errorMsg);
|
|
209
|
+
errors.push(errorMsg);
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
}));
|
|
213
|
+
components.push(...lwcComponents.filter(isDefined));
|
|
214
|
+
// Scan Aura Components (container-based)
|
|
215
|
+
const auraDirs = await this.findDirectories(packagePath, '**/aura/*');
|
|
216
|
+
const auraComponents = await Promise.all(auraDirs
|
|
217
|
+
.filter((auraDir) => !this.shouldIgnore(auraDir))
|
|
218
|
+
.map(async (auraDir) => {
|
|
219
|
+
const componentName = path.basename(auraDir);
|
|
220
|
+
const cmpFile = path.join(auraDir, `${componentName}.cmp`);
|
|
221
|
+
if (!(await this.fileExists(cmpFile))) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const cmpContent = await fs.readFile(cmpFile, 'utf-8');
|
|
226
|
+
const parsed = parseAura(componentName, cmpContent);
|
|
227
|
+
const deps = new Set();
|
|
228
|
+
if (parsed.apexController)
|
|
229
|
+
deps.add(parsed.apexController);
|
|
230
|
+
if (parsed.extendsComponent)
|
|
231
|
+
deps.add(parsed.extendsComponent);
|
|
232
|
+
parsed.implementsInterfaces.forEach((i) => deps.add(i));
|
|
233
|
+
parsed.childComponents.forEach((c) => deps.add(`c:${c}`));
|
|
234
|
+
return {
|
|
235
|
+
name: componentName,
|
|
236
|
+
type: 'AuraDefinitionBundle',
|
|
237
|
+
filePath: cmpFile,
|
|
238
|
+
dependencies: deps,
|
|
239
|
+
dependents: new Set(),
|
|
240
|
+
priorityBoost: 0,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
const errorMsg = `Failed to parse Aura component ${componentName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
245
|
+
logger.warn(errorMsg);
|
|
246
|
+
errors.push(errorMsg);
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
}));
|
|
250
|
+
components.push(...auraComponents.filter(isDefined));
|
|
251
|
+
// Scan Flows
|
|
252
|
+
const flowFiles = await this.findFiles(packagePath, '**/flows/**/*.flow-meta.xml');
|
|
253
|
+
const flowComponents = await Promise.all(flowFiles
|
|
254
|
+
.filter((filePath) => !this.shouldIgnore(filePath))
|
|
255
|
+
.map(async (filePath) => {
|
|
256
|
+
try {
|
|
257
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
258
|
+
const parsed = parseFlow(filePath, content);
|
|
259
|
+
const deps = new Set();
|
|
260
|
+
parsed.dependencies.forEach((d) => {
|
|
261
|
+
if (d.type === 'apex_action' || d.type === 'subflow') {
|
|
262
|
+
deps.add(d.name);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
name: parsed.flowName,
|
|
267
|
+
type: 'Flow',
|
|
268
|
+
filePath,
|
|
269
|
+
dependencies: deps,
|
|
270
|
+
dependents: new Set(),
|
|
271
|
+
priorityBoost: 0,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
const errorMsg = `Failed to parse Flow ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
276
|
+
logger.warn(errorMsg);
|
|
277
|
+
errors.push(errorMsg);
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
}));
|
|
281
|
+
components.push(...flowComponents.filter(isDefined));
|
|
282
|
+
// Scan Custom Objects
|
|
283
|
+
const objectDirs = await this.findDirectories(packagePath, '**/objects/*');
|
|
284
|
+
const objectComponents = await Promise.all(objectDirs.map(async (objectDir) => {
|
|
285
|
+
if (this.shouldIgnore(objectDir)) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
const objectName = path.basename(objectDir);
|
|
289
|
+
const objectFile = path.join(objectDir, `${objectName}.object-meta.xml`);
|
|
290
|
+
if (!(await this.fileExists(objectFile))) {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const content = await fs.readFile(objectFile, 'utf-8');
|
|
295
|
+
const parsed = await parseCustomObject(objectName, content);
|
|
296
|
+
const deps = new Set();
|
|
297
|
+
parsed.dependencies.forEach((dependency) => {
|
|
298
|
+
if ((dependency.type === 'lookup_field' || dependency.type === 'master_detail_field') &&
|
|
299
|
+
dependency.referencedObject) {
|
|
300
|
+
deps.add(dependency.referencedObject);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return {
|
|
304
|
+
name: objectName,
|
|
305
|
+
type: 'CustomObject',
|
|
306
|
+
filePath: objectFile,
|
|
307
|
+
dependencies: deps,
|
|
308
|
+
dependents: new Set(),
|
|
309
|
+
priorityBoost: 0,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
const errorMsg = `Failed to parse Custom Object ${objectName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
314
|
+
logger.warn(errorMsg);
|
|
315
|
+
errors.push(errorMsg);
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
}));
|
|
319
|
+
components.push(...objectComponents.filter(isDefined));
|
|
320
|
+
// Scan Custom Metadata Types
|
|
321
|
+
const cmtDirs = await this.findDirectories(packagePath, '**/customMetadata/*');
|
|
322
|
+
const customMetadataComponents = await Promise.all(cmtDirs.map(async (cmtDir) => {
|
|
323
|
+
if (this.shouldIgnore(cmtDir)) {
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
const typeName = path.basename(cmtDir);
|
|
327
|
+
const typeFile = path.join(cmtDir, `${typeName}.md-meta.xml`);
|
|
328
|
+
if (!(await this.fileExists(typeFile))) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
const content = await fs.readFile(typeFile, 'utf-8');
|
|
333
|
+
await parseCustomMetadataType(typeName, content);
|
|
334
|
+
return {
|
|
335
|
+
name: typeName,
|
|
336
|
+
type: 'CustomMetadata',
|
|
337
|
+
filePath: typeFile,
|
|
338
|
+
dependencies: new Set(),
|
|
339
|
+
dependents: new Set(),
|
|
340
|
+
priorityBoost: 0,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
const errorMsg = `Failed to parse Custom Metadata Type ${typeName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
345
|
+
logger.warn(errorMsg);
|
|
346
|
+
errors.push(errorMsg);
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
}));
|
|
350
|
+
components.push(...customMetadataComponents.filter(isDefined));
|
|
351
|
+
// Scan Profiles
|
|
352
|
+
const profileFiles = await this.findFiles(packagePath, '**/profiles/**/*.profile-meta.xml');
|
|
353
|
+
const profileComponents = await Promise.all(profileFiles.map(async (filePath) => {
|
|
354
|
+
if (this.shouldIgnore(filePath)) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
const profileName = path.basename(filePath, '.profile-meta.xml');
|
|
359
|
+
const parsed = await parseProfile(filePath, profileName);
|
|
360
|
+
const deps = new Set();
|
|
361
|
+
parsed.objectPermissions.forEach((permission) => deps.add(permission));
|
|
362
|
+
parsed.apexClassAccesses.forEach((classAccess) => deps.add(classAccess));
|
|
363
|
+
parsed.layoutAssignments.forEach((layoutAssignment) => deps.add(layoutAssignment));
|
|
364
|
+
return {
|
|
365
|
+
name: profileName,
|
|
366
|
+
type: 'Profile',
|
|
367
|
+
filePath,
|
|
368
|
+
dependencies: deps,
|
|
369
|
+
dependents: new Set(),
|
|
370
|
+
priorityBoost: 0,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
const errorMsg = `Failed to parse Profile ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
375
|
+
logger.warn(errorMsg);
|
|
376
|
+
errors.push(errorMsg);
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
379
|
+
}));
|
|
380
|
+
components.push(...profileComponents.filter(isDefined));
|
|
381
|
+
// Scan Permission Sets
|
|
382
|
+
const permSetFiles = await this.findFiles(packagePath, '**/permissionsets/**/*.permissionset-meta.xml');
|
|
383
|
+
const permissionSetComponents = await Promise.all(permSetFiles.map(async (filePath) => {
|
|
384
|
+
if (this.shouldIgnore(filePath)) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
const permSetName = path.basename(filePath, '.permissionset-meta.xml');
|
|
389
|
+
const parsed = await parsePermissionSet(filePath, permSetName);
|
|
390
|
+
const deps = new Set();
|
|
391
|
+
parsed.objectPermissions.forEach((permission) => deps.add(permission));
|
|
392
|
+
parsed.apexClassAccesses.forEach((classAccess) => deps.add(classAccess));
|
|
393
|
+
parsed.customPermissions.forEach((customPermission) => deps.add(customPermission));
|
|
394
|
+
return {
|
|
395
|
+
name: permSetName,
|
|
396
|
+
type: 'PermissionSet',
|
|
397
|
+
filePath,
|
|
398
|
+
dependencies: deps,
|
|
399
|
+
dependents: new Set(),
|
|
400
|
+
priorityBoost: 0,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
const errorMsg = `Failed to parse Permission Set ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
405
|
+
logger.warn(errorMsg);
|
|
406
|
+
errors.push(errorMsg);
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
}));
|
|
410
|
+
components.push(...permissionSetComponents.filter(isDefined));
|
|
411
|
+
// Scan FlexiPages
|
|
412
|
+
const flexipageFiles = await this.findFiles(packagePath, '**/flexipages/**/*.flexipage-meta.xml');
|
|
413
|
+
const flexipageComponents = await Promise.all(flexipageFiles.map(async (filePath) => {
|
|
414
|
+
if (this.shouldIgnore(filePath)) {
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const flexipageName = path.basename(filePath, '.flexipage-meta.xml');
|
|
419
|
+
const parsed = await parseFlexiPage(filePath, flexipageName);
|
|
420
|
+
const deps = new Set();
|
|
421
|
+
parsed.lwcComponents.forEach((componentName) => deps.add(`c:${componentName}`));
|
|
422
|
+
parsed.auraComponents.forEach((componentName) => deps.add(`c:${componentName}`));
|
|
423
|
+
parsed.objects.forEach((objectName) => deps.add(objectName));
|
|
424
|
+
return {
|
|
425
|
+
name: flexipageName,
|
|
426
|
+
type: 'FlexiPage',
|
|
427
|
+
filePath,
|
|
428
|
+
dependencies: deps,
|
|
429
|
+
dependents: new Set(),
|
|
430
|
+
priorityBoost: 0,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
const errorMsg = `Failed to parse FlexiPage ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
435
|
+
logger.warn(errorMsg);
|
|
436
|
+
errors.push(errorMsg);
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
}));
|
|
440
|
+
components.push(...flexipageComponents.filter(isDefined));
|
|
441
|
+
// Scan Layouts
|
|
442
|
+
const layoutFiles = await this.findFiles(packagePath, '**/layouts/**/*.layout-meta.xml');
|
|
443
|
+
const layoutComponents = await Promise.all(layoutFiles.map(async (filePath) => {
|
|
444
|
+
if (this.shouldIgnore(filePath)) {
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const layoutName = path.basename(filePath, '.layout-meta.xml');
|
|
449
|
+
const parsed = await parseLayout(filePath, layoutName);
|
|
450
|
+
const deps = new Set();
|
|
451
|
+
deps.add(parsed.object);
|
|
452
|
+
parsed.customButtons.forEach((buttonName) => deps.add(buttonName));
|
|
453
|
+
parsed.visualforcePages.forEach((pageName) => deps.add(pageName));
|
|
454
|
+
return {
|
|
455
|
+
name: layoutName,
|
|
456
|
+
type: 'Layout',
|
|
457
|
+
filePath,
|
|
458
|
+
dependencies: deps,
|
|
459
|
+
dependents: new Set(),
|
|
460
|
+
priorityBoost: 0,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
const errorMsg = `Failed to parse Layout ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
465
|
+
logger.warn(errorMsg);
|
|
466
|
+
errors.push(errorMsg);
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
}));
|
|
470
|
+
components.push(...layoutComponents.filter(isDefined));
|
|
471
|
+
// Scan Email Templates
|
|
472
|
+
const emailTemplateFiles = await this.findFiles(packagePath, '**/email/**/*.email-meta.xml');
|
|
473
|
+
const emailTemplateComponents = await Promise.all(emailTemplateFiles.map(async (filePath) => {
|
|
474
|
+
if (this.shouldIgnore(filePath)) {
|
|
475
|
+
return undefined;
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const templateName = path.basename(filePath, '.email-meta.xml');
|
|
479
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
480
|
+
const parsed = await parseEmailTemplate(templateName, content, content);
|
|
481
|
+
const deps = new Set();
|
|
482
|
+
parsed.dependencies.forEach((dependency) => {
|
|
483
|
+
if (dependency.type === 'visualforce_page') {
|
|
484
|
+
deps.add(dependency.name);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
return {
|
|
488
|
+
name: templateName,
|
|
489
|
+
type: 'EmailTemplate',
|
|
490
|
+
filePath,
|
|
491
|
+
dependencies: deps,
|
|
492
|
+
dependents: new Set(),
|
|
493
|
+
priorityBoost: 0,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
const errorMsg = `Failed to parse Email Template ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
498
|
+
logger.warn(errorMsg);
|
|
499
|
+
errors.push(errorMsg);
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
502
|
+
}));
|
|
503
|
+
components.push(...emailTemplateComponents.filter(isDefined));
|
|
504
|
+
// Scan Bots
|
|
505
|
+
const botFiles = await this.findFiles(packagePath, '**/bots/**/*.bot-meta.xml');
|
|
506
|
+
const botComponents = await Promise.all(botFiles.map(async (filePath) => {
|
|
507
|
+
if (this.shouldIgnore(filePath)) {
|
|
508
|
+
return undefined;
|
|
509
|
+
}
|
|
510
|
+
try {
|
|
511
|
+
const botName = path.basename(filePath, '.bot-meta.xml');
|
|
512
|
+
const parsed = await parseBot(filePath, botName);
|
|
513
|
+
const deps = new Set();
|
|
514
|
+
parsed.flows.forEach((flowName) => deps.add(flowName));
|
|
515
|
+
parsed.apexActions.forEach((actionName) => deps.add(actionName));
|
|
516
|
+
parsed.genAiPrompts.forEach((promptName) => deps.add(promptName));
|
|
517
|
+
return {
|
|
518
|
+
name: botName,
|
|
519
|
+
type: 'Bot',
|
|
520
|
+
filePath,
|
|
521
|
+
dependencies: deps,
|
|
522
|
+
dependents: new Set(),
|
|
523
|
+
priorityBoost: 0,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
catch (error) {
|
|
527
|
+
const errorMsg = `Failed to parse Bot ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
528
|
+
logger.warn(errorMsg);
|
|
529
|
+
errors.push(errorMsg);
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
}));
|
|
533
|
+
components.push(...botComponents.filter(isDefined));
|
|
534
|
+
// Scan GenAI Prompts
|
|
535
|
+
const genaiFiles = await this.findFiles(packagePath, '**/genaiPromptTemplates/**/*.genAiPromptTemplate-meta.xml');
|
|
536
|
+
const genAiPromptComponents = await Promise.all(genaiFiles.map(async (filePath) => {
|
|
537
|
+
if (this.shouldIgnore(filePath)) {
|
|
538
|
+
return undefined;
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
const promptName = path.basename(filePath, '.genAiPromptTemplate-meta.xml');
|
|
542
|
+
const parsed = await parseGenAiPrompt(filePath, promptName);
|
|
543
|
+
const deps = new Set();
|
|
544
|
+
parsed.sobjects.forEach((sObjectName) => deps.add(sObjectName));
|
|
545
|
+
parsed.dependencies.sobjects.forEach((sObjectName) => deps.add(sObjectName));
|
|
546
|
+
return {
|
|
547
|
+
name: promptName,
|
|
548
|
+
type: 'GenAiPromptTemplate',
|
|
549
|
+
filePath,
|
|
550
|
+
dependencies: deps,
|
|
551
|
+
dependents: new Set(),
|
|
552
|
+
priorityBoost: 0,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
const errorMsg = `Failed to parse GenAI Prompt ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
557
|
+
logger.warn(errorMsg);
|
|
558
|
+
errors.push(errorMsg);
|
|
559
|
+
return undefined;
|
|
560
|
+
}
|
|
561
|
+
}));
|
|
562
|
+
components.push(...genAiPromptComponents.filter(isDefined));
|
|
563
|
+
// Scan Visualforce Pages/Components
|
|
564
|
+
const vfPageFiles = await this.findFiles(packagePath, '**/pages/**/*.page');
|
|
565
|
+
const vfComponentFiles = await this.findFiles(packagePath, '**/components/**/*.component');
|
|
566
|
+
const visualforceComponents = await Promise.all([...vfPageFiles, ...vfComponentFiles].map(async (filePath) => {
|
|
567
|
+
if (this.shouldIgnore(filePath)) {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
572
|
+
const fileName = path.basename(filePath);
|
|
573
|
+
const parsed = parseVisualforce(fileName, content);
|
|
574
|
+
const deps = new Set();
|
|
575
|
+
parsed.dependencies.forEach((dependency) => {
|
|
576
|
+
if (dependency.type === 'apex_controller' || dependency.type === 'apex_extension') {
|
|
577
|
+
deps.add(dependency.name);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
const componentType = parsed.type === 'page' ? 'VisualforcePage' : 'VisualforceComponent';
|
|
581
|
+
return {
|
|
582
|
+
name: parsed.name,
|
|
583
|
+
type: componentType,
|
|
584
|
+
filePath,
|
|
585
|
+
dependencies: deps,
|
|
586
|
+
dependents: new Set(),
|
|
587
|
+
priorityBoost: 0,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
catch (error) {
|
|
591
|
+
const errorMsg = `Failed to parse Visualforce ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
592
|
+
logger.warn(errorMsg);
|
|
593
|
+
errors.push(errorMsg);
|
|
594
|
+
return undefined;
|
|
595
|
+
}
|
|
596
|
+
}));
|
|
597
|
+
components.push(...visualforceComponents.filter(isDefined));
|
|
598
|
+
return components;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Find files matching a glob pattern
|
|
602
|
+
*/
|
|
603
|
+
async findFiles(rootPath, pattern) {
|
|
604
|
+
const fullPattern = path.join(rootPath, pattern);
|
|
605
|
+
const files = await globAsync(fullPattern, {
|
|
606
|
+
ignore: ['**/node_modules/**', '**/.git/**'],
|
|
607
|
+
});
|
|
608
|
+
return files;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Find directories matching a glob pattern
|
|
612
|
+
*/
|
|
613
|
+
async findDirectories(rootPath, pattern) {
|
|
614
|
+
const fullPattern = path.join(rootPath, pattern);
|
|
615
|
+
const allMatches = await globAsync(fullPattern, {
|
|
616
|
+
ignore: ['**/node_modules/**', '**/.git/**'],
|
|
617
|
+
});
|
|
618
|
+
const directoryMatches = await Promise.all(allMatches.map(async (match) => {
|
|
619
|
+
try {
|
|
620
|
+
const stat = await fs.stat(match);
|
|
621
|
+
return stat.isDirectory() ? match : undefined;
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
626
|
+
}));
|
|
627
|
+
return directoryMatches.filter((match) => match !== undefined);
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Check if file should be ignored by .forceignore
|
|
631
|
+
*/
|
|
632
|
+
shouldIgnore(filePath) {
|
|
633
|
+
if (!this.forceIgnoreParser) {
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
return this.forceIgnoreParser.isIgnored(filePath);
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Check if file/directory exists
|
|
640
|
+
*/
|
|
641
|
+
async fileExists(filePath) {
|
|
642
|
+
try {
|
|
643
|
+
await fs.access(filePath);
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
catch {
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
//# sourceMappingURL=metadata-scanner-service.js.map
|