@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
|
@@ -1,685 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wrangler D1 Database Manager
|
|
3
|
-
*
|
|
4
|
-
* Specialized module for managing Cloudflare D1 database operations through wrangler CLI.
|
|
5
|
-
* Extracted from WranglerDeployer to provide focused, reusable D1 database management.
|
|
6
|
-
*
|
|
7
|
-
* Responsibilities:
|
|
8
|
-
* - D1 database creation and management
|
|
9
|
-
* - D1 binding validation and configuration
|
|
10
|
-
* - D1 error detection and recovery
|
|
11
|
-
* - Interactive D1 database selection flows
|
|
12
|
-
*
|
|
13
|
-
* This module integrates with:
|
|
14
|
-
* - deployment-db-manager.js (higher-level database operations)
|
|
15
|
-
* - wrangler-deployer.js (deployment-time D1 validation)
|
|
16
|
-
* - enterprise-db-manager.js (portfolio-wide database management)
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { spawn } from 'child_process';
|
|
20
|
-
import { readFileSync, writeFileSync } from 'fs';
|
|
21
|
-
import { askYesNo, askChoice, askUser } from '../shared/utils/interactive-prompts.js';
|
|
22
|
-
export class WranglerD1Manager {
|
|
23
|
-
constructor(options = {}) {
|
|
24
|
-
this.cwd = options.cwd || process.cwd();
|
|
25
|
-
this.timeout = options.timeout || 60000; // 1 minute for D1 operations
|
|
26
|
-
this.retryAttempts = options.retryAttempts || 2;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Execute wrangler D1 command and capture output
|
|
31
|
-
* @param {Array<string>} args - Command arguments starting with 'wrangler'
|
|
32
|
-
* @returns {Promise<Object>} Command execution result
|
|
33
|
-
*/
|
|
34
|
-
async executeWranglerCommand(args) {
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
const process = spawn(args[0], args.slice(1), {
|
|
37
|
-
cwd: this.cwd,
|
|
38
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
39
|
-
shell: true
|
|
40
|
-
});
|
|
41
|
-
let stdout = '';
|
|
42
|
-
let stderr = '';
|
|
43
|
-
process.stdout.on('data', data => {
|
|
44
|
-
stdout += data.toString();
|
|
45
|
-
});
|
|
46
|
-
process.stderr.on('data', data => {
|
|
47
|
-
stderr += data.toString();
|
|
48
|
-
});
|
|
49
|
-
const timer = setTimeout(() => {
|
|
50
|
-
process.kill();
|
|
51
|
-
reject(new Error(`Command timed out after ${this.timeout}ms`));
|
|
52
|
-
}, this.timeout);
|
|
53
|
-
process.on('close', code => {
|
|
54
|
-
clearTimeout(timer);
|
|
55
|
-
const result = {
|
|
56
|
-
success: code === 0,
|
|
57
|
-
code,
|
|
58
|
-
stdout,
|
|
59
|
-
stderr,
|
|
60
|
-
output: stdout || stderr
|
|
61
|
-
};
|
|
62
|
-
if (code === 0) {
|
|
63
|
-
resolve(result);
|
|
64
|
-
} else {
|
|
65
|
-
const error = new Error(stderr || stdout || `Command failed with exit code ${code}`);
|
|
66
|
-
error.code = code;
|
|
67
|
-
error.stdout = stdout;
|
|
68
|
-
error.stderr = stderr;
|
|
69
|
-
reject(error);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
process.on('error', error => {
|
|
73
|
-
clearTimeout(timer);
|
|
74
|
-
reject(error);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Extract D1 database bindings from wrangler.toml content
|
|
81
|
-
* @param {string} configContent - Content of wrangler.toml file
|
|
82
|
-
* @returns {Array<Object>} Array of D1 database bindings
|
|
83
|
-
*/
|
|
84
|
-
extractD1Bindings(configContent) {
|
|
85
|
-
const bindings = [];
|
|
86
|
-
const lines = configContent.split('\n');
|
|
87
|
-
let currentBinding = null;
|
|
88
|
-
let inD1Section = false;
|
|
89
|
-
for (let i = 0; i < lines.length; i++) {
|
|
90
|
-
const line = lines[i].trim();
|
|
91
|
-
|
|
92
|
-
// Check for D1 database section start
|
|
93
|
-
if (line === '[[d1_databases]]') {
|
|
94
|
-
inD1Section = true;
|
|
95
|
-
currentBinding = {};
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Check for end of D1 section (next section or empty line with next section)
|
|
100
|
-
if (inD1Section && line.startsWith('[') && !line.startsWith('[[d1_databases]]')) {
|
|
101
|
-
if (currentBinding && Object.keys(currentBinding).length > 0) {
|
|
102
|
-
bindings.push(currentBinding);
|
|
103
|
-
}
|
|
104
|
-
inD1Section = false;
|
|
105
|
-
currentBinding = null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Parse D1 binding properties
|
|
109
|
-
if (inD1Section && line.includes('=')) {
|
|
110
|
-
const [key, ...valueParts] = line.split('=');
|
|
111
|
-
const value = valueParts.join('=').trim().replace(/^["']|["']$/g, '');
|
|
112
|
-
const cleanKey = key.trim();
|
|
113
|
-
if (cleanKey === 'binding') {
|
|
114
|
-
currentBinding.binding = value;
|
|
115
|
-
} else if (cleanKey === 'database_name') {
|
|
116
|
-
currentBinding.database_name = value;
|
|
117
|
-
} else if (cleanKey === 'database_id') {
|
|
118
|
-
currentBinding.database_id = value;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Handle last binding if file ends in D1 section
|
|
124
|
-
if (inD1Section && currentBinding && Object.keys(currentBinding).length > 0) {
|
|
125
|
-
bindings.push(currentBinding);
|
|
126
|
-
}
|
|
127
|
-
return bindings;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Check if a D1 database exists by name or ID
|
|
132
|
-
* @param {string} nameOrId - Database name or ID to check
|
|
133
|
-
* @returns {Promise<Object>} Object with exists flag and database info
|
|
134
|
-
*/
|
|
135
|
-
async checkD1DatabaseExists(nameOrId) {
|
|
136
|
-
try {
|
|
137
|
-
const result = await this.executeWranglerCommand(['wrangler', 'd1', 'list']);
|
|
138
|
-
if (!result.success) {
|
|
139
|
-
throw new Error(`Failed to list D1 databases: ${result.stderr}`);
|
|
140
|
-
}
|
|
141
|
-
const databases = [];
|
|
142
|
-
const lines = result.stdout.split('\n');
|
|
143
|
-
|
|
144
|
-
// Parse the database list output
|
|
145
|
-
for (const line of lines) {
|
|
146
|
-
// Look for database entries (skip headers and empty lines)
|
|
147
|
-
if (line.trim() && !line.includes('Database ID') && !line.includes('---')) {
|
|
148
|
-
// Try to parse database info from the line
|
|
149
|
-
const parts = line.trim().split(/\s+/);
|
|
150
|
-
if (parts.length >= 2) {
|
|
151
|
-
const dbId = parts[0];
|
|
152
|
-
const dbName = parts[1];
|
|
153
|
-
databases.push({
|
|
154
|
-
id: dbId,
|
|
155
|
-
name: dbName,
|
|
156
|
-
exists: true
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Check if this matches what we're looking for
|
|
160
|
-
if (dbId === nameOrId || dbName === nameOrId) {
|
|
161
|
-
return {
|
|
162
|
-
exists: true,
|
|
163
|
-
database: {
|
|
164
|
-
id: dbId,
|
|
165
|
-
name: dbName
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
exists: false,
|
|
174
|
-
availableDatabases: databases
|
|
175
|
-
};
|
|
176
|
-
} catch (error) {
|
|
177
|
-
throw new Error(`Failed to check D1 database existence: ${error.message}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Validate all D1 database bindings in wrangler configuration
|
|
183
|
-
* @param {Object} deployConfig - Deployment configuration
|
|
184
|
-
* @returns {Promise<Object>} Validation result with detailed information
|
|
185
|
-
*/
|
|
186
|
-
async validateD1Bindings(deployConfig) {
|
|
187
|
-
const validation = {
|
|
188
|
-
valid: true,
|
|
189
|
-
bindings: [],
|
|
190
|
-
issues: [],
|
|
191
|
-
missingDatabases: [],
|
|
192
|
-
suggestions: []
|
|
193
|
-
};
|
|
194
|
-
try {
|
|
195
|
-
// Read wrangler config
|
|
196
|
-
const configPath = deployConfig.configPath || 'wrangler.toml';
|
|
197
|
-
const configContent = readFileSync(configPath, 'utf8');
|
|
198
|
-
|
|
199
|
-
// Extract D1 bindings
|
|
200
|
-
const bindings = this.extractD1Bindings(configContent);
|
|
201
|
-
validation.bindings = bindings;
|
|
202
|
-
if (bindings.length === 0) {
|
|
203
|
-
validation.suggestions.push('No D1 databases configured. Consider adding D1 bindings if your application uses databases.');
|
|
204
|
-
return validation;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Validate each binding
|
|
208
|
-
for (const binding of bindings) {
|
|
209
|
-
const bindingIssues = [];
|
|
210
|
-
|
|
211
|
-
// Check required fields
|
|
212
|
-
if (!binding.binding) {
|
|
213
|
-
bindingIssues.push('Missing binding name');
|
|
214
|
-
}
|
|
215
|
-
if (!binding.database_name) {
|
|
216
|
-
bindingIssues.push('Missing database_name');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Check if database exists (by name or ID)
|
|
220
|
-
let databaseExists = false;
|
|
221
|
-
let existingDatabase = null;
|
|
222
|
-
if (binding.database_name) {
|
|
223
|
-
try {
|
|
224
|
-
const existsResult = await this.checkD1DatabaseExists(binding.database_name);
|
|
225
|
-
if (existsResult.exists) {
|
|
226
|
-
databaseExists = true;
|
|
227
|
-
existingDatabase = existsResult.database;
|
|
228
|
-
}
|
|
229
|
-
} catch (error) {
|
|
230
|
-
bindingIssues.push(`Failed to verify database existence: ${error.message}`);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (binding.database_id && !databaseExists) {
|
|
234
|
-
try {
|
|
235
|
-
const existsResult = await this.checkD1DatabaseExists(binding.database_id);
|
|
236
|
-
if (existsResult.exists) {
|
|
237
|
-
databaseExists = true;
|
|
238
|
-
existingDatabase = existsResult.database;
|
|
239
|
-
}
|
|
240
|
-
} catch (error) {
|
|
241
|
-
bindingIssues.push(`Failed to verify database existence by ID: ${error.message}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (!databaseExists) {
|
|
245
|
-
validation.missingDatabases.push({
|
|
246
|
-
binding: binding.binding,
|
|
247
|
-
database_name: binding.database_name,
|
|
248
|
-
database_id: binding.database_id
|
|
249
|
-
});
|
|
250
|
-
bindingIssues.push(`Database '${binding.database_name || binding.database_id}' not found`);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Check for ID/name consistency if both are provided
|
|
254
|
-
if (binding.database_id && binding.database_name && existingDatabase) {
|
|
255
|
-
if (existingDatabase.id !== binding.database_id) {
|
|
256
|
-
bindingIssues.push(`Database ID mismatch: expected ${binding.database_id}, found ${existingDatabase.id}`);
|
|
257
|
-
}
|
|
258
|
-
if (existingDatabase.name !== binding.database_name) {
|
|
259
|
-
bindingIssues.push(`Database name mismatch: expected ${binding.database_name}, found ${existingDatabase.name}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (bindingIssues.length > 0) {
|
|
263
|
-
validation.valid = false;
|
|
264
|
-
validation.issues.push({
|
|
265
|
-
binding: binding.binding || 'unknown',
|
|
266
|
-
issues: bindingIssues
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Add suggestions for missing databases
|
|
272
|
-
if (validation.missingDatabases.length > 0) {
|
|
273
|
-
validation.suggestions.push('Run database creation or update binding configuration to fix missing databases');
|
|
274
|
-
validation.suggestions.push('Use wrangler d1 create <database-name> to create missing databases');
|
|
275
|
-
}
|
|
276
|
-
} catch (error) {
|
|
277
|
-
validation.valid = false;
|
|
278
|
-
validation.issues.push({
|
|
279
|
-
binding: 'configuration',
|
|
280
|
-
issues: [`Failed to validate D1 bindings: ${error.message}`]
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
return validation;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Extract database name/ID from D1 binding error message
|
|
288
|
-
* @param {string} errorMessage - Error message from wrangler
|
|
289
|
-
* @returns {string|null} Extracted database name or ID
|
|
290
|
-
*/
|
|
291
|
-
extractDbNameFromError(errorMessage) {
|
|
292
|
-
const patterns = [/Couldn't find a D1 DB with the name or binding '([^']+)'/, /Database '([^']+)' not found/, /Unknown database: ([^\s]+)/, /D1 database ([^\s]+) does not exist/, /Missing D1 database: ([^\s]+)/];
|
|
293
|
-
for (const pattern of patterns) {
|
|
294
|
-
const match = errorMessage.match(pattern);
|
|
295
|
-
if (match) {
|
|
296
|
-
return match[1];
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Check if error message indicates a D1 binding issue
|
|
304
|
-
* @param {string} errorMessage - Error message to analyze
|
|
305
|
-
* @returns {Object} Analysis result with error type and details
|
|
306
|
-
*/
|
|
307
|
-
analyzeD1Error(errorMessage) {
|
|
308
|
-
const analysis = {
|
|
309
|
-
isD1Error: false,
|
|
310
|
-
errorType: null,
|
|
311
|
-
databaseName: null,
|
|
312
|
-
bindingName: null,
|
|
313
|
-
canRecover: false,
|
|
314
|
-
suggestion: null
|
|
315
|
-
};
|
|
316
|
-
const errorLower = errorMessage.toLowerCase();
|
|
317
|
-
|
|
318
|
-
// Check for D1-related keywords
|
|
319
|
-
if (errorLower.includes('d1') || errorLower.includes('database')) {
|
|
320
|
-
analysis.isD1Error = true;
|
|
321
|
-
|
|
322
|
-
// Determine error type
|
|
323
|
-
if (errorLower.includes("couldn't find") || errorLower.includes('not found')) {
|
|
324
|
-
analysis.errorType = 'database_not_found';
|
|
325
|
-
analysis.canRecover = true;
|
|
326
|
-
analysis.suggestion = 'Create the database or update the binding configuration';
|
|
327
|
-
} else if (errorLower.includes('binding')) {
|
|
328
|
-
analysis.errorType = 'binding_configuration_error';
|
|
329
|
-
analysis.canRecover = true;
|
|
330
|
-
analysis.suggestion = 'Update the D1 binding configuration in wrangler.toml';
|
|
331
|
-
} else if (errorLower.includes('permission') || errorLower.includes('access')) {
|
|
332
|
-
analysis.errorType = 'permission_error';
|
|
333
|
-
analysis.canRecover = false;
|
|
334
|
-
analysis.suggestion = 'Check Cloudflare account permissions for D1 databases';
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Extract database/binding names
|
|
338
|
-
analysis.databaseName = this.extractDbNameFromError(errorMessage);
|
|
339
|
-
|
|
340
|
-
// Try to extract binding name if different from database name
|
|
341
|
-
const bindingMatch = errorMessage.match(/binding '([^']+)'/);
|
|
342
|
-
if (bindingMatch) {
|
|
343
|
-
analysis.bindingName = bindingMatch[1];
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return analysis;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Create a new D1 database
|
|
351
|
-
* @param {string} databaseName - Name for the new database
|
|
352
|
-
* @returns {Promise<Object>} Creation result
|
|
353
|
-
*/
|
|
354
|
-
async createD1Database(databaseName) {
|
|
355
|
-
try {
|
|
356
|
-
console.log(` 🔨 Creating D1 database: ${databaseName}`);
|
|
357
|
-
const result = await this.executeWranglerCommand(['wrangler', 'd1', 'create', databaseName]);
|
|
358
|
-
if (result.success) {
|
|
359
|
-
// Extract database ID from output
|
|
360
|
-
const dbIdMatch = result.stdout.match(/database_id = "([^"]+)"/);
|
|
361
|
-
const databaseId = dbIdMatch ? dbIdMatch[1] : null;
|
|
362
|
-
return {
|
|
363
|
-
success: true,
|
|
364
|
-
database: {
|
|
365
|
-
name: databaseName,
|
|
366
|
-
id: databaseId
|
|
367
|
-
},
|
|
368
|
-
output: result.stdout
|
|
369
|
-
};
|
|
370
|
-
} else {
|
|
371
|
-
throw new Error(result.stderr || result.stdout);
|
|
372
|
-
}
|
|
373
|
-
} catch (error) {
|
|
374
|
-
throw new Error(`Failed to create D1 database '${databaseName}': ${error.message}`);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Update wrangler.toml with correct database binding information
|
|
380
|
-
* @param {string} bindingName - Binding name to update
|
|
381
|
-
* @param {string} databaseName - Database name
|
|
382
|
-
* @param {string} databaseId - Database ID
|
|
383
|
-
* @param {string} configPath - Path to wrangler.toml
|
|
384
|
-
* @returns {Promise<Object>} Update result
|
|
385
|
-
*/
|
|
386
|
-
async updateWranglerD1Binding(bindingName, databaseName, databaseId, configPath = 'wrangler.toml') {
|
|
387
|
-
try {
|
|
388
|
-
console.log(` 📝 Updating D1 binding: ${bindingName} -> ${databaseName} (${databaseId})`);
|
|
389
|
-
|
|
390
|
-
// Create backup
|
|
391
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
392
|
-
const backupPath = `${configPath}.backup.${timestamp}`;
|
|
393
|
-
const originalContent = readFileSync(configPath, 'utf8');
|
|
394
|
-
writeFileSync(backupPath, originalContent);
|
|
395
|
-
let updatedContent = originalContent;
|
|
396
|
-
const bindings = this.extractD1Bindings(originalContent);
|
|
397
|
-
|
|
398
|
-
// Find existing binding or add new one
|
|
399
|
-
const existingBinding = bindings.find(b => b.binding === bindingName);
|
|
400
|
-
if (existingBinding) {
|
|
401
|
-
// Update existing binding
|
|
402
|
-
const bindingRegex = new RegExp(`(\\[\\[d1_databases\\]\\][\\s\\S]*?binding\\s*=\\s*["']${bindingName}["'][\\s\\S]*?)database_name\\s*=\\s*["'][^"']*["']`, 'g');
|
|
403
|
-
updatedContent = updatedContent.replace(bindingRegex, `$1database_name = "${databaseName}"`);
|
|
404
|
-
const idRegex = new RegExp(`(\\[\\[d1_databases\\]\\][\\s\\S]*?binding\\s*=\\s*["']${bindingName}["'][\\s\\S]*?)database_id\\s*=\\s*["'][^"']*["']`, 'g');
|
|
405
|
-
updatedContent = updatedContent.replace(idRegex, `$1database_id = "${databaseId}"`);
|
|
406
|
-
} else {
|
|
407
|
-
// Add new binding
|
|
408
|
-
const newBinding = `
|
|
409
|
-
[[d1_databases]]
|
|
410
|
-
binding = "${bindingName}"
|
|
411
|
-
database_name = "${databaseName}"
|
|
412
|
-
database_id = "${databaseId}"
|
|
413
|
-
`;
|
|
414
|
-
updatedContent += newBinding;
|
|
415
|
-
}
|
|
416
|
-
writeFileSync(configPath, updatedContent);
|
|
417
|
-
return {
|
|
418
|
-
success: true,
|
|
419
|
-
backupPath,
|
|
420
|
-
binding: {
|
|
421
|
-
name: bindingName,
|
|
422
|
-
database_name: databaseName,
|
|
423
|
-
database_id: databaseId
|
|
424
|
-
}
|
|
425
|
-
};
|
|
426
|
-
} catch (error) {
|
|
427
|
-
throw new Error(`Failed to update D1 binding: ${error.message}`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Handle D1 binding errors with interactive recovery options
|
|
433
|
-
* @param {string} error - Error message
|
|
434
|
-
* @param {Object} context - Error context
|
|
435
|
-
* @returns {Promise<Object>} Recovery result
|
|
436
|
-
*/
|
|
437
|
-
async handleD1BindingError(error, context = {}) {
|
|
438
|
-
const errorMessage = typeof error === 'string' ? error : error.message;
|
|
439
|
-
const analysis = this.analyzeD1Error(errorMessage);
|
|
440
|
-
if (!analysis.isD1Error || !analysis.canRecover) {
|
|
441
|
-
return {
|
|
442
|
-
handled: false,
|
|
443
|
-
reason: analysis.isD1Error ? 'Cannot recover from this D1 error type' : 'Not a D1 error'
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
console.log('\n🔧 D1 Database Error Recovery');
|
|
447
|
-
console.log('=============================');
|
|
448
|
-
console.log(` Error Type: ${analysis.errorType}`);
|
|
449
|
-
console.log(` Database: ${analysis.databaseName || 'Unknown'}`);
|
|
450
|
-
console.log(` Suggestion: ${analysis.suggestion}`);
|
|
451
|
-
const databaseName = analysis.databaseName;
|
|
452
|
-
if (!databaseName) {
|
|
453
|
-
return {
|
|
454
|
-
handled: true,
|
|
455
|
-
action: 'cancelled',
|
|
456
|
-
reason: 'Could not extract database name from error'
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// Check if database exists
|
|
461
|
-
const existsResult = await this.checkD1DatabaseExists(databaseName);
|
|
462
|
-
if (existsResult.exists) {
|
|
463
|
-
return await this.updateBindingFlow(databaseName, context);
|
|
464
|
-
} else {
|
|
465
|
-
return await this.createMissingDatabaseFlow(databaseName, context);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Flow to create a missing database
|
|
471
|
-
* @param {string} databaseName - Name of the database to create
|
|
472
|
-
* @param {Object} context - Context information
|
|
473
|
-
* @returns {Promise<Object>} Creation flow result
|
|
474
|
-
*/
|
|
475
|
-
async createMissingDatabaseFlow(databaseName, context) {
|
|
476
|
-
console.log(`\n❌ Database '${databaseName}' not found`);
|
|
477
|
-
const availableResult = await this.checkD1DatabaseExists('');
|
|
478
|
-
if (availableResult.availableDatabases && availableResult.availableDatabases.length > 0) {
|
|
479
|
-
console.log('\n📋 Available databases:');
|
|
480
|
-
availableResult.availableDatabases.forEach((db, index) => {
|
|
481
|
-
console.log(` ${index + 1}. ${db.name} (${db.id})`);
|
|
482
|
-
});
|
|
483
|
-
const choices = [`Create new database '${databaseName}'`, 'Select from existing databases', 'Cancel and fix manually'];
|
|
484
|
-
const choice = await askChoice('What would you like to do?', choices, 0);
|
|
485
|
-
switch (choice) {
|
|
486
|
-
case 0:
|
|
487
|
-
return await this.createNewDatabaseAndConfigure(databaseName, context);
|
|
488
|
-
case 1:
|
|
489
|
-
return await this.showAvailableDatabasesFlow(context);
|
|
490
|
-
case 2:
|
|
491
|
-
return {
|
|
492
|
-
handled: true,
|
|
493
|
-
action: 'cancelled'
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
} else {
|
|
497
|
-
const shouldCreate = await askYesNo(`Create new D1 database '${databaseName}'?`, true);
|
|
498
|
-
if (shouldCreate) {
|
|
499
|
-
return await this.createNewDatabaseAndConfigure(databaseName, context);
|
|
500
|
-
} else {
|
|
501
|
-
return {
|
|
502
|
-
handled: true,
|
|
503
|
-
action: 'cancelled'
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* Create new database and configure binding
|
|
511
|
-
*/
|
|
512
|
-
async createNewDatabaseAndConfigure(databaseName, context) {
|
|
513
|
-
try {
|
|
514
|
-
const createResult = await this.createD1Database(databaseName);
|
|
515
|
-
if (createResult.success && createResult.database.id) {
|
|
516
|
-
// Update wrangler.toml with new database info
|
|
517
|
-
const bindingName = databaseName; // Use database name as binding name
|
|
518
|
-
await this.updateWranglerD1Binding(bindingName, databaseName, createResult.database.id, context.configPath || 'wrangler.toml');
|
|
519
|
-
return {
|
|
520
|
-
handled: true,
|
|
521
|
-
action: 'created_and_configured',
|
|
522
|
-
databaseName,
|
|
523
|
-
databaseId: createResult.database.id,
|
|
524
|
-
bindingName
|
|
525
|
-
};
|
|
526
|
-
} else {
|
|
527
|
-
throw new Error('Database creation did not return expected ID');
|
|
528
|
-
}
|
|
529
|
-
} catch (error) {
|
|
530
|
-
return {
|
|
531
|
-
handled: true,
|
|
532
|
-
action: 'failed',
|
|
533
|
-
error: error.message
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Flow to show available databases for selection
|
|
540
|
-
* @param {Object} context - Context information
|
|
541
|
-
* @returns {Promise<Object>} Selection flow result
|
|
542
|
-
*/
|
|
543
|
-
async showAvailableDatabasesFlow(context) {
|
|
544
|
-
try {
|
|
545
|
-
const availableResult = await this.checkD1DatabaseExists('');
|
|
546
|
-
if (!availableResult.availableDatabases || availableResult.availableDatabases.length === 0) {
|
|
547
|
-
console.log(' ⚠️ No existing databases found');
|
|
548
|
-
return {
|
|
549
|
-
handled: true,
|
|
550
|
-
action: 'no_databases_available'
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
const dbChoices = availableResult.availableDatabases.map(db => `${db.name} (${db.id})`);
|
|
554
|
-
dbChoices.push('Cancel');
|
|
555
|
-
const dbChoice = await askChoice('Select a database to use:', dbChoices);
|
|
556
|
-
if (dbChoice === dbChoices.length - 1) {
|
|
557
|
-
return {
|
|
558
|
-
handled: true,
|
|
559
|
-
action: 'cancelled'
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
const selectedDb = availableResult.availableDatabases[dbChoice];
|
|
563
|
-
const bindingName = selectedDb.name; // Use database name as binding name
|
|
564
|
-
|
|
565
|
-
// Update wrangler.toml
|
|
566
|
-
await this.updateWranglerD1Binding(bindingName, selectedDb.name, selectedDb.id, context.configPath || 'wrangler.toml');
|
|
567
|
-
return {
|
|
568
|
-
handled: true,
|
|
569
|
-
action: 'database_selected_and_configured',
|
|
570
|
-
databaseName: selectedDb.name,
|
|
571
|
-
databaseId: selectedDb.id,
|
|
572
|
-
bindingName
|
|
573
|
-
};
|
|
574
|
-
} catch (error) {
|
|
575
|
-
return {
|
|
576
|
-
handled: true,
|
|
577
|
-
action: 'failed',
|
|
578
|
-
error: error.message
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Flow to update binding configuration
|
|
585
|
-
* @param {string} originalName - Original database name from error
|
|
586
|
-
* @param {Object} context - Context information
|
|
587
|
-
* @returns {Promise<Object>} Update flow result
|
|
588
|
-
*/
|
|
589
|
-
async updateBindingFlow(originalName, context) {
|
|
590
|
-
console.log(`\n✅ Database '${originalName}' exists but binding may be misconfigured`);
|
|
591
|
-
const shouldUpdate = await askYesNo('Update the wrangler.toml binding configuration?', true);
|
|
592
|
-
if (!shouldUpdate) {
|
|
593
|
-
return {
|
|
594
|
-
handled: true,
|
|
595
|
-
action: 'cancelled'
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
try {
|
|
599
|
-
const existsResult = await this.checkD1DatabaseExists(originalName);
|
|
600
|
-
const database = existsResult.database;
|
|
601
|
-
await this.updateWranglerD1Binding(originalName,
|
|
602
|
-
// Use original name as binding name
|
|
603
|
-
database.name, database.id, context.configPath || 'wrangler.toml');
|
|
604
|
-
return {
|
|
605
|
-
handled: true,
|
|
606
|
-
action: 'binding_updated',
|
|
607
|
-
databaseName: database.name,
|
|
608
|
-
databaseId: database.id,
|
|
609
|
-
bindingName: originalName
|
|
610
|
-
};
|
|
611
|
-
} catch (error) {
|
|
612
|
-
return {
|
|
613
|
-
handled: true,
|
|
614
|
-
action: 'failed',
|
|
615
|
-
error: error.message
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* List all available D1 databases
|
|
622
|
-
* @returns {Promise<Array>} Array of database objects
|
|
623
|
-
*/
|
|
624
|
-
async listD1Databases() {
|
|
625
|
-
try {
|
|
626
|
-
const result = await this.executeWranglerCommand(['wrangler', 'd1', 'list']);
|
|
627
|
-
if (!result.success) {
|
|
628
|
-
throw new Error(`Failed to list D1 databases: ${result.stderr}`);
|
|
629
|
-
}
|
|
630
|
-
const databases = [];
|
|
631
|
-
const lines = result.stdout.split('\n');
|
|
632
|
-
for (const line of lines) {
|
|
633
|
-
if (line.trim() && !line.includes('Database ID') && !line.includes('---')) {
|
|
634
|
-
const parts = line.trim().split(/\s+/);
|
|
635
|
-
if (parts.length >= 2) {
|
|
636
|
-
databases.push({
|
|
637
|
-
id: parts[0],
|
|
638
|
-
name: parts[1]
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
return databases;
|
|
644
|
-
} catch (error) {
|
|
645
|
-
throw new Error(`Failed to list D1 databases: ${error.message}`);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
/**
|
|
650
|
-
* Get comprehensive D1 status for a configuration
|
|
651
|
-
* @param {string} configPath - Path to wrangler.toml
|
|
652
|
-
* @returns {Promise<Object>} D1 status information
|
|
653
|
-
*/
|
|
654
|
-
async getD1Status(configPath = 'wrangler.toml') {
|
|
655
|
-
try {
|
|
656
|
-
const configContent = readFileSync(configPath, 'utf8');
|
|
657
|
-
const bindings = this.extractD1Bindings(configContent);
|
|
658
|
-
const validation = await this.validateD1Bindings({
|
|
659
|
-
configPath
|
|
660
|
-
});
|
|
661
|
-
const availableDatabases = await this.listD1Databases();
|
|
662
|
-
return {
|
|
663
|
-
bindings,
|
|
664
|
-
validation,
|
|
665
|
-
availableDatabases,
|
|
666
|
-
summary: {
|
|
667
|
-
totalBindings: bindings.length,
|
|
668
|
-
validBindings: validation.valid ? bindings.length : bindings.length - validation.missingDatabases.length,
|
|
669
|
-
missingDatabases: validation.missingDatabases.length,
|
|
670
|
-
availableDatabases: availableDatabases.length
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
} catch (error) {
|
|
674
|
-
return {
|
|
675
|
-
error: error.message,
|
|
676
|
-
bindings: [],
|
|
677
|
-
validation: {
|
|
678
|
-
valid: false,
|
|
679
|
-
issues: [error.message]
|
|
680
|
-
},
|
|
681
|
-
availableDatabases: []
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|