@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.
- package/CHANGELOG.md +564 -0
- package/LICENSE +21 -0
- package/README.md +1393 -0
- package/bin/README.md +71 -0
- package/bin/clodo-service.js +416 -0
- package/bin/security/security-cli.js +96 -0
- package/bin/service-management/README.md +74 -0
- package/bin/service-management/create-service.js +129 -0
- package/bin/service-management/init-service.js +102 -0
- package/bin/service-management/init-service.js.backup +889 -0
- package/bin/shared/config/customer-cli.js +293 -0
- package/dist/config/ConfigurationManager.js +159 -0
- package/dist/config/CustomerConfigCLI.js +220 -0
- package/dist/config/FeatureManager.js +426 -0
- package/dist/config/customers.js +441 -0
- package/dist/config/domains.js +180 -0
- package/dist/config/features.js +225 -0
- package/dist/config/index.js +6 -0
- package/dist/database/database-orchestrator.js +730 -0
- package/dist/database/index.js +4 -0
- package/dist/deployment/auditor.js +971 -0
- package/dist/deployment/index.js +10 -0
- package/dist/deployment/rollback-manager.js +523 -0
- package/dist/deployment/testers/api-tester.js +80 -0
- package/dist/deployment/testers/auth-tester.js +129 -0
- package/dist/deployment/testers/core.js +217 -0
- package/dist/deployment/testers/database-tester.js +105 -0
- package/dist/deployment/testers/index.js +74 -0
- package/dist/deployment/testers/load-tester.js +120 -0
- package/dist/deployment/testers/performance-tester.js +105 -0
- package/dist/deployment/validator.js +558 -0
- package/dist/deployment/wrangler-deployer.js +574 -0
- package/dist/handlers/GenericRouteHandler.js +532 -0
- package/dist/index.js +39 -0
- package/dist/migration/MigrationAdapters.js +562 -0
- package/dist/modules/ModuleManager.js +668 -0
- package/dist/modules/security.js +98 -0
- package/dist/orchestration/cross-domain-coordinator.js +1083 -0
- package/dist/orchestration/index.js +5 -0
- package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
- package/dist/orchestration/modules/DomainResolver.js +196 -0
- package/dist/orchestration/modules/StateManager.js +332 -0
- package/dist/orchestration/multi-domain-orchestrator.js +255 -0
- package/dist/routing/EnhancedRouter.js +158 -0
- package/dist/schema/SchemaManager.js +778 -0
- package/dist/security/ConfigurationValidator.js +490 -0
- package/dist/security/DeploymentManager.js +208 -0
- package/dist/security/SecretGenerator.js +142 -0
- package/dist/security/SecurityCLI.js +228 -0
- package/dist/security/index.js +51 -0
- package/dist/security/patterns/environment-rules.js +66 -0
- package/dist/security/patterns/insecure-patterns.js +21 -0
- package/dist/service-management/ConfirmationEngine.js +411 -0
- package/dist/service-management/ErrorTracker.js +294 -0
- package/dist/service-management/GenerationEngine.js +3109 -0
- package/dist/service-management/InputCollector.js +237 -0
- package/dist/service-management/ServiceCreator.js +229 -0
- package/dist/service-management/ServiceInitializer.js +448 -0
- package/dist/service-management/ServiceOrchestrator.js +638 -0
- package/dist/service-management/handlers/ConfigMutator.js +130 -0
- package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
- package/dist/service-management/handlers/GenerationHandler.js +80 -0
- package/dist/service-management/handlers/InputHandler.js +59 -0
- package/dist/service-management/handlers/ValidationHandler.js +203 -0
- package/dist/service-management/index.js +7 -0
- package/dist/services/GenericDataService.js +488 -0
- package/dist/shared/cloudflare/domain-discovery.js +562 -0
- package/dist/shared/cloudflare/domain-manager.js +912 -0
- package/dist/shared/cloudflare/index.js +8 -0
- package/dist/shared/cloudflare/ops.js +387 -0
- package/dist/shared/config/cache.js +1167 -0
- package/dist/shared/config/command-config-manager.js +174 -0
- package/dist/shared/config/customer-cli.js +258 -0
- package/dist/shared/config/index.js +9 -0
- package/dist/shared/config/manager.js +289 -0
- package/dist/shared/database/connection-manager.js +338 -0
- package/dist/shared/database/index.js +7 -0
- package/dist/shared/database/orchestrator.js +632 -0
- package/dist/shared/deployment/auditor.js +971 -0
- package/dist/shared/deployment/index.js +10 -0
- package/dist/shared/deployment/rollback-manager.js +523 -0
- package/dist/shared/deployment/validator.js +558 -0
- package/dist/shared/index.js +32 -0
- package/dist/shared/monitoring/health-checker.js +250 -0
- package/dist/shared/monitoring/index.js +8 -0
- package/dist/shared/monitoring/memory-manager.js +382 -0
- package/dist/shared/monitoring/production-monitor.js +390 -0
- package/dist/shared/production-tester/api-tester.js +80 -0
- package/dist/shared/production-tester/auth-tester.js +129 -0
- package/dist/shared/production-tester/core.js +217 -0
- package/dist/shared/production-tester/database-tester.js +105 -0
- package/dist/shared/production-tester/index.js +74 -0
- package/dist/shared/production-tester/load-tester.js +120 -0
- package/dist/shared/production-tester/performance-tester.js +105 -0
- package/dist/shared/security/api-token-manager.js +296 -0
- package/dist/shared/security/index.js +8 -0
- package/dist/shared/security/secret-generator.js +918 -0
- package/dist/shared/security/secure-token-manager.js +379 -0
- package/dist/shared/utils/error-recovery.js +240 -0
- package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
- package/dist/shared/utils/index.js +9 -0
- package/dist/shared/utils/interactive-prompts.js +134 -0
- package/dist/shared/utils/rate-limiter.js +249 -0
- package/dist/utils/ErrorHandler.js +173 -0
- package/dist/utils/deployment/config-cache.js +1160 -0
- package/dist/utils/deployment/index.js +6 -0
- package/dist/utils/deployment/interactive-prompts.js +97 -0
- package/dist/utils/deployment/secret-generator.js +896 -0
- package/dist/utils/dirname-helper.js +35 -0
- package/dist/utils/domain-config.js +159 -0
- package/dist/utils/error-recovery.js +240 -0
- package/dist/utils/esm-helper.js +52 -0
- package/dist/utils/framework-config.js +481 -0
- package/dist/utils/graceful-shutdown-manager.js +379 -0
- package/dist/utils/health-checker.js +114 -0
- package/dist/utils/index.js +36 -0
- package/dist/utils/prompt-handler.js +98 -0
- package/dist/utils/usage-tracker.js +252 -0
- package/dist/utils/validation.js +112 -0
- package/dist/version/VersionDetector.js +723 -0
- package/dist/worker/index.js +4 -0
- package/dist/worker/integration.js +332 -0
- package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
- package/docs/INTEGRATION_GUIDE.md +2045 -0
- package/docs/README.md +82 -0
- package/docs/SECURITY.md +242 -0
- package/docs/deployment/deployment-guide.md +540 -0
- package/docs/overview.md +280 -0
- package/package.json +176 -0
- package/types/index.d.ts +575 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-compatible __dirname utility
|
|
3
|
+
* Handles both ESM (import.meta.url) and CommonJS (__dirname) environments
|
|
4
|
+
*/
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
export function getDirname(importMetaUrl) {
|
|
8
|
+
// In test environment (CommonJS), use __dirname if available
|
|
9
|
+
if (typeof __dirname !== 'undefined') {
|
|
10
|
+
return __dirname;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// In ESM environment, use import.meta.url
|
|
14
|
+
if (importMetaUrl) {
|
|
15
|
+
const __filename = fileURLToPath(importMetaUrl);
|
|
16
|
+
return path.dirname(__filename);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Fallback
|
|
20
|
+
return process.cwd();
|
|
21
|
+
}
|
|
22
|
+
export function getFilename(importMetaUrl) {
|
|
23
|
+
// In test environment (CommonJS), use __filename if available
|
|
24
|
+
if (typeof __filename !== 'undefined') {
|
|
25
|
+
return __filename;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// In ESM environment, use import.meta.url
|
|
29
|
+
if (importMetaUrl) {
|
|
30
|
+
return fileURLToPath(importMetaUrl);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fallback
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Configuration Schema Utilities
|
|
3
|
+
* Provides schema creation and validation for domain configurations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a domain configuration schema with validation rules
|
|
8
|
+
* @param {Object} options - Schema configuration options
|
|
9
|
+
* @returns {Object} Domain configuration schema
|
|
10
|
+
*/
|
|
11
|
+
export function createDomainConfigSchema(options = {}) {
|
|
12
|
+
const defaultSchema = {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
domains: {
|
|
16
|
+
type: 'array',
|
|
17
|
+
items: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
name: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
minLength: 1
|
|
23
|
+
},
|
|
24
|
+
domain: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
pattern: '^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\\.[a-zA-Z]{2,}$'
|
|
27
|
+
},
|
|
28
|
+
accountId: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
minLength: 1
|
|
31
|
+
},
|
|
32
|
+
zoneId: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
minLength: 1
|
|
35
|
+
},
|
|
36
|
+
environment: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
enum: ['development', 'staging', 'production']
|
|
39
|
+
},
|
|
40
|
+
ssl: {
|
|
41
|
+
type: 'boolean'
|
|
42
|
+
},
|
|
43
|
+
cors: {
|
|
44
|
+
type: 'boolean'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
required: ['name', 'domain', 'environment'],
|
|
48
|
+
additionalProperties: false
|
|
49
|
+
},
|
|
50
|
+
minItems: 1
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
required: ['domains'],
|
|
54
|
+
additionalProperties: false,
|
|
55
|
+
...options
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
schema: defaultSchema,
|
|
59
|
+
validate: function (data) {
|
|
60
|
+
const errors = [];
|
|
61
|
+
if (!data || typeof data !== 'object') {
|
|
62
|
+
errors.push({
|
|
63
|
+
field: 'root',
|
|
64
|
+
message: 'Data must be an object'
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
valid: false,
|
|
68
|
+
errors
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (!Array.isArray(data.domains)) {
|
|
72
|
+
errors.push({
|
|
73
|
+
field: 'domains',
|
|
74
|
+
message: 'Domains must be an array'
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
valid: false,
|
|
78
|
+
errors
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (data.domains.length === 0) {
|
|
82
|
+
errors.push({
|
|
83
|
+
field: 'domains',
|
|
84
|
+
message: 'At least one domain is required'
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
errors
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
data.domains.forEach((domain, index) => {
|
|
92
|
+
if (!domain.name) {
|
|
93
|
+
errors.push({
|
|
94
|
+
field: `domains[${index}].name`,
|
|
95
|
+
message: 'Domain name is required'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (!domain.domain) {
|
|
99
|
+
errors.push({
|
|
100
|
+
field: `domains[${index}].domain`,
|
|
101
|
+
message: 'Domain URL is required'
|
|
102
|
+
});
|
|
103
|
+
} else if (!/^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.[a-zA-Z]{2,}$/.test(domain.domain)) {
|
|
104
|
+
errors.push({
|
|
105
|
+
field: `domains[${index}].domain`,
|
|
106
|
+
message: 'Invalid domain format'
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (!domain.environment) {
|
|
110
|
+
errors.push({
|
|
111
|
+
field: `domains[${index}].environment`,
|
|
112
|
+
message: 'Environment is required'
|
|
113
|
+
});
|
|
114
|
+
} else if (!['development', 'staging', 'production'].includes(domain.environment)) {
|
|
115
|
+
errors.push({
|
|
116
|
+
field: `domains[${index}].environment`,
|
|
117
|
+
message: 'Environment must be development, staging, or production'
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
valid: errors.length === 0,
|
|
123
|
+
errors
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Validate domain configuration data
|
|
131
|
+
* @param {Object} data - Domain configuration data to validate
|
|
132
|
+
* @param {Object} schema - Optional custom schema
|
|
133
|
+
* @returns {Object} Validation result
|
|
134
|
+
*/
|
|
135
|
+
export function validateDomainConfig(data, schema = null) {
|
|
136
|
+
const configSchema = schema || createDomainConfigSchema();
|
|
137
|
+
return configSchema.validate(data);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Create a default domain configuration
|
|
142
|
+
* @param {string} serviceName - Name of the service
|
|
143
|
+
* @param {string} domainName - Primary domain name
|
|
144
|
+
* @param {string} environment - Target environment
|
|
145
|
+
* @returns {Object} Default domain configuration
|
|
146
|
+
*/
|
|
147
|
+
export function createDefaultDomainConfig(serviceName, domainName, environment = 'development') {
|
|
148
|
+
return {
|
|
149
|
+
domains: [{
|
|
150
|
+
name: serviceName,
|
|
151
|
+
domain: domainName,
|
|
152
|
+
accountId: 'your-account-id',
|
|
153
|
+
zoneId: 'your-zone-id',
|
|
154
|
+
environment,
|
|
155
|
+
ssl: true,
|
|
156
|
+
cors: environment !== 'production'
|
|
157
|
+
}]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
@@ -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('./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
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESM Helper - Provides __dirname and __filename for ES modules
|
|
3
|
+
* Handles both normal ESM environment and test environment transformation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { dirname, join } from 'path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get __filename and __dirname for current module
|
|
11
|
+
* @param {string} importMetaUrl - import.meta.url from calling module
|
|
12
|
+
* @param {string} fallbackPath - fallback path relative to project root for tests
|
|
13
|
+
* @returns {Object} - {__filename, __dirname}
|
|
14
|
+
*/
|
|
15
|
+
export function getFileInfo(importMetaUrl, fallbackPath) {
|
|
16
|
+
try {
|
|
17
|
+
const __filename = fileURLToPath(importMetaUrl);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
return {
|
|
20
|
+
__filename,
|
|
21
|
+
__dirname
|
|
22
|
+
};
|
|
23
|
+
} catch (error) {
|
|
24
|
+
// Fallback for test environment where import.meta is transformed
|
|
25
|
+
const __dirname = join(process.cwd(), fallbackPath);
|
|
26
|
+
const __filename = join(__dirname, 'index.js'); // Generic filename
|
|
27
|
+
return {
|
|
28
|
+
__filename,
|
|
29
|
+
__dirname
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get just __dirname for current module
|
|
36
|
+
* @param {string} importMetaUrl - import.meta.url from calling module
|
|
37
|
+
* @param {string} fallbackPath - fallback path relative to project root for tests
|
|
38
|
+
* @returns {string} - __dirname
|
|
39
|
+
*/
|
|
40
|
+
export function getDirname(importMetaUrl, fallbackPath) {
|
|
41
|
+
return getFileInfo(importMetaUrl, fallbackPath).__dirname;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get just __filename for current module
|
|
46
|
+
* @param {string} importMetaUrl - import.meta.url from calling module
|
|
47
|
+
* @param {string} fallbackPath - fallback path relative to project root for tests
|
|
48
|
+
* @returns {string} - __filename
|
|
49
|
+
*/
|
|
50
|
+
export function getFilename(importMetaUrl, fallbackPath) {
|
|
51
|
+
return getFileInfo(importMetaUrl, fallbackPath).__filename;
|
|
52
|
+
}
|