UNPKG

@decaf-ts/decorator-validation

Version:
109 lines 13.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Validator = void 0; const constants_1 = require("./constants.cjs"); const strings_1 = require("./../../utils/strings.cjs"); const reflection_1 = require("@decaf-ts/reflection"); /** * @description Abstract base class for all validators in the validation framework * @summary The Validator class provides the foundation for all validator implementations. * It handles type checking, error message formatting, and defines the common interface * that all validators must implement. This class is designed to be extended by specific * validator implementations that provide concrete validation logic. * * @param {string} message - Default error message to display when validation fails, defaults to {@link DEFAULT_ERROR_MESSAGES#DEFAULT} * @param {string[]} acceptedTypes - Array of type names that this validator can validate * * @class Validator * @abstract * * @example * ```typescript * // Example of extending the Validator class to create a custom validator * class CustomValidator extends Validator<CustomValidatorOptions> { * constructor(message: string = "Custom validation failed") { * // Specify that this validator accepts String and Number types * super(message, String.name, Number.name); * } * * public hasErrors(value: any, options?: CustomValidatorOptions): string | undefined { * // Implement custom validation logic * if (someCondition) { * return this.getMessage(options?.message || this.message); * } * return undefined; // No errors * } * } * ``` * * @mermaid * sequenceDiagram * participant C as Client * participant V as Validator Subclass * participant B as Base Validator * * C->>V: new CustomValidator(message) * V->>B: super(message, acceptedTypes) * B->>B: Store message and types * B->>B: Wrap hasErrors with type checking * C->>V: hasErrors(value, options) * alt value type not in acceptedTypes * B-->>C: Type error message * else value type is accepted * V->>V: Custom validation logic * V-->>C: Validation result * end * * @category Validators */ class Validator { constructor(message = constants_1.DEFAULT_ERROR_MESSAGES.DEFAULT, ...acceptedTypes) { this.message = message; if (acceptedTypes.length) this.acceptedTypes = acceptedTypes; if (this.acceptedTypes) this.hasErrors = this.checkTypeAndHasErrors(this.hasErrors.bind(this)); } /** * @description Formats an error message with optional arguments * @summary Creates a formatted error message by replacing placeholders with provided arguments. * This method uses the string formatting utility to generate consistent error messages * across all validators. * * @param {string} message - The message template with placeholders * @param {...any} args - Values to insert into the message template * @return {string} The formatted error message * @protected */ getMessage(message, ...args) { return (0, strings_1.sf)(message, ...args); } /** * @description Creates a type-checking wrapper around the hasErrors method * @summary Wraps the hasErrors method with type validation logic to ensure that * the value being validated is of an accepted type before performing specific validation. * This method is called during construction if acceptedTypes are provided. * * @param {Function} unbound - The original hasErrors method to be wrapped * @return {Function} A new function that performs type checking before calling the original method * @private */ checkTypeAndHasErrors(unbound) { return function (value, ...args) { if (value === undefined || !this.acceptedTypes) return unbound(value, ...args); if (!reflection_1.Reflection.checkTypes(value, this.acceptedTypes)) return this.getMessage(constants_1.DEFAULT_ERROR_MESSAGES.TYPE, this.acceptedTypes.join(", "), typeof value); return unbound(value, ...args); }.bind(this); } /** * @summary Duck typing for Validators * @param val */ static isValidator(val) { return val.constructor && !!val["hasErrors"]; } } exports.Validator = Validator; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3ZhbGlkYXRpb24vVmFsaWRhdG9ycy9WYWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsK0NBQXFEO0FBQ3JELHVEQUF5QztBQUN6QyxxREFBa0Q7QUFJbEQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1ERztBQUNILE1BQXNCLFNBQVM7SUFJN0IsWUFDRSxVQUFrQixrQ0FBc0IsQ0FBQyxPQUFPLEVBQ2hELEdBQUcsYUFBdUI7UUFFMUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFFdkIsSUFBSSxhQUFhLENBQUMsTUFBTTtZQUFFLElBQUksQ0FBQyxhQUFhLEdBQUcsYUFBYSxDQUFDO1FBQzdELElBQUksSUFBSSxDQUFDLGFBQWE7WUFDcEIsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7T0FVRztJQUNPLFVBQVUsQ0FBQyxPQUFlLEVBQUUsR0FBRyxJQUFXO1FBQ2xELE9BQU8sSUFBQSxZQUFFLEVBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFDOUIsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNLLHFCQUFxQixDQUMzQixPQUEyRDtRQUUzRCxPQUFPLFVBRUwsS0FBVSxFQUNWLEdBQUcsSUFBVztZQUVkLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhO2dCQUM1QyxPQUFPLE9BQU8sQ0FBQyxLQUFLLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztZQUNqQyxJQUFJLENBQUMsdUJBQVUsQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUM7Z0JBQ25ELE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FDcEIsa0NBQXNCLENBQUMsSUFBSSxFQUMzQixJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFDN0IsT0FBTyxLQUFLLENBQ2IsQ0FBQztZQUNKLE9BQU8sT0FBTyxDQUFDLEtBQUssRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQ2pDLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDZixDQUFDO0lBeUJEOzs7T0FHRztJQUNILE1BQU0sQ0FBQyxXQUFXLENBQUMsR0FBUTtRQUN6QixPQUFPLEdBQUcsQ0FBQyxXQUFXLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUMvQyxDQUFDO0NBQ0Y7QUExRkQsOEJBMEZDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgREVGQVVMVF9FUlJPUl9NRVNTQUdFUyB9IGZyb20gXCIuL2NvbnN0YW50c1wiO1xuaW1wb3J0IHsgc2YgfSBmcm9tIFwiLi4vLi4vdXRpbHMvc3RyaW5nc1wiO1xuaW1wb3J0IHsgUmVmbGVjdGlvbiB9IGZyb20gXCJAZGVjYWYtdHMvcmVmbGVjdGlvblwiO1xuaW1wb3J0IHsgVmFsaWRhdG9yT3B0aW9ucyB9IGZyb20gXCIuLi90eXBlc1wiO1xuaW1wb3J0IHR5cGUgeyBQYXRoUHJveHkgfSBmcm9tIFwiLi4vLi4vdXRpbHNcIjtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gQWJzdHJhY3QgYmFzZSBjbGFzcyBmb3IgYWxsIHZhbGlkYXRvcnMgaW4gdGhlIHZhbGlkYXRpb24gZnJhbWV3b3JrXG4gKiBAc3VtbWFyeSBUaGUgVmFsaWRhdG9yIGNsYXNzIHByb3ZpZGVzIHRoZSBmb3VuZGF0aW9uIGZvciBhbGwgdmFsaWRhdG9yIGltcGxlbWVudGF0aW9ucy5cbiAqIEl0IGhhbmRsZXMgdHlwZSBjaGVja2luZywgZXJyb3IgbWVzc2FnZSBmb3JtYXR0aW5nLCBhbmQgZGVmaW5lcyB0aGUgY29tbW9uIGludGVyZmFjZVxuICogdGhhdCBhbGwgdmFsaWRhdG9ycyBtdXN0IGltcGxlbWVudC4gVGhpcyBjbGFzcyBpcyBkZXNpZ25lZCB0byBiZSBleHRlbmRlZCBieSBzcGVjaWZpY1xuICogdmFsaWRhdG9yIGltcGxlbWVudGF0aW9ucyB0aGF0IHByb3ZpZGUgY29uY3JldGUgdmFsaWRhdGlvbiBsb2dpYy5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIERlZmF1bHQgZXJyb3IgbWVzc2FnZSB0byBkaXNwbGF5IHdoZW4gdmFsaWRhdGlvbiBmYWlscywgZGVmYXVsdHMgdG8ge0BsaW5rIERFRkFVTFRfRVJST1JfTUVTU0FHRVMjREVGQVVMVH1cbiAqIEBwYXJhbSB7c3RyaW5nW119IGFjY2VwdGVkVHlwZXMgLSBBcnJheSBvZiB0eXBlIG5hbWVzIHRoYXQgdGhpcyB2YWxpZGF0b3IgY2FuIHZhbGlkYXRlXG4gKlxuICogQGNsYXNzIFZhbGlkYXRvclxuICogQGFic3RyYWN0XG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIEV4YW1wbGUgb2YgZXh0ZW5kaW5nIHRoZSBWYWxpZGF0b3IgY2xhc3MgdG8gY3JlYXRlIGEgY3VzdG9tIHZhbGlkYXRvclxuICogY2xhc3MgQ3VzdG9tVmFsaWRhdG9yIGV4dGVuZHMgVmFsaWRhdG9yPEN1c3RvbVZhbGlkYXRvck9wdGlvbnM+IHtcbiAqICAgY29uc3RydWN0b3IobWVzc2FnZTogc3RyaW5nID0gXCJDdXN0b20gdmFsaWRhdGlvbiBmYWlsZWRcIikge1xuICogICAgIC8vIFNwZWNpZnkgdGhhdCB0aGlzIHZhbGlkYXRvciBhY2NlcHRzIFN0cmluZyBhbmQgTnVtYmVyIHR5cGVzXG4gKiAgICAgc3VwZXIobWVzc2FnZSwgU3RyaW5nLm5hbWUsIE51bWJlci5uYW1lKTtcbiAqICAgfVxuICpcbiAqICAgcHVibGljIGhhc0Vycm9ycyh2YWx1ZTogYW55LCBvcHRpb25zPzogQ3VzdG9tVmFsaWRhdG9yT3B0aW9ucyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gKiAgICAgLy8gSW1wbGVtZW50IGN1c3RvbSB2YWxpZGF0aW9uIGxvZ2ljXG4gKiAgICAgaWYgKHNvbWVDb25kaXRpb24pIHtcbiAqICAgICAgIHJldHVybiB0aGlzLmdldE1lc3NhZ2Uob3B0aW9ucz8ubWVzc2FnZSB8fCB0aGlzLm1lc3NhZ2UpO1xuICogICAgIH1cbiAqICAgICByZXR1cm4gdW5kZWZpbmVkOyAvLyBObyBlcnJvcnNcbiAqICAgfVxuICogfVxuICogYGBgXG4gKlxuICogQG1lcm1haWRcbiAqIHNlcXVlbmNlRGlhZ3JhbVxuICogICBwYXJ0aWNpcGFudCBDIGFzIENsaWVudFxuICogICBwYXJ0aWNpcGFudCBWIGFzIFZhbGlkYXRvciBTdWJjbGFzc1xuICogICBwYXJ0aWNpcGFudCBCIGFzIEJhc2UgVmFsaWRhdG9yXG4gKlxuICogICBDLT4+VjogbmV3IEN1c3RvbVZhbGlkYXRvcihtZXNzYWdlKVxuICogICBWLT4+Qjogc3VwZXIobWVzc2FnZSwgYWNjZXB0ZWRUeXBlcylcbiAqICAgQi0+PkI6IFN0b3JlIG1lc3NhZ2UgYW5kIHR5cGVzXG4gKiAgIEItPj5COiBXcmFwIGhhc0Vycm9ycyB3aXRoIHR5cGUgY2hlY2tpbmdcbiAqICAgQy0+PlY6IGhhc0Vycm9ycyh2YWx1ZSwgb3B0aW9ucylcbiAqICAgYWx0IHZhbHVlIHR5cGUgbm90IGluIGFjY2VwdGVkVHlwZXNcbiAqICAgICBCLS0+PkM6IFR5cGUgZXJyb3IgbWVzc2FnZVxuICogICBlbHNlIHZhbHVlIHR5cGUgaXMgYWNjZXB0ZWRcbiAqICAgICBWLT4+VjogQ3VzdG9tIHZhbGlkYXRpb24gbG9naWNcbiAqICAgICBWLS0+PkM6IFZhbGlkYXRpb24gcmVzdWx0XG4gKiAgIGVuZFxuICpcbiAqIEBjYXRlZ29yeSBWYWxpZGF0b3JzXG4gKi9cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBWYWxpZGF0b3I8ViBleHRlbmRzIFZhbGlkYXRvck9wdGlvbnMgPSBWYWxpZGF0b3JPcHRpb25zPiB7XG4gIHJlYWRvbmx5IG1lc3NhZ2U6IHN0cmluZztcbiAgcmVhZG9ubHkgYWNjZXB0ZWRUeXBlcz86IHN0cmluZ1tdO1xuXG4gIHByb3RlY3RlZCBjb25zdHJ1Y3RvcihcbiAgICBtZXNzYWdlOiBzdHJpbmcgPSBERUZBVUxUX0VSUk9SX01FU1NBR0VTLkRFRkFVTFQsXG4gICAgLi4uYWNjZXB0ZWRUeXBlczogc3RyaW5nW11cbiAgKSB7XG4gICAgdGhpcy5tZXNzYWdlID0gbWVzc2FnZTtcblxuICAgIGlmIChhY2NlcHRlZFR5cGVzLmxlbmd0aCkgdGhpcy5hY2NlcHRlZFR5cGVzID0gYWNjZXB0ZWRUeXBlcztcbiAgICBpZiAodGhpcy5hY2NlcHRlZFR5cGVzKVxuICAgICAgdGhpcy5oYXNFcnJvcnMgPSB0aGlzLmNoZWNrVHlwZUFuZEhhc0Vycm9ycyh0aGlzLmhhc0Vycm9ycy5iaW5kKHRoaXMpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRm9ybWF0cyBhbiBlcnJvciBtZXNzYWdlIHdpdGggb3B0aW9uYWwgYXJndW1lbnRzXG4gICAqIEBzdW1tYXJ5IENyZWF0ZXMgYSBmb3JtYXR0ZWQgZXJyb3IgbWVzc2FnZSBieSByZXBsYWNpbmcgcGxhY2Vob2xkZXJzIHdpdGggcHJvdmlkZWQgYXJndW1lbnRzLlxuICAgKiBUaGlzIG1ldGhvZCB1c2VzIHRoZSBzdHJpbmcgZm9ybWF0dGluZyB1dGlsaXR5IHRvIGdlbmVyYXRlIGNvbnNpc3RlbnQgZXJyb3IgbWVzc2FnZXNcbiAgICogYWNyb3NzIGFsbCB2YWxpZGF0b3JzLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBtZXNzYWdlIHRlbXBsYXRlIHdpdGggcGxhY2Vob2xkZXJzXG4gICAqIEBwYXJhbSB7Li4uYW55fSBhcmdzIC0gVmFsdWVzIHRvIGluc2VydCBpbnRvIHRoZSBtZXNzYWdlIHRlbXBsYXRlXG4gICAqIEByZXR1cm4ge3N0cmluZ30gVGhlIGZvcm1hdHRlZCBlcnJvciBtZXNzYWdlXG4gICAqIEBwcm90ZWN0ZWRcbiAgICovXG4gIHByb3RlY3RlZCBnZXRNZXNzYWdlKG1lc3NhZ2U6IHN0cmluZywgLi4uYXJnczogYW55W10pIHtcbiAgICByZXR1cm4gc2YobWVzc2FnZSwgLi4uYXJncyk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENyZWF0ZXMgYSB0eXBlLWNoZWNraW5nIHdyYXBwZXIgYXJvdW5kIHRoZSBoYXNFcnJvcnMgbWV0aG9kXG4gICAqIEBzdW1tYXJ5IFdyYXBzIHRoZSBoYXNFcnJvcnMgbWV0aG9kIHdpdGggdHlwZSB2YWxpZGF0aW9uIGxvZ2ljIHRvIGVuc3VyZSB0aGF0XG4gICAqIHRoZSB2YWx1ZSBiZWluZyB2YWxpZGF0ZWQgaXMgb2YgYW4gYWNjZXB0ZWQgdHlwZSBiZWZvcmUgcGVyZm9ybWluZyBzcGVjaWZpYyB2YWxpZGF0aW9uLlxuICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgZHVyaW5nIGNvbnN0cnVjdGlvbiBpZiBhY2NlcHRlZFR5cGVzIGFyZSBwcm92aWRlZC5cbiAgICpcbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gdW5ib3VuZCAtIFRoZSBvcmlnaW5hbCBoYXNFcnJvcnMgbWV0aG9kIHRvIGJlIHdyYXBwZWRcbiAgICogQHJldHVybiB7RnVuY3Rpb259IEEgbmV3IGZ1bmN0aW9uIHRoYXQgcGVyZm9ybXMgdHlwZSBjaGVja2luZyBiZWZvcmUgY2FsbGluZyB0aGUgb3JpZ2luYWwgbWV0aG9kXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBwcml2YXRlIGNoZWNrVHlwZUFuZEhhc0Vycm9ycyhcbiAgICB1bmJvdW5kOiAodmFsdWU6IGFueSwgLi4uYXJnczogYW55W10pID0+IHN0cmluZyB8IHVuZGVmaW5lZFxuICApIHtcbiAgICByZXR1cm4gZnVuY3Rpb24gKFxuICAgICAgdGhpczogVmFsaWRhdG9yLFxuICAgICAgdmFsdWU6IGFueSxcbiAgICAgIC4uLmFyZ3M6IGFueVtdXG4gICAgKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8ICF0aGlzLmFjY2VwdGVkVHlwZXMpXG4gICAgICAgIHJldHVybiB1bmJvdW5kKHZhbHVlLCAuLi5hcmdzKTtcbiAgICAgIGlmICghUmVmbGVjdGlvbi5jaGVja1R5cGVzKHZhbHVlLCB0aGlzLmFjY2VwdGVkVHlwZXMpKVxuICAgICAgICByZXR1cm4gdGhpcy5nZXRNZXNzYWdlKFxuICAgICAgICAgIERFRkFVTFRfRVJST1JfTUVTU0FHRVMuVFlQRSxcbiAgICAgICAgICB0aGlzLmFjY2VwdGVkVHlwZXMuam9pbihcIiwgXCIpLFxuICAgICAgICAgIHR5cGVvZiB2YWx1ZVxuICAgICAgICApO1xuICAgICAgcmV0dXJuIHVuYm91bmQodmFsdWUsIC4uLmFyZ3MpO1xuICAgIH0uYmluZCh0aGlzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gVmFsaWRhdGVzIGEgdmFsdWUgYWdhaW5zdCBzcGVjaWZpYyB2YWxpZGF0aW9uIHJ1bGVzXG4gICAqIEBzdW1tYXJ5IEFic3RyYWN0IG1ldGhvZCB0aGF0IG11c3QgYmUgaW1wbGVtZW50ZWQgYnkgYWxsIHZhbGlkYXRvciBzdWJjbGFzc2VzLlxuICAgKiBUaGlzIG1ldGhvZCBjb250YWlucyB0aGUgY29yZSB2YWxpZGF0aW9uIGxvZ2ljIHRoYXQgZGV0ZXJtaW5lcyB3aGV0aGVyIGEgdmFsdWVcbiAgICogaXMgdmFsaWQgYWNjb3JkaW5nIHRvIHRoZSBzcGVjaWZpYyBydWxlcyBvZiB0aGUgdmFsaWRhdG9yLiBJZiB0aGUgdmFsdWUgaXMgdmFsaWQsXG4gICAqIHRoZSBtZXRob2QgcmV0dXJucyB1bmRlZmluZWQ7IG90aGVyd2lzZSwgaXQgcmV0dXJucyBhbiBlcnJvciBtZXNzYWdlLlxuICAgKlxuICAgKiBAdGVtcGxhdGUgViAtIFR5cGUgb2YgdGhlIG9wdGlvbnMgb2JqZWN0IHRoYXQgY2FuIGJlIHBhc3NlZCB0byB0aGUgdmFsaWRhdG9yXG4gICAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byB2YWxpZGF0ZVxuICAgKiBAcGFyYW0ge1Z9IFtvcHRpb25zXSAtIE9wdGlvbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgY3VzdG9taXppbmcgdmFsaWRhdGlvbiBiZWhhdmlvclxuICAgKiBAcGFyYW0ge1BhdGhQcm94eTxhbnk+fSBwcm94eSAtXG4gICAqIEByZXR1cm4ge3N0cmluZyB8IHVuZGVmaW5lZH0gRXJyb3IgbWVzc2FnZSBpZiB2YWxpZGF0aW9uIGZhaWxzLCB1bmRlZmluZWQgaWYgdmFsaWRhdGlvbiBwYXNzZXNcbiAgICpcbiAgICogQGFic3RyYWN0XG4gICAqXG4gICAqIEBzZWUgTW9kZWwjdmFsaWRhdGVcbiAgICovXG4gIHB1YmxpYyBhYnN0cmFjdCBoYXNFcnJvcnMoXG4gICAgdmFsdWU6IGFueSxcbiAgICBvcHRpb25zPzogVixcbiAgICBwcm94eT86IFBhdGhQcm94eTxhbnk+XG4gICk6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogQHN1bW1hcnkgRHVjayB0eXBpbmcgZm9yIFZhbGlkYXRvcnNcbiAgICogQHBhcmFtIHZhbFxuICAgKi9cbiAgc3RhdGljIGlzVmFsaWRhdG9yKHZhbDogYW55KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHZhbC5jb25zdHJ1Y3RvciAmJiAhIXZhbFtcImhhc0Vycm9yc1wiXTtcbiAgfVxufVxuIl19