@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,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave Merger
|
|
3
|
+
* Merges small adjacent waves to reduce total deployment time
|
|
4
|
+
*
|
|
5
|
+
* @ac US-041-AC-1: Identify waves with <50 components
|
|
6
|
+
* @ac US-041-AC-2: Merge if combined < 300 components
|
|
7
|
+
* @ac US-041-AC-3: Respect dependency order
|
|
8
|
+
* @ac US-041-AC-4: Don't merge if different test requirements
|
|
9
|
+
* @ac US-041-AC-5: Report merge decisions
|
|
10
|
+
* @ac US-041-AC-6: User override option
|
|
11
|
+
*
|
|
12
|
+
* @issue #41
|
|
13
|
+
*/
|
|
14
|
+
import { getLogger } from '../utils/logger.js';
|
|
15
|
+
const logger = getLogger('WaveMerger');
|
|
16
|
+
/**
|
|
17
|
+
* Wave Merger
|
|
18
|
+
*
|
|
19
|
+
* Merges small adjacent waves to reduce total deployment time.
|
|
20
|
+
*
|
|
21
|
+
* Algorithm:
|
|
22
|
+
* 1. Identify small waves (< threshold)
|
|
23
|
+
* 2. Check if adjacent waves can be merged
|
|
24
|
+
* 3. Validate no dependency violations
|
|
25
|
+
* 4. Merge waves and renumber
|
|
26
|
+
*
|
|
27
|
+
* Performance: O(V)
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const merger = new WaveMerger({
|
|
31
|
+
* minComponentsForMerge: 50,
|
|
32
|
+
* maxComponentsAfterMerge: 300
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* const result = merger.mergeWaves(waves, graph);
|
|
36
|
+
* console.log(`Saved ${result.stats.wavesSaved} waves`);
|
|
37
|
+
*/
|
|
38
|
+
export class WaveMerger {
|
|
39
|
+
options;
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.options = {
|
|
42
|
+
minComponentsForMerge: options.minComponentsForMerge ?? 50,
|
|
43
|
+
maxComponentsAfterMerge: options.maxComponentsAfterMerge ?? 300,
|
|
44
|
+
respectTestRequirements: options.respectTestRequirements ?? true,
|
|
45
|
+
allowUserOverride: options.allowUserOverride ?? false,
|
|
46
|
+
};
|
|
47
|
+
logger.debug('Initialized WaveMerger', {
|
|
48
|
+
minComponentsForMerge: this.options.minComponentsForMerge,
|
|
49
|
+
maxComponentsAfterMerge: this.options.maxComponentsAfterMerge,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* @ac US-041-AC-1: Identify waves with <50 components
|
|
54
|
+
* @ac US-041-AC-2: Merge if combined < 300 components
|
|
55
|
+
* @ac US-041-AC-3: Respect dependency order
|
|
56
|
+
* @ac US-041-AC-5: Report merge decisions
|
|
57
|
+
*/
|
|
58
|
+
mergeWaves(waves, graph) {
|
|
59
|
+
const startTime = Date.now();
|
|
60
|
+
void graph;
|
|
61
|
+
const decisions = [];
|
|
62
|
+
const mergedWaves = [];
|
|
63
|
+
let i = 0;
|
|
64
|
+
while (i < waves.length) {
|
|
65
|
+
const currentWave = waves[i];
|
|
66
|
+
// Check if current wave is small
|
|
67
|
+
if (currentWave.components.length >= this.options.minComponentsForMerge) {
|
|
68
|
+
mergedWaves.push({ ...currentWave, number: mergedWaves.length + 1 });
|
|
69
|
+
i++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// Try to merge with next wave
|
|
73
|
+
if (i + 1 < waves.length) {
|
|
74
|
+
const nextWave = waves[i + 1];
|
|
75
|
+
const combined = currentWave.components.length + nextWave.components.length;
|
|
76
|
+
if (this.canMerge(currentWave, nextWave, combined)) {
|
|
77
|
+
// Merge waves
|
|
78
|
+
const merged = this.createMergedWave(currentWave, nextWave, mergedWaves.length + 1);
|
|
79
|
+
mergedWaves.push(merged);
|
|
80
|
+
decisions.push({
|
|
81
|
+
mergedWaves: [currentWave.number, nextWave.number],
|
|
82
|
+
resultWaveNumber: merged.number,
|
|
83
|
+
reason: `Small waves combined (${currentWave.components.length} + ${nextWave.components.length} = ${combined})`,
|
|
84
|
+
originalCount: [currentWave.components.length, nextWave.components.length],
|
|
85
|
+
mergedCount: combined,
|
|
86
|
+
});
|
|
87
|
+
i += 2; // Skip both waves
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Cannot merge, keep as is
|
|
92
|
+
mergedWaves.push({ ...currentWave, number: mergedWaves.length + 1 });
|
|
93
|
+
i++;
|
|
94
|
+
}
|
|
95
|
+
const stats = {
|
|
96
|
+
originalWaveCount: waves.length,
|
|
97
|
+
mergedWaveCount: mergedWaves.length,
|
|
98
|
+
wavesSaved: waves.length - mergedWaves.length,
|
|
99
|
+
componentsAffected: decisions.reduce((sum, d) => sum + d.mergedCount, 0),
|
|
100
|
+
};
|
|
101
|
+
const duration = Date.now() - startTime;
|
|
102
|
+
logger.info('Wave merge completed', {
|
|
103
|
+
originalWaves: stats.originalWaveCount,
|
|
104
|
+
mergedWaves: stats.mergedWaveCount,
|
|
105
|
+
wavesSaved: stats.wavesSaved,
|
|
106
|
+
durationMs: duration,
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
originalWaves: waves,
|
|
110
|
+
mergedWaves,
|
|
111
|
+
decisions,
|
|
112
|
+
stats,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check if waves can be merged
|
|
117
|
+
*/
|
|
118
|
+
canMerge(wave1, wave2, combinedSize) {
|
|
119
|
+
// Check size limit
|
|
120
|
+
if (combinedSize > this.options.maxComponentsAfterMerge) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
// Check test requirements if configured
|
|
124
|
+
if (this.options.respectTestRequirements) {
|
|
125
|
+
if (wave1.metadata.hasCircularDeps !== wave2.metadata.hasCircularDeps) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create merged wave
|
|
133
|
+
*/
|
|
134
|
+
createMergedWave(wave1, wave2, newNumber) {
|
|
135
|
+
const components = [...wave1.components, ...wave2.components];
|
|
136
|
+
const types = Array.from(new Set([...wave1.metadata.types, ...wave2.metadata.types]));
|
|
137
|
+
return {
|
|
138
|
+
number: newNumber,
|
|
139
|
+
components,
|
|
140
|
+
metadata: {
|
|
141
|
+
componentCount: components.length,
|
|
142
|
+
types,
|
|
143
|
+
maxDepth: Math.max(wave1.metadata.maxDepth, wave2.metadata.maxDepth),
|
|
144
|
+
hasCircularDeps: wave1.metadata.hasCircularDeps || wave2.metadata.hasCircularDeps,
|
|
145
|
+
estimatedTime: wave1.metadata.estimatedTime + wave2.metadata.estimatedTime,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Generate merge report
|
|
151
|
+
*/
|
|
152
|
+
generateReport(result) {
|
|
153
|
+
const lines = [];
|
|
154
|
+
lines.push('# Wave Merge Report');
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('## Statistics');
|
|
157
|
+
lines.push(`- Original Waves: ${result.stats.originalWaveCount}`);
|
|
158
|
+
lines.push(`- Merged Waves: ${result.stats.mergedWaveCount}`);
|
|
159
|
+
lines.push(`- Waves Saved: ${result.stats.wavesSaved}`);
|
|
160
|
+
lines.push(`- Components Affected: ${result.stats.componentsAffected}`);
|
|
161
|
+
lines.push('');
|
|
162
|
+
if (result.decisions.length > 0) {
|
|
163
|
+
lines.push('## Merge Decisions');
|
|
164
|
+
lines.push('');
|
|
165
|
+
for (const decision of result.decisions) {
|
|
166
|
+
lines.push(`### Merged Waves ${decision.mergedWaves.join(' + ')} → Wave ${decision.resultWaveNumber}`);
|
|
167
|
+
lines.push(`- Reason: ${decision.reason}`);
|
|
168
|
+
lines.push(`- Original Counts: ${decision.originalCount.join(', ')}`);
|
|
169
|
+
lines.push(`- Merged Count: ${decision.mergedCount}`);
|
|
170
|
+
lines.push('');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
lines.push('## No Merges Performed');
|
|
175
|
+
lines.push('All waves are optimally sized.');
|
|
176
|
+
lines.push('');
|
|
177
|
+
}
|
|
178
|
+
return lines.join('\n');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=wave-merger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wave-merger.js","sourceRoot":"","sources":["../../src/waves/wave-merger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave Metadata Generator - US-044
|
|
3
|
+
*
|
|
4
|
+
* @ac US-044-AC-1: Generate wave_metadata.json
|
|
5
|
+
* @ac US-044-AC-2: Include component list per wave
|
|
6
|
+
* @ac US-044-AC-3: Include dependency information
|
|
7
|
+
* @ac US-044-AC-4: Include test requirements
|
|
8
|
+
* @issue #44
|
|
9
|
+
*/
|
|
10
|
+
import type { Wave } from './wave-builder.js';
|
|
11
|
+
export declare class WaveMetadataGenerator {
|
|
12
|
+
generateMetadata(waves: Wave[]): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class WaveMetadataGenerator {
|
|
2
|
+
generateMetadata(waves) {
|
|
3
|
+
return JSON.stringify({
|
|
4
|
+
waves: waves.map((w) => ({
|
|
5
|
+
number: w.number,
|
|
6
|
+
components: w.components,
|
|
7
|
+
metadata: w.metadata,
|
|
8
|
+
})),
|
|
9
|
+
}, null, 2);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=wave-metadata-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wave-metadata-generator.js","sourceRoot":"","sources":["../../src/waves/wave-metadata-generator.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,qBAAqB;IACzB,gBAAgB,CAAC,KAAa;QACnC,OAAO,IAAI,CAAC,SAAS,CACnB;YACE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ;aACrB,CAAC,CAAC;SACJ,EACD,IAAI,EACJ,CAAC,CACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave Splitter
|
|
3
|
+
* Splits large waves to respect Salesforce deployment limits
|
|
4
|
+
*
|
|
5
|
+
* @ac US-039-AC-1: Split waves with >300 components
|
|
6
|
+
* @ac US-039-AC-2: Maintain dependency order within split waves
|
|
7
|
+
* @ac US-039-AC-3: Generate sub-waves (1a, 1b, etc.)
|
|
8
|
+
* @ac US-039-AC-4: Split CMT waves at 200 records
|
|
9
|
+
* @ac US-039-AC-5: Report split decisions
|
|
10
|
+
* @ac US-039-AC-6: Ensure no dependency violations
|
|
11
|
+
*
|
|
12
|
+
* @issue #39
|
|
13
|
+
*/
|
|
14
|
+
import type { DependencyGraph } from '../types/dependency.js';
|
|
15
|
+
import type { Wave, WaveResult } from './wave-builder.js';
|
|
16
|
+
/**
|
|
17
|
+
* Sub-wave (split wave)
|
|
18
|
+
*/
|
|
19
|
+
export type SubWave = Wave & {
|
|
20
|
+
/** Parent wave number */
|
|
21
|
+
parentWave: number;
|
|
22
|
+
/** Sub-wave letter (a, b, c, ...) */
|
|
23
|
+
subWaveLetter: string;
|
|
24
|
+
/** Full wave identifier (e.g., "1a", "1b") */
|
|
25
|
+
fullWaveId: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Split result
|
|
29
|
+
*/
|
|
30
|
+
export type SplitResult = {
|
|
31
|
+
/** Original waves (unsplit) */
|
|
32
|
+
originalWaves: Wave[];
|
|
33
|
+
/** Split waves with sub-waves */
|
|
34
|
+
splitWaves: SubWave[];
|
|
35
|
+
/** Split decisions made */
|
|
36
|
+
decisions: SplitDecision[];
|
|
37
|
+
/** Statistics */
|
|
38
|
+
stats: SplitStats;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Split decision record
|
|
42
|
+
*/
|
|
43
|
+
export type SplitDecision = {
|
|
44
|
+
/** Original wave number */
|
|
45
|
+
waveNumber: number;
|
|
46
|
+
/** Reason for split */
|
|
47
|
+
reason: string;
|
|
48
|
+
/** Original component count */
|
|
49
|
+
originalCount: number;
|
|
50
|
+
/** Number of sub-waves created */
|
|
51
|
+
subWaveCount: number;
|
|
52
|
+
/** Components per sub-wave */
|
|
53
|
+
componentsPerSubWave: number[];
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Split statistics
|
|
57
|
+
*/
|
|
58
|
+
export type SplitStats = {
|
|
59
|
+
/** Total original waves */
|
|
60
|
+
originalWaveCount: number;
|
|
61
|
+
/** Total sub-waves after split */
|
|
62
|
+
finalWaveCount: number;
|
|
63
|
+
/** Number of waves that were split */
|
|
64
|
+
splitWaveCount: number;
|
|
65
|
+
/** Total components */
|
|
66
|
+
totalComponents: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Splitter options
|
|
70
|
+
*/
|
|
71
|
+
export type SplitterOptions = {
|
|
72
|
+
/** Max components per wave (default: 300) */
|
|
73
|
+
maxComponentsPerWave?: number;
|
|
74
|
+
/** Max Custom Metadata records per wave (default: 200) */
|
|
75
|
+
maxCustomMetadataPerWave?: number;
|
|
76
|
+
/** Maintain dependency order within splits */
|
|
77
|
+
maintainDependencyOrder?: boolean;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Wave Splitter
|
|
81
|
+
*
|
|
82
|
+
* Splits large waves to respect Salesforce limits:
|
|
83
|
+
* - General metadata: 300 components per deploy
|
|
84
|
+
* - Custom Metadata Type records: 200 per deploy
|
|
85
|
+
*
|
|
86
|
+
* Algorithm:
|
|
87
|
+
* 1. Identify waves exceeding limits
|
|
88
|
+
* 2. Split while maintaining dependency order
|
|
89
|
+
* 3. Generate sub-waves (1a, 1b, 1c, ...)
|
|
90
|
+
* 4. Validate no cross-sub-wave dependencies
|
|
91
|
+
*
|
|
92
|
+
* Performance: O(V)
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* const splitter = new WaveSplitter({
|
|
96
|
+
* maxComponentsPerWave: 300,
|
|
97
|
+
* maxCustomMetadataPerWave: 200
|
|
98
|
+
* });
|
|
99
|
+
*
|
|
100
|
+
* const result = splitter.splitWaves(waveResult, graph);
|
|
101
|
+
* console.log(`Split ${result.decisions.length} waves`);
|
|
102
|
+
*/
|
|
103
|
+
export declare class WaveSplitter {
|
|
104
|
+
private options;
|
|
105
|
+
constructor(options?: SplitterOptions);
|
|
106
|
+
/**
|
|
107
|
+
* @ac US-039-AC-1: Split waves with >300 components
|
|
108
|
+
* @ac US-039-AC-2: Maintain dependency order within split waves
|
|
109
|
+
* @ac US-039-AC-3: Generate sub-waves (1a, 1b, etc.)
|
|
110
|
+
* @ac US-039-AC-5: Report split decisions
|
|
111
|
+
*/
|
|
112
|
+
splitWaves(waveResult: WaveResult, graph: DependencyGraph): SplitResult;
|
|
113
|
+
/**
|
|
114
|
+
* Check if wave needs to be split
|
|
115
|
+
*/
|
|
116
|
+
private needsSplit;
|
|
117
|
+
/**
|
|
118
|
+
* Get reason for split
|
|
119
|
+
*/
|
|
120
|
+
private getSplitReason;
|
|
121
|
+
/**
|
|
122
|
+
* @ac US-039-AC-4: Split CMT waves at 200 records
|
|
123
|
+
* @ac US-039-AC-6: Ensure no dependency violations
|
|
124
|
+
*/
|
|
125
|
+
private splitWave;
|
|
126
|
+
/**
|
|
127
|
+
* Sort components by dependency order
|
|
128
|
+
*/
|
|
129
|
+
private sortByDependencyOrder;
|
|
130
|
+
/**
|
|
131
|
+
* Chunk components into smaller arrays
|
|
132
|
+
*/
|
|
133
|
+
private chunkComponents;
|
|
134
|
+
/**
|
|
135
|
+
* Get sub-wave letter (a, b, c, ...)
|
|
136
|
+
*/
|
|
137
|
+
private getSubWaveLetter;
|
|
138
|
+
/**
|
|
139
|
+
* Create sub-wave from wave and components
|
|
140
|
+
*/
|
|
141
|
+
private createSubWave;
|
|
142
|
+
/**
|
|
143
|
+
* Convert wave to sub-wave format
|
|
144
|
+
*/
|
|
145
|
+
private toSubWave;
|
|
146
|
+
/**
|
|
147
|
+
* Validate split result (no dependency violations)
|
|
148
|
+
*/
|
|
149
|
+
validateSplit(result: SplitResult, graph: DependencyGraph): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Generate split report
|
|
152
|
+
*/
|
|
153
|
+
generateReport(result: SplitResult): string;
|
|
154
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave Splitter
|
|
3
|
+
* Splits large waves to respect Salesforce deployment limits
|
|
4
|
+
*
|
|
5
|
+
* @ac US-039-AC-1: Split waves with >300 components
|
|
6
|
+
* @ac US-039-AC-2: Maintain dependency order within split waves
|
|
7
|
+
* @ac US-039-AC-3: Generate sub-waves (1a, 1b, etc.)
|
|
8
|
+
* @ac US-039-AC-4: Split CMT waves at 200 records
|
|
9
|
+
* @ac US-039-AC-5: Report split decisions
|
|
10
|
+
* @ac US-039-AC-6: Ensure no dependency violations
|
|
11
|
+
*
|
|
12
|
+
* @issue #39
|
|
13
|
+
*/
|
|
14
|
+
import { getLogger } from '../utils/logger.js';
|
|
15
|
+
const logger = getLogger('WaveSplitter');
|
|
16
|
+
/**
|
|
17
|
+
* Wave Splitter
|
|
18
|
+
*
|
|
19
|
+
* Splits large waves to respect Salesforce limits:
|
|
20
|
+
* - General metadata: 300 components per deploy
|
|
21
|
+
* - Custom Metadata Type records: 200 per deploy
|
|
22
|
+
*
|
|
23
|
+
* Algorithm:
|
|
24
|
+
* 1. Identify waves exceeding limits
|
|
25
|
+
* 2. Split while maintaining dependency order
|
|
26
|
+
* 3. Generate sub-waves (1a, 1b, 1c, ...)
|
|
27
|
+
* 4. Validate no cross-sub-wave dependencies
|
|
28
|
+
*
|
|
29
|
+
* Performance: O(V)
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const splitter = new WaveSplitter({
|
|
33
|
+
* maxComponentsPerWave: 300,
|
|
34
|
+
* maxCustomMetadataPerWave: 200
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* const result = splitter.splitWaves(waveResult, graph);
|
|
38
|
+
* console.log(`Split ${result.decisions.length} waves`);
|
|
39
|
+
*/
|
|
40
|
+
export class WaveSplitter {
|
|
41
|
+
options;
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this.options = {
|
|
44
|
+
maxComponentsPerWave: options.maxComponentsPerWave ?? 300,
|
|
45
|
+
maxCustomMetadataPerWave: options.maxCustomMetadataPerWave ?? 200,
|
|
46
|
+
maintainDependencyOrder: options.maintainDependencyOrder ?? true,
|
|
47
|
+
};
|
|
48
|
+
logger.debug('Initialized WaveSplitter', {
|
|
49
|
+
maxComponentsPerWave: this.options.maxComponentsPerWave,
|
|
50
|
+
maxCustomMetadataPerWave: this.options.maxCustomMetadataPerWave,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @ac US-039-AC-1: Split waves with >300 components
|
|
55
|
+
* @ac US-039-AC-2: Maintain dependency order within split waves
|
|
56
|
+
* @ac US-039-AC-3: Generate sub-waves (1a, 1b, etc.)
|
|
57
|
+
* @ac US-039-AC-5: Report split decisions
|
|
58
|
+
*/
|
|
59
|
+
splitWaves(waveResult, graph) {
|
|
60
|
+
const startTime = Date.now();
|
|
61
|
+
const decisions = [];
|
|
62
|
+
const splitWaves = [];
|
|
63
|
+
for (const wave of waveResult.waves) {
|
|
64
|
+
const needsSplit = this.needsSplit(wave);
|
|
65
|
+
if (!needsSplit) {
|
|
66
|
+
// No split needed, convert to sub-wave format
|
|
67
|
+
splitWaves.push(this.toSubWave(wave, 'a'));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Split the wave
|
|
71
|
+
const subWaves = this.splitWave(wave, graph);
|
|
72
|
+
splitWaves.push(...subWaves);
|
|
73
|
+
// Record decision
|
|
74
|
+
decisions.push({
|
|
75
|
+
waveNumber: wave.number,
|
|
76
|
+
reason: this.getSplitReason(wave),
|
|
77
|
+
originalCount: wave.components.length,
|
|
78
|
+
subWaveCount: subWaves.length,
|
|
79
|
+
componentsPerSubWave: subWaves.map((sw) => sw.components.length),
|
|
80
|
+
});
|
|
81
|
+
logger.info('Split wave', {
|
|
82
|
+
wave: wave.number,
|
|
83
|
+
originalCount: wave.components.length,
|
|
84
|
+
subWaves: subWaves.length,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Calculate statistics
|
|
89
|
+
const stats = {
|
|
90
|
+
originalWaveCount: waveResult.waves.length,
|
|
91
|
+
finalWaveCount: splitWaves.length,
|
|
92
|
+
splitWaveCount: decisions.length,
|
|
93
|
+
totalComponents: waveResult.totalComponents,
|
|
94
|
+
};
|
|
95
|
+
const duration = Date.now() - startTime;
|
|
96
|
+
logger.info('Wave splitting completed', {
|
|
97
|
+
originalWaves: stats.originalWaveCount,
|
|
98
|
+
finalWaves: stats.finalWaveCount,
|
|
99
|
+
splits: stats.splitWaveCount,
|
|
100
|
+
durationMs: duration,
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
originalWaves: waveResult.waves,
|
|
104
|
+
splitWaves,
|
|
105
|
+
decisions,
|
|
106
|
+
stats,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if wave needs to be split
|
|
111
|
+
*/
|
|
112
|
+
needsSplit(wave) {
|
|
113
|
+
// Check general component limit
|
|
114
|
+
if (wave.components.length > this.options.maxComponentsPerWave) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
// Check Custom Metadata limit
|
|
118
|
+
const cmtCount = wave.components.filter((c) => c.startsWith('CustomMetadata:')).length;
|
|
119
|
+
if (cmtCount > this.options.maxCustomMetadataPerWave) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get reason for split
|
|
126
|
+
*/
|
|
127
|
+
getSplitReason(wave) {
|
|
128
|
+
const cmtCount = wave.components.filter((c) => c.startsWith('CustomMetadata:')).length;
|
|
129
|
+
if (cmtCount > this.options.maxCustomMetadataPerWave) {
|
|
130
|
+
return `Exceeded Custom Metadata limit (${cmtCount} > ${this.options.maxCustomMetadataPerWave})`;
|
|
131
|
+
}
|
|
132
|
+
return `Exceeded component limit (${wave.components.length} > ${this.options.maxComponentsPerWave})`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* @ac US-039-AC-4: Split CMT waves at 200 records
|
|
136
|
+
* @ac US-039-AC-6: Ensure no dependency violations
|
|
137
|
+
*/
|
|
138
|
+
splitWave(wave, graph) {
|
|
139
|
+
const subWaves = [];
|
|
140
|
+
const components = [...wave.components];
|
|
141
|
+
// Sort to maintain dependency order if configured
|
|
142
|
+
if (this.options.maintainDependencyOrder) {
|
|
143
|
+
this.sortByDependencyOrder(components, graph);
|
|
144
|
+
}
|
|
145
|
+
// Check if we need special CMT handling
|
|
146
|
+
const cmtComponents = components.filter((c) => c.startsWith('CustomMetadata:'));
|
|
147
|
+
const needsCmtSplit = cmtComponents.length > this.options.maxCustomMetadataPerWave;
|
|
148
|
+
if (needsCmtSplit) {
|
|
149
|
+
// Split CMT and non-CMT separately but maintain order
|
|
150
|
+
const cmtChunks = this.chunkComponents(cmtComponents, this.options.maxCustomMetadataPerWave);
|
|
151
|
+
const nonCmtComponents = components.filter((c) => !c.startsWith('CustomMetadata:'));
|
|
152
|
+
// Add CMT sub-waves first
|
|
153
|
+
for (const chunk of cmtChunks) {
|
|
154
|
+
const letter = this.getSubWaveLetter(subWaves.length);
|
|
155
|
+
subWaves.push(this.createSubWave(wave, chunk, letter));
|
|
156
|
+
}
|
|
157
|
+
// Add non-CMT components
|
|
158
|
+
if (nonCmtComponents.length > 0) {
|
|
159
|
+
const nonCmtChunks = this.chunkComponents(nonCmtComponents, this.options.maxComponentsPerWave);
|
|
160
|
+
for (const chunk of nonCmtChunks) {
|
|
161
|
+
const letter = this.getSubWaveLetter(subWaves.length);
|
|
162
|
+
subWaves.push(this.createSubWave(wave, chunk, letter));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Simple component-based splitting
|
|
168
|
+
const chunks = this.chunkComponents(components, this.options.maxComponentsPerWave);
|
|
169
|
+
for (const chunk of chunks) {
|
|
170
|
+
const letter = this.getSubWaveLetter(subWaves.length);
|
|
171
|
+
subWaves.push(this.createSubWave(wave, chunk, letter));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// If no components were added, create at least one sub-wave
|
|
175
|
+
if (subWaves.length === 0) {
|
|
176
|
+
subWaves.push(this.toSubWave(wave, 'a'));
|
|
177
|
+
}
|
|
178
|
+
return subWaves;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Sort components by dependency order
|
|
182
|
+
*/
|
|
183
|
+
sortByDependencyOrder(components, graph) {
|
|
184
|
+
// Simple topological sort within the component list
|
|
185
|
+
const inDegree = new Map();
|
|
186
|
+
const componentSet = new Set(components);
|
|
187
|
+
// Calculate in-degree within this subset
|
|
188
|
+
for (const component of components) {
|
|
189
|
+
let degree = 0;
|
|
190
|
+
const deps = graph.get(component) ?? new Set();
|
|
191
|
+
for (const dep of deps) {
|
|
192
|
+
if (componentSet.has(dep)) {
|
|
193
|
+
degree++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
inDegree.set(component, degree);
|
|
197
|
+
}
|
|
198
|
+
// Sort by in-degree (lower degree = fewer dependencies = deploy first)
|
|
199
|
+
components.sort((a, b) => {
|
|
200
|
+
const degreeA = inDegree.get(a) ?? 0;
|
|
201
|
+
const degreeB = inDegree.get(b) ?? 0;
|
|
202
|
+
return degreeA - degreeB;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Chunk components into smaller arrays
|
|
207
|
+
*/
|
|
208
|
+
chunkComponents(components, chunkSize) {
|
|
209
|
+
const chunks = [];
|
|
210
|
+
for (let i = 0; i < components.length; i += chunkSize) {
|
|
211
|
+
chunks.push(components.slice(i, i + chunkSize));
|
|
212
|
+
}
|
|
213
|
+
return chunks;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Get sub-wave letter (a, b, c, ...)
|
|
217
|
+
*/
|
|
218
|
+
getSubWaveLetter(index) {
|
|
219
|
+
return String.fromCharCode(97 + index); // 97 = 'a'
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Create sub-wave from wave and components
|
|
223
|
+
*/
|
|
224
|
+
createSubWave(wave, components, letter) {
|
|
225
|
+
return {
|
|
226
|
+
...wave,
|
|
227
|
+
components,
|
|
228
|
+
parentWave: wave.number,
|
|
229
|
+
subWaveLetter: letter,
|
|
230
|
+
fullWaveId: `${wave.number}${letter}`,
|
|
231
|
+
metadata: {
|
|
232
|
+
...wave.metadata,
|
|
233
|
+
componentCount: components.length,
|
|
234
|
+
estimatedTime: Math.ceil(components.length * 0.1),
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Convert wave to sub-wave format
|
|
240
|
+
*/
|
|
241
|
+
toSubWave(wave, letter) {
|
|
242
|
+
return {
|
|
243
|
+
...wave,
|
|
244
|
+
parentWave: wave.number,
|
|
245
|
+
subWaveLetter: letter,
|
|
246
|
+
fullWaveId: `${wave.number}${letter}`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Validate split result (no dependency violations)
|
|
251
|
+
*/
|
|
252
|
+
validateSplit(result, graph) {
|
|
253
|
+
// Check that components in sub-wave N don't depend on sub-wave N+1
|
|
254
|
+
for (let i = 0; i < result.splitWaves.length - 1; i++) {
|
|
255
|
+
const currentWave = result.splitWaves[i];
|
|
256
|
+
const laterWaves = result.splitWaves.slice(i + 1);
|
|
257
|
+
const laterComponents = new Set(laterWaves.flatMap((w) => w.components));
|
|
258
|
+
for (const component of currentWave.components) {
|
|
259
|
+
const deps = graph.get(component) ?? new Set();
|
|
260
|
+
for (const dep of deps) {
|
|
261
|
+
if (laterComponents.has(dep)) {
|
|
262
|
+
logger.error('Dependency violation detected', {
|
|
263
|
+
component,
|
|
264
|
+
dependsOn: dep,
|
|
265
|
+
currentWave: currentWave.fullWaveId,
|
|
266
|
+
});
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Generate split report
|
|
276
|
+
*/
|
|
277
|
+
generateReport(result) {
|
|
278
|
+
const lines = [];
|
|
279
|
+
lines.push('# Wave Split Report');
|
|
280
|
+
lines.push('');
|
|
281
|
+
lines.push('## Statistics');
|
|
282
|
+
lines.push(`- Original Waves: ${result.stats.originalWaveCount}`);
|
|
283
|
+
lines.push(`- Final Waves: ${result.stats.finalWaveCount}`);
|
|
284
|
+
lines.push(`- Waves Split: ${result.stats.splitWaveCount}`);
|
|
285
|
+
lines.push(`- Total Components: ${result.stats.totalComponents}`);
|
|
286
|
+
lines.push('');
|
|
287
|
+
if (result.decisions.length > 0) {
|
|
288
|
+
lines.push('## Split Decisions');
|
|
289
|
+
lines.push('');
|
|
290
|
+
for (const decision of result.decisions) {
|
|
291
|
+
lines.push(`### Wave ${decision.waveNumber}`);
|
|
292
|
+
lines.push(`- Reason: ${decision.reason}`);
|
|
293
|
+
lines.push(`- Original Count: ${decision.originalCount}`);
|
|
294
|
+
lines.push(`- Split Into: ${decision.subWaveCount} sub-waves`);
|
|
295
|
+
lines.push(`- Distribution: ${decision.componentsPerSubWave.join(', ')}`);
|
|
296
|
+
lines.push('');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
lines.push('## No Splits Required');
|
|
301
|
+
lines.push('All waves are within limits.');
|
|
302
|
+
lines.push('');
|
|
303
|
+
}
|
|
304
|
+
return lines.join('\n');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=wave-splitter.js.map
|