@meinestadt.de/glue-schema-registry
Version:
This is a SerDe library to interact with the AWS Glue Schema Registry. It makes it easy to encode and decode messages with Avro schemas and the AWS' wire format.
318 lines • 38.9 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.GlueSchemaRegistry = exports.ERROR = exports.SchemaCompatibilityType = exports.SchemaType = void 0;
const crypto = __importStar(require("crypto"));
const uuid = __importStar(require("uuid"));
const avro = __importStar(require("avsc"));
const zlib = __importStar(require("zlib"));
const gluesdk = __importStar(require("@aws-sdk/client-glue"));
var SchemaType;
(function (SchemaType) {
SchemaType["AVRO"] = "AVRO";
})(SchemaType || (exports.SchemaType = SchemaType = {}));
var SchemaCompatibilityType;
(function (SchemaCompatibilityType) {
SchemaCompatibilityType["NONE"] = "NONE";
SchemaCompatibilityType["BACKWARD"] = "BACKWARD";
SchemaCompatibilityType["BACKWARD_ALL"] = "BACKWARD_ALL";
SchemaCompatibilityType["DISABLED"] = "DISABLED";
SchemaCompatibilityType["FORWARD"] = "FORWARD";
SchemaCompatibilityType["FORWARD_ALL"] = "FORWARD_ALL";
SchemaCompatibilityType["FULL"] = "FULL";
SchemaCompatibilityType["FULL_ALL"] = "FULL_ALL";
})(SchemaCompatibilityType || (exports.SchemaCompatibilityType = SchemaCompatibilityType = {}));
var ERROR;
(function (ERROR) {
ERROR[ERROR["NO_ERROR"] = 0] = "NO_ERROR";
ERROR[ERROR["INVALID_HEADER_VERSION"] = 1] = "INVALID_HEADER_VERSION";
ERROR[ERROR["INVALID_COMPRESSION"] = 2] = "INVALID_COMPRESSION";
ERROR[ERROR["INVALID_SCHEMA_ID"] = 3] = "INVALID_SCHEMA_ID";
ERROR[ERROR["INVALID_SCHEMA"] = 4] = "INVALID_SCHEMA";
})(ERROR || (exports.ERROR = ERROR = {}));
class GlueSchemaRegistry {
/**
* Constructs a GlueSchemaRegistry
*
* @param registryName - name of the Glue registry you want to use
* @param props - optional AWS properties that are used when constructing the Glue object from the AWS SDK
*/
constructor(registryName, props) {
this.gc = new gluesdk.GlueClient(props);
this.registryName = registryName;
this.glueSchemaIdCache = {};
this.avroSchemaCache = {};
}
/**
* Updates the Glue client. Useful if you need to update the credentials, for example.
*
* @param props settings for the AWS Glue client
*/
updateGlueClient(props) {
this.gc = new gluesdk.GlueClient(props);
}
async loadGlueSchema(schemaId) {
const existingschema = await this.gc.send(new gluesdk.GetSchemaVersionCommand({
SchemaVersionId: schemaId,
}));
return existingschema;
}
/**
* Creates a new schema in the AWS Glue Schema Registry.
* Note: do not use createSchema if you want to create a new version of an existing schema.
* Instead use register().
*
* @param props - the details about the schema
* @throws if the schema already exists
* @throws if the Glue compatibility check fails
*/
async createSchema(props) {
const res = await this.gc.send(new gluesdk.CreateSchemaCommand({
DataFormat: props.type,
Compatibility: props.compatibility,
SchemaName: props.schemaName,
SchemaDefinition: props.schema,
RegistryId: {
RegistryName: this.registryName,
},
}));
if (res.SchemaVersionStatus === 'FAILURE')
throw new Error('Schema registration failure');
return res.SchemaVersionId;
}
/**
* Registers a new version of an existing schema.
* Returns the id of the existing schema version if a similar version already exists.
*
* @param props - the details about the schema
* @returns {string} the id of the schema version
* @throws if the schema does not exist
* @throws if the Glue compatibility check fails
*/
async register(props) {
const hash = crypto.createHash('SHA256').update(props.schemaName + '.' + props.schema);
const hashString = hash.digest('hex').toString();
const cachehit = this.glueSchemaIdCache[hashString];
if (cachehit) {
return cachehit;
}
const schema = await this.gc.send(new gluesdk.RegisterSchemaVersionCommand({
SchemaDefinition: props.schema,
SchemaId: {
RegistryName: this.registryName,
SchemaName: props.schemaName,
},
}));
if (!schema.SchemaVersionId)
throw new Error('Schema does not have SchemaVersionId');
if (schema.Status === 'FAILURE')
throw new Error('Schema registration failure');
this.glueSchemaIdCache[hashString] = schema.SchemaVersionId;
// store the avro schema in its cache to avoid another glue lookup when it's used
const avroSchema = avro.Type.forSchema(JSON.parse(props.schema));
this.avroSchemaCache[schema.SchemaVersionId] = avroSchema;
return schema.SchemaVersionId;
}
/**
* Encode the object with a specific glue schema version
*
* @param glueSchemaId - UUID of the Glue schema version that should be used to encode the message
* @param object - the object to encode
* @param props - optional encoding options
* @returns - a Buffer containing the binary message
*/
async encode(glueSchemaId, object, props) {
const ZLIB_COMPRESS_FUNC = (buf) => {
return new Promise((resolve, reject) => {
zlib.deflate(buf, (err, data) => {
if (err) {
reject(err);
}
else {
resolve(data);
}
});
});
};
const NO_COMPRESS_FUNC = (buf) => new Promise((resolve) => {
resolve(buf);
});
const schema = await this.getAvroSchemaForGlueId(glueSchemaId);
// construct the message binary
const buf = schema.toBuffer(object);
let compression_func = ZLIB_COMPRESS_FUNC;
let compressionbyte = GlueSchemaRegistry.COMPRESSION_ZLIB_BYTE;
if (props && !props.compress) {
compression_func = NO_COMPRESS_FUNC;
compressionbyte = GlueSchemaRegistry.COMPRESSION_DEFAULT_BYTE;
}
const output = Buffer.concat([
GlueSchemaRegistry.HEADER_VERSION_BYTE,
compressionbyte,
this.UUIDstringToByteArray(glueSchemaId),
await compression_func(buf),
]);
return output;
}
/**
* Analyze the binary message to determine if it is valid and if so, what schema version it was encoded with.
*
* @param message - the binary message to analyze
* @returns - an object containing the analysis results @see AnalyzeMessageResult
*/
async analyzeMessage(message) {
const headerversion = message.readInt8(0);
if (headerversion !== GlueSchemaRegistry.HEADER_VERSION) {
return {
valid: false,
error: ERROR.INVALID_HEADER_VERSION,
};
}
const compression = message.readInt8(1);
if (compression !== GlueSchemaRegistry.COMPRESSION_DEFAULT &&
compression !== GlueSchemaRegistry.COMPRESSION_ZLIB) {
return {
valid: false,
error: ERROR.INVALID_COMPRESSION,
};
}
try {
const producerSchemaId = uuid.stringify(message, 2);
try {
const producerschema = await this.loadGlueSchema(producerSchemaId);
if (!producerschema)
throw new Error('Schema not found');
if (producerschema.Status === 'FAILURE') {
return {
valid: false,
error: ERROR.INVALID_SCHEMA,
};
}
return {
valid: true,
headerversion,
compression,
schemaId: producerSchemaId,
schema: producerschema,
};
}
catch (e) {
return {
valid: false,
exception: e,
error: ERROR.INVALID_SCHEMA,
};
}
}
catch (e) {
return {
valid: false,
exception: e,
error: ERROR.INVALID_SCHEMA_ID,
};
}
}
/**
* Decode a message with a specific schema.
*
* @param message - Buffer with the binary encoded message
* @param consumerschema - The Avro schema that should be used to decode the message
* @returns - the deserialized message as object
*/
async decode(message, consumerschema) {
const headerversion = message.readInt8(0);
const compression = message.readInt8(1);
if (headerversion !== GlueSchemaRegistry.HEADER_VERSION) {
throw new Error(`Only header version ${GlueSchemaRegistry.HEADER_VERSION} is supported, received ${headerversion}`);
}
if (compression !== GlueSchemaRegistry.COMPRESSION_DEFAULT &&
compression !== GlueSchemaRegistry.COMPRESSION_ZLIB) {
throw new Error(`Only compression type 0 and 5 are supported, received ${compression}`);
}
const ZLIB_UNCOMPRESS_FUNC = (buf) => {
return new Promise((resolve, reject) => {
zlib.inflate(buf, (err, data) => {
if (err) {
reject(err);
}
else {
resolve(data);
}
});
});
};
const NO_UNCOMPRESS_FUNC = (buf) => new Promise((resolve) => {
resolve(buf);
});
const producerSchemaId = uuid.stringify(message, 2);
const producerschema = await this.getAvroSchemaForGlueId(producerSchemaId);
const resolver = this.getResolver(producerschema, consumerschema);
const content = Buffer.from(message.subarray(18));
let handlecompression = NO_UNCOMPRESS_FUNC;
if (compression === GlueSchemaRegistry.COMPRESSION_ZLIB) {
handlecompression = ZLIB_UNCOMPRESS_FUNC;
}
return consumerschema.fromBuffer(await handlecompression(content), resolver);
}
async getAvroSchemaForGlueId(id) {
if (this.avroSchemaCache[id])
return this.avroSchemaCache[id];
const schemastring = (await this.loadGlueSchema(id)).SchemaDefinition;
if (!schemastring)
throw new Error('Glue returned undefined schema definition');
const schema = avro.Type.forSchema(JSON.parse(schemastring));
this.avroSchemaCache[id] = schema;
return schema;
}
UUIDstringToByteArray(id) {
const idasbytes = uuid.parse(id);
return new Uint8Array(idasbytes);
}
getResolver(producerschema, consumerschema) {
const resolver = consumerschema.createResolver(producerschema);
return resolver;
}
static initByteBuffer(value) {
return Buffer.from([value]);
}
}
exports.GlueSchemaRegistry = GlueSchemaRegistry;
GlueSchemaRegistry.COMPRESSION_DEFAULT = 0;
GlueSchemaRegistry.COMPRESSION_ZLIB = 5;
GlueSchemaRegistry.HEADER_VERSION = 3;
GlueSchemaRegistry.HEADER_VERSION_BYTE = GlueSchemaRegistry.initByteBuffer(GlueSchemaRegistry.HEADER_VERSION); // default version 3
GlueSchemaRegistry.COMPRESSION_DEFAULT_BYTE = GlueSchemaRegistry.initByteBuffer(GlueSchemaRegistry.COMPRESSION_DEFAULT);
GlueSchemaRegistry.COMPRESSION_ZLIB_BYTE = GlueSchemaRegistry.initByteBuffer(GlueSchemaRegistry.COMPRESSION_ZLIB);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSwrQ0FBZ0M7QUFDaEMsMkNBQTRCO0FBQzVCLDJDQUE0QjtBQUM1QiwyQ0FBNEI7QUFDNUIsOERBQStDO0FBRS9DLElBQVksVUFFWDtBQUZELFdBQVksVUFBVTtJQUNwQiwyQkFBYSxDQUFBO0FBQ2YsQ0FBQyxFQUZXLFVBQVUsMEJBQVYsVUFBVSxRQUVyQjtBQU1ELElBQVksdUJBU1g7QUFURCxXQUFZLHVCQUF1QjtJQUNqQyx3Q0FBYSxDQUFBO0lBQ2IsZ0RBQXFCLENBQUE7SUFDckIsd0RBQTZCLENBQUE7SUFDN0IsZ0RBQXFCLENBQUE7SUFDckIsOENBQW1CLENBQUE7SUFDbkIsc0RBQTJCLENBQUE7SUFDM0Isd0NBQWEsQ0FBQTtJQUNiLGdEQUFxQixDQUFBO0FBQ3ZCLENBQUMsRUFUVyx1QkFBdUIsdUNBQXZCLHVCQUF1QixRQVNsQztBQVdELElBQVksS0FNWDtBQU5ELFdBQVksS0FBSztJQUNmLHlDQUFZLENBQUE7SUFDWixxRUFBMEIsQ0FBQTtJQUMxQiwrREFBdUIsQ0FBQTtJQUN2QiwyREFBcUIsQ0FBQTtJQUNyQixxREFBa0IsQ0FBQTtBQUNwQixDQUFDLEVBTlcsS0FBSyxxQkFBTCxLQUFLLFFBTWhCO0FBK0JELE1BQWEsa0JBQWtCO0lBZTdCOzs7OztPQUtHO0lBQ0gsWUFBWSxZQUFvQixFQUFFLEtBQStCO1FBQy9ELElBQUksQ0FBQyxFQUFFLEdBQUcsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBQ3ZDLElBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFBO1FBQ2hDLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxFQUFFLENBQUE7UUFDM0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxFQUFFLENBQUE7SUFDM0IsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxnQkFBZ0IsQ0FBQyxLQUErQjtRQUNyRCxJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUN6QyxDQUFDO0lBRU8sS0FBSyxDQUFDLGNBQWMsQ0FBQyxRQUFnQjtRQUMzQyxNQUFNLGNBQWMsR0FBRyxNQUFNLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUN2QyxJQUFJLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQztZQUNsQyxlQUFlLEVBQUUsUUFBUTtTQUMxQixDQUFDLENBQ0gsQ0FBQTtRQUNELE9BQU8sY0FBYyxDQUFBO0lBQ3ZCLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNJLEtBQUssQ0FBQyxZQUFZLENBQUMsS0FBd0I7UUFDaEQsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FDNUIsSUFBSSxPQUFPLENBQUMsbUJBQW1CLENBQUM7WUFDOUIsVUFBVSxFQUFFLEtBQUssQ0FBQyxJQUFJO1lBQ3RCLGFBQWEsRUFBRSxLQUFLLENBQUMsYUFBYTtZQUNsQyxVQUFVLEVBQUUsS0FBSyxDQUFDLFVBQVU7WUFDNUIsZ0JBQWdCLEVBQUUsS0FBSyxDQUFDLE1BQU07WUFDOUIsVUFBVSxFQUFFO2dCQUNWLFlBQVksRUFBRSxJQUFJLENBQUMsWUFBWTthQUNoQztTQUNGLENBQUMsQ0FDSCxDQUFBO1FBQ0QsSUFBSSxHQUFHLENBQUMsbUJBQW1CLEtBQUssU0FBUztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQTtRQUN6RixPQUFPLEdBQUcsQ0FBQyxlQUFlLENBQUE7SUFDNUIsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0gsS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUEwQjtRQUN2QyxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLEdBQUcsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDdEYsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQTtRQUNoRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLENBQUE7UUFDbkQsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUNiLE9BQU8sUUFBUSxDQUFBO1FBQ2pCLENBQUM7UUFDRCxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUMvQixJQUFJLE9BQU8sQ0FBQyw0QkFBNEIsQ0FBQztZQUN2QyxnQkFBZ0IsRUFBRSxLQUFLLENBQUMsTUFBTTtZQUM5QixRQUFRLEVBQUU7Z0JBQ1IsWUFBWSxFQUFFLElBQUksQ0FBQyxZQUFZO2dCQUMvQixVQUFVLEVBQUUsS0FBSyxDQUFDLFVBQVU7YUFDN0I7U0FDRixDQUFDLENBQ0gsQ0FBQTtRQUNELElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZTtZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQTtRQUNwRixJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssU0FBUztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQTtRQUMvRSxJQUFJLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLEdBQUcsTUFBTSxDQUFDLGVBQWUsQ0FBQTtRQUMzRCxpRkFBaUY7UUFDakYsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQTtRQUNoRSxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsR0FBRyxVQUFVLENBQUE7UUFDekQsT0FBTyxNQUFNLENBQUMsZUFBZSxDQUFBO0lBQy9CLENBQUM7SUFlRDs7Ozs7OztPQU9HO0lBQ0gsS0FBSyxDQUFDLE1BQU0sQ0FBQyxZQUFvQixFQUFFLE1BQVMsRUFBRSxLQUFtQjtRQUMvRCxNQUFNLGtCQUFrQixHQUFHLENBQUMsR0FBVyxFQUFtQixFQUFFO1lBQzFELE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7Z0JBQ3JDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO29CQUM5QixJQUFJLEdBQUcsRUFBRSxDQUFDO3dCQUNSLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQTtvQkFDYixDQUFDO3lCQUFNLENBQUM7d0JBQ04sT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFBO29CQUNmLENBQUM7Z0JBQ0gsQ0FBQyxDQUFDLENBQUE7WUFDSixDQUFDLENBQUMsQ0FBQTtRQUNKLENBQUMsQ0FBQTtRQUNELE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxHQUFXLEVBQW1CLEVBQUUsQ0FDeEQsSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUN0QixPQUFPLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDZCxDQUFDLENBQUMsQ0FBQTtRQUNKLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUFDLFlBQVksQ0FBQyxDQUFBO1FBQzlELCtCQUErQjtRQUMvQixNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1FBQ25DLElBQUksZ0JBQWdCLEdBQUcsa0JBQWtCLENBQUE7UUFDekMsSUFBSSxlQUFlLEdBQUcsa0JBQWtCLENBQUMscUJBQXFCLENBQUE7UUFDOUQsSUFBSSxLQUFLLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDN0IsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUE7WUFDbkMsZUFBZSxHQUFHLGtCQUFrQixDQUFDLHdCQUF3QixDQUFBO1FBQy9ELENBQUM7UUFDRCxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO1lBQzNCLGtCQUFrQixDQUFDLG1CQUFtQjtZQUN0QyxlQUFlO1lBQ2YsSUFBSSxDQUFDLHFCQUFxQixDQUFDLFlBQVksQ0FBQztZQUN4QyxNQUFNLGdCQUFnQixDQUFDLEdBQUcsQ0FBQztTQUM1QixDQUFDLENBQUE7UUFDRixPQUFPLE1BQU0sQ0FBQTtJQUNmLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQUMsT0FBZTtRQUNsQyxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQ3pDLElBQUksYUFBYSxLQUFLLGtCQUFrQixDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3hELE9BQU87Z0JBQ0wsS0FBSyxFQUFFLEtBQUs7Z0JBQ1osS0FBSyxFQUFFLEtBQUssQ0FBQyxzQkFBc0I7YUFDcEMsQ0FBQTtRQUNILENBQUM7UUFDRCxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQ3ZDLElBQ0UsV0FBVyxLQUFLLGtCQUFrQixDQUFDLG1CQUFtQjtZQUN0RCxXQUFXLEtBQUssa0JBQWtCLENBQUMsZ0JBQWdCLEVBQ25ELENBQUM7WUFDRCxPQUFPO2dCQUNMLEtBQUssRUFBRSxLQUFLO2dCQUNaLEtBQUssRUFBRSxLQUFLLENBQUMsbUJBQW1CO2FBQ2pDLENBQUE7UUFDSCxDQUFDO1FBQ0QsSUFBSSxDQUFDO1lBQ0gsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQTtZQUNuRCxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxjQUFjLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLGdCQUFnQixDQUFDLENBQUE7Z0JBQ2xFLElBQUksQ0FBQyxjQUFjO29CQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtnQkFDeEQsSUFBSSxjQUFjLENBQUMsTUFBTSxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUN4QyxPQUFPO3dCQUNMLEtBQUssRUFBRSxLQUFLO3dCQUNaLEtBQUssRUFBRSxLQUFLLENBQUMsY0FBYztxQkFDNUIsQ0FBQTtnQkFDSCxDQUFDO2dCQUNELE9BQU87b0JBQ0wsS0FBSyxFQUFFLElBQUk7b0JBQ1gsYUFBYTtvQkFDYixXQUFXO29CQUNYLFFBQVEsRUFBRSxnQkFBZ0I7b0JBQzFCLE1BQU0sRUFBRSxjQUFjO2lCQUN2QixDQUFBO1lBQ0gsQ0FBQztZQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ1gsT0FBTztvQkFDTCxLQUFLLEVBQUUsS0FBSztvQkFDWixTQUFTLEVBQUUsQ0FBQztvQkFDWixLQUFLLEVBQUUsS0FBSyxDQUFDLGNBQWM7aUJBQzVCLENBQUE7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDWCxPQUFPO2dCQUNMLEtBQUssRUFBRSxLQUFLO2dCQUNaLFNBQVMsRUFBRSxDQUFDO2dCQUNaLEtBQUssRUFBRSxLQUFLLENBQUMsaUJBQWlCO2FBQy9CLENBQUE7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBZSxFQUFFLGNBQXlCO1FBQ3JELE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDekMsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUN2QyxJQUFJLGFBQWEsS0FBSyxrQkFBa0IsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN4RCxNQUFNLElBQUksS0FBSyxDQUNiLHVCQUF1QixrQkFBa0IsQ0FBQyxjQUFjLDJCQUEyQixhQUFhLEVBQUUsQ0FDbkcsQ0FBQTtRQUNILENBQUM7UUFDRCxJQUNFLFdBQVcsS0FBSyxrQkFBa0IsQ0FBQyxtQkFBbUI7WUFDdEQsV0FBVyxLQUFLLGtCQUFrQixDQUFDLGdCQUFnQixFQUNuRCxDQUFDO1lBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyx5REFBeUQsV0FBVyxFQUFFLENBQUMsQ0FBQTtRQUN6RixDQUFDO1FBQ0QsTUFBTSxvQkFBb0IsR0FBRyxDQUFDLEdBQVcsRUFBbUIsRUFBRTtZQUM1RCxPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO2dCQUNyQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRTtvQkFDOUIsSUFBSSxHQUFHLEVBQUUsQ0FBQzt3QkFDUixNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7b0JBQ2IsQ0FBQzt5QkFBTSxDQUFDO3dCQUNOLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQTtvQkFDZixDQUFDO2dCQUNILENBQUMsQ0FBQyxDQUFBO1lBQ0osQ0FBQyxDQUFDLENBQUE7UUFDSixDQUFDLENBQUE7UUFDRCxNQUFNLGtCQUFrQixHQUFHLENBQUMsR0FBVyxFQUFtQixFQUFFLENBQzFELElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDdEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQ2QsQ0FBQyxDQUFDLENBQUE7UUFDSixNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFBO1FBQ25ELE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUFDLGdCQUFnQixDQUFDLENBQUE7UUFDMUUsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLENBQUE7UUFDakUsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUE7UUFDakQsSUFBSSxpQkFBaUIsR0FBRyxrQkFBa0IsQ0FBQTtRQUMxQyxJQUFJLFdBQVcsS0FBSyxrQkFBa0IsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3hELGlCQUFpQixHQUFHLG9CQUFvQixDQUFBO1FBQzFDLENBQUM7UUFDRCxPQUFPLGNBQWMsQ0FBQyxVQUFVLENBQUMsTUFBTSxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQTtJQUM5RSxDQUFDO0lBRU8sS0FBSyxDQUFDLHNCQUFzQixDQUFDLEVBQVU7UUFDN0MsSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztZQUFFLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUM3RCxNQUFNLFlBQVksR0FBRyxDQUFDLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFBO1FBQ3JFLElBQUksQ0FBQyxZQUFZO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQywyQ0FBMkMsQ0FBQyxDQUFBO1FBQy9FLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQTtRQUM1RCxJQUFJLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQTtRQUNqQyxPQUFPLE1BQU0sQ0FBQTtJQUNmLENBQUM7SUFFTyxxQkFBcUIsQ0FBQyxFQUFVO1FBQ3RDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDaEMsT0FBTyxJQUFJLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUNsQyxDQUFDO0lBRU8sV0FBVyxDQUFDLGNBQXlCLEVBQUUsY0FBeUI7UUFDdEUsTUFBTSxRQUFRLEdBQUcsY0FBYyxDQUFDLGNBQWMsQ0FBQyxjQUFjLENBQUMsQ0FBQTtRQUM5RCxPQUFPLFFBQVEsQ0FBQTtJQUNqQixDQUFDO0lBRU8sTUFBTSxDQUFDLGNBQWMsQ0FBQyxLQUFhO1FBQ3pDLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUE7SUFDN0IsQ0FBQzs7QUE5UkgsZ0RBK1JDO0FBdExRLHNDQUFtQixHQUFHLENBQUMsQ0FBQTtBQUN2QixtQ0FBZ0IsR0FBRyxDQUFDLENBQUE7QUFDcEIsaUNBQWMsR0FBRyxDQUFDLENBQUE7QUFDVixzQ0FBbUIsR0FBRyxrQkFBa0IsQ0FBQyxjQUFjLENBQ3BFLGtCQUFrQixDQUFDLGNBQWMsQ0FDbEMsQ0FBQSxDQUFDLG9CQUFvQjtBQUNQLDJDQUF3QixHQUFHLGtCQUFrQixDQUFDLGNBQWMsQ0FDekUsa0JBQWtCLENBQUMsbUJBQW1CLENBQ3ZDLENBQUE7QUFDYyx3Q0FBcUIsR0FBRyxrQkFBa0IsQ0FBQyxjQUFjLENBQ3RFLGtCQUFrQixDQUFDLGdCQUFnQixDQUNwQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0bydcbmltcG9ydCAqIGFzIHV1aWQgZnJvbSAndXVpZCdcbmltcG9ydCAqIGFzIGF2cm8gZnJvbSAnYXZzYydcbmltcG9ydCAqIGFzIHpsaWIgZnJvbSAnemxpYidcbmltcG9ydCAqIGFzIGdsdWVzZGsgZnJvbSAnQGF3cy1zZGsvY2xpZW50LWdsdWUnXG5cbmV4cG9ydCBlbnVtIFNjaGVtYVR5cGUge1xuICBBVlJPID0gJ0FWUk8nLFxufVxuZXhwb3J0IGludGVyZmFjZSBSZWdpc3RlclNjaGVtYVByb3BzIHtcbiAgdHlwZTogU2NoZW1hVHlwZVxuICBzY2hlbWFOYW1lOiBzdHJpbmdcbiAgc2NoZW1hOiBzdHJpbmdcbn1cbmV4cG9ydCBlbnVtIFNjaGVtYUNvbXBhdGliaWxpdHlUeXBlIHtcbiAgTk9ORSA9ICdOT05FJyxcbiAgQkFDS1dBUkQgPSAnQkFDS1dBUkQnLFxuICBCQUNLV0FSRF9BTEwgPSAnQkFDS1dBUkRfQUxMJyxcbiAgRElTQUJMRUQgPSAnRElTQUJMRUQnLFxuICBGT1JXQVJEID0gJ0ZPUldBUkQnLFxuICBGT1JXQVJEX0FMTCA9ICdGT1JXQVJEX0FMTCcsXG4gIEZVTEwgPSAnRlVMTCcsXG4gIEZVTExfQUxMID0gJ0ZVTExfQUxMJyxcbn1cbmV4cG9ydCBpbnRlcmZhY2UgQ3JlYXRlU2NoZW1hUHJvcHMge1xuICB0eXBlOiBTY2hlbWFUeXBlXG4gIHNjaGVtYU5hbWU6IHN0cmluZ1xuICBjb21wYXRpYmlsaXR5OiBTY2hlbWFDb21wYXRpYmlsaXR5VHlwZVxuICBzY2hlbWE6IHN0cmluZ1xufVxuZXhwb3J0IGludGVyZmFjZSBFbmNvZGVQcm9wcyB7XG4gIGNvbXByZXNzOiBib29sZWFuXG59XG5cbmV4cG9ydCBlbnVtIEVSUk9SIHtcbiAgTk9fRVJST1IgPSAwLFxuICBJTlZBTElEX0hFQURFUl9WRVJTSU9OID0gMSxcbiAgSU5WQUxJRF9DT01QUkVTU0lPTiA9IDIsXG4gIElOVkFMSURfU0NIRU1BX0lEID0gMyxcbiAgSU5WQUxJRF9TQ0hFTUEgPSA0LFxufVxuXG5leHBvcnQgdHlwZSBBbmFseXplTWVzc2FnZVJlc3VsdCA9IHtcbiAgLyoqXG4gICAqIHRydWUgaWYgdGhlIG1lc3NhZ2UgaXMgdmFsaWRcbiAgICovXG4gIHZhbGlkOiBib29sZWFuXG4gIC8qKlxuICAgKiB0aGUgZXJyb3IgY29kZSwgaWYgdmFsaWQgaXMgZmFsc2UsIG90aGVyd2lzZSB1bmRlZmluZWRcbiAgICovXG4gIGVycm9yPzogRVJST1JcbiAgLyoqIHRoZSBvcmlnaW5hbCBleGNlcHRpb24sIGlmIGF2YWlsYWJsZSAqL1xuICBleGNlcHRpb24/OiB1bmtub3duXG4gIC8qKlxuICAgKiB0aGUgaGVhZGVyIHZlcnNpb25cbiAgICovXG4gIGhlYWRlcnZlcnNpb24/OiBudW1iZXJcbiAgLyoqXG4gICAqIHRoZSBjb21wcmVzc2lvbiB0eXBlLCBtYXkgYmUgMCAobm9uZSkgb3IgNSAoZ3ppcClcbiAgICovXG4gIGNvbXByZXNzaW9uPzogbnVtYmVyXG4gIC8qKlxuICAgKiB0aGUgdXVpZCBvZiB0aGUgc2NoZW1hXG4gICAqL1xuICBzY2hlbWFJZD86IHN0cmluZ1xuICAvKipcbiAgICogdGhlIGdsdWUgc2NoZW1hXG4gICAqL1xuICBzY2hlbWE/OiBnbHVlc2RrLkdldFNjaGVtYVZlcnNpb25SZXNwb25zZVxufVxuXG5leHBvcnQgY2xhc3MgR2x1ZVNjaGVtYVJlZ2lzdHJ5PFQ+IHtcbiAgLypcbiAgVGhpcyBjbGFzcyBhaW1zIHRvIGJlIGNvbXBhdGlibGUgd2l0aCB0aGUgamF2YSBzZXJkZSBpbXBsZW1lbnRhdGlvbiBmcm9tIEFXUy5cbiAgaHR0cHM6Ly9naXRodWIuY29tL2F3c2xhYnMvYXdzLWdsdWUtc2NoZW1hLXJlZ2lzdHJ5L2Jsb2IvbWFzdGVyL3NlcmlhbGl6ZXItZGVzZXJpYWxpemVyL3NyYy9tYWluL2phdmEvY29tL2FtYXpvbmF3cy9zZXJ2aWNlcy9zY2hlbWFyZWdpc3RyeS9zZXJpYWxpemVycy9TZXJpYWxpemF0aW9uRGF0YUVuY29kZXIuamF2YVxuICBodHRwczovL2dpdGh1Yi5jb20vYXdzbGFicy9hd3MtZ2x1ZS1zY2hlbWEtcmVnaXN0cnkvYmxvYi9tYXN0ZXIvY29tbW9uL3NyYy9tYWluL2phdmEvY29tL2FtYXpvbmF3cy9zZXJ2aWNlcy9zY2hlbWFyZWdpc3RyeS91dGlscy9BV1NTY2hlbWFSZWdpc3RyeUNvbnN0YW50cy5qYXZhXG4gICovXG4gIHByaXZhdGUgZ2M6IGdsdWVzZGsuR2x1ZUNsaWVudFxuICBwdWJsaWMgcmVhZG9ubHkgcmVnaXN0cnlOYW1lOiBzdHJpbmdcbiAgcHJpdmF0ZSBnbHVlU2NoZW1hSWRDYWNoZToge1xuICAgIFtoYXNoOiBzdHJpbmddOiBzdHJpbmdcbiAgfVxuICBwcml2YXRlIGF2cm9TY2hlbWFDYWNoZToge1xuICAgIFtrZXk6IHN0cmluZ106IGF2cm8uVHlwZVxuICB9XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgYSBHbHVlU2NoZW1hUmVnaXN0cnlcbiAgICpcbiAgICogQHBhcmFtIHJlZ2lzdHJ5TmFtZSAtIG5hbWUgb2YgdGhlIEdsdWUgcmVnaXN0cnkgeW91IHdhbnQgdG8gdXNlXG4gICAqIEBwYXJhbSBwcm9wcyAtIG9wdGlvbmFsIEFXUyBwcm9wZXJ0aWVzIHRoYXQgYXJlIHVzZWQgd2hlbiBjb25zdHJ1Y3RpbmcgdGhlIEdsdWUgb2JqZWN0IGZyb20gdGhlIEFXUyBTREtcbiAgICovXG4gIGNvbnN0cnVjdG9yKHJlZ2lzdHJ5TmFtZTogc3RyaW5nLCBwcm9wczogZ2x1ZXNkay5HbHVlQ2xpZW50Q29uZmlnKSB7XG4gICAgdGhpcy5nYyA9IG5ldyBnbHVlc2RrLkdsdWVDbGllbnQocHJvcHMpXG4gICAgdGhpcy5yZWdpc3RyeU5hbWUgPSByZWdpc3RyeU5hbWVcbiAgICB0aGlzLmdsdWVTY2hlbWFJZENhY2hlID0ge31cbiAgICB0aGlzLmF2cm9TY2hlbWFDYWNoZSA9IHt9XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlcyB0aGUgR2x1ZSBjbGllbnQuIFVzZWZ1bCBpZiB5b3UgbmVlZCB0byB1cGRhdGUgdGhlIGNyZWRlbnRpYWxzLCBmb3IgZXhhbXBsZS5cbiAgICpcbiAgICogQHBhcmFtIHByb3BzIHNldHRpbmdzIGZvciB0aGUgQVdTIEdsdWUgY2xpZW50XG4gICAqL1xuICBwdWJsaWMgdXBkYXRlR2x1ZUNsaWVudChwcm9wczogZ2x1ZXNkay5HbHVlQ2xpZW50Q29uZmlnKSB7XG4gICAgdGhpcy5nYyA9IG5ldyBnbHVlc2RrLkdsdWVDbGllbnQocHJvcHMpXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGxvYWRHbHVlU2NoZW1hKHNjaGVtYUlkOiBzdHJpbmcpIHtcbiAgICBjb25zdCBleGlzdGluZ3NjaGVtYSA9IGF3YWl0IHRoaXMuZ2Muc2VuZChcbiAgICAgIG5ldyBnbHVlc2RrLkdldFNjaGVtYVZlcnNpb25Db21tYW5kKHtcbiAgICAgICAgU2NoZW1hVmVyc2lvbklkOiBzY2hlbWFJZCxcbiAgICAgIH0pLFxuICAgIClcbiAgICByZXR1cm4gZXhpc3RpbmdzY2hlbWFcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmV3IHNjaGVtYSBpbiB0aGUgQVdTIEdsdWUgU2NoZW1hIFJlZ2lzdHJ5LlxuICAgKiBOb3RlOiBkbyBub3QgdXNlIGNyZWF0ZVNjaGVtYSBpZiB5b3Ugd2FudCB0byBjcmVhdGUgYSBuZXcgdmVyc2lvbiBvZiBhbiBleGlzdGluZyBzY2hlbWEuXG4gICAqIEluc3RlYWQgdXNlIHJlZ2lzdGVyKCkuXG4gICAqXG4gICAqIEBwYXJhbSBwcm9wcyAtIHRoZSBkZXRhaWxzIGFib3V0IHRoZSBzY2hlbWFcbiAgICogQHRocm93cyBpZiB0aGUgc2NoZW1hIGFscmVhZHkgZXhpc3RzXG4gICAqIEB0aHJvd3MgaWYgdGhlIEdsdWUgY29tcGF0aWJpbGl0eSBjaGVjayBmYWlsc1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGNyZWF0ZVNjaGVtYShwcm9wczogQ3JlYXRlU2NoZW1hUHJvcHMpIHtcbiAgICBjb25zdCByZXMgPSBhd2FpdCB0aGlzLmdjLnNlbmQoXG4gICAgICBuZXcgZ2x1ZXNkay5DcmVhdGVTY2hlbWFDb21tYW5kKHtcbiAgICAgICAgRGF0YUZvcm1hdDogcHJvcHMudHlwZSxcbiAgICAgICAgQ29tcGF0aWJpbGl0eTogcHJvcHMuY29tcGF0aWJpbGl0eSxcbiAgICAgICAgU2NoZW1hTmFtZTogcHJvcHMuc2NoZW1hTmFtZSxcbiAgICAgICAgU2NoZW1hRGVmaW5pdGlvbjogcHJvcHMuc2NoZW1hLFxuICAgICAgICBSZWdpc3RyeUlkOiB7XG4gICAgICAgICAgUmVnaXN0cnlOYW1lOiB0aGlzLnJlZ2lzdHJ5TmFtZSxcbiAgICAgICAgfSxcbiAgICAgIH0pLFxuICAgIClcbiAgICBpZiAocmVzLlNjaGVtYVZlcnNpb25TdGF0dXMgPT09ICdGQUlMVVJFJykgdGhyb3cgbmV3IEVycm9yKCdTY2hlbWEgcmVnaXN0cmF0aW9uIGZhaWx1cmUnKVxuICAgIHJldHVybiByZXMuU2NoZW1hVmVyc2lvbklkXG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgbmV3IHZlcnNpb24gb2YgYW4gZXhpc3Rpbmcgc2NoZW1hLlxuICAgKiBSZXR1cm5zIHRoZSBpZCBvZiB0aGUgZXhpc3Rpbmcgc2NoZW1hIHZlcnNpb24gaWYgYSBzaW1pbGFyIHZlcnNpb24gYWxyZWFkeSBleGlzdHMuXG4gICAqXG4gICAqIEBwYXJhbSBwcm9wcyAtIHRoZSBkZXRhaWxzIGFib3V0IHRoZSBzY2hlbWFcbiAgICogQHJldHVybnMge3N0cmluZ30gdGhlIGlkIG9mIHRoZSBzY2hlbWEgdmVyc2lvblxuICAgKiBAdGhyb3dzIGlmIHRoZSBzY2hlbWEgZG9lcyBub3QgZXhpc3RcbiAgICogQHRocm93cyBpZiB0aGUgR2x1ZSBjb21wYXRpYmlsaXR5IGNoZWNrIGZhaWxzXG4gICAqL1xuICBhc3luYyByZWdpc3Rlcihwcm9wczogUmVnaXN0ZXJTY2hlbWFQcm9wcyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgaGFzaCA9IGNyeXB0by5jcmVhdGVIYXNoKCdTSEEyNTYnKS51cGRhdGUocHJvcHMuc2NoZW1hTmFtZSArICcuJyArIHByb3BzLnNjaGVtYSlcbiAgICBjb25zdCBoYXNoU3RyaW5nID0gaGFzaC5kaWdlc3QoJ2hleCcpLnRvU3RyaW5nKClcbiAgICBjb25zdCBjYWNoZWhpdCA9IHRoaXMuZ2x1ZVNjaGVtYUlkQ2FjaGVbaGFzaFN0cmluZ11cbiAgICBpZiAoY2FjaGVoaXQpIHtcbiAgICAgIHJldHVybiBjYWNoZWhpdFxuICAgIH1cbiAgICBjb25zdCBzY2hlbWEgPSBhd2FpdCB0aGlzLmdjLnNlbmQoXG4gICAgICBuZXcgZ2x1ZXNkay5SZWdpc3RlclNjaGVtYVZlcnNpb25Db21tYW5kKHtcbiAgICAgICAgU2NoZW1hRGVmaW5pdGlvbjogcHJvcHMuc2NoZW1hLFxuICAgICAgICBTY2hlbWFJZDoge1xuICAgICAgICAgIFJlZ2lzdHJ5TmFtZTogdGhpcy5yZWdpc3RyeU5hbWUsXG4gICAgICAgICAgU2NoZW1hTmFtZTogcHJvcHMuc2NoZW1hTmFtZSxcbiAgICAgICAgfSxcbiAgICAgIH0pLFxuICAgIClcbiAgICBpZiAoIXNjaGVtYS5TY2hlbWFWZXJzaW9uSWQpIHRocm93IG5ldyBFcnJvcignU2NoZW1hIGRvZXMgbm90IGhhdmUgU2NoZW1hVmVyc2lvbklkJylcbiAgICBpZiAoc2NoZW1hLlN0YXR1cyA9PT0gJ0ZBSUxVUkUnKSB0aHJvdyBuZXcgRXJyb3IoJ1NjaGVtYSByZWdpc3RyYXRpb24gZmFpbHVyZScpXG4gICAgdGhpcy5nbHVlU2NoZW1hSWRDYWNoZVtoYXNoU3RyaW5nXSA9IHNjaGVtYS5TY2hlbWFWZXJzaW9uSWRcbiAgICAvLyBzdG9yZSB0aGUgYXZybyBzY2hlbWEgaW4gaXRzIGNhY2hlIHRvIGF2b2lkIGFub3RoZXIgZ2x1ZSBsb29rdXAgd2hlbiBpdCdzIHVzZWRcbiAgICBjb25zdCBhdnJvU2NoZW1hID0gYXZyby5UeXBlLmZvclNjaGVtYShKU09OLnBhcnNlKHByb3BzLnNjaGVtYSkpXG4gICAgdGhpcy5hdnJvU2NoZW1hQ2FjaGVbc2NoZW1hLlNjaGVtYVZlcnNpb25JZF0gPSBhdnJvU2NoZW1hXG4gICAgcmV0dXJuIHNjaGVtYS5TY2hlbWFWZXJzaW9uSWRcbiAgfVxuXG4gIHN0YXRpYyBDT01QUkVTU0lPTl9ERUZBVUxUID0gMFxuICBzdGF0aWMgQ09NUFJFU1NJT05fWkxJQiA9IDVcbiAgc3RhdGljIEhFQURFUl9WRVJTSU9OID0gM1xuICBwcml2YXRlIHN0YXRpYyBIRUFERVJfVkVSU0lPTl9CWVRFID0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LmluaXRCeXRlQnVmZmVyKFxuICAgIEdsdWVTY2hlbWFSZWdpc3RyeS5IRUFERVJfVkVSU0lPTixcbiAgKSAvLyBkZWZhdWx0IHZlcnNpb24gM1xuICBwcml2YXRlIHN0YXRpYyBDT01QUkVTU0lPTl9ERUZBVUxUX0JZVEUgPSBHbHVlU2NoZW1hUmVnaXN0cnkuaW5pdEJ5dGVCdWZmZXIoXG4gICAgR2x1ZVNjaGVtYVJlZ2lzdHJ5LkNPTVBSRVNTSU9OX0RFRkFVTFQsIC8vIG5vIGNvbXByZXNzaW9uXG4gIClcbiAgcHJpdmF0ZSBzdGF0aWMgQ09NUFJFU1NJT05fWkxJQl9CWVRFID0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LmluaXRCeXRlQnVmZmVyKFxuICAgIEdsdWVTY2hlbWFSZWdpc3RyeS5DT01QUkVTU0lPTl9aTElCLFxuICApXG5cbiAgLyoqXG4gICAqIEVuY29kZSB0aGUgb2JqZWN0IHdpdGggYSBzcGVjaWZpYyBnbHVlIHNjaGVtYSB2ZXJzaW9uXG4gICAqXG4gICAqIEBwYXJhbSBnbHVlU2NoZW1hSWQgLSBVVUlEIG9mIHRoZSBHbHVlIHNjaGVtYSB2ZXJzaW9uIHRoYXQgc2hvdWxkIGJlIHVzZWQgdG8gZW5jb2RlIHRoZSBtZXNzYWdlXG4gICAqIEBwYXJhbSBvYmplY3QgLSB0aGUgb2JqZWN0IHRvIGVuY29kZVxuICAgKiBAcGFyYW0gcHJvcHMgLSBvcHRpb25hbCBlbmNvZGluZyBvcHRpb25zXG4gICAqIEByZXR1cm5zIC0gYSBCdWZmZXIgY29udGFpbmluZyB0aGUgYmluYXJ5IG1lc3NhZ2VcbiAgICovXG4gIGFzeW5jIGVuY29kZShnbHVlU2NoZW1hSWQ6IHN0cmluZywgb2JqZWN0OiBULCBwcm9wcz86IEVuY29kZVByb3BzKSB7XG4gICAgY29uc3QgWkxJQl9DT01QUkVTU19GVU5DID0gKGJ1ZjogQnVmZmVyKTogUHJvbWlzZTxCdWZmZXI+ID0+IHtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIHpsaWIuZGVmbGF0ZShidWYsIChlcnIsIGRhdGEpID0+IHtcbiAgICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICByZWplY3QoZXJyKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXNvbHZlKGRhdGEpXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgfSlcbiAgICB9XG4gICAgY29uc3QgTk9fQ09NUFJFU1NfRlVOQyA9IChidWY6IEJ1ZmZlcik6IFByb21pc2U8QnVmZmVyPiA9PlxuICAgICAgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgcmVzb2x2ZShidWYpXG4gICAgICB9KVxuICAgIGNvbnN0IHNjaGVtYSA9IGF3YWl0IHRoaXMuZ2V0QXZyb1NjaGVtYUZvckdsdWVJZChnbHVlU2NoZW1hSWQpXG4gICAgLy8gY29uc3RydWN0IHRoZSBtZXNzYWdlIGJpbmFyeVxuICAgIGNvbnN0IGJ1ZiA9IHNjaGVtYS50b0J1ZmZlcihvYmplY3QpXG4gICAgbGV0IGNvbXByZXNzaW9uX2Z1bmMgPSBaTElCX0NPTVBSRVNTX0ZVTkNcbiAgICBsZXQgY29tcHJlc3Npb25ieXRlID0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LkNPTVBSRVNTSU9OX1pMSUJfQllURVxuICAgIGlmIChwcm9wcyAmJiAhcHJvcHMuY29tcHJlc3MpIHtcbiAgICAgIGNvbXByZXNzaW9uX2Z1bmMgPSBOT19DT01QUkVTU19GVU5DXG4gICAgICBjb21wcmVzc2lvbmJ5dGUgPSBHbHVlU2NoZW1hUmVnaXN0cnkuQ09NUFJFU1NJT05fREVGQVVMVF9CWVRFXG4gICAgfVxuICAgIGNvbnN0IG91dHB1dCA9IEJ1ZmZlci5jb25jYXQoW1xuICAgICAgR2x1ZVNjaGVtYVJlZ2lzdHJ5LkhFQURFUl9WRVJTSU9OX0JZVEUsXG4gICAgICBjb21wcmVzc2lvbmJ5dGUsXG4gICAgICB0aGlzLlVVSURzdHJpbmdUb0J5dGVBcnJheShnbHVlU2NoZW1hSWQpLFxuICAgICAgYXdhaXQgY29tcHJlc3Npb25fZnVuYyhidWYpLFxuICAgIF0pXG4gICAgcmV0dXJuIG91dHB1dFxuICB9XG5cbiAgLyoqXG4gICAqIEFuYWx5emUgdGhlIGJpbmFyeSBtZXNzYWdlIHRvIGRldGVybWluZSBpZiBpdCBpcyB2YWxpZCBhbmQgaWYgc28sIHdoYXQgc2NoZW1hIHZlcnNpb24gaXQgd2FzIGVuY29kZWQgd2l0aC5cbiAgICpcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSB0aGUgYmluYXJ5IG1lc3NhZ2UgdG8gYW5hbHl6ZVxuICAgKiBAcmV0dXJucyAtIGFuIG9iamVjdCBjb250YWluaW5nIHRoZSBhbmFseXNpcyByZXN1bHRzIEBzZWUgQW5hbHl6ZU1lc3NhZ2VSZXN1bHRcbiAgICovXG4gIGFzeW5jIGFuYWx5emVNZXNzYWdlKG1lc3NhZ2U6IEJ1ZmZlcik6IFByb21pc2U8QW5hbHl6ZU1lc3NhZ2VSZXN1bHQ+IHtcbiAgICBjb25zdCBoZWFkZXJ2ZXJzaW9uID0gbWVzc2FnZS5yZWFkSW50OCgwKVxuICAgIGlmIChoZWFkZXJ2ZXJzaW9uICE9PSBHbHVlU2NoZW1hUmVnaXN0cnkuSEVBREVSX1ZFUlNJT04pIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZhbGlkOiBmYWxzZSxcbiAgICAgICAgZXJyb3I6IEVSUk9SLklOVkFMSURfSEVBREVSX1ZFUlNJT04sXG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGNvbXByZXNzaW9uID0gbWVzc2FnZS5yZWFkSW50OCgxKVxuICAgIGlmIChcbiAgICAgIGNvbXByZXNzaW9uICE9PSBHbHVlU2NoZW1hUmVnaXN0cnkuQ09NUFJFU1NJT05fREVGQVVMVCAmJlxuICAgICAgY29tcHJlc3Npb24gIT09IEdsdWVTY2hlbWFSZWdpc3RyeS5DT01QUkVTU0lPTl9aTElCXG4gICAgKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB2YWxpZDogZmFsc2UsXG4gICAgICAgIGVycm9yOiBFUlJPUi5JTlZBTElEX0NPTVBSRVNTSU9OLFxuICAgICAgfVxuICAgIH1cbiAgICB0cnkge1xuICAgICAgY29uc3QgcHJvZHVjZXJTY2hlbWFJZCA9IHV1aWQuc3RyaW5naWZ5KG1lc3NhZ2UsIDIpXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBwcm9kdWNlcnNjaGVtYSA9IGF3YWl0IHRoaXMubG9hZEdsdWVTY2hlbWEocHJvZHVjZXJTY2hlbWFJZClcbiAgICAgICAgaWYgKCFwcm9kdWNlcnNjaGVtYSkgdGhyb3cgbmV3IEVycm9yKCdTY2hlbWEgbm90IGZvdW5kJylcbiAgICAgICAgaWYgKHByb2R1Y2Vyc2NoZW1hLlN0YXR1cyA9PT0gJ0ZBSUxVUkUnKSB7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHZhbGlkOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBFUlJPUi5JTlZBTElEX1NDSEVNQSxcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB2YWxpZDogdHJ1ZSxcbiAgICAgICAgICBoZWFkZXJ2ZXJzaW9uLFxuICAgICAgICAgIGNvbXByZXNzaW9uLFxuICAgICAgICAgIHNjaGVtYUlkOiBwcm9kdWNlclNjaGVtYUlkLFxuICAgICAgICAgIHNjaGVtYTogcHJvZHVjZXJzY2hlbWEsXG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB2YWxpZDogZmFsc2UsXG4gICAgICAgICAgZXhjZXB0aW9uOiBlLFxuICAgICAgICAgIGVycm9yOiBFUlJPUi5JTlZBTElEX1NDSEVNQSxcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZhbGlkOiBmYWxzZSxcbiAgICAgICAgZXhjZXB0aW9uOiBlLFxuICAgICAgICBlcnJvcjogRVJST1IuSU5WQUxJRF9TQ0hFTUFfSUQsXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIERlY29kZSBhIG1lc3NhZ2Ugd2l0aCBhIHNwZWNpZmljIHNjaGVtYS5cbiAgICpcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBCdWZmZXIgd2l0aCB0aGUgYmluYXJ5IGVuY29kZWQgbWVzc2FnZVxuICAgKiBAcGFyYW0gY29uc3VtZXJzY2hlbWEgLSBUaGUgQXZybyBzY2hlbWEgdGhhdCBzaG91bGQgYmUgdXNlZCB0byBkZWNvZGUgdGhlIG1lc3NhZ2VcbiAgICogQHJldHVybnMgLSB0aGUgZGVzZXJpYWxpemVkIG1lc3NhZ2UgYXMgb2JqZWN0XG4gICAqL1xuICBhc3luYyBkZWNvZGUobWVzc2FnZTogQnVmZmVyLCBjb25zdW1lcnNjaGVtYTogYXZyby5UeXBlKTogUHJvbWlzZTxUPiB7XG4gICAgY29uc3QgaGVhZGVydmVyc2lvbiA9IG1lc3NhZ2UucmVhZEludDgoMClcbiAgICBjb25zdCBjb21wcmVzc2lvbiA9IG1lc3NhZ2UucmVhZEludDgoMSlcbiAgICBpZiAoaGVhZGVydmVyc2lvbiAhPT0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LkhFQURFUl9WRVJTSU9OKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBPbmx5IGhlYWRlciB2ZXJzaW9uICR7R2x1ZVNjaGVtYVJlZ2lzdHJ5LkhFQURFUl9WRVJTSU9OfSBpcyBzdXBwb3J0ZWQsIHJlY2VpdmVkICR7aGVhZGVydmVyc2lvbn1gLFxuICAgICAgKVxuICAgIH1cbiAgICBpZiAoXG4gICAgICBjb21wcmVzc2lvbiAhPT0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LkNPTVBSRVNTSU9OX0RFRkFVTFQgJiZcbiAgICAgIGNvbXByZXNzaW9uICE9PSBHbHVlU2NoZW1hUmVnaXN0cnkuQ09NUFJFU1NJT05fWkxJQlxuICAgICkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBPbmx5IGNvbXByZXNzaW9uIHR5cGUgMCBhbmQgNSBhcmUgc3VwcG9ydGVkLCByZWNlaXZlZCAke2NvbXByZXNzaW9ufWApXG4gICAgfVxuICAgIGNvbnN0IFpMSUJfVU5DT01QUkVTU19GVU5DID0gKGJ1ZjogQnVmZmVyKTogUHJvbWlzZTxCdWZmZXI+ID0+IHtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIHpsaWIuaW5mbGF0ZShidWYsIChlcnIsIGRhdGEpID0+IHtcbiAgICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICByZWplY3QoZXJyKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXNvbHZlKGRhdGEpXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgfSlcbiAgICB9XG4gICAgY29uc3QgTk9fVU5DT01QUkVTU19GVU5DID0gKGJ1ZjogQnVmZmVyKTogUHJvbWlzZTxCdWZmZXI+ID0+XG4gICAgICBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICByZXNvbHZlKGJ1ZilcbiAgICAgIH0pXG4gICAgY29uc3QgcHJvZHVjZXJTY2hlbWFJZCA9IHV1aWQuc3RyaW5naWZ5KG1lc3NhZ2UsIDIpXG4gICAgY29uc3QgcHJvZHVjZXJzY2hlbWEgPSBhd2FpdCB0aGlzLmdldEF2cm9TY2hlbWFGb3JHbHVlSWQocHJvZHVjZXJTY2hlbWFJZClcbiAgICBjb25zdCByZXNvbHZlciA9IHRoaXMuZ2V0UmVzb2x2ZXIocHJvZHVjZXJzY2hlbWEsIGNvbnN1bWVyc2NoZW1hKVxuICAgIGNvbnN0IGNvbnRlbnQgPSBCdWZmZXIuZnJvbShtZXNzYWdlLnN1YmFycmF5KDE4KSlcbiAgICBsZXQgaGFuZGxlY29tcHJlc3Npb24gPSBOT19VTkNPTVBSRVNTX0ZVTkNcbiAgICBpZiAoY29tcHJlc3Npb24gPT09IEdsdWVTY2hlbWFSZWdpc3RyeS5DT01QUkVTU0lPTl9aTElCKSB7XG4gICAgICBoYW5kbGVjb21wcmVzc2lvbiA9IFpMSUJfVU5DT01QUkVTU19GVU5DXG4gICAgfVxuICAgIHJldHVybiBjb25zdW1lcnNjaGVtYS5mcm9tQnVmZmVyKGF3YWl0IGhhbmRsZWNvbXByZXNzaW9uKGNvbnRlbnQpLCByZXNvbHZlcilcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZ2V0QXZyb1NjaGVtYUZvckdsdWVJZChpZDogc3RyaW5nKSB7XG4gICAgaWYgKHRoaXMuYXZyb1NjaGVtYUNhY2hlW2lkXSkgcmV0dXJuIHRoaXMuYXZyb1NjaGVtYUNhY2hlW2lkXVxuICAgIGNvbnN0IHNjaGVtYXN0cmluZyA9IChhd2FpdCB0aGlzLmxvYWRHbHVlU2NoZW1hKGlkKSkuU2NoZW1hRGVmaW5pdGlvblxuICAgIGlmICghc2NoZW1hc3RyaW5nKSB0aHJvdyBuZXcgRXJyb3IoJ0dsdWUgcmV0dXJuZWQgdW5kZWZpbmVkIHNjaGVtYSBkZWZpbml0aW9uJylcbiAgICBjb25zdCBzY2hlbWEgPSBhdnJvLlR5cGUuZm9yU2NoZW1hKEpTT04ucGFyc2Uoc2NoZW1hc3RyaW5nKSlcbiAgICB0aGlzLmF2cm9TY2hlbWFDYWNoZVtpZF0gPSBzY2hlbWFcbiAgICByZXR1cm4gc2NoZW1hXG4gIH1cblxuICBwcml2YXRlIFVVSURzdHJpbmdUb0J5dGVBcnJheShpZDogc3RyaW5nKSB7XG4gICAgY29uc3QgaWRhc2J5dGVzID0gdXVpZC5wYXJzZShpZClcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoaWRhc2J5dGVzKVxuICB9XG5cbiAgcHJpdmF0ZSBnZXRSZXNvbHZlcihwcm9kdWNlcnNjaGVtYTogYXZyby5UeXBlLCBjb25zdW1lcnNjaGVtYTogYXZyby5UeXBlKSB7XG4gICAgY29uc3QgcmVzb2x2ZXIgPSBjb25zdW1lcnNjaGVtYS5jcmVhdGVSZXNvbHZlcihwcm9kdWNlcnNjaGVtYSlcbiAgICByZXR1cm4gcmVzb2x2ZXJcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGluaXRCeXRlQnVmZmVyKHZhbHVlOiBudW1iZXIpIHtcbiAgICByZXR1cm4gQnVmZmVyLmZyb20oW3ZhbHVlXSlcbiAgfVxufVxuIl19