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.

386 lines 45.1 kB
/** * MetadataService - Centralized metadata normalization and defaults * * Eliminates ~300 LOC of duplicate metadata handling across 6 element managers. * * Key Features: * - Consistent metadata structure across all element types * - Automatic default value assignment * - Version normalization (1.0 → 1.0.0) * - Unique ID generation * - User attribution with fallback chain * - Date formatting (ISO 8601) * * Architecture: * - Pure service (no state except currentUser) * - Type-safe with TypeScript strict mode * - Security-first (validates all inputs) * - Preserves exact current behavior from managers * * @example * ```typescript * const metadata = metadataService.normalizeMetadata( * { name: 'My Skill', description: 'Does things' }, * ElementType.SKILL * ); * // Returns: { name: 'My Skill', description: 'Does things', * // version: '1.0.0', author: 'user', created: '2025-11-19', ... } * ``` */ import { userInfo } from 'node:os'; import { ElementType } from '../portfolio/types.js'; import { toSingularLabel } from '../utils/elementTypeNormalization.js'; import { generateAnonymousId, generateUniqueId } from '../utils/filesystem.js'; import { normalizeVersion } from '../elements/BaseElement.js'; import { logger } from '../utils/logger.js'; /** * MetadataService - Centralized metadata normalization and defaults * * ZERO FUNCTIONALITY LOSS: * This service extracts exact behavior from all 6 managers without any changes. * Every default value, every normalization rule, every edge case is preserved. * * USAGE: * ```typescript * const metadata = metadataService.normalizeMetadata( * rawMetadata, * ElementType.SKILL, * { * typeDefaults: { * category: 'general', * difficulty: 'intermediate' * } * } * ); * ``` */ export class MetadataService { currentUser = null; /** * Normalize metadata with type-specific defaults * * Applies: * - Default values for missing fields * - Version normalization (1.0 → 1.0.0) * - Unique ID generation * - User attribution * - Date formatting (ISO 8601) * * PRESERVES EXACT BEHAVIOR: * - PersonaManager: lines 164-205 (normalizePersonaForSave) * - AgentManager: lines 113, 642-644 (getCurrentUserForAttribution) * - MemoryManager: lines 1342-1421 (parseMemoryFile metadata defaults) * - SkillManager: lines 44-102 (create method defaults) * - TemplateManager: lines 298-375 (validateMetadata defaults) * - EnsembleManager: lines 86-347 (parseMetadata defaults) * * @param raw - Raw metadata from user input or file * @param elementType - Type of element being created * @param options - Normalization options * @returns Fully normalized metadata * * @example * const metadata = metadataService.normalizeMetadata( * { name: 'My Skill', description: 'Does things' }, * ElementType.SKILL * ); * // Returns: { name: 'My Skill', description: 'Does things', * // version: '1.0.0', author: 'user', created: '2025-11-19', ... } */ normalizeMetadata(raw, elementType, options = {}) { // Set default options const opts = { preserveExisting: options.preserveExisting ?? false, generateId: options.generateId ?? true, normalizeVersion: options.normalizeVersion ?? true, setAuthor: options.setAuthor ?? true, setCreated: options.setCreated ?? true, setModified: options.setModified ?? true, typeDefaults: options.typeDefaults ?? {}, skipTypeDefaults: options.skipTypeDefaults ?? false }; // Start with raw metadata const normalized = { ...raw }; // Apply type-specific defaults FIRST (can be overridden by standard defaults) // Skip if skipTypeDefaults is true (for BaseElement constructor) if (opts.typeDefaults && Object.keys(opts.typeDefaults).length > 0 && !opts.skipTypeDefaults) { for (const [key, value] of Object.entries(opts.typeDefaults)) { if (normalized[key] === undefined || (opts.preserveExisting === false && !normalized[key])) { normalized[key] = value; } } } // Apply standard metadata defaults // Name (required field, should already be set, but ensure it exists) if (!normalized.name) { normalized.name = `Untitled ${this.getElementTypeName(elementType)}`; } // Description if (normalized.description === undefined || normalized.description === null) { normalized.description = ''; } // Version normalization if (opts.normalizeVersion) { if (!normalized.version) { normalized.version = '1.0.0'; } else { normalized.version = this.normalizeVersion(String(normalized.version)); } } // Author attribution if (opts.setAuthor && !normalized.author) { normalized.author = this.getCurrentUser(); } // Created date (YYYY-MM-DD format, matching PersonaManager line 178) if (opts.setCreated && !normalized.created) { if (normalized.created_date) { normalized.created = String(normalized.created_date).split('T')[0]; } else { normalized.created = this.generateDate('date-only'); } } // Modified date (ISO 8601 full format, matching AgentManager line 231) if (opts.setModified && !normalized.modified) { normalized.modified = this.generateDate('full'); } // Tags (initialize empty array if not provided) if (!normalized.tags) { normalized.tags = []; } // Unique ID generation (if enabled and missing) if (opts.generateId && !normalized.unique_id) { const author = normalized.author || this.getCurrentUser(); normalized.unique_id = this.assignUniqueId(normalized.name, author); } // Set element type if not present if (!normalized.type) { normalized.type = elementType; } return normalized; } /** * Generate default metadata for an element type * * Provides baseline metadata structure with all required fields populated. * Useful for creating new elements or testing. * * @param elementType - Type of element * @param overrides - Optional field overrides * @returns Default metadata structure * * @example * const defaults = metadataService.generateDefaultMetadata(ElementType.SKILL); * // Returns: { name: 'Untitled Skill', description: '', * // version: '1.0.0', author: 'anonymous', ... } */ generateDefaultMetadata(elementType, overrides = {}) { const defaults = { name: `Untitled ${this.getElementTypeName(elementType)}`, description: '', version: '1.0.0', author: this.getCurrentUser(), created: this.generateDate('date-only'), modified: this.generateDate('full'), type: elementType }; return { ...defaults, ...overrides }; } /** * Assign a unique ID based on name and author * * Format: Uses existing generateUniqueId utility which creates: * `{slugified-name}-{author-hash}` or random UUID * * PRESERVES EXACT BEHAVIOR: * - PersonaManager line 181-182: generateUniqueId(metadata.name, metadata.author) * - PersonaManager line 567: generateUniqueId(sanitizedName, this.getCurrentUserForAttribution() || undefined) * * @param name - Element name * @param author - Element author * @returns Unique identifier string * * @example * const id = metadataService.assignUniqueId('My Skill', 'john'); * // Returns: 'my-skill-a3f8d9e2' (or similar hash) */ assignUniqueId(name, author) { // Use existing utility function (preserves exact behavior) return generateUniqueId(name, author); } /** * Get current user for attribution * * Priority: * 1. Service-level currentUser (set via setCurrentUser) * 2. Environment variable DOLLHOUSE_USER * 3. OS system username (os.userInfo().username) * 4. Anonymous ID generation (last resort) * * The result is cached on first resolution so subsequent calls * return the same value without re-resolving. * * @returns Username or anonymous ID */ getCurrentUser() { if (!this.currentUser) { const envUser = process.env.DOLLHOUSE_USER; if (envUser) { this.currentUser = envUser; logger.debug('[MetadataService] User resolved from DOLLHOUSE_USER env var', { user: envUser }); } else { let osUser = null; try { osUser = userInfo().username || null; } catch { /* platform unsupported */ } if (osUser) { this.currentUser = osUser; logger.debug('[MetadataService] User resolved from OS username', { user: osUser }); } else { this.currentUser = generateAnonymousId(); logger.debug('[MetadataService] User resolved as anonymous (no env var or OS username available)', { user: this.currentUser }); } } } return this.currentUser; } /** * Set current user identity * * @param username - Username to set (null to clear) * * @example * metadataService.setCurrentUser('john'); * metadataService.setCurrentUser(null); // Clear */ setCurrentUser(username) { this.currentUser = username; logger.debug('[MetadataService] Current user set', { username }); } /** * Normalize version string to semantic versioning format * * Handles: * - 1.0 → 1.0.0 * - 2 → 2.0.0 * - 1.2.3-beta → 1.2.3-beta (preserves pre-release) * * PRESERVES EXACT BEHAVIOR: * - PersonaManager line 173: metadata.version = normalizeVersion(metadata.version) * - PersonaManager line 786-787: normalizeVersion(String(editablePersona.version)) * - Uses existing normalizeVersion() utility from BaseElement * * @param version - Version string to normalize * @returns Normalized version string * * @example * const v = metadataService.normalizeVersion('1.0'); * // Returns: '1.0.0' */ normalizeVersion(version) { // Use existing utility function (preserves exact behavior) return normalizeVersion(version); } /** * Generate ISO 8601 date string * * PRESERVES EXACT BEHAVIOR: * - PersonaManager line 178: new Date().toISOString().split('T')[0] (date-only) * - AgentManager line 231: new Date().toISOString() (full) * * @param format - Date format ('full' or 'date-only') * @returns Formatted date string * * @example * const date = metadataService.generateDate('date-only'); * // Returns: '2025-11-19' * * const datetime = metadataService.generateDate('full'); * // Returns: '2025-11-19T10:30:45.123Z' */ generateDate(format = 'full') { const now = new Date().toISOString(); if (format === 'date-only') { // Match PersonaManager line 178 exactly return now.split('T')[0]; } // Full ISO 8601 format return now; } /** * Validate metadata completeness * * Checks: * - Required fields present (name, description) * - Valid types for all fields * - Value constraints (version format, etc.) * * @param metadata - Metadata to validate * @param elementType - Element type for type-specific validation * @returns Validation result * * @example * const result = metadataService.validateMetadata(metadata, ElementType.SKILL); * if (!result.valid) { * console.error('Validation errors:', result.errors); * } */ validateMetadata(metadata, elementType) { const errors = []; // Required fields if (!metadata.name || typeof metadata.name !== 'string' || metadata.name.trim().length === 0) { errors.push('name is required and must be a non-empty string'); } if (metadata.description === undefined || metadata.description === null) { errors.push('description is required (can be empty string)'); } if (!metadata.version || typeof metadata.version !== 'string') { errors.push('version is required and must be a string'); } // Type validation if (metadata.author !== undefined && typeof metadata.author !== 'string') { errors.push('author must be a string'); } if (metadata.created !== undefined && typeof metadata.created !== 'string') { errors.push('created must be a date string'); } if (metadata.modified !== undefined && typeof metadata.modified !== 'string') { errors.push('modified must be a date string'); } if (metadata.tags !== undefined && !Array.isArray(metadata.tags)) { errors.push('tags must be an array'); } if (metadata.triggers !== undefined && !Array.isArray(metadata.triggers)) { errors.push('triggers must be an array'); } // Issue #755: Accept both plural ElementType ('agents') and singular label ('agent') if (metadata.type !== undefined && metadata.type !== elementType && metadata.type !== toSingularLabel(elementType)) { errors.push(`type must match element type (expected: ${elementType}, got: ${metadata.type})`); } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined }; } // Private helper methods /** * Get human-readable element type name * @private */ getElementTypeName(elementType) { const typeNames = { [ElementType.SKILL]: 'Skill', [ElementType.PERSONA]: 'Persona', [ElementType.TEMPLATE]: 'Template', [ElementType.AGENT]: 'Agent', [ElementType.MEMORY]: 'Memory', [ElementType.ENSEMBLE]: 'Ensemble' }; return typeNames[elementType] || 'Element'; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWV0YWRhdGFTZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZpY2VzL01ldGFkYXRhU2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTRCRztBQUVILE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDbkMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUN2RSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMvRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUErQzVDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9CRztBQUNILE1BQU0sT0FBTyxlQUFlO0lBQ2xCLFdBQVcsR0FBa0IsSUFBSSxDQUFDO0lBRTFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0E4Qkc7SUFDSCxpQkFBaUIsQ0FDZixHQUFlLEVBQ2YsV0FBd0IsRUFDeEIsVUFBd0MsRUFBRTtRQUUxQyxzQkFBc0I7UUFDdEIsTUFBTSxJQUFJLEdBQUc7WUFDWCxnQkFBZ0IsRUFBRSxPQUFPLENBQUMsZ0JBQWdCLElBQUksS0FBSztZQUNuRCxVQUFVLEVBQUUsT0FBTyxDQUFDLFVBQVUsSUFBSSxJQUFJO1lBQ3RDLGdCQUFnQixFQUFFLE9BQU8sQ0FBQyxnQkFBZ0IsSUFBSSxJQUFJO1lBQ2xELFNBQVMsRUFBRSxPQUFPLENBQUMsU0FBUyxJQUFJLElBQUk7WUFDcEMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVLElBQUksSUFBSTtZQUN0QyxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVcsSUFBSSxJQUFJO1lB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SSxRQUFRLENBQUMsSUFBSSxLQUFLLGVBQWUsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO1lBQ25ILE1BQU0sQ0FBQyxJQUFJLENBQUMsMkNBQTJDLFdBQVcsVUFBVSxRQUFRLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQztRQUNoRyxDQUFDO1FBRUQsT0FBTztZQUNMLEtBQUssRUFBRSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDMUIsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFNBQVM7U0FDL0MsQ0FBQztJQUNKLENBQUM7SUFFRCx5QkFBeUI7SUFFekI7OztPQUdHO0lBQ0ssa0JBQWtCLENBQUMsV0FBd0I7UUFDakQsTUFBTSxTQUFTLEdBQWdDO1lBQzdDLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU87WUFDNUIsQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLEVBQUUsU0FBUztZQUNoQyxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRSxVQUFVO1lBQ2xDLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU87WUFDNUIsQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUUsUUFBUTtZQUM5QixDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRSxVQUFVO1NBQ25DLENBQUM7UUFFRixPQUFPLFNBQVMsQ0FBQyxXQUFXLENBQUMsSUFBSSxTQUFTLENBQUM7SUFDN0MsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNZXRhZGF0YVNlcnZpY2UgLSBDZW50cmFsaXplZCBtZXRhZGF0YSBub3JtYWxpemF0aW9uIGFuZCBkZWZhdWx0c1xuICpcbiAqIEVsaW1pbmF0ZXMgfjMwMCBMT0Mgb2YgZHVwbGljYXRlIG1ldGFkYXRhIGhhbmRsaW5nIGFjcm9zcyA2IGVsZW1lbnQgbWFuYWdlcnMuXG4gKlxuICogS2V5IEZlYXR1cmVzOlxuICogLSBDb25zaXN0ZW50IG1ldGFkYXRhIHN0cnVjdHVyZSBhY3Jvc3MgYWxsIGVsZW1lbnQgdHlwZXNcbiAqIC0gQXV0b21hdGljIGRlZmF1bHQgdmFsdWUgYXNzaWdubWVudFxuICogLSBWZXJzaW9uIG5vcm1hbGl6YXRpb24gKDEuMCDihpIgMS4wLjApXG4gKiAtIFVuaXF1ZSBJRCBnZW5lcmF0aW9uXG4gKiAtIFVzZXIgYXR0cmlidXRpb24gd2l0aCBmYWxsYmFjayBjaGFpblxuICogLSBEYXRlIGZvcm1hdHRpbmcgKElTTyA4NjAxKVxuICpcbiAqIEFyY2hpdGVjdHVyZTpcbiAqIC0gUHVyZSBzZXJ2aWNlIChubyBzdGF0ZSBleGNlcHQgY3VycmVudFVzZXIpXG4gKiAtIFR5cGUtc2FmZSB3aXRoIFR5cGVTY3JpcHQgc3RyaWN0IG1vZGVcbiAqIC0gU2VjdXJpdHktZmlyc3QgKHZhbGlkYXRlcyBhbGwgaW5wdXRzKVxuICogLSBQcmVzZXJ2ZXMgZXhhY3QgY3VycmVudCBiZWhhdmlvciBmcm9tIG1hbmFnZXJzXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IG1ldGFkYXRhID0gbWV0YWRhdGFTZXJ2aWNlLm5vcm1hbGl6ZU1ldGFkYXRhKFxuICogICB7IG5hbWU6ICdNeSBTa2lsbCcsIGRlc2NyaXB0aW9uOiAnRG9lcyB0aGluZ3MnIH0sXG4gKiAgIEVsZW1lbnRUeXBlLlNLSUxMXG4gKiApO1xuICogLy8gUmV0dXJuczogeyBuYW1lOiAnTXkgU2tpbGwnLCBkZXNjcmlwdGlvbjogJ0RvZXMgdGhpbmdzJyxcbiAqIC8vICAgICAgICAgICB2ZXJzaW9uOiAnMS4wLjAnLCBhdXRob3I6ICd1c2VyJywgY3JlYXRlZDogJzIwMjUtMTEtMTknLCAuLi4gfVxuICogYGBgXG4gKi9cblxuaW1wb3J0IHsgdXNlckluZm8gfSBmcm9tICdub2RlOm9zJztcbmltcG9ydCB7IEVsZW1lbnRUeXBlIH0gZnJvbSAnLi4vcG9ydGZvbGlvL3R5cGVzLmpzJztcbmltcG9ydCB7IHRvU2luZ3VsYXJMYWJlbCB9IGZyb20gJy4uL3V0aWxzL2VsZW1lbnRUeXBlTm9ybWFsaXphdGlvbi5qcyc7XG5pbXBvcnQgeyBnZW5lcmF0ZUFub255bW91c0lkLCBnZW5lcmF0ZVVuaXF1ZUlkIH0gZnJvbSAnLi4vdXRpbHMvZmlsZXN5c3RlbS5qcyc7XG5pbXBvcnQgeyBub3JtYWxpemVWZXJzaW9uIH0gZnJvbSAnLi4vZWxlbWVudHMvQmFzZUVsZW1lbnQuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuLyoqXG4gKiBCYXNlIG1ldGFkYXRhIGludGVyZmFjZSAoY29tbW9uIHRvIGFsbCBlbGVtZW50cylcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCYXNlTWV0YWRhdGEge1xuICBuYW1lOiBzdHJpbmc7XG4gIGRlc2NyaXB0aW9uOiBzdHJpbmc7XG4gIHZlcnNpb24/OiBzdHJpbmc7ICAvLyBNYWRlIG9wdGlvbmFsIC0gbm9ybWFsaXplTWV0YWRhdGEgd2lsbCBzZXQgZGVmYXVsdCAnMS4wLjAnXG4gIGF1dGhvcj86IHN0cmluZztcbiAgY3JlYXRlZD86IHN0cmluZztcbiAgbW9kaWZpZWQ/OiBzdHJpbmc7XG4gIHRhZ3M/OiBzdHJpbmdbXTtcbiAgdHJpZ2dlcnM/OiBzdHJpbmdbXTtcbiAgdW5pcXVlX2lkPzogc3RyaW5nO1xuICB0eXBlPzogRWxlbWVudFR5cGU7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgbWV0YWRhdGEgbm9ybWFsaXphdGlvblxuICovXG5leHBvcnQgaW50ZXJmYWNlIE1ldGFkYXRhTm9ybWFsaXphdGlvbk9wdGlvbnMge1xuICAvKiogV2hldGhlciB0byBwcmVzZXJ2ZSBleGlzdGluZyB2YWx1ZXMgaW5zdGVhZCBvZiBhcHBseWluZyBkZWZhdWx0cyAoZGVmYXVsdDogZmFsc2UpICovXG4gIHByZXNlcnZlRXhpc3Rpbmc/OiBib29sZWFuO1xuXG4gIC8qKiBXaGV0aGVyIHRvIGdlbmVyYXRlIHVuaXF1ZSBJRCBpZiBtaXNzaW5nIChkZWZhdWx0OiB0cnVlKSAqL1xuICBnZW5lcmF0ZUlkPzogYm9vbGVhbjtcblxuICAvKiogV2hldGhlciB0byBub3JtYWxpemUgdmVyc2lvbiBmb3JtYXQgKGRlZmF1bHQ6IHRydWUpICovXG4gIG5vcm1hbGl6ZVZlcnNpb24/OiBib29sZWFuO1xuXG4gIC8qKiBXaGV0aGVyIHRvIHNldCBjdXJyZW50IHVzZXIgYXMgYXV0aG9yIChkZWZhdWx0OiB0cnVlKSAqL1xuICBzZXRBdXRob3I/OiBib29sZWFuO1xuXG4gIC8qKiBXaGV0aGVyIHRvIHNldCBjcmVhdGlvbiBkYXRlIGlmIG1pc3NpbmcgKGRlZmF1bHQ6IHRydWUpICovXG4gIHNldENyZWF0ZWQ/OiBib29sZWFuO1xuXG4gIC8qKiBXaGV0aGVyIHRvIHNldCBtb2RpZmllZCBkYXRlIChkZWZhdWx0OiB0cnVlKSAqL1xuICBzZXRNb2RpZmllZD86IGJvb2xlYW47XG5cbiAgLyoqIEFkZGl0aW9uYWwgdHlwZS1zcGVjaWZpYyBkZWZhdWx0cyB0byBtZXJnZSAqL1xuICB0eXBlRGVmYXVsdHM/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuXG4gIC8qKiBTa2lwIGFwcGx5aW5nIHR5cGVEZWZhdWx0cyAoZm9yIEJhc2VFbGVtZW50IGNvbnN0cnVjdG9yKSAtIG9ubHkgbm9ybWFsaXplIGNvbW1vbiBmaWVsZHMgKi9cbiAgc2tpcFR5cGVEZWZhdWx0cz86IGJvb2xlYW47XG59XG5cbi8qKlxuICogTWV0YWRhdGFTZXJ2aWNlIC0gQ2VudHJhbGl6ZWQgbWV0YWRhdGEgbm9ybWFsaXphdGlvbiBhbmQgZGVmYXVsdHNcbiAqXG4gKiBaRVJPIEZVTkNUSU9OQUxJVFkgTE9TUzpcbiAqIFRoaXMgc2VydmljZSBleHRyYWN0cyBleGFjdCBiZWhhdmlvciBmcm9tIGFsbCA2IG1hbmFnZXJzIHdpdGhvdXQgYW55IGNoYW5nZXMuXG4gKiBFdmVyeSBkZWZhdWx0IHZhbHVlLCBldmVyeSBub3JtYWxpemF0aW9uIHJ1bGUsIGV2ZXJ5IGVkZ2UgY2FzZSBpcyBwcmVzZXJ2ZWQuXG4gKlxuICogVVNBR0U6XG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBtZXRhZGF0YSA9IG1ldGFkYXRhU2VydmljZS5ub3JtYWxpemVNZXRhZGF0YShcbiAqICAgcmF3TWV0YWRhdGEsXG4gKiAgIEVsZW1lbnRUeXBlLlNLSUxMLFxuICogICB7XG4gKiAgICAgdHlwZURlZmF1bHRzOiB7XG4gKiAgICAgICBjYXRlZ29yeTogJ2dlbmVyYWwnLFxuICogICAgICAgZGlmZmljdWx0eTogJ2ludGVybWVkaWF0ZSdcbiAqICAgICB9XG4gKiAgIH1cbiAqICk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIE1ldGFkYXRhU2VydmljZSB7XG4gIHByaXZhdGUgY3VycmVudFVzZXI6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKlxuICAgKiBOb3JtYWxpemUgbWV0YWRhdGEgd2l0aCB0eXBlLXNwZWNpZmljIGRlZmF1bHRzXG4gICAqXG4gICAqIEFwcGxpZXM6XG4gICAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIG1pc3NpbmcgZmllbGRzXG4gICAqIC0gVmVyc2lvbiBub3JtYWxpemF0aW9uICgxLjAg4oaSIDEuMC4wKVxuICAgKiAtIFVuaXF1ZSBJRCBnZW5lcmF0aW9uXG4gICAqIC0gVXNlciBhdHRyaWJ1dGlvblxuICAgKiAtIERhdGUgZm9ybWF0dGluZyAoSVNPIDg2MDEpXG4gICAqXG4gICAqIFBSRVNFUlZFUyBFWEFDVCBCRUhBVklPUjpcbiAgICogLSBQZXJzb25hTWFuYWdlcjogbGluZXMgMTY0LTIwNSAobm9ybWFsaXplUGVyc29uYUZvclNhdmUpXG4gICAqIC0gQWdlbnRNYW5hZ2VyOiBsaW5lcyAxMTMsIDY0Mi02NDQgKGdldEN1cnJlbnRVc2VyRm9yQXR0cmlidXRpb24pXG4gICAqIC0gTWVtb3J5TWFuYWdlcjogbGluZXMgMTM0Mi0xNDIxIChwYXJzZU1lbW9yeUZpbGUgbWV0YWRhdGEgZGVmYXVsdHMpXG4gICAqIC0gU2tpbGxNYW5hZ2VyOiBsaW5lcyA0NC0xMDIgKGNyZWF0ZSBtZXRob2QgZGVmYXVsdHMpXG4gICAqIC0gVGVtcGxhdGVNYW5hZ2VyOiBsaW5lcyAyOTgtMzc1ICh2YWxpZGF0ZU1ldGFkYXRhIGRlZmF1bHRzKVxuICAgKiAtIEVuc2VtYmxlTWFuYWdlcjogbGluZXMgODYtMzQ3IChwYXJzZU1ldGFkYXRhIGRlZmF1bHRzKVxuICAgKlxuICAgKiBAcGFyYW0gcmF3IC0gUmF3IG1ldGFkYXRhIGZyb20gdXNlciBpbnB1dCBvciBmaWxlXG4gICAqIEBwYXJhbSBlbGVtZW50VHlwZSAtIFR5cGUgb2YgZWxlbWVudCBiZWluZyBjcmVhdGVkXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gTm9ybWFsaXphdGlvbiBvcHRpb25zXG4gICAqIEByZXR1cm5zIEZ1bGx5IG5vcm1hbGl6ZWQgbWV0YWRhdGFcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogY29uc3QgbWV0YWRhdGEgPSBtZXRhZGF0YVNlcnZpY2Uubm9ybWFsaXplTWV0YWRhdGEoXG4gICAqICAgeyBuYW1lOiAnTXkgU2tpbGwnLCBkZXNjcmlwdGlvbjogJ0RvZXMgdGhpbmdzJyB9LFxuICAgKiAgIEVsZW1lbnRUeXBlLlNLSUxMXG4gICAqICk7XG4gICAqIC8vIFJldHVybnM6IHsgbmFtZTogJ015IFNraWxsJywgZGVzY3JpcHRpb246ICdEb2VzIHRoaW5ncycsXG4gICAqIC8vICAgICAgICAgICB2ZXJzaW9uOiAnMS4wLjAnLCBhdXRob3I6ICd1c2VyJywgY3JlYXRlZDogJzIwMjUtMTEtMTknLCAuLi4gfVxuICAgKi9cbiAgbm9ybWFsaXplTWV0YWRhdGE8VCBleHRlbmRzIEJhc2VNZXRhZGF0YT4oXG4gICAgcmF3OiBQYXJ0aWFsPFQ+LFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSxcbiAgICBvcHRpb25zOiBNZXRhZGF0YU5vcm1hbGl6YXRpb25PcHRpb25zID0ge31cbiAgKTogVCB7XG4gICAgLy8gU2V0IGRlZmF1bHQgb3B0aW9uc1xuICAgIGNvbnN0IG9wdHMgPSB7XG4gICAgICBwcmVzZXJ2ZUV4aXN0aW5nOiBvcHRpb25zLnByZXNlcnZlRXhpc3RpbmcgPz8gZmFsc2UsXG4gICAgICBnZW5lcmF0ZUlkOiBvcHRpb25zLmdlbmVyYXRlSWQgPz8gdHJ1ZSxcbiAgICAgIG5vcm1hbGl6ZVZlcnNpb246IG9wdGlvbnMubm9ybWFsaXplVmVyc2lvbiA/PyB0cnVlLFxuICAgICAgc2V0QXV0aG9yOiBvcHRpb25zLnNldEF1dGhvciA/PyB0cnVlLFxuICAgICAgc2V0Q3JlYXRlZDogb3B0aW9ucy5zZXRDcmVhdGVkID8/IHRydWUsXG4gICAgICBzZXRNb2RpZmllZDogb3B0aW9ucy5zZXRNb2RpZmllZCA/PyB0cnVlLFxuICAgICAgdHlwZURlZmF1bHRzOiBvcHRpb25zLnR5cGVEZWZhdWx0cyA/PyB7fSxcbiAgICAgIHNraXBUeXBlRGVmYXVsdHM6IG9wdGlvbnMuc2tpcFR5cGVEZWZhdWx0cyA/PyBmYWxzZVxuICAgIH07XG5cbiAgICAvLyBTdGFydCB3aXRoIHJhdyBtZXRhZGF0YVxuICAgIGNvbnN0IG5vcm1hbGl6ZWQ6IGFueSA9IHsgLi4ucmF3IH07XG5cbiAgICAvLyBBcHBseSB0eXBlLXNwZWNpZmljIGRlZmF1bHRzIEZJUlNUIChjYW4gYmUgb3ZlcnJpZGRlbiBieSBzdGFuZGFyZCBkZWZhdWx0cylcbiAgICAvLyBTa2lwIGlmIHNraXBUeXBlRGVmYXVsdHMgaXMgdHJ1ZSAoZm9yIEJhc2VFbGVtZW50IGNvbnN0cnVjdG9yKVxuICAgIGlmIChvcHRzLnR5cGVEZWZhdWx0cyAmJiBPYmplY3Qua2V5cyhvcHRzLnR5cGVEZWZhdWx0cykubGVuZ3RoID4gMCAmJiAhb3B0cy5za2lwVHlwZURlZmF1bHRzKSB7XG4gICAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvcHRzLnR5cGVEZWZhdWx0cykpIHtcbiAgICAgICAgaWYgKG5vcm1hbGl6ZWRba2V5XSA9PT0gdW5kZWZpbmVkIHx8IChvcHRzLnByZXNlcnZlRXhpc3RpbmcgPT09IGZhbHNlICYmICFub3JtYWxpemVkW2tleV0pKSB7XG4gICAgICAgICAgbm9ybWFsaXplZFtrZXldID0gdmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBcHBseSBzdGFuZGFyZCBtZXRhZGF0YSBkZWZhdWx0c1xuXG4gICAgLy8gTmFtZSAocmVxdWlyZWQgZmllbGQsIHNob3VsZCBhbHJlYWR5IGJlIHNldCwgYnV0IGVuc3VyZSBpdCBleGlzdHMpXG4gICAgaWYgKCFub3JtYWxpemVkLm5hbWUpIHtcbiAgICAgIG5vcm1hbGl6ZWQubmFtZSA9IGBVbnRpdGxlZCAke3RoaXMuZ2V0RWxlbWVudFR5cGVOYW1lKGVsZW1lbnRUeXBlKX1gO1xuICAgIH1cblxuICAgIC8vIERlc2NyaXB0aW9uXG4gICAgaWYgKG5vcm1hbGl6ZWQuZGVzY3JpcHRpb24gPT09IHVuZGVmaW5lZCB8fCBub3JtYWxpemVkLmRlc2NyaXB0aW9uID09PSBudWxsKSB7XG4gICAgICBub3JtYWxpemVkLmRlc2NyaXB0aW9uID0gJyc7XG4gICAgfVxuXG4gICAgLy8gVmVyc2lvbiBub3JtYWxpemF0aW9uXG4gICAgaWYgKG9wdHMubm9ybWFsaXplVmVyc2lvbikge1xuICAgICAgaWYgKCFub3JtYWxpemVkLnZlcnNpb24pIHtcbiAgICAgICAgbm9ybWFsaXplZC52ZXJzaW9uID0gJzEuMC4wJztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG5vcm1hbGl6ZWQudmVyc2lvbiA9IHRoaXMubm9ybWFsaXplVmVyc2lvbihTdHJpbmcobm9ybWFsaXplZC52ZXJzaW9uKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQXV0aG9yIGF0dHJpYnV0aW9uXG4gICAgaWYgKG9wdHMuc2V0QXV0aG9yICYmICFub3JtYWxpemVkLmF1dGhvcikge1xuICAgICAgbm9ybWFsaXplZC5hdXRob3IgPSB0aGlzLmdldEN1cnJlbnRVc2VyKCk7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlZCBkYXRlIChZWVlZLU1NLUREIGZvcm1hdCwgbWF0Y2hpbmcgUGVyc29uYU1hbmFnZXIgbGluZSAxNzgpXG4gICAgaWYgKG9wdHMuc2V0Q3JlYXRlZCAmJiAhbm9ybWFsaXplZC5jcmVhdGVkKSB7XG4gICAgICBpZiAobm9ybWFsaXplZC5jcmVhdGVkX2RhdGUpIHtcbiAgICAgICAgbm9ybWFsaXplZC5jcmVhdGVkID0gU3RyaW5nKG5vcm1hbGl6ZWQuY3JlYXRlZF9kYXRlKS5zcGxpdCgnVCcpWzBdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbm9ybWFsaXplZC5jcmVhdGVkID0gdGhpcy5nZW5lcmF0ZURhdGUoJ2RhdGUtb25seScpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIE1vZGlmaWVkIGRhdGUgKElTTyA4NjAxIGZ1bGwgZm9ybWF0LCBtYXRjaGluZyBBZ2VudE1hbmFnZXIgbGluZSAyMzEpXG4gICAgaWYgKG9wdHMuc2V0TW9kaWZpZWQgJiYgIW5vcm1hbGl6ZWQubW9kaWZpZWQpIHtcbiAgICAgIG5vcm1hbGl6ZWQubW9kaWZpZWQgPSB0aGlzLmdlbmVyYXRlRGF0ZSgnZnVsbCcpO1xuICAgIH1cblxuICAgIC8vIFRhZ3MgKGluaXRpYWxpemUgZW1wdHkgYXJyYXkgaWYgbm90IHByb3ZpZGVkKVxuICAgIGlmICghbm9ybWFsaXplZC50YWdzKSB7XG4gICAgICBub3JtYWxpemVkLnRhZ3MgPSBbXTtcbiAgICB9XG5cbiAgICAvLyBVbmlxdWUgSUQgZ2VuZXJhdGlvbiAoaWYgZW5hYmxlZCBhbmQgbWlzc2luZylcbiAgICBpZiAob3B0cy5nZW5lcmF0ZUlkICYmICFub3JtYWxpemVkLnVuaXF1ZV9pZCkge1xuICAgICAgY29uc3QgYXV0aG9yID0gbm9ybWFsaXplZC5hdXRob3IgfHwgdGhpcy5nZXRDdXJyZW50VXNlcigpO1xuICAgICAgbm9ybWFsaXplZC51bmlxdWVfaWQgPSB0aGlzLmFzc2lnblVuaXF1ZUlkKG5vcm1hbGl6ZWQubmFtZSwgYXV0aG9yKTtcbiAgICB9XG5cbiAgICAvLyBTZXQgZWxlbWVudCB0eXBlIGlmIG5vdCBwcmVzZW50XG4gICAgaWYgKCFub3JtYWxpemVkLnR5cGUpIHtcbiAgICAgIG5vcm1hbGl6ZWQudHlwZSA9IGVsZW1lbnRUeXBlO1xuICAgIH1cblxuICAgIHJldHVybiBub3JtYWxpemVkIGFzIFQ7XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgZGVmYXVsdCBtZXRhZGF0YSBmb3IgYW4gZWxlbWVudCB0eXBlXG4gICAqXG4gICAqIFByb3ZpZGVzIGJhc2VsaW5lIG1ldGFkYXRhIHN0cnVjdHVyZSB3aXRoIGFsbCByZXF1aXJlZCBmaWVsZHMgcG9wdWxhdGVkLlxuICAgKiBVc2VmdWwgZm9yIGNyZWF0aW5nIG5ldyBlbGVtZW50cyBvciB0ZXN0aW5nLlxuICAgKlxuICAgKiBAcGFyYW0gZWxlbWVudFR5cGUgLSBUeXBlIG9mIGVsZW1lbnRcbiAgICogQHBhcmFtIG92ZXJyaWRlcyAtIE9wdGlvbmFsIGZpZWxkIG92ZXJyaWRlc1xuICAgKiBAcmV0dXJucyBEZWZhdWx0IG1ldGFkYXRhIHN0cnVjdHVyZVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBjb25zdCBkZWZhdWx0cyA9IG1ldGFkYXRhU2VydmljZS5nZW5lcmF0ZURlZmF1bHRNZXRhZGF0YShFbGVtZW50VHlwZS5TS0lMTCk7XG4gICAqIC8vIFJldHVybnM6IHsgbmFtZTogJ1VudGl0bGVkIFNraWxsJywgZGVzY3JpcHRpb246ICcnLFxuICAgKiAvLyAgICAgICAgICAgdmVyc2lvbjogJzEuMC4wJywgYXV0aG9yOiAnYW5vbnltb3VzJywgLi4uIH1cbiAgICovXG4gIGdlbmVyYXRlRGVmYXVsdE1ldGFkYXRhKFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSxcbiAgICBvdmVycmlkZXM6IFBhcnRpYWw8QmFzZU1ldGFkYXRhPiA9IHt9XG4gICk6IEJhc2VNZXRhZGF0YSB7XG4gICAgY29uc3QgZGVmYXVsdHM6IEJhc2VNZXRhZGF0YSA9IHtcbiAgICAgIG5hbWU6IGBVbnRpdGxlZCAke3RoaXMuZ2V0RWxlbWVudFR5cGVOYW1lKGVsZW1lbnRUeXBlKX1gLFxuICAgICAgZGVzY3JpcHRpb246ICcnLFxuICAgICAgdmVyc2lvbjogJzEuMC4wJyxcbiAgICAgIGF1dGhvcjogdGhpcy5nZXRDdXJyZW50VXNlcigpLFxuICAgICAgY3JlYXRlZDogdGhpcy5nZW5lcmF0ZURhdGUoJ2RhdGUtb25seScpLFxuICAgICAgbW9kaWZpZWQ6IHRoaXMuZ2VuZXJhdGVEYXRlKCdmdWxsJyksXG4gICAgICB0eXBlOiBlbGVtZW50VHlwZVxuICAgIH07XG5cbiAgICByZXR1cm4geyAuLi5kZWZhdWx0cywgLi4ub3ZlcnJpZGVzIH07XG4gIH1cblxuICAvKipcbiAgICogQXNzaWduIGEgdW5pcXVlIElEIGJhc2VkIG9uIG5hbWUgYW5kIGF1dGhvclxuICAgKlxuICAgKiBGb3JtYXQ6IFVzZXMgZXhpc3RpbmcgZ2VuZXJhdGVVbmlxdWVJZCB1dGlsaXR5IHdoaWNoIGNyZWF0ZXM6XG4gICAqIGB7c2x1Z2lmaWVkLW5hbWV9LXthdXRob3ItaGFzaH1gIG9yIHJhbmRvbSBVVUlEXG4gICAqXG4gICAqIFBSRVNFUlZFUyBFWEFDVCBCRUhBVklPUjpcbiAgICogLSBQZXJzb25hTWFuYWdlciBsaW5lIDE4MS0xODI6IGdlbmVyYXRlVW5pcXVlSWQobWV0YWRhdGEubmFtZSwgbWV0YWRhdGEuYXV0aG9yKVxuICAgKiAtIFBlcnNvbmFNYW5hZ2VyIGxpbmUgNTY3OiBnZW5lcmF0ZVVuaXF1ZUlkKHNhbml0aXplZE5hbWUsIHRoaXMuZ2V0Q3VycmVudFVzZXJGb3JBdHRyaWJ1dGlvbigpIHx8IHVuZGVmaW5lZClcbiAgICpcbiAgICogQHBhcmFtIG5hbWUgLSBFbGVtZW50IG5hbWVcbiAgICogQHBhcmFtIGF1dGhvciAtIEVsZW1lbnQgYXV0aG9yXG4gICAqIEByZXR1cm5zIFVuaXF1ZSBpZGVudGlmaWVyIHN0cmluZ1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBjb25zdCBpZCA9IG1ldGFkYXRhU2VydmljZS5hc3NpZ25VbmlxdWVJZCgnTXkgU2tpbGwnLCAnam9obicpO1xuICAgKiAvLyBSZXR1cm5zOiAnbXktc2tpbGwtYTNmOGQ5ZTInIChvciBzaW1pbGFyIGhhc2gpXG4gICAqL1xuICBhc3NpZ25VbmlxdWVJZChuYW1lOiBzdHJpbmcsIGF1dGhvcjogc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyBVc2UgZXhpc3RpbmcgdXRpbGl0eSBmdW5jdGlvbiAocHJlc2VydmVzIGV4YWN0IGJlaGF2aW9yKVxuICAgIHJldHVybiBnZW5lcmF0ZVVuaXF1ZUlkKG5hbWUsIGF1dGhvcik7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGN1cnJlbnQgdXNlciBmb3IgYXR0cmlidXRpb25cbiAgICpcbiAgICogUHJpb3JpdHk6XG4gICAqIDEuIFNlcnZpY2UtbGV2ZWwgY3VycmVudFVzZXIgKHNldCB2aWEgc2V0Q3VycmVudFVzZXIpXG4gICAqIDIuIEVudmlyb25tZW50IHZhcmlhYmxlIERPTExIT1VTRV9VU0VSXG4gICAqIDMuIE9TIHN5c3RlbSB1c2VybmFtZSAob3MudXNlckluZm8oKS51c2VybmFtZSlcbiAgICogNC4gQW5vbnltb3VzIElEIGdlbmVyYXRpb24gKGxhc3QgcmVzb3J0KVxuICAgKlxuICAgKiBUaGUgcmVzdWx0IGlzIGNhY2hlZCBvbiBmaXJzdCByZXNvbHV0aW9uIHNvIHN1YnNlcXVlbnQgY2FsbHNcbiAgICogcmV0dXJuIHRoZSBzYW1lIHZhbHVlIHdpdGhvdXQgcmUtcmVzb2x2aW5nLlxuICAgKlxuICAgKiBAcmV0dXJucyBVc2VybmFtZSBvciBhbm9ueW1vdXMgSURcbiAgICovXG4gIGdldEN1cnJlbnRVc2VyKCk6IHN0cmluZyB7XG4gICAgaWYgKCF0aGlzLmN1cnJlbnRVc2VyKSB7XG4gICAgICBjb25zdCBlbnZVc2VyID0gcHJvY2Vzcy5lbnYuRE9MTEhPVVNFX1VTRVI7XG4gICAgICBpZiAoZW52VXNlcikge1xuICAgICAgICB0aGlzLmN1cnJlbnRVc2VyID0gZW52VXNlcjtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdbTWV0YWRhdGFTZXJ2aWNlXSBVc2VyIHJlc29sdmVkIGZyb20gRE9MTEhPVVNFX1VTRVIgZW52IHZhcicsIHsgdXNlcjogZW52VXNlciB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxldCBvc1VzZXI6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAgICAgICB0cnkgeyBvc1VzZXIgPSB1c2VySW5mbygpLnVzZXJuYW1lIHx8IG51bGw7IH0gY2F0Y2ggeyAvKiBwbGF0Zm9ybSB1bnN1cHBvcnRlZCAqLyB9XG4gICAgICAgIGlmIChvc1VzZXIpIHtcbiAgICAgICAgICB0aGlzLmN1cnJlbnRVc2VyID0gb3NVc2VyO1xuICAgICAgICAgIGxvZ2dlci5kZWJ1ZygnW01ldGFkYXRhU2VydmljZV0gVXNlciByZXNvbHZlZCBmcm9tIE9TIHVzZXJuYW1lJywgeyB1c2VyOiBvc1VzZXIgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5jdXJyZW50VXNlciA9IGdlbmVyYXRlQW5vbnltb3VzSWQoKTtcbiAgICAgICAgICBsb2dnZXIuZGVidWcoJ1tNZXRhZGF0YVNlcnZpY2VdIFVzZXIgcmVzb2x2ZWQgYXMgYW5vbnltb3VzIChubyBlbnYgdmFyIG9yIE9TIHVzZXJuYW1lIGF2YWlsYWJsZSknLCB7IHVzZXI6IHRoaXMuY3VycmVudFVzZXIgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFVzZXI7XG4gIH1cblxuICAvKipcbiAgICogU2V0IGN1cnJlbnQgdXNlciBpZGVudGl0eVxuICAgKlxuICAgKiBAcGFyYW0gdXNlcm5hbWUgLSBVc2VybmFtZSB0byBzZXQgKG51bGwgdG8gY2xlYXIpXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIG1ldGFkYXRhU2VydmljZS5zZXRDdXJyZW50VXNlcignam9obicpO1xuICAgKiBtZXRhZGF0YVNlcnZpY2Uuc2V0Q3VycmVudFVzZXIobnVsbCk7IC8vIENsZWFyXG4gICAqL1xuICBzZXRDdXJyZW50VXNlcih1c2VybmFtZTogc3RyaW5nIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuY3VycmVudFVzZXIgPSB1c2VybmFtZTtcbiAgICBsb2dnZXIuZGVidWcoJ1tNZXRhZGF0YVNlcnZpY2VdIEN1cnJlbnQgdXNlciBzZXQnLCB7IHVzZXJuYW1lIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIE5vcm1hbGl6ZSB2ZXJzaW9uIHN0cmluZyB0byBzZW1hbnRpYyB2ZXJzaW9uaW5nIGZvcm1hdFxuICAgKlxuICAgKiBIYW5kbGVzOlxuICAgKiAtIDEuMCDihpIgMS4wLjBcbiAgICogLSAyIOKGkiAyLjAuMFxuICAgKiAtIDEuMi4zLWJldGEg4oaSIDEuMi4zLWJldGEgKHByZXNlcnZlcyBwcmUtcmVsZWFzZSlcbiAgICpcbiAgICogUFJFU0VSVkVTIEVYQUNUIEJFSEFWSU9SOlxuICAgKiAtIFBlcnNvbmFNYW5hZ2VyIGxpbmUgMTczOiBtZXRhZGF0YS52ZXJzaW9uID0gbm9ybWFsaXplVmVyc2lvbihtZXRhZGF0YS52ZXJzaW9uKVxuICAgKiAtIFBlcnNvbmFNYW5hZ2VyIGxpbmUgNzg2LTc4Nzogbm9ybWFsaXplVmVyc2lvbihTdHJpbmcoZWRpdGFibGVQZXJzb25hLnZlcnNpb24pKVxuICAgKiAtIFVzZXMgZXhpc3Rpbmcgbm9ybWFsaXplVmVyc2lvbigpIHV0aWxpdHkgZnJvbSBCYXNlRWxlbWVudFxuICAgKlxuICAgKiBAcGFyYW0gdmVyc2lvbiAtIFZlcnNpb24gc3RyaW5nIHRvIG5vcm1hbGl6ZVxuICAgKiBAcmV0dXJucyBOb3JtYWxpemVkIHZlcnNpb24gc3RyaW5nXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGNvbnN0IHYgPSBtZXRhZGF0YVNlcnZpY2Uubm9ybWFsaXplVmVyc2lvbignMS4wJyk7XG4gICAqIC8vIFJldHVybnM6ICcxLjAuMCdcbiAgICovXG4gIG5vcm1hbGl6ZVZlcnNpb24odmVyc2lvbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyBVc2UgZXhpc3RpbmcgdXRpbGl0eSBmdW5jdGlvbiAocHJlc2VydmVzIGV4YWN0IGJlaGF2aW9yKVxuICAgIHJldHVybiBub3JtYWxpemVWZXJzaW9uKHZlcnNpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIElTTyA4NjAxIGRhdGUgc3RyaW5nXG4gICAqXG4gICAqIFBSRVNFUlZFUyBFWEFDVCBCRUhBVklPUjpcbiAgICogLSBQZXJzb25hTWFuYWdlciBsaW5lIDE3ODogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLnNwbGl0KCdUJylbMF0gKGRhdGUtb25seSlcbiAgICogLSBBZ2VudE1hbmFnZXIgbGluZSAyMzE6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSAoZnVsbClcbiAgICpcbiAgICogQHBhcmFtIGZvcm1hdCAtIERhdGUgZm9ybWF0ICgnZnVsbCcgb3IgJ2RhdGUtb25seScpXG4gICAqIEByZXR1cm5zIEZvcm1hdHRlZCBkYXRlIHN0cmluZ1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBjb25zdCBkYXRlID0gbWV0YWRhdGFTZXJ2aWNlLmdlbmVyYXRlRGF0ZSgnZGF0ZS1vbmx5Jyk7XG4gICAqIC8vIFJldHVybnM6ICcyMDI1LTExLTE5J1xuICAgKlxuICAgKiBjb25zdCBkYXRldGltZSA9IG1ldGFkYXRhU2VydmljZS5nZW5lcmF0ZURhdGUoJ2Z1bGwnKTtcbiAgICogLy8gUmV0dXJuczogJzIwMjUtMTEtMTlUMTA6MzA6NDUuMTIzWidcbiAgICovXG4gIGdlbmVyYXRlRGF0ZShmb3JtYXQ6ICdmdWxsJyB8ICdkYXRlLW9ubHknID0gJ2Z1bGwnKTogc3RyaW5nIHtcbiAgICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG5cbiAgICBpZiAoZm9ybWF0ID09PSAnZGF0ZS1vbmx5Jykge1xuICAgICAgLy8gTWF0Y2ggUGVyc29uYU1hbmFnZXIgbGluZSAxNzggZXhhY3RseVxuICAgICAgcmV0dXJuIG5vdy5zcGxpdCgnVCcpWzBdO1xuICAgIH1cblxuICAgIC8vIEZ1bGwgSVNPIDg2MDEgZm9ybWF0XG4gICAgcmV0dXJuIG5vdztcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBtZXRhZGF0YSBjb21wbGV0ZW5lc3NcbiAgICpcbiAgICogQ2hlY2tzOlxuICAgKiAtIFJlcXVpcmVkIGZpZWxkcyBwcmVzZW50IChuYW1lLCBkZXNjcmlwdGlvbilcbiAgICogLSBWYWxpZCB0eXBlcyBmb3IgYWxsIGZpZWxkc1xuICAgKiAtIFZhbHVlIGNvbnN0cmFpbnRzICh2ZXJzaW9uIGZvcm1hdCwgZXRjLilcbiAgICpcbiAgICogQHBhcmFtIG1ldGFkYXRhIC0gTWV0YWRhdGEgdG8gdmFsaWRhdGVcbiAgICogQHBhcmFtIGVsZW1lbnRUeXBlIC0gRWxlbWVudCB0eXBlIGZvciB0eXBlLXNwZWNpZmljIHZhbGlkYXRpb25cbiAgICogQHJldHVybnMgVmFsaWRhdGlvbiByZXN1bHRcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogY29uc3QgcmVzdWx0ID0gbWV0YWRhdGFTZXJ2aWNlLnZhbGlkYXRlTWV0YWRhdGEobWV0YWRhdGEsIEVsZW1lbnRUeXBlLlNLSUxMKTtcbiAgICogaWYgKCFyZXN1bHQudmFsaWQpIHtcbiAgICogICBjb25zb2xlLmVycm9yKCdWYWxpZGF0aW9uIGVycm9yczonLCByZXN1bHQuZXJyb3JzKTtcbiAgICogfVxuICAgKi9cbiAgdmFsaWRhdGVNZXRhZGF0YShcbiAgICBtZXRhZGF0YTogQmFzZU1ldGFkYXRhLFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZVxuICApOiB7IHZhbGlkOiBib29sZWFuOyBlcnJvcnM/OiBzdHJpbmdbXSB9IHtcbiAgICBjb25zdCBlcnJvcnM6IHN0cmluZ1tdID0gW107XG5cbiAgICAvLyBSZXF1aXJlZCBmaWVsZHNcbiAgICBpZiAoIW1ldGFkYXRhLm5hbWUgfHwgdHlwZW9mIG1ldGFkYXRhLm5hbWUgIT09ICdzdHJpbmcnIHx8IG1ldGFkYXRhLm5hbWUudHJpbSgpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgZXJyb3JzLnB1c2goJ25hbWUgaXMgcmVxdWlyZWQgYW5kIG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nJyk7XG4gICAgfVxuXG4gICAgaWYgKG1ldGFkYXRhLmRlc2NyaXB0aW9uID09PSB1bmRlZmluZWQgfHwgbWV0YWRhdGEuZGVzY3JpcHRpb24gPT09IG51bGwpIHtcbiAgICAgIGVycm9ycy5wdXNoKCdkZXNjcmlwdGlvbiBpcyByZXF1aXJlZCAoY2FuIGJlIGVtcHR5IHN0cmluZyknKTtcbiAgICB9XG5cbiAgICBpZiAoIW1ldGFkYXRhLnZlcnNpb24gfHwgdHlwZW9mIG1ldGFkYXRhLnZlcnNpb24gIT09ICdzdHJpbmcnKSB7XG4gICAgICBlcnJvcnMucHVzaCgndmVyc2lvbiBpcyByZXF1aXJlZCBhbmQgbXVzdCBiZSBhIHN0cmluZycpO1xuICAgIH1cblxuICAgIC8vIFR5cGUgdmFsaWRhdGlvblxuICAgIGlmIChtZXRhZGF0YS5hdXRob3IgIT09IHVuZGVmaW5lZCAmJiB0eXBlb2YgbWV0YWRhdGEuYXV0aG9yICE9PSAnc3RyaW5nJykge1xuICAgICAgZXJyb3JzLnB1c2goJ2F1dGhvciBtdXN0IGJlIGEgc3RyaW5nJyk7XG4gICAgfVxuXG4gICAgaWYgKG1ldGFkYXRhLmNyZWF0ZWQgIT09IHVuZGVmaW5lZCAmJiB0eXBlb2YgbWV0YWRhdGEuY3JlYXRlZCAhPT0gJ3N0cmluZycpIHtcbiAgICAgIGVycm9ycy5wdXNoKCdjcmVhdGVkIG11c3QgYmUgYSBkYXRlIHN0cmluZycpO1xuICAgIH1cblxuICAgIGlmIChtZXRhZGF0YS5tb2RpZmllZCAhPT0gdW5kZWZpbmVkICYmIHR5cGVvZiBtZXRhZGF0YS5tb2RpZmllZCAhPT0gJ3N0cmluZycpIHtcbiAgICAgIGVycm9ycy5wdXNoKCdtb2RpZmllZCBtdXN0IGJlIGEgZGF0ZSBzdHJpbmcnKTtcbiAgICB9XG5cbiAgICBpZiAobWV0YWRhdGEudGFncyAhPT0gdW5kZWZpbmVkICYmICFBcnJheS5pc0FycmF5KG1ldGFkYXRhLnRhZ3MpKSB7XG4gICAgICBlcnJvcnMucHVzaCgndGFncyBtdXN0IGJlIGFuIGFycmF5Jyk7XG4gICAgfVxuXG4gICAgaWYgKG1ldGFkYXRhLnRyaWdnZXJzICE9PSB1bmRlZmluZWQgJiYgIUFycmF5LmlzQXJyYXkobWV0YWRhdGEudHJpZ2dlcnMpKSB7XG4gICAgICBlcnJvcnMucHVzaCgndHJpZ2dlcnMgbXVzdCBiZSBhbiBhcnJheScpO1xuICAgIH1cblxuICAgIC8vIElzc3VlICM3NTU6IEFjY2VwdCBib3RoIHBsdXJhbCBFbGVtZW50VHlwZSAoJ2FnZW50cycpIGFuZCBzaW5ndWxhciBsYWJlbCAoJ2FnZW50JylcbiAgICBpZiAobWV0YWRhdGEudHlwZSAhPT0gdW5kZWZpbmVkICYmIG1ldGFkYXRhLnR5cGUgIT09IGVsZW1lbnRUeXBlICYmIG1ldGFkYXRhLnR5cGUgIT09IHRvU2luZ3VsYXJMYWJlbChlbGVtZW50VHlwZSkpIHtcbiAgICAgIGVycm9ycy5wdXNoKGB0eXBlIG11c3QgbWF0Y2ggZWxlbWVudCB0eXBlIChleHBlY3RlZDogJHtlbGVtZW50VHlwZX0sIGdvdDogJHttZXRhZGF0YS50eXBlfSlgKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgdmFsaWQ6IGVycm9ycy5sZW5ndGggPT09IDAsXG4gICAgICBlcnJvcnM6IGVycm9ycy5sZW5ndGggPiAwID8gZXJyb3JzIDogdW5kZWZpbmVkXG4gICAgfTtcbiAgfVxuXG4gIC8vIFByaXZhdGUgaGVscGVyIG1ldGhvZHNcblxuICAvKipcbiAgICogR2V0IGh1bWFuLXJlYWRhYmxlIGVsZW1lbnQgdHlwZSBuYW1lXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBwcml2YXRlIGdldEVsZW1lbnRUeXBlTmFtZShlbGVtZW50VHlwZTogRWxlbWVudFR5cGUpOiBzdHJpbmcge1xuICAgIGNvbnN0IHR5cGVOYW1lczogUmVjb3JkPEVsZW1lbnRUeXBlLCBzdHJpbmc+ID0ge1xuICAgICAgW0VsZW1lbnRUeXBlLlNLSUxMXTogJ1NraWxsJyxcbiAgICAgIFtFbGVtZW50VHlwZS5QRVJTT05BXTogJ1BlcnNvbmEnLFxuICAgICAgW0VsZW1lbnRUeXBlLlRFTVBMQVRFXTogJ1RlbXBsYXRlJyxcbiAgICAgIFtFbGVtZW50VHlwZS5BR0VOVF06ICdBZ2VudCcsXG4gICAgICBbRWxlbWVudFR5cGUuTUVNT1JZXTogJ01lbW9yeScsXG4gICAgICBbRWxlbWVudFR5cGUuRU5TRU1CTEVdOiAnRW5zZW1ibGUnXG4gICAgfTtcblxuICAgIHJldHVybiB0eXBlTmFtZXNbZWxlbWVudFR5cGVdIHx8ICdFbGVtZW50JztcbiAgfVxufVxuIl19