@tamyla/clodo-framework 3.1.22 → 3.1.24

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.
Files changed (210) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +230 -1
  3. package/dist/cli/commands/assess.js +192 -0
  4. package/dist/cli/commands/create.js +77 -0
  5. package/dist/{bin → cli}/commands/deploy.js +36 -14
  6. package/dist/cli/commands/diagnose.js +83 -0
  7. package/dist/{bin → cli}/commands/helpers/deployment-verification.js +2 -3
  8. package/dist/{bin → cli}/commands/helpers/error-recovery.js +1 -1
  9. package/dist/{bin → cli}/commands/helpers/resource-detection.js +1 -1
  10. package/dist/cli/commands/helpers.js +110 -0
  11. package/dist/cli/commands/init-config.js +57 -0
  12. package/dist/cli/commands/update.js +102 -0
  13. package/dist/cli/commands/validate.js +65 -0
  14. package/dist/cli/security-cli.js +118 -0
  15. package/dist/config/FeatureManager.js +6 -0
  16. package/dist/config/clodo-create.example.json +26 -0
  17. package/dist/config/clodo-deploy.example.json +41 -0
  18. package/dist/config/clodo-update.example.json +46 -0
  19. package/dist/config/clodo-validate.example.json +41 -0
  20. package/dist/config/customers/template/development.env.template +37 -0
  21. package/dist/config/customers/template/production.env.template +39 -0
  22. package/dist/config/customers/template/staging.env.template +37 -0
  23. package/dist/config/customers.js +625 -0
  24. package/dist/config/domain-examples/README.md +464 -0
  25. package/dist/config/domain-examples/environment-mapped.json +168 -0
  26. package/dist/config/domain-examples/multi-domain.json +144 -0
  27. package/dist/config/domain-examples/single-domain.json +50 -0
  28. package/dist/config/domains.js +186 -0
  29. package/dist/config/examples +12 -0
  30. package/dist/config/features.js +61 -0
  31. package/dist/config/index.js +6 -0
  32. package/dist/config/staging-deployment.json +60 -0
  33. package/dist/config/validation-config.json +347 -0
  34. package/dist/database/database-orchestrator.js +795 -0
  35. package/dist/database/index.js +4 -0
  36. package/dist/deployment/index.js +11 -0
  37. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +426 -0
  38. package/dist/deployment/orchestration/PortfolioOrchestrator.js +273 -0
  39. package/dist/deployment/orchestration/SingleServiceOrchestrator.js +231 -0
  40. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +662 -0
  41. package/dist/deployment/orchestration/index.js +17 -0
  42. package/dist/deployment/rollback-manager.js +36 -0
  43. package/dist/deployment/wrangler-deployer.js +640 -0
  44. package/dist/handlers/GenericRouteHandler.js +532 -0
  45. package/dist/lib/database/deployment-db-manager.js +423 -0
  46. package/dist/lib/database/wrangler-d1-manager.js +685 -0
  47. package/dist/lib/deployment/modules/DeploymentConfiguration.js +395 -0
  48. package/dist/lib/deployment/modules/DeploymentOrchestrator.js +492 -0
  49. package/dist/lib/deployment/modules/EnvironmentManager.js +517 -0
  50. package/dist/lib/deployment/modules/MonitoringIntegration.js +560 -0
  51. package/dist/lib/deployment/modules/ValidationManager.js +342 -0
  52. package/dist/lib/deployment/orchestration/BaseDeploymentOrchestrator.js +426 -0
  53. package/dist/lib/deployment/orchestration/EnterpriseOrchestrator.js +21 -0
  54. package/dist/lib/deployment/orchestration/PortfolioOrchestrator.js +273 -0
  55. package/dist/lib/deployment/orchestration/SingleServiceOrchestrator.js +231 -0
  56. package/dist/lib/deployment/orchestration/UnifiedDeploymentOrchestrator.js +662 -0
  57. package/dist/{bin → lib}/shared/cloudflare/domain-discovery.js +1 -1
  58. package/dist/{bin → lib}/shared/cloudflare/domain-manager.js +1 -1
  59. package/dist/{bin → lib}/shared/cloudflare/ops.js +4 -4
  60. package/dist/{bin → lib}/shared/config/command-config-manager.js +1 -1
  61. package/dist/{bin → lib}/shared/config/index.js +1 -1
  62. package/dist/{bin → lib}/shared/config/manifest-loader.js +3 -3
  63. package/dist/{bin → lib}/shared/deployment/credential-collector.js +1 -1
  64. package/dist/lib/shared/deployment/index.js +10 -0
  65. package/dist/{bin → lib}/shared/deployment/rollback-manager.js +1 -1
  66. package/dist/{bin → lib}/shared/deployment/utilities/d1-error-recovery.js +1 -1
  67. package/dist/{bin → lib}/shared/deployment/validator.js +1 -1
  68. package/dist/{bin → lib}/shared/deployment/workflows/interactive-database-workflow.js +1 -1
  69. package/dist/{bin → lib}/shared/monitoring/health-checker.js +2 -2
  70. package/dist/{bin → lib}/shared/routing/domain-router.js +1 -1
  71. package/dist/{bin → lib}/shared/utils/config-loader.js +47 -11
  72. package/dist/lib/shared/utils/service-config-manager.js +227 -0
  73. package/dist/{bin → lib}/shared/validation/ValidationRegistry.js +1 -1
  74. package/dist/migration/MigrationAdapters.js +608 -0
  75. package/dist/modules/ModuleManager.js +668 -0
  76. package/dist/modules/security.js +96 -0
  77. package/dist/orchestration/cross-domain-coordinator.js +1083 -0
  78. package/dist/orchestration/index.js +5 -0
  79. package/dist/orchestration/modules/DeploymentCoordinator.js +368 -0
  80. package/dist/orchestration/modules/DomainResolver.js +198 -0
  81. package/dist/orchestration/modules/StateManager.js +332 -0
  82. package/dist/orchestration/multi-domain-orchestrator.js +765 -0
  83. package/dist/routing/EnhancedRouter.js +158 -0
  84. package/dist/schema/SchemaManager.js +778 -0
  85. package/dist/security/index.js +2 -2
  86. package/dist/service-management/ConfirmationEngine.js +412 -0
  87. package/dist/service-management/ErrorTracker.js +299 -0
  88. package/dist/service-management/GenerationEngine.js +447 -0
  89. package/dist/service-management/InputCollector.js +619 -0
  90. package/dist/service-management/ServiceCreator.js +21 -0
  91. package/dist/service-management/ServiceOrchestrator.js +672 -0
  92. package/dist/service-management/generators/BaseGenerator.js +233 -0
  93. package/dist/service-management/generators/GeneratorRegistry.js +254 -0
  94. package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +87 -0
  95. package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +106 -0
  96. package/dist/service-management/generators/code/ServiceHandlersGenerator.js +235 -0
  97. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +116 -0
  98. package/dist/service-management/generators/code/ServiceUtilsGenerator.js +246 -0
  99. package/dist/service-management/generators/code/WorkerIndexGenerator.js +143 -0
  100. package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +101 -0
  101. package/dist/service-management/generators/config/DomainsConfigGenerator.js +175 -0
  102. package/dist/service-management/generators/config/EnvExampleGenerator.js +178 -0
  103. package/dist/service-management/generators/config/ProductionEnvGenerator.js +97 -0
  104. package/dist/service-management/generators/config/StagingEnvGenerator.js +97 -0
  105. package/dist/service-management/generators/config/WranglerTomlGenerator.js +238 -0
  106. package/dist/service-management/generators/core/PackageJsonGenerator.js +243 -0
  107. package/dist/service-management/generators/core/SiteConfigGenerator.js +115 -0
  108. package/dist/service-management/generators/documentation/ApiDocsGenerator.js +331 -0
  109. package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +294 -0
  110. package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +244 -0
  111. package/dist/service-management/generators/documentation/ReadmeGenerator.js +196 -0
  112. package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +190 -0
  113. package/dist/service-management/generators/scripts/DeployScriptGenerator.js +123 -0
  114. package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +101 -0
  115. package/dist/service-management/generators/scripts/SetupScriptGenerator.js +88 -0
  116. package/dist/service-management/generators/service-types/StaticSiteGenerator.js +342 -0
  117. package/dist/service-management/generators/testing/EslintConfigGenerator.js +85 -0
  118. package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +237 -0
  119. package/dist/service-management/generators/testing/JestConfigGenerator.js +72 -0
  120. package/dist/service-management/generators/testing/UnitTestsGenerator.js +277 -0
  121. package/dist/service-management/generators/tooling/DockerComposeGenerator.js +71 -0
  122. package/dist/service-management/generators/tooling/GitignoreGenerator.js +143 -0
  123. package/dist/service-management/generators/utils/FileWriter.js +179 -0
  124. package/dist/service-management/generators/utils/PathResolver.js +157 -0
  125. package/dist/service-management/generators/utils/ServiceManifestGenerator.js +111 -0
  126. package/dist/service-management/generators/utils/TemplateEngine.js +185 -0
  127. package/dist/service-management/generators/utils/index.js +18 -0
  128. package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
  129. package/dist/service-management/handlers/GenerationHandler.js +80 -0
  130. package/dist/service-management/handlers/InputHandler.js +59 -0
  131. package/dist/service-management/handlers/ValidationHandler.js +216 -0
  132. package/dist/service-management/index.js +14 -0
  133. package/dist/service-management/routing/DomainRouteMapper.js +311 -0
  134. package/dist/service-management/routing/RouteGenerator.js +266 -0
  135. package/dist/service-management/routing/WranglerRoutesBuilder.js +273 -0
  136. package/dist/service-management/routing/index.js +14 -0
  137. package/dist/service-management/services/DirectoryStructureService.js +56 -0
  138. package/dist/service-management/services/GenerationCoordinator.js +208 -0
  139. package/dist/service-management/services/GeneratorRegistry.js +174 -0
  140. package/dist/services/GenericDataService.js +501 -0
  141. package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +286 -0
  142. package/dist/ui-structures/concepts/service-lifecycle-management.md +150 -0
  143. package/dist/ui-structures/concepts/service-manifest-guide.md +309 -0
  144. package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +231 -0
  145. package/dist/ui-structures/creation/automated-generation-ui.json +246 -0
  146. package/dist/ui-structures/creation/core-inputs-ui.json +217 -0
  147. package/dist/ui-structures/creation/smart-confirmable-ui.json +451 -0
  148. package/dist/ui-structures/reference/absolutely-required-inputs.json +315 -0
  149. package/dist/ui-structures/reference/service-manifest-template.json +342 -0
  150. package/dist/utils/cloudflare/ops.js +1 -1
  151. package/dist/utils/file-manager.js +1 -1
  152. package/dist/utils/formatters.js +1 -1
  153. package/dist/utils/logger.js +1 -1
  154. package/dist/version/VersionDetector.js +813 -0
  155. package/dist/worker/index.js +4 -0
  156. package/dist/worker/integration.js +351 -0
  157. package/package.json +7 -15
  158. package/dist/bin/commands/validate.js +0 -51
  159. package/dist/bin/security/security-cli.js +0 -108
  160. package/dist/bin/shared/deployment/index.js +0 -10
  161. /package/dist/{bin → cli}/clodo-service.js +0 -0
  162. /package/dist/{bin → cli}/commands/helpers/deployment-ui.js +0 -0
  163. /package/dist/{bin → lib}/shared/cache/configuration-cache.js +0 -0
  164. /package/dist/{bin → lib}/shared/cloudflare/index.js +0 -0
  165. /package/dist/{bin → lib}/shared/config/ConfigurationManager.js +0 -0
  166. /package/dist/{bin → lib}/shared/config/cache.js +0 -0
  167. /package/dist/{bin → lib}/shared/config/cloudflare-service-validator.js +0 -0
  168. /package/dist/{bin → lib}/shared/config/manager.js +0 -0
  169. /package/dist/{bin → lib}/shared/database/connection-manager.js +0 -0
  170. /package/dist/{bin → lib}/shared/database/index.js +0 -0
  171. /package/dist/{bin → lib}/shared/database/orchestrator.js +0 -0
  172. /package/dist/{bin → lib}/shared/deployment/auditor.js +0 -0
  173. /package/dist/{bin → lib}/shared/deployment/workflows/deployment-summary.js +0 -0
  174. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-confirmation.js +0 -0
  175. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-domain-info-gatherer.js +0 -0
  176. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-secret-workflow.js +0 -0
  177. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-testing-workflow.js +0 -0
  178. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-validation.js +0 -0
  179. /package/dist/{bin → lib}/shared/error-handling/error-classifier.js +0 -0
  180. /package/dist/{bin → lib}/shared/index.js +0 -0
  181. /package/dist/{bin → lib}/shared/logging/Logger.js +0 -0
  182. /package/dist/{bin → lib}/shared/monitoring/index.js +0 -0
  183. /package/dist/{bin → lib}/shared/monitoring/memory-manager.js +0 -0
  184. /package/dist/{bin → lib}/shared/monitoring/production-monitor.js +0 -0
  185. /package/dist/{bin → lib}/shared/production-tester/api-tester.js +0 -0
  186. /package/dist/{bin → lib}/shared/production-tester/auth-tester.js +0 -0
  187. /package/dist/{bin → lib}/shared/production-tester/core.js +0 -0
  188. /package/dist/{bin → lib}/shared/production-tester/database-tester.js +0 -0
  189. /package/dist/{bin → lib}/shared/production-tester/index.js +0 -0
  190. /package/dist/{bin → lib}/shared/production-tester/load-tester.js +0 -0
  191. /package/dist/{bin → lib}/shared/production-tester/performance-tester.js +0 -0
  192. /package/dist/{bin → lib}/shared/security/api-token-manager.js +0 -0
  193. /package/dist/{bin → lib}/shared/security/index.js +0 -0
  194. /package/dist/{bin → lib}/shared/security/secret-generator.js +0 -0
  195. /package/dist/{bin → lib}/shared/security/secure-token-manager.js +0 -0
  196. /package/dist/{bin → lib}/shared/utils/ErrorHandler.js +0 -0
  197. /package/dist/{bin → lib}/shared/utils/cli-options.js +0 -0
  198. /package/dist/{bin → lib}/shared/utils/deployment-validator.js +0 -0
  199. /package/dist/{bin → lib}/shared/utils/error-recovery.js +0 -0
  200. /package/dist/{bin → lib}/shared/utils/file-manager.js +0 -0
  201. /package/dist/{bin → lib}/shared/utils/formatters.js +0 -0
  202. /package/dist/{bin → lib}/shared/utils/graceful-shutdown-manager.js +0 -0
  203. /package/dist/{bin → lib}/shared/utils/index.js +0 -0
  204. /package/dist/{bin → lib}/shared/utils/interactive-prompts.js +0 -0
  205. /package/dist/{bin → lib}/shared/utils/interactive-utils.js +0 -0
  206. /package/dist/{bin → lib}/shared/utils/output-formatter.js +0 -0
  207. /package/dist/{bin → lib}/shared/utils/progress-manager.js +0 -0
  208. /package/dist/{bin → lib}/shared/utils/progress-spinner.js +0 -0
  209. /package/dist/{bin → lib}/shared/utils/rate-limiter.js +0 -0
  210. /package/dist/{bin → lib}/shared/utils/sensitive-redactor.js +0 -0
