@tamyla/clodo-framework 3.0.15 → 3.1.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 (70) hide show
  1. package/CHANGELOG.md +215 -163
  2. package/README.md +133 -1
  3. package/bin/clodo-service.js +0 -0
  4. package/bin/security/security-cli.js +0 -0
  5. package/bin/service-management/create-service.js +0 -0
  6. package/bin/service-management/init-service.js +2 -1
  7. package/dist/service-management/GenerationEngine.js +298 -3025
  8. package/dist/service-management/ServiceCreator.js +19 -3
  9. package/dist/service-management/generators/BaseGenerator.js +233 -0
  10. package/dist/service-management/generators/GeneratorRegistry.js +254 -0
  11. package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +87 -0
  12. package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +106 -0
  13. package/dist/service-management/generators/code/ServiceHandlersGenerator.js +235 -0
  14. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +116 -0
  15. package/dist/service-management/generators/code/ServiceUtilsGenerator.js +246 -0
  16. package/dist/service-management/generators/code/WorkerIndexGenerator.js +143 -0
  17. package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +101 -0
  18. package/dist/service-management/generators/config/DomainsConfigGenerator.js +175 -0
  19. package/dist/service-management/generators/config/EnvExampleGenerator.js +178 -0
  20. package/dist/service-management/generators/config/ProductionEnvGenerator.js +97 -0
  21. package/dist/service-management/generators/config/StagingEnvGenerator.js +97 -0
  22. package/dist/service-management/generators/config/WranglerTomlGenerator.js +238 -0
  23. package/dist/service-management/generators/core/PackageJsonGenerator.js +243 -0
  24. package/dist/service-management/generators/core/SiteConfigGenerator.js +115 -0
  25. package/dist/service-management/generators/documentation/ApiDocsGenerator.js +331 -0
  26. package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +294 -0
  27. package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +244 -0
  28. package/dist/service-management/generators/documentation/ReadmeGenerator.js +196 -0
  29. package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +190 -0
  30. package/dist/service-management/generators/scripts/DeployScriptGenerator.js +123 -0
  31. package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +101 -0
  32. package/dist/service-management/generators/scripts/SetupScriptGenerator.js +88 -0
  33. package/dist/service-management/generators/service-types/StaticSiteGenerator.js +342 -0
  34. package/dist/service-management/generators/testing/EslintConfigGenerator.js +85 -0
  35. package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +237 -0
  36. package/dist/service-management/generators/testing/JestConfigGenerator.js +72 -0
  37. package/dist/service-management/generators/testing/UnitTestsGenerator.js +277 -0
  38. package/dist/service-management/generators/tooling/DockerComposeGenerator.js +71 -0
  39. package/dist/service-management/generators/tooling/GitignoreGenerator.js +143 -0
  40. package/dist/service-management/generators/utils/FileWriter.js +179 -0
  41. package/dist/service-management/generators/utils/PathResolver.js +157 -0
  42. package/dist/service-management/generators/utils/ServiceManifestGenerator.js +111 -0
  43. package/dist/service-management/generators/utils/TemplateEngine.js +185 -0
  44. package/dist/service-management/generators/utils/index.js +18 -0
  45. package/dist/service-management/routing/DomainRouteMapper.js +311 -0
  46. package/dist/service-management/routing/RouteGenerator.js +266 -0
  47. package/dist/service-management/routing/WranglerRoutesBuilder.js +273 -0
  48. package/dist/service-management/routing/index.js +14 -0
  49. package/dist/service-management/services/DirectoryStructureService.js +56 -0
  50. package/dist/service-management/services/GenerationCoordinator.js +208 -0
  51. package/dist/service-management/services/GeneratorRegistry.js +174 -0
  52. package/dist/services/GenericDataService.js +14 -1
  53. package/dist/utils/config/unified-config-manager.js +128 -12
  54. package/dist/utils/framework-config.js +74 -2
  55. package/dist/worker/integration.js +4 -1
  56. package/package.json +6 -1
  57. package/templates/generic/clodo-service-manifest.json +25 -0
  58. package/templates/static-site/.env.example +61 -0
  59. package/templates/static-site/README.md +176 -0
  60. package/templates/static-site/clodo-service-manifest.json +66 -0
  61. package/templates/static-site/package.json +28 -0
  62. package/templates/static-site/public/404.html +87 -0
  63. package/templates/static-site/public/app.js +100 -0
  64. package/templates/static-site/public/index.html +48 -0
  65. package/templates/static-site/public/styles.css +123 -0
  66. package/templates/static-site/scripts/deploy.ps1 +121 -0
  67. package/templates/static-site/scripts/setup.ps1 +179 -0
  68. package/templates/static-site/src/config/domains.js +35 -0
  69. package/templates/static-site/src/worker/index.js +153 -0
  70. package/templates/static-site/wrangler.toml +43 -0
