@tamyla/clodo-framework 3.1.22 → 3.1.23

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 (208) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +230 -1
  3. package/dist/cli/commands/assess.js +183 -0
  4. package/dist/cli/commands/create.js +77 -0
  5. package/dist/{bin → cli}/commands/deploy.js +8 -8
  6. package/dist/cli/commands/diagnose.js +83 -0
  7. package/dist/{bin → cli}/commands/helpers/deployment-verification.js +2 -3
  8. package/dist/{bin → cli}/commands/helpers/error-recovery.js +1 -1
  9. package/dist/{bin → cli}/commands/helpers/resource-detection.js +1 -1
  10. package/dist/cli/commands/helpers.js +110 -0
  11. package/dist/cli/commands/init-config.js +57 -0
  12. package/dist/cli/commands/update.js +75 -0
  13. package/dist/{bin → cli}/commands/validate.js +5 -5
  14. package/dist/cli/security-cli.js +118 -0
  15. package/dist/config/FeatureManager.js +6 -0
  16. package/dist/config/clodo-create.example.json +26 -0
  17. package/dist/config/clodo-deploy.example.json +41 -0
  18. package/dist/config/clodo-update.example.json +46 -0
  19. package/dist/config/clodo-validate.example.json +41 -0
  20. package/dist/config/customers/template/development.env.template +37 -0
  21. package/dist/config/customers/template/production.env.template +39 -0
  22. package/dist/config/customers/template/staging.env.template +37 -0
  23. package/dist/config/customers.js +625 -0
  24. package/dist/config/domain-examples/README.md +464 -0
  25. package/dist/config/domain-examples/environment-mapped.json +168 -0
  26. package/dist/config/domain-examples/multi-domain.json +144 -0
  27. package/dist/config/domain-examples/single-domain.json +50 -0
  28. package/dist/config/domains.js +186 -0
  29. package/dist/config/examples +12 -0
  30. package/dist/config/features.js +61 -0
  31. package/dist/config/index.js +6 -0
  32. package/dist/config/staging-deployment.json +60 -0
  33. package/dist/config/validation-config.json +347 -0
  34. package/dist/database/database-orchestrator.js +795 -0
  35. package/dist/database/index.js +4 -0
  36. package/dist/deployment/index.js +11 -0
  37. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +426 -0
  38. package/dist/deployment/orchestration/PortfolioOrchestrator.js +273 -0
  39. package/dist/deployment/orchestration/SingleServiceOrchestrator.js +231 -0
  40. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +662 -0
  41. package/dist/deployment/orchestration/index.js +17 -0
  42. package/dist/deployment/rollback-manager.js +36 -0
  43. package/dist/deployment/wrangler-deployer.js +640 -0
  44. package/dist/handlers/GenericRouteHandler.js +532 -0
  45. package/dist/lib/database/deployment-db-manager.js +423 -0
  46. package/dist/lib/database/wrangler-d1-manager.js +685 -0
  47. package/dist/lib/deployment/modules/DeploymentConfiguration.js +395 -0
  48. package/dist/lib/deployment/modules/DeploymentOrchestrator.js +492 -0
  49. package/dist/lib/deployment/modules/EnvironmentManager.js +517 -0
  50. package/dist/lib/deployment/modules/MonitoringIntegration.js +560 -0
  51. package/dist/lib/deployment/modules/ValidationManager.js +342 -0
  52. package/dist/lib/deployment/orchestration/BaseDeploymentOrchestrator.js +426 -0
  53. package/dist/lib/deployment/orchestration/EnterpriseOrchestrator.js +21 -0
  54. package/dist/lib/deployment/orchestration/PortfolioOrchestrator.js +273 -0
  55. package/dist/lib/deployment/orchestration/SingleServiceOrchestrator.js +231 -0
  56. package/dist/lib/deployment/orchestration/UnifiedDeploymentOrchestrator.js +662 -0
  57. package/dist/{bin → lib}/shared/cloudflare/domain-discovery.js +1 -1
  58. package/dist/{bin → lib}/shared/cloudflare/domain-manager.js +1 -1
  59. package/dist/{bin → lib}/shared/cloudflare/ops.js +4 -4
  60. package/dist/{bin → lib}/shared/config/command-config-manager.js +1 -1
  61. package/dist/{bin → lib}/shared/config/index.js +1 -1
  62. package/dist/{bin → lib}/shared/deployment/credential-collector.js +1 -1
  63. package/dist/lib/shared/deployment/index.js +10 -0
  64. package/dist/{bin → lib}/shared/deployment/rollback-manager.js +1 -1
  65. package/dist/{bin → lib}/shared/deployment/utilities/d1-error-recovery.js +1 -1
  66. package/dist/{bin → lib}/shared/deployment/validator.js +1 -1
  67. package/dist/{bin → lib}/shared/deployment/workflows/interactive-database-workflow.js +1 -1
  68. package/dist/{bin → lib}/shared/monitoring/health-checker.js +2 -2
  69. package/dist/{bin → lib}/shared/routing/domain-router.js +1 -1
  70. package/dist/{bin → lib}/shared/validation/ValidationRegistry.js +1 -1
  71. package/dist/migration/MigrationAdapters.js +608 -0
  72. package/dist/modules/ModuleManager.js +668 -0
  73. package/dist/modules/security.js +96 -0
  74. package/dist/orchestration/cross-domain-coordinator.js +1083 -0
  75. package/dist/orchestration/index.js +5 -0
  76. package/dist/orchestration/modules/DeploymentCoordinator.js +368 -0
  77. package/dist/orchestration/modules/DomainResolver.js +198 -0
  78. package/dist/orchestration/modules/StateManager.js +332 -0
  79. package/dist/orchestration/multi-domain-orchestrator.js +765 -0
  80. package/dist/routing/EnhancedRouter.js +158 -0
  81. package/dist/schema/SchemaManager.js +778 -0
  82. package/dist/security/index.js +2 -2
  83. package/dist/service-management/ConfirmationEngine.js +412 -0
  84. package/dist/service-management/ErrorTracker.js +299 -0
  85. package/dist/service-management/GenerationEngine.js +447 -0
  86. package/dist/service-management/InputCollector.js +619 -0
  87. package/dist/service-management/ServiceCreator.js +21 -0
  88. package/dist/service-management/ServiceOrchestrator.js +631 -0
  89. package/dist/service-management/generators/BaseGenerator.js +233 -0
  90. package/dist/service-management/generators/GeneratorRegistry.js +254 -0
  91. package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +87 -0
  92. package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +106 -0
  93. package/dist/service-management/generators/code/ServiceHandlersGenerator.js +235 -0
  94. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +116 -0
  95. package/dist/service-management/generators/code/ServiceUtilsGenerator.js +246 -0
  96. package/dist/service-management/generators/code/WorkerIndexGenerator.js +143 -0
  97. package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +101 -0
  98. package/dist/service-management/generators/config/DomainsConfigGenerator.js +175 -0
  99. package/dist/service-management/generators/config/EnvExampleGenerator.js +178 -0
  100. package/dist/service-management/generators/config/ProductionEnvGenerator.js +97 -0
  101. package/dist/service-management/generators/config/StagingEnvGenerator.js +97 -0
  102. package/dist/service-management/generators/config/WranglerTomlGenerator.js +238 -0
  103. package/dist/service-management/generators/core/PackageJsonGenerator.js +243 -0
  104. package/dist/service-management/generators/core/SiteConfigGenerator.js +115 -0
  105. package/dist/service-management/generators/documentation/ApiDocsGenerator.js +331 -0
  106. package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +294 -0
  107. package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +244 -0
  108. package/dist/service-management/generators/documentation/ReadmeGenerator.js +196 -0
  109. package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +190 -0
  110. package/dist/service-management/generators/scripts/DeployScriptGenerator.js +123 -0
  111. package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +101 -0
  112. package/dist/service-management/generators/scripts/SetupScriptGenerator.js +88 -0
  113. package/dist/service-management/generators/service-types/StaticSiteGenerator.js +342 -0
  114. package/dist/service-management/generators/testing/EslintConfigGenerator.js +85 -0
  115. package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +237 -0
  116. package/dist/service-management/generators/testing/JestConfigGenerator.js +72 -0
  117. package/dist/service-management/generators/testing/UnitTestsGenerator.js +277 -0
  118. package/dist/service-management/generators/tooling/DockerComposeGenerator.js +71 -0
  119. package/dist/service-management/generators/tooling/GitignoreGenerator.js +143 -0
  120. package/dist/service-management/generators/utils/FileWriter.js +179 -0
  121. package/dist/service-management/generators/utils/PathResolver.js +157 -0
  122. package/dist/service-management/generators/utils/ServiceManifestGenerator.js +111 -0
  123. package/dist/service-management/generators/utils/TemplateEngine.js +185 -0
  124. package/dist/service-management/generators/utils/index.js +18 -0
  125. package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
  126. package/dist/service-management/handlers/GenerationHandler.js +80 -0
  127. package/dist/service-management/handlers/InputHandler.js +59 -0
  128. package/dist/service-management/handlers/ValidationHandler.js +203 -0
  129. package/dist/service-management/index.js +14 -0
  130. package/dist/service-management/routing/DomainRouteMapper.js +311 -0
  131. package/dist/service-management/routing/RouteGenerator.js +266 -0
  132. package/dist/service-management/routing/WranglerRoutesBuilder.js +273 -0
  133. package/dist/service-management/routing/index.js +14 -0
  134. package/dist/service-management/services/DirectoryStructureService.js +56 -0
  135. package/dist/service-management/services/GenerationCoordinator.js +208 -0
  136. package/dist/service-management/services/GeneratorRegistry.js +174 -0
  137. package/dist/services/GenericDataService.js +501 -0
  138. package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +286 -0
  139. package/dist/ui-structures/concepts/service-lifecycle-management.md +150 -0
  140. package/dist/ui-structures/concepts/service-manifest-guide.md +309 -0
  141. package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +231 -0
  142. package/dist/ui-structures/creation/automated-generation-ui.json +246 -0
  143. package/dist/ui-structures/creation/core-inputs-ui.json +217 -0
  144. package/dist/ui-structures/creation/smart-confirmable-ui.json +451 -0
  145. package/dist/ui-structures/reference/absolutely-required-inputs.json +315 -0
  146. package/dist/ui-structures/reference/service-manifest-template.json +342 -0
  147. package/dist/utils/cloudflare/ops.js +1 -1
  148. package/dist/utils/file-manager.js +1 -1
  149. package/dist/utils/formatters.js +1 -1
  150. package/dist/utils/logger.js +1 -1
  151. package/dist/version/VersionDetector.js +813 -0
  152. package/dist/worker/index.js +4 -0
  153. package/dist/worker/integration.js +351 -0
  154. package/package.json +7 -15
  155. package/dist/bin/security/security-cli.js +0 -108
  156. package/dist/bin/shared/deployment/index.js +0 -10
  157. /package/dist/{bin → cli}/clodo-service.js +0 -0
  158. /package/dist/{bin → cli}/commands/helpers/deployment-ui.js +0 -0
  159. /package/dist/{bin → lib}/shared/cache/configuration-cache.js +0 -0
  160. /package/dist/{bin → lib}/shared/cloudflare/index.js +0 -0
  161. /package/dist/{bin → lib}/shared/config/ConfigurationManager.js +0 -0
  162. /package/dist/{bin → lib}/shared/config/cache.js +0 -0
  163. /package/dist/{bin → lib}/shared/config/cloudflare-service-validator.js +0 -0
  164. /package/dist/{bin → lib}/shared/config/manager.js +0 -0
  165. /package/dist/{bin → lib}/shared/config/manifest-loader.js +0 -0
  166. /package/dist/{bin → lib}/shared/database/connection-manager.js +0 -0
  167. /package/dist/{bin → lib}/shared/database/index.js +0 -0
  168. /package/dist/{bin → lib}/shared/database/orchestrator.js +0 -0
  169. /package/dist/{bin → lib}/shared/deployment/auditor.js +0 -0
  170. /package/dist/{bin → lib}/shared/deployment/workflows/deployment-summary.js +0 -0
  171. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-confirmation.js +0 -0
  172. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-domain-info-gatherer.js +0 -0
  173. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-secret-workflow.js +0 -0
  174. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-testing-workflow.js +0 -0
  175. /package/dist/{bin → lib}/shared/deployment/workflows/interactive-validation.js +0 -0
  176. /package/dist/{bin → lib}/shared/error-handling/error-classifier.js +0 -0
  177. /package/dist/{bin → lib}/shared/index.js +0 -0
  178. /package/dist/{bin → lib}/shared/logging/Logger.js +0 -0
  179. /package/dist/{bin → lib}/shared/monitoring/index.js +0 -0
  180. /package/dist/{bin → lib}/shared/monitoring/memory-manager.js +0 -0
  181. /package/dist/{bin → lib}/shared/monitoring/production-monitor.js +0 -0
  182. /package/dist/{bin → lib}/shared/production-tester/api-tester.js +0 -0
  183. /package/dist/{bin → lib}/shared/production-tester/auth-tester.js +0 -0
  184. /package/dist/{bin → lib}/shared/production-tester/core.js +0 -0
  185. /package/dist/{bin → lib}/shared/production-tester/database-tester.js +0 -0
  186. /package/dist/{bin → lib}/shared/production-tester/index.js +0 -0
  187. /package/dist/{bin → lib}/shared/production-tester/load-tester.js +0 -0
  188. /package/dist/{bin → lib}/shared/production-tester/performance-tester.js +0 -0
  189. /package/dist/{bin → lib}/shared/security/api-token-manager.js +0 -0
  190. /package/dist/{bin → lib}/shared/security/index.js +0 -0
  191. /package/dist/{bin → lib}/shared/security/secret-generator.js +0 -0
  192. /package/dist/{bin → lib}/shared/security/secure-token-manager.js +0 -0
  193. /package/dist/{bin → lib}/shared/utils/ErrorHandler.js +0 -0
  194. /package/dist/{bin → lib}/shared/utils/cli-options.js +0 -0
  195. /package/dist/{bin → lib}/shared/utils/config-loader.js +0 -0
  196. /package/dist/{bin → lib}/shared/utils/deployment-validator.js +0 -0
  197. /package/dist/{bin → lib}/shared/utils/error-recovery.js +0 -0
  198. /package/dist/{bin → lib}/shared/utils/file-manager.js +0 -0
  199. /package/dist/{bin → lib}/shared/utils/formatters.js +0 -0
  200. /package/dist/{bin → lib}/shared/utils/graceful-shutdown-manager.js +0 -0
  201. /package/dist/{bin → lib}/shared/utils/index.js +0 -0
  202. /package/dist/{bin → lib}/shared/utils/interactive-prompts.js +0 -0
  203. /package/dist/{bin → lib}/shared/utils/interactive-utils.js +0 -0
  204. /package/dist/{bin → lib}/shared/utils/output-formatter.js +0 -0
  205. /package/dist/{bin → lib}/shared/utils/progress-manager.js +0 -0
  206. /package/dist/{bin → lib}/shared/utils/progress-spinner.js +0 -0
  207. /package/dist/{bin → lib}/shared/utils/rate-limiter.js +0 -0
  208. /package/dist/{bin → lib}/shared/utils/sensitive-redactor.js +0 -0
