UNPKG

@decaf-ts/decorator-validation

Version:
386 lines 56.3 kB
import { ModelErrorDefinition } from "./ModelErrorDefinition.js"; import { ModelKeys } from "./../utils/constants.js"; import { Model } from "./Model.js"; import { Validation } from "./../validation/Validation.js"; import { ValidationKeys } from "./../validation/Validators/constants.js"; import { PathProxyEngine } from "./../utils/PathProxy.js"; import { ASYNC_META_KEY, VALIDATION_PARENT_KEY } from "./../constants/index.js"; import { Reflection } from "@decaf-ts/reflection"; import { toConditionalPromise } from "./utils.js"; /** * Retrieves the validation metadata decorators associated with a specific property of a model, * using the reflective metadata key. * * @param model - The model instance or class containing the decorated property. * @param {string} prop - The name of the property whose decorators should be retrieved. * @param {string} reflectKey - The metadata key used to retrieve the decorators. * Defaults to `ValidationKeys.REFLECT`. * * @returns The validation decorators applied to the property */ export function getValidationDecorators(model, prop, reflectKey = ValidationKeys.REFLECT) { return Reflection.getPropertyDecorators(reflectKey, model, prop); } /** * @description * Retrieves all validatable property decorators from a given model, excluding specified properties. * * @summary * Iterates through the own enumerable properties of a model instance, filtering out any properties * listed in the `propsToIgnore` array. For each remaining property, it checks whether validation * decorators are present using `getValidationDecorators`, and if so, collects them in the result array. * * @template M - A generic parameter extending the `Model` class, representing the model type being inspected. * * @param {M} model - An instance of a class extending `Model` from which validatable properties will be extracted. * @param {string[]} propsToIgnore - An array of property names that should be excluded from validation inspection. * * @return {ValidationPropertyDecoratorDefinition[]} An array of validation decorator definitions * associated with the model's properties, excluding those listed in `propsToIgnore`. * * @function getValidatableProperties */ export function getValidatableProperties(model, propsToIgnore) { const decoratedProperties = []; for (const prop in model) { if (Object.prototype.hasOwnProperty.call(model, prop) && !propsToIgnore.includes(prop)) { const dec = getValidationDecorators(model, prop); if (dec) decoratedProperties.push(dec); } } return decoratedProperties; } /** * Safely sets temporary metadata on an object */ function setTemporaryContext(target, key, value) { if (!Object.hasOwnProperty.call(target, key)) target[key] = value; } /** * Safely removes temporary metadata from an object */ function cleanupTemporaryContext(target, key) { if (Object.hasOwnProperty.call(target, key)) delete target[key]; } /** * Executes validation with temporary context and returns the validation result * * @param nestedModel - The instance to validate * @param parentModel - Reference to a parent object for nested validation * @param isAsync - Whether to perform async validation * @returns Validation result from hasErrors() */ function getNestedValidationErrors(nestedModel, parentModel, isAsync) { // Set temporary context for nested models if (parentModel) { setTemporaryContext(nestedModel, VALIDATION_PARENT_KEY, parentModel); } setTemporaryContext(nestedModel, ASYNC_META_KEY, !!isAsync); const errs = nestedModel.hasErrors(); cleanupTemporaryContext(nestedModel, VALIDATION_PARENT_KEY); cleanupTemporaryContext(nestedModel, ASYNC_META_KEY); return errs; } export function validateChildValue(childValue, parentModel, allowedTypes, async) { let err = undefined; let atLeastOneMatched = false; for (const allowedType of allowedTypes) { const Constr = Model.get(allowedType); if (!Constr) { err = new ModelErrorDefinition({ [ValidationKeys.TYPE]: `Unable to verify type consistency, missing model registry for ${allowedType}`, }); } if (childValue instanceof Constr) { atLeastOneMatched = true; err = getNestedValidationErrors(childValue, parentModel, async); break; } } if (atLeastOneMatched) return err; return (err || new ModelErrorDefinition({ [ValidationKeys.TYPE]: `Value must be an instance of one of the expected types: ${allowedTypes.join(", ")}`, })); } export function validateDecorator(model, value, decorator, async) { const validator = Validation.get(decorator.key); if (!validator) { throw new Error(`Missing validator for ${decorator.key}`); } // skip async decorators if validateDecorators is called synchronously (async = false) if (!async && decorator.props.async) return undefined; const decoratorProps = decorator.key === ModelKeys.TYPE ? [decorator.props] : decorator.props || {}; const context = PathProxyEngine.create(model, { ignoreUndefined: true, ignoreNull: true, }); const maybeAsyncErrors = validator.hasErrors(value, decoratorProps, context); return toConditionalPromise(maybeAsyncErrors, async); } /** * @description * Executes validation logic for a set of decorators applied to a model's property, handling both * synchronous and asynchronous validations, including support for nested validations and lists. * * @summary * Iterates over an array of decorator metadata objects and applies each validation rule to the * provided value. For list decorators (`ValidationKeys.LIST`), it performs element-wise validation, * supporting nested model validation and type checks. If the `async` flag is set, asynchronous * validation is supported using `Promise.all`. The result is a record mapping validation keys to * error messages, or `undefined` if no errors are found. * * @template M - A type parameter extending `Model`, representing the model type being validated. * @template Async - A boolean indicating whether validation should be performed asynchronously. * * @param {M} model - The model instance that the validation is associated with. * @param {any} value - The value to be validated against the provided decorators. * @param {DecoratorMetadataAsync[]} decorators - An array of metadata objects representing validation decorators. * @param {Async} [async] - Optional flag indicating whether validation should be performed asynchronously. * * @return {ConditionalAsync<Async, Record<string, string>> | undefined} * Returns either a record of validation errors (keyed by the decorator key) or `undefined` if no errors are found. * If `async` is true, the return value is a Promise resolving to the same structure. * * @function validateDecorators */ export function validateDecorators(model, value, decorators, async) { const result = {}; for (const decorator of decorators) { // skip async decorators if validateDecorators is called synchronously (async = false) if (!async && decorator.props.async) continue; let validationErrors = validateDecorator(model, value, decorator, async); /* If the decorator is a list, each element must be checked. When 'async' is true, the 'err' will always be a pending promise initially, so the '!err' check will evaluate to false (even if the promise later resolves with no errors) */ if (decorator.key === ValidationKeys.LIST && (!validationErrors || async)) { const values = value instanceof Set ? [...value] : value; if (values && values.length > 0) { const types = (decorator.props.class || decorator.props.clazz || decorator.props.customTypes); const allowedTypes = [types].flat().map((t) => String(t).toLowerCase()); // const reserved = Object.values(ReservedModels).map((v) => v.toLowerCase()) as string[]; const errs = values.map((childValue) => { // if (Model.isModel(v) && !reserved.includes(v) { if (Model.isModel(childValue)) { return validateChildValue(childValue, model, [types].flat(), !!async); // return getNestedValidationErrors(childValue, model, async); } return allowedTypes.includes(typeof childValue) ? undefined : "Value has no validatable type"; }); if (async) { validationErrors = Promise.all(errs).then((result) => { const allEmpty = result.every((r) => !r); return allEmpty ? undefined : result; }); } else { const allEmpty = errs.every((r) => !r); validationErrors = errs.length > 0 && !allEmpty ? errs : undefined; } } } if (validationErrors) result[decorator.key] = validationErrors; } if (!async) return Object.keys(result).length > 0 ? result : undefined; const keys = Object.keys(result); const promises = Object.values(result); return Promise.all(promises).then((resolvedValues) => { const res = {}; for (let i = 0; i < resolvedValues.length; i++) { const val = resolvedValues[i]; if (val !== undefined) { res[keys[i]] = val; } } return Object.keys(res).length > 0 ? res : undefined; }); } /** * @function validate * @template M * @template Async * @memberOf module:decorator-validation * @category Model * * @description * Validates the properties of a {@link Model} instance using registered decorators. * Supports both synchronous and asynchronous validation flows, depending on the `async` flag. * * @summary * This function inspects a given model object, identifies decorated properties that require validation, * and applies the corresponding validation rules. It also supports nested model validation and gracefully * merges any validation errors. For collections (Array/Set), it enforces the presence of the `@list` decorator * and checks the type of elements. If a property is a nested model, it will call `hasErrors` on it and flatten * the nested error keys using dot notation. * * @param {M} model - The model instance to be validated. Must extend from {@link Model}. * @param {Async} [async] - A flag indicating whether validation should be asynchronous. * @param {...string} propsToIgnore - A variadic list of property names that should be skipped during validation. * * @returns {ConditionalAsync<Async, ModelErrorDefinition | undefined>} * Returns either a {@link ModelErrorDefinition} containing validation errors, * or `undefined` if no errors are found. When `async` is `true`, returns a Promise. * * @see {@link Model} * @see {@link ModelErrorDefinition} * @see {@link validateDecorators} * @see {@link getValidatableProperties} * * @mermaid * sequenceDiagram * participant Caller * participant validate * participant getValidatableProperties * participant validateDecorators * participant ModelInstance * Caller->>validate: call with obj, async, propsToIgnore * validate->>getValidatableProperties: retrieve decorated props * loop for each property * validate->>validateDecorators: validate using decorators * alt is nested model * validate->>ModelInstance: call hasErrors() * end * end * alt async * validate->>validate: Promise.allSettled for errors * end * validate-->>Caller: return ModelErrorDefinition | undefined */ export function validate(model, async, ...propsToIgnore) { const decoratedProperties = getValidatableProperties(model, propsToIgnore); const result = {}; const nestedErrors = {}; for (const { prop, decorators } of decoratedProperties) { const propKey = String(prop); let propValue = model[prop]; if (!decorators?.length) continue; // Get the default type validator const designTypeDec = decorators.find((d) => { return [ModelKeys.TYPE, ValidationKeys.TYPE].includes(d.key); }); if (!designTypeDec) continue; const designType = designTypeDec.props.name; // Handle array or Set types and enforce the presence of @list decorator if ([Array.name, Set.name].includes(designType)) { if (!decorators.some((d) => d.key === ValidationKeys.LIST)) { result[propKey] = { [ValidationKeys.TYPE]: `Array or Set property '${propKey}' requires a @list decorator`, }; continue; } if (propValue && !(Array.isArray(propValue) || propValue instanceof Set)) { result[propKey] = { [ValidationKeys.TYPE]: `Property '${String(prop)}' must be either an Array or a Set`, }; continue; } // Remove design:type decorator, since @list decorator already ensures type for (let i = decorators.length - 1; i >= 0; i--) { if (decorators[i].key === ModelKeys.TYPE) { decorators.splice(i, 1); } } propValue = propValue instanceof Set ? [...propValue] : propValue; } const propErrors = validateDecorators(model, propValue, decorators, async) || {}; // Check for nested properties. // To prevent unnecessary processing, "propValue" must be defined and validatable // let nestedErrors: Record<string, any> = {}; const isConstr = Model.isPropertyModel(model, propKey); const hasPropValue = propValue !== null && propValue !== undefined; if (isConstr && hasPropValue) { const instance = propValue; const isInvalidModel = typeof instance !== "object" || typeof instance.hasErrors !== "function"; if (isInvalidModel) { // propErrors[ValidationKeys.TYPE] = "Model should be validatable but it's not."; console.warn("Model should be validatable but it's not."); } else { const Constr = Model.get(designType); // Ensure instance is of the expected model class. if (!Constr || !(instance instanceof Constr)) { propErrors[ValidationKeys.TYPE] = !Constr ? `Unable to verify type consistency, missing model registry for ${designType} on prop ${propKey}` : `Value must be an instance of ${Constr.name}`; } else { nestedErrors[propKey] = getNestedValidationErrors(instance, model, async); } } } // Add to the result if we have any errors // Async mode returns a Promise that resolves to undefined when no errors exist if (Object.keys(propErrors).length > 0 || async) result[propKey] = propErrors; // Then merge any nested errors if (!async) { Object.entries(nestedErrors[propKey] || {}).forEach(([key, error]) => { if (error !== undefined) { result[`${propKey}.${key}`] = error; } }); } } // Synchronous return if (!async) { return (Object.keys(result).length > 0 ? new ModelErrorDefinition(result) : undefined); } const merged = result; // TODO: apply filtering const keys = Object.keys(merged); const promises = Object.values(merged); return Promise.allSettled(promises).then(async (results) => { const result = {}; for (const [parentProp, nestedErrPromise] of Object.entries(nestedErrors)) { const nestedPropDecErrors = (await nestedErrPromise); if (nestedPropDecErrors) Object.entries(nestedPropDecErrors).forEach(([nestedProp, nestedPropDecError]) => { if (nestedPropDecError !== undefined) { const nestedKey = [parentProp, nestedProp].join("."); result[nestedKey] = nestedPropDecError; } }); } for (let i = 0; i < results.length; i++) { const key = keys[i]; const res = results[i]; if (res.status === "fulfilled" && res.value !== undefined) { result[key] = res.value; } else if (res.status === "rejected") { result[key] = res.reason instanceof Error ? res.reason.message : String(res.reason || "Validation failed"); } } return Object.keys(result).length > 0 ? new ModelErrorDefinition(result) : undefined; }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tb2RlbC92YWxpZGF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxrQ0FBK0I7QUFDOUQsT0FBTyxFQUFFLFNBQVMsRUFBRSxnQ0FBMkI7QUFDL0MsT0FBTyxFQUFFLEtBQUssRUFBRSxtQkFBZ0I7QUFDaEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxzQ0FBaUM7QUFDdEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxnREFBMkM7QUFNcEUsT0FBTyxFQUFFLGVBQWUsRUFBRSxnQ0FBMkI7QUFDckQsT0FBTyxFQUFFLGNBQWMsRUFBRSxxQkFBcUIsRUFBRSxnQ0FBcUI7QUFFckUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxtQkFBZ0I7QUFFL0M7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sVUFBVSx1QkFBdUIsQ0FDckMsS0FBMEIsRUFDMUIsSUFBWSxFQUNaLGFBQXFCLGNBQWMsQ0FBQyxPQUFPO0lBRTNDLE9BQU8sVUFBVSxDQUFDLHFCQUFxQixDQUNyQyxVQUFVLEVBQ1YsS0FBSyxFQUNMLElBQUksQ0FDK0MsQ0FBQztBQUN4RCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNILE1BQU0sVUFBVSx3QkFBd0IsQ0FDdEMsS0FBUSxFQUNSLGFBQXVCO0lBRXZCLE1BQU0sbUJBQW1CLEdBQTRDLEVBQUUsQ0FBQztJQUV4RSxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQ3pCLElBQ0UsTUFBTSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUM7WUFDakQsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUM3QixDQUFDO1lBQ0QsTUFBTSxHQUFH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QyxNQUFNLEtBQUssV0FBVyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ3pELE1BQWMsQ0FBQyxHQUFHLENBQUMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDO1lBQ25DLENBQUM7aUJBQU0sSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUNwQyxNQUFjLENBQUMsR0FBRyxDQUFDO29CQUNsQixHQUFHLENBQUMsTUFBTSxZQUFZLEtBQUs7d0JBQ3pCLENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE9BQU87d0JBQ3BCLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sSUFBSSxtQkFBbUIsQ0FBQyxDQUFDO1lBQ2xELENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDO1lBQ25DLENBQUMsQ0FBQyxJQUFJLG9CQUFvQixDQUFDLE1BQU0sQ0FBQztZQUNsQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQ2hCLENBQUMsQ0FBUSxDQUFDO0FBQ1osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE1vZGVsRXJyb3JEZWZpbml0aW9uIH0gZnJvbSBcIi4vTW9kZWxFcnJvckRlZmluaXRpb25cIjtcbmltcG9ydCB7IE1vZGVsS2V5cyB9IGZyb20gXCIuLi91dGlscy9jb25zdGFudHNcIjtcbmltcG9ydCB7IE1vZGVsIH0gZnJvbSBcIi4vTW9kZWxcIjtcbmltcG9ydCB7IFZhbGlkYXRpb24gfSBmcm9tIFwiLi4vdmFsaWRhdGlvbi9WYWxpZGF0aW9uXCI7XG5pbXBvcnQgeyBWYWxpZGF0aW9uS2V5cyB9IGZyb20gXCIuLi92YWxpZGF0aW9uL1ZhbGlkYXRvcnMvY29uc3RhbnRzXCI7XG5pbXBvcnQge1xuICBNb2RlbEVycm9ycyxcbiAgVmFsaWRhdGlvblByb3BlcnR5RGVjb3JhdG9yRGVmaW5pdGlvbixcbiAgVmFsaWRhdG9yT3B0aW9ucyxcbn0gZnJvbSBcIi4uL3ZhbGlkYXRpb25cIjtcbmltcG9ydCB7IFBhdGhQcm94eUVuZ2luZSB9IGZyb20gXCIuLi91dGlscy9QYXRoUHJveHlcIjtcbmltcG9ydCB7IEFTWU5DX01FVEFfS0VZLCBWQUxJREFUSU9OX1BBUkVOVF9LRVkgfSBmcm9tIFwiLi4vY29uc3RhbnRzXCI7XG5pbXBvcnQgeyBDb25kaXRpb25hbEFzeW5jLCBEZWNvcmF0b3JNZXRhZGF0YUFzeW5jIH0gZnJvbSBcIi4uL3R5cGVzXCI7XG5pbXBvcnQgeyBSZWZsZWN0aW9uIH0gZnJvbSBcIkBkZWNhZi10cy9yZWZsZWN0aW9uXCI7XG5pbXBvcnQgeyB0b0NvbmRpdGlvbmFsUHJvbWlzZSB9IGZyb20gXCIuL3V0aWxzXCI7XG5cbi8qKlxuICogUmV0cmlldmVzIHRoZSB2YWxpZGF0aW9uIG1ldGFkYXRhIGRlY29yYXRvcnMgYXNzb2NpYXRlZCB3aXRoIGEgc3BlY2lmaWMgcHJvcGVydHkgb2YgYSBtb2RlbCxcbiAqIHVzaW5nIHRoZSByZWZsZWN0aXZlIG1ldGFkYXRhIGtleS5cbiAqXG4gKiBAcGFyYW0gbW9kZWwgLSBUaGUgbW9kZWwgaW5zdGFuY2Ugb3IgY2xhc3MgY29udGFpbmluZyB0aGUgZGVjb3JhdGVkIHByb3BlcnR5LlxuICogQHBhcmFtIHtzdHJpbmd9IHByb3AgLSBUaGUgbmFtZSBvZiB0aGUgcHJvcGVydHkgd2hvc2UgZGVjb3JhdG9ycyBzaG91bGQgYmUgcmV0cmlldmVkLlxuICogQHBhcmFtIHtzdHJpbmd9IHJlZmxlY3RLZXkgLSBUaGUgbWV0YWRhdGEga2V5IHVzZWQgdG8gcmV0cmlldmUgdGhlIGRlY29yYXRvcnMuXG4gKiAgICAgICAgICAgICAgICAgICAgIERlZmF1bHRzIHRvIGBWYWxpZGF0aW9uS2V5cy5SRUZMRUNUYC5cbiAqXG4gKiBAcmV0dXJucyBUaGUgdmFsaWRhdGlvbiBkZWNvcmF0b3JzIGFwcGxpZWQgdG8gdGhlIHByb3BlcnR5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRWYWxpZGF0aW9uRGVjb3JhdG9ycyhcbiAgbW9kZWw6IFJlY29yZDxzdHJpbmcsIGFueT4sXG4gIHByb3A6IHN0cmluZyxcbiAgcmVmbGVjdEtleTogc3RyaW5nID0gVmFsaWRhdGlvbktleXMuUkVGTEVDVFxuKTogVmFsaWRhdGlvblByb3BlcnR5RGVjb3JhdG9yRGVmaW5pdGlvbiB7XG4gIHJldHVybiBSZWZsZWN0aW9uLmdldFByb3BlcnR5RGVjb3JhdG9ycyhcbiAgICByZWZsZWN0S2V5LFxuICAgIG1vZGVsLFxuICAgIHByb3BcbiAgKSBhcyB1bmtub3duIGFzIFZhbGlkYXRpb25Qcm9wZXJ0eURlY29yYXRvckRlZmluaXRpb247XG59XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uXG4gKiBSZXRyaWV2ZXMgYWxsIHZhbGlkYXRhYmxlIHByb3BlcnR5IGRlY29yYXRvcnMgZnJvbSBhIGdpdmVuIG1vZGVsLCBleGNsdWRpbmcgc3BlY2lmaWVkIHByb3BlcnRpZXMuXG4gKlxuICogQHN1bW1hcnlcbiAqIEl0ZXJhdGVzIHRocm91Z2ggdGhlIG93biBlbnVtZXJhYmxlIHByb3BlcnRpZXMgb2YgYSBtb2RlbCBpbnN0YW5jZSwgZmlsdGVyaW5nIG91dCBhbnkgcHJvcGVydGllc1xuICogbGlzdGVkIGluIHRoZSBgcHJvcHNUb0lnbm9yZWAgYXJyYXkuIEZvciBlYWNoIHJlbWFpbmluZyBwcm9wZXJ0eSwgaXQgY2hlY2tzIHdoZXRoZXIgdmFsaWRhdGlvblxuICogZGVjb3JhdG9ycyBhcmUgcHJlc2VudCB1c2luZyBgZ2V0VmFsaWRhdGlvbkRlY29yYXRvcnNgLCBhbmQgaWYgc28sIGNvbGxlY3RzIHRoZW0gaW4gdGhlIHJlc3VsdCBhcnJheS5cbiAqXG4gKiBAdGVtcGxhdGUgTSAtIEEgZ2VuZXJpYyBwYXJhbWV0ZXIgZXh0ZW5kaW5nIHRoZSBgTW9kZWxgIGNsYXNzLCByZXByZXNlbnRpbmcgdGhlIG1vZGVsIHR5cGUgYmVpbmcgaW5zcGVjdGVkLlxuICpcbiAqIEBwYXJhbSB7TX0gbW9kZWwgLSBBbiBpbnN0YW5jZSBvZiBhIGNsYXNzIGV4dGVuZGluZyBgTW9kZWxgIGZyb20gd2hpY2ggdmFsaWRhdGFibGUgcHJvcGVydGllcyB3aWxsIGJlIGV4dHJhY3RlZC5cbiAqIEBwYXJhbSB7c3RyaW5nW119IHByb3BzVG9JZ25vcmUgLSBBbiBhcnJheSBvZiBwcm9wZXJ0eSBuYW1lcyB0aGF0IHNob3VsZCBiZSBleGNsdWRlZCBmcm9tIHZhbGlkYXRpb24gaW5zcGVjdGlvbi5cbiAqXG4gKiBAcmV0dXJuIHtWYWxpZGF0aW9uUHJvcGVydHlEZWNvcmF0b3JEZWZpbml0aW9uW119IEFuIGFycmF5IG9mIHZhbGlkYXRpb24gZGVjb3JhdG9yIGRlZmluaXRpb25zXG4gKiBhc3NvY2lhdGVkIHdpdGggdGhlIG1vZGVsJ3MgcHJvcGVydGllcywgZXhjbHVkaW5nIHRob3NlIGxpc3RlZCBpbiBgcHJvcHNUb0lnbm9yZWAuXG4gKlxuICogQGZ1bmN0aW9uIGdldFZhbGlkYXRhYmxlUHJvcGVydGllc1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VmFsaWRhdGFibGVQcm9wZXJ0aWVzPE0gZXh0ZW5kcyBNb2RlbD4oXG4gIG1vZGVsOiBNLFxuICBwcm9wc1RvSWdub3JlOiBzdHJpbmdbXVxuKTogVmFsaWRhdGlvblByb3BlcnR5RGVjb3JhdG9yRGVmaW5pdGlvbltdIHtcbiAgY29uc3QgZGVjb3JhdGVkUHJvcGVydGllczogVmFsaWRhdGlvblByb3BlcnR5RGVjb3JhdG9yRGVmaW5pdGlvbltdID0gW107XG5cbiAgZm9yIChjb25zdCBwcm9wIGluIG1vZGVsKSB7XG4gICAgaWYgKFxuICAgICAgT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG1vZGVsLCBwcm9wKSAmJlxuICAgICAgIXByb3BzVG9JZ25vcmUuaW5jbHVkZXMocHJvcClcbiAgICApIHtcbiAgICAgIGNvbnN0IGRlYyA9IGdldFZhbGlkYXRpb25EZWNvcmF0b3JzKG1vZGVsLCBwcm9wKTtcbiAgICAgIGlmIChkZWMpIGRlY29yYXRlZFByb3BlcnRpZXMucHVzaChkZWMpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBkZWNvcmF0ZWRQcm9wZXJ0aWVzO1xufVxuXG4vKipcbiAqIFNhZmVseSBzZXRzIHRlbXBvcmFyeSBtZXRhZGF0YSBvbiBhbiBvYmplY3RcbiAqL1xuZnVuY3Rpb24gc2V0VGVtcG9yYXJ5Q29udGV4dChcbiAgdGFyZ2V0OiBhbnksXG4gIGtleTogc3ltYm9sIHwgc3RyaW5nLFxuICB2YWx1ZTogdW5rbm93blxuKTogdm9pZCB7XG4gIGlmICghT2JqZWN0Lmhhc093blByb3BlcnR5LmNhbGwodGFyZ2V0LCBrZXkpKSB0YXJnZXRba2V5XSA9IHZhbHVlO1xufVxuXG4vKipcbiAqIFNhZmVseSByZW1vdmVzIHRlbXBvcmFyeSBtZXRhZGF0YSBmcm9tIGFuIG9iamVjdFxuICovXG5mdW5jdGlvbiBjbGVhbnVwVGVtcG9yYXJ5Q29udGV4dCh0YXJnZXQ6IGFueSwga2V5OiBzeW1ib2wgfCBzdHJpbmcpOiB2b2lkIHtcbiAgaWYgKE9iamVjdC5oYXNPd25Qcm9wZXJ0eS5jYWxsKHRhcmdldCwga2V5KSkgZGVsZXRlIHRhcmdldFtrZXldO1xufVxuXG4vKipcbiAqIEV4ZWN1dGVzIHZhbGlkYXRpb24gd2l0aCB0ZW1wb3JhcnkgY29udGV4dCBhbmQgcmV0dXJucyB0aGUgdmFsaWRhdGlvbiByZXN1bHRcbiAqXG4gKiBAcGFyYW0gbmVzdGVkTW9kZWwgLSBUaGUgaW5zdGFuY2UgdG8gdmFsaWRhdGVcbiAqIEBwYXJhbSBwYXJlbnRNb2RlbCAtIFJlZmVyZW5jZSB0byBhIHBhcmVudCBvYmplY3QgZm9yIG5lc3RlZCB2YWxpZGF0aW9uXG4gKiBAcGFyYW0gaXNBc3luYyAtIFdoZXRoZXIgdG8gcGVyZm9ybSBhc3luYyB2YWxpZGF0aW9uXG4gKiBAcmV0dXJucyBWYWxpZGF0aW9uIHJlc3VsdCBmcm9tIGhhc0Vycm9ycygpXG4gKi9cbmZ1bmN0aW9uIGdldE5lc3RlZFZhbGlkYXRpb25FcnJvcnM8XG4gIE0gZXh0ZW5kcyBNb2RlbCxcbiAgQXN5bmMgZXh0ZW5kcyBib29sZWFuID0gZmFsc2UsXG4+KFxuICBuZXN0ZWRNb2RlbDogTSxcbiAgcGFyZW50TW9kZWw/OiBNLFxuICBpc0FzeW5jPzogQXN5bmNcbik6IENvbmRpdGlvbmFsQXN5bmM8QXN5bmMsIE1vZGVsRXJyb3JEZWZpbml0aW9uIHwgdW5kZWZpbmVkPiB7XG4gIC8vIFNldCB0ZW1wb3JhcnkgY29udGV4dCBmb3IgbmVzdGVkIG1vZGVsc1xuICBpZiAocGFyZW50TW9kZWwpIHtcbiAgICBzZXRUZW1wb3JhcnlDb250ZXh0KG5lc3RlZE1vZGVsLCBWQUxJREFUSU9OX1BBUkVOVF9LRVksIHBhcmVudE1vZGVsKTtcbiAgfVxuICBzZXRUZW1wb3JhcnlDb250ZXh0KG5lc3RlZE1vZGVsLCBBU1lOQ19NRVRBX0tFWSwgISFpc0FzeW5jKTtcblxuICBjb25zdCBlcnJzID0gbmVzdGVkTW9kZWwuaGFzRXJyb3JzKCk7XG4gIGNsZWFudXBUZW1wb3JhcnlDb250ZXh0KG5lc3RlZE1vZGVsLCBWQUxJREFUSU9OX1BBUkVOVF9LRVkpO1xuICBjbGVhbnVwVGVtcG9yYXJ5Q29udGV4dChuZXN0ZWRNb2RlbCwgQVNZTkNfTUVUQV9LRVkpO1xuICByZXR1cm4gZXJycyBhcyBhbnk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUNoaWxkVmFsdWU8TSBleHRlbmRzIE1vZGVsPihcbiAgY2hpbGRWYWx1ZTogYW55LFxuICBwYXJlbnRNb2RlbDogTSxcbiAgYWxsb3dlZFR5cGVzOiBzdHJpbmdbXSxcbiAgYXN5bmM6IGJvb2xlYW5cbik6IHN0cmluZyB8IHVuZGVmaW5lZCB8IE1vZGVsRXJyb3JEZWZpbml0aW9uIHtcbiAgbGV0IGVycjogTW9kZWxFcnJvckRlZmluaXRpb24gfCBzdHJpbmcgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG4gIGxldCBhdExlYXN0T25lTWF0Y2hlZCA9IGZhbHNlO1xuICBmb3IgKGNvbnN0IGFsbG93ZWRUeXBlIG9mIGFsbG93ZWRUeXBlcykge1xuICAgIGNvbnN0IENvbnN0ciA9IE1vZGVsLmdldChhbGxvd2VkVHlwZSkgYXMgYW55O1xuICAgIGlmICghQ29uc3RyKSB7XG4gICAgICBlcnIgPSBuZXcgTW9kZWxFcnJvckRlZmluaXRpb24oe1xuICAgICAgICBbVmFsaWRhdGlvbktleXMuVFlQRV06IGBVbmFibGUgdG8gdmVyaWZ5IHR5cGUgY29uc2lzdGVuY3ksIG1pc3NpbmcgbW9kZWwgcmVnaXN0cnkgZm9yICR7YWxsb3dlZFR5cGV9YCxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGlmIChjaGlsZFZhbHVlIGluc3RhbmNlb2YgQ29uc3RyKSB7XG4gICAgICBhdExlYXN0T25lTWF0Y2hlZCA9IHRydWU7XG4gICAgICBlcnIgPSBnZXROZXN0ZWRWYWxpZGF0aW9uRXJyb3JzKGNoaWxkVmFsdWUsIHBhcmVudE1vZGVsLCBhc3luYyk7XG4gICAgICBicmVhaztcbiAgICB9XG4gIH1cblxuICBpZiAoYXRMZWFzdE9uZU1hdGNoZWQpIHJldHVybiBlcnI7XG5cbiAgcmV0dXJuIChcbiAgICBlcnIgfHxcbiAgICBuZXcgTW9kZWxFcnJvckRlZmluaXRpb24oe1xuICAgICAgW1ZhbGlkYXRpb25LZXlzLlRZUEVdOiBgVmFsdWUgbXVzdCBiZSBhbiBpbnN0YW5jZSBvZiBvbmUgb2YgdGhlIGV4cGVjdGVkIHR5cGVzOiAke2FsbG93ZWRUeXBlcy5qb2luKFwiLCBcIil9YCxcbiAgICB9KVxuICApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVEZWNvcmF0b3I8XG4gIE0gZXh0ZW5kcyBNb2RlbCxcbiAgQXN5bmMgZXh0ZW5kcyBib29sZWFuID0gZmFsc2UsXG4+KFxuICBtb2RlbDogTSxcbiAgdmFsdWU6IGFueSxcbiAgZGVjb3JhdG9yOiBEZWNvcmF0b3JNZXRhZGF0YUFzeW5jLFxuICBhc3luYz86IEFzeW5jXG4pOiBDb25kaXRpb25hbEFzeW5jPEFzeW5jLCBzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgY29uc3QgdmFsaWRhdG9yID0gVmFsaWRhdGlvbi5nZXQoZGVjb3JhdG9yLmtleSk7XG4gIGlmICghdmFsaWRhdG9yKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBNaXNzaW5nIHZhbGlkYXRvciBmb3IgJHtkZWNvcmF0b3Iua2V5fWApO1xuICB9XG5cbiAgLy8gc2tpcCBhc3luYyBkZWNvcmF0b3JzIGlmIHZhbGlkYXRlRGVjb3JhdG9ycyBpcyBjYWxsZWQgc3luY2hyb25vdXNseSAoYXN5bmMgPSBmYWxzZSlcbiAgaWYgKCFhc3luYyAmJiBkZWNvcmF0b3IucHJvcHMuYXN5bmMpIHJldHVybiB1bmRlZmluZWQgYXMgYW55O1xuXG4gIGNvbnN0IGRlY29yYXRvclByb3BzID1cbiAgICBkZWNvcmF0b3Iua2V5ID09PSBNb2RlbEtleXMuVFlQRVxuICAgICAgPyBbZGVjb3JhdG9yLnByb3BzXVxuICAgICAgOiBkZWNvcmF0b3IucHJvcHMgfHwge307XG5cbiAgY29uc3QgY29udGV4dCA9IFBhdGhQcm94eUVuZ2luZS5jcmVhdGUobW9kZWwsIHtcbiAgICBpZ25vcmVVbmRlZmluZWQ6IHRydWUsXG4gICAgaWdub3JlTnVsbDogdHJ1ZSxcbiAgfSk7XG5cbiAgY29uc3QgbWF5YmVBc3luY0Vycm9ycyA9IHZhbGlkYXRvci5oYXNFcnJvcnMoXG4gICAgdmFsdWUsXG4gICAgZGVjb3JhdG9yUHJvcHMgYXMgVmFsaWRhdG9yT3B0aW9ucyxcbiAgICBjb250ZXh0XG4gICk7XG5cbiAgcmV0dXJuIHRvQ29uZGl0aW9uYWxQcm9taXNlKG1heWJlQXN5bmNFcnJvcnMsIGFzeW5jKTtcbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb25cbiAqIEV4ZWN1dGVzIHZhbGlkYXRpb24gbG9naWMgZm9yIGEgc2V0IG9mIGRlY29yYXRvcnMgYXBwbGllZCB0byBhIG1vZGVsJ3MgcHJvcGVydHksIGhhbmRsaW5nIGJvdGhcbiAqIHN5bmNocm9ub3VzIGFuZCBhc3luY2hyb25vdXMgdmFsaWRhdGlvbnMsIGluY2x1ZGluZyBzdXBwb3J0IGZvciBuZXN0ZWQgdmFsaWRhdGlvbnMgYW5kIGxpc3RzLlxuICpcbiAqIEBzdW1tYXJ5XG4gKiBJdGVyYXRlcyBvdmVyIGFuIGFycmF5IG9mIGRlY29yYXRvciBtZXRhZGF0YSBvYmplY3RzIGFuZCBhcHBsaWVzIGVhY2ggdmFsaWRhdGlvbiBydWxlIHRvIHRoZVxuICogcHJvdmlkZWQgdmFsdWUuIEZvciBsaXN0IGRlY29yYXRvcnMgKGBWYWxpZGF0aW9uS2V5cy5MSVNUYCksIGl0IHBlcmZvcm1zIGVsZW1lbnQtd2lzZSB2YWxpZGF0aW9uLFxuICogc3VwcG9ydGluZyBuZXN0ZWQgbW9kZWwgdmFsaWRhdGlvbiBhbmQgdHlwZSBjaGVja3MuIElmIHRoZSBgYXN5bmNgIGZsYWcgaXMgc2V0LCBhc3luY2hyb25vdXNcbiAqIHZhbGlkYXRpb24gaXMgc3VwcG9ydGVkIHVzaW5nIGBQcm9taXNlLmFsbGAuIFRoZSByZXN1bHQgaXMgYSByZWNvcmQgbWFwcGluZyB2YWxpZGF0aW9uIGtleXMgdG9cbiAqIGVycm9yIG1lc3NhZ2VzLCBvciBgdW5kZWZpbmVkYCBpZiBubyBlcnJvcnMgYXJlIGZvdW5kLlxuICpcbiAqIEB0ZW1wbGF0ZSBNIC0gQSB0eXBlIHBhcmFtZXRlciBleHRlbmRpbmcgYE1vZGVsYCwgcmVwcmVzZW50aW5nIHRoZSBtb2RlbCB0eXBlIGJlaW5nIHZhbGlkYXRlZC5cbiAqIEB0ZW1wbGF0ZSBBc3luYyAtIEEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdmFsaWRhdGlvbiBzaG91bGQgYmUgcGVyZm9ybWVkIGFzeW5jaHJvbm91c2x5LlxuICpcbiAqIEBwYXJhbSB7TX0gbW9kZWwgLSBUaGUgbW9kZWwgaW5zdGFuY2UgdGhhdCB0aGUgdmFsaWRhdGlvbiBpcyBhc3NvY2lhdGVkIHdpdGguXG4gKiBAcGFyYW0ge2FueX0gdmFsdWUgLSBUaGUgdmFsdWUgdG8gYmUgdmFsaWRhdGVkIGFnYWluc3QgdGhlIHByb3ZpZGVkIGRlY29yYXRvcnMuXG4gKiBAcGFyYW0ge0RlY29yYXRvck1ldGFkYXRhQXN5bmNbXX0gZGVjb3JhdG9ycyAtIEFuIGFycmF5IG9mIG1ldGFkYXRhIG9iamVjdHMgcmVwcmVzZW50aW5nIHZhbGlkYXRpb24gZGVjb3JhdG9ycy5cbiAqIEBwYXJhbSB7QXN5bmN9IFthc3luY10gLSBPcHRpb25hbCBmbGFnIGluZGljYXRpbmcgd2hldGhlciB2YWxpZGF0aW9uIHNob3VsZCBiZSBwZXJmb3JtZWQgYXN5bmNocm9ub3VzbHkuXG4gKlxuICogQHJldHVybiB7Q29uZGl0aW9uYWxBc3luYzxBc3luYywgUmVjb3JkPHN0cmluZywgc3RyaW5nPj4gfCB1bmRlZmluZWR9XG4gKiBSZXR1cm5zIGVpdGhlciBhIHJlY29yZCBvZiB2YWxpZGF0aW9uIGVycm9ycyAoa2V5ZWQgYnkgdGhlIGRlY29yYXRvciBrZXkpIG9yIGB1bmRlZmluZWRgIGlmIG5vIGVycm9ycyBhcmUgZm91bmQuXG4gKiBJZiBgYXN5bmNgIGlzIHRydWUsIHRoZSByZXR1cm4gdmFsdWUgaXMgYSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgc2FtZSBzdHJ1Y3R1cmUuXG4gKlxuICogQGZ1bmN0aW9uIHZhbGlkYXRlRGVjb3JhdG9yc1xuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVEZWNvcmF0b3JzPFxuICBNIGV4dGVuZHMgTW9kZWwsXG4gIEFzeW5jIGV4dGVuZHMgYm9vbGVhbiA9IGZhbHNlLFxuPihcbiAgbW9kZWw6IE0sXG4gIHZhbHVlOiBhbnksXG4gIGRlY29yYXRvcnM6IERlY29yYXRvck1ldGFkYXRhQXN5bmNbXSxcbiAgYXN5bmM/OiBBc3luY1xuKTogQ29uZGl0aW9uYWxBc3luYzxBc3luYywgUmVjb3JkPHN0cmluZywgc3RyaW5nPiB8IHVuZGVmaW5lZD4ge1xuICBjb25zdCByZXN1bHQ6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IFByb21pc2U8c3RyaW5nPj4gPSB7fTtcblxuICBmb3IgKGNvbnN0IGRlY29yYXRvciBvZiBkZWNvcmF0b3JzKSB7XG4gICAgLy8gc2tpcCBhc3luYyBkZWNvcmF0b3JzIGlmIHZhbGlkYXRlRGVjb3JhdG9ycyBpcyBjYWxsZWQgc3luY2hyb25vdXNseSAoYXN5bmMgPSBmYWxzZSlcbiAgICBpZiAoIWFzeW5jICYmIGRlY29yYXRvci5wcm9wcy5hc3luYykgY29udGludWU7XG5cbiAgICBsZXQgdmFsaWRhdGlvbkVycm9ycyA9IHZhbGlkYXRlRGVjb3JhdG9yKG1vZGVsLCB2YWx1ZSwgZGVjb3JhdG9yLCBhc3luYyk7XG5cbiAgICAvKlxuICAgIElmIHRoZSBkZWNvcmF0b3IgaXMgYSBsaXN0LCBlYWNoIGVsZW1lbnQgbXVzdCBiZSBjaGVja2VkLlxuICAgIFdoZW4gJ2FzeW5jJyBpcyB0cnVlLCB0aGUgJ2Vycicgd2lsbCBhbHdheXMgYmUgYSBwZW5kaW5nIHByb21pc2UgaW5pdGlhbGx5LFxuICAgIHNvIHRoZSAnIWVycicgY2hlY2sgd2lsbCBldmFsdWF0ZSB0byBmYWxzZSAoZXZlbiBpZiB0aGUgcHJvbWlzZSBsYXRlciByZXNvbHZlcyB3aXRoIG5vIGVycm9ycylcbiAgICAqL1xuICAgIGlmIChkZWNvcmF0b3Iua2V5ID09PSBWYWxpZGF0aW9uS2V5cy5MSVNUICYmICghdmFsaWRhdGlvbkVycm9ycyB8fCBhc3luYykpIHtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IHZhbHVlIGluc3RhbmNlb2YgU2V0ID8gWy4uLnZhbHVlXSA6IHZhbHVlO1xuICAgICAgaWYgKHZhbHVlcyAmJiB2YWx1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCB0eXBlcyA9IChkZWNvcmF0b3IucHJvcHMuY2xhc3MgfHxcbiAgICAgICAgICBkZWNvcmF0b3IucHJvcHMuY2xhenogfHxcbiAgICAgICAgICBkZWNvcmF0b3IucHJvcHMuY3VzdG9tVHlwZXMpIGFzIHN0cmluZyB8IHN0cmluZ1tdO1xuXG4gICAgICAgIGNvbnN0IGFsbG93ZWRUeXBlcyA9IFt0eXBlc10uZmxhdCgpLm1hcCgodCkgPT4gU3RyaW5nKHQpLnRvTG93ZXJDYXNlKCkpO1xuICAgICAgICAvLyBjb25zdCByZXNlcnZlZCA9IE9iamVjdC52YWx1ZXMoUmVzZXJ2ZWRNb2RlbHMpLm1hcCgodikgPT4gdi50b0xvd2VyQ2FzZSgpKSBhcyBzdHJpbmdbXTtcblxuICAgICAgICBjb25zdCBlcnJzID0gdmFsdWVzLm1hcCgoY2hpbGRWYWx1ZTogYW55KSA9PiB7XG4gICAgICAgICAgLy8gaWYgKE1vZGVsLmlzTW9kZWwodikgJiYgIXJlc2VydmVkLmluY2x1ZGVzKHYpIHtcbiAgICAgICAgICBpZiAoTW9kZWwuaXNNb2RlbChjaGlsZFZhbHVlKSkge1xuICAgICAgICAgICAgcmV0dXJuIHZhbGlkYXRlQ2hpbGRWYWx1ZShcbiAgICAgICAgICAgICAgY2hpbGRWYWx1ZSxcbiAgICAgICAgICAgICAgbW9kZWwsXG4gICAgICAgICAgICAgIFt0eXBlc10uZmxhdCgpLFxuICAgICAgICAgICAgICAhIWFzeW5jXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgLy8gcmV0dXJuIGdldE5lc3RlZFZhbGlkYXRpb25FcnJvcnMoY2hpbGRWYWx1ZSwgbW9kZWwsIGFzeW5jKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXR1cm4gYWxsb3dlZFR5cGVzLmluY2x1ZGVzKHR5cGVvZiBjaGlsZFZhbHVlKVxuICAgICAgICAgICAgPyB1bmRlZmluZWRcbiAgICAgICAgICAgIDogXCJWYWx1ZSBoYXMgbm8gdmFsaWRhdGFibGUgdHlwZVwiO1xuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoYXN5bmMpIHtcbiAgICAgICAgICB2YWxpZGF0aW9uRXJyb3JzID0gUHJvbWlzZS5hbGwoZXJycykudGhlbigocmVzdWx0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBhbGxFbXB0eSA9IHJlc3VsdC5ldmVyeSgocikgPT4gIXIpO1xuICAgICAgICAgICAgcmV0dXJuIGFsbEVtcHR5ID8gdW5kZWZpbmVkIDogcmVzdWx0O1xuICAgICAgICAgIH0pIGFzIGFueTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBhbGxFbXB0eSA9IGVycnMuZXZlcnkoKHI6IHN0cmluZyB8IHVuZGVmaW5lZCkgPT4gIXIpO1xuICAgICAgICAgIHZhbGlkYXRpb25FcnJvcnMgPSBlcnJzLmxlbmd0aCA+IDAgJiYgIWFsbEVtcHR5ID8gZXJycyA6IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh2YWxpZGF0aW9uRXJyb3JzKSAocmVzdWx0IGFzIGFueSlbZGVjb3JhdG9yLmtleV0gPSB2YWxpZGF0aW9uRXJyb3JzO1xuICB9XG5cbiAgaWYgKCFhc3luYylcbiAgICByZXR1cm4gT2JqZWN0LmtleXMocmVzdWx0KS5sZW5ndGggPiAwXG4gICAgICA/IChyZXN1bHQgYXMgYW55KVxuICAgICAgOiAodW5kZWZpbmVkIGFzIGFueSk7XG5cbiAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKHJlc3VsdCk7XG4gIGNvbnN0IHByb21pc2VzID0gT2JqZWN0LnZhbHVlcyhyZXN1bHQpIGFzIFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPltdO1xuICByZXR1cm4gUHJvbWlzZS5hbGwocHJvbWlzZXMpLnRoZW4oKHJlc29sdmVkVmFsdWVzKSA9PiB7XG4gICAgY29uc3QgcmVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZXNvbHZlZFZhbHVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdmFsID0gcmVzb2x2ZWRWYWx1ZXNbaV07XG4gICAgICBpZiAodmFsICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmVzW2tleXNbaV1dID0gdmFsO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gT2JqZWN0LmtleXMocmVzKS5sZW5ndGggPiAwID8gcmVzIDogdW5kZWZpbmVkO1xuICB9KSBhcyBhbnk7XG59XG5cbi8qKlxuICogQGZ1bmN0aW9uIHZhbGlkYXRlXG4gKiBAdGVtcGxhdGUgTVxuICogQHRlbXBsYXRlIEFzeW5jXG4gKiBAbWVtYmVyT2YgbW9kdWxlOmRlY29yYXRvci12YWxpZGF0aW9uXG4gKiBAY2F0ZWdvcnkgTW9kZWxcbiAqXG4gKiBAZGVzY3JpcHRpb25cbiAqIFZhbGlkYXRlcyB0aGUgcHJvcGVydGllcyBvZiBhIHtAbGluayBNb2RlbH0gaW5zdGFuY2UgdXNpbmcgcmVnaXN0ZXJlZCBkZWNvcmF0b3JzLlxuICogU3VwcG9ydHMgYm90aCBzeW5jaHJvbm91cyBhbmQgYXN5bmNocm9ub3VzIHZhbGlkYXRpb24gZmxvd3MsIGRlcGVuZGluZyBvbiB0aGUgYGFzeW5jYCBmbGFnLlxuICpcbiAqIEBzdW1tYXJ5XG4gKiBUaGlzIGZ1bmN0aW9uIGluc3BlY3RzIGEgZ2l2ZW4gbW9kZWwgb2JqZWN0LCBpZGVudGlmaWVzIGRlY29yYXRlZCBwcm9wZXJ0aWVzIHRoYXQgcmVxdWlyZSB2YWxpZGF0aW9uLFxuICogYW5kIGFwcGxpZXMgdGhlIGNvcnJlc3BvbmRpbmcgdmFsaWRhdGlvbiBydWxlcy4gSXQgYWxzbyBzdXBwb3J0cyBuZXN0ZWQgbW9kZWwgdmFsaWRhdGlvbiBhbmQgZ3JhY2VmdWxseVxuICogbWVyZ2VzIGFueSB2YWxpZGF0aW9uIGVycm9ycy4gRm9yIGNvbGxlY3Rpb25zIChBcnJheS9TZXQpLCBpdCBlbmZvcmNlcyB0aGUgcHJlc2VuY2Ugb2YgdGhlIGBAbGlzdGAgZGVjb3JhdG9yXG4gKiBhbmQgY2hlY2tzIHRoZSB0eXBlIG9mIGVsZW1lbnRzLiBJZiBhIHByb3BlcnR5IGlzIGEgbmVzdGVkIG1vZGVsLCBpdCB3aWxsIGNhbGwgYGhhc0Vycm9yc2Agb24gaXQgYW5kIGZsYXR0ZW5cbiAqIHRoZSBuZXN0ZWQgZXJyb3Iga2V5cyB1c2luZyBkb3Qgbm90YXRpb24uXG4gKlxuICogQHBhcmFtIHtNfSBtb2RlbCAtIFRoZSBtb2RlbCBpbnN0YW5jZSB0byBiZSB2YWxpZGF0ZWQuIE11c3QgZXh0ZW5kIGZyb20ge0BsaW5rIE1vZGVsfS5cbiAqIEBwYXJhbSB7QXN5bmN9IFthc3luY10gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIHZhbGlkYXRpb24gc2hvdWxkIGJlIGFzeW5jaHJvbm91cy5cbiAqIEBwYXJhbSB7Li4uc3RyaW5nfSBwcm9wc1RvSWdub3JlIC0gQSB2YXJpYWRpYyBsaXN0IG9mIHByb3BlcnR5IG5hbWVzIHRoYXQgc2hvdWxkIGJlIHNraXBwZWQgZHVyaW5nIHZhbGlkYXRpb24uXG4gKlxuICogQHJldHVybnMge0NvbmRpdGlvbmFsQXN5bmM8QXN5bmMsIE1vZGVsRXJyb3JEZWZpbml0aW9uIHwgdW5kZWZpbmVkPn1cbiAqIFJldHVybnMgZWl0aGVyIGEge0BsaW5rIE1vZGVsRXJyb3JEZWZpbml0aW9ufSBjb250YWluaW5nIHZhbGlkYXRpb24gZXJyb3JzLFxuICogb3IgYHVuZGVmaW5lZGAgaWYgbm8gZXJyb3JzIGFyZSBmb3VuZC4gV2hlbiBgYXN5bmNgIGlzIGB0cnVlYCwgcmV0dXJucyBhIFByb21pc2UuXG4gKlxuICogQHNlZSB7QGxpbmsgTW9kZWx9XG4gKiBAc2VlIHtAbGluayBNb2RlbEVycm9yRGVmaW5pdGlvbn1cbiAqIEBzZWUge0BsaW5rIHZhbGlkYXRlRGVjb3JhdG9yc31cbiAqIEBzZWUge0BsaW5rIGdldFZhbGlkYXRhYmxlUHJvcGVydGllc31cbiAqXG4gKiBAbWVybWFpZFxuICogc2VxdWVuY2VEaWFncmFtXG4gKiAgICAgcGFydGljaXBhbnQgQ2FsbGVyXG4gKiAgICAgcGFydGljaXBhbnQgdmFsaWRhdGVcbiAqICAgICBwYXJ0aWNpcGFudCBnZXRWYWxpZGF0YWJsZVByb3BlcnRpZXNcbiAqICAgICBwYXJ0aWNpcGFudCB2YWxpZGF0ZURlY29yYXRvcnNcbiAqICAgICBwYXJ0aWNpcGFudCBNb2RlbEluc3RhbmNlXG4gKiAgICAgQ2FsbGVyLT4+dmFsaWRhdGU6IGNhbGwgd2l0aCBvYmosIGFzeW5jLCBwcm9wc1RvSWdub3JlXG4gKiAgICAgdmFsaWRhdGUtPj5nZXRWYWxpZGF0YWJsZVByb3BlcnRpZXM6IHJldHJpZXZlIGRlY29yYXRlZCBwcm9wc1xuICogICAgIGxvb3AgZm9yIGVhY2ggcHJvcGVydHlcbiAqICAgICAgICAgdmFsaWRhdGUtPj52YWxpZGF0ZURlY29yYXRvcnM6IHZhbGlkYXRlIHVzaW5nIGRlY29yYXRvcnNcbiAqICAgICAgICAgYWx0IGlzIG5lc3RlZCBtb2RlbFxuICogICAgICAgICAgICAgdmFsaWRhdGUtPj5Nb2RlbEluc3RhbmNlOiBjYWxsIGhhc0Vycm9ycygpXG4gKiAgICAgICAgIGVuZFxuICogICAgIGVuZFxuICogICAgIGFsdCBhc3luY1xuICogICAgICAgICB2YWxpZGF0ZS0+PnZhbGlkYXRlOiBQcm9taXNlLmFsbFNldHRsZWQgZm9yIGVycm9yc1xuICogICAgIGVuZFxuICogICAgIHZhbGlkYXRlLS0+PkNhbGxlcjogcmV0dXJuIE1vZGVsRXJyb3JEZWZpbml0aW9uIHwgdW5kZWZpbmVkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZTxcbiAgTSBleHRlbmRzIE1vZGVsPGJvb2xlYW4+LFxuICBBc3luYyBleHRlbmRzIGJvb2xlYW4gPSBmYWxzZSxcbj4oXG4gIG1vZGVsOiBNLFxuICBhc3luYzogQXN5bmMsXG4gIC4uLnByb3BzVG9JZ25vcmU6IHN0cmluZ1tdXG4pOiBDb25kaXRpb25hbEFzeW5jPEFzeW5jLCBNb2RlbEVycm9yRGVmaW5pdGlvbiB8IHVuZGVmaW5lZD4ge1xuICBjb25zdCBkZWNvcmF0ZWRQcm9wZXJ0aWVzOiBWYWxpZGF0aW9uUHJvcGVydHlEZWNvcmF0b3JEZWZpbml0aW9uW10gPVxuICAgIGdldFZhbGlkYXRhYmxlUHJvcGVydGllcyhtb2RlbCwgcHJvcHNUb0lnbm9yZSk7XG5cbiAgY29uc3QgcmVzdWx0OiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID0ge307XG4gIGNvbnN0IG5lc3RlZEVycm9yczogUmVjb3JkPHN0cmluZywgYW55PiA9IHt9O1xuXG4gIGZvciAoY29uc3QgeyBwcm9wLCBkZWNvcmF0b3JzIH0gb2YgZGVjb3JhdGVkUHJvcGVydGllcykge1xuICAgIGNvbnN0IHByb3BLZXkgPSBTdHJpbmcocHJvcCk7XG4gICAgbGV0IHByb3BWYWx1ZSA9IChtb2RlbCBhcyBhbnkpW3Byb3BdO1xuXG4gICAgaWYgKC