@@ -0,0 +1,97 @@
1
+ import { BaseGenerator } from '../BaseGenerator.js';
2
+ import { join } from 'path';
3
+
4
+ /**
5
+ * StagingEnvGenerator
6
+ *
7
+ * Generates config/staging.env file for staging environment.
8
+ */
9
+ export class StagingEnvGenerator extends BaseGenerator {
10
+ constructor(options = {}) {
11
+ super({
12
+ name: 'StagingEnvGenerator',
13
+ description: 'Generates staging environment configuration',
14
+ outputPath: 'config/staging.env',
15
+ ...options
16
+ });
17
+ }
18
+ shouldGenerate(context) {
19
+ return true;
20
+ }
21
+ async generate(context) {
22
+ const coreInputs = context.coreInputs || context;
23
+ const confirmedValues = context.confirmedValues || context;
24
+ const servicePath = context.servicePath || context.outputDir;
25
+ this.setContext({
26
+ coreInputs,
27
+ confirmedValues,
28
+ servicePath
29
+ });
30
+ const content = `# Staging Environment Configuration
31
+ # Generated by Clodo Framework GenerationEngine
32
+
33
+ # Service Configuration
34
+ SERVICE_NAME=${coreInputs.serviceName}
35
+ SERVICE_TYPE=${coreInputs.serviceType}
36
+ ENVIRONMENT=staging
37
+
38
+ # URLs
39
+ PRODUCTION_URL=${confirmedValues.productionUrl}
40
+ STAGING_URL=${confirmedValues.stagingUrl}
41
+ DEVELOPMENT_URL=${confirmedValues.developmentUrl}
42
+
43
+ # Cloudflare Configuration
44
+ CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}
45
+ CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}
46
+
47
+ # Database
48
+ DATABASE_NAME=${confirmedValues.databaseName}
49
+
50
+ # Logging and Monitoring
51
+ LOG_LEVEL=info
52
+ METRICS_ENABLED=true
53
+ ERROR_REPORTING_ENABLED=true
54
+ `;
55
+ await this.writeFile(join('config', 'staging.env'), content);
56
+ return join(servicePath, 'config', 'staging.env');
57
+ }
58
+ validateContext(context) {
59
+ const coreInputs = context.coreInputs || context;
60
+ const confirmedValues = context.confirmedValues || context;
61
+ const required = [{
62
+ field: 'serviceName',
63
+ source: coreInputs
64
+ }, {
65
+ field: 'serviceType',
66
+ source: coreInputs
67
+ }, {
68
+ field: 'cloudflareAccountId',
69
+ source: coreInputs
70
+ }, {
71
+ field: 'cloudflareZoneId',
72
+ source: coreInputs
73
+ }, {
74
+ field: 'productionUrl',
75
+ source: confirmedValues
76
+ }, {
77
+ field: 'stagingUrl',
78
+ source: confirmedValues
79
+ }, {
80
+ field: 'developmentUrl',
81
+ source: confirmedValues
82
+ }, {
83
+ field: 'databaseName',
84
+ source: confirmedValues
85
+ }];
86
+ const missing = required.filter(({
87
+ field,
88
+ source
89
+ }) => !source || !source[field]).map(({
90
+ field
91
+ }) => field);
92
+ if (missing.length > 0) {
93
+ throw new Error(`StagingEnvGenerator: Missing required fields: ${missing.join(', ')}`);
94
+ }
95
+ return true;
96
+ }
97
+ }
@@ -0,0 +1,238 @@
1
+ import { BaseGenerator } from '../BaseGenerator.js';
2
+ import { join } from 'path';
3
+
4
+ /**
5
+ * WranglerTomlGenerator
6
+ *
7
+ * Generates wrangler.toml configuration files for Cloudflare Workers.
8
+ *
9
+ * Responsibilities:
10
+ * - Generate main worker configuration (name, main, compatibility)
11
+ * - Include routes configuration from RouteGenerator
12
+ * - Include Workers Sites configuration from SiteConfigGenerator
13
+ * - Configure environment-specific settings (dev/staging/production)
14
+ * - Configure D1 database bindings
15
+ * - Configure environment variables and feature flags
16
+ *
17
+ * Dependencies:
18
+ * - RouteGenerator (for routes configuration)
19
+ * - SiteConfigGenerator (for Workers Sites configuration)
20
+ */
21
+ export class WranglerTomlGenerator extends BaseGenerator {
22
+ constructor(options = {}) {
23
+ super({
24
+ name: 'WranglerTomlGenerator',
25
+ description: 'Generates wrangler.toml configuration for Cloudflare Workers',
26
+ outputPath: 'wrangler.toml',
27
+ ...options
28
+ });
29
+ this.routeGenerator = options.routeGenerator;
30
+ this.siteConfigGenerator = options.siteConfigGenerator;
31
+ }
32
+
33
+ /**
34
+ * Always generate wrangler.toml
35
+ */
36
+ shouldGenerate(context) {
37
+ return true;
38
+ }
39
+
40
+ /**
41
+ * Generate wrangler.toml content
42
+ */
43
+ async generate(context) {
44
+ // Support both new structured format and legacy flat format
45
+ const coreInputs = context.coreInputs || context;
46
+ const confirmedValues = context.confirmedValues || context;
47
+ const servicePath = context.servicePath || context.outputDir;
48
+ this.setContext({
49
+ coreInputs,
50
+ confirmedValues,
51
+ servicePath
52
+ });
53
+
54
+ // Generate routes configuration
55
+ const routesConfig = await this._generateRoutesConfig(coreInputs, confirmedValues);
56
+
57
+ // Generate Workers Sites configuration (static-site only)
58
+ const siteConfig = await this._generateSiteConfig(coreInputs);
59
+
60
+ // Build wrangler.toml content
61
+ const content = this._buildWranglerToml(coreInputs, confirmedValues, routesConfig, siteConfig);
62
+
63
+ // Write file
64
+ await this.writeFile('wrangler.toml', content);
65
+
66
+ // Return full path for backward compatibility
67
+ return join(servicePath, 'wrangler.toml');
68
+ }
69
+
70
+ /**
71
+ * Generate routes configuration using RouteGenerator
72
+ */
73
+ async _generateRoutesConfig(coreInputs, confirmedValues) {
74
+ if (!this.routeGenerator) {
75
+ console.warn('⚠️ RouteGenerator not available. Routes will need to be configured manually.');
76
+ return '# Routes will be configured during deployment\n';
77
+ }
78
+ try {
79
+ const routesConfig = this.routeGenerator.generateRoutes(coreInputs, confirmedValues, {
80
+ includeComments: true,
81
+ includeZoneId: true
82
+ });
83
+ return routesConfig;
84
+ } catch (error) {
85
+ console.warn(`⚠️ Could not generate routes: ${error.message}`);
86
+ console.warn(' Continuing without automatic routes. You can add them manually later.');
87
+ return '# Routes will be configured during deployment\n';
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Generate Workers Sites configuration using SiteConfigGenerator
93
+ */
94
+ async _generateSiteConfig(coreInputs) {
95
+ if (!this.siteConfigGenerator) {
96
+ return '';
97
+ }
98
+
99
+ // Only generate site config for static-site service type
100
+ if (coreInputs.serviceType !== 'static-site') {
101
+ return '';
102
+ }
103
+ try {
104
+ const siteConfig = await this.siteConfigGenerator.generate({
105
+ coreInputs,
106
+ siteConfig: coreInputs.siteConfig || {}
107
+ });
108
+ return siteConfig;
109
+ } catch (error) {
110
+ console.warn(`⚠️ Could not generate Workers Sites config: ${error.message}`);
111
+ return '';
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Build the complete wrangler.toml content
117
+ */
118
+ _buildWranglerToml(coreInputs, confirmedValues, routesConfig, siteConfig) {
119
+ const compatDate = new Date().toISOString().split('T')[0];
120
+ return `# Cloudflare Workers Configuration for ${confirmedValues.displayName}
121
+ name = "${confirmedValues.workerName}"
122
+ main = "src/worker/index.js"
123
+ compatibility_date = "${compatDate}"
124
+ compatibility_flags = ["nodejs_compat"]
125
+
126
+ # Account configuration
127
+ account_id = "${coreInputs.cloudflareAccountId}"
128
+
129
+ ${routesConfig}
130
+ ${siteConfig ? '\n' + siteConfig : ''}
131
+
132
+ # Environment configurations
133
+ [env.development]
134
+ name = "${confirmedValues.workerName}-dev"
135
+
136
+ [env.staging]
137
+ name = "${confirmedValues.workerName}-staging"
138
+
139
+ [env.production]
140
+ name = "${confirmedValues.workerName}"
141
+
142
+ # Database bindings
143
+ [[d1_databases]]
144
+ binding = "DB"
145
+ database_name = "${confirmedValues.databaseName}"
146
+ database_id = "" # To be configured during setup
147
+
148
+ # Environment variables
149
+ [vars]
150
+ SERVICE_NAME = "${coreInputs.serviceName}"
151
+ SERVICE_TYPE = "${coreInputs.serviceType}"
152
+ DOMAIN_NAME = "${coreInputs.domainName}"
153
+ ENVIRONMENT = "${coreInputs.environment}"
154
+ API_BASE_PATH = "${confirmedValues.apiBasePath}"
155
+ HEALTH_CHECK_PATH = "${confirmedValues.healthCheckPath}"
156
+
157
+ # Domain-specific variables
158
+ PRODUCTION_URL = "${confirmedValues.productionUrl}"
159
+ STAGING_URL = "${confirmedValues.stagingUrl}"
160
+ DEVELOPMENT_URL = "${confirmedValues.developmentUrl}"
161
+
162
+ # Feature flags
163
+ ${this._generateFeatureFlags(confirmedValues.features)}
164
+
165
+ # Custom environment variables (configure as needed)
166
+ # CUSTOM_VAR = "value"
167
+ `;
168
+ }
169
+
170
+ /**
171
+ * Generate feature flags section
172
+ */
173
+ _generateFeatureFlags(features) {
174
+ if (!features || typeof features !== 'object') {
175
+ return '# No feature flags configured';
176
+ }
177
+ const flagEntries = Object.entries(features).filter(([, enabled]) => enabled).map(([feature, enabled]) => `FEATURE_${feature.toUpperCase()} = ${enabled}`);
178
+ return flagEntries.length > 0 ? flagEntries.join('\n') : '# No feature flags configured';
179
+ }
180
+
181
+ /**
182
+ * Validate context has required fields
183
+ */
184
+ validateContext(context) {
185
+ const coreInputs = context.coreInputs || context;
186
+ const confirmedValues = context.confirmedValues || context;
187
+ const required = [{
188
+ field: 'cloudflareAccountId',
189
+ source: coreInputs
190
+ }, {
191
+ field: 'serviceName',
192
+ source: coreInputs
193
+ }, {
194
+ field: 'serviceType',
195
+ source: coreInputs
196
+ }, {
197
+ field: 'domainName',
198
+ source: coreInputs
199
+ }, {
200
+ field: 'environment',
201
+ source: coreInputs
202
+ }, {
203
+ field: 'workerName',
204
+ source: confirmedValues
205
+ }, {
206
+ field: 'displayName',
207
+ source: confirmedValues
208
+ }, {
209
+ field: 'databaseName',
210
+ source: confirmedValues
211
+ }, {
212
+ field: 'apiBasePath',
213
+ source: confirmedValues
214
+ }, {
215
+ field: 'healthCheckPath',
216
+ source: confirmedValues
217
+ }, {
218
+ field: 'productionUrl',
219
+ source: confirmedValues
220
+ }, {
221
+ field: 'stagingUrl',
222
+ source: confirmedValues
223
+ }, {
224
+ field: 'developmentUrl',
225
+ source: confirmedValues
226
+ }];
227
+ const missing = required.filter(({
228
+ field,
229
+ source
230
+ }) => !source || !source[field]).map(({
231
+ field
232
+ }) => field);
233
+ if (missing.length > 0) {
234
+ throw new Error(`WranglerTomlGenerator: Missing required fields: ${missing.join(', ')}`);
235
+ }
236
+ return true;
237
+ }
238
+ }
@@ -0,0 +1,243 @@
1
+ /**
2
+ * PackageJsonGenerator - Generates package.json for services
3
+ *
4
+ * Creates a complete package.json with service-type specific dependencies,
5
+ * scripts for development/testing/deployment, and proper metadata.
6
+ */
7
+ import { BaseGenerator } from '../BaseGenerator.js';
8
+ export class PackageJsonGenerator extends BaseGenerator {
9
+ /**
10
+ * Create a new PackageJsonGenerator instance
11
+ * @param {Object} options - Generator options
12
+ */
13
+ constructor(options = {}) {
14
+ super({
15
+ name: 'PackageJsonGenerator',
16
+ ...options
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Generate package.json file
22
+ *
23
+ * Supports two calling conventions for backward compatibility:
24
+ * 1. New: context = { coreInputs: {...}, confirmedValues: {...} }
25
+ * 2. Merged: context = { ...coreInputs, ...confirmedValues }
26
+ *
27
+ * @param {Object} context - Generation context
28
+ * @param {Object} context.coreInputs - Core service inputs (serviceType, etc.) [new convention]
29
+ * @param {Object} context.confirmedValues - Confirmed user values (packageName, version, etc.) [new convention]
30
+ * @returns {Promise<void>}
31
+ */
32
+ async generate(context) {
33
+ this.setContext(context);
34
+
35
+ // Support both calling conventions
36
+ // New: { coreInputs: {...}, confirmedValues: {...} }
37
+ // Merged: { serviceName, serviceType, packageName, version, ... }
38
+ let coreInputs, confirmedValues;
39
+ if (context.coreInputs || context.confirmedValues) {
40
+ // New convention - use separate objects (with defaults for missing ones)
41
+ coreInputs = context.coreInputs || {};
42
+ confirmedValues = context.confirmedValues || {};
43
+ } else {
44
+ // Merged convention - extract from flat context
45
+ coreInputs = {
46
+ serviceName: context.serviceName,
47
+ serviceType: context.serviceType,
48
+ domainName: context.domainName,
49
+ cloudflareAccountId: context.cloudflareAccountId,
50
+ cloudflareZoneId: context.cloudflareZoneId,
51
+ environment: context.environment
52
+ };
53
+ confirmedValues = {
54
+ packageName: context.packageName,
55
+ version: context.version,
56
+ description: context.description,
57
+ author: context.author,
58
+ displayName: context.displayName,
59
+ gitRepositoryUrl: context.gitRepositoryUrl,
60
+ workerName: context.workerName,
61
+ databaseName: context.databaseName,
62
+ apiBasePath: context.apiBasePath,
63
+ healthCheckPath: context.healthCheckPath,
64
+ productionUrl: context.productionUrl,
65
+ stagingUrl: context.stagingUrl,
66
+ developmentUrl: context.developmentUrl,
67
+ features: context.features,
68
+ keywords: context.keywords
69
+ };
70
+ }
71
+ if (!coreInputs || !confirmedValues) {
72
+ throw new Error('PackageJsonGenerator requires service configuration in context');
73
+ }
74
+ const packageJson = this.buildPackageJson(coreInputs, confirmedValues);
75
+ const content = JSON.stringify(packageJson, null, 2);
76
+ await this.writeFile('package.json', content);
77
+ }
78
+
79
+ /**
80
+ * Build the package.json object
81
+ * @param {Object} coreInputs - Core service inputs
82
+ * @param {Object} confirmedValues - Confirmed user values
83
+ * @returns {Object} - Package.json object
84
+ */
85
+ buildPackageJson(coreInputs, confirmedValues) {
86
+ return {
87
+ name: confirmedValues.packageName,
88
+ version: confirmedValues.version || '1.0.0',
89
+ description: confirmedValues.description || `Clodo Framework ${coreInputs.serviceType} service`,
90
+ main: 'src/worker/index.js',
91
+ type: 'module',
92
+ scripts: this.buildScripts(coreInputs),
93
+ dependencies: this.buildDependencies(coreInputs),
94
+ devDependencies: this.buildDevDependencies(coreInputs),
95
+ author: confirmedValues.author,
96
+ // Match original - can be undefined
97
+ license: confirmedValues.license || 'MIT',
98
+ repository: this.buildRepository(confirmedValues),
99
+ keywords: this.buildKeywords(coreInputs, confirmedValues),
100
+ engines: {
101
+ node: '>=18.0.0'
102
+ }
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Build npm scripts section
108
+ * @param {Object} coreInputs - Core service inputs
109
+ * @returns {Object} - Scripts object
110
+ */
111
+ buildScripts(coreInputs) {
112
+ const scripts = {
113
+ // Development
114
+ dev: 'wrangler dev',
115
+ // Testing
116
+ test: 'jest',
117
+ 'test:watch': 'jest --watch',
118
+ 'test:coverage': 'jest --coverage',
119
+ // Deployment (cross-platform via framework)
120
+ deploy: 'clodo-service deploy',
121
+ 'deploy:dev': 'node scripts/deploy.js development',
122
+ 'deploy:staging': 'node scripts/deploy.js staging',
123
+ 'deploy:prod': 'node scripts/deploy.js production',
124
+ // Code Quality
125
+ lint: 'eslint src/ test/',
126
+ 'lint:fix': 'eslint src/ test/ --fix',
127
+ format: 'prettier --write src/ test/',
128
+ // Utilities
129
+ validate: 'clodo-service validate .',
130
+ diagnose: 'clodo-service diagnose .',
131
+ build: 'wrangler deploy --dry-run',
132
+ clean: 'rimraf dist/ coverage/'
133
+ };
134
+
135
+ // Add service-type specific scripts
136
+ if (coreInputs.serviceType === 'static-site') {
137
+ scripts['build:assets'] = 'echo "Add your static asset build command here"';
138
+ scripts['preview'] = 'wrangler dev --local';
139
+ }
140
+ return scripts;
141
+ }
142
+
143
+ /**
144
+ * Build production dependencies
145
+ *
146
+ * NOTE: UUID is included for all service types to match original GenerationEngine behavior.
147
+ * While not all services may need it, it's a small dependency and many services use it for
148
+ * request tracking, correlation IDs, or other purposes.
149
+ *
150
+ * @param {Object} coreInputs - Core service inputs
151
+ * @returns {Object} - Dependencies object
152
+ */
153
+ buildDependencies(coreInputs) {
154
+ const dependencies = {
155
+ '@tamyla/clodo-framework': '^3.0.15',
156
+ uuid: '^13.0.0',
157
+ // Always included (matches original GenerationEngine behavior)
158
+ wrangler: '^3.0.0'
159
+ };
160
+
161
+ // Service-type specific additional dependencies
162
+ switch (coreInputs.serviceType) {
163
+ case 'auth':
164
+ dependencies.bcrypt = '^5.1.0'; // For password hashing
165
+ break;
166
+ case 'static-site':
167
+ dependencies['mime-types'] = '^2.1.35'; // For MIME type detection
168
+ break;
169
+
170
+ // data, content, api-gateway, generic: uuid is already included above
171
+ default:
172
+ break;
173
+ }
174
+ return dependencies;
175
+ }
176
+
177
+ /**
178
+ * Build development dependencies
179
+ * @param {Object} coreInputs - Core service inputs
180
+ * @returns {Object} - DevDependencies object
181
+ */
182
+ buildDevDependencies(coreInputs) {
183
+ const devDependencies = {
184
+ '@types/jest': '^29.5.0',
185
+ '@types/node': '^20.0.0',
186
+ eslint: '^8.54.0',
187
+ jest: '^29.7.0',
188
+ prettier: '^3.1.0',
189
+ rimraf: '^5.0.0'
190
+ };
191
+
192
+ // Add service-type specific dev dependencies
193
+ if (coreInputs.serviceType === 'static-site') {
194
+ devDependencies['@types/mime-types'] = '^2.1.1';
195
+ }
196
+ return devDependencies;
197
+ }
198
+
199
+ /**
200
+ * Build repository configuration
201
+ * @param {Object} confirmedValues - Confirmed user values
202
+ * @returns {Object|undefined} - Repository object or undefined
203
+ */
204
+ buildRepository(confirmedValues) {
205
+ if (!confirmedValues.gitRepositoryUrl) {
206
+ return undefined;
207
+ }
208
+ return {
209
+ type: 'git',
210
+ url: confirmedValues.gitRepositoryUrl
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Build keywords array
216
+ * Matches original GenerationEngine behavior exactly
217
+ * @param {Object} coreInputs - Core service inputs
218
+ * @param {Object} confirmedValues - Confirmed user values
219
+ * @returns {string[]} - Keywords array
220
+ */
221
+ buildKeywords(coreInputs, confirmedValues) {
222
+ const keywords = ['clodo-framework', coreInputs.serviceType, 'cloudflare', 'serverless'
223
+ // Note: 'workers' removed to match original GenerationEngine
224
+ ];
225
+
226
+ // Add custom keywords if provided
227
+ if (confirmedValues.keywords && Array.isArray(confirmedValues.keywords)) {
228
+ keywords.push(...confirmedValues.keywords);
229
+ }
230
+ return keywords;
231
+ }
232
+
233
+ /**
234
+ * Determine if this generator should run
235
+ * @param {Object} context - Generation context
236
+ * @returns {boolean} - True if should generate
237
+ */
238
+ shouldGenerate(context) {
239
+ // Always generate package.json for all service types
240
+ return true;
241
+ }
242
+ }
243
+ export default PackageJsonGenerator;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * SiteConfigGenerator - Generates Workers Sites [site] configuration
3
+ *
4
+ * Creates the [site] section for wrangler.toml for static-site services.
5
+ * Only generates for static-site template, not for other service types.
6
+ */
7
+ import { BaseGenerator } from '../BaseGenerator.js';
8
+ export class SiteConfigGenerator extends BaseGenerator {
9
+ /**
10
+ * Create a new SiteConfigGenerator instance
11
+ * @param {Object} options - Generator options
12
+ */
13
+ constructor(options = {}) {
14
+ super({
15
+ name: 'SiteConfigGenerator',
16
+ ...options
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Generate Workers Sites configuration
22
+ * Returns TOML [site] section as a string
23
+ *
24
+ * Supports two calling conventions:
25
+ * 1. New: context = { coreInputs: { serviceType }, siteConfig: {...} }
26
+ * 2. Old: context = { serviceType, siteConfig: {...} }
27
+ *
28
+ * @param {Object} context - Generation context
29
+ * @param {Object} context.coreInputs - Core service inputs (serviceType, etc.) [new convention]
30
+ * @param {string} context.serviceType - Service type [old convention - deprecated]
31
+ * @param {Object} context.siteConfig - Optional site configuration overrides
32
+ * @returns {Promise<string>} - TOML [site] section or empty string
33
+ */
34
+ async generate(context) {
35
+ this.setContext(context);
36
+
37
+ // Support both old and new calling conventions
38
+ // Old: { serviceType: 'static-site', siteConfig: {...} }
39
+ // New: { coreInputs: { serviceType: 'static-site' }, siteConfig: {...} }
40
+ const serviceType = context.coreInputs?.serviceType || context.serviceType;
41
+ const siteConfig = context.siteConfig || {};
42
+
43
+ // Handle undefined/null serviceType gracefully (like original)
44
+ if (!serviceType) {
45
+ this.log('No service type provided - skipping [site] config');
46
+ return '';
47
+ }
48
+
49
+ // Only generate for static-site template
50
+ if (serviceType !== 'static-site') {
51
+ this.log('Skipping [site] config - not a static-site service');
52
+ return '';
53
+ }
54
+ return this.buildSiteConfig(siteConfig);
55
+ }
56
+
57
+ /**
58
+ * Build the [site] configuration section
59
+ * @param {Object} siteConfig - Site configuration overrides
60
+ * @returns {string} - TOML [site] section
61
+ */
62
+ buildSiteConfig(siteConfig = {}) {
63
+ const bucket = siteConfig.bucket || './public';
64
+ const include = siteConfig.include || ['**/*'];
65
+ const exclude = siteConfig.exclude || this.getDefaultExcludes();
66
+
67
+ // Build TOML [site] section
68
+ const lines = ['', '# Workers Sites configuration', '# Serves static assets from the bucket directory', '[site]', `bucket = "${bucket}"`, `include = ${JSON.stringify(include)}`, `exclude = ${JSON.stringify(exclude)}`, ''];
69
+ return lines.join('\n');
70
+ }
71
+
72
+ /**
73
+ * Get default file exclusions for Workers Sites
74
+ * @returns {string[]} - Array of glob patterns to exclude
75
+ */
76
+ getDefaultExcludes() {
77
+ return ['node_modules/**', '.git/**', '.*', '*.md', '.env*', 'secrets/**', 'wrangler.toml', 'package.json', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'];
78
+ }
79
+
80
+ /**
81
+ * Determine if this generator should run
82
+ *
83
+ * Supports both calling conventions for backward compatibility.
84
+ *
85
+ * @param {Object} context - Generation context
86
+ * @returns {boolean} - True if service type is static-site
87
+ */
88
+ shouldGenerate(context) {
89
+ const serviceType = context?.coreInputs?.serviceType || context?.serviceType;
90
+ return serviceType === 'static-site';
91
+ }
92
+
93
+ /**
94
+ * Validate site configuration
95
+ * @param {Object} siteConfig - Site config to validate
96
+ * @returns {Object} - Validation result { valid: boolean, errors: string[] }
97
+ */
98
+ validateSiteConfig(siteConfig) {
99
+ const errors = [];
100
+ if (siteConfig.bucket && typeof siteConfig.bucket !== 'string') {
101
+ errors.push('bucket must be a string path');
102
+ }
103
+ if (siteConfig.include && !Array.isArray(siteConfig.include)) {
104
+ errors.push('include must be an array of glob patterns');
105
+ }
106
+ if (siteConfig.exclude && !Array.isArray(siteConfig.exclude)) {
107
+ errors.push('exclude must be an array of glob patterns');
108
+ }
109
+ return {
110
+ valid: errors.length === 0,
111
+ errors
112
+ };
113
+ }
114
+ }
115
+ export default SiteConfigGenerator;