@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
@@ -6,8 +6,8 @@
6
6
 
7
7
  import { readFileSync, writeFileSync } from 'fs';
8
8
  import { deploySecret, runMigrations, checkHealth } from '../../shared/cloudflare/ops.js';
9
- import { WranglerDeployer } from "../../deployment/wrangler-deployer.js";
10
- import { DeploymentDatabaseManager } from '../../database/deployment-db-manager.js';
9
+ import { WranglerDeployer } from '../deployment/wrangler-deployer.js';
10
+ import { DeploymentDatabaseManager } from '../database/deployment-db-manager.js';
11
11
  import { DeploymentConfiguration } from './DeploymentConfiguration.js';
12
12
 
13
13
  /**
@@ -3,8 +3,8 @@
3
3
  * Handles environment configuration, domain mapping, deployment mode selection, and cross-domain coordination
4
4
  */
5
5
 
6
- import { MultiDomainOrchestrator } from "../../../orchestration/multi-domain-orchestrator.js";
7
- import { CrossDomainCoordinator } from "../../../orchestration/cross-domain-coordinator.js";
6
+ import { MultiDomainOrchestrator } from '../../orchestration/multi-domain-orchestrator.js';
7
+ import { CrossDomainCoordinator } from '../../orchestration/cross-domain-coordinator.js';
8
8
  import { DomainDiscovery } from '../../shared/cloudflare/domain-discovery.js';
9
9
  import { askChoice, askUser, askYesNo, DeploymentInteractiveUtils } from '../../shared/utils/interactive-utils.js';
10
10
  export class EnvironmentManager {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Enterprise Orchestrator - STUB
3
+ *
4
+ * This is a stub implementation for the basic clodo-framework package.
5
+ * Enterprise features are only available in the enterprise package.
6
+ *
7
+ * To use enterprise deployment features, please install the enterprise package:
8
+ * npm install @tamyla/clodo-framework-enterprise
9
+ */
10
+
11
+ import { BaseDeploymentOrchestrator } from './BaseDeploymentOrchestrator.js';
12
+ export class EnterpriseOrchestrator extends BaseDeploymentOrchestrator {
13
+ /**
14
+ * Constructor for enterprise orchestrator - STUB
15
+ * @throws {Error} Enterprise features not available in basic package
16
+ */
17
+ constructor(options = {}) {
18
+ super(options);
19
+ throw new Error('Enterprise deployment features are not available in the basic clodo-framework package.\n' + 'To use enterprise deployment capabilities (multi-region, high-availability, compliance, etc.),\n' + 'please install the enterprise package: npm install @tamyla/clodo-framework-enterprise');
20
+ }
21
+ }
@@ -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
+ }
@@ -9,7 +9,7 @@
9
9
  import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
10
10
  import { join, dirname } from 'path';
11
11
  import { fileURLToPath } from 'url';
12
- import { NameFormatters, UrlFormatters, ResourceFormatters, EnvironmentFormatters } from '../utils/Formatters.js';
12
+ import { NameFormatters, UrlFormatters, ResourceFormatters, EnvironmentFormatters } from '../utils/formatters.js';
13
13
  const __filename = fileURLToPath(import.meta.url);
14
14
  const __dirname = dirname(__filename);
15
15
 
@@ -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;
@@ -240,7 +242,7 @@ export async function listDatabases(options = {}) {
240
242
  if (apiToken && accountId) {
241
243
  const {
242
244
  CloudflareAPI
243
- } = await import('../../../utils/cloudflare/api.js');
245
+ } = await import('../../utils/cloudflare/api.js');
244
246
  const cf = new CloudflareAPI(apiToken);
245
247
  return await cf.listD1Databases(accountId);
246
248
  }
@@ -265,7 +267,7 @@ export async function databaseExists(databaseName, options = {}) {
265
267
  if (apiToken && accountId) {
266
268
  const {
267
269
  CloudflareAPI
268
- } = await import('../../../utils/cloudflare/api.js');
270
+ } = await import('../../utils/cloudflare/api.js');
269
271
  const cf = new CloudflareAPI(apiToken);
270
272
  return await cf.d1DatabaseExists(accountId, databaseName);
271
273
  }
