@tamyla/clodo-framework 3.2.5 → 4.0.0
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 +34 -0
- package/dist/config/domains.js +20 -1
- package/dist/database/database-orchestrator.js +1 -1
- package/dist/deployment/index.js +4 -0
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +1 -1
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +1 -1
- package/dist/lib/shared/database/connection-manager.js +2 -8
- package/dist/lib/shared/database/orchestrator.js +2 -4
- package/dist/lib/shared/security/secret-generator.js +2 -4
- package/dist/lib/shared/security/secure-token-manager.js +1 -3
- package/dist/lib/shared/utils/error-recovery.js +2 -4
- package/dist/lib/shared/utils/framework-config.js +567 -0
- package/dist/lib/shared/utils/graceful-shutdown-manager.js +2 -4
- package/dist/lib/shared/utils/index.js +4 -1
- package/dist/security/index.js +3 -1
- package/dist/service-management/handlers/ValidationHandler.js +42 -1
- package/dist/service-management/routing/DomainRouteMapper.js +1 -1
- package/dist/service-management/routing/RouteGenerator.js +1 -1
- package/dist/utils/deployment/config-cache.js +1 -1
- package/dist/utils/deployment/index.js +1 -1
- package/dist/utils/framework-config.js +6 -6
- package/dist/utils/graceful-shutdown-manager.js +2 -2
- package/dist/utils/index.js +2 -2
- package/dist/worker/features.js +43 -0
- package/dist/worker/integration.js +5 -5
- package/dist/worker-entry.js +28 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
# [4.0.0](https://github.com/tamylaa/clodo-framework/compare/v3.2.5...v4.0.0) (2025-12-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* resolve remaining 5 import path issues in dist files ([383a625](https://github.com/tamylaa/clodo-framework/commit/383a6257910546f4d9a9f1442f4005c0cc166704))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Code Refactoring
|
|
10
|
+
|
|
11
|
+
* replace band-aid import fixes with proper wrapper pattern ([5385ef2](https://github.com/tamylaa/clodo-framework/commit/5385ef23e9a9b8be26fce9d9fe6df2184667168c))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### BREAKING CHANGES
|
|
15
|
+
|
|
16
|
+
* from previous approach:
|
|
17
|
+
- Removed post-build path manipulation that was fragile and hard to maintain
|
|
18
|
+
- Implemented proper wrapper re-export pattern consistent with existing codebase
|
|
19
|
+
|
|
20
|
+
Changes:
|
|
21
|
+
- Create lib/shared/utils/framework-config.js wrapper that re-exports from src/utils
|
|
22
|
+
- Update connection-manager.js to import from ../utils/framework-config.js wrapper
|
|
23
|
+
- Update secret-generator.js to import from ../utils/framework-config.js wrapper
|
|
24
|
+
- Update graceful-shutdown-manager.js to import from ./framework-config.js wrapper
|
|
25
|
+
- Replace complex import path fixes with single minimal transformation in fix-dist-imports.js
|
|
26
|
+
- Maintains all 23 exports working correctly
|
|
27
|
+
|
|
28
|
+
Benefits:
|
|
29
|
+
Clean, maintainable architecture following existing wrapper patterns
|
|
30
|
+
No circular dependencies
|
|
31
|
+
Imports work correctly in both source and compiled dist/
|
|
32
|
+
Easier to debug and understand import resolution
|
|
33
|
+
Scales better as codebase grows
|
|
34
|
+
|
|
1
35
|
## [3.2.5](https://github.com/tamylaa/clodo-framework/compare/v3.2.4...v3.2.5) (2025-12-06)
|
|
2
36
|
|
|
3
37
|
|
package/dist/config/domains.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
// Simple inline logger to avoid circular dependency with index.js
|
|
2
|
-
|
|
2
|
+
// Import only pure JS utilities (no Node.js dependencies)
|
|
3
|
+
const deepMerge = (target, source) => {
|
|
4
|
+
const result = {
|
|
5
|
+
...target
|
|
6
|
+
};
|
|
7
|
+
for (const key in source) {
|
|
8
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
9
|
+
result[key] = deepMerge(result[key] || {}, source[key]);
|
|
10
|
+
} else {
|
|
11
|
+
result[key] = source[key];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
const validateRequired = (obj, requiredFields) => {
|
|
17
|
+
const missing = requiredFields.filter(field => !obj[field]);
|
|
18
|
+
if (missing.length > 0) {
|
|
19
|
+
throw new Error(`Missing required fields: ${missing.join(', ')}`);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
3
22
|
const logger = {
|
|
4
23
|
info: (message, ...args) => console.log(`[DomainConfig] ${message}`, ...args),
|
|
5
24
|
error: (message, ...args) => console.error(`[DomainConfig] ${message}`, ...args),
|
|
@@ -128,7 +128,7 @@ export class DatabaseOrchestrator {
|
|
|
128
128
|
// Import framework config for consistent timing and database settings
|
|
129
129
|
const {
|
|
130
130
|
frameworkConfig
|
|
131
|
-
} = await import('../utils/framework-config.js');
|
|
131
|
+
} = await import('../lib/shared/utils/framework-config.js');
|
|
132
132
|
const timing = frameworkConfig.getTiming();
|
|
133
133
|
const database = frameworkConfig.getDatabaseConfig();
|
|
134
134
|
const configPaths = frameworkConfig.getPaths();
|
package/dist/deployment/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// Deployment Module
|
|
2
2
|
// Core deployment components for the Clodo Framework
|
|
3
3
|
|
|
4
|
+
// Validation and Auditing
|
|
5
|
+
export { DeploymentValidator } from './validator.js';
|
|
6
|
+
export { DeploymentAuditor } from './auditor.js';
|
|
7
|
+
|
|
4
8
|
// NOTE: WranglerDeployer has lib/ dependencies not available in npm distribution
|
|
5
9
|
// export { WranglerDeployer } from './wrangler-deployer.js';
|
|
6
10
|
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
import { BaseDeploymentOrchestrator } from './BaseDeploymentOrchestrator.js';
|
|
37
37
|
import { SingleServiceOrchestrator } from './SingleServiceOrchestrator.js';
|
|
38
38
|
import { PortfolioOrchestrator } from './PortfolioOrchestrator.js';
|
|
39
|
-
import { ErrorHandler } from '
|
|
39
|
+
import { ErrorHandler } from '../../lib/shared/utils/ErrorHandler.js';
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Capability descriptors - Defines what each capability provides
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { executeSql } from '../cloudflare/ops.js';
|
|
7
|
-
import { ErrorRecoveryManager } from '../utils/index.js';
|
|
7
|
+
import { ErrorRecoveryManager, frameworkConfig } from '../utils/index.js';
|
|
8
8
|
export class DatabaseConnectionManager {
|
|
9
9
|
constructor(options = {}) {
|
|
10
10
|
this.options = options;
|
|
@@ -18,10 +18,7 @@ export class DatabaseConnectionManager {
|
|
|
18
18
|
* Initialize with framework configuration
|
|
19
19
|
*/
|
|
20
20
|
async initialize() {
|
|
21
|
-
//
|
|
22
|
-
const {
|
|
23
|
-
frameworkConfig
|
|
24
|
-
} = await import('../../utils/framework-config.js');
|
|
21
|
+
// Use framework config for consistent database connection settings
|
|
25
22
|
const timing = frameworkConfig.getTiming();
|
|
26
23
|
const database = frameworkConfig.getDatabaseConfig();
|
|
27
24
|
this.config = {
|
|
@@ -36,9 +33,6 @@ export class DatabaseConnectionManager {
|
|
|
36
33
|
};
|
|
37
34
|
|
|
38
35
|
// Initialize error recovery with loaded config
|
|
39
|
-
const {
|
|
40
|
-
ErrorRecoveryManager
|
|
41
|
-
} = await import('../utils/index.js');
|
|
42
36
|
this.errorRecovery = new ErrorRecoveryManager({
|
|
43
37
|
maxRetries: this.config.maxRetries,
|
|
44
38
|
retryDelay: this.config.retryDelay,
|
|
@@ -13,6 +13,7 @@ import { join, dirname } from 'path';
|
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
14
|
import { promisify } from 'util';
|
|
15
15
|
import { logger } from '../logging/Logger.js';
|
|
16
|
+
import { frameworkConfig } from '../utils/framework-config.js';
|
|
16
17
|
const execAsync = promisify(exec);
|
|
17
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
19
|
const __dirname = dirname(__filename);
|
|
@@ -108,10 +109,7 @@ export class DatabaseOrchestrator {
|
|
|
108
109
|
* Initialize with framework configuration
|
|
109
110
|
*/
|
|
110
111
|
async initialize() {
|
|
111
|
-
//
|
|
112
|
-
const {
|
|
113
|
-
frameworkConfig
|
|
114
|
-
} = await import('../../utils/framework-config.js');
|
|
112
|
+
// Use framework config for consistent timing and database settings
|
|
115
113
|
const timing = frameworkConfig.getTiming();
|
|
116
114
|
const database = frameworkConfig.getDatabaseConfig();
|
|
117
115
|
this.config = {
|
|
@@ -15,6 +15,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSy
|
|
|
15
15
|
import { join, dirname } from 'path';
|
|
16
16
|
import { execSync } from 'child_process';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
18
|
+
import { frameworkConfig } from '../utils/framework-config.js';
|
|
18
19
|
const __dirname = (() => {
|
|
19
20
|
try {
|
|
20
21
|
const filename = fileURLToPath(import.meta.url);
|
|
@@ -107,10 +108,7 @@ export class EnhancedSecretManager {
|
|
|
107
108
|
* Initialize with framework configuration
|
|
108
109
|
*/
|
|
109
110
|
async initialize() {
|
|
110
|
-
//
|
|
111
|
-
const {
|
|
112
|
-
frameworkConfig
|
|
113
|
-
} = await import('../../utils/framework-config.js');
|
|
111
|
+
// Use framework config for consistent timing and retry settings
|
|
114
112
|
const timing = frameworkConfig.getTiming();
|
|
115
113
|
const security = frameworkConfig.getSecurity();
|
|
116
114
|
const configPaths = frameworkConfig.getPaths();
|
|
@@ -8,6 +8,7 @@ import { join } from 'path';
|
|
|
8
8
|
import { exec } from 'child_process';
|
|
9
9
|
import { promisify } from 'util';
|
|
10
10
|
import crypto from 'crypto';
|
|
11
|
+
import { frameworkConfig } from '../utils/framework-config.js';
|
|
11
12
|
const execAsync = promisify(exec);
|
|
12
13
|
export class SecureTokenManager {
|
|
13
14
|
constructor(options = {}) {
|
|
@@ -34,9 +35,6 @@ export class SecureTokenManager {
|
|
|
34
35
|
async initialize() {
|
|
35
36
|
try {
|
|
36
37
|
// Load framework configuration
|
|
37
|
-
const {
|
|
38
|
-
frameworkConfig
|
|
39
|
-
} = await import('../../utils/framework-config.js');
|
|
40
38
|
this.frameworkConfig = frameworkConfig;
|
|
41
39
|
|
|
42
40
|
// Update paths with framework config
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* This is the canonical version. All imports should use this version.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { frameworkConfig } from './framework-config.js';
|
|
14
15
|
export class ErrorRecoveryManager {
|
|
15
16
|
constructor(options = {}) {
|
|
16
17
|
this.options = options;
|
|
@@ -23,11 +24,8 @@ export class ErrorRecoveryManager {
|
|
|
23
24
|
* Initialize with framework configuration
|
|
24
25
|
*/
|
|
25
26
|
async initialize() {
|
|
26
|
-
//
|
|
27
|
+
// Use framework config for consistent timing and retry settings
|
|
27
28
|
try {
|
|
28
|
-
const {
|
|
29
|
-
frameworkConfig
|
|
30
|
-
} = await import('./framework-config.js');
|
|
31
29
|
const timing = frameworkConfig.getTiming();
|
|
32
30
|
this.config = {
|
|
33
31
|
maxRetries: this.options.maxRetries || timing.retryAttempts,
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Configuration Manager
|
|
3
|
+
* Centralized configuration loading and management for the Clodo Framework
|
|
4
|
+
*
|
|
5
|
+
* Replaces hardcoded values throughout the codebase with configurable settings
|
|
6
|
+
* from validation-config.json and environment variables
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { FileManager } from './file-manager.js';
|
|
12
|
+
export class FrameworkConfig {
|
|
13
|
+
constructor(configPath = null) {
|
|
14
|
+
this.fileManager = new FileManager({
|
|
15
|
+
enableCache: true
|
|
16
|
+
});
|
|
17
|
+
this.configPath = configPath || this.findConfigFile();
|
|
18
|
+
this.config = this.loadConfig();
|
|
19
|
+
this.environment = process.env.ENVIRONMENT || 'development';
|
|
20
|
+
|
|
21
|
+
// Validate environment variables on initialization
|
|
22
|
+
this.validateEnvironmentVariables();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Find the configuration file in standard locations
|
|
27
|
+
*
|
|
28
|
+
* For a packaged framework, we should only look within the framework's own directory
|
|
29
|
+
* to maintain self-containment. Users can explicitly pass config paths if needed.
|
|
30
|
+
*/
|
|
31
|
+
findConfigFile() {
|
|
32
|
+
let __filename, __dirname;
|
|
33
|
+
|
|
34
|
+
// Get the framework's directory (works in both source and packaged environments)
|
|
35
|
+
try {
|
|
36
|
+
__filename = fileURLToPath(import.meta.url);
|
|
37
|
+
__dirname = dirname(__filename);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// Fallback for environments where import.meta is not available
|
|
40
|
+
// This should not happen in modern Node.js environments
|
|
41
|
+
console.warn('⚠️ Unable to determine framework directory, using current directory');
|
|
42
|
+
__dirname = process.cwd();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Only look for config files within the framework's package directory
|
|
46
|
+
// This ensures the framework is self-contained and doesn't depend on user project files
|
|
47
|
+
const frameworkRoot = join(__dirname, '..', '..');
|
|
48
|
+
const possiblePaths = [join(frameworkRoot, 'validation-config.json'), join(frameworkRoot, 'config', 'validation-config.json'),
|
|
49
|
+
// Allow explicit config path via environment variable (for advanced users)
|
|
50
|
+
process.env.CLODO_FRAMEWORK_CONFIG && join(process.cwd(), process.env.CLODO_FRAMEWORK_CONFIG)].filter(Boolean); // Remove falsy values
|
|
51
|
+
|
|
52
|
+
for (const path of possiblePaths) {
|
|
53
|
+
if (this.fileManager.exists(path)) {
|
|
54
|
+
return path;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Return null instead of throwing - will use default config
|
|
59
|
+
// This is expected behavior - services don't need their own config
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Load and parse the configuration file
|
|
65
|
+
*/
|
|
66
|
+
loadConfig() {
|
|
67
|
+
// If no config file found, return default configuration
|
|
68
|
+
// This is normal - services use framework defaults unless they need custom settings
|
|
69
|
+
if (!this.configPath) {
|
|
70
|
+
return this.getDefaultConfig();
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const configContent = this.fileManager.readFile(this.configPath, 'utf8');
|
|
74
|
+
const config = JSON.parse(configContent);
|
|
75
|
+
console.log(`📋 Loaded configuration from: ${this.configPath}`);
|
|
76
|
+
return config;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.warn(`⚠️ Failed to load configuration from ${this.configPath}: ${error.message}`);
|
|
79
|
+
console.log('📋 Falling back to default configuration');
|
|
80
|
+
return this.getDefaultConfig();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get default configuration when validation-config.json is not available
|
|
86
|
+
*/
|
|
87
|
+
getDefaultConfig() {
|
|
88
|
+
return {
|
|
89
|
+
// Timing configuration (all in milliseconds)
|
|
90
|
+
deploymentTimeout: 30000,
|
|
91
|
+
retryAttempts: 3,
|
|
92
|
+
retryDelay: 1000,
|
|
93
|
+
connectionTimeout: 5000,
|
|
94
|
+
heartbeatInterval: 5000,
|
|
95
|
+
shutdownTimeout: 10000,
|
|
96
|
+
backupTimeout: 15000,
|
|
97
|
+
restoreTimeout: 20000,
|
|
98
|
+
migrationTimeout: 25000,
|
|
99
|
+
validationTimeout: 8000,
|
|
100
|
+
rollbackTimeout: 12000,
|
|
101
|
+
healthCheckInterval: 30000,
|
|
102
|
+
auditInterval: 60000,
|
|
103
|
+
cleanupInterval: 300000,
|
|
104
|
+
monitoringInterval: 15000,
|
|
105
|
+
alertThrottle: 5000,
|
|
106
|
+
// Network configuration
|
|
107
|
+
networking: {
|
|
108
|
+
maxConcurrentConnections: 10,
|
|
109
|
+
connectionPoolSize: 5,
|
|
110
|
+
requestTimeout: 30000,
|
|
111
|
+
maxRetries: 3,
|
|
112
|
+
retryDelay: 1000
|
|
113
|
+
},
|
|
114
|
+
// Database configuration
|
|
115
|
+
database: {
|
|
116
|
+
connectionTimeout: 10000,
|
|
117
|
+
queryTimeout: 30000,
|
|
118
|
+
transactionTimeout: 60000,
|
|
119
|
+
poolMin: 2,
|
|
120
|
+
poolMax: 10,
|
|
121
|
+
acquireTimeoutMillis: 60000,
|
|
122
|
+
createTimeoutMillis: 30000,
|
|
123
|
+
destroyTimeoutMillis: 5000,
|
|
124
|
+
idleTimeoutMillis: 30000,
|
|
125
|
+
reapIntervalMillis: 1000,
|
|
126
|
+
createRetryIntervalMillis: 200
|
|
127
|
+
},
|
|
128
|
+
// Environment configuration
|
|
129
|
+
environments: {
|
|
130
|
+
development: {
|
|
131
|
+
logLevel: 'debug',
|
|
132
|
+
debugMode: true,
|
|
133
|
+
dryRun: false
|
|
134
|
+
},
|
|
135
|
+
staging: {
|
|
136
|
+
logLevel: 'info',
|
|
137
|
+
debugMode: false,
|
|
138
|
+
dryRun: false
|
|
139
|
+
},
|
|
140
|
+
production: {
|
|
141
|
+
logLevel: 'warn',
|
|
142
|
+
debugMode: false,
|
|
143
|
+
dryRun: false
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
// Path configuration - all generated files go under generated/
|
|
147
|
+
paths: {
|
|
148
|
+
// Base generated directory
|
|
149
|
+
generated: process.env.FRAMEWORK_GENERATED_DIR || 'generated',
|
|
150
|
+
// Specific generated subdirectories
|
|
151
|
+
logs: process.env.FRAMEWORK_LOGS_DIR || 'generated/logs',
|
|
152
|
+
auditLogs: process.env.FRAMEWORK_AUDIT_DIR || 'generated/audit/logs',
|
|
153
|
+
backups: process.env.FRAMEWORK_BACKUP_DIR || 'generated/backups',
|
|
154
|
+
configCache: process.env.FRAMEWORK_CONFIG_CACHE_DIR || 'generated/cache/config',
|
|
155
|
+
secureTokens: process.env.FRAMEWORK_TOKEN_DIR || 'generated/cache/tokens',
|
|
156
|
+
auditReports: process.env.FRAMEWORK_REPORTS_DIR || 'generated/audit/reports',
|
|
157
|
+
testResults: process.env.FRAMEWORK_TEST_RESULTS_DIR || 'generated/test-results',
|
|
158
|
+
services: process.env.FRAMEWORK_SERVICES_DIR || 'generated/services',
|
|
159
|
+
temp: process.env.FRAMEWORK_TEMP_DIR || 'generated/temp'
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Reload the configuration from file (useful for testing)
|
|
166
|
+
*/
|
|
167
|
+
reload() {
|
|
168
|
+
this.config = this.loadConfig();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get timing configuration with environment variable overrides
|
|
173
|
+
*/
|
|
174
|
+
getTiming() {
|
|
175
|
+
const timing = this.config.timing || {};
|
|
176
|
+
return {
|
|
177
|
+
deploymentTimeout: parseInt(process.env.DEPLOYMENT_TIMEOUT) || timing.deploymentTimeout || 300000,
|
|
178
|
+
discoveryTimeout: parseInt(process.env.DISCOVERY_TIMEOUT) || timing.discoveryTimeout || 30000,
|
|
179
|
+
healthCheckTimeout: parseInt(process.env.HEALTH_CHECK_TIMEOUT) || timing.healthCheckTimeout || 10000,
|
|
180
|
+
productionTestTimeout: parseInt(process.env.PRODUCTION_TEST_TIMEOUT) || timing.productionTestTimeout || 30000,
|
|
181
|
+
shutdownTimeout: parseInt(process.env.SHUTDOWN_TIMEOUT) || timing.shutdownTimeout || 30000,
|
|
182
|
+
forceShutdownTimeout: parseInt(process.env.FORCE_SHUTDOWN_TIMEOUT) || timing.forceShutdownTimeout || 5000,
|
|
183
|
+
retryDelay: parseInt(process.env.RETRY_DELAY) || timing.retryDelay || 1000,
|
|
184
|
+
retryAttempts: parseInt(process.env.RETRY_ATTEMPTS) || timing.retryAttempts || 3,
|
|
185
|
+
cacheTTL: parseInt(process.env.CACHE_TTL) || timing.cacheTTL || 3600000,
|
|
186
|
+
maxAge: parseInt(process.env.MAX_AGE) || timing.maxAge || 86400000,
|
|
187
|
+
rateLimitWindow: parseInt(process.env.RATE_LIMIT_WINDOW) || timing.rateLimitWindow || 60000,
|
|
188
|
+
circuitBreakerTimeout: parseInt(process.env.CIRCUIT_BREAKER_TIMEOUT) || timing.circuitBreakerTimeout || 60000,
|
|
189
|
+
circuitBreakerThreshold: parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD) || timing.circuitBreakerThreshold || 5,
|
|
190
|
+
deploymentInterval: parseInt(process.env.DEPLOYMENT_INTERVAL) || timing.deploymentInterval || 5000,
|
|
191
|
+
endpointValidationTimeout: parseInt(process.env.ENDPOINT_VALIDATION_TIMEOUT) || timing.endpointValidationTimeout || 5000,
|
|
192
|
+
maxConcurrentDeployments: parseInt(process.env.MAX_CONCURRENT_DEPLOYMENTS) || timing.maxConcurrentDeployments || 3
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get networking configuration
|
|
198
|
+
*/
|
|
199
|
+
getNetworking() {
|
|
200
|
+
const networking = this.config.networking || {};
|
|
201
|
+
return {
|
|
202
|
+
endpoints: networking.endpoints || {},
|
|
203
|
+
development: networking.development || {},
|
|
204
|
+
rateLimiting: {
|
|
205
|
+
defaultRequests: parseInt(process.env.RATE_LIMIT_REQUESTS) || networking.rateLimiting?.defaultRequests || 100,
|
|
206
|
+
defaultWindow: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || networking.rateLimiting?.defaultWindow || 60000,
|
|
207
|
+
burstLimit: parseInt(process.env.RATE_LIMIT_BURST) || networking.rateLimiting?.burstLimit || 200,
|
|
208
|
+
strictMode: process.env.RATE_LIMIT_STRICT === 'true' || networking.rateLimiting?.strictMode || false
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get database naming configuration
|
|
215
|
+
*/
|
|
216
|
+
getDatabaseConfig() {
|
|
217
|
+
const database = this.config.database || {};
|
|
218
|
+
const naming = database.namingConvention || {};
|
|
219
|
+
return {
|
|
220
|
+
naming: {
|
|
221
|
+
development: naming.development || '{service}-dev',
|
|
222
|
+
staging: naming.staging || '{service}-staging',
|
|
223
|
+
production: naming.production || '{service}'
|
|
224
|
+
},
|
|
225
|
+
migration: database.migration || {},
|
|
226
|
+
connection: {
|
|
227
|
+
timeout: parseInt(process.env.DB_TIMEOUT) || database.connection?.timeout || 30000,
|
|
228
|
+
retryAttempts: parseInt(process.env.DB_RETRY_ATTEMPTS) || database.connection?.retryAttempts || 3,
|
|
229
|
+
poolSize: parseInt(process.env.DB_POOL_SIZE) || database.connection?.poolSize || 10
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get development ports configuration
|
|
236
|
+
*/
|
|
237
|
+
getDevelopmentPorts() {
|
|
238
|
+
const networking = this.config.networking || {};
|
|
239
|
+
const development = networking.development || {};
|
|
240
|
+
const defaultPorts = development.defaultPorts || {};
|
|
241
|
+
return {
|
|
242
|
+
frontend: parseInt(process.env.FRONTEND_PORT) || defaultPorts.frontend || 3000,
|
|
243
|
+
api: parseInt(process.env.API_PORT) || defaultPorts.api || 8787,
|
|
244
|
+
worker: parseInt(process.env.WORKER_PORT) || defaultPorts.worker || 8787,
|
|
245
|
+
preview: parseInt(process.env.PREVIEW_PORT) || defaultPorts.preview || 8788
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Get caching configuration
|
|
251
|
+
*/
|
|
252
|
+
getCaching() {
|
|
253
|
+
const caching = this.config.caching || {};
|
|
254
|
+
return {
|
|
255
|
+
maxCacheSize: parseInt(process.env.MAX_CACHE_SIZE) || caching.maxCacheSize || 52428800,
|
|
256
|
+
compressionThreshold: parseInt(process.env.COMPRESSION_THRESHOLD) || caching.compressionThreshold || 1024,
|
|
257
|
+
cleanupInterval: parseInt(process.env.CLEANUP_INTERVAL) || caching.cleanupInterval || 3600000,
|
|
258
|
+
enableCompression: process.env.ENABLE_COMPRESSION === 'true' || caching.enableCompression || true,
|
|
259
|
+
maxEntries: parseInt(process.env.MAX_CACHE_ENTRIES) || caching.maxEntries || 1000
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Get monitoring configuration
|
|
265
|
+
*/
|
|
266
|
+
getMonitoring() {
|
|
267
|
+
const monitoring = this.config.monitoring || {};
|
|
268
|
+
return {
|
|
269
|
+
healthCheck: monitoring.healthCheck || {},
|
|
270
|
+
metrics: monitoring.metrics || {},
|
|
271
|
+
logging: monitoring.logging || {}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get security configuration
|
|
277
|
+
*/
|
|
278
|
+
getSecurity() {
|
|
279
|
+
const security = this.config.security || {};
|
|
280
|
+
return {
|
|
281
|
+
requiredEnvironmentVars: security.requiredEnvironmentVars || [],
|
|
282
|
+
optionalEnvironmentVars: security.optionalEnvironmentVars || [],
|
|
283
|
+
secretsConfig: security.secretsConfig || {}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get path configuration with environment variable overrides
|
|
289
|
+
* All generated files are organized under the generated/ folder by default
|
|
290
|
+
*/
|
|
291
|
+
getPaths() {
|
|
292
|
+
const paths = this.config.paths || {};
|
|
293
|
+
return {
|
|
294
|
+
// Base generated directory
|
|
295
|
+
generated: process.env.FRAMEWORK_GENERATED_DIR || paths.generated || 'generated',
|
|
296
|
+
// Specific generated subdirectories
|
|
297
|
+
logs: process.env.FRAMEWORK_LOGS_DIR || paths.logs || 'generated/logs',
|
|
298
|
+
auditLogs: process.env.FRAMEWORK_AUDIT_DIR || paths.auditLogs || 'generated/audit/logs',
|
|
299
|
+
backups: process.env.FRAMEWORK_BACKUP_DIR || paths.backups || 'generated/backups',
|
|
300
|
+
configCache: process.env.FRAMEWORK_CONFIG_CACHE_DIR || paths.configCache || 'generated/cache/config',
|
|
301
|
+
secureTokens: process.env.FRAMEWORK_TOKEN_DIR || paths.secureTokens || 'generated/cache/tokens',
|
|
302
|
+
auditReports: process.env.FRAMEWORK_REPORTS_DIR || paths.auditReports || 'generated/audit/reports',
|
|
303
|
+
testResults: process.env.FRAMEWORK_TEST_RESULTS_DIR || paths.testResults || 'generated/test-results',
|
|
304
|
+
services: process.env.FRAMEWORK_SERVICES_DIR || paths.services || 'generated/services',
|
|
305
|
+
temp: process.env.FRAMEWORK_TEMP_DIR || paths.temp || 'generated/temp'
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get a clean command for removing all generated files
|
|
311
|
+
*/
|
|
312
|
+
getCleanupCommand() {
|
|
313
|
+
const paths = this.getPaths();
|
|
314
|
+
return `rm -rf ${paths.generated}`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Get environment configuration with validation
|
|
319
|
+
*/
|
|
320
|
+
getEnvironmentConfig(environment = null) {
|
|
321
|
+
const env = environment || this.environment;
|
|
322
|
+
const environments = this.config.environments || {};
|
|
323
|
+
const envConfig = environments[env] || environments.development || {};
|
|
324
|
+
if (!envConfig || Object.keys(envConfig).length === 0) {
|
|
325
|
+
console.warn(`⚠️ No configuration found for environment: ${env}. Using development defaults.`);
|
|
326
|
+
return {
|
|
327
|
+
domainTemplate: process.env.DOMAIN_TEMPLATE || '{service}.{domain}',
|
|
328
|
+
workerSuffix: process.env.WORKER_SUFFIX || '',
|
|
329
|
+
databaseSuffix: process.env.DATABASE_SUFFIX || '',
|
|
330
|
+
logLevel: this.validateLogLevel(process.env.LOG_LEVEL) || 'info',
|
|
331
|
+
enableMetrics: process.env.ENABLE_METRICS === 'true' || true,
|
|
332
|
+
strictValidation: process.env.STRICT_VALIDATION === 'true' || false,
|
|
333
|
+
debugMode: env === 'development',
|
|
334
|
+
dryRun: false
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
domainTemplate: process.env.DOMAIN_TEMPLATE || envConfig.domainTemplate || '{service}.{domain}',
|
|
339
|
+
workerSuffix: process.env.WORKER_SUFFIX || envConfig.workerSuffix || '',
|
|
340
|
+
databaseSuffix: process.env.DATABASE_SUFFIX || envConfig.databaseSuffix || '',
|
|
341
|
+
logLevel: this.validateLogLevel(process.env.LOG_LEVEL) || this.validateLogLevel(envConfig.logLevel) || 'info',
|
|
342
|
+
enableMetrics: process.env.ENABLE_METRICS === 'true' || envConfig.enableMetrics || true,
|
|
343
|
+
strictValidation: process.env.STRICT_VALIDATION === 'true' || envConfig.strictValidation || false,
|
|
344
|
+
debugMode: envConfig.debugMode || false,
|
|
345
|
+
dryRun: envConfig.dryRun || false
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Validate LOG_LEVEL environment variable
|
|
351
|
+
*/
|
|
352
|
+
validateLogLevel(level) {
|
|
353
|
+
const validLevels = ['error', 'warn', 'info', 'debug', 'trace'];
|
|
354
|
+
if (!level) return null;
|
|
355
|
+
const normalizedLevel = level.toLowerCase();
|
|
356
|
+
if (!validLevels.includes(normalizedLevel)) {
|
|
357
|
+
console.warn(`⚠️ Invalid LOG_LEVEL "${level}". Valid values: ${validLevels.join(', ')}. Using 'info'.`);
|
|
358
|
+
return 'info';
|
|
359
|
+
}
|
|
360
|
+
return normalizedLevel;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Validate required environment variables
|
|
365
|
+
*/
|
|
366
|
+
validateEnvironmentVariables() {
|
|
367
|
+
const errors = [];
|
|
368
|
+
const warnings = [];
|
|
369
|
+
|
|
370
|
+
// Validate LOG_LEVEL if present
|
|
371
|
+
if (process.env.LOG_LEVEL) {
|
|
372
|
+
this.validateLogLevel(process.env.LOG_LEVEL);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Validate ENVIRONMENT if present
|
|
376
|
+
if (process.env.ENVIRONMENT) {
|
|
377
|
+
const validEnvironments = ['development', 'staging', 'production'];
|
|
378
|
+
if (!validEnvironments.includes(process.env.ENVIRONMENT)) {
|
|
379
|
+
warnings.push(`Invalid ENVIRONMENT "${process.env.ENVIRONMENT}". Valid values: ${validEnvironments.join(', ')}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Validate framework-specific environment variables
|
|
384
|
+
const frameworkEnvVars = ['FRAMEWORK_LOGS_DIR', 'FRAMEWORK_AUDIT_DIR', 'FRAMEWORK_BACKUP_DIR', 'FRAMEWORK_CONFIG_CACHE_DIR', 'FRAMEWORK_TOKEN_DIR', 'FRAMEWORK_REPORTS_DIR'];
|
|
385
|
+
frameworkEnvVars.forEach(varName => {
|
|
386
|
+
if (process.env[varName]) {
|
|
387
|
+
// Validate path doesn't contain dangerous characters
|
|
388
|
+
const value = process.env[varName];
|
|
389
|
+
if (value.includes('..') || value.includes('~')) {
|
|
390
|
+
warnings.push(`Environment variable ${varName} contains potentially dangerous path: ${value}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Log validation results
|
|
396
|
+
if (warnings.length > 0) {
|
|
397
|
+
console.warn('⚠️ Environment variable validation warnings:');
|
|
398
|
+
warnings.forEach(warning => console.warn(` - ${warning}`));
|
|
399
|
+
}
|
|
400
|
+
if (errors.length > 0) {
|
|
401
|
+
console.error('❌ Environment variable validation errors:');
|
|
402
|
+
errors.forEach(error => console.error(` - ${error}`));
|
|
403
|
+
throw new Error('Environment variable validation failed');
|
|
404
|
+
}
|
|
405
|
+
if (warnings.length === 0 && errors.length === 0) {
|
|
406
|
+
console.log('✅ Environment variables validated successfully');
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
errors,
|
|
410
|
+
warnings
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Get testing configuration
|
|
416
|
+
*/
|
|
417
|
+
getTesting() {
|
|
418
|
+
const testing = this.config.testing || {};
|
|
419
|
+
return {
|
|
420
|
+
production: {
|
|
421
|
+
enabled: process.env.PRODUCTION_TESTS === 'true' || testing.production?.enabled || true,
|
|
422
|
+
lightweight: process.env.LIGHTWEIGHT_TESTS === 'true' || testing.production?.lightweight || true,
|
|
423
|
+
skipHeavyTests: process.env.SKIP_HEAVY_TESTS === 'true' || testing.production?.skipHeavyTests || true,
|
|
424
|
+
generateReports: process.env.GENERATE_REPORTS === 'true' || testing.production?.generateReports || false,
|
|
425
|
+
testTimeout: parseInt(process.env.TEST_TIMEOUT) || testing.production?.testTimeout || 30000
|
|
426
|
+
},
|
|
427
|
+
integration: testing.integration || {}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Generate database name based on service and environment
|
|
433
|
+
*/
|
|
434
|
+
generateDatabaseName(serviceName, environment = this.environment) {
|
|
435
|
+
const dbConfig = this.getDatabaseConfig();
|
|
436
|
+
const template = dbConfig.naming[environment] || dbConfig.naming.production || '{service}';
|
|
437
|
+
return template.replace('{service}', serviceName);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Generate development URLs based on service and configuration
|
|
442
|
+
*/
|
|
443
|
+
generateDevelopmentUrls(serviceName) {
|
|
444
|
+
const ports = this.getDevelopmentPorts();
|
|
445
|
+
return {
|
|
446
|
+
frontend: `http://localhost:${ports.frontend}`,
|
|
447
|
+
api: `http://localhost:${ports.api}`,
|
|
448
|
+
worker: `http://localhost:${ports.worker}`,
|
|
449
|
+
preview: `http://localhost:${ports.preview}`
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Validate environment variables
|
|
455
|
+
*/
|
|
456
|
+
validateEnvironment() {
|
|
457
|
+
const security = this.getSecurity();
|
|
458
|
+
const missing = [];
|
|
459
|
+
for (const envVar of security.requiredEnvironmentVars) {
|
|
460
|
+
if (!process.env[envVar]) {
|
|
461
|
+
missing.push(envVar);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (missing.length > 0) {
|
|
465
|
+
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
|
|
466
|
+
}
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Get routing configuration
|
|
472
|
+
* Provides route generation settings from validation-config.json
|
|
473
|
+
*/
|
|
474
|
+
getRoutingConfig() {
|
|
475
|
+
const routing = this.config.routing || {};
|
|
476
|
+
const templates = this.config.templates || {};
|
|
477
|
+
const workersDomain = templates.defaults?.WORKERS_DEV_DOMAIN || 'workers.dev';
|
|
478
|
+
return {
|
|
479
|
+
defaults: {
|
|
480
|
+
includeComments: routing.defaults?.includeComments !== false,
|
|
481
|
+
includeZoneId: routing.defaults?.includeZoneId !== false,
|
|
482
|
+
targetEnvironment: routing.defaults?.targetEnvironment || 'all',
|
|
483
|
+
orderStrategy: routing.defaults?.orderStrategy || 'most-specific-first'
|
|
484
|
+
},
|
|
485
|
+
domains: {
|
|
486
|
+
skipPatterns: routing.domains?.skipPatterns || [],
|
|
487
|
+
workersDomain,
|
|
488
|
+
// Reuse from templates.defaults.WORKERS_DEV_DOMAIN
|
|
489
|
+
complexTLDs: routing.domains?.complexTLDs || ['.co.uk', '.com.au', '.org.uk', '.gov.uk'],
|
|
490
|
+
subdomainMinParts: routing.domains?.subdomainMinParts || 3,
|
|
491
|
+
ignoreSubdomains: routing.domains?.ignoreSubdomains || ['www']
|
|
492
|
+
},
|
|
493
|
+
validation: {
|
|
494
|
+
zoneIdPattern: routing.validation?.zoneIdPattern || '^[a-f0-9]{32}$',
|
|
495
|
+
domainPattern: routing.validation?.domainPattern || '^[a-z0-9.-]+$',
|
|
496
|
+
strictMode: routing.validation?.strictMode !== false
|
|
497
|
+
},
|
|
498
|
+
comments: {
|
|
499
|
+
enabled: routing.comments?.enabled !== false,
|
|
500
|
+
templates: routing.comments?.templates || {
|
|
501
|
+
production: '# Production environment routes\n# Domain: {{domain}}',
|
|
502
|
+
staging: '# Staging environment routes\n# Domain: {{domain}}',
|
|
503
|
+
development: '# Development environment\n# Uses {{WORKERS_DEV_DOMAIN}} subdomain'
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Get environment-specific routing configuration
|
|
511
|
+
* Combines global routing config with environment-specific settings
|
|
512
|
+
*/
|
|
513
|
+
getEnvironmentRoutingConfig(environment = null) {
|
|
514
|
+
const env = environment || this.environment;
|
|
515
|
+
const environments = this.config.environments || {};
|
|
516
|
+
const envConfig = environments[env] || environments.development || {};
|
|
517
|
+
const routingConfig = envConfig.routing || {};
|
|
518
|
+
|
|
519
|
+
// Fallback defaults if not in config
|
|
520
|
+
const defaultPrefixes = {
|
|
521
|
+
development: '/dev-api',
|
|
522
|
+
staging: '/staging-api',
|
|
523
|
+
production: '/api'
|
|
524
|
+
};
|
|
525
|
+
return {
|
|
526
|
+
defaultPathPrefix: routingConfig.defaultPathPrefix || defaultPrefixes[env] || '/api',
|
|
527
|
+
wildcardPattern: routingConfig.wildcardPattern || '/*',
|
|
528
|
+
generateFallbackRoute: routingConfig.generateFallbackRoute !== false,
|
|
529
|
+
nestedInToml: env !== 'production' && routingConfig.nestedInToml !== false
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Get all environment names from config
|
|
535
|
+
*/
|
|
536
|
+
getEnvironmentNames() {
|
|
537
|
+
const environments = this.config.environments || {};
|
|
538
|
+
return Object.keys(environments).length > 0 ? Object.keys(environments) : ['development', 'staging', 'production'];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Get aggregated configuration for all needs
|
|
543
|
+
*/
|
|
544
|
+
getAll() {
|
|
545
|
+
return {
|
|
546
|
+
timing: this.getTiming(),
|
|
547
|
+
networking: this.getNetworking(),
|
|
548
|
+
environment: this.getEnvironmentConfig(),
|
|
549
|
+
database: this.getDatabaseConfig(),
|
|
550
|
+
ports: this.getDevelopmentPorts(),
|
|
551
|
+
caching: this.getCaching(),
|
|
552
|
+
monitoring: this.getMonitoring(),
|
|
553
|
+
security: this.getSecurity(),
|
|
554
|
+
testing: this.getTesting(),
|
|
555
|
+
routing: this.getRoutingConfig()
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Export singleton instance for easy access
|
|
561
|
+
export const frameworkConfig = new FrameworkConfig();
|
|
562
|
+
|
|
563
|
+
// Export utility functions
|
|
564
|
+
export const getFrameworkConfig = (configPath = null) => {
|
|
565
|
+
return configPath ? new FrameworkConfig(configPath) : frameworkConfig;
|
|
566
|
+
};
|
|
567
|
+
export default FrameworkConfig;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Implements SIGTERM/SIGINT handlers and cleanup routines for CLI tools
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { frameworkConfig } from './framework-config.js';
|
|
6
7
|
export class GracefulShutdownManager {
|
|
7
8
|
constructor(options = {}) {
|
|
8
9
|
this.options = options;
|
|
@@ -17,10 +18,7 @@ export class GracefulShutdownManager {
|
|
|
17
18
|
* Initialize with framework configuration
|
|
18
19
|
*/
|
|
19
20
|
async initialize() {
|
|
20
|
-
//
|
|
21
|
-
const {
|
|
22
|
-
frameworkConfig
|
|
23
|
-
} = await import('../../utils/framework-config.js');
|
|
21
|
+
// Use framework config for consistent timing settings
|
|
24
22
|
const timing = frameworkConfig.getTiming();
|
|
25
23
|
this.config = {
|
|
26
24
|
shutdownTimeout: this.options.shutdownTimeout || timing.shutdownTimeout,
|
|
@@ -28,4 +28,7 @@ export { validateDeploymentPrerequisites, validateDeploymentInputs } from './dep
|
|
|
28
28
|
export { redactSensitiveInfo, redactSensitiveObject } from './sensitive-redactor.js';
|
|
29
29
|
|
|
30
30
|
// Configuration loading
|
|
31
|
-
export { ConfigLoader } from './config-loader.js';
|
|
31
|
+
export { ConfigLoader } from './config-loader.js';
|
|
32
|
+
|
|
33
|
+
// Framework configuration
|
|
34
|
+
export { FrameworkConfig, frameworkConfig, getFrameworkConfig } from './framework-config.js';
|
package/dist/security/index.js
CHANGED
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
import { ConfigurationValidator } from './ConfigurationValidator.js';
|
|
7
7
|
// DeploymentManager removed - replaced by MultiDomainOrchestrator + WranglerConfigManager
|
|
8
8
|
import { SecretGenerator } from './SecretGenerator.js';
|
|
9
|
+
import { SecurityCLI } from './SecurityCLI.js';
|
|
9
10
|
// InteractiveDeploymentConfigurator removed - replaced by InputCollector
|
|
10
11
|
|
|
11
12
|
export { ConfigurationValidator } from './ConfigurationValidator.js';
|
|
12
|
-
// export { DeploymentManager
|
|
13
|
+
// export { DeploymentManager} - DEPRECATED: Use MultiDomainOrchestrator instead
|
|
13
14
|
export { SecretGenerator } from './SecretGenerator.js';
|
|
15
|
+
export { SecurityCLI } from './SecurityCLI.js';
|
|
14
16
|
// export { InteractiveDeploymentConfigurator } - DEPRECATED: Use InputCollector instead
|
|
15
17
|
|
|
16
18
|
// Re-export patterns and rules for advanced usage
|
|
@@ -5,12 +5,49 @@
|
|
|
5
5
|
|
|
6
6
|
import fs from 'fs/promises';
|
|
7
7
|
import path from 'path';
|
|
8
|
+
import { FrameworkConfig } from '../../utils/framework-config.js';
|
|
8
9
|
export class ValidationHandler {
|
|
9
10
|
constructor(options = {}) {
|
|
10
11
|
this.strict = options.strict || false;
|
|
11
12
|
this.customConfig = options.customConfig || {};
|
|
13
|
+
this.configLoaded = false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Load validation configuration from validation-config.json if it exists
|
|
18
|
+
*/
|
|
19
|
+
loadValidationConfig(servicePath) {
|
|
20
|
+
// Skip if already loaded or custom config provided
|
|
21
|
+
if (this.configLoaded || Object.keys(this.customConfig).length > 0) {
|
|
22
|
+
return this.validationConfig;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
// Try to load validation-config.json from service directory
|
|
26
|
+
const configPath = path.join(servicePath, 'validation-config.json');
|
|
27
|
+
const frameworkConfig = new FrameworkConfig(configPath);
|
|
28
|
+
const config = frameworkConfig.config;
|
|
29
|
+
|
|
30
|
+
// Extract validation section from config
|
|
31
|
+
if (config && config.validation) {
|
|
32
|
+
// Log that we loaded the config
|
|
33
|
+
console.log(`📋 Loaded configuration from: ${configPath}`);
|
|
34
|
+
this.validationConfig = {
|
|
35
|
+
requiredFiles: config.validation.requiredFiles || ['package.json', 'src/config/domains.js', 'src/worker/index.js', 'wrangler.toml'],
|
|
36
|
+
optionalFiles: config.validation.optionalFiles || ['README.md', 'LICENSE', '.gitignore'],
|
|
37
|
+
requiredFields: config.validation.requiredFields || {
|
|
38
|
+
'package.json': ['name', 'version', 'type', 'main'],
|
|
39
|
+
'wrangler.toml': ['name', 'main', 'compatibility_date']
|
|
40
|
+
},
|
|
41
|
+
serviceTypes: config.validation.serviceTypes || ['data-service', 'auth-service', 'content-service', 'api-gateway', 'static-site', 'generic']
|
|
42
|
+
};
|
|
43
|
+
this.configLoaded = true;
|
|
44
|
+
return this.validationConfig;
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Config loading failed, use defaults silently
|
|
48
|
+
}
|
|
12
49
|
|
|
13
|
-
// Use custom
|
|
50
|
+
// Use custom config if provided, otherwise use defaults
|
|
14
51
|
this.validationConfig = {
|
|
15
52
|
requiredFiles: this.customConfig.requiredFiles || ['package.json', 'src/config/domains.js', 'src/worker/index.js', 'wrangler.toml'],
|
|
16
53
|
optionalFiles: this.customConfig.optionalFiles || ['README.md', 'LICENSE', '.gitignore'],
|
|
@@ -20,12 +57,16 @@ export class ValidationHandler {
|
|
|
20
57
|
},
|
|
21
58
|
serviceTypes: this.customConfig.serviceTypes || ['data-service', 'auth-service', 'content-service', 'api-gateway', 'static-site', 'generic']
|
|
22
59
|
};
|
|
60
|
+
this.configLoaded = true;
|
|
61
|
+
return this.validationConfig;
|
|
23
62
|
}
|
|
24
63
|
|
|
25
64
|
/**
|
|
26
65
|
* Validate complete service configuration
|
|
27
66
|
*/
|
|
28
67
|
async validateService(servicePath) {
|
|
68
|
+
// Load validation config from validation-config.json if it exists
|
|
69
|
+
this.loadValidationConfig(servicePath);
|
|
29
70
|
const issues = [];
|
|
30
71
|
|
|
31
72
|
// Check for required files using custom config
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module service-management/routing/DomainRouteMapper
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { frameworkConfig } from '../../utils/framework-config.js';
|
|
10
|
+
import { frameworkConfig } from '../../lib/shared/utils/framework-config.js';
|
|
11
11
|
export class DomainRouteMapper {
|
|
12
12
|
constructor(options = {}) {
|
|
13
13
|
// Load environment-specific routing config
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { DomainRouteMapper } from './DomainRouteMapper.js';
|
|
11
11
|
import { WranglerRoutesBuilder } from './WranglerRoutesBuilder.js';
|
|
12
|
-
import { frameworkConfig } from '../../utils/framework-config.js';
|
|
12
|
+
import { frameworkConfig } from '../../lib/shared/utils/framework-config.js';
|
|
13
13
|
export class RouteGenerator {
|
|
14
14
|
constructor(options = {}) {
|
|
15
15
|
this.mapper = new DomainRouteMapper(options);
|
|
@@ -21,7 +21,7 @@ import { access, readFile, writeFile, mkdir, readdir, stat } from 'fs/promises';
|
|
|
21
21
|
import { join, dirname } from 'path';
|
|
22
22
|
import { promisify } from 'util';
|
|
23
23
|
import { exec } from 'child_process';
|
|
24
|
-
import { frameworkConfig } from '
|
|
24
|
+
import { frameworkConfig } from '../../lib/shared/utils/framework-config.js';
|
|
25
25
|
import { NameFormatters } from '../formatters.js';
|
|
26
26
|
const execAsync = promisify(exec);
|
|
27
27
|
export class ConfigurationCacheManager {
|
|
@@ -5,4 +5,4 @@ export { ConfigurationCacheManager } from './config-cache.js';
|
|
|
5
5
|
export { EnhancedSecretManager } from './secret-generator.js';
|
|
6
6
|
export { UnifiedConfigManager, unifiedConfigManager } from '../config/unified-config-manager.js';
|
|
7
7
|
export { askUser, askYesNo, askChoice, closePrompts } from '../interactive-prompts.js';
|
|
8
|
-
export { DeploymentCredentialCollector } from '
|
|
8
|
+
export { DeploymentCredentialCollector } from '../../lib/shared/deployment/credential-collector.js';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
* Enhanced Configuration Manager
|
|
3
|
+
* Centralized configuration loading and management for the Clodo Framework
|
|
4
|
+
*
|
|
5
|
+
* Replaces hardcoded values throughout the codebase with configurable settings
|
|
6
|
+
* from validation-config.json and environment variables
|
|
7
|
+
*/
|
|
8
8
|
|
|
9
9
|
import { join, dirname } from 'path';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
@@ -17,10 +17,10 @@ export class GracefulShutdownManager {
|
|
|
17
17
|
* Initialize with framework configuration
|
|
18
18
|
*/
|
|
19
19
|
async initialize() {
|
|
20
|
-
// Import framework config for consistent timing
|
|
20
|
+
// Import framework config for consistent timing settings
|
|
21
21
|
const {
|
|
22
22
|
frameworkConfig
|
|
23
|
-
} = await import('
|
|
23
|
+
} = await import('../lib/shared/utils/framework-config.js');
|
|
24
24
|
const timing = frameworkConfig.getTiming();
|
|
25
25
|
this.config = {
|
|
26
26
|
shutdownTimeout: this.options.shutdownTimeout || timing.shutdownTimeout,
|
package/dist/utils/index.js
CHANGED
|
@@ -35,8 +35,8 @@ export * from './validation.js';
|
|
|
35
35
|
// Health checking utilities
|
|
36
36
|
export * from './health-checker.js';
|
|
37
37
|
|
|
38
|
-
// Framework configuration
|
|
39
|
-
export
|
|
38
|
+
// Framework configuration - re-export from canonical location
|
|
39
|
+
export { FrameworkConfig, frameworkConfig, getFrameworkConfig } from '../lib/shared/utils/framework-config.js';
|
|
40
40
|
|
|
41
41
|
// Deployment utilities
|
|
42
42
|
export * from './deployment/index.js';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common Feature Constants
|
|
3
|
+
* Worker-safe constants extracted from ConfigurationManager
|
|
4
|
+
* No Node.js dependencies (fs, path, etc.)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const COMMON_FEATURES = {
|
|
8
|
+
// Schema Manager Features
|
|
9
|
+
ENABLE_ENHANCED_SCHEMA: true,
|
|
10
|
+
ENABLE_SCHEMA_CACHING: true,
|
|
11
|
+
ENABLE_COMPREHENSIVE_VALIDATION: true,
|
|
12
|
+
ENABLE_SQL_CACHING: true,
|
|
13
|
+
// Generic Data Service Features
|
|
14
|
+
ENABLE_QUERY_CACHING: true,
|
|
15
|
+
ENABLE_SECURITY_CONTROLS: true,
|
|
16
|
+
ENABLE_ADVANCED_PAGINATION: true,
|
|
17
|
+
ENABLE_RELATIONSHIP_LOADING: true,
|
|
18
|
+
// Module Manager Features
|
|
19
|
+
ENABLE_ENHANCED_HOOKS: true,
|
|
20
|
+
ENABLE_HOOK_TIMEOUT: true,
|
|
21
|
+
ENABLE_HOOK_METRICS: true,
|
|
22
|
+
ENABLE_PARALLEL_EXECUTION: true,
|
|
23
|
+
// Performance Features
|
|
24
|
+
ENABLE_PERFORMANCE_MONITORING: true,
|
|
25
|
+
ENABLE_CACHE_METRICS: true,
|
|
26
|
+
ENABLE_QUERY_OPTIMIZATION: true,
|
|
27
|
+
// Development Features
|
|
28
|
+
ENABLE_DEBUG_LOGGING: false,
|
|
29
|
+
ENABLE_DEVELOPMENT_MODE: false,
|
|
30
|
+
ENABLE_STRICT_VALIDATION: false,
|
|
31
|
+
// Migration Features
|
|
32
|
+
ENABLE_LEGACY_COMPATIBILITY: true,
|
|
33
|
+
ENABLE_DEPRECATION_WARNINGS: true,
|
|
34
|
+
ENABLE_MIGRATION_HELPERS: true,
|
|
35
|
+
// Domain-specific features
|
|
36
|
+
AUTHENTICATION: true,
|
|
37
|
+
AUTHORIZATION: true,
|
|
38
|
+
LOGGING: true,
|
|
39
|
+
MONITORING: true,
|
|
40
|
+
CACHING: true,
|
|
41
|
+
RATE_LIMITING: true,
|
|
42
|
+
FILE_STORAGE: true
|
|
43
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { getDomainFromEnv, createEnvironmentConfig } from '../config/domains.js';
|
|
2
2
|
|
|
3
|
-
// Import COMMON_FEATURES from
|
|
4
|
-
import { COMMON_FEATURES } from '
|
|
3
|
+
// Import COMMON_FEATURES from worker-safe constants (no Node.js dependencies)
|
|
4
|
+
import { COMMON_FEATURES } from './features.js';
|
|
5
5
|
|
|
6
6
|
// Simple feature manager interface (replaces ConfigurationManager dependency)
|
|
7
7
|
export const configManager = {
|
|
8
8
|
setDomain: () => {},
|
|
9
|
-
getEnabledFeatures: () => Object.
|
|
10
|
-
isFeatureEnabled: feature =>
|
|
9
|
+
getEnabledFeatures: () => Object.keys(COMMON_FEATURES).filter(key => COMMON_FEATURES[key]),
|
|
10
|
+
isFeatureEnabled: feature => COMMON_FEATURES[feature] === true
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// Legacy featureManager compatibility interface
|
|
@@ -185,7 +185,7 @@ export const createRateLimitGuard = (options = {}) => {
|
|
|
185
185
|
return handler => {
|
|
186
186
|
return async (request, env, ctx) => {
|
|
187
187
|
// Skip rate limiting if feature is disabled
|
|
188
|
-
if (!featureManager.isEnabled(
|
|
188
|
+
if (!featureManager.isEnabled('RATE_LIMITING')) {
|
|
189
189
|
return handler(request, env, ctx);
|
|
190
190
|
}
|
|
191
191
|
const clientId = request.headers.get('CF-Connecting-IP') || 'anonymous';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Entry Point
|
|
3
|
+
* Minimal exports for Cloudflare Workers - excludes CLI/Node.js dependencies
|
|
4
|
+
*
|
|
5
|
+
* This is the entry point for wrangler.toml
|
|
6
|
+
* Only exports worker-compatible code (no fs, path, child_process, etc.)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Worker integration (no Node.js dependencies)
|
|
10
|
+
export { initializeService, configManager } from './worker/integration.js';
|
|
11
|
+
|
|
12
|
+
// Domain configuration (pure JS, no fs dependencies)
|
|
13
|
+
export { getDomainFromEnv, createEnvironmentConfig } from './config/domains.js';
|
|
14
|
+
|
|
15
|
+
// Framework version
|
|
16
|
+
export const FRAMEWORK_VERSION = '1.0.0';
|
|
17
|
+
|
|
18
|
+
// Default export for module worker format
|
|
19
|
+
export default {
|
|
20
|
+
async fetch(request, env, ctx) {
|
|
21
|
+
return new Response('Clodo Framework Worker - Use initializeService() to set up your worker', {
|
|
22
|
+
status: 200,
|
|
23
|
+
headers: {
|
|
24
|
+
'content-type': 'text/plain'
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamyla/clodo-framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -88,6 +88,8 @@
|
|
|
88
88
|
"test:worker": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/worker/ --verbose",
|
|
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
|
+
"test:package": "node scripts/test-package.js",
|
|
92
|
+
"prepublishOnly": "npm run build:ci",
|
|
91
93
|
"test:coverage:cli": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/cli-integration/ --coverage",
|
|
92
94
|
"test:coverage:deployment": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/deployment/ test/deployment-security.spec.js --coverage",
|
|
93
95
|
"test:coverage:services": "cross-env NODE_OPTIONS=--experimental-vm-modules jest test/service-management/ test/services/ --coverage",
|
|
@@ -101,7 +103,6 @@
|
|
|
101
103
|
"analyze:bundle": "echo 'Bundle analysis not implemented yet'",
|
|
102
104
|
"docs": "echo 'JSDoc documentation generation not configured yet'",
|
|
103
105
|
"validate": "npm run check:all",
|
|
104
|
-
"prepublishOnly": "npm run build:ci",
|
|
105
106
|
"postpublish": "echo 'Published successfully to npm registry!'",
|
|
106
107
|
"publish:beta": "npm publish --tag beta",
|
|
107
108
|
"publish:latest": "npm publish --tag latest",
|