@aws-cdk-testing/cli-integ
Version:
Integration tests for the AWS CDK CLI
207 lines • 23.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.XpMutex = exports.XpMutexPool = void 0;
const fs_1 = require("fs");
const os = require("os");
const path = require("path");
class XpMutexPool {
static fromDirectory(directory) {
(0, fs_1.mkdirSync)(directory, { recursive: true });
return new XpMutexPool(directory);
}
static fromName(name) {
return XpMutexPool.fromDirectory(path.join(os.tmpdir(), name));
}
constructor(directory) {
this.directory = directory;
this.waitingResolvers = new Set();
this.startWatch();
}
mutex(name) {
return new XpMutex(this, name);
}
/**
* Await an unlock event
*
* (An unlock event is when a file in the directory gets deleted, with a tiny
* random sleep attached to it).
*/
awaitUnlock(maxWaitMs) {
const wait = new Promise(ok => {
this.waitingResolvers.add(async () => {
await randomSleep(10);
ok();
});
});
if (maxWaitMs) {
return Promise.race([wait, sleep(maxWaitMs)]);
}
else {
return wait;
}
}
startWatch() {
this.watcher = (0, fs_1.watch)(this.directory);
this.watcher.unref(); // @types doesn't know about this but it exists
this.watcher.on('change', async (eventType, fname) => {
// Only trigger on 'deletes'.
// After receiving the event, we check if the file exists.
// - If no: the file was deleted! Huzzah, this counts as a wakeup.
// - If yes: either the file was just created (in which case we don't need to wakeup)
// or the event was due to a delete but someone raced us to it and claimed the
// file already (in which case we also don't need to wake up).
if (eventType === 'rename' && !await fileExists(path.join(this.directory, fname.toString()))) {
this.notifyWaiters();
}
});
this.watcher.on('error', async (e) => {
// eslint-disable-next-line no-console
console.error(e);
await randomSleep(100);
this.startWatch();
});
}
notifyWaiters() {
for (const promise of this.waitingResolvers) {
promise();
}
this.waitingResolvers.clear();
}
}
exports.XpMutexPool = XpMutexPool;
/**
* Cross-process mutex
*
* Uses the presence of a file on disk and `fs.watch` to represent the mutex
* and discover unlocks.
*/
class XpMutex {
constructor(pool, mutexName) {
this.pool = pool;
this.mutexName = mutexName;
this.fileName = path.join(pool.directory, `${mutexName}.mutex`);
}
/**
* Try to acquire the lock (may fail)
*/
async tryAcquire() {
while (true) {
// Acquire lock by being the one to create the file
try {
return await this.writePidFile('wx'); // Fails if the file already exists
}
catch (e) {
if (e.code !== 'EEXIST') {
throw e;
}
}
// File already exists. Read the contents, see if it's an existent PID (if so, the lock is taken)
const ownerPid = await this.readPidFile();
if (ownerPid === undefined) {
// File got deleted just now, maybe we can acquire it again
continue;
}
if (processExists(ownerPid)) {
return undefined;
}
// If not, the lock is stale and will never be released anymore. We may
// delete it and acquire it anyway, but we may be racing someone else trying
// to do the same. Solve this as follows:
// - Try to acquire a lock that gives us permissions to declare the existing lock stale.
// - Sleep a small random period to reduce contention on this operation
await randomSleep(10);
const innerMux = new XpMutex(this.pool, `${this.mutexName}.${ownerPid}`);
const innerLock = await innerMux.tryAcquire();
if (!innerLock) {
return undefined;
}
// We may not release the 'inner lock' we used to acquire the rights to declare the other
// lock stale until we release the actual lock itself. If we did, other contenders might
// see it released while they're still in this fallback block and accidentally steal
// from a new legitimate owner.
return this.writePidFile('w', innerLock); // Force write lock file, attach inner lock as well
}
}
/**
* Acquire the lock, waiting until we can
*/
async acquire() {
while (true) {
// Start the wait here, so we don't miss the signal if it comes after
// we try but before we sleep.
//
// We also periodically retry anyway since we may have missed the delete
// signal due to unfortunate timing.
const wait = this.pool.awaitUnlock(5000);
const lock = await this.tryAcquire();
if (lock) {
// Ignore the wait (count as handled)
wait.then(() => { }, () => { });
return lock;
}
await wait;
await randomSleep(100);
}
}
async readPidFile() {
const deadLine = Date.now() + 1000;
while (Date.now() < deadLine) {
let contents;
try {
contents = await fs_1.promises.readFile(this.fileName, { encoding: 'utf-8' });
}
catch (e) {
if (e.code === 'ENOENT') {
return undefined;
}
throw e;
}
// Retry until we've seen the full contents
if (contents.endsWith('.')) {
return parseInt(contents.substring(0, contents.length - 1), 10);
}
await sleep(10);
}
throw new Error(`${this.fileName} was never completely written`);
}
async writePidFile(mode, additionalLock) {
const fd = await fs_1.promises.open(this.fileName, mode); // May fail if the file already exists
await fd.write(`${process.pid}.`); // Period guards against partial reads
await fd.close();
return {
release: async () => {
await fs_1.promises.unlink(this.fileName);
await (additionalLock === null || additionalLock === void 0 ? void 0 : additionalLock.release());
},
};
}
}
exports.XpMutex = XpMutex;
async function fileExists(fileName) {
try {
await fs_1.promises.stat(fileName);
return true;
}
catch (e) {
if (e.code === 'ENOENT') {
return false;
}
throw e;
}
}
function processExists(pid) {
try {
process.kill(pid, 0);
return true;
}
catch {
return false;
}
}
function sleep(ms) {
return new Promise(ok => setTimeout(ok, ms).unref());
}
function randomSleep(ms) {
return sleep(Math.floor(Math.random() * ms));
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieHBtdXRleC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInhwbXV0ZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkJBQXNEO0FBQ3RELHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFFN0IsTUFBYSxXQUFXO0lBQ2YsTUFBTSxDQUFDLGFBQWEsQ0FBQyxTQUFpQjtRQUMzQyxJQUFBLGNBQVMsRUFBQyxTQUFTLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUMxQyxPQUFPLElBQUksV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFTSxNQUFNLENBQUMsUUFBUSxDQUFDLElBQVk7UUFDakMsT0FBTyxXQUFXLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDakUsQ0FBQztJQUtELFlBQW9DLFNBQWlCO1FBQWpCLGNBQVMsR0FBVCxTQUFTLENBQVE7UUFIcEMscUJBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQWMsQ0FBQztRQUl4RCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQUVNLEtBQUssQ0FBQyxJQUFZO1FBQ3ZCLE9BQU8sSUFBSSxPQUFPLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLFdBQVcsQ0FBQyxTQUFrQjtRQUNuQyxNQUFNLElBQUksR0FBRyxJQUFJLE9BQU8sQ0FBTyxFQUFFLENBQUMsRUFBRTtZQUNsQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFO2dCQUNuQyxNQUFNLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDdEIsRUFBRSxFQUFFLENBQUM7WUFDUCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO1FBRUgsSUFBSSxTQUFTLEVB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dXJjZXNDb250ZW50IjpbImltcG9ydCB7IHdhdGNoLCBwcm9taXNlcyBhcyBmcywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcblxuZXhwb3J0IGNsYXNzIFhwTXV0ZXhQb29sIHtcbiAgcHVibGljIHN0YXRpYyBmcm9tRGlyZWN0b3J5KGRpcmVjdG9yeTogc3RyaW5nKSB7XG4gICAgbWtkaXJTeW5jKGRpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgcmV0dXJuIG5ldyBYcE11dGV4UG9vbChkaXJlY3RvcnkpO1xuICB9XG5cbiAgcHVibGljIHN0YXRpYyBmcm9tTmFtZShuYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gWHBNdXRleFBvb2wuZnJvbURpcmVjdG9yeShwYXRoLmpvaW4ob3MudG1wZGlyKCksIG5hbWUpKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVhZG9ubHkgd2FpdGluZ1Jlc29sdmVycyA9IG5ldyBTZXQ8KCkgPT4gdm9pZD4oKTtcbiAgcHJpdmF0ZSB3YXRjaGVyOiBSZXR1cm5UeXBlPHR5cGVvZiB3YXRjaD4gfCB1bmRlZmluZWQ7XG5cbiAgcHJpdmF0ZSBjb25zdHJ1Y3RvcihwdWJsaWMgcmVhZG9ubHkgZGlyZWN0b3J5OiBzdHJpbmcpIHtcbiAgICB0aGlzLnN0YXJ0V2F0Y2goKTtcbiAgfVxuXG4gIHB1YmxpYyBtdXRleChuYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gbmV3IFhwTXV0ZXgodGhpcywgbmFtZSk7XG4gIH1cblxuICAvKipcbiAgICogQXdhaXQgYW4gdW5sb2NrIGV2ZW50XG4gICAqXG4gICAqIChBbiB1bmxvY2sgZXZlbnQgaXMgd2hlbiBhIGZpbGUgaW4gdGhlIGRpcmVjdG9yeSBnZXRzIGRlbGV0ZWQsIHdpdGggYSB0aW55XG4gICAqIHJhbmRvbSBzbGVlcCBhdHRhY2hlZCB0byBpdCkuXG4gICAqL1xuICBwdWJsaWMgYXdhaXRVbmxvY2sobWF4V2FpdE1zPzogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgd2FpdCA9IG5ldyBQcm9taXNlPHZvaWQ+KG9rID0+IHtcbiAgICAgIHRoaXMud2FpdGluZ1Jlc29sdmVycy5hZGQoYXN5bmMgKCkgPT4ge1xuICAgICAgICBhd2FpdCByYW5kb21TbGVlcCgxMCk7XG4gICAgICAgIG9rKCk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIGlmIChtYXhXYWl0TXMpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJhY2UoW3dhaXQsIHNsZWVwKG1heFdhaXRNcyldKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHdhaXQ7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdGFydFdhdGNoKCkge1xuICAgIHRoaXMud2F0Y2hlciA9IHdhdGNoKHRoaXMuZGlyZWN0b3J5KTtcbiAgICAodGhpcy53YXRjaGVyIGFzIGFueSkudW5yZWYoKTsgLy8gQHR5cGVzIGRvZXNuJ3Qga25vdyBhYm91dCB0aGlzIGJ1dCBpdCBleGlzdHNcbiAgICB0aGlzLndhdGNoZXIub24oJ2NoYW5nZScsIGFzeW5jIChldmVudFR5cGUsIGZuYW1lKSA9PiB7XG4gICAgICAvLyBPbmx5IHRyaWdnZXIgb24gJ2RlbGV0ZXMnLlxuICAgICAgLy8gQWZ0ZXIgcmVjZWl2aW5nIHRoZSBldmVudCwgd2UgY2hlY2sgaWYgdGhlIGZpbGUgZXhpc3RzLlxuICAgICAgLy8gLSBJZiBubzogdGhlIGZpbGUgd2FzIGRlbGV0ZWQhIEh1enphaCwgdGhpcyBjb3VudHMgYXMgYSB3YWtldXAuXG4gICAgICAvLyAtIElmIHllczogZWl0aGVyIHRoZSBmaWxlIHdhcyBqdXN0IGNyZWF0ZWQgKGluIHdoaWNoIGNhc2Ugd2UgZG9uJ3QgbmVlZCB0byB3YWtldXApXG4gICAgICAvLyAgIG9yIHRoZSBldmVudCB3YXMgZHVlIHRvIGEgZGVsZXRlIGJ1dCBzb21lb25lIHJhY2VkIHVzIHRvIGl0IGFuZCBjbGFpbWVkIHRoZVxuICAgICAgLy8gICBmaWxlIGFscmVhZHkgKGluIHdoaWNoIGNhc2Ugd2UgYWxzbyBkb24ndCBuZWVkIHRvIHdha2UgdXApLlxuICAgICAgaWYgKGV2ZW50VHlwZSA9PT0gJ3JlbmFtZScgJiYgIWF3YWl0IGZpbGVFeGlzdHMocGF0aC5qb2luKHRoaXMuZGlyZWN0b3J5LCBmbmFtZS50b1N0cmluZygpKSkpIHtcbiAgICAgICAgdGhpcy5ub3RpZnlXYWl0ZXJzKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy53YXRjaGVyLm9uKCdlcnJvcicsIGFzeW5jIChlKSA9PiB7XG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgIGF3YWl0IHJhbmRvbVNsZWVwKDEwMCk7XG4gICAgICB0aGlzLnN0YXJ0V2F0Y2goKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgbm90aWZ5V2FpdGVycygpIHtcbiAgICBmb3IgKGNvbnN0IHByb21pc2Ugb2YgdGhpcy53YWl0aW5nUmVzb2x2ZXJzKSB7XG4gICAgICBwcm9taXNlKCk7XG4gICAgfVxuICAgIHRoaXMud2FpdGluZ1Jlc29sdmVycy5jbGVhcigpO1xuICB9XG59XG5cbi8qKlxuICogQ3Jvc3MtcHJvY2VzcyBtdXRleFxuICpcbiAqIFVzZXMgdGhlIHByZXNlbmNlIG9mIGEgZmlsZSBvbiBkaXNrIGFuZCBgZnMud2F0Y2hgIHRvIHJlcHJlc2VudCB0aGUgbXV0ZXhcbiAqIGFuZCBkaXNjb3ZlciB1bmxvY2tzLlxuICovXG5leHBvcnQgY2xhc3MgWHBNdXRleCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgZmlsZU5hbWU6IHN0cmluZztcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHBvb2w6IFhwTXV0ZXhQb29sLCBwdWJsaWMgcmVhZG9ubHkgbXV0ZXhOYW1lOiBzdHJpbmcpIHtcbiAgICB0aGlzLmZpbGVOYW1lID0gcGF0aC5qb2luKHBvb2wuZGlyZWN0b3J5LCBgJHttdXRleE5hbWV9Lm11dGV4YCk7XG4gIH1cblxuICAvKipcbiAgICogVHJ5IHRvIGFjcXVpcmUgdGhlIGxvY2sgKG1heSBmYWlsKVxuICAgKi9cbiAgcHVibGljIGFzeW5jIHRyeUFjcXVpcmUoKTogUHJvbWlzZTxJTG9jayB8IHVuZGVmaW5lZD4ge1xuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAvLyBBY3F1aXJlIGxvY2sgYnkgYmVpbmcgdGhlIG9uZSB0byBjcmVhdGUgdGhlIGZpbGVcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLndyaXRlUGlkRmlsZSgnd3gnKTsgLy8gRmFpbHMgaWYgdGhlIGZpbGUgYWxyZWFkeSBleGlzdHNcbiAgICAgIH0gY2F0Y2ggKGU6IGFueSkge1xuICAgICAgICBpZiAoZS5jb2RlICE9PSAnRUVYSVNUJykgeyB0aHJvdyBlOyB9XG4gICAgICB9XG5cbiAgICAgIC8vIEZpbGUgYWxyZWFkeSBleGlzdHMuIFJlYWQgdGhlIGNvbnRlbnRzLCBzZWUgaWYgaXQncyBhbiBleGlzdGVudCBQSUQgKGlmIHNvLCB0aGUgbG9jayBpcyB0YWtlbilcbiAgICAgIGNvbnN0IG93bmVyUGlkID0gYXdhaXQgdGhpcy5yZWFkUGlkRmlsZSgpO1xuICAgICAgaWYgKG93bmVyUGlkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgLy8gRmlsZSBnb3QgZGVsZXRlZCBqdXN0IG5vdywgbWF5YmUgd2UgY2FuIGFjcXVpcmUgaXQgYWdhaW5cbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAocHJvY2Vzc0V4aXN0cyhvd25lclBpZCkpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH1cblxuICAgICAgLy8gSWYgbm90LCB0aGUgbG9jayBpcyBzdGFsZSBhbmQgd2lsbCBuZXZlciBiZSByZWxlYXNlZCBhbnltb3JlLiBXZSBtYXlcbiAgICAgIC8vIGRlbGV0ZSBpdCBhbmQgYWNxdWlyZSBpdCBhbnl3YXksIGJ1dCB3ZSBtYXkgYmUgcmFjaW5nIHNvbWVvbmUgZWxzZSB0cnlpbmdcbiAgICAgIC8vIHRvIGRvIHRoZSBzYW1lLiBTb2x2ZSB0aGlzIGFzIGZvbGxvd3M6XG4gICAgICAvLyAtIFRyeSB0byBhY3F1aXJlIGEgbG9jayB0aGF0IGdpdmVzIHVzIHBlcm1pc3Npb25zIHRvIGRlY2xhcmUgdGhlIGV4aXN0aW5nIGxvY2sgc3RhbGUuXG4gICAgICAvLyAtIFNsZWVwIGEgc21hbGwgcmFuZG9tIHBlcmlvZCB0byByZWR1Y2UgY29udGVudGlvbiBvbiB0aGlzIG9wZXJhdGlvblxuICAgICAgYXdhaXQgcmFuZG9tU2xlZXAoMTApO1xuICAgICAgY29uc3QgaW5uZXJNdXggPSBuZXcgWHBNdXRleCh0aGlzLnBvb2wsIGAke3RoaXMubXV0ZXhOYW1lfS4ke293bmVyUGlkfWApO1xuICAgICAgY29uc3QgaW5uZXJMb2NrID0gYXdhaXQgaW5uZXJNdXgudHJ5QWNxdWlyZSgpO1xuICAgICAgaWYgKCFpbm5lckxvY2spIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH1cblxuICAgICAgLy8gV2UgbWF5IG5vdCByZWxlYXNlIHRoZSAnaW5uZXIgbG9jaycgd2UgdXNlZCB0byBhY3F1aXJlIHRoZSByaWdodHMgdG8gZGVjbGFyZSB0aGUgb3RoZXJcbiAgICAgIC8vIGxvY2sgc3RhbGUgdW50aWwgd2UgcmVsZWFzZSB0aGUgYWN0dWFsIGxvY2sgaXRzZWxmLiBJZiB3ZSBkaWQsIG90aGVyIGNvbnRlbmRlcnMgbWlnaHRcbiAgICAgIC8vIHNlZSBpdCByZWxlYXNlZCB3aGlsZSB0aGV5J3JlIHN0aWxsIGluIHRoaXMgZmFsbGJhY2sgYmxvY2sgYW5kIGFjY2lkZW50YWxseSBzdGVhbFxuICAgICAgLy8gZnJvbSBhIG5ldyBsZWdpdGltYXRlIG93bmVyLlxuICAgICAgcmV0dXJuIHRoaXMud3JpdGVQaWRGaWxlKCd3JywgaW5uZXJMb2NrKTsgLy8gRm9yY2Ugd3JpdGUgbG9jayBmaWxlLCBhdHRhY2ggaW5uZXIgbG9jayBhcyB3ZWxsXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFjcXVpcmUgdGhlIGxvY2ssIHdhaXRpbmcgdW50aWwgd2UgY2FuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgYWNxdWlyZSgpOiBQcm9taXNlPElMb2NrPiB7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIC8vIFN0YXJ0IHRoZSB3YWl0IGhlcmUsIHNvIHdlIGRvbid0IG1pc3MgdGhlIHNpZ25hbCBpZiBpdCBjb21lcyBhZnRlclxuICAgICAgLy8gd2UgdHJ5IGJ1dCBiZWZvcmUgd2Ugc2xlZXAuXG4gICAgICAvL1xuICAgICAgLy8gV2UgYWxzbyBwZXJpb2RpY2FsbHkgcmV0cnkgYW55d2F5IHNpbmNlIHdlIG1heSBoYXZlIG1pc3NlZCB0aGUgZGVsZXRlXG4gICAgICAvLyBzaWduYWwgZHVlIHRvIHVuZm9ydHVuYXRlIHRpbWluZy5cbiAgICAgIGNvbnN0IHdhaXQgPSB0aGlzLnBvb2wuYXdhaXRVbmxvY2soNTAwMCk7XG5cbiAgICAgIGNvbnN0IGxvY2sgPSBhd2FpdCB0aGlzLnRyeUFjcXVpcmUoKTtcbiAgICAgIGlmIChsb2NrKSB7XG4gICAgICAgIC8vIElnbm9yZSB0aGUgd2FpdCAoY291bnQgYXMgaGFuZGxlZClcbiAgICAgICAgd2FpdC50aGVuKCgpID0+IHt9LCAoKSA9PiB7fSk7XG4gICAgICAgIHJldHVybiBsb2NrO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCB3YWl0O1xuICAgICAgYXdhaXQgcmFuZG9tU2xlZXAoMTAwKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlYWRQaWRGaWxlKCk6IFByb21pc2U8bnVtYmVyIHwgdW5kZWZpbmVkPiB7XG4gICAgY29uc3QgZGVhZExpbmUgPSBEYXRlLm5vdygpICsgMTAwMDtcbiAgICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRMaW5lKSB7XG4gICAgICBsZXQgY29udGVudHM7XG4gICAgICB0cnkge1xuICAgICAgICBjb250ZW50cyA9IGF3YWl0IGZzLnJlYWRGaWxlKHRoaXMuZmlsZU5hbWUsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSk7XG4gICAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgICAgaWYgKGUuY29kZSA9PT0gJ0VOT0VOVCcpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXRyeSB1bnRpbCB3ZSd2ZSBzZWVuIHRoZSBmdWxsIGNvbnRlbnRzXG4gICAgICBpZiAoY29udGVudHMuZW5kc1dpdGgoJy4nKSkgeyByZXR1cm4gcGFyc2VJbnQoY29udGVudHMuc3Vic3RyaW5nKDAsIGNvbnRlbnRzLmxlbmd0aCAtIDEpLCAxMCk7IH1cbiAgICAgIGF3YWl0IHNsZWVwKDEwKTtcbiAgICB9XG5cbiAgICB0aHJvdyBuZXcgRXJyb3IoYCR7dGhpcy5maWxlTmFtZX0gd2FzIG5ldmVyIGNvbXBsZXRlbHkgd3JpdHRlbmApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB3cml0ZVBpZEZpbGUobW9kZTogc3RyaW5nLCBhZGRpdGlvbmFsTG9jaz86IElMb2NrKTogUHJvbWlzZTxJTG9jaz4ge1xuICAgIGNvbnN0IGZkID0gYXdhaXQgZnMub3Blbih0aGlzLmZpbGVOYW1lLCBtb2RlKTsgLy8gTWF5IGZhaWwgaWYgdGhlIGZpbGUgYWxyZWFkeSBleGlzdHNcbiAgICBhd2FpdCBmZC53cml0ZShgJHtwcm9jZXNzLnBpZH0uYCk7IC8vIFBlcmlvZCBndWFyZHMgYWdhaW5zdCBwYXJ0aWFsIHJlYWRzXG4gICAgYXdhaXQgZmQuY2xvc2UoKTtcblxuICAgIHJldHVybiB7XG4gICAgICByZWxlYXNlOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmZpbGVOYW1lKTtcbiAgICAgICAgYXdhaXQgYWRkaXRpb25hbExvY2s/LnJlbGVhc2UoKTtcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIElMb2NrIHtcbiAgcmVsZWFzZSgpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5hc3luYyBmdW5jdGlvbiBmaWxlRXhpc3RzKGZpbGVOYW1lOiBzdHJpbmcpIHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy5zdGF0KGZpbGVOYW1lKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgaWYgKGUuY29kZSA9PT0gJ0VOT0VOVCcpIHsgcmV0dXJuIGZhbHNlOyB9XG4gICAgdGhyb3cgZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBwcm9jZXNzRXhpc3RzKHBpZDogbnVtYmVyKSB7XG4gIHRyeSB7XG4gICAgcHJvY2Vzcy5raWxsKHBpZCwgMCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZShvayA9PiAoc2V0VGltZW91dChvaywgbXMpIGFzIGFueSkudW5yZWYoKSk7XG59XG5cbmZ1bmN0aW9uIHJhbmRvbVNsZWVwKG1zOiBudW1iZXIpIHtcbiAgcmV0dXJuIHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG59XG4iXX0=