@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
+ * Secure Token Manager
3
+ * Implements secure token storage, rotation, and access control
4
+ */
5
+
6
+ import { readFile, writeFile, access, mkdir } from 'fs/promises';
7
+ import { join } from 'path';
8
+ import { exec } from 'child_process';
9
+ import { promisify } from 'util';
10
+ import crypto from 'crypto';
11
+ const execAsync = promisify(exec);
12
+ export class SecureTokenManager {
13
+ constructor(options = {}) {
14
+ this.frameworkConfig = null; // Will be loaded in initialize()
15
+ this.config = {
16
+ tokenDir: options.tokenDir,
17
+ // Will be set from framework config if not provided
18
+ encryptionKey: options.encryptionKey || this.generateEncryptionKey(),
19
+ tokenRotationInterval: options.tokenRotationInterval || 24 * 60 * 60 * 1000,
20
+ // 24 hours
21
+ maxTokensPerService: options.maxTokensPerService || 3,
22
+ enableAudit: options.enableAudit !== false,
23
+ auditLogPath: options.auditLogPath,
24
+ // Will be set from framework config if not provided
25
+ ...options
26
+ };
27
+ this.tokens = new Map(); // service -> token data
28
+ this.auditLog = [];
29
+ }
30
+
31
+ /**
32
+ * Initialize the token manager
33
+ */
34
+ async initialize() {
35
+ try {
36
+ // Load framework configuration
37
+ const {
38
+ frameworkConfig
39
+ } = await import('../../../src/utils/framework-config.js');
40
+ this.frameworkConfig = frameworkConfig;
41
+
42
+ // Update paths with framework config
43
+ const configPaths = this.frameworkConfig.getPaths();
44
+ this.config.tokenDir = this.config.tokenDir || configPaths.secureTokens;
45
+ this.config.auditLogPath = this.config.auditLogPath || join(configPaths.auditLogs, 'token-audit.log');
46
+ console.log(`🔐 Token directory configured: ${this.config.tokenDir}`);
47
+ console.log(`📋 Token audit log: ${this.config.auditLogPath}`);
48
+ } catch (error) {
49
+ console.warn(`⚠️ Could not load framework config: ${error.message}. Using default paths.`);
50
+ // Use fallback defaults
51
+ this.config.tokenDir = this.config.tokenDir || '.secure-tokens';
52
+ this.config.auditLogPath = this.config.auditLogPath || 'token-audit.log';
53
+ }
54
+ await this.ensureSecureDirectory();
55
+ await this.loadTokens();
56
+ await this.rotateExpiredTokens();
57
+ if (this.config.enableAudit) {
58
+ this.logAuditEvent('TOKEN_MANAGER_INITIALIZED', {
59
+ timestamp: new Date()
60
+ });
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Store a token securely
66
+ */
67
+ async storeToken(service, token, metadata = {}) {
68
+ const tokenData = {
69
+ service,
70
+ token: this.encrypt(token),
71
+ created: new Date(),
72
+ expires: new Date(Date.now() + this.config.tokenRotationInterval),
73
+ metadata: {
74
+ ...metadata,
75
+ permissions: metadata.permissions || ['read'],
76
+ environment: metadata.environment || 'production'
77
+ },
78
+ fingerprint: this.generateFingerprint(token)
79
+ };
80
+
81
+ // Check token limits
82
+ const serviceTokens = this.getServiceTokens(service);
83
+ if (serviceTokens.length >= this.config.maxTokensPerService) {
84
+ // Remove oldest token
85
+ const oldestToken = serviceTokens.sort((a, b) => a.created - b.created)[0];
86
+ await this.revokeToken(service, oldestToken.fingerprint);
87
+ }
88
+ this.tokens.set(`${service}_${tokenData.fingerprint}`, tokenData);
89
+ await this.saveTokens();
90
+ if (this.config.enableAudit) {
91
+ this.logAuditEvent('TOKEN_STORED', {
92
+ service,
93
+ fingerprint: tokenData.fingerprint,
94
+ permissions: tokenData.metadata.permissions
95
+ });
96
+ }
97
+ return tokenData.fingerprint;
98
+ }
99
+
100
+ /**
101
+ * Retrieve a token securely
102
+ */
103
+ async retrieveToken(service, fingerprint, requiredPermissions = []) {
104
+ const tokenKey = `${service}_${fingerprint}`;
105
+ const tokenData = this.tokens.get(tokenKey);
106
+ if (!tokenData) {
107
+ throw new Error(`Token not found for service: ${service}`);
108
+ }
109
+
110
+ // Check expiration
111
+ if (new Date() > tokenData.expires) {
112
+ await this.revokeToken(service, fingerprint);
113
+ throw new Error(`Token expired for service: ${service}`);
114
+ }
115
+
116
+ // Check permissions
117
+ if (requiredPermissions.length > 0) {
118
+ const hasPermissions = requiredPermissions.every(perm => tokenData.metadata.permissions.includes(perm));
119
+ if (!hasPermissions) {
120
+ if (this.config.enableAudit) {
121
+ this.logAuditEvent('TOKEN_ACCESS_DENIED', {
122
+ service,
123
+ fingerprint,
124
+ requiredPermissions,
125
+ tokenPermissions: tokenData.metadata.permissions
126
+ });
127
+ }
128
+ throw new Error(`Insufficient permissions for token access`);
129
+ }
130
+ }
131
+ if (this.config.enableAudit) {
132
+ this.logAuditEvent('TOKEN_RETRIEVED', {
133
+ service,
134
+ fingerprint,
135
+ permissions: tokenData.metadata.permissions
136
+ });
137
+ }
138
+ return this.decrypt(tokenData.token);
139
+ }
140
+
141
+ /**
142
+ * Rotate a token
143
+ */
144
+ async rotateToken(service, fingerprint, newToken) {
145
+ const tokenKey = `${service}_${fingerprint}`;
146
+ const tokenData = this.tokens.get(tokenKey);
147
+ if (!tokenData) {
148
+ throw new Error(`Token not found for rotation: ${service}`);
149
+ }
150
+ const newFingerprint = this.generateFingerprint(newToken);
151
+ const rotatedTokenData = {
152
+ ...tokenData,
153
+ token: this.encrypt(newToken),
154
+ created: new Date(),
155
+ expires: new Date(Date.now() + this.config.tokenRotationInterval),
156
+ fingerprint: newFingerprint,
157
+ rotatedFrom: fingerprint
158
+ };
159
+
160
+ // Remove old token
161
+ this.tokens.delete(tokenKey);
162
+
163
+ // Store new token
164
+ this.tokens.set(`${service}_${newFingerprint}`, rotatedTokenData);
165
+ await this.saveTokens();
166
+ if (this.config.enableAudit) {
167
+ this.logAuditEvent('TOKEN_ROTATED', {
168
+ service,
169
+ oldFingerprint: fingerprint,
170
+ newFingerprint: newFingerprint
171
+ });
172
+ }
173
+ return newFingerprint;
174
+ }
175
+
176
+ /**
177
+ * Revoke a token
178
+ */
179
+ async revokeToken(service, fingerprint) {
180
+ const tokenKey = `${service}_${fingerprint}`;
181
+ const tokenData = this.tokens.get(tokenKey);
182
+ if (tokenData) {
183
+ this.tokens.delete(tokenKey);
184
+ await this.saveTokens();
185
+ if (this.config.enableAudit) {
186
+ this.logAuditEvent('TOKEN_REVOKED', {
187
+ service,
188
+ fingerprint,
189
+ reason: 'manual_revoke'
190
+ });
191
+ }
192
+ }
193
+ }
194
+
195
+ /**
196
+ * List tokens for a service
197
+ */
198
+ listTokens(service) {
199
+ const serviceTokens = [];
200
+ for (const [key, tokenData] of this.tokens) {
201
+ if (key.startsWith(`${service}_`)) {
202
+ serviceTokens.push({
203
+ fingerprint: tokenData.fingerprint,
204
+ created: tokenData.created,
205
+ expires: tokenData.expires,
206
+ permissions: tokenData.metadata.permissions,
207
+ environment: tokenData.metadata.environment
208
+ });
209
+ }
210
+ }
211
+ return serviceTokens;
212
+ }
213
+
214
+ /**
215
+ * Get service tokens
216
+ */
217
+ getServiceTokens(service) {
218
+ const serviceTokens = [];
219
+ for (const [key, tokenData] of this.tokens) {
220
+ if (key.startsWith(`${service}_`)) {
221
+ serviceTokens.push(tokenData);
222
+ }
223
+ }
224
+ return serviceTokens;
225
+ }
226
+
227
+ /**
228
+ * Rotate expired tokens
229
+ */
230
+ async rotateExpiredTokens() {
231
+ const now = new Date();
232
+ const expiredTokens = [];
233
+ for (const [key, tokenData] of this.tokens) {
234
+ if (now > tokenData.expires) {
235
+ expiredTokens.push({
236
+ key,
237
+ tokenData
238
+ });
239
+ }
240
+ }
241
+ for (const {
242
+ key,
243
+ tokenData
244
+ } of expiredTokens) {
245
+ this.tokens.delete(key);
246
+ if (this.config.enableAudit) {
247
+ this.logAuditEvent('TOKEN_EXPIRED', {
248
+ service: tokenData.service,
249
+ fingerprint: tokenData.fingerprint
250
+ });
251
+ }
252
+ }
253
+ if (expiredTokens.length > 0) {
254
+ await this.saveTokens();
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Encrypt token data
260
+ */
261
+ encrypt(data) {
262
+ const iv = crypto.randomBytes(16);
263
+ const cipher = crypto.createCipher('aes-256-gcm', this.config.encryptionKey);
264
+ cipher.setIV(iv);
265
+ let encrypted = cipher.update(data, 'utf8', 'hex');
266
+ encrypted += cipher.final('hex');
267
+ const authTag = cipher.getAuthTag();
268
+ return {
269
+ encrypted,
270
+ iv: iv.toString('hex'),
271
+ authTag: authTag.toString('hex')
272
+ };
273
+ }
274
+
275
+ /**
276
+ * Decrypt token data
277
+ */
278
+ decrypt(encryptedData) {
279
+ const decipher = crypto.createDecipher('aes-256-gcm', this.config.encryptionKey);
280
+ decipher.setIV(Buffer.from(encryptedData.iv, 'hex'));
281
+ decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
282
+ let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
283
+ decrypted += decipher.final('utf8');
284
+ return decrypted;
285
+ }
286
+
287
+ /**
288
+ * Generate encryption key
289
+ */
290
+ generateEncryptionKey() {
291
+ return crypto.randomBytes(32).toString('hex');
292
+ }
293
+
294
+ /**
295
+ * Generate token fingerprint
296
+ */
297
+ generateFingerprint(token) {
298
+ return crypto.createHash('sha256').update(token).digest('hex').substring(0, 16);
299
+ }
300
+
301
+ /**
302
+ * Ensure secure directory exists
303
+ */
304
+ async ensureSecureDirectory() {
305
+ try {
306
+ await access(this.config.tokenDir);
307
+ } catch {
308
+ await mkdir(this.config.tokenDir, {
309
+ mode: 0o700
310
+ }); // Secure permissions
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Save tokens to disk
316
+ */
317
+ async saveTokens() {
318
+ const tokenFile = join(this.config.tokenDir, 'tokens.json');
319
+ const tokenData = {};
320
+ for (const [key, token] of this.tokens) {
321
+ tokenData[key] = token;
322
+ }
323
+ await writeFile(tokenFile, JSON.stringify(tokenData, null, 2));
324
+ }
325
+
326
+ /**
327
+ * Load tokens from disk
328
+ */
329
+ async loadTokens() {
330
+ try {
331
+ const tokenFile = join(this.config.tokenDir, 'tokens.json');
332
+ const data = await readFile(tokenFile, 'utf8');
333
+ const tokenData = JSON.parse(data);
334
+ for (const [key, token] of Object.entries(tokenData)) {
335
+ // Convert date strings back to Date objects
336
+ token.created = new Date(token.created);
337
+ token.expires = new Date(token.expires);
338
+ this.tokens.set(key, token);
339
+ }
340
+ } catch (error) {
341
+ // File doesn't exist or is corrupted, start fresh
342
+ this.tokens.clear();
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Log audit event
348
+ */
349
+ logAuditEvent(event, details) {
350
+ const auditEntry = {
351
+ timestamp: new Date(),
352
+ event,
353
+ details
354
+ };
355
+ this.auditLog.push(auditEntry);
356
+
357
+ // Keep only last 1000 entries in memory
358
+ if (this.auditLog.length > 1000) {
359
+ this.auditLog.shift();
360
+ }
361
+
362
+ // In a real implementation, you'd write to a secure audit log
363
+ console.log(`[TOKEN_AUDIT] ${event}:`, details);
364
+ }
365
+
366
+ /**
367
+ * Get audit log
368
+ */
369
+ getAuditLog(limit = 100) {
370
+ return this.auditLog.slice(-limit);
371
+ }
372
+
373
+ /**
374
+ * Validate token permissions
375
+ */
376
+ validatePermissions(tokenPermissions, requiredPermissions) {
377
+ return requiredPermissions.every(perm => tokenPermissions.includes(perm));
378
+ }
379
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Error Recovery Module
3
+ * Implements circuit breakers, retries, and graceful degradation
4
+ */
5
+
6
+ export class ErrorRecoveryManager {
7
+ constructor(options = {}) {
8
+ this.options = options;
9
+ this.config = null;
10
+ this.circuitStates = new Map(); // service -> { failures, lastFailure, state }
11
+ this.retryStates = new Map(); // operation -> retry count
12
+ }
13
+
14
+ /**
15
+ * Initialize with framework configuration
16
+ */
17
+ async initialize() {
18
+ // Import framework config for consistent timing and retry settings
19
+ const {
20
+ frameworkConfig
21
+ } = await import('../../../src/utils/framework-config.js');
22
+ const timing = frameworkConfig.getTiming();
23
+ this.config = {
24
+ maxRetries: this.options.maxRetries || timing.retryAttempts,
25
+ retryDelay: this.options.retryDelay || timing.retryDelay,
26
+ circuitBreakerThreshold: this.options.circuitBreakerThreshold || timing.circuitBreakerThreshold,
27
+ circuitBreakerTimeout: this.options.circuitBreakerTimeout || timing.circuitBreakerTimeout,
28
+ gracefulDegradation: this.options.gracefulDegradation !== false,
29
+ ...this.options
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Execute operation with error recovery
35
+ */
36
+ async executeWithRecovery(operation, options = {}) {
37
+ const config = {
38
+ ...this.config,
39
+ ...options
40
+ };
41
+ const operationId = this.getOperationId(operation);
42
+
43
+ // Check circuit breaker
44
+ if (this.isCircuitOpen(operationId)) {
45
+ if (config.gracefulDegradation) {
46
+ return this.executeGracefulFallback(operation, config);
47
+ }
48
+ throw new Error(`Circuit breaker open for operation: ${operationId}`);
49
+ }
50
+ let lastError;
51
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
52
+ try {
53
+ const result = await operation();
54
+ this.recordSuccess(operationId);
55
+ return result;
56
+ } catch (error) {
57
+ lastError = error;
58
+ this.recordFailure(operationId, error);
59
+ if (attempt < config.maxRetries) {
60
+ const delay = this.calculateRetryDelay(attempt, config.retryDelay);
61
+ await this.delay(delay);
62
+ }
63
+ }
64
+ }
65
+
66
+ // All retries exhausted
67
+ if (config.gracefulDegradation) {
68
+ return this.executeGracefulFallback(operation, config);
69
+ }
70
+ throw lastError;
71
+ }
72
+
73
+ /**
74
+ * Check if circuit breaker is open
75
+ */
76
+ isCircuitOpen(operationId) {
77
+ const state = this.circuitStates.get(operationId);
78
+ if (!state) return false;
79
+ if (state.state === 'open') {
80
+ // Check if timeout has passed
81
+ if (Date.now() - state.lastFailure > this.config.circuitBreakerTimeout) {
82
+ state.state = 'half-open';
83
+ state.failures = 0;
84
+ return false;
85
+ }
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+
91
+ /**
92
+ * Record operation success
93
+ */
94
+ recordSuccess(operationId) {
95
+ const state = this.circuitStates.get(operationId);
96
+ if (state) {
97
+ if (state.state === 'half-open') {
98
+ state.state = 'closed';
99
+ state.failures = 0;
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Record operation failure
106
+ */
107
+ recordFailure(operationId, error) {
108
+ let state = this.circuitStates.get(operationId);
109
+ if (!state) {
110
+ state = {
111
+ failures: 0,
112
+ lastFailure: 0,
113
+ state: 'closed'
114
+ };
115
+ this.circuitStates.set(operationId, state);
116
+ }
117
+ state.failures++;
118
+ state.lastFailure = Date.now();
119
+ if (state.failures >= this.config.circuitBreakerThreshold) {
120
+ state.state = 'open';
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Calculate retry delay with exponential backoff
126
+ */
127
+ calculateRetryDelay(attempt, baseDelay) {
128
+ const exponentialDelay = baseDelay * Math.pow(2, attempt);
129
+ const jitter = Math.random() * 0.1 * exponentialDelay;
130
+ return Math.min(exponentialDelay + jitter, 30000); // Max 30 seconds
131
+ }
132
+
133
+ /**
134
+ * Execute graceful fallback
135
+ */
136
+ async executeGracefulFallback(operation, config) {
137
+ console.warn(`Executing graceful fallback for operation`);
138
+
139
+ // Try to execute with reduced functionality
140
+ try {
141
+ // For deployment operations, try a simplified version
142
+ if (operation.name && operation.name.includes('deploy')) {
143
+ return {
144
+ success: false,
145
+ degraded: true,
146
+ message: 'Operation executed in degraded mode'
147
+ };
148
+ }
149
+
150
+ // For data operations, return cached or default data
151
+ if (operation.name && operation.name.includes('fetch')) {
152
+ return {
153
+ data: [],
154
+ cached: true,
155
+ degraded: true
156
+ };
157
+ }
158
+
159
+ // Default fallback
160
+ return {
161
+ success: false,
162
+ degraded: true,
163
+ fallback: true
164
+ };
165
+ } catch (fallbackError) {
166
+ console.error('Graceful fallback also failed:', fallbackError);
167
+ throw fallbackError;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Get unique operation ID
173
+ */
174
+ getOperationId(operation) {
175
+ if (typeof operation === 'function' && operation.name) {
176
+ return operation.name;
177
+ }
178
+ return `operation_${Date.now()}_${Math.random()}`;
179
+ }
180
+
181
+ /**
182
+ * Utility delay function
183
+ */
184
+ delay(ms) {
185
+ return new Promise(resolve => setTimeout(resolve, ms));
186
+ }
187
+
188
+ /**
189
+ * Get circuit breaker status
190
+ */
191
+ getCircuitStatus(operationId) {
192
+ const state = this.circuitStates.get(operationId);
193
+ if (!state) {
194
+ return {
195
+ state: 'closed',
196
+ failures: 0
197
+ };
198
+ }
199
+ return {
200
+ state: state.state,
201
+ failures: state.failures,
202
+ lastFailure: state.lastFailure,
203
+ timeSinceLastFailure: Date.now() - state.lastFailure
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Reset circuit breaker
209
+ */
210
+ resetCircuit(operationId) {
211
+ this.circuitStates.delete(operationId);
212
+ }
213
+
214
+ /**
215
+ * Get all circuit statuses
216
+ */
217
+ getAllCircuitStatuses() {
218
+ const statuses = {};
219
+ for (const [operationId, state] of this.circuitStates) {
220
+ statuses[operationId] = this.getCircuitStatus(operationId);
221
+ }
222
+ return statuses;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Retry wrapper for functions
228
+ */
229
+ export function withRetry(fn, options = {}) {
230
+ const recovery = new ErrorRecoveryManager(options);
231
+ return (...args) => recovery.executeWithRecovery(() => fn(...args), options);
232
+ }
233
+
234
+ /**
235
+ * Circuit breaker wrapper for functions
236
+ */
237
+ export function withCircuitBreaker(fn, options = {}) {
238
+ const recovery = new ErrorRecoveryManager(options);
239
+ return (...args) => recovery.executeWithRecovery(() => fn(...args), options);
240
+ }