@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.
97 lines • 13.7 kB
JavaScript
/**
* ResourceHandler - Manages MCP Resources capability
*
* Handles registration and serving of MCP resources including:
* - Capability Index (summary, full, stats variants)
*
* Uses dependency injection pattern consistent with refactored architecture.
* Resources are disabled by default and require explicit configuration.
*/
import { ListResourcesRequestSchema, ReadResourceRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
import { CapabilityIndexResource } from '../server/resources/CapabilityIndexResource.js';
import { logger } from '../utils/logger.js';
import { ErrorHandler } from '../utils/ErrorHandler.js';
import { FileOperationsService } from '../services/FileOperationsService.js';
import { FileLockManager } from '../security/fileLockManager.js';
/**
* Handler for MCP Resources protocol
*/
export class ResourceHandler {
configManager;
capabilityIndexResource;
isEnabled = false;
enabledVariants = [];
constructor(configManager) {
this.configManager = configManager;
}
/**
* Initialize and register resource handlers with the MCP server
*
* This method:
* 1. Checks configuration to see if resources are enabled
* 2. Initializes the CapabilityIndexResource
* 3. Registers ListResourcesRequest and ReadResourceRequest handlers
*
* @param server - MCP Server instance to register handlers with
*/
async initialize(server) {
try {
// Check if resources are enabled in configuration
const resourcesConfig = this.configManager.getSetting('elements.enhanced_index.resources');
if (!resourcesConfig?.advertise_resources) {
logger.info('[ResourceHandler] MCP Resources disabled (future-proof implementation, opt-in required)');
return;
}
// Resources are enabled
this.isEnabled = true;
// Initialize resource handler with FileOperationsService
const fileLockManager = new FileLockManager();
const fileOperations = new FileOperationsService(fileLockManager);
this.capabilityIndexResource = new CapabilityIndexResource(fileOperations);
// Determine which variants are enabled
if (resourcesConfig.variants?.summary)
this.enabledVariants.push('summary');
if (resourcesConfig.variants?.full)
this.enabledVariants.push('full');
if (resourcesConfig.variants?.stats)
this.enabledVariants.push('stats');
logger.info(`[ResourceHandler] MCP Resources enabled: capability-index (variants: ${this.enabledVariants.join(', ') || 'none'})`);
// Register ListResourcesRequest handler
server.setRequestHandler(ListResourcesRequestSchema, async () => {
if (!this.capabilityIndexResource) {
throw new McpError(ErrorCode.InternalError, 'Resource handler not initialized');
}
const result = await this.capabilityIndexResource.listResources();
return result; // Type assertion needed for MCP SDK compatibility
});
// Register ReadResourceRequest handler
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (!this.capabilityIndexResource) {
throw new McpError(ErrorCode.InternalError, 'Resource handler not initialized');
}
const result = await this.capabilityIndexResource.readResource(request.params.uri);
return result; // Type assertion needed for MCP SDK compatibility
});
logger.debug('[ResourceHandler] MCP resource handlers registered successfully');
}
catch (error) {
ErrorHandler.logError('ResourceHandler.initialize', error);
// Don't throw - resource setup failures shouldn't prevent server startup
logger.warn('[ResourceHandler] Failed to setup MCP resource handlers, continuing without resources');
this.isEnabled = false;
}
}
/**
* Check if resources are currently enabled
*/
getIsEnabled() {
return this.isEnabled;
}
/**
* Get list of enabled resource variants
*/
getEnabledVariants() {
return [...this.enabledVariants];
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVzb3VyY2VIYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hhbmRsZXJzL1Jlc291cmNlSGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQUdILE9BQU8sRUFBRSwwQkFBMEIsRUFBRSx5QkFBeUIsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDaEksT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sZ0RBQWdELENBQUM7QUFFekYsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN4RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUM3RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFakU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sZUFBZTtJQU1QO0lBTFgsdUJBQXVCLENBQTJCO0lBQ2xELFNBQVMsR0FBWSxLQUFLLENBQUM7SUFDM0IsZUFBZSxHQUFhLEVBQUUsQ0FBQztJQUV2QyxZQUNtQixhQUE0QjtRQUE1QixrQkFBYSxHQUFiLGFBQWEsQ0FBZTtJQUM1QyxDQUFDO0lBRUo7Ozs7Ozs7OztPQVNHO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FBQyxNQUFjO1FBQzdCLElBQUksQ0FBQztZQUNILGtEQUFrRDtZQUNsRCxNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBTSxtQ0FBbUMsQ0FBQyxDQUFDO1lBRWhHLElBQUksQ0FBQyxlQUFlLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztnQkFDMUMsTUFBTSxDQUFDLElBQUksQ0FBQyx5RkFBeUYsQ0FBQyxDQUFDO2dCQUN2RyxPQUFPO1lBQ1QsQ0FBQztZQUVELHdCQUF3QjtZQUN4QixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztZQUV0Qix5REFBeUQ7WUFDekQsTUFBTSxlQUFlLEdBQUcsSUFBSSxlQUFlLEVBQUUsQ0FBQztZQUM5QyxNQUFNLGNBQWMsR0FBRyxJQUFJLHFCQUFxQixDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQ2xFLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLHVCQUF1QixDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBRTNFLHVDQUF1QztZQUN2QyxJQUFJLGVBQWUsQ0FBQyxRQUFRLEVBQUUsT0FBTztnQkFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM1RSxJQUFJLGVBQWUsQ0FBQyxRQUFRLEVBQUUsSUFBSTtnQkFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUN0RSxJQUFJLGVBQWUsQ0FBQyxRQUFRLEVBQUUsS0FBSztnQkFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUV4RSxNQUFNLENBQUMsSUFBSSxDQUFDLHdFQUF3RSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEdBQUcsQ0FBQyxDQUFDO1lBRWxJLHdDQUF3QztZQUN4QyxNQUFNLENBQUMsaUJBQWlCLENBQUMsMEJBQTBCLEVBQUUsS0FBSyxJQUFJLEVBQUU7Z0JBQzlELElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztvQkFDbEMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxTQUFTLENBQUMsYUFBYSxFQUFFLGtDQUFrQyxDQUFDLENBQUM7Z0JBQ2xGLENBQUM7Z0JBQ0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsdUJBQXVCLENBQUMsYUFBYSxFQUFFLENBQUM7Z0JBQ2xFLE9BQU8sTUFBYSxDQUFDLENBQUMsa0RBQWtEO1lBQzFFLENBQUMsQ0FBQyxDQUFDO1lBRUgsdUNBQXVDO1lBQ3ZDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyx5QkFBeUIsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7Z0JBQ3BFLElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztvQkFDbEMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxTQUFTLENBQUMsYUFBYSxFQUFFLGtDQUFrQyxDQUFDLENBQUM7Z0JBQ2xGLENBQUM7Z0JBQ0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsdUJBQXVCLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ25GLE9BQU8sTUFBYSxDQUFDLENBQUMsa0RBQWtEO1lBQzFFLENBQUMsQ0FBQyxDQUFDO1lBRUgsTUFBTSxDQUFDLEtBQUssQ0FBQyxpRUFBaUUsQ0FBQyxDQUFDO1FBQ2xGLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsWUFBWSxDQUFDLFFBQVEsQ0FBQyw0QkFBNEIsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUMzRCx5RUFBeUU7WUFDekUsTUFBTSxDQUFDLElBQUksQ0FBQyx1RkFBdUYsQ0FBQyxDQUFDO1lBQ3JHLElBQUksQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDO1FBQ3pCLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxZQUFZO1FBQ1YsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7T0FFRztJQUNILGtCQUFrQjtRQUNoQixPQUFPLENBQUMsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDbkMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZXNvdXJjZUhhbmRsZXIgLSBNYW5hZ2VzIE1DUCBSZXNvdXJjZXMgY2FwYWJpbGl0eVxuICpcbiAqIEhhbmRsZXMgcmVnaXN0cmF0aW9uIGFuZCBzZXJ2aW5nIG9mIE1DUCByZXNvdXJjZXMgaW5jbHVkaW5nOlxuICogLSBDYXBhYmlsaXR5IEluZGV4IChzdW1tYXJ5LCBmdWxsLCBzdGF0cyB2YXJpYW50cylcbiAqXG4gKiBVc2VzIGRlcGVuZGVuY3kgaW5qZWN0aW9uIHBhdHRlcm4gY29uc2lzdGVudCB3aXRoIHJlZmFjdG9yZWQgYXJjaGl0ZWN0dXJlLlxuICogUmVzb3VyY2VzIGFyZSBkaXNhYmxlZCBieSBkZWZhdWx0IGFuZCByZXF1aXJlIGV4cGxpY2l0IGNvbmZpZ3VyYXRpb24uXG4gKi9cblxuaW1wb3J0IHsgU2VydmVyIH0gZnJvbSBcIkBtb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvc2VydmVyL2luZGV4LmpzXCI7XG5pbXBvcnQgeyBMaXN0UmVzb3VyY2VzUmVxdWVzdFNjaGVtYSwgUmVhZFJlc291cmNlUmVxdWVzdFNjaGVtYSwgRXJyb3JDb2RlLCBNY3BFcnJvciB9IGZyb20gXCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2RrL3R5cGVzLmpzXCI7XG5pbXBvcnQgeyBDYXBhYmlsaXR5SW5kZXhSZXNvdXJjZSB9IGZyb20gJy4uL3NlcnZlci9yZXNvdXJjZXMvQ2FwYWJpbGl0eUluZGV4UmVzb3VyY2UuanMnO1xuaW1wb3J0IHsgQ29uZmlnTWFuYWdlciB9IGZyb20gJy4uL2NvbmZpZy9Db25maWdNYW5hZ2VyLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBFcnJvckhhbmRsZXIgfSBmcm9tICcuLi91dGlscy9FcnJvckhhbmRsZXIuanMnO1xuaW1wb3J0IHsgRmlsZU9wZXJhdGlvbnNTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvRmlsZU9wZXJhdGlvbnNTZXJ2aWNlLmpzJztcbmltcG9ydCB7IEZpbGVMb2NrTWFuYWdlciB9IGZyb20gJy4uL3NlY3VyaXR5L2ZpbGVMb2NrTWFuYWdlci5qcyc7XG5cbi8qKlxuICogSGFuZGxlciBmb3IgTUNQIFJlc291cmNlcyBwcm90b2NvbFxuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VIYW5kbGVyIHtcbiAgcHJpdmF0ZSBjYXBhYmlsaXR5SW5kZXhSZXNvdXJjZT86IENhcGFiaWxpdHlJbmRleFJlc291cmNlO1xuICBwcml2YXRlIGlzRW5hYmxlZDogYm9vbGVhbiA9IGZhbHNlO1xuICBwcml2YXRlIGVuYWJsZWRWYXJpYW50czogc3RyaW5nW10gPSBbXTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbmZpZ01hbmFnZXI6IENvbmZpZ01hbmFnZXJcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplIGFuZCByZWdpc3RlciByZXNvdXJjZSBoYW5kbGVycyB3aXRoIHRoZSBNQ1Agc2VydmVyXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kOlxuICAgKiAxLiBDaGVja3MgY29uZmlndXJhdGlvbiB0byBzZWUgaWYgcmVzb3VyY2VzIGFyZSBlbmFibGVkXG4gICAqIDIuIEluaXRpYWxpemVzIHRoZSBDYXBhYmlsaXR5SW5kZXhSZXNvdXJjZVxuICAgKiAzLiBSZWdpc3RlcnMgTGlzdFJlc291cmNlc1JlcXVlc3QgYW5kIFJlYWRSZXNvdXJjZVJlcXVlc3QgaGFuZGxlcnNcbiAgICpcbiAgICogQHBhcmFtIHNlcnZlciAtIE1DUCBTZXJ2ZXIgaW5zdGFuY2UgdG8gcmVnaXN0ZXIgaGFuZGxlcnMgd2l0aFxuICAgKi9cbiAgYXN5bmMgaW5pdGlhbGl6ZShzZXJ2ZXI6IFNlcnZlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayBpZiByZXNvdXJjZXMgYXJlIGVuYWJsZWQgaW4gY29uZmlndXJhdGlvblxuICAgICAgY29uc3QgcmVzb3VyY2VzQ29uZmlnID0gdGhpcy5jb25maWdNYW5hZ2VyLmdldFNldHRpbmc8YW55PignZWxlbWVudHMuZW5oYW5jZWRfaW5kZXgucmVzb3VyY2VzJyk7XG5cbiAgICAgIGlmICghcmVzb3VyY2VzQ29uZmlnPy5hZHZlcnRpc2VfcmVzb3VyY2VzKSB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdbUmVzb3VyY2VIYW5kbGVyXSBNQ1AgUmVzb3VyY2VzIGRpc2FibGVkIChmdXR1cmUtcHJvb2YgaW1wbGVtZW50YXRpb24sIG9wdC1pbiByZXF1aXJlZCknKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXNvdXJjZXMgYXJlIGVuYWJsZWRcbiAgICAgIHRoaXMuaXNFbmFibGVkID0gdHJ1ZTtcblxuICAgICAgLy8gSW5pdGlhbGl6ZSByZXNvdXJjZSBoYW5kbGVyIHdpdGggRmlsZU9wZXJhdGlvbnNTZXJ2aWNlXG4gICAgICBjb25zdCBmaWxlTG9ja01hbmFnZXIgPSBuZXcgRmlsZUxvY2tNYW5hZ2VyKCk7XG4gICAgICBjb25zdCBmaWxlT3BlcmF0aW9ucyA9IG5ldyBGaWxlT3BlcmF0aW9uc1NlcnZpY2UoZmlsZUxvY2tNYW5hZ2VyKTtcbiAgICAgIHRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UgPSBuZXcgQ2FwYWJpbGl0eUluZGV4UmVzb3VyY2UoZmlsZU9wZXJhdGlvbnMpO1xuXG4gICAgICAvLyBEZXRlcm1pbmUgd2hpY2ggdmFyaWFudHMgYXJlIGVuYWJsZWRcbiAgICAgIGlmIChyZXNvdXJjZXNDb25maWcudmFyaWFudHM/LnN1bW1hcnkpIHRoaXMuZW5hYmxlZFZhcmlhbnRzLnB1c2goJ3N1bW1hcnknKTtcbiAgICAgIGlmIChyZXNvdXJjZXNDb25maWcudmFyaWFudHM/LmZ1bGwpIHRoaXMuZW5hYmxlZFZhcmlhbnRzLnB1c2goJ2Z1bGwnKTtcbiAgICAgIGlmIChyZXNvdXJjZXNDb25maWcudmFyaWFudHM/LnN0YXRzKSB0aGlzLmVuYWJsZWRWYXJpYW50cy5wdXNoKCdzdGF0cycpO1xuXG4gICAgICBsb2dnZXIuaW5mbyhgW1Jlc291cmNlSGFuZGxlcl0gTUNQIFJlc291cmNlcyBlbmFibGVkOiBjYXBhYmlsaXR5LWluZGV4ICh2YXJpYW50czogJHt0aGlzLmVuYWJsZWRWYXJpYW50cy5qb2luKCcsICcpIHx8ICdub25lJ30pYCk7XG5cbiAgICAgIC8vIFJlZ2lzdGVyIExpc3RSZXNvdXJjZXNSZXF1ZXN0IGhhbmRsZXJcbiAgICAgIHNlcnZlci5zZXRSZXF1ZXN0SGFuZGxlcihMaXN0UmVzb3VyY2VzUmVxdWVzdFNjaGVtYSwgYXN5bmMgKCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgTWNwRXJyb3IoRXJyb3JDb2RlLkludGVybmFsRXJyb3IsICdSZXNvdXJjZSBoYW5kbGVyIG5vdCBpbml0aWFsaXplZCcpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UubGlzdFJlc291cmNlcygpO1xuICAgICAgICByZXR1cm4gcmVzdWx0IGFzIGFueTsgLy8gVHlwZSBhc3NlcnRpb24gbmVlZGVkIGZvciBNQ1AgU0RLIGNvbXBhdGliaWxpdHlcbiAgICAgIH0pO1xuXG4gICAgICAvLyBSZWdpc3RlciBSZWFkUmVzb3VyY2VSZXF1ZXN0IGhhbmRsZXJcbiAgICAgIHNlcnZlci5zZXRSZXF1ZXN0SGFuZGxlcihSZWFkUmVzb3VyY2VSZXF1ZXN0U2NoZW1hLCBhc3luYyAocmVxdWVzdCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgTWNwRXJyb3IoRXJyb3JDb2RlLkludGVybmFsRXJyb3IsICdSZXNvdXJjZSBoYW5kbGVyIG5vdCBpbml0aWFsaXplZCcpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuY2FwYWJpbGl0eUluZGV4UmVzb3VyY2UucmVhZFJlc291cmNlKHJlcXVlc3QucGFyYW1zLnVyaSk7XG4gICAgICAgIHJldHVybiByZXN1bHQgYXMgYW55OyAvLyBUeXBlIGFzc2VydGlvbiBuZWVkZWQgZm9yIE1DUCBTREsgY29tcGF0aWJpbGl0eVxuICAgICAgfSk7XG5cbiAgICAgIGxvZ2dlci5kZWJ1ZygnW1Jlc291cmNlSGFuZGxlcl0gTUNQIHJlc291cmNlIGhhbmRsZXJzIHJlZ2lzdGVyZWQgc3VjY2Vzc2Z1bGx5Jyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIEVycm9ySGFuZGxlci5sb2dFcnJvcignUmVzb3VyY2VIYW5kbGVyLmluaXRpYWxpemUnLCBlcnJvcik7XG4gICAgICAvLyBEb24ndCB0aHJvdyAtIHJlc291cmNlIHNldHVwIGZhaWx1cmVzIHNob3VsZG4ndCBwcmV2ZW50IHNlcnZlciBzdGFydHVwXG4gICAgICBsb2dnZXIud2FybignW1Jlc291cmNlSGFuZGxlcl0gRmFpbGVkIHRvIHNldHVwIE1DUCByZXNvdXJjZSBoYW5kbGVycywgY29udGludWluZyB3aXRob3V0IHJlc291cmNlcycpO1xuICAgICAgdGhpcy5pc0VuYWJsZWQgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgcmVzb3VyY2VzIGFyZSBjdXJyZW50bHkgZW5hYmxlZFxuICAgKi9cbiAgZ2V0SXNFbmFibGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzRW5hYmxlZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgbGlzdCBvZiBlbmFibGVkIHJlc291cmNlIHZhcmlhbnRzXG4gICAqL1xuICBnZXRFbmFibGVkVmFyaWFudHMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiBbLi4udGhpcy5lbmFibGVkVmFyaWFudHNdO1xuICB9XG59XG4iXX0=