@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.
183 lines • 20.1 kB
JavaScript
/**
* File locking utility for preventing race conditions
*
* Uses advisory locks with timeout and retry logic
*/
import * as fs from 'fs/promises';
import { logger } from './logger.js';
export class FileLock {
lockPath;
acquired = false;
lockId;
pendingTimeouts = new Set();
disposed = false;
constructor(filePath) {
this.lockPath = `${filePath}.lock`;
this.lockId = `${process.pid}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Dispose the file lock and clear any pending timers.
*/
dispose() {
this.disposed = true;
for (const timeoutId of this.pendingTimeouts) {
clearTimeout(timeoutId);
}
this.pendingTimeouts.clear();
}
/**
* Acquire a lock on the file
*/
async acquire(options = {}) {
const { timeout = 5000, retryInterval = 100, stale = 30000 // 30 seconds
} = options;
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
// Try to create lock file exclusively
const handle = await fs.open(this.lockPath, 'wx');
// Write lock info
await handle.write(JSON.stringify({
pid: process.pid,
id: this.lockId,
timestamp: Date.now(),
hostname: process.env.HOSTNAME || 'unknown'
}));
await handle.close();
this.acquired = true;
return true;
}
catch (error) {
if (error.code === 'EEXIST') {
// Lock file exists, check if it's stale
try {
const lockData = await fs.readFile(this.lockPath, 'utf-8');
const lock = JSON.parse(lockData);
if (Date.now() - lock.timestamp > stale) {
// Lock is stale, remove it
logger.warn('Removing stale lock', {
lock: this.lockPath,
age: Date.now() - lock.timestamp
});
await this.forceRelease();
// Continue to try acquiring
}
else {
// Lock is held by another process
await this.sleep(retryInterval);
}
}
catch (readError) {
// Can't read lock file - could be permissions issue or concurrent deletion
logger.debug('Failed to read lock file, retrying', {
lock: this.lockPath,
error: readError instanceof Error ? readError.message : String(readError)
});
await this.sleep(retryInterval);
}
}
else {
// Some other error
logger.error('Failed to acquire lock', { error });
return false;
}
}
}
logger.warn('Lock acquisition timeout', {
lock: this.lockPath,
timeout
});
return false;
}
/**
* Release the lock
*/
async release() {
if (!this.acquired) {
return;
}
try {
// Verify we still own the lock
const lockData = await fs.readFile(this.lockPath, 'utf-8');
const lock = JSON.parse(lockData);
if (lock.id === this.lockId) {
await fs.unlink(this.lockPath);
this.acquired = false;
}
else {
logger.warn('Lock owned by different process', {
lock: this.lockPath,
ourId: this.lockId,
theirId: lock.id
});
}
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.error('Failed to release lock', { error });
}
this.acquired = false;
}
}
/**
* Force release (use with caution)
*/
async forceRelease() {
try {
await fs.unlink(this.lockPath);
this.acquired = false;
logger.warn('Lock forcibly released', { lock: this.lockPath });
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.error('Failed to force release lock', { error });
}
}
}
/**
* Check if lock is held
*/
async isLocked() {
try {
await fs.access(this.lockPath);
return true;
}
catch (error) {
// Lock file doesn't exist or not accessible
logger.debug('Lock file not accessible', {
lock: this.lockPath,
code: error.code
});
return false;
}
}
/**
* Execute a function with lock protection
*/
async withLock(fn, options = {}) {
const acquired = await this.acquire(options);
if (!acquired) {
logger.error('Failed to acquire lock for operation');
return null;
}
try {
return await fn();
}
finally {
await this.release();
}
}
sleep(ms) {
if (this.disposed) {
return Promise.resolve();
}
return new Promise(resolve => {
const timeoutId = setTimeout(() => {
this.pendingTimeouts.delete(timeoutId);
resolve();
}, ms);
this.pendingTimeouts.add(timeoutId);
});
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmlsZUxvY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvRmlsZUxvY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFRckMsTUFBTSxPQUFPLFFBQVE7SUFDWCxRQUFRLENBQVM7SUFDakIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUMxQixNQUFNLENBQVM7SUFDZixlQUFlLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDNUMsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUV6QixZQUFZLFFBQWdCO1FBQzFCLElBQUksQ0FBQyxRQUFRLEdBQUcsR0FBRyxRQUFRLE9BQU8sQ0FBQztRQUNuQyxJQUFJLENBQUMsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDMUYsQ0FBQztJQUVEOztPQUVHO0lBQ0ksT0FBTztRQUNaLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDO1FBQ3JCLEtBQUssTUFBTSxTQUFTLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQzdDLFlBQVksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMxQixDQUFDO1FBQ0QsSUFBSSxDQUFDLGVBQWUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUMvQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsT0FBTyxDQUFDLFVBQXVCLEVBQUU7UUFDNUMsTUFBTSxFQUNKLE9BQU8sR0FBRyxJQUFJLEVBQ2QsYUFBYSxHQUFHLEdBQUcsRUFDbkIsS0FBSyxHQUFHLEtB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O1lBQ1QsTUFBTSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFFTyxLQUFLLENBQUMsRUFBVTtRQUN0QixJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNsQixPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMzQixDQUFDO1FBQ0QsT0FBTyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRTtZQUMzQixNQUFNLFNBQVMsR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO2dCQUNoQyxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxFQUFFLENBQUM7WUFDWixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDUCxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN0QyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRmlsZSBsb2NraW5nIHV0aWxpdHkgZm9yIHByZXZlbnRpbmcgcmFjZSBjb25kaXRpb25zXG4gKlxuICogVXNlcyBhZHZpc29yeSBsb2NrcyB3aXRoIHRpbWVvdXQgYW5kIHJldHJ5IGxvZ2ljXG4gKi9cblxuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIExvY2tPcHRpb25zIHtcbiAgdGltZW91dD86IG51bWJlcjsgICAgICAgIC8vIE1heCB0aW1lIHRvIHdhaXQgZm9yIGxvY2sgKG1zKVxuICByZXRyeUludGVydmFsPzogbnVtYmVyOyAgLy8gVGltZSBiZXR3ZWVuIHJldHJpZXMgKG1zKVxuICBzdGFsZT86IG51bWJlcjsgICAgICAgICAvLyBDb25zaWRlciBsb2NrIHN0YWxlIGFmdGVyIHRoaXMgdGltZSAobXMpXG59XG5cbmV4cG9ydCBjbGFzcyBGaWxlTG9jayB7XG4gIHByaXZhdGUgbG9ja1BhdGg6IHN0cmluZztcbiAgcHJpdmF0ZSBhY3F1aXJlZDogYm9vbGVhbiA9IGZhbHNlO1xuICBwcml2YXRlIGxvY2tJZDogc3RyaW5nO1xuICBwcml2YXRlIHBlbmRpbmdUaW1lb3V0cyA9IG5ldyBTZXQ8Tm9kZUpTLlRpbWVvdXQ+KCk7XG4gIHByaXZhdGUgZGlzcG9zZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3RvcihmaWxlUGF0aDogc3RyaW5nKSB7XG4gICAgdGhpcy5sb2NrUGF0aCA9IGAke2ZpbGVQYXRofS5sb2NrYDtcbiAgICB0aGlzLmxvY2tJZCA9IGAke3Byb2Nlc3MucGlkfS0ke0RhdGUubm93KCl9LSR7TWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDkpfWA7XG4gIH1cblxuICAvKipcbiAgICogRGlzcG9zZSB0aGUgZmlsZSBsb2NrIGFuZCBjbGVhciBhbnkgcGVuZGluZyB0aW1lcnMuXG4gICAqL1xuICBwdWJsaWMgZGlzcG9zZSgpOiB2b2lkIHtcbiAgICB0aGlzLmRpc3Bvc2VkID0gdHJ1ZTtcbiAgICBmb3IgKGNvbnN0IHRpbWVvdXRJZCBvZiB0aGlzLnBlbmRpbmdUaW1lb3V0cykge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gICAgfVxuICAgIHRoaXMucGVuZGluZ1RpbWVvdXRzLmNsZWFyKCk7XG4gIH1cblxuICAvKipcbiAgICogQWNxdWlyZSBhIGxvY2sgb24gdGhlIGZpbGVcbiAgICovXG4gIHB1YmxpYyBhc3luYyBhY3F1aXJlKG9wdGlvbnM6IExvY2tPcHRpb25zID0ge30pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCB7XG4gICAgICB0aW1lb3V0ID0gNTAwMCxcbiAgICAgIHJldHJ5SW50ZXJ2YWwgPSAxMDAsXG4gICAgICBzdGFsZSA9IDMwMDAwICAvLyAzMCBzZWNvbmRzXG4gICAgfSA9IG9wdGlvbnM7XG5cbiAgICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuXG4gICAgd2hpbGUgKERhdGUubm93KCkgLSBzdGFydFRpbWUgPCB0aW1lb3V0KSB7XG4gICAgICB0cnkge1xuICAgICAgICAvLyBUcnkgdG8gY3JlYXRlIGxvY2sgZmlsZSBleGNsdXNpdmVseVxuICAgICAgICBjb25zdCBoYW5kbGUgPSBhd2FpdCBmcy5vcGVuKHRoaXMubG9ja1BhdGgsICd3eCcpO1xuXG4gICAgICAgIC8vIFdyaXRlIGxvY2sgaW5mb1xuICAgICAgICBhd2FpdCBoYW5kbGUud3JpdGUoSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgIHBpZDogcHJvY2Vzcy5waWQsXG4gICAgICAgICAgaWQ6IHRoaXMubG9ja0lkLFxuICAgICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgICBob3N0bmFtZTogcHJvY2Vzcy5lbnYuSE9TVE5BTUUgfHwgJ3Vua25vd24nXG4gICAgICAgIH0pKTtcblxuICAgICAgICBhd2FpdCBoYW5kbGUuY2xvc2UoKTtcbiAgICAgICAgdGhpcy5hY3F1aXJlZCA9IHRydWU7XG5cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSA9PT0gJ0VFWElTVCcpIHtcbiAgICAgICAgICAvLyBMb2NrIGZpbGUgZXhpc3RzLCBjaGVjayBpZiBpdCdzIHN0YWxlXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGxvY2tEYXRhID0gYXdhaXQgZnMucmVhZEZpbGUodGhpcy5sb2NrUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAgICAgICBjb25zdCBsb2NrID0gSlNPTi5wYXJzZShsb2NrRGF0YSk7XG5cbiAgICAgICAgICAgIGlmIChEYXRlLm5vdygpIC0gbG9jay50aW1lc3RhbXAgPiBzdGFsZSkge1xuICAgICAgICAgICAgICAvLyBMb2NrIGlzIHN0YWxlLCByZW1vdmUgaXRcbiAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ1JlbW92aW5nIHN0YWxlIGxvY2snLCB7XG4gICAgICAgICAgICAgICAgbG9jazogdGhpcy5sb2NrUGF0aCxcbiAgICAgICAgICAgICAgICBhZ2U6IERhdGUubm93KCkgLSBsb2NrLnRpbWVzdGFtcFxuICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICBhd2FpdCB0aGlzLmZvcmNlUmVsZWFzZSgpO1xuICAgICAgICAgICAgICAvLyBDb250aW51ZSB0byB0cnkgYWNxdWlyaW5nXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBMb2NrIGlzIGhlbGQgYnkgYW5vdGhlciBwcm9jZXNzXG4gICAgICAgICAgICAgIGF3YWl0IHRoaXMuc2xlZXAocmV0cnlJbnRlcnZhbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAocmVhZEVycm9yKSB7XG4gICAgICAgICAgICAvLyBDYW4ndCByZWFkIGxvY2sgZmlsZSAtIGNvdWxkIGJlIHBlcm1pc3Npb25zIGlzc3VlIG9yIGNvbmN1cnJlbnQgZGVsZXRpb25cbiAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZygnRmFpbGVkIHRvIHJlYWQgbG9jayBmaWxlLCByZXRyeWluZycsIHtcbiAgICAgICAgICAgICAgbG9jazogdGhpcy5sb2NrUGF0aCxcbiAgICAgICAgICAgICAgZXJyb3I6IHJlYWRFcnJvciBpbnN0YW5jZW9mIEVycm9yID8gcmVhZEVycm9yLm1lc3NhZ2UgOiBTdHJpbmcocmVhZEVycm9yKVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNsZWVwKHJldHJ5SW50ZXJ2YWwpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBTb21lIG90aGVyIGVycm9yXG4gICAgICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gYWNxdWlyZSBsb2NrJywgeyBlcnJvciB9KTtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBsb2dnZXIud2FybignTG9jayBhY3F1aXNpdGlvbiB0aW1lb3V0Jywge1xuICAgICAgbG9jazogdGhpcy5sb2NrUGF0aCxcbiAgICAgIHRpbWVvdXRcbiAgICB9KTtcblxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWxlYXNlIHRoZSBsb2NrXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcmVsZWFzZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuYWNxdWlyZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgLy8gVmVyaWZ5IHdlIHN0aWxsIG93biB0aGUgbG9ja1xuICAgICAgY29uc3QgbG9ja0RhdGEgPSBhd2FpdCBmcy5yZWFkRmlsZSh0aGlzLmxvY2tQYXRoLCAndXRmLTgnKTtcbiAgICAgIGNvbnN0IGxvY2sgPSBKU09OLnBhcnNlKGxvY2tEYXRhKTtcblxuICAgICAgaWYgKGxvY2suaWQgPT09IHRoaXMubG9ja0lkKSB7XG4gICAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmxvY2tQYXRoKTtcbiAgICAgICAgdGhpcy5hY3F1aXJlZCA9IGZhbHNlO1xuXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2dnZXIud2FybignTG9jayBvd25lZCBieSBkaWZmZXJlbnQgcHJvY2VzcycsIHtcbiAgICAgICAgICBsb2NrOiB0aGlzLmxvY2tQYXRoLFxuICAgICAgICAgIG91cklkOiB0aGlzLmxvY2tJZCxcbiAgICAgICAgICB0aGVpcklkOiBsb2NrLmlkXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSAhPT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gcmVsZWFzZSBsb2NrJywgeyBlcnJvciB9KTtcbiAgICAgIH1cbiAgICAgIHRoaXMuYWNxdWlyZWQgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRm9yY2UgcmVsZWFzZSAodXNlIHdpdGggY2F1dGlvbilcbiAgICovXG4gIHB1YmxpYyBhc3luYyBmb3JjZVJlbGVhc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmxvY2tQYXRoKTtcbiAgICAgIHRoaXMuYWNxdWlyZWQgPSBmYWxzZTtcbiAgICAgIGxvZ2dlci53YXJuKCdMb2NrIGZvcmNpYmx5IHJlbGVhc2VkJywgeyBsb2NrOiB0aGlzLmxvY2tQYXRoIH0pO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSAhPT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gZm9yY2UgcmVsZWFzZSBsb2NrJywgeyBlcnJvciB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgbG9jayBpcyBoZWxkXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgaXNMb2NrZWQoKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLmFjY2Vzcyh0aGlzLmxvY2tQYXRoKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBMb2NrIGZpbGUgZG9lc24ndCBleGlzdCBvciBub3QgYWNjZXNzaWJsZVxuICAgICAgbG9nZ2VyLmRlYnVnKCdMb2NrIGZpbGUgbm90IGFjY2Vzc2libGUnLCB7XG4gICAgICAgIGxvY2s6IHRoaXMubG9ja1BhdGgsXG4gICAgICAgIGNvZGU6IChlcnJvciBhcyBOb2RlSlMuRXJybm9FeGNlcHRpb24pLmNvZGVcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFeGVjdXRlIGEgZnVuY3Rpb24gd2l0aCBsb2NrIHByb3RlY3Rpb25cbiAgICovXG4gIHB1YmxpYyBhc3luYyB3aXRoTG9jazxUPihcbiAgICBmbjogKCkgPT4gUHJvbWlzZTxUPixcbiAgICBvcHRpb25zOiBMb2NrT3B0aW9ucyA9IHt9XG4gICk6IFByb21pc2U8VCB8IG51bGw+IHtcbiAgICBjb25zdCBhY3F1aXJlZCA9IGF3YWl0IHRoaXMuYWNxdWlyZShvcHRpb25zKTtcblxuICAgIGlmICghYWNxdWlyZWQpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGFjcXVpcmUgbG9jayBmb3Igb3BlcmF0aW9uJyk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IGZuKCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGF3YWl0IHRoaXMucmVsZWFzZSgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc2xlZXAobXM6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmRpc3Bvc2VkKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHtcbiAgICAgIGNvbnN0IHRpbWVvdXRJZCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLnBlbmRpbmdUaW1lb3V0cy5kZWxldGUodGltZW91dElkKTtcbiAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgfSwgbXMpO1xuICAgICAgdGhpcy5wZW5kaW5nVGltZW91dHMuYWRkKHRpbWVvdXRJZCk7XG4gICAgfSk7XG4gIH1cbn0iXX0=