@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.
Files changed (169) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +53 -0
  3. package/dist/bin/clodo-service.js +47 -15
  4. package/dist/bin/commands/deploy.js +115 -83
  5. package/dist/bin/commands/helpers/deployment-ui.js +138 -0
  6. package/dist/bin/commands/helpers/deployment-verification.js +251 -0
  7. package/dist/bin/commands/helpers/error-recovery.js +80 -0
  8. package/dist/bin/commands/helpers/resource-detection.js +113 -0
  9. package/dist/bin/commands/validate.js +1 -1
  10. package/dist/bin/security/security-cli.js +1 -1
  11. package/dist/bin/shared/cache/configuration-cache.js +82 -0
  12. package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
  13. package/dist/bin/shared/cloudflare/index.js +1 -1
  14. package/dist/bin/shared/cloudflare/ops.js +6 -4
  15. package/dist/bin/shared/config/ConfigurationManager.js +23 -1
  16. package/dist/bin/shared/config/command-config-manager.js +19 -3
  17. package/dist/bin/shared/config/index.js +1 -1
  18. package/dist/bin/shared/deployment/credential-collector.js +30 -7
  19. package/dist/bin/shared/deployment/index.js +2 -2
  20. package/dist/bin/shared/deployment/rollback-manager.js +4 -520
  21. package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
  22. package/dist/bin/shared/deployment/validator.js +40 -10
  23. package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
  24. package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
  25. package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
  26. package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
  27. package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
  28. package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
  29. package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
  30. package/dist/bin/shared/error-handling/error-classifier.js +46 -0
  31. package/dist/bin/shared/monitoring/health-checker.js +129 -1
  32. package/dist/bin/shared/monitoring/memory-manager.js +17 -6
  33. package/dist/bin/shared/routing/domain-router.js +1 -1
  34. package/dist/bin/shared/utils/deployment-validator.js +97 -0
  35. package/dist/bin/shared/utils/formatters.js +10 -0
  36. package/dist/bin/shared/utils/index.js +13 -1
  37. package/dist/bin/shared/utils/interactive-prompts.js +34 -18
  38. package/dist/bin/shared/utils/progress-manager.js +2 -2
  39. package/dist/bin/shared/utils/progress-spinner.js +53 -0
  40. package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
  41. package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
  42. package/dist/security/index.js +1 -1
  43. package/dist/security/patterns/insecure-patterns.js +1 -1
  44. package/dist/utils/constants.js +102 -0
  45. package/dist/utils/deployment/wrangler-config-manager.js +215 -48
  46. package/dist/utils/framework-config.js +2 -2
  47. package/dist/utils/interactive-prompts.js +10 -59
  48. package/package.json +16 -8
  49. package/dist/bin/clodo-service-old.js +0 -868
  50. package/dist/bin/clodo-service-test.js +0 -10
  51. package/dist/bin/commands/assess.js +0 -91
  52. package/dist/bin/commands/create.js +0 -77
  53. package/dist/bin/commands/diagnose.js +0 -83
  54. package/dist/bin/commands/helpers.js +0 -138
  55. package/dist/bin/commands/update.js +0 -75
  56. package/dist/bin/database/deployment-db-manager.js +0 -423
  57. package/dist/bin/database/enterprise-db-manager.js +0 -457
  58. package/dist/bin/database/wrangler-d1-manager.js +0 -685
  59. package/dist/bin/deployment/enterprise-deploy.js +0 -877
  60. package/dist/bin/deployment/master-deploy.js +0 -1376
  61. package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
  62. package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
  63. package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
  64. package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
  65. package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
  66. package/dist/bin/deployment/modules/ValidationManager.js +0 -342
  67. package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  68. package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  69. package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  70. package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  71. package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  72. package/dist/bin/deployment/test-interactive-utils.js +0 -66
  73. package/dist/bin/portfolio/portfolio-manager.js +0 -487
  74. package/dist/bin/service-management/create-service.js +0 -122
  75. package/dist/bin/service-management/init-service.js +0 -79
  76. package/dist/config/customers.js +0 -623
  77. package/dist/config/domains.js +0 -186
  78. package/dist/config/index.js +0 -6
  79. package/dist/database/database-orchestrator.js +0 -795
  80. package/dist/database/index.js +0 -4
  81. package/dist/deployment/index.js +0 -11
  82. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  83. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  84. package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  85. package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  86. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  87. package/dist/deployment/orchestration/index.js +0 -17
  88. package/dist/deployment/rollback-manager.js +0 -36
  89. package/dist/deployment/wrangler-deployer.js +0 -640
  90. package/dist/handlers/GenericRouteHandler.js +0 -532
  91. package/dist/migration/MigrationAdapters.js +0 -562
  92. package/dist/modules/ModuleManager.js +0 -668
  93. package/dist/modules/security.js +0 -96
  94. package/dist/orchestration/cross-domain-coordinator.js +0 -1083
  95. package/dist/orchestration/index.js +0 -5
  96. package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
  97. package/dist/orchestration/modules/DomainResolver.js +0 -198
  98. package/dist/orchestration/modules/StateManager.js +0 -332
  99. package/dist/orchestration/multi-domain-orchestrator.js +0 -724
  100. package/dist/routing/EnhancedRouter.js +0 -158
  101. package/dist/schema/SchemaManager.js +0 -778
  102. package/dist/service-management/ConfirmationEngine.js +0 -412
  103. package/dist/service-management/ErrorTracker.js +0 -299
  104. package/dist/service-management/GenerationEngine.js +0 -447
  105. package/dist/service-management/InputCollector.js +0 -619
  106. package/dist/service-management/ServiceCreator.js +0 -265
  107. package/dist/service-management/ServiceInitializer.js +0 -453
  108. package/dist/service-management/ServiceOrchestrator.js +0 -633
  109. package/dist/service-management/generators/BaseGenerator.js +0 -233
  110. package/dist/service-management/generators/GeneratorRegistry.js +0 -254
  111. package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
  112. package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
  113. package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
  114. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
  115. package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
  116. package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
  117. package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
  118. package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
  119. package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
  120. package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
  121. package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
  122. package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
  123. package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
  124. package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
  125. package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
  126. package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
  127. package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
  128. package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
  129. package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
  130. package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
  131. package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
  132. package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
  133. package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
  134. package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
  135. package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
  136. package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
  137. package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
  138. package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
  139. package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
  140. package/dist/service-management/generators/utils/FileWriter.js +0 -179
  141. package/dist/service-management/generators/utils/PathResolver.js +0 -157
  142. package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
  143. package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
  144. package/dist/service-management/generators/utils/index.js +0 -18
  145. package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
  146. package/dist/service-management/handlers/GenerationHandler.js +0 -80
  147. package/dist/service-management/handlers/InputHandler.js +0 -59
  148. package/dist/service-management/handlers/ValidationHandler.js +0 -203
  149. package/dist/service-management/index.js +0 -14
  150. package/dist/service-management/routing/DomainRouteMapper.js +0 -311
  151. package/dist/service-management/routing/RouteGenerator.js +0 -266
  152. package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
  153. package/dist/service-management/routing/index.js +0 -14
  154. package/dist/service-management/services/DirectoryStructureService.js +0 -56
  155. package/dist/service-management/services/GenerationCoordinator.js +0 -208
  156. package/dist/service-management/services/GeneratorRegistry.js +0 -174
  157. package/dist/services/GenericDataService.js +0 -501
  158. package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
  159. package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
  160. package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
  161. package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
  162. package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
  163. package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
  164. package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
  165. package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
  166. package/dist/ui-structures/reference/service-manifest-template.json +0 -342
  167. package/dist/version/VersionDetector.js +0 -723
  168. package/dist/worker/index.js +0 -4
  169. 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
- }