@tamyla/clodo-framework 3.1.21 → 3.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +53 -0
- package/dist/bin/clodo-service.js +47 -15
- package/dist/bin/commands/deploy.js +115 -83
- package/dist/bin/commands/helpers/deployment-ui.js +138 -0
- package/dist/bin/commands/helpers/deployment-verification.js +251 -0
- package/dist/bin/commands/helpers/error-recovery.js +80 -0
- package/dist/bin/commands/helpers/resource-detection.js +113 -0
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/shared/cache/configuration-cache.js +82 -0
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/cloudflare/index.js +1 -1
- package/dist/bin/shared/cloudflare/ops.js +6 -4
- package/dist/bin/shared/config/ConfigurationManager.js +23 -1
- package/dist/bin/shared/config/command-config-manager.js +19 -3
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/credential-collector.js +30 -7
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/deployment/rollback-manager.js +4 -520
- package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/bin/shared/deployment/validator.js +40 -10
- package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/bin/shared/error-handling/error-classifier.js +46 -0
- package/dist/bin/shared/monitoring/health-checker.js +129 -1
- package/dist/bin/shared/monitoring/memory-manager.js +17 -6
- package/dist/bin/shared/routing/domain-router.js +1 -1
- package/dist/bin/shared/utils/deployment-validator.js +97 -0
- package/dist/bin/shared/utils/formatters.js +10 -0
- package/dist/bin/shared/utils/index.js +13 -1
- package/dist/bin/shared/utils/interactive-prompts.js +34 -18
- package/dist/bin/shared/utils/progress-manager.js +2 -2
- package/dist/bin/shared/utils/progress-spinner.js +53 -0
- package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/package.json +16 -8
- 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/commands/create.js +0 -77
- package/dist/bin/commands/diagnose.js +0 -83
- package/dist/bin/commands/helpers.js +0 -138
- package/dist/bin/commands/update.js +0 -75
- package/dist/bin/database/deployment-db-manager.js +0 -423
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/database/wrangler-d1-manager.js +0 -685
- 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/modules/DeploymentConfiguration.js +0 -395
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
- package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
- package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
- package/dist/bin/deployment/modules/ValidationManager.js +0 -342
- package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/config/customers.js +0 -623
- package/dist/config/domains.js +0 -186
- package/dist/config/index.js +0 -6
- package/dist/database/database-orchestrator.js +0 -795
- package/dist/database/index.js +0 -4
- package/dist/deployment/index.js +0 -11
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/deployment/orchestration/index.js +0 -17
- package/dist/deployment/rollback-manager.js +0 -36
- package/dist/deployment/wrangler-deployer.js +0 -640
- package/dist/handlers/GenericRouteHandler.js +0 -532
- package/dist/migration/MigrationAdapters.js +0 -562
- package/dist/modules/ModuleManager.js +0 -668
- package/dist/modules/security.js +0 -96
- package/dist/orchestration/cross-domain-coordinator.js +0 -1083
- package/dist/orchestration/index.js +0 -5
- package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
- package/dist/orchestration/modules/DomainResolver.js +0 -198
- package/dist/orchestration/modules/StateManager.js +0 -332
- package/dist/orchestration/multi-domain-orchestrator.js +0 -724
- package/dist/routing/EnhancedRouter.js +0 -158
- package/dist/schema/SchemaManager.js +0 -778
- package/dist/service-management/ConfirmationEngine.js +0 -412
- package/dist/service-management/ErrorTracker.js +0 -299
- package/dist/service-management/GenerationEngine.js +0 -447
- package/dist/service-management/InputCollector.js +0 -619
- package/dist/service-management/ServiceCreator.js +0 -265
- package/dist/service-management/ServiceInitializer.js +0 -453
- package/dist/service-management/ServiceOrchestrator.js +0 -633
- package/dist/service-management/generators/BaseGenerator.js +0 -233
- package/dist/service-management/generators/GeneratorRegistry.js +0 -254
- package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
- package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
- package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
- package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
- package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
- package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
- package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
- package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
- package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
- package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
- package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
- package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
- package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
- package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
- package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
- package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
- package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
- package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
- package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
- package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
- package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
- package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
- package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
- package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
- package/dist/service-management/generators/utils/FileWriter.js +0 -179
- package/dist/service-management/generators/utils/PathResolver.js +0 -157
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
- package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
- package/dist/service-management/generators/utils/index.js +0 -18
- package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
- package/dist/service-management/handlers/GenerationHandler.js +0 -80
- package/dist/service-management/handlers/InputHandler.js +0 -59
- package/dist/service-management/handlers/ValidationHandler.js +0 -203
- package/dist/service-management/index.js +0 -14
- package/dist/service-management/routing/DomainRouteMapper.js +0 -311
- package/dist/service-management/routing/RouteGenerator.js +0 -266
- package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
- package/dist/service-management/routing/index.js +0 -14
- package/dist/service-management/services/DirectoryStructureService.js +0 -56
- package/dist/service-management/services/GenerationCoordinator.js +0 -208
- package/dist/service-management/services/GeneratorRegistry.js +0 -174
- package/dist/services/GenericDataService.js +0 -501
- package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
- package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
- package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
- package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
- package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
- package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
- package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
- package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
- package/dist/ui-structures/reference/service-manifest-template.json +0 -342
- package/dist/version/VersionDetector.js +0 -723
- package/dist/worker/index.js +0 -4
- package/dist/worker/integration.js +0 -351
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Testing Workflow Module
|
|
3
|
+
*
|
|
4
|
+
* Provides reusable post-deployment testing workflows.
|
|
5
|
+
* Extracted from enterprise-deployment/master-deploy.js for modularity.
|
|
6
|
+
*
|
|
7
|
+
* @module interactive-testing-workflow
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { askYesNo } from '../utils/interactive-prompts.js';
|
|
11
|
+
import { checkHealth } from '../monitoring/health-checker.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Interactive Testing Workflow
|
|
15
|
+
* Handles post-deployment testing with user interaction
|
|
16
|
+
*/
|
|
17
|
+
export class InteractiveTestingWorkflow {
|
|
18
|
+
/**
|
|
19
|
+
* @param {Object} options - Configuration options
|
|
20
|
+
* @param {boolean} options.interactive - Enable interactive prompts
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.interactive = options.interactive !== false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Execute post-deployment testing
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} config - Deployment configuration
|
|
30
|
+
* @param {Object} options - Testing options
|
|
31
|
+
* @returns {Promise<Object>} Test results
|
|
32
|
+
*/
|
|
33
|
+
async executePostDeploymentTesting(config, options = {}) {
|
|
34
|
+
if (!config.deployment?.runTests && !options.force) {
|
|
35
|
+
console.log('\n⏭️ Skipping tests (as requested)');
|
|
36
|
+
return {
|
|
37
|
+
skipped: true
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
console.log('\n🧪 Post-deployment Testing');
|
|
41
|
+
console.log('==========================');
|
|
42
|
+
|
|
43
|
+
// Ask for confirmation if interactive
|
|
44
|
+
if (this.interactive && !options.force) {
|
|
45
|
+
const runTests = await askYesNo('Run comprehensive integration tests?', 'y');
|
|
46
|
+
if (!runTests) {
|
|
47
|
+
console.log(' ⏭️ Tests skipped by user');
|
|
48
|
+
return {
|
|
49
|
+
skipped: true,
|
|
50
|
+
userCancelled: true
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const results = {
|
|
55
|
+
health: null,
|
|
56
|
+
authentication: null,
|
|
57
|
+
overall: false
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Test health endpoint
|
|
61
|
+
results.health = await this.testHealthEndpoint(config);
|
|
62
|
+
|
|
63
|
+
// Test authentication
|
|
64
|
+
results.authentication = await this.testAuthentication(config);
|
|
65
|
+
|
|
66
|
+
// Determine overall success
|
|
67
|
+
results.overall = results.health?.success && results.authentication?.success;
|
|
68
|
+
console.log('\n✅ Basic tests completed');
|
|
69
|
+
return results;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Test health endpoint
|
|
74
|
+
*
|
|
75
|
+
* @param {Object} config - Configuration
|
|
76
|
+
* @returns {Promise<Object>} Health test result
|
|
77
|
+
*/
|
|
78
|
+
async testHealthEndpoint(config) {
|
|
79
|
+
console.log('\n🏥 Testing health endpoint...');
|
|
80
|
+
try {
|
|
81
|
+
const health = await checkHealth(config.worker.url);
|
|
82
|
+
const modelCount = health.framework?.models?.length || 0;
|
|
83
|
+
const routeCount = health.framework?.routes?.length || 0;
|
|
84
|
+
console.log(` ✅ Health OK: ${modelCount} models, ${routeCount} routes`);
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
status: health.status,
|
|
88
|
+
models: modelCount,
|
|
89
|
+
routes: routeCount,
|
|
90
|
+
framework: health.framework
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.log(` ⚠️ Health test failed: ${error.message}`);
|
|
94
|
+
return {
|
|
95
|
+
success: false,
|
|
96
|
+
error: error.message
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Test authentication endpoint
|
|
103
|
+
*
|
|
104
|
+
* @param {Object} config - Configuration
|
|
105
|
+
* @returns {Promise<Object>} Authentication test result
|
|
106
|
+
*/
|
|
107
|
+
async testAuthentication(config) {
|
|
108
|
+
console.log('\n🔐 Testing authentication...');
|
|
109
|
+
try {
|
|
110
|
+
const testEmail = `test-${Date.now()}@${config.domain}.com`;
|
|
111
|
+
console.log(` 📧 Testing magic link for: ${testEmail}`);
|
|
112
|
+
console.log(' ✅ Authentication system accessible');
|
|
113
|
+
return {
|
|
114
|
+
success: true,
|
|
115
|
+
testEmail,
|
|
116
|
+
accessible: true
|
|
117
|
+
};
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.log(` ⚠️ Auth test failed: ${error.message}`);
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
error: error.message
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Execute comprehensive testing with production tester
|
|
129
|
+
*
|
|
130
|
+
* @param {Object} config - Configuration
|
|
131
|
+
* @param {Object} productionTester - ProductionTester instance
|
|
132
|
+
* @returns {Promise<Object>} Comprehensive test results
|
|
133
|
+
*/
|
|
134
|
+
async executeComprehensiveTesting(config, productionTester) {
|
|
135
|
+
console.log('\n🧪 Comprehensive Integration Testing');
|
|
136
|
+
console.log('====================================');
|
|
137
|
+
if (this.interactive) {
|
|
138
|
+
const runComprehensive = await askYesNo('Run comprehensive integration tests with production tester?', 'y');
|
|
139
|
+
if (!runComprehensive) {
|
|
140
|
+
console.log(' ⏭️ Comprehensive tests skipped by user');
|
|
141
|
+
return {
|
|
142
|
+
skipped: true
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const testResults = await productionTester.runComprehensiveTests({
|
|
148
|
+
domain: config.domain,
|
|
149
|
+
environment: config.environment,
|
|
150
|
+
workerUrl: config.worker.url,
|
|
151
|
+
databaseName: config.database?.name
|
|
152
|
+
});
|
|
153
|
+
if (testResults.success) {
|
|
154
|
+
console.log(' ✅ All comprehensive tests passed');
|
|
155
|
+
} else {
|
|
156
|
+
console.log(` ⚠️ Some tests failed: ${testResults.failedCount} failures`);
|
|
157
|
+
}
|
|
158
|
+
return testResults;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.log(` ❌ Comprehensive testing failed: ${error.message}`);
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
error: error.message
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Execute smoke tests (quick validation)
|
|
170
|
+
*
|
|
171
|
+
* @param {Object} config - Configuration
|
|
172
|
+
* @returns {Promise<Object>} Smoke test results
|
|
173
|
+
*/
|
|
174
|
+
async executeSmokeTests(config) {
|
|
175
|
+
console.log('\n🔥 Smoke Tests (Quick Validation)');
|
|
176
|
+
console.log('==================================');
|
|
177
|
+
const results = {
|
|
178
|
+
endpoints: [],
|
|
179
|
+
overall: true
|
|
180
|
+
};
|
|
181
|
+
const endpoints = [{
|
|
182
|
+
path: '/health',
|
|
183
|
+
name: 'Health Check'
|
|
184
|
+
}, {
|
|
185
|
+
path: '/auth/magic-link',
|
|
186
|
+
name: 'Authentication'
|
|
187
|
+
}, {
|
|
188
|
+
path: '/api',
|
|
189
|
+
name: 'API Root'
|
|
190
|
+
}];
|
|
191
|
+
for (const endpoint of endpoints) {
|
|
192
|
+
try {
|
|
193
|
+
const url = `${config.worker.url}${endpoint.path}`;
|
|
194
|
+
console.log(` Testing ${endpoint.name}...`);
|
|
195
|
+
|
|
196
|
+
// In a real implementation, we'd make HTTP requests
|
|
197
|
+
// For now, just check if URLs are well-formed
|
|
198
|
+
new URL(url);
|
|
199
|
+
console.log(` ✅ ${endpoint.name} endpoint valid`);
|
|
200
|
+
results.endpoints.push({
|
|
201
|
+
...endpoint,
|
|
202
|
+
success: true
|
|
203
|
+
});
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.log(` ⚠️ ${endpoint.name} check failed: ${error.message}`);
|
|
206
|
+
results.endpoints.push({
|
|
207
|
+
...endpoint,
|
|
208
|
+
success: false,
|
|
209
|
+
error: error.message
|
|
210
|
+
});
|
|
211
|
+
results.overall = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return results;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get testing summary
|
|
219
|
+
*
|
|
220
|
+
* @param {Object} testResults - Test results
|
|
221
|
+
* @returns {string} Summary message
|
|
222
|
+
*/
|
|
223
|
+
getSummary(testResults) {
|
|
224
|
+
if (testResults.skipped) {
|
|
225
|
+
return '⏭️ Tests skipped';
|
|
226
|
+
}
|
|
227
|
+
if (testResults.overall) {
|
|
228
|
+
return '✅ All tests passed';
|
|
229
|
+
}
|
|
230
|
+
const failures = [];
|
|
231
|
+
if (!testResults.health?.success) failures.push('health');
|
|
232
|
+
if (!testResults.authentication?.success) failures.push('authentication');
|
|
233
|
+
return `⚠️ Tests completed with failures: ${failures.join(', ')}`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -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('../../../
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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';
|