@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,889 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Clodo Framework - Service Initializer
5
+ *
6
+ * Initializes a new service with auto-generated configurations,
7
+ * eliminating the need for manual wrangler.toml and domains.js setup.
8
+ *
9
+ * This solves the workflow order problem by making configuration
10
+ * generation the first step, not a prerequisite.
11
+ */
12
+
13
+ import { program } from 'commander';
14
+ import { ServiceInitializer } from '../../dist/service-management/ServiceInitializer.js';
15
+
16
+ const SERVICE_TYPES = ['generic', 'data-service', 'auth-service', 'content-service', 'api-gateway'];
17
+
18
+ class ServiceInitializerCLI {
19
+ constructor() {
20
+ this.setupCLI();
21
+ }
22
+
23
+ setupCLI() {
24
+ program
25
+ .name('clodo-init-service')
26
+ .description('Initialize a Clodo Framework service with auto-generated configurations')
27
+ .version('1.0.0')
28
+ .argument('<service-name>', 'Name of the service to initialize')
29
+ .option('-t, --type <type>', 'Service type', 'generic')
30
+ .option('-d, --domains <domains>', 'Comma-separated list of domains (can include account info)')
31
+ .option('-e, --env <environment>', 'Target environment', 'development')
32
+ .option('--api-token <token>', 'Cloudflare API token for domain discovery')
33
+ .option('--account-id <id>', 'Default Cloudflare account ID')
34
+ .option('--zone-id <id>', 'Default Cloudflare zone ID')
35
+ .option('-o, --output <path>', 'Output directory (services will be created in a services/ subdirectory)', process.cwd())
36
+ .option('-f, --force', 'Overwrite existing service directory')
37
+ .option('--dry-run', 'Show what would be created without creating files')
38
+ .option('--multi-domain', 'Generate configurations for multiple domains')
39
+ .action((serviceName, options) => {
40
+ this.initializeService(serviceName, options);
41
+ });
42
+
43
+ program.on('--help', () => {
44
+ console.log('\nExamples:');
45
+ console.log(' clodo-init-service my-api --type api-gateway --domains "api.example.com,staging.example.com"');
46
+ console.log(' clodo-init-service data-service --type data-service --api-token $CF_TOKEN');
47
+ console.log(' clodo-init-service my-service --env production --account-id $CF_ACCOUNT_ID');
48
+ console.log('\nServices are created in: ./services/{service-name}/');
49
+ console.log('\nEnvironment Variables:');
50
+ console.log(' CLOUDFLARE_API_TOKEN - API token for domain discovery');
51
+ console.log(' CLOUDFLARE_ACCOUNT_ID - Account ID for configurations');
52
+ console.log(' CLOUDFLARE_ZONE_ID - Zone ID for domain configurations');
53
+ });
54
+ }
55
+
56
+ async initializeService(serviceName, options) {
57
+ try {
58
+ console.log('🚀 Clodo Framework - Service Initializer');
59
+ console.log('=' .repeat(50));
60
+ console.log('📦 Using Clodo Framework ServiceInitializer module');
61
+ console.log('');
62
+
63
+ const initializer = new ServiceInitializer();
64
+ const result = await initializer.initializeService(serviceName, {
65
+ type: options.type,
66
+ domains: options.domains,
67
+ env: options.env,
68
+ apiToken: options.apiToken,
69
+ accountId: options.accountId,
70
+ zoneId: options.zoneId,
71
+ output: options.output,
72
+ force: options.force,
73
+ dryRun: options.dryRun
74
+ });
75
+
76
+ if (result.success) {
77
+ if (result.dryRun) {
78
+ console.log('📋 Dry run - would create the following:');
79
+ console.log('Files:', result.configs.join(', '));
80
+ return;
81
+ }
82
+
83
+ console.log('\n✅ Service initialized successfully!');
84
+ console.log('\n📝 Next steps:');
85
+ console.log(` cd services/${serviceName}`);
86
+ console.log(' npm install');
87
+ console.log(' npm run dev # Start development server');
88
+ console.log(' npm run deploy # Deploy to Cloudflare');
89
+ } else {
90
+ console.error('\n❌ Initialization failed:', result.error);
91
+ process.exit(1);
92
+ }
93
+ } catch (error) {
94
+ console.error('\n❌ Unexpected error:', error.message);
95
+ process.exit(1);
96
+ }
97
+ }
98
+ }
99
+
100
+ // Run the CLI
101
+ const cli = new ServiceInitializerCLI();
102
+ program.parse();
103
+
104
+ // Discover domains if API token provided
105
+ const domainInfo = await this.discoverDomains(options, serviceName);
106
+
107
+ // Generate configurations
108
+ const configs = this.generateConfigurations(serviceName, options, domainInfo);
109
+
110
+ if (options.dryRun) {
111
+ this.showDryRun(configs);
112
+ return;
113
+ }
114
+
115
+ // Create service structure
116
+ this.createServiceStructure(serviceName, options, configs);
117
+
118
+ // Validate setup
119
+ this.validateSetup(serviceName, options);
120
+
121
+ console.log('\n✅ Service initialized successfully!');
122
+ console.log('\n📝 Next steps:');
123
+ console.log(` cd services/${serviceName}`);
124
+ console.log(' npm install');
125
+ console.log(' npm run dev # Start development server');
126
+ console.log(' npm run deploy # Deploy to Cloudflare');
127
+
128
+ } catch (error) {
129
+ console.error('\n❌ Initialization failed:', error.message);
130
+ process.exit(1);
131
+ }
132
+ }
133
+
134
+ validateInputs(serviceName, options) {
135
+ // Validate service name
136
+ if (!/^[a-z0-9-]+$/.test(serviceName)) {
137
+ throw new Error('Service name must contain only lowercase letters, numbers, and hyphens');
138
+ }
139
+ if (serviceName.length < 3 || serviceName.length > 50) {
140
+ throw new Error('Service name must be between 3 and 50 characters');
141
+ }
142
+
143
+ // Validate service type
144
+ if (!SERVICE_TYPES.includes(options.type)) {
145
+ throw new Error(`Invalid service type. Must be one of: ${SERVICE_TYPES.join(', ')}`);
146
+ }
147
+
148
+ // Check if service directory exists
149
+ const servicesDir = join(options.output, 'services');
150
+ const serviceDir = join(servicesDir, serviceName);
151
+ if (existsSync(serviceDir) && !options.force) {
152
+ throw new Error(`Service directory already exists: ${serviceDir}. Use --force to overwrite.`);
153
+ }
154
+ }
155
+
156
+ async discoverDomains(options, serviceName) {
157
+ const domainInfo = {
158
+ domains: [],
159
+ defaultAccountId: options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID,
160
+ defaultZoneId: options.zoneId || process.env.CLOUDFLARE_ZONE_ID,
161
+ apiToken: options.apiToken || process.env.CLOUDFLARE_API_TOKEN
162
+ };
163
+
164
+ // Parse domains from command line - support format: domain[:accountId[:zoneId]]
165
+ if (options.domains) {
166
+ const domainSpecs = options.domains.split(',').map(d => d.trim());
167
+ domainInfo.domains = domainSpecs.map(spec => {
168
+ const parts = spec.split(':');
169
+ return {
170
+ domain: parts[0],
171
+ accountId: parts[1] || domainInfo.defaultAccountId,
172
+ zoneId: parts[2] || domainInfo.defaultZoneId,
173
+ name: `${serviceName}-${parts[0].replace(/[^a-zA-Z0-9]/g, '-')}`
174
+ };
175
+ });
176
+ }
177
+
178
+ // If API token provided and no domains specified, try to discover
179
+ if (domainInfo.apiToken && !options.domains) {
180
+ console.log('🔍 Discovering domains from Cloudflare...');
181
+ try {
182
+ // Mock discovery for now - would integrate with real API
183
+ domainInfo.domains = [
184
+ {
185
+ domain: `${options.env === 'production' ? '' : options.env + '-'}api.example.com`,
186
+ accountId: domainInfo.defaultAccountId,
187
+ zoneId: domainInfo.defaultZoneId,
188
+ name: `${serviceName}-api`
189
+ }
190
+ ];
191
+ console.log(` Found domain: ${domainInfo.domains[0].domain}`);
192
+ } catch (error) {
193
+ console.warn('⚠️ Domain discovery failed, using defaults:', error.message);
194
+ domainInfo.domains = [{
195
+ domain: `api.example.com`,
196
+ accountId: domainInfo.defaultAccountId,
197
+ zoneId: domainInfo.defaultZoneId,
198
+ name: `${options.serviceName}-api`
199
+ }];
200
+ }
201
+ }
202
+
203
+ // Ensure we have at least one domain
204
+ if (domainInfo.domains.length === 0) {
205
+ domainInfo.domains = [{
206
+ domain: `api.example.com`,
207
+ accountId: domainInfo.defaultAccountId,
208
+ zoneId: domainInfo.defaultZoneId,
209
+ name: `${serviceName}-api`
210
+ }];
211
+ }
212
+
213
+ return domainInfo;
214
+ }
215
+
216
+ generateConfigurations(serviceName, options, domainInfo) {
217
+ console.log('🔧 Generating configurations...');
218
+
219
+ const configs = {};
220
+
221
+ // Generate wrangler.toml
222
+ configs.wrangler = this.generateWranglerConfig(serviceName, options, domainInfo);
223
+
224
+ // Generate domains.js
225
+ configs.domains = this.generateDomainsConfig(serviceName, options, domainInfo);
226
+
227
+ // Generate package.json
228
+ configs.package = this.generatePackageConfig(serviceName, options);
229
+
230
+ return configs;
231
+ }
232
+
233
+ generateWranglerConfig(serviceName, options, domainInfo) {
234
+ const env = options.env;
235
+
236
+ // For multiple domains, generate separate wrangler files
237
+ if (domainInfo.domains.length > 1) {
238
+ const wranglerConfigs = {};
239
+ domainInfo.domains.forEach(domainConfig => {
240
+ const configName = `wrangler.${domainConfig.name}.toml`;
241
+ wranglerConfigs[configName] = this.generateSingleWranglerConfig(
242
+ domainConfig.name,
243
+ env,
244
+ options,
245
+ domainConfig,
246
+ serviceName
247
+ );
248
+ });
249
+ return wranglerConfigs;
250
+ }
251
+
252
+ // For single domain, generate single wrangler.toml
253
+ return {
254
+ 'wrangler.toml': this.generateSingleWranglerConfig(
255
+ `${serviceName}-${env}`,
256
+ env,
257
+ options,
258
+ domainInfo.domains[0],
259
+ serviceName
260
+ )
261
+ };
262
+ }
263
+
264
+ generateSingleWranglerConfig(name, env, options, domainConfig, serviceName) {
265
+ const isProduction = env === 'production';
266
+
267
+ let config = `name = "${name}"
268
+ main = "src/worker/index.js"
269
+ compatibility_date = "${new Date().toISOString().split('T')[0]}"
270
+
271
+ `;
272
+
273
+ if (!isProduction) {
274
+ config += `[env.${env}]
275
+ name = "${name}"
276
+
277
+ `;
278
+ }
279
+
280
+ config += `[env.production]
281
+ name = "${name.replace(`-${env}`, '')}-production"
282
+
283
+ `;
284
+
285
+ // Add D1 databases for data services
286
+ if (options.type === 'data-service') {
287
+ config += `# Database bindings
288
+ [[d1_databases]]
289
+ binding = "${name.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_DB"
290
+ database_name = "${name}-db"
291
+
292
+ `;
293
+ }
294
+
295
+ // Environment variables
296
+ config += `# Environment variables
297
+ [vars]
298
+ DOMAIN_NAME = "${domainConfig.domain}"
299
+ ENVIRONMENT = "${env}"
300
+ SERVICE_NAME = "${serviceName}"
301
+ SERVICE_TYPE = "${options.type}"
302
+ DOMAIN_CONFIG = "${domainConfig.name}"
303
+
304
+ `;
305
+
306
+ // Add service-specific environment variables
307
+ if (options.type === 'auth-service') {
308
+ config += `JWT_SECRET = "change-in-production"
309
+ AUTH_PROVIDERS = "google,github"
310
+ `;
311
+ } else if (options.type === 'api-gateway') {
312
+ config += `RATE_LIMIT_REQUESTS = "100"
313
+ RATE_LIMIT_WINDOW_MS = "60000"
314
+ ENABLE_CORS = "true"
315
+ `;
316
+ }
317
+
318
+ return config;
319
+ }
320
+
321
+ generateDomainsConfig(serviceName, options, domainInfo) {
322
+ let config = `import { createDomainConfigSchema } from '@tamyla/clodo-framework';
323
+
324
+ /**
325
+ * Domain configurations for ${serviceName}
326
+ *
327
+ * Auto-generated by Clodo Framework Service Initializer
328
+ * Generated on: ${new Date().toISOString()}
329
+ *
330
+ * This service supports multiple domains with domain-specific configurations
331
+ */
332
+
333
+ export const domains = {
334
+ `;
335
+
336
+ domainInfo.domains.forEach((domainConfig, index) => {
337
+ const isLast = index === domainInfo.domains.length - 1;
338
+ config += this.generateSingleDomainConfig(serviceName, options, domainConfig, isLast);
339
+ });
340
+
341
+ config += `};
342
+
343
+ /**
344
+ * Get domain configuration by domain name
345
+ */
346
+ export function getDomainConfig(domainName) {
347
+ // Try exact match first
348
+ if (domains[domainName]) {
349
+ return domains[domainName];
350
+ }
351
+
352
+ // Try to find by domain (for runtime domain detection)
353
+ for (const [key, config] of Object.entries(domains)) {
354
+ if (config.domains.production === domainName ||
355
+ config.domains.staging === domainName ||
356
+ config.domains.development === domainName) {
357
+ return config;
358
+ }
359
+ }
360
+
361
+ // Fallback to first domain
362
+ const firstDomain = Object.keys(domains)[0];
363
+ console.warn(\`Domain \${domainName} not found, using \${firstDomain}\`);
364
+ return domains[firstDomain];
365
+ }
366
+
367
+ /**
368
+ * Get all available domains
369
+ */
370
+ export function getAvailableDomains() {
371
+ return Object.keys(domains);
372
+ }
373
+
374
+ /**
375
+ * Get domain configuration for current environment
376
+ */
377
+ export function getCurrentDomainConfig() {
378
+ // This would be determined by the request hostname in the worker
379
+ // For now, return the first domain
380
+ const firstDomain = Object.keys(domains)[0];
381
+ return domains[firstDomain];
382
+ }
383
+ `;
384
+
385
+ return config;
386
+ }
387
+
388
+ generateSingleDomainConfig(serviceName, options, domainConfig, isLast) {
389
+ const env = options.env;
390
+
391
+ let config = ` '${domainConfig.name}': {
392
+ ...createDomainConfigSchema(),
393
+ name: '${domainConfig.name}',
394
+ displayName: '${this.toTitleCase(domainConfig.name.replace(/-/g, ' '))}',
395
+ accountId: '${domainConfig.accountId || 'YOUR_CLOUDFLARE_ACCOUNT_ID'}',
396
+ zoneId: '${domainConfig.zoneId || 'YOUR_CLOUDFLARE_ZONE_ID'}',
397
+ domains: {
398
+ production: '${domainConfig.domain}',
399
+ staging: 'staging-${domainConfig.domain}',
400
+ development: 'dev-${domainConfig.domain}'
401
+ },
402
+ services: [
403
+ '${serviceName}'
404
+ ],
405
+ `;
406
+
407
+ // Add service-specific databases
408
+ if (options.type === 'data-service') {
409
+ config += `
410
+ databases: [
411
+ {
412
+ name: '${domainConfig.name}-db',
413
+ type: 'd1'
414
+ }
415
+ ],`;
416
+ }
417
+
418
+ // Add features based on service type
419
+ config += `
420
+ features: {
421
+ // Core features (always enabled)
422
+ logging: true,
423
+ monitoring: true,
424
+ errorReporting: true,
425
+ `;
426
+
427
+ // Service-specific features
428
+ if (options.type === 'data-service') {
429
+ config += `
430
+ // Data service features
431
+ authentication: true,
432
+ authorization: true,
433
+ fileStorage: false,
434
+ search: true,
435
+ filtering: true,
436
+ pagination: true,
437
+ `;
438
+ } else if (options.type === 'auth-service') {
439
+ config += `
440
+ // Auth service features
441
+ authentication: true,
442
+ authorization: true,
443
+ userProfiles: true,
444
+ emailNotifications: true,
445
+ magicLinkAuth: true,
446
+ `;
447
+ } else if (options.type === 'api-gateway') {
448
+ config += `
449
+ // API Gateway features
450
+ authentication: false,
451
+ authorization: false,
452
+ rateLimiting: true,
453
+ caching: true,
454
+ monitoring: true,
455
+ `;
456
+ } else if (options.type === 'content-service') {
457
+ config += `
458
+ // Content service features
459
+ fileStorage: true,
460
+ search: true,
461
+ filtering: true,
462
+ pagination: true,
463
+ caching: true,
464
+ `;
465
+ }
466
+
467
+ config += `
468
+ // Domain-specific customizations
469
+ domainCustomizations: true,
470
+ },
471
+ settings: {
472
+ environment: '${env}',
473
+ logLevel: 'info',
474
+ corsOrigins: ['https://${domainConfig.domain}'], // Domain-specific CORS
475
+ rateLimitRequests: 100,
476
+ rateLimitWindowMs: 60000,
477
+ enableMetrics: true,
478
+ metricsEndpoint: '/metrics',
479
+ domainName: '${domainConfig.domain}'
480
+ }
481
+ }`;
482
+
483
+ if (!isLast) {
484
+ config += ',';
485
+ }
486
+ config += '\n';
487
+
488
+ return config;
489
+ }
490
+
491
+ generatePackageConfig(serviceName, options) {
492
+ const config = {
493
+ name: serviceName,
494
+ version: '1.0.0',
495
+ description: `${this.toTitleCase(serviceName.replace(/-/g, ' '))} - ${options.type} service`,
496
+ type: 'module',
497
+ main: 'src/worker/index.js',
498
+ scripts: {
499
+ build: 'babel src/ --out-dir dist/',
500
+ dev: 'wrangler dev',
501
+ deploy: 'wrangler deploy',
502
+ test: 'jest',
503
+ lint: 'eslint src',
504
+ 'lint:fix': 'eslint src --fix'
505
+ },
506
+ dependencies: {
507
+ '@tamyla/clodo-framework': 'file:../'
508
+ },
509
+ devDependencies: {
510
+ wrangler: '^3.0.0',
511
+ jest: '^29.0.0',
512
+ eslint: '^8.0.0',
513
+ '@babel/cli': '^7.0.0',
514
+ '@babel/core': '^7.0.0',
515
+ '@babel/preset-env': '^7.0.0'
516
+ },
517
+ keywords: [
518
+ 'cloudflare',
519
+ 'workers',
520
+ 'clodo-framework',
521
+ options.type
522
+ ],
523
+ author: 'Generated by Clodo Framework',
524
+ license: 'MIT'
525
+ };
526
+
527
+ return JSON.stringify(config, null, 2);
528
+ }
529
+
530
+ showDryRun(configs) {
531
+ console.log('\n📋 DRY RUN - Would create the following files:');
532
+ console.log('\n📄 wrangler.toml:');
533
+ console.log(configs.wrangler);
534
+ console.log('\n📄 src/config/domains.js:');
535
+ console.log(configs.domains);
536
+ console.log('\n📄 package.json:');
537
+ console.log(configs.package);
538
+ }
539
+
540
+ createServiceStructure(serviceName, options, configs) {
541
+ const servicesDir = join(options.output, 'services');
542
+ const serviceDir = join(servicesDir, serviceName);
543
+
544
+ console.log(`📁 Creating service structure in: ${serviceDir}`);
545
+
546
+ // Create directories
547
+ const dirs = [
548
+ serviceDir,
549
+ join(serviceDir, 'src'),
550
+ join(serviceDir, 'src', 'worker'),
551
+ join(serviceDir, 'src', 'config'),
552
+ join(serviceDir, 'tests'),
553
+ join(serviceDir, 'docs')
554
+ ];
555
+
556
+ for (const dir of dirs) {
557
+ if (!existsSync(dir)) {
558
+ mkdirSync(dir, { recursive: true });
559
+ }
560
+ }
561
+
562
+ // Write wrangler configurations (could be single file or multiple)
563
+ if (typeof configs.wrangler === 'string') {
564
+ // Single wrangler.toml
565
+ writeFileSync(join(serviceDir, 'wrangler.toml'), configs.wrangler);
566
+ } else {
567
+ // Multiple wrangler files
568
+ for (const [filename, content] of Object.entries(configs.wrangler)) {
569
+ writeFileSync(join(serviceDir, filename), content);
570
+ }
571
+ }
572
+
573
+ // Write other configuration files
574
+ writeFileSync(join(serviceDir, 'package.json'), configs.package);
575
+ writeFileSync(join(serviceDir, 'src', 'config', 'domains.js'), configs.domains);
576
+
577
+ // Create basic worker file
578
+ this.createWorkerFile(serviceName, options, serviceDir);
579
+
580
+ // Create README
581
+ this.createReadmeFile(serviceName, options, serviceDir);
582
+
583
+ console.log('✅ Configuration files created');
584
+ }
585
+
586
+ createWorkerFile(serviceName, options, serviceDir) {
587
+ const workerContent = `import { initializeService, createFeatureGuard, COMMON_FEATURES } from '@tamyla/clodo-framework';
588
+ import { domains, getDomainConfig } from '../config/domains.js';
589
+
590
+ /**
591
+ * ${this.toTitleCase(serviceName.replace(/-/g, ' '))} - ${options.type} service
592
+ *
593
+ * Auto-generated by Clodo Framework Service Initializer
594
+ * Generated on: ${new Date().toISOString()}
595
+ *
596
+ * Supports multiple domains with domain-specific configurations
597
+ */
598
+
599
+ export default {
600
+ async fetch(request, env, ctx) {
601
+ try {
602
+ // Get domain configuration based on request hostname
603
+ const hostname = new URL(request.url).hostname;
604
+ const domainConfig = getDomainConfig(hostname);
605
+
606
+ // Initialize service with domain-specific configuration
607
+ const service = initializeService(env, { [domainConfig.name]: domainConfig });
608
+
609
+ // Log request (if logging is enabled)
610
+ if (service.features.includes(COMMON_FEATURES.LOGGING)) {
611
+ console.log(\`\${request.method} \${request.url} - \${hostname} (\${service.environment}) [\${domainConfig.name}]\`);
612
+ }
613
+
614
+ const url = new URL(request.url);
615
+
616
+ // Health check endpoint
617
+ if (url.pathname === '/health') {
618
+ return new Response(JSON.stringify({
619
+ status: 'healthy',
620
+ service: '${serviceName}',
621
+ version: '1.0.0',
622
+ type: '${options.type}',
623
+ domain: hostname,
624
+ domainConfig: domainConfig.name,
625
+ features: service.features,
626
+ environment: service.environment,
627
+ timestamp: new Date().toISOString()
628
+ }), {
629
+ headers: {
630
+ 'Content-Type': 'application/json',
631
+ 'Cache-Control': 'no-cache'
632
+ }
633
+ });
634
+ }
635
+
636
+ // Domain info endpoint
637
+ if (url.pathname === '/domain') {
638
+ return new Response(JSON.stringify({
639
+ hostname: hostname,
640
+ domainConfig: domainConfig.name,
641
+ accountId: domainConfig.accountId,
642
+ zoneId: domainConfig.zoneId,
643
+ corsOrigins: domainConfig.settings.corsOrigins,
644
+ rateLimit: {
645
+ requests: domainConfig.settings.rateLimitRequests,
646
+ windowMs: domainConfig.settings.rateLimitWindowMs
647
+ }
648
+ }), {
649
+ headers: {
650
+ 'Content-Type': 'application/json'
651
+ }
652
+ });
653
+ }
654
+
655
+ // Service-specific endpoints
656
+ ${this.getServiceSpecificEndpoints(options.type)}
657
+
658
+ // Default response
659
+ return new Response(JSON.stringify({
660
+ message: '${serviceName} service running',
661
+ type: '${options.type}',
662
+ domain: hostname,
663
+ domainConfig: domainConfig.name,
664
+ endpoints: ['/health', '/domain'${this.getServiceEndpoints(options.type)}]
665
+ }), {
666
+ headers: {
667
+ 'Content-Type': 'application/json'
668
+ }
669
+ });
670
+
671
+ } catch (error) {
672
+ console.error('Service error:', error);
673
+ return new Response(JSON.stringify({
674
+ error: error.message,
675
+ status: 'error'
676
+ }), {
677
+ status: 500,
678
+ headers: {
679
+ 'Content-Type': 'application/json'
680
+ }
681
+ });
682
+ }
683
+ }
684
+ };
685
+ `;
686
+
687
+ writeFileSync(join(serviceDir, 'src', 'worker', 'index.js'), workerContent);
688
+ }
689
+
690
+ getServiceSpecificEndpoints(serviceType) {
691
+ const endpoints = {
692
+ 'data-service': `
693
+ // Data service endpoints
694
+ if (url.pathname.startsWith('/api/data')) {
695
+ // Handle data operations
696
+ return new Response(JSON.stringify({
697
+ message: 'Data service endpoint',
698
+ path: url.pathname
699
+ }), {
700
+ headers: { 'Content-Type': 'application/json' }
701
+ });
702
+ }`,
703
+ 'auth-service': `
704
+ // Auth service endpoints
705
+ if (url.pathname.startsWith('/auth')) {
706
+ // Handle authentication operations
707
+ return new Response(JSON.stringify({
708
+ message: 'Auth service endpoint',
709
+ path: url.pathname
710
+ }), {
711
+ headers: { 'Content-Type': 'application/json' }
712
+ });
713
+ }`,
714
+ 'api-gateway': `
715
+ // API Gateway endpoints
716
+ if (url.pathname.startsWith('/api/')) {
717
+ // Route to appropriate services
718
+ return new Response(JSON.stringify({
719
+ message: 'API Gateway routing',
720
+ path: url.pathname
721
+ }), {
722
+ headers: { 'Content-Type': 'application/json' }
723
+ });
724
+ }`,
725
+ 'content-service': `
726
+ // Content service endpoints
727
+ if (url.pathname.startsWith('/content')) {
728
+ // Handle content operations
729
+ return new Response(JSON.stringify({
730
+ message: 'Content service endpoint',
731
+ path: url.pathname
732
+ }), {
733
+ headers: { 'Content-Type': 'application/json' }
734
+ });
735
+ }`,
736
+ 'generic': `
737
+ // Generic service endpoints
738
+ if (url.pathname.startsWith('/api/generic')) {
739
+ // Handle generic operations
740
+ return new Response(JSON.stringify({
741
+ message: 'Generic service endpoint',
742
+ path: url.pathname
743
+ }), {
744
+ headers: { 'Content-Type': 'application/json' }
745
+ });
746
+ }`
747
+ };
748
+
749
+ return endpoints[serviceType] || endpoints['generic'];
750
+ }
751
+
752
+ // Duplicate method removed - keeping the more comprehensive version below
753
+
754
+ getServiceEndpoints(serviceType) {
755
+ switch (serviceType) {
756
+ case 'data-service':
757
+ return ', \'/api/data\'';
758
+ case 'auth-service':
759
+ return ', \'/auth/login\'';
760
+ default:
761
+ return '';
762
+ }
763
+ }
764
+
765
+ createReadmeFile(serviceName, options, serviceDir) {
766
+ const readme = `# ${this.toTitleCase(serviceName.replace(/-/g, ' '))}
767
+
768
+ ${options.type} service built with Clodo Framework.
769
+
770
+ ## Setup
771
+
772
+ This service was auto-initialized with the following configuration:
773
+
774
+ - **Service Type**: ${options.type}
775
+ - **Environment**: ${options.env}
776
+ - **Framework**: Clodo Framework
777
+
778
+ ## Quick Start
779
+
780
+ \`\`\`bash
781
+ npm install
782
+ npm run dev
783
+ \`\`\`
784
+
785
+ ## Deployment
786
+
787
+ \`\`\`bash
788
+ npm run deploy
789
+ \`\`\`
790
+
791
+ ## Configuration
792
+
793
+ ### wrangler.toml
794
+ Contains Cloudflare Workers configuration with environment-specific settings.
795
+
796
+ ### src/config/domains.js
797
+ Contains domain-specific configuration and feature flags.
798
+
799
+ ## Environment Variables
800
+
801
+ Set these in your Cloudflare Workers environment or wrangler.toml:
802
+
803
+ - \`CLOUDFLARE_ACCOUNT_ID\` - Your Cloudflare account ID
804
+ - \`CLOUDFLARE_ZONE_ID\` - Your Cloudflare zone ID
805
+
806
+ ## Development
807
+
808
+ \`\`\`bash
809
+ # Run tests
810
+ npm test
811
+
812
+ # Lint code
813
+ npm run lint
814
+
815
+ # Fix linting issues
816
+ npm run lint:fix
817
+ \`\`\`
818
+
819
+ ## API Endpoints
820
+
821
+ - \`GET /health\` - Health check endpoint
822
+ ${this.getApiDocs(options.type)}
823
+
824
+ ---
825
+
826
+ *Generated by Clodo Framework Service Initializer on ${new Date().toISOString()}*
827
+ `;
828
+
829
+ writeFileSync(join(serviceDir, 'README.md'), readme);
830
+ }
831
+
832
+ getApiDocs(serviceType) {
833
+ switch (serviceType) {
834
+ case 'data-service':
835
+ return '- `GET/POST/PUT/DELETE /api/data` - Data operations';
836
+ case 'auth-service':
837
+ return '- `POST /auth/login` - User authentication\n- `POST /auth/logout` - User logout\n- `GET /auth/profile` - User profile';
838
+ case 'api-gateway':
839
+ return '- `/*` - API Gateway routes (configure in domains.js)';
840
+ case 'content-service':
841
+ return '- `GET /content/*` - Content delivery\n- `POST /content/upload` - Content upload';
842
+ default:
843
+ return '- Service-specific endpoints (configure as needed)';
844
+ }
845
+ }
846
+
847
+ validateSetup(serviceName, options) {
848
+ const servicesDir = join(options.output, 'services');
849
+ const serviceDir = join(servicesDir, serviceName);
850
+
851
+ console.log('🔍 Validating setup...');
852
+
853
+ const requiredFiles = [
854
+ 'package.json',
855
+ 'src/worker/index.js',
856
+ 'src/config/domains.js',
857
+ 'README.md'
858
+ ];
859
+
860
+ // Check for wrangler files - either single or multiple
861
+ const wranglerFiles = readdirSync(serviceDir).filter(file => file.startsWith('wrangler.') && file.endsWith('.toml'));
862
+ if (wranglerFiles.length === 0 && !existsSync(join(serviceDir, 'wrangler.toml'))) {
863
+ throw new Error('No wrangler configuration files found');
864
+ }
865
+
866
+ for (const file of requiredFiles) {
867
+ const filePath = join(serviceDir, file);
868
+ if (!existsSync(filePath)) {
869
+ throw new Error(`Required file not created: ${file}`);
870
+ }
871
+ }
872
+
873
+ console.log('✅ All required files created successfully');
874
+ }
875
+
876
+ toTitleCase(str) {
877
+ return str.split(' ')
878
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
879
+ .join(' ');
880
+ }
881
+
882
+ run() {
883
+ program.parse();
884
+ }
885
+ }
886
+
887
+ // Run the initializer
888
+ const initializer = new ServiceInitializer();
889
+ initializer.run();