xypriss 1.2.3 → 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.
Files changed (103) hide show
  1. package/README.md +138 -3
  2. package/dist/cjs/mods/security/src/components/cache/index.js +1 -1
  3. package/dist/cjs/shared/logger/Logger.js +1 -0
  4. package/dist/cjs/shared/logger/Logger.js.map +1 -1
  5. package/dist/cjs/src/cluster/bun-cluster-manager.js +1567 -0
  6. package/dist/cjs/src/cluster/bun-cluster-manager.js.map +1 -0
  7. package/dist/cjs/src/cluster/cluster-manager.js +1 -1
  8. package/dist/cjs/src/cluster/cluster-manager.js.map +1 -1
  9. package/dist/cjs/src/cluster/index.js +25 -6
  10. package/dist/cjs/src/cluster/index.js.map +1 -1
  11. package/dist/cjs/src/cluster/memory-manager.js +463 -0
  12. package/dist/cjs/src/cluster/memory-manager.js.map +1 -0
  13. package/dist/cjs/src/cluster/modules/BunIPCManager.js +603 -0
  14. package/dist/cjs/src/cluster/modules/BunIPCManager.js.map +1 -0
  15. package/dist/cjs/src/cluster/modules/ClusterFactory.js +22 -1
  16. package/dist/cjs/src/cluster/modules/ClusterFactory.js.map +1 -1
  17. package/dist/cjs/src/cluster/modules/CpuMonitor.js +658 -0
  18. package/dist/cjs/src/cluster/modules/CpuMonitor.js.map +1 -0
  19. package/dist/cjs/src/cluster/modules/ProcessMonitor.js +513 -0
  20. package/dist/cjs/src/cluster/modules/ProcessMonitor.js.map +1 -0
  21. package/dist/cjs/src/plugins/server-maintenance-plugin.js +1 -1
  22. package/dist/cjs/src/server/FastServer.js +64 -43
  23. package/dist/cjs/src/server/FastServer.js.map +1 -1
  24. package/dist/cjs/src/server/components/fastapi/ClusterManagerComponent.js +226 -10
  25. package/dist/cjs/src/server/components/fastapi/ClusterManagerComponent.js.map +1 -1
  26. package/dist/cjs/src/server/const/Cluster.config.js +174 -31
  27. package/dist/cjs/src/server/const/Cluster.config.js.map +1 -1
  28. package/dist/cjs/src/server/const/default.js +11 -2
  29. package/dist/cjs/src/server/const/default.js.map +1 -1
  30. package/dist/cjs/src/server/utils/PortManager.js +26 -15
  31. package/dist/cjs/src/server/utils/PortManager.js.map +1 -1
  32. package/dist/esm/mods/security/src/components/cache/index.js +1 -1
  33. package/dist/esm/shared/logger/Logger.js +1 -0
  34. package/dist/esm/shared/logger/Logger.js.map +1 -1
  35. package/dist/esm/src/cluster/bun-cluster-manager.js +1565 -0
  36. package/dist/esm/src/cluster/bun-cluster-manager.js.map +1 -0
  37. package/dist/esm/src/cluster/cluster-manager.js +1 -1
  38. package/dist/esm/src/cluster/cluster-manager.js.map +1 -1
  39. package/dist/esm/src/cluster/index.js +25 -6
  40. package/dist/esm/src/cluster/index.js.map +1 -1
  41. package/dist/esm/src/cluster/memory-manager.js +461 -0
  42. package/dist/esm/src/cluster/memory-manager.js.map +1 -0
  43. package/dist/esm/src/cluster/modules/BunIPCManager.js +601 -0
  44. package/dist/esm/src/cluster/modules/BunIPCManager.js.map +1 -0
  45. package/dist/esm/src/cluster/modules/ClusterFactory.js +22 -1
  46. package/dist/esm/src/cluster/modules/ClusterFactory.js.map +1 -1
  47. package/dist/esm/src/cluster/modules/CpuMonitor.js +656 -0
  48. package/dist/esm/src/cluster/modules/CpuMonitor.js.map +1 -0
  49. package/dist/esm/src/cluster/modules/ProcessMonitor.js +511 -0
  50. package/dist/esm/src/cluster/modules/ProcessMonitor.js.map +1 -0
  51. package/dist/esm/src/plugins/server-maintenance-plugin.js +1 -1
  52. package/dist/esm/src/server/FastServer.js +64 -43
  53. package/dist/esm/src/server/FastServer.js.map +1 -1
  54. package/dist/esm/src/server/components/fastapi/ClusterManagerComponent.js +226 -10
  55. package/dist/esm/src/server/components/fastapi/ClusterManagerComponent.js.map +1 -1
  56. package/dist/esm/src/server/const/Cluster.config.js +174 -31
  57. package/dist/esm/src/server/const/Cluster.config.js.map +1 -1
  58. package/dist/esm/src/server/const/default.js +11 -2
  59. package/dist/esm/src/server/const/default.js.map +1 -1
  60. package/dist/esm/src/server/utils/PortManager.js +26 -15
  61. package/dist/esm/src/server/utils/PortManager.js.map +1 -1
  62. package/dist/index.d.ts +78 -1
  63. package/package.json +3 -1
  64. package/dist/cjs/src/plugins/modules/network/index.js +0 -120
  65. package/dist/cjs/src/plugins/modules/network/index.js.map +0 -1
  66. package/dist/cjs/src/server/plugins/PluginEngine.js +0 -378
  67. package/dist/cjs/src/server/plugins/PluginEngine.js.map +0 -1
  68. package/dist/cjs/src/server/plugins/PluginRegistry.js +0 -339
  69. package/dist/cjs/src/server/plugins/PluginRegistry.js.map +0 -1
  70. package/dist/cjs/src/server/plugins/builtin/JWTAuthPlugin.js +0 -591
  71. package/dist/cjs/src/server/plugins/builtin/JWTAuthPlugin.js.map +0 -1
  72. package/dist/cjs/src/server/plugins/builtin/ResponseTimePlugin.js +0 -413
  73. package/dist/cjs/src/server/plugins/builtin/ResponseTimePlugin.js.map +0 -1
  74. package/dist/cjs/src/server/plugins/builtin/SmartCachePlugin.js +0 -843
  75. package/dist/cjs/src/server/plugins/builtin/SmartCachePlugin.js.map +0 -1
  76. package/dist/cjs/src/server/plugins/core/CachePlugin.js +0 -1975
  77. package/dist/cjs/src/server/plugins/core/CachePlugin.js.map +0 -1
  78. package/dist/cjs/src/server/plugins/core/PerformancePlugin.js +0 -894
  79. package/dist/cjs/src/server/plugins/core/PerformancePlugin.js.map +0 -1
  80. package/dist/cjs/src/server/plugins/core/SecurityPlugin.js +0 -799
  81. package/dist/cjs/src/server/plugins/core/SecurityPlugin.js.map +0 -1
  82. package/dist/cjs/src/server/plugins/types/PluginTypes.js +0 -47
  83. package/dist/cjs/src/server/plugins/types/PluginTypes.js.map +0 -1
  84. package/dist/esm/src/plugins/modules/network/index.js +0 -109
  85. package/dist/esm/src/plugins/modules/network/index.js.map +0 -1
  86. package/dist/esm/src/server/plugins/PluginEngine.js +0 -376
  87. package/dist/esm/src/server/plugins/PluginEngine.js.map +0 -1
  88. package/dist/esm/src/server/plugins/PluginRegistry.js +0 -337
  89. package/dist/esm/src/server/plugins/PluginRegistry.js.map +0 -1
  90. package/dist/esm/src/server/plugins/builtin/JWTAuthPlugin.js +0 -589
  91. package/dist/esm/src/server/plugins/builtin/JWTAuthPlugin.js.map +0 -1
  92. package/dist/esm/src/server/plugins/builtin/ResponseTimePlugin.js +0 -411
  93. package/dist/esm/src/server/plugins/builtin/ResponseTimePlugin.js.map +0 -1
  94. package/dist/esm/src/server/plugins/builtin/SmartCachePlugin.js +0 -841
  95. package/dist/esm/src/server/plugins/builtin/SmartCachePlugin.js.map +0 -1
  96. package/dist/esm/src/server/plugins/core/CachePlugin.js +0 -1973
  97. package/dist/esm/src/server/plugins/core/CachePlugin.js.map +0 -1
  98. package/dist/esm/src/server/plugins/core/PerformancePlugin.js +0 -872
  99. package/dist/esm/src/server/plugins/core/PerformancePlugin.js.map +0 -1
  100. package/dist/esm/src/server/plugins/core/SecurityPlugin.js +0 -797
  101. package/dist/esm/src/server/plugins/core/SecurityPlugin.js.map +0 -1
  102. package/dist/esm/src/server/plugins/types/PluginTypes.js +0 -47
  103. package/dist/esm/src/server/plugins/types/PluginTypes.js.map +0 -1
