@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
183 lines • 23.2 kB
JavaScript
/**
* Persistent cache for collection data to support offline/anonymous browsing
*/
import * as path from 'path';
import { logger } from '../utils/logger.js';
import { SecurityMonitor } from '../security/securityMonitor.js';
/**
* Persistent cache for collection data that supports offline browsing
*/
export class CollectionCache {
cacheDir;
cacheFile;
CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours for collection cache
// File operations service for secure file I/O
fileOperations;
constructor(fileOperations, baseDir) {
// Initialize file operations service
this.fileOperations = fileOperations;
// Use environment variable if set, otherwise fall back to parameter or default
const envCacheDir = process.env.DOLLHOUSE_CACHE_DIR;
if (envCacheDir) {
this.cacheDir = envCacheDir;
logger.debug(`CollectionCache: Using environment cache directory: ${this.cacheDir}`);
}
else {
const defaultBaseDir = baseDir || process.cwd();
this.cacheDir = path.join(defaultBaseDir, '.dollhousemcp', 'cache');
logger.debug(`CollectionCache: Using default cache directory: ${this.cacheDir}`);
}
this.cacheFile = path.join(this.cacheDir, 'collection-cache.json');
}
/**
* Initialize cache directory
*/
async ensureCacheDir() {
try {
await this.fileOperations.createDirectory(this.cacheDir);
}
catch (error) {
logger.error(`Failed to create cache directory: ${error}`);
throw error;
}
}
/**
* Load collection data from persistent cache
*/
async loadCache() {
try {
// Validate cache file path (basic security check)
if (this.cacheFile.includes('..') || this.cacheFile.includes('\0')) {
// SECURITY FIX: Add audit logging for path traversal attempt detection
SecurityMonitor.logSecurityEvent({
type: 'PATH_TRAVERSAL_ATTEMPT',
severity: 'HIGH',
source: 'CollectionCache.loadCache',
details: `Potential path traversal attempt detected in cache file path: ${this.cacheFile.substring(0, 100)}`
});
logger.warn('Invalid cache file path, skipping cache load');
return null;
}
const data = await this.fileOperations.readFile(this.cacheFile, {
source: 'CollectionCache.loadCache',
maxSize: 50 * 1024 * 1024 // 50MB for collection cache
});
const cache = JSON.parse(data);
// Check if cache is expired
if (Date.now() - cache.timestamp > this.CACHE_TTL_MS) {
logger.debug('Collection cache expired, will refresh from GitHub');
return null;
}
logger.debug(`Loaded ${cache.items.length} items from collection cache`);
return cache;
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.debug(`Failed to load collection cache: ${error}`);
}
return null;
}
}
/**
* Save collection data to persistent cache
*/
async saveCache(items, etag) {
try {
await this.ensureCacheDir();
const cacheEntry = {
items,
timestamp: Date.now(),
etag
};
const data = JSON.stringify(cacheEntry, null, 2);
await this.fileOperations.writeFile(this.cacheFile, data, {
source: 'CollectionCache.saveCache',
maxSize: 50 * 1024 * 1024 // 50MB for collection cache
});
logger.debug(`Saved ${items.length} items to collection cache`);
// SECURITY FIX: Add audit logging for cache write operations
logger.debug('Security audit: Cache write operation completed successfully');
// Log operation completed successfully
logger.debug(`Cache file operation completed with ${items.length} items`);
}
catch (error) {
logger.error(`Failed to save collection cache: ${error}`);
// Don't throw - caching failures shouldn't break functionality
}
}
/**
* Search cached collection items with fuzzy matching
*/
async searchCache(query) {
const cache = await this.loadCache();
if (!cache) {
return [];
}
const normalizedQuery = this.normalizeSearchTerm(query);
return cache.items.filter(item => {
// Search in filename and path with normalization
const normalizedName = this.normalizeSearchTerm(item.name);
const normalizedPath = this.normalizeSearchTerm(item.path);
return normalizedName.includes(normalizedQuery) ||
normalizedPath.includes(normalizedQuery) ||
(item.content && this.normalizeSearchTerm(item.content).includes(normalizedQuery));
});
}
/**
* Normalize search terms for better matching (handles spaces, dashes, etc.)
*/
normalizeSearchTerm(term) {
return term.toLowerCase()
.replaceAll(/[-_\s]+/g, ' ') // Convert dashes, underscores to spaces
.replace(/\.md$/, '') // Remove .md extension
.trim();
}
/**
* Get cached collection items by type/path
*/
async getItemsByPath(pathPrefix) {
const cache = await this.loadCache();
if (!cache) {
return [];
}
return cache.items.filter(item => item.path.startsWith(pathPrefix));
}
/**
* Check if cache exists and is valid
*/
async isCacheValid() {
const cache = await this.loadCache();
return cache !== null;
}
/**
* Clear the cache
*/
async clearCache() {
try {
await this.fileOperations.deleteFile(this.cacheFile, undefined, {
source: 'CollectionCache.clearCache'
});
logger.debug('Collection cache cleared');
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.debug(`Failed to clear collection cache: ${error}`);
}
}
}
/**
* Get cache stats for debugging
*/
async getCacheStats() {
const cache = await this.loadCache();
if (!cache) {
return { itemCount: 0, cacheAge: 0, isValid: false };
}
return {
itemCount: cache.items.length,
cacheAge: Date.now() - cache.timestamp,
isValid: Date.now() - cache.timestamp <= this.CACHE_TTL_MS
};
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29sbGVjdGlvbkNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NhY2hlL0NvbGxlY3Rpb25DYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFpQmpFOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGVBQWU7SUFDbEIsUUFBUSxDQUFTO0lBQ2pCLFNBQVMsQ0FBUztJQUNULFlBQVksR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyxnQ0FBZ0M7SUFFckYsOENBQThDO0lBQzdCLGNBQWMsQ0FBeUI7SUFFeEQsWUFBWSxjQUFzQyxFQUFFLE9BQWdCO1FBQ2xFLHFDQUFxQztRQUNyQyxJQUFJLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQztRQUVyQywrRUFBK0U7UUFDL0UsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQztRQUNwRCxJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ2hCLElBQUksQ0FBQyxRQUFRLEdBQUcsV0FBVyxDQUFDO1lBQzVCLE1BQU0sQ0FBQyxLQUFLLENBQUMsdURBQXVELElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZGLENBQUM7YUFBTSxDQUFDO1lBQ04sTUFBTSxjQUFjLEdBQUcsT0FBTyxJQUFJLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNoRCxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLGVBQWUsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNwRSxNQUFNLENBQUMsS0FBSyxDQUFDLG1EQUFtRCxJQUFJLENBQUMsUUFBUSxFQUFF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QyxDQUFDO1lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1FBQzNDLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSyxLQUFhLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUNyQyxNQUFNLENBQUMsS0FBSyxDQUFDLHFDQUFxQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQzdELENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLGFBQWE7UUFDakIsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDckMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsT0FBTyxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDdkQsQ0FBQztRQUVELE9BQU87WUFDTCxTQUFTLEVBQUUsS0FBSyxDQUFDLEtBQUssQ0FBQyxNQUFNO1lBQzdCLFFBQVEsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsS0FBSyxDQUFDLFNBQVM7WUFDdEMsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxZQUFZO1NBQzNELENBQUM7SUFDSixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBlcnNpc3RlbnQgY2FjaGUgZm9yIGNvbGxlY3Rpb24gZGF0YSB0byBzdXBwb3J0IG9mZmxpbmUvYW5vbnltb3VzIGJyb3dzaW5nXG4gKi9cblxuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgSUZpbGVPcGVyYXRpb25zU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL0ZpbGVPcGVyYXRpb25zU2VydmljZS5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29sbGVjdGlvbkl0ZW0ge1xuICBuYW1lOiBzdHJpbmc7XG4gIHBhdGg6IHN0cmluZztcbiAgc2hhOiBzdHJpbmc7XG4gIGNvbnRlbnQ/OiBzdHJpbmc7XG4gIGxhc3RfbW9kaWZpZWQ/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29sbGVjdGlvbkNhY2hlRW50cnkge1xuICBpdGVtczogQ29sbGVjdGlvbkl0ZW1bXTtcbiAgdGltZXN0YW1wOiBudW1iZXI7XG4gIGV0YWc/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogUGVyc2lzdGVudCBjYWNoZSBmb3IgY29sbGVjdGlvbiBkYXRhIHRoYXQgc3VwcG9ydHMgb2ZmbGluZSBicm93c2luZ1xuICovXG5leHBvcnQgY2xhc3MgQ29sbGVjdGlvbkNhY2hlIHtcbiAgcHJpdmF0ZSBjYWNoZURpcjogc3RyaW5nO1xuICBwcml2YXRlIGNhY2hlRmlsZTogc3RyaW5nO1xuICBwcml2YXRlIHJlYWRvbmx5IENBQ0hFX1RUTF9NUyA9IDI0ICogNjAgKiA2MCAqIDEwMDA7IC8vIDI0IGhvdXJzIGZvciBjb2xsZWN0aW9uIGNhY2hlXG5cbiAgLy8gRmlsZSBvcGVyYXRpb25zIHNlcnZpY2UgZm9yIHNlY3VyZSBmaWxlIEkvT1xuICBwcml2YXRlIHJlYWRvbmx5IGZpbGVPcGVyYXRpb25zOiBJRmlsZU9wZXJhdGlvbnNTZXJ2aWNlO1xuXG4gIGNvbnN0cnVjdG9yKGZpbGVPcGVyYXRpb25zOiBJRmlsZU9wZXJhdGlvbnNTZXJ2aWNlLCBiYXNlRGlyPzogc3RyaW5nKSB7XG4gICAgLy8gSW5pdGlhbGl6ZSBmaWxlIG9wZXJhdGlvbnMgc2VydmljZVxuICAgIHRoaXMuZmlsZU9wZXJhdGlvbnMgPSBmaWxlT3BlcmF0aW9ucztcblxuICAgIC8vIFVzZSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpZiBzZXQsIG90aGVyd2lzZSBmYWxsIGJhY2sgdG8gcGFyYW1ldGVyIG9yIGRlZmF1bHRcbiAgICBjb25zdCBlbnZDYWNoZURpciA9IHByb2Nlc3MuZW52LkRPTExIT1VTRV9DQUNIRV9ESVI7XG4gICAgaWYgKGVudkNhY2hlRGlyKSB7XG4gICAgICB0aGlzLmNhY2hlRGlyID0gZW52Q2FjaGVEaXI7XG4gICAgICBsb2dnZXIuZGVidWcoYENvbGxlY3Rpb25DYWNoZTogVXNpbmcgZW52aXJvbm1lbnQgY2FjaGUgZGlyZWN0b3J5OiAke3RoaXMuY2FjaGVEaXJ9YCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGRlZmF1bHRCYXNlRGlyID0gYmFzZURpciB8fCBwcm9jZXNzLmN3ZCgpO1xuICAgICAgdGhpcy5jYWNoZURpciA9IHBhdGguam9pbihkZWZhdWx0QmFzZURpciwgJy5kb2xsaG91c2VtY3AnLCAnY2FjaGUnKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgQ29sbGVjdGlvbkNhY2hlOiBVc2luZyBkZWZhdWx0IGNhY2hlIGRpcmVjdG9yeTogJHt0aGlzLmNhY2hlRGlyfWApO1xuICAgIH1cbiAgICB0aGlzLmNhY2hlRmlsZSA9IHBhdGguam9pbih0aGlzLmNhY2hlRGlyLCAnY29sbGVjdGlvbi1jYWNoZS5qc29uJyk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBJbml0aWFsaXplIGNhY2hlIGRpcmVjdG9yeVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVDYWNoZURpcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy5jcmVhdGVEaXJlY3RvcnkodGhpcy5jYWNoZURpcik7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcihgRmFpbGVkIHRvIGNyZWF0ZSBjYWNoZSBkaXJlY3Rvcnk6ICR7ZXJyb3J9YCk7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMb2FkIGNvbGxlY3Rpb24gZGF0YSBmcm9tIHBlcnNpc3RlbnQgY2FjaGVcbiAgICovXG4gIGFzeW5jIGxvYWRDYWNoZSgpOiBQcm9taXNlPENvbGxlY3Rpb25DYWNoZUVudHJ5IHwgbnVsbD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBWYWxpZGF0ZSBjYWNoZSBmaWxlIHBhdGggKGJhc2ljIHNlY3VyaXR5IGNoZWNrKVxuICAgICAgaWYgKHRoaXMuY2FjaGVGaWxlLmluY2x1ZGVzKCcuLicpIHx8IHRoaXMuY2FjaGVGaWxlLmluY2x1ZGVzKCdcXDAnKSkge1xuICAgICAgICAvLyBTRUNVUklUWSBGSVg6IEFkZCBhdWRpdCBsb2dnaW5nIGZvciBwYXRoIHRyYXZlcnNhbCBhdHRlbXB0IGRldGVjdGlvblxuICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgdHlwZTogJ1BBVEhfVFJBVkVSU0FMX0FUVEVNUFQnLFxuICAgICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgICAgc291cmNlOiAnQ29sbGVjdGlvbkNhY2hlLmxvYWRDYWNoZScsXG4gICAgICAgICAgZGV0YWlsczogYFBvdGVudGlhbCBwYXRoIHRyYXZlcnNhbCBhdHRlbXB0IGRldGVjdGVkIGluIGNhY2hlIGZpbGUgcGF0aDogJHt0aGlzLmNhY2hlRmlsZS5zdWJzdHJpbmcoMCwgMTAwKX1gXG4gICAgICAgIH0pO1xuICAgICAgICBsb2dnZXIud2FybignSW52YWxpZCBjYWNoZSBmaWxlIHBhdGgsIHNraXBwaW5nIGNhY2hlIGxvYWQnKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCB0aGlzLmZpbGVPcGVyYXRpb25zLnJlYWRGaWxlKHRoaXMuY2FjaGVGaWxlLCB7XG4gICAgICAgIHNvdXJjZTogJ0NvbGxlY3Rpb25DYWNoZS5sb2FkQ2FjaGUnLFxuICAgICAgICBtYXhTaXplOiA1MCAqIDEwMjQgKiAxMDI0IC8vIDUwTUIgZm9yIGNvbGxlY3Rpb24gY2FjaGVcbiAgICAgIH0pO1xuICAgICAgY29uc3QgY2FjaGU6IENvbGxlY3Rpb25DYWNoZUVudHJ5ID0gSlNPTi5wYXJzZShkYXRhKTtcblxuICAgICAgLy8gQ2hlY2sgaWYgY2FjaGUgaXMgZXhwaXJlZFxuICAgICAgaWYgKERhdGUubm93KCkgLSBjYWNoZS50aW1lc3RhbXAgPiB0aGlzLkNBQ0hFX1RUTF9NUykge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gY2FjaGUgZXhwaXJlZCwgd2lsbCByZWZyZXNoIGZyb20gR2l0SHViJyk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuXG4gICAgICBsb2dnZXIuZGVidWcoYExvYWRlZCAke2NhY2hlLml0ZW1zLmxlbmd0aH0gaXRlbXMgZnJvbSBjb2xsZWN0aW9uIGNhY2hlYCk7XG4gICAgICByZXR1cm4gY2FjaGU7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlICE9PSAnRU5PRU5UJykge1xuICAgICAgICBsb2dnZXIuZGVidWcoYEZhaWxlZCB0byBsb2FkIGNvbGxlY3Rpb24gY2FjaGU6ICR7ZXJyb3J9YCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBTYXZlIGNvbGxlY3Rpb24gZGF0YSB0byBwZXJzaXN0ZW50IGNhY2hlXG4gICAqL1xuICBhc3luYyBzYXZlQ2FjaGUoaXRlbXM6IENvbGxlY3Rpb25JdGVtW10sIGV0YWc/OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5lbnN1cmVDYWNoZURpcigpO1xuXG4gICAgICBjb25zdCBjYWNoZUVudHJ5OiBDb2xsZWN0aW9uQ2FjaGVFbnRyeSA9IHtcbiAgICAgICAgaXRlbXMsXG4gICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgZXRhZ1xuICAgICAgfTtcblxuICAgICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGNhY2hlRW50cnksIG51bGwsIDIpO1xuICAgICAgYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy53cml0ZUZpbGUodGhpcy5jYWNoZUZpbGUsIGRhdGEsIHtcbiAgICAgICAgc291cmNlOiAnQ29sbGVjdGlvbkNhY2hlLnNhdmVDYWNoZScsXG4gICAgICAgIG1heFNpemU6IDUwICogMTAyNCAqIDEwMjQgLy8gNTBNQiBmb3IgY29sbGVjdGlvbiBjYWNoZVxuICAgICAgfSk7XG5cbiAgICAgIGxvZ2dlci5kZWJ1ZyhgU2F2ZWQgJHtpdGVtcy5sZW5ndGh9IGl0ZW1zIHRvIGNvbGxlY3Rpb24gY2FjaGVgKTtcblxuICAgICAgLy8gU0VDVVJJVFkgRklYOiBBZGQgYXVkaXQgbG9nZ2luZyBmb3IgY2FjaGUgd3JpdGUgb3BlcmF0aW9uc1xuICAgICAgbG9nZ2VyLmRlYnVnKCdTZWN1cml0eSBhdWRpdDogQ2FjaGUgd3JpdGUgb3BlcmF0aW9uIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHknKTtcblxuICAgICAgLy8gTG9nIG9wZXJhdGlvbiBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5XG4gICAgICBsb2dnZXIuZGVidWcoYENhY2hlIGZpbGUgb3BlcmF0aW9uIGNvbXBsZXRlZCB3aXRoICR7aXRlbXMubGVuZ3RofSBpdGVtc2ApO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoYEZhaWxlZCB0byBzYXZlIGNvbGxlY3Rpb24gY2FjaGU6ICR7ZXJyb3J9YCk7XG4gICAgICAvLyBEb24ndCB0aHJvdyAtIGNhY2hpbmcgZmFpbHVyZXMgc2hvdWxkbid0IGJyZWFrIGZ1bmN0aW9uYWxpdHlcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBTZWFyY2ggY2FjaGVkIGNvbGxlY3Rpb24gaXRlbXMgd2l0aCBmdXp6eSBtYXRjaGluZ1xuICAgKi9cbiAgYXN5bmMgc2VhcmNoQ2FjaGUocXVlcnk6IHN0cmluZyk6IFByb21pc2U8Q29sbGVjdGlvbkl0ZW1bXT4ge1xuICAgIGNvbnN0IGNhY2hlID0gYXdhaXQgdGhpcy5sb2FkQ2FjaGUoKTtcbiAgICBpZiAoIWNhY2hlKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IG5vcm1hbGl6ZWRRdWVyeSA9IHRoaXMubm9ybWFsaXplU2VhcmNoVGVybShxdWVyeSk7XG4gICAgcmV0dXJuIGNhY2hlLml0ZW1zLmZpbHRlcihpdGVtID0+IHtcbiAgICAgIC8vIFNlYXJjaCBpbiBmaWxlbmFtZSBhbmQgcGF0aCB3aXRoIG5vcm1hbGl6YXRpb25cbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWROYW1lID0gdGhpcy5ub3JtYWxpemVTZWFyY2hUZXJtKGl0ZW0ubmFtZSk7XG4gICAgICBjb25zdCBub3JtYWxpemVkUGF0aCA9IHRoaXMubm9ybWFsaXplU2VhcmNoVGVybShpdGVtLnBhdGgpO1xuICAgICAgXG4gICAgICByZXR1cm4gbm9ybWFsaXplZE5hbWUuaW5jbHVkZXMobm9ybWFsaXplZFF1ZXJ5KSB8fCBcbiAgICAgICAgICAgICBub3JtYWxpemVkUGF0aC5pbmNsdWRlcyhub3JtYWxpemVkUXVlcnkpIHx8XG4gICAgICAgICAgICAgKGl0ZW0uY29udGVudCAmJiB0aGlzLm5vcm1hbGl6ZVNlYXJjaFRlcm0oaXRlbS5jb250ZW50KS5pbmNsdWRlcyhub3JtYWxpemVkUXVlcnkpKTtcbiAgICB9KTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIE5vcm1hbGl6ZSBzZWFyY2ggdGVybXMgZm9yIGJldHRlciBtYXRjaGluZyAoaGFuZGxlcyBzcGFjZXMsIGRhc2hlcywgZXRjLilcbiAgICovXG4gIHByaXZhdGUgbm9ybWFsaXplU2VhcmNoVGVybSh0ZXJtOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiB0ZXJtLnRvTG93ZXJDYXNlKClcbiAgICAgIC5yZXBsYWNlQWxsKC9bLV9cXHNdKy9nLCAnICcpICAvLyBDb252ZXJ0IGRhc2hlcywgdW5kZXJzY29yZXMgdG8gc3BhY2VzXG4gICAgICAucmVwbGFjZSgvXFwubWQkLywgJycpICAgICAgIC8vIFJlbW92ZSAubWQgZXh0ZW5zaW9uXG4gICAgICAudHJpbSgpO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IGNhY2hlZCBjb2xsZWN0aW9uIGl0ZW1zIGJ5IHR5cGUvcGF0aFxuICAgKi9cbiAgYXN5bmMgZ2V0SXRlbXNCeVBhdGgocGF0aFByZWZpeDogc3RyaW5nKTogUHJvbWlzZTxDb2xsZWN0aW9uSXRlbVtdPiB7XG4gICAgY29uc3QgY2FjaGUgPSBhd2FpdCB0aGlzLmxvYWRDYWNoZSgpO1xuICAgIGlmICghY2FjaGUpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGNhY2hlLml0ZW1zLmZpbHRlcihpdGVtID0+IGl0ZW0ucGF0aC5zdGFydHNXaXRoKHBhdGhQcmVmaXgpKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIGNhY2hlIGV4aXN0cyBhbmQgaXMgdmFsaWRcbiAgICovXG4gIGFzeW5jIGlzQ2FjaGVWYWxpZCgpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBjYWNoZSA9IGF3YWl0IHRoaXMubG9hZENhY2hlKCk7XG4gICAgcmV0dXJuIGNhY2hlICE9PSBudWxsO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2xlYXIgdGhlIGNhY2hlXG4gICAqL1xuICBhc3luYyBjbGVhckNhY2hlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLmZpbGVPcGVyYXRpb25zLmRlbGV0ZUZpbGUodGhpcy5jYWNoZUZpbGUsIHVuZGVmaW5lZCwge1xuICAgICAgICBzb3VyY2U6ICdDb2xsZWN0aW9uQ2FjaGUuY2xlYXJDYWNoZSdcbiAgICAgIH0pO1xuICAgICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIGNhY2hlIGNsZWFyZWQnKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKChlcnJvciBhcyBhbnkpLmNvZGUgIT09ICdFTk9FTlQnKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgRmFpbGVkIHRvIGNsZWFyIGNvbGxlY3Rpb24gY2FjaGU6ICR7ZXJyb3J9YCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogR2V0IGNhY2hlIHN0YXRzIGZvciBkZWJ1Z2dpbmdcbiAgICovXG4gIGFzeW5jIGdldENhY2hlU3RhdHMoKTogUHJvbWlzZTx7IGl0ZW1Db3VudDogbnVtYmVyOyBjYWNoZUFnZTogbnVtYmVyOyBpc1ZhbGlkOiBib29sZWFuIH0+IHtcbiAgICBjb25zdCBjYWNoZSA9IGF3YWl0IHRoaXMubG9hZENhY2hlKCk7XG4gICAgaWYgKCFjYWNoZSkge1xuICAgICAgcmV0dXJuIHsgaXRlbUNvdW50OiAwLCBjYWNoZUFnZTogMCwgaXNWYWxpZDogZmFsc2UgfTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIGl0ZW1Db3VudDogY2FjaGUuaXRlbXMubGVuZ3RoLFxuICAgICAgY2FjaGVBZ2U6IERhdGUubm93KCkgLSBjYWNoZS50aW1lc3RhbXAsXG4gICAgICBpc1ZhbGlkOiBEYXRlLm5vdygpIC0gY2FjaGUudGltZXN0YW1wIDw9IHRoaXMuQ0FDSEVfVFRMX01TXG4gICAgfTtcbiAgfVxufSJdfQ==