diff --git a/index.js b/index.js index 8d43a52..856f4d7 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ // TODO: Rename this to something more appropriate, like any/anyOf? How to make sure that doesn't cause confusion with `oneOf`? const splitFilter = require("split-filter"); +const flatten = require("flatten"); const ValidationError = require("@validatem/error"); const combinator = require("@validatem/combinator"); @@ -10,6 +11,18 @@ const validationResult = require("@validatem/validation-result"); const areErrorsFromSameOrigin = require("./src/are-errors-from-same-origin"); +function unpackNestedEithers(errors) { + // NOTE: We only unpack `either` errors that occurred *at the same level* as this error, ie. where there's directly a case of `either(either(...), ...)`, without any kind of data nesting (like `arrayOf` or `hasShape`) inbetween. Nested-data failures should still be shown separately, as their resolution strategy is actually different; unlike same-level nested `either` errors, where the nesting is purely an implementation detail that allows composing sets of alternatives together. + + return flatten(errors.map((error) => { + if (error.path.length === 0 && error.__isValidatemEitherError) { + return error.subErrors; + } else { + return error; + } + })); +} + module.exports = function (alternatives) { if (!Array.isArray(alternatives)) { throw new Error(`Must specify an array of alternatives`); @@ -27,8 +40,12 @@ module.exports = function (alternatives) { if (errors.length === 0) { return newValue; + } else if (errors.length === 1) { + allErrors.push(errors[0]); } else { - allErrors.push(...errors); + allErrors.push(new ValidationError("Must satisfy all of the following:", { + subErrors: errors + })); } } @@ -54,7 +71,10 @@ module.exports = function (alternatives) { // One of the alternatives *did* match, but it failed somewhere further down the tree, and we don't have any errors originating from *other* nested rules. Chances are that the user intended to match the failing branch, so we pretend that the other alternatives don't exist, and pass through the original error(s). return validationResult({ errors: nestedErrors }); } else { - throw new ValidationError(`Must satisfy at least one of:`, { subErrors: allErrors }); + throw new ValidationError(`Must satisfy at least one of:`, { + subErrors: unpackNestedEithers(allErrors), + __isValidatemEitherError: true + }); } }); } diff --git a/package.json b/package.json index 04ca4da..1dcd97d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@validatem/combinator": "^0.1.1", "@validatem/error": "^1.0.0", "@validatem/validation-result": "^0.1.2", + "flatten": "^1.0.3", "split-filter": "^1.1.3" } }