@tamyla/clodo-framework 3.1.4 → 3.1.8

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 (62) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/bin/clodo-service.js +29 -989
  3. package/bin/database/enterprise-db-manager.js +7 -5
  4. package/bin/security/security-cli.js +0 -0
  5. package/bin/service-management/create-service.js +0 -0
  6. package/bin/service-management/init-service.js +0 -0
  7. package/bin/shared/cloudflare/domain-discovery.js +11 -10
  8. package/bin/shared/cloudflare/ops.js +1 -1
  9. package/bin/shared/config/ConfigurationManager.js +539 -0
  10. package/bin/shared/config/index.js +13 -1
  11. package/bin/shared/database/connection-manager.js +2 -2
  12. package/bin/shared/database/orchestrator.js +5 -4
  13. package/bin/shared/deployment/auditor.js +9 -8
  14. package/bin/shared/logging/Logger.js +214 -0
  15. package/bin/shared/monitoring/production-monitor.js +21 -9
  16. package/bin/shared/utils/ErrorHandler.js +675 -0
  17. package/bin/shared/utils/error-recovery.js +33 -13
  18. package/bin/shared/utils/file-manager.js +162 -0
  19. package/bin/shared/utils/formatters.js +247 -0
  20. package/bin/shared/utils/index.js +14 -4
  21. package/bin/shared/validation/ValidationRegistry.js +143 -0
  22. package/dist/deployment/auditor.js +23 -8
  23. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +426 -0
  24. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +401 -0
  25. package/dist/deployment/orchestration/PortfolioOrchestrator.js +273 -0
  26. package/dist/deployment/orchestration/SingleServiceOrchestrator.js +231 -0
  27. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +662 -0
  28. package/dist/deployment/orchestration/index.js +17 -0
  29. package/dist/index.js +12 -0
  30. package/dist/orchestration/modules/DomainResolver.js +8 -6
  31. package/dist/orchestration/multi-domain-orchestrator.js +13 -1
  32. package/dist/security/index.js +2 -2
  33. package/dist/service-management/ConfirmationEngine.js +8 -7
  34. package/dist/service-management/ErrorTracker.js +7 -2
  35. package/dist/service-management/InputCollector.js +31 -16
  36. package/dist/service-management/ServiceCreator.js +22 -7
  37. package/dist/service-management/ServiceInitializer.js +12 -18
  38. package/dist/shared/cloudflare/domain-discovery.js +11 -10
  39. package/dist/shared/cloudflare/ops.js +1 -1
  40. package/dist/shared/config/ConfigurationManager.js +519 -0
  41. package/dist/shared/config/index.js +5 -1
  42. package/dist/shared/database/connection-manager.js +2 -2
  43. package/dist/shared/database/orchestrator.js +13 -4
  44. package/dist/shared/deployment/auditor.js +23 -8
  45. package/dist/shared/logging/Logger.js +209 -0
  46. package/dist/shared/monitoring/production-monitor.js +24 -8
  47. package/dist/{utils → shared/utils}/ErrorHandler.js +306 -28
  48. package/dist/shared/utils/error-recovery.js +33 -13
  49. package/dist/shared/utils/file-manager.js +155 -0
  50. package/dist/shared/utils/formatters.js +215 -0
  51. package/dist/shared/utils/index.js +14 -4
  52. package/dist/shared/validation/ValidationRegistry.js +126 -0
  53. package/dist/utils/config/unified-config-manager.js +14 -12
  54. package/dist/utils/deployment/config-cache.js +3 -1
  55. package/dist/utils/deployment/secret-generator.js +32 -29
  56. package/dist/utils/framework-config.js +6 -3
  57. package/dist/utils/ui-structures-loader.js +3 -0
  58. package/dist/worker/integration.js +11 -1
  59. package/package.json +31 -3
  60. package/dist/config/FeatureManager.js +0 -426
  61. package/dist/config/features.js +0 -230
  62. package/dist/utils/error-recovery.js +0 -240
