@tamyla/clodo-framework 4.0.11 → 4.0.13
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 +702 -388
- package/dist/cli/commands/assess.js +2 -1
- package/dist/cli/commands/create.js +2 -1
- package/dist/cli/commands/deploy.js +2 -1
- package/dist/cli/commands/diagnose.js +2 -1
- package/dist/cli/commands/update.js +2 -1
- package/dist/cli/commands/validate.js +2 -1
- package/dist/deployment/wrangler-deployer.js +1 -1
- package/dist/lib/shared/utils/configuration-validator.js +250 -0
- package/dist/lib/shared/utils/wrangler-compatibility.js +191 -0
- package/dist/lib/shared/validation/config-validator.js +383 -0
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +33 -3
- package/dist/service-management/handlers/ValidationHandler.js +15 -0
- package/dist/utils/file-manager.js +5 -12
- package/package.json +4 -2
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { ServiceOrchestrator,
|
|
8
|
+
import { ServiceOrchestrator, ServiceConfigManager } from '@tamyla/clodo-framework';
|
|
9
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
9
10
|
export function registerAssessCommand(program) {
|
|
10
11
|
const command = program.command('assess [service-path]').description('Run intelligent capability assessment (requires @tamyla/clodo-orchestration)').option('--export <file>', 'Export assessment results to JSON file').option('--domain <domain>', 'Domain name for assessment').option('--service-type <type>', 'Service type for assessment').option('--token <token>', 'Cloudflare API token').option('--show-config-sources', 'Display all configuration sources and merged result');
|
|
11
12
|
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
-
import { Clodo,
|
|
9
|
+
import { Clodo, ConfigLoader } from '@tamyla/clodo-framework';
|
|
10
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
10
11
|
export function registerCreateCommand(program) {
|
|
11
12
|
const command = program.command('create').description('Create a new Clodo service with conversational setup').option('-n, --non-interactive', 'Run in non-interactive mode with all required parameters').option('--service-name <name>', 'Service name (required in non-interactive mode)').option('--service-type <type>', 'Service type: data-service, auth-service, content-service, api-gateway, generic', 'generic').option('--domain-name <domain>', 'Domain name (required in non-interactive mode)').option('--cloudflare-token <token>', 'Cloudflare API token (required in non-interactive mode)').option('--cloudflare-account-id <id>', 'Cloudflare account ID (required in non-interactive mode)').option('--cloudflare-zone-id <id>', 'Cloudflare zone ID (required in non-interactive mode)').option('--environment <env>', 'Target environment: development, staging, production', 'development').option('--output-path <path>', 'Output directory for generated service', '.').option('--template-path <path>', 'Path to service templates', './templates').option('--force', 'Skip confirmation prompts').option('--validate', 'Validate service after creation');
|
|
12
13
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { Clodo,
|
|
2
|
+
import { Clodo, ConfigLoader, InteractiveDeploymentCoordinator, OutputFormatter } from '@tamyla/clodo-framework';
|
|
3
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
3
4
|
export function registerDeployCommand(program) {
|
|
4
5
|
const command = program.command('deploy').description('Deploy a Clodo service with interactive configuration and validation')
|
|
5
6
|
// Cloudflare-specific options
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { ServiceOrchestrator,
|
|
3
|
+
import { ServiceOrchestrator, ServiceConfigManager } from '@tamyla/clodo-framework';
|
|
4
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
4
5
|
export function registerDiagnoseCommand(program) {
|
|
5
6
|
const command = program.command('diagnose [service-path]').description('Diagnose and report issues with an existing service').option('--deep-scan', 'Perform deep analysis including dependencies and deployment readiness').option('--export-report <file>', 'Export diagnostic report to file').option('--fix-suggestions', 'Include suggested fixes for issues').option('--show-config-sources', 'Display all configuration sources and merged result');
|
|
6
7
|
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import path from 'path';
|
|
7
|
-
import { ServiceOrchestrator,
|
|
7
|
+
import { ServiceOrchestrator, ServiceConfigManager } from '@tamyla/clodo-framework';
|
|
8
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
8
9
|
export function registerUpdateCommand(program) {
|
|
9
10
|
const command = program.command('update [service-path]').description('Update an existing service configuration').option('-i, --interactive', 'Run in interactive mode to select what to update').option('--domain-name <domain>', 'Update domain name').option('--cloudflare-token <token>', 'Update Cloudflare API token').option('--cloudflare-account-id <id>', 'Update Cloudflare account ID').option('--cloudflare-zone-id <id>', 'Update Cloudflare zone ID').option('--environment <env>', 'Update target environment: development, staging, production').option('--add-feature <feature>', 'Add a feature flag').option('--remove-feature <feature>', 'Remove a feature flag').option('--regenerate-configs', 'Regenerate all configuration files').option('--fix-errors', 'Attempt to fix common configuration errors').option('--preview', 'Show what would be changed without applying').option('--show-config-sources', 'Display all configuration sources and merged result').option('--force', 'Skip confirmation prompts');
|
|
10
11
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { Clodo
|
|
2
|
+
import { Clodo } from '@tamyla/clodo-framework';
|
|
3
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
3
4
|
export function registerValidateCommand(program) {
|
|
4
5
|
const command = program.command('validate <service-path>').description('Validate an existing service configuration').option('--export-report <file>', 'Export validation report to JSON file');
|
|
5
6
|
|
|
@@ -2,7 +2,7 @@ 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 '
|
|
5
|
+
import { WranglerD1Manager } from '../lib/database/wrangler-d1-manager.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* WranglerDeployer - Executes actual Cloudflare Workers deployments using wrangler CLI
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates service configurations before deployment to catch issues early
|
|
5
|
+
* and provide actionable error messages.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, existsSync } from 'fs';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { WranglerCompatibilityDetector } from './wrangler-compatibility.js';
|
|
11
|
+
export class ConfigurationValidator {
|
|
12
|
+
/**
|
|
13
|
+
* Validate a service configuration
|
|
14
|
+
* @param {string} servicePath - Path to the service directory
|
|
15
|
+
* @returns {Promise<ValidationResult>} Validation result
|
|
16
|
+
*/
|
|
17
|
+
static async validateServiceConfig(servicePath) {
|
|
18
|
+
const result = {
|
|
19
|
+
isValid: true,
|
|
20
|
+
errors: [],
|
|
21
|
+
warnings: [],
|
|
22
|
+
suggestions: []
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
// Check if wrangler.toml exists
|
|
26
|
+
const wranglerPath = join(servicePath, 'wrangler.toml');
|
|
27
|
+
if (!existsSync(wranglerPath)) {
|
|
28
|
+
result.isValid = false;
|
|
29
|
+
result.errors.push('Missing wrangler.toml file');
|
|
30
|
+
result.suggestions.push('Run: npx clodo-service create --service-name <name>');
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Load and parse wrangler.toml
|
|
35
|
+
const wranglerConfig = ConfigurationValidator.parseWranglerToml(wranglerPath);
|
|
36
|
+
if (!wranglerConfig) {
|
|
37
|
+
result.isValid = false;
|
|
38
|
+
result.errors.push('Invalid wrangler.toml format');
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate required fields
|
|
43
|
+
ConfigurationValidator.validateRequiredFields(wranglerConfig, result);
|
|
44
|
+
|
|
45
|
+
// Validate compatibility configuration
|
|
46
|
+
await ConfigurationValidator.validateCompatibilityConfig(wranglerConfig, result);
|
|
47
|
+
|
|
48
|
+
// Validate build configuration
|
|
49
|
+
ConfigurationValidator.validateBuildConfig(wranglerConfig, result);
|
|
50
|
+
|
|
51
|
+
// Check for common issues
|
|
52
|
+
ConfigurationValidator.checkCommonIssues(servicePath, wranglerConfig, result);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
result.isValid = false;
|
|
55
|
+
result.errors.push(`Configuration validation failed: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse wrangler.toml file
|
|
62
|
+
* @param {string} filePath - Path to wrangler.toml
|
|
63
|
+
* @returns {Object|null} Parsed configuration or null if invalid
|
|
64
|
+
*/
|
|
65
|
+
static parseWranglerToml(filePath) {
|
|
66
|
+
try {
|
|
67
|
+
const content = readFileSync(filePath, 'utf8');
|
|
68
|
+
// Simple TOML-like parsing (basic implementation)
|
|
69
|
+
const config = {};
|
|
70
|
+
const lines = content.split('\n').map(line => line.trim()).filter(line => line && !line.startsWith('#'));
|
|
71
|
+
let currentSection = null;
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
if (line.startsWith('[') && line.endsWith(']')) {
|
|
74
|
+
currentSection = line.slice(1, -1);
|
|
75
|
+
config[currentSection] = {};
|
|
76
|
+
} else if (line.includes('=')) {
|
|
77
|
+
const [key, ...valueParts] = line.split('=');
|
|
78
|
+
const value = valueParts.join('=').trim();
|
|
79
|
+
if (currentSection) {
|
|
80
|
+
config[currentSection][key.trim()] = this.parseValue(value);
|
|
81
|
+
} else {
|
|
82
|
+
config[key.trim()] = this.parseValue(value);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return config;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.warn(`Failed to parse wrangler.toml: ${error.message}`);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Parse TOML value
|
|
95
|
+
* @param {string} value - Raw value string
|
|
96
|
+
* @returns {any} Parsed value
|
|
97
|
+
*/
|
|
98
|
+
static parseValue(value) {
|
|
99
|
+
// Remove quotes
|
|
100
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
101
|
+
return value.slice(1, -1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Parse booleans
|
|
105
|
+
if (value === 'true') return true;
|
|
106
|
+
if (value === 'false') return false;
|
|
107
|
+
|
|
108
|
+
// Parse arrays (basic)
|
|
109
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
110
|
+
try {
|
|
111
|
+
return JSON.parse(value);
|
|
112
|
+
} catch {
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Validate required fields
|
|
121
|
+
* @param {Object} config - Wrangler configuration
|
|
122
|
+
* @param {ValidationResult} result - Validation result to update
|
|
123
|
+
*/
|
|
124
|
+
static validateRequiredFields(config, result) {
|
|
125
|
+
const required = ['name', 'main', 'compatibility_date', 'account_id'];
|
|
126
|
+
for (const field of required) {
|
|
127
|
+
if (!config[field]) {
|
|
128
|
+
result.isValid = false;
|
|
129
|
+
result.errors.push(`Missing required field: ${field}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check for D1 databases
|
|
134
|
+
if (config['d1_databases'] || config['[[d1_databases]]']) {
|
|
135
|
+
if (!config['d1_databases']?.binding || !config['[[d1_databases]]']?.binding) {
|
|
136
|
+
result.warnings.push('D1 database configured but binding not specified');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Validate compatibility configuration
|
|
143
|
+
* @param {Object} config - Wrangler configuration
|
|
144
|
+
* @param {ValidationResult} result - Validation result to update
|
|
145
|
+
*/
|
|
146
|
+
static async validateCompatibilityConfig(config, result) {
|
|
147
|
+
try {
|
|
148
|
+
const detector = new WranglerCompatibilityDetector();
|
|
149
|
+
const wranglerVersion = await Promise.race([detector.detectVersion(), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 2000))]);
|
|
150
|
+
const optimalConfig = detector.getOptimalConfig(wranglerVersion);
|
|
151
|
+
|
|
152
|
+
// Check if current config matches optimal
|
|
153
|
+
if (optimalConfig.nodejs_compat !== undefined) {
|
|
154
|
+
if (config.nodejs_compat !== optimalConfig.nodejs_compat) {
|
|
155
|
+
result.warnings.push(`nodejs_compat should be ${optimalConfig.nodejs_compat} for Wrangler ${wranglerVersion}`);
|
|
156
|
+
result.suggestions.push('Consider updating compatibility configuration');
|
|
157
|
+
}
|
|
158
|
+
} else if (optimalConfig.compatibility_flags) {
|
|
159
|
+
const currentFlags = config.compatibility_flags;
|
|
160
|
+
if (!currentFlags || !Array.isArray(currentFlags) || !ConfigurationValidator.arraysEqual(currentFlags, optimalConfig.compatibility_flags)) {
|
|
161
|
+
result.warnings.push(`compatibility_flags should be ${JSON.stringify(optimalConfig.compatibility_flags)} for Wrangler ${wranglerVersion}`);
|
|
162
|
+
result.suggestions.push('Consider updating compatibility configuration');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
result.warnings.push(`Could not validate compatibility configuration: ${error.message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Validate build configuration
|
|
172
|
+
* @param {Object} config - Wrangler configuration
|
|
173
|
+
* @param {ValidationResult} result - Validation result to update
|
|
174
|
+
*/
|
|
175
|
+
static validateBuildConfig(config, result) {
|
|
176
|
+
const build = config.build || {};
|
|
177
|
+
|
|
178
|
+
// Check for build command
|
|
179
|
+
if (!build.command) {
|
|
180
|
+
result.warnings.push('No build command specified - deployment may fail if code needs compilation');
|
|
181
|
+
result.suggestions.push('Add: [build] command = "npm run build"');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check for upload format
|
|
185
|
+
const upload = build.upload || {};
|
|
186
|
+
if (!upload.format) {
|
|
187
|
+
result.warnings.push('No upload format specified - using default may cause issues');
|
|
188
|
+
result.suggestions.push('Add: [build.upload] format = "modules"');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check for external includes (important for Node.js modules)
|
|
192
|
+
const external = upload.external || {};
|
|
193
|
+
if (!external.include || !external.include.includes('node:*')) {
|
|
194
|
+
result.warnings.push('Node.js modules may not be properly externalized');
|
|
195
|
+
result.suggestions.push('Add: [build.upload.external] include = ["node:*"]');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Check for common configuration issues
|
|
201
|
+
* @param {string} servicePath - Service directory path
|
|
202
|
+
* @param {Object} config - Wrangler configuration
|
|
203
|
+
* @param {ValidationResult} result - Validation result to update
|
|
204
|
+
*/
|
|
205
|
+
static checkCommonIssues(servicePath, config, result) {
|
|
206
|
+
// Check if main file exists
|
|
207
|
+
if (config.main) {
|
|
208
|
+
const mainPath = join(servicePath, config.main);
|
|
209
|
+
if (!existsSync(mainPath)) {
|
|
210
|
+
result.isValid = false;
|
|
211
|
+
result.errors.push(`Main file does not exist: ${config.main}`);
|
|
212
|
+
result.suggestions.push('Ensure the main file exists or update the main field in wrangler.toml');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Check for environment variables without values
|
|
217
|
+
const vars = config.vars || {};
|
|
218
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
219
|
+
if (value === '' || value === '""') {
|
|
220
|
+
result.warnings.push(`Environment variable ${key} has empty value`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Check for database_id placeholders
|
|
225
|
+
if (config['d1_databases']?.database_id === '') {
|
|
226
|
+
result.warnings.push('D1 database_id is empty - needs to be configured before deployment');
|
|
227
|
+
result.suggestions.push('Run: wrangler d1 create <database-name>');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Check if two arrays are equal
|
|
233
|
+
* @param {Array} a - First array
|
|
234
|
+
* @param {Array} b - Second array
|
|
235
|
+
* @returns {boolean} True if arrays are equal
|
|
236
|
+
*/
|
|
237
|
+
static arraysEqual(a, b) {
|
|
238
|
+
if (a.length !== b.length) return false;
|
|
239
|
+
return a.every((val, index) => val === b[index]);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Validation result interface
|
|
245
|
+
* @typedef {Object} ValidationResult
|
|
246
|
+
* @property {boolean} isValid - Whether configuration is valid
|
|
247
|
+
* @property {string[]} errors - Critical errors that prevent deployment
|
|
248
|
+
* @property {string[]} warnings - Non-critical issues that should be addressed
|
|
249
|
+
* @property {string[]} suggestions - Actionable suggestions for improvement
|
|
250
|
+
*/
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrangler Compatibility Detector
|
|
3
|
+
*
|
|
4
|
+
* Automatically detects Wrangler version and provides optimal configuration
|
|
5
|
+
* to prevent deployment failures due to compatibility issues.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { exec } from 'child_process';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
const execAsync = promisify(exec);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wrangler Compatibility Detector
|
|
14
|
+
* Handles version detection and optimal configuration generation
|
|
15
|
+
*/
|
|
16
|
+
export class WranglerCompatibilityDetector {
|
|
17
|
+
/**
|
|
18
|
+
* Detects the installed Wrangler version
|
|
19
|
+
* @returns {Promise<string>} Wrangler version string
|
|
20
|
+
*/
|
|
21
|
+
async detectVersion() {
|
|
22
|
+
try {
|
|
23
|
+
const {
|
|
24
|
+
stdout
|
|
25
|
+
} = await execAsync('npx wrangler --version');
|
|
26
|
+
const version = stdout.trim();
|
|
27
|
+
|
|
28
|
+
// Validate version format (e.g., "3.15.0", "4.0.0")
|
|
29
|
+
if (!this.isValidVersion(version)) {
|
|
30
|
+
console.warn(`⚠️ Detected Wrangler version '${version}' may not be valid. Using fallback detection.`);
|
|
31
|
+
return this.fallbackDetection();
|
|
32
|
+
}
|
|
33
|
+
return version;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.warn(`⚠️ Failed to detect Wrangler version: ${error.message}`);
|
|
36
|
+
return this.fallbackDetection();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Validates version string format
|
|
42
|
+
* @param {string} version - Version string to validate
|
|
43
|
+
* @returns {boolean} True if version format is valid
|
|
44
|
+
*/
|
|
45
|
+
isValidVersion(version) {
|
|
46
|
+
// Match semantic versioning (e.g., 3.15.0, 4.0.0-beta.1)
|
|
47
|
+
const versionRegex = /^\d+\.\d+\.\d+(-[\w\.\-]+)?$/;
|
|
48
|
+
return versionRegex.test(version);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Fallback version detection when direct detection fails
|
|
53
|
+
* @returns {string} Fallback version string
|
|
54
|
+
*/
|
|
55
|
+
fallbackDetection() {
|
|
56
|
+
// Check for wrangler in package.json
|
|
57
|
+
try {
|
|
58
|
+
const fs = require('fs');
|
|
59
|
+
const path = require('path');
|
|
60
|
+
|
|
61
|
+
// Look for wrangler in package.json
|
|
62
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
63
|
+
if (fs.existsSync(packagePath)) {
|
|
64
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
65
|
+
const wranglerVersion = packageJson.dependencies?.wrangler || packageJson.devDependencies?.wrangler;
|
|
66
|
+
if (wranglerVersion) {
|
|
67
|
+
// Extract version from semver range (e.g., "^3.15.0" -> "3.15.0")
|
|
68
|
+
const cleanVersion = wranglerVersion.replace(/[^\d.]/g, '');
|
|
69
|
+
if (cleanVersion) {
|
|
70
|
+
console.log(`📋 Using Wrangler version from package.json: ${cleanVersion}`);
|
|
71
|
+
return cleanVersion;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.warn(`⚠️ Fallback detection failed: ${error.message}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Default to latest stable version
|
|
80
|
+
console.log('📋 Using default Wrangler version: 4.0.0');
|
|
81
|
+
return '4.0.0';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Gets optimal Wrangler configuration for the detected version
|
|
86
|
+
* @param {string} version - Wrangler version
|
|
87
|
+
* @returns {Object} Optimal configuration object
|
|
88
|
+
*/
|
|
89
|
+
getOptimalConfig(version) {
|
|
90
|
+
const majorVersion = parseInt(version.split('.')[0]);
|
|
91
|
+
if (majorVersion >= 4) {
|
|
92
|
+
// Wrangler v4+ uses nodejs_compat flag
|
|
93
|
+
return {
|
|
94
|
+
nodejs_compat: true
|
|
95
|
+
};
|
|
96
|
+
} else if (majorVersion >= 3) {
|
|
97
|
+
// Wrangler v3 uses compatibility_flags array
|
|
98
|
+
return {
|
|
99
|
+
compatibility_flags: ["nodejs_compat"]
|
|
100
|
+
};
|
|
101
|
+
} else {
|
|
102
|
+
// Wrangler v2 and below - minimal compatibility
|
|
103
|
+
return {
|
|
104
|
+
compatibility_flags: []
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Gets build configuration optimized for the Wrangler version
|
|
111
|
+
* @param {string} version - Wrangler version
|
|
112
|
+
* @returns {Object} Build configuration object
|
|
113
|
+
*/
|
|
114
|
+
getBuildConfig(version) {
|
|
115
|
+
const majorVersion = parseInt(version.split('.')[0]);
|
|
116
|
+
const baseConfig = {
|
|
117
|
+
command: "npm run build",
|
|
118
|
+
upload: {
|
|
119
|
+
format: "modules"
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Add Node.js module externalization for v3+
|
|
124
|
+
if (majorVersion >= 3) {
|
|
125
|
+
baseConfig.upload.external = {
|
|
126
|
+
include: ["node:*"]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return baseConfig;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Validates if current configuration is optimal for the Wrangler version
|
|
134
|
+
* @param {Object} config - Current Wrangler configuration
|
|
135
|
+
* @param {string} version - Wrangler version
|
|
136
|
+
* @returns {Object} Validation result with issues and suggestions
|
|
137
|
+
*/
|
|
138
|
+
validateConfig(config, version) {
|
|
139
|
+
const issues = [];
|
|
140
|
+
const optimalConfig = this.getOptimalConfig(version);
|
|
141
|
+
|
|
142
|
+
// Check Node.js compatibility settings
|
|
143
|
+
if (optimalConfig.nodejs_compat !== undefined) {
|
|
144
|
+
if (config.nodejs_compat !== optimalConfig.nodejs_compat) {
|
|
145
|
+
issues.push({
|
|
146
|
+
type: 'COMPATIBILITY_CONFIG',
|
|
147
|
+
severity: 'ERROR',
|
|
148
|
+
message: `nodejs_compat should be ${optimalConfig.nodejs_compat} for Wrangler ${version}`,
|
|
149
|
+
fix: `Set nodejs_compat = ${optimalConfig.nodejs_compat} in wrangler.toml`
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
} else if (optimalConfig.compatibility_flags) {
|
|
153
|
+
const hasNodejsCompat = config.compatibility_flags?.includes('nodejs_compat');
|
|
154
|
+
const shouldHaveNodejsCompat = optimalConfig.compatibility_flags.includes('nodejs_compat');
|
|
155
|
+
if (hasNodejsCompat !== shouldHaveNodejsCompat) {
|
|
156
|
+
issues.push({
|
|
157
|
+
type: 'COMPATIBILITY_FLAGS',
|
|
158
|
+
severity: 'ERROR',
|
|
159
|
+
message: `compatibility_flags should ${shouldHaveNodejsCompat ? 'include' : 'exclude'} "nodejs_compat" for Wrangler ${version}`,
|
|
160
|
+
fix: shouldHaveNodejsCompat ? 'Add "nodejs_compat" to compatibility_flags array' : 'Remove "nodejs_compat" from compatibility_flags array'
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
isValid: issues.filter(issue => issue.severity === 'ERROR').length === 0,
|
|
166
|
+
issues
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Generates a complete optimized Wrangler configuration
|
|
172
|
+
* @param {string} version - Wrangler version
|
|
173
|
+
* @param {Object} baseConfig - Base configuration to extend
|
|
174
|
+
* @returns {Object} Complete optimized configuration
|
|
175
|
+
*/
|
|
176
|
+
generateOptimalConfig(version, baseConfig = {}) {
|
|
177
|
+
const optimalConfig = this.getOptimalConfig(version);
|
|
178
|
+
const buildConfig = this.getBuildConfig(version);
|
|
179
|
+
return {
|
|
180
|
+
...baseConfig,
|
|
181
|
+
...optimalConfig,
|
|
182
|
+
build: {
|
|
183
|
+
...buildConfig,
|
|
184
|
+
...(baseConfig.build || {})
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Export singleton instance for convenience
|
|
191
|
+
export const wranglerCompatibility = new WranglerCompatibilityDetector();
|