@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.
- package/CHANGELOG.md +17 -0
- package/README.md +283 -1
- package/dist/{bin → cli}/clodo-service.js +47 -15
- package/dist/cli/commands/assess.js +183 -0
- package/dist/{bin → cli}/commands/create.js +5 -5
- package/dist/{bin → cli}/commands/deploy.js +122 -90
- package/dist/{bin → cli}/commands/diagnose.js +5 -5
- package/dist/cli/commands/helpers/deployment-ui.js +138 -0
- package/dist/cli/commands/helpers/deployment-verification.js +250 -0
- package/dist/cli/commands/helpers/error-recovery.js +80 -0
- package/dist/cli/commands/helpers/resource-detection.js +113 -0
- package/dist/{bin → cli}/commands/helpers.js +0 -28
- package/dist/cli/commands/init-config.js +57 -0
- package/dist/{bin → cli}/commands/update.js +5 -5
- package/dist/{bin → cli}/commands/validate.js +5 -5
- package/dist/cli/security-cli.js +118 -0
- package/dist/config/FeatureManager.js +6 -0
- package/dist/config/clodo-create.example.json +26 -0
- package/dist/config/clodo-deploy.example.json +41 -0
- package/dist/config/clodo-update.example.json +46 -0
- package/dist/config/clodo-validate.example.json +41 -0
- package/dist/config/customers/template/development.env.template +37 -0
- package/dist/config/customers/template/production.env.template +39 -0
- package/dist/config/customers/template/staging.env.template +37 -0
- package/dist/config/customers.js +28 -26
- package/dist/config/domain-examples/README.md +464 -0
- package/dist/config/domain-examples/environment-mapped.json +168 -0
- package/dist/config/domain-examples/multi-domain.json +144 -0
- package/dist/config/domain-examples/single-domain.json +50 -0
- package/dist/config/examples +12 -0
- package/dist/config/features.js +61 -0
- package/dist/config/staging-deployment.json +60 -0
- package/dist/config/validation-config.json +347 -0
- package/dist/deployment/wrangler-deployer.js +1 -1
- package/dist/{bin → lib}/deployment/modules/DeploymentOrchestrator.js +2 -2
- package/dist/{bin → lib}/deployment/modules/EnvironmentManager.js +2 -2
- package/dist/lib/deployment/orchestration/EnterpriseOrchestrator.js +21 -0
- package/dist/lib/shared/cache/configuration-cache.js +82 -0
- package/dist/{bin → lib}/shared/cloudflare/domain-discovery.js +1 -1
- package/dist/{bin → lib}/shared/cloudflare/domain-manager.js +1 -1
- package/dist/{bin → lib}/shared/cloudflare/index.js +1 -1
- package/dist/{bin → lib}/shared/cloudflare/ops.js +10 -8
- package/dist/{bin → lib}/shared/config/ConfigurationManager.js +23 -1
- package/dist/{bin → lib}/shared/config/command-config-manager.js +19 -3
- package/dist/{bin → lib}/shared/config/index.js +1 -1
- package/dist/{bin → lib}/shared/deployment/credential-collector.js +30 -7
- package/dist/lib/shared/deployment/index.js +10 -0
- package/dist/lib/shared/deployment/rollback-manager.js +7 -0
- package/dist/lib/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/{bin → lib}/shared/deployment/validator.js +40 -10
- package/dist/lib/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/lib/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/lib/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/lib/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/lib/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/lib/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/lib/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/lib/shared/error-handling/error-classifier.js +46 -0
- package/dist/{bin → lib}/shared/monitoring/health-checker.js +129 -1
- package/dist/{bin → lib}/shared/monitoring/memory-manager.js +17 -6
- package/dist/{bin → lib}/shared/routing/domain-router.js +1 -1
- package/dist/lib/shared/utils/deployment-validator.js +97 -0
- package/dist/{bin → lib}/shared/utils/formatters.js +10 -0
- package/dist/{bin → lib}/shared/utils/index.js +13 -1
- package/dist/{bin → lib}/shared/utils/interactive-prompts.js +34 -18
- package/dist/{bin → lib}/shared/utils/progress-manager.js +2 -2
- package/dist/lib/shared/utils/progress-spinner.js +53 -0
- package/dist/lib/shared/utils/sensitive-redactor.js +91 -0
- package/dist/{bin → lib}/shared/validation/ValidationRegistry.js +1 -1
- package/dist/migration/MigrationAdapters.js +50 -4
- package/dist/orchestration/cross-domain-coordinator.js +5 -5
- package/dist/orchestration/multi-domain-orchestrator.js +63 -22
- package/dist/security/index.js +2 -2
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/service-management/ConfirmationEngine.js +1 -1
- package/dist/service-management/ErrorTracker.js +1 -1
- package/dist/service-management/InputCollector.js +1 -1
- package/dist/service-management/ServiceCreator.js +11 -255
- package/dist/service-management/ServiceOrchestrator.js +0 -2
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +4 -4
- package/dist/service-management/index.js +1 -1
- package/dist/utils/cloudflare/ops.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/file-manager.js +1 -1
- package/dist/utils/formatters.js +1 -1
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/dist/utils/logger.js +1 -1
- package/dist/version/VersionDetector.js +99 -9
- package/dist/worker/integration.js +1 -1
- package/package.json +10 -10
- package/dist/bin/clodo-service-old.js +0 -868
- package/dist/bin/clodo-service-test.js +0 -10
- package/dist/bin/commands/assess.js +0 -91
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/deployment/enterprise-deploy.js +0 -877
- package/dist/bin/deployment/master-deploy.js +0 -1376
- package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/security/security-cli.js +0 -108
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/bin/shared/deployment/index.js +0 -10
- package/dist/bin/shared/deployment/rollback-manager.js +0 -523
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/service-management/ServiceInitializer.js +0 -453
- /package/dist/{bin → lib}/database/deployment-db-manager.js +0 -0
- /package/dist/{bin → lib}/database/wrangler-d1-manager.js +0 -0
- /package/dist/{bin → lib}/deployment/modules/DeploymentConfiguration.js +0 -0
- /package/dist/{bin → lib}/deployment/modules/MonitoringIntegration.js +0 -0
- /package/dist/{bin → lib}/deployment/modules/ValidationManager.js +0 -0
- /package/dist/{bin → lib}/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -0
- /package/dist/{bin → lib}/deployment/orchestration/PortfolioOrchestrator.js +0 -0
- /package/dist/{bin → lib}/deployment/orchestration/SingleServiceOrchestrator.js +0 -0
- /package/dist/{bin → lib}/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -0
- /package/dist/{bin → lib}/shared/config/cache.js +0 -0
- /package/dist/{bin → lib}/shared/config/cloudflare-service-validator.js +0 -0
- /package/dist/{bin → lib}/shared/config/manager.js +0 -0
- /package/dist/{bin → lib}/shared/config/manifest-loader.js +0 -0
- /package/dist/{bin → lib}/shared/database/connection-manager.js +0 -0
- /package/dist/{bin → lib}/shared/database/index.js +0 -0
- /package/dist/{bin → lib}/shared/database/orchestrator.js +0 -0
- /package/dist/{bin → lib}/shared/deployment/auditor.js +0 -0
- /package/dist/{bin → lib}/shared/index.js +0 -0
- /package/dist/{bin → lib}/shared/logging/Logger.js +0 -0
- /package/dist/{bin → lib}/shared/monitoring/index.js +0 -0
- /package/dist/{bin → lib}/shared/monitoring/production-monitor.js +0 -0
- /package/dist/{bin → lib}/shared/production-tester/api-tester.js +0 -0
- /package/dist/{bin → lib}/shared/production-tester/auth-tester.js +0 -0
- /package/dist/{bin → lib}/shared/production-tester/core.js +0 -0
- /package/dist/{bin → lib}/shared/production-tester/database-tester.js +0 -0
- /package/dist/{bin → lib}/shared/production-tester/index.js +0 -0
- /package/dist/{bin → lib}/shared/production-tester/load-tester.js +0 -0
- /package/dist/{bin → lib}/shared/production-tester/performance-tester.js +0 -0
- /package/dist/{bin → lib}/shared/security/api-token-manager.js +0 -0
- /package/dist/{bin → lib}/shared/security/index.js +0 -0
- /package/dist/{bin → lib}/shared/security/secret-generator.js +0 -0
- /package/dist/{bin → lib}/shared/security/secure-token-manager.js +0 -0
- /package/dist/{bin → lib}/shared/utils/ErrorHandler.js +0 -0
- /package/dist/{bin → lib}/shared/utils/cli-options.js +0 -0
- /package/dist/{bin → lib}/shared/utils/config-loader.js +0 -0
- /package/dist/{bin → lib}/shared/utils/error-recovery.js +0 -0
- /package/dist/{bin → lib}/shared/utils/file-manager.js +0 -0
- /package/dist/{bin → lib}/shared/utils/graceful-shutdown-manager.js +0 -0
- /package/dist/{bin → lib}/shared/utils/interactive-utils.js +0 -0
- /package/dist/{bin → lib}/shared/utils/output-formatter.js +0 -0
- /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
|
|
10
|
-
import { DeploymentDatabaseManager } from '
|
|
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
|
|
7
|
-
import { CrossDomainCoordinator } from
|
|
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/
|
|
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
|
|
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);
|
|
@@ -44,15 +44,17 @@ const dbManager = new DatabaseConnectionManager({
|
|
|
44
44
|
maxPoolSize: 5
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
// Memory management
|
|
48
|
-
|
|
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.
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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 '
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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,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
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|