@@ -1,6 +1,14 @@
1
1
  /**
2
- * Error Recovery Module
2
+ * Error Recovery Module - UNIFIED
3
3
  * Implements circuit breakers, retries, and graceful degradation
4
+ *
5
+ * Consolidation: Oct 26, 2025 - Phase 3.2.3a
6
+ *
7
+ * Merged from:
8
+ * - src/utils/error-recovery.js (225 lines)
9
+ * - bin/shared/utils/error-recovery.js (225 lines - DUPLICATE)
10
+ *
11
+ * This is the canonical version. All imports should use this version.
4
12
  */
5
13
 
6
14
  export class ErrorRecoveryManager {
@@ -16,17 +24,29 @@ export class ErrorRecoveryManager {
16
24
  */
17
25
  async initialize() {
18
26
  // Import framework config for consistent timing and retry settings
19
- const { frameworkConfig } = await import('../../../dist/utils/framework-config.js');
20
- const timing = frameworkConfig.getTiming();
21
-
22
- this.config = {
23
- maxRetries: this.options.maxRetries || timing.retryAttempts,
24
- retryDelay: this.options.retryDelay || timing.retryDelay,
25
- circuitBreakerThreshold: this.options.circuitBreakerThreshold || timing.circuitBreakerThreshold,
26
- circuitBreakerTimeout: this.options.circuitBreakerTimeout || timing.circuitBreakerTimeout,
27
- gracefulDegradation: this.options.gracefulDegradation !== false,
28
- ...this.options
29
- };
27
+ try {
28
+ const { frameworkConfig } = await import('./framework-config.js');
29
+ const timing = frameworkConfig.getTiming();
30
+
31
+ this.config = {
32
+ maxRetries: this.options.maxRetries || timing.retryAttempts,
33
+ retryDelay: this.options.retryDelay || timing.retryDelay,
34
+ circuitBreakerThreshold: this.options.circuitBreakerThreshold || timing.circuitBreakerThreshold,
35
+ circuitBreakerTimeout: this.options.circuitBreakerTimeout || timing.circuitBreakerTimeout,
36
+ gracefulDegradation: this.options.gracefulDegradation !== false,
37
+ ...this.options
38
+ };
39
+ } catch (error) {
40
+ // Fallback to defaults if framework config not available
41
+ this.config = {
42
+ maxRetries: this.options.maxRetries || 3,
43
+ retryDelay: this.options.retryDelay || 1000,
44
+ circuitBreakerThreshold: this.options.circuitBreakerThreshold || 5,
45
+ circuitBreakerTimeout: this.options.circuitBreakerTimeout || 60000,
46
+ gracefulDegradation: this.options.gracefulDegradation !== false,
47
+ ...this.options
48
+ };
49
+ }
30
50
  }
31
51
 
32
52
  /**
@@ -222,4 +242,4 @@ export function withRetry(fn, options = {}) {
222
242
  export function withCircuitBreaker(fn, options = {}) {
223
243
  const recovery = new ErrorRecoveryManager(options);
224
244
  return (...args) => recovery.executeWithRecovery(() => fn(...args), options);
225
- }
245
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Centralized File Manager
3
+ * Replaces: 12 scattered file operation implementations
4
+ * Savings: 200+ lines
5
+ */
6
+
7
+ import {
8
+ readFileSync, writeFileSync, appendFileSync,
9
+ existsSync, mkdirSync, statSync
10
+ } from 'fs';
11
+ import { dirname, basename } from 'path';
12
+
13
+ export class FileManager {
14
+ constructor(options = {}) {
15
+ this.cache = new Map();
16
+ this.enableCache = options.enableCache !== false;
17
+ this.createBackups = options.createBackups !== false;
18
+ this.backupDir = options.backupDir || '.backups';
19
+ }
20
+
21
+ /**
22
+ * Read JSON configuration file
23
+ */
24
+ readConfig(path, defaultValue = null) {
25
+ try {
26
+ if (this.enableCache && this.cache.has(path)) {
27
+ return this.cache.get(path);
28
+ }
29
+
30
+ if (!existsSync(path)) {
31
+ if (defaultValue !== null) return defaultValue;
32
+ throw new Error(`Configuration file not found: ${path}`);
33
+ }
34
+
35
+ const content = readFileSync(path, 'utf8');
36
+ const data = JSON.parse(content);
37
+
38
+ if (this.enableCache) {
39
+ this.cache.set(path, data);
40
+ }
41
+
42
+ return data;
43
+ } catch (error) {
44
+ if (error.message.includes('JSON')) {
45
+ throw new Error(`Invalid JSON in configuration file: ${error.message}`);
46
+ }
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Write JSON configuration file with optional backup
53
+ */
54
+ writeConfig(path, data, options = {}) {
55
+ try {
56
+ // Create backup if needed
57
+ if (this.createBackups && existsSync(path)) {
58
+ this.createBackup(path);
59
+ }
60
+
61
+ // Ensure directory exists
62
+ this.ensureDir(dirname(path));
63
+
64
+ // Write file
65
+ const content = JSON.stringify(data, null, 2);
66
+ writeFileSync(path, content, 'utf8');
67
+
68
+ // Clear cache
69
+ if (this.enableCache) {
70
+ this.cache.delete(path);
71
+ }
72
+
73
+ return { success: true, path };
74
+ } catch (error) {
75
+ throw new Error(`Failed to write configuration: ${error.message}`);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Read text file
81
+ */
82
+ readFile(path) {
83
+ if (!existsSync(path)) {
84
+ throw new Error(`File not found: ${path}`);
85
+ }
86
+ return readFileSync(path, 'utf8');
87
+ }
88
+
89
+ /**
90
+ * Write text file
91
+ */
92
+ writeFile(path, content) {
93
+ this.ensureDir(dirname(path));
94
+ writeFileSync(path, content, 'utf8');
95
+ }
96
+
97
+ /**
98
+ * Append to file
99
+ */
100
+ appendFile(path, content) {
101
+ this.ensureDir(dirname(path));
102
+ appendFileSync(path, content, 'utf8');
103
+ }
104
+
105
+ /**
106
+ * Check if file exists
107
+ */
108
+ exists(path) {
109
+ return existsSync(path);
110
+ }
111
+
112
+ /**
113
+ * Ensure directory exists
114
+ */
115
+ ensureDir(dir) {
116
+ if (!existsSync(dir)) {
117
+ mkdirSync(dir, { recursive: true });
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Create backup of file
123
+ */
124
+ createBackup(path) {
125
+ this.ensureDir(this.backupDir);
126
+
127
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
128
+ const fileName = basename(path);
129
+ const backupPath = `${this.backupDir}/${fileName}.${timestamp}.bak`;
130
+
131
+ const content = readFileSync(path, 'utf8');
132
+ writeFileSync(backupPath, content, 'utf8');
133
+
134
+ return backupPath;
135
+ }
136
+
137
+ /**
138
+ * Get file stats
139
+ */
140
+ getStats(path) {
141
+ if (!existsSync(path)) {
142
+ return null;
143
+ }
144
+ return statSync(path);
145
+ }
146
+
147
+ /**
148
+ * Clear cache
149
+ */
150
+ clearCache(path = null) {
151
+ if (path) {
152
+ this.cache.delete(path);
153
+ } else {
154
+ this.cache.clear();
155
+ }
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Export singleton instance
161
+ */
162
+ export const fileManager = new FileManager();
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Data Formatters for Clodo Framework
3
+ * Centralizes all data transformation logic
4
+ * Replaces: 5 scattered formatting implementations
5
+ * Savings: 150+ lines
6
+ */
7
+
8
+ /**
9
+ * Name formatting utilities
10
+ */
11
+ export const NameFormatters = {
12
+ /**
13
+ * Convert kebab-case to Display Case
14
+ * Example: 'my-service' → 'My Service'
15
+ */
16
+ toDisplayName(kebabCase) {
17
+ if (!kebabCase) return '';
18
+ return kebabCase
19
+ .replace(/-/g, ' ')
20
+ .replace(/\b\w/g, l => l.toUpperCase());
21
+ },
22
+
23
+ /**
24
+ * Convert camelCase to kebab-case
25
+ * Example: 'myService' → 'my-service'
26
+ */
27
+ toKebabCase(camelCase) {
28
+ if (!camelCase) return '';
29
+ return camelCase
30
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
31
+ .replace(/^([A-Z])([A-Z])/g, '$1-$2')
32
+ .toLowerCase();
33
+ },
34
+
35
+ /**
36
+ * Convert kebab-case to camelCase
37
+ * Example: 'my-service' → 'myService'
38
+ */
39
+ toCamelCase(kebabCase) {
40
+ if (!kebabCase) return '';
41
+ return kebabCase
42
+ .split('-')
43
+ .map((part, i) =>
44
+ i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)
45
+ )
46
+ .join('');
47
+ },
48
+
49
+ /**
50
+ * Convert snake_case to camelCase
51
+ * Example: 'my_service' → 'myService'
52
+ */
53
+ snakeToCamel(snakeCase) {
54
+ if (!snakeCase) return '';
55
+ return snakeCase
56
+ .split('_')
57
+ .map((part, i) =>
58
+ i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)
59
+ )
60
+ .join('');
61
+ }
62
+ };
63
+
64
+ /**
65
+ * URL formatting utilities
66
+ */
67
+ export const UrlFormatters = {
68
+ /**
69
+ * Build service URL
70
+ * Example: buildServiceUrl('api', 'example.com', 'production')
71
+ * → 'https://api.example.com'
72
+ */
73
+ buildServiceUrl(serviceName, domain, environment = 'production') {
74
+ if (!serviceName || !domain) return '';
75
+
76
+ const prefix = environment === 'production'
77
+ ? serviceName
78
+ : `${serviceName}-${environment.substring(0, 3)}`;
79
+
80
+ return `https://${prefix}.${domain}`;
81
+ },
82
+
83
+ /**
84
+ * Build production URL
85
+ */
86
+ buildProductionUrl(serviceName, domain) {
87
+ return this.buildServiceUrl(serviceName, domain, 'production');
88
+ },
89
+
90
+ /**
91
+ * Build staging URL
92
+ */
93
+ buildStagingUrl(serviceName, domain) {
94
+ return this.buildServiceUrl(serviceName, domain, 'staging');
95
+ },
96
+
97
+ /**
98
+ * Build development URL
99
+ */
100
+ buildDevUrl(serviceName, domain) {
101
+ return this.buildServiceUrl(serviceName, domain, 'development');
102
+ },
103
+
104
+ /**
105
+ * Build API endpoint URL
106
+ */
107
+ buildApiUrl(serviceName, domain, path = '') {
108
+ const baseUrl = this.buildProductionUrl(serviceName, domain);
109
+ return path ? `${baseUrl}${path}` : baseUrl;
110
+ }
111
+ };
112
+
113
+ /**
114
+ * Resource name formatters
115
+ */
116
+ export const ResourceFormatters = {
117
+ /**
118
+ * Format database name
119
+ * Example: 'my-service' → 'my-service-db'
120
+ */
121
+ databaseName(serviceName) {
122
+ if (!serviceName) return '';
123
+ return `${serviceName}-db`;
124
+ },
125
+
126
+ /**
127
+ * Format worker name
128
+ * Example: 'my-service' → 'my-service-worker'
129
+ */
130
+ workerName(serviceName) {
131
+ if (!serviceName) return '';
132
+ return `${serviceName}-worker`;
133
+ },
134
+
135
+ /**
136
+ * Format service directory
137
+ * Example: 'my-service' → './services/my-service'
138
+ */
139
+ serviceDirectory(serviceName) {
140
+ if (!serviceName) return '';
141
+ return `./services/${serviceName}`;
142
+ },
143
+
144
+ /**
145
+ * Format configuration key
146
+ * Example: 'cloudflareApiToken' → 'cloudflare-api-token'
147
+ */
148
+ configKey(camelCase) {
149
+ return NameFormatters.toKebabCase(camelCase);
150
+ }
151
+ };
152
+
153
+ /**
154
+ * Environment-related formatters
155
+ */
156
+ export const EnvironmentFormatters = {
157
+ /**
158
+ * Get environment variable prefix
159
+ * Example: 'production' → 'PROD_'
160
+ */
161
+ getEnvPrefix(environment) {
162
+ switch (environment) {
163
+ case 'production':
164
+ return 'PROD_';
165
+ case 'staging':
166
+ return 'STAGING_';
167
+ case 'development':
168
+ return 'DEV_';
169
+ default:
170
+ return 'APP_';
171
+ }
172
+ },
173
+
174
+ /**
175
+ * Get log level for environment
176
+ * Example: 'production' → 'warn'
177
+ */
178
+ getLogLevel(environment) {
179
+ switch (environment) {
180
+ case 'production':
181
+ return 'warn';
182
+ case 'staging':
183
+ return 'info';
184
+ case 'development':
185
+ return 'debug';
186
+ default:
187
+ return 'info';
188
+ }
189
+ },
190
+
191
+ /**
192
+ * Get CORS policy for environment
193
+ */
194
+ getCorsPolicy(domain, environment) {
195
+ switch (environment) {
196
+ case 'production':
197
+ return `https://${domain}`;
198
+ case 'staging':
199
+ return `https://${domain}`;
200
+ case 'development':
201
+ return '*'; // Allow all in development
202
+ default:
203
+ return '*';
204
+ }
205
+ }
206
+ };
207
+
208
+ /**
209
+ * Version formatting
210
+ */
211
+ export const VersionFormatters = {
212
+ /**
213
+ * Normalize version string
214
+ */
215
+ normalize(version) {
216
+ const match = version.match(/(\d+\.\d+\.\d+)/);
217
+ return match ? match[1] : '1.0.0';
218
+ },
219
+
220
+ /**
221
+ * Bump version
222
+ */
223
+ bump(version, type = 'patch') {
224
+ const [major, minor, patch] = version.split('.').map(Number);
225
+
226
+ switch (type) {
227
+ case 'major':
228
+ return `${major + 1}.0.0`;
229
+ case 'minor':
230
+ return `${major}.${minor + 1}.0`;
231
+ case 'patch':
232
+ default:
233
+ return `${major}.${minor}.${patch + 1}`;
234
+ }
235
+ }
236
+ };
237
+
238
+ /**
239
+ * Convenience exports - combine all formatters
240
+ */
241
+ export const Formatters = {
242
+ ...NameFormatters,
243
+ ...UrlFormatters,
244
+ ...ResourceFormatters,
245
+ ...EnvironmentFormatters,
246
+ ...VersionFormatters
247
+ };
@@ -3,7 +3,17 @@
3
3
  * Exports all general utility functions and managers
4
4
  */
5
5
 
6
- export { InteractivePrompts } from './interactive-prompts.js';
7
- export { ErrorRecovery } from './error-recovery.js';
8
- export { GracefulShutdownManager } from './graceful-shutdown-manager.js';
9
- export { RateLimiter } from './rate-limiter.js';
6
+ // Interactive prompts utilities
7
+ export { askUser, askYesNo, askChoice, askMultiChoice, closePrompts, showProgress, askPassword } from './interactive-prompts.js';
8
+
9
+ // Error recovery and handling
10
+ export { ErrorRecoveryManager, withRetry, withCircuitBreaker } from './error-recovery.js';
11
+
12
+ // Graceful shutdown
13
+ export { GracefulShutdownManager, getShutdownManager, initializeGracefulShutdown, withGracefulShutdown } from './graceful-shutdown-manager.js';
14
+
15
+ // Rate limiting
16
+ export { executeWithRateLimit, queueRequest, getRateLimitStatus, clearQueues, RATE_LIMITS } from './rate-limiter.js';
17
+
18
+ // Unified error handling module (Phase 3.2.3d - consolidated from 5 sources)
19
+ export { default as ErrorHandler, createErrorResponse, createContextualError, createErrorHandler } from './ErrorHandler.js';
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Unified Validation Registry
3
+ * Centralizes all validation logic
4
+ * Replaces: Fragmented validation across 3+ files
5
+ * Savings: 100+ lines
6
+ */
7
+
8
+ /**
9
+ * Import validators from src/utils (source of truth)
10
+ */
11
+ import {
12
+ validateServiceName,
13
+ validateDomainName,
14
+ validateCloudflareToken,
15
+ validateCloudflareId,
16
+ validateServiceType,
17
+ validateEnvironment
18
+ } from '../../../src/utils/validation.js';
19
+
20
+ /**
21
+ * Validation Registry - Single source of truth for all validators
22
+ */
23
+ export class ValidationRegistry {
24
+ /**
25
+ * Standard validation rules
26
+ */
27
+ static RULES = {
28
+ // Service configuration
29
+ serviceName: {
30
+ validator: validateServiceName,
31
+ message: 'Service name must be 3-50 characters, lowercase with hyphens only'
32
+ },
33
+
34
+ domainName: {
35
+ validator: validateDomainName,
36
+ message: 'Domain name must be valid (e.g., example.com)'
37
+ },
38
+
39
+ serviceType: {
40
+ validator: validateServiceType,
41
+ message: 'Service type must be one of: data-service, auth-service, content-service, api-gateway, generic'
42
+ },
43
+
44
+ environment: {
45
+ validator: validateEnvironment,
46
+ message: 'Environment must be one of: development, staging, production'
47
+ },
48
+
49
+ // Cloudflare configuration
50
+ cloudflareToken: {
51
+ validator: validateCloudflareToken,
52
+ message: 'Cloudflare API token must be at least 20 characters'
53
+ },
54
+
55
+ cloudflareAccountId: {
56
+ validator: validateCloudflareId,
57
+ message: 'Cloudflare Account ID must be 32 hexadecimal characters'
58
+ },
59
+
60
+ cloudflareZoneId: {
61
+ validator: validateCloudflareId,
62
+ message: 'Cloudflare Zone ID must be 32 hexadecimal characters'
63
+ }
64
+ };
65
+
66
+ /**
67
+ * Validate a value against a registered rule
68
+ * Returns: { valid: boolean, message: string }
69
+ */
70
+ static validate(ruleName, value) {
71
+ const rule = this.RULES[ruleName];
72
+
73
+ if (!rule) {
74
+ return {
75
+ valid: false,
76
+ message: `Unknown validation rule: ${ruleName}`
77
+ };
78
+ }
79
+
80
+ try {
81
+ const isValid = rule.validator(value);
82
+ return {
83
+ valid: isValid,
84
+ message: isValid ? 'Valid' : rule.message
85
+ };
86
+ } catch (error) {
87
+ return {
88
+ valid: false,
89
+ message: `Validation error: ${error.message}`
90
+ };
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Validate multiple fields
96
+ * Returns: { valid: boolean, errors: Map<fieldName, message> }
97
+ */
98
+ static validateMultiple(fields) {
99
+ const errors = new Map();
100
+
101
+ for (const [fieldName, value] of Object.entries(fields)) {
102
+ const result = this.validate(fieldName, value);
103
+ if (!result.valid) {
104
+ errors.set(fieldName, result.message);
105
+ }
106
+ }
107
+
108
+ return {
109
+ valid: errors.size === 0,
110
+ errors
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Register custom validation rule
116
+ * Usage: ValidationRegistry.register('customRule', customValidator, 'Custom error message')
117
+ */
118
+ static register(ruleName, validator, message = 'Invalid value') {
119
+ this.RULES[ruleName] = {
120
+ validator,
121
+ message
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Get all registered rules
127
+ */
128
+ static getAllRules() {
129
+ return Object.keys(this.RULES);
130
+ }
131
+
132
+ /**
133
+ * Get rule details
134
+ */
135
+ static getRule(ruleName) {
136
+ return this.RULES[ruleName];
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Export for convenience
142
+ */
143
+ export const validators = ValidationRegistry;