UNPKG

@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.

354 lines 59.1 kB
/** * TemplateManager - Refactored to extend BaseElementManager, keeping * template-specific validation, import/export logic, and analytics helpers. */ import * as path from 'path'; import { ElementType } from '../../portfolio/types.js'; import { toSingularLabel } from '../../utils/elementTypeNormalization.js'; import { SecurityMonitor } from '../../security/securityMonitor.js'; import { sanitizeInput } from '../../security/InputValidator.js'; import { UnicodeValidator } from '../../security/validators/unicodeValidator.js'; import { logger } from '../../utils/logger.js'; import { BaseElementManager } from '../base/BaseElementManager.js'; import { Template } from './Template.js'; import { sanitizeGatekeeperPolicy } from '../../handlers/mcp-aql/policies/ElementPolicies.js'; import { SECURITY_LIMITS } from '../../security/constants.js'; export class TemplateManager extends BaseElementManager { metadataService; triggerValidationService; validationService; serializationService; constructor(portfolioManager, fileLockManager, fileOperationsService, validationRegistry, serializationService, metadataService, fileWatchService, memoryBudget, backupService) { super(ElementType.TEMPLATE, portfolioManager, fileLockManager, { fileWatchService, memoryBudget, backupService }, fileOperationsService, validationRegistry); this.metadataService = metadataService; this.triggerValidationService = validationRegistry.getTriggerValidationService(); this.validationService = validationRegistry.getValidationService(); this.serializationService = serializationService; } getElementLabel() { return 'template'; } async load(filePath) { const template = await super.load(filePath); SecurityMonitor.logSecurityEvent({ type: 'TEMPLATE_LOADED', severity: 'LOW', source: 'TemplateManager.load', details: `Template loaded: ${template.metadata.name} from ${path.basename(filePath)}` }); return template; } async save(template, filePath) { // Auto-derive variables from content (#1896): ensures every {{placeholder}} // has a matching schema entry so render() never silently returns unfilled text. // Existing entries are never overwritten — user-set descriptions, types, and // required flags survive. New entries default to type: 'string', required: false. if (template.content) { template.metadata.variables = Template.deriveVariablesFromContent(template.content, template.metadata.variables ?? []); } await super.save(template, filePath); SecurityMonitor.logSecurityEvent({ type: 'TEMPLATE_SAVED', severity: 'LOW', source: 'TemplateManager.save', details: `Template saved: ${template.metadata.name} to ${path.basename(filePath)}` }); logger.info(`Template saved: ${template.metadata.name}`); } async delete(filePath) { await super.delete(filePath); SecurityMonitor.logSecurityEvent({ type: 'TEMPLATE_DELETED', severity: 'MEDIUM', source: 'TemplateManager.delete', details: `Template deleted: ${path.basename(filePath)}` }); logger.info(`Template deleted: ${path.basename(filePath)}`); } async create(data) { // Use specialized validator for input validation const validationResult = await this.validator.validateCreate({ name: data.name, description: data.description, content: data.content }); if (!validationResult.isValid) { throw new Error(`Validation failed: ${validationResult.errors.join(', ')}`); } // Log warnings if any if (validationResult.warnings && validationResult.warnings.length > 0) { logger.warn(`Template creation warnings: ${validationResult.warnings.join(', ')}`); } // Get sanitized name for file operations (validator already validated) const nameInput = data.name || 'new-template'; const nameResult = this.validationService.validateAndSanitizeInput(nameInput, { maxLength: SECURITY_LIMITS.MAX_NAME_LENGTH, allowSpaces: true }); const sanitizedName = nameResult.sanitizedValue; // Use inherited getElementFilename() for consistent filename normalization const filename = this.getElementFilename(sanitizedName); // FIX: Issue #20 - Check for duplicate before creating const existingTemplates = await this.list(); const duplicate = existingTemplates.find(t => t.metadata.name.toLowerCase() === sanitizedName.toLowerCase()); if (duplicate) { throw new Error(`A template named "${sanitizedName}" already exists`); } const template = new Template({ ...data.metadata, name: sanitizedName, description: data.description || '' }, data.content || '', this.metadataService); // Set instructions (rendering directives) if provided if (data.instructions) { template.instructions = data.instructions; } await this.save(template, filename); // Note: No reload() here — save() caches the element correctly. // See Issue #491 for why PersonaManager's reload-after-create was removed. SecurityMonitor.logSecurityEvent({ type: 'ELEMENT_CREATED', severity: 'LOW', source: 'TemplateManager.create', details: `Template created: ${template.metadata.name}` }); return template; } async importElement(data, format = 'markdown') { try { let metadata; let content = ''; switch (format) { case 'json': { const jsonData = this.serializationService.parseJson(data, { source: 'TemplateManager.importElement' }); metadata = await this.sanitizeMetadata(jsonData.metadata || {}); content = jsonData.content || ''; break; } case 'yaml': { const parsed = this.serializationService.parseFrontmatter(data, { maxYamlSize: 64 * 1024, validateContent: true, source: 'TemplateManager.importElement' }); metadata = await this.sanitizeMetadata(parsed.data.metadata || parsed.data); content = parsed.content || ''; break; } case 'markdown': { const mdParsed = this.serializationService.parseFrontmatter(data, { maxYamlSize: 64 * 1024, validateContent: true, source: 'TemplateManager.importElement' }); metadata = await this.sanitizeMetadata(mdParsed.data); content = mdParsed.content; break; } default: throw new Error(`Unsupported import format: ${format}`); } const template = new Template(metadata, content, this.metadataService); const validation = template.validate(); if (!validation.valid) { throw new Error(`Invalid template: ${validation.errors?.map(e => e.message).join(', ')}`); } return template; } catch (error) { logger.error(`Failed to import template: ${error}`); throw error; } } async exportElement(template, format = 'markdown') { try { switch (format) { case 'json': return template.serializeToJSON ? template.serializeToJSON() : template.serialize(); case 'yaml': { const yamlData = { metadata: template.metadata, content: template.content, id: template.id, version: template.version }; return this.serializationService.dumpYaml(yamlData, { schema: 'json', // Fix #914: standardize on JSON schema across all managers noRefs: true, sortKeys: true, skipInvalid: true }); } case 'markdown': { return this.serializationService.createFrontmatter(template.metadata, template.content, { method: 'manual', schema: 'json', // Fix #914: standardize on JSON schema across all managers cleanMetadata: true, cleaningStrategy: 'remove-both' // Fix #913: standardize across all managers }); } default: throw new Error(`Unsupported export format: ${format}`); } } catch (error) { logger.error(`Failed to export template: ${error}`); throw error; } } async findByCategory(category) { const sanitizedCategory = sanitizeInput(category, 50); const templates = await this.list(); return templates.filter(t => t.metadata.category === sanitizedCategory); } async findByTag(tag) { const sanitizedTag = sanitizeInput(tag, 50); const templates = await this.list(); return templates.filter(t => t.metadata.tags?.includes(sanitizedTag)); } async getMostUsed(limit = 10) { const MIN_LIMIT = 1; const MAX_LIMIT = 100; const validatedLimit = Math.max(MIN_LIMIT, Math.min(MAX_LIMIT, Math.floor(limit))); if (limit !== validatedLimit) { logger.warn(`getMostUsed: limit ${limit} adjusted to ${validatedLimit} (valid range: ${MIN_LIMIT}-${MAX_LIMIT})`); } const templates = await this.list(); return templates .sort((a, b) => (b.metadata.usage_count || 0) - (a.metadata.usage_count || 0)) .slice(0, validatedLimit); } getFileExtension() { return '.md'; } async parseMetadata(data) { const sanitized = await this.sanitizeMetadata(data); // Use Template constructor to ensure required fields are populated const tempTemplate = new Template(sanitized, '', this.metadataService); return tempTemplate.metadata; } createElement(metadata, bodyContent) { // Fix #912: Prefer explicit format_version marker, fall back to instructions-presence check const metadataInstructions = metadata.instructions; delete metadata.format_version; // Strip marker from runtime metadata const template = new Template(metadata, bodyContent, this.metadataService); if (metadataInstructions) { template.instructions = metadataInstructions; delete metadata.instructions; } return template; } buildDefaultBody(template) { const name = (template.metadata.name ?? '').trim(); const description = (template.metadata.description ?? '').trim(); const lines = []; if (name) { lines.push(`# ${name}`); lines.push(''); } if (description) { lines.push(description); lines.push(''); } // Section format: <template> for Handlebars content, <style>/<script> for raw passthrough. // Auto-detection (issue #705): section mode activates when body has a <template> root element. lines.push('<template>'); lines.push('<!-- HTML content with {{variable}} substitution -->'); lines.push('</template>'); lines.push(''); lines.push('<style>'); lines.push('/* CSS styles (}} is safe here — not Handlebars processed) */'); lines.push('</style>'); lines.push(''); lines.push('<script>'); lines.push('// JavaScript (}} is safe here — not Handlebars processed)'); lines.push('</script>'); return lines.join('\n'); } async serializeElement(template) { // v2.0 format: add instructions to YAML frontmatter if present const metadata = { ...template.metadata }; // Issue #755: Serialize type as singular and persist unique_id metadata.type = toSingularLabel(ElementType.TEMPLATE); metadata.unique_id = template.id; // Fix #912: Explicit format marker metadata.format_version = 'v2'; if (template.instructions) { metadata.instructions = template.instructions; } return this.serializationService.createFrontmatter(metadata, template.content || this.buildDefaultBody(template), { method: 'manual', schema: 'json', // Fix #914: standardize on JSON schema across all managers cleanMetadata: true, cleaningStrategy: 'remove-both', // Fix #913: standardize across all managers sortKeys: true, lineWidth: 80, skipInvalid: true }); } async sanitizeMetadata(data) { const metadata = {}; if (data.name) { metadata.name = sanitizeInput(UnicodeValidator.normalize(data.name).normalizedContent, 100); } if (data.description) { metadata.description = sanitizeInput(UnicodeValidator.normalize(data.description).normalizedContent, 500); } if (data.category) { // SECURITY FIX: Use ValidationService for category validation const categoryResult = this.validationService.validateCategory(data.category); if (categoryResult.isValid && categoryResult.sanitizedValue) { metadata.category = categoryResult.sanitizedValue; } } if (data.output_format) { metadata.output_format = sanitizeInput(data.output_format, 20); } if (Array.isArray(data.tags)) { metadata.tags = data.tags.map((tag) => sanitizeInput(String(tag), 50)); } if (Array.isArray(data.includes)) { metadata.includes = data.includes.map((inc) => sanitizeInput(String(inc), 200)); } if (data.triggers && Array.isArray(data.triggers)) { const validationResult = this.triggerValidationService.validateTriggers(data.triggers, ElementType.TEMPLATE, metadata.name || 'unknown'); metadata.triggers = validationResult.validTriggers; } if (typeof data.usage_count === 'number') { metadata.usage_count = Math.max(0, Math.floor(data.usage_count)); } if (data.last_used) { metadata.last_used = sanitizeInput(String(data.last_used), 50); } if (Array.isArray(data.variables)) { metadata.variables = data.variables.map((v) => ({ name: sanitizeInput(v.name || '', 50), type: sanitizeInput(v.type || 'string', 20), description: v.description ? sanitizeInput(v.description, SECURITY_LIMITS.MAX_DOCUMENTATION_FIELD_LENGTH) : undefined, required: Boolean(v.required), default: v.default, validation: v.validation ? sanitizeInput(v.validation, 200) : undefined, options: Array.isArray(v.options) ? v.options.map((o) => sanitizeInput(String(o), 100)) : undefined, format: v.format ? sanitizeInput(v.format, 50) : undefined })); } if (Array.isArray(data.examples)) { metadata.examples = data.examples.map((ex) => ({ title: sanitizeInput(ex.title || '', 100), description: ex.description ? sanitizeInput(ex.description, SECURITY_LIMITS.MAX_DOCUMENTATION_FIELD_LENGTH) : undefined, variables: ex.variables || {}, output: ex.output ? sanitizeInput(ex.output, 5000) : undefined })); } metadata.author = data.author ? sanitizeInput(data.author, 100) : undefined; metadata.version = data.version ? sanitizeInput(data.version, 20) : undefined; // Issue #524 — Gatekeeper policy (all element types) metadata.gatekeeper = sanitizeGatekeeperPolicy(data.gatekeeper, metadata.name || 'unknown', 'template', metadata); metadata.name = metadata.name ?? 'Untitled Template'; metadata.description = metadata.description ?? ''; return metadata; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVGVtcGxhdGVNYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VsZW1lbnRzL3RlbXBsYXRlcy9UZW1wbGF0ZU1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFFN0IsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUMxRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDcEUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBRWpGLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNuRSxPQUFPLEVBQUUsUUFBUSxFQUFvQixNQUFNLGVBQWUsQ0FBQztBQVMzRCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxvREFBb0QsQ0FBQztBQUM5RixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFOUQsTUFBTSxPQUFPLGVBQWdCLFNBQVEsa0JBQTRCO0lBV3JEO0lBVkYsd0JBQXdCLENBQTJCO0lBQ25ELGlCQUFpQixDQUFvQjtJQUNyQyxvQkFBb0IsQ0FBdUI7SUFFbkQsWUFDRSxnQkFBa0MsRUFDbEMsZUFBZ0MsRUFDaEMscUJBQTRDLEVBQzVDLGtCQUFzQyxFQUN0QyxvQkFBMEMsRUFDbEMsZUFBZ0MsRUFDeEMsZ0JBQW1DLEVBQ25DLFlBQTJFLEVBQzNFLGFBQXVFO1FBRXZFLEtBQUssQ0FBQyxXQUFXLENBQUMsUUFBUSxFQUFFLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxF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QyxNQUFNLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7UUFDNUUsUUFBUSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO1FBRTlFLHFEQUFxRDtRQUNyRCxRQUFRLENBQUMsVUFBVSxHQUFHLHdCQUF3QixDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsUUFBUSxDQUFDLElBQUksSUFBSSxTQUFTLEVBQUUsVUFBVSxFQUFFLFFBQW1DLENBQUMsQ0FBQztRQUU3SSxRQUFRLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLElBQUksbUJBQW1CLENBQUM7UUFDckQsUUFBUSxDQUFDLFdBQVcsR0FBRyxRQUFRLENBQUMsV0FBVyxJQUFJLEVBQUUsQ0FBQztRQUVsRCxPQUFPLFFBQVEsQ0FBQztJQUNsQixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRlbXBsYXRlTWFuYWdlciAtIFJlZmFjdG9yZWQgdG8gZXh0ZW5kIEJhc2VFbGVtZW50TWFuYWdlciwga2VlcGluZ1xuICogdGVtcGxhdGUtc3BlY2lmaWMgdmFsaWRhdGlvbiwgaW1wb3J0L2V4cG9ydCBsb2dpYywgYW5kIGFuYWx5dGljcyBoZWxwZXJzLlxuICovXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5cbmltcG9ydCB7IEVsZW1lbnRUeXBlIH0gZnJvbSAnLi4vLi4vcG9ydGZvbGlvL3R5cGVzLmpzJztcbmltcG9ydCB7IHRvU2luZ3VsYXJMYWJlbCB9IGZyb20gJy4uLy4uL3V0aWxzL2VsZW1lbnRUeXBlTm9ybWFsaXphdGlvbi5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgc2FuaXRpemVJbnB1dCB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L0lucHV0VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgRmlsZUxvY2tNYW5hZ2VyIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvZmlsZUxvY2tNYW5hZ2VyLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBCYXNlRWxlbWVudE1hbmFnZXIgfSBmcm9tICcuLi9iYXNlL0Jhc2VFbGVtZW50TWFuYWdlci5qcyc7XG5pbXBvcnQgeyBUZW1wbGF0ZSwgVGVtcGxhdGVNZXRhZGF0YSB9IGZyb20gJy4vVGVtcGxhdGUuanMnO1xuaW1wb3J0IHsgUG9ydGZvbGlvTWFuYWdlciB9IGZyb20gJy4uLy4uL3BvcnRmb2xpby9Qb3J0Zm9saW9NYW5hZ2VyLmpzJztcbmltcG9ydCB7IFZhbGlkYXRpb25SZWdpc3RyeSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3ZhbGlkYXRpb24vVmFsaWRhdGlvblJlZ2lzdHJ5LmpzJztcbmltcG9ydCB7IFRyaWdnZXJWYWxpZGF0aW9uU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3ZhbGlkYXRpb24vVHJpZ2dlclZhbGlkYXRpb25TZXJ2aWNlLmpzJztcbmltcG9ydCB7IFZhbGlkYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvdmFsaWRhdGlvbi9WYWxpZGF0aW9uU2VydmljZS5qcyc7XG5pbXBvcnQgeyBTZXJpYWxpemF0aW9uU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL1NlcmlhbGl6YXRpb25TZXJ2aWNlLmpzJztcbmltcG9ydCB7IE1ldGFkYXRhU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL01ldGFkYXRhU2VydmljZS5qcyc7XG5pbXBvcnQgeyBGaWxlT3BlcmF0aW9uc1NlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9GaWxlT3BlcmF0aW9uc1NlcnZpY2UuanMnO1xuaW1wb3J0IHsgRmlsZVdhdGNoU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL0ZpbGVXYXRjaFNlcnZpY2UuanMnO1xuaW1wb3J0IHsgc2FuaXRpemVHYXRla2VlcGVyUG9saWN5IH0gZnJvbSAnLi4vLi4vaGFuZGxlcnMvbWNwLWFxbC9wb2xpY2llcy9FbGVtZW50UG9saWNpZXMuanMnO1xuaW1wb3J0IHsgU0VDVVJJVFlfTElNSVRTIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvY29uc3RhbnRzLmpzJztcblxuZXhwb3J0IGNsYXNzIFRlbXBsYXRlTWFuYWdlciBleHRlbmRzIEJhc2VFbGVtZW50TWFuYWdlcjxUZW1wbGF0ZT4ge1xuICBwcml2YXRlIHRyaWdnZXJWYWxpZGF0aW9uU2VydmljZTogVHJpZ2dlclZhbGlkYXRpb25TZXJ2aWNlO1xuICBwcml2YXRlIHZhbGlkYXRpb25TZXJ2aWNlOiBWYWxpZGF0aW9uU2VydmljZTtcbiAgcHJpdmF0ZSBzZXJpYWxpemF0aW9uU2VydmljZTogU2VyaWFsaXphdGlvblNlcnZpY2U7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcG9ydGZvbGlvTWFuYWdlcjogUG9ydGZvbGlvTWFuYWdlcixcbiAgICBmaWxlTG9ja01hbmFnZXI6IEZpbGVMb2NrTWFuYWdlcixcbiAgICBmaWxlT3BlcmF0aW9uc1NlcnZpY2U6IEZpbGVPcGVyYXRpb25zU2VydmljZSxcbiAgICB2YWxpZGF0aW9uUmVnaXN0cnk6IFZhbGlkYXRpb25SZWdpc3RyeSxcbiAgICBzZXJpYWxpemF0aW9uU2VydmljZTogU2VyaWFsaXphdGlvblNlcnZpY2UsXG4gICAgcHJpdmF0ZSBtZXRhZGF0YVNlcnZpY2U6IE1ldGFkYXRhU2VydmljZSxcbiAgICBmaWxlV2F0Y2hTZXJ2aWNlPzogRmlsZVdhdGNoU2VydmljZSxcbiAgICBtZW1vcnlCdWRnZXQ/OiBpbXBvcnQoJy4uLy4uL2NhY2hlL0NhY2hlTWVtb3J5QnVkZ2V0LmpzJykuQ2FjaGVNZW1vcnlCdWRnZXQsXG4gICAgYmFja3VwU2VydmljZT86IGltcG9ydCgnLi4vLi4vc2VydmljZXMvQmFja3VwU2VydmljZS5qcycpLkJhY2t1cFNlcnZpY2VcbiAgKSB7XG4gICAgc3VwZXIoRWxlbWVudFR5cGUuVEVNUExBVEUsIHBvcnRmb2xpb01hbmFnZXIsIGZpbGVMb2NrTWFuYWdlciwgeyBmaWxlV2F0Y2hTZXJ2aWNlLCBtZW1vcnlCdWRnZXQsIGJhY2t1cFNlcnZpY2UgfSwgZmlsZU9wZXJhdGlvbnNTZXJ2aWNlLCB2YWxpZGF0aW9uUmVnaXN0cnkpO1xuICAgIHRoaXMudHJpZ2dlclZhbGlkYXRpb25TZXJ2aWNlID0gdmFsaWRhdGlvblJlZ2lzdHJ5LmdldFRyaWdnZXJWYWxpZGF0aW9uU2VydmljZSgpO1xuICAgIHRoaXMudmFsaWRhdGlvblNlcnZpY2UgPSB2YWxpZGF0aW9uUmVnaXN0cnkuZ2V0VmFsaWRhdGlvblNlcnZpY2UoKTtcbiAgICB0aGlzLnNlcmlhbGl6YXRpb25TZXJ2aWNlID0gc2VyaWFsaXphdGlvblNlcnZpY2U7XG4gIH1cblxuICBwcm90ZWN0ZWQgb3ZlcnJpZGUgZ2V0RWxlbWVudExhYmVsKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuICd0ZW1wbGF0ZSc7XG4gIH1cblxuICBvdmVycmlkZSBhc3luYyBsb2FkKGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPFRlbXBsYXRlPiB7XG4gICAgY29uc3QgdGVtcGxhdGUgPSBhd2FpdCBzdXBlci5sb2FkKGZpbGVQYXRoKTtcblxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdURU1QTEFURV9MT0FERUQnLFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnVGVtcGxhdGVNYW5hZ2VyLmxvYWQnLFxuICAgICAgZGV0YWlsczogYFRlbXBsYXRlIGxvYWRlZDogJHt0ZW1wbGF0ZS5tZXRhZGF0YS5uYW1lfSBmcm9tICR7cGF0aC5iYXNlbmFtZShmaWxlUGF0aCl9YFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHRlbXBsYXRlO1xuICB9XG5cbiAgb3ZlcnJpZGUgYXN5bmMgc2F2ZSh0ZW1wbGF0ZTogVGVtcGxhdGUsIGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBBdXRvLWRlcml2ZSB2YXJpYWJsZXMgZnJvbSBjb250ZW50ICgjMTg5Nik6IGVuc3VyZXMgZXZlcnkge3twbGFjZWhvbGRlcn19XG4gICAgLy8gaGFzIGEgbWF0Y2hpbmcgc2NoZW1hIGVudHJ5IHNvIHJlbmRlcigpIG5ldmVyIHNpbGVudGx5IHJldHVybnMgdW5maWxsZWQgdGV4dC5cbiAgICAvLyBFeGlzdGluZyBlbnRyaWVzIGFyZSBuZXZlciBvdmVyd3JpdHRlbiDigJQgdXNlci1zZXQgZGVzY3JpcHRpb25zLCB0eXBlcywgYW5kXG4gICAgLy8gcmVxdWlyZWQgZmxhZ3Mgc3Vydml2ZS4gTmV3IGVudHJpZXMgZGVmYXVsdCB0byB0eXBlOiAnc3RyaW5nJywgcmVxdWlyZWQ6IGZhbHNlLlxuICAgIGlmICh0ZW1wbGF0ZS5jb250ZW50KSB7XG4gICAgICB0ZW1wbGF0ZS5tZXRhZGF0YS52YXJpYWJsZXMgPSBUZW1wbGF0ZS5kZXJpdmVWYXJpYWJsZXNGcm9tQ29udGVudChcbiAgICAgICAgdGVtcGxhdGUuY29udGVudCxcbiAgICAgICAgdGVtcGxhdGUubWV0YWRhdGEudmFyaWFibGVzID8/IFtdXG4gICAgICApO1xuICAgIH1cblxuICAgIGF3YWl0IHN1cGVyLnNhdmUodGVtcGxhdGUsIGZpbGVQYXRoKTtcblxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdURU1QTEFURV9TQVZFRCcsXG4gICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICBzb3VyY2U6ICdUZW1wbGF0ZU1hbmFnZXIuc2F2ZScsXG4gICAgICBkZXRhaWxzOiBgVGVtcGxhdGUgc2F2ZWQ6ICR7dGVtcGxhdGUubWV0YWRhdGEubmFtZX0gdG8gJHtwYXRoLmJhc2VuYW1lKGZpbGVQYXRoKX1gXG4gICAgfSk7XG5cbiAgICBsb2dnZXIuaW5mbyhgVGVtcGxhdGUgc2F2ZWQ6ICR7dGVtcGxhdGUubWV0YWRhdGEubmFtZX1gKTtcbiAgfVxuXG4gIG92ZXJyaWRlIGFzeW5jIGRlbGV0ZShmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgc3VwZXIuZGVsZXRlKGZpbGVQYXRoKTtcblxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdURU1QTEFURV9ERUxFVEVEJyxcbiAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgIHNvdXJjZTogJ1RlbXBsYXRlTWFuYWdlci5kZWxldGUnLFxuICAgICAgZGV0YWlsczogYFRlbXBsYXRlIGRlbGV0ZWQ6ICR7cGF0aC5iYXNlbmFtZShmaWxlUGF0aCl9YFxuICAgIH0pO1xuXG4gICAgbG9nZ2VyLmluZm8oYFRlbXBsYXRlIGRlbGV0ZWQ6ICR7cGF0aC5iYXNlbmFtZShmaWxlUGF0aCl9YCk7XG4gIH1cblxuICBhc3luYyBjcmVhdGUoZGF0YToge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICAgIGNvbnRlbnQ/OiBzdHJpbmc7XG4gICAgaW5zdHJ1Y3Rpb25zPzogc3RyaW5nO1xuICAgIG1ldGFkYXRhPzogUGFydGlhbDxUZW1wbGF0ZU1ldGFkYXRhPjtcbiAgfSk6IFByb21pc2U8VGVtcGxhdGU+IHtcbiAgICAvLyBVc2Ugc3BlY2lhbGl6ZWQgdmFsaWRhdG9yIGZvciBpbnB1dCB2YWxpZGF0aW9uXG4gICAgY29uc3QgdmFsaWRhdGlvblJlc3VsdCA9IGF3YWl0IHRoaXMudmFsaWRhdG9yLnZhbGlkYXRlQ3JlYXRlKHtcbiAgICAgIG5hbWU6IGRhdGEubmFtZSxcbiAgICAgIGRlc2NyaXB0aW9uOiBkYXRhLmRlc2NyaXB0aW9uLFxuICAgICAgY29udGVudDogZGF0YS5jb250ZW50XG4gICAgfSk7XG5cbiAgICBpZiAoIXZhbGlkYXRpb25SZXN1bHQuaXNWYWxpZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBWYWxpZGF0aW9uIGZhaWxlZDogJHt2YWxpZGF0aW9uUmVzdWx0LmVycm9ycy5qb2luKCcsICcpfWApO1xuICAgIH1cblxuICAgIC8vIExvZyB3YXJuaW5ncyBpZiBhbnlcbiAgICBpZiAodmFsaWRhdGlvblJlc3VsdC53YXJuaW5ncyAmJiB2YWxpZGF0aW9uUmVzdWx0Lndhcm5pbmdzLmxlbmd0aCA+IDApIHtcbiAgICAgIGxvZ2dlci53YXJuKGBUZW1wbGF0ZSBjcmVhdGlvbiB3YXJuaW5nczogJHt2YWxpZGF0aW9uUmVzdWx0Lndhcm5pbmdzLmpvaW4oJywgJyl9YCk7XG4gICAgfVxuXG4gICAgLy8gR2V0IHNhbml0aXplZCBuYW1lIGZvciBmaWxlIG9wZXJhdGlvbnMgKHZhbGlkYXRvciBhbHJlYWR5IHZhbGlkYXRlZClcbiAgICBjb25zdCBuYW1lSW5wdXQgPSBkYXRhLm5hbWUgfHwgJ25ldy10ZW1wbGF0ZSc7XG4gICAgY29uc3QgbmFtZVJlc3VsdCA9IHRoaXMudmFsaWRhdGlvblNlcnZpY2UudmFsaWRhdGVBbmRTYW5pdGl6ZUlucHV0KG5hbWVJbnB1dCwge1xuICAgICAgbWF4TGVuZ3RoOiBTRUNVUklUWV9MSU1JVFMuTUFYX05BTUVfTEVOR1RILFxuICAgICAgYWxsb3dTcGFjZXM6IHRydWVcbiAgICB9KTtcbiAgICBjb25zdCBzYW5pdGl6ZWROYW1lID0gbmFtZVJlc3VsdC5zYW5pdGl6ZWRWYWx1ZSE7XG5cbiAgICAvLyBVc2UgaW5oZXJpdGVkIGdldEVsZW1lbnRGaWxlbmFtZSgpIGZvciBjb25zaXN0ZW50IGZpbGVuYW1lIG5vcm1hbGl6YXRpb25cbiAgICBjb25zdCBmaWxlbmFtZSA9IHRoaXMuZ2V0RWxlbWVudEZpbGVuYW1lKHNhbml0aXplZE5hbWUpO1xuXG4gICAgLy8gRklYOiBJc3N1ZSAjMjAgLSBDaGVjayBmb3IgZHVwbGljYXRlIGJlZm9yZSBjcmVhdGluZ1xuICAgIGNvbnN0IGV4aXN0aW5nVGVtcGxhdGVzID0gYXdhaXQgdGhpcy5saXN0KCk7XG4gICAgY29uc3QgZHVwbGljYXRlID0gZXhpc3RpbmdUZW1wbGF0ZXMuZmluZCh0ID0+XG4gICAgICB0Lm1ldGFkYXRhLm5hbWUudG9Mb3dlckNhc2UoKSA9PT0gc2FuaXRpemVkTmFtZS50b0xvd2VyQ2FzZSgpXG4gICAgKTtcblxuICAgIGlmIChkdXBsaWNhdGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgQSB0ZW1wbGF0ZSBuYW1lZCBcIiR7c2FuaXRpemVkTmFtZX1cIiBhbHJlYWR5IGV4aXN0c2ApO1xuICAgIH1cblxuICAgIGNvbnN0IHRlbXBsYXRlID0gbmV3IFRlbXBsYXRlKFxuICAgICAge1xuICAgICAgICAuLi5kYXRhLm1ldGFkYXRhLFxuICAgICAgICBuYW1lOiBzYW5pdGl6ZWROYW1lLFxuICAgICAgICBkZXNjcmlwdGlvbjogZGF0YS5kZXNjcmlwdGlvbiB8fCAnJ1xuICAgICAgfSxcbiAgICAgIGRhdGEuY29udGVudCB8fCAnJyxcbiAgICAgIHRoaXMubWV0YWRhdGFTZXJ2aWNlXG4gICAgKTtcbiAgICAvLyBTZXQgaW5zdHJ1Y3Rpb25zIChyZW5kZXJpbmcgZGlyZWN0aXZlcykgaWYgcHJvdmlkZWRcbiAgICBpZiAoZGF0YS5pbnN0cnVjdGlvbnMpIHtcbiAgICAgIHRlbXBsYXRlLmluc3RydWN0aW9ucyA9IGRhdGEuaW5zdHJ1Y3Rpb25zO1xuICAgIH1cblxuICAgIGF3YWl0IHRoaXMuc2F2ZSh0ZW1wbGF0ZSwgZmlsZW5hbWUpO1xuICAgIC8vIE5vdGU6IE5vIHJlbG9hZCgpIGhlcmUg4oCUIHNhdmUoKSBjYWNoZXMgdGhlIGVsZW1lbnQgY29ycmVjdGx5LlxuICAgIC8vIFNlZSBJc3N1ZSAjNDkxIGZvciB3aHkgUGVyc29uYU1hbmFnZXIncyByZWxvYWQtYWZ0ZXItY3JlYXRlIHdhcyByZW1vdmVkLlxuXG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogJ0VMRU1FTlRfQ1JFQVRFRCcsXG4gICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICBzb3VyY2U6ICdUZW1wbGF0ZU1hbmFnZXIuY3JlYXRlJyxcbiAgICAgIGRldGFpbHM6IGBUZW1wbGF0ZSBjcmVhdGVkOiAke3RlbXBsYXRlLm1ldGFkYXRhLm5hbWV9YFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHRlbXBsYXRlO1xuICB9XG5cbiAgYXN5bmMgaW1wb3J0RWxlbWVudChkYXRhOiBzdHJpbmcsIGZvcm1hdDogJ2pzb24nIHwgJ3lhbWwnIHwgJ21hcmtkb3duJyA9ICdtYXJrZG93bicpOiBQcm9taXNlPFRlbXBsYXRlPiB7XG4gICAgdHJ5IHtcbiAgICAgIGxldCBtZXRhZGF0YTogUGFydGlhbDxUZW1wbGF0ZU1ldGFkYXRhPjtcbiAgICAgIGxldCBjb250ZW50ID0gJyc7XG5cbiAgICAgIHN3aXRjaCAoZm9ybWF0KSB7XG4gICAgICAgIGNhc2UgJ2pzb24nOiB7XG4gICAgICAgICAgY29uc3QganNvbkRhdGEgPSB0aGlzLnNlcmlhbGl6YXRpb25TZXJ2aWNlLnBhcnNlSnNvbihkYXRhLCB7XG4gICAgICAgICAgICBzb3VyY2U6ICdUZW1wbGF0ZU1hbmFnZXIuaW1wb3J0RWxlbWVudCdcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBtZXRhZGF0YSA9IGF3YWl0IHRoaXMuc2FuaXRpemVNZXRhZGF0YShqc29uRGF0YS5tZXRhZGF0YSB8fCB7fSk7XG4gICAgICAgICAgY29udGVudCA9IGpzb25EYXRhLmNvbnRlbnQgfHwgJyc7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBjYXNlICd5YW1sJzoge1xuICAgICAgICAgIGNvbnN0IHBhcnNlZCA9IHRoaXMuc2VyaWFsaXphdGlvblNlcnZpY2UucGFyc2VGcm9udG1hdHRlcihkYXRhLCB7XG4gICAgICAgICAgICBtYXhZYW1sU2l6ZTogNjQgKiAxMDI0LFxuICAgICAgICAgICAgdmFsaWRhdGVDb250ZW50OiB0cnVlLFxuICAgICAgICAgICAgc291cmNlOiAnVGVtcGxhdGVNYW5hZ2VyLmltcG9ydEVsZW1lbnQnXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICBtZXRhZGF0YSA9IGF3YWl0IHRoaXMuc2FuaXRpemVNZXRhZGF0YShwYXJzZWQuZGF0YS5tZXRhZGF0YSB8fCBwYXJzZWQuZGF0YSk7XG4gICAgICAgICAgY29udGVudCA9IHBhcnNlZC5jb250ZW50IHx8ICcnO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG5cbiAgICAgICAgY2FzZSAnbWFya2Rvd24nOiB7XG4gICAgICAgICAgY29uc3QgbWRQYXJzZWQgPSB0aGlzLnNlcmlhbGl6YXRpb25TZXJ2aWNlLnBhcnNlRnJvbnRtYXR0ZXIoZGF0YSwge1xuICAgICAgICAgICAgbWF4WWFtbFNpemU6IDY0ICogMTAyNCxcbiAgICAgICAgICAgIHZhbGlkYXRlQ29udGVudDogdHJ1ZSxcbiAgICAgICAgICAgIHNvdXJjZTogJ1RlbXBsYXRlTWFuYWdlci5pbXBvcnRFbGVtZW50J1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIG1ldGFkYXRhID0gYXdhaXQgdGhpcy5zYW5pdGl6ZU1ldGFkYXRhKG1kUGFyc2VkLmRhdGEpO1xuICAgICAgICAgIGNvbnRlbnQgPSBtZFBhcnNlZC5jb250ZW50O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG5cbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFVuc3VwcG9ydGVkIGltcG9ydCBmb3JtYXQ6ICR7Zm9ybWF0fWApO1xuICAgICAgfVxuXG4gICAgICBjb25zdCB0ZW1wbGF0ZSA9IG5ldyBUZW1wbGF0ZShtZXRhZGF0YSwgY29udGVudCwgdGhpcy5tZXRhZGF0YVNlcnZpY2UpO1xuICAgICAgY29uc3QgdmFsaWRhdGlvbiA9IHRlbXBsYXRlLnZhbGlkYXRlKCk7XG4gICAgICBpZiAoIXZhbGlkYXRpb24udmFsaWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHRlbXBsYXRlOiAke3ZhbGlkYXRpb24uZXJyb3JzPy5tYXAoZSA9PiBlLm1lc3NhZ2UpLmpvaW4oJywgJyl9YCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0ZW1wbGF0ZTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKGBGYWlsZWQgdG8gaW1wb3J0IHRlbXBsYXRlOiAke2Vycm9yfWApO1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZXhwb3J0RWxlbWVudCh0ZW1wbGF0ZTogVGVtcGxhdGUsIGZvcm1hdDogJ2pzb24nIHwgJ3lhbWwnIHwgJ21hcmtkb3duJyA9ICdtYXJrZG93bicpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHRyeSB7XG4gICAgICBzd2l0Y2ggKGZvcm1hdCkge1xuICAgICAgICBjYXNlICdqc29uJzpcbiAgICAgICAgICByZXR1cm4gKHRlbXBsYXRlIGFzIGFueSkuc2VyaWFsaXplVG9KU09OID8gKHRlbXBsYXRlIGFzIGFueSkuc2VyaWFsaXplVG9KU09OKCkgOiB0ZW1wbGF0ZS5zZXJpYWxpemUoKTtcblxuICAgICAgICBjYXNlICd5YW1sJzoge1xuICAgICAgICAgIGNvbnN0IHlhbWxEYXRhID0ge1xuICAgICAgICAgICAgbWV0YWRhdGE6IHRlbXBsYXRlLm1ldGFkYXRhLFxuICAgICAgICAgICAgY29udGVudDogdGVtcGxhdGUuY29udGVudCxcbiAgICAgICAgICAgIGlkOiB0ZW1wbGF0ZS5pZCxcbiAgICAgICAgICAgIHZlcnNpb246IHRlbXBsYXRlLnZlcnNpb25cbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgcmV0dXJuIHRoaXMuc2VyaWFsaXphdGlvblNlcnZpY2UuZHVtcFlhbWwoeWFtbERhdGEsIHtcbiAgICAgICAgICAgIHNjaGVtYTogJ2pzb24nLCAgLy8gRml4ICM5MTQ6IHN0YW5kYXJkaXplIG9uIEpTT04gc2NoZW1hIGFjcm9zcyBhbGwgbWFuYWdlcnNcbiAgICAgICAgICAgIG5vUmVmczogdHJ1ZSxcbiAgICAgICAgICAgIHNvcnRLZXlzOiB0cnVlLFxuICAgICAgICAgICAgc2tpcEludmFsaWQ6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNhc2UgJ21hcmtkb3duJzoge1xuICAgICAgICAgIHJldHVybiB0aGlzLnNlcmlhbGl6YXRpb25TZXJ2aWNlLmNyZWF0ZUZyb250bWF0dGVyKFxuICAgICAgICAgICAgdGVtcGxhdGUubWV0YWRhdGEsXG4gICAgICAgICAgICB0ZW1wbGF0ZS5jb250ZW50LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBtZXRob2Q6ICdtYW51YWwnLFxuICAgICAgICAgICAgICBzY2hlbWE6ICdqc29uJywgIC8vIEZpeCAjOTE0OiBzdGFuZGFyZGl6ZSBvbiBKU09OIHNjaGVtYSBhY3Jvc3MgYWxsIG1hbmFnZXJzXG4gICAgICAgICAgICAgIGNsZWFuTWV0YWRhdGE6IHRydWUsXG4gICAgICAgICAgICAgIGNsZWFuaW5nU3RyYXRlZ3k6ICdyZW1vdmUtYm90aCcgIC8vIEZpeCAjOTEzOiBzdGFuZGFyZGl6ZSBhY3Jvc3MgYWxsIG1hbmFnZXJzXG4gICAgICAgICAgICB9XG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbnN1cHBvcnRlZCBleHBvcnQgZm9ybWF0OiAke2Zvcm1hdH1gKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKGBGYWlsZWQgdG8gZXhwb3J0IHRlbXBsYXRlOiAke2Vycm9yfWApO1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZmluZEJ5Q2F0ZWdvcnkoY2F0ZWdvcnk6IHN0cmluZyk6IFByb21pc2U8VGVtcGxhdGVbXT4ge1xuICAgIGNvbnN0IHNhbml0aXplZENhdGVnb3J5ID0gc2FuaXRpemVJbnB1dChjYXRlZ29yeSwgNTApO1xuICAgIGNvbnN0IHRlbXBsYXRlcyA9IGF3YWl0IHRoaXMubGlzdCgpO1xuICAgIHJldHVybiB0ZW1wbGF0ZXMuZmlsdGVyKHQgPT4gdC5tZXRhZGF0YS5jYXRlZ29yeSA9PT0gc2FuaXRpemVkQ2F0ZWdvcnkpO1xuICB9XG5cbiAgYXN5bmMgZmluZEJ5VGFnKHRhZzogc3RyaW5nKTogUHJvbWlzZTxUZW1wbGF0ZVtdPiB7XG4gICAgY29uc3Qgc2FuaXRpemVkVGFnID0gc2FuaXRpemVJbnB1dCh0YWcsIDUwKTtcbiAgICBjb25zdCB0ZW1wbGF0ZXMgPSBhd2FpdCB0aGlzLmxpc3QoKTtcbiAgICByZXR1cm4gdGVtcGxhdGVzLmZpbHRlcih0ID0+IHQubWV0YWRhdGEudGFncz8uaW5jbHVkZXMoc2FuaXRpemVkVGFnKSk7XG4gIH1cblxuICBhc3luYyBnZXRNb3N0VXNlZChsaW1pdDogbnVtYmVyID0gMTApOiBQcm9taXNlPFRlbXBsYXRlW10+IHtcbiAgICBjb25zdCBNSU5fTElNSVQgPSAxO1xuICAgIGNvbnN0IE1BWF9MSU1JVCA9IDEwMDtcbiAgICBjb25zdCB2YWxpZGF0ZWRMaW1