@tamyla/clodo-framework 1.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +564 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1393 -0
  4. package/bin/README.md +71 -0
  5. package/bin/clodo-service.js +416 -0
  6. package/bin/security/security-cli.js +96 -0
  7. package/bin/service-management/README.md +74 -0
  8. package/bin/service-management/create-service.js +129 -0
  9. package/bin/service-management/init-service.js +102 -0
  10. package/bin/service-management/init-service.js.backup +889 -0
  11. package/bin/shared/config/customer-cli.js +293 -0
  12. package/dist/config/ConfigurationManager.js +159 -0
  13. package/dist/config/CustomerConfigCLI.js +220 -0
  14. package/dist/config/FeatureManager.js +426 -0
  15. package/dist/config/customers.js +441 -0
  16. package/dist/config/domains.js +180 -0
  17. package/dist/config/features.js +225 -0
  18. package/dist/config/index.js +6 -0
  19. package/dist/database/database-orchestrator.js +730 -0
  20. package/dist/database/index.js +4 -0
  21. package/dist/deployment/auditor.js +971 -0
  22. package/dist/deployment/index.js +10 -0
  23. package/dist/deployment/rollback-manager.js +523 -0
  24. package/dist/deployment/testers/api-tester.js +80 -0
  25. package/dist/deployment/testers/auth-tester.js +129 -0
  26. package/dist/deployment/testers/core.js +217 -0
  27. package/dist/deployment/testers/database-tester.js +105 -0
  28. package/dist/deployment/testers/index.js +74 -0
  29. package/dist/deployment/testers/load-tester.js +120 -0
  30. package/dist/deployment/testers/performance-tester.js +105 -0
  31. package/dist/deployment/validator.js +558 -0
  32. package/dist/deployment/wrangler-deployer.js +574 -0
  33. package/dist/handlers/GenericRouteHandler.js +532 -0
  34. package/dist/index.js +39 -0
  35. package/dist/migration/MigrationAdapters.js +562 -0
  36. package/dist/modules/ModuleManager.js +668 -0
  37. package/dist/modules/security.js +98 -0
  38. package/dist/orchestration/cross-domain-coordinator.js +1083 -0
  39. package/dist/orchestration/index.js +5 -0
  40. package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
  41. package/dist/orchestration/modules/DomainResolver.js +196 -0
  42. package/dist/orchestration/modules/StateManager.js +332 -0
  43. package/dist/orchestration/multi-domain-orchestrator.js +255 -0
  44. package/dist/routing/EnhancedRouter.js +158 -0
  45. package/dist/schema/SchemaManager.js +778 -0
  46. package/dist/security/ConfigurationValidator.js +490 -0
  47. package/dist/security/DeploymentManager.js +208 -0
  48. package/dist/security/SecretGenerator.js +142 -0
  49. package/dist/security/SecurityCLI.js +228 -0
  50. package/dist/security/index.js +51 -0
  51. package/dist/security/patterns/environment-rules.js +66 -0
  52. package/dist/security/patterns/insecure-patterns.js +21 -0
  53. package/dist/service-management/ConfirmationEngine.js +411 -0
  54. package/dist/service-management/ErrorTracker.js +294 -0
  55. package/dist/service-management/GenerationEngine.js +3109 -0
  56. package/dist/service-management/InputCollector.js +237 -0
  57. package/dist/service-management/ServiceCreator.js +229 -0
  58. package/dist/service-management/ServiceInitializer.js +448 -0
  59. package/dist/service-management/ServiceOrchestrator.js +638 -0
  60. package/dist/service-management/handlers/ConfigMutator.js +130 -0
  61. package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
  62. package/dist/service-management/handlers/GenerationHandler.js +80 -0
  63. package/dist/service-management/handlers/InputHandler.js +59 -0
  64. package/dist/service-management/handlers/ValidationHandler.js +203 -0
  65. package/dist/service-management/index.js +7 -0
  66. package/dist/services/GenericDataService.js +488 -0
  67. package/dist/shared/cloudflare/domain-discovery.js +562 -0
  68. package/dist/shared/cloudflare/domain-manager.js +912 -0
  69. package/dist/shared/cloudflare/index.js +8 -0
  70. package/dist/shared/cloudflare/ops.js +387 -0
  71. package/dist/shared/config/cache.js +1167 -0
  72. package/dist/shared/config/command-config-manager.js +174 -0
  73. package/dist/shared/config/customer-cli.js +258 -0
  74. package/dist/shared/config/index.js +9 -0
  75. package/dist/shared/config/manager.js +289 -0
  76. package/dist/shared/database/connection-manager.js +338 -0
  77. package/dist/shared/database/index.js +7 -0
  78. package/dist/shared/database/orchestrator.js +632 -0
  79. package/dist/shared/deployment/auditor.js +971 -0
  80. package/dist/shared/deployment/index.js +10 -0
  81. package/dist/shared/deployment/rollback-manager.js +523 -0
  82. package/dist/shared/deployment/validator.js +558 -0
  83. package/dist/shared/index.js +32 -0
  84. package/dist/shared/monitoring/health-checker.js +250 -0
  85. package/dist/shared/monitoring/index.js +8 -0
  86. package/dist/shared/monitoring/memory-manager.js +382 -0
  87. package/dist/shared/monitoring/production-monitor.js +390 -0
  88. package/dist/shared/production-tester/api-tester.js +80 -0
  89. package/dist/shared/production-tester/auth-tester.js +129 -0
  90. package/dist/shared/production-tester/core.js +217 -0
  91. package/dist/shared/production-tester/database-tester.js +105 -0
  92. package/dist/shared/production-tester/index.js +74 -0
  93. package/dist/shared/production-tester/load-tester.js +120 -0
  94. package/dist/shared/production-tester/performance-tester.js +105 -0
  95. package/dist/shared/security/api-token-manager.js +296 -0
  96. package/dist/shared/security/index.js +8 -0
  97. package/dist/shared/security/secret-generator.js +918 -0
  98. package/dist/shared/security/secure-token-manager.js +379 -0
  99. package/dist/shared/utils/error-recovery.js +240 -0
  100. package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
  101. package/dist/shared/utils/index.js +9 -0
  102. package/dist/shared/utils/interactive-prompts.js +134 -0
  103. package/dist/shared/utils/rate-limiter.js +249 -0
  104. package/dist/utils/ErrorHandler.js +173 -0
  105. package/dist/utils/deployment/config-cache.js +1160 -0
  106. package/dist/utils/deployment/index.js +6 -0
  107. package/dist/utils/deployment/interactive-prompts.js +97 -0
  108. package/dist/utils/deployment/secret-generator.js +896 -0
  109. package/dist/utils/dirname-helper.js +35 -0
  110. package/dist/utils/domain-config.js +159 -0
  111. package/dist/utils/error-recovery.js +240 -0
  112. package/dist/utils/esm-helper.js +52 -0
  113. package/dist/utils/framework-config.js +481 -0
  114. package/dist/utils/graceful-shutdown-manager.js +379 -0
  115. package/dist/utils/health-checker.js +114 -0
  116. package/dist/utils/index.js +36 -0
  117. package/dist/utils/prompt-handler.js +98 -0
  118. package/dist/utils/usage-tracker.js +252 -0
  119. package/dist/utils/validation.js +112 -0
  120. package/dist/version/VersionDetector.js +723 -0
  121. package/dist/worker/index.js +4 -0
  122. package/dist/worker/integration.js +332 -0
  123. package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
  124. package/docs/INTEGRATION_GUIDE.md +2045 -0
  125. package/docs/README.md +82 -0
  126. package/docs/SECURITY.md +242 -0
  127. package/docs/deployment/deployment-guide.md +540 -0
  128. package/docs/overview.md +280 -0
  129. package/package.json +176 -0
  130. package/types/index.d.ts +575 -0