@@ -1,843 +0,0 @@
1
- 'use strict';
2
-
3
- var CachePlugin = require('../core/CachePlugin.js');
4
- var PluginTypes = require('../types/PluginTypes.js');
5
-
6
- /**
7
- * Smart Cache Plugin
8
- *
9
- * Intelligent caching plugin with <0.5ms execution overhead
10
- * leveraging XyPrissJS cache systems for optimal performance.
11
- */
12
- /**
13
- * Smart Cache Plugin for intelligent request caching
14
- */
15
- class SmartCachePlugin extends CachePlugin.CachePlugin {
16
- constructor() {
17
- super(...arguments);
18
- this.id = "nehonix.ftfy.cache";
19
- this.name = "Smart Cache Plugin";
20
- this.version = "1.0.0";
21
- this.priority = PluginTypes.PluginPriority.HIGH;
22
- // Cache configuration
23
- this.cacheStrategy = "hybrid";
24
- this.compressionEnabled = true;
25
- this.encryptionEnabled = true; // Keep encryption for security
26
- // Smart caching rules
27
- this.cachingRules = new Map();
28
- // Cache analytics
29
- this.cacheAnalytics = {
30
- totalRequests: 0,
31
- cacheableRequests: 0,
32
- cacheHits: 0,
33
- cacheMisses: 0,
34
- cacheSkips: 0,
35
- averageHitTime: 0,
36
- averageMissTime: 0,
37
- compressionSavings: 0,
38
- };
39
- // Dynamic TTL adjustment based on request patterns
40
- this.requestPatterns = new Map();
41
- // Background prefetching queue and worker
42
- this.prefetchQueue = [];
43
- this.prefetchWorkerActive = false;
44
- this.prefetchStats = {
45
- totalPrefetched: 0,
46
- successfulPrefetches: 0,
47
- failedPrefetches: 0,
48
- averagePrefetchTime: 0,
49
- };
50
- }
51
- /**
52
- * Initialize smart cache plugin
53
- */
54
- async initializeCachePlugin(context) {
55
- // Setup default caching rules
56
- this.setupDefaultCachingRules();
57
- // Configure custom rules from settings
58
- if (context.config.customSettings.cachingRules) {
59
- this.configureCachingRules(context.config.customSettings.cachingRules);
60
- }
61
- // Setup cache analytics cleanup
62
- this.setupAnalyticsCleanup();
63
- // Setup dynamic TTL adjustment
64
- this.setupDynamicTTLAdjustment();
65
- context.logger.info("Smart Cache Plugin initialized with intelligent caching rules");
66
- }
67
- /**
68
- * Check if request should be cached (plugin-specific logic)
69
- */
70
- shouldCacheRequest(context) {
71
- const { req } = context;
72
- // Apply smart caching rules
73
- for (const [ruleName, rule] of this.cachingRules.entries()) {
74
- if (rule.enabled && rule.pattern.test(req.path)) {
75
- // Check additional conditions
76
- if (this.shouldApplyRule(context, rule)) {
77
- return true;
78
- }
79
- }
80
- }
81
- // Fallback to intelligent heuristics
82
- return this.applyIntelligentCaching(context);
83
- }
84
- /**
85
- * Get custom cache key components
86
- */
87
- getCustomKeyComponents(context) {
88
- const { req } = context;
89
- const components = [];
90
- // Add user-specific components for personalized content
91
- if (context.security.isAuthenticated) {
92
- components.push(`user:${context.security.userId}`);
93
- // Add role-based caching
94
- if (context.security.roles.length > 0) {
95
- components.push(`roles:${context.security.roles.sort().join(",")}`);
96
- }
97
- }
98
- // Add device type for responsive caching
99
- const userAgent = req.headers["user-agent"];
100
- if (userAgent) {
101
- const deviceType = this.detectDeviceType(userAgent);
102
- components.push(`device:${deviceType}`);
103
- }
104
- // Add language for i18n caching
105
- const acceptLanguage = req.headers["accept-language"];
106
- if (acceptLanguage) {
107
- const primaryLanguage = acceptLanguage.split(",")[0].split("-")[0];
108
- components.push(`lang:${primaryLanguage}`);
109
- }
110
- // Add API version for versioned APIs
111
- const apiVersion = req.headers["api-version"] || req.query.version;
112
- if (apiVersion) {
113
- components.push(`version:${apiVersion}`);
114
- }
115
- return components;
116
- }
117
- /**
118
- * Get custom TTL for request
119
- */
120
- getCustomTTL(context) {
121
- const { req } = context;
122
- const route = this.normalizeRoute(req.path);
123
- // Check if we have pattern data for dynamic TTL
124
- const pattern = this.requestPatterns.get(route);
125
- if (pattern) {
126
- return this.calculateDynamicTTL(pattern);
127
- }
128
- // Apply rule-based TTL
129
- for (const [ruleName, rule] of this.cachingRules.entries()) {
130
- if (rule.enabled && rule.pattern.test(req.path)) {
131
- return rule.ttl;
132
- }
133
- }
134
- // Default TTL based on content type
135
- return this.getDefaultTTLByContentType(req.path);
136
- }
137
- /**
138
- * Handle custom cache operations
139
- */
140
- async handleCustomCacheOperation(context, operation) {
141
- switch (operation) {
142
- case "analyze":
143
- return await this.analyzeCachePerformance(context);
144
- case "optimize":
145
- return await this.optimizeCacheStrategy(context);
146
- case "prefetch":
147
- return await this.prefetchRelatedContent(context);
148
- default:
149
- return { operation, supported: false };
150
- }
151
- }
152
- /**
153
- * Precompile cache operations
154
- */
155
- async precompileCacheOperations() {
156
- // Pre-warm route normalization
157
- this.normalizeRoute("/api/users/123");
158
- // Pre-warm device detection
159
- this.detectDeviceType("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
160
- // Pre-warm TTL calculation
161
- this.calculateDynamicTTL({
162
- frequency: 10,
163
- lastAccess: Date.now(),
164
- averageResponseTime: 100,
165
- volatility: 0.1,
166
- });
167
- }
168
- // ===== SMART CACHING LOGIC =====
169
- /**
170
- * Setup default caching rules
171
- */
172
- setupDefaultCachingRules() {
173
- // Static assets - long TTL
174
- this.cachingRules.set("static", {
175
- pattern: /\.(css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$/,
176
- ttl: 86400000, // 24 hours
177
- enabled: true,
178
- compression: true,
179
- tags: ["static"],
180
- });
181
- // API responses - medium TTL
182
- this.cachingRules.set("api", {
183
- pattern: /^\/api\/(?!auth|admin)/,
184
- ttl: 300000, // 5 minutes
185
- enabled: true,
186
- compression: true,
187
- tags: ["api"],
188
- });
189
- // Public pages - short TTL
190
- this.cachingRules.set("public", {
191
- pattern: /^\/(?!admin|dashboard|profile)/,
192
- ttl: 60000, // 1 minute
193
- enabled: true,
194
- compression: true,
195
- tags: ["public"],
196
- });
197
- // User-specific content - very short TTL
198
- this.cachingRules.set("user", {
199
- pattern: /^\/(profile|dashboard|settings)/,
200
- ttl: 30000, // 30 seconds
201
- enabled: true,
202
- compression: false,
203
- tags: ["user"],
204
- });
205
- }
206
- /**
207
- * Configure custom caching rules
208
- */
209
- configureCachingRules(rules) {
210
- for (const rule of rules) {
211
- this.cachingRules.set(rule.name, {
212
- pattern: new RegExp(rule.pattern),
213
- ttl: rule.ttl || 300000,
214
- enabled: rule.enabled !== false,
215
- compression: rule.compression !== false,
216
- tags: rule.tags || [],
217
- });
218
- }
219
- }
220
- /**
221
- * Check if caching rule should be applied
222
- */
223
- shouldApplyRule(context, rule) {
224
- const { req } = context;
225
- // Don't cache authenticated requests for public rules
226
- if (rule.tags.includes("public") && context.security.isAuthenticated) {
227
- return false;
228
- }
229
- // Don't cache if request has cache-control: no-cache
230
- const cacheControl = req.headers["cache-control"];
231
- if (cacheControl && cacheControl.includes("no-cache")) {
232
- return false;
233
- }
234
- // Don't cache if request has pragma: no-cache
235
- const pragma = req.headers.pragma;
236
- if (pragma && pragma.includes("no-cache")) {
237
- return false;
238
- }
239
- return true;
240
- }
241
- /**
242
- * Apply intelligent caching heuristics
243
- */
244
- applyIntelligentCaching(context) {
245
- const { req } = context;
246
- // Analyze request characteristics
247
- const hasQueryParams = Object.keys(req.query).length > 0;
248
- const hasBody = req.body && Object.keys(req.body).length > 0;
249
- const isIdempotent = ["GET", "HEAD", "OPTIONS"].includes(req.method);
250
- // Don't cache non-idempotent requests
251
- if (!isIdempotent) {
252
- return false;
253
- }
254
- // Don't cache requests with complex query parameters
255
- if (hasQueryParams && this.hasComplexQueryParams(req.query)) {
256
- return false;
257
- }
258
- // Cache simple GET requests
259
- if (req.method === "GET" && !hasBody) {
260
- return true;
261
- }
262
- return false;
263
- }
264
- /**
265
- * Calculate dynamic TTL based on request patterns
266
- */
267
- calculateDynamicTTL(pattern) {
268
- const baseTime = 300000; // 5 minutes base
269
- // Adjust based on frequency (more frequent = longer cache)
270
- const frequencyMultiplier = Math.min(pattern.frequency / 10, 2);
271
- // Adjust based on volatility (more volatile = shorter cache)
272
- const volatilityMultiplier = Math.max(1 - pattern.volatility, 0.1);
273
- // Adjust based on response time (slower = longer cache)
274
- const responseTimeMultiplier = Math.min(pattern.averageResponseTime / 100, 3);
275
- return Math.round(baseTime *
276
- frequencyMultiplier *
277
- volatilityMultiplier *
278
- responseTimeMultiplier);
279
- }
280
- /**
281
- * Get default TTL by content type
282
- */
283
- getDefaultTTLByContentType(path) {
284
- if (path.match(/\.(css|js)$/)) {
285
- return 3600000; // 1 hour for CSS/JS
286
- }
287
- if (path.match(/\.(png|jpg|jpeg|gif|svg|ico)$/)) {
288
- return 86400000; // 24 hours for images
289
- }
290
- if (path.startsWith("/api/")) {
291
- return 300000; // 5 minutes for API
292
- }
293
- return 60000; // 1 minute default
294
- }
295
- // ===== ANALYTICS AND OPTIMIZATION =====
296
- /**
297
- * Analyze cache performance
298
- */
299
- async analyzeCachePerformance(context) {
300
- const hitRate = this.cacheAnalytics.totalRequests > 0
301
- ? (this.cacheAnalytics.cacheHits /
302
- this.cacheAnalytics.totalRequests) *
303
- 100
304
- : 0;
305
- const cacheableRate = this.cacheAnalytics.totalRequests > 0
306
- ? (this.cacheAnalytics.cacheableRequests /
307
- this.cacheAnalytics.totalRequests) *
308
- 100
309
- : 0;
310
- return {
311
- hitRate: Math.round(hitRate * 100) / 100,
312
- cacheableRate: Math.round(cacheableRate * 100) / 100,
313
- totalRequests: this.cacheAnalytics.totalRequests,
314
- cacheHits: this.cacheAnalytics.cacheHits,
315
- cacheMisses: this.cacheAnalytics.cacheMisses,
316
- averageHitTime: this.cacheAnalytics.averageHitTime,
317
- averageMissTime: this.cacheAnalytics.averageMissTime,
318
- compressionSavings: this.cacheAnalytics.compressionSavings,
319
- topPatterns: this.getTopRequestPatterns(),
320
- };
321
- }
322
- /**
323
- * Optimize cache strategy
324
- */
325
- async optimizeCacheStrategy(context) {
326
- const optimizations = [];
327
- // Analyze hit rates by rule
328
- for (const [ruleName, rule] of this.cachingRules.entries()) {
329
- // Suggest optimizations based on performance
330
- if (rule.enabled) {
331
- optimizations.push(`Rule '${ruleName}' is active`);
332
- }
333
- }
334
- // Suggest TTL adjustments
335
- const ttlSuggestions = this.suggestTTLOptimizations();
336
- optimizations.push(...ttlSuggestions);
337
- return {
338
- optimizations,
339
- suggestions: this.generateSmartOptimizationSuggestions(),
340
- };
341
- }
342
- /**
343
- * Prefetch related content
344
- */
345
- async prefetchRelatedContent(context) {
346
- const { req } = context;
347
- const relatedUrls = this.identifyRelatedContent(req.path);
348
- // Queue related URLs for background prefetching
349
- const queuedUrls = [];
350
- for (const url of relatedUrls) {
351
- const priority = this.calculatePrefetchPriority(url, context);
352
- // Only queue high-priority URLs to avoid overwhelming the system
353
- if (priority > 0.5) {
354
- this.queueForPrefetch(url, priority, context);
355
- queuedUrls.push(url);
356
- }
357
- }
358
- // Start background prefetch worker if not already active
359
- if (!this.prefetchWorkerActive && this.prefetchQueue.length > 0) {
360
- this.startPrefetchWorker();
361
- }
362
- return {
363
- prefetched: queuedUrls.length,
364
- urls: queuedUrls,
365
- queueSize: this.prefetchQueue.length,
366
- };
367
- }
368
- /**
369
- * Queue a URL for background prefetching
370
- */
371
- queueForPrefetch(url, priority, context) {
372
- // Avoid duplicate entries
373
- const existingIndex = this.prefetchQueue.findIndex((item) => item.url === url);
374
- if (existingIndex >= 0) {
375
- // Update priority if higher
376
- if (this.prefetchQueue[existingIndex].priority < priority) {
377
- this.prefetchQueue[existingIndex].priority = priority;
378
- this.prefetchQueue[existingIndex].timestamp = Date.now();
379
- }
380
- return;
381
- }
382
- // Add to queue
383
- this.prefetchQueue.push({
384
- url,
385
- priority,
386
- timestamp: Date.now(),
387
- context: {
388
- method: context.req.method,
389
- headers: context.req.headers,
390
- baseUrl: `${context.req.protocol}://${context.req.get("host")}`,
391
- },
392
- });
393
- // Sort by priority (highest first)
394
- this.prefetchQueue.sort((a, b) => b.priority - a.priority);
395
- // Limit queue size to prevent memory issues
396
- if (this.prefetchQueue.length > 100) {
397
- this.prefetchQueue = this.prefetchQueue.slice(0, 100);
398
- }
399
- }
400
- /**
401
- * Calculate prefetch priority for a URL
402
- */
403
- calculatePrefetchPriority(url, context) {
404
- let priority = 0.3; // Base priority
405
- // Check request patterns
406
- const pattern = this.requestPatterns.get(url);
407
- if (pattern) {
408
- // Higher frequency = higher priority
409
- priority += Math.min(pattern.frequency / 100, 0.4);
410
- // Recent access = higher priority
411
- const timeSinceAccess = Date.now() - pattern.lastAccess;
412
- if (timeSinceAccess < 300000) {
413
- // 5 minutes
414
- priority += 0.2;
415
- }
416
- // Lower volatility = higher priority (more stable content)
417
- priority += (1 - pattern.volatility) * 0.1;
418
- }
419
- // Check if URL matches high-priority patterns
420
- if (url.includes("/api/") || url.includes("/static/")) {
421
- priority += 0.2;
422
- }
423
- // Check if it's a common resource type
424
- if (url.match(/\.(css|js|png|jpg|jpeg|gif|svg|woff|woff2)$/)) {
425
- priority += 0.3;
426
- }
427
- return Math.min(priority, 1.0);
428
- }
429
- /**
430
- * Start the background prefetch worker
431
- */
432
- async startPrefetchWorker() {
433
- if (this.prefetchWorkerActive)
434
- return;
435
- this.prefetchWorkerActive = true;
436
- try {
437
- while (this.prefetchQueue.length > 0) {
438
- const item = this.prefetchQueue.shift();
439
- if (!item)
440
- break;
441
- // Skip items that are too old (older than 5 minutes)
442
- if (Date.now() - item.timestamp > 300000) {
443
- continue;
444
- }
445
- await this.performPrefetch(item);
446
- // Small delay to prevent overwhelming the server
447
- await new Promise((resolve) => setTimeout(resolve, 10));
448
- }
449
- }
450
- catch (error) {
451
- console.error("Prefetch worker error:", error);
452
- }
453
- finally {
454
- this.prefetchWorkerActive = false;
455
- }
456
- }
457
- /**
458
- * Perform actual prefetch operation
459
- */
460
- async performPrefetch(item) {
461
- const startTime = Date.now();
462
- try {
463
- // Perform actual HTTP request for prefetching to warm up the cache
464
- const response = await this.performActualPrefetchRequest(item.url, item.context);
465
- if (response.success) {
466
- this.prefetchStats.successfulPrefetches++;
467
- // Update request patterns
468
- this.updateRequestPattern(item.url, Date.now() - startTime);
469
- }
470
- else {
471
- this.prefetchStats.failedPrefetches++;
472
- }
473
- this.prefetchStats.totalPrefetched++;
474
- // Update average prefetch time
475
- const prefetchTime = Date.now() - startTime;
476
- this.prefetchStats.averagePrefetchTime =
477
- (this.prefetchStats.averagePrefetchTime *
478
- (this.prefetchStats.totalPrefetched - 1) +
479
- prefetchTime) /
480
- this.prefetchStats.totalPrefetched;
481
- }
482
- catch (error) {
483
- this.prefetchStats.failedPrefetches++;
484
- this.prefetchStats.totalPrefetched++;
485
- console.error(`Prefetch failed for ${item.url}:`, error);
486
- }
487
- }
488
- /**
489
- * Perform actual prefetch request with real HTTP call
490
- */
491
- async performActualPrefetchRequest(url, context) {
492
- try {
493
- // Construct full URL if relative
494
- let fullUrl = url;
495
- if (context?.baseUrl && !url.startsWith("http")) {
496
- fullUrl = `${context.baseUrl}${url.startsWith("/") ? url : "/" + url}`;
497
- }
498
- // Prepare request options
499
- const requestOptions = {
500
- method: context?.method || "GET",
501
- headers: {
502
- "User-Agent": "XyPrissJS-SmartCache/1.0",
503
- Accept: "*/*",
504
- "Cache-Control": "no-cache", // Force fresh fetch for prefetching
505
- ...this.getFilteredHeaders(context?.headers),
506
- },
507
- timeout: 5000, // 5 second timeout for prefetch requests
508
- redirect: "follow",
509
- maxRedirects: 3,
510
- };
511
- // Use Node.js built-in fetch if available (Node 18+), otherwise use a fallback
512
- let response;
513
- let responseData;
514
- if (typeof fetch !== "undefined") {
515
- // Use native fetch
516
- response = await fetch(fullUrl, requestOptions);
517
- if (response.ok) {
518
- // Try to get response data based on content type
519
- const contentType = response.headers.get("content-type") || "";
520
- if (contentType.includes("application/json")) {
521
- responseData = await response.json();
522
- }
523
- else if (contentType.includes("text/")) {
524
- responseData = await response.text();
525
- }
526
- else {
527
- // For binary data, just get the size
528
- const buffer = await response.arrayBuffer();
529
- responseData = {
530
- size: buffer.byteLength,
531
- type: "binary",
532
- };
533
- }
534
- }
535
- else {
536
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
537
- }
538
- }
539
- else {
540
- // Fallback using Node.js http/https modules
541
- response = await this.makeHttpRequest(fullUrl, requestOptions);
542
- responseData = response.data;
543
- }
544
- // Cache the prefetched content
545
- await this.cachePrefetchedContent(url, responseData, response);
546
- return {
547
- success: true,
548
- data: responseData,
549
- statusCode: response.status || response.statusCode,
550
- headers: response.headers,
551
- };
552
- }
553
- catch (error) {
554
- console.warn(`Prefetch failed for ${url}:`, error);
555
- return {
556
- success: false,
557
- error: error instanceof Error ? error.message : "Unknown error",
558
- };
559
- }
560
- }
561
- /**
562
- * Filter headers to only include safe ones for prefetching
563
- */
564
- getFilteredHeaders(headers) {
565
- if (!headers)
566
- return {};
567
- const safeHeaders = {};
568
- const allowedHeaders = [
569
- "accept",
570
- "accept-language",
571
- "accept-encoding",
572
- "user-agent",
573
- ];
574
- for (const [key, value] of Object.entries(headers)) {
575
- if (allowedHeaders.includes(key.toLowerCase()) &&
576
- typeof value === "string") {
577
- safeHeaders[key] = value;
578
- }
579
- }
580
- return safeHeaders;
581
- }
582
- /**
583
- * Make HTTP request using Node.js built-in modules
584
- */
585
- async makeHttpRequest(url, options) {
586
- return new Promise((resolve, reject) => {
587
- const urlObj = new URL(url);
588
- const isHttps = urlObj.protocol === "https:";
589
- // Dynamically import http/https modules
590
- const httpModule = isHttps ? require("https") : require("http");
591
- const requestOptions = {
592
- hostname: urlObj.hostname,
593
- port: urlObj.port || (isHttps ? 443 : 80),
594
- path: urlObj.pathname + urlObj.search,
595
- method: options.method || "GET",
596
- headers: options.headers || {},
597
- timeout: options.timeout || 5000,
598
- };
599
- const req = httpModule.request(requestOptions, (res) => {
600
- let data = "";
601
- res.on("data", (chunk) => {
602
- data += chunk;
603
- });
604
- res.on("end", () => {
605
- resolve({
606
- statusCode: res.statusCode,
607
- headers: res.headers,
608
- data: data,
609
- });
610
- });
611
- });
612
- req.on("error", (error) => {
613
- reject(error);
614
- });
615
- req.on("timeout", () => {
616
- req.destroy();
617
- reject(new Error("Request timeout"));
618
- });
619
- req.end();
620
- });
621
- }
622
- /**
623
- * Cache the prefetched content
624
- */
625
- async cachePrefetchedContent(url, data, response) {
626
- try {
627
- // Generate cache key for the prefetched content
628
- const cacheKey = this.generatePrefetchCacheKey(url);
629
- // Determine TTL based on response headers
630
- const ttl = this.calculatePrefetchTTL(response);
631
- // Store in cache with appropriate metadata
632
- const cacheEntry = {
633
- url,
634
- data,
635
- timestamp: Date.now(),
636
- statusCode: response.status || response.statusCode,
637
- headers: response.headers,
638
- prefetched: true,
639
- };
640
- // Use the plugin's cache if available
641
- if (this.cache) {
642
- await this.cache.set(cacheKey, cacheEntry, { ttl });
643
- }
644
- }
645
- catch (error) {
646
- console.warn(`Failed to cache prefetched content for ${url}:`, error);
647
- }
648
- }
649
- /**
650
- * Generate cache key for prefetched content
651
- */
652
- generatePrefetchCacheKey(url) {
653
- return `prefetch:${url}`;
654
- }
655
- /**
656
- * Calculate TTL for prefetched content based on response headers
657
- */
658
- calculatePrefetchTTL(response) {
659
- const headers = response.headers || {};
660
- // Check Cache-Control header
661
- const cacheControl = headers["cache-control"] || headers.get?.("cache-control");
662
- if (cacheControl) {
663
- const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
664
- if (maxAgeMatch) {
665
- return parseInt(maxAgeMatch[1]) * 1000; // Convert to milliseconds
666
- }
667
- }
668
- // Check Expires header
669
- const expires = headers["expires"] || headers.get?.("expires");
670
- if (expires) {
671
- const expiresDate = new Date(expires);
672
- const now = new Date();
673
- if (expiresDate > now) {
674
- return expiresDate.getTime() - now.getTime();
675
- }
676
- }
677
- // Default TTL for prefetched content (5 minutes)
678
- return 300000;
679
- }
680
- /**
681
- * Update request pattern data
682
- */
683
- updateRequestPattern(url, responseTime) {
684
- const pattern = this.requestPatterns.get(url) || {
685
- frequency: 0,
686
- lastAccess: 0,
687
- averageResponseTime: 0,
688
- volatility: 0.5,
689
- };
690
- pattern.frequency++;
691
- pattern.lastAccess = Date.now();
692
- pattern.averageResponseTime =
693
- (pattern.averageResponseTime * (pattern.frequency - 1) +
694
- responseTime) /
695
- pattern.frequency;
696
- this.requestPatterns.set(url, pattern);
697
- }
698
- // ===== UTILITY METHODS =====
699
- /**
700
- * Normalize route for pattern tracking
701
- */
702
- normalizeRoute(path) {
703
- return path
704
- .replace(/\/\d+/g, "/:id")
705
- .replace(/\/[a-f0-9-]{36}/g, "/:uuid")
706
- .replace(/\?.*$/, "");
707
- }
708
- /**
709
- * Detect device type from user agent
710
- */
711
- detectDeviceType(userAgent) {
712
- if (/Mobile|Android|iPhone|iPad/.test(userAgent)) {
713
- return "mobile";
714
- }
715
- if (/Tablet|iPad/.test(userAgent)) {
716
- return "tablet";
717
- }
718
- return "desktop";
719
- }
720
- /**
721
- * Check for complex query parameters
722
- */
723
- hasComplexQueryParams(query) {
724
- const complexParams = [
725
- "search",
726
- "filter",
727
- "sort",
728
- "timestamp",
729
- "random",
730
- ];
731
- return complexParams.some((param) => param in query);
732
- }
733
- /**
734
- * Get top request patterns
735
- */
736
- getTopRequestPatterns() {
737
- const patterns = Array.from(this.requestPatterns.entries())
738
- .sort((a, b) => b[1].frequency - a[1].frequency)
739
- .slice(0, 10);
740
- return patterns.map(([route, data]) => ({
741
- route,
742
- frequency: data.frequency,
743
- averageResponseTime: data.averageResponseTime,
744
- volatility: data.volatility,
745
- }));
746
- }
747
- /**
748
- * Suggest TTL optimizations
749
- */
750
- suggestTTLOptimizations() {
751
- const suggestions = [];
752
- for (const [route, pattern] of this.requestPatterns.entries()) {
753
- if (pattern.frequency > 50 && pattern.volatility < 0.1) {
754
- suggestions.push(`Increase TTL for high-frequency, stable route: ${route}`);
755
- }
756
- if (pattern.volatility > 0.8) {
757
- suggestions.push(`Decrease TTL for volatile route: ${route}`);
758
- }
759
- }
760
- return suggestions;
761
- }
762
- /**
763
- * Generate optimization suggestions
764
- */
765
- generateSmartOptimizationSuggestions() {
766
- const suggestions = [];
767
- const hitRate = this.cacheAnalytics.totalRequests > 0
768
- ? (this.cacheAnalytics.cacheHits /
769
- this.cacheAnalytics.totalRequests) *
770
- 100
771
- : 0;
772
- if (hitRate < 30) {
773
- suggestions.push("Consider increasing TTL values to improve hit rate");
774
- }
775
- if (hitRate > 90) {
776
- suggestions.push("Excellent cache performance - consider expanding caching rules");
777
- }
778
- if (this.cacheAnalytics.averageMissTime >
779
- this.cacheAnalytics.averageHitTime * 10) {
780
- suggestions.push("High miss penalty - consider cache warming strategies");
781
- }
782
- return suggestions;
783
- }
784
- /**
785
- * Identify related content for prefetching
786
- */
787
- identifyRelatedContent(path) {
788
- const related = [];
789
- // Simple related content identification
790
- if (path.startsWith("/api/users/")) {
791
- related.push("/api/users/profile", "/api/users/preferences");
792
- }
793
- if (path.startsWith("/api/products/")) {
794
- related.push("/api/products/categories", "/api/products/featured");
795
- }
796
- return related;
797
- }
798
- /**
799
- * Setup analytics cleanup
800
- */
801
- setupAnalyticsCleanup() {
802
- // Reset analytics every hour
803
- setInterval(() => {
804
- this.resetAnalytics();
805
- }, 3600000); // 1 hour
806
- }
807
- /**
808
- * Setup dynamic TTL adjustment
809
- */
810
- setupDynamicTTLAdjustment() {
811
- // Analyze patterns every 10 minutes
812
- setInterval(() => {
813
- this.analyzeRequestPatterns();
814
- }, 600000); // 10 minutes
815
- }
816
- /**
817
- * Reset analytics
818
- */
819
- resetAnalytics() {
820
- // Keep some historical data, reset counters
821
- this.cacheAnalytics.totalRequests = 0;
822
- this.cacheAnalytics.cacheableRequests = 0;
823
- this.cacheAnalytics.cacheHits = 0;
824
- this.cacheAnalytics.cacheMisses = 0;
825
- this.cacheAnalytics.cacheSkips = 0;
826
- }
827
- /**
828
- * Analyze request patterns for optimization
829
- */
830
- analyzeRequestPatterns() {
831
- const now = Date.now();
832
- // Clean up old patterns
833
- for (const [route, pattern] of this.requestPatterns.entries()) {
834
- if (now - pattern.lastAccess > 3600000) {
835
- // 1 hour
836
- this.requestPatterns.delete(route);
837
- }
838
- }
839
- }
840
- }
841
-
842
- exports.SmartCachePlugin = SmartCachePlugin;
843
- //# sourceMappingURL=SmartCachePlugin.js.map