kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
601 lines (570 loc) • 65.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SUPPORTED_DUCKDB_DROP_EXTENSIONS = void 0;
exports.checkIsSelectQuery = checkIsSelectQuery;
exports.constructST_asWKBQuery = constructST_asWKBQuery;
exports.dropTableIfExists = void 0;
exports.getDuckDBColumnTypes = getDuckDBColumnTypes;
exports.getDuckDBColumnTypesMap = getDuckDBColumnTypesMap;
exports.getGeometryColumns = getGeometryColumns;
exports.isGeoArrowLineString = isGeoArrowLineString;
exports.isGeoArrowMultiLineString = isGeoArrowMultiLineString;
exports.isGeoArrowMultiPoint = isGeoArrowMultiPoint;
exports.isGeoArrowMultiPolygon = isGeoArrowMultiPolygon;
exports.isGeoArrowPoint = isGeoArrowPoint;
exports.isGeoArrowPolygon = isGeoArrowPolygon;
exports.removeSQLComments = removeSQLComments;
exports.restoreUnsupportedExtensions = exports.restoreArrowTable = exports.removeUnsupportedExtensions = void 0;
exports.sanitizeDuckDBTableName = sanitizeDuckDBTableName;
exports.setGeoArrowWKBExtension = setGeoArrowWKBExtension;
exports.splitSqlStatements = splitSqlStatements;
exports.tableFromFile = tableFromFile;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var arrow = _interopRequireWildcard(require("apache-arrow"));
var _type = require("apache-arrow/type");
var _duckdbWasm = require("@duckdb/duckdb-wasm");
var _constants = require("@kepler.gl/constants");
var _init = require("../init");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
// Copied from loaders.gl/geoarrow
// TODO: Remove isGeoArrow* once Kepler.gl is upgraded to loaders.gl 4.4+
var SUPPORTED_DUCKDB_DROP_EXTENSIONS = exports.SUPPORTED_DUCKDB_DROP_EXTENSIONS = ['arrow', 'csv', 'geojson', 'json', 'parquet'];
/**
* Queries a DuckDB table for the schema description.
* @param connection An active DuckDB connection.
* @param tableName A name of DuckDB table to query.
* @returns An array of column names and DuckDB types.
*/
function getDuckDBColumnTypes(_x, _x2) {
return _getDuckDBColumnTypes.apply(this, arguments);
}
/**
* Generates a mapping of column names to their corresponding DuckDB data types.
* @param columns An array of column descriptions from DuckDB. Check getDuckDBColumnTypes.
* @returns A record where keys are column names and values are their data types.
*/
function _getDuckDBColumnTypes() {
_getDuckDBColumnTypes = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(connection, tableName) {
var resDescribe, duckDbTypes, numRows, i, _resDescribe$getChild, _resDescribe$getChild2, columnName, columnType;
return _regenerator["default"].wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
_context2.next = 2;
return connection.query("DESCRIBE \"".concat(tableName, "\";"));
case 2:
resDescribe = _context2.sent;
duckDbTypes = [];
numRows = resDescribe.numRows;
for (i = 0; i < numRows; ++i) {
columnName = (_resDescribe$getChild = resDescribe.getChildAt(0)) === null || _resDescribe$getChild === void 0 ? void 0 : _resDescribe$getChild.get(i);
columnType = (_resDescribe$getChild2 = resDescribe.getChildAt(1)) === null || _resDescribe$getChild2 === void 0 ? void 0 : _resDescribe$getChild2.get(i);
duckDbTypes.push({
name: columnName,
type: columnType
});
}
return _context2.abrupt("return", duckDbTypes);
case 7:
case "end":
return _context2.stop();
}
}, _callee2);
}));
return _getDuckDBColumnTypes.apply(this, arguments);
}
function getDuckDBColumnTypesMap(columns) {
return columns.reduce(function (acc, value) {
acc[value.name] = value.type;
return acc;
}, {});
}
/**
* Constructs an SQL query to select all columns from a given table,
* converting specified columns to Well-Known Binary (WKB) format using ST_AsWKB.
* @param tableName The name of the table from which to select data.
* @param columnsToConvertToWKB An array of column names that should be converted to WKB format.
* @returns The constructed SQL query.
*/
function constructST_asWKBQuery(tableName, columnsToConvertToWKB) {
var exclude = columnsToConvertToWKB.length > 0 ? "EXCLUDE ".concat(columnsToConvertToWKB.join(', ')) : '';
var asWKB = columnsToConvertToWKB.length > 0 ? ", ".concat(columnsToConvertToWKB.map(function (column) {
return "ST_AsWKB(".concat(column, ") as ").concat(column);
}).join(', ')) : '';
return "SELECT * ".concat(exclude, " ").concat(asWKB, " FROM '").concat(tableName, "';");
}
/**
* Finds the names of columns that have a GEOMETRY type.
* @param columns An array of column descriptors from a DuckDB table.
* @returns An array of column names that are of type GEOMETRY.
*/
function getGeometryColumns(columns) {
return columns.filter(function (column) {
return column.type === 'GEOMETRY';
}).map(function (column) {
return column.name;
});
}
/**
* Sets the GeoArrow WKB extension metadata for columns of type GEOMETRY in an Arrow table.
* @param table The Apache Arrow table whose schema fields will be modified.
* @param columns An array of column descriptors from a DuckDB table.
*/
function setGeoArrowWKBExtension(table, columns) {
table.schema.fields.forEach(function (field) {
var info = columns.find(function (t) {
return t.name === field.name;
});
if ((info === null || info === void 0 ? void 0 : info.type) === 'GEOMETRY') {
field.metadata.set(_constants.GEOARROW_METADATA_KEY, _constants.GEOARROW_EXTENSIONS.WKB);
}
});
}
/**
* Creates an arrow table from an array of arrow vectors and fields.
* @param columns An array of arrow vectors.
* @param fields An array of fields per arrow vector.
* @param arrowSchema Optional arrow table schema when available.
* @returns An arrow table.
*/
var restoreArrowTable = exports.restoreArrowTable = function restoreArrowTable(columns, fields, arrowSchema) {
var creaOpts = {};
fields.map(function (field, index) {
creaOpts[field.name] = columns[index];
});
return arrowSchema ? new arrow.Table(arrowSchema, creaOpts) : new arrow.Table(creaOpts);
};
/**
* DuckDb throws when geoarrow extensions are present in metadata.
* @param table An arrow table to clear from extensions.
* @returns A map of removed per field geoarrow extensions.
*/
var removeUnsupportedExtensions = exports.removeUnsupportedExtensions = function removeUnsupportedExtensions(table) {
var removedMetadata = {};
table.schema.fields.forEach(function (field) {
var extension = field.metadata.get(_constants.GEOARROW_METADATA_KEY);
if (extension !== null && extension !== void 0 && extension.startsWith('geoarrow')) {
removedMetadata[field.name] = extension;
field.metadata["delete"](_constants.GEOARROW_METADATA_KEY);
}
});
return removedMetadata;
};
/**
* Restore removed metadata extensions after a call to removeUnsupportedExtensions.
* @param table An arrow table to restore geoarrow extensions.
* @param removedExtensions A map of per field geoarrow extensions to restore.
*/
var restoreUnsupportedExtensions = exports.restoreUnsupportedExtensions = function restoreUnsupportedExtensions(table, removedExtensions) {
table.schema.fields.forEach(function (field) {
var extension = removedExtensions[field.name];
if (extension) {
field.metadata.set(_constants.GEOARROW_METADATA_KEY, extension);
}
});
};
/** Checks whether the given Apache Arrow JS type is a Point data type */
function isGeoArrowPoint(type) {
if (_type.DataType.isFixedSizeList(type)) {
// Check list size
if (![2, 3, 4].includes(type.listSize)) {
return false;
}
// Check child of FixedSizeList is floating type
if (!_type.DataType.isFloat(type.children[0])) {
return false;
}
return true;
}
return false;
}
/** Checks whether the given Apache Arrow JS type is a Point data type */
function isGeoArrowLineString(type) {
// Check the outer type is a List
if (!_type.DataType.isList(type)) {
return false;
}
// Check the child is a point type
if (!isGeoArrowPoint(type.children[0].type)) {
return false;
}
return true;
}
/** Checks whether the given Apache Arrow JS type is a Polygon data type */
function isGeoArrowPolygon(type) {
// Check the outer vector is a List
if (!_type.DataType.isList(type)) {
return false;
}
// Check the child is a linestring vector
if (!isGeoArrowLineString(type.children[0].type)) {
return false;
}
return true;
}
/** Checks whether the given Apache Arrow JS type is a Polygon data type */
function isGeoArrowMultiPoint(type) {
// Check the outer vector is a List
if (!_type.DataType.isList(type)) {
return false;
}
// Check the child is a point vector
if (!isGeoArrowPoint(type.children[0].type)) {
return false;
}
return true;
}
/** Checks whether the given Apache Arrow JS type is a Polygon data type */
function isGeoArrowMultiLineString(type) {
// Check the outer vector is a List
if (!_type.DataType.isList(type)) {
return false;
}
// Check the child is a linestring vector
if (!isGeoArrowLineString(type.children[0].type)) {
return false;
}
return true;
}
/** Checks whether the given Apache Arrow JS type is a Polygon data type */
function isGeoArrowMultiPolygon(type) {
// Check the outer vector is a List
if (!_type.DataType.isList(type)) {
return false;
}
// Check the child is a polygon vector
if (!isGeoArrowPolygon(type.children[0].type)) {
return false;
}
return true;
}
/**
* Checks if the given SQL query is a SELECT query by using the EXPLAIN command.
* @param connection The DuckDB connection instance.
* @param query The SQL query to check.
* @returns Resolves to `true` if the query is a SELECT statement, otherwise `false`.
*/
function checkIsSelectQuery(_x3, _x4) {
return _checkIsSelectQuery.apply(this, arguments);
}
/**
* Split a string with potentially multiple SQL queries (separated as usual by ';') into an array of queries.
* This implementation:
* - Handles single and double quoted strings with proper escaping
* - Ignores semicolons in line comments (--) and block comments (slash asterisk)
* - Trims whitespace from queries
* - Handles SQL-style escaped quotes ('' inside strings)
* - Returns only non-empty queries
* @param input A string with potentially multiple SQL queries.
* @returns An array of queries.
*/
function _checkIsSelectQuery() {
_checkIsSelectQuery = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(connection, query) {
var result;
return _regenerator["default"].wrap(function _callee3$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
_context3.prev = 0;
_context3.next = 3;
return connection.query("EXPLAIN (".concat(query, ")"));
case 3:
result = _context3.sent;
return _context3.abrupt("return", result.numRows > 0);
case 7:
_context3.prev = 7;
_context3.t0 = _context3["catch"](0);
return _context3.abrupt("return", false);
case 10:
case "end":
return _context3.stop();
}
}, _callee3, null, [[0, 7]]);
}));
return _checkIsSelectQuery.apply(this, arguments);
}
function splitSqlStatements(input) {
var queries = [];
var currentQuery = '';
var inSingleQuote = false;
var inDoubleQuote = false;
var inLineComment = false;
var inBlockComment = false;
for (var i = 0; i < input.length; i++) {
var _char = input[i];
if (inLineComment) {
currentQuery += _char;
if (_char === '\n') {
inLineComment = false;
}
continue;
}
if (inBlockComment) {
currentQuery += _char;
if (_char === '*' && input[i + 1] === '/') {
inBlockComment = false;
currentQuery += input[++i]; // Consume '/'
}
continue;
}
if (inSingleQuote) {
currentQuery += _char;
if (_char === "'") {
// Handle escaped single quotes in SQL
if (i + 1 < input.length && input[i + 1] === "'") {
currentQuery += input[++i];
} else {
inSingleQuote = false;
}
}
continue;
}
if (inDoubleQuote) {
currentQuery += _char;
if (_char === '"') {
// Handle escaped double quotes
if (i + 1 < input.length && input[i + 1] === '"') {
currentQuery += input[++i];
} else {
inDoubleQuote = false;
}
}
continue;
}
// Check for comment starts
if (_char === '-' && input[i + 1] === '-') {
inLineComment = true;
currentQuery += _char + input[++i];
continue;
}
if (_char === '/' && input[i + 1] === '*') {
inBlockComment = true;
currentQuery += _char + input[++i];
continue;
}
// Check for quote starts
if (_char === "'") {
inSingleQuote = true;
currentQuery += _char;
continue;
}
if (_char === '"') {
inDoubleQuote = true;
currentQuery += _char;
continue;
}
// Handle query separator
if (_char === ';') {
var _trimmed = currentQuery.trim();
if (_trimmed.length > 0) {
queries.push(_trimmed);
}
currentQuery = '';
continue;
}
currentQuery += _char;
}
// Add the final query
var trimmed = currentQuery.trim();
if (trimmed.length > 0) {
queries.push(trimmed);
}
return queries;
}
/**
* Removes SQL comments from a given SQL string.
* @param sql The SQL query string from which comments should be removed.
* @returns The cleaned SQL string without comments.
*/
function removeSQLComments(sql) {
// Remove multi-line comments (/* ... */)
sql = sql.replace(/\/\*[\s\S]*?\*\//g, '');
// Remove single-line comments (-- ...)
sql = sql.replace(/--.*$/gm, '');
return sql.trim();
}
/**
* Drops a table if it exists in the DuckDB database.
* @param connection The DuckDB connection instance.
* @param tableName The name of the table to drop.
* @returns A promise that resolves when the operation is complete.
* @throws Logs an error if the table drop operation fails.
*/
var dropTableIfExists = exports.dropTableIfExists = /*#__PURE__*/function () {
var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(connection, tableName) {
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
_context.next = 3;
return connection.query("DROP TABLE IF EXISTS \"".concat(tableName, "\";"));
case 3:
_context.next = 8;
break;
case 5:
_context.prev = 5;
_context.t0 = _context["catch"](0);
console.error('Dropping table failed', tableName, _context.t0);
case 8:
case "end":
return _context.stop();
}
}, _callee, null, [[0, 5]]);
}));
return function dropTableIfExists(_x5, _x6) {
return _ref.apply(this, arguments);
};
}();
/**
* Imports a file into DuckDB as a table, supporting multiple formats from SUPPORTED_DUCKDB_DROP_EXTENSIONS.
* @param file The file to be imported.
* @returns A promise that resolves when the file has been processed into a DuckDB table.
*/
function tableFromFile(_x7) {
return _tableFromFile.apply(this, arguments);
}
/**
* Sanitizes a file name to be a valid DuckDB table name.
* @param fileName The input file name to be sanitized.
* @returns A valid DuckDB table name.
*/
function _tableFromFile() {
_tableFromFile = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(file) {
var fileExt, db, c, error, tableName, sourceName, arrayBuffer, uint8Array, arrowTable, message;
return _regenerator["default"].wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
if (file) {
_context4.next = 2;
break;
}
return _context4.abrupt("return", new Error('File Drag & Drop: No file'));
case 2:
fileExt = SUPPORTED_DUCKDB_DROP_EXTENSIONS.find(function (ext) {
return file.name.endsWith(ext);
});
if (fileExt) {
_context4.next = 5;
break;
}
return _context4.abrupt("return", new Error("File Drag & Drop: File extension isn't supported"));
case 5:
_context4.next = 7;
return (0, _init.getDuckDB)();
case 7:
db = _context4.sent;
_context4.next = 10;
return db.connect();
case 10:
c = _context4.sent;
error = null;
_context4.prev = 12;
tableName = sanitizeDuckDBTableName(file.name);
sourceName = 'temp_file_handle';
c.query("install spatial;\n load spatial;");
if (!(fileExt === 'arrow')) {
_context4.next = 26;
break;
}
_context4.next = 19;
return file.arrayBuffer();
case 19:
arrayBuffer = _context4.sent;
uint8Array = new Uint8Array(arrayBuffer);
arrowTable = arrow.tableFromIPC(uint8Array);
_context4.next = 24;
return c.insertArrowTable(arrowTable, {
name: tableName
});
case 24:
_context4.next = 46;
break;
case 26:
_context4.next = 28;
return db.registerFileHandle(sourceName, file, _duckdbWasm.DuckDBDataProtocol.BROWSER_FILEREADER, true);
case 28:
if (!(fileExt === 'csv')) {
_context4.next = 33;
break;
}
_context4.next = 31;
return c.query("\n CREATE TABLE '".concat(tableName, "' AS\n SELECT *\n FROM read_csv('").concat(sourceName, "', header = true, auto_detect = true, sample_size = -1);\n "));
case 31:
_context4.next = 46;
break;
case 33:
if (!(fileExt === 'json')) {
_context4.next = 38;
break;
}
_context4.next = 36;
return c.query("\n CREATE TABLE '".concat(tableName, "' AS\n SELECT *\n FROM read_json_auto('").concat(sourceName, "');\n "));
case 36:
_context4.next = 46;
break;
case 38:
if (!(fileExt === 'geojson')) {
_context4.next = 43;
break;
}
_context4.next = 41;
return c.query("\n CREATE TABLE '".concat(tableName, "' AS\n SELECT *\n FROM ST_READ('").concat(sourceName, "', keep_wkb = TRUE);\n "));
case 41:
_context4.next = 46;
break;
case 43:
if (!(fileExt === 'parquet')) {
_context4.next = 46;
break;
}
_context4.next = 46;
return c.query("\n CREATE TABLE '".concat(tableName, "' AS\n SELECT *\n FROM read_parquet('").concat(sourceName, "')\n "));
case 46:
_context4.next = 52;
break;
case 48:
_context4.prev = 48;
_context4.t0 = _context4["catch"](12);
if (_context4.t0 instanceof Error) {
message = _context4.t0.message || ''; // output more readable errors for known issues
if (message.includes('Arrow Type with extension name: geoarrow')) {
error = new Error('The GeoArrow extensions are not implemented in the connected DuckDB version.');
} else if (message.includes("Geoparquet column 'geometry' does not have geometry types")) {
error = new Error("Invalid Input Error: Geoparquet column 'geometry' does not have geometry types.\nPossible reasons:\n - Old .parquet files that don't match the Parquet format specification.\n - Unsupported compression.");
}
}
if (!error) {
error = _context4.t0;
}
case 52:
_context4.next = 54;
return c.close();
case 54:
return _context4.abrupt("return", error);
case 55:
case "end":
return _context4.stop();
}
}, _callee4, null, [[12, 48]]);
}));
return _tableFromFile.apply(this, arguments);
}
function sanitizeDuckDBTableName(fileName) {
// Replace invalid characters with underscores
var name = fileName.replace(/[^a-zA-Z0-9_]/g, '_');
// Ensure it doesn't start with a digit
if (/^\d/.test(name)) {
name = "t_".concat(name);
}
return name || 'default_table';
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhcnJvdyIsIl9pbnRlcm9wUmVxdWlyZVdpbGRjYXJkIiwicmVxdWlyZSIsIl90eXBlIiwiX2R1Y2tkYldhc20iLCJfY29uc3RhbnRzIiwiX2luaXQiLCJfZ2V0UmVxdWlyZVdpbGRjYXJkQ2FjaGUiLCJlIiwiV2Vha01hcCIsInIiLCJ0IiwiX19lc01vZHVsZSIsIl90eXBlb2YiLCJoYXMiLCJnZXQiLCJuIiwiX19wcm90b19fIiwiYSIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwidSIsImhhc093blByb3BlcnR5IiwiY2FsbCIsImkiLCJzZXQiLCJTVVBQT1JURURfRFVDS0RCX0RST1BfRVhURU5TSU9OUyIsImV4cG9ydHMiLCJnZXREdWNrREJDb2x1bW5UeXBlcyIsIl94IiwiX3gyIiwiX2dldER1Y2tEQkNvbHVtblR5cGVzIiwiYXBwbHkiLCJhcmd1bWVudHMiLCJfYXN5bmNUb0dlbmVyYXRvcjIiLCJfcmVnZW5lcmF0b3IiLCJtYXJrIiwiX2NhbGxlZTIiLCJjb25uZWN0aW9uIiwidGFibGVOYW1lIiwicmVzRGVzY3JpYmUiLCJkdWNrRGJUeXBlcyIsIm51bVJvd3MiLCJfcmVzRGVzY3JpYmUkZ2V0Q2hpbGQiLCJfcmVzRGVzY3JpYmUkZ2V0Q2hpbGQyIiwiY29sdW1uTmFtZSIsImNvbHVtblR5cGUiLCJ3cmFwIiwiX2NhbGxlZTIkIiwiX2NvbnRleHQyIiwicHJldiIsIm5leHQiLCJxdWVyeSIsImNvbmNhdCIsInNlbnQiLCJnZXRDaGlsZEF0IiwicHVzaCIsIm5hbWUiLCJ0eXBlIiwiYWJydXB0Iiwic3RvcCIsImdldER1Y2tEQkNvbHVtblR5cGVzTWFwIiwiY29sdW1ucyIsInJlZHVjZSIsImFjYyIsInZhbHVlIiwiY29uc3RydWN0U1RfYXNXS0JRdWVyeSIsImNvbHVtbnNUb0NvbnZlcnRUb1dLQiIsImV4Y2x1ZGUiLCJsZW5ndGgiLCJqb2luIiwiYXNXS0IiLCJtYXAiLCJjb2x1bW4iLCJnZXRHZW9tZXRyeUNvbHVtbnMiLCJmaWx0ZXIiLCJzZXRHZW9BcnJvd1dLQkV4dGVuc2lvbiIsInRhYmxlIiwic2NoZW1hIiwiZmllbGRzIiwiZm9yRWFjaCIsImZpZWxkIiwiaW5mbyIsImZpbmQiLCJtZXRhZGF0YSIsIkdFT0FSUk9XX01FVEFEQVRBX0tFWSIsIkdFT0FSUk9XX0VYVEVOU0lPTlMiLCJXS0IiLCJyZXN0b3JlQXJyb3dUYWJsZSIsImFycm93U2NoZW1hIiwiY3JlYU9wdHMiLCJpbmRleCIsIlRhYmxlIiwicmVtb3ZlVW5zdXBwb3J0ZWRFeHRlbnNpb25zIiwicmVtb3ZlZE1ldGFkYXRhIiwiZXh0ZW5zaW9uIiwic3RhcnRzV2l0aCIsInJlc3RvcmVVbnN1cHBvcnRlZEV4dGVuc2lvbnMiLCJyZW1vdmVkRXh0ZW5zaW9ucyIsImlzR2VvQXJyb3dQb2ludCIsIkRhdGFUeXBlIiwiaXNGaXhlZFNpemVMaXN0IiwiaW5jbHVkZXMiLCJsaXN0U2l6ZSIsImlzRmxvYXQiLCJjaGlsZHJlbiIsImlzR2VvQXJyb3dMaW5lU3RyaW5nIiwiaXNMaXN0IiwiaXNHZW9BcnJvd1BvbHlnb24iLCJpc0dlb0Fycm93TXVsdGlQb2ludCIsImlzR2VvQXJyb3dNdWx0aUxpbmVTdHJpbmciLCJpc0dlb0Fycm93TXVsdGlQb2x5Z29uIiwiY2hlY2tJc1NlbGVjdFF1ZXJ5IiwiX3gzIiwiX3g0IiwiX2NoZWNrSXNTZWxlY3RRdWVyeSIsIl9jYWxsZWUzIiwicmVzdWx0IiwiX2NhbGxlZTMkIiwiX2NvbnRleHQzIiwidDAiLCJzcGxpdFNxbFN0YXRlbWVudHMiLCJpbnB1dCIsInF1ZXJpZXMiLCJjdXJyZW50UXVlcnkiLCJpblNpbmdsZVF1b3RlIiwiaW5Eb3VibGVRdW90ZSIsImluTGluZUNvbW1lbnQiLCJpbkJsb2NrQ29tbWVudCIsImNoYXIiLCJ0cmltbWVkIiwidHJpbSIsInJlbW92ZVNRTENvbW1lbnRzIiwic3FsIiwicmVwbGFjZSIsImRyb3BUYWJsZUlmRXhpc3RzIiwiX3JlZiIsIl9jYWxsZWUiLCJfY2FsbGVlJCIsIl9jb250ZXh0IiwiY29uc29sZSIsImVycm9yIiwiX3g1IiwiX3g2IiwidGFibGVGcm9tRmlsZSIsIl94NyIsIl90YWJsZUZyb21GaWxlIiwiX2NhbGxlZTQiLCJmaWxlIiwiZmlsZUV4dCIsImRiIiwiYyIsInNvdXJjZU5hbWUiLCJhcnJheUJ1ZmZlciIsInVpbnQ4QXJyYXkiLCJhcnJvd1RhYmxlIiwibWVzc2FnZSIsIl9jYWxsZWU0JCIsIl9jb250ZXh0NCIsIkVycm9yIiwiZXh0IiwiZW5kc1dpdGgiLCJnZXREdWNrREIiLCJjb25uZWN0Iiwic2FuaXRpemVEdWNrREJUYWJsZU5hbWUiLCJVaW50OEFycmF5IiwidGFibGVGcm9tSVBDIiwiaW5zZXJ0QXJyb3dUYWJsZSIsInJlZ2lzdGVyRmlsZUhhbmRsZSIsIkR1Y2tEQkRhdGFQcm90b2NvbCIsIkJST1dTRVJfRklMRVJFQURFUiIsImNsb3NlIiwiZmlsZU5hbWUiLCJ0ZXN0Il0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RhYmxlL2R1Y2tkYi10YWJsZS11dGlscy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG4vLyBsb2FkZXJzLmdsXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgKGMpIHZpcy5nbCBjb250cmlidXRvcnNcblxuLy8gQ29waWVkIGZyb20gbG9hZGVycy5nbC9nZW9hcnJvd1xuXG4vLyBUT0RPOiBSZW1vdmUgaXNHZW9BcnJvdyogb25jZSBLZXBsZXIuZ2wgaXMgdXBncmFkZWQgdG8gbG9hZGVycy5nbCA0LjQrXG5cbmltcG9ydCAqIGFzIGFycm93IGZyb20gJ2FwYWNoZS1hcnJvdyc7XG5pbXBvcnQge0RhdGFUeXBlfSBmcm9tICdhcGFjaGUtYXJyb3cvdHlwZSc7XG5pbXBvcnQge0FzeW5jRHVja0RCQ29ubmVjdGlvbiwgRHVja0RCRGF0YVByb3RvY29sfSBmcm9tICdAZHVja2RiL2R1Y2tkYi13YXNtJztcblxuaW1wb3J0IHtHRU9BUlJPV19FWFRFTlNJT05TLCBHRU9BUlJPV19NRVRBREFUQV9LRVl9IGZyb20gJ0BrZXBsZXIuZ2wvY29uc3RhbnRzJztcbmltcG9ydCB7UHJvdG9EYXRhc2V0RmllbGR9IGZyb20gJ0BrZXBsZXIuZ2wvdHlwZXMnO1xuXG5pbXBvcnQge2dldER1Y2tEQn0gZnJvbSAnLi4vaW5pdCc7XG5cbmV4cG9ydCBjb25zdCBTVVBQT1JURURfRFVDS0RCX0RST1BfRVhURU5TSU9OUyA9IFsnYXJyb3cnLCAnY3N2JywgJ2dlb2pzb24nLCAnanNvbicsICdwYXJxdWV0J107XG5cbmV4cG9ydCB0eXBlIER1Y2tEQkNvbHVtbkRlc2MgPSB7bmFtZTogc3RyaW5nOyB0eXBlOiBzdHJpbmd9O1xuXG4vKipcbiAqIFF1ZXJpZXMgYSBEdWNrREIgdGFibGUgZm9yIHRoZSBzY2hlbWEgZGVzY3JpcHRpb24uXG4gKiBAcGFyYW0gY29ubmVjdGlvbiBBbiBhY3RpdmUgRHVja0RCIGNvbm5lY3Rpb24uXG4gKiBAcGFyYW0gdGFibGVOYW1lIEEgbmFtZSBvZiBEdWNrREIgdGFibGUgdG8gcXVlcnkuXG4gKiBAcmV0dXJucyBBbiBhcnJheSBvZiBjb2x1bW4gbmFtZXMgYW5kIER1Y2tEQiB0eXBlcy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldER1Y2tEQkNvbHVtblR5cGVzKFxuICBjb25uZWN0aW9uOiBBc3luY0R1Y2tEQkNvbm5lY3Rpb24sXG4gIHRhYmxlTmFtZTogc3RyaW5nXG4pOiBQcm9taXNlPER1Y2tEQkNvbHVtbkRlc2NbXT4ge1xuICBjb25zdCByZXNEZXNjcmliZSA9IGF3YWl0IGNvbm5lY3Rpb24ucXVlcnkoYERFU0NSSUJFIFwiJHt0YWJsZU5hbWV9XCI7YCk7XG5cbiAgY29uc3QgZHVja0RiVHlwZXM6IER1Y2tEQkNvbHVtbkRlc2NbXSA9IFtdO1xuICBjb25zdCBudW1Sb3dzID0gcmVzRGVzY3JpYmUubnVtUm93cztcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBudW1Sb3dzOyArK2kpIHtcbiAgICBjb25zdCBjb2x1bW5OYW1lID0gcmVzRGVzY3JpYmUuZ2V0Q2hpbGRBdCgwKT8uZ2V0KGkpO1xuICAgIGNvbnN0IGNvbHVtblR5cGUgPSByZXNEZXNjcmliZS5nZXRDaGlsZEF0KDEpPy5nZXQoaSk7XG5cbiAgICBkdWNrRGJUeXBlcy5wdXNoKHtcbiAgICAgIG5hbWU6IGNvbHVtbk5hbWUsXG4gICAgICB0eXBlOiBjb2x1bW5UeXBlXG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gZHVja0RiVHlwZXM7XG59XG5cbi8qKlxuICogR2VuZXJhdGVzIGEgbWFwcGluZyBvZiBjb2x1bW4gbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZyBEdWNrREIgZGF0YSB0eXBlcy5cbiAqIEBwYXJhbSBjb2x1bW5zIEFuIGFycmF5IG9mIGNvbHVtbiBkZXNjcmlwdGlvbnMgZnJvbSBEdWNrREIuIENoZWNrIGdldER1Y2tEQkNvbHVtblR5cGVzLlxuICogQHJldHVybnMgQSByZWNvcmQgd2hlcmUga2V5cyBhcmUgY29sdW1uIG5hbWVzIGFuZCB2YWx1ZXMgYXJlIHRoZWlyIGRhdGEgdHlwZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXREdWNrREJDb2x1bW5UeXBlc01hcChjb2x1bW5zOiBEdWNrREJDb2x1bW5EZXNjW10pIHtcbiAgcmV0dXJuIGNvbHVtbnMucmVkdWNlKChhY2MsIHZhbHVlKSA9PiB7XG4gICAgYWNjW3ZhbHVlLm5hbWVdID0gdmFsdWUudHlwZTtcbiAgICByZXR1cm4gYWNjO1xuICB9LCB7fSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTtcbn1cblxuLyoqXG4gKiBDb25zdHJ1Y3RzIGFuIFNRTCBxdWVyeSB0byBzZWxlY3QgYWxsIGNvbHVtbnMgZnJvbSBhIGdpdmVuIHRhYmxlLFxuICogY29udmVydGluZyBzcGVjaWZpZWQgY29sdW1ucyB0byBXZWxsLUtub3duIEJpbmFyeSAoV0tCKSBmb3JtYXQgdXNpbmcgU1RfQXNXS0IuXG4gKiBAcGFyYW0gdGFibGVOYW1lIFRoZSBuYW1lIG9mIHRoZSB0YWJsZSBmcm9tIHdoaWNoIHRvIHNlbGVjdCBkYXRhLlxuICogQHBhcmFtIGNvbHVtbnNUb0NvbnZlcnRUb1dLQiBBbiBhcnJheSBvZiBjb2x1bW4gbmFtZXMgdGhhdCBzaG91bGQgYmUgY29udmVydGVkIHRvIFdLQiBmb3JtYXQuXG4gKiBAcmV0dXJucyBUaGUgY29uc3RydWN0ZWQgU1FMIHF1ZXJ5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29uc3RydWN0U1RfYXNXS0JRdWVyeSh0YWJsZU5hbWU6IHN0cmluZywgY29sdW1uc1RvQ29udmVydFRvV0tCOiBzdHJpbmdbXSk6IHN0cmluZyB7XG4gIGNvbnN0IGV4Y2x1ZGUgPVxuICAgIGNvbHVtbnNUb0NvbnZlcnRUb1dLQi5sZW5ndGggPiAwID8gYEVYQ0xVREUgJHtjb2x1bW5zVG9Db252ZXJ0VG9XS0Iuam9pbignLCAnKX1gIDogJyc7XG4gIGNvbnN0IGFzV0tCID1cbiAgICBjb2x1bW5zVG9Db252ZXJ0VG9XS0IubGVuZ3RoID4gMFxuICAgICAgPyBgLCAke2NvbHVtbnNUb0NvbnZlcnRUb1dLQi5tYXAoY29sdW1uID0+IGBTVF9Bc1dLQigke2NvbHVtbn0pIGFzICR7Y29sdW1ufWApLmpvaW4oJywgJyl9YFxuICAgICAgOiAnJztcbiAgcmV0dXJuIGBTRUxFQ1QgKiAke2V4Y2x1ZGV9ICR7YXNXS0J9IEZST00gJyR7dGFibGVOYW1lfSc7YDtcbn1cblxuLyoqXG4gKiBGaW5kcyB0aGUgbmFtZXMgb2YgY29sdW1ucyB0aGF0IGhhdmUgYSBHRU9NRVRSWSB0eXBlLlxuICogQHBhcmFtIGNvbHVtbnMgQW4gYXJyYXkgb2YgY29sdW1uIGRlc2NyaXB0b3JzIGZyb20gYSBEdWNrREIgdGFibGUuXG4gKiBAcmV0dXJucyBBbiBhcnJheSBvZiBjb2x1bW4gbmFtZXMgdGhhdCBhcmUgb2YgdHlwZSBHRU9NRVRSWS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEdlb21ldHJ5Q29sdW1ucyhjb2x1bW5zOiBEdWNrREJDb2x1bW5EZXNjW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBjb2x1bW5zLmZpbHRlcihjb2x1bW4gPT4gY29sdW1uLnR5cGUgPT09ICdHRU9NRVRSWScpLm1hcChjb2x1bW4gPT4gY29sdW1uLm5hbWUpO1xufVxuXG4vKipcbiAqIFNldHMgdGhlIEdlb0Fycm93IFdLQiBleHRlbnNpb24gbWV0YWRhdGEgZm9yIGNvbHVtbnMgb2YgdHlwZSBHRU9NRVRSWSBpbiBhbiBBcnJvdyB0YWJsZS5cbiAqIEBwYXJhbSB0YWJsZSBUaGUgQXBhY2hlIEFycm93IHRhYmxlIHdob3NlIHNjaGVtYSBmaWVsZHMgd2lsbCBiZSBtb2RpZmllZC5cbiAqIEBwYXJhbSBjb2x1bW5zIEFuIGFycmF5IG9mIGNvbHVtbiBkZXNjcmlwdG9ycyBmcm9tIGEgRHVja0RCIHRhYmxlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0R2VvQXJyb3dXS0JFeHRlbnNpb24odGFibGU6IGFycm93LlRhYmxlLCBjb2x1bW5zOiBEdWNrREJDb2x1bW5EZXNjW10pIHtcbiAgdGFibGUuc2NoZW1hLmZpZWxkcy5mb3JFYWNoKGZpZWxkID0+IHtcbiAgICBjb25zdCBpbmZvID0gY29sdW1ucy5maW5kKHQgPT4gdC5uYW1lID09PSBmaWVsZC5uYW1lKTtcbiAgICBpZiAoaW5mbz8udHlwZSA9PT0gJ0dFT01FVFJZJykge1xuICAgICAgZmllbGQubWV0YWRhdGEuc2V0KEdFT0FSUk9XX01FVEFEQVRBX0tFWSwgR0VPQVJST1dfRVhURU5TSU9OUy5XS0IpO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhbiBhcnJvdyB0YWJsZSBmcm9tIGFuIGFycmF5IG9mIGFycm93IHZlY3RvcnMgYW5kIGZpZWxkcy5cbiAqIEBwYXJhbSBjb2x1bW5zIEFuIGFycmF5IG9mIGFycm93IHZlY3RvcnMuXG4gKiBAcGFyYW0gZmllbGRzIEFuIGFycmF5IG9mIGZpZWxkcyBwZXIgYXJyb3cgdmVjdG9yLlxuICogQHBhcmFtIGFycm93U2NoZW1hIE9wdGlvbmFsIGFycm93IHRhYmxlIHNjaGVtYSB3aGVuIGF2YWlsYWJsZS5cbiAqIEByZXR1cm5zIEFuIGFycm93IHRhYmxlLlxuICovXG5leHBvcnQgY29uc3QgcmVzdG9yZUFycm93VGFibGUgPSAoXG4gIGNvbHVtbnM6IGFycm93LlZlY3RvcltdLFxuICBmaWVsZHM6IFByb3RvRGF0YXNldEZpZWxkW10sXG4gIGFycm93U2NoZW1hPzogYXJyb3cuU2NoZW1hXG4pID0+IHtcbiAgY29uc3QgY3JlYU9wdHMgPSB7fTtcbiAgZmllbGRzLm1hcCgoZmllbGQsIGluZGV4KSA9PiB7XG4gICAgY3JlYU9wdHNbZmllbGQubmFtZV0gPSBjb2x1bW5zW2luZGV4XTtcbiAgfSk7XG5cbiAgcmV0dXJuIGFycm93U2NoZW1hID8gbmV3IGFycm93LlRhYmxlKGFycm93U2NoZW1hLCBjcmVhT3B0cykgOiBuZXcgYXJyb3cuVGFibGUoY3JlYU9wdHMpO1xufTtcblxuLyoqXG4gKiBEdWNrRGIgdGhyb3dzIHdoZW4gZ2VvYXJyb3cgZXh0ZW5zaW9ucyBhcmUgcHJlc2VudCBpbiBtZXRhZGF0YS5cbiAqIEBwYXJhbSB0YWJsZSBBbiBhcnJvdyB0YWJsZSB0byBjbGVhciBmcm9tIGV4dGVuc2lvbnMuXG4gKiBAcmV0dXJucyBBIG1hcCBvZiByZW1vdmVkIHBlciBmaWVsZCBnZW9hcnJvdyBleHRlbnNpb25zLlxuICovXG5leHBvcnQgY29uc3QgcmVtb3ZlVW5zdXBwb3J0ZWRFeHRlbnNpb25zID0gKHRhYmxlOiBhcnJvdy5UYWJsZSk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPT4ge1xuICBjb25zdCByZW1vdmVkTWV0YWRhdGE6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcbiAgdGFibGUuc2NoZW1hLmZpZWxkcy5mb3JFYWNoKGZpZWxkID0+IHtcbiAgICBjb25zdCBleHRlbnNpb24gPSBmaWVsZC5tZXRhZGF0YS5nZXQoR0VPQVJST1dfTUVUQURBVEFfS0VZKTtcbiAgICBpZiAoZXh0ZW5zaW9uPy5zdGFydHNXaXRoKCdnZW9hcnJvdycpKSB7XG4gICAgICByZW1vdmVkTWV0YWRhdGFbZmllbGQubmFtZV0gPSBleHRlbnNpb247XG4gICAgICBmaWVsZC5tZXRhZGF0YS5kZWxldGUoR0VPQVJST1dfTUVUQURBVEFfS0VZKTtcbiAgICB9XG4gIH0pO1xuICByZXR1cm4gcmVtb3ZlZE1ldGFkYXRhO1xufTtcblxuLyoqXG4gKiBSZXN0b3JlIHJlbW92ZWQgbWV0YWRhdGEgZXh0ZW5zaW9ucyBhZnRlciBhIGNhbGwgdG8gcmVtb3ZlVW5zdXBwb3J0ZWRFeHRlbnNpb25zLlxuICogQHBhcmFtIHRhYmxlIEFuIGFycm93IHRhYmxlIHRvIHJlc3RvcmUgZ2VvYXJyb3cgZXh0ZW5zaW9ucy5cbiAqIEBwYXJhbSByZW1vdmVkRXh0ZW5zaW9ucyBBIG1hcCBvZiBwZXIgZmllbGQgZ2VvYXJyb3cgZXh0ZW5zaW9ucyB0byByZXN0b3JlLlxuICovXG5leHBvcnQgY29uc3QgcmVzdG9yZVVuc3VwcG9ydGVkRXh0ZW5zaW9ucyA9IChcbiAgdGFibGU6IGFycm93LlRhYmxlLFxuICByZW1vdmVkRXh0ZW5zaW9uczogUmVjb3JkPHN0cmluZywgc3RyaW5nPlxuKSA9PiB7XG4gIHRhYmxlLnNjaGVtYS5maWVsZHMuZm9yRWFjaChmaWVsZCA9PiB7XG4gICAgY29uc3QgZXh0ZW5zaW9uID0gcmVtb3ZlZEV4dGVuc2lvbnNbZmllbGQubmFtZV07XG4gICAgaWYgKGV4dGVuc2lvbikge1xuICAgICAgZmllbGQubWV0YWRhdGEuc2V0KEdFT0FSUk9XX01FVEFEQVRBX0tFWSwgZXh0ZW5zaW9uKTtcbiAgICB9XG4gIH0pO1xufTtcblxuLyoqIENoZWNrcyB3aGV0aGVyIHRoZSBnaXZlbiBBcGFjaGUgQXJyb3cgSlMgdHlwZSBpcyBhIFBvaW50IGRhdGEgdHlwZSAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzR2VvQXJyb3dQb2ludCh0eXBlOiBEYXRhVHlwZSkge1xuICBpZiAoRGF0YVR5cGUuaXNGaXhlZFNpemVMaXN0KHR5cGUpKSB7XG4gICAgLy8gQ2hlY2sgbGlzdCBzaXplXG4gICAgaWYgKCFbMiwgMywgNF0uaW5jbHVkZXModHlwZS5saXN0U2l6ZSkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBjaGlsZCBvZiBGaXhlZFNpemVMaXN0IGlzIGZsb2F0aW5nIHR5cGVcbiAgICBpZiAoIURhdGFUeXBlLmlzRmxvYXQodHlwZS5jaGlsZHJlblswXSkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqIENoZWNrcyB3aGV0aGVyIHRoZSBnaXZlbiBBcGFjaGUgQXJyb3cgSlMgdHlwZSBpcyBhIFBvaW50IGRhdGEgdHlwZSAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzR2VvQXJyb3dMaW5lU3RyaW5nKHR5cGU6IERhdGFUeXBlKSB7XG4gIC8vIENoZWNrIHRoZSBvdXRlciB0eXBlIGlzIGEgTGlzdFxuICBpZiAoIURhdGFUeXBlLmlzTGlzdCh0eXBlKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8vIENoZWNrIHRoZSBjaGlsZCBpcyBhIHBvaW50IHR5cGVcbiAgaWYgKCFpc0dlb0Fycm93UG9pbnQodHlwZS5jaGlsZHJlblswXS50eXBlKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufVxuXG4vKiogQ2hlY2tzIHdoZXRoZXIgdGhlIGdpdmVuIEFwYWNoZSBBcnJvdyBKUyB0eXBlIGlzIGEgUG9seWdvbiBkYXRhIHR5cGUgKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0dlb0Fycm93UG9seWdvbih0eXBlOiBEYXRhVHlwZSkge1xuICAvLyBDaGVjayB0aGUgb3V0ZXIgdmVjdG9yIGlzIGEgTGlzdFxuICBpZiAoIURhdGFUeXBlLmlzTGlzdCh0eXBlKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8vIENoZWNrIHRoZSBjaGlsZCBpcyBhIGxpbmVzdHJpbmcgdmVjdG9yXG4gIGlmICghaXNHZW9BcnJvd0xpbmVTdHJpbmcodHlwZS5jaGlsZHJlblswXS50eXBlKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufVxuXG4vKiogQ2hlY2tzIHdoZXRoZXIgdGhlIGdpdmVuIEFwYWNoZSBBcnJvdyBKUyB0eXBlIGlzIGEgUG9seWdvbiBkYXRhIHR5cGUgKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0dlb0Fycm93TXVsdGlQb2ludCh0eXBlOiBEYXRhVHlwZSkge1xuICAvLyBDaGVjayB0aGUgb3V0ZXIgdmVjdG9yIGlzIGEgTGlzdFxuICBpZiAoIURhdGFUeXBlLmlzTGlzdCh0eXBlKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8vIENoZWNrIHRoZSBjaGlsZCBpcyBhIHBvaW50IHZlY3RvclxuICBpZiAoIWlzR2VvQXJyb3dQb2ludCh0eXBlLmNoaWxkcmVuWzBdLnR5cGUpKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKiBDaGVja3Mgd2hldGhlciB0aGUgZ2l2ZW4gQXBhY2hlIEFycm93IEpTIHR5cGUgaXMgYSBQb2x5Z29uIGRhdGEgdHlwZSAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzR2VvQXJyb3dNdWx0aUxpbmVTdHJpbmcodHlwZTogRGF0YVR5cGUpIHtcbiAgLy8gQ2hlY2sgdGhlIG91dGVyIHZlY3RvciBpcyBhIExpc3RcbiAgaWYgKCFEYXRhVHlwZS5pc0xpc3QodHlwZSkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvLyBDaGVjayB0aGUgY2hpbGQgaXMgYSBsaW5lc3RyaW5nIHZlY3RvclxuICBpZiAoIWlzR2VvQXJyb3dMaW5lU3RyaW5nKHR5cGUuY2hpbGRyZW5bMF0udHlwZSkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn1cblxuLyoqIENoZWNrcyB3aGV0aGVyIHRoZSBnaXZlbiBBcGFjaGUgQXJyb3cgSlMgdHlwZSBpcyBhIFBvbHlnb24gZGF0YSB0eXBlICovXG5leHBvcnQgZnVuY3Rpb24gaXNHZW9BcnJvd011bHRpUG9seWdvbih0eXBlOiBEYXRhVHlwZSkge1xuICAvLyBDaGVjayB0aGUgb3V0ZXIgdmVjdG9yIGlzIGEgTGlzdFxuICBpZiAoIURhdGFUeXBlLmlzTGlzdCh0eXBlKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8vIENoZWNrIHRoZSBjaGlsZCBpcyBhIHBvbHlnb24gdmVjdG9yXG4gIGlmICghaXNHZW9BcnJvd1BvbHlnb24odHlwZS5jaGlsZHJlblswXS50eXBlKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gU1FMIHF1ZXJ5IGlzIGEgU0VMRUNUIHF1ZXJ5IGJ5IHVzaW5nIHRoZSBFWFBMQUlOIGNvbW1hbmQuXG4gKiBAcGFyYW0gY29ubmVjdGlvbiBUaGUgRHVja0RCIGNvbm5lY3Rpb24gaW5zdGFuY2UuXG4gKiBAcGFyYW0gcXVlcnkgVGhlIFNRTCBxdWVyeSB0byBjaGVjay5cbiAqIEByZXR1cm5zIFJlc29sdmVzIHRvIGB0cnVlYCBpZiB0aGUgcXVlcnkgaXMgYSBTRUxFQ1Qgc3RhdGVtZW50LCBvdGhlcndpc2UgYGZhbHNlYC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrSXNTZWxlY3RRdWVyeShcbiAgY29ubmVjdGlvbjogQXN5bmNEdWNrREJDb25uZWN0aW9uLFxuICBxdWVyeTogc3RyaW5nXG4pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBjb25uZWN0aW9uLnF1ZXJ5KGBFWFBMQUlOICgke3F1ZXJ5fSlgKTtcbiAgICByZXR1cm4gcmVzdWx0Lm51bVJvd3MgPiAwO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuXG4vKipcbiAqIFNwbGl0IGEgc3RyaW5nIHdpdGggcG90ZW50aWFsbHkgbXVsdGlwbGUgU1FMIHF1ZXJpZXMgKHNlcGFyYXRlZCBhcyB1c3VhbCBieSAnOycpIGludG8gYW4gYXJyYXkgb2YgcXVlcmllcy5cbiAqIFRoaXMgaW1wbGVtZW50YXRpb246XG4gKiAgLSBIYW5kbGVzIHNpbmdsZSBhbmQgZG91YmxlIHF1b3RlZCBzdHJpbmdzIHdpdGggcHJvcGVyIGVzY2FwaW5nXG4gKiAgLSBJZ25vcmVzIHNlbWljb2xvbnMgaW4gbGluZSBjb21tZW50cyAoLS0pIGFuZCBibG9jayBjb21tZW50cyAoc2xhc2ggYXN0ZXJpc2spXG4gKiAgLSBUcmltcyB3aGl0ZXNwYWNlIGZyb20gcXVlcmllc1xuICogIC0gSGFuZGxlcyBTUUwtc3R5bGUgZXNjYXBlZCBxdW90ZXMgKCcnIGluc2lkZSBzdHJpbmdzKVxuICogIC0gUmV0dXJucyBvbmx5IG5vbi1lbXB0eSBxdWVyaWVzXG4gKiBAcGFyYW0gaW5wdXQgQSBzdHJpbmcgd2l0aCBwb3RlbnRpYWxseSBtdWx0aXBsZSBTUUwgcXVlcmllcy5cbiAqIEByZXR1cm5zIEFuIGFycmF5IG9mIHF1ZXJpZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzcGxpdFNxbFN0YXRlbWVudHMoaW5wdXQ6IHN0cmluZyk6IHN0cmluZ1tdIHtcbiAgY29uc3QgcXVlcmllczogc3RyaW5nW10gPSBbXTtcbiAgbGV0IGN1cnJlbnRRdWVyeSA9ICcnO1xuICBsZXQgaW5TaW5nbGVRdW90ZSA9IGZhbHNlO1xuICBsZXQgaW5Eb3VibGVRdW90ZSA9IGZhbHNlO1xuICBsZXQgaW5MaW5lQ29tbWVudCA9IGZhbHNlO1xuICBsZXQgaW5CbG9ja0NvbW1lbnQgPSBmYWxzZTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IGlucHV0Lmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgY2hhciA9IGlucHV0W2ldO1xuXG4gICAgaWYgKGluTGluZUNvbW1lbnQpIHtcbiAgICAgIGN1cnJlbnRRdWVyeSArPSBjaGFyO1xuICAgICAgaWYgKGNoYXIgPT09ICdcXG4nKSB7XG4gICAgICAgIGluTGluZUNvbW1lbnQgPSBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGlmIChpbkJsb2NrQ29tbWVudCkge1xuICAgICAgY3VycmVudFF1ZXJ5ICs9IGNoYXI7XG4gICAgICBpZiAoY2hhciA9PT0gJyonICYmIGlucHV0W2kgKyAxXSA9PT0gJy8nKSB7XG4gICAgICAgIGluQmxvY2tDb21tZW50ID0gZmFsc2U7XG4gICAgICAgIGN1cnJlbnRRdWVyeSArPSBpbnB1dFsrK2ldOyAvLyBDb25zdW1lICcvJ1xuICAgICAgfVxuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgaWYgKGluU2luZ2xlUXVvdGUpIHtcbiAgICAgIGN1cnJlbnRRdWVyeSArPSBjaGFyO1xuICAgICAgaWYgKGNoYXIgPT09IFwiJ1wiKSB7XG4gICAgICAgIC8vIEhhbmRsZSBlc2NhcGVkIHNpbmdsZSBxdW90ZXMgaW4gU1FMXG4gICAgICAgIGlmIChpICsgMSA8IGlucHV0Lmxlbmd0aCAmJiBpbnB1dFtpICsgMV0gPT09IFwiJ1wiKSB7XG4gICAgICAgICAgY3VycmVudFF1ZXJ5ICs9IGlucHV0WysraV07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaW5TaW5nbGVRdW90ZSA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoaW5Eb3VibGVRdW90ZSkge1xuICAgICAgY3VycmVudFF1ZXJ5ICs9IGNoYXI7XG4gICAgICBpZiAoY2hhciA9PT0gJ1wiJykge1xuICAgICAgICAvLyBIYW5kbGUgZXNjYXBlZCBkb3VibGUgcXVvdGVzXG4gICAgICAgIGlmIChpICsgMSA8IGlucHV0Lmxlbmd0aCAmJiBpbnB1dFtpICsgMV0gPT09ICdcIicpIHtcbiAgICAgICAgICBjdXJyZW50UXVlcnkgKz0gaW5wdXRbKytpXTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpbkRvdWJsZVF1b3RlID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIENoZWNrIGZvciBjb21tZW50IHN0YXJ0c1xuICAgIGlmIChjaGFyID09PSAnLScgJiYgaW5wdXRbaSArIDFdID09PSAnLScpIHtcbiAgICAgIGluTGluZUNvbW1lbnQgPSB0cnVlO1xuICAgICAgY3VycmVudFF1ZXJ5ICs9IGNoYXIgKyBpbnB1dFsrK2ldO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgaWYgKGNoYXIgPT09ICcvJyAmJiBpbnB1dFtpICsgMV0gPT09ICcqJykge1xuICAgICAgaW5CbG9ja0NvbW1lbnQgPSB0cnVlO1xuICAgICAgY3VycmVudFF1ZXJ5ICs9IGNoYXIgKyBpbnB1dFsrK2ldO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIHF1b3RlIHN0YXJ0c1xuICAgIGlmIChjaGFyID09PSBcIidcIikge1xuICAgICAgaW5TaW5nbGVRdW90ZSA9IHRydWU7XG4gICAgICBjdXJyZW50UXVlcnkgKz0gY2hhcjtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGlmIChjaGFyID09PSAnXCInKSB7XG4gICAgICBpbkRvdWJsZVF1b3RlID0gdHJ1ZTtcbiAgICAgIGN1cnJlbnRRdWVyeSArPSBjaGFyO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIHF1ZXJ5IHNlcGFyYXRvclxuICAgIGlmIChjaGFyID09PSAnOycpIHtcbiAgICAgIGNvbnN0IHRyaW1tZWQgPSBjdXJyZW50UXVlcnkudHJpbSgpO1xuICAgICAgaWYgKHRyaW1tZWQubGVuZ3RoID4gMCkge1xuICAgICAgICBxdWVyaWVzLnB1c2godHJpbW1lZCk7XG4gICAgICB9XG4gICAgICBjdXJyZW50UXVlcnkgPSAnJztcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGN1cnJlbnRRdWVyeSArPSBjaGFyO1xuICB9XG5cbiAgLy8gQWRkIHRoZSBmaW5hbCBxdWVyeVxuICBjb25zdCB0cmltbWVkID0gY3VycmVudFF1ZXJ5LnRyaW0oKTtcbiAgaWYgKHRyaW1tZWQubGVuZ3RoID4gMCkge1xuICAgIHF1ZXJpZXMucHVzaCh0cmltbWVkKTtcbiAgfVxuXG4gIHJldHVybiBxdWVyaWVzO1xufVxuXG4vKipcbiAqIFJlbW92ZXMgU1FMIGNvbW1lbnRzIGZyb20gYSBnaXZlbiBTUUwgc3RyaW5nLlxuICogQHBhcmFtIHNxbCBUaGUgU1FMIHF1ZXJ5IHN0cmluZyBmcm9tIHdoaWNoIGNvbW1lbnRzIHNob3VsZCBiZSByZW1vdmVkLlxuICogQHJldHVybnMgVGhlIGNsZWFuZWQgU1FMIHN0cmluZyB3aXRob3V0IGNvbW1lbnRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVtb3ZlU1FMQ29tbWVudHMoc3FsOiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBSZW1vdmUgbXVsdGktbGluZSBjb21tZW50cyAoLyogLi4uICovKVxuICBzcWwgPSBzcWwucmVwbGFjZSgvXFwvXFwqW1xcc1xcU10qP1xcKlxcLy9nLCAnJyk7XG4gIC8vIFJlbW92ZSBzaW5nbGUtbGluZSBjb21tZW50cyAoLS0gLi4uKVxuICBzcWwgPSBzcWwucmVwbGFjZSgvLS0uKiQvZ20sICcnKTtcbiAgcmV0dXJuIHNxbC50cmltKCk7XG59XG5cbi8qKlxuICogRHJvcHMgYSB0YWJsZSBpZiBpdCBleGlzdHMgaW4gdGhlIER1Y2tEQiBkYXRhYmFzZS5cbiAqIEBwYXJhbSBjb25uZWN0aW9uIFRoZSBEdWNrREIgY29ubmVjdGlvbiBpbnN0YW5jZS5cbiAqIEBwYXJhbSB0YWJsZU5hbWUgVGhlIG5hbWUgb2YgdGhlIHRhYmxlIHRvIGRyb3AuXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBvcGVyYXRpb24gaXMgY29tcGxldGUuXG4gKiBAdGhyb3dzIExvZ3MgYW4gZXJyb3IgaWYgdGhlIHRhYmxlIGRyb3Agb3BlcmF0aW9uIGZhaWxzLlxuICovXG5leHBvcnQgY29uc3QgZHJvcFRhYmxlSWZFeGlzdHMgPSBhc3luYyAoY29ubmVjdGlvbjogQXN5bmNEdWNrREJDb25uZWN0aW9uLCB0YWJsZU5hbWU6IHN0cmluZykgPT4ge1xuICB0cnkge1xuICAgIGF3YWl0IGNvbm5lY3Rpb24ucXVlcnkoYERST1AgVEFCTEUgSUYgRVhJU1RTIFwiJHt0YWJsZU5hbWV9XCI7YCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc29sZS5lcnJvcignRHJvcHBpbmcgdGFibGUgZmFpbGVkJywgdGFibGVOYW1lLCBlcnJvcik7XG4gIH1cbn07XG5cbi8qKlxuICogSW1wb3J0cyBhIGZpbGUgaW50byBEdWNrREIgYXMgYSB0YWJsZSwgc3VwcG9ydGluZyBtdWx0aXBsZSBmb3JtYXRzIGZyb20gU1VQUE9SVEVEX0RVQ0tEQl9EUk9QX0VYVEVOU0lPTlMuXG4gKiBAcGFyYW0gZmlsZSBUaGUgZmlsZSB0byBiZSBpbXBvcnRlZC5cbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIGZpbGUgaGFzIGJlZW4gcHJvY2Vzc2VkIGludG8gYSBEdWNrREIgdGFibGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB0YWJsZUZyb21GaWxlKGZpbGU6IEZpbGUgfCBudWxsKTogUHJvbWlzZTxudWxsIHwgRXJyb3I+IHtcbiAgaWYgKCFmaWxlKSByZXR1cm4gbmV3IEVycm9yKCdGaWxlIERyYWcgJiBEcm9wOiBObyBmaWxlJyk7XG5cbiAgY29uc3QgZmlsZUV4dCA9IFNVUFBPUlRFRF9EVUNLREJfRFJPUF9FWFRFTlNJT05TLmZpbmQoZXh0ID0+IGZpbGUubmFtZS5lbmRzV2l0aChleHQpKTtcbiAgaWYgKCFmaWxlRXh0KSB7XG4gICAgcmV0dXJuIG5ldyBFcnJvcihcIkZpbGUgRHJhZyAmIERyb3A6IEZpbGUgZXh0ZW5zaW9uIGlzbid0IHN1cHBvcnRlZFwiKTtcbiAgfVxuXG4gIGNvbnN0IGRiID0gYXdhaXQgZ2V0RHVja0RCKCk7XG4gIGNvbnN0IGMgPSBhd2FpdCBkYi5jb25uZWN0KCk7XG5cbiAgbGV0IGVycm9yOiBFcnJvciB8IG51bGwgPSBudWxsO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgdGFibGVOYW1lID0gc2FuaXRpemVEdWNrREJUYWJsZU5hbWUoZmlsZS5uYW1lKTtcbiAgICBjb25zdCBzb3VyY2VOYW1lID0gJ3RlbXBfZmlsZV9oYW5kbGUnO1xuXG4gICAgYy5xdWVyeShgaW5zdGFsbCBzcGF0aWFsO1xuICAgICAgbG9hZCBzcGF0aWFsO2ApO1xuXG4gICAgaWYgKGZpbGVFeHQgPT09ICdhcnJvdycpIHtcbiAgICAgIGNvbnN0IGFycmF5QnVmZmVyID0gYXdhaXQgZmlsZS5hcnJheUJ1ZmZlcigpO1xuICAgICAgY29uc3QgdWludDhBcnJheSA9IG5ldyBVaW50OEFycmF5KGFycmF5QnVmZmVyKTtcbiAgICAgIGNvbnN0IGFycm93VGFibGUgPSBhcnJvdy50YWJsZUZyb21JUEModWludDhBcnJheSk7XG5cbiAgICAgIGF3YWl0IGMuaW5zZXJ0QXJyb3dUYWJsZShhcnJvd1RhYmxlLCB7bmFtZTogdGFibGVOYW1lfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IGRiLnJlZ2lzdGVyRmlsZUhhbmRsZShzb3VyY2VOYW1lLCBmaWxlLCBEdWNrREJEYXRhUHJvdG9jb2wuQlJPV1NFUl9GSUxFUkVBREVSLCB0cnVlKTtcblxuICAgICAgaWYgKGZpbGVFeHQgPT09ICdjc3YnKSB7XG4gICAgICAgIGF3YWl0IGMucXVlcnkoYFxuICAgICAgICAgICAgQ1JFQVRFIFRBQkxFICcke3RhYmxlTmFtZX0nIEFTXG4gICAgICAgICAgICBTRUxFQ1QgKlxuICAgICAgICAgICAgRlJPTSByZWFkX2NzdignJHtzb3VyY2VOYW1lfScsIGhlYWRlciA9IHRydWUsIGF1dG9fZGV0ZWN0ID0gdHJ1ZSwgc2FtcGxlX3NpemUgPSAtMSk7XG4gICAgICAgICAgYCk7XG4gICAgICB9IGVsc2UgaWYgKGZpbGVFeHQgPT09ICdqc29uJykge1xuICAgICAgICBhd2FpdCBjLnF1ZXJ5KGBcbiAgICAgICAgICAgIENSRUFURSBUQUJMRSAnJHt0YWJsZU5hbWV9JyBBU1xuICAgICAgICAgICAgU0VMRUNUICpcbiAgICAgICAgICAgIEZST00gcmVhZF9qc29uX2F1dG8oJyR7c291cmNlTmFtZX0nKTtcbiAgICAgICAgICBgKTtcbiAgICAgIH0gZWxzZSBpZiAoZmlsZUV4dCA9PT0gJ2dlb2pzb24nKSB7XG4gICAgICAgIGF3YWl0IGMucXVlcnkoYFxuICAgICAgICAgICAgQ1JFQVRFIFRBQkxFICcke3RhYmxlTmFtZX0nIEFTXG4gICAgICAgICAgICBTRUxFQ1QgKlxuICAgICAgICAgICAgRlJPTSBTVF9SRUFEKCcke3NvdXJjZU5hbWV9Jywga2VlcF93a2IgPSBUUlVFKTtcbiAgICAgICAgICBgKTtcbiAgICAgIH0gZWxzZSBpZiAoZmlsZUV4dCA9PT0gJ3BhcnF1ZXQnKSB7XG4gICAgICAgIGF3YWl0IGMucXVlcnkoYFxuICAgICAgICAgICAgQ1JFQVRFIFRBQkxFICcke3RhYmxlTmFtZX0nIEFTXG4gICAgICAgICAgICBTRUxFQ1QgKlxuICAgICAgICAgICAgRlJPTSByZWFkX3BhcnF1ZXQoJyR7c291cmNlTmFtZX0nKVxuICAgICAgICAgIGApO1xuICAgICAgfVxuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3JEYXRhKSB7XG4gICAgaWYgKGVycm9yRGF0YSBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICBjb25zdCBtZXNzYWdlID0gZXJyb3JEYXRhLm1lc3NhZ2UgfHwgJyc7XG4gICAgICAvLyBvdXRwdXQgbW9yZSByZWFkYWJsZSBlcnJvcnMgZm9yIGtub3duIGlzc3Vlc1xuICAgICAgaWYgKG1lc3NhZ2UuaW5jbHVkZXMoJ0Fycm93IFR5cGUgd2l0aCBleHRlbnNpb24gbmFtZTogZ2VvYXJyb3cnKSkge1xuICAgICAgICBlcnJvciA9IG5ldyBFcnJvcihcbiAgICAgICAgICAnVGhlIEdlb0Fycm93IGV4dGVuc2lvbnMgYXJlIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29ubmVjdGVkIER1Y2tEQiB2ZXJzaW9uLidcbiAgICAgICAgKTtcbiAgICAgIH0gZWxzZSBpZiAobWVzc2FnZS5pbmNsdWRlcyhcIkdlb3BhcnF1ZXQgY29sdW1uICdnZW9tZXRyeScgZG9lcyBub3QgaGF2ZSBnZW9tZXRyeSB0eXBlc1wiKSkge1xuICAgICAgICBlcnJvciA9IG5ldyBFcnJvcihcbiAgICAgICAgICBgSW52YWxpZCBJbnB1dCBFcnJvcjogR2VvcGFycXVldCBjb2x1bW4gJ2dlb21ldHJ5JyBkb2VzIG5vdCBoYXZlIGdlb21ldHJ5IHR5cGVzLlxuUG9zc2libGUgcmVhc29uczpcbiAgLSBPbGQgLnBhcnF1ZXQgZmlsZXMgdGhhdCBkb24ndCBtYXRjaCB0aGUgUGFycXVldCBmb3JtYXQgc3BlY2lmaWNhdGlvbi5cbiAgLSBVbnN1cHBvcnRlZCBjb21wcmVzc2lvbi5gXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFlcnJvcikge1xuICAgICAgZXJyb3IgPSBlcnJvckRhdGEgYXMgRXJyb3I7XG4gICAgfVxuICB9XG5cbiAgYXdhaXQgYy5jbG9zZSgpO1xuXG4gIHJldHVybiBlcnJvcjtcbn1cblxuLyoqXG4gKiBTYW5pdGl6ZXMgYSBmaWxlIG5hbWUgdG8gYmUgYSB2YWxpZCBEdWNrREIgdGFibGUgbmFtZS5cbiAqIEBwYXJhbSBmaWxlTmFtZSBUaGUgaW5wdXQgZmlsZSBuYW1lIHRvIGJlIHNhbml0aXplZC5cbiAqIEByZXR1cm5zIEEgdmFsaWQgRHVja0RCIHRhYmxlIG5hbWUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzYW5pdGl6ZUR1Y2tEQlRhYmxlTmFtZShmaWxlTmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gUmVwbGFjZSBpbnZhbGlkIGNoYXJhY3RlcnMgd2l0aCB1bmRlcnNjb3Jlc1xuICBsZXQgbmFtZSA9IGZpbGVOYW1lLnJlcGxhY2UoL1teYS16QS1aMC05X10vZywgJ18nKTtcbiAgLy8gRW5zdXJlIGl0IGRvZXNuJ3Qgc3RhcnQgd2l0aCBhIGRpZ2l0XG4gIGlmICgvXlxcZC8udGVzdChuYW1lKSkge1xuICAgIG5hbWUgPSBgdF8ke25hbWV9YDtcbiAgfVxuICByZXR1cm4gbmFtZSB8fCAnZGVmYXVsdF90YWJsZSc7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFXQSxJQUFBQSxLQUFBLEdBQUFDLHVCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBQyxLQUFBLEdBQUFELE9BQUE7QUFDQSxJQUFBRSxXQUFBLEdBQUFGLE9BQUE7QUFFQSxJQUFBRyxVQUFBLEdBQUFILE9BQUE7QUFHQSxJQUFBSSxLQUFBLEdBQUFKLE9BQUE7QUFBa0MsU0FBQUsseUJBQUFDLENBQUEsNkJBQUFDLE9BQUEsbUJBQUFDLENBQUEsT0FBQUQsT0FBQSxJQUFBRSxDQUFBLE9BQUFGLE9BQUEsWUFBQUYsd0JBQUEsWUFBQUEseUJBQUFDLENBQUEsV0FBQUEsQ0FBQSxHQUFBRyxDQUFBLEdBQUFELENBQUEsS0FBQUYsQ0FBQTtBQUFBLFNBQUFQLHdCQUFBTyxDQUFBLEVBQUFFLENBQUEsU0FBQUEsQ0FBQSxJQUFBRixDQUFBLElBQUFBLENBQUEsQ0FBQUksVUFBQSxTQUFBSixDQUFBLGVBQUFBLENBQUEsZ0JBQUFLLE9BQUEsQ0FBQUwsQ0FBQSwwQkFBQUEsQ0FBQSxzQkFBQUEsQ0FBQSxRQUFBRyxDQUFBLEdBQUFKLHdCQUFBLENBQUFHLENBQUEsT0FBQUMsQ0FBQSxJQUFBQSxDQUFBLENBQUFHLEdBQUEsQ0FBQU4sQ0FBQSxVQUFBRyxDQUFBLENBQUFJLEdBQUEsQ0FBQVAsQ0FBQSxPQUFBUSxDQUFBLEtBQUFDLFNBQUEsVUFBQUMsQ0FBQSxHQUFBQyxNQUFBLENBQUFDLGNBQUEsSUFBQUQsTUFBQSxDQUFBRSx3QkFBQSxXQUFBQyxDQUFBLElBQUFkLENBQUEsb0JBQUFjLENBQUEsT0FBQUMsY0FB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