"use strict"; const asExpression = require("as-expression"); const defaultValue = require("default-value"); const flat = require("array.prototype.flat"); const ValidationError = require("@validatem/error"); const validationResult = require("@validatem/validation-result"); const annotateErrors = require("@validatem/annotate-errors"); const combinator = require("@validatem/combinator"); function containsRules(rules) { // TODO: We cannot use normalize-rules here (for now) since that would create a cyclical dependency; figure out a way to work around this, maybe by removing the hasShape feature in `normalize-rules`, or moving the flattening itself out into a separate `flatten-rules` package? if (rules == null) { return false; } else { // TODO: Switch to `Array#flat` once it is available let flattenedRules = flat(rules); if (!flattenedRules.some((rule) => rule != null)) { return false; } else { return true; } } } module.exports = function hasShape(rules) { return combinator((object, applyValidators, context) => { let allowExtraProperties = defaultValue(context.allowExtraProperties, false); let unexpectedPropertyErrors = asExpression(() => { if (!allowExtraProperties) { return Object.keys(object).map((propertyName) => { if (!containsRules(rules[propertyName])) { return new ValidationError(`Encountered an unexpected property '${propertyName}'`); } else { return null; } }).filter((error) => { return (error != null); }); } else { return []; } }); if (unexpectedPropertyErrors.length > 0) { return validationResult({ errors: unexpectedPropertyErrors, newValue: object }); } else { let errors = []; let newObject = {}; for (let [ key, rule ] of Object.entries(rules)) { let value = object[key]; let { errors: keyErrors, newValue } = applyValidators(value, rule); let annotatedErrors = annotateErrors({ pathSegments: [ key ], errors: keyErrors }); errors.push(... annotatedErrors); newObject[key] = newValue; } return validationResult({ errors: errors, newValue: newObject }); } }); };