UNPKG

@decaf-ts/decorator-validation

Version:
583 lines 71.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Model = exports.ModelRegistryManager = void 0; exports.bulkModelRegister = bulkModelRegister; const serialization_1 = require("./../utils/serialization.cjs"); const reflection_1 = require("@decaf-ts/reflection"); const validation_1 = require("./validation.cjs"); const hashing_1 = require("./../utils/hashing.cjs"); const constants_1 = require("./../utils/constants.cjs"); const constants_2 = require("./../validation/Validators/constants.cjs"); const constants_3 = require("./constants.cjs"); const utils_1 = require("./utils.cjs"); let modelBuilderFunction; let actingModelRegistry; /** * @description Registry manager for model constructors that enables serialization and rebuilding * @summary The ModelRegistryManager implements the ModelRegistry interface and provides * functionality for registering, retrieving, and building model instances. It maintains * a cache of model constructors indexed by name, allowing for efficient lookup and instantiation. * This class is essential for the serialization and deserialization of model objects. * * @param {function(Record<string, any>): boolean} [testFunction] - Function to test if an object is a model, defaults to {@link Model#isModel} * * @class ModelRegistryManager * @template M Type of model that can be registered, must extend Model * @implements ModelRegistry<M> * @category Model * * @example * ```typescript * // Create a model registry * const registry = new ModelRegistryManager(); * * // Register a model class * registry.register(User); * * // Retrieve a model constructor by name * const UserClass = registry.get("User"); * * // Build a model instance from a plain object * const userData = { name: "John", age: 30 }; * const user = registry.build(userData, "User"); * ``` * * @mermaid * sequenceDiagram * participant C as Client * participant R as ModelRegistryManager * participant M as Model Class * * C->>R: new ModelRegistryManager(testFunction) * C->>R: register(ModelClass) * R->>R: Store in cache * C->>R: get("ModelName") * R-->>C: ModelClass constructor * C->>R: build(data, "ModelName") * R->>R: Get constructor from cache * R->>M: new ModelClass(data) * M-->>R: Model instance * R-->>C: Model instance */ class ModelRegistryManager { constructor(testFunction = Model.isModel) { this.cache = {}; this.testFunction = testFunction; } /** * @description Registers a model constructor with the registry * @summary Adds a model constructor to the registry cache, making it available for * later retrieval and instantiation. If no name is provided, the constructor's name * property is used as the key in the registry. * * @param {ModelConstructor<M>} constructor - The model constructor to register * @param {string} [name] - Optional name to register the constructor under, defaults to constructor.name * @return {void} * @throws {Error} If the constructor is not a function */ register(constructor, name) { if (typeof constructor !== "function") throw new Error("Model registering failed. Missing Class name or constructor"); name = name || constructor.name; this.cache[name] = constructor; } /** * @summary Gets a registered Model {@link ModelConstructor} * @param {string} name */ get(name) { try { return this.cache[name]; // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { return undefined; } } /** * @param {Record<string, any>} obj * @param {string} [clazz] when provided, it will attempt to find the matching constructor * * @throws Error If clazz is not found, or obj is not a {@link Model} meaning it has no {@link ModelKeys.ANCHOR} property */ build(obj = {}, clazz) { if (!clazz && !this.testFunction(obj)) throw new Error("Provided obj is not a Model object"); const name = clazz || Model.getMetadata(obj); if (!(name in this.cache)) throw new Error(`Provided class ${name} is not a registered Model object`); return new this.cache[name](obj); } } exports.ModelRegistryManager = ModelRegistryManager; /** * @summary Bulk Registers Models * @description Useful when using bundlers that might not evaluate all the code at once * * @template M extends Model * @param {Array<Constructor<M>> | Array<{name: string, constructor: Constructor<M>}>} [models] * * @memberOf module:decorator-validation * @category Model */ function bulkModelRegister(...models) { models.forEach((m) => { const constructor = (m.constructor ? m.constructor : m); Model.register(constructor, m.name); }); } /** * @summary Abstract class representing a Validatable Model object * @description Meant to be used as a base class for all Model classes * * Model objects must: * - Have all their required properties marked with '!'; * - Have all their optional properties marked as '?': * * @param {ModelArg<Model>} model base object from which to populate properties from * * @class Model * @category Model * @abstract * @implements Validatable * @implements Serializable * * @example * class ClassName { * @required() * requiredPropertyName!: PropertyType; * * optionalPropertyName?: PropertyType; * } */ class Model { // eslint-disable-next-line @typescript-eslint/no-unused-vars constructor(arg) { } /** * @description Validates the model object against its defined validation rules * @summary Validates the object according to its decorated properties, returning any validation errors * * @param {any[]} [exceptions] - Properties in the object to be ignored for the validation. Marked as 'any' to allow for extension but expects strings * @return {ModelErrorDefinition | undefined} - Returns a ModelErrorDefinition object if validation errors exist, otherwise undefined */ hasErrors(...exceptions) { return (0, validation_1.validate)(this, ...exceptions); } /** * @description Determines if this model is equal to another object * @summary Compare object equality recursively, checking all properties unless excluded * * @param {any} obj - Object to compare to * @param {string[]} [exceptions] - Property names to be excluded from the comparison * @return {boolean} - True if objects are equal, false otherwise */ equals(obj, ...exceptions) { return (0, reflection_1.isEqual)(this, obj, ...exceptions); } /** * @description Converts the model to a serialized string representation * @summary Returns the serialized model according to the currently defined {@link Serializer} * * @return {string} - The serialized string representation of the model */ serialize() { return Model.serialize(this); } /** * @description Provides a human-readable string representation of the model * @summary Override the implementation for js's 'toString()' to provide a more useful representation * * @return {string} - A string representation of the model including its class name and JSON representation * @override */ toString() { return this.constructor.name + ": " + JSON.stringify(this, undefined, 2); } /** * @description Generates a hash string for the model object * @summary Defines a default implementation for object hash, relying on a basic implementation based on Java's string hash * * @return {string} - A hash string representing the model */ hash() { return Model.hash(this); } /** * @description Converts a serialized string back into a model instance * @summary Deserializes a Model from its string representation * * @param {string} str - The serialized string to convert back to a model * @return {any} - The deserialized model instance * @throws {Error} If it fails to parse the string, or if it fails to build the model */ static deserialize(str) { const metadata = Reflect.getMetadata(Model.key(constants_1.ModelKeys.SERIALIZATION), this.constructor); if (metadata && metadata.serializer) return serialization_1.Serialization.deserialize(str, metadata.serializer, ...(metadata.args || [])); return serialization_1.Serialization.deserialize(str); } /** * @description Copies properties from a source object to a model instance * @summary Repopulates the Object properties with the ones from the new object * * @template T * @param {T} self - The target model instance to update * @param {T | Record<string, any>} [obj] - The source object containing properties to copy * @return {T} - The updated model instance */ static fromObject(self, obj) { if (!obj) obj = {}; for (const prop of Model.getAttributes(self)) { self[prop] = obj[prop] || undefined; } return self; } /** * @description Copies and rebuilds properties from a source object to a model instance, handling nested models * @summary Repopulates the instance with properties from the new Model Object, recursively rebuilding nested models * * @template T * @param {T} self - The target model instance to update * @param {T | Record<string, any>} [obj] - The source object containing properties to copy * @return {T} - The updated model instance with rebuilt nested models * * @mermaid * sequenceDiagram * participant C as Client * participant M as Model.fromModel * participant B as Model.build * participant R as Reflection * * C->>M: fromModel(self, obj) * M->>M: Get attributes from self * loop For each property * M->>M: Copy property from obj to self * alt Property is a model * M->>M: Check if property is a model * M->>B: build(property, modelType) * B-->>M: Return built model * else Property is a complex type * M->>R: Get property decorators * R-->>M: Return decorators * M->>M: Filter type decorators * alt Property is Array/Set with list decorator * M->>M: Process each item in collection * loop For each item * M->>B: build(item, itemModelType) * B-->>M: Return built model * end * else Property is another model type * M->>B: build(property, propertyType) * B-->>M: Return built model * end * end * end * M-->>C: Return updated self */ static fromModel(self, obj) { if (!obj) obj = {}; let decorators, dec; const props = Model.getAttributes(self); for (const prop of props) { self[prop] = obj[prop] ?? undefined; if (typeof self[prop] !== "object") continue; const propM = Model.isPropertyModel(self, prop); if (propM) { try { self[prop] = Model.build(self[prop], typeof propM === "string" ? propM : undefined); } catch (e) { console.log(e); } continue; } const allDecorators = reflection_1.Reflection.getPropertyDecorators(constants_2.ValidationKeys.REFLECT, self, prop).decorators; decorators = allDecorators.filter((d) => [constants_1.ModelKeys.TYPE, constants_2.ValidationKeys.TYPE].indexOf(d.key) !== -1); if (!decorators || !decorators.length) throw new Error(`failed to find decorators for property ${prop}`); dec = decorators.pop(); const clazz = dec.props.name ? [dec.props.name] : Array.isArray(dec.props.customTypes) ? dec.props.customTypes : [dec.props.customTypes]; const reserved = Object.values(constants_3.ReservedModels).map((v) => v.toLowerCase()); clazz.forEach((c) => { if (reserved.indexOf(c.toLowerCase()) === -1) try { switch (c) { case "Array": case "Set": if (allDecorators.length) { const listDec = allDecorators.find((d) => d.key === constants_2.ValidationKeys.LIST); if (listDec) { const clazzName = listDec.props.clazz.find((t) => !constants_3.jsTypes.includes(t.toLowerCase())); if (c === "Array") self[prop] = self[prop].map((el) => { return ["object", "function"].includes(typeof el) && clazzName ? Model.build(el, clazzName) : el; }); if (c === "Set") { const s = new Set(); for (const v of self[prop]) { if (["object", "function"].includes(typeof v) && clazzName) { s.add(Model.build(v, clazzName)); } else { s.add(v); } } self[prop] = s; } } } break; default: if (self[prop]) self[prop] = Model.build(self[prop], c); } } catch (e) { console.log(e); // do nothing. we have no registry of this class } }); } return self; } /** * @description Configures the global model builder function * @summary Sets the Global {@link ModelBuilderFunction} used for building model instances * * @param {ModelBuilderFunction} [builder] - The builder function to set as the global builder * @return {void} */ static setBuilder(builder) { modelBuilderFunction = builder; } /** * @description Retrieves the currently configured global model builder function * @summary Returns the current global {@link ModelBuilderFunction} used for building model instances * * @return {ModelBuilderFunction | undefined} - The current global builder function or undefined if not set */ static getBuilder() { return modelBuilderFunction; } /** * @description Provides access to the current model registry * @summary Returns the current {@link ModelRegistryManager} instance, creating one if it doesn't exist * * @return {ModelRegistry<any>} - The current model registry, defaults to a new {@link ModelRegistryManager} if not set * @private */ static getRegistry() { if (!actingModelRegistry) actingModelRegistry = new ModelRegistryManager(); return actingModelRegistry; } /** * @description Configures the model registry to be used by the Model system * @summary Sets the current model registry to a custom implementation * * @param {BuilderRegistry<any>} modelRegistry - The new implementation of Registry to use * @return {void} */ static setRegistry(modelRegistry) { actingModelRegistry = modelRegistry; } /** * @description Registers a model constructor with the model registry * @summary Registers new model classes to make them available for serialization and deserialization * * @template T * @param {ModelConstructor<T>} constructor - The model constructor to register * @param {string} [name] - Optional name to register the constructor under, defaults to constructor.name * @return {void} * * @see ModelRegistry */ static register(constructor, name) { return Model.getRegistry().register(constructor, name); } /** * @description Retrieves a registered model constructor by name * @summary Gets a registered Model {@link ModelConstructor} from the model registry * * @template T * @param {string} name - The name of the model constructor to retrieve * @return {ModelConstructor<T> | undefined} - The model constructor if found, undefined otherwise * * @see ModelRegistry */ static get(name) { return Model.getRegistry().get(name); } /** * @description Creates a model instance from a plain object * @summary Builds a model instance using the model registry, optionally specifying the model class * * @template T * @param {Record<string, any>} obj - The source object to build the model from * @param {string} [clazz] - When provided, it will attempt to find the matching constructor by name * @return {T} - The built model instance * @throws {Error} If clazz is not found, or obj is not a {@link Model} meaning it has no {@link ModelKeys.ANCHOR} property * * @see ModelRegistry */ static build(obj = {}, clazz) { return Model.getRegistry().build(obj, clazz); } /** * @description Retrieves the model metadata from a model instance * @summary Gets the metadata associated with a model instance, typically the model class name * * @template M * @param {M} model - The model instance to get metadata from * @return {string} - The model metadata (typically the class name) */ static getMetadata(model) { return (0, utils_1.getMetadata)(model); } /** * @description Retrieves all attribute names from a model class or instance * @summary Gets all attributes defined in a model, traversing the prototype chain to include inherited attributes * * @template V * @param {Constructor<V> | V} model - The model class or instance to get attributes from * @return {string[]} - Array of attribute names defined in the model */ static getAttributes(model) { const result = []; let prototype = model instanceof Model ? Object.getPrototypeOf(model) : model.prototype; while (prototype != null) { const props = prototype[constants_1.ModelKeys.ATTRIBUTE]; if (props) { result.push(...props); } prototype = Object.getPrototypeOf(prototype); } return result; } /** * @description Compares two model instances for equality * @summary Determines if two model instances are equal by comparing their properties * * @template M * @param {M} obj1 - First model instance to compare * @param {M} obj2 - Second model instance to compare * @param {any[]} [exceptions] - Property names to exclude from comparison * @return {boolean} - True if the models are equal, false otherwise */ static equals(obj1, obj2, ...exceptions) { return (0, reflection_1.isEqual)(obj1, obj2, ...exceptions); } /** * @description Validates a model instance against its validation rules * @summary Checks if a model has validation errors, optionally ignoring specified properties * * @template M * @param {M} model - The model instance to validate * @param {string[]} [propsToIgnore] - Properties to exclude from validation * @return {ModelErrorDefinition | undefined} - Returns validation errors if any, otherwise undefined */ static hasErrors(model, ...propsToIgnore) { return (0, validation_1.validate)(model, ...propsToIgnore); } /** * @description Converts a model instance to a serialized string * @summary Serializes a model instance using the configured serializer or the default one * * @template M * @param {M} model - The model instance to serialize * @return {string} - The serialized string representation of the model */ static serialize(model) { const metadata = Reflect.getMetadata(Model.key(constants_1.ModelKeys.SERIALIZATION), model.constructor); if (metadata && metadata.serializer) return serialization_1.Serialization.serialize(this, metadata.serializer, ...(metadata.args || [])); return serialization_1.Serialization.serialize(model); } /** * @description Generates a hash string for a model instance * @summary Creates a hash representation of a model using the configured algorithm or the default one * * @template M * @param {M} model - The model instance to hash * @return {string} - The hash string representing the model */ static hash(model) { const metadata = Reflect.getMetadata(Model.key(constants_1.ModelKeys.HASHING), model.constructor); if (metadata && metadata.algorithm) return hashing_1.Hashing.hash(model, metadata.algorithm, ...(metadata.args || [])); return hashing_1.Hashing.hash(model); } /** * @description Creates a metadata key for use with the Reflection API * @summary Builds the key to store as Metadata under Reflections * * @param {string} str - The base key to concatenate with the model reflection prefix * @return {string} - The complete metadata key */ static key(str) { return (0, utils_1.getModelKey)(str); } /** * @description Determines if an object is a model instance or has model metadata * @summary Checks whether a given object is either an instance of the Model class or * has model metadata attached to it. This function is essential for serialization and * deserialization processes, as it helps identify model objects that need special handling. * It safely handles potential errors during metadata retrieval. * * @param {Record<string, any>} target - The object to check * @return {boolean} True if the object is a model instance or has model metadata, false otherwise * * @example * ```typescript * // Check if an object is a model * const user = new User({ name: "John" }); * const isUserModel = isModel(user); // true * * // Check a plain object * const plainObject = { name: "John" }; * const isPlainObjectModel = isModel(plainObject); // false * ``` */ static isModel(target) { try { return target instanceof Model || !!Model.getMetadata(target); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { return false; } } /** * @description Checks if a property of a model is itself a model or has a model type * @summary Determines whether a specific property of a model instance is either a model instance * or has a type that is registered as a model * * @template M * @param {M} target - The model instance to check * @param {string} attribute - The property name to check * @return {boolean | string | undefined} - Returns true if the property is a model instance, * the model name if the property has a model type, or undefined if not a model */ static isPropertyModel(target, attribute) { if (Model.isModel(target[attribute])) return true; const metadata = Reflect.getMetadata(constants_1.ModelKeys.TYPE, target, attribute); return Model.get(metadata.name) ? metadata.name : undefined; } } exports.Model = Model; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwvTW9kZWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBNEpBLDhDQVdDO0FBdktELGdFQUF1RDtBQWF2RCxxREFBOEU7QUFDOUUsaURBQXdDO0FBQ3hDLG9EQUEyQztBQUMzQyx3REFBK0M7QUFDL0Msd0VBQW9FO0FBQ3BFLCtDQUFzRDtBQUN0RCx1Q0FBbUQ7QUFFbkQsSUFBSSxvQkFBc0QsQ0FBQztBQUMzRCxJQUFJLG1CQUF5QyxDQUFDO0FBZ0I5Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThDRztBQUNILE1BQWEsb0JBQW9CO0lBSS9CLFlBQ0UsZUFBc0QsS0FBSyxDQUFDLE9BQU87UUFKN0QsVUFBSyxHQUF3QyxFQUFFLENBQUM7UUFNdEQsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7SUFDbkMsQ0FBQztJQUVEOzs7Ozs7Ozs7O09BVUc7SUFDSCxRQUFRLENBQUMsV0FBZ0MsRUFBRSxJQUFhO1FBQ3RELElBQUksT0FBTyxXQUFXLEtBQUssVUFBVTtZQUNuQyxNQUFNLElBQUksS0FBSyxDQUNiLDZEQUE2RCxDQUM5RCxDQUFDO1FBQ0osSUFBSSxHQUFHLElBQUksSUFBSSxXQUFXLENBQUMsSUFBSSxDQUFDO1FBQ2hDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsV0FBVyxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7O09BR0c7SUFDSCxHQUFHLENBQUMsSUFBWTtRQUNkLElBQUksQ0FBQztZQUNILE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUN4Qiw2REFBNkQ7UUFDL0QsQ0FBQztRQUFDLE9BQU8sQ0FBTSxFQUFFLENBQUM7WUFDaEIsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxN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LENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUM5RCxDQUFDO0NBQ0Y7QUExZkQsc0JBMGZDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgU2VyaWFsaXphdGlvbiB9IGZyb20gXCIuLi91dGlscy9zZXJpYWxpemF0aW9uXCI7XG5pbXBvcnQgeyBCdWlsZGVyUmVnaXN0cnkgfSBmcm9tIFwiLi4vdXRpbHMvcmVnaXN0cnlcIjtcbmltcG9ydCB7IE1vZGVsRXJyb3JEZWZpbml0aW9uIH0gZnJvbSBcIi4vTW9kZWxFcnJvckRlZmluaXRpb25cIjtcbmltcG9ydCB7XG4gIENvbXBhcmFibGUsXG4gIENvbnN0cnVjdG9yLFxuICBIYXNoYWJsZSxcbiAgTW9kZWxBcmcsXG4gIE1vZGVsQnVpbGRlckZ1bmN0aW9uLFxuICBNb2RlbENvbnN0cnVjdG9yLFxuICBTZXJpYWxpemFibGUsXG4gIFZhbGlkYXRhYmxlLFxufSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgRGVjb3JhdG9yTWV0YWRhdGEsIGlzRXF1YWwsIFJlZmxlY3Rpb24gfSBmcm9tIFwiQGRlY2FmLXRzL3JlZmxlY3Rpb25cIjtcbmltcG9ydCB7IHZhbGlkYXRlIH0gZnJvbSBcIi4vdmFsaWRhdGlvblwiO1xuaW1wb3J0IHsgSGFzaGluZyB9IGZyb20gXCIuLi91dGlscy9oYXNoaW5nXCI7XG5pbXBvcnQgeyBNb2RlbEtleXMgfSBmcm9tIFwiLi4vdXRpbHMvY29uc3RhbnRzXCI7XG5pbXBvcnQgeyBWYWxpZGF0aW9uS2V5cyB9IGZyb20gXCIuLi92YWxpZGF0aW9uL1ZhbGlkYXRvcnMvY29uc3RhbnRzXCI7XG5pbXBvcnQgeyBqc1R5cGVzLCBSZXNlcnZlZE1vZGVscyB9IGZyb20gXCIuL2NvbnN0YW50c1wiO1xuaW1wb3J0IHsgZ2V0TW9kZWxLZXksIGdldE1ldGFkYXRhIH0gZnJvbSBcIi4vdXRpbHNcIjtcblxubGV0IG1vZGVsQnVpbGRlckZ1bmN0aW9uOiBNb2RlbEJ1aWxkZXJGdW5jdGlvbiB8IHVuZGVmaW5lZDtcbmxldCBhY3RpbmdNb2RlbFJlZ2lzdHJ5OiBCdWlsZGVyUmVnaXN0cnk8YW55PjtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gUmVnaXN0cnkgdHlwZSBmb3Igc3RvcmluZyBhbmQgcmV0cmlldmluZyBtb2RlbCBjb25zdHJ1Y3RvcnNcbiAqIEBzdW1tYXJ5IFRoZSBNb2RlbFJlZ2lzdHJ5IHR5cGUgZGVmaW5lcyBhIHJlZ2lzdHJ5IGZvciBtb2RlbCBjb25zdHJ1Y3RvcnMgdGhhdCBleHRlbmRzXG4gKiB0aGUgQnVpbGRlclJlZ2lzdHJ5IGludGVyZmFjZS4gSXQgcHJvdmlkZXMgYSBzdGFuZGFyZGl6ZWQgd2F5IHRvIHJlZ2lzdGVyLCByZXRyaWV2ZSxcbiAqIGFuZCBidWlsZCBtb2RlbCBpbnN0YW5jZXMsIGVuYWJsaW5nIHRoZSBtb2RlbCBzeXN0ZW0gdG8gd29yayB3aXRoIGRpZmZlcmVudCB0eXBlcyBvZiBtb2RlbHMuXG4gKlxuICogQGludGVyZmFjZSBNb2RlbFJlZ2lzdHJ5XG4gKiBAdGVtcGxhdGUgVCBUeXBlIG9mIG1vZGVsIHRoYXQgY2FuIGJlIHJlZ2lzdGVyZWQsIG11c3QgZXh0ZW5kIE1vZGVsXG4gKiBAZXh0ZW5kcyBCdWlsZGVyUmVnaXN0cnk8VD5cbiAqIEBtZW1iZXJPZiBtb2R1bGU6ZGVjb3JhdG9yLXZhbGlkYXRpb25cbiAqIEBjYXRlZ29yeSBNb2RlbFxuICovXG5leHBvcnQgdHlwZSBNb2RlbFJlZ2lzdHJ5PFQgZXh0ZW5kcyBNb2RlbD4gPSBCdWlsZGVyUmVnaXN0cnk8VD47XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIFJlZ2lzdHJ5IG1hbmFnZXIgZm9yIG1vZGVsIGNvbnN0cnVjdG9ycyB0aGF0IGVuYWJsZXMgc2VyaWFsaXphdGlvbiBhbmQgcmVidWlsZGluZ1xuICogQHN1bW1hcnkgVGhlIE1vZGVsUmVnaXN0cnlNYW5hZ2VyIGltcGxlbWVudHMgdGhlIE1vZGVsUmVnaXN0cnkgaW50ZXJmYWNlIGFuZCBwcm92aWRlc1xuICogZnVuY3Rpb25hbGl0eSBmb3IgcmVnaXN0ZXJpbmcsIHJldHJpZXZpbmcsIGFuZCBidWlsZGluZyBtb2RlbCBpbnN0YW5jZXMuIEl0IG1haW50YWluc1xuICogYSBjYWNoZSBvZiBtb2RlbCBjb25zdHJ1Y3RvcnMgaW5kZXhlZCBieSBuYW1lLCBhbGxvd2luZyBmb3IgZWZmaWNpZW50IGxvb2t1cCBhbmQgaW5zdGFudGlhdGlvbi5cbiAqIFRoaXMgY2xhc3MgaXMgZXNzZW50aWFsIGZvciB0aGUgc2VyaWFsaXphdGlvbiBhbmQgZGVzZXJpYWxpemF0aW9uIG9mIG1vZGVsIG9iamVjdHMuXG4gKlxuICogQHBhcmFtIHtmdW5jdGlvbihSZWNvcmQ8c3RyaW5nLCBhbnk+KTogYm9vbGVhbn0gW3Rlc3RGdW5jdGlvbl0gLSBGdW5jdGlvbiB0byB0ZXN0IGlmIGFuIG9iamVjdCBpcyBhIG1vZGVsLCBkZWZhdWx0cyB0byB7QGxpbmsgTW9kZWwjaXNNb2RlbH1cbiAqXG4gKiBAY2xhc3MgTW9kZWxSZWdpc3RyeU1hbmFnZXJcbiAqIEB0ZW1wbGF0ZSBNIFR5cGUgb2YgbW9kZWwgdGhhdCBjYW4gYmUgcmVnaXN0ZXJlZCwgbXVzdCBleHRlbmQgTW9kZWxcbiAqIEBpbXBsZW1lbnRzIE1vZGVsUmVnaXN0cnk8TT5cbiAqIEBjYXRlZ29yeSBNb2RlbFxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBDcmVhdGUgYSBtb2RlbCByZWdpc3RyeVxuICogY29uc3QgcmVnaXN0cnkgPSBuZXcgTW9kZWxSZWdpc3RyeU1hbmFnZXIoKTtcbiAqXG4gKiAvLyBSZWdpc3RlciBhIG1vZGVsIGNsYXNzXG4gKiByZWdpc3RyeS5yZWdpc3RlcihVc2VyKTtcbiAqXG4gKiAvLyBSZXRyaWV2ZSBhIG1vZGVsIGNvbnN0cnVjdG9yIGJ5IG5hbWVcbiAqIGNvbnN0IFVzZXJDbGFzcyA9IHJlZ2lzdHJ5LmdldChcIlVzZXJcIik7XG4gKlxuICogLy8gQnVpbGQgYSBtb2RlbCBpbnN0YW5jZSBmcm9tIGEgcGxhaW4gb2JqZWN0XG4gKiBjb25zdCB1c2VyRGF0YSA9IHsgbmFtZTogXCJKb2huXCIsIGFnZTogMzAgfTtcbiAqIGNvbnN0IHVzZXIgPSByZWdpc3RyeS5idWlsZCh1c2VyRGF0YSwgXCJVc2VyXCIpO1xuICogYGBgXG4gKlxuICogQG1lcm1haWRcbiAqIHNlcXVlbmNlRGlhZ3JhbVxuICogICBwYXJ0aWNpcGFudCBDIGFzIENsaWVudFxuICogICBwYXJ0aWNpcGFudCBSIGFzIE1vZGVsUmVnaXN0cnlNYW5hZ2VyXG4gKiAgIHBhcnRpY2lwYW50IE0gYXMgTW9kZWwgQ2xhc3NcbiAqXG4gKiAgIEMtPj5SOiBuZXcgTW9kZWxSZWdpc3RyeU1hbmFnZXIodGVzdEZ1bmN0aW9uKVxuICogICBDLT4+UjogcmVnaXN0ZXIoTW9kZWxDbGFzcylcbiAqICAgUi0+PlI6IFN0b3JlIGluIGNhY2hlXG4gKiAgIEMtPj5SOiBnZXQoXCJNb2RlbE5hbWVcIilcbiAqICAgUi0tPj5DOiBNb2RlbENsYXNzIGNvbnN0cnVjdG9yXG4gKiAgIEMtPj5SOiBidWlsZChkYXRhLCBcIk1vZGVsTmFtZVwiKVxuICogICBSLT4+UjogR2V0IGNvbnN0cnVjdG9yIGZyb20gY2FjaGVcbiAqICAgUi0+Pk06IG5ldyBNb2RlbENsYXNzKGRhdGEpXG4gKiAgIE0tLT4+UjogTW9kZWwgaW5zdGFuY2VcbiAqICAgUi0tPj5DOiBNb2RlbCBpbnN0YW5jZVxuICovXG5leHBvcnQgY2xhc3MgTW9kZWxSZWdpc3RyeU1hbmFnZXI8TSBleHRlbmRzIE1vZGVsPiBpbXBsZW1lbnRzIE1vZGVsUmVnaXN0cnk8TT4ge1xuICBwcml2YXRlIGNhY2hlOiBSZWNvcmQ8c3RyaW5nLCBNb2RlbENvbnN0cnVjdG9yPE0+PiA9IHt9O1xuICBwcml2YXRlIHJlYWRvbmx5IHRlc3RGdW5jdGlvbjogKG9iajogb2JqZWN0KSA9PiBib29sZWFuO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHRlc3RGdW5jdGlvbjogKG9iajogUmVjb3JkPHN0cmluZywgYW55PikgPT4gYm9vbGVhbiA9IE1vZGVsLmlzTW9kZWxcbiAgKSB7XG4gICAgdGhpcy50ZXN0RnVuY3Rpb24gPSB0ZXN0RnVuY3Rpb247XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJlZ2lzdGVycyBhIG1vZGVsIGNvbnN0cnVjdG9yIHdpdGggdGhlIHJlZ2lzdHJ5XG4gICAqIEBzdW1tYXJ5IEFkZHMgYSBtb2RlbCBjb25zdHJ1Y3RvciB0byB0aGUgcmVnaXN0cnkgY2FjaGUsIG1ha2luZyBpdCBhdmFpbGFibGUgZm9yXG4gICAqIGxhdGVyIHJldHJpZXZhbCBhbmQgaW5zdGFudGlhdGlvbi4gSWYgbm8gbmFtZSBpcyBwcm92aWRlZCwgdGhlIGNvbnN0cnVjdG9yJ3MgbmFtZVxuICAgKiBwcm9wZXJ0eSBpcyB1c2VkIGFzIHRoZSBrZXkgaW4gdGhlIHJlZ2lzdHJ5LlxuICAgKlxuICAgKiBAcGFyYW0ge01vZGVsQ29uc3RydWN0b3I8TT59IGNvbnN0cnVjdG9yIC0gVGhlIG1vZGVsIGNvbnN0cnVjdG9yIHRvIHJlZ2lzdGVyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbbmFtZV0gLSBPcHRpb25hbCBuYW1lIHRvIHJlZ2lzdGVyIHRoZSBjb25zdHJ1Y3RvciB1bmRlciwgZGVmYXVsdHMgdG8gY29uc3RydWN0b3IubmFtZVxuICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgKiBAdGhyb3dzIHtFcnJvcn0gSWYgdGhlIGNvbnN0cnVjdG9yIGlzIG5vdCBhIGZ1bmN0aW9uXG4gICAqL1xuICByZWdpc3Rlcihjb25zdHJ1Y3RvcjogTW9kZWxDb25zdHJ1Y3RvcjxNPiwgbmFtZT86IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0eXBlb2YgY29uc3RydWN0b3IgIT09IFwiZnVuY3Rpb25cIilcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJNb2RlbCByZWdpc3RlcmluZyBmYWlsZWQuIE1pc3NpbmcgQ2xhc3MgbmFtZSBvciBjb25zdHJ1Y3RvclwiXG4gICAgICApO1xuICAgIG5hbWUgPSBuYW1lIHx8IGNvbnN0cnVjdG9yLm5hbWU7XG4gICAgdGhpcy5jYWNoZVtuYW1lXSA9IGNvbnN0cnVjdG9yO1xuICB9XG5cbiAgLyoqXG4gICAqIEBzdW1tYXJ5IEdldHMgYSByZWdpc3RlcmVkIE1vZGVsIHtAbGluayBNb2RlbENvbnN0cnVjdG9yfVxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgKi9cbiAgZ2V0KG5hbWU6IHN0cmluZyk6IE1vZGVsQ29uc3RydWN0b3I8TT4gfCB1bmRlZmluZWQge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gdGhpcy5jYWNoZVtuYW1lXTtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW51c2VkLXZhcnNcbiAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgYW55Pn0gb2JqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbY2xhenpdIHdoZW4gcHJvdmlkZWQsIGl0IHdpbGwgYXR0ZW1wdCB0byBmaW5kIHRoZSBtYXRjaGluZyBjb25zdHJ1Y3RvclxuICAgKlxuICAgKiBAdGhyb3dzIEVycm9yIElmIGNsYXp6IGlzIG5vdCBmb3VuZCwgb3Igb2JqIGlzIG5vdCBhIHtAbGluayBNb2RlbH0gbWVhbmluZyBpdCBoYXMgbm8ge0BsaW5rIE1vZGVsS2V5cy5BTkNIT1J9IHByb3BlcnR5XG4gICAqL1xuICBidWlsZChvYmo6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fSwgY2xheno/OiBzdHJpbmcpOiBNIHtcbiAgICBpZiAoIWNsYXp6ICYmICF0aGlzLnRlc3RGdW5jdGlvbihvYmopKVxuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiUHJvdmlkZWQgb2JqIGlzIG5vdCBhIE1vZGVsIG9iamVjdFwiKTtcbiAgICBjb25zdCBuYW1lID0gY2xhenogfHwgTW9kZWwuZ2V0TWV0YWRhdGEob2JqIGFzIGFueSk7XG4gICAgaWYgKCEobmFtZSBpbiB0aGlzLmNhY2hlKSlcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYFByb3ZpZGVkIGNsYXNzICR7bmFtZX0gaXMgbm90IGEgcmVnaXN0ZXJlZCBNb2RlbCBvYmplY3RgXG4gICAgICApO1xuICAgIHJldHVybiBuZXcgdGhpcy5jYWNoZVtuYW1lXShvYmopO1xuICB9XG59XG5cbi8qKlxuICogQHN1bW1hcnkgQnVsayBSZWdpc3RlcnMgTW9kZWxzXG4gKiBAZGVzY3JpcHRpb24gVXNlZnVsIHdoZW4gdXNpbmcgYnVuZGxlcnMgdGhhdCBtaWdodCBub3QgZXZhbHVhdGUgYWxsIHRoZSBjb2RlIGF0IG9uY2VcbiAqXG4gKiBAdGVtcGxhdGUgTSBleHRlbmRzIE1vZGVsXG4gKiBAcGFyYW0ge0FycmF5PENvbnN0cnVjdG9yPE0+PiB8IEFycmF5PHtuYW1lOiBzdHJpbmcsIGNvbnN0cnVjdG9yOiBDb25zdHJ1Y3RvcjxNPn0+fSBbbW9kZWxzXVxuICpcbiAqIEBtZW1iZXJPZiBtb2R1bGU6ZGVjb3JhdG9yLXZhbGlkYXRpb25cbiAqIEBjYXRlZ29yeSBNb2RlbFxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVsa01vZGVsUmVnaXN0ZXI8TSBleHRlbmRzIE1vZGVsPihcbiAgLi4ubW9kZWxzOiAoQ29uc3RydWN0b3I8TT4gfCB7IG5hbWU6IHN0cmluZzsgY29uc3RydWN0b3I6IENvbnN0cnVjdG9yPE0+IH0pW11cbikge1xuICBtb2RlbHMuZm9yRWFjaChcbiAgICAobTogQ29uc3RydWN0b3I8TT4gfCB7IG5hbWU6IHN0cmluZzsgY29uc3RydWN0b3I6IENvbnN0cnVjdG9yPE0+IH0pID0+IHtcbiAgICAgIGNvbnN0IGNvbnN0cnVjdG9yOiBDb25zdHJ1Y3RvcjxNPiA9IChcbiAgICAgICAgbS5jb25zdHJ1Y3RvciA/IG0uY29uc3RydWN0b3IgOiBtXG4gICAgICApIGFzIENvbnN0cnVjdG9yPE0+O1xuICAgICAgTW9kZWwucmVnaXN0ZXIoY29uc3RydWN0b3IsIChtIGFzIENvbnN0cnVjdG9yPE0+KS5uYW1lKTtcbiAgICB9XG4gICk7XG59XG5cbi8qKlxuICogQHN1bW1hcnkgQWJzdHJhY3QgY2xhc3MgcmVwcmVzZW50aW5nIGEgVmFsaWRhdGFibGUgTW9kZWwgb2JqZWN0XG4gKiBAZGVzY3JpcHRpb24gTWVhbnQgdG8gYmUgdXNlZCBhcyBhIGJhc2UgY2xhc3MgZm9yIGFsbCBNb2RlbCBjbGFzc2VzXG4gKlxuICogTW9kZWwgb2JqZWN0cyBtdXN0OlxuICogIC0gSGF2ZSBhbGwgdGhlaXIgcmVxdWlyZWQgcHJvcGVydGllcyBtYXJrZWQgd2l0aCAnISc7XG4gKiAgLSBIYXZlIGFsbCB0aGVpciBvcHRpb25hbCBwcm9wZXJ0aWVzIG1hcmtlZCBhcyAnPyc6XG4gKlxuICogQHBhcmFtIHtNb2RlbEFyZzxNb2RlbD59IG1vZGVsIGJhc2Ugb2JqZWN0IGZyb20gd2hpY2ggdG8gcG9wdWxhdGUgcHJvcGVydGllcyBmcm9tXG4gKlxuICogQGNsYXNzIE1vZGVsXG4gKiBAY2F0ZWdvcnkgTW9kZWxcbiAqIEBhYnN0cmFjdFxuICogQGltcGxlbWVudHMgVmFsaWRhdGFibGVcbiAqIEBpbXBsZW1lbnRzIFNlcmlhbGl6YWJsZVxuICpcbiAqIEBleGFtcGxlXG4gKiAgICAgIGNsYXNzIENsYXNzTmFtZSB7XG4gKiAgICAgICAgICBAcmVxdWlyZWQoKVxuICogICAgICAgICAgcmVxdWlyZWRQcm9wZXJ0eU5hbWUhOiBQcm9wZXJ0eVR5cGU7XG4gKlxuICogICAgICAgICAgb3B0aW9uYWxQcm9wZXJ0eU5hbWU/OiBQcm9wZXJ0eVR5cGU7XG4gKiAgICAgIH1cbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIE1vZGVsXG4gIGltcGxlbWVudHMgVmFsaWRhdGFibGUsIFNlcmlhbGl6YWJsZSwgSGFzaGFibGUsIENvbXBhcmFibGU8TW9kZWw+XG57XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW51c2VkLXZhcnNcbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGFyZz86IE1vZGVsQXJnPE1vZGVsPikge31cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFZhbGlkYXRlcyB0aGUgbW9kZWwgb2JqZWN0IGFnYWluc3QgaXRzIGRlZmluZWQgdmFsaWRhdGlvbiBydWxlc1xuICAgKiBAc3VtbWFyeSBWYWxpZGF0ZXMgdGhlIG9iamVjdCBhY2NvcmRpbmcgdG8gaXRzIGRlY29yYXRlZCBwcm9wZXJ0aWVzLCByZXR1cm5pbmcgYW55IHZhbGlkYXRpb24gZXJyb3JzXG4gICAqXG4gICAqIEBwYXJhbSB7YW55W119IFtleGNlcHRpb25zXSAtIFByb3BlcnRpZXMgaW4gdGhlIG9iamVjdCB0byBiZSBpZ25vcmVkIGZvciB0aGUgdmFsaWRhdGlvbi4gTWFya2VkIGFzICdhbnknIHRvIGFsbG93IGZvciBleHRlbnNpb24gYnV0IGV4cGVjdHMgc3RyaW5nc1xuICAgKiBAcmV0dXJuIHtNb2RlbEVycm9yRGVmaW5pdGlvbiB8IHVuZGVmaW5lZH0gLSBSZXR1cm5zIGEgTW9kZWxFcnJvckRlZmluaXRpb24gb2JqZWN0IGlmIHZhbGlkYXRpb24gZXJyb3JzIGV4aXN0LCBvdGhlcndpc2UgdW5kZWZpbmVkXG4gICAqL1xuICBwdWJsaWMgaGFzRXJyb3JzKC4uLmV4Y2VwdGlvbnM6IGFueVtdKTogTW9kZWxFcnJvckRlZmluaXRpb24gfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB2YWxpZGF0ZSh0aGlzLCAuLi5leGNlcHRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRGV0ZXJtaW5lcyBpZiB0aGlzIG1vZGVsIGlzIGVxdWFsIHRvIGFub3RoZXIgb2JqZWN0XG4gICAqIEBzdW1tYXJ5IENvbXBhcmUgb2JqZWN0IGVxdWFsaXR5IHJlY3Vyc2l2ZWx5LCBjaGVja2luZyBhbGwgcHJvcGVydGllcyB1bmxlc3MgZXhjbHVkZWRcbiAgICpcbiAgICogQHBhcmFtIHthbnl9IG9iaiAtIE9iamVjdCB0byBjb21wYXJlIHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtleGNlcHRpb25zXSAtIFByb3BlcnR5IG5hbWVzIHRvIGJlIGV4Y2x1ZGVkIGZyb20gdGhlIGNvbXBhcmlzb25cbiAgICogQHJldHVybiB7Ym9vbGVhbn0gLSBUcnVlIGlmIG9iamVjdHMgYXJlIGVxdWFsLCBmYWxzZSBvdGhlcndpc2VcbiAgICovXG4gIHB1YmxpYyBlcXVhbHMob2JqOiBhbnksIC4uLmV4Y2VwdGlvbnM6IHN0cmluZ1tdKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzRXF1YWwodGhpcywgb2JqLCAuLi5leGNlcHRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ29udmVydHMgdGhlIG1vZGVsIHRvIGEgc2VyaWFsaXplZCBzdHJpbmcgcmVwcmVzZW50YXRpb25cbiAgICogQHN1bW1hcnkgUmV0dXJucyB0aGUgc2VyaWFsaXplZCBtb2RlbCBhY2NvcmRpbmcgdG8gdGhlIGN1cnJlbnRseSBkZWZpbmVkIHtAbGluayBTZXJpYWxpemVyfVxuICAgKiBcbiAgICogQHJldHVybiB7c3RyaW5nfSAtIFRoZSBzZXJpYWxpemVkIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgbW9kZWxcbiAgICovXG4gIHNlcmlhbGl6ZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiBNb2RlbC5zZXJpYWxpemUodGhpcyk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFByb3ZpZGVzIGEgaHVtYW4tcmVhZGFibGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBtb2RlbFxuICAgKiBAc3VtbWFyeSBPdmVycmlkZSB0aGUgaW1wbGVtZW50YXRpb24gZm9yIGpzJ3MgJ3RvU3RyaW5nKCknIHRvIHByb3ZpZGUgYSBtb3JlIHVzZWZ1bCByZXByZXNlbnRhdGlvblxuICAgKiBcbiAgICogQHJldHVybiB7c3RyaW5nfSAtIEEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBtb2RlbCBpbmNsdWRpbmcgaXRzIGNsYXNzIG5hbWUgYW5kIEpTT04gcmVwcmVzZW50YXRpb25cbiAgICogQG92ZXJyaWRlXG4gICAqL1xuICBwdWJsaWMgdG9TdHJpbmcoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5jb25zdHJ1Y3Rvci5uYW1lICsgXCI6IFwiICsgSlNPTi5zdHJpbmdpZnkodGhpcywgdW5kZWZpbmVkLCAyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gR2VuZXJhdGVzIGEgaGFzaCBzdHJpbmcgZm9yIHRoZSBtb2RlbCBvYmplY3RcbiAgICogQHN1bW1hcnkgRGVmaW5lcyBhIGRlZmF1bHQgaW1wbGVtZW50YXRpb24gZm9yIG9iamVjdCBoYXNoLCByZWx5aW5nIG9uIGEgYmFzaWMgaW1wbGVtZW50YXRpb24gYmFzZWQgb24gSmF2YSdzIHN0cmluZyBoYXNoXG4gICAqIFxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IC0gQSBoYXNoIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG1vZGVsXG4gICAqL1xuICBwdWJsaWMgaGFzaCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBNb2RlbC5oYXNoKHRoaXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDb252ZXJ0cyBhIHNlcmlhbGl6ZWQgc3RyaW5nIGJhY2sgaW50byBhIG1vZGVsIGluc3RhbmNlXG4gICAqIEBzdW1tYXJ5IERlc2VyaWFsaXplcyBhIE1vZGVsIGZyb20gaXRzIHN0cmluZyByZXByZXNlbnRhdGlvblxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3RyIC0gVGhlIHNlcmlhbGl6ZWQgc3RyaW5nIHRvIGNvbnZlcnQgYmFjayB0byBhIG1vZGVsXG4gICAqIEByZXR1cm4ge2FueX0gLSBUaGUgZGVzZXJpYWxpemVkIG1vZGVsIGluc3RhbmNlXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBJZiBpdCBmYWlscyB0byBwYXJzZSB0aGUgc3RyaW5nLCBvciBpZiBpdCBmYWlscyB0byBidWlsZCB0aGUgbW9kZWxcbiAgICovXG4gIHN0YXRpYyBkZXNlcmlhbGl6ZShzdHI6IHN0cmluZykge1xuICAgIGNvbnN0IG1ldGFkYXRhID0