UNPKG

@aws-cdk-testing/cli-integ

Version:

Integration tests for the AWS CDK CLI

214 lines 23.9 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 { 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=