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.

661 lines 96.3 kB
/**
 * Element Policies
 *
 * Handles element-based access control for the Gatekeeper.
 * Allows ANY DollhouseMCP element to define policies in its metadata
 * that override or restrict the default operation policies.
 *
 * Policy Resolution Order:
 * 1. Active element deny list (highest priority - blocks operation)
 * 2. Active element confirm list (requires confirmation)
 * 3. Active element allow list (auto-approves)
 * 4. Operation default permission (fallback)
 */
import { PermissionLevel, GatekeeperErrorCode, } from '../GatekeeperTypes.js';
import { getDefaultPermissionLevel, canOperationBeElevated, getOperationPolicy } from './OperationPolicies.js';
import { SecurityMonitor } from '../../../security/securityMonitor.js';
import { logger } from '../../../utils/logger.js';
import { MAX_GLOB_PATTERN_LENGTH } from '../../../utils/patternMatcher.js';
/**
 * Resolve the effective permission level for an operation
 * considering all active elements and their policies.
 *
 * @param operation - The operation to check
 * @param activeElements - Currently active elements with their metadata
 * @param targetElementType - Optional element type being operated on
 * @returns The resolved policy result
 */
export function resolveElementPolicy(operation, activeElements, targetElementType) {
    // Start with the default operation permission level
    let effectiveLevel = getDefaultPermissionLevel(operation);
    let sourceElement;
    let matchedPolicy;
    let scopeBlocked = false;
    // Issue #674: Track whether CONFIRM was set by an element policy (not just the route default)
    // allow can override the route default but NOT another element's confirm policy
    let confirmedByElement = false;
    // Track elements that wanted allow but were overridden by an element-set confirm/deny
    const conflictingElements = [];
    // Check each active element's policy in order
    for (const element of activeElements) {
        const policy = element.metadata.gatekeeper;
        if (!policy) {
            continue;
        }
        // 1. Check deny list first (highest priority)
        if (policy.deny?.includes(operation)) {
            return {
                permissionLevel: PermissionLevel.DENY,
                sourceElement: element.name,
                matchedPolicy: 'deny',
                conflictingElements: conflictingElements.length > 0 ? conflictingElements : undefined,
            };
        }
        // 2. Check scope restrictions
        if (targetElementType && policy.scopeRestrictions) {
            const { allowedTypes, blockedTypes } = policy.scopeRestrictions;
            // If allowedTypes is specified, target must be in the list
            if (allowedTypes && !allowedTypes.includes(targetElementType)) {
                scopeBlocked = true;
                sourceElement = element.name;
                matchedPolicy = 'scope_restriction';
                continue; // Check other elements, might have different policy
            }
            // If blockedTypes is specified, target must NOT be in the list
            if (blockedTypes?.includes(targetElementType)) {
                scopeBlocked = true;
                sourceElement = element.name;
                matchedPolicy = 'scope_restriction';
                continue;
            }
        }
        // 3. Check confirm list (requires confirmation)
        if (policy.confirm?.includes(operation)) {
            // Don't downgrade from DENY or CONFIRM_SINGLE_USE
            if (effectiveLevel !== PermissionLevel.DENY) {
                effectiveLevel = PermissionLevel.CONFIRM_SESSION;
                confirmedByElement = true;
                sourceElement = element.name;
                matchedPolicy = 'confirm';
            }
        }
        // 4. Check allow list (auto-approves)
        // Issue #674: allow CAN override the route default, but NOT another element's confirm policy.
        // Priority hierarchy: element deny > element confirm > element allow > route default
        if (policy.allow?.includes(operation)) {
            // Only elevate if the operation allows elevation
            if (canOperationBeElevated(operation)) {
                if (!confirmedByElement) {
                    // No element has confirmed this — safe to elevate (overrides route default)
                    effectiveLevel = PermissionLevel.AUTO_APPROVE;
                    sourceElement = element.name;
                    matchedPolicy = 'allow';
                }
                else {
                    // Another element's confirm policy takes priority over this allow
                    conflictingElements.push({ name: element.name, wantedLevel: PermissionLevel.AUTO_APPROVE });
                }
            }
        }
    }
    // If scope was blocked by all elements with restrictions
    if (scopeBlocked) {
        return {
            permissionLevel: PermissionLevel.DENY,
            sourceElement,
            matchedPolicy: 'scope_restriction',
            scopeBlocked: true,
        };
    }
    return {
        permissionLevel: effectiveLevel,
        sourceElement,
        matchedPolicy,
        conflictingElements: conflictingElements.length > 0 ? conflictingElements : undefined,
    };
}
/**
 * Create a Gatekeeper decision from element policy resolution.
 *
 * @param operation - The operation that was checked
 * @param result - The element policy resolution result
 * @param targetElementType - Optional element type being operated on
 * @returns A GatekeeperDecision object
 */
export function createDecisionFromPolicy(operation, result, targetElementType) {
    const { permissionLevel, sourceElement, scopeBlocked, conflictingElements } = result;
    // Handle DENY
    if (permissionLevel === PermissionLevel.DENY) {
        if (scopeBlocked) {
            return {
                allowed: false,
                permissionLevel,
                errorCode: GatekeeperErrorCode.SCOPE_RESTRICTION,
                reason: `Operation "${operation}" is not allowed on element type "${targetElementType}" due to scope restrictions in active element "${sourceElement}"`,
                suggestion: `Deactivate the element "${sourceElement}" or use a different element type`,
                policySource: 'element_policy',
            };
        }
        return {
            allowed: false,
            permissionLevel,
            errorCode: GatekeeperErrorCode.ELEMENT_POLICY_VIOLATION,
            reason: `Operation "${operation}" is blocked by active element "${sourceElement}"'s deny policy`,
            suggestion: `Deactivate the element "${sourceElement}" to proceed with this operation`,
            policySource: 'element_policy',
        };
    }
    // Handle confirmation required
    if (permissionLevel === PermissionLevel.CONFIRM_SESSION ||
        permissionLevel === PermissionLevel.CONFIRM_SINGLE_USE) {
        // Build human-readable rationale explaining WHY confirmation is needed
        const policy = getOperationPolicy(operation);
        const levelLabel = permissionLevel === PermissionLevel.CONFIRM_SINGLE_USE
            ? 'requires confirmation each time'
            : 'requires confirmation once per session';
        let rationale;
        if (sourceElement && result.matchedPolicy === 'confirm') {
            // Element policy elevated this operation's confirmation requirement
            const policyDetail = policy?.rationale ? ` ${policy.rationale}` : '';
            rationale = `Active element "${sourceElement}" policy requires confirmation for this operation.${policyDetail}`;
        }
        else if (policy?.rationale) {
            // Route-level default with documented rationale
            rationale = policy.rationale;
        }
        else {
            // Endpoint-level default, no specific rationale
            rationale = 'This operation modifies data and requires human approval before proceeding';
        }
        // Issue #674: Surface elements that wanted to auto-approve but were overridden
        const conflictNote = conflictingElements && conflictingElements.length > 0
            ? ` (Note: ${conflictingElements.map(e => `"${e.name}"`).join(', ')} would auto-approve this but ${conflictingElements.length === 1 ? 'is' : 'are'} overridden by the confirm policy.)`
            : '';
        return {
            allowed: false,
            permissionLevel,
            errorCode: GatekeeperErrorCode.CONFIRMATION_REQUIRED,
            reason: `Operation "${operation}" ${levelLabel}. ${rationale}${conflictNote}`,
            suggestion: 'Use the confirmation dialog to approve this operation',
            confirmationPending: true,
            policySource: sourceElement ? 'element_policy' : 'operation_default',
        };
    }
    // Handle AUTO_APPROVE
    return {
        allowed: true,
        permissionLevel,
        reason: sourceElement
            ? `Operation "${operation}" auto-approved by element "${sourceElement}"`
            : `Operation "${operation}" auto-approved by default policy`,
        policySource: sourceElement ? 'element_policy' : 'operation_default',
    };
}
/**
 * Parse and validate a Gatekeeper policy from element metadata.
 *
 * @param metadata - The element metadata to parse
 * @returns The parsed policy, or undefined if no policy is defined
 * @throws Error if the policy is malformed
 */
export function parseElementPolicy(metadata) {
    if (!metadata || typeof metadata !== 'object') {
        return undefined;
    }
    const meta = metadata;
    const gatekeeper = meta.gatekeeper;
    if (!gatekeeper) {
        return undefined;
    }
    if (typeof gatekeeper !== 'object' || gatekeeper === null) {
        throw new Error('Invalid gatekeeper policy: must be an object');
    }
    const policy = gatekeeper;
    // Validate and extract allow list
    const allow = validateStringArray(policy.allow, 'allow');
    const confirm = validateStringArray(policy.confirm, 'confirm');
    const deny = validateStringArray(policy.deny, 'deny');
    // Validate scope restrictions
    let scopeRestrictions;
    if (policy.scopeRestrictions) {
        if (typeof policy.scopeRestrictions !== 'object' || policy.scopeRestrictions === null) {
            throw new Error('Invalid gatekeeper policy: scopeRestrictions must be an object');
        }
        const sr = policy.scopeRestrictions;
        scopeRestrictions = {
            allowedTypes: validateStringArray(sr.allowedTypes, 'scopeRestrictions.allowedTypes'),
            blockedTypes: validateStringArray(sr.blockedTypes, 'scopeRestrictions.blockedTypes'),
        };
    }
    // Validate external restrictions (Issue #625 Phase 2)
    let externalRestrictions;
    if (policy.externalRestrictions) {
        if (typeof policy.externalRestrictions !== 'object' || policy.externalRestrictions === null) {
            throw new Error('Invalid gatekeeper policy: externalRestrictions must be an object');
        }
        const er = policy.externalRestrictions;
        if (!er.description || typeof er.description !== 'string') {
            throw new Error('Invalid gatekeeper policy: externalRestrictions.description is required and must be a non-empty string');
        }
        const denyPatterns = validateStringArray(er.denyPatterns, 'externalRestrictions.denyPatterns');
        const confirmPatterns = validateStringArray(er.confirmPatterns, 'externalRestrictions.confirmPatterns');
        const allowPatterns = validateStringArray(er.allowPatterns, 'externalRestrictions.allowPatterns');
        if (denyPatterns)
            validatePatternStrings(denyPatterns, 'externalRestrictions.denyPatterns');
        if (confirmPatterns)
            validatePatternStrings(confirmPatterns, 'externalRestrictions.confirmPatterns');
        if (allowPatterns)
            validatePatternStrings(allowPatterns, 'externalRestrictions.allowPatterns');
        // Validate approvalPolicy (Issue #625 Phase 3)
        let approvalPolicy = undefined;
        if (er.approvalPolicy !== undefined) {
            if (typeof er.approvalPolicy !== 'object' || er.approvalPolicy === null) {
                throw new Error('Invalid gatekeeper policy: externalRestrictions.approvalPolicy must be an object');
            }
            const ap = er.approvalPolicy;
            // Validate requireApproval
            let requireApproval;
            if (ap.requireApproval !== undefined) {
                if (!Array.isArray(ap.requireApproval)) {
                    throw new Error('Invalid gatekeeper policy: externalRestrictions.approvalPolicy.requireApproval must be an array');
                }
                const validLevels = new Set(['moderate', 'dangerous']);
                for (const level of ap.requireApproval) {
                    if (typeof level !== 'string' || !validLevels.has(level)) {
                        throw new Error(`Invalid gatekeeper policy: externalRestrictions.approvalPolicy.requireApproval contains invalid value "${level}". Must be "moderate" or "dangerous".`);
                    }
                }
                requireApproval = ap.requireApproval;
            }
            // Validate defaultScope
            let defaultScope;
            if (ap.defaultScope !== undefined) {
                if (ap.defaultScope !== 'single' && ap.defaultScope !== 'tool_session') {
                    throw new Error(`Invalid gatekeeper policy: externalRestrictions.approvalPolicy.defaultScope must be "single" or "tool_session", got "${ap.defaultScope}"`);
                }
                defaultScope = ap.defaultScope;
            }
            // Validate ttlSeconds
            let ttlSeconds;
            if (ap.ttlSeconds !== undefined) {
                if (typeof ap.ttlSeconds !== 'number' || !Number.isFinite(ap.ttlSeconds)) {
                    throw new Error('Invalid gatekeeper policy: externalRestrictions.approvalPolicy.ttlSeconds must be a number');
                }
                if (ap.ttlSeconds < 30 || ap.ttlSeconds > 3600) {
                    throw new Error(`Invalid gatekeeper policy: externalRestrictions.approvalPolicy.ttlSeconds must be between 30 and 3600, got ${ap.ttlSeconds}`);
                }
                ttlSeconds = ap.ttlSeconds;
            }
            approvalPolicy = { requireApproval, defaultScope, ttlSeconds };
        }
        externalRestrictions = {
            description: er.description,
            denyPatterns,
            confirmPatterns,
            allowPatterns,
            approvalPolicy,
        };
    }
    return {
        allow,
        confirm,
        deny,
        scopeRestrictions,
        externalRestrictions,
    };
}
/**
 * Validate authored gatekeeper input before save.
 *
 * Authoring-time validation is stricter than load-time sanitization: it should
 * reject misplaced policy blocks instead of silently saving an element that
 * later appears active but has non-enforceable external restrictions.
 */
export function getGatekeeperAuthoringErrors(record) {
    if (!record || typeof record !== 'object') {
        return [];
    }
    const errors = [];
    if (Object.hasOwn(record, 'externalRestrictions')) {
        errors.push('Invalid gatekeeper policy: externalRestrictions must be nested under gatekeeper.externalRestrictions');
    }
    if (record.gatekeeper !== undefined) {
        try {
            parseElementPolicy({ gatekeeper: record.gatekeeper });
        }
        catch (error) {
            errors.push(error instanceof Error ? error.message : String(error));
        }
    }
    return errors;
}
/**
 * Validate that a value is an array of strings.
 *
 * @param value - The value to validate
 * @param fieldName - The field name for error messages
 * @returns The validated array, or undefined
 */
function validateStringArray(value, fieldName) {
    if (value === undefined) {
        return undefined;
    }
    if (!Array.isArray(value)) {
        throw new Error(`Invalid gatekeeper policy: ${fieldName} must be an array`);
    }
    if (!value.every((item) => typeof item === 'string')) {
        throw new Error(`Invalid gatekeeper policy: ${fieldName} must contain only strings`);
    }
    return value;
}
/**
 * Validate pattern strings for length and non-emptiness.
 *
 * @param patterns - Array of pattern strings to validate
 * @param fieldName - Field name for error messages
 * @throws Error if any pattern is empty or exceeds MAX_GLOB_PATTERN_LENGTH
 */
function validatePatternStrings(patterns, fieldName) {
    for (const pattern of patterns) {
        if (!pattern) {
            throw new Error(`Invalid gatekeeper policy: ${fieldName} contains an empty pattern string`);
        }
        if (pattern.length > MAX_GLOB_PATTERN_LENGTH) {
            throw new Error(`Invalid gatekeeper policy: ${fieldName} pattern exceeds maximum length of ${MAX_GLOB_PATTERN_LENGTH} characters`);
        }
    }
}
/**
 * Known CLI tool names used in externalRestrictions patterns.
 * Patterns should start with one of these followed by ':' to be effective.
 */
const KNOWN_TOOL_PREFIXES = new Set([
    'Bash', 'Edit', 'Write', 'Read', 'Glob', 'Grep',
    'WebFetch', 'WebSearch', 'NotebookEdit',
]);
/**
 * Regex characters that have no meaning in glob syntax.
 * Users who include these likely intend regex but the matcher only supports * and ?.
 */
const REGEX_SYNTAX_PATTERN = /[|(){}[\]\\+^$]/;
/**
 * Analyze externalRestrictions patterns for common mistakes and suspicious syntax.
 *
 * Returns non-fatal warnings to help LLMs and users write effective patterns.
 * Does NOT throw — validation errors are handled by {@link validatePatternStrings}.
 *
 * Checks performed (Issue #1664):
 * - Missing tool prefix (pattern doesn't start with ToolName:)
 * - Overly broad patterns (bare `*` or `ToolName:*`)
 * - Regex syntax that won't work in glob matching
 * - Leading/trailing whitespace
 *
 * @param patterns - Array of pattern strings to analyze
 * @param fieldName - Field name for warning messages
 * @returns Array of warning messages (empty if no issues found)
 */
export function analyzePatternSyntax(patterns, fieldName) {
    const warnings = [];
    for (const pattern of patterns) {
        analyzeOnePattern(pattern, fieldName, warnings);
    }
    return warnings;
}
/**
 * Analyze a single pattern string and append any warnings found.
 * Extracted from {@link analyzePatternSyntax} to reduce cognitive complexity.
 */
function analyzeOnePattern(pattern, fieldName, warnings) {
    // Check leading/trailing whitespace
    if (pattern !== pattern.trim()) {
        warnings.push(`${fieldName} pattern '${pattern}' has leading/trailing whitespace — this may prevent expected matches`);
    }
    // Check for bare wildcard (matches everything)
    if (pattern === '*') {
        warnings.push(`${fieldName} pattern '*' matches everything — this is likely unintentional`);
        return;
    }
    // Check for tool prefix and related issues
    const colonIndex = pattern.indexOf(':');
    if (colonIndex === -1) {
        warnings.push(`${fieldName} pattern '${pattern}' has no tool prefix (e.g., 'Bash:', 'Edit:'). ` +
            `Patterns are matched against 'ToolName:input' strings and will not match without a prefix.`);
    }
    else {
        checkToolPrefix(pattern, colonIndex, fieldName, warnings);
    }
    // Check for regex syntax that won't work in glob
    const regexChars = new Set();
    for (const ch of pattern) {
        if (REGEX_SYNTAX_PATTERN.test(ch))
            regexChars.add(ch);
    }
    if (regexChars.size > 0) {
        warnings.push(`${fieldName} pattern '${pattern}' contains regex syntax '${[...regexChars].join(', ')}' — ` +
            `only glob wildcards (* and ?) are supported`);
    }
}
/**
 * Check tool prefix validity and broadness for a pattern that contains ':'.
 */
function checkToolPrefix(pattern, colonIndex, fieldName, warnings) {
    const prefix = pattern.slice(0, colonIndex);
    const afterColon = pattern.slice(colonIndex + 1);
    // Check for unknown tool prefix
    if (!KNOWN_TOOL_PREFIXES.has(prefix) && !prefix.startsWith('mcp_')) {
        warnings.push(`${fieldName} pattern '${pattern}' uses unknown tool prefix '${prefix}'. ` +
            `Known prefixes: ${[...KNOWN_TOOL_PREFIXES].join(', ')}. MCP tools use 'mcp_' prefix.`);
    }
    // Check for overly broad ToolName:* pattern
    if (afterColon === '*') {
        warnings.push(`${fieldName} pattern '${pattern}' matches ALL ${prefix} operations — verify this is intentional`);
    }
}
/**
 * Validate and sanitize a gatekeeper policy during element deserialization.
 *
 * Returns a validated {@link ElementGatekeeperPolicy} if the raw data is
 * structurally valid, or `undefined` if the data is absent, falsy, or
 * malformed. Malformed policies are logged as security events and stripped
 * so they never reach the enforcement pipeline.
 *
 * Use this at every deserialization boundary (manager `parseMetadata` /
 * `sanitizeMetadata` / `parseMemoryFile`) to guarantee that only well-formed
 * policies survive loading.
 *
 * @param rawPolicy - The raw gatekeeper value from parsed YAML/JSON
 * @param elementName - Element name for diagnostic logging
 * @param elementType - Element type for diagnostic logging
 * @returns Validated policy or undefined
 *
 * @since Issue #524 — Runtime validation for all element types
 */