@@ -0,0 +1,765 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Multi-Domain Orchestrator Module
5
+ * Enterprise-grade deployment orchestration with state management,
6
+ * rollback capabilities, and portfolio-wide coordination
7
+ *
8
+ * Now uses modular architecture for improved maintainability and testability
9
+ */
10
+ import { DomainResolver } from './modules/DomainResolver.js';
11
+ import { DeploymentCoordinator } from './modules/DeploymentCoordinator.js';
12
+ import { StateManager } from './modules/StateManager.js';
13
+ import { DatabaseOrchestrator } from '../database/database-orchestrator.js';
14
+ import { EnhancedSecretManager } from '../utils/deployment/secret-generator.js';
15
+ import { WranglerConfigManager } from '../utils/deployment/wrangler-config-manager.js';
16
+ import { ConfigurationValidator } from '../security/ConfigurationValidator.js';
17
+ import { buildCustomDomain } from '../utils/constants.js';
18
+ import { exec } from 'child_process';
19
+ import { promisify } from 'util';
20
+ import { join } from 'path';
21
+ import { createDatabase, databaseExists, getDatabaseId } from '../utils/cloudflare/index.js';
22
+ const execAsync = promisify(exec);
23
+
24
+ /**
25
+ * Multi-Domain Deployment Orchestrator
26
+ * Manages enterprise-level deployments across multiple domains with comprehensive state tracking
27
+ *
28
+ * REFACTORED: Now uses modular components for domain resolution, deployment coordination, and state management
29
+ */
30
+ export class MultiDomainOrchestrator {
31
+ constructor(options = {}) {
32
+ this.domains = options.domains || [];
33
+ this.environment = options.environment || 'production';
34
+ this.dryRun = options.dryRun || false;
35
+ this.skipTests = options.skipTests || false;
36
+ this.parallelDeployments = options.parallelDeployments || 3;
37
+ this.servicePath = options.servicePath || process.cwd();
38
+ this.serviceName = options.serviceName || 'worker'; // Service name for custom domain
39
+
40
+ // Wrangler config path - allows using customer-specific wrangler.toml files
41
+ // If not specified, wrangler uses the default wrangler.toml in servicePath
42
+ this.wranglerConfigPath = options.wranglerConfigPath;
43
+
44
+ // Cloudflare credentials for API-based operations
45
+ // Support both legacy individual params and new cloudflareSettings object
46
+ const cfSettings = options.cloudflareSettings || {};
47
+ this.cloudflareToken = options.cloudflareToken || cfSettings.token;
48
+ this.cloudflareAccountId = options.cloudflareAccountId || cfSettings.accountId;
49
+ this.cloudflareZoneId = options.cloudflareZoneId || cfSettings.zoneId;
50
+ this.cloudflareZoneName = options.cloudflareZoneName || cfSettings.zoneName;
51
+
52
+ // Configure wrangler to use API token when available
53
+ // Configure wrangler authentication via environment variables
54
+ // Note: account_id comes from wrangler.toml, not env var
55
+ // (env var would be ignored by wrangler when wrangler.toml has account_id)
56
+ if (this.cloudflareToken) {
57
+ process.env.CLOUDFLARE_API_TOKEN = this.cloudflareToken;
58
+ console.log(`🔑 Configured wrangler to use API token authentication`);
59
+ }
60
+ if (this.cloudflareAccountId) {
61
+ process.env.CLOUDFLARE_ACCOUNT_ID = this.cloudflareAccountId;
62
+ console.log(`🔑 Configured wrangler to use account ID: ${this.cloudflareAccountId}`);
63
+ }
64
+ if (this.cloudflareZoneId) {
65
+ process.env.CLOUDFLARE_ZONE_ID = this.cloudflareZoneId;
66
+ console.log(`🔑 Configured wrangler to use zone ID: ${this.cloudflareZoneId}`);
67
+ }
68
+ if (this.cloudflareZoneName) {
69
+ console.log(`🌐 Deploying to zone: ${this.cloudflareZoneName}`);
70
+ }
71
+
72
+ // Initialize modular components
73
+ this.domainResolver = new DomainResolver({
74
+ environment: this.environment,
75
+ validationLevel: options.validationLevel || 'basic',
76
+ cacheEnabled: options.cacheEnabled !== false
77
+ });
78
+ this.deploymentCoordinator = new DeploymentCoordinator({
79
+ parallelDeployments: this.parallelDeployments,
80
+ skipTests: this.skipTests,
81
+ dryRun: this.dryRun,
82
+ environment: this.environment,
83
+ batchPauseMs: options.batchPauseMs || 2000
84
+ });
85
+ this.stateManager = new StateManager({
86
+ environment: this.environment,
87
+ dryRun: this.dryRun,
88
+ domains: this.domains,
89
+ enablePersistence: options.enablePersistence !== false,
90
+ rollbackEnabled: options.rollbackEnabled !== false
91
+ });
92
+
93
+ // Initialize enterprise-grade utilities
94
+ this.databaseOrchestrator = new DatabaseOrchestrator({
95
+ projectRoot: this.servicePath,
96
+ dryRun: this.dryRun,
97
+ cloudflareToken: this.cloudflareToken,
98
+ cloudflareAccountId: this.cloudflareAccountId,
99
+ cloudflareZoneId: this.cloudflareZoneId
100
+ });
101
+ this.secretManager = new EnhancedSecretManager({
102
+ projectRoot: this.servicePath,
103
+ dryRun: this.dryRun
104
+ });
105
+ this.wranglerConfigManager = new WranglerConfigManager({
106
+ projectRoot: this.servicePath,
107
+ dryRun: this.dryRun,
108
+ verbose: options.verbose || false,
109
+ accountId: this.cloudflareAccountId
110
+ });
111
+
112
+ // ConfigurationValidator is a static class - don't instantiate
113
+ // Access via ConfigurationValidator.validate() directly
114
+
115
+ // Legacy compatibility: expose portfolioState for backward compatibility
116
+ this.portfolioState = this.stateManager.portfolioState;
117
+
118
+ // Note: Async initialization required - call initialize() after construction
119
+ }
120
+
121
+ /**
122
+ * Initialize the orchestrator asynchronously
123
+ * Uses modular components for domain resolution and state initialization
124
+ */
125
+ async initialize() {
126
+ console.log('🌐 Multi-Domain Orchestrator v2.0 (Modular)');
127
+ console.log('===========================================');
128
+ console.log(`📊 Portfolio: ${this.domains.length} domains`);
129
+ console.log(`🌍 Environment: ${this.environment}`);
130
+ console.log(`🆔 Orchestration ID: ${this.portfolioState.orchestrationId}`);
131
+ console.log(`🔍 Mode: ${this.dryRun ? 'DRY RUN' : 'LIVE DEPLOYMENT'}`);
132
+ console.log(`⚡ Parallel Deployments: ${this.parallelDeployments}`);
133
+ console.log('🧩 Modular Components: DomainResolver, DeploymentCoordinator, StateManager');
134
+ console.log('');
135
+
136
+ // Initialize all modular components
137
+ await this.stateManager.initializeDomainStates(this.domains);
138
+
139
+ // Pre-resolve all domain configurations if domains are specified
140
+ if (this.domains.length > 0) {
141
+ const configs = await this.domainResolver.resolveMultipleDomains(this.domains);
142
+
143
+ // Update domain states with resolved configurations
144
+ for (const [domain, config] of Object.entries(configs)) {
145
+ const domainState = this.portfolioState.domainStates.get(domain);
146
+ if (domainState) {
147
+ domainState.config = config;
148
+ this.stateManager.updateDomainState(domain, {
149
+ config
150
+ });
151
+ }
152
+ }
153
+ }
154
+ await this.stateManager.logAuditEvent('orchestrator_initialized', {
155
+ domains: this.domains,
156
+ environment: this.environment,
157
+ modularComponents: ['DomainResolver', 'DeploymentCoordinator', 'StateManager']
158
+ });
159
+ }
160
+
161
+ /**
162
+ * Legacy method for backward compatibility
163
+ * @deprecated Use stateManager.generateOrchestrationId() instead
164
+ */
165
+ generateOrchestrationId() {
166
+ return this.stateManager.generateOrchestrationId();
167
+ }
168
+
169
+ /**
170
+ * Legacy method for backward compatibility
171
+ * @deprecated Use stateManager.generateDeploymentId() instead
172
+ */
173
+ generateDeploymentId(domain) {
174
+ return this.stateManager.generateDeploymentId(domain);
175
+ }
176
+
177
+ /**
178
+ * Legacy method for backward compatibility
179
+ * @deprecated Use domainResolver.generateDomainConfig() instead
180
+ */
181
+ generateDomainConfig(domain) {
182
+ return this.domainResolver.generateDomainConfig(domain);
183
+ }
184
+
185
+ /**
186
+ * Deploy to single domain using modular deployment coordinator
187
+ * @param {string} domain - Domain to deploy
188
+ * @param {Object} deploymentOptions - Deployment configuration options
189
+ * @returns {Promise<Object>} Deployment result
190
+ */
191
+ async deploySingleDomain(domain, deploymentOptions = {}) {
192
+ const domainState = this.portfolioState.domainStates.get(domain);
193
+ if (!domainState) {
194
+ throw new Error(`Domain ${domain} not found in portfolio`);
195
+ }
196
+
197
+ // Store deployment options in domain state for handlers to access
198
+ domainState.deploymentOptions = deploymentOptions;
199
+
200
+ // Create handlers that delegate to our legacy methods for backward compatibility
201
+ const handlers = {
202
+ validation: d => this.validateDomainPrerequisites(d),
203
+ initialization: d => this.initializeDomainDeployment(d),
204
+ database: d => this.setupDomainDatabase(d),
205
+ secrets: d => this.handleDomainSecrets(d),
206
+ deployment: d => this.deployDomainWorker(d),
207
+ 'post-validation': d => this.validateDomainDeployment(d)
208
+ };
209
+ return await this.deploymentCoordinator.deploySingleDomain(domain, domainState, handlers);
210
+ }
211
+
212
+ /**
213
+ * Deploy to multiple domains using modular deployment coordinator
214
+ * @returns {Promise<Object>} Portfolio deployment results
215
+ */
216
+ async deployPortfolio() {
217
+ // Create handlers that delegate to our legacy methods for backward compatibility
218
+ const handlers = {
219
+ validation: d => this.validateDomainPrerequisites(d),
220
+ initialization: d => this.initializeDomainDeployment(d),
221
+ database: d => this.setupDomainDatabase(d),
222
+ secrets: d => this.handleDomainSecrets(d),
223
+ deployment: d => this.deployDomainWorker(d),
224
+ 'post-validation': d => this.validateDomainDeployment(d)
225
+ };
226
+ return await this.deploymentCoordinator.deployPortfolio(this.domains, this.portfolioState.domainStates, handlers);
227
+ }
228
+
229
+ /**
230
+ * Legacy method for backward compatibility
231
+ * @deprecated Use deploymentCoordinator.createDeploymentBatches() instead
232
+ */
233
+ createDeploymentBatches() {
234
+ return this.deploymentCoordinator.createDeploymentBatches(this.domains);
235
+ }
236
+
237
+ /**
238
+ * Legacy method for backward compatibility
239
+ * @deprecated Use stateManager.logAuditEvent() instead
240
+ */
241
+ logAuditEvent(event, domain, details = {}) {
242
+ return this.stateManager.logAuditEvent(event, domain, details);
243
+ }
244
+
245
+ /**
246
+ * Legacy method for backward compatibility
247
+ * @deprecated Use stateManager.saveAuditLog() instead
248
+ */
249
+ async saveAuditLog() {
250
+ return await this.stateManager.saveAuditLog();
251
+ }
252
+
253
+ /**
254
+ * Legacy method for backward compatibility
255
+ * @deprecated Use domainResolver.validateDomainPrerequisites() instead
256
+ */
257
+ async validateDomainPrerequisites(domain) {
258
+ return await this.domainResolver.validateDomainPrerequisites(domain);
259
+ }
260
+
261
+ /**
262
+ * Initialize domain deployment with security validation
263
+ */
264
+ async initializeDomainDeployment(domain) {
265
+ console.log(` 🔧 Initializing deployment for ${domain}`);
266
+
267
+ // Validate domain configuration using ConfigurationValidator
268
+ try {
269
+ const domainState = this.portfolioState.domainStates.get(domain);
270
+ const config = domainState?.config || {};
271
+
272
+ // Perform security validation using static method
273
+ const validationIssues = ConfigurationValidator.validate(config, this.environment);
274
+ if (validationIssues.length > 0) {
275
+ console.log(` ⚠️ Found ${validationIssues.length} configuration warnings:`);
276
+ validationIssues.forEach(issue => {
277
+ console.log(` • ${issue}`);
278
+ });
279
+
280
+ // Don't block deployment for warnings, just log them
281
+ this.stateManager.logAuditEvent('VALIDATION_WARNINGS', domain, {
282
+ issues: validationIssues,
283
+ environment: this.environment
284
+ });
285
+ } else {
286
+ console.log(` ✅ Configuration validated successfully`);
287
+ }
288
+ return true;
289
+ } catch (error) {
290
+ console.error(` ❌ Initialization failed: ${error.message}`);
291
+ throw error;
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Setup domain database using DatabaseOrchestrator
297
+ */
298
+ async setupDomainDatabase(domain) {
299
+ console.log(` 🗄️ Setting up database for ${domain}`);
300
+ if (this.dryRun) {
301
+ console.log(` � DRY RUN: Would create database for ${domain}`);
302
+ const databaseName = `${domain.replace(/\./g, '-')}-${this.environment}-db`;
303
+ return {
304
+ databaseName,
305
+ databaseId: 'dry-run-id',
306
+ created: false
307
+ };
308
+ }
309
+ try {
310
+ // Create D1 database using Cloudflare ops
311
+ const databaseName = `${domain.replace(/\./g, '-')}-${this.environment}-db`;
312
+
313
+ // Check if database already exists
314
+ console.log(` � Checking if database exists: ${databaseName}`);
315
+ let exists,
316
+ databaseId,
317
+ created = false;
318
+
319
+ // Use API-based operations if credentials are available
320
+ if (this.cloudflareToken && this.cloudflareAccountId) {
321
+ console.log(` 🔑 Using API token authentication for account: ${this.cloudflareAccountId}`);
322
+ try {
323
+ exists = await databaseExists(databaseName, {
324
+ apiToken: this.cloudflareToken,
325
+ accountId: this.cloudflareAccountId
326
+ });
327
+ if (exists) {
328
+ console.log(` ✅ Database already exists: ${databaseName}`);
329
+ databaseId = await getDatabaseId(databaseName, {
330
+ apiToken: this.cloudflareToken,
331
+ accountId: this.cloudflareAccountId
332
+ });
333
+ console.log(` 📊 Existing Database ID: ${databaseId}`);
334
+ } else {
335
+ console.log(` 📦 Creating database: ${databaseName}`);
336
+ databaseId = await createDatabase(databaseName, {
337
+ apiToken: this.cloudflareToken,
338
+ accountId: this.cloudflareAccountId
339
+ });
340
+ console.log(` ✅ Database created: ${databaseName}`);
341
+ console.log(` 📊 Database ID: ${databaseId}`);
342
+ created = true;
343
+ }
344
+ } catch (apiError) {
345
+ // Check if this is an authentication or permission error
346
+ if (apiError.message.includes('permission denied') || apiError.message.includes('403') || apiError.message.includes('authentication failed') || apiError.message.includes('401')) {
347
+ if (apiError.message.includes('401')) {
348
+ console.log(` ❌ API token authentication failed (invalid/expired token)`);
349
+ console.log(` 🔗 Check/create token at: https://dash.cloudflare.com/profile/api-tokens`);
350
+ } else {
351
+ console.log(` ⚠️ API token lacks D1 database permissions`);
352
+ console.log(` 💡 Required permission: 'Cloudflare D1:Edit'`);
353
+ console.log(` 🔗 Update token at: https://dash.cloudflare.com/profile/api-tokens`);
354
+ }
355
+ console.log(` 🔄 Falling back to OAuth authentication...`);
356
+ console.log(` ⚠️ WARNING: OAuth uses your personal account, not the API token account!`);
357
+
358
+ // Fall back to OAuth-based operations with warning
359
+ console.log(` 🔐 Using OAuth authentication (wrangler CLI)`);
360
+ exists = await databaseExists(databaseName);
361
+ if (exists) {
362
+ console.log(` ✅ Database already exists: ${databaseName}`);
363
+ databaseId = await getDatabaseId(databaseName);
364
+ console.log(` 📊 Existing Database ID: ${databaseId}`);
365
+ } else {
366
+ console.log(` 📦 Creating database: ${databaseName}`);
367
+ databaseId = await createDatabase(databaseName);
368
+ console.log(` ✅ Database created: ${databaseName}`);
369
+ console.log(` 📊 Database ID: ${databaseId}`);
370
+ created = true;
371
+ }
372
+ } else {
373
+ // Re-throw non-auth/permission errors
374
+ throw apiError;
375
+ }
376
+ }
377
+ } else {
378
+ // Fallback to CLI-based operations (OAuth)
379
+ console.log(` 🔐 Using OAuth authentication (wrangler CLI)`);
380
+ exists = await databaseExists(databaseName);
381
+ if (exists) {
382
+ console.log(` ✅ Database already exists: ${databaseName}`);
383
+ databaseId = await getDatabaseId(databaseName);
384
+ console.log(` 📊 Existing Database ID: ${databaseId}`);
385
+ } else {
386
+ console.log(` 📦 Creating database: ${databaseName}`);
387
+ databaseId = await createDatabase(databaseName);
388
+ console.log(` ✅ Database created: ${databaseName}`);
389
+ console.log(` 📊 Database ID: ${databaseId}`);
390
+ created = true;
391
+ }
392
+ }
393
+
394
+ // Store database info in domain state
395
+ const domainState = this.portfolioState.domainStates.get(domain);
396
+ if (domainState) {
397
+ domainState.databaseName = databaseName;
398
+ domainState.databaseId = databaseId;
399
+ }
400
+
401
+ // CRITICAL: Update wrangler.toml BEFORE attempting migrations
402
+ console.log(` 📝 Configuring wrangler.toml for database...`);
403
+ console.log(` 📁 Service path: ${this.servicePath}`);
404
+ console.log(` 📁 Current working directory: ${process.cwd()}`);
405
+ try {
406
+ // Set account_id if API credentials are available
407
+ if (this.cloudflareAccountId) {
408
+ await this.wranglerConfigManager.setAccountId(this.cloudflareAccountId);
409
+ }
410
+
411
+ // Ensure environment section exists
412
+ await this.wranglerConfigManager.ensureEnvironment(this.environment);
413
+
414
+ // Add database binding (use snake_case for wrangler.toml compatibility)
415
+ await this.wranglerConfigManager.addDatabaseBinding(this.environment, {
416
+ binding: 'DB',
417
+ database_name: databaseName,
418
+ database_id: databaseId
419
+ });
420
+ console.log(` ✅ wrangler.toml updated with database configuration`);
421
+ console.log(` 📄 wrangler.toml location: ${this.wranglerConfigManager.configPath}`);
422
+ } catch (configError) {
423
+ console.warn(` ⚠️ Failed to update wrangler.toml: ${configError.message}`);
424
+ console.warn(` 💡 You may need to manually add database configuration`);
425
+ }
426
+
427
+ // Apply migrations using DatabaseOrchestrator's enterprise capabilities
428
+ console.log(` 🔄 Applying database migrations...`);
429
+ try {
430
+ // Use the real applyDatabaseMigrations method
431
+ // Note: bindingName defaults to 'DB' if not provided
432
+ // Since databases are created remotely via Cloudflare API, always use remote flag
433
+ await this.databaseOrchestrator.applyDatabaseMigrations(databaseName, 'DB',
434
+ // bindingName - wrangler.toml binding name
435
+ this.environment, true // Always remote since databases are created in Cloudflare
436
+ );
437
+ console.log(` ✅ Migrations applied successfully`);
438
+ } catch (migrationError) {
439
+ console.warn(` ⚠️ Migration warning: ${migrationError.message}`);
440
+ console.warn(` 💡 Migrations can be applied manually later`);
441
+ }
442
+
443
+ // Log comprehensive audit event
444
+ this.stateManager.logAuditEvent(created ? 'DATABASE_CREATED' : 'DATABASE_FOUND', domain, {
445
+ databaseName,
446
+ databaseId,
447
+ environment: this.environment,
448
+ migrationsApplied: true,
449
+ isRemote: true,
450
+ // Always remote since databases are created in Cloudflare
451
+ created
452
+ });
453
+ return {
454
+ databaseName,
455
+ databaseId,
456
+ created,
457
+ migrationsApplied: true
458
+ };
459
+ } catch (error) {
460
+ console.error(` ❌ Database creation failed: ${error.message}`);
461
+ throw error;
462
+ }
463
+ }
464
+
465
+ /**
466
+ * Handle domain secrets using EnhancedSecretManager
467
+ */
468
+ async handleDomainSecrets(domain) {
469
+ console.log(` 🔐 Handling secrets for ${domain}`);
470
+ if (this.dryRun) {
471
+ console.log(` � DRY RUN: Would upload secrets for ${domain}`);
472
+ return {
473
+ secrets: [],
474
+ uploaded: 0
475
+ };
476
+ }
477
+ try {
478
+ // Generate secrets for this domain using EnhancedSecretManager
479
+ // Use the actual method: generateDomainSpecificSecrets
480
+ const secretResult = await this.secretManager.generateDomainSpecificSecrets(domain, this.environment, {
481
+ customConfigs: {},
482
+ reuseExisting: true,
483
+ rotateAll: false,
484
+ formats: ['env', 'wrangler'] // Generate both .env and wrangler CLI formats
485
+ });
486
+ const secrets = secretResult.secrets || {};
487
+ const secretNames = Object.keys(secrets);
488
+ if (secretNames.length > 0) {
489
+ console.log(` ✅ Generated ${secretNames.length} secrets: ${secretNames.join(', ')}`);
490
+ console.log(` 🔒 Secret values are encrypted and not displayed`);
491
+ console.log(` 📄 Distribution files: ${secretResult.distributionFiles?.join(', ') || 'N/A'}`);
492
+
493
+ // Log audit event with full metadata
494
+ this.stateManager.logAuditEvent('SECRETS_GENERATED', domain, {
495
+ count: secretNames.length,
496
+ names: secretNames,
497
+ environment: this.environment,
498
+ formats: secretResult.formats || [],
499
+ distributionPath: secretResult.distributionPath
500
+ });
501
+ } else {
502
+ console.log(` ℹ️ No secrets to upload for ${domain}`);
503
+ }
504
+ return {
505
+ secrets: secretNames,
506
+ uploaded: secretNames.length,
507
+ distributionPath: secretResult.distributionPath,
508
+ formats: secretResult.formats
509
+ };
510
+ } catch (error) {
511
+ console.error(` ⚠️ Secret generation failed: ${error.message}`);
512
+ // Don't fail deployment if secrets fail - they can be added manually
513
+ return {
514
+ secrets: [],
515
+ uploaded: 0,
516
+ error: error.message
517
+ };
518
+ }
519
+ }
520
+
521
+ /**
522
+ * Deploy domain worker (executes actual wrangler deploy)
523
+ */
524
+ async deployDomainWorker(domain) {
525
+ console.log(` 🚀 Deploying worker for ${domain}`);
526
+ if (this.dryRun) {
527
+ console.log(` 🔍 DRY RUN: Would deploy worker for ${domain}`);
528
+ // Use centralized domain template from validation-config.json
529
+ const customUrl = buildCustomDomain(this.serviceName, domain, this.environment);
530
+ return {
531
+ url: customUrl,
532
+ deployed: false,
533
+ dryRun: true
534
+ };
535
+ }
536
+ try {
537
+ // Generate/update customer-specific wrangler.toml, then copy to root
538
+ // This implements the architecture where:
539
+ // 1. Customer configs are persistent (deployment history/state)
540
+ // 2. Root wrangler.toml is ephemeral (reflects current active deployment)
541
+ if (this.cloudflareZoneName) {
542
+ console.log(` 🔧 Preparing customer config for zone: ${this.cloudflareZoneName}`);
543
+
544
+ // Generate or update customer config with current deployment parameters
545
+ const customerConfigPath = await this.wranglerConfigManager.generateCustomerConfig(this.cloudflareZoneName, {
546
+ accountId: this.cloudflareAccountId,
547
+ environment: this.environment
548
+ });
549
+
550
+ // Copy customer config to root (ephemeral working copy for this deployment)
551
+ console.log(` 📋 Copying customer config to root wrangler.toml`);
552
+ await this.wranglerConfigManager.copyCustomerConfig(customerConfigPath);
553
+ } else {
554
+ // Fallback: Update root wrangler.toml directly if no zone name available
555
+ console.log(` ⚠️ No zone name available, updating root wrangler.toml directly`);
556
+ if (this.cloudflareAccountId) {
557
+ await this.wranglerConfigManager.setAccountId(this.cloudflareAccountId);
558
+ }
559
+ }
560
+
561
+ // Ensure environment section exists in wrangler.toml
562
+ console.log(` 📝 Verifying wrangler.toml configuration...`);
563
+ try {
564
+ await this.wranglerConfigManager.ensureEnvironment(this.environment);
565
+ } catch (configError) {
566
+ console.warn(` ⚠️ Could not verify wrangler.toml: ${configError.message}`);
567
+ // Continue anyway - wrangler will provide clearer error if config is wrong
568
+ }
569
+
570
+ // Build deploy command
571
+ // Note: We already copied customer config to root wrangler.toml above
572
+ // So wrangler will use root wrangler.toml which has the correct account_id
573
+ let deployCommand = `npx wrangler deploy`;
574
+
575
+ // ALWAYS add environment flag to use environment-specific config
576
+ // Even for production, we use [env.production] section which has the correct worker name and DB
577
+ deployCommand += ` --env ${this.environment}`;
578
+ console.log(` 🔧 Executing: ${deployCommand}`);
579
+ console.log(` 📁 Working directory: ${this.servicePath}`);
580
+ console.log(` 📄 Using wrangler.toml from service root (already updated with customer config)`);
581
+ console.log(` 🌍 Environment: ${this.environment}`);
582
+
583
+ // Execute deployment with timeout
584
+ const {
585
+ stdout,
586
+ stderr
587
+ } = await execAsync(deployCommand, {
588
+ cwd: this.servicePath,
589
+ timeout: 120000,
590
+ // 2 minute timeout
591
+ maxBuffer: 1024 * 1024 * 10 // 10MB buffer for large outputs
592
+ });
593
+
594
+ // Log output for debugging
595
+ if (stdout) {
596
+ console.log(` 📄 Deployment output:`);
597
+ stdout.split('\n').filter(line => line.trim()).forEach(line => {
598
+ console.log(` ${line}`);
599
+ });
600
+ }
601
+ if (stderr && !stderr.includes('deprecated')) {
602
+ console.warn(` ⚠️ Deployment warnings: ${stderr}`);
603
+ }
604
+
605
+ // Parse worker URL from wrangler output
606
+ // Wrangler outputs: "Published service-name (version) to https://worker-url"
607
+ const urlMatch = stdout.match(/https:\/\/[^\s]+/);
608
+ const workerUrl = urlMatch ? urlMatch[0] : null;
609
+
610
+ // Construct custom domain URL using centralized template from validation-config.json
611
+ // Handles all environment patterns: production, staging, development
612
+ const customUrl = buildCustomDomain(this.serviceName, domain, this.environment);
613
+
614
+ // Store URLs in domain state
615
+ const domainState = this.portfolioState.domainStates.get(domain);
616
+ if (domainState) {
617
+ domainState.workerUrl = workerUrl;
618
+ domainState.deploymentUrl = customUrl;
619
+ }
620
+ if (workerUrl) {
621
+ console.log(` ✅ Worker deployed successfully`);
622
+ console.log(` 🔗 Worker URL: ${workerUrl}`);
623
+ console.log(` 🔗 Custom URL: ${customUrl}`);
624
+ } else {
625
+ console.log(` ✅ Deployment completed (URL not detected in output)`);
626
+ console.log(` 🔗 Expected URL: ${customUrl}`);
627
+ }
628
+ return {
629
+ url: customUrl,
630
+ workerUrl: workerUrl,
631
+ deployed: true,
632
+ stdout,
633
+ stderr
634
+ };
635
+ } catch (error) {
636
+ console.error(` ❌ Worker deployment failed: ${error.message}`);
637
+
638
+ // Parse error for helpful diagnostics
639
+ if (error.message.includes('wrangler.toml')) {
640
+ console.error(` 💡 Ensure wrangler.toml exists in ${this.servicePath}`);
641
+ }
642
+ if (error.message.includes('No environment found')) {
643
+ console.error(` 💡 Add [env.${this.environment}] section to wrangler.toml`);
644
+ }
645
+ if (error.stderr) {
646
+ console.error(` 📄 Error details: ${error.stderr}`);
647
+ }
648
+ throw new Error(`Worker deployment failed for ${domain}: ${error.message}`);
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Validate domain deployment with real HTTP health check (with retries)
654
+ */
655
+ async validateDomainDeployment(domain) {
656
+ console.log(` ✅ Validating deployment for ${domain}`);
657
+ if (this.dryRun || this.skipTests) {
658
+ console.log(` ⏭️ Skipping health check (${this.dryRun ? 'dry run' : 'tests disabled'})`);
659
+ return true;
660
+ }
661
+
662
+ // Get the deployment URL from domain state
663
+ const domainState = this.portfolioState.domainStates.get(domain);
664
+ const deploymentUrl = domainState?.deploymentUrl;
665
+ if (!deploymentUrl) {
666
+ console.log(` ⚠️ No deployment URL found, skipping health check`);
667
+ return true;
668
+ }
669
+ console.log(` 🔍 Running health check: ${deploymentUrl}/health`);
670
+
671
+ // Retry logic for health checks
672
+ const maxRetries = 3;
673
+ const retryDelay = 5000; // 5 seconds between retries
674
+
675
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
676
+ try {
677
+ const startTime = Date.now();
678
+ console.log(` Attempt ${attempt}/${maxRetries}...`);
679
+
680
+ // Perform actual HTTP health check
681
+ const response = await fetch(`${deploymentUrl}/health`, {
682
+ method: 'GET',
683
+ headers: {
684
+ 'User-Agent': 'Clodo-Orchestrator/2.0'
685
+ },
686
+ signal: AbortSignal.timeout(15000) // 15 second timeout
687
+ });
688
+ const responseTime = Date.now() - startTime;
689
+ const status = response.status;
690
+ if (status === 200) {
691
+ console.log(` ✅ Health check passed (${status}) - Response time: ${responseTime}ms`);
692
+
693
+ // Log successful health check
694
+ this.stateManager.logAuditEvent('HEALTH_CHECK_PASSED', domain, {
695
+ url: deploymentUrl,
696
+ status,
697
+ responseTime,
698
+ attempt,
699
+ environment: this.environment
700
+ });
701
+ return true;
702
+ } else {
703
+ const errorMsg = `Health check returned ${status} - deployment may have issues`;
704
+ console.log(` ⚠️ ${errorMsg}`);
705
+ this.stateManager.logAuditEvent('HEALTH_CHECK_WARNING', domain, {
706
+ url: deploymentUrl,
707
+ status,
708
+ responseTime,
709
+ attempt,
710
+ environment: this.environment
711
+ });
712
+
713
+ // Don't fail deployment for non-200 status, just warn
714
+ return true;
715
+ }
716
+ } catch (error) {
717
+ const isLastAttempt = attempt === maxRetries;
718
+ const errorMsg = `Health check failed: ${error.message}`;
719
+ if (isLastAttempt) {
720
+ console.log(` ❌ ${errorMsg} (final attempt)`);
721
+ console.log(` 💡 The service may still be deploying. Check manually: curl ${deploymentUrl}/health`);
722
+ this.stateManager.logAuditEvent('HEALTH_CHECK_FAILED', domain, {
723
+ url: deploymentUrl,
724
+ error: error.message,
725
+ attempts: maxRetries,
726
+ environment: this.environment
727
+ });
728
+
729
+ // Don't fail deployment for health check failure - it might just need time
730
+ return true;
731
+ } else {
732
+ console.log(` ⚠️ ${errorMsg} (attempt ${attempt}/${maxRetries})`);
733
+ console.log(` ⏳ Retrying in ${retryDelay / 1000} seconds...`);
734
+ await new Promise(resolve => setTimeout(resolve, retryDelay));
735
+ }
736
+ }
737
+ }
738
+ return true;
739
+ }
740
+
741
+ /**
742
+ * Get rollback plan using state manager
743
+ * @returns {Array} Rollback plan from state manager
744
+ */
745
+ getRollbackPlan() {
746
+ return this.stateManager.portfolioState.rollbackPlan;
747
+ }
748
+
749
+ /**
750
+ * Execute rollback using state manager
751
+ * @returns {Promise<Object>} Rollback result
752
+ */
753
+ async executeRollback() {
754
+ return await this.stateManager.executeRollback();
755
+ }
756
+
757
+ /**
758
+ * Get portfolio statistics from state manager
759
+ * @returns {Object} Portfolio statistics
760
+ */
761
+ getPortfolioStats() {
762
+ return this.stateManager.getPortfolioSummary();
763
+ }
764
+ }
765
+ export default MultiDomainOrchestrator;