@tamyla/clodo-framework 4.4.0 → 4.5.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 (36) hide show
  1. package/CHANGELOG.md +2 -1844
  2. package/README.md +44 -18
  3. package/dist/cli/commands/add.js +325 -0
  4. package/dist/config/service-schema-config.js +98 -5
  5. package/dist/index.js +22 -3
  6. package/dist/middleware/Composer.js +2 -1
  7. package/dist/middleware/factories.js +445 -0
  8. package/dist/middleware/index.js +4 -1
  9. package/dist/modules/ModuleManager.js +6 -2
  10. package/dist/routing/EnhancedRouter.js +248 -44
  11. package/dist/routing/RequestContext.js +393 -0
  12. package/dist/schema/SchemaManager.js +6 -2
  13. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +79 -223
  14. package/dist/service-management/generators/code/WorkerIndexGenerator.js +241 -98
  15. package/dist/service-management/generators/config/WranglerTomlGenerator.js +130 -89
  16. package/dist/simple-api.js +4 -4
  17. package/dist/utilities/index.js +134 -1
  18. package/dist/utils/config/environment-var-normalizer.js +233 -0
  19. package/dist/validation/environmentGuard.js +172 -0
  20. package/docs/CHANGELOG.md +1877 -0
  21. package/docs/api-reference.md +153 -0
  22. package/package.json +4 -1
  23. package/scripts/repro-clodo.js +123 -0
  24. package/templates/ai-worker/package.json +19 -0
  25. package/templates/ai-worker/src/index.js +160 -0
  26. package/templates/cron-worker/package.json +19 -0
  27. package/templates/cron-worker/src/index.js +211 -0
  28. package/templates/edge-proxy/package.json +18 -0
  29. package/templates/edge-proxy/src/index.js +150 -0
  30. package/templates/minimal/package.json +17 -0
  31. package/templates/minimal/src/index.js +40 -0
  32. package/templates/queue-processor/package.json +19 -0
  33. package/templates/queue-processor/src/index.js +213 -0
  34. package/templates/rest-api/.dev.vars +2 -0
  35. package/templates/rest-api/package.json +19 -0
  36. package/templates/rest-api/src/index.js +124 -0