@@ -0,0 +1,37 @@
1
+ # Customer Staging Environment Variables
2
+ # Template for {{CUSTOMER_NAME}} staging environment
3
+
4
+ # Customer Information
5
+ CUSTOMER_NAME={{CUSTOMER_NAME}}
6
+ CUSTOMER_DOMAIN={{CUSTOMER_DOMAIN}}
7
+ ENVIRONMENT={{ENVIRONMENT}}
8
+
9
+ # Domain Configuration
10
+ DOMAIN={{DOMAIN}}
11
+ API_DOMAIN=api-staging.{{DOMAIN}}
12
+ AUTH_DOMAIN=auth-staging.{{DOMAIN}}
13
+
14
+ # Database Configuration
15
+ DATABASE_URL=postgresql://user:password@staging-db:5432/{{CUSTOMER_NAME}}_staging
16
+ REDIS_URL=redis://staging-redis:6379/0
17
+
18
+ # Feature Flags
19
+ ENABLE_DEBUG_LOGGING=false
20
+ ENABLE_DEVELOPMENT_MODE=false
21
+ ENABLE_STRICT_VALIDATION=true
22
+
23
+ # Security
24
+ JWT_SECRET=staging-jwt-secret-{{CUSTOMER_NAME}}
25
+ API_KEY=staging-api-key-{{CUSTOMER_NAME}}
26
+
27
+ # External Services
28
+ STRIPE_PUBLIC_KEY=pk_test_...
29
+ STRIPE_SECRET_KEY=sk_test_...
30
+
31
+ # Logging
32
+ LOG_LEVEL=info
33
+ LOG_FORMAT=json
34
+
35
+ # Performance
36
+ CACHE_TTL=600
37
+ RATE_LIMIT=5000
@@ -0,0 +1,625 @@
1
+ import fs from 'fs';
2
+ // eslint-disable-next-line no-unused-vars
3
+ import { resolve, join } from 'path';
4
+ import toml from '@iarna/toml';
5
+ import { createDomainConfigSchema, validateDomainConfig, createDomainRegistry } from './domains.js';
6
+ import { getDirname } from '../utils/esm-helper.js';
7
+ const __dirname = getDirname(import.meta.url, 'src/config');
8
+
9
+ // Simple inline logger to avoid circular dependency with index.js
10
+ const logger = {
11
+ info: (message, ...args) => console.log(`[CustomerConfig] ${message}`, ...args),
12
+ error: (message, ...args) => console.error(`[CustomerConfig] ${message}`, ...args),
13
+ warn: (message, ...args) => console.warn(`[CustomerConfig] ${message}`, ...args),
14
+ debug: (message, ...args) => console.debug(`[CustomerConfig] ${message}`, ...args)
15
+ };
16
+
17
+ /**
18
+ * Customer Configuration Manager
19
+ * Manages multi-environment, multi-customer configuration structure
20
+ * Integrates with existing domain and feature flag systems
21
+ */
22
+ export class CustomerConfigurationManager {
23
+ constructor(options = {}) {
24
+ this.configDir = options.configDir || resolve(__dirname, '..', '..', 'config');
25
+ this.environments = options.environments || ['development', 'staging', 'production'];
26
+ this.domainRegistry = createDomainRegistry({});
27
+ this.customers = new Map();
28
+ }
29
+
30
+ /**
31
+ * Create a new customer configuration from template
32
+ * @param {string} customerName - Customer identifier
33
+ * @param {string} domain - Customer domain (optional)
34
+ * @param {Object} options - Additional customer options
35
+ */
36
+ async createCustomer(customerName, domain = null, options = {}) {
37
+ logger.info(`Creating customer configuration: ${customerName}`);
38
+ const customerDir = resolve(this.configDir, 'customers', customerName);
39
+
40
+ // Create customer directory structure
41
+ console.log(`DEBUG: Checking if directory exists: ${customerDir}`);
42
+ if (!fs.existsSync(customerDir)) {
43
+ console.log(`DEBUG: Creating directory: ${customerDir}`);
44
+ fs.mkdirSync(customerDir, {
45
+ recursive: true
46
+ });
47
+ logger.info(`Created customer directory: ${customerDir}`);
48
+ }
49
+
50
+ // Create domain configuration for customer
51
+ const domainConfig = this.createCustomerDomainConfig(customerName, domain, options);
52
+ this.domainRegistry.add(customerName, domainConfig);
53
+
54
+ // Create environment-specific configs from templates
55
+ for (const env of this.environments) {
56
+ await this.createCustomerEnvironment(customerName, env, domain, options);
57
+ }
58
+
59
+ // Store customer metadata
60
+ this.customers.set(customerName, {
61
+ name: customerName,
62
+ domain: domain,
63
+ createdAt: new Date().toISOString(),
64
+ environments: this.environments,
65
+ ...options
66
+ });
67
+ logger.info(`Customer ${customerName} configuration created successfully`);
68
+ return this.getCustomerInfo(customerName);
69
+ }
70
+
71
+ /**
72
+ * Create domain configuration for customer
73
+ */
74
+ createCustomerDomainConfig(customerName, domain, options) {
75
+ const baseConfig = createDomainConfigSchema();
76
+
77
+ // Generate customer-specific domain config
78
+ const domainConfig = {
79
+ ...baseConfig,
80
+ name: customerName,
81
+ displayName: options.displayName || this.capitalizeFirst(customerName),
82
+ accountId: options.accountId || '00000000000000000000000000000000',
83
+ // Placeholder for onboarding
84
+ zoneId: options.zoneId || '00000000000000000000000000000000',
85
+ // Placeholder for onboarding
86
+
87
+ domains: {
88
+ production: domain || `${customerName}.com`,
89
+ staging: `staging.${domain || `${customerName}.com`}`,
90
+ development: `dev.${domain || `${customerName}.com`}`
91
+ },
92
+ features: {
93
+ // Customer-specific feature flags
94
+ customerIsolation: true,
95
+ customDomain: !!domain,
96
+ multiEnvironment: true,
97
+ ...options.features
98
+ },
99
+ settings: {
100
+ ...baseConfig.settings,
101
+ customerName: customerName,
102
+ customerDomain: domain,
103
+ ...options.settings
104
+ }
105
+ };
106
+
107
+ // Skip validation during initial creation if using placeholders
108
+ if (options.skipValidation || !options.accountId || !options.zoneId) {
109
+ logger.info(`Created customer domain config for ${customerName} (framework mode - placeholders used)`);
110
+ return domainConfig;
111
+ }
112
+ validateDomainConfig(domainConfig);
113
+ return domainConfig;
114
+ }
115
+
116
+ /**
117
+ * Create customer environment config from template
118
+ */
119
+ // eslint-disable-next-line no-unused-vars
120
+ async createCustomerEnvironment(customerName, environment, domain, options) {
121
+ const templatePath = resolve(this.configDir, 'customers', 'template', `${environment}.env.template`);
122
+ const outputPath = resolve(this.configDir, 'customers', customerName, `${environment}.env`);
123
+
124
+ // Check if template exists
125
+ if (!fs.existsSync(templatePath)) {
126
+ logger.warn(`Template not found: ${templatePath}, skipping ${environment} config`);
127
+ return;
128
+ }
129
+
130
+ // Skip if config already exists
131
+ if (fs.existsSync(outputPath)) {
132
+ logger.info(`Config already exists: ${outputPath}, skipping`);
133
+ return;
134
+ }
135
+ try {
136
+ // Read template and replace placeholders
137
+ let template = fs.readFileSync(templatePath, 'utf8');
138
+
139
+ // Replace customer-specific placeholders
140
+ template = template.replace(/\{\{CUSTOMER_NAME\}\}/g, customerName);
141
+ template = template.replace(/\{\{CUSTOMER_DOMAIN\}\}/g, domain || `${customerName}.com`);
142
+ template = template.replace(/\{\{ENVIRONMENT\}\}/g, environment);
143
+
144
+ // Environment-specific domain replacements
145
+ const envDomains = this.getEnvironmentDomains(customerName, domain);
146
+ template = template.replace(/\{\{DOMAIN\}\}/g, envDomains[environment]);
147
+
148
+ // Write customer config
149
+ fs.writeFileSync(outputPath, template);
150
+ logger.info(`Created customer config: ${environment}.env for ${customerName}`);
151
+ } catch (error) {
152
+ logger.error(`Failed to create ${environment} config for ${customerName}:`, error.message);
153
+ throw error;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Get environment-specific domains for customer
159
+ */
160
+ getEnvironmentDomains(customerName, domain) {
161
+ const baseDomain = domain || `${customerName}.com`;
162
+ return {
163
+ production: baseDomain,
164
+ staging: `staging.${baseDomain}`,
165
+ development: `dev.${baseDomain}`
166
+ };
167
+ }
168
+
169
+ /**
170
+ * Validate customer configurations
171
+ */
172
+ async validateConfigs() {
173
+ logger.info('Validating customer configuration structure...');
174
+ let valid = true;
175
+ const errors = [];
176
+
177
+ // Validate base configuration files
178
+ const baseFiles = ['wrangler.base.toml', 'variables.base.env'];
179
+ for (const file of baseFiles) {
180
+ const path = resolve(this.configDir, 'base', file);
181
+ if (!fs.existsSync(path)) {
182
+ errors.push(`Missing base config: ${file}`);
183
+ valid = false;
184
+ }
185
+ }
186
+
187
+ // Validate environment configs
188
+ for (const env of this.environments) {
189
+ const envFile = `${env}.toml`;
190
+ const path = resolve(this.configDir, 'environments', envFile);
191
+ if (!fs.existsSync(path)) {
192
+ errors.push(`Missing environment config: ${envFile}`);
193
+ valid = false;
194
+ }
195
+ }
196
+
197
+ // Validate customer configs
198
+ const customersDir = resolve(this.configDir, 'customers');
199
+ if (fs.existsSync(customersDir)) {
200
+ const customerDirs = this.getCustomerDirectories();
201
+ for (const customerName of customerDirs) {
202
+ const customerValid = await this.validateCustomerConfig(customerName);
203
+ if (!customerValid) {
204
+ valid = false;
205
+ }
206
+ }
207
+ }
208
+ if (errors.length > 0) {
209
+ logger.error('Configuration validation errors:', errors);
210
+ }
211
+ logger.info(`Configuration validation ${valid ? 'passed' : 'failed'}`);
212
+ return {
213
+ valid,
214
+ errors
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Validate individual customer configuration
220
+ */
221
+ async validateCustomerConfig(customerName) {
222
+ const customerDir = resolve(this.configDir, 'customers', customerName);
223
+ let valid = true;
224
+
225
+ // Check environment files exist
226
+ for (const env of this.environments) {
227
+ const envFile = resolve(customerDir, `${env}.env`);
228
+ if (!fs.existsSync(envFile)) {
229
+ logger.error(`Missing ${env}.env for customer ${customerName}`);
230
+ valid = false;
231
+ }
232
+ }
233
+
234
+ // Validate domain configuration (skip in framework mode)
235
+ try {
236
+ const domainConfig = this.domainRegistry.get(customerName);
237
+ if (!domainConfig.settings?.isFrameworkMode) {
238
+ validateDomainConfig(domainConfig);
239
+ } else {
240
+ logger.info(`Skipping domain validation for ${customerName} (framework mode)`);
241
+ }
242
+ } catch (error) {
243
+ logger.error(`Invalid domain config for ${customerName}:`, error.message);
244
+ valid = false;
245
+ }
246
+ return valid;
247
+ }
248
+
249
+ /**
250
+ * Show effective configuration for customer/environment
251
+ */
252
+ showConfig(customerName, environment) {
253
+ logger.info(`Showing effective configuration: ${customerName}/${environment}`);
254
+
255
+ // Get domain config from registry
256
+ let domainConfig;
257
+ try {
258
+ domainConfig = this.domainRegistry.get(customerName);
259
+ } catch (error) {
260
+ // If not in registry, try to find customer metadata
261
+ const customerMeta = this.customers.get(customerName);
262
+ if (customerMeta) {
263
+ // Recreate domain config from metadata
264
+ domainConfig = this.createCustomerDomainConfig(customerName, customerMeta.domain, customerMeta);
265
+ } else {
266
+ throw new Error(`Customer not found: ${customerName}`);
267
+ }
268
+ }
269
+ const config = {
270
+ customer: customerName,
271
+ environment: environment,
272
+ domain: domainConfig,
273
+ variables: {},
274
+ features: {}
275
+ };
276
+
277
+ // Load base variables
278
+ const baseVarsPath = resolve(this.configDir, 'base', 'variables.base.env');
279
+ if (fs.existsSync(baseVarsPath)) {
280
+ config.variables.base = this.parseEnvFile(baseVarsPath);
281
+ }
282
+
283
+ // Load customer environment variables
284
+ const customerConfigPath = resolve(this.configDir, 'customers', customerName, `${environment}.env`);
285
+ if (fs.existsSync(customerConfigPath)) {
286
+ config.variables.customer = this.parseEnvFile(customerConfigPath);
287
+ }
288
+
289
+ // Get domain features
290
+ config.features = domainConfig.features || {};
291
+ return config;
292
+ }
293
+
294
+ /**
295
+ * Generate deployment command for customer/environment
296
+ */
297
+ getDeployCommand(customerName, environment) {
298
+ const commands = {
299
+ development: `wrangler dev --config config/environments/development.toml`,
300
+ staging: `wrangler deploy --config config/environments/staging.toml --env staging`,
301
+ production: `wrangler deploy --env production`
302
+ };
303
+ const command = commands[environment];
304
+ if (!command) {
305
+ throw new Error(`Unknown environment: ${environment}`);
306
+ }
307
+ logger.info(`Generated deploy command for ${customerName}/${environment}`);
308
+ return {
309
+ command: command,
310
+ customer: customerName,
311
+ environment: environment,
312
+ configPath: `config/customers/${customerName}/${environment}.env`
313
+ };
314
+ }
315
+
316
+ /**
317
+ * Get customer information
318
+ */
319
+ getCustomerInfo(customerName) {
320
+ const domainConfig = this.domainRegistry.get(customerName);
321
+ const customerMeta = this.customers.get(customerName);
322
+ return {
323
+ ...customerMeta,
324
+ domainConfig: domainConfig,
325
+ configPath: resolve(this.configDir, 'customers', customerName)
326
+ };
327
+ }
328
+
329
+ /**
330
+ * List all customers
331
+ */
332
+ listCustomers() {
333
+ return Array.from(this.customers.keys()).map(name => ({
334
+ name: name,
335
+ ...this.getCustomerInfo(name)
336
+ }));
337
+ }
338
+
339
+ /**
340
+ * Load existing customers from filesystem
341
+ */
342
+ async loadExistingCustomers() {
343
+ const customersDir = resolve(this.configDir, 'customers');
344
+ if (!fs.existsSync(customersDir)) {
345
+ return;
346
+ }
347
+ try {
348
+ // First, try to read from root wrangler.toml (real deployment config)
349
+ const rootWranglerPath = resolve(this.configDir, '..', 'wrangler.toml');
350
+ let wranglerConfig = null;
351
+ let globalAccountId = null;
352
+ if (fs.existsSync(rootWranglerPath)) {
353
+ try {
354
+ const wranglerContent = fs.readFileSync(rootWranglerPath, 'utf8');
355
+ wranglerConfig = toml.parse(wranglerContent);
356
+ globalAccountId = wranglerConfig.account_id;
357
+ logger.info(`Loaded wrangler.toml with account_id: ${globalAccountId ? globalAccountId.substring(0, 8) + '...' : 'not found'}`);
358
+ } catch (error) {
359
+ logger.warn('Could not parse root wrangler.toml:', error.message);
360
+ }
361
+ }
362
+
363
+ // Read customer directories
364
+ const customerDirs = this.getCustomerDirectories();
365
+ for (const customerName of customerDirs) {
366
+ // Skip template directory
367
+ if (customerName === 'template') continue;
368
+ const customerDir = resolve(customersDir, customerName);
369
+
370
+ // Initialize customer metadata
371
+ const metadata = {
372
+ name: customerName,
373
+ createdAt: new Date().toISOString(),
374
+ environments: this.environments,
375
+ accountId: globalAccountId // Start with global account ID
376
+ };
377
+
378
+ // Try to load from wrangler.toml [env.production] section
379
+ if (wranglerConfig && wranglerConfig.env && wranglerConfig.env.production) {
380
+ const prodEnv = wranglerConfig.env.production;
381
+
382
+ // Extract SERVICE_DOMAIN (maps to customer name usually)
383
+ if (prodEnv.vars && prodEnv.vars.SERVICE_DOMAIN) {
384
+ metadata.serviceDomain = prodEnv.vars.SERVICE_DOMAIN;
385
+
386
+ // If SERVICE_DOMAIN matches customer name, this is their config
387
+ if (prodEnv.vars.SERVICE_DOMAIN === customerName) {
388
+ // Extract database info
389
+ if (prodEnv.d1_databases && prodEnv.d1_databases.length > 0) {
390
+ const db = prodEnv.d1_databases[0];
391
+ metadata.databaseId = db.database_id;
392
+ metadata.databaseName = db.database_name;
393
+ }
394
+
395
+ // Extract zone_id if present
396
+ if (prodEnv.zone_id) {
397
+ metadata.zoneId = prodEnv.zone_id;
398
+ }
399
+
400
+ // Extract route to infer domain
401
+ if (prodEnv.route) {
402
+ // Route format: "example.com/*" or "*.example.com/*"
403
+ const domain = prodEnv.route.replace(/\/\*$/, '').replace(/^\*\./, '');
404
+ metadata.domain = domain;
405
+ }
406
+ }
407
+ }
408
+ }
409
+
410
+ // Read customer-specific env file to get CUSTOMER_DOMAIN and other info
411
+ const prodConfigPath = resolve(customerDir, 'production.env');
412
+ if (fs.existsSync(prodConfigPath)) {
413
+ try {
414
+ const prodConfig = this.parseEnvFile(prodConfigPath);
415
+
416
+ // Use CUSTOMER_DOMAIN (correct field name in real configs)
417
+ if (prodConfig.CUSTOMER_DOMAIN) {
418
+ metadata.customerDomain = prodConfig.CUSTOMER_DOMAIN.replace(/^https?:\/\//, '');
419
+ // If we didn't get domain from wrangler.toml, use this
420
+ if (!metadata.domain) {
421
+ metadata.domain = metadata.customerDomain;
422
+ }
423
+ }
424
+
425
+ // Also check old DOMAIN field for backward compatibility
426
+ if (!metadata.domain && prodConfig.DOMAIN) {
427
+ metadata.domain = prodConfig.DOMAIN.replace(/^https?:\/\//, '');
428
+ }
429
+
430
+ // Extract customer ID
431
+ if (prodConfig.CUSTOMER_ID) {
432
+ metadata.customerId = prodConfig.CUSTOMER_ID;
433
+ }
434
+ } catch (error) {
435
+ logger.warn(`Could not parse customer env for ${customerName}:`, error.message);
436
+ }
437
+ }
438
+
439
+ // Check if secrets exist (we can't read them, but can note they should be set)
440
+ // In a real system, you'd run: wrangler secret list --env production
441
+ // For now, we just note that secrets should be managed separately
442
+ metadata.hasSecrets = true; // Assume secrets are managed via wrangler secret commands
443
+
444
+ // Register customer
445
+ this.customers.set(customerName, metadata);
446
+
447
+ // Try to register domain config
448
+ try {
449
+ const domainConfig = this.createCustomerDomainConfig(customerName, metadata.domain, {
450
+ skipValidation: true,
451
+ isFrameworkMode: true,
452
+ accountId: metadata.accountId,
453
+ zoneId: metadata.zoneId
454
+ });
455
+ this.domainRegistry.add(customerName, domainConfig);
456
+ } catch (error) {
457
+ logger.warn(`Could not register domain for existing customer ${customerName}:`, error.message);
458
+ }
459
+ logger.info(`Loaded existing customer: ${customerName}`);
460
+ }
461
+ } catch (error) {
462
+ logger.error('Error loading existing customers:', error.message);
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Get customer directories from filesystem
468
+ */
469
+ getCustomerDirectories() {
470
+ const customersDir = resolve(this.configDir, 'customers');
471
+ if (!fs.existsSync(customersDir)) {
472
+ return [];
473
+ }
474
+ try {
475
+ // Read directory contents
476
+ const items = fs.readdirSync(customersDir);
477
+ return items.filter(item => {
478
+ const itemPath = resolve(customersDir, item);
479
+ return fs.statSync(itemPath).isDirectory() && item !== 'template';
480
+ });
481
+ } catch (error) {
482
+ logger.error('Error reading customer directories:', error.message);
483
+ return [];
484
+ }
485
+ }
486
+
487
+ /**
488
+ * Parse environment file
489
+ */
490
+ parseEnvFile(filePath) {
491
+ const content = fs.readFileSync(filePath, 'utf8');
492
+ const variables = {};
493
+ content.split('\n').forEach(line => {
494
+ const trimmed = line.trim();
495
+ if (trimmed && !trimmed.startsWith('#')) {
496
+ const [key, ...valueParts] = trimmed.split('=');
497
+ if (key && valueParts.length > 0) {
498
+ variables[key.trim()] = valueParts.join('=').trim();
499
+ }
500
+ }
501
+ });
502
+ return variables;
503
+ }
504
+
505
+ /**
506
+ * Update wrangler.toml with new configuration
507
+ * @param {string} wranglerPath - Path to wrangler.toml
508
+ * @param {Object} updates - Configuration updates to merge
509
+ * @returns {boolean} Success status
510
+ */
511
+ updateWranglerToml(wranglerPath, updates) {
512
+ try {
513
+ let existingConfig = {};
514
+
515
+ // Read existing config if file exists
516
+ if (fs.existsSync(wranglerPath)) {
517
+ const content = fs.readFileSync(wranglerPath, 'utf8');
518
+ existingConfig = toml.parse(content);
519
+ }
520
+
521
+ // Deep merge updates into existing config
522
+ const mergedConfig = this.deepMergeConfig(existingConfig, updates);
523
+
524
+ // Write back to file
525
+ const tomlContent = toml.stringify(mergedConfig);
526
+ fs.writeFileSync(wranglerPath, tomlContent, 'utf8');
527
+ logger.info(`Updated wrangler.toml: ${wranglerPath}`);
528
+ return true;
529
+ } catch (error) {
530
+ logger.error(`Failed to update wrangler.toml: ${error.message}`);
531
+ return false;
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Deep merge two configuration objects
537
+ * @private
538
+ */
539
+ deepMergeConfig(target, source) {
540
+ const output = {
541
+ ...target
542
+ };
543
+ for (const key in source) {
544
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
545
+ // Recursively merge objects
546
+ output[key] = this.deepMergeConfig(target[key] || {}, source[key]);
547
+ } else {
548
+ // Overwrite arrays and primitives
549
+ output[key] = source[key];
550
+ }
551
+ }
552
+ return output;
553
+ }
554
+
555
+ /**
556
+ * Create or update environment section in wrangler.toml
557
+ * @param {string} wranglerPath - Path to wrangler.toml
558
+ * @param {string} environment - Environment name (production, staging, development)
559
+ * @param {Object} envConfig - Environment configuration
560
+ * @returns {boolean} Success status
561
+ */
562
+ updateEnvironmentConfig(wranglerPath, environment, envConfig) {
563
+ const updates = {
564
+ env: {
565
+ [environment]: envConfig
566
+ }
567
+ };
568
+ return this.updateWranglerToml(wranglerPath, updates);
569
+ }
570
+
571
+ /**
572
+ * Add D1 database binding to environment
573
+ * @param {string} wranglerPath - Path to wrangler.toml
574
+ * @param {string} environment - Environment name
575
+ * @param {Object} databaseConfig - Database configuration
576
+ * @returns {boolean} Success status
577
+ */
578
+ addD1Database(wranglerPath, environment, databaseConfig) {
579
+ try {
580
+ const content = fs.readFileSync(wranglerPath, 'utf8');
581
+ const config = toml.parse(content);
582
+
583
+ // Ensure env section exists
584
+ if (!config.env) config.env = {};
585
+ if (!config.env[environment]) config.env[environment] = {};
586
+
587
+ // Ensure d1_databases array exists
588
+ if (!config.env[environment].d1_databases) {
589
+ config.env[environment].d1_databases = [];
590
+ }
591
+
592
+ // Add or update database
593
+ const existingIndex = config.env[environment].d1_databases.findIndex(db => db.binding === databaseConfig.binding);
594
+ if (existingIndex >= 0) {
595
+ config.env[environment].d1_databases[existingIndex] = databaseConfig;
596
+ } else {
597
+ config.env[environment].d1_databases.push(databaseConfig);
598
+ }
599
+
600
+ // Write back
601
+ fs.writeFileSync(wranglerPath, toml.stringify(config), 'utf8');
602
+ logger.info(`Added D1 database to ${environment} environment`);
603
+ return true;
604
+ } catch (error) {
605
+ logger.error(`Failed to add D1 database: ${error.message}`);
606
+ return false;
607
+ }
608
+ }
609
+
610
+ /**
611
+ * Capitalize first letter
612
+ */
613
+ capitalizeFirst(str) {
614
+ return str.charAt(0).toUpperCase() + str.slice(1);
615
+ }
616
+ }
617
+
618
+ // Default instance
619
+ export const customerConfigManager = new CustomerConfigurationManager();
620
+
621
+ // Convenience functions
622
+ export const createCustomer = (name, domain, options) => customerConfigManager.createCustomer(name, domain, options);
623
+ export const validateCustomerConfigs = () => customerConfigManager.validateConfigs();
624
+ export const showCustomerConfig = (customer, env) => customerConfigManager.showConfig(customer, env);
625
+ export const getCustomerDeployCommand = (customer, env) => customerConfigManager.getDeployCommand(customer, env);