@tamyla/clodo-framework 3.1.21 → 3.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +283 -1
  3. package/dist/{bin → cli}/clodo-service.js +47 -15
  4. package/dist/cli/commands/assess.js +183 -0
  5. package/dist/{bin → cli}/commands/create.js +5 -5
  6. package/dist/{bin → cli}/commands/deploy.js +122 -90
  7. package/dist/{bin → cli}/commands/diagnose.js +5 -5
  8. package/dist/cli/commands/helpers/deployment-ui.js +138 -0
  9. package/dist/cli/commands/helpers/deployment-verification.js +250 -0
  10. package/dist/cli/commands/helpers/error-recovery.js +80 -0
  11. package/dist/cli/commands/helpers/resource-detection.js +113 -0
  12. package/dist/{bin → cli}/commands/helpers.js +0 -28
  13. package/dist/cli/commands/init-config.js +57 -0
  14. package/dist/{bin → cli}/commands/update.js +5 -5
  15. package/dist/{bin → cli}/commands/validate.js +5 -5
  16. package/dist/cli/security-cli.js +118 -0
  17. package/dist/config/FeatureManager.js +6 -0
  18. package/dist/config/clodo-create.example.json +26 -0
  19. package/dist/config/clodo-deploy.example.json +41 -0
  20. package/dist/config/clodo-update.example.json +46 -0
  21. package/dist/config/clodo-validate.example.json +41 -0
  22. package/dist/config/customers/template/development.env.template +37 -0
  23. package/dist/config/customers/template/production.env.template +39 -0
  24. package/dist/config/customers/template/staging.env.template +37 -0
  25. package/dist/config/customers.js +28 -26
  26. package/dist/config/domain-examples/README.md +464 -0
  27. package/dist/config/domain-examples/environment-mapped.json +168 -0
  28. package/dist/config/domain-examples/multi-domain.json +144 -0
  29. package/dist/config/domain-examples/single-domain.json +50 -0
  30. package/dist/config/examples +12 -0
  31. package/dist/config/features.js +61 -0
  32. package/dist/config/staging-deployment.json +60 -0
  33. package/dist/config/validation-config.json +347 -0
  34. package/dist/deployment/wrangler-deployer.js +1 -1
  35. package/dist/{bin → lib}/deployment/modules/DeploymentOrchestrator.js +2 -2
  36. package/dist/{bin → lib}/deployment/modules/EnvironmentManager.js +2 -2
  37. package/dist/lib/deployment/orchestration/EnterpriseOrchestrator.js +21 -0
  38. package/dist/lib/shared/cache/configuration-cache.js +82 -0
  39. package/dist/{bin → lib}/shared/cloudflare/domain-discovery.js +1 -1
  40. package/dist/{bin → lib}/shared/cloudflare/domain-manager.js +1 -1
  41. package/dist/{bin → lib}/shared/cloudflare/index.js +1 -1
  42. package/dist/{bin → lib}/shared/cloudflare/ops.js +10 -8
  43. package/dist/{bin → lib}/shared/config/ConfigurationManager.js +23 -1
  44. package/dist/{bin → lib}/shared/config/command-config-manager.js +19 -3
  45. package/dist/{bin → lib}/shared/config/index.js +1 -1
  46. package/dist/{bin → lib}/shared/deployment/credential-collector.js +30 -7
  47. package/dist/lib/shared/deployment/index.js +10 -0
  48. package/dist/lib/shared/deployment/rollback-manager.js +7 -0
  49. package/dist/lib/shared/deployment/utilities/d1-error-recovery.js +177 -0
  50. package/dist/{bin → lib}/shared/deployment/validator.js +40 -10
  51. package/dist/lib/shared/deployment/workflows/deployment-summary.js +214 -0
  52. package/dist/lib/shared/deployment/workflows/interactive-confirmation.js +188 -0
  53. package/dist/lib/shared/deployment/workflows/interactive-database-workflow.js +234 -0
  54. package/dist/lib/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
  55. package/dist/lib/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
  56. package/dist/lib/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
  57. package/dist/lib/shared/deployment/workflows/interactive-validation.js +218 -0
  58. package/dist/lib/shared/error-handling/error-classifier.js +46 -0
  59. package/dist/{bin → lib}/shared/monitoring/health-checker.js +129 -1
  60. package/dist/{bin → lib}/shared/monitoring/memory-manager.js +17 -6
  61. package/dist/{bin → lib}/shared/routing/domain-router.js +1 -1
  62. package/dist/lib/shared/utils/deployment-validator.js +97 -0
  63. package/dist/{bin → lib}/shared/utils/formatters.js +10 -0
  64. package/dist/{bin → lib}/shared/utils/index.js +13 -1
  65. package/dist/{bin → lib}/shared/utils/interactive-prompts.js +34 -18
  66. package/dist/{bin → lib}/shared/utils/progress-manager.js +2 -2
  67. package/dist/lib/shared/utils/progress-spinner.js +53 -0
  68. package/dist/lib/shared/utils/sensitive-redactor.js +91 -0
  69. package/dist/{bin → lib}/shared/validation/ValidationRegistry.js +1 -1
  70. package/dist/migration/MigrationAdapters.js +50 -4
  71. package/dist/orchestration/cross-domain-coordinator.js +5 -5
  72. package/dist/orchestration/multi-domain-orchestrator.js +63 -22
  73. package/dist/security/index.js +2 -2
  74. package/dist/security/patterns/insecure-patterns.js +1 -1
  75. package/dist/service-management/ConfirmationEngine.js +1 -1
  76. package/dist/service-management/ErrorTracker.js +1 -1
  77. package/dist/service-management/InputCollector.js +1 -1
  78. package/dist/service-management/ServiceCreator.js +11 -255
  79. package/dist/service-management/ServiceOrchestrator.js +0 -2
  80. package/dist/service-management/generators/testing/UnitTestsGenerator.js +4 -4
  81. package/dist/service-management/index.js +1 -1
  82. package/dist/utils/cloudflare/ops.js +1 -1
  83. package/dist/utils/constants.js +102 -0
  84. package/dist/utils/deployment/wrangler-config-manager.js +215 -48
  85. package/dist/utils/file-manager.js +1 -1
  86. package/dist/utils/formatters.js +1 -1
  87. package/dist/utils/framework-config.js +2 -2
  88. package/dist/utils/interactive-prompts.js +10 -59
  89. package/dist/utils/logger.js +1 -1
  90. package/dist/version/VersionDetector.js +99 -9
  91. package/dist/worker/integration.js +1 -1
  92. package/package.json +10 -10
  93. package/dist/bin/clodo-service-old.js +0 -868
  94. package/dist/bin/clodo-service-test.js +0 -10
  95. package/dist/bin/commands/assess.js +0 -91
  96. package/dist/bin/database/enterprise-db-manager.js +0 -457
  97. package/dist/bin/deployment/enterprise-deploy.js +0 -877
  98. package/dist/bin/deployment/master-deploy.js +0 -1376
  99. package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
  100. package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  101. package/dist/bin/deployment/test-interactive-utils.js +0 -66
  102. package/dist/bin/portfolio/portfolio-manager.js +0 -487
  103. package/dist/bin/security/security-cli.js +0 -108
  104. package/dist/bin/service-management/create-service.js +0 -122
  105. package/dist/bin/service-management/init-service.js +0 -79
  106. package/dist/bin/shared/deployment/index.js +0 -10
  107. package/dist/bin/shared/deployment/rollback-manager.js +0 -523
  108. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  109. package/dist/service-management/ServiceInitializer.js +0 -453
  110. /package/dist/{bin → lib}/database/deployment-db-manager.js +0 -0
  111. /package/dist/{bin → lib}/database/wrangler-d1-manager.js +0 -0
  112. /package/dist/{bin → lib}/deployment/modules/DeploymentConfiguration.js +0 -0
  113. /package/dist/{bin → lib}/deployment/modules/MonitoringIntegration.js +0 -0
  114. /package/dist/{bin → lib}/deployment/modules/ValidationManager.js +0 -0
  115. /package/dist/{bin → lib}/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -0
  116. /package/dist/{bin → lib}/deployment/orchestration/PortfolioOrchestrator.js +0 -0
  117. /package/dist/{bin → lib}/deployment/orchestration/SingleServiceOrchestrator.js +0 -0
  118. /package/dist/{bin → lib}/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -0
  119. /package/dist/{bin → lib}/shared/config/cache.js +0 -0
  120. /package/dist/{bin → lib}/shared/config/cloudflare-service-validator.js +0 -0
  121. /package/dist/{bin → lib}/shared/config/manager.js +0 -0
  122. /package/dist/{bin → lib}/shared/config/manifest-loader.js +0 -0
  123. /package/dist/{bin → lib}/shared/database/connection-manager.js +0 -0
  124. /package/dist/{bin → lib}/shared/database/index.js +0 -0
  125. /package/dist/{bin → lib}/shared/database/orchestrator.js +0 -0
  126. /package/dist/{bin → lib}/shared/deployment/auditor.js +0 -0
  127. /package/dist/{bin → lib}/shared/index.js +0 -0
  128. /package/dist/{bin → lib}/shared/logging/Logger.js +0 -0
  129. /package/dist/{bin → lib}/shared/monitoring/index.js +0 -0
  130. /package/dist/{bin → lib}/shared/monitoring/production-monitor.js +0 -0
  131. /package/dist/{bin → lib}/shared/production-tester/api-tester.js +0 -0
  132. /package/dist/{bin → lib}/shared/production-tester/auth-tester.js +0 -0
  133. /package/dist/{bin → lib}/shared/production-tester/core.js +0 -0
  134. /package/dist/{bin → lib}/shared/production-tester/database-tester.js +0 -0
  135. /package/dist/{bin → lib}/shared/production-tester/index.js +0 -0
  136. /package/dist/{bin → lib}/shared/production-tester/load-tester.js +0 -0
  137. /package/dist/{bin → lib}/shared/production-tester/performance-tester.js +0 -0
  138. /package/dist/{bin → lib}/shared/security/api-token-manager.js +0 -0
  139. /package/dist/{bin → lib}/shared/security/index.js +0 -0
  140. /package/dist/{bin → lib}/shared/security/secret-generator.js +0 -0
  141. /package/dist/{bin → lib}/shared/security/secure-token-manager.js +0 -0
  142. /package/dist/{bin → lib}/shared/utils/ErrorHandler.js +0 -0
  143. /package/dist/{bin → lib}/shared/utils/cli-options.js +0 -0
  144. /package/dist/{bin → lib}/shared/utils/config-loader.js +0 -0
  145. /package/dist/{bin → lib}/shared/utils/error-recovery.js +0 -0
  146. /package/dist/{bin → lib}/shared/utils/file-manager.js +0 -0
  147. /package/dist/{bin → lib}/shared/utils/graceful-shutdown-manager.js +0 -0
  148. /package/dist/{bin → lib}/shared/utils/interactive-utils.js +0 -0
  149. /package/dist/{bin → lib}/shared/utils/output-formatter.js +0 -0
  150. /package/dist/{bin → lib}/shared/utils/rate-limiter.js +0 -0
