UNPKG

@aws-cdk/core

Version:

AWS Cloud Development Kit Core Library

130 lines 18.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.contentFingerprint = exports.fingerprint = void 0; const crypto = require("crypto"); const fs = require("fs"); const path = require("path"); const ignore_1 = require("./ignore"); const options_1 = require("./options"); const utils_1 = require("./utils"); const BUFFER_SIZE = 8 * 1024; const CTRL_SOH = '\x01'; const CTRL_SOT = '\x02'; const CTRL_ETX = '\x03'; const CR = '\r'; const LF = '\n'; const CRLF = `${CR}${LF}`; /** * Produces fingerprint based on the contents of a single file or an entire directory tree. * * Line endings are converted from CRLF to LF. * * The fingerprint will also include: * 1. An extra string if defined in `options.extra`. * 2. The symlink follow mode value. * * @param fileOrDirectory The directory or file to fingerprint * @param options Fingerprinting options */ function fingerprint(fileOrDirectory, options = {}) { const hash = crypto.createHash('sha256'); _hashField(hash, 'options.extra', options.extraHash || ''); const follow = options.follow || options_1.SymlinkFollowMode.EXTERNAL; _hashField(hash, 'options.follow', follow); // Resolve symlinks in the initial path (for example, the root directory // might be symlinked). It's important that we know the absolute path, so we // can judge if further symlinks inside the target directory are within the // target or not (if we don't resolve, we would test w.r.t. the wrong path). fileOrDirectory = fs.realpathSync(fileOrDirectory); const isDir = fs.statSync(fileOrDirectory).isDirectory(); const rootDirectory = isDir ? fileOrDirectory : path.dirname(fileOrDirectory); const ignoreMode = options.ignoreMode || options_1.IgnoreMode.GLOB; if (ignoreMode != options_1.IgnoreMode.GLOB) { _hashField(hash, 'options.ignoreMode', ignoreMode); } const ignoreStrategy = ignore_1.IgnoreStrategy.fromCopyOptions(options, fileOrDirectory); _processFileOrDirectory(fileOrDirectory, isDir); return hash.digest('hex'); function _processFileOrDirectory(symbolicPath, isRootDir = false, realPath = symbolicPath) { if (!isRootDir && ignoreStrategy.ignores(symbolicPath)) { return; } const stat = fs.lstatSync(realPath); // Use relative path as hash component. Normalize it with forward slashes to ensure // same hash on Windows and Linux. const hashComponent = path.relative(fileOrDirectory, symbolicPath).replace(/\\/g, '/'); if (stat.isSymbolicLink()) { const linkTarget = fs.readlinkSync(realPath); const resolvedLinkTarget = path.resolve(path.dirname(realPath), linkTarget); if (utils_1.shouldFollow(follow, rootDirectory, resolvedLinkTarget)) { _processFileOrDirectory(symbolicPath, false, resolvedLinkTarget); } else { _hashField(hash, `link:${hashComponent}`, linkTarget); } } else if (stat.isFile()) { _hashField(hash, `file:${hashComponent}`, contentFingerprint(realPath)); } else if (stat.isDirectory()) { for (const item of fs.readdirSync(realPath).sort()) { _processFileOrDirectory(path.join(symbolicPath, item), false, path.join(realPath, item)); } } else { throw new Error(`Unable to hash ${symbolicPath}: it is neither a file nor a directory`); } } } exports.fingerprint = fingerprint; function contentFingerprint(file) { const hash = crypto.createHash('sha256'); const buffer = Buffer.alloc(BUFFER_SIZE); // eslint-disable-next-line no-bitwise const fd = fs.openSync(file, fs.constants.O_DSYNC | fs.constants.O_RDONLY | fs.constants.O_SYNC); let size = 0; let isBinary = false; let lastStr = ''; let read = 0; try { while ((read = fs.readSync(fd, buffer, 0, BUFFER_SIZE, null)) !== 0) { const slicedBuffer = buffer.slice(0, read); // Detect if file is binary by checking the first 8k bytes for the // null character (git like implementation) if (size === 0) { isBinary = slicedBuffer.indexOf(0) !== -1; } let dataBuffer = slicedBuffer; if (!isBinary) { // Line endings normalization (CRLF -> LF) const str = buffer.slice(0, read).toString(); // We are going to normalize line endings to LF. So if the current // buffer ends with CR, it could be that the next one starts with // LF so we need to save it for later use. if (new RegExp(`${CR}$`).test(str)) { lastStr += str; continue; } const data = lastStr + str; const normalizedData = data.replace(new RegExp(CRLF, 'g'), LF); dataBuffer = Buffer.from(normalizedData); lastStr = ''; } size += dataBuffer.length; hash.update(dataBuffer); } if (lastStr) { hash.update(Buffer.from(lastStr)); } } finally { fs.closeSync(fd); } return `${size}:${hash.digest('hex')}`; } exports.contentFingerprint = contentFingerprint; function _hashField(hash, header, value) { hash.update(CTRL_SOH).update(header).update(CTRL_SOT).update(value).update(CTRL_ETX); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmluZ2VycHJpbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJmaW5nZXJwcmludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxpQ0FBaUM7QUFDakMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3QixxQ0FBMEM7QUFDMUMsdUNBQThFO0FBQzlFLG1DQUF1QztBQUV2QyxNQUFNLFdBQVcsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDO0FBQzdCLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQztBQUN4QixNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUM7QUFDeEIsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDO0FBQ3hCLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQztBQUNoQixNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFDaEIsTUFBTSxJQUFJLEdBQUcsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUM7QUFFMUI7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxTQUFnQixXQUFXLENBQUMsZUFBdUIsRUFBRSxVQUE4QixFQUFHO0lBQ3BGLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDekMsVUFBVSxDQUFDLElBQUksRUFBRSxlQUFlLEVBQUUsT0FBTyxDQUFDLFNBQVMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMzRCxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxJQUFJLDJCQUFpQixDQUFDLFFBQVEsQ0FBQztJQUM1RCxVQUFVLENBQUMsSUFBSSxFQUFFLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBRTNDLHdFQUF3RTtJQUN4RSw0RUFBNEU7SUFDNUUsMkVBQTJFO0lBQzNFLDRFQUE0RTtJQUM1RSxlQUFlLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUVuRCxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLGVBQWUsQ0FBQyxDQUFDLFdB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M0IsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQy9ELFVBQVUsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO2dCQUN6QyxPQUFPLEdBQUcsRUFBRSxDQUFDO2FBQ2Q7WUFFRCxJQUFJLElBQUksVUFBVSxDQUFDLE1BQU0sQ0FBQztZQUMxQixJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1NBQ3pCO1FBRUQsSUFBSSxPQUFPLEVBQUU7WUFDWCxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztTQUNuQztLQUNGO1lBQVM7UUFDUixFQUFFLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0tBQ2xCO0lBQ0QsT0FBTyxHQUFHLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7QUFDekMsQ0FBQztBQWhERCxnREFnREM7QUFFRCxTQUFTLFVBQVUsQ0FBQyxJQUFpQixFQUFFLE1BQWMsRUFBRSxLQUFpQztJQUN0RixJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUN2RixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgSWdub3JlU3RyYXRlZ3kgfSBmcm9tICcuL2lnbm9yZSc7XG5pbXBvcnQgeyBGaW5nZXJwcmludE9wdGlvbnMsIElnbm9yZU1vZGUsIFN5bWxpbmtGb2xsb3dNb2RlIH0gZnJvbSAnLi9vcHRpb25zJztcbmltcG9ydCB7IHNob3VsZEZvbGxvdyB9IGZyb20gJy4vdXRpbHMnO1xuXG5jb25zdCBCVUZGRVJfU0laRSA9IDggKiAxMDI0O1xuY29uc3QgQ1RSTF9TT0ggPSAnXFx4MDEnO1xuY29uc3QgQ1RSTF9TT1QgPSAnXFx4MDInO1xuY29uc3QgQ1RSTF9FVFggPSAnXFx4MDMnO1xuY29uc3QgQ1IgPSAnXFxyJztcbmNvbnN0IExGID0gJ1xcbic7XG5jb25zdCBDUkxGID0gYCR7Q1J9JHtMRn1gO1xuXG4vKipcbiAqIFByb2R1Y2VzIGZpbmdlcnByaW50IGJhc2VkIG9uIHRoZSBjb250ZW50cyBvZiBhIHNpbmdsZSBmaWxlIG9yIGFuIGVudGlyZSBkaXJlY3RvcnkgdHJlZS5cbiAqXG4gKiBMaW5lIGVuZGluZ3MgYXJlIGNvbnZlcnRlZCBmcm9tIENSTEYgdG8gTEYuXG4gKlxuICogVGhlIGZpbmdlcnByaW50IHdpbGwgYWxzbyBpbmNsdWRlOlxuICogMS4gQW4gZXh0cmEgc3RyaW5nIGlmIGRlZmluZWQgaW4gYG9wdGlvbnMuZXh0cmFgLlxuICogMi4gVGhlIHN5bWxpbmsgZm9sbG93IG1vZGUgdmFsdWUuXG4gKlxuICogQHBhcmFtIGZpbGVPckRpcmVjdG9yeSBUaGUgZGlyZWN0b3J5IG9yIGZpbGUgdG8gZmluZ2VycHJpbnRcbiAqIEBwYXJhbSBvcHRpb25zIEZpbmdlcnByaW50aW5nIG9wdGlvbnNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbmdlcnByaW50KGZpbGVPckRpcmVjdG9yeTogc3RyaW5nLCBvcHRpb25zOiBGaW5nZXJwcmludE9wdGlvbnMgPSB7IH0pIHtcbiAgY29uc3QgaGFzaCA9IGNyeXB0by5jcmVhdGVIYXNoKCdzaGEyNTYnKTtcbiAgX2hhc2hGaWVsZChoYXNoLCAnb3B0aW9ucy5leHRyYScsIG9wdGlvbnMuZXh0cmFIYXNoIHx8ICcnKTtcbiAgY29uc3QgZm9sbG93ID0gb3B0aW9ucy5mb2xsb3cgfHwgU3ltbGlua0ZvbGxvd01vZGUuRVhURVJOQUw7XG4gIF9oYXNoRmllbGQoaGFzaCwgJ29wdGlvbnMuZm9sbG93JywgZm9sbG93KTtcblxuICAvLyBSZXNvbHZlIHN5bWxpbmtzIGluIHRoZSBpbml0aWFsIHBhdGggKGZvciBleGFtcGxlLCB0aGUgcm9vdCBkaXJlY3RvcnlcbiAgLy8gbWlnaHQgYmUgc3ltbGlua2VkKS4gSXQncyBpbXBvcnRhbnQgdGhhdCB3ZSBrbm93IHRoZSBhYnNvbHV0ZSBwYXRoLCBzbyB3ZVxuICAvLyBjYW4ganVkZ2UgaWYgZnVydGhlciBzeW1saW5rcyBpbnNpZGUgdGhlIHRhcmdldCBkaXJlY3RvcnkgYXJlIHdpdGhpbiB0aGVcbiAgLy8gdGFyZ2V0IG9yIG5vdCAoaWYgd2UgZG9uJ3QgcmVzb2x2ZSwgd2Ugd291bGQgdGVzdCB3LnIudC4gdGhlIHdyb25nIHBhdGgpLlxuICBmaWxlT3JEaXJlY3RvcnkgPSBmcy5yZWFscGF0aFN5bmMoZmlsZU9yRGlyZWN0b3J5KTtcblxuICBjb25zdCBpc0RpciA9IGZzLnN0YXRTeW5jKGZpbGVPckRpcmVjdG9yeSkuaXNEaXJlY3RvcnkoKTtcbiAgY29uc3Qgcm9vdERpcmVjdG9yeSA9IGlzRGlyXG4gICAgPyBmaWxlT3JEaXJlY3RvcnlcbiAgICA6IHBhdGguZGlybmFtZShmaWxlT3JEaXJlY3RvcnkpO1xuXG4gIGNvbnN0IGlnbm9yZU1vZGUgPSBvcHRpb25zLmlnbm9yZU1vZGUgfHwgSWdub3JlTW9kZS5HTE9CO1xuICBpZiAoaWdub3JlTW9kZSAhPSBJZ25vcmVNb2RlLkdMT0IpIHtcbiAgICBfaGFzaEZpZWxkKGhhc2gsICdvcHRpb25zLmlnbm9yZU1vZGUnLCBpZ25vcmVNb2RlKTtcbiAgfVxuXG4gIGNvbnN0IGlnbm9yZVN0cmF0ZWd5ID0gSWdub3JlU3RyYXRlZ3kuZnJvbUNvcHlPcHRpb25zKG9wdGlvbnMsIGZpbGVPckRpcmVjdG9yeSk7XG4gIF9wcm9jZXNzRmlsZU9yRGlyZWN0b3J5KGZpbGVPckRpcmVjdG9yeSwgaXNEaXIpO1xuXG4gIHJldHVybiBoYXNoLmRpZ2VzdCgnaGV4Jyk7XG5cbiAgZnVuY3Rpb24gX3Byb2Nlc3NGaWxlT3JEaXJlY3Rvcnkoc3ltYm9saWNQYXRoOiBzdHJpbmcsIGlzUm9vdERpcjogYm9vbGVhbiA9IGZhbHNlLCByZWFsUGF0aCA9IHN5bWJvbGljUGF0aCkge1xuICAgIGlmICghaXNSb290RGlyICYmIGlnbm9yZVN0cmF0ZWd5Lmlnbm9yZXMoc3ltYm9saWNQYXRoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHN0YXQgPSBmcy5sc3RhdFN5bmMocmVhbFBhdGgpO1xuXG4gICAgLy8gVXNlIHJlbGF0aXZlIHBhdGggYXMgaGFzaCBjb21wb25lbnQuIE5vcm1hbGl6ZSBpdCB3aXRoIGZvcndhcmQgc2xhc2hlcyB0byBlbnN1cmVcbiAgICAvLyBzYW1lIGhhc2ggb24gV2luZG93cyBhbmQgTGludXguXG4gICAgY29uc3QgaGFzaENvbXBvbmVudCA9IHBhdGgucmVsYXRpdmUoZmlsZU9yRGlyZWN0b3J5LCBzeW1ib2xpY1BhdGgpLnJlcGxhY2UoL1xcXFwvZywgJy8nKTtcblxuICAgIGlmIChzdGF0LmlzU3ltYm9saWNMaW5rKCkpIHtcbiAgICAgIGNvbnN0IGxpbmtUYXJnZXQgPSBmcy5yZWFkbGlua1N5bmMocmVhbFBhdGgpO1xuICAgICAgY29uc3QgcmVzb2x2ZWRMaW5rVGFyZ2V0ID0gcGF0aC5yZXNvbHZlKHBhdGguZGlybmFtZShyZWFsUGF0aCksIGxpbmtUYXJnZXQpO1xuICAgICAgaWYgKHNob3VsZEZvbGxvdyhmb2xsb3csIHJvb3REaXJlY3RvcnksIHJlc29sdmVkTGlua1RhcmdldCkpIHtcbiAgICAgICAgX3Byb2Nlc3NGaWxlT3JEaXJlY3Rvcnkoc3ltYm9saWNQYXRoLCBmYWxzZSwgcmVzb2x2ZWRMaW5rVGFyZ2V0KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIF9oYXNoRmllbGQoaGFzaCwgYGxpbms6JHtoYXNoQ29tcG9uZW50fWAsIGxpbmtUYXJnZXQpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoc3RhdC5pc0ZpbGUoKSkge1xuICAgICAgX2hhc2hGaWVsZChoYXNoLCBgZmlsZToke2hhc2hDb21wb25lbnR9YCwgY29udGVudEZpbmdlcnByaW50KHJlYWxQYXRoKSk7XG4gICAgfSBlbHNlIGlmIChzdGF0LmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgIGZvciAoY29uc3QgaXRlbSBvZiBmcy5yZWFkZGlyU3luYyhyZWFsUGF0aCkuc29ydCgpKSB7XG4gICAgICAgIF9wcm9jZXNzRmlsZU9yRGlyZWN0b3J5KHBhdGguam9pbihzeW1ib2xpY1BhdGgsIGl0ZW0pLCBmYWxzZSwgcGF0aC5qb2luKHJlYWxQYXRoLCBpdGVtKSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIGhhc2ggJHtzeW1ib2xpY1BhdGh9OiBpdCBpcyBuZWl0aGVyIGEgZmlsZSBub3IgYSBkaXJlY3RvcnlgKTtcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbnRlbnRGaW5nZXJwcmludChmaWxlOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBoYXNoID0gY3J5cHRvLmNyZWF0ZUhhc2goJ3NoYTI1NicpO1xuICBjb25zdCBidWZmZXIgPSBCdWZmZXIuYWxsb2MoQlVGRkVSX1NJWkUpO1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tYml0d2lzZVxuICBjb25zdCBmZCA9IGZzLm9wZW5TeW5jKGZpbGUsIGZzLmNvbnN0YW50cy5PX0RTWU5DIHwgZnMuY29uc3RhbnRzLk9fUkRPTkxZIHwgZnMuY29uc3RhbnRzLk9fU1lOQyk7XG4gIGxldCBzaXplID0gMDtcbiAgbGV0IGlzQmluYXJ5ID0gZmFsc2U7XG4gIGxldCBsYXN0U3RyID0gJyc7XG4gIGxldCByZWFkID0gMDtcbiAgdHJ5IHtcbiAgICB3aGlsZSAoKHJlYWQgPSBmcy5yZWFkU3luYyhmZCwgYnVmZmVyLCAwLCBCVUZGRVJfU0laRSwgbnVsbCkpICE9PSAwKSB7XG4gICAgICBjb25zdCBzbGljZWRCdWZmZXIgPSBidWZmZXIuc2xpY2UoMCwgcmVhZCk7XG5cbiAgICAgIC8vIERldGVjdCBpZiBmaWxlIGlzIGJpbmFyeSBieSBjaGVja2luZyB0aGUgZmlyc3QgOGsgYnl0ZXMgZm9yIHRoZVxuICAgICAgLy8gbnVsbCBjaGFyYWN0ZXIgKGdpdCBsaWtlIGltcGxlbWVudGF0aW9uKVxuICAgICAgaWYgKHNpemUgPT09IDApIHtcbiAgICAgICAgaXNCaW5hcnkgPSBzbGljZWRCdWZmZXIuaW5kZXhPZigwKSAhPT0gLTE7XG4gICAgICB9XG5cbiAgICAgIGxldCBkYXRhQnVmZmVyID0gc2xpY2VkQnVmZmVyO1xuICAgICAgaWYgKCFpc0JpbmFyeSkgeyAvLyBMaW5lIGVuZGluZ3Mgbm9ybWFsaXphdGlvbiAoQ1JMRiAtPiBMRilcbiAgICAgICAgY29uc3Qgc3RyID0gYnVmZmVyLnNsaWNlKDAsIHJlYWQpLnRvU3RyaW5nKCk7XG5cbiAgICAgICAgLy8gV2UgYXJlIGdvaW5nIHRvIG5vcm1hbGl6ZSBsaW5lIGVuZGluZ3MgdG8gTEYuIFNvIGlmIHRoZSBjdXJyZW50XG4gICAgICAgIC8vIGJ1ZmZlciBlbmRzIHdpdGggQ1IsIGl0IGNvdWxkIGJlIHRoYXQgdGhlIG5leHQgb25lIHN0YXJ0cyB3aXRoXG4gICAgICAgIC8vIExGIHNvIHdlIG5lZWQgdG8gc2F2ZSBpdCBmb3IgbGF0ZXIgdXNlLlxuICAgICAgICBpZiAobmV3IFJlZ0V4cChgJHtDUn0kYCkudGVzdChzdHIpKSB7XG4gICAgICAgICAgbGFzdFN0ciArPSBzdHI7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBkYXRhID0gbGFzdFN0ciArIHN0cjtcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZERhdGEgPSBkYXRhLnJlcGxhY2UobmV3IFJlZ0V4cChDUkxGLCAnZycpLCBMRik7XG4gICAgICAgIGRhdGFCdWZmZXIgPSBCdWZmZXIuZnJvbShub3JtYWxpemVkRGF0YSk7XG4gICAgICAgIGxhc3RTdHIgPSAnJztcbiAgICAgIH1cblxuICAgICAgc2l6ZSArPSBkYXRhQnVmZmVyLmxlbmd0aDtcbiAgICAgIGhhc2gudXBkYXRlKGRhdGFCdWZmZXIpO1xuICAgIH1cblxuICAgIGlmIChsYXN0U3RyKSB7XG4gICAgICBoYXNoLnVwZGF0ZShCdWZmZXIuZnJvbShsYXN0U3RyKSk7XG4gICAgfVxuICB9IGZpbmFsbHkge1xuICAgIGZzLmNsb3NlU3luYyhmZCk7XG4gIH1cbiAgcmV0dXJuIGAke3NpemV9OiR7aGFzaC5kaWdlc3QoJ2hleCcpfWA7XG59XG5cbmZ1bmN0aW9uIF9oYXNoRmllbGQoaGFzaDogY3J5cHRvLkhhc2gsIGhlYWRlcjogc3RyaW5nLCB2YWx1ZTogc3RyaW5nIHwgQnVmZmVyIHwgRGF0YVZpZXcpIHtcbiAgaGFzaC51cGRhdGUoQ1RSTF9TT0gpLnVwZGF0ZShoZWFkZXIpLnVwZGF0ZShDVFJMX1NPVCkudXBkYXRlKHZhbHVlKS51cGRhdGUoQ1RSTF9FVFgpO1xufVxuIl19