@@ -59,121 +59,264 @@ export class WorkerIndexGenerator extends BaseGenerator {
59
59
 
60
60
  /**
61
61
  * Build the complete worker index.js content
62
+ *
63
+ * v4.4.1+: Generates code that imports directly from @tamyla/clodo-framework
64
+ * using EnhancedRouter, composeMiddleware, createEnvironmentGuard, and
65
+ * middleware factories — NOT the old MiddlewareRegistry/Composer pattern.
62
66
  */
63
67
  _buildWorkerIndex(coreInputs, confirmedValues) {
68
+ const features = confirmedValues.features || {};
69
+ const serviceName = coreInputs.serviceName;
70
+ const serviceType = coreInputs.serviceType;
71
+
72
+ // Determine which env bindings are required based on features
73
+ const requiredBindings = [];
74
+ const optionalBindings = ['DEBUG'];
75
+ if (features.d1 || features.database) requiredBindings.push('DB');
76
+ if (features.kv || features.upstash) requiredBindings.push('KV');
77
+ if (features.r2) requiredBindings.push('R2_STORAGE');
78
+ if (features.ai) requiredBindings.push('AI');
79
+ if (features.vectorize) requiredBindings.push('VECTORIZE_INDEX');
80
+ if (features.queues) requiredBindings.push('QUEUE');
81
+ if (features.email) requiredBindings.push('EMAIL');
82
+ if (features.hyperdrive) requiredBindings.push('HYPERDRIVE');
83
+ if (features.durableObject || features.durableObjects) requiredBindings.push('DURABLE_OBJECT');
84
+
85
+ // Build framework imports
86
+ const frameworkImports = ['createEnhancedRouter', 'createCorsMiddleware', 'createErrorHandler', 'createLogger', 'composeMiddleware', 'createEnvironmentGuard'];
87
+ if (features.rateLimiting) frameworkImports.push('createRateLimitGuard');
88
+
89
+ // Build utility imports based on features
90
+ const utilityImports = [];
91
+ if (features.kv || features.upstash) utilityImports.push('getKV', 'putKV', 'listKV', 'deleteKV');
92
+ if (features.r2) utilityImports.push('putR2Object', 'getR2Object');
93
+ if (features.ai) utilityImports.push('runAIModel', 'streamAIResponse');
94
+
95
+ // Build route stubs based on service type
96
+ const routeStubs = this._buildRouteStubs(coreInputs, confirmedValues, features);
97
+
98
+ // Build queue handler if queues feature is enabled
99
+ const queueHandler = features.queues ? this._buildQueueHandler(serviceName) : '';
100
+
101
+ // Build cron handler if cron feature is enabled
102
+ const cronHandler = features.cron ? this._buildCronHandler(serviceName) : '';
64
103
  return `/**
65
104
  * ${confirmedValues.displayName} - Cloudflare Worker
66
105
  *
67
- * Generated by Clodo Framework GenerationEngine
68
- * Service Type: ${coreInputs.serviceType}
106
+ * Generated by Clodo Framework v4.4.1
107
+ * Service Type: ${serviceType}
69
108
  */
70
109
 
71
- import { domains } from '../config/domains.js';
72
- import { createServiceHandlers } from '../handlers/service-handlers.js';
73
- import { MiddlewareRegistry, MiddlewareComposer } from '../middleware/runtime.js';
74
- import * as Shared from '../middleware/shared/index.js';
110
+ import {
111
+ ${frameworkImports.join(',\n ')}
112
+ } from '@tamyla/clodo-framework';
113
+ ${utilityImports.length > 0 ? `\nimport { ${utilityImports.join(', ')} } from '@tamyla/clodo-framework/utilities';\n` : ''}
114
+ // ── Environment validation ────────────────────────────────────────────
115
+ const envGuard = createEnvironmentGuard({
116
+ required: [${requiredBindings.map(b => `'${b}'`).join(', ')}],
117
+ optional: [${optionalBindings.map(b => `'${b}'`).join(', ')}]
118
+ });
119
+
120
+ // ── Middleware stack ──────────────────────────────────────────────────
121
+ const middleware = composeMiddleware(
122
+ createCorsMiddleware({ origins: ['*'] }),
123
+ createLogger({ prefix: '${serviceName}', level: 'info' }),${features.rateLimiting ? `\n createRateLimitGuard({ maxRequests: 100, windowMs: 60000 }),` : ''}
124
+ createErrorHandler({ includeStack: false })
125
+ );
75
126
 
127
+ // ── Router ────────────────────────────────────────────────────────────
128
+ const router = createEnhancedRouter(${features.d1 || features.database ? 'null /* D1 injected per-request via env.DB */' : 'null'}, {
129
+ autoRegisterGenericRoutes: false
130
+ });
131
+
132
+ // Health check
133
+ router.get('${confirmedValues.healthCheckPath || '/health'}', (c) => {
134
+ return c.json({
135
+ status: 'healthy',
136
+ service: '${serviceName}',
137
+ version: '${confirmedValues.version}',
138
+ timestamp: new Date().toISOString()
139
+ });
140
+ });
141
+
142
+ ${routeStubs}
143
+
144
+ // ── Worker entry point ────────────────────────────────────────────────
76
145
  export default {
77
146
  async fetch(request, env, ctx) {
78
- try {
79
- // Get service configuration
80
- const serviceConfig = domains['${coreInputs.serviceName}'];
147
+ envGuard.check(env);
148
+ const url = new URL(request.url);
81
149
 
82
- // Build shared middleware instances
83
- const sharedMiddlewares = [
84
- Shared.cors({ origin: serviceConfig.corsPolicy || '*' }),
85
- Shared.logging({ level: serviceConfig.logLevel || 'info' })
86
- ];
150
+ return middleware.execute(request, () =>
151
+ router.handleRequest(request.method, url.pathname, request, env, ctx)
152
+ );
153
+ }${queueHandler}${cronHandler}
154
+ };
155
+ `;
156
+ }
87
157
 
88
- // Lazy-load service middleware and support legacy factory compatibility
89
- let serviceMiddlewareInstance = null;
90
- let legacyFactory = null;
158
+ /**
159
+ * Generate route stubs based on service type and features
160
+ */
161
+ _buildRouteStubs(coreInputs, confirmedValues, features) {
162
+ const apiBase = confirmedValues.apiBasePath || '/api/v1';
163
+ const serviceType = coreInputs.serviceType;
91
164
 
92
- try {
93
- const mod = await import('../middleware/service-middleware.js');
94
-
95
- if (mod?.registerMiddleware) {
96
- // New-style registration helper
97
- mod.registerMiddleware(MiddlewareRegistry, serviceConfig.name);
98
- serviceMiddlewareInstance = MiddlewareRegistry.get(serviceConfig.name);
99
- } else if (mod?.default) {
100
- const def = mod.default;
101
- // If the default is a class (constructor), instantiate and register
102
- if (typeof def === 'function' && def.prototype) {
103
- try {
104
- const instance = new def();
105
- MiddlewareRegistry.register(serviceConfig.name, instance);
106
- serviceMiddlewareInstance = instance;
107
- } catch (e) {
108
- // ignore instantiation errors
109
- }
110
- } else if (typeof def === 'function') {
111
- // Legacy factory exported as default
112
- legacyFactory = def;
113
- }
114
- } else if (mod?.createServiceMiddleware) {
115
- legacyFactory = mod.createServiceMiddleware;
116
- }
117
- } catch (e) {
118
- // No service-specific middleware found - continue with shared only
119
- }
165
+ // REST API routes with KV
166
+ if ((features.kv || features.upstash) && !features.d1) {
167
+ return `// ── API Routes (KV-backed) ─────────────────────────────────────────────
120
168
 
121
- // Compose final middleware chain
122
- let chain;
123
-
124
- if (legacyFactory) {
125
- const legacyInstance = legacyFactory(serviceConfig, env);
126
- const adapter = {
127
- preprocess: async (req) => {
128
- if (legacyInstance && typeof legacyInstance.processRequest === 'function') {
129
- const processed = await legacyInstance.processRequest(req);
130
- if (processed instanceof Response) return processed; // short-circuit
131
- return null; // continue (legacy returns a Request)
132
- }
133
- return null;
134
- },
135
- postprocess: async (res) => {
136
- if (legacyInstance && typeof legacyInstance.processResponse === 'function') {
137
- const r = await legacyInstance.processResponse(res);
138
- return r instanceof Response ? r : res;
139
- }
140
- return res;
141
- }
142
- };
143
-
144
- chain = MiddlewareComposer.compose(...sharedMiddlewares, adapter);
145
- } else {
146
- const svcMw = serviceMiddlewareInstance || MiddlewareRegistry.get(serviceConfig.name);
147
- chain = MiddlewareComposer.compose(...sharedMiddlewares, svcMw);
148
- }
169
+ router.get('${apiBase}/items', async (c) => {
170
+ const list = await listKV(c.env.KV, { prefix: 'item:' });
171
+ const items = await Promise.all(
172
+ (list.keys || []).map(async (k) => getKV(c.env.KV, k.name, { type: 'json' }))
173
+ );
174
+ return c.json({ items: items.filter(Boolean), count: items.length });
175
+ });
176
+
177
+ router.get('${apiBase}/items/:id', async (c) => {
178
+ const id = c.req.param('id');
179
+ const item = await getKV(c.env.KV, \`item:\${id}\`, { type: 'json' });
180
+ if (!item) return c.json({ error: 'Not found' }, 404);
181
+ return c.json(item);
182
+ });
149
183
 
150
- // Execute middleware chain with final handler
151
- const response = await chain.execute(request, async (req) => {
152
- const handlers = createServiceHandlers(serviceConfig, env);
153
- return handlers.handleRequest(req, ctx);
154
- });
155
-
156
- return response;
157
-
158
- } catch (error) {
159
- console.error('Worker error:', error);
160
-
161
- return new Response(JSON.stringify({
162
- error: 'Internal Server Error',
163
- message: error.message,
164
- timestamp: new Date().toISOString()
165
- }), {
166
- status: 500,
167
- headers: {
168
- 'Content-Type': 'application/json',
169
- 'X-Service': '${coreInputs.serviceName}',
170
- 'X-Version': '${confirmedValues.version}'
171
- }
172
- });
184
+ router.post('${apiBase}/items', async (c) => {
185
+ const body = await c.req.json();
186
+ const id = crypto.randomUUID();
187
+ const item = { id, ...body, createdAt: new Date().toISOString() };
188
+ await putKV(c.env.KV, \`item:\${id}\`, JSON.stringify(item));
189
+ return c.json(item, 201);
190
+ });
191
+
192
+ router.put('${apiBase}/items/:id', async (c) => {
193
+ const id = c.req.param('id');
194
+ const existing = await getKV(c.env.KV, \`item:\${id}\`, { type: 'json' });
195
+ if (!existing) return c.json({ error: 'Not found' }, 404);
196
+ const body = await c.req.json();
197
+ const updated = { ...existing, ...body, id, updatedAt: new Date().toISOString() };
198
+ await putKV(c.env.KV, \`item:\${id}\`, JSON.stringify(updated));
199
+ return c.json(updated);
200
+ });
201
+
202
+ router.delete('${apiBase}/items/:id', async (c) => {
203
+ const id = c.req.param('id');
204
+ await deleteKV(c.env.KV, \`item:\${id}\`);
205
+ return c.json({ deleted: true, id });
206
+ });`;
173
207
  }
208
+
209
+ // D1-backed routes
210
+ if (features.d1 || features.database) {
211
+ return `// ── API Routes (D1-backed) ─────────────────────────────────────────────
212
+
213
+ router.get('${apiBase}/items', async (c) => {
214
+ const { results } = await c.env.DB.prepare('SELECT * FROM items ORDER BY created_at DESC LIMIT 50').all();
215
+ return c.json({ items: results, count: results.length });
216
+ });
217
+
218
+ router.get('${apiBase}/items/:id', async (c) => {
219
+ const id = c.req.param('id');
220
+ const item = await c.env.DB.prepare('SELECT * FROM items WHERE id = ?').bind(id).first();
221
+ if (!item) return c.json({ error: 'Not found' }, 404);
222
+ return c.json(item);
223
+ });
224
+
225
+ router.post('${apiBase}/items', async (c) => {
226
+ const body = await c.req.json();
227
+ const id = crypto.randomUUID();
228
+ await c.env.DB.prepare('INSERT INTO items (id, name, data, created_at) VALUES (?, ?, ?, ?)')
229
+ .bind(id, body.name, JSON.stringify(body.data || {}), new Date().toISOString())
230
+ .run();
231
+ return c.json({ id, ...body, createdAt: new Date().toISOString() }, 201);
232
+ });
233
+
234
+ router.delete('${apiBase}/items/:id', async (c) => {
235
+ const id = c.req.param('id');
236
+ await c.env.DB.prepare('DELETE FROM items WHERE id = ?').bind(id).run();
237
+ return c.json({ deleted: true, id });
238
+ });`;
239
+ }
240
+
241
+ // AI worker routes
242
+ if (features.ai) {
243
+ return `// ── AI Routes ─────────────────────────────────────────────────────────
244
+
245
+ router.post('${apiBase}/chat', async (c) => {
246
+ const { messages, model } = await c.req.json();
247
+ const result = await runAIModel(c.env.AI, model || '@cf/meta/llama-3.1-8b-instruct', {
248
+ messages: messages || []
249
+ });
250
+ return c.json({ response: result.response });
251
+ });
252
+
253
+ router.post('${apiBase}/chat/stream', async (c) => {
254
+ const { messages, model } = await c.req.json();
255
+ const stream = await runAIModel(c.env.AI, model || '@cf/meta/llama-3.1-8b-instruct', {
256
+ messages: messages || [], stream: true
257
+ });
258
+ return streamAIResponse(stream);
259
+ });
260
+
261
+ router.get('${apiBase}/models', (c) => {
262
+ return c.json({
263
+ models: [
264
+ { name: 'llama-3.1-8b-instruct', type: 'chat' },
265
+ { name: 'bge-base-en-v1.5', type: 'embeddings' }
266
+ ]
267
+ });
268
+ });`;
269
+ }
270
+
271
+ // Default minimal routes
272
+ return `// ── API Routes ─────────────────────────────────────────────────────────
273
+
274
+ router.get('${apiBase}/status', (c) => {
275
+ return c.json({
276
+ service: '${coreInputs.serviceName}',
277
+ status: 'running',
278
+ timestamp: new Date().toISOString()
279
+ });
280
+ });
281
+
282
+ // Add your routes here:
283
+ // router.get('${apiBase}/items', async (c) => { ... });
284
+ // router.post('${apiBase}/items', async (c) => { ... });`;
174
285
  }
175
- };
176
- `;
286
+
287
+ /**
288
+ * Build queue consumer handler
289
+ */
290
+ _buildQueueHandler(serviceName) {
291
+ return `,
292
+
293
+ // Queue consumer handler
294
+ async queue(batch, env, ctx) {
295
+ console.log(\`[${serviceName}] Processing batch of \${batch.messages.length} messages\`);
296
+ for (const message of batch.messages) {
297
+ try {
298
+ console.log('Processing:', message.body);
299
+ // Add your message processing logic here
300
+ message.ack();
301
+ } catch (error) {
302
+ console.error('Message processing failed:', error.message);
303
+ message.retry();
304
+ }
305
+ }
306
+ }`;
307
+ }
308
+
309
+ /**
310
+ * Build cron/scheduled handler
311
+ */
312
+ _buildCronHandler(serviceName) {
313
+ return `,
314
+
315
+ // Scheduled (cron) handler
316
+ async scheduled(event, env, ctx) {
317
+ console.log(\`[${serviceName}] Cron triggered: \${event.cron} at \${new Date(event.scheduledTime).toISOString()}\`);
318
+ // Add your scheduled job logic here
319
+ }`;
177
320
  }
178
321
 
179
322
  /**
@@ -1,23 +1,18 @@
1
1
  import { BaseGenerator } from '../BaseGenerator.js';
2
- import { WranglerCompatibilityDetector } from '../../../lib/shared/utils/wrangler-compatibility.js';
3
2
  import { join } from 'path';
4
3
 
5
4
  /**
6
- * WranglerTomlGenerator
5
+ * WranglerTomlGenerator (v4.4.1+)
7
6
  *
8
7
  * Generates wrangler.toml configuration files for Cloudflare Workers.
9
8
  *
10
- * Responsibilities:
11
- * - Generate main worker configuration (name, main, compatibility)
12
- * - Include routes configuration from RouteGenerator
13
- * - Include Workers Sites configuration from SiteConfigGenerator
14
- * - Configure environment-specific settings (dev/staging/production)
15
- * - Configure D1 database bindings
16
- * - Configure environment variables and feature flags
17
- *
18
- * Dependencies:
19
- * - RouteGenerator (for routes configuration)
20
- * - SiteConfigGenerator (for Workers Sites configuration)
9
+ * v4.4.1 changes:
10
+ * - Supports ALL modern Cloudflare bindings: D1, KV, R2, AI, Vectorize,
11
+ * Queues, Email, Hyperdrive, Browser, Durable Objects, Analytics Engine
12
+ * - Uses RECOMMENDED_COMPATIBILITY_DATE from framework
13
+ * - Generates empty IDs for auto-provisioning in local dev (Wrangler 3.91+)
14
+ * - Includes [observability] defaults
15
+ * - Clean comments explaining each binding
21
16
  */
22
17
  export class WranglerTomlGenerator extends BaseGenerator {
23
18
  constructor(options = {}) {
@@ -114,109 +109,155 @@ export class WranglerTomlGenerator extends BaseGenerator {
114
109
  }
115
110
 
116
111
  /**
117
- * Build the complete wrangler.toml content
112
+ * Build the complete wrangler.toml content (v4.4.1+)
113
+ * Supports all modern Cloudflare bindings.
118
114
  */
119
115
  async _buildWranglerToml(coreInputs, confirmedValues, routesConfig, siteConfig) {
120
- const compatDate = new Date().toISOString().split('T')[0];
116
+ const compatDate = '2024-12-01'; // Framework recommended compatibility date
121
117
 
122
- // Detect Wrangler version and get optimal compatibility config
123
- let compatibilityString = 'compatibility_flags = ["nodejs_compat"]';
124
- let buildString = '\n\n[build]\ncommand = "npm run build"\n\n[build.upload]\nformat = "modules"\n\n[build.upload.external]\ninclude = ["node:*"]';
125
- try {
126
- const compatibilityDetector = new WranglerCompatibilityDetector();
127
- const wranglerVersion = await Promise.race([compatibilityDetector.detectVersion(), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 2000)) // 2 second timeout
128
- ]);
129
- const compatibilityConfig = compatibilityDetector.getOptimalConfig(wranglerVersion);
130
- const buildConfig = compatibilityDetector.getBuildConfig(wranglerVersion);
118
+ const features = confirmedValues.features || {};
119
+ const workerName = confirmedValues.workerName || coreInputs.serviceName;
131
120
 
132
- // Build compatibility flags string
133
- if (compatibilityConfig.nodejs_compat !== undefined) {
134
- compatibilityString = `nodejs_compat = ${compatibilityConfig.nodejs_compat}`;
135
- } else if (compatibilityConfig.compatibility_flags) {
136
- compatibilityString = `compatibility_flags = ${JSON.stringify(compatibilityConfig.compatibility_flags)}`;
137
- }
121
+ // ── Binding blocks based on features ──────────────────────────────
138
122
 
139
- // Add any additional build config from detector (if needed)
140
- if (buildConfig.command && buildConfig.command !== "npm run build") {
141
- buildString += `\n\n# Additional build config from Wrangler ${wranglerVersion}\n[build.v${wranglerVersion.split('.')[0]}]\ncommand = "${buildConfig.command}"`;
142
- }
143
- if (buildConfig.upload && buildConfig.upload.format && buildConfig.upload.format !== "modules") {
144
- buildString += `\nformat_override = "${buildConfig.upload.format}"`;
145
- }
146
- } catch (error) {
147
- // Fall back to default configuration if detection fails
148
- console.warn(`⚠️ Wrangler compatibility detection failed, using defaults: ${error.message}`);
149
- }
150
-
151
- // Only include D1/kv/r2 bindings when the confirmed values indicate features are enabled
152
- const d1Block = confirmedValues.features && confirmedValues.features.d1 ? `
153
-
154
- # Database bindings
123
+ const bindingBlocks = [];
124
+ if (features.d1 || features.database) {
125
+ bindingBlocks.push(`
126
+ # ═══ D1 Database ═══════════════════════════════════════════════════
127
+ # Local: Leave database_id empty for auto-provisioned SQLite
128
+ # Production: Run \`wrangler d1 create ${confirmedValues.databaseName || workerName + '-db'}\` and paste ID
155
129
  [[d1_databases]]
156
130
  binding = "DB"
157
- database_name = "${confirmedValues.databaseName}"
158
- database_id = "" # To be configured during setup
159
- ` : '';
160
-
161
- // Support both generic KV flag and provider-specific flag (upstash)
162
- const kvEnabled = confirmedValues.features && (confirmedValues.features.kv || confirmedValues.features.upstash);
163
- const kvBlock = kvEnabled ? `
164
-
165
- # KV namespaces
131
+ database_name = "${confirmedValues.databaseName || workerName + '-db'}"
132
+ database_id = ""`);
133
+ }
134
+ if (features.kv || features.upstash) {
135
+ bindingBlocks.push(`
136
+ # ═══ KV Namespace ══════════════════════════════════════════════════
137
+ # Local: Leave id empty for auto-provisioned local KV
138
+ # Production: Run \`wrangler kv namespace create KV\` and paste ID
166
139
  [[kv_namespaces]]
167
140
  binding = "KV"
168
- namespace_id = "" # To be configured during setup
169
- ` : '';
170
- const r2Block = confirmedValues.features && confirmedValues.features.r2 ? `
171
-
172
- # R2 buckets
141
+ id = ""`);
142
+ }
143
+ if (features.r2) {
144
+ bindingBlocks.push(`
145
+ # ═══ R2 Object Storage ═════════════════════════════════════════════
146
+ # Production: Run \`wrangler r2 bucket create ${workerName}-storage\`
173
147
  [[r2_buckets]]
174
148
  binding = "R2_STORAGE"
175
- bucket_name = "${confirmedValues.bucketName || confirmedValues.databaseName || ''}"
176
- bucket_id = "" # To be configured during setup
149
+ bucket_name = "${confirmedValues.bucketName || workerName + '-storage'}"`);
150
+ }
151
+ if (features.ai) {
152
+ bindingBlocks.push(`
153
+ # ═══ Workers AI ════════════════════════════════════════════════════
154
+ [ai]
155
+ binding = "AI"`);
156
+ }
157
+ if (features.vectorize) {
158
+ bindingBlocks.push(`
159
+ # ═══ Vectorize (Vector Database) ═══════════════════════════════════
160
+ # Production: Run \`wrangler vectorize create ${workerName}-index --dimensions 768 --metric cosine\`
161
+ [[vectorize]]
162
+ binding = "VECTORIZE_INDEX"
163
+ index_name = "${workerName}-index"`);
164
+ }
165
+ if (features.queues) {
166
+ bindingBlocks.push(`
167
+ # ═══ Queues ════════════════════════════════════════════════════════
168
+ [[queues.producers]]
169
+ binding = "QUEUE"
170
+ queue = "${workerName}-queue"
171
+
172
+ [[queues.consumers]]
173
+ queue = "${workerName}-queue"
174
+ max_batch_size = 10
175
+ max_batch_timeout = 30
176
+ max_retries = 3
177
+ dead_letter_queue = "${workerName}-queue-dlq"`);
178
+ }
179
+ if (features.email) {
180
+ bindingBlocks.push(`
181
+ # ═══ Email Workers ═════════════════════════════════════════════════
182
+ [send_email]
183
+ binding = "EMAIL"`);
184
+ }
185
+ if (features.hyperdrive) {
186
+ bindingBlocks.push(`
187
+ # ═══ Hyperdrive (Postgres Connection Pool) ═════════════════════════
188
+ # Production: Run \`wrangler hyperdrive create ${workerName}-db --connection-string "postgres://..."\`
189
+ [[hyperdrive]]
190
+ binding = "HYPERDRIVE"
191
+ id = ""`);
192
+ }
193
+ if (features.browser) {
194
+ bindingBlocks.push(`
195
+ # ═══ Browser Rendering ═════════════════════════════════════════════
196
+ [browser]
197
+ binding = "BROWSER"`);
198
+ }
199
+ if (features.durableObject || features.durableObjects) {
200
+ bindingBlocks.push(`
201
+ # ═══ Durable Objects ══════════════════════════════════════════════
202
+ [[durable_objects.bindings]]
203
+ name = "DURABLE_OBJECT"
204
+ class_name = "MyDurableObject"
205
+
206
+ [[migrations]]
207
+ tag = "v1"
208
+ new_classes = ["MyDurableObject"]`);
209
+ }
210
+ if (features.analytics) {
211
+ bindingBlocks.push(`
212
+ # ═══ Analytics Engine ══════════════════════════════════════════════
213
+ [[analytics_engine_datasets]]
214
+ binding = "ANALYTICS"
215
+ dataset = "${workerName}-analytics"`);
216
+ }
217
+
218
+ // ── Cron triggers ─────────────────────────────────────────────────
219
+ const cronBlock = features.cron ? `
220
+ # ═══ Cron Triggers ═════════════════════════════════════════════════
221
+ [triggers]
222
+ crons = ["*/5 * * * *"] # Every 5 minutes — customize as needed
177
223
  ` : '';
178
- return `# Cloudflare Workers Configuration for ${confirmedValues.displayName}
179
- name = "${confirmedValues.workerName}"
224
+ return `# ═══════════════════════════════════════════════════════════════════
225
+ # ${confirmedValues.displayName} — Cloudflare Worker Configuration
226
+ # Generated by Clodo Framework v4.4.1
227
+ # ═══════════════════════════════════════════════════════════════════
228
+ name = "${workerName}"
180
229
  main = "src/worker/index.js"
181
230
  compatibility_date = "${compatDate}"
182
- ${compatibilityString}${buildString}
231
+ compatibility_flags = ["nodejs_compat"]
183
232
 
184
233
  # Account configuration
185
- account_id = "${coreInputs.cloudflareAccountId}"
234
+ account_id = "${coreInputs.cloudflareAccountId || ''}"
186
235
 
187
- ${routesConfig}
188
- ${siteConfig ? '\n' + siteConfig : ''}
189
-
190
- # Environment configurations
236
+ ${routesConfig}${siteConfig ? '\n' + siteConfig : ''}
237
+ # ═══ Environment configurations ════════════════════════════════════
191
238
  [env.development]
192
- name = "${confirmedValues.workerName}-dev"
239
+ name = "${workerName}-dev"
193
240
 
194
241
  [env.staging]
195
- name = "${confirmedValues.workerName}-staging"
242
+ name = "${workerName}-staging"
196
243
 
197
244
  [env.production]
198
- name = "${confirmedValues.workerName}"
199
-
200
- ${d1Block}${kvBlock}${r2Block}
201
- # Environment variables
245
+ name = "${workerName}"
246
+ ${bindingBlocks.join('\n')}
247
+ ${cronBlock}
248
+ # ═══ Environment variables ═════════════════════════════════════════
202
249
  [vars]
203
250
  SERVICE_NAME = "${coreInputs.serviceName}"
204
251
  SERVICE_TYPE = "${coreInputs.serviceType}"
205
- DOMAIN_NAME = "${coreInputs.domainName}"
206
- ENVIRONMENT = "${coreInputs.environment}"
207
- API_BASE_PATH = "${confirmedValues.apiBasePath}"
208
- HEALTH_CHECK_PATH = "${confirmedValues.healthCheckPath}"
209
-
210
- # Domain-specific variables
211
- PRODUCTION_URL = "${confirmedValues.productionUrl}"
212
- STAGING_URL = "${confirmedValues.stagingUrl}"
213
- DEVELOPMENT_URL = "${confirmedValues.developmentUrl}"
214
252
 
215
- # Feature flags
216
- ${this._generateFeatureFlags(confirmedValues.features)}
253
+ # ═══ Observability ═════════════════════════════════════════════════
254
+ [observability]
255
+ enabled = true
217
256
 
218
- # Custom environment variables (configure as needed)
219
- # CUSTOM_VAR = "value"
257
+ [observability.logs]
258
+ enabled = true
259
+ invocation_logs = true
260
+ # head_sampling_rate = 0.01 # Uncomment in production
220
261
  `;
221
262
  }
222
263
 
@@ -108,10 +108,10 @@ export class Clodo {
108
108
  */
109
109
  static getInfo() {
110
110
  return {
111
- name: 'Clodo Framework',
112
- version: '3.1.23',
113
- description: 'Unified service management framework for Cloudflare Workers',
114
- features: ['Service Creation', 'Multi-Domain Deployment', 'Configuration Management', 'Validation & Diagnostics', 'Worker Integration']
111
+ name: '@tamyla/clodo-framework',
112
+ version: '4.4.1',
113
+ description: 'Batteries-included framework for Cloudflare Workers — routing, middleware, AI, KV, R2, Vectorize, Queues & more',
114
+ features: ['Enhanced Router (Express/Hono-style)', 'Middleware Composition', 'Workers AI Integration', 'KV / R2 / D1 Storage', 'Vectorize (Vector DB)', 'Queues (Producer/Consumer)', 'Cron / Scheduled Handlers', 'Durable Objects', 'Email Workers', 'Service Bindings', 'Analytics Engine', 'Health Checks & Monitoring', 'Feature Flags', 'Rate Limiting', 'Environment Validation', 'Service Creation & Deployment', 'Multi-Domain Orchestration']
115
115
  };
116
116
  }
117
117
  }