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.

349 lines 42.7 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 PromiseDispatcher { constructor(limit) { this.limit = limit; this.active = 0; this.queue = []; } async run(fn) { if (this.active >= this.limit) { await new Promise((resolve) => this.queue.push(resolve)); } this.active++; try { return await fn(); } finally { this.active--; const next = this.queue.shift(); if (next) next(); } } } 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 * @param maxConcurrentGlueCalls - optional maximum number of concurrent calls to the Glue service. Defaults to 1. */ constructor(registryName, props, maxConcurrentGlueCalls = 1) { this.runningGlueSchemaLoads = new Map(); this.gc = new gluesdk.GlueClient(props); this.registryName = registryName; this.glueSchemaIdCache = {}; this.avroSchemaCache = {}; this.limiter = new PromiseDispatcher(Math.max(1, maxConcurrentGlueCalls)); } /** * 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 existing = this.runningGlueSchemaLoads.get(schemaId); if (existing) return existing; const p = this.limiter.run(() => this.gc.send(new gluesdk.GetSchemaVersionCommand({ SchemaVersionId: schemaId, }))); this.runningGlueSchemaLoads.set(schemaId, p); try { const res = await p; return res; } finally { this.runningGlueSchemaLoads.delete(schemaId); } } /** * * Creates a new schema in the glue schema registry. * * Throws if a SchemaVersionStatus in the response equals 'FAILURE'. * @param props * @returns the id of the created schema version */ async createSchema(props) { const res = await this.limiter.run(() => 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) { return consumerschema.createResolver(producerschema); } 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsK0NBQWdDO0FBQ2hDLDJDQUE0QjtBQUM1QiwyQ0FBNEI7QUFDNUIsMkNBQTRCO0FBQzVCLDhEQUErQztBQUUvQyxJQUFZLFVBRVg7QUFGRCxXQUFZLFVBQVU7SUFDcEIsMkJBQWEsQ0FBQTtBQUNmLENBQUMsRUFGVyxVQUFVLDBCQUFWLFVBQVUsUUFFckI7QUFNRCxJQUFZLHVCQVNYO0FBVEQsV0FBWSx1QkFBdUI7SUFDakMsd0NBQWEsQ0FBQTtJQUNiLGdEQUFxQixDQUFBO0lBQ3JCLHdEQUE2QixDQUFBO0lBQzdCLGdEQUFxQixDQUFBO0lBQ3JCLDhDQUFtQixDQUFBO0lBQ25CLHNEQUEyQixDQUFBO0lBQzNCLHdDQUFhLENBQUE7SUFDYixnREFBcUIsQ0FBQTtBQUN2QixDQUFDLEVBVFcsdUJBQXVCLHVDQUF2Qix1QkFBdUIsUUFTbEM7QUFXRCxJQUFZLEtBTVg7QUFORCxXQUFZLEtBQUs7SUFDZix5Q0FBWSxDQUFBO0lBQ1oscUVBQTBCLENBQUE7SUFDMUIsK0RBQXVCLENBQUE7SUFDdkIsMkRBQXFCLENBQUE7SUFDckIscURBQWtCLENBQUE7QUFDcEIsQ0FBQyxFQU5XLEtBQUsscUJBQUwsS0FBSyxRQU1oQjtBQStCRCxNQUFNLGlCQUFpQjtJQUdyQixZQUE2QixLQUFhO1FBQWIsVUFBSyxHQUFMLEtBQUssQ0FBUTtRQUZsQyxXQUFNLEdBQUcsQ0FBQyxDQUFBO1FBQ1YsVUFBSyxHQUFzQixFQUFFLENBQUE7SUFDUSxDQUFDO0lBRTlDLEtBQUssQ0FBQyxHQUFHLENBQUksRUFBb0I7UUFDL0IsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUM5QixNQUFNLElBQUksT0FBTyxDQUFPLENBQUMsT0FB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QUUsY0FBeUI7UUFDdEUsT0FBTyxjQUFjLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FBQyxDQUFBO0lBQ3RELENBQUM7SUFDTyxNQUFNLENBQUMsY0FBYyxDQUFDLEtBQWE7UUFDekMsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQTtJQUM3QixDQUFDOztBQTVTSCxnREE2U0M7QUFuTFEsc0NBQW1CLEdBQUcsQ0FBQyxBQUFKLENBQUk7QUFDdkIsbUNBQWdCLEdBQUcsQ0FBQyxBQUFKLENBQUk7QUFDcEIsaUNBQWMsR0FBRyxDQUFDLEFBQUosQ0FBSTtBQUNWLHNDQUFtQixHQUFHLGtCQUFrQixDQUFDLGNBQWMsQ0FDcEUsa0JBQWtCLENBQUMsY0FBYyxDQUNsQyxBQUZpQyxDQUVqQyxDQUFDLG9CQUFvQjtBQUNQLDJDQUF3QixHQUFHLGtCQUFrQixDQUFDLGNBQWMsQ0FDekUsa0JBQWtCLENBQUMsbUJBQW1CLENBQ3ZDLEFBRnNDLENBRXRDO0FBQ2Msd0NBQXFCLEdBQUcsa0JBQWtCLENBQUMsY0FBYyxDQUN0RSxrQkFBa0IsQ0FBQyxnQkFBZ0IsQ0FDcEMsQUFGbUMsQ0FFbkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSAnY3J5cHRvJ1xuaW1wb3J0ICogYXMgdXVpZCBmcm9tICd1dWlkJ1xuaW1wb3J0ICogYXMgYXZybyBmcm9tICdhdnNjJ1xuaW1wb3J0ICogYXMgemxpYiBmcm9tICd6bGliJ1xuaW1wb3J0ICogYXMgZ2x1ZXNkayBmcm9tICdAYXdzLXNkay9jbGllbnQtZ2x1ZSdcblxuZXhwb3J0IGVudW0gU2NoZW1hVHlwZSB7XG4gIEFWUk8gPSAnQVZSTycsXG59XG5leHBvcnQgaW50ZXJmYWNlIFJlZ2lzdGVyU2NoZW1hUHJvcHMge1xuICB0eXBlOiBTY2hlbWFUeXBlXG4gIHNjaGVtYU5hbWU6IHN0cmluZ1xuICBzY2hlbWE6IHN0cmluZ1xufVxuZXhwb3J0IGVudW0gU2NoZW1hQ29tcGF0aWJpbGl0eVR5cGUge1xuICBOT05FID0gJ05PTkUnLFxuICBCQUNLV0FSRCA9ICdCQUNLV0FSRCcsXG4gIEJBQ0tXQVJEX0FMTCA9ICdCQUNLV0FSRF9BTEwnLFxuICBESVNBQkxFRCA9ICdESVNBQkxFRCcsXG4gIEZPUldBUkQgPSAnRk9SV0FSRCcsXG4gIEZPUldBUkRfQUxMID0gJ0ZPUldBUkRfQUxMJyxcbiAgRlVMTCA9ICdGVUxMJyxcbiAgRlVMTF9BTEwgPSAnRlVMTF9BTEwnLFxufVxuZXhwb3J0IGludGVyZmFjZSBDcmVhdGVTY2hlbWFQcm9wcyB7XG4gIHR5cGU6IFNjaGVtYVR5cGVcbiAgc2NoZW1hTmFtZTogc3RyaW5nXG4gIGNvbXBhdGliaWxpdHk6IFNjaGVtYUNvbXBhdGliaWxpdHlUeXBlXG4gIHNjaGVtYTogc3RyaW5nXG59XG5leHBvcnQgaW50ZXJmYWNlIEVuY29kZVByb3BzIHtcbiAgY29tcHJlc3M6IGJvb2xlYW5cbn1cblxuZXhwb3J0IGVudW0gRVJST1Ige1xuICBOT19FUlJPUiA9IDAsXG4gIElOVkFMSURfSEVBREVSX1ZFUlNJT04gPSAxLFxuICBJTlZBTElEX0NPTVBSRVNTSU9OID0gMixcbiAgSU5WQUxJRF9TQ0hFTUFfSUQgPSAzLFxuICBJTlZBTElEX1NDSEVNQSA9IDQsXG59XG5cbmV4cG9ydCB0eXBlIEFuYWx5emVNZXNzYWdlUmVzdWx0ID0ge1xuICAvKipcbiAgICogdHJ1ZSBpZiB0aGUgbWVzc2FnZSBpcyB2YWxpZFxuICAgKi9cbiAgdmFsaWQ6IGJvb2xlYW5cbiAgLyoqXG4gICAqIHRoZSBlcnJvciBjb2RlLCBpZiB2YWxpZCBpcyBmYWxzZSwgb3RoZXJ3aXNlIHVuZGVmaW5lZFxuICAgKi9cbiAgZXJyb3I/OiBFUlJPUlxuICAvKiogdGhlIG9yaWdpbmFsIGV4Y2VwdGlvbiwgaWYgYXZhaWxhYmxlICovXG4gIGV4Y2VwdGlvbj86IHVua25vd25cbiAgLyoqXG4gICAqIHRoZSBoZWFkZXIgdmVyc2lvblxuICAgKi9cbiAgaGVhZGVydmVyc2lvbj86IG51bWJlclxuICAvKipcbiAgICogdGhlIGNvbXByZXNzaW9uIHR5cGUsIG1heSBiZSAwIChub25lKSBvciA1IChnemlwKVxuICAgKi9cbiAgY29tcHJlc3Npb24/OiBudW1iZXJcbiAgLyoqXG4gICAqIHRoZSB1dWlkIG9mIHRoZSBzY2hlbWFcbiAgICovXG4gIHNjaGVtYUlkPzogc3RyaW5nXG4gIC8qKlxuICAgKiB0aGUgZ2x1ZSBzY2hlbWFcbiAgICovXG4gIHNjaGVtYT86IGdsdWVzZGsuR2V0U2NoZW1hVmVyc2lvblJlc3BvbnNlXG59XG5cbmNsYXNzIFByb21pc2VEaXNwYXRjaGVyIHtcbiAgcHJpdmF0ZSBhY3RpdmUgPSAwXG4gIHByaXZhdGUgcXVldWU6IEFycmF5PCgpID0+IHZvaWQ+ID0gW11cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBsaW1pdDogbnVtYmVyKSB7fVxuXG4gIGFzeW5jIHJ1bjxUPihmbjogKCkgPT4gUHJvbWlzZTxUPik6IFByb21pc2U8VD4ge1xuICAgIGlmICh0aGlzLmFjdGl2ZSA+PSB0aGlzLmxpbWl0KSB7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4gdGhpcy5xdWV1ZS5wdXNoKHJlc29sdmUpKVxuICAgIH1cbiAgICB0aGlzLmFjdGl2ZSsrXG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBmbigpXG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuYWN0aXZlLS1cbiAgICAgIGNvbnN0IG5leHQgPSB0aGlzLnF1ZXVlLnNoaWZ0KClcbiAgICAgIGlmIChuZXh0KSBuZXh0KClcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEdsdWVTY2hlbWFSZWdpc3RyeSB7XG4gIC8qXG4gIFRoaXMgY2xhc3MgYWltcyB0byBiZSBjb21wYXRpYmxlIHdpdGggdGhlIGphdmEgc2VyZGUgaW1wbGVtZW50YXRpb24gZnJvbSBBV1MuXG4gIGh0dHBzOi8vZ2l0aHViLmNvbS9hd3NsYWJzL2F3cy1nbHVlLXNjaGVtYS1yZWdpc3RyeS9ibG9iL21hc3Rlci9zZXJpYWxpemVyLWRlc2VyaWFsaXplci9zcmMvbWFpbi9qYXZhL2NvbS9hbWF6b25hd3Mvc2VydmljZXMvc2NoZW1hcmVnaXN0cnkvc2VyaWFsaXplcnMvU2VyaWFsaXphdGlvbkRhdGFFbmNvZGVyLmphdmFcbiAgaHR0cHM6Ly9naXRodWIuY29tL2F3c2xhYnMvYXdzLWdsdWUtc2NoZW1hLXJlZ2lzdHJ5L2Jsb2IvbWFzdGVyL2NvbW1vbi9zcmMvbWFpbi9qYXZhL2NvbS9hbWF6b25hd3Mvc2VydmljZXMvc2NoZW1hcmVnaXN0cnkvdXRpbHMvQVdTU2NoZW1hUmVnaXN0cnlDb25zdGFudHMuamF2YVxuICAqL1xuICBwcml2YXRlIGdjOiBnbHVlc2RrLkdsdWVDbGllbnRcbiAgcHVibGljIHJlYWRvbmx5IHJlZ2lzdHJ5TmFtZTogc3RyaW5nXG5cbiAgcHJpdmF0ZSBnbHVlU2NoZW1hSWRDYWNoZToge1xuICAgIFtoYXNoOiBzdHJpbmddOiBzdHJpbmdcbiAgfVxuICBwcml2YXRlIGF2cm9TY2hlbWFDYWNoZToge1xuICAgIFtrZXk6IHN0cmluZ106IGF2cm8uVHlwZVxuICB9XG5cbiAgcHJpdmF0ZSBydW5uaW5nR2x1ZVNjaGVtYUxvYWRzID0gbmV3IE1hcDxzdHJpbmcsIFByb21pc2U8Z2x1ZXNkay5HZXRTY2hlbWFWZXJzaW9uUmVzcG9uc2U+PigpXG4gIHByaXZhdGUgbGltaXRlcjogUHJvbWlzZURpc3BhdGNoZXJcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhIEdsdWVTY2hlbWFSZWdpc3RyeVxuICAgKlxuICAgKiBAcGFyYW0gcmVnaXN0cnlOYW1lIC0gbmFtZSBvZiB0aGUgR2x1ZSByZWdpc3RyeSB5b3Ugd2FudCB0byB1c2VcbiAgICogQHBhcmFtIHByb3BzIC0gb3B0aW9uYWwgQVdTIHByb3BlcnRpZXMgdGhhdCBhcmUgdXNlZCB3aGVuIGNvbnN0cnVjdGluZyB0aGUgR2x1ZSBvYmplY3QgZnJvbSB0aGUgQVdTIFNES1xuICAgKiBAcGFyYW0gbWF4Q29uY3VycmVudEdsdWVDYWxscyAtIG9wdGlvbmFsIG1heGltdW0gbnVtYmVyIG9mIGNvbmN1cnJlbnQgY2FsbHMgdG8gdGhlIEdsdWUgc2VydmljZS4gRGVmYXVsdHMgdG8gMS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHJlZ2lzdHJ5TmFtZTogc3RyaW5nLCBwcm9wczogZ2x1ZXNkay5HbHVlQ2xpZW50Q29uZmlnLCBtYXhDb25jdXJyZW50R2x1ZUNhbGxzID0gMSkge1xuICAgIHRoaXMuZ2MgPSBuZXcgZ2x1ZXNkay5HbHVlQ2xpZW50KHByb3BzKVxuICAgIHRoaXMucmVnaXN0cnlOYW1lID0gcmVnaXN0cnlOYW1lXG4gICAgdGhpcy5nbHVlU2NoZW1hSWRDYWNoZSA9IHt9XG4gICAgdGhpcy5hdnJvU2NoZW1hQ2FjaGUgPSB7fVxuICAgIHRoaXMubGltaXRlciA9IG5ldyBQcm9taXNlRGlzcGF0Y2hlcihNYXRoLm1heCgxLCBtYXhDb25jdXJyZW50R2x1ZUNhbGxzKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIHRoZSBHbHVlIGNsaWVudC4gVXNlZnVsIGlmIHlvdSBuZWVkIHRvIHVwZGF0ZSB0aGUgY3JlZGVudGlhbHMsIGZvciBleGFtcGxlLlxuICAgKlxuICAgKiBAcGFyYW0gcHJvcHMgc2V0dGluZ3MgZm9yIHRoZSBBV1MgR2x1ZSBjbGllbnRcbiAgICovXG4gIHVwZGF0ZUdsdWVDbGllbnQocHJvcHM6IGdsdWVzZGsuR2x1ZUNsaWVudENvbmZpZykge1xuICAgIHRoaXMuZ2MgPSBuZXcgZ2x1ZXNkay5HbHVlQ2xpZW50KHByb3BzKVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBsb2FkR2x1ZVNjaGVtYShzY2hlbWFJZDogc3RyaW5nKSB7XG4gICAgY29uc3QgZXhpc3RpbmcgPSB0aGlzLnJ1bm5pbmdHbHVlU2NoZW1hTG9hZHMuZ2V0KHNjaGVtYUlkKVxuICAgIGlmIChleGlzdGluZykgcmV0dXJuIGV4aXN0aW5nXG4gICAgY29uc3QgcCA9IHRoaXMubGltaXRlci5ydW4oKCkgPT5cbiAgICAgIHRoaXMuZ2Muc2VuZChcbiAgICAgICAgbmV3IGdsdWVzZGsuR2V0U2NoZW1hVmVyc2lvbkNvbW1hbmQoe1xuICAgICAgICAgIFNjaGVtYVZlcnNpb25JZDogc2NoZW1hSWQsXG4gICAgICAgIH0pLFxuICAgICAgKSxcbiAgICApXG5cbiAgICB0aGlzLnJ1bm5pbmdHbHVlU2NoZW1hTG9hZHMuc2V0KHNjaGVtYUlkLCBwKVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IHBcbiAgICAgIHJldHVybiByZXNcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5ydW5uaW5nR2x1ZVNjaGVtYUxvYWRzLmRlbGV0ZShzY2hlbWFJZClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICpcbiAgICogQ3JlYXRlcyBhIG5ldyBzY2hlbWEgaW4gdGhlIGdsdWUgc2NoZW1hIHJlZ2lzdHJ5LlxuICAgKlxuICAgKiBUaHJvd3MgaWYgYSBTY2hlbWFWZXJzaW9uU3RhdHVzIGluIHRoZSByZXNwb25zZSBlcXVhbHMgJ0ZBSUxVUkUnLlxuICAgKiBAcGFyYW0gcHJvcHNcbiAgICogQHJldHVybnMgdGhlIGlkIG9mIHRoZSBjcmVhdGVkIHNjaGVtYSB2ZXJzaW9uXG4gICAqL1xuICBhc3luYyBjcmVhdGVTY2hlbWEocHJvcHM6IENyZWF0ZVNjaGVtYVByb3BzKSB7XG4gICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5saW1pdGVyLnJ1bigoKSA9PlxuICAgICAgdGhpcy5nYy5zZW5kKFxuICAgICAgICBuZXcgZ2x1ZXNkay5DcmVhdGVTY2hlbWFDb21tYW5kKHtcbiAgICAgICAgICBEYXRhRm9ybWF0OiBwcm9wcy50eXBlLFxuICAgICAgICAgIENvbXBhdGliaWxpdHk6IHByb3BzLmNvbXBhdGliaWxpdHksXG4gICAgICAgICAgU2NoZW1hTmFtZTogcHJvcHMuc2NoZW1hTmFtZSxcbiAgICAgICAgICBTY2hlbWFEZWZpbml0aW9uOiBwcm9wcy5zY2hlbWEsXG4gICAgICAgICAgUmVnaXN0cnlJZDogeyBSZWdpc3RyeU5hbWU6IHRoaXMucmVnaXN0cnlOYW1lIH0sXG4gICAgICAgIH0pLFxuICAgICAgKSxcbiAgICApXG4gICAgaWYgKHJlcy5TY2hlbWFWZXJzaW9uU3RhdHVzID09PSAnRkFJTFVSRScpIHRocm93IG5ldyBFcnJvcignU2NoZW1hIHJlZ2lzdHJhdGlvbiBmYWlsdXJlJylcbiAgICByZXR1cm4gcmVzLlNjaGVtYVZlcnNpb25JZFxuICB9XG5cbiAgLyoqXG4gICAqIFJlZ2lzdGVycyBhIG5ldyB2ZXJzaW9uIG9mIGFuIGV4aXN0aW5nIHNjaGVtYS5cbiAgICogUmV0dXJucyB0aGUgaWQgb2YgdGhlIGV4aXN0aW5nIHNjaGVtYSB2ZXJzaW9uIGlmIGEgc2ltaWxhciB2ZXJzaW9uIGFscmVhZHkgZXhpc3RzLlxuICAgKlxuICAgKiBAcGFyYW0gcHJvcHMgLSB0aGUgZGV0YWlscyBhYm91dCB0aGUgc2NoZW1hXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IHRoZSBpZCBvZiB0aGUgc2NoZW1hIHZlcnNpb25cbiAgICogQHRocm93cyBpZiB0aGUgc2NoZW1hIGRvZXMgbm90IGV4aXN0XG4gICAqIEB0aHJvd3MgaWYgdGhlIEdsdWUgY29tcGF0aWJpbGl0eSBjaGVjayBmYWlsc1xuICAgKi9cbiAgYXN5bmMgcmVnaXN0ZXIocHJvcHM6IFJlZ2lzdGVyU2NoZW1hUHJvcHMpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGNvbnN0IGhhc2ggPSBjcnlwdG8uY3JlYXRlSGFzaCgnU0hBMjU2JykudXBkYXRlKHByb3BzLnNjaGVtYU5hbWUgKyAnLicgKyBwcm9wcy5zY2hlbWEpXG4gICAgY29uc3QgaGFzaFN0cmluZyA9IGhhc2guZGlnZXN0KCdoZXgnKS50b1N0cmluZygpXG4gICAgY29uc3QgY2FjaGVoaXQgPSB0aGlzLmdsdWVTY2hlbWFJZENhY2hlW2hhc2hTdHJpbmddXG4gICAgaWYgKGNhY2hlaGl0KSB7XG4gICAgICByZXR1cm4gY2FjaGVoaXRcbiAgICB9XG4gICAgY29uc3Qgc2NoZW1hID0gYXdhaXQgdGhpcy5nYy5zZW5kKFxuICAgICAgbmV3IGdsdWVzZGsuUmVnaXN0ZXJTY2hlbWFWZXJzaW9uQ29tbWFuZCh7XG4gICAgICAgIFNjaGVtYURlZmluaXRpb246IHByb3BzLnNjaGVtYSxcbiAgICAgICAgU2NoZW1hSWQ6IHtcbiAgICAgICAgICBSZWdpc3RyeU5hbWU6IHRoaXMucmVnaXN0cnlOYW1lLFxuICAgICAgICAgIFNjaGVtYU5hbWU6IHByb3BzLnNjaGVtYU5hbWUsXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICApXG4gICAgaWYgKCFzY2hlbWEuU2NoZW1hVmVyc2lvbklkKSB0aHJvdyBuZXcgRXJyb3IoJ1NjaGVtYSBkb2VzIG5vdCBoYXZlIFNjaGVtYVZlcnNpb25JZCcpXG4gICAgaWYgKHNjaGVtYS5TdGF0dXMgPT09ICdGQUlMVVJFJykgdGhyb3cgbmV3IEVycm9yKCdTY2hlbWEgcmVnaXN0cmF0aW9uIGZhaWx1cmUnKVxuICAgIHRoaXMuZ2x1ZVNjaGVtYUlkQ2FjaGVbaGFzaFN0cmluZ10gPSBzY2hlbWEuU2NoZW1hVmVyc2lvbklkXG4gICAgLy8gc3RvcmUgdGhlIGF2cm8gc2NoZW1hIGluIGl0cyBjYWNoZSB0byBhdm9pZCBhbm90aGVyIGdsdWUgbG9va3VwIHdoZW4gaXQncyB1c2VkXG4gICAgY29uc3QgYXZyb1NjaGVtYSA9IGF2cm8uVHlwZS5mb3JTY2hlbWEoSlNPTi5wYXJzZShwcm9wcy5zY2hlbWEpKVxuICAgIHRoaXMuYXZyb1NjaGVtYUNhY2hlW3NjaGVtYS5TY2hlbWFWZXJzaW9uSWRdID0gYXZyb1NjaGVtYVxuICAgIHJldHVybiBzY2hlbWEuU2NoZW1hVmVyc2lvbklkXG4gIH1cblxuICBzdGF0aWMgQ09NUFJFU1NJT05fREVGQVVMVCA9IDBcbiAgc3RhdGljIENPTVBSRVNTSU9OX1pMSUIgPSA1XG4gIHN0YXRpYyBIRUFERVJfVkVSU0lPTiA9IDNcbiAgcHJpdmF0ZSBzdGF0aWMgSEVBREVSX1ZFUlNJT05fQllURSA9IEdsdWVTY2hlbWFSZWdpc3RyeS5pbml0Qnl0ZUJ1ZmZlcihcbiAgICBHbHVlU2NoZW1hUmVnaXN0cnkuSEVBREVSX1ZFUlNJT04sXG4gICkgLy8gZGVmYXVsdCB2ZXJzaW9uIDNcbiAgcHJpdmF0ZSBzdGF0aWMgQ09NUFJFU1NJT05fREVGQVVMVF9CWVRFID0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LmluaXRCeXRlQnVmZmVyKFxuICAgIEdsdWVTY2hlbWFSZWdpc3RyeS5DT01QUkVTU0lPTl9ERUZBVUxULCAvLyBubyBjb21wcmVzc2lvblxuICApXG4gIHByaXZhdGUgc3RhdGljIENPTVBSRVNTSU9OX1pMSUJfQllURSA9IEdsdWVTY2hlbWFSZWdpc3RyeS5pbml0Qnl0ZUJ1ZmZlcihcbiAgICBHbHVlU2NoZW1hUmVnaXN0cnkuQ09NUFJFU1NJT05fWkxJQixcbiAgKVxuXG4gIC8qKlxuICAgKiBFbmNvZGUgdGhlIG9iamVjdCB3aXRoIGEgc3BlY2lmaWMgZ2x1ZSBzY2hlbWEgdmVyc2lvblxuICAgKlxuICAgKiBAcGFyYW0gZ2x1ZVNjaGVtYUlkIC0gVVVJRCBvZiB0aGUgR2x1ZSBzY2hlbWEgdmVyc2lvbiB0aGF0IHNob3VsZCBiZSB1c2VkIHRvIGVuY29kZSB0aGUgbWVzc2FnZVxuICAgKiBAcGFyYW0gb2JqZWN0IC0gdGhlIG9iamVjdCB0byBlbmNvZGVcbiAgICogQHBhcmFtIHByb3BzIC0gb3B0aW9uYWwgZW5jb2Rpbmcgb3B0aW9uc1xuICAgKiBAcmV0dXJucyAtIGEgQnVmZmVyIGNvbnRhaW5pbmcgdGhlIGJpbmFyeSBtZXNzYWdlXG4gICAqL1xuICBhc3luYyBlbmNvZGU8VD4oZ2x1ZVNjaGVtYUlkOiBzdHJpbmcsIG9iamVjdDogVCwgcHJvcHM/OiBFbmNvZGVQcm9wcykge1xuICAgIGNvbnN0IFpMSUJfQ09NUFJFU1NfRlVOQyA9IChidWY6IEJ1ZmZlcik6IFByb21pc2U8QnVmZmVyPiA9PiB7XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB6bGliLmRlZmxhdGUoYnVmLCAoZXJyLCBkYXRhKSA9PiB7XG4gICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgcmVqZWN0KGVycilcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVzb2x2ZShkYXRhKVxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgIH0pXG4gICAgfVxuICAgIGNvbnN0IE5PX0NPTVBSRVNTX0ZVTkMgPSAoYnVmOiBCdWZmZXIpOiBQcm9taXNlPEJ1ZmZlcj4gPT5cbiAgICAgIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgIHJlc29sdmUoYnVmKVxuICAgICAgfSlcbiAgICBjb25zdCBzY2hlbWEgPSBhd2FpdCB0aGlzLmdldEF2cm9TY2hlbWFGb3JHbHVlSWQoZ2x1ZVNjaGVtYUlkKVxuICAgIC8vIGNvbnN0cnVjdCB0aGUgbWVzc2FnZSBiaW5hcnlcbiAgICBjb25zdCBidWYgPSBzY2hlbWEudG9CdWZmZXIob2JqZWN0KVxuICAgIGxldCBjb21wcmVzc2lvbl9mdW5jID0gWkxJQl9DT01QUkVTU19GVU5DXG4gICAgbGV0IGNvbXByZXNzaW9uYnl0ZSA9IEdsdWVTY2hlbWFSZWdpc3RyeS5DT01QUkVTU0lPTl9aTElCX0JZVEVcbiAgICBpZiAocHJvcHMgJiYgIXByb3BzLmNvbXByZXNzKSB7XG4gICAgICBjb21wcmVzc2lvbl9mdW5jID0gTk9fQ09NUFJFU1NfRlVOQ1xuICAgICAgY29tcHJlc3Npb25ieXRlID0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LkNPTVBSRVNTSU9OX0RFRkFVTFRfQllURVxuICAgIH1cbiAgICBjb25zdCBvdXRwdXQgPSBCdWZmZXIuY29uY2F0KFtcbiAgICAgIEdsdWVTY2hlbWFSZWdpc3RyeS5IRUFERVJfVkVSU0lPTl9CWVRFLFxuICAgICAgY29tcHJlc3Npb25ieXRlLFxuICAgICAgdGhpcy5VVUlEc3RyaW5nVG9CeXRlQXJyYXkoZ2x1ZVNjaGVtYUlkKSxcbiAgICAgIGF3YWl0IGNvbXByZXNzaW9uX2Z1bmMoYnVmKSxcbiAgICBdKVxuICAgIHJldHVybiBvdXRwdXRcbiAgfVxuXG4gIC8qKlxuICAgKiBBbmFseXplIHRoZSBiaW5hcnkgbWVzc2FnZSB0byBkZXRlcm1pbmUgaWYgaXQgaXMgdmFsaWQgYW5kIGlmIHNvLCB3aGF0IHNjaGVtYSB2ZXJzaW9uIGl0IHdhcyBlbmNvZGVkIHdpdGguXG4gICAqXG4gICAqIEBwYXJhbSBtZXNzYWdlIC0gdGhlIGJpbmFyeSBtZXNzYWdlIHRvIGFuYWx5emVcbiAgICogQHJldHVybnMgLSBhbiBvYmplY3QgY29udGFpbmluZyB0aGUgYW5hbHlzaXMgcmVzdWx0cyBAc2VlIEFuYWx5emVNZXNzYWdlUmVzdWx0XG4gICAqL1xuICBhc3luYyBhbmFseXplTWVzc2FnZShtZXNzYWdlOiBCdWZmZXIpOiBQcm9taXNlPEFuYWx5emVNZXNzYWdlUmVzdWx0PiB7XG4gICAgY29uc3QgaGVhZGVydmVyc2lvbiA9IG1lc3NhZ2UucmVhZEludDgoMClcbiAgICBpZiAoaGVhZGVydmVyc2lvbiAhPT0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LkhFQURFUl9WRVJTSU9OKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB2YWxpZDogZmFsc2UsXG4gICAgICAgIGVycm9yOiBFUlJPUi5JTlZBTElEX0hFQURFUl9WRVJTSU9OLFxuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCBjb21wcmVzc2lvbiA9IG1lc3NhZ2UucmVhZEludDgoMSlcbiAgICBpZiAoXG4gICAgICBjb21wcmVzc2lvbiAhPT0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LkNPTVBSRVNTSU9OX0RFRkFVTFQgJiZcbiAgICAgIGNvbXByZXNzaW9uICE9PSBHbHVlU2NoZW1hUmVnaXN0cnkuQ09NUFJFU1NJT05fWkxJQlxuICAgICkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdmFsaWQ6IGZhbHNlLFxuICAgICAgICBlcnJvcjogRVJST1IuSU5WQUxJRF9DT01QUkVTU0lPTixcbiAgICAgIH1cbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHByb2R1Y2VyU2NoZW1hSWQgPSB1dWlkLnN0cmluZ2lmeShtZXNzYWdlLCAyKVxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcHJvZHVjZXJzY2hlbWEgPSBhd2FpdCB0aGlzLmxvYWRHbHVlU2NoZW1hKHByb2R1Y2VyU2NoZW1hSWQpXG4gICAgICAgIGlmICghcHJvZHVjZXJzY2hlbWEpIHRocm93IG5ldyBFcnJvcignU2NoZW1hIG5vdCBmb3VuZCcpXG4gICAgICAgIGlmIChwcm9kdWNlcnNjaGVtYS5TdGF0dXMgPT09ICdGQUlMVVJFJykge1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB2YWxpZDogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogRVJST1IuSU5WQUxJRF9TQ0hFTUEsXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdmFsaWQ6IHRydWUsXG4gICAgICAgICAgaGVhZGVydmVyc2lvbixcbiAgICAgICAgICBjb21wcmVzc2lvbixcbiAgICAgICAgICBzY2hlbWFJZDogcHJvZHVjZXJTY2hlbWFJZCxcbiAgICAgICAgICBzY2hlbWE6IHByb2R1Y2Vyc2NoZW1hLFxuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdmFsaWQ6IGZhbHNlLFxuICAgICAgICAgIGV4Y2VwdGlvbjogZSxcbiAgICAgICAgICBlcnJvcjogRVJST1IuSU5WQUxJRF9TQ0hFTUEsXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB2YWxpZDogZmFsc2UsXG4gICAgICAgIGV4Y2VwdGlvbjogZSxcbiAgICAgICAgZXJyb3I6IEVSUk9SLklOVkFMSURfU0NIRU1BX0lELFxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNvZGUgYSBtZXNzYWdlIHdpdGggYSBzcGVjaWZpYyBzY2hlbWEuXG4gICAqXG4gICAqIEBwYXJhbSBtZXNzYWdlIC0gQnVmZmVyIHdpdGggdGhlIGJpbmFyeSBlbmNvZGVkIG1lc3NhZ2VcbiAgICogQHBhcmFtIGNvbnN1bWVyc2NoZW1hIC0gVGhlIEF2cm8gc2NoZW1hIHRoYXQgc2hvdWxkIGJlIHVzZWQgdG8gZGVjb2RlIHRoZSBtZXNzYWdlXG4gICAqIEByZXR1cm5zIC0gdGhlIGRlc2VyaWFsaXplZCBtZXNzYWdlIGFzIG9iamVjdFxuICAgKi9cbiAgYXN5bmMgZGVjb2RlPFQ+KG1lc3NhZ2U6IEJ1ZmZlciwgY29uc3VtZXJzY2hlbWE6IGF2cm8uVHlwZSk6IFByb21pc2U8VD4ge1xuICAgIGNvbnN0IGhlYWRlcnZlcnNpb24gPSBtZXNzYWdlLnJlYWRJbnQ4KDApXG4gICAgY29uc3QgY29tcHJlc3Npb24gPSBtZXNzYWdlLnJlYWRJbnQ4KDEpXG4gICAgaWYgKGhlYWRlcnZlcnNpb24gIT09IEdsdWVTY2hlbWFSZWdpc3RyeS5IRUFERVJfVkVSU0lPTikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgT25seSBoZWFkZXIgdmVyc2lvbiAke0dsdWVTY2hlbWFSZWdpc3RyeS5IRUFERVJfVkVSU0lPTn0gaXMgc3VwcG9ydGVkLCByZWNlaXZlZCAke2hlYWRlcnZlcnNpb259YCxcbiAgICAgIClcbiAgICB9XG4gICAgaWYgKFxuICAgICAgY29tcHJlc3Npb24gIT09IEdsdWVTY2hlbWFSZWdpc3RyeS5DT01QUkVTU0lPTl9ERUZBVUxUICYmXG4gICAgICBjb21wcmVzc2lvbiAhPT0gR2x1ZVNjaGVtYVJlZ2lzdHJ5LkNPTVBSRVNTSU9OX1pMSUJcbiAgICApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgT25seSBjb21wcmVzc2lvbiB0eXBlIDAgYW5kIDUgYXJlIHN1cHBvcnRlZCwgcmVjZWl2ZWQgJHtjb21wcmVzc2lvbn1gKVxuICAgIH1cbiAgICBjb25zdCBaTElCX1VOQ09NUFJFU1NfRlVOQyA9IChidWY6IEJ1ZmZlcik6IFByb21pc2U8QnVmZmVyPiA9PiB7XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB6bGliLmluZmxhdGUoYnVmLCAoZXJyLCBkYXRhKSA9PiB7XG4gICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgcmVqZWN0KGVycilcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVzb2x2ZShkYXRhKVxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgIH0pXG4gICAgfVxuICAgIGNvbnN0IE5PX1VOQ09NUFJFU1NfRlVOQyA9IChidWY6IEJ1ZmZlcik6IFByb21pc2U8QnVmZmVyPiA9PlxuICAgICAgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgcmVzb2x2ZShidWYpXG4gICAgICB9KVxuICAgIGNvbnN0IHByb2R1Y2VyU2NoZW1hSWQgPSB1dWlkLnN0cmluZ2lmeShtZXNzYWdlLCAyKVxuICAgIGNvbnN0IHByb2R1Y2Vyc2NoZW1hID0gYXdhaXQgdGhpcy5nZXRBdnJvU2NoZW1hRm9yR2x1ZUlkKHByb2R1Y2VyU2NoZW1hSWQpXG4gICAgY29uc3QgcmVzb2x2ZXIgPSB0aGlzLmdldFJlc29sdmVyKHByb2R1Y2Vyc2NoZW1hLCBjb25zdW1lcnNjaGVtYSlcbiAgICBjb25zdCBjb250ZW50ID0gQnVmZmVyLmZyb20obWVzc2FnZS5zdWJhcnJheSgxOCkpXG4gICAgbGV0IGhhbmRsZWNvbXByZXNzaW9uID0gTk9fVU5DT01QUkVTU19GVU5DXG4gICAgaWYgKGNvbXByZXNzaW9uID09PSBHbHVlU2NoZW1hUmVnaXN0cnkuQ09NUFJFU1NJT05fWkxJQikge1xuICAgICAgaGFuZGxlY29tcHJlc3Npb24gPSBaTElCX1VOQ09NUFJFU1NfRlVOQ1xuICAgIH1cbiAgICByZXR1cm4gY29uc3VtZXJzY2hlbWEuZnJvbUJ1ZmZlcihhd2FpdCBoYW5kbGVjb21wcmVzc2lvbihjb250ZW50KSwgcmVzb2x2ZXIpXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldEF2cm9TY2hlbWFGb3JHbHVlSWQoaWQ6IHN0cmluZykge1xuICAgIGlmICh0aGlzLmF2cm9TY2hlbWFDYWNoZVtpZF0pIHJldHVybiB0aGlzLmF2cm9TY2hlbWFDYWNoZVtpZF1cbiAgICBjb25zdCBzY2hlbWFzdHJpbmcgPSAoYXdhaXQgdGhpcy5sb2FkR2x1ZVNjaGVtYShpZCkpLlNjaGVtYURlZmluaXRpb25cbiAgICBpZiAoIXNjaGVtYXN0cmluZykgdGhyb3cgbmV3IEVycm9yKCdHbHVlIHJldHVybmVkIHVuZGVmaW5lZCBzY2hlbWEgZGVmaW5pdGlvbicpXG4gICAgY29uc3Qgc2NoZW1hID0gYXZyby5UeXBlLmZvclNjaGVtYShKU09OLnBhcnNlKHNjaGVtYXN0cmluZykpXG4gICAgdGhpcy5hdnJvU2NoZW1hQ2FjaGVbaWRdID0gc2NoZW1hXG4gICAgcmV0dXJuIHNjaGVtYVxuICB9XG5cbiAgcHJpdmF0ZSBVVUlEc3RyaW5nVG9CeXRlQXJyYXkoaWQ6IHN0cmluZykge1xuICAgIGNvbnN0IGlkYXNieXRlcyA9IHV1aWQucGFyc2UoaWQpXG4gICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KGlkYXNieXRlcylcbiAgfVxuICBwcml2YXRlIGdldFJlc29sdmVyKHByb2R1Y2Vyc2NoZW1hOiBhdnJvLlR5cGUsIGNvbnN1bWVyc2NoZW1hOiBhdnJvLlR5cGUpIHtcbiAgICByZXR1cm4gY29uc3VtZXJzY2hlbWEuY3JlYXRlUmVzb2x2ZXIocHJvZHVjZXJzY2hlbWEpXG4gIH1cbiAgcHJpdmF0ZSBzdGF0aWMgaW5pdEJ5dGVCdWZmZXIodmFsdWU6IG51bWJlcikge1xuICAgIHJldHVybiBCdWZmZXIuZnJvbShbdmFsdWVdKVxuICB9XG59XG4iXX0=