@tamyla/clodo-framework 3.1.21 → 3.1.23

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 (150) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +283 -1
  3. package/dist/{bin → cli}/clodo-service.js +47 -15
  4. package/dist/cli/commands/assess.js +183 -0
  5. package/dist/{bin → cli}/commands/create.js +5 -5
  6. package/dist/{bin → cli}/commands/deploy.js +122 -90
  7. package/dist/{bin → cli}/commands/diagnose.js +5 -5
  8. package/dist/cli/commands/helpers/deployment-ui.js +138 -0
  9. package/dist/cli/commands/helpers/deployment-verification.js +250 -0
  10. package/dist/cli/commands/helpers/error-recovery.js +80 -0
  11. package/dist/cli/commands/helpers/resource-detection.js +113 -0
  12. package/dist/{bin → cli}/commands/helpers.js +0 -28
  13. package/dist/cli/commands/init-config.js +57 -0
  14. package/dist/{bin → cli}/commands/update.js +5 -5
  15. package/dist/{bin → cli}/commands/validate.js +5 -5
  16. package/dist/cli/security-cli.js +118 -0
  17. package/dist/config/FeatureManager.js +6 -0
  18. package/dist/config/clodo-create.example.json +26 -0
  19. package/dist/config/clodo-deploy.example.json +41 -0
  20. package/dist/config/clodo-update.example.json +46 -0
  21. package/dist/config/clodo-validate.example.json +41 -0
  22. package/dist/config/customers/template/development.env.template +37 -0
  23. package/dist/config/customers/template/production.env.template +39 -0
  24. package/dist/config/customers/template/staging.env.template +37 -0
  25. package/dist/config/customers.js +28 -26
  26. package/dist/config/domain-examples/README.md +464 -0
  27. package/dist/config/domain-examples/environment-mapped.json +168 -0
  28. package/dist/config/domain-examples/multi-domain.json +144 -0
  29. package/dist/config/domain-examples/single-domain.json +50 -0
  30. package/dist/config/examples +12 -0
  31. package/dist/config/features.js +61 -0
  32. package/dist/config/staging-deployment.json +60 -0
  33. package/dist/config/validation-config.json +347 -0
  34. package/dist/deployment/wrangler-deployer.js +1 -1
  35. package/dist/{bin → lib}/deployment/modules/DeploymentOrchestrator.js +2 -2
  36. package/dist/{bin → lib}/deployment/modules/EnvironmentManager.js +2 -2
  37. package/dist/lib/deployment/orchestration/EnterpriseOrchestrator.js +21 -0
  38. package/dist/lib/shared/cache/configuration-cache.js +82 -0
  39. package/dist/{bin → lib}/shared/cloudflare/domain-discovery.js +1 -1
  40. package/dist/{bin → lib}/shared/cloudflare/domain-manager.js +1 -1
  41. package/dist/{bin → lib}/shared/cloudflare/index.js +1 -1
  42. package/dist/{bin → lib}/shared/cloudflare/ops.js +10 -8
  43. package/dist/{bin → lib}/shared/config/ConfigurationManager.js +23 -1
  44. package/dist/{bin → lib}/shared/config/command-config-manager.js +19 -3
  45. package/dist/{bin → lib}/shared/config/index.js +1 -1
  46. package/dist/{bin → lib}/shared/deployment/credential-collector.js +30 -7
  47. package/dist/lib/shared/deployment/index.js +10 -0
  48. package/dist/lib/shared/deployment/rollback-manager.js +7 -0
  49. package/dist/lib/shared/deployment/utilities/d1-error-recovery.js +177 -0
  50. package/dist/{bin → lib}/shared/deployment/validator.js +40 -10
  51. package/dist/lib/shared/deployment/workflows/deployment-summary.js +214 -0
  52. package/dist/lib/shared/deployment/workflows/interactive-confirmation.js +188 -0
  53. package/dist/lib/shared/deployment/workflows/interactive-database-workflow.js +234 -0
  54. package/dist/lib/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
  55. package/dist/lib/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
  56. package/dist/lib/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
  57. package/dist/lib/shared/deployment/workflows/interactive-validation.js +218 -0
  58. package/dist/lib/shared/error-handling/error-classifier.js +46 -0
  59. package/dist/{bin → lib}/shared/monitoring/health-checker.js +129 -1
  60. package/dist/{bin → lib}/shared/monitoring/memory-manager.js +17 -6
  61. package/dist/{bin → lib}/shared/routing/domain-router.js +1 -1
  62. package/dist/lib/shared/utils/deployment-validator.js +97 -0
  63. package/dist/{bin → lib}/shared/utils/formatters.js +10 -0
  64. package/dist/{bin → lib}/shared/utils/index.js +13 -1
  65. package/dist/{bin → lib}/shared/utils/interactive-prompts.js +34 -18
  66. package/dist/{bin → lib}/shared/utils/progress-manager.js +2 -2
  67. package/dist/lib/shared/utils/progress-spinner.js +53 -0
  68. package/dist/lib/shared/utils/sensitive-redactor.js +91 -0
  69. package/dist/{bin → lib}/shared/validation/ValidationRegistry.js +1 -1
  70. package/dist/migration/MigrationAdapters.js +50 -4
  71. package/dist/orchestration/cross-domain-coordinator.js +5 -5
  72. package/dist/orchestration/multi-domain-orchestrator.js +63 -22
  73. package/dist/security/index.js +2 -2
  74. package/dist/security/patterns/insecure-patterns.js +1 -1
  75. package/dist/service-management/ConfirmationEngine.js +1 -1
  76. package/dist/service-management/ErrorTracker.js +1 -1
  77. package/dist/service-management/InputCollector.js +1 -1
  78. package/dist/service-management/ServiceCreator.js +11 -255
  79. package/dist/service-management/ServiceOrchestrator.js +0 -2
  80. package/dist/service-management/generators/testing/UnitTestsGenerator.js +4 -4
  81. package/dist/service-management/index.js +1 -1
  82. package/dist/utils/cloudflare/ops.js +1 -1
  83. package/dist/utils/constants.js +102 -0
  84. package/dist/utils/deployment/wrangler-config-manager.js +215 -48
  85. package/dist/utils/file-manager.js +1 -1
  86. package/dist/utils/formatters.js +1 -1
  87. package/dist/utils/framework-config.js +2 -2
  88. package/dist/utils/interactive-prompts.js +10 -59
  89. package/dist/utils/logger.js +1 -1
  90. package/dist/version/VersionDetector.js +99 -9
  91. package/dist/worker/integration.js +1 -1
  92. package/package.json +10 -10
  93. package/dist/bin/clodo-service-old.js +0 -868
  94. package/dist/bin/clodo-service-test.js +0 -10
  95. package/dist/bin/commands/assess.js +0 -91
  96. package/dist/bin/database/enterprise-db-manager.js +0 -457
  97. package/dist/bin/deployment/enterprise-deploy.js +0 -877
  98. package/dist/bin/deployment/master-deploy.js +0 -1376
  99. package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
  100. package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  101. package/dist/bin/deployment/test-interactive-utils.js +0 -66
  102. package/dist/bin/portfolio/portfolio-manager.js +0 -487
  103. package/dist/bin/security/security-cli.js +0 -108
  104. package/dist/bin/service-management/create-service.js +0 -122
  105. package/dist/bin/service-management/init-service.js +0 -79
  106. package/dist/bin/shared/deployment/index.js +0 -10
  107. package/dist/bin/shared/deployment/rollback-manager.js +0 -523
  108. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  109. package/dist/service-management/ServiceInitializer.js +0 -453
  110. /package/dist/{bin → lib}/database/deployment-db-manager.js +0 -0
  111. /package/dist/{bin → lib}/database/wrangler-d1-manager.js +0 -0
  112. /package/dist/{bin → lib}/deployment/modules/DeploymentConfiguration.js +0 -0
  113. /package/dist/{bin → lib}/deployment/modules/MonitoringIntegration.js +0 -0
  114. /package/dist/{bin → lib}/deployment/modules/ValidationManager.js +0 -0
  115. /package/dist/{bin → lib}/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -0
  116. /package/dist/{bin → lib}/deployment/orchestration/PortfolioOrchestrator.js +0 -0
  117. /package/dist/{bin → lib}/deployment/orchestration/SingleServiceOrchestrator.js +0 -0
  118. /package/dist/{bin → lib}/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -0
  119. /package/dist/{bin → lib}/shared/config/cache.js +0 -0
  120. /package/dist/{bin → lib}/shared/config/cloudflare-service-validator.js +0 -0
  121. /package/dist/{bin → lib}/shared/config/manager.js +0 -0
  122. /package/dist/{bin → lib}/shared/config/manifest-loader.js +0 -0
  123. /package/dist/{bin → lib}/shared/database/connection-manager.js +0 -0
  124. /package/dist/{bin → lib}/shared/database/index.js +0 -0
  125. /package/dist/{bin → lib}/shared/database/orchestrator.js +0 -0
  126. /package/dist/{bin → lib}/shared/deployment/auditor.js +0 -0
  127. /package/dist/{bin → lib}/shared/index.js +0 -0
  128. /package/dist/{bin → lib}/shared/logging/Logger.js +0 -0
  129. /package/dist/{bin → lib}/shared/monitoring/index.js +0 -0
  130. /package/dist/{bin → lib}/shared/monitoring/production-monitor.js +0 -0
  131. /package/dist/{bin → lib}/shared/production-tester/api-tester.js +0 -0
  132. /package/dist/{bin → lib}/shared/production-tester/auth-tester.js +0 -0
  133. /package/dist/{bin → lib}/shared/production-tester/core.js +0 -0
  134. /package/dist/{bin → lib}/shared/production-tester/database-tester.js +0 -0
  135. /package/dist/{bin → lib}/shared/production-tester/index.js +0 -0
  136. /package/dist/{bin → lib}/shared/production-tester/load-tester.js +0 -0
  137. /package/dist/{bin → lib}/shared/production-tester/performance-tester.js +0 -0
  138. /package/dist/{bin → lib}/shared/security/api-token-manager.js +0 -0
  139. /package/dist/{bin → lib}/shared/security/index.js +0 -0
  140. /package/dist/{bin → lib}/shared/security/secret-generator.js +0 -0
  141. /package/dist/{bin → lib}/shared/security/secure-token-manager.js +0 -0
  142. /package/dist/{bin → lib}/shared/utils/ErrorHandler.js +0 -0
  143. /package/dist/{bin → lib}/shared/utils/cli-options.js +0 -0
  144. /package/dist/{bin → lib}/shared/utils/config-loader.js +0 -0
  145. /package/dist/{bin → lib}/shared/utils/error-recovery.js +0 -0
  146. /package/dist/{bin → lib}/shared/utils/file-manager.js +0 -0
  147. /package/dist/{bin → lib}/shared/utils/graceful-shutdown-manager.js +0 -0
  148. /package/dist/{bin → lib}/shared/utils/interactive-utils.js +0 -0
  149. /package/dist/{bin → lib}/shared/utils/output-formatter.js +0 -0
  150. /package/dist/{bin → lib}/shared/utils/rate-limiter.js +0 -0
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Interactive Validation Workflow Module
3
+ *
4
+ * Provides reusable interactive validation workflows.
5
+ * Extracted from enterprise-deployment/master-deploy.js for modularity.
6
+ *
7
+ * @module interactive-validation
8
+ */
9
+
10
+ import { askYesNo } from '../utils/interactive-prompts.js';
11
+ import { validatePrerequisites, checkAuth, authenticate, workerExists } from '../cloudflare/ops.js';
12
+
13
+ /**
14
+ * Interactive Validation Workflow
15
+ * Handles pre-deployment checks and comprehensive validation
16
+ */
17
+ export class InteractiveValidation {
18
+ /**
19
+ * @param {Object} options - Configuration options
20
+ */
21
+ constructor(options = {}) {
22
+ this.interactive = options.interactive !== false;
23
+ }
24
+
25
+ /**
26
+ * Execute pre-deployment checks
27
+ *
28
+ * @param {Object} config - Deployment configuration
29
+ * @returns {Promise<Object>} Validation results
30
+ */
31
+ async executePreDeploymentChecks(config) {
32
+ console.log('\n✅ Pre-deployment Validation');
33
+ console.log('============================');
34
+
35
+ // Check prerequisites
36
+ await this.validatePrerequisites();
37
+
38
+ // Check authentication
39
+ await this.validateAuthentication();
40
+
41
+ // Check for existing deployments
42
+ await this.checkExistingDeployments(config);
43
+ console.log('\n✅ All pre-deployment checks passed');
44
+ return {
45
+ prerequisites: true,
46
+ authentication: true,
47
+ existingDeployments: false
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Validate system prerequisites
53
+ *
54
+ * @returns {Promise<void>}
55
+ */
56
+ async validatePrerequisites() {
57
+ console.log('\n🔍 Checking prerequisites...');
58
+ const prereqs = await validatePrerequisites();
59
+ for (const prereq of prereqs) {
60
+ if (prereq.status === 'ok') {
61
+ console.log(` ✅ ${prereq.name}: ${prereq.version}`);
62
+ } else {
63
+ throw new Error(`${prereq.name} is not available: ${prereq.error}`);
64
+ }
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Validate Cloudflare authentication
70
+ *
71
+ * @returns {Promise<void>}
72
+ */
73
+ async validateAuthentication() {
74
+ console.log('\n🔐 Checking Cloudflare authentication...');
75
+ const isAuthenticated = await checkAuth();
76
+ if (!isAuthenticated) {
77
+ if (!this.interactive) {
78
+ throw new Error('Cloudflare authentication required but running in non-interactive mode');
79
+ }
80
+ const shouldAuthenticate = await askYesNo('Cloudflare authentication required. Run authentication now?', 'y');
81
+ if (shouldAuthenticate) {
82
+ console.log('\n🔑 Please complete Cloudflare authentication...');
83
+ await authenticate();
84
+ } else {
85
+ throw new Error('Cloudflare authentication is required for deployment');
86
+ }
87
+ } else {
88
+ console.log(' ✅ Cloudflare: Authenticated');
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Check for existing deployments
94
+ *
95
+ * @param {Object} config - Deployment configuration
96
+ * @returns {Promise<boolean>} True if existing deployment found and should continue
97
+ */
98
+ async checkExistingDeployments(config) {
99
+ console.log('\n🔍 Checking for existing deployments...');
100
+ const workerExistsAlready = await workerExists(config.worker.name);
101
+ if (workerExistsAlready) {
102
+ console.log(` ⚠️ Worker '${config.worker.name}' already exists`);
103
+ if (!this.interactive) {
104
+ console.log(' ℹ️ Non-interactive mode: will overwrite existing worker');
105
+ return true;
106
+ }
107
+ const shouldOverwrite = await askYesNo('Do you want to overwrite the existing worker?', 'n');
108
+ if (!shouldOverwrite) {
109
+ throw new Error('Deployment cancelled - worker already exists');
110
+ }
111
+ return true;
112
+ } else {
113
+ console.log(` ✅ Worker name '${config.worker.name}' is available`);
114
+ return false;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Execute comprehensive validation
120
+ *
121
+ * @param {Object} config - Deployment configuration
122
+ * @param {Object} validationManager - ValidationManager instance
123
+ * @returns {Promise<Object>} Validation results
124
+ */
125
+ async executeComprehensiveValidation(config, validationManager) {
126
+ console.log('\n🔍 Comprehensive Validation');
127
+ console.log('==========================');
128
+ try {
129
+ const validationResult = await validationManager.validateDeploymentConfiguration({
130
+ domain: config.domain,
131
+ environment: config.environment,
132
+ worker: config.worker,
133
+ database: config.database,
134
+ secrets: config.secrets,
135
+ comprehensive: true
136
+ });
137
+ if (validationResult.valid) {
138
+ console.log(' ✅ Configuration validation passed');
139
+ if (validationResult.warnings?.length > 0) {
140
+ console.log(` ⚠️ ${validationResult.warnings.length} warnings found`);
141
+ for (const warning of validationResult.warnings) {
142
+ console.log(` - ${warning}`);
143
+ }
144
+ }
145
+ } else {
146
+ console.log(' ❌ Configuration validation failed');
147
+ for (const error of validationResult.errors) {
148
+ console.log(` - ${error}`);
149
+ }
150
+ throw new Error('Configuration validation failed');
151
+ }
152
+ return validationResult;
153
+ } catch (error) {
154
+ console.log(` ❌ Validation error: ${error.message}`);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Validate configuration object
161
+ *
162
+ * @param {Object} config - Configuration to validate
163
+ * @returns {Promise<Object>} Validation results
164
+ */
165
+ async validateConfiguration(config) {
166
+ const errors = [];
167
+ const warnings = [];
168
+
169
+ // Domain validation
170
+ if (!config.domain || config.domain.length === 0) {
171
+ errors.push('Domain name is required');
172
+ }
173
+
174
+ // Environment validation
175
+ if (!config.environment) {
176
+ errors.push('Environment is required');
177
+ }
178
+
179
+ // Worker validation
180
+ if (!config.worker?.name) {
181
+ errors.push('Worker name is required');
182
+ }
183
+
184
+ // Database validation
185
+ if (config.database?.name && !config.database?.id) {
186
+ warnings.push('Database name specified but no database ID');
187
+ }
188
+
189
+ // Secrets validation
190
+ if (Object.keys(config.secrets?.keys || {}).length === 0) {
191
+ warnings.push('No secrets configured');
192
+ }
193
+ return {
194
+ valid: errors.length === 0,
195
+ errors,
196
+ warnings
197
+ };
198
+ }
199
+
200
+ /**
201
+ * Get validation summary
202
+ *
203
+ * @param {Object} validationResult - Validation result
204
+ * @returns {string} Summary message
205
+ */
206
+ getValidationSummary(validationResult) {
207
+ if (validationResult.valid) {
208
+ const warningCount = validationResult.warnings?.length || 0;
209
+ if (warningCount > 0) {
210
+ return `✅ Validation passed with ${warningCount} warning(s)`;
211
+ }
212
+ return '✅ Validation passed';
213
+ } else {
214
+ const errorCount = validationResult.errors?.length || 0;
215
+ return `❌ Validation failed with ${errorCount} error(s)`;
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Error Classification and Recovery Module
3
+ * Provides intelligent error classification for contextual recovery suggestions
4
+ */
5
+
6
+ /**
7
+ * Classify error type for contextual recovery suggestions
8
+ * @param {Error} error - The error to classify
9
+ * @returns {string} Error type classification
10
+ */
11
+ export function classifyError(error) {
12
+ const msg = error.message.toLowerCase();
13
+ if (msg.includes('credential') || msg.includes('auth') || msg.includes('token') || msg.includes('unauthorized') || msg.includes('forbidden')) {
14
+ return 'credentials';
15
+ }
16
+ if (msg.includes('domain') || msg.includes('zone') || msg.includes('dns')) {
17
+ return 'domain';
18
+ }
19
+ if (msg.includes('network') || msg.includes('timeout') || msg.includes('econnrefused') || msg.includes('enotfound') || msg.includes('fetch failed')) {
20
+ return 'network';
21
+ }
22
+ if (msg.includes('bundle') || msg.includes('syntax') || msg.includes('compile') || msg.includes('build') || msg.includes('module not found')) {
23
+ return 'bundle';
24
+ }
25
+ if (msg.includes('database') || msg.includes('d1') || msg.includes('migration') || msg.includes('sql')) {
26
+ return 'database';
27
+ }
28
+ return 'unknown';
29
+ }
30
+
31
+ /**
32
+ * Get recovery suggestions for an error type
33
+ * @param {string} errorType - The classified error type
34
+ * @returns {Array<string>} Array of suggestion strings
35
+ */
36
+ export function getRecoverySuggestions(errorType) {
37
+ const suggestions = {
38
+ credentials: ['Check your API token, account ID, and zone ID', 'Verify token has not expired', 'Visit: https://dash.cloudflare.com/profile/api-tokens'],
39
+ domain: ['Verify domain exists in Cloudflare dashboard', 'Check API token has zone:read permissions', 'Ensure zone ID matches the domain'],
40
+ network: ['Check internet connectivity', 'Verify Cloudflare API is accessible', 'Check for firewall/proxy issues'],
41
+ bundle: ['Check for syntax errors in your code', 'Verify all dependencies are installed', 'Run: npm install or yarn install'],
42
+ database: ['Verify database exists in Cloudflare', 'Check migrations are valid SQL', 'Ensure D1 binding name matches wrangler.toml'],
43
+ unknown: ['Check the error message for details', 'Run with DEBUG=1 for full stack trace', 'Review deployment logs']
44
+ };
45
+ return suggestions[errorType] || suggestions.unknown;
46
+ }
@@ -14,7 +14,7 @@ const execAsync = promisify(exec);
14
14
  // Load framework configuration
15
15
  const {
16
16
  frameworkConfig
17
- } = await import('../../../dist/utils/framework-config.js');
17
+ } = await import('../../utils/framework-config.js');
18
18
  const timing = frameworkConfig.getTiming();
19
19
  function makeHttpRequest(url, method = 'GET', timeout = 5000) {
20
20
  return new Promise((resolve, reject) => {
@@ -447,4 +447,132 @@ export function formatD1HealthResults(d1Results) {
447
447
  }
448
448
  }
449
449
  return lines.join('\n');
450
+ }
451
+
452
+ /**
453
+ * Modern fetch-based health check with exponential backoff
454
+ * @param {string} baseUrl - Base URL of the service
455
+ * @param {Object} options - Health check options
456
+ * @returns {Promise<Object>} Health check result
457
+ */
458
+ export async function healthCheckWithBackoff(baseUrl, options = {}) {
459
+ const {
460
+ maxAttempts = 10,
461
+ initialDelay = 1000,
462
+ maxDelay = 8000,
463
+ timeout = 5000,
464
+ silent = false
465
+ } = options;
466
+ let attempt = 0;
467
+ let delay = initialDelay;
468
+ while (attempt < maxAttempts) {
469
+ attempt++;
470
+ try {
471
+ if (!silent) {
472
+ process.stdout.write(` Attempt ${attempt}/${maxAttempts}... `);
473
+ }
474
+ const controller = new AbortController();
475
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
476
+ const startTime = Date.now();
477
+ const response = await fetch(`${baseUrl}/health`, {
478
+ signal: controller.signal,
479
+ headers: {
480
+ 'User-Agent': 'clodo-service-health-check'
481
+ }
482
+ });
483
+ clearTimeout(timeoutId);
484
+ const responseTime = Date.now() - startTime;
485
+ if (response.ok) {
486
+ // Success!
487
+ let status = null;
488
+ try {
489
+ const data = await response.json();
490
+ status = data.status || 'healthy';
491
+ } catch {
492
+ // Not JSON, that's ok
493
+ }
494
+ if (!silent) console.log('✓');
495
+ return {
496
+ healthy: true,
497
+ responseTime,
498
+ attempts: attempt,
499
+ status
500
+ };
501
+ }
502
+
503
+ // Non-200 but got response
504
+ if (!silent) console.log(`✗ (HTTP ${response.status})`);
505
+ } catch (error) {
506
+ if (!silent) {
507
+ if (error.name === 'AbortError') {
508
+ console.log('✗ (timeout)');
509
+ } else {
510
+ console.log(`✗ (${error.message})`);
511
+ }
512
+ }
513
+ }
514
+
515
+ // Wait before retry (exponential backoff)
516
+ if (attempt < maxAttempts) {
517
+ await new Promise(resolve => setTimeout(resolve, delay));
518
+ delay = Math.min(delay * 2, maxDelay); // Exponential backoff, capped
519
+ }
520
+ }
521
+ return {
522
+ healthy: false,
523
+ attempts: maxAttempts
524
+ };
525
+ }
526
+
527
+ /**
528
+ * Verify worker deployment via Cloudflare API
529
+ * @param {string} workerName - Name of the worker to verify
530
+ * @param {Object} credentials - Cloudflare credentials
531
+ * @param {Object} options - Verification options
532
+ * @returns {Promise<Object>} Verification result
533
+ */
534
+ export async function verifyWorkerDeployment(workerName, credentials, options = {}) {
535
+ const {
536
+ maxAttempts = 5,
537
+ delay = 2000,
538
+ silent = false
539
+ } = options;
540
+ let attempt = 0;
541
+ while (attempt < maxAttempts) {
542
+ attempt++;
543
+ try {
544
+ if (!silent) {
545
+ process.stdout.write(` Checking deployment status (attempt ${attempt}/${maxAttempts})... `);
546
+ }
547
+ const {
548
+ CloudflareAPI
549
+ } = await import('../../utils/cloudflare/api.js');
550
+ const cfApi = new CloudflareAPI(credentials.token);
551
+
552
+ // List all workers to find ours
553
+ const workers = await cfApi.listWorkers(credentials.accountId);
554
+ const deployedWorker = workers.find(w => w.name === workerName);
555
+ if (deployedWorker) {
556
+ if (!silent) console.log('✓');
557
+ return {
558
+ success: true,
559
+ status: 'Active',
560
+ modifiedOn: deployedWorker.modifiedOn ? new Date(deployedWorker.modifiedOn).toLocaleString() : null,
561
+ etag: deployedWorker.etag
562
+ };
563
+ }
564
+ if (!silent) console.log('Not found yet');
565
+ } catch (error) {
566
+ if (!silent) console.log(`✗ (${error.message})`);
567
+ }
568
+
569
+ // Wait before retry
570
+ if (attempt < maxAttempts) {
571
+ await new Promise(resolve => setTimeout(resolve, delay));
572
+ }
573
+ }
574
+ return {
575
+ success: false,
576
+ error: 'Worker not found in Cloudflare API after deployment'
577
+ };
450
578
  }
@@ -46,7 +46,11 @@ export class MemoryManager {
46
46
 
47
47
  // Register process cleanup handlers
48
48
  this.registerProcessHandlers();
49
- console.log('🧠 Memory monitoring started');
49
+
50
+ // Only log if verbose mode or DEBUG is enabled
51
+ if (process.env.DEBUG || process.env.VERBOSE) {
52
+ console.log('🧠 Memory monitoring started');
53
+ }
50
54
  }
51
55
 
52
56
  /**
@@ -87,16 +91,23 @@ export class MemoryManager {
87
91
  // Check memory thresholds
88
92
  const heapUsagePercent = memUsage.heapUsed / memUsage.heapTotal;
89
93
  if (heapUsagePercent > this.config.memoryThreshold) {
90
- console.warn(`⚠️ High memory usage detected: ${(heapUsagePercent * 100).toFixed(1)}%`);
94
+ // Only warn if verbose/debug mode or if usage is critically high (>95%)
95
+ if (process.env.DEBUG || process.env.VERBOSE || heapUsagePercent > 0.95) {
96
+ console.warn(`⚠️ High memory usage detected: ${(heapUsagePercent * 100).toFixed(1)}%`);
97
+ }
91
98
 
92
99
  // Force garbage collection if available
93
- if (global.gc) {
94
- console.log('🗑️ Running forced garbage collection');
100
+ if (global.gc && heapUsagePercent > 0.90) {
101
+ if (process.env.DEBUG) {
102
+ console.log('🗑️ Running forced garbage collection');
103
+ }
95
104
  global.gc();
96
105
  }
97
106
 
98
- // Run cleanup routines
99
- this.runCleanupRoutines();
107
+ // Run cleanup routines only if critically high
108
+ if (heapUsagePercent > 0.90) {
109
+ this.runCleanupRoutines();
110
+ }
100
111
  }
101
112
 
102
113
  // Check for memory leaks
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { existsSync, readFileSync } from 'fs';
16
16
  import { resolve } from 'path';
17
- import { MultiDomainOrchestrator } from "../../../orchestration/multi-domain-orchestrator.js";
17
+ import { MultiDomainOrchestrator } from '../../orchestration/multi-domain-orchestrator.js';
18
18
  export class DomainRouter {
19
19
  constructor(options = {}) {
20
20
  this.configPath = options.configPath || './config/domains.json';
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Deployment Validator Utility
3
+ * Pre-deployment validation and prerequisite checking
4
+ * Extracted from clodo-service-old.js for modular reuse
5
+ */
6
+
7
+ import { existsSync } from 'fs';
8
+ import { join } from 'path';
9
+ import chalk from 'chalk';
10
+
11
+ /**
12
+ * Validate deployment prerequisites before proceeding
13
+ * @param {Object} coreInputs - Core deployment inputs
14
+ * @param {Object} options - Deployment options
15
+ * @returns {Promise<boolean>} True if all prerequisites are valid
16
+ */
17
+ export async function validateDeploymentPrerequisites(coreInputs, options) {
18
+ const issues = [];
19
+ console.log(chalk.cyan('\n🔍 Pre-deployment Validation'));
20
+ console.log(chalk.gray('─'.repeat(40)));
21
+
22
+ // Check required fields
23
+ if (!coreInputs.customer) {
24
+ issues.push('Customer name is required');
25
+ }
26
+ if (!coreInputs.environment) {
27
+ issues.push('Environment is required');
28
+ }
29
+ if (!coreInputs.domainName) {
30
+ issues.push('Domain name is required');
31
+ }
32
+ if (!coreInputs.cloudflareToken) {
33
+ issues.push('Cloudflare API token is required');
34
+ }
35
+
36
+ // Check Cloudflare token format (basic validation)
37
+ if (coreInputs.cloudflareToken && !coreInputs.cloudflareToken.startsWith('CLOUDFLARE_API_TOKEN=')) {
38
+ if (coreInputs.cloudflareToken.length < 40) {
39
+ issues.push('Cloudflare API token appears to be invalid (too short)');
40
+ }
41
+ }
42
+
43
+ // Check if service path exists
44
+ if (options.servicePath && options.servicePath !== '.') {
45
+ if (!existsSync(options.servicePath)) {
46
+ issues.push(`Service path does not exist: ${options.servicePath}`);
47
+ }
48
+ }
49
+
50
+ // Check for wrangler.toml if not dry run
51
+ if (!options.dryRun) {
52
+ const wranglerPath = join(options.servicePath || '.', 'wrangler.toml');
53
+ if (!existsSync(wranglerPath)) {
54
+ issues.push('wrangler.toml not found in service directory');
55
+ }
56
+ }
57
+
58
+ // Report issues
59
+ if (issues.length > 0) {
60
+ console.log(chalk.red('\n❌ Validation Failed:'));
61
+ issues.forEach(issue => {
62
+ console.log(chalk.red(` • ${issue}`));
63
+ });
64
+ console.log(chalk.gray('\n─'.repeat(40)));
65
+ return false;
66
+ }
67
+ console.log(chalk.green('✅ All prerequisites validated'));
68
+ console.log(chalk.gray('─'.repeat(40)));
69
+ return true;
70
+ }
71
+
72
+ /**
73
+ * Validate basic deployment inputs
74
+ * @param {Object} inputs - Input object to validate
75
+ * @param {Array<string>} requiredFields - Required field names
76
+ * @returns {Object} Validation result { valid, errors }
77
+ */
78
+ export function validateDeploymentInputs(inputs, requiredFields = ['customer', 'environment', 'domainName', 'cloudflareToken']) {
79
+ const result = {
80
+ valid: true,
81
+ errors: []
82
+ };
83
+
84
+ // Handle null/undefined inputs
85
+ if (!inputs) {
86
+ result.errors.push('Input object is required');
87
+ result.valid = false;
88
+ return result;
89
+ }
90
+ requiredFields.forEach(field => {
91
+ if (!inputs[field]) {
92
+ result.errors.push(`${field} is required`);
93
+ result.valid = false;
94
+ }
95
+ });
96
+ return result;
97
+ }
@@ -118,6 +118,16 @@ export const ResourceFormatters = {
118
118
  */
119
119
  configKey(camelCase) {
120
120
  return NameFormatters.toKebabCase(camelCase);
121
+ },
122
+ /**
123
+ * Format package name for NPM
124
+ * Example: 'my-service' → 'my-service'
125
+ */
126
+ packageName(serviceName) {
127
+ if (!serviceName) return '';
128
+ // For NPM packages, just use the service name as-is
129
+ // Scoped packages would be handled separately (e.g., @company/my-service)
130
+ return serviceName;
121
131
  }
122
132
  };
123
133
 
@@ -16,4 +16,16 @@ export { GracefulShutdownManager, getShutdownManager, initializeGracefulShutdown
16
16
  export { executeWithRateLimit, queueRequest, getRateLimitStatus, clearQueues, RATE_LIMITS } from './rate-limiter.js';
17
17
 
18
18
  // Unified error handling module (Phase 3.2.3d - consolidated from 5 sources)
19
- export { default as ErrorHandler, createErrorResponse, createContextualError, createErrorHandler } from './ErrorHandler.js';
19
+ export { default as ErrorHandler, createErrorResponse, createContextualError, createErrorHandler } from './ErrorHandler.js';
20
+
21
+ // Progress display utilities
22
+ export { showProgressSpinner, showProgressWithSpinner } from './progress-spinner.js';
23
+
24
+ // Deployment validation
25
+ export { validateDeploymentPrerequisites, validateDeploymentInputs } from './deployment-validator.js';
26
+
27
+ // Sensitive information redaction
28
+ export { redactSensitiveInfo, redactSensitiveObject } from './sensitive-redactor.js';
29
+
30
+ // Configuration loading
31
+ export { ConfigLoader } from './config-loader.js';
@@ -98,6 +98,7 @@ export function showProgress(message, steps = ['⏳', '⚡', '✅']) {
98
98
 
99
99
  /**
100
100
  * Ask for sensitive input (like API tokens) with hidden input
101
+ * Supports pasting with Ctrl+V or right-click paste
101
102
  */
102
103
  export function askPassword(question) {
103
104
  return new Promise(resolve => {
@@ -107,26 +108,41 @@ export function askPassword(question) {
107
108
  // Hide input for sensitive data
108
109
  process.stdin.setRawMode(true);
109
110
  process.stdin.resume();
111
+ process.stdin.setEncoding('utf8');
110
112
  let password = '';
111
- const onData = char => {
112
- const charCode = char[0];
113
- if (charCode === 13) {
114
- // Enter key
115
- process.stdin.setRawMode(false);
116
- process.stdin.pause();
117
- process.stdin.removeListener('data', onData);
118
- process.stdout.write('\n');
119
- resolve(password);
120
- } else if (charCode === 127 || charCode === 8) {
121
- // Backspace
122
- if (password.length > 0) {
123
- password = password.slice(0, -1);
124
- process.stdout.write('\b \b');
113
+ const onData = chunk => {
114
+ // Handle multi-character input (paste operations)
115
+ for (let i = 0; i < chunk.length; i++) {
116
+ const char = chunk[i];
117
+ const charCode = char.charCodeAt(0);
118
+ if (charCode === 13 || charCode === 10) {
119
+ // Enter key (CR or LF)
120
+ process.stdin.setRawMode(false);
121
+ process.stdin.pause();
122
+ process.stdin.removeListener('data', onData);
123
+ process.stdout.write('\n');
124
+ resolve(password);
125
+ return;
126
+ } else if (charCode === 127 || charCode === 8) {
127
+ // Backspace
128
+ if (password.length > 0) {
129
+ password = password.slice(0, -1);
130
+ process.stdout.write('\b \b');
131
+ }
132
+ } else if (charCode === 3) {
133
+ // Ctrl+C
134
+ process.stdin.setRawMode(false);
135
+ process.stdin.pause();
136
+ process.stdin.removeListener('data', onData);
137
+ process.stdout.write('\n');
138
+ console.log('\nOperation cancelled');
139
+ process.exit(0);
140
+ } else if (charCode >= 32 && charCode <= 126) {
141
+ // Printable characters
142
+ password += char;
143
+ process.stdout.write('*');
125
144
  }
126
- } else if (charCode >= 32 && charCode <= 126) {
127
- // Printable characters
128
- password += char.toString();
129
- process.stdout.write('*');
145
+ // Ignore other control characters (allows paste to work)
130
146
  }
131
147
  };
132
148
  process.stdin.on('data', onData);
@@ -71,11 +71,11 @@ export class ProgressManager {
71
71
  * @returns {ProgressManager} This instance for chaining
72
72
  */
73
73
  nextStep(stepName = null) {
74
+ this.currentStep++;
75
+ this.stepStartTime = Date.now();
74
76
  if (this.quiet) {
75
77
  return this;
76
78
  }
77
- this.currentStep++;
78
- this.stepStartTime = Date.now();
79
79
  const name = stepName || this.steps[this.currentStep - 1] || `Step ${this.currentStep}`;
80
80
  const progress = this.totalSteps > 0 ? Math.round(this.currentStep / this.totalSteps * 100) : 0;
81
81
  if (this.output) {