@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.
683 lines • 78.7 kB
JavaScript
/**
* SerializationService - Centralized serialization for all element types
*
* Eliminates ~300+ lines of duplicate serialization code across SkillManager,
* MemoryManager, TemplateManager, AgentManager, and EnsembleManager.
*
* Key Features:
* - Unified YAML parsing and dumping with security validation
* - Consistent JSON operations across all element types
* - Comprehensive metadata cleaning utilities
* - Format auto-detection
* - Security event logging integration
*
* @example
* ```typescript
* // Inject via DI container
* constructor(private serializationService: SerializationService) {}
*
* // Parse YAML with frontmatter
* const result = this.serializationService.parseFrontmatter(data, {
* maxYamlSize: 64 * 1024,
* validateContent: true,
* source: 'SkillManager.importElement'
* });
*
* // Create frontmatter
* const markdown = this.serializationService.createFrontmatter(metadata, content, {
* schema: 'failsafe',
* cleanMetadata: true
* });
* ```
*/
import * as yaml from 'js-yaml';
import matter from 'gray-matter';
import { SecureYamlParser } from '../security/secureYamlParser.js';
import { SecurityMonitor } from '../security/securityMonitor.js';
import { logger } from '../utils/logger.js';
// ============================================================================
// SERVICE IMPLEMENTATION
// ============================================================================
/**
* Service for centralized serialization operations
*
* This service provides unified serialization functionality to eliminate code
* duplication across element managers. Supports:
* - YAML parsing and dumping
* - JSON parsing and stringification
* - Frontmatter creation and parsing
* - Metadata cleaning
* - Format detection
* - Security validation
*/
/**
* Fix #910: Canonical metadata field order for deterministic YAML output.
* Fields are ordered: identity → format → content → type-specific → infrastructure.
* Fields not listed appear after all listed fields, sorted alphabetically.
*/
export const METADATA_FIELD_ORDER = [
// Identity
'name', 'type', 'format_version', 'version', 'description',
// Attribution
'author', 'created', 'modified', 'category',
// Content fields
'instructions',
// Classification
'tags', 'triggers',
// Agent-specific (ordered by importance)
'goal', 'activates', 'tools', 'systemPrompt', 'autonomy', 'resilience',
// Ensemble-specific
'elements', 'activationStrategy', 'conflictResolution', 'contextSharing',
'resourceLimits', 'allowNested', 'maxNestingDepth',
// Template-specific
'variables', 'outputFormat', 'output_format',
// Memory-specific
'autoLoad', 'priority', 'retention', 'retentionPolicy',
// Security
'gatekeeper',
// Infrastructure (always last)
'unique_id',
];
const TRANSIENT_METADATA_FIELDS = new Set([
'gatekeeperDiagnostics',
]);
// Pre-created Set for O(1) lookup in orderMetadataFields (avoids allocation per call)
const METADATA_FIELD_ORDER_SET = new Set(METADATA_FIELD_ORDER);
/**
* Reorder an object's keys according to METADATA_FIELD_ORDER.
* Keys not in the order list appear after all ordered keys, sorted alphabetically.
*/
export function orderMetadataFields(metadata) {
const ordered = {};
// First: add fields in canonical order
for (const key of METADATA_FIELD_ORDER) {
if (key in metadata) {
ordered[key] = metadata[key];
}
}
// Then: add remaining fields alphabetically
const remaining = Object.keys(metadata).filter(k => !METADATA_FIELD_ORDER_SET.has(k)).sort((a, b) => a.localeCompare(b));
for (const key of remaining) {
ordered[key] = metadata[key];
}
return ordered;
}
export class SerializationService {
// Default limits
static DEFAULT_MAX_YAML_SIZE = 64 * 1024; // 64KB
static DEFAULT_MAX_CONTENT_SIZE = 1024 * 1024; // 1MB
constructor() {
// No configuration needed - stateless service
}
// ========================================================================
// YAML PARSING
// ========================================================================
/**
* Parse pure YAML with security validation
*
* Handles:
* - Size validation
* - Schema selection
* - Structure validation
* - Security event logging
*
* @param data - YAML string to parse
* @param options - Parsing options
* @returns Parsed object
* @throws Error if size exceeds limit or structure is invalid
*
* @example
* ```typescript
* const parsed = service.parsePureYaml(yamlString, {
* schema: 'failsafe',
* maxSize: 64 * 1024,
* validateStructure: true,
* source: 'SkillManager.importElement'
* });
* ```
*/
parsePureYaml(data, options = {}) {
const { schema = 'failsafe', maxSize = SerializationService.DEFAULT_MAX_YAML_SIZE, validateStructure = true, source = 'SerializationService.parsePureYaml' } = options;
// Size validation
if (data.length > maxSize) {
throw new Error(`YAML content exceeds allowed size of ${maxSize} bytes`);
}
// Select YAML schema
const yamlSchema = this.getYamlSchema(schema);
// Parse YAML
let parsed;
try {
parsed = yaml.load(data, {
schema: yamlSchema,
json: false,
onWarning: (warning) => {
SecurityMonitor.logSecurityEvent({
type: 'YAML_PARSING_WARNING',
severity: 'LOW',
source,
details: `YAML warning: ${warning.message}`
});
}
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn(`YAML parsing failed in ${source}: ${errorMessage}`);
throw new Error(`Failed to parse YAML: ${errorMessage}`);
}
// Structure validation
if (validateStructure) {
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
throw new Error('YAML must contain an object at root level');
}
// Check for malicious keys
const keys = Object.keys(parsed);
for (const key of keys) {
if (key.includes('[object Object]') || key.includes('function')) {
throw new Error('Invalid YAML structure detected');
}
}
}
// Log success
SecurityMonitor.logSecurityEvent({
type: 'YAML_PARSE_SUCCESS',
severity: 'LOW',
source,
details: 'YAML content safely parsed'
});
return parsed;
}
/**
* Parse frontmatter + content (Markdown with YAML)
*
* Auto-detects:
* - Pure YAML (wraps with --- markers for parsing)
* - Markdown with frontmatter (parses as-is)
*
* Uses SecureYamlParser for security validation.
*
* @param data - Markdown string with frontmatter
* @param options - Parsing options
* @returns Parsed frontmatter and content
*
* @example
* ```typescript
* const result = service.parseFrontmatter(markdownString, {
* maxYamlSize: 64 * 1024,
* validateContent: true,
* source: 'TemplateManager.importElement'
* });
* // result.data = { name: '...', description: '...' }
* // result.content = 'Markdown content...'
* ```
*/
parseFrontmatter(data, options = {}) {
const { maxYamlSize = SerializationService.DEFAULT_MAX_YAML_SIZE, maxContentSize = SerializationService.DEFAULT_MAX_CONTENT_SIZE, validateContent = false, source = 'SerializationService.parseFrontmatter', schema = 'failsafe' } = options;
// Check if data has frontmatter markers
if (this.hasFrontmatter(data)) {
// Use SecureYamlParser for frontmatter format
const parsed = SecureYamlParser.parse(data, {
maxYamlSize,
maxContentSize,
validateContent
});
return {
data: parsed.data,
content: parsed.content.trim() // Trim leading/trailing whitespace
};
}
else {
// Pure YAML without frontmatter markers (MemoryManager pattern)
// Parse the entire content as YAML
// Use caller-specified schema to preserve types when needed (e.g., 'json' for booleans/numbers)
const parsed = this.parsePureYaml(data, {
schema,
maxSize: maxYamlSize,
validateStructure: true,
source
});
return {
data: parsed,
content: '' // No markdown content for pure YAML
};
}
}
/**
* Auto-detect format and parse
*
* Detects:
* - Frontmatter (starts with ---)
* - Pure YAML (valid YAML object)
* - JSON (starts with { or [)
*
* @param data - String to parse
* @param options - Parsing options for each format
* @returns Parsed result with detected format
*
* @example
* ```typescript
* const result = service.parseAuto(data, {
* source: 'SkillManager.importElement'
* });
* if (result.format === 'frontmatter') {
* console.log(result.data, result.content);
* } else if (result.format === 'yaml') {
* console.log(result.data);
* }
* ```
*/
parseAuto(data, options = {}) {
const format = this.detectFormat(data);
switch (format) {
case 'frontmatter': {
const parsed = this.parseFrontmatter(data, options.frontmatterOptions);
return {
format,
data: parsed.data,
content: parsed.content
};
}
case 'yaml': {
const parsed = this.parsePureYaml(data, options.yamlOptions);
return {
format,
data: parsed
};
}
case 'json': {
const parsed = this.parseJson(data, options.jsonOptions);
return {
format,
data: parsed
};
}
default:
throw new Error('Unable to detect valid format (frontmatter, YAML, or JSON)');
}
}
// ========================================================================
// YAML DUMPING
// ========================================================================
/**
* Dump object to YAML string
*
* Supports:
* - FAILSAFE_SCHEMA (default - strings only, most secure)
* - DEFAULT_SCHEMA (for TemplateManager compatibility)
* - CORE_SCHEMA (for special cases)
*
* @param data - Object to serialize
* @param options - Dump options
* @returns YAML string
*
* @example
* ```typescript
* const yamlString = service.dumpYaml(data, {
* schema: 'failsafe',
* sortKeys: true,
* skipInvalid: true,
* noRefs: true
* });
* ```
*/
dumpYaml(data, options = {}) {
const { schema = 'failsafe', sortKeys = true, skipInvalid = true, noRefs = true, indent = 2, lineWidth = 80, flowLevel = -1 } = options;
const yamlSchema = this.getYamlSchema(schema);
return yaml.dump(data, {
schema: yamlSchema,
sortKeys,
skipInvalid,
noRefs,
indent,
lineWidth,
flowLevel
});
}
/**
* Create frontmatter + content (Markdown with YAML)
*
* Methods:
* - 'matter' - Uses matter.stringify() (SkillManager pattern)
* - 'manual' - Manual YAML dump + concatenation (other managers)
*
* @param metadata - Frontmatter metadata
* @param content - Markdown content
* @param options - Dump options
* @returns Markdown string with frontmatter
*
* @example
* ```typescript
* const markdown = service.createFrontmatter(metadata, content, {
* schema: 'failsafe',
* method: 'matter',
* cleanMetadata: true,
* cleaningStrategy: 'remove-undefined'
* });
* // Returns: ---\nmetadata...\n---\n\nContent...
* ```
*/
createFrontmatter(metadata, content, options = {}) {
const { method = 'manual', cleanMetadata = false, cleaningStrategy = 'remove-undefined', schema = 'failsafe', sortKeys: _sortKeys = true, skipInvalid = true, noRefs = true } = options;
// Clean metadata if requested
let processedMetadata = cleanMetadata
? this.cleanMetadata(metadata, { strategy: cleaningStrategy })
: metadata;
// Fix #910: Apply canonical field ordering instead of alphabetical sort
processedMetadata = orderMetadataFields(processedMetadata);
if (method === 'matter') {
// Use matter.stringify() (SkillManager pattern)
return matter.stringify(content, processedMetadata);
}
else {
// Manual construction (other managers)
const yamlString = this.dumpYaml(processedMetadata, {
schema,
sortKeys: false, // Fix #910: field order is now canonical, not alphabetical
skipInvalid,
noRefs
});
// Construct frontmatter manually
if (content) {
return `---\n${yamlString}---\n\n${content}`;
}
else {
return `---\n${yamlString}---\n`;
}
}
}
// ========================================================================
// JSON OPERATIONS
// ========================================================================
/**
* Parse JSON with error handling and security logging
*
* @param data - JSON string
* @param options - Parse options
* @returns Parsed object
* @throws Error if parsing fails
*
* @example
* ```typescript
* const parsed = service.parseJson(jsonString, {
* source: 'TemplateManager.importElement'
* });
* ```
*/
parseJson(data, options = {}) {
const { maxSize = SerializationService.DEFAULT_MAX_CONTENT_SIZE, validateStructure = false, source = 'SerializationService.parseJson' } = options;
// Size validation
this.validateSize(data, maxSize, 'JSON content');
try {
const parsed = JSON.parse(data);
// Structure validation
if (validateStructure) {
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
throw new Error('JSON must contain an object at root level');
}
}
return parsed;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn(`JSON parsing failed in ${source}: ${errorMessage}`);
throw new Error(`Failed to parse JSON: ${errorMessage}`);
}
}
/**
* Stringify object to JSON
*
* @param data - Object to serialize
* @param options - Stringify options
* @returns JSON string
*
* @example
* ```typescript
* const jsonString = service.stringifyJson(data, {
* indent: 2,
* cleanMetadata: true,
* cleaningStrategy: 'remove-undefined'
* });
* ```
*/
stringifyJson(data, options = {}) {
const { pretty = false, indent = 2, cleanMetadata = false, cleaningStrategy = 'remove-undefined' } = options;
// Clean metadata if requested
const processedData = cleanMetadata
? this.cleanMetadata(data, { strategy: cleaningStrategy })
: data;
// Use indent if pretty is true, otherwise no formatting
const indentValue = pretty ? indent : 0;
return JSON.stringify(processedData, null, indentValue);
}
// ========================================================================
// METADATA UTILITIES
// ========================================================================
/**
* Clean metadata by removing undefined/null values
*
* Strategies:
* - 'remove-undefined' - Remove undefined only (SkillManager, TemplateManager)
* - 'remove-null' - Remove null only
* - 'remove-both' - Remove both undefined and null (AgentManager)
* - 'none' - No cleaning (MemoryManager, EnsembleManager)
*
* @param metadata - Metadata object to clean
* @param options - Cleaning options
* @returns Cleaned metadata (new object, original not modified)
*
* @example
* ```typescript
* const cleaned = service.cleanMetadata(metadata, {
* strategy: 'remove-undefined'
* });
* ```
*/
cleanMetadata(metadata, options = {}) {
const { strategy = 'remove-undefined', removeEmpty = false } = options;
if (strategy === 'none' && !removeEmpty) {
return metadata;
}
// Handle non-object cases
if (typeof metadata !== 'object' || metadata === null) {
return metadata;
}
// Handle arrays
if (Array.isArray(metadata)) {
// Filter out null/undefined based on strategy
let cleaned = metadata.map(item => this.cleanMetadata(item, options));
if (strategy === 'remove-undefined') {
cleaned = cleaned.filter(item => item !== undefined);
}
else if (strategy === 'remove-null') {
cleaned = cleaned.filter(item => item !== null);
}
else if (strategy === 'remove-both') {
cleaned = cleaned.filter(item => item !== undefined && item !== null);
}
return cleaned;
}
// Clean object based on strategy
const cleaned = {};
for (const [key, value] of Object.entries(metadata)) {
if (TRANSIENT_METADATA_FIELDS.has(key)) {
continue;
}
const shouldRemove = (strategy === 'remove-undefined' && value === undefined) ||
(strategy === 'remove-null' && value === null) ||
(strategy === 'remove-both' && (value === undefined || value === null));
if (!shouldRemove) {
// Recursively clean nested objects/arrays
if (typeof value === 'object' && value !== null) {
const cleanedValue = this.cleanMetadata(value, options);
// Check if we should remove empty arrays/objects
if (removeEmpty) {
const isEmpty = Array.isArray(cleanedValue)
? cleanedValue.length === 0
: Object.keys(cleanedValue).length === 0;
if (!isEmpty) {
cleaned[key] = cleanedValue;
}
}
else {
cleaned[key] = cleanedValue;
}
}
else {
cleaned[key] = value;
}
}
}
return cleaned;
}
// ========================================================================
// FORMAT DETECTION
// ========================================================================
/**
* Detect format of input data
*
* Detection logic:
* 1. Check for frontmatter (starts with --- after optional whitespace)
* 2. Check for JSON (starts with { or [)
* 3. Try to parse as YAML
* 4. Return 'unknown' if none match
*
* @param data - String to analyze
* @returns Detected format
*
* @example
* ```typescript
* const format = service.detectFormat(data);
* // Returns: 'frontmatter' | 'yaml' | 'json' | 'unknown'
* ```
*/
detectFormat(data) {
const trimmed = data.trim();
// Check for frontmatter
if (this.hasFrontmatter(trimmed)) {
return 'frontmatter';
}
// Check for JSON
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
try {
JSON.parse(trimmed);
return 'json';
}
catch {
// Not valid JSON, continue checking
}
}
// Try parsing as YAML
try {
const parsed = yaml.load(trimmed, {
schema: yaml.FAILSAFE_SCHEMA,
json: false
});
// Valid YAML that parses to an object
if (typeof parsed === 'object' && parsed !== null) {
return 'yaml';
}
}
catch {
// Not valid YAML
}
return 'unknown';
}
/**
* Check if data has frontmatter markers
*
* Checks for '---' at the start after optional whitespace
*
* @param data - String to check
* @returns True if has frontmatter
*
* @example
* ```typescript
* if (service.hasFrontmatter(data)) {
* const result = service.parseFrontmatter(data);
* }
* ```
*/
hasFrontmatter(data) {
const trimmed = data.trim();
return trimmed.startsWith('---');
}
// ========================================================================
// SECURITY UTILITIES
// ========================================================================
/**
* Log security event (wrapper for SecurityMonitor)
*
* Provides consistent security event logging across serialization operations
*
* @param type - Event type
* @param severity - Event severity
* @param source - Event source
* @param details - Event details
*
* @example
* ```typescript
* service.logSecurityEvent(
* 'YAML_PARSE_SUCCESS',
* 'LOW',
* 'SkillManager.importElement',
* 'Successfully parsed YAML content'
* );
* ```
*/
logSecurityEvent(type, severity, source, details) {
SecurityMonitor.logSecurityEvent({
type,
severity,
source,
details
});
}
/**
* Validate size limits
*
* Throws error if data exceeds the specified maximum size
*
* @param data - Data to check
* @param maxSize - Maximum size in bytes
* @param context - Context for error messages
* @throws Error if size exceeds limit
*
* @example
* ```typescript
* service.validateSize(data, 64 * 1024, 'YAML frontmatter');
* // Throws if data > 64KB
* ```
*/
validateSize(data, maxSize, context) {
if (data.length > maxSize) {
throw new Error(`${context} exceeds allowed size of ${maxSize} bytes (actual: ${data.length} bytes)`);
}
}
// ========================================================================
// PRIVATE HELPERS
// ========================================================================
/**
* Get YAML schema object from string identifier
*
* @param schema - Schema identifier
* @returns YAML schema object
*/
getYamlSchema(schema) {
switch (schema) {
case 'failsafe':
return yaml.FAILSAFE_SCHEMA;
case 'default':
return yaml.DEFAULT_SCHEMA;
case 'core':
return yaml.CORE_SCHEMA;
case 'json':
// JSON_SCHEMA = FAILSAFE + bool/int/float/null (safer than DEFAULT which adds timestamps)
// Use this for memories to preserve booleans (autoLoad) and numbers (priority)
return yaml.JSON_SCHEMA;
default:
logger.warn(`Unknown YAML schema '${schema}', using FAILSAFE_SCHEMA`);
return yaml.FAILSAFE_SCHEMA;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VyaWFsaXphdGlvblNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvU2VyaWFsaXphdGlvblNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0ErQkc7QUFFSCxPQUFPLEtBQUssSUFBSSxNQUFNLFNBQVMsQ0FBQztBQUNoQyxPQUFPLE1BQU0sTUFBTSxhQUFhLENBQUM7QUFDakMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxFQUFFLGVBQWUsRUFBaUIsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNoRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFxTTVDLCtFQUErRTtBQUMvRSx5QkFBeUI7QUFDekIsK0VBQStFO0FBRS9FOzs7Ozs7Ozs7OztHQVdHO0FBQ0g7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFzQjtJQUNyRCxXQUFXO0lBQ1gsTUFBTSxFQUFFLE1BQU0sRUFBRSxnQkFBZ0IsRUFBRSxTQUFTLEVBQUUsYUFBYTtJQUMxRCxjQUFjO0lBQ2QsUUFBUSxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsVUFBVTtJQUMzQyxpQkFBaUI7SUFDakIsY0FBYztJQUNkLGlCQUFpQjtJQUNqQixNQUFNLEVBQUUsVUFBVTtJQUNsQix5Q0FBeUM7SUFDekMsTUFBTSxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxZQUFZO0lBQ3RFLG9CQUFvQjtJQUNwQixVQUFVLEVBQUUsb0JBQW9CLEVBQUUsb0JBQW9CLEVBQUUsZ0JBQWdCO0lBQ3hFLGdCQUFnQixFQUFFLGFBQWEsRUFBRSxpQkFBaUI7SUFDbEQsb0JBQW9CO0lBQ3BCLFdBQVcsRUFBRSxjQUFjLEVBQUUsZUFBZTtJQUM1QyxrQkFBa0I7SUFDbEIsVUFBVSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsaUJBQWlCO0lBQ3RELFdBQVc7SUFDWCxZQUFZO0lBQ1osK0JBQStCO0lBQy9CLFdBQVc7Q0FDWixDQUFDO0FBRUYsTUFBTSx5QkFBeUIsR0FBRyxJQUFJLEdBQUcsQ0FBQztJQUN4Qyx1QkFBdUI7Q0FDeEIsQ0FBQyxDQUFDO0FBRUgsc0ZBQXNGO0FBQ3RGLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxHQUFHLENBQUMsb0JBQW9CLENBQUMsQ0FBQztBQUUvRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsbUJBQW1CLENBQUMsUUFBaUM7SUFDbkUsTUFBTSxPQUFPLEdBQTRCLEVBQUUsQ0FBQztJQUM1Qyx1Q0FBdUM7SUFDdkMsS0FBSyxNQUFNLEdBQUcsSUFBSSxvQkFBb0IsRUFBRSxDQUFDO1FBQ3ZDLElBQUksR0FBRyxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQ3BCLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDL0IsQ0FBQztJQUNILENBQUM7SUFDRCw0Q0FBNEM7SUFDNUMsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLHdCQUF3QixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN6SCxLQUFLLE1BQU0sR0FBRyxJQUFJLFNBQVMsRUFBRSxDQUFDO1FBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRCxNQUFNLE9BQU8sb0JBQW9CO0lBQy9CLGlCQUFpQjtJQUNULE1BQU0sQ0FBVSxxQkFBcUIsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUMsT0FBTztJQUMxRCxNQUFNLENBQVUsd0JBQXdCLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQyxDQUFDLE1BQU07SUFFdEU7UUFDRSw4Q0FBOEM7SUFDaEQsQ0FBQztJQUVELDJFQUEyRTtJQUMzRSxlQUFlO0lBQ2YsMkVBQTJFO0lBRTNFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQXVCRztJQUNILGFBQWEsQ0FBQyxJQUFZLEVBQUUsVUFBNEIsRUFBRTtRQUN4RCxNQUFNLEVBQ0osTUFBTSxHQUFHLFVBQVUsRUFDbkIsT0FBTyxHQUFHLG9CQUFvQixDQUFDLHFCQUFxQixFQUNwRCxpQkFBaUIsR0FBRyxJQUFJLEVBQ3hCLE1BQU0sR0FBRyxvQ0FBb0MsRUFDOUMsR0FBRyxPQUFPLENBQUM7UUFFWixrQkFBa0I7UUFDbEIsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sRUFBRSxDQUFDO1lBQzFCLE1BQU0sSUFBSSxLQUFLLENBQUMsd0NBQXdDLE9BQU8sUUFBUSxDQUFDLENBQUM7UUFDM0UsQ0FBQztRQUVELHFCQUFxQjtRQUNyQixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRTlDLGFBQWE7UUFDYixJQUFJLE1BQVcsQ0FBQztRQUNoQixJQUFJLENBQUM7WUFDSCxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7Z0JBQ3ZCLE1BQU0sRUFBRSxVQUFVO2dCQUNsQixJQUFJLEVBQUUsS0FBSztnQkFDWCxTQUFTLEVBQUUsQ0FBQyxPQUFZLEVBQUUsRUFBRTtvQkFDMUIsZUFBZSxDQUFDLGdCQUFnQixDQUFDO3dCQUMvQixJQUFJLEVBQUUsc0JBQXNCO3dCQUM1QixRQUFRLEVBQUUsS0FBSzt3QkFDZixNQUFNO3dCQUNOLE9BQU8sRUFBRSxpQkFBaUIsT0FBTyxDQUFDLE9BQU8sRUFBRTtxQkFDNUMsQ0FBQyxDQUFDO2dCQUNMLENBQUM7YUFDRixDQUFDLENBQUM7UUFDTCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sWUFBWSxHQUFHLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUM1RSxNQUFNLENBQUMsSUFBSSxDQUFDLDBCQUEwQixNQUFNLEtBQUssWUFBWSxFQUFFLENBQUMsQ0FBQztZQUNqRSxNQUFNLElBQUksS0FBSyxDQUFDLHlCQUF5QixZQUFZLEVBQUUsQ0FBQyxDQUFDO1FBQzNELENBQUM7UUFFRCx1QkFBdUI7UUFDdkIsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1lBQ3RCLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxJQUFJLE1BQU0sS0FBSyxJQUFJLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO2dCQUMzRSxNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUM7WUFDL0QsQ0FBQztZQUVELDJCQUEyQjtZQUMzQixNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ2pDLEtBQUssTUFBTSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7Z0JBQ3ZCLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztvQkFDaEUsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO2dCQUNyRCxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCxjQUFjO1FBQ2QsZUFBZSxDQUFDLGdCQUFnQixDQUFDO1lBQy9CLElBQUksRUFBRSxvQkFBb0I7WUFDMUIsUUFBUSxFQUFFLEtBQUs7WUFDZixNQUFNO1lBQ04sT0FBTyxFQUFFLDRCQUE0QjtTQUN0QyxDQUFDLENBQUM7UUFFSCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O09BdUJHO0lBQ0gsZ0JBQWdCLENBQ2QsSUFBWSxFQUNaLFVBQW1DLEVBQUU7UUFFckMsTUFBTSxFQUNKLFdBQVcsR0FBRyxvQkFBb0IsQ0FBQyxxQkFBcUIsRUFDeEQsY0FBYyxHQUFHLG9CQUFvQixDQUFDLHdCQUF3QixFQUM5RCxlQUFlLEdBQUcsS0FBSyxFQUN2QixNQUFNLEdBQUcsdUNBQXVDLEVBQ2hELE1BQU0sR0FBRyxVQUFVLEVBQ3BCLEdBQUcsT0FBTyxDQUFDO1FBRVosd0NBQXdDO1FBQ3hDLElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQzlCLDhDQUE4QztZQUM5QyxNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFO2dCQUMxQyxXQUFXO2dCQUNYLGNBQWM7Z0JBQ2QsZUFBZTthQUNoQixDQUFDLENBQUM7WUFFSCxPQUFPO2dCQUNMLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtnQkFDakIsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsbUNBQW1DO2FBQ25FLENBQUM7UUFDSixDQUFDO2FBQU0sQ0FBQztZQUNOLGdFQUFnRTtZQUNoRSxtQ0FBbUM7WUFDbkMsZ0dBQWdHO1lBQ2hHLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFO2dCQUN0QyxNQUFNO2dCQUNOLE9BQU8sRUFBRSxXQUFXO2dCQUNwQixpQkFBaUIsRUFBRSxJQUFJO2dCQUN2QixNQUFNO2FBQ1AsQ0FBQyxDQUFDO1lBRUgsT0FBTztnQkFDTCxJQUFJLEVBQUUsTUFBTTtnQkFDWixPQUFPLEVBQUUsRUFBRSxDQUFDLG9DQUFvQzthQUNqRCxDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0F1Qkc7SUFDSCxTQUFTLENBQ1AsSUFBWSxFQUNaLFVBQTRCLEVBQUU7UUFFOUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV2QyxRQUFRLE1BQU0sRUFBRSxDQUFDO1lBQ2YsS0FBSyxhQUFhLENBQUMsQ0FBQyxDQUFDO2dCQUNuQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO2dCQUN2RSxPQUFPO29CQUNMLE1BQU07b0JBQ04sSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO29CQUNqQixPQUFPLEVBQUUsTUFBTSxDQUFDLE9BQU87aUJBQ3hCLENBQUM7WUFDSixDQUFDO1lBRUQsS0FBSyxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUNaLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDN0QsT0FBTztvQkFDTCxNQUFNO29CQUNOLElBQUksRUFBRSxNQUFNO2lCQUNiLENBQUM7WUFDSixDQUFDO1lBRUQsS0FBSyxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUNaLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDekQsT0FBTztvQkFDTCxNQUFNO29CQUNOLElBQUksRUFBRSxNQUFNO2lCQUNiLENBQUM7WUFDSixDQUFDO1lBRUQ7Z0JBQ0UsTUFBTSxJQUFJLEtBQUssQ0FBQyw0REFBNEQsQ0FBQyxDQUFDO1FBQ2xGLENBQUM7SUFDSCxDQUFDO0lBRUQsMkVBQTJFO0lBQzNFLGVBQWU7SUFDZiwyRUFBMkU7SUFFM0U7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQXFCRztJQUNILFFBQVEsQ0FBQyxJQUFTLEVBQUUsVUFBMkIsRUFBRTtRQUMvQyxNQUFNLEVBQ0osTUFBTSxHQUFHLFVBQVUsRUFDbkIsUUFBUSxHQUFHLElBQUksRUFDZixXQUFXLEdBQUcsSUFBSSxFQUNsQixNQUFNLEdBQUcsSUFBSSxFQUNiLE1BQU0sR0FBRyxDQUFDLEVBQ1YsU0FBUyxHQUFHLEVBQUUsRUFDZCxTQUFTLEdBQUcsQ0FBQyxDQUFDLEVBQ2YsR0FBRyxPQUFPLENBQUM7UUFFWixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRTlDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDckIsTUFBTSxFQUFFLFVBQVU7WUFDbEIsUUFBUTtZQUNSLFdBQVc7WUFDWCxNQUFNO1lBQ04sTUFBTTtZQUNOLFNBQVM7WUFDVCxTQUFTO1NBQ1YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O09Bc0JHO0lBQ0gsaUJBQWlCLENBQ2YsUUFBYSxFQUNiLE9BQWUsRUFDZixVQUFrQyxFQUFFO1FBRXBDLE1BQU0sRUFDSixNQUFNLEdBQUcsUUFBUSxFQUNqQixhQUFhLEdBQUcsS0FBSyxFQUNyQixnQkFBZ0IsR0FBRyxrQkFBa0IsRUFDckMsTUFBTSxHQUFHLFVBQVUsRUFDbkIsUUFBUSxFQUFFLFNBQVMsR0FBRyxJQUFJLEVBQzFCLFdBQVcsR0FBRyxJQUFJLEVBQ2xCLE1BQU0sR0FBRyxJQUFJLEVBQ2QsR0FBRyxPQUFPLENBQUM7UUFFWiw4QkFBOEI7UUFDOUIsSUFBSSxpQkFBaUIsR0FBRyxhQUFhO1lBQ25DLENBQUMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsRUFBRSxFQUFFLFFBQVEsRUFBRSxnQkFBZ0IsRUFBRSxDQUFDO1lBQzlELENBQUMsQ0FBQyxRQUFRLENBQUM7UUFFYix3RUFBd0U7UUFDeEUsaUJBQWlCLEdBQUcsbUJBQW1CLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUUzRCxJQUFJLE1BQU0sS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUN4QixnREFBZ0Q7WUFDaEQsT0FBTyxNQUFNLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1FBQ3RELENBQUM7YUFBTSxDQUFDO1lBQ04sdUNBQXVDO1lBQ3ZDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsaUJBQWlCLEVBQUU7Z0JBQ2xELE1BQU07Z0JBQ04sUUFBUSxFQUFFLEtBQUssRUFBRywyREFBMkQ7Z0JBQzdFLFdBQVc7Z0JBQ1gsTUFBTTthQUNQLENBQUMsQ0FBQztZQUVILGlDQUFpQztZQUNqQyxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLE9BQU8sUUFBUSxVQUFVLFVBQVUsT0FBTyxFQUFFLENBQUM7WUFDL0MsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE9BQU8sUUFBUSxVQUFVLE9BQU8sQ0FBQztZQUNuQyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRCwyRUFBMkU7SUFDM0Usa0JBQWtCO0lBQ2xCLDJFQUEyRTtJQUUzRTs7Ozs7Ozs7Ozs7Ozs7T0FjRztJQUNILFNBQVMsQ0FDUCxJQUFZLEVBQ1osVUFBNEIsRUFBRTtRQUU5QixNQUFNLEVBQ0osT0FBTyxHQUFHLG9CQUFvQixDQUFDLHdCQUF3QixFQUN2RCxpQkFBaUIsR0FBRyxLQUFLLEVBQ3pCLE1BQU0sR0FBRyxnQ0FBZ0MsRUFDMUMsR0FBRyxPQUFPLENBQUM7UUFFWixrQkFBa0I7UUFDbEIsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1FBRWpELElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFaEMsdUJBQXVCO1lBQ3ZCLElBQUksaUJBQWlCLEVBQUUsQ0FBQztnQkFDdEIsSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLElBQUksTUFBTSxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7b0JBQzNFLE1BQU0sSUFBSSxLQUFLLENBQUMsMkNBQTJDLENBQUMsQ0FBQztnQkFDL0QsQ0FBQztZQUNILENBQUM7WUFFRCxPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sWUFBWSxHQUFHLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUM1RSxNQUFNLENBQUMsSUFBSSxDQUFDLDBCQUEwQixNQUFNLEtBQUssWUFBWSxFQUFFLENBQUMsQ0FBQztZQUNqRSxNQUFNLElBQUksS0FBSyxDQUFDLHlCQUF5QixZQUFZLEVBQUUsQ0FBQyxDQUFDO1FBQzNELENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7OztPQWVHO0lBQ0gsYUFBYSxDQUNYLElBQVMsRUFDVCxVQUFnQyxFQUFFO1FBRWxDLE1BQU0sRUFDSixNQUFNLEdBQUcsS0FBSyxFQUNkLE1BQU0sR0FBRyxDQUFDLEVBQ1YsYUFBYSxHQUFHLEtBQUssRUFDckIsZ0JBQWdCLEdBQUcsa0JBQWtCLEVBQ3RDLEdBQUcsT0FBTyxDQUFDO1FBRVosOEJBQThCO1FBQzlCLE1BQU0sYUFBYSxHQUFHLGFBQWE7WUFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLEVBQUUsUUFBUSxFQUFFLGdCQUFnQixFQUFFLENBQUM7WUFDMUQsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUVULHdEQUF3RDtRQUN4RCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3hDLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDO0lBQzFELENBQUM7SUFFRCwyRUFBMkU7SUFDM0UscUJBQXFCO0lBQ3JCLDJFQUEyRTtJQUUzRTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQW1CRztJQUNILGFBQWEsQ0FDWCxRQUFhLEVBQ2IsVUFBZ0MsRUFBRTtRQUVsQyxNQUFNLEVBQUUsUUFBUSxHQUFHLGtCQUFrQixFQUFFLFdBQVcsR0FBRyxLQUFLLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFFdkUsSUFBSSxRQUFRLEtBQUssTUFBTSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDeEMsT0FBTyxRQUFRLENBQUM7UUFDbEIsQ0FBQztRQUVELDBCQUEwQjtRQUMxQixJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxRQUFRLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDdEQsT0FBTyxRQUFRLENBQUM7UUFDbEIsQ0FBQztRQUVELGdCQUFnQjtRQUNoQixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUM1Qiw4Q0FBOEM7WUFDOUMsSUFBSSxPQUFPLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFFdEUsSUFBSSxRQUFRLEtBQUssa0JBQWtCLEVBQUUsQ0FBQztnQkFDcEMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEtBQUssU0FBUyxDQUFDLENBQUM7WUFDdkQsQ0FBQztpQkFBTSxJQUFJLFFBQVEsS0FBSyxhQUFhLEVBQUUsQ0FBQztnQkFDdEMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLENBQUM7WUFDbEQsQ0FBQztpQkFBTSxJQUFJLFFBQVEsS0FBSyxhQUFhLEVBQUUsQ0FBQztnQkFDdEMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEtBQUssU0FBUyxJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsQ0FBQztZQUN4RSxDQUFDO1lBRUQsT0FBTyxPQUFPLENBQUM7UUFDakIsQ0FBQztRQUVELGlDQUFpQztRQUNqQyxNQUFNLE9BQU8sR0FBd0IsRUFBRSxDQUFDO1FBRXhDLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDcEQsSUFBSSx5QkFBeUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDdkMsU0FBUztZQUNYLENBQUM7WUFFRCxNQUFNLFlBQVksR0FDaEIsQ0FBQyxRQUFRLEtBQUssa0JBQWtCLElBQUksS0FBSyxLQUFLLFNBQVMsQ0FBQztnQkFDeEQsQ0FBQyxRQUFRLEtBQUssYUFBYSxJQUFJLEtBQUssS0FBSyxJQUFJLENBQUM7Z0JBQzlDLENBQUMsUUFBUSxLQUFLLGFBQWEsSUFBSSxDQUFDLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxLQUFLLElBQUksQ0FBQyxDQUFDLENBQUM7WUFFMUUsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO2dCQUNsQiwwQ0FBMEM7Z0JBQzFDLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLEVBQUUsQ0FBQztvQkFDaEQsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUM7b0JBRXhELGlEQUFpRDtvQkFDakQsSUFBSSxXQUFXLEVBQUUsQ0FBQzt3QkFDaEIsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUM7NEJBQ3pDLENBQUMsQ0FBQyxZQUFZLENBQUMsTUFBTSxLQUFLLENBQUM7NEJBQzNCLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUM7d0JBRTNDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQzs0QkFDYixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsWUFBWSxDQUFDO3dCQUM5QixDQUFDO29CQUNILENBQUM7eUJBQU0sQ0FBQzt3QkFDTixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsWUFBWSxDQUFDO29CQUM5QixDQUFDO2dCQUNILENBQUM7cUJBQU0sQ0FBQztvQkFDTixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDO2dCQUN2QixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQsMkVBQTJFO0lBQzNFLG1CQUFtQjtJQUNuQiwyRUFBMkU7SUFFM0U7Ozs7Ozs7Ozs7Ozs7Ozs7O09BaUJHO0lBQ0gsWUFBWSxDQUFDLElBQVk7UUFDdkIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBRTVCLHdCQUF3QjtRQUN4QixJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUNqQyxPQUFPLGFBQWEsQ0FBQztRQUN2QixDQUFDO1FBRUQsaUJBQWlCO1FBQ2pCLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDdkQsSUFBSSxDQUFDO2dCQUNILElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQ3BCLE9BQU8sTUFBTSxDQUFDO1lBQ2hCLENBQUM7WUFBQyxNQUFNLENBQUM7Z0JBQ1Asb0NBQW9DO1lBQ3RDLENBQUM7UUFDSCxDQUFDO1FBRUQsc0JBQXNCO1FBQ3RCLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFO2dCQUNoQyxNQUFNLEVBQUUsSUFBSSxDQUFDLGVBQWU7Z0JBQzVCLElBQUksRUFBRSxLQUFLO2FBQ1osQ0FBQyxDQUFDO1lBRUgsc0NBQXNDO1lBQ3RDLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxJQUFJLE1BQU0sS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDbEQsT0FBTyxNQUFNLENBQUM7WUFDaEIsQ0FBQztRQUNILENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxpQkFBaUI7UUFDbkIsQ0FBQztRQUVELE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7T0FjRztJQUNILGNBQWMsQ0FBQyxJQUFZO1FBQ3pCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUM1QixPQUFPLE9BQU8sQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDbkMsQ0FBQztJQUVELDJFQUEyRTtJQUMzRSxxQkFBcUI7SUFDckIsMkVBQTJFO0lBRTNFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O09BbUJHO0lBQ0gsZ0JBQWdCLENBQ2QsSUFBMkIsRUFDM0IsUUFBZ0QsRUFDaEQsTUFBYyxFQUNkLE9BQWU7UUFFZixlQUFlLENBQUMsZ0JBQWdCLENBQUM7WUFDL0IsSUFBSTtZQUNKLFFBQVE7WUFDUixNQUFNO1lBQ04sT0FBTztTQUNSLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7O09BZUc7SUFDSCxZQUFZLENBQUMsSUFBWSxFQUFFLE9BQWUsRUFBRSxPQUFlO1FBQ3pELElBQUksSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLEVBQUUsQ0FBQztZQUMxQixNQUFNLElBQUksS0FBSyxDQUNiLEdBQUcsT0FBTyw0QkFBNEIsT0FBTyxtQkFBbUIsSUFBSSxDQUFDLE1BQU0sU0FBUyxDQUNyRixDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7SUFFRCwyRUFBMkU7SUFDM0Usa0JBQWtCO0lBQ2xCLDJFQUEyRTtJQUUzRTs7Ozs7T0FLRztJQUNLLGFBQWEsQ0FBQyxNQUFrQjtRQUN0QyxRQUFRLE1BQU0sRUFBRSxDQUFDO1lBQ2YsS0FBSyxVQUFVO2dCQUNiLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQztZQUM5QixLQUFLLFNBQVM7Z0JBQ1osT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDO1lBQzdCLEtBQUssTUFBTTtnQkFDVCxPQUFPLElBQUksQ0FBQyxXQUFXLENBQUM7WUFDMUIsS0FBSyxNQUFNO2dCQUNULDBGQUEwRjtnQkFDMUYsK0VBQStFO2dCQUMvRSxPQUFPLElBQUksQ0FBQyxXQUFXLENBQUM7WUFDMUI7Z0JBQ0UsTUFBTSxDQUFDLElBQUksQ0FBQyx3QkFBd0IsTUFBTSwwQkFBMEIsQ0FBQyxDQUFDO2dCQUN0RSxPQUFPLElBQUksQ0FBQyxlQUFlLENBQUM7UUFDaEMsQ0FBQztJQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNlcmlhbGl6YXRpb25TZXJ2aWNlIC0gQ2VudHJhbGl6ZWQgc2VyaWFsaXphdGlvbiBmb3IgYWxsIGVsZW1lbnQgdHlwZXNcbiAqXG4gKiBFbGltaW5hdGVzIH4zMDArIGxpbmVzIG9mIGR1cGxpY2F0ZSBzZXJpYWxpemF0aW9uIGNvZGUgYWNyb3NzIFNraWxsTWFuYWdlcixcbiAqIE1lbW9yeU1hbmFnZXIsIFRlbXBsYXRlTWFuYWdlciwgQWdlbnRNYW5hZ2VyLCBhbmQgRW5zZW1ibGVNYW5hZ2VyLlxuICpcbiAqIEtleSBGZWF0dXJlczpcbiAqIC0gVW5pZmllZCBZQU1MIHBhcnNpbmcgYW5kIGR1bXBpbmcgd2l0aCBzZWN1cml0eSB2YWxpZGF0aW9uXG4gKiAtIENvbnNpc3RlbnQgSlNPTiBvcGVyYXRpb25zIGFjcm9zcyBhbGwgZWxlbWVudCB0eXBlc1xuICogLSBDb21wcmVoZW5zaXZlIG1ldGFkYXRhIGNsZWFuaW5nIHV0aWxpdGllc1xuICogLSBGb3JtYXQgYXV0by1kZXRlY3Rpb25cbiAqIC0gU2VjdXJpdHkgZXZlbnQgbG9nZ2luZyBpbnRlZ3JhdGlvblxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBJbmplY3QgdmlhIERJIGNvbnRhaW5lclxuICogY29uc3RydWN0b3IocHJpdmF0ZSBzZXJpYWxpemF0aW9uU2VydmljZTogU2VyaWFsaXphdGlvblNlcnZpY2UpIHt9XG4gKlxuICogLy8gUGFyc2UgWUFNTCB3aXRoIGZyb250bWF0dGVyXG4gKiBjb25zdCByZXN1bHQgPSB0aGlzLnNlcmlhbGl6YXRpb25TZXJ2aWNlLnBhcnNlRnJvbnRtYXR0ZXIoZGF0YSwge1xuICogICBtYXhZYW1sU2l6ZTogNjQgKiAxMDI0LFxuICogICB2YWxpZGF0ZUNvbnRlbnQ6IHRydWUsXG4gKiAgIHNvdXJjZTogJ1NraWxsTWFuYWdlci5pbXBvcnRFbGVtZW50J1xuICogfSk7XG4gKlxuICogLy8gQ3JlYXRlIGZyb250bWF0dGVyXG4gKiBjb25zdCBtYXJrZG93biA9IHRoaXMuc2VyaWFsaXphdGlvblNlcnZpY2UuY3JlYXRlRnJvbnRtYXR0ZXIobWV0YWRhdGEsIGNvbnRlbnQsIHtcbiAqICAgc2NoZW1hOiAnZmFpbHNhZmUnLFxuICogICBjbGVhbk1ldGFkYXRhOiB0cnVlXG4gKiB9KTtcbiAqIGBgYFxuICovXG5cbmltcG9ydCAqIGFzIHlhbWwgZnJvbSAnanMteWFtbCc7XG5pbXBvcnQgbWF0dGVyIGZyb20gJ2dyYXktbWF0dGVyJztcbmltcG9ydCB7IFNlY3VyZVlhbWxQYXJzZXIgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cmVZYW1sUGFyc2VyLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5TW9uaXRvciwgU2VjdXJpdHlFdmVudCB9IGZyb20gJy4uL3NlY3VyaXR5L3NlY3VyaXR5TW9uaXRvci5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBUWVBFIERFRklOSVRJT05TXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogWUFNTCBzY2hlbWEgc2VsZWN0aW9uXG4gKi9cbmV4cG9ydCB0eXBlIFlhbWxTY2hlbWEgPSAnZmFpbHNhZmUnIHwgJ2RlZmF1bHQnIHwgJ2NvcmUnIHwgJ2pzb24nO1xuXG4vKipcbiAqIE1ldGFkYXRhIGNsZWFuaW5nIHN0cmF0ZWd5XG4gKi9cbmV4cG9ydCB0eXBlIENsZWFuaW5nU3RyYXRlZ3kgPVxuICB8ICdyZW1vdmUtdW5kZWZpbmVkJyAgLy8gUmVtb3ZlIHVuZGVmaW5lZCBvbmx5IChTa2lsbE1hbmFnZXIsIFRlbXBsYXRlTWFuYWdlcilcbiAgfCAncmVtb3ZlLW51bGwnICAgICAgIC8vIFJlbW92ZSBudWxsIG9ubHlcbiAgfCAncmVtb3ZlLWJvdGgnICAgICAgIC8vIFJlbW92ZSBib3RoIHVuZGVmaW5lZCBhbmQgbnVsbCAoQWdlbnRNYW5hZ2VyKVxuICB8ICdub25lJzsgICAgICAgICAgICAgLy8gTm8gY2xlYW5pbmcgKE1lbW9yeU1hbmFnZXIsIEVuc2VtYmxlTWFuYWdlcilcblxuLyoqXG4gKiBGcm9udG1hdHRlciBjcmVhdGlvbiBtZXRob2RcbiAqL1xuZXhwb3J0IHR5cGUgRnJvbnRtYXR0ZXJNZXRob2QgPVxuICB8ICdtYXR0ZXInICAvLyBVc2VzIG1hdHRlci5zdHJpbmdpZnkoKSAoU2tpbGxNYW5hZ2VyKVxuICB8ICdtYW51YWwnOyAvLyBNYW51YWwgWUFNTCBkdW1wICsgY29uY2F0ZW5hdGlvbiAob3RoZXJzKVxuXG4vKipcbiAqIERldGVjdGVkIGZvcm1hdCB0eXBlXG4gKi9cbmV4cG9ydCB0eXBlIERldGVjdGVkRm9ybWF0ID0gJ2Zyb250bWF0dGVyJyB8ICd5YW1sJyB8ICdqc29uJyB8ICd1bmtub3duJztcblxuLyoqXG4gKiBPcHRpb25zIGZvciBwYXJzaW5nIHB1cmUgWUFNTFxuICovXG5leHBvcnQgaW50ZXJmYWNlIFlhbWxQYXJzZU9wdGlvbnMge1xuICAvKiogWUFNTCBzY2hlbWEgdG8gdXNlIChkZWZhdWx0OiAnZmFpbHNhZmUnIGZvciBzZWN1cml0eSkgKi9cbiAgc2NoZW1hPzogWWFtbFNjaGVtYTtcblxuICAvKiogTWF4aW11bSBZQU1MIHNpemUgaW4gYnl0ZXMgKGRlZmF1bHQ6IDY0S0IpICovXG4gIG1heFNpemU/OiBudW1iZXI7XG5cbiAgLyoqIFZhbGlkYXRlIHJvb3Qgc3RydWN0dXJlIGlzIGFuIG9iamVjdCAoZGVmYXVsdDogdHJ1ZSkgKi9cbiAgdmFsaWRhdGVTdHJ1Y3R1cmU/OiBib29sZWFuO1xuXG4gIC8qKiBTb3VyY2UgaWRlbnRpZmllciBmb3Igc2VjdXJpdHkgbG9nZ2luZyAqL1xuICBzb3VyY2U/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgcGFyc2luZyBmcm9udG1hdHRlciArIGNvbnRlbnRcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBGcm9udG1hdHRlclBhcnNlT3B0aW9ucyB7XG4gIC8qKiBNYXhpbXVtIFlBTUwgZnJvbnRtYXR0ZXIgc2l6ZSBpbiBieXRlcyAoZGVmYXVsdDogNjRLQikgKi9cbiAgbWF4WWFtbFNpemU/OiBudW1iZXI7XG5cbiAgLyoqIE1heGltdW0gY29udGVudCBzaXplIGluIGJ5dGVzIChkZWZhdWx0OiAxTUIpICovXG4gIG1heENvbnRlbnRTaXplPzogbnVtYmVyO1xuXG4gIC8qKiBFbmFibGUgY29udGVudCB2YWxpZGF0aW9uIChkZWZhdWx0OiBmYWxzZSBmb3IgbG9jYWwgZmlsZXMpICovXG4gIHZhbGlkYXRlQ29udGVudD86IGJvb2xlYW47XG5cbiAgLyoqIFNvdXJjZSBpZGVudGlmaWVyIGZvciBzZWN1cml0eSBsb2dnaW5nICovXG4gIHNvdXJjZT86IHN0cmluZztcblxuICAvKipcbiAgICogWUFNTCBzY2hlbWEgdG8gdXNlIGZvciBwdXJlIFlBTUwgcGFyc2luZyAoZGVmYXVsdDogJ2ZhaWxzYWZlJylcbiAgICogVXNlICdqc29uJyB0byBwcmVzZXJ2ZSBib29sZWFucyBhbmQgbnVtYmVycyB3aGVuIHJlYWRpbmcgZmlsZXNcbiAgICogd3JpdHRlbiB3aXRoICdqc29uJyBzY2hlbWEgKGUuZy4sIE1lbW9yeU1hbmFnZXIpXG4gICAqL1xuICBzY2hlbWE/OiBZYW1sU2NoZW1hO1xufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIGF1dG8tcGFyc2luZyAoZm9ybWF0IGRldGVjdGlvbilcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBBdXRvUGFyc2VPcHRpb25zIHtcbiAgLyoqIE9wdGlvbnMgdG8gdXNlIGlmIGZvcm1hdCBpcyBmcm9udG1hdHRlciAqL1xuICBmcm9udG1hdHRlck9wdGlvbnM/OiBGcm9udG1hdHRlclBhcnNlT3B0aW9ucztcblxuICAvKiogT3B0aW9ucyB0byB1c2UgaWYgZm9ybWF0IGlzIHB1cmUgWUFNTCAqL1xuICB5YW1sT3B0aW9ucz86IFlhbWxQYXJzZU9wdGlvbnM7XG5cbiAgLyoqIE9wdGlvbnMgdG8gdXNlIGlmIGZvcm1hdCBpcyBKU09OICovXG4gIGpzb25PcHRpb25zPzogSnNvblBhcnNlT3B0aW9ucztcblxuICAvKiogU291cmNlIGlkZW50aWZpZXIgZm9yIHNlY3VyaXR5IGxvZ2dpbmcgKi9cbiAgc291cmNlPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIFlBTUwgZHVtcFxuICovXG5leHBvcnQgaW50ZXJmYWNlIFlhbWxEdW1wT3B0aW9ucyB7XG4gIC8qKiBZQU1MIHNjaGVtYSB0byB1c2UgKGRlZmF1bHQ6ICdmYWlsc2FmZScpICovXG4gIHNjaGVtYT86IFlhbWxTY2hlbWE7XG5cbiAgLyoqIFNvcnQga2V5cyBhbHBoYWJldGljYWxseSAoZGVmYXVsdDogdHJ1ZSkgKi9cbiAgc29ydEtleXM/OiBib29sZWFuO1xuXG4gIC8qKiBTa2lwIGludmFsaWQgdmFsdWVzIGluc3RlYWQgb2YgdGhyb3dpbmcgKGRlZmF1bHQ6IHRydWUpICovXG4gIHNraXBJbnZhbGlkPzogYm9vbGVhbjtcblxuICAvKiogRG9uJ3QgY3JlYXRlIGFuY2hvcnMvYWxpYXNlcyAoZGVmYXVsdDogdHJ1ZSBmb3Igc2VjdXJpdHkpICovXG4gIG5vUmVmcz86IGJvb2xlYW47XG5cbiAgLyoqIEluZGVudGF0aW9uIGZvciBuZXN0ZWQgZWxlbWVudHMgKGRlZmF1bHQ6IDIpICovXG4gIGluZGVudD86IG51bWJlcjtcblxuICAvKiogTGluZSB3aWR0aCBmb3Igd3JhcHBpbmcgKGRlZmF1bHQ6IDgwKSAqL1xuICBsaW5lV2lkdGg/OiBudW1iZXI7XG5cbiAgLyoqIEZsb3cgbGV2ZWwgKC0xIGZvciBibG9jayBzdHlsZSwgMCsgZm9yIGZsb3cgc3R5bGUgYXQgdGhhdCBsZXZlbCkgKi9cbiAgZmxvd0xldmVsPzogbnVtYmVyO1xufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIGZyb250bWF0dGVyIGNyZWF0aW9uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRnJvbnRtYXR0ZXJEdW1wT3B0aW9ucyBleHRlbmRzIFlhbWxEdW1wT3B0aW9ucyB7XG4gIC8qKiBNZXRob2QgdG8gdXNlIGZvciBjcmVhdGluZyBmcm9udG1hdHRlciAoZGVmYXVsdDogJ21hbnVhbCcpICovXG4gIG1ldGhvZD86IEZyb250bWF0dGVyTWV0aG9kO1xuXG4gIC8qKiBDbGVhbiBtZXRhZGF0YSBiZWZvcmUgZHVtcGluZyAoZGVmYXVsdDogZmFsc2UpICovXG4gIGNsZWFuTWV0YWRhdGE/OiBib29sZWFuO1xuXG4gIC8qKiBDbGVhbmluZyBzdHJhdGVneSBpZiBjbGVhbk1ldGFkYXRhIGlzIHRydWUgKi9cbiAgY2xlYW5pbmdTdHJhdGVneT86IENsZWFuaW5nU3RyYXRlZ3k7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgSlNPTiBwYXJzaW5nXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSnNvblBhcnNlT3B0aW9ucyB7XG4gIC8qKiBNYXhpbXVtIEpTT04gc2l6ZSBpbiBieXRlcyAoZGVmYXVsdDogMU1CKSAqL1xuICBtYXhTaXplPzogbnVtYmVyO1xuXG4gIC8qKiBWYWxpZGF0ZSB0aGF0IHJvb3QgaXMgYW4gb2JqZWN0IChkZWZhdWx0OiBmYWxzZSkgKi9cbiAgdmFsaWRhdGVTdHJ1Y3R1cmU/OiBib29sZWFuO1xuXG4gIC8qKiBTb3VyY2UgaWRlbnRpZmllciBmb3Igc2VjdXJpdHkgbG9nZ2luZyAqL1xuICBzb3VyY2U/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgSlNPTiBzdHJpbmdpZmljYXRpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBKc29uU3RyaW5naWZ5T3B0aW9ucyB7XG4gIC8qKiBQcmV0dHkgcHJpbnQgb3V0cHV0IChkZWZhdWx0OiBmYWxzZSkgKi9cbiAgcHJldHR5PzogYm9vbGVhbjtcblxuICAvKiogSW5kZW50YXRpb24gc3BhY2VzIChkZWZhdWx0OiAyKSAqL1xuICBpbmRlbnQ/OiBudW1iZXI7XG5cbiAgLyoqIENsZWFuIG1ldGFkYXRhIGJlZm9yZSBzdHJpbmdpZnlpbmcgKGRlZmF1bHQ6IGZhbHNlKSAqL1xuICBjbGVhbk1ldGFkYXRhPzogYm9vbGVhbjtcblxuICAvKiogQ2xlYW5pbmcgc3RyYXRlZ3kgaWYgY2xlYW5NZXRhZGF0YSBpcyB0cnVlICovXG4gIGNsZWFuaW5nU3RyYXRlZ3k/OiBDbGVhbmluZ1N0cmF0ZWd5O1xufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIG1ldGFkYXRhIGNsZWFuaW5nXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTWV0YWRhdGFDbGVhbk9wdGlvbnMge1xuICAvKiogQ2xlYW5pbmcgc3RyYXRlZ3kgKGRlZmF1bHQ6ICdyZW1vdmUtdW5kZWZpbmVkJykgKi9cbiAgc3RyYXRlZ3k/OiBDbGVhbmluZ1N0cmF0ZWd5O1xuXG4gIC8qKiBSZW1vdmUgZW1wdHkgYXJyYXlzIGFuZCBvYmplY3RzIChkZWZhdWx0OiBmYWxzZSkgKi9cbiAgcmVtb3ZlRW1wdHk/OiBib29sZWFuO1xufVxuXG4vKipcbiAqIFJlc3VsdCBmcm9tIGZyb250bWF0dGVyIHBhcnNpbmdcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQYXJzZWRGcm9udG1hdHRlciB7XG4gIC8qKiBQYXJzZWQgZnJvbnRtYXR0ZXIgbWV0YWRhdGEgKi9cbiAgZGF0YTogUmVjb3JkPHN0cmluZywgYW55PjtcblxuICAvKiogTWFya2Rvd24gY29udGVudCBhZnRlciBmcm9udG1hdHRlciAqL1xuICBjb250ZW50OiBzdHJpbmc7XG59XG5cbi8qKlxuICogUmVzdWx0IGZyb20gYXV0by1wYXJzaW5nXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXV0b1BhcnNlZFJlc3VsdCB7XG4gIC8qKiBEZXRlY3RlZCBmb3JtYXQgKi9cbiAgZm9ybWF0OiBEZXRlY3RlZEZvcm1hdDtcblxuICAvKiogUGFyc2VkIGRhdGEgKG1ldGFkYXRhIGZvciBmcm9udG1hdHRlciwgb2JqZWN0IGZvciBZQU1ML0pTT04pICovXG4gIGRhdGE6IGFueTtcblxuICAvKiogQ29udGVudCAob25seSBmb3IgZnJvbnRtYXR0ZXIgZm9ybWF0KSAqL1xuICBjb250ZW50Pzogc3RyaW5nO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBTRVJWSUNFIElNUExFTUVOVEFUSU9OXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogU2VydmljZSBmb3IgY2VudHJhbGl6ZWQgc2VyaWFsaXphdGlvbiBvcGVyYXRpb25zXG4gKlxuICogVGhpcyBzZXJ2aWNlIHByb3ZpZGVzIHVuaWZpZWQgc2VyaWFsaXphdGlvbiBmdW5jdGlvbmFsaXR5IHRvIGVsaW1pbmF0ZSBjb2RlXG4gKiBkdXBsaWNhdGlvbiBhY3Jvc3MgZWxlbWVudCBtYW5hZ2Vycy4gU3VwcG9ydHM6XG4gKiAtIFlBTUwgcGFyc2luZyBhbmQgZHVtcGluZ1xuICogLSBKU09OIHBhcnNpbmcgYW5kIHN0cmluZ2lmaWNhdGlvblxuICogLSBGcm9udG1hdHRlciBjcmVhdGlvbiBhbmQgcGFyc2luZ1xuICogLSBNZXRhZGF0YSBjbGVhbmluZ1xuICogLSBGb3JtYXQgZGV0ZWN0aW9uXG4gKiAtIFNlY3VyaXR5IHZhbGlkYXRpb25cbiAqL1xuLyoqXG4gKiBGaXggIzkxMDogQ2Fub25pY2FsIG1ldGFkYXRhIGZpZWxkIG9yZGVyIGZvciBkZXRlcm1pbmlzdGljIFlBTUwgb3V0cHV0LlxuICogRmllbGRzIGFyZSBvcmRlcmVkOiBpZGVudGl0eSDihpIgZm9ybWF0IOKGkiBjb250ZW50IOKGkiB0eXBlLXNwZWNpZmljIOKGkiBpbmZyYXN0cnVjdHVyZS5cbiAqIEZpZWxkcyBub3QgbGlzdGVkIGFwcGVhciBhZnRlciBhbGwgbGlzdGVkIGZpZWxkcywgc29ydGVkIGFscGhhYmV0aWNhbGx5LlxuICovXG5leHBvcnQgY29uc3QgTUVUQURBVEFfRklFTERfT1JERVI6IHJlYWRvbmx5IHN0cmluZ1tdID0gW1xuICAvLyBJZGVudGl0eVxuICAnbmFtZScsICd0eXBlJywgJ2Zvcm1hdF92ZXJzaW9uJywgJ3ZlcnNpb24nLCAnZGVzY3JpcHRpb24nLFxuICAvLyBBdHRyaWJ1dGlvblxuICAnYXV0a