{"id":13801461,"url":"https://github.com/jupegarnica/garn-validator","last_synced_at":"2025-04-13T12:24:43.376Z","repository":{"id":42747045,"uuid":"280854988","full_name":"jupegarnica/garn-validator","owner":"jupegarnica","description":"Create validations with ease","archived":false,"fork":false,"pushed_at":"2024-04-23T15:41:35.000Z","size":2374,"stargazers_count":41,"open_issues_count":7,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-27T03:22:44.757Z","etag":null,"topics":["checker","deno","nodejs","primitives-checking","schema","type","types","validation","validator"],"latest_commit_sha":null,"homepage":"https://npmjs.com/garn-validator","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jupegarnica.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-19T11:51:45.000Z","updated_at":"2024-04-23T15:41:38.000Z","dependencies_parsed_at":"2024-06-21T16:49:53.658Z","dependency_job_id":"639a445f-9d90-4494-a7d3-95cf86689c5f","html_url":"https://github.com/jupegarnica/garn-validator","commit_stats":{"total_commits":486,"total_committers":3,"mean_commits":162.0,"dds":"0.12962962962962965","last_synced_commit":"e252c936bd6bdaeaafd5e8604d9be6c3c6a764ab"},"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jupegarnica%2Fgarn-validator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jupegarnica%2Fgarn-validator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jupegarnica%2Fgarn-validator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jupegarnica%2Fgarn-validator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jupegarnica","download_url":"https://codeload.github.com/jupegarnica/garn-validator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248317727,"owners_count":21083528,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["checker","deno","nodejs","primitives-checking","schema","type","types","validation","validator"],"created_at":"2024-08-04T00:01:23.045Z","updated_at":"2025-04-13T12:24:43.327Z","avatar_url":"https://github.com/jupegarnica.png","language":"JavaScript","readme":"\u003ch1\u003egarn-validator:  \u003ci\u003eCreate validations with ease\u003c/i\u003e \u003c/h1\u003e\n\n\u003ch2\u003eFeatures\u003c/h2\u003e\n\n- Supports checking primitives or objects with **schemas**\n- Apply default value if validation fails.\n- Easy to use and learn but **powerful**\n- It's totally **composable**\n- **Fast** and **without dependencies**\n- **Six behaviors**:\n  - `mustBe` returns the value evaluated or it throws. (default export)\n  - `isValid` returns `true` or `false`, never throws\n  - `isValidOrLog` returns `true` or `false` and log error, never throws\n  - `hasErrors` returns null or Array of errors, never throws\n  - `mustBeOrThrowAll` returns the value evaluated or throws AggregateError\n  - `isValidOrLogAll` returns `true` or `false` and log all errors, never throws\n- Works with ESModules or CommonJS from **Node** 10.x or **Deno**\n- Works in all modern browsers\n- Works in all frontend frameworks: **React, Angular, Vue,** etc...\n\n[![npm version](https://img.shields.io/npm/v/garn-validator?logo=npm)](https://www.npmjs.com/package/garn-validator)\n[![npm downloads](https://img.shields.io/npm/dm/garn-validator.svg?logo=npm\u0026color=blue)](https://www.npmjs.com/package/garn-validator)\n[![Node Tests CI](https://github.com/jupegarnica/garn-validator/workflows/Node%20Tests%20CI/badge.svg?branch=master)](https://github.com/jupegarnica/garn-validator/actions?query=workflow%3A%22Node+Tests+CI%22)\n[![Deno Tests CI](https://github.com/jupegarnica/garn-validator/workflows/Deno%20Tests%20CI/badge.svg?branch=master)](https://github.com/jupegarnica/garn-validator/actions?query=workflow%3A%22Deno+Tests+CI%22)\n\n\u003e DEPRECATION WARNING: `isValidOrThrow` behavior has been deprecated in favor of `mustBe`. Learn more at [mustBe](#mustbe)\n\n\u003ch2\u003eExample\u003c/h2\u003e\n\n```js\nimport mustBe, { isValid, isValidOrLog } from \"garn-validator\";\n\nconst isValidPassword = isValid(\n  String, //  must be String\n  (str) =\u003e str.length \u003e= 8, // and length \u003e= 8\n  /[a-z]/, // and have at least one lowercase\n  /[A-Z]/, // and have at least one uppercase\n  /[0-9]/, // and have at least one digit\n  /[-_/!¡?¿$%\u0026/()]/ // and have at least one especial character\n);\n\nisValidPassword(\"12345Aa?\"); // returns true\n\nconst isValidName = mustBe(String, (name) =\u003e name.length \u003e 3).or(\"anonymous\"); // will auto correct to 'anonymous' if fails\n\n\nconst isValidAge = isValidOrLog(\n  Number,\n  (age) =\u003e age \u003e 18,\n  (age) =\u003e age \u003c 40\n);\n\nisValidAge(15); // false, and logs 15 do not match validator (age) =\u003e age \u003e 18\n\n// Composition\nconst isValidUser = mustBe({\n  name: isValidName,\n  age: isValidAge,\n  password: isValidPassword,\n  country: [\"ES\", \"UK\"], // 'ES' or 'UK'\n});\n\nconst newUser = isValidUser({\n  name: \"\", // will be fixed\n  age: 38,\n  password: \"12345zZ?\",\n  country: \"ES\",\n}); // returns { name: 'anonymous', age: 38, password: '12345zZ?', country: 'ES' }\n\nconst anotherUser = isValidUser({\n  name: \"garn\",\n  age: 38,\n  password: \"1234\", // incorrect\n  country: \"ES\",\n}); // it throws --\u003e TypeValidationError: At path /password \"1234\" do not match validator (str) =\u003e str.length \u003e= 8\n```\n\n\u003ch1\u003eContents\u003c/h1\u003e\n\n- [Get started](#get-started)\n  - [Node](#node)\n    - [Import with ES Modules](#import-with-es-modules)\n    - [Require with CommonJs](#require-with-commonjs)\n  - [Deno](#deno)\n  - [Basic Usage](#basic-usage)\n    - [Check against constructor](#check-against-constructor)\n    - [Check against primitive](#check-against-primitive)\n    - [Check string against regex](#check-string-against-regex)\n    - [Check against custom function](#check-against-custom-function)\n    - [Check against enums (OR operator)](#check-against-enums-or-operator)\n    - [Check multiple validations (AND operator)](#check-multiple-validations-and-operator)\n    - [Check object against an schema](#check-object-against-an-schema)\n  - [Behaviors](#behaviors)\n    - [mustBe](#mustbe)\n    - [isValid, isValidOrLog and isValidOrLogAll](#isvalid-isvalidorlog-and-isvalidorlogall)\n    - [hasErrors](#haserrors)\n    - [mustBe vs mustBeOrThrowAll](#mustbe-vs-mustbeorthrowall)\n  - [Utils](#utils)\n    - [Logical utils](#logical-utils)\n    - [Numeric utils](#numeric-utils)\n- [In depth](#in-depth)\n  - [Types of validations](#types-of-validations)\n    - [Primitives](#primitives)\n    - [Constructors](#constructors)\n      - [Proxy detection](#proxy-detection)\n    - [RegExp](#regexp)\n    - [Custom function](#custom-function)\n    - [Enums](#enums)\n    - [Schema](#schema)\n      - [Optional Keys](#optional-keys)\n      - [Regexp keys](#regexp-keys)\n      - [Custom validation used in schemas](#custom-validation-used-in-schemas)\n    - [Validations in serie (AND operator)](#validations-in-serie-and-operator)\n  - [Errors](#errors)\n    - [AggregateError](#aggregateerror)\n      - [SchemaValidationError](#schemavalidationerror)\n      - [EnumValidationError](#enumvalidationerror)\n      - [SerieValidationError](#serievalidationerror)\n      - [hasErrors](#haserrors-1)\n    - [Raw Error data](#raw-error-data)\n    - [Composition in depth](#composition-in-depth)\n  - [Especial cases](#especial-cases)\n    - [AsyncFunction \u0026 GeneratorFunction](#asyncfunction--generatorfunction)\n- [Roadmap](#roadmap)\n\n# Get started\n\n## Node\n\n```bash\nnpm install garn-validator\n```\n\n### Import with ES Modules\n\n```js\n// default export is mustBe\nimport mustBe from \"garn-validator\";\n// or use named export\nimport { mustBe } from \"garn-validator\";\n```\n\n### Require with CommonJs\n\n```js\nconst { mustBe } = require(\"garn-validator/commonjs\");\n// or use de default export\nconst mustBe = require(\"garn-validator/commonjs\").default;\n```\n\n## Deno\n\nThe library can be used as is in typescript\n\nImport from deno third party modules: [deno.land/x/garn_validator](https://deno.land/x/garn_validator)\n\n```ts\n// mod.ts\nimport mustBe from \"https://deno.land/x/garn_validator/src/index.js\";\n```\n\nTo have type definitions you can do:\n\n```js\n\nimport * as garnValidator from \"https://deno.land/x/garn_validator/src/index.js\";\nimport * as ValidatorTypes from \"https://deno.land/x/garn_validator/src/index.d.ts\";\ngarnValidator as typeof ValidatorTypes;\n\nconst { mustBe } = garnValidator;\n\n```\n\n\u003c!-- TODO ## Browser --\u003e\n\u003c!-- https://jspm.org/ --\u003e\n\n## Basic Usage\n\n```js\nimport mustBe from \"garn-validator\"; // default export is mustBe\n\nconst isValidUser = mustBe({ name: String, age: Number });\n\nisValidUser({ name: \"garn\", age: 38 }); // returns { name: \"garn\", age: 38 }\nisValidUser({ name: \"garn\", age: \"38\" }); // it throws\n```\n\n### Check against constructor\n\n```js\nmustBe(Number)(2); // returns 2\nmustBe(String)(2); // it throws\nmustBe(Array)([1, 2]); // returns [1, 2]\nmustBe(Object)([1, 2]); // it throws\n```\n\nLearn more in depth at [Constructors](#constructors)\n\n### Check against primitive\n\n```js\nmustBe(\"a\")(\"a\"); // returns \"a\"\nmustBe(true)(false); // it throws\n```\n\nLearn more in depth at [Primitives](#primitives)\n\n### Check string against regex\n\n```js\nmustBe(/a*/)(\"a\"); // returns \"a\"\nmustBe(/a/)(\"b\"); // it throws\n```\n\nLearn more in depth at [RegExp](#regexp)\n\n### Check against custom function\n\n```js\nmustBe((value) =\u003e value \u003e 0)(33); // returns 33\nmustBe((value) =\u003e value \u003e 0)(-1); // wil throw\nmustBe(Number.isNaN)(NaN); // returns NaN\nmustBe(Number.isInteger)(1.1); // wil throw\n```\n\nLearn more in depth at [Custom function](#custom-function)\n\n### Check against enums (OR operator)\n\n```js\nmustBe([\"a\", \"b\"])(\"a\"); // returns \"a\"\nmustBe([\"a\", \"b\"])(\"c\"); // it throws\nmustBe([Number, String])(\"18\"); // returns \"18\"\nmustBe([null, undefined, false, 0, \"\"])(18); // it throws\n```\n\nLearn more in depth at [Enums](#enums)\n\n### Check multiple validations (AND operator)\n\n```js\nmustBe(Array, (array) =\u003e array.length === 2)([1, 2]); // returns [1, 2]\nmustBe(\n  (v) =\u003e v \u003e 0,\n  (v) =\u003e v \u003c 50\n)(100); // it throws\n```\n\nLearn more in depth at [Validations in serie (AND operator)](#validations-in-serie-and-operator)\n\n### Check object against an schema\n\n```js\nconst schema = { a: Number, b: Number }; // a and b are required\nconst obj = { a: 1, b: 2 };\nmustBe(schema)(obj); // returns obj\n\nmustBe({ a: 1 })({ a: 1, b: 2 }); // returns { a: 1, b: 2 },  because b is not checked\nmustBe({ c: Number })({ a: 1, b: 2 }); // it throws (c is missing)\n\n// Optional keys\nmustBe({ x$: String })({}); // returns {}\n```\n\nLearn more in depth at [Schema](#schema)\n\n## Behaviors\n\nAll behaviors run the same algorithm but differs in what returns and how behaves.\n\nThere are six behaviors that can be divided in two categories:\n\n- It stops in first error (bail):\n\n  - `mustBe` returns the value evaluated or it throws. (default export)\n  - `isValid` returns `true` or `false`, never throws\n  - `isValidOrLog` returns `true` or `false` and log error, never throws\n\n- It collects all Errors:\n  - `hasErrors` returns null or Array of errors, never throws\n  - `mustBeOrThrowAll` returns the value evaluated or throws AggregateError\n  - `isValidOrLogAll` returns `true` or `false` and log all errors, never throws\n\n### mustBe\n\n`mustBe` returns the value evaluated or it throws.\n\n```js\nlet input = \"Garn\";\nlet userName = mustBe(String)(input); //  return 'Garn'\n```\n\n```js\nlet input = \"Garn\";\nlet userName;\nlet isValidName = mustBe(String, (val) =\u003e val.length \u003e 4);\ntry {\n  userName = isValidName(input); //  it throws\n} catch (err) {\n  userName = \"anonymous\";\n}\n```\n\n`mustBe` may have attached an .or() to apply a default value if the validation fail.\n\n```js\nlet input = \"Garn\";\nlet isValidName = mustBe(String, (val) =\u003e val.length \u003e 4).or(\"anonymous\");\nlet userName = isValidName(input); //  returns 'anonymous'\n```\n\nThe .or() can receive a function to apply a transformation to the original value.\n\n```js\nlet input = \"42\";\nlet asNumber = mustBe(Number).or((value /* \"42\" */) =\u003e Number(value));\nlet number = asNumber(input); //  returns 42\n```\n\nIf you need to apply a function as default, you can use a function the returns a function.\n\n```js\nlet input = \"i am not a function\";\nlet noop = () =\u003e {};\nlet mustBeFunction = mustBe(Function).or(() =\u003e noop);\nlet fn = mustBeFunction(input); //  returns () =\u003e {}\n```\n\nIt can work nested in a schema\n\n```js\nlet input = { name: \"Garn\" };\nlet isValidName = mustBe(String, (val) =\u003e val.length \u003e 4).or(\"anonymous\");\n\nlet user = mustBe({ name: isValidName })(input); // { name:'anonymous' }\n```\n\nIf the .or() fails the whole validation will fail\n\n```js\nlet input = \"not a valid number\";\nlet transformToNumberIfPosible = (maybeNumber) =\u003e {\n  let number = Number(maybeNumber);\n  if (number == maybeNumber) return number;\n  else throw new TypeError(\"not valid number\");\n};\nlet asNumber = mustBe(Number).or(transformToNumberIfPosible);\nlet number = asNumber(input); //  it throws CastError (aggregateError) with TypeError: not valid number within its array of errors\n```\n\n### isValid, isValidOrLog and isValidOrLogAll\n\n`isValid` returns `true` or `false` never throws, so if it fails for any reason you should know it won't tell you anything but false.\n\n```js\nimport { isValid } from \"garn-validator\";\n\n// stops in first Error\nisValid(/[a-z]/)(\"g\"); // returns true\nisValid(/[a-z]/)(\"G\"); // returns false, doesn't throws\n```\n\n`isValidOrLog` is the same as `isValid` but log first error found and stops validating.\n\n`isValidOrLogAll` returns `true` or `false` and log all errors, never throws\n\n```js\nimport { isValidOrLog } from \"garn-validator\";\n\n// stops in first Error\nisValidOrLog(/[a-z]/)(\"g\"); // do nothing (but also returns true)\nisValidOrLog(/[a-z]/)(\"G\"); // logs error and return false\n```\n\n### hasErrors\n\n`hasErrors` returns null or Array with all errors found, never throws.\n\nIt's very useful to show the user all errors that need to be fixed.\n\n```js\nimport { hasErrors } from \"garn-validator\";\n\n// return null or array or errors\n// checks until the end\nhasErrors(/[a-z]/)(\"g\"); // null\nhasErrors(/[a-z]/, Number)(\"G\"); // [TypeValidationError, TypeValidationError]\n```\n\n### mustBe vs mustBeOrThrowAll\n\n`mustBe` returns the value evaluated or it throws the first error found.\n\n```js\ntry {\n  mustBe({ a: Number, b: String })({ a: null, b: null });\n} catch (error) {\n  error instanceof TypeValidationError; // true\n  error.message; // At path /a null do not match constructor Number\n}\n```\n\n`mustBeOrThrowAll` returns the value evaluated or it throws an [`AggregateError`](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/AggregateError) with all errors found.\n\n```js\ntry {\n  mustBeOrThrowAll({ a: Number, b: String })({ a: null, b: null });\n} catch (error) {\n  error instanceof AggregateError; // true\n  error instanceof SchemaValidationError; // true\n  console.log(error);\n  /*\n    SchemaValidationError: value {\"a\":null,\"b\":null} do not match schema {\"a\":Number,\"b\":String}\n  */\n  console.log(error.errors);\n  /*\n    [\n      TypeValidationError: At path /a null do not match constructor Number ,\n      TypeValidationError: At path /b null do not match constructor String ,\n    ]\n  */\n}\n```\n\nBut if it finds only one error, it will throw `TypeValidationError`, no AggregateError\n\n```js\ntry {\n  mustBeOrThrowAll({ a: Number, b: String })({ a: null, b: \"str\" });\n} catch (error) {\n  console.log(error);\n  /*\n    TypeValidationError: At path /a null do not match constructor Number ,\n  */\n  error instanceof TypeValidationError; // true\n  error instanceof SchemaValidationError; // false\n}\n```\n\nLearn more at [Errors](#errors)\n\n## Utils\n\nThe library has a bunch of pre-made validations (and growing), which makes easier validations of stings, numbers, objects or dates.\n\nLearn more at [utils test](https://github.com/jupegarnica/garn-validator/blob/1cd727ae647a7a79ffe4ba49312513193d683bdb/tests/utils.test.js)\n\n```js\nimport {\n  mustBe,\n  arrayOf,\n  and,\n  Integer,\n  Numeric,\n  Positive,\n  Lowercase,\n  before,\n} from \"garn-validator\";\n\nlet Schema = {\n  name: Lowercase,\n  birthday: before(new Date()),\n  height: and(Number, Positive, Integer),\n  creditCard: Numeric,\n  books_id: arrayOf(String),\n};\n\nlet data = {\n  name: \"garn\",\n  birthday: \"1982-03-16\",\n  height: 170,\n  creditCard: \"42424242424242\",\n  books_id: [\"123\", \"321\"],\n};\n\nlet user = mustBe(Schema).or(null)(data);\n```\n\n### Logical utils\n\n**`or(...validations)`** Is just a shortcut to an enum.\n\n\u003e Not to be confused with `mustBe().or()`\n\n```js\nimport { mustBe, or } from \"garn-validator\";\n\nmustBe(or(Number, String));\n// same as:\nmustBe([Number, String]);\n```\n\n**`and(...validations)`** is a shortcut to `mustBe(...args)`, but semantically useful.\n\n```js\nimport { mustBe, and } from \"garn-validator\";\n\nmustBe({ age: and(Number, (num) =\u003e num \u003e 18) });\n// same as:\nmustBe({ age: mustBe(Number, (num) =\u003e num \u003e 18) });\n```\n\n**`not(...validations)`** it negates the validations passed\n\n```js\nimport { mustBe, not } from \"garn-validator\";\n\n// anything but Number\nmustBe(not(Number))(\"qwerty\"); // valid, return 'qwerty'\n```\n\u003c!--\n### Numeric utils\n\nIn order to validate numbers we have a few pseudo-type as Integer, or Numeric.\n\n```js\nimport {\n  mustBe,\n  Integer,\n  Finite,\n  Numeric,\n  Odd,\n  Even,\n  Positive,\n  Negative,\n  SafeInteger,\n  SafeNumber,\n} from \"garn-validator\";\n\nlet schema = {\n  int: Integer,\n  finite: Finite,\n  string: Numeric,\n  bigInt: Numeric,\n  number: Numeric,\n  odd: Odd,\n  even: Even,\n  positive: Positive,\n  Negative: Negative,\n  safeInt: SafeInteger,\n  safeNum: SafeNumber,\n};\nlet numbers = {\n  int: 2.1, // will fail\n  finite: Infinity, // will fail\n  string: \"42\", // ok\n  bigInt: 123456789n, // ok\n  number: 42, // ok\n  odd: 1, // ok\n  even: 2, // ok\n  positive: 1, // ok\n  Negative: -1, // ok\n  safeInt: 1e53, // will fail\n  safeNum: 99999999999999.999999999, // will fail\n};\nmustBe(schema)(numbers);\n```\n\nWe also have custom validators as between(), or le().\n\n```js\nimport {\n  mustBe,\n  between,\n  le,\n\n} from \"garn-validator\";\n\n// let schema = {\n//   int: Integer,\n//   finite: Finite,\n\n// };\n// let numbers = {\n//   int: 2.1, // will fail\n\n// };\n// mustBe(schema)(numbers);\n``` --\u003e\n\u003c!-- TODO STRING UTILS --\u003e\n\n### Object utils\n\n**arrayOf()**\n\nAs we use the array `[]` as enum, if you need to check the items of an array, you should treat it as an object and check against an schema.\n\n```js\nimport mustBe from \"garn-validator\";\n\nmustBe(Array, { [/\\d/]: Number })([1, 2, 3]); // returns [1, 2, 3]\nmustBe(Array, { [/\\d/]: Number })([1, 2, \"3\"]); // throws\n```\n\nIn order to not be so ugly you can import `arrayOf` from garn-validator as a shortcut to:\n\n`export const arrayOf = type =\u003e isValid(Array, {[/^\\d$/]: type})`\n\n```js\nimport mustBe, { arrayOf } from \"garn-validator\";\n\nmustBe(arrayOf(Number))([1, 2, 3]); // returns [1, 2, 3]\nmustBe(arrayOf(Number))([1, 2, \"3\"]); // throws\n```\n\n**objectOf**\n\nYou can import `objectOf` from garn-validator as a shortcut to:\n\n`export const objectOf = type =\u003e isValid(Object, {[/./]: type})`\n\n```js\nimport mustBe, { objectOf } from \"garn-validator\";\n\nmustBe(objectOf(Number))({ a: 1, b: 2 }); // returns { a: 1, b: 2 }\nmustBe(objectOf(Number))({ a: 1, b: \"2\" }); // throws\n```\n\n# In depth\n\n## Types of validations\n\nThere are six types of validations: Primitives, Constructors, RegExp, Enums, Schemas and Custom functions\n\n### Primitives\n\nChecking a primitive is a === comparison\n\nAnything that is not and object in JS is a primitive: `Number`, `String`, `undefined`, `null` and `Symbol`\n\n```js\nmustBe(1)(1); // returns 1 --\u003e 1 === 1\n\nmustBe(\"1\")(1); // throws,  '1' !== 1\n\nmustBe(1n)(1); // throws,  1n !== 1\n\nmustBe(undefined)(null); // throws,  undefined !== null\n\n// keep in mind than a symbol is only equal to itself\nlet s = Symbol();\nmustBe(s)(s); // returns s\nmustBe(s)(Symbol()); // throws\n```\n\n### Constructors\n\nChecking against a constructor means to know if the value evaluated has been created from that constructor\n\n```js\nmustBe(Number)(2); // (2).constructor === Number  --\u003e true\nmustBe(Symbol)(Symbol()); // ok\n```\n\nA valid constructor is a `class` or any built-in constructor.\n\n```js\nclass Car {}\nlet honda = new Car();\nmustBe(Car)(honda); // honda.constructor === Car  --\u003e true\n```\n\n\u003e **You can't use a normal function used as constructor from the old JS times.**\n\n```js\nfunction Car(name) {\n  this.name = name;\n}\nlet honda = new Car(\"honda\");\nmustBe(Car)(honda); // throws.  Car is detected as custom validator function\n```\n\nAll [built in Constructors](https://github.com/jupegarnica/garn-validator/blob/master/src/constructors.js#L47) are supported\n\n#### Proxy detection\n\n\u003e NOT YET WORKING IN DENO\n\nIn order to detect any Object (or Array) is a Proxy we intercept the creation of Proxies.\n\nTo have that functionality you must `import \"garn-validator/src/proxyDetection.js\"` before any creation of Proxies you need to detect;\n\n```js\nimport \"garn-validator/src/proxyDetection.js\";\n\nconst target = { a: 1 };\nconst proxy = new Proxy(target, {\n  get: () =\u003e 33,\n});\n\nmustBe(Proxy)(proxy); // returns proxy\n```\n\nwithout running garn-validator/src/proxyDetection.js\n\n```js\n// NO IMPORT\nconst target = { a: 1 };\nconst proxy = new Proxy(target, {\n  get: () =\u003e 33,\n});\n\nmustBe(Proxy)(proxy); // fails\n```\n\n### RegExp\n\nThe perfect validator to check strings. It does what you expect:\n\n```js\nlet isLowerCased = mustBe(/^[a-z]+$/);\n\nisLowerCased(\"honda\"); // /^[a-z]+$/.test('honda') --\u003e true\n// or building a regexp with the constructor RexExp;\nmustBe(new RegExp(/^[a-z]+$/))(\"honda\"); //  true\n```\n\n### Custom function\n\nAny function that is not a constructor is treated as custom validator.\n\nIt must return any truthy value in order to pass the validation.\n\n```js\nmustBe((val) =\u003e val \u003e= 0)(10); // returns 10\nmustBe((val) =\u003e val \u003e= 0)(-10); // throws\n\nmustBe(() =\u003e \"I am truthy\")(10); // returns 10\nmustBe(() =\u003e [])(10); // returns 10\n```\n\nTo fail a validation may return a falsy value or throw an error.\n\nIf it returns a falsy value, the default error will be thrown: TypeValidationError\n\nIf it throws an error, that error will be thrown.\n\n```js\nmustBe(() =\u003e false)(10); // throws TypeValidationError\nmustBe(() =\u003e 0)(10); // throws TypeValidationError\n\nmustBe(() =\u003e {\n  throw new RangeError(\"ups\");\n})(10); // throws RangeError\n\nmustBe(() =\u003e {\n  throw \"ups\";\n})(10); // throws 'ups'\n```\n\n### Enums\n\nEnums works as OR operator. Must be an array which represent all options.\n\n```js\nlet cities = [\"valencia\", \"new york\", \"salzburg\"];\nmustBe(cities)(\"valencia\"); // returns \"valencia\"\nmustBe(cities)(\"madrid\"); // throws\n```\n\nBut it's much more powerful than checking against primitives. It can contain any type of validator.\n\nIt checks every item until one passes.\n\n```js\nlet isNumberOrBigInt = [Number, BigInt]; // must be Number or BigInt\nmustBe(isNumberOrBigInt)(1n); // returns 1n\nmustBe(isNumberOrBigInt)(1); // returns 1\n\nlet isFalsy = [0, \"\", null, undefined, false];\nmustBe(isFalsy)(\"\"); // returns \"\"\n\nlet isNumberAlike = [Number, (val) =\u003e val === Number(val)];\nmustBe(isNumberAlike)(1n); // returns 1n\nmustBe(isNumberAlike)(1); // returns 1\nmustBe(isNumberAlike)(\"1\"); // returns \"1\"\n```\n\n### Schema\n\nAn Schema is just a plain object telling in each key which validation must pass.\n\n```js\nlet schema = {\n  name: String, // required and be a Number\n  age: (age) =\u003e age \u003e 18, // required and be a greater than 18\n  tel: [Number, String], // required and be Number or String\n  role: [\"admin\", \"user\"], // required and be 'admin' or 'user'\n  credentials: {\n    // must be and object and will be validate with this \"subSchema\"\n    pass: String,\n    email: String,\n  },\n};\nlet obj = {\n  name: \"garn\",\n  age: 20,\n  tel: \"+34617819234\",\n  role: \"admin\",\n  credentials: {\n    pass: \"1234\",\n    email: \"email@example.com\",\n  },\n};\nmustBe(schema)(obj); // returns obj\n```\n\n\u003e **Only the keys in the schema will be checked. Any key not present in the schema won't be checked (under consideration to be changed)**\n\n```js\nmustBe({})({ a: 1 }); // returns { a: 1 } , a is not in the schema\n```\n\n#### Optional Keys\n\nAnd optional key must be `undefined` , `null`, or pass the validation\n\n```js\nmustBe({ x$: Number })({ x: 1 }); // returns { x: 1 }, x is present and is Number\nmustBe({ x$: String })({ x: 1 }); // it throws, x is present but is not String\n\nmustBe({ x$: String })({}); // returns {}, x is undefined\nmustBe({ x$: String })({ x: undefined }); // returns { x: undefined }, x is undefined\nmustBe({ x$: String })({ x: null }); // returns { x: null }, x is null\n```\n\nYou can use `key$` or `'key?'`. It would be nicer to have `key?` without quotes but is not valid JS\n\n```js\nmustBe({ \"x?\": String })({}); // returns {}\n```\n\n#### Regexp keys\n\nYou can validate multiple keys at once using a regexp key\n\n```js\nmustBe({\n  [/./]: String,\n})({\n  a: \"a\",\n  b: \"b\",\n}); // ok\n\n// or write it as plain string\nmustBe({\n  \"/./\": String,\n})({\n  a: \"a\",\n  b: 1, // fails\n}); // throws\n\n// only checks the keys that matches regex\nmustBe({\n  [/^[a-z]+$/]: Number,\n})({\n  x: 1,\n  y: 2,\n  z: 3,\n  CONSTANT: \"foo\", // not checked\n}); // ok, all lowercased keys are numbers\n```\n\n\u003e **The required keys and optional won't be check against a regexp key**\n\n```js\nmustBe({\n  [/./]: Number,\n  x: String, //  this required key has priority against regex key\n})({\n  x: \"x\", // not checked as Number, checked as String\n}); // ok,  x is String\n\nmustBe({\n  [/./]: Number,\n  x: String,\n})({\n  x: \"x\", // not checked as Number, checked as String\n  y: \"y\", // checked as Number, fails\n}); // throw\n\nmustBe({\n  [/./]: Number,\n  $x: String,\n  y: String,\n})({\n  x: \"x\", // not checked as Number, checked as String\n  y: \"y\", // checked as String\n  z: \"z\", // checked as Number, fails\n}); // throw\n```\n\nThis feature is perfect to note that any key not specified in schema is not allowed\n\n```js\nmustBe({\n  x: String,\n  [/./]: () =\u003e false,\n})({\n  x: \"x\",\n  y: \"y\", // fails\n});\n```\n\n#### Custom validation used in schemas\n\nWhen using a custom validator inside an schema will be run with 3 arguments: `(value, root, keyName) =\u003e {}`\n\n- value: the value present in that key from the object\n- root: the whole object, not matter how deep the validation occurs\n- keyName: the name of the key to be checked.\n\n```js\n//  against root obj\nmustBe({\n  max: (val, root, keyName) =\u003e val \u003e root.min,\n  min: (val, root, keyName) =\u003e val \u003c root.max,\n})({\n  max: 1,\n  min: -1,\n}); // ok\n\nmustBe({\n  max: (val, root, keyName) =\u003e val \u003e root.min,\n  min: (val, root, keyName) =\u003e val \u003c root.max,\n})({\n  max: 10,\n  min: 50,\n}); // it throws\n\n// all key must be at least 3 characters\nmustBe({\n  [/./]: (val, root, keyName) =\u003e keyName.length \u003e 3,\n})({\n  max: 1, // key too short\n  longKey: 1, // valid key\n}); // it throws, max key is too short\n```\n\n### Validations in serie (AND operator)\n\nThe validator constructor can receive as many validations as needed.\n\nAll will be checked until one fails\n\n```js\nconst isArrayOfLength2 = mustBe(Array, (array) =\u003e array.length === 2);\nisArrayOfLength2([1, 2]); // returns [1, 2]\n\nmustBe(\n  (v) =\u003e v \u003e 0,\n  (v) =\u003e v \u003c 50 // will fail\n)(100); // it throws\n```\n\n```js\nconst isValidPassword = mustBe(\n  String, // must be an String\n  (str) =\u003e str.length \u003e= 8, // and its length must be at least 8\n  /[a-z]/, // and must have at least one lowercase\n  /[A-Z]/, // and must have at least one uppercase\n  /[0-9]/, // and must have at least one number\n  /[-_/!·$%\u0026/()]/ // and must have at least one especial character\n);\n\nisValidPassword(\"12345wW-\"); // returns \"12345wW-\"\nisValidPassword(\"12345\"); // fails\n```\n\n## Errors\n\nIf a validation fails it will throw `new TypeValidationError(meaningfulMessage)` which inherits from `TypeError`. It can be imported.\n\nIf it throws an error from a custom validator, that error will be thrown.\n\n```js\nimport { mustBe, TypeValidationError } from \"garn-validator\";\n\ntry {\n  mustBe(Boolean)(33);\n} catch (error) {\n  error instanceof TypeValidationError; // true\n  error instanceof TypeError; // true\n}\n\ntry {\n  mustBe(() =\u003e {\n    throw \"ups\";\n  })(33);\n} catch (error) {\n  error === \"ups\"; // true\n}\n\ntry {\n  mustBe(() =\u003e {\n    throw new RangeError(\"out of range\");\n  })(33);\n} catch (error) {\n  error instanceof RangeError; // true\n  error instanceof TypeError; // false\n}\n```\n\n### AggregateError\n\nThere are 3 types a `AggregateError` that can be thrown:\n\n- `SchemaValidationError`: thrown when more than one key fails checking an schema\n- `EnumValidationError`: thrown when all validations fails checking an enum\n- `SerieValidationError`: thrown when more than one validation fails checking an Serie\n\nAll of them inherits from `AggregateError` and has a property errors with an array of all errors collected\n\n```js\ntry {\n  mustBeOrThrowAll(Number, String)(null);\n} catch (error) {\n  error instanceof AggregateError; // true\n  console.log(error.errors);\n  /*\n    [\n      TypeValidationError: value null do not match constructor Number ,\n      TypeValidationError: value null do not match constructor String ,\n    ]\n  */\n}\n```\n\n#### SchemaValidationError\n\nIf using mustBeOrThrowAll more than one key fails checking an Schema , it will throw a SchemaValidationError with all Errors aggregated in error.errors.\n\nIf only one key fail it will throw only that Error (not an AggregateError)\n\nSchemaValidationError inherits from AggregateError,\n\n\u003e But if using `mustBe` only the first Error will be thrown.\n\n```js\n// more than 2 keys fails\ntry {\n  mustBeOrThrowAll({ a: 1, b: 2 })({});\n} catch (error) {\n  console.log(error instanceof SchemaValidationError); // true\n  console.log(error instanceof AggregateError); // true\n  console.log(error.errors.length); // 2\n}\n\n// only 1 key fails\ntry {\n  mustBeOrThrowAll({ a: 1 })({});\n} catch (error) {\n  console.log(error instanceof TypeError); // true\n  console.log(error instanceof SchemaValidationError); // false\n}\n```\n\n#### EnumValidationError\n\nIf all validations of an enum fails, it will throw a EnumValidationError with all Errors aggregated in error.errors\n\nBut if the length of the enum is 1, it will throw only that error.\n\nEnumValidationError inherits from AggregateError.\n\n```js\ntry {\n  mustBe([Boolean, String])(1);\n} catch (error) {\n  console.log(error instanceof EnumValidationError); // true\n  console.log(error instanceof AggregateError); // true\n}\n\ntry {\n  mustBe([Boolean])(1);\n} catch (error) {\n  console.log(error instanceof EnumValidationError); // false\n  console.log(error instanceof TypeError); // true\n}\n```\n\n#### SerieValidationError\n\nIf using mustBeOrThrowAll fails all validations of a serie , it will throw a SerieValidationError with all Errors aggregated in error.errors\n\nBut if the length of the enum is 1. it will throw only this error.\n\nSerieValidationError inherits from AggregateError.\n\n```js\ntry {\n  mustBeOrThrowAll(Boolean, String)(1);\n} catch (error) {\n  console.log(error instanceof SerieValidationError); // true\n  console.log(error instanceof AggregateError); // true\n}\n\ntry {\n  mustBeOrThrowAll(Boolean)(1);\n} catch (error) {\n  console.log(error instanceof SerieValidationError); // false\n  console.log(error instanceof TypeError); // true\n}\n```\n\n#### hasErrors\n\n`hasErrors` will flatMap all errors found. No AggregateError will be in the array returned.\n\n```js\nhasErrors(/[a-z]/)(\"g\"); // null\nhasErrors(/[a-z]/, Number)(\"G\");\n/*\n[\n  TypeValidationError: value \"G\" do not match regex /[a-z]/,\n  TypeValidationError: value \"G\" do not match constructor Number ,\n]\n*/\n\nhasErrors({ a: Number, b: String })({ a: null, b: null });\n/*\n[\n  TypeValidationError: At path /a null do not match constructor Number,\n  TypeValidationError: At path /b null do not match constructor String\n]\n*/\n```\n\n### Raw Error data\n\nAll errors the library throws has the raw data collected in a property called `raw`.\n\n```js\ntry {\n  mustBe({ a: Number })({ a: null });\n} catch (error) {\n  console.log(error.raw);\n}\n/*\n{\n\n  //  the validation failing\n  type: [Function: Number],\n\n  //  the value evaluated\n  value: null,\n\n  // the root object\n  root: { a: null },\n\n  // the key failing\n  keyName: 'a',\n\n  // the whole path where the evaluation happens as an array\n  path: [ 'a' ],\n\n  // the error message\n  message: 'At path /a null do not match constructor Number',\n\n   // the error constructor\n  '$Error': [class TypeValidationError extends TypeError],\n\n  // the behavior applied\n  behavior: {\n    collectAllErrors: false,\n    onValid: [Function: onValidDefault],\n    onInvalid: [Function: onInvalidDefault]\n  },\n}\n */\n```\n\n### Composition in depth\n\nYou can create your own validators and use them as custom validation creating new ones.\n\n```js\nconst isPositive = mustBe((v) =\u003e v \u003e 0);\nconst isNotBig = mustBe((v) =\u003e v \u003c 100);\nconst isNumber = mustBe([Number, String], (num) =\u003e num == Number(num));\n\nmustBe(isNumber, isPositive, isNotBig)(\"10\"); // returns \"10\"\nmustBe(isNumber, isPositive, isNotBig)(200); // it throws\n```\n\nWhen used inside another kind of behavior, it will inherit the behavior from where it has been used.\n\n```js\nconst isNotBig = isValidOrLog((v) =\u003e v \u003c 100);\n// its normal behavior\nisNotBig(200); // false, logs '200 do not match validator (v) =\u003e v \u003c 100'\n\nisValid(isNotBig)(200); // false , and won't log\nmustBe(isNotBig)(200); // fails , and won't log\nhasErrors(isNotBig)(200); // array,  won't log\n/*\n[\n  new TypeValidationError('200 do not match validator (v) =\u003e v \u003c 100')\n]\n */\n```\n\nActually, it's not treated as a custom validation function. No matter is your are using `hasErrors` which return null when nothing fails, and it's just works.\n\n```js\nconst isBigNumber = hasErrors(\n  [Number, String],\n  (num) =\u003e num == Number(num),\n  (num) =\u003e num \u003e 1000\n);\n\n// its normal behavior\nisBigNumber(\"a12\");\n/* [\n  new TypeValidationError(\"\"a12\" do not match validator (num) =\u003e num == Number(num)\"),\n  new TypeValidationError(\"\"a12\" do not match validator num =\u003e num \u003e 1000\"),\n];\n */\n\n// inherit behavior\nisValidOrLog(isBigNumber)(\"a12\"); // false, and log only one error value \"a10\" do not match validator (num) =\u003e num == Number(num)\n```\n\n## Especial cases\n\n### AsyncFunction \u0026 GeneratorFunction\n\n`AsyncFunction` and `GeneratorFunction` constructors are not in the global scope of any of the three JS environments (node, browser or deno). If you need to check an async function or a generator you can import them from garn-validator.\n\n\u003e Note: Async functions and generators are not normal function, so it will fail against Function constructor\n\n```js\nimport mustBe, { AsyncFunction, GeneratorFunction } from \"garn-validator\";\n\nmustBe(AsyncFunction)(async () =\u003e {}); // ok\nmustBe(GeneratorFunction)(function* () {}); // ok\n\nmustBe(Function)(function* () {}); // throws\nmustBe(Function)(async function () {}); // throws\n```\n\n# Roadmap\n\n- [x] Check value by constructor\n- [x] Enum type\n- [x] Shape type\n- [x] Custom validation with a function (value, root, keyName)\n- [x] Check RegEx\n- [x] Match object key by RegEx\n- [x] Multiples behaviors\n- [x] ArrayOf \u0026 objectOf\n- [x] Multiples validations `isValid(String, val =\u003e val.length \u003e 3, /^[a-z]+$/ )('foo')`\n- [x] Schema with optionals key `{ 'optionalKey?': Number }` or `{ optionalKey$: Number }`\n- [x] Setting for check all keys (no matter if it fails) and return (or throw) an array of errors\n- [x] Support for deno\n- [x] Support for browser\n- [x] Behavior applyDefaultsOnError. (syntax `mustBe(Number).or(0)`)\n- [ ] Async validation support\n- [ ] More built-in utils functions\n","funding_links":[],"categories":["Modules","JavaScript Libs"],"sub_categories":["Utils","Assistants","Misc"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjupegarnica%2Fgarn-validator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjupegarnica%2Fgarn-validator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjupegarnica%2Fgarn-validator/lists"}