@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.
270 lines • 27.9 kB
JavaScript
/**
* SortService - Element-agnostic sorting service
*
* Provides stable, secure sorting for any element type with support for:
* - Multiple sortable fields (name, created, modified, version, retention)
* - Ascending and descending order
* - Semantic version comparison
* - ISO 8601 date comparison
* - Null-safe handling (sorts nulls last)
* - Immutability (does not mutate input array)
*
* SECURITY NOTES:
* - All sortBy values validated against SortableField enum
* - No arbitrary field access allowed
* - Stable sort prevents timing attacks
* - Input validation prevents injection
*
* @module SortService
*/
import { logger } from '../../utils/logger.js';
import { extractSortValue, } from './types.js';
/**
* Service for sorting arrays of elements
*
* Implements ISortService with support for multiple sort fields and stable ordering.
* Designed to be stateless and injectable via dependency injection.
*
* @template T - Element type extending IElement
*/
export class SortService {
static DEFAULT_SORT_BY = 'name';
static DEFAULT_SORT_ORDER = 'asc';
static VALID_SORT_FIELDS = [
'name',
'created',
'modified',
'version',
'retention',
];
/**
* Sort an array of elements according to the specified options
*
* IMPLEMENTATION NOTES:
* - Does NOT mutate the input array (creates a shallow copy)
* - Maintains stable sort order (items with equal values retain original order)
* - Handles missing values by sorting them last
* - Validates sortBy field against allowed enum values
*
* @param items - Array of elements to sort
* @param options - Sorting configuration
* @returns New sorted array
* @throws {Error} If sort options are invalid
*/
sort(items, options) {
// Validate options
this.validateOptions(options);
// Return empty array as-is
if (items.length === 0) {
return [];
}
// Extract validated options with defaults
const sortBy = options?.sortBy ?? SortService.DEFAULT_SORT_BY;
const sortOrder = options?.sortOrder ?? SortService.DEFAULT_SORT_ORDER;
// Create shallow copy to avoid mutation
const sortedItems = [...items];
// Sort with field-specific comparator
sortedItems.sort((a, b) => {
const result = this.compareElements(a, b, sortBy);
return sortOrder === 'desc' ? -result : result;
});
return sortedItems;
}
/**
* Get the default sorting configuration
*
* @returns Default sort options
*/
getDefaultSorting() {
return {
sortBy: SortService.DEFAULT_SORT_BY,
sortOrder: SortService.DEFAULT_SORT_ORDER,
};
}
/**
* Validate sort options without applying them
*
* SECURITY: Validates sortBy against enum to prevent arbitrary field access
*
* @param options - Sort options to validate
* @returns True if options are valid
* @throws {Error} If options are invalid
*/
validateOptions(options) {
if (!options) {
return true; // No options means use defaults
}
// Validate sortBy field
if (options.sortBy !== undefined) {
if (!SortService.VALID_SORT_FIELDS.includes(options.sortBy)) {
const error = `Invalid sortBy field: ${options.sortBy}. Must be one of: ${SortService.VALID_SORT_FIELDS.join(', ')}`;
logger.error('SortService.validateOptions', { error });
throw new Error(error);
}
}
// Validate sortOrder
if (options.sortOrder !== undefined) {
if (options.sortOrder !== 'asc' && options.sortOrder !== 'desc') {
const error = `Invalid sortOrder: ${options.sortOrder}. Must be 'asc' or 'desc'`;
logger.error('SortService.validateOptions', { error });
throw new Error(error);
}
}
return true;
}
/**
* Compare two elements for sorting
*
* IMPLEMENTATION NOTES:
* - Returns negative if a < b, positive if a > b, zero if equal
* - Handles null/undefined values by sorting them last
* - Uses field-specific comparison logic
* - Maintains stable sort (returns 0 for equal values)
*
* @param a - First element
* @param b - Second element
* @param field - Field to compare
* @returns Comparison result (-1, 0, 1)
*/
compareElements(a, b, field) {
const valueA = extractSortValue(a, field);
const valueB = extractSortValue(b, field);
// Handle null/undefined - sort last
if (valueA === undefined && valueB === undefined) {
return 0;
}
if (valueA === undefined) {
return 1; // a is null, sort after b
}
if (valueB === undefined) {
return -1; // b is null, sort a before b
}
// Field-specific comparison
switch (field) {
case 'name':
return this.compareStrings(String(valueA), String(valueB));
case 'created':
case 'modified':
return this.compareDates(String(valueA), String(valueB));
case 'version':
return this.compareVersions(String(valueA), String(valueB));
case 'retention':
return this.compareNumbers(Number(valueA), Number(valueB));
default:
// Should never reach here due to validation
logger.warn('SortService.compareElements', {
message: 'Unknown sort field, using string comparison',
field,
});
return this.compareStrings(String(valueA), String(valueB));
}
}
/**
* Compare two strings case-insensitively
*
* Uses localeCompare for proper Unicode handling and stable ordering.
*
* @param a - First string
* @param b - Second string
* @returns Comparison result
*/
compareStrings(a, b) {
return a.localeCompare(b, undefined, { sensitivity: 'base' });
}
/**
* Compare two ISO 8601 date strings as timestamps
*
* Converts to Date objects and compares numerically.
* Invalid dates are treated as undefined (sorted last).
*
* @param a - First date string
* @param b - Second date string
* @returns Comparison result
*/
compareDates(a, b) {
const dateA = new Date(a).getTime();
const dateB = new Date(b).getTime();
// Handle invalid dates
if (isNaN(dateA) && isNaN(dateB)) {
return 0;
}
if (isNaN(dateA)) {
return 1;
}
if (isNaN(dateB)) {
return -1;
}
return dateA - dateB;
}
/**
* Compare two semantic version strings (x.y.z format)
*
* Compares major, minor, and patch versions numerically.
* Invalid versions are sorted using string comparison.
*
* @param a - First version string
* @param b - Second version string
* @returns Comparison result
*/
compareVersions(a, b) {
const partsA = this.parseVersion(a);
const partsB = this.parseVersion(b);
// If either version is invalid, fall back to string comparison
if (!partsA || !partsB) {
return this.compareStrings(a, b);
}
// Compare major version
if (partsA.major !== partsB.major) {
return partsA.major - partsB.major;
}
// Compare minor version
if (partsA.minor !== partsB.minor) {
return partsA.minor - partsB.minor;
}
// Compare patch version
return partsA.patch - partsB.patch;
}
/**
* Parse a semantic version string into components
*
* Handles formats like "1.0.0", "2.5.3", etc.
* Returns null for invalid versions.
*
* @param version - Version string to parse
* @returns Parsed version components or null
*/
parseVersion(version) {
const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
if (!match) {
return null;
}
return {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
};
}
/**
* Compare two numbers
*
* Handles NaN by treating as undefined (sorted last).
*
* @param a - First number
* @param b - Second number
* @returns Comparison result
*/
compareNumbers(a, b) {
// Handle NaN
if (isNaN(a) && isNaN(b)) {
return 0;
}
if (isNaN(a)) {
return 1;
}
if (isNaN(b)) {
return -1;
}
return a - b;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU29ydFNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmljZXMvcXVlcnkvU29ydFNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUdILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBTUwsZ0JBQWdCLEdBQ2pCLE1BQU0sWUFBWSxDQUFDO0FBRXBCOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLE9BQU8sV0FBVztJQUNkLE1BQU0sQ0FBVSxlQUFlLEdBQWtCLE1BQU0sQ0FBQztJQUN4RCxNQUFNLENBQVUsa0JBQWtCLEdBQWMsS0FBSyxDQUFDO0lBQ3RELE1BQU0sQ0FBVSxpQkFBaUIsR0FBNkI7UUFDcEUsTUFBTTtRQUNOLFNBQVM7UUFDVCxVQUFVO1FBQ1YsU0FBUztRQUNULFdBQVc7S0FDSCxDQUFDO0lBRVg7Ozs7Ozs7Ozs7Ozs7T0FhRztJQUNJLElBQUksQ0FBQyxLQUFVLEVBQUUsT0FBcUI7UUFDM0MsbUJBQW1CO1FBQ25CLElBQUksQ0FBQyxlQUFlLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFOUIsMkJBQTJCO1FBQzNCLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN2QixPQUFPLEVBQUUsQ0FBQztRQUNaLENBQUM7UUFFRCwwQ0FBMEM7UUFDMUMsTUFBTSxNQUFNLEdBQUcsT0FBTyxFQUFFLE1BQU0sSUFBSSxXQUFXLENBQUMsZUFBZSxDQUFDO1FBQzlELE1BQU0sU0FBUyxHQUFHLE9BQU8sRUFBRSxTQUFTLElBQUksV0FBVyxDQUFDLGtCQUFrQixDQUFDO1FBRXZFLHdDQUF3QztRQUN4QyxNQUFNLFdBQVcsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUM7UUFFL0Isc0NBQXNDO1FBQ3RDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDeEIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQ2xELE9BQU8sU0FBUyxLQUFLLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztRQUNqRCxDQUFDLENBQUMsQ0FBQztRQUVILE9BQU8sV0FBVyxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksaUJBQWlCO1FBQ3RCLE9BQU87WUFDTCxNQUFNLEVBQUUsV0FBVyxDQUFDLGVBQWU7WUFDbkMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxrQkFBa0I7U0FDMUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNJLGVBQWUsQ0FBQyxPQUFxQjtRQUMxQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDYixPQUFPLElBQUksQ0FBQyxDQUFDLGdDQUFnQztRQUMvQyxDQUFDO1FBRUQsd0JBQXdCO1FBQ3hCLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNqQyxJQUFJLENBQUMsV0FBVyxDQUFDLGlCQUFpQixDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztnQkFDNUQsTUFBTSxLQUFLLEdBQUcseUJBQXlCLE9BQU8sQ0FBQyxNQUFNLHFCQUFxQixXQUFXLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ3JILE1BQU0sQ0FBQyxLQUFLLENBQUMsNkJBQTZCLEVBQUUsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUN2RCxNQUFNLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ3pCLENBQUM7UUFDSCxDQUFDO1FBRUQscUJBQXFCO1FBQ3JCLElBQUksT0FBTyxDQUFDLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNwQyxJQUFJLE9BQU8sQ0FBQyxTQUFTLEtBQUssS0FBSyxJQUFJLE9BQU8sQ0FBQyxTQUFTLEtBQUssTUFBTSxFQUFFLENBQUM7Z0JBQ2hFLE1BQU0sS0FBSyxHQUFHLHNCQUFzQixPQUFPLENBQUMsU0FBUywyQkFBMkIsQ0FBQztnQkFDakYsTUFBTSxDQUFDLEtBQUssQ0FBQyw2QkFBNkIsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQ3ZELE1BQU0sSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDekIsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7OztPQWFHO0lBQ0ssZUFBZSxDQUFDLENBQUksRUFBRSxDQUFJLEVBQUUsS0FBb0I7UUFDdEQsTUFBTSxNQUFNLEdBQUcsZ0JBQWdCLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzFDLE1BQU0sTUFBTSxHQUFHLGdCQUFnQixDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUUxQyxvQ0FBb0M7UUFDcEMsSUFBSSxNQUFNLEtBQUssU0FBUyxJQUFJLE1BQU0sS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNqRCxPQUFPLENBQUMsQ0FBQztRQUNYLENBQUM7UUFDRCxJQUFJLE1BQU0sS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN6QixPQUFPLENBQUMsQ0FBQyxDQUFDLDBCQUEwQjtRQUN0QyxDQUFDO1FBQ0QsSUFBSSxNQUFNLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDekIsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLDZCQUE2QjtRQUMxQyxDQUFDO1FBRUQsNEJBQTRCO1FBQzVCLFFBQVEsS0FBSyxFQUFFLENBQUM7WUFDZCxLQUFLLE1BQU07Z0JBQ1QsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztZQUU3RCxLQUFLLFNBQVMsQ0FBQztZQUNmLEtBQUssVUFBVTtnQkFDYixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBRTNELEtBQUssU0FBUztnQkFDWixPQUFPLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBRTlELEtBQUssV0FBVztnQkFDZCxPQUFPLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBRTdEO2dCQUNFLDRDQUE0QztnQkFDNUMsTUFBTSxDQUFDLElBQUksQ0FBQyw2QkFBNkIsRUFBRTtvQkFDekMsT0FBTyxFQUFFLDZDQUE2QztvQkFDdEQsS0FBSztpQkFDTixDQUFDLENBQUM7Z0JBQ0gsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMvRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ssY0FBYyxDQUFDLENBQVMsRUFBRSxDQUFTO1FBQ3pDLE9BQU8sQ0FBQyxDQUFDLGFBQWEsQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNLLFlBQVksQ0FBQyxDQUFTLEVBQUUsQ0FBUztRQUN2QyxNQUFNLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNwQyxNQUFNLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUVwQyx1QkFBdUI7UUFDdkIsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDakMsT0FBTyxDQUFDLENBQUM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNqQixPQUFPLENBQUMsQ0FBQztRQUNYLENBQUM7UUFDRCxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ2pCLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDWixDQUFDO1FBRUQsT0FBTyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSyxlQUFlLENBQUMsQ0FBUyxFQUFFLENBQVM7UUFDMUMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNwQyxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRXBDLCtEQUErRDtRQUMvRCxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDdkIsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNuQyxDQUFDO1FBRUQsd0JBQXdCO1FBQ3hCLElBQUksTUFBTSxDQUFDLEtBQUssS0FBSyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDbEMsT0FBTyxNQUFNLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUM7UUFDckMsQ0FBQztRQUVELHdCQUF3QjtRQUN4QixJQUFJLE1BQU0sQ0FBQyxLQUFLLEtBQUssTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xDLE9BQU8sTUFBTSxDQUFDLEtBQUssR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDO1FBQ3JDLENBQUM7UUFFRCx3QkFBd0I7UUFDeEIsT0FBTyxNQUFNLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUM7SUFDckMsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ssWUFBWSxDQUFDLE9BQWU7UUFDbEMsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNYLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELE9BQU87WUFDTCxLQUFLLEVBQUUsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDN0IsS0FBSyxFQUFFLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQzdCLEtBQUssRUFBRSxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQztTQUM5QixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ssY0FBYyxDQUFDLENBQVMsRUFBRSxDQUFTO1FBQ3pDLGFBQWE7UUFDYixJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN6QixPQUFPLENBQUMsQ0FBQztRQUNYLENBQUM7UUFDRCxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ2IsT0FBTyxDQUFDLENBQUM7UUFDWCxDQUFDO1FBQ0QsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNiLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDWixDQUFDO1FBRUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU29ydFNlcnZpY2UgLSBFbGVtZW50LWFnbm9zdGljIHNvcnRpbmcgc2VydmljZVxuICpcbiAqIFByb3ZpZGVzIHN0YWJsZSwgc2VjdXJlIHNvcnRpbmcgZm9yIGFueSBlbGVtZW50IHR5cGUgd2l0aCBzdXBwb3J0IGZvcjpcbiAqIC0gTXVsdGlwbGUgc29ydGFibGUgZmllbGRzIChuYW1lLCBjcmVhdGVkLCBtb2RpZmllZCwgdmVyc2lvbiwgcmV0ZW50aW9uKVxuICogLSBBc2NlbmRpbmcgYW5kIGRlc2NlbmRpbmcgb3JkZXJcbiAqIC0gU2VtYW50aWMgdmVyc2lvbiBjb21wYXJpc29uXG4gKiAtIElTTyA4NjAxIGRhdGUgY29tcGFyaXNvblxuICogLSBOdWxsLXNhZmUgaGFuZGxpbmcgKHNvcnRzIG51bGxzIGxhc3QpXG4gKiAtIEltbXV0YWJpbGl0eSAoZG9lcyBub3QgbXV0YXRlIGlucHV0IGFycmF5KVxuICpcbiAqIFNFQ1VSSVRZIE5PVEVTOlxuICogLSBBbGwgc29ydEJ5IHZhbHVlcyB2YWxpZGF0ZWQgYWdhaW5zdCBTb3J0YWJsZUZpZWxkIGVudW1cbiAqIC0gTm8gYXJiaXRyYXJ5IGZpZWxkIGFjY2VzcyBhbGxvd2VkXG4gKiAtIFN0YWJsZSBzb3J0IHByZXZlbnRzIHRpbWluZyBhdHRhY2tzXG4gKiAtIElucHV0IHZhbGlkYXRpb24gcHJldmVudHMgaW5qZWN0aW9uXG4gKlxuICogQG1vZHVsZSBTb3J0U2VydmljZVxuICovXG5cbmltcG9ydCB7IElFbGVtZW50IH0gZnJvbSAnLi4vLi4vdHlwZXMvZWxlbWVudHMvSUVsZW1lbnQuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7XG4gIFNvcnRPcHRpb25zLFxuICBTb3J0YWJsZUZpZWxkLFxuICBTb3J0T3JkZXIsXG4gIEFwcGxpZWRTb3J0aW5nLFxuICBJU29ydFNlcnZpY2UsXG4gIGV4dHJhY3RTb3J0VmFsdWUsXG59IGZyb20gJy4vdHlwZXMuanMnO1xuXG4vKipcbiAqIFNlcnZpY2UgZm9yIHNvcnRpbmcgYXJyYXlzIG9mIGVsZW1lbnRzXG4gKlxuICogSW1wbGVtZW50cyBJU29ydFNlcnZpY2Ugd2l0aCBzdXBwb3J0IGZvciBtdWx0aXBsZSBzb3J0IGZpZWxkcyBhbmQgc3RhYmxlIG9yZGVyaW5nLlxuICogRGVzaWduZWQgdG8gYmUgc3RhdGVsZXNzIGFuZCBpbmplY3RhYmxlIHZpYSBkZXBlbmRlbmN5IGluamVjdGlvbi5cbiAqXG4gKiBAdGVtcGxhdGUgVCAtIEVsZW1lbnQgdHlwZSBleHRlbmRpbmcgSUVsZW1lbnRcbiAqL1xuZXhwb3J0IGNsYXNzIFNvcnRTZXJ2aWNlPFQgZXh0ZW5kcyBJRWxlbWVudCA9IElFbGVtZW50PiBpbXBsZW1lbnRzIElTb3J0U2VydmljZTxUPiB7XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IERFRkFVTFRfU09SVF9CWTogU29ydGFibGVGaWVsZCA9ICduYW1lJztcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgREVGQVVMVF9TT1JUX09SREVSOiBTb3J0T3JkZXIgPSAnYXNjJztcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgVkFMSURfU09SVF9GSUVMRFM6IHJlYWRvbmx5IFNvcnRhYmxlRmllbGRbXSA9IFtcbiAgICAnbmFtZScsXG4gICAgJ2NyZWF0ZWQnLFxuICAgICdtb2RpZmllZCcsXG4gICAgJ3ZlcnNpb24nLFxuICAgICdyZXRlbnRpb24nLFxuICBdIGFzIGNvbnN0O1xuXG4gIC8qKlxuICAgKiBTb3J0IGFuIGFycmF5IG9mIGVsZW1lbnRzIGFjY29yZGluZyB0byB0aGUgc3BlY2lmaWVkIG9wdGlvbnNcbiAgICpcbiAgICogSU1QTEVNRU5UQVRJT04gTk9URVM6XG4gICAqIC0gRG9lcyBOT1QgbXV0YXRlIHRoZSBpbnB1dCBhcnJheSAoY3JlYXRlcyBhIHNoYWxsb3cgY29weSlcbiAgICogLSBNYWludGFpbnMgc3RhYmxlIHNvcnQgb3JkZXIgKGl0ZW1zIHdpdGggZXF1YWwgdmFsdWVzIHJldGFpbiBvcmlnaW5hbCBvcmRlcilcbiAgICogLSBIYW5kbGVzIG1pc3NpbmcgdmFsdWVzIGJ5IHNvcnRpbmcgdGhlbSBsYXN0XG4gICAqIC0gVmFsaWRhdGVzIHNvcnRCeSBmaWVsZCBhZ2FpbnN0IGFsbG93ZWQgZW51bSB2YWx1ZXNcbiAgICpcbiAgICogQHBhcmFtIGl0ZW1zIC0gQXJyYXkgb2YgZWxlbWVudHMgdG8gc29ydFxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIFNvcnRpbmcgY29uZmlndXJhdGlvblxuICAgKiBAcmV0dXJucyBOZXcgc29ydGVkIGFycmF5XG4gICAqIEB0aHJvd3Mge0Vycm9yfSBJZiBzb3J0IG9wdGlvbnMgYXJlIGludmFsaWRcbiAgICovXG4gIHB1YmxpYyBzb3J0KGl0ZW1zOiBUW10sIG9wdGlvbnM/OiBTb3J0T3B0aW9ucyk6IFRbXSB7XG4gICAgLy8gVmFsaWRhdGUgb3B0aW9uc1xuICAgIHRoaXMudmFsaWRhdGVPcHRpb25zKG9wdGlvbnMpO1xuXG4gICAgLy8gUmV0dXJuIGVtcHR5IGFycmF5IGFzLWlzXG4gICAgaWYgKGl0ZW1zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cblxuICAgIC8vIEV4dHJhY3QgdmFsaWRhdGVkIG9wdGlvbnMgd2l0aCBkZWZhdWx0c1xuICAgIGNvbnN0IHNvcnRCeSA9IG9wdGlvbnM/LnNvcnRCeSA/PyBTb3J0U2VydmljZS5ERUZBVUxUX1NPUlRfQlk7XG4gICAgY29uc3Qgc29ydE9yZGVyID0gb3B0aW9ucz8uc29ydE9yZGVyID8/IFNvcnRTZXJ2aWNlLkRFRkFVTFRfU09SVF9PUkRFUjtcblxuICAgIC8vIENyZWF0ZSBzaGFsbG93IGNvcHkgdG8gYXZvaWQgbXV0YXRpb25cbiAgICBjb25zdCBzb3J0ZWRJdGVtcyA9IFsuLi5pdGVtc107XG5cbiAgICAvLyBTb3J0IHdpdGggZmllbGQtc3BlY2lmaWMgY29tcGFyYXRvclxuICAgIHNvcnRlZEl0ZW1zLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuY29tcGFyZUVsZW1lbnRzKGEsIGIsIHNvcnRCeSk7XG4gICAgICByZXR1cm4gc29ydE9yZGVyID09PSAnZGVzYycgPyAtcmVzdWx0IDogcmVzdWx0O1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHNvcnRlZEl0ZW1zO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgZGVmYXVsdCBzb3J0aW5nIGNvbmZpZ3VyYXRpb25cbiAgICpcbiAgICogQHJldHVybnMgRGVmYXVsdCBzb3J0IG9wdGlvbnNcbiAgICovXG4gIHB1YmxpYyBnZXREZWZhdWx0U29ydGluZygpOiBBcHBsaWVkU29ydGluZyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNvcnRCeTogU29ydFNlcnZpY2UuREVGQVVMVF9TT1JUX0JZLFxuICAgICAgc29ydE9yZGVyOiBTb3J0U2VydmljZS5ERUZBVUxUX1NPUlRfT1JERVIsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBzb3J0IG9wdGlvbnMgd2l0aG91dCBhcHBseWluZyB0aGVtXG4gICAqXG4gICAqIFNFQ1VSSVRZOiBWYWxpZGF0ZXMgc29ydEJ5IGFnYWluc3QgZW51bSB0byBwcmV2ZW50IGFyYml0cmFyeSBmaWVsZCBhY2Nlc3NcbiAgICpcbiAgICogQHBhcmFtIG9wdGlvbnMgLSBTb3J0IG9wdGlvbnMgdG8gdmFsaWRhdGVcbiAgICogQHJldHVybnMgVHJ1ZSBpZiBvcHRpb25zIGFyZSB2YWxpZFxuICAgKiBAdGhyb3dzIHtFcnJvcn0gSWYgb3B0aW9ucyBhcmUgaW52YWxpZFxuICAgKi9cbiAgcHVibGljIHZhbGlkYXRlT3B0aW9ucyhvcHRpb25zPzogU29ydE9wdGlvbnMpOiBib29sZWFuIHtcbiAgICBpZiAoIW9wdGlvbnMpIHtcbiAgICAgIHJldHVybiB0cnVlOyAvLyBObyBvcHRpb25zIG1lYW5zIHVzZSBkZWZhdWx0c1xuICAgIH1cblxuICAgIC8vIFZhbGlkYXRlIHNvcnRCeSBmaWVsZFxuICAgIGlmIChvcHRpb25zLnNvcnRCeSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAoIVNvcnRTZXJ2aWNlLlZBTElEX1NPUlRfRklFTERTLmluY2x1ZGVzKG9wdGlvbnMuc29ydEJ5KSkge1xuICAgICAgICBjb25zdCBlcnJvciA9IGBJbnZhbGlkIHNvcnRCeSBmaWVsZDogJHtvcHRpb25zLnNvcnRCeX0uIE11c3QgYmUgb25lIG9mOiAke1NvcnRTZXJ2aWNlLlZBTElEX1NPUlRfRklFTERTLmpvaW4oJywgJyl9YDtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdTb3J0U2VydmljZS52YWxpZGF0ZU9wdGlvbnMnLCB7IGVycm9yIH0pO1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoZXJyb3IpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFZhbGlkYXRlIHNvcnRPcmRlclxuICAgIGlmIChvcHRpb25zLnNvcnRPcmRlciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAob3B0aW9ucy5zb3J0T3JkZXIgIT09ICdhc2MnICYmIG9wdGlvbnMuc29ydE9yZGVyICE9PSAnZGVzYycpIHtcbiAgICAgICAgY29uc3QgZXJyb3IgPSBgSW52YWxpZCBzb3J0T3JkZXI6ICR7b3B0aW9ucy5zb3J0T3JkZXJ9LiBNdXN0IGJlICdhc2MnIG9yICdkZXNjJ2A7XG4gICAgICAgIGxvZ2dlci5lcnJvcignU29ydFNlcnZpY2UudmFsaWRhdGVPcHRpb25zJywgeyBlcnJvciB9KTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGVycm9yKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wYXJlIHR3byBlbGVtZW50cyBmb3Igc29ydGluZ1xuICAgKlxuICAgKiBJTVBMRU1FTlRBVElPTiBOT1RFUzpcbiAgICogLSBSZXR1cm5zIG5lZ2F0aXZlIGlmIGEgPCBiLCBwb3NpdGl2ZSBpZiBhID4gYiwgemVybyBpZiBlcXVhbFxuICAgKiAtIEhhbmRsZXMgbnVsbC91bmRlZmluZWQgdmFsdWVzIGJ5IHNvcnRpbmcgdGhlbSBsYXN0XG4gICAqIC0gVXNlcyBmaWVsZC1zcGVjaWZpYyBjb21wYXJpc29uIGxvZ2ljXG4gICAqIC0gTWFpbnRhaW5zIHN0YWJsZSBzb3J0IChyZXR1cm5zIDAgZm9yIGVxdWFsIHZhbHVlcylcbiAgICpcbiAgICogQHBhcmFtIGEgLSBGaXJzdCBlbGVtZW50XG4gICAqIEBwYXJhbSBiIC0gU2Vjb25kIGVsZW1lbnRcbiAgICogQHBhcmFtIGZpZWxkIC0gRmllbGQgdG8gY29tcGFyZVxuICAgKiBAcmV0dXJucyBDb21wYXJpc29uIHJlc3VsdCAoLTEsIDAsIDEpXG4gICAqL1xuICBwcml2YXRlIGNvbXBhcmVFbGVtZW50cyhhOiBULCBiOiBULCBmaWVsZDogU29ydGFibGVGaWVsZCk6IG51bWJlciB7XG4gICAgY29uc3QgdmFsdWVBID0gZXh0cmFjdFNvcnRWYWx1ZShhLCBmaWVsZCk7XG4gICAgY29uc3QgdmFsdWVCID0gZXh0cmFjdFNvcnRWYWx1ZShiLCBmaWVsZCk7XG5cbiAgICAvLyBIYW5kbGUgbnVsbC91bmRlZmluZWQgLSBzb3J0IGxhc3RcbiAgICBpZiAodmFsdWVBID09PSB1bmRlZmluZWQgJiYgdmFsdWVCID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICBpZiAodmFsdWVBID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiAxOyAvLyBhIGlzIG51bGwsIHNvcnQgYWZ0ZXIgYlxuICAgIH1cbiAgICBpZiAodmFsdWVCID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiAtMTsgLy8gYiBpcyBudWxsLCBzb3J0IGEgYmVmb3JlIGJcbiAgICB9XG5cbiAgICAvLyBGaWVsZC1zcGVjaWZpYyBjb21wYXJpc29uXG4gICAgc3dpdGNoIChmaWVsZCkge1xuICAgICAgY2FzZSAnbmFtZSc6XG4gICAgICAgIHJldHVybiB0aGlzLmNvbXBhcmVTdHJpbmdzKFN0cmluZyh2YWx1ZUEpLCBTdHJpbmcodmFsdWVCKSk7XG5cbiAgICAgIGNhc2UgJ2NyZWF0ZWQnOlxuICAgICAgY2FzZSAnbW9kaWZpZWQnOlxuICAgICAgICByZXR1cm4gdGhpcy5jb21wYXJlRGF0ZXMoU3RyaW5nKHZhbHVlQSksIFN0cmluZyh2YWx1ZUIpKTtcblxuICAgICAgY2FzZSAndmVyc2lvbic6XG4gICAgICAgIHJldHVybiB0aGlzLmNvbXBhcmVWZXJzaW9ucyhTdHJpbmcodmFsdWVBKSwgU3RyaW5nKHZhbHVlQikpO1xuXG4gICAgICBjYXNlICdyZXRlbnRpb24nOlxuICAgICAgICByZXR1cm4gdGhpcy5jb21wYXJlTnVtYmVycyhOdW1iZXIodmFsdWVBKSwgTnVtYmVyKHZhbHVlQikpO1xuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICAvLyBTaG91bGQgbmV2ZXIgcmVhY2ggaGVyZSBkdWUgdG8gdmFsaWRhdGlvblxuICAgICAgICBsb2dnZXIud2FybignU29ydFNlcnZpY2UuY29tcGFyZUVsZW1lbnRzJywge1xuICAgICAgICAgIG1lc3NhZ2U6ICdVbmtub3duIHNvcnQgZmllbGQsIHVzaW5nIHN0cmluZyBjb21wYXJpc29uJyxcbiAgICAgICAgICBmaWVsZCxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbXBhcmVTdHJpbmdzKFN0cmluZyh2YWx1ZUEpLCBTdHJpbmcodmFsdWVCKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENvbXBhcmUgdHdvIHN0cmluZ3MgY2FzZS1pbnNlbnNpdGl2ZWx5XG4gICAqXG4gICAqIFVzZXMgbG9jYWxlQ29tcGFyZSBmb3IgcHJvcGVyIFVuaWNvZGUgaGFuZGxpbmcgYW5kIHN0YWJsZSBvcmRlcmluZy5cbiAgICpcbiAgICogQHBhcmFtIGEgLSBGaXJzdCBzdHJpbmdcbiAgICogQHBhcmFtIGIgLSBTZWNvbmQgc3RyaW5nXG4gICAqIEByZXR1cm5zIENvbXBhcmlzb24gcmVzdWx0XG4gICAqL1xuICBwcml2YXRlIGNvbXBhcmVTdHJpbmdzKGE6IHN0cmluZywgYjogc3RyaW5nKTogbnVtYmVyIHtcbiAgICByZXR1cm4gYS5sb2NhbGVDb21wYXJlKGIsIHVuZGVmaW5lZCwgeyBzZW5zaXRpdml0eTogJ2Jhc2UnIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXBhcmUgdHdvIElTTyA4NjAxIGRhdGUgc3RyaW5ncyBhcyB0aW1lc3RhbXBzXG4gICAqXG4gICAqIENvbnZlcnRzIHRvIERhdGUgb2JqZWN0cyBhbmQgY29tcGFyZXMgbnVtZXJpY2FsbHkuXG4gICAqIEludmFsaWQgZGF0ZXMgYXJlIHRyZWF0ZWQgYXMgdW5kZWZpbmVkIChzb3J0ZWQgbGFzdCkuXG4gICAqXG4gICAqIEBwYXJhbSBhIC0gRmlyc3QgZGF0ZSBzdHJpbmdcbiAgICogQHBhcmFtIGIgLSBTZWNvbmQgZGF0ZSBzdHJpbmdcbiAgICogQHJldHVybnMgQ29tcGFyaXNvbiByZXN1bHRcbiAgICovXG4gIHByaXZhdGUgY29tcGFyZURhdGVzKGE6IHN0cmluZywgYjogc3RyaW5nKTogbnVtYmVyIHtcbiAgICBjb25zdCBkYXRlQSA9IG5ldyBEYXRlKGEpLmdldFRpbWUoKTtcbiAgICBjb25zdCBkYXRlQiA9IG5ldyBEYXRlKGIpLmdldFRpbWUoKTtcblxuICAgIC8vIEhhbmRsZSBpbnZhbGlkIGRhdGVzXG4gICAgaWYgKGlzTmFOKGRhdGVBKSAmJiBpc05hTihkYXRlQikpIHtcbiAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICBpZiAoaXNOYU4oZGF0ZUEpKSB7XG4gICAgICByZXR1cm4gMTtcbiAgICB9XG4gICAgaWYgKGlzTmFOKGRhdGVCKSkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cblxuICAgIHJldHVybiBkYXRlQSAtIGRhdGVCO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXBhcmUgdHdvIHNlbWFudGljIHZlcnNpb24gc3RyaW5ncyAoeC55LnogZm9ybWF0KVxuICAgKlxuICAgKiBDb21wYXJlcyBtYWpvciwgbWlub3IsIGFuZCBwYXRjaCB2ZXJzaW9ucyBudW1lcmljYWxseS5cbiAgICogSW52YWxpZCB2ZXJzaW9ucyBhcmUgc29ydGVkIHVzaW5nIHN0cmluZyBjb21wYXJpc29uLlxuICAgKlxuICAgKiBAcGFyYW0gYSAtIEZpcnN0IHZlcnNpb24gc3RyaW5nXG4gICAqIEBwYXJhbSBiIC0gU2Vjb25kIHZlcnNpb24gc3RyaW5nXG4gICAqIEByZXR1cm5zIENvbXBhcmlzb24gcmVzdWx0XG4gICAqL1xuICBwcml2YXRlIGNvbXBhcmVWZXJzaW9ucyhhOiBzdHJpbmcsIGI6IHN0cmluZyk6IG51bWJlciB7XG4gICAgY29uc3QgcGFydHNBID0gdGhpcy5wYXJzZVZlcnNpb24oYSk7XG4gICAgY29uc3QgcGFydHNCID0gdGhpcy5wYXJzZVZlcnNpb24oYik7XG5cbiAgICAvLyBJZiBlaXRoZXIgdmVyc2lvbiBpcyBpbnZhbGlkLCBmYWxsIGJhY2sgdG8gc3RyaW5nIGNvbXBhcmlzb25cbiAgICBpZiAoIXBhcnRzQSB8fCAhcGFydHNCKSB7XG4gICAgICByZXR1cm4gdGhpcy5jb21wYXJlU3RyaW5ncyhhLCBiKTtcbiAgICB9XG5cbiAgICAvLyBDb21wYXJlIG1ham9yIHZlcnNpb25cbiAgICBpZiAocGFydHNBLm1ham9yICE9PSBwYXJ0c0IubWFqb3IpIHtcbiAgICAgIHJldHVybiBwYXJ0c0EubWFqb3IgLSBwYXJ0c0IubWFqb3I7XG4gICAgfVxuXG4gICAgLy8gQ29tcGFyZSBtaW5vciB2ZXJzaW9uXG4gICAgaWYgKHBhcnRzQS5taW5vciAhPT0gcGFydHNCLm1pbm9yKSB7XG4gICAgICByZXR1cm4gcGFydHNBLm1pbm9yIC0gcGFydHNCLm1pbm9yO1xuICAgIH1cblxuICAgIC8vIENvbXBhcmUgcGF0Y2ggdmVyc2lvblxuICAgIHJldHVybiBwYXJ0c0EucGF0Y2ggLSBwYXJ0c0IucGF0Y2g7XG4gIH1cblxuICAvKipcbiAgICogUGFyc2UgYSBzZW1hbnRpYyB2ZXJzaW9uIHN0cmluZyBpbnRvIGNvbXBvbmVudHNcbiAgICpcbiAgICogSGFuZGxlcyBmb3JtYXRzIGxpa2UgXCIxLjAuMFwiLCBcIjIuNS4zXCIsIGV0Yy5cbiAgICogUmV0dXJucyBudWxsIGZvciBpbnZhbGlkIHZlcnNpb25zLlxuICAgKlxuICAgKiBAcGFyYW0gdmVyc2lvbiAtIFZlcnNpb24gc3RyaW5nIHRvIHBhcnNlXG4gICAqIEByZXR1cm5zIFBhcnNlZCB2ZXJzaW9uIGNvbXBvbmVudHMgb3IgbnVsbFxuICAgKi9cbiAgcHJpdmF0ZSBwYXJzZVZlcnNpb24odmVyc2lvbjogc3RyaW5nKTogeyBtYWpvcjogbnVtYmVyOyBtaW5vcjogbnVtYmVyOyBwYXRjaDogbnVtYmVyIH0gfCBudWxsIHtcbiAgICBjb25zdCBtYXRjaCA9IHZlcnNpb24ubWF0Y2goL14oXFxkKylcXC4oXFxkKylcXC4oXFxkKykvKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgbWFqb3I6IHBhcnNlSW50KG1hdGNoWzFdLCAxMCksXG4gICAgICBtaW5vcjogcGFyc2VJbnQobWF0Y2hbMl0sIDEwKSxcbiAgICAgIHBhdGNoOiBwYXJzZUludChtYXRjaFszXSwgMTApLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ29tcGFyZSB0d28gbnVtYmVyc1xuICAgKlxuICAgKiBIYW5kbGVzIE5hTiBieSB0cmVhdGluZyBhcyB1bmRlZmluZWQgKHNvcnRlZCBsYXN0KS5cbiAgICpcbiAgICogQHBhcmFtIGEgLSBGaXJzdCBudW1iZXJcbiAgICogQHBhcmFtIGIgLSBTZWNvbmQgbnVtYmVyXG4gICAqIEByZXR1cm5zIENvbXBhcmlzb24gcmVzdWx0XG4gICAqL1xuICBwcml2YXRlIGNvbXBhcmVOdW1iZXJzKGE6IG51bWJlciwgYjogbnVtYmVyKTogbnVtYmVyIHtcbiAgICAvLyBIYW5kbGUgTmFOXG4gICAgaWYgKGlzTmFOKGEpICYmIGlzTmFOKGIpKSB7XG4gICAgICByZXR1cm4gMDtcbiAgICB9XG4gICAgaWYgKGlzTmFOKGEpKSB7XG4gICAgICByZXR1cm4gMTtcbiAgICB9XG4gICAgaWYgKGlzTmFOKGIpKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuXG4gICAgcmV0dXJuIGEgLSBiO1xuICB9XG59XG4iXX0=