mcp-wordpress 2.11.13 → 3.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/README.md +14 -29
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +7 -0
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +21 -7
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/SEOCacheManager.d.ts.map +1 -1
- package/dist/cache/SEOCacheManager.js +6 -1
- package/dist/cache/SEOCacheManager.js.map +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/client/CachedWordPressClient.d.ts.map +1 -1
- package/dist/client/CachedWordPressClient.js.map +1 -1
- package/dist/client/MockWordPressClient.d.ts.map +1 -1
- package/dist/client/MockWordPressClient.js.map +1 -1
- package/dist/client/SEOWordPressClient.d.ts.map +1 -1
- package/dist/client/SEOWordPressClient.js.map +1 -1
- package/dist/client/api.d.ts +11 -26
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +111 -203
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.d.ts.map +1 -1
- package/dist/client/auth.js.map +1 -1
- package/dist/client/managers/AuthManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +6 -5
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/client/managers/composed/MigrationAdapter.d.ts +3 -3
- package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -1
- package/dist/client/managers/composed/MigrationAdapter.js +2 -2
- package/dist/client/managers/composed/MigrationAdapter.js.map +1 -1
- package/dist/client/managers/composed/index.d.ts +7 -7
- package/dist/client/managers/composed/index.d.ts.map +1 -1
- package/dist/client/managers/composed/index.js +6 -6
- package/dist/client/managers/composed/index.js.map +1 -1
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +1 -1
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -1
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +1 -1
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -1
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +1 -1
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -1
- package/dist/client/operations/comments.d.ts +58 -0
- package/dist/client/operations/comments.d.ts.map +1 -0
- package/dist/client/operations/comments.js +74 -0
- package/dist/client/operations/comments.js.map +1 -0
- package/dist/client/operations/index.d.ts +12 -0
- package/dist/client/operations/index.d.ts.map +1 -0
- package/dist/client/operations/index.js +12 -0
- package/dist/client/operations/index.js.map +1 -0
- package/dist/client/operations/media.d.ts +55 -0
- package/dist/client/operations/media.d.ts.map +1 -0
- package/dist/client/operations/media.js +132 -0
- package/dist/client/operations/media.js.map +1 -0
- package/dist/client/operations/pages.d.ts +50 -0
- package/dist/client/operations/pages.d.ts.map +1 -0
- package/dist/client/operations/pages.js +56 -0
- package/dist/client/operations/pages.js.map +1 -0
- package/dist/client/operations/posts.d.ts +50 -0
- package/dist/client/operations/posts.d.ts.map +1 -0
- package/dist/client/operations/posts.js +53 -0
- package/dist/client/operations/posts.js.map +1 -0
- package/dist/client/operations/site.d.ts +60 -0
- package/dist/client/operations/site.d.ts.map +1 -0
- package/dist/client/operations/site.js +83 -0
- package/dist/client/operations/site.js.map +1 -0
- package/dist/client/operations/taxonomies.d.ts +69 -0
- package/dist/client/operations/taxonomies.d.ts.map +1 -0
- package/dist/client/operations/taxonomies.js +87 -0
- package/dist/client/operations/taxonomies.js.map +1 -0
- package/dist/client/operations/users.d.ts +50 -0
- package/dist/client/operations/users.d.ts.map +1 -0
- package/dist/client/operations/users.js +57 -0
- package/dist/client/operations/users.js.map +1 -0
- package/dist/config/ServerConfiguration.d.ts.map +1 -1
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/performance/PerformanceMonitor.js.map +1 -1
- package/dist/security/AISecurityScanner.d.ts.map +1 -1
- package/dist/security/AISecurityScanner.js +3 -2
- package/dist/security/AISecurityScanner.js.map +1 -1
- package/dist/security/AutomatedRemediation.js.map +1 -1
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +30 -18
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityCIPipeline.d.ts +19 -196
- package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
- package/dist/security/SecurityCIPipeline.js +95 -639
- package/dist/security/SecurityCIPipeline.js.map +1 -1
- package/dist/security/SecurityConfig.js.map +1 -1
- package/dist/security/SecurityConfigManager.js.map +1 -1
- package/dist/security/SecurityGateExecutor.d.ts +67 -0
- package/dist/security/SecurityGateExecutor.d.ts.map +1 -0
- package/dist/security/SecurityGateExecutor.js +363 -0
- package/dist/security/SecurityGateExecutor.js.map +1 -0
- package/dist/security/SecurityMonitoring.js.map +1 -1
- package/dist/security/SecurityReportGenerator.d.ts +65 -0
- package/dist/security/SecurityReportGenerator.d.ts.map +1 -0
- package/dist/security/SecurityReportGenerator.js +210 -0
- package/dist/security/SecurityReportGenerator.js.map +1 -0
- package/dist/security/SecurityReviewer.js.map +1 -1
- package/dist/security/SecurityTypes.d.ts +188 -0
- package/dist/security/SecurityTypes.d.ts.map +1 -0
- package/dist/security/SecurityTypes.js +6 -0
- package/dist/security/SecurityTypes.js.map +1 -0
- package/dist/security/index.d.ts +5 -28
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +4 -0
- package/dist/security/index.js.map +1 -1
- package/dist/server/ConnectionTester.d.ts.map +1 -1
- package/dist/server/ConnectionTester.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/tools/BaseToolManager.d.ts.map +1 -1
- package/dist/tools/BaseToolManager.js.map +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/cache.d.ts.map +1 -1
- package/dist/tools/cache.js.map +1 -1
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/media.d.ts.map +1 -1
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance/PerformanceHelpers.d.ts +116 -0
- package/dist/tools/performance/PerformanceHelpers.d.ts.map +1 -0
- package/dist/tools/performance/PerformanceHelpers.js +298 -0
- package/dist/tools/performance/PerformanceHelpers.js.map +1 -0
- package/dist/tools/performance/PerformanceTools.d.ts +54 -0
- package/dist/tools/performance/PerformanceTools.d.ts.map +1 -0
- package/dist/tools/performance/PerformanceTools.js +687 -0
- package/dist/tools/performance/PerformanceTools.js.map +1 -0
- package/dist/tools/performance/index.d.ts +8 -0
- package/dist/tools/performance/index.d.ts.map +1 -0
- package/dist/tools/performance/index.js +8 -0
- package/dist/tools/performance/index.js.map +1 -0
- package/dist/tools/performance.d.ts +12 -69
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +12 -920
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts.d.ts.map +1 -1
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -1
- package/dist/tools/seo/analyzers/ContentAnalyzer.js +14 -3
- package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -1
- package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -1
- package/dist/tools/seo/auditors/SiteAuditor.js +12 -3
- package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -1
- package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -1
- package/dist/tools/seo/generators/MetaGenerator.js +25 -8
- package/dist/tools/seo/generators/MetaGenerator.js.map +1 -1
- package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -1
- package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -1
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -1
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -1
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts.map +1 -1
- package/dist/tools/taxonomies.js.map +1 -1
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js.map +1 -1
- package/dist/utils/CircuitBreaker.d.ts +243 -0
- package/dist/utils/CircuitBreaker.d.ts.map +1 -0
- package/dist/utils/CircuitBreaker.js +456 -0
- package/dist/utils/CircuitBreaker.js.map +1 -0
- package/dist/utils/debug.d.ts.map +1 -1
- package/dist/utils/debug.js.map +1 -1
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts.map +1 -1
- package/docs/DEPRECATIONS.md +157 -0
- package/package.json +2 -3
- package/src/cache/CacheInvalidation.ts +1 -1
- package/src/cache/CacheManager.ts +25 -8
- package/src/cache/HttpCacheWrapper.ts +1 -1
- package/src/cache/SEOCacheManager.ts +9 -3
- package/src/cache/index.ts +1 -1
- package/src/client/CachedWordPressClient.ts +6 -6
- package/src/client/MockWordPressClient.ts +3 -3
- package/src/client/SEOWordPressClient.ts +6 -6
- package/src/client/api.ts +129 -215
- package/src/client/auth.ts +3 -3
- package/src/client/managers/AuthManager.ts +1 -1
- package/src/client/managers/RequestManager.ts +6 -7
- package/src/client/managers/composed/MigrationAdapter.ts +4 -4
- package/src/client/managers/composed/index.ts +7 -7
- package/src/client/managers/implementations/ConfigurationProviderImpl.ts +1 -1
- package/src/client/managers/implementations/ErrorHandlerImpl.ts +1 -1
- package/src/client/managers/implementations/ParameterValidatorImpl.ts +1 -1
- package/src/client/operations/comments.ts +96 -0
- package/src/client/operations/index.ts +12 -0
- package/src/client/operations/media.ts +162 -0
- package/src/client/operations/pages.ts +71 -0
- package/src/client/operations/posts.ts +68 -0
- package/src/client/operations/site.ts +106 -0
- package/src/client/operations/taxonomies.ts +115 -0
- package/src/client/operations/users.ts +72 -0
- package/src/config/ServerConfiguration.ts +6 -6
- package/src/docs/DocumentationGenerator.ts +3 -3
- package/src/performance/MetricsCollector.ts +4 -4
- package/src/performance/PerformanceMonitor.ts +1 -1
- package/src/security/AISecurityScanner.ts +4 -3
- package/src/security/AutomatedRemediation.ts +1 -1
- package/src/security/InputValidator.ts +36 -19
- package/src/security/SecurityCIPipeline.ts +130 -953
- package/src/security/SecurityConfig.ts +1 -1
- package/src/security/SecurityConfigManager.ts +1 -1
- package/src/security/SecurityGateExecutor.ts +485 -0
- package/src/security/SecurityMonitoring.ts +1 -1
- package/src/security/SecurityReportGenerator.ts +272 -0
- package/src/security/SecurityReviewer.ts +1 -1
- package/src/security/SecurityTypes.ts +199 -0
- package/src/security/index.ts +6 -1
- package/src/server/ConnectionTester.ts +4 -4
- package/src/server/ToolRegistry.ts +6 -6
- package/src/tools/BaseToolManager.ts +2 -2
- package/src/tools/auth.ts +3 -3
- package/src/tools/cache.ts +3 -3
- package/src/tools/comments.ts +3 -3
- package/src/tools/media.ts +3 -3
- package/src/tools/pages.ts +3 -3
- package/src/tools/performance/PerformanceHelpers.ts +330 -0
- package/src/tools/performance/PerformanceTools.ts +854 -0
- package/src/tools/performance/index.ts +8 -0
- package/src/tools/performance.ts +12 -1073
- package/src/tools/posts.ts +1 -1
- package/src/tools/seo/analyzers/ContentAnalyzer.ts +21 -7
- package/src/tools/seo/auditors/SiteAuditor.ts +18 -7
- package/src/tools/seo/generators/MetaGenerator.ts +33 -12
- package/src/tools/seo/generators/SchemaGenerator.ts +3 -3
- package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +4 -4
- package/src/tools/site.ts +3 -3
- package/src/tools/taxonomies.ts +3 -3
- package/src/tools/users.ts +4 -4
- package/src/utils/CircuitBreaker.ts +572 -0
- package/src/utils/debug.ts +3 -3
- package/src/utils/error.ts +1 -1
- package/src/utils/index.ts +3 -0
- package/src/utils/logger.ts +1 -1
- package/src/utils/toolWrapper.ts +2 -2
- package/docs/BRANCH_PROTECTION.md +0 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker Pattern Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides fault tolerance for external service calls by preventing
|
|
5
|
+
* cascading failures and allowing systems to recover gracefully.
|
|
6
|
+
*
|
|
7
|
+
* States:
|
|
8
|
+
* - CLOSED: Normal operation, requests pass through
|
|
9
|
+
* - OPEN: Failures exceeded threshold, requests fail fast
|
|
10
|
+
* - HALF_OPEN: Testing if service recovered, limited requests allowed
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const breaker = new CircuitBreaker({
|
|
15
|
+
* failureThreshold: 5,
|
|
16
|
+
* resetTimeout: 30000,
|
|
17
|
+
* name: 'wordpress-api'
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* try {
|
|
21
|
+
* const result = await breaker.execute(() => apiCall());
|
|
22
|
+
* } catch (error) {
|
|
23
|
+
* if (error instanceof CircuitBreakerOpenError) {
|
|
24
|
+
* // Circuit is open, use fallback
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { createLogger } from "./logger.js";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Circuit breaker states
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Circuit state values
|
|
37
|
+
*/
|
|
38
|
+
export const CircuitState = {
|
|
39
|
+
CLOSED: "CLOSED",
|
|
40
|
+
OPEN: "OPEN",
|
|
41
|
+
HALF_OPEN: "HALF_OPEN",
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Circuit state type
|
|
46
|
+
*/
|
|
47
|
+
export type CircuitStateType = (typeof CircuitState)[keyof typeof CircuitState];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Circuit breaker configuration options
|
|
51
|
+
*/
|
|
52
|
+
export interface CircuitBreakerOptions {
|
|
53
|
+
/** Name for logging and identification */
|
|
54
|
+
name: string;
|
|
55
|
+
/** Number of failures before opening circuit (default: 5) */
|
|
56
|
+
failureThreshold?: number;
|
|
57
|
+
/** Time in ms before attempting to close circuit (default: 30000) */
|
|
58
|
+
resetTimeout?: number;
|
|
59
|
+
/** Number of successful calls needed to close circuit from half-open (default: 2) */
|
|
60
|
+
successThreshold?: number;
|
|
61
|
+
/** Time window in ms to count failures (default: 60000) */
|
|
62
|
+
failureWindow?: number;
|
|
63
|
+
/** Timeout for individual operations in ms (default: 30000) */
|
|
64
|
+
timeout?: number;
|
|
65
|
+
/** Function to determine if an error should trip the breaker */
|
|
66
|
+
isFailure?: (error: Error) => boolean;
|
|
67
|
+
/** Callback when circuit opens */
|
|
68
|
+
onOpen?: (name: string, failures: number) => void;
|
|
69
|
+
/** Callback when circuit closes */
|
|
70
|
+
onClose?: (name: string) => void;
|
|
71
|
+
/** Callback when circuit enters half-open state */
|
|
72
|
+
onHalfOpen?: (name: string) => void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Circuit breaker statistics
|
|
77
|
+
*/
|
|
78
|
+
export interface CircuitBreakerStats {
|
|
79
|
+
state: CircuitStateType;
|
|
80
|
+
failures: number;
|
|
81
|
+
successes: number;
|
|
82
|
+
lastFailure: Date | null;
|
|
83
|
+
lastSuccess: Date | null;
|
|
84
|
+
totalRequests: number;
|
|
85
|
+
failedRequests: number;
|
|
86
|
+
successfulRequests: number;
|
|
87
|
+
rejectedRequests: number;
|
|
88
|
+
timeInCurrentState: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Error thrown when circuit breaker is open
|
|
93
|
+
*/
|
|
94
|
+
export class CircuitBreakerOpenError extends Error {
|
|
95
|
+
constructor(
|
|
96
|
+
public readonly circuitName: string,
|
|
97
|
+
public readonly resetTime: number,
|
|
98
|
+
) {
|
|
99
|
+
super(`Circuit breaker '${circuitName}' is open. Retry after ${Math.ceil(resetTime / 1000)}s`);
|
|
100
|
+
this.name = "CircuitBreakerOpenError";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Error thrown when operation times out
|
|
106
|
+
*/
|
|
107
|
+
export class CircuitBreakerTimeoutError extends Error {
|
|
108
|
+
constructor(
|
|
109
|
+
public readonly circuitName: string,
|
|
110
|
+
public readonly timeout: number,
|
|
111
|
+
) {
|
|
112
|
+
super(`Circuit breaker '${circuitName}' operation timed out after ${timeout}ms`);
|
|
113
|
+
this.name = "CircuitBreakerTimeoutError";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Failure record for tracking failures within time window
|
|
119
|
+
*/
|
|
120
|
+
interface FailureRecord {
|
|
121
|
+
timestamp: number;
|
|
122
|
+
error: Error;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Circuit Breaker Implementation
|
|
127
|
+
*/
|
|
128
|
+
export class CircuitBreaker {
|
|
129
|
+
private state: CircuitStateType = CircuitState.CLOSED;
|
|
130
|
+
private failures: FailureRecord[] = [];
|
|
131
|
+
private successes: number = 0;
|
|
132
|
+
private lastStateChange: number = Date.now();
|
|
133
|
+
private lastFailure: Date | null = null;
|
|
134
|
+
private lastSuccess: Date | null = null;
|
|
135
|
+
|
|
136
|
+
// Statistics
|
|
137
|
+
private totalRequests: number = 0;
|
|
138
|
+
private failedRequests: number = 0;
|
|
139
|
+
private successfulRequests: number = 0;
|
|
140
|
+
private rejectedRequests: number = 0;
|
|
141
|
+
|
|
142
|
+
// Configuration with defaults
|
|
143
|
+
private readonly name: string;
|
|
144
|
+
private readonly failureThreshold: number;
|
|
145
|
+
private readonly resetTimeout: number;
|
|
146
|
+
private readonly successThreshold: number;
|
|
147
|
+
private readonly failureWindow: number;
|
|
148
|
+
private readonly timeout: number;
|
|
149
|
+
private readonly isFailure: (error: Error) => boolean;
|
|
150
|
+
private readonly onOpen: ((name: string, failures: number) => void) | undefined;
|
|
151
|
+
private readonly onClose: ((name: string) => void) | undefined;
|
|
152
|
+
private readonly onHalfOpen: ((name: string) => void) | undefined;
|
|
153
|
+
|
|
154
|
+
private readonly logger = createLogger("CircuitBreaker");
|
|
155
|
+
|
|
156
|
+
constructor(options: CircuitBreakerOptions) {
|
|
157
|
+
this.name = options.name;
|
|
158
|
+
this.failureThreshold = options.failureThreshold ?? 5;
|
|
159
|
+
this.resetTimeout = options.resetTimeout ?? 30000;
|
|
160
|
+
this.successThreshold = options.successThreshold ?? 2;
|
|
161
|
+
this.failureWindow = options.failureWindow ?? 60000;
|
|
162
|
+
this.timeout = options.timeout ?? 30000;
|
|
163
|
+
this.isFailure = options.isFailure ?? this.defaultIsFailure;
|
|
164
|
+
this.onOpen = options.onOpen;
|
|
165
|
+
this.onClose = options.onClose;
|
|
166
|
+
this.onHalfOpen = options.onHalfOpen;
|
|
167
|
+
|
|
168
|
+
this.logger.debug(`Circuit breaker '${this.name}' initialized`, {
|
|
169
|
+
failureThreshold: this.failureThreshold,
|
|
170
|
+
resetTimeout: this.resetTimeout,
|
|
171
|
+
successThreshold: this.successThreshold,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Execute an operation through the circuit breaker
|
|
177
|
+
*/
|
|
178
|
+
async execute<T>(operation: () => Promise<T>): Promise<T> {
|
|
179
|
+
this.totalRequests++;
|
|
180
|
+
|
|
181
|
+
// Check if circuit should transition from OPEN to HALF_OPEN
|
|
182
|
+
if (this.state === CircuitState.OPEN) {
|
|
183
|
+
if (this.shouldAttemptReset()) {
|
|
184
|
+
this.transitionTo(CircuitState.HALF_OPEN);
|
|
185
|
+
} else {
|
|
186
|
+
this.rejectedRequests++;
|
|
187
|
+
const remainingTime = this.getRemainingResetTime();
|
|
188
|
+
throw new CircuitBreakerOpenError(this.name, remainingTime);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const result = await this.executeWithTimeout(operation);
|
|
194
|
+
this.onSuccess();
|
|
195
|
+
return result;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
this.onError(error as Error);
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Execute operation with timeout
|
|
204
|
+
*/
|
|
205
|
+
private async executeWithTimeout<T>(operation: () => Promise<T>): Promise<T> {
|
|
206
|
+
return new Promise<T>((resolve, reject) => {
|
|
207
|
+
const timeoutId = setTimeout(() => {
|
|
208
|
+
reject(new CircuitBreakerTimeoutError(this.name, this.timeout));
|
|
209
|
+
}, this.timeout);
|
|
210
|
+
|
|
211
|
+
operation()
|
|
212
|
+
.then((result) => {
|
|
213
|
+
clearTimeout(timeoutId);
|
|
214
|
+
resolve(result);
|
|
215
|
+
})
|
|
216
|
+
.catch((error) => {
|
|
217
|
+
clearTimeout(timeoutId);
|
|
218
|
+
reject(error);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Handle successful operation
|
|
225
|
+
*/
|
|
226
|
+
private onSuccess(): void {
|
|
227
|
+
this.successfulRequests++;
|
|
228
|
+
this.lastSuccess = new Date();
|
|
229
|
+
this.successes++;
|
|
230
|
+
|
|
231
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
232
|
+
if (this.successes >= this.successThreshold) {
|
|
233
|
+
this.transitionTo(CircuitState.CLOSED);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Clear old failures when successful in closed state
|
|
238
|
+
if (this.state === CircuitState.CLOSED) {
|
|
239
|
+
this.cleanOldFailures();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Handle operation error
|
|
245
|
+
*/
|
|
246
|
+
private onError(error: Error): void {
|
|
247
|
+
// Check if this error should count as a failure
|
|
248
|
+
if (!this.isFailure(error)) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.failedRequests++;
|
|
253
|
+
this.lastFailure = new Date();
|
|
254
|
+
|
|
255
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
256
|
+
// Any failure in half-open state reopens the circuit
|
|
257
|
+
this.transitionTo(CircuitState.OPEN);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (this.state === CircuitState.CLOSED) {
|
|
262
|
+
this.recordFailure(error);
|
|
263
|
+
|
|
264
|
+
// Clean old failures and check threshold
|
|
265
|
+
this.cleanOldFailures();
|
|
266
|
+
if (this.failures.length >= this.failureThreshold) {
|
|
267
|
+
this.transitionTo(CircuitState.OPEN);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Record a failure
|
|
274
|
+
*/
|
|
275
|
+
private recordFailure(error: Error): void {
|
|
276
|
+
this.failures.push({
|
|
277
|
+
timestamp: Date.now(),
|
|
278
|
+
error,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Remove failures outside the time window
|
|
284
|
+
*/
|
|
285
|
+
private cleanOldFailures(): void {
|
|
286
|
+
const cutoff = Date.now() - this.failureWindow;
|
|
287
|
+
this.failures = this.failures.filter((f) => f.timestamp > cutoff);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Check if we should attempt to reset (transition to half-open)
|
|
292
|
+
*/
|
|
293
|
+
private shouldAttemptReset(): boolean {
|
|
294
|
+
return Date.now() - this.lastStateChange >= this.resetTimeout;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get remaining time until reset attempt
|
|
299
|
+
*/
|
|
300
|
+
private getRemainingResetTime(): number {
|
|
301
|
+
const elapsed = Date.now() - this.lastStateChange;
|
|
302
|
+
return Math.max(0, this.resetTimeout - elapsed);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Transition to a new state
|
|
307
|
+
*/
|
|
308
|
+
private transitionTo(newState: CircuitStateType): void {
|
|
309
|
+
const oldState = this.state;
|
|
310
|
+
this.state = newState;
|
|
311
|
+
this.lastStateChange = Date.now();
|
|
312
|
+
|
|
313
|
+
this.logger.info(`Circuit '${this.name}' state change: ${oldState} -> ${newState}`);
|
|
314
|
+
|
|
315
|
+
// Reset counters on state change
|
|
316
|
+
if (newState === CircuitState.CLOSED) {
|
|
317
|
+
this.failures = [];
|
|
318
|
+
this.successes = 0;
|
|
319
|
+
this.onClose?.(this.name);
|
|
320
|
+
} else if (newState === CircuitState.OPEN) {
|
|
321
|
+
this.successes = 0;
|
|
322
|
+
this.onOpen?.(this.name, this.failures.length);
|
|
323
|
+
} else if (newState === CircuitState.HALF_OPEN) {
|
|
324
|
+
this.successes = 0;
|
|
325
|
+
this.onHalfOpen?.(this.name);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Default failure detection - all errors are failures except timeouts and circuit breaker errors
|
|
331
|
+
*/
|
|
332
|
+
private defaultIsFailure(error: Error): boolean {
|
|
333
|
+
// Don't count circuit breaker's own errors as failures
|
|
334
|
+
if (error instanceof CircuitBreakerOpenError || error instanceof CircuitBreakerTimeoutError) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Count all other errors as failures
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get current circuit breaker statistics
|
|
344
|
+
*/
|
|
345
|
+
getStats(): CircuitBreakerStats {
|
|
346
|
+
this.cleanOldFailures();
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
state: this.state,
|
|
350
|
+
failures: this.failures.length,
|
|
351
|
+
successes: this.successes,
|
|
352
|
+
lastFailure: this.lastFailure,
|
|
353
|
+
lastSuccess: this.lastSuccess,
|
|
354
|
+
totalRequests: this.totalRequests,
|
|
355
|
+
failedRequests: this.failedRequests,
|
|
356
|
+
successfulRequests: this.successfulRequests,
|
|
357
|
+
rejectedRequests: this.rejectedRequests,
|
|
358
|
+
timeInCurrentState: Date.now() - this.lastStateChange,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Get current state
|
|
364
|
+
*/
|
|
365
|
+
getState(): CircuitStateType {
|
|
366
|
+
// Check for automatic transition to half-open
|
|
367
|
+
if (this.state === CircuitState.OPEN && this.shouldAttemptReset()) {
|
|
368
|
+
this.transitionTo(CircuitState.HALF_OPEN);
|
|
369
|
+
}
|
|
370
|
+
return this.state;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Check if circuit is allowing requests
|
|
375
|
+
*/
|
|
376
|
+
isAvailable(): boolean {
|
|
377
|
+
const state = this.getState();
|
|
378
|
+
return state === CircuitState.CLOSED || state === CircuitState.HALF_OPEN;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Force circuit to open state (for testing or manual intervention)
|
|
383
|
+
*/
|
|
384
|
+
forceOpen(): void {
|
|
385
|
+
this.logger.warn(`Circuit '${this.name}' forced open`);
|
|
386
|
+
this.transitionTo(CircuitState.OPEN);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Force circuit to closed state (for testing or manual intervention)
|
|
391
|
+
*/
|
|
392
|
+
forceClose(): void {
|
|
393
|
+
this.logger.warn(`Circuit '${this.name}' forced closed`);
|
|
394
|
+
this.transitionTo(CircuitState.CLOSED);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Reset circuit breaker to initial state
|
|
399
|
+
*/
|
|
400
|
+
reset(): void {
|
|
401
|
+
this.state = CircuitState.CLOSED;
|
|
402
|
+
this.failures = [];
|
|
403
|
+
this.successes = 0;
|
|
404
|
+
this.lastStateChange = Date.now();
|
|
405
|
+
this.totalRequests = 0;
|
|
406
|
+
this.failedRequests = 0;
|
|
407
|
+
this.successfulRequests = 0;
|
|
408
|
+
this.rejectedRequests = 0;
|
|
409
|
+
this.lastFailure = null;
|
|
410
|
+
this.lastSuccess = null;
|
|
411
|
+
|
|
412
|
+
this.logger.info(`Circuit '${this.name}' reset`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Circuit Breaker Registry
|
|
418
|
+
* Manages multiple circuit breakers for different services
|
|
419
|
+
*/
|
|
420
|
+
export class CircuitBreakerRegistry {
|
|
421
|
+
private static instance: CircuitBreakerRegistry;
|
|
422
|
+
private breakers: Map<string, CircuitBreaker> = new Map();
|
|
423
|
+
private readonly logger = createLogger("CircuitBreakerRegistry");
|
|
424
|
+
|
|
425
|
+
private constructor() {}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Get singleton instance
|
|
429
|
+
*/
|
|
430
|
+
static getInstance(): CircuitBreakerRegistry {
|
|
431
|
+
if (!CircuitBreakerRegistry.instance) {
|
|
432
|
+
CircuitBreakerRegistry.instance = new CircuitBreakerRegistry();
|
|
433
|
+
}
|
|
434
|
+
return CircuitBreakerRegistry.instance;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Get or create a circuit breaker
|
|
439
|
+
*/
|
|
440
|
+
getBreaker(options: CircuitBreakerOptions): CircuitBreaker {
|
|
441
|
+
let breaker = this.breakers.get(options.name);
|
|
442
|
+
|
|
443
|
+
if (!breaker) {
|
|
444
|
+
breaker = new CircuitBreaker(options);
|
|
445
|
+
this.breakers.set(options.name, breaker);
|
|
446
|
+
this.logger.debug(`Created circuit breaker: ${options.name}`);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return breaker;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Get existing circuit breaker by name
|
|
454
|
+
*/
|
|
455
|
+
get(name: string): CircuitBreaker | undefined {
|
|
456
|
+
return this.breakers.get(name);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Get all circuit breaker statistics
|
|
461
|
+
*/
|
|
462
|
+
getAllStats(): Record<string, CircuitBreakerStats> {
|
|
463
|
+
const stats: Record<string, CircuitBreakerStats> = {};
|
|
464
|
+
|
|
465
|
+
for (const [name, breaker] of this.breakers) {
|
|
466
|
+
stats[name] = breaker.getStats();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return stats;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Get health summary of all circuit breakers
|
|
474
|
+
*/
|
|
475
|
+
getHealthSummary(): {
|
|
476
|
+
total: number;
|
|
477
|
+
closed: number;
|
|
478
|
+
open: number;
|
|
479
|
+
halfOpen: number;
|
|
480
|
+
healthy: boolean;
|
|
481
|
+
} {
|
|
482
|
+
let closed = 0;
|
|
483
|
+
let open = 0;
|
|
484
|
+
let halfOpen = 0;
|
|
485
|
+
|
|
486
|
+
for (const breaker of this.breakers.values()) {
|
|
487
|
+
switch (breaker.getState()) {
|
|
488
|
+
case CircuitState.CLOSED:
|
|
489
|
+
closed++;
|
|
490
|
+
break;
|
|
491
|
+
case CircuitState.OPEN:
|
|
492
|
+
open++;
|
|
493
|
+
break;
|
|
494
|
+
case CircuitState.HALF_OPEN:
|
|
495
|
+
halfOpen++;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
total: this.breakers.size,
|
|
502
|
+
closed,
|
|
503
|
+
open,
|
|
504
|
+
halfOpen,
|
|
505
|
+
healthy: open === 0,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Reset all circuit breakers
|
|
511
|
+
*/
|
|
512
|
+
resetAll(): void {
|
|
513
|
+
for (const breaker of this.breakers.values()) {
|
|
514
|
+
breaker.reset();
|
|
515
|
+
}
|
|
516
|
+
this.logger.info("All circuit breakers reset");
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Remove a circuit breaker
|
|
521
|
+
*/
|
|
522
|
+
remove(name: string): boolean {
|
|
523
|
+
return this.breakers.delete(name);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Clear all circuit breakers
|
|
528
|
+
*/
|
|
529
|
+
clear(): void {
|
|
530
|
+
this.breakers.clear();
|
|
531
|
+
this.logger.info("All circuit breakers cleared");
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Create a circuit breaker with default WordPress API settings
|
|
537
|
+
*/
|
|
538
|
+
export function createWordPressCircuitBreaker(
|
|
539
|
+
siteId: string,
|
|
540
|
+
options?: Partial<CircuitBreakerOptions>,
|
|
541
|
+
): CircuitBreaker {
|
|
542
|
+
return CircuitBreakerRegistry.getInstance().getBreaker({
|
|
543
|
+
name: `wordpress-api-${siteId}`,
|
|
544
|
+
failureThreshold: 5,
|
|
545
|
+
resetTimeout: 30000,
|
|
546
|
+
successThreshold: 2,
|
|
547
|
+
failureWindow: 60000,
|
|
548
|
+
timeout: 30000,
|
|
549
|
+
isFailure: (error: Error) => {
|
|
550
|
+
// Don't trip on client errors (4xx) except rate limiting
|
|
551
|
+
const message = error.message.toLowerCase();
|
|
552
|
+
if (message.includes("401") || message.includes("403")) {
|
|
553
|
+
return false; // Auth errors shouldn't trip the breaker
|
|
554
|
+
}
|
|
555
|
+
if (message.includes("404")) {
|
|
556
|
+
return false; // Not found is a valid response
|
|
557
|
+
}
|
|
558
|
+
if (message.includes("429")) {
|
|
559
|
+
return true; // Rate limiting should trip the breaker
|
|
560
|
+
}
|
|
561
|
+
// Server errors (5xx) and network errors should trip
|
|
562
|
+
return (
|
|
563
|
+
message.includes("5") ||
|
|
564
|
+
message.includes("timeout") ||
|
|
565
|
+
message.includes("network") ||
|
|
566
|
+
message.includes("econnrefused") ||
|
|
567
|
+
message.includes("econnreset")
|
|
568
|
+
);
|
|
569
|
+
},
|
|
570
|
+
...options,
|
|
571
|
+
});
|
|
572
|
+
}
|
package/src/utils/debug.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* This prevents console.log from interfering with MCP STDIO communication.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { DebugInfo } from "
|
|
9
|
-
import { config } from "
|
|
8
|
+
import type { DebugInfo } from "@/types/index.js";
|
|
9
|
+
import { config } from "@/config/Config.js";
|
|
10
10
|
|
|
11
11
|
// Log levels
|
|
12
12
|
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
@@ -25,7 +25,7 @@ export interface StructuredLogger extends Logger {
|
|
|
25
25
|
child(context: Record<string, unknown>): StructuredLogger;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
import { ConfigHelpers } from "
|
|
28
|
+
import { ConfigHelpers } from "@/config/Config.js";
|
|
29
29
|
|
|
30
30
|
// Check if debug mode is enabled
|
|
31
31
|
const isDebugMode = (): boolean => ConfigHelpers.shouldDebug();
|
package/src/utils/error.ts
CHANGED
package/src/utils/index.ts
CHANGED
package/src/utils/logger.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Integrates with the centralized Config system for environment-aware behavior.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { ConfigHelpers } from "
|
|
8
|
+
import { ConfigHelpers } from "@/config/Config.js";
|
|
9
9
|
|
|
10
10
|
export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
|
11
11
|
export type LogContext = Record<string, unknown>;
|
package/src/utils/toolWrapper.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { getErrorMessage } from "./error.js";
|
|
7
|
-
import type { IWordPressClient } from "
|
|
8
|
-
import type { BaseToolParams } from "
|
|
7
|
+
import type { IWordPressClient } from "@/types/client.js";
|
|
8
|
+
import type { BaseToolParams } from "@/types/tools.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Wrapper for tool methods that standardizes error handling
|
|
File without changes
|