@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.
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Configuration Validator
3
+ *
4
+ * Validates Wrangler and service configurations before deployment
5
+ * to prevent common deployment failures and provide actionable feedback.
6
+ */
7
+
8
+ import { WranglerCompatibilityDetector } from '../utils/wrangler-compatibility.js';
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+
12
+ /**
13
+ * Configuration Validation Result
14
+ * @typedef {Object} ValidationResult
15
+ * @property {boolean} isValid - Whether configuration is valid
16
+ * @property {ValidationIssue[]} issues - List of validation issues
17
+ * @property {string[]} warnings - Non-blocking warnings
18
+ * @property {string[]} suggestions - Improvement suggestions
19
+ */
20
+
21
+ /**
22
+ * Validation Issue
23
+ * @typedef {Object} ValidationIssue
24
+ * @property {string} type - Issue type identifier
25
+ * @property {string} severity - ERROR, WARNING, or INFO
26
+ * @property {string} message - Human-readable message
27
+ * @property {string} fix - Suggested fix
28
+ * @property {string} [file] - File where issue occurs
29
+ * @property {number} [line] - Line number if applicable
30
+ */
31
+
32
+ /**
33
+ * Configuration Validator
34
+ * Handles pre-deployment configuration validation
35
+ */
36
+ export class ConfigurationValidator {
37
+ constructor() {
38
+ this.compatibilityDetector = new WranglerCompatibilityDetector();
39
+ }
40
+
41
+ /**
42
+ * Validates a complete service configuration
43
+ * @param {string} servicePath - Path to service directory
44
+ * @param {Object} options - Validation options
45
+ * @returns {Promise<ValidationResult>} Validation result
46
+ */
47
+ async validateServiceConfig(servicePath, options = {}) {
48
+ const issues = [];
49
+ const warnings = [];
50
+ const suggestions = [];
51
+ try {
52
+ // Load wrangler.toml
53
+ const wranglerConfig = await this.loadWranglerConfig(servicePath);
54
+ if (!wranglerConfig) {
55
+ issues.push({
56
+ type: 'MISSING_CONFIG',
57
+ severity: 'ERROR',
58
+ message: 'wrangler.toml not found in service directory',
59
+ fix: 'Create wrangler.toml file or run service generation'
60
+ });
61
+ return {
62
+ isValid: false,
63
+ issues,
64
+ warnings,
65
+ suggestions
66
+ };
67
+ }
68
+
69
+ // Detect Wrangler version
70
+ const wranglerVersion = await this.compatibilityDetector.detectVersion();
71
+
72
+ // Validate compatibility settings
73
+ const compatibilityResult = this.compatibilityDetector.validateConfig(wranglerConfig, wranglerVersion);
74
+ issues.push(...compatibilityResult.issues);
75
+
76
+ // Validate build configuration
77
+ const buildIssues = this.validateBuildConfig(wranglerConfig);
78
+ issues.push(...buildIssues);
79
+
80
+ // Validate service structure
81
+ const structureIssues = await this.validateServiceStructure(servicePath);
82
+ issues.push(...structureIssues);
83
+
84
+ // Check for common issues
85
+ const commonIssues = await this.checkCommonIssues(servicePath, wranglerConfig);
86
+ warnings.push(...commonIssues.warnings);
87
+ suggestions.push(...commonIssues.suggestions);
88
+
89
+ // Generate improvement suggestions
90
+ if (options.verbose) {
91
+ suggestions.push(...this.generateOptimizationSuggestions(wranglerConfig, wranglerVersion));
92
+ }
93
+ const isValid = issues.filter(issue => issue.severity === 'ERROR').length === 0;
94
+ return {
95
+ isValid,
96
+ issues,
97
+ warnings,
98
+ suggestions
99
+ };
100
+ } catch (error) {
101
+ issues.push({
102
+ type: 'VALIDATION_ERROR',
103
+ severity: 'ERROR',
104
+ message: `Configuration validation failed: ${error.message}`,
105
+ fix: 'Check service directory structure and configuration files'
106
+ });
107
+ return {
108
+ isValid: false,
109
+ issues,
110
+ warnings,
111
+ suggestions
112
+ };
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Loads and parses wrangler.toml configuration
118
+ * @param {string} servicePath - Path to service directory
119
+ * @returns {Promise<Object|null>} Parsed configuration or null if not found
120
+ */
121
+ async loadWranglerConfig(servicePath) {
122
+ const configPath = path.join(servicePath, 'wrangler.toml');
123
+ try {
124
+ if (!fs.existsSync(configPath)) {
125
+ return null;
126
+ }
127
+
128
+ // For now, return a basic parsed config
129
+ // In a real implementation, you'd use a TOML parser
130
+ const configContent = fs.readFileSync(configPath, 'utf8');
131
+
132
+ // Basic TOML parsing (simplified - would use proper TOML parser in production)
133
+ return this.parseTomlConfig(configContent);
134
+ } catch (error) {
135
+ throw new Error(`Failed to load wrangler.toml: ${error.message}`);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Basic TOML config parser (simplified)
141
+ * @param {string} content - TOML content
142
+ * @returns {Object} Parsed configuration
143
+ */
144
+ parseTomlConfig(content) {
145
+ const config = {};
146
+
147
+ // Very basic parsing - in production, use a proper TOML library
148
+ const lines = content.split('\n');
149
+ for (const line of lines) {
150
+ const trimmed = line.trim();
151
+
152
+ // Skip comments and empty lines
153
+ if (!trimmed || trimmed.startsWith('#')) continue;
154
+
155
+ // Parse key = value
156
+ const equalsIndex = trimmed.indexOf('=');
157
+ if (equalsIndex > 0) {
158
+ const key = trimmed.substring(0, equalsIndex).trim();
159
+ const value = trimmed.substring(equalsIndex + 1).trim();
160
+
161
+ // Handle different value types
162
+ if (value === 'true') {
163
+ config[key] = true;
164
+ } else if (value === 'false') {
165
+ config[key] = false;
166
+ } else if (value.startsWith('"') && value.endsWith('"')) {
167
+ config[key] = value.slice(1, -1);
168
+ } else if (value.startsWith('[') && value.endsWith(']')) {
169
+ // Basic array parsing
170
+ const arrayContent = value.slice(1, -1);
171
+ config[key] = arrayContent.split(',').map(item => item.trim().replace(/"/g, ''));
172
+ } else {
173
+ config[key] = value;
174
+ }
175
+ }
176
+ }
177
+ return config;
178
+ }
179
+
180
+ /**
181
+ * Validates build configuration
182
+ * @param {Object} config - Wrangler configuration
183
+ * @returns {ValidationIssue[]} Build-related issues
184
+ */
185
+ validateBuildConfig(config) {
186
+ const issues = [];
187
+
188
+ // Check build command
189
+ if (!config.build?.command) {
190
+ issues.push({
191
+ type: 'MISSING_BUILD_COMMAND',
192
+ severity: 'WARNING',
193
+ message: 'Build command not specified',
194
+ fix: 'Add build.command = "npm run build" to wrangler.toml'
195
+ });
196
+ }
197
+
198
+ // Check upload format
199
+ if (config.build?.upload?.format !== 'modules') {
200
+ issues.push({
201
+ type: 'BUILD_FORMAT',
202
+ severity: 'INFO',
203
+ message: 'Consider using modules format for better performance',
204
+ fix: 'Set build.upload.format = "modules"'
205
+ });
206
+ }
207
+ return issues;
208
+ }
209
+
210
+ /**
211
+ * Validates service directory structure
212
+ * @param {string} servicePath - Path to service directory
213
+ * @returns {Promise<ValidationIssue[]>} Structure-related issues
214
+ */
215
+ async validateServiceStructure(servicePath) {
216
+ const issues = [];
217
+
218
+ // Check for package.json
219
+ const packagePath = path.join(servicePath, 'package.json');
220
+ if (!fs.existsSync(packagePath)) {
221
+ issues.push({
222
+ type: 'MISSING_PACKAGE_JSON',
223
+ severity: 'ERROR',
224
+ message: 'package.json not found',
225
+ fix: 'Create package.json with service dependencies'
226
+ });
227
+ }
228
+
229
+ // Check for src directory
230
+ const srcPath = path.join(servicePath, 'src');
231
+ if (!fs.existsSync(srcPath)) {
232
+ issues.push({
233
+ type: 'MISSING_SRC_DIR',
234
+ severity: 'WARNING',
235
+ message: 'src directory not found',
236
+ fix: 'Create src directory with service code'
237
+ });
238
+ }
239
+ return issues;
240
+ }
241
+
242
+ /**
243
+ * Checks for common configuration issues
244
+ * @param {string} servicePath - Path to service directory
245
+ * @param {Object} config - Wrangler configuration
246
+ * @returns {Object} Common issues with warnings and suggestions
247
+ */
248
+ async checkCommonIssues(servicePath, config) {
249
+ const warnings = [];
250
+ const suggestions = [];
251
+
252
+ // Check for large bundle potential
253
+ if (!config.build?.upload?.external?.include?.includes('node:*')) {
254
+ warnings.push('Consider externalizing Node.js modules to reduce bundle size');
255
+ }
256
+
257
+ // Check for missing environment variables
258
+ if (config.vars) {
259
+ const varKeys = Object.keys(config.vars);
260
+ suggestions.push(`Consider moving ${varKeys.length} environment variables to .env file for security`);
261
+ }
262
+ return {
263
+ warnings,
264
+ suggestions
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Generates optimization suggestions
270
+ * @param {Object} config - Current configuration
271
+ * @param {string} wranglerVersion - Wrangler version
272
+ * @returns {string[]} Optimization suggestions
273
+ */
274
+ generateOptimizationSuggestions(config, wranglerVersion) {
275
+ const suggestions = [];
276
+
277
+ // Performance suggestions
278
+ if (!config.build?.minify) {
279
+ suggestions.push('Enable minification: build.minify = true');
280
+ }
281
+
282
+ // Security suggestions
283
+ if (!config.build?.upload?.external) {
284
+ suggestions.push('Externalize dependencies to reduce bundle size and improve cold start performance');
285
+ }
286
+ return suggestions;
287
+ }
288
+
289
+ /**
290
+ * Fixes common configuration issues automatically
291
+ * @param {string} servicePath - Path to service directory
292
+ * @param {ValidationResult} validationResult - Validation result
293
+ * @returns {Promise<Object>} Fix result
294
+ */
295
+ async autoFix(servicePath, validationResult) {
296
+ const fixes = [];
297
+ const errors = [];
298
+ try {
299
+ const wranglerVersion = await this.compatibilityDetector.detectVersion();
300
+ const currentConfig = await this.loadWranglerConfig(servicePath);
301
+ if (!currentConfig) {
302
+ errors.push('Cannot auto-fix: wrangler.toml not found');
303
+ return {
304
+ success: false,
305
+ fixes,
306
+ errors
307
+ };
308
+ }
309
+
310
+ // Generate optimal config
311
+ const optimalConfig = this.compatibilityDetector.generateOptimalConfig(wranglerVersion, currentConfig);
312
+
313
+ // Apply fixes for auto-fixable issues
314
+ for (const issue of validationResult.issues) {
315
+ if (issue.type === 'COMPATIBILITY_CONFIG' && optimalConfig.nodejs_compat !== undefined) {
316
+ optimalConfig.nodejs_compat = optimalConfig.nodejs_compat;
317
+ fixes.push('Fixed nodejs_compat setting');
318
+ } else if (issue.type === 'COMPATIBILITY_FLAGS') {
319
+ optimalConfig.compatibility_flags = optimalConfig.compatibility_flags;
320
+ fixes.push('Fixed compatibility_flags setting');
321
+ }
322
+ }
323
+
324
+ // Save updated config
325
+ await this.saveWranglerConfig(servicePath, optimalConfig);
326
+ fixes.push('Updated wrangler.toml with optimal configuration');
327
+ return {
328
+ success: true,
329
+ fixes,
330
+ errors
331
+ };
332
+ } catch (error) {
333
+ errors.push(`Auto-fix failed: ${error.message}`);
334
+ return {
335
+ success: false,
336
+ fixes,
337
+ errors
338
+ };
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Saves wrangler configuration to file
344
+ * @param {string} servicePath - Path to service directory
345
+ * @param {Object} config - Configuration to save
346
+ */
347
+ async saveWranglerConfig(servicePath, config) {
348
+ const configPath = path.join(servicePath, 'wrangler.toml');
349
+
350
+ // Basic TOML generation (simplified - would use proper TOML library in production)
351
+ let tomlContent = '# Auto-generated by Clodo Framework\n\n';
352
+
353
+ // Add main config
354
+ if (config.name) tomlContent += `name = "${config.name}"\n`;
355
+ if (config.main) tomlContent += `main = "${config.main}"\n`;
356
+ if (config.compatibility_flags) {
357
+ tomlContent += `compatibility_flags = ${JSON.stringify(config.compatibility_flags)}\n`;
358
+ }
359
+ if (config.nodejs_compat !== undefined) {
360
+ tomlContent += `nodejs_compat = ${config.nodejs_compat}\n`;
361
+ }
362
+
363
+ // Add build config
364
+ if (config.build) {
365
+ tomlContent += '\n[build]\n';
366
+ if (config.build.command) tomlContent += `command = "${config.build.command}"\n`;
367
+ if (config.build.upload) {
368
+ tomlContent += '\n[build.upload]\n';
369
+ if (config.build.upload.format) tomlContent += `format = "${config.build.upload.format}"\n`;
370
+ if (config.build.upload.external) {
371
+ tomlContent += '\n[build.upload.external]\n';
372
+ if (config.build.upload.external.include) {
373
+ tomlContent += `include = ${JSON.stringify(config.build.upload.external.include)}\n`;
374
+ }
375
+ }
376
+ }
377
+ }
378
+ fs.writeFileSync(configPath, tomlContent, 'utf8');
379
+ }
380
+ }
381
+
382
+ // Export singleton instance
383
+ export const configValidator = new ConfigurationValidator();
@@ -1,4 +1,5 @@
1
1
  import { BaseGenerator } from '../BaseGenerator.js';
2
+ import { WranglerCompatibilityDetector } from '../../../lib/shared/utils/wrangler-compatibility.js';
2
3
  import { join } from 'path';
3
4
 
4
5
  /**
@@ -58,7 +59,7 @@ export class WranglerTomlGenerator extends BaseGenerator {
58
59
  const siteConfig = await this._generateSiteConfig(coreInputs);
59
60
 
60
61
  // Build wrangler.toml content
61
- const content = this._buildWranglerToml(coreInputs, confirmedValues, routesConfig, siteConfig);
62
+ const content = await this._buildWranglerToml(coreInputs, confirmedValues, routesConfig, siteConfig);
62
63
 
63
64
  // Write file
64
65
  await this.writeFile('wrangler.toml', content);
@@ -115,13 +116,42 @@ export class WranglerTomlGenerator extends BaseGenerator {
115
116
  /**
116
117
  * Build the complete wrangler.toml content
117
118
  */
118
- _buildWranglerToml(coreInputs, confirmedValues, routesConfig, siteConfig) {
119
+ async _buildWranglerToml(coreInputs, confirmedValues, routesConfig, siteConfig) {
119
120
  const compatDate = new Date().toISOString().split('T')[0];
121
+
122
+ // Detect Wrangler version and get optimal compatibility config
123
+ let compatibilityString = 'compatibility_flags = ["nodejs_compat"]';
124
+ let buildString = '\n\n[build]\ncommand = "npm run build"\n\n[build.upload]\nformat = "modules"\n\n[build.upload.external]\ninclude = ["node:*"]';
125
+ try {
126
+ const compatibilityDetector = new WranglerCompatibilityDetector();
127
+ const wranglerVersion = await Promise.race([compatibilityDetector.detectVersion(), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 2000)) // 2 second timeout
128
+ ]);
129
+ const compatibilityConfig = compatibilityDetector.getOptimalConfig(wranglerVersion);
130
+ const buildConfig = compatibilityDetector.getBuildConfig(wranglerVersion);
131
+
132
+ // Build compatibility flags string
133
+ if (compatibilityConfig.nodejs_compat !== undefined) {
134
+ compatibilityString = `nodejs_compat = ${compatibilityConfig.nodejs_compat}`;
135
+ } else if (compatibilityConfig.compatibility_flags) {
136
+ compatibilityString = `compatibility_flags = ${JSON.stringify(compatibilityConfig.compatibility_flags)}`;
137
+ }
138
+
139
+ // Add any additional build config from detector (if needed)
140
+ if (buildConfig.command && buildConfig.command !== "npm run build") {
141
+ buildString += `\n\n# Additional build config from Wrangler ${wranglerVersion}\n[build.v${wranglerVersion.split('.')[0]}]\ncommand = "${buildConfig.command}"`;
142
+ }
143
+ if (buildConfig.upload && buildConfig.upload.format && buildConfig.upload.format !== "modules") {
144
+ buildString += `\nformat_override = "${buildConfig.upload.format}"`;
145
+ }
146
+ } catch (error) {
147
+ // Fall back to default configuration if detection fails
148
+ console.warn(`⚠️ Wrangler compatibility detection failed, using defaults: ${error.message}`);
149
+ }
120
150
  return `# Cloudflare Workers Configuration for ${confirmedValues.displayName}
121
151
  name = "${confirmedValues.workerName}"
122
152
  main = "src/worker/index.js"
123
153
  compatibility_date = "${compatDate}"
124
- compatibility_flags = ["nodejs_compat"]
154
+ ${compatibilityString}${buildString}
125
155
 
126
156
  # Account configuration
127
157
  account_id = "${coreInputs.cloudflareAccountId}"
@@ -6,6 +6,8 @@
6
6
  import fs from 'fs/promises';
7
7
  import path from 'path';
8
8
  import { FrameworkConfig } from '../../utils/framework-config.js';
9
+ // import { ConfigurationValidator } from '../../lib/shared/utils/configuration-validator.js';
10
+
9
11
  export class ValidationHandler {
10
12
  constructor(options = {}) {
11
13
  this.strict = options.strict || false;
@@ -90,6 +92,19 @@ export class ValidationHandler {
90
92
  // Validate wrangler configuration
91
93
  const wranglerValidation = await this.validateWranglerConfig(servicePath);
92
94
  issues.push(...wranglerValidation.issues);
95
+
96
+ // Run comprehensive configuration validation using ConfigurationValidator
97
+ // Temporarily disabled due to import issues
98
+ // try {
99
+ // const configValidation = await ConfigurationValidator.validateServiceConfig(servicePath);
100
+ // if (!configValidation.isValid) {
101
+ // issues.push(...configValidation.errors);
102
+ // issues.push(...configValidation.warnings.map(w => `Warning: ${w}`));
103
+ // }
104
+ // } catch (error) {
105
+ // issues.push(`Configuration validation failed: ${error.message}`);
106
+ // }
107
+
93
108
  return {
94
109
  valid: issues.length === 0,
95
110
  issues,
@@ -3,18 +3,11 @@
3
3
  *
4
4
  * Re-exports FileManager from lib/ for library use.
5
5
  *
6
- * IMPORTANT PATH CALCULATION FOR NPM PACKAGE:
7
- * - Source: src/utils/file-manager.js (2 levels from root)
8
- * - Compiled: dist/utils/file-manager.js (2 levels from root)
9
- *
10
- * When installed via npm, structure is:
11
- * node_modules/@tamyla/clodo-framework/
12
- * dist/
13
- * utils/file-manager.js
14
- * lib/shared/utils/file-manager.js
15
- *
16
- * From dist/utils/, need to go UP one level (../) to reach lib/
17
- * Source path must account for compilation depth adjustment
6
+ * IMPORTANT PATH CALCULATION:
7
+ * - Source: src/utils/file-manager.js
8
+ * - Compiled: dist/utils/file-manager.js
9
+ * - Target: dist/lib/shared/utils/file-manager.js
10
+ * - From dist/utils/, path to dist/lib/ is ../lib/
18
11
  */
19
12
 
20
13
  export { FileManager } from '../lib/shared/utils/file-manager.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamyla/clodo-framework",
3
- "version": "4.0.11",
3
+ "version": "4.0.13",
4
4
  "description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -60,7 +60,7 @@
60
60
  "build:ci": "npm run prebuild:ci && babel src/ --out-dir dist/ && babel cli/ --out-dir dist/cli/ --ignore 'cli/**/*.test.js' && babel lib/ --out-dir dist/lib/ --ignore 'lib/**/*.test.js' && node -e \"const fs=require('fs'); fs.cpSync('ui-structures', 'dist/ui-structures', {recursive: true}); fs.cpSync('config', 'dist/config', {recursive: true});\" && npm run postbuild",
61
61
  "prebuild": "npm run clean && npm run type-check",
62
62
  "prebuild:ci": "npm run clean && npm run type-check",
63
- "postbuild": "npm run check:bundle && npm run check:imports && node scripts/utilities/fix-dist-imports.js && node scripts/utilities/validate-dist-imports.js",
63
+ "postbuild": "npm run check:bundle && npm run check:imports && node scripts/utilities/fix-dist-imports.js && node scripts/utilities/validate-dist-imports.js && npm run check:dist-imports",
64
64
  "clean": "rimraf dist",
65
65
  "clean:generated": "rimraf generated",
66
66
  "clean:all": "npm run clean && npm run clean:generated",
@@ -89,6 +89,7 @@
89
89
  "test:integration": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/integration/ --verbose",
90
90
  "test:comprehensive": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/comprehensive-suite.spec.js --verbose",
91
91
  "test:package": "node scripts/test-package.js",
92
+ "test:packaged-artifact": "node scripts/utilities/test-packaged-artifact.js",
92
93
  "prepublishOnly": "npm run build:ci",
93
94
  "test:coverage:cli": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/cli-integration/ --coverage",
94
95
  "test:coverage:deployment": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/deployment/ test/deployment-security.spec.js --coverage",
@@ -99,6 +100,7 @@
99
100
  "check:types": "npm run type-check",
100
101
  "check:bundle": "node scripts/utilities/check-bundle.js || echo 'Bundle check passed'",
101
102
  "check:imports": "node scripts/utilities/check-import-paths.js",
103
+ "check:dist-imports": "node scripts/utilities/check-dist-imports.js",
102
104
  "check:all": "npm run type-check && npm run test",
103
105
  "diagnose": "node scripts/framework-diagnostic.js",
104
106
  "analyze:bundle": "echo 'Bundle analysis not implemented yet'",