@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.
222 lines • 32.4 kB
JavaScript
/**
* MemoryElementValidator - Specialized validator for Memory elements
*
* Extends GenericElementValidator to add Memory-specific validation:
* - Storage backend validation
* - Retention policy validation
* - Auto-load flag validation
* - Entry size validation
*/
import { ElementType } from '../../portfolio/types.js';
import { GenericElementValidator } from './GenericElementValidator.js';
import { ValidatorHelpers } from './ElementValidator.js';
import { MEMORY_CONSTANTS } from '../../elements/memories/constants.js';
import { SECURITY_LIMITS } from '../../security/constants.js';
const VALID_STORAGE_BACKENDS = ['file', 'memory', 'sqlite', 'hybrid'];
const VALID_RETENTION_PATTERNS = /^(\d+)([dDwWmMyY])$/; // e.g., "30d", "1y", "6m"
export class MemoryElementValidator extends GenericElementValidator {
constructor(validationService, triggerValidationService, metadataService) {
super(ElementType.MEMORY, validationService, triggerValidationService, metadataService);
}
/**
* Override validateCreate to add memory-specific validation
*/
async validateCreate(data, options) {
// First run generic validation
const baseResult = await super.validateCreate(data, options);
const errors = [...baseResult.errors];
const warnings = [...baseResult.warnings];
const suggestions = [...(baseResult.suggestions || [])];
if (!data || typeof data !== 'object') {
return baseResult;
}
const record = data;
// Validate storage backend
if (record.storageBackend !== undefined || record.storage_backend !== undefined) {
const backend = record.storageBackend || record.storage_backend;
const backendResult = this.validateStorageBackend(backend);
if (!backendResult.isValid) {
errors.push(...backendResult.errors);
}
warnings.push(...backendResult.warnings);
}
// Validate retention policy
if (record.retentionDays !== undefined || record.retention_days !== undefined) {
const retention = record.retentionDays || record.retention_days;
const retentionResult = this.validateRetentionDays(retention);
if (!retentionResult.isValid) {
errors.push(...retentionResult.errors);
}
warnings.push(...retentionResult.warnings);
}
// Validate retention policy object format
if (record.retention_policy !== undefined) {
const policyResult = this.validateRetentionPolicy(record.retention_policy);
if (!policyResult.isValid) {
errors.push(...policyResult.errors);
}
warnings.push(...policyResult.warnings);
}
// Validate auto-load flag
if (record.autoLoad !== undefined || record.auto_load !== undefined) {
// Use ternary to properly handle false values (|| treats false as falsy)
const autoLoad = record.autoLoad !== undefined ? record.autoLoad : record.auto_load;
const autoLoadResult = this.validateAutoLoad(autoLoad);
if (!autoLoadResult.isValid) {
errors.push(...autoLoadResult.errors);
}
warnings.push(...autoLoadResult.warnings);
}
// Validate priority if autoLoad is true
if ((record.autoLoad === true || record.auto_load === true) && record.priority !== undefined) {
const priorityResult = this.validatePriority(record.priority);
if (!priorityResult.isValid) {
errors.push(...priorityResult.errors);
}
warnings.push(...priorityResult.warnings);
}
// Memory-specific suggestions
if (!record.storageBackend && !record.storage_backend) {
suggestions.push(`Consider specifying a storage backend (default: ${MEMORY_CONSTANTS.DEFAULT_STORAGE_BACKEND})`);
}
if (!record.retentionDays && !record.retention_days && !record.retention_policy) {
suggestions.push('Consider setting a retention policy for memory entries');
}
return {
isValid: errors.length === 0,
errors,
warnings,
suggestions: suggestions.length > 0 ? suggestions : undefined
};
}
/**
* Validate storage backend
*/
validateStorageBackend(backend) {
if (typeof backend !== 'string') {
return ValidatorHelpers.fail(['Storage backend must be a string']);
}
const sanitized = this.validationService.validateAndSanitizeInput(backend, {
maxLength: SECURITY_LIMITS.MAX_ENUM_FIELD_LENGTH,
allowSpaces: false
});
if (!sanitized.isValid) {
return ValidatorHelpers.fail(sanitized.errors || ['Invalid storage backend']);
}
const normalizedBackend = sanitized.sanitizedValue.toLowerCase();
if (!VALID_STORAGE_BACKENDS.includes(normalizedBackend)) {
return ValidatorHelpers.fail([
`Invalid storage backend '${normalizedBackend}'. Valid options: ${VALID_STORAGE_BACKENDS.join(', ')}`
]);
}
return ValidatorHelpers.pass();
}
/**
* Validate retention days (number or string format like "30d")
*/
validateRetentionDays(retention) {
if (retention === null || retention === undefined) {
return ValidatorHelpers.pass();
}
// Handle numeric retention days
// Note: Memories are permanent by default. We only validate that the value is valid,
// not that it's "too long" - permanent retention is expected behavior.
if (typeof retention === 'number') {
if (!Number.isInteger(retention) || retention < 0) {
return ValidatorHelpers.fail(['Retention days must be a positive integer']);
}
return ValidatorHelpers.pass();
}
// Handle string format (e.g., "30d", "1y")
if (typeof retention === 'string') {
const match = retention.match(VALID_RETENTION_PATTERNS);
if (!match) {
return ValidatorHelpers.fail([
'Invalid retention format. Use number of days or format like "30d", "6m", "1y"'
]);
}
// Format is valid - no need to validate the actual duration
// since permanent retention is the expected default
return ValidatorHelpers.pass();
}
return ValidatorHelpers.fail(['Retention must be a number or string like "30d"']);
}
/**
* Validate retention policy object
*/
validateRetentionPolicy(policy) {
if (!policy || typeof policy !== 'object' || Array.isArray(policy)) {
return ValidatorHelpers.fail(['Retention policy must be an object']);
}
const policyObj = policy;
const warnings = [];
// Validate default retention
if (policyObj.default !== undefined) {
const defaultResult = this.validateRetentionDays(policyObj.default);
if (!defaultResult.isValid) {
return ValidatorHelpers.fail([
`Invalid default retention: ${defaultResult.errors.join(', ')}`
]);
}
warnings.push(...defaultResult.warnings);
}
// Validate tag-specific retentions
if (policyObj.byTag !== undefined && policyObj.by_tag !== undefined) {
const byTag = policyObj.byTag || policyObj.by_tag;
if (typeof byTag !== 'object' || Array.isArray(byTag)) {
return ValidatorHelpers.fail(['Retention policy byTag must be an object']);
}
for (const [tag, retention] of Object.entries(byTag)) {
const tagResult = this.validateRetentionDays(retention);
if (!tagResult.isValid) {
return ValidatorHelpers.fail([
`Invalid retention for tag '${tag}': ${tagResult.errors.join(', ')}`
]);
}
warnings.push(...tagResult.warnings.map(w => `Tag '${tag}': ${w}`));
}
}
return {
isValid: true,
errors: [],
warnings
};
}
/**
* Validate auto-load flag
*/
validateAutoLoad(autoLoad) {
if (typeof autoLoad !== 'boolean') {
return ValidatorHelpers.fail(['Auto-load flag must be a boolean']);
}
return ValidatorHelpers.pass();
}
/**
* Validate priority for auto-load memories
*/
validatePriority(priority) {
if (typeof priority !== 'number') {
return ValidatorHelpers.fail(['Priority must be a number']);
}
if (!Number.isInteger(priority) || priority < 1 || priority > 99) {
return ValidatorHelpers.fail(['Priority must be an integer between 1 and 99']);
}
if (priority > 50) {
return {
isValid: true,
errors: [],
warnings: ['High priority (> 50) memories will load very early in the session']
};
}
return ValidatorHelpers.pass();
}
/**
* Override validateContent for memory-specific size limits
*/
async validateContent(content, maxLength) {
// Use memory-specific max size
const maxSize = maxLength || MEMORY_CONSTANTS.MAX_ENTRY_SIZE || 100000;
return super.validateContent(content, maxSize);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVtb3J5RWxlbWVudFZhbGlkYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy92YWxpZGF0aW9uL01lbW9yeUVsZW1lbnRWYWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFFSCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdkQsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDdkUsT0FBTyxFQUFvQixnQkFBZ0IsRUFBNEIsTUFBTSx1QkFBdUIsQ0FBQztBQUlyRyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUN4RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFOUQsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLE1BQU0sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFDO0FBQ3RFLE1BQU0sd0JBQXdCLEdBQUcscUJBQXFCLENBQUMsQ0FBQywwQkFBMEI7QUFFbEYsTUFBTSxPQUFPLHNCQUF1QixTQUFRLHVCQUF1QjtJQUNqRSxZQUNFLGlCQUFvQyxFQUNwQyx3QkFBa0QsRUFDbEQsZUFBZ0M7UUFFaEMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsaUJBQWlCLEVBQUUsd0JBQXdCLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDMUYsQ0FBQztJQUVEOztPQUVHO0lBQ00sS0FBSyxDQUFDLGNBQWMsQ0FDM0IsSUFBYSxFQUNiLE9BQWtDO1FBRWxDLCtCQUErQjtRQUMvQixNQUFNLFVBQVUsR0FBRyxNQUFNLEtBQUssQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzdELE1BQU0sTUFBTSxHQUFHLENBQUMsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDdEMsTUFBTSxRQUFRLEdBQUcsQ0FB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SUFBSSxnQkFBZ0IsQ0FBQyxjQUFjLElBQUksTUFBTSxDQUFDO1FBQ3ZFLE9BQU8sS0FBSyxDQUFDLGVBQWUsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDakQsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNZW1vcnlFbGVtZW50VmFsaWRhdG9yIC0gU3BlY2lhbGl6ZWQgdmFsaWRhdG9yIGZvciBNZW1vcnkgZWxlbWVudHNcbiAqXG4gKiBFeHRlbmRzIEdlbmVyaWNFbGVtZW50VmFsaWRhdG9yIHRvIGFkZCBNZW1vcnktc3BlY2lmaWMgdmFsaWRhdGlvbjpcbiAqIC0gU3RvcmFnZSBiYWNrZW5kIHZhbGlkYXRpb25cbiAqIC0gUmV0ZW50aW9uIHBvbGljeSB2YWxpZGF0aW9uXG4gKiAtIEF1dG8tbG9hZCBmbGFnIHZhbGlkYXRpb25cbiAqIC0gRW50cnkgc2l6ZSB2YWxpZGF0aW9uXG4gKi9cblxuaW1wb3J0IHsgRWxlbWVudFR5cGUgfSBmcm9tICcuLi8uLi9wb3J0Zm9saW8vdHlwZXMuanMnO1xuaW1wb3J0IHsgR2VuZXJpY0VsZW1lbnRWYWxpZGF0b3IgfSBmcm9tICcuL0dlbmVyaWNFbGVtZW50VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFZhbGlkYXRpb25SZXN1bHQsIFZhbGlkYXRvckhlbHBlcnMsIEVsZW1lbnRWYWxpZGF0aW9uT3B0aW9ucyB9IGZyb20gJy4vRWxlbWVudFZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBWYWxpZGF0aW9uU2VydmljZSB9IGZyb20gJy4vVmFsaWRhdGlvblNlcnZpY2UuanMnO1xuaW1wb3J0IHsgVHJpZ2dlclZhbGlkYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi9UcmlnZ2VyVmFsaWRhdGlvblNlcnZpY2UuanMnO1xuaW1wb3J0IHsgTWV0YWRhdGFTZXJ2aWNlIH0gZnJvbSAnLi4vTWV0YWRhdGFTZXJ2aWNlLmpzJztcbmltcG9ydCB7IE1FTU9SWV9DT05TVEFOVFMgfSBmcm9tICcuLi8uLi9lbGVtZW50cy9tZW1vcmllcy9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgU0VDVVJJVFlfTElNSVRTIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvY29uc3RhbnRzLmpzJztcblxuY29uc3QgVkFMSURfU1RPUkFHRV9CQUNLRU5EUyA9IFsnZmlsZScsICdtZW1vcnknLCAnc3FsaXRlJywgJ2h5YnJpZCddO1xuY29uc3QgVkFMSURfUkVURU5USU9OX1BBVFRFUk5TID0gL14oXFxkKykoW2REd1dtTXlZXSkkLzsgLy8gZS5nLiwgXCIzMGRcIiwgXCIxeVwiLCBcIjZtXCJcblxuZXhwb3J0IGNsYXNzIE1lbW9yeUVsZW1lbnRWYWxpZGF0b3IgZXh0ZW5kcyBHZW5lcmljRWxlbWVudFZhbGlkYXRvciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHZhbGlkYXRpb25TZXJ2aWNlOiBWYWxpZGF0aW9uU2VydmljZSxcbiAgICB0cmlnZ2VyVmFsaWRhdGlvblNlcnZpY2U6IFRyaWdnZXJWYWxpZGF0aW9uU2VydmljZSxcbiAgICBtZXRhZGF0YVNlcnZpY2U6IE1ldGFkYXRhU2VydmljZVxuICApIHtcbiAgICBzdXBlcihFbGVtZW50VHlwZS5NRU1PUlksIHZhbGlkYXRpb25TZXJ2aWNlLCB0cmlnZ2VyVmFsaWRhdGlvblNlcnZpY2UsIG1ldGFkYXRhU2VydmljZSk7XG4gIH1cblxuICAvKipcbiAgICogT3ZlcnJpZGUgdmFsaWRhdGVDcmVhdGUgdG8gYWRkIG1lbW9yeS1zcGVjaWZpYyB2YWxpZGF0aW9uXG4gICAqL1xuICBvdmVycmlkZSBhc3luYyB2YWxpZGF0ZUNyZWF0ZShcbiAgICBkYXRhOiB1bmtub3duLFxuICAgIG9wdGlvbnM/OiBFbGVtZW50VmFsaWRhdGlvbk9wdGlvbnNcbiAgKTogUHJvbWlzZTxWYWxpZGF0aW9uUmVzdWx0PiB7XG4gICAgLy8gRmlyc3QgcnVuIGdlbmVyaWMgdmFsaWRhdGlvblxuICAgIGNvbnN0IGJhc2VSZXN1bHQgPSBhd2FpdCBzdXBlci52YWxpZGF0ZUNyZWF0ZShkYXRhLCBvcHRpb25zKTtcbiAgICBjb25zdCBlcnJvcnMgPSBbLi4uYmFzZVJlc3VsdC5lcnJvcnNdO1xuICAgIGNvbnN0IHdhcm5pbmdzID0gWy4uLmJhc2VSZXN1bHQud2FybmluZ3NdO1xuICAgIGNvbnN0IHN1Z2dlc3Rpb25zID0gWy4uLihiYXNlUmVzdWx0LnN1Z2dlc3Rpb25zIHx8IFtdKV07XG5cbiAgICBpZiAoIWRhdGEgfHwgdHlwZW9mIGRhdGEgIT09ICdvYmplY3QnKSB7XG4gICAgICByZXR1cm4gYmFzZVJlc3VsdDtcbiAgICB9XG5cbiAgICBjb25zdCByZWNvcmQgPSBkYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuXG4gICAgLy8gVmFsaWRhdGUgc3RvcmFnZSBiYWNrZW5kXG4gICAgaWYgKHJlY29yZC5zdG9yYWdlQmFja2VuZCAhPT0gdW5kZWZpbmVkIHx8IHJlY29yZC5zdG9yYWdlX2JhY2tlbmQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgYmFja2VuZCA9IHJlY29yZC5zdG9yYWdlQmFja2VuZCB8fCByZWNvcmQuc3RvcmFnZV9iYWNrZW5kO1xuICAgICAgY29uc3QgYmFja2VuZFJlc3VsdCA9IHRoaXMudmFsaWRhdGVTdG9yYWdlQmFja2VuZChiYWNrZW5kKTtcbiAgICAgIGlmICghYmFja2VuZFJlc3VsdC5pc1ZhbGlkKSB7XG4gICAgICAgIGVycm9ycy5wdXNoKC4uLmJhY2tlbmRSZXN1bHQuZXJyb3JzKTtcbiAgICAgIH1cbiAgICAgIHdhcm5pbmdzLnB1c2goLi4uYmFja2VuZFJlc3VsdC53YXJuaW5ncyk7XG4gICAgfVxuXG4gICAgLy8gVmFsaWRhdGUgcmV0ZW50aW9uIHBvbGljeVxuICAgIGlmIChyZWNvcmQucmV0ZW50aW9uRGF5cyAhPT0gdW5kZWZpbmVkIHx8IHJlY29yZC5yZXRlbnRpb25fZGF5cyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb25zdCByZXRlbnRpb24gPSByZWNvcmQucmV0ZW50aW9uRGF5cyB8fCByZWNvcmQucmV0ZW50aW9uX2RheXM7XG4gICAgICBjb25zdCByZXRlbnRpb25SZXN1bHQgPSB0aGlzLnZhbGlkYXRlUmV0ZW50aW9uRGF5cyhyZXRlbnRpb24pO1xuICAgICAgaWYgKCFyZXRlbnRpb25SZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICBlcnJvcnMucHVzaCguLi5yZXRlbnRpb25SZXN1bHQuZXJyb3JzKTtcbiAgICAgIH1cbiAgICAgIHdhcm5pbmdzLnB1c2goLi4ucmV0ZW50aW9uUmVzdWx0Lndhcm5pbmdzKTtcbiAgICB9XG5cbiAgICAvLyBWYWxpZGF0ZSByZXRlbnRpb24gcG9saWN5IG9iamVjdCBmb3JtYXRcbiAgICBpZiAocmVjb3JkLnJldGVudGlvbl9wb2xpY3kgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgcG9saWN5UmVzdWx0ID0gdGhpcy52YWxpZGF0ZVJldGVudGlvblBvbGljeShyZWNvcmQucmV0ZW50aW9uX3BvbGljeSk7XG4gICAgICBpZiAoIXBvbGljeVJlc3VsdC5pc1ZhbGlkKSB7XG4gICAgICAgIGVycm9ycy5wdXNoKC4uLnBvbGljeVJlc3VsdC5lcnJvcnMpO1xuICAgICAgfVxuICAgICAgd2FybmluZ3MucHVzaCguLi5wb2xpY3lSZXN1bHQud2FybmluZ3MpO1xuICAgIH1cblxuICAgIC8vIFZhbGlkYXRlIGF1dG8tbG9hZCBmbGFnXG4gICAgaWYgKHJlY29yZC5hdXRvTG9hZCAhPT0gdW5kZWZpbmVkIHx8IHJlY29yZC5hdXRvX2xvYWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgLy8gVXNlIHRlcm5hcnkgdG8gcHJvcGVybHkgaGFuZGxlIGZhbHNlIHZhbHVlcyAofHwgdHJlYXRzIGZhbHNlIGFzIGZhbHN5KVxuICAgICAgY29uc3QgYXV0b0xvYWQgPSByZWNvcmQuYXV0b0xvYWQgIT09IHVuZGVmaW5lZCA/IHJlY29yZC5hdXRvTG9hZCA6IHJlY29yZC5hdXRvX2xvYWQ7XG4gICAgICBjb25zdCBhdXRvTG9hZFJlc3VsdCA9IHRoaXMudmFsaWRhdGVBdXRvTG9hZChhdXRvTG9hZCk7XG4gICAgICBpZiAoIWF1dG9Mb2FkUmVzdWx0LmlzVmFsaWQpIHtcbiAgICAgICAgZXJyb3JzLnB1c2goLi4uYXV0b0xvYWRSZXN1bHQuZXJyb3JzKTtcbiAgICAgIH1cbiAgICAgIHdhcm5pbmdzLnB1c2goLi4uYXV0b0xvYWRSZXN1bHQud2FybmluZ3MpO1xuICAgIH1cblxuICAgIC8vIFZhbGlkYXRlIHByaW9yaXR5IGlmIGF1dG9Mb2FkIGlzIHRydWVcbiAgICBpZiAoKHJlY29yZC5hdXRvTG9hZCA9PT0gdHJ1ZSB8fCByZWNvcmQuYXV0b19sb2FkID09PSB0cnVlKSAmJiByZWNvcmQucHJpb3JpdHkgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgcHJpb3JpdHlSZXN1bHQgPSB0aGlzLnZhbGlkYXRlUHJpb3JpdHkocmVjb3JkLnByaW9yaXR5KTtcbiAgICAgIGlmICghcHJpb3JpdHlSZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICBlcnJvcnMucHVzaCguLi5wcmlvcml0eVJlc3VsdC5lcnJvcnMpO1xuICAgICAgfVxuICAgICAgd2FybmluZ3MucHVzaCguLi5wcmlvcml0eVJlc3VsdC53YXJuaW5ncyk7XG4gICAgfVxuXG4gICAgLy8gTWVtb3J5LXNwZWNpZmljIHN1Z2dlc3Rpb25zXG4gICAgaWYgKCFyZWNvcmQuc3RvcmFnZUJhY2tlbmQgJiYgIXJlY29yZC5zdG9yYWdlX2JhY2tlbmQpIHtcbiAgICAgIHN1Z2dlc3Rpb25zLnB1c2goYENvbnNpZGVyIHNwZWNpZnlpbmcgYSBzdG9yYWdlIGJhY2tlbmQgKGRlZmF1bHQ6ICR7TUVNT1JZX0NPTlNUQU5UUy5ERUZBVUxUX1NUT1JBR0VfQkFDS0VORH0pYCk7XG4gICAgfVxuXG4gICAgaWYgKCFyZWNvcmQucmV0ZW50aW9uRGF5cyAmJiAhcmVjb3JkLnJldGVudGlvbl9kYXlzICYmICFyZWNvcmQucmV0ZW50aW9uX3BvbGljeSkge1xuICAgICAgc3VnZ2VzdGlvbnMucHVzaCgnQ29uc2lkZXIgc2V0dGluZyBhIHJldGVudGlvbiBwb2xpY3kgZm9yIG1lbW9yeSBlbnRyaWVzJyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlzVmFsaWQ6IGVycm9ycy5sZW5ndGggPT09IDAsXG4gICAgICBlcnJvcnMsXG4gICAgICB3YXJuaW5ncyxcbiAgICAgIHN1Z2dlc3Rpb25zOiBzdWdnZXN0aW9ucy5sZW5ndGggPiAwID8gc3VnZ2VzdGlvbnMgOiB1bmRlZmluZWRcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIHN0b3JhZ2UgYmFja2VuZFxuICAgKi9cbiAgcHJpdmF0ZSB2YWxpZGF0ZVN0b3JhZ2VCYWNrZW5kKGJhY2tlbmQ6IHVua25vd24pOiBWYWxpZGF0aW9uUmVzdWx0IHtcbiAgICBpZiAodHlwZW9mIGJhY2tlbmQgIT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKFsnU3RvcmFnZSBiYWNrZW5kIG11c3QgYmUgYSBzdHJpbmcnXSk7XG4gICAgfVxuXG4gICAgY29uc3Qgc2FuaXRpemVkID0gdGhpcy52YWxpZGF0aW9uU2VydmljZS52YWxpZGF0ZUFuZFNhbml0aXplSW5wdXQoYmFja2VuZCwge1xuICAgICAgbWF4TGVuZ3RoOiBTRUNVUklUWV9MSU1JVFMuTUFYX0VOVU1fRklFTERfTEVOR1RILFxuICAgICAgYWxsb3dTcGFjZXM6IGZhbHNlXG4gICAgfSk7XG5cbiAgICBpZiAoIXNhbml0aXplZC5pc1ZhbGlkKSB7XG4gICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKHNhbml0aXplZC5lcnJvcnMgfHwgWydJbnZhbGlkIHN0b3JhZ2UgYmFja2VuZCddKTtcbiAgICB9XG5cbiAgICBjb25zdCBub3JtYWxpemVkQmFja2VuZCA9IHNhbml0aXplZC5zYW5pdGl6ZWRWYWx1ZSEudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAoIVZBTElEX1NUT1JBR0VfQkFDS0VORFMuaW5jbHVkZXMobm9ybWFsaXplZEJhY2tlbmQpKSB7XG4gICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKFtcbiAgICAgICAgYEludmFsaWQgc3RvcmFnZSBiYWNrZW5kICcke25vcm1hbGl6ZWRCYWNrZW5kfScuIFZhbGlkIG9wdGlvbnM6ICR7VkFMSURfU1RPUkFHRV9CQUNLRU5EUy5qb2luKCcsICcpfWBcbiAgICAgIF0pO1xuICAgIH1cblxuICAgIHJldHVybiBWYWxpZGF0b3JIZWxwZXJzLnBhc3MoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSByZXRlbnRpb24gZGF5cyAobnVtYmVyIG9yIHN0cmluZyBmb3JtYXQgbGlrZSBcIjMwZFwiKVxuICAgKi9cbiAgcHJpdmF0ZSB2YWxpZGF0ZVJldGVudGlvbkRheXMocmV0ZW50aW9uOiB1bmtub3duKTogVmFsaWRhdGlvblJlc3VsdCB7XG4gICAgaWYgKHJldGVudGlvbiA9PT0gbnVsbCB8fCByZXRlbnRpb24gPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMucGFzcygpO1xuICAgIH1cblxuICAgIC8vIEhhbmRsZSBudW1lcmljIHJldGVudGlvbiBkYXlzXG4gICAgLy8gTm90ZTogTWVtb3JpZXMgYXJlIHBlcm1hbmVudCBieSBkZWZhdWx0LiBXZSBvbmx5IHZhbGlkYXRlIHRoYXQgdGhlIHZhbHVlIGlzIHZhbGlkLFxuICAgIC8vIG5vdCB0aGF0IGl0J3MgXCJ0b28gbG9uZ1wiIC0gcGVybWFuZW50IHJldGVudGlvbiBpcyBleHBlY3RlZCBiZWhhdmlvci5cbiAgICBpZiAodHlwZW9mIHJldGVudGlvbiA9PT0gJ251bWJlcicpIHtcbiAgICAgIGlmICghTnVtYmVyLmlzSW50ZWdlcihyZXRlbnRpb24pIHx8IHJldGVudGlvbiA8IDApIHtcbiAgICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChbJ1JldGVudGlvbiBkYXlzIG11c3QgYmUgYSBwb3NpdGl2ZSBpbnRlZ2VyJ10pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMucGFzcygpO1xuICAgIH1cblxuICAgIC8vIEhhbmRsZSBzdHJpbmcgZm9ybWF0IChlLmcuLCBcIjMwZFwiLCBcIjF5XCIpXG4gICAgaWYgKHR5cGVvZiByZXRlbnRpb24gPT09ICdzdHJpbmcnKSB7XG4gICAgICBjb25zdCBtYXRjaCA9IHJldGVudGlvbi5tYXRjaChWQUxJRF9SRVRFTlRJT05fUEFUVEVSTlMpO1xuICAgICAgaWYgKCFtYXRjaCkge1xuICAgICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKFtcbiAgICAgICAgICAnSW52YWxpZCByZXRlbnRpb24gZm9ybWF0LiBVc2UgbnVtYmVyIG9mIGRheXMgb3IgZm9ybWF0IGxpa2UgXCIzMGRcIiwgXCI2bVwiLCBcIjF5XCInXG4gICAgICAgIF0pO1xuICAgICAgfVxuXG4gICAgICAvLyBGb3JtYXQgaXMgdmFsaWQgLSBubyBuZWVkIHRvIHZhbGlkYXRlIHRoZSBhY3R1YWwgZHVyYXRpb25cbiAgICAgIC8vIHNpbmNlIHBlcm1hbmVudCByZXRlbnRpb24gaXMgdGhlIGV4cGVjdGVkIGRlZmF1bHRcbiAgICAgIHJldHVybiBWYWxpZGF0b3JIZWxwZXJzLnBhc3MoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKFsnUmV0ZW50aW9uIG11c3QgYmUgYSBudW1iZXIgb3Igc3RyaW5nIGxpa2UgXCIzMGRcIiddKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSByZXRlbnRpb24gcG9saWN5IG9iamVjdFxuICAgKi9cbiAgcHJpdmF0ZSB2YWxpZGF0ZVJldGVudGlvblBvbGljeShwb2xpY3k6IHVua25vd24pOiBWYWxpZGF0aW9uUmVzdWx0IHtcbiAgICBpZiAoIXBvbGljeSB8fCB0eXBlb2YgcG9saWN5ICE9PSAnb2JqZWN0JyB8fCBBcnJheS5pc0FycmF5KHBvbGljeSkpIHtcbiAgICAgIHJldHVybiBWYWxpZGF0b3JIZWxwZXJzLmZhaWwoWydSZXRlbnRpb24gcG9saWN5IG11c3QgYmUgYW4gb2JqZWN0J10pO1xuICAgIH1cblxuICAgIGNvbnN0IHBvbGljeU9iaiA9IHBvbGljeSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBjb25zdCB3YXJuaW5nczogc3RyaW5nW10gPSBbXTtcblxuICAgIC8vIFZhbGlkYXRlIGRlZmF1bHQgcmV0ZW50aW9uXG4gICAgaWYgKHBvbGljeU9iai5kZWZhdWx0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IGRlZmF1bHRSZXN1bHQgPSB0aGlzLnZhbGlkYXRlUmV0ZW50aW9uRGF5cyhwb2xpY3lPYmouZGVmYXVsdCk7XG4gICAgICBpZiAoIWRlZmF1bHRSZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKFtcbiAgICAgICAgICBgSW52YWxpZCBkZWZhdWx0IHJldGVudGlvbjogJHtkZWZhdWx0UmVzdWx0LmVycm9ycy5qb2luKCcsICcpfWBcbiAgICAgICAgXSk7XG4gICAgICB9XG4gICAgICB3YXJuaW5ncy5wdXNoKC4uLmRlZmF1bHRSZXN1bHQud2FybmluZ3MpO1xuICAgIH1cblxuICAgIC8vIFZhbGlkYXRlIHRhZy1zcGVjaWZpYyByZXRlbnRpb25zXG4gICAgaWYgKHBvbGljeU9iai5ieVRhZyAhPT0gdW5kZWZpbmVkICYmIHBvbGljeU9iai5ieV90YWcgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgYnlUYWcgPSBwb2xpY3lPYmouYnlUYWcgfHwgcG9saWN5T2JqLmJ5X3RhZztcbiAgICAgIGlmICh0eXBlb2YgYnlUYWcgIT09ICdvYmplY3QnIHx8IEFycmF5LmlzQXJyYXkoYnlUYWcpKSB7XG4gICAgICAgIHJldHVybiBWYWxpZGF0b3JIZWxwZXJzLmZhaWwoWydSZXRlbnRpb24gcG9saWN5IGJ5VGFnIG11c3QgYmUgYW4gb2JqZWN0J10pO1xuICAgICAgfVxuXG4gICAgICBmb3IgKGNvbnN0IFt0YWcsIHJldGVudGlvbl0gb2YgT2JqZWN0LmVudHJpZXMoYnlUYWcgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pKSB7XG4gICAgICAgIGNvbnN0IHRhZ1Jlc3VsdCA9IHRoaXMudmFsaWRhdGVSZXRlbnRpb25EYXlzKHJldGVudGlvbik7XG4gICAgICAgIGlmICghdGFnUmVzdWx0LmlzVmFsaWQpIHtcbiAgICAgICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKFtcbiAgICAgICAgICAgIGBJbnZhbGlkIHJldGVudGlvbiBmb3IgdGFnICcke3RhZ30nOiAke3RhZ1Jlc3VsdC5lcnJvcnMuam9pbignLCAnKX1gXG4gICAgICAgICAgXSk7XG4gICAgICAgIH1cbiAgICAgICAgd2FybmluZ3MucHVzaCguLi50YWdSZXN1bHQud2FybmluZ3MubWFwKHcgPT4gYFRhZyAnJHt0YWd9JzogJHt3fWApKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgaXNWYWxpZDogdHJ1ZSxcbiAgICAgIGVycm9yczogW10sXG4gICAgICB3YXJuaW5nc1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgYXV0by1sb2FkIGZsYWdcbiAgICovXG4gIHByaXZhdGUgdmFsaWRhdGVBdXRvTG9hZChhdXRvTG9hZDogdW5rbm93bik6IFZhbGlkYXRpb25SZXN1bHQge1xuICAgIGlmICh0eXBlb2YgYXV0b0xvYWQgIT09ICdib29sZWFuJykge1xuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChbJ0F1dG8tbG9hZCBmbGFnIG11c3QgYmUgYSBib29sZWFuJ10pO1xuICAgIH1cblxuICAgIHJldHVybiBWYWxpZGF0b3JIZWxwZXJzLnBhc3MoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBwcmlvcml0eSBmb3IgYXV0by1sb2FkIG1lbW9yaWVzXG4gICAqL1xuICBwcml2YXRlIHZhbGlkYXRlUHJpb3JpdHkocHJpb3JpdHk6IHVua25vd24pOiBWYWxpZGF0aW9uUmVzdWx0IHtcbiAgICBpZiAodHlwZW9mIHByaW9yaXR5ICE9PSAnbnVtYmVyJykge1xuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChbJ1ByaW9yaXR5IG11c3QgYmUgYSBudW1iZXInXSk7XG4gICAgfVxuXG4gICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKHByaW9yaXR5KSB8fCBwcmlvcml0eSA8IDEgfHwgcHJpb3JpdHkgPiA5OSkge1xuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChbJ1ByaW9yaXR5IG11c3QgYmUgYW4gaW50ZWdlciBiZXR3ZWVuIDEgYW5kIDk5J10pO1xuICAgIH1cblxuICAgIGlmIChwcmlvcml0eSA+IDUwKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBpc1ZhbGlkOiB0cnVlLFxuICAgICAgICBlcnJvcnM6IFtdLFxuICAgICAgICB3YXJuaW5nczogWydIaWdoIHByaW9yaXR5ICg+IDUwKSBtZW1vcmllcyB3aWxsIGxvYWQgdmVyeSBlYXJseSBpbiB0aGUgc2Vzc2lvbiddXG4gICAgICB9O1xuICAgIH1cblxuICAgIHJldHVybiBWYWxpZGF0b3JIZWxwZXJzLnBhc3MoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPdmVycmlkZSB2YWxpZGF0ZUNvbnRlbnQgZm9yIG1lbW9yeS1zcGVjaWZpYyBzaXplIGxpbWl0c1xuICAgKi9cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIHZhbGlkYXRlQ29udGVudChcbiAgICBjb250ZW50OiB1bmtub3duLFxuICAgIG1heExlbmd0aD86IG51bWJlclxuICApOiBQcm9taXNlPFZhbGlkYXRpb25SZXN1bHQ+IHtcbiAgICAvLyBVc2UgbWVtb3J5LXNwZWNpZmljIG1heCBzaXplXG4gICAgY29uc3QgbWF4U2l6ZSA9IG1heExlbmd0aCB8fCBNRU1PUllfQ09OU1RBTlRTLk1BWF9FTlRSWV9TSVpFIHx8IDEwMDAwMDtcbiAgICByZXR1cm4gc3VwZXIudmFsaWRhdGVDb250ZW50KGNvbnRlbnQsIG1heFNpemUpO1xuICB9XG59Il19