UNPKG

@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
"use strict"; 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