{"id":20509630,"url":"https://github.com/aminnairi/kryptonian","last_synced_at":"2026-04-20T02:05:24.081Z","repository":{"id":207650662,"uuid":"719769913","full_name":"aminnairi/kryptonian","owner":"aminnairi","description":"Purity, hope, and the strength of Krypton in one package","archived":false,"fork":false,"pushed_at":"2023-12-26T10:12:04.000Z","size":401,"stargazers_count":2,"open_issues_count":9,"forks_count":0,"subscribers_count":2,"default_branch":"development","last_synced_at":"2025-01-16T08:36:28.810Z","etag":null,"topics":["client","client-server","clientserver","function","functional","integrity","json","json-rpc","jsonrpc","krypton","pure","purity","quality","reliability","rpc","server","server-client","serverclient","superman","validation"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/kryptonian","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/aminnairi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-11-16T21:35:15.000Z","updated_at":"2023-11-22T08:34:34.000Z","dependencies_parsed_at":"2023-11-30T21:29:43.103Z","dependency_job_id":"90090da0-f412-49ec-96e1-7843c4ddb242","html_url":"https://github.com/aminnairi/kryptonian","commit_stats":{"total_commits":359,"total_committers":1,"mean_commits":359.0,"dds":0.0,"last_synced_commit":"eb714cff821e97ee442f6e460667a3d8225295e3"},"previous_names":["aminnairi/kalel"],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminnairi%2Fkryptonian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminnairi%2Fkryptonian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminnairi%2Fkryptonian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aminnairi%2Fkryptonian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aminnairi","download_url":"https://codeload.github.com/aminnairi/kryptonian/tar.gz/refs/heads/development","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242112273,"owners_count":20073560,"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":["client","client-server","clientserver","function","functional","integrity","json","json-rpc","jsonrpc","krypton","pure","purity","quality","reliability","rpc","server","server-client","serverclient","superman","validation"],"created_at":"2024-11-15T20:25:47.238Z","updated_at":"2026-04-20T02:05:24.030Z","avatar_url":"https://github.com/aminnairi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kryptonian\n\nPurity, hope, and the strength of Krypton in one package\n\n[![npm](https://img.shields.io/npm/v/kryptonian)](https://www.npmjs.com/package/kryptonian) [![Coveralls branch](https://img.shields.io/coverallsCoverage/github/aminnairi/kryptonian)](https://coveralls.io/github/aminnairi/kryptonian) [![npm type definitions](https://img.shields.io/npm/types/kryptonian)](https://github.com/aminnairi/kryptonian) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/kryptonian)](https://bundlephobia.com/package/kryptonian) [![NPM](https://img.shields.io/npm/l/kryptonian)](https://github.com/aminnairi/kryptonian/blob/development/LICENSE)\n\n## Summary\n- [Features](#features)\n- [TypeScript](#typescript)\n- [Functional Programming](#functional-programming)\n- [Treeshakeable](#treeshakeable)\n- [Extensible](#extensible)\n- [Custom Error Messages](#custom-error-messages)\n- [Client \u0026 Server-Side Support](#client--server-side-support)\n- [Zero Dependencies](#zero-dependencies)\n- [Inspired by Zod](#inspired-by-zod)\n- [Open-Source](#open-source)\n- [Installation](#installation)\n- [Usage](#usage)\n- [API](#api)\n- [createProtector](#createprotector)\n- [literal](#literal)\n- [boolean](#boolean)\n- [none](#none)\n- [notDefined](#notdefined)\n- [string](#string)\n    - [length](#length)\n    - [minimumLength](#minimumlength)\n    - [includes](#includes)\n    - [startsWith](#startswith)\n    - [endsWith](#endswith)\n    - [email](#email)\n    - [uniformResourceLocator](#uniformresourcelocator)\n    - [internetProtocolVersion4](#internetprotocolversion4)\n    - [internetProtocolVersion4WithClasslessInterDomainRouting](#internetprotocolversion4withclasslessinterdomainrouting)\n- [number](#number)\n    - [between](#between)\n    - [divisibleBy](#divisibleby)\n    - [notDivisibleBy](#notdivisibleby)\n    - [even](#even)\n    - [odd](#odd)\n    - [positive](#positive)\n    - [negative](#negative)\n    - [integer](#integer)\n    - [greater](#greater)\n    - [lower](#lower)\n    - [greaterOrEqual](#greaterorequal)\n    - [lowerOrEqual](#lowerorequal)\n    - [finite](#finite)\n- [date](#date)\n    - [between](#between-1)\n    - [before](#before)\n    - [after](#after)\n- [object](#object)\n- [array](#array)\n    - [length](#length-1)\n    - [lengthBetween](#lengthbetween)\n    - [minimumLength](#minimumlength-1)\n    - [maximumLength](#maximumlength)\n    - [nonEmpty](#nonempty)\n- [document](#document)\n- [unknown](#unknown)\n- [any](#any)\n- [empty](#empty)\n- [oneOf](#oneof)\n- [Jorel](#jorel)\n    - [createRoutes](#createroutes)\n    - [createRoute](#createroute)\n    - [createServerRouter](#createserverrouter)\n    - [createServerRoute](#createserverroute)\n    - [createClientRoutes](#createclientroutes)\n    - [Document](#document-1)\n    - [getting started](#getting-started)\n- [InferType](#infertype)\n- [Custom rules](#custom-rules)\n- [Issues](#issues)\n- [Changelog](#changelog)\n- [Code of conduct](#code-of-conduct)\n- [Contributing](#contributing)\n- [License](#license)\n- [Security](#security)\n## Features\n\n### TypeScript\n\nBenefit from robust TypeScript support with intelligent code completion, type inference, and error checking. The library provides a seamless development experience by leveraging TypeScript's static typing capabilities, catching potential issues during development rather than at runtime.\n\n[Back to summary](#summary)\n\n### Functional Programming\n\nAdopt a functional programming paradigm within your validation logic, promoting immutability and avoiding mutations. This approach ensures that the state of your data remains predictable and maintainable, contributing to a more reliable codebase.\n\n[Back to summary](#summary)\n\n### Treeshakeable\n\nExperience optimized bundles through the library's tree-shaking capabilities. This feature allows you to eliminate unused code during the build process, resulting in smaller production bundles and improved application performance.\n\n[Back to summary](#summary)\n\n### Extensible\n\nEnjoy a comprehensive set of TypeScript types that enhance the overall developer experience. The library provides rich type definitions, ensuring maximum code quality and facilitating a smooth integration process within TypeScript projects.\n\n[Back to summary](#summary)\n\n### Custom Error Messages\n\nProvide custom error messages for rules and schemas, enhancing user-facing error feedback. Tailor error messages to better communicate validation issues, improving the overall user experience.\n\n[Back to summary](#summary)\n\n### Client \u0026 Server-Side Support\n\nSeamlessly integrate the validation library into both client and server-side projects. Whether you're building a web application or a server-side API, the library offers universal compatibility, empowering you to maintain consistent validation logic across different environments.\n\n[Back to summary](#summary)\n\n### Zero Dependencies\n\nMinimize project dependencies with the library's commitment to a lightweight footprint. By avoiding external dependencies, you can maintain a streamlined project structure and reduce potential compatibility issues, contributing to a more efficient development process.\n\n[Back to summary](#summary)\n\n### Inspired by Zod\n\nDraw inspiration from Zod's design principles, incorporating best practices for validation. The library takes cues from Zod to provide a well-designed and reliable validation solution that aligns with industry standards.\n\n[Back to summary](#summary)\n\n### Open-Source\n\nParticipate in the open-source community by contributing to the project through bug reports, feature requests, or pull requests. The library encourages collaboration and welcomes input from developers worldwide, fostering a community-driven approach to continuous improvement.\n\n[Back to summary](#summary)\n\n## Installation\n\n```bash\nnpm install kryptonian\n```\n\n[Back to summary](#summary)\n\n## Usage\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nexport const routes = Kryptonian.Jorel.createRoutes({\n  getKryptonians: {\n    request: Kryptonian.Kalel.empty({\n      message: \"Request should be void or undefined\"\n    }),\n    response: Kryptonian.Kalel.array({\n      message: \"Response should be an array\",\n      rules: [],\n      schema: Kryptonian.Kalel.object({\n        message: \"Response should be an object\",\n        fields: {\n          createdAt: Kryptonian.Kalel.date({\n            message: \"Response object should have a property createdAt that is a date\",\n            rules: []\n          }),\n          name: Kryptonian.Kalel.string({\n            message: \"Response object should have a property name that is a string\",\n            rules: []\n          })\n        }\n      })\n    })\n  }\n});\n```\n\n```typescript\nimport * as Kryptonian from \"kryptonian\"\nimport * as Http from \"http\";\nimport { routes } from \"@template/shared\";\nimport { createExpressServer } from \"./adapters/createExpressServer\";\n\nconst router = Kryptonian.Jorel.createServerRouter({\n  routes,\n  implementations: {\n    getKryptonians: async () =\u003e {\n      return [\n        {\n          name: \"Kalel\",\n          createdAt: new Date()\n        },\n        {\n          name: \"Jorel\",\n          createdAt: new Date()\n        },\n        {\n          name: \"Zorel\",\n          createdAt: new Date()\n        }\n      ];\n    }\n  }\n});\n\nconst server = createExpressServer({\n  router,\n  clients: [\"http://localhost:8000\"]\n});\n\nserver.listen(8000, \"0.0.0.0\", () =\u003e {\n  console.log(\"Server launched and ready for communications\");\n});\n```\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\nimport { routes } from \"@template/shared\";\n\nconst client = Kryptonian.Jorel.createClientRoutes({\n  server: \"http://localhost:8000\",\n  routes\n});\n\nclient.getKryptonians({\n  parameters: null,\n  options: {}\n}).then(kryptonians =\u003e {\n  console.log(kryptonians);\n}).catch(error =\u003e {\n  console.error(error);\n})\n```\n\n[Back to summary](#summary)\n\n## API\n\n### createProtector\n\nCreate a protection function helping you validate data according to the schema passed as argument. Unless you type check that the `success` property is true or false, you do not get access to the `data` property. This prevents unintentional access when there might be an error, protecting your from making mistakes in your source-code.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not a string\",\n  rules: []\n}));\n\nconst data: unknown = \"Hello, world!\";\nconst protection = protect(data);\n\nif (protection.success) {\n  console.log(protection.data);\n} else {\n  console.log(protection.errors);\n}\n```\n\n```json\n\"Hello, world!\"\n```\n\n[Back to summary](#summary)\n\n### literal\n\n`literal` is a function that will create a `LiteralSchema\u003cValue\u003e` that can validate values that are exactly equal to the value that you'll pass. Beware, literal values works well for scalar data types that can be compared with each others like `string` or `boolean`, but not so well for `array` and `object` since they are compared by references.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.literal({\n  message: \"This should be true\",\n  value: true as const\n}));\n\nconst goodData: unknown = true;\nconst badData: unknown = false;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\ntrue\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be true\"\n  }\n]\n```\n\nBeware, if you use this in a server, you should use the `as const` keyword in order to create literal values instead of plain values.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\nimport { routes } from \"./routes\";\n\nexport const router = Kryptonian.Jorel.createServerRouter({\n  getKryptonians: async () =\u003e {\n    return [\n      {\n        success: true as const, // Instead of just \"true\"\n        name: \"Kalel\"\n      }\n    ];\n  }\n});\n```\n\n[Back to summary](#summary)\n\n### boolean\n\nBoolean is a schema representing a value that can either be true or false.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.boolean({\n  message: \"This is not a boolean\"\n}));\n\nconst goodData: unknown = true;\nconst badData: unknown = [];\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\ntrue\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not a boolean\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### none\n\nNone is a schema representing a value that can be null.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.none({\n  message: \"This is not null\"\n}));\n\nconst goodData: unknown = null;\nconst badData: unknown = undefined;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\nnull\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not null\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### notDefined\n\nNotDefined is a schema representing a value that can be undefined.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.notDefined({\n  message: \"This is not undefined\"\n}));\n\nconst goodData: unknown = undefined;\nconst badData: unknown = null;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"undefined\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not undefined\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### string\n\n`string` is a schema representing a string.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not a string\",\n  rules: []\n}));\n\nconst goodData: unknown = \"Hello, world!\";\nconst badData: unknown = 123;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"Hello, world!\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not a string\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### length\n\nValidate that a string has exactly a given length.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not a string\",\n  rules: [\n    Kryptonian.Kalel.String.length({\n      length: 13,\n      message: \"This should be a string of 10 characters\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"Hello, world!\";\nconst wrongData: unknown = \"Hello\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"Hello, world!\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a string of 10 characters\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### minimumLength\n\nValidate that a string has a minimum length.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.String.minimumLength({\n      minimum: 10,\n      message: \"This should be a string of at least 10 characters\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"Hello, world!\";\nconst wrongData: unknown = \"Hello\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"Hello, world!\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a string of at least 10 characters\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### includes\n\nValidate that a string is included in another one.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.String.includes({\n      string: \"type\",\n      message: \"This should be a string with the word type\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"typescript\";\nconst wrongData: unknown = \"javascript\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"typescript\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a string with the word type\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### startsWith\n\nValidate that a string is starting with another one.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.String.startsWith({\n      string: \"type\",\n      message: \"This should be a string starting with the word type\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"typescript\";\nconst wrongData: unknown = \"javascript\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"typescript\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a string starting with the word type\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### endsWith\n\nValidate that a string is ending with another one.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.String.endsWith({\n      string: \"script\",\n      message: \"This should be a string ending with the word script\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"typescript\";\nconst wrongData: unknown = \"typeform\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"typescript\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a string ending with the word script\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### email\n\nValidate that a string is a valid email.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not a string\",\n  rules: [\n    Kryptonian.Kalel.String.email({\n      message: \"This should be a valid email\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"kalel@krypton.io\";\nconst wrongData: unknown = \"kalel@krypton\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"kalel@krypton.io\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a valid email\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### uniformResourceLocator\n\nValidate that a string is a valid url.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not a string\",\n  rules: [\n    Kryptonian.Kalel.String.uniformResourceLocator({\n      message: \"This should be a valid URL\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"https://krypton.dev\";\nconst wrongData: unknown = \"https:/krypton.dev\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"https://krypton.dev\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a valid URL\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### internetProtocolVersion4\n\nValidate that a string is a valid Internet Protocol version 4 format.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.String.internetProtocolVersion4({\n      message: \"This should be a valid IPv4 address\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"1.2.3.4\";\nconst wrongData: unknown = \"1.2.3\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"1.2.3.4\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a valid IPv4 address\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### internetProtocolVersion4WithClasslessInterDomainRouting\n\nValidate that a string is a valid Internet Protocol version 4 with classless inter-domain routing format.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.string({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.String.internetProtocolVersion4WithClasslessInterDomainRouting({\n      message: \"This should be a valid IPv4 address with CIDR\"\n    })\n  ]\n}));\n\nconst goodData: unknown = \"1.2.3.4/16\";\nconst wrongData: unknown = \"1.2.3/32\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"1.2.3.4/16\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a valid IPv4 address with CIDR\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### number\n\n`number` is a schema representing a number.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: []\n}));\n\nconst goodData: unknown = 123;\nconst wrongData: unknown = \"Hello, world!\";\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n123\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not a number\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### between\n\nValidate that a number is between two values.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.between({\n      minimum: 10,\n      maximum: 20,\n      message: \"This should be a number between 10 \u0026 20\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 15;\nconst wrongData: unknown = 172;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a number between 10 \u0026 20\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### divisibleBy\n\nValidate that a number can be divided by another number without remaining value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.divisibleBy({\n      divisor: 5,\n      message: \"This should be a number divisible by 5\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 15;\nconst wrongData: unknown = 172;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a number divisible by 5\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### notDivisibleBy\n\nValidate that a number cannot be divided by another number without remaining value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.notDivisibleBy({\n      divisor: 2,\n      message: \"This should be a number not divisible by 2\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 15;\nconst wrongData: unknown = 172;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a number not divisible by 2\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### even\n\nValidate that a number is even.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.even({\n      message: \"This should be an even number\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 14;\nconst wrongData: unknown = 173;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n14\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an even number\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### odd\n\nValidate that a number is odd.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.odd({\n      message: \"This should be an odd number\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 15;\nconst wrongData: unknown = 172;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an odd number\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### positive\n\nValidate that a number is positive.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.positive({\n      message: \"This should be a positive number\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 15;\nconst wrongData: unknown = -172;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a positive number\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### negative\n\nValidate that a number is negative.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.negative({\n      message: \"This should be a negative number\"\n    })\n  ]\n}));\n\nconst goodData: unknown = -15;\nconst wrongData: unknown = 172;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n-15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a negative number\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### integer\n\nValidate that a number is integer\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.integer({\n      message: \"This should be an integer number\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 15;\nconst wrongData: unknown = 17.2;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an integer number\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### greater\n\nValidate that a number is greater than another value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.greater({\n      number: 5,\n      message: \"This should be greater than 5\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 15;\nconst wrongData: unknown = 2;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n15\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be greater than 5\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### lower\n\nValidate that a number is lower than another value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.lower({\n      number: 5,\n      message: \"This should be lower than 5\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 2;\nconst wrongData: unknown = 15;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n2\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be lower than 5\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### greaterOrEqual\n\nValidate that a number is greater or equal to another value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.greaterOrEqual({\n      number: 5,\n      message: \"This should be greater or equal to 5\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 5;\nconst wrongData: unknown = 2;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n5\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be greater or equal to 5\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### lowerOrEqual\n\nValidate that a number is greater or equal to another value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.lowerOrEqual({\n      number: 5,\n      message: \"This should be lower or equal to 5\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 5;\nconst wrongData: unknown = 7;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n5\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be lower or equal to 5\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### finite\n\nValidate that a number is finite.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.number({\n  message: \"This is not a number\",\n  rules: [\n    Kryptonian.Kalel.Number.finite({\n      message: \"This should be finite\"\n    })\n  ]\n}));\n\nconst goodData: unknown = 5;\nconst wrongData: unknown = Infinity;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n5\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be finite\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### date\n\nDate is a schema representing a date object.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.date({\n  message: \"This should be a date\",\n  rules: []\n}));\n\nconst goodData: unknown = new Date(2023, 1, 2, 3, 4, 5);\nconst alsoGoodData: unknown = \"2023-11-19T13:32:34.479Z\";\nconst badData: unknown = NaN;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneRightAgain = protect(alsoGoodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneRightAgain.success) {\n  console.log(protectionGoneRightAgain.data);\n} else {\n  console.log(protectionGoneRightAgain.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"Thu Feb 02 2023 03:04:05 GMT+0100\"\n\"Sun Nov 19 2023 14:32:34 GMT+0100\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a date\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### between\n\nValidate that a date is between two dates.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.date({\n  message: \"This should be a date\",\n  rules: [\n    Kryptonian.Kalel.Date.between({\n      minimum: new Date(2021, 0, 1, 0, 0, 0),\n      maximum: new Date(2024, 0, 1, 1, 1, 1),\n      message: \"This should be a date between 01/01/2021 \u0026 01/01/2024\"\n    })\n  ]\n}));\n\nconst goodData: unknown = new Date(2023, 0, 1, 0, 0, 0);\nconst badData: unknown = new Date(2025, 0, 1, 0, 0, 0);\nconst alsoBadData: unknown = new Date(2020, 0, 1, 0, 0, 0);\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\nconst protectionGoneWrongAgain = protect(alsoBadData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n\nif (protectionGoneWrongAgain.success) {\n  console.log(protectionGoneWrongAgain.data);\n} else {\n  console.log(protectionGoneWrongAgain.errors);\n}\n```\n\n```json\n\"Sun Jan 01 2023 00:00:00 GMT+0100\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a date between 01/01/2021 \u0026 01/01/2024\"\n  }\n]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a date between 01/01/2021 \u0026 01/01/2024\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### before\n\nValidate that a date is before a given date.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.date({\n  message: \"This should be a date\",\n  rules: [\n    Kryptonian.Kalel.Date.before({\n      date: new Date(2025, 0, 1, 0, 0, 0),\n      message: \"This should be a date before 01/01/2025\"\n    })\n  ]\n}));\n\nconst goodData: unknown = new Date(2023, 0, 1, 0, 0, 0);\nconst badData: unknown = new Date(2028, 0, 1, 0, 0, 0);\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"Sun Jan 01 2023 00:00:00 GMT+0100\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a date before 01/01/2025\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### after\n\nValidate that a date is after a given date.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.date({\n  message: \"This should be a date\",\n  rules: [\n    Kryptonian.Kalel.Date.after({\n      date: new Date(2021, 0, 1, 0, 0, 0),\n      message: \"This should be a date after 01/01/2021\"\n    })\n  ]\n}));\n\nconst goodData: unknown = new Date(2023, 0, 1, 0, 0, 0);\nconst badData: unknown = new Date(2020, 0, 1, 0, 0, 0);\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n\"Sun Jan 01 2023 00:00:00 GMT+0100\"\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a date after 01/01/2021\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### object\n\n`object` is a schema representing an object.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.object({\n  message: \"This is not an object\",\n  fields: {\n    email: Kryptonian.Kalel.string({\n      message: \"This is not a string\",\n      rules: []\n    })\n  }\n}));\n\nconst goodData: unknown = {\n  email: \"kalel@krypton.io\"\n};\n\nconst wrongData: unknown = \"Hello, world!\";\n\nconst anotherWrongData: unknown = {\n  email: 123\n}\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\nconst protectionGoneWrongAgain = protect(anotherWrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n\nif (protectionGoneWrongAgain.success) {\n  console.log(protectionGoneWrongAgain.data);\n} else {\n  console.log(protectionGoneWrongAgain.errors);\n}\n```\n\n```json\n{\n  \"email\": \"kalel@krypton.io\"\n}\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not an object\"\n  }\n]\n[\n  {\n    \"path\": \".email\",\n    \"message\": \"This is not a string\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### array\n\n`array` is a schema representing an array\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.array({\n  message: \"This is not an array\",\n  rules: [],\n  schema: Kryptonian.Kalel.string({\n    message: \"This is not a string\",\n    rules: []\n  })\n}));\n\nconst goodData: unknown = [ \"Hello\", \"world!\" ];\n\nconst wrongData: unknown = \"Hello, world!\";\n\nconst anotherWrongData: unknown = [ \"Hello\", 123 ];\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\nconst protectionGoneWrongAgain = protect(anotherWrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n\nif (protectionGoneWrongAgain.success) {\n  console.log(protectionGoneWrongAgain.data);\n} else {\n  console.log(protectionGoneWrongAgain.errors);\n}\n```\n\n```json\n[ \"Hello\", \"world!\" ]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not an array\"\n  }\n]\n[\n  {\n    \"path\": \"[1]\",\n    \"message\": \"This is not a string\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### length\n\nValidate the length of a array.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.array({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.Array.length({\n      length: 3,\n      message: \"This should be an array of 3 elements\"\n    })\n  ],\n  schema: Kryptonian.Kalel.string({\n    message: \"This is not a string\",\n    rules: []\n  })\n}));\n\nconst goodData: unknown = [ \"Hello\", \"world\", \"!\" ];\n\nconst wrongData: unknown = \"Hello, world!\";\n\nconst anotherWrongData: unknown = [ \"Hello\", \"world!\" ];\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\nconst protectionGoneWrongAgain = protect(anotherWrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n\nif (protectionGoneWrongAgain.success) {\n  console.log(protectionGoneWrongAgain.data);\n} else {\n  console.log(protectionGoneWrongAgain.errors);\n}\n```\n\n```json\n[ \"Hello\", \"world\", \"!\" ]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This is not an array\"\n  }\n]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an array of 3 elements\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### lengthBetween\n\nValidate that the length of a array is between a range.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.array({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.Array.lengthBetween({\n      minimum: 2,\n      maximum: 3,\n      message: \"This should be an array of 2 to 3 elements\"\n    })\n  ],\n  schema: Kryptonian.Kalel.string({\n    message: \"This is not a string\",\n    rules: []\n  })\n}));\n\nconst goodData: unknown = [ \"Hello\", \"world\", \"!\" ];\n\nconst wrongData: unknown = [ \"Hello\" ];\n\nconst anotherWrongData: unknown = [ \"Well\", \"hello\", \"world\", \"!\" ];\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\nconst protectionGoneWrongAgain = protect(anotherWrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n\nif (protectionGoneWrongAgain.success) {\n  console.log(protectionGoneWrongAgain.data);\n} else {\n  console.log(protectionGoneWrongAgain.errors);\n}\n```\n\n```json\n[ \"Hello\", \"world\", \"!\" ]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an array of 2 to 3 elements\"\n  }\n]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an array of 2 to 3 elements\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### minimumLength\n\nValidate that the length of a array is above a value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.array({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.Array.minimumLength({\n      minimum: 2,\n      message: \"This should be an array of at least 2 elements\"\n    })\n  ],\n  schema: Kryptonian.Kalel.string({\n    message: \"This is not a string\",\n    rules: []\n  })\n}));\n\nconst goodData: unknown = [ \"Hello\", \"world\" ];\n\nconst wrongData: unknown = [ \"Hello\" ];\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n[ \"Hello\", \"world\" ]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an array of at least 2 elements\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### maximumLength\n\nValidate that the length of a array is above a value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.array({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.Array.maximumLength({\n      maximum: 2,\n      message: \"This should be an array of at most 2 elements\"\n    })\n  ],\n  schema: Kryptonian.Kalel.string({\n    message: \"This is not a string\",\n    rules: []\n  })\n}));\n\nconst goodData: unknown = [ \"Hello\", \"world\" ];\n\nconst wrongData: unknown = [ \"Hello\", \"world\", \"!\" ];\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n[ \"Hello\", \"world\" ]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an array of at most 2 elements\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n#### nonEmpty\n\nValidate that the length of a array is above 0.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.array({\n  message: \"This is not an array\",\n  rules: [\n    Kryptonian.Kalel.Array.nonEmpty({\n      message: \"This should be an array of at least 1 element\"\n    })\n  ],\n  schema: Kryptonian.Kalel.string({\n    message: \"This is not a string\",\n    rules: []\n  })\n}));\n\nconst goodData: unknown = [ \"Hello\", \"world\" ];\n\nconst wrongData: unknown = [];\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n[ \"Hello\", \"world\" ]\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be an array of at least 1 element\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### document\n\n`document` is a schema that represents a `Kryptonian.Jorel.Document` that you can use to validate files. This is tailored to work well with Jorel, our client/server architecture solution.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.document({\n  message: \"This should be a Document\"\n}));\n\nconst goodData: unknown = Kryptonian.Jorel.Document.fromFile(new File([], \"good.txt\", {\n  type: \"text/plain\"\n}));\n\nconst badData: unknown = new File([], \"bad.txt\");\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(wrongData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\n{\n  \"bytes\": \"\",\n  \"name\": \"good.txt\",\n  \"mimeType\": \"text/plain\"\n}\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be a Document\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### unknown\n\nUnknown is a schema representing a TypeScript unknown value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.unknown());\n\nconst goodData: unknown = \"Hello, world!\";\nconst alsoGoodData: unknown = 42;\n\nconst protectionGoneRight = protect(goodData);\nconst alsoGoodProtection = protect(alsoGoodData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (alsoGoodProtection.success) {\n  console.log(alsoGoodProtection.data);\n} else {\n  console.log(alsoGoodProtection.errors);\n}\n```\n\n```json\n\"Hello, world!\"\n42\n```\n\n[Back to summary](#summary)\n\n### any\n\nUnknown is a schema representing a TypeScript any value.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.any());\n\nconst goodData: unknown = \"Hello, world!\";\nconst alsoGoodData: unknown = 42;\n\nconst protectionGoneRight = protect(goodData);\nconst alsoGoodProtection = protect(alsoGoodData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (alsoGoodProtection.success) {\n  console.log(alsoGoodProtection.data);\n} else {\n  console.log(alsoGoodProtection.errors);\n}\n```\n\n```json\n\"Hello, world!\"\n42\n```\n\n[Back to summary](#summary)\n\n### empty\n\n`empty` is a schema representing the `void` value in TypeScript. Any value that\nis `undefined` is allowed in this schema. Also, if validating function\narguments, `void` represent the absence of value, so you can also omit the\nvalue in this schema.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.empty({\n  message: \"This should be empty (void or undefined)\"\n}));\n\nconst goodData: unknown = undefined;\nconst badData: unknown = null;\n\nconst protectionGoneRight = protect(goodData);\nconst protectionGoneWrong = protect(badData);\n\nif (protectionGoneRight.success) {\n  console.log(protectionGoneRight.data);\n} else {\n  console.log(protectionGoneRight.errors);\n}\n\nif (protectionGoneWrong.success) {\n  console.log(protectionGoneWrong.data);\n} else {\n  console.log(protectionGoneWrong.errors);\n}\n```\n\n```json\nundefined\n[\n  {\n    \"path\": \"\",\n    \"message\": \"This should be empty (void or undefined)\"\n  }\n]\n```\n\n[Back to summary](#summary)\n\n### oneOf\n\n`oneOf` is a function that returns a `OneOfSchema\u003cSchema\u003e` that helps you validate a union of multiple things, very useful to return multiple types at once and have the client validate them.\n\nFor instance, you may want to return multiple business errors without the fear of changing anything and desynchronizing your client application and your server application. Returning business errors this way is a very powerful way of discriminating errors and preventing logic errors.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst protect = Kryptonian.Kalel.createProtector(Kryptonian.Kalel.oneOf([\n  Kryptonian.Kalel.object({\n    message: \"This should be an object\",\n    fields: {\n      success: Kryptonian.Kalel.literal({\n        value: true as const,\n        message: \"This should be true\"\n      }),\n      message: Kryptonian.Kalel.string({\n        message: \"This should be a string\",\n        rules: []\n      })\n    }\n  }),\n  Kryptonian.Kalel.object({\n    message: \"This should be an object\",\n    fields: {\n      success: Kryptonian.Kalel.literal({\n        value: false as const,\n        message: \"This should be false\"\n      }),\n      error: Kryptonian.Kalel.string({\n        message: \"This should be a string\",\n        rules: []\n      })\n    }\n  })\n]));\n\nconst data: unknown = {\n  success: true,\n  message: \"Successfully added the user in database\"\n};\n\nconst anotherData: unknown = {\n  success: false,\n  error: \"Username is already taken\"\n}\n\nconst protection = protect(data);\nconst anotherProtection = protect(anotherData);\n\nif (protection.success) {\n  if (protection.data.success) {\n    console.log(protection.data.message);\n  } else {\n    protection.data.error;\n  }\n} else {\n  console.log(protection.errors);\n}\n\nif (anotherProtection.success) {\n  if (anotherProtection.data.success) {\n    console.log(anotherProtection.data.message);\n  } else {\n    console.log(anotherProtection.data.error)\n  }\n} else {\n  console.log(anotherProtection.errors);\n}\n```\n\n```json\n\"Successfully added the user in database\"\n\"Username is already taken\"\n```\n\n[Back to summary](#summary)\n\n### Jorel\n\nJorel is the name of the client/server technology that is inherent to the Kryptonian library. With it, you can define a server and a client that sends data to each other in a pure, functional and safe way.\n\n[Back to summary](#summary)\n\n#### createRoutes\n\ncreateRoutes is a function that will help you define the shape of your server using a validation schema.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst routes = Kryptonian.Kalel.Jorel.createRoutes({\n  createKryptonian: {\n    request: Kryptonian.Kalel.object({\n      message: \"This should be a object\",\n      fields: {\n        name: Kryptonian.Kalel.string({\n          message: \"Name is not a string\",\n          rules: []\n        })\n      }\n    }),\n    response: Kryptonian.Kalel.string({\n      rules: [],\n      message: \"Expected a string as the response\"\n    })\n  },\n  getKryptonians: {\n    request: Kryptonian.Kalel.none({\n      message: \"Expected nothing except null\"\n    }),\n    response: Kryptonian.Kalel.array({\n      message: \"Response is not a array\",\n      rules: [],\n      schema: Kryptonian.Kalel.string({\n        message: \"Response is not a array of string\",\n        rules: []\n      })\n    })\n  }\n});\n```\n\n[Back to summary](#summary)\n\n#### createRoute\n\nWhen your application grows in complexity and number of routes, you can use the `createRoute` function to create routes in their own files, allowing you to scale your application easily. It also helps reducing the amount of lines of file could have if you declared all of your routes inside the `createRoutes` function.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nexport const createKryptonian = Kryptonian.Jorel.createRoute({\n  request: Kryptonian.Kalel.object({\n    message: \"This should be a object\",\n    fields: {\n      name: Kryptonian.Kalel.string({\n        message: \"Name is not a string\",\n        rules: []\n      })\n    }\n  }),\n  response: Kryptonian.Kalel.string({\n    rules: [],\n    message: \"Expected a string as the response\"\n  })\n});\n```\n\n[Back to summary](#summary)\n\n#### createServerRouter\n\n`createServerRouter` is a function that will take as input your server, and will let your define an implementation for the latter. Once it has been created, it must be fed to an adapter that can turn an abstract response into a concrete HTTP response.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\nimport { routes } from \"@template/shared\";\nimport { getKryptonians } from \"./routes/getKryptonians\";\nimport { createKryptonian } from \"./routes/createKryptonian\";\nimport { createHttpServer } from \"./adapters/createHttpServer\";\n\nconst router = Kryptonian.Jorel.createServerRouter({\n  routes,\n  implementations: {\n    getKryptonians,\n    createKryptonian\n  }\n});\n\nconst server = createHttpServer({\n  router,\n  clients: [\"http://localhost:5173\"]\n});\n\nserver.listen(8000, \"0.0.0.0\", () =\u003e {\n  console.log(\"Server launched and ready for communications\");\n});\n```\n\nAdapters are function that take a router (just like the `createHttpServer` function above) and will receive each requests from the client applications. Whenever a request has been made, the adapter transform the request into an abstraction that can be understood by the router, and will get back an abstract response that can be turned into a concrete HTTP response.\n\nYou can of course create your own adapter to support your favorite HTTP library. Here is an example of an adapter for the built-in `http` module.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\nimport * as Http from \"http\";\nimport * as Path from \"path\";\n\n/**\n * Options used to create the HTTP server's router adapter\n */\nexport type CreateHttpServerOptions = {\n  /**\n   * The router created using the Kryptonian.Jorel.createServerRouter function\n   */\n  router: Kryptonian.Jorel.Router,\n  /**\n   * A list of clients that must be allowed to request the server when in a browser\n   */\n  clients: Array\u003cstring\u003e\n}\n\n/**\n * Create an adapter for the Router using the Node.js built-in HTTP module\n */\nexport const createHttpServer = ({ clients, router }: CreateHttpServerOptions) =\u003e {\n  const getJsonBody = (request: Http.IncomingMessage) =\u003e {\n    return new Promise\u003cJSON\u003e((resolve, reject) =\u003e {\n      let body = \"\";\n\n      request.on(\"data\", chunk =\u003e {\n        body += chunk;\n      });\n\n      request.on(\"end\", () =\u003e {\n        try {\n          const parsedBody = JSON.parse(body);\n          resolve(parsedBody);\n        } catch (error) {\n          resolve(undefined);\n        }\n      });\n\n      request.on(\"error\", (error) =\u003e {\n        reject(new Error(String(error)));\n      });\n    });\n  };\n\n  return Http.createServer(async (request, response) =\u003e {\n    const url = new URL(Path.join(\"http://localhost/\", request.url ?? \"\"));\n    const origin = request.headers.origin ?? \"\";\n    const path = url.pathname;\n    const method = request.method ?? \"GET\";\n    const foundClient = clients.find(client =\u003e origin === client) ?? \"\";\n\n    const baseHeaders = {\n      \"Content-Type\": \"application/json\",\n      \"Access-Control-Allow-Headers\": \"Content-Type\",\n      \"Access-Control-Allow-Methods\": \"POST,OPTIONS\",\n      \"Access-Control-Allow-Origin\": foundClient\n    };\n\n    try {\n      const body = await getJsonBody(request);\n\n      const routerResponse = await router({\n        body,\n        origin,\n        method,\n        path\n      });\n\n      const routerResponseHeadersWithBaseHeaders = {\n        ...routerResponse.headers,\n        ...baseHeaders\n      };\n\n      response\n        .writeHead(routerResponse.status, routerResponseHeadersWithBaseHeaders)\n        .end(JSON.stringify(routerResponse.body));\n    } catch (error) {\n      response\n        .writeHead(500, baseHeaders)\n        .end(JSON.stringify({\n          success: false,\n          errors: [\n            {\n              path: \"\",\n              message: String(error)\n            }\n          ]\n        }));\n    }\n  });\n};\n```\n\nYou can find examples of adapters in the [`template/server/apdaters`](./template/server/adapters) folder.\n\n[Back to summary](#summary)\n\n#### createServerRoute\n\n`createServerRoute` is a functon that will help you break down the routes implementations into smaller route that you can easily export.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst routes = Kryptonian.Jorel.createRoutes({\n  getUsers: {\n    request: Kryptonian.Kalel.object({\n      message: \"Request should be an object\",\n      fields: {\n        since: Kryptonian.Kalel.date({\n          message: \"Request should contain a property since of type Date\",\n          rules: []\n        })\n      }\n    }),\n    response: Kryptonian.Kalel.array({\n      message: \"Response should be an array\",\n      rules: [],\n      schema: Kryptonian.Kalel.string({\n        message: \"Response should be an array of strings\",\n        rules: []\n      })\n    })\n  }\n});\n```\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\nimport { routes } from \"./routes\";\n\nexport const getUsers = Kryptonian.Jorel.createServerRoute({\n  routes,\n  route: \"getUsers\",\n  response: async ({ since }) =\u003e {\n    return [\n      \"Kalel\",\n      \"Zorel\",\n      \"Jorel\"\n    ]\n  }\n});\n```\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\nimport * as Http from \"http\";\nimport { routes } from \"./routes\";\nimport { getUsers } from \"./routes/getUsers\";\n\nconst router = Kryptonian.Jorel.createServerRouter({\n  routes,\n  clients: [\n    \"http://localhost:5173\"\n  ],\n  implementations: {\n    getUsers\n  }\n});\n\nconst server = Http.createServer(router);\n\nserver.listen(8000, \"0.0.0.0\", () =\u003e {\n  console.log(\"Server launched and ready for communications!\");\n});\n```\n\n#### createClientRoutes\n\n`createClientRoutes` is a function that will help you request informations from the server. It implements the `Fetch` Web API, and we intend on adding support for more HTTP libraries such as Axios for instance. Each time you request something, it will pick-up the validation schema and forces you to use this schema for all your request, preventing mistakes even if you decide to update the schema. When receiving the body, data validation is also applied, so that you can't mess up manipulating data that is not validated yet.\n\n```tsx\nimport * as React from \"react\";\nimport * as Kryptonian from \"kryptonian\";\nimport { routes } from \"./routes\";\n\nconst client = Kryptonian.Jorel.createClientRoutes({\n  server: \"http://localhost:8000\",\n  routes\n});\n\nexport const Component = () =\u003e {\n  const [kryptonian, setKryptonian] = useState(\"\");\n  const [kryptonians, setKryptonians] = React.useState\u003cArray\u003cstring\u003e\u003e([]);\n\n  const updateKryptonian: React.ChangeEventHandler\u003cHTMLInputElement\u003e = React.useCallback(event =\u003e {\n    setKryptonian(event.target.value);\n  }, []);\n\n  const getKryptonians = React.useCallback(() =\u003e {\n    client.getKryptonians({\n      parameters: null,\n      options: {}\n    }).then(kryptonian =\u003e {\n      setKryptonians(kryptonian);\n    }).catch(() =\u003e {\n      if (error instanceof Kryptonian.Jorel.BadRequestError) {\n        console.log(error.errors);\n\n        return alert(\"Bad request, please check your form\");\n      } \n\n      if (error instanceof Kryptonian.Jorel.BadResponseError) {\n        console.log(error.errors);\n\n        return alert(\"Bad response from the server, please try again later.\");\n      }\n\n      alert(\"Unknown error, sorry for the inconvenience!\");\n    });\n  }, []);\n\n  const createKryptonian: React.FormEventHandler = React.useCallback(event =\u003e {\n    event.preventDefault();\n\n    client.createKryptonian({\n      parameters: {\n        name: kryptonian\n      },\n      options: {}\n    }).then(() =\u003e {\n      alert(\"Kryptonian saved!\");\n    }).catch(error =\u003e {\n      alert(\"An error occurred\");\n    });\n  }, [kryptonian]);\n\n  return (\n    \u003cReact.Fragment\u003e\n      \u003cform onSubmit={createKryptonian}\u003e\n        \u003cinput\n          type=\"text\"\n          value={kryptonian}\n          onChange={updateKryptonian} /\u003e\n        \u003cbutton type=\"submit\"\u003e\n          Save\n        \u003c/button\u003e\n      \u003c/form\u003e\n      \u003cul\u003e\n        {kryptonians.map(kryptonian =\u003e (\n          \u003cli\u003e\n            {kryptonian}\n          \u003c/li\u003e\n        ))}\n      \u003c/ul\u003e\n    \u003c/React.Fragment\u003e\n  );\n};\n```\n\nThat's it, in a few lines, you just created your own server/client application using React for this example. Note that it works with absolutely any JavaScript framework, and even Vanilla TypeScript since it has no external dependencies. You could of course do the same in Vue.js for instance.\n\n```html\n\u003cscript lang=\"ts\" setup\u003e\nimport * as Vue from \"vue\";\nimport * as Kryptonian from \"kryptonian\";\nimport { routes } from \"./routes\";\n\nconst client = Kryptonian.Jorel.createClientRoutes({\n  server: \"http://localhost:8000\",\n  routes\n});\n\nconst kryptonian = Vue.ref(\"\");\nconst kryptonians = Vue.ref\u003cArray\u003cstring\u003e\u003e([]);\n\nconst updateKryptonian = (event: EventTarget) =\u003e {\n  kryptonian.value = event.target.value;\n};\n\nconst getKryptonians = () =\u003e {\n  client.getKryptonians({\n    parameters: null,\n    options: {}\n  }).then(kryptonian =\u003e {\n    setKryptonians(kryptonian);\n  }).catch(error =\u003e {\n    if (error instanceof Kryptonian.Jorel.BadRequestError) {\n      console.log(error.errors);\n\n      return alert(\"Bad request, please check your form\");\n    } \n\n    if (error instanceof Kryptonian.Jorel.BadResponseError) {\n      console.log(error.errors);\n\n      return alert(\"Bad response from the server, please try again later.\");\n    }\n\n    alert(\"Unknown error, sorry for the inconvenience!\");\n  });\n};\n\nconst createKryptonian = (event: FormEvent) =\u003e {\n  event.preventDefault();\n\n  client.createKryptonian({\n    parameters: {\n      name: kryptonian.value\n    },\n    options: {}\n  }).then(() =\u003e {\n    alert(\"Kryptonian saved!\");\n  }).catch(error =\u003e {\n    alert(\"An error occurred\");\n  });\n}\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cform\u003e\n    \u003cinput v-model=\"kryptonian\"\u003e\n  \u003c/form\u003e\n  \u003cul\u003e\n    \u003cli v-for=\"kryptonian in kryptonians\"\u003e\n      {{ kryptonian }}\n    \u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/template\u003e\n```\n\n[Back to summary](#summary)\n\n#### Document\n\n`Kryptonian.Jorel.Document` is a special class that brings a lot more capabilities than a regular `File` since it can be serialized and sent easily along with your other regular data. This makes it trivial to send files from a client and implement your business logic around your data on the server.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst routes = Kryptonian.Jorel.createRoutes({\n  sendKryptonianFile: {\n    request: Kryptonian.Kalel.object({\n      message: \"Request should be an object\",\n      fields: {\n        file: Kryptonian.Kalel.document({\n          message: \"file should be a file\",\n        }),\n        message: Kryptonian.Kalel.string({\n          message: \"message should be a string\",\n          rules: []\n        })\n      }\n    }),\n    response: Kryptonian.Kalel.none({\n      message: \"\"\n    })\n  }\n});\n```\n\n```typescript\nimport { routes } from \"@template/shared\";\nimport * as Kryptonian from \"kryptonian\";\nimport * as FileSystem from \"fs/promises\";\nimport * as Path from \"path\";\nimport * as Crypto from \"crypto\";\n\nexport const sendKryptonianFile = Kryptonian.Jorel.createServerRoute({\n  routes,\n  route: \"sendKryptonianFile\",\n  response: async ({ file, message }) =\u003e {\n    const extension = Path.extname(file.name);\n    const uploadFolderPath = \"uploads/files\";\n    const fileName = `${uploadFolderPath}/${Crypto.randomUUID()}${extension}`;\n\n    await FileSystem.mkdir(uploadFolderPath, { recursive: true });\n    await FileSystem.writeFile(fileName, file.toBuffer());\n\n    console.log({ message: `Message from the client: ${message}` });\n\n    return null;\n  }\n});\n```\n\n```typescript\nimport * as React from \"react\";\nimport * as Kryptonian from \"kryptonian\";\nimport { routes } from \"@template/shared\";\n\nconst client = Kryptonian.Jorel.createClientRoutes({\n  server: \"http://localhost:8000\",\n  routes\n});\n\nexport const App = () =\u003e {\n  const [file, setFile] = React.useState(new File([], \"\"));\n  const [message, setMessage] = React.useState(\"\");\n\n  const updateFile: React.ChangeEventHandler\u003cHTMLInputElement\u003e = React.useCallback(event =\u003e {\n    if (event.target.files \u0026\u0026 event.target.files[0] instanceof File) {\n      setFile(event.target.files[0]);\n    }\n  }, []);\n\n  const updateMessage: React.ChangeEventHandler\u003cHTMLInputElement\u003e = React.useCallback(event =\u003e {\n    setMessage(event.target.value);\n  }, []);\n\n  const sendKryptonianFile: React.FormEventHandler = React.useCallback(event =\u003e {\n    event.preventDefault();\n\n    Kryptonian.Jorel.Document.fromFile(file).then(documentFile =\u003e {\n      return client.sendKryptonianFile({\n        parameters: {\n          file: documentFile,\n          message\n        },\n        options: {}\n      }).then(response =\u003e {\n        console.log(\"Success!\");\n      }).catch(error =\u003e {\n        console.error(error);\n      });\n    }).catch(error =\u003e {\n      console.error(error);\n    });\n  }, [file, message]);\n\n  return (\n    \u003cform onSubmit={sendKryptonianFile}\u003e\n      \u003clabel htmlFor=\"message\"\u003e\n        Message about this file\n      \u003c/label\u003e\n      \u003cinput\n        id=\"message\"\n        type=\"text\"\n        value={message}\n        onChange={updateMessage} /\u003e\n      \u003cinput\n        type=\"file\"\n        onChange={updateFile} /\u003e\n      \u003cbutton type=\"submit\"\u003e\n        Send\n      \u003c/button\u003e\n    \u003c/form\u003e\n  );\n};\n```\n\n**Important note**: this works by converting the file into its `base64` representation. This has been tested locally with files over 10mo and a total overhead of 50ms so this would be just fine for images for instances. For larger files, and if you are working on computation-based platform like AWS or GCP, you should probably be better off using a plain Express route to handle files separately for instance.\n\nIf you decide to use this solution, you'll need to increase the body size limit of your web server's implementation. For instance, if you are using the `createExpressAdapter` for your server.\n\n```typescript\n  ...\n  // This limit right there ----------------------------------+\n  //                                                          |\n  //                                                          |\n  //                                                          |\n  //                                                          v\n  server.post(\"*\", bodyParser.json({ strict: false, limit: \"100mb\" }), async (request, response) =\u003e {\n    const url = new URL(Path.join(\"http://localhost:8000\", request.url));\n    const origin = request.headers.origin ?? \"\";\n    const method = \"POST\";\n    const path = url.pathname;\n    const body = request.body;\n    ...\n```\n\nAlso note that if your Node.js application is behind a reverse-proxy server (NGINX, Apache, etc...), you will probably need to increase the body size limit for your proxy as well.\n\n[Back to summary](#summary)\n\n#### getting started\n\nYou can start writing client \u0026 server applications right away by using the template folder.\n\nHere is how you can get this template to get started developing locally.\n\n```bash\nnpx degit aminnairi/kryptonian/template#production my-project\ncd my-project\n# read the README.md!\n```\n\nWhere `production` is the branch to use. We recommend starting from the `production` branch since it is the most stable branch for using this template, but you can use any branches from the GitHub repository as well as tags.\n\nHere is an example using the `2.0.0` release tag for demonstration purposes.\n\n```bash\nnpx degit aminnairi/kryptonian/template#2.0.0 my-project\ncd my-project\n# read the README.md!\n```\n\nBe sure to install the package for all workspaces inside the template using the following command before starting the servers.\n\nHere again, using the `2.0.0` release tag for demonstration purposes.\n\n```bash\nnpm --workspaces install kryptonian@2.0.0\n```\n\n[Back to summary](#summary)\n\n### InferType\n\n`InferType` helps you get the underlying TypeScript type of a schema.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nconst schema = Kryptonian.Kalel.array({\n  message: \"This should be an array\",\n  rules: [],\n  schema: Kryptonian.Kalel.string({\n    message: \"This should be an array of strings\",\n    rules: []\n  })\n});\n\ntype Schema = Kryptonian.InferType\u003ctypeof schema\u003e;\n// string[]\n\nconst anotherSchema = Kryptonian.Kalel.object({\n  message: \"This should be an object\",\n  fields: {\n    email: Kryptonian.Kalel.string({\n      message: \"Field email should be a string\"\n    }),\n    administrator: Kryptonian.Kalel.boolean({\n      message: \"Field administrator should be a boolean\"\n    })\n  }\n});\n\ntype AnotherSchema = Kryptonian.Kalel.InferType\u003ctypeof anotherSchema\u003e;\n// { email: string, administrator: boolean }\n```\n\n[Back to summary](#summary)\n\n### Custom rules\n\nA rule is a function that is applied in most functions exposed by this library. Every time you see a function taking a `rules` properties as its argument, it means that it is accepting an array of rules. In fact, creating custom rules would be exactly the same as the rules defined by this library. Here is for instance the source-code for the `Kyrptonian.Kalel.Array.length` rule.\n\n```typescript\nexport interface LengthOptions {\n  length: number,\n  message: string\n}\n\nexport const length = ({ length, message }: LengthOptions): ArrayRule =\u003e {\n  return {\n    message,\n    valid: value =\u003e value.length === length\n  };\n};\n```\n\nAnd here is another example featuring the `Kryptonian.Kalel.String.minimumLength` rule.\n\n```typescript\nexport interface MinimumLengthOptions {\n  minimum: number,\n  message: string\n}\n\nexport const minimumLength = ({ minimum, message }: MinimumLengthOptions): StringRule =\u003e {\n  return {\n    message,\n    valid: value =\u003e value.length \u003e= minimum\n  }\n}\n```\n\nAs you probably guessed, a rule is a function. And those functions must return a rule. There as several rules that you can return and for each type that is exposed (`Kryptonian.Kalel.Array`, `Kryptonian.Kalel.String`, ...) there is an associated rule that you can import.\n\nHere is a non-exhaustive list of rules, and many more to come in a near future.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nKryptonian.Kalel.StringRule; // For strings\nKryptonian.Kalel.NumberRule; // For numbers\nKryptonian.Kalel.ArrayRule; // For arrays\nKryptonian.Kalel.DateRule; // For dates\n```\n\nEvery rule is a pure function, meaning it should take an argument (you can also choose not to accept any argument such as the `Kryptonian.Kalel.String.email` rule) and must return a rule. There is no mutation nor effect that is going on in a rule, bringing guarantees and robustness to the library itself. In fact, every function exposed (except for `Jorel.createClientRoutes` and `Jorel.createServerRouter`) is a pure function and will not imply side-effects of any kind.\n\nHere is an example of a rule that you might want to create to valide that a user's age is in legal compliance with your business domain.\n\n```typescript\nimport * Kryptonian from \"kryptonian\";\n\nexport interface ValidAgeOptions {\n  message: string\n}\n\nexport const validAge = ({ message }: ValidAgeOptions): Kryptonian.Kalel.NumberRule =\u003e {\n  return {\n    message,\n    valid: age =\u003e age \u003e= 18 \u0026\u0026 age \u003c= 60\n  }\n};\n```\n\nAs you can see, here we can validate that the age of a user is between `18` and `60` (those are abritrary values, of course this would be different from an application to another).\n\nOne important thing to note here is that you don't have to type yourself the value of the `age` variable in this case, the `NumberRule` is here to ensure that it is always a `number` type.\n\nWhat would happen if you use the wrong rule? Let's find out.\n\n```typescript\nimport * as Kryptonian from \"kryptonian\";\n\nexport interface ValidAgeOptions {\n  message: string\n}\n\nexport const validAge = ({ message }: ValidAgeOptions): Kryptonian.Kalel.StringRule =\u003e {\n  return {\n    message,\n    valid: age =\u003e age \u003e= 18 \u0026\u0026 age \u003c= 60\n    // Operator '\u003e=' cannot be applied to types 'string' and 'number'.ts(2365)\n    // Operator '\u003c=' cannot be applied to types 'string' and 'number'.ts(2365)\n  }\n};\n```\n\nWe added a comment to help you understand this code without having to test it yourself (but you are encouraged to do so!). As you can see, there is no way we can make a mistake by comparing a `string` with a `number` here since now that we replaced `Kryptonian.Kalel.NumberRule` with `Kryptonian.Kalel.StringRule`, the `age` value is typed as a `string`, not a `number`. Hence the error we got in the comment below the comparison.\n\nThat's it! There is nothing more to know about custom rules and it is very trivial and easy to create its own. \n\nMore validation are yet to be brought with each and every future releases of this library, and if you don't find your use-case in those, you can probably be off creating your own custom rule in no time!\n\n[Back to summary](#summary)\n\n## Issues\n\nSee [`issues`](./issues).\n\n[Back to summary](#summary)\n\n## Changelog\n\nSee [`CHANGELOG.md`](./CHANGELOG.md).\n\n[Back to summary](#summary)\n\n## Code of conduct\n\nSee [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md).\n\n[Back to summary](#summary)\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](./CONTRIBUTING.md).\n\n[Back to summary](#summary)\n\n## License\n\nSee [`LICENSE`](./LICENSE).\n\n[Back to summary](#summary)\n\n## Security\n\nSee [`SECURITY.md`](./SECURITY.md).\n\n[Back to summary](#summary)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faminnairi%2Fkryptonian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faminnairi%2Fkryptonian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faminnairi%2Fkryptonian/lists"}