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.

189 lines 21.5 kB
/** * RateLimiter - Implements rate limiting for API calls to prevent abuse * * Features: * - Token bucket algorithm for flexible rate limiting * - Configurable limits per time window * - Memory-efficient implementation * - Thread-safe for concurrent requests */ export class RateLimiter { tokens; lastRefill; lastRequest; maxTokens; refillRate; minDelay; constructor(config) { if (config.maxRequests <= 0) { throw new Error('maxRequests must be positive'); } if (config.windowMs <= 0) { throw new Error('windowMs must be positive'); } this.maxTokens = config.maxRequests; this.tokens = this.maxTokens; this.refillRate = this.maxTokens / config.windowMs; // Validate refill rate to prevent division by zero if (this.refillRate <= 0 || !Number.isFinite(this.refillRate)) { throw new Error('Invalid configuration: refill rate must be positive and finite'); } this.lastRefill = Date.now(); this.lastRequest = 0; this.minDelay = config.minDelayMs || 0; } /** * Check if a request is allowed under the rate limit * @returns Status object indicating if request is allowed */ checkLimit() { const now = Date.now(); // Refill tokens based on time elapsed this.refillTokens(now); // Check minimum delay between requests if (this.minDelay > 0 && this.lastRequest > 0) { const timeSinceLastRequest = now - this.lastRequest; if (timeSinceLastRequest < this.minDelay) { const retryAfterMs = Math.max(1, this.minDelay - timeSinceLastRequest); return { allowed: false, retryAfterMs, remainingTokens: Math.floor(this.tokens), resetTime: new Date(now + retryAfterMs) }; } } // Check if we have tokens available if (this.tokens < 1) { // Calculate when the next token will be available const tokensNeeded = 1 - this.tokens; const rawMs = tokensNeeded / this.refillRate; // Clamp to safe bounds: at least 1ms, at most 1 hour, and always finite const retryAfterMs = Math.max(1, Math.min(3_600_000, Number.isFinite(rawMs) ? Math.ceil(rawMs) : 60_000)); return { allowed: false, retryAfterMs, remainingTokens: 0, resetTime: new Date(now + retryAfterMs) }; } // Request is allowed return { allowed: true, remainingTokens: Math.floor(this.tokens), resetTime: this.getResetTime() }; } /** * Consume a token for an allowed request * Should be called after checkLimit() returns allowed: true */ consumeToken() { const now = Date.now(); this.refillTokens(now); if (this.tokens >= 1) { this.tokens -= 1; this.lastRequest = now; } } /** * Get current rate limit status without consuming a token */ getStatus() { const now = Date.now(); this.refillTokens(now); return { allowed: this.tokens >= 1, remainingTokens: Math.floor(this.tokens), resetTime: this.getResetTime() }; } /** * Reset the rate limiter to full capacity * Useful for testing or manual intervention */ reset() { this.tokens = this.maxTokens; this.lastRefill = Date.now(); this.lastRequest = 0; } /** * Refill tokens based on time elapsed */ refillTokens(now) { const timeSinceLastRefill = now - this.lastRefill; const tokensToAdd = timeSinceLastRefill * this.refillRate; this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd); this.lastRefill = now; } /** * Calculate when the rate limit window will reset */ getResetTime() { const now = Date.now(); const tokensToFull = this.maxTokens - this.tokens; const msUntilFull = tokensToFull / this.refillRate; return new Date(now + msUntilFull); } /** * Get human-readable rate limit information */ toString() { const status = this.getStatus(); return `RateLimit: ${status.remainingTokens}/${this.maxTokens} tokens, ` + `resets at ${status.resetTime.toISOString()}`; } } /** * Factory function to create common rate limiters */ export class RateLimiterFactory { /** * GitHub API rate limiter (60 requests per hour for unauthenticated) */ static createGitHubLimiter() { return new RateLimiter({ maxRequests: 60, windowMs: 60 * 60 * 1000, // 1 hour minDelayMs: 1000 // 1 second minimum between requests }); } /** * Conservative rate limiter for update checks * Allows 10 checks per hour with 30 second minimum delay */ static createUpdateCheckLimiter() { return new RateLimiter({ maxRequests: 10, windowMs: 60 * 60 * 1000, // 1 hour minDelayMs: 30 * 1000 // 30 seconds between checks }); } /** * Strict rate limiter for sensitive operations * Allows 5 requests per hour with 1 minute minimum delay */ static createStrictLimiter() { return new RateLimiter({ maxRequests: 5, windowMs: 60 * 60 * 1000, // 1 hour minDelayMs: 60 * 1000 // 1 minute between requests }); } /** * Rate limiter for permission_prompt evaluations (Issue #625 Phase 4). * 100 requests per 60-second window — high throughput for rapid tool calls. */ static createPermissionPromptLimiter(maxRequests = 100, windowMs = 60_000) { return new RateLimiter({ maxRequests, windowMs }); } /** * Rate limiter for CLI approval record creation (Issue #625 Phase 4). * Default 20 requests per 60-second window — prevents approval request flooding. * Configurable via parameters (MCPAQLHandler reads from env vars). */ static createCliApprovalLimiter(maxRequests = 20, windowMs = 60_000) { return new RateLimiter({ maxRequests, windowMs }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmF0ZUxpbWl0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvUmF0ZUxpbWl0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFlSCxNQUFNLE9BQU8sV0FBVztJQUNkLE1BQU0sQ0FBUztJQUNmLFVBQVUsQ0FBUztJQUNuQixXQUFXLENBQVM7SUFDWCxTQUFTLENBQVM7SUFDbEIsVUFBVSxDQUFTO0lBQ25CLFFBQVEsQ0FBUztJQUVsQyxZQUFZLE1BQXlCO1FBQ25DLElBQUksTUFBTSxDQUFDLFdBQVcsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUM1QixNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLFFBQVEsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN6QixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQztRQUNwQyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDN0IsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUM7UUFFbkQsbURBQW1EO1FBQ25ELElBQUksSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQzlELE1BQU0sSUFBSSxLQUFLLENBQUMsZ0VBQWdFLENBQUMsQ0FBQztRQUNwRixDQUFDO1FBRUQsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxDQUFDLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxN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RCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFJhdGVMaW1pdGVyIC0gSW1wbGVtZW50cyByYXRlIGxpbWl0aW5nIGZvciBBUEkgY2FsbHMgdG8gcHJldmVudCBhYnVzZVxuICogXG4gKiBGZWF0dXJlczpcbiAqIC0gVG9rZW4gYnVja2V0IGFsZ29yaXRobSBmb3IgZmxleGlibGUgcmF0ZSBsaW1pdGluZ1xuICogLSBDb25maWd1cmFibGUgbGltaXRzIHBlciB0aW1lIHdpbmRvd1xuICogLSBNZW1vcnktZWZmaWNpZW50IGltcGxlbWVudGF0aW9uXG4gKiAtIFRocmVhZC1zYWZlIGZvciBjb25jdXJyZW50IHJlcXVlc3RzXG4gKi9cblxuZXhwb3J0IGludGVyZmFjZSBSYXRlTGltaXRlckNvbmZpZyB7XG4gIG1heFJlcXVlc3RzOiBudW1iZXI7ICAgICAgLy8gTWF4aW11bSByZXF1ZXN0cyBhbGxvd2VkXG4gIHdpbmRvd01zOiBudW1iZXI7ICAgICAgICAgLy8gVGltZSB3aW5kb3cgaW4gbWlsbGlzZWNvbmRzXG4gIG1pbkRlbGF5TXM/OiBudW1iZXI7ICAgICAgLy8gTWluaW11bSBkZWxheSBiZXR3ZWVuIHJlcXVlc3RzIChvcHRpb25hbClcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSYXRlTGltaXRTdGF0dXMge1xuICBhbGxvd2VkOiBib29sZWFuO1xuICByZXRyeUFmdGVyTXM/OiBudW1iZXI7XG4gIHJlbWFpbmluZ1Rva2VuczogbnVtYmVyO1xuICByZXNldFRpbWU6IERhdGU7XG59XG5cbmV4cG9ydCBjbGFzcyBSYXRlTGltaXRlciB7XG4gIHByaXZhdGUgdG9rZW5zOiBudW1iZXI7XG4gIHByaXZhdGUgbGFzdFJlZmlsbDogbnVtYmVyO1xuICBwcml2YXRlIGxhc3RSZXF1ZXN0OiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWF4VG9rZW5zOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgcmVmaWxsUmF0ZTogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IG1pbkRlbGF5OiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3IoY29uZmlnOiBSYXRlTGltaXRlckNvbmZpZykge1xuICAgIGlmIChjb25maWcubWF4UmVxdWVzdHMgPD0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtYXhSZXF1ZXN0cyBtdXN0IGJlIHBvc2l0aXZlJyk7XG4gICAgfVxuICAgIGlmIChjb25maWcud2luZG93TXMgPD0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd3aW5kb3dNcyBtdXN0IGJlIHBvc2l0aXZlJyk7XG4gICAgfVxuXG4gICAgdGhpcy5tYXhUb2tlbnMgPSBjb25maWcubWF4UmVxdWVzdHM7XG4gICAgdGhpcy50b2tlbnMgPSB0aGlzLm1heFRva2VucztcbiAgICB0aGlzLnJlZmlsbFJhdGUgPSB0aGlzLm1heFRva2VucyAvIGNvbmZpZy53aW5kb3dNcztcbiAgICBcbiAgICAvLyBWYWxpZGF0ZSByZWZpbGwgcmF0ZSB0byBwcmV2ZW50IGRpdmlzaW9uIGJ5IHplcm9cbiAgICBpZiAodGhpcy5yZWZpbGxSYXRlIDw9IDAgfHwgIU51bWJlci5pc0Zpbml0ZSh0aGlzLnJlZmlsbFJhdGUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY29uZmlndXJhdGlvbjogcmVmaWxsIHJhdGUgbXVzdCBiZSBwb3NpdGl2ZSBhbmQgZmluaXRlJyk7XG4gICAgfVxuICAgIFxuICAgIHRoaXMubGFzdFJlZmlsbCA9IERhdGUubm93KCk7XG4gICAgdGhpcy5sYXN0UmVxdWVzdCA9IDA7XG4gICAgdGhpcy5taW5EZWxheSA9IGNvbmZpZy5taW5EZWxheU1zIHx8IDA7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgYSByZXF1ZXN0IGlzIGFsbG93ZWQgdW5kZXIgdGhlIHJhdGUgbGltaXRcbiAgICogQHJldHVybnMgU3RhdHVzIG9iamVjdCBpbmRpY2F0aW5nIGlmIHJlcXVlc3QgaXMgYWxsb3dlZFxuICAgKi9cbiAgY2hlY2tMaW1pdCgpOiBSYXRlTGltaXRTdGF0dXMge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgXG4gICAgLy8gUmVmaWxsIHRva2VucyBiYXNlZCBvbiB0aW1lIGVsYXBzZWRcbiAgICB0aGlzLnJlZmlsbFRva2Vucyhub3cpO1xuXG4gICAgLy8gQ2hlY2sgbWluaW11bSBkZWxheSBiZXR3ZWVuIHJlcXVlc3RzXG4gICAgaWYgKHRoaXMubWluRGVsYXkgPiAwICYmIHRoaXMubGFzdFJlcXVlc3QgPiAwKSB7XG4gICAgICBjb25zdCB0aW1lU2luY2VMYXN0UmVxdWVzdCA9IG5vdyAtIHRoaXMubGFzdFJlcXVlc3Q7XG4gICAgICBpZiAodGltZVNpbmNlTGFzdFJlcXVlc3QgPCB0aGlzLm1pbkRlbGF5KSB7XG4gICAgICAgIGNvbnN0IHJldHJ5QWZ0ZXJNcyA9IE1hdGgubWF4KDEsIHRoaXMubWluRGVsYXkgLSB0aW1lU2luY2VMYXN0UmVxdWVzdCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgYWxsb3dlZDogZmFsc2UsXG4gICAgICAgICAgcmV0cnlBZnRlck1zLFxuICAgICAgICAgIHJlbWFpbmluZ1Rva2VuczogTWF0aC5mbG9vcih0aGlzLnRva2VucyksXG4gICAgICAgICAgcmVzZXRUaW1lOiBuZXcgRGF0ZShub3cgKyByZXRyeUFmdGVyTXMpXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgaWYgd2UgaGF2ZSB0b2tlbnMgYXZhaWxhYmxlXG4gICAgaWYgKHRoaXMudG9rZW5zIDwgMSkge1xuICAgICAgLy8gQ2FsY3VsYXRlIHdoZW4gdGhlIG5leHQgdG9rZW4gd2lsbCBiZSBhdmFpbGFibGVcbiAgICAgIGNvbnN0IHRva2Vuc05lZWRlZCA9IDEgLSB0aGlzLnRva2VucztcbiAgICAgIGNvbnN0IHJhd01zID0gdG9rZW5zTmVlZGVkIC8gdGhpcy5yZWZpbGxSYXRlO1xuICAgICAgLy8gQ2xhbXAgdG8gc2FmZSBib3VuZHM6IGF0IGxlYXN0IDFtcywgYXQgbW9zdCAxIGhvdXIsIGFuZCBhbHdheXMgZmluaXRlXG4gICAgICBjb25zdCByZXRyeUFmdGVyTXMgPSBNYXRoLm1heCgxLCBNYXRoLm1pbigzXzYwMF8wMDAsIE51bWJlci5pc0Zpbml0ZShyYXdNcykgPyBNYXRoLmNlaWwocmF3TXMpIDogNjBfMDAwKSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGFsbG93ZWQ6IGZhbHNlLFxuICAgICAgICByZXRyeUFmdGVyTXMsXG4gICAgICAgIHJlbWFpbmluZ1Rva2VuczogMCxcbiAgICAgICAgcmVzZXRUaW1lOiBuZXcgRGF0ZShub3cgKyByZXRyeUFmdGVyTXMpXG4gICAgICB9O1xuICAgIH1cblxuICAgIC8vIFJlcXVlc3QgaXMgYWxsb3dlZFxuICAgIHJldHVybiB7XG4gICAgICBhbGxvd2VkOiB0cnVlLFxuICAgICAgcmVtYWluaW5nVG9rZW5zOiBNYXRoLmZsb29yKHRoaXMudG9rZW5zKSxcbiAgICAgIHJlc2V0VGltZTogdGhpcy5nZXRSZXNldFRpbWUoKVxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ29uc3VtZSBhIHRva2VuIGZvciBhbiBhbGxvd2VkIHJlcXVlc3RcbiAgICogU2hvdWxkIGJlIGNhbGxlZCBhZnRlciBjaGVja0xpbWl0KCkgcmV0dXJucyBhbGxvd2VkOiB0cnVlXG4gICAqL1xuICBjb25zdW1lVG9rZW4oKTogdm9pZCB7XG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICB0aGlzLnJlZmlsbFRva2Vucyhub3cpO1xuICAgIFxuICAgIGlmICh0aGlzLnRva2VucyA+PSAxKSB7XG4gICAgICB0aGlzLnRva2VucyAtPSAxO1xuICAgICAgdGhpcy5sYXN0UmVxdWVzdCA9IG5vdztcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IGN1cnJlbnQgcmF0ZSBsaW1pdCBzdGF0dXMgd2l0aG91dCBjb25zdW1pbmcgYSB0b2tlblxuICAgKi9cbiAgZ2V0U3RhdHVzKCk6IFJhdGVMaW1pdFN0YXR1cyB7XG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICB0aGlzLnJlZmlsbFRva2Vucyhub3cpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGFsbG93ZWQ6IHRoaXMudG9rZW5zID49IDEsXG4gICAgICByZW1haW5pbmdUb2tlbnM6IE1hdGguZmxvb3IodGhpcy50b2tlbnMpLFxuICAgICAgcmVzZXRUaW1lOiB0aGlzLmdldFJlc2V0VGltZSgpXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNldCB0aGUgcmF0ZSBsaW1pdGVyIHRvIGZ1bGwgY2FwYWNpdHlcbiAgICogVXNlZnVsIGZvciB0ZXN0aW5nIG9yIG1hbnVhbCBpbnRlcnZlbnRpb25cbiAgICovXG4gIHJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMudG9rZW5zID0gdGhpcy5tYXhUb2tlbnM7XG4gICAgdGhpcy5sYXN0UmVmaWxsID0gRGF0ZS5ub3coKTtcbiAgICB0aGlzLmxhc3RSZXF1ZXN0ID0gMDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWZpbGwgdG9rZW5zIGJhc2VkIG9uIHRpbWUgZWxhcHNlZFxuICAgKi9cbiAgcHJpdmF0ZSByZWZpbGxUb2tlbnMobm93OiBudW1iZXIpOiB2b2lkIHtcbiAgICBjb25zdCB0aW1lU2luY2VMYXN0UmVmaWxsID0gbm93IC0gdGhpcy5sYXN0UmVmaWxsO1xuICAgIGNvbnN0IHRva2Vuc1RvQWRkID0gdGltZVNpbmNlTGFzdFJlZmlsbCAqIHRoaXMucmVmaWxsUmF0ZTtcbiAgICBcbiAgICB0aGlzLnRva2VucyA9IE1hdGgubWluKHRoaXMubWF4VG9rZW5zLCB0aGlzLnRva2VucyArIHRva2Vuc1RvQWRkKTtcbiAgICB0aGlzLmxhc3RSZWZpbGwgPSBub3c7XG4gIH1cblxuICAvKipcbiAgICogQ2FsY3VsYXRlIHdoZW4gdGhlIHJhdGUgbGltaXQgd2luZG93IHdpbGwgcmVzZXRcbiAgICovXG4gIHByaXZhdGUgZ2V0UmVzZXRUaW1lKCk6IERhdGUge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3QgdG9rZW5zVG9GdWxsID0gdGhpcy5tYXhUb2tlbnMgLSB0aGlzLnRva2VucztcbiAgICBjb25zdCBtc1VudGlsRnVsbCA9IHRva2Vuc1RvRnVsbCAvIHRoaXMucmVmaWxsUmF0ZTtcbiAgICByZXR1cm4gbmV3IERhdGUobm93ICsgbXNVbnRpbEZ1bGwpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBodW1hbi1yZWFkYWJsZSByYXRlIGxpbWl0IGluZm9ybWF0aW9uXG4gICAqL1xuICB0b1N0cmluZygpOiBzdHJpbmcge1xuICAgIGNvbnN0IHN0YXR1cyA9IHRoaXMuZ2V0U3RhdHVzKCk7XG4gICAgcmV0dXJuIGBSYXRlTGltaXQ6ICR7c3RhdHVzLnJlbWFpbmluZ1Rva2Vuc30vJHt0aGlzLm1heFRva2Vuc30gdG9rZW5zLCBgICtcbiAgICAgICAgICAgYHJlc2V0cyBhdCAke3N0YXR1cy5yZXNldFRpbWUudG9JU09TdHJpbmcoKX1gO1xuICB9XG59XG5cbi8qKlxuICogRmFjdG9yeSBmdW5jdGlvbiB0byBjcmVhdGUgY29tbW9uIHJhdGUgbGltaXRlcnNcbiAqL1xuZXhwb3J0IGNsYXNzIFJhdGVMaW1pdGVyRmFjdG9yeSB7XG4gIC8qKlxuICAgKiBHaXRIdWIgQVBJIHJhdGUgbGltaXRlciAoNjAgcmVxdWVzdHMgcGVyIGhvdXIgZm9yIHVuYXV0aGVudGljYXRlZClcbiAgICovXG4gIHN0YXRpYyBjcmVhdGVHaXRIdWJMaW1pdGVyKCk6IFJhdGVMaW1pdGVyIHtcbiAgICByZXR1cm4gbmV3IFJhdGVMaW1pdGVyKHtcbiAgICAgIG1heFJlcXVlc3RzOiA2MCxcbiAgICAgIHdpbmRvd01zOiA2MCAqIDYwICogMTAwMCwgLy8gMSBob3VyXG4gICAgICBtaW5EZWxheU1zOiAxMDAwIC8vIDEgc2Vjb25kIG1pbmltdW0gYmV0d2VlbiByZXF1ZXN0c1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnNlcnZhdGl2ZSByYXRlIGxpbWl0ZXIgZm9yIHVwZGF0ZSBjaGVja3NcbiAgICogQWxsb3dzIDEwIGNoZWNrcyBwZXIgaG91ciB3aXRoIDMwIHNlY29uZCBtaW5pbXVtIGRlbGF5XG4gICAqL1xuICBzdGF0aWMgY3JlYXRlVXBkYXRlQ2hlY2tMaW1pdGVyKCk6IFJhdGVMaW1pdGVyIHtcbiAgICByZXR1cm4gbmV3IFJhdGVMaW1pdGVyKHtcbiAgICAgIG1heFJlcXVlc3RzOiAxMCxcbiAgICAgIHdpbmRvd01zOiA2MCAqIDYwICogMTAwMCwgLy8gMSBob3VyXG4gICAgICBtaW5EZWxheU1zOiAzMCAqIDEwMDAgLy8gMzAgc2Vjb25kcyBiZXR3ZWVuIGNoZWNrc1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0cmljdCByYXRlIGxpbWl0ZXIgZm9yIHNlbnNpdGl2ZSBvcGVyYXRpb25zXG4gICAqIEFsbG93cyA1IHJlcXVlc3RzIHBlciBob3VyIHdpdGggMSBtaW51dGUgbWluaW11bSBkZWxheVxuICAgKi9cbiAgc3RhdGljIGNyZWF0ZVN0cmljdExpbWl0ZXIoKTogUmF0ZUxpbWl0ZXIge1xuICAgIHJldHVybiBuZXcgUmF0ZUxpbWl0ZXIoe1xuICAgICAgbWF4UmVxdWVzdHM6IDUsXG4gICAgICB3aW5kb3dNczogNjAgKiA2MCAqIDEwMDAsIC8vIDEgaG91clxuICAgICAgbWluRGVsYXlNczogNjAgKiAxMDAwIC8vIDEgbWludXRlIGJldHdlZW4gcmVxdWVzdHNcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSYXRlIGxpbWl0ZXIgZm9yIHBlcm1pc3Npb25fcHJvbXB0IGV2YWx1YXRpb25zIChJc3N1ZSAjNjI1IFBoYXNlIDQpLlxuICAgKiAxMDAgcmVxdWVzdHMgcGVyIDYwLXNlY29uZCB3aW5kb3cg4oCUIGhpZ2ggdGhyb3VnaHB1dCBmb3IgcmFwaWQgdG9vbCBjYWxscy5cbiAgICovXG4gIHN0YXRpYyBjcmVhdGVQZXJtaXNzaW9uUHJvbXB0TGltaXRlcihtYXhSZXF1ZXN0cyA9IDEwMCwgd2luZG93TXMgPSA2MF8wMDApOiBSYXRlTGltaXRlciB7XG4gICAgcmV0dXJuIG5ldyBSYXRlTGltaXRlcih7IG1heFJlcXVlc3RzLCB3aW5kb3dNcyB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSYXRlIGxpbWl0ZXIgZm9yIENMSSBhcHByb3ZhbCByZWNvcmQgY3JlYXRpb24gKElzc3VlICM2MjUgUGhhc2UgNCkuXG4gICAqIERlZmF1bHQgMjAgcmVxdWVzdHMgcGVyIDYwLXNlY29uZCB3aW5kb3cg4oCUIHByZXZlbnRzIGFwcHJvdmFsIHJlcXVlc3QgZmxvb2RpbmcuXG4gICAqIENvbmZpZ3VyYWJsZSB2aWEgcGFyYW1ldGVycyAoTUNQQVFMSGFuZGxlciByZWFkcyBmcm9tIGVudiB2YXJzKS5cbiAgICovXG4gIHN0YXRpYyBjcmVhdGVDbGlBcHByb3ZhbExpbWl0ZXIobWF4UmVxdWVzdHMgPSAyMCwgd2luZG93TXMgPSA2MF8wMDApOiBSYXRlTGltaXRlciB7XG4gICAgcmV0dXJuIG5ldyBSYXRlTGltaXRlcih7IG1heFJlcXVlc3RzLCB3aW5kb3dNcyB9KTtcbiAgfVxufSJdfQ==