{"id":16931611,"url":"https://github.com/romelperez/yrel","last_synced_at":"2025-03-17T07:32:14.378Z","repository":{"id":194654587,"uuid":"690893269","full_name":"romelperez/yrel","owner":"romelperez","description":"JavaScript JSON schema validation with TypeScript type inference.","archived":false,"fork":false,"pushed_at":"2024-07-28T03:49:54.000Z","size":884,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-14T20:44:27.956Z","etag":null,"topics":["data-validation","schema-validation","type-inference","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/romelperez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"romelperez"}},"created_at":"2023-09-13T05:14:23.000Z","updated_at":"2024-07-28T03:50:15.000Z","dependencies_parsed_at":"2023-09-14T13:39:45.738Z","dependency_job_id":"d0c80983-e03b-4374-9ae2-3dd865fc4598","html_url":"https://github.com/romelperez/yrel","commit_stats":null,"previous_names":["romelperez/yrel"],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fyrel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fyrel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fyrel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fyrel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/romelperez","download_url":"https://codeload.github.com/romelperez/yrel/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221674191,"owners_count":16861788,"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":["data-validation","schema-validation","type-inference","typescript"],"created_at":"2024-10-13T20:44:22.776Z","updated_at":"2024-10-27T12:15:38.985Z","avatar_url":"https://github.com/romelperez.png","language":"TypeScript","funding_links":["https://github.com/sponsors/romelperez"],"categories":[],"sub_categories":[],"readme":"![](https://github.com/romelperez/yrel/raw/main/yrel.jpg)\n\n# Yrel\n\n[![version](https://img.shields.io/npm/v/yrel)](https://npmjs.org/package/yrel)\n[![tests](https://github.com/romelperez/yrel/workflows/tests/badge.svg)](https://github.com/romelperez/yrel/actions)\n[![codefactor](https://www.codefactor.io/repository/github/romelperez/yrel/badge)](https://www.codefactor.io/repository/github/romelperez/yrel)\n[![npm bundle size](https://img.shields.io/bundlephobia/minzip/yrel.svg)](https://bundlephobia.com/package/yrel)\n[![downloads](https://img.shields.io/npm/dm/yrel.svg)](https://npmjs.org/package/yrel)\n[![github stars](https://img.shields.io/github/stars/romelperez/yrel.svg?style=social\u0026label=stars)](https://github.com/romelperez/yrel)\n[![license](https://img.shields.io/github/license/romelperez/yrel.svg)](https://github.com/romelperez/yrel/blob/main/LICENSE)\n\nJavaScript JSON schema validation with TypeScript type inference.\n\n## Install\n\nFor any ESM and CommonJS JavaScript environment. If TypeScript is used, version 4.5+ is required.\n\n```bash\nnpm i yrel\n```\n\nFor UMD version:\n\n```ts\nimport { y, validateYrel } from 'yrel/build/umd/yrel.umd.cjs'\n```\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/yrel/build/umd/yrel.umd.cjs\" /\u003e\n```\n\n```html\n\u003cscript src=\"https://unpkg.com/yrel/build/umd/yrel.umd.cjs\" /\u003e\n```\n\n## Basic Usage\n\n```ts\nimport { y, validateYrel } from 'yrel'\n\nconst schema = y.object({\n  name: y.string().min(2),\n  age: y.number().gte(18)\n})\n\nconst data = {\n  name: 'yrel',\n  age: 21\n}\n\nconst validation = validateYrel(schema, data)\n\nconsole.log(validation.isValid) // true\nconsole.log(validation.data) // { name: 'yrel', age: 21 }\nconsole.log(validation.issues) // []\n```\n\n## Type Inference\n\n```ts\nimport { y, type InferYrel } from 'yrel'\n\nconst schema = y.object({\n  name: y.string().min(2).max(100),\n  age: y.number().gte(18).lte(150).optional(),\n  pets: y.array(y.string()).min(2).max(10)\n})\n\ntype Schema = InferYrel\u003ctypeof schema\u003e\n/*\n{\n  name: string;\n  age?: number | undefined;\n  pets: string[]\n}\n*/\n\nconst data = {\n  name: 'yrel',\n  age: 21,\n  pets: ['dog', 'cat']\n} satisfies Schema\n```\n\n## Optional and Nullable\n\nAll schemas can be optional and/or nullable.\n\n```ts\nconst schema = y.string().optional()\ntype Schema = InferYrel\u003ctypeof schema\u003e // string | undefined\n```\n\n```ts\nconst schema = y.number().nullable()\ntype Schema = InferYrel\u003ctypeof schema\u003e // number | null\n```\n\n```ts\nconst schema = y.object({\n  name: y.string().optional(),\n  age: y.number().nullable(),\n  is_married: y.boolean().optional().nullable()\n})\ntype Schema = InferYrel\u003ctypeof schema\u003e\n/*\n{\n  name?: string | undefined\n  age: number | null\n  is_married?: boolean | undefined | null\n}\n*/\n```\n\nThe methods should be called at the end of schema definition.\n\n## Preprocessors\n\nAll schemas can be pre-processed before validation. After checking a value is\nnot optional nor nullable, a schema can be pre-processed to change its value\nor anything required.\n\n```ts\nconst schema = y.string().preprocess((data) =\u003e String(data))\nvalidateYrel(schema, 100) // { isValid: true, data: '100' }\n```\n\n## Defaults\n\nAll schemas can have a default value if the received data is `undefined`.\n\n```ts\nconst schema = y.string().defaultsTo('cat')\nvalidateYrel(schema, undefined) // { isValid: true, data: 'cat' }\nvalidateYrel(schema, 'dog') // { isValid: true, data: 'dog' }\n```\n\nIt can not be used with optional schemas since it will mark it as valid and\nignore the default value.\n\n## Coercers\n\nAfter checking a value is not optional nor nullable, and running all defined pre-processors,\nsome schemas can be coerced to their data types.\n\n```ts\nconst schema = y.string().coerce()\nvalidateYrel(schema, 100) // { isValid: true, data: '100' }\n```\n\n_See [API](#api) for more details._\n\n## Transformers\n\nAll schemas data can be transformed. When a schema is valid, the schema data can\nbe transformed to a new value of the same type.\n\n```ts\nconst schema = y.string().transform((data) =\u003e data.toLowerCase())\nvalidateYrel(schema, 'ABC') // { isValid: true, data: 'abc' }\n```\n\nThe transformed data may not be valid since validations were ran before transformation.\ne.g. A schema validator for a number greater than 10 is configured and the transformation\nsets the data to 5, then the data type may be still valid but it is actually invalid and\nreported as valid.\n\n```ts\nconst schema = y\n  .number()\n  .gt(10)\n  .transform(() =\u003e 5)\nvalidateYrel(schema, 20) // { isValid: true, data: 5 }\n// It should be invalid since 5 is not greater than 10, as configured.\n```\n\n## Error Handling\n\nYrel provides a set of validators with predefined error codes for the error report.\n\n```ts\nconst schema = y.object({\n  name: y.string().min(2),\n  age: y.number().gte(18)\n})\n\nconst validation = validateYrel(schema, {\n  name: true,\n  age: 12\n})\n\nconsole.log(validation.isValid) // false\nconsole.log(validation.data) // undefined\nconsole.log(validation.issues)\n/*\n[\n  {\n    \"key\": \"name\",\n    \"errors\": [\n      [\"err_string\"]\n    ]\n  },\n  {\n    \"key\": \"age\",\n    \"errors\": [\n      [\"err_number_gte\", { \"gte\": 18 }]\n    ]\n  }\n]\n*/\n```\n\nThe report error `key` is a string with the path to the schema which reported\nthe error joined by dots. For arrays and tuples, the item index is used.\n\n```ts\nconst schema = y.object({\n  users: y.array(\n    y.object({\n      name: y.string(),\n      pets: y.array(y.string())\n    })\n  )\n})\nconst validation = validateYrel(schema, {\n  users: [\n    { name: 'a', pets: [] },\n    { name: 'b', pets: ['cat', 100, 'dog', true] }\n  ]\n})\n\nconsole.log(validation.issues)\n/*\n[\n  {\n    \"key\": \"users.1.pets.1\",\n    \"errors\": [\n      [\"err_string\"]\n    ]\n  },\n  {\n    \"key\": \"users.1.pets.3\",\n    \"errors\": [\n      [\"err_string\"]\n    ]\n  }\n]\n*/\n```\n\nIf the error is in the root schema, the key is an empty string.\n\n```ts\nconst schema = y.string()\nconst validation = validateYrel(schema, 100)\n\nconsole.log(validation.issues)\n/*\n[\n  {\n    \"key\": \"\",\n    \"errors\": [\n      [\"err_string\"]\n    ]\n  }\n]\n*/\n```\n\nA custom root key can be configured too with `validateYrel(schema, data, { rootKey: 'root' })`.\n\n## Custom Validators\n\nAll schemas support the `.validate(data =\u003e YrelValidation)` method to add custom\nvalidators. They must return either `true` or a list of errors. Every error is tuple\nwith the predefined error code and the according parameters if applicable.\n\nValidators libraries such as [validator](https://npmjs.com/package/validator) can\nbe used for more custom validations.\n\n```ts\nimport { y, validateYrel, type YrelValidation } from 'yrel'\nimport isEmail from 'validator/lib/isEmail'\n\nconst validateEmail = (value: string): YrelValidation =\u003e\n  isEmail(String(value)) || [['err_string_email']]\n\nconst schema = y.object({\n  name: y.string().min(2),\n  age: y.number().gte(18),\n  email: y.string().validate(validateEmail)\n})\n\nconst validation = validateYrel(schema, {\n  name: 'yrel',\n  age: 18,\n  email: 'yrel@example'\n})\n\nconsole.log(validation.isValid) // false\nconsole.log(validation.issues)\n/*\n[\n  {\n    \"key\": \"email\",\n    \"errors\": [\n      [\"err_string_email\"]\n    ]\n  }\n]\n*/\n```\n\nYrel comes with a predefined list of error codes with possible extra parameters for\nthe error report. The following is a list of them. If the type to the right is `undefined`\nit says that it does not require parameters. Otherwise, it defines the parameters types.\n\n- `err_unknown: undefined`\n- `err_boolean: undefined`\n- `err_boolean_truthy: undefined`\n- `err_number: undefined`\n- `err_number_gt: [{ gt: number }]`\n- `err_number_gte: [{ gte: number }]`\n- `err_number_lt: [{ lt: number }]`\n- `err_number_lte: [{ lte: number }]`\n- `err_number_integer: undefined`\n- `err_number_currency: undefined`\n- `err_string: undefined`\n- `err_string_nonempty: undefined`\n- `err_string_trim: undefined`\n- `err_string_length: [{ length: number }]`\n- `err_string_min: [{ min: number }]`\n- `err_string_max: [{ max: number }]`\n- `err_string_date_time: undefined`\n- `err_string_date: undefined`\n- `err_string_time: undefined`\n- `err_string_lowercase: undefined`\n- `err_string_uppercase: undefined`\n- `err_string_capitalcase: [{ lower: boolean }]`\n- `err_string_email: undefined`\n- `err_string_credit_card: undefined`\n- `err_string_url: undefined`\n- `err_string_uuid: undefined`\n- `err_literal: [{ literal: boolean | number | string }]`\n- `err_array: undefined`\n- `err_array_nonempty: undefined`\n- `err_array_length: [{ length: number }]`\n- `err_array_min: [{ min: number }]`\n- `err_array_max: [{ max: number }]`\n- `err_union: undefined`\n- `err_tuple: undefined`\n- `err_object: undefined`\n- `err_object_unexpected_props: [{ props: string[] }]`\n- `err_record: undefined`\n- `err_record_keys: [{ keys: string[] }]`\n\nOne error with parameters can be the `err_number_gte` which requires the parameter\n`gte: number`, so the report may be `['err_number_gte', { gte: 18 }]`.\n\n## Custom Error Reports\n\nValidators can return custom error reports. They need to be expressed as a tuple\n`['err_custom', string, object?]`.\n\n```ts\nimport { y, validateYrel, type YrelValidation } from 'yrel'\n\n// Check that the string has the format \"xxx-xxx\".\nconst validateUserId = (value: string): YrelValidation =\u003e\n  /^\\w{3,3}-\\w{3,3}$/.test(value) || [['err_custom', 'my_custom_error_invalid_user_id']]\n\nconst schema = y.object({\n  id: y.string().validate(validateUserId),\n  name: y.string().min(2),\n  age: y.number().gte(18),\n  pets: y.array(\n    y.union([y.literal('dog'), y.literal('cat'), y.literal('parrot')], {\n      errors: [['err_custom', 'my_custom_error_invalid_pet']]\n    })\n  )\n})\n\nconst validation = validateYrel(schema, {\n  id: 'abc-d',\n  name: 'yrel',\n  age: 18,\n  pets: ['cat', 'monkey', 'dog', 'fish']\n})\n\nconsole.log(validation.isValid) // false\nconsole.log(validation.issues)\n/*\n[\n  {\n    \"key\": \"id\",\n    \"errors\": [\n      [\"err_custom\", \"my_custom_error_invalid_user_id\"]\n    ]\n  },\n  {\n    \"key\": \"pets.1\",\n    \"errors\": [\n      [\"err_custom\", \"my_custom_error_invalid_pet\"]\n    ]\n  },\n  {\n    \"key\": \"pets.3\",\n    \"errors\": [\n      [\"err_custom\", \"my_custom_error_invalid_pet\"]\n    ]\n  }\n]\n*/\n```\n\nIf custom errors need to be reported for children elements, such as properties\nof an object, the utility `reportYrel` can be used.\n\n```ts\nimport { y, validateYrel, reportYrel } from 'yrel'\n\nconst schema = y\n  .object({\n    name: y.string(),\n    password: y.string(),\n    passwordConfirmation: y.string()\n  })\n  .validate(\n    (value) =\u003e\n      value.password === value.passwordConfirmation ||\n      reportYrel({\n        children: [\n          { key: 'password', errors: [['err_custom', 'passwords_dont_match']] },\n          { key: 'passwordConfirmation', errors: [['err_custom', 'passwords_dont_match']] }\n        ]\n      })\n  )\n\nconst validation = validateYrel(schema, {\n  name: 'a',\n  password: 'x',\n  passwordConfirmation: 'y'\n})\n\nconsole.log(validation.isValid) // false\nconsole.log(validation.issues)\n/*\n[\n  {\n    \"key\": \"password\",\n    \"errors\": [\n      [\"err_custom\", \"passwords_dont_match\"]\n    ]\n  },\n  {\n    \"key\": \"passwordConfirmation\",\n    \"errors\": [\n      [\"err_custom\", \"passwords_dont_match\"]\n    ]\n  }\n]\n*/\n```\n\n`reportYrel` can also accept `errors` as first parameter property for the same\nschema error report.\n\n## Error Translations\n\nYrel reports the validation issues with only the error codes with their\nrespective parameters.\n\nA example validation use case can be:\n\n```ts\nimport { y, validateYrel } from 'yrel'\n\nconst schema = y.object({\n  name: y.string().min(2).max(10),\n  age: y.number().gte(0).lte(100),\n  married: y.boolean().optional(),\n  sex: y\n    .union(\n      [y.literal('female'), y.literal('male')],\n      { errors: [['err_custom', 'err_custom_sex']] } // Custom error report.\n    )\n    .optional(),\n  pets: y.array(y.string().nonempty().max(10)).max(3).nullable()\n})\n\nconst data = {\n  name: 'y',\n  age: -1,\n  married: 10,\n  sex: 'unicorn',\n  pets: [true, 'unknown species']\n}\n\nconst validation = validateYrel(schema, data)\n```\n\nSince the data is invalid, the following issues are reported:\n\n```json\n[\n  {\n    \"key\": \"name\",\n    \"errors\": [[\"err_string_min\", { \"min\": 2 }]]\n  },\n  {\n    \"key\": \"age\",\n    \"errors\": [[\"err_number_gte\", { \"gte\": 0 }]]\n  },\n  {\n    \"key\": \"married\",\n    \"errors\": [[\"err_boolean\"]]\n  },\n  {\n    \"key\": \"sex\",\n    \"errors\": [[\"err_custom\", \"err_custom_sex\"]]\n  },\n  {\n    \"key\": \"pets.0\",\n    \"errors\": [[\"err_string\"]]\n  },\n  {\n    \"key\": \"pets.1\",\n    \"errors\": [[\"err_string_max\", { \"max\": 10 }]]\n  }\n]\n```\n\nIf they need to be presented to the end user, a tool like\n[Ukti](https://github.com/romelperez/ukti) can be used to translate them.\nThen each issue can be mapped to their respective translation:\n\n```ts\nimport { type YrelErrorTranslations } from 'yrel'\nimport { createUktiTranslator, type UktiTranslations } from 'ukti'\n\n// For custom error reports.\ntype ErrorCustomTranslations = {\n  err_custom_sex: undefined\n}\n\nconst translations: UktiTranslations\u003cYrelErrorTranslations \u0026 ErrorCustomTranslations\u003e = {\n  en: {\n    err_boolean: 'This field should be a boolean.',\n    err_number: 'A valid number is required.',\n    err_number_gt: 'This number should be greater than {{gt}}.',\n    err_number_gte: 'This number should be at least {{gte}}.',\n    err_number_lt: 'This number should be less than {{lt}}.',\n    err_number_lte: 'This number should be at most {{lte}}.',\n    err_string: 'This text field is required.',\n    err_string_min: 'The field should have at least {{min}} character{{min === 1 ? \"\" : \"s\"}}.',\n    err_string_max: 'The field should have at most {{max}} character{{max === 1 ? \"\" : \"s\"}}.',\n    // ...\n    err_custom_sex: 'Invalid sex.'\n  }\n}\n\nconst translator = createUktiTranslator\u003cYrelErrorTranslations \u0026 ErrorCustomTranslations\u003e({\n  translations\n})\nconst translate = translator('en')\n\n// Using the previous Yrel schema validation result:\nif (!validation.isValid) {\n  validation.issues.forEach((issue) =\u003e {\n    issue.errors.forEach((err) =\u003e {\n      // Type-safety is not enforced here but should be enforced when creating\n      // the translation definition, since this snippet is mostly going to be used\n      // by libraries or app utilities once.\n      const errorMessage =\n        err[0] === 'err_custom'\n          ? (translate[err[1] as keyof ErrorCustomTranslations] as any)(err[2])\n          : (translate[err[0]] as any)(err[1])\n      console.log(`${issue.key}:`, errorMessage)\n    })\n  })\n}\n\n// Logs:\n// 'name: The field should have at least 2 characters.'\n// 'age: This number should be at least 0.'\n// 'married: This field should be a boolean.'\n// 'sex: Invalid sex.'\n// 'pets.0: This text field is required.'\n// 'pets.1: The field should have at most 10 characters.'\n```\n\nSee [Ukti](https://github.com/romelperez/ukti) for more details on translations.\n\n## Custom Schemas\n\nSchemas with custom data types can be created with the general schema `.any\u003ctype\u003e()`.\n\n```ts\nconst schema = y\n  .any\u003c'cat' | 'dog'\u003e()\n  .validate((data) =\u003e data === 'cat' || data === 'dog' || [['err_custom', 'not_a_pet']])\n\ntype Schema = InferYrel\u003ctypeof schema\u003e // 'cat' | 'dog'\n\nvalidateYrel(schema, 'cat') // { isValid: true }\nvalidateYrel(schema, 'dolphin') // { isValid: false }\n```\n\n## Schema Detection\n\n```ts\nimport { y, isYrel } from 'yrel'\n\nconst fakeSchema = {}\nconst validSchema = y.string()\n\nconsole.log(isYrel(fakeSchema)) // false\nconsole.log(isYrel(validSchema)) // true\n```\n\n## API\n\n### `y.any\u003cData = any\u003e(): YrelSchemaAny\u003cData\u003e`\n\nAny kind of value.\n\n```ts\nconst schema = y.any() // any\n```\n\n### `y.boolean(): YrelSchemaBoolean`\n\nBoolean values.\n\n```ts\nconst schema = y.boolean() // boolean\n```\n\n#### `.coerce()`\n\nForce the data input value to `Boolean(input)` before validation.\n\n```ts\nconst schema = y.boolean().coerce()\nvalidateYrel(schema, 0) // { isValid: true, data: false }\nvalidateYrel(schema, '') // { isValid: true, data: false }\nvalidateYrel(schema, 100) // { isValid: true, data: true }\nvalidateYrel(schema, 'abc') // { isValid: true, data: true }\nvalidateYrel(schema, {}) // { isValid: true, data: true }\n```\n\n#### `.truthy()`\n\nOnly `true` values.\n\n### `y.number(): YrelSchemaNumber`\n\nNumeric and finite numbers.\n\n```ts\nconst schema = y.number() // number\n```\n\n#### `.coerce()`\n\nForce the data input value to `Number(input)` before validation. `Date` objects\nare coerced with `.getTime()`.\n\n```ts\nconst schema = y.number().coerce()\nvalidateYrel(schema, true) // { isValid: true, data: 1 }\nvalidateYrel(schema, '100') // { isValid: true, data: 100 }\nvalidateYrel(schema, new Date()) // { isValid: true, data: 1695336434720 }\n```\n\n#### `.gt(value: number)`\n\nA number greater than the defined value.\n\n#### `.gte(value: number)`\n\nA number greater than or equal to the defined value.\n\n#### `.lt(value: number)`\n\nA number less than the defined value.\n\n#### `.lte(value: number)`\n\nA number less than or equal to the defined value.\n\n#### `.integer()`\n\nA safe integer number.\n\n### `y.string(): YrelSchemaString`\n\nA string value.\n\n```ts\nconst schema = y.string() // string\n```\n\nTo validate an optional non-empty string validation, it can be done like this:\n\n```ts\nconst schema = y.union([y.string().date(), y.literal('')])\nvalidateYrel(schema, '2000-10-10') // valid\nvalidateYrel(schema, '') // valid\n```\n\n#### `.coerce()`\n\nForce the data input value to `String(input)` before validation. `Date` objects\nare coerced with `.toISOString()`.\n\n```ts\nconst schema = y.string().coerce()\nvalidateYrel(schema, true) // { isValid: true, data: 'true' }\nvalidateYrel(schema, 100) // { isValid: true, data: '100' }\nvalidateYrel(schema, new Date()) // { isValid: true, data: '2023-09-21T22:46:09.059Z' }\n```\n\n#### `.nonempty()`\n\nNon empty string.\n\n#### `.trim()`\n\nA string without spaces at the beginning or end.\n\n#### `.length(value: number)`\n\nA string with specified length.\n\n#### `.min(value: number)`\n\nA string with at least the specified length.\n\n#### `.max(value: number)`\n\nA string with at most the specified length.\n\n#### `.datetime(value: number)`\n\nA valid datetime string in ISO 8601 format. e.g.:\n\n```ts\n'2050-10-25T14:45:30Z'\n'2050-10-25T14:45:30.3Z'\n'2050-10-25T14:45:30.37Z'\n'2050-10-25T14:45:30.370Z'\n```\n\n#### `.date(value: number)`\n\nA valid date string fragment of the ISO 8601 format. e.g. `2050-10-25`.\n\n#### `.time(value: number)`\n\nA valid time string fragment of the ISO 8601 format. e.g.:\n\n```ts\n'14:45:30'\n'14:45:30.3'\n'14:45:30.37'\n'14:45:30.370'\n```\n\n#### `.lowercase()`\n\nA string in lowercase.\n\n#### `.uppercase()`\n\nA string in uppercase.\n\n#### `.capitalcase(conf?: { lower?: boolean })`\n\nA string in capital case. By default, it allows any uppercase characters such as `Abc Def`\nor `ABc DEF`. If `.capitalcase({ lower: true })` is defined, it will only accept lowercase\nchactaters for non-first word letters such as `Abc Def`.\n\n### `y.literal(value: boolean | number | string): YrelSchemaLiteral`\n\nA literal primitive value.\n\n```ts\nconst schema = y.literal('cat') // 'cat'\n```\n\n### `y.array(schema: YrelSchema)`\n\nAn array of the specified schema.\n\n```ts\nconst schema = y.array(y.string()) // string[]\n```\n\n#### `.nonempty()`\n\nNon empty arrays.\n\n#### `.length(value: number)`\n\nAn array of the specified length.\n\n#### `.min(value: number)`\n\nAn array of at least the specified length.\n\n#### `.max(value: number)`\n\nAn array of at most the specified length.\n\n### `y.union(schemas: [YrelSchema, YrelSchema, ...YrelSchema[]]): YrelSchemaUnion`\n\nA value that matches one of the specified schemas.\n\n```ts\nconst schema = y.union([y.number(), y.literal('cat'), y.literal('dog'), y.literal('parrot')])\n// number | 'cat' | 'dog' | 'parrot'\n```\n\nFor dynamically created union of literals, the dynamic types can be set like:\n\n```ts\nimport { type YrelSchemaLiteral } from 'yrel'\n\ntype Languages = 'en' | 'es' | 'fr' | 'hi' | 'zh'\nconst languages: Languages[] = ['en', 'es', 'fr', 'hi', 'zh']\n\nconst schema = y.union\u003c[YrelSchemaLiteral\u003cLanguages\u003e, YrelSchemaLiteral\u003cLanguages\u003e]\u003e(\n  languages.map((lang) =\u003e y.literal(lang)) as [\n    YrelSchemaLiteral\u003cLanguages\u003e,\n    YrelSchemaLiteral\u003cLanguages\u003e\n  ]\n)\ntype Schema = InferYrel\u003ctypeof schema\u003e // 'en' | 'es' | 'fr' | 'hi' | 'zh'\n```\n\nOr using a custom type with `y.any\u003ctype\u003e()`.\n\n### `y.tuple(schemas: [YrelSchema, ...YrelSchema[]]): YrelSchemaTuple`\n\nAn array with fixed number of elements and each of them with a specific data schema.\n\n```ts\nconst schema = y.tuple([y.number(), y.string(), y.boolean().optional()])\n// [number, string, boolean | undefined]\n```\n\n### `y.object(shape: Record\u003cstring, YrelSchema\u003e): YrelSchemaObject`\n\nA plain object and each property with the specified data schema.\n\n```ts\nconst schema = y.object({\n  name: y.string(),\n  age: y.number()\n})\n// { name: string; age: number; }\n```\n\n#### `.shape`\n\nThe object shape structure.\n\n#### `.passthrough()`\n\nBy default the object data schema will report an error if the validated object contains\nunexpected properties which are not defined in the schema shape. This will allow such\nproperties.\n\n### `y.record(key: YrelSchemaString, value: YrelSchema): YrelSchemaRecord`\n\nA plain object with a not specified number of properties. All object keys have to be\nstring with `y.string()`.\n\n```ts\nconst schema = y.record(y.string(), y.number())\n// { [key: string]: number }\n```\n\nTo validate the record key:\n\n```ts\nconst schema = y.record(y.string().date(), y.boolean())\n// { [name: string]: boolean }\n\nvalidateYrel(schema, { '2000': true, '2001': false }) // invalid\nvalidateYrel(schema, { '2000-10-10': true, '2000-10-11': false }) // valid\n```\n\n## Logo\n\nThe Yrel logo is an [illustration](https://kuridelblack.tumblr.com/post/189438276843/yrel-light-of-hope)\nof the character [Yrel](https://wowpedia.fandom.com/wiki/Yrel) in the game\n[World of Warcraft](https://worldofwarcraft.blizzard.com) from the awesome illustrator\n**[@KuridelBlack](https://twitter.com/KuridelBlack)**. Check out her work at\n[linktr.ee/kuridelblack](https://linktr.ee/kuridelblack).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromelperez%2Fyrel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fromelperez%2Fyrel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromelperez%2Fyrel/lists"}