@@ -288,7 +290,7 @@ export async function createDatabase(name, options = {}) {
288
290
  if (apiToken && accountId) {
289
291
  const {
290
292
  CloudflareAPI
291
- } = await import('../../../utils/cloudflare/api.js');
293
+ } = await import('../../utils/cloudflare/api.js');
292
294
  const cf = new CloudflareAPI(apiToken);
293
295
  const result = await cf.createD1Database(accountId, name);
294
296
  return result.uuid; // Return UUID to match CLI behavior
@@ -403,7 +405,7 @@ export async function getDatabaseId(databaseName, options = {}) {
403
405
  if (apiToken && accountId) {
404
406
  const {
405
407
  CloudflareAPI
406
- } = await import('../../../utils/cloudflare/api.js');
408
+ } = await import('../../utils/cloudflare/api.js');
407
409
  const cf = new CloudflareAPI(apiToken);
408
410
  const db = await cf.getD1Database(accountId, databaseName);
409
411
  return db?.uuid || 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';
@@ -11,7 +11,7 @@
11
11
  import chalk from 'chalk';
12
12
  import { askUser, askPassword, askChoice, closePrompts } from '../utils/interactive-prompts.js';
13
13
  import { ApiTokenManager } from '../security/api-token-manager.js';
14
- import { CloudflareAPI } from "../../../utils/cloudflare/api.js";
14
+ import { CloudflareAPI } from '../../utils/cloudflare/api.js';
15
15
  export class DeploymentCredentialCollector {
16
16
  constructor(options = {}) {
17
17
  this.servicePath = options.servicePath || '.';
@@ -32,13 +32,16 @@ export class DeploymentCredentialCollector {
32
32
  const startCredentials = {
33
33
  token: options.token || process.env.CLOUDFLARE_API_TOKEN || null,
34
34
  accountId: options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID || null,
35
- zoneId: options.zoneId || process.env.CLOUDFLARE_ZONE_ID || null
35
+ zoneId: options.zoneId || process.env.CLOUDFLARE_ZONE_ID || null,
36
+ zoneName: null // Will be populated when fetching zone
36
37
  };
37
38
 
38
39
  // All credentials provided - quick path
39
40
  if (startCredentials.token && startCredentials.accountId && startCredentials.zoneId) {
40
41
  if (!this.quiet) {
41
- console.log(chalk.green('\n✅ All credentials provided via flags or environment variables'));
42
+ const tokenSource = options.token ? '--token flag' : 'environment variable';
43
+ console.log(chalk.blue(`\nℹ️ Using credentials from: ${tokenSource}`));
44
+ console.log(chalk.green('✅ All credentials provided via flags or environment variables'));
42
45
  }
43
46
  return startCredentials;
44
47
  }
@@ -65,6 +68,7 @@ export class DeploymentCredentialCollector {
65
68
  }
66
69
  let accountId = startCredentials.accountId;
67
70
  let zoneId = startCredentials.zoneId;
71
+ let zoneName = startCredentials.zoneName;
68
72
  try {
69
73
  const cloudflareAPI = new CloudflareAPI(token);
70
74
 
@@ -86,7 +90,9 @@ export class DeploymentCredentialCollector {
86
90
 
87
91
  // If zone ID not provided, fetch from Cloudflare
88
92
  if (!zoneId) {
89
- zoneId = await this.fetchZoneId(cloudflareAPI);
93
+ const zoneInfo = await this.fetchZoneId(cloudflareAPI);
94
+ zoneId = zoneInfo.id;
95
+ zoneName = zoneInfo.name;
90
96
  }
91
97
  } catch (error) {
92
98
  console.error(chalk.red(`\n❌ Credential validation failed:`));
@@ -96,10 +102,21 @@ export class DeploymentCredentialCollector {
96
102
  if (!this.quiet) {
97
103
  console.log(chalk.green('\n✅ All credentials collected and validated\n'));
98
104
  }
105
+
106
+ // Return structured Cloudflare settings object
107
+ // This encapsulates all zone-specific configuration for multi-domain deployments
99
108
  return {
100
109
  token,
101
110
  accountId,
102
- zoneId
111
+ zoneId,
112
+ zoneName,
113
+ // Convenience: cloudflareSettings object for passing to orchestrators
114
+ cloudflareSettings: {
115
+ token,
116
+ accountId,
117
+ zoneId,
118
+ zoneName
119
+ }
103
120
  };
104
121
  }
105
122
 
@@ -197,7 +214,10 @@ export class DeploymentCredentialCollector {
197
214
  if (!this.quiet) {
198
215
  console.log(chalk.green(`✅ Found domain: ${zones[0].name}\n`));
199
216
  }
200
- return zones[0].id;
217
+ return {
218
+ id: zones[0].id,
219
+ name: zones[0].name
220
+ };
201
221
  }
202
222
 
203
223
  // Multiple zones - let user choose
@@ -210,7 +230,10 @@ export class DeploymentCredentialCollector {
210
230
  if (!this.quiet) {
211
231
  console.log(chalk.green(`✅ Selected: ${selectedZone.name}\n`));
212
232
  }
213
- return selectedZone.id;
233
+ return {
234
+ id: selectedZone.id,
235
+ name: selectedZone.name
236
+ };
214
237
  } catch (error) {
215
238
  console.error(chalk.red('❌ Failed to fetch domains:'));
216
239
  console.error(chalk.yellow(error.message));
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Deployment Module
3
+ * Exports all deployment-related orchestrators, validators, and managers
4
+ */
5
+
6
+ export { DeploymentValidator } from './validator.js';
7
+ export { MultiDomainOrchestrator } from '../orchestration/multi-domain-orchestrator.js';
8
+ export { CrossDomainCoordinator } from '../orchestration/cross-domain-coordinator.js';
9
+ export { DeploymentAuditor } from './auditor.js';
10
+ export { RollbackManager } from './rollback-manager.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Rollback Manager - Deployment Module Re-export
3
+ *
4
+ * Re-exports the RollbackManager for deployment operations
5
+ */
6
+
7
+ export { RollbackManager } from '../../deployment/rollback-manager.js';
@@ -0,0 +1,177 @@
1
+ /**
2
+ * D1 Error Recovery Workflow Module
3
+ *
4
+ * Provides sophisticated D1 database binding error detection and recovery.
5
+ * Extracted from enterprise-deployment/master-deploy.js for modularity.
6
+ *
7
+ * @module d1-error-recovery
8
+ */
9
+
10
+ /**
11
+ * D1 Error Recovery Manager
12
+ * Handles D1 database binding errors during deployment with automatic recovery
13
+ */
14
+ export class D1ErrorRecoveryManager {
15
+ /**
16
+ * @param {Object} options - Configuration options
17
+ * @param {Array} options.rollbackActions - Array to track rollback actions
18
+ * @param {Object} options.wranglerDeployer - Optional WranglerDeployer instance for testing
19
+ */
20
+ constructor(options = {}) {
21
+ this.rollbackActions = options.rollbackActions || [];
22
+ this.wranglerDeployer = options.wranglerDeployer;
23
+ }
24
+
25
+ /**
26
+ * Handle D1 database binding errors during deployment
27
+ *
28
+ * @param {Error} error - Deployment error to analyze
29
+ * @param {Object} config - Deployment configuration
30
+ * @param {string} config.environment - Target environment
31
+ * @param {string} [config.configPath='wrangler.toml'] - Path to wrangler config
32
+ * @param {string} [config.cwd] - Working directory
33
+ * @returns {Promise<Object>} Recovery result { handled, retry, action, message }
34
+ */
35
+ async handleD1BindingError(error, config = {}) {
36
+ try {
37
+ // Use provided WranglerDeployer or import dynamically
38
+ let deployer;
39
+ if (this.wranglerDeployer) {
40
+ deployer = this.wranglerDeployer;
41
+ } else {
42
+ // Import WranglerDeployer for D1 error handling
43
+ const {
44
+ WranglerDeployer
45
+ } = await import('../../deployment/wrangler-deployer.js');
46
+ deployer = new WranglerDeployer({
47
+ cwd: config.cwd || process.cwd(),
48
+ environment: config.environment
49
+ });
50
+ }
51
+
52
+ // Attempt D1 error recovery
53
+ const recoveryResult = await deployer.handleD1BindingError(error, {
54
+ configPath: config.configPath || 'wrangler.toml',
55
+ environment: config.environment
56
+ });
57
+ if (recoveryResult.handled) {
58
+ console.log(` 🔧 D1 Error Recovery: ${recoveryResult.action}`);
59
+
60
+ // Handle configuration backup if created
61
+ if (recoveryResult.backupPath) {
62
+ console.log(` 📁 Configuration backup: ${recoveryResult.backupPath}`);
63
+
64
+ // Add rollback action to restore backup
65
+ this.rollbackActions.unshift({
66
+ type: 'restore-wrangler-config',
67
+ backupPath: recoveryResult.backupPath,
68
+ description: 'Restore wrangler.toml backup after D1 recovery'
69
+ });
70
+ }
71
+
72
+ // Determine if deployment should be retried
73
+ const shouldRetry = this.shouldRetryAfterRecovery(recoveryResult.action);
74
+ return {
75
+ handled: true,
76
+ retry: shouldRetry,
77
+ action: recoveryResult.action,
78
+ message: this.getRecoveryMessage(recoveryResult),
79
+ databaseName: recoveryResult.databaseName,
80
+ databaseId: recoveryResult.databaseId
81
+ };
82
+ }
83
+
84
+ // Error was not a D1 binding error
85
+ return {
86
+ handled: false,
87
+ retry: false
88
+ };
89
+ } catch (recoveryError) {
90
+ console.log(` ⚠️ D1 error recovery failed: ${recoveryError.message}`);
91
+ return {
92
+ handled: true,
93
+ retry: false,
94
+ message: `D1 error recovery failed: ${recoveryError.message}`
95
+ };
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Determine if deployment should be retried after recovery
101
+ *
102
+ * @param {string} action - Recovery action taken
103
+ * @returns {boolean} True if deployment should retry
104
+ */
105
+ shouldRetryAfterRecovery(action) {
106
+ const retryableActions = ['created_and_configured', 'database_selected_and_configured', 'binding_updated'];
107
+ return retryableActions.includes(action);
108
+ }
109
+
110
+ /**
111
+ * Get user-friendly message for recovery result
112
+ *
113
+ * @param {Object} recoveryResult - Recovery result from WranglerDeployer
114
+ * @returns {string} User-friendly message
115
+ */
116
+ getRecoveryMessage(recoveryResult) {
117
+ const messages = {
118
+ 'created_and_configured': `Created D1 database '${recoveryResult.databaseName}' and updated configuration`,
119
+ 'database_selected_and_configured': `Selected existing database '${recoveryResult.databaseName}' and updated configuration`,
120
+ 'binding_updated': `Updated D1 database binding configuration`,
121
+ 'cancelled': 'D1 error recovery was cancelled by user',
122
+ 'creation_failed': `Failed to create D1 database: ${recoveryResult.error}`,
123
+ 'selection_failed': `Failed to update database selection: ${recoveryResult.error}`,
124
+ 'no_databases_available': 'No D1 databases available in account',
125
+ 'manual': 'User chose to resolve D1 issues manually',
126
+ 'not_d1_error': 'Error is not related to D1 database bindings'
127
+ };
128
+ return messages[recoveryResult.action] || `D1 recovery completed with action: ${recoveryResult.action}`;
129
+ }
130
+
131
+ /**
132
+ * Deploy worker with automatic D1 error recovery
133
+ *
134
+ * @param {Function} deployFunction - Function to deploy worker
135
+ * @param {Object} config - Deployment configuration
136
+ * @returns {Promise<void>}
137
+ */
138
+ async deployWithRecovery(deployFunction, config = {}) {
139
+ try {
140
+ await deployFunction();
141
+ console.log(' ✅ Worker deployed successfully');
142
+ } catch (error) {
143
+ // Attempt D1 error recovery
144
+ const recoveryResult = await this.handleD1BindingError(error, config);
145
+ if (recoveryResult.handled && recoveryResult.retry) {
146
+ console.log(' 🔄 Retrying deployment after D1 error recovery...');
147
+ try {
148
+ await deployFunction();
149
+ console.log(' ✅ Worker deployed successfully after D1 recovery');
150
+ } catch (retryError) {
151
+ console.log(' ❌ Deployment failed even after D1 recovery');
152
+ throw retryError;
153
+ }
154
+ } else if (recoveryResult.handled) {
155
+ // Error was handled but no retry requested
156
+ throw new Error(`Deployment failed: ${recoveryResult.message || error.message}`);
157
+ } else {
158
+ // Not a D1 error, rethrow original
159
+ throw error;
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Get recovery statistics
166
+ *
167
+ * @returns {Object} Recovery statistics
168
+ */
169
+ getStatistics() {
170
+ const d1RollbackActions = this.rollbackActions.filter(action => action.type === 'restore-wrangler-config');
171
+ return {
172
+ totalRecoveries: d1RollbackActions.length,
173
+ hasBackups: d1RollbackActions.length > 0,
174
+ latestBackup: d1RollbackActions[0]?.backupPath
175
+ };
176
+ }
177
+ }
@@ -10,8 +10,15 @@ import { access, readFile } from 'fs/promises';
10
10
  import { readFileSync } from 'fs';
11
11
  import { promisify } from 'util';
12
12
  import { exec } from 'child_process';
13
- import { join } from 'path';
13
+ import { join, dirname } from 'path';
14
+ import { fileURLToPath } from 'url';
14
15
  import { getCommandConfig } from '../config/command-config-manager.js';
16
+
17
+ // Get the directory of this module (framework's bin directory)
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+ // Navigate up to framework root, then to config directory
21
+ const FRAMEWORK_CONFIG_PATH = join(__dirname, '../../config/validation-config.json');
15
22
  const execAsync = promisify(exec);
16
23
 
17
24
  /**
@@ -58,6 +65,7 @@ export class DeploymentValidator {
58
65
  loadValidationConfig() {
59
66
  const configPath = join(process.cwd(), 'validation-config.json');
60
67
  try {
68
+ // First try to load from service directory
61
69
  const configData = readFileSync(configPath, 'utf-8');
62
70
  const userConfig = JSON.parse(configData);
63
71
 
@@ -85,15 +93,37 @@ export class DeploymentValidator {
85
93
  console.log('📋 Loaded validation config from validation-config.json');
86
94
  return defaultConfig;
87
95
  } catch (error) {
88
- // Fall back to defaults if file doesn't exist or is invalid
89
- console.log('📋 Using default validation config (validation-config.json not found or invalid)');
90
- return {
91
- requiredCommands: Object.keys(this.cmdConfig?.config?.requiredCommands || {}) || ['npx', 'node', 'npm', 'wrangler'],
92
- requiredFiles: ['package.json', 'wrangler.toml'],
93
- requiredEnvironmentVars: [],
94
- networkEndpoints: ['https://api.cloudflare.com', 'https://registry.npmjs.org'],
95
- expectedEndpoints: this.getExpectedEndpoints()
96
- };
96
+ // If service config not found, try framework's internal config
97
+ try {
98
+ const frameworkConfigData = readFileSync(FRAMEWORK_CONFIG_PATH, 'utf-8');
99
+ const frameworkConfig = JSON.parse(frameworkConfigData);
100
+
101
+ // Extract relevant parts for validation
102
+ const defaultConfig = {
103
+ requiredCommands: Object.values(frameworkConfig.commands?.required || {}) || ['npx', 'node', 'npm', 'wrangler'],
104
+ requiredFiles: ['package.json', 'wrangler.toml'],
105
+ requiredEnvironmentVars: [],
106
+ networkEndpoints: Object.values(frameworkConfig.networking?.endpoints || {}),
107
+ expectedEndpoints: this.getExpectedEndpoints(),
108
+ // Include timing and other framework config
109
+ timing: frameworkConfig.timing,
110
+ networking: frameworkConfig.networking,
111
+ caching: frameworkConfig.caching,
112
+ environments: frameworkConfig.environments
113
+ };
114
+ console.log('📋 Loaded validation config from framework defaults');
115
+ return defaultConfig;
116
+ } catch (frameworkError) {
117
+ // If neither exists, use hardcoded defaults
118
+ console.log('📋 Using minimal validation config (no config files found)');
119
+ return {
120
+ requiredCommands: Object.keys(this.cmdConfig?.config?.requiredCommands || {}) || ['npx', 'node', 'npm', 'wrangler'],
121
+ requiredFiles: ['package.json', 'wrangler.toml'],
122
+ requiredEnvironmentVars: [],
123
+ networkEndpoints: ['https://api.cloudflare.com', 'https://registry.npmjs.org'],
124
+ expectedEndpoints: this.getExpectedEndpoints()
125
+ };
126
+ }
97
127
  }
98
128
  }
99
129