@@ -0,0 +1,441 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
2
+ // eslint-disable-next-line no-unused-vars
3
+ import { resolve, join } from 'path';
4
+ import { createDomainConfigSchema, validateDomainConfig, createDomainRegistry } from './domains.js';
5
+ import { createLogger } from '../utils/index.js';
6
+ import { getDirname } from '../utils/esm-helper.js';
7
+ const __dirname = getDirname(import.meta.url, 'src/config');
8
+ const logger = createLogger('CustomerConfig');
9
+
10
+ /**
11
+ * Customer Configuration Manager
12
+ * Manages multi-environment, multi-customer configuration structure
13
+ * Integrates with existing domain and feature flag systems
14
+ */
15
+ export class CustomerConfigurationManager {
16
+ constructor(options = {}) {
17
+ this.configDir = options.configDir || resolve(__dirname, '..', '..', 'config');
18
+ this.environments = options.environments || ['development', 'staging', 'production'];
19
+ this.domainRegistry = createDomainRegistry({});
20
+ this.customers = new Map();
21
+ }
22
+
23
+ /**
24
+ * Create a new customer configuration from template
25
+ * @param {string} customerName - Customer identifier
26
+ * @param {string} domain - Customer domain (optional)
27
+ * @param {Object} options - Additional customer options
28
+ */
29
+ async createCustomer(customerName, domain = null, options = {}) {
30
+ logger.info(`Creating customer configuration: ${customerName}`);
31
+ const customerDir = resolve(this.configDir, 'customers', customerName);
32
+
33
+ // Create customer directory structure
34
+ if (!existsSync(customerDir)) {
35
+ mkdirSync(customerDir, {
36
+ recursive: true
37
+ });
38
+ logger.info(`Created customer directory: ${customerDir}`);
39
+ }
40
+
41
+ // Create domain configuration for customer
42
+ const domainConfig = this.createCustomerDomainConfig(customerName, domain, options);
43
+ this.domainRegistry.add(customerName, domainConfig);
44
+
45
+ // Create environment-specific configs from templates
46
+ for (const env of this.environments) {
47
+ await this.createCustomerEnvironment(customerName, env, domain, options);
48
+ }
49
+
50
+ // Store customer metadata
51
+ this.customers.set(customerName, {
52
+ name: customerName,
53
+ domain: domain,
54
+ createdAt: new Date().toISOString(),
55
+ environments: this.environments,
56
+ ...options
57
+ });
58
+ logger.info(`Customer ${customerName} configuration created successfully`);
59
+ return this.getCustomerInfo(customerName);
60
+ }
61
+
62
+ /**
63
+ * Create domain configuration for customer
64
+ */
65
+ createCustomerDomainConfig(customerName, domain, options) {
66
+ const baseConfig = createDomainConfigSchema();
67
+
68
+ // Generate customer-specific domain config
69
+ const domainConfig = {
70
+ ...baseConfig,
71
+ name: customerName,
72
+ displayName: options.displayName || this.capitalizeFirst(customerName),
73
+ accountId: options.accountId || '00000000000000000000000000000000',
74
+ // Placeholder for onboarding
75
+ zoneId: options.zoneId || '00000000000000000000000000000000',
76
+ // Placeholder for onboarding
77
+
78
+ domains: {
79
+ production: domain || `${customerName}.com`,
80
+ staging: `staging.${domain || `${customerName}.com`}`,
81
+ development: `dev.${domain || `${customerName}.com`}`
82
+ },
83
+ features: {
84
+ // Customer-specific feature flags
85
+ customerIsolation: true,
86
+ customDomain: !!domain,
87
+ multiEnvironment: true,
88
+ ...options.features
89
+ },
90
+ settings: {
91
+ ...baseConfig.settings,
92
+ customerName: customerName,
93
+ customerDomain: domain,
94
+ ...options.settings
95
+ }
96
+ };
97
+
98
+ // Skip validation during initial creation if using placeholders
99
+ if (options.skipValidation || !options.accountId || !options.zoneId) {
100
+ logger.info(`Created customer domain config for ${customerName} (framework mode - placeholders used)`);
101
+ return domainConfig;
102
+ }
103
+ validateDomainConfig(domainConfig);
104
+ return domainConfig;
105
+ }
106
+
107
+ /**
108
+ * Create customer environment config from template
109
+ */
110
+ // eslint-disable-next-line no-unused-vars
111
+ async createCustomerEnvironment(customerName, environment, domain, options) {
112
+ const templatePath = resolve(this.configDir, 'customers', 'template', `${environment}.env.template`);
113
+ const outputPath = resolve(this.configDir, 'customers', customerName, `${environment}.env`);
114
+
115
+ // Check if template exists
116
+ if (!existsSync(templatePath)) {
117
+ logger.warn(`Template not found: ${templatePath}, skipping ${environment} config`);
118
+ return;
119
+ }
120
+
121
+ // Skip if config already exists
122
+ if (existsSync(outputPath)) {
123
+ logger.info(`Config already exists: ${outputPath}, skipping`);
124
+ return;
125
+ }
126
+ try {
127
+ // Read template and replace placeholders
128
+ let template = readFileSync(templatePath, 'utf8');
129
+
130
+ // Replace customer-specific placeholders
131
+ template = template.replace(/\{\{CUSTOMER_NAME\}\}/g, customerName);
132
+ template = template.replace(/\{\{CUSTOMER_DOMAIN\}\}/g, domain || `${customerName}.com`);
133
+ template = template.replace(/\{\{ENVIRONMENT\}\}/g, environment);
134
+
135
+ // Environment-specific domain replacements
136
+ const envDomains = this.getEnvironmentDomains(customerName, domain);
137
+ template = template.replace(/\{\{DOMAIN\}\}/g, envDomains[environment]);
138
+
139
+ // Write customer config
140
+ writeFileSync(outputPath, template);
141
+ logger.info(`Created customer config: ${environment}.env for ${customerName}`);
142
+ } catch (error) {
143
+ logger.error(`Failed to create ${environment} config for ${customerName}:`, error.message);
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Get environment-specific domains for customer
150
+ */
151
+ getEnvironmentDomains(customerName, domain) {
152
+ const baseDomain = domain || `${customerName}.com`;
153
+ return {
154
+ production: baseDomain,
155
+ staging: `staging.${baseDomain}`,
156
+ development: `dev.${baseDomain}`
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Validate customer configurations
162
+ */
163
+ async validateConfigs() {
164
+ logger.info('Validating customer configuration structure...');
165
+ let valid = true;
166
+ const errors = [];
167
+
168
+ // Validate base configuration files
169
+ const baseFiles = ['wrangler.base.toml', 'variables.base.env'];
170
+ for (const file of baseFiles) {
171
+ const path = resolve(this.configDir, 'base', file);
172
+ if (!existsSync(path)) {
173
+ errors.push(`Missing base config: ${file}`);
174
+ valid = false;
175
+ }
176
+ }
177
+
178
+ // Validate environment configs
179
+ for (const env of this.environments) {
180
+ const envFile = `${env}.toml`;
181
+ const path = resolve(this.configDir, 'environments', envFile);
182
+ if (!existsSync(path)) {
183
+ errors.push(`Missing environment config: ${envFile}`);
184
+ valid = false;
185
+ }
186
+ }
187
+
188
+ // Validate customer configs
189
+ const customersDir = resolve(this.configDir, 'customers');
190
+ if (existsSync(customersDir)) {
191
+ const customerDirs = this.getCustomerDirectories();
192
+ for (const customerName of customerDirs) {
193
+ const customerValid = await this.validateCustomerConfig(customerName);
194
+ if (!customerValid) {
195
+ valid = false;
196
+ }
197
+ }
198
+ }
199
+ if (errors.length > 0) {
200
+ logger.error('Configuration validation errors:', errors);
201
+ }
202
+ logger.info(`Configuration validation ${valid ? 'passed' : 'failed'}`);
203
+ return {
204
+ valid,
205
+ errors
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Validate individual customer configuration
211
+ */
212
+ async validateCustomerConfig(customerName) {
213
+ const customerDir = resolve(this.configDir, 'customers', customerName);
214
+ let valid = true;
215
+
216
+ // Check environment files exist
217
+ for (const env of this.environments) {
218
+ const envFile = resolve(customerDir, `${env}.env`);
219
+ if (!existsSync(envFile)) {
220
+ logger.error(`Missing ${env}.env for customer ${customerName}`);
221
+ valid = false;
222
+ }
223
+ }
224
+
225
+ // Validate domain configuration (skip in framework mode)
226
+ try {
227
+ const domainConfig = this.domainRegistry.get(customerName);
228
+ if (!domainConfig.settings?.isFrameworkMode) {
229
+ validateDomainConfig(domainConfig);
230
+ } else {
231
+ logger.info(`Skipping domain validation for ${customerName} (framework mode)`);
232
+ }
233
+ } catch (error) {
234
+ logger.error(`Invalid domain config for ${customerName}:`, error.message);
235
+ valid = false;
236
+ }
237
+ return valid;
238
+ }
239
+
240
+ /**
241
+ * Show effective configuration for customer/environment
242
+ */
243
+ showConfig(customerName, environment) {
244
+ logger.info(`Showing effective configuration: ${customerName}/${environment}`);
245
+
246
+ // Get domain config from registry
247
+ let domainConfig;
248
+ try {
249
+ domainConfig = this.domainRegistry.get(customerName);
250
+ } catch (error) {
251
+ // If not in registry, try to find customer metadata
252
+ const customerMeta = this.customers.get(customerName);
253
+ if (customerMeta) {
254
+ // Recreate domain config from metadata
255
+ domainConfig = this.createCustomerDomainConfig(customerName, customerMeta.domain, customerMeta);
256
+ } else {
257
+ throw new Error(`Customer not found: ${customerName}`);
258
+ }
259
+ }
260
+ const config = {
261
+ customer: customerName,
262
+ environment: environment,
263
+ domain: domainConfig,
264
+ variables: {},
265
+ features: {}
266
+ };
267
+
268
+ // Load base variables
269
+ const baseVarsPath = resolve(this.configDir, 'base', 'variables.base.env');
270
+ if (existsSync(baseVarsPath)) {
271
+ config.variables.base = this.parseEnvFile(baseVarsPath);
272
+ }
273
+
274
+ // Load customer environment variables
275
+ const customerConfigPath = resolve(this.configDir, 'customers', customerName, `${environment}.env`);
276
+ if (existsSync(customerConfigPath)) {
277
+ config.variables.customer = this.parseEnvFile(customerConfigPath);
278
+ }
279
+
280
+ // Get domain features
281
+ config.features = domainConfig.features || {};
282
+ return config;
283
+ }
284
+
285
+ /**
286
+ * Generate deployment command for customer/environment
287
+ */
288
+ getDeployCommand(customerName, environment) {
289
+ const commands = {
290
+ development: `wrangler dev --config config/environments/development.toml`,
291
+ staging: `wrangler deploy --config config/environments/staging.toml --env staging`,
292
+ production: `wrangler deploy --env production`
293
+ };
294
+ const command = commands[environment];
295
+ if (!command) {
296
+ throw new Error(`Unknown environment: ${environment}`);
297
+ }
298
+ logger.info(`Generated deploy command for ${customerName}/${environment}`);
299
+ return {
300
+ command: command,
301
+ customer: customerName,
302
+ environment: environment,
303
+ configPath: `config/customers/${customerName}/${environment}.env`
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Get customer information
309
+ */
310
+ getCustomerInfo(customerName) {
311
+ const domainConfig = this.domainRegistry.get(customerName);
312
+ const customerMeta = this.customers.get(customerName);
313
+ return {
314
+ ...customerMeta,
315
+ domainConfig: domainConfig,
316
+ configPath: resolve(this.configDir, 'customers', customerName)
317
+ };
318
+ }
319
+
320
+ /**
321
+ * List all customers
322
+ */
323
+ listCustomers() {
324
+ return Array.from(this.customers.keys()).map(name => ({
325
+ name: name,
326
+ ...this.getCustomerInfo(name)
327
+ }));
328
+ }
329
+
330
+ /**
331
+ * Load existing customers from filesystem
332
+ */
333
+ async loadExistingCustomers() {
334
+ const customersDir = resolve(this.configDir, 'customers');
335
+ if (!existsSync(customersDir)) {
336
+ return;
337
+ }
338
+ try {
339
+ // Read customer directories
340
+ const customerDirs = this.getCustomerDirectories();
341
+ for (const customerName of customerDirs) {
342
+ // Skip template directory
343
+ if (customerName === 'template') continue;
344
+ const customerDir = resolve(customersDir, customerName);
345
+
346
+ // Try to load customer metadata from a metadata file or infer from configs
347
+ const metadata = {
348
+ name: customerName,
349
+ createdAt: new Date().toISOString(),
350
+ // Placeholder
351
+ environments: this.environments
352
+ };
353
+
354
+ // Try to infer domain from production config
355
+ const prodConfigPath = resolve(customerDir, 'production.env');
356
+ if (existsSync(prodConfigPath)) {
357
+ try {
358
+ const prodConfig = this.parseEnvFile(prodConfigPath);
359
+ if (prodConfig.DOMAIN) {
360
+ metadata.domain = prodConfig.DOMAIN.replace(/^https?:\/\//, '');
361
+ }
362
+ } catch (error) {
363
+ logger.warn(`Could not parse production config for ${customerName}:`, error.message);
364
+ }
365
+ }
366
+
367
+ // Register customer
368
+ this.customers.set(customerName, metadata);
369
+
370
+ // Try to register domain config
371
+ try {
372
+ const domainConfig = this.createCustomerDomainConfig(customerName, metadata.domain, {
373
+ skipValidation: true,
374
+ isFrameworkMode: true
375
+ });
376
+ this.domainRegistry.add(customerName, domainConfig);
377
+ } catch (error) {
378
+ logger.warn(`Could not register domain for existing customer ${customerName}:`, error.message);
379
+ }
380
+ logger.info(`Loaded existing customer: ${customerName}`);
381
+ }
382
+ } catch (error) {
383
+ logger.error('Error loading existing customers:', error.message);
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Get customer directories from filesystem
389
+ */
390
+ getCustomerDirectories() {
391
+ const customersDir = resolve(this.configDir, 'customers');
392
+ if (!existsSync(customersDir)) {
393
+ return [];
394
+ }
395
+ try {
396
+ // Read directory contents
397
+ const items = readdirSync(customersDir);
398
+ return items.filter(item => {
399
+ const itemPath = resolve(customersDir, item);
400
+ return statSync(itemPath).isDirectory() && item !== 'template';
401
+ });
402
+ } catch (error) {
403
+ logger.error('Error reading customer directories:', error.message);
404
+ return [];
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Parse environment file
410
+ */
411
+ parseEnvFile(filePath) {
412
+ const content = readFileSync(filePath, 'utf8');
413
+ const variables = {};
414
+ content.split('\n').forEach(line => {
415
+ const trimmed = line.trim();
416
+ if (trimmed && !trimmed.startsWith('#')) {
417
+ const [key, ...valueParts] = trimmed.split('=');
418
+ if (key && valueParts.length > 0) {
419
+ variables[key.trim()] = valueParts.join('=').trim();
420
+ }
421
+ }
422
+ });
423
+ return variables;
424
+ }
425
+
426
+ /**
427
+ * Capitalize first letter
428
+ */
429
+ capitalizeFirst(str) {
430
+ return str.charAt(0).toUpperCase() + str.slice(1);
431
+ }
432
+ }
433
+
434
+ // Default instance
435
+ export const customerConfigManager = new CustomerConfigurationManager();
436
+
437
+ // Convenience functions
438
+ export const createCustomer = (name, domain, options) => customerConfigManager.createCustomer(name, domain, options);
439
+ export const validateCustomerConfigs = () => customerConfigManager.validateConfigs();
440
+ export const showCustomerConfig = (customer, env) => customerConfigManager.showConfig(customer, env);
441
+ export const getCustomerDeployCommand = (customer, env) => customerConfigManager.getDeployCommand(customer, env);
@@ -0,0 +1,180 @@
1
+ import { deepMerge, validateRequired, createLogger } from '../utils/index.js';
2
+ const logger = createLogger('DomainConfig');
3
+
4
+ /**
5
+ * Creates a base domain configuration schema
6
+ * @returns {Object} Base domain configuration template
7
+ */
8
+ export const createDomainConfigSchema = () => ({
9
+ name: '',
10
+ displayName: '',
11
+ accountId: '',
12
+ zoneId: '',
13
+ domains: {
14
+ production: '',
15
+ staging: '',
16
+ development: ''
17
+ },
18
+ services: {},
19
+ databases: {},
20
+ features: {},
21
+ settings: {
22
+ environment: 'development',
23
+ logLevel: 'info',
24
+ corsOrigins: ['*']
25
+ }
26
+ });
27
+
28
+ /**
29
+ * Validates a domain configuration object
30
+ * @param {Object} config - Domain configuration to validate
31
+ * @throws {Error} If validation fails
32
+ */
33
+ export const validateDomainConfig = config => {
34
+ try {
35
+ // Required top-level fields
36
+ validateRequired(config, ['name', 'accountId', 'zoneId']);
37
+
38
+ // Validate Cloudflare IDs format (basic check)
39
+ if (!/^[a-f0-9]{32}$/.test(config.accountId)) {
40
+ throw new Error('accountId must be a valid Cloudflare account ID (32 hex characters)');
41
+ }
42
+ if (!/^[a-f0-9]{32}$/.test(config.zoneId)) {
43
+ throw new Error('zoneId must be a valid Cloudflare zone ID (32 hex characters)');
44
+ }
45
+
46
+ // Validate domains structure
47
+ if (!config.domains || typeof config.domains !== 'object') {
48
+ throw new Error('domains must be an object with production/staging/development keys');
49
+ }
50
+
51
+ // At least one domain should be configured
52
+ const hasDomain = Object.values(config.domains).some(domain => domain && domain.trim());
53
+ if (!hasDomain) {
54
+ throw new Error('At least one domain (production, staging, or development) must be configured');
55
+ }
56
+
57
+ // Validate domain formats (basic URL validation)
58
+ Object.entries(config.domains).forEach(([env, domain]) => {
59
+ if (domain && domain.trim()) {
60
+ try {
61
+ new URL(`https://${domain}`);
62
+ } catch {
63
+ throw new Error(`Invalid domain format for ${env}: ${domain}`);
64
+ }
65
+ }
66
+ });
67
+ logger.info(`Domain configuration validated: ${config.name}`);
68
+ } catch (error) {
69
+ logger.error(`Domain configuration validation failed: ${error.message}`);
70
+ throw error;
71
+ }
72
+ };
73
+
74
+ /**
75
+ * Merges base domain config with service-specific extensions
76
+ * @param {Object} baseConfig - Base domain configuration
77
+ * @param {Object} serviceConfig - Service-specific configuration extensions
78
+ * @returns {Object} Merged configuration
79
+ */
80
+ export const mergeDomainConfigs = (baseConfig, serviceConfig) => {
81
+ if (!baseConfig || !serviceConfig) {
82
+ throw new Error('Both baseConfig and serviceConfig are required for merging');
83
+ }
84
+ const merged = deepMerge(baseConfig, serviceConfig);
85
+
86
+ // Validate the merged configuration
87
+ validateDomainConfig(merged);
88
+ logger.info(`Domain configurations merged: ${baseConfig.name}`);
89
+ return merged;
90
+ };
91
+
92
+ /**
93
+ * Creates a domain configuration registry
94
+ * @param {Object} domainConfigs - Object containing domain configurations
95
+ * @returns {Object} Domain registry with lookup methods
96
+ */
97
+ export const createDomainRegistry = domainConfigs => {
98
+ const registry = {
99
+ ...domainConfigs
100
+ };
101
+ return {
102
+ get: domainName => {
103
+ const config = registry[domainName];
104
+ if (!config) {
105
+ throw new Error(`Domain not found: ${domainName}`);
106
+ }
107
+ return config;
108
+ },
109
+ list: () => Object.keys(registry),
110
+ validateAll: () => {
111
+ Object.values(registry).forEach(validateDomainConfig);
112
+ logger.info(`All ${Object.keys(registry).length} domain configurations validated`);
113
+ },
114
+ add: (domainName, config) => {
115
+ validateDomainConfig(config);
116
+ registry[domainName] = config;
117
+ logger.info(`Domain added to registry: ${domainName}`);
118
+ },
119
+ remove: domainName => {
120
+ if (!registry[domainName]) {
121
+ throw new Error(`Domain not found: ${domainName}`);
122
+ }
123
+ delete registry[domainName];
124
+ logger.info(`Domain removed from registry: ${domainName}`);
125
+ }
126
+ };
127
+ };
128
+
129
+ /**
130
+ * Gets domain configuration from environment variables
131
+ * @param {Object} env - Environment variables
132
+ * @param {Object} domainConfigs - Available domain configurations
133
+ * @returns {Object} Domain configuration for current environment
134
+ */
135
+ export const getDomainFromEnv = (env, domainConfigs) => {
136
+ const domainName = env.DOMAIN_NAME || env.CF_DOMAIN_NAME || 'default';
137
+ if (!domainConfigs[domainName]) {
138
+ logger.warn(`Domain not found: ${domainName}, using default`);
139
+ return domainConfigs.default || Object.values(domainConfigs)[0];
140
+ }
141
+ return domainConfigs[domainName];
142
+ };
143
+
144
+ /**
145
+ * Creates environment-specific configuration overrides
146
+ * @param {Object} baseConfig - Base domain configuration
147
+ * @param {string} environment - Target environment (production, staging, development)
148
+ * @returns {Object} Environment-specific configuration
149
+ */
150
+ export const createEnvironmentConfig = (baseConfig, environment = 'development') => {
151
+ const envConfig = {
152
+ ...baseConfig
153
+ };
154
+
155
+ // Set environment-specific settings
156
+ envConfig.settings = {
157
+ ...envConfig.settings,
158
+ environment,
159
+ isProduction: environment === 'production',
160
+ isStaging: environment === 'staging',
161
+ isDevelopment: environment === 'development'
162
+ };
163
+
164
+ // Environment-specific domain selection
165
+ if (envConfig.domains[environment]) {
166
+ envConfig.currentDomain = envConfig.domains[environment];
167
+ }
168
+
169
+ // Environment-specific feature overrides
170
+ if (environment === 'production') {
171
+ // Ensure critical features are enabled in production
172
+ envConfig.features = {
173
+ ...envConfig.features,
174
+ errorReporting: true,
175
+ monitoring: true
176
+ };
177
+ }
178
+ logger.info(`Environment config created for ${baseConfig.name} (${environment})`);
179
+ return envConfig;
180
+ };