@tamyla/clodo-framework 3.1.9 → 3.1.11

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