@tamyla/clodo-framework 2.0.19 → 3.0.2

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 (56) hide show
  1. package/CHANGELOG.md +206 -62
  2. package/bin/clodo-service.js +32 -56
  3. package/bin/database/README.md +33 -0
  4. package/bin/database/deployment-db-manager.js +527 -0
  5. package/bin/database/enterprise-db-manager.js +736 -0
  6. package/bin/database/wrangler-d1-manager.js +775 -0
  7. package/bin/shared/cloudflare/domain-discovery.js +636 -0
  8. package/bin/shared/cloudflare/domain-manager.js +952 -0
  9. package/bin/shared/cloudflare/index.js +8 -0
  10. package/bin/shared/cloudflare/ops.js +359 -0
  11. package/bin/shared/config/index.js +1 -1
  12. package/bin/shared/database/connection-manager.js +374 -0
  13. package/bin/shared/database/index.js +7 -0
  14. package/bin/shared/database/orchestrator.js +726 -0
  15. package/bin/shared/deployment/auditor.js +969 -0
  16. package/bin/shared/deployment/index.js +10 -0
  17. package/bin/shared/deployment/rollback-manager.js +570 -0
  18. package/bin/shared/deployment/validator.js +779 -0
  19. package/bin/shared/index.js +32 -0
  20. package/bin/shared/monitoring/health-checker.js +484 -0
  21. package/bin/shared/monitoring/index.js +8 -0
  22. package/bin/shared/monitoring/memory-manager.js +387 -0
  23. package/bin/shared/monitoring/production-monitor.js +391 -0
  24. package/bin/shared/production-tester/api-tester.js +82 -0
  25. package/bin/shared/production-tester/auth-tester.js +132 -0
  26. package/bin/shared/production-tester/core.js +197 -0
  27. package/bin/shared/production-tester/database-tester.js +109 -0
  28. package/bin/shared/production-tester/index.js +77 -0
  29. package/bin/shared/production-tester/load-tester.js +131 -0
  30. package/bin/shared/production-tester/performance-tester.js +103 -0
  31. package/bin/shared/security/api-token-manager.js +312 -0
  32. package/bin/shared/security/index.js +8 -0
  33. package/bin/shared/security/secret-generator.js +937 -0
  34. package/bin/shared/security/secure-token-manager.js +398 -0
  35. package/bin/shared/utils/error-recovery.js +225 -0
  36. package/bin/shared/utils/graceful-shutdown-manager.js +390 -0
  37. package/bin/shared/utils/index.js +9 -0
  38. package/bin/shared/utils/interactive-prompts.js +146 -0
  39. package/bin/shared/utils/interactive-utils.js +530 -0
  40. package/bin/shared/utils/rate-limiter.js +246 -0
  41. package/dist/database/database-orchestrator.js +34 -12
  42. package/dist/deployment/index.js +2 -2
  43. package/dist/orchestration/multi-domain-orchestrator.js +8 -6
  44. package/dist/service-management/GenerationEngine.js +76 -28
  45. package/dist/service-management/ServiceInitializer.js +5 -3
  46. package/dist/shared/cloudflare/domain-manager.js +1 -1
  47. package/dist/shared/cloudflare/ops.js +27 -12
  48. package/dist/shared/config/index.js +1 -1
  49. package/dist/shared/deployment/index.js +2 -2
  50. package/dist/shared/security/secret-generator.js +4 -2
  51. package/dist/shared/utils/error-recovery.js +1 -1
  52. package/dist/shared/utils/graceful-shutdown-manager.js +4 -3
  53. package/dist/utils/deployment/secret-generator.js +19 -6
  54. package/package.json +7 -6
  55. package/bin/shared/config/customer-cli.js +0 -182
  56. package/dist/shared/config/customer-cli.js +0 -175
