@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
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 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=