@tamyla/clodo-framework 3.2.0 → 3.2.1

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 (27) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cli/commands/deploy.js +58 -20
  3. package/dist/config/.env.staging.example +25 -0
  4. package/dist/deployment/index.js +2 -2
  5. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +1 -1
  6. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +2 -4
  7. package/dist/lib/deployment/orchestration/BaseDeploymentOrchestrator.js +1 -1
  8. package/dist/lib/shared/cloudflare/ops.js +103 -17
  9. package/dist/lib/shared/deployment/credential-collector.js +134 -1
  10. package/dist/lib/shared/deployment/index.js +7 -3
  11. package/dist/lib/shared/deployment/rollback-manager.js +1 -1
  12. package/dist/lib/shared/deployment/utilities/d1-error-recovery.js +1 -1
  13. package/dist/lib/shared/deployment/workflows/index.js +13 -0
  14. package/dist/lib/shared/deployment/workflows/interactive-confirmation.js +1 -1
  15. package/dist/lib/shared/deployment/workflows/interactive-deployment-coordinator.js +229 -0
  16. package/dist/lib/shared/deployment/workflows/interactive-domain-info-gatherer.js +1 -1
  17. package/dist/lib/shared/deployment/workflows/interactive-secret-workflow.js +3 -3
  18. package/dist/lib/shared/deployment/workflows/interactive-testing-workflow.js +2 -2
  19. package/dist/lib/shared/deployment/workflows/interactive-validation.js +2 -2
  20. package/dist/lib/shared/monitoring/health-checker.js +11 -5
  21. package/dist/lib/shared/validation/ValidationRegistry.js +1 -1
  22. package/dist/utils/deployment/index.js +2 -1
  23. package/dist/utils/health-checker.js +53 -76
  24. package/dist/utils/index.js +3 -0
  25. package/package.json +2 -2
  26. package/templates/generic/src/worker/index.js +1 -1
  27. package/templates/static-site/src/worker/index.js +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [3.2.1](https://github.com/tamylaa/clodo-framework/compare/v3.2.0...v3.2.1) (2025-12-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * enhance deploy command with prerequisite checking and organize project structure ([660c5ab](https://github.com/tamylaa/clodo-framework/commit/660c5ab82546ca1a80b42c1d4cbc6d3972cc6203))
7
+
1
8
  # [3.2.0](https://github.com/tamylaa/clodo-framework/compare/v3.1.27...v3.2.0) (2025-12-03)
2
9
 
3
10
 
@@ -2,10 +2,11 @@ import chalk from 'chalk';
2
2
  import { Clodo } from '@tamyla/clodo-framework';
3
3
  import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
4
4
  import { ConfigLoader } from '../../lib/shared/utils/config-loader.js';
5
+ import { InteractiveDeploymentCoordinator } from '../../lib/shared/deployment/workflows/interactive-deployment-coordinator.js';
5
6
  export function registerDeployCommand(program) {
6
- const command = program.command('deploy').description('Deploy a Clodo service with smart credential handling and domain selection')
7
+ const command = program.command('deploy').description('Deploy a Clodo service with interactive configuration and validation')
7
8
  // Cloudflare-specific options
8
- .option('--token <token>', 'Cloudflare API token').option('--account-id <id>', 'Cloudflare account ID').option('--zone-id <id>', 'Cloudflare zone ID').option('--domain <domain>', 'Specific domain to deploy to (otherwise prompted if multiple exist)').option('--environment <env>', 'Target environment (development, staging, production)', 'production').option('--development', 'Deploy to development environment (shorthand for --environment development)').option('--staging', 'Deploy to staging environment (shorthand for --environment staging)').option('--production', 'Deploy to production environment (shorthand for --environment production)').option('--dry-run', 'Simulate deployment without making changes').option('-y, --yes', 'Skip confirmation prompts (for CI/CD)').option('--service-path <path>', 'Path to service directory', '.');
9
+ .option('--token <token>', 'Cloudflare API token (or set CLOUDFLARE_API_TOKEN env var)').option('--account-id <id>', 'Cloudflare account ID (or set CLOUDFLARE_ACCOUNT_ID env var)').option('--zone-id <id>', 'Cloudflare zone ID (or set CLOUDFLARE_ZONE_ID env var)').option('--domain <domain>', 'Specific domain to deploy to').option('--environment <env>', 'Target environment (development, staging, production)', 'production').option('--development', 'Deploy to development environment (shorthand for --environment development)').option('--staging', 'Deploy to staging environment (shorthand for --environment staging)').option('--production', 'Deploy to production environment (shorthand for --environment production)').option('--dry-run', 'Simulate deployment without making changes').option('-y, --yes', 'Skip confirmation prompts (for CI/CD)').option('--service-path <path>', 'Path to service directory', '.').option('--check-prereqs', 'Check deployment prerequisites before starting').option('--check-auth', 'Check Wrangler authentication status').option('--check-network', 'Check network connectivity to Cloudflare');
9
10
 
10
11
  // Add standard options (--verbose, --quiet, --json, --no-color, --config-file)
11
12
  StandardOptions.define(command).action(async options => {
@@ -38,26 +39,63 @@ export function registerDeployCommand(program) {
38
39
  // Merge config file defaults with CLI options (CLI takes precedence)
39
40
  const mergedOptions = configLoader.merge(configFileData, options);
40
41
 
41
- // Use simple API for deployment
42
- const result = await Clodo.deploy({
43
- servicePath: mergedOptions.servicePath || '.',
44
- environment: mergedOptions.environment || 'production',
45
- domain: mergedOptions.domain,
46
- dryRun: mergedOptions.dryRun || false,
47
- credentials: {
48
- token: mergedOptions.token,
49
- accountId: mergedOptions.accountId,
50
- zoneId: mergedOptions.zoneId
51
- }
52
- });
53
- if (result.success) {
54
- output.success(result.message);
55
- if (result.deployedDomains && result.deployedDomains.length > 0) {
56
- output.info(`Deployed to domains: ${result.deployedDomains.join(', ')}`);
42
+ // Determine if interactive mode should be enabled
43
+ const interactive = !mergedOptions.nonInteractive && !mergedOptions.yes;
44
+ if (interactive) {
45
+ console.log(chalk.cyan('\nšŸš€ Interactive Clodo Service Deployment'));
46
+ console.log(chalk.gray('═'.repeat(60)));
47
+ console.log(chalk.white('Welcome to the interactive deployment wizard!\n'));
48
+
49
+ // Use the interactive deployment coordinator
50
+ const coordinator = new InteractiveDeploymentCoordinator({
51
+ servicePath: mergedOptions.servicePath || '.',
52
+ environment: mergedOptions.environment || 'production',
53
+ domain: mergedOptions.domain,
54
+ dryRun: mergedOptions.dryRun || false,
55
+ credentials: {
56
+ token: mergedOptions.token,
57
+ accountId: mergedOptions.accountId,
58
+ zoneId: mergedOptions.zoneId
59
+ },
60
+ checkPrereqs: mergedOptions.checkPrereqs,
61
+ checkAuth: mergedOptions.checkAuth,
62
+ checkNetwork: mergedOptions.checkNetwork,
63
+ verbose: mergedOptions.verbose,
64
+ quiet: mergedOptions.quiet,
65
+ json: mergedOptions.json
66
+ });
67
+ const result = await coordinator.runInteractiveDeployment();
68
+ if (result.success) {
69
+ output.success(result.message);
70
+ if (result.deployedDomains && result.deployedDomains.length > 0) {
71
+ output.info(`Deployed to domains: ${result.deployedDomains.join(', ')}`);
72
+ }
73
+ } else {
74
+ output.error('Interactive deployment failed');
75
+ process.exit(1);
57
76
  }
58
77
  } else {
59
- output.error('Deployment failed');
60
- process.exit(1);
78
+ // Use simple API for deployment (non-interactive/CI mode)
79
+ const result = await Clodo.deploy({
80
+ servicePath: mergedOptions.servicePath || '.',
81
+ environment: mergedOptions.environment || 'production',
82
+ domain: mergedOptions.domain,
83
+ dryRun: mergedOptions.dryRun || false,
84
+ credentials: {
85
+ token: mergedOptions.token,
86
+ accountId: mergedOptions.accountId,
87
+ zoneId: mergedOptions.zoneId
88
+ }
89
+ });
90
+ if (result.success) {
91
+ output.success(result.message);
92
+ if (result.deployedDomains && result.deployedDomains.length > 0) {
93
+ output.info(`Deployed to domains: ${result.deployedDomains.join(', ')}`);
94
+ }
95
+ } else {
96
+ output.error('Deployment failed');
97
+ process.exit(1);
98
+ }
61
99
  }
62
100
  } catch (error) {
63
101
  const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options || {});
@@ -0,0 +1,25 @@
1
+ # Staging Environment Configuration
2
+ # Copy this file to .env.staging and fill in your actual Cloudflare credentials
3
+ # DO NOT commit this file to version control
4
+
5
+ # Cloudflare API Credentials for Staging Environment
6
+ CLOUDFLARE_API_TOKEN=your_staging_cloudflare_api_token_here
7
+ CLOUDFLARE_ACCOUNT_ID=your_staging_cloudflare_account_id_here
8
+ CLOUDFLARE_ZONE_ID=your_staging_cloudflare_zone_id_here
9
+
10
+ # Staging Domain Configuration
11
+ STAGING_DOMAIN=staging.example.com
12
+ STAGING_ZONE_ID=${CLOUDFLARE_ZONE_ID}
13
+
14
+ # Database Configuration (if different from development)
15
+ STAGING_DATABASE_ID=your_staging_database_id_here
16
+ STAGING_DATABASE_NAME=api2-com-staging-db
17
+
18
+ # Monitoring and Alerting
19
+ STAGING_METRICS_ENDPOINT=https://metrics-staging.example.com
20
+ STAGING_ALERT_EMAIL=staging-alerts@example.com
21
+
22
+ # Security Settings
23
+ STAGING_WAF_ENABLED=true
24
+ STAGING_RATE_LIMIT=500
25
+ STAGING_CORS_ORIGINS=https://staging.example.com,https://app-staging.example.com
@@ -1,11 +1,11 @@
1
1
  // Deployment Module
2
2
  // Core deployment components for the Clodo Framework
3
3
 
4
- export { WranglerDeployer } from './wrangler-deployer.js';
4
+ // NOTE: WranglerDeployer has lib/ dependencies not available in npm distribution
5
+ // export { WranglerDeployer } from './wrangler-deployer.js';
5
6
 
6
7
  // Orchestration Framework - Public API for downstream consumers
7
8
  export { BaseDeploymentOrchestrator } from './orchestration/BaseDeploymentOrchestrator.js';
8
9
  export { SingleServiceOrchestrator } from './orchestration/SingleServiceOrchestrator.js';
9
10
  export { PortfolioOrchestrator } from './orchestration/PortfolioOrchestrator.js';
10
- export { EnterpriseOrchestrator } from './orchestration/EnterpriseOrchestrator.js';
11
11
  export { UnifiedDeploymentOrchestrator } from './orchestration/UnifiedDeploymentOrchestrator.js';
@@ -19,7 +19,7 @@
19
19
  * @abstract
20
20
  */
21
21
 
22
- import { ErrorHandler } from '../../shared/utils/ErrorHandler.js';
22
+ import { ErrorHandler } from '../../../lib/shared/utils/ErrorHandler.js';
23
23
 
24
24
  /**
25
25
  * Phase state and execution order
@@ -36,8 +36,7 @@
36
36
  import { BaseDeploymentOrchestrator } from './BaseDeploymentOrchestrator.js';
37
37
  import { SingleServiceOrchestrator } from './SingleServiceOrchestrator.js';
38
38
  import { PortfolioOrchestrator } from './PortfolioOrchestrator.js';
39
- import { EnterpriseOrchestrator } from './EnterpriseOrchestrator.js';
40
- import { ErrorHandler } from '../../shared/utils/ErrorHandler.js';
39
+ import { ErrorHandler } from '../../../lib/shared/utils/ErrorHandler.js';
41
40
 
42
41
  /**
43
42
  * Capability descriptors - Defines what each capability provides
@@ -204,8 +203,7 @@ export class UnifiedDeploymentOrchestrator extends BaseDeploymentOrchestrator {
204
203
  // System adapters for backward compatibility
205
204
  this.systemAdapters = {
206
205
  single: SingleServiceOrchestrator,
207
- portfolio: PortfolioOrchestrator,
208
- enterprise: EnterpriseOrchestrator
206
+ portfolio: PortfolioOrchestrator
209
207
  };
210
208
 
211
209
  // Deployment context
@@ -19,7 +19,7 @@
19
19
  * @abstract
20
20
  */
21
21
 
22
- import { ErrorHandler } from '../../shared/utils/ErrorHandler.js';
22
+ import { ErrorHandler } from '../../../shared/utils/ErrorHandler.js';
23
23
 
24
24
  /**
25
25
  * Phase state and execution order
@@ -242,7 +242,7 @@ export async function listDatabases(options = {}) {
242
242
  if (apiToken && accountId) {
243
243
  const {
244
244
  CloudflareAPI
245
- } = await import('../../../src/utils/cloudflare/api.js');
245
+ } = await import('@tamyla/clodo-framework/utils/cloudflare');
246
246
  const cf = new CloudflareAPI(apiToken);
247
247
  return await cf.listD1Databases(accountId);
248
248
  }
@@ -267,7 +267,7 @@ export async function databaseExists(databaseName, options = {}) {
267
267
  if (apiToken && accountId) {
268
268
  const {
269
269
  CloudflareAPI
270
- } = await import('../../../src/utils/cloudflare/api.js');
270
+ } = await import('@tamyla/clodo-framework/utils/cloudflare');
271
271
  const cf = new CloudflareAPI(apiToken);
272
272
  return await cf.d1DatabaseExists(accountId, databaseName);
273
273
  }
@@ -290,7 +290,7 @@ export async function createDatabase(name, options = {}) {
290
290
  if (apiToken && accountId) {
291
291
  const {
292
292
  CloudflareAPI
293
- } = await import('../../../src/utils/cloudflare/api.js');
293
+ } = await import('@tamyla/clodo-framework/utils/cloudflare');
294
294
  const cf = new CloudflareAPI(apiToken);
295
295
  const result = await cf.createD1Database(accountId, name);
296
296
  return result.uuid; // Return UUID to match CLI behavior
@@ -405,7 +405,7 @@ export async function getDatabaseId(databaseName, options = {}) {
405
405
  if (apiToken && accountId) {
406
406
  const {
407
407
  CloudflareAPI
408
- } = await import('../../../src/utils/cloudflare/api.js');
408
+ } = await import('@tamyla/clodo-framework/utils/cloudflare');
409
409
  const cf = new CloudflareAPI(apiToken);
410
410
  const db = await cf.getD1Database(accountId, databaseName);
411
411
  return db?.uuid || null;
@@ -430,33 +430,119 @@ export async function getDatabaseId(databaseName, options = {}) {
430
430
  }
431
431
 
432
432
  // Validate prerequisites
433
- export async function validatePrerequisites() {
433
+ export async function validatePrerequisites(options = {}) {
434
+ const {
435
+ checkAuth = false,
436
+ checkNetwork = false,
437
+ verbose = false
438
+ } = options;
434
439
  const checks = [{
435
440
  name: 'Node.js',
436
- command: 'node --version'
441
+ command: 'node --version',
442
+ required: true,
443
+ description: 'Required for running the framework and build processes',
444
+ minVersion: '18.0.0'
437
445
  }, {
438
446
  name: 'NPM',
439
- command: 'npm --version'
447
+ command: 'npm --version',
448
+ required: true,
449
+ description: 'Required for package management and scripts',
450
+ minVersion: '8.0.0'
440
451
  }, {
441
452
  name: 'Wrangler',
442
- command: 'npx wrangler --version'
453
+ command: 'npx wrangler --version',
454
+ required: true,
455
+ description: 'Cloudflare CLI for Workers and D1 database management',
456
+ minVersion: '3.0.0'
443
457
  }];
458
+
459
+ // Add optional checks
460
+ if (checkAuth) {
461
+ checks.push({
462
+ name: 'Wrangler Auth',
463
+ command: 'npx wrangler whoami',
464
+ required: false,
465
+ description: 'Cloudflare authentication (optional - can authenticate during deployment)',
466
+ async: true
467
+ });
468
+ }
469
+ if (checkNetwork) {
470
+ checks.push({
471
+ name: 'Network',
472
+ command: null,
473
+ required: false,
474
+ description: 'Internet connectivity to Cloudflare API',
475
+ async: true,
476
+ checkFunction: async () => {
477
+ try {
478
+ const response = await fetch('https://api.cloudflare.com/client/v4/user/tokens/verify', {
479
+ method: 'GET',
480
+ headers: {
481
+ 'Authorization': 'Bearer test'
482
+ },
483
+ signal: AbortSignal.timeout(5000)
484
+ });
485
+ return {
486
+ status: 'ok',
487
+ version: 'Connected'
488
+ };
489
+ } catch (error) {
490
+ if (error.name === 'AbortError') {
491
+ return {
492
+ status: 'failed',
493
+ error: 'Connection timeout'
494
+ };
495
+ }
496
+ return {
497
+ status: 'ok',
498
+ version: 'Connected (API responded)'
499
+ };
500
+ }
501
+ }
502
+ });
503
+ }
444
504
  const results = [];
445
505
  for (const check of checks) {
446
506
  try {
447
- const {
448
- stdout: version
449
- } = await execAsync(check.command, {
450
- encoding: 'utf8'
451
- });
507
+ let result;
508
+ if (check.async && check.checkFunction) {
509
+ result = await check.checkFunction();
510
+ } else if (check.command) {
511
+ const {
512
+ stdout: version
513
+ } = await execAsync(check.command, {
514
+ encoding: 'utf8',
515
+ timeout: 10000
516
+ });
517
+ result = {
518
+ status: 'ok',
519
+ version: version.trim()
520
+ };
521
+
522
+ // Version checking for critical components
523
+ if (check.minVersion && check.name === 'Node.js') {
524
+ const versionNum = version.trim().replace('v', '');
525
+ if (versionNum < check.minVersion) {
526
+ result = {
527
+ status: 'warning',
528
+ version: version.trim(),
529
+ warning: `Node.js ${check.minVersion}+ recommended, found ${versionNum}`
530
+ };
531
+ }
532
+ }
533
+ } else {
534
+ result = {
535
+ status: 'skipped',
536
+ version: 'N/A'
537
+ };
538
+ }
452
539
  results.push({
453
- name: check.name,
454
- status: 'ok',
455
- version: version.trim()
540
+ ...check,
541
+ ...result
456
542
  });
457
543
  } catch (error) {
458
544
  results.push({
459
- name: check.name,
545
+ ...check,
460
546
  status: 'failed',
461
547
  error: error.message
462
548
  });
@@ -11,7 +11,8 @@
11
11
  import chalk from 'chalk';
12
12
  import { askUser, askPassword, askChoice, closePrompts } from '../utils/interactive-prompts.js';
13
13
  import { ApiTokenManager } from '../security/api-token-manager.js';
14
- import { CloudflareAPI } from '../../../src/utils/cloudflare/api.js';
14
+ import { CloudflareAPI } from '@tamyla/clodo-framework/utils/cloudflare';
15
+ import { validatePrerequisites } from '../cloudflare/ops.js';
15
16
  export class DeploymentCredentialCollector {
16
17
  constructor(options = {}) {
17
18
  this.servicePath = options.servicePath || '.';
@@ -29,6 +30,10 @@ export class DeploymentCredentialCollector {
29
30
  * 4. Return complete credential set
30
31
  */
31
32
  async collectCredentials(options = {}) {
33
+ // First, check prerequisites
34
+ if (!this.quiet) {
35
+ await this.displayPrerequisites(options);
36
+ }
32
37
  const startCredentials = {
33
38
  token: options.token || process.env.CLOUDFLARE_API_TOKEN || null,
34
39
  accountId: options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID || null,
@@ -36,6 +41,11 @@ export class DeploymentCredentialCollector {
36
41
  zoneName: null // Will be populated when fetching zone
37
42
  };
38
43
 
44
+ // Show credential status if not quiet
45
+ if (!this.quiet) {
46
+ this.displayCredentialStatus(startCredentials, options);
47
+ }
48
+
39
49
  // All credentials provided - quick path
40
50
  if (startCredentials.token && startCredentials.accountId && startCredentials.zoneId) {
41
51
  if (!this.quiet) {
@@ -247,5 +257,128 @@ export class DeploymentCredentialCollector {
247
257
  cleanup() {
248
258
  closePrompts();
249
259
  }
260
+
261
+ /**
262
+ * Display prerequisite check results
263
+ * @param {Object} options - Options for prerequisite checking
264
+ */
265
+ async displayPrerequisites(options = {}) {
266
+ console.log(chalk.cyan('\nšŸ”§ Deployment Prerequisites\n'));
267
+ try {
268
+ const results = await validatePrerequisites({
269
+ checkAuth: options.checkAuth || false,
270
+ checkNetwork: options.checkNetwork || false,
271
+ verbose: !this.quiet
272
+ });
273
+ let allOk = true;
274
+ let hasWarnings = false;
275
+ results.forEach(result => {
276
+ const icon = result.status === 'ok' ? 'āœ…' : result.status === 'warning' ? 'āš ļø' : result.status === 'failed' ? 'āŒ' : 'ā­ļø';
277
+ const color = result.status === 'ok' ? chalk.green : result.status === 'warning' ? chalk.yellow : result.status === 'failed' ? chalk.red : chalk.gray;
278
+ console.log(`${icon} ${color(result.name)}: ${result.version || 'N/A'}`);
279
+ if (result.description) {
280
+ console.log(` ${chalk.gray(result.description)}`);
281
+ }
282
+ if (result.warning) {
283
+ console.log(` ${chalk.yellow('āš ļø ' + result.warning)}`);
284
+ hasWarnings = true;
285
+ }
286
+ if (result.error) {
287
+ console.log(` ${chalk.red('āŒ ' + result.error)}`);
288
+ allOk = false;
289
+ }
290
+ if (result.status === 'failed' && result.required) {
291
+ console.log(` ${chalk.red('šŸ’” Required for deployment - please install/update ' + result.name.toLowerCase())}`);
292
+ }
293
+ });
294
+ console.log('');
295
+
296
+ // Summary
297
+ if (allOk && !hasWarnings) {
298
+ console.log(chalk.green('šŸŽ‰ All prerequisites met - ready for deployment!\n'));
299
+ } else if (allOk && hasWarnings) {
300
+ console.log(chalk.yellow('āš ļø Prerequisites OK but with warnings - deployment may proceed\n'));
301
+ } else {
302
+ console.log(chalk.red('āŒ Some required prerequisites are missing - please resolve before deploying\n'));
303
+ }
304
+ } catch (error) {
305
+ console.log(chalk.yellow(`āš ļø Could not check prerequisites: ${error.message}\n`));
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Display credential status showing what's available vs missing
311
+ * @param {Object} credentials - Current credential state
312
+ * @param {Object} options - CLI options
313
+ */
314
+ displayCredentialStatus(credentials, options) {
315
+ console.log(chalk.cyan('\nšŸ” Credential Status\n'));
316
+ const status = {
317
+ token: {
318
+ available: !!credentials.token,
319
+ source: this.getCredentialSource('token', credentials.token, options.token),
320
+ required: true
321
+ },
322
+ accountId: {
323
+ available: !!credentials.accountId,
324
+ source: this.getCredentialSource('accountId', credentials.accountId, options.accountId),
325
+ required: false
326
+ },
327
+ zoneId: {
328
+ available: !!credentials.zoneId,
329
+ source: this.getCredentialSource('zoneId', credentials.zoneId, options.zoneId),
330
+ required: false
331
+ }
332
+ };
333
+
334
+ // Display each credential
335
+ Object.entries(status).forEach(([key, info]) => {
336
+ const icon = info.available ? 'āœ…' : 'āŒ';
337
+ const color = info.available ? chalk.green : chalk.red;
338
+ const label = key === 'token' ? 'API Token' : key === 'accountId' ? 'Account ID' : 'Zone ID';
339
+ const envVar = key === 'token' ? 'CLOUDFLARE_API_TOKEN' : key === 'accountId' ? 'CLOUDFLARE_ACCOUNT_ID' : 'CLOUDFLARE_ZONE_ID';
340
+ console.log(`${icon} ${color(label)}: ${info.available ? info.source : 'Missing'}`);
341
+ if (!info.available) {
342
+ console.log(` ${chalk.gray(`Set via --${key.replace('Id', '-id')} flag or ${envVar} environment variable`)}`);
343
+ console.log(` ${chalk.gray('To set environment variable:')}`);
344
+ console.log(` ${chalk.gray(` Windows PowerShell: $env:${envVar}="your-value-here"`)}`);
345
+ console.log(` ${chalk.gray(` Windows CMD: set ${envVar}=your-value-here`)}`);
346
+ console.log(` ${chalk.gray(` Linux/macOS: export ${envVar}="your-value-here"`)}`);
347
+ console.log(` ${chalk.gray(` Or add to .env file: ${envVar}=your-value-here`)}`);
348
+ console.log('');
349
+ }
350
+ });
351
+ console.log('');
352
+
353
+ // Summary
354
+ const availableCount = Object.values(status).filter(s => s.available).length;
355
+ const totalCount = Object.keys(status).length;
356
+ if (availableCount === totalCount) {
357
+ console.log(chalk.green('šŸŽ‰ All credentials available - ready for deployment!\n'));
358
+ } else if (availableCount >= 1) {
359
+ console.log(chalk.yellow(`āš ļø ${availableCount}/${totalCount} credentials available. Missing credentials will be fetched interactively.\n`));
360
+ } else {
361
+ console.log(chalk.red('āŒ No credentials found. Interactive collection required.\n'));
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Get the source of a credential (flag, env var, or none)
367
+ * @param {string} key - Credential key
368
+ * @param {*} value - Credential value
369
+ * @param {*} flagValue - CLI flag value
370
+ * @returns {string} Source description
371
+ */
372
+ getCredentialSource(key, value, flagValue) {
373
+ if (!value) return 'Not set';
374
+
375
+ // Check if it came from CLI flag
376
+ if (flagValue) return 'CLI flag';
377
+
378
+ // Check if it came from environment variable
379
+ const envVar = key === 'token' ? 'CLOUDFLARE_API_TOKEN' : key === 'accountId' ? 'CLOUDFLARE_ACCOUNT_ID' : 'CLOUDFLARE_ZONE_ID';
380
+ if (process.env[envVar]) return 'Environment variable';
381
+ return 'Unknown source';
382
+ }
250
383
  }
251
384
  export default DeploymentCredentialCollector;
@@ -4,7 +4,11 @@
4
4
  */
5
5
 
6
6
  export { DeploymentValidator } from './validator.js';
7
- export { MultiDomainOrchestrator } from '../../../src/orchestration/multi-domain-orchestrator.js';
8
- export { CrossDomainCoordinator } from '../../../src/orchestration/cross-domain-coordinator.js';
7
+ export { MultiDomainOrchestrator } from '@tamyla/clodo-framework/orchestration';
8
+ export { CrossDomainCoordinator } from '@tamyla/clodo-framework/orchestration';
9
9
  export { DeploymentAuditor } from './auditor.js';
10
- export { RollbackManager } from './rollback-manager.js';
10
+ export { RollbackManager } from './rollback-manager.js';
11
+ export { DeploymentCredentialCollector } from './credential-collector.js';
12
+
13
+ // Export workflow modules
14
+ export * from './workflows/index.js';
@@ -6,4 +6,4 @@
6
6
  * In npm packages, this resolves to dist/deployment/rollback-manager.js
7
7
  */
8
8
 
9
- export { RollbackManager } from '../../../src/deployment/rollback-manager.js';
9
+ export { RollbackManager } from '@tamyla/clodo-framework/deployment';
@@ -42,7 +42,7 @@ export class D1ErrorRecoveryManager {
42
42
  // Import WranglerDeployer for D1 error handling
43
43
  const {
44
44
  WranglerDeployer
45
- } = await import('../../../src/deployment/wrangler-deployer.js');
45
+ } = await import('@tamyla/clodo-framework/deployment');
46
46
  deployer = new WranglerDeployer({
47
47
  cwd: config.cwd || process.cwd(),
48
48
  environment: config.environment
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Deployment Workflows Module
3
+ * Exports all interactive deployment workflow coordinators and helpers
4
+ */
5
+
6
+ export { InteractiveDeploymentCoordinator } from './interactive-deployment-coordinator.js';
7
+ export { InteractiveConfirmationWorkflow } from './interactive-confirmation.js';
8
+ export { InteractiveDatabaseWorkflow } from './interactive-database-workflow.js';
9
+ export { InteractiveDomainInfoGatherer } from './interactive-domain-info-gatherer.js';
10
+ export { InteractiveSecretWorkflow } from './interactive-secret-workflow.js';
11
+ export { InteractiveTestingWorkflow } from './interactive-testing-workflow.js';
12
+ export { InteractiveValidationWorkflow } from './interactive-validation.js';
13
+ export { DeploymentSummaryWorkflow } from './deployment-summary.js';
@@ -7,7 +7,7 @@
7
7
  * @module interactive-confirmation
8
8
  */
9
9
 
10
- import { askYesNo } from '../utils/interactive-prompts.js';
10
+ import { askYesNo } from '../../utils/interactive-prompts.js';
11
11
 
12
12
  /**
13
13
  * Interactive Confirmation Manager
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Interactive Deployment Coordinator
3
+ *
4
+ * Restores the interactive deployment functionality that was lost during enterprise code separation.
5
+ * Orchestrates the interactive workflows that were extracted from enterprise-deployment/master-deploy.js.
6
+ *
7
+ * This coordinator brings together:
8
+ * - InteractiveDomainInfoGatherer
9
+ * - InteractiveDatabaseWorkflow
10
+ * - InteractiveSecretWorkflow
11
+ * - InteractiveValidation
12
+ * - InteractiveConfirmation
13
+ * - InteractiveTestingWorkflow
14
+ *
15
+ * @module interactive-deployment-coordinator
16
+ */
17
+
18
+ import { InteractiveDomainInfoGatherer } from './interactive-domain-info-gatherer.js';
19
+ import { InteractiveDatabaseWorkflow } from './interactive-database-workflow.js';
20
+ import { InteractiveSecretWorkflow } from './interactive-secret-workflow.js';
21
+ import { InteractiveValidation } from './interactive-validation.js';
22
+ import { InteractiveConfirmation } from './interactive-confirmation.js';
23
+ import { InteractiveTestingWorkflow } from './interactive-testing-workflow.js';
24
+ import { Clodo } from '@tamyla/clodo-framework';
25
+ import { OutputFormatter } from '../../utils/output-formatter.js';
26
+ import { DeploymentCredentialCollector } from '../credential-collector.js';
27
+
28
+ /**
29
+ * Interactive Deployment Coordinator
30
+ * Orchestrates the complete interactive deployment workflow
31
+ */
32
+ export class InteractiveDeploymentCoordinator {
33
+ /**
34
+ * @param {Object} options - Coordinator options
35
+ * @param {string} options.servicePath - Path to service directory
36
+ * @param {string} options.environment - Target environment
37
+ * @param {string} options.domain - Specific domain (optional)
38
+ * @param {boolean} options.dryRun - Dry run mode
39
+ * @param {Object} options.credentials - Cloudflare credentials
40
+ * @param {boolean} options.checkPrereqs - Check prerequisites
41
+ * @param {boolean} options.checkAuth - Check authentication
42
+ * @param {boolean} options.checkNetwork - Check network connectivity
43
+ * @param {boolean} options.verbose - Verbose output
44
+ * @param {boolean} options.quiet - Quiet output
45
+ * @param {boolean} options.json - JSON output
46
+ */
47
+ constructor(options = {}) {
48
+ this.options = options;
49
+ this.output = new OutputFormatter(options);
50
+ this.deploymentState = {
51
+ config: {},
52
+ validation: {},
53
+ resources: {},
54
+ testing: {}
55
+ };
56
+
57
+ // Initialize credential collector
58
+ this.credentialCollector = new DeploymentCredentialCollector({
59
+ quiet: options.quiet,
60
+ servicePath: options.servicePath
61
+ });
62
+
63
+ // Initialize interactive workflow components
64
+ this.workflows = {
65
+ domainGatherer: new InteractiveDomainInfoGatherer({
66
+ interactive: true,
67
+ configCache: null // TODO: Add config cache if available
68
+ }),
69
+ databaseWorkflow: new InteractiveDatabaseWorkflow({
70
+ interactive: true
71
+ }),
72
+ secretWorkflow: new InteractiveSecretWorkflow({
73
+ interactive: true
74
+ }),
75
+ validation: new InteractiveValidation({
76
+ interactive: true
77
+ }),
78
+ confirmation: InteractiveConfirmation,
79
+ testingWorkflow: new InteractiveTestingWorkflow({
80
+ interactive: true
81
+ })
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Run the complete interactive deployment workflow
87
+ * @returns {Promise<Object>} Deployment result
88
+ */
89
+ async runInteractiveDeployment() {
90
+ try {
91
+ console.log('\nšŸ”„ Starting Interactive Deployment Workflow...\n');
92
+
93
+ // Phase 1: Gather domain and environment information
94
+ await this.gatherDeploymentInfo();
95
+
96
+ // Phase 2: Configure database resources
97
+ await this.configureDatabase();
98
+
99
+ // Phase 3: Configure secrets and credentials
100
+ await this.configureSecrets();
101
+
102
+ // Phase 4: Validate configuration
103
+ await this.validateConfiguration();
104
+
105
+ // Phase 5: Show final confirmation
106
+ await this.showConfirmation();
107
+
108
+ // Phase 6: Execute deployment
109
+ const result = await this.executeDeployment();
110
+
111
+ // Phase 7: Run post-deployment testing
112
+ await this.runPostDeploymentTests();
113
+ return result;
114
+ } catch (error) {
115
+ this.output.error(`Interactive deployment failed: ${error.message}`);
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Phase 1: Gather domain and environment information
122
+ */
123
+ async gatherDeploymentInfo() {
124
+ console.log('šŸ“‹ Phase 1: Gathering Deployment Information');
125
+ console.log('─'.repeat(50));
126
+
127
+ // Start with basic config from options
128
+ this.deploymentState.config = {
129
+ servicePath: this.options.servicePath || '.',
130
+ environment: this.options.environment || 'production',
131
+ domain: this.options.domain,
132
+ dryRun: this.options.dryRun || false
133
+ };
134
+
135
+ // Collect credentials with prerequisite checking
136
+ const credentialOptions = {
137
+ token: this.options.credentials?.token,
138
+ accountId: this.options.credentials?.accountId,
139
+ zoneId: this.options.credentials?.zoneId,
140
+ checkAuth: this.options.checkAuth,
141
+ checkNetwork: this.options.checkNetwork
142
+ };
143
+ this.deploymentState.config.credentials = await this.credentialCollector.collectCredentials(credentialOptions);
144
+
145
+ // Use interactive domain gatherer for missing information
146
+ if (!this.deploymentState.config.domain) {
147
+ this.deploymentState.config = await this.workflows.domainGatherer.gatherSingleDomainInfo(this.deploymentState.config);
148
+ }
149
+ console.log('āœ… Domain information gathered\n');
150
+ }
151
+
152
+ /**
153
+ * Phase 2: Configure database resources
154
+ */
155
+ async configureDatabase() {
156
+ console.log('šŸ—„ļø Phase 2: Configuring Database Resources');
157
+ console.log('─'.repeat(50));
158
+ this.deploymentState.resources.database = await this.workflows.databaseWorkflow.handleDatabaseSetup(this.deploymentState.config.domain, this.deploymentState.config.environment, {
159
+ interactive: true
160
+ });
161
+ console.log('āœ… Database configuration complete\n');
162
+ }
163
+
164
+ /**
165
+ * Phase 3: Configure secrets and credentials
166
+ */
167
+ async configureSecrets() {
168
+ console.log('šŸ” Phase 3: Configuring Secrets & Credentials');
169
+ console.log('─'.repeat(50));
170
+ const workerName = `${this.deploymentState.config.domain}-data-service`;
171
+ this.deploymentState.resources.secrets = await this.workflows.secretWorkflow.handleSecretManagement(this.deploymentState.config.domain, this.deploymentState.config.environment, workerName, {
172
+ interactive: true
173
+ });
174
+ console.log('āœ… Secrets configuration complete\n');
175
+ }
176
+
177
+ /**
178
+ * Phase 4: Validate configuration
179
+ */
180
+ async validateConfiguration() {
181
+ console.log('āœ… Phase 4: Validating Configuration');
182
+ console.log('─'.repeat(50));
183
+ this.deploymentState.validation = await this.workflows.validation.executePreDeploymentChecks(this.deploymentState.config);
184
+ console.log('āœ… Configuration validation complete\n');
185
+ }
186
+
187
+ /**
188
+ * Phase 5: Show final confirmation
189
+ */
190
+ async showConfirmation() {
191
+ console.log('šŸŽÆ Phase 5: Final Confirmation');
192
+ console.log('─'.repeat(50));
193
+ await this.workflows.confirmation.showFinalConfirmation(this.deploymentState.config, this.deploymentState, {
194
+ defaultAnswer: 'n'
195
+ });
196
+ console.log('āœ… Deployment confirmed\n');
197
+ }
198
+
199
+ /**
200
+ * Phase 6: Execute deployment
201
+ */
202
+ async executeDeployment() {
203
+ console.log('šŸš€ Phase 6: Executing Deployment');
204
+ console.log('─'.repeat(50));
205
+ const result = await Clodo.deploy({
206
+ servicePath: this.deploymentState.config.servicePath,
207
+ environment: this.deploymentState.config.environment,
208
+ domain: this.deploymentState.config.domain,
209
+ dryRun: this.deploymentState.config.dryRun,
210
+ credentials: this.deploymentState.config.credentials
211
+ });
212
+ if (result.success) {
213
+ console.log('āœ… Deployment executed successfully\n');
214
+ } else {
215
+ throw new Error('Deployment execution failed');
216
+ }
217
+ return result;
218
+ }
219
+
220
+ /**
221
+ * Phase 7: Run post-deployment testing
222
+ */
223
+ async runPostDeploymentTests() {
224
+ console.log('🧪 Phase 7: Running Post-Deployment Tests');
225
+ console.log('─'.repeat(50));
226
+ this.deploymentState.testing = await this.workflows.testingWorkflow.executePostDeploymentTesting(this.deploymentState.config);
227
+ console.log('āœ… Post-deployment testing complete\n');
228
+ }
229
+ }
@@ -7,7 +7,7 @@
7
7
  * @module interactive-domain-info-gatherer
8
8
  */
9
9
 
10
- import { askUser, askYesNo, askChoice } from '../utils/interactive-prompts.js';
10
+ import { askUser, askYesNo, askChoice } from '../../utils/interactive-prompts.js';
11
11
 
12
12
  /**
13
13
  * Interactive Domain Info Gatherer
@@ -7,9 +7,9 @@
7
7
  * @module interactive-secret-workflow
8
8
  */
9
9
 
10
- import { askYesNo } from '../utils/interactive-prompts.js';
11
- import { generateSecrets, saveSecrets, distributeSecrets } from '../security/secret-generator.js';
12
- import { deploySecret } from '../cloudflare/ops.js';
10
+ import { askYesNo } from '../../utils/interactive-prompts.js';
11
+ import { generateSecrets, saveSecrets, distributeSecrets } from '../../security/secret-generator.js';
12
+ import { deploySecret } from '../../cloudflare/ops.js';
13
13
  import { join } from 'path';
14
14
  import { existsSync, readFileSync } from 'fs';
15
15
 
@@ -7,8 +7,8 @@
7
7
  * @module interactive-testing-workflow
8
8
  */
9
9
 
10
- import { askYesNo } from '../utils/interactive-prompts.js';
11
- import { checkHealth } from '../monitoring/health-checker.js';
10
+ import { askYesNo } from '../../utils/interactive-prompts.js';
11
+ import { checkHealth } from '../../monitoring/health-checker.js';
12
12
 
13
13
  /**
14
14
  * Interactive Testing Workflow
@@ -7,8 +7,8 @@
7
7
  * @module interactive-validation
8
8
  */
9
9
 
10
- import { askYesNo } from '../utils/interactive-prompts.js';
11
- import { validatePrerequisites, checkAuth, authenticate, workerExists } from '../cloudflare/ops.js';
10
+ import { askYesNo } from '../../utils/interactive-prompts.js';
11
+ import { validatePrerequisites, checkAuth, authenticate, workerExists } from '../../cloudflare/ops.js';
12
12
 
13
13
  /**
14
14
  * Interactive Validation Workflow
@@ -12,10 +12,16 @@ import http from 'http';
12
12
  const execAsync = promisify(exec);
13
13
 
14
14
  // Load framework configuration
15
- const {
16
- frameworkConfig
17
- } = await import('../../../src/utils/framework-config.js');
18
- const timing = frameworkConfig.getTiming();
15
+ // const { frameworkConfig } = await import('@tamyla/clodo-framework/utils');
16
+ // const timing = frameworkConfig.getTiming();
17
+
18
+ // Use default timing values for now
19
+ const timing = {
20
+ healthCheckTimeout: 10000,
21
+ deploymentTimeout: 300000,
22
+ deploymentInterval: 5000,
23
+ endpointValidationTimeout: 5000
24
+ };
19
25
  function makeHttpRequest(url, method = 'GET', timeout = 5000) {
20
26
  return new Promise((resolve, reject) => {
21
27
  const protocol = url.startsWith('https:') ? https : http;
@@ -546,7 +552,7 @@ export async function verifyWorkerDeployment(workerName, credentials, options =
546
552
  }
547
553
  const {
548
554
  CloudflareAPI
549
- } = await import('../../../src/utils/cloudflare/api.js');
555
+ } = await import('@tamyla/clodo-framework/utils/cloudflare');
550
556
  const cfApi = new CloudflareAPI(credentials.token);
551
557
 
552
558
  // List all workers to find ours
@@ -8,7 +8,7 @@
8
8
  /**
9
9
  * Import validators from utils (source of truth)
10
10
  */
11
- import { validateServiceName, validateDomainName, validateCloudflareToken, validateCloudflareId, validateServiceType, validateEnvironment } from '../../../src/utils/validation.js';
11
+ import { validateServiceName, validateDomainName, validateCloudflareToken, validateCloudflareId, validateServiceType, validateEnvironment } from '@tamyla/clodo-framework/utils';
12
12
 
13
13
  /**
14
14
  * Validation Registry - Single source of truth for all validators
@@ -4,4 +4,5 @@
4
4
  export { ConfigurationCacheManager } from './config-cache.js';
5
5
  export { EnhancedSecretManager } from './secret-generator.js';
6
6
  export { UnifiedConfigManager, unifiedConfigManager } from '../config/unified-config-manager.js';
7
- export { askUser, askYesNo, askChoice, closePrompts } from '../interactive-prompts.js';
7
+ export { askUser, askYesNo, askChoice, closePrompts } from '../interactive-prompts.js';
8
+ export { DeploymentCredentialCollector } from '../../../lib/shared/deployment/credential-collector.js';
@@ -1,43 +1,41 @@
1
1
  /**
2
2
  * Health Checker Module
3
3
  * Endpoint health checking and validation utilities
4
+ * Worker-compatible version using fetch API
4
5
  */
5
6
 
6
- import https from 'https';
7
- import http from 'http';
8
-
9
7
  /**
10
- * Make HTTP request with timeout
8
+ * Make HTTP request with timeout using fetch
11
9
  * @param {string} url - URL to request
12
10
  * @param {string} method - HTTP method
13
11
  * @param {number} timeout - Request timeout in ms
14
12
  * @returns {Promise<Object>} Response data and status
15
13
  */
16
- function makeHttpRequest(url, method = 'GET', timeout = 5000) {
17
- return new Promise((resolve, reject) => {
18
- const protocol = url.startsWith('https:') ? https : http;
19
- const req = protocol.request(url, {
14
+ async function makeHttpRequest(url, method = 'GET', timeout = 5000) {
15
+ const controller = new AbortController();
16
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
17
+ try {
18
+ const response = await fetch(url, {
20
19
  method,
21
- timeout
22
- }, res => {
23
- let data = '';
24
- res.on('data', chunk => data += chunk);
25
- res.on('end', () => {
26
- resolve({
27
- data,
28
- statusCode: res.statusCode
29
- });
30
- });
31
- });
32
- req.on('error', err => {
33
- reject(err);
20
+ signal: controller.signal,
21
+ headers: {
22
+ 'User-Agent': 'Clodo-Framework-Health-Check/1.0'
23
+ }
34
24
  });
35
- req.on('timeout', () => {
36
- req.destroy();
37
- reject(new Error('Request timed out'));
38
- });
39
- req.end();
40
- });
25
+ clearTimeout(timeoutId);
26
+ const data = await response.text();
27
+ return {
28
+ data,
29
+ statusCode: response.status,
30
+ headers: Object.fromEntries(response.headers.entries())
31
+ };
32
+ } catch (error) {
33
+ clearTimeout(timeoutId);
34
+ if (error.name === 'AbortError') {
35
+ throw new Error('Request timed out');
36
+ }
37
+ throw error;
38
+ }
41
39
  }
42
40
 
43
41
  /**
@@ -47,55 +45,34 @@ function makeHttpRequest(url, method = 'GET', timeout = 5000) {
47
45
  * @returns {Promise<Object>} Health check result
48
46
  */
49
47
  export async function checkHealth(url, timeout = 10000) {
50
- return new Promise((resolve, reject) => {
51
- const protocol = url.startsWith('https:') ? https : http;
52
- const healthUrl = `${url.replace(/\/$/, '')}/health`;
53
- const req = protocol.get(healthUrl, {
54
- timeout
55
- }, res => {
56
- let data = '';
57
- res.on('data', chunk => data += chunk);
58
- res.on('end', () => {
59
- try {
60
- const result = {
61
- url: healthUrl,
62
- status: res.statusCode,
63
- healthy: res.statusCode >= 200 && res.statusCode < 300,
64
- responseTime: Date.now() - req.startTime,
65
- data: data ? JSON.parse(data) : null
66
- };
67
- resolve(result);
68
- } catch (parseError) {
69
- resolve({
70
- url: healthUrl,
71
- status: res.statusCode,
72
- healthy: res.statusCode >= 200 && res.statusCode < 300,
73
- responseTime: Date.now() - req.startTime,
74
- data: null,
75
- parseError: parseError.message
76
- });
77
- }
78
- });
79
- });
80
- req.startTime = Date.now();
81
- req.on('error', err => {
82
- reject({
83
- url: healthUrl,
84
- error: err.message,
85
- healthy: false,
86
- responseTime: Date.now() - req.startTime
87
- });
88
- });
89
- req.on('timeout', () => {
90
- req.destroy();
91
- reject({
92
- url: healthUrl,
93
- error: 'Health check timed out',
94
- healthy: false,
95
- responseTime: timeout
96
- });
97
- });
98
- });
48
+ const startTime = Date.now();
49
+ const healthUrl = `${url.replace(/\/$/, '')}/health`;
50
+ try {
51
+ const result = await makeHttpRequest(healthUrl, 'GET', timeout);
52
+ const responseTime = Date.now() - startTime;
53
+ let data = null;
54
+ let parseError = null;
55
+ try {
56
+ data = result.data ? JSON.parse(result.data) : null;
57
+ } catch (error) {
58
+ parseError = error.message;
59
+ }
60
+ return {
61
+ url: healthUrl,
62
+ status: result.statusCode,
63
+ healthy: result.statusCode >= 200 && result.statusCode < 300,
64
+ responseTime,
65
+ data,
66
+ parseError
67
+ };
68
+ } catch (error) {
69
+ return {
70
+ url: healthUrl,
71
+ error: error.message,
72
+ healthy: false,
73
+ responseTime: Date.now() - startTime
74
+ };
75
+ }
99
76
  }
100
77
 
101
78
  /**
@@ -32,5 +32,8 @@ export const createLogger = (prefix = 'ClodoFramework') => {
32
32
  // Health checking utilities
33
33
  export * from './health-checker.js';
34
34
 
35
+ // Framework configuration
36
+ export * from './framework-config.js';
37
+
35
38
  // Deployment utilities
36
39
  export * from './deployment/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamyla/clodo-framework",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -22,7 +22,7 @@
22
22
  "./config/customers": "./dist/config/customers.js",
23
23
  "./utils/config": "./dist/utils/config/unified-config-manager.js",
24
24
  "./worker": "./dist/worker/index.js",
25
- "./utils": "./dist/utils/index.js",
25
+ "./utils/cloudflare": "./dist/utils/cloudflare/index.js",
26
26
  "./utils/deployment": "./dist/utils/deployment/index.js",
27
27
  "./orchestration": "./dist/orchestration/index.js",
28
28
  "./deployment": "./dist/deployment/index.js",
@@ -1,4 +1,4 @@
1
- import { initializeService, createFeatureGuard, COMMON_FEATURES } from '@tamyla/clodo-framework';
1
+ import { initializeService, createFeatureGuard, COMMON_FEATURES } from '@tamyla/clodo-framework/worker';
2
2
  import { domains } from './config/domains.js';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { initializeService, createFeatureGuard, COMMON_FEATURES } from '@tamyla/clodo-framework';
1
+ import { initializeService, createFeatureGuard, COMMON_FEATURES } from '@tamyla/clodo-framework/worker';
2
2
  import { domains } from './config/domains.js';
3
3
 
4
4
  /**