@@ -96,6 +96,192 @@ export class WranglerConfigManager {
96
96
  }
97
97
  }
98
98
 
99
+ /**
100
+ * Get customer config path based on zone name
101
+ * Maps zone names to customer config directories using conventions:
102
+ * - clodo.dev → config/customers/clodo/wrangler.toml
103
+ * - tamyla.com → config/customers/tamyla/wrangler.toml
104
+ * - wetechfounders.com → config/customers/wetechfounders/wrangler.toml
105
+ *
106
+ * @param {string} zoneName - Cloudflare zone name (domain)
107
+ * @returns {string} Path to customer config (may not exist yet)
108
+ */
109
+ getCustomerConfigPath(zoneName) {
110
+ if (!zoneName) {
111
+ throw new Error('Zone name is required to get customer config path');
112
+ }
113
+
114
+ // Extract base name from domain (e.g., clodo.dev → clodo)
115
+ const baseName = zoneName.split('.')[0];
116
+
117
+ // Return customer directory path based on base name
118
+ return join(this.projectRoot, 'config', 'customers', baseName, 'wrangler.toml');
119
+ }
120
+
121
+ /**
122
+ * Generate or update customer-specific wrangler.toml
123
+ * Creates a persistent config for the customer based on current deployment parameters
124
+ * This serves as deployment history and source of truth for reruns
125
+ *
126
+ * @param {string} zoneName - Cloudflare zone name (domain)
127
+ * @param {Object} params - Deployment parameters
128
+ * @param {string} params.accountId - Cloudflare account ID
129
+ * @param {string} params.environment - Deployment environment (production, staging, development)
130
+ * @returns {Promise<string>} Path to generated/updated customer config
131
+ */
132
+ async generateCustomerConfig(zoneName, params = {}) {
133
+ const {
134
+ accountId,
135
+ environment = 'production'
136
+ } = params;
137
+ if (!zoneName) {
138
+ throw new Error('Zone name is required to generate customer config');
139
+ }
140
+ const customerConfigPath = this.getCustomerConfigPath(zoneName);
141
+ const customerDir = dirname(customerConfigPath);
142
+ if (this.dryRun) {
143
+ console.log(` 🔍 DRY RUN: Would generate/update customer config at ${customerConfigPath}`);
144
+ return customerConfigPath;
145
+ }
146
+
147
+ // Ensure customer directory exists
148
+ await mkdir(customerDir, {
149
+ recursive: true
150
+ });
151
+
152
+ // Read existing config or start with current root config as template
153
+ let config;
154
+ try {
155
+ await access(customerConfigPath, constants.F_OK);
156
+ // Customer config exists - read and update it
157
+ const content = await readFile(customerConfigPath, 'utf-8');
158
+ config = parseToml(content);
159
+ if (this.verbose) {
160
+ console.log(` 📋 Updating existing customer config: ${customerConfigPath}`);
161
+ }
162
+ } catch (error) {
163
+ // Customer config doesn't exist - use root config as template
164
+ try {
165
+ config = await this.readConfig();
166
+ if (this.verbose) {
167
+ console.log(` 📋 Creating new customer config from root template: ${customerConfigPath}`);
168
+ }
169
+ } catch (readError) {
170
+ // No root config either - create minimal config
171
+ config = {
172
+ name: 'worker',
173
+ main: 'src/index.js',
174
+ compatibility_date: new Date().toISOString().split('T')[0],
175
+ env: {}
176
+ };
177
+ if (this.verbose) {
178
+ console.log(` 📋 Creating new minimal customer config: ${customerConfigPath}`);
179
+ }
180
+ }
181
+ }
182
+
183
+ // Update account_id if provided
184
+ if (accountId) {
185
+ config.account_id = accountId;
186
+ console.log(` ✅ Set account_id to ${accountId} in customer config`);
187
+ }
188
+
189
+ // Update worker name based on zone
190
+ // Extract base service name and apply zone-based naming
191
+ const customerPrefix = zoneName.split('.')[0]; // clodo.dev → clodo
192
+
193
+ // Update root-level worker name
194
+ if (config.name) {
195
+ const baseName = config.name.replace(/^[^-]+-/, ''); // Remove existing prefix
196
+ const newWorkerName = `${customerPrefix}-${baseName}`;
197
+ config.name = newWorkerName;
198
+ console.log(` ✅ Set worker name to ${newWorkerName} in customer config`);
199
+ }
200
+
201
+ // Update SERVICE_DOMAIN in environment variables (all environments)
202
+ const updateServiceDomain = varsObj => {
203
+ if (varsObj && 'SERVICE_DOMAIN' in varsObj) {
204
+ varsObj.SERVICE_DOMAIN = customerPrefix;
205
+ console.log(` ✅ Set SERVICE_DOMAIN to ${customerPrefix}`);
206
+ }
207
+ };
208
+
209
+ // Update top-level vars if they exist
210
+ if (config.vars) {
211
+ updateServiceDomain(config.vars);
212
+ }
213
+
214
+ // Ensure environment section exists
215
+ if (environment !== 'production') {
216
+ if (!config.env) {
217
+ config.env = {};
218
+ }
219
+ if (!config.env[environment]) {
220
+ config.env[environment] = {
221
+ name: config.name || 'worker'
222
+ };
223
+ }
224
+ }
225
+
226
+ // Update worker name and SERVICE_DOMAIN in all environment sections
227
+ if (config.env) {
228
+ for (const [envName, envConfig] of Object.entries(config.env)) {
229
+ if (envConfig.name) {
230
+ const baseName = envConfig.name.replace(/^[^-]+-/, ''); // Remove existing prefix
231
+ envConfig.name = `${customerPrefix}-${baseName}`;
232
+ console.log(` ✅ Set [env.${envName}] worker name to ${envConfig.name}`);
233
+ }
234
+ if (envConfig.vars) {
235
+ updateServiceDomain(envConfig.vars);
236
+ }
237
+ }
238
+ }
239
+
240
+ // Write customer config
241
+ const tomlContent = stringifyToml(config);
242
+ await writeFile(customerConfigPath, tomlContent, 'utf-8');
243
+ console.log(` ✅ Customer config saved: ${customerConfigPath}`);
244
+ return customerConfigPath;
245
+ }
246
+
247
+ /**
248
+ * Copy customer-specific wrangler.toml to root (ephemeral working copy)
249
+ * This implements the architecture where:
250
+ * - config/customers/{customer}/wrangler.toml = source of truth (versioned)
251
+ * - wrangler.toml (root) = ephemeral working copy (reflects last deployment)
252
+ *
253
+ * @param {string} customerConfigPath - Path to customer's wrangler.toml
254
+ * @returns {Promise<void>}
255
+ */
256
+ async copyCustomerConfig(customerConfigPath) {
257
+ if (!customerConfigPath) {
258
+ throw new Error('Customer config path is required');
259
+ }
260
+ if (this.dryRun) {
261
+ console.log(` 🔍 DRY RUN: Would copy ${customerConfigPath} → ${this.configPath}`);
262
+ return;
263
+ }
264
+ try {
265
+ // Read customer config
266
+ const customerContent = await readFile(customerConfigPath, 'utf-8');
267
+ if (this.verbose) {
268
+ console.log(` 📋 Copying customer config: ${customerConfigPath}`);
269
+ }
270
+
271
+ // Write to root wrangler.toml (ephemeral working copy)
272
+ await mkdir(dirname(this.configPath), {
273
+ recursive: true
274
+ });
275
+ await writeFile(this.configPath, customerContent, 'utf-8');
276
+ console.log(` ✅ Copied customer config to root wrangler.toml`);
277
+ } catch (error) {
278
+ if (error.code === 'ENOENT') {
279
+ throw new Error(`Customer config not found: ${customerConfigPath}`);
280
+ }
281
+ throw new Error(`Failed to copy customer config: ${error.message}`);
282
+ }
283
+ }
284
+
99
285
  /**
100
286
  * Set account_id in wrangler.toml
101
287
  * @param {string} accountId - Cloudflare account ID
@@ -186,44 +372,31 @@ export class WranglerConfigManager {
186
372
  database_name: databaseName,
187
373
  database_id: databaseId
188
374
  };
189
- if (environment === 'production') {
190
- // Add to top-level d1_databases array
191
- if (!config.d1_databases) {
192
- config.d1_databases = [];
193
- }
194
375
 
195
- // Check if binding already exists
196
- const existingIndex = config.d1_databases.findIndex(db => db.binding === binding || db.database_name === databaseName);
197
- if (existingIndex >= 0) {
198
- console.log(` 🔄 Updating existing database binding`);
199
- config.d1_databases[existingIndex] = dbBinding;
200
- } else {
201
- console.log(` ➕ Adding new database binding`);
202
- config.d1_databases.push(dbBinding);
203
- }
204
- } else {
205
- // Add to environment-specific section
206
- if (!config.env) {
207
- config.env = {};
208
- }
209
- if (!config.env[environment]) {
210
- config.env[environment] = {
211
- name: config.name || 'worker'
212
- };
213
- }
214
- if (!config.env[environment].d1_databases) {
215
- config.env[environment].d1_databases = [];
216
- }
376
+ // Always add to environment-specific section for consistency
377
+ // This ensures database bindings are scoped to their environment
378
+ if (!config.env) {
379
+ config.env = {};
380
+ }
381
+ if (!config.env[environment]) {
382
+ // Create environment section with proper name
383
+ const envName = environment === 'production' ? `${config.name}-prod` : config.name || 'worker';
384
+ config.env[environment] = {
385
+ name: envName
386
+ };
387
+ }
388
+ if (!config.env[environment].d1_databases) {
389
+ config.env[environment].d1_databases = [];
390
+ }
217
391
 
218
- // Check if binding already exists
219
- const existingIndex = config.env[environment].d1_databases.findIndex(db => db.binding === binding || db.database_name === databaseName);
220
- if (existingIndex >= 0) {
221
- console.log(` 🔄 Updating existing database binding`);
222
- config.env[environment].d1_databases[existingIndex] = dbBinding;
223
- } else {
224
- console.log(` ➕ Adding new database binding`);
225
- config.env[environment].d1_databases.push(dbBinding);
226
- }
392
+ // Check if binding already exists
393
+ const existingIndex = config.env[environment].d1_databases.findIndex(db => db.binding === binding || db.database_name === databaseName);
394
+ if (existingIndex >= 0) {
395
+ console.log(` 🔄 Updating existing database binding`);
396
+ config.env[environment].d1_databases[existingIndex] = dbBinding;
397
+ } else {
398
+ console.log(` ➕ Adding new database binding`);
399
+ config.env[environment].d1_databases.push(dbBinding);
227
400
  }
228
401
  await this.writeConfig(config);
229
402
  console.log(` ✅ D1 database binding added successfully`);
@@ -239,18 +412,12 @@ export class WranglerConfigManager {
239
412
  async removeDatabaseBinding(environment, bindingOrName) {
240
413
  const config = await this.readConfig();
241
414
  let removed = false;
242
- if (environment === 'production') {
243
- if (config.d1_databases) {
244
- const initialLength = config.d1_databases.length;
245
- config.d1_databases = config.d1_databases.filter(db => db.binding !== bindingOrName && db.database_name !== bindingOrName);
246
- removed = config.d1_databases.length < initialLength;
247
- }
248
- } else {
249
- if (config.env?.[environment]?.d1_databases) {
250
- const initialLength = config.env[environment].d1_databases.length;
251
- config.env[environment].d1_databases = config.env[environment].d1_databases.filter(db => db.binding !== bindingOrName && db.database_name !== bindingOrName);
252
- removed = config.env[environment].d1_databases.length < initialLength;
253
- }
415
+
416
+ // Always remove from environment-specific section
417
+ if (config.env?.[environment]?.d1_databases) {
418
+ const initialLength = config.env[environment].d1_databases.length;
419
+ config.env[environment].d1_databases = config.env[environment].d1_databases.filter(db => db.binding !== bindingOrName && db.database_name !== bindingOrName);
420
+ removed = config.env[environment].d1_databases.length < initialLength;
254
421
  }
255
422
  if (removed) {
256
423
  await this.writeConfig(config);
@@ -5,4 +5,4 @@
5
5
  * Gets compiled to dist/ for published package.
6
6
  */
