@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.
303 lines • 36.9 kB
JavaScript
/**
* FieldFilter - GraphQL-style field selection for MCP-AQL responses
*
* Provides field filtering and name transformation for element responses:
* - Filters object fields based on a fields array
* - Transforms field names for LLM consistency (name → element_name)
* - Supports nested paths (e.g., metadata.author, metadata.tags)
* - Handles arrays of objects (filter each item)
* - Provides preset field sets (minimal, standard, full)
* - Applies Unicode normalization to field names for security (DMCP-SEC-004)
*
* PERFORMANCE NOTE:
* Field selection is applied AFTER handlers return complete data.
* This design choice:
* - Keeps handlers simple (no field-awareness needed)
* - Ensures consistent handler behavior regardless of field selection
* - Makes preset changes easy without modifying handlers
* - Has minimal overhead (~0.5ms per 100 elements)
*
* Token savings benefit the LLM context window, not internal processing.
* Empirical measurements show 80-90% token reduction with minimal preset.
*
* @see Issue #202 - Implement GraphQL field selection for response token optimization
* @see tests/unit/utils/FieldFilter.tokenSavings.test.ts - Empirical token savings
*/
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
// ============================================================================
// Constants
// ============================================================================
/**
* Field name transformations for LLM consistency.
* These ensure response field names match input parameter names.
*
* Key = internal field name, Value = external field name
*/
export const FIELD_TRANSFORMS = {
name: 'element_name',
};
/**
* Reverse mapping for field selection.
* Allows LLM to request either 'name' or 'element_name'.
*
* Key = external field name, Value = array of candidate internal paths (tried in order)
* First match wins — supports both flat objects (name) and IElement objects (metadata.name).
*/
export const FIELD_ALIASES = {
element_name: ['name', 'metadata.name'],
description: ['description', 'metadata.description'],
};
/**
* Preset field sets for common use cases.
* null = return all fields (default behavior)
*/
export const FIELD_PRESETS = {
minimal: ['element_name', 'description'],
standard: ['element_name', 'description', 'metadata.tags', 'metadata.triggers'],
full: null,
};
// ============================================================================
// Core Functions
// ============================================================================
/**
* Normalize a field name for security (DMCP-SEC-004).
* Applies Unicode normalization to prevent homograph and bypass attacks.
*
* @param field - The field name to normalize
* @returns Normalized field name
*/
function normalizeFieldName(field) {
const result = UnicodeValidator.normalize(field);
return result.normalizedContent;
}
/**
* Normalize an array of field names.
* Returns both normalized fields and any invalid field warnings.
*
* @param fields - Array of field names to normalize
* @returns Object with normalized fields and optional warnings
*/
export function normalizeFieldNames(fields) {
const normalized = [];
const warnings = [];
for (const field of fields) {
const result = UnicodeValidator.normalize(field);
normalized.push(result.normalizedContent);
if (result.detectedIssues && result.detectedIssues.length > 0) {
warnings.push(`Field "${field}": ${result.detectedIssues.join(', ')}`);
}
}
return {
normalized,
warnings: warnings.length > 0 ? warnings : undefined,
};
}
/**
* Get value from nested path (e.g., 'metadata.author')
*/
function getNestedValue(obj, path) {
const parts = path.split('.');
let current = obj;
for (const part of parts) {
if (current === null || current === undefined) {
return undefined;
}
if (typeof current !== 'object') {
return undefined;
}
current = current[part];
}
return current;
}
/** Keys that must never be set via dynamic path assignment (prototype pollution prevention) */
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
/**
* Set value at nested path, creating intermediate objects as needed.
* Rejects prototype-polluting keys (__proto__, constructor, prototype).
*/
function setNestedValue(obj, path, value) {
const parts = path.split('.');
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
if (DANGEROUS_KEYS.has(part))
continue; // Prevent prototype pollution
if (!(part in current) || typeof current[part] !== 'object') {
current[part] = {};
}
current = current[part];
}
const finalKey = parts[parts.length - 1];
if (DANGEROUS_KEYS.has(finalKey))
return; // Prevent prototype pollution
current[finalKey] = value;
}
/**
* Transform field name from internal to external format.
*/
function transformFieldName(name) {
return FIELD_TRANSFORMS[name] ?? name;
}
/**
* Resolve field alias to candidate internal paths.
* Allows LLM to request 'element_name' which maps to ['name', 'metadata.name'] internally.
* Returns an array of candidate paths to try in order.
* Applies Unicode normalization for security (DMCP-SEC-004).
*/
function resolveFieldAlias(field) {
// Normalize field name for security
const normalizedField = normalizeFieldName(field);
// Check if top-level field has an alias
const topLevel = normalizedField.split('.')[0];
const suffix = normalizedField.slice(topLevel.length);
if (FIELD_ALIASES[topLevel]) {
return FIELD_ALIASES[topLevel].map(candidate => candidate + suffix);
}
return [normalizedField];
}
/**
* Filter a single object based on requested fields.
* Tries each candidate path from alias resolution until one yields a value.
* Uses the user's requested field name (or its transform) as the output key.
*/
function filterObject(obj, fields, transform) {
const result = {};
for (const field of fields) {
// Resolve alias (element_name → ['name', 'metadata.name'])
const candidatePaths = resolveFieldAlias(field);
// Try each candidate path until one yields a value
let value;
let resolvedPath;
for (const candidate of candidatePaths) {
value = getNestedValue(obj, candidate);
if (value !== undefined) {
resolvedPath = candidate;
break;
}
}
if (value !== undefined && resolvedPath !== undefined) {
// Use the user's requested field name as the output key
// Apply transform only to the requested field name, not the resolved internal path
const normalizedField = normalizeFieldName(field);
const topLevel = normalizedField.split('.')[0];
const outputField = transform
? (transformFieldName(topLevel) + normalizedField.slice(topLevel.length))
: field;
setNestedValue(result, outputField, value);
}
}
return result;
}
/**
* Apply name transformations to an entire object (no field filtering).
* Used when no fields are specified but transformation is enabled.
*/
function transformObject(obj) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
if (DANGEROUS_KEYS.has(key))
continue; // Prevent prototype pollution
const transformedKey = transformFieldName(key);
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
result[transformedKey] = transformObject(value);
}
else if (Array.isArray(value)) {
result[transformedKey] = value.map(item => item !== null && typeof item === 'object' && !Array.isArray(item)
? transformObject(item)
: item);
}
else {
result[transformedKey] = value;
}
}
return result;
}
// ============================================================================
// Public API
// ============================================================================
/**
* Filter and transform response data based on field selection.
*
* @param data - The data to filter (object or array of objects)
* @param options - Filtering options
* @returns Filtered and transformed data with metadata
*
* @example
* // With specific fields
* filterFields({ name: 'test', description: 'desc', content: '...' }, {
* fields: ['element_name', 'description']
* });
* // Returns: { data: { element_name: 'test', description: 'desc' }, transformed: true }
*
* @example
* // With preset
* filterFields(data, { preset: 'minimal' });
* // Returns: { data: { element_name, description }, transformed: true }
*
* @example
* // No fields = transform only
* filterFields({ name: 'test' }, {});
* // Returns: { data: { element_name: 'test' }, transformed: true }
*/
export function filterFields(data, options = {}) {
const transform = options.transformNames !== false;
// Resolve fields from direct specification or preset
// Explicit fields take precedence over preset
let fields = null;
if (options.fields && options.fields.length > 0) {
fields = options.fields;
}
else if (options.preset && FIELD_PRESETS[options.preset] !== undefined) {
fields = FIELD_PRESETS[options.preset];
}
// Handle null/undefined
if (data === null || data === undefined) {
return { data, transformed: false };
}
// Handle arrays
if (Array.isArray(data)) {
const filtered = data.map(item => {
if (item !== null && typeof item === 'object') {
if (fields) {
return filterObject(item, fields, transform);
}
return transform ? transformObject(item) : item;
}
return item;
});
return { data: filtered, transformed: transform };
}
// Handle objects
if (typeof data === 'object') {
const obj = data;
if (fields) {
return {
data: filterObject(obj, fields, transform),
transformed: transform,
};
}
return {
data: transform ? transformObject(obj) : obj,
transformed: transform,
};
}
// Primitives pass through
return { data, transformed: false };
}
/**
* Check if a preset name is valid.
* Applies Unicode normalization for security (DMCP-SEC-004).
*/
export function isValidPreset(preset) {
const normalizedPreset = normalizeFieldName(preset);
return normalizedPreset in FIELD_PRESETS;
}
/**
* Get fields for a preset name.
* Applies Unicode normalization for security (DMCP-SEC-004).
*/
export function getPresetFields(preset) {
const normalizedPreset = normalizeFieldName(preset);
return FIELD_PRESETS[normalizedPreset] ?? null;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmllbGRGaWx0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvRmllbGRGaWx0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRztBQUVILE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBd0I5RSwrRUFBK0U7QUFDL0UsWUFBWTtBQUNaLCtFQUErRTtBQUUvRTs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUEyQjtJQUN0RCxJQUFJLEVBQUUsY0FBYztDQUNyQixDQUFDO0FBRUY7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUE2QjtJQUNyRCxZQUFZLEVBQUUsQ0FBQyxNQUFNLEVBQUUsZUFBZSxDQUFDO0lBQ3ZDLFdBQVcsRUFBRSxDQUFDLGFBQWEsRUFBRSxzQkFBc0IsQ0FBQztDQUNyRCxDQUFDO0FBRUY7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFvQztJQUM1RCxPQUFPLEVBQUUsQ0FBQyxjQUFjLEVBQUUsYUFBYSxDQUFDO0lBQ3hDLFFBQVEsRUFBRSxDQUFDLGNBQWMsRUFBRSxhQUFhLEVBQUUsZUFBZSxFQUFFLG1CQUFtQixDQUFDO0lBQy9FLElBQUksRUFBRSxJQUFJO0NBQ1gsQ0FBQztBQUVGLCtFQUErRTtBQUMvRSxpQkFBaUI7QUFDakIsK0VBQStFO0FBRS9FOzs7Ozs7R0FNRztBQUNILFNBQVMsa0JBQWtCLENBQUMsS0FBYTtJQUN2QyxNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDakQsT0FBTyxNQUFNLENBQUMsaUJBQWlCLENBQUM7QUFDbEMsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxNQUFnQjtJQUlsRCxNQUFNLFVBQVUsR0FBYSxFQUFFLENBQUM7SUFD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LENBQUM7SUFDSixDQUFDO0lBRUQsMEJBQTBCO0lBQzFCLE9BQU8sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxDQUFDO0FBQ3RDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLE1BQWM7SUFDMUMsTUFBTSxnQkFBZ0IsR0FBRyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNwRCxPQUFPLGdCQUFnQixJQUFJLGFBQWEsQ0FBQztBQUMzQyxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLGVBQWUsQ0FBQyxNQUF1QztJQUNyRSxNQUFNLGdCQUFnQixHQUFHLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3BELE9BQU8sYUFBYSxDQUFDLGdCQUFnQixDQUFDLElBQUksSUFBSSxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEZpZWxkRmlsdGVyIC0gR3JhcGhRTC1zdHlsZSBmaWVsZCBzZWxlY3Rpb24gZm9yIE1DUC1BUUwgcmVzcG9uc2VzXG4gKlxuICogUHJvdmlkZXMgZmllbGQgZmlsdGVyaW5nIGFuZCBuYW1lIHRyYW5zZm9ybWF0aW9uIGZvciBlbGVtZW50IHJlc3BvbnNlczpcbiAqIC0gRmlsdGVycyBvYmplY3QgZmllbGRzIGJhc2VkIG9uIGEgZmllbGRzIGFycmF5XG4gKiAtIFRyYW5zZm9ybXMgZmllbGQgbmFtZXMgZm9yIExMTSBjb25zaXN0ZW5jeSAobmFtZSDihpIgZWxlbWVudF9uYW1lKVxuICogLSBTdXBwb3J0cyBuZXN0ZWQgcGF0aHMgKGUuZy4sIG1ldGFkYXRhLmF1dGhvciwgbWV0YWRhdGEudGFncylcbiAqIC0gSGFuZGxlcyBhcnJheXMgb2Ygb2JqZWN0cyAoZmlsdGVyIGVhY2ggaXRlbSlcbiAqIC0gUHJvdmlkZXMgcHJlc2V0IGZpZWxkIHNldHMgKG1pbmltYWwsIHN0YW5kYXJkLCBmdWxsKVxuICogLSBBcHBsaWVzIFVuaWNvZGUgbm9ybWFsaXphdGlvbiB0byBmaWVsZCBuYW1lcyBmb3Igc2VjdXJpdHkgKERNQ1AtU0VDLTAwNClcbiAqXG4gKiBQRVJGT1JNQU5DRSBOT1RFOlxuICogRmllbGQgc2VsZWN0aW9uIGlzIGFwcGxpZWQgQUZURVIgaGFuZGxlcnMgcmV0dXJuIGNvbXBsZXRlIGRhdGEuXG4gKiBUaGlzIGRlc2lnbiBjaG9pY2U6XG4gKiAtIEtlZXBzIGhhbmRsZXJzIHNpbXBsZSAobm8gZmllbGQtYXdhcmVuZXNzIG5lZWRlZClcbiAqIC0gRW5zdXJlcyBjb25zaXN0ZW50IGhhbmRsZXIgYmVoYXZpb3IgcmVnYXJkbGVzcyBvZiBmaWVsZCBzZWxlY3Rpb25cbiAqIC0gTWFrZXMgcHJlc2V0IGNoYW5nZXMgZWFzeSB3aXRob3V0IG1vZGlmeWluZyBoYW5kbGVyc1xuICogLSBIYXMgbWluaW1hbCBvdmVyaGVhZCAofjAuNW1zIHBlciAxMDAgZWxlbWVudHMpXG4gKlxuICogVG9rZW4gc2F2aW5ncyBiZW5lZml0IHRoZSBMTE0gY29udGV4dCB3aW5kb3csIG5vdCBpbnRlcm5hbCBwcm9jZXNzaW5nLlxuICogRW1waXJpY2FsIG1lYXN1cmVtZW50cyBzaG93IDgwLTkwJSB0b2tlbiByZWR1Y3Rpb24gd2l0aCBtaW5pbWFsIHByZXNldC5cbiAqXG4gKiBAc2VlIElzc3VlICMyMDIgLSBJbXBsZW1lbnQgR3JhcGhRTCBmaWVsZCBzZWxlY3Rpb24gZm9yIHJlc3BvbnNlIHRva2VuIG9wdGltaXphdGlvblxuICogQHNlZSB0ZXN0cy91bml0L3V0aWxzL0ZpZWxkRmlsdGVyLnRva2VuU2F2aW5ncy50ZXN0LnRzIC0gRW1waXJpY2FsIHRva2VuIHNhdmluZ3NcbiAqL1xuXG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gVHlwZXNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IGludGVyZmFjZSBGaWVsZEZpbHRlck9wdGlvbnMge1xuICAvKiogU3BlY2lmaWMgZmllbGRzIHRvIGluY2x1ZGUgaW4gcmVzcG9uc2UgKi9cbiAgZmllbGRzPzogc3RyaW5nW107XG4gIC8qKiBQcmVzZXQgZmllbGQgc2V0czogJ21pbmltYWwnLCAnc3RhbmRhcmQnLCAnZnVsbCcgKi9cbiAgcHJlc2V0PzogJ21pbmltYWwnIHwgJ3N0YW5kYXJkJyB8ICdmdWxsJztcbiAgLyoqIFRyYW5zZm9ybSBmaWVsZCBuYW1lcyBmb3IgY29uc2lzdGVuY3kgKGRlZmF1bHQ6IHRydWUpICovXG4gIHRyYW5zZm9ybU5hbWVzPzogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBGaWVsZEZpbHRlclJlc3VsdCB7XG4gIC8qKiBUaGUgZmlsdGVyZWQgYW5kIHRyYW5zZm9ybWVkIGRhdGEgKi9cbiAgZGF0YTogdW5rbm93bjtcbiAgLyoqIEZpZWxkcyB0aGF0IHdlcmUgcmVxdWVzdGVkIGJ1dCBub3QgZm91bmQgaW4gZGF0YSAqL1xuICBtaXNzaW5nRmllbGRzPzogc3RyaW5nW107XG4gIC8qKiBXaGV0aGVyIG5hbWUgdHJhbnNmb3JtYXRpb24gd2FzIGFwcGxpZWQgKi9cbiAgdHJhbnNmb3JtZWQ6IGJvb2xlYW47XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENvbnN0YW50c1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIEZpZWxkIG5hbWUgdHJhbnNmb3JtYXRpb25zIGZvciBMTE0gY29uc2lzdGVuY3kuXG4gKiBUaGVzZSBlbnN1cmUgcmVzcG9uc2UgZmllbGQgbmFtZXMgbWF0Y2ggaW5wdXQgcGFyYW1ldGVyIG5hbWVzLlxuICpcbiAqIEtleSA9IGludGVybmFsIGZpZWxkIG5hbWUsIFZhbHVlID0gZXh0ZXJuYWwgZmllbGQgbmFtZVxuICovXG5leHBvcnQgY29uc3QgRklFTERfVFJBTlNGT1JNUzogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgbmFtZTogJ2VsZW1lbnRfbmFtZScsXG59O1xuXG4vKipcbiAqIFJldmVyc2UgbWFwcGluZyBmb3IgZmllbGQgc2VsZWN0aW9uLlxuICogQWxsb3dzIExMTSB0byByZXF1ZXN0IGVpdGhlciAnbmFtZScgb3IgJ2VsZW1lbnRfbmFtZScuXG4gKlxuICogS2V5ID0gZXh0ZXJuYWwgZmllbGQgbmFtZSwgVmFsdWUgPSBhcnJheSBvZiBjYW5kaWRhdGUgaW50ZXJuYWwgcGF0aHMgKHRyaWVkIGluIG9yZGVyKVxuICogRmlyc3QgbWF0Y2ggd2lucyDigJQgc3VwcG9ydHMgYm90aCBmbGF0IG9iamVjdHMgKG5hbWUpIGFuZCBJRWxlbWVudCBvYmplY3RzIChtZXRhZGF0YS5uYW1lKS5cbiAqL1xuZXhwb3J0IGNvbnN0IEZJRUxEX0FMSUFTRVM6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHtcbiAgZWxlbWVudF9uYW1lOiBbJ25hbWUnLCAnbWV0YWRhdGEubmFtZSddLFxuICBkZXNjcmlwdGlvbjogWydkZXNjcmlwdGlvbicsICdtZXRhZGF0YS5kZXNjcmlwdGlvbiddLFxufTtcblxuLyoqXG4gKiBQcmVzZXQgZmllbGQgc2V0cyBmb3IgY29tbW9uIHVzZSBjYXNlcy5cbiAqIG51bGwgPSByZXR1cm4gYWxsIGZpZWxkcyAoZGVmYXVsdCBiZWhhdmlvcilcbiAqL1xuZXhwb3J0IGNvbnN0IEZJRUxEX1BSRVNFVFM6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdIHwgbnVsbD4gPSB7XG4gIG1pbmltYWw6IFsnZWxlbWVudF9uYW1lJywgJ2Rlc2NyaXB0aW9uJ10sXG4gIHN0YW5kYXJkOiBbJ2VsZW1lbnRfbmFtZScsICdkZXNjcmlwdGlvbicsICdtZXRhZGF0YS50YWdzJywgJ21ldGFkYXRhLnRyaWdnZXJzJ10sXG4gIGZ1bGw6IG51bGwsXG59O1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBDb3JlIEZ1bmN0aW9uc1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIE5vcm1hbGl6ZSBhIGZpZWxkIG5hbWUgZm9yIHNlY3VyaXR5IChETUNQLVNFQy0wMDQpLlxuICogQXBwbGllcyBVbmljb2RlIG5vcm1hbGl6YXRpb24gdG8gcHJldmVudCBob21vZ3JhcGggYW5kIGJ5cGFzcyBhdHRhY2tzLlxuICpcbiAqIEBwYXJhbSBmaWVsZCAtIFRoZSBmaWVsZCBuYW1lIHRvIG5vcm1hbGl6ZVxuICogQHJldHVybnMgTm9ybWFsaXplZCBmaWVsZCBuYW1lXG4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZUZpZWxkTmFtZShmaWVsZDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgcmVzdWx0ID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoZmllbGQpO1xuICByZXR1cm4gcmVzdWx0Lm5vcm1hbGl6ZWRDb250ZW50O1xufVxuXG4vKipcbiAqIE5vcm1hbGl6ZSBhbiBhcnJheSBvZiBmaWVsZCBuYW1lcy5cbiAqIFJldHVybnMgYm90aCBub3JtYWxpemVkIGZpZWxkcyBhbmQgYW55IGludmFsaWQgZmllbGQgd2FybmluZ3MuXG4gKlxuICogQHBhcmFtIGZpZWxkcyAtIEFycmF5IG9mIGZpZWxkIG5hbWVzIHRvIG5vcm1hbGl6ZVxuICogQHJldHVybnMgT2JqZWN0IHdpdGggbm9ybWFsaXplZCBmaWVsZHMgYW5kIG9wdGlvbmFsIHdhcm5pbmdzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVGaWVsZE5hbWVzKGZpZWxkczogc3RyaW5nW10pOiB7XG4gIG5vcm1hbGl6ZWQ6IHN0cmluZ1tdO1xuICB3YXJuaW5ncz86IHN0cmluZ1tdO1xufSB7XG4gIGNvbnN0IG5vcm1hbGl6ZWQ6IHN0cmluZ1tdID0gW107XG4gIGNvbnN0IHdhcm5pbmdzOiBzdHJpbmdbXSA9IFtdO1xuXG4gIGZvciAoY29uc3QgZmllbGQgb2YgZmllbGRzKSB7XG4gICAgY29uc3QgcmVzdWx0ID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoZmllbGQpO1xuICAgIG5vcm1hbGl6ZWQucHVzaChyZXN1bHQubm9ybWFsaXplZENvbnRlbnQpO1xuXG4gICAgaWYgKHJlc3VsdC5kZXRlY3RlZElzc3VlcyAmJiByZXN1bHQuZGV0ZWN0ZWRJc3N1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgd2FybmluZ3MucHVzaChgRmllbGQgXCIke2ZpZWxkfVwiOiAke3Jlc3VsdC5kZXRlY3RlZElzc3Vlcy5qb2luKCcsICcpfWApO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgbm9ybWFsaXplZCxcbiAgICB3YXJuaW5nczogd2FybmluZ3MubGVuZ3RoID4gMCA/IHdhcm5pbmdzIDogdW5kZWZpbmVkLFxuICB9O1xufVxuXG4vKipcbiAqIEdldCB2YWx1ZSBmcm9tIG5lc3RlZCBwYXRoIChlLmcuLCAnbWV0YWRhdGEuYXV0aG9yJylcbiAqL1xuZnVuY3Rpb24gZ2V0TmVzdGVkVmFsdWUob2JqOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiwgcGF0aDogc3RyaW5nKTogdW5rbm93biB7XG4gIGNvbnN0IHBhcnRzID0gcGF0aC5zcGxpdCgnLicpO1xuICBsZXQgY3VycmVudDogdW5rbm93biA9IG9iajtcblxuICBmb3IgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICBpZiAoY3VycmVudCA9PT0gbnVsbCB8fCBjdXJyZW50ID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGlmICh0eXBlb2YgY3VycmVudCAhPT0gJ29iamVjdCcpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGN1cnJlbnQgPSAoY3VycmVudCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilbcGFydF07XG4gIH1cblxuICByZXR1cm4gY3VycmVudDtcbn1cblxuLyoqIEtleXMgdGhhdCBtdXN0IG5ldmVyIGJlIHNldCB2aWEgZHluYW1pYyBwYXRoIGFzc2lnbm1lbnQgKHByb3RvdHlwZSBwb2xsdXRpb24gcHJldmVudGlvbikgKi9cbmNvbnN0IERBTkdFUk9VU19LRVlTID0gbmV3IFNldChbJ19fcHJvdG9fXycsICdjb25zdHJ1Y3RvcicsICdwcm90b3R5cGUnXSk7XG5cbi8qKlxuICogU2V0IHZhbHVlIGF0IG5lc3RlZCBwYXRoLCBjcmVhdGluZyBpbnRlcm1lZGlhdGUgb2JqZWN0cyBhcyBuZWVkZWQuXG4gKiBSZWplY3RzIHByb3RvdHlwZS1wb2xsdXRpbmcga2V5cyAoX19wcm90b19fLCBjb25zdHJ1Y3RvciwgcHJvdG90eXBlKS5cbiAqL1xuZnVuY3Rpb24gc2V0TmVzdGVkVmFsdWUob2JqOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiwgcGF0aDogc3RyaW5nLCB2YWx1ZTogdW5rbm93bik6IHZvaWQge1xuICBjb25zdCBwYXJ0cyA9IHBhdGguc3BsaXQoJy4nKTtcbiAgbGV0IGN1cnJlbnQgPSBvYmo7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGggLSAxOyBpKyspIHtcbiAgICBjb25zdCBwYXJ0ID0gcGFydHNbaV07XG4gICAgaWYgKERBTkdFUk9VU19LRVlTLmhhcyhwYXJ0KSkgY29udGludWU7IC8vIFByZXZlbnQgcHJvdG90eXBlIHBvbGx1dGlvblxuICAgIGlmICghKHBhcnQgaW4gY3VycmVudCkgfHwgdHlwZW9mIGN1cnJlbnRbcGFydF0gIT09ICdvYmplY3QnKSB7XG4gICAgICBjdXJyZW50W3BhcnRdID0ge307XG4gICAgfVxuICAgIGN1cnJlbnQgPSBjdXJyZW50W3BhcnRdIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICB9XG5cbiAgY29uc3QgZmluYWxLZXkgPSBwYXJ0c1twYXJ0cy5sZW5ndGggLSAxXTtcbiAgaWYgKERBTkdFUk9VU19LRVlTLmhhcyhmaW5hbEtleSkpIHJldHVybjsgLy8gUHJldmVudCBwcm90b3R5cGUgcG9sbHV0aW9uXG4gIGN1cnJlbnRbZmluYWxLZXldID0gdmFsdWU7XG59XG5cbi8qKlxuICogVHJhbnNmb3JtIGZpZWxkIG5hbWUgZnJvbSBpbnRlcm5hbCB0byBleHRlcm5hbCBmb3JtYXQuXG4gKi9cbmZ1bmN0aW9uIHRyYW5zZm9ybUZpZWxkTmFtZShuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gRklFTERfVFJBTlNGT1JNU1tuYW1lXSA/PyBuYW1lO1xufVxuXG4vKipcbiAqIFJlc29sdmUgZmllbGQgYWxpYXMgdG8gY2FuZGlkYXRlIGludGVybmFsIHBhdGhzLlxuICogQWxsb3dzIExMTSB0byByZXF1ZXN0ICdlbGVtZW50X25hbWUnIHdoaWNoIG1hcHMgdG8gWyduYW1lJywgJ21ldGFkYXRhLm5hbWUnXSBpbnRlcm5hbGx5LlxuICogUmV0dXJucyBhbiBhcnJheSBvZiBjYW5kaWRhdGUgcGF0aHMgdG8gdHJ5IGluIG9yZGVyLlxuICogQXBwbGllcyBVbmljb2RlIG5vcm1hbGl6YXRpb24gZm9yIHNlY3VyaXR5IChETUNQLVNFQy0wMDQpLlxuICovXG5mdW5jdGlvbiByZXNvbHZlRmllbGRBbGlhcyhmaWVsZDogc3RyaW5nKTogc3RyaW5nW10ge1xuICAvLyBOb3JtYWxpemUgZmllbGQgbmFtZSBmb3Igc2VjdXJpdHlcbiAgY29uc3Qgbm9ybWFsaXplZEZpZWxkID0gbm9ybWFsaXplRmllbGROYW1lKGZpZWxkKTtcblxuICAvLyBDaGVjayBpZiB0b3AtbGV2ZWwgZmllbGQgaGFzIGFuIGFsaWFzXG4gIGNvbnN0IHRvcExldmVsID0gbm9ybWFsaXplZEZpZWxkLnNwbGl0KCcuJylbMF07XG4gIGNvbnN0IHN1ZmZpeCA9IG5vcm1hbGl6ZWRGaWVsZC5zbGljZSh0b3BMZXZlbC5sZW5ndGgpO1xuICBpZiAoRklFTERfQUxJQVNFU1t0b3BMZXZlbF0pIHtcbiAgICByZXR1cm4gRklFTERfQUxJQVNFU1t0b3BMZXZlbF0ubWFwKGNhbmRpZGF0ZSA9PiBjYW5kaWRhdGUgKyBzdWZmaXgpO1xuICB9XG4gIHJldHVybiBbbm9ybWFsaXplZEZpZWxkXTtcbn1cblxuLyoqXG4gKiBGaWx0ZXIgYSBzaW5nbGUgb2JqZWN0IGJhc2VkIG9uIHJlcXVlc3RlZCBmaWVsZHMuXG4gKiBUcmllcyBlYWNoIGNhbmRpZGF0ZSBwYXRoIGZyb20gYWxpYXMgcmVzb2x1dGlvbiB1bnRpbCBvbmUgeWllbGRzIGEgdmFsdWUuXG4gKiBVc2VzIHRoZSB1c2VyJ3MgcmVxdWVzdGVkIGZpZWxkIG5hbWUgKG9yIGl0cyB0cmFuc2Zvcm0pIGFzIHRoZSBvdXRwdXQga2V5LlxuICovXG5mdW5jdGlvbiBmaWx0ZXJPYmplY3QoXG4gIG9iajogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIGZpZWxkczogc3RyaW5nW10sXG4gIHRyYW5zZm9ybTogYm9vbGVhblxuKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICBjb25zdCByZXN1bHQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG5cbiAgZm9yIChjb25zdCBmaWVsZCBvZiBmaWVsZHMpIHtcbiAgICAvLyBSZXNvbHZlIGFsaWFzIChlbGVtZW50X25hbWUg4oaSIFsnbmFtZScsICdtZXRhZGF0YS5uYW1lJ10pXG4gICAgY29uc3QgY2FuZGlkYXRlUGF0aHMgPSByZXNvbHZlRmllbGRBbGlhcyhmaWVsZCk7XG5cbiAgICAvLyBUcnkgZWFjaCBjYW5kaWRhdGUgcGF0aCB1bnRpbCBvbmUgeWllbGRzIGEgdmFsdWVcbiAgICBsZXQgdmFsdWU6IHVua25vd247XG4gICAgbGV0IHJlc29sdmVkUGF0aDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIGZvciAoY29uc3QgY2FuZGlkYXRlIG9mIGNhbmRpZGF0ZVBhdGhzKSB7XG4gICAgICB2YWx1ZSA9IGdldE5lc3RlZFZhbHVlKG9iaiwgY2FuZGlkYXRlKTtcbiAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJlc29sdmVkUGF0aCA9IGNhbmRpZGF0ZTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHZhbHVlICE9PSB1bmRlZmluZWQgJiYgcmVzb2x2ZWRQYXRoICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIFVzZSB0aGUgdXNlcidzIHJlcXVlc3RlZCBmaWVsZCBuYW1lIGFzIHRoZSBvdXRwdXQga2V5XG4gICAgICAvLyBBcHBseSB0cmFuc2Zvcm0gb25seSB0byB0aGUgcmVxdWVzdGVkIGZpZWxkIG5hbWUsIG5vdCB0aGUgcmVzb2x2ZWQgaW50ZXJuYWwgcGF0aFxuICAgICAgY29uc3Qgbm9ybWFsaXplZEZpZWxkID0gbm9ybWFsaXplRmllbGROYW1lKGZpZWxkKTtcbiAgICAgIGNvbnN0IHRvcExldmVsID0gbm9ybWFsaXplZEZpZWxkLnNwbGl0KCcuJylbMF07XG4gICAgICBjb25zdCBvdXRwdXRGaWVsZCA9IHRyYW5zZm9ybVxuICAgICAgICA/ICh0cmFuc2Zvcm1GaWVsZE5hbWUodG9wTGV2ZWwpICsgbm9ybWFsaXplZEZpZWxkLnNsaWNlKHRvcExldmVsLmxlbmd0aCkpXG4gICAgICAgIDogZmllbGQ7XG4gICAgICBzZXROZXN0ZWRWYWx1ZShyZXN1bHQsIG91dHB1dEZpZWxkLCB2YWx1ZSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBBcHBseSBuYW1lIHRyYW5zZm9ybWF0aW9ucyB0byBhbiBlbnRpcmUgb2JqZWN0IChubyBmaWVsZCBmaWx0ZXJpbmcpLlxuICogVXNlZCB3aGVuIG5vIGZpZWxkcyBhcmUgc3BlY2lmaWVkIGJ1dCB0cmFuc2Zvcm1hdGlvbiBpcyBlbmFibGVkLlxuICovXG5mdW5jdGlvbiB0cmFuc2Zvcm1PYmplY3Qob2JqOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgY29uc3QgcmVzdWx0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG9iaikpIHtcbiAgICBpZiAoREFOR0VST1VTX0tFWVMuaGFzKGtleSkpIGNvbnRpbnVlOyAvLyBQcmV2ZW50IHByb3RvdHlwZSBwb2xsdXRpb25cbiAgICBjb25zdCB0cmFuc2Zvcm1lZEtleSA9IHRyYW5zZm9ybUZpZWxkTmFtZShrZXkpO1xuXG4gICAgaWYgKHZhbHVlICE9PSBudWxsICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICByZXN1bHRbdHJhbnNmb3JtZWRLZXldID0gdHJhbnNmb3JtT2JqZWN0KHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KTtcbiAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICByZXN1bHRbdHJhbnNmb3JtZWRLZXldID0gdmFsdWUubWFwKGl0ZW0gPT5cbiAgICAgICAgaXRlbSAhPT0gbnVsbCAmJiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkoaXRlbSlcbiAgICAgICAgICA/IHRyYW5zZm9ybU9iamVjdChpdGVtIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KVxuICAgICAgICAgIDogaXRlbVxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzdWx0W3RyYW5zZm9ybWVkS2V5XSA9IHZhbHVlO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFB1YmxpYyBBUElcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBGaWx0ZXIgYW5kIHRyYW5zZm9ybSByZXNwb25zZSBkYXRhIGJhc2VkIG9uIGZpZWxkIHNlbGVjdGlvbi5cbiAqXG4gKiBAcGFyYW0gZGF0YSAtIFRoZSBkYXRhIHRvIGZpbHRlciAob2JqZWN0IG9yIGFycmF5IG9mIG9iamVjdHMpXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEZpbHRlcmluZyBvcHRpb25zXG4gKiBAcmV0dXJucyBGaWx0ZXJlZCBhbmQgdHJhbnNmb3JtZWQgZGF0YSB3aXRoIG1ldGFkYXRhXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIFdpdGggc3BlY2lmaWMgZmllbGRzXG4gKiBmaWx0ZXJGaWVsZHMoeyBuYW1lOiAndGVzdCcsIGRlc2NyaXB0aW9uOiAnZGVzYycsIGNvbnRlbnQ6ICcuLi4nIH0sIHtcbiAqICAgZmllbGRzOiBbJ2VsZW1lbnRfbmFtZScsICdkZXNjcmlwdGlvbiddXG4gKiB9KTtcbiAqIC8vIFJldHVybnM6IHsgZGF0YTogeyBlbGVtZW50X25hbWU6ICd0ZXN0JywgZGVzY3JpcHRpb246ICdkZXNjJyB9LCB0cmFuc2Zvcm1lZDogdHJ1ZSB9XG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIFdpdGggcHJlc2V0XG4gKiBmaWx0ZXJGaWVsZHMoZGF0YSwgeyBwcmVzZXQ6ICdtaW5pbWFsJyB9KTtcbiAqIC8vIFJldHVybnM6IHsgZGF0YTogeyBlbGVtZW50X25hbWUsIGRlc2NyaXB0aW9uIH0sIHRyYW5zZm9ybWVkOiB0cnVlIH1cbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gTm8gZmllbGRzID0gdHJhbnNmb3JtIG9ubHlcbiAqIGZpbHRlckZpZWxkcyh7IG5hbWU6ICd0ZXN0JyB9LCB7fSk7XG4gKiAvLyBSZXR1cm5zOiB7IGRhdGE6IHsgZWxlbWVudF9uYW1lOiAndGVzdCcgfSwgdHJhbnNmb3JtZWQ6IHRydWUgfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZmlsdGVyRmllbGRzKFxuICBkYXRhOiB1bmtub3duLFxuICBvcHRpb25zOiBGaWVsZEZpbHRlck9wdGlvbnMgPSB7fVxuKTogRmllbGRGaWx0ZXJSZXN1bHQge1xuICBjb25zdCB0cmFuc2Zvcm0gPSBvcHRpb25zLnRyYW5zZm9ybU5hbWVzICE9PSBmYWxzZTtcblxuICAvLyBSZXNvbHZlIGZpZWxkcyBmcm9tIGRpcmVjdCBzcGVjaWZpY2F0aW9uIG9yIHByZXNldFxuICAvLyBFeHBsaWNpdCBmaWVsZHMgdGFrZSBwcmVjZWRlbmNlIG92ZXIgcHJlc2V0XG4gIGxldCBmaWVsZHM6IHN0cmluZ1tdIHwgbnVsbCA9IG51bGw7XG4gIGlmIChvcHRpb25zLmZpZWxkcyAmJiBvcHRpb25zLmZpZWxkcy5sZW5ndGggPiAwKSB7XG4gICAgZmllbGRzID0gb3B0aW9ucy5maWVsZHM7XG4gIH0gZWxzZSBpZiAob3B0aW9ucy5wcmVzZXQgJiYgRklFTERfUFJFU0VUU1tvcHRpb25zLnByZXNldF0gIT09IHVuZGVmaW5lZCkge1xuICAgIGZpZWxkcyA9IEZJRUxEX1BSRVNFVFNbb3B0aW9ucy5wcmVzZXRdO1xuICB9XG5cbiAgLy8gSGFuZGxlIG51bGwvdW5kZWZpbmVkXG4gIGlmIChkYXRhID09PSBudWxsIHx8IGRhdGEgPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiB7IGRhdGEsIHRyYW5zZm9ybWVkOiBmYWxzZSB9O1xuICB9XG5cbiAgLy8gSGFuZGxlIGFycmF5c1xuICBpZiAoQXJyYXkuaXNBcnJheShkYXRhKSkge1xuICAgIGNvbnN0IGZpbHRlcmVkID0gZGF0YS5tYXAoaXRlbSA9PiB7XG4gICAgICBpZiAoaXRlbSAhPT0gbnVsbCAmJiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgaWYgKGZpZWxkcykge1xuICAgICAgICAgIHJldHVybiBmaWx0ZXJPYmplY3QoaXRlbSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiwgZmllbGRzLCB0cmFuc2Zvcm0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cmFuc2Zvcm0gPyB0cmFuc2Zvcm1PYmplY3QoaXRlbSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgOiBpdGVtO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGl0ZW07XG4gICAgfSk7XG4gICAgcmV0dXJuIHsgZGF0YTogZmlsdGVyZWQsIHRyYW5zZm9ybWVkOiB0cmFuc2Zvcm0gfTtcbiAgfVxuXG4gIC8vIEhhbmRsZSBvYmplY3RzXG4gIGlmICh0eXBlb2YgZGF0YSA9PT0gJ29iamVjdCcpIHtcbiAgICBjb25zdCBvYmogPSBkYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIGlmIChmaWVsZHMpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGRhdGE6IGZpbHRlck9iamVjdChvYmosIGZpZWxkcywgdHJhbnNmb3JtKSxcbiAgICAgICAgdHJhbnNmb3JtZWQ6IHRyYW5zZm9ybSxcbiAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBkYXRhOiB0cmFuc2Zvcm0gPyB0cmFuc2Zvcm1PYmplY3Qob2JqKSA6IG9iaixcbiAgICAgIHRyYW5zZm9ybWVkOiB0cmFuc2Zvcm0sXG4gICAgfTtcbiAgfVxuXG4gIC8vIFByaW1pdGl2ZXMgcGFzcyB0aHJvdWdoXG4gIHJldHVybiB7IGRhdGEsIHRyYW5zZm9ybWVkOiBmYWxzZSB9O1xufVxuXG4vKipcbiAqIENoZWNrIGlmIGEgcHJlc2V0IG5hbWUgaXMgdmFsaWQuXG4gKiBBcHBsaWVzIFVuaWNvZGUgbm9ybWFsaXphdGlvbiBmb3Igc2VjdXJpdHkgKERNQ1AtU0VDLTAwNCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1ZhbGlkUHJlc2V0KHByZXNldDogc3RyaW5nKTogcHJlc2V0IGlzICdtaW5pbWFsJyB8ICdzdGFuZGFyZCcgfCAnZnVsbCcge1xuICBjb25zdCBub3JtYWxpemVkUHJlc2V0ID0gbm9ybWFsaXplRmllbGROYW1lKHByZXNldCk7XG4gIHJldHVybiBub3JtYWxpemVkUHJlc2V0IGluIEZJRUxEX1BSRVNFVFM7XG59XG5cbi8qKlxuICogR2V0IGZpZWxkcyBmb3IgYSBwcmVzZXQgbmFtZS5cbiAqIEFwcGxpZXMgVW5pY29kZSBub3JtYWxpemF0aW9uIGZvciBzZWN1cml0eSAoRE1DUC1TRUMtMDA0KS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFByZXNldEZpZWxkcyhwcmVzZXQ6ICdtaW5pbWFsJyB8ICdzdGFuZGFyZCcgfCAnZnVsbCcpOiBzdHJpbmdbXSB8IG51bGwge1xuICBjb25zdCBub3JtYWxpemVkUHJlc2V0ID0gbm9ybWFsaXplRmllbGROYW1lKHByZXNldCk7XG4gIHJldHVybiBGSUVMRF9QUkVTRVRTW25vcm1hbGl6ZWRQcmVzZXRdID8/IG51bGw7XG59XG4iXX0=