@tamyla/clodo-framework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +564 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1393 -0
  4. package/bin/README.md +71 -0
  5. package/bin/clodo-service.js +416 -0
  6. package/bin/security/security-cli.js +96 -0
  7. package/bin/service-management/README.md +74 -0
  8. package/bin/service-management/create-service.js +129 -0
  9. package/bin/service-management/init-service.js +102 -0
  10. package/bin/service-management/init-service.js.backup +889 -0
  11. package/bin/shared/config/customer-cli.js +293 -0
  12. package/dist/config/ConfigurationManager.js +159 -0
  13. package/dist/config/CustomerConfigCLI.js +220 -0
  14. package/dist/config/FeatureManager.js +426 -0
  15. package/dist/config/customers.js +441 -0
  16. package/dist/config/domains.js +180 -0
  17. package/dist/config/features.js +225 -0
  18. package/dist/config/index.js +6 -0
  19. package/dist/database/database-orchestrator.js +730 -0
  20. package/dist/database/index.js +4 -0
  21. package/dist/deployment/auditor.js +971 -0
  22. package/dist/deployment/index.js +10 -0
  23. package/dist/deployment/rollback-manager.js +523 -0
  24. package/dist/deployment/testers/api-tester.js +80 -0
  25. package/dist/deployment/testers/auth-tester.js +129 -0
  26. package/dist/deployment/testers/core.js +217 -0
  27. package/dist/deployment/testers/database-tester.js +105 -0
  28. package/dist/deployment/testers/index.js +74 -0
  29. package/dist/deployment/testers/load-tester.js +120 -0
  30. package/dist/deployment/testers/performance-tester.js +105 -0
  31. package/dist/deployment/validator.js +558 -0
  32. package/dist/deployment/wrangler-deployer.js +574 -0
  33. package/dist/handlers/GenericRouteHandler.js +532 -0
  34. package/dist/index.js +39 -0
  35. package/dist/migration/MigrationAdapters.js +562 -0
  36. package/dist/modules/ModuleManager.js +668 -0
  37. package/dist/modules/security.js +98 -0
  38. package/dist/orchestration/cross-domain-coordinator.js +1083 -0
  39. package/dist/orchestration/index.js +5 -0
  40. package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
  41. package/dist/orchestration/modules/DomainResolver.js +196 -0
  42. package/dist/orchestration/modules/StateManager.js +332 -0
  43. package/dist/orchestration/multi-domain-orchestrator.js +255 -0
  44. package/dist/routing/EnhancedRouter.js +158 -0
  45. package/dist/schema/SchemaManager.js +778 -0
  46. package/dist/security/ConfigurationValidator.js +490 -0
  47. package/dist/security/DeploymentManager.js +208 -0
  48. package/dist/security/SecretGenerator.js +142 -0
  49. package/dist/security/SecurityCLI.js +228 -0
  50. package/dist/security/index.js +51 -0
  51. package/dist/security/patterns/environment-rules.js +66 -0
  52. package/dist/security/patterns/insecure-patterns.js +21 -0
  53. package/dist/service-management/ConfirmationEngine.js +411 -0
  54. package/dist/service-management/ErrorTracker.js +294 -0
  55. package/dist/service-management/GenerationEngine.js +3109 -0
  56. package/dist/service-management/InputCollector.js +237 -0
  57. package/dist/service-management/ServiceCreator.js +229 -0
  58. package/dist/service-management/ServiceInitializer.js +448 -0
  59. package/dist/service-management/ServiceOrchestrator.js +638 -0
  60. package/dist/service-management/handlers/ConfigMutator.js +130 -0
  61. package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
  62. package/dist/service-management/handlers/GenerationHandler.js +80 -0
  63. package/dist/service-management/handlers/InputHandler.js +59 -0
  64. package/dist/service-management/handlers/ValidationHandler.js +203 -0
  65. package/dist/service-management/index.js +7 -0
  66. package/dist/services/GenericDataService.js +488 -0
  67. package/dist/shared/cloudflare/domain-discovery.js +562 -0
  68. package/dist/shared/cloudflare/domain-manager.js +912 -0
  69. package/dist/shared/cloudflare/index.js +8 -0
  70. package/dist/shared/cloudflare/ops.js +387 -0
  71. package/dist/shared/config/cache.js +1167 -0
  72. package/dist/shared/config/command-config-manager.js +174 -0
  73. package/dist/shared/config/customer-cli.js +258 -0
  74. package/dist/shared/config/index.js +9 -0
  75. package/dist/shared/config/manager.js +289 -0
  76. package/dist/shared/database/connection-manager.js +338 -0
  77. package/dist/shared/database/index.js +7 -0
  78. package/dist/shared/database/orchestrator.js +632 -0
  79. package/dist/shared/deployment/auditor.js +971 -0
  80. package/dist/shared/deployment/index.js +10 -0
  81. package/dist/shared/deployment/rollback-manager.js +523 -0
  82. package/dist/shared/deployment/validator.js +558 -0
  83. package/dist/shared/index.js +32 -0
  84. package/dist/shared/monitoring/health-checker.js +250 -0
  85. package/dist/shared/monitoring/index.js +8 -0
  86. package/dist/shared/monitoring/memory-manager.js +382 -0
  87. package/dist/shared/monitoring/production-monitor.js +390 -0
  88. package/dist/shared/production-tester/api-tester.js +80 -0
  89. package/dist/shared/production-tester/auth-tester.js +129 -0
  90. package/dist/shared/production-tester/core.js +217 -0
  91. package/dist/shared/production-tester/database-tester.js +105 -0
  92. package/dist/shared/production-tester/index.js +74 -0
  93. package/dist/shared/production-tester/load-tester.js +120 -0
  94. package/dist/shared/production-tester/performance-tester.js +105 -0
  95. package/dist/shared/security/api-token-manager.js +296 -0
  96. package/dist/shared/security/index.js +8 -0
  97. package/dist/shared/security/secret-generator.js +918 -0
  98. package/dist/shared/security/secure-token-manager.js +379 -0
  99. package/dist/shared/utils/error-recovery.js +240 -0
  100. package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
  101. package/dist/shared/utils/index.js +9 -0
  102. package/dist/shared/utils/interactive-prompts.js +134 -0
  103. package/dist/shared/utils/rate-limiter.js +249 -0
  104. package/dist/utils/ErrorHandler.js +173 -0
  105. package/dist/utils/deployment/config-cache.js +1160 -0
  106. package/dist/utils/deployment/index.js +6 -0
  107. package/dist/utils/deployment/interactive-prompts.js +97 -0
  108. package/dist/utils/deployment/secret-generator.js +896 -0
  109. package/dist/utils/dirname-helper.js +35 -0
  110. package/dist/utils/domain-config.js +159 -0
  111. package/dist/utils/error-recovery.js +240 -0
  112. package/dist/utils/esm-helper.js +52 -0
  113. package/dist/utils/framework-config.js +481 -0
  114. package/dist/utils/graceful-shutdown-manager.js +379 -0
  115. package/dist/utils/health-checker.js +114 -0
  116. package/dist/utils/index.js +36 -0
  117. package/dist/utils/prompt-handler.js +98 -0
  118. package/dist/utils/usage-tracker.js +252 -0
  119. package/dist/utils/validation.js +112 -0
  120. package/dist/version/VersionDetector.js +723 -0
  121. package/dist/worker/index.js +4 -0
  122. package/dist/worker/integration.js +332 -0
  123. package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
  124. package/docs/INTEGRATION_GUIDE.md +2045 -0
  125. package/docs/README.md +82 -0
  126. package/docs/SECURITY.md +242 -0
  127. package/docs/deployment/deployment-guide.md +540 -0
  128. package/docs/overview.md +280 -0
  129. package/package.json +176 -0
  130. package/types/index.d.ts +575 -0