/**
 * Gatekeeper infrastructure operations — two related sets with distinct purposes.
 *
 * UNGATABLE_OPERATIONS: Operations that must NEVER appear in element policy lists.
 * These are pure internal plumbing — gating them serves no security purpose and
 * breaks critical flows (verification, CLI approval, permission evaluation).
 * Stripped from ALL policy lists (allow, confirm, deny) during sanitization.
 *
 * GATEKEEPER_INFRA_OPERATIONS: Operations that skip Layer 2 (element policy
 * resolution) during primary enforcement. This is a SUPERSET of UNGATABLE_OPERATIONS
 * — it adds confirm_operation, which IS a valid policy target (deny = nuclear sandbox)
 * but must not be gated through the normal enforce() path (that creates the
 * cascading confirmation loop from Issue #758). confirm_operation's element policies
 * are enforced through a separate check in the confirm handler instead.
 *
 * Relationship: GATEKEEPER_INFRA_OPERATIONS = UNGATABLE_OPERATIONS + confirm_operation
 * This derivation is explicit in code to prevent the sets from drifting apart.
 */
const UNGATABLE_OPERATIONS = new Set([
    'verify_challenge',
    'release_deadlock',
    'approve_cli_permission',
    'permission_prompt',
]);
/** Derived from UNGATABLE_OPERATIONS + confirm_operation. See block comment above. */
const GATEKEEPER_INFRA_OPERATIONS = new Set([
    ...UNGATABLE_OPERATIONS,
    'confirm_operation',
]);
/**
 * Check if an operation is a gatekeeper infrastructure operation that should
 * skip element policy evaluation in the primary enforcement path.
 * Exported for use by MCPAQLHandler. Issue #758.
 */
export function isGatekeeperInfraOperation(operation) {
    return GATEKEEPER_INFRA_OPERATIONS.has(operation);
}
/**
 * Check if any active elements deny confirm_operation (nuclear sandbox).
 * Returns the denying element name if found, undefined otherwise.
 */
export function findConfirmDenyingElement(activeElements) {
    for (const element of activeElements) {
        const gatekeeper = element.metadata?.gatekeeper;
        const denyList = gatekeeper?.deny;
        if (Array.isArray(denyList) && denyList.includes('confirm_operation')) {
            return { name: element.name, type: element.type };
        }
    }
    return undefined;
}
/**
 * Check if any active elements have confirm_operation in their confirm list (advisory).
 * Returns the element names that request additional scrutiny.
 */
export function findConfirmAdvisoryElements(activeElements) {
    const advisories = [];
    for (const element of activeElements) {
        const gatekeeper = element.metadata?.gatekeeper;
        const confirmList = gatekeeper?.confirm;
        if (Array.isArray(confirmList) && confirmList.includes('confirm_operation')) {
            advisories.push({ name: element.name, type: element.type });
        }
    }
    return advisories;
}
/**
 * Strip ungatable operations from a policy list and log warnings.
 * Returns the filtered list (may be empty).
 *
 * Note: confirm_operation is NOT stripped from deny lists — it's a legitimate
 * sandbox mechanism. It IS stripped from allow/confirm lists since those are
 * handled through advisory messaging, not through enforce().
 */
