Source: is.js

'use strict';

/**
 * Global constant & API entry point for Fluent JSON validator.
 *
 * @namespace  is
 * @type       {Object}
 */
const is = {

    /**
     * Function to return a new configurable validator. You don't need to call this directly! Use a factory method instead.
     *
     * @class   is.__validationUnit
     * @return  {Object}  The new, unconfigured validator.
     */
    __validationUnit: function() {

        this.__type = undefined;
        this.__required = true;
        this.__functionalValidators = [];
        this.__validateType = (subject) => {
            return false; // FIXME should this actually throw an exception?
        }

        /**
         * Validate the subject against the set schema.
         *
         * @param      {any}      subject   The subject to validate.
         * @return     {Boolean}            Validation result: true if the subject matches the schema, false otherwise.
         */
        this.validate = (subject) => {
            if (typeof subject === "undefined" && this.__type !== "array" && this.__type !== "various") {
                return !this.__required;
            } else {
                return this.__validateType(subject) &&
                       this.__functionalValidators.filter(functionalValidator => !functionalValidator(subject)).length == 0;
                       // ^ == there is no functional validator lambda which does not validate the subject.
            };
        };

        /**
         * Set the validator to optional, so it accepts missing values.
         *
         * @return     {is.__validationUnit}  The validator itself.
         */
        this.optional = function() {
            this.__required = false;
            return this;
        };

        /**
         * Add a new functional validation lambda to the validator.
         *
         * @param      {lambda}               lambda  The functional validator, which accesses the subject and returns true or false.
         * @return     {is.__validationUnit}          The validator itself.
         */
        this.Which = function(lambda) {
            this.__functionalValidators.push(lambda);
            return this;
        }

        /**
         * Set the validator to validate strings.
         *
         * @return     {is.__validationUnit}  The validator itself.
         */
        this.String = function() {
            this.__type = "string";
            this.__validateType = (value) => (
                typeof value === "string"
            );
            return this;
        };

        /**
         * Set the validator to validate booleans.
         *
         * @return     {is.__validationUnit}  The validator itself.
         */
        this.Boolean = function() {
            this.__type = "boolean";
            this.__validateType = (value) => (
                typeof value === "boolean"
            );
            return this;
        }

        /**
         * Set the validator to validate numbers.
         *
         * @return     {is.__validationUnit}  The validator itself.
         */
        this.Number = function() {
            this.__type = "number";
            this.__validateType = (value) => (
                typeof value === "number"
            );
            return this;
        };

        /**
         * Set the validator to validate objects.
         *
         * @param      {Object}               schemaObj  The schema of the object to validate, made up of other validators.
         * @return     {is.__validationUnit}             The validator itself.
         */
        this.Object = function(schemaObj) {
            this.__type = "object";
            this.__schemaObj = schemaObj || {};
            this.__validateType = (value) => {
                if (typeof value !== "object") {
                    return false;
                };
                for (const schemaKey in this.__schemaObj) {
                    // here we iterate over the keys of the schema
                    // and validate the corresponding properties of the value object.
                    if (!schemaObj[schemaKey].validate(value[schemaKey])) {
                        return false;
                    };
                };
                return true;
            };
            return this;
        };

        /**
         * Set the validator to validate arrays.
         *
         * @param      {is.__validationUnit}  schema  The validator which must validate every element of the array.
         * @return     {is.__validationUnit}          The validator itself.
         */
        this.ArrayOf = function(schema) {
            this.__type = "array";
            this.__schema = schema;
            this.__validateType = (value) => {
                if (Array.isArray(value)) {
                    return value.filter(e => !this.__schema.validate(e)).length == 0;
                    // ^ == there is no item in the array which is not validated by the schema
                } else {
                    return (!this.__required && typeof value === "undefined");
                };
            };
            return this;
        };

        /**
         * Set the validator to validate a variable type.
         *
         * @param      {is.__validationUnit}  schemaArray  An array of other validators.
         * @return     {is.__validationUnit}               The validator itself.
         */
        this.OneOf = function(schemaArray) {
            this.__type = "various";
            this.__schemaArray = schemaArray;
            this.__validateType = (value) => {
                if (!this.__required && typeof value === "undefined") {
                    return true;
                } else {
                    return this.__schemaArray.filter(schema => schema.validate(value)).length > 0;
                    // ^ == there is at least one schema which validates the value
                };
            };
            return this;
        }

        return this;
    },
};

/**
 * Create a new validator object that is optional.
 *
 * @return     {is.__validationUnit}  A new optional validator object.
 */
is.optional = () => (new is.__validationUnit().optional());

/**
 * Create a new functional validator.
 *
 * @param      {Function}             functionalValidator  The functional validator which checks the subject for functional requirements.
 * @return     {is.__validationUnit}                       The new functional validator.
 */
is.Which = (functionalValidator) => (new is.__validationUnit().Which(functionalValidator));

/**
 * Create a new string validator.
 *
 * @return     {is.__validationUnit}  The new string validator.
 */
is.String = () => (new is.__validationUnit().String());

/**
 * Create a new boolean validator.
 *
 * @return     {is.__validationUnit}  The new boolean validator.
 */
is.Boolean = () => (new is.__validationUnit().Boolean());

/**
 * Create a new number validator.
 *
 * @return     {is.__validationUnit}  The new number validator.
 */
is.Number = () => (new is.__validationUnit().Number());

/**
 * Create an object validator.
 *
 * @param      {Object}               schemaObj  The schema object, made up of other validators.
 * @return     {is.__validationUnit}             The new object validator.
 */
is.Object = (schemaObj) => (new is.__validationUnit().Object(schemaObj));

/**
 * Create an array validator.
 *
 * @param      {is.__validationUnit}  schema  Any validator that the array elements must validate against.
 * @return     {is.__validationUnit}          The new array validator.
 */
is.ArrayOf = (schema) => (new is.__validationUnit().ArrayOf(schema));

/**
 * Create a variable type validator.
 *
 * @param      {is.__validationUnit[]}  schemaArray  Array of validator objects the subject must match against (at least one of them).
 * @return     {is.__validationUnit}                 The new variable type validator.
 */
is.OneOf = (schemaArray) => (new is.__validationUnit().OneOf(schemaArray));

export { is };