UNPKG

valia

Version:

A runtime data validator in TypeScript with advanced type inference, built-in validation functions, and seamless integration for server and client environments.

417 lines (339 loc) β€’ 13.1 kB
# Valia &middot; [![npm version](https://img.shields.io/npm/v/valia.svg?style=flat)](https://www.npmjs.com/package/valia) Runtime data validator witten in TypeScript. ⚑ **Powerful and Smart Validation** – Seamlessly integrates into **server** and **client** environments. 🧠 **Advanced Type Inference** – With advanced type inference, you can retrieve a type that strictly adheres to your schema definition. Combined with **Type Guards**, this ensures that the validated data’s type is **precisely inferred**, allowing you to handle your data **safely and reliably** across your application. πŸ“¦ **Built-in Validation** – Standard functions like `isEmail`, `isUuid`, and more to handle common validation needs effortlessly, while ensuring industry-standard compliance, saving you from writing tedious custom validators πŸš€. ## Table of Contents - [Schema](#schema) - [Instance](#instance) - [Definition](#definition) - [Testers](#testers) - [String](#string-1) - [Object](#object) - [Tools](#tools) - [String](#string-2) ## Getting started ``` npm install valia ``` ```ts import { Schema, SchemaInfer } from 'valia'; const userSchema = new Schema({ type: "struct", struct: { name: { type: "string" }, role: { type: "string", enum: ["ADMIN", "WORKER", "CUSTOMER"] } } }); type UserSchema = SchemaInfer<typeof userSchema>; let data: unknown = { name: "Tintin", role: "ADMIN" }; if (userSchema.validate(data) { /* The data type is now: data: { name: string; role: "ADMIN" | "WORKER" | "CUSTOMER"; } */ console.log(data); } ``` <br/><br/> # Schema ## Instance |Property / Method|Description| |--|--| |`criteria` |Property representing the mounted validation criteria.| |`validate()`|Validates the provided data against the schema. A boolean is returned. This function is a type guard, so if it returns true, the value passed as a parameter will be of the type defined by your schema.<br/>[Learn more about type guards](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)| |`evaluate()`|Validates the provided data against the schema. An object is returned with the value passed as a parameter and the rejection status. If the reject status is null, then the returned value will have the type defined by your schema.| ```ts criteria: MountedCriteria<T>; validate(value: unknown): value is GuardedCriteria<T>; evaluate(value: unknown): { reject: SchemaReject, value: null } | { reject: null, value: GuardedCriteria<T> }; ``` ```ts interface SchemaReject { path: { explicit: [], implicit: [] }; code: string; type: string; label: string | undefined; message: string | undefined; }; ``` #### Exemple : > [!NOTE] > The `criteria` properties of schemas are mounted only once, even if you use them in another schema. > This can be useful if memory is an important consideration for you or if you plan to create many sub-schemas. ```ts const nameFormat = new Schema({ label: "NAME_FORMAT", type: "string", min: 3, max: 32 }); const ageFormat = new Schema({ label: "AGE_FORMAT", type: "number", min: 13, max: 128 }); const userSchema = new Schema({ type: "struct", struct: { name: nameFormat.criteria, age: ageFormat.criteria } }); let data = { name: "Waitron", age: 200 }; const { reject } = userSchema.evaluate(data); console.log(reject); ``` ``` { path: { explicit: ['struct', 'age'], implicit: ['&', 'age'] }, code: 'VALUE_SUPERIOR_MAX', type: 'number', label: 'AGE_FORMAT', message: undefined } ``` ## Definition [Number](#number) β€’ [String](#string) β€’ [Boolean](#boolean) β€’ [Struct](#struct) β€’ [Record](#record) β€’ [Tuple](#tuple) β€’ [Array](#array) β€’ [Union](#union) β€’ [Symbol](#symbol) > [!NOTE] > The order of property definitions in the arrays is the same as when executing the tests by the checker. ### Global |Property|Type|Default|Description| |--|--|--|--| |`nullable?` |`boolean`|`false`|Allows `null`| |`undefinable?`|`boolean`|`false`|Allows `undefined`| |`label?` |`string` | |String that will be returned in the reject object. Ideal for adding your own error codes, for example.| |`message?` |`string` | |String that will be returned in the reject object.| ```ts const schema = new Schema({ type: "string", optional: true, label: "OPTIONAL_STRING" }); ``` ### Number |Property<br/><img width="auto"/>|Type<br/><img width="310"/>|Description<br/><img width="auto"/>| |--|--|--| |`type` |`"number"` |Type name| |`min?` |`number` |Minimum value accepted| |`max?` |`number` |Maximum value accepted| |`enum?` |`number[]\|Record<string\|number, number>`|Restrict the value to the items of an array, the values of an object, or the values of a TypeScript Enum.| |`custom?`|`(x: number) => boolean` |Customized test function| ```ts const schema = new Schema({ type: "number", min: 0, max: 50, custom(x) { return (x % 2 === 0); } }); ``` ### String |Property<br/><img width="auto"/>|Type<img width="320"/>|Default<br/><img width="auto"/>|Description<br/><img width="auto"/>| |--|--|--|--| |`type` |`"string"` | |Type name| |`empty?` |`boolean` |`true`|If the string can be empty| |`min?` |`number` | |Minimum length accepted| |`max?` |`number` | |Maximum length accepted| |`enum?` |`string[]\|Record<string\|number, string>`| |Restrict the value to the items of an array, the values of an object, or the values of a TypeScript Enum.| |`regex?` |`RegExp` | |A native regex| |`tester?`|`{ name: string, params: object}` | |Allows you to directly apply a test that you will find [here](#string-1), with its parameters if necessary.| |`custom?`|`(x: string) => boolean` | |Customized test function| ```ts const schema = new Schema({ type: "string", min: 0, max: 6, empty: false, tester: { name: "isAscii" } }); ``` ### Boolean |Property|Type|Default|Description| |--|--|--|--| |`type`|`"boolean"`||Type name| ```ts const schema = new Schema({ type: "boolean" }); ``` ### Struct |Property|Type|Description| |--|--|--| |`type` |`"struct"` |Type name| |`optional?` |`Array<string \| symbol>` |Array of optional keys| |`struct` |`{`<br/>` [key: string \| symbol]:`<br/>` SetableCriteria \| SetableStruct;`<br/>`}`|The object's keys represent the expected keys<br/>and the attributes represent the expected types.<br/>By default, the keys are considered required.| ```ts const schema = new Schema({ type: "struct", optional: ["description"], struct: { name: { type: "string", max: 20 }, price: { type: "number", min: 0.1 }, description: { brand: { type: "string", max: 40 }, color: { type: "string", enum: ["BLACK", "WHITE"] } } } }); ``` ### Record |Property|Type|Default|Description| |--|--|--|--| |`type` |`"record"` | |Type name| |`empty?`|`boolean` |`false`|If the object can be empty| |`min?` |`number` | |Minimum properties accepted| |`max?` |`number` | |Maximum properties accepted| |`key` |`Criteria<string \| symbol>`| |Criteria of key| |`value` |`Criteria` | |Criteria of value| ```ts const schema = new Schema({ type: "record", max: 10, key: { type: "string" }, value: { type: "number" } }); ``` ### Tuple |Property|Type|Default|Description| |--|--|--|--| |`type` |`"tuple"` ||Type name| |`tuple` |`[Criteria, ...Criteria[]]`||Criteria of the tuple items| ```ts const schema = new Schema({ type: "tuple", tuple: [{ type: "string" }, { type: "number" } }); ``` ### Array |Property|Type|Default|Description| |--|--|--|--| |`type` |`"array"` | |Type name| |`empty?`|`boolean` |`false`|If the array can be empty| |`min?` |`number` | |Minimum items accepted| |`max?` |`number` | |Maximum items accepted| |`item` |`Criteria`| |Criteria of the array items| ```ts const schema = new Schema({ type: "array", empty: true, tuple: [{ type: "string" }, { type: "number" }] }); ``` ### Union |Property|Type|Default|Description| |--|--|--|--| |`type` |`"union"` ||Type name| |`union` |`[Criteria, ...Criteria[]]`||Array in which the possible criteria are listed| ```ts const schema = new Schema({ type: "array", item: { type: "union", union: [{ type: "string"}, { type: "number" }] }); ``` ### Symbol |Property|Type|Default|Description| |--|--|--|--| |`type` |`"symbol"`||Type name| |`symbol?`|`symbol` ||Symbol to check| ```ts const mySymbol = Symbol("enjoy"); const schema = new Schema({ type: "symbol", symbol: mySymbol }); ``` <br/><br/> # Testers ### String |Function|Description| |--|--| |`isAscii` |Check if all characters of the string are in the ASCII table.| |`isIp` |See **isIpV4** and **isIpV6**| |`isIpV4` |**Standard:** No standard| |`isIpV6` |**Standard:** No standard| |`isEmail` |**Standard:** RFC 5321| |`isDomain` |**Standard:** RFC 1035| |`isDataURL` |**Standard:** RFC 2397| |`isUuid` |**Standard:** RFC 9562| |`isBase16` |**Standard:** RFC 4648| |`isBase32` |**Standard:** RFC 4648| |`isBase32Hex`|**Standard:** RFC 4648| |`isBase64` |**Standard:** RFC 4648| |`isBase64Url`|**Standard:** RFC 4648| <br/> ```ts isIp(str:string, params: IsIpParams): boolean; ``` |Parameter|Description| |--|--| |`prefix?: boolean`|Must have a prefix at the end of the IP address indicating the subnet mask.<br/>(e.g., `192.168.0.1/22`)| <br/> ```ts isEmail(str:string, params: IsEmailParams): boolean; ``` |Parameter|Description| |--|--| |`allowQuotedString?: boolean` |Allows a string enclosed in quotes in the first part of the email address.| |`allowAddressLiteral?: boolean`|Allows an IPv4 or IPv6 address in place of the domain name.| <br/> ```ts isDataURL(str:string, params: IsDataUrlParams): boolean; ``` |Parameter|Description| |--|--| |`type?: string` |Specifies the type of media, corresponding to the **image** type in the example.<br/>(e.g., `data:image/gif;base64,R0lGODdhMA`)| |`subtype?: string[]`|Specifies the sub-type of media, corresponding to the **gif** sub-type in the example.<br/>(e.g., `data:image/gif;base64,R0lGODdhMA`)| <br/> ```ts isUuid(str: string, params?: IsUuidParams): boolean; ``` |Parameter|Description| |--|--| |`version?: 1\|2\|3\|4\|5\|6\|7`|The version you wish to validate. By default, all versions are validated.| <br/> ### Object |Function|Description| |--|--| |`isObject` |Checks if it is an object.| |`isBasicObject` |Checks if it is an object and if it has a prototype of `Object.prototype` or `null`.| |`isPlainObject` |Checks if it is an object, if it has a prototype of `Object.prototype` or `null`, and if it contains only keys of type `string` or `symbol`.| |`isArray` |Checks if it is an array.| |`isFunction` |Checks if it is an function.| |`isBasicFunction` |Checks if it is a function but not an async, generator or async generator function. For example, an function like `async () => void` will return false.| |`isAsyncFunction` |Checks if it is an async function.| |`isGeneratorFunction` |Checks if it is an generator function.| |`isAsyncGeneratorFunction`|Checks if it is an async generator function.| <br/><br/> # Tools ### String |Function|Description| |--|--| |`base16ToBase64`|**Standard :** RFC 4648<br/>Conversion of a string from **base16** to a string in **base64** or **base64Url**.<br/>The input does not need to be in the standard, but the output will be.| |`base16ToBase32`|**Standard :** RFC 4648<br/>Conversion of a string from **base16** to a string in **base32** or **base32Hex**.<br/>The input does not need to be in the standard, but the output will be.| |`base64ToBase16`|**Standard :** RFC 4648<br/>Conversion of a string from **base64** or **base64Url** to a string in **base16**.<br/>The input does not need to be in the standard, but the output will be.| |`base32ToBase16`|**Standard :** RFC 4648<br/>Conversion of a string from **base32** or **base32Hex** to a string in **base16**.<br/>The input does not need to be in the standard, but the output will be.| ```ts base16ToBase64(input: string, to: "B64" | "B64URL" = "B64", padding: boolean = true): string; base16ToBase32(input: string, to: "B16" | "B16HEX" = "B16", padding: boolean = true): string; base64ToBase16(input: string, from: "B64" | "B64URL" = "B64"): string; base32ToBase16(input: string, from: "B16" | "B16HEX" = "B16") => string; ``` <br/><br/> Developed in France with passion πŸ‡«πŸ‡·