mcp-wordpress 1.2.2 → 1.3.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 +210 -182
- package/dist/cache/CacheInvalidation.d.ts +3 -3
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +119 -119
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +5 -0
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +26 -16
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
- package/dist/cache/HttpCacheWrapper.js +29 -29
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/__tests__/CacheInvalidation.test.js +96 -94
- package/dist/cache/__tests__/CacheInvalidation.test.js.map +1 -1
- package/dist/cache/__tests__/CacheManager.test.js +113 -113
- package/dist/cache/__tests__/CacheManager.test.js.map +1 -1
- package/dist/cache/__tests__/CachedWordPressClient.test.js +102 -99
- package/dist/cache/__tests__/CachedWordPressClient.test.js.map +1 -1
- package/dist/cache/__tests__/HttpCacheWrapper.test.js +98 -95
- package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -1
- package/dist/cache/index.d.ts +7 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +4 -4
- package/dist/cache/index.js.map +1 -1
- package/dist/client/CachedWordPressClient.d.ts +4 -4
- package/dist/client/CachedWordPressClient.d.ts.map +1 -1
- package/dist/client/CachedWordPressClient.js +55 -51
- package/dist/client/CachedWordPressClient.js.map +1 -1
- package/dist/client/api.d.ts +10 -10
- package/dist/client/api.js +158 -158
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.d.ts +2 -2
- package/dist/client/auth.js +72 -72
- package/dist/client/managers/AuthenticationManager.d.ts +2 -2
- package/dist/client/managers/AuthenticationManager.js +46 -46
- package/dist/client/managers/BaseManager.d.ts +1 -1
- package/dist/client/managers/BaseManager.js +9 -9
- package/dist/client/managers/RequestManager.d.ts +5 -3
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +39 -19
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/client/managers/index.d.ts +3 -3
- package/dist/client/managers/index.js +3 -3
- package/dist/config/ConfigurationSchema.d.ts +2 -2
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +40 -40
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +2 -2
- package/dist/config/ServerConfiguration.js +35 -35
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
- package/dist/docs/DocumentationGenerator.js +296 -255
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/docs/MarkdownFormatter.d.ts +1 -1
- package/dist/docs/MarkdownFormatter.d.ts.map +1 -1
- package/dist/docs/MarkdownFormatter.js +60 -51
- package/dist/docs/MarkdownFormatter.js.map +1 -1
- package/dist/docs/index.d.ts +3 -3
- package/dist/docs/index.d.ts.map +1 -1
- package/dist/docs/index.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -16
- package/dist/index.js.map +1 -1
- package/dist/mcp-wordpress-1.3.0.tgz +0 -0
- package/dist/performance/MetricsCollector.d.ts +3 -3
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js +33 -27
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/performance/PerformanceAnalytics.d.ts +12 -12
- package/dist/performance/PerformanceAnalytics.d.ts.map +1 -1
- package/dist/performance/PerformanceAnalytics.js +200 -154
- package/dist/performance/PerformanceAnalytics.js.map +1 -1
- package/dist/performance/PerformanceMonitor.d.ts +5 -5
- package/dist/performance/PerformanceMonitor.d.ts.map +1 -1
- package/dist/performance/PerformanceMonitor.js +53 -52
- package/dist/performance/PerformanceMonitor.js.map +1 -1
- package/dist/performance/index.d.ts +6 -6
- package/dist/performance/index.d.ts.map +1 -1
- package/dist/performance/index.js +3 -3
- package/dist/security/InputValidator.d.ts +1 -1
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +111 -88
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityConfig.d.ts +5 -5
- package/dist/security/SecurityConfig.js +92 -92
- package/dist/security/SecurityConfig.js.map +1 -1
- package/dist/server/ConnectionTester.d.ts +1 -1
- package/dist/server/ConnectionTester.d.ts.map +1 -1
- package/dist/server/ConnectionTester.js +4 -4
- package/dist/server/ConnectionTester.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts +2 -2
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js +35 -32
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/tools/BaseToolManager.js +5 -5
- package/dist/tools/auth.d.ts +2 -2
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +32 -31
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/cache.d.ts +1 -1
- package/dist/tools/cache.d.ts.map +1 -1
- package/dist/tools/cache.js +71 -71
- package/dist/tools/cache.js.map +1 -1
- package/dist/tools/comments.d.ts +2 -2
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +79 -79
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/index.d.ts +10 -10
- package/dist/tools/index.js +10 -10
- package/dist/tools/media.d.ts +2 -2
- package/dist/tools/media.js +80 -80
- package/dist/tools/pages.d.ts +2 -2
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +75 -75
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance.d.ts +1 -1
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +311 -287
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts.d.ts +2 -2
- package/dist/tools/posts.d.ts.map +1 -1
- package/dist/tools/posts.js +94 -94
- package/dist/tools/posts.js.map +1 -1
- package/dist/tools/site.d.ts +2 -2
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +60 -60
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts +2 -2
- package/dist/tools/taxonomies.js +89 -89
- package/dist/tools/users.d.ts +2 -2
- package/dist/tools/users.js +68 -68
- package/dist/tools/users.js.map +1 -1
- package/dist/types/client.d.ts +13 -13
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js +12 -12
- package/dist/types/client.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -3
- package/dist/types/mcp.d.ts +7 -7
- package/dist/types/wordpress.d.ts +21 -21
- package/dist/types/wordpress.d.ts.map +1 -1
- package/dist/utils/debug.d.ts +2 -2
- package/dist/utils/debug.js +28 -28
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.js +13 -13
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts.map +1 -1
- package/dist/utils/toolWrapper.js +5 -5
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +41 -31
- package/dist/utils/validation.js.map +1 -1
- package/docs/CACHING.md +36 -2
- package/docs/DOCKER.md +24 -18
- package/docs/PERFORMANCE_MONITORING.md +49 -1
- package/docs/SECURITY_TESTING.md +30 -1
- package/docs/api/README.md +9 -1
- package/docs/api/summary.json +1 -1
- package/docs/contract-testing.md +24 -3
- package/docs/developer/GITHUB_ACTIONS_SETUP.md +8 -2
- package/docs/developer/MAINTENANCE.md +29 -3
- package/docs/developer/MIGRATION_GUIDE.md +13 -1
- package/docs/developer/NPM_AUTH_SETUP.md +13 -2
- package/docs/developer/REFACTORING.md +31 -1
- package/docs/releases/COMMUNITY_ANNOUNCEMENT_v1.1.2.md +18 -7
- package/docs/releases/RELEASE_NOTES_v1.1.2.md +31 -5
- package/docs/user-guides/DOCKER_SETUP.md +264 -0
- package/docs/user-guides/DTX_SETUP.md +327 -0
- package/docs/user-guides/NPM_SETUP.md +109 -0
- package/docs/user-guides/NPX_SETUP.md +281 -0
- package/docs/wordpress-rest-api-authentication-troubleshooting.md +13 -2
- package/package.json +27 -8
- package/src/cache/CacheInvalidation.ts +140 -132
- package/src/cache/CacheManager.ts +40 -29
- package/src/cache/HttpCacheWrapper.ts +105 -68
- package/src/cache/__tests__/CacheInvalidation.test.ts +123 -118
- package/src/cache/__tests__/CacheManager.test.ts +156 -152
- package/src/cache/__tests__/CachedWordPressClient.test.ts +131 -116
- package/src/cache/__tests__/HttpCacheWrapper.test.ts +118 -115
- package/src/cache/index.ts +13 -13
- package/src/client/CachedWordPressClient.ts +90 -80
- package/src/client/api.ts +205 -205
- package/src/client/auth.ts +80 -80
- package/src/client/managers/AuthenticationManager.ts +61 -61
- package/src/client/managers/BaseManager.ts +11 -11
- package/src/client/managers/RequestManager.ts +79 -47
- package/src/client/managers/index.ts +3 -3
- package/src/config/ConfigurationSchema.ts +44 -44
- package/src/config/ServerConfiguration.ts +39 -39
- package/src/docs/DocumentationGenerator.ts +402 -295
- package/src/docs/MarkdownFormatter.ts +94 -69
- package/src/docs/index.ts +4 -4
- package/src/index.ts +24 -21
- package/src/performance/MetricsCollector.ts +90 -58
- package/src/performance/PerformanceAnalytics.ts +386 -262
- package/src/performance/PerformanceMonitor.ts +152 -118
- package/src/performance/index.ts +9 -9
- package/src/security/InputValidator.ts +148 -91
- package/src/security/SecurityConfig.ts +94 -94
- package/src/server/ConnectionTester.ts +21 -15
- package/src/server/ToolRegistry.ts +64 -51
- package/src/server.ts +2 -2
- package/src/tools/BaseToolManager.ts +6 -6
- package/src/tools/auth.ts +42 -37
- package/src/tools/cache.ts +85 -81
- package/src/tools/comments.ts +93 -91
- package/src/tools/index.ts +10 -10
- package/src/tools/media.ts +89 -89
- package/src/tools/pages.ts +89 -87
- package/src/tools/performance.ts +443 -352
- package/src/tools/posts.ts +109 -107
- package/src/tools/site.ts +86 -77
- package/src/tools/taxonomies.ts +102 -102
- package/src/tools/users.ts +77 -77
- package/src/types/client.ts +157 -60
- package/src/types/index.ts +49 -27
- package/src/types/mcp.ts +15 -15
- package/src/types/wordpress.ts +57 -29
- package/src/utils/debug.ts +37 -37
- package/src/utils/error.ts +47 -25
- package/src/utils/toolWrapper.ts +12 -8
- package/src/utils/validation.ts +116 -65
- package/dist/client/WordPressClient.d.ts +0 -81
- package/dist/client/WordPressClient.d.ts.map +0 -1
- package/dist/client/WordPressClient.js +0 -354
- package/dist/client/WordPressClient.js.map +0 -1
- package/dist/performance/AnomalyDetector.d.ts +0 -63
- package/dist/performance/AnomalyDetector.d.ts.map +0 -1
- package/dist/performance/AnomalyDetector.js +0 -222
- package/dist/performance/AnomalyDetector.js.map +0 -1
- package/dist/performance/BenchmarkAnalyzer.d.ts +0 -67
- package/dist/performance/BenchmarkAnalyzer.d.ts.map +0 -1
- package/dist/performance/BenchmarkAnalyzer.js +0 -301
- package/dist/performance/BenchmarkAnalyzer.js.map +0 -1
- package/dist/performance/TrendAnalyzer.d.ts +0 -69
- package/dist/performance/TrendAnalyzer.d.ts.map +0 -1
- package/dist/performance/TrendAnalyzer.js +0 -203
- package/dist/performance/TrendAnalyzer.js.map +0 -1
- package/dist/tools/BaseToolClass.d.ts +0 -76
- package/dist/tools/BaseToolClass.d.ts.map +0 -1
- package/dist/tools/BaseToolClass.js +0 -104
- package/dist/tools/BaseToolClass.js.map +0 -1
- package/dist/tools/base.d.ts +0 -37
- package/dist/tools/base.d.ts.map +0 -1
- package/dist/tools/base.js +0 -60
- package/dist/tools/base.js.map +0 -1
- package/docs/user-guides/CLAUDE_DESKTOP_SETUP.md +0 -187
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Implements multi-layer caching with TTL, LRU eviction, and site-specific keys
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as crypto from
|
|
6
|
+
import * as crypto from "crypto";
|
|
7
7
|
|
|
8
8
|
export interface CacheEntry<T = any> {
|
|
9
9
|
value: T;
|
|
@@ -37,12 +37,13 @@ export interface CacheConfig {
|
|
|
37
37
|
export class CacheManager {
|
|
38
38
|
private cache: Map<string, CacheEntry> = new Map();
|
|
39
39
|
private accessOrder: string[] = [];
|
|
40
|
+
private cleanupInterval: NodeJS.Timeout | null = null;
|
|
40
41
|
private stats: CacheStats = {
|
|
41
42
|
hits: 0,
|
|
42
43
|
misses: 0,
|
|
43
44
|
evictions: 0,
|
|
44
45
|
totalSize: 0,
|
|
45
|
-
hitRate: 0
|
|
46
|
+
hitRate: 0,
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
constructor(private config: CacheConfig) {
|
|
@@ -55,16 +56,16 @@ export class CacheManager {
|
|
|
55
56
|
*/
|
|
56
57
|
generateKey(siteId: string, endpoint: string, params?: any): string {
|
|
57
58
|
const baseKey = `${siteId}:${endpoint}`;
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
if (!params || Object.keys(params).length === 0) {
|
|
60
61
|
return baseKey;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
// Create deterministic hash of parameters
|
|
64
65
|
const paramHash = crypto
|
|
65
|
-
.createHash(
|
|
66
|
+
.createHash("md5")
|
|
66
67
|
.update(JSON.stringify(this.normalizeParams(params)))
|
|
67
|
-
.digest(
|
|
68
|
+
.digest("hex")
|
|
68
69
|
.substring(0, 8);
|
|
69
70
|
|
|
70
71
|
return `${baseKey}:${paramHash}`;
|
|
@@ -75,7 +76,7 @@ export class CacheManager {
|
|
|
75
76
|
*/
|
|
76
77
|
get<T>(key: string): T | null {
|
|
77
78
|
const entry = this.cache.get(key);
|
|
78
|
-
|
|
79
|
+
|
|
79
80
|
if (!entry) {
|
|
80
81
|
this.stats.misses++;
|
|
81
82
|
this.updateHitRate();
|
|
@@ -95,10 +96,10 @@ export class CacheManager {
|
|
|
95
96
|
entry.accessCount++;
|
|
96
97
|
entry.lastAccessed = Date.now();
|
|
97
98
|
this.updateAccessOrder(key);
|
|
98
|
-
|
|
99
|
+
|
|
99
100
|
this.stats.hits++;
|
|
100
101
|
this.updateHitRate();
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
return entry.value;
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -106,11 +107,11 @@ export class CacheManager {
|
|
|
106
107
|
* Set value in cache with TTL
|
|
107
108
|
*/
|
|
108
109
|
set<T>(
|
|
109
|
-
key: string,
|
|
110
|
-
value: T,
|
|
110
|
+
key: string,
|
|
111
|
+
value: T,
|
|
111
112
|
ttl: number = this.config.defaultTTL,
|
|
112
113
|
etag?: string,
|
|
113
|
-
lastModified?: string
|
|
114
|
+
lastModified?: string,
|
|
114
115
|
): void {
|
|
115
116
|
// Check if we need to evict entries
|
|
116
117
|
if (this.cache.size >= this.config.maxSize && !this.cache.has(key)) {
|
|
@@ -124,7 +125,7 @@ export class CacheManager {
|
|
|
124
125
|
etag,
|
|
125
126
|
lastModified,
|
|
126
127
|
accessCount: 1,
|
|
127
|
-
lastAccessed: Date.now()
|
|
128
|
+
lastAccessed: Date.now(),
|
|
128
129
|
};
|
|
129
130
|
|
|
130
131
|
// If updating existing entry, remove from access order first
|
|
@@ -242,13 +243,13 @@ export class CacheManager {
|
|
|
242
243
|
if (!entry) return {};
|
|
243
244
|
|
|
244
245
|
const headers: Record<string, string> = {};
|
|
245
|
-
|
|
246
|
+
|
|
246
247
|
if (entry.etag) {
|
|
247
|
-
headers[
|
|
248
|
+
headers["If-None-Match"] = entry.etag;
|
|
248
249
|
}
|
|
249
|
-
|
|
250
|
+
|
|
250
251
|
if (entry.lastModified) {
|
|
251
|
-
headers[
|
|
252
|
+
headers["If-Modified-Since"] = entry.lastModified;
|
|
252
253
|
}
|
|
253
254
|
|
|
254
255
|
return headers;
|
|
@@ -301,7 +302,7 @@ export class CacheManager {
|
|
|
301
302
|
* Normalize parameters for consistent hashing
|
|
302
303
|
*/
|
|
303
304
|
private normalizeParams(params: any): any {
|
|
304
|
-
if (typeof params !==
|
|
305
|
+
if (typeof params !== "object" || params === null) {
|
|
305
306
|
return params;
|
|
306
307
|
}
|
|
307
308
|
|
|
@@ -328,11 +329,21 @@ export class CacheManager {
|
|
|
328
329
|
* Start periodic cleanup of expired entries
|
|
329
330
|
*/
|
|
330
331
|
private startCleanupInterval(): void {
|
|
331
|
-
setInterval(() => {
|
|
332
|
+
this.cleanupInterval = setInterval(() => {
|
|
332
333
|
this.cleanupExpired();
|
|
333
334
|
}, 60000); // Cleanup every minute
|
|
334
335
|
}
|
|
335
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Stop the cleanup interval and clean up resources
|
|
339
|
+
*/
|
|
340
|
+
destroy(): void {
|
|
341
|
+
if (this.cleanupInterval) {
|
|
342
|
+
clearInterval(this.cleanupInterval);
|
|
343
|
+
this.cleanupInterval = null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
336
347
|
/**
|
|
337
348
|
* Remove expired entries
|
|
338
349
|
*/
|
|
@@ -362,30 +373,30 @@ export const CachePresets = {
|
|
|
362
373
|
// Static data: site settings, user roles
|
|
363
374
|
STATIC: {
|
|
364
375
|
ttl: 4 * 60 * 60 * 1000, // 4 hours
|
|
365
|
-
cacheControl:
|
|
376
|
+
cacheControl: "public, max-age=14400",
|
|
366
377
|
},
|
|
367
|
-
|
|
368
|
-
// Semi-static: categories, tags, user profiles
|
|
378
|
+
|
|
379
|
+
// Semi-static: categories, tags, user profiles
|
|
369
380
|
SEMI_STATIC: {
|
|
370
381
|
ttl: 2 * 60 * 60 * 1000, // 2 hours
|
|
371
|
-
cacheControl:
|
|
382
|
+
cacheControl: "public, max-age=7200",
|
|
372
383
|
},
|
|
373
|
-
|
|
384
|
+
|
|
374
385
|
// Dynamic: posts, pages, comments
|
|
375
386
|
DYNAMIC: {
|
|
376
387
|
ttl: 15 * 60 * 1000, // 15 minutes
|
|
377
|
-
cacheControl:
|
|
388
|
+
cacheControl: "public, max-age=900",
|
|
378
389
|
},
|
|
379
|
-
|
|
390
|
+
|
|
380
391
|
// Session: authentication, current user
|
|
381
392
|
SESSION: {
|
|
382
393
|
ttl: 30 * 60 * 1000, // 30 minutes
|
|
383
|
-
cacheControl:
|
|
394
|
+
cacheControl: "private, max-age=1800",
|
|
384
395
|
},
|
|
385
|
-
|
|
396
|
+
|
|
386
397
|
// Fast changing: real-time data
|
|
387
398
|
REALTIME: {
|
|
388
399
|
ttl: 60 * 1000, // 1 minute
|
|
389
|
-
cacheControl:
|
|
390
|
-
}
|
|
400
|
+
cacheControl: "public, max-age=60",
|
|
401
|
+
},
|
|
391
402
|
};
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Implements WordPress REST API caching best practices
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { CacheManager, CachePresets } from
|
|
7
|
-
import * as crypto from
|
|
6
|
+
import { CacheManager, CachePresets } from "./CacheManager.js";
|
|
7
|
+
import * as crypto from "crypto";
|
|
8
8
|
|
|
9
9
|
export interface HttpCacheOptions {
|
|
10
10
|
ttl?: number;
|
|
@@ -37,19 +37,28 @@ export interface RequestOptions {
|
|
|
37
37
|
export class HttpCacheWrapper {
|
|
38
38
|
constructor(
|
|
39
39
|
private cacheManager: CacheManager,
|
|
40
|
-
private siteId: string
|
|
40
|
+
private siteId: string,
|
|
41
41
|
) {}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Execute request with intelligent caching
|
|
45
45
|
*/
|
|
46
46
|
async request<T = any>(
|
|
47
|
-
requestFn: () => Promise<{
|
|
47
|
+
requestFn: () => Promise<{
|
|
48
|
+
data: T;
|
|
49
|
+
status: number;
|
|
50
|
+
headers: Record<string, string>;
|
|
51
|
+
}>,
|
|
48
52
|
options: RequestOptions,
|
|
49
|
-
cacheOptions?: HttpCacheOptions
|
|
50
|
-
): Promise<{
|
|
53
|
+
cacheOptions?: HttpCacheOptions,
|
|
54
|
+
): Promise<{
|
|
55
|
+
data: T;
|
|
56
|
+
status: number;
|
|
57
|
+
headers: Record<string, string>;
|
|
58
|
+
cached?: boolean;
|
|
59
|
+
}> {
|
|
51
60
|
// Only cache GET requests
|
|
52
|
-
if (options.method.toUpperCase() !==
|
|
61
|
+
if (options.method.toUpperCase() !== "GET") {
|
|
53
62
|
return await requestFn();
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -58,36 +67,42 @@ export class HttpCacheWrapper {
|
|
|
58
67
|
|
|
59
68
|
// Check for conditional request support
|
|
60
69
|
if (cachedEntry && this.cacheManager.supportsConditionalRequest(cacheKey)) {
|
|
61
|
-
const conditionalHeaders =
|
|
62
|
-
|
|
70
|
+
const conditionalHeaders =
|
|
71
|
+
this.cacheManager.getConditionalHeaders(cacheKey);
|
|
72
|
+
|
|
63
73
|
// Add conditional headers to request
|
|
64
74
|
const requestWithHeaders = {
|
|
65
75
|
...options,
|
|
66
76
|
headers: {
|
|
67
77
|
...options.headers,
|
|
68
|
-
...conditionalHeaders
|
|
69
|
-
}
|
|
78
|
+
...conditionalHeaders,
|
|
79
|
+
},
|
|
70
80
|
};
|
|
71
81
|
|
|
72
82
|
try {
|
|
73
|
-
const response = await this.executeRequestWithHeaders(
|
|
74
|
-
|
|
83
|
+
const response = await this.executeRequestWithHeaders(
|
|
84
|
+
requestFn,
|
|
85
|
+
requestWithHeaders,
|
|
86
|
+
);
|
|
87
|
+
|
|
75
88
|
// 304 Not Modified - return cached data
|
|
76
89
|
if (response.status === 304) {
|
|
77
90
|
return {
|
|
78
91
|
data: cachedEntry.value.data,
|
|
79
92
|
status: 200,
|
|
80
93
|
headers: cachedEntry.value.headers,
|
|
81
|
-
cached: true
|
|
94
|
+
cached: true,
|
|
82
95
|
};
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
// Content changed - update cache
|
|
86
99
|
return await this.cacheAndReturn(response, cacheKey, cacheOptions);
|
|
87
|
-
|
|
88
100
|
} catch (error) {
|
|
89
101
|
// If conditional request fails, try without conditions
|
|
90
|
-
console.warn(
|
|
102
|
+
console.warn(
|
|
103
|
+
"Conditional request failed, falling back to regular request:",
|
|
104
|
+
error,
|
|
105
|
+
);
|
|
91
106
|
}
|
|
92
107
|
}
|
|
93
108
|
|
|
@@ -98,7 +113,7 @@ export class HttpCacheWrapper {
|
|
|
98
113
|
data: cached.data,
|
|
99
114
|
status: cached.status,
|
|
100
115
|
headers: cached.headers,
|
|
101
|
-
cached: true
|
|
116
|
+
cached: true,
|
|
102
117
|
};
|
|
103
118
|
}
|
|
104
119
|
|
|
@@ -111,7 +126,11 @@ export class HttpCacheWrapper {
|
|
|
111
126
|
* Invalidate cache for specific endpoint
|
|
112
127
|
*/
|
|
113
128
|
invalidate(endpoint: string, params?: any): void {
|
|
114
|
-
const cacheKey = this.cacheManager.generateKey(
|
|
129
|
+
const cacheKey = this.cacheManager.generateKey(
|
|
130
|
+
this.siteId,
|
|
131
|
+
endpoint,
|
|
132
|
+
params,
|
|
133
|
+
);
|
|
115
134
|
this.cacheManager.delete(cacheKey);
|
|
116
135
|
}
|
|
117
136
|
|
|
@@ -133,17 +152,27 @@ export class HttpCacheWrapper {
|
|
|
133
152
|
/**
|
|
134
153
|
* Pre-warm cache with data
|
|
135
154
|
*/
|
|
136
|
-
warm<T>(
|
|
137
|
-
|
|
155
|
+
warm<T>(
|
|
156
|
+
endpoint: string,
|
|
157
|
+
data: T,
|
|
158
|
+
params?: any,
|
|
159
|
+
cacheOptions?: HttpCacheOptions,
|
|
160
|
+
): void {
|
|
161
|
+
const cacheKey = this.cacheManager.generateKey(
|
|
162
|
+
this.siteId,
|
|
163
|
+
endpoint,
|
|
164
|
+
params,
|
|
165
|
+
);
|
|
138
166
|
const ttl = cacheOptions?.ttl || this.getDefaultTTL(endpoint);
|
|
139
|
-
|
|
167
|
+
|
|
140
168
|
const cachedResponse: CachedResponse = {
|
|
141
169
|
data,
|
|
142
170
|
status: 200,
|
|
143
171
|
headers: this.generateCacheHeaders(cacheOptions, endpoint),
|
|
144
172
|
etag: this.generateETag(data),
|
|
145
173
|
lastModified: new Date().toUTCString(),
|
|
146
|
-
cacheControl:
|
|
174
|
+
cacheControl:
|
|
175
|
+
cacheOptions?.cacheControl || this.getDefaultCacheControl(endpoint),
|
|
147
176
|
};
|
|
148
177
|
|
|
149
178
|
this.cacheManager.set(
|
|
@@ -151,7 +180,7 @@ export class HttpCacheWrapper {
|
|
|
151
180
|
cachedResponse,
|
|
152
181
|
ttl,
|
|
153
182
|
cachedResponse.etag,
|
|
154
|
-
cachedResponse.lastModified
|
|
183
|
+
cachedResponse.lastModified,
|
|
155
184
|
);
|
|
156
185
|
}
|
|
157
186
|
|
|
@@ -170,7 +199,7 @@ export class HttpCacheWrapper {
|
|
|
170
199
|
return this.cacheManager.generateKey(this.siteId, endpoint, {
|
|
171
200
|
...options.params,
|
|
172
201
|
// Include relevant headers that affect response
|
|
173
|
-
...this.extractCacheableHeaders(options.headers)
|
|
202
|
+
...this.extractCacheableHeaders(options.headers),
|
|
174
203
|
});
|
|
175
204
|
}
|
|
176
205
|
|
|
@@ -186,11 +215,13 @@ export class HttpCacheWrapper {
|
|
|
186
215
|
/**
|
|
187
216
|
* Extract headers that affect caching
|
|
188
217
|
*/
|
|
189
|
-
private extractCacheableHeaders(
|
|
218
|
+
private extractCacheableHeaders(
|
|
219
|
+
headers?: Record<string, string>,
|
|
220
|
+
): Record<string, string> {
|
|
190
221
|
if (!headers) return {};
|
|
191
222
|
|
|
192
223
|
const cacheableHeaders: Record<string, string> = {};
|
|
193
|
-
const relevantHeaders = [
|
|
224
|
+
const relevantHeaders = ["accept", "accept-language", "authorization"];
|
|
194
225
|
|
|
195
226
|
for (const header of relevantHeaders) {
|
|
196
227
|
if (headers[header]) {
|
|
@@ -205,8 +236,12 @@ export class HttpCacheWrapper {
|
|
|
205
236
|
* Execute request with modified headers
|
|
206
237
|
*/
|
|
207
238
|
private async executeRequestWithHeaders(
|
|
208
|
-
requestFn: () => Promise<{
|
|
209
|
-
|
|
239
|
+
requestFn: () => Promise<{
|
|
240
|
+
data: any;
|
|
241
|
+
status: number;
|
|
242
|
+
headers: Record<string, string>;
|
|
243
|
+
}>,
|
|
244
|
+
options: RequestOptions,
|
|
210
245
|
) {
|
|
211
246
|
// This is a simplified approach - in practice, you'd need to modify the actual request
|
|
212
247
|
// The actual implementation would depend on your HTTP client (axios, fetch, etc.)
|
|
@@ -219,9 +254,13 @@ export class HttpCacheWrapper {
|
|
|
219
254
|
private async cacheAndReturn<T>(
|
|
220
255
|
response: { data: T; status: number; headers: Record<string, string> },
|
|
221
256
|
cacheKey: string,
|
|
222
|
-
cacheOptions?: HttpCacheOptions
|
|
223
|
-
): Promise<{
|
|
224
|
-
|
|
257
|
+
cacheOptions?: HttpCacheOptions,
|
|
258
|
+
): Promise<{
|
|
259
|
+
data: T;
|
|
260
|
+
status: number;
|
|
261
|
+
headers: Record<string, string>;
|
|
262
|
+
cached?: boolean;
|
|
263
|
+
}> {
|
|
225
264
|
// Don't cache error responses (unless specifically configured)
|
|
226
265
|
if (response.status >= 400) {
|
|
227
266
|
return response;
|
|
@@ -229,40 +268,35 @@ export class HttpCacheWrapper {
|
|
|
229
268
|
|
|
230
269
|
const endpoint = this.extractEndpointFromKey(cacheKey);
|
|
231
270
|
const ttl = cacheOptions?.ttl || this.getDefaultTTL(endpoint);
|
|
232
|
-
|
|
271
|
+
|
|
233
272
|
// Generate ETags and cache headers
|
|
234
273
|
const etag = this.generateETag(response.data);
|
|
235
274
|
const lastModified = new Date().toUTCString();
|
|
236
|
-
const cacheControl =
|
|
275
|
+
const cacheControl =
|
|
276
|
+
cacheOptions?.cacheControl || this.getDefaultCacheControl(endpoint);
|
|
237
277
|
|
|
238
278
|
const cachedResponse: CachedResponse = {
|
|
239
279
|
data: response.data,
|
|
240
280
|
status: response.status,
|
|
241
281
|
headers: {
|
|
242
282
|
...response.headers,
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
283
|
+
etag: etag,
|
|
284
|
+
"last-modified": lastModified,
|
|
285
|
+
"cache-control": cacheControl,
|
|
246
286
|
},
|
|
247
287
|
etag,
|
|
248
288
|
lastModified,
|
|
249
|
-
cacheControl
|
|
289
|
+
cacheControl,
|
|
250
290
|
};
|
|
251
291
|
|
|
252
292
|
// Store in cache
|
|
253
|
-
this.cacheManager.set(
|
|
254
|
-
cacheKey,
|
|
255
|
-
cachedResponse,
|
|
256
|
-
ttl,
|
|
257
|
-
etag,
|
|
258
|
-
lastModified
|
|
259
|
-
);
|
|
293
|
+
this.cacheManager.set(cacheKey, cachedResponse, ttl, etag, lastModified);
|
|
260
294
|
|
|
261
295
|
return {
|
|
262
296
|
data: response.data,
|
|
263
297
|
status: response.status,
|
|
264
298
|
headers: cachedResponse.headers,
|
|
265
|
-
cached: false
|
|
299
|
+
cached: false,
|
|
266
300
|
};
|
|
267
301
|
}
|
|
268
302
|
|
|
@@ -271,9 +305,9 @@ export class HttpCacheWrapper {
|
|
|
271
305
|
*/
|
|
272
306
|
private generateETag(data: any): string {
|
|
273
307
|
const hash = crypto
|
|
274
|
-
.createHash(
|
|
308
|
+
.createHash("md5")
|
|
275
309
|
.update(JSON.stringify(data))
|
|
276
|
-
.digest(
|
|
310
|
+
.digest("hex");
|
|
277
311
|
return `"${hash}"`;
|
|
278
312
|
}
|
|
279
313
|
|
|
@@ -285,17 +319,17 @@ export class HttpCacheWrapper {
|
|
|
285
319
|
if (this.isStaticEndpoint(endpoint)) {
|
|
286
320
|
return CachePresets.STATIC.ttl;
|
|
287
321
|
}
|
|
288
|
-
|
|
322
|
+
|
|
289
323
|
// Semi-static data endpoints
|
|
290
324
|
if (this.isSemiStaticEndpoint(endpoint)) {
|
|
291
325
|
return CachePresets.SEMI_STATIC.ttl;
|
|
292
326
|
}
|
|
293
|
-
|
|
327
|
+
|
|
294
328
|
// Session/auth endpoints
|
|
295
329
|
if (this.isSessionEndpoint(endpoint)) {
|
|
296
330
|
return CachePresets.SESSION.ttl;
|
|
297
331
|
}
|
|
298
|
-
|
|
332
|
+
|
|
299
333
|
// Default to dynamic for posts, comments, etc.
|
|
300
334
|
return CachePresets.DYNAMIC.ttl;
|
|
301
335
|
}
|
|
@@ -307,32 +341,35 @@ export class HttpCacheWrapper {
|
|
|
307
341
|
if (this.isStaticEndpoint(endpoint)) {
|
|
308
342
|
return CachePresets.STATIC.cacheControl;
|
|
309
343
|
}
|
|
310
|
-
|
|
344
|
+
|
|
311
345
|
if (this.isSemiStaticEndpoint(endpoint)) {
|
|
312
346
|
return CachePresets.SEMI_STATIC.cacheControl;
|
|
313
347
|
}
|
|
314
|
-
|
|
348
|
+
|
|
315
349
|
if (this.isSessionEndpoint(endpoint)) {
|
|
316
350
|
return CachePresets.SESSION.cacheControl;
|
|
317
351
|
}
|
|
318
|
-
|
|
352
|
+
|
|
319
353
|
return CachePresets.DYNAMIC.cacheControl;
|
|
320
354
|
}
|
|
321
355
|
|
|
322
356
|
/**
|
|
323
357
|
* Generate cache headers
|
|
324
358
|
*/
|
|
325
|
-
private generateCacheHeaders(
|
|
359
|
+
private generateCacheHeaders(
|
|
360
|
+
options?: HttpCacheOptions,
|
|
361
|
+
endpoint?: string,
|
|
362
|
+
): Record<string, string> {
|
|
326
363
|
const headers: Record<string, string> = {};
|
|
327
|
-
|
|
364
|
+
|
|
328
365
|
if (options?.cacheControl) {
|
|
329
|
-
headers[
|
|
366
|
+
headers["cache-control"] = options.cacheControl;
|
|
330
367
|
} else if (endpoint) {
|
|
331
|
-
headers[
|
|
368
|
+
headers["cache-control"] = this.getDefaultCacheControl(endpoint);
|
|
332
369
|
}
|
|
333
|
-
|
|
370
|
+
|
|
334
371
|
if (options?.varyHeaders?.length) {
|
|
335
|
-
headers[
|
|
372
|
+
headers["vary"] = options.varyHeaders.join(", ");
|
|
336
373
|
}
|
|
337
374
|
|
|
338
375
|
return headers;
|
|
@@ -342,31 +379,31 @@ export class HttpCacheWrapper {
|
|
|
342
379
|
* Check if endpoint contains static data
|
|
343
380
|
*/
|
|
344
381
|
private isStaticEndpoint(endpoint: string): boolean {
|
|
345
|
-
const staticEndpoints = [
|
|
346
|
-
return staticEndpoints.some(pattern => endpoint.includes(pattern));
|
|
382
|
+
const staticEndpoints = ["settings", "types", "statuses"];
|
|
383
|
+
return staticEndpoints.some((pattern) => endpoint.includes(pattern));
|
|
347
384
|
}
|
|
348
385
|
|
|
349
386
|
/**
|
|
350
|
-
* Check if endpoint contains semi-static data
|
|
387
|
+
* Check if endpoint contains semi-static data
|
|
351
388
|
*/
|
|
352
389
|
private isSemiStaticEndpoint(endpoint: string): boolean {
|
|
353
|
-
const semiStaticEndpoints = [
|
|
354
|
-
return semiStaticEndpoints.some(pattern => endpoint.includes(pattern));
|
|
390
|
+
const semiStaticEndpoints = ["categories", "tags", "users", "taxonomies"];
|
|
391
|
+
return semiStaticEndpoints.some((pattern) => endpoint.includes(pattern));
|
|
355
392
|
}
|
|
356
393
|
|
|
357
394
|
/**
|
|
358
395
|
* Check if endpoint is session-related
|
|
359
396
|
*/
|
|
360
397
|
private isSessionEndpoint(endpoint: string): boolean {
|
|
361
|
-
const sessionEndpoints = [
|
|
362
|
-
return sessionEndpoints.some(pattern => endpoint.includes(pattern));
|
|
398
|
+
const sessionEndpoints = ["users/me", "application-passwords"];
|
|
399
|
+
return sessionEndpoints.some((pattern) => endpoint.includes(pattern));
|
|
363
400
|
}
|
|
364
401
|
|
|
365
402
|
/**
|
|
366
403
|
* Extract endpoint from cache key
|
|
367
404
|
*/
|
|
368
405
|
private extractEndpointFromKey(cacheKey: string): string {
|
|
369
|
-
const parts = cacheKey.split(
|
|
370
|
-
return parts[1] ||
|
|
406
|
+
const parts = cacheKey.split(":");
|
|
407
|
+
return parts[1] || "";
|
|
371
408
|
}
|
|
372
409
|
}
|