@aws-cdk-testing/cli-integ
Version:
Integration tests for the AWS CDK CLI
214 lines • 23.9 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 {
directory;
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));
}
waitingResolvers = new Set();
watcher;
constructor(directory) {
this.directory = directory;
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 {
pool;
mutexName;
fileName;
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?.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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieHBtdXRleC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInhwbXV0ZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkJBQXNEO0FBQ3RELHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFFN0IsTUFBYSxXQUFXO0lBYWM7SUFaN0IsTUFBTSxDQUFDLGFBQWEsQ0FBQyxTQUFpQjtRQUMzQyxJQUFBLGNBQVMsRUFBQyxTQUFTLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUMxQyxPQUFPLElBQUksV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFTSxNQUFNLENBQUMsUUFBUSxDQUFDLElBQVk7UUFDakMsT0FBTyxXQUFXLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDakUsQ0FBQztJQUVnQixnQkFBZ0IsR0FBRyxJQUFJLEdBQUcsRUFBYyxDQUFDO0lBQ2xELE9BQU8sQ0FBdUM7SUFFdEQsWUFBb0MsU0FBaUI7UUFBakIsY0FBUyxHQUFULFNBQVMsQ0FBUTtRQUNuRCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQUVNLEtBQUssQ0FBQyxJQUFZO1FBQ3ZCLE9BQU8sSUFBSSxPQUFPLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLFdBQVcsQ0FBQyxTQUFrQjtRQUNuQyxNQUFNLElBQUksR0FBRyxJQUFJLE9BQU8sQ0FBTyxFQUFFLENBQUMsRUFBRTtZQUNsQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFO2dCQUNuQyxNQUFNLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDdEIsRUFBRSxFQUFFLENBQUM7WUFDUCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FB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QUMsQ0FBQztBQUMvQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgd2F0Y2gsIHByb21pc2VzIGFzIGZzLCBta2RpclN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBvcyBmcm9tICdvcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuXG5leHBvcnQgY2xhc3MgWHBNdXRleFBvb2wge1xuICBwdWJsaWMgc3RhdGljIGZyb21EaXJlY3RvcnkoZGlyZWN0b3J5OiBzdHJpbmcpIHtcbiAgICBta2RpclN5bmMoZGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICByZXR1cm4gbmV3IFhwTXV0ZXhQb29sKGRpcmVjdG9yeSk7XG4gIH1cblxuICBwdWJsaWMgc3RhdGljIGZyb21OYW1lKG5hbWU6IHN0cmluZykge1xuICAgIHJldHVybiBYcE11dGV4UG9vbC5mcm9tRGlyZWN0b3J5KHBhdGguam9pbihvcy50bXBkaXIoKSwgbmFtZSkpO1xuICB9XG5cbiAgcHJpdmF0ZSByZWFkb25seSB3YWl0aW5nUmVzb2x2ZXJzID0gbmV3IFNldDwoKSA9PiB2b2lkPigpO1xuICBwcml2YXRlIHdhdGNoZXI6IFJldHVyblR5cGU8dHlwZW9mIHdhdGNoPiB8IHVuZGVmaW5lZDtcblxuICBwcml2YXRlIGNvbnN0cnVjdG9yKHB1YmxpYyByZWFkb25seSBkaXJlY3Rvcnk6IHN0cmluZykge1xuICAgIHRoaXMuc3RhcnRXYXRjaCgpO1xuICB9XG5cbiAgcHVibGljIG11dGV4KG5hbWU6IHN0cmluZykge1xuICAgIHJldHVybiBuZXcgWHBNdXRleCh0aGlzLCBuYW1lKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBd2FpdCBhbiB1bmxvY2sgZXZlbnRcbiAgICpcbiAgICogKEFuIHVubG9jayBldmVudCBpcyB3aGVuIGEgZmlsZSBpbiB0aGUgZGlyZWN0b3J5IGdldHMgZGVsZXRlZCwgd2l0aCBhIHRpbnlcbiAgICogcmFuZG9tIHNsZWVwIGF0dGFjaGVkIHRvIGl0KS5cbiAgICovXG4gIHB1YmxpYyBhd2FpdFVubG9jayhtYXhXYWl0TXM/OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB3YWl0ID0gbmV3IFByb21pc2U8dm9pZD4ob2sgPT4ge1xuICAgICAgdGhpcy53YWl0aW5nUmVzb2x2ZXJzLmFkZChhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IHJhbmRvbVNsZWVwKDEwKTtcbiAgICAgICAgb2soKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgaWYgKG1heFdhaXRNcykge1xuICAgICAgcmV0dXJuIFByb21pc2UucmFjZShbd2FpdCwgc2xlZXAobWF4V2FpdE1zKV0pO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gd2FpdDtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0V2F0Y2goKSB7XG4gICAgdGhpcy53YXRjaGVyID0gd2F0Y2godGhpcy5kaXJlY3RvcnkpO1xuICAgICh0aGlzLndhdGNoZXIgYXMgYW55KS51bnJlZigpOyAvLyBAdHlwZXMgZG9lc24ndCBrbm93IGFib3V0IHRoaXMgYnV0IGl0IGV4aXN0c1xuICAgIHRoaXMud2F0Y2hlci5vbignY2hhbmdlJywgYXN5bmMgKGV2ZW50VHlwZSwgZm5hbWUpID0+IHtcbiAgICAgIC8vIE9ubHkgdHJpZ2dlciBvbiAnZGVsZXRlcycuXG4gICAgICAvLyBBZnRlciByZWNlaXZpbmcgdGhlIGV2ZW50LCB3ZSBjaGVjayBpZiB0aGUgZmlsZSBleGlzdHMuXG4gICAgICAvLyAtIElmIG5vOiB0aGUgZmlsZSB3YXMgZGVsZXRlZCEgSHV6emFoLCB0aGlzIGNvdW50cyBhcyBhIHdha2V1cC5cbiAgICAgIC8vIC0gSWYgeWVzOiBlaXRoZXIgdGhlIGZpbGUgd2FzIGp1c3QgY3JlYXRlZCAoaW4gd2hpY2ggY2FzZSB3ZSBkb24ndCBuZWVkIHRvIHdha2V1cClcbiAgICAgIC8vICAgb3IgdGhlIGV2ZW50IHdhcyBkdWUgdG8gYSBkZWxldGUgYnV0IHNvbWVvbmUgcmFjZWQgdXMgdG8gaXQgYW5kIGNsYWltZWQgdGhlXG4gICAgICAvLyAgIGZpbGUgYWxyZWFkeSAoaW4gd2hpY2ggY2FzZSB3ZSBhbHNvIGRvbid0IG5lZWQgdG8gd2FrZSB1cCkuXG4gICAgICBpZiAoZXZlbnRUeXBlID09PSAncmVuYW1lJyAmJiAhYXdhaXQgZmlsZUV4aXN0cyhwYXRoLmpvaW4odGhpcy5kaXJlY3RvcnksIGZuYW1lLnRvU3RyaW5nKCkpKSkge1xuICAgICAgICB0aGlzLm5vdGlmeVdhaXRlcnMoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLndhdGNoZXIub24oJ2Vycm9yJywgYXN5bmMgKGUpID0+IHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICBjb25zb2xlLmVycm9yKGUpO1xuICAgICAgYXdhaXQgcmFuZG9tU2xlZXAoMTAwKTtcbiAgICAgIHRoaXMuc3RhcnRXYXRjaCgpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBub3RpZnlXYWl0ZXJzKCkge1xuICAgIGZvciAoY29uc3QgcHJvbWlzZSBvZiB0aGlzLndhaXRpbmdSZXNvbHZlcnMpIHtcbiAgICAgIHByb21pc2UoKTtcbiAgICB9XG4gICAgdGhpcy53YWl0aW5nUmVzb2x2ZXJzLmNsZWFyKCk7XG4gIH1cbn1cblxuLyoqXG4gKiBDcm9zcy1wcm9jZXNzIG11dGV4XG4gKlxuICogVXNlcyB0aGUgcHJlc2VuY2Ugb2YgYSBmaWxlIG9uIGRpc2sgYW5kIGBmcy53YXRjaGAgdG8gcmVwcmVzZW50IHRoZSBtdXRleFxuICogYW5kIGRpc2NvdmVyIHVubG9ja3MuXG4gKi9cbmV4cG9ydCBjbGFzcyBYcE11dGV4IHtcbiAgcHJpdmF0ZSByZWFkb25seSBmaWxlTmFtZTogc3RyaW5nO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgcG9vbDogWHBNdXRleFBvb2wsIHB1YmxpYyByZWFkb25seSBtdXRleE5hbWU6IHN0cmluZykge1xuICAgIHRoaXMuZmlsZU5hbWUgPSBwYXRoLmpvaW4ocG9vbC5kaXJlY3RvcnksIGAke211dGV4TmFtZX0ubXV0ZXhgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcnkgdG8gYWNxdWlyZSB0aGUgbG9jayAobWF5IGZhaWwpXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdHJ5QWNxdWlyZSgpOiBQcm9taXNlPElMb2NrIHwgdW5kZWZpbmVkPiB7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIC8vIEFjcXVpcmUgbG9jayBieSBiZWluZyB0aGUgb25lIHRvIGNyZWF0ZSB0aGUgZmlsZVxuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMud3JpdGVQaWRGaWxlKCd3eCcpOyAvLyBGYWlscyBpZiB0aGUgZmlsZSBhbHJlYWR5IGV4aXN0c1xuICAgICAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgICAgIGlmIChlLmNvZGUgIT09ICdFRVhJU1QnKSB7XG4gICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBGaWxlIGFscmVhZHkgZXhpc3RzLiBSZWFkIHRoZSBjb250ZW50cywgc2VlIGlmIGl0J3MgYW4gZXhpc3RlbnQgUElEIChpZiBzbywgdGhlIGxvY2sgaXMgdGFrZW4pXG4gICAgICBjb25zdCBvd25lclBpZCA9IGF3YWl0IHRoaXMucmVhZFBpZEZpbGUoKTtcbiAgICAgIGlmIChvd25lclBpZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIC8vIEZpbGUgZ290IGRlbGV0ZWQganVzdCBub3csIG1heWJlIHdlIGNhbiBhY3F1aXJlIGl0IGFnYWluXG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHByb2Nlc3NFeGlzdHMob3duZXJQaWQpKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIG5vdCwgdGhlIGxvY2sgaXMgc3RhbGUgYW5kIHdpbGwgbmV2ZXIgYmUgcmVsZWFzZWQgYW55bW9yZS4gV2UgbWF5XG4gICAgICAvLyBkZWxldGUgaXQgYW5kIGFjcXVpcmUgaXQgYW55d2F5LCBidXQgd2UgbWF5IGJlIHJhY2luZyBzb21lb25lIGVsc2UgdHJ5aW5nXG4gICAgICAvLyB0byBkbyB0aGUgc2FtZS4gU29sdmUgdGhpcyBhcyBmb2xsb3dzOlxuICAgICAgLy8gLSBUcnkgdG8gYWNxdWlyZSBhIGxvY2sgdGhhdCBnaXZlcyB1cyBwZXJtaXNzaW9ucyB0byBkZWNsYXJlIHRoZSBleGlzdGluZyBsb2NrIHN0YWxlLlxuICAgICAgLy8gLSBTbGVlcCBhIHNtYWxsIHJhbmRvbSBwZXJpb2QgdG8gcmVkdWNlIGNvbnRlbnRpb24gb24gdGhpcyBvcGVyYXRpb25cbiAgICAgIGF3YWl0IHJhbmRvbVNsZWVwKDEwKTtcbiAgICAgIGNvbnN0IGlubmVyTXV4ID0gbmV3IFhwTXV0ZXgodGhpcy5wb29sLCBgJHt0aGlzLm11dGV4TmFtZX0uJHtvd25lclBpZH1gKTtcbiAgICAgIGNvbnN0IGlubmVyTG9jayA9IGF3YWl0IGlubmVyTXV4LnRyeUFjcXVpcmUoKTtcbiAgICAgIGlmICghaW5uZXJMb2NrKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICB9XG5cbiAgICAgIC8vIFdlIG1heSBub3QgcmVsZWFzZSB0aGUgJ2lubmVyIGxvY2snIHdlIHVzZWQgdG8gYWNxdWlyZSB0aGUgcmlnaHRzIHRvIGRlY2xhcmUgdGhlIG90aGVyXG4gICAgICAvLyBsb2NrIHN0YWxlIHVudGlsIHdlIHJlbGVhc2UgdGhlIGFjdHVhbCBsb2NrIGl0c2VsZi4gSWYgd2UgZGlkLCBvdGhlciBjb250ZW5kZXJzIG1pZ2h0XG4gICAgICAvLyBzZWUgaXQgcmVsZWFzZWQgd2hpbGUgdGhleSdyZSBzdGlsbCBpbiB0aGlzIGZhbGxiYWNrIGJsb2NrIGFuZCBhY2NpZGVudGFsbHkgc3RlYWxcbiAgICAgIC8vIGZyb20gYSBuZXcgbGVnaXRpbWF0ZSBvd25lci5cbiAgICAgIHJldHVybiB0aGlzLndyaXRlUGlkRmlsZSgndycsIGlubmVyTG9jayk7IC8vIEZvcmNlIHdyaXRlIGxvY2sgZmlsZSwgYXR0YWNoIGlubmVyIGxvY2sgYXMgd2VsbFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBY3F1aXJlIHRoZSBsb2NrLCB3YWl0aW5nIHVudGlsIHdlIGNhblxuICAgKi9cbiAgcHVibGljIGFzeW5jIGFjcXVpcmUoKTogUHJvbWlzZTxJTG9jaz4ge1xuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAvLyBTdGFydCB0aGUgd2FpdCBoZXJlLCBzbyB3ZSBkb24ndCBtaXNzIHRoZSBzaWduYWwgaWYgaXQgY29tZXMgYWZ0ZXJcbiAgICAgIC8vIHdlIHRyeSBidXQgYmVmb3JlIHdlIHNsZWVwLlxuICAgICAgLy9cbiAgICAgIC8vIFdlIGFsc28gcGVyaW9kaWNhbGx5IHJldHJ5IGFueXdheSBzaW5jZSB3ZSBtYXkgaGF2ZSBtaXNzZWQgdGhlIGRlbGV0ZVxuICAgICAgLy8gc2lnbmFsIGR1ZSB0byB1bmZvcnR1bmF0ZSB0aW1pbmcuXG4gICAgICBjb25zdCB3YWl0ID0gdGhpcy5wb29sLmF3YWl0VW5sb2NrKDUwMDApO1xuXG4gICAgICBjb25zdCBsb2NrID0gYXdhaXQgdGhpcy50cnlBY3F1aXJlKCk7XG4gICAgICBpZiAobG9jaykge1xuICAgICAgICAvLyBJZ25vcmUgdGhlIHdhaXQgKGNvdW50IGFzIGhhbmRsZWQpXG4gICAgICAgIHdhaXQudGhlbigoKSA9PiB7XG4gICAgICAgIH0sICgpID0+IHtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBsb2NrO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCB3YWl0O1xuICAgICAgYXdhaXQgcmFuZG9tU2xlZXAoMTAwKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlYWRQaWRGaWxlKCk6IFByb21pc2U8bnVtYmVyIHwgdW5kZWZpbmVkPiB7XG4gICAgY29uc3QgZGVhZExpbmUgPSBEYXRlLm5vdygpICsgMTAwMDtcbiAgICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRMaW5lKSB7XG4gICAgICBsZXQgY29udGVudHM7XG4gICAgICB0cnkge1xuICAgICAgICBjb250ZW50cyA9IGF3YWl0IGZzLnJlYWRGaWxlKHRoaXMuZmlsZU5hbWUsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSk7XG4gICAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgICAgaWYgKGUuY29kZSA9PT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICAgIHRocm93IGU7XG4gICAgICB9XG5cbiAgICAgIC8vIFJldHJ5IHVudGlsIHdlJ3ZlIHNlZW4gdGhlIGZ1bGwgY29udGVudHNcbiAgICAgIGlmIChjb250ZW50cy5lbmRzV2l0aCgnLicpKSB7XG4gICAgICAgIHJldHVybiBwYXJzZUludChjb250ZW50cy5zdWJzdHJpbmcoMCwgY29udGVudHMubGVuZ3RoIC0gMSksIDEwKTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IHNsZWVwKDEwKTtcbiAgICB9XG5cbiAgICB0aHJvdyBuZXcgRXJyb3IoYCR7dGhpcy5maWxlTmFtZX0gd2FzIG5ldmVyIGNvbXBsZXRlbHkgd3JpdHRlbmApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB3cml0ZVBpZEZpbGUobW9kZTogc3RyaW5nLCBhZGRpdGlvbmFsTG9jaz86IElMb2NrKTogUHJvbWlzZTxJTG9jaz4ge1xuICAgIGNvbnN0IGZkID0gYXdhaXQgZnMub3Blbih0aGlzLmZpbGVOYW1lLCBtb2RlKTsgLy8gTWF5IGZhaWwgaWYgdGhlIGZpbGUgYWxyZWFkeSBleGlzdHNcbiAgICBhd2FpdCBmZC53cml0ZShgJHtwcm9jZXNzLnBpZH0uYCk7IC8vIFBlcmlvZCBndWFyZHMgYWdhaW5zdCBwYXJ0aWFsIHJlYWRzXG4gICAgYXdhaXQgZmQuY2xvc2UoKTtcblxuICAgIHJldHVybiB7XG4gICAgICByZWxlYXNlOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmZpbGVOYW1lKTtcbiAgICAgICAgYXdhaXQgYWRkaXRpb25hbExvY2s/LnJlbGVhc2UoKTtcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIElMb2NrIHtcbiAgcmVsZWFzZSgpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5hc3luYyBmdW5jdGlvbiBmaWxlRXhpc3RzKGZpbGVOYW1lOiBzdHJpbmcpIHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy5zdGF0KGZpbGVOYW1lKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgaWYgKGUuY29kZSA9PT0gJ0VOT0VOVCcpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgdGhyb3cgZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBwcm9jZXNzRXhpc3RzKHBpZDogbnVtYmVyKSB7XG4gIHRyeSB7XG4gICAgcHJvY2Vzcy5raWxsKHBpZCwgMCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZShvayA9PiAoc2V0VGltZW91dChvaywgbXMpIGFzIGFueSkudW5yZWYoKSk7XG59XG5cbmZ1bmN0aW9uIHJhbmRvbVNsZWVwKG1zOiBudW1iZXIpIHtcbiAgcmV0dXJuIHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG59XG4iXX0=