@@ -0,0 +1,411 @@
1
+ /**
2
+ * ConfirmationEngine - Tier 2: Smart Confirmations
3
+ *
4
+ * Takes the 6 core inputs and generates 15 derived confirmation values
5
+ * that users can review and modify before service generation.
6
+ *
7
+ * Core Inputs (6):
8
+ * 1. Service Name
9
+ * 2. Service Type
10
+ * 3. Domain Name
11
+ * 4. Cloudflare Token
12
+ * 5. Cloudflare Account ID
13
+ * 6. Cloudflare Zone ID
14
+ * 7. Environment
15
+ *
16
+ * Derived Confirmations (15):
17
+ * 1. Display Name
18
+ * 2. Description
19
+ * 3. Version
20
+ * 4. Author
21
+ * 5. Production URL
22
+ * 6. Staging URL
23
+ * 7. Development URL
24
+ * 8. Features Configuration
25
+ * 9. Database Name
26
+ * 10. Worker Name
27
+ * 11. Package Name
28
+ * 12. Git Repository URL
29
+ * 13. Documentation URL
30
+ * 14. Health Check Path
31
+ * 15. API Base Path
32
+ */
33
+
34
+ import { createInterface } from 'readline';
35
+ import chalk from 'chalk';
36
+ import { validateServiceName, validateDomainName } from '../utils/validation.js';
37
+ export class ConfirmationEngine {
38
+ constructor(options = {}) {
39
+ this.interactive = options.interactive !== false;
40
+ this.rl = this.interactive ? createInterface({
41
+ input: process.stdin,
42
+ output: process.stdout
43
+ }) : null;
44
+ }
45
+
46
+ /**
47
+ * Generate and confirm all derived values from core inputs
48
+ */
49
+ async generateAndConfirm(coreInputs) {
50
+ console.log(chalk.cyan('\n🔍 Tier 2: Smart Confirmations'));
51
+ console.log(chalk.white('Reviewing and confirming 15 derived configuration values...\n'));
52
+
53
+ // Generate smart defaults
54
+ const derivedValues = this.generateSmartDefaults(coreInputs);
55
+
56
+ // Interactive confirmation if enabled
57
+ if (this.interactive) {
58
+ return await this.interactiveConfirmation(derivedValues, coreInputs);
59
+ } else {
60
+ console.log(chalk.gray('⚠️ Non-interactive mode: Using generated defaults'));
61
+ return derivedValues;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Generate smart defaults for all 15 confirmation values
67
+ */
68
+ generateSmartDefaults(coreInputs) {
69
+ const {
70
+ serviceName,
71
+ serviceType,
72
+ domainName,
73
+ environment
74
+ } = coreInputs;
75
+ return {
76
+ // 1. Display Name - Convert kebab-case to Title Case
77
+ displayName: this.generateDisplayName(serviceName),
78
+ // 2. Description - Based on service type
79
+ description: this.generateDescription(serviceType),
80
+ // 3. Version - Standard semantic versioning
81
+ version: '1.0.0',
82
+ // 4. Author - Default framework author
83
+ author: 'Clodo Framework',
84
+ // 5-7. URLs - Derived from domain
85
+ productionUrl: `https://api.${domainName}`,
86
+ stagingUrl: `https://staging-api.${domainName}`,
87
+ developmentUrl: `https://dev-api.${domainName}`,
88
+ // 8. Features - Based on service type
89
+ features: this.generateFeaturesForType(serviceType),
90
+ // 9. Database Name - Cloudflare D1 naming
91
+ databaseName: `${serviceName}-db`,
92
+ // 10. Worker Name - Cloudflare Worker naming
93
+ workerName: `${serviceName}-worker`,
94
+ // 11. Package Name - NPM package naming
95
+ packageName: `@clodo/${serviceName}`,
96
+ // 12. Git Repository URL - GitHub naming
97
+ gitRepositoryUrl: `https://github.com/tamylaa/${serviceName}`,
98
+ // 13. Documentation URL - Based on domain
99
+ documentationUrl: `https://docs.${domainName}`,
100
+ // 14. Health Check Path - Standard health endpoint
101
+ healthCheckPath: '/health',
102
+ // 15. API Base Path - Service-specific API path
103
+ apiBasePath: `/api/v1/${serviceName.replace('-', '/')}`
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Interactive confirmation process
109
+ */
110
+ async interactiveConfirmation(derivedValues, coreInputs) {
111
+ console.log(chalk.cyan('Please review and confirm the following derived values:'));
112
+ console.log(chalk.gray('Press Enter to accept default, or type new value to modify.\n'));
113
+ const confirmed = {
114
+ ...derivedValues
115
+ };
116
+
117
+ // Group confirmations for better UX
118
+ const confirmationGroups = [{
119
+ title: '📋 Basic Information',
120
+ items: [{
121
+ key: 'displayName',
122
+ label: 'Display Name',
123
+ description: 'Human-readable service name'
124
+ }, {
125
+ key: 'description',
126
+ label: 'Description',
127
+ description: 'Service description'
128
+ }, {
129
+ key: 'version',
130
+ label: 'Version',
131
+ description: 'Initial version number'
132
+ }, {
133
+ key: 'author',
134
+ label: 'Author',
135
+ description: 'Service author/maintainer'
136
+ }]
137
+ }, {
138
+ title: '🌐 URLs & Endpoints',
139
+ items: [{
140
+ key: 'productionUrl',
141
+ label: 'Production URL',
142
+ description: 'Live production endpoint'
143
+ }, {
144
+ key: 'stagingUrl',
145
+ label: 'Staging URL',
146
+ description: 'Staging environment endpoint'
147
+ }, {
148
+ key: 'developmentUrl',
149
+ label: 'Development URL',
150
+ description: 'Development environment endpoint'
151
+ }, {
152
+ key: 'documentationUrl',
153
+ label: 'Documentation URL',
154
+ description: 'API documentation location'
155
+ }]
156
+ }, {
157
+ title: '⚙️ Service Configuration',
158
+ items: [{
159
+ key: 'databaseName',
160
+ label: 'Database Name',
161
+ description: 'Cloudflare D1 database name'
162
+ }, {
163
+ key: 'workerName',
164
+ label: 'Worker Name',
165
+ description: 'Cloudflare Worker script name'
166
+ }, {
167
+ key: 'packageName',
168
+ label: 'Package Name',
169
+ description: 'NPM package identifier'
170
+ }, {
171
+ key: 'healthCheckPath',
172
+ label: 'Health Check Path',
173
+ description: 'Health endpoint path'
174
+ }, {
175
+ key: 'apiBasePath',
176
+ label: 'API Base Path',
177
+ description: 'Base path for API endpoints'
178
+ }]
179
+ }];
180
+ for (const group of confirmationGroups) {
181
+ console.log(chalk.yellow(`\n${group.title}`));
182
+ console.log(chalk.gray('─'.repeat(50)));
183
+ for (const item of group.items) {
184
+ const currentValue = confirmed[item.key];
185
+ const newValue = await this.confirmValue(`${item.label} (${item.description})`, currentValue);
186
+ if (newValue !== null && newValue !== currentValue) {
187
+ // Validate the new value if it's a URL or name
188
+ if (this.validateConfirmationValue(item.key, newValue)) {
189
+ confirmed[item.key] = newValue;
190
+ console.log(chalk.green(`✓ Updated: ${newValue}`));
191
+ } else {
192
+ console.log(chalk.red(`✗ Invalid value, keeping: ${currentValue}`));
193
+ }
194
+ } else {
195
+ console.log(chalk.gray(`✓ Keeping: ${currentValue}`));
196
+ }
197
+ }
198
+ }
199
+
200
+ // Special handling for features
201
+ console.log(chalk.yellow('\n🔧 Feature Configuration'));
202
+ console.log(chalk.gray('─'.repeat(50)));
203
+ console.log(chalk.white('Current features for service type:'), chalk.cyan(coreInputs.serviceType));
204
+ this.displayFeatures(confirmed.features);
205
+ const modifyFeatures = await this.confirmYesNo('\nWould you like to modify feature flags?', false);
206
+ if (modifyFeatures) {
207
+ confirmed.features = await this.interactiveFeatureConfiguration(confirmed.features, coreInputs.serviceType);
208
+ }
209
+ return confirmed;
210
+ }
211
+
212
+ /**
213
+ * Confirm a single value with user
214
+ */
215
+ async confirmValue(prompt, currentValue) {
216
+ return new Promise(resolve => {
217
+ const displayValue = typeof currentValue === 'object' ? JSON.stringify(currentValue, null, 2) : String(currentValue);
218
+ this.rl.question(`${prompt} [${displayValue}]: `, answer => {
219
+ resolve(answer.trim() || null);
220
+ });
221
+ });
222
+ }
223
+
224
+ /**
225
+ * Confirm yes/no question
226
+ */
227
+ async confirmYesNo(question, defaultValue = true) {
228
+ return new Promise(resolve => {
229
+ const defaultText = defaultValue ? '[Y/n]' : '[y/N]';
230
+ this.rl.question(`${question} ${defaultText}: `, answer => {
231
+ const normalized = answer.toLowerCase().trim();
232
+ if (normalized === '') {
233
+ resolve(defaultValue);
234
+ } else if (normalized === 'y' || normalized === 'yes') {
235
+ resolve(true);
236
+ } else if (normalized === 'n' || normalized === 'no') {
237
+ resolve(false);
238
+ } else {
239
+ resolve(defaultValue);
240
+ }
241
+ });
242
+ });
243
+ }
244
+
245
+ /**
246
+ * Interactive feature configuration
247
+ */
248
+ async interactiveFeatureConfiguration(currentFeatures, serviceType) {
249
+ console.log(chalk.cyan('\nFeature Configuration:'));
250
+ console.log(chalk.white('Type feature name to toggle, or "done" to finish'));
251
+ const features = {
252
+ ...currentFeatures
253
+ };
254
+ for (;;) {
255
+ console.log(chalk.gray('\nCurrent features:'));
256
+ this.displayFeatures(features);
257
+ const input = await new Promise(resolve => {
258
+ this.rl.question('Feature to toggle (or "done"): ', resolve);
259
+ });
260
+ const feature = input.trim().toLowerCase();
261
+ if (feature === 'done' || feature === '') {
262
+ break;
263
+ }
264
+ if (Object.prototype.hasOwnProperty.call(features, feature)) {
265
+ features[feature] = !features[feature];
266
+ console.log(chalk.green(`✓ ${feature}: ${features[feature] ? 'ENABLED' : 'DISABLED'}`));
267
+ } else {
268
+ console.log(chalk.red(`✗ Unknown feature: ${feature}`));
269
+ console.log(chalk.gray('Available features:'), Object.keys(features).join(', '));
270
+ }
271
+ }
272
+ return features;
273
+ }
274
+
275
+ /**
276
+ * Display features in a nice format
277
+ */
278
+ displayFeatures(features) {
279
+ const enabled = Object.entries(features).filter(([, enabled]) => enabled).map(([name]) => name);
280
+ const disabled = Object.entries(features).filter(([, enabled]) => !enabled).map(([name]) => name);
281
+ if (enabled.length > 0) {
282
+ console.log(chalk.green(' Enabled:'), enabled.join(', '));
283
+ }
284
+ if (disabled.length > 0) {
285
+ console.log(chalk.gray(' Disabled:'), disabled.join(', '));
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Validate confirmation value based on type
291
+ */
292
+ validateConfirmationValue(key, value) {
293
+ switch (key) {
294
+ case 'displayName':
295
+ return value.length > 0 && value.length <= 100;
296
+ case 'description':
297
+ return value.length > 0 && value.length <= 500;
298
+ case 'version':
299
+ return /^\d+\.\d+\.\d+$/.test(value);
300
+ case 'productionUrl':
301
+ case 'stagingUrl':
302
+ case 'developmentUrl':
303
+ case 'documentationUrl':
304
+ return /^https?:\/\/.+/.test(value);
305
+ case 'databaseName':
306
+ case 'workerName':
307
+ return validateServiceName(value);
308
+ case 'packageName':
309
+ return /^@?[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/.test(value);
310
+ case 'healthCheckPath':
311
+ case 'apiBasePath':
312
+ return value.startsWith('/');
313
+ default:
314
+ return true;
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Generate display name from service name
320
+ */
321
+ generateDisplayName(serviceName) {
322
+ return serviceName.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
323
+ }
324
+
325
+ /**
326
+ * Generate description based on service type
327
+ */
328
+ generateDescription(serviceType) {
329
+ const descriptions = {
330
+ 'data-service': 'A comprehensive data service providing CRUD operations, search, filtering, and pagination capabilities',
331
+ 'auth-service': 'Authentication and authorization service with user management and security features',
332
+ 'content-service': 'Content management service with file storage, search, and delivery capabilities',
333
+ 'api-gateway': 'API gateway providing routing, rate limiting, authentication, and monitoring',
334
+ 'generic': 'A Clodo Framework service providing core functionality and extensibility'
335
+ };
336
+ return descriptions[serviceType] || descriptions.generic;
337
+ }
338
+
339
+ /**
340
+ * Generate features based on service type
341
+ */
342
+ generateFeaturesForType(serviceType) {
343
+ const baseFeatures = {
344
+ logging: true,
345
+ monitoring: true,
346
+ errorReporting: true,
347
+ metrics: true,
348
+ healthChecks: true
349
+ };
350
+ const typeSpecificFeatures = {
351
+ 'data-service': {
352
+ authentication: true,
353
+ authorization: true,
354
+ database: true,
355
+ search: true,
356
+ filtering: true,
357
+ pagination: true,
358
+ caching: true,
359
+ backup: true
360
+ },
361
+ 'auth-service': {
362
+ authentication: true,
363
+ authorization: true,
364
+ userProfiles: true,
365
+ emailNotifications: true,
366
+ magicLinkAuth: true,
367
+ passwordReset: true,
368
+ sessionManagement: true,
369
+ rateLimiting: true
370
+ },
371
+ 'content-service': {
372
+ fileStorage: true,
373
+ search: true,
374
+ filtering: true,
375
+ pagination: true,
376
+ caching: true,
377
+ cdn: true,
378
+ imageProcessing: true,
379
+ metadata: true
380
+ },
381
+ 'api-gateway': {
382
+ authentication: true,
383
+ authorization: true,
384
+ rateLimiting: true,
385
+ caching: true,
386
+ monitoring: true,
387
+ loadBalancing: true,
388
+ requestRouting: true,
389
+ responseTransformation: true
390
+ },
391
+ 'generic': {
392
+ extensibility: true,
393
+ configuration: true,
394
+ deployment: true
395
+ }
396
+ };
397
+ return {
398
+ ...baseFeatures,
399
+ ...(typeSpecificFeatures[serviceType] || typeSpecificFeatures.generic)
400
+ };
401
+ }
402
+
403
+ /**
404
+ * Close readline interface
405
+ */
406
+ close() {
407
+ if (this.rl) {
408
+ this.rl.close();
409
+ }
410
+ }
411
+ }
@@ -0,0 +1,294 @@
1
+ /**
2
+ * ErrorTracker - Comprehensive Error Handling and Recovery System
3
+ *
4
+ * Captures failures, tracks input states, and provides recovery mechanisms
5
+ * for the Clodo Framework service lifecycle management.
6
+ */
7
+
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ import chalk from 'chalk';
11
+ export class ErrorTracker {
12
+ constructor() {
13
+ this.errors = [];
14
+ this.errorLogPath = './clodo-service-errors.log';
15
+ this.maxErrors = 100; // Keep last 100 errors
16
+ }
17
+
18
+ /**
19
+ * Capture an error with context and input state
20
+ */
21
+ captureError(error, context = {}) {
22
+ const errorEntry = {
23
+ timestamp: new Date().toISOString(),
24
+ error: {
25
+ message: error.message,
26
+ stack: error.stack,
27
+ name: error.name
28
+ },
29
+ context: {
30
+ command: context.command || 'unknown',
31
+ servicePath: context.servicePath || process.cwd(),
32
+ inputState: context.inputState || {},
33
+ userInputs: context.userInputs || {},
34
+ action: context.action || 'unknown',
35
+ options: context.options || {}
36
+ },
37
+ recovery: this.generateRecoverySuggestions(error, context),
38
+ severity: this.determineSeverity(error, context)
39
+ };
40
+ this.errors.push(errorEntry);
41
+
42
+ // Keep only recent errors
43
+ if (this.errors.length > this.maxErrors) {
44
+ this.errors = this.errors.slice(-this.maxErrors);
45
+ }
46
+
47
+ // Log to file asynchronously (don't block)
48
+ this.logErrorToFile(errorEntry).catch(err => {
49
+ console.warn(chalk.yellow(`Failed to write error log: ${err.message}`));
50
+ });
51
+ return errorEntry;
52
+ }
53
+
54
+ /**
55
+ * Generate recovery suggestions based on error type and context
56
+ */
57
+ generateRecoverySuggestions(error, context) {
58
+ const suggestions = [];
59
+
60
+ // Network/API errors
61
+ if (error.message.includes('fetch') || error.message.includes('network') || error.message.includes('API')) {
62
+ suggestions.push('Check your internet connection');
63
+ suggestions.push('Verify Cloudflare API token is valid and has required permissions');
64
+ suggestions.push('Confirm Cloudflare account and zone IDs are correct');
65
+ }
66
+
67
+ // Authentication errors
68
+ if (error.message.includes('auth') || error.message.includes('token') || error.message.includes('401') || error.message.includes('403')) {
69
+ suggestions.push('Verify Cloudflare API token has not expired');
70
+ suggestions.push('Check that the token has permissions for the required operations');
71
+ suggestions.push('Regenerate API token if necessary');
72
+ }
73
+
74
+ // File system errors
75
+ if (error.message.includes('ENOENT') || error.message.includes('permission') || error.message.includes('access')) {
76
+ suggestions.push('Check file permissions on the service directory');
77
+ suggestions.push('Ensure you have write access to the target directory');
78
+ suggestions.push('Verify the service path exists and is accessible');
79
+ }
80
+
81
+ // Validation errors
82
+ if (error.message.includes('validation') || error.message.includes('invalid')) {
83
+ suggestions.push('Review input values for correctness');
84
+ suggestions.push('Use clodo-service validate <path> to check service configuration');
85
+ suggestions.push('Run clodo-service diagnose <path> for detailed issue analysis');
86
+ }
87
+
88
+ // Configuration errors
89
+ if (error.message.includes('config') || error.message.includes('configuration')) {
90
+ suggestions.push('Run clodo-service update --fix-errors to attempt automatic fixes');
91
+ suggestions.push('Check domain configuration in src/config/domains.js');
92
+ suggestions.push('Verify package.json has all required fields');
93
+ }
94
+
95
+ // Template errors
96
+ if (error.message.includes('template') || error.message.includes('variable')) {
97
+ suggestions.push('Check that all required template variables are provided');
98
+ suggestions.push('Verify template files exist and are readable');
99
+ suggestions.push('Regenerate service with clodo-service update --regenerate-configs');
100
+ }
101
+
102
+ // Service creation/update specific
103
+ if (context.action === 'create' || context.action === 'update') {
104
+ suggestions.push('Try running the command again with --non-interactive flag');
105
+ suggestions.push('Use clodo-service diagnose to identify specific issues');
106
+ suggestions.push('Check that service name follows naming conventions (lowercase, hyphens only)');
107
+ }
108
+
109
+ // Cloudflare specific
110
+ if (context.action && context.action.includes('cloudflare')) {
111
+ suggestions.push('Verify Cloudflare account has D1 database enabled');
112
+ suggestions.push('Check zone ID corresponds to the correct domain');
113
+ suggestions.push('Ensure API token has D1:Edit permission');
114
+ }
115
+
116
+ // Add generic suggestions if none specific found
117
+ if (suggestions.length === 0) {
118
+ suggestions.push('Check the error message for specific details');
119
+ suggestions.push('Review the Clodo Framework documentation');
120
+ suggestions.push('Try the operation again after reviewing inputs');
121
+ suggestions.push('Contact support if the issue persists');
122
+ }
123
+ return suggestions;
124
+ }
125
+
126
+ /**
127
+ * Determine error severity level
128
+ */
129
+ determineSeverity(error, context) {
130
+ // Critical errors that prevent operation
131
+ if (error.message.includes('authentication') || error.message.includes('permission') || error.message.includes('access denied') || error.message.includes('critical')) {
132
+ return 'critical';
133
+ }
134
+
135
+ // High severity - operation fails but may be recoverable
136
+ if (error.message.includes('network') || error.message.includes('timeout') || error.message.includes('connection') || error.message.includes('validation')) {
137
+ return 'high';
138
+ }
139
+
140
+ // Medium severity - partial failure or warnings
141
+ if (error.message.includes('warning') || error.message.includes('deprecated') || error.message.includes('not found')) {
142
+ return 'medium';
143
+ }
144
+
145
+ // Low severity - minor issues
146
+ return 'low';
147
+ }
148
+
149
+ /**
150
+ * Log error to file
151
+ */
152
+ async logErrorToFile(errorEntry) {
153
+ try {
154
+ const logEntry = JSON.stringify(errorEntry, null, 2) + '\n---\n';
155
+ await fs.appendFile(this.errorLogPath, logEntry);
156
+ } catch (error) {
157
+ // If we can't write to the log file, at least show a warning
158
+ console.warn(chalk.yellow(`Could not write to error log: ${error.message}`));
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Get recent errors
164
+ */
165
+ getRecentErrors(limit = 10) {
166
+ return this.errors.slice(-limit);
167
+ }
168
+
169
+ /**
170
+ * Get errors by severity
171
+ */
172
+ getErrorsBySeverity(severity) {
173
+ return this.errors.filter(error => error.severity === severity);
174
+ }
175
+
176
+ /**
177
+ * Get errors by command
178
+ */
179
+ getErrorsByCommand(command) {
180
+ return this.errors.filter(error => error.context.command === command);
181
+ }
182
+
183
+ /**
184
+ * Clear error history
185
+ */
186
+ clearErrors() {
187
+ this.errors = [];
188
+ }
189
+
190
+ /**
191
+ * Export error report
192
+ */
193
+ async exportErrorReport(filePath) {
194
+ const report = {
195
+ generated: new Date().toISOString(),
196
+ totalErrors: this.errors.length,
197
+ errorsBySeverity: {
198
+ critical: this.getErrorsBySeverity('critical').length,
199
+ high: this.getErrorsBySeverity('high').length,
200
+ medium: this.getErrorsBySeverity('medium').length,
201
+ low: this.getErrorsBySeverity('low').length
202
+ },
203
+ recentErrors: this.getRecentErrors(20),
204
+ summary: this.generateErrorSummary()
205
+ };
206
+ await fs.writeFile(filePath, JSON.stringify(report, null, 2), 'utf8');
207
+ }
208
+
209
+ /**
210
+ * Generate error summary
211
+ */
212
+ generateErrorSummary() {
213
+ const summary = {
214
+ mostCommonErrors: [],
215
+ mostProblematicCommands: [],
216
+ recentTrends: 'Analysis not implemented yet'
217
+ };
218
+
219
+ // Count error types
220
+ const errorCounts = {};
221
+ this.errors.forEach(error => {
222
+ const key = error.error.name || 'Unknown';
223
+ errorCounts[key] = (errorCounts[key] || 0) + 1;
224
+ });
225
+ summary.mostCommonErrors = Object.entries(errorCounts).sort(([, a], [, b]) => b - a).slice(0, 5).map(([type, count]) => ({
226
+ type,
227
+ count
228
+ }));
229
+
230
+ // Count problematic commands
231
+ const commandCounts = {};
232
+ this.errors.forEach(error => {
233
+ const command = error.context.command || 'unknown';
234
+ commandCounts[command] = (commandCounts[command] || 0) + 1;
235
+ });
236
+ summary.mostProblematicCommands = Object.entries(commandCounts).sort(([, a], [, b]) => b - a).slice(0, 5).map(([command, count]) => ({
237
+ command,
238
+ count
239
+ }));
240
+ return summary;
241
+ }
242
+
243
+ /**
244
+ * Display error summary to console
245
+ */
246
+ displayErrorSummary() {
247
+ const summary = this.generateErrorSummary();
248
+ console.log(chalk.cyan('\n📊 Error Summary'));
249
+ console.log(chalk.white(`Total Errors: ${this.errors.length}`));
250
+ if (summary.mostCommonErrors.length > 0) {
251
+ console.log(chalk.cyan('\nMost Common Errors:'));
252
+ summary.mostCommonErrors.forEach(({
253
+ type,
254
+ count
255
+ }) => {
256
+ console.log(chalk.white(` ${type}: ${count} times`));
257
+ });
258
+ }
259
+ if (summary.mostProblematicCommands.length > 0) {
260
+ console.log(chalk.cyan('\nMost Problematic Commands:'));
261
+ summary.mostProblematicCommands.forEach(({
262
+ command,
263
+ count
264
+ }) => {
265
+ console.log(chalk.white(` ${command}: ${count} errors`));
266
+ });
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Attempt automatic error recovery
272
+ */
273
+ async attemptRecovery(errorEntry) {
274
+ const recoveries = [];
275
+ try {
276
+ // Try common recovery actions based on error type
277
+ if (errorEntry.error.message.includes('permission') || errorEntry.error.message.includes('access')) {
278
+ // Try to fix permissions (limited in what we can do)
279
+ recoveries.push('Checked permissions - manual intervention may be required');
280
+ }
281
+ if (errorEntry.error.message.includes('network') || errorEntry.error.message.includes('timeout')) {
282
+ // Wait and retry logic could be implemented here
283
+ recoveries.push('Network error detected - consider retrying the operation');
284
+ }
285
+ if (errorEntry.context.action === 'validate' || errorEntry.context.action === 'create') {
286
+ // Try to validate/fix configuration
287
+ recoveries.push('Consider running clodo-service update --fix-errors');
288
+ }
289
+ } catch (recoveryError) {
290
+ recoveries.push(`Recovery attempt failed: ${recoveryError.message}`);
291
+ }
292
+ return recoveries;
293
+ }
294
+ }