@aws-cdk/core
Version:
AWS Cloud Development Kit Core Library
130 lines • 18.2 kB
JavaScript
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
;