@tamyla/clodo-framework 2.0.1 → 2.0.2
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/bin/clodo-service.js +2 -2
- package/bin/security/security-cli.js +1 -1
- package/bin/service-management/create-service.js +1 -1
- package/bin/service-management/init-service.js +1 -1
- package/bin/shared/config/cache.js +1230 -0
- package/bin/shared/config/command-config-manager.js +184 -0
- package/bin/shared/config/customer-cli.js +1 -1
- package/bin/shared/config/index.js +9 -0
- package/bin/shared/config/manager.js +315 -0
- package/dist/shared/config/customer-cli.js +1 -1
- package/package.json +6 -4
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Configuration Manager
|
|
3
|
+
* Loads and manages configurable system commands from validation-config.json
|
|
4
|
+
*
|
|
5
|
+
* Ensures all commands are configurable and platform-specific
|
|
6
|
+
* No hardcoded commands in the codebase
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
|
|
12
|
+
export class CommandConfigManager {
|
|
13
|
+
constructor(configPath = null) {
|
|
14
|
+
this.configPath = configPath || join(process.cwd(), 'validation-config.json');
|
|
15
|
+
this.config = null;
|
|
16
|
+
this.loadConfig();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load configuration from JSON file
|
|
21
|
+
*/
|
|
22
|
+
loadConfig() {
|
|
23
|
+
try {
|
|
24
|
+
const configData = readFileSync(this.configPath, 'utf-8');
|
|
25
|
+
this.config = JSON.parse(configData);
|
|
26
|
+
console.log('📋 Loaded command configuration from validation-config.json');
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.log('⚠️ Could not load command config, using defaults');
|
|
29
|
+
this.config = this.getDefaultConfig();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get default configuration fallback
|
|
35
|
+
*/
|
|
36
|
+
getDefaultConfig() {
|
|
37
|
+
return {
|
|
38
|
+
requiredCommands: {
|
|
39
|
+
npx: 'npx',
|
|
40
|
+
node: 'node',
|
|
41
|
+
npm: 'npm',
|
|
42
|
+
wrangler: 'npx wrangler'
|
|
43
|
+
},
|
|
44
|
+
cloudflareCommands: {
|
|
45
|
+
whoami: 'npx wrangler whoami',
|
|
46
|
+
auth_login: 'npx wrangler auth login',
|
|
47
|
+
deployments_list: 'npx wrangler deployments list',
|
|
48
|
+
list_workers: 'npx wrangler dev --help',
|
|
49
|
+
deploy: 'npx wrangler deploy',
|
|
50
|
+
list_zones: 'npx wrangler zone list',
|
|
51
|
+
worker_status: 'npx wrangler status'
|
|
52
|
+
},
|
|
53
|
+
systemCommands: {
|
|
54
|
+
powershell_web_request: 'powershell -Command "try { Invoke-WebRequest -Uri \'{url}\' -TimeoutSec 10 -UseBasicParsing | Out-Null } catch { exit 1 }"',
|
|
55
|
+
curl_test: 'curl -s --connect-timeout 10 {url} -o /dev/null'
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get a Cloudflare command
|
|
62
|
+
*/
|
|
63
|
+
getCloudflareCommand(commandName, params = {}) {
|
|
64
|
+
const command = this.config?.cloudflareCommands?.[commandName];
|
|
65
|
+
if (!command) {
|
|
66
|
+
throw new Error(`Cloudflare command '${commandName}' not found in configuration`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return this.interpolateParams(command, params);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get a system command
|
|
74
|
+
*/
|
|
75
|
+
getSystemCommand(commandName, params = {}) {
|
|
76
|
+
const command = this.config?.systemCommands?.[commandName];
|
|
77
|
+
if (!command) {
|
|
78
|
+
throw new Error(`System command '${commandName}' not found in configuration`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return this.interpolateParams(command, params);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get a required command
|
|
86
|
+
*/
|
|
87
|
+
getRequiredCommand(commandName) {
|
|
88
|
+
const command = this.config?.requiredCommands?.[commandName];
|
|
89
|
+
if (!command) {
|
|
90
|
+
throw new Error(`Required command '${commandName}' not found in configuration`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return command;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get network test command for current platform
|
|
98
|
+
*/
|
|
99
|
+
getNetworkTestCommand(url) {
|
|
100
|
+
const isWindows = process.platform === 'win32';
|
|
101
|
+
const commandName = isWindows ? 'powershell_web_request' : 'curl_test';
|
|
102
|
+
|
|
103
|
+
return this.getSystemCommand(commandName, { url });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Interpolate parameters in command strings
|
|
108
|
+
*/
|
|
109
|
+
interpolateParams(command, params) {
|
|
110
|
+
let result = command;
|
|
111
|
+
|
|
112
|
+
for (const [key, value] of Object.entries(params)) {
|
|
113
|
+
const placeholder = `{${key}}`;
|
|
114
|
+
result = result.replace(new RegExp(placeholder.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'g'), value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get all available commands for debugging
|
|
122
|
+
*/
|
|
123
|
+
getAllCommands() {
|
|
124
|
+
return {
|
|
125
|
+
requiredCommands: this.config?.requiredCommands || {},
|
|
126
|
+
cloudflareCommands: this.config?.cloudflareCommands || {},
|
|
127
|
+
systemCommands: this.config?.systemCommands || {}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Validate command configuration
|
|
133
|
+
*/
|
|
134
|
+
validateConfig() {
|
|
135
|
+
const errors = [];
|
|
136
|
+
|
|
137
|
+
// Check required sections
|
|
138
|
+
const requiredSections = ['requiredCommands', 'cloudflareCommands', 'systemCommands'];
|
|
139
|
+
|
|
140
|
+
for (const section of requiredSections) {
|
|
141
|
+
if (!this.config[section] || typeof this.config[section] !== 'object') {
|
|
142
|
+
errors.push(`Missing or invalid section: ${section}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check essential Cloudflare commands
|
|
147
|
+
const essentialCfCommands = ['whoami', 'auth_login', 'deployments_list'];
|
|
148
|
+
|
|
149
|
+
for (const cmd of essentialCfCommands) {
|
|
150
|
+
if (!this.config?.cloudflareCommands?.[cmd]) {
|
|
151
|
+
errors.push(`Missing essential Cloudflare command: ${cmd}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (errors.length > 0) {
|
|
156
|
+
throw new Error(`Command configuration validation failed:\\n${errors.join('\\n')}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Reload configuration
|
|
164
|
+
*/
|
|
165
|
+
reload() {
|
|
166
|
+
this.loadConfig();
|
|
167
|
+
this.validateConfig();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Singleton instance
|
|
172
|
+
let commandConfigInstance = null;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get singleton instance of command config manager
|
|
176
|
+
*/
|
|
177
|
+
export function getCommandConfig(configPath = null) {
|
|
178
|
+
if (!commandConfigInstance || configPath) {
|
|
179
|
+
commandConfigInstance = new CommandConfigManager(configPath);
|
|
180
|
+
}
|
|
181
|
+
return commandConfigInstance;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default CommandConfigManager;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Integrates with Clodo Framework domain and feature flag systems
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { CustomerConfigCLI } from '../../../
|
|
9
|
+
import { CustomerConfigCLI } from '../../../dist/config/CustomerConfigCLI.js';
|
|
10
10
|
|
|
11
11
|
const command = process.argv[2];
|
|
12
12
|
const args = process.argv.slice(3);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Module
|
|
3
|
+
* Exports all configuration management utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { ConfigCache } from './cache.js';
|
|
7
|
+
export { ConfigManager } from './manager.js';
|
|
8
|
+
export { CommandConfigManager } from './command-config-manager.js';
|
|
9
|
+
export { CustomerConfigurationManager } from '../../../src/config/customers.js';
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Manager Module
|
|
3
|
+
* Configuration file management and validation
|
|
4
|
+
*
|
|
5
|
+
* Consolidates config management across 34+ scripts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, writeFileSync, existsSync, copyFileSync, readdirSync, statSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
|
|
11
|
+
export function updateWranglerConfig(updates, configPath = 'wrangler.toml') {
|
|
12
|
+
if (!existsSync(configPath)) {
|
|
13
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let config = readFileSync(configPath, 'utf8');
|
|
17
|
+
let changesMade = [];
|
|
18
|
+
|
|
19
|
+
// Apply updates systematically
|
|
20
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
21
|
+
switch (key) {
|
|
22
|
+
case 'workerName':
|
|
23
|
+
// Update main worker name
|
|
24
|
+
if (config.match(/^name = "[^"]*"/m)) {
|
|
25
|
+
config = config.replace(/^name = "[^"]*"/m, `name = "${value}"`);
|
|
26
|
+
changesMade.push(`Updated main worker name to: ${value}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Update production environment name
|
|
30
|
+
if (config.match(/^\[env\.production\]\s*\nname = "[^"]*"/m)) {
|
|
31
|
+
config = config.replace(/^\[env\.production\]\s*\nname = "[^"]*"/m, `[env.production]\nname = "${value}"`);
|
|
32
|
+
changesMade.push(`Updated production worker name to: ${value}`);
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
|
|
36
|
+
case 'databaseName': {
|
|
37
|
+
const dbNameRegex = /database_name = "[^"]*"/g;
|
|
38
|
+
if (config.match(dbNameRegex)) {
|
|
39
|
+
config = config.replace(dbNameRegex, `database_name = "${value}"`);
|
|
40
|
+
changesMade.push(`Updated database name to: ${value}`);
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
case 'databaseId': {
|
|
46
|
+
const dbIdRegex = /database_id = "[^"]*"/g;
|
|
47
|
+
if (config.match(dbIdRegex)) {
|
|
48
|
+
config = config.replace(dbIdRegex, `database_id = "${value}"`);
|
|
49
|
+
changesMade.push(`Updated database ID to: ${value}`);
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'serviceDomain': {
|
|
55
|
+
const domainRegex = /SERVICE_DOMAIN = "[^"]*"/g;
|
|
56
|
+
if (config.match(domainRegex)) {
|
|
57
|
+
config = config.replace(domainRegex, `SERVICE_DOMAIN = "${value}"`);
|
|
58
|
+
changesMade.push(`Updated service domain to: ${value}`);
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
case 'compatibilityDate': {
|
|
64
|
+
const dateRegex = /^compatibility_date = "[^"]*"/m;
|
|
65
|
+
if (config.match(dateRegex)) {
|
|
66
|
+
config = config.replace(dateRegex, `compatibility_date = "${value}"`);
|
|
67
|
+
changesMade.push(`Updated compatibility date to: ${value}`);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
default:
|
|
73
|
+
console.warn(`Unknown config update key: ${key}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
writeFileSync(configPath, config);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
configPath,
|
|
81
|
+
changesMade,
|
|
82
|
+
success: changesMade.length > 0
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function backupConfig(configPath = 'wrangler.toml', suffix = null) {
|
|
87
|
+
if (!existsSync(configPath)) {
|
|
88
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const timestamp = suffix || new Date().toISOString().replace(/[:.]/g, '-');
|
|
92
|
+
const backupPath = `${configPath}.backup.${timestamp}`;
|
|
93
|
+
|
|
94
|
+
copyFileSync(configPath, backupPath);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
originalPath: configPath,
|
|
98
|
+
backupPath,
|
|
99
|
+
timestamp: new Date().toISOString()
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function validateConfig(configPath = 'wrangler.toml') {
|
|
104
|
+
if (!existsSync(configPath)) {
|
|
105
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const config = readFileSync(configPath, 'utf8');
|
|
109
|
+
const issues = [];
|
|
110
|
+
const warnings = [];
|
|
111
|
+
|
|
112
|
+
// Required fields validation
|
|
113
|
+
const requiredFields = [
|
|
114
|
+
{ pattern: /^name = "/m, description: 'Worker name' },
|
|
115
|
+
{ pattern: /^compatibility_date = "/m, description: 'Compatibility date' },
|
|
116
|
+
{ pattern: /^main = "/m, description: 'Main entry point' }
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
requiredFields.forEach(field => {
|
|
120
|
+
if (!config.match(field.pattern)) {
|
|
121
|
+
issues.push(`Missing required field: ${field.description}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Database configuration validation
|
|
126
|
+
const hasDatabaseBinding = config.includes('[[d1_databases]]');
|
|
127
|
+
if (hasDatabaseBinding) {
|
|
128
|
+
if (!config.includes('database_name =')) {
|
|
129
|
+
issues.push('Database binding found but missing database_name');
|
|
130
|
+
}
|
|
131
|
+
if (!config.includes('database_id =')) {
|
|
132
|
+
issues.push('Database binding found but missing database_id');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Environment validation
|
|
137
|
+
const hasProductionEnv = config.includes('[env.production]');
|
|
138
|
+
if (hasProductionEnv) {
|
|
139
|
+
const prodSection = config.split('[env.production]')[1];
|
|
140
|
+
if (prodSection && !prodSection.includes('name =')) {
|
|
141
|
+
warnings.push('Production environment missing explicit name');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Compatibility date validation
|
|
146
|
+
const compatMatch = config.match(/compatibility_date = "([^"]+)"/);
|
|
147
|
+
if (compatMatch) {
|
|
148
|
+
const date = new Date(compatMatch[1]);
|
|
149
|
+
const now = new Date();
|
|
150
|
+
const daysDiff = (now - date) / (1000 * 60 * 60 * 24);
|
|
151
|
+
|
|
152
|
+
if (daysDiff > 365) {
|
|
153
|
+
warnings.push(`Compatibility date is ${Math.floor(daysDiff)} days old`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
isValid: issues.length === 0,
|
|
159
|
+
issues,
|
|
160
|
+
warnings,
|
|
161
|
+
summary: {
|
|
162
|
+
errors: issues.length,
|
|
163
|
+
warnings: warnings.length,
|
|
164
|
+
status: issues.length === 0 ? 'valid' : 'invalid'
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function parseWranglerConfig(configPath = 'wrangler.toml') {
|
|
170
|
+
if (!existsSync(configPath)) {
|
|
171
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const config = readFileSync(configPath, 'utf8');
|
|
175
|
+
const parsed = {};
|
|
176
|
+
|
|
177
|
+
// Extract main fields
|
|
178
|
+
const extractField = (pattern, key) => {
|
|
179
|
+
const match = config.match(pattern);
|
|
180
|
+
if (match) parsed[key] = match[1];
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
extractField(/^name = "([^"]+)"/m, 'name');
|
|
184
|
+
extractField(/^compatibility_date = "([^"]+)"/m, 'compatibilityDate');
|
|
185
|
+
extractField(/^main = "([^"]+)"/m, 'main');
|
|
186
|
+
|
|
187
|
+
// Extract database configuration
|
|
188
|
+
if (config.includes('[[d1_databases]]')) {
|
|
189
|
+
parsed.database = {};
|
|
190
|
+
extractField(/database_name = "([^"]+)"/m, 'name');
|
|
191
|
+
extractField(/database_id = "([^"]+)"/m, 'id');
|
|
192
|
+
|
|
193
|
+
if (parsed.name) parsed.database.name = parsed.name;
|
|
194
|
+
if (parsed.id) parsed.database.id = parsed.id;
|
|
195
|
+
delete parsed.name;
|
|
196
|
+
delete parsed.id;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Extract environment configurations
|
|
200
|
+
parsed.environments = {};
|
|
201
|
+
const envMatches = config.matchAll(/\[env\.([^\]]+)\]/g);
|
|
202
|
+
for (const match of envMatches) {
|
|
203
|
+
const envName = match[1];
|
|
204
|
+
parsed.environments[envName] = {};
|
|
205
|
+
|
|
206
|
+
// Extract env-specific settings (simplified parsing)
|
|
207
|
+
const envSection = config.split(`[env.${envName}]`)[1];
|
|
208
|
+
if (envSection) {
|
|
209
|
+
const nextEnv = envSection.indexOf('[env.');
|
|
210
|
+
const sectionContent = nextEnv > 0 ? envSection.substring(0, nextEnv) : envSection;
|
|
211
|
+
|
|
212
|
+
const nameMatch = sectionContent.match(/name = "([^"]+)"/);
|
|
213
|
+
if (nameMatch) parsed.environments[envName].name = nameMatch[1];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return parsed;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function generateWranglerConfig(options) {
|
|
221
|
+
const {
|
|
222
|
+
name,
|
|
223
|
+
main = 'src/worker/index.js',
|
|
224
|
+
compatibilityDate = new Date().toISOString().split('T')[0],
|
|
225
|
+
database = null,
|
|
226
|
+
environments = {},
|
|
227
|
+
vars = {},
|
|
228
|
+
secrets = []
|
|
229
|
+
} = options;
|
|
230
|
+
|
|
231
|
+
let config = `# Generated Wrangler Configuration
|
|
232
|
+
# Timestamp: ${new Date().toISOString()}
|
|
233
|
+
|
|
234
|
+
name = "${name}"
|
|
235
|
+
main = "${main}"
|
|
236
|
+
compatibility_date = "${compatibilityDate}"
|
|
237
|
+
`;
|
|
238
|
+
|
|
239
|
+
// Add variables
|
|
240
|
+
if (Object.keys(vars).length > 0) {
|
|
241
|
+
config += '\n[vars]\n';
|
|
242
|
+
Object.entries(vars).forEach(([key, value]) => {
|
|
243
|
+
config += `${key} = "${value}"\n`;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Add database configuration
|
|
248
|
+
if (database) {
|
|
249
|
+
config += `
|
|
250
|
+
[[d1_databases]]
|
|
251
|
+
binding = "DB"
|
|
252
|
+
database_name = "${database.name}"
|
|
253
|
+
database_id = "${database.id}"
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Add environments
|
|
258
|
+
Object.entries(environments).forEach(([envName, envConfig]) => {
|
|
259
|
+
config += `\n[env.${envName}]\n`;
|
|
260
|
+
if (envConfig.name) config += `name = "${envConfig.name}"\n`;
|
|
261
|
+
|
|
262
|
+
if (envConfig.vars) {
|
|
263
|
+
Object.entries(envConfig.vars).forEach(([key, value]) => {
|
|
264
|
+
config += `${key} = "${value}"\n`;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (envConfig.database) {
|
|
269
|
+
config += `
|
|
270
|
+
[[env.${envName}.d1_databases]]
|
|
271
|
+
binding = "DB"
|
|
272
|
+
database_name = "${envConfig.database.name}"
|
|
273
|
+
database_id = "${envConfig.database.id}"
|
|
274
|
+
`;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
return config;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function listConfigFiles(directory = '.') {
|
|
282
|
+
const configFiles = [];
|
|
283
|
+
const patterns = ['wrangler.toml', 'package.json', '.env*'];
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
const items = readdirSync(directory);
|
|
287
|
+
|
|
288
|
+
items.forEach(item => {
|
|
289
|
+
const path = join(directory, item);
|
|
290
|
+
const stat = statSync(path);
|
|
291
|
+
|
|
292
|
+
if (stat.isFile()) {
|
|
293
|
+
const matches = patterns.some(pattern => {
|
|
294
|
+
if (pattern.includes('*')) {
|
|
295
|
+
return item.startsWith(pattern.replace('*', ''));
|
|
296
|
+
}
|
|
297
|
+
return item === pattern;
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
if (matches) {
|
|
301
|
+
configFiles.push({
|
|
302
|
+
name: item,
|
|
303
|
+
path,
|
|
304
|
+
size: stat.size,
|
|
305
|
+
modified: stat.mtime
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
return configFiles.sort((a, b) => a.name.localeCompare(b.name));
|
|
312
|
+
} catch (error) {
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Manages multi-environment, multi-customer configuration structure
|
|
6
6
|
* Integrates with Clodo Framework domain and feature flag systems
|
|
7
7
|
*/
|
|
8
|
-
import { CustomerConfigCLI } from '../../../
|
|
8
|
+
import { CustomerConfigCLI } from '../../../dist/config/CustomerConfigCLI.js';
|
|
9
9
|
const command = process.argv[2];
|
|
10
10
|
const args = process.argv.slice(3);
|
|
11
11
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamyla/clodo-framework",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -53,8 +53,10 @@
|
|
|
53
53
|
"files": [
|
|
54
54
|
"dist",
|
|
55
55
|
"types",
|
|
56
|
+
"bin/clodo-service.js",
|
|
56
57
|
"bin/service-management",
|
|
57
58
|
"bin/security",
|
|
59
|
+
"bin/shared/config",
|
|
58
60
|
"docs/README.md",
|
|
59
61
|
"docs/overview.md",
|
|
60
62
|
"docs/SECURITY.md",
|
|
@@ -108,7 +110,9 @@
|
|
|
108
110
|
"author": "Tamyla",
|
|
109
111
|
"license": "GPL-3.0-or-later",
|
|
110
112
|
"dependencies": {
|
|
111
|
-
"wrangler": ">=3.0.0"
|
|
113
|
+
"wrangler": ">=3.0.0",
|
|
114
|
+
"chalk": "^5.3.0",
|
|
115
|
+
"commander": "^11.0.0"
|
|
112
116
|
},
|
|
113
117
|
"devDependencies": {
|
|
114
118
|
"@babel/cli": "^7.23.0",
|
|
@@ -119,8 +123,6 @@
|
|
|
119
123
|
"@semantic-release/git": "^10.0.1",
|
|
120
124
|
"@types/node": "^20.19.19",
|
|
121
125
|
"babel-plugin-transform-import-meta": "^2.3.3",
|
|
122
|
-
"chalk": "^5.3.0",
|
|
123
|
-
"commander": "^11.0.0",
|
|
124
126
|
"eslint": "^8.54.0",
|
|
125
127
|
"jest": "^29.7.0",
|
|
126
128
|
"rimraf": "^5.0.10",
|