@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,379 @@
1
+ /**
2
+ * Graceful Shutdown Manager
3
+ * Implements SIGTERM/SIGINT handlers and cleanup routines for CLI tools
4
+ */
5
+
6
+ export class GracefulShutdownManager {
7
+ constructor(options = {}) {
8
+ this.options = options;
9
+ this.config = null;
10
+ this.isShuttingDown = false;
11
+ this.shutdownHandlers = [];
12
+ this.shutdownPromise = null;
13
+ this.registered = false;
14
+ }
15
+
16
+ /**
17
+ * Initialize with framework configuration
18
+ */
19
+ async initialize() {
20
+ // Import framework config for consistent timing
21
+ const {
22
+ frameworkConfig
23
+ } = await import('./framework-config.js');
24
+ const timing = frameworkConfig.getTiming();
25
+ this.config = {
26
+ shutdownTimeout: this.options.shutdownTimeout || timing.shutdownTimeout,
27
+ forceShutdownTimeout: this.options.forceShutdownTimeout || timing.forceShutdownTimeout,
28
+ enableLogging: this.options.enableLogging !== false,
29
+ ...this.options
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Register the shutdown manager
35
+ */
36
+ register() {
37
+ if (this.registered) return;
38
+ this.registered = true;
39
+
40
+ // Register signal handlers
41
+ process.on('SIGTERM', () => this.initiateShutdown('SIGTERM'));
42
+ process.on('SIGINT', () => this.initiateShutdown('SIGINT'));
43
+
44
+ // Handle uncaught exceptions and unhandled rejections
45
+ process.on('uncaughtException', error => {
46
+ console.error('💥 Uncaught exception:', error);
47
+ this.initiateShutdown('uncaughtException', error);
48
+ });
49
+ process.on('unhandledRejection', (reason, promise) => {
50
+ console.error('💥 Unhandled rejection at:', promise, 'reason:', reason);
51
+ this.initiateShutdown('unhandledRejection', reason);
52
+ });
53
+ if (this.config.enableLogging) {
54
+ console.log('🛑 Graceful shutdown manager registered');
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Unregister the shutdown manager
60
+ */
61
+ unregister() {
62
+ if (!this.registered) return;
63
+ this.registered = false;
64
+ process.removeAllListeners('SIGTERM');
65
+ process.removeAllListeners('SIGINT');
66
+ process.removeAllListeners('uncaughtException');
67
+ process.removeAllListeners('unhandledRejection');
68
+ if (this.config.enableLogging) {
69
+ console.log('🛑 Graceful shutdown manager unregistered');
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Add a shutdown handler
75
+ */
76
+ addShutdownHandler(handler, priority = 0) {
77
+ this.shutdownHandlers.push({
78
+ handler,
79
+ priority,
80
+ id: `handler_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
81
+ });
82
+
83
+ // Sort by priority (higher priority first)
84
+ this.shutdownHandlers.sort((a, b) => b.priority - a.priority);
85
+
86
+ // Return removal function
87
+ return () => {
88
+ const index = this.shutdownHandlers.findIndex(h => h.id === this.id);
89
+ if (index !== -1) {
90
+ this.shutdownHandlers.splice(index, 1);
91
+ }
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Remove a shutdown handler
97
+ */
98
+ removeShutdownHandler(handlerId) {
99
+ const index = this.shutdownHandlers.findIndex(h => h.id === handlerId);
100
+ if (index !== -1) {
101
+ this.shutdownHandlers.splice(index, 1);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Initiate graceful shutdown
107
+ */
108
+ async initiateShutdown(signal, error = null) {
109
+ if (this.isShuttingDown) return;
110
+ this.isShuttingDown = true;
111
+ if (this.config.enableLogging) {
112
+ console.log(`🛑 Initiating graceful shutdown: ${signal}`);
113
+ if (error) {
114
+ console.error('Shutdown triggered by error:', error);
115
+ }
116
+ }
117
+
118
+ // Prevent new work from starting
119
+ this.preventNewWork();
120
+
121
+ // Execute shutdown handlers
122
+ await this.executeShutdownHandlers(signal, error);
123
+
124
+ // Force exit if handlers don't complete in time
125
+ this.forceExitIfNeeded(signal);
126
+
127
+ // Exit with appropriate code
128
+ const exitCode = error ? 1 : 0;
129
+ process.exit(exitCode);
130
+ }
131
+
132
+ /**
133
+ * Prevent new work from starting
134
+ */
135
+ preventNewWork() {
136
+ // Set global shutdown flag
137
+ if (typeof global !== 'undefined') {
138
+ global.isShuttingDown = true;
139
+ }
140
+
141
+ // Prevent new database connections
142
+ if (typeof global !== 'undefined' && global.dbManagers) {
143
+ global.dbManagers.forEach(manager => {
144
+ if (typeof manager.preventNewConnections === 'function') {
145
+ manager.preventNewConnections();
146
+ }
147
+ });
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Execute shutdown handlers
153
+ */
154
+ async executeShutdownHandlers(signal, error) {
155
+ const shutdownPromises = [];
156
+ for (const {
157
+ handler
158
+ } of this.shutdownHandlers) {
159
+ try {
160
+ const promise = handler(signal, error);
161
+ if (promise && typeof promise.then === 'function') {
162
+ shutdownPromises.push(promise);
163
+ }
164
+ } catch (handlerError) {
165
+ console.error('Error in shutdown handler:', handlerError);
166
+ }
167
+ }
168
+
169
+ // Wait for all handlers to complete or timeout
170
+ if (shutdownPromises.length > 0) {
171
+ try {
172
+ await Promise.race([Promise.all(shutdownPromises), new Promise(resolve => setTimeout(resolve, this.config.shutdownTimeout))]);
173
+ } catch (timeoutError) {
174
+ console.error('Shutdown handlers timed out:', timeoutError);
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Force exit if needed
181
+ */
182
+ forceExitIfNeeded(signal) {
183
+ setTimeout(() => {
184
+ console.error(`💥 Force exiting after ${this.config.forceShutdownTimeout}ms`);
185
+ process.exit(1);
186
+ }, this.config.forceShutdownTimeout).unref();
187
+ }
188
+
189
+ /**
190
+ * Create a shutdown-aware interval
191
+ */
192
+ createShutdownAwareInterval(callback, delay) {
193
+ const intervalId = setInterval(() => {
194
+ if (this.isShuttingDown) {
195
+ clearInterval(intervalId);
196
+ return;
197
+ }
198
+ try {
199
+ callback();
200
+ } catch (error) {
201
+ console.error('Error in shutdown-aware interval:', error);
202
+ clearInterval(intervalId);
203
+ }
204
+ }, delay);
205
+ intervalId.unref?.();
206
+
207
+ // Add cleanup handler
208
+ this.addShutdownHandler(() => {
209
+ clearInterval(intervalId);
210
+ }, 100); // High priority
211
+
212
+ return intervalId;
213
+ }
214
+
215
+ /**
216
+ * Create a shutdown-aware timeout
217
+ */
218
+ createShutdownAwareTimeout(callback, delay) {
219
+ const timeoutId = setTimeout(() => {
220
+ if (this.isShuttingDown) return;
221
+ try {
222
+ callback();
223
+ } catch (error) {
224
+ console.error('Error in shutdown-aware timeout:', error);
225
+ }
226
+ }, delay);
227
+ timeoutId.unref?.();
228
+
229
+ // Add cleanup handler
230
+ this.addShutdownHandler(() => {
231
+ clearTimeout(timeoutId);
232
+ }, 100); // High priority
233
+
234
+ return timeoutId;
235
+ }
236
+
237
+ /**
238
+ * Wait for shutdown signal
239
+ */
240
+ async waitForShutdownSignal() {
241
+ return new Promise(resolve => {
242
+ const handler = signal => {
243
+ resolve(signal);
244
+ };
245
+ process.once('SIGTERM', () => handler('SIGTERM'));
246
+ process.once('SIGINT', () => handler('SIGINT'));
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Get shutdown status
252
+ */
253
+ getShutdownStatus() {
254
+ return {
255
+ isShuttingDown: this.isShuttingDown,
256
+ handlerCount: this.shutdownHandlers.length,
257
+ registered: this.registered,
258
+ config: this.config
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Create database cleanup handler
264
+ */
265
+ createDatabaseCleanupHandler(dbManager) {
266
+ return async signal => {
267
+ if (dbManager && typeof dbManager.closeAllConnections === 'function') {
268
+ console.log('🗃️ Closing database connections...');
269
+ await dbManager.closeAllConnections();
270
+ console.log('✅ Database connections closed');
271
+ }
272
+ };
273
+ }
274
+
275
+ /**
276
+ * Create monitoring cleanup handler
277
+ */
278
+ createMonitoringCleanupHandler(monitor) {
279
+ return async signal => {
280
+ if (monitor && typeof monitor.stopMonitoring === 'function') {
281
+ console.log('📊 Stopping monitoring...');
282
+ await monitor.stopMonitoring();
283
+ console.log('✅ Monitoring stopped');
284
+ }
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Create token cleanup handler
290
+ */
291
+ createTokenCleanupHandler(tokenManager) {
292
+ return async signal => {
293
+ // Token manager doesn't need special cleanup, but we could log the event
294
+ console.log('🔐 Token manager cleanup completed');
295
+ };
296
+ }
297
+
298
+ /**
299
+ * Setup standard handlers for common services
300
+ */
301
+ setupStandardHandlers(services = {}) {
302
+ const {
303
+ dbManager,
304
+ monitor,
305
+ tokenManager,
306
+ memoryManager
307
+ } = services;
308
+
309
+ // Database cleanup (high priority)
310
+ if (dbManager) {
311
+ this.addShutdownHandler(this.createDatabaseCleanupHandler(dbManager), 1000);
312
+ }
313
+
314
+ // Monitoring cleanup (medium priority)
315
+ if (monitor) {
316
+ this.addShutdownHandler(this.createMonitoringCleanupHandler(monitor), 500);
317
+ }
318
+
319
+ // Memory cleanup (medium priority)
320
+ if (memoryManager) {
321
+ this.addShutdownHandler(async signal => {
322
+ console.log('🧠 Stopping memory monitoring...');
323
+ memoryManager.stopMonitoring();
324
+ console.log('✅ Memory monitoring stopped');
325
+ }, 500);
326
+ }
327
+
328
+ // Token cleanup (low priority)
329
+ if (tokenManager) {
330
+ this.addShutdownHandler(this.createTokenCleanupHandler(tokenManager), 100);
331
+ }
332
+
333
+ // Final cleanup (lowest priority)
334
+ this.addShutdownHandler(async signal => {
335
+ console.log('🧹 Running final cleanup...');
336
+ // Force garbage collection if available
337
+ if (global.gc) {
338
+ global.gc();
339
+ }
340
+ console.log('✅ Final cleanup completed');
341
+ }, 1);
342
+ }
343
+ }
344
+
345
+ // Global shutdown manager instance
346
+ let globalShutdownManager = null;
347
+
348
+ /**
349
+ * Get the global shutdown manager instance
350
+ */
351
+ export function getShutdownManager(options = {}) {
352
+ if (!globalShutdownManager) {
353
+ globalShutdownManager = new GracefulShutdownManager(options);
354
+ }
355
+ return globalShutdownManager;
356
+ }
357
+
358
+ /**
359
+ * Initialize graceful shutdown handling
360
+ */
361
+ export function initializeGracefulShutdown(services = {}, options = {}) {
362
+ const manager = getShutdownManager(options);
363
+ manager.setupStandardHandlers(services);
364
+ manager.register();
365
+ return manager;
366
+ }
367
+
368
+ /**
369
+ * Quick shutdown for simple scripts
370
+ */
371
+ export async function withGracefulShutdown(callback, services = {}, options = {}) {
372
+ const manager = initializeGracefulShutdown(services, options);
373
+ try {
374
+ await callback();
375
+ } catch (error) {
376
+ console.error('Error in main execution:', error);
377
+ await manager.initiateShutdown('error', error);
378
+ }
379
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Health Checker Module
3
+ * Endpoint health checking and validation utilities
4
+ */
5
+
6
+ import https from 'https';
7
+ import http from 'http';
8
+
9
+ /**
10
+ * Make HTTP request with timeout
11
+ * @param {string} url - URL to request
12
+ * @param {string} method - HTTP method
13
+ * @param {number} timeout - Request timeout in ms
14
+ * @returns {Promise<Object>} Response data and status
15
+ */
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, {
20
+ 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);
34
+ });
35
+ req.on('timeout', () => {
36
+ req.destroy();
37
+ reject(new Error('Request timed out'));
38
+ });
39
+ req.end();
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Check health of a deployment endpoint
45
+ * @param {string} url - Base URL to check
46
+ * @param {number} timeout - Health check timeout in ms
47
+ * @returns {Promise<Object>} Health check result
48
+ */
49
+ 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
+ });
99
+ }
100
+
101
+ /**
102
+ * Check if URL is accessible
103
+ * @param {string} url - URL to check
104
+ * @param {number} timeout - Timeout in ms
105
+ * @returns {Promise<boolean>} Whether URL is accessible
106
+ */
107
+ export async function isUrlAccessible(url, timeout = 5000) {
108
+ try {
109
+ const result = await makeHttpRequest(url, 'HEAD', timeout);
110
+ return result.statusCode >= 200 && result.statusCode < 400;
111
+ } catch (error) {
112
+ return false;
113
+ }
114
+ }
@@ -0,0 +1,36 @@
1
+ // Utility Functions
2
+ // Common utilities used across the framework
3
+
4
+ export const deepMerge = (target, source) => {
5
+ const result = {
6
+ ...target
7
+ };
8
+ for (const key in source) {
9
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
10
+ result[key] = deepMerge(result[key] || {}, source[key]);
11
+ } else {
12
+ result[key] = source[key];
13
+ }
14
+ }
15
+ return result;
16
+ };
17
+ export const validateRequired = (obj, requiredFields) => {
18
+ const missing = requiredFields.filter(field => !obj[field]);
19
+ if (missing.length > 0) {
20
+ throw new Error(`Missing required fields: ${missing.join(', ')}`);
21
+ }
22
+ };
23
+ export const createLogger = (prefix = 'ClodoFramework') => {
24
+ return {
25
+ info: (message, ...args) => console.log(`[${prefix}] ${message}`, ...args),
26
+ warn: (message, ...args) => console.warn(`[${prefix}] ${message}`, ...args),
27
+ error: (message, ...args) => console.error(`[${prefix}] ${message}`, ...args),
28
+ debug: (message, ...args) => console.debug(`[${prefix}] ${message}`, ...args)
29
+ };
30
+ };
31
+
32
+ // Health checking utilities
33
+ export * from './health-checker.js';
34
+
35
+ // Deployment utilities
36
+ export * from './deployment/index.js';
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Shared Prompt Utilities
3
+ * Common prompt handling functionality used across the framework
4
+ */
5
+
6
+ import readline from 'readline/promises';
7
+ import { stdin as input, stdout as output } from 'node:process';
8
+
9
+ /**
10
+ * Base prompt handler for interactive user input
11
+ */
12
+ export class PromptHandler {
13
+ constructor(options = {}) {
14
+ this.interactive = options.interactive !== false;
15
+ this.rl = null;
16
+ }
17
+
18
+ /**
19
+ * Initialize readline interface
20
+ */
21
+ async initialize() {
22
+ if (this.interactive && !this.rl) {
23
+ this.rl = readline.createInterface({
24
+ input,
25
+ output
26
+ });
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Prompt user for input
32
+ */
33
+ async prompt(question) {
34
+ if (!this.interactive) {
35
+ return '';
36
+ }
37
+ await this.initialize();
38
+ try {
39
+ const answer = await this.rl.question(question);
40
+ return answer.trim();
41
+ } catch (error) {
42
+ console.error('Prompt error:', error.message);
43
+ return '';
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Prompt with choices
49
+ */
50
+ async promptChoice(question, choices, defaultIndex = 0) {
51
+ if (!this.interactive) {
52
+ return choices[defaultIndex];
53
+ }
54
+ console.log(question);
55
+ choices.forEach((choice, index) => {
56
+ const marker = index === defaultIndex ? ' (default)' : '';
57
+ console.log(` ${index + 1}. ${choice}${marker}`);
58
+ });
59
+ const answer = await this.prompt('Enter choice: ');
60
+ if (!answer) return choices[defaultIndex];
61
+ const choiceIndex = parseInt(answer) - 1;
62
+ if (choiceIndex >= 0 && choiceIndex < choices.length) {
63
+ return choices[choiceIndex];
64
+ }
65
+ return choices[defaultIndex];
66
+ }
67
+
68
+ /**
69
+ * Confirm yes/no question
70
+ */
71
+ async confirm(question, defaultValue = false) {
72
+ if (!this.interactive) {
73
+ return defaultValue;
74
+ }
75
+ const suffix = defaultValue ? ' (Y/n)' : ' (y/N)';
76
+ const answer = await this.prompt(question + suffix);
77
+ if (!answer) return defaultValue;
78
+ const normalized = answer.toLowerCase();
79
+ return normalized === 'y' || normalized === 'yes';
80
+ }
81
+
82
+ /**
83
+ * Cleanup readline interface
84
+ */
85
+ async close() {
86
+ if (this.rl) {
87
+ this.rl.close();
88
+ this.rl = null;
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Factory for creating prompt handlers
95
+ */
96
+ export function createPromptHandler(options = {}) {
97
+ return new PromptHandler(options);
98
+ }