@tamyla/clodo-framework 3.1.21 → 3.1.22

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 (169) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +53 -0
  3. package/dist/bin/clodo-service.js +47 -15
  4. package/dist/bin/commands/deploy.js +115 -83
  5. package/dist/bin/commands/helpers/deployment-ui.js +138 -0
  6. package/dist/bin/commands/helpers/deployment-verification.js +251 -0
  7. package/dist/bin/commands/helpers/error-recovery.js +80 -0
  8. package/dist/bin/commands/helpers/resource-detection.js +113 -0
  9. package/dist/bin/commands/validate.js +1 -1
  10. package/dist/bin/security/security-cli.js +1 -1
  11. package/dist/bin/shared/cache/configuration-cache.js +82 -0
  12. package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
  13. package/dist/bin/shared/cloudflare/index.js +1 -1
  14. package/dist/bin/shared/cloudflare/ops.js +6 -4
  15. package/dist/bin/shared/config/ConfigurationManager.js +23 -1
  16. package/dist/bin/shared/config/command-config-manager.js +19 -3
  17. package/dist/bin/shared/config/index.js +1 -1
  18. package/dist/bin/shared/deployment/credential-collector.js +30 -7
  19. package/dist/bin/shared/deployment/index.js +2 -2
  20. package/dist/bin/shared/deployment/rollback-manager.js +4 -520
  21. package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
  22. package/dist/bin/shared/deployment/validator.js +40 -10
  23. package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
  24. package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
  25. package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
  26. package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
  27. package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
  28. package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
  29. package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
  30. package/dist/bin/shared/error-handling/error-classifier.js +46 -0
  31. package/dist/bin/shared/monitoring/health-checker.js +129 -1
  32. package/dist/bin/shared/monitoring/memory-manager.js +17 -6
  33. package/dist/bin/shared/routing/domain-router.js +1 -1
  34. package/dist/bin/shared/utils/deployment-validator.js +97 -0
  35. package/dist/bin/shared/utils/formatters.js +10 -0
  36. package/dist/bin/shared/utils/index.js +13 -1
  37. package/dist/bin/shared/utils/interactive-prompts.js +34 -18
  38. package/dist/bin/shared/utils/progress-manager.js +2 -2
  39. package/dist/bin/shared/utils/progress-spinner.js +53 -0
  40. package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
  41. package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
  42. package/dist/security/index.js +1 -1
  43. package/dist/security/patterns/insecure-patterns.js +1 -1
  44. package/dist/utils/constants.js +102 -0
  45. package/dist/utils/deployment/wrangler-config-manager.js +215 -48
  46. package/dist/utils/framework-config.js +2 -2
  47. package/dist/utils/interactive-prompts.js +10 -59
  48. package/package.json +16 -8
  49. package/dist/bin/clodo-service-old.js +0 -868
  50. package/dist/bin/clodo-service-test.js +0 -10
  51. package/dist/bin/commands/assess.js +0 -91
  52. package/dist/bin/commands/create.js +0 -77
  53. package/dist/bin/commands/diagnose.js +0 -83
  54. package/dist/bin/commands/helpers.js +0 -138
  55. package/dist/bin/commands/update.js +0 -75
  56. package/dist/bin/database/deployment-db-manager.js +0 -423
  57. package/dist/bin/database/enterprise-db-manager.js +0 -457
  58. package/dist/bin/database/wrangler-d1-manager.js +0 -685
  59. package/dist/bin/deployment/enterprise-deploy.js +0 -877
  60. package/dist/bin/deployment/master-deploy.js +0 -1376
  61. package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
  62. package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
  63. package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
  64. package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
  65. package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
  66. package/dist/bin/deployment/modules/ValidationManager.js +0 -342
  67. package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  68. package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  69. package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  70. package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  71. package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  72. package/dist/bin/deployment/test-interactive-utils.js +0 -66
  73. package/dist/bin/portfolio/portfolio-manager.js +0 -487
  74. package/dist/bin/service-management/create-service.js +0 -122
  75. package/dist/bin/service-management/init-service.js +0 -79
  76. package/dist/config/customers.js +0 -623
  77. package/dist/config/domains.js +0 -186
  78. package/dist/config/index.js +0 -6
  79. package/dist/database/database-orchestrator.js +0 -795
  80. package/dist/database/index.js +0 -4
  81. package/dist/deployment/index.js +0 -11
  82. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  83. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  84. package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  85. package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  86. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  87. package/dist/deployment/orchestration/index.js +0 -17
  88. package/dist/deployment/rollback-manager.js +0 -36
  89. package/dist/deployment/wrangler-deployer.js +0 -640
  90. package/dist/handlers/GenericRouteHandler.js +0 -532
  91. package/dist/migration/MigrationAdapters.js +0 -562
  92. package/dist/modules/ModuleManager.js +0 -668
  93. package/dist/modules/security.js +0 -96
  94. package/dist/orchestration/cross-domain-coordinator.js +0 -1083
  95. package/dist/orchestration/index.js +0 -5
  96. package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
  97. package/dist/orchestration/modules/DomainResolver.js +0 -198
  98. package/dist/orchestration/modules/StateManager.js +0 -332
  99. package/dist/orchestration/multi-domain-orchestrator.js +0 -724
  100. package/dist/routing/EnhancedRouter.js +0 -158
  101. package/dist/schema/SchemaManager.js +0 -778
  102. package/dist/service-management/ConfirmationEngine.js +0 -412
  103. package/dist/service-management/ErrorTracker.js +0 -299
  104. package/dist/service-management/GenerationEngine.js +0 -447
  105. package/dist/service-management/InputCollector.js +0 -619
  106. package/dist/service-management/ServiceCreator.js +0 -265
  107. package/dist/service-management/ServiceInitializer.js +0 -453
  108. package/dist/service-management/ServiceOrchestrator.js +0 -633
  109. package/dist/service-management/generators/BaseGenerator.js +0 -233
  110. package/dist/service-management/generators/GeneratorRegistry.js +0 -254
  111. package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
  112. package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
  113. package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
  114. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
  115. package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
  116. package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
  117. package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
  118. package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
  119. package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
  120. package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
  121. package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
  122. package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
  123. package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
  124. package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
  125. package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
  126. package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
  127. package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
  128. package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
  129. package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
  130. package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
  131. package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
  132. package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
  133. package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
  134. package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
  135. package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
  136. package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
  137. package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
  138. package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
  139. package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
  140. package/dist/service-management/generators/utils/FileWriter.js +0 -179
  141. package/dist/service-management/generators/utils/PathResolver.js +0 -157
  142. package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
  143. package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
  144. package/dist/service-management/generators/utils/index.js +0 -18
  145. package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
  146. package/dist/service-management/handlers/GenerationHandler.js +0 -80
  147. package/dist/service-management/handlers/InputHandler.js +0 -59
  148. package/dist/service-management/handlers/ValidationHandler.js +0 -203
  149. package/dist/service-management/index.js +0 -14
  150. package/dist/service-management/routing/DomainRouteMapper.js +0 -311
  151. package/dist/service-management/routing/RouteGenerator.js +0 -266
  152. package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
  153. package/dist/service-management/routing/index.js +0 -14
  154. package/dist/service-management/services/DirectoryStructureService.js +0 -56
  155. package/dist/service-management/services/GenerationCoordinator.js +0 -208
  156. package/dist/service-management/services/GeneratorRegistry.js +0 -174
  157. package/dist/services/GenericDataService.js +0 -501
  158. package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
  159. package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
  160. package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
  161. package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
  162. package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
  163. package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
  164. package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
  165. package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
  166. package/dist/ui-structures/reference/service-manifest-template.json +0 -342
  167. package/dist/version/VersionDetector.js +0 -723
  168. package/dist/worker/index.js +0 -4
  169. package/dist/worker/integration.js +0 -351
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Deployment Verification Helpers (UI Wrapper)
3
+ * Provides UI-specific deployment verification by delegating to shared infrastructure
4
+ */
5
+
6
+ import { verifyWorkerDeployment, healthCheckWithBackoff } from '../../shared/monitoring/health-checker.js';
7
+ import { checkHealth } from '../../shared/monitoring/health-checker.js';
8
+ import chalk from 'chalk';
9
+ import readline from 'readline';
10
+
11
+ /**
12
+ * Run comprehensive post-deployment verification
13
+ * @param {string} serviceName - Name of the service
14
+ * @param {Object} deploymentResult - Result from deployment
15
+ * @param {Object} credentials - Cloudflare credentials
16
+ * @param {Object} options - Verification options
17
+ * @returns {Promise<Object>} Verification result
18
+ */
19
+ export async function runPostDeploymentVerification(serviceName, deploymentResult, credentials, options = {}) {
20
+ console.log('\n🔍 Post-deployment Verification');
21
+
22
+ // Skip health checks in dry-run mode with clear explanation
23
+ if (options.dryRun) {
24
+ console.log(chalk.blue(' ℹ️ Health checks skipped in dry-run mode'));
25
+ console.log(chalk.gray(' 💡 Run without --dry-run to deploy and verify the worker'));
26
+ return {
27
+ success: true,
28
+ healthCheckPassed: null,
29
+ reason: 'dry-run-skipped'
30
+ };
31
+ }
32
+ try {
33
+ // Wait for deployment to propagate
34
+ console.log(' ⏳ Waiting for deployment to propagate...');
35
+ await new Promise(resolve => setTimeout(resolve, 5000));
36
+
37
+ // Run health checks with enhanced URL discovery
38
+ console.log(' 🏥 Running health checks...');
39
+
40
+ // Try multiple sources for worker URL (in priority order)
41
+ const workerUrl = await discoverWorkerUrl(deploymentResult, credentials, options);
42
+ if (workerUrl) {
43
+ console.log(chalk.gray(` 🔗 Testing: ${workerUrl}`));
44
+ const health = await checkHealth(workerUrl).catch(err => {
45
+ console.log(chalk.yellow(` ⚠️ Health check failed: ${err.message}`));
46
+ return {
47
+ status: 'unknown',
48
+ error: err.message
49
+ };
50
+ });
51
+ if (health && health.status === 'ok') {
52
+ console.log(` ✅ Deployment verified: ${health.framework?.models?.length || 0} models active`);
53
+ return {
54
+ success: true,
55
+ healthCheckPassed: true,
56
+ healthData: health,
57
+ url: workerUrl
58
+ };
59
+ } else {
60
+ console.log(` ⚠️ Health check returned: ${health?.status || 'unknown'}`);
61
+ if (health.error) {
62
+ console.log(chalk.gray(` Error: ${health.error}`));
63
+ }
64
+ // Don't fail deployment for health check issues
65
+ return {
66
+ success: true,
67
+ healthCheckPassed: false,
68
+ healthData: health,
69
+ url: workerUrl
70
+ };
71
+ }
72
+ } else {
73
+ console.log(chalk.yellow(' ⚠️ No worker URL could be determined'));
74
+ console.log(chalk.gray(' 💡 To fix: Add route in wrangler.toml or check Cloudflare dashboard'));
75
+ return {
76
+ success: true,
77
+ healthCheckPassed: null,
78
+ reason: 'no-url-discovered'
79
+ };
80
+ }
81
+ } catch (error) {
82
+ console.log(chalk.yellow(` ⚠️ Post-deployment verification warning: ${error.message}`));
83
+ // Don't fail overall deployment for verification issues
84
+ return {
85
+ success: true,
86
+ healthCheckPassed: false,
87
+ error: error.message
88
+ };
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Discover worker URL from multiple sources
94
+ * Priority: deploymentResult.url > workers.dev convention > Cloudflare API discovery
95
+ * @param {Object} deploymentResult - Result from deployment
96
+ * @param {Object} credentials - Cloudflare credentials
97
+ * @param {Object} options - Discovery options
98
+ * @returns {Promise<string|null>} Discovered worker URL or null
99
+ */
100
+ async function discoverWorkerUrl(deploymentResult, credentials, options = {}) {
101
+ // Source 1: Direct URL from deployment result
102
+ if (deploymentResult?.url) {
103
+ return deploymentResult.url;
104
+ }
105
+
106
+ // Source 2: Construct workers.dev URL from credentials
107
+ if (credentials?.cloudflareSettings?.accountId && options.serviceName) {
108
+ const accountHash = credentials.cloudflareSettings.accountId;
109
+ const scriptName = options.serviceName || 'my-worker';
110
+ const workersDevUrl = `https://${scriptName}.${accountHash}.workers.dev`;
111
+
112
+ // Verify this URL exists by checking if we can reach it
113
+ const urlExists = await verifyUrlExists(workersDevUrl);
114
+ if (urlExists) {
115
+ return workersDevUrl;
116
+ }
117
+ }
118
+
119
+ // Source 3: Query Cloudflare API for worker routes
120
+ if (credentials?.cloudflareSettings) {
121
+ const discoveredUrl = await discoverUrlFromCloudflare(credentials.cloudflareSettings, options);
122
+ if (discoveredUrl) {
123
+ return discoveredUrl;
124
+ }
125
+ }
126
+
127
+ // Source 4: Custom domain from options
128
+ if (options.customDomain) {
129
+ return `https://${options.customDomain}`;
130
+ }
131
+ return null;
132
+ }
133
+
134
+ /**
135
+ * Verify if a URL exists (can be reached)
136
+ * @param {string} url - URL to verify
137
+ * @returns {Promise<boolean>} True if URL responds
138
+ */
139
+ async function verifyUrlExists(url) {
140
+ try {
141
+ const response = await fetch(url, {
142
+ method: 'HEAD',
143
+ signal: AbortSignal.timeout(5000)
144
+ });
145
+ return response.ok || response.status === 404; // 404 means route exists but no handler
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Discover worker URL from Cloudflare API
153
+ * @param {Object} cloudflareSettings - Cloudflare credentials
154
+ * @param {Object} options - Discovery options
155
+ * @returns {Promise<string|null>} Discovered URL or null
156
+ */
157
+ async function discoverUrlFromCloudflare(cloudflareSettings, options = {}) {
158
+ try {
159
+ const {
160
+ CloudflareAPI
161
+ } = await import('../../../utils/cloudflare/api.js');
162
+ const api = new CloudflareAPI(cloudflareSettings.token);
163
+
164
+ // Get worker routes for the zone
165
+ if (cloudflareSettings.zoneId) {
166
+ const response = await api.request(`/zones/${cloudflareSettings.zoneId}/workers/routes`);
167
+ const routes = response.result || [];
168
+ if (routes.length > 0) {
169
+ // Find route for this worker
170
+ const scriptName = options.serviceName || options.workerName;
171
+ const matchingRoute = routes.find(r => r.script && (r.script === scriptName || r.script.includes(scriptName)));
172
+ if (matchingRoute && matchingRoute.pattern) {
173
+ // Convert route pattern to URL (basic conversion)
174
+ const url = matchingRoute.pattern.replace('*', '').replace('//', '//');
175
+ return url.startsWith('http') ? url : `https://${url}`;
176
+ }
177
+ }
178
+ }
179
+
180
+ // Fallback: Get worker info from scripts API
181
+ const scriptsResponse = await api.request(`/accounts/${cloudflareSettings.accountId}/workers/scripts`);
182
+ const scripts = scriptsResponse.result || [];
183
+ const scriptName = options.serviceName || options.workerName;
184
+ const script = scripts.find(s => s.id === scriptName || s.id.includes(scriptName));
185
+ if (script && script.url) {
186
+ return script.url;
187
+ }
188
+ } catch (error) {
189
+ // API discovery failed - not critical
190
+ if (options.verbose) {
191
+ console.log(chalk.gray(` Could not discover URL from Cloudflare API: ${error.message}`));
192
+ }
193
+ }
194
+ return null;
195
+ }
196
+
197
+ /**
198
+ * Verify deployment via Cloudflare API (UI wrapper)
199
+ * @param {string} workerName - Name of the worker to verify
200
+ * @param {Object} credentials - Cloudflare credentials
201
+ * @param {Object} options - Verification options
202
+ * @returns {Promise<Object>} Verification result
203
+ */
204
+ export async function verifyDeployment(workerName, credentials, options = {}) {
205
+ // Delegate to shared infrastructure
206
+ return await verifyWorkerDeployment(workerName, credentials, options);
207
+ }
208
+
209
+ /**
210
+ * Perform health check with user permission prompt
211
+ * @param {string} baseUrl - Base URL to check
212
+ * @param {Object} options - Health check options
213
+ * @returns {Promise<Object>} Health check result
214
+ */
215
+ export async function performHealthCheckWithPermission(baseUrl, options = {}) {
216
+ const {
217
+ skipPrompt = false,
218
+ ...healthOptions
219
+ } = options;
220
+ if (!skipPrompt) {
221
+ const permission = await promptForHealthCheck();
222
+ if (!permission) {
223
+ console.log(chalk.yellow('\n⏭️ Health check skipped by user'));
224
+ return {
225
+ healthy: null,
226
+ skipped: true
227
+ };
228
+ }
229
+ }
230
+ console.log(chalk.gray('\n🏥 Running health checks with exponential backoff...\n'));
231
+
232
+ // Delegate to shared infrastructure
233
+ return await healthCheckWithBackoff(baseUrl, healthOptions);
234
+ }
235
+
236
+ /**
237
+ * Prompt user for health check permission
238
+ * @returns {Promise<boolean>} Whether to proceed with health check
239
+ */
240
+ async function promptForHealthCheck() {
241
+ const rl = readline.createInterface({
242
+ input: process.stdin,
243
+ output: process.stdout
244
+ });
245
+ return new Promise(resolve => {
246
+ rl.question(chalk.cyan('\n❓ Run health checks to verify deployment? (Y/n): '), answer => {
247
+ rl.close();
248
+ resolve(!answer || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
249
+ });
250
+ });
251
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Error Recovery Helpers (UI Wrapper)
3
+ * Provides interactive error recovery by delegating to shared error classification
4
+ */
5
+
6
+ import { classifyError, getRecoverySuggestions } from '../../shared/error-handling/error-classifier.js';
7
+ import chalk from 'chalk';
8
+ import readline from 'readline';
9
+
10
+ /**
11
+ * Handle deployment error with contextual recovery options
12
+ * @param {Error} error - The deployment error
13
+ * @param {Object} context - Deployment context
14
+ * @returns {Promise<string>} Recovery action: 'retry', 'dry-run', or 'abort'
15
+ */
16
+ export async function handleDeploymentError(error, context = {}) {
17
+ // Classify error using shared infrastructure
18
+ const errorType = classifyError(error);
19
+ const suggestions = getRecoverySuggestions(errorType);
20
+
21
+ // Display error with contextual information
22
+ console.error(chalk.red(`\n❌ Deployment failed: ${error.message}\n`));
23
+ console.log(chalk.yellow(`Error Type: ${errorType.toUpperCase()}\n`));
24
+
25
+ // Display recovery suggestions
26
+ console.log(chalk.cyan('💡 Suggestions:'));
27
+ suggestions.forEach(suggestion => {
28
+ console.log(chalk.gray(` • ${suggestion}`));
29
+ });
30
+
31
+ // Prompt for recovery action
32
+ return await promptForRecoveryAction();
33
+ }
34
+
35
+ /**
36
+ * Prompt user for recovery action
37
+ * @returns {Promise<string>} Recovery action
38
+ */
39
+ async function promptForRecoveryAction() {
40
+ const rl = readline.createInterface({
41
+ input: process.stdin,
42
+ output: process.stdout
43
+ });
44
+ console.log(chalk.cyan('\nRecovery Options:'));
45
+ console.log(chalk.gray(' 1) Retry deployment'));
46
+ console.log(chalk.gray(' 2) Run in dry-run mode to diagnose'));
47
+ console.log(chalk.gray(' 3) Abort'));
48
+ return new Promise(resolve => {
49
+ rl.question(chalk.cyan('\nSelect option (1-3): '), answer => {
50
+ rl.close();
51
+ switch (answer.trim()) {
52
+ case '1':
53
+ resolve('retry');
54
+ break;
55
+ case '2':
56
+ resolve('dry-run');
57
+ break;
58
+ case '3':
59
+ default:
60
+ resolve('abort');
61
+ break;
62
+ }
63
+ });
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Display error summary for non-interactive mode
69
+ * @param {Error} error - The error
70
+ */
71
+ export function displayErrorSummary(error) {
72
+ const errorType = classifyError(error);
73
+ const suggestions = getRecoverySuggestions(errorType);
74
+ console.error(chalk.red(`\n❌ ${error.message}`));
75
+ console.log(chalk.yellow(`\nError Type: ${errorType.toUpperCase()}`));
76
+ console.log(chalk.cyan('\nSuggestions:'));
77
+ suggestions.forEach(suggestion => {
78
+ console.log(chalk.gray(` • ${suggestion}`));
79
+ });
80
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Resource Detection Helpers
3
+ * Detects existing Cloudflare resources (workers, databases) for change tracking
4
+ */
5
+
6
+ import chalk from 'chalk';
7
+
8
+ /**
9
+ * Detect existing resources to show what will change
10
+ * @param {string} serviceName - Name of the service
11
+ * @param {Object} manifest - Service manifest
12
+ * @param {Object} credentials - Cloudflare credentials
13
+ * @param {Object} output - Output formatter
14
+ * @param {boolean} verbose - Verbose logging flag
15
+ * @returns {Promise<Object>} Detected resources
16
+ */
17
+ export async function detectExistingResources(serviceName, manifest, credentials, output, verbose = false) {
18
+ let existingWorker = null;
19
+ let existingDatabases = [];
20
+ let resourceDetectionFailed = false;
21
+ try {
22
+ // Import CloudflareAPI to check existing resources
23
+ const {
24
+ CloudflareAPI
25
+ } = await import('../../../utils/cloudflare/api.js');
26
+ const cfApi = new CloudflareAPI(credentials.token);
27
+
28
+ // Check if worker already exists
29
+ const workers = await cfApi.listWorkers(credentials.accountId);
30
+ existingWorker = workers.find(w => w.name === serviceName);
31
+
32
+ // Check existing databases
33
+ existingDatabases = await cfApi.listD1Databases(credentials.accountId);
34
+ } catch (error) {
35
+ resourceDetectionFailed = true;
36
+ if (verbose && output) {
37
+ output.warn(`Could not detect existing resources: ${error.message}`);
38
+ }
39
+ }
40
+ return {
41
+ existingWorker,
42
+ existingDatabases,
43
+ resourceDetectionFailed
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Display deployment plan with change detection badges
49
+ * @param {Object} planData - Deployment plan data
50
+ */
51
+ export function displayDeploymentPlan(planData) {
52
+ const {
53
+ serviceName,
54
+ serviceType,
55
+ selectedDomain,
56
+ environment,
57
+ credentials,
58
+ dryRun,
59
+ existingWorker,
60
+ existingDatabases,
61
+ resourceDetectionFailed,
62
+ manifest
63
+ } = planData;
64
+ console.log(chalk.cyan('\n📋 Deployment Plan:'));
65
+ console.log(chalk.gray('─'.repeat(60)));
66
+ console.log(chalk.white(`Service: ${serviceName}`));
67
+ console.log(chalk.white(`Type: ${serviceType}`));
68
+ console.log(chalk.white(`Domain: ${selectedDomain}`));
69
+ console.log(chalk.white(`Environment: ${environment || 'production'}`));
70
+ console.log(chalk.white(`Account: ${credentials.accountId.substring(0, 8)}...`));
71
+ console.log(chalk.white(`Zone: ${credentials.zoneId.substring(0, 8)}...`));
72
+ console.log(chalk.white(`Deployment Mode: ${dryRun ? 'DRY RUN' : 'LIVE'}`));
73
+ console.log(chalk.gray('─'.repeat(60)));
74
+
75
+ // Show what will be deployed with change badges
76
+ console.log(chalk.cyan('\n📦 Resources:'));
77
+
78
+ // Worker status
79
+ if (existingWorker) {
80
+ const lastModified = existingWorker.modifiedOn ? new Date(existingWorker.modifiedOn).toLocaleString() : 'unknown';
81
+ console.log(chalk.yellow(` [CHANGE] Worker: ${serviceName}`));
82
+ console.log(chalk.gray(` Last deployed: ${lastModified}`));
83
+ } else if (!resourceDetectionFailed) {
84
+ console.log(chalk.green(` [NEW] Worker: ${serviceName}`));
85
+ } else {
86
+ console.log(chalk.white(` [DEPLOY] Worker: ${serviceName}`));
87
+ }
88
+
89
+ // Database status (if manifest has database config)
90
+ if (manifest.database && manifest.database.name) {
91
+ const dbName = manifest.database.name;
92
+ const existingDb = existingDatabases.find(db => db.name === dbName || db.name === `${serviceName}-${dbName}`);
93
+ if (existingDb) {
94
+ console.log(chalk.yellow(` [CHANGE] Database: ${dbName} (binding update)`));
95
+ } else if (!resourceDetectionFailed) {
96
+ console.log(chalk.green(` [NEW] Database: ${dbName}`));
97
+ } else {
98
+ console.log(chalk.white(` [DEPLOY] Database: ${dbName}`));
99
+ }
100
+ }
101
+
102
+ // Routes
103
+ const workersDomain = `${serviceName}.${credentials.accountId}.workers.dev`;
104
+ console.log(chalk.green(` [NEW] Route: https://${workersDomain}`));
105
+ if (selectedDomain && selectedDomain !== 'workers.cloudflare.com') {
106
+ console.log(chalk.yellow(` [MANUAL] Custom domain: ${selectedDomain}`));
107
+ console.log(chalk.gray(` ⚠️ Requires DNS configuration (see post-deployment steps)`));
108
+ }
109
+ console.log(chalk.gray('─'.repeat(60)));
110
+ if (dryRun) {
111
+ console.log(chalk.yellow('\n🔍 DRY RUN MODE - No changes will be made\n'));
112
+ }
113
+ }
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import chalk from 'chalk';
6
- import { ServiceOrchestrator } from "../../service-management/ServiceOrchestrator.js";
6
+ import { ServiceOrchestrator } from '../../service-management/ServiceOrchestrator.js';
7
7
  import { StandardOptions } from '../shared/utils/cli-options.js';
8
8
  import { ConfigLoader } from '../shared/utils/config-loader.js';
9
9
  export function registerValidateCommand(program) {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { SecurityCLI } from "../../security/SecurityCLI.js";
2
+ import { SecurityCLI } from '../../security/SecurityCLI.js';
3
3
  const command = process.argv[2];
4
4
  const args = process.argv.slice(3);
5
5
  async function main() {
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Configuration Cache
3
+ *
4
+ * Basic configuration caching implementation for enterprise modules
5
+ */
6
+
7
+ export class ConfigurationCache {
8
+ constructor(options = {}) {
9
+ this.cache = new Map();
10
+ this.options = {
11
+ maxSize: options.maxSize || 1000,
12
+ ttl: options.ttl || 3600000,
13
+ // 1 hour default
14
+ ...options
15
+ };
16
+ }
17
+ async set(key, value, ttl = null) {
18
+ const expiry = ttl || this.options.ttl;
19
+ const entry = {
20
+ value,
21
+ expiry: Date.now() + expiry,
22
+ created: Date.now()
23
+ };
24
+
25
+ // Simple LRU: if at max size, remove oldest entry
26
+ if (this.cache.size >= this.options.maxSize) {
27
+ const firstKey = this.cache.keys().next().value;
28
+ this.cache.delete(firstKey);
29
+ }
30
+ this.cache.set(key, entry);
31
+ return true;
32
+ }
33
+ async get(key) {
34
+ const entry = this.cache.get(key);
35
+ if (!entry) return null;
36
+
37
+ // Check if expired
38
+ if (Date.now() > entry.expiry) {
39
+ this.cache.delete(key);
40
+ return null;
41
+ }
42
+ return entry.value;
43
+ }
44
+ async has(key) {
45
+ const entry = this.cache.get(key);
46
+ if (!entry) return false;
47
+ if (Date.now() > entry.expiry) {
48
+ this.cache.delete(key);
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+ async delete(key) {
54
+ return this.cache.delete(key);
55
+ }
56
+ async clear() {
57
+ this.cache.clear();
58
+ return true;
59
+ }
60
+ async size() {
61
+ // Clean expired entries
62
+ const now = Date.now();
63
+ for (const [key, entry] of this.cache.entries()) {
64
+ if (now > entry.expiry) {
65
+ this.cache.delete(key);
66
+ }
67
+ }
68
+ return this.cache.size;
69
+ }
70
+ async keys() {
71
+ const now = Date.now();
72
+ const validKeys = [];
73
+ for (const [key, entry] of this.cache.entries()) {
74
+ if (now <= entry.expiry) {
75
+ validKeys.push(key);
76
+ } else {
77
+ this.cache.delete(key);
78
+ }
79
+ }
80
+ return validKeys;
81
+ }
82
+ }
@@ -15,7 +15,7 @@ import { promisify } from 'util';
15
15
  import { exec } from 'child_process';
16
16
  import { askChoice, askYesNo } from '../utils/interactive-prompts.js';
17
17
  import { DomainDiscovery } from './domain-discovery.js';
18
- import { MultiDomainOrchestrator } from "../../../orchestration/multi-domain-orchestrator.js";
18
+ import { MultiDomainOrchestrator } from '../../../orchestration/multi-domain-orchestrator.js';
19
19
  import { getCommandConfig } from '../config/command-config-manager.js';
20
20
  import { CloudflareTokenManager } from '../security/api-token-manager.js';
21
21
  const execAsync = promisify(exec);
@@ -5,4 +5,4 @@
5
5
 
6
6
  export { CloudflareDomainManager } from './domain-manager.js';
7
7
  export { DomainDiscovery } from './domain-discovery.js';
8
- export { CloudflareOps } from './ops.js';
8
+ export * from './ops.js';
@@ -44,15 +44,17 @@ const dbManager = new DatabaseConnectionManager({
44
44
  maxPoolSize: 5
45
45
  });
46
46
 
47
- // Memory management
48
- const memoryManager = startMemoryMonitoring({
47
+ // Memory management - Disabled for CLI commands (short-lived processes)
48
+ // Only enable for long-running server processes
49
+ const memoryManager = process.env.ENABLE_MEMORY_MONITORING === 'true' ? startMemoryMonitoring({
49
50
  gcInterval: 300000,
50
51
  // 5 minutes
51
- memoryThreshold: 0.8,
52
+ memoryThreshold: 0.9,
53
+ // 90% threshold (increased from 80%)
52
54
  cleanupInterval: 60000,
53
55
  // 1 minute
54
56
  leakDetection: true
55
- });
57
+ }) : null;
56
58
 
57
59
  // Graceful shutdown handling (lazy initialization to avoid sync issues)
58
60
  let shutdownManager = null;
@@ -71,7 +71,29 @@ export class ConfigurationManager {
71
71
  this._validateConfiguration();
72
72
  }
73
73
 
74
- // ============ FEATURE FLAG MANAGEMENT ============
74
+ /**
75
+ * Deep merge two configuration objects
76
+ * @param {Object} defaults - Default configuration
77
+ * @param {Object} userConfig - User-provided configuration
78
+ * @returns {Object} Merged configuration
79
+ */
80
+ mergeConfigurations(defaults, userConfig) {
81
+ const output = {
82
+ ...defaults
83
+ };
84
+ for (const key in userConfig) {
85
+ if (userConfig[key] && typeof userConfig[key] === 'object' && !Array.isArray(userConfig[key])) {
86
+ // Recursively merge objects
87
+ output[key] = this.mergeConfigurations(defaults[key] || {}, userConfig[key]);
88
+ } else {
89
+ // Overwrite arrays and primitives
90
+ output[key] = userConfig[key];
91
+ }
92
+ }
93
+ return output;
94
+ }
95
+
96
+ // ============ UTILITY METHODS ============
75
97
 
76
98
  /**
77
99
  * Check if a feature is enabled
@@ -7,7 +7,14 @@
7
7
  */
8
8
 
9
9
  import { readFileSync } from 'fs';
10
- import { join } from 'path';
10
+ import { join, dirname } from 'path';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ // Get the directory of this module (framework's bin directory)
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+ // Navigate up to framework root, then to config directory
17
+ const FRAMEWORK_CONFIG_PATH = join(__dirname, '../../../config/validation-config.json');
11
18
  export class CommandConfigManager {
12
19
  constructor(configPath = null) {
13
20
  this.configPath = configPath || join(process.cwd(), 'validation-config.json');
@@ -20,12 +27,21 @@ export class CommandConfigManager {
20
27
  */
21
28
  loadConfig() {
22
29
  try {
30
+ // First try to load from service directory
23
31
  const configData = readFileSync(this.configPath, 'utf-8');
24
32
  this.config = JSON.parse(configData);
25
33
  console.log('📋 Loaded command configuration from validation-config.json');
26
34
  } catch (error) {
27
- console.log('⚠️ Could not load command config, using defaults');
28
- this.config = this.getDefaultConfig();
35
+ // If service config not found, try framework's internal config
36
+ try {
37
+ const frameworkConfigData = readFileSync(FRAMEWORK_CONFIG_PATH, 'utf-8');
38
+ this.config = JSON.parse(frameworkConfigData);
39
+ console.log('📋 Loaded command configuration from framework defaults');
40
+ } catch (frameworkError) {
41
+ // If neither exists, use hardcoded defaults
42
+ console.log('⚠️ Could not load command config, using minimal defaults');
43
+ this.config = this.getDefaultConfig();
44
+ }
29
45
  }
30
46
  }
31
47
 
@@ -7,7 +7,7 @@
7
7
  export { ConfigCache } from './cache.js';
8
8
  export { ConfigManager } from './manager.js';
9
9
  export { CommandConfigManager } from './command-config-manager.js';
10
- export { CustomerConfigurationManager } from '../../../src/config/customers.js';
10
+ export { CustomerConfigurationManager } from '../../../config/customers.js';
11
11
 
12
12
  // Phase 3.2 consolidated configuration management
13
13
  export { ConfigurationManager, configManager, isFeatureEnabled, getEnabledFeatures, withFeature, FEATURES, COMMON_FEATURES } from './ConfigurationManager.js';