UNPKG

@aws-cdk-testing/cli-integ

Version:

Integration tests for the AWS CDK CLI

207 lines 23.7 kB
"use strict"; 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=