{"id":15233419,"url":"https://github.com/denostack/safen","last_synced_at":"2025-04-10T05:30:32.707Z","repository":{"id":26895920,"uuid":"110927634","full_name":"denostack/safen","owner":"denostack","description":"Safen is a top-performing validation and sanitization library with easy type inference.","archived":false,"fork":false,"pushed_at":"2024-03-20T06:32:21.000Z","size":2993,"stargazers_count":38,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-01T12:03:53.316Z","etag":null,"topics":["deno","sanitization","type-inference","typescript","validation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/denostack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-16T05:32:33.000Z","updated_at":"2024-07-05T10:08:29.000Z","dependencies_parsed_at":"2024-06-19T17:35:49.338Z","dependency_job_id":"cdef5410-23ce-4707-b346-a7de2d7f875a","html_url":"https://github.com/denostack/safen","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fsafen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fsafen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fsafen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fsafen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/denostack","download_url":"https://codeload.github.com/denostack/safen/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248162903,"owners_count":21057834,"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":["deno","sanitization","type-inference","typescript","validation"],"created_at":"2024-09-29T05:08:46.447Z","updated_at":"2025-04-10T05:30:32.674Z","avatar_url":"https://github.com/denostack.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Safen \u003ca href=\"https://github.com/denostack\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/denostack/images/main/logo.svg\" width=\"160\" align=\"right\" /\u003e\u003c/a\u003e\n\n\u003cp\u003e\n  \u003ca href=\"https://github.com/denostack/safen/actions\"\u003e\u003cimg alt=\"Build\" src=\"https://img.shields.io/github/actions/workflow/status/denostack/safen/ci.yml?branch=main\u0026logo=github\u0026style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/denostack/safen\"\u003e\u003cimg alt=\"Coverage\" src=\"https://img.shields.io/codecov/c/gh/denostack/safen?style=flat-square\" /\u003e\u003c/a\u003e\n  \u003cimg alt=\"License\" src=\"https://img.shields.io/npm/l/safen.svg?style=flat-square\" /\u003e\n  \u003cimg alt=\"Language Typescript\" src=\"https://img.shields.io/badge/language-Typescript-007acc.svg?style=flat-square\" /\u003e\n  \u003cbr /\u003e\n  \u003ca href=\"https://deno.land/x/safen\"\u003e\u003cimg alt=\"deno.land/x/safen\" src=\"https://img.shields.io/badge/dynamic/json?url=https://api.github.com/repos/denostack/safen/tags\u0026query=$[0].name\u0026display_name=tag\u0026label=deno.land/x/safen@\u0026style=flat-square\u0026logo=deno\u0026labelColor=000\u0026color=777\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/safen\"\u003e\u003cimg alt=\"Version\" src=\"https://img.shields.io/npm/v/safen.svg?style=flat-square\u0026logo=npm\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://npmcharts.com/compare/safen?minimal=true\"\u003e\u003cimg alt=\"Downloads\" src=\"https://img.shields.io/npm/dt/safen.svg?style=flat-square\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nSafen is a high-performance validation and sanitization library with easy type\ninference. Its syntax is similar to TypeScript interface, making it easy to\ncreate validation rules.\n\nhttps://user-images.githubusercontent.com/4086535/203831205-8b3481cb-bb8d-4f3c-9876-e41adb6855fd.mp4\n\n## Installation\n\n**Node**\n\n```bash\nnpm install safen\n```\n\n**Deno**\n\n```ts\nimport {\n  s, // create sanitize,\n  v, // create validate,\n} from \"https://deno.land/x/safen/mod.ts\";\n```\n\n## Basic Usage\n\n**Create Validate Fn**\n\n```ts\nimport { v } from \"https://deno.land/x/safen/mod.ts\";\n\nconst validate = v(String); // now, validate: (data: unknown) =\u003e data is string\n\nconst input = {} as unknown;\nif (validate(input)) {\n  // now input is string!\n}\n```\n\n**Create Sanitize Fn**\n\n```ts\nimport { s } from \"https://deno.land/x/safen/mod.ts\";\n\nconst sanitize = s(String); // now, sanitize: (data: unknown) =\u003e string\n\nconst input = {} as unknown; // some unknown value\n\nsanitize(\"something\" as unknown); // return \"something\"\nsanitize(null as unknown); // throw InvalidValueError\n```\n\n## Types\n\n```ts\n// Primitive Types\nconst validate = v(String); // (data: unknown) =\u003e data is string\nconst validate = v(Number); // (data: unknown) =\u003e data is number\nconst validate = v(Boolean); // (data: unknown) =\u003e data is boolean\nconst validate = v(BigInt); // (data: unknown) =\u003e data is bigint\nconst validate = v(Symbol); // (data: unknown) =\u003e data is symbol\n\n// Literal Types\nconst validate = v(\"foo\"); // (data: unknown) =\u003e data is \"foo\"\nconst validate = v(1024); // (data: unknown) =\u003e data is 1024\nconst validate = v(true); // (data: unknown) =\u003e data is true\nconst validate = v(2048n); // (data: unknown) =\u003e data is 2048n\nconst validate = v(null); // (data: unknown) =\u003e data is null\nconst validate = v(undefined); // (data: unknown) =\u003e data is undefined\n\n// Special\nconst validate = v(v.any()); // (data: unknown) =\u003e data is any\nconst validate = v(Array); // (data: unknown) =\u003e data is any[]\n\n// Object\nconst Point = { x: Number, y: Number };\nconst validate = v({ p1: Point, p2: Point }); // (data: unknown) =\u003e data is { p1: { x: number, y: number }, p2: { x: number, y: number } }\n\n// Union\nconst validate = v(v.union([String, Number])); // (data: unknown) =\u003e data is string | number\n\n// Array\nconst validate = v([String]); // (data: unknown) =\u003e data is string[]\nconst validate = v([v.union([String, Number])]); // (data: unknown) =\u003e data is (string | number)[]\n```\n\n## Decorator\n\nDecorators do not affect type inference, but do affect additional validation and\ndata transformation.\n\n**Step1. Basic Sanitize**\n\n```ts\nconst sanitize = s(s.union([\n  String,\n  null,\n]));\n\nsanitize(\"hello world!\"); // return \"hello world!\"\nsanitize(\"  hello world!  \"); // return \"  hello world!  \"\nsanitize(\"    \"); // return \"    \"\nsanitize(null); // return null\n```\n\n**Step2. Add trim decorator**\n\n```ts\nconst sanitize = s(s.union([\n  s.decorate(String, (d) =\u003e d.trim()),\n  null,\n]));\n\nsanitize(\"hello world!\"); // return \"hello world!\"\nsanitize(\"  hello world!  \"); // return \"hello world!\"\nsanitize(\"    \"); // return \"\"\nsanitize(null); // return null\n```\n\n**Step3. Add emptyToNull decorator**\n\n```ts\nconst sanitize = s(\n  s.decorate(\n    s.union([\n      s.decorate(String, (d) =\u003e d.trim()),\n      null,\n    ]),\n    (d) =\u003e d.emptyToNull(),\n  ),\n);\n\nsanitize(\"hello world!\"); // return \"hello world!\"\nsanitize(\"  hello world!  \"); // return \"hello world!\"\nsanitize(\"    \"); // return null\nsanitize(null); // return null\n```\n\n### Defined Decorators\n\n| Decorator                 | Validate | Transform | Type               | Description                                                                         |\n| ------------------------- | -------- | --------- | ------------------ | ----------------------------------------------------------------------------------- |\n| `alpha`                   | ✅       |           | `string`           | contains only letters([a-zA-Z]).                                                    |\n| `alphanum`                | ✅       |           | `string`           | contains only letters and numbers([a-zA-Z0-9])                                      |\n| `ascii`                   | ✅       |           | `string`           | contains only ascii characters.                                                     |\n| `base64`                  | ✅       |           | `string`           | Base64.                                                                             |\n| `between(min, max)`       | ✅       |           | `string`, `number` | value is between `{min}` and `{max}`. (ex) `between(\"aaa\",\"zzz\")`, `between(1,100)` |\n| `ceil`                    |          | ✅        | `number`           | Math.ceil. (ref. `floor`, `round`)                                                  |\n| `creditcard`              | ✅       |           | `string`           | valid Credit Card number. cf. `0000-0000-0000-0000`                                 |\n| `dateformat`              | ✅       |           | `string`           | valid Date string(RFC2822, ISO8601). cf. `2018-12-25`, `12/25/2018`, `Dec 25, 2018` |\n| `email`                   | ✅       |           | `string`           | valid E-mail string.                                                                |\n| `emptyToNull`             |          | ✅        | `string or null`   | empty string(`\"\"`) to null                                                          |\n| `floor`                   |          | ✅        | `number`           | Math.floor. (ref. `ceil`, `round`)                                                  |\n| `hexcolor`                | ✅       |           | `string`           | valid Hex Color string. cf. `#ffffff`                                               |\n| `ip(version = null)`      | ✅       |           | `string`           | valid UUID.\u003cbr /\u003eversion is one of `null`(both, default), `v4`, and `v6`.           |\n| `json`                    | ✅       |           | `string`           | valid JSON.                                                                         |\n| `length(size)`            | ✅       |           | `string`, `any[]`  | length is `{size}`.                                                                 |\n| `lengthBetween(min, max)` | ✅       |           | `string`, `any[]`  | length is between `{min}` and `{max}`.                                              |\n| `lengthMax(max)`          | ✅       |           | `string`, `any[]`  | length is less than `{max}`.                                                        |\n| `lengthMin(min)`          | ✅       |           | `string`, `any[]`  | length is greater than `{min}`.                                                     |\n| `lowercase`               | ✅       |           | `string`           | lowercase.                                                                          |\n| `macaddress`              | ✅       |           | `string`           | valid Mac Address.                                                                  |\n| `max(max)`                | ✅       |           | `string`, `number` | value is less than `{min}`.                                                         |\n| `min(min)`                | ✅       |           | `string`, `number` | value is greater than `{max}`.                                                      |\n| `port`                    | ✅       |           | `number`           | valid PORT(0-65535).                                                                |\n| `re`                      | ✅       |           | `string`           | match RegExp.                                                                       |\n| `round`                   |          | ✅        | `number`           | Math.round. (ref. `ceil`, `floor`)                                                  |\n| `stringify`               |          | ✅        | `string`           | cast to string                                                                      |\n| `toLower`                 |          | ✅        | `string`           | change to lower case.                                                               |\n| `toUpper`                 |          | ✅        | `string`           | change to upper case.                                                               |\n| `trim`                    |          | ✅        | `string`           | trim.                                                                               |\n| `uppercase`               | ✅       |           | `string`           | uppercase.                                                                          |\n| `url`                     | ✅       |           | `string`           | valid URL.                                                                          |\n| `uuid(version = null)`    | ✅       |           | `string`           | valid UUID.\u003cbr /\u003eversion is one of `null`(default), `v3`, `v4`, and `v5`.           |\n\n## Custom Decorator\n\n```mermaid\ngraph LR;\n  A[input] --\u003e|type = unknown| B{cast};\n  B --\u003e|type = T| C{validate};\n  C --\u003e|true| D{transform};\n  C --\u003e|false| E[error];\n  D --\u003e F[output];\n```\n\n```ts\ninterface Decorator\u003cT\u003e {\n  name: string;\n  cast?(v: unknown): T;\n  validate?(v: T): boolean;\n  transform?(v: T): T;\n}\n```\n\nThe `cast` function is invoked at the beginning of the data processing pipeline,\nbefore the `validate` and `transform` functions. The purpose of the `cast`\nfunction is to ensure that the data is in the right type before being processed\nfurther.\n\nThis is an example of a cast-only function:\n\n```ts\nconst decorator: Decorator\u003cstring\u003e = {\n  name: \"json_string\",\n  cast: (v) =\u003e JSON.stringify(v),\n};\n```\n\nOnce the data has been casted, the `validate` function is called to verify the\ncontent and format of the data. This function ensures that the data is valid and\nmeets the specified criteria before being processed further.\n\nThe `transform` function, on the other hand, is invoked only after the\nvalidation function returns a `true` result. The `transform` function then\nprocesses the data according to the specified rules and criteria.\n\nTherefore, the `cast`, `validate`, and `transform` functions work together to\nensure that the data is in the right format, is valid, and is properly\nprocessed.\n\n## Benchmark\n\nPlease see [benchmark results](.benchmark).\n\n## Old Version Docs\n\n- [1.x](https://github.com/denostack/safen/tree/1.x)\n- [2.x](https://github.com/denostack/safen/tree/1.x)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenostack%2Fsafen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdenostack%2Fsafen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenostack%2Fsafen/lists"}