├── .eslintrc.yml ├── .gitbook.yaml ├── .github ├── dependabot.yml ├── semantic.yml └── workflows │ ├── auto-approve-dependabot-workflow.yml │ ├── continuous-deployment-workflow.yml │ ├── continuous-integration-workflow.yml │ └── lock-closed-issues-workflow.yml ├── .gitignore ├── .prettierrc.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── codecov.yml ├── docs ├── README.md ├── basics │ └── validating-objects.md └── introduction │ └── installation.md ├── jest.config.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── sample ├── sample1-simple-validation │ ├── Post.ts │ └── app.ts ├── sample2-using-groups │ ├── Post.ts │ └── app.ts ├── sample3-nested-objects │ ├── Post.ts │ ├── Tag.ts │ └── app.ts ├── sample4-custom-validator │ ├── CustomTextLength.ts │ ├── Post.ts │ └── app.ts ├── sample5-schemas │ ├── Post.ts │ ├── app.ts │ └── post.json ├── sample6-custom-decorator │ ├── IsLongerThan.ts │ ├── IsUserAlreadyExist.ts │ ├── User.ts │ └── app.ts ├── sample7-inheritance-support │ ├── BaseContent.ts │ ├── Post.ts │ └── app.ts ├── sample8-es6-maps │ ├── Post.ts │ ├── Tag.ts │ └── app.ts └── sample9-es6-sets │ ├── Post.ts │ ├── Tag.ts │ └── app.ts ├── src ├── container.ts ├── decorator │ ├── ValidationOptions.ts │ ├── array │ │ ├── ArrayContains.ts │ │ ├── ArrayMaxSize.ts │ │ ├── ArrayMinSize.ts │ │ ├── ArrayNotContains.ts │ │ ├── ArrayNotEmpty.ts │ │ └── ArrayUnique.ts │ ├── common │ │ ├── Allow.ts │ │ ├── Equals.ts │ │ ├── IsDefined.ts │ │ ├── IsEmpty.ts │ │ ├── IsIn.spec.ts │ │ ├── IsIn.ts │ │ ├── IsLatLong.ts │ │ ├── IsLatitude.ts │ │ ├── IsLongitude.ts │ │ ├── IsNotEmpty.ts │ │ ├── IsNotIn.ts │ │ ├── IsOptional.ts │ │ ├── NotEquals.ts │ │ ├── Validate.ts │ │ ├── ValidateBy.ts │ │ ├── ValidateIf.ts │ │ ├── ValidateNested.ts │ │ └── ValidatePromise.ts │ ├── date │ │ ├── MaxDate.ts │ │ └── MinDate.ts │ ├── decorators.ts │ ├── number │ │ ├── IsDivisibleBy.ts │ │ ├── IsNegative.ts │ │ ├── IsPositive.ts │ │ ├── Max.ts │ │ └── Min.ts │ ├── object │ │ ├── IsInstance.ts │ │ └── IsNotEmptyObject.ts │ ├── string │ │ ├── Contains.ts │ │ ├── IsAlpha.ts │ │ ├── IsAlphanumeric.ts │ │ ├── IsAscii.ts │ │ ├── IsBIC.ts │ │ ├── IsBase32.ts │ │ ├── IsBase58.ts │ │ ├── IsBase64.ts │ │ ├── IsBooleanString.ts │ │ ├── IsBtcAddress.ts │ │ ├── IsByteLength.ts │ │ ├── IsCreditCard.ts │ │ ├── IsCurrency.ts │ │ ├── IsDataURI.ts │ │ ├── IsDateString.ts │ │ ├── IsDecimal.ts │ │ ├── IsEAN.ts │ │ ├── IsEmail.ts │ │ ├── IsEthereumAddress.ts │ │ ├── IsFQDN.ts │ │ ├── IsFirebasePushId.ts │ │ ├── IsFullWidth.ts │ │ ├── IsHSL.ts │ │ ├── IsHalfWidth.ts │ │ ├── IsHash.ts │ │ ├── IsHexColor.ts │ │ ├── IsHexadecimal.ts │ │ ├── IsIBAN.ts │ │ ├── IsIP.ts │ │ ├── IsISBN.ts │ │ ├── IsISIN.ts │ │ ├── IsISO31661Alpha2.ts │ │ ├── IsISO31661Alpha3.ts │ │ ├── IsISO8601.ts │ │ ├── IsISRC.ts │ │ ├── IsISSN.ts │ │ ├── IsIdentityCard.ts │ │ ├── IsJSON.ts │ │ ├── IsJWT.ts │ │ ├── IsLocale.ts │ │ ├── IsLowercase.ts │ │ ├── IsMacAddress.ts │ │ ├── IsMagnetURI.ts │ │ ├── IsMilitaryTime.ts │ │ ├── IsMimeType.ts │ │ ├── IsMobilePhone.ts │ │ ├── IsMongoId.ts │ │ ├── IsMultibyte.ts │ │ ├── IsNumberString.ts │ │ ├── IsOctal.ts │ │ ├── IsPassportNumber.ts │ │ ├── IsPhoneNumber.spec.ts │ │ ├── IsPhoneNumber.ts │ │ ├── IsPort.ts │ │ ├── IsPostalCode.ts │ │ ├── IsRFC3339.ts │ │ ├── IsRgbColor.ts │ │ ├── IsSemVer.ts │ │ ├── IsStrongPassword.ts │ │ ├── IsSurrogatePair.ts │ │ ├── IsTimeZone.ts │ │ ├── IsUUID.ts │ │ ├── IsUppercase.ts │ │ ├── IsUrl.ts │ │ ├── IsVariableWidth.ts │ │ ├── Length.ts │ │ ├── Matches.ts │ │ ├── MaxLength.ts │ │ ├── MinLength.ts │ │ ├── NotContains.ts │ │ ├── is-iso4217-currency-code.ts │ │ └── is-tax-id.ts │ └── typechecker │ │ ├── IsArray.ts │ │ ├── IsBoolean.ts │ │ ├── IsDate.ts │ │ ├── IsEnum.ts │ │ ├── IsInt.ts │ │ ├── IsNumber.ts │ │ ├── IsObject.ts │ │ └── IsString.ts ├── index.ts ├── metadata │ ├── ConstraintMetadata.ts │ ├── MetadataStorage.ts │ ├── ValidationMetadata.ts │ └── ValidationMetadataArgs.ts ├── register-decorator.ts ├── utils │ ├── convert-to-array.util.ts │ ├── get-global.util.ts │ ├── index.ts │ └── is-promise.util.ts ├── validation-schema │ ├── ValidationSchema.ts │ └── ValidationSchemaToMetadataTransformer.ts └── validation │ ├── ValidationArguments.ts │ ├── ValidationError.ts │ ├── ValidationExecutor.ts │ ├── ValidationTypes.ts │ ├── ValidationUtils.ts │ ├── Validator.ts │ ├── ValidatorConstraintInterface.ts │ └── ValidatorOptions.ts ├── test ├── functional │ ├── conditional-validation.spec.ts │ ├── custom-decorators.spec.ts │ ├── inherited-validation.spec.ts │ ├── nested-validation.spec.ts │ ├── promise-validation.spec.ts │ ├── reject-validation.spec.ts │ ├── sync-validation.spec.ts │ ├── validation-error.spec.ts │ ├── validation-functions-and-decorators.spec.ts │ ├── validation-options.spec.ts │ ├── validator-options.spec.ts │ └── whitelist-validation.spec.ts └── utils.spec.ts ├── tsconfig.json ├── tsconfig.prod.cjs.json ├── tsconfig.prod.esm2015.json ├── tsconfig.prod.esm5.json ├── tsconfig.prod.json ├── tsconfig.prod.types.json └── tsconfig.spec.json /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | parser: '@typescript-eslint/parser' 2 | plugins: 3 | - '@typescript-eslint' 4 | parserOptions: 5 | ecmaVersion: 2018 6 | sourceType: module 7 | project: 8 | - ./tsconfig.json 9 | - ./tsconfig.spec.json 10 | extends: 11 | - 'plugin:@typescript-eslint/recommended' 12 | - 'plugin:@typescript-eslint/recommended-requiring-type-checking' 13 | - 'plugin:jest/recommended' 14 | - 'prettier' 15 | rules: 16 | '@typescript-eslint/explicit-member-accessibility': off 17 | '@typescript-eslint/no-angle-bracket-type-assertion': off 18 | '@typescript-eslint/no-parameter-properties': off 19 | '@typescript-eslint/explicit-function-return-type': off 20 | '@typescript-eslint/member-delimiter-style': off 21 | '@typescript-eslint/no-inferrable-types': off 22 | '@typescript-eslint/no-explicit-any': off 23 | '@typescript-eslint/member-ordering': 'error' 24 | '@typescript-eslint/no-unused-vars': 25 | - 'error' 26 | - args: 'none' 27 | # TODO: Remove these and fixed issues once we merged all the current PRs. 28 | '@typescript-eslint/ban-types': off 29 | '@typescript-eslint/no-unsafe-return': off 30 | '@typescript-eslint/no-unsafe-assignment': off 31 | '@typescript-eslint/no-unsafe-call': off 32 | '@typescript-eslint/no-unsafe-member-access': off 33 | '@typescript-eslint/no-unsafe-argument': off 34 | '@typescript-eslint/explicit-module-boundary-types': off 35 | '@typescript-eslint/restrict-template-expressions': off 36 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: docs 2 | structure: 3 | readme: README.md 4 | summary: docs/README.md -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | timezone: Europe/Budapest 9 | open-pull-requests-limit: 0 10 | versioning-strategy: increase 11 | commit-message: 12 | prefix: build 13 | include: scope 14 | ignore: 15 | - dependency-name: "husky" 16 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | titleAndCommits: true 2 | allowMergeCommits: false 3 | scopes: 4 | - deps 5 | - deps-dev 6 | types: 7 | - feat 8 | - fix 9 | - docs 10 | - style 11 | - refactor 12 | - perf 13 | - test 14 | - build 15 | - ci 16 | - chore 17 | - revert 18 | - merge 19 | -------------------------------------------------------------------------------- /.github/workflows/auto-approve-dependabot-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: 3 | pull_request_target 4 | jobs: 5 | dependabot: 6 | runs-on: ubuntu-latest 7 | if: github.actor == 'dependabot[bot]' 8 | steps: 9 | - name: 'Auto approve PR by Dependabot' 10 | uses: hmarr/auto-approve-action@v2.0.0 11 | with: 12 | github-token: "${{ secrets.TYPESTACK_BOT_TOKEN }}" 13 | - name: 'Comment merge command' 14 | uses: actions/github-script@v3 15 | with: 16 | github-token: ${{secrets.TYPESTACK_BOT_TOKEN }} 17 | script: | 18 | await github.issues.createComment({ 19 | owner: context.repo.owner, 20 | repo: context.repo.repo, 21 | issue_number: context.issue.number, 22 | body: '@dependabot squash and merge' 23 | }) 24 | -------------------------------------------------------------------------------- /.github/workflows/continuous-deployment-workflow.yml: -------------------------------------------------------------------------------- 1 | name: CD 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | publish: 7 | name: Publish to NPM 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: 'lts/*' 14 | registry-url: https://registry.npmjs.org 15 | - run: npm ci --ignore-scripts 16 | - run: npm run prettier:check 17 | - run: npm run lint:check 18 | - run: npm run test:ci 19 | - run: npm run build:es2015 20 | - run: npm run build:esm5 21 | - run: npm run build:cjs 22 | - run: npm run build:umd 23 | - run: npm run build:types 24 | - run: cp LICENSE build/LICENSE 25 | - run: cp README.md build/README.md 26 | - run: jq 'del(.devDependencies) | del(.scripts)' package.json > build/package.json 27 | - run: npm publish ./build 28 | env: 29 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration-workflow.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | checks: 5 | name: Linters 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: actions/setup-node@v3 10 | with: 11 | node-version: 'lts/*' 12 | - run: npm ci --ignore-scripts 13 | - run: npm run prettier:check 14 | - run: npm run lint:check 15 | tests: 16 | name: Tests 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | node-version: ['lts/*', 'current'] 21 | fail-fast: false 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Setting up Node.js (v${{ matrix.node-version }}.x) 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm ci --ignore-scripts 29 | - run: npm run test:ci 30 | - name: Upload coverage to Codecov 31 | uses: codecov/codecov-action@v5 32 | if: ${{ matrix.node-version == 'current' && github.actor != 'dependabot[bot]' }} 33 | with: 34 | files: ./coverage/clover.xml 35 | directory: ./coverage/lcov-report/ 36 | token: ${{ secrets.CODECOV_TOKEN }} 37 | verbose: true 38 | fail_ci_if_error: true 39 | build: 40 | name: Build 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v3 44 | - uses: actions/setup-node@v3 45 | with: 46 | node-version: 'lts/*' 47 | - run: npm ci --ignore-scripts 48 | - run: npm run build:es2015 49 | - run: npm run build:esm5 50 | - run: npm run build:cjs 51 | - run: npm run build:umd 52 | - run: npm run build:types 53 | -------------------------------------------------------------------------------- /.github/workflows/lock-closed-issues-workflow.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock inactive threads' 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | jobs: 6 | lock: 7 | name: Lock closed issues 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: dessant/lock-threads@v2 11 | with: 12 | github-token: ${{ github.token }} 13 | issue-lock-inactive-days: 30 14 | pr-lock-inactive-days: 30 15 | issue-lock-comment: > 16 | This issue has been automatically locked since there 17 | has not been any recent activity after it was closed. 18 | Please open a new issue for related bugs. 19 | pr-lock-comment: > 20 | This pull request has been automatically locked since there 21 | has not been any recent activity after it was closed. 22 | Please open a new issue for related bugs. 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Log files 2 | logs 3 | *.log 4 | *.tmp 5 | *.tmp.* 6 | log.txt 7 | npm-debug.log* 8 | 9 | # Testing output 10 | lib-cov/** 11 | coverage/** 12 | 13 | # Environment files 14 | .env 15 | 16 | # Dependency directories 17 | node_modules 18 | 19 | # MacOS related files 20 | *.DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | ._* 24 | UserInterfaceState.xcuserstate 25 | 26 | # Windows related files 27 | Thumbs.db 28 | Desktop.ini 29 | $RECYCLE.BIN/ 30 | 31 | # IDE - Sublime 32 | *.sublime-project 33 | *.sublime-workspace 34 | 35 | # IDE - VSCode 36 | .vscode/** 37 | !.vscode/tasks.json 38 | !.vscode/launch.json 39 | 40 | # IDE - IntelliJ 41 | .idea 42 | 43 | # Compilation output folders 44 | dist/ 45 | build/ 46 | tmp/ 47 | out-tsc/ 48 | temp 49 | 50 | # Files for playing around locally 51 | playground.ts 52 | playground.js 53 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | printWidth: 120 2 | tabWidth: 2 3 | useTabs: false 4 | semi: true 5 | singleQuote: true 6 | trailingComma: es5 7 | bracketSpacing: true 8 | arrowParens: avoid 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License 3 | 4 | Copyright (c) 2015-2020 TypeStack 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 70..100 3 | round: down 4 | precision: 2 5 | status: 6 | project: 7 | default: 8 | threshold: 0% 9 | paths: 10 | - src/**/*.ts 11 | comment: off 12 | ignore: 13 | - testing/**/*.ts 14 | - src/**/*.interface.ts 15 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | - [Read Me](../README.md) 4 | - Getting Started 5 | - [Installation](introduction/installation.md) 6 | - [Usage with Typescript](introduction/usage-with-typescript.md) 7 | - [Usage with Javascript](introduction/usage-with-javascript.md) 8 | - [Dependency Injection](introduction/usage-with-di.md) 9 | - [Core Principes](introduction/core-principes.md) 10 | - [Basic Usage](basics/README.md) 11 | - [Validating objects](basics/validating-objects.md) 12 | - [Validating arrays](basics/validating-arrays.md) 13 | - [Validating nested objects](basics/validating-nested-objects.md) 14 | - [Advanced Usage](advanced/README.md) 15 | - [Conditional Validation](advanced/conditional-validation.md) 16 | - [Validation Groups](advanced/validation-groups.md) 17 | - [Inheritance](advanced/inheritance.md) 18 | - [Custom Validation Decorators](advanced/validations-decoratos.md) 19 | - [Custom Validation Classes](advanced/validation-classes.md) 20 | - [Decorators Reference](reference/decoratos.md) 21 | - [Common Decorators](reference/common-decoratos.md) 22 | - [Number Decorators](reference/number-decoratos.md) 23 | - [String Decorators](reference/string-decoratos.md) 24 | - [Date Decorators](reference/date-decoratos.md) 25 | - [Array Decorators](reference/array-decoratos.md) 26 | - [Recipes](recipes/README.md) 27 | - [Simple Validations](https://stackblitz.com/edit/class-transformer-simple-validations) 28 | - [Nested Objects](https://stackblitz.com/edit/class-transformer-nested-objects) 29 | - [Using Groups](https://stackblitz.com/edit/class-transformer-using-groups) 30 | - [Custom Validators](https://stackblitz.com/edit/class-transformer-custom-validator) 31 | - [Custom Decorators](https://stackblitz.com/edit/class-transformer-custom-decorators) 32 | - [Using Schemas](https://stackblitz.com/edit/class-transformer-schemas) 33 | - [Inheritance](https://stackblitz.com/edit/class-transformer-inheritance) 34 | - [API Reference](api/README.md) 35 | - [validate](api/validate.md) 36 | - [ValidatorOptions ](api/ValidatorOptions.md) 37 | - [ValidationError ](api/ValidationError.md) 38 | - [Change Log](../CHANGELOG.md) 39 | -------------------------------------------------------------------------------- /docs/basics/validating-objects.md: -------------------------------------------------------------------------------- 1 | # Validating objects 2 | 3 | ```ts 4 | import { 5 | validate, 6 | validateOrReject, 7 | IsString, 8 | IsInt, 9 | IsDate, 10 | MaxLength, 11 | Min, 12 | Max, 13 | ValidationError, 14 | } from 'class-validator'; 15 | 16 | export class Book { 17 | @IsString() 18 | @MaxLength(255) 19 | title: string; 20 | 21 | @IsString() 22 | @MaxLength(255) 23 | author: string; 24 | 25 | @IsInt() 26 | @Min(0) 27 | @Max(10) 28 | rating: number; 29 | 30 | @IsDate() 31 | publishDate: Date; 32 | } 33 | 34 | const book = new Book(); 35 | book.title = 'Don Quixote'; 36 | book.author = 'Miguel De Cervantes'; 37 | book.rating = 11; 38 | book.publishDate = new Date(); 39 | 40 | validate(book).then((errors: ValidationError[]) => { 41 | if (errors.length > 0) { 42 | console.warn('validate() - Validation failed. Errors: ', errors); 43 | } 44 | }); 45 | 46 | validateOrReject(book).catch((errors: ValidationError[]) => { 47 | console.warn('validateOrReject() - Validation failed. Errors: ', errors); 48 | }); 49 | 50 | awaitExample(); 51 | 52 | async function awaitExample() { 53 | try { 54 | await validateOrReject(book); 55 | } catch (errors) { 56 | console.warn('Async validateOrReject() - Validation failed. Errors: ', errors); 57 | } 58 | } 59 | ``` 60 | 61 | Run this example on [Stackblitz](https://stackblitz.com/edit/class-validator-simple-example-u9h1ve?file=index.ts) 62 | -------------------------------------------------------------------------------- /docs/introduction/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | To install the stable version: 4 | 5 | ``` 6 | npm install --save class-validator 7 | ``` 8 | 9 | ### Usage in the browser 10 | 11 | If you want to use `class-validator` in the browser and you use Webpack then just import it into your project and Webpack will take care of the rest. 12 | 13 | ### Next version 14 | 15 | You can install the next version of `class-validator` via 16 | 17 | ``` 18 | npm install --save class-validator@next 19 | ``` 20 | 21 | > Note: The next version can break anytime without notice. Do not use this in production. 22 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | collectCoverageFrom: ['src/**/*.ts', '!src/**/index.ts', '!src/**/*.interface.ts'], 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: 'tsconfig.spec.json', 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "class-validator", 3 | "version": "0.14.2", 4 | "description": "Decorator-based property validation for classes.", 5 | "author": "TypeStack contributors", 6 | "license": "MIT", 7 | "sideEffects": false, 8 | "main": "./cjs/index.js", 9 | "module": "./esm5/index.js", 10 | "es2015": "./esm2015/index.js", 11 | "typings": "./types/index.d.ts", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/typestack/class-validator.git" 15 | }, 16 | "tags": [ 17 | "validator", 18 | "validation", 19 | "decorators", 20 | "typescript" 21 | ], 22 | "scripts": { 23 | "build": "npm run build:cjs", 24 | "build:clean": "rimraf build", 25 | "build:es2015": "tsc --project tsconfig.prod.esm2015.json", 26 | "build:esm5": "tsc --project tsconfig.prod.esm5.json", 27 | "build:cjs": "tsc --project tsconfig.prod.cjs.json", 28 | "build:umd": "rollup --config rollup.config.js", 29 | "build:types": "tsc --project tsconfig.prod.types.json", 30 | "prettier:fix": "prettier --write \"**/*.{ts,md}\"", 31 | "prettier:check": "prettier --check \"**/*.{ts,md}\"", 32 | "lint:fix": "eslint --max-warnings 0 --fix --ext .ts src/", 33 | "lint:check": "eslint --max-warnings 0 --ext .ts src/", 34 | "test": "jest --coverage --verbose", 35 | "test:watch": "jest --watch", 36 | "test:ci": "jest --runInBand --no-cache --coverage --verbose" 37 | }, 38 | "dependencies": { 39 | "@types/validator": "^13.11.8", 40 | "libphonenumber-js": "^1.11.1", 41 | "validator": "^13.9.0" 42 | }, 43 | "devDependencies": { 44 | "@rollup/plugin-commonjs": "^25.0.7", 45 | "@rollup/plugin-node-resolve": "^15.2.3", 46 | "@types/jest": "^29.5.12", 47 | "@types/node": "^20.12.10", 48 | "@typescript-eslint/eslint-plugin": "^5.62.0", 49 | "@typescript-eslint/parser": "^5.62.0", 50 | "eslint": "^8.57.0", 51 | "eslint-config-prettier": "^9.1.0", 52 | "eslint-plugin-jest": "^27.9.0", 53 | "husky": "^4.3.8", 54 | "jest": "^29.7.0", 55 | "lint-staged": "^15.2.2", 56 | "prettier": "^2.8.8", 57 | "reflect-metadata": "0.2.2", 58 | "rimraf": "5.0.5", 59 | "rollup": "^2.79.1", 60 | "rollup-plugin-terser": "^7.0.2", 61 | "ts-jest": "^29.1.2", 62 | "ts-node": "^10.9.2", 63 | "typescript": "^5.4.5" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import { terser } from 'rollup-plugin-terser'; 4 | 5 | export default { 6 | input: 'build/esm5/index.js', 7 | output: [ 8 | { 9 | name: 'ClassValidator', 10 | format: 'umd', 11 | file: 'build/bundles/class-validator.umd.js', 12 | sourcemap: true, 13 | }, 14 | { 15 | name: 'ClassValidator', 16 | format: 'umd', 17 | file: 'build/bundles/class-validator.umd.min.js', 18 | sourcemap: true, 19 | plugins: [terser()], 20 | }, 21 | ], 22 | plugins: [commonjs(), nodeResolve()], 23 | }; 24 | -------------------------------------------------------------------------------- /sample/sample1-simple-validation/Post.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contains, 3 | IsInt, 4 | MinLength, 5 | MaxLength, 6 | IsEmail, 7 | IsFQDN, 8 | IsDate, 9 | ArrayNotEmpty, 10 | ArrayMinSize, 11 | ArrayMaxSize, 12 | IsEnum, 13 | } from '../../src/decorator/decorators'; 14 | 15 | export enum PostType { 16 | Public, 17 | Private, 18 | } 19 | 20 | export class Post { 21 | @MinLength(10) 22 | @MaxLength(20) 23 | title: string; 24 | 25 | @Contains('hello') 26 | text: string; 27 | 28 | @IsInt() 29 | rating: number; 30 | 31 | @IsEmail() 32 | email: string; 33 | 34 | @IsFQDN() 35 | site: string; 36 | 37 | @IsDate() 38 | createDate: Date; 39 | 40 | @ArrayNotEmpty() 41 | @ArrayMinSize(2) 42 | @ArrayMaxSize(5) 43 | @MinLength(3, { each: true, message: 'Tag is too short. Minimal length is $value characters' }) 44 | @MaxLength(50, { each: true, message: 'Tag is too long. Maximal length is $value characters' }) 45 | tags: string[]; 46 | 47 | @IsEnum(PostType) 48 | type: PostType; 49 | } 50 | -------------------------------------------------------------------------------- /sample/sample2-using-groups/Post.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; 2 | 3 | export class Post { 4 | @Length(10, 20, { 5 | message: 'Incorrect length!', 6 | groups: ['users', 'moderators'], 7 | }) 8 | @Length(0, 20, { 9 | message: 'Incorrect length!', 10 | groups: ['admins'], 11 | }) 12 | title: string; 13 | 14 | @Contains('hello', { 15 | message: 'It should contain word "hello!"', 16 | groups: ['users', 'moderators'], 17 | }) 18 | text: string; 19 | 20 | @IsInt() 21 | rating: number; 22 | 23 | @IsEmail(undefined, { 24 | always: true, 25 | }) 26 | email: string; 27 | 28 | @IsFQDN(undefined, { 29 | message: 'Site address should be correct', 30 | groups: ['users'], 31 | }) 32 | site: string; 33 | 34 | @IsDate() 35 | createDate: Date; 36 | } 37 | -------------------------------------------------------------------------------- /sample/sample2-using-groups/app.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from '../../src/validation/Validator'; 2 | import { Post } from './Post'; 3 | 4 | let validator = new Validator(); 5 | 6 | let post1 = new Post(); 7 | post1.title = 'Hello world'; // should pass 8 | post1.text = 'this is a great post about hello world'; // should pass 9 | post1.rating = 10; // should pass 10 | post1.email = 'info@google.com'; // should pass 11 | post1.site = 'google.com'; // should pass 12 | post1.createDate = new Date(); // should pass 13 | 14 | validator.validate(post1, { groups: ['users'] }).then(result => { 15 | console.log('1.1. should pass: ', result); 16 | }); 17 | 18 | validator.validate(post1, { groups: ['admins'] }).then(result => { 19 | console.log('1.2. should pass: ', result); 20 | }); 21 | 22 | let post2 = new Post(); 23 | post2.title = 'Hi!'; // should not pass for user or moderator, but should pass for admin 24 | post2.text = 'this is a great post about hello world'; // should pass 25 | post2.rating = 10; // should pass 26 | post2.email = 'info@google.com'; // should pass 27 | post2.site = 'google.com'; // should pass 28 | post2.createDate = new Date(); // should pass 29 | 30 | validator.validate(post2, { groups: ['users'] }).then(result => { 31 | console.log('2.1. should not pass: ', result); 32 | }); 33 | 34 | validator.validate(post2, { groups: ['moderators'] }).then(result => { 35 | console.log('2.2. should not pass: ', result); 36 | }); 37 | 38 | validator.validate(post2, { groups: ['admins'] }).then(result => { 39 | console.log('2.3. should pass: ', result); 40 | }); 41 | 42 | validator.validate(post2, { groups: ['users', 'admins'] }).then(result => { 43 | console.log('2.4. should not pass: ', result); 44 | }); 45 | 46 | let post3 = new Post(); 47 | post3.title = 'Hello world'; // should not pass for user or moderator, but should pass for admin 48 | post3.text = 'this is a great post about hello world'; // should pass 49 | post3.rating = 10; // should pass 50 | post3.email = 'info@google.com'; // should pass 51 | post3.site = 'google.com'; // should pass 52 | // note that we dont set date 53 | 54 | validator.validate(post3, { groups: ['users'] }).then(result => { 55 | console.log('3.1. should pass: ', result); 56 | }); 57 | 58 | validator.validate(post3).then(result => { 59 | console.log('3.2. should not pass: ', result); 60 | }); 61 | 62 | let post4 = new Post(); 63 | post4.title = 'Hello world'; // should not pass for user or moderator, but should pass for admin 64 | post4.text = 'this is a great post about hello world'; // should pass 65 | post4.rating = 10; // should pass 66 | post4.email = ''; // should not pass 67 | post4.site = 'google.com'; // should pass 68 | // note that we dont set date 69 | 70 | validator.validate(post4, { groups: ['users'] }).then(result => { 71 | console.log('4.1. should not pass: ', result); 72 | }); 73 | 74 | validator.validate(post4).then(result => { 75 | console.log('4.2. should not pass: ', result); 76 | }); 77 | -------------------------------------------------------------------------------- /sample/sample3-nested-objects/Post.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, ValidateNested } from '../../src/decorator/decorators'; 2 | import { Tag } from './Tag'; 3 | 4 | export class Post { 5 | @Length(10, 20, { 6 | message: 'Incorrect length!', 7 | }) 8 | title: string; 9 | 10 | @ValidateNested() 11 | tags: Tag[]; 12 | } 13 | -------------------------------------------------------------------------------- /sample/sample3-nested-objects/Tag.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; 2 | 3 | export class Tag { 4 | @Length(10, 20, { 5 | message: 'Tag is too short or long', 6 | }) 7 | name: string; 8 | } 9 | -------------------------------------------------------------------------------- /sample/sample3-nested-objects/app.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from '../../src/validation/Validator'; 2 | import { Post } from './Post'; 3 | import { Tag } from './Tag'; 4 | 5 | let validator = new Validator(); 6 | 7 | let tag1 = new Tag(); 8 | tag1.name = 'ja'; 9 | 10 | let tag2 = new Tag(); 11 | tag2.name = 'node.js'; 12 | 13 | let post1 = new Post(); 14 | post1.title = 'Hello world'; 15 | post1.tags = [tag1, tag2]; 16 | 17 | validator.validate(post1).then(result => { 18 | console.log('1. should not pass: ', result); 19 | }); 20 | -------------------------------------------------------------------------------- /sample/sample4-custom-validator/CustomTextLength.ts: -------------------------------------------------------------------------------- 1 | import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface'; 2 | import { ValidatorConstraint } from '../../src/decorator/decorators'; 3 | 4 | @ValidatorConstraint() 5 | export class CustomTextLength implements ValidatorConstraintInterface { 6 | validate(text: string) { 7 | return text.length > 1 && text.length < 10; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/sample4-custom-validator/Post.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contains, 3 | IsInt, 4 | MinLength, 5 | MaxLength, 6 | IsEmail, 7 | IsFQDN, 8 | IsDate, 9 | IsNotEmpty, 10 | ArrayNotEmpty, 11 | ArrayMinSize, 12 | ArrayMaxSize, 13 | } from '../../src/decorator/decorators'; 14 | import { Validate } from '../../src/decorator/decorators'; 15 | import { CustomTextLength } from './CustomTextLength'; 16 | 17 | export class Post { 18 | @Validate(CustomTextLength, { 19 | message: 'Wrong post title', 20 | }) 21 | title: string; 22 | } 23 | -------------------------------------------------------------------------------- /sample/sample4-custom-validator/app.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from '../../src/validation/Validator'; 2 | import { Post } from './Post'; 3 | 4 | let validator = new Validator(); 5 | 6 | let post1 = new Post(); 7 | post1.title = 'Hello world'; 8 | 9 | validator.validate(post1).then(result => { 10 | console.log('1. should not pass: ', result); 11 | }); 12 | 13 | let post2 = new Post(); 14 | post2.title = 'Hello !!!'; 15 | 16 | validator.validate(post2).then(result => { 17 | console.log('2. should pass: ', result); 18 | }); 19 | -------------------------------------------------------------------------------- /sample/sample5-schemas/Post.ts: -------------------------------------------------------------------------------- 1 | export class Post { 2 | title: string; 3 | text: string; 4 | rating: number; 5 | email: string; 6 | site: string; 7 | createDate: Date; 8 | tags: string[]; 9 | } 10 | -------------------------------------------------------------------------------- /sample/sample5-schemas/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "post", 3 | "properties": { 4 | "title": [{ 5 | "type": "minLength", 6 | "constraints": [10] 7 | }, 8 | { 9 | "type": "maxLength", 10 | "constraints": [20] 11 | }], 12 | "text": [{ 13 | "type": "contains", 14 | "constraints": ["hello"] 15 | }], 16 | "rating": [{ 17 | "type": "isInt" 18 | }], 19 | "email": [{ 20 | "type": "isEmail" 21 | }], 22 | "site": [{ 23 | "type": "isFqdn" 24 | }], 25 | "createDate": [{ 26 | "type": "isDate" 27 | }], 28 | "tags": [{ 29 | "type": "arrayNotEmpty" 30 | }, 31 | { 32 | "type": "arrayMinSize", 33 | "constraints": [2] 34 | }, 35 | { 36 | "type": "arrayMaxSize", 37 | "constraints": [5] 38 | }, 39 | { 40 | "type": "minLength", 41 | "each": true, 42 | "constraints": [3], 43 | "message": "Tag is too short. Minimal length is $constraint1 characters" 44 | }, 45 | { 46 | "type": "maxLength", 47 | "each": true, 48 | "constraints": [50], 49 | "message": "Tag is too long. Maximal length is $constraint1 characters" 50 | }] 51 | } 52 | } -------------------------------------------------------------------------------- /sample/sample6-custom-decorator/IsLongerThan.ts: -------------------------------------------------------------------------------- 1 | import { registerDecorator } from '../../src/index'; 2 | import { ValidationOptions } from '../../src/decorator/ValidationOptions'; 3 | import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface'; 4 | import { ValidatorConstraint } from '../../src/decorator/decorators'; 5 | import { ValidationArguments } from '../../src/validation/ValidationArguments'; 6 | 7 | export function IsLongerThan(property: string, validationOptions?: ValidationOptions) { 8 | return function (object: Object, propertyName: string) { 9 | registerDecorator({ 10 | target: object.constructor, 11 | propertyName: propertyName, 12 | options: validationOptions, 13 | constraints: [property], 14 | validator: IsLongerThanConstraint, 15 | }); 16 | }; 17 | } 18 | 19 | @ValidatorConstraint({ name: 'isLongerThan' }) 20 | export class IsLongerThanConstraint implements ValidatorConstraintInterface { 21 | validate(value: any, args: ValidationArguments) { 22 | const [relatedPropertyName] = args.constraints; 23 | const relatedValue = (args.object as any)[relatedPropertyName]; 24 | return typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/sample6-custom-decorator/IsUserAlreadyExist.ts: -------------------------------------------------------------------------------- 1 | import { registerDecorator } from '../../src/index'; 2 | import { ValidationOptions } from '../../src/decorator/ValidationOptions'; 3 | import { ValidationArguments } from '../../src/validation/ValidationArguments'; 4 | 5 | export function IsUserAlreadyExist(validationOptions?: ValidationOptions) { 6 | return function (object: Object, propertyName: string) { 7 | registerDecorator({ 8 | name: 'isUserAlreadyExist', 9 | async: true, 10 | target: object.constructor, 11 | propertyName: propertyName, 12 | options: validationOptions, 13 | validator: { 14 | validate(value: any, args: ValidationArguments) { 15 | return new Promise(ok => { 16 | if (value !== 'admin' && value !== 'user') { 17 | ok(true); 18 | } else { 19 | ok(false); 20 | } 21 | }); 22 | }, 23 | }, 24 | }); 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /sample/sample6-custom-decorator/User.ts: -------------------------------------------------------------------------------- 1 | import { IsUserAlreadyExist } from './IsUserAlreadyExist'; 2 | import { IsLongerThan } from './IsLongerThan'; 3 | 4 | export class User { 5 | @IsUserAlreadyExist({ 6 | message: 'User with name $value already exists', 7 | }) 8 | firstName: string; 9 | 10 | @IsLongerThan('firstName', { 11 | message: "User's last name must be longer than firstName", 12 | }) 13 | lastName: string; 14 | } 15 | -------------------------------------------------------------------------------- /sample/sample6-custom-decorator/app.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from '../../src/validation/Validator'; 2 | import { User } from './User'; 3 | 4 | let validator = new Validator(); 5 | 6 | let user1 = new User(); 7 | user1.firstName = 'Umed'; 8 | 9 | validator.validate(user1, { skipMissingProperties: true }).then(result => { 10 | console.log('1. should pass: ', result); 11 | }); 12 | 13 | let user2 = new User(); 14 | user2.firstName = 'admin'; 15 | 16 | validator.validate(user2, { skipMissingProperties: true }).then(result => { 17 | console.log('2. should not pass: ', result); 18 | }); 19 | 20 | let user3 = new User(); 21 | user3.firstName = 'user'; 22 | 23 | validator.validate(user3, { skipMissingProperties: true }).then(result => { 24 | console.log('3. should not pass: ', result); 25 | }); 26 | 27 | let user4 = new User(); 28 | user4.firstName = 'Zak'; 29 | user4.lastName = 'Henry'; 30 | 31 | validator.validate(user4).then(result => { 32 | console.log('4. should pass: ', result); 33 | }); 34 | 35 | let user5 = new User(); 36 | user5.firstName = 'Henry'; 37 | user5.lastName = 'Zak'; 38 | 39 | validator.validate(user5).then(result => { 40 | console.log('5. should not pass: ', result); 41 | }); 42 | -------------------------------------------------------------------------------- /sample/sample7-inheritance-support/BaseContent.ts: -------------------------------------------------------------------------------- 1 | import { IsEmail } from '../../src/decorator/decorators'; 2 | 3 | export class BaseContent { 4 | @IsEmail() 5 | email: string; 6 | } 7 | -------------------------------------------------------------------------------- /sample/sample7-inheritance-support/Post.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, MinLength, MaxLength } from '../../src/decorator/decorators'; 2 | import { BaseContent } from './BaseContent'; 3 | 4 | export class Post extends BaseContent { 5 | @MinLength(10) 6 | @MaxLength(20) 7 | title: string; 8 | 9 | @Contains('hello') 10 | text: string; 11 | 12 | @IsInt() 13 | rating: number; 14 | } 15 | -------------------------------------------------------------------------------- /sample/sample7-inheritance-support/app.ts: -------------------------------------------------------------------------------- 1 | import { validate } from '../../src/index'; 2 | import { Post } from './Post'; 3 | 4 | // Sample1. simple validation 5 | 6 | let post1 = new Post(); 7 | post1.title = 'Hello world'; // should pass 8 | post1.text = 'this is a great post about hello world'; // should pass 9 | post1.rating = 10; // should pass 10 | post1.email = '@google.com'; // should not pass 11 | 12 | validate(post1).then(result => { 13 | console.log('1. should not pass: ', result); // should not pass completely 14 | }); 15 | -------------------------------------------------------------------------------- /sample/sample8-es6-maps/Post.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, ValidateNested } from '../../src/decorator/decorators'; 2 | import { Tag } from './Tag'; 3 | 4 | export class Post { 5 | @Length(10, 20, { 6 | message: 'Incorrect length!', 7 | }) 8 | title: string; 9 | 10 | @ValidateNested() 11 | tags: Map; 12 | } 13 | -------------------------------------------------------------------------------- /sample/sample8-es6-maps/Tag.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; 2 | 3 | export class Tag { 4 | @Length(10, 20, { 5 | message: 'Tag value is too short or long', 6 | }) 7 | value: string; 8 | } 9 | -------------------------------------------------------------------------------- /sample/sample8-es6-maps/app.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from '../../src/validation/Validator'; 2 | import { Post } from './Post'; 3 | import { Tag } from './Tag'; 4 | 5 | let validator = new Validator(); 6 | 7 | let tag1 = new Tag(); 8 | tag1.value = 'ja'; 9 | 10 | let tag2 = new Tag(); 11 | tag2.value = 'node.js'; 12 | 13 | let post1 = new Post(); 14 | post1.title = 'Hello world'; 15 | post1.tags = new Map(); 16 | post1.tags.set('tag1', tag1); 17 | post1.tags.set('tag2', tag2); 18 | 19 | validator.validate(post1).then(result => { 20 | console.log('1. should not pass: ', result); 21 | }); 22 | -------------------------------------------------------------------------------- /sample/sample9-es6-sets/Post.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, ValidateNested } from '../../src/decorator/decorators'; 2 | import { Tag } from './Tag'; 3 | 4 | export class Post { 5 | @Length(10, 20, { 6 | message: 'Incorrect length!', 7 | }) 8 | title: string; 9 | 10 | @ValidateNested() 11 | tags: Set; 12 | } 13 | -------------------------------------------------------------------------------- /sample/sample9-es6-sets/Tag.ts: -------------------------------------------------------------------------------- 1 | import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; 2 | 3 | export class Tag { 4 | @Length(10, 20, { 5 | message: 'Tag value is too short or long', 6 | }) 7 | value: string; 8 | } 9 | -------------------------------------------------------------------------------- /sample/sample9-es6-sets/app.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from '../../src/validation/Validator'; 2 | import { Post } from './Post'; 3 | import { Tag } from './Tag'; 4 | 5 | let validator = new Validator(); 6 | 7 | let tag1 = new Tag(); 8 | tag1.value = 'ja'; 9 | 10 | let tag2 = new Tag(); 11 | tag2.value = 'node.js'; 12 | 13 | let post1 = new Post(); 14 | post1.title = 'Hello world'; 15 | post1.tags = new Set(); 16 | post1.tags.add(tag1); 17 | post1.tags.add(tag2); 18 | 19 | validator.validate(post1).then(result => { 20 | console.log('1. should not pass: ', result); 21 | }); 22 | -------------------------------------------------------------------------------- /src/container.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Container options. 3 | */ 4 | export interface UseContainerOptions { 5 | /** 6 | * If set to true, then default container will be used in the case if given container haven't returned anything. 7 | */ 8 | fallback?: boolean; 9 | 10 | /** 11 | * If set to true, then default container will be used in the case if given container thrown an exception. 12 | */ 13 | fallbackOnErrors?: boolean; 14 | } 15 | 16 | /** 17 | * Container to be used by this library for inversion control. If container was not implicitly set then by default 18 | * container simply creates a new instance of the given class. 19 | */ 20 | const defaultContainer: { get(someClass: { new (...args: any[]): T } | Function): T } = new (class { 21 | private instances: { type: Function; object: any }[] = []; 22 | get(someClass: { new (...args: any[]): T }): T { 23 | let instance = this.instances.find(instance => instance.type === someClass); 24 | if (!instance) { 25 | instance = { type: someClass, object: new someClass() }; 26 | this.instances.push(instance); 27 | } 28 | 29 | return instance.object; 30 | } 31 | })(); 32 | 33 | let userContainer: { get(someClass: { new (...args: any[]): T } | Function): T }; 34 | let userContainerOptions: UseContainerOptions; 35 | 36 | /** 37 | * Sets container to be used by this library. 38 | */ 39 | export function useContainer(iocContainer: { get(someClass: any): any }, options?: UseContainerOptions): void { 40 | userContainer = iocContainer; 41 | userContainerOptions = options; 42 | } 43 | 44 | /** 45 | * Gets the IOC container used by this library. 46 | */ 47 | export function getFromContainer(someClass: { new (...args: any[]): T } | Function): T { 48 | if (userContainer) { 49 | try { 50 | const instance = userContainer.get(someClass); 51 | if (instance) return instance; 52 | 53 | if (!userContainerOptions || !userContainerOptions.fallback) return instance; 54 | } catch (error) { 55 | if (!userContainerOptions || !userContainerOptions.fallbackOnErrors) throw error; 56 | } 57 | } 58 | return defaultContainer.get(someClass); 59 | } 60 | -------------------------------------------------------------------------------- /src/decorator/ValidationOptions.ts: -------------------------------------------------------------------------------- 1 | import { ValidationArguments } from '../validation/ValidationArguments'; 2 | 3 | /** 4 | * Options used to pass to validation decorators. 5 | */ 6 | export interface ValidationOptions { 7 | /** 8 | * Specifies if validated value is an array and each of its items must be validated. 9 | */ 10 | each?: boolean; 11 | 12 | /** 13 | * Error message to be used on validation fail. 14 | * Message can be either string or a function that returns a string. 15 | */ 16 | message?: string | ((validationArguments: ValidationArguments) => string); 17 | 18 | /** 19 | * Validation groups used for this validation. 20 | */ 21 | groups?: string[]; 22 | 23 | /** 24 | * Indicates if validation must be performed always, no matter of validation groups used. 25 | */ 26 | always?: boolean; 27 | 28 | /* 29 | * A transient set of data passed through to the validation result for response mapping 30 | */ 31 | context?: any; 32 | } 33 | 34 | export function isValidationOptions(val: any): val is ValidationOptions { 35 | if (!val) { 36 | return false; 37 | } 38 | return 'each' in val || 'message' in val || 'groups' in val || 'always' in val || 'context' in val; 39 | } 40 | -------------------------------------------------------------------------------- /src/decorator/array/ArrayContains.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const ARRAY_CONTAINS = 'arrayContains'; 5 | 6 | /** 7 | * Checks if array contains all values from the given array of values. 8 | * If null or undefined is given then this function returns false. 9 | */ 10 | export function arrayContains(array: unknown, values: any[]): boolean { 11 | if (!Array.isArray(array)) return false; 12 | 13 | return values.every(value => array.indexOf(value) !== -1); 14 | } 15 | 16 | /** 17 | * Checks if array contains all values from the given array of values. 18 | * If null or undefined is given then this function returns false. 19 | */ 20 | export function ArrayContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: ARRAY_CONTAINS, 24 | constraints: [values], 25 | validator: { 26 | validate: (value, args): boolean => arrayContains(value, args?.constraints[0]), 27 | defaultMessage: buildMessage( 28 | eachPrefix => eachPrefix + '$property must contain $constraint1 values', 29 | validationOptions 30 | ), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/array/ArrayMaxSize.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const ARRAY_MAX_SIZE = 'arrayMaxSize'; 5 | 6 | /** 7 | * Checks if the array's length is less or equal to the specified number. 8 | * If null or undefined is given then this function returns false. 9 | */ 10 | export function arrayMaxSize(array: unknown, max: number): boolean { 11 | return Array.isArray(array) && array.length <= max; 12 | } 13 | 14 | /** 15 | * Checks if the array's length is less or equal to the specified number. 16 | * If null or undefined is given then this function returns false. 17 | */ 18 | export function ArrayMaxSize(max: number, validationOptions?: ValidationOptions): PropertyDecorator { 19 | return ValidateBy( 20 | { 21 | name: ARRAY_MAX_SIZE, 22 | constraints: [max], 23 | validator: { 24 | validate: (value, args): boolean => arrayMaxSize(value, args?.constraints[0]), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain no more than $constraint1 elements', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/array/ArrayMinSize.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const ARRAY_MIN_SIZE = 'arrayMinSize'; 5 | 6 | /** 7 | * Checks if the array's length is greater than or equal to the specified number. 8 | * If null or undefined is given then this function returns false. 9 | */ 10 | export function arrayMinSize(array: unknown, min: number): boolean { 11 | return Array.isArray(array) && array.length >= min; 12 | } 13 | 14 | /** 15 | * Checks if the array's length is greater than or equal to the specified number. 16 | * If null or undefined is given then this function returns false. 17 | */ 18 | export function ArrayMinSize(min: number, validationOptions?: ValidationOptions): PropertyDecorator { 19 | return ValidateBy( 20 | { 21 | name: ARRAY_MIN_SIZE, 22 | constraints: [min], 23 | validator: { 24 | validate: (value, args): boolean => arrayMinSize(value, args?.constraints[0]), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain at least $constraint1 elements', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/array/ArrayNotContains.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const ARRAY_NOT_CONTAINS = 'arrayNotContains'; 5 | 6 | /** 7 | * Checks if array does not contain any of the given values. 8 | * If null or undefined is given then this function returns false. 9 | */ 10 | export function arrayNotContains(array: unknown, values: any[]): boolean { 11 | if (!Array.isArray(array)) return false; 12 | 13 | return values.every(value => array.indexOf(value) === -1); 14 | } 15 | 16 | /** 17 | * Checks if array does not contain any of the given values. 18 | * If null or undefined is given then this function returns false. 19 | */ 20 | export function ArrayNotContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: ARRAY_NOT_CONTAINS, 24 | constraints: [values], 25 | validator: { 26 | validate: (value, args): boolean => arrayNotContains(value, args?.constraints[0]), 27 | defaultMessage: buildMessage( 28 | eachPrefix => eachPrefix + '$property should not contain $constraint1 values', 29 | validationOptions 30 | ), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/array/ArrayNotEmpty.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const ARRAY_NOT_EMPTY = 'arrayNotEmpty'; 5 | 6 | /** 7 | * Checks if given array is not empty. 8 | * If null or undefined is given then this function returns false. 9 | */ 10 | export function arrayNotEmpty(array: unknown): boolean { 11 | return Array.isArray(array) && array.length > 0; 12 | } 13 | 14 | /** 15 | * Checks if given array is not empty. 16 | * If null or undefined is given then this function returns false. 17 | */ 18 | export function ArrayNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { 19 | return ValidateBy( 20 | { 21 | name: ARRAY_NOT_EMPTY, 22 | validator: { 23 | validate: (value, args): boolean => arrayNotEmpty(value), 24 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property should not be empty', validationOptions), 25 | }, 26 | }, 27 | validationOptions 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/decorator/array/ArrayUnique.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const ARRAY_UNIQUE = 'arrayUnique'; 5 | export type ArrayUniqueIdentifier = (o: T) => any; 6 | 7 | /** 8 | * Checks if all array's values are unique. Comparison for objects is reference-based. 9 | * If null or undefined is given then this function returns false. 10 | */ 11 | export function arrayUnique(array: unknown[], identifier?: ArrayUniqueIdentifier): boolean { 12 | if (!Array.isArray(array)) return false; 13 | 14 | if (identifier) { 15 | array = array.map(o => (o != null ? identifier(o) : o)); 16 | } 17 | 18 | const uniqueItems = array.filter((a, b, c) => c.indexOf(a) === b); 19 | return array.length === uniqueItems.length; 20 | } 21 | 22 | /** 23 | * Checks if all array's values are unique. Comparison for objects is reference-based. 24 | * If null or undefined is given then this function returns false. 25 | */ 26 | export function ArrayUnique( 27 | identifierOrOptions?: ArrayUniqueIdentifier | ValidationOptions, 28 | validationOptions?: ValidationOptions 29 | ): PropertyDecorator { 30 | const identifier = typeof identifierOrOptions === 'function' ? identifierOrOptions : undefined; 31 | const options = typeof identifierOrOptions !== 'function' ? identifierOrOptions : validationOptions; 32 | 33 | return ValidateBy( 34 | { 35 | name: ARRAY_UNIQUE, 36 | validator: { 37 | validate: (value, args): boolean => arrayUnique(value, identifier), 38 | defaultMessage: buildMessage(eachPrefix => eachPrefix + "All $property's elements must be unique", options), 39 | }, 40 | }, 41 | options 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/decorator/common/Allow.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; 3 | import { ValidationTypes } from '../../validation/ValidationTypes'; 4 | import { ValidationMetadata } from '../../metadata/ValidationMetadata'; 5 | import { getMetadataStorage } from '../../metadata/MetadataStorage'; 6 | 7 | /** 8 | * If object has both allowed and not allowed properties a validation error will be thrown. 9 | */ 10 | export function Allow(validationOptions?: ValidationOptions): PropertyDecorator { 11 | return function (object: object, propertyName: string): void { 12 | const args: ValidationMetadataArgs = { 13 | type: ValidationTypes.WHITELIST, 14 | target: object.constructor, 15 | propertyName: propertyName, 16 | validationOptions: validationOptions, 17 | }; 18 | getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/decorator/common/Equals.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const EQUALS = 'equals'; 5 | 6 | /** 7 | * Checks if value matches ("===") the comparison. 8 | */ 9 | export function equals(value: unknown, comparison: unknown): boolean { 10 | return value === comparison; 11 | } 12 | 13 | /** 14 | * Checks if value matches ("===") the comparison. 15 | */ 16 | export function Equals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: EQUALS, 20 | constraints: [comparison], 21 | validator: { 22 | validate: (value, args): boolean => equals(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be equal to $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/common/IsDefined.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from './ValidateBy'; 3 | import { ValidationTypes } from '../../validation/ValidationTypes'; 4 | 5 | // isDefined is (yet) a special case 6 | export const IS_DEFINED = ValidationTypes.IS_DEFINED; 7 | 8 | /** 9 | * Checks if value is defined (!== undefined, !== null). 10 | */ 11 | export function isDefined(value: T | undefined | null): value is T { 12 | return value !== undefined && value !== null; 13 | } 14 | 15 | /** 16 | * Checks if value is defined (!== undefined, !== null). 17 | */ 18 | export function IsDefined(validationOptions?: ValidationOptions): PropertyDecorator { 19 | return ValidateBy( 20 | { 21 | name: IS_DEFINED, 22 | validator: { 23 | validate: (value): boolean => isDefined(value), 24 | defaultMessage: buildMessage( 25 | eachPrefix => eachPrefix + '$property should not be null or undefined', 26 | validationOptions 27 | ), 28 | }, 29 | }, 30 | validationOptions 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/decorator/common/IsEmpty.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_EMPTY = 'isEmpty'; 5 | 6 | /** 7 | * Checks if given value is empty (=== '', === null, === undefined). 8 | */ 9 | export function isEmpty(value: unknown): boolean { 10 | return value === '' || value === null || value === undefined; 11 | } 12 | 13 | /** 14 | * Checks if given value is empty (=== '', === null, === undefined). 15 | */ 16 | export function IsEmpty(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_EMPTY, 20 | validator: { 21 | validate: (value, args): boolean => isEmpty(value), 22 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be empty', validationOptions), 23 | }, 24 | }, 25 | validationOptions 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/decorator/common/IsIn.spec.ts: -------------------------------------------------------------------------------- 1 | import { isIn } from './IsIn'; 2 | 3 | describe('@IsIn decorator implementation', () => { 4 | describe('isIn validator', () => { 5 | it('should accept valid values', () => { 6 | expect(isIn('A', ['A', 'B'])).toBe(true); 7 | expect(isIn('A', ['B', 'C'])).toBe(false); 8 | expect(isIn('A', [1, 2])).toBe(false); 9 | }); 10 | 11 | it('should not accept invalid values', () => { 12 | expect(isIn('A', 5 as any)).toBe(false); 13 | expect(isIn('A', 'ABC' as any)).toBe(false); 14 | expect(isIn('A', false as any)).toBe(false); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/decorator/common/IsIn.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_IN = 'isIn'; 5 | 6 | /** 7 | * Checks if given value is in a array of allowed values. 8 | */ 9 | export function isIn(value: unknown, possibleValues: readonly unknown[]): boolean { 10 | return Array.isArray(possibleValues) && possibleValues.some(possibleValue => possibleValue === value); 11 | } 12 | 13 | /** 14 | * Checks if given value is in a array of allowed values. 15 | */ 16 | export function IsIn(values: readonly any[], validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_IN, 20 | constraints: [values], 21 | validator: { 22 | validate: (value, args): boolean => isIn(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be one of the following values: $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/common/IsLatLong.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from './ValidateBy'; 3 | import isLatLongValidator from 'validator/lib/isLatLong'; 4 | 5 | export const IS_LATLONG = 'isLatLong'; 6 | 7 | /** 8 | * Checks if a value is string in format a "latitude,longitude". 9 | */ 10 | export function isLatLong(value: string): boolean { 11 | return typeof value === 'string' && isLatLongValidator(value); 12 | } 13 | 14 | /** 15 | * Checks if a value is string in format a "latitude,longitude". 16 | */ 17 | export function IsLatLong(validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_LATLONG, 21 | validator: { 22 | validate: (value, args): boolean => isLatLong(value), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be a latitude,longitude string', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/common/IsLatitude.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from './ValidateBy'; 3 | import { isLatLong } from './IsLatLong'; 4 | 5 | export const IS_LATITUDE = 'isLatitude'; 6 | 7 | /** 8 | * Checks if a given value is a latitude. 9 | */ 10 | export function isLatitude(value: string): boolean { 11 | return (typeof value === 'number' || typeof value === 'string') && isLatLong(`${value},0`); 12 | } 13 | 14 | /** 15 | * Checks if a given value is a latitude. 16 | */ 17 | export function IsLatitude(validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_LATITUDE, 21 | validator: { 22 | validate: (value, args): boolean => isLatitude(value), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be a latitude string or number', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/common/IsLongitude.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from './ValidateBy'; 3 | import { isLatLong } from './IsLatLong'; 4 | 5 | export const IS_LONGITUDE = 'isLongitude'; 6 | 7 | /** 8 | * Checks if a given value is a longitude. 9 | */ 10 | export function isLongitude(value: string): boolean { 11 | return (typeof value === 'number' || typeof value === 'string') && isLatLong(`0,${value}`); 12 | } 13 | 14 | /** 15 | * Checks if a given value is a longitude. 16 | */ 17 | export function IsLongitude(validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_LONGITUDE, 21 | validator: { 22 | validate: (value, args): boolean => isLongitude(value), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be a longitude string or number', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/common/IsNotEmpty.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_NOT_EMPTY = 'isNotEmpty'; 5 | 6 | /** 7 | * Checks if given value is not empty (!== '', !== null, !== undefined). 8 | */ 9 | export function isNotEmpty(value: unknown): boolean { 10 | return value !== '' && value !== null && value !== undefined; 11 | } 12 | 13 | /** 14 | * Checks if given value is not empty (!== '', !== null, !== undefined). 15 | */ 16 | export function IsNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_NOT_EMPTY, 20 | validator: { 21 | validate: (value, args): boolean => isNotEmpty(value), 22 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property should not be empty', validationOptions), 23 | }, 24 | }, 25 | validationOptions 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/decorator/common/IsNotIn.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_NOT_IN = 'isNotIn'; 5 | 6 | /** 7 | * Checks if given value not in a array of allowed values. 8 | */ 9 | export function isNotIn(value: unknown, possibleValues: readonly unknown[]): boolean { 10 | return !Array.isArray(possibleValues) || !possibleValues.some(possibleValue => possibleValue === value); 11 | } 12 | 13 | /** 14 | * Checks if given value not in a array of allowed values. 15 | */ 16 | export function IsNotIn(values: readonly any[], validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_NOT_IN, 20 | constraints: [values], 21 | validator: { 22 | validate: (value, args): boolean => isNotIn(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property should not be one of the following values: $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/common/IsOptional.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; 3 | import { ValidationTypes } from '../../validation/ValidationTypes'; 4 | import { ValidationMetadata } from '../../metadata/ValidationMetadata'; 5 | import { getMetadataStorage } from '../../metadata/MetadataStorage'; 6 | 7 | export const IS_OPTIONAL = 'isOptional'; 8 | 9 | /** 10 | * Checks if value is missing and if so, ignores all validators. 11 | */ 12 | export function IsOptional(validationOptions?: ValidationOptions): PropertyDecorator { 13 | return function (object: object, propertyName: string): void { 14 | const args: ValidationMetadataArgs = { 15 | type: ValidationTypes.CONDITIONAL_VALIDATION, 16 | name: IS_OPTIONAL, 17 | target: object.constructor, 18 | propertyName: propertyName, 19 | constraints: [ 20 | (object: any, value: any): boolean => { 21 | return object[propertyName] !== null && object[propertyName] !== undefined; 22 | }, 23 | ], 24 | validationOptions: validationOptions, 25 | }; 26 | getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/decorator/common/NotEquals.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const NOT_EQUALS = 'notEquals'; 5 | 6 | /** 7 | * Checks if value does not match ("!==") the comparison. 8 | */ 9 | export function notEquals(value: unknown, comparison: unknown): boolean { 10 | return value !== comparison; 11 | } 12 | 13 | /** 14 | * Checks if value does not match ("!==") the comparison. 15 | */ 16 | export function NotEquals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: NOT_EQUALS, 20 | constraints: [comparison], 21 | validator: { 22 | validate: (value, args): boolean => notEquals(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property should not be equal to $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/common/Validate.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; 3 | import { ValidationMetadata } from '../../metadata/ValidationMetadata'; 4 | import { getMetadataStorage } from '../../metadata/MetadataStorage'; 5 | import { ValidationTypes } from '../../validation/ValidationTypes'; 6 | import { ConstraintMetadata } from '../../metadata/ConstraintMetadata'; 7 | 8 | /** 9 | * Registers custom validator class. 10 | */ 11 | export function ValidatorConstraint(options?: { name?: string; async?: boolean }) { 12 | return function (target: Function): void { 13 | const isAsync = options && options.async; 14 | let name = options && options.name ? options.name : ''; 15 | if (!name) { 16 | name = (target as any).name; 17 | if (!name) 18 | // generate name if it was not given 19 | name = name.replace(/\.?([A-Z]+)/g, (x, y) => '_' + (y as string).toLowerCase()).replace(/^_/, ''); 20 | } 21 | const metadata = new ConstraintMetadata(target, name, isAsync); 22 | getMetadataStorage().addConstraintMetadata(metadata); 23 | }; 24 | } 25 | 26 | /** 27 | * Performs validation based on the given custom validation class. 28 | * Validation class must be decorated with ValidatorConstraint decorator. 29 | */ 30 | export function Validate(constraintClass: Function, validationOptions?: ValidationOptions): PropertyDecorator; 31 | export function Validate( 32 | constraintClass: Function, 33 | constraints?: any[], 34 | validationOptions?: ValidationOptions 35 | ): PropertyDecorator; 36 | export function Validate( 37 | constraintClass: Function, 38 | constraintsOrValidationOptions?: any[] | ValidationOptions, 39 | maybeValidationOptions?: ValidationOptions 40 | ): PropertyDecorator { 41 | return function (object: object, propertyName: string): void { 42 | const args: ValidationMetadataArgs = { 43 | type: ValidationTypes.CUSTOM_VALIDATION, 44 | target: object.constructor, 45 | propertyName: propertyName, 46 | constraintCls: constraintClass, 47 | constraints: Array.isArray(constraintsOrValidationOptions) ? constraintsOrValidationOptions : undefined, 48 | validationOptions: !Array.isArray(constraintsOrValidationOptions) 49 | ? constraintsOrValidationOptions 50 | : maybeValidationOptions, 51 | }; 52 | getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/decorator/common/ValidateBy.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { registerDecorator } from '../../register-decorator'; 3 | import { ValidationArguments } from '../../validation/ValidationArguments'; 4 | import { ValidatorConstraintInterface } from '../../validation/ValidatorConstraintInterface'; 5 | 6 | export interface ValidateByOptions { 7 | name: string; 8 | constraints?: any[]; 9 | validator: ValidatorConstraintInterface | Function; 10 | async?: boolean; 11 | } 12 | 13 | export function buildMessage( 14 | impl: (eachPrefix: string, args?: ValidationArguments) => string, 15 | validationOptions?: ValidationOptions 16 | ): (validationArguments?: ValidationArguments) => string { 17 | return (validationArguments?: ValidationArguments): string => { 18 | const eachPrefix = validationOptions && validationOptions.each ? 'each value in ' : ''; 19 | return impl(eachPrefix, validationArguments); 20 | }; 21 | } 22 | 23 | export function ValidateBy(options: ValidateByOptions, validationOptions?: ValidationOptions): PropertyDecorator { 24 | return function (object: object, propertyName: string): void { 25 | registerDecorator({ 26 | name: options.name, 27 | target: object.constructor, 28 | propertyName: propertyName, 29 | options: validationOptions, 30 | constraints: options.constraints, 31 | validator: options.validator, 32 | }); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/common/ValidateIf.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; 3 | import { ValidationTypes } from '../../validation/ValidationTypes'; 4 | import { ValidationMetadata } from '../../metadata/ValidationMetadata'; 5 | import { getMetadataStorage } from '../../metadata/MetadataStorage'; 6 | 7 | /** 8 | * Ignores the other validators on a property when the provided condition function returns false. 9 | */ 10 | export function ValidateIf( 11 | condition: (object: any, value: any) => boolean, 12 | validationOptions?: ValidationOptions 13 | ): PropertyDecorator { 14 | return function (object: object, propertyName: string): void { 15 | const args: ValidationMetadataArgs = { 16 | type: ValidationTypes.CONDITIONAL_VALIDATION, 17 | target: object.constructor, 18 | propertyName: propertyName, 19 | constraints: [condition], 20 | validationOptions: validationOptions, 21 | }; 22 | getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/decorator/common/ValidateNested.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; 3 | import { ValidationTypes } from '../../validation/ValidationTypes'; 4 | import { ValidationMetadata } from '../../metadata/ValidationMetadata'; 5 | import { getMetadataStorage } from '../../metadata/MetadataStorage'; 6 | 7 | /** 8 | * Objects / object arrays marked with this decorator will also be validated. 9 | */ 10 | export function ValidateNested(validationOptions?: ValidationOptions): PropertyDecorator { 11 | const opts: ValidationOptions = { ...validationOptions }; 12 | const eachPrefix = opts.each ? 'each value in ' : ''; 13 | opts.message = opts.message || eachPrefix + 'nested property $property must be either object or array'; 14 | 15 | return function (object: object, propertyName: string): void { 16 | const args: ValidationMetadataArgs = { 17 | type: ValidationTypes.NESTED_VALIDATION, 18 | target: object.constructor, 19 | propertyName: propertyName, 20 | validationOptions: opts, 21 | }; 22 | getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/decorator/common/ValidatePromise.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; 3 | import { ValidationTypes } from '../../validation/ValidationTypes'; 4 | import { ValidationMetadata } from '../../metadata/ValidationMetadata'; 5 | import { getMetadataStorage } from '../../metadata/MetadataStorage'; 6 | 7 | /** 8 | * Resolve promise before validation 9 | */ 10 | export function ValidatePromise(validationOptions?: ValidationOptions): PropertyDecorator { 11 | return function (object: object, propertyName: string): void { 12 | const args: ValidationMetadataArgs = { 13 | type: ValidationTypes.PROMISE_VALIDATION, 14 | target: object.constructor, 15 | propertyName: propertyName, 16 | validationOptions: validationOptions, 17 | }; 18 | getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/decorator/date/MaxDate.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const MAX_DATE = 'maxDate'; 5 | 6 | /** 7 | * Checks if the value is a date that's before the specified date. 8 | */ 9 | export function maxDate(date: unknown, maxDate: Date | (() => Date)): boolean { 10 | return date instanceof Date && date.getTime() <= (maxDate instanceof Date ? maxDate : maxDate()).getTime(); 11 | } 12 | 13 | /** 14 | * Checks if the value is a date that's before the specified date. 15 | */ 16 | export function MaxDate(date: Date | (() => Date), validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: MAX_DATE, 20 | constraints: [date], 21 | validator: { 22 | validate: (value, args): boolean => maxDate(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => 'maximal allowed date for ' + eachPrefix + '$property is $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/date/MinDate.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const MIN_DATE = 'minDate'; 5 | 6 | /** 7 | * Checks if the value is a date that's after the specified date. 8 | */ 9 | export function minDate(date: unknown, minDate: Date | (() => Date)): boolean { 10 | return date instanceof Date && date.getTime() >= (minDate instanceof Date ? minDate : minDate()).getTime(); 11 | } 12 | 13 | /** 14 | * Checks if the value is a date that's after the specified date. 15 | */ 16 | export function MinDate(date: Date | (() => Date), validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: MIN_DATE, 20 | constraints: [date], 21 | validator: { 22 | validate: (value, args): boolean => minDate(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => 'minimal allowed date for ' + eachPrefix + '$property is $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/number/IsDivisibleBy.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isDivisibleByValidator from 'validator/lib/isDivisibleBy'; 4 | 5 | export const IS_DIVISIBLE_BY = 'isDivisibleBy'; 6 | 7 | /** 8 | * Checks if value is a number that's divisible by another. 9 | */ 10 | export function isDivisibleBy(value: unknown, num: number): boolean { 11 | return typeof value === 'number' && typeof num === 'number' && isDivisibleByValidator(String(value), num); 12 | } 13 | 14 | /** 15 | * Checks if value is a number that's divisible by another. 16 | */ 17 | export function IsDivisibleBy(num: number, validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_DIVISIBLE_BY, 21 | constraints: [num], 22 | validator: { 23 | validate: (value, args): boolean => isDivisibleBy(value, args?.constraints[0]), 24 | defaultMessage: buildMessage( 25 | eachPrefix => eachPrefix + '$property must be divisible by $constraint1', 26 | validationOptions 27 | ), 28 | }, 29 | }, 30 | validationOptions 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/decorator/number/IsNegative.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_NEGATIVE = 'isNegative'; 5 | 6 | /** 7 | * Checks if the value is a negative number smaller than zero. 8 | */ 9 | export function isNegative(value: unknown): boolean { 10 | return typeof value === 'number' && value < 0; 11 | } 12 | 13 | /** 14 | * Checks if the value is a negative number smaller than zero. 15 | */ 16 | export function IsNegative(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_NEGATIVE, 20 | validator: { 21 | validate: (value, args): boolean => isNegative(value), 22 | defaultMessage: buildMessage( 23 | eachPrefix => eachPrefix + '$property must be a negative number', 24 | validationOptions 25 | ), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/number/IsPositive.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_POSITIVE = 'isPositive'; 5 | 6 | /** 7 | * Checks if the value is a positive number greater than zero. 8 | */ 9 | export function isPositive(value: unknown): boolean { 10 | return typeof value === 'number' && value > 0; 11 | } 12 | 13 | /** 14 | * Checks if the value is a positive number greater than zero. 15 | */ 16 | export function IsPositive(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_POSITIVE, 20 | validator: { 21 | validate: (value, args): boolean => isPositive(value), 22 | defaultMessage: buildMessage( 23 | eachPrefix => eachPrefix + '$property must be a positive number', 24 | validationOptions 25 | ), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/number/Max.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const MAX = 'max'; 5 | 6 | /** 7 | * Checks if the first number is less than or equal to the second. 8 | */ 9 | export function max(num: unknown, max: number): boolean { 10 | return typeof num === 'number' && typeof max === 'number' && num <= max; 11 | } 12 | 13 | /** 14 | * Checks if the value is less than or equal to the allowed maximum value. 15 | */ 16 | export function Max(maxValue: number, validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: MAX, 20 | constraints: [maxValue], 21 | validator: { 22 | validate: (value, args): boolean => max(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must not be greater than $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/number/Min.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const MIN = 'min'; 5 | 6 | /** 7 | * Checks if the first number is greater than or equal to the second. 8 | */ 9 | export function min(num: unknown, min: number): boolean { 10 | return typeof num === 'number' && typeof min === 'number' && num >= min; 11 | } 12 | 13 | /** 14 | * Checks if the value is greater than or equal to the allowed minimum value. 15 | */ 16 | export function Min(minValue: number, validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: MIN, 20 | constraints: [minValue], 21 | validator: { 22 | validate: (value, args): boolean => min(value, args?.constraints[0]), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must not be less than $constraint1', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/object/IsInstance.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_INSTANCE = 'isInstance'; 5 | 6 | /** 7 | * Checks if the value is an instance of the specified object. 8 | */ 9 | export function isInstance(object: unknown, targetTypeConstructor: new (...args: any[]) => any): boolean { 10 | return ( 11 | targetTypeConstructor && typeof targetTypeConstructor === 'function' && object instanceof targetTypeConstructor 12 | ); 13 | } 14 | 15 | /** 16 | * Checks if the value is an instance of the specified object. 17 | */ 18 | export function IsInstance( 19 | targetType: new (...args: any[]) => any, 20 | validationOptions?: ValidationOptions 21 | ): PropertyDecorator { 22 | return ValidateBy( 23 | { 24 | name: IS_INSTANCE, 25 | constraints: [targetType], 26 | validator: { 27 | validate: (value, args): boolean => isInstance(value, args?.constraints[0]), 28 | defaultMessage: buildMessage((eachPrefix, args) => { 29 | if (args?.constraints[0]) { 30 | return eachPrefix + `$property must be an instance of ${args?.constraints[0].name as string}`; 31 | } else { 32 | return eachPrefix + `${IS_INSTANCE} decorator expects and object as value, but got falsy value.`; 33 | } 34 | }, validationOptions), 35 | }, 36 | }, 37 | validationOptions 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/decorator/object/IsNotEmptyObject.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import { isObject } from '../typechecker/IsObject'; 4 | 5 | export const IS_NOT_EMPTY_OBJECT = 'isNotEmptyObject'; 6 | 7 | /** 8 | * Checks if the value is valid Object & not empty. 9 | * Returns false if the value is not an object or an empty valid object. 10 | */ 11 | export function isNotEmptyObject(value: unknown, options?: { nullable?: boolean }): boolean { 12 | if (!isObject(value)) { 13 | return false; 14 | } 15 | 16 | if (options?.nullable === false) { 17 | return !Object.values(value).every(propertyValue => propertyValue === null || propertyValue === undefined); 18 | } 19 | 20 | for (const key in value) { 21 | if (value.hasOwnProperty(key)) { 22 | return true; 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | 29 | /** 30 | * Checks if the value is valid Object & not empty. 31 | * Returns false if the value is not an object or an empty valid object. 32 | */ 33 | export function IsNotEmptyObject( 34 | options?: { nullable?: boolean }, 35 | validationOptions?: ValidationOptions 36 | ): PropertyDecorator { 37 | return ValidateBy( 38 | { 39 | name: IS_NOT_EMPTY_OBJECT, 40 | constraints: [options], 41 | validator: { 42 | validate: (value, args): boolean => isNotEmptyObject(value, args?.constraints[0]), 43 | defaultMessage: buildMessage( 44 | eachPrefix => eachPrefix + '$property must be a non-empty object', 45 | validationOptions 46 | ), 47 | }, 48 | }, 49 | validationOptions 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/decorator/string/Contains.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import containsValidator from 'validator/lib/contains'; 4 | 5 | export const CONTAINS = 'contains'; 6 | 7 | /** 8 | * Checks if the string contains the seed. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function contains(value: unknown, seed: string): boolean { 12 | return typeof value === 'string' && containsValidator(value, seed); 13 | } 14 | 15 | /** 16 | * Checks if the string contains the seed. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function Contains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: CONTAINS, 23 | constraints: [seed], 24 | validator: { 25 | validate: (value, args): boolean => contains(value, args?.constraints[0]), 26 | defaultMessage: buildMessage( 27 | eachPrefix => eachPrefix + '$property must contain a $constraint1 string', 28 | validationOptions 29 | ), 30 | }, 31 | }, 32 | validationOptions 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/string/IsAlpha.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isAlphaValidator from 'validator/lib/isAlpha'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_ALPHA = 'isAlpha'; 7 | 8 | /** 9 | * Checks if the string contains only letters (a-zA-Z). 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isAlpha(value: unknown, locale?: ValidatorJS.AlphaLocale): boolean { 13 | return typeof value === 'string' && isAlphaValidator(value, locale); 14 | } 15 | 16 | /** 17 | * Checks if the string contains only letters (a-zA-Z). 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsAlpha(locale?: ValidatorJS.AlphaLocale, validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: IS_ALPHA, 24 | constraints: [locale], 25 | validator: { 26 | validate: (value, args): boolean => isAlpha(value, args?.constraints[0]), 27 | defaultMessage: buildMessage( 28 | eachPrefix => eachPrefix + '$property must contain only letters (a-zA-Z)', 29 | validationOptions 30 | ), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsAlphanumeric.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isAlphanumericValidator from 'validator/lib/isAlphanumeric'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_ALPHANUMERIC = 'isAlphanumeric'; 7 | 8 | /** 9 | * Checks if the string contains only letters and numbers. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isAlphanumeric(value: unknown, locale?: ValidatorJS.AlphanumericLocale): boolean { 13 | return typeof value === 'string' && isAlphanumericValidator(value, locale); 14 | } 15 | 16 | /** 17 | * Checks if the string contains only letters and numbers. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsAlphanumeric( 21 | locale?: ValidatorJS.AlphanumericLocale, 22 | validationOptions?: ValidationOptions 23 | ): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_ALPHANUMERIC, 27 | constraints: [locale], 28 | validator: { 29 | validate: (value, args): boolean => isAlphanumeric(value, args?.constraints[0]), 30 | defaultMessage: buildMessage( 31 | eachPrefix => eachPrefix + '$property must contain only letters and numbers', 32 | validationOptions 33 | ), 34 | }, 35 | }, 36 | validationOptions 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/decorator/string/IsAscii.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isAsciiValidator from 'validator/lib/isAscii'; 4 | 5 | export const IS_ASCII = 'isAscii'; 6 | 7 | /** 8 | * Checks if the string contains ASCII chars only. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isAscii(value: unknown): boolean { 12 | return typeof value === 'string' && isAsciiValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string contains ASCII chars only. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsAscii(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_ASCII, 23 | validator: { 24 | validate: (value, args): boolean => isAscii(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain only ASCII characters', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsBIC.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isBICValidator from 'validator/lib/isBIC'; 4 | 5 | export const IS_BIC = 'isBIC'; 6 | 7 | /** 8 | * Check if a string is a BIC (Bank Identification Code) or SWIFT code. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isBIC(value: unknown): boolean { 12 | return typeof value === 'string' && isBICValidator(value); 13 | } 14 | 15 | /** 16 | * Check if a string is a BIC (Bank Identification Code) or SWIFT code. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsBIC(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_BIC, 23 | validator: { 24 | validate: (value, args): boolean => isBIC(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a BIC or SWIFT code', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsBase32.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isBase32Validator from 'validator/lib/isBase32'; 4 | 5 | export const IS_BASE32 = 'isBase32'; 6 | 7 | /** 8 | * Checks if a string is base32 encoded. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isBase32(value: unknown): boolean { 12 | return typeof value === 'string' && isBase32Validator(value); 13 | } 14 | 15 | /** 16 | * Check if a string is base32 encoded. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsBase32(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_BASE32, 23 | validator: { 24 | validate: (value, args): boolean => isBase32(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be base32 encoded', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsBase58.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isBase58Validator from 'validator/lib/isBase58'; 4 | 5 | export const IS_BASE58 = 'isBase58'; 6 | 7 | /** 8 | * Checks if a string is base58 encoded. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isBase58(value: unknown): boolean { 12 | return typeof value === 'string' && isBase58Validator(value); 13 | } 14 | 15 | /** 16 | * Checks if a string is base58 encoded. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsBase58(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_BASE58, 23 | validator: { 24 | validate: (value, args): boolean => isBase58(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be base58 encoded', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsBase64.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isBase64Validator from 'validator/lib/isBase64'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_BASE64 = 'isBase64'; 7 | 8 | /** 9 | * Checks if a string is base64 encoded. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isBase64(value: unknown, options?: ValidatorJS.IsBase64Options): boolean { 13 | return typeof value === 'string' && isBase64Validator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if a string is base64 encoded. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsBase64( 21 | options?: ValidatorJS.IsBase64Options, 22 | validationOptions?: ValidationOptions 23 | ): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_BASE64, 27 | constraints: [options], 28 | validator: { 29 | validate: (value, args): boolean => isBase64(value, args?.constraints[0]), 30 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be base64 encoded', validationOptions), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsBooleanString.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isBooleanValidator from 'validator/lib/isBoolean'; 4 | 5 | export const IS_BOOLEAN_STRING = 'isBooleanString'; 6 | 7 | /** 8 | * Checks if a string is a boolean. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isBooleanString(value: unknown): boolean { 12 | return typeof value === 'string' && isBooleanValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if a string is a boolean. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsBooleanString(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_BOOLEAN_STRING, 23 | validator: { 24 | validate: (value, args): boolean => isBooleanString(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a boolean string', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsBtcAddress.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isBtcAddressValidator from 'validator/lib/isBtcAddress'; 4 | 5 | export const IS_BTC_ADDRESS = 'isBtcAddress'; 6 | 7 | /** 8 | * Check if the string is a valid BTC address. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isBtcAddress(value: unknown): boolean { 12 | return typeof value === 'string' && isBtcAddressValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a valid BTC address. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsBtcAddress(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_BTC_ADDRESS, 23 | validator: { 24 | validate: (value, args): boolean => isBtcAddress(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a BTC address', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsByteLength.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isByteLengthValidator from 'validator/lib/isByteLength'; 4 | 5 | export const IS_BYTE_LENGTH = 'isByteLength'; 6 | 7 | /** 8 | * Checks if the string's length (in bytes) falls in a range. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isByteLength(value: unknown, min: number, max?: number): boolean { 12 | return typeof value === 'string' && isByteLengthValidator(value, { min, max }); 13 | } 14 | 15 | /** 16 | * Checks if the string's length (in bytes) falls in a range. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsByteLength(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_BYTE_LENGTH, 23 | constraints: [min, max], 24 | validator: { 25 | validate: (value, args): boolean => isByteLength(value, args?.constraints[0], args?.constraints[1]), 26 | defaultMessage: buildMessage( 27 | eachPrefix => eachPrefix + "$property's byte length must fall into ($constraint1, $constraint2) range", 28 | validationOptions 29 | ), 30 | }, 31 | }, 32 | validationOptions 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/string/IsCreditCard.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isCreditCardValidator from 'validator/lib/isCreditCard'; 4 | 5 | export const IS_CREDIT_CARD = 'isCreditCard'; 6 | 7 | /** 8 | * Checks if the string is a credit card. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isCreditCard(value: unknown): boolean { 12 | return typeof value === 'string' && isCreditCardValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is a credit card. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsCreditCard(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_CREDIT_CARD, 23 | validator: { 24 | validate: (value, args): boolean => isCreditCard(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a credit card', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsCurrency.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isCurrencyValidator from 'validator/lib/isCurrency'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_CURRENCY = 'isCurrency'; 7 | 8 | /** 9 | * Checks if the string is a valid currency amount. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isCurrency(value: unknown, options?: ValidatorJS.IsCurrencyOptions): boolean { 13 | return typeof value === 'string' && isCurrencyValidator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if the string is a valid currency amount. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsCurrency( 21 | options?: ValidatorJS.IsCurrencyOptions, 22 | validationOptions?: ValidationOptions 23 | ): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_CURRENCY, 27 | constraints: [options], 28 | validator: { 29 | validate: (value, args): boolean => isCurrency(value, args?.constraints[0]), 30 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a currency', validationOptions), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsDataURI.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isDataURIValidator from 'validator/lib/isDataURI'; 4 | 5 | export const IS_DATA_URI = 'isDataURI'; 6 | 7 | /** 8 | * Check if the string is a data uri format. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isDataURI(value: unknown): boolean { 12 | return typeof value === 'string' && isDataURIValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a data uri format. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsDataURI(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_DATA_URI, 23 | validator: { 24 | validate: (value, args): boolean => isDataURI(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a data uri format', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsDateString.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import * as ValidatorJS from 'validator'; 4 | import { isISO8601 } from './IsISO8601'; 5 | 6 | export const IS_DATE_STRING = 'isDateString'; 7 | 8 | /** 9 | * Alias for IsISO8601 validator 10 | */ 11 | export function isDateString(value: unknown, options?: ValidatorJS.IsISO8601Options): boolean { 12 | return isISO8601(value, options); 13 | } 14 | 15 | /** 16 | * Alias for IsISO8601 validator 17 | */ 18 | export function IsDateString( 19 | options?: ValidatorJS.IsISO8601Options, 20 | validationOptions?: ValidationOptions 21 | ): PropertyDecorator { 22 | return ValidateBy( 23 | { 24 | name: IS_DATE_STRING, 25 | constraints: [options], 26 | validator: { 27 | validate: (value): boolean => isDateString(value, options), 28 | defaultMessage: buildMessage( 29 | eachPrefix => eachPrefix + '$property must be a valid ISO 8601 date string', 30 | validationOptions 31 | ), 32 | }, 33 | }, 34 | validationOptions 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/decorator/string/IsDecimal.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isDecimalValidator from 'validator/lib/isDecimal'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_DECIMAL = 'isDecimal'; 7 | 8 | /** 9 | * Checks if the string is a valid decimal. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isDecimal(value: unknown, options?: ValidatorJS.IsDecimalOptions): boolean { 13 | return typeof value === 'string' && isDecimalValidator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if the string is a valid decimal. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsDecimal( 21 | options?: ValidatorJS.IsDecimalOptions, 22 | validationOptions?: ValidationOptions 23 | ): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_DECIMAL, 27 | constraints: [options], 28 | validator: { 29 | validate: (value, args): boolean => isDecimal(value, args?.constraints[0]), 30 | defaultMessage: buildMessage( 31 | eachPrefix => eachPrefix + '$property is not a valid decimal number.', 32 | validationOptions 33 | ), 34 | }, 35 | }, 36 | validationOptions 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/decorator/string/IsEAN.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isEANValidator from 'validator/lib/isEAN'; 4 | 5 | export const IS_EAN = 'isEAN'; 6 | 7 | /** 8 | * Check if the string is an EAN (European Article Number). 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isEAN(value: unknown): boolean { 12 | return typeof value === 'string' && isEANValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is an EAN (European Article Number). 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsEAN(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_EAN, 23 | validator: { 24 | validate: (value, args): boolean => isEAN(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be an EAN (European Article Number)', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsEmail.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isEmailValidator from 'validator/lib/isEmail'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_EMAIL = 'isEmail'; 7 | 8 | /** 9 | * Checks if the string is an email. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isEmail(value: unknown, options?: ValidatorJS.IsEmailOptions): boolean { 13 | return typeof value === 'string' && isEmailValidator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if the string is an email. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsEmail( 21 | options?: ValidatorJS.IsEmailOptions, 22 | validationOptions?: ValidationOptions 23 | ): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_EMAIL, 27 | constraints: [options], 28 | validator: { 29 | validate: (value, args): boolean => isEmail(value, args?.constraints[0]), 30 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an email', validationOptions), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsEthereumAddress.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isEthereumAddressValidator from 'validator/lib/isEthereumAddress'; 4 | 5 | export const IS_ETHEREUM_ADDRESS = 'isEthereumAddress'; 6 | 7 | /** 8 | * Check if the string is an Ethereum address using basic regex. Does not validate address checksums. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isEthereumAddress(value: unknown): boolean { 12 | return typeof value === 'string' && isEthereumAddressValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is an Ethereum address using basic regex. Does not validate address checksums. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsEthereumAddress(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_ETHEREUM_ADDRESS, 23 | validator: { 24 | validate: (value, args): boolean => isEthereumAddress(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be an Ethereum address', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsFQDN.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isFqdnValidator from 'validator/lib/isFQDN'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_FQDN = 'isFqdn'; 7 | 8 | /** 9 | * Checks if the string is a fully qualified domain name (e.g. domain.com). 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isFQDN(value: unknown, options?: ValidatorJS.IsFQDNOptions): boolean { 13 | return typeof value === 'string' && isFqdnValidator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if the string is a fully qualified domain name (e.g. domain.com). 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsFQDN(options?: ValidatorJS.IsFQDNOptions, validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: IS_FQDN, 24 | constraints: [options], 25 | validator: { 26 | validate: (value, args): boolean => isFQDN(value, args?.constraints[0]), 27 | defaultMessage: buildMessage( 28 | eachPrefix => eachPrefix + '$property must be a valid domain name', 29 | validationOptions 30 | ), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsFirebasePushId.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_FIREBASE_PUSH_ID = 'IsFirebasePushId'; 5 | 6 | /** 7 | * Checks if the string is a Firebase Push Id 8 | * If given value is not a Firebase Push Id, it returns false 9 | */ 10 | export function isFirebasePushId(value: unknown): boolean { 11 | const webSafeRegex = /^[a-zA-Z0-9_-]*$/; 12 | return typeof value === 'string' && value.length === 20 && webSafeRegex.test(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is a Firebase Push Id 17 | * If given value is not a Firebase Push Id, it returns false 18 | */ 19 | export function IsFirebasePushId(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_FIREBASE_PUSH_ID, 23 | validator: { 24 | validate: (value, args): boolean => isFirebasePushId(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a Firebase Push Id', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsFullWidth.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isFullWidthValidator from 'validator/lib/isFullWidth'; 4 | 5 | export const IS_FULL_WIDTH = 'isFullWidth'; 6 | 7 | /** 8 | * Checks if the string contains any full-width chars. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isFullWidth(value: unknown): boolean { 12 | return typeof value === 'string' && isFullWidthValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string contains any full-width chars. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsFullWidth(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_FULL_WIDTH, 23 | validator: { 24 | validate: (value, args): boolean => isFullWidth(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain a full-width characters', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsHSL.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isHSLValidator from 'validator/lib/isHSL'; 4 | 5 | export const IS_HSL = 'isHSL'; 6 | 7 | /** 8 | * Check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on CSS Colors Level 4 specification. 9 | * Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: hsl(200grad+.1%62%/1)). 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isHSL(value: unknown): boolean { 13 | return typeof value === 'string' && isHSLValidator(value); 14 | } 15 | 16 | /** 17 | * Check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on CSS Colors Level 4 specification. 18 | * Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: hsl(200grad+.1%62%/1)). 19 | * If given value is not a string, then it returns false. 20 | */ 21 | export function IsHSL(validationOptions?: ValidationOptions): PropertyDecorator { 22 | return ValidateBy( 23 | { 24 | name: IS_HSL, 25 | validator: { 26 | validate: (value, args): boolean => isHSL(value), 27 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a HSL color', validationOptions), 28 | }, 29 | }, 30 | validationOptions 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/decorator/string/IsHalfWidth.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isHalfWidthValidator from 'validator/lib/isHalfWidth'; 4 | 5 | export const IS_HALF_WIDTH = 'isHalfWidth'; 6 | 7 | /** 8 | * Checks if the string contains any half-width chars. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isHalfWidth(value: unknown): boolean { 12 | return typeof value === 'string' && isHalfWidthValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string contains any half-width chars. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsHalfWidth(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_HALF_WIDTH, 23 | validator: { 24 | validate: (value, args): boolean => isHalfWidth(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain a half-width characters', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsHash.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isHashValidator from 'validator/lib/isHash'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_HASH = 'isHash'; 7 | 8 | /** 9 | * Check if the string is a hash of type algorithm. 10 | * Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 11 | * 'tiger160', 'tiger192', 'crc32', 'crc32b'] 12 | */ 13 | export function isHash(value: unknown, algorithm: ValidatorJS.HashAlgorithm): boolean { 14 | return typeof value === 'string' && isHashValidator(value, algorithm); 15 | } 16 | 17 | /** 18 | * Check if the string is a hash of type algorithm. 19 | * Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 20 | * 'tiger160', 'tiger192', 'crc32', 'crc32b'] 21 | */ 22 | export function IsHash(algorithm: string, validationOptions?: ValidationOptions): PropertyDecorator { 23 | return ValidateBy( 24 | { 25 | name: IS_HASH, 26 | constraints: [algorithm], 27 | validator: { 28 | validate: (value, args): boolean => isHash(value, args?.constraints[0]), 29 | defaultMessage: buildMessage( 30 | eachPrefix => eachPrefix + '$property must be a hash of type $constraint1', 31 | validationOptions 32 | ), 33 | }, 34 | }, 35 | validationOptions 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /src/decorator/string/IsHexColor.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isHexColorValidator from 'validator/lib/isHexColor'; 4 | 5 | export const IS_HEX_COLOR = 'isHexColor'; 6 | 7 | /** 8 | * Checks if the string is a hexadecimal color. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isHexColor(value: unknown): boolean { 12 | return typeof value === 'string' && isHexColorValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is a hexadecimal color. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsHexColor(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_HEX_COLOR, 23 | validator: { 24 | validate: (value, args): boolean => isHexColor(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a hexadecimal color', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsHexadecimal.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isHexadecimalValidator from 'validator/lib/isHexadecimal'; 4 | 5 | export const IS_HEXADECIMAL = 'isHexadecimal'; 6 | 7 | /** 8 | * Checks if the string is a hexadecimal number. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isHexadecimal(value: unknown): boolean { 12 | return typeof value === 'string' && isHexadecimalValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is a hexadecimal number. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsHexadecimal(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_HEXADECIMAL, 23 | validator: { 24 | validate: (value, args): boolean => isHexadecimal(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a hexadecimal number', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsIBAN.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isIBANValidator from 'validator/lib/isIBAN'; 4 | 5 | export const IS_IBAN = 'isIBAN'; 6 | 7 | /** 8 | * Check if a string is a IBAN (International Bank Account Number). 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isIBAN(value: unknown): boolean { 12 | return typeof value === 'string' && isIBANValidator(value); 13 | } 14 | 15 | /** 16 | * Check if a string is a IBAN (International Bank Account Number). 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsIBAN(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_IBAN, 23 | validator: { 24 | validate: (value, args): boolean => isIBAN(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an IBAN', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsIP.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isIPValidator from 'validator/lib/isIP'; 4 | 5 | export type IsIpVersion = '4' | '6' | 4 | 6; 6 | 7 | export const IS_IP = 'isIp'; 8 | 9 | /** 10 | * Checks if the string is an IP (version 4 or 6). 11 | * If given value is not a string, then it returns false. 12 | */ 13 | export function isIP(value: unknown, version?: IsIpVersion): boolean { 14 | /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion */ 15 | const versionStr = version ? (`${version}` as '4' | '6') : undefined; 16 | return typeof value === 'string' && isIPValidator(value, versionStr); 17 | } 18 | 19 | /** 20 | * Checks if the string is an IP (version 4 or 6). 21 | * If given value is not a string, then it returns false. 22 | */ 23 | export function IsIP(version?: IsIpVersion, validationOptions?: ValidationOptions): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_IP, 27 | constraints: [version], 28 | validator: { 29 | validate: (value, args): boolean => isIP(value, args?.constraints[0]), 30 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an ip address', validationOptions), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsISBN.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isIsbnValidator from 'validator/lib/isISBN'; 4 | 5 | export type IsISBNVersion = '10' | '13' | 10 | 13; 6 | 7 | export const IS_ISBN = 'isIsbn'; 8 | 9 | /** 10 | * Checks if the string is an ISBN (version 10 or 13). 11 | * If given value is not a string, then it returns false. 12 | */ 13 | export function isISBN(value: unknown, version?: IsISBNVersion): boolean { 14 | /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion */ 15 | const versionStr = version ? (`${version}` as '10' | '13') : undefined; 16 | return typeof value === 'string' && isIsbnValidator(value, versionStr); 17 | } 18 | 19 | /** 20 | * Checks if the string is an ISBN (version 10 or 13). 21 | * If given value is not a string, then it returns false. 22 | */ 23 | export function IsISBN(version?: IsISBNVersion, validationOptions?: ValidationOptions): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_ISBN, 27 | constraints: [version], 28 | validator: { 29 | validate: (value, args): boolean => isISBN(value, args?.constraints[0]), 30 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an ISBN', validationOptions), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsISIN.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isIsinValidator from 'validator/lib/isISIN'; 4 | 5 | export const IS_ISIN = 'isIsin'; 6 | 7 | /** 8 | * Checks if the string is an ISIN (stock/security identifier). 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isISIN(value: unknown): boolean { 12 | return typeof value === 'string' && isIsinValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is an ISIN (stock/security identifier). 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsISIN(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_ISIN, 23 | validator: { 24 | validate: (value, args): boolean => isISIN(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be an ISIN (stock/security identifier)', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsISO31661Alpha2.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isISO31661Alpha2Validator from 'validator/lib/isISO31661Alpha2'; 4 | 5 | export const IS_ISO31661_ALPHA_2 = 'isISO31661Alpha2'; 6 | 7 | /** 8 | * Check if the string is a valid [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) officially assigned country code. 9 | */ 10 | export function isISO31661Alpha2(value: unknown): boolean { 11 | return typeof value === 'string' && isISO31661Alpha2Validator(value); 12 | } 13 | 14 | /** 15 | * Check if the string is a valid [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) officially assigned country code. 16 | */ 17 | export function IsISO31661Alpha2(validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_ISO31661_ALPHA_2, 21 | validator: { 22 | validate: (value, args): boolean => isISO31661Alpha2(value), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be a valid ISO31661 Alpha2 code', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/string/IsISO31661Alpha3.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isISO31661Alpha3Validator from 'validator/lib/isISO31661Alpha3'; 4 | 5 | export const IS_ISO31661_ALPHA_3 = 'isISO31661Alpha3'; 6 | 7 | /** 8 | * Check if the string is a valid [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) officially assigned country code. 9 | */ 10 | export function isISO31661Alpha3(value: unknown): boolean { 11 | return typeof value === 'string' && isISO31661Alpha3Validator(value); 12 | } 13 | 14 | /** 15 | * Check if the string is a valid [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) officially assigned country code. 16 | */ 17 | export function IsISO31661Alpha3(validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_ISO31661_ALPHA_3, 21 | validator: { 22 | validate: (value, args): boolean => isISO31661Alpha3(value), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be a valid ISO31661 Alpha3 code', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/string/IsISO8601.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isIso8601Validator from 'validator/lib/isISO8601'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_ISO8601 = 'isIso8601'; 7 | 8 | /** 9 | * Checks if the string is a valid ISO 8601 date. 10 | * If given value is not a string, then it returns false. 11 | * Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. 12 | */ 13 | export function isISO8601(value: unknown, options?: ValidatorJS.IsISO8601Options): boolean { 14 | return typeof value === 'string' && isIso8601Validator(value, options); 15 | } 16 | 17 | /** 18 | * Checks if the string is a valid ISO 8601 date. 19 | * If given value is not a string, then it returns false. 20 | * Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. 21 | */ 22 | export function IsISO8601( 23 | options?: ValidatorJS.IsISO8601Options, 24 | validationOptions?: ValidationOptions 25 | ): PropertyDecorator { 26 | return ValidateBy( 27 | { 28 | name: IS_ISO8601, 29 | constraints: [options], 30 | validator: { 31 | validate: (value, args): boolean => isISO8601(value, args?.constraints[0]), 32 | defaultMessage: buildMessage( 33 | eachPrefix => eachPrefix + '$property must be a valid ISO 8601 date string', 34 | validationOptions 35 | ), 36 | }, 37 | }, 38 | validationOptions 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/decorator/string/IsISRC.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isISRCValidator from 'validator/lib/isISRC'; 4 | 5 | export const IS_ISRC = 'isISRC'; 6 | 7 | /** 8 | * Check if the string is a ISRC. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isISRC(value: unknown): boolean { 12 | return typeof value === 'string' && isISRCValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a ISRC. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsISRC(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_ISRC, 23 | validator: { 24 | validate: (value, args): boolean => isISRC(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an ISRC', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsISSN.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isISSNValidator from 'validator/lib/isISSN'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_ISSN = 'isISSN'; 7 | 8 | /** 9 | * Checks if the string is a ISSN. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isISSN(value: unknown, options?: ValidatorJS.IsISSNOptions): boolean { 13 | return typeof value === 'string' && isISSNValidator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if the string is a ISSN. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsISSN(options?: ValidatorJS.IsISSNOptions, validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: IS_ISSN, 24 | constraints: [options], 25 | validator: { 26 | validate: (value, args): boolean => isISSN(value, args?.constraints[0]), 27 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a ISSN', validationOptions), 28 | }, 29 | }, 30 | validationOptions 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/decorator/string/IsIdentityCard.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isIdentityCardValidator from 'validator/lib/isIdentityCard'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_IDENTITY_CARD = 'isIdentityCard'; 7 | 8 | /** 9 | * Check if the string is a valid identity card code. 10 | * locale is one of ['ES', 'zh-TW', 'he-IL', 'ar-TN'] OR 'any'. If 'any' is used, function will check if any of the locals match. 11 | * Defaults to 'any'. 12 | * If given value is not a string, then it returns false. 13 | */ 14 | export function isIdentityCard(value: unknown, locale: ValidatorJS.IdentityCardLocale): boolean { 15 | return typeof value === 'string' && isIdentityCardValidator(value, locale); 16 | } 17 | 18 | /** 19 | * Check if the string is a valid identity card code. 20 | * locale is one of ['ES', 'zh-TW', 'he-IL', 'ar-TN'] OR 'any'. If 'any' is used, function will check if any of the locals match. 21 | * Defaults to 'any'. 22 | * If given value is not a string, then it returns false. 23 | */ 24 | export function IsIdentityCard( 25 | locale?: ValidatorJS.IdentityCardLocale, 26 | validationOptions?: ValidationOptions 27 | ): PropertyDecorator { 28 | return ValidateBy( 29 | { 30 | name: IS_IDENTITY_CARD, 31 | constraints: [locale], 32 | validator: { 33 | validate: (value, args): boolean => isIdentityCard(value, args?.constraints[0]), 34 | defaultMessage: buildMessage( 35 | eachPrefix => eachPrefix + '$property must be a identity card number', 36 | validationOptions 37 | ), 38 | }, 39 | }, 40 | validationOptions 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/decorator/string/IsJSON.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isJSONValidator from 'validator/lib/isJSON'; 4 | 5 | export const IS_JSON = 'isJson'; 6 | 7 | /** 8 | * Checks if the string is valid JSON (note: uses JSON.parse). 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isJSON(value: unknown): boolean { 12 | return typeof value === 'string' && isJSONValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is valid JSON (note: uses JSON.parse). 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsJSON(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_JSON, 23 | validator: { 24 | validate: (value, args): boolean => isJSON(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a json string', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsJWT.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isJwtValidator from 'validator/lib/isJWT'; 4 | 5 | export const IS_JWT = 'isJwt'; 6 | 7 | /** 8 | * Checks if the string is valid JWT token. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isJWT(value: unknown): boolean { 12 | return typeof value === 'string' && isJwtValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is valid JWT token. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsJWT(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_JWT, 23 | validator: { 24 | validate: (value, args): boolean => isJWT(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a jwt string', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsLocale.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isLocaleValidator from 'validator/lib/isLocale'; 4 | 5 | export const IS_LOCALE = 'isLocale'; 6 | 7 | /** 8 | * Check if the string is a locale. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isLocale(value: unknown): boolean { 12 | return typeof value === 'string' && isLocaleValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a locale. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsLocale(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_LOCALE, 23 | validator: { 24 | validate: (value, args): boolean => isLocale(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be locale', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsLowercase.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isLowercaseValidator from 'validator/lib/isLowercase'; 4 | 5 | export const IS_LOWERCASE = 'isLowercase'; 6 | 7 | /** 8 | * Checks if the string is lowercase. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isLowercase(value: unknown): boolean { 12 | return typeof value === 'string' && isLowercaseValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is lowercase. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsLowercase(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_LOWERCASE, 23 | validator: { 24 | validate: (value, args): boolean => isLowercase(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a lowercase string', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsMacAddress.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions, isValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isMacAddressValidator from 'validator/lib/isMACAddress'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_MAC_ADDRESS = 'isMacAddress'; 7 | 8 | /** 9 | * Check if the string is a MAC address. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isMACAddress(value: unknown, options?: ValidatorJS.IsMACAddressOptions): boolean { 13 | return typeof value === 'string' && isMacAddressValidator(value, options); 14 | } 15 | 16 | /** 17 | * Check if the string is a MAC address. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsMACAddress( 21 | optionsArg?: ValidatorJS.IsMACAddressOptions, 22 | validationOptionsArg?: ValidationOptions 23 | ): PropertyDecorator; 24 | export function IsMACAddress(validationOptionsArg?: ValidationOptions): PropertyDecorator; 25 | export function IsMACAddress( 26 | optionsOrValidationOptionsArg?: ValidatorJS.IsMACAddressOptions | ValidationOptions, 27 | validationOptionsArg?: ValidationOptions 28 | ): PropertyDecorator { 29 | const options = !isValidationOptions(optionsOrValidationOptionsArg) ? optionsOrValidationOptionsArg : undefined; 30 | const validationOptions = isValidationOptions(optionsOrValidationOptionsArg) 31 | ? optionsOrValidationOptionsArg 32 | : validationOptionsArg; 33 | 34 | return ValidateBy( 35 | { 36 | name: IS_MAC_ADDRESS, 37 | constraints: [options], 38 | validator: { 39 | validate: (value, args): boolean => isMACAddress(value, options), 40 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a MAC Address', validationOptions), 41 | }, 42 | }, 43 | validationOptions 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/decorator/string/IsMagnetURI.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isMagnetURIValidator from 'validator/lib/isMagnetURI'; 4 | 5 | export const IS_MAGNET_URI = 'isMagnetURI'; 6 | 7 | /** 8 | * Check if the string is a magnet uri format. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isMagnetURI(value: unknown): boolean { 12 | return typeof value === 'string' && isMagnetURIValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a magnet uri format. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsMagnetURI(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_MAGNET_URI, 23 | validator: { 24 | validate: (value, args): boolean => isMagnetURI(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be magnet uri format', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsMilitaryTime.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import matchesValidator from 'validator/lib/matches'; 4 | 5 | export const IS_MILITARY_TIME = 'isMilitaryTime'; 6 | 7 | /** 8 | * Checks if the string represents a time without a given timezone in the format HH:MM (military) 9 | * If the given value does not match the pattern HH:MM, then it returns false. 10 | */ 11 | export function isMilitaryTime(value: unknown): boolean { 12 | const militaryTimeRegex = /^([01]\d|2[0-3]):?([0-5]\d)$/; 13 | return typeof value === 'string' && matchesValidator(value, militaryTimeRegex); 14 | } 15 | 16 | /** 17 | * Checks if the string represents a time without a given timezone in the format HH:MM (military) 18 | * If the given value does not match the pattern HH:MM, then it returns false. 19 | */ 20 | export function IsMilitaryTime(validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: IS_MILITARY_TIME, 24 | validator: { 25 | validate: (value, args): boolean => isMilitaryTime(value), 26 | defaultMessage: buildMessage( 27 | eachPrefix => eachPrefix + '$property must be a valid representation of military time in the format HH:MM', 28 | validationOptions 29 | ), 30 | }, 31 | }, 32 | validationOptions 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/string/IsMimeType.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isMimeTypeValidator from 'validator/lib/isMimeType'; 4 | 5 | export const IS_MIME_TYPE = 'isMimeType'; 6 | 7 | /** 8 | * Check if the string matches to a valid MIME type format 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isMimeType(value: unknown): boolean { 12 | return typeof value === 'string' && isMimeTypeValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string matches to a valid MIME type format 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsMimeType(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_MIME_TYPE, 23 | validator: { 24 | validate: (value, args): boolean => isMimeType(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be MIME type format', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsMongoId.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isMongoIdValidator from 'validator/lib/isMongoId'; 4 | 5 | export const IS_MONGO_ID = 'isMongoId'; 6 | 7 | /** 8 | * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isMongoId(value: unknown): boolean { 12 | return typeof value === 'string' && isMongoIdValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsMongoId(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_MONGO_ID, 23 | validator: { 24 | validate: (value, args): boolean => isMongoId(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a mongodb id', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsMultibyte.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isMultibyteValidator from 'validator/lib/isMultibyte'; 4 | 5 | export const IS_MULTIBYTE = 'isMultibyte'; 6 | 7 | /** 8 | * Checks if the string contains one or more multibyte chars. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isMultibyte(value: unknown): boolean { 12 | return typeof value === 'string' && isMultibyteValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string contains one or more multibyte chars. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsMultibyte(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_MULTIBYTE, 23 | validator: { 24 | validate: (value, args): boolean => isMultibyte(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain one or more multibyte chars', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsNumberString.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isNumericValidator from 'validator/lib/isNumeric'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_NUMBER_STRING = 'isNumberString'; 7 | 8 | /** 9 | * Checks if the string is numeric. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isNumberString(value: unknown, options?: ValidatorJS.IsNumericOptions): boolean { 13 | return typeof value === 'string' && isNumericValidator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if the string is numeric. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsNumberString( 21 | options?: ValidatorJS.IsNumericOptions, 22 | validationOptions?: ValidationOptions 23 | ): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_NUMBER_STRING, 27 | constraints: [options], 28 | validator: { 29 | validate: (value, args): boolean => isNumberString(value, args?.constraints[0]), 30 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a number string', validationOptions), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsOctal.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isOctalValidator from 'validator/lib/isOctal'; 4 | 5 | export const IS_OCTAL = 'isOctal'; 6 | 7 | /** 8 | * Check if the string is a valid octal number. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isOctal(value: unknown): boolean { 12 | return typeof value === 'string' && isOctalValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a valid octal number. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsOctal(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_OCTAL, 23 | validator: { 24 | validate: (value, args): boolean => isOctal(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be valid octal number', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsPassportNumber.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isPassportNumberValidator from 'validator/lib/isPassportNumber'; 4 | 5 | export const IS_PASSPORT_NUMBER = 'isPassportNumber'; 6 | 7 | /** 8 | * Check if the string is a valid passport number relative to a specific country code. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isPassportNumber(value: unknown, countryCode: string): boolean { 12 | return typeof value === 'string' && isPassportNumberValidator(value, countryCode); 13 | } 14 | 15 | /** 16 | * Check if the string is a valid passport number relative to a specific country code. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsPassportNumber(countryCode: string, validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_PASSPORT_NUMBER, 23 | constraints: [countryCode], 24 | validator: { 25 | validate: (value, args): boolean => isPassportNumber(value, args?.constraints[0]), 26 | defaultMessage: buildMessage( 27 | eachPrefix => eachPrefix + '$property must be valid passport number', 28 | validationOptions 29 | ), 30 | }, 31 | }, 32 | validationOptions 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/string/IsPhoneNumber.spec.ts: -------------------------------------------------------------------------------- 1 | import { isPhoneNumber } from './IsPhoneNumber'; 2 | 3 | describe('@IsPhoneNumber decorator implementation', () => { 4 | describe('isPhoneNumber validator', () => { 5 | it('should accept valid values', () => { 6 | expect(isPhoneNumber('+36 20 111 1111')).toBe(true); 7 | expect(isPhoneNumber('+36 20 111 1111', 'HU')).toBe(true); 8 | expect(isPhoneNumber('20 111 1111', 'HU')).toBe(true); 9 | }); 10 | 11 | describe('should not accept invalid values', () => { 12 | it('xxx', () => { 13 | expect(isPhoneNumber('aaaaa', 'HU')).toBe(false); 14 | expect(isPhoneNumber('aaaa')).toBe(false); 15 | }); 16 | 17 | it('when country code do not match number', () => { 18 | // US number with Hungarian locale 19 | expect(isPhoneNumber('+1 213 373 4253', 'HU')).toBe(false); 20 | // Hungarian number with US locale 21 | expect(isPhoneNumber('+36 20 111 1111', 'US')).toBe(false); 22 | }); 23 | 24 | it('when number is invalid in given locale', () => { 25 | expect(isPhoneNumber('+36 00 111 1111', 'HU')).toBe(false); 26 | }); 27 | 28 | it('when phone number length is incorrect', () => { 29 | expect(isPhoneNumber('+36 20 111 111', 'HU')).toBe(false); 30 | }); 31 | 32 | it('when there are letters after or before the phone number', () => { 33 | expect(isPhoneNumber('abc +36 20 111 111', 'HU')).toBe(false); 34 | expect(isPhoneNumber('+36 20 111 111 abc', 'HU')).toBe(false); 35 | }); 36 | }); 37 | 38 | it('should not accept values with extra whitespace', () => { 39 | expect(isPhoneNumber(' +36 20 111 1111', 'HU')).toBe(false); 40 | expect(isPhoneNumber('+36 20 111 1111 ', 'HU')).toBe(false); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/decorator/string/IsPhoneNumber.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import { parsePhoneNumber, CountryCode } from 'libphonenumber-js/max'; 4 | 5 | export const IS_PHONE_NUMBER = 'isPhoneNumber'; 6 | 7 | /** 8 | * Checks if the string is a valid phone number. To successfully validate any phone number the text must include 9 | * the intl. calling code, if the calling code wont be provided then the region must be set. 10 | * 11 | * @param value the potential phone number string to test 12 | * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. 13 | * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. 14 | */ 15 | export function isPhoneNumber(value: string, region?: CountryCode): boolean { 16 | if (typeof value !== 'string' || value.trim() !== value) { 17 | return false; 18 | } 19 | 20 | try { 21 | const phoneNumber = parsePhoneNumber(value, region); 22 | 23 | /** 24 | * We fail the validation if the user provided a region code 25 | * and it doesn't match with the country code of the parsed number. 26 | **/ 27 | if (region && phoneNumber.country !== region) { 28 | return false; 29 | } 30 | 31 | return phoneNumber.isValid(); 32 | } catch (error) { 33 | return false; 34 | } 35 | } 36 | 37 | /** 38 | * Checks if the string is a valid phone number. To successfully validate any phone number the text must include 39 | * the intl. calling code, if the calling code wont be provided then the region must be set. 40 | * 41 | * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. 42 | * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. 43 | */ 44 | export function IsPhoneNumber(region?: CountryCode, validationOptions?: ValidationOptions): PropertyDecorator { 45 | return ValidateBy( 46 | { 47 | name: IS_PHONE_NUMBER, 48 | constraints: [region], 49 | validator: { 50 | validate: (value, args): boolean => isPhoneNumber(value, args?.constraints[0]), 51 | defaultMessage: buildMessage( 52 | eachPrefix => eachPrefix + '$property must be a valid phone number', 53 | validationOptions 54 | ), 55 | }, 56 | }, 57 | validationOptions 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/decorator/string/IsPort.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isPortValidator from 'validator/lib/isPort'; 4 | 5 | export const IS_PORT = 'isPort'; 6 | 7 | /** 8 | * Check if the string is a valid port number. 9 | */ 10 | export function isPort(value: unknown): boolean { 11 | return typeof value === 'string' && isPortValidator(value); 12 | } 13 | 14 | /** 15 | * Check if the string is a valid port number. 16 | */ 17 | export function IsPort(validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_PORT, 21 | validator: { 22 | validate: (value, args): boolean => isPort(value), 23 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a port', validationOptions), 24 | }, 25 | }, 26 | validationOptions 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/decorator/string/IsPostalCode.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isPostalCodeValidator from 'validator/lib/isPostalCode'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_POSTAL_CODE = 'isPostalCode'; 7 | 8 | /** 9 | * Check if the string is a postal code, in the specified locale. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isPostalCode(value: unknown, locale: 'any' | ValidatorJS.PostalCodeLocale): boolean { 13 | return typeof value === 'string' && isPostalCodeValidator(value, locale); 14 | } 15 | 16 | /** 17 | * Check if the string is a postal code, in the specified locale. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsPostalCode( 21 | locale?: 'any' | ValidatorJS.PostalCodeLocale, 22 | validationOptions?: ValidationOptions 23 | ): PropertyDecorator { 24 | return ValidateBy( 25 | { 26 | name: IS_POSTAL_CODE, 27 | constraints: [locale], 28 | validator: { 29 | validate: (value, args): boolean => isPostalCode(value, args?.constraints[0]), 30 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a postal code', validationOptions), 31 | }, 32 | }, 33 | validationOptions 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/decorator/string/IsRFC3339.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isRFC3339Validator from 'validator/lib/isRFC3339'; 4 | 5 | export const IS_RFC_3339 = 'isRFC3339'; 6 | 7 | /** 8 | * Check if the string is a valid RFC 3339 date. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isRFC3339(value: unknown): boolean { 12 | return typeof value === 'string' && isRFC3339Validator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a valid RFC 3339 date. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsRFC3339(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_RFC_3339, 23 | validator: { 24 | validate: (value, args): boolean => isRFC3339(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be RFC 3339 date', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsRgbColor.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isRgbColorValidator from 'validator/lib/isRgbColor'; 4 | 5 | export const IS_RGB_COLOR = 'isRgbColor'; 6 | 7 | /** 8 | * Check if the string is a rgb or rgba color. 9 | * `includePercentValues` defaults to true. If you don't want to allow to set rgb or rgba values with percents, like rgb(5%,5%,5%), or rgba(90%,90%,90%,.3), then set it to false. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isRgbColor(value: unknown, includePercentValues?: boolean): boolean { 13 | return typeof value === 'string' && isRgbColorValidator(value, includePercentValues); 14 | } 15 | 16 | /** 17 | * Check if the string is a rgb or rgba color. 18 | * `includePercentValues` defaults to true. If you don't want to allow to set rgb or rgba values with percents, like rgb(5%,5%,5%), or rgba(90%,90%,90%,.3), then set it to false. 19 | * If given value is not a string, then it returns false. 20 | */ 21 | export function IsRgbColor(includePercentValues?: boolean, validationOptions?: ValidationOptions): PropertyDecorator { 22 | return ValidateBy( 23 | { 24 | name: IS_RGB_COLOR, 25 | constraints: [includePercentValues], 26 | validator: { 27 | validate: (value, args): boolean => isRgbColor(value, args?.constraints[0]), 28 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be RGB color', validationOptions), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsSemVer.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isSemVerValidator from 'validator/lib/isSemVer'; 4 | 5 | export const IS_SEM_VER = 'isSemVer'; 6 | 7 | /** 8 | * Check if the string is a Semantic Versioning Specification (SemVer). 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isSemVer(value: unknown): boolean { 12 | return typeof value === 'string' && isSemVerValidator(value); 13 | } 14 | 15 | /** 16 | * Check if the string is a Semantic Versioning Specification (SemVer). 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsSemVer(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_SEM_VER, 23 | validator: { 24 | validate: (value, args): boolean => isSemVer(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must be a Semantic Versioning Specification', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsStrongPassword.ts: -------------------------------------------------------------------------------- 1 | import * as validator from 'validator'; 2 | import { ValidationOptions } from '../ValidationOptions'; 3 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 4 | 5 | export const IS_STRONG_PASSWORD = 'isStrongPassword'; 6 | 7 | /** 8 | * Options to be passed to IsStrongPassword decorator. 9 | */ 10 | export type IsStrongPasswordOptions = Pick< 11 | validator.StrongPasswordOptions, 12 | 'minLength' | 'minLowercase' | 'minUppercase' | 'minNumbers' | 'minSymbols' 13 | >; 14 | 15 | /** 16 | * Checks if the string is a strong password. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function isStrongPassword(value: unknown, options?: IsStrongPasswordOptions): boolean { 20 | return typeof value === 'string' && validator.isStrongPassword(value, options); 21 | } 22 | 23 | /** 24 | * Checks if the string is a strong password. 25 | * If given value is not a string, then it returns false. 26 | */ 27 | export function IsStrongPassword( 28 | options?: IsStrongPasswordOptions, 29 | validationOptions?: ValidationOptions 30 | ): PropertyDecorator { 31 | return ValidateBy( 32 | { 33 | name: IS_STRONG_PASSWORD, 34 | constraints: [options], 35 | validator: { 36 | validate: (value, args): boolean => isStrongPassword(value, args.constraints[0]), 37 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property is not strong enough', validationOptions), 38 | }, 39 | }, 40 | validationOptions 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/decorator/string/IsSurrogatePair.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isSurrogatePairValidator from 'validator/lib/isSurrogatePair'; 4 | 5 | export const IS_SURROGATE_PAIR = 'isSurrogatePair'; 6 | 7 | /** 8 | * Checks if the string contains any surrogate pairs chars. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isSurrogatePair(value: unknown): boolean { 12 | return typeof value === 'string' && isSurrogatePairValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string contains any surrogate pairs chars. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsSurrogatePair(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_SURROGATE_PAIR, 23 | validator: { 24 | validate: (value, args): boolean => isSurrogatePair(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain any surrogate pairs chars', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/IsTimeZone.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_TIMEZONE = 'isTimeZone'; 5 | 6 | /** 7 | * Checks if the string represents a valid IANA timezone 8 | * If the given value is not a valid IANA timezone, then it returns false. 9 | */ 10 | export function isTimeZone(value: unknown): boolean { 11 | try { 12 | if (typeof value !== 'string') { 13 | return false; 14 | } 15 | 16 | /** Specifying an invalid time-zone will raise a `RangeError: Invalid time zone specified` error. */ 17 | Intl.DateTimeFormat(undefined, { timeZone: value }); 18 | 19 | return true; 20 | } catch (exception) { 21 | return false; 22 | } 23 | } 24 | 25 | /** 26 | * Checks if the string represents a valid IANA timezone 27 | * If the given value is not a valid IANA timezone, then it returns false. 28 | */ 29 | export function IsTimeZone(validationOptions?: ValidationOptions): PropertyDecorator { 30 | return ValidateBy( 31 | { 32 | name: IS_TIMEZONE, 33 | validator: { 34 | validate: (value, args): boolean => isTimeZone(value), 35 | defaultMessage: buildMessage( 36 | eachPrefix => eachPrefix + '$property must be a valid IANA time-zone', 37 | validationOptions 38 | ), 39 | }, 40 | }, 41 | validationOptions 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/decorator/string/IsUUID.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isUuidValidator from 'validator/lib/isUUID'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_UUID = 'isUuid'; 7 | 8 | /** 9 | * Checks if the string is a UUID (version 3, 4 or 5). 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isUUID(value: unknown, version?: ValidatorJS.UUIDVersion): boolean { 13 | return typeof value === 'string' && isUuidValidator(value, version); 14 | } 15 | 16 | /** 17 | * Checks if the string is a UUID (version 3, 4 or 5). 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsUUID(version?: ValidatorJS.UUIDVersion, validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: IS_UUID, 24 | constraints: [version], 25 | validator: { 26 | validate: (value, args): boolean => isUUID(value, args?.constraints[0]), 27 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a UUID', validationOptions), 28 | }, 29 | }, 30 | validationOptions 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/decorator/string/IsUppercase.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isUppercaseValidator from 'validator/lib/isUppercase'; 4 | 5 | export const IS_UPPERCASE = 'isUppercase'; 6 | 7 | /** 8 | * Checks if the string is uppercase. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isUppercase(value: unknown): boolean { 12 | return typeof value === 'string' && isUppercaseValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string is uppercase. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsUppercase(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_UPPERCASE, 23 | validator: { 24 | validate: (value, args): boolean => isUppercase(value), 25 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be uppercase', validationOptions), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/string/IsUrl.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isUrlValidator from 'validator/lib/isURL'; 4 | import * as ValidatorJS from 'validator'; 5 | 6 | export const IS_URL = 'isUrl'; 7 | 8 | /** 9 | * Checks if the string is a url. 10 | * If given value is not a string, then it returns false. 11 | */ 12 | export function isURL(value: string, options?: ValidatorJS.IsURLOptions): boolean { 13 | return typeof value === 'string' && isUrlValidator(value, options); 14 | } 15 | 16 | /** 17 | * Checks if the string is a url. 18 | * If given value is not a string, then it returns false. 19 | */ 20 | export function IsUrl(options?: ValidatorJS.IsURLOptions, validationOptions?: ValidationOptions): PropertyDecorator { 21 | return ValidateBy( 22 | { 23 | name: IS_URL, 24 | constraints: [options], 25 | validator: { 26 | validate: (value, args): boolean => isURL(value, args?.constraints[0]), 27 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a URL address', validationOptions), 28 | }, 29 | }, 30 | validationOptions 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/decorator/string/IsVariableWidth.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isVariableWidthValidator from 'validator/lib/isVariableWidth'; 4 | 5 | export const IS_VARIABLE_WIDTH = 'isVariableWidth'; 6 | 7 | /** 8 | * Checks if the string contains variable-width chars. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function isVariableWidth(value: unknown): boolean { 12 | return typeof value === 'string' && isVariableWidthValidator(value); 13 | } 14 | 15 | /** 16 | * Checks if the string contains variable-width chars. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function IsVariableWidth(validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_VARIABLE_WIDTH, 23 | validator: { 24 | validate: (value, args): boolean => isVariableWidth(value), 25 | defaultMessage: buildMessage( 26 | eachPrefix => eachPrefix + '$property must contain a full-width and half-width characters', 27 | validationOptions 28 | ), 29 | }, 30 | }, 31 | validationOptions 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/decorator/string/Length.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isLengthValidator from 'validator/lib/isLength'; 4 | 5 | export const IS_LENGTH = 'isLength'; 6 | 7 | /** 8 | * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function length(value: unknown, min: number, max?: number): boolean { 12 | return typeof value === 'string' && isLengthValidator(value, { min, max }); 13 | } 14 | 15 | /** 16 | * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function Length(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: IS_LENGTH, 23 | constraints: [min, max], 24 | validator: { 25 | validate: (value, args): boolean => length(value, args?.constraints[0], args?.constraints[1]), 26 | defaultMessage: buildMessage((eachPrefix, args) => { 27 | const isMinLength = args?.constraints[0] !== null && args?.constraints[0] !== undefined; 28 | const isMaxLength = args?.constraints[1] !== null && args?.constraints[1] !== undefined; 29 | if (isMinLength && (!args.value || args.value.length < args?.constraints[0])) { 30 | return eachPrefix + '$property must be longer than or equal to $constraint1 characters'; 31 | } else if (isMaxLength && args.value.length > args?.constraints[1]) { 32 | return eachPrefix + '$property must be shorter than or equal to $constraint2 characters'; 33 | } 34 | return ( 35 | eachPrefix + 36 | '$property must be longer than or equal to $constraint1 and shorter than or equal to $constraint2 characters' 37 | ); 38 | }, validationOptions), 39 | }, 40 | }, 41 | validationOptions 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/decorator/string/Matches.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import matchesValidator from 'validator/lib/matches'; 4 | 5 | export const MATCHES = 'matches'; 6 | 7 | /** 8 | * Checks if string matches the pattern. Either matches('foo', /foo/i). 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function matches(value: string, pattern: RegExp): boolean; 12 | export function matches(value: string, pattern: string, modifiers: string): boolean; 13 | export function matches(value: string, pattern: RegExp | string, modifiers?: string): boolean { 14 | return typeof value === 'string' && matchesValidator(value, pattern as unknown as any, modifiers); 15 | } 16 | 17 | /** 18 | * Checks if string matches the pattern. Either matches('foo', /foo/i) 19 | * If given value is not a string, then it returns false. 20 | */ 21 | export function Matches(pattern: RegExp, validationOptions?: ValidationOptions): PropertyDecorator; 22 | export function Matches(pattern: string, modifiers?: string, validationOptions?: ValidationOptions): PropertyDecorator; 23 | export function Matches( 24 | pattern: RegExp | string, 25 | modifiersOrAnnotationOptions?: string | ValidationOptions, 26 | validationOptions?: ValidationOptions 27 | ): PropertyDecorator { 28 | let modifiers: string; 29 | if (modifiersOrAnnotationOptions && modifiersOrAnnotationOptions instanceof Object && !validationOptions) { 30 | validationOptions = modifiersOrAnnotationOptions; 31 | } else { 32 | modifiers = modifiersOrAnnotationOptions as string; 33 | } 34 | 35 | return ValidateBy( 36 | { 37 | name: MATCHES, 38 | constraints: [pattern, modifiers], 39 | validator: { 40 | validate: (value, args): boolean => matches(value, args?.constraints[0], args?.constraints[1]), 41 | defaultMessage: buildMessage( 42 | (eachPrefix, args) => eachPrefix + '$property must match $constraint1 regular expression', 43 | validationOptions 44 | ), 45 | }, 46 | }, 47 | validationOptions 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/decorator/string/MaxLength.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isLengthValidator from 'validator/lib/isLength'; 4 | 5 | export const MAX_LENGTH = 'maxLength'; 6 | 7 | /** 8 | * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function maxLength(value: unknown, max: number): boolean { 12 | return typeof value === 'string' && isLengthValidator(value, { min: 0, max }); 13 | } 14 | 15 | /** 16 | * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function MaxLength(max: number, validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: MAX_LENGTH, 23 | constraints: [max], 24 | validator: { 25 | validate: (value, args): boolean => maxLength(value, args?.constraints[0]), 26 | defaultMessage: buildMessage( 27 | eachPrefix => eachPrefix + '$property must be shorter than or equal to $constraint1 characters', 28 | validationOptions 29 | ), 30 | }, 31 | }, 32 | validationOptions 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/string/MinLength.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isLengthValidator from 'validator/lib/isLength'; 4 | 5 | export const MIN_LENGTH = 'minLength'; 6 | 7 | /** 8 | * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function minLength(value: unknown, min: number): boolean { 12 | return typeof value === 'string' && isLengthValidator(value, { min }); 13 | } 14 | 15 | /** 16 | * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function MinLength(min: number, validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: MIN_LENGTH, 23 | constraints: [min], 24 | validator: { 25 | validate: (value, args): boolean => minLength(value, args?.constraints[0]), 26 | defaultMessage: buildMessage( 27 | eachPrefix => eachPrefix + '$property must be longer than or equal to $constraint1 characters', 28 | validationOptions 29 | ), 30 | }, 31 | }, 32 | validationOptions 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/string/NotContains.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import containsValidator from 'validator/lib/contains'; 4 | 5 | export const NOT_CONTAINS = 'notContains'; 6 | 7 | /** 8 | * Checks if the string does not contain the seed. 9 | * If given value is not a string, then it returns false. 10 | */ 11 | export function notContains(value: unknown, seed: string): boolean { 12 | return typeof value === 'string' && !containsValidator(value, seed); 13 | } 14 | 15 | /** 16 | * Checks if the string does not contain the seed. 17 | * If given value is not a string, then it returns false. 18 | */ 19 | export function NotContains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { 20 | return ValidateBy( 21 | { 22 | name: NOT_CONTAINS, 23 | constraints: [seed], 24 | validator: { 25 | validate: (value, args): boolean => notContains(value, args?.constraints[0]), 26 | defaultMessage: buildMessage( 27 | eachPrefix => eachPrefix + '$property should not contain a $constraint1 string', 28 | validationOptions 29 | ), 30 | }, 31 | }, 32 | validationOptions 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/decorator/string/is-iso4217-currency-code.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isISO4217Validator from 'validator/lib/isISO4217'; 4 | 5 | export const IS_ISO4217_CURRENCY_CODE = 'isISO4217CurrencyCode'; 6 | 7 | /** 8 | * Check if the string is a valid [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) officially assigned currency code. 9 | */ 10 | export function isISO4217CurrencyCode(value: unknown): boolean { 11 | return typeof value === 'string' && isISO4217Validator(value); 12 | } 13 | 14 | /** 15 | * Check if the string is a valid [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) officially assigned currency code. 16 | */ 17 | export function IsISO4217CurrencyCode(validationOptions?: ValidationOptions): PropertyDecorator { 18 | return ValidateBy( 19 | { 20 | name: IS_ISO4217_CURRENCY_CODE, 21 | validator: { 22 | validate: (value, args): boolean => isISO4217CurrencyCode(value), 23 | defaultMessage: buildMessage( 24 | eachPrefix => eachPrefix + '$property must be a valid ISO4217 currency code', 25 | validationOptions 26 | ), 27 | }, 28 | }, 29 | validationOptions 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/decorator/string/is-tax-id.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | import isTaxIDValidator from 'validator/lib/isTaxID'; 4 | 5 | export const IS_TAX_ID = 'isTaxId'; 6 | 7 | /** 8 | * Checks if the string is a valid tax ID. Default locale is `en-US`. 9 | * If given value is not a string, then it returns false. 10 | * 11 | * Supported locales: bg-BG, cs-CZ, de-AT, de-DE, dk-DK, el-CY, el-GR, en-CA, 12 | * en-IE, en-US, es-ES, et-EE, fi-FI, fr-BE, fr-FR, fr-LU, hr-HR, hu-HU, it-IT, 13 | * lv-LV, mt-MT, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, sk-SK, sl-SI, sv-SE. 14 | */ 15 | export function isTaxId(value: unknown, locale?: string): boolean { 16 | return typeof value === 'string' && isTaxIDValidator(value, locale || 'en-US'); 17 | } 18 | 19 | /** 20 | * Checks if the string is a valid tax ID. Default locale is `en-US`. 21 | * If given value is not a string, then it returns false. 22 | * 23 | * Supported locales: bg-BG, cs-CZ, de-AT, de-DE, dk-DK, el-CY, el-GR, en-CA, 24 | * en-IE, en-US, es-ES, et-EE, fi-FI, fr-BE, fr-FR, fr-LU, hr-HR, hu-HU, it-IT, 25 | * lv-LV, mt-MT, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, sk-SK, sl-SI, sv-SE. 26 | */ 27 | export function IsTaxId(locale?: string, validationOptions?: ValidationOptions): PropertyDecorator { 28 | return ValidateBy( 29 | { 30 | name: IS_TAX_ID, 31 | constraints: [locale], 32 | validator: { 33 | validate: (value, args): boolean => isTaxId(value, args?.constraints[0]), 34 | defaultMessage: buildMessage( 35 | eachPrefix => eachPrefix + '$property must be a Tax Identification Number', 36 | validationOptions 37 | ), 38 | }, 39 | }, 40 | validationOptions 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsArray.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_ARRAY = 'isArray'; 5 | 6 | /** 7 | * Checks if a given value is an array 8 | */ 9 | export function isArray(value: unknown): value is Array { 10 | return Array.isArray(value); 11 | } 12 | 13 | /** 14 | * Checks if a given value is an array 15 | */ 16 | export function IsArray(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_ARRAY, 20 | validator: { 21 | validate: (value, args): boolean => isArray(value), 22 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an array', validationOptions), 23 | }, 24 | }, 25 | validationOptions 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsBoolean.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_BOOLEAN = 'isBoolean'; 5 | 6 | /** 7 | * Checks if a given value is a boolean. 8 | */ 9 | export function isBoolean(value: unknown): value is boolean { 10 | return value instanceof Boolean || typeof value === 'boolean'; 11 | } 12 | 13 | /** 14 | * Checks if a value is a boolean. 15 | */ 16 | export function IsBoolean(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_BOOLEAN, 20 | validator: { 21 | validate: (value, args): boolean => isBoolean(value), 22 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a boolean value', validationOptions), 23 | }, 24 | }, 25 | validationOptions 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsDate.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_DATE = 'isDate'; 5 | 6 | /** 7 | * Checks if a given value is a date. 8 | */ 9 | export function isDate(value: unknown): value is Date { 10 | return value instanceof Date && !isNaN(value.getTime()); 11 | } 12 | 13 | /** 14 | * Checks if a value is a date. 15 | */ 16 | export function IsDate(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_DATE, 20 | validator: { 21 | validate: (value, args): boolean => isDate(value), 22 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a Date instance', validationOptions), 23 | }, 24 | }, 25 | validationOptions 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsEnum.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_ENUM = 'isEnum'; 5 | 6 | /** 7 | * Checks if a given value is the member of the provided enum. 8 | */ 9 | export function isEnum(value: unknown, entity: any): boolean { 10 | const enumValues = Object.keys(entity).map(k => entity[k]); 11 | return enumValues.includes(value); 12 | } 13 | 14 | /** 15 | * Returns the possible values from an enum (both simple number indexed and string indexed enums). 16 | */ 17 | function validEnumValues(entity: any): string[] { 18 | return Object.entries(entity) 19 | .filter(([key, value]) => isNaN(parseInt(key))) 20 | .map(([key, value]) => value as string); 21 | } 22 | 23 | /** 24 | * Checks if a given value is the member of the provided enum. 25 | */ 26 | export function IsEnum(entity: object, validationOptions?: ValidationOptions): PropertyDecorator { 27 | return ValidateBy( 28 | { 29 | name: IS_ENUM, 30 | constraints: [entity, validEnumValues(entity)], 31 | validator: { 32 | validate: (value, args): boolean => isEnum(value, args?.constraints[0]), 33 | defaultMessage: buildMessage( 34 | eachPrefix => eachPrefix + '$property must be one of the following values: $constraint2', 35 | validationOptions 36 | ), 37 | }, 38 | }, 39 | validationOptions 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsInt.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_INT = 'isInt'; 5 | 6 | /** 7 | * Checks if value is an integer. 8 | */ 9 | export function isInt(val: unknown): val is Number { 10 | return typeof val === 'number' && Number.isInteger(val); 11 | } 12 | 13 | /** 14 | * Checks if value is an integer. 15 | */ 16 | export function IsInt(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_INT, 20 | validator: { 21 | validate: (value, args): boolean => isInt(value), 22 | defaultMessage: buildMessage( 23 | eachPrefix => eachPrefix + '$property must be an integer number', 24 | validationOptions 25 | ), 26 | }, 27 | }, 28 | validationOptions 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsNumber.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_NUMBER = 'isNumber'; 5 | 6 | /** 7 | * Options to be passed to IsNumber decorator. 8 | */ 9 | export interface IsNumberOptions { 10 | allowNaN?: boolean; 11 | allowInfinity?: boolean; 12 | maxDecimalPlaces?: number; 13 | } 14 | 15 | /** 16 | * Checks if a given value is a number. 17 | */ 18 | export function isNumber(value: unknown, options: IsNumberOptions = {}): value is number { 19 | if (typeof value !== 'number') { 20 | return false; 21 | } 22 | 23 | if (value === Infinity || value === -Infinity) { 24 | return !!options.allowInfinity; 25 | } 26 | 27 | if (Number.isNaN(value)) { 28 | return !!options.allowNaN; 29 | } 30 | 31 | if (options.maxDecimalPlaces !== undefined) { 32 | let decimalPlaces = 0; 33 | if (value % 1 !== 0) { 34 | decimalPlaces = value.toString().split('.')[1].length; 35 | } 36 | if (decimalPlaces > options.maxDecimalPlaces) { 37 | return false; 38 | } 39 | } 40 | 41 | return Number.isFinite(value); 42 | } 43 | 44 | /** 45 | * Checks if a value is a number. 46 | */ 47 | export function IsNumber(options: IsNumberOptions = {}, validationOptions?: ValidationOptions): PropertyDecorator { 48 | return ValidateBy( 49 | { 50 | name: IS_NUMBER, 51 | constraints: [options], 52 | validator: { 53 | validate: (value, args): boolean => isNumber(value, args?.constraints[0]), 54 | defaultMessage: buildMessage( 55 | eachPrefix => eachPrefix + '$property must be a number conforming to the specified constraints', 56 | validationOptions 57 | ), 58 | }, 59 | }, 60 | validationOptions 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsObject.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_OBJECT = 'isObject'; 5 | 6 | /** 7 | * Checks if the value is valid Object. 8 | * Returns false if the value is not an object. 9 | */ 10 | export function isObject(value: unknown): value is T { 11 | return value != null && (typeof value === 'object' || typeof value === 'function') && !Array.isArray(value); 12 | } 13 | 14 | /** 15 | * Checks if the value is valid Object. 16 | * Returns false if the value is not an object. 17 | */ 18 | export function IsObject(validationOptions?: ValidationOptions): PropertyDecorator { 19 | return ValidateBy( 20 | { 21 | name: IS_OBJECT, 22 | validator: { 23 | validate: (value, args): boolean => isObject(value), 24 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an object', validationOptions), 25 | }, 26 | }, 27 | validationOptions 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/decorator/typechecker/IsString.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../ValidationOptions'; 2 | import { buildMessage, ValidateBy } from '../common/ValidateBy'; 3 | 4 | export const IS_STRING = 'isString'; 5 | 6 | /** 7 | * Checks if a given value is a real string. 8 | */ 9 | export function isString(value: unknown): value is string { 10 | return value instanceof String || typeof value === 'string'; 11 | } 12 | 13 | /** 14 | * Checks if a given value is a real string. 15 | */ 16 | export function IsString(validationOptions?: ValidationOptions): PropertyDecorator { 17 | return ValidateBy( 18 | { 19 | name: IS_STRING, 20 | validator: { 21 | validate: (value, args): boolean => isString(value), 22 | defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a string', validationOptions), 23 | }, 24 | }, 25 | validationOptions 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/metadata/ConstraintMetadata.ts: -------------------------------------------------------------------------------- 1 | import { ValidatorConstraintInterface } from '../validation/ValidatorConstraintInterface'; 2 | import { getFromContainer } from '../container'; 3 | 4 | /** 5 | * This metadata interface contains information for custom validators. 6 | */ 7 | export class ConstraintMetadata { 8 | // ------------------------------------------------------------------------- 9 | // Properties 10 | // ------------------------------------------------------------------------- 11 | 12 | /** 13 | * Target class which performs validation. 14 | */ 15 | target: Function; 16 | 17 | /** 18 | * Custom validation's name, that will be used as validation error type. 19 | */ 20 | name: string; 21 | 22 | /** 23 | * Indicates if this validation is asynchronous or not. 24 | */ 25 | async: boolean; 26 | 27 | // ------------------------------------------------------------------------- 28 | // Constructor 29 | // ------------------------------------------------------------------------- 30 | 31 | constructor(target: Function, name?: string, async: boolean = false) { 32 | this.target = target; 33 | this.name = name; 34 | this.async = async; 35 | } 36 | 37 | // ------------------------------------------------------------------------- 38 | // Accessors 39 | // ------------------------------------------------------------------------- 40 | 41 | /** 42 | * Instance of the target custom validation class which performs validation. 43 | */ 44 | get instance(): ValidatorConstraintInterface { 45 | return getFromContainer(this.target); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/metadata/ValidationMetadata.ts: -------------------------------------------------------------------------------- 1 | import { ValidationMetadataArgs } from './ValidationMetadataArgs'; 2 | import { ValidationArguments } from '../validation/ValidationArguments'; 3 | 4 | /** 5 | * This metadata contains validation rules. 6 | */ 7 | export class ValidationMetadata { 8 | // ------------------------------------------------------------------------- 9 | // Properties 10 | // ------------------------------------------------------------------------- 11 | 12 | /** 13 | * Validation type. 14 | */ 15 | type: string; 16 | 17 | /** 18 | * Validator name. 19 | */ 20 | name?: string; 21 | 22 | /** 23 | * Target class to which this validation is applied. 24 | */ 25 | target: Function | string; 26 | 27 | /** 28 | * Property of the object to be validated. 29 | */ 30 | propertyName: string; 31 | 32 | /** 33 | * Constraint class that performs validation. Used only for custom validations. 34 | */ 35 | constraintCls: Function; 36 | 37 | /** 38 | * Array of constraints of this validation. 39 | */ 40 | constraints: any[]; 41 | 42 | /** 43 | * Validation message to be shown in the case of error. 44 | */ 45 | message: string | ((args: ValidationArguments) => string); 46 | 47 | /** 48 | * Validation groups used for this validation. 49 | */ 50 | groups: string[] = []; 51 | 52 | /** 53 | * Indicates if validation must be performed always, no matter of validation groups used. 54 | */ 55 | always?: boolean; 56 | 57 | /** 58 | * Specifies if validated value is an array and each of its item must be validated. 59 | */ 60 | each: boolean = false; 61 | 62 | /* 63 | * A transient set of data passed through to the validation result for response mapping 64 | */ 65 | context?: any = undefined; 66 | 67 | /** 68 | * Extra options specific to validation type. 69 | */ 70 | validationTypeOptions: any; 71 | 72 | // ------------------------------------------------------------------------- 73 | // Constructor 74 | // ------------------------------------------------------------------------- 75 | 76 | constructor(args: ValidationMetadataArgs) { 77 | this.type = args.type; 78 | this.name = args.name; 79 | this.target = args.target; 80 | this.propertyName = args.propertyName; 81 | this.constraints = args?.constraints; 82 | this.constraintCls = args.constraintCls; 83 | this.validationTypeOptions = args.validationTypeOptions; 84 | if (args.validationOptions) { 85 | this.message = args.validationOptions.message; 86 | this.groups = args.validationOptions.groups; 87 | this.always = args.validationOptions.always; 88 | this.each = args.validationOptions.each; 89 | this.context = args.validationOptions.context; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/metadata/ValidationMetadataArgs.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOptions } from '../decorator/ValidationOptions'; 2 | 3 | /** 4 | * Constructor arguments for ValidationMetadata class. 5 | */ 6 | export interface ValidationMetadataArgs { 7 | /** 8 | * Validation type. 9 | */ 10 | type: string; 11 | 12 | /** 13 | * Validator name. 14 | */ 15 | name?: string; 16 | 17 | /** 18 | * Object that is used to be validated. 19 | */ 20 | target: Function | string; 21 | 22 | /** 23 | * Property of the object to be validated. 24 | */ 25 | propertyName: string; 26 | 27 | /** 28 | * Constraint class that performs validation. Used only for custom validations. 29 | */ 30 | constraintCls?: Function; 31 | 32 | /** 33 | * Array of constraints of this validation. 34 | */ 35 | constraints?: any[]; 36 | 37 | /** 38 | * Validation options. 39 | */ 40 | validationOptions?: ValidationOptions; 41 | 42 | /** 43 | * Extra options specific to validation type. 44 | */ 45 | validationTypeOptions?: any; 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/convert-to-array.util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert Map, Set to Array 3 | */ 4 | export function convertToArray(val: Array | Set | Map): Array { 5 | if (val instanceof Map) { 6 | return Array.from(val.values()); 7 | } 8 | return Array.isArray(val) ? val : Array.from(val); 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/get-global.util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This function returns the global object across Node and browsers. 3 | * 4 | * Note: `globalThis` is the standardized approach however it has been added to 5 | * Node.js in version 12. We need to include this snippet until Node 12 EOL. 6 | */ 7 | export function getGlobal() { 8 | if (typeof globalThis !== 'undefined') { 9 | return globalThis; 10 | } 11 | 12 | if (typeof global !== 'undefined') { 13 | return global; 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 17 | // @ts-ignore: Cannot find name 'window'. 18 | if (typeof window !== 'undefined') { 19 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 20 | // @ts-ignore: Cannot find name 'window'. 21 | return window; 22 | } 23 | 24 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 25 | // @ts-ignore: Cannot find name 'self'. 26 | if (typeof self !== 'undefined') { 27 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 28 | // @ts-ignore: Cannot find name 'self'. 29 | return self; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './convert-to-array.util'; 2 | export * from './get-global.util'; 3 | export * from './is-promise.util'; 4 | -------------------------------------------------------------------------------- /src/utils/is-promise.util.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/TylorS/typed-is-promise/blob/abf1514e1b6961adfc75765476b0debb96b2c3ae/src/index.ts 2 | 3 | export function isPromise(p: any): p is Promise { 4 | return p !== null && typeof p === 'object' && typeof p.then === 'function'; 5 | } 6 | -------------------------------------------------------------------------------- /src/validation-schema/ValidationSchema.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Validation schema is a decorator-free way of validation of your objects. 3 | * Also using validation schemas makes this library to be easily used with es6/es5. 4 | */ 5 | export interface ValidationSchema { 6 | /** 7 | * Schema name. This is required, because we tell validator to validate by this schema using its name. 8 | */ 9 | name: string; 10 | 11 | /** 12 | * Validated properties. 13 | */ 14 | properties: { 15 | /** 16 | * Name of the object's property to be validated which holds an array of validation constraints. 17 | */ 18 | [propertyName: string]: { 19 | /** 20 | * Validation type. Should be one of the ValidationTypes value. 21 | */ 22 | type: string; 23 | 24 | /** 25 | * Validator name. 26 | */ 27 | name?: string; 28 | 29 | /** 30 | * Constraints set by validation type. 31 | */ 32 | constraints?: any[]; 33 | 34 | /** 35 | * Error message used to be used on validation fail. 36 | * You can use "$value" to use value that was failed by validation. 37 | * You can use "$constraint1" and "$constraint2" keys in the message string, 38 | * and they will be replaced with constraint values if they exist. 39 | * Message can be either string, either a function that returns a string. 40 | * Second option allows to use values and custom messages depend of them. 41 | */ 42 | message?: string | ((value?: any, constraint1?: any, constraint2?: any) => string); 43 | 44 | /** 45 | * Specifies if validated value is an array and each of its item must be validated. 46 | */ 47 | each?: boolean; 48 | 49 | /** 50 | * Indicates if validation must be performed always, no matter of validation groups used. 51 | */ 52 | always?: boolean; 53 | 54 | /** 55 | * Validation groups used for this validation. 56 | */ 57 | groups?: string[]; 58 | 59 | /** 60 | * Specific validation type options. 61 | */ 62 | options?: any; 63 | }[]; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/validation-schema/ValidationSchemaToMetadataTransformer.ts: -------------------------------------------------------------------------------- 1 | import { ValidationSchema } from './ValidationSchema'; 2 | import { ValidationMetadata } from '../metadata/ValidationMetadata'; 3 | import { ValidationMetadataArgs } from '../metadata/ValidationMetadataArgs'; 4 | import { ValidationOptions } from '../decorator/ValidationOptions'; 5 | 6 | /** 7 | * Used to transform validation schemas to validation metadatas. 8 | */ 9 | export class ValidationSchemaToMetadataTransformer { 10 | transform(schema: ValidationSchema): ValidationMetadata[] { 11 | const metadatas: ValidationMetadata[] = []; 12 | Object.keys(schema.properties).forEach(property => { 13 | schema.properties[property].forEach(validation => { 14 | const validationOptions: ValidationOptions = { 15 | message: validation.message, 16 | groups: validation.groups, 17 | always: validation.always, 18 | each: validation.each, 19 | }; 20 | const args: ValidationMetadataArgs = { 21 | type: validation.type, 22 | name: validation.name, 23 | target: schema.name, 24 | propertyName: property, 25 | constraints: validation.constraints, 26 | validationTypeOptions: validation.options, 27 | validationOptions: validationOptions, 28 | }; 29 | metadatas.push(new ValidationMetadata(args)); 30 | }); 31 | }); 32 | return metadatas; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/validation/ValidationArguments.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Arguments being sent to message builders - user can create message either by simply returning a string, 3 | * either by returning a function that accepts MessageArguments and returns a message string built based on these arguments. 4 | */ 5 | export interface ValidationArguments { 6 | /** 7 | * Validating value. 8 | */ 9 | value: any; 10 | 11 | /** 12 | * Constraints set by this validation type. 13 | */ 14 | constraints: any[]; 15 | 16 | /** 17 | * Name of the target that is being validated. 18 | */ 19 | targetName: string; 20 | 21 | /** 22 | * Object that is being validated. 23 | */ 24 | object: object; 25 | 26 | /** 27 | * Name of the object's property being validated. 28 | */ 29 | property: string; 30 | } 31 | -------------------------------------------------------------------------------- /src/validation/ValidationTypes.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Validation types. 3 | */ 4 | export class ValidationTypes { 5 | /* system */ 6 | static CUSTOM_VALIDATION = 'customValidation'; // done 7 | static NESTED_VALIDATION = 'nestedValidation'; // done 8 | static PROMISE_VALIDATION = 'promiseValidation'; // done 9 | static CONDITIONAL_VALIDATION = 'conditionalValidation'; // done 10 | static WHITELIST = 'whitelistValidation'; // done 11 | static IS_DEFINED = 'isDefined'; // done 12 | 13 | /** 14 | * Checks if validation type is valid. 15 | */ 16 | static isValid(type: string): boolean { 17 | return ( 18 | type !== 'isValid' && 19 | type !== 'getMessage' && 20 | Object.keys(this) 21 | .map(key => (this as any)[key]) 22 | .indexOf(type) !== -1 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/validation/ValidationUtils.ts: -------------------------------------------------------------------------------- 1 | import { ValidationArguments } from './ValidationArguments'; 2 | 3 | /** 4 | * Convert the constraint to a string to be shown in an error 5 | */ 6 | export function constraintToString(constraint: unknown): string { 7 | if (Array.isArray(constraint)) { 8 | return constraint.join(', '); 9 | } 10 | 11 | if (typeof constraint === 'symbol') { 12 | constraint = constraint.description; 13 | } 14 | 15 | return `${constraint}`; 16 | } 17 | 18 | export class ValidationUtils { 19 | static replaceMessageSpecialTokens( 20 | message: string | ((args: ValidationArguments) => string), 21 | validationArguments: ValidationArguments 22 | ): string { 23 | let messageString: string; 24 | if (message instanceof Function) { 25 | messageString = (message as (args: ValidationArguments) => string)(validationArguments); 26 | } else if (typeof message === 'string') { 27 | messageString = message; 28 | } 29 | 30 | if (messageString && Array.isArray(validationArguments.constraints)) { 31 | validationArguments.constraints.forEach((constraint, index) => { 32 | messageString = messageString.replace( 33 | new RegExp(`\\$constraint${index + 1}`, 'g'), 34 | constraintToString(constraint) 35 | ); 36 | }); 37 | } 38 | 39 | if ( 40 | messageString && 41 | validationArguments.value !== undefined && 42 | validationArguments.value !== null && 43 | ['string', 'boolean', 'number'].includes(typeof validationArguments.value) 44 | ) 45 | messageString = messageString.replace(/\$value/g, validationArguments.value); 46 | if (messageString) messageString = messageString.replace(/\$property/g, validationArguments.property); 47 | if (messageString) messageString = messageString.replace(/\$target/g, validationArguments.targetName); 48 | 49 | return messageString; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/validation/ValidatorConstraintInterface.ts: -------------------------------------------------------------------------------- 1 | import { ValidationArguments } from './ValidationArguments'; 2 | /** 3 | * Custom validators must implement this interface to provide custom validation logic. 4 | */ 5 | export interface ValidatorConstraintInterface { 6 | /** 7 | * Method to be called to perform custom validation over given value. 8 | */ 9 | validate(value: any, validationArguments?: ValidationArguments): Promise | boolean; 10 | 11 | /** 12 | * Gets default message when validation for this constraint fail. 13 | */ 14 | defaultMessage?(validationArguments?: ValidationArguments): string; 15 | } 16 | -------------------------------------------------------------------------------- /src/validation/ValidatorOptions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Options passed to validator during validation. 3 | */ 4 | export interface ValidatorOptions { 5 | /** 6 | * If set to true then class-validator will print extra warning messages to the console when something is not right. 7 | */ 8 | enableDebugMessages?: boolean; 9 | 10 | /** 11 | * If set to true then validator will skip validation of all properties that are undefined in the validating object. 12 | */ 13 | skipUndefinedProperties?: boolean; 14 | 15 | /** 16 | * If set to true then validator will skip validation of all properties that are null in the validating object. 17 | */ 18 | skipNullProperties?: boolean; 19 | 20 | /** 21 | * If set to true then validator will skip validation of all properties that are null or undefined in the validating object. 22 | */ 23 | skipMissingProperties?: boolean; 24 | 25 | /** 26 | * If set to true validator will strip validated object of any properties that do not have any decorators. 27 | * 28 | * Tip: if no other decorator is suitable for your property use @Allow decorator. 29 | */ 30 | whitelist?: boolean; 31 | 32 | /** 33 | * If set to true, instead of stripping non-whitelisted properties validator will throw an error 34 | */ 35 | forbidNonWhitelisted?: boolean; 36 | 37 | /** 38 | * Groups to be used during validation of the object. 39 | */ 40 | groups?: string[]; 41 | 42 | /** 43 | * Set default for `always` option of decorators. Default can be overridden in decorator options. 44 | */ 45 | always?: boolean; 46 | 47 | /** 48 | * If [groups]{@link ValidatorOptions#groups} is not given or is empty, 49 | * ignore decorators with at least one group. 50 | */ 51 | strictGroups?: boolean; 52 | 53 | /** 54 | * If set to true, the validation will not use default messages. 55 | * Error message always will be undefined if its not explicitly set. 56 | */ 57 | dismissDefaultMessages?: boolean; 58 | 59 | /** 60 | * ValidationError special options. 61 | */ 62 | validationError?: { 63 | /** 64 | * Indicates if target should be exposed in ValidationError. 65 | */ 66 | target?: boolean; 67 | 68 | /** 69 | * Indicates if validated value should be exposed in ValidationError. 70 | */ 71 | value?: boolean; 72 | }; 73 | 74 | /** 75 | * Fails validation for objects unknown to class-validator. Defaults to true. 76 | * 77 | * For instance, since a plain empty object has no annotations used for validation: 78 | * - `validate({})` // fails. 79 | * - `validate({}, { forbidUnknownValues: true })` // fails. 80 | * - `validate({}, { forbidUnknownValues: false })` // passes. 81 | * - `validate(new SomeAnnotatedEmptyClass(), { forbidUnknownValues: true })` // passes. 82 | */ 83 | forbidUnknownValues?: boolean; 84 | 85 | /** 86 | * When set to true, validation of the given property will stop after encountering the first error. Defaults to false. 87 | */ 88 | stopAtFirstError?: boolean; 89 | } 90 | -------------------------------------------------------------------------------- /test/functional/inherited-validation.spec.ts: -------------------------------------------------------------------------------- 1 | import { Contains, MinLength } from '../../src/decorator/decorators'; 2 | import { Validator } from '../../src/validation/Validator'; 3 | 4 | const validator = new Validator(); 5 | 6 | describe('inherited validation', () => { 7 | it('should validate inherited properties', () => { 8 | expect.assertions(9); 9 | 10 | class MyClass { 11 | @Contains('hello') 12 | title: string; 13 | } 14 | 15 | class MySubClass extends MyClass { 16 | @MinLength(5) 17 | name: string; 18 | } 19 | 20 | const model = new MySubClass(); 21 | model.title = 'helo world'; 22 | model.name = 'my'; 23 | return validator.validate(model).then(errors => { 24 | expect(errors.length).toEqual(2); 25 | // subclass own props are validated first 26 | expect(errors[0].target).toEqual(model); 27 | expect(errors[0].property).toEqual('name'); 28 | expect(errors[0].constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); 29 | expect(errors[0].value).toEqual('my'); 30 | // parent props are validated afterwards 31 | expect(errors[1].target).toEqual(model); 32 | expect(errors[1].property).toEqual('title'); 33 | expect(errors[1].constraints).toEqual({ contains: 'title must contain a hello string' }); 34 | expect(errors[1].value).toEqual('helo world'); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/functional/reject-validation.spec.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from './../../src/validation/ValidationError'; 2 | import { Contains } from '../../src/decorator/decorators'; 3 | import { Validator } from '../../src/validation/Validator'; 4 | 5 | class MyClass { 6 | @Contains('hello', { 7 | message: '$value is not valid. Your string must contain a hello word', 8 | }) 9 | someProperty: string; 10 | } 11 | 12 | describe('validateOrReject()', () => { 13 | let validator: Validator; 14 | let model: MyClass; 15 | 16 | beforeEach(() => { 17 | validator = new Validator(); 18 | model = new MyClass(); 19 | }); 20 | 21 | it('should resolve promise when no error', () => { 22 | expect.assertions(1); 23 | model.someProperty = 'hello world'; 24 | return validator.validateOrReject(model).then(args => { 25 | expect(args).toBeUndefined(); 26 | }); 27 | }); 28 | 29 | it('should reject promise on error', () => { 30 | expect.assertions(2); 31 | model.someProperty = 'hell no world'; 32 | return validator.validateOrReject(model).catch((errors: ValidationError[]) => { 33 | expect(errors.length).toEqual(1); 34 | expect(errors[0].constraints).toEqual({ 35 | contains: 'hell no world is not valid. Your string must contain a hello word', 36 | }); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/functional/sync-validation.spec.ts: -------------------------------------------------------------------------------- 1 | import { Validator } from '../../src/validation/Validator'; 2 | import { ValidationArguments } from '../../src/validation/ValidationArguments'; 3 | import { registerDecorator } from '../../src/register-decorator'; 4 | import { ValidationOptions } from '../../src/decorator/ValidationOptions'; 5 | import { ValidatorConstraint, Validate, IsNotEmpty } from '../../src/decorator/decorators'; 6 | import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface'; 7 | 8 | const validator = new Validator(); 9 | 10 | describe('sync validation should ignore async validation constraints', () => { 11 | @ValidatorConstraint({ name: 'isShortenThan', async: true }) 12 | class IsShortenThanConstraint implements ValidatorConstraintInterface { 13 | validate(value: any, args: ValidationArguments): Promise { 14 | return Promise.resolve(false); 15 | } 16 | } 17 | 18 | function IsLonger(property: string, validationOptions?: ValidationOptions) { 19 | return function (object: object, propertyName: string): void { 20 | registerDecorator({ 21 | target: object.constructor, 22 | propertyName: propertyName, 23 | options: validationOptions, 24 | constraints: [property], 25 | async: true, 26 | name: 'isLonger', 27 | validator: { 28 | validate(value: any, args: ValidationArguments): Promise { 29 | return Promise.resolve(false); 30 | }, 31 | }, 32 | }); 33 | }; 34 | } 35 | 36 | class SecondClass { 37 | @IsLonger('lastName') 38 | firstName: string; 39 | 40 | @Validate(IsShortenThanConstraint) 41 | lastName: string; 42 | 43 | @IsNotEmpty({ message: 'name should not be empty' }) 44 | name: string; 45 | 46 | @IsNotEmpty() 47 | alwaysWithValue: string = 'this field always has a value'; 48 | } 49 | 50 | it('should ignore async validations and validate only sync validation types', () => { 51 | expect.assertions(1); 52 | const model = new SecondClass(); 53 | model.firstName = 'such validation may lead'; 54 | model.firstName = 'to recursion'; 55 | model.name = 'Umed'; 56 | const errors = validator.validateSync(model); 57 | expect(errors.length).toEqual(0); 58 | }); 59 | 60 | it('should ignore async validations and validate only sync validation types', () => { 61 | expect.assertions(2); 62 | const model = new SecondClass(); 63 | model.firstName = 'such validation may lead'; 64 | model.firstName = 'to recursion'; 65 | model.name = ''; 66 | const errors = validator.validateSync(model); 67 | expect(errors.length).toEqual(1); 68 | expect(errors[0].constraints).toEqual({ isNotEmpty: 'name should not be empty' }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/functional/whitelist-validation.spec.ts: -------------------------------------------------------------------------------- 1 | import { Allow, IsDefined, IsOptional, Min } from '../../src/decorator/decorators'; 2 | import { Validator } from '../../src/validation/Validator'; 3 | import { ValidationTypes } from '../../src'; 4 | 5 | const validator = new Validator(); 6 | 7 | describe('whitelist validation', () => { 8 | it('should strip non whitelisted properties, but leave whitelisted untouched', () => { 9 | class MyClass { 10 | @IsDefined() 11 | title: string; 12 | 13 | @Min(0) 14 | views: number; 15 | } 16 | 17 | const model: any = new MyClass(); 18 | 19 | model.title = 'hello'; 20 | model.views = 56; 21 | model.unallowedProperty = 42; 22 | return validator.validate(model, { whitelist: true }).then(errors => { 23 | expect(errors.length).toEqual(0); 24 | expect(model.unallowedProperty).toBeUndefined(); 25 | expect(model.title).toEqual('hello'); 26 | expect(model.views).toEqual(56); 27 | }); 28 | }); 29 | 30 | it('should be able to whitelist with @Allow', () => { 31 | class MyClass { 32 | @Allow() 33 | views: number; 34 | } 35 | 36 | const model: any = new MyClass(); 37 | 38 | model.views = 420; 39 | model.unallowedProperty = 'non-whitelisted'; 40 | 41 | return validator.validate(model, { whitelist: true }).then(errors => { 42 | expect(errors.length).toEqual(0); 43 | expect(model.unallowedProperty).toBeUndefined(); 44 | expect(model.views).toEqual(420); 45 | }); 46 | }); 47 | 48 | it('should throw an error when forbidNonWhitelisted flag is set', () => { 49 | class MyPayload { 50 | /** 51 | * Since forbidUnknownValues defaults to true, we must add a property to 52 | * register the class in the metadata storage. Otherwise the unknown value check 53 | * would take priority (first check) and exit without running the whitelist logic. 54 | */ 55 | @IsOptional() 56 | propertyToRegisterClass: string; 57 | 58 | nonDecorated: string; 59 | 60 | constructor(nonDecorated: string) { 61 | this.nonDecorated = nonDecorated; 62 | } 63 | } 64 | 65 | const instance = new MyPayload('non-whitelisted'); 66 | 67 | return validator.validate(instance, { whitelist: true, forbidNonWhitelisted: true }).then(errors => { 68 | expect(errors.length).toEqual(1); 69 | expect(errors[0].target).toEqual(instance); 70 | expect(errors[0].property).toEqual('nonDecorated'); 71 | expect(errors[0].constraints).toHaveProperty(ValidationTypes.WHITELIST); 72 | expect(() => errors[0].toString()).not.toThrow(); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { convertToArray } from '../src/utils'; 2 | 3 | describe('convertToArray', () => { 4 | it('convert Set into array', () => { 5 | const setExample = new Set(); 6 | setExample.add('hello'); 7 | setExample.add('world'); 8 | const newArr = convertToArray(setExample); 9 | expect(newArr).toBeInstanceOf(Array); 10 | expect(newArr.length).toEqual(2); 11 | expect(newArr).toContain('hello'); 12 | expect(newArr).toContain('world'); 13 | }); 14 | 15 | it('convert Map into array of values', () => { 16 | const map = new Map(); 17 | map.set('key1', 'hello'); 18 | map.set('key2', 'world'); 19 | const newArr = convertToArray(map); 20 | expect(newArr).toBeInstanceOf(Array); 21 | expect(newArr.length).toEqual(2); 22 | expect(newArr).toContain('hello'); 23 | expect(newArr).toContain('world'); 24 | }); 25 | 26 | it('should return array untouched', () => { 27 | const arr = ['hello', 'world']; 28 | expect(arr).toBeInstanceOf(Array); 29 | expect(arr.length).toEqual(2); 30 | expect(arr).toContain('hello'); 31 | expect(arr).toContain('world'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "target": "es2018", 6 | "lib": ["es2018"], 7 | "outDir": "build/node", 8 | "rootDir": "./src", 9 | "strict": true, 10 | "sourceMap": true, 11 | "inlineSources": true, 12 | "removeComments": false, 13 | "esModuleInterop": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true, 16 | "forceConsistentCasingInFileNames": true 17 | }, 18 | "exclude": ["build", "node_modules", "sample", "**/*.spec.ts", "test/**"] 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.prod.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.prod.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "outDir": "build/cjs" 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.prod.esm2015.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.prod.json", 3 | "compilerOptions": { 4 | "module": "ES2015", 5 | "outDir": "build/esm2015", 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.prod.esm5.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.prod.json", 3 | "compilerOptions": { 4 | "module": "ES2015", 5 | "target": "ES5", 6 | "outDir": "build/esm5", 7 | "downlevelIteration": true, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "strict": false, 5 | "declaration": false, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.prod.types.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.prod.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "emitDeclarationOnly": true, 6 | "outDir": "build/types", 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "strict": false, 5 | "strictPropertyInitialization": false, 6 | "sourceMap": false, 7 | "removeComments": true, 8 | "noImplicitAny": false, 9 | }, 10 | "exclude": ["node_modules"] 11 | } 12 | --------------------------------------------------------------------------------