@json-to-office/shared 0.1.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.
@@ -0,0 +1,738 @@
1
+ // src/cache/manager.ts
2
+ import { EventEmitter } from "events";
3
+ var ComponentCacheManager = class extends EventEmitter {
4
+ config;
5
+ stats;
6
+ constructor(config) {
7
+ super();
8
+ this.config = config;
9
+ this.stats = this.initializeStats();
10
+ }
11
+ /**
12
+ * Get cache statistics with deep immutability
13
+ */
14
+ getStats() {
15
+ const componentStatsCopy = /* @__PURE__ */ new Map();
16
+ this.stats.componentStats.forEach((value, key) => {
17
+ componentStatsCopy.set(key, { ...value });
18
+ });
19
+ return {
20
+ ...this.stats,
21
+ componentStats: componentStatsCopy
22
+ };
23
+ }
24
+ /**
25
+ * Get configuration with deep immutability
26
+ */
27
+ getConfig() {
28
+ const configCopy = {
29
+ enabled: this.config.enabled,
30
+ evictionPolicy: this.config.evictionPolicy,
31
+ memory: { ...this.config.memory },
32
+ performance: { ...this.config.performance }
33
+ };
34
+ if (this.config.disk) {
35
+ configCopy.disk = { ...this.config.disk };
36
+ }
37
+ if (this.config.componentConfig) {
38
+ configCopy.componentConfig = {};
39
+ for (const [key, value] of Object.entries(this.config.componentConfig)) {
40
+ configCopy.componentConfig[key] = { ...value };
41
+ }
42
+ }
43
+ return configCopy;
44
+ }
45
+ /**
46
+ * Get multiple entries (batch operation)
47
+ */
48
+ async getMany(keys) {
49
+ const results = /* @__PURE__ */ new Map();
50
+ if (this.config.performance.parallelProcessing) {
51
+ const promises = keys.map(async (key) => {
52
+ const value = await this.get(key);
53
+ if (value) results.set(key, value);
54
+ });
55
+ await Promise.all(promises);
56
+ } else {
57
+ for (const key of keys) {
58
+ const value = await this.get(key);
59
+ if (value) results.set(key, value);
60
+ }
61
+ }
62
+ return results;
63
+ }
64
+ /**
65
+ * Set multiple entries (batch operation)
66
+ */
67
+ async setMany(entries) {
68
+ if (this.config.performance.parallelProcessing) {
69
+ const promises = entries.map(([key, value]) => this.set(key, value));
70
+ await Promise.all(promises);
71
+ } else {
72
+ for (const [key, value] of entries) {
73
+ await this.set(key, value);
74
+ }
75
+ }
76
+ }
77
+ /**
78
+ * Delete multiple entries
79
+ */
80
+ async deleteMany(keys) {
81
+ let deleted = 0;
82
+ if (this.config.performance.parallelProcessing) {
83
+ const results = await Promise.all(keys.map((key) => this.delete(key)));
84
+ deleted = results.filter(Boolean).length;
85
+ } else {
86
+ for (const key of keys) {
87
+ if (await this.delete(key)) {
88
+ deleted++;
89
+ }
90
+ }
91
+ }
92
+ return deleted;
93
+ }
94
+ /**
95
+ * Initialize statistics
96
+ */
97
+ initializeStats() {
98
+ return {
99
+ entries: 0,
100
+ totalSize: 0,
101
+ hitRate: 0,
102
+ missRate: 0,
103
+ totalHits: 0,
104
+ totalMisses: 0,
105
+ avgResponseTime: 0,
106
+ evictions: 0,
107
+ componentStats: /* @__PURE__ */ new Map()
108
+ };
109
+ }
110
+ /**
111
+ * Update statistics on cache hit
112
+ */
113
+ updateHitStats(key, component) {
114
+ this.stats.totalHits++;
115
+ this.updateComponentStats(component.componentName, "hit");
116
+ this.recalculateRates();
117
+ this.emit("hit", key, component);
118
+ }
119
+ /**
120
+ * Update statistics on cache miss
121
+ */
122
+ updateMissStats(key, componentName) {
123
+ this.stats.totalMisses++;
124
+ if (componentName) {
125
+ this.updateComponentStats(componentName, "miss");
126
+ }
127
+ this.recalculateRates();
128
+ this.emit("miss", key);
129
+ }
130
+ /**
131
+ * Update component-specific statistics
132
+ */
133
+ updateComponentStats(componentName, event) {
134
+ if (!this.stats.componentStats.has(componentName)) {
135
+ this.stats.componentStats.set(componentName, {
136
+ name: componentName,
137
+ hits: 0,
138
+ misses: 0,
139
+ avgProcessTime: 0,
140
+ avgSize: 0,
141
+ entries: 0
142
+ });
143
+ }
144
+ const stats = this.stats.componentStats.get(componentName);
145
+ if (event === "hit") {
146
+ stats.hits++;
147
+ } else {
148
+ stats.misses++;
149
+ }
150
+ }
151
+ /**
152
+ * Recalculate hit/miss rates
153
+ */
154
+ recalculateRates() {
155
+ const total = this.stats.totalHits + this.stats.totalMisses;
156
+ if (total > 0) {
157
+ this.stats.hitRate = this.stats.totalHits / total;
158
+ this.stats.missRate = this.stats.totalMisses / total;
159
+ }
160
+ }
161
+ /**
162
+ * Emit statistics periodically
163
+ */
164
+ emitStats() {
165
+ this.emit("stats", this.getStats());
166
+ }
167
+ };
168
+
169
+ // src/cache/memory-cache.ts
170
+ var LRUNode = class {
171
+ key;
172
+ value;
173
+ prev = null;
174
+ next = null;
175
+ constructor(key, value) {
176
+ this.key = key;
177
+ this.value = value;
178
+ }
179
+ };
180
+ var MemoryCache = class extends ComponentCacheManager {
181
+ cache;
182
+ head = null;
183
+ tail = null;
184
+ currentSize = 0;
185
+ keyToComponentName = /* @__PURE__ */ new Map();
186
+ cleanupTimer;
187
+ constructor(config) {
188
+ super(config);
189
+ this.cache = /* @__PURE__ */ new Map();
190
+ if (config.memory.cleanupInterval > 0) {
191
+ this.startCleanupTimer();
192
+ }
193
+ }
194
+ async get(key) {
195
+ const node = this.cache.get(key);
196
+ if (!node) {
197
+ const componentName = this.keyToComponentName.get(key) || this.extractComponentNameFromKey(key);
198
+ this.updateMissStats(key, componentName);
199
+ return void 0;
200
+ }
201
+ const component = node.value;
202
+ if (this.isExpired(component)) {
203
+ await this.delete(key);
204
+ this.updateMissStats(key, component.componentName);
205
+ return void 0;
206
+ }
207
+ component.lastAccessed = Date.now();
208
+ component.hits++;
209
+ this.moveToHead(node);
210
+ this.updateHitStats(key, component);
211
+ return component;
212
+ }
213
+ async set(key, component) {
214
+ if (!this.isCacheable(component.componentName)) {
215
+ return;
216
+ }
217
+ this.keyToComponentName.set(key, component.componentName);
218
+ if (this.cache.has(key)) {
219
+ await this.delete(key);
220
+ }
221
+ await this.ensureSpace(component.size);
222
+ const node = new LRUNode(key, component);
223
+ this.cache.set(key, node);
224
+ this.addToHead(node);
225
+ this.currentSize += component.size;
226
+ this.stats.entries++;
227
+ this.stats.totalSize = this.currentSize;
228
+ let componentStats = this.stats.componentStats.get(component.componentName);
229
+ if (!componentStats) {
230
+ componentStats = {
231
+ name: component.componentName,
232
+ hits: 0,
233
+ misses: 0,
234
+ entries: 0,
235
+ avgSize: 0,
236
+ avgProcessTime: 0
237
+ };
238
+ this.stats.componentStats.set(component.componentName, componentStats);
239
+ }
240
+ componentStats.entries++;
241
+ componentStats.avgSize = (componentStats.avgSize * (componentStats.entries - 1) + component.size) / componentStats.entries;
242
+ this.emit("set", key, component);
243
+ }
244
+ async has(key) {
245
+ const node = this.cache.get(key);
246
+ if (!node) return false;
247
+ return !this.isExpired(node.value);
248
+ }
249
+ async delete(key) {
250
+ const node = this.cache.get(key);
251
+ if (!node) return false;
252
+ this.removeNode(node);
253
+ this.cache.delete(key);
254
+ this.keyToComponentName.delete(key);
255
+ this.currentSize -= node.value.size;
256
+ this.stats.entries--;
257
+ this.stats.totalSize = this.currentSize;
258
+ const componentStats = this.stats.componentStats.get(node.value.componentName);
259
+ if (componentStats && componentStats.entries > 0) {
260
+ componentStats.entries--;
261
+ }
262
+ return true;
263
+ }
264
+ async clear() {
265
+ this.cache.clear();
266
+ this.keyToComponentName.clear();
267
+ this.head = null;
268
+ this.tail = null;
269
+ this.currentSize = 0;
270
+ this.stats = this.initializeStats();
271
+ }
272
+ async getKeys() {
273
+ return Array.from(this.cache.keys());
274
+ }
275
+ /**
276
+ * Extract component name from cache key
277
+ */
278
+ extractComponentNameFromKey(key) {
279
+ const parts = key.split(":");
280
+ return parts.length >= 2 ? parts[1] : void 0;
281
+ }
282
+ /**
283
+ * Check if component is expired
284
+ */
285
+ isExpired(component) {
286
+ if (!component.ttl) return false;
287
+ return Date.now() - component.timestamp > component.ttl * 1e3;
288
+ }
289
+ /**
290
+ * Check if component is cacheable
291
+ */
292
+ isCacheable(componentName) {
293
+ const componentConfig = this.config.componentConfig?.[componentName];
294
+ return componentConfig?.cacheable !== false;
295
+ }
296
+ /**
297
+ * Ensure enough space for new entry
298
+ */
299
+ async ensureSpace(requiredSize) {
300
+ const maxSize = this.config.memory.maxSize * 1024 * 1024;
301
+ while (this.currentSize + requiredSize > maxSize && this.tail) {
302
+ const keyToEvict = this.tail.key;
303
+ await this.delete(keyToEvict);
304
+ this.stats.evictions++;
305
+ this.emit("evict", keyToEvict, "size");
306
+ }
307
+ const maxEntries = this.config.memory.maxEntries;
308
+ while (this.stats.entries >= maxEntries && this.tail) {
309
+ const keyToEvict = this.tail.key;
310
+ await this.delete(keyToEvict);
311
+ this.stats.evictions++;
312
+ this.emit("evict", keyToEvict, "size");
313
+ }
314
+ }
315
+ /**
316
+ * LRU operations - Add node to head
317
+ */
318
+ addToHead(node) {
319
+ node.prev = null;
320
+ node.next = this.head;
321
+ if (this.head) {
322
+ this.head.prev = node;
323
+ }
324
+ this.head = node;
325
+ if (!this.tail) {
326
+ this.tail = node;
327
+ }
328
+ }
329
+ /**
330
+ * Remove node from list
331
+ */
332
+ removeNode(node) {
333
+ if (node.prev) {
334
+ node.prev.next = node.next;
335
+ } else {
336
+ this.head = node.next;
337
+ }
338
+ if (node.next) {
339
+ node.next.prev = node.prev;
340
+ } else {
341
+ this.tail = node.prev;
342
+ }
343
+ }
344
+ /**
345
+ * Move node to head
346
+ */
347
+ moveToHead(node) {
348
+ if (node === this.head) return;
349
+ this.removeNode(node);
350
+ this.addToHead(node);
351
+ }
352
+ /**
353
+ * Start cleanup timer for expired entries
354
+ */
355
+ startCleanupTimer() {
356
+ this.cleanupTimer = setInterval(
357
+ async () => {
358
+ const keysToDelete = [];
359
+ for (const [key, node] of this.cache) {
360
+ if (this.isExpired(node.value)) {
361
+ keysToDelete.push(key);
362
+ }
363
+ }
364
+ for (const key of keysToDelete) {
365
+ await this.delete(key);
366
+ this.stats.evictions++;
367
+ this.emit("evict", key, "ttl");
368
+ }
369
+ },
370
+ (this.config.memory?.cleanupInterval || 300) * 1e3
371
+ );
372
+ }
373
+ /**
374
+ * Stop cleanup timer
375
+ */
376
+ destroy() {
377
+ if (this.cleanupTimer) {
378
+ clearInterval(this.cleanupTimer);
379
+ }
380
+ }
381
+ };
382
+
383
+ // src/cache/config.ts
384
+ var DEFAULT_CACHE_CONFIG = {
385
+ enabled: true,
386
+ memory: {
387
+ enabled: true,
388
+ maxSize: 100,
389
+ // 100MB
390
+ maxEntries: 1e3,
391
+ defaultTTL: 3600,
392
+ // 1 hour
393
+ cleanupInterval: 300
394
+ // 5 minutes
395
+ },
396
+ evictionPolicy: "lru",
397
+ componentConfig: {
398
+ // Static content - longer TTL
399
+ text: { cacheable: true, ttl: 7200 },
400
+ // 2 hours
401
+ heading: { cacheable: true, ttl: 7200 },
402
+ columns: { cacheable: true, ttl: 3600 },
403
+ // 1 hour
404
+ section: { cacheable: true, ttl: 3600 },
405
+ // Dynamic content - shorter TTL
406
+ "custom-data": { cacheable: true, ttl: 300 },
407
+ // 5 minutes
408
+ "api-content": { cacheable: true, ttl: 180 },
409
+ // 3 minutes
410
+ // Resource-intensive components - medium TTL
411
+ image: { cacheable: true, ttl: 1800 },
412
+ // 30 minutes
413
+ table: { cacheable: true, ttl: 1800 }
414
+ },
415
+ performance: {
416
+ trackMetrics: true,
417
+ metricsSampleRate: 1,
418
+ enableWarming: false,
419
+ parallelProcessing: true
420
+ }
421
+ };
422
+ function getCacheConfigFromEnv() {
423
+ const config = {};
424
+ if (process.env.CACHE_ENABLED !== void 0) {
425
+ config.enabled = process.env.CACHE_ENABLED !== "false";
426
+ }
427
+ if (process.env.CACHE_MAX_SIZE || process.env.CACHE_MAX_ENTRIES || process.env.CACHE_TTL) {
428
+ config.memory = {
429
+ enabled: true,
430
+ maxSize: parseInt(process.env.CACHE_MAX_SIZE || "100"),
431
+ maxEntries: parseInt(process.env.CACHE_MAX_ENTRIES || "1000"),
432
+ defaultTTL: parseInt(process.env.CACHE_TTL || "3600"),
433
+ cleanupInterval: parseInt(process.env.CACHE_CLEANUP_INTERVAL || "300")
434
+ };
435
+ }
436
+ if (process.env.CACHE_TRACK_METRICS !== void 0 || process.env.CACHE_WARMING !== void 0) {
437
+ config.performance = {
438
+ trackMetrics: process.env.CACHE_TRACK_METRICS !== "false",
439
+ metricsSampleRate: parseFloat(process.env.CACHE_SAMPLE_RATE || "1.0"),
440
+ enableWarming: process.env.CACHE_WARMING === "true",
441
+ parallelProcessing: process.env.CACHE_PARALLEL !== "false"
442
+ };
443
+ }
444
+ return config;
445
+ }
446
+ function mergeConfigs(...configs) {
447
+ const merged = { ...DEFAULT_CACHE_CONFIG };
448
+ for (const config of configs) {
449
+ if (config.enabled !== void 0) {
450
+ merged.enabled = config.enabled;
451
+ }
452
+ if (config.memory) {
453
+ merged.memory = { ...merged.memory, ...config.memory };
454
+ }
455
+ if (config.disk) {
456
+ merged.disk = { ...merged.disk, ...config.disk };
457
+ }
458
+ if (config.evictionPolicy) {
459
+ merged.evictionPolicy = config.evictionPolicy;
460
+ }
461
+ if (config.componentConfig) {
462
+ merged.componentConfig = { ...merged.componentConfig, ...config.componentConfig };
463
+ }
464
+ if (config.performance) {
465
+ merged.performance = { ...merged.performance, ...config.performance };
466
+ }
467
+ }
468
+ return merged;
469
+ }
470
+
471
+ // src/cache/analytics.ts
472
+ var ComponentCacheAnalytics = class {
473
+ historyWindow = 36e5;
474
+ // 1 hour in milliseconds
475
+ metricsHistory = /* @__PURE__ */ new Map();
476
+ performanceBaseline = /* @__PURE__ */ new Map();
477
+ /**
478
+ * Analyze cache statistics and generate comprehensive report
479
+ */
480
+ analyzeCache(stats) {
481
+ const componentMetrics = this.calculateComponentMetrics(stats);
482
+ const trends = this.calculateTrends(stats);
483
+ const recommendations = this.generateRecommendations(componentMetrics, trends);
484
+ const healthScore = this.calculateHealthScore(componentMetrics);
485
+ const overallEfficiency = this.calculateOverallEfficiency(componentMetrics);
486
+ const performanceGain = this.calculatePerformanceGain(componentMetrics);
487
+ const sortedByEfficiency = [...componentMetrics].sort(
488
+ (a, b) => b.efficiencyScore - a.efficiencyScore
489
+ );
490
+ const topPerformers = sortedByEfficiency.slice(0, 3).map((m) => m.componentName);
491
+ const needsAttention = sortedByEfficiency.filter((m) => m.efficiencyScore < 50).map((m) => m.componentName);
492
+ return {
493
+ timestamp: Date.now(),
494
+ healthScore,
495
+ overallEfficiency,
496
+ componentMetrics,
497
+ trends,
498
+ recommendations,
499
+ topPerformers,
500
+ needsAttention,
501
+ performanceGain
502
+ };
503
+ }
504
+ /**
505
+ * Calculate detailed metrics for each component
506
+ */
507
+ calculateComponentMetrics(stats) {
508
+ const metrics = [];
509
+ stats.componentStats.forEach((componentStats, componentName) => {
510
+ const totalRequests = componentStats.hits + componentStats.misses;
511
+ const hitRate = totalRequests > 0 ? componentStats.hits / totalRequests : 0;
512
+ const efficiencyScore = this.calculateEfficiencyScore(
513
+ hitRate,
514
+ componentStats.avgProcessTime,
515
+ componentStats.avgSize,
516
+ componentStats.entries
517
+ );
518
+ const timeSaved = componentStats.hits * componentStats.avgProcessTime;
519
+ const memoryCost = componentStats.entries * componentStats.avgSize;
520
+ const timeBenefit = timeSaved;
521
+ const costBenefitRatio = memoryCost > 0 ? timeBenefit / memoryCost : 0;
522
+ metrics.push({
523
+ componentName,
524
+ hitRate,
525
+ totalRequests,
526
+ avgHitTime: 1,
527
+ // Cache hits are typically ~1ms
528
+ avgMissTime: componentStats.avgProcessTime,
529
+ efficiencyScore,
530
+ memoryUsage: componentStats.entries * componentStats.avgSize,
531
+ timeSaved,
532
+ costBenefitRatio
533
+ });
534
+ });
535
+ return metrics;
536
+ }
537
+ /**
538
+ * Calculate efficiency score for a component
539
+ */
540
+ calculateEfficiencyScore(hitRate, avgProcessTime, avgSize, entries) {
541
+ const hitRateWeight = 0.4;
542
+ const processingTimeWeight = 0.3;
543
+ const memorySizeWeight = 0.2;
544
+ const utilizationWeight = 0.1;
545
+ const hitRateScore = hitRate * 100;
546
+ const processingTimeScore = Math.min(100, avgProcessTime / 10 * 100);
547
+ const memorySizeScore = Math.max(0, 100 - avgSize / 1e4 * 100);
548
+ const utilizationScore = Math.min(100, entries / 100 * 100);
549
+ const score = hitRateScore * hitRateWeight + processingTimeScore * processingTimeWeight + memorySizeScore * memorySizeWeight + utilizationScore * utilizationWeight;
550
+ return Math.round(score);
551
+ }
552
+ /**
553
+ * Calculate trends over time
554
+ */
555
+ calculateTrends(stats) {
556
+ const trends = [];
557
+ const now = Date.now();
558
+ stats.componentStats.forEach((componentStats, componentName) => {
559
+ const historyKey = `${componentName}_hitRate`;
560
+ if (!this.metricsHistory.has(historyKey)) {
561
+ this.metricsHistory.set(historyKey, []);
562
+ }
563
+ const history = this.metricsHistory.get(historyKey);
564
+ const totalRequests = componentStats.hits + componentStats.misses;
565
+ const hitRate = totalRequests > 0 ? componentStats.hits / totalRequests : 0;
566
+ history.push({ timestamp: now, value: hitRate });
567
+ const cutoff = now - this.historyWindow;
568
+ const cleanedHistory = history.filter(
569
+ (point) => point.timestamp > cutoff
570
+ );
571
+ this.metricsHistory.set(historyKey, cleanedHistory);
572
+ trends.push({
573
+ componentName,
574
+ hitRateTrend: [...cleanedHistory],
575
+ requestVolumeTrend: this.getOrCreateHistory(
576
+ `${componentName}_volume`,
577
+ totalRequests
578
+ ),
579
+ responseTimeTrend: this.getOrCreateHistory(
580
+ `${componentName}_response`,
581
+ componentStats.avgProcessTime
582
+ ),
583
+ memoryUsageTrend: this.getOrCreateHistory(
584
+ `${componentName}_memory`,
585
+ componentStats.entries * componentStats.avgSize
586
+ )
587
+ });
588
+ });
589
+ return trends;
590
+ }
591
+ /**
592
+ * Get or create history for a metric
593
+ */
594
+ getOrCreateHistory(key, currentValue) {
595
+ const now = Date.now();
596
+ if (!this.metricsHistory.has(key)) {
597
+ this.metricsHistory.set(key, []);
598
+ }
599
+ const history = this.metricsHistory.get(key);
600
+ history.push({ timestamp: now, value: currentValue });
601
+ const cutoff = now - this.historyWindow;
602
+ const cleanedHistory = history.filter((point) => point.timestamp > cutoff);
603
+ this.metricsHistory.set(key, cleanedHistory);
604
+ return [...cleanedHistory];
605
+ }
606
+ /**
607
+ * Generate optimization recommendations
608
+ */
609
+ generateRecommendations(metrics, trends) {
610
+ const recommendations = [];
611
+ metrics.forEach((metric) => {
612
+ const trend = trends.find((t) => t.componentName === metric.componentName);
613
+ if (metric.hitRate < 0.3 && metric.totalRequests > 10) {
614
+ recommendations.push({
615
+ componentName: metric.componentName,
616
+ type: "increase_ttl",
617
+ description: `Increase TTL for ${metric.componentName} components to improve hit rate`,
618
+ expectedImprovement: 20,
619
+ priority: 4,
620
+ reasoning: `Current hit rate of ${(metric.hitRate * 100).toFixed(1)}% is below optimal threshold. Increasing TTL could improve cache effectiveness.`
621
+ });
622
+ }
623
+ if (metric.memoryUsage > 1e6 && metric.hitRate < 0.5) {
624
+ recommendations.push({
625
+ componentName: metric.componentName,
626
+ type: "decrease_ttl",
627
+ description: `Reduce cache size for ${metric.componentName} components`,
628
+ expectedImprovement: 15,
629
+ priority: 3,
630
+ reasoning: `High memory usage (${(metric.memoryUsage / 1024 / 1024).toFixed(2)}MB) with moderate hit rate suggests over-caching.`
631
+ });
632
+ }
633
+ if (metric.efficiencyScore < 30) {
634
+ recommendations.push({
635
+ componentName: metric.componentName,
636
+ type: "disable_cache",
637
+ description: `Consider disabling cache for ${metric.componentName} components`,
638
+ expectedImprovement: 10,
639
+ priority: 2,
640
+ reasoning: `Efficiency score of ${metric.efficiencyScore} indicates caching may not be beneficial for this component.`
641
+ });
642
+ }
643
+ if (trend && this.isDecliningSlopbankTrend(trend.hitRateTrend)) {
644
+ recommendations.push({
645
+ componentName: metric.componentName,
646
+ type: "increase_size",
647
+ description: `Increase cache capacity for ${metric.componentName} components`,
648
+ expectedImprovement: 25,
649
+ priority: 5,
650
+ reasoning: "Hit rate is declining over time, suggesting cache capacity may be insufficient."
651
+ });
652
+ }
653
+ });
654
+ return recommendations.sort((a, b) => b.priority - a.priority);
655
+ }
656
+ /**
657
+ * Check if a trend is declining
658
+ */
659
+ isDecliningSlopbankTrend(trend) {
660
+ if (trend.length < 3) return false;
661
+ const n = trend.length;
662
+ const recent = trend.slice(-Math.min(10, n));
663
+ if (recent.length < 3) return false;
664
+ const firstValue = recent[0].value;
665
+ const lastValue = recent[recent.length - 1].value;
666
+ return (firstValue - lastValue) / firstValue > 0.1;
667
+ }
668
+ /**
669
+ * Calculate overall cache health score
670
+ */
671
+ calculateHealthScore(metrics) {
672
+ if (metrics.length === 0) return 0;
673
+ const avgEfficiency = metrics.reduce((sum, m) => sum + m.efficiencyScore, 0) / metrics.length;
674
+ const avgHitRate = metrics.reduce((sum, m) => sum + m.hitRate, 0) / metrics.length;
675
+ const healthScore = avgEfficiency * 0.6 + avgHitRate * 100 * 0.4;
676
+ return Math.round(healthScore);
677
+ }
678
+ /**
679
+ * Calculate overall cache efficiency
680
+ */
681
+ calculateOverallEfficiency(metrics) {
682
+ if (metrics.length === 0) return 0;
683
+ const totalTimeSaved = metrics.reduce((sum, m) => sum + m.timeSaved, 0);
684
+ const totalMemoryUsed = metrics.reduce((sum, m) => sum + m.memoryUsage, 0);
685
+ const totalRequests = metrics.reduce((sum, m) => sum + m.totalRequests, 0);
686
+ const totalHits = metrics.reduce(
687
+ (sum, m) => sum + m.hitRate * m.totalRequests,
688
+ 0
689
+ );
690
+ if (totalRequests === 0) return 0;
691
+ const hitRateEfficiency = totalHits / totalRequests * 100;
692
+ const resourceEfficiency = totalMemoryUsed > 0 ? Math.min(100, totalTimeSaved / totalMemoryUsed * 1e3) : 0;
693
+ return Math.round(hitRateEfficiency * 0.7 + resourceEfficiency * 0.3);
694
+ }
695
+ /**
696
+ * Calculate performance gain from caching
697
+ */
698
+ calculatePerformanceGain(metrics) {
699
+ const totalTimeSaved = metrics.reduce((sum, m) => sum + m.timeSaved, 0);
700
+ const totalProcessingTime = metrics.reduce(
701
+ (sum, m) => sum + m.totalRequests * m.avgMissTime,
702
+ 0
703
+ );
704
+ const cpuReduction = totalProcessingTime > 0 ? Math.round(totalTimeSaved / totalProcessingTime * 100) : 0;
705
+ const uniqueEntries = metrics.reduce(
706
+ (sum, m) => sum + m.memoryUsage / m.avgHitTime,
707
+ 0
708
+ );
709
+ const totalDataProcessed = metrics.reduce(
710
+ (sum, m) => sum + m.totalRequests * m.memoryUsage,
711
+ 0
712
+ );
713
+ const memoryOptimization = totalDataProcessed > 0 ? Math.round(
714
+ (totalDataProcessed - uniqueEntries) / totalDataProcessed * 100
715
+ ) : 0;
716
+ return {
717
+ timeReduction: Math.round(totalTimeSaved),
718
+ cpuReduction: Math.min(100, cpuReduction),
719
+ memoryOptimization: Math.min(100, memoryOptimization)
720
+ };
721
+ }
722
+ /**
723
+ * Reset analytics history
724
+ */
725
+ reset() {
726
+ this.metricsHistory.clear();
727
+ this.performanceBaseline.clear();
728
+ }
729
+ };
730
+ export {
731
+ ComponentCacheAnalytics,
732
+ ComponentCacheManager,
733
+ DEFAULT_CACHE_CONFIG,
734
+ MemoryCache,
735
+ getCacheConfigFromEnv,
736
+ mergeConfigs
737
+ };
738
+ //# sourceMappingURL=index.js.map