@@ -0,0 +1,530 @@
1
+ /**
2
+ * Enhanced Interactive Utils Module
3
+ * Unified interactive utilities with advanced deployment capabilities
4
+ *
5
+ * Consolidates duplicate interactive-prompts.js modules and provides:
6
+ * - Enhanced user input validation
7
+ * - Progress tracking integration
8
+ * - Multi-step workflow support
9
+ * - Deployment-specific prompts
10
+ * - Rich formatting and colors
11
+ * - Error recovery and retry logic
12
+ *
13
+ * @version 2.0.0 - Enhanced Interactive Base
14
+ */
15
+
16
+ import readline from 'readline';
17
+ import { existsSync, readFileSync } from 'fs';
18
+ import { join } from 'path';
19
+
20
+ // Singleton readline interface
21
+ let rl = null;
22
+ let isInitialized = false;
23
+
24
+ /**
25
+ * Enhanced Interactive Utils Class
26
+ */
27
+ export class InteractiveUtils {
28
+ constructor(options = {}) {
29
+ this.options = {
30
+ enableColors: options.enableColors !== false,
31
+ enableProgress: options.enableProgress !== false,
32
+ validateInputs: options.validateInputs !== false,
33
+ retryOnError: options.retryOnError !== false,
34
+ maxRetries: options.maxRetries || 3,
35
+ ...options
36
+ };
37
+
38
+ this.progressState = {
39
+ currentStep: 0,
40
+ totalSteps: 0,
41
+ stepName: '',
42
+ startTime: null
43
+ };
44
+
45
+ this.validationRules = new Map();
46
+ this.inputHistory = [];
47
+ }
48
+
49
+ /**
50
+ * Initialize readline interface
51
+ */
52
+ static getReadlineInterface() {
53
+ if (!rl) {
54
+ rl = readline.createInterface({
55
+ input: process.stdin,
56
+ output: process.stdout,
57
+ terminal: true
58
+ });
59
+
60
+ // Enhanced signal handling
61
+ rl.on('SIGINT', () => {
62
+ console.log('\\n\\n🚫 Operation cancelled by user');
63
+ process.exit(0);
64
+ });
65
+
66
+ isInitialized = true;
67
+ }
68
+ return rl;
69
+ }
70
+
71
+ /**
72
+ * Enhanced user input with validation and retry logic
73
+ */
74
+ static async askUser(question, defaultValue = null, options = {}) {
75
+ const utils = new InteractiveUtils(options);
76
+ return await utils.askUserWithValidation(question, defaultValue, options.validator);
77
+ }
78
+
79
+ /**
80
+ * Enhanced yes/no prompts with clear formatting
81
+ */
82
+ static async askYesNo(question, defaultValue = 'n', options = {}) {
83
+ const utils = new InteractiveUtils(options);
84
+
85
+ let attempts = 0;
86
+ const maxAttempts = options.maxRetries || 3;
87
+
88
+ while (attempts < maxAttempts) {
89
+ try {
90
+ const defaultDisplay = defaultValue === 'y' ? 'Y/n' : 'y/N';
91
+ const coloredQuestion = utils.formatQuestion(question);
92
+ const prompt = `${coloredQuestion} [${defaultDisplay}]: `;
93
+
94
+ const answer = await utils.promptUser(prompt);
95
+ const response = answer.trim().toLowerCase() || defaultValue;
96
+
97
+ if (['y', 'yes', 'n', 'no'].includes(response)) {
98
+ const result = response === 'y' || response === 'yes';
99
+ utils.recordInput(question, result);
100
+ return result;
101
+ } else {
102
+ attempts++;
103
+ console.log(` ⚠️ Please answer 'y' or 'n'. (Attempt ${attempts}/${maxAttempts})`);
104
+ if (attempts >= maxAttempts) {
105
+ throw new Error('Maximum retry attempts exceeded');
106
+ }
107
+ }
108
+ } catch (error) {
109
+ if (attempts >= maxAttempts - 1) throw error;
110
+ attempts++;
111
+ }
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Enhanced choice selection with better formatting
117
+ */
118
+ static async askChoice(question, choices, defaultIndex = 0, options = {}) {
119
+ const utils = new InteractiveUtils(options);
120
+
121
+ if (!Array.isArray(choices) || choices.length === 0) {
122
+ throw new Error('Choices must be a non-empty array');
123
+ }
124
+
125
+ let attempts = 0;
126
+ const maxAttempts = options.maxRetries || 3;
127
+
128
+ while (attempts < maxAttempts) {
129
+ try {
130
+ const coloredQuestion = utils.formatQuestion(question);
131
+ console.log(`\\n${coloredQuestion}`);
132
+
133
+ // Enhanced choice formatting
134
+ choices.forEach((choice, index) => {
135
+ const marker = index === defaultIndex ? '▶' : ' ';
136
+ const color = index === defaultIndex ? utils.colors.cyan : utils.colors.white;
137
+ console.log(`${marker} ${color}${index + 1}. ${choice}${utils.colors.reset}`);
138
+ });
139
+
140
+ const prompt = `\\nSelect option [1-${choices.length}] (default: ${defaultIndex + 1}): `;
141
+ const answer = await utils.promptUser(prompt);
142
+
143
+ let choice;
144
+ if (answer.trim() === '') {
145
+ choice = defaultIndex;
146
+ } else {
147
+ choice = parseInt(answer) - 1;
148
+ }
149
+
150
+ if (choice >= 0 && choice < choices.length) {
151
+ utils.recordInput(question, { choice, value: choices[choice] });
152
+ return choice;
153
+ } else {
154
+ attempts++;
155
+ console.log(` ⚠️ Please select a number between 1 and ${choices.length}. (Attempt ${attempts}/${maxAttempts})`);
156
+ if (attempts >= maxAttempts) {
157
+ throw new Error('Maximum retry attempts exceeded');
158
+ }
159
+ }
160
+ } catch (error) {
161
+ if (attempts >= maxAttempts - 1) throw error;
162
+ attempts++;
163
+ }
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Enhanced password input (hidden)
169
+ */
170
+ static async askPassword(question, options = {}) {
171
+ const utils = new InteractiveUtils(options);
172
+ const coloredQuestion = utils.formatQuestion(question);
173
+
174
+ return new Promise((resolve) => {
175
+ const rl = InteractiveUtils.getReadlineInterface();
176
+
177
+ // Temporarily hide input
178
+ const stdin = process.stdin;
179
+ stdin.setRawMode(true);
180
+ stdin.resume();
181
+
182
+ let password = '';
183
+ console.log(`${coloredQuestion}: `);
184
+
185
+ stdin.on('data', function handler(char) {
186
+ char = char.toString();
187
+
188
+ switch (char) {
189
+ case '\\n':
190
+ case '\\r':
191
+ case '\\u0004': // Ctrl+D
192
+ stdin.setRawMode(false);
193
+ stdin.removeListener('data', handler);
194
+ console.log(''); // New line
195
+ utils.recordInput(question, '[HIDDEN]');
196
+ resolve(password);
197
+ break;
198
+ case '\\u0003': // Ctrl+C
199
+ console.log('\\n\\n🚫 Operation cancelled');
200
+ process.exit(0);
201
+ break;
202
+ case '\\u007f': // Backspace
203
+ if (password.length > 0) {
204
+ password = password.slice(0, -1);
205
+ process.stdout.write('\\b \\b');
206
+ }
207
+ break;
208
+ default:
209
+ password += char;
210
+ process.stdout.write('*');
211
+ }
212
+ });
213
+ });
214
+ }
215
+
216
+ /**
217
+ * Multi-step progress tracking
218
+ */
219
+ static startProgress(totalSteps, initialStepName = '') {
220
+ const utils = new InteractiveUtils();
221
+ utils.progressState = {
222
+ currentStep: 0,
223
+ totalSteps,
224
+ stepName: initialStepName,
225
+ startTime: new Date()
226
+ };
227
+
228
+ if (initialStepName) {
229
+ utils.displayProgress();
230
+ }
231
+
232
+ return utils;
233
+ }
234
+
235
+ /**
236
+ * Update progress step
237
+ */
238
+ nextStep(stepName) {
239
+ this.progressState.currentStep++;
240
+ this.progressState.stepName = stepName;
241
+ this.displayProgress();
242
+ }
243
+
244
+ /**
245
+ * Display progress with formatting
246
+ */
247
+ displayProgress() {
248
+ if (!this.options.enableProgress) return;
249
+
250
+ const { currentStep, totalSteps, stepName, startTime } = this.progressState;
251
+ const percentage = Math.round((currentStep / totalSteps) * 100);
252
+ const elapsed = startTime ? Math.round((Date.now() - startTime.getTime()) / 1000) : 0;
253
+
254
+ const progressBar = this.generateProgressBar(percentage);
255
+
256
+ console.log(`\\n${this.colors.blue}[${currentStep}/${totalSteps}] ${progressBar} ${percentage}%${this.colors.reset}`);
257
+ console.log(`${this.colors.cyan}🔄 ${stepName}${this.colors.reset} ${elapsed > 0 ? `(${elapsed}s)` : ''}`);
258
+ }
259
+
260
+ /**
261
+ * Generate visual progress bar
262
+ */
263
+ generateProgressBar(percentage, length = 20) {
264
+ const filled = Math.round((percentage / 100) * length);
265
+ const empty = length - filled;
266
+ return `${'█'.repeat(filled)}${'░'.repeat(empty)}`;
267
+ }
268
+
269
+ /**
270
+ * Internal method: Ask user with validation
271
+ */
272
+ async askUserWithValidation(question, defaultValue = null, validator = null) {
273
+ let attempts = 0;
274
+ const maxAttempts = this.options.maxRetries;
275
+
276
+ while (attempts < maxAttempts) {
277
+ try {
278
+ const coloredQuestion = this.formatQuestion(question);
279
+ const prompt = defaultValue ? `${coloredQuestion} [${defaultValue}]: ` : `${coloredQuestion}: `;
280
+
281
+ const answer = await this.promptUser(prompt);
282
+ const finalAnswer = answer.trim() || defaultValue;
283
+
284
+ // Validation
285
+ if (validator) {
286
+ const validationResult = await this.validateInput(finalAnswer, validator);
287
+ if (!validationResult.valid) {
288
+ attempts++;
289
+ console.log(` ❌ ${validationResult.error} (Attempt ${attempts}/${maxAttempts})`);
290
+ if (attempts >= maxAttempts) {
291
+ throw new Error('Maximum validation attempts exceeded');
292
+ }
293
+ continue;
294
+ }
295
+ }
296
+
297
+ this.recordInput(question, finalAnswer);
298
+ return finalAnswer;
299
+
300
+ } catch (error) {
301
+ if (attempts >= maxAttempts - 1) throw error;
302
+ attempts++;
303
+ }
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Internal method: Prompt user for input
309
+ */
310
+ promptUser(prompt) {
311
+ return new Promise((resolve) => {
312
+ InteractiveUtils.getReadlineInterface().question(prompt, (answer) => {
313
+ resolve(answer);
314
+ });
315
+ });
316
+ }
317
+
318
+ /**
319
+ * Validate user input
320
+ */
321
+ async validateInput(input, validator) {
322
+ if (typeof validator === 'function') {
323
+ try {
324
+ const result = await validator(input);
325
+ return { valid: result === true, error: typeof result === 'string' ? result : 'Invalid input' };
326
+ } catch (error) {
327
+ return { valid: false, error: error.message };
328
+ }
329
+ }
330
+
331
+ if (typeof validator === 'object') {
332
+ // Built-in validation rules
333
+ if (validator.required && (!input || input.trim() === '')) {
334
+ return { valid: false, error: 'This field is required' };
335
+ }
336
+
337
+ if (validator.minLength && input.length < validator.minLength) {
338
+ return { valid: false, error: `Minimum length is ${validator.minLength} characters` };
339
+ }
340
+
341
+ if (validator.maxLength && input.length > validator.maxLength) {
342
+ return { valid: false, error: `Maximum length is ${validator.maxLength} characters` };
343
+ }
344
+
345
+ if (validator.pattern && !validator.pattern.test(input)) {
346
+ return { valid: false, error: validator.message || 'Invalid format' };
347
+ }
348
+
349
+ if (validator.custom) {
350
+ return await this.validateInput(input, validator.custom);
351
+ }
352
+ }
353
+
354
+ return { valid: true };
355
+ }
356
+
357
+ /**
358
+ * Format question with colors
359
+ */
360
+ formatQuestion(question) {
361
+ if (!this.options.enableColors) return question;
362
+ return `${this.colors.yellow}❓ ${question}${this.colors.reset}`;
363
+ }
364
+
365
+ /**
366
+ * Record input for history/debugging
367
+ */
368
+ recordInput(question, answer) {
369
+ this.inputHistory.push({
370
+ timestamp: new Date(),
371
+ question,
372
+ answer: typeof answer === 'object' ? JSON.stringify(answer) : answer
373
+ });
374
+ }
375
+
376
+ /**
377
+ * Color codes for enhanced display
378
+ */
379
+ get colors() {
380
+ if (!this.options.enableColors) {
381
+ return {
382
+ reset: '', red: '', green: '', yellow: '', blue: '', cyan: '', white: ''
383
+ };
384
+ }
385
+
386
+ return {
387
+ reset: '\\x1b[0m',
388
+ red: '\\x1b[31m',
389
+ green: '\\x1b[32m',
390
+ yellow: '\\x1b[33m',
391
+ blue: '\\x1b[34m',
392
+ cyan: '\\x1b[36m',
393
+ white: '\\x1b[37m'
394
+ };
395
+ }
396
+
397
+ /**
398
+ * Export input history
399
+ */
400
+ getInputHistory() {
401
+ return this.inputHistory;
402
+ }
403
+
404
+ /**
405
+ * Clean up readline interface
406
+ */
407
+ static closePrompts() {
408
+ if (rl && isInitialized) {
409
+ rl.close();
410
+ rl = null;
411
+ isInitialized = false;
412
+ }
413
+ }
414
+ }
415
+
416
+ // Export static convenience functions for backward compatibility
417
+ export const askUser = InteractiveUtils.askUser;
418
+ export const askYesNo = InteractiveUtils.askYesNo;
419
+ export const askChoice = InteractiveUtils.askChoice;
420
+ export const askPassword = InteractiveUtils.askPassword;
421
+ export const closePrompts = InteractiveUtils.closePrompts;
422
+ export const startProgress = InteractiveUtils.startProgress;
423
+
424
+ // Export class for advanced usage
425
+ export { InteractiveUtils as default };
426
+
427
+ /**
428
+ * Deployment-specific interactive utilities
429
+ */
430
+ export class DeploymentInteractiveUtils extends InteractiveUtils {
431
+ constructor(options = {}) {
432
+ super(options);
433
+
434
+ // Deployment-specific validation rules
435
+ this.validationRules.set('domain', {
436
+ pattern: /^[a-z0-9-]+$/,
437
+ message: 'Domain must contain only lowercase letters, numbers, and hyphens'
438
+ });
439
+
440
+ this.validationRules.set('email', {
441
+ pattern: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/,
442
+ message: 'Please enter a valid email address'
443
+ });
444
+
445
+ this.validationRules.set('url', {
446
+ pattern: /^https?:\/\/[^\s]+$/,
447
+ message: 'Please enter a valid URL starting with http:// or https://'
448
+ });
449
+ }
450
+
451
+ /**
452
+ * Ask for domain name with validation
453
+ */
454
+ async askDomain(question = 'Enter domain name', defaultValue = null) {
455
+ return await this.askUserWithValidation(
456
+ question,
457
+ defaultValue,
458
+ this.validationRules.get('domain')
459
+ );
460
+ }
461
+
462
+ /**
463
+ * Ask for email with validation
464
+ */
465
+ async askEmail(question = 'Enter email address', defaultValue = null) {
466
+ return await this.askUserWithValidation(
467
+ question,
468
+ defaultValue,
469
+ this.validationRules.get('email')
470
+ );
471
+ }
472
+
473
+ /**
474
+ * Ask for URL with validation
475
+ */
476
+ async askUrl(question = 'Enter URL', defaultValue = null) {
477
+ return await this.askUserWithValidation(
478
+ question,
479
+ defaultValue,
480
+ this.validationRules.get('url')
481
+ );
482
+ }
483
+
484
+ /**
485
+ * Deployment mode selection
486
+ */
487
+ async askDeploymentMode(defaultMode = 0) {
488
+ const modes = [
489
+ 'Single Domain (Recommended for first-time users)',
490
+ 'Multi-Domain (Deploy multiple domains together)',
491
+ 'Portfolio Mode (Advanced: Full portfolio management)'
492
+ ];
493
+
494
+ return await InteractiveUtils.askChoice(
495
+ 'Select deployment mode:',
496
+ modes,
497
+ defaultMode,
498
+ { enableColors: true }
499
+ );
500
+ }
501
+
502
+ /**
503
+ * Environment selection with warnings
504
+ */
505
+ async askEnvironment(defaultEnv = 0) {
506
+ const environments = ['production', 'staging', 'development', 'preview'];
507
+
508
+ const choice = await InteractiveUtils.askChoice(
509
+ 'Select deployment environment:',
510
+ environments,
511
+ defaultEnv,
512
+ { enableColors: true }
513
+ );
514
+
515
+ // Show environment-specific warnings
516
+ if (environments[choice] === 'production') {
517
+ console.log(`\\n${this.colors.yellow}⚠️ Production Environment Selected:${this.colors.reset}`);
518
+ console.log(' - Enhanced security validation will be performed');
519
+ console.log(' - Backup and recovery mechanisms will be enabled');
520
+ console.log(' - Performance monitoring will be activated');
521
+
522
+ const confirm = await InteractiveUtils.askYesNo('Continue with production deployment?', 'y');
523
+ if (!confirm) {
524
+ throw new Error('Production deployment cancelled by user');
525
+ }
526
+ }
527
+
528
+ return choice;
529
+ }
530
+ }