@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.
419 lines • 59.6 kB
JavaScript
/**
* AnthropicToDollhouseConverter - Converts multi-file Anthropic Skills to single-file DollhouseMCP skills
*
* This is the INVERSE of DollhouseToAnthropicConverter.
*
* Implements the reverse transformation:
* Anthropic Skill (directory with separated components) → DollhouseMCP Skill (single .md file)
*
* Algorithm (inverse of decomposition):
* 1. Read SKILL.md and extract minimal YAML frontmatter
* 2. Enrich YAML with DollhouseMCP fields (version, created, modified, tags, etc.)
* 3. Read all scripts/ files and embed as code blocks
* 4. Read all reference/ files and embed as documentation sections
* 5. Read all examples/ files and embed as example sections
* 6. Read all themes/ files and embed as templates
* 7. Combine all content into single .md file with rich frontmatter
* 8. Return single-file content
*
* SECURITY MODEL:
* - This is a FORMAT TRANSFORMER, not a security boundary
* - Preserves content fidelity - no modification, sanitization, or validation during conversion
* - YAML parsing uses CORE_SCHEMA to prevent deserialization attacks only
* - Output validation happens when user loads skill via SkillManager.load()
* - SkillManager.load() applies SecureYamlParser and full security validation
* - Converted skills must pass DollhouseMCP security checks before activation
*/
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as yaml from 'js-yaml';
import { SchemaMapper } from './SchemaMapper.js';
export class AnthropicToDollhouseConverter {
schemaMapper;
constructor() {
this.schemaMapper = new SchemaMapper();
}
/**
* Convert an Anthropic Skill directory to a single DollhouseMCP skill file
*
* INVERSE ALGORITHM:
* 1. Read SKILL.md and extract minimal YAML
* 2. Enrich YAML with DollhouseMCP fields
* 3. Read all scripts/ files and embed as code blocks
* 4. Read all reference/ files and embed as documentation sections
* 5. Read all examples/ files and embed as example sections
* 6. Read all themes/ files and embed as templates
* 7. Combine all content into single .md file
* 8. Return single-file content
*/
async convertSkill(skillDirPath, options) {
// Step 1: Read the Anthropic skill directory structure
// NOTE: No Unicode normalization - preserves content fidelity
// Output will be validated when loaded via SkillManager.load()
const skillData = await this.readAnthropicStructure(skillDirPath);
// Step 2: Check for preserved DollhouseMCP metadata, or enrich if not found
let enrichedMetadata;
if (skillData.metadata?.has('dollhouse.yaml')) {
// Use preserved metadata for perfect roundtrip
const preservedYAML = skillData.metadata.get('dollhouse.yaml');
// FIX (DMCP-SEC-005): Use CORE_SCHEMA to prevent YAML deserialization attacks
enrichedMetadata = yaml.load(preservedYAML, { schema: yaml.CORE_SCHEMA });
// Apply any custom metadata overrides
if (options?.customMetadata) {
Object.assign(enrichedMetadata, options.customMetadata);
}
}
else {
// Fall back to enrichment if no preserved metadata
enrichedMetadata = this.enrichMetadata(skillData.skillMD.metadata, options);
}
// Step 3-6: Combine all components
const combinedContent = this.combineComponents(skillData);
// Step 7: Create single .md file with rich frontmatter
const dollhouseSkill = this.createDollhouseSkill(enrichedMetadata, combinedContent);
return dollhouseSkill;
}
/**
* Convert in-memory Anthropic skill structure to DollhouseMCP format
*/
convertFromStructure(structure, options) {
// Parse SKILL.md
// NOTE: No Unicode normalization - preserves content fidelity
const { metadata, content } = this.parseSkillMD(structure['SKILL.md']);
// Convert structure to directory format
const skillData = this.buildSkillDataFromStructure(structure, metadata, content);
// Get enriched metadata (either preserved or generated)
const enrichedMetadata = this.getEnrichedMetadata(skillData, metadata, options);
// Combine components and create final skill
const combinedContent = this.combineComponents(skillData);
return this.createDollhouseSkill(enrichedMetadata, combinedContent);
}
/**
* Build skill data structure from Anthropic structure
* REFACTORED: Extracted to reduce cognitive complexity
*/
buildSkillDataFromStructure(structure, metadata, content) {
const skillData = {
skillMD: { metadata, content },
scripts: new Map(),
reference: new Map(),
examples: new Map(),
themes: new Map()
};
// Process all structure components
this.processStructureScripts(structure, skillData);
this.processStructureDirectory(structure['reference/'], skillData.reference);
this.processStructureDirectory(structure['examples/'], skillData.examples);
this.processStructureDirectory(structure['themes/'], skillData.themes);
this.processStructureMetadata(structure, skillData);
if (structure['LICENSE.txt']) {
skillData.license = structure['LICENSE.txt'];
}
return skillData;
}
/**
* Process scripts from structure
* REFACTORED: Extracted to reduce cognitive complexity
*/
processStructureScripts(structure, skillData) {
if (!structure['scripts/'])
return;
for (const [filename, content] of Object.entries(structure['scripts/'])) {
const language = this.inferLanguageFromFilename(filename);
const cleanContent = this.removeShebangAndHeaders(content);
skillData.scripts.set(filename, { content: cleanContent, language });
}
}
/**
* Process generic directory structure (reference, examples, themes)
* REFACTORED: Extracted to reduce cognitive complexity
*/
processStructureDirectory(sourceDir, targetMap) {
if (!sourceDir)
return;
for (const [filename, content] of Object.entries(sourceDir)) {
targetMap.set(filename, content);
}
}
/**
* Process metadata from structure
* REFACTORED: Extracted to reduce cognitive complexity
*/
processStructureMetadata(structure, skillData) {
if (!structure['metadata/'])
return;
skillData.metadata = new Map();
for (const [filename, content] of Object.entries(structure['metadata/'])) {
skillData.metadata.set(filename, content);
}
}
/**
* Get enriched metadata (preserved or generated)
* REFACTORED: Extracted to reduce cognitive complexity
*/
getEnrichedMetadata(skillData, metadata, options) {
// Check for preserved DollhouseMCP metadata
if (skillData.metadata?.has('dollhouse.yaml')) {
return this.loadPreservedMetadata(skillData.metadata.get('dollhouse.yaml'), options);
}
// Fall back to enrichment if no preserved metadata
return this.enrichMetadata(metadata, options);
}
/**
* Load preserved metadata from YAML
* REFACTORED: Extracted to reduce cognitive complexity
*/
loadPreservedMetadata(preservedYAML, options) {
// FIX (DMCP-SEC-005): Use CORE_SCHEMA to prevent YAML deserialization attacks
const enrichedMetadata = yaml.load(preservedYAML, { schema: yaml.CORE_SCHEMA });
// Apply any custom metadata overrides
if (options?.customMetadata) {
Object.assign(enrichedMetadata, options.customMetadata);
}
return enrichedMetadata;
}
/**
* Read Anthropic skill directory structure from disk
* REFACTORED: Simplified by extracting directory reading logic
*/
async readAnthropicStructure(skillDirPath) {
this.validateSkillDirectory(skillDirPath);
// Read and parse SKILL.md
const { metadata, content } = this.readSkillMD(skillDirPath);
// Initialize skill data structure
const skillData = {
skillMD: { metadata, content },
scripts: new Map(),
reference: new Map(),
examples: new Map(),
themes: new Map()
};
// Read all directory components
this.readScriptsDirectory(skillDirPath, skillData);
this.readFilesDirectory(skillDirPath, 'reference', skillData.reference);
this.readFilesDirectory(skillDirPath, 'examples', skillData.examples);
this.readFilesDirectory(skillDirPath, 'themes', skillData.themes);
this.readMetadataDirectory(skillDirPath, skillData);
this.readLicenseFile(skillDirPath, skillData);
return skillData;
}
/**
* Validate skill directory exists and has SKILL.md
* REFACTORED: Extracted to reduce cognitive complexity
*/
validateSkillDirectory(skillDirPath) {
if (!fs.existsSync(skillDirPath)) {
throw new Error(`Skill directory not found: ${skillDirPath}`);
}
const skillMDPath = path.join(skillDirPath, 'SKILL.md');
if (!fs.existsSync(skillMDPath)) {
throw new Error(`SKILL.md not found in ${skillDirPath}`);
}
}
/**
* Read and parse SKILL.md file
* REFACTORED: Extracted to reduce cognitive complexity
*/
readSkillMD(skillDirPath) {
const skillMDPath = path.join(skillDirPath, 'SKILL.md');
const skillMDContent = fs.readFileSync(skillMDPath, 'utf-8');
return this.parseSkillMD(skillMDContent);
}
/**
* Read scripts directory and process script files
* REFACTORED: Extracted to reduce cognitive complexity
*/
readScriptsDirectory(skillDirPath, skillData) {
const scriptsDir = path.join(skillDirPath, 'scripts');
if (!fs.existsSync(scriptsDir))
return;
const scriptFiles = fs.readdirSync(scriptsDir);
for (const filename of scriptFiles) {
const filePath = path.join(scriptsDir, filename);
const content = fs.readFileSync(filePath, 'utf-8');
const language = this.inferLanguageFromFilename(filename);
const cleanContent = this.removeShebangAndHeaders(content);
skillData.scripts.set(filename, { content: cleanContent, language });
}
}
/**
* Read generic files directory (reference, examples, themes)
* REFACTORED: Extracted to reduce cognitive complexity and reuse code
*/
readFilesDirectory(skillDirPath, dirName, targetMap) {
const dirPath = path.join(skillDirPath, dirName);
if (!fs.existsSync(dirPath))
return;
const files = fs.readdirSync(dirPath);
for (const filename of files) {
const filePath = path.join(dirPath, filename);
const content = fs.readFileSync(filePath, 'utf-8');
targetMap.set(filename, content);
}
}
/**
* Read metadata directory
* REFACTORED: Extracted to reduce cognitive complexity
*/
readMetadataDirectory(skillDirPath, skillData) {
const metadataDir = path.join(skillDirPath, 'metadata');
if (!fs.existsSync(metadataDir))
return;
skillData.metadata = new Map();
const metadataFiles = fs.readdirSync(metadataDir);
for (const filename of metadataFiles) {
const filePath = path.join(metadataDir, filename);
const content = fs.readFileSync(filePath, 'utf-8');
skillData.metadata.set(filename, content);
}
}
/**
* Read LICENSE.txt file if it exists
* REFACTORED: Extracted to reduce cognitive complexity
*/
readLicenseFile(skillDirPath, skillData) {
const licensePath = path.join(skillDirPath, 'LICENSE.txt');
if (fs.existsSync(licensePath)) {
skillData.license = fs.readFileSync(licensePath, 'utf-8');
}
}
/**
* Parse SKILL.md and extract metadata and content
*/
parseSkillMD(skillMDContent) {
const yamlMatch = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(skillMDContent);
if (!yamlMatch) {
throw new Error('No YAML frontmatter found in SKILL.md');
}
// FIX (DMCP-SEC-005): Use CORE_SCHEMA to prevent YAML deserialization attacks
const metadata = yaml.load(yamlMatch[1], { schema: yaml.CORE_SCHEMA });
const content = yamlMatch[2].trim();
return { metadata, content };
}
/**
* Enrich minimal Anthropic metadata with DollhouseMCP fields
*/
enrichMetadata(anthropicMeta, options) {
// Infer tags and category from name/description
const inferredTags = this.schemaMapper.inferTags(anthropicMeta.name, anthropicMeta.description);
const inferredCategory = this.schemaMapper.inferCategory(anthropicMeta.name, anthropicMeta.description);
// Use SchemaMapper to convert
const enriched = this.schemaMapper.anthropicToDollhouse(anthropicMeta, {
inferredTags,
inferredCategory,
inferredType: 'skill'
});
// Apply custom metadata overrides
if (options?.customMetadata) {
Object.assign(enriched, options.customMetadata);
}
return enriched;
}
/**
* Combine all Anthropic skill components into single markdown content
*/
combineComponents(skillData) {
const sections = [];
// Start with main content from SKILL.md
sections.push(skillData.skillMD.content);
// Add embedded scripts as code blocks
if (skillData.scripts.size > 0) {
sections.push('\n## Scripts\n');
for (const [filename, { content, language }] of skillData.scripts) {
const title = this.filenameToTitle(filename);
sections.push(`### ${title}\n`, '```' + language, content, '```\n');
}
}
// Add reference documentation sections
if (skillData.reference.size > 0) {
for (const [, content] of skillData.reference) {
sections.push('\n' + content);
}
}
// Add examples
if (skillData.examples.size > 0) {
sections.push('\n## Examples\n');
for (const [, content] of skillData.examples) {
sections.push(content, '\n');
}
}
// Add themes/templates
if (skillData.themes.size > 0) {
sections.push('\n## Templates\n');
for (const [, content] of skillData.themes) {
sections.push(content, '\n');
}
}
return sections.join('\n').trim();
}
/**
* Create final DollhouseMCP skill with rich frontmatter
*/
createDollhouseSkill(metadata, content) {
const yamlString = yaml.dump(metadata);
return `---\n${yamlString}---\n\n${content}\n`;
}
/**
* Infer programming language from filename
*/
inferLanguageFromFilename(filename) {
const ext = path.extname(filename).toLowerCase();
const languageMap = {
'.sh': 'bash',
'.bash': 'bash',
'.py': 'python',
'.js': 'javascript',
'.ts': 'typescript',
'.rb': 'ruby',
'.pl': 'perl',
'.php': 'php'
};
return languageMap[ext] || 'text';
}
/**
* Remove shebang and auto-generated headers from extracted scripts
*/
removeShebangAndHeaders(content) {
const lines = content.split('\n');
let startIndex = 0;
// Skip shebang
if (lines[0]?.startsWith('#!')) {
startIndex = 1;
}
// Skip auto-generated header comments
if (lines[startIndex]?.startsWith('# Extracted script')) {
startIndex++;
}
// Skip empty lines after headers
while (startIndex < lines.length && lines[startIndex]?.trim() === '') {
startIndex++;
}
return lines.slice(startIndex).join('\n').trim();
}
/**
* Convert filename to readable title
*/
filenameToTitle(filename) {
// Remove extension
const nameWithoutExt = path.basename(filename, path.extname(filename));
// Convert hyphens/underscores to spaces and capitalize
return nameWithoutExt
.replaceAll(/[-_]/g, ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
/**
* Write DollhouseMCP skill to disk
*/
async writeToFile(skillContent, outputPath) {
// Ensure output directory exists
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// Write skill file
fs.writeFileSync(outputPath, skillContent, 'utf-8');
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQW50aHJvcGljVG9Eb2xsaG91c2VDb252ZXJ0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29udmVydGVycy9BbnRocm9waWNUb0RvbGxob3VzZUNvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQzlCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxJQUFJLE1BQU0sU0FBUyxDQUFDO0FBQ2hDLE9BQU8sRUFBRSxZQUFZLEVBQStELE1BQU0sbUJBQW1CLENBQUM7QUFnQjlHLE1BQU0sT0FBTyw2QkFBNkI7SUFDckIsWUFBWSxDQUFlO0lBRTVDO1FBQ0ksSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLFlBQVksRUFBRSxDQUFDO0lBQzNDLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxLQUFLLENBQUMsWUFBWSxDQUFDLFlBQW9CLEVBQUUsT0FHeEM7UUFDRyx1REFBdUQ7UUFDdkQsOERBQThEO1FBQzlELCtEQUErRDtRQUMvRCxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUVsRSw0RUFBNEU7UUFDNUUsSUFBSSxnQkFBMkMsQ0FBQztRQUNoRCxJQUFJLFNBQVMsQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztZQUM1QywrQ0FBK0M7WUFDL0MsTUFBTSxhQUFhLEdBQUcsU0FBUyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUUsQ0FBQztZQUNoRSw4RUFBOEU7WUFDOUUsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUE4QixDQUFD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Q3BDLG1CQUFtQjtRQUNuQixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFFdkUsdURBQXVEO1FBQ3ZELE9BQU8sY0FBYzthQUNoQixVQUFVLENBQUMsT0FBTyxFQUFFLEdBQUcsQ0FBQzthQUN4QixLQUFLLENBQUMsR0FBRyxDQUFDO2FBQ1YsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLEVBQUUsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ3pELElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNuQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsV0FBVyxDQUFDLFlBQW9CLEVBQUUsVUFBa0I7UUFDdEQsaUNBQWlDO1FBQ2pDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0MsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztZQUM1QixFQUFFLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ2pELENBQUM7UUFFRCxtQkFBbUI7UUFDbkIsRUFBRSxDQUFDLGFBQWEsQ0FBQyxVQUFVLEVBQUUsWUFBWSxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3hELENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQW50aHJvcGljVG9Eb2xsaG91c2VDb252ZXJ0ZXIgLSBDb252ZXJ0cyBtdWx0aS1maWxlIEFudGhyb3BpYyBTa2lsbHMgdG8gc2luZ2xlLWZpbGUgRG9sbGhvdXNlTUNQIHNraWxsc1xuICpcbiAqIFRoaXMgaXMgdGhlIElOVkVSU0Ugb2YgRG9sbGhvdXNlVG9BbnRocm9waWNDb252ZXJ0ZXIuXG4gKlxuICogSW1wbGVtZW50cyB0aGUgcmV2ZXJzZSB0cmFuc2Zvcm1hdGlvbjpcbiAqIEFudGhyb3BpYyBTa2lsbCAoZGlyZWN0b3J5IHdpdGggc2VwYXJhdGVkIGNvbXBvbmVudHMpIOKGkiBEb2xsaG91c2VNQ1AgU2tpbGwgKHNpbmdsZSAubWQgZmlsZSlcbiAqXG4gKiBBbGdvcml0aG0gKGludmVyc2Ugb2YgZGVjb21wb3NpdGlvbik6XG4gKiAxLiBSZWFkIFNLSUxMLm1kIGFuZCBleHRyYWN0IG1pbmltYWwgWUFNTCBmcm9udG1hdHRlclxuICogMi4gRW5yaWNoIFlBTUwgd2l0aCBEb2xsaG91c2VNQ1AgZmllbGRzICh2ZXJzaW9uLCBjcmVhdGVkLCBtb2RpZmllZCwgdGFncywgZXRjLilcbiAqIDMuIFJlYWQgYWxsIHNjcmlwdHMvIGZpbGVzIGFuZCBlbWJlZCBhcyBjb2RlIGJsb2Nrc1xuICogNC4gUmVhZCBhbGwgcmVmZXJlbmNlLyBmaWxlcyBhbmQgZW1iZWQgYXMgZG9jdW1lbnRhdGlvbiBzZWN0aW9uc1xuICogNS4gUmVhZCBhbGwgZXhhbXBsZXMvIGZpbGVzIGFuZCBlbWJlZCBhcyBleGFtcGxlIHNlY3Rpb25zXG4gKiA2LiBSZWFkIGFsbCB0aGVtZXMvIGZpbGVzIGFuZCBlbWJlZCBhcyB0ZW1wbGF0ZXNcbiAqIDcuIENvbWJpbmUgYWxsIGNvbnRlbnQgaW50byBzaW5nbGUgLm1kIGZpbGUgd2l0aCByaWNoIGZyb250bWF0dGVyXG4gKiA4LiBSZXR1cm4gc2luZ2xlLWZpbGUgY29udGVudFxuICpcbiAqIFNFQ1VSSVRZIE1PREVMOlxuICogLSBUaGlzIGlzIGEgRk9STUFUIFRSQU5TRk9STUVSLCBub3QgYSBzZWN1cml0eSBib3VuZGFyeVxuICogLSBQcmVzZXJ2ZXMgY29udGVudCBmaWRlbGl0eSAtIG5vIG1vZGlmaWNhdGlvbiwgc2FuaXRpemF0aW9uLCBvciB2YWxpZGF0aW9uIGR1cmluZyBjb252ZXJzaW9uXG4gKiAtIFlBTUwgcGFyc2luZyB1c2VzIENPUkVfU0NIRU1BIHRvIHByZXZlbnQgZGVzZXJpYWxpemF0aW9uIGF0dGFja3Mgb25seVxuICogLSBPdXRwdXQgdmFsaWRhdGlvbiBoYXBwZW5zIHdoZW4gdXNlciBsb2FkcyBza2lsbCB2aWEgU2tpbGxNYW5hZ2VyLmxvYWQoKVxuICogLSBTa2lsbE1hbmFnZXIubG9hZCgpIGFwcGxpZXMgU2VjdXJlWWFtbFBhcnNlciBhbmQgZnVsbCBzZWN1cml0eSB2YWxpZGF0aW9uXG4gKiAtIENvbnZlcnRlZCBza2lsbHMgbXVzdCBwYXNzIERvbGxob3VzZU1DUCBzZWN1cml0eSBjaGVja3MgYmVmb3JlIGFjdGl2YXRpb25cbiAqL1xuXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdub2RlOmZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCAqIGFzIHlhbWwgZnJvbSAnanMteWFtbCc7XG5pbXBvcnQgeyBTY2hlbWFNYXBwZXIsIHR5cGUgQW50aHJvcGljU2tpbGxNZXRhZGF0YSwgdHlwZSBEb2xsaG91c2VNQ1BTa2lsbE1ldGFkYXRhIH0gZnJvbSAnLi9TY2hlbWFNYXBwZXIuanMnO1xuaW1wb3J0IHR5cGUgeyBBbnRocm9waWNTa2lsbFN0cnVjdHVyZSB9IGZyb20gJy4vRG9sbGhvdXNlVG9BbnRocm9waWNDb252ZXJ0ZXIuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEFudGhyb3BpY1NraWxsRGlyZWN0b3J5IHtcbiAgICBza2lsbE1EOiB7XG4gICAgICAgIG1ldGFkYXRhOiBBbnRocm9waWNTa2lsbE1ldGFkYXRhO1xuICAgICAgICBjb250ZW50OiBzdHJpbmc7XG4gICAgfTtcbiAgICBzY3JpcHRzOiBNYXA8c3RyaW5nLCB7IGNvbnRlbnQ6IHN0cmluZzsgbGFuZ3VhZ2U6IHN0cmluZyB9PjtcbiAgICByZWZlcmVuY2U6IE1hcDxzdHJpbmcsIHN0cmluZz47XG4gICAgZXhhbXBsZXM6IE1hcDxzdHJpbmcsIHN0cmluZz47XG4gICAgdGhlbWVzOiBNYXA8c3RyaW5nLCBzdHJpbmc+O1xuICAgIG1ldGFkYXRhPzogTWFwPHN0cmluZywgc3RyaW5nPjtcbiAgICBsaWNlbnNlPzogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgQW50aHJvcGljVG9Eb2xsaG91c2VDb252ZXJ0ZXIge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc2NoZW1hTWFwcGVyOiBTY2hlbWFNYXBwZXI7XG5cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zY2hlbWFNYXBwZXIgPSBuZXcgU2NoZW1hTWFwcGVyKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29udmVydCBhbiBBbnRocm9waWMgU2tpbGwgZGlyZWN0b3J5IHRvIGEgc2luZ2xlIERvbGxob3VzZU1DUCBza2lsbCBmaWxlXG4gICAgICpcbiAgICAgKiBJTlZFUlNFIEFMR09SSVRITTpcbiAgICAgKiAxLiBSZWFkIFNLSUxMLm1kIGFuZCBleHRyYWN0IG1pbmltYWwgWUFNTFxuICAgICAqIDIuIEVucmljaCBZQU1MIHdpdGggRG9sbGhvdXNlTUNQIGZpZWxkc1xuICAgICAqIDMuIFJlYWQgYWxsIHNjcmlwdHMvIGZpbGVzIGFuZCBlbWJlZCBhcyBjb2RlIGJsb2Nrc1xuICAgICAqIDQuIFJlYWQgYWxsIHJlZmVyZW5jZS8gZmlsZXMgYW5kIGVtYmVkIGFzIGRvY3VtZW50YXRpb24gc2VjdGlvbnNcbiAgICAgKiA1LiBSZWFkIGFsbCBleGFtcGxlcy8gZmlsZXMgYW5kIGVtYmVkIGFzIGV4YW1wbGUgc2VjdGlvbnNcbiAgICAgKiA2LiBSZWFkIGFsbCB0aGVtZXMvIGZpbGVzIGFuZCBlbWJlZCBhcyB0ZW1wbGF0ZXNcbiAgICAgKiA3LiBDb21iaW5lIGFsbCBjb250ZW50IGludG8gc2luZ2xlIC5tZCBmaWxlXG4gICAgICogOC4gUmV0dXJuIHNpbmdsZS1maWxlIGNvbnRlbnRcbiAgICAgKi9cbiAgICBhc3luYyBjb252ZXJ0U2tpbGwoc2tpbGxEaXJQYXRoOiBzdHJpbmcsIG9wdGlvbnM/OiB7XG4gICAgICAgIHByZXNlcnZlU291cmNlPzogYm9vbGVhbjtcbiAgICAgICAgY3VzdG9tTWV0YWRhdGE/OiBQYXJ0aWFsPERvbGxob3VzZU1DUFNraWxsTWV0YWRhdGE+O1xuICAgIH0pOiBQcm9taXNlPHN0cmluZz4ge1xuICAgICAgICAvLyBTdGVwIDE6IFJlYWQgdGhlIEFudGhyb3BpYyBza2lsbCBkaXJlY3Rvcnkgc3RydWN0dXJlXG4gICAgICAgIC8vIE5PVEU6IE5vIFVuaWNvZGUgbm9ybWFsaXphdGlvbiAtIHByZXNlcnZlcyBjb250ZW50IGZpZGVsaXR5XG4gICAgICAgIC8vIE91dHB1dCB3aWxsIGJlIHZhbGlkYXRlZCB3aGVuIGxvYWRlZCB2aWEgU2tpbGxNYW5hZ2VyLmxvYWQoKVxuICAgICAgICBjb25zdCBza2lsbERhdGEgPSBhd2FpdCB0aGlzLnJlYWRBbnRocm9waWNTdHJ1Y3R1cmUoc2tpbGxEaXJQYXRoKTtcblxuICAgICAgICAvLyBTdGVwIDI6IENoZWNrIGZvciBwcmVzZXJ2ZWQgRG9sbGhvdXNlTUNQIG1ldGFkYXRhLCBvciBlbnJpY2ggaWYgbm90IGZvdW5kXG4gICAgICAgIGxldCBlbnJpY2hlZE1ldGFkYXRhOiBEb2xsaG91c2VNQ1BTa2lsbE1ldGFkYXRhO1xuICAgICAgICBpZiAoc2tpbGxEYXRhLm1ldGFkYXRhPy5oYXMoJ2RvbGxob3VzZS55YW1sJykpIHtcbiAgICAgICAgICAgIC8vIFVzZSBwcmVzZXJ2ZWQgbWV0YWRhdGEgZm9yIHBlcmZlY3Qgcm91bmR0cmlwXG4gICAgICAgICAgICBjb25zdCBwcmVzZXJ2ZWRZQU1MID0gc2tpbGxEYXRhLm1ldGFkYXRhLmdldCgnZG9sbGhvdXNlLnlhbWwnKSE7XG4gICAgICAgICAgICAvLyBGSVggKERNQ1AtU0VDLTAwNSk6IFVzZSBDT1JFX1NDSEVNQSB0byBwcmV2ZW50IFlBTUwgZGVzZXJpYWxpemF0aW9uIGF0dGFja3NcbiAgICAgICAgICAgIGVucmljaGVkTWV0YWRhdGEgPSB5YW1sLmxvYWQocHJlc2VydmVkWUFNTCwgeyBzY2hlbWE6IHlhbWwuQ09SRV9TQ0hFTUEgfSkgYXMgRG9sbGhvdXNlTUNQU2tpbGxNZXRhZGF0YTtcbiAgICAgICAgICAgIC8vIEFwcGx5IGFueSBjdXN0b20gbWV0YWRhdGEgb3ZlcnJpZGVzXG4gICAgICAgICAgICBpZiAob3B0aW9ucz8uY3VzdG9tTWV0YWRhdGEpIHtcbiAgICAgICAgICAgICAgICBPYmplY3QuYXNzaWduKGVucmljaGVkTWV0YWRhdGEsIG9wdGlvbnMuY3VzdG9tTWV0YWRhdGEpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gRmFsbCBiYWNrIHRvIGVucmljaG1lbnQgaWYgbm8gcHJlc2VydmVkIG1ldGFkYXRhXG4gICAgICAgICAgICBlbnJpY2hlZE1ldGFkYXRhID0gdGhpcy5lbnJpY2hNZXRhZGF0YShza2lsbERhdGEuc2tpbGxNRC5tZXRhZGF0YSwgb3B0aW9ucyk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTdGVwIDMtNjogQ29tYmluZSBhbGwgY29tcG9uZW50c1xuICAgICAgICBjb25zdCBjb21iaW5lZENvbnRlbnQgPSB0aGlzLmNvbWJpbmVDb21wb25lbnRzKHNraWxsRGF0YSk7XG5cbiAgICAgICAgLy8gU3RlcCA3OiBDcmVhdGUgc2luZ2xlIC5tZCBmaWxlIHdpdGggcmljaCBmcm9udG1hdHRlclxuICAgICAgICBjb25zdCBkb2xsaG91c2VTa2lsbCA9IHRoaXMuY3JlYXRlRG9sbGhvdXNlU2tpbGwoZW5yaWNoZWRNZXRhZGF0YSwgY29tYmluZWRDb250ZW50KTtcblxuICAgICAgICByZXR1cm4gZG9sbGhvdXNlU2tpbGw7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29udmVydCBpbi1tZW1vcnkgQW50aHJvcGljIHNraWxsIHN0cnVjdHVyZSB0byBEb2xsaG91c2VNQ1AgZm9ybWF0XG4gICAgICovXG4gICAgY29udmVydEZyb21TdHJ1Y3R1cmUoc3RydWN0dXJlOiBBbnRocm9waWNTa2lsbFN0cnVjdHVyZSwgb3B0aW9ucz86IHtcbiAgICAgICAgcHJlc2VydmVTb3VyY2U/OiBib29sZWFuO1xuICAgICAgICBjdXN0b21NZXRhZGF0YT86IFBhcnRpYWw8RG9sbGhvdXNlTUNQU2tpbGxNZXRhZGF0YT47XG4gICAgfSk6IHN0cmluZyB7XG4gICAgICAgIC8vIFBhcnNlIFNLSUxMLm1kXG4gICAgICAgIC8vIE5PVEU6IE5vIFVuaWNvZGUgbm9ybWFsaXphdGlvbiAtIHByZXNlcnZlcyBjb250ZW50IGZpZGVsaXR5XG4gICAgICAgIGNvbnN0IHsgbWV0YWRhdGEsIGNvbnRlbnQgfSA9IHRoaXMucGFyc2VTa2lsbE1EKHN0cnVjdHVyZVsnU0tJTEwubWQnXSk7XG5cbiAgICAgICAgLy8gQ29udmVydCBzdHJ1Y3R1cmUgdG8gZGlyZWN0b3J5IGZvcm1hdFxuICAgICAgICBjb25zdCBza2lsbERhdGEgPSB0aGlzLmJ1aWxkU2tpbGxEYXRhRnJvbVN0cnVjdHVyZShzdHJ1Y3R1cmUsIG1ldGFkYXRhLCBjb250ZW50KTtcblxuICAgICAgICAvLyBHZXQgZW5yaWNoZWQgbWV0YWRhdGEgKGVpdGhlciBwcmVzZXJ2ZWQgb3IgZ2VuZXJhdGVkKVxuICAgICAgICBjb25zdCBlbnJpY2hlZE1ldGFkYXRhID0gdGhpcy5nZXRFbnJpY2hlZE1ldGFkYXRhKHNraWxsRGF0YSwgbWV0YWRhdGEsIG9wdGlvbnMpO1xuXG4gICAgICAgIC8vIENvbWJpbmUgY29tcG9uZW50cyBhbmQgY3JlYXRlIGZpbmFsIHNraWxsXG4gICAgICAgIGNvbnN0IGNvbWJpbmVkQ29udGVudCA9IHRoaXMuY29tYmluZUNvbXBvbmVudHMoc2tpbGxEYXRhKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlRG9sbGhvdXNlU2tpbGwoZW5yaWNoZWRNZXRhZGF0YSwgY29tYmluZWRDb250ZW50KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBCdWlsZCBza2lsbCBkYXRhIHN0cnVjdHVyZSBmcm9tIEFudGhyb3BpYyBzdHJ1Y3R1cmVcbiAgICAgKiBSRUZBQ1RPUkVEOiBFeHRyYWN0ZWQgdG8gcmVkdWNlIGNvZ25pdGl2ZSBjb21wbGV4aXR5XG4gICAgICovXG4gICAgcHJpdmF0ZSBidWlsZFNraWxsRGF0YUZyb21TdHJ1Y3R1cmUoXG4gICAgICAgIHN0cnVjdHVyZTogQW50aHJvcGljU2tpbGxTdHJ1Y3R1cmUsXG4gICAgICAgIG1ldGFkYXRhOiBBbnRocm9waWNTa2lsbE1ldGFkYXRhLFxuICAgICAgICBjb250ZW50OiBzdHJpbmdcbiAgICApOiBBbnRocm9waWNTa2lsbERpcmVjdG9yeSB7XG4gICAgICAgIGNvbnN0IHNraWxsRGF0YTogQW50aHJvcGljU2tpbGxEaXJlY3RvcnkgPSB7XG4gICAgICAgICAgICBza2lsbE1EOiB7IG1ldGFkYXRhLCBjb250ZW50IH0sXG4gICAgICAgICAgICBzY3JpcHRzOiBuZXcgTWFwKCksXG4gICAgICAgICAgICByZWZlcmVuY2U6IG5ldyBNYXAoKSxcbiAgICAgICAgICAgIGV4YW1wbGVzOiBuZXcgTWFwKCksXG4gICAgICAgICAgICB0aGVtZXM6IG5ldyBNYXAoKVxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIFByb2Nlc3MgYWxsIHN0cnVjdHVyZSBjb21wb25lbnRzXG4gICAgICAgIHRoaXMucHJvY2Vzc1N0cnVjdHVyZVNjcmlwdHMoc3RydWN0dXJlLCBza2lsbERhdGEpO1xuICAgICAgICB0aGlzLnByb2Nlc3NTdHJ1Y3R1cmVEaXJlY3Rvcnkoc3RydWN0dXJlWydyZWZlcmVuY2UvJ10sIHNraWxsRGF0YS5yZWZlcmVuY2UpO1xuICAgICAgICB0aGlzLnByb2Nlc3NTdHJ1Y3R1cmVEaXJlY3Rvcnkoc3RydWN0dXJlWydleGFtcGxlcy8nXSwgc2tpbGxEYXRhLmV4YW1wbGVzKTtcbiAgICAgICAgdGhpcy5wcm9jZXNzU3RydWN0dXJlRGlyZWN0b3J5KHN0cnVjdHVyZVsndGhlbWVzLyddLCBza2lsbERhdGEudGhlbWVzKTtcbiAgICAgICAgdGhpcy5wcm9jZXNzU3RydWN0dXJlTWV0YWRhdGEoc3RydWN0dXJlLCBza2lsbERhdGEpO1xuXG4gICAgICAgIGlmIChzdHJ1Y3R1cmVbJ0xJQ0VOU0UudHh0J10pIHtcbiAgICAgICAgICAgIHNraWxsRGF0YS5saWNlbnNlID0gc3RydWN0dXJlWydMSUNFTlNFLnR4dCddO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHNraWxsRGF0YTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBQcm9jZXNzIHNjcmlwdHMgZnJvbSBzdHJ1Y3R1cmVcbiAgICAgKiBSRUZBQ1RPUkVEOiBFeHRyYWN0ZWQgdG8gcmVkdWNlIGNvZ25pdGl2ZSBjb21wbGV4aXR5XG4gICAgICovXG4gICAgcHJpdmF0ZSBwcm9jZXNzU3RydWN0dXJlU2NyaXB0cyhzdHJ1Y3R1cmU6IEFudGhyb3BpY1NraWxsU3RydWN0dXJlLCBza2lsbERhdGE6IEFudGhyb3BpY1NraWxsRGlyZWN0b3J5KTogdm9pZCB7XG4gICAgICAgIGlmICghc3RydWN0dXJlWydzY3JpcHRzLyddKSByZXR1cm47XG5cbiAgICAgICAgZm9yIChjb25zdCBbZmlsZW5hbWUsIGNvbnRlbnRdIG9mIE9iamVjdC5lbnRyaWVzKHN0cnVjdHVyZVsnc2NyaXB0cy8nXSkpIHtcbiAgICAgICAgICAgIGNvbnN0IGxhbmd1YWdlID0gdGhpcy5pbmZlckxhbmd1YWdlRnJvbUZpbGVuYW1lKGZpbGVuYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGNsZWFuQ29udGVudCA9IHRoaXMucmVtb3ZlU2hlYmFuZ0FuZEhlYWRlcnMoY29udGVudCk7XG4gICAgICAgICAgICBza2lsbERhdGEuc2NyaXB0cy5zZXQoZmlsZW5hbWUsIHsgY29udGVudDogY2xlYW5Db250ZW50LCBsYW5ndWFnZSB9KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFByb2Nlc3MgZ2VuZXJpYyBkaXJlY3Rvcnkgc3RydWN0dXJlIChyZWZlcmVuY2UsIGV4YW1wbGVzLCB0aGVtZXMpXG4gICAgICogUkVGQUNUT1JFRDogRXh0cmFjdGVkIHRvIHJlZHVjZSBjb2duaXRpdmUgY29tcGxleGl0eVxuICAgICAqL1xuICAgIHByaXZhdGUgcHJvY2Vzc1N0cnVjdHVyZURpcmVjdG9yeShcbiAgICAgICAgc291cmNlRGlyOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHwgdW5kZWZpbmVkLFxuICAgICAgICB0YXJnZXRNYXA6IE1hcDxzdHJpbmcsIHN0cmluZz5cbiAgICApOiB2b2lkIHtcbiAgICAgICAgaWYgKCFzb3VyY2VEaXIpIHJldHVybjtcblxuICAgICAgICBmb3IgKGNvbnN0IFtmaWxlbmFtZSwgY29udGVudF0gb2YgT2JqZWN0LmVudHJpZXMoc291cmNlRGlyKSkge1xuICAgICAgICAgICAgdGFyZ2V0TWFwLnNldChmaWxlbmFtZSwgY29udGVudCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBQcm9jZXNzIG1ldGFkYXRhIGZyb20gc3RydWN0dXJlXG4gICAgICogUkVGQUNUT1JFRDogRXh0cmFjdGVkIHRvIHJlZHVjZSBjb2duaXRpdmUgY29tcGxleGl0eVxuICAgICAqL1xuICAgIHByaXZhdGUgcHJvY2Vzc1N0cnVjdHVyZU1ldGFkYXRhKHN0cnVjdHVyZTogQW50aHJvcGljU2tpbGxTdHJ1Y3R1cmUsIHNraWxsRGF0YTogQW50aHJvcGljU2tpbGxEaXJlY3RvcnkpOiB2b2lkIHtcbiAgICAgICAgaWYgKCFzdHJ1Y3R1cmVbJ21ldGFkYXRhLyddKSByZXR1cm47XG5cbiAgICAgICAgc2tpbGxEYXRhLm1ldGFkYXRhID0gbmV3IE1hcCgpO1xuICAgICAgICBmb3IgKGNvbnN0IFtmaWxlbmFtZSwgY29udGVudF0gb2YgT2JqZWN0LmVudHJpZXMoc3RydWN0dXJlWydtZXRhZGF0YS8nXSkpIHtcbiAgICAgICAgICAgIHNraWxsRGF0YS5tZXRhZGF0YS5zZXQoZmlsZW5hbWUsIGNvbnRlbnQpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IGVucmljaGVkIG1ldGFkYXRhIChwcmVzZXJ2ZWQgb3IgZ2VuZXJhdGVkKVxuICAgICAqIFJFRkFDVE9SRUQ6IEV4dHJhY3RlZCB0byByZWR1Y2UgY29nbml0aXZlIGNvbXBsZXhpdHlcbiAgICAgKi9cbiAgICBwcml2YXRlIGdldEVucmljaGVkTWV0YWRhdGEoXG4gICAgICAgIHNraWxsRGF0YTogQW50aHJvcGljU2tpbGxEaXJlY3RvcnksXG4gICAgICAgIG1ldGFkYXRhOiBBbnRocm9waWNTa2lsbE1ldGFkYXRhLFxuICAgICAgICBvcHRpb25zPzoge1xuICAgICAgICAgICAgcHJlc2VydmVTb3VyY2U/OiBib29sZWFuO1xuICAgICAgICAgICAgY3VzdG9tTWV0YWRhdGE/OiBQYXJ0aWFsPERvbGxob3VzZU1DUFNraWxsTWV0YWRhdGE+O1xuICAgICAgICB9XG4gICAgKTogRG9sbGhvdXNlTUNQU2tpbGxNZXRhZGF0YSB7XG4gICAgICAgIC8vIENoZWNrIGZvciBwcmVzZXJ2ZWQgRG9sbGhvdXNlTUNQIG1ldGFkYXRhXG4gICAgICAgIGlmIChza2lsbERhdGEubWV0YWRhdGE/LmhhcygnZG9sbGhvdXNlLnlhbWwnKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMubG9hZFByZXNlcnZlZE1ldGFkYXRhKHNraWxsRGF0YS5tZXRhZGF0YS5nZXQoJ2RvbGxob3VzZS55YW1sJykhLCBvcHRpb25zKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEZhbGwgYmFjayB0byBlbnJpY2htZW50IGlmIG5vIHByZXNlcnZlZCBtZXRhZGF0YVxuICAgICAgICByZXR1cm4gdGhpcy5lbnJpY2hNZXRhZGF0YShtZXRhZGF0YSwgb3B0aW9ucyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9hZCBwcmVzZXJ2ZWQgbWV0YWRhdGEgZnJvbSBZQU1MXG4gICAgICogUkVGQUNUT1JFRDogRXh0cmFjdGVkIHRvIHJlZHVjZSBjb2duaXRpdmUgY29tcGxleGl0eVxuICAgICAqL1xuICAgIHByaXZhdGUgbG9hZFByZXNlcnZlZE1ldGFkYXRhKFxuICAgICAgICBwcmVzZXJ2ZWRZQU1MOiBzdHJpbmcsXG4gICAgICAgIG9wdGlvbnM/OiB7XG4gICAgICAgICAgICBjdXN0b21NZXRhZGF0YT86IFBhcnRpYWw8RG9sbGhvdXNlTUNQU2tpbGxNZXRhZGF0YT47XG4gICAgICAgIH1cbiAgICApOiBEb2xsaG91c2VNQ1BTa2lsbE1ldGFkYXRhIHtcbiAgICAgICAgLy8gRklYIChETUNQLVNFQy0wMDUpOiBVc2UgQ09SRV9TQ0hFTUEgdG8gcHJldmVudCBZQU1MIGRlc2VyaWFsaXphdGlvbiBhdHRhY2tzXG4gICAgICAgIGNvbnN0IGVucmljaGVkTWV0YWRhdGEgPSB5YW1sLmxvYWQocHJlc2VydmVkWUFNTCwgeyBzY2hlbWE6IHlhbWwuQ09SRV9TQ0hFTUEgfSkgYXMgRG9sbGhvdXNlTUNQU2tpbGxNZXRhZGF0YTtcblxuICAgICAgICAvLyBBcHBseSBhbnkgY3VzdG9tIG1ldGFkYXRhIG92ZXJyaWRlc1xuICAgICAgICBpZiAob3B0aW9ucz8uY3VzdG9tTWV0YWRhdGEpIHtcbiAgICAgICAgICAgIE9iamVjdC5hc3NpZ24oZW5yaWNoZWRNZXRhZGF0YSwgb3B0aW9ucy5jdXN0b21NZXRhZGF0YSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gZW5yaWNoZWRNZXRhZGF0YTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWFkIEFudGhyb3BpYyBza2lsbCBkaXJlY3Rvcnkgc3RydWN0dXJlIGZyb20gZGlza1xuICAgICAqIFJFRkFDVE9SRUQ6IFNpbXBsaWZpZWQgYnkgZXh0cmFjdGluZyBkaXJlY3RvcnkgcmVhZGluZyBsb2dpY1xuICAgICAqL1xuICAgIGFzeW5jIHJlYWRBbnRocm9waWNTdHJ1Y3R1cmUoc2tpbGxEaXJQYXRoOiBzdHJpbmcpOiBQcm9taXNlPEFudGhyb3BpY1NraWxsRGlyZWN0b3J5PiB7XG4gICAgICAgIHRoaXMudmFsaWRhdGVTa2lsbERpcmVjdG9yeShza2lsbERpclBhdGgpO1xuXG4gICAgICAgIC8vIFJlYWQgYW5kIHBhcnNlIFNLSUxMLm1kXG4gICAgICAgIGNvbnN0IHsgbWV0YWRhdGEsIGNvbnRlbnQgfSA9IHRoaXMucmVhZFNraWxsTUQoc2tpbGxEaXJQYXRoKTtcblxuICAgICAgICAvLyBJbml0aWFsaXplIHNraWxsIGRhdGEgc3RydWN0dXJlXG4gICAgICAgIGNvbnN0IHNraWxsRGF0YTogQW50aHJvcGljU2tpbGxEaXJlY3RvcnkgPSB7XG4gICAgICAgICAgICBza2lsbE1EOiB7IG1ldGFkYXRhLCBjb250ZW50IH0sXG4gICAgICAgICAgICBzY3JpcHRzOiBuZXcgTWFwKCksXG4gICAgICAgICAgICByZWZlcmVuY2U6IG5ldyBNYXAoKSxcbiAgICAgICAgICAgIGV4YW1wbGVzOiBuZXcgTWFwKCksXG4gICAgICAgICAgICB0aGVtZXM6IG5ldyBNYXAoKVxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIFJlYWQgYWxsIGRpcmVjdG9yeSBjb21wb25lbnRzXG4gICAgICAgIHRoaXMucmVhZFNjcmlwdHNEaXJlY3Rvcnkoc2tpbGxEaXJQYXRoLCBza2lsbERhdGEpO1xuICAgICAgICB0aGlzLnJlYWRGaWxlc0RpcmVjdG9yeShza2lsbERpclBhdGgsICdyZWZlcmVuY2UnLCBza2lsbERhdGEucmVmZXJlbmNlKTtcbiAgICAgICAgdGhpcy5yZWFkRmlsZXNEaXJlY3Rvcnkoc2tpbGxEaXJQYXRoLCAnZXhhbXBsZXMnLCBza2lsbERhdGEuZXhhbXBsZXMpO1xuICAgICAgICB0aGlzLnJlYWRGaWxlc0RpcmVjdG9yeShza2lsbERpclBhdGgsICd0aGVtZXMnLCBza2lsbERhdGEudGhlbWVzKTtcbiAgICAgICAgdGhpcy5yZWFkTWV0YWRhdGFEaXJlY3Rvcnkoc2tpbGxEaXJQYXRoLCBza2lsbERhdGEpO1xuICAgICAgICB0aGlzLnJlYWRMaWNlbnNlRmlsZShza2lsbERpclBhdGgsIHNraWxsRGF0YSk7XG5cbiAgICAgICAgcmV0dXJuIHNraWxsRGF0YTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBWYWxpZGF0ZSBza2lsbCBkaXJlY3RvcnkgZXhpc3RzIGFuZCBoYXMgU0tJTEwubWRcbiAgICAgKiBSRUZBQ1RPUkVEOiBFeHRyYWN0ZWQgdG8gcmVkdWNlIGNvZ25pdGl2ZSBjb21wbGV4aXR5XG4gICAgICovXG4gICAgcHJpdmF0ZSB2YWxpZGF0ZVNraWxsRGlyZWN0b3J5KHNraWxsRGlyUGF0aDogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGlmICghZnMuZXhpc3RzU3luYyhza2lsbERpclBhdGgpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFNraWxsIGRpcmVjdG9yeSBub3QgZm91bmQ6ICR7c2tpbGxEaXJQYXRofWApO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3Qgc2tpbGxNRFBhdGggPSBwYXRoLmpvaW4oc2tpbGxEaXJQYXRoLCAnU0tJTEwubWQnKTtcbiAgICAgICAgaWYgKCFmcy5leGlzdHNTeW5jKHNraWxsTURQYXRoKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBTS0lMTC5tZCBub3QgZm91bmQgaW4gJHtza2lsbERpclBhdGh9YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWFkIGFuZCBwYXJzZSBTS0lMTC5tZCBmaWxlXG4gICAgICogUkVGQUNUT1JFRDogRXh0cmFjdGVkIHRvIHJlZHVjZSBjb2duaXRpdmUgY29tcGxleGl0eVxuICAgICAqL1xuICAgIHByaXZhdGUgcmVhZFNraWxsTUQoc2tpbGxEaXJQYXRoOiBzdHJpbmcpOiB7IG1ldGFkYXRhOiBBbnRocm9waWNTa2lsbE1ldGFkYXRhOyBjb250ZW50OiBzdHJpbmcgfSB7XG4gICAgICAgIGNvbnN0IHNraWxsTURQYXRoID0gcGF0aC5qb2luKHNraWxsRGlyUGF0aCwgJ1NLSUxMLm1kJyk7XG4gICAgICAgIGNvbnN0IHNraWxsTURDb250ZW50ID0gZnMucmVhZEZpbGVTeW5jKHNraWxsTURQYXRoLCAndXRmLTgnKTtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFyc2VTa2lsbE1EKHNraWxsTURDb250ZW50KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWFkIHNjcmlwdHMgZGlyZWN0b3J5IGFuZCBwcm9jZXNzIHNjcmlwdCBmaWxlc1xuICAgICAqIFJFRkFDVE9SRUQ6IEV4dHJhY3RlZCB0byByZWR1Y2UgY29nbml0aXZlIGNvbXBsZXhpdHlcbiAgICAgKi9cbiAgICBwcml2YXRlIHJlYWRTY3JpcHRzRGlyZWN0b3J5KHNraWxsRGlyUGF0aDogc3RyaW5nLCBza2lsbERhdGE6IEFudGhyb3BpY1NraWxsRGlyZWN0b3J5KTogdm9pZCB7XG4gICAgICAgIGNvbnN0IHNjcmlwdHNEaXIgPSBwYXRoLmpvaW4oc2tpbGxEaXJQYXRoLCAnc2NyaXB0cycpO1xuICAgICAgICBpZiAoIWZzLmV4aXN0c1N5bmMoc2NyaXB0c0RpcikpIHJldHVybjtcblxuICAgICAgICBjb25zdCBzY3JpcHRGaWxlcyA9IGZzLnJlYWRkaXJTeW5jKHNjcmlwdHNEaXIpO1xuICAgICAgICBmb3IgKGNvbnN0IGZpbGVuYW1lIG9mIHNjcmlwdEZpbGVzKSB7XG4gICAgICAgICAgICBjb25zdCBmaWxlUGF0aCA9IHBhdGguam9pbihzY3JpcHRzRGlyLCBmaWxlbmFtZSk7XG4gICAgICAgICAgICBjb25zdCBjb250ZW50ID0gZnMucmVhZEZpbGVTeW5jKGZpbGVQYXRoLCAndXRmLTgnKTtcbiAgICAgICAgICAgIGNvbnN0IGxhbmd1YWdlID0gdGhpcy5pbmZlckxhbmd1YWdlRnJvbUZpbGVuYW1lKGZpbGVuYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGNsZWFuQ29udGVudCA9IHRoaXMucmVtb3ZlU2hlYmFuZ0FuZEhlYWRlcnMoY29udGVudCk7XG4gICAgICAgICAgICBza2lsbERhdGEuc2NyaXB0cy5zZXQoZmlsZW5hbWUsIHsgY29udGVudDogY2xlYW5Db250ZW50LCBsYW5ndWFnZSB9KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlYWQgZ2VuZXJpYyBmaWxlcyBkaXJlY3RvcnkgKHJlZmVyZW5jZSwgZXhhbXBsZXMsIHRoZW1lcylcbiAgICAgKiBSRUZBQ1RPUkVEOiBFeHRyYWN0ZWQgdG8gcmVkdWNlIGNvZ25pdGl2ZSBjb21wbGV4aXR5IGFuZCByZXVzZSBjb2RlXG4gICAgICovXG4gICAgcHJpdmF0ZSByZWFkRmlsZXNEaXJlY3RvcnkoXG4gICAgICAgIHNraWxsRGlyUGF0aDogc3RyaW5nLFxuICAgICAgICBkaXJOYW1lOiBzdHJpbmcsXG4gICAgICAgIHRhcmdldE1hcDogTWFwPHN0cmluZywgc3RyaW5nPlxuICAgICk6IHZvaWQge1xuICAgICAgICBjb25zdCBkaXJQYXRoID0gcGF0aC5qb2luKHNraWxsRGlyUGF0aCwgZGlyTmFtZSk7XG4gICA