@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.
390 lines • 50.4 kB
JavaScript
/**
* RetentionPolicyService - Generic retention policy enforcement (Issue #51)
*
* This service manages retention policies across ALL element types, ensuring that:
* 1. Nothing is auto-deleted without explicit user consent
* 2. Users have full visibility into what would be deleted
* 3. Each element type can have its own retention strategy
* 4. Compliance use cases (legal, GDPR, etc.) are supported when enabled
*
* IMPORTANT: Retention enforcement is DISABLED by default.
* Users must explicitly enable it in config.yml if they want automatic cleanup.
*
* Architecture:
* - This service is element-type agnostic
* - Element-specific logic is delegated to IRetentionStrategy implementations
* - Strategies are registered per element type
* - Designed to support 50+ element types
*
* @module RetentionPolicyService
*/
import { logger } from '../../utils/logger.js';
import { SecurityMonitor } from '../../security/securityMonitor.js';
import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
/**
* Generic retention policy service supporting multiple element types
*/
export class RetentionPolicyService {
configManager;
strategies = new Map();
constructor(configManager) {
this.configManager = configManager;
}
/**
* Register a retention strategy for an element type
* Call this during DI setup for each element type that supports retention
*/
registerStrategy(strategy) {
const typeKey = this.normalizeElementType(strategy.elementType);
this.strategies.set(typeKey, strategy);
logger.debug(`[RetentionPolicyService] Registered strategy for ${typeKey}`);
}
/**
* Get the strategy for an element type
*/
getStrategy(elementType) {
return this.strategies.get(this.normalizeElementType(elementType));
}
/**
* Get registered element types
*/
getRegisteredTypes() {
return Array.from(this.strategies.keys());
}
/**
* Get the global retention policy configuration
*/
getGlobalConfig() {
return this.configManager.getConfig().retentionPolicy;
}
/**
* Get retention configuration for a specific element type
* Merges global defaults with any type-specific overrides
*/
getElementConfig(elementType) {
const global = this.getGlobalConfig();
// Note: typeKey will be used for per-element-type config overrides in future
const _typeKey = this.normalizeElementType(elementType);
// Base configuration from global settings
const config = {
enabled: global.enabled,
defaultTtlDays: global.defaults.ttl_days,
maxItems: global.defaults.max_entries,
enforcementMode: global.enforcement_mode,
allowPinning: true, // Allow pinning by default
};
// Type-specific overrides will be added here
// e.g., from config.retentionPolicy.elementTypes[_typeKey]
return config;
}
/**
* Check if retention enforcement is globally enabled
*/
isEnabled() {
return this.getGlobalConfig().enabled === true;
}
/**
* Check if enforcement should happen on load for a given element type
*/
shouldEnforceOnLoad(elementType) {
if (!this.isEnabled()) {
return false;
}
const config = elementType
? this.getElementConfig(elementType)
: { enforcementMode: this.getGlobalConfig().enforcement_mode };
return config.enforcementMode === 'on_load';
}
/**
* Check if enforcement requires confirmation
*/
requiresConfirmation() {
return this.getGlobalConfig().safety.require_confirmation;
}
/**
* Check if dry run should be performed first
*/
shouldDryRunFirst() {
return this.getGlobalConfig().safety.dry_run_first;
}
/**
* Check items for retention status without modifying anything
* Works with any element type that has a registered strategy
*/
checkItems(elementType, items) {
const config = this.getElementConfig(elementType);
const typeKey = this.normalizeElementType(elementType);
const strategy = this.strategies.get(typeKey);
const summary = {
expiringSoon: [],
alreadyExpired: [],
countsByType: {
[typeKey]: { total: items.size, expiringSoon: 0, expired: 0 }
}
};
if (!strategy) {
logger.warn(`[RetentionPolicyService] No strategy registered for ${typeKey}`);
return summary;
}
// Note: warning_threshold_days is used by strategies in their checkItem implementation
for (const [id, item] of items) {
const result = strategy.checkItem(item, config);
if (!result.shouldRetain && result.reason === 'expired') {
summary.alreadyExpired.push({
elementType: typeKey,
itemId: id,
contentPreview: strategy.getContentPreview(item),
expiresAt: item.expiresAt,
daysOverdue: Math.abs(result.daysUntilExpiry || 0)
});
summary.countsByType[typeKey].expired++;
}
else if (result.reason === 'within_warning' && result.daysUntilExpiry !== undefined) {
summary.expiringSoon.push({
elementType: typeKey,
itemId: id,
contentPreview: strategy.getContentPreview(item),
expiresAt: item.expiresAt,
daysUntilExpiry: result.daysUntilExpiry
});
summary.countsByType[typeKey].expiringSoon++;
}
}
return summary;
}
/**
* Preview what would be deleted (dry run)
* Works with any element type
*/
preview(elementType, element) {
const typeKey = this.normalizeElementType(elementType);
const strategy = this.strategies.get(typeKey);
if (!strategy) {
return {
elementType: typeKey,
success: false,
itemsChecked: 0,
itemsRemoved: 0,
affectedItems: [],
dryRun: true,
warnings: [`No retention strategy registered for ${typeKey}`],
timestamp: new Date()
};
}
const config = this.getElementConfig(elementType);
const items = strategy.getRetainableItems(element);
const affectedItems = [];
for (const [, item] of items) {
const result = strategy.checkItem(item, config);
if (!result.shouldRetain) {
affectedItems.push(result);
}
}
return {
elementType: typeKey,
success: true,
itemsChecked: items.size,
itemsRemoved: affectedItems.length,
affectedItems,
dryRun: true,
warnings: affectedItems.length > 0
? [`${affectedItems.length} items would be deleted`]
: [],
timestamp: new Date()
};
}
/**
* Enforce retention policy on an element
* Works with any element type that has a registered strategy
*
* @param elementType - The type of element (e.g., 'memory', 'agent')
* @param element - The element instance to enforce retention on
* @param options - Enforcement options including force, failFast, and callbacks
* @returns Result including items checked/removed, affected items, and any warnings
*
* ## Partial Failure Behavior
*
* By default, the method continues processing after individual item removal failures:
* - Failed items are logged and added to `result.warnings[]`
* - Successfully removed items are counted in `result.itemsRemoved`
* - `result.success` remains true unless all items fail
*
* This allows maximum cleanup even when some items cannot be removed (e.g., file locks).
*
* ### Atomic Mode (failFast: true)
*
* For scenarios requiring all-or-nothing behavior, use `options.failFast: true`:
* - Stops immediately on first removal error
* - Sets `result.success = false` and `result.error` with details
* - No further items are processed after the failure
*
* **WARNING**: With failFast, any items removed before the failure will NOT be rolled back.
* True transactional semantics are not supported.
*
* @example
* // Default: continue on errors
* const result = await service.enforce('memory', myMemory, { force: true });
* if (result.warnings.length > 0) console.log('Some items failed:', result.warnings);
*
* @example
* // Fail fast: stop on first error
* const result = await service.enforce('memory', myMemory, { force: true, failFast: true });
* if (!result.success) console.log('Failed:', result.error);
*/
async enforce(elementType, element, options = {}) {
const typeKey = this.normalizeElementType(elementType);
const strategy = this.strategies.get(typeKey);
const global = this.getGlobalConfig();
const config = this.getElementConfig(elementType);
// SECURITY: Normalize elementName user input
const sanitizedElementName = options.elementName
? UnicodeValidator.normalize(options.elementName).normalizedContent
: undefined;
// Base result structure
const result = {
elementType: typeKey,
success: true,
itemsChecked: 0,
itemsRemoved: 0,
affectedItems: [],
dryRun: false,
warnings: [],
timestamp: new Date()
};
// Check for registered strategy
if (!strategy) {
result.success = false;
result.error = `No retention strategy registered for ${typeKey}`;
return result;
}
// Check if retention is enabled
if (!this.isEnabled()) {
result.warnings.push('Retention enforcement is disabled globally. Enable in config to allow cleanup.');
return result;
}
// Check enforcement mode
if (config.enforcementMode === 'disabled') {
result.warnings.push('Enforcement mode is "disabled". Use explicit enforcement command or change mode.');
return result;
}
// Get items to check
const items = strategy.getRetainableItems(element);
result.itemsChecked = items.size;
// Check each item
const toRemove = [];
for (const [, item] of items) {
const checkResult = strategy.checkItem(item, config);
if (!checkResult.shouldRetain) {
toRemove.push(checkResult);
}
}
// If dry_run_first is enabled and force is not set, return preview only
if (global.safety.dry_run_first && !options.force) {
result.dryRun = true;
result.itemsRemoved = toRemove.length;
result.affectedItems = toRemove;
if (toRemove.length > 0) {
result.warnings.push(`DRY RUN: ${toRemove.length} items would be deleted. Use force=true to actually delete.`);
}
return result;
}
// Perform actual removal
for (const checkResult of toRemove) {
try {
// Log before deletion if auditing is enabled
if (global.audit.log_deletions) {
logger.info('[RetentionPolicyService] Removing expired item', {
elementType: typeKey,
elementName: sanitizedElementName,
itemId: checkResult.item.id,
reason: checkResult.reason,
daysOverdue: checkResult.daysUntilExpiry ? Math.abs(checkResult.daysUntilExpiry) : undefined
});
}
strategy.removeItem(element, checkResult.item.id);
result.itemsRemoved++;
result.affectedItems.push(checkResult);
// Progress callback
if (options.onProgress) {
options.onProgress(result.itemsRemoved, toRemove.length);
}
}
catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error);
logger.error('[RetentionPolicyService] Failed to remove item', {
itemId: checkResult.item.id,
error: errorMsg
});
result.warnings.push(`Failed to remove item ${checkResult.item.id}: ${errorMsg}`);
// If failFast is enabled, throw immediately on first error
if (options.failFast) {
result.success = false;
result.error = `Failed to remove item ${checkResult.item.id}: ${errorMsg}`;
throw new Error(result.error);
}
}
}
// Log security event
if (result.itemsRemoved > 0) {
SecurityMonitor.logSecurityEvent({
type: 'RETENTION_POLICY_ENFORCED',
severity: 'LOW',
source: 'RetentionPolicyService.enforce',
details: `Removed ${result.itemsRemoved} expired items from ${typeKey}`,
additionalData: {
elementType: typeKey,
elementName: sanitizedElementName,
itemsRemoved: result.itemsRemoved,
enforcementMode: config.enforcementMode
}
});
}
return result;
}
/**
* Get a human-readable status message about retention
*/
getStatusMessage(elementType) {
const global = this.getGlobalConfig();
if (!global.enabled) {
return 'Retention enforcement is DISABLED globally. No items will be automatically deleted.';
}
let message = `Retention enforcement is ENABLED (mode: ${global.enforcement_mode}).\n`;
message += `\nGlobal defaults:\n`;
message += ` - Default TTL: ${global.defaults.ttl_days} days\n`;
message += ` - Max items: ${global.defaults.max_entries}\n`;
message += ` - Warning threshold: ${global.safety.warning_threshold_days} days before expiry\n`;
if (global.safety.require_confirmation) {
message += ' - Confirmation required before deletion\n';
}
if (global.safety.dry_run_first) {
message += ' - Dry run preview required before actual deletion\n';
}
if (global.audit.backup_before_delete) {
message += ` - Backups kept for ${global.audit.backup_retention_days} days\n`;
}
const registeredTypes = this.getRegisteredTypes();
if (registeredTypes.length > 0) {
message += `\nRegistered element types: ${registeredTypes.join(', ')}`;
}
if (elementType) {
const config = this.getElementConfig(elementType);
message += `\n\nConfiguration for ${elementType}:\n`;
message += ` - Enabled: ${config.enabled}\n`;
message += ` - TTL: ${config.defaultTtlDays} days\n`;
message += ` - Max items: ${config.maxItems}\n`;
message += ` - Enforcement: ${config.enforcementMode}\n`;
}
return message;
}
/**
* Normalize element type to consistent string key
* SECURITY: Applies Unicode normalization to prevent bypass attacks
*/
normalizeElementType(elementType) {
const typeStr = String(elementType);
const normalized = UnicodeValidator.normalize(typeStr).normalizedContent;
return normalized.toLowerCase();
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmV0ZW50aW9uUG9saWN5U2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9yZXRlbnRpb24vUmV0ZW50aW9uUG9saWN5U2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFHcEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFXakY7O0dBRUc7QUFDSCxNQUFNLE9BQU8sc0JBQXNCO0lBQ3pCLGFBQWEsQ0FBZ0I7SUFDN0IsVUFBVSxHQUFvQyxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBRWhFLFlBQVksYUFBNEI7UUFDdEMsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUM7SUFDckMsQ0FBQztJQUVEOzs7T0FHRztJQUNJLGdCQUFnQixDQUFDLFFBQTRCO1FBQ2xELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDaEUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ3ZDLE1BQU0sQ0FBQyxLQUFLLENBQUMsb0RBQW9ELE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDOUUsQ0FBQztJQUVEOztPQUVHO0lBQ0ksV0FBVyxDQUFDLFdBQWlDO1FBQ2xELE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVEOztPQUVHO0lBQ0ksa0JBQWtCO1FBQ3ZCLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElB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LEVBQUUsQ0FBQztZQUMvQixPQUFPLElBQUksK0JBQStCLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUN6RSxDQUFDO1FBRUQsSUFBSSxXQUFXLEVBQUUsQ0FBQztZQUNoQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDbEQsT0FBTyxJQUFJLHlCQUF5QixXQUFXLEtBQUssQ0FBQztZQUNyRCxPQUFPLElBQUksZ0JBQWdCLE1BQU0sQ0FBQyxPQUFPLElBQUksQ0FBQztZQUM5QyxPQUFPLElBQUksWUFBWSxNQUFNLENBQUMsY0FBYyxTQUFTLENBQUM7WUFDdEQsT0FBTyxJQUFJLGtCQUFrQixNQUFNLENBQUMsUUFBUSxJQUFJLENBQUM7WUFDakQsT0FBTyxJQUFJLG9CQUFvQixNQUFNLENBQUMsZUFBZSxJQUFJLENBQUM7UUFDNUQsQ0FBQztRQUVELE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7O09BR0c7SUFDSyxvQkFBb0IsQ0FBQyxXQUFpQztRQUM1RCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDcEMsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLGlCQUFpQixDQUFDO1FBQ3pFLE9BQU8sVUFBVSxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQ2xDLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmV0ZW50aW9uUG9saWN5U2VydmljZSAtIEdlbmVyaWMgcmV0ZW50aW9uIHBvbGljeSBlbmZvcmNlbWVudCAoSXNzdWUgIzUxKVxuICpcbiAqIFRoaXMgc2VydmljZSBtYW5hZ2VzIHJldGVudGlvbiBwb2xpY2llcyBhY3Jvc3MgQUxMIGVsZW1lbnQgdHlwZXMsIGVuc3VyaW5nIHRoYXQ6XG4gKiAxLiBOb3RoaW5nIGlzIGF1dG8tZGVsZXRlZCB3aXRob3V0IGV4cGxpY2l0IHVzZXIgY29uc2VudFxuICogMi4gVXNlcnMgaGF2ZSBmdWxsIHZpc2liaWxpdHkgaW50byB3aGF0IHdvdWxkIGJlIGRlbGV0ZWRcbiAqIDMuIEVhY2ggZWxlbWVudCB0eXBlIGNhbiBoYXZlIGl0cyBvd24gcmV0ZW50aW9uIHN0cmF0ZWd5XG4gKiA0LiBDb21wbGlhbmNlIHVzZSBjYXNlcyAobGVnYWwsIEdEUFIsIGV0Yy4pIGFyZSBzdXBwb3J0ZWQgd2hlbiBlbmFibGVkXG4gKlxuICogSU1QT1JUQU5UOiBSZXRlbnRpb24gZW5mb3JjZW1lbnQgaXMgRElTQUJMRUQgYnkgZGVmYXVsdC5cbiAqIFVzZXJzIG11c3QgZXhwbGljaXRseSBlbmFibGUgaXQgaW4gY29uZmlnLnltbCBpZiB0aGV5IHdhbnQgYXV0b21hdGljIGNsZWFudXAuXG4gKlxuICogQXJjaGl0ZWN0dXJlOlxuICogLSBUaGlzIHNlcnZpY2UgaXMgZWxlbWVudC10eXBlIGFnbm9zdGljXG4gKiAtIEVsZW1lbnQtc3BlY2lmaWMgbG9naWMgaXMgZGVsZWdhdGVkIHRvIElSZXRlbnRpb25TdHJhdGVneSBpbXBsZW1lbnRhdGlvbnNcbiAqIC0gU3RyYXRlZ2llcyBhcmUgcmVnaXN0ZXJlZCBwZXIgZWxlbWVudCB0eXBlXG4gKiAtIERlc2lnbmVkIHRvIHN1cHBvcnQgNTArIGVsZW1lbnQgdHlwZXNcbiAqXG4gKiBAbW9kdWxlIFJldGVudGlvblBvbGljeVNlcnZpY2VcbiAqL1xuXG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi8uLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IENvbmZpZ01hbmFnZXIsIFJldGVudGlvblBvbGljeUNvbmZpZyB9IGZyb20gJy4uLy4uL2NvbmZpZy9Db25maWdNYW5hZ2VyLmpzJztcbmltcG9ydCB7IEVsZW1lbnRUeXBlIH0gZnJvbSAnLi4vLi4vcG9ydGZvbGlvL3R5cGVzLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHtcbiAgSVJldGVudGlvblN0cmF0ZWd5LFxuICBJUmV0YWluYWJsZUl0ZW0sXG4gIEVsZW1lbnRSZXRlbnRpb25Db25maWcsXG4gIFJldGVudGlvbkVuZm9yY2VtZW50UmVzdWx0LFxuICBSZXRlbnRpb25TdW1tYXJ5LFxuICBSZXRlbnRpb25DaGVja1Jlc3VsdCxcbiAgRW5mb3JjZW1lbnRPcHRpb25zLFxufSBmcm9tICcuL3R5cGVzLmpzJztcblxuLyoqXG4gKiBHZW5lcmljIHJldGVudGlvbiBwb2xpY3kgc2VydmljZSBzdXBwb3J0aW5nIG11bHRpcGxlIGVsZW1lbnQgdHlwZXNcbiAqL1xuZXhwb3J0IGNsYXNzIFJldGVudGlvblBvbGljeVNlcnZpY2Uge1xuICBwcml2YXRlIGNvbmZpZ01hbmFnZXI6IENvbmZpZ01hbmFnZXI7XG4gIHByaXZhdGUgc3RyYXRlZ2llczogTWFwPHN0cmluZywgSVJldGVudGlvblN0cmF0ZWd5PiA9IG5ldyBNYXAoKTtcblxuICBjb25zdHJ1Y3Rvcihjb25maWdNYW5hZ2VyOiBDb25maWdNYW5hZ2VyKSB7XG4gICAgdGhpcy5jb25maWdNYW5hZ2VyID0gY29uZmlnTWFuYWdlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RlciBhIHJldGVudGlvbiBzdHJhdGVneSBmb3IgYW4gZWxlbWVudCB0eXBlXG4gICAqIENhbGwgdGhpcyBkdXJpbmcgREkgc2V0dXAgZm9yIGVhY2ggZWxlbWVudCB0eXBlIHRoYXQgc3VwcG9ydHMgcmV0ZW50aW9uXG4gICAqL1xuICBwdWJsaWMgcmVnaXN0ZXJTdHJhdGVneShzdHJhdGVneTogSVJldGVudGlvblN0cmF0ZWd5KTogdm9pZCB7XG4gICAgY29uc3QgdHlwZUtleSA9IHRoaXMubm9ybWFsaXplRWxlbWVudFR5cGUoc3RyYXRlZ3kuZWxlbWVudFR5cGUpO1xuICAgIHRoaXMuc3RyYXRlZ2llcy5zZXQodHlwZUtleSwgc3RyYXRlZ3kpO1xuICAgIGxvZ2dlci5kZWJ1ZyhgW1JldGVudGlvblBvbGljeVNlcnZpY2VdIFJlZ2lzdGVyZWQgc3RyYXRlZ3kgZm9yICR7dHlwZUtleX1gKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIHN0cmF0ZWd5IGZvciBhbiBlbGVtZW50IHR5cGVcbiAgICovXG4gIHB1YmxpYyBnZXRTdHJhdGVneShlbGVtZW50VHlwZTogRWxlbWVudFR5cGUgfCBzdHJpbmcpOiBJUmV0ZW50aW9uU3RyYXRlZ3kgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0aGlzLnN0cmF0ZWdpZXMuZ2V0KHRoaXMubm9ybWFsaXplRWxlbWVudFR5cGUoZWxlbWVudFR5cGUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgcmVnaXN0ZXJlZCBlbGVtZW50IHR5cGVzXG4gICAqL1xuICBwdWJsaWMgZ2V0UmVnaXN0ZXJlZFR5cGVzKCk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gQXJyYXkuZnJvbSh0aGlzLnN0cmF0ZWdpZXMua2V5cygpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIGdsb2JhbCByZXRlbnRpb24gcG9saWN5IGNvbmZpZ3VyYXRpb25cbiAgICovXG4gIHB1YmxpYyBnZXRHbG9iYWxDb25maWcoKTogUmV0ZW50aW9uUG9saWN5Q29uZmlnIHtcbiAgICByZXR1cm4gdGhpcy5jb25maWdNYW5hZ2VyLmdldENvbmZpZygpLnJldGVudGlvblBvbGljeTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgcmV0ZW50aW9uIGNvbmZpZ3VyYXRpb24gZm9yIGEgc3BlY2lmaWMgZWxlbWVudCB0eXBlXG4gICAqIE1lcmdlcyBnbG9iYWwgZGVmYXVsdHMgd2l0aCBhbnkgdHlwZS1zcGVjaWZpYyBvdmVycmlkZXNcbiAgICovXG4gIHB1YmxpYyBnZXRFbGVtZW50Q29uZmlnKGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSB8IHN0cmluZyk6IEVsZW1lbnRSZXRlbnRpb25Db25maWcge1xuICAgIGNvbnN0IGdsb2JhbCA9IHRoaXMuZ2V0R2xvYmFsQ29uZmlnKCk7XG4gICAgLy8gTm90ZTogdHlwZUtleSB3aWxsIGJlIHVzZWQgZm9yIHBlci1lbGVtZW50LXR5cGUgY29uZmlnIG92ZXJyaWRlcyBpbiBmdXR1cmVcbiAgICBjb25zdCBfdHlwZUtleSA9IHRoaXMubm9ybWFsaXplRWxlbWVudFR5cGUoZWxlbWVudFR5cGUpO1xuXG4gICAgLy8gQmFzZSBjb25maWd1cmF0aW9uIGZyb20gZ2xvYmFsIHNldHRpbmdzXG4gICAgY29uc3QgY29uZmlnOiBFbGVtZW50UmV0ZW50aW9uQ29uZmlnID0ge1xuICAgICAgZW5hYmxlZDogZ2xvYmFsLmVuYWJsZWQsXG4gICAgICBkZWZhdWx0VHRsRGF5czogZ2xvYmFsLmRlZmF1bHRzLnR0bF9kYXlzLFxuICAgICAgbWF4SXRlbXM6IGdsb2JhbC5kZWZhdWx0cy5tYXhfZW50cmllcyxcbiAgICAgIGVuZm9yY2VtZW50TW9kZTogZ2xvYmFsLmVuZm9yY2VtZW50X21vZGUgYXMgRWxlbWVudFJldGVudGlvbkNvbmZpZ1snZW5mb3JjZW1lbnRNb2RlJ10sXG4gICAgICBhbGxvd1Bpbm5pbmc6IHRydWUsIC8vIEFsbG93IHBpbm5pbmcgYnkgZGVmYXVsdFxuICAgIH07XG5cbiAgICAvLyBUeXBlLXNwZWNpZmljIG92ZXJyaWRlcyB3aWxsIGJlIGFkZGVkIGhlcmVcbiAgICAvLyBlLmcuLCBmcm9tIGNvbmZpZy5yZXRlbnRpb25Qb2xpY3kuZWxlbWVudFR5cGVzW190eXBlS2V5XVxuXG4gICAgcmV0dXJuIGNvbmZpZztcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiByZXRlbnRpb24gZW5mb3JjZW1lbnQgaXMgZ2xvYmFsbHkgZW5hYmxlZFxuICAgKi9cbiAgcHVibGljIGlzRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5nZXRHbG9iYWxDb25maWcoKS5lbmFibGVkID09PSB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGVuZm9yY2VtZW50IHNob3VsZCBoYXBwZW4gb24gbG9hZCBmb3IgYSBnaXZlbiBlbGVtZW50IHR5cGVcbiAgICovXG4gIHB1YmxpYyBzaG91bGRFbmZvcmNlT25Mb2FkKGVsZW1lbnRUeXBlPzogRWxlbWVudFR5cGUgfCBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBpZiAoIXRoaXMuaXNFbmFibGVkKCkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBjb25zdCBjb25maWcgPSBlbGVtZW50VHlwZVxuICAgICAgPyB0aGlzLmdldEVsZW1lbnRDb25maWcoZWxlbWVudFR5cGUpXG4gICAgICA6IHsgZW5mb3JjZW1lbnRNb2RlOiB0aGlzLmdldEdsb2JhbENvbmZpZygpLmVuZm9yY2VtZW50X21vZGUgfTtcblxuICAgIHJldHVybiBjb25maWcuZW5mb3JjZW1lbnRNb2RlID09PSAnb25fbG9hZCc7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgZW5mb3JjZW1lbnQgcmVxdWlyZXMgY29uZmlybWF0aW9uXG4gICAqL1xuICBwdWJsaWMgcmVxdWlyZXNDb25maXJtYXRpb24oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0R2xvYmFsQ29uZmlnKCkuc2FmZXR5LnJlcXVpcmVfY29uZmlybWF0aW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGRyeSBydW4gc2hvdWxkIGJlIHBlcmZvcm1lZCBmaXJzdFxuICAgKi9cbiAgcHVibGljIHNob3VsZERyeVJ1bkZpcnN0KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmdldEdsb2JhbENvbmZpZygpLnNhZmV0eS5kcnlfcnVuX2ZpcnN0O1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGl0ZW1zIGZvciByZXRlbnRpb24gc3RhdHVzIHdpdGhvdXQgbW9kaWZ5aW5nIGFueXRoaW5nXG4gICAqIFdvcmtzIHdpdGggYW55IGVsZW1lbnQgdHlwZSB0aGF0IGhhcyBhIHJlZ2lzdGVyZWQgc3RyYXRlZ3lcbiAgICovXG4gIHB1YmxpYyBjaGVja0l0ZW1zPFQgZXh0ZW5kcyBJUmV0YWluYWJsZUl0ZW0+KFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSB8IHN0cmluZyxcbiAgICBpdGVtczogTWFwPHN0cmluZywgVD5cbiAgKTogUmV0ZW50aW9uU3VtbWFyeSB7XG4gICAgY29uc3QgY29uZmlnID0gdGhpcy5nZXRFbGVtZW50Q29uZmlnKGVsZW1lbnRUeXBlKTtcbiAgICBjb25zdCB0eXBlS2V5ID0gdGhpcy5ub3JtYWxpemVFbGVtZW50VHlwZShlbGVtZW50VHlwZSk7XG4gICAgY29uc3Qgc3RyYXRlZ3kgPSB0aGlzLnN0cmF0ZWdpZXMuZ2V0KHR5cGVLZXkpO1xuXG4gICAgY29uc3Qgc3VtbWFyeTogUmV0ZW50aW9uU3VtbWFyeSA9IHtcbiAgICAgIGV4cGlyaW5nU29vbjogW10sXG4gICAgICBhbHJlYWR5RXhwaXJlZDogW10sXG4gICAgICBjb3VudHNCeVR5cGU6IHtcbiAgICAgICAgW3R5cGVLZXldOiB7IHRvdGFsOiBpdGVtcy5zaXplLCBleHBpcmluZ1Nvb246IDAsIGV4cGlyZWQ6IDAgfVxuICAgICAgfVxuICAgIH07XG5cbiAgICBpZiAoIXN0cmF0ZWd5KSB7XG4gICAgICBsb2dnZXIud2FybihgW1JldGVudGlvblBvbGljeVNlcnZpY2VdIE5vIHN0cmF0ZWd5IHJlZ2lzdGVyZWQgZm9yICR7dHlwZUtleX1gKTtcbiAgICAgIHJldHVybiBzdW1tYXJ5O1xuICAgIH1cblxuICAgIC8vIE5vdGU6IHdhcm5pbmdfdGhyZXNob2xkX2RheXMgaXMgdXNlZCBieSBzdHJhdGVnaWVzIGluIHRoZWlyIGNoZWNrSXRlbSBpbXBsZW1lbnRhdGlvblxuICAgIGZvciAoY29uc3QgW2lkLCBpdGVtXSBvZiBpdGVtcykge1xuICAgICAgY29uc3QgcmVzdWx0ID0gc3RyYXRlZ3kuY2hlY2tJdGVtKGl0ZW0sIGNvbmZpZyk7XG5cbiAgICAgIGlmICghcmVzdWx0LnNob3VsZFJldGFpbiAmJiByZXN1bHQucmVhc29uID09PSAnZXhwaXJlZCcpIHtcbiAgICAgICAgc3VtbWFyeS5hbHJlYWR5RXhwaXJlZC5wdXNoKHtcbiAgICAgICAgICBlbGVtZW50VHlwZTogdHlwZUtleSxcbiAgICAgICAgICBpdGVtSWQ6IGlkLFxuICAgICAgICAgIGNvbnRlbnRQcmV2aWV3OiBzdHJhdGVneS5nZXRDb250ZW50UHJldmlldyhpdGVtKSxcbiAgICAgICAgICBleHBpcmVzQXQ6IGl0ZW0uZXhwaXJlc0F0ISxcbiAgICAgICAgICBkYXlzT3ZlcmR1ZTogTWF0aC5hYnMocmVzdWx0LmRheXNVbnRpbEV4cGlyeSB8fCAwKVxuICAgICAgICB9KTtcbiAgICAgICAgc3VtbWFyeS5jb3VudHNCeVR5cGVbdHlwZUtleV0uZXhwaXJlZCsrO1xuICAgICAgfSBlbHNlIGlmIChyZXN1bHQucmVhc29uID09PSAnd2l0aGluX3dhcm5pbmcnICYmIHJlc3VsdC5kYXlzVW50aWxFeHBpcnkgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBzdW1tYXJ5LmV4cGlyaW5nU29vbi5wdXNoKHtcbiAgICAgICAgICBlbGVtZW50VHlwZTogdHlwZUtleSxcbiAgICAgICAgICBpdGVtSWQ6IGlkLFxuICAgICAgICAgIGNvbnRlbnRQcmV2aWV3OiBzdHJhdGVneS5nZXRDb250ZW50UHJldmlldyhpdGVtKSxcbiAgICAgICAgICBleHBpcmVzQXQ6IGl0ZW0uZXhwaXJlc0F0ISxcbiAgICAgICAgICBkYXlzVW50aWxFeHBpcnk6IHJlc3VsdC5kYXlzVW50aWxFeHBpcnlcbiAgICAgICAgfSk7XG4gICAgICAgIHN1bW1hcnkuY291bnRzQnlUeXBlW3R5cGVLZXldLmV4cGlyaW5nU29vbisrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzdW1tYXJ5O1xuICB9XG5cbiAgLyoqXG4gICAqIFByZXZpZXcgd2hhdCB3b3VsZCBiZSBkZWxldGVkIChkcnkgcnVuKVxuICAgKiBXb3JrcyB3aXRoIGFueSBlbGVtZW50IHR5cGVcbiAgICovXG4gIHB1YmxpYyBwcmV2aWV3KFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSB8IHN0cmluZyxcbiAgICBlbGVtZW50OiB1bmtub3duXG4gICk6IFJldGVudGlvbkVuZm9yY2VtZW50UmVzdWx0IHtcbiAgICBjb25zdCB0eXBlS2V5ID0gdGhpcy5ub3JtYWxpemVFbGVtZW50VHlwZShlbGVtZW50VHlwZSk7XG4gICAgY29uc3Qgc3RyYXRlZ3kgPSB0aGlzLnN0cmF0ZWdpZXMuZ2V0KHR5cGVLZXkpO1xuXG4gICAgaWYgKCFzdHJhdGVneSkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZWxlbWVudFR5cGU6IHR5cGVLZXksXG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBpdGVtc0NoZWNrZWQ6IDAsXG4gICAgICAgIGl0ZW1zUmVtb3ZlZDogMCxcbiAgICAgICAgYWZmZWN0ZWRJdGVtczogW10sXG4gICAgICAgIGRyeVJ1bjogdHJ1ZSxcbiAgICAgICAgd2FybmluZ3M6IFtgTm8gcmV0ZW50aW9uIHN0cmF0ZWd5IHJlZ2lzdGVyZWQgZm9yICR7dHlwZUtleX1gXSxcbiAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpXG4gICAgICB9O1xuICAgIH1cblxuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuZ2V0RWxlbWVudENvbmZpZyhlbGVtZW50VHlwZSk7XG4gICAgY29uc3QgaXRlbXMgPSBzdHJhdGVneS5nZXRSZXRhaW5hYmxlSXRlbXMoZWxlbWVudCk7XG4gICAgY29uc3QgYWZmZWN0ZWRJdGVtczogUmV0ZW50aW9uQ2hlY2tSZXN1bHRbXSA9IFtdO1xuXG4gICAgZm9yIChjb25zdCBbLCBpdGVtXSBvZiBpdGVtcykge1xuICAgICAgY29uc3QgcmVzdWx0ID0gc3RyYXRlZ3kuY2hlY2tJdGVtKGl0ZW0sIGNvbmZpZyk7XG4gICAgICBpZiAoIXJlc3VsdC5zaG91bGRSZXRhaW4pIHtcbiAgICAgICAgYWZmZWN0ZWRJdGVtcy5wdXNoKHJlc3VsdCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGVsZW1lbnRUeXBlOiB0eXBlS2V5LFxuICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgIGl0ZW1zQ2hlY2tlZDogaXRlbXMuc2l6ZSxcbiAgICAgIGl0ZW1zUmVtb3ZlZDogYWZmZWN0ZWRJdGVtcy5sZW5ndGgsXG4gICAgICBhZmZlY3RlZEl0ZW1zLFxuICAgICAgZHJ5UnVuOiB0cnVlLFxuICAgICAgd2FybmluZ3M6IGFmZmVjdGVkSXRlbXMubGVuZ3RoID4gMFxuICAgICAgICA/IFtgJHthZmZlY3RlZEl0ZW1zLmxlbmd0aH0gaXRlbXMgd291bGQgYmUgZGVsZXRlZGBdXG4gICAgICAgIDogW10sXG4gICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKClcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEVuZm9yY2UgcmV0ZW50aW9uIHBvbGljeSBvbiBhbiBlbGVtZW50XG4gICAqIFdvcmtzIHdpdGggYW55IGVsZW1lbnQgdHlwZSB0aGF0IGhhcyBhIHJlZ2lzdGVyZWQgc3RyYXRlZ3lcbiAgICpcbiAgICogQHBhcmFtIGVsZW1lbnRUeXBlIC0gVGhlIHR5cGUgb2YgZWxlbWVudCAoZS5nLiwgJ21lbW9yeScsICdhZ2VudCcpXG4gICAqIEBwYXJhbSBlbGVtZW50IC0gVGhlIGVsZW1lbnQgaW5zdGFuY2UgdG8gZW5mb3JjZSByZXRlbnRpb24gb25cbiAgICogQHBhcmFtIG9wdGlvbnMgLSBFbmZvcmNlbWVudCBvcHRpb25zIGluY2x1ZGluZyBmb3JjZSwgZmFpbEZhc3QsIGFuZCBjYWxsYmFja3NcbiAgICogQHJldHVybnMgUmVzdWx0IGluY2x1ZGluZyBpdGVtcyBjaGVja2VkL3JlbW92ZWQsIGFmZmVjdGVkIGl0ZW1zLCBhbmQgYW55IHdhcm5pbmdzXG4gICAqXG4gICAqICMjIFBhcnRpYWwgRmFpbHVyZSBCZWhhdmlvclxuICAgKlxuICAgKiBCeSBkZWZhdWx0LCB0aGUgbWV0aG9kIGNvbnRpbnVlcyBwcm9jZXNzaW5nIGFmdGVyIGluZGl2aWR1YWwgaXRlbSByZW1vdmFsIGZhaWx1cmVzOlxuICAgKiAtIEZhaWxlZCBpdGVtcyBhcmUgbG9nZ2VkIGFuZCBhZGRlZCB0byBgcmVzdWx0Lndhcm5pbmdzW11gXG4gICAqIC0gU3VjY2Vzc2Z1bGx5IHJlbW92ZWQgaXRlbXMgYXJlIGNvdW50ZWQgaW4gYHJlc3VsdC5pdGVtc1JlbW92ZWRgXG4gICAqIC0gYHJlc3VsdC5zdWNjZXNzYCByZW1haW5zIHRydWUgdW5sZXNzIGFsbCBpdGVtcyBmYWlsXG4gICAqXG4gICAqIFRoaXMgYWxsb3dzIG1heGltdW0gY2xlYW51cCBldmVuIHdoZW4gc29tZSBpdGVtcyBjYW5ub3QgYmUgcmVtb3ZlZCAoZS5nLiwgZmlsZSBsb2NrcykuXG4gICAqXG4gICAqICMjIyBBdG9taWMgTW9kZSAoZmFpbEZhc3Q6IHRydWUpXG4gICAqXG4gICAqIEZvciBzY2VuYXJpb3MgcmVxdWlyaW5nIGFsbC1vci1ub3RoaW5nIGJlaGF2aW9yLCB1c2UgYG9wdGlvbnMuZmFpbEZhc3Q6IHRydWVgOlxuICAgKiAtIFN0b3BzIGltbWVkaWF0ZWx5IG9uIGZpcnN0IHJlbW92YWwgZXJyb3JcbiAgICogLSBTZXRzIGByZXN1bHQuc3VjY2VzcyA9IGZhbHNlYCBhbmQgYHJlc3VsdC5lcnJvcmAgd2l0aCBkZXRhaWxzXG4gICAqIC0gTm8gZnVydGhlciBpdGVtcyBhcmUgcHJvY2Vzc2VkIGFmdGVyIHRoZSBmYWlsdXJlXG4gICAqXG4gICAqICoqV0FSTklORyoqOiBXaXRoIGZhaWxGYXN0LCBhbnkgaXRlbXMgcmVtb3ZlZCBiZWZvcmUgdGhlIGZhaWx1cmUgd2lsbCBOT1QgYmUgcm9sbGVkIGJhY2suXG4gICAqIFRydWUgdHJhbnNhY3Rpb25hbCBzZW1hbnRpY3MgYXJlIG5vdCBzdXBwb3J0ZWQuXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIC8vIERlZmF1bHQ6IGNvbnRpbnVlIG9uIGVycm9yc1xuICAgKiBjb25zdCByZXN1bHQgPSBhd2FpdCBzZXJ2aWNlLmVuZm9yY2UoJ21lbW9yeScsIG15TWVtb3J5LCB7IGZvcmNlOiB0cnVlIH0pO1xuICAgKiBpZiAocmVzdWx0Lndhcm5pbmdzLmxlbmd0aCA+IDApIGNvbnNvbGUubG9nKCdTb21lIGl0ZW1zIGZhaWxlZDonLCByZXN1bHQud2FybmluZ3MpO1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiAvLyBGYWlsIGZhc3Q6IHN0b3Agb24gZmlyc3QgZXJyb3JcbiAgICogY29uc3QgcmVzdWx0ID0gYXdhaXQgc2VydmljZS5lbmZvcmNlKCdtZW1vcnknLCBteU1lbW9yeSwgeyBmb3JjZTogdHJ1ZSwgZmFpbEZhc3Q6IHRydWUgfSk7XG4gICAqIGlmICghcmVzdWx0LnN1Y2Nlc3MpIGNvbnNvbGUubG9nKCdGYWlsZWQ6JywgcmVzdWx0LmVycm9yKTtcbiAgICovXG4gIHB1YmxpYyBhc3luYyBlbmZvcmNlKFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSB8IHN0cmluZyxcbiAgICBlbGVtZW50OiB1bmtub3duLFxuICAgIG9wdGlvbnM6IEVuZm9yY2VtZW50T3B0aW9ucyA9IHt9XG4gICk6IFByb21pc2U8UmV0ZW50aW9uRW5mb3JjZW1lbnRSZXN1bHQ+IHtcbiAgICBjb25zdCB0eXBlS2V5ID0gdGhpcy5ub3JtYWxpemVFbGVtZW50VHlwZShlbGVtZW50VHlwZSk7XG4gICAgY29uc3Qgc3RyYXRlZ3kgPSB0aGlzLnN0cmF0ZWdpZXMuZ2V0KHR5cGVLZXkpO1xuICAgIGNvbnN0IGdsb2JhbCA9IHRoaXMuZ2V0R2xvYmFsQ29uZmlnKCk7XG4gICAgY29uc3QgY29uZmlnID0gdGhpcy5nZXRFbGVtZW50Q29uZmlnKGVsZW1lbnRUeXBlKTtcblxuICAgIC8vIFNFQ1VSSVRZOiBOb3JtYWxpemUgZWxlbWVudE5hbWUgdXNlciBpbnB1dFxuICAgIGNvbnN0IHNhbml0aXplZEVsZW1lbnROYW1lID0gb3B0aW9ucy5lbGVtZW50TmFtZVxuICAgICAgPyBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShvcHRpb25zLmVsZW1lbnROYW1lKS5ub3JtYWxpemVkQ29udGVudFxuICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICAvLyBCYXNlIHJlc3VsdCBzdHJ1Y3R1cmVcbiAgICBjb25zdCByZXN1bHQ6IFJldGVudGlvbkVuZm9yY2VtZW50UmVzdWx0ID0ge1xuICAgICAgZWxlbWVudFR5cGU6IHR5cGVLZXksXG4gICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgaXRlbXNDaGVja2VkOiAwLFxuICAgICAgaXRlbXNSZW1vdmVkOiAwLFxuICAgICAgYWZmZWN0ZWRJdGVtczogW10sXG4gICAgICBkcnlSdW46IGZhbHNlLFxuICAgICAgd2FybmluZ3M6IFtdLFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpXG4gICAgfTtcblxuICAgIC8vIENoZWNrIGZvciByZWdpc3RlcmVkIHN0cmF0ZWd5XG4gICAgaWYgKCFzdHJhdGVneSkge1xuICAgICAgcmVzdWx0LnN1Y2Nlc3MgPSBmYWxzZTtcbiAgICAgIHJlc3VsdC5lcnJvciA9IGBObyByZXRlbnRpb24gc3RyYXRlZ3kgcmVnaXN0ZXJlZCBmb3IgJHt0eXBlS2V5fWA7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIC8vIENoZWNrIGlmIHJldGVudGlvbiBpcyBlbmFibGVkXG4gICAgaWYgKCF0aGlzLmlzRW5hYmxlZCgpKSB7XG4gICAgICByZXN1bHQud2FybmluZ3MucHVzaCgnUmV0ZW50aW9uIGVuZm9yY2VtZW50IGlzIGRpc2FibGVkIGdsb2JhbGx5LiBFbmFibGUgaW4gY29uZmlnIHRvIGFsbG93IGNsZWFudXAuJyk7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIC8vIENoZWNrIGVuZm9yY2VtZW50IG1vZGVcbiAgICBpZiAoY29uZmlnLmVuZm9yY2VtZW50TW9kZSA9PT0gJ2Rpc2FibGVkJykge1xuICAgICAgcmVzdWx0Lndhcm5pbmdzLnB1c2goJ0VuZm9yY2VtZW50IG1vZGUgaXMgXCJkaXNhYmxlZFwiLiBVc2UgZXhwbGljaXQgZW5mb3JjZW1lbnQgY29tbWFuZCBvciBjaGFuZ2UgbW9kZS4nKTtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgLy8gR2V0IGl0ZW1zIHRvIGNoZWNrXG4gICAgY29uc3QgaXRlbXMgPSBzdHJhdGVneS5nZXRSZXRhaW5hYmxlSXRlbXMoZWxlbWVudCk7XG4gICAgcmVzdWx0Lml0ZW1zQ2hlY2tlZCA9IGl0ZW1zLnNpemU7XG5cbiAgICAvLyBDaGVjayBlYWNoIGl0ZW1cbiAgICBjb25zdCB0b1JlbW92ZTogUmV0ZW50aW9uQ2hlY2tSZXN1bHRbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgWywgaXRlbV0gb2YgaXRlbXMpIHtcbiAgICAgIGNvbnN0IGNoZWNrUmVzdWx0ID0gc3RyYXRlZ3kuY2hlY2tJdGVtKGl0ZW0sIGNvbmZpZyk7XG4gICAgICBpZiAoIWNoZWNrUmVzdWx0LnNob3VsZFJldGFpbikge1xuICAgICAgICB0b1JlbW92ZS5wdXNoKGNoZWNrUmVzdWx0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJZiBkcnlfcnVuX2ZpcnN0IGlzIGVuYWJsZWQgYW5kIGZvcmNlIGlzIG5vdCBzZXQsIHJldHVybiBwcmV2aWV3IG9ubHlcbiAgICBpZiAoZ2xvYmFsLnNhZmV0eS5kcnlfcnVuX2ZpcnN0ICYmICFvcHRpb25zLmZvcmNlKSB7XG4gICAgICByZXN1bHQuZHJ5UnVuID0gdHJ1ZTtcbiAgICAgIHJlc3VsdC5pdGVtc1JlbW92ZWQgPSB0b1JlbW92ZS5sZW5ndGg7XG4gICAgICByZXN1bHQuYWZmZWN0ZWRJdGVtcyA9IHRvUmVtb3ZlO1xuICAgICAgaWYgKHRvUmVtb3ZlLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmVzdWx0Lndhcm5pbmdzLnB1c2goXG4gICAgICAgICAgYERSWSBSVU46ICR7dG9SZW1vdmUubGVuZ3RofSBpdGVtcyB3b3VsZCBiZSBkZWxldGVkLiBVc2UgZm9yY2U9dHJ1ZSB0byBhY3R1YWxseSBkZWxldGUuYFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG5cbiAgICAvLyBQZXJmb3JtIGFjdHVhbCByZW1vdmFsXG4gICAgZm9yIChjb25zdCBjaGVja1Jlc3VsdCBvZiB0b1JlbW92ZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gTG9nIGJlZm9yZSBkZWxldGlvbiBpZiBhdWRpdGluZyBpcyBlbmFibGVkXG4gICAgICAgIGlmIChnbG9iYWwuYXVkaXQubG9nX2RlbGV0aW9ucykge1xuICAgICAgICAgIGxvZ2dlci5pbmZvKCdbUmV0ZW50aW9uUG9saWN5U2VydmljZV0gUmVtb3ZpbmcgZXhwaXJlZCBpdGVtJywge1xuICAgICAgICAgICAgZWxlbWVudFR5cGU6IHR5cGVLZXksXG4gICAgICAgICAgICBlbGVtZW50TmFtZTogc2FuaXRpemVkRWxlbWVudE5hbWUsXG4gICAgICAgICAgICBpdGVtSWQ6IGNoZWNrUmVzdWx0Lml0ZW0uaWQsXG4gICAgICAgICAgICByZWFzb246IGNoZWNrUmVzdWx0LnJlYXNvbixcbiAgICAgICAgICAgIGRheXNPdmVyZHVlOiBjaGVja1Jlc3VsdC5kYXlzVW50aWxFeHBpcnkgPyBNYXRoLmFicyhjaGVja1Jlc3VsdC5kYXlzVW50aWxFeHBpcnkpIDogdW5kZWZpbmVkXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICBzdHJhdGVneS5yZW1vdmVJdGVtKGVsZW1lbnQsIGNoZWNrUmVzdWx0Lml0ZW0uaWQpO1xuICAgICAgICByZXN1bHQuaXRlbXNSZW1vdmVkKys7XG4gICAgICAgIHJlc3VsdC5hZmZlY3RlZEl0ZW1zLnB1c2goY2hlY2tSZXN1bHQpO1xuXG4gICAgICAgIC8vIFByb2dyZXNzIGNhbGxiYWNrXG4gICAgICAgIGlmIChvcHRpb25zLm9uUHJvZ3Jlc3MpIHtcbiAgICAgICAgICBvcHRpb25zLm9uUHJvZ3Jlc3MocmVzdWx0Lml0ZW1zUmVtb3ZlZCwgdG9SZW1vdmUubGVuZ3RoKTtcbiAgICAgICAgfVxuXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zdCBlcnJvck1zZyA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKTtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdbUmV0ZW50aW9uUG9saWN5U2VydmljZV0gRmFpbGVkIHRvIHJlbW92ZSBpdGVtJywge1xuICAgICAgICAgIGl0ZW1JZDogY2hlY2tSZXN1bHQuaXRlbS5pZCxcbiAgICAgICAgICBlcnJvcjogZXJyb3JNc2dcbiAgICAgICAgfSk7XG4gICAgICAgIHJlc3VsdC53YXJuaW5ncy5wdXNoKGBGYWlsZWQgdG8gcmVtb3ZlIGl0ZW0gJHtjaGVja1Jlc3VsdC5pdGVtLmlkfTogJHtlcnJvck1zZ31gKTtcblxuICAgICAgICAvLyBJZiBmYWlsRmFzdCBpcyBlbmFibGVkLCB0aHJvdyBpbW1lZGlhdGVseSBvbiBmaXJzdCBlcnJvclxuICAgICAgICBpZiAob3B0aW9ucy5mYWlsRmFzdCkge1xuICAgICAgICAgIHJlc3VsdC5zdWNjZXNzID0gZmFsc2U7XG4gICAgICAgICAgcmVzdWx0LmVycm9yID0gYEZhaWxlZCB0byByZW1vdmUgaXRlbSAke2NoZWNrUmVzdWx0Lml0ZW0uaWR9OiAke2Vycm9yTXNnfWA7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKHJlc3VsdC5lcnJvcik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBMb2cgc2VjdXJpdHkgZXZlbnRcbiAgICBpZiAocmVzdWx0Lml0ZW1zUmVtb3ZlZCA+IDApIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1JFVEVOVElPTl9QT0xJQ1lfRU5GT1JDRUQnLFxuICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgIHNvdXJjZTogJ1JldGVudGlvblBvbGljeVNlcnZpY2UuZW5mb3JjZScsXG4gICAgICAgIGRldGFpbHM6IGBSZW1vdmVkICR7cmVzdWx0Lml0ZW1zUmVtb3ZlZH0gZXhwaXJlZCBpdGVtcyBmcm9tICR7dHlwZUtleX1gLFxuICAgICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICAgIGVsZW1lbnRUeXBlOiB0eXBlS2V5LFxuICAgICAgICAgIGVsZW1lbnROYW1lOiBzYW5pdGl6ZWRFbGVtZW50TmFtZSxcbiAgICAgICAgICBpdGVtc1JlbW92ZWQ6IHJlc3VsdC5pdGVtc1JlbW92ZWQsXG4gICAgICAgICAgZW5mb3JjZW1lbnRNb2RlOiBjb25maWcuZW5mb3JjZW1lbnRNb2RlXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgaHVtYW4tcmVhZGFibGUgc3RhdHVzIG1lc3NhZ2UgYWJvdXQgcmV0ZW50aW9uXG4gICAqL1xuICBwdWJsaWMgZ2V0U3RhdHVzTWVzc2FnZShlbGVtZW50VHlwZT86IEVsZW1lbnRUeXBlIHwgc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBnbG9iYWwgPSB0aGlzLmdldEdsb2JhbENvbmZpZygpO1xuXG4gICAgaWYgKCFnbG9iYWwuZW5hYmxlZCkge1xuICAgICAgcmV0dXJuICdSZXRlbnRpb24gZW5mb3JjZW1lbnQgaXMgRElTQUJMRUQgZ2xvYmFsbHkuIE5vIGl0ZW1zIHdpbGwgYmUgYXV0b21hdGljYWxseSBkZWxldGVkLic7XG4gICAgfVxuXG4gICAgbGV0IG1lc3NhZ2UgPSBgUmV0ZW50aW9uIGVuZm9yY2VtZW50IGlzIEVOQUJMRUQgKG1vZGU6ICR7Z2xvYmFsLmVuZm9yY2VtZW50X21vZGV9KS5cXG5gO1xuICAgIG1lc3NhZ2UgKz0gYFxcbkdsb2JhbCBkZWZhdWx0czpcXG5gO1xuICAgIG1lc3NhZ2UgKz0gYCAgLSBEZWZhdWx0IFRUTDogJHtnbG9iYWwuZGVmYXVsdHMudHRsX2RheXN9IGRheXNcXG5gO1xuICAgIG1lc3NhZ2UgKz0gYCAgLSBNYXggaXRlbXM6ICR7Z2xvYmFsLmRlZmF1bHRzLm1heF9lbnRyaWVzfVxcbmA7XG4gICAgbWVzc2FnZSArPSBgICAtIFdhcm5pbmcgdGhyZXNob2xkOiAke2dsb2JhbC5zYWZldHkud2FybmluZ190aHJlc2hvbGRfZGF5c30gZGF5cyBiZWZvcmUgZXhwaXJ5XFxuYDtcblxuICAgIGlmIChnbG9iYWwuc2FmZXR5LnJlcXVpcmVfY29uZmlybWF0aW9uKSB7XG4gICAgICBtZXNzYWdlICs9ICcgIC0gQ29uZmlybWF0aW9uIHJlcXVpcmVkIGJlZm9yZSBkZWxldGlvblxcbic7XG4gICAgfVxuICAgIGlmIChnbG9iYWwuc2FmZXR5LmRyeV9ydW5fZmlyc3QpIHtcbiAgICAgIG1lc3NhZ2UgKz0gJyAgLSBEcnkgcnVuIHByZXZpZXcgcmVxdWlyZWQgYmVmb3JlIGFjdHVhbCBkZWxldGlvblxcbic7XG4gICAgfVxuICAgIGlmIChnbG9iYWwuYXVkaXQuYmFja3VwX2JlZm9yZV9kZWxldGUpIHtcbiAgICAgIG1lc3NhZ2UgKz0gYCAgLSBCYWNrdXBzIGtlcHQgZm9yICR7Z2xvYmFsLmF1ZGl0LmJhY2t1cF9yZXRlbnRpb25fZGF5c30gZGF5c1xcbmA7XG4gICAgfVxuXG4gICAgY29uc3QgcmVnaXN0ZXJlZFR5cGVzID0gdGhpcy5nZXRSZWdpc3RlcmVkVHlwZXMoKTtcbiAgICBpZiAocmVnaXN0ZXJlZFR5cGVzLmxlbmd0aCA+IDApIHtcbiAgICAgIG1lc3NhZ2UgKz0gYFxcblJlZ2lzdGVyZWQgZWxlbWVudCB0eXBlczogJHtyZWdpc3RlcmVkVHlwZXMuam9pbignLCAnKX1gO1xuICAgIH1cblxuICAgIGlmIChlbGVtZW50VHlwZSkge1xuICAgICAgY29uc3QgY29uZmlnID0gdGhpcy5nZXRFbGVtZW50Q29uZmlnKGVsZW1lbnRUeXBlKTtcbiAgICAgIG1lc3NhZ2UgKz0gYFxcblxcbkNvbmZpZ3VyYXRpb24gZm9yICR7ZWxlbWVudFR5cGV9OlxcbmA7XG4gICAgICBtZXNzYWdlICs9IGAgIC0gRW5hYmxlZDogJHtjb25maWcuZW5hYmxlZH1cXG5gO1xuICAgICAgbWVzc2FnZSArPSBgICAtIFRUTDogJHtjb25maWcuZGVmYXVsdFR0bERheXN9IGRheXNcXG5gO1xuICAgICAgbWVzc2FnZSArPSBgICAtIE1heCBpdGVtczogJHtjb25maWcubWF4SXRlbXN9XFxuYDtcbiAgICAgIG1lc3NhZ2UgKz0gYCAgLSBFbmZvcmNlbWVudDogJHtjb25maWcuZW5mb3JjZW1lbnRNb2RlfVxcbmA7XG4gICAgfVxuXG4gICAgcmV0dXJuIG1lc3NhZ2U7XG4gIH1cblxuICAvKipcbiAgICogTm9ybWFsaXplIGVsZW1lbnQgdHlwZSB0byBjb25zaXN0ZW50IHN0cmluZyBrZXlcbiA