mcp-wordpress 1.1.7 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +388 -66
- package/dist/cache/CacheInvalidation.d.ts +118 -0
- package/dist/cache/CacheInvalidation.d.ts.map +1 -0
- package/dist/cache/CacheInvalidation.js +349 -0
- package/dist/cache/CacheInvalidation.js.map +1 -0
- package/dist/cache/CacheManager.d.ts +143 -0
- package/dist/cache/CacheManager.d.ts.map +1 -0
- package/dist/cache/CacheManager.js +308 -0
- package/dist/cache/CacheManager.js.map +1 -0
- package/dist/cache/HttpCacheWrapper.d.ts +121 -0
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -0
- package/dist/cache/HttpCacheWrapper.js +280 -0
- package/dist/cache/HttpCacheWrapper.js.map +1 -0
- package/dist/cache/__tests__/CacheInvalidation.test.d.ts +5 -0
- package/dist/cache/__tests__/CacheInvalidation.test.d.ts.map +1 -0
- package/dist/cache/__tests__/CacheInvalidation.test.js +236 -0
- package/dist/cache/__tests__/CacheInvalidation.test.js.map +1 -0
- package/dist/cache/__tests__/CacheManager.test.d.ts +5 -0
- package/dist/cache/__tests__/CacheManager.test.d.ts.map +1 -0
- package/dist/cache/__tests__/CacheManager.test.js +233 -0
- package/dist/cache/__tests__/CacheManager.test.js.map +1 -0
- package/dist/cache/__tests__/CachedWordPressClient.test.d.ts +5 -0
- package/dist/cache/__tests__/CachedWordPressClient.test.d.ts.map +1 -0
- package/dist/cache/__tests__/CachedWordPressClient.test.js +228 -0
- package/dist/cache/__tests__/CachedWordPressClient.test.js.map +1 -0
- package/dist/cache/__tests__/HttpCacheWrapper.test.d.ts +5 -0
- package/dist/cache/__tests__/HttpCacheWrapper.test.d.ts.map +1 -0
- package/dist/cache/__tests__/HttpCacheWrapper.test.js +296 -0
- package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -0
- package/dist/cache/index.d.ts +12 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +9 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/client/CachedWordPressClient.d.ts +160 -0
- package/dist/client/CachedWordPressClient.d.ts.map +1 -0
- package/dist/client/CachedWordPressClient.js +338 -0
- package/dist/client/CachedWordPressClient.js.map +1 -0
- package/dist/client/WordPressClient.d.ts +81 -0
- package/dist/client/WordPressClient.d.ts.map +1 -0
- package/dist/client/WordPressClient.js +354 -0
- package/dist/client/WordPressClient.js.map +1 -0
- package/dist/config/ConfigurationSchema.d.ts +281 -0
- package/dist/config/ConfigurationSchema.d.ts.map +1 -0
- package/dist/config/ConfigurationSchema.js +205 -0
- package/dist/config/ConfigurationSchema.js.map +1 -0
- package/dist/config/ServerConfiguration.d.ts +38 -0
- package/dist/config/ServerConfiguration.d.ts.map +1 -0
- package/dist/config/ServerConfiguration.js +158 -0
- package/dist/config/ServerConfiguration.js.map +1 -0
- package/dist/docs/DocumentationGenerator.d.ts +184 -0
- package/dist/docs/DocumentationGenerator.d.ts.map +1 -0
- package/dist/docs/DocumentationGenerator.js +735 -0
- package/dist/docs/DocumentationGenerator.js.map +1 -0
- package/dist/docs/MarkdownFormatter.d.ts +84 -0
- package/dist/docs/MarkdownFormatter.d.ts.map +1 -0
- package/dist/docs/MarkdownFormatter.js +448 -0
- package/dist/docs/MarkdownFormatter.js.map +1 -0
- package/dist/docs/index.d.ts +8 -0
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +7 -0
- package/dist/docs/index.js.map +1 -0
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -212
- package/dist/index.js.map +1 -1
- package/dist/performance/AnomalyDetector.d.ts +63 -0
- package/dist/performance/AnomalyDetector.d.ts.map +1 -0
- package/dist/performance/AnomalyDetector.js +222 -0
- package/dist/performance/AnomalyDetector.js.map +1 -0
- package/dist/performance/BenchmarkAnalyzer.d.ts +67 -0
- package/dist/performance/BenchmarkAnalyzer.d.ts.map +1 -0
- package/dist/performance/BenchmarkAnalyzer.js +301 -0
- package/dist/performance/BenchmarkAnalyzer.js.map +1 -0
- package/dist/performance/MetricsCollector.d.ts +139 -0
- package/dist/performance/MetricsCollector.d.ts.map +1 -0
- package/dist/performance/MetricsCollector.js +320 -0
- package/dist/performance/MetricsCollector.js.map +1 -0
- package/dist/performance/PerformanceAnalytics.d.ts +162 -0
- package/dist/performance/PerformanceAnalytics.d.ts.map +1 -0
- package/dist/performance/PerformanceAnalytics.js +554 -0
- package/dist/performance/PerformanceAnalytics.js.map +1 -0
- package/dist/performance/PerformanceMonitor.d.ts +202 -0
- package/dist/performance/PerformanceMonitor.d.ts.map +1 -0
- package/dist/performance/PerformanceMonitor.js +478 -0
- package/dist/performance/PerformanceMonitor.js.map +1 -0
- package/dist/performance/TrendAnalyzer.d.ts +69 -0
- package/dist/performance/TrendAnalyzer.d.ts.map +1 -0
- package/dist/performance/TrendAnalyzer.js +203 -0
- package/dist/performance/TrendAnalyzer.js.map +1 -0
- package/dist/performance/index.d.ts +11 -0
- package/dist/performance/index.d.ts.map +1 -0
- package/dist/performance/index.js +8 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/security/InputValidator.d.ts +215 -0
- package/dist/security/InputValidator.d.ts.map +1 -0
- package/dist/security/InputValidator.js +278 -0
- package/dist/security/InputValidator.js.map +1 -0
- package/dist/security/SecurityConfig.d.ts +129 -0
- package/dist/security/SecurityConfig.d.ts.map +1 -0
- package/dist/security/SecurityConfig.js +262 -0
- package/dist/security/SecurityConfig.js.map +1 -0
- package/dist/server/ConnectionTester.d.ts +24 -0
- package/dist/server/ConnectionTester.d.ts.map +1 -0
- package/dist/server/ConnectionTester.js +61 -0
- package/dist/server/ConnectionTester.js.map +1 -0
- package/dist/server/ToolRegistry.d.ts +46 -0
- package/dist/server/ToolRegistry.d.ts.map +1 -0
- package/dist/server/ToolRegistry.js +148 -0
- package/dist/server/ToolRegistry.js.map +1 -0
- package/dist/tools/BaseToolClass.d.ts +76 -0
- package/dist/tools/BaseToolClass.d.ts.map +1 -0
- package/dist/tools/BaseToolClass.js +104 -0
- package/dist/tools/BaseToolClass.js.map +1 -0
- package/dist/tools/BaseToolManager.d.ts +26 -0
- package/dist/tools/BaseToolManager.d.ts.map +1 -0
- package/dist/tools/BaseToolManager.js +56 -0
- package/dist/tools/BaseToolManager.js.map +1 -0
- package/dist/tools/base.d.ts +37 -0
- package/dist/tools/base.d.ts.map +1 -0
- package/dist/tools/base.js +60 -0
- package/dist/tools/base.js.map +1 -0
- package/dist/tools/cache.d.ts +260 -0
- package/dist/tools/cache.d.ts.map +1 -0
- package/dist/tools/cache.js +237 -0
- package/dist/tools/cache.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/performance.d.ts +63 -0
- package/dist/tools/performance.d.ts.map +1 -0
- package/dist/tools/performance.js +865 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/types/client.d.ts +1 -0
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts +4 -0
- package/dist/utils/toolWrapper.d.ts.map +1 -1
- package/dist/utils/toolWrapper.js +11 -0
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation.d.ts +68 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +185 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/CACHING.md +340 -0
- package/docs/DOCKER.md +451 -0
- package/docs/PERFORMANCE_MONITORING.md +471 -0
- package/docs/SECURITY_TESTING.md +393 -0
- package/docs/api/README.md +200 -0
- package/docs/api/categories/auth.md +40 -0
- package/docs/api/categories/cache.md +41 -0
- package/docs/api/categories/comment.md +44 -0
- package/docs/api/categories/media.md +43 -0
- package/docs/api/categories/page.md +43 -0
- package/docs/api/categories/performance.md +44 -0
- package/docs/api/categories/post.md +43 -0
- package/docs/api/categories/site.md +43 -0
- package/docs/api/categories/taxonomy.md +47 -0
- package/docs/api/categories/user.md +43 -0
- package/docs/api/openapi.json +3305 -0
- package/docs/api/summary.json +12 -0
- package/docs/api/tools/wp_approve_comment.md +98 -0
- package/docs/api/tools/wp_cache_clear.md +120 -0
- package/docs/api/tools/wp_cache_info.md +119 -0
- package/docs/api/tools/wp_cache_stats.md +119 -0
- package/docs/api/tools/wp_cache_warm.md +119 -0
- package/docs/api/tools/wp_create_application_password.md +102 -0
- package/docs/api/tools/wp_create_category.md +102 -0
- package/docs/api/tools/wp_create_comment.md +128 -0
- package/docs/api/tools/wp_create_page.md +135 -0
- package/docs/api/tools/wp_create_post.md +147 -0
- package/docs/api/tools/wp_create_tag.md +101 -0
- package/docs/api/tools/wp_create_user.md +135 -0
- package/docs/api/tools/wp_delete_application_password.md +101 -0
- package/docs/api/tools/wp_delete_category.md +100 -0
- package/docs/api/tools/wp_delete_comment.md +101 -0
- package/docs/api/tools/wp_delete_media.md +108 -0
- package/docs/api/tools/wp_delete_page.md +108 -0
- package/docs/api/tools/wp_delete_post.md +117 -0
- package/docs/api/tools/wp_delete_tag.md +100 -0
- package/docs/api/tools/wp_delete_user.md +108 -0
- package/docs/api/tools/wp_get_application_passwords.md +103 -0
- package/docs/api/tools/wp_get_auth_status.md +101 -0
- package/docs/api/tools/wp_get_category.md +103 -0
- package/docs/api/tools/wp_get_comment.md +103 -0
- package/docs/api/tools/wp_get_current_user.md +101 -0
- package/docs/api/tools/wp_get_media.md +103 -0
- package/docs/api/tools/wp_get_page.md +103 -0
- package/docs/api/tools/wp_get_page_revisions.md +103 -0
- package/docs/api/tools/wp_get_post.md +112 -0
- package/docs/api/tools/wp_get_post_revisions.md +103 -0
- package/docs/api/tools/wp_get_site_settings.md +108 -0
- package/docs/api/tools/wp_get_tag.md +103 -0
- package/docs/api/tools/wp_get_user.md +103 -0
- package/docs/api/tools/wp_list_categories.md +111 -0
- package/docs/api/tools/wp_list_comments.md +111 -0
- package/docs/api/tools/wp_list_media.md +145 -0
- package/docs/api/tools/wp_list_pages.md +145 -0
- package/docs/api/tools/wp_list_posts.md +156 -0
- package/docs/api/tools/wp_list_tags.md +110 -0
- package/docs/api/tools/wp_list_users.md +111 -0
- package/docs/api/tools/wp_performance_alerts.md +162 -0
- package/docs/api/tools/wp_performance_benchmark.md +160 -0
- package/docs/api/tools/wp_performance_export.md +162 -0
- package/docs/api/tools/wp_performance_history.md +161 -0
- package/docs/api/tools/wp_performance_optimize.md +162 -0
- package/docs/api/tools/wp_performance_stats.md +160 -0
- package/docs/api/tools/wp_search_site.md +99 -0
- package/docs/api/tools/wp_spam_comment.md +98 -0
- package/docs/api/tools/wp_switch_auth_method.md +122 -0
- package/docs/api/tools/wp_test_auth.md +96 -0
- package/docs/api/tools/wp_update_category.md +102 -0
- package/docs/api/tools/wp_update_comment.md +127 -0
- package/docs/api/tools/wp_update_media.md +129 -0
- package/docs/api/tools/wp_update_page.md +135 -0
- package/docs/api/tools/wp_update_post.md +144 -0
- package/docs/api/tools/wp_update_site_settings.md +127 -0
- package/docs/api/tools/wp_update_tag.md +102 -0
- package/docs/api/tools/wp_update_user.md +134 -0
- package/docs/api/tools/wp_upload_media.md +131 -0
- package/docs/api/types/WordPressPost.md +39 -0
- package/docs/contract-testing.md +183 -0
- package/docs/developer/NPM_AUTH_SETUP.md +3 -3
- package/docs/wordpress-rest-api-authentication-troubleshooting.md +218 -0
- package/package.json +84 -64
- package/src/cache/CacheInvalidation.ts +421 -0
- package/src/cache/CacheManager.ts +391 -0
- package/src/cache/HttpCacheWrapper.ts +372 -0
- package/src/cache/__tests__/CacheInvalidation.test.ts +299 -0
- package/src/cache/__tests__/CacheManager.test.ts +300 -0
- package/src/cache/__tests__/CachedWordPressClient.test.ts +304 -0
- package/src/cache/__tests__/HttpCacheWrapper.test.ts +359 -0
- package/src/cache/index.ts +26 -0
- package/src/client/CachedWordPressClient.ts +442 -0
- package/src/config/ConfigurationSchema.ts +246 -0
- package/src/config/ServerConfiguration.ts +215 -0
- package/src/docs/DocumentationGenerator.ts +952 -0
- package/src/docs/MarkdownFormatter.ts +494 -0
- package/src/docs/index.ts +21 -0
- package/src/index.ts +14 -274
- package/src/performance/MetricsCollector.ts +447 -0
- package/src/performance/PerformanceAnalytics.ts +762 -0
- package/src/performance/PerformanceMonitor.ts +649 -0
- package/src/performance/index.ts +28 -0
- package/src/security/InputValidator.ts +319 -0
- package/src/security/SecurityConfig.ts +301 -0
- package/src/server/ConnectionTester.ts +74 -0
- package/src/server/ToolRegistry.ts +194 -0
- package/src/tools/BaseToolManager.ts +66 -0
- package/src/tools/cache.ts +259 -0
- package/src/tools/index.ts +2 -0
- package/src/tools/performance.ts +948 -0
- package/src/types/client.ts +1 -0
- package/src/utils/toolWrapper.ts +11 -0
- package/src/utils/validation.ts +259 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intelligent caching system for WordPress MCP Server
|
|
3
|
+
* Implements multi-layer caching with TTL, LRU eviction, and site-specific keys
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
|
|
8
|
+
export interface CacheEntry<T = any> {
|
|
9
|
+
value: T;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
ttl: number;
|
|
12
|
+
etag?: string | undefined;
|
|
13
|
+
lastModified?: string | undefined;
|
|
14
|
+
accessCount: number;
|
|
15
|
+
lastAccessed: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CacheStats {
|
|
19
|
+
hits: number;
|
|
20
|
+
misses: number;
|
|
21
|
+
evictions: number;
|
|
22
|
+
totalSize: number;
|
|
23
|
+
hitRate: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CacheConfig {
|
|
27
|
+
maxSize: number;
|
|
28
|
+
defaultTTL: number;
|
|
29
|
+
enableLRU: boolean;
|
|
30
|
+
enableStats: boolean;
|
|
31
|
+
sitePrefix?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* High-performance in-memory cache with TTL and LRU eviction
|
|
36
|
+
*/
|
|
37
|
+
export class CacheManager {
|
|
38
|
+
private cache: Map<string, CacheEntry> = new Map();
|
|
39
|
+
private accessOrder: string[] = [];
|
|
40
|
+
private stats: CacheStats = {
|
|
41
|
+
hits: 0,
|
|
42
|
+
misses: 0,
|
|
43
|
+
evictions: 0,
|
|
44
|
+
totalSize: 0,
|
|
45
|
+
hitRate: 0
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
constructor(private config: CacheConfig) {
|
|
49
|
+
// Start cleanup interval
|
|
50
|
+
this.startCleanupInterval();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Generate cache key with site prefix and parameter hash
|
|
55
|
+
*/
|
|
56
|
+
generateKey(siteId: string, endpoint: string, params?: any): string {
|
|
57
|
+
const baseKey = `${siteId}:${endpoint}`;
|
|
58
|
+
|
|
59
|
+
if (!params || Object.keys(params).length === 0) {
|
|
60
|
+
return baseKey;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create deterministic hash of parameters
|
|
64
|
+
const paramHash = crypto
|
|
65
|
+
.createHash('md5')
|
|
66
|
+
.update(JSON.stringify(this.normalizeParams(params)))
|
|
67
|
+
.digest('hex')
|
|
68
|
+
.substring(0, 8);
|
|
69
|
+
|
|
70
|
+
return `${baseKey}:${paramHash}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get value from cache if not expired
|
|
75
|
+
*/
|
|
76
|
+
get<T>(key: string): T | null {
|
|
77
|
+
const entry = this.cache.get(key);
|
|
78
|
+
|
|
79
|
+
if (!entry) {
|
|
80
|
+
this.stats.misses++;
|
|
81
|
+
this.updateHitRate();
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if expired
|
|
86
|
+
if (this.isExpired(entry)) {
|
|
87
|
+
this.cache.delete(key);
|
|
88
|
+
this.removeFromAccessOrder(key);
|
|
89
|
+
this.stats.misses++;
|
|
90
|
+
this.updateHitRate();
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Update access tracking
|
|
95
|
+
entry.accessCount++;
|
|
96
|
+
entry.lastAccessed = Date.now();
|
|
97
|
+
this.updateAccessOrder(key);
|
|
98
|
+
|
|
99
|
+
this.stats.hits++;
|
|
100
|
+
this.updateHitRate();
|
|
101
|
+
|
|
102
|
+
return entry.value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Set value in cache with TTL
|
|
107
|
+
*/
|
|
108
|
+
set<T>(
|
|
109
|
+
key: string,
|
|
110
|
+
value: T,
|
|
111
|
+
ttl: number = this.config.defaultTTL,
|
|
112
|
+
etag?: string,
|
|
113
|
+
lastModified?: string
|
|
114
|
+
): void {
|
|
115
|
+
// Check if we need to evict entries
|
|
116
|
+
if (this.cache.size >= this.config.maxSize && !this.cache.has(key)) {
|
|
117
|
+
this.evictLRU();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const entry: CacheEntry<T> = {
|
|
121
|
+
value,
|
|
122
|
+
timestamp: Date.now(),
|
|
123
|
+
ttl,
|
|
124
|
+
etag,
|
|
125
|
+
lastModified,
|
|
126
|
+
accessCount: 1,
|
|
127
|
+
lastAccessed: Date.now()
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// If updating existing entry, remove from access order first
|
|
131
|
+
if (this.cache.has(key)) {
|
|
132
|
+
this.removeFromAccessOrder(key);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.cache.set(key, entry);
|
|
136
|
+
this.accessOrder.push(key);
|
|
137
|
+
this.stats.totalSize = this.cache.size;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if cache entry exists and is valid
|
|
142
|
+
*/
|
|
143
|
+
has(key: string): boolean {
|
|
144
|
+
const entry = this.cache.get(key);
|
|
145
|
+
return entry !== undefined && !this.isExpired(entry);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Delete specific cache entry
|
|
150
|
+
*/
|
|
151
|
+
delete(key: string): boolean {
|
|
152
|
+
const existed = this.cache.delete(key);
|
|
153
|
+
if (existed) {
|
|
154
|
+
this.removeFromAccessOrder(key);
|
|
155
|
+
this.stats.totalSize = this.cache.size;
|
|
156
|
+
}
|
|
157
|
+
return existed;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Clear all cache entries
|
|
162
|
+
*/
|
|
163
|
+
clear(): void {
|
|
164
|
+
this.cache.clear();
|
|
165
|
+
this.accessOrder = [];
|
|
166
|
+
this.stats.totalSize = 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Clear cache entries for specific site
|
|
171
|
+
*/
|
|
172
|
+
clearSite(siteId: string): number {
|
|
173
|
+
let cleared = 0;
|
|
174
|
+
const keysToDelete: string[] = [];
|
|
175
|
+
|
|
176
|
+
for (const key of this.cache.keys()) {
|
|
177
|
+
if (key.startsWith(`${siteId}:`)) {
|
|
178
|
+
keysToDelete.push(key);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const key of keysToDelete) {
|
|
183
|
+
this.delete(key);
|
|
184
|
+
cleared++;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return cleared;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Clear cache entries matching pattern
|
|
192
|
+
*/
|
|
193
|
+
clearPattern(pattern: RegExp): number {
|
|
194
|
+
let cleared = 0;
|
|
195
|
+
const keysToDelete: string[] = [];
|
|
196
|
+
|
|
197
|
+
for (const key of this.cache.keys()) {
|
|
198
|
+
if (pattern.test(key)) {
|
|
199
|
+
keysToDelete.push(key);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (const key of keysToDelete) {
|
|
204
|
+
this.delete(key);
|
|
205
|
+
cleared++;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return cleared;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get cache statistics
|
|
213
|
+
*/
|
|
214
|
+
getStats(): CacheStats {
|
|
215
|
+
return { ...this.stats };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get cache entry with metadata
|
|
220
|
+
*/
|
|
221
|
+
getEntry(key: string): CacheEntry | null {
|
|
222
|
+
const entry = this.cache.get(key);
|
|
223
|
+
if (!entry || this.isExpired(entry)) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return { ...entry };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check if entry supports conditional requests
|
|
231
|
+
*/
|
|
232
|
+
supportsConditionalRequest(key: string): boolean {
|
|
233
|
+
const entry = this.cache.get(key);
|
|
234
|
+
return !!(entry && (entry.etag || entry.lastModified));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get conditional request headers
|
|
239
|
+
*/
|
|
240
|
+
getConditionalHeaders(key: string): Record<string, string> {
|
|
241
|
+
const entry = this.cache.get(key);
|
|
242
|
+
if (!entry) return {};
|
|
243
|
+
|
|
244
|
+
const headers: Record<string, string> = {};
|
|
245
|
+
|
|
246
|
+
if (entry.etag) {
|
|
247
|
+
headers['If-None-Match'] = entry.etag;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (entry.lastModified) {
|
|
251
|
+
headers['If-Modified-Since'] = entry.lastModified;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return headers;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Check if entry is expired
|
|
259
|
+
*/
|
|
260
|
+
private isExpired(entry: CacheEntry): boolean {
|
|
261
|
+
return Date.now() - entry.timestamp > entry.ttl;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Evict least recently used entry
|
|
266
|
+
*/
|
|
267
|
+
private evictLRU(): void {
|
|
268
|
+
if (this.accessOrder.length === 0) return;
|
|
269
|
+
|
|
270
|
+
const lruKey = this.accessOrder[0];
|
|
271
|
+
this.cache.delete(lruKey);
|
|
272
|
+
this.accessOrder.shift();
|
|
273
|
+
this.stats.evictions++;
|
|
274
|
+
this.stats.totalSize = this.cache.size;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Update access order for LRU tracking
|
|
279
|
+
*/
|
|
280
|
+
private updateAccessOrder(key: string): void {
|
|
281
|
+
if (!this.config.enableLRU) return;
|
|
282
|
+
|
|
283
|
+
const index = this.accessOrder.indexOf(key);
|
|
284
|
+
if (index > -1) {
|
|
285
|
+
this.accessOrder.splice(index, 1);
|
|
286
|
+
}
|
|
287
|
+
this.accessOrder.push(key);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Remove key from access order
|
|
292
|
+
*/
|
|
293
|
+
private removeFromAccessOrder(key: string): void {
|
|
294
|
+
const index = this.accessOrder.indexOf(key);
|
|
295
|
+
if (index > -1) {
|
|
296
|
+
this.accessOrder.splice(index, 1);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Normalize parameters for consistent hashing
|
|
302
|
+
*/
|
|
303
|
+
private normalizeParams(params: any): any {
|
|
304
|
+
if (typeof params !== 'object' || params === null) {
|
|
305
|
+
return params;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Sort object keys for consistent hashing
|
|
309
|
+
const normalized: any = Array.isArray(params) ? [] : {};
|
|
310
|
+
const keys = Object.keys(params).sort();
|
|
311
|
+
|
|
312
|
+
for (const key of keys) {
|
|
313
|
+
normalized[key] = this.normalizeParams(params[key]);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return normalized;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Update hit rate calculation
|
|
321
|
+
*/
|
|
322
|
+
private updateHitRate(): void {
|
|
323
|
+
const total = this.stats.hits + this.stats.misses;
|
|
324
|
+
this.stats.hitRate = total > 0 ? this.stats.hits / total : 0;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Start periodic cleanup of expired entries
|
|
329
|
+
*/
|
|
330
|
+
private startCleanupInterval(): void {
|
|
331
|
+
setInterval(() => {
|
|
332
|
+
this.cleanupExpired();
|
|
333
|
+
}, 60000); // Cleanup every minute
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Remove expired entries
|
|
338
|
+
*/
|
|
339
|
+
private cleanupExpired(): void {
|
|
340
|
+
const now = Date.now();
|
|
341
|
+
const keysToDelete: string[] = [];
|
|
342
|
+
|
|
343
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
344
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
345
|
+
keysToDelete.push(key);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
for (const key of keysToDelete) {
|
|
350
|
+
this.cache.delete(key);
|
|
351
|
+
this.removeFromAccessOrder(key);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this.stats.totalSize = this.cache.size;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Cache configuration presets for different data types
|
|
360
|
+
*/
|
|
361
|
+
export const CachePresets = {
|
|
362
|
+
// Static data: site settings, user roles
|
|
363
|
+
STATIC: {
|
|
364
|
+
ttl: 4 * 60 * 60 * 1000, // 4 hours
|
|
365
|
+
cacheControl: 'public, max-age=14400'
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
// Semi-static: categories, tags, user profiles
|
|
369
|
+
SEMI_STATIC: {
|
|
370
|
+
ttl: 2 * 60 * 60 * 1000, // 2 hours
|
|
371
|
+
cacheControl: 'public, max-age=7200'
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
// Dynamic: posts, pages, comments
|
|
375
|
+
DYNAMIC: {
|
|
376
|
+
ttl: 15 * 60 * 1000, // 15 minutes
|
|
377
|
+
cacheControl: 'public, max-age=900'
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
// Session: authentication, current user
|
|
381
|
+
SESSION: {
|
|
382
|
+
ttl: 30 * 60 * 1000, // 30 minutes
|
|
383
|
+
cacheControl: 'private, max-age=1800'
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
// Fast changing: real-time data
|
|
387
|
+
REALTIME: {
|
|
388
|
+
ttl: 60 * 1000, // 1 minute
|
|
389
|
+
cacheControl: 'public, max-age=60'
|
|
390
|
+
}
|
|
391
|
+
};
|