@tamyla/clodo-framework 3.1.10 → 3.1.11
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 +7 -0
- package/dist/bin/clodo-service-old.js +2 -2
- package/dist/bin/commands/create.js +1 -1
- package/dist/bin/commands/diagnose.js +1 -1
- package/dist/bin/commands/update.js +1 -1
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/database/enterprise-db-manager.js +3 -3
- package/dist/bin/deployment/enterprise-deploy.js +3 -3
- package/dist/bin/deployment/master-deploy.js +3 -3
- package/dist/bin/deployment/modular-enterprise-deploy.js +3 -3
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +1 -1
- package/dist/bin/deployment/modules/EnvironmentManager.js +2 -2
- package/dist/bin/portfolio/portfolio-manager.js +3 -3
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/service-management/create-service.js +1 -1
- package/dist/bin/service-management/init-service.js +1 -1
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/package.json +1 -6
- package/bin/README.md +0 -71
- package/bin/clodo-service.js +0 -72
- package/bin/database/README.md +0 -33
- package/bin/database/deployment-db-manager.js +0 -527
- package/bin/database/enterprise-db-manager.js +0 -738
- package/bin/database/wrangler-d1-manager.js +0 -775
- package/bin/security/security-cli.js +0 -117
- package/bin/service-management/README.md +0 -74
- package/bin/service-management/create-service.js +0 -129
- package/bin/service-management/init-service.js +0 -103
- package/bin/service-management/init-service.js.backup +0 -889
- package/bin/shared/cloudflare/domain-discovery.js +0 -637
- package/bin/shared/cloudflare/domain-manager.js +0 -952
- package/bin/shared/cloudflare/index.js +0 -8
- package/bin/shared/cloudflare/ops.js +0 -401
- package/bin/shared/config/ConfigurationManager.js +0 -539
- package/bin/shared/config/cache.js +0 -1230
- package/bin/shared/config/command-config-manager.js +0 -184
- package/bin/shared/config/index.js +0 -21
- package/bin/shared/config/manager.js +0 -315
- package/bin/shared/database/connection-manager.js +0 -374
- package/bin/shared/database/index.js +0 -7
- package/bin/shared/database/orchestrator.js +0 -727
- package/bin/shared/deployment/auditor.js +0 -970
- package/bin/shared/deployment/index.js +0 -10
- package/bin/shared/deployment/rollback-manager.js +0 -570
- package/bin/shared/deployment/validator.js +0 -779
- package/bin/shared/index.js +0 -32
- package/bin/shared/logging/Logger.js +0 -214
- package/bin/shared/monitoring/health-checker.js +0 -484
- package/bin/shared/monitoring/index.js +0 -8
- package/bin/shared/monitoring/memory-manager.js +0 -387
- package/bin/shared/monitoring/production-monitor.js +0 -403
- package/bin/shared/production-tester/api-tester.js +0 -82
- package/bin/shared/production-tester/auth-tester.js +0 -132
- package/bin/shared/production-tester/core.js +0 -197
- package/bin/shared/production-tester/database-tester.js +0 -109
- package/bin/shared/production-tester/index.js +0 -77
- package/bin/shared/production-tester/load-tester.js +0 -131
- package/bin/shared/production-tester/performance-tester.js +0 -103
- package/bin/shared/security/api-token-manager.js +0 -312
- package/bin/shared/security/index.js +0 -8
- package/bin/shared/security/secret-generator.js +0 -942
- package/bin/shared/security/secure-token-manager.js +0 -398
- package/bin/shared/utils/ErrorHandler.js +0 -675
- package/bin/shared/utils/error-recovery.js +0 -245
- package/bin/shared/utils/file-manager.js +0 -162
- package/bin/shared/utils/formatters.js +0 -247
- package/bin/shared/utils/graceful-shutdown-manager.js +0 -390
- package/bin/shared/utils/index.js +0 -19
- package/bin/shared/utils/interactive-prompts.js +0 -146
- package/bin/shared/utils/interactive-utils.js +0 -530
- package/bin/shared/utils/rate-limiter.js +0 -246
- package/bin/shared/validation/ValidationRegistry.js +0 -143
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error Recovery Module - UNIFIED
|
|
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.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export class ErrorRecoveryManager {
|
|
15
|
-
constructor(options = {}) {
|
|
16
|
-
this.options = options;
|
|
17
|
-
this.config = null;
|
|
18
|
-
this.circuitStates = new Map(); // service -> { failures, lastFailure, state }
|
|
19
|
-
this.retryStates = new Map(); // operation -> retry count
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Initialize with framework configuration
|
|
24
|
-
*/
|
|
25
|
-
async initialize() {
|
|
26
|
-
// Import framework config for consistent timing and retry settings
|
|
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
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Execute operation with error recovery
|
|
54
|
-
*/
|
|
55
|
-
async executeWithRecovery(operation, options = {}) {
|
|
56
|
-
const config = { ...this.config, ...options };
|
|
57
|
-
const operationId = this.getOperationId(operation);
|
|
58
|
-
|
|
59
|
-
// Check circuit breaker
|
|
60
|
-
if (this.isCircuitOpen(operationId)) {
|
|
61
|
-
if (config.gracefulDegradation) {
|
|
62
|
-
return this.executeGracefulFallback(operation, config);
|
|
63
|
-
}
|
|
64
|
-
throw new Error(`Circuit breaker open for operation: ${operationId}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let lastError;
|
|
68
|
-
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
69
|
-
try {
|
|
70
|
-
const result = await operation();
|
|
71
|
-
this.recordSuccess(operationId);
|
|
72
|
-
return result;
|
|
73
|
-
} catch (error) {
|
|
74
|
-
lastError = error;
|
|
75
|
-
this.recordFailure(operationId, error);
|
|
76
|
-
|
|
77
|
-
if (attempt < config.maxRetries) {
|
|
78
|
-
const delay = this.calculateRetryDelay(attempt, config.retryDelay);
|
|
79
|
-
await this.delay(delay);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// All retries exhausted
|
|
85
|
-
if (config.gracefulDegradation) {
|
|
86
|
-
return this.executeGracefulFallback(operation, config);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
throw lastError;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Check if circuit breaker is open
|
|
94
|
-
*/
|
|
95
|
-
isCircuitOpen(operationId) {
|
|
96
|
-
const state = this.circuitStates.get(operationId);
|
|
97
|
-
if (!state) return false;
|
|
98
|
-
|
|
99
|
-
if (state.state === 'open') {
|
|
100
|
-
// Check if timeout has passed
|
|
101
|
-
if (Date.now() - state.lastFailure > this.config.circuitBreakerTimeout) {
|
|
102
|
-
state.state = 'half-open';
|
|
103
|
-
state.failures = 0;
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Record operation success
|
|
114
|
-
*/
|
|
115
|
-
recordSuccess(operationId) {
|
|
116
|
-
const state = this.circuitStates.get(operationId);
|
|
117
|
-
if (state) {
|
|
118
|
-
if (state.state === 'half-open') {
|
|
119
|
-
state.state = 'closed';
|
|
120
|
-
state.failures = 0;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Record operation failure
|
|
127
|
-
*/
|
|
128
|
-
recordFailure(operationId, error) {
|
|
129
|
-
let state = this.circuitStates.get(operationId);
|
|
130
|
-
if (!state) {
|
|
131
|
-
state = { failures: 0, lastFailure: 0, state: 'closed' };
|
|
132
|
-
this.circuitStates.set(operationId, state);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
state.failures++;
|
|
136
|
-
state.lastFailure = Date.now();
|
|
137
|
-
|
|
138
|
-
if (state.failures >= this.config.circuitBreakerThreshold) {
|
|
139
|
-
state.state = 'open';
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Calculate retry delay with exponential backoff
|
|
145
|
-
*/
|
|
146
|
-
calculateRetryDelay(attempt, baseDelay) {
|
|
147
|
-
const exponentialDelay = baseDelay * Math.pow(2, attempt);
|
|
148
|
-
const jitter = Math.random() * 0.1 * exponentialDelay;
|
|
149
|
-
return Math.min(exponentialDelay + jitter, 30000); // Max 30 seconds
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Execute graceful fallback
|
|
154
|
-
*/
|
|
155
|
-
async executeGracefulFallback(operation, config) {
|
|
156
|
-
console.warn(`Executing graceful fallback for operation`);
|
|
157
|
-
|
|
158
|
-
// Try to execute with reduced functionality
|
|
159
|
-
try {
|
|
160
|
-
// For deployment operations, try a simplified version
|
|
161
|
-
if (operation.name && operation.name.includes('deploy')) {
|
|
162
|
-
return { success: false, degraded: true, message: 'Operation executed in degraded mode' };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// For data operations, return cached or default data
|
|
166
|
-
if (operation.name && operation.name.includes('fetch')) {
|
|
167
|
-
return { data: [], cached: true, degraded: true };
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Default fallback
|
|
171
|
-
return { success: false, degraded: true, fallback: true };
|
|
172
|
-
|
|
173
|
-
} catch (fallbackError) {
|
|
174
|
-
console.error('Graceful fallback also failed:', fallbackError);
|
|
175
|
-
throw fallbackError;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Get unique operation ID
|
|
181
|
-
*/
|
|
182
|
-
getOperationId(operation) {
|
|
183
|
-
if (typeof operation === 'function' && operation.name) {
|
|
184
|
-
return operation.name;
|
|
185
|
-
}
|
|
186
|
-
return `operation_${Date.now()}_${Math.random()}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Utility delay function
|
|
191
|
-
*/
|
|
192
|
-
delay(ms) {
|
|
193
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Get circuit breaker status
|
|
198
|
-
*/
|
|
199
|
-
getCircuitStatus(operationId) {
|
|
200
|
-
const state = this.circuitStates.get(operationId);
|
|
201
|
-
if (!state) {
|
|
202
|
-
return { state: 'closed', failures: 0 };
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
state: state.state,
|
|
206
|
-
failures: state.failures,
|
|
207
|
-
lastFailure: state.lastFailure,
|
|
208
|
-
timeSinceLastFailure: Date.now() - state.lastFailure
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Reset circuit breaker
|
|
214
|
-
*/
|
|
215
|
-
resetCircuit(operationId) {
|
|
216
|
-
this.circuitStates.delete(operationId);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get all circuit statuses
|
|
221
|
-
*/
|
|
222
|
-
getAllCircuitStatuses() {
|
|
223
|
-
const statuses = {};
|
|
224
|
-
for (const [operationId, state] of this.circuitStates) {
|
|
225
|
-
statuses[operationId] = this.getCircuitStatus(operationId);
|
|
226
|
-
}
|
|
227
|
-
return statuses;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Retry wrapper for functions
|
|
233
|
-
*/
|
|
234
|
-
export function withRetry(fn, options = {}) {
|
|
235
|
-
const recovery = new ErrorRecoveryManager(options);
|
|
236
|
-
return (...args) => recovery.executeWithRecovery(() => fn(...args), options);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Circuit breaker wrapper for functions
|
|
241
|
-
*/
|
|
242
|
-
export function withCircuitBreaker(fn, options = {}) {
|
|
243
|
-
const recovery = new ErrorRecoveryManager(options);
|
|
244
|
-
return (...args) => recovery.executeWithRecovery(() => fn(...args), options);
|
|
245
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
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();
|
|
@@ -1,247 +0,0 @@
|
|
|
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
|
-
};
|