@tamyla/clodo-framework 2.0.18 → 2.0.20

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.
@@ -1,347 +0,0 @@
1
- /**
2
- * Configuration Persistence Manager
3
- * Saves deployment configurations to customer config files for reuse
4
- *
5
- * This eliminates developer friction by:
6
- * - Saving all deployment inputs after successful deployment
7
- * - Auto-loading configurations for repeat deployments
8
- * - Eliminating re-entry of account IDs, zone IDs, URLs, etc.
9
- */
10
-
11
- import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';
12
- import { join, dirname } from 'path';
13
- import { fileURLToPath } from 'url';
14
-
15
- // ESM-compatible __dirname and __filename
16
- let __dirname;
17
- let __filename;
18
- try {
19
- __filename = fileURLToPath(import.meta.url);
20
- __dirname = dirname(__filename);
21
- } catch {
22
- // Fallback for test environments
23
- __dirname = process.cwd();
24
- __filename = __filename || 'config-persistence.js';
25
- }
26
-
27
- /**
28
- * ConfigPersistenceManager
29
- * Handles saving and loading deployment configurations
30
- */
31
- export class ConfigPersistenceManager {
32
- constructor(options = {}) {
33
- // Default to config/customers in the target service directory
34
- this.configDir = options.configDir || join(process.cwd(), 'config', 'customers');
35
- }
36
-
37
- /**
38
- * Save deployment configuration to customer environment file
39
- * @param {Object} deployment - Complete deployment configuration
40
- * @param {string} deployment.customer - Customer name
41
- * @param {string} deployment.environment - Environment (development/staging/production)
42
- * @param {Object} deployment.coreInputs - Tier 1 core inputs
43
- * @param {Object} deployment.confirmations - Tier 2 confirmations
44
- * @param {Object} deployment.result - Deployment result
45
- */
46
- async saveDeploymentConfig(deployment) {
47
- const {
48
- customer,
49
- environment,
50
- coreInputs,
51
- confirmations,
52
- result
53
- } = deployment;
54
- if (!customer || !environment) {
55
- throw new Error('Customer and environment are required');
56
- }
57
-
58
- // Create customer directory if it doesn't exist
59
- const customerDir = join(this.configDir, customer);
60
- if (!existsSync(customerDir)) {
61
- mkdirSync(customerDir, {
62
- recursive: true
63
- });
64
- }
65
-
66
- // Generate environment file content
67
- const envContent = this.generateEnvFileContent({
68
- customer,
69
- environment,
70
- coreInputs,
71
- confirmations,
72
- result
73
- });
74
-
75
- // Write to customer environment file
76
- const envFile = join(customerDir, `${environment}.env`);
77
- writeFileSync(envFile, envContent, 'utf8');
78
- console.log(` 💾 Configuration saved: ${envFile}`);
79
- return envFile;
80
- }
81
-
82
- /**
83
- * Generate .env file content from deployment data
84
- * @param {Object} data - Deployment data
85
- * @returns {string} .env file content
86
- */
87
- generateEnvFileContent(data) {
88
- const {
89
- customer,
90
- environment,
91
- coreInputs,
92
- confirmations,
93
- result
94
- } = data;
95
- const timestamp = new Date().toISOString();
96
- const lines = [`# Deployment Configuration - ${customer} (${environment})`, `# Last Updated: ${timestamp}`, `# Auto-generated by Clodo Framework deployment`, '', '# ============================================', '# Core Customer Identity', '# ============================================', `CUSTOMER_ID=${customer}`, `CUSTOMER_NAME=${customer}`, `ENVIRONMENT=${environment}`, ''];
97
-
98
- // Cloudflare Configuration
99
- if (coreInputs.cloudflareAccountId || coreInputs.cloudflareZoneId) {
100
- lines.push('# ============================================');
101
- lines.push('# Cloudflare Configuration');
102
- lines.push('# ============================================');
103
- if (coreInputs.cloudflareAccountId) {
104
- lines.push(`CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}`);
105
- }
106
- if (coreInputs.cloudflareZoneId) {
107
- lines.push(`CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}`);
108
- }
109
- if (coreInputs.domainName) {
110
- lines.push(`CUSTOMER_DOMAIN=${coreInputs.domainName}`);
111
- lines.push(`DOMAIN=${coreInputs.domainName}`);
112
- }
113
- lines.push('# CLOUDFLARE_API_TOKEN=<stored-securely-in-env>');
114
- lines.push('');
115
- }
116
-
117
- // Service Configuration
118
- lines.push('# ============================================');
119
- lines.push('# Service Configuration');
120
- lines.push('# ============================================');
121
- if (coreInputs.serviceName) {
122
- lines.push(`SERVICE_NAME=${coreInputs.serviceName}`);
123
- }
124
- if (coreInputs.serviceType) {
125
- lines.push(`SERVICE_TYPE=${coreInputs.serviceType}`);
126
- }
127
- if (confirmations.displayName) {
128
- lines.push(`DISPLAY_NAME=${confirmations.displayName}`);
129
- }
130
- if (confirmations.description) {
131
- lines.push(`DESCRIPTION=${confirmations.description}`);
132
- }
133
- if (confirmations.version) {
134
- lines.push(`VERSION=${confirmations.version}`);
135
- }
136
- if (confirmations.author) {
137
- lines.push(`AUTHOR=${confirmations.author}`);
138
- }
139
- lines.push('');
140
-
141
- // Database & Worker
142
- if (confirmations.databaseName || confirmations.workerName) {
143
- lines.push('# ============================================');
144
- lines.push('# Cloudflare Resources');
145
- lines.push('# ============================================');
146
- if (confirmations.workerName) {
147
- lines.push(`WORKER_NAME=${confirmations.workerName}`);
148
- }
149
- if (confirmations.databaseName) {
150
- lines.push(`DATABASE_NAME=${confirmations.databaseName}`);
151
- lines.push(`D1_DATABASE_NAME=${confirmations.databaseName}`);
152
- }
153
- lines.push('');
154
- }
155
-
156
- // URLs & Endpoints
157
- if (confirmations.productionUrl || confirmations.stagingUrl || confirmations.developmentUrl) {
158
- lines.push('# ============================================');
159
- lines.push('# Environment URLs');
160
- lines.push('# ============================================');
161
- if (confirmations.productionUrl) {
162
- lines.push(`PRODUCTION_URL=${confirmations.productionUrl}`);
163
- }
164
- if (confirmations.stagingUrl) {
165
- lines.push(`STAGING_URL=${confirmations.stagingUrl}`);
166
- }
167
- if (confirmations.developmentUrl) {
168
- lines.push(`DEVELOPMENT_URL=${confirmations.developmentUrl}`);
169
- }
170
- if (confirmations.documentationUrl) {
171
- lines.push(`DOCUMENTATION_URL=${confirmations.documentationUrl}`);
172
- }
173
- if (confirmations.deploymentUrl) {
174
- lines.push(`DEPLOYMENT_URL=${confirmations.deploymentUrl}`);
175
- }
176
- if (result && result.url) {
177
- lines.push(`API_URL=${result.url}`);
178
- }
179
- lines.push('');
180
- }
181
-
182
- // API Configuration
183
- if (confirmations.packageName || confirmations.apiBasePath || confirmations.healthCheckPath) {
184
- lines.push('# ============================================');
185
- lines.push('# API Configuration');
186
- lines.push('# ============================================');
187
- if (confirmations.packageName) {
188
- lines.push(`PACKAGE_NAME=${confirmations.packageName}`);
189
- }
190
- if (confirmations.apiBasePath) {
191
- lines.push(`API_BASE_PATH=${confirmations.apiBasePath}`);
192
- }
193
- if (confirmations.healthCheckPath) {
194
- lines.push(`HEALTH_CHECK_PATH=${confirmations.healthCheckPath}`);
195
- }
196
- lines.push(`API_VERSION=v1`);
197
- lines.push('');
198
- }
199
-
200
- // Feature Flags
201
- if (confirmations.features && Object.keys(confirmations.features).length > 0) {
202
- lines.push('# ============================================');
203
- lines.push('# Feature Flags');
204
- lines.push('# ============================================');
205
- Object.entries(confirmations.features).forEach(([feature, enabled]) => {
206
- const featureName = feature.toUpperCase().replace(/([A-Z])/g, '_$1').replace(/^_/, '');
207
- lines.push(`FEATURE_${featureName}=${enabled ? 'true' : 'false'}`);
208
- });
209
- lines.push('');
210
- }
211
-
212
- // Environment-specific settings
213
- lines.push('# ============================================');
214
- lines.push(`# ${environment.charAt(0).toUpperCase() + environment.slice(1)} Environment Settings`);
215
- lines.push('# ============================================');
216
- switch (environment) {
217
- case 'development':
218
- lines.push('ENABLE_DEBUG_MODE=true');
219
- lines.push('ENABLE_DETAILED_LOGGING=true');
220
- lines.push('LOG_LEVEL=debug');
221
- lines.push('RATE_LIMIT_REQUESTS_PER_MINUTE=10000');
222
- lines.push('CORS_ORIGINS=*');
223
- break;
224
- case 'staging':
225
- lines.push('ENABLE_DEBUG_MODE=true');
226
- lines.push('LOG_LEVEL=info');
227
- lines.push('RATE_LIMIT_REQUESTS_PER_MINUTE=1000');
228
- break;
229
- case 'production':
230
- lines.push('ENABLE_DEBUG_MODE=false');
231
- lines.push('LOG_LEVEL=warn');
232
- lines.push('RATE_LIMIT_REQUESTS_PER_MINUTE=60');
233
- lines.push('# Add production-specific secrets via Cloudflare dashboard or wrangler');
234
- break;
235
- }
236
- lines.push('');
237
-
238
- // Monitoring
239
- lines.push('# ============================================');
240
- lines.push('# Monitoring & Observability');
241
- lines.push('# ============================================');
242
- lines.push('METRICS_ENABLED=true');
243
- lines.push('TRACING_ENABLED=true');
244
- lines.push('ERROR_REPORTING_ENABLED=true');
245
- lines.push('');
246
-
247
- // Deployment metadata
248
- lines.push('# ============================================');
249
- lines.push('# Deployment Metadata');
250
- lines.push('# ============================================');
251
- lines.push(`DEPLOYED_AT=${timestamp}`);
252
- if (result && result.status) {
253
- lines.push(`DEPLOYMENT_STATUS=${result.status}`);
254
- }
255
- lines.push('');
256
- return lines.join('\n');
257
- }
258
-
259
- /**
260
- * Check if customer configuration exists
261
- * @param {string} customer - Customer name
262
- * @param {string} environment - Environment
263
- * @returns {boolean} True if configuration exists
264
- */
265
- configExists(customer, environment) {
266
- const envFile = join(this.configDir, customer, `${environment}.env`);
267
- return existsSync(envFile);
268
- }
269
-
270
- /**
271
- * Get all configured customers
272
- * @returns {Array<string>} List of customer names
273
- */
274
- getConfiguredCustomers() {
275
- if (!existsSync(this.configDir)) {
276
- return [];
277
- }
278
-
279
- // ESM-compatible fs import (already imported at top)
280
- const entries = readdirSync(this.configDir, {
281
- withFileTypes: true
282
- });
283
- return entries.filter(entry => entry.isDirectory() && entry.name !== 'template').map(entry => entry.name);
284
- }
285
-
286
- /**
287
- * Load environment file and parse into object
288
- * @param {string} customer - Customer name
289
- * @param {string} environment - Environment
290
- * @returns {Object|null} Parsed environment variables or null if not found
291
- */
292
- loadEnvironmentConfig(customer, environment) {
293
- const envFile = join(this.configDir, customer, `${environment}.env`);
294
- if (!existsSync(envFile)) {
295
- return null;
296
- }
297
- const content = readFileSync(envFile, 'utf8');
298
- const envVars = {};
299
- content.split('\n').forEach(line => {
300
- line = line.trim();
301
-
302
- // Skip comments and empty lines
303
- if (!line || line.startsWith('#')) {
304
- return;
305
- }
306
-
307
- // Parse KEY=value
308
- const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
309
- if (match) {
310
- const [, key, value] = match;
311
- envVars[key] = value;
312
- }
313
- });
314
- return envVars;
315
- }
316
-
317
- /**
318
- * Display customer configuration summary
319
- * @param {string} customer - Customer name
320
- * @param {string} environment - Environment
321
- */
322
- displayCustomerConfig(customer, environment) {
323
- const config = this.loadEnvironmentConfig(customer, environment);
324
- if (!config) {
325
- console.log(` ⚠️ No configuration found for ${customer}/${environment}`);
326
- return;
327
- }
328
- console.log(`\n 📋 Existing Configuration for ${customer}/${environment}:`);
329
- console.log(' ' + '─'.repeat(60));
330
- const highlights = ['CUSTOMER_DOMAIN', 'CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_ZONE_ID', 'SERVICE_NAME', 'WORKER_NAME', 'DATABASE_NAME', 'DEPLOYMENT_URL', 'DEPLOYED_AT'];
331
- highlights.forEach(key => {
332
- if (config[key]) {
333
- let value = config[key];
334
-
335
- // Truncate long IDs for display
336
- if (key.includes('_ID') && value.length > 20) {
337
- value = value.substring(0, 8) + '...' + value.substring(value.length - 4);
338
- }
339
- console.log(` ${key.padEnd(25)}: ${value}`);
340
- }
341
- });
342
- console.log(' ' + '─'.repeat(60));
343
- }
344
- }
345
-
346
- // Export singleton instance for convenience
347
- export const configPersistence = new ConfigPersistenceManager();