UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

683 lines 78.7 kB
/** * 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