7
7
 
8
- export { FileManager } from '../bin/shared/utils/file-manager.js';
8
+ export { FileManager } from '../lib/shared/utils/file-manager.js';
@@ -5,4 +5,4 @@
5
5
  * Gets compiled to dist/ for published package.
6
6
  */
7
7
 
8
- export { NameFormatters } from '../bin/shared/utils/Formatters.js';
8
+ export { NameFormatters } from '../lib/shared/utils/formatters.js';
@@ -45,7 +45,7 @@ export class FrameworkConfig {
45
45
  }
46
46
 
47
47
  // Return null instead of throwing - will use default config
48
- console.warn('⚠️ validation-config.json not found. Using default configuration values.');
48
+ // Note: This is expected behavior - services don't need their own config
49
49
  return null;
50
50
  }
51
51
 
@@ -54,8 +54,8 @@ export class FrameworkConfig {
54
54
  */
55
55
  loadConfig() {
56
56
  // If no config file found, return default configuration
57
+ // This is normal - services use framework defaults unless they need custom settings
57
58
  if (!this.configPath) {
58
- console.log('📋 Using default framework configuration');
59
59
  return this.getDefaultConfig();
60
60
  }
61
61
  try {
@@ -98,64 +98,15 @@ export function showProgress(message, steps = ['⏳', '⚡', '✅']) {
98
98
 
99
99
  /**
100
100
  * Ask for sensitive input (like API tokens) with hidden input
101
- * CRITICAL: Properly restores stdin state for subsequent readline operations
101
+ * Uses inquirer for better paste support and robust input handling
102
102
  */
103
- export function askPassword(question) {
104
- return new Promise(resolve => {
105
- const prompt = `${question}: `;
106
- process.stdout.write(prompt);
107
-
108
- // Save original state
109
- const wasRaw = process.stdin.isRaw;
110
- const wasPaused = process.stdin.isPaused();
111
-
112
- // Hide input for sensitive data
113
- if (process.stdin.isTTY) {
114
- process.stdin.setRawMode(true);
115
- }
116
- process.stdin.resume();
117
- let password = '';
118
- const onData = char => {
119
- const charCode = char[0];
120
- if (charCode === 13 || charCode === 10) {
121
- // Enter key (CR or LF)
122
- // Restore original state BEFORE resolving
123
- if (process.stdin.isTTY) {
124
- process.stdin.setRawMode(wasRaw || false);
125
- }
126
-
127
- // Important: Resume stdin so readline can use it
128
- if (!wasPaused) {
129
- process.stdin.resume();
130
- } else {
131
- process.stdin.pause();
132
- }
133
- process.stdin.removeListener('data', onData);
134
- process.stdout.write('\n');
135
-
136
- // Small delay to let stdin stabilize before next readline operation
137
- setTimeout(() => resolve(password), 50);
138
- } else if (charCode === 127 || charCode === 8) {
139
- // Backspace
140
- if (password.length > 0) {
141
- password = password.slice(0, -1);
142
- process.stdout.write('\b \b');
143
- }
144
- } else if (charCode === 3) {
145
- // Ctrl+C
146
- // Restore state and exit gracefully
147
- if (process.stdin.isTTY) {
148
- process.stdin.setRawMode(wasRaw || false);
149
- }
150
- process.stdin.removeListener('data', onData);
151
- process.stdout.write('\n');
152
- process.exit(0);
153
- } else if (charCode >= 32 && charCode <= 126) {
154
- // Printable characters
155
- password += char.toString();
156
- process.stdout.write('*');
157
- }
158
- };
159
- process.stdin.on('data', onData);
160
- });
103
+ export async function askPassword(question) {
104
+ const inquirer = (await import('inquirer')).default;
105
+ const answers = await inquirer.prompt([{
106
+ type: 'password',
107
+ name: 'password',
108
+ message: question,
109
+ mask: '*'
110
+ }]);
111
+ return answers.password;
161
112
  }
@@ -5,4 +5,4 @@
5
5
  * Gets compiled to dist/ for published package.
6
6
  */
7
7
 
8
- export { Logger } from '../bin/shared/logging/Logger.js';
8
+ export { Logger } from '../lib/shared/logging/Logger.js';
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { featureManager, FEATURES } from '../config/FeatureManager.js';
7
- import { MigrationFactory } from './MigrationAdapters.js';
7
+ import { MigrationFactory } from '../migration/MigrationAdapters.js';
8
8
 
9
9
  /**
10
10
  * Version Detection and Compatibility Manager
@@ -19,12 +19,46 @@ export class VersionDetector {
19
19
  this._detectCurrentVersion();
20
20
  }
21
21
 
22
+ /**
23
+ * Detect current version on initialization
24
+ * @private
25
+ */
26
+ _detectCurrentVersion() {
27
+ try {
28
+ this.currentVersion = this._performVersionDetection();
29
+ if (this.currentVersion && this.currentVersion.version) {
30
+ // Parse version string to extract major, minor, patch
31
+ const versionParts = this.currentVersion.version.split('.').map(Number);
32
+ this.currentVersion.major = versionParts[0] || 0;
33
+ this.currentVersion.minor = versionParts[1] || 0;
34
+ this.currentVersion.patch = versionParts[2] || 0;
35
+ }
36
+ } catch (error) {
37
+ // If detection fails, set a default version
38
+ this.currentVersion = {
39
+ version: '1.0.0',
40
+ major: 1,
41
+ minor: 0,
42
+ patch: 0,
43
+ type: 'basic',
44
+ confidence: 0.5,
45
+ method: 'default',
46
+ features: ['basic']
47
+ };
48
+ }
49
+ }
50
+
22
51
  /**
23
52
  * Detect the current framework version
24
53
  * @returns {Object} Version information
25
54
  */
26
55
  detectVersion() {
27
56
  if (this.currentVersion) {
57
+ // Ensure cache is set for consistency
58
+ const cacheKey = 'framework_version';
59
+ if (!this.detectionCache.has(cacheKey)) {
60
+ this.detectionCache.set(cacheKey, this.currentVersion);
61
+ }
28
62
  return this.currentVersion;
29
63
  }
30
64
  const cacheKey = 'framework_version';
@@ -32,6 +66,13 @@ export class VersionDetector {
32
66
  return this.detectionCache.get(cacheKey);
33
67
  }
34
68
  const versionInfo = this._performVersionDetection();
69
+ if (versionInfo && versionInfo.version) {
70
+ // Parse version string to extract major, minor, patch
71
+ const versionParts = versionInfo.version.split('.').map(Number);
72
+ versionInfo.major = versionParts[0] || 0;
73
+ versionInfo.minor = versionParts[1] || 0;
74
+ versionInfo.patch = versionParts[2] || 0;
75
+ }
35
76
  this.detectionCache.set(cacheKey, versionInfo);
36
77
  this.currentVersion = versionInfo;
37
78
  return versionInfo;
@@ -173,14 +214,6 @@ export class VersionDetector {
173
214
  });
174
215
  }
175
216
 
176
- /**
177
- * Detect current framework version
178
- * @private
179
- */
180
- _detectCurrentVersion() {
181
- this.currentVersion = this._performVersionDetection();
182
- }
183
-
184
217
  /**
185
218
  * Perform actual version detection
186
219
  * @private
@@ -696,6 +729,63 @@ export class VersionDetector {
696
729
  version: version.version
697
730
  };
698
731
  }
732
+
733
+ /**
734
+ * Determine migration strategy
735
+ * @private
736
+ */
737
+ _determineMigrationStrategy(currentVersion, targetVersion, compatibility) {
738
+ if (!compatibility.compatible) {
739
+ return 'incompatible';
740
+ }
741
+ return compatibility.requiresAdapters ? 'adapter_based' : 'direct';
742
+ }
743
+
744
+ /**
745
+ * Generate migration phases
746
+ * @private
747
+ */
748
+ _generateMigrationPhases(currentVersion, targetVersion) {
749
+ return [{
750
+ phase: 'backup',
751
+ description: 'Create backup of current configuration'
752
+ }, {
753
+ phase: 'adapters',
754
+ description: 'Install compatibility adapters'
755
+ }, {
756
+ phase: 'migration',
757
+ description: 'Migrate configuration and data'
758
+ }, {
759
+ phase: 'validation',
760
+ description: 'Validate migration success'
761
+ }];
762
+ }
763
+
764
+ /**
765
+ * Assess migration risks
766
+ * @private
767
+ */
768
+ _assessMigrationRisks(currentVersion, targetVersion) {
769
+ return [{
770
+ level: 'low',
771
+ description: 'Data loss risk is minimal'
772
+ }, {
773
+ level: 'medium',
774
+ description: 'Configuration changes may require manual review'
775
+ }];
776
+ }
777
+
778
+ /**
779
+ * Estimate migration timeline
780
+ * @private
781
+ */
782
+ _estimateMigrationTimeline(currentVersion, targetVersion) {
783
+ return {
784
+ estimatedHours: 2,
785
+ complexity: 'low',
786
+ requiresDowntime: false
787
+ };
788
+ }
699
789
  }
700
790
 
701
791
  // Export singleton instance
@@ -1,4 +1,4 @@
1
- import { COMMON_FEATURES, ConfigurationManager } from "../../dist/bin/shared/config/ConfigurationManager.js";
1
+ import { COMMON_FEATURES, ConfigurationManager } from '../lib/shared/config/ConfigurationManager.js';
2
2
  import { getDomainFromEnv, createEnvironmentConfig } from '../config/domains.js';
3
3
 
4
4
  // Create a singleton instance of ConfigurationManager for use in integration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamyla/clodo-framework",
3
- "version": "3.1.21",
3
+ "version": "3.1.23",
4
4
  "description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -42,16 +42,15 @@
42
42
  "./modules/security": "./dist/modules/security.js"
43
43
  },
44
44
  "bin": {
45
- "clodo-service": "./dist/bin/clodo-service.js",
46
- "clodo-create-service": "./dist/bin/service-management/create-service.js",
47
- "clodo-init-service": "./dist/bin/service-management/init-service.js",
48
- "clodo-security": "./dist/bin/security/security-cli.js"
45
+ "clodo-service": "./dist/cli/clodo-service.js",
46
+ "clodo-security": "./dist/cli/security-cli.js"
49
47
  },
50
48
  "publishConfig": {
51
49
  "access": "public"
52
50
  },
53
51
  "files": [
54
- "dist",
52
+ "dist/",
53
+ "!dist/internal/",
55
54
  "types",
56
55
  "templates",
57
56
  "ui-structures",
@@ -64,11 +63,11 @@
64
63
  "LICENSE"
65
64
  ],
66
65
  "scripts": {
67
- "build": "npm run prebuild && babel src/ --out-dir dist/ && babel bin/ --out-dir dist/bin/ --ignore 'bin/**/*.test.js' && node -e \"const fs=require('fs'); fs.cpSync('ui-structures', 'dist/ui-structures', {recursive: true});\" && npm run postbuild",
68
- "build:ci": "npm run prebuild:ci && babel src/ --out-dir dist/ && babel bin/ --out-dir dist/bin/ --ignore 'bin/**/*.test.js' && node -e \"const fs=require('fs'); fs.cpSync('ui-structures', 'dist/ui-structures', {recursive: true});\" && npm run postbuild",
66
+ "build": "npm run prebuild && babel src/ --out-dir dist/ && babel cli/ --out-dir dist/cli/ --ignore 'cli/**/*.test.js' && babel lib/ --out-dir dist/lib/ --ignore 'lib/**/*.test.js' && node -e \"const fs=require('fs'); fs.cpSync('ui-structures', 'dist/ui-structures', {recursive: true}); fs.cpSync('config', 'dist/config', {recursive: true});\" && npm run postbuild",
67
+ "build:ci": "npm run prebuild:ci && babel src/ --out-dir dist/ && babel cli/ --out-dir dist/cli/ --ignore 'cli/**/*.test.js' && babel lib/ --out-dir dist/lib/ --ignore 'lib/**/*.test.js' && node -e \"const fs=require('fs'); fs.cpSync('ui-structures', 'dist/ui-structures', {recursive: true}); fs.cpSync('config', 'dist/config', {recursive: true});\" && npm run postbuild",
69
68
  "prebuild": "npm run clean && npm run type-check",
70
69
  "prebuild:ci": "npm run clean && npm run type-check",
71
- "postbuild": "npm run check:bundle && npm run check:imports",
70
+ "postbuild": "npm run check:bundle && npm run check:imports && node scripts/utilities/fix-dist-imports.js",
72
71
  "clean": "rimraf dist",
73
72
  "clean:generated": "rimraf generated",
74
73
  "clean:all": "npm run clean && npm run clean:generated",
@@ -147,6 +146,7 @@
147
146
  "@iarna/toml": "^2.2.5",
148
147
  "chalk": "^5.3.0",
149
148
  "commander": "^11.0.0",
149
+ "inquirer": "^12.10.0",
150
150
  "uuid": "^13.0.0",
151
151
  "wrangler": ">=3.0.0"
152
152
  },
@@ -157,7 +157,7 @@
157
157
  "@babel/preset-env": "^7.23.0",
158
158
  "@semantic-release/changelog": "^6.0.3",
159
159
  "@semantic-release/git": "^10.0.1",
160
- "@types/node": "^20.19.19",
160
+ "@types/node": "^20.19.24",
161
161
  "babel-plugin-transform-import-meta": "^2.3.3",
162
162
  "cross-env": "^10.1.0",
163
163
  "eslint": "^8.54.0",