@tamyla/clodo-framework 1.0.0 → 2.0.1

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 CHANGED
@@ -1,3 +1,22 @@
1
+ ## [2.0.1](https://github.com/tamylaa/clodo-framework/compare/v2.0.0...v2.0.1) (2025-10-10)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * reorganize documentation structure and fix package distribution ([598d44b](https://github.com/tamylaa/clodo-framework/commit/598d44b669f65c222d215ba33d0361d736a15ac9))
7
+
8
+ # [2.0.0](https://github.com/tamylaa/clodo-framework/compare/v1.0.0...v2.0.0) (2025-10-10)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * enhance enterprise readiness with comprehensive testing roadmap and documentation ([25702d6](https://github.com/tamylaa/clodo-framework/commit/25702d624047ef40991de47eba0b8dabcfaecfa8))
14
+
15
+
16
+ ### BREAKING CHANGES
17
+
18
+ * Framework now requires structured testing approach for enterprise deployment
19
+
1
20
  # 1.0.0 (2025-10-09)
2
21
 
3
22
 
@@ -270,6 +270,9 @@ export class DeploymentValidator {
270
270
 
271
271
  // Validate environment configuration
272
272
  await this.validateEnvironmentConfig();
273
+
274
+ // Validate D1 database bindings
275
+ await this.validateD1Configuration();
273
276
  this.results.categories.configuration = 'passed';
274
277
  this.addResult('configuration', 'Configuration files validated', 'success');
275
278
  } catch (error) {
@@ -457,6 +460,112 @@ export class DeploymentValidator {
457
460
  console.log(` āœ… Environment: ${this.environment} configuration valid`);
458
461
  this.addResult('environment', `${this.environment} environment validated`, 'info');
459
462
  }
463
+
464
+ /**
465
+ * Validate D1 database configuration across all services
466
+ */
467
+ async validateD1Configuration() {
468
+ console.log(' šŸ—„ļø Validating D1 database configuration...');
469
+ try {
470
+ // Import WranglerDeployer for D1 validation capabilities
471
+ const {
472
+ WranglerDeployer
473
+ } = await import('../../../src/deployment/wrangler-deployer.js');
474
+
475
+ // Check if this is a framework-level validation (no specific service)
476
+ if (!this.options?.servicePath) {
477
+ console.log(' ā­ļø Skipping D1 validation (framework-level validation)');
478
+ this.addResult('d1-config', 'D1 validation skipped for framework-level validation', 'info');
479
+ return;
480
+ }
481
+
482
+ // Create deployer instance for the specific service
483
+ const deployer = new WranglerDeployer({
484
+ cwd: this.options.servicePath,
485
+ environment: this.environment
486
+ });
487
+
488
+ // Discover deployment configuration
489
+ const deployConfig = await deployer.discoverDeploymentConfig(this.environment);
490
+ if (!deployConfig.configPath) {
491
+ console.log(' ā­ļø No wrangler.toml found, skipping D1 validation');
492
+ this.addResult('d1-config', 'No wrangler.toml found', 'info');
493
+ return;
494
+ }
495
+
496
+ // Validate D1 bindings
497
+ const d1Validation = await deployer.validateD1Bindings(deployConfig);
498
+ if (d1Validation.valid) {
499
+ if (d1Validation.bindings.length === 0) {
500
+ console.log(' āœ… No D1 databases configured');
501
+ this.addResult('d1-config', 'No D1 databases configured', 'info');
502
+ } else {
503
+ console.log(` āœ… All ${d1Validation.summary.total} D1 database bindings valid`);
504
+ this.addResult('d1-config', `${d1Validation.summary.total} D1 bindings validated`, 'success');
505
+
506
+ // Log details about each binding
507
+ d1Validation.bindings.forEach(binding => {
508
+ if (binding.valid) {
509
+ console.log(` āœ… ${binding.binding}: ${binding.database_name}`);
510
+ }
511
+ });
512
+ }
513
+ } else {
514
+ console.log(` āŒ D1 validation failed: ${d1Validation.summary.invalid} invalid bindings`);
515
+
516
+ // Log details about invalid bindings
517
+ d1Validation.invalidBindings.forEach(binding => {
518
+ console.log(` āŒ ${binding.binding || 'unnamed'}: ${binding.issues.join(', ')}`);
519
+ });
520
+
521
+ // Add warning with suggestions
522
+ const suggestions = this.generateD1Suggestions(d1Validation);
523
+ this.addWarning('d1-config', `D1 configuration issues found. ${suggestions}`);
524
+
525
+ // Don't fail validation entirely for D1 issues if not in strict mode
526
+ if (!this.strictMode) {
527
+ console.log(' āš ļø Continuing with warnings (non-strict mode)');
528
+ this.addResult('d1-config', 'D1 issues found but continuing', 'warning');
529
+ } else {
530
+ throw new Error(`D1 validation failed: ${d1Validation.summary.invalid} invalid bindings`);
531
+ }
532
+ }
533
+ } catch (error) {
534
+ if (error.message.includes('D1 validation failed')) {
535
+ throw error; // Re-throw D1 validation errors
536
+ }
537
+
538
+ // Handle other errors (import errors, etc.)
539
+ console.log(` āš ļø D1 validation error: ${error.message}`);
540
+ this.addWarning('d1-config', `D1 validation error: ${error.message}`);
541
+ if (this.strictMode) {
542
+ throw new Error(`D1 validation failed: ${error.message}`);
543
+ }
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Generate helpful suggestions for D1 configuration issues
549
+ * @param {Object} d1Validation - D1 validation results
550
+ * @returns {string} Suggestions text
551
+ */
552
+ generateD1Suggestions(d1Validation) {
553
+ const suggestions = [];
554
+ d1Validation.invalidBindings.forEach(binding => {
555
+ binding.issues.forEach(issue => {
556
+ if (issue.includes('not found')) {
557
+ suggestions.push('Run "wrangler d1 list" to see available databases');
558
+ suggestions.push('Create missing database with "wrangler d1 create <name>"');
559
+ } else if (issue.includes('Missing')) {
560
+ suggestions.push('Check wrangler.toml [[d1_databases]] section completeness');
561
+ }
562
+ });
563
+ });
564
+
565
+ // Remove duplicates
566
+ const uniqueSuggestions = [...new Set(suggestions)];
567
+ return uniqueSuggestions.length > 0 ? `Suggestions: ${uniqueSuggestions.slice(0, 2).join('; ')}` : 'Check D1 database configuration in wrangler.toml';
568
+ }
460
569
  async validateDomainEndpoints(domain) {
461
570
  console.log(` Validating endpoints for ${domain}...`);
462
571
  console.log(` šŸ”§ DEBUG: skipEndpointCheck = ${this.options?.skipEndpointCheck}`);
@@ -538,6 +647,9 @@ export class DeploymentValidator {
538
647
  this.results.errors.push(message);
539
648
  }
540
649
  }
650
+ addWarning(category, message) {
651
+ this.addResult(category, message, 'warning');
652
+ }
541
653
  printValidationSummary() {
542
654
  console.log('\nšŸ“Š VALIDATION SUMMARY');
543
655
  console.log('====================');
@@ -2,10 +2,13 @@ import { spawn } from 'child_process';
2
2
  import { execSync } from 'child_process';
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
+ import { WranglerD1Manager } from '../../bin/database/wrangler-d1-manager.js';
5
6
 
6
7
  /**
7
8
  * WranglerDeployer - Executes actual Cloudflare Workers deployments using wrangler CLI
8
9
  * Provides integration between Clodo Framework orchestration and wrangler deployment
10
+ *
11
+ * Now integrated with WranglerD1Manager for comprehensive D1 database operations
9
12
  */
10
13
  export class WranglerDeployer {
11
14
  constructor(options = {}) {
@@ -15,6 +18,12 @@ export class WranglerDeployer {
15
18
  this.maxRetries = options.maxRetries || 1;
16
19
  this.environment = options.environment || this.detectEnvironment();
17
20
  this.serviceInfo = options.serviceInfo || this.discoverServiceInfo();
21
+
22
+ // Initialize D1 manager for database operations
23
+ this.d1Manager = new WranglerD1Manager({
24
+ cwd: this.cwd,
25
+ timeout: 60000 // 1 minute for D1 operations
26
+ });
18
27
  }
19
28
 
20
29
  /**
@@ -487,14 +496,19 @@ export class WranglerDeployer {
487
496
 
488
497
  // Validate configuration can be parsed
489
498
  const configValidation = await this.validateWranglerConfig(deployConfig, environment);
499
+
500
+ // Validate D1 database bindings using D1Manager
501
+ const d1Validation = await this.d1Manager.validateD1Bindings(deployConfig);
502
+ const overallValid = configValidation.valid && d1Validation.valid;
490
503
  return {
491
- valid: true,
504
+ valid: overallValid,
492
505
  version: versionResult.output.trim(),
493
506
  config: deployConfig,
494
507
  account: accountInfo,
495
508
  configValidation,
509
+ d1Validation,
496
510
  authenticated: true,
497
- ready: configValidation.valid
511
+ ready: overallValid
498
512
  };
499
513
  } catch (error) {
500
514
  return {
@@ -570,5 +584,36 @@ export class WranglerDeployer {
570
584
  }
571
585
  return warnings;
572
586
  }
587
+
588
+ /**
589
+ * Handle D1 binding errors with interactive recovery options (delegated to D1Manager)
590
+ * @param {string} error - Error message
591
+ * @param {Object} context - Error context
592
+ * @returns {Promise<Object>} Recovery result
593
+ */
594
+ async handleD1BindingError(error, context = {}) {
595
+ return await this.d1Manager.handleD1BindingError(error, context);
596
+ }
597
+
598
+ /**
599
+ * Validate D1 bindings using D1Manager (delegated)
600
+ */
601
+ async validateD1Bindings(deployConfig) {
602
+ return await this.d1Manager.validateD1Bindings(deployConfig);
603
+ }
604
+
605
+ /**
606
+ * Extract D1 bindings using D1Manager (delegated)
607
+ */
608
+ extractD1Bindings(configContent) {
609
+ return this.d1Manager.extractD1Bindings(configContent);
610
+ }
611
+
612
+ /**
613
+ * Check D1 database existence using D1Manager (delegated)
614
+ */
615
+ async checkD1DatabaseExists(nameOrId) {
616
+ return await this.d1Manager.checkD1DatabaseExists(nameOrId);
617
+ }
573
618
  }
574
619
  export default WranglerDeployer;
@@ -14,6 +14,7 @@
14
14
  import { createInterface } from 'readline';
15
15
  import chalk from 'chalk';
16
16
  import { validateServiceName, validateDomainName } from '../utils/validation.js';
17
+ import { uiStructuresLoader } from '../utils/ui-structures-loader.js';
17
18
  export class InputCollector {
18
19
  constructor(options = {}) {
19
20
  this.interactive = options.interactive !== false;
@@ -24,34 +25,221 @@ export class InputCollector {
24
25
  }
25
26
 
26
27
  /**
27
- * Collect all 6 core inputs interactively
28
+ * Collect all inputs using three-tier template-driven approach
29
+ * Tier 1: 6 core inputs (required)
30
+ * Tier 2: 15 smart confirmations (assumed, user can modify)
31
+ * Tier 3: 67 automated generations (framework handles)
28
32
  */
29
- async collectCoreInputs() {
30
- const inputs = {};
31
- console.log(chalk.cyan('šŸ“ Collecting Core Service Information'));
32
- console.log(chalk.white('These 6 inputs are required to create your Clodo service.\n'));
33
+ async collectInputsWithTransparency() {
34
+ await uiStructuresLoader.loadTemplates();
35
+ const result = {
36
+ collectionMetadata: {
37
+ method: 'three-tier-template-driven',
38
+ timestamp: new Date().toISOString(),
39
+ tiers: {
40
+ core: 6,
41
+ confirmable: 15,
42
+ automated: 67
43
+ }
44
+ },
45
+ coreInputs: {},
46
+ smartConfirmations: {},
47
+ automatedGenerations: {},
48
+ userModifications: []
49
+ };
50
+
51
+ // Tier 1: Core Inputs (required from user)
52
+ console.log(chalk.cyan('\nšŸ“ Tier 1: Core Service Information'));
53
+ console.log(chalk.white('These 6 inputs are required to create your service.\n'));
54
+ const coreTemplate = uiStructuresLoader.getCoreInputsTemplate();
55
+ if (!coreTemplate) {
56
+ throw new Error('Core inputs template not found. Cannot proceed with input collection.');
57
+ }
58
+ if (coreTemplate) {
59
+ for (const inputDef of coreTemplate.inputs) {
60
+ const value = await this.collectInputFromDefinition(inputDef);
61
+ result.coreInputs[inputDef.id] = {
62
+ value,
63
+ source: 'user-provided',
64
+ required: true
65
+ };
66
+ }
67
+ }
33
68
 
34
- // 1. Service Name
35
- inputs.serviceName = await this.collectServiceName();
69
+ // Tier 2: Smart Confirmations (framework assumptions, user can modify)
70
+ console.log(chalk.cyan('\nšŸ¤” Tier 2: Smart Confirmations'));
71
+ console.log(chalk.white('Based on your core inputs, we\'ve made smart assumptions. Review and modify as needed.\n'));
72
+ const confirmTemplate = uiStructuresLoader.getSmartConfirmableTemplate();
73
+ if (confirmTemplate) {
74
+ for (const category of confirmTemplate.categories) {
75
+ console.log(chalk.yellow(`\n${category.title}`));
76
+ console.log(chalk.gray(`${category.description}\n`));
77
+ for (const inputId of category.inputs) {
78
+ // Generate smart default based on core inputs
79
+ const smartDefault = this.generateSmartDefault(inputId, result.coreInputs);
80
+ const userValue = await this.confirmOrModifyValue(inputId, smartDefault);
81
+ result.smartConfirmations[inputId] = {
82
+ value: userValue,
83
+ defaultAssumed: smartDefault,
84
+ userModified: userValue !== smartDefault,
85
+ source: userValue === smartDefault ? 'framework-assumed' : 'user-modified'
86
+ };
87
+ if (userValue !== smartDefault) {
88
+ result.userModifications.push({
89
+ field: inputId,
90
+ assumed: smartDefault,
91
+ chosen: userValue
92
+ });
93
+ }
94
+ }
95
+ }
96
+ }
36
97
 
37
- // 2. Service Type
38
- inputs.serviceType = await this.collectServiceType();
98
+ // Tier 3: Automated Generation (show transparency)
99
+ console.log(chalk.cyan('\n⚔ Tier 3: Automated Generation'));
100
+ console.log(chalk.white('The following will be automatically generated from your inputs:\n'));
101
+ const autoTemplate = uiStructuresLoader.getAutomatedGenerationTemplate();
102
+ if (autoTemplate) {
103
+ console.log(chalk.gray(`šŸ“Š ${autoTemplate.collectionStrategy.inputCount} configurations will be generated automatically`));
104
+ console.log(chalk.gray(`ā±ļø Estimated time: ${autoTemplate.template.estimatedTime}`));
39
105
 
40
- // 3. Domain Name
41
- inputs.domainName = await this.collectDomainName();
106
+ // Show some examples of what will be automated
107
+ result.automatedGenerations = {
108
+ count: autoTemplate.collectionStrategy.inputCount,
109
+ estimatedTime: autoTemplate.template.estimatedTime,
110
+ examples: ['Database connection strings', 'Environment variables', 'API endpoints', 'Security configurations', 'Deployment scripts']
111
+ };
112
+ }
42
113
 
43
- // 4. Cloudflare API Token
44
- inputs.cloudflareToken = await this.collectCloudflareToken();
114
+ // Summary
115
+ console.log(chalk.green('\nāœ… Collection Complete!'));
116
+ console.log(chalk.white(`Core inputs: ${Object.keys(result.coreInputs).length}`));
117
+ console.log(chalk.white(`Smart confirmations: ${Object.keys(result.smartConfirmations).length}`));
118
+ console.log(chalk.white(`Automated generations: ${result.automatedGenerations.count || 0}`));
119
+ if (result.userModifications.length > 0) {
120
+ console.log(chalk.yellow(`User modifications: ${result.userModifications.length}`));
121
+ }
122
+ return result;
123
+ }
45
124
 
46
- // 5. Cloudflare Account ID
47
- inputs.cloudflareAccountId = await this.collectCloudflareAccountId();
125
+ /**
126
+ * Generate smart defaults based on core inputs
127
+ */
128
+ generateSmartDefault(inputId, coreInputs) {
129
+ const serviceName = coreInputs.serviceName?.value || '';
130
+ const environment = coreInputs.environment?.value || 'development';
131
+ const domainName = coreInputs.domainName?.value || '';
132
+ switch (inputId) {
133
+ case 'display-name':
134
+ return serviceName ? serviceName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) : '';
135
+ case 'description':
136
+ return `A service built with CLODO Framework`;
137
+ case 'version':
138
+ return '1.0.0';
139
+ case 'author':
140
+ return 'CLODO Framework';
141
+ case 'production-url':
142
+ return domainName ? `https://api.${domainName}` : '';
143
+ case 'staging-url':
144
+ return domainName ? `https://staging-api.${domainName}` : '';
145
+ case 'development-url':
146
+ return domainName ? `https://dev-api.${domainName}` : '';
147
+ case 'service-directory':
148
+ return serviceName ? `./services/${serviceName}` : '';
149
+ case 'database-name':
150
+ return serviceName ? `${serviceName}-db` : '';
151
+ case 'worker-name':
152
+ return serviceName ? `${serviceName}-worker` : '';
153
+ case 'log-level':
154
+ return environment === 'production' ? 'warn' : environment === 'staging' ? 'info' : 'debug';
155
+ case 'env-prefix':
156
+ return environment === 'production' ? 'PROD_' : environment === 'staging' ? 'STAGING_' : 'DEV_';
157
+ default:
158
+ return '';
159
+ }
160
+ }
48
161
 
49
- // 6. Cloudflare Zone ID
50
- inputs.cloudflareZoneId = await this.collectCloudflareZoneId();
162
+ /**
163
+ * Allow user to confirm or modify a smart default
164
+ */
165
+ async confirmOrModifyValue(inputId, defaultValue) {
166
+ console.log(chalk.blue(`ā“ ${this.formatFieldName(inputId)}`));
167
+ console.log(chalk.gray(` Suggested: ${defaultValue}`));
168
+ const answer = await this.question(`Press Enter to accept, or enter new value: `);
169
+ return answer.trim() || defaultValue;
170
+ }
51
171
 
52
- // 7. Target Environment
53
- inputs.environment = await this.collectEnvironment();
54
- return inputs;
172
+ /**
173
+ * Format field names for display
174
+ */
175
+ formatFieldName(inputId) {
176
+ return inputId.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
177
+ }
178
+
179
+ /**
180
+ * Collect a single input based on UI definition
181
+ */
182
+ async collectInputFromDefinition(inputDef) {
183
+ const {
184
+ id,
185
+ ui,
186
+ validation,
187
+ examples,
188
+ followUp
189
+ } = inputDef;
190
+
191
+ // Display prompt
192
+ console.log(chalk.blue(`ā“ ${ui.label}`));
193
+ if (ui.description) {
194
+ console.log(chalk.gray(` ${ui.description}`));
195
+ }
196
+ if (examples && examples.length > 0) {
197
+ console.log(chalk.gray(` Examples: ${examples.join(', ')}`));
198
+ }
199
+ for (;;) {
200
+ const answer = await this.question(`${ui.placeholder || 'Enter value'}: `);
201
+
202
+ // Basic validation
203
+ if (validation) {
204
+ if (validation.required && !answer) {
205
+ console.log(chalk.red('āŒ This field is required'));
206
+ continue;
207
+ }
208
+ if (validation.minLength && answer.length < validation.minLength) {
209
+ console.log(chalk.red(`āŒ Minimum length: ${validation.minLength}`));
210
+ continue;
211
+ }
212
+ if (validation.maxLength && answer.length > validation.maxLength) {
213
+ console.log(chalk.red(`āŒ Maximum length: ${validation.maxLength}`));
214
+ continue;
215
+ }
216
+ if (validation.pattern && !new RegExp(validation.pattern).test(answer)) {
217
+ console.log(chalk.red(`āŒ ${validation.customMessage || 'Invalid format'}`));
218
+ continue;
219
+ }
220
+ }
221
+
222
+ // Follow-up message
223
+ if (followUp) {
224
+ const message = followUp.message.replace('{value}', answer);
225
+ console.log(chalk.green(`āœ… ${message}`));
226
+ if (followUp.preview) {
227
+ console.log(chalk.gray(` ${followUp.preview.replace('{value}', answer)}`));
228
+ }
229
+ }
230
+ return answer;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Promisified readline question
236
+ */
237
+ question(prompt) {
238
+ return new Promise(resolve => {
239
+ this.rl.question(prompt, answer => {
240
+ resolve(answer.trim());
241
+ });
242
+ });
55
243
  }
56
244
 
57
245
  /**
@@ -270,6 +270,9 @@ export class DeploymentValidator {
270
270
 
271
271
  // Validate environment configuration
272
272
  await this.validateEnvironmentConfig();
273
+
274
+ // Validate D1 database bindings
275
+ await this.validateD1Configuration();
273
276
  this.results.categories.configuration = 'passed';
274
277
  this.addResult('configuration', 'Configuration files validated', 'success');
275
278
  } catch (error) {
@@ -457,6 +460,112 @@ export class DeploymentValidator {
457
460
  console.log(` āœ… Environment: ${this.environment} configuration valid`);
458
461
  this.addResult('environment', `${this.environment} environment validated`, 'info');
459
462
  }
463
+
464
+ /**
465
+ * Validate D1 database configuration across all services
466
+ */
467
+ async validateD1Configuration() {
468
+ console.log(' šŸ—„ļø Validating D1 database configuration...');
469
+ try {
470
+ // Import WranglerDeployer for D1 validation capabilities
471
+ const {
472
+ WranglerDeployer
473
+ } = await import('../../../src/deployment/wrangler-deployer.js');
474
+
475
+ // Check if this is a framework-level validation (no specific service)
476
+ if (!this.options?.servicePath) {
477
+ console.log(' ā­ļø Skipping D1 validation (framework-level validation)');
478
+ this.addResult('d1-config', 'D1 validation skipped for framework-level validation', 'info');
479
+ return;
480
+ }
481
+
482
+ // Create deployer instance for the specific service
483
+ const deployer = new WranglerDeployer({
484
+ cwd: this.options.servicePath,
485
+ environment: this.environment
486
+ });
487
+
488
+ // Discover deployment configuration
489
+ const deployConfig = await deployer.discoverDeploymentConfig(this.environment);
490
+ if (!deployConfig.configPath) {
491
+ console.log(' ā­ļø No wrangler.toml found, skipping D1 validation');
492
+ this.addResult('d1-config', 'No wrangler.toml found', 'info');
493
+ return;
494
+ }
495
+
496
+ // Validate D1 bindings
497
+ const d1Validation = await deployer.validateD1Bindings(deployConfig);
498
+ if (d1Validation.valid) {
499
+ if (d1Validation.bindings.length === 0) {
500
+ console.log(' āœ… No D1 databases configured');
501
+ this.addResult('d1-config', 'No D1 databases configured', 'info');
502
+ } else {
503
+ console.log(` āœ… All ${d1Validation.summary.total} D1 database bindings valid`);
504
+ this.addResult('d1-config', `${d1Validation.summary.total} D1 bindings validated`, 'success');
505
+
506
+ // Log details about each binding
507
+ d1Validation.bindings.forEach(binding => {
508
+ if (binding.valid) {
509
+ console.log(` āœ… ${binding.binding}: ${binding.database_name}`);
510
+ }
511
+ });
512
+ }
513
+ } else {
514
+ console.log(` āŒ D1 validation failed: ${d1Validation.summary.invalid} invalid bindings`);
515
+
516
+ // Log details about invalid bindings
517
+ d1Validation.invalidBindings.forEach(binding => {
518
+ console.log(` āŒ ${binding.binding || 'unnamed'}: ${binding.issues.join(', ')}`);
519
+ });
520
+
521
+ // Add warning with suggestions
522
+ const suggestions = this.generateD1Suggestions(d1Validation);
523
+ this.addWarning('d1-config', `D1 configuration issues found. ${suggestions}`);
524
+
525
+ // Don't fail validation entirely for D1 issues if not in strict mode
526
+ if (!this.strictMode) {
527
+ console.log(' āš ļø Continuing with warnings (non-strict mode)');
528
+ this.addResult('d1-config', 'D1 issues found but continuing', 'warning');
529
+ } else {
530
+ throw new Error(`D1 validation failed: ${d1Validation.summary.invalid} invalid bindings`);
531
+ }
532
+ }
533
+ } catch (error) {
534
+ if (error.message.includes('D1 validation failed')) {
535
+ throw error; // Re-throw D1 validation errors
536
+ }
537
+
538
+ // Handle other errors (import errors, etc.)
539
+ console.log(` āš ļø D1 validation error: ${error.message}`);
540
+ this.addWarning('d1-config', `D1 validation error: ${error.message}`);
541
+ if (this.strictMode) {
542
+ throw new Error(`D1 validation failed: ${error.message}`);
543
+ }
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Generate helpful suggestions for D1 configuration issues
549
+ * @param {Object} d1Validation - D1 validation results
550
+ * @returns {string} Suggestions text
551
+ */
552
+ generateD1Suggestions(d1Validation) {
553
+ const suggestions = [];
554
+ d1Validation.invalidBindings.forEach(binding => {
555
+ binding.issues.forEach(issue => {
556
+ if (issue.includes('not found')) {
557
+ suggestions.push('Run "wrangler d1 list" to see available databases');
558
+ suggestions.push('Create missing database with "wrangler d1 create <name>"');
559
+ } else if (issue.includes('Missing')) {
560
+ suggestions.push('Check wrangler.toml [[d1_databases]] section completeness');
561
+ }
562
+ });
563
+ });
564
+
565
+ // Remove duplicates
566
+ const uniqueSuggestions = [...new Set(suggestions)];
567
+ return uniqueSuggestions.length > 0 ? `Suggestions: ${uniqueSuggestions.slice(0, 2).join('; ')}` : 'Check D1 database configuration in wrangler.toml';
568
+ }
460
569
  async validateDomainEndpoints(domain) {
461
570
  console.log(` Validating endpoints for ${domain}...`);
462
571
  console.log(` šŸ”§ DEBUG: skipEndpointCheck = ${this.options?.skipEndpointCheck}`);
@@ -538,6 +647,9 @@ export class DeploymentValidator {
538
647
  this.results.errors.push(message);
539
648
  }
540
649
  }
650
+ addWarning(category, message) {
651
+ this.addResult(category, message, 'warning');
652
+ }
541
653
  printValidationSummary() {
542
654
  console.log('\nšŸ“Š VALIDATION SUMMARY');
543
655
  console.log('====================');