function stripUngatable(operations, listName, elementName, elementType) {
    if (!operations?.length)
        return operations;
    // confirm_operation in deny lists = nuclear sandbox (preserve)
    // confirm_operation in confirm lists = advisory marker (preserve — inert in enforce path,
    //   read at runtime by findConfirmAdvisoryElements(). Safe because skipElementPolicies
    //   prevents resolveElementPolicy() from ever evaluating confirm_operation as an operation.)
    // confirm_operation in allow lists = redundant with route default AUTO_APPROVE (strip)
    const opsToStrip = listName === 'allow'
        ? new Set([...UNGATABLE_OPERATIONS, 'confirm_operation'])
        : UNGATABLE_OPERATIONS;
    const stripped = operations.filter(op => {
        if (opsToStrip.has(op)) {
            logger.warn(`[Gatekeeper] Stripped "${op}" from ${listName} list in ${elementType} "${elementName}" — ` +
                `gatekeeper infrastructure operations cannot be gated by element policies (Issue #758)`);
            return false;
        }
        return true;
    });
    return stripped.length > 0 ? stripped : undefined;
}
export function sanitizeGatekeeperPolicy(rawPolicy, elementName, elementType, diagnosticsTarget) {
    if (rawPolicy === undefined || rawPolicy === null) {
        return undefined;
    }
    try {
        // Wrap in a metadata envelope so parseElementPolicy can extract it
        const validated = parseElementPolicy({ gatekeeper: rawPolicy });
        clearGatekeeperDiagnostics(diagnosticsTarget ?? rawPolicy);
        if (validated) {
            // Issue #758: Strip gatekeeper infrastructure operations from element policies
            // to prevent cascading confirmation loops
            validated.allow = stripUngatable(validated.allow, 'allow', elementName, elementType);
            validated.confirm = stripUngatable(validated.confirm, 'confirm', elementName, elementType);
            validated.deny = stripUngatable(validated.deny, 'deny', elementName, elementType);
            logger.debug(`Loaded gatekeeper policy for ${elementType} "${elementName}"`, {
                allow: validated.allow?.length ?? 0,
                confirm: validated.confirm?.length ?? 0,
                deny: validated.deny?.length ?? 0,
                hasScopeRestrictions: !!validated.scopeRestrictions,
            });
        }
        return validated;
    }
    catch (error) {
        const message = formatGatekeeperDiagnosticMessage(error instanceof Error ? error.message : String(error));
        SecurityMonitor.logSecurityEvent({
            type: 'YAML_PARSING_WARNING',
            severity: 'MEDIUM',
            source: `${elementType}.deserialize`,
            details: `Malformed gatekeeper policy in "${elementName}" stripped during load: ${message}`,
        });
        logger.warn(`Stripped malformed gatekeeper policy from ${elementType} "${elementName}": ${message}`);
        attachGatekeeperDiagnostics(diagnosticsTarget ?? rawPolicy, message);
        return undefined;
    }
}
function formatGatekeeperDiagnosticMessage(message) {
    const normalized = message.trim();
    const guidance = getGatekeeperFixGuidance(normalized);
    return guidance ? `${normalized} Fix: ${guidance}` : normalized;
}
function getGatekeeperFixGuidance(message) {
    if (message.includes('externalRestrictions must be nested under gatekeeper.externalRestrictions')) {
        return 'Move externalRestrictions under gatekeeper.externalRestrictions and keep allowPatterns, confirmPatterns, and denyPatterns inside that nested object.';
    }
    if (message.includes('externalRestrictions.description is required')) {
        return 'Add gatekeeper.externalRestrictions.description with a short explanation, for example description: "Read-only shell policy".';
    }
    if (message.includes('must be an array') || message.includes('must contain only strings')) {
        return 'Use YAML arrays of strings, for example denyPatterns: ["Bash:rm *"] or allow: ["read_*"].';
    }
    if (message.includes('scopeRestrictions must be an object')) {
        return 'Use scopeRestrictions as an object, for example scopeRestrictions: { allowedTypes: ["persona", "skill"] }.';
    }
    if (message.includes('must be an object')) {
        return 'Use gatekeeper as an object, for example gatekeeper: { deny: ["delete_element"] }.';
    }
    return 'Compare the element against the gatekeeper examples from introspection or the security docs, then reactivate it after fixing the structure.';
}
export function attachGatekeeperDiagnostics(target, message) {
    if (!target || typeof target !== 'object') {
        return;
    }
    target.gatekeeperDiagnostics = {
        valid: false,
        enforceable: false,
        message,
    };
}
export function clearGatekeeperDiagnostics(target) {
    if (!target || typeof target !== 'object') {
        return;
    }
    delete target.gatekeeperDiagnostics;
}
export function getGatekeeperDiagnostics(target) {
    if (!target || typeof target !== 'object') {
        return undefined;
    }
    const diagnostics = target.gatekeeperDiagnostics;
    if (!diagnostics || typeof diagnostics !== 'object') {
        return undefined;
    }
    const record = diagnostics;
    if (record.valid === false && record.enforceable === false && typeof record.message === 'string' && record.message.trim() !== '') {
        return {
            valid: false,
            enforceable: false,
            message: record.message,
        };
    }
    return undefined;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRWxlbWVudFBvbGljaWVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2hhbmRsZXJzL21jcC1hcWwvcG9saWNpZXMvRWxlbWVudFBvbGljaWVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7R0FZRztBQUVILE9BQU8sRUFDTCxlQUFlLEVBSWYsbUJBQW1CLEdBQ3BCLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUFFLHlCQUF5QixFQUFFLHNCQUFzQixFQUFFLGtCQUFrQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDL0csT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNsRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQWtEM0U7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQ2xDLFNBQWlCLEVBQ2pCLGNBQStCLEVBQy9CLGlCQUEwQjtJQUUxQixvREFBb0Q7SUFDcEQsSUFBSSxjQUFjLEdBQUcseUJBQXlCLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDMUQsSUFBSSxhQUFpQyxDQUFDO0lBQ3RDLElBQUksYUFBbUQsQ0FBQztJQUN4RCxJQUFJLFlBQVksR0FBRyxLQUFLLENBQUM7SUFDekIsOEZBQThGO0lBQzlGLGdGQUFnRjtJQUNoRixJQUFJLGtCQUFrQixHQUFHLEtBQUssQ0FBQztJQUMvQixzRkFBc0Y7SUFDdEYsTUFBTSxtQkFBbUIsR0FBMEQsRUFBRSxDQUFDO0lBRXRGLDhDQUE4QztJQUM5QyxLQUFLLE1BQU0sT0FBTyxJQUFJLGNBQWMsRUFBRSxDQUFDO1FBQ3JDLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDO1FBQzNDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLFNBQVM7UUFDWCxDQUFDO1FBRUQsOENBQThDO1FBQzlDLElBQUksTUFBTSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztZQUNyQyxPQUFPO2dCQUNMLGVBQWUsRUFBRSxlQUFlLENBQUMsSUFBSTtnQkFDckMsYUFBYSxFQUFFLE9BQU8sQ0FBQyxJQUFJO2dCQUMzQixhQUFhLEVBQUUsTUFBTTtnQkFDckIsbUJBQW1CLEVBQUUsbUJBQW1CLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLFNBQVM7YUFDdEYsQ0FBQztRQUNKLENBQUM7UUFFRCw4QkFBOEI7UUFDOUIsSUFBSSxpQkFBaUIsSUFBSSxNQUFNLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUNsRCxNQUFNLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxHQUFHLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQztZQUVoRSwyREFBMkQ7WUFDM0QsSUFBSSxZQUFZLElBQUksQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztnQkFDOUQsWUFBWSxHQUFHLElBQUksQ0FBQztnQkFDcEIsYUFBYSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUM7Z0JBQzdCLGFBQWEsR0FBRyxtQkFBbUIsQ0FBQztnQkFDcEMsU0FBUyxDQUFDLG9EQUFvRDtZQUNoRSxDQUFDO1lBRUQsK0RBQStEO1lBQy9ELElBQUksWUFBWSxFQUFFLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7Z0JBQzlDLFlBQVksR0FBRyxJQUFJLENBQUM7Z0JBQ3BCLGFBQWEsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO2dCQUM3QixhQUFhLEdBQUcsbUJBQW1CLENBQUM7Z0JBQ3BDLFNBQVM7WUFDWCxDQUFDO1FBQ0gsQ0FBQztRQUVELGdEQUFnRDtRQUNoRCxJQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7WUFDeEMsa0RBQWtEO1lBQ2xELElBQUksY0FBYyxLQUFLLGVBQWUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDNUMsY0FBYyxHQUFHLGVBQWUsQ0FBQyxlQUFlLENBQUM7Z0JBQ2pELGtCQUFrQixHQUFHLElBQUksQ0FBQztnQkFDMUIsYUFBYSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUM7Z0JBQzdCLGFBQWEsR0FBRyxTQUFTLENBQUM7WUFDNUIsQ0FBQztRQUNILENBQUM7UUFFRCxzQ0FBc0M7UUFDdEMsOEZBQThGO1FBQzlGLHFGQUFxRjtRQUNyRixJQUFJLE1BQU0sQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7WUFDdEMsaURBQWlEO1lBQ2pELElBQUksc0JBQXNCLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztnQkFDdEMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7b0JBQ3hCLDRFQUE0RTtvQkFDNUUsY0FBYyxHQUFHLGVBQWUsQ0FBQyxZQUFZLENBQUM7b0JBQzlDLGFBQWEsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO29CQUM3QixhQUFhLEdBQUcsT0FBTyxDQUFDO2dCQUMxQixDQUFDO3FCQUFNLENBQUM7b0JBQ04sa0VBQWtFO29CQUNsRSxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUsZUFBZSxDQUFDLFlBQVksRUFBRSxDQUFDLENBQUM7Z0JBQzlGLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRCx5REFBeUQ7SUFDekQsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUNqQixPQUFPO1lBQ0wsZUFBZSxFQUFFLGVBQWUsQ0FBQyxJQUFJO1lBQ3JDLGFBQWE7WUFDYixhQUFhLEVBQUUsbUJBQW1CO1lBQ2xDLFlBQVksRUFBRSxJQUFJO1NBQ25CLENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTztRQUNMLGVBQWUsRUFBRSxjQUFjO1FBQy9CLGFBQWE7UUFDYixhQUFhO1FBQ2IsbUJBQW1CLEVBQUUsbUJBQW1CLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLFNBQVM7S0FDdEYsQ0FBQztBQUNKLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLHdCQUF3QixDQUN0QyxTQUFpQixFQUNqQixNQUEyQixFQUMzQixpQkFBMEI7SUFFMUIsTUFBTSxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLG1CQUFtQixFQUFFLEdBQUcsTUFBTSxDQUFDO0lBRXJGLGNBQWM7SUFDZCxJQUFJLGVBQWUsS0FBSyxlQUFlLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDN0MsSUFBSSxZQUFZLEVBQUUsQ0FBQztZQUNqQixPQUFPO2dCQUNMLE9BQU8sRUFBRSxLQUFLO2dCQUNkLGVBQWU7Z0JBQ2YsU0FBUyxFQUFFLG1CQUFtQixDQUFDLGlCQUFpQjtnQkFDaEQsTUFBTSxFQUFFLGNBQWMsU0FBUyxxQ0FBcUMsaUJBQWlCLGtEQUFrRCxhQUFhLEdBQUc7Z0JBQ3ZKLFVBQVUsRUFBRSwyQkFBMkIsYUFBYSxtQ0FBbUM7Z0JBQ3ZGLFlBQVksRUFBRSxnQkFBZ0I7YUFDL0IsQ0FBQztRQUNKLENBQUM7UUFFRCxPQUFPO1lBQ0wsT0FBTyxFQUFFLEtBQUs7WUFDZCxlQUFlO1lBQ2YsU0FBUyxFQUFFLG1CQUFtQixDQUFDLHdCQUF3QjtZQUN2RCxNQUFNLEVBQUUsY0FBYyxTQUFTLG1DQUFtQyxhQUFhLGlCQUFpQjtZQUNoRyxVQUFVLEVBQUUsMkJBQTJCLGFBQWEsa0NBQWtDO1lBQ3RGLFlBQVksRUFBRSxnQkFBZ0I7U0FDL0IsQ0FBQztJQUNKLENBQUM7SUFFRCwrQkFBK0I7SUFDL0IsSUFDRSxlQUFlLEtBQUssZUFBZSxDQUFDLGVBQWU7UUFDbkQsZUFBZSxLQUFLLGVBQWUsQ0FBQyxrQkFBa0IsRUFDdEQsQ0FBQztRQUNELHVFQUF1RTtRQUN2RSxNQUFNLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM3QyxNQUFNLFVBQVUsR0FBRyxlQUFlLEtBQUssZUFBZSxDQUFDLGtCQUFrQjtZQUN2RSxDQUFDLENBQUMsaUNBQWlDO1lBQ25DLENBQUMsQ0FBQyx3Q0FBd0MsQ0FBQztRQUU3QyxJQUFJLFNBQWlCLENBQUM7UUFDdEIsSUFBSSxhQUFhLElBQUksTUFBTSxDQUFDLGFBQWEsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN4RCxvRUFBb0U7WUFDcEUsTUFBTSxZQUFZLEdBQUcsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNyRSxTQUFTLEdBQUcsbUJBQW1CLGFBQWEscURBQXFELFlBQVksRUFBRSxDQUFDO1FBQ2xILENBQUM7YUFBTSxJQUFJLE1BQU0sRUFBRSxTQUFTLEVBQUUsQ0FBQztZQUM3QixnREFBZ0Q7WUFDaEQsU0FBUyxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFDL0IsQ0FBQzthQUFNLENBQUM7WUFDTixnREFBZ0Q7WUFDaEQsU0FBUyxHQUFHLDRFQUE0RSxDQUFDO1FBQzNGLENBQUM7UUFFRCwrRUFBK0U7UUFDL0UsTUFBTSxZQUFZLEdBQUcsbUJBQW1CLElBQUksbUJBQW1CLENBQUMsTUFBTSxHQUFHLENBQUM7WUFDeEUsQ0FBQyxDQUFDLFdBQVcsbUJBQW1CLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGdDQUFnQyxtQkFBbUIsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUsscUNBQXFDO1lBQ3ZMLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFFUCxPQUFPO1lBQ0wsT0FBTyxFQUFFLEtBQUs7WUFDZCxlQUFlO1lBQ2YsU0FBUyxFQUFFLG1CQUFtQixDQUFDLHFCQUFxQjtZQUNwRCxNQUFNLEVBQUUsY0FBYyxTQUFTLEtBQUssVUFBVSxLQUFLLFNBQVMsR0FBRyxZQUFZLEVBQUU7WUFDN0UsVUFBVSxFQUFFLHVEQUF1RDtZQUNuRSxtQkFBbUIsRUFBRSxJQUFJO1lBQ3pCLFlBQVksRUFBRSxhQUFhLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxtQkFBbUI7U0FDckUsQ0FBQztJQUNKLENBQUM7SUFFRCxzQkFBc0I7SUFDdEIsT0FBTztRQUNMLE9BQU8sRUFBRSxJQUFJO1FBQ2IsZUFBZTtRQUNmLE1BQU0sRUFBRSxhQUFhO1lBQ25CLENBQUMsQ0FBQyxjQUFjLFNBQVMsK0JBQStCLGFBQWEsR0FBRztZQUN4RSxDQUFDLENBQUMsY0FBYyxTQUFTLG1DQUFtQztRQUM5RCxZQUFZLEVBQUUsYUFBYSxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsbUJBQW1CO0tBQ3JFLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUNoQyxRQUFpQjtJQUVqQixJQUFJLENBQUMsUUFBUSxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzlDLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxNQUFNLElBQUksR0FBRyxRQUFtQyxDQUFDO0lBQ2pELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFFbkMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2hCLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxJQUFJLE9BQU8sVUFBVSxLQUFLLFFBQVEsSUFBSSxVQUFVLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDMUQsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBRyxVQUFxQyxDQUFDO0lBRXJELGtDQUFrQztJQUNsQyxNQUFNLEtBQUssR0FBRyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pELE1BQU0sT0FBTyxHQUFHLG1CQUFtQixDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDL0QsTUFBTSxJQUFJLEdBQUcsbUJBQW1CLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztJQUV0RCw4QkFBOEI7SUFDOUIsSUFBSSxpQkFBK0QsQ0FBQztJQUNwRSxJQUFJLE1BQU0sQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBQzdCLElBQUksT0FBTyxNQUFNLENBQUMsaUJBQWlCLEtBQUssUUFBUSxJQUFJLE1BQU0sQ0FBQyxpQkFBaUIsS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUN0RixNQUFNLElBQUksS0FBSyxDQUFDLGdFQUFnRSxDQUFDLENBQUM7UUFDcEYsQ0FBQztRQUNELE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyxpQkFBNEMsQ0FBQztRQUMvRCxpQkFBaUIsR0FBRztZQUNsQixZQUFZLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxDQUFDLFlBQVksRUFBRSxnQ0FBZ0MsQ0FBQztZQUNwRixZQUFZLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxDQUFDLFlBQVksRUFBRSxnQ0FBZ0MsQ0FBQztTQUNyRixDQUFDO0lBQ0osQ0FBQztJQUVELHNEQUFzRDtJQUN0RCxJQUFJLG9CQUFxRSxDQUFDO0lBQzFFLElBQUksTUFBTSxDQUFDLG9CQUFvQixFQUFFLENBQUM7UUFDaEMsSUFBSSxPQUFPLE1BQU0sQ0FBQyxvQkFBb0IsS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLG9CQUFvQixLQUFLLElBQUksRUFBRSxDQUFDO1lBQzVGLE1BQU0sSUFBSSxLQUFLLENBQUMsbUVBQW1FLENBQUMsQ0FBQztRQUN2RixDQUFDO1FBQ0QsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDLG9CQUErQyxDQUFDO1FBQ2xFLElBQUksQ0FBQyxFQUFFLENBQUMsV0FBVyxJQUFJLE9BQU8sRUFBRSxDQUFDLFdBQVcsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUMxRCxNQUFNLElBQUksS0FBSyxDQUFDLHdHQUF3RyxDQUFDLENBQUM7UUFDNUgsQ0FBQztRQUNELE1BQU0sWUFBWSxHQUFHLG1CQUFtQixDQUFDLEVBQUUsQ0FBQyxZQUFZLEVBQUUsbUNBQW1DLENBQUMsQ0FBQztRQUMvRixNQUFNLGVBQWUsR0FBRyxtQkFBbUIsQ0FBQyxFQUFFLENBQUMsZUFBZSxFQUFFLHNDQUFzQyxDQUFDLENBQUM7UUFDeEcsTUFBTSxhQUFhLEdBQUcsbUJBQW1CLENBQUMsRUFBRSxDQUFDLGFBQWEsRUFBRSxvQ0FBb0MsQ0FBQyxDQUFDO1FBQ2xHLElBQUksWUFBWTtZQUFFLHNCQUFzQixDQUFDLFlBQVksRUFBRSxtQ0FBbUMsQ0FBQyxDQUFDO1FBQzVGLElBQUksZUFBZTtZQUFFLHNCQUFzQixDQUFDLGVBQWUsRUFBRSxzQ0FBc0MsQ0FBQyxDQUFDO1FBQ3JHLElBQUksYUFBYTtZQUFFLHNCQUFzQixDQUFDLGFBQWEsRUFBRSxvQ0FBb0MsQ0FBQyxDQUFDO1FBQy9GLCtDQUErQztRQUMvQyxJQUFJLGNBQWMsR0FBa0MsU0FBUyxDQUFDO1FBQzlELElBQUksRUFBRSxDQUFDLGNBQWMsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUNwQyxJQUFJLE9BQU8sRUFBRSxDQUFDLGNBQWMsS0FBSyxRQUFRLElBQUksRUFBRSxDQUFDLGNBQWMsS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDeEUsTUFBTSxJQUFJLEtBQUssQ0FBQyxrRkFBa0YsQ0FBQyxDQUFDO1lBQ3RHLENBQUM7WUFDRCxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsY0FBeUMsQ0FBQztZQUV4RCwyQkFBMkI7WUFDM0IsSUFBSSxlQUF5RCxDQUFDO1lBQzlELElBQUksRUFBRSxDQUFDLGVBQWUsS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDckMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7b0JBQ3ZDLE1BQU0sSUFBSSxLQUFLLENBQUMsaUdBQWlHLENBQUMsQ0FBQztnQkFDckgsQ0FBQztnQkFDRCxNQUFNLFdBQVcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDO2dCQUN2RCxLQUFLLE1BQU0sS0FBSyxJQUFJLEVBQUUsQ0FBQyxlQUFlLEVBQUUsQ0FBQztvQkFDdkMsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7d0JBQ3pELE1BQU0sSUFBSSxLQUFLLENBQUMsMEdBQTBHLEtBQUssdUNBQXVDLENBQUMsQ0FBQztvQkFDMUssQ0FBQztnQkFDSCxDQUFDO2dCQUNELGVBQWUsR0FBRyxFQUFFLENBQUMsZUFBK0MsQ0FBQztZQUN2RSxDQUFDO1lBRUQsd0JBQXdCO1lBQ3hCLElBQUksWUFBbUQsQ0FBQztZQUN4RCxJQUFJLEVBQUUsQ0FBQyxZQUFZLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ2xDLElBQUksRUFBRSxDQUFDLFlBQVksS0FBSyxRQUFRLElBQUksRUFBRSxDQUFDLFlBQVksS0FBSyxjQUFjLEVBQUUsQ0FBQztvQkFDdkUsTUFBTSxJQUFJLEtBQUssQ0FBQyx3SEFBd0gsRUFBRSxDQUFDLFlBQVksR0FBRyxDQUFDLENBQUM7Z0JBQzlKLENBQUM7Z0JBQ0QsWUFBWSxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUM7WUFDakMsQ0FBQztZQUVELHNCQUFzQjtZQUN0QixJQUFJLFVBQThCLENBQUM7WUFDbkMsSUFBSSxFQUFFLENBQUMsVUFBVSxLQUFLLFNBQVMsRUFBRSxDQUFDO2dCQUNoQyxJQUFJLE9BQU8sRUFBRSxDQUFDLFVBQVUsS0FBSyxRQUFRLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO29CQUN6RSxNQUFNLElBQUksS0FBSyxDQUFDLDRGQUE0RixDQUFDLENBQUM7Z0JBQ2hILENBQUM7Z0JBQ0QsSUFBSSxFQUFFLENBQUMsVUFBVSxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUMsVUFBVSxHQUFHLElBQUksRUFBRSxDQUFDO29CQUMvQyxNQUFNLElBQUksS0FBSyxDQUFDLDhHQUE4RyxFQUFFLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQztnQkFDakosQ0FBQztnQkFDRCxVQUFVLEdBQUcsRUFBRSxDQUFDLFVBQVUsQ0FBQztZQUM3QixDQUFDO1lBRUQsY0FBYyxHQUFHLEVBQUUsZUFBZSxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsQ0FBQztRQUNqRSxDQUFDO1FBRUQsb0JBQW9CLEdBQUc7WUFDckIsV0FBVyxFQUFFLEVBQUUsQ0FBQyxXQUFxQjtZQUNyQyxZQUFZO1lBQ1osZUFBZTtZQUNmLGFBQWE7WUFDYixjQUFjO1NBQ2YsQ0FBQztJQUNKLENBQUM7SUFFRCxPQUFPO1FBQ0wsS0FBSztRQUNMLE9BQU87UUFDUCxJQUFJO1FBQ0osaUJBQWlCO1FBQ2pCLG9CQUFvQjtLQUNyQixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSw0QkFBNEIsQ0FDMUMsTUFBMkM7SUFFM0MsSUFBSSxDQUFDLE1BQU0sSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUMxQyxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFNUIsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxzQkFBc0IsQ0FBQyxFQUFFLENBQUM7UUFDbEQsTUFBTSxDQUFDLElBQUksQ0FDVCxzR0FBc0csQ0FDdkcsQ0FBQztJQUNKLENBQUM7SUFFRCxJQUFJLE1BQU0sQ0FBQyxVQUFVLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDcEMsSUFBSSxDQUFDO1lBQ0gsa0JBQWtCLENBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDeEQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQ3RFLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILFNBQVMsbUJBQW1CLENBQzFCLEtBQWMsRUFDZCxTQUFpQjtJQUVqQixJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN4QixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUMxQixNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixTQUFTLG1CQUFtQixDQUFDLENBQUM7SUFDOUUsQ0FBQztJQUVELElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxFQUFrQixFQUFFLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNyRSxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixTQUFTLDRCQUE0QixDQUFDLENBQUM7SUFDdkYsQ0FBQztJQUVELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILFNBQVMsc0JBQXNCLENBQzdCLFFBQWtCLEVBQ2xCLFNBQWlCO0lBRWpCLEtBQUssTUFBTSxPQUFPLElBQUksUUFBUSxFQUFFLENBQUM7UUFDL0IsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsU0FBUyxtQ0FBbUMsQ0FBQyxDQUFDO1FBQzlGLENBQUM7UUFDRCxJQUFJLE9BQU8sQ0FBQyxNQUFNLEdBQUcsdUJBQXVCLEVBQUUsQ0FBQztZQUM3QyxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixTQUFTLHNDQUFzQyx1QkFBdUIsYUFBYSxDQUFDLENBQUM7UUFDckksQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLEdBQUcsQ0FBQztJQUNsQyxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU07SUFDL0MsVUFBVSxFQUFFLFdBQVcsRUFBRSxjQUFjO0NBQ3hDLENBQUMsQ0FBQztBQUVIOzs7R0FHRztBQUNILE1BQU0sb0JBQW9CLEdBQUcsaUJBQWlCLENBQUM7QUFFL0M7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUNsQyxRQUFrQixFQUNsQixTQUFpQjtJQUVqQixNQUFNLFFBQVEsR0FBYSxFQUFFLENBQUM7SUFDOUIsS0FBSyxNQUFNLE9BQU8sSUFBSSxRQUFRLEVBQUUsQ0FBQztRQUMvQixpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFDRCxPQUFPLFFBQVEsQ0FBQztBQUNsQixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBUyxpQkFBaUIsQ0FDeEIsT0FBZSxFQUNmLFNBQWlCLEVBQ2pCLFFBQWtCO0lBRWxCLG9DQUFvQztJQUNwQyxJQUFJLE9BQU8sS0FBSyxPQUFPLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQztRQUMvQixRQUFRLENBQUMsSUFBSSxDQUNYLEdBQUcsU0FBUyxhQUFhLE9BQU8sdUVBQXVFLENBQ3hHLENBQUM7SUFDSixDQUFDO0lBRUQsK0NBQStDO0lBQy9DLElBQUksT0FBTyxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ3BCLFFBQVEsQ0FBQyxJQUFJLENBQ1gsR0FBRyxTQUFTLGdFQUFnRSxDQUM3RSxDQUFDO1FBQ0YsT0FBTztJQUNULENBQUM7SUFFRCwyQ0FBMkM7SUFDM0MsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN4QyxJQUFJLFVBQVUsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3RCLFFBQVEsQ0FBQyxJQUFJLENBQ1gsR0FBRyxTQUFTLGFBQWEsT0FBTyxpREFBaUQ7WUFDakYsNEZBQTRGLENBQzdGLENBQUM7SUFDSixDQUFDO1NBQU0sQ0FBQztRQUNOLGVBQWUsQ0FBQyxPQUFPLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBRUQsaURBQWlEO0lBQ2pELE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDckMsS0FBSyxNQUFNLEVBQUUsSUFBSSxPQUFPLEVBQUUsQ0FBQztRQUN6QixJQUFJLG9CQUFvQixDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFBRSxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFDRCxJQUFJLFVBQVUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDeEIsUUFBUSxDQUFDLElBQUksQ0FDWCxHQUFHLFNBQVMsYUFBYSxPQUFPLDRCQUE0QixDQUFDLEdBQUcsVUFBVSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQzVGLDZDQUE2QyxDQUM5QyxDQUFDO0lBQ0osQ0FBQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMsZUFBZSxDQUN0QixPQUFlLEVBQ2YsVUFBa0IsRUFDbEIsU0FBaUIsRUFDakIsUUFBa0I7SUFFbEIsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDNUMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFFakQsZ0NBQWdDO0lBQ2hDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFDbkUsUUFBUSxDQUFDLElBQUksQ0FDWCxHQUFHLFNBQVMsYUFBYSxPQUFPLCtCQUErQixNQUFNLEtBQUs7WUFDMUUsbUJBQW1CLENBQUMsR0FBRyxtQkFBbUIsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZ0NBQWdDLENBQ3ZGLENBQUM7SUFDSixDQUFDO0lBRUQsNENBQTRDO0lBQzVDLElBQUksVUFBVSxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLFFBQVEsQ0FBQyxJQUFJLENBQ1gsR0FBRyxTQUFTLGFBQWEsT0FBTyxpQkFBaUIsTUFBTSwwQ0FBMEMsQ0FDbEcsQ0FBQztJQUNKLENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNIOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUNILE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDbkMsa0JBQWtCO0lBQ2xCLGtCQUFrQjtJQUNsQix3QkFBd0I7SUFDeEIsbUJBQW1CO0NBQ3BCLENBQUMsQ0FBQztBQUVILHNGQUFzRjtBQUN0RixNQUFNLDJCQUEyQixHQUFHLElBQUksR0FBRyxDQUFDO0lBQzFDLEdBQUcsb0JBQW9CO0lBQ3ZCLG1CQUFtQjtDQUNwQixDQUFDLENBQUM7QUFFSDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLDBCQUEwQixDQUFDLFNBQWlCO0lBQzFELE9BQU8sMkJBQTJCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBQ3BELENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUseUJBQXlCLENBQ3ZDLGNBQXdGO0lBRXhGLEtBQUssTUFBTSxPQUFPLElBQUksY0FBYyxFQUFFLENBQUM7UUFDckMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLFFBQVEsRUFBRSxVQUFpRCxDQUFDO1FBQ3ZGLE1BQU0sUUFBUSxHQUFHLFVBQVUsRUFBRSxJQUFJLENBQUM7UUFDbEMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsbUJBQW1CLENBQUMsRUFBRSxDQUFDO1lBQ3RFLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3BELENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSwyQkFBMkIsQ0FDekMsY0FBd0Y7SUFFeEYsTUFBTSxVQUFVLEdBQTBDLEVBQUUsQ0FBQztJQUM3RCxLQUFLLE1BQU0sT0FBTyxJQUFJLGNBQWMsRUFBRSxDQUFDO1FBQ3JDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxRQUFRLEVBQUUsVUFBaUQsQ0FBQztRQUN2RixNQUFNLFdBQVcsR0FBRyxVQUFVLEVBQUUsT0FBTyxDQUFDO1FBQ3hDLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxXQUFXLENBQUMsUUFBUSxDQUFDLG1CQUFtQixDQUFDLEVBQUUsQ0FBQztZQUM1RSxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzlELENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxVQUFVLENBQUM7QUFDcEIsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxTQUFTLGNBQWMsQ0FDckIsVUFBZ0MsRUFDaEMsUUFBZ0IsRUFDaEIsV0FBbUIsRUFDbkIsV0FBbUI7SUFFbkIsSUFBSSxDQUFDLFVBQVUsRUFBRSxNQUFNO1FBQUUsT0FBTyxVQUFVLENBQUM7SUFFM0MsK0RBQStEO0lBQy9ELDBGQUEwRjtJQUMxRix1RkFBdUY7SUFDdkYsNkZBQTZGO0lBQzdGLHVGQUF1RjtJQUN2RixNQUFNLFVBQVUsR0FBRyxRQUFRLEtBQUssT0FBTztRQUNyQyxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLG9CQUFvQixFQUFFLG1CQUFtQixDQUFDLENBQUM7UUFDekQsQ0FBQyxDQUFDLG9CQUFvQixDQUFDO0lBRXpCLE1BQU0sUUFBUSxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLEVBQUU7UUFDdEMsSUFBSSxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDdkIsTUFBTSxDQUFDLElBQUksQ0FDVCwwQkFBMEIsRUFBRSxVQUFVLFFBQVEsWUFBWSxXQUFXLEtBQUssV0FBVyxNQUFNO2dCQUMzRix1RkFBdUYsQ0FDeEYsQ0FBQztZQUNGLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQyxDQUFDLENBQUM7SUFDSCxPQUFPLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUNwRCxDQUFDO0FBRUQsTUFBTSxVQUFVLHdCQUF3QixDQUN0QyxTQUFrQixFQUNsQixXQUFtQixFQUNuQixXQUFtQixFQUNuQixpQkFBMkM7SUFFM0MsSUFBSSxTQUFTLEtBQUssU0FBUyxJQUFJLFNBQVMsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUNsRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsbUVBQW1FO1FBQ25FLE1BQU0sU0FBUyxHQUFHLGtCQUFrQixDQUFDLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7UUFDaEUsMEJBQTBCLENBQUMsaUJBQWlCLElBQUksU0FBUyxDQUFDLENBQUM7UUFDM0QsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLCtFQUErRTtZQUMvRSwwQ0FBMEM7WUFDMUMsU0FBUyxDQUFDLEtBQUssR0FBRyxjQUFjLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsV0FBVyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBQ3JGLFNBQVMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxXQUFXLENBQUMsQ0FBQztZQUMzRixTQUFTLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsV0FBVyxDQUFDLENBQUM7WUFFbEYsTUFBTSxDQUFDLEtBQUssQ0FBQyxnQ0FBZ0MsV0FBVyxLQUFLLFdBQVcsR0FBRyxFQUFFO2dCQUMzRSxLQUFLLEVBQUUsU0FBUyxDQUFDLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztnQkFDbkMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxJQUFJLENBQUM7Z0JBQ3ZDLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxFQUFFLE1BQU0sSUFBSSxDQUFDO2dCQUNqQyxvQkFBb0IsRUFBRSxDQUFDLENBQUMsU0FBUyxDQUFDLGlCQUFpQjthQUNwRCxDQUFDLENBQUM7UUFDTCxDQUFDO1FBQ0QsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixNQUFNLE9BQU8sR0FBRyxpQ0FBaUMsQ0FBQyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUMxRyxlQUFlLENBQUMsZ0JBQWdCLENBQUM7WUFDL0IsSUFBSSxFQUFFLHNCQUFzQjtZQUM1QixRQUFRLEVBQUUsUUFBUTtZQUNsQixNQUFNLEVBQUUsR0FBRyxXQUFXLGNBQWM7WUFDcEMsT0FBTyxFQUFFLG1DQUFtQyxXQUFXLDJCQUEyQixPQUFPLEVBQUU7U0FDNUYsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxDQUFDLElBQUksQ0FBQyw2Q0FBNkMsV0FBVyxLQUFLLFdBQVcsTUFBTSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ3JHLDJCQUEyQixDQUFDLGlCQUFpQixJQUFJLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNyRSxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0FBQ0gsQ0FBQztBQUVELFNBQVMsaUNBQWlDLENBQUMsT0FBZTtJQUN4RCxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDbEMsTUFBTSxRQUFRLEdBQUcsd0JBQXdCLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDdEQsT0FBTyxRQUFRLENBQUMsQ0FBQyxDQUFDLEdBQUcsVUFBVSxTQUFTLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUM7QUFDbEUsQ0FBQztBQUVELFNBQVMsd0JBQXdCLENBQUMsT0FBZTtJQUMvQyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsMkVBQTJFLENBQUMsRUFBRSxDQUFDO1FBQ2xHLE9BQU8sc0pBQXNKLENBQUM7SUFDaEssQ0FBQztJQUVELElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyw4Q0FBOEMsQ0FBQyxFQUFFLENBQUM7UUFDckUsT0FBTyw4SEFBOEgsQ0FBQztJQUN4SSxDQUFDO0lBRUQsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUFDLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQywyQkFBMkIsQ0FBQyxFQUFFLENBQUM7UUFDMUYsT0FBTywyRkFBMkYsQ0FBQztJQUNyRyxDQUFDO0lBRUQsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLHFDQUFxQyxDQUFDLEVBQUUsQ0FBQztRQUM1RCxPQUFPLDRHQUE0RyxDQUFDO0lBQ3RILENBQUM7SUFFRCxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsbUJBQW1CLENBQUMsRUFBRSxDQUFDO1FBQzFDLE9BQU8sb0ZBQW9GLENBQUM7SUFDOUYsQ0FBQztJQUVELE9BQU8sNklBQTZJLENBQUM7QUFDdkosQ0FBQztBQUVELE1BQU0sVUFBVSwyQkFBMkIsQ0FBQyxNQUFlLEVBQUUsT0FBZTtJQUMxRSxJQUFJLENBQUMsTUFBTSxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzFDLE9BQU87SUFDVCxDQUFDO0lBRUEsTUFBa0MsQ0FBQyxxQkFBcUIsR0FBRztRQUMxRCxLQUFLLEVBQUUsS0FBSztRQUNaLFdBQVcsRUFBRSxLQUFLO1FBQ2xCLE9BQU87S0FDOEIsQ0FBQztBQUMxQyxDQUFDO0FBRUQsTUFBTSxVQUFVLDBCQUEwQixDQUFDLE1BQWU7SUFDeEQsSUFBSSxDQUFDLE1BQU0sSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUMxQyxPQUFPO0lBQ1QsQ0FBQztJQUVELE9BQVEsTUFBa0MsQ0FBQyxxQkFBcUIsQ0FBQztBQUNuRSxDQUFDO0FBRUQsTUFBTSxVQUFVLHdCQUF3QixDQUFDLE1BQWU7SUFDdEQsSUFBSSxDQUFDLE1BQU0sSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUMxQyxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQsTUFBTSxXQUFXLEdBQUksTUFBa0MsQ0FBQyxxQkFBcUIsQ0FBQztJQUM5RSxJQUFJLENBQUMsV0FBVyxJQUFJLE9BQU8sV0FBVyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ3BELE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBRyxXQUFzQyxDQUFDO0lBQ3RELElBQUksTUFBTSxDQUFDLEtBQUssS0FBSyxLQUFLLElBQUksTUFBTSxDQUFDLFdBQVcsS0FBSyxLQUFLLElBQUksT0FBTyxNQUFNLENBQUMsT0FBTyxLQUFLLFFBQVEsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ2pJLE9BQU87WUFDTCxLQUFLLEVBQUUsS0FBSztZQUNaLFdBQVcsRUFBRSxLQUFLO1lBQ2xCLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTztTQUN4QixDQUFDO0lBQ0osQ0FBQztJQUVELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEVsZW1lbnQgUG9saWNpZXNcbiAqXG4gKiBIYW5kbGVzIGVsZW1lbnQtYmFzZWQgYWNjZXNzIGNvbnRyb2wgZm9yIHRoZSBHYXRla2VlcGVyLlxuICogQWxsb3dzIEFOWSBEb2xsaG91c2VNQ1AgZWxlbWVudCB0byBkZWZpbmUgcG9saWNpZXMgaW4gaXRzIG1ldGFkYXRhXG4gKiB0aGF0IG92ZXJyaWRlIG9yIHJlc3RyaWN0IHRoZSBkZWZhdWx0IG9wZXJhdGlvbiBwb2xpY2llcy5cbiAqXG4gKiBQb2xpY3kgUmVzb2x1dGlvbiBPcmRlcjpcbiAqIDEuIEFjdGl2ZSBlbGVtZW50IGRlbnkgbGlzdCAoaGlnaGVzdCBwcmlvcml0eSAtIGJsb2NrcyBvcGVyYXRpb24pXG4gKiAyLiBBY3RpdmUgZWxlbWVudCBjb25maXJtIGxpc3QgKHJlcXVpcmVzIGNvbmZpcm1hdGlvbilcbiAqIDMuIEFjdGl2ZSBlbGVtZW50IGFsbG93IGxpc3QgKGF1dG8tYXBwcm92ZXMpXG4gKiA0LiBPcGVyYXRpb24gZGVmYXVsdCBwZXJtaXNzaW9uIChmYWxsYmFjaylcbiAqL1xuXG5pbXBvcnQge1xuICBQZXJtaXNzaW9uTGV2ZWwsXG4gIHR5cGUgRWxlbWVudEdhdGVrZWVwZXJQb2xpY3ksXG4gIHR5cGUgQ2xpQXBwcm92YWxQb2xpY3ksXG4gIHR5cGUgR2F0ZWtlZXBlckRlY2lzaW9uLFxuICBHYXRla2VlcGVyRXJyb3JDb2RlLFxufSBmcm9tICcuLi9HYXRla2VlcGVyVHlwZXMuanMnO1xuaW1wb3J0IHsgZ2V0RGVmYXVsdFBlcm1pc3Npb25MZXZlbCwgY2FuT3BlcmF0aW9uQmVFbGV2YXRlZCwgZ2V0T3BlcmF0aW9uUG9saWN5IH0gZnJvbSAnLi9PcGVyYXRpb25Qb2xpY2llcy5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IE1BWF9HTE9CX1BBVFRFUk5fTEVOR1RIIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGF0dGVybk1hdGNoZXIuanMnO1xuXG4vKipcbiAqIE1ldGFkYXRhIHN0cnVjdHVyZSBmb3IgZWxlbWVudHMgd2l0aCBHYXRla2VlcGVyIHBvbGljaWVzLlxuICogVGhlICdnYXRla2VlcGVyJyBmaWVsZCBpcyBvcHRpb25hbCBhbmQgY29udGFpbnMgcG9saWN5IGRlZmluaXRpb25zLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEVsZW1lbnRNZXRhZGF0YVdpdGhQb2xpY3kge1xuICBuYW1lOiBzdHJpbmc7XG4gIGRlc2NyaXB0aW9uPzogc3RyaW5nO1xuICBnYXRla2VlcGVyPzogRWxlbWVudEdhdGVrZWVwZXJQb2xpY3k7XG4gIFtrZXk6IHN0cmluZ106IHVua25vd247XG59XG5cbi8qKlxuICogQWN0aXZlIGVsZW1lbnQgZm9yIHBvbGljeSBldmFsdWF0aW9uLlxuICogUmVwcmVzZW50cyBhbiBlbGVtZW50IGN1cnJlbnRseSBhY3RpdmUgaW4gdGhlIHNlc3Npb24uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQWN0aXZlRWxlbWVudCB7XG4gIHR5cGU6IHN0cmluZztcbiAgbmFtZTogc3RyaW5nO1xuICBtZXRhZGF0YTogRWxlbWVudE1ldGFkYXRhV2l0aFBvbGljeTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHYXRla2VlcGVyUG9saWN5RGlhZ25vc3RpY3Mge1xuICB2YWxpZDogZmFsc2U7XG4gIGVuZm9yY2VhYmxlOiBmYWxzZTtcbiAgbWVzc2FnZTogc3RyaW5nO1xufVxuXG4vKipcbiAqIFJlc3VsdCBvZiBlbGVtZW50IHBvbGljeSByZXNvbHV0aW9uLlxuICogQ29udGFpbnMgdGhlIGVmZmVjdGl2ZSBwZXJtaXNzaW9uIGxldmVsIGFuZCBwb2xpY3kgc291cmNlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEVsZW1lbnRQb2xpY3lSZXN1bHQge1xuICAvKiogRWZmZWN0aXZlIHBlcm1pc3Npb24gbGV2ZWwgYWZ0ZXIgZWxlbWVudCBwb2xpY3kgYXBwbGljYXRpb24gKi9cbiAgcGVybWlzc2lvbkxldmVsOiBQZXJtaXNzaW9uTGV2ZWw7XG4gIC8qKiBXaGljaCBlbGVtZW50J3MgcG9saWN5IGRldGVybWluZWQgdGhpcyByZXN1bHQgKi9cbiAgc291cmNlRWxlbWVudD86IHN0cmluZztcbiAgLyoqIFRoZSBzcGVjaWZpYyBwb2xpY3kgZmllbGQgdGhhdCBtYXRjaGVkIChhbGxvdy9jb25maXJtL2RlbnkpICovXG4gIG1hdGNoZWRQb2xpY3k/OiAnYWxsb3cnIHwgJ2NvbmZpcm0nIHwgJ2RlbnknIHwgJ3Njb3BlX3Jlc3RyaWN0aW9uJztcbiAgLyoqIFdoZXRoZXIgdGhlIG9wZXJhdGlvbiB3YXMgYmxvY2tlZCBieSBzY29wZSByZXN0cmljdGlvbnMgKi9cbiAgc2NvcGVCbG9ja2VkPzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIEVsZW1lbnRzIHRoYXQgd2FudGVkIHRvIGF1dG8tYXBwcm92ZSB0aGlzIG9wZXJhdGlvbiBidXQgd2VyZSBvdmVycmlkZGVuXG4gICAqIGJ5IGEgaGlnaGVyLXByaW9yaXR5IGNvbmZpcm0gb3IgZGVueSBwb2xpY3kgZnJvbSBhbm90aGVyIGVsZW1lbnQuXG4gICAqIElzc3VlICM2NzQ6IGFsbG93IGNhbm5vdCBvdmVycmlkZSBjb25maXJtLlxuICAgKi9cbiAgY29uZmxpY3RpbmdFbGVtZW50cz86IEFycmF5PHsgbmFtZTogc3RyaW5nOyB3YW50ZWRMZXZlbDogUGVybWlzc2lvbkxldmVsIH0+O1xufVxuXG4vKipcbiAqIFJlc29sdmUgdGhlIGVmZmVjdGl2ZSBwZXJtaXNzaW9uIGxldmVsIGZvciBhbiBvcGVyYXRpb25cbiAqIGNvbnNpZGVyaW5nIGFsbCBhY3RpdmUgZWxlbWVudHMgYW5kIHRoZWlyIHBvbGljaWVzLlxuICpcbiAqIEBwYXJhbSBvcGVyYXRpb24gLSBUaGUgb3BlcmF0aW9uIHRvIGNoZWNrXG4gKiBAcGFyYW0gYWN0aXZlRWxlbWVudHMgLSBDdXJyZW50bHkgYWN0aXZlIGVsZW1lbnRzIHdpdGggdGhlaXIgbWV0YWRhdGFcbiAqIEBwYXJhbSB0YXJnZXRFbGVtZW50VHlwZSAtIE9wdGlvbmFsIGVsZW1lbnQgdHlwZSBiZWluZyBvcGVyYXRlZCBvblxuICogQHJldHVybnMgVGhlIHJlc29sdmVkIHBvbGljeSByZXN1bHRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVFbGVtZW50UG9saWN5KFxuICBvcGVyYXRpb246IHN0cmluZyxcbiAgYWN0aXZlRWxlbWVudHM6IEFjdGl2ZUVsZW1lbnRbXSxcbiAgdGFyZ2V0RWxlbWVudFR5cGU/OiBzdHJpbmdcbik6IEVsZW1lbnRQb2xpY3lSZXN1bHQge1xuICAvLyBTdGFydCB3aXRoIHRoZSBkZWZhdWx0IG9wZXJhdGlvbiBwZXJtaXNzaW9uIGxldmVsXG4gIGxldCBlZmZlY3RpdmVMZXZlbCA9IGdldERlZmF1bHRQZXJtaXNzaW9uTGV2ZWwob3BlcmF0aW9uKTtcbiAgbGV0IHNvdXJjZUVsZW1lbnQ6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgbGV0IG1hdGNoZWRQb2xpY3k6IEVsZW1lbnRQb2xpY3lSZXN1bHRbJ21hdGNoZWRQb2xpY3knXTtcbiAgbGV0IHNjb3BlQmxvY2tlZCA9IGZhbHNlO1xuICAvLyBJc3N1ZSAjNjc0OiBUcmFjayB3aGV0aGVyIENPTkZJUk0gd2FzIHNldCBieSBhbiBlbGVtZW50IHBvbGljeSAobm90IGp1c3QgdGhlIHJvdXRlIGRlZmF1bHQpXG4gIC8vIGFsbG93IGNhbiBvdmVycmlkZSB0aGUgcm91dGUgZGVmYXVsdCBidXQgTk9UIGFub3RoZXIgZWxlbWVudCdzIGNvbmZpcm0gcG9saWN5XG4gIGxldCBjb25maXJtZWRCeUVsZW1lbnQgPSBmYWxzZTtcbiAgLy8gVHJhY2sgZWxlbWVudHMgdGhhdCB3YW50ZWQgYWxsb3cgYnV0IHdlcmUgb3ZlcnJpZGRlbiBieSBhbiBlbGVtZW50LXNldCBjb25maXJtL2RlbnlcbiAgY29uc3QgY29uZmxpY3RpbmdFbGVtZW50czogQXJyYXk8eyBuYW1lOiBzdHJpbmc7IHdhbnRlZExldmVsOiBQZXJtaXNzaW9uTGV2ZWwgfT4gPSBbXTtcblxuICAvLyBDaGVjayBlYWNoIGFjdGl2ZSBlbGVtZW50J3MgcG9saWN5IGluIG9yZGVyXG4gIGZvciAoY29uc3QgZWxlbWVudCBvZiBhY3RpdmVFbGVtZW50cykge1xuICAgIGNvbnN0IHBvbGljeSA9IGVsZW1lbnQubWV0YWRhdGEuZ2F0ZWtlZXBlcjtcbiAgICBpZiAoIXBvbGljeSkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gMS4gQ2hlY2sgZGVueSBsaXN0IGZpcnN0IChoaWdoZXN0IHByaW9yaXR5KVxuICAgIGlmIChwb2xpY3kuZGVueT8uaW5jbHVkZXMob3BlcmF0aW9uKSkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcGVybWlzc2lvbkxldmVsOiBQZXJtaXNzaW9uTGV2ZWwuREVOWSxcbiAgICAgICAgc291cmNlRWxlbWVudDogZWxlbWVudC5uYW1lLFxuICAgICAgICBtYXRjaGVkUG9saWN5OiAnZGVueScsXG4gICAgICAgIGNvbmZsaWN0aW5nRWxlbWVudHM6IGNvbmZsaWN0aW5nRWxlbWVudHMubGVuZ3RoID4gMCA/IGNvbmZsaWN0aW5nRWxlbWVudHMgOiB1bmRlZmluZWQsXG4gICAgICB9O1xuICAgIH1cblxuICAgIC8vIDIuIENoZWNrIHNjb3BlIHJlc3RyaWN0aW9uc1xuICAgIGlmICh0YXJnZXRFbGVtZW50VHlwZSAmJiBwb2xpY3kuc2NvcGVSZXN0cmljdGlvbnMpIHtcbiAgICAgIGNvbnN0IHsgYWxsb3dlZFR5cGVzLCBibG9ja2VkVHlwZXMgfSA9IHBvbGljeS5zY29wZVJlc3RyaWN0aW9ucztcblxuICAgICAgLy8gSWYgYWxsb3dlZFR5cGVzIGlzIHNwZWNpZmllZCwgdGFyZ2V0IG11c3QgYmUgaW4gdGhlIGxpc3RcbiAgICAgIGlmIChhbGxvd2VkVHlwZXMgJiYgIWFsbG93ZWRUeXBlcy5pbmNsdWRlcyh0YXJnZXRFbGVtZW50VHlwZSkpIHtcbiAgICAgICAgc2NvcGVCbG9ja2VkID0gdHJ1ZTtcbiAgICAgICAgc291cmNlRWxlbWVudCA9IGVsZW1lbnQubmFtZTtcbiAgICAgICAgbWF0Y2hlZFBvbGljeSA9ICdzY29wZV9yZXN0cmljdGlvbic7XG4gICAgICAgIGNvbnRpbnVlOyAvLyBDaGVjayBvdGhlciBlbGVtZW50cywgbWlnaHQgaGF2ZSBkaWZmZXJlbnQgcG9saWN5XG4gICAgICB9XG5cbiAgICAgIC8vIElmIGJsb2NrZWRUeXBlcyBpcyBzcGVjaWZpZWQsIHRhcmdldCBtdXN0IE5PVCBiZSBpbiB0aGUgbGlzdFxuICAgICAgaWYgKGJsb2NrZWRUeXBlcz8uaW5jbHVkZXModGFyZ2V0RWxlbWVudFR5cGUpKSB7XG4gICAgICAgIHNjb3BlQmxvY2tlZCA9IHRydWU7XG4gICAgICAgIHNvdXJjZUVsZW1lbnQgPSBlbGVtZW50Lm5hbWU7XG4gICAgICAgIG1hdGNoZWRQb2xpY3kgPSAnc2NvcGVfcmVzdHJpY3Rpb24nO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyAzLiBDaGVjayBjb25maXJtIGxpc3QgKHJlcXVpcmVzIGNvbmZpcm1hdGlvbilcbiAgICBpZiAocG9saWN5LmNvbmZpcm0/LmluY2x1ZGVzKG9wZXJhdGlvbikpIHtcbiAgICAgIC8vIERvbid0IGRvd25ncmFkZSBmcm9tIERFTlkgb3IgQ09ORklSTV9TSU5HTEVfVVNFXG4gICAgICBpZiAoZWZmZWN0aXZlTGV2ZWwgIT09IFBlcm1pc3Npb25MZXZlbC5ERU5ZKSB7XG4gICAgICAgIGVmZmVjdGl2ZUxldmVsID0gUGVybWlzc2lvbkxldmVsLkNPTkZJUk1fU0VTU0lPTjtcbiAgICAgICAgY29uZmlybWVkQnlFbGVtZW50ID0gdHJ1ZTtcbiAgICAgICAgc291cmNlRWxlbWVudCA9IGVsZW1lbnQubmFtZTtcbiAgICAgICAgbWF0Y2hlZFBvbGljeSA9ICdjb25maXJtJztcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyA0LiBDaGVjayBhbGxvdyBsaXN0IChhdXRvLWFwcHJvdmVzKVxuICAgIC8vIElzc3VlICM2NzQ6IGFsbG93IENBTiBvdmVycmlkZSB0aGUgcm91dGUgZGVmYXVsdCwgYnV0IE5PVCBhbm90aGVyIGVsZW1lbnQncyBjb25maXJtIHBvbGljeS5cbiAgICAvLyBQcmlvcml0eSBoaWVyYXJjaHk6IGVsZW1lbnQgZGVueSA+IGVsZW1lbnQgY29uZmlybSA+IGVsZW1lbnQgYWxsb3cgPiByb3V0ZSBkZWZhdWx0XG4gICAgaWYgKHBvbGljeS5hbGxvdz8uaW5jbHVkZXMob3BlcmF0aW9uKSkge1xuICAgICAgLy8gT25seSBlbGV2YXRlIGlmIHRoZSBvcGVyYXRpb24gYWxsb3dzIGVsZXZhdGlvblxuICAgICAgaWYgKGNhbk9wZXJhdGlvbkJlRWxldmF0ZWQob3BlcmF0aW9uKSkge1xuICAgICAgICBpZiAoIWNvbmZpcm1lZEJ5RWxlbWVudCkge1xuICAgICAgICAgIC8vIE5vIGVsZW1lbnQgaGFzIGNvbmZpcm1lZCB0aGlzIOKAlCBzYWZlIHRvIGVsZXZhdGUgKG92ZXJyaWRlcyByb3V0ZSBkZWZhdWx0KVxuICAgICAgICAgIGVmZmVjdGl2ZUxldmVsID0gUGVybWlzc2lvbkxldmVsLkFVVE9fQVBQUk9WRTtcbiAgICAgICAgICBzb3VyY2VFbGVtZW50ID0gZWxlbWVudC5uYW1lO1xuICAgICAgICAgIG1hdGNoZWRQb2xpY3kgPSAnYWxsb3cnO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEFub3RoZXIgZWxlbWVudCdzIGNvbmZpcm0gcG9saWN5IHRha2VzIHByaW9yaXR5IG92ZXIgdGhpcyBhbGxvd1xuICAgICAgICAgIGNvbmZsaWN0aW5nRWxlbWVudHMucHVzaCh7IG5hbWU6IGVsZW1lbnQubmFtZSwgd2FudGVkTGV2ZWw6IFBlcm1pc3Npb25MZXZlbC5BVVRPX0FQUFJPVkUgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBJZiBzY29wZSB3YXMgYmxvY2tlZCBieSBhbGwgZWxlbWVudHMgd2l0aCByZXN0cmljdGlvbnNcbiAgaWYgKHNjb3BlQmxvY2tlZCkge1xuICAgIHJldHVybiB7XG4gICAgICBwZXJtaXNzaW9uTGV2ZWw6IFBlcm1pc3Npb25MZXZlbC5ERU5ZLFxuICAgICAgc291cmNlRWxlbWVudCxcbiAgICAgIG1hdGNoZWRQb2xpY3k6ICdzY29wZV9yZXN0cmljdGlvbicsXG4gICAgICBzY29wZUJsb2NrZWQ6IHRydWUsXG4gICAgfTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgcGVybWlzc2lvbkxldmVsOiBlZmZlY3RpdmVMZXZlbCxcbiAgICBzb3VyY2VFbGVtZW50LFxuICAgIG1hdGNoZWRQb2xpY3ksXG4gICAgY29uZmxpY3RpbmdFbGVtZW50czogY29uZmxpY3RpbmdFbGVtZW50cy5sZW5ndGggPiAwID8gY29uZmxpY3RpbmdFbGVtZW50cyA6IHVuZGVmaW5lZCxcbiAgfTtcbn1cblxuLyoqXG4gKiBDcmVhdGUgYSBHYXRla2VlcGVyIGRlY2lzaW9uIGZyb20gZWxlbWVudCBwb2xpY3kgcmVzb2x1dGlvbi5cbiAqXG4gKiBAcGFyYW0gb3BlcmF0aW9uIC0gVGhlIG9wZXJhdGlvbiB0aGF0IHdhcyBjaGVja2VkXG4gKiBAcGFyYW0gcmVzdWx0IC0gVGhlIGVsZW1lbnQgcG9saWN5IHJlc29sdXRpb24gcmVzdWx0XG4gKiBAcGFyYW0gdGFyZ2V0RWxlbWVudFR5cGUgLSBPcHRpb25hbCBlbGVtZW50IHR5cGUgYmVpbmcgb3BlcmF0ZWQgb25cbiAqIEByZXR1cm5zIEEgR2F0ZWtlZXBlckRlY2lzaW9uIG9iamVjdFxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRGVjaXNpb25Gcm9tUG9saWN5KFxuICBvcGVyYXRpb246IHN0cmluZyxcbiAgcmVzdWx0OiBFbGVtZW50UG9saWN5UmVzdWx0LFxuICB0YXJnZXRFbGVtZW50VHlwZT86IHN0cmluZ1xuKTogR2F0ZWtlZXBlckRlY2lzaW9uIHtcbiAgY29uc3QgeyBwZXJtaXNzaW9uTGV2ZWwsIHNvdXJjZUVsZW1lbnQsIHNjb3BlQmxvY2tlZCwgY29uZmxpY3RpbmdFbGVtZW50cyB9ID0gcmVzdWx0O1xuXG4gIC8vIEhhbmRsZSBERU5ZXG4gIGlmIChwZXJtaXNzaW9uTGV2ZWwgPT09IFBlcm1pc3Npb25MZXZlbC5ERU5ZKSB7XG4gICAgaWYgKHNjb3BlQmxvY2tlZCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgYWxsb3dlZDogZmFsc2UsXG4gICAgICAgIHBlcm1pc3Npb25MZXZlbCxcbiAgICAgICAgZXJyb3JDb2RlOiBHYXRla2VlcGVyRXJyb3JDb2RlLlNDT1BFX1JFU1RSSUNUSU9OLFxuICAgICAgICByZWFzb246IGBPcGVyYXRpb24gXCIke29wZXJhdGlvbn1cIiBpcyBub3QgYWxsb3dlZCBvbiBlbGVtZW50IHR5cGUgXCIke3RhcmdldEVsZW1lbnRUeXBlfVwiIGR1ZSB0byBzY29wZSByZXN0cmljdGlvbnMgaW4gYWN0aXZlIGVsZW1lbnQgXCIke3NvdXJjZUVsZW1lbnR9XCJgLFxuICAgICAgICBzdWdnZXN0aW9uOiBgRGVhY3RpdmF0ZSB0aGUgZWxlbWVudCBcIiR7c291cmNlRWxlbWVudH1cIiBvciB1c2UgYSBkaWZmZXJlbnQgZWxlbWVudCB0eXBlYCxcbiAgICAgICAgcG9saWN5U291cmNlOiAnZWxlbWVudF9wb2xpY3knLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgYWxsb3dlZDogZmFsc2UsXG4gICAgICBwZXJtaXNzaW9uTGV2ZWwsXG4gICAgICBlcnJvckNvZGU6IEdhdGVrZWVwZXJFcnJvckNvZGUuRUxFTUVOVF9QT0xJQ1lfVklPTEFUSU9OLFxuICAgICAgcmVhc29uOiBgT3BlcmF0aW9uIFwiJHtvcGVyYXRpb259XCIgaXMgYmxvY2tlZCBieSBhY3RpdmUgZWxlbWVudCBcIiR7c291cmNlRWxlbWVudH1cIidzIGRlbnkgcG9saWN5YCxcbiAgICAgIHN1Z2dlc3Rpb246IGBEZWFjdGl2YXRlIHRoZSBlbGVtZW50IFwiJHtzb3VyY2VFbGVtZW50fVwiIHRvIHByb2NlZWQgd2l0aCB0aGlzIG9wZXJhdGlvbmAsXG4gICAgICBwb2xpY3lTb3VyY2U6ICdlbGVtZW50X3BvbGljeScsXG4gICAgfTtcbiAgfVxuXG4gIC8vIEhhbmRsZSBjb25maXJtYXRpb24gcmVxdWlyZWRcbiAgaWYgKFxuICAgIHBlcm1pc3Npb25MZXZlbCA9PT0gUGVybWlzc2lvbkxldmVsLkNPTkZJUk1fU0VTU0lPTiB8fFxuICAgIHBlcm1pc3Npb25MZXZlbCA9PT0gUGVybWlzc2lvbkxldmVsLkNPTkZJUk1fU0lOR0xFX1VTRVxuICApIHtcbiAgICAvLyBCdWlsZCBodW1hbi1yZWFkYWJsZSByYXRpb25hbGUgZXhwbGFpbmluZyBXSFkgY29uZmlybWF0aW9uIGlzIG5lZWRlZFxuICAgIGNvbnN0IHBvbGljeSA9IGdldE9wZXJhdGlvblBvbGljeShvcGVyYXRpb24pO1xuICAgIGNvbnN0IGxldmVsTGFiZWwgPSBwZXJtaXNzaW9uTGV2ZWwgPT09IFBlcm1pc3Npb25MZXZlbC5DT05GSVJNX1NJTkdMRV9VU0VcbiAgICAgID8gJ3JlcXVpcmVzIGNvbmZpcm1hdGlvbiBlYWNoIHRpbWUnXG4gICAgICA6ICdyZXF1aXJlcyBjb25maXJtYXRpb24gb25jZSBwZXIgc2Vzc2lvbic7XG5cbiAgICBsZXQgcmF0aW9uYWxlOiBzdHJpbmc7XG4gICAgaWYgKHNvdXJjZUVsZW1lbnQgJiYgcmVzdWx0Lm1hdGNoZWRQb2xpY3kgPT09ICdjb25maXJtJykge1xuICAgICAgLy8gRWxlbWVudCBwb2xpY3kgZWxldmF0ZWQgdGhpcyBvcGVyYXRpb24ncyBjb25maXJtYXRpb24gcmVxdWlyZW1lbnRcbiAgICAgIGNvbnN0IHBvbGljeURldGFpbCA9IHBvbGljeT8ucmF0aW9uYWxlID8gYCAke3BvbGljeS5yYXRpb25hbGV9YCA6ICcnO1xuICAgICAgcmF0aW9uYWxlID0gYEFjdGl2ZSBlbGVtZW50IFwiJHtzb3VyY2VFbGVtZW50fVwiIHBvbGljeSByZXF1aXJlcyBjb25maXJtYXRpb24gZm9yIHRoaXMgb3BlcmF0aW9uLiR7cG9saWN5RGV0YWlsfWA7XG4gICAgfSBlbHNlIGlmIChwb2xpY3k/LnJhdGlvbmFsZSkge1xuICAgICAgLy8gUm91dGUtbGV2ZWwgZGVmYXVsdCB3aXRoIGRvY3VtZW50ZWQgcmF0aW9uYWxlXG4gICAgICByYXRpb25hbGUgPSBwb2xpY3kucmF0aW9uYWxlO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBFbmRwb2ludC1sZXZlbCBkZWZhdWx0LCBubyBzcGVjaWZpYyByYXRpb25hbGVcbiAgICAgIHJhdGlvbmFsZSA9ICdUaGlzIG9wZXJhdGlvbiBtb2RpZmllcyBkYXRhIGFuZCByZXF1aXJlcyBodW1hbiBhcHByb3ZhbCBiZWZvcmUgcHJvY2VlZGluZyc7XG4gICAgfVxuXG4gICAgLy8gSXNzdWUgIzY3NDogU3VyZmFjZSBlbGVtZW50cyB0aGF0IHdhbnRlZCB0byBhdXRvLWFwcHJvdmUgYnV0IHdlcmUgb3ZlcnJpZGRlblxuICAgIGNvbnN0IGNvbmZsaWN0Tm90ZSA9IGNvbmZsaWN0aW5nRWxlbWVudHMgJiYgY29uZmxpY3RpbmdFbGVtZW50cy5sZW5ndGggPiAwXG4gICAgICA/IGAgKE5vdGU6ICR7Y29uZmxpY3RpbmdFbGVtZW50cy5tYXAoZSA9PiBgXCIke2UubmFtZX1cImApLmpvaW4oJywgJyl9IHdvdWxkIGF1dG8tYXBwcm92ZSB0aGlzIGJ1dCAke2NvbmZsaWN0aW5nRWxlbWVudHMubGVuZ3RoID09PSAxID8gJ2lzJyA6ICdhcmUnfSBvdmVycmlkZGVuIGJ5IHRoZSBjb25maXJtIHBvbGljeS4pYFxuICAgICAgOiAnJztcblxuICAgIHJldHVybiB7XG4gICAgICBhbGxvd2VkOiBmYWxzZSxcbiAgICAgIHBlcm1pc3Npb25MZXZlbCxcbiAgICAgIGVycm9yQ29kZTogR2F0ZWtlZXBlckVycm9yQ29kZS5DT05GSVJNQVRJT05fUkVRVUlSRUQsXG4gICAgICByZWFzb246IGBPcGVyYXRpb24gXCIke29wZXJhdGlvbn1cIiAke2xldmVsTGFiZWx9LiAke3JhdGlvbmFsZX0ke2NvbmZsaWN0Tm90ZX1gLFxuICAgICAgc3VnZ2VzdGlvbjogJ1VzZSB0aGUgY29uZmlybWF0aW9uIGRpYWxvZyB0byBhcHByb3ZlIHRoaXMgb3BlcmF0aW9uJyxcbiAgICAgIGNvbmZpcm1hdGlvblBlbmRpbmc6IHRydWUsXG4gICAgICBwb2xpY3lTb3VyY2U6IHNvdXJjZUVsZW1lbnQgPyAnZWxlbWVudF9wb2xpY3knIDogJ29wZXJhdGlvbl9kZWZhdWx0JyxcbiAgICB9O1xuICB9XG5cbiAgLy8gSGFuZGxlIEFVVE9fQVBQUk9WRVxuICByZXR1cm4ge1xuICAgIGFsbG93ZWQ6IHRydWUsXG4gICAgcGVybWlzc2lvbkxldmVsLFxuICAgIHJlYXNvbjogc291cmNlRWxlbWVudFxuICAgICAgPyBgT3BlcmF0aW9uIFwiJHtvcGVyYXRpb259XCIgYXV0by1hcHByb3ZlZCBieSBlbGVtZW50IFwiJHtzb3VyY2VFbGVtZW50fVwiYFxuICAgICAgOiBgT3BlcmF0aW9uIFwiJHtvcGVyYXRpb259XCIgYXV0by1hcHByb3ZlZCBieSBkZWZhdWx0IHBvbGljeWAsXG4gICAgcG9saWN5U291cmNlOiBzb3VyY2VFbGVtZW50ID8gJ2VsZW1lbnRfcG9saWN5JyA6ICdvcGVyYXRpb25fZGVmYXVsdCcsXG4gIH07XG59XG5cbi8qKlxuICogUGFyc2UgYW5kIHZhbGlkYXRlIGEgR2F0ZWtlZXBlciBwb2xpY3kgZnJvbSBlbGVtZW50IG1ldGFkYXRhLlxuICpcbiAqIEBwYXJhbSBtZXRhZGF0YSAtIFRoZSBlbGVtZW50IG1ldGFkYXRhIHRvIHBhcnNlXG4gKiBAcmV0dXJucyBUaGUgcGFyc2VkIHBvbGljeSwgb3IgdW5kZWZpbmVkIGlmIG5vIHBvbGljeSBpcyBkZWZpbmVkXG4gKiBAdGhyb3dzIEVycm9yIGlmIHRoZSBwb2xpY3kgaXMgbWFsZm9ybWVkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZUVsZW1lbnRQb2xpY3koXG4gIG1ldGFkYXRhOiB1bmtub3duXG4pOiBFbGVtZW50R2F0ZWtlZXBlclBvbGljeSB8IHVuZGVmaW5lZCB7XG4gIGlmICghbWV0YWRhdGEgfHwgdHlwZW9mIG1ldGFkYXRhICE9PSAnb2JqZWN0Jykge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBjb25zdCBtZXRhID0gbWV0YWRhdGEgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gIGNvbnN0IGdhdGVrZWVwZXIgPSBtZXRhLmdhdGVrZWVwZXI7XG5cbiAgaWYgKCFnYXRla2VlcGVyKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIGlmICh0eXBlb2YgZ2F0ZWtlZXBlciAhPT0gJ29iamVjdCcgfHwgZ2F0ZWtlZXBlciA9PT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBnYXRla2VlcGVyIHBvbGljeTogbXVzdCBiZSBhbiBvYmplY3QnKTtcbiAgfVxuXG4gIGNvbnN0IHBvbGljeSA9IGdhdGVrZWVwZXIgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG5cbiAgLy8gVmFsaWRhdGUgYW5kIGV4dHJhY3QgYWxsb3cgbGlzdFxuICBjb25zdCBhbGxvdyA9IHZhbGlkYXRlU3RyaW5nQXJyYXkocG9saWN5LmFsbG93LCAnYWxsb3cnKTtcbiAgY29uc3QgY29uZmlybSA9IHZhbGlkYXRlU3RyaW5nQXJyYXkocG9saWN5LmNvbmZpcm0sICdjb25maXJtJyk7XG4gIGNvbnN0IGRlbnkgPSB2YWxpZGF0ZVN0cmluZ0FycmF5KHBvbGljeS5kZW55LCAnZGVueScpO1xuXG4gIC8vIFZhbGlkYXRlIHNjb3BlIHJlc3RyaWN0aW9uc1xuICBsZXQgc2NvcGVSZXN0cmljdGlvbnM6IEVsZW1lbnRHYXRla2VlcGVyUG9saWN5WydzY29wZVJlc3RyaWN0aW9ucyddO1xuICBpZiAocG9saWN5LnNjb3BlUmVzdHJpY3Rpb25zKSB7XG4gICAgaWYgKHR5cGVvZiBwb2xpY3kuc2NvcGVSZXN0cmljdGlvbnMgIT09ICdvYmplY3QnIHx8IHBvbGljeS5zY29wZVJlc3RyaWN0aW9ucyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGdhdGVrZWVwZXIgcG9saWN5OiBzY29wZVJlc3RyaWN0aW9ucyBtdXN0IGJlIGFuIG9iamVjdCcpO1xuICAgIH1cbiAgICBjb25zdCBzciA9IHBvbGljeS5zY29wZVJlc3RyaWN0aW9ucyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBzY29wZVJlc3RyaWN0aW9ucyA9IHtcbiAgICAgIGFsbG93ZWRUeXBlczogdmFsaWRhdGVTdHJpbmdBcnJheShzci5hbGxvd2VkVHlwZXMsICdzY29wZVJlc3RyaWN0aW9ucy5hbGxvd2VkVHlwZXMnKSxcbiAgICAgIGJsb2NrZWRUeXBlczogdmFsaWRhdGVTdHJpbmdBcnJheShzci5ibG9ja2VkVHlwZXMsICdzY29wZVJlc3RyaWN0aW9ucy5ibG9ja2VkVHlwZXMnKSxcbiAgICB9O1xuICB9XG5cbiAgLy8gVmFsaWRhdGUgZXh0ZXJuYWwgcmVzdHJpY3Rpb25zIChJc3N1ZSAjNjI1IFBoYXNlIDIpXG4gIGxldCBleHRlcm5hbFJlc3RyaWN0aW9uczogRWxlbWVudEdhdGVrZWVwZXJQb2xpY3lbJ2V4dGVybmFsUmVzdHJpY3Rpb25zJ107XG4gIGlmIChwb2xpY3kuZXh0ZXJuYWxSZXN0cmljdGlvbnMpIHtcbiAgICBpZiAodHlwZW9mIHBvbGljeS5leHRlcm5hbFJlc3RyaWN0aW9ucyAhPT0gJ29iamVjdCcgfHwgcG9saWN5LmV4dGVybmFsUmVzdHJpY3Rpb25zID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZ2F0ZWtlZXBlciBwb2xpY3k6IGV4dGVybmFsUmVzdHJpY3Rpb25zIG11c3QgYmUgYW4gb2JqZWN0Jyk7XG4gICAgfVxuICAgIGNvbnN0IGVyID0gcG9saWN5LmV4dGVybmFsUmVzdHJpY3Rpb25zIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIGlmICghZXIuZGVzY3JpcHRpb24gfHwgdHlwZW9mIGVyLmRlc2NyaXB0aW9uICE9PSAnc3RyaW5nJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGdhdGVrZWVwZXIgcG9saWN5OiBleHRlcm5hbFJlc3RyaWN0aW9ucy5kZXNjcmlwdGlvbiBpcyByZXF1aXJlZCBhbmQgbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcnKTtcbiAgICB9XG4gICAgY29uc3QgZGVueVBhdHRlcm5zID0gdmFsaWRhdGVTdHJpbmdBcnJheShlci5kZW55UGF0dGVybnMsICdleHRlcm5hbFJlc3RyaWN0aW9ucy5kZW55UGF0dGVybnMnKTtcbiAgICBjb25zdCBjb25maXJtUGF0dGVybnMgPSB2YWxpZGF0ZVN0cmluZ0FycmF5KGVyLmNvbmZpcm1QYXR0ZXJucywgJ2V4dGVybmFsUmVzdHJpY3Rpb25zLmNvbmZpcm1QYXR0ZXJucycpO1xuICAgIGNvbnN0IGFsbG93UGF0dGVybnMgPSB2YWxpZGF0ZVN0cmluZ0FycmF5KGVyLmFsbG93UGF0dGVybnMsICdleHRlcm5hbFJlc3RyaWN0aW9ucy5hbGxvd1BhdHRlcm5zJyk7XG4gICAgaWYgKGRlbnlQYXR0ZXJucykgdmFsaWRhdGVQYXR0ZXJuU3RyaW5ncyhkZW55UGF0dGVybnMsICdleHRlcm5hbFJlc3RyaWN0aW9ucy5kZW55UGF0dGVybnMnKTtcbiAgICBpZiAoY29uZmlybVBhdHRlcm5zKSB2YWxpZGF0ZVBhdHRlcm5TdHJpbmdzKGNvbmZpcm1QYXR0ZXJucywgJ2V4dGVybmFsUmVzdHJpY3Rpb25zLmNvbmZpcm1QYXR0ZXJucycpO1xuICAgIGlmIChhbGxvd1BhdHRlcm5zKSB2YWxpZGF0ZVBhdHRlcm5TdHJpbmdzKGFsbG93UGF0dGVybnMsICdleHRlcm5hbFJlc3RyaWN0aW9ucy5hbGxvd1BhdHRlcm5zJyk7XG4gICAgLy8gVmFsaWRhdGUgYXBwcm92YWxQb2xpY3kgKElzc3VlICM2MjUgUGhhc2UgMylcbiAgICBsZXQgYXBwcm92YWxQb2xpY3k6IENsaUFwcHJvdmFsUG9saWN5IHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuICAgIGlmIChlci5hcHByb3ZhbFBvbGljeSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAodHlwZW9mIGVyLmFwcHJvdmFsUG9saWN5ICE9PSAnb2JqZWN0JyB8fCBlci5hcHByb3ZhbFBvbGljeSA9PT0gbnVsbCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZ2F0ZWtlZXBlciBwb2xpY3k6IGV4dGVybmFsUmVzdHJpY3Rpb25zLmFwcHJvdmFsUG9saWN5IG11c3QgYmUgYW4gb2JqZWN0Jyk7XG4gICAgICB9XG4gICAgICBjb25zdCBhcCA9IGVyLmFwcHJvdmFsUG9saWN5IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuXG4gICAgICAvLyBWYWxpZGF0ZSByZXF1aXJlQXBwcm92YWxcbiAgICAgIGxldCByZXF1aXJlQXBwcm92YWw6ICgnbW9kZXJhdGUnIHwgJ2Rhbmdlcm91cycpW10gfCB1bmRlZmluZWQ7XG4gICAgICBpZiAoYXAucmVxdWlyZUFwcHJvdmFsICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKCFBcnJheS5pc0FycmF5KGFwLnJlcXVpcmVBcHByb3ZhbCkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZ2F0ZWtlZXBlciBwb2xpY3k6IGV4dGVybmFsUmVzdHJpY3Rpb25zLmFwcHJvdmFsUG9saWN5LnJlcXVpcmVBcHByb3ZhbCBtdXN0IGJlIGFuIGFycmF5Jyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdmFsaWRMZXZlbHMgPSBuZXcgU2V0KFsnbW9kZXJhdGUnLCAnZGFuZ2Vyb3VzJ10pO1xuICAgICAgICBmb3IgKGNvbnN0IGxldmVsIG9mIGFwLnJlcXVpcmVBcHByb3ZhbCkge1xuICAgICAgICAgIGlmICh0eXBlb2YgbGV2ZWwgIT09ICdzdHJpbmcnIHx8ICF2YWxpZExldmVscy5oYXMobGV2ZWwpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZ2F0ZWtlZXBlciBwb2xpY3k6IGV4dGVybmFsUmVzdHJpY3Rpb25zLmFwcHJvdmFsUG9saWN5LnJlcXVpcmVBcHByb3ZhbCBjb250YWlucyBpbnZhbGlkIHZhbHVlIFwiJHtsZXZlbH1cIi4gTXVzdCBiZSBcIm1vZGVyYXRlXCIgb3IgXCJkYW5nZXJvdXNcIi5gKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmVxdWlyZUFwcHJvdmFsID0gYXAucmVxdWlyZUFwcHJvdmFsIGFzICgnbW9kZXJhdGUnIHwgJ2Rhbmdlcm91cycpW107XG4gICAgICB9XG5cbiAgICAgIC8vIFZhbGlkYXRlIGRlZmF1bHRTY29wZVxuICAgICAgbGV0IGRlZmF1bHRTY29wZTogJ3NpbmdsZScgfCAndG9vbF9zZXNzaW9uJyB8IHVuZGVmaW5lZDtcbiAgICAgIGlmIChhcC5kZWZhdWx0U2NvcGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoYXAuZGVmYXVsdFNjb3BlICE9PSAnc2luZ2xlJyAmJiBhcC5kZWZhdWx0U2NvcGUgIT09ICd0b29sX3Nlc3Npb24nKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGdhdGVrZWVwZXIgcG9saWN5OiBleHRlcm5hbFJlc3RyaWN0aW9ucy5hcHByb3ZhbFBvbGljeS5kZWZhdWx0U2NvcGUgbXVzdCBiZSBcInNpbmdsZVwiIG9yIFwidG9vbF9zZXNzaW9uXCIsIGdvdCBcIiR7YXAuZGVmYXVsdFNjb3BlfVwiYCk7XG4gICAgICAgIH1cbiAgICAgICAgZGVmYXVsdFNjb3BlID0gYXAuZGVmYXVsdFNjb3BlO1xuICAgICAgfVxuXG4gICAgICAvLyBWYWxpZGF0ZSB0dGxTZWNvbmRzXG4gICAgICBsZXQgdHRsU2Vjb25kczogbnVtYmVyIHwgdW5kZWZpbmVkO1xuICAgICAgaWYgKGFwLnR0bFNlY29uZHMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAodHlwZW9mIGFwLnR0bFNlY29uZHMgIT09ICdudW1iZXInIHx8ICFOdW1iZXIuaXNGaW5pdGUoYXAudHRsU2Vjb25kcykpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZ2F0ZWtlZXBlciBwb2xpY3k6IGV4dGVybmFsUmVzdHJpY3Rpb25zLmFwcHJvdmFsUG9saWN5LnR0bFNlY29uZHMgbXVzdCBiZSBhIG51bWJlcicpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChhcC50dGxTZWNvbmRzIDwgMzAgfHwgYXAudHRsU2Vjb25kcyA+IDM2MDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgZ2F0ZWtlZXBlciBwb2xpY3k6IGV4dGVybmFsUmVzdHJpY3Rpb25zLmFwcHJvdmFsUG9saWN5LnR0bFNlY29uZHMgbXVzdCBiZSBiZXR3ZWVuIDMwIGFuZCAzNjAwLCBnb3QgJHthcC50dGxTZWNvbmRzfWApO1xuICAgICAgICB9XG4gICAgICAgIHR0bFNlY29uZHMgPSBhcC50dGxTZWNvbmRzO1xuICAgICAgfVxuXG4gICAgICBhcHByb3ZhbFBvbGljeSA9IHsgcmVxdWlyZUFwcHJvdmFsLCBkZWZhdWx0U2NvcGUsIHR0bFNlY29uZHMgfTtcbiAgICB9XG5cbiAgICBleHRlcm5hbFJlc3RyaWN0aW9ucyA9IHtcbiAgICAgIGRlc2NyaXB0aW9uOiBlci5kZXNjcmlwdGlvbiBhcyBzdHJpbmcsXG4gICAgICBkZW55UGF0dGVybnMsXG4gICAgICBjb25maXJtUGF0dGVybnMsXG4gICAgICBhbGxvd1BhdHRlcm5zLFxuICAgICAgYXBwcm92YWxQb2xpY3ksXG4gICAgfTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgYWxsb3csXG4gICAgY29uZmlybSxcbiAgICBkZW55LFxuICAgIHNjb3BlUmVzdHJpY3Rpb25zLFxuICAgIGV4dGVybmFsUmVzdHJpY3Rpb25zLFxuICB9O1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIGF1dGhvcmVkIGdhdGVrZWVwZXIgaW5wdXQgYmVmb3JlIHNhdmUuXG4gKlxuICogQXV0aG9yaW5nLXRpbWUgdmFsaWRhdGlvbiBpcyBzdHJpY3RlciB0aGFuIGxvYWQtdGltZSBzYW5pdGl6YXRpb246IGl0IHNob3VsZFxuICogcmVqZWN0IG1pc3BsYWNlZCBwb2xpY3kgYmxvY2tzIGluc3RlYWQgb2Ygc2lsZW50bHkgc2F2aW5nIGFuIGVsZW1lbnQgdGhhdFxuICogbGF0ZXIgYXBwZWFycyBhY3RpdmUgYnV0IGhhcyBub24tZW5mb3JjZWFibGUgZXh0ZXJuYWwgcmVzdHJpY3Rpb25zLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0R2F0ZWtlZXBlckF1dGhvcmluZ0Vycm9ycyhcbiAgcmVjb3JkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZFxuKTogc3RyaW5nW10ge1xuICBpZiAoIXJlY29yZCB8fCB0eXBlb2YgcmVjb3JkICE9PSAnb2JqZWN0Jykge1xuICAgIHJldHVybiBbXTtcbiAgfVxuXG4gIGNvbnN0IGVycm9yczogc3RyaW5nW10gPSBbXTtcblxuICBpZiAoT2JqZWN0Lmhhc093bihyZWNvcmQsICdleHRlcm5hbFJlc3RyaWN0aW9ucycpKSB7XG4gICAgZXJyb3JzLnB1c2goXG4gICAgICAnSW52YWxpZCBnYXRla2VlcGVyIHBvbGljeTogZXh0ZXJuYWxSZXN0cmljdGlvbnMgbXVzdCBiZSBuZXN0ZWQgdW5kZXIgZ2F0ZWtlZXBlci5leHRlcm5hbFJlc3RyaWN0aW9ucydcbiAgICApO1xuICB9XG5cbiAgaWYgKHJlY29yZC5nYXRla2VlcGVyICE9PSB1bmRlZmluZWQpIHtcbiAgICB0cnkge1xuICAgICAgcGFyc2VFbGVtZW50UG9saWN5KHsgZ2F0ZWtlZXBlcjogcmVjb3JkLmdhdGVrZWVwZXIgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGVycm9ycy5wdXNoKGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGVycm9ycztcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZSB0aGF0IGEgdmFsdWUgaXMgYW4gYXJyYXkgb2Ygc3RyaW5ncy5cbiAqXG4gKiBAcGFyYW0gdmFsdWUgLSBUaGUgdmFsdWUgdG8gdmFsaWRhdGVcbiAqIEBwYXJhbSBmaWVsZE5hbWUgLSBUaGUgZmllbGQgbmFtZSBmb3IgZXJyb3IgbWVzc2FnZXNcbiAqIEByZXR1cm5zIFRoZSB2YWxpZGF0ZWQgYXJyYXksIG9yIHVuZGVmaW5lZFxuICovXG5mdW5jdGlvbiB2YWxpZGF0ZVN0cmluZ0FycmF5KFxuICB2YWx1ZTogdW5rbm93bixcbiAgZmllbGROYW1lOiBzdHJpbmdcbik6IHN0cmluZ1tdIHwgdW5kZWZpbmVkIHtcbiAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgaWYgKCFBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBnYXRla2VlcGVyIHBvbGljeTogJHtmaWVsZE5hbWV9IG11c3QgYmUgYW4gYXJyYXlgKTtcbiAgfVxuXG4gIGlmICghdmFsdWUuZXZlcnkoKGl0ZW0pOiBpdGVtIGlzIHN0cmluZyA9PiB0eXBlb2YgaXRlbSA9PT0gJ3N0cmluZycpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGdhdGVrZWVwZXIgcG9saWN5OiAke2ZpZWxkTmFtZX0gbXVzdCBjb250YWluIG9ubHkgc3RyaW5nc2ApO1xuICB9XG5cbiAgcmV0dXJuIHZhbHVlO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIHBhdHRlcm4gc3RyaW5ncyBmb3IgbGVuZ3RoIGFuZCBub24tZW1wdGluZXNzLlxuICpcbiAqIEBwYXJhbSBwYXR0ZXJucyAtIEFycmF5IG9mIHBhdHRlcm4gc3RyaW5ncyB0byB2YWxpZGF0ZVxuICogQHBhcmFtIGZpZWxkTmFtZSAtIEZpZWxkIG5hbWUgZm9yIGVycm9yIG1lc3NhZ2VzXG4gKiBAdGhyb3dzIEVycm9yIGlmIGFueSBwYXR0ZXJuIGlzIGVtcHR5IG9yIGV4Y2VlZHMgTUFYX0dMT0JfUEFUVEVSTl9MRU5HVEhcbiAqL1xuZnVuY3Rpb24gdmFsaWRhdGVQYXR0ZXJuU3RyaW5ncyhcbiAgcGF0dGVybnM6IHN0cmluZ1tdLFxuICBmaWVsZE5hbWU6IHN0cmluZ1xuKTogdm9pZCB7XG4gIGZvciAoY29uc3QgcGF0dGVybiBvZiBwYXR0ZXJucykge1xuICAgIGlmICghcGF0dGVybikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGdhdGVrZWVwZXIgcG9saWN5OiAke2ZpZWxkTmFtZX0gY29udGFpbnMgYW4gZW1wdHkgcGF0dGVybiBzdHJpbmdgKTtcbiAgICB9XG4gICAgaWYgKHBhdHRlcm4ubGVuZ3RoID4gTUFYX0dMT0JfUEFUVEVSTl9MRU5HVEgpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBnYXRla2VlcGVyIHBvbGljeTogJHtmaWVsZE5hbWV9IHBhdHRlcm4gZXhjZWVkcyBtYXhpbXVtIGxlbmd0aCBvZiAke01BWF9HTE9CX1BBVFRFUk5fTEVOR1RIfSBjaGFyYWN0ZXJzYCk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogS25vd24gQ0xJIHRvb2wgbmFtZXMgdXNlZCBpbiBleHRlcm5hbFJlc3RyaWN0aW9ucyBwYXR0ZXJucy5cbiAqIFBhdHRlcm5zIHNob3VsZCBzdGFydCB3aXRoIG9uZSBvZiB0aGVzZSBmb2xsb3dlZCBieSAnOicgdG8gYmUgZWZmZWN0aXZlLlxuICovXG5jb25zdCBLTk9XTl9UT09MX1BSRUZJWEVTID0gbmV3IFNldChbXG4gICdCYXNoJywgJ0VkaXQnLCAnV3JpdGUnLCAnUmVhZCcsICdHbG9iJywgJ0dyZXAnLFxuICAnV2ViRmV0Y2gnLCAnV2ViU2VhcmNoJywgJ05vdGVib29rRWRpdCcsXG5dKTtcblxuLyoqXG4gKiBSZWdleCBjaGFyYWN0ZXJzIHRoYXQgaGF2ZSBubyBtZWFuaW5nIGluIGdsb2Igc3ludGF4LlxuICogVXNlcnMgd2hvIGluY2x1ZGUgdGhlc2UgbGlrZWx5IGludGVuZCByZWdleCBidXQgdGhlIG1hdGNoZXIgb25seSBzdXBwb3J0cyAqIGFuZCA/LlxuICovXG5jb25zdCBSRUdFWF9TWU5UQVhfUEFUVEVSTiA9IC9bfCgpe31bXFxdXFxcXCteJF0vO1xuXG4vKipcbiAqIEFuYWx5emUgZXh0ZXJuYWxSZXN0cmljdGlvbnMgcGF0dGVybnMgZm9yIGNvbW1vbiBtaXN0YWtlcyBhbmQgc3VzcGljaW91cyBzeW50YXguXG4gKlxuICogUmV0dXJucyBub24tZmF0YWwgd2FybmluZ3MgdG8gaGVscCBMTE1zIGFuZCB1c2VycyB3cml0ZSBlZmZlY3RpdmUgcGF0dGVybnMuXG4gKiBEb2VzIE5PVCB0aHJvdyDigJQgdmFsaWRhdGlvbiBlcnJvcnMgYXJlIGhhbmRsZWQgYnkge0BsaW5rIHZhbGlkYXRlUGF0dGVyblN0cmluZ3N9LlxuICpcbiAqIENoZWNrcyBwZXJmb3JtZWQgKElzc3VlICMxNjY0KTpcbiAqIC0gTWlzc2luZyB0b29sIHByZWZpeCAocGF0dGVybiBkb2Vzbid0IHN0YXJ0IHdpdGggVG9vbE5hbWU6KVxuICogLSBPdmVybHkgYnJvYWQgcGF0dGVybnMgKGJhcmUgYCpgIG9yIGBUb29sTmFtZToqYClcbiAqIC0gUmVnZXggc3ludGF4IHRoYXQgd29uJ3Qgd29yayBpbiBnbG9iIG1hdGNoaW5nXG4gKiAtIExlYWRpbmcvdHJhaWxpbmcgd2hpdGVzcGFjZVxuICpcbiAqIEBwYXJhbSBwYXR0ZXJucyAtIEFycmF5IG9mIHBhdHRlcm4gc3RyaW5ncyB0byBhbmFseXplXG4gKiBAcGFyYW0gZmllbGROYW1lIC0gRmllbGQgbmFtZSBmb3Igd2FybmluZyBtZXNzYWdlc1xuICogQHJldHVybnMgQXJyYXkgb2Ygd2FybmluZyBtZXNzYWdlcyAoZW1wdHkgaWYgbm8gaXNzdWVzIGZvdW5kKVxuICovXG5leHBvcnQgZnVuY3Rpb24gYW5hbHl6ZVBhdHRlcm5TeW50YXgoXG4gIHBhdHRlcm5zOiBzdHJpbmdbXSxcbiAgZmllbGROYW1lOiBzdHJpbmdcbik6IHN0cmluZ1tdIHtcbiAgY29uc3Qgd2FybmluZ3M6IHN0cmluZ1tdID0gW107XG4gIGZvciAoY29uc3QgcGF0dGVybiBvZiBwYXR0ZXJucykge1xuICAgIGFuYWx5emVPbmVQYXR0ZXJuKHBhdHRlcm4sIGZpZWxkTmFtZSwgd2FybmluZ3MpO1xuICB9XG4gIHJldHVybiB3YXJuaW5ncztcbn1cblxuLyoqXG4gKiBBbmFseXplIGEgc2luZ2xlIHBhdHRlcm4gc3RyaW5nIGFuZCBhcHBlbmQgYW55IHdhcm5pbmdzIGZvdW5kLlxuICogRXh0cmFjdGVkIGZyb20ge0BsaW5rIGFuYWx5emVQYXR0ZXJuU3ludGF4fSB0byByZWR1Y2UgY29nbml0aXZlIGNvbXBsZXhpdHkuXG4gKi9cbmZ1bmN0aW9uIGFuYWx5emVPbmVQYXR0ZXJuKFxuICBwYXR0ZXJuOiBzdHJpbmcsXG4gIGZpZWxkTmFtZTogc3RyaW5nLFxuICB3YXJuaW5nczogc3RyaW5nW11cbik6IHZvaWQge1xuICAvLyBDaGVjayBsZWFkaW5nL3RyYWlsaW5nIHdoaXRlc3BhY2VcbiAgaWYgKHBhdHRlcm4gIT09IHBhdHRlcm4udHJpbSgpKSB7XG4gICAgd2FybmluZ3MucHVzaChcbiAgICAgIGAke2ZpZWxkTmFtZX0gcGF0dGVybiAnJHtwYXR0ZXJufScgaGFzIGxlYWRpbmcvdHJhaWxpbmcgd2hpdGVzcGFjZSDigJQgdGhpcyBtYXkgcHJldmVudCBleHBlY3RlZCBtYXRjaGVzYFxuICAgICk7XG4gIH1cblxuICAvLyBDaGVjayBmb3IgYmFyZSB3aWxkY2FyZCAobWF0Y2hlcyBldmVyeXRoaW5nKVxuICBpZiAocGF0dGVybiA9PT0gJyonKSB7XG4gICAgd2FybmluZ3MucHVzaChcbiAgICAgIGAke2ZpZWxkTmFtZX0gcGF0dGVybiAnKicgbWF0Y2hlcyBldmVyeXRoaW5nIOKAlCB0aGlzIGlzIGxpa2VseSB1bmludGVudGlvbmFsYFxuICAgICk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gQ2hlY2sgZm9yIHRvb2wgcHJlZml4IGFuZCByZWxhdGVkIGlzc3Vlc1xuICBjb25zdCBjb2xvbkluZGV4ID0gcGF0dGVybi5pbmRleE9mKCc6Jyk7XG4gIGlmIChjb2xvbkluZGV4ID09PSAtMSkge1xuICAgIHdhcm5pbmdzLnB1c2goXG4gICAgICBgJHtmaWVsZE5hbWV9IHBhdHRlcm4gJyR7cGF0dGVybn0nIGhhcyBubyB0b29sIHByZWZpeCAoZS5nLiwgJ0Jhc2g6JywgJ0VkaXQ6JykuIGAgK1xuICAgICAgYFBhdHRlcm5zIGFyZSBtYXRjaGVkIGFnYWluc3QgJ1Rvb2xOYW1lOmlucHV0JyBzdHJpbmdzIGFuZCB3aWxsIG5vdCBtYXRjaCB3aXRob3V0IGEgcHJlZml4LmBcbiAgICApO1xuICB9IGVsc2Uge1xuICAgIGNoZWNrVG9vbFByZWZpeChwYXR0ZXJuLCBjb2xvbkluZGV4LCBmaWVsZE5hbWUsIHdhcm5pbmdzKTtcbiAgfVxuXG4gIC8vIENoZWNrIGZvciByZWdleCBzeW50YXggdGhhdCB3b24ndCB3b3JrIGluIGdsb2JcbiAgY29uc3QgcmVnZXhDaGFycyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBmb3IgKGNvbnN0IGNoIG9mIHBhdHRlcm4pIHtcbiAgICBpZiAoUkVHRVhfU1lOVEFYX1BBVFRFUk4udGVzdChjaCkpIHJlZ2V4Q2hhcnMuYWRkKGNoKTtcbiAgfVxuICBpZiAocmVnZXhDaGFycy5zaXplID4gMCkge1xuICAgIHdhcm5pbmdzLnB1c2goXG4gICAgICBgJHtmaWVsZE5hbWV9IHBhdHRlcm4gJyR7cGF0dGVybn0nIGNvbnRhaW5zIHJlZ2V4IHN5bnRheCAnJHtbLi4ucmVnZXhDaGFyc10uam9pbignLCAnKX0nIOKAlCBgICtcbiAgICAgIGBvbmx5IGdsb2Igd2lsZGNhcmRzICgqIGFuZCA/KSBhcmUgc3VwcG9ydGVkYFxuICAgICk7XG4gIH1cbn1cblxuLyoqXG4gKiBDaGVjayB0b29sIHByZWZpeCB2YWxpZGl0eSBhbmQgYnJvYWRuZXNzIGZvciBhIHBhdHRlcm4gdGhhdCBjb250YWlucyAnOicuXG4gKi9cbmZ1bmN0aW9uIGNoZWNrVG9vbFByZWZpeChcbiAgcGF0dGVybjogc3RyaW5nLFxuICBjb2xvbkluZGV4OiBudW1iZXIsXG4gIGZpZWxkTmFtZTogc3RyaW5nLFxuICB3YXJuaW5nczogc3RyaW5nW11cbik6IHZvaWQge1xuICBjb25zdCBwcmVmaXggPSBwYXR0ZXJuLnNsaWNlKDAsIGNvbG9uSW5kZXgpO1xuICBjb25zdCBhZnRlckNvbG9uID0gcGF0dGVybi5zbGljZShjb2xvbkluZGV4ICsgMSk7XG5cbiAgLy8gQ2hlY2sgZm9yIHVua25vd24gdG9vbCBwcmVmaXhcbiAgaWYgKCFLTk9XTl9UT09MX1BSRUZJWEVTLmhhcyhwcmVmaXgpICYmICFwcmVmaXguc3RhcnRzV2l0aCgnbWNwXycpKSB7XG4gICAgd2FybmluZ3MucHVzaChcbiAgICAgIGAke2ZpZWxkTmFtZX0gcGF0dGVybiAnJHtwYXR0ZXJufScgdXNlcyB1bmtub3duIHRvb2wgcHJlZml4ICcke3ByZWZpeH0nLiBgICtcbiAgICAgIGBLbm93biBwcmVmaXhlczogJHtbLi4uS05PV05fVE9PTF9QUkVGSVhFU10uam9pbignLCAnKX0uIE1DUCB0b29scyB1c2UgJ21jcF8nIHByZWZpeC5gXG4gICAgKTtcbiAgfVxuXG4gIC8vIENoZWNrIGZvciBvdmVybHkgYnJvYWQgVG9vbE5hbWU6KiBwYXR0ZXJuXG4gIGlmIChhZnRlckNvbG9uID09PSAnKicpIHtcbiAgICB3YXJuaW5ncy5wdXNoKFxuICAgICAgYCR7ZmllbGROYW1lfSBwYXR0ZXJuICcke3BhdHRlcm59JyBtYXRjaGVzIEFMTCAke3ByZWZpeH0gb3BlcmF0aW9ucyDigJQgdmVyaWZ5IHRoaXMgaXMgaW50ZW50aW9uYWxgXG4gICAgKTtcbiAgfVxufVxuXG4vKipcbiAqIFZhbGlkYXRlIGFuZCBzYW5pdGl6ZSBhIGdhdGVrZWVwZXIgcG9saWN5IGR1cmluZyBlbGVtZW50IGRlc2VyaWFsaXphdGlvbi5cbiAqXG4gKiBSZXR1cm5zIGEgdmFsaWRhdGVkIHtAbGluayBFbGVtZW50R2F0ZWtlZXBlclBvbGljeX0gaWYgdGhlIHJhdyBkYXRhIGlzXG4gKiBzdHJ1Y3R1cmFsbHkgdmFsaWQsIG9yIGB1bmRlZmluZWRgIGlmIHRoZSBkYXRhIGlzIGFic2VudCwgZmFsc3ksIG9yXG4gKiBtYWxmb3JtZWQuIE1hbGZvcm1lZCBwb2xpY2llcyBhcmUgbG9nZ2VkIGFzIHNlY3VyaXR5IGV2ZW50cyBhbmQgc3RyaXBwZWRcbiAqIHNvIHRoZXkgbmV2ZXIgcmVhY2ggdGhlIGVuZm9yY2VtZW50IHBpcGVsaW5lLlxuICpcbiAqIFVzZSB0aGlzIGF0IGV2ZXJ5IGRlc2VyaWFsaXphdGlvbiBib3VuZGFyeSAobWFuYWdlciBgcGFyc2VNZXRhZGF0YWAgL1xuICogYHNhbml0aXplTWV0YWRhdGFgIC8gYHBhcnNlTWVtb3J5RmlsZWApIHRvIGd1YXJhbnRlZSB0aGF0IG9ubHkgd2VsbC1mb3JtZWRcbiAqIHBvbGljaWVzIHN1cnZpdmUgbG9hZGluZy5cbiAqXG4gKiBAcGFyYW0gcmF3UG9saWN5IC0gVGhlIHJhdyBnYXRla2VlcGVyIHZhbHVlIGZyb20gcGFyc2VkIFlBTUwvSlNPTlxuICogQHBhcmFtIGVsZW1lbnROYW1lIC0gRWxlbWVudCBuYW1lIGZvciBkaWFnbm9zdGljIGxvZ2dpbmdcbiAqIEBwYXJhbSBlbGVtZW50VHlwZSAtIEVsZW1lbnQgdHlwZSBmb3IgZGlhZ25vc3RpYyBsb2dnaW5nXG4gKiBAcmV0dXJucyBWYWxpZGF0ZWQgcG9saWN5IG9yIHVuZGVmaW5lZFxuICpcbiAqIEBzaW5jZSBJc3N1ZSAjNTI0IOKAlCBSdW50aW1lIHZhbGlkYXRpb24gZm9yIGFsbCBlbGVtZW50IHR5cGVzXG4gKi9cbi8qKlxuICogR2F0ZWtlZXBlciBpbmZyYXN0cnVjdHVyZSBvcGVyYXRpb25zIOKAlCB0d28gcmVsYXRlZCBzZXRzIHdpdGggZGlzdGluY3QgcHVycG9zZXMuXG4gKlxuICogVU5HQVRBQkxFX09QRVJBVElPTlM6IE9wZXJhdGlvbnMgdGhhdCBtdXN0IE5FVkVSIGFwcGVhciBpbiBlbGVtZW50IHBvbGljeSBsaXN0cy5cbiAqIFRoZXNlIGFyZSBwdXJlIGludGVybmFsIHBsdW1iaW5nIOKAlCBnYXRpbmcgdGhlbSBzZXJ2ZXMgbm8gc2VjdXJpdHkgcHVycG9zZSBhbmRcbiAqIGJyZWFrcyBjcml0aWNhbCBmbG93cyAodmVyaWZpY2F0aW9uLCBDTEkgYXBwcm92YWwsIHBlcm1pc3Npb24gZXZhbHVhdGlvbikuXG4gKiBTdHJpcHBlZCBmcm9tIEFMTCBwb2xpY3kgbGlzdHMgKGFsbG93LCBjb25maXJtLCBkZW55KSBkdXJpbmcgc2FuaXRpemF0aW9uLlxuICpcbiAqIEdBVEVLRUVQRVJfSU5GUkFfT1BFUkFUSU9OUzogT3BlcmF0aW9ucyB0aGF0IHNraXAgTGF5ZXIgMiAoZWxlbWVudCBwb2xpY3lcbiAqIHJlc29sdXRpb24pIGR1cmluZyBwcmltYXJ5IGVuZm9yY2VtZW50LiBUaGlzIGlzIGEgU1VQRVJTRVQgb2YgVU5HQVRBQkxFX09QRVJBVElPTlNcbiAqIOKAlCBpdCBhZGRzIGNvbmZpcm1fb3BlcmF0aW9uLCB3aGljaCBJUyBhIHZhbGlkIHBvbGljeSB0YXJnZXQgKGRlbnkgPSBudWNsZWFyIHNhbmRib3gpXG4gKiBidXQgbXVzdCBub3QgYmUgZ2F0ZWQgdGhyb3VnaCB0aGUgbm9ybWFsIGVuZm9yY2UoKSBwYXRoICh0aGF0IGNyZWF0ZXMgdGhlXG4gKiBjYXNjYWRpbmcgY29uZmlybWF0aW9uIGxvb3AgZnJvbSBJc3N1ZSAjNzU4KS4gY29uZmlybV9vcGVyYXRpb24ncyBlbGVtZW50IHBvbGljaWVzXG4gKiBhcmUgZW5mb3JjZWQgdGhyb3VnaCBhIHNlcGFyYXRlIGNoZWNrIGluIHRoZSBjb25maXJtIGhhbmRsZXIgaW5zdGVhZC5cbiAqXG4gKiBSZWxhdGlvbnNoaXA6IEdBVEVLRUVQRVJfSU5GUkFfT1BFUkFUSU9OUyA9IFVOR0FUQUJMRV9PUEVSQVRJT05TICsgY29uZmlybV9vcGVyYXRpb25cbiAqIFRoaXMgZGVyaXZhdGlvbiBpcyBleHBsaWNpdCBpbiBjb2RlIHRvIHByZXZlbnQgdGhlIHNldHMgZnJvbSBkcmlmdGluZyBhcGFydC5cbiAqL1xuY29uc3QgVU5HQVRBQkxFX09QRVJBVElPTlMgPSBuZXcgU2V0KFtcbiAgJ3ZlcmlmeV9jaGFsbGVuZ2UnLFxuICAncmVsZWFzZV9kZWFkbG9jaycsXG4gICdhcHByb3ZlX2NsaV9wZXJtaXNzaW9uJyxcbiAgJ3Blcm1pc3Npb25fcHJvbXB0Jyxcbl0pO1xuXG4vKiogRGVyaXZlZCBmcm9tIFVOR0FUQUJMRV9PUEVSQVRJT05TICsgY29uZmlybV9vcGVyYXRpb24uIFNlZSBibG9jayBjb21tZW50IGFib3ZlLiAqL1xuY29uc3QgR0FURUtFRVBFUl9JTkZSQV9PUEVSQVRJT05TID0gbmV3IFNldChbXG4gIC4uLlVOR0FUQUJMRV9PUEVSQVRJT05TLFxuICAnY29uZmlybV9vcGVyYXRpb24nLFxuXSk7XG5cbi8qKlxuICogQ2hlY2sgaWYgYW4gb3BlcmF0aW9uIGlzIGEgZ2F0ZWtlZXBlciBpbmZyYXN0cnVjdHVyZSBvcGVyYXRpb24gdGhhdCBzaG91bGRcbiAqIHNraXAgZWxlbWVudCBwb2xpY3kgZXZhbHVhdGlvbiBpbiB0aGUgcHJpbWFyeSBlbmZvcmNlbWVudCBwYXRoLlxuICogRXhwb3J0ZWQgZm9yIHVzZSBieSBNQ1BBUUxIYW5kbGVyLiBJc3N1ZSAjNzU4LlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNHYXRla2VlcGVySW5mcmFPcGVyYXRpb24ob3BlcmF0aW9uOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIEdBVEVLRUVQRVJfSU5GUkFfT1BFUkFUSU9OUy5oYXMob3BlcmF0aW9uKTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiBhbnkgYWN0aXZlIGVsZW1lbnRzIGRlbnkgY29uZmlybV9vcGVyYXRpb24gKG51Y2xlYXIgc2FuZGJveCkuXG4gKiBSZXR1cm5zIHRoZSBkZW55aW5nIGVsZW1lbnQgbmFtZSBpZiBmb3VuZCwgdW5kZWZpbmVkIG90aGVyd2lzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbmRDb25maXJtRGVueWluZ0VsZW1lbnQoXG4gIGFjdGl2ZUVsZW1lbnRzOiBBcnJheTx7IG5hbWU6IHN0cmluZzsgdHlwZTogc3RyaW5nOyBtZXRhZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfT5cbik6IHsgbmFtZTogc3RyaW5nOyB0eXBlOiBzdHJpbmcgfSB8IHVuZGVmaW5lZCB7XG4gIGZvciAoY29uc3QgZWxlbWVudCBvZiBhY3RpdmVFbGVtZW50cykge1xuICAgIGNvbnN0IGdhdGVrZWVwZXIgPSBlbGVtZW50Lm1ldGFkYXRhPy5nYXRla2VlcGVyIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkO1xuICAgIGNvbnN0IGRlbnlMaXN0ID0gZ2F0ZWtlZXBlcj8uZGVueTtcbiAgICBpZiAoQXJyYXkuaXNBcnJheShkZW55TGlzdCkgJiYgZGVueUxpc3QuaW5jbHVkZXMoJ2NvbmZpcm1fb3BlcmF0aW9uJykpIHtcbiAgICAgIHJldHVybiB7IG5hbWU6IGVsZW1lbnQubmFtZSwgdHlwZTogZWxlbWVudC50eXBlIH07XG4gICAgfVxuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogQ2hlY2sgaWYgYW55IGFjdGl2ZSBlbGVtZW50cyBoYXZlIGNvbmZpcm1fb3BlcmF0aW9uIGluIHRoZWlyIGNvbmZpcm0gbGlzdCAoYWR2aXNvcnkpLlxuICogUmV0dXJucyB0aGUgZWxlbWVudCBuYW1lcyB0aGF0IHJlcXVlc3QgYWRkaXRpb25hbCBzY3J1dGlueS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbmRDb25maXJtQWR2aXNvcnlFbGVtZW50cyhcbiAgYWN0aXZlRWxlbWVudHM6IEFycmF5PHsgbmFtZTogc3RyaW5nOyB0eXBlOiBzdHJpbmc7IG1ldGFkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PlxuKTogQXJyYXk8eyBuYW1lOiBzdHJpbmc7IHR5cGU6IHN0cmluZyB9PiB7XG4gIGNvbnN0IGFkdmlzb3JpZXM6IEFycmF5PHsgbmFtZTogc3RyaW5nOyB0eXBlOiBzdHJpbmcgfT4gPSBbXTtcbiAgZm9yIChjb25zdCBlbGVtZW50IG9mIGFjdGl2ZUVsZW1lbnRzKSB7XG4gICAgY29uc3QgZ2F0ZWtlZXBlciA9IGVsZW1lbnQubWV0YWRhdGE/LmdhdGVrZWVwZXIgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmRlZmluZWQ7XG4gICAgY29uc3QgY29uZmlybUxpc3QgPSBnYXRla2VlcGVyPy5jb25maXJtO1xuICAgIGlmIChBcnJheS5pc0FycmF5KGNvbmZpcm1MaXN0KSAmJiBjb25maXJtTGlzdC5pbmNsdWRlcygnY29uZmlybV9vcGVyYXRpb24nKSkge1xuICAgICAgYWR2aXNvcmllcy5wdXNoKHsgbmFtZTogZWxlbWVudC5uYW1lLCB0eXBlOiBlbGVtZW50LnR5cGUgfSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBhZHZpc29yaWVzO1xufVxuXG4vKipcbiAqIFN0cmlwIHVuZ2F0YWJsZSBvcGVyYXRpb25zIGZyb20gYSBwb2xpY3kgbGlzdCBhbmQgbG9nIHdhcm5pbmdzLlxuICogUmV0dXJucyB0aGUgZmlsdGVyZWQgbGlzdCAobWF5IGJlIGVtcHR5KS5cbiAqXG4gKiBOb3RlOiBjb25maXJtX29wZXJhdGlvbiBpcyBOT1Qgc3RyaXBwZWQgZnJvbSBkZW55IGxpc3RzIOKAlCBpdCdzIGEgbGVnaXRpbWF0ZVxuICogc2FuZGJveCBtZWNoYW5pc20uIEl0IElTIHN0cmlwcGVkIGZyb20gYWxsb3cvY29uZmlybSBsaXN0cyBzaW5jZSB0aG9zZSBhcmVcbiAqIGhhbmRsZWQgdGhyb3VnaCBhZHZpc29yeSBtZXNzYWdpbmcsIG5vdCB0aHJvdWdoIGVuZm9yY2UoKS5cbiAqL1xuZnVuY3Rpb24gc3RyaXBVbmdhdGFibGUoXG4gIG9wZXJhdGlvbnM6IHN0cmluZ1tdIHwgdW5kZWZpbmVkLFxuICBsaXN0TmFtZTogc3RyaW5nLFxuICBlbGVtZW50TmFtZTogc3RyaW5nLFxuICBlbGVtZW50VHlwZTogc3RyaW5nLFxuKTogc3RyaW5nW10gfCB1bmRlZmluZWQge1xuICBpZiAoIW9wZXJhdGlvbnM/Lmxlbmd0aCkgcmV0dXJuIG9wZXJhdGlvbnM7XG5cbiAgLy8gY29uZmlybV9vcGVyYXRpb24gaW4gZGVueSBsaXN0cyA9IG51Y2xlYXIgc2FuZGJveCAocHJlc2VydmUpXG4gIC8vIGNvbmZpcm1fb3BlcmF0aW9uIGluIGNvbmZpcm0gbGlzdHMgPSBhZHZpc29yeSBtYXJrZXIgKHByZXNlcnZlIOKAlCBpbmVydCBpbiBlbmZvcmNlIHBhdGgsXG4gIC8vICAgcmVhZCBhdCBydW50aW1lIGJ5IGZpbmRDb25maXJtQWR2aXNvcnlFbGVtZW50cygpLiBTYWZlIGJlY2F1c2Ugc2tpcEVsZW1lbnRQb2xpY2llc1xuICAvLyAgIHByZXZlbnRzIHJlc29sdmVFbGVtZW50UG9saWN5KCkgZnJvbSBldmVyIGV2YWx1YXRpbmcgY29uZmlybV9vcGVyYXRpb24gYXMgYW4gb3BlcmF0aW9uLilcbiAgLy8gY29uZmlybV9vcGVyYXRpb24gaW4gYWxsb3cgbGlzdHMgPSByZWR1bmRhbnQgd2l0aCByb3V0ZSBkZWZhdWx0IEFVVE9fQVBQUk9WRSAoc3RyaXApXG4gIGNvbnN0IG9wc1RvU3RyaXAgPSBsaXN0TmFtZSA9PT0gJ2FsbG93J1xuICAgID8gbmV3IFNldChbLi4uVU5HQVRBQkxFX09QRVJBVElPTlMsICdjb25maXJtX29wZXJhdGlvbiddKVxuICAgIDogVU5HQVRBQkxFX09QRVJBVElPTlM7XG5cbiAgY29uc3Qgc3RyaXBwZWQgPSBvcGVyYXRpb25zLmZpbHRlcihvcCA9PiB7XG4gICAgaWYgKG9wc1RvU3RyaXAuaGFzKG9wKSkge1xuICAgICAgbG9nZ2VyLndhcm4oXG4gICAgICAgIGBbR2F0ZWtlZXBlcl0gU3RyaXBwZWQgXCIke29wfVwiIGZyb20gJHtsaXN0TmFtZX0gbGlzdCBpbiAke2VsZW1lbnRUeXBlfSBcIiR7ZWxlbWVudE5hbWV9XCIg4oCUIGAgK1xuICAgICAgICBgZ2F0ZWtlZXBlciBpbmZyYXN0cnVjdHVyZSBvcGVyYXRpb25zIGNhbm5vdCBiZSBnYXRlZCBieSBlbGVtZW50IHBvbGljaWVzIChJc3N1ZSAjNzU4KWBcbiAgICAgICk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9KTtcbiAgcmV0dXJuIHN0cmlwcGVkLmxlbmd0aCA+IDAgPyBzdHJpcHBlZCA6IHVuZGVmaW5lZDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplR2F0ZWtlZXBlclBvbGljeShcbiAgcmF3UG9saWN5OiB1bmtub3duLFxuICBlbGVtZW50TmFtZTogc3RyaW5nLFxuICBlbGVtZW50VHlwZTogc3RyaW5nLFxuICBkaWFnbm9zdGljc1RhcmdldD86IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuKTogRWxlbWVudEdhdGVrZWVwZXJQb2xpY3kgfCB1bmRlZmluZWQge1xuICBpZiAocmF3UG9saWN5ID09PSB1bmRlZmluZWQgfHwgcmF3UG9saWN5ID09PSBudWxsKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gV3JhcCBpbiBhIG1ldGFkYXRhIGVudmVsb3BlIHNvIHBhcnNlRWxlbWVudFBvbGljeSBjYW4gZXh0cmFjdCBpdFxuICAgIGNvbnN0IHZhbGlkYXRlZCA9IHBhcnNlRWxlbWVudFBvbGljeSh7IGdhdGVrZWVwZXI6IHJhd1BvbGljeSB9KTtcbiAgICBjbGVhckdhdGVrZWVwZXJEaWFnbm9zdGljcyhkaWFnbm9zdGljc1RhcmdldCA/PyByYXdQb2xpY3kpO1xuICAgIGlmICh2YWxpZGF0ZWQpIHtcbiAgICAgIC8vIElzc3VlICM3NTg6IFN0cmlwIGdhdGVrZWVwZXIgaW5mcmFzdHJ1Y3R1cmUgb3BlcmF0aW9ucyBmcm9tIGVsZW1lbnQgcG9saWNpZXNcbiAgICAgIC8vIHRvIHByZXZlbnQgY2FzY2FkaW5nIGNvbmZpcm1hdGlvbiBsb29wc1xuICAgICAgdmFsaWRhdGVkLmFsbG93ID0gc3RyaXBVbmdhdGFibGUodmFsaWRhdGVkLmFsbG93LCAnYWxsb3cnLCBlbGVtZW50TmFtZSwgZWxlbWVudFR5cGUpO1xuICAgICAgdmFsaWRhdGVkLmNvbmZpcm0gPSBzdHJpcFVuZ2F0YWJsZSh2YWxpZGF0ZWQuY29uZmlybSwgJ2NvbmZpcm0nLCBlbGVtZW50TmFtZSwgZWxlbWVudFR5cGUpO1xuICAgICAgdmFsaWRhdGVkLmRlbnkgPSBzdHJpcFVuZ2F0YWJsZSh2YWxpZGF0ZWQuZGVueSwgJ2RlbnknLCBlbGVtZW50TmFtZSwgZWxlbWVudFR5cGUpO1xuXG4gICAgICBsb2dnZXIuZGVidWcoYExvYWRlZCBnYXRla2VlcGVyIHBvbGljeSBmb3IgJHtlbGVtZW50VHlwZX0gXCIke2VsZW1lbnROYW1lfVwiYCwge1xuICAgICAgICBhbGxvdzogdmFsaWRhdGVkLmFsbG93Py5sZW5ndGggPz8gMCxcbiAgICAgICAgY29uZmlybTogdmFsaWRhdGVkLmNvbmZpcm0/Lmxlbmd0aCA/PyAwLFxuICAgICAgICBkZW55OiB2YWxpZGF0ZWQuZGVueT8ubGVuZ3RoID8/IDAsXG4gICAgICAgIGhhc1Njb3BlUmVzdHJpY3Rpb25zOiAhIXZhbGlkYXRlZC5zY29wZVJlc3RyaWN0aW9ucyxcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gdmFsaWRhdGVkO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBmb3JtYXRHYXRla2VlcGVyRGlhZ25vc3RpY01lc3NhZ2UoZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpKTtcbiAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICB0eXBlOiAnWUFNTF9QQVJTSU5HX1dBUk5JTkcnLFxuICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgc291cmNlOiBgJHtlbGVtZW50VHlwZX0uZGVzZXJpYWxpemVgLFxuICAgICAgZGV0YWlsczogYE1hbGZvcm1lZCBnYXRla2VlcGVyIHBvbGljeSBpbiBcIiR7ZWxlbWVudE5hbWV9XCIgc3RyaXBwZWQgZHVyaW5nIGxvYWQ6ICR7bWVzc2FnZX1gLFxuICAgIH0pO1xuICAgIGxvZ2dlci53YXJuKGBTdHJpcHBlZCBtYWxmb3JtZWQgZ2F0ZWtlZXBlciBwb2xpY3kgZnJvbSAke2VsZW1lbnRUeXBlfSBcIiR7ZWxlbWVudE5hbWV9XCI6ICR7bWVzc2FnZX1gKTtcbiAgICBhdHRhY2hHYXRla2VlcGVyRGlhZ25vc3RpY3MoZGlhZ25vc3RpY3NUYXJnZXQgPz8gcmF3UG9saWN5LCBtZXNzYWdlKTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59XG5cbmZ1bmN0aW9uIGZvcm1hdEdhdGVrZWVwZXJEaWFnbm9zdGljTWVzc2FnZShtZXNzYWdlOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBub3JtYWxpemVkID0gbWVzc2FnZS50cmltKCk7XG4gIGNvbnN0IGd1aWRhbmNlID0gZ2V0R2F0ZWtlZXBlckZpeEd1aWRhbmNlKG5vcm1hbGl6ZWQpO1xuICByZXR1cm4gZ3VpZGFuY2UgPyBgJHtub3JtYWxpemVkfSBGaXg6ICR7Z3VpZGFuY2V9YCA6IG5vcm1hbGl6ZWQ7XG59XG5cbmZ1bmN0aW9uIGdldEdhdGVrZWVwZXJGaXhHdWlkYW5jZShtZXNzYWdlOiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBpZiAobWVzc2FnZS5pbmNsdWRlcygnZXh0ZXJuYWxSZXN0cmljdGlvbnMgbXVzdCBiZSBuZXN0ZWQgdW5kZXIgZ2F0ZWtlZXBlci5leHRlcm5hbFJlc3RyaWN0aW9ucycpKSB7XG4gICAgcmV0dXJuICdNb3ZlIGV4dGVybmFsUmVzdHJpY3Rpb25zIHVuZGVyIGdhdGVrZWVwZXIuZXh0ZXJuYWxSZXN0cmljdGlvbnMgYW5kIGtlZXAgYWxsb3dQYXR0ZXJucywgY29uZmlybVBhdHRlcm5zLCBhbmQgZGVueVBhdHRlcm5zIGluc2lkZSB0aGF0IG5lc3RlZCBvYmplY3QuJztcbiAgfVxuXG4gIGlmIChtZXNzYWdlLmluY2x1ZGVzKCdleHRlcm5hbFJlc3RyaWN0aW9ucy5kZXNjcmlwdGlvbiBpcyByZXF1aXJlZCcpKSB7XG4gICAgcmV0dXJuICdBZGQgZ2F0ZWtlZXBlci5leHRlcm5hbFJlc3RyaWN0aW9ucy5kZXNjcmlwdGlvbiB3aXRoIGEgc2hvcnQgZXhwbGFuYXRpb24sIGZvciBleGFtcGxlIGRlc2NyaXB0aW9uOiBcIlJlYWQtb25seSBzaGVsbCBwb2xpY3lcIi4nO1xuICB9XG5cbiAgaWYgKG1lc3NhZ2UuaW5jbHVkZXMoJ211c3QgYmUgYW4gYXJyYXknKSB8fCBtZXNzYWdlLmluY2x1ZGVzKCdtdXN0IGNvbnRhaW4gb25seSBzdHJpbmdzJykpIHtcbiAgICByZXR1cm4gJ1VzZSBZQU1MIGFycmF5cyBvZiBzdHJpbmdzLCBmb3IgZXhhbXBsZSBkZW55UGF0dGVybnM6IFtcIkJhc2g6cm0gKlwiXSBvciBhbGxvdzogW1wicmVhZF8qXCJdLic7XG4gIH1cblxuICBpZiAobWVzc2FnZS5pbmNsdWRlcygnc2NvcGVSZXN0cmljdGlvbnMgbXVzdCBiZSBhbiBvYmplY3QnKSkge1xuICAgIHJldHVybiAnVXNlIHNjb3BlUmVzdHJpY3Rpb25zIGFzIGFuIG9iamVjdCwgZm9yIGV4YW1wbGUgc2NvcGVSZXN0cmljdGlvbnM6IHsgYWxsb3dlZFR5cGVzOiBbXCJwZXJzb25hXCIsIFwic2tpbGxcIl0gfS4nO1xuICB9XG5cbiAgaWYgKG1lc3NhZ2UuaW5jbHVkZXMoJ211c3QgYmUgYW4gb2JqZWN0JykpIHtcbiAgICByZXR1cm4gJ1VzZSBnYXRla2VlcGVyIGFzIGFuIG9iamVjdCwgZm9yIGV4YW1wbGUgZ2F0ZWtlZXBlcjogeyBkZW55OiBbXCJkZWxldGVfZWxlbWVudFwiXSB9Lic7XG4gIH1cblxuICByZXR1cm4gJ0NvbXBhcmUgdGhlIGVsZW1lbnQgYWdhaW5zdCB0aGUgZ2F0ZWtlZXBlciBleGFtcGxlcyBmcm9tIGludHJvc3BlY3Rpb24gb3IgdGhlIHNlY3VyaXR5IGRvY3MsIHRoZW4gcmVhY3RpdmF0ZSBpdCBhZnRlciBmaXhpbmcgdGhlIHN0cnVjdHVyZS4nO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXR0YWNoR2F0ZWtlZXBlckRpYWdub3N0aWNzKHRhcmdldDogdW5rbm93biwgbWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gIGlmICghdGFyZ2V0IHx8IHR5cGVvZiB0YXJnZXQgIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgKHRhcmdldCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikuZ2F0ZWtlZXBlckRpYWdub3N0aWNzID0ge1xuICAgIHZhbGlkOiBmYWxzZSxcbiAgICBlbmZvcmNlYWJsZTogZmFsc2UsXG4gICAgbWVzc2FnZSxcbiAgfSBzYXRpc2ZpZXMgR2F0ZWtlZXBlclBvbGljeURpYWdub3N0aWNzO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2xlYXJHYXRla2VlcGVyRGlhZ25vc3RpY3ModGFyZ2V0OiB1bmtub3duKTogdm9pZCB7XG4gIGlmICghdGFyZ2V0IHx8IHR5cGVvZiB0YXJnZXQgIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgZGVsZXRlICh0YXJnZXQgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pLmdhdGVrZWVwZXJEaWFnbm9zdGljcztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEdhdGVrZWVwZXJEaWFnbm9zdGljcyh0YXJnZXQ6IHVua25vd24pOiBHYXRla2VlcGVyUG9saWN5RGlhZ25vc3RpY3MgfCB1bmRlZmluZWQge1xuICBpZiAoIXRhcmdldCB8fCB0eXBlb2YgdGFyZ2V0ICE9PSAnb2JqZWN0Jykge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBjb25zdCBkaWFnbm9zdGljcyA9ICh0YXJnZXQgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pLmdhdGVrZWVwZXJEaWFnbm9zdGljcztcbiAgaWYgKCFkaWFnbm9zdGljcyB8fCB0eXBlb2YgZGlhZ25vc3RpY3MgIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIGNvbnN0IHJlY29yZCA9IGRpYWdub3N0aWNzIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBpZiAocmVjb3JkLnZhbGlkID09PSBmYWxzZSAmJiByZWNvcmQuZW5mb3JjZWFibGUgPT09IGZhbHNlICYmIHR5cGVvZiByZWNvcmQubWVzc2FnZSA9PT0gJ3N0cmluZycgJiYgcmVjb3JkLm1lc3NhZ2UudHJpbSgpICE9PSAnJykge1xuICAgIHJldHVybiB7XG4gICAgICB2YWxpZDogZmFsc2UsXG4gICAgICBlbmZvcmNlYWJsZTogZmFsc2UsXG4gICAgICBtZXNzYWdlOiByZWNvcmQubWVzc2FnZSxcbiAgICB9O1xuICB9XG5cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cbiJdfQ==