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.

552 lines 75.4 kB
/** * Base abstract class implementing IElement interface. * Provides common functionality that all element types can extend. */ import { ElementStatus } from '../types/elements/index.js'; import { v4 as uuidv4 } from 'uuid'; import * as yaml from 'js-yaml'; import { logger } from '../utils/logger.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; import { SecurityMonitor } from '../security/securityMonitor.js'; import { SecureYamlParser } from '../security/secureYamlParser.js'; /** * Normalizes version strings to full semver format (X.Y.Z) * This helps maintain consistency while accepting flexible input formats * * @param version - The version string to normalize * @returns Normalized version string in X.Y.Z format with leading zeros removed * * @example * normalizeVersion("1") // "1.0.0" * normalizeVersion("1.2") // "1.2.0" * normalizeVersion("1.2.3") // "1.2.3" * normalizeVersion("1.0-beta") // "1.0.0-beta" * normalizeVersion("01.02.03") // "1.2.3" (strips leading zeros) */ export function normalizeVersion(version) { // Extract base version and any prerelease/build metadata const match = version.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?([-+].+)?$/); if (!match) { // Return as-is if not a valid version format return version; } const [, major, minor = '0', patch = '0', suffix = ''] = match; // Strip leading zeros but preserve "0" as valid const normalizedMajor = Number.parseInt(major, 10).toString(); const normalizedMinor = Number.parseInt(minor, 10).toString(); const normalizedPatch = Number.parseInt(patch, 10).toString(); return `${normalizedMajor}.${normalizedMinor}.${normalizedPatch}${suffix}`; } export class BaseElement { static TRANSIENT_METADATA_FIELDS = new Set([ 'gatekeeperDiagnostics', ]); // Identity id; type; version; // Metadata metadata; // Dual-field semantic architecture (v2.0) // All element types inherit these; subclasses may override with richer behavior. // - instructions: behavioral directives (command voice, imperatives to follow) // - content: reference material (informational data to draw from) // Declared as accessor pairs so subclasses (e.g., Memory) can override with custom getters. _instructions = ''; _content = ''; get instructions() { return this._instructions; } set instructions(value) { this._instructions = value; } get content() { return this._content; } set content(value) { this._content = value; } // Features references; extensions; ratings; // Internal state _status = ElementStatus.INACTIVE; _isDirty = false; // Constants MAX_FEEDBACK_HISTORY = 100; constructor(type, metadata = {}, metadataService // Required injection for DI ) { // Normalize common metadata via service (skip type-specific defaults) const normalized = metadataService.normalizeMetadata(metadata, type, { skipTypeDefaults: true // Let element-specific classes handle their own defaults }); this.type = type; this.id = metadata.name ? this.generateId(normalized.name) : uuidv4(); this.version = normalized.version || '1.0.0'; // Guaranteed by normalizeMetadata, but add fallback for type safety // Spread normalized common metadata const baseMetadata = { ...normalized, type: this.type // Ensure type field is set correctly }; // Preserve element-specific fields that MetadataService doesn't handle // These are passed through unchanged via the spread operator in MetadataService // Preserve triggers (Persona/Memory-specific) if ('triggers' in metadata && Array.isArray(metadata.triggers)) { baseMetadata.triggers = metadata.triggers; } // FIX #1430: Preserve Memory-specific metadata fields (autoLoad, priority) // These are defined in MemoryMetadata but need to be preserved in baseMetadata if ('autoLoad' in metadata) { baseMetadata.autoLoad = metadata.autoLoad; } if ('priority' in metadata) { baseMetadata.priority = metadata.priority; } this.metadata = baseMetadata; // Initialize optional features this.references = []; this.extensions = {}; this.ratings = { aiRating: 0, userRating: undefined, ratingCount: 0, lastEvaluated: new Date(), confidence: 0, trend: 'stable', feedbackHistory: [] }; } /** * Generate a unique ID for the element based on its name and type. * Format: type_name-slug_timestamp */ generateId(name) { const typeSlug = this.type.toLowerCase(); const nameSlug = name .toLowerCase() .replaceAll(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric chars with hyphens .replaceAll(/(^-)|(-$)/g, ''); // Trim leading/trailing hyphens const timestamp = Date.now(); return `${typeSlug}_${nameSlug}_${timestamp}`; } /** * Core validation that all elements share. * Subclasses should override and call super.validate() first. */ validate() { const errors = []; const warnings = []; const suggestions = []; // Validate required fields if (!this.id) { errors.push({ field: 'id', message: 'Element ID is required' }); } if (!this.metadata.name || this.metadata.name.trim() === '') { errors.push({ field: 'metadata.name', message: 'Element name is required' }); } if (!this.metadata.description || this.metadata.description.trim() === '') { warnings.push({ field: 'metadata.description', message: 'Element description is recommended', severity: 'medium' }); } // Validate version format - more flexible to support LLM-generated content // FIX for Issue #935: Allow flexible version formats like "1.0", "1.1", "2.0.0" // Previously: Strict semver regex requiring X.Y.Z format caused skills activation failures // Now: Accept common version patterns that LLMs and humans naturally use // Note: Leading zeros are allowed (e.g., "01.02.03") but will be normalized to "1.2.3" // Security: No injection risk as version is just metadata, not executed const flexibleVersionRegex = /^\d+(\.\d+)?(\.\d+)?(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/; if (!flexibleVersionRegex.test(this.version)) { errors.push({ field: 'version', message: 'Version must start with numbers in format: MAJOR[.MINOR][.PATCH][-PRERELEASE][+BUILD]. Valid examples: "1", "1.0", "1.0.0", "2.1", "1.0.0-beta", "1.0.0-alpha.1", "1.0.0+build123". The major version is required, minor and patch are optional. Note: Leading zeros (e.g., "01.02") are accepted but will be normalized to "1.2".', code: 'INVALID_VERSION_FORMAT' }); } // Validate references if (this.references) { this.references.forEach((ref, index) => { if (!ref.uri || ref.uri.trim() === '') { errors.push({ field: `references[${index}].uri`, message: 'Reference URI is required' }); } if (!ref.title || ref.title.trim() === '') { warnings.push({ field: `references[${index}].title`, message: 'Reference title is recommended', severity: 'low' }); } }); } // Validate ratings if present if (this.ratings) { if (this.ratings.aiRating < 0 || this.ratings.aiRating > 5) { errors.push({ field: 'ratings.aiRating', message: 'AI rating must be between 0 and 5' }); } if (this.ratings.userRating !== undefined && (this.ratings.userRating < 0 || this.ratings.userRating > 5)) { errors.push({ field: 'ratings.userRating', message: 'User rating must be between 0 and 5' }); } } // Add suggestions if (!this.metadata.tags || this.metadata.tags.length === 0) { suggestions.push('Consider adding tags to improve discoverability'); } if (!this.metadata.author) { suggestions.push('Consider adding author information'); } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, warnings: warnings.length > 0 ? warnings : undefined, suggestions: suggestions.length > 0 ? suggestions : undefined }; } /** * Serialize to JSON format for internal use and testing. * Maintains backward compatibility with existing tests. */ serializeToJSON() { const data = { id: this.id, type: this.type, version: this.version, metadata: this.metadata, references: this.references, extensions: this.extensions, ratings: this.ratings }; return JSON.stringify(data, null, 2); } /** * Default serialization to markdown with YAML frontmatter. * Uses js-yaml for secure YAML generation to prevent injection attacks. * FIX: Changed from JSON to proper markdown format for GitHub portfolio storage. * This ensures elements are readable on GitHub and compatible with collection workflow. */ serialize() { // Build YAML frontmatter starting with all metadata fields // This ensures subclasses can add their own fields const frontmatter = { ...this.metadata, // Include all metadata fields type: this.type, version: this.version }; // Note: metadata already includes name, description, author, created, modified // and any additional fields added by subclasses if (this.references && this.references.length > 0) { frontmatter.references = this.references.map(ref => ({ type: ref.type, uri: ref.uri, title: ref.title })); } if (this.ratings && this.ratings.aiRating > 0) { frontmatter.ratings = { aiRating: this.ratings.aiRating, userRating: this.ratings.userRating, ratingCount: this.ratings.ratingCount }; } // Remove undefined/null values recursively const cleanFrontmatter = this.deepCleanObject(frontmatter); // Use js-yaml for secure YAML generation // This prevents YAML injection attacks and handles special characters properly let yamlFrontmatter; try { yamlFrontmatter = yaml.dump(cleanFrontmatter, { noRefs: true, // Don't use YAML references sortKeys: false, // Keep our order lineWidth: -1, // Don't wrap lines quotingType: '"', // Use double quotes when needed forceQuotes: false, // Only quote when necessary skipInvalid: false // Don't skip invalid values }); } catch (error) { // If YAML generation fails, log and throw a more informative error logger.error('Failed to generate YAML frontmatter', { error, frontmatter: cleanFrontmatter }); throw new Error(`Failed to serialize element metadata to YAML: ${error instanceof Error ? error.message : 'Unknown error'}`); } // Validate the generated YAML can be parsed back using SecureYamlParser // HIGH SEVERITY FIX: Use SecureYamlParser instead of yaml.load to prevent code execution try { SecureYamlParser.parse(yamlFrontmatter, { maxYamlSize: 64 * 1024, // 64KB limit for frontmatter validateContent: true }); } catch (error) { logger.error('Generated invalid YAML', { error, yaml: yamlFrontmatter }); throw new Error(`Generated YAML is invalid: ${error instanceof Error ? error.message : 'Unknown error'}`); } // Get content - subclasses should override this to provide actual content const content = this.getContent ? this.getContent() : `# ${this.metadata.name}\n\n${this.metadata.description || ''}`; // Trim the YAML to remove trailing newline that yaml.dump adds return `---\n${yamlFrontmatter.trim()}\n---\n\n${content}`; } /** * Recursively remove undefined and null values from an object. * This ensures YAML serialization doesn't fail on undefined values. */ deepCleanObject(obj) { if (obj === null || obj === undefined) { return undefined; } if (Array.isArray(obj)) { return obj .map(item => this.deepCleanObject(item)) .filter(item => item !== undefined); } if (typeof obj === 'object') { const cleaned = {}; for (const [key, value] of Object.entries(obj)) { if (BaseElement.TRANSIENT_METADATA_FIELDS.has(key)) { continue; } const cleanedValue = this.deepCleanObject(value); if (cleanedValue !== undefined) { cleaned[key] = cleanedValue; } } return cleaned; } return obj; } /** * Default deserialization from JSON. * Subclasses can override for custom formats. */ deserialize(data) { try { // Normalize Unicode input before parsing const validationResult = UnicodeValidator.normalize(data); const parsed = JSON.parse(validationResult.normalizedContent); // Validate required fields if (!parsed.id || !parsed.type || !parsed.metadata) { throw new Error('Invalid element data: missing required fields'); } // Update properties this.id = parsed.id; this.type = parsed.type; this.version = normalizeVersion(String(parsed.version ?? '1.0.0')); this.metadata = parsed.metadata; this.references = parsed.references || []; this.extensions = parsed.extensions || {}; this.ratings = parsed.ratings; this._isDirty = false; } catch (error) { // Enhanced error context preservation const errorMessage = error instanceof Error ? error.message : String(error); const errorStack = error instanceof Error ? error.stack : undefined; logger.error('Failed to deserialize element', { error: errorMessage, stack: errorStack, dataPreview: data.substring(0, 200), // First 200 chars for context elementType: this.type }); // Create new error with original as cause const deserializeError = new Error(`BaseElement deserialization failed: ${errorMessage}`); if (error instanceof Error) { deserializeError.cause = error; } throw deserializeError; } } /** * Process user feedback and update ratings. */ receiveFeedback(feedback, context) { // Normalize Unicode input to prevent security issues const validationResult = UnicodeValidator.normalize(feedback); const normalizedFeedback = validationResult.normalizedContent; // Log security event for feedback processing SecurityMonitor.logSecurityEvent({ type: 'CONTENT_INJECTION_ATTEMPT', severity: 'LOW', source: 'BaseElement.receiveFeedback', details: `Feedback processed for element ${this.type}:${this.id}`, additionalData: { elementType: this.type, elementId: this.id, feedbackLength: feedback.length, hasUnicodeIssues: !validationResult.isValid } }); if (!this.ratings) { this.ratings = { aiRating: 0, userRating: undefined, ratingCount: 0, lastEvaluated: new Date(), confidence: 0, trend: 'stable', feedbackHistory: [] }; } // Create feedback entry with normalized content const userFeedback = { timestamp: new Date(), feedback: normalizedFeedback, sentiment: this.analyzeSentiment(normalizedFeedback), inferredRating: this.inferRating(normalizedFeedback), context, elementVersion: this.version }; // Add to history with bounds checking if (!this.ratings.feedbackHistory) { this.ratings.feedbackHistory = []; } this.ratings.feedbackHistory.push(userFeedback); // Prevent unbounded growth if (this.ratings.feedbackHistory.length > this.MAX_FEEDBACK_HISTORY) { this.ratings.feedbackHistory = this.ratings.feedbackHistory.slice(-this.MAX_FEEDBACK_HISTORY); logger.debug(`Feedback history trimmed to ${this.MAX_FEEDBACK_HISTORY} entries for element ${this.id}`); } // Update user rating if we inferred one if (userFeedback.inferredRating !== undefined) { this.updateUserRating(userFeedback.inferredRating); } this._isDirty = true; } /** * Simple sentiment analysis. * Subclasses can override for more sophisticated analysis. */ analyzeSentiment(feedback) { const lower = feedback.toLowerCase(); const positiveWords = ['excellent', 'great', 'good', 'helpful', 'useful', 'perfect', 'amazing', 'love']; const negativeWords = ['bad', 'poor', 'terrible', 'useless', 'broken', 'hate', 'awful', 'disappointing']; const positiveCount = positiveWords.filter(word => lower.includes(word)).length; const negativeCount = negativeWords.filter(word => lower.includes(word)).length; if (positiveCount > negativeCount) return 'positive'; if (negativeCount > positiveCount) return 'negative'; return 'neutral'; } /** * Simple rating inference from feedback. * Subclasses can override for more sophisticated inference. */ inferRating(feedback) { const sentiment = this.analyzeSentiment(feedback); const lower = feedback.toLowerCase(); // Look for explicit ratings const ratingMatch = lower.match(/(\d+)\s*(stars?|\/5|out of 5)/); if (ratingMatch) { const rating = Number.parseInt(ratingMatch[1]); if (rating >= 1 && rating <= 5) return rating; } // Infer from sentiment if (sentiment === 'positive') { if (lower.includes('perfect') || lower.includes('excellent')) return 5; if (lower.includes('great') || lower.includes('very good')) return 4; return 4; } else if (sentiment === 'negative') { if (lower.includes('terrible') || lower.includes('awful')) return 1; if (lower.includes('poor') || lower.includes('bad')) return 2; return 2; } return 3; // Neutral } /** * Update user rating with a new value. */ updateUserRating(newRating) { if (!this.ratings) return; if (this.ratings.userRating === undefined) { this.ratings.userRating = newRating; this.ratings.ratingCount = 1; } else { // Calculate running average const totalRating = this.ratings.userRating * this.ratings.ratingCount + newRating; this.ratings.ratingCount++; this.ratings.userRating = totalRating / this.ratings.ratingCount; } // Update delta and trend this.ratings.ratingDelta = this.ratings.userRating - this.ratings.aiRating; // Simple trend calculation based on recent feedback const recentFeedback = this.ratings.feedbackHistory?.slice(-5) || []; const recentSentiments = recentFeedback.map(f => f.sentiment); const positiveCount = recentSentiments.filter(s => s === 'positive').length; const negativeCount = recentSentiments.filter(s => s === 'negative').length; if (positiveCount > negativeCount + 1) { this.ratings.trend = 'improving'; } else if (negativeCount > positiveCount + 1) { this.ratings.trend = 'declining'; } else { this.ratings.trend = 'stable'; } } /** * Get current element status. */ getStatus() { return this._status; } /** * Status property getter for convenient access. */ get status() { return this._status; } /** * Default lifecycle methods - subclasses should override as needed. */ async beforeActivate() { logger.debug(`Preparing to activate ${this.type} element: ${this.metadata.name}`); this._status = ElementStatus.ACTIVATING; } async activate() { if (this._status !== ElementStatus.ACTIVE) { logger.info(`Activating ${this.type} element: ${this.metadata.name}`); } // Re-activations of the same element are silent — no value in logging this._status = ElementStatus.ACTIVE; } async afterActivate() { logger.debug(`Completed activation of ${this.type} element: ${this.metadata.name}`); } async deactivate() { logger.info(`Deactivating ${this.type} element: ${this.metadata.name}`); this._status = ElementStatus.DEACTIVATING; // Subclasses should implement cleanup logic this._status = ElementStatus.INACTIVE; } /** * Mark element as modified. */ markDirty() { this._isDirty = true; this.metadata.modified = new Date().toISOString(); } /** * Check if element has unsaved changes. */ isDirty() { return this._isDirty; } /** * Mark element as saved. */ markClean() { this._isDirty = false; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmFzZUVsZW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZWxlbWVudHMvQmFzZUVsZW1lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUdMLGFBQWEsRUFRZCxNQUFNLDRCQUE0QixDQUFDO0FBRXBDLE9BQU8sRUFBRSxFQUFFLElBQUksTUFBTSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxJQUFJLE1BQU0sU0FBUyxDQUFDO0FBQ2hDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUM5RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDakUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFHbkU7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxPQUFlO0lBQzlDLHlEQUF5RDtJQUN6RCxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLDBDQUEwQyxDQUFDLENBQUM7SUFFeEUsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsNkNBQTZDO1FBQzdDLE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxNQUFNLENBQUMsRUFBRSxLQUFLLEVBQUUsS0FBSyxHQUFHLEdBQUcsRUFBRSxLQUFLLEdBQUcsR0FBRyxFQUFFLE1BQU0sR0FBRyxFQUFFLENBQUMsR0FBRyxLQUFLLENBQUM7SUFFL0QsZ0RBQWdEO0lBQ2hELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzlELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzlELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBRTlELE9BQU8sR0FBRyxlQUFlLElBQUksZUFBZSxJQUFJLGVBQWUsR0FBRyxNQUFNLEVBQUUsQ0FBQztBQUM3RSxDQUFDO0FBRUQsTUFBTSxPQUFnQixXQUFXO0lBQ3ZCLE1BQU0sQ0FBVSx5QkFBeUIsR0FBRyxJQUFJLEdBQUcsQ0FBQztRQUMxRCx1QkFBdUI7S0FDeEIsQ0FBQyxDQUFDO0lBQ0gsV0FBVztJQUNKLEVBQUUsQ0FBUztJQUNYLElBQUksQ0FBYztJQUNsQixPQUFPLENBQVM7SUFFdkIsV0FBVztJQUNKLFFBQVEsQ0FBbUI7SUFFbEMsMENBQTBDO0lBQzFDLGlGQUFpRjtJQUNqRiwrRUFBK0U7SUFDL0Usa0VBQWtFO0lBQ2xFLDRGQUE0RjtJQUNsRixhQUFhLEdBQVcsRUFBRSxDQUFDO0lBQzNCLFFBQVEsR0FBVyxFQUFFLENBQUM7SUFFaEMsSUFBVyxZQUFZLEtBQWEsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztJQUNoRSxJQUFXLFlBQVksQ0FBQyxLQUFhLElBQUksSUFBSSxDQUFDLGFBQWEsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBRXRFLElBQVcsT0FBTyxLQUFhLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7SUFDdEQsSUFBVyxPQUFPLENBQUMsS0FBYSxJQUFJLElBQUksQ0FBQyxRQUFRLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUU1RCxXQUFXO0lBQ0osVUFBVSxDQUFlO0lBQ3pCLFVBQVUsQ0FBdUI7SUFDakMsT0FBTyxDQUFrQjtJQUVoQyxpQkFBaUI7SUFDUCxPQUFPLEdBQWtCLGFBQWEsQ0FBQyxRQUFRLENBQUM7SUFDaEQsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUVwQyxZQUFZO0lBQ0ssb0JBQW9CLEdBQUcsR0FBRyxDQUFDO0lBRTVDLFlBQ0UsSUFBaUIsRUFDakIsV0FBc0MsRUFBRSxFQUN4QyxlQUFnQyxDQUFFLDRCQUE0Qjs7UUFFOUQsc0VBQXNFO1FBQ3RFLE1BQU0sVUFBVSxHQUFHLGVBQWUsQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFO1lBQ25FLGdCQUFnQixFQUFFLElBQUksQ0FBRSx5REFBeUQ7U0FDbEYsQ0FBQyxDQUFDO1FBRUgsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLEVBQUUsR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDdEUsSUFBSSxDQUFDLE9BQU8sR0FBRyxVQUFVLENBQUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFFLG9FQUFvRTtRQUVuSCxvQ0FBb0M7UUFDcEMsTUFBTSxZQUFZLEdBQVE7WUFDeEIsR0FBRyxVQUFVO1lBQ2IsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUUscUNBQXFDO1NBQ3ZELENBQUM7UUFFRix1RUFBdUU7UUFDdkUsZ0ZBQWdGO1FBRWhGLDhDQUE4QztRQUM5QyxJQUFJLFVBQVUsSUFBSSxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBRSxRQUFnQixDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDeEUsWUFBWSxDQUFDLFFBQVEsR0FBSSxRQUFnQixDQUFDLFFBQVEsQ0FBQztRQUNyRCxDQUFDO1FBRUQsMkVBQTJFO1FBQzNFLCtFQUErRTtRQUMvRSxJQUFJLFVBQVUsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUMzQixZQUFZLENBQUMsUUFBUSxHQUFJLFFBQWdCLENBQUMsUUFBUSxDQUFDO1FBQ3JELENBQUM7UUFDRCxJQUFJLFVBQVUsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUMzQixZQUFZLENBQUMsUUFBUSxHQUFJLFFBQWdCLENBQUMsUUFBUSxDQUFDO1FBQ3JELENBQUM7UUFFRCxJQUFJLENBQUMsUUFBUSxHQUFHLFlBQVksQ0FBQztRQUU3QiwrQkFBK0I7UUFDL0IsSUFBSSxDQUFDLFVBQVUsR0FBRyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFVBQVUsR0FBRyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNiLFFBQVEsRUFBRSxDQUFDO1lBQ1gsVUFBVSxFQUFFLFNBQVM7WUFDckIsV0FBVyxFQUFFLENBQUM7WUFDZCxhQUFhLEVBQUUsSUFBSSxJQUFJLEVBQUU7WUFDekIsVUFBVSxFQUFFLENBQUM7WUFDYixLQUFLLEVBQUUsUUFBUTtZQUNmLGVBQWUsRUFBRSxFQUFFO1NBQ3BCLENBQUM7SUFDSixDQUFDO0lBRUQ7OztPQUdHO0lBQ08sVUFBVSxDQUFDLElBQVk7UUFDL0IsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN6QyxNQUFNLFFBQVEsR0FBRyxJQUFJO2FBQ2xCLFdBQVcsRUFBRTthQUNiLFVBQVUsQ0FBQyxhQUFhLEVBQUUsR0FBRyxDQUFDLENBQUUsOENBQThDO2FBQzlFLFVBQVUsQ0FBQyxZQUFZLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBRyxnQ0FBZ0M7UUFDbkUsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRTdCLE9BQU8sR0FBRyxRQUFRLElBQUksUUFBUSxJQUFJLFNBQVMsRUFBRSxDQUFDO0lBQ2hELENBQUM7SUFFRDs7O09BR0c7SUFDSSxRQUFRO1FBQ2IsTUFBTSxNQUFNLEdBQXNCLEVBQUUsQ0FBQztRQUNyQyxNQUFNLFFBQVEsR0FBd0IsRUFBRSxDQUFDO1FBQ3pDLE1BQU0sV0FBVyxHQUFhLEVBQUUsQ0FBQztRQUVqQywyQkFBMkI7UUFDM0IsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNiLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxDQUFDLENBQUM7UUFDbEUsQ0FBQztRQUVELElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQztZQUM1RCxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLGVBQWUsRUFBRSxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsQ0FBQyxDQUFDO1FBQy9FLENBQUM7UUFFRCxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7WUFDMUUsUUFBUSxDQUFDLElBQUksQ0FBQztnQkFDWixLQUFLLEVBQUUsc0JBQXNCO2dCQUM3QixPQUFPLEVBQUUsb0NBQW9DO2dCQUM3QyxRQUFRLEVBQUUsUUFBUTthQUNuQixDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsMkVBQTJFO1FBQzNFLGdGQUFnRjtRQUNoRiwyRkFBMkY7UUFDM0YseUVBQXlFO1FBQ3pFLHVGQUF1RjtRQUN2Rix3RUFBd0U7UUFDeEUsTUFBTSxvQkFBb0IsR0FBRyw0REFBNEQsQ0FBQztRQUMxRixJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQzdDLE1BQU0sQ0FBQyxJQUFJLENBQUM7Z0JBQ1YsS0FBSyxFQUFFLFNBQVM7Z0JBQ2hCLE9BQU8sRUFBRSxvVUFBb1U7Z0JBQzdVLElBQUksRUFBRSx3QkFBd0I7YUFDL0IsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELHNCQUFzQjtRQUN0QixJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNwQixJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsRUFBRTtnQkFDckMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQztvQkFDdEMsTUFBTSxDQUFDLElBQUksQ0FBQzt3QkFDVixLQUFLLEVBQUUsY0FBYyxLQUFLLE9BQU87d0JBQ2pDLE9BQU8sRUFBRSwyQkFBMkI7cUJBQ3JDLENBQUMsQ0FBQztnQkFDTCxDQUFDO2dCQUNELElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7b0JBQzFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7d0JBQ1osS0FBSyxFQUFFLGNBQWMsS0FBSyxTQUFTO3dCQUNuQyxPQUFPLEVBQUUsZ0NBQWdDO3dCQUN6QyxRQUFRLEVBQUUsS0FBSztxQkFDaEIsQ0FBQyxDQUFDO2dCQUNMLENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCw4QkFBOEI7UUFDOUIsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDakIsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQzNELE1BQU0sQ0FBQyxJQUFJLENBQUM7b0JBQ1YsS0FBSyxFQUFFLGtCQUFrQjtvQkFDekIsT0FBTyxFQUFFLG1DQUFtQztpQkFDN0MsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztZQUNELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEtBQUssU0FBUztnQkFDckMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDakUsTUFBTSxDQUFDLElBQUksQ0FBQztvQkFDVixLQUFLLEVBQUUsb0JBQW9CO29CQUMzQixPQUFPLEVBQUUscUNBQXFDO2lCQUMvQyxDQUFDLENBQUM7WUFDTCxDQUFDO1FBQ0gsQ0FBQztRQUVELGtCQUFrQjtRQUNsQixJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzNELFdBQVcsQ0FBQyxJQUFJLENBQUMsaURBQWlELENBQUMsQ0FBQztRQUN0RSxDQUFDO1FBRUQsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDMUIsV0FBVyxDQUFDLElBQUksQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDO1FBQ3pELENBQUM7UUFFRCxPQUFPO1lBQ0wsS0FBSyxFQUFFLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQztZQUMxQixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsU0FBUztZQUM5QyxRQUFRLEVBQUUsUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUztZQUNwRCxXQUFXLEVBQUUsV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsU0FBUztTQUM5RCxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7T0FHRztJQUNJLGVBQWU7UUFDcEIsTUFBTSxJQUFJLEdBQUc7WUFDWCxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUU7WUFDWCxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUk7WUFDZixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87WUFDckIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1lBQ3ZCLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBVTtZQUMzQixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVU7WUFDM0IsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPO1NBQ3RCLENBQUM7UUFFRixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxTQUFTO1FBQ2QsMkRBQTJEO1FBQzNELG1EQUFtRDtRQUNuRCxNQUFNLFdBQVcsR0FBd0I7WUFDdkMsR0FBRyxJQUFJLENBQUMsUUFBUSxFQUFHLDhCQUE4QjtZQUNqRCxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUk7WUFDZixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87U0FDdEIsQ0FBQztRQUVGLCtFQUErRTtRQUMvRSxnREFBZ0Q7UUFDaEQsSUFBSSxJQUFJLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ2xELFdBQVcsQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUNuRCxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7Z0JBQ2QsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHO2dCQUNaLEtBQUssRUFBRSxHQUFHLENBQUMsS0FBSzthQUNqQixDQUFDLENBQUMsQ0FBQztRQUNOLENBQUM7UUFDRCxJQUFJLElBQUksQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDOUMsV0FBVyxDQUFDLE9BQU8sR0FBRztnQkFDcEIsUUFBUSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUTtnQkFDL0IsVUFBVSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVTtnQkFDbkMsV0FBVyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVzthQUN0QyxDQUFDO1FBQ0osQ0FBQztRQUVELDJDQUEyQztRQUMzQyxNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsV0FBVyxDQUFDLENBQUM7UUFFM0QseUNBQXlDO1FBQ3pDLCtFQUErRTtRQUMvRSxJQUFJLGVBQXVCLENBQUM7UUFDNUIsSUFBSSxDQUFDO1lBQ0gsZUFBZSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7Z0JBQzVDLE1BQU0sRUFBRSxJQUFJLEVBQVcsNEJBQTRCO2dCQUNuRCxRQUFRLEVBQUUsS0FBSyxFQUFRLGlCQUFpQjtnQkFDeEMsU0FBUyxFQUFFLENBQUMsQ0FBQyxFQUFVLG1CQUFtQjtnQkFDMUMsV0FBVyxFQUFFLEdBQUcsRUFBTyxnQ0FBZ0M7Z0JBQ3ZELFdBQVcsRUFBRSxLQUFLLEVBQUssNEJBQTRCO2dCQUNuRCxXQUFXLEVBQUUsS0FBSyxDQUFLLDRCQUE0QjthQUNwRCxDQUFDLENBQUM7UUFDTCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLG1FQUFtRTtZQUNuRSxNQUFNLENBQUMsS0FBSyxDQUFDLHFDQUFxQyxFQUFFLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxDQUFDLENBQUM7WUFDOUYsTUFBTSxJQUFJLEtBQUssQ0FBQyxpREFBaUQsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsZUFBZSxFQUFFLENBQUMsQ0FBQztRQUMvSCxDQUFDO1FBRUQsd0VBQXdFO1FBQ3hFLHlGQUF5RjtRQUN6RixJQUFJLENBQUM7WUFDSCxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsZUFBZSxFQUFFO2dCQUN0QyxXQUFXLEVBQUUsRUFBRSxHQUFHLElBQUksRUFBRSw2QkFBNkI7Z0JBQ3JELGVBQWUsRUFBRSxJQUFJO2FBQ3RCLENBQUMsQ0FBQztRQUNMLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQyx3QkFBd0IsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQztZQUN6RSxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxDQUFDO1FBQzVHLENBQUM7UUFFRCwwRUFBMEU7UUFDMUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxJQUFJLEVBQUUsRUFBRSxDQUFDO1FBRXRILCtEQUErRDtRQUMvRCxPQUFPLFFBQVEsZUFBZSxDQUFDLElBQUksRUFBRSxZQUFZLE9BQU8sRUFBRSxDQUFDO0lBQzdELENBQUM7SUFRRDs7O09BR0c7SUFDSyxlQUFlLENBQUMsR0FBUTtRQUM5QixJQUFJLEdBQUcsS0FBSyxJQUFJLElBQUksR0FBRyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ3RDLE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7UUFFRCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN2QixPQUFPLEdBQUc7aUJBQ1AsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQztpQkFDdkMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxLQUFLLFNBQVMsQ0FBQyxDQUFDO1FBQ3hDLENBQUM7UUFFRCxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzVCLE1BQU0sT0FBTyxHQUFRLEVBQUUsQ0FBQztZQUN4QixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUMvQyxJQUFJLFdBQVcsQ0FBQyx5QkFBeUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDbkQsU0FBUztnQkFDWCxDQUFDO2dCQUVELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ2pELElBQUksWUFBWSxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUMvQixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsWUFBWSxDQUFDO2dCQUM5QixDQUFDO1lBQ0gsQ0FBQztZQUNELE9BQU8sT0FBTyxDQUFDO1FBQ2pCLENBQUM7UUFFRCxPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUM7SUFFRDs7O09BR0c7SUFDSSxXQUFXLENBQUMsSUFBWTtRQUM3QixJQUFJLENBQUM7WUFDSCx5Q0FBeUM7WUFDekMsTUFBTSxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUQsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1lBRTlELDJCQUEyQjtZQUMzQixJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQ25ELE1BQU0sSUFBSSxLQUFLLENBQUMsK0NBQStDLENBQUMsQ0FBQztZQUNuRSxDQUFDO1lBRUQsb0JBQW9CO1lBQ3BCLElBQUksQ0FBQyxFQUFFLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNwQixJQUFJLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUM7WUFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ25FLElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQztZQUNoQyxJQUFJLENBQUMsVUFBVSxHQUFHLE1BQU0sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDO1lBQzFDLElBQUksQ0FBQyxVQUFVLEdBQUcsTUFBTSxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUM7WUFDMUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDO1lBRTlCLElBQUksQ0FBQyxRQUFRLEdBQUcsS0FBSyxDQUFDO1FBQ3hCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2Ysc0NBQXNDO1lBQ3RDLE1BQU0sWUFBWSxHQUFHLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUM1RSxNQUFNLFVBQVUsR0FBRyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7WUFFcEUsTUFBTSxDQUFDLEtBQUssQ0FBQywrQkFBK0IsRUFBRTtnQkFDNUMsS0FBSyxFQUFFLFlBQVk7Z0JBQ25CLEtBQUssRUFBRSxVQUFVO2dCQUNqQixXQUFXLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLEVBQUUsOEJBQThCO2dCQUNuRSxXQUFXLEVBQUUsSUFBSSxDQUFDLElBQUk7YUFDdkIsQ0FBQyxDQUFDO1lBRUgsMENBQTBDO1lBQzFDLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxLQUFLLENBQUMsdUNBQXVDLFlBQVksRUFBRSxDQUFDLENBQUM7WUFDMUYsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7Z0JBQzNCLGdCQUFnQixDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7WUFDakMsQ0FBQztZQUNELE1BQU0sZ0JBQWdCLENBQUM7UUFDekIsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLGVBQWUsQ0FBQyxRQUFnQixFQUFFLE9BQXlCO1FBQ2hFLHFEQUFxRDtRQUNyRCxNQUFNLGdCQUFnQixHQUFHLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM5RCxNQUFNLGtCQUFrQixHQUFHLGdCQUFnQixDQUFDLGlCQUFpQixDQUFDO1FBRTlELDZDQUE2QztRQUM3QyxlQUFlLENBQUMsZ0JBQWdCLENBQUM7WUFDL0IsSUFBSSxFQUFFLDJCQUEyQjtZQUNqQyxRQUFRLEVBQUUsS0FBSztZQUNmLE1BQU0sRUFBRSw2QkFBNkI7WUFDckMsT0FBTyxFQUFFLGtDQUFrQyxJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxFQUFFLEVBQUU7WUFDakUsY0FBYyxFQUFFO2dCQUNkLFdBQVcsRUFBRSxJQUFJLENBQUMsSUFBSTtnQkFDdEIsU0FBUyxFQUFFLElBQUksQ0FBQyxFQUFFO2dCQUNsQixjQUFjLEVBQUUsUUFBUSxDQUFDLE1BQU07Z0JBQy9CLGdCQUFnQixFQUFFLENBQUMsZ0JBQWdCLENBQUMsT0FBTzthQUM1QztTQUNGLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDbEIsSUFBSSxDQUFDLE9BQU8sR0FBRztnQkFDYixRQUFRLEVBQUUsQ0FBQztnQkFDWCxVQUFVLEVBQUUsU0FBUztnQkFDckIsV0FBVyxFQUFFLENBQUM7Z0JBQ2QsYUFBYSxFQUFFLElBQUksSUFBSSxFQUFFO2dCQUN6QixVQUFVLEVBQUUsQ0FBQztnQkFDYixLQUFLLEVBQUUsUUFBUTtnQkFDZixlQUFlLEVBQUUsRUFBRTthQUNwQixDQUFDO1FBQ0osQ0FBQztRQUVELGdEQUFnRDtRQUNoRCxNQUFNLFlBQVksR0FBaUI7WUFDakMsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFO1lBQ3JCLFFBQVEsRUFBRSxrQkFBa0I7WUFDNUIsU0FBUyxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxrQkFBa0IsQ0FBQztZQUNwRCxjQUFjLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxrQkFBa0IsQ0FBQztZQUNwRCxPQUFPO1lBQ1AsY0FBYyxFQUFFLElBQUksQ0FBQyxPQUFPO1NBQzdCLENBQUM7UUFFRixzQ0FBc0M7UUFDdEMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDbEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLEdBQUcsRUFBRSxDQUFDO1FBQ3BDLENBQUM7UUFDRCxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7UUFFaEQsMkJBQTJCO1FBQzNCLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1lBQ3BFLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1lBQzlGLE1BQU0sQ0FBQyxLQUFLLENBQUMsK0JBQStCLElBQUksQ0FBQyxvQkFBb0Isd0JBQXdCLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQzFHLENBQUM7UUFFRCx3Q0FBd0M7UUFDeEMsSUFBSSxZQUFZLENBQUMsY0FBYyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzlDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDckQsQ0FBQztRQUVELElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7O09BR0c7SUFDTyxnQkFBZ0IsQ0FBQyxRQUFnQjtRQUN6QyxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFckMsTUFBTSxhQUFhLEdBQUcsQ0FBQyxXQUFXLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDeEcsTUFBTSxhQUFhLEdBQUcsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFFekcsTUFBTSxhQUFhLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDaEYsTUFBTSxhQUFhLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFFaEYsSUFBSSxhQUFhLEdBQUcsYUFBYTtZQUFFLE9BQU8sVUFBVSxDQUFDO1FBQ3JELElBQUksYUFBYSxHQUFHLGFBQWE7WUFBRSxPQUFPLFVBQVUsQ0FBQztRQUNyRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQ7OztPQUdHO0lBQ08sV0FBVyxDQUFDLFFBQWdCO1FBQ3BDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNsRCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFckMsNEJBQTRCO1FBQzVCLE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsK0JBQStCLENBQUMsQ0FBQztRQUNqRSxJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ2hCLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDL0MsSUFBSSxNQUFNLElBQUksQ0FBQyxJQUFJLE1BQU0sSUFBSSxDQUFDO2dCQUFFLE9BQU8sTUFBTSxDQUFDO1FBQ2hELENBQUM7UUFFRCx1QkFBdUI7UUFDdkIsSUFBSSxTQUFTLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDN0IsSUFBSSxLQUFLLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDO2dCQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ3ZFLElBQUksS0FBSyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxLQUFLLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQztnQkFBRSxPQUFPLENBQUMsQ0FBQztZQUNyRSxPQUFPLENBQUMsQ0FBQztRQUNYLENBQUM7YUFBTSxJQUFJLFNBQVMsS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUNwQyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksS0FBSyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7Z0JBQUUsT0FBTyxDQUFDLENBQUM7WUFDcEUsSUFBSSxLQUFLLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDO2dCQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQzlELE9BQU8sQ0FBQyxDQUFDO1FBQ1gsQ0FBQztRQUVELE9BQU8sQ0FBQyxDQUFDLENBQUMsVUFBVTtJQUN0QixDQUFDO0lBRUQ7O09BRUc7SUFDTyxnQkFBZ0IsQ0FBQyxTQUFpQjtRQUMxQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPO1FBRTFCLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDMUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEdBQUcsU0FBUyxDQUFDO1lBQ3BDLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztRQUMvQixDQUFDO2FBQU0sQ0FBQztZQUNOLDRCQUE0QjtZQUM1QixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUM7WUFDbkYsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUMzQixJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsR0FBRyxXQUFXLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUM7UUFDbkUsQ0FBQztRQUVELHlCQUF5QjtRQUN6QixJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUUzRSxvREFBb0Q7UUFDcEQsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3JFLE1BQU0sZ0JBQWdCLEdBQUcsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM5RCxNQUFNLGFBQWEsR0FBRyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssVUFBVSxDQUFDLENBQUMsTUFBTSxDQUFDO1FBQzVFLE1BQU0sYUFBYSxHQUFHLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxVQUFVLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFFNUUsSUFBSSxhQUFhLEdBQUcsYUFBYSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxHQUFHLFdBQVcsQ0FBQztRQUNuQyxDQUFDO2FBQU0sSUFBSSxhQUFhLEdBQUcsYUFBYSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQzdDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxHQUFHLFdBQVcsQ0FBQztRQUNuQyxDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxHQUFHLFFBQVEsQ0FBQztRQUNoQyxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksU0FBUztRQUNkLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQztJQUN0QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLE1BQU07UUFDZixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDdEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLGNBQWM7UUFDekIsTUFBTSxDQUFDLEtBQUssQ0FBQyx5QkFBeUIsSUFBSSxDQUFDLElBQUksYUFBYSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDbEYsSUFBSSxDQUFDLE9BQU8sR0FBRyxhQUFhLENBQUMsVUFBVSxDQUFDO0lBQzFDLENBQUM7SUFFTSxLQUFLLENBQUMsUUFBUTtRQUNuQixJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssYUFBYSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQzFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsY0FBYyxJQUFJLENBQUMsSUFBSSxhQUFhLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUN4RSxDQUFDO1FBQ0Qsc0VBQXNFO1FBQ3RFLElBQUksQ0FBQyxPQUFPLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FBQztJQUN0QyxDQUFDO0lBRU0sS0FBSyxDQUFDLGFBQWE7UUFDeEIsTUFBTSxDQUFDLEtBQUssQ0FBQywyQkFBMkIsSUFBSSxDQUFDLElBQUksYUFBYSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdEYsQ0FBQztJQUVNLEtBQUssQ0FBQyxVQUFVO1FBQ3JCLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLElBQUksQ0FBQyxJQUFJLGFBQWEsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3hFLElBQUksQ0FBQyxPQUFPLEdBQUcsYUFBYSxDQUFDLFlBQVksQ0FBQztRQUMxQyw0Q0FBNEM7UUFDNUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxhQUFhLENBQUMsUUFBUSxDQUFDO0lBQ3hDLENBQUM7SUFFRDs7T0FFRztJQUNPLFNBQVM7UUFDakIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNwRCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPO1FBQ1osT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNJLFNBQVM7UUFDZCxJQUFJLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUN4QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBCYXNlIGFic3RyYWN0IGNsYXNzIGltcGxlbWVudGluZyBJRWxlbWVudCBpbnRlcmZhY2UuXG4gKiBQcm92aWRlcyBjb21tb24gZnVuY3Rpb25hbGl0eSB0aGF0IGFsbCBlbGVtZW50IHR5cGVzIGNhbiBleHRlbmQuXG4gKi9cblxuaW1wb3J0IHtcbiAgSUVsZW1lbnQsXG4gIElFbGVtZW50TWV0YWRhdGEsXG4gIEVsZW1lbnRTdGF0dXMsXG4gIEVsZW1lbnRSYXRpbmdzLFxuICBSZWZlcmVuY2UsXG4gIEVsZW1lbnRWYWxpZGF0aW9uUmVzdWx0LFxuICBWYWxpZGF0aW9uRXJyb3IsXG4gIFZhbGlkYXRpb25XYXJuaW5nLFxuICBGZWVkYmFja0NvbnRleHQsXG4gIFVzZXJGZWVkYmFja1xufSBmcm9tICcuLi90eXBlcy9lbGVtZW50cy9pbmRleC5qcyc7XG5pbXBvcnQgeyBFbGVtZW50VHlwZSB9IGZyb20gJy4uL3BvcnRmb2xpby90eXBlcy5qcyc7XG5pbXBvcnQgeyB2NCBhcyB1dWlkdjQgfSBmcm9tICd1dWlkJztcbmltcG9ydCAqIGFzIHlhbWwgZnJvbSAnanMteWFtbCc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgU2VjdXJlWWFtbFBhcnNlciB9IGZyb20gJy4uL3NlY3VyaXR5L3NlY3VyZVlhbWxQYXJzZXIuanMnO1xuaW1wb3J0IHsgTWV0YWRhdGFTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvTWV0YWRhdGFTZXJ2aWNlLmpzJztcblxuLyoqXG4gKiBOb3JtYWxpemVzIHZlcnNpb24gc3RyaW5ncyB0byBmdWxsIHNlbXZlciBmb3JtYXQgKFguWS5aKVxuICogVGhpcyBoZWxwcyBtYWludGFpbiBjb25zaXN0ZW5jeSB3aGlsZSBhY2NlcHRpbmcgZmxleGlibGUgaW5wdXQgZm9ybWF0c1xuICogXG4gKiBAcGFyYW0gdmVyc2lvbiAtIFRoZSB2ZXJzaW9uIHN0cmluZyB0byBub3JtYWxpemVcbiAqIEByZXR1cm5zIE5vcm1hbGl6ZWQgdmVyc2lvbiBzdHJpbmcgaW4gWC5ZLlogZm9ybWF0IHdpdGggbGVhZGluZyB6ZXJvcyByZW1vdmVkXG4gKiBcbiAqIEBleGFtcGxlXG4gKiBub3JtYWxpemVWZXJzaW9uKFwiMVwiKSAgICAgICAgLy8gXCIxLjAuMFwiXG4gKiBub3JtYWxpemVWZXJzaW9uKFwiMS4yXCIpICAgICAgLy8gXCIxLjIuMFwiXG4gKiBub3JtYWxpemVWZXJzaW9uKFwiMS4yLjNcIikgICAgLy8gXCIxLjIuM1wiXG4gKiBub3JtYWxpemVWZXJzaW9uKFwiMS4wLWJldGFcIikgLy8gXCIxLjAuMC1iZXRhXCJcbiAqIG5vcm1hbGl6ZVZlcnNpb24oXCIwMS4wMi4wM1wiKSAvLyBcIjEuMi4zXCIgKHN0cmlwcyBsZWFkaW5nIHplcm9zKVxuICovXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplVmVyc2lvbih2ZXJzaW9uOiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBFeHRyYWN0IGJhc2UgdmVyc2lvbiBhbmQgYW55IHByZXJlbGVhc2UvYnVpbGQgbWV0YWRhdGFcbiAgY29uc3QgbWF0Y2ggPSB2ZXJzaW9uLm1hdGNoKC9eKFxcZCspKD86XFwuKFxcZCspKT8oPzpcXC4oXFxkKykpPyhbLStdLispPyQvKTtcbiAgXG4gIGlmICghbWF0Y2gpIHtcbiAgICAvLyBSZXR1cm4gYXMtaXMgaWYgbm90IGEgdmFsaWQgdmVyc2lvbiBmb3JtYXRcbiAgICByZXR1cm4gdmVyc2lvbjtcbiAgfVxuICBcbiAgY29uc3QgWywgbWFqb3IsIG1pbm9yID0gJzAnLCBwYXRjaCA9ICcwJywgc3VmZml4ID0gJyddID0gbWF0Y2g7XG4gIFxuICAvLyBTdHJpcCBsZWFkaW5nIHplcm9zIGJ1dCBwcmVzZXJ2ZSBcIjBcIiBhcyB2YWxpZFxuICBjb25zdCBub3JtYWxpemVkTWFqb3IgPSBOdW1iZXIucGFyc2VJbnQobWFqb3IsIDEwKS50b1N0cmluZygpO1xuICBjb25zdCBub3JtYWxpemVkTWlub3IgPSBOdW1iZXIucGFyc2VJbnQobWlub3IsIDEwKS50b1N0cmluZygpO1xuICBjb25zdCBub3JtYWxpemVkUGF0Y2ggPSBOdW1iZXIucGFyc2VJbnQocGF0Y2gsIDEwKS50b1N0cmluZygpO1xuICBcbiAgcmV0dXJuIGAke25vcm1hbGl6ZWRNYWpvcn0uJHtub3JtYWxpemVkTWlub3J9LiR7bm9ybWFsaXplZFBhdGNofSR7c3VmZml4fWA7XG59XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBCYXNlRWxlbWVudCBpbXBsZW1lbnRzIElFbGVtZW50IHtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgVFJBTlNJRU5UX01FVEFEQVRBX0ZJRUxEUyA9IG5ldyBTZXQoW1xuICAgICdnYXRla2VlcGVyRGlhZ25vc3RpY3MnLFxuICBdKTtcbiAgLy8gSWRlbnRpdHlcbiAgcHVibGljIGlkOiBzdHJpbmc7XG4gIHB1YmxpYyB0eXBlOiBFbGVtZW50VHlwZTtcbiAgcHVibGljIHZlcnNpb246IHN0cmluZztcbiAgXG4gIC8vIE1ldGFkYXRhXG4gIHB1YmxpYyBtZXRhZGF0YTogSUVsZW1lbnRNZXRhZGF0YTtcbiAgXG4gIC8vIER1YWwtZmllbGQgc2VtYW50aWMgYXJjaGl0ZWN0dXJlICh2Mi4wKVxuICAvLyBBbGwgZWxlbWVudCB0eXBlcyBpbmhlcml0IHRoZXNlOyBzdWJjbGFzc2VzIG1heSBvdmVycmlkZSB3aXRoIHJpY2hlciBiZWhhdmlvci5cbiAgLy8gLSBpbnN0cnVjdGlvbnM6IGJlaGF2aW9yYWwgZGlyZWN0aXZlcyAoY29tbWFuZCB2b2ljZSwgaW1wZXJhdGl2ZXMgdG8gZm9sbG93KVxuICAvLyAtIGNvbnRlbnQ6IHJlZmVyZW5jZSBtYXRlcmlhbCAoaW5mb3JtYXRpb25hbCBkYXRhIHRvIGRyYXcgZnJvbSlcbiAgLy8gRGVjbGFyZWQgYXMgYWNjZXNzb3IgcGFpcnMgc28gc3ViY2xhc3NlcyAoZS5nLiwgTWVtb3J5KSBjYW4gb3ZlcnJpZGUgd2l0aCBjdXN0b20gZ2V0dGVycy5cbiAgcHJvdGVjdGVkIF9pbnN0cnVjdGlvbnM6IHN0cmluZyA9ICcnO1xuICBwcm90ZWN0ZWQgX2NvbnRlbnQ6IHN0cmluZyA9ICcnO1xuXG4gIHB1YmxpYyBnZXQgaW5zdHJ1Y3Rpb25zKCk6IHN0cmluZyB7IHJldHVybiB0aGlzLl9pbnN0cnVjdGlvbnM7IH1cbiAgcHVibGljIHNldCBpbnN0cnVjdGlvbnModmFsdWU6IHN0cmluZykgeyB0aGlzLl9pbnN0cnVjdGlvbnMgPSB2YWx1ZTsgfVxuXG4gIHB1YmxpYyBnZXQgY29udGVudCgpOiBzdHJpbmcgeyByZXR1cm4gdGhpcy5fY29udGVudDsgfVxuICBwdWJsaWMgc2V0IGNvbnRlbnQodmFsdWU6IHN0cmluZykgeyB0aGlzLl9jb250ZW50ID0gdmFsdWU7IH1cblxuICAvLyBGZWF0dXJlc1xuICBwdWJsaWMgcmVmZXJlbmNlcz86IFJlZmVyZW5jZVtdO1xuICBwdWJsaWMgZXh0ZW5zaW9ucz86IFJlY29yZDxzdHJpbmcsIGFueT47XG4gIHB1YmxpYyByYXRpbmdzPzogRWxlbWVudFJhdGluZ3M7XG4gIFxuICAvLyBJbnRlcm5hbCBzdGF0ZVxuICBwcm90ZWN0ZWQgX3N0YXR1czogRWxlbWVudFN0YXR1cyA9IEVsZW1lbnRTdGF0dXMuSU5BQ1RJVkU7XG4gIHByb3RlY3RlZCBfaXNEaXJ0eTogYm9vbGVhbiA9IGZhbHNlO1xuICBcbiAgLy8gQ29uc3RhbnRzXG4gIHByaXZhdGUgcmVhZG9ubHkgTUFYX0ZFRURCQUNLX0hJU1RPUlkgPSAxMDA7XG4gIFxuICBjb25zdHJ1Y3RvcihcbiAgICB0eXBlOiBFbGVtZW50VHlwZSxcbiAgICBtZXRhZGF0YTogUGFydGlhbDxJRWxlbWVudE1ldGFkYXRhPiA9IHt9LFxuICAgIG1ldGFkYXRhU2VydmljZTogTWV0YWRhdGFTZXJ2aWNlICAvLyBSZXF1aXJlZCBpbmplY3Rpb24gZm9yIERJXG4gICkge1xuICAgIC8vIE5vcm1hbGl6ZSBjb21tb24gbWV0YWRhdGEgdmlhIHNlcnZpY2UgKHNraXAgdHlwZS1zcGVjaWZpYyBkZWZhdWx0cylcbiAgICBjb25zdCBub3JtYWxpemVkID0gbWV0YWRhdGFTZXJ2aWNlLm5vcm1hbGl6ZU1ldGFkYXRhKG1ldGFkYXRhLCB0eXBlLCB7XG4gICAgICBza2lwVHlwZURlZmF1bHRzOiB0cnVlICAvLyBMZXQgZWxlbWVudC1zcGVjaWZpYyBjbGFzc2VzIGhhbmRsZSB0aGVpciBvd24gZGVmYXVsdHNcbiAgICB9KTtcblxuICAgIHRoaXMudHlwZSA9IHR5cGU7XG4gICAgdGhpcy5pZCA9IG1ldGFkY