@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.
211 lines • 29.2 kB
JavaScript
/**
* Central coordinator for the Unified Logging System.
*
* Accepts UnifiedLogEntry objects from all sources, enforces level filtering
* and entry size limits, routes entries to registered sinks, and manages
* flush lifecycle (timer + buffer threshold + immediate flush for security).
*
* See docs/LOGGING-DESIGN.md §4.2 for the full design.
*/
import { EvictingQueue } from '../utils/EvictingQueue.js';
import { LOG_LEVEL_PRIORITY, } from './types.js';
export class LogManager {
sinks = [];
buffer;
config;
flushTimer = null;
entryCounter = 0;
dropCount = 0;
previousBufferSize = 0;
// Sliding-window rate limiter for immediate flushes (per-second window)
immediateFlushWindowStart = 0;
immediateFlushCount = 0;
constructor(config) {
this.config = config;
this.buffer = new EvictingQueue(config.bufferSize);
this.startFlushTimer();
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Route a log entry to all registered sinks.
*
* - Skips entries below the minimum log level.
* - Enforces max entry size (truncates `data`, sets `_truncated`).
* - Security warn/error entries trigger an immediate (rate-limited) flush.
* - Other entries are buffered; a flush is triggered when the buffer is full.
*/
log(entry) {
// Level gate
if (LOG_LEVEL_PRIORITY[entry.level] < LOG_LEVEL_PRIORITY[this.config.logLevel]) {
return;
}
// Enforce entry size limit
const enforced = this.enforceEntrySize(entry);
// Route to every registered sink
for (const sink of this.sinks) {
sink.write(enforced);
}
// Determine flush strategy
const needsImmediateFlush = enforced.category === 'security' &&
(enforced.level === 'warn' || enforced.level === 'error');
if (needsImmediateFlush && this.canImmediateFlush()) {
// Fire-and-forget — logging must never block the caller
void this.flush();
}
else {
// Track buffer occupancy for backpressure reporting
const wasFull = this.buffer.size === this.buffer.capacity;
this.buffer.push(enforced);
// Detect eviction (buffer was at capacity before push)
if (wasFull) {
this.dropCount++;
}
// Buffer-full flush — only on the transition to full, not while already full.
// The periodic flush timer handles steady-state draining.
if (!wasFull && this.buffer.size >= this.config.bufferSize) {
void this.flush();
}
}
}
/** Register an output sink. */
registerSink(sink) {
this.sinks.push(sink);
}
/** Flush all registered sinks and report drops if any occurred. */
async flush() {
// Accumulate drops and only report when meaningful.
// Single-entry evictions are normal ring-buffer cycling under load;
// only warn when drops exceed 10% of buffer capacity per flush interval.
const dropThreshold = Math.max(5, Math.ceil(this.config.bufferSize * 0.1));
if (this.dropCount >= dropThreshold) {
const dropEntry = this.createMetaEntry('warn', `Backpressure: ${this.dropCount} buffered entries evicted since last flush`, { droppedCount: this.dropCount });
for (const sink of this.sinks) {
sink.write(dropEntry);
}
this.dropCount = 0;
}
const flushPromises = this.sinks.map((sink) => sink.flush());
await Promise.allSettled(flushPromises);
}
/** Graceful shutdown: clear timer, flush, then close all sinks. */
async close() {
this.stopFlushTimer();
await this.flush();
const closePromises = this.sinks.map((sink) => sink.close());
await Promise.allSettled(closePromises);
}
/**
* Generate a unique log entry ID.
* Format: `LOG-{timestamp}-{counter}`
*/
generateId() {
return `LOG-${Date.now()}-${this.entryCounter++}`;
}
// ---------------------------------------------------------------------------
// Internals
// ---------------------------------------------------------------------------
startFlushTimer() {
if (this.config.flushIntervalMs > 0) {
this.flushTimer = setInterval(() => {
void this.flush();
}, this.config.flushIntervalMs);
// Allow the process to exit even if the timer is still running
if (this.flushTimer && typeof this.flushTimer === 'object' && 'unref' in this.flushTimer) {
this.flushTimer.unref();
}
}
}
stopFlushTimer() {
if (this.flushTimer !== null) {
clearInterval(this.flushTimer);
this.flushTimer = null;
}
}
/**
* Sliding-window rate limiter for immediate flushes.
* Returns `true` if an immediate flush is allowed right now.
*/
canImmediateFlush() {
const now = Date.now();
const windowMs = 1000;
if (now - this.immediateFlushWindowStart >= windowMs) {
// New window
this.immediateFlushWindowStart = now;
this.immediateFlushCount = 1;
return true;
}
if (this.immediateFlushCount < this.config.immediateFlushRate) {
this.immediateFlushCount++;
return true;
}
// Rate exceeded — demote to buffered path (caller will buffer instead)
return false;
}
/**
* Enforce `maxEntrySize` by truncating the `data` field if necessary.
* Returns the original entry unmodified when under the limit, or a
* shallow copy with `data` replaced when truncation is needed.
*/
enforceEntrySize(entry) {
if (!entry.data)
return entry;
const serialized = JSON.stringify(entry);
if (serialized.length <= this.config.maxEntrySize)
return entry;
const originalSize = serialized.length;
// Emit a warn meta-entry about the truncation
const truncationNotice = this.createMetaEntry('warn', `Entry truncated: original size ${originalSize} bytes exceeds limit ${this.config.maxEntrySize}`, { originalSize, maxSize: this.config.maxEntrySize, entryId: entry.id });
for (const sink of this.sinks) {
sink.write(truncationNotice);
}
// Return a copy with data replaced by a truncation marker
return {
...entry,
data: { _truncated: true, _originalSize: originalSize },
};
}
/** Create a meta/system log entry from the LogManager itself. */
createMetaEntry(level, message, data) {
return {
id: this.generateId(),
timestamp: new Date().toISOString(),
category: 'application',
level,
source: 'LogManager',
message,
data,
};
}
}
// ---------------------------------------------------------------------------
// Helper: build LogManagerConfig from validated env vars
// ---------------------------------------------------------------------------
/**
* Map the flat env object (from Zod-parsed `process.env`) to a typed
* `LogManagerConfig`. Keeps the mapping in one place so the DI container
* only needs `buildLogManagerConfig(env)`.
*/
export function buildLogManagerConfig(envVars) {
return {
logDir: envVars.DOLLHOUSE_LOG_DIR,
logFormat: envVars.DOLLHOUSE_LOG_FORMAT,
retentionDays: envVars.DOLLHOUSE_LOG_RETENTION_DAYS,
securityRetentionDays: envVars.DOLLHOUSE_LOG_SECURITY_RETENTION_DAYS,
flushIntervalMs: envVars.DOLLHOUSE_LOG_FLUSH_INTERVAL_MS,
bufferSize: envVars.DOLLHOUSE_LOG_BUFFER_SIZE,
memoryCapacity: envVars.DOLLHOUSE_LOG_MEMORY_CAPACITY,
memoryAppCapacity: envVars.DOLLHOUSE_LOG_MEMORY_APP_CAPACITY,
memorySecurityCapacity: envVars.DOLLHOUSE_LOG_MEMORY_SECURITY_CAPACITY,
memoryPerfCapacity: envVars.DOLLHOUSE_LOG_MEMORY_PERF_CAPACITY,
memoryTelemetryCapacity: envVars.DOLLHOUSE_LOG_MEMORY_TELEMETRY_CAPACITY,
maxEntrySize: envVars.DOLLHOUSE_LOG_MAX_ENTRY_SIZE,
immediateFlushRate: envVars.DOLLHOUSE_LOG_IMMEDIATE_FLUSH_RATE,
fileMaxSize: envVars.DOLLHOUSE_LOG_FILE_MAX_SIZE,
maxDirSizeBytes: envVars.DOLLHOUSE_LOG_MAX_DIR_SIZE_BYTES,
maxFilesPerCategory: envVars.DOLLHOUSE_LOG_MAX_FILES_PER_CATEGORY,
logLevel: envVars.LOG_LEVEL,
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTG9nTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sb2dnaW5nL0xvZ01hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFFSCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDMUQsT0FBTyxFQUtMLGtCQUFrQixHQUNuQixNQUFNLFlBQVksQ0FBQztBQUVwQixNQUFNLE9BQU8sVUFBVTtJQUNKLEtBQUssR0FBZSxFQUFFLENBQUM7SUFDdkIsTUFBTSxDQUFpQztJQUN2QyxNQUFNLENBQW1CO0lBQ2xDLFVBQVUsR0FBMEMsSUFBSSxDQUFDO0lBQ3pELFlBQVksR0FBRyxDQUFDLENBQUM7SUFDakIsU0FBUyxHQUFHLENBQUMsQ0FBQztJQUNkLGtCQUFrQixHQUFHLENBQUMsQ0FBQztJQUUvQix3RUFBd0U7SUFDaEUseUJBQXlCLEdBQUcsQ0FBQyxDQUFDO0lBQzlCLG1CQUFtQixHQUFHLENBQUMsQ0FBQztJQUVoQyxZQUFZLE1BQXdCO1FBQ2xDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxhQUFhLENBQWtCLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDekIsQ0FBQztJQUVELDhFQUE4RTtJQUM5RSxhQUFhO0lBQ2IsOEVBQThFO0lBRTlFOzs7Ozs7O09BT0c7SUFDSCxHQUFHLENBQUMsS0FBc0I7UUFDeEIsYUFBYTtRQUNiLElBQUksa0JBQWtCLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLGtCQUFrQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUMvRSxPQUFPO1FBQ1QsQ0FBQztRQUVELDJCQUEyQjtRQUMzQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMs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QUFFLE9BQU8sQ0FBQyxnQ0FBZ0M7UUFDekQsbUJBQW1CLEVBQUUsT0FBTyxDQUFDLG9DQUFvQztRQUNqRSxRQUFRLEVBQUUsT0FBTyxDQUFDLFNBQVM7S0FDNUIsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENlbnRyYWwgY29vcmRpbmF0b3IgZm9yIHRoZSBVbmlmaWVkIExvZ2dpbmcgU3lzdGVtLlxuICpcbiAqIEFjY2VwdHMgVW5pZmllZExvZ0VudHJ5IG9iamVjdHMgZnJvbSBhbGwgc291cmNlcywgZW5mb3JjZXMgbGV2ZWwgZmlsdGVyaW5nXG4gKiBhbmQgZW50cnkgc2l6ZSBsaW1pdHMsIHJvdXRlcyBlbnRyaWVzIHRvIHJlZ2lzdGVyZWQgc2lua3MsIGFuZCBtYW5hZ2VzXG4gKiBmbHVzaCBsaWZlY3ljbGUgKHRpbWVyICsgYnVmZmVyIHRocmVzaG9sZCArIGltbWVkaWF0ZSBmbHVzaCBmb3Igc2VjdXJpdHkpLlxuICpcbiAqIFNlZSBkb2NzL0xPR0dJTkctREVTSUdOLm1kIMKnNC4yIGZvciB0aGUgZnVsbCBkZXNpZ24uXG4gKi9cblxuaW1wb3J0IHsgRXZpY3RpbmdRdWV1ZSB9IGZyb20gJy4uL3V0aWxzL0V2aWN0aW5nUXVldWUuanMnO1xuaW1wb3J0IHtcbiAgdHlwZSBVbmlmaWVkTG9nRW50cnksXG4gIHR5cGUgSUxvZ1NpbmssXG4gIHR5cGUgTG9nTGV2ZWwsXG4gIHR5cGUgTG9nTWFuYWdlckNvbmZpZyxcbiAgTE9HX0xFVkVMX1BSSU9SSVRZLFxufSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IGNsYXNzIExvZ01hbmFnZXIge1xuICBwcml2YXRlIHJlYWRvbmx5IHNpbmtzOiBJTG9nU2lua1tdID0gW107XG4gIHByaXZhdGUgcmVhZG9ubHkgYnVmZmVyOiBFdmljdGluZ1F1ZXVlPFVuaWZpZWRMb2dFbnRyeT47XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBMb2dNYW5hZ2VyQ29uZmlnO1xuICBwcml2YXRlIGZsdXNoVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldEludGVydmFsPiB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGVudHJ5Q291bnRlciA9IDA7XG4gIHByaXZhdGUgZHJvcENvdW50ID0gMDtcbiAgcHJpdmF0ZSBwcmV2aW91c0J1ZmZlclNpemUgPSAwO1xuXG4gIC8vIFNsaWRpbmctd2luZG93IHJhdGUgbGltaXRlciBmb3IgaW1tZWRpYXRlIGZsdXNoZXMgKHBlci1zZWNvbmQgd2luZG93KVxuICBwcml2YXRlIGltbWVkaWF0ZUZsdXNoV2luZG93U3RhcnQgPSAwO1xuICBwcml2YXRlIGltbWVkaWF0ZUZsdXNoQ291bnQgPSAwO1xuXG4gIGNvbnN0cnVjdG9yKGNvbmZpZzogTG9nTWFuYWdlckNvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMuYnVmZmVyID0gbmV3IEV2aWN0aW5nUXVldWU8VW5pZmllZExvZ0VudHJ5Pihjb25maWcuYnVmZmVyU2l6ZSk7XG4gICAgdGhpcy5zdGFydEZsdXNoVGltZXIoKTtcbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBQdWJsaWMgQVBJXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIC8qKlxuICAgKiBSb3V0ZSBhIGxvZyBlbnRyeSB0byBhbGwgcmVnaXN0ZXJlZCBzaW5rcy5cbiAgICpcbiAgICogLSBTa2lwcyBlbnRyaWVzIGJlbG93IHRoZSBtaW5pbXVtIGxvZyBsZXZlbC5cbiAgICogLSBFbmZvcmNlcyBtYXggZW50cnkgc2l6ZSAodHJ1bmNhdGVzIGBkYXRhYCwgc2V0cyBgX3RydW5jYXRlZGApLlxuICAgKiAtIFNlY3VyaXR5IHdhcm4vZXJyb3IgZW50cmllcyB0cmlnZ2VyIGFuIGltbWVkaWF0ZSAocmF0ZS1saW1pdGVkKSBmbHVzaC5cbiAgICogLSBPdGhlciBlbnRyaWVzIGFyZSBidWZmZXJlZDsgYSBmbHVzaCBpcyB0cmlnZ2VyZWQgd2hlbiB0aGUgYnVmZmVyIGlzIGZ1bGwuXG4gICAqL1xuICBsb2coZW50cnk6IFVuaWZpZWRMb2dFbnRyeSk6IHZvaWQge1xuICAgIC8vIExldmVsIGdhdGVcbiAgICBpZiAoTE9HX0xFVkVMX1BSSU9SSVRZW2VudHJ5LmxldmVsXSA8IExPR19MRVZFTF9QUklPUklUWVt0aGlzLmNvbmZpZy5sb2dMZXZlbF0pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBFbmZvcmNlIGVudHJ5IHNpemUgbGltaXRcbiAgICBjb25zdCBlbmZvcmNlZCA9IHRoaXMuZW5mb3JjZUVudHJ5U2l6ZShlbnRyeSk7XG5cbiAgICAvLyBSb3V0ZSB0byBldmVyeSByZWdpc3RlcmVkIHNpbmtcbiAgICBmb3IgKGNvbnN0IHNpbmsgb2YgdGhpcy5zaW5rcykge1xuICAgICAgc2luay53cml0ZShlbmZvcmNlZCk7XG4gICAgfVxuXG4gICAgLy8gRGV0ZXJtaW5lIGZsdXNoIHN0cmF0ZWd5XG4gICAgY29uc3QgbmVlZHNJbW1lZGlhdGVGbHVzaCA9XG4gICAgICBlbmZvcmNlZC5jYXRlZ29yeSA9PT0gJ3NlY3VyaXR5JyAmJlxuICAgICAgKGVuZm9yY2VkLmxldmVsID09PSAnd2FybicgfHwgZW5mb3JjZWQubGV2ZWwgPT09ICdlcnJvcicpO1xuXG4gICAgaWYgKG5lZWRzSW1tZWRpYXRlRmx1c2ggJiYgdGhpcy5jYW5JbW1lZGlhdGVGbHVzaCgpKSB7XG4gICAgICAvLyBGaXJlLWFuZC1mb3JnZXQg4oCUIGxvZ2dpbmcgbXVzdCBuZXZlciBibG9jayB0aGUgY2FsbGVyXG4gICAgICB2b2lkIHRoaXMuZmx1c2goKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gVHJhY2sgYnVmZmVyIG9jY3VwYW5jeSBmb3IgYmFja3ByZXNzdXJlIHJlcG9ydGluZ1xuICAgICAgY29uc3Qgd2FzRnVsbCA9IHRoaXMuYnVmZmVyLnNpemUgPT09IHRoaXMuYnVmZmVyLmNhcGFjaXR5O1xuICAgICAgdGhpcy5idWZmZXIucHVzaChlbmZvcmNlZCk7XG5cbiAgICAgIC8vIERldGVjdCBldmljdGlvbiAoYnVmZmVyIHdhcyBhdCBjYXBhY2l0eSBiZWZvcmUgcHVzaClcbiAgICAgIGlmICh3YXNGdWxsKSB7XG4gICAgICAgIHRoaXMuZHJvcENvdW50Kys7XG4gICAgICB9XG5cbiAgICAgIC8vIEJ1ZmZlci1mdWxsIGZsdXNoIOKAlCBvbmx5IG9uIHRoZSB0cmFuc2l0aW9uIHRvIGZ1bGwsIG5vdCB3aGlsZSBhbHJlYWR5IGZ1bGwuXG4gICAgICAvLyBUaGUgcGVyaW9kaWMgZmx1c2ggdGltZXIgaGFuZGxlcyBzdGVhZHktc3RhdGUgZHJhaW5pbmcuXG4gICAgICBpZiAoIXdhc0Z1bGwgJiYgdGhpcy5idWZmZXIuc2l6ZSA+PSB0aGlzLmNvbmZpZy5idWZmZXJTaXplKSB7XG4gICAgICAgIHZvaWQgdGhpcy5mbHVzaCgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBSZWdpc3RlciBhbiBvdXRwdXQgc2luay4gKi9cbiAgcmVnaXN0ZXJTaW5rKHNpbms6IElMb2dTaW5rKTogdm9pZCB7XG4gICAgdGhpcy5zaW5rcy5wdXNoKHNpbmspO1xuICB9XG5cbiAgLyoqIEZsdXNoIGFsbCByZWdpc3RlcmVkIHNpbmtzIGFuZCByZXBvcnQgZHJvcHMgaWYgYW55IG9jY3VycmVkLiAqL1xuICBhc3luYyBmbHVzaCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBBY2N1bXVsYXRlIGRyb3BzIGFuZCBvbmx5IHJlcG9ydCB3aGVuIG1lYW5pbmdmdWwuXG4gICAgLy8gU2luZ2xlLWVudHJ5IGV2aWN0aW9ucyBhcmUgbm9ybWFsIHJpbmctYnVmZmVyIGN5Y2xpbmcgdW5kZXIgbG9hZDtcbiAgICAvLyBvbmx5IHdhcm4gd2hlbiBkcm9wcyBleGNlZWQgMTAlIG9mIGJ1ZmZlciBjYXBhY2l0eSBwZXIgZmx1c2ggaW50ZXJ2YWwuXG4gICAgY29uc3QgZHJvcFRocmVzaG9sZCA9IE1hdGgubWF4KDUsIE1hdGguY2VpbCh0aGlzLmNvbmZpZy5idWZmZXJTaXplICogMC4xKSk7XG4gICAgaWYgKHRoaXMuZHJvcENvdW50ID49IGRyb3BUaHJlc2hvbGQpIHtcbiAgICAgIGNvbnN0IGRyb3BFbnRyeSA9IHRoaXMuY3JlYXRlTWV0YUVudHJ5KFxuICAgICAgICAnd2FybicsXG4gICAgICAgIGBCYWNrcHJlc3N1cmU6ICR7dGhpcy5kcm9wQ291bnR9IGJ1ZmZlcmVkIGVudHJpZXMgZXZpY3RlZCBzaW5jZSBsYXN0IGZsdXNoYCxcbiAgICAgICAgeyBkcm9wcGVkQ291bnQ6IHRoaXMuZHJvcENvdW50IH0sXG4gICAgICApO1xuICAgICAgZm9yIChjb25zdCBzaW5rIG9mIHRoaXMuc2lua3MpIHtcbiAgICAgICAgc2luay53cml0ZShkcm9wRW50cnkpO1xuICAgICAgfVxuICAgICAgdGhpcy5kcm9wQ291bnQgPSAwO1xuICAgIH1cblxuICAgIGNvbnN0IGZsdXNoUHJvbWlzZXMgPSB0aGlzLnNpbmtzLm1hcCgoc2luaykgPT4gc2luay5mbHVzaCgpKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoZmx1c2hQcm9taXNlcyk7XG4gIH1cblxuICAvKiogR3JhY2VmdWwgc2h1dGRvd246IGNsZWFyIHRpbWVyLCBmbHVzaCwgdGhlbiBjbG9zZSBhbGwgc2lua3MuICovXG4gIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuc3RvcEZsdXNoVGltZXIoKTtcbiAgICBhd2FpdCB0aGlzLmZsdXNoKCk7XG4gICAgY29uc3QgY2xvc2VQcm9taXNlcyA9IHRoaXMuc2lua3MubWFwKChzaW5rKSA9PiBzaW5rLmNsb3NlKCkpO1xuICAgIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChjbG9zZVByb21pc2VzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBhIHVuaXF1ZSBsb2cgZW50cnkgSUQuXG4gICAqIEZvcm1hdDogYExPRy17dGltZXN0YW1wfS17Y291bnRlcn1gXG4gICAqL1xuICBnZW5lcmF0ZUlkKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGBMT0ctJHtEYXRlLm5vdygpfS0ke3RoaXMuZW50cnlDb3VudGVyKyt9YDtcbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBJbnRlcm5hbHNcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgcHJpdmF0ZSBzdGFydEZsdXNoVGltZXIoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmZsdXNoSW50ZXJ2YWxNcyA+IDApIHtcbiAgICAgIHRoaXMuZmx1c2hUaW1lciA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgdm9pZCB0aGlzLmZsdXNoKCk7XG4gICAgICB9LCB0aGlzLmNvbmZpZy5mbHVzaEludGVydmFsTXMpO1xuXG4gICAgICAvLyBBbGxvdyB0aGUgcHJvY2VzcyB0byBleGl0IGV2ZW4gaWYgdGhlIHRpbWVyIGlzIHN0aWxsIHJ1bm5pbmdcbiAgICAgIGlmICh0aGlzLmZsdXNoVGltZXIgJiYgdHlwZW9mIHRoaXMuZmx1c2hUaW1lciA9PT0gJ29iamVjdCcgJiYgJ3VucmVmJyBpbiB0aGlzLmZsdXNoVGltZXIpIHtcbiAgICAgICAgdGhpcy5mbHVzaFRpbWVyLnVucmVmKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdG9wRmx1c2hUaW1lcigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5mbHVzaFRpbWVyICE9PSBudWxsKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMuZmx1c2hUaW1lcik7XG4gICAgICB0aGlzLmZsdXNoVGltZXIgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTbGlkaW5nLXdpbmRvdyByYXRlIGxpbWl0ZXIgZm9yIGltbWVkaWF0ZSBmbHVzaGVzLlxuICAgKiBSZXR1cm5zIGB0cnVlYCBpZiBhbiBpbW1lZGlhdGUgZmx1c2ggaXMgYWxsb3dlZCByaWdodCBub3cuXG4gICAqL1xuICBwcml2YXRlIGNhbkltbWVkaWF0ZUZsdXNoKCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3Qgd2luZG93TXMgPSAxMDAwO1xuXG4gICAgaWYgKG5vdyAtIHRoaXMuaW1tZWRpYXRlRmx1c2hXaW5kb3dTdGFydCA+PSB3aW5kb3dNcykge1xuICAgICAgLy8gTmV3IHdpbmRvd1xuICAgICAgdGhpcy5pbW1lZGlhdGVGbHVzaFdpbmRvd1N0YXJ0ID0gbm93O1xuICAgICAgdGhpcy5pbW1lZGlhdGVGbHVzaENvdW50ID0gMTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmltbWVkaWF0ZUZsdXNoQ291bnQgPCB0aGlzLmNvbmZpZy5pbW1lZGlhdGVGbHVzaFJhdGUpIHtcbiAgICAgIHRoaXMuaW1tZWRpYXRlRmx1c2hDb3VudCsrO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgLy8gUmF0ZSBleGNlZWRlZCDigJQgZGVtb3RlIHRvIGJ1ZmZlcmVkIHBhdGggKGNhbGxlciB3aWxsIGJ1ZmZlciBpbnN0ZWFkKVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmZvcmNlIGBtYXhFbnRyeVNpemVgIGJ5IHRydW5jYXRpbmcgdGhlIGBkYXRhYCBmaWVsZCBpZiBuZWNlc3NhcnkuXG4gICAqIFJldHVybnMgdGhlIG9yaWdpbmFsIGVudHJ5IHVubW9kaWZpZWQgd2hlbiB1bmRlciB0aGUgbGltaXQsIG9yIGFcbiAgICogc2hhbGxvdyBjb3B5IHdpdGggYGRhdGFgIHJlcGxhY2VkIHdoZW4gdHJ1bmNhdGlvbiBpcyBuZWVkZWQuXG4gICAqL1xuICBwcml2YXRlIGVuZm9yY2VFbnRyeVNpemUoZW50cnk6IFVuaWZpZWRMb2dFbnRyeSk6IFVuaWZpZWRMb2dFbnRyeSB7XG4gICAgaWYgKCFlbnRyeS5kYXRhKSByZXR1cm4gZW50cnk7XG5cbiAgICBjb25zdCBzZXJpYWxpemVkID0gSlNPTi5zdHJpbmdpZnkoZW50cnkpO1xuICAgIGlmIChzZXJpYWxpemVkLmxlbmd0aCA8PSB0aGlzLmNvbmZpZy5tYXhFbnRyeVNpemUpIHJldHVybiBlbnRyeTtcblxuICAgIGNvbnN0IG9yaWdpbmFsU2l6ZSA9IHNlcmlhbGl6ZWQubGVuZ3RoO1xuXG4gICAgLy8gRW1pdCBhIHdhcm4gbWV0YS1lbnRyeSBhYm91dCB0aGUgdHJ1bmNhdGlvblxuICAgIGNvbnN0IHRydW5jYXRpb25Ob3RpY2UgPSB0aGlzLmNyZWF0ZU1ldGFFbnRyeShcbiAgICAgICd3YXJuJyxcbiAgICAgIGBFbnRyeSB0cnVuY2F0ZWQ6IG9yaWdpbmFsIHNpemUgJHtvcmlnaW5hbFNpemV9IGJ5dGVzIGV4Y2VlZHMgbGltaXQgJHt0aGlzLmNvbmZpZy5tYXhFbnRyeVNpemV9YCxcbiAgICAgIHsgb3JpZ2luYWxTaXplLCBtYXhTaXplOiB0aGlzLmNvbmZpZy5tYXhFbnRyeVNpemUsIGVudHJ5SWQ6IGVudHJ5LmlkIH0sXG4gICAgKTtcbiAgICBmb3IgKGNvbnN0IHNpbmsgb2YgdGhpcy5zaW5rcykge1xuICAgICAgc2luay53cml0ZSh0cnVuY2F0aW9uTm90aWNlKTtcbiAgICB9XG5cbiAgICAvLyBSZXR1cm4gYSBjb3B5IHdpdGggZGF0YSByZXBsYWNlZCBieSBhIHRydW5jYXRpb24gbWFya2VyXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLmVudHJ5LFxuICAgICAgZGF0YTogeyBfdHJ1bmNhdGVkOiB0cnVlLCBfb3JpZ2luYWxTaXplOiBvcmlnaW5hbFNpemUgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIENyZWF0ZSBhIG1ldGEvc3lzdGVtIGxvZyBlbnRyeSBmcm9tIHRoZSBMb2dNYW5hZ2VyIGl0c2VsZi4gKi9cbiAgcHJpdmF0ZSBjcmVhdGVNZXRhRW50cnkoXG4gICAgbGV2ZWw6IExvZ0xldmVsLFxuICAgIG1lc3NhZ2U6IHN0cmluZyxcbiAgICBkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICk6IFVuaWZpZWRMb2dFbnRyeSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGlkOiB0aGlzLmdlbmVyYXRlSWQoKSxcbiAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgY2F0ZWdvcnk6ICdhcHBsaWNhdGlvbicsXG4gICAgICBsZXZlbCxcbiAgICAgIHNvdXJjZTogJ0xvZ01hbmFnZXInLFxuICAgICAgbWVzc2FnZSxcbiAgICAgIGRhdGEsXG4gICAgfTtcbiAgfVxufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEhlbHBlcjogYnVpbGQgTG9nTWFuYWdlckNvbmZpZyBmcm9tIHZhbGlkYXRlZCBlbnYgdmFyc1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8qKlxuICogTWFwIHRoZSBmbGF0IGVudiBvYmplY3QgKGZyb20gWm9kLXBhcnNlZCBgcHJvY2Vzcy5lbnZgKSB0byBhIHR5cGVkXG4gKiBgTG9nTWFuYWdlckNvbmZpZ2AuIEtlZXBzIHRoZSBtYXBwaW5nIGluIG9uZSBwbGFjZSBzbyB0aGUgREkgY29udGFpbmVyXG4gKiBvbmx5IG5lZWRzIGBidWlsZExvZ01hbmFnZXJDb25maWcoZW52KWAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZExvZ01hbmFnZXJDb25maWcoZW52VmFyczoge1xuICBET0xMSE9VU0VfTE9HX0RJUjogc3RyaW5nO1xuICBET0xMSE9VU0VfTE9HX0ZPUk1BVDogJ3RleHQnIHwgJ2pzb25sJztcbiAgRE9MTEhPVVNFX0xPR19SRVRFTlRJT05fREFZUzogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX1NFQ1VSSVRZX1JFVEVOVElPTl9EQVlTOiBudW1iZXI7XG4gIERPTExIT1VTRV9MT0dfRkxVU0hfSU5URVJWQUxfTVM6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19CVUZGRVJfU0laRTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01FTU9SWV9DQVBBQ0lUWTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01FTU9SWV9BUFBfQ0FQQUNJVFk6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19NRU1PUllfU0VDVVJJVFlfQ0FQQUNJVFk6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19NRU1PUllfUEVSRl9DQVBBQ0lUWTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01FTU9SWV9URUxFTUVUUllfQ0FQQUNJVFk6IG51bWJlcjtcbiAgRE9MTEhPVVNFX0xPR19NQVhfRU5UUllfU0laRTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX0lNTUVESUFURV9GTFVTSF9SQVRFOiBudW1iZXI7XG4gIERPTExIT1VTRV9MT0dfRklMRV9NQVhfU0laRTogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01BWF9ESVJfU0laRV9CWVRFUzogbnVtYmVyO1xuICBET0xMSE9VU0VfTE9HX01BWF9GSUxFU19QRVJfQ0FURUdPUlk6IG51bWJlcjtcbiAgTE9HX0xFVkVMOiAnZGVidWcnIHwgJ2luZm8nIHwgJ3dhcm4nIHwgJ2Vycm9yJztcbn0pOiBMb2dNYW5hZ2VyQ29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICBsb2dEaXI6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19ESVIsXG4gICAgbG9nRm9ybWF0OiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfRk9STUFULFxuICAgIHJldGVudGlvbkRheXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19SRVRFTlRJT05fREFZUyxcbiAgICBzZWN1cml0eVJldGVudGlvbkRheXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19TRUNVUklUWV9SRVRFTlRJT05fREFZUyxcbiAgICBmbHVzaEludGVydmFsTXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19GTFVTSF9JTlRFUlZBTF9NUyxcbiAgICBidWZmZXJTaXplOiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfQlVGRkVSX1NJWkUsXG4gICAgbWVtb3J5Q2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfQ0FQQUNJVFksXG4gICAgbWVtb3J5QXBwQ2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfQVBQX0NBUEFDSVRZLFxuICAgIG1lbW9yeVNlY3VyaXR5Q2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfU0VDVVJJVFlfQ0FQQUNJVFksXG4gICAgbWVtb3J5UGVyZkNhcGFjaXR5OiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfTUVNT1JZX1BFUkZfQ0FQQUNJVFksXG4gICAgbWVtb3J5VGVsZW1ldHJ5Q2FwYWNpdHk6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NRU1PUllfVEVMRU1FVFJZX0NBUEFDSVRZLFxuICAgIG1heEVudHJ5U2l6ZTogZW52VmFycy5ET0xMSE9VU0VfTE9HX01BWF9FTlRSWV9TSVpFLFxuICAgIGltbWVkaWF0ZUZsdXNoUmF0ZTogZW52VmFycy5ET0xMSE9VU0VfTE9HX0lNTUVESUFURV9GTFVTSF9SQVRFLFxuICAgIGZpbGVNYXhTaXplOiBlbnZWYXJzLkRPTExIT1VTRV9MT0dfRklMRV9NQVhfU0laRSxcbiAgICBtYXhEaXJTaXplQnl0ZXM6IGVudlZhcnMuRE9MTEhPVVNFX0xPR19NQVhfRElSX1NJWkVfQllURVMsXG4gICAgbWF4RmlsZXNQZXJDYXRlZ29yeTogZW52VmFycy5ET0xMSE9VU0VfTE9HX01BWF9GSUxFU19QRVJfQ0FURUdPUlksXG4gICAgbG9nTGV2ZWw6IGVudlZhcnMuTE9HX0xFVkVMLFxuICB9O1xufVxuIl19