@dollhousemcp/mcp-server 1.5.1 → 1.6.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/CHANGELOG.md +90 -0
- package/README.md +497 -110
- package/data/agents/code-reviewer.md +8 -1
- package/data/agents/research-assistant.md +8 -1
- package/data/agents/task-manager.md +8 -1
- package/data/ensembles/business-advisor.md +8 -1
- package/data/ensembles/creative-studio.md +8 -1
- package/data/ensembles/development-team.md +8 -1
- package/data/ensembles/security-analysis-team.md +8 -1
- package/data/memories/conversation-history.md +8 -1
- package/data/memories/learning-progress.md +8 -1
- package/data/memories/project-context.md +8 -1
- package/data/personas/business-consultant.md +8 -1
- package/data/personas/creative-writer.md +8 -1
- package/data/personas/debug-detective.md +8 -1
- package/data/personas/eli5-explainer.md +8 -1
- package/data/personas/security-analyst.md +8 -1
- package/data/personas/technical-analyst.md +8 -1
- package/data/skills/code-review.md +8 -1
- package/data/skills/creative-writing.md +8 -1
- package/data/skills/data-analysis.md +8 -1
- package/data/skills/penetration-testing.md +8 -1
- package/data/skills/research.md +8 -1
- package/data/skills/threat-modeling.md +8 -1
- package/data/skills/translation.md +8 -1
- package/data/templates/code-documentation.md +8 -1
- package/data/templates/email-professional.md +8 -1
- package/data/templates/meeting-notes.md +8 -1
- package/data/templates/penetration-test-report.md +8 -1
- package/data/templates/project-brief.md +8 -1
- package/data/templates/report-executive.md +8 -1
- package/data/templates/security-vulnerability-report.md +8 -1
- package/data/templates/threat-assessment-report.md +8 -1
- package/dist/auth/GitHubAuthManager.d.ts +6 -1
- package/dist/auth/GitHubAuthManager.d.ts.map +1 -1
- package/dist/auth/GitHubAuthManager.js +45 -18
- package/dist/benchmarks/IndexPerformanceBenchmark.d.ts +98 -0
- package/dist/benchmarks/IndexPerformanceBenchmark.d.ts.map +1 -0
- package/dist/benchmarks/IndexPerformanceBenchmark.js +531 -0
- package/dist/cache/CollectionCache.d.ts +65 -0
- package/dist/cache/CollectionCache.d.ts.map +1 -0
- package/dist/cache/CollectionCache.js +172 -0
- package/dist/cache/CollectionIndexCache.d.ts +77 -0
- package/dist/cache/CollectionIndexCache.d.ts.map +1 -0
- package/dist/cache/CollectionIndexCache.js +349 -0
- package/dist/cache/LRUCache.d.ts +93 -0
- package/dist/cache/LRUCache.d.ts.map +1 -0
- package/dist/cache/LRUCache.js +299 -0
- package/dist/cache/index.d.ts +2 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +3 -1
- package/dist/collection/CollectionBrowser.d.ts +44 -1
- package/dist/collection/CollectionBrowser.d.ts.map +1 -1
- package/dist/collection/CollectionBrowser.js +260 -28
- package/dist/collection/CollectionIndexManager.d.ts +151 -0
- package/dist/collection/CollectionIndexManager.d.ts.map +1 -0
- package/dist/collection/CollectionIndexManager.js +499 -0
- package/dist/collection/CollectionSearch.d.ts +75 -1
- package/dist/collection/CollectionSearch.d.ts.map +1 -1
- package/dist/collection/CollectionSearch.js +435 -6
- package/dist/collection/CollectionSeeder.d.ts +36 -0
- package/dist/collection/CollectionSeeder.d.ts.map +1 -0
- package/dist/collection/CollectionSeeder.js +267 -0
- package/dist/collection/ElementInstaller.d.ts +31 -0
- package/dist/collection/ElementInstaller.d.ts.map +1 -1
- package/dist/collection/ElementInstaller.js +77 -15
- package/dist/collection/PersonaSubmitter.d.ts +48 -1
- package/dist/collection/PersonaSubmitter.d.ts.map +1 -1
- package/dist/collection/PersonaSubmitter.js +170 -34
- package/dist/collection/index.d.ts +2 -0
- package/dist/collection/index.d.ts.map +1 -1
- package/dist/collection/index.js +3 -1
- package/dist/config/ConfigManager.d.ts +78 -0
- package/dist/config/ConfigManager.d.ts.map +1 -0
- package/dist/config/ConfigManager.js +216 -0
- package/dist/config/element-types.d.ts +135 -0
- package/dist/config/element-types.d.ts.map +1 -0
- package/dist/config/element-types.js +108 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -1
- package/dist/config/portfolio-constants.d.ts +83 -0
- package/dist/config/portfolio-constants.d.ts.map +1 -0
- package/dist/config/portfolio-constants.js +99 -0
- package/dist/elements/BaseElement.d.ts +14 -2
- package/dist/elements/BaseElement.d.ts.map +1 -1
- package/dist/elements/BaseElement.js +88 -6
- package/dist/elements/agents/Agent.d.ts +10 -1
- package/dist/elements/agents/Agent.d.ts.map +1 -1
- package/dist/elements/agents/Agent.js +66 -19
- package/dist/elements/agents/AgentManager.d.ts +2 -0
- package/dist/elements/agents/AgentManager.d.ts.map +1 -1
- package/dist/elements/agents/AgentManager.js +12 -10
- package/dist/elements/skills/Skill.d.ts +10 -1
- package/dist/elements/skills/Skill.d.ts.map +1 -1
- package/dist/elements/skills/Skill.js +40 -3
- package/dist/elements/skills/SkillManager.d.ts +1 -0
- package/dist/elements/skills/SkillManager.d.ts.map +1 -1
- package/dist/elements/skills/SkillManager.js +10 -4
- package/dist/elements/templates/Template.d.ts +10 -1
- package/dist/elements/templates/Template.d.ts.map +1 -1
- package/dist/elements/templates/Template.js +35 -18
- package/dist/elements/templates/TemplateManager.d.ts +1 -1
- package/dist/elements/templates/TemplateManager.d.ts.map +1 -1
- package/dist/elements/templates/TemplateManager.js +6 -5
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/index.barrel.d.ts +1 -2
- package/dist/index.barrel.d.ts.map +1 -1
- package/dist/index.barrel.js +2 -4
- package/dist/index.d.ts +154 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1951 -264
- package/dist/persona/PersonaElement.d.ts +10 -0
- package/dist/persona/PersonaElement.d.ts.map +1 -1
- package/dist/persona/PersonaElement.js +55 -32
- package/dist/persona/PersonaElementManager.d.ts.map +1 -1
- package/dist/persona/PersonaElementManager.js +13 -11
- package/dist/persona/PersonaLoader.d.ts.map +1 -1
- package/dist/persona/PersonaLoader.js +8 -2
- package/dist/persona/export-import/PersonaImporter.d.ts.map +1 -1
- package/dist/persona/export-import/PersonaImporter.js +24 -5
- package/dist/persona/export-import/PersonaSharer.d.ts +21 -0
- package/dist/persona/export-import/PersonaSharer.d.ts.map +1 -1
- package/dist/persona/export-import/PersonaSharer.js +198 -22
- package/dist/portfolio/DefaultElementProvider.d.ts +90 -0
- package/dist/portfolio/DefaultElementProvider.d.ts.map +1 -1
- package/dist/portfolio/DefaultElementProvider.js +499 -7
- package/dist/portfolio/GitHubPortfolioIndexer.d.ts +129 -0
- package/dist/portfolio/GitHubPortfolioIndexer.d.ts.map +1 -0
- package/dist/portfolio/GitHubPortfolioIndexer.js +475 -0
- package/dist/portfolio/MigrationManager.d.ts.map +1 -1
- package/dist/portfolio/MigrationManager.js +136 -3
- package/dist/portfolio/PortfolioIndexManager.d.ts +130 -0
- package/dist/portfolio/PortfolioIndexManager.d.ts.map +1 -0
- package/dist/portfolio/PortfolioIndexManager.js +478 -0
- package/dist/portfolio/PortfolioManager.d.ts +5 -0
- package/dist/portfolio/PortfolioManager.d.ts.map +1 -1
- package/dist/portfolio/PortfolioManager.js +61 -20
- package/dist/portfolio/PortfolioRepoManager.d.ts +75 -0
- package/dist/portfolio/PortfolioRepoManager.d.ts.map +1 -0
- package/dist/portfolio/PortfolioRepoManager.js +337 -0
- package/dist/portfolio/UnifiedIndexManager.d.ts +388 -0
- package/dist/portfolio/UnifiedIndexManager.d.ts.map +1 -0
- package/dist/portfolio/UnifiedIndexManager.js +1434 -0
- package/dist/portfolio/index.d.ts +15 -0
- package/dist/portfolio/index.d.ts.map +1 -0
- package/dist/portfolio/index.js +15 -0
- package/dist/portfolio/types.d.ts +7 -0
- package/dist/portfolio/types.d.ts.map +1 -1
- package/dist/portfolio/types.js +6 -1
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +50 -48
- package/dist/security/audit/SecurityAuditor.d.ts.map +1 -1
- package/dist/security/audit/SecurityAuditor.js +17 -9
- package/dist/security/audit/config/suppressions.d.ts.map +1 -1
- package/dist/security/audit/config/suppressions.js +19 -3
- package/dist/security/contentValidator.d.ts +2 -0
- package/dist/security/contentValidator.d.ts.map +1 -1
- package/dist/security/contentValidator.js +115 -4
- package/dist/security/secureYamlParser.d.ts +1 -0
- package/dist/security/secureYamlParser.d.ts.map +1 -1
- package/dist/security/secureYamlParser.js +29 -7
- package/dist/security/securityMonitor.d.ts +1 -1
- package/dist/security/securityMonitor.d.ts.map +1 -1
- package/dist/security/securityMonitor.js +1 -1
- package/dist/security/tokenManager.d.ts +1 -1
- package/dist/security/tokenManager.d.ts.map +1 -1
- package/dist/security/tokenManager.js +30 -10
- package/dist/server/ServerSetup.d.ts +22 -2
- package/dist/server/ServerSetup.d.ts.map +1 -1
- package/dist/server/ServerSetup.js +77 -12
- package/dist/server/tools/AuthTools.d.ts.map +1 -1
- package/dist/server/tools/AuthTools.js +33 -1
- package/dist/server/tools/BuildInfoTools.d.ts +25 -0
- package/dist/server/tools/BuildInfoTools.d.ts.map +1 -0
- package/dist/server/tools/BuildInfoTools.js +36 -0
- package/dist/server/tools/CollectionTools.d.ts.map +1 -1
- package/dist/server/tools/CollectionTools.js +62 -42
- package/dist/server/tools/ConfigTools.d.ts.map +1 -1
- package/dist/server/tools/ConfigTools.js +29 -1
- package/dist/server/tools/PersonaTools.d.ts +4 -2
- package/dist/server/tools/PersonaTools.d.ts.map +1 -1
- package/dist/server/tools/PersonaTools.js +5 -152
- package/dist/server/tools/PortfolioTools.d.ts +12 -0
- package/dist/server/tools/PortfolioTools.d.ts.map +1 -0
- package/dist/server/tools/PortfolioTools.js +221 -0
- package/dist/server/tools/index.d.ts +3 -1
- package/dist/server/tools/index.d.ts.map +1 -1
- package/dist/server/tools/index.js +4 -2
- package/dist/server/types.d.ts +41 -5
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +1 -1
- package/dist/services/BuildInfoService.d.ts +84 -0
- package/dist/services/BuildInfoService.d.ts.map +1 -0
- package/dist/services/BuildInfoService.js +271 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.d.ts +54 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.d.ts.map +1 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.js +229 -0
- package/dist/tools/portfolio/submitToPortfolioTool.d.ts +164 -0
- package/dist/tools/portfolio/submitToPortfolioTool.d.ts.map +1 -0
- package/dist/tools/portfolio/submitToPortfolioTool.js +1523 -0
- package/dist/tools/portfolio/types.d.ts +41 -0
- package/dist/tools/portfolio/types.d.ts.map +1 -0
- package/dist/tools/portfolio/types.js +15 -0
- package/dist/types/collection.d.ts +51 -0
- package/dist/types/collection.d.ts.map +1 -1
- package/dist/types/collection.js +1 -1
- package/dist/utils/EarlyTerminationSearch.d.ts +41 -0
- package/dist/utils/EarlyTerminationSearch.d.ts.map +1 -0
- package/dist/utils/EarlyTerminationSearch.js +164 -0
- package/dist/utils/ErrorHandler.d.ts +86 -0
- package/dist/utils/ErrorHandler.d.ts.map +1 -0
- package/dist/utils/ErrorHandler.js +201 -0
- package/dist/utils/FileDiscoveryUtil.d.ts +53 -0
- package/dist/utils/FileDiscoveryUtil.d.ts.map +1 -0
- package/dist/utils/FileDiscoveryUtil.js +169 -0
- package/dist/utils/GitHubRateLimiter.d.ts +88 -0
- package/dist/utils/GitHubRateLimiter.d.ts.map +1 -0
- package/dist/utils/GitHubRateLimiter.js +315 -0
- package/dist/utils/PerformanceMonitor.d.ts +134 -0
- package/dist/utils/PerformanceMonitor.d.ts.map +1 -0
- package/dist/utils/PerformanceMonitor.js +347 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +172 -0
- package/dist/utils/SecureDownloader.d.ts +241 -0
- package/dist/utils/SecureDownloader.d.ts.map +1 -0
- package/dist/utils/SecureDownloader.js +759 -0
- package/dist/utils/ToolCache.d.ts +82 -0
- package/dist/utils/ToolCache.d.ts.map +1 -0
- package/dist/utils/ToolCache.js +196 -0
- package/dist/utils/errorCodes.d.ts +136 -0
- package/dist/utils/errorCodes.d.ts.map +1 -0
- package/dist/utils/errorCodes.js +87 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/installation.d.ts +1 -1
- package/dist/utils/installation.d.ts.map +1 -1
- package/dist/utils/installation.js +9 -8
- package/dist/utils/searchUtils.d.ts +54 -0
- package/dist/utils/searchUtils.d.ts.map +1 -0
- package/dist/utils/searchUtils.js +118 -0
- package/package.json +17 -7
- package/dist/config/updateConfig.d.ts +0 -84
- package/dist/config/updateConfig.d.ts.map +0 -1
- package/dist/config/updateConfig.js +0 -148
- package/dist/server/tools/UpdateTools.d.ts +0 -10
- package/dist/server/tools/UpdateTools.d.ts.map +0 -1
- package/dist/server/tools/UpdateTools.js +0 -85
- package/dist/update/BackupManager.d.ts +0 -63
- package/dist/update/BackupManager.d.ts.map +0 -1
- package/dist/update/BackupManager.js +0 -370
- package/dist/update/DependencyChecker.d.ts +0 -41
- package/dist/update/DependencyChecker.d.ts.map +0 -1
- package/dist/update/DependencyChecker.js +0 -132
- package/dist/update/RateLimiter.d.ts.map +0 -1
- package/dist/update/RateLimiter.js +0 -172
- package/dist/update/SignatureVerifier.d.ts +0 -71
- package/dist/update/SignatureVerifier.d.ts.map +0 -1
- package/dist/update/SignatureVerifier.js +0 -214
- package/dist/update/UpdateChecker.d.ts +0 -132
- package/dist/update/UpdateChecker.d.ts.map +0 -1
- package/dist/update/UpdateChecker.js +0 -506
- package/dist/update/UpdateManager.d.ts +0 -60
- package/dist/update/UpdateManager.d.ts.map +0 -1
- package/dist/update/UpdateManager.js +0 -730
- package/dist/update/VersionManager.d.ts +0 -31
- package/dist/update/VersionManager.d.ts.map +0 -1
- package/dist/update/VersionManager.js +0 -181
- package/dist/update/index.d.ts +0 -9
- package/dist/update/index.d.ts.map +0 -1
- package/dist/update/index.js +0 -9
- /package/dist/{update → utils}/RateLimiter.d.ts +0 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart cache for collection index with conditional fetching and performance optimization
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from 'fs/promises';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { SecurityMonitor } from '../security/securityMonitor.js';
|
|
8
|
+
import { CacheFactory } from './LRUCache.js';
|
|
9
|
+
import { PerformanceMonitor } from '../utils/PerformanceMonitor.js';
|
|
10
|
+
export class CollectionIndexCache {
|
|
11
|
+
cache = null;
|
|
12
|
+
TTL = 15 * 60 * 1000; // 15 minutes in milliseconds
|
|
13
|
+
INDEX_URL = 'https://raw.githubusercontent.com/DollhouseMCP/collection/main/public/collection-index.json';
|
|
14
|
+
cacheDir;
|
|
15
|
+
cacheFile;
|
|
16
|
+
githubClient;
|
|
17
|
+
performanceMonitor;
|
|
18
|
+
memoryCache;
|
|
19
|
+
fetchPromise = null; // Prevent concurrent fetches
|
|
20
|
+
constructor(githubClient, baseDir = process.cwd()) {
|
|
21
|
+
this.githubClient = githubClient;
|
|
22
|
+
this.cacheDir = path.join(baseDir, '.dollhousemcp', 'cache');
|
|
23
|
+
this.cacheFile = path.join(this.cacheDir, 'collection-index-cache.json');
|
|
24
|
+
this.performanceMonitor = PerformanceMonitor.getInstance();
|
|
25
|
+
// Initialize memory cache for frequently accessed data
|
|
26
|
+
this.memoryCache = CacheFactory.createAPICache({
|
|
27
|
+
maxSize: 50,
|
|
28
|
+
maxMemoryMB: 10,
|
|
29
|
+
ttlMs: 5 * 60 * 1000, // 5 minutes
|
|
30
|
+
onEviction: (key, value) => {
|
|
31
|
+
logger.debug('Collection memory cache eviction', { key });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the collection index with performance optimization and lazy loading
|
|
37
|
+
*/
|
|
38
|
+
async getIndex(lazyLoad = false) {
|
|
39
|
+
const startTime = Date.now();
|
|
40
|
+
const memoryBefore = process.memoryUsage().heapUsed;
|
|
41
|
+
try {
|
|
42
|
+
// Check memory cache first for fastest access
|
|
43
|
+
const memoryCached = this.memoryCache.get('collection-index');
|
|
44
|
+
if (memoryCached && this.isValid()) {
|
|
45
|
+
logger.debug('Using memory cached collection index');
|
|
46
|
+
this.recordPerformanceMetrics(startTime, memoryBefore, 'memory-hit');
|
|
47
|
+
return memoryCached;
|
|
48
|
+
}
|
|
49
|
+
// Check if we have valid disk cached data
|
|
50
|
+
if (this.isValid()) {
|
|
51
|
+
logger.debug('Using valid disk cached collection index');
|
|
52
|
+
const result = this.cache.data;
|
|
53
|
+
this.memoryCache.set('collection-index', result);
|
|
54
|
+
this.recordPerformanceMetrics(startTime, memoryBefore, 'disk-hit');
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
// Prevent concurrent fetches
|
|
58
|
+
if (this.fetchPromise) {
|
|
59
|
+
logger.debug('Waiting for ongoing collection index fetch');
|
|
60
|
+
return await this.fetchPromise;
|
|
61
|
+
}
|
|
62
|
+
// Lazy loading: Only fetch if not in lazy mode or absolutely necessary
|
|
63
|
+
if (lazyLoad && this.cache?.data) {
|
|
64
|
+
logger.debug('Using stale cache in lazy load mode');
|
|
65
|
+
this.recordPerformanceMetrics(startTime, memoryBefore, 'lazy-stale');
|
|
66
|
+
return this.cache.data;
|
|
67
|
+
}
|
|
68
|
+
// Create fetch promise to prevent concurrent requests
|
|
69
|
+
this.fetchPromise = this.fetchFreshWithFallback();
|
|
70
|
+
try {
|
|
71
|
+
const result = await this.fetchPromise;
|
|
72
|
+
this.memoryCache.set('collection-index', result);
|
|
73
|
+
this.recordPerformanceMetrics(startTime, memoryBefore, 'fresh-fetch');
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this.fetchPromise = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
logger.error('Failed to get collection index:', error);
|
|
82
|
+
// Try loading from persistent cache as last resort
|
|
83
|
+
const persistentCache = await this.loadFromDisk();
|
|
84
|
+
if (persistentCache) {
|
|
85
|
+
logger.debug('Using persistent cache as last resort');
|
|
86
|
+
this.cache = persistentCache;
|
|
87
|
+
const result = persistentCache.data;
|
|
88
|
+
this.memoryCache.set('collection-index', result);
|
|
89
|
+
this.recordPerformanceMetrics(startTime, memoryBefore, 'disk-fallback');
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check if current cache is valid (within TTL)
|
|
97
|
+
*/
|
|
98
|
+
isValid() {
|
|
99
|
+
if (!this.cache) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const age = Date.now() - this.cache.fetchedAt.getTime();
|
|
103
|
+
return age < this.TTL;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Fetch fresh index from GitHub with conditional requests
|
|
107
|
+
*/
|
|
108
|
+
async fetchFresh() {
|
|
109
|
+
try {
|
|
110
|
+
// Build headers for conditional request
|
|
111
|
+
const headers = {
|
|
112
|
+
'Accept': 'application/json',
|
|
113
|
+
'User-Agent': 'DollhouseMCP/1.0'
|
|
114
|
+
};
|
|
115
|
+
// Add conditional headers if we have cache
|
|
116
|
+
if (this.cache?.etag) {
|
|
117
|
+
headers['If-None-Match'] = this.cache.etag;
|
|
118
|
+
}
|
|
119
|
+
if (this.cache?.lastModified) {
|
|
120
|
+
headers['If-Modified-Since'] = this.cache.lastModified;
|
|
121
|
+
}
|
|
122
|
+
// Use fetch directly for better control over conditional requests
|
|
123
|
+
const response = await fetch(this.INDEX_URL, { headers });
|
|
124
|
+
// 304 Not Modified - use cached data
|
|
125
|
+
if (response.status === 304) {
|
|
126
|
+
if (this.cache) {
|
|
127
|
+
// Update timestamp to extend cache validity
|
|
128
|
+
this.cache.fetchedAt = new Date();
|
|
129
|
+
await this.saveToDisk(this.cache);
|
|
130
|
+
logger.debug('Collection index not modified - refreshed cache timestamp');
|
|
131
|
+
return this.cache.data;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
136
|
+
}
|
|
137
|
+
const indexData = await response.json();
|
|
138
|
+
// Validate the index structure
|
|
139
|
+
if (!this.validateIndexStructure(indexData)) {
|
|
140
|
+
throw new Error('Invalid collection index structure received');
|
|
141
|
+
}
|
|
142
|
+
// Create new cache entry
|
|
143
|
+
const newCache = {
|
|
144
|
+
data: indexData,
|
|
145
|
+
fetchedAt: new Date(),
|
|
146
|
+
etag: response.headers.get('etag') || undefined,
|
|
147
|
+
lastModified: response.headers.get('last-modified') || undefined
|
|
148
|
+
};
|
|
149
|
+
this.cache = newCache;
|
|
150
|
+
// Save to persistent cache in background
|
|
151
|
+
this.saveToDisk(newCache).catch(error => {
|
|
152
|
+
logger.debug('Failed to save index cache to disk:', error);
|
|
153
|
+
});
|
|
154
|
+
logger.debug(`Fresh collection index fetched with ${indexData.total_elements} elements`);
|
|
155
|
+
return indexData;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
logger.debug('Failed to fetch fresh collection index:', error);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Validate the structure of a collection index
|
|
164
|
+
*/
|
|
165
|
+
validateIndexStructure(index) {
|
|
166
|
+
return (index &&
|
|
167
|
+
typeof index === 'object' &&
|
|
168
|
+
typeof index.version === 'string' &&
|
|
169
|
+
typeof index.generated === 'string' &&
|
|
170
|
+
typeof index.total_elements === 'number' &&
|
|
171
|
+
index.index &&
|
|
172
|
+
typeof index.index === 'object' &&
|
|
173
|
+
index.metadata &&
|
|
174
|
+
typeof index.metadata === 'object');
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Save cache to persistent storage
|
|
178
|
+
*/
|
|
179
|
+
async saveToDisk(cache) {
|
|
180
|
+
try {
|
|
181
|
+
await this.ensureCacheDir();
|
|
182
|
+
const cacheData = {
|
|
183
|
+
...cache,
|
|
184
|
+
fetchedAt: cache.fetchedAt.toISOString() // Serialize date
|
|
185
|
+
};
|
|
186
|
+
const data = JSON.stringify(cacheData, null, 2);
|
|
187
|
+
await fs.writeFile(this.cacheFile, data, 'utf8');
|
|
188
|
+
logger.debug('Collection index cache saved to disk');
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
logger.debug('Failed to save collection index cache:', error);
|
|
192
|
+
// Don't throw - cache persistence failures shouldn't break functionality
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Load cache from persistent storage
|
|
197
|
+
*/
|
|
198
|
+
async loadFromDisk() {
|
|
199
|
+
try {
|
|
200
|
+
// Basic security check for path traversal
|
|
201
|
+
if (this.cacheFile.includes('..') || this.cacheFile.includes('\0')) {
|
|
202
|
+
SecurityMonitor.logSecurityEvent({
|
|
203
|
+
type: 'PATH_TRAVERSAL_ATTEMPT',
|
|
204
|
+
severity: 'HIGH',
|
|
205
|
+
source: 'CollectionIndexCache.loadFromDisk',
|
|
206
|
+
details: `Potential path traversal attempt detected: ${this.cacheFile.substring(0, 100)}`
|
|
207
|
+
});
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
const data = await fs.readFile(this.cacheFile, 'utf8');
|
|
211
|
+
const cacheData = JSON.parse(data);
|
|
212
|
+
return {
|
|
213
|
+
...cacheData,
|
|
214
|
+
fetchedAt: new Date(cacheData.fetchedAt) // Deserialize date
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (error.code !== 'ENOENT') {
|
|
219
|
+
logger.debug('Failed to load collection index cache from disk:', error);
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Ensure cache directory exists
|
|
226
|
+
*/
|
|
227
|
+
async ensureCacheDir() {
|
|
228
|
+
try {
|
|
229
|
+
await fs.mkdir(this.cacheDir, { recursive: true });
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
logger.error('Failed to create cache directory:', error);
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Clear all cache data with performance monitoring
|
|
238
|
+
*/
|
|
239
|
+
async clearCache() {
|
|
240
|
+
const startTime = Date.now();
|
|
241
|
+
this.cache = null;
|
|
242
|
+
this.memoryCache.clear();
|
|
243
|
+
// Cancel any ongoing fetch
|
|
244
|
+
this.fetchPromise = null;
|
|
245
|
+
try {
|
|
246
|
+
await fs.unlink(this.cacheFile);
|
|
247
|
+
logger.debug('Collection index cache cleared from disk');
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
if (error.code !== 'ENOENT') {
|
|
251
|
+
logger.debug('Failed to clear collection index cache:', error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
logger.debug('Collection cache cleared', {
|
|
255
|
+
duration: Date.now() - startTime,
|
|
256
|
+
memoryFreed: this.memoryCache.getMemoryUsageMB()
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get comprehensive cache statistics for debugging and monitoring
|
|
261
|
+
*/
|
|
262
|
+
getCacheStats() {
|
|
263
|
+
if (!this.cache) {
|
|
264
|
+
return {
|
|
265
|
+
isValid: false,
|
|
266
|
+
age: 0,
|
|
267
|
+
hasCache: false,
|
|
268
|
+
elements: 0,
|
|
269
|
+
memoryCache: this.memoryCache.getStats(),
|
|
270
|
+
performanceMetrics: null
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
const age = Date.now() - this.cache.fetchedAt.getTime();
|
|
274
|
+
return {
|
|
275
|
+
isValid: this.isValid(),
|
|
276
|
+
age,
|
|
277
|
+
hasCache: true,
|
|
278
|
+
elements: this.cache.data.total_elements,
|
|
279
|
+
memoryCache: this.memoryCache.getStats(),
|
|
280
|
+
performanceMetrics: {
|
|
281
|
+
cacheHitRate: this.calculateCacheHitRate(),
|
|
282
|
+
averageAccessTime: this.calculateAverageAccessTime()
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// =====================================================
|
|
287
|
+
// PRIVATE HELPER METHODS FOR PERFORMANCE
|
|
288
|
+
// =====================================================
|
|
289
|
+
/**
|
|
290
|
+
* Fetch fresh index with comprehensive fallback strategy
|
|
291
|
+
*/
|
|
292
|
+
async fetchFreshWithFallback() {
|
|
293
|
+
try {
|
|
294
|
+
// Try to fetch fresh index
|
|
295
|
+
const freshIndex = await this.fetchFresh();
|
|
296
|
+
if (freshIndex) {
|
|
297
|
+
logger.debug('Collection index fetched successfully');
|
|
298
|
+
return freshIndex;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
logger.warn('Fresh fetch failed, trying fallback', {
|
|
303
|
+
error: error instanceof Error ? error.message : String(error)
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
// Fall back to stale cache if available
|
|
307
|
+
if (this.cache?.data) {
|
|
308
|
+
logger.debug('Using stale cached collection index as fallback');
|
|
309
|
+
return this.cache.data;
|
|
310
|
+
}
|
|
311
|
+
throw new Error('No collection index available - fresh fetch failed and no cache exists');
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Record performance metrics for cache operations
|
|
315
|
+
*/
|
|
316
|
+
recordPerformanceMetrics(startTime, memoryBefore, operation) {
|
|
317
|
+
const duration = Date.now() - startTime;
|
|
318
|
+
const memoryAfter = process.memoryUsage().heapUsed;
|
|
319
|
+
logger.debug('Collection cache operation completed', {
|
|
320
|
+
operation,
|
|
321
|
+
duration,
|
|
322
|
+
memoryUsageMB: (memoryAfter - memoryBefore) / (1024 * 1024)
|
|
323
|
+
});
|
|
324
|
+
// Record cache performance metrics
|
|
325
|
+
this.performanceMonitor.recordCachePerformance('collectionIndex', {
|
|
326
|
+
hitRate: operation.includes('hit') ? 1 : 0,
|
|
327
|
+
avgHitTime: operation.includes('hit') ? duration : 0,
|
|
328
|
+
avgMissTime: operation.includes('hit') ? 0 : duration,
|
|
329
|
+
totalHits: operation.includes('hit') ? 1 : 0,
|
|
330
|
+
totalMisses: operation.includes('hit') ? 0 : 1,
|
|
331
|
+
evictions: 0
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Calculate cache hit rate (placeholder for future implementation)
|
|
336
|
+
*/
|
|
337
|
+
calculateCacheHitRate() {
|
|
338
|
+
// This would be implemented with actual metrics tracking
|
|
339
|
+
return this.memoryCache.getStats().hitRate;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Calculate average access time (placeholder for future implementation)
|
|
343
|
+
*/
|
|
344
|
+
calculateAverageAccessTime() {
|
|
345
|
+
// This would be implemented with actual timing metrics
|
|
346
|
+
return 5; // Placeholder value
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29sbGVjdGlvbkluZGV4Q2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2FjaGUvQ29sbGVjdGlvbkluZGV4Q2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsQyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFNUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2pFLE9BQU8sRUFBWSxZQUFZLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdkQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFcEUsTUFBTSxPQUFPLG9CQUFvQjtJQUN2QixLQUFLLEdBQXVCLElBQUksQ0FBQztJQUN4QixHQUFHLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyw2QkFBNkI7SUFDbkQsU0FBUyxHQUFHLDZGQUE2RixDQUFDO0lBQ25ILFFBQVEsQ0FBUztJQUNqQixTQUFTLENBQVM7SUFDbEIsWUFBWSxDQUFlO0lBQzNCLGtCQUFrQixDQUFxQjtJQUN2QyxXQUFXLENBQWdCO0lBQzNCLFlBQVksR0FBb0MsSUFBSSxDQUFDLENBQUMsNkJBQTZCO0lBRTNGLFlBQVksWUFBMEIsRUFBRSxVQUFrQixPQUFPLENBQUMsR0FBRyxFQUFFO1FBQ3JFLElBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzdELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLDZCQUE2QixDQUFDLENBQUM7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SyxxQkFBcUI7UUFDM0IseURBQXlEO1FBQ3pELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxPQUFPLENBQUM7SUFDN0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ssMEJBQTBCO1FBQ2hDLHVEQUF1RDtRQUN2RCxPQUFPLENBQUMsQ0FBQyxDQUFDLG9CQUFvQjtJQUNoQyxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNtYXJ0IGNhY2hlIGZvciBjb2xsZWN0aW9uIGluZGV4IHdpdGggY29uZGl0aW9uYWwgZmV0Y2hpbmcgYW5kIHBlcmZvcm1hbmNlIG9wdGltaXphdGlvblxuICovXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBDb2xsZWN0aW9uSW5kZXgsIENhY2hlZEluZGV4IH0gZnJvbSAnLi4vdHlwZXMvY29sbGVjdGlvbi5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgR2l0SHViQ2xpZW50IH0gZnJvbSAnLi4vY29sbGVjdGlvbi9HaXRIdWJDbGllbnQuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IExSVUNhY2hlLCBDYWNoZUZhY3RvcnkgfSBmcm9tICcuL0xSVUNhY2hlLmpzJztcbmltcG9ydCB7IFBlcmZvcm1hbmNlTW9uaXRvciB9IGZyb20gJy4uL3V0aWxzL1BlcmZvcm1hbmNlTW9uaXRvci5qcyc7XG5cbmV4cG9ydCBjbGFzcyBDb2xsZWN0aW9uSW5kZXhDYWNoZSB7XG4gIHByaXZhdGUgY2FjaGU6IENhY2hlZEluZGV4IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgcmVhZG9ubHkgVFRMID0gMTUgKiA2MCAqIDEwMDA7IC8vIDE1IG1pbnV0ZXMgaW4gbWlsbGlzZWNvbmRzXG4gIHByaXZhdGUgcmVhZG9ubHkgSU5ERVhfVVJMID0gJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9Eb2xsaG91c2VNQ1AvY29sbGVjdGlvbi9tYWluL3B1YmxpYy9jb2xsZWN0aW9uLWluZGV4Lmpzb24nO1xuICBwcml2YXRlIGNhY2hlRGlyOiBzdHJpbmc7XG4gIHByaXZhdGUgY2FjaGVGaWxlOiBzdHJpbmc7XG4gIHByaXZhdGUgZ2l0aHViQ2xpZW50OiBHaXRIdWJDbGllbnQ7XG4gIHByaXZhdGUgcGVyZm9ybWFuY2VNb25pdG9yOiBQZXJmb3JtYW5jZU1vbml0b3I7XG4gIHByaXZhdGUgbWVtb3J5Q2FjaGU6IExSVUNhY2hlPGFueT47XG4gIHByaXZhdGUgZmV0Y2hQcm9taXNlOiBQcm9taXNlPENvbGxlY3Rpb25JbmRleD4gfCBudWxsID0gbnVsbDsgLy8gUHJldmVudCBjb25jdXJyZW50IGZldGNoZXNcbiAgXG4gIGNvbnN0cnVjdG9yKGdpdGh1YkNsaWVudDogR2l0SHViQ2xpZW50LCBiYXNlRGlyOiBzdHJpbmcgPSBwcm9jZXNzLmN3ZCgpKSB7XG4gICAgdGhpcy5naXRodWJDbGllbnQgPSBnaXRodWJDbGllbnQ7XG4gICAgdGhpcy5jYWNoZURpciA9IHBhdGguam9pbihiYXNlRGlyLCAnLmRvbGxob3VzZW1jcCcsICdjYWNoZScpO1xuICAgIHRoaXMuY2FjaGVGaWxlID0gcGF0aC5qb2luKHRoaXMuY2FjaGVEaXIsICdjb2xsZWN0aW9uLWluZGV4LWNhY2hlLmpzb24nKTtcbiAgICB0aGlzLnBlcmZvcm1hbmNlTW9uaXRvciA9IFBlcmZvcm1hbmNlTW9uaXRvci5nZXRJbnN0YW5jZSgpO1xuICAgIFxuICAgIC8vIEluaXRpYWxpemUgbWVtb3J5IGNhY2hlIGZvciBmcmVxdWVudGx5IGFjY2Vzc2VkIGRhdGFcbiAgICB0aGlzLm1lbW9yeUNhY2hlID0gQ2FjaGVGYWN0b3J5LmNyZWF0ZUFQSUNhY2hlKHtcbiAgICAgIG1heFNpemU6IDUwLFxuICAgICAgbWF4TWVtb3J5TUI6IDEwLFxuICAgICAgdHRsTXM6IDUgKiA2MCAqIDEwMDAsIC8vIDUgbWludXRlc1xuICAgICAgb25FdmljdGlvbjogKGtleSwgdmFsdWUpID0+IHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIG1lbW9yeSBjYWNoZSBldmljdGlvbicsIHsga2V5IH0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IHRoZSBjb2xsZWN0aW9uIGluZGV4IHdpdGggcGVyZm9ybWFuY2Ugb3B0aW1pemF0aW9uIGFuZCBsYXp5IGxvYWRpbmdcbiAgICovXG4gIGFzeW5jIGdldEluZGV4KGxhenlMb2FkOiBib29sZWFuID0gZmFsc2UpOiBQcm9taXNlPENvbGxlY3Rpb25JbmRleD4ge1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgY29uc3QgbWVtb3J5QmVmb3JlID0gcHJvY2Vzcy5tZW1vcnlVc2FnZSgpLmhlYXBVc2VkO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayBtZW1vcnkgY2FjaGUgZmlyc3QgZm9yIGZhc3Rlc3QgYWNjZXNzXG4gICAgICBjb25zdCBtZW1vcnlDYWNoZWQgPSB0aGlzLm1lbW9yeUNhY2hlLmdldCgnY29sbGVjdGlvbi1pbmRleCcpO1xuICAgICAgaWYgKG1lbW9yeUNhY2hlZCAmJiB0aGlzLmlzVmFsaWQoKSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ1VzaW5nIG1lbW9yeSBjYWNoZWQgY29sbGVjdGlvbiBpbmRleCcpO1xuICAgICAgICB0aGlzLnJlY29yZFBlcmZvcm1hbmNlTWV0cmljcyhzdGFydFRpbWUsIG1lbW9yeUJlZm9yZSwgJ21lbW9yeS1oaXQnKTtcbiAgICAgICAgcmV0dXJuIG1lbW9yeUNhY2hlZDtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQ2hlY2sgaWYgd2UgaGF2ZSB2YWxpZCBkaXNrIGNhY2hlZCBkYXRhXG4gICAgICBpZiAodGhpcy5pc1ZhbGlkKCkpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdVc2luZyB2YWxpZCBkaXNrIGNhY2hlZCBjb2xsZWN0aW9uIGluZGV4Jyk7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuY2FjaGUhLmRhdGE7XG4gICAgICAgIHRoaXMubWVtb3J5Q2FjaGUuc2V0KCdjb2xsZWN0aW9uLWluZGV4JywgcmVzdWx0KTtcbiAgICAgICAgdGhpcy5yZWNvcmRQZXJmb3JtYW5jZU1ldHJpY3Moc3RhcnRUaW1lLCBtZW1vcnlCZWZvcmUsICdkaXNrLWhpdCcpO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBQcmV2ZW50IGNvbmN1cnJlbnQgZmV0Y2hlc1xuICAgICAgaWYgKHRoaXMuZmV0Y2hQcm9taXNlKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnV2FpdGluZyBmb3Igb25nb2luZyBjb2xsZWN0aW9uIGluZGV4IGZldGNoJyk7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmZldGNoUHJvbWlzZTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gTGF6eSBsb2FkaW5nOiBPbmx5IGZldGNoIGlmIG5vdCBpbiBsYXp5IG1vZGUgb3IgYWJzb2x1dGVseSBuZWNlc3NhcnlcbiAgICAgIGlmIChsYXp5TG9hZCAmJiB0aGlzLmNhY2hlPy5kYXRhKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnVXNpbmcgc3RhbGUgY2FjaGUgaW4gbGF6eSBsb2FkIG1vZGUnKTtcbiAgICAgICAgdGhpcy5yZWNvcmRQZXJmb3JtYW5jZU1ldHJpY3Moc3RhcnRUaW1lLCBtZW1vcnlCZWZvcmUsICdsYXp5LXN0YWxlJyk7XG4gICAgICAgIHJldHVybiB0aGlzLmNhY2hlLmRhdGE7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIENyZWF0ZSBmZXRjaCBwcm9taXNlIHRvIHByZXZlbnQgY29uY3VycmVudCByZXF1ZXN0c1xuICAgICAgdGhpcy5mZXRjaFByb21pc2UgPSB0aGlzLmZldGNoRnJlc2hXaXRoRmFsbGJhY2soKTtcbiAgICAgIFxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5mZXRjaFByb21pc2U7XG4gICAgICAgIHRoaXMubWVtb3J5Q2FjaGUuc2V0KCdjb2xsZWN0aW9uLWluZGV4JywgcmVzdWx0KTtcbiAgICAgICAgdGhpcy5yZWNvcmRQZXJmb3JtYW5jZU1ldHJpY3Moc3RhcnRUaW1lLCBtZW1vcnlCZWZvcmUsICdmcmVzaC1mZXRjaCcpO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgdGhpcy5mZXRjaFByb21pc2UgPSBudWxsO1xuICAgICAgfVxuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGdldCBjb2xsZWN0aW9uIGluZGV4OicsIGVycm9yKTtcbiAgICAgIFxuICAgICAgLy8gVHJ5IGxvYWRpbmcgZnJvbSBwZXJzaXN0ZW50IGNhY2hlIGFzIGxhc3QgcmVzb3J0XG4gICAgICBjb25zdCBwZXJzaXN0ZW50Q2FjaGUgPSBhd2FpdCB0aGlzLmxvYWRGcm9tRGlzaygpO1xuICAgICAgaWYgKHBlcnNpc3RlbnRDYWNoZSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ1VzaW5nIHBlcnNpc3RlbnQgY2FjaGUgYXMgbGFzdCByZXNvcnQnKTtcbiAgICAgICAgdGhpcy5jYWNoZSA9IHBlcnNpc3RlbnRDYWNoZTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gcGVyc2lzdGVudENhY2hlLmRhdGE7XG4gICAgICAgIHRoaXMubWVtb3J5Q2FjaGUuc2V0KCdjb2xsZWN0aW9uLWluZGV4JywgcmVzdWx0KTtcbiAgICAgICAgdGhpcy5yZWNvcmRQZXJmb3JtYW5jZU1ldHJpY3Moc3RhcnRUaW1lLCBtZW1vcnlCZWZvcmUsICdkaXNrLWZhbGxiYWNrJyk7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIGN1cnJlbnQgY2FjaGUgaXMgdmFsaWQgKHdpdGhpbiBUVEwpXG4gICAqL1xuICBwcml2YXRlIGlzVmFsaWQoKTogYm9vbGVhbiB7XG4gICAgaWYgKCF0aGlzLmNhY2hlKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IGFnZSA9IERhdGUubm93KCkgLSB0aGlzLmNhY2hlLmZldGNoZWRBdC5nZXRUaW1lKCk7XG4gICAgcmV0dXJuIGFnZSA8IHRoaXMuVFRMO1xuICB9XG4gIFxuICAvKipcbiAgICogRmV0Y2ggZnJlc2ggaW5kZXggZnJvbSBHaXRIdWIgd2l0aCBjb25kaXRpb25hbCByZXF1ZXN0c1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBmZXRjaEZyZXNoKCk6IFByb21pc2U8Q29sbGVjdGlvbkluZGV4IHwgbnVsbD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBCdWlsZCBoZWFkZXJzIGZvciBjb25kaXRpb25hbCByZXF1ZXN0XG4gICAgICBjb25zdCBoZWFkZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICAgICAnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICAnVXNlci1BZ2VudCc6ICdEb2xsaG91c2VNQ1AvMS4wJ1xuICAgICAgfTtcbiAgICAgIFxuICAgICAgLy8gQWRkIGNvbmRpdGlvbmFsIGhlYWRlcnMgaWYgd2UgaGF2ZSBjYWNoZVxuICAgICAgaWYgKHRoaXMuY2FjaGU/LmV0YWcpIHtcbiAgICAgICAgaGVhZGVyc1snSWYtTm9uZS1NYXRjaCddID0gdGhpcy5jYWNoZS5ldGFnO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuY2FjaGU/Lmxhc3RNb2RpZmllZCkge1xuICAgICAgICBoZWFkZXJzWydJZi1Nb2RpZmllZC1TaW5jZSddID0gdGhpcy5jYWNoZS5sYXN0TW9kaWZpZWQ7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFVzZSBmZXRjaCBkaXJlY3RseSBmb3IgYmV0dGVyIGNvbnRyb2wgb3ZlciBjb25kaXRpb25hbCByZXF1ZXN0c1xuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGlzLklOREVYX1VSTCwgeyBoZWFkZXJzIH0pO1xuICAgICAgXG4gICAgICAvLyAzMDQgTm90IE1vZGlmaWVkIC0gdXNlIGNhY2hlZCBkYXRhXG4gICAgICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSAzMDQpIHtcbiAgICAgICAgaWYgKHRoaXMuY2FjaGUpIHtcbiAgICAgICAgICAvLyBVcGRhdGUgdGltZXN0YW1wIHRvIGV4dGVuZCBjYWNoZSB2YWxpZGl0eVxuICAgICAgICAgIHRoaXMuY2FjaGUuZmV0Y2hlZEF0ID0gbmV3IERhdGUoKTtcbiAgICAgICAgICBhd2FpdCB0aGlzLnNhdmVUb0Rpc2sodGhpcy5jYWNoZSk7XG4gICAgICAgICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIGluZGV4IG5vdCBtb2RpZmllZCAtIHJlZnJlc2hlZCBjYWNoZSB0aW1lc3RhbXAnKTtcbiAgICAgICAgICByZXR1cm4gdGhpcy5jYWNoZS5kYXRhO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBcbiAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBIVFRQICR7cmVzcG9uc2Uuc3RhdHVzfTogJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgICAgfVxuICAgICAgXG4gICAgICBjb25zdCBpbmRleERhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCkgYXMgQ29sbGVjdGlvbkluZGV4O1xuICAgICAgXG4gICAgICAvLyBWYWxpZGF0ZSB0aGUgaW5kZXggc3RydWN0dXJlXG4gICAgICBpZiAoIXRoaXMudmFsaWRhdGVJbmRleFN0cnVjdHVyZShpbmRleERhdGEpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBjb2xsZWN0aW9uIGluZGV4IHN0cnVjdHVyZSByZWNlaXZlZCcpO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBDcmVhdGUgbmV3IGNhY2hlIGVudHJ5XG4gICAgICBjb25zdCBuZXdDYWNoZTogQ2FjaGVkSW5kZXggPSB7XG4gICAgICAgIGRhdGE6IGluZGV4RGF0YSxcbiAgICAgICAgZmV0Y2hlZEF0OiBuZXcgRGF0ZSgpLFxuICAgICAgICBldGFnOiByZXNwb25zZS5oZWFkZXJzLmdldCgnZXRhZycpIHx8IHVuZGVmaW5lZCxcbiAgICAgICAgbGFzdE1vZGlmaWVkOiByZXNwb25zZS5oZWFkZXJzLmdldCgnbGFzdC1tb2RpZmllZCcpIHx8IHVuZGVmaW5lZFxuICAgICAgfTtcbiAgICAgIFxuICAgICAgdGhpcy5jYWNoZSA9IG5ld0NhY2hlO1xuICAgICAgXG4gICAgICAvLyBTYXZlIHRvIHBlcnNpc3RlbnQgY2FjaGUgaW4gYmFja2dyb3VuZFxuICAgICAgdGhpcy5zYXZlVG9EaXNrKG5ld0NhY2hlKS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnRmFpbGVkIHRvIHNhdmUgaW5kZXggY2FjaGUgdG8gZGlzazonLCBlcnJvcik7XG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgbG9nZ2VyLmRlYnVnKGBGcmVzaCBjb2xsZWN0aW9uIGluZGV4IGZldGNoZWQgd2l0aCAke2luZGV4RGF0YS50b3RhbF9lbGVtZW50c30gZWxlbWVudHNgKTtcbiAgICAgIHJldHVybiBpbmRleERhdGE7XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdGYWlsZWQgdG8gZmV0Y2ggZnJlc2ggY29sbGVjdGlvbiBpbmRleDonLCBlcnJvcik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSB0aGUgc3RydWN0dXJlIG9mIGEgY29sbGVjdGlvbiBpbmRleFxuICAgKi9cbiAgcHJpdmF0ZSB2YWxpZGF0ZUluZGV4U3RydWN0dXJlKGluZGV4OiBhbnkpOiBpbmRleCBpcyBDb2xsZWN0aW9uSW5kZXgge1xuICAgIHJldHVybiAoXG4gICAgICBpbmRleCAmJlxuICAgICAgdHlwZW9mIGluZGV4ID09PSAnb2JqZWN0JyAmJlxuICAgICAgdHlwZW9mIGluZGV4LnZlcnNpb24gPT09ICdzdHJpbmcnICYmXG4gICAgICB0eXBlb2YgaW5kZXguZ2VuZXJhdGVkID09PSAnc3RyaW5nJyAmJlxuICAgICAgdHlwZW9mIGluZGV4LnRvdGFsX2VsZW1lbnRzID09PSAnbnVtYmVyJyAmJlxuICAgICAgaW5kZXguaW5kZXggJiZcbiAgICAgIHR5cGVvZiBpbmRleC5pbmRleCA9PT0gJ29iamVjdCcgJiZcbiAgICAgIGluZGV4Lm1ldGFkYXRhICYmXG4gICAgICB0eXBlb2YgaW5kZXgubWV0YWRhdGEgPT09ICdvYmplY3QnXG4gICAgKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFNhdmUgY2FjaGUgdG8gcGVyc2lzdGVudCBzdG9yYWdlXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHNhdmVUb0Rpc2soY2FjaGU6IENhY2hlZEluZGV4KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuZW5zdXJlQ2FjaGVEaXIoKTtcbiAgICAgIFxuICAgICAgY29uc3QgY2FjaGVEYXRhID0ge1xuICAgICAgICAuLi5jYWNoZSxcbiAgICAgICAgZmV0Y2hlZEF0OiBjYWNoZS5mZXRjaGVkQXQudG9JU09TdHJpbmcoKSAvLyBTZXJpYWxpemUgZGF0ZVxuICAgICAgfTtcbiAgICAgIFxuICAgICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGNhY2hlRGF0YSwgbnVsbCwgMik7XG4gICAgICBhd2FpdCBmcy53cml0ZUZpbGUodGhpcy5jYWNoZUZpbGUsIGRhdGEsICd1dGY4Jyk7XG4gICAgICBcbiAgICAgIGxvZ2dlci5kZWJ1ZygnQ29sbGVjdGlvbiBpbmRleCBjYWNoZSBzYXZlZCB0byBkaXNrJyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnRmFpbGVkIHRvIHNhdmUgY29sbGVjdGlvbiBpbmRleCBjYWNoZTonLCBlcnJvcik7XG4gICAgICAvLyBEb24ndCB0aHJvdyAtIGNhY2hlIHBlcnNpc3RlbmNlIGZhaWx1cmVzIHNob3VsZG4ndCBicmVhayBmdW5jdGlvbmFsaXR5XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogTG9hZCBjYWNoZSBmcm9tIHBlcnNpc3RlbnQgc3RvcmFnZVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBsb2FkRnJvbURpc2soKTogUHJvbWlzZTxDYWNoZWRJbmRleCB8IG51bGw+IHtcbiAgICB0cnkge1xuICAgICAgLy8gQmFzaWMgc2VjdXJpdHkgY2hlY2sgZm9yIHBhdGggdHJhdmVyc2FsXG4gICAgICBpZiAodGhpcy5jYWNoZUZpbGUuaW5jbHVkZXMoJy4uJykgfHwgdGhpcy5jYWNoZUZpbGUuaW5jbHVkZXMoJ1xcMCcpKSB7XG4gICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICB0eXBlOiAnUEFUSF9UUkFWRVJTQUxfQVRURU1QVCcsXG4gICAgICAgICAgc2V2ZXJpdHk6ICdISUdIJyxcbiAgICAgICAgICBzb3VyY2U6ICdDb2xsZWN0aW9uSW5kZXhDYWNoZS5sb2FkRnJvbURpc2snLFxuICAgICAgICAgIGRldGFpbHM6IGBQb3RlbnRpYWwgcGF0aCB0cmF2ZXJzYWwgYXR0ZW1wdCBkZXRlY3RlZDogJHt0aGlzLmNhY2hlRmlsZS5zdWJzdHJpbmcoMCwgMTAwKX1gXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IGZzLnJlYWRGaWxlKHRoaXMuY2FjaGVGaWxlLCAndXRmOCcpO1xuICAgICAgY29uc3QgY2FjaGVEYXRhID0gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgIFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4uY2FjaGVEYXRhLFxuICAgICAgICBmZXRjaGVkQXQ6IG5ldyBEYXRlKGNhY2hlRGF0YS5mZXRjaGVkQXQpIC8vIERlc2VyaWFsaXplIGRhdGVcbiAgICAgIH07XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlICE9PSAnRU5PRU5UJykge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ZhaWxlZCB0byBsb2FkIGNvbGxlY3Rpb24gaW5kZXggY2FjaGUgZnJvbSBkaXNrOicsIGVycm9yKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEVuc3VyZSBjYWNoZSBkaXJlY3RvcnkgZXhpc3RzXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGVuc3VyZUNhY2hlRGlyKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy5ta2Rpcih0aGlzLmNhY2hlRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gY3JlYXRlIGNhY2hlIGRpcmVjdG9yeTonLCBlcnJvcik7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDbGVhciBhbGwgY2FjaGUgZGF0YSB3aXRoIHBlcmZvcm1hbmNlIG1vbml0b3JpbmdcbiAgICovXG4gIGFzeW5jIGNsZWFyQ2FjaGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBcbiAgICB0aGlzLmNhY2hlID0gbnVsbDtcbiAgICB0aGlzLm1lbW9yeUNhY2hlLmNsZWFyKCk7XG4gICAgXG4gICAgLy8gQ2FuY2VsIGFueSBvbmdvaW5nIGZldGNoXG4gICAgdGhpcy5mZXRjaFByb21pc2UgPSBudWxsO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy51bmxpbmsodGhpcy5jYWNoZUZpbGUpO1xuICAgICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIGluZGV4IGNhY2hlIGNsZWFyZWQgZnJvbSBkaXNrJyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlICE9PSAnRU5PRU5UJykge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ZhaWxlZCB0byBjbGVhciBjb2xsZWN0aW9uIGluZGV4IGNhY2hlOicsIGVycm9yKTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIGNhY2hlIGNsZWFyZWQnLCB7XG4gICAgICBkdXJhdGlvbjogRGF0ZS5ub3coKSAtIHN0YXJ0VGltZSxcbiAgICAgIG1lbW9yeUZyZWVkOiB0aGlzLm1lbW9yeUNhY2hlLmdldE1lbW9yeVVzYWdlTUIoKVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IGNvbXByZWhlbnNpdmUgY2FjaGUgc3RhdGlzdGljcyBmb3IgZGVidWdnaW5nIGFuZCBtb25pdG9yaW5nXG4gICAqL1xuICBnZXRDYWNoZVN0YXRzKCk6IHsgXG4gICAgaXNWYWxpZDogYm9vbGVhbjsgXG4gICAgYWdlOiBudW1iZXI7IFxuICAgIGhhc0NhY2hlOiBib29sZWFuOyBcbiAgICBlbGVtZW50czogbnVtYmVyO1xuICAgIG1lbW9yeUNhY2hlOiBhbnk7XG4gICAgcGVyZm9ybWFuY2VNZXRyaWNzOiBhbnk7XG4gIH0ge1xuICAgIGlmICghdGhpcy5jYWNoZSkge1xuICAgICAgcmV0dXJuIHsgXG4gICAgICAgIGlzVmFsaWQ6IGZhbHNlLCBcbiAgICAgICAgYWdlOiAwLCBcbiAgICAgICAgaGFzQ2FjaGU6IGZhbHNlLCBcbiAgICAgICAgZWxlbWVudHM6IDAsXG4gICAgICAgIG1lbW9yeUNhY2hlOiB0aGlzLm1lbW9yeUNhY2hlLmdldFN0YXRzKCksXG4gICAgICAgIHBlcmZvcm1hbmNlTWV0cmljczogbnVsbFxuICAgICAgfTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgYWdlID0gRGF0ZS5ub3coKSAtIHRoaXMuY2FjaGUuZmV0Y2hlZEF0LmdldFRpbWUoKTtcbiAgICByZXR1cm4ge1xuICAgICAgaXNWYWxpZDogdGhpcy5pc1ZhbGlkKCksXG4gICAgICBhZ2UsXG4gICAgICBoYXNDYWNoZTogdHJ1ZSxcbiAgICAgIGVsZW1lbnRzOiB0aGlzLmNhY2hlLmRhdGEudG90YWxfZWxlbWVudHMsXG4gICAgICBtZW1vcnlDYWNoZTogdGhpcy5tZW1vcnlDYWNoZS5nZXRTdGF0cygpLFxuICAgICAgcGVyZm9ybWFuY2VNZXRyaWNzOiB7XG4gICAgICAgIGNhY2hlSGl0UmF0ZTogdGhpcy5jYWxjdWxhdGVDYWNoZUhpdFJhdGUoKSxcbiAgICAgICAgYXZlcmFnZUFjY2Vzc1RpbWU6IHRoaXMuY2FsY3VsYXRlQXZlcmFnZUFjY2Vzc1RpbWUoKVxuICAgICAgfVxuICAgIH07XG4gIH1cbiAgXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIFBSSVZBVEUgSEVMUEVSIE1FVEhPRFMgRk9SIFBFUkZPUk1BTkNFXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIFxuICAvKipcbiAgICogRmV0Y2ggZnJlc2ggaW5kZXggd2l0aCBjb21wcmVoZW5zaXZlIGZhbGxiYWNrIHN0cmF0ZWd5XG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoRnJlc2hXaXRoRmFsbGJhY2soKTogUHJvbWlzZTxDb2xsZWN0aW9uSW5kZXg+IHtcbiAgICB0cnkge1xuICAgICAgLy8gVHJ5IHRvIGZldGNoIGZyZXNoIGluZGV4XG4gICAgICBjb25zdCBmcmVzaEluZGV4ID0gYXdhaXQgdGhpcy5mZXRjaEZyZXNoKCk7XG4gICAgICBpZiAoZnJlc2hJbmRleCkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gaW5kZXggZmV0Y2hlZCBzdWNjZXNzZnVsbHknKTtcbiAgICAgICAgcmV0dXJuIGZyZXNoSW5kZXg7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdGcmVzaCBmZXRjaCBmYWlsZWQsIHRyeWluZyBmYWxsYmFjaycsIHtcbiAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgfSk7XG4gICAgfVxuICAgIFxuICAgIC8vIEZhbGwgYmFjayB0byBzdGFsZSBjYWNoZSBpZiBhdmFpbGFibGVcbiAgICBpZiAodGhpcy5jYWNoZT8uZGF0YSkge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdVc2luZyBzdGFsZSBjYWNoZWQgY29sbGVjdGlvbiBpbmRleCBhcyBmYWxsYmFjaycpO1xuICAgICAgcmV0dXJuIHRoaXMuY2FjaGUuZGF0YTtcbiAgICB9XG4gICAgXG4gICAgdGhyb3cgbmV3IEVycm9yKCdObyBjb2xsZWN0aW9uIGluZGV4IGF2YWlsYWJsZSAtIGZyZXNoIGZldGNoIGZhaWxlZCBhbmQgbm8gY2FjaGUgZXhpc3RzJyk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZWNvcmQgcGVyZm9ybWFuY2UgbWV0cmljcyBmb3IgY2FjaGUgb3BlcmF0aW9uc1xuICAgKi9cbiAgcHJpdmF0ZSByZWNvcmRQZXJmb3JtYW5jZU1ldHJpY3Moc3RhcnRUaW1lOiBudW1iZXIsIG1lbW9yeUJlZm9yZTogbnVtYmVyLCBvcGVyYXRpb246IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGR1cmF0aW9uID0gRGF0ZS5ub3coKSAtIHN0YXJ0VGltZTtcbiAgICBjb25zdCBtZW1vcnlBZnRlciA9IHByb2Nlc3MubWVtb3J5VXNhZ2UoKS5oZWFwVXNlZDtcbiAgICBcbiAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gY2FjaGUgb3BlcmF0aW9uIGNvbXBsZXRlZCcsIHtcbiAgICAgIG9wZXJhdGlvbixcbiAgICAgIGR1cmF0aW9uLFxuICAgICAgbWVtb3J5VXNhZ2VNQjogKG1lbW9yeUFmdGVyIC0gbWVtb3J5QmVmb3JlKSAvICgxMDI0ICogMTAyNClcbiAgICB9KTtcbiAgICBcbiAgICAvLyBSZWNvcmQgY2FjaGUgcGVyZm9ybWFuY2UgbWV0cmljc1xuICAgIHRoaXMucGVyZm9ybWFuY2VNb25pdG9yLnJlY29yZENhY2hlUGVyZm9ybWFuY2UoJ2NvbGxlY3Rpb25JbmRleCcsIHtcbiAgICAgIGhpdFJhdGU6IG9wZXJhdGlvbi5pbmNsdWRlcygnaGl0JykgPyAxIDogMCxcbiAgICAgIGF2Z0hpdFRpbWU6IG9wZXJhdGlvbi5pbmNsdWRlcygnaGl0JykgPyBkdXJhdGlvbiA6IDAsXG4gICAgICBhdmdNaXNzVGltZTogb3BlcmF0aW9uLmluY2x1ZGVzKCdoaXQnKSA/IDAgOiBkdXJhdGlvbixcbiAgICAgIHRvdGFsSGl0czogb3BlcmF0aW9uLmluY2x1ZGVzKCdoaXQnKSA/IDEgOiAwLFxuICAgICAgdG90YWxNaXNzZXM6IG9wZXJhdGlvbi5pbmNsdWRlcygnaGl0JykgPyAwIDogMSxcbiAgICAgIGV2aWN0aW9uczogMFxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2FsY3VsYXRlIGNhY2hlIGhpdCByYXRlIChwbGFjZWhvbGRlciBmb3IgZnV0dXJlIGltcGxlbWVudGF0aW9uKVxuICAgKi9cbiAgcHJpdmF0ZSBjYWxjdWxhdGVDYWNoZUhpdFJhdGUoKTogbnVtYmVyIHtcbiAgICAvLyBUaGlzIHdvdWxkIGJlIGltcGxlbWVudGVkIHdpdGggYWN0dWFsIG1ldHJpY3MgdHJhY2tpbmdcbiAgICByZXR1cm4gdGhpcy5tZW1vcnlDYWNoZS5nZXRTdGF0cygpLmhpdFJhdGU7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgYXZlcmFnZSBhY2Nlc3MgdGltZSAocGxhY2Vob2xkZXIgZm9yIGZ1dHVyZSBpbXBsZW1lbnRhdGlvbilcbiAgICovXG4gIHByaXZhdGUgY2FsY3VsYXRlQXZlcmFnZUFjY2Vzc1RpbWUoKTogbnVtYmVyIHtcbiAgICAvLyBUaGlzIHdvdWxkIGJlIGltcGxlbWVudGVkIHdpdGggYWN0dWFsIHRpbWluZyBtZXRyaWNzXG4gICAgcmV0dXJuIDU7IC8vIFBsYWNlaG9sZGVyIHZhbHVlXG4gIH1cbn0iXX0=
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-performance LRU Cache with memory monitoring and automatic cleanup
|
|
3
|
+
* Optimized for large-scale indexing operations with configurable memory limits
|
|
4
|
+
*/
|
|
5
|
+
export interface LRUCacheOptions {
|
|
6
|
+
maxSize: number;
|
|
7
|
+
maxMemoryMB?: number;
|
|
8
|
+
ttlMs?: number;
|
|
9
|
+
onEviction?: (key: string, value: any) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface CacheStats {
|
|
12
|
+
size: number;
|
|
13
|
+
maxSize: number;
|
|
14
|
+
hitCount: number;
|
|
15
|
+
missCount: number;
|
|
16
|
+
evictionCount: number;
|
|
17
|
+
memoryUsageMB: number;
|
|
18
|
+
hitRate: number;
|
|
19
|
+
}
|
|
20
|
+
export declare class LRUCache<T> {
|
|
21
|
+
private readonly maxSize;
|
|
22
|
+
private readonly maxMemoryBytes;
|
|
23
|
+
private readonly ttlMs;
|
|
24
|
+
private readonly onEviction?;
|
|
25
|
+
private cache;
|
|
26
|
+
private head;
|
|
27
|
+
private tail;
|
|
28
|
+
private currentMemoryBytes;
|
|
29
|
+
private hitCount;
|
|
30
|
+
private missCount;
|
|
31
|
+
private evictionCount;
|
|
32
|
+
constructor(options: LRUCacheOptions);
|
|
33
|
+
/**
|
|
34
|
+
* Get value from cache with automatic cleanup
|
|
35
|
+
*/
|
|
36
|
+
get(key: string): T | null;
|
|
37
|
+
/**
|
|
38
|
+
* Set value in cache with automatic eviction
|
|
39
|
+
*/
|
|
40
|
+
set(key: string, value: T): void;
|
|
41
|
+
/**
|
|
42
|
+
* Delete specific key from cache
|
|
43
|
+
*/
|
|
44
|
+
delete(key: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check if key exists in cache
|
|
47
|
+
*/
|
|
48
|
+
has(key: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Clear all entries from cache
|
|
51
|
+
*/
|
|
52
|
+
clear(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get cache statistics
|
|
55
|
+
*/
|
|
56
|
+
getStats(): CacheStats;
|
|
57
|
+
/**
|
|
58
|
+
* Get all keys in access order (most recent first)
|
|
59
|
+
*/
|
|
60
|
+
keys(): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Get current memory usage in MB
|
|
63
|
+
*/
|
|
64
|
+
getMemoryUsageMB(): number;
|
|
65
|
+
/**
|
|
66
|
+
* Manually trigger cleanup of expired entries
|
|
67
|
+
*/
|
|
68
|
+
cleanup(): number;
|
|
69
|
+
private moveToFront;
|
|
70
|
+
private addToFront;
|
|
71
|
+
private removeNode;
|
|
72
|
+
private evictIfNecessary;
|
|
73
|
+
private evictLeastRecentlyUsed;
|
|
74
|
+
private estimateSize;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Factory for creating optimized LRU caches for different use cases
|
|
78
|
+
*/
|
|
79
|
+
export declare class CacheFactory {
|
|
80
|
+
/**
|
|
81
|
+
* Create cache optimized for search results
|
|
82
|
+
*/
|
|
83
|
+
static createSearchResultCache<T>(options?: Partial<LRUCacheOptions>): LRUCache<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Create cache optimized for index data
|
|
86
|
+
*/
|
|
87
|
+
static createIndexCache<T>(options?: Partial<LRUCacheOptions>): LRUCache<T>;
|
|
88
|
+
/**
|
|
89
|
+
* Create cache optimized for API responses
|
|
90
|
+
*/
|
|
91
|
+
static createAPICache<T>(options?: Partial<LRUCacheOptions>): LRUCache<T>;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=LRUCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LRUCache.d.ts","sourceRoot":"","sources":["../../src/cache/LRUCache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAWD,qBAAa,QAAQ,CAAC,CAAC;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAkC;IAE9D,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,kBAAkB,CAAK;IAG/B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAK;gBAEd,OAAO,EAAE,eAAe;IAOpC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAqB1B;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAgChC;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAiB5B;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAezB;;OAEG;IACH,KAAK,IAAI,IAAI;IAcb;;OAEG;IACH,QAAQ,IAAI,UAAU;IAYtB;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;IAYhB;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAI1B;;OAEG;IACH,OAAO,IAAI,MAAM;IAuBjB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,sBAAsB;IAgB9B,OAAO,CAAC,YAAY;CA8BrB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IASlF;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAS3E;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;CAQ1E"}
|