From 11a7b8bb59c83d55bc723ebb236fdca912f49d88 Mon Sep 17 00:00:00 2001 From: Vlad Poluch Date: Fri, 17 Apr 2020 23:22:13 +0200 Subject: [PATCH] refactor: update build process to enable tree shaking (#568) BREAKING CHANGE: Validation functions was removed from `Validator` class to enable tree shaking. BEFORE: import {Validator} from "class-validator"; const validator = new Validator(); validator.isNotIn(value, possibleValues); validator.isBoolean(value); AFTER: import {isNotIn, isBoolean} from "class-validator"; isNotIn(value, possibleValues); isBoolean(value); Closes #258, #248, #247, #212 --- .gitignore | 50 +- README.md | 105 +- gulpfile.ts | 215 ++- package-lock.json | 474 +++++- package.json | 32 +- src/decorator/ValidationOptions.ts | 14 +- src/decorator/array/ArrayContains.ts | 36 + src/decorator/array/ArrayMaxSize.ts | 33 + src/decorator/array/ArrayMinSize.ts | 33 + src/decorator/array/ArrayNotContains.ts | 36 + src/decorator/array/ArrayNotEmpty.ts | 32 + src/decorator/array/ArrayUnique.ts | 36 + src/decorator/common/Allow.ts | 20 + src/decorator/common/Equals.ts | 31 + src/decorator/common/IsDefined.ts | 32 + src/decorator/common/IsEmpty.ts | 30 + src/decorator/common/IsIn.ts | 31 + src/decorator/common/IsLatLong.ts | 31 + src/decorator/common/IsLatitude.ts | 31 + src/decorator/common/IsLongitude.ts | 31 + src/decorator/common/IsNotEmpty.ts | 30 + src/decorator/common/IsNotIn.ts | 31 + src/decorator/common/IsOptional.ts | 23 + src/decorator/common/NotEquals.ts | 31 + src/decorator/common/Validate.ts | 43 + src/decorator/common/ValidateBy.ts | 36 + src/decorator/common/ValidateIf.ts | 21 + src/decorator/common/ValidateNested.ts | 24 + src/decorator/common/ValidatePromise.ts | 20 + src/decorator/date/MaxDate.ts | 31 + src/decorator/date/MinDate.ts | 31 + src/decorator/decorators.ts | 1470 ++--------------- src/decorator/number/IsDivisibleBy.ts | 34 + src/decorator/number/IsNegative.ts | 30 + src/decorator/number/IsPositive.ts | 30 + src/decorator/number/Max.ts | 31 + src/decorator/number/Min.ts | 31 + src/decorator/object/IsInstance.ts | 39 + src/decorator/object/IsNotEmptyObject.ts | 42 + src/decorator/string/Contains.ts | 34 + src/decorator/string/IsAlpha.ts | 34 + src/decorator/string/IsAlphanumeric.ts | 34 + src/decorator/string/IsAscii.ts | 33 + src/decorator/string/IsBase64.ts | 33 + src/decorator/string/IsBooleanString.ts | 33 + src/decorator/string/IsByteLength.ts | 34 + src/decorator/string/IsCreditCard.ts | 33 + src/decorator/string/IsCurrency.ts | 34 + src/decorator/string/IsDateString.ts | 31 + src/decorator/string/IsDecimal.ts | 34 + src/decorator/string/IsEmail.ts | 34 + src/decorator/string/IsFQDN.ts | 34 + src/decorator/string/IsFirebasePushId.ts | 33 + src/decorator/string/IsFullWidth.ts | 33 + src/decorator/string/IsHalfWidth.ts | 33 + src/decorator/string/IsHash.ts | 36 + src/decorator/string/IsHexColor.ts | 33 + src/decorator/string/IsHexadecimal.ts | 33 + src/decorator/string/IsIP.ts | 37 + src/decorator/string/IsISBN.ts | 37 + src/decorator/string/IsISIN.ts | 33 + src/decorator/string/IsISO31661Alpha2.ts | 31 + src/decorator/string/IsISO31661Alpha3.ts | 31 + src/decorator/string/IsISO8601.ts | 36 + src/decorator/string/IsISSN.ts | 34 + src/decorator/string/IsJSON.ts | 33 + src/decorator/string/IsJWT.ts | 33 + src/decorator/string/IsLowercase.ts | 33 + src/decorator/string/IsMacAddress.ts | 39 + src/decorator/string/IsMilitaryTime.ts | 34 + src/decorator/string/IsMobilePhone.ts | 36 + src/decorator/string/IsMongoId.ts | 33 + src/decorator/string/IsMultibyte.ts | 33 + src/decorator/string/IsNumberString.ts | 34 + src/decorator/string/IsPhoneNumber.ts | 47 + src/decorator/string/IsPort.ts | 31 + src/decorator/string/IsSurrogatePair.ts | 33 + src/decorator/string/IsUUID.ts | 36 + src/decorator/string/IsUppercase.ts | 33 + src/decorator/string/IsUrl.ts | 34 + src/decorator/string/IsVariableWidth.ts | 33 + src/decorator/string/Length.ts | 43 + src/decorator/string/Matches.ts | 45 + src/decorator/string/MaxLength.ts | 34 + src/decorator/string/MinLength.ts | 34 + src/decorator/string/NotContains.ts | 34 + src/decorator/typechecker/IsArray.ts | 30 + src/decorator/typechecker/IsBoolean.ts | 30 + src/decorator/typechecker/IsDate.ts | 30 + src/decorator/typechecker/IsEnum.ts | 33 + src/decorator/typechecker/IsInt.ts | 30 + src/decorator/typechecker/IsNumber.ts | 62 + src/decorator/typechecker/IsObject.ts | 32 + src/decorator/typechecker/IsString.ts | 30 + src/index.ts | 14 +- src/metadata/MetadataStorage.ts | 32 +- src/register-decorator.ts | 9 +- src/types.d.ts | 3 +- .../ValidationSchemaToMetadataTransformer.ts | 5 +- src/validation/ValidationExecutor.ts | 32 +- src/validation/ValidationTypeOptions.ts | 8 - src/validation/ValidationTypes.ts | 313 +--- src/validation/Validator.ts | 889 ---------- ...alidation-functions-and-decorators.spec.ts | 381 +++-- tsconfig.json | 10 +- 105 files changed, 3973 insertions(+), 2981 deletions(-) create mode 100644 src/decorator/array/ArrayContains.ts create mode 100644 src/decorator/array/ArrayMaxSize.ts create mode 100644 src/decorator/array/ArrayMinSize.ts create mode 100644 src/decorator/array/ArrayNotContains.ts create mode 100644 src/decorator/array/ArrayNotEmpty.ts create mode 100644 src/decorator/array/ArrayUnique.ts create mode 100644 src/decorator/common/Allow.ts create mode 100644 src/decorator/common/Equals.ts create mode 100644 src/decorator/common/IsDefined.ts create mode 100644 src/decorator/common/IsEmpty.ts create mode 100644 src/decorator/common/IsIn.ts create mode 100644 src/decorator/common/IsLatLong.ts create mode 100644 src/decorator/common/IsLatitude.ts create mode 100644 src/decorator/common/IsLongitude.ts create mode 100644 src/decorator/common/IsNotEmpty.ts create mode 100644 src/decorator/common/IsNotIn.ts create mode 100644 src/decorator/common/IsOptional.ts create mode 100644 src/decorator/common/NotEquals.ts create mode 100644 src/decorator/common/Validate.ts create mode 100644 src/decorator/common/ValidateBy.ts create mode 100644 src/decorator/common/ValidateIf.ts create mode 100644 src/decorator/common/ValidateNested.ts create mode 100644 src/decorator/common/ValidatePromise.ts create mode 100644 src/decorator/date/MaxDate.ts create mode 100644 src/decorator/date/MinDate.ts create mode 100644 src/decorator/number/IsDivisibleBy.ts create mode 100644 src/decorator/number/IsNegative.ts create mode 100644 src/decorator/number/IsPositive.ts create mode 100644 src/decorator/number/Max.ts create mode 100644 src/decorator/number/Min.ts create mode 100644 src/decorator/object/IsInstance.ts create mode 100644 src/decorator/object/IsNotEmptyObject.ts create mode 100644 src/decorator/string/Contains.ts create mode 100644 src/decorator/string/IsAlpha.ts create mode 100644 src/decorator/string/IsAlphanumeric.ts create mode 100644 src/decorator/string/IsAscii.ts create mode 100644 src/decorator/string/IsBase64.ts create mode 100644 src/decorator/string/IsBooleanString.ts create mode 100644 src/decorator/string/IsByteLength.ts create mode 100644 src/decorator/string/IsCreditCard.ts create mode 100644 src/decorator/string/IsCurrency.ts create mode 100644 src/decorator/string/IsDateString.ts create mode 100644 src/decorator/string/IsDecimal.ts create mode 100644 src/decorator/string/IsEmail.ts create mode 100644 src/decorator/string/IsFQDN.ts create mode 100644 src/decorator/string/IsFirebasePushId.ts create mode 100644 src/decorator/string/IsFullWidth.ts create mode 100644 src/decorator/string/IsHalfWidth.ts create mode 100644 src/decorator/string/IsHash.ts create mode 100644 src/decorator/string/IsHexColor.ts create mode 100644 src/decorator/string/IsHexadecimal.ts create mode 100644 src/decorator/string/IsIP.ts create mode 100644 src/decorator/string/IsISBN.ts create mode 100644 src/decorator/string/IsISIN.ts create mode 100644 src/decorator/string/IsISO31661Alpha2.ts create mode 100644 src/decorator/string/IsISO31661Alpha3.ts create mode 100644 src/decorator/string/IsISO8601.ts create mode 100644 src/decorator/string/IsISSN.ts create mode 100644 src/decorator/string/IsJSON.ts create mode 100644 src/decorator/string/IsJWT.ts create mode 100644 src/decorator/string/IsLowercase.ts create mode 100644 src/decorator/string/IsMacAddress.ts create mode 100644 src/decorator/string/IsMilitaryTime.ts create mode 100644 src/decorator/string/IsMobilePhone.ts create mode 100644 src/decorator/string/IsMongoId.ts create mode 100644 src/decorator/string/IsMultibyte.ts create mode 100644 src/decorator/string/IsNumberString.ts create mode 100644 src/decorator/string/IsPhoneNumber.ts create mode 100644 src/decorator/string/IsPort.ts create mode 100644 src/decorator/string/IsSurrogatePair.ts create mode 100644 src/decorator/string/IsUUID.ts create mode 100644 src/decorator/string/IsUppercase.ts create mode 100644 src/decorator/string/IsUrl.ts create mode 100644 src/decorator/string/IsVariableWidth.ts create mode 100644 src/decorator/string/Length.ts create mode 100644 src/decorator/string/Matches.ts create mode 100644 src/decorator/string/MaxLength.ts create mode 100644 src/decorator/string/MinLength.ts create mode 100644 src/decorator/string/NotContains.ts create mode 100644 src/decorator/typechecker/IsArray.ts create mode 100644 src/decorator/typechecker/IsBoolean.ts create mode 100644 src/decorator/typechecker/IsDate.ts create mode 100644 src/decorator/typechecker/IsEnum.ts create mode 100644 src/decorator/typechecker/IsInt.ts create mode 100644 src/decorator/typechecker/IsNumber.ts create mode 100644 src/decorator/typechecker/IsObject.ts create mode 100644 src/decorator/typechecker/IsString.ts delete mode 100644 src/validation/ValidationTypeOptions.ts diff --git a/.gitignore b/.gitignore index 9eb1ab4ae..321cdfe19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,48 @@ -build/ -node_modules/ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Yarn Integrity file +.yarn-integrity + +# Output of 'npm pack' +*.tgz + +# Optional npm cache directory +.npm + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Coverage report dir coverage/ -npm-debug.log + +# Dependency directories +node_modules/ + +# rollup.js build output +dist/ +build/ + + +# IDEs *.iml -.idea/ \ No newline at end of file +.idea/ +.vscode/ + diff --git a/README.md b/README.md index 5484e65cf..3eb5258c0 100644 --- a/README.md +++ b/README.md @@ -790,105 +790,10 @@ If you want to perform a simple non async validation you can use `validateSync` There are several method exist in the Validator that allows to perform non-decorator based validation: ```typescript -import {Validator} from "class-validator"; - -// Validation methods -const validator = new Validator(); - -// common validation methods -validator.isDefined(value); // Checks if value is defined ("!==undefined"). -validator.equals(value, comparison); // Checks if value matches ("===") the comparison. -validator.notEquals(value, comparison); // Checks if value does not match ("!==") the comparison. -validator.isEmpty(value); // Checks if given value is empty (=== '', === null, === undefined). -validator.isNotEmpty(value); // Checks if given value is not empty (!== '', !== null, !== undefined). -validator.isIn(value, possibleValues); // Checks if given value is in a array of allowed values. -validator.isNotIn(value, possibleValues); // Checks if given value not in a array of allowed values. - -// type validation methods -validator.isBoolean(value); // Checks if a given value is a real boolean. -validator.isDate(value); // Checks if a given value is a real date. -validator.isString(value); // Checks if a given value is a real string. -validator.isArray(value); // Checks if a given value is an array. -validator.isNumber(value, options); // Checks if a given value is a real number. -validator.isInt(value); // Checks if value is an integer. -validator.isEnum(value, entity); // Checks if value is valid for a certain enum entity. - -// number validation methods -validator.isDivisibleBy(value, num); // Checks if value is a number that's divisible by another. -validator.isPositive(value); // Checks if the value is a positive number. -validator.isNegative(value); // Checks if the value is a negative number. -validator.min(num, min); // Checks if the first number is greater than or equal to the second. -validator.max(num, max); // Checks if the first number is less than or equal to the second. - -// date validation methods -validator.minDate(date, minDate); // Checks if the value is a date that's after the specified date. -validator.maxDate(date, maxDate); // Checks if the value is a date that's before the specified date. - -// string-type validation methods -validator.isBooleanString(str); // Checks if a string is a boolean. -validator.isNumberString(str); // Checks if the string is numeric. - -// string validation methods -validator.contains(str, seed); // Checks if the string contains the seed. -validator.notContains(str, seed); // Checks if the string does not contain the seed. -validator.isAlpha(str); // Checks if the string contains only letters (a-zA-Z). -validator.isAlphanumeric(str); // Checks if the string contains only letters and numbers. -validator.isDecimal(str, options); // Checks if the string is a valid decimal value. -validator.isAscii(str); // Checks if the string contains ASCII chars only. -validator.isBase64(str); // Checks if a string is base64 encoded. -validator.isByteLength(str, min, max); // Checks if the string's length (in bytes) falls in a range. -validator.isCreditCard(str); // Checks if the string is a credit card. -validator.isCurrency(str, options); // Checks if the string is a valid currency amount. -validator.isEmail(str, options); // Checks if the string is an email. -validator.isFQDN(str, options); // Checks if the string is a fully qualified domain name (e.g. domain.com). -validator.isFullWidth(str); // Checks if the string contains any full-width chars. -validator.isHalfWidth(str); // Checks if the string contains any half-width chars. -validator.isVariableWidth(str); // Checks if the string contains variable-width chars. -validator.isHexColor(str); // Checks if the string is a hexadecimal color. -validator.isHexadecimal(str); // Checks if the string is a hexadecimal number. -validator.isMACAddress(str); // Checks if the string is a MAC Address. -validator.isIP(str, version); // Checks if the string is an IP (version 4 or 6). -validator.isPort(str); // Check if the string is a valid port number. -validator.isISBN(str, version); // Checks if the string is an ISBN (version 10 or 13). -validator.isISIN(str); // Checks if the string is an ISIN (stock/security identifier). -validator.isISO8601(str, options); // Checks if the string is a valid ISO 8601 date. Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. -validator.isJSON(str); // Checks if the string is valid JSON (note: uses JSON.parse). -validator.isJWT(str) // Checks if the string is valid JWT. -validator.isObject(object); // Checks if the object is valid Object (null, functions, arrays will return false) -validator.isNotEmptyObject(object); // Checks if the object is not empty -validator.isLowercase(str); // Checks if the string is lowercase. -validator.isLatLong(str); // Checks if the string is a valid latitude-longitude coordinate in the format lat,long -validator.isLatitude(str); // Checks if the string or number is a valid latitude coordinate -validator.isLongtitude(str); // Checks if the string or number is a valid longitude coordinate -validator.isMobilePhone(str, locale); // Checks if the string is a mobile phone number. -validator.isISO31661Alpha2(str); // Check if the string is a valid ISO 3166-1 alpha-2 -validator.isISO31661Alpha3(str); // Check if the string is a valid ISO 3166-1 alpha-3 -validator.isPhoneNumber(str, region); // Checks if the string is a valid phone number. -validator.isMongoId(str); // Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. -validator.isMultibyte(str); // Checks if the string contains one or more multibyte chars. -validator.isSurrogatePair(str); // Checks if the string contains any surrogate pairs chars. -validator.isURL(str, options); // Checks if the string is an url. -validator.isUUID(str, version); // Checks if the string is a UUID (version 3, 4, 5 or all). -validator.IsFirebasePushId(str); // Checks if the string is a Firebase Push Id -validator.isUppercase(str); // Checks if the string is uppercase. -validator.length(str, min, max); // Checks if the string's length falls in a range. -validator.minLength(str, min); // Checks if the string's length is not less than given number. -validator.maxLength(str, max); // Checks if the string's length is not more than given number. -validator.matches(str, pattern, modifiers); // Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). -validator.isMilitaryTime(str); // Checks if the string is a valid representation of military time in the format HH:MM. -validator.isHash(str, algorithm); // Checks if the string is a hash of type algorithm. -validator.isISSN(str, options); // Checks if the string is a ISSN. - -// array validation methods -validator.arrayContains(array, values); // Checks if array contains all values from the given array of values. -validator.arrayNotContains(array, values); // Checks if array does not contain any of the given values. -validator.arrayNotEmpty(array); // Checks if given array is not empty. -validator.arrayMinSize(array, min); // Checks if array's length is at least `min` number. -validator.arrayMaxSize(array, max); // Checks if array's length is as most `max` number. -validator.arrayUnique(array); // Checks if all array's values are unique. Comparison for objects is reference-based. - -// object validation methods -validator.isInstance(value, target); // Checks value is an instance of the target. +import {isEmpty, isBoolean} from "class-validator"; + +isEmpty(value); +isBoolean(value); ``` ## Validation decorators @@ -943,7 +848,7 @@ validator.isInstance(value, target); // Checks value is an instance of the targe | `@IsVariableWidth()` | Checks if the string contains a mixture of full and half-width chars. | | `@IsHexColor()` | Checks if the string is a hexadecimal color. | | `@IsHexadecimal()` | Checks if the string is a hexadecimal number. | -| `@IsMACAddress()` | Checks if the string is a MAC Address. | +| `@IsMACAddress(options?: IsMACAddressOptions)` | Checks if the string is a MAC Address. | | `@IsIP(version?: "4"\|"6")` | Checks if the string is an IP (version 4 or 6). | | `@IsPort()` | Check if the string is a valid port number. | | `@IsISBN(version?: "10"\|"13")` | Checks if the string is an ISBN (version 10 or 13). | diff --git a/gulpfile.ts b/gulpfile.ts index e418ac16c..1c16a1f5e 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -1,4 +1,5 @@ -import { Gulpclass, Task, SequenceTask, MergedTask } from "gulpclass"; +import { resolve } from "path"; +import { Gulpclass, Task, SequenceTask } from "gulpclass"; import * as gulp from "gulp"; import * as del from "del"; import * as shell from "gulp-shell"; @@ -9,6 +10,17 @@ import tslintPlugin from "gulp-tslint"; import * as ts from "gulp-typescript"; import * as sourcemaps from "gulp-sourcemaps"; import * as istanbul from "gulp-istanbul"; +import { rollup, RollupOptions, Plugin } from "rollup"; +import { terser as rollupTerser } from "rollup-plugin-terser"; + +const pkg = require("./package.json"); + +const rollupSourceMaps = require("rollup-plugin-sourcemaps"); +const rollupCommonjs = require("rollup-plugin-commonjs"); +const rollupJson = require("rollup-plugin-json"); +const rollupNodeResolve = require("rollup-plugin-node-resolve"); +const rollupUglify = require("rollup-plugin-uglify"); + const conventionalChangelog = require("gulp-conventional-changelog"); const remapIstanbul = require("remap-istanbul/lib/gulpRemapIstanbul"); @@ -16,6 +28,30 @@ const remapIstanbul = require("remap-istanbul/lib/gulpRemapIstanbul"); @Gulpclass() export class Gulpfile { + rollupExternal = [ + ...Object.keys(pkg.peerDependencies), + ...Object.keys(pkg.dependencies) + ]; + rollupCommonPlugins: Plugin[] = [ + // Allow json resolution + rollupJson(), + // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs) + rollupCommonjs(), + // Allow node_modules resolution, so you can use 'external' to control + // which external modules to include in the bundle + // https://github.com/rollup/rollup-plugin-node-resolve#usage + rollupNodeResolve(), + // Resolve source maps to the original source + rollupSourceMaps(), + ]; + rollupCommonOptions: RollupOptions = { + inlineDynamicImports: true, + // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash') + external: this.rollupExternal, + }; + + + // ------------------------------------------------------------------------- // General tasks // ------------------------------------------------------------------------- @@ -26,11 +62,8 @@ export class Gulpfile { @Task() clean() { return del([ + "dist/**", "build/**", - "!build", - "!build/package", - "!build/package/node_modules", - "!build/package/node_modules/**" ]); } @@ -38,9 +71,15 @@ export class Gulpfile { * Runs typescript files compilation. */ @Task() - compile() { - return gulp.src("*.ts", { read: false }) - .pipe(shell(["tsc"])); + compileTests() { + const tsProjectEsm5 = ts.createProject("tsconfig.json", { rootDir: "./" }); + const tsResultEsm5 = gulp.src(["./{test,src}/**/*.ts"]) + .pipe(sourcemaps.init()) + .pipe(tsProjectEsm5()); + + return tsResultEsm5.js + .pipe(sourcemaps.write(".", { sourceRoot: "", includeContent: true })) + .pipe(gulp.dest("build/compile")); } // ------------------------------------------------------------------------- @@ -54,14 +93,14 @@ export class Gulpfile { // conventional-changelog options go here preset: "angular" }, { - // context goes here - }, { - // git-raw-commits options go here - }, { - // conventional-commits-parser options go here - }, { - // conventional-changelog-writer options go here - })) + // context goes here + }, { + // git-raw-commits options go here + }, { + // conventional-commits-parser options go here + }, { + // conventional-changelog-writer options go here + })) .pipe(gulp.dest("./")); } @@ -72,47 +111,78 @@ export class Gulpfile { npmPublish() { return gulp.src("*.js", { read: false }) .pipe(shell([ - "cd ./build/package && npm publish" + "cd ./dist/ && npm publish" ])); } - /** - * Copies all sources to the package directory. - */ - @MergedTask() - packageCompile() { - const tsProject = ts.createProject("tsconfig.json"); + @Task() + packageCompileEsm5() { + const tsProjectEsm5 = ts.createProject("tsconfig.json", { module: "esnext", target: "es5" }); + const tsResultEsm5 = gulp.src(["./src/**/*.ts"]) + .pipe(sourcemaps.init()) + .pipe(tsProjectEsm5()); + + return tsResultEsm5.js + .pipe(sourcemaps.write(".", { sourceRoot: "", includeContent: true })) + .pipe(gulp.dest("dist/esm5")); + } + + @Task() + packageCompileEsm2015() { + const tsProjectEsm2015 = ts.createProject("tsconfig.json", { module: "esnext", target: "es2018" }); + const tsResultEsm2015 = gulp.src(["./src/**/*.ts"]) + .pipe(sourcemaps.init()) + .pipe(tsProjectEsm2015()); + + return tsResultEsm2015.js + .pipe(sourcemaps.write(".", { sourceRoot: "", includeContent: true })) + .pipe(gulp.dest("dist/esm2015")); + } + + @Task() + packageCompileTypes() { + const tsProject = ts.createProject("tsconfig.json", { module: "esnext" }); const tsResult = gulp.src(["./src/**/*.ts"]) .pipe(sourcemaps.init()) .pipe(tsProject()); - return [ - tsResult.dts.pipe(gulp.dest("./build/package")), - tsResult.js - .pipe(sourcemaps.write(".", { sourceRoot: "", includeContent: true })) - .pipe(gulp.dest("./build/package")) - ]; + return tsResult.dts.pipe(gulp.dest("dist/types")); } + /** - * Moves all compiled files to the final package directory. + * Copies all sources to the package directory. */ + @SequenceTask() + packageCompile() { + return [ + ["packageCompileEsm5", "packageCompileEsm2015", "packageCompileTypes"] + ]; + } + @Task() - packageMoveCompiledFiles() { - return gulp.src("./build/package/src/**/*") - .pipe(gulp.dest("./build/package")); + packageBundleEsm5() { + return Promise.all([ + this._rollupPackageBundleEsm5(true), + this._rollupPackageBundleEsm5(false), + ]); } - /** - * Moves all compiled files to the final package directory. - */ @Task() - packageClearCompileDirectory(cb: Function) { - return del([ - "build/package/src/**" + packageBundleEsm2015() { + return Promise.all([ + this._rollupPackageBundleEsm2015(true), + this._rollupPackageBundleEsm2015(false), ]); } + @SequenceTask() + packageBundle() { + return [ + ["packageBundleEsm5", "packageBundleEsm2015"] + ]; + } + /** * Change the "private" state of the packaged package.json file to public. */ @@ -120,7 +190,7 @@ export class Gulpfile { packagePreparePackageFile() { return gulp.src("./package.json") .pipe(replace("\"private\": true,", "\"private\": false,")) - .pipe(gulp.dest("./build/package")); + .pipe(gulp.dest("./dist")); } /** @@ -131,7 +201,7 @@ export class Gulpfile { packageReadmeFile() { return gulp.src("./README.md") .pipe(replace(/```typescript([\s\S]*?)```/g, "```javascript$1```")) - .pipe(gulp.dest("./build/package")); + .pipe(gulp.dest("./dist")); } /** @@ -142,8 +212,7 @@ export class Gulpfile { return [ "clean", "packageCompile", - "packageMoveCompiledFiles", - "packageClearCompileDirectory", + "packageBundle", ["packagePreparePackageFile", "packageReadmeFile"] ]; } @@ -203,7 +272,7 @@ export class Gulpfile { chai.use(require("sinon-chai")); chai.use(require("chai-as-promised")); - return gulp.src(["./build/compiled/test/**/*.js"]) + return gulp.src(["./build/compile/test/**/*.js"]) .pipe(mocha()) .pipe(istanbul.writeReports()); } @@ -220,7 +289,63 @@ export class Gulpfile { */ @SequenceTask() tests() { - return ["compile", "tslint", "coveragePre", "coveragePost", "coverageRemap"]; + return ["clean", "compileTests", "tslint", "coveragePre", "coveragePost", "coverageRemap"]; + } + + private _rollupPackageBundleEsm5(isMin: boolean) { + return rollup({ + ...this.rollupCommonOptions, + plugins: [ + ...this.rollupCommonPlugins, + ...(isMin ? [rollupUglify.uglify()] : []), + ], + input: resolve(__dirname, "dist/esm5/index.js"), + }).then(bundle => { + return bundle.write({ + file: this._getOutputFileName(resolve(__dirname, "dist/bundles/index.umd.js"), isMin), + format: "umd", + name: this._pascalCase(this._normalizePackageName(pkg.name)), + sourcemap: true, + }); + }); + } + + private _rollupPackageBundleEsm2015(isMin: boolean) { + return rollup({ + ...this.rollupCommonOptions, + plugins: [ + ...this.rollupCommonPlugins, + ...(isMin ? [rollupTerser()] : []), + ], + input: resolve(__dirname, "dist/esm2015/index.js"), + }).then(bundle => { + return bundle.write({ + file: this._getOutputFileName(resolve(__dirname, "dist/bundles/index.esm.js"), isMin), + format: "es", + sourcemap: true, + }); + }); + } + + private _dashToCamelCase(myStr: string) { + return myStr.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); + } + + private _toUpperCase(myStr: string) { + return `${myStr.charAt(0).toUpperCase()}${myStr.substr(1)}`; + } + + private _pascalCase(myStr: string) { + return this._toUpperCase(this._dashToCamelCase(myStr)); + } + + private _normalizePackageName(rawPackageName: string) { + const scopeEnd = rawPackageName.indexOf("/") + 1; + return rawPackageName.substring(scopeEnd); + } + + private _getOutputFileName(fileName: string, isMin = false) { + return isMin ? fileName.replace(/\.js$/, ".min.js") : fileName; } } diff --git a/package-lock.json b/package-lock.json index 5f65de05f..ae4e938b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,78 @@ { "name": "class-validator", - "version": "0.11.0", + "version": "0.12.0-refactor.3", "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "@gulp-sourcemaps/identity-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", @@ -137,6 +206,12 @@ "del": "*" } }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, "@types/events": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", @@ -245,6 +320,59 @@ "integrity": "sha512-aK9jxMypeSrhiYofWWBf/T7O+KwaiAHzM4sveCdWPn71lzUSMimRnKzhXDKfKwV1kWoBo2P1aGgaIYGLf9/ljw==", "dev": true }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/rollup-plugin-json": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/rollup-plugin-json/-/rollup-plugin-json-3.0.2.tgz", + "integrity": "sha512-eTRym5nG4HEKDR/KrTnCMYwF7V0hgVjEesvtJCK3V8ho/aT0ZFRFgsDtx38VarM30HCsN372+i4FKYwnhcwiVA==", + "dev": true, + "requires": { + "@types/node": "*", + "rollup": "^0.63.4" + }, + "dependencies": { + "rollup": { + "version": "0.63.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.63.5.tgz", + "integrity": "sha512-dFf8LpUNzIj3oE0vCvobX6rqOzHzLBoblyFp+3znPbjiSmSvOoK2kMKx+Fv9jYduG1rvcCfCveSgEaQHjWRF6g==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "*" + } + } + } + }, + "@types/rollup-plugin-sourcemaps": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@types/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", + "integrity": "sha512-dqF1rMFy4O8yNlQYwYPos5Cfav0f6M7PLH8B33gsslQ0zA9MX1jMGokwNuJ3Z3EXAzsKF/xAWNHpFmELcgYJww==", + "dev": true, + "requires": { + "@types/node": "*", + "rollup": "^0.63.4" + }, + "dependencies": { + "rollup": { + "version": "0.63.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.63.5.tgz", + "integrity": "sha512-dFf8LpUNzIj3oE0vCvobX6rqOzHzLBoblyFp+3znPbjiSmSvOoK2kMKx+Fv9jYduG1rvcCfCveSgEaQHjWRF6g==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "*" + } + } + } + }, "@types/sinon": { "version": "7.0.13", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.0.13.tgz", @@ -268,9 +396,9 @@ "dev": true }, "@types/validator": { - "version": "10.11.3", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-10.11.3.tgz", - "integrity": "sha512-GKF2VnEkMmEeEGvoo03ocrP9ySMuX1ypKazIYMlsjfslfBMhOAtC5dmEWKdJioW4lJN7MZRS88kalTsVClyQ9w==" + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-12.0.1.tgz", + "integrity": "sha512-l57fIANZLMe8DArz+SDb+7ATXnDm15P7u2wHBw5mb0aSMd+UuvmvhouBF2hdLgQPDMJ39sh9g2MJO4GkZ0VAdQ==" }, "@types/vinyl": { "version": "2.0.2", @@ -442,9 +570,9 @@ "dev": true }, "arg": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", - "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { @@ -2159,6 +2287,12 @@ "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", "dev": true }, + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", @@ -3672,9 +3806,9 @@ } }, "google-libphonenumber": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.1.10.tgz", - "integrity": "sha512-rbq206gtM62kwEMDLuc2K2CcfUsIxGlOcN5psDFx6cpJmTzgQrspO4A4ombKn9UxDidxs0j+jN+z9/Y3M1D4Ig==" + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.8.tgz", + "integrity": "sha512-iWs1KcxOozmKQbCeGjvU0M7urrkNjBYOSBtb819RjkUNJHJLfn7DADKkKwdJTOMPLcLOE11/4h/FyFwJsTiwLg==" }, "graceful-fs": { "version": "4.1.15", @@ -4909,6 +5043,12 @@ "is-extglob": "^2.1.0" } }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -4986,6 +5126,15 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-reference": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", + "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", + "dev": true, + "requires": { + "@types/estree": "0.0.39" + } + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -5166,6 +5315,33 @@ "textextensions": "2" } }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -5491,6 +5667,15 @@ "es5-ext": "~0.10.2" } }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, "make-error": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", @@ -6909,7 +7094,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -7362,6 +7547,178 @@ } } }, + "rollup": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", + "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + } + } + }, + "rollup-plugin-commonjs": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", + "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + }, + "dependencies": { + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "rollup-plugin-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-json/-/rollup-plugin-json-4.0.0.tgz", + "integrity": "sha512-hgb8N7Cgfw5SZAkb3jf0QXii6QX/FOkiIq2M7BAQIEydjHvTyxXHQiIzZaTFgx1GK0cRCHOCBHIyEkkLdWKxow==", + "dev": true, + "requires": { + "rollup-pluginutils": "^2.5.0" + } + }, + "rollup-plugin-node-resolve": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", + "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", + "dev": true, + "requires": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.1", + "rollup-pluginutils": "^2.8.1" + }, + "dependencies": { + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "rollup-plugin-replace": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz", + "integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==", + "dev": true, + "requires": { + "magic-string": "^0.25.2", + "rollup-pluginutils": "^2.6.0" + } + }, + "rollup-plugin-sourcemaps": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", + "integrity": "sha1-YhJaqUCHqt97g+9N+vYptHMTXoc=", + "dev": true, + "requires": { + "rollup-pluginutils": "^2.0.1", + "source-map-resolve": "^0.5.0" + } + }, + "rollup-plugin-terser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.3.0.tgz", + "integrity": "sha512-XGMJihTIO3eIBsVGq7jiNYOdDMb3pVxuzY0uhOE/FM4x/u9nQgr3+McsjzqBn3QfHIpNSZmFnpoKAwHBEcsT7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "jest-worker": "^24.9.0", + "rollup-pluginutils": "^2.8.2", + "serialize-javascript": "^2.1.2", + "terser": "^4.6.2" + } + }, + "rollup-plugin-uglify": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.4.tgz", + "integrity": "sha512-ddgqkH02klveu34TF0JqygPwZnsbhHVI6t8+hGTcYHngPkQb5MIHI0XiztXIN/d6V9j+efwHAqEL7LspSxQXGw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "jest-worker": "^24.0.0", + "serialize-javascript": "^2.1.2", + "uglify-js": "^3.4.9" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-js": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz", + "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==", + "dev": true, + "requires": { + "commander": "~2.20.3", + "source-map": "~0.6.1" + } + } + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + } + }, "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", @@ -7376,7 +7733,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -7404,6 +7761,12 @@ "sver-compat": "^1.5.0" } }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -7654,6 +8017,12 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, "sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", @@ -7874,6 +8243,31 @@ } } }, + "terser": { + "version": "4.6.10", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.10.tgz", + "integrity": "sha512-qbF/3UOo11Hggsbsqm2hPa6+L4w7bkr+09FNseEe8xrcVD3APGLFqE+Oz1ZKAxjYnFsj80rLOfgAtJ0LNJjtTA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "text-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.0.0.tgz", @@ -8039,30 +8433,30 @@ "dev": true }, "ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.8.1.tgz", + "integrity": "sha512-10DE9ONho06QORKAaCBpPiFCdW+tZJuY/84tyypGtl6r+/C7Asq0dhqbRZURuUlLQtZxxDvT8eoj8cGW0ha6Bg==", "dev": true, "requires": { "arg": "^4.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "source-map-support": "^0.5.6", - "yn": "^3.0.0" + "yn": "3.1.1" }, "dependencies": { "diff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", - "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true } } }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", "dev": true }, "tslint": { @@ -8455,9 +8849,9 @@ } }, "validator": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-12.0.0.tgz", - "integrity": "sha512-r5zA1cQBEOgYlesRmSEwc9LkbfNLTtji+vWyaHzRZUxCTHdsX3bd+sdHfs5tGZ2W6ILGGsxWxCNwT/h3IY/3ng==" + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz", + "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ==" }, "value-or-function": { "version": "3.0.0", @@ -8643,6 +9037,30 @@ "source-map": "^0.5.1" } }, + "webpack-config-utils": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-config-utils/-/webpack-config-utils-2.3.1.tgz", + "integrity": "sha512-0uC5uj7sThFTePTQjfpe5Wqcbw3KSCxqswOmW96lwk2ZI2CU098rWY2ZqOVGJQYJ3hfEltmjcLNkKutw8LJAlg==", + "dev": true, + "requires": { + "webpack-combine-loaders": "2.0.4" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "bundled": true, + "dev": true + }, + "webpack-combine-loaders": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "qs": "^6.5.2" + } + } + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -8905,9 +9323,9 @@ } }, "yn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", - "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true } } diff --git a/package.json b/package.json index 03e3cee32..34bd51741 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,15 @@ { "name": "class-validator", "private": true, - "version": "0.11.1", + "version": "0.12.0-refactor.5", "description": "Class-based validation with Typescript / ES6 / ES5 using decorators or validation schemas. Supports both node.js and browser", "license": "MIT", "readmeFilename": "README.md", + "main": "./bundles/index.umd.js", + "module": "./esm5/index.js", + "es2015": "./esm2015/index.js", + "typings": "./types/index.d.ts", + "sideEffects": false, "author": { "name": "Umed Khudoiberdiev", "email": "pleerock.me@gmail.com" @@ -22,10 +27,13 @@ "typescript", "typescript-validator" ], + "peerDependencies": { + "tslib": ">=1.9.0" + }, "dependencies": { - "@types/validator": "10.11.3", - "google-libphonenumber": "^3.1.6", - "validator": "12.0.0" + "@types/validator": "12.0.1", + "google-libphonenumber": "^3.2.8", + "validator": "12.2.0" }, "devDependencies": { "@types/chai": "^4.2.0", @@ -38,6 +46,8 @@ "@types/gulp-sourcemaps": "0.0.32", "@types/mocha": "^7.0.0", "@types/node": "^12.7.1", + "@types/rollup-plugin-json": "^3.0.2", + "@types/rollup-plugin-sourcemaps": "^0.4.2", "@types/sinon": "^7.0.13", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", @@ -58,12 +68,22 @@ "gulpclass": "^0.2.0", "mocha": "^7.0.1", "remap-istanbul": "^0.13.0", + "rollup": "^1.20.1", + "rollup-plugin-commonjs": "^10.0.2", + "rollup-plugin-json": "^4.0.0", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-replace": "^2.2.0", + "rollup-plugin-sourcemaps": "^0.4.2", + "rollup-plugin-terser": "^5.3.0", + "rollup-plugin-uglify": "^6.0.4", "sinon": "^8.0.0", "sinon-chai": "^3.2.0", - "ts-node": "^8.3.0", + "ts-node": "^8.8.1", + "tslib": "^1.11.1", "tslint": "^5.11.0", "tslint-stylish": "^2.1.0", - "typescript": "^3.5.3" + "typescript": "^3.5.3", + "webpack-config-utils": "^2.3.1" }, "scripts": { "build": "gulp package", diff --git a/src/decorator/ValidationOptions.ts b/src/decorator/ValidationOptions.ts index 04cfb42c4..3a44d0fa2 100644 --- a/src/decorator/ValidationOptions.ts +++ b/src/decorator/ValidationOptions.ts @@ -1,4 +1,4 @@ -import {ValidationArguments} from "../validation/ValidationArguments"; +import { ValidationArguments } from "../validation/ValidationArguments"; /** * Options used to pass to validation decorators. @@ -31,3 +31,15 @@ export interface ValidationOptions { */ context?: any; } + + +export function isValidationOptions(val: any): val is ValidationOptions { + if (!val) { + return false; + } + return "each" in val + || "message" in val + || "groups" in val + || "always" in val + || "context" in val; +} diff --git a/src/decorator/array/ArrayContains.ts b/src/decorator/array/ArrayContains.ts new file mode 100644 index 000000000..9f7616c71 --- /dev/null +++ b/src/decorator/array/ArrayContains.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const ARRAY_CONTAINS = "arrayContains"; + +/** + * Checks if array contains all values from the given array of values. + * If null or undefined is given then this function returns false. + */ +export function arrayContains(array: unknown, values: any[]) { + if (!(array instanceof Array)) + return false; + + return values.every(value => array.indexOf(value) !== -1); +} + +/** + * Checks if array contains all values from the given array of values. + * If null or undefined is given then this function returns false. + */ +export function ArrayContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_CONTAINS, + constraints: [values], + validator: { + validate: (value, args) => arrayContains(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain $constraint1 values", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayMaxSize.ts b/src/decorator/array/ArrayMaxSize.ts new file mode 100644 index 000000000..bc5533092 --- /dev/null +++ b/src/decorator/array/ArrayMaxSize.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const ARRAY_MAX_SIZE = "arrayMaxSize"; + +/** + * Checks if array's length is as maximal this number. + * If null or undefined is given then this function returns false. + */ +export function arrayMaxSize(array: unknown, max: number) { + return array instanceof Array && array.length <= max; +} + +/** + * Checks if array's length is as maximal this number. + * If null or undefined is given then this function returns false. + */ +export function ArrayMaxSize(max: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_MAX_SIZE, + constraints: [max], + validator: { + validate: (value, args) => arrayMaxSize(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain not more than $constraint1 elements", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayMinSize.ts b/src/decorator/array/ArrayMinSize.ts new file mode 100644 index 000000000..759f43253 --- /dev/null +++ b/src/decorator/array/ArrayMinSize.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const ARRAY_MIN_SIZE = "arrayMinSize"; + +/** + * Checks if array's length is as minimal this number. + * If null or undefined is given then this function returns false. + */ +export function arrayMinSize(array: unknown, min: number) { + return array instanceof Array && array.length >= min; +} + +/** + * Checks if array's length is as minimal this number. + * If null or undefined is given then this function returns false. + */ +export function ArrayMinSize(min: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_MIN_SIZE, + constraints: [min], + validator: { + validate: (value, args) => arrayMinSize(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain at least $constraint1 elements", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayNotContains.ts b/src/decorator/array/ArrayNotContains.ts new file mode 100644 index 000000000..aa0175290 --- /dev/null +++ b/src/decorator/array/ArrayNotContains.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const ARRAY_NOT_CONTAINS = "arrayNotContains"; + +/** + * Checks if array does not contain any of the given values. + * If null or undefined is given then this function returns false. + */ +export function arrayNotContains(array: unknown, values: any[]) { + if (!(array instanceof Array)) + return false; + + return values.every(value => array.indexOf(value) === -1); +} + +/** + * Checks if array does not contain any of the given values. + * If null or undefined is given then this function returns false. + */ +export function ArrayNotContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_NOT_CONTAINS, + constraints: [values], + validator: { + validate: (value, args) => arrayNotContains(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property should not contain $constraint1 values", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayNotEmpty.ts b/src/decorator/array/ArrayNotEmpty.ts new file mode 100644 index 000000000..4804940eb --- /dev/null +++ b/src/decorator/array/ArrayNotEmpty.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const ARRAY_NOT_EMPTY = "arrayNotEmpty"; + +/** + * Checks if given array is not empty. + * If null or undefined is given then this function returns false. + */ +export function arrayNotEmpty(array: unknown) { + return array instanceof Array && array.length > 0; +} + +/** + * Checks if given array is not empty. + * If null or undefined is given then this function returns false. + */ +export function ArrayNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_NOT_EMPTY, + validator: { + validate: (value, args) => arrayNotEmpty(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property should not be empty", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayUnique.ts b/src/decorator/array/ArrayUnique.ts new file mode 100644 index 000000000..24b8ec755 --- /dev/null +++ b/src/decorator/array/ArrayUnique.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const ARRAY_UNIQUE = "arrayUnique"; + +/** + * Checks if all array's values are unique. Comparison for objects is reference-based. + * If null or undefined is given then this function returns false. + */ +export function arrayUnique(array: unknown) { + if (!(array instanceof Array)) + return false; + + const uniqueItems = array.filter((a, b, c) => c.indexOf(a) === b); + return array.length === uniqueItems.length; +} + +/** + * Checks if all array's values are unique. Comparison for objects is reference-based. + * If null or undefined is given then this function returns false. + */ +export function ArrayUnique(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_UNIQUE, + validator: { + validate: (value, args) => arrayUnique(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "All $property's elements must be unique", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/Allow.ts b/src/decorator/common/Allow.ts new file mode 100644 index 000000000..5a35597f3 --- /dev/null +++ b/src/decorator/common/Allow.ts @@ -0,0 +1,20 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { ValidationMetadataArgs } from "../../metadata/ValidationMetadataArgs"; +import { ValidationTypes } from "../../validation/ValidationTypes"; +import { ValidationMetadata } from "../../metadata/ValidationMetadata"; +import { getMetadataStorage } from "../../metadata/MetadataStorage"; + +/** + * If object has both allowed and not allowed properties a validation error will be thrown. + */ +export function Allow(validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: Object, propertyName: string) { + const args: ValidationMetadataArgs = { + type: ValidationTypes.WHITELIST, + target: object.constructor, + propertyName: propertyName, + validationOptions: validationOptions + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/Equals.ts b/src/decorator/common/Equals.ts new file mode 100644 index 000000000..a3563791d --- /dev/null +++ b/src/decorator/common/Equals.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const EQUALS = "equals"; + +/** + * Checks if value matches ("===") the comparison. + */ +export function equals(value: unknown, comparison: unknown): boolean { + return value === comparison; +} + +/** + * Checks if value matches ("===") the comparison. + */ +export function Equals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: EQUALS, + constraints: [comparison], + validator: { + validate: (value, args) => equals(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be equal to $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsDefined.ts b/src/decorator/common/IsDefined.ts new file mode 100644 index 000000000..63992ac6d --- /dev/null +++ b/src/decorator/common/IsDefined.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "./ValidateBy"; +import { ValidationTypes } from "../../validation/ValidationTypes"; + +// isDefined is (yet) a special case +export const IS_DEFINED = ValidationTypes.IS_DEFINED; + +/** + * Checks if value is defined (!== undefined, !== null). + */ +export function isDefined(value: any): boolean { + return value !== undefined && value !== null; +} + +/** + * Checks if value is defined (!== undefined, !== null). + */ +export function IsDefined(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DEFINED, + validator: { + validate: (value) => isDefined(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property should not be null or undefined", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsEmpty.ts b/src/decorator/common/IsEmpty.ts new file mode 100644 index 000000000..10f977d7b --- /dev/null +++ b/src/decorator/common/IsEmpty.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_EMPTY = "isEmpty"; + +/** + * Checks if given value is empty (=== '', === null, === undefined). + */ +export function isEmpty(value: unknown): boolean { + return value === "" || value === null || value === undefined; +} + +/** + * Checks if given value is empty (=== '', === null, === undefined). + */ +export function IsEmpty(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_EMPTY, + validator: { + validate: (value, args) => isEmpty(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be empty", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsIn.ts b/src/decorator/common/IsIn.ts new file mode 100644 index 000000000..4bbd63111 --- /dev/null +++ b/src/decorator/common/IsIn.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_IN = "isIn"; + +/** + * Checks if given value is in a array of allowed values. + */ +export function isIn(value: unknown, possibleValues: unknown[]): boolean { + return !(possibleValues instanceof Array) || possibleValues.some(possibleValue => possibleValue === value); +} + +/** + * Checks if given value is in a array of allowed values. + */ +export function IsIn(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_IN, + constraints: [values], + validator: { + validate: (value, args) => isIn(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be one of the following values: $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsLatLong.ts b/src/decorator/common/IsLatLong.ts new file mode 100644 index 000000000..3eeba6c0c --- /dev/null +++ b/src/decorator/common/IsLatLong.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "./ValidateBy"; +import validator from "validator"; + +export const IS_LATLONG = "isLatLong"; + +/** + * Checks if a value is string in format a "latitude,longitude". + */ +export function isLatLong(value: string): boolean { + return typeof value === "string" && validator.isLatLong(value); +} + +/** + * Checks if a value is string in format a "latitude,longitude". + */ +export function IsLatLong(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LATLONG, + validator: { + validate: (value, args) => isLatLong(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a latitude,longitude string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsLatitude.ts b/src/decorator/common/IsLatitude.ts new file mode 100644 index 000000000..b2b3402d8 --- /dev/null +++ b/src/decorator/common/IsLatitude.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "./ValidateBy"; +import { isLatLong } from "./IsLatLong"; + +export const IS_LATITUDE = "isLatitude"; + +/** + * Checks if a given value is a latitude. + */ +export function isLatitude(value: string): boolean { + return (typeof value === "number" || typeof value === "string") && isLatLong(`${value},0`); +} + +/** + * Checks if a given value is a latitude. + */ +export function IsLatitude(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LATITUDE, + validator: { + validate: (value, args) => isLatitude(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a latitude string or number", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsLongitude.ts b/src/decorator/common/IsLongitude.ts new file mode 100644 index 000000000..2340d853a --- /dev/null +++ b/src/decorator/common/IsLongitude.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "./ValidateBy"; +import { isLatLong } from "./IsLatLong"; + +export const IS_LONGITUDE = "isLongitude"; + +/** + * Checks if a given value is a longitude. + */ +export function isLongitude(value: string): boolean { + return (typeof value === "number" || typeof value === "string") && isLatLong(`0,${value}`); +} + +/** + * Checks if a given value is a longitude. + */ +export function IsLongitude(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LONGITUDE, + validator: { + validate: (value, args) => isLongitude(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a longitude string or number", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsNotEmpty.ts b/src/decorator/common/IsNotEmpty.ts new file mode 100644 index 000000000..9a6077a0d --- /dev/null +++ b/src/decorator/common/IsNotEmpty.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_NOT_EMPTY = "isNotEmpty"; + +/** + * Checks if given value is not empty (!== '', !== null, !== undefined). + */ +export function isNotEmpty(value: unknown): boolean { + return value !== "" && value !== null && value !== undefined; +} + +/** + * Checks if given value is not empty (!== '', !== null, !== undefined). + */ +export function IsNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NOT_EMPTY, + validator: { + validate: (value, args) => isNotEmpty(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property should not be empty", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsNotIn.ts b/src/decorator/common/IsNotIn.ts new file mode 100644 index 000000000..255fb68e3 --- /dev/null +++ b/src/decorator/common/IsNotIn.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_NOT_IN = "isNotIn"; + +/** + * Checks if given value not in a array of allowed values. + */ +export function isNotIn(value: unknown, possibleValues: unknown[]): boolean { + return !(possibleValues instanceof Array) || !possibleValues.some(possibleValue => possibleValue === value); +} + +/** + * Checks if given value not in a array of allowed values. + */ +export function IsNotIn(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NOT_IN, + constraints: [values], + validator: { + validate: (value, args) => isNotIn(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property should not be one of the following values: $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsOptional.ts b/src/decorator/common/IsOptional.ts new file mode 100644 index 000000000..82fabdcd9 --- /dev/null +++ b/src/decorator/common/IsOptional.ts @@ -0,0 +1,23 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { ValidationMetadataArgs } from "../../metadata/ValidationMetadataArgs"; +import { ValidationTypes } from "../../validation/ValidationTypes"; +import { ValidationMetadata } from "../../metadata/ValidationMetadata"; +import { getMetadataStorage } from "../../metadata/MetadataStorage"; + +/** + * Checks if value is missing and if so, ignores all validators. + */ +export function IsOptional(validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: Object, propertyName: string) { + const args: ValidationMetadataArgs = { + type: ValidationTypes.CONDITIONAL_VALIDATION, + target: object.constructor, + propertyName: propertyName, + constraints: [(object: any, value: any) => { + return object[propertyName] !== null && object[propertyName] !== undefined; + }], + validationOptions: validationOptions + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/NotEquals.ts b/src/decorator/common/NotEquals.ts new file mode 100644 index 000000000..a1c252470 --- /dev/null +++ b/src/decorator/common/NotEquals.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const NOT_EQUALS = "notEquals"; + +/** + * Checks if value does not match ("!==") the comparison. + */ +export function notEquals(value: unknown, comparison: unknown): boolean { + return value !== comparison; +} + +/** + * Checks if value does not match ("!==") the comparison. + */ +export function NotEquals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: NOT_EQUALS, + constraints: [comparison], + validator: { + validate: (value, args) => notEquals(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property should not be equal to $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/common/Validate.ts b/src/decorator/common/Validate.ts new file mode 100644 index 000000000..2de7ee2b5 --- /dev/null +++ b/src/decorator/common/Validate.ts @@ -0,0 +1,43 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { ValidationMetadataArgs } from "../../metadata/ValidationMetadataArgs"; +import { ValidationMetadata } from "../../metadata/ValidationMetadata"; +import { getMetadataStorage } from "../../metadata/MetadataStorage"; +import { ValidationTypes } from "../../validation/ValidationTypes"; +import { ConstraintMetadata } from "../../metadata/ConstraintMetadata"; + +/** + * Registers custom validator class. + */ +export function ValidatorConstraint(options?: { name?: string, async?: boolean }) { + return function (target: Function) { + const isAsync = options && options.async ? true : false; + let name = options && options.name ? options.name : ""; + if (!name) { + name = (target as any).name; + if (!name) // generate name if it was not given + name = name.replace(/\.?([A-Z]+)/g, (x, y) => "_" + y.toLowerCase()).replace(/^_/, ""); + } + const metadata = new ConstraintMetadata(target, name, isAsync); + getMetadataStorage().addConstraintMetadata(metadata); + }; +} + +/** + * Performs validation based on the given custom validation class. + * Validation class must be decorated with ValidatorConstraint decorator. + */ +export function Validate(constraintClass: Function, validationOptions?: ValidationOptions): PropertyDecorator; +export function Validate(constraintClass: Function, constraints?: any[], validationOptions?: ValidationOptions): PropertyDecorator; +export function Validate(constraintClass: Function, constraintsOrValidationOptions?: any[] | ValidationOptions, maybeValidationOptions?: ValidationOptions): PropertyDecorator { + return function (object: Object, propertyName: string) { + const args: ValidationMetadataArgs = { + type: ValidationTypes.CUSTOM_VALIDATION, + target: object.constructor, + propertyName: propertyName, + constraintCls: constraintClass, + constraints: constraintsOrValidationOptions instanceof Array ? constraintsOrValidationOptions as any[] : undefined, + validationOptions: !(constraintsOrValidationOptions instanceof Array) ? constraintsOrValidationOptions as ValidationOptions : maybeValidationOptions + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/ValidateBy.ts b/src/decorator/common/ValidateBy.ts new file mode 100644 index 000000000..1d15a43ad --- /dev/null +++ b/src/decorator/common/ValidateBy.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { registerDecorator } from "../../register-decorator"; +import { ValidationArguments } from "../../validation/ValidationArguments"; +import { ValidatorConstraintInterface } from "../../validation/ValidatorConstraintInterface"; + +export interface ValidateByOptions { + name: string; + constraints?: any[]; + validator: ValidatorConstraintInterface | Function; + async?: boolean; +} + +export function buildMessage( + impl: (eachPrefix: string, args?: ValidationArguments) => string, + validationOptions?: ValidationOptions) + : (validationArguments?: ValidationArguments) => string { + return (validationArguments?: ValidationArguments) => { + const eachPrefix = validationOptions && validationOptions.each + ? "each value in " + : ""; + return impl(eachPrefix, validationArguments); + }; +} + +export function ValidateBy(options: ValidateByOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: Object, propertyName: string) { + registerDecorator({ + name: options.name, + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: options.constraints, + validator: options.validator + }); + }; +} diff --git a/src/decorator/common/ValidateIf.ts b/src/decorator/common/ValidateIf.ts new file mode 100644 index 000000000..e1180f7f6 --- /dev/null +++ b/src/decorator/common/ValidateIf.ts @@ -0,0 +1,21 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { ValidationMetadataArgs } from "../../metadata/ValidationMetadataArgs"; +import { ValidationTypes } from "../../validation/ValidationTypes"; +import { ValidationMetadata } from "../../metadata/ValidationMetadata"; +import { getMetadataStorage } from "../../metadata/MetadataStorage"; + +/** + * Objects / object arrays marked with this decorator will also be validated. + */ +export function ValidateIf(condition: (object: any, value: any) => boolean, validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: Object, propertyName: string) { + const args: ValidationMetadataArgs = { + type: ValidationTypes.CONDITIONAL_VALIDATION, + target: object.constructor, + propertyName: propertyName, + constraints: [condition], + validationOptions: validationOptions + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/ValidateNested.ts b/src/decorator/common/ValidateNested.ts new file mode 100644 index 000000000..8ee0ac9c1 --- /dev/null +++ b/src/decorator/common/ValidateNested.ts @@ -0,0 +1,24 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { ValidationMetadataArgs } from "../../metadata/ValidationMetadataArgs"; +import { ValidationTypes } from "../../validation/ValidationTypes"; +import { ValidationMetadata } from "../../metadata/ValidationMetadata"; +import { getMetadataStorage } from "../../metadata/MetadataStorage"; + +/** + * Objects / object arrays marked with this decorator will also be validated. + */ +export function ValidateNested(validationOptions?: ValidationOptions): PropertyDecorator { + const opts: ValidationOptions = { ...validationOptions }; + const eachPrefix = opts.each ? "each value in " : ""; + opts.message = opts.message || eachPrefix + "nested property $property must be either object or array"; + + return function (object: Object, propertyName: string) { + const args: ValidationMetadataArgs = { + type: ValidationTypes.NESTED_VALIDATION, + target: object.constructor, + propertyName: propertyName, + validationOptions: opts, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/ValidatePromise.ts b/src/decorator/common/ValidatePromise.ts new file mode 100644 index 000000000..aeb90f417 --- /dev/null +++ b/src/decorator/common/ValidatePromise.ts @@ -0,0 +1,20 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { ValidationMetadataArgs } from "../../metadata/ValidationMetadataArgs"; +import { ValidationTypes } from "../../validation/ValidationTypes"; +import { ValidationMetadata } from "../../metadata/ValidationMetadata"; +import { getMetadataStorage } from "../../metadata/MetadataStorage"; + +/** + * Resolve promise before validation + */ +export function ValidatePromise(validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: Object, propertyName: string) { + const args: ValidationMetadataArgs = { + type: ValidationTypes.PROMISE_VALIDATION, + target: object.constructor, + propertyName: propertyName, + validationOptions: validationOptions, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/date/MaxDate.ts b/src/decorator/date/MaxDate.ts new file mode 100644 index 000000000..2f7e366cf --- /dev/null +++ b/src/decorator/date/MaxDate.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const MAX_DATE = "maxDate"; + + /** + * Checks if the value is a date that's before the specified date. + */ +export function maxDate(date: unknown, maxDate: Date): boolean { + return date instanceof Date && date.getTime() <= maxDate.getTime(); +} + +/** + * Checks if the value is a date that's after the specified date. + */ +export function MaxDate(date: Date, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MAX_DATE, + constraints: [date], + validator: { + validate: (value, args) => maxDate(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => "maximal allowed date for " + eachPrefix + "$property is $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/date/MinDate.ts b/src/decorator/date/MinDate.ts new file mode 100644 index 000000000..aa323b486 --- /dev/null +++ b/src/decorator/date/MinDate.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const MIN_DATE = "minDate"; + +/** + * Checks if the value is a date that's after the specified date. + */ +export function minDate(date: unknown, minDate: Date): boolean { + return date instanceof Date && date.getTime() >= minDate.getTime(); +} + +/** + * Checks if the value is a date that's after the specified date. + */ +export function MinDate(date: Date, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MIN_DATE, + constraints: [date], + validator: { + validate: (value, args) => minDate(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => "minimal allowed date for " + eachPrefix + "$property is $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/decorators.ts b/src/decorator/decorators.ts index bcafe8d7e..bdff4a8ca 100644 --- a/src/decorator/decorators.ts +++ b/src/decorator/decorators.ts @@ -1,1411 +1,125 @@ -import {ValidationTypes} from "../validation/ValidationTypes"; -import {IsNumberOptions} from "../validation/ValidationTypeOptions"; -import {ValidationOptions} from "./ValidationOptions"; -import {ValidationMetadata} from "../metadata/ValidationMetadata"; -import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs"; -import {ConstraintMetadata} from "../metadata/ConstraintMetadata"; -import { getMetadataStorage } from ".."; - // ------------------------------------------------------------------------- // System // ------------------------------------------------------------------------- -/** - * Registers custom validator class. - */ -export function ValidatorConstraint(options?: { name?: string, async?: boolean }) { - return function(target: Function) { - const isAsync = options && options.async ? true : false; - let name = options && options.name ? options.name : ""; - if (!name) { - name = (target as any).name; - if (!name) // generate name if it was not given - name = name.replace(/\.?([A-Z]+)/g, (x, y) => "_" + y.toLowerCase()).replace(/^_/, ""); - } - const metadata = new ConstraintMetadata(target, name, isAsync); - getMetadataStorage().addConstraintMetadata(metadata); - }; -} - -/** - * Performs validation based on the given custom validation class. - * Validation class must be decorated with ValidatorConstraint decorator. - */ -export function Validate(constraintClass: Function, validationOptions?: ValidationOptions): Function; -export function Validate(constraintClass: Function, constraints?: any[], validationOptions?: ValidationOptions): Function; -export function Validate(constraintClass: Function, constraintsOrValidationOptions?: any[]|ValidationOptions, maybeValidationOptions?: ValidationOptions): Function { - return function(object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CUSTOM_VALIDATION, - target: object.constructor, - propertyName: propertyName, - constraintCls: constraintClass, - constraints: constraintsOrValidationOptions instanceof Array ? constraintsOrValidationOptions as any[] : undefined, - validationOptions: !(constraintsOrValidationOptions instanceof Array) ? constraintsOrValidationOptions as ValidationOptions : maybeValidationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Objects / object arrays marked with this decorator will also be validated. - */ -export function ValidateNested(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.NESTED_VALIDATION, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Objects / object arrays marked with this decorator will also be validated. - */ -export function ValidatePromise(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.PROMISE_VALIDATION, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * If object has both allowed and not allowed properties a validation error will be thrown. - */ -export function Allow(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.WHITELIST, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - - -/** - * Objects / object arrays marked with this decorator will also be validated. - */ -export function ValidateIf(condition: (object: any, value: any) => boolean, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CONDITIONAL_VALIDATION, - target: object.constructor, - propertyName: propertyName, - constraints: [condition], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - // ------------------------------------------------------------------------- // Common checkers // ------------------------------------------------------------------------- -/** - * Checks if given value is defined (!== undefined, !== null). - */ -export function IsDefined(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DEFINED, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value match ("===") the comparison. - */ -export function Equals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.EQUALS, - target: object.constructor, - propertyName: propertyName, - constraints: [comparison], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value does not match ("!==") the comparison. - */ -export function NotEquals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.NOT_EQUALS, - target: object.constructor, - propertyName: propertyName, - constraints: [comparison], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if given value is empty (=== '', === null, === undefined). - */ -export function IsEmpty(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_EMPTY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if given value is not empty (!== '', !== null, !== undefined). - */ -export function IsNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NOT_EMPTY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if value is in a array of allowed values. - */ -export function IsIn(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_IN, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if value is not in a array of disallowed values. - */ -export function IsNotIn(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NOT_IN, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if value is missing and if so, ignores all validators. - */ -export function IsOptional(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CONDITIONAL_VALIDATION, - target: object.constructor, - propertyName: propertyName, - constraints: [(object: any, value: any) => { - return object[propertyName] !== null && object[propertyName] !== undefined; - }], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -// ------------------------------------------------------------------------- -// Type checkers -// ------------------------------------------------------------------------- - -/** - * Checks if a value is a boolean. - */ -export function IsBoolean(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BOOLEAN, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a latitude,longitude. - */ -export function IsLatLong(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_LATLONG, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a latitude,longitude. - */ -export function IsLatitude(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_LATITUDE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a latitude,longitude. - */ -export function IsLongitude(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_LONGITUDE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a date. - */ -export function IsDate(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DATE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a number. - */ -export function IsNumber(options: IsNumberOptions = {}, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NUMBER, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is an integer number. - */ -export function IsInt(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_INT, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a string. - */ -export function IsString(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_STRING, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -export function IsDateString(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DATE_STRING, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is an array. - */ -export function IsArray(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ARRAY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a number enum. - */ -export function IsEnum(entity: Object, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ENUM, - target: object.constructor, - propertyName: propertyName, - constraints: [entity], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - +export * from "./common/Allow"; +export * from "./common/IsDefined"; +export * from "./common/IsOptional"; +export * from "./common/Validate"; +export * from "./common/ValidateBy"; +export * from "./common/ValidateIf"; +export * from "./common/ValidateNested"; +export * from "./common/ValidatePromise"; +export * from "./common/IsLatLong"; +export * from "./common/IsLatitude"; +export * from "./common/IsLongitude"; +export * from "./common/Equals"; +export * from "./common/NotEquals"; +export * from "./common/IsEmpty"; +export * from "./common/IsNotEmpty"; +export * from "./common/IsIn"; +export * from "./common/IsNotIn"; // ------------------------------------------------------------------------- // Number checkers // ------------------------------------------------------------------------- -/** - * Checks if the value is a number that's divisible by another. - */ -export function IsDivisibleBy(num: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DIVISIBLE_BY, - target: object.constructor, - propertyName: propertyName, - constraints: [num], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a positive number. - */ -export function IsPositive(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_POSITIVE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a negative number. - */ -export function IsNegative(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NEGATIVE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} -/** - * Checks if the given number is greater than or equal to given number. - */ -export function Min(min: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MIN, - target: object.constructor, - propertyName: propertyName, - constraints: [min], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the given number is less than or equal to given number. - */ -export function Max(max: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MAX, - target: object.constructor, - propertyName: propertyName, - constraints: [max], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from "./number/IsDivisibleBy"; +export * from "./number/IsPositive"; +export * from "./number/IsNegative"; +export * from "./number/Max"; +export * from "./number/Min"; // ------------------------------------------------------------------------- // Date checkers // ------------------------------------------------------------------------- -/** - * Checks if the value is a date that's after the specified date. - */ -export function MinDate(date: Date, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MIN_DATE, - target: object.constructor, - propertyName: propertyName, - constraints: [date], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a date that's before the specified date. - */ -export function MaxDate(date: Date, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MAX_DATE, - target: object.constructor, - propertyName: propertyName, - constraints: [date], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from "./date/MinDate"; +export * from "./date/MaxDate"; // ------------------------------------------------------------------------- -// String-as-types checkers +// String checkers // ------------------------------------------------------------------------- -/** - * Checks if a string is a boolean. - */ -export function IsBooleanString(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BOOLEAN_STRING, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a number. - */ -export function IsNumberString(options?: ValidatorJS.IsNumericOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NUMBER_STRING, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from "./string/Contains"; +export * from "./string/NotContains"; +export * from "./string/IsAlpha"; +export * from "./string/IsAlphanumeric"; +export * from "./string/IsDecimal"; +export * from "./string/IsAscii"; +export * from "./string/IsBase64"; +export * from "./string/IsByteLength"; +export * from "./string/IsCreditCard"; +export * from "./string/IsCurrency"; +export * from "./string/IsEmail"; +export * from "./string/IsFQDN"; +export * from "./string/IsFullWidth"; +export * from "./string/IsHalfWidth"; +export * from "./string/IsVariableWidth"; +export * from "./string/IsHexColor"; +export * from "./string/IsHexadecimal"; +export * from "./string/IsMacAddress"; +export * from "./string/IsIP"; +export * from "./string/IsPort"; +export * from "./string/IsISBN"; +export * from "./string/IsISIN"; +export * from "./string/IsISO8601"; +export * from "./string/IsJSON"; +export * from "./string/IsJWT"; +export * from "./string/IsLowercase"; +export * from "./string/IsMobilePhone"; +export * from "./string/IsISO31661Alpha2"; +export * from "./string/IsISO31661Alpha3"; +export * from "./string/IsMongoId"; +export * from "./string/IsMultibyte"; +export * from "./string/IsSurrogatePair"; +export * from "./string/IsUrl"; +export * from "./string/IsUUID"; +export * from "./string/IsFirebasePushId"; +export * from "./string/IsUppercase"; +export * from "./string/Length"; +export * from "./string/MaxLength"; +export * from "./string/MinLength"; +export * from "./string/Matches"; +export * from "./string/IsPhoneNumber"; +export * from "./string/IsMilitaryTime"; +export * from "./string/IsHash"; +export * from "./string/IsISSN"; +export * from "./string/IsDateString"; +export * from "./string/IsBooleanString"; +export * from "./string/IsNumberString"; // ------------------------------------------------------------------------- -// String checkers +// Type checkers // ------------------------------------------------------------------------- -/** - * Checks if the string contains the seed. - */ -export function Contains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [seed], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string does not contain the seed. - */ -export function NotContains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.NOT_CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [seed], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains only letters (a-zA-Z). - */ -export function IsAlpha(locale?: string, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ALPHA, - target: object.constructor, - propertyName: propertyName, - constraints: [locale], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains only letters and numbers. - */ -export function IsAlphanumeric(locale?: string, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ALPHANUMERIC, - target: object.constructor, - propertyName: propertyName, - constraints: [locale], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the given number is a valid decimal. - */ -export function IsDecimal(options?: ValidatorJS.IsDecimalOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DECIMAL, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains ASCII chars only. - */ -export function IsAscii(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ASCII, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a string is base64 encoded. - */ -export function IsBase64(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BASE64, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length (in bytes) falls in a range. - */ -export function IsByteLength(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BYTE_LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [min, max], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a credit card. - */ -export function IsCreditCard(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_CREDIT_CARD, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid currency amount. - */ -export function IsCurrency(options?: ValidatorJS.IsCurrencyOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_CURRENCY, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an email. - */ -export function IsEmail(options?: ValidatorJS.IsEmailOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_EMAIL, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a fully qualified domain name (e.g. domain.com). - */ -export function IsFQDN(options?: ValidatorJS.IsFQDNOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_FQDN, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains any full-width chars. - */ -export function IsFullWidth(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_FULL_WIDTH, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains any half-width chars. - */ -export function IsHalfWidth(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_HALF_WIDTH, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains a mixture of full and half-width chars. - */ -export function IsVariableWidth(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_VARIABLE_WIDTH, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a hexadecimal color. - */ -export function IsHexColor(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_HEX_COLOR, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a hexadecimal number. - */ -export function IsHexadecimal(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_HEXADECIMAL, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is MAC Address. - */ -export function IsMACAddress(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MAC_ADDRESS, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an IP (version 4 or 6). - */ -export function IsIP(version?: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_IP, - target: object.constructor, - propertyName: propertyName, - constraints: [version], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Check if the string is a valid port number. - */ -export function IsPort(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_PORT, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an ISBN (version 10 or 13). - */ -export function IsISBN(version?: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISBN, - target: object.constructor, - propertyName: propertyName, - constraints: [version], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an ISIN (stock/security identifier). - */ -export function IsISIN(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISIN, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid ISO 8601 date. Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. - */ -export function IsISO8601(options?: ValidatorJS.IsISO8601Options, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISO8601, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is valid JSON (note: uses JSON.parse). - */ -export function IsJSON(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_JSON, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is valid JWT. - */ -export function IsJWT(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_JWT, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a valid object. - */ -export function IsObject(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_OBJECT, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a valid object & not empty. - */ -export function IsNotEmptyObject(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NOT_EMPTY_OBJECT, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is lowercase. - */ -export function IsLowercase(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_LOWERCASE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a mobile phone number (locale is one of ['zh-CN', 'zh-TW', 'en-ZA', 'en-AU', 'en-HK', - * 'pt-PT', 'fr-FR', 'el-GR', 'en-GB', 'en-US', 'en-ZM', 'ru-RU', 'nb-NO', 'nn-NO', 'vi-VN', 'en-NZ']). - */ -export function IsMobilePhone(locale: string, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MOBILE_PHONE, - target: object.constructor, - propertyName: propertyName, - constraints: [locale], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid phone number. - * @param {string} region 2 characters uppercase country code (e.g. DE, US, CH). - * If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region. - * See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33} - */ -export function IsPhoneNumber(region: string, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_PHONE_NUMBER, - target: object.constructor, - propertyName: propertyName, - constraints: [region], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Check if the string is a valid ISO 3166-1 alpha-2. - * See heck if [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) officially assigned country code. - */ -export function IsISO31661Alpha2(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISO31661_ALPHA_2, - target: object.constructor, - propertyName: propertyName, - constraints: [], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Check if the string is a valid ISO 3166-1 alpha-3. - * See heck if [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) officially assigned country code. - */ -export function IsISO31661Alpha3(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISO31661_ALPHA_3, - target: object.constructor, - propertyName: propertyName, - constraints: [], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. - */ -export function IsMongoId(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MONGO_ID, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains one or more multibyte chars. - */ -export function IsMultibyte(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MULTIBYTE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains any surrogate pairs chars. - */ -export function IsSurrogatePair(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_SURROGATE_PAIR, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an url. - */ -export function IsUrl(options?: ValidatorJS.IsURLOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_URL, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a UUID (version 3, 4 or 5). - */ -export function IsUUID(version?: "3"|"4"|"5"|"all", validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_UUID, - target: object.constructor, - propertyName: propertyName, - constraints: [version], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a PushId - */ -export function IsFirebasePushId(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_FIREBASE_PUSH_ID, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is uppercase. - */ -export function IsUppercase(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_UPPERCASE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. - */ -export function Length(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [min, max], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. - */ -export function MinLength(min: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MIN_LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [min], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. - */ -export function MaxLength(max: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MAX_LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [max], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). - */ -export function Matches(pattern: RegExp, validationOptions?: ValidationOptions): Function; -export function Matches(pattern: RegExp, modifiers?: string, validationOptions?: ValidationOptions): Function; -export function Matches(pattern: RegExp, modifiersOrAnnotationOptions?: string|ValidationOptions, validationOptions?: ValidationOptions): Function { - let modifiers: string; - if (modifiersOrAnnotationOptions && modifiersOrAnnotationOptions instanceof Object && !validationOptions) { - validationOptions = modifiersOrAnnotationOptions as ValidationOptions; - } else { - modifiers = modifiersOrAnnotationOptions as string; - } - - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MATCHES, - target: object.constructor, - propertyName: propertyName, - constraints: [pattern, modifiers], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string correctly represents a time in the format HH:MM - */ -export function IsMilitaryTime(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MILITARY_TIME, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a mobile phone number (locale is one of ['zh-CN', 'zh-TW', 'en-ZA', 'en-AU', 'en-HK', - * 'pt-PT', 'fr-FR', 'el-GR', 'en-GB', 'en-US', 'en-ZM', 'ru-RU', 'nb-NO', 'nn-NO', 'vi-VN', 'en-NZ']). - */ -export function IsHash(algorithm: string, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_HASH, - target: object.constructor, - propertyName: propertyName, - constraints: [algorithm], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - - -/** - * Checks if the string is a valid ISSN. - */ -export function IsISSN(options?: ValidatorJS.IsISSNOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISSN, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from "./typechecker/IsBoolean"; +export * from "./typechecker/IsDate"; +export * from "./typechecker/IsNumber"; +export * from "./typechecker/IsEnum"; +export * from "./typechecker/IsInt"; +export * from "./typechecker/IsString"; +export * from "./typechecker/IsArray"; +export * from "./typechecker/IsObject"; // ------------------------------------------------------------------------- // Array checkers // ------------------------------------------------------------------------- -/** - * Checks if array contains all values from the given array of values. - */ -export function ArrayContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if array does not contain any of the given values. - */ -export function ArrayNotContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_NOT_CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from "./array/ArrayContains"; +export * from "./array/ArrayNotContains"; +export * from "./array/ArrayNotEmpty"; +export * from "./array/ArrayMinSize"; +export * from "./array/ArrayMaxSize"; +export * from "./array/ArrayUnique"; -/** - * Checks if given array is not empty. - */ -export function ArrayNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_NOT_EMPTY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if array's length is as minimal this number. - */ -export function ArrayMinSize(min: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_MIN_SIZE, - target: object.constructor, - propertyName: propertyName, - constraints: [min], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if array's length is as maximal this number. - */ -export function ArrayMaxSize(max: number, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_MAX_SIZE, - target: object.constructor, - propertyName: propertyName, - constraints: [max], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if all array's values are unique. Comparison for objects is reference-based. - */ -export function ArrayUnique(validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_UNIQUE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} +// ------------------------------------------------------------------------- +// Object checkers +// ------------------------------------------------------------------------- -/** - * Checks if the value is an instance of the specified object. - */ -export function IsInstance(targetType: new (...args: any[]) => any, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_INSTANCE, - target: object.constructor, - propertyName: propertyName, - constraints: [targetType], - validationOptions: validationOptions - }; - getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from "./object/IsNotEmptyObject"; +export * from "./object/IsInstance"; diff --git a/src/decorator/number/IsDivisibleBy.ts b/src/decorator/number/IsDivisibleBy.ts new file mode 100644 index 000000000..765017739 --- /dev/null +++ b/src/decorator/number/IsDivisibleBy.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_DIVISIBLE_BY = "isDivisibleBy"; + +/** + * Checks if value is a number that's divisible by another. + */ +export function isDivisibleBy(value: unknown, num: number): boolean { + return typeof value === "number" && + typeof num === "number" && + validator.isDivisibleBy(String(value), num); +} + +/** + * Checks if value is a number that's divisible by another. + */ +export function IsDivisibleBy(num: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DIVISIBLE_BY, + constraints: [num], + validator: { + validate: (value, args) => isDivisibleBy(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be divisible by $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/number/IsNegative.ts b/src/decorator/number/IsNegative.ts new file mode 100644 index 000000000..c3f7eb130 --- /dev/null +++ b/src/decorator/number/IsNegative.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_NEGATIVE = "isNegative"; + +/** + * Checks if the value is a negative number. + */ +export function isNegative(value: unknown): boolean { + return typeof value === "number" && value < 0; +} + +/** + * Checks if the value is a negative number. + */ +export function IsNegative(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NEGATIVE, + validator: { + validate: (value, args) => isNegative(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a negative number", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/number/IsPositive.ts b/src/decorator/number/IsPositive.ts new file mode 100644 index 000000000..94d887e0f --- /dev/null +++ b/src/decorator/number/IsPositive.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_POSITIVE = "isPositive"; + +/** + * Checks if the value is a positive number. + */ +export function isPositive(value: unknown): boolean { + return typeof value === "number" && value > 0; +} + +/** + * Checks if the value is a positive number. + */ +export function IsPositive(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_POSITIVE, + validator: { + validate: (value, args) => isPositive(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a positive number", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/number/Max.ts b/src/decorator/number/Max.ts new file mode 100644 index 000000000..311e20c27 --- /dev/null +++ b/src/decorator/number/Max.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const MAX = "max"; + +/** + * Checks if the first number is less than or equal to the second. + */ +export function max(num: unknown, max: number): boolean { + return typeof num === "number" && typeof max === "number" && num <= max; +} + +/** + * Checks if the first number is less than or equal to the second. + */ +export function Max(maxValue: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MAX, + constraints: [maxValue], + validator: { + validate: (value, args) => max(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must not be greater than $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/number/Min.ts b/src/decorator/number/Min.ts new file mode 100644 index 000000000..ce3278b27 --- /dev/null +++ b/src/decorator/number/Min.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const MIN = "min"; + +/** + * Checks if the first number is greater than or equal to the second. + */ +export function min(num: unknown, min: number): boolean { + return typeof num === "number" && typeof min === "number" && num >= min; +} + +/** + * Checks if the first number is greater than or equal to the second. + */ +export function Min(minValue: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MIN, + constraints: [minValue], + validator: { + validate: (value, args) => min(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must not be less than $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/object/IsInstance.ts b/src/decorator/object/IsInstance.ts new file mode 100644 index 000000000..962744047 --- /dev/null +++ b/src/decorator/object/IsInstance.ts @@ -0,0 +1,39 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_INSTANCE = "isInstance"; + +/** + * Checks if the value is an instance of the specified object. + */ +export function isInstance(object: unknown, targetTypeConstructor: new (...args: any[]) => any) { + return targetTypeConstructor + && typeof targetTypeConstructor === "function" + && object instanceof targetTypeConstructor; +} + +/** + * Checks if the value is an instance of the specified object. + */ +export function IsInstance(targetType: new (...args: any[]) => any, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_INSTANCE, + constraints: [targetType], + validator: { + validate: (value, args) => isInstance(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix, args) => { + if (args.constraints[0]) { + return eachPrefix + `$property must be an instance of ${args.constraints[0].name}`; + } else { + return eachPrefix + `${IS_INSTANCE} decorator expects and object as value, but got falsy value.`; + } + }, + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/object/IsNotEmptyObject.ts b/src/decorator/object/IsNotEmptyObject.ts new file mode 100644 index 000000000..4085adcb3 --- /dev/null +++ b/src/decorator/object/IsNotEmptyObject.ts @@ -0,0 +1,42 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import { isObject } from "../typechecker/IsObject"; + +export const IS_NOT_EMPTY_OBJECT = "isNotEmptyObject"; + +/** + * Checks if the value is valid Object & not empty. + * Returns false if the value is not an object or an empty valid object. + */ +export function isNotEmptyObject(value: unknown): boolean { + if (!isObject(value)) { + return false; + } + for (const key in value) { + if (value.hasOwnProperty(key)) { + return true; + } + } + + return false; +} + +/** + * Checks if the value is valid Object & not empty. + * Returns false if the value is not an object or an empty valid object. + */ +export function IsNotEmptyObject(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NOT_EMPTY_OBJECT, + validator: { + validate: (value, args) => isNotEmptyObject(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a non-empty object", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/Contains.ts b/src/decorator/string/Contains.ts new file mode 100644 index 000000000..db14394e6 --- /dev/null +++ b/src/decorator/string/Contains.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const CONTAINS = "contains"; + +/** + * Checks if the string contains the seed. + * If given value is not a string, then it returns false. + */ +export function contains(value: unknown, seed: string): boolean { + return typeof value === "string" && validator.contains(value, seed); +} + +/** + * Checks if the string contains the seed. + * If given value is not a string, then it returns false. + */ +export function Contains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: CONTAINS, + constraints: [seed], + validator: { + validate: (value, args) => contains(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain a $constraint1 string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsAlpha.ts b/src/decorator/string/IsAlpha.ts new file mode 100644 index 000000000..4b1ce9a13 --- /dev/null +++ b/src/decorator/string/IsAlpha.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_ALPHA = "isAlpha"; + +/** + * Checks if the string contains only letters (a-zA-Z). + * If given value is not a string, then it returns false. + */ +export function isAlpha(value: unknown, locale?: ValidatorJS.AlphaLocale): boolean { + return typeof value === "string" && ValidatorJS.isAlpha(value, locale); +} + +/** + * Checks if the string contains only letters (a-zA-Z). + * If given value is not a string, then it returns false. + */ +export function IsAlpha(locale?: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ALPHA, + constraints: [locale], + validator: { + validate: (value, args) => isAlpha(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain only letters (a-zA-Z)", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsAlphanumeric.ts b/src/decorator/string/IsAlphanumeric.ts new file mode 100644 index 000000000..8e3e40d7a --- /dev/null +++ b/src/decorator/string/IsAlphanumeric.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_ALPHANUMERIC = "isAlphanumeric"; + +/** + * Checks if the string contains only letters and numbers. + * If given value is not a string, then it returns false. + */ +export function isAlphanumeric(value: unknown, locale?: ValidatorJS.AlphanumericLocale): boolean { + return typeof value === "string" && ValidatorJS.isAlphanumeric(value, locale); +} + +/** + * Checks if the string contains only letters and numbers. + * If given value is not a string, then it returns false. + */ +export function IsAlphanumeric(locale?: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ALPHANUMERIC, + constraints: [locale], + validator: { + validate: (value, args) => isAlphanumeric(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain only letters and numbers", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsAscii.ts b/src/decorator/string/IsAscii.ts new file mode 100644 index 000000000..682afeffd --- /dev/null +++ b/src/decorator/string/IsAscii.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_ASCII = "isAscii"; + +/** + * Checks if the string contains ASCII chars only. + * If given value is not a string, then it returns false. + */ +export function isAscii(value: unknown): boolean { + return typeof value === "string" && validator.isAscii(value); +} + +/** + * Checks if the string contains ASCII chars only. + * If given value is not a string, then it returns false. + */ +export function IsAscii(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ASCII, + validator: { + validate: (value, args) => isAscii(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain only ASCII characters", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBase64.ts b/src/decorator/string/IsBase64.ts new file mode 100644 index 000000000..ba2a6ae6e --- /dev/null +++ b/src/decorator/string/IsBase64.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_BASE64 = "isBase64"; + +/** + * Checks if a string is base64 encoded. + * If given value is not a string, then it returns false. + */ +export function isBase64(value: unknown): boolean { + return typeof value === "string" && validator.isBase64(value); +} + +/** + * Checks if a string is base64 encoded. + * If given value is not a string, then it returns false. + */ +export function IsBase64(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BASE64, + validator: { + validate: (value, args) => isBase64(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be base64 encoded", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBooleanString.ts b/src/decorator/string/IsBooleanString.ts new file mode 100644 index 000000000..fd2ab0b85 --- /dev/null +++ b/src/decorator/string/IsBooleanString.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_BOOLEAN_STRING = "isBooleanString"; + +/** + * Checks if a string is a boolean. + * If given value is not a string, then it returns false. + */ +export function isBooleanString(value: unknown): boolean { + return typeof value === "string" && validator.isBoolean(value); +} + +/** + * Checks if a string is a boolean. + * If given value is not a string, then it returns false. + */ +export function IsBooleanString(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BOOLEAN_STRING, + validator: { + validate: (value, args) => isBooleanString(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a boolean string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsByteLength.ts b/src/decorator/string/IsByteLength.ts new file mode 100644 index 000000000..cce52e748 --- /dev/null +++ b/src/decorator/string/IsByteLength.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_BYTE_LENGTH = "isByteLength"; + +/** + * Checks if the string's length (in bytes) falls in a range. + * If given value is not a string, then it returns false. + */ +export function isByteLength(value: unknown, min: number, max?: number): boolean { + return typeof value === "string" && validator.isByteLength(value, { min, max }); +} + +/** + * Checks if the string's length (in bytes) falls in a range. + * If given value is not a string, then it returns false. + */ +export function IsByteLength(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BYTE_LENGTH, + constraints: [min, max], + validator: { + validate: (value, args) => isByteLength(value, args.constraints[0], args.constraints[1]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property's byte length must fall into ($constraint1, $constraint2) range", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsCreditCard.ts b/src/decorator/string/IsCreditCard.ts new file mode 100644 index 000000000..693fae966 --- /dev/null +++ b/src/decorator/string/IsCreditCard.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_CREDIT_CARD = "isCreditCard"; + +/** + * Checks if the string is a credit card. + * If given value is not a string, then it returns false. + */ +export function isCreditCard(value: unknown): boolean { + return typeof value === "string" && validator.isCreditCard(value); +} + +/** + * Checks if the string is a credit card. + * If given value is not a string, then it returns false. + */ +export function IsCreditCard(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_CREDIT_CARD, + validator: { + validate: (value, args) => isCreditCard(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a credit card", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsCurrency.ts b/src/decorator/string/IsCurrency.ts new file mode 100644 index 000000000..7561228de --- /dev/null +++ b/src/decorator/string/IsCurrency.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_CURRENCY = "isCurrency"; + +/** + * Checks if the string is a valid currency amount. + * If given value is not a string, then it returns false. + */ +export function isCurrency(value: unknown, options?: ValidatorJS.IsCurrencyOptions): boolean { + return typeof value === "string" && ValidatorJS.isCurrency(value, options); +} + +/** + * Checks if the string is a valid currency amount. + * If given value is not a string, then it returns false. + */ +export function IsCurrency(options?: ValidatorJS.IsCurrencyOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_CURRENCY, + constraints: [options], + validator: { + validate: (value, args) => isCurrency(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a currency", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsDateString.ts b/src/decorator/string/IsDateString.ts new file mode 100644 index 000000000..c8cab8c3d --- /dev/null +++ b/src/decorator/string/IsDateString.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_DATE_STRING = "isDateString"; + +/** + * Checks if a given value is a ISOString date. + */ +export function isDateString(value: unknown): boolean { + const regex = /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[\+\-][0-2]\d(?:\:[0-5]\d)?)?$/g; + return typeof value === "string" && regex.test(value); +} + +/** + * Checks if a given value is a ISOString date. + */ +export function IsDateString(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DATE_STRING, + validator: { + validate: (value, args) => isDateString(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a ISOString", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsDecimal.ts b/src/decorator/string/IsDecimal.ts new file mode 100644 index 000000000..1fc5f56fe --- /dev/null +++ b/src/decorator/string/IsDecimal.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_DECIMAL = "isDecimal"; + +/** + * Checks if the string is a valid decimal. + * If given value is not a string, then it returns false. + */ +export function isDecimal(value: unknown, options?: ValidatorJS.IsDecimalOptions): boolean { + return typeof value === "string" && ValidatorJS.isDecimal(value, options); +} + +/** + * Checks if the string contains only letters and numbers. + * If given value is not a string, then it returns false. + */ +export function IsDecimal(options?: ValidatorJS.IsDecimalOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DECIMAL, + constraints: [options], + validator: { + validate: (value, args) => isDecimal(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property is not a valid decimal number.", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsEmail.ts b/src/decorator/string/IsEmail.ts new file mode 100644 index 000000000..532912cdd --- /dev/null +++ b/src/decorator/string/IsEmail.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_EMAIL = "isEmail"; + +/** + * Checks if the string is an email. + * If given value is not a string, then it returns false. + */ +export function isEmail(value: unknown, options?: ValidatorJS.IsEmailOptions): boolean { + return typeof value === "string" && ValidatorJS.isEmail(value, options); +} + +/** + * Checks if the string is an email. + * If given value is not a string, then it returns false. + */ +export function IsEmail(options?: ValidatorJS.IsEmailOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_EMAIL, + constraints: [options], + validator: { + validate: (value, args) => isEmail(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an email", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsFQDN.ts b/src/decorator/string/IsFQDN.ts new file mode 100644 index 000000000..0d8820011 --- /dev/null +++ b/src/decorator/string/IsFQDN.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_FQDN = "isFqdn"; + +/** + * Checks if the string is a fully qualified domain name (e.g. domain.com). + * If given value is not a string, then it returns false. + */ +export function isFQDN(value: unknown, options?: ValidatorJS.IsFQDNOptions): boolean { + return typeof value === "string" && ValidatorJS.isFQDN(value, options); +} + +/** + * Checks if the string is a fully qualified domain name (e.g. domain.com). + * If given value is not a string, then it returns false. + */ +export function IsFQDN(options?: ValidatorJS.IsFQDNOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_FQDN, + constraints: [options], + validator: { + validate: (value, args) => isFQDN(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a valid domain name", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsFirebasePushId.ts b/src/decorator/string/IsFirebasePushId.ts new file mode 100644 index 000000000..7dfb09618 --- /dev/null +++ b/src/decorator/string/IsFirebasePushId.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_FIREBASE_PUSH_ID = "IsFirebasePushId"; + +/** + * Checks if the string is a Firebase Push Id + * If given value is not a Firebase Push Id, it returns false + */ +export function isFirebasePushId(value: unknown): boolean { + const webSafeRegex = /^[a-zA-Z0-9_-]*$/; + return typeof value === "string" && value.length === 20 && webSafeRegex.test(value); +} + +/** + * Checks if the string is a Firebase Push Id + * If given value is not a Firebase Push Id, it returns false + */ +export function IsFirebasePushId(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_FIREBASE_PUSH_ID, + validator: { + validate: (value, args) => isFirebasePushId(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a Firebase Push Id", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsFullWidth.ts b/src/decorator/string/IsFullWidth.ts new file mode 100644 index 000000000..b466bdf33 --- /dev/null +++ b/src/decorator/string/IsFullWidth.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_FULL_WIDTH = "isFullWidth"; + +/** + * Checks if the string contains any full-width chars. + * If given value is not a string, then it returns false. + */ +export function isFullWidth(value: unknown): boolean { + return typeof value === "string" && validator.isFullWidth(value); +} + +/** + * Checks if the string contains any full-width chars. + * If given value is not a string, then it returns false. + */ +export function IsFullWidth(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_FULL_WIDTH, + validator: { + validate: (value, args) => isFullWidth(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain a full-width characters", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHalfWidth.ts b/src/decorator/string/IsHalfWidth.ts new file mode 100644 index 000000000..be1e12185 --- /dev/null +++ b/src/decorator/string/IsHalfWidth.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_HALF_WIDTH = "isHalfWidth"; + +/** + * Checks if the string contains any half-width chars. + * If given value is not a string, then it returns false. + */ +export function isHalfWidth(value: unknown): boolean { + return typeof value === "string" && validator.isHalfWidth(value); +} + +/** + * Checks if the string contains any full-width chars. + * If given value is not a string, then it returns false. + */ +export function IsHalfWidth(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HALF_WIDTH, + validator: { + validate: (value, args) => isHalfWidth(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain a half-width characters", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHash.ts b/src/decorator/string/IsHash.ts new file mode 100644 index 000000000..2a4cac401 --- /dev/null +++ b/src/decorator/string/IsHash.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_HASH = "isHash"; + +/** + * check if the string is a hash of type algorithm. + * Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', + * 'tiger160', 'tiger192', 'crc32', 'crc32b'] + */ +export function isHash(value: unknown, algorithm: ValidatorJS.HashAlgorithm): boolean { + return typeof value === "string" && ValidatorJS.isHash(value, algorithm); +} + +/** + * check if the string is a hash of type algorithm. + * Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', + * 'tiger160', 'tiger192', 'crc32', 'crc32b'] + */ +export function IsHash(algorithm: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HASH, + constraints: [algorithm], + validator: { + validate: (value, args) => isHash(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a hash of type $constraint1", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHexColor.ts b/src/decorator/string/IsHexColor.ts new file mode 100644 index 000000000..5ab0a8de9 --- /dev/null +++ b/src/decorator/string/IsHexColor.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_HEX_COLOR = "isHexColor"; + +/** + * Checks if the string is a hexadecimal color. + * If given value is not a string, then it returns false. + */ +export function isHexColor(value: unknown): boolean { + return typeof value === "string" && validator.isHexColor(value); +} + +/** + * Checks if the string is a hexadecimal color. + * If given value is not a string, then it returns false. + */ +export function IsHexColor(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HEX_COLOR, + validator: { + validate: (value, args) => isHexColor(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a hexadecimal color", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHexadecimal.ts b/src/decorator/string/IsHexadecimal.ts new file mode 100644 index 000000000..6b6bf7c11 --- /dev/null +++ b/src/decorator/string/IsHexadecimal.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_HEXADECIMAL = "isHexadecimal"; + +/** + * Checks if the string is a hexadecimal number. + * If given value is not a string, then it returns false. + */ +export function isHexadecimal(value: unknown): boolean { + return typeof value === "string" && validator.isHexadecimal(value); +} + +/** + * Checks if the string is a hexadecimal number. + * If given value is not a string, then it returns false. + */ +export function IsHexadecimal(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HEXADECIMAL, + validator: { + validate: (value, args) => isHexadecimal(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a hexadecimal number", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsIP.ts b/src/decorator/string/IsIP.ts new file mode 100644 index 000000000..3a05cc5c8 --- /dev/null +++ b/src/decorator/string/IsIP.ts @@ -0,0 +1,37 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export type IsIpVersion = "4" | "6" | 4 | 6; + +export const IS_IP = "isIp"; + +/** + * Checks if the string is an IP (version 4 or 6). + * If given value is not a string, then it returns false. + */ +export function isIP(value: unknown, version?: IsIpVersion): boolean { + const versionStr = version ? (`${version}` as "4" | "6") : undefined; + return typeof value === "string" && ValidatorJS.isIP(value, versionStr); +} + +/** + * Checks if the string is an IP (version 4 or 6). + * If given value is not a string, then it returns false. + */ +export function IsIP(version?: IsIpVersion, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_IP, + constraints: [version], + validator: { + validate: (value, args) => isIP(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an ip address", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISBN.ts b/src/decorator/string/IsISBN.ts new file mode 100644 index 000000000..58c68344e --- /dev/null +++ b/src/decorator/string/IsISBN.ts @@ -0,0 +1,37 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export type IsISBNVersion = "10" | "13" | 10 | 13; + +export const IS_ISBN = "isIsbn"; + +/** + * Checks if the string is an ISBN (version 10 or 13). + * If given value is not a string, then it returns false. + */ +export function isISBN(value: unknown, version?: IsISBNVersion): boolean { + const versionStr = version ? (`${version}` as "10" | "13") : undefined; + return typeof value === "string" && ValidatorJS.isISBN(value, versionStr); +} + +/** + * Checks if the string is an ISBN (version 10 or 13). + * If given value is not a string, then it returns false. + */ +export function IsISBN(version?: IsISBNVersion, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISBN, + constraints: [version], + validator: { + validate: (value, args) => isISBN(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an ISBN", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISIN.ts b/src/decorator/string/IsISIN.ts new file mode 100644 index 000000000..95f315743 --- /dev/null +++ b/src/decorator/string/IsISIN.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_ISIN = "isIsin"; + +/** + * Checks if the string is an ISIN (stock/security identifier). + * If given value is not a string, then it returns false. + */ +export function isISIN(value: unknown): boolean { + return typeof value === "string" && validator.isISIN(value); +} + +/** + * Checks if the string is an ISIN (stock/security identifier). + * If given value is not a string, then it returns false. + */ +export function IsISIN(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISIN, + validator: { + validate: (value, args) => isISIN(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an ISIN (stock/security identifier)", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISO31661Alpha2.ts b/src/decorator/string/IsISO31661Alpha2.ts new file mode 100644 index 000000000..79c7fe106 --- /dev/null +++ b/src/decorator/string/IsISO31661Alpha2.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_ISO31661_ALPHA_2 = "isISO31661Alpha2"; + +/** + * 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. + */ +export function isISO31661Alpha2(value: unknown): boolean { + return typeof value === "string" && validator.isISO31661Alpha2(value); +} + +/** + * 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. + */ +export function IsISO31661Alpha2(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISO31661_ALPHA_2, + validator: { + validate: (value, args) => isISO31661Alpha2(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a valid ISO31661 Alpha2 code", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISO31661Alpha3.ts b/src/decorator/string/IsISO31661Alpha3.ts new file mode 100644 index 000000000..094211470 --- /dev/null +++ b/src/decorator/string/IsISO31661Alpha3.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_ISO31661_ALPHA_3 = "isISO31661Alpha3"; + +/** + * 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. + */ +export function isISO31661Alpha3(value: unknown): boolean { + return typeof value === "string" && validator.isISO31661Alpha3(value); +} + +/** + * 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. + */ +export function IsISO31661Alpha3(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISO31661_ALPHA_3, + validator: { + validate: (value, args) => isISO31661Alpha3(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a valid ISO31661 Alpha3 code", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISO8601.ts b/src/decorator/string/IsISO8601.ts new file mode 100644 index 000000000..3faebc9c1 --- /dev/null +++ b/src/decorator/string/IsISO8601.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_ISO8601 = "isIso8601"; + +/** + * Checks if the string is a valid ISO 8601 date. + * If given value is not a string, then it returns false. + * Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. + */ +export function isISO8601(value: unknown, options?: ValidatorJS.IsISO8601Options): boolean { + return typeof value === "string" && ValidatorJS.isISO8601(value, options); +} + +/** + * Checks if the string is a valid ISO 8601 date. + * If given value is not a string, then it returns false. + * Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. + */ +export function IsISO8601(options?: ValidatorJS.IsISO8601Options, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISO8601, + constraints: [options], + validator: { + validate: (value, args) => isISO8601(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a valid ISO 8601 date string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISSN.ts b/src/decorator/string/IsISSN.ts new file mode 100644 index 000000000..ba3bab791 --- /dev/null +++ b/src/decorator/string/IsISSN.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_ISSN = "isISSN"; + +/** + * Checks if the string is a ISSN. + * If given value is not a string, then it returns false. + */ +export function isISSN(value: unknown, options?: ValidatorJS.IsISSNOptions): boolean { + return typeof value === "string" && ValidatorJS.isISSN(value, options); +} + +/** + * Checks if the string is a ISSN. + * If given value is not a string, then it returns false. + */ +export function IsISSN(options?: ValidatorJS.IsISSNOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISSN, + constraints: [options], + validator: { + validate: (value, args) => isISSN(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a ISSN", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsJSON.ts b/src/decorator/string/IsJSON.ts new file mode 100644 index 000000000..92f1805e4 --- /dev/null +++ b/src/decorator/string/IsJSON.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_JSON = "isJson"; + +/** + * Checks if the string is valid JSON (note: uses JSON.parse). + * If given value is not a string, then it returns false. + */ +export function isJSON(value: unknown): boolean { + return typeof value === "string" && validator.isJSON(value); +} + +/** + * Checks if the string is valid JSON (note: uses JSON.parse). + * If given value is not a string, then it returns false. + */ +export function IsJSON(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_JSON, + validator: { + validate: (value, args) => isJSON(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a json string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsJWT.ts b/src/decorator/string/IsJWT.ts new file mode 100644 index 000000000..21a181167 --- /dev/null +++ b/src/decorator/string/IsJWT.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_JWT = "isJwt"; + +/** + * Checks if the string is valid JWT token. + * If given value is not a string, then it returns false. + */ +export function isJWT(value: unknown): boolean { + return typeof value === "string" && validator.isJWT(value); +} + +/** + * Checks if the string is valid JWT token. + * If given value is not a string, then it returns false. + */ +export function IsJWT(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_JWT, + validator: { + validate: (value, args) => isJWT(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a jwt string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsLowercase.ts b/src/decorator/string/IsLowercase.ts new file mode 100644 index 000000000..38fea6d79 --- /dev/null +++ b/src/decorator/string/IsLowercase.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_LOWERCASE = "isLowercase"; + +/** + * Checks if the string is lowercase. + * If given value is not a string, then it returns false. + */ +export function isLowercase(value: unknown): boolean { + return typeof value === "string" && validator.isLowercase(value); +} + +/** + * Checks if the string is lowercase. + * If given value is not a string, then it returns false. + */ +export function IsLowercase(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LOWERCASE, + validator: { + validate: (value, args) => isLowercase(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a lowercase string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMacAddress.ts b/src/decorator/string/IsMacAddress.ts new file mode 100644 index 000000000..2aeb0810a --- /dev/null +++ b/src/decorator/string/IsMacAddress.ts @@ -0,0 +1,39 @@ +import { ValidationOptions, isValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_MAC_ADDRESS = "isMacAddress"; + +/** + * Check if the string is a MAC address. + * If given value is not a string, then it returns false. + */ +export function isMACAddress(value: unknown, options?: ValidatorJS.IsMACAddressOptions): boolean { + return typeof value === "string" && ValidatorJS.isMACAddress(value, options); +} + +/** + * Check if the string is a MAC address. + * If given value is not a string, then it returns false. + */ +export function IsMACAddress(optionsArg?: ValidatorJS.IsMACAddressOptions, validationOptionsArg?: ValidationOptions): PropertyDecorator; +export function IsMACAddress(validationOptionsArg?: ValidationOptions): PropertyDecorator; +export function IsMACAddress(optionsOrValidationOptionsArg?: ValidatorJS.IsMACAddressOptions | ValidationOptions, validationOptionsArg?: ValidationOptions): PropertyDecorator { + const options = !isValidationOptions(optionsOrValidationOptionsArg) ? optionsOrValidationOptionsArg : undefined; + const validationOptions = isValidationOptions(optionsOrValidationOptionsArg) ? optionsOrValidationOptionsArg : validationOptionsArg; + + return ValidateBy( + { + name: IS_MAC_ADDRESS, + constraints: [options], + validator: { + validate: (value, args) => isMACAddress(value, options), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a MAC Address", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMilitaryTime.ts b/src/decorator/string/IsMilitaryTime.ts new file mode 100644 index 000000000..6c49a61e5 --- /dev/null +++ b/src/decorator/string/IsMilitaryTime.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_MILITARY_TIME = "isMilitaryTime"; + +/** + * Checks if the string represents a time without a given timezone in the format HH:MM (military) + * If the given value does not match the pattern HH:MM, then it returns false. + */ +export function isMilitaryTime(value: unknown): boolean { + const militaryTimeRegex = /^([01]\d|2[0-3]):?([0-5]\d)$/; + return typeof value === "string" && validator.matches(value, militaryTimeRegex); +} + +/** + * Checks if the string represents a time without a given timezone in the format HH:MM (military) + * If the given value does not match the pattern HH:MM, then it returns false. + */ +export function IsMilitaryTime(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MILITARY_TIME, + validator: { + validate: (value, args) => isMilitaryTime(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a valid representation of military time in the format HH:MM", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMobilePhone.ts b/src/decorator/string/IsMobilePhone.ts new file mode 100644 index 000000000..61494d8cf --- /dev/null +++ b/src/decorator/string/IsMobilePhone.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_MOBILE_PHONE = "isMobilePhone"; + +/** + * Checks if the string is a mobile phone number (locale is one of ['zh-CN', 'zh-TW', 'en-ZA', 'en-AU', 'en-HK', + * 'pt-PT', 'fr-FR', 'el-GR', 'en-GB', 'en-US', 'en-ZM', 'ru-RU', 'nb-NO', 'nn-NO', 'vi-VN', 'en-NZ']). + * If given value is not a string, then it returns false. + */ +export function isMobilePhone(value: unknown, locale: ValidatorJS.MobilePhoneLocale): boolean { + return typeof value === "string" && ValidatorJS.isMobilePhone(value, locale); +} + +/** + * Checks if the string is a mobile phone number (locale is one of ['zh-CN', 'zh-TW', 'en-ZA', 'en-AU', 'en-HK', + * 'pt-PT', 'fr-FR', 'el-GR', 'en-GB', 'en-US', 'en-ZM', 'ru-RU', 'nb-NO', 'nn-NO', 'vi-VN', 'en-NZ']). + * If given value is not a string, then it returns false. + */ +export function IsMobilePhone(locale?: ValidatorJS.MobilePhoneLocale, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MOBILE_PHONE, + constraints: [locale], + validator: { + validate: (value, args) => isMobilePhone(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a phone number", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMongoId.ts b/src/decorator/string/IsMongoId.ts new file mode 100644 index 000000000..33bc0eac4 --- /dev/null +++ b/src/decorator/string/IsMongoId.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_MONGO_ID = "isMongoId"; + +/** + * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. + * If given value is not a string, then it returns false. + */ +export function isMongoId(value: unknown): boolean { + return typeof value === "string" && validator.isMongoId(value); +} + +/** + * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. + * If given value is not a string, then it returns false. + */ +export function IsMongoId(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MONGO_ID, + validator: { + validate: (value, args) => isMongoId(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a mongodb id", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMultibyte.ts b/src/decorator/string/IsMultibyte.ts new file mode 100644 index 000000000..cac35df1c --- /dev/null +++ b/src/decorator/string/IsMultibyte.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_MULTIBYTE = "isMultibyte"; + +/** + * Checks if the string contains one or more multibyte chars. + * If given value is not a string, then it returns false. + */ +export function isMultibyte(value: unknown): boolean { + return typeof value === "string" && validator.isMultibyte(value); +} + +/** + * Checks if the string contains one or more multibyte chars. + * If given value is not a string, then it returns false. + */ +export function IsMultibyte(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MULTIBYTE, + validator: { + validate: (value, args) => isMultibyte(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain one or more multibyte chars", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsNumberString.ts b/src/decorator/string/IsNumberString.ts new file mode 100644 index 000000000..f58aa2d8b --- /dev/null +++ b/src/decorator/string/IsNumberString.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_NUMBER_STRING = "isNumberString"; + +/** + * Checks if the string is numeric. + * If given value is not a string, then it returns false. + */ +export function isNumberString(value: unknown, options?: ValidatorJS.IsNumericOptions): boolean { + return typeof value === "string" && ValidatorJS.isNumeric(value, options); +} + +/** + * Checks if the string is numeric. + * If given value is not a string, then it returns false. + */ +export function IsNumberString(options?: ValidatorJS.IsNumericOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NUMBER_STRING, + constraints: [options], + validator: { + validate: (value, args) => isNumberString(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a number string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsPhoneNumber.ts b/src/decorator/string/IsPhoneNumber.ts new file mode 100644 index 000000000..f313b5899 --- /dev/null +++ b/src/decorator/string/IsPhoneNumber.ts @@ -0,0 +1,47 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import { PhoneNumberUtil } from "google-libphonenumber"; + +export const IS_PHONE_NUMBER = "isPhoneNumber"; + +/** + * Checks if the string is a valid phone number. + * @param value the potential phone number string to test + * @param {string} region 2 characters uppercase country code (e.g. DE, US, CH). + * If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region. + * See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33} + */ +export function isPhoneNumber(value: string, region: string | null): boolean { + const phoneUtil = PhoneNumberUtil.getInstance(); + try { + const phoneNum = phoneUtil.parseAndKeepRawInput(value, region); + const result = phoneUtil.isValidNumber(phoneNum); + return result; + } catch (error) { + // logging? + return false; + } +} + +/** + * Checks if the string is a valid phone number. + * @param region 2 characters uppercase country code (e.g. DE, US, CH). + * If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region. + * See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33} + */ +export function IsPhoneNumber(region: string | null, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_PHONE_NUMBER, + constraints: [region], + validator: { + validate: (value, args) => isPhoneNumber(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a valid phone number", + validationOptions + ), + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsPort.ts b/src/decorator/string/IsPort.ts new file mode 100644 index 000000000..6fae4abe0 --- /dev/null +++ b/src/decorator/string/IsPort.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_PORT = "isPort"; + +/** + * Check if the string is a valid port number. + */ +export function isPort(value: unknown): boolean { + return typeof value === "string" && validator.isPort(value); +} + +/** + * Check if the string is a valid port number. + */ +export function IsPort(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_PORT, + validator: { + validate: (value, args) => isPort(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a port", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsSurrogatePair.ts b/src/decorator/string/IsSurrogatePair.ts new file mode 100644 index 000000000..5e55b6999 --- /dev/null +++ b/src/decorator/string/IsSurrogatePair.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_SURROGATE_PAIR = "isSurrogatePair"; + +/** + * Checks if the string contains any surrogate pairs chars. + * If given value is not a string, then it returns false. + */ +export function isSurrogatePair(value: unknown): boolean { + return typeof value === "string" && validator.isSurrogatePair(value); +} + +/** + * Checks if the string contains any surrogate pairs chars. + * If given value is not a string, then it returns false. + */ +export function IsSurrogatePair(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_SURROGATE_PAIR, + validator: { + validate: (value, args) => isSurrogatePair(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain any surrogate pairs chars", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsUUID.ts b/src/decorator/string/IsUUID.ts new file mode 100644 index 000000000..c352e7c55 --- /dev/null +++ b/src/decorator/string/IsUUID.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export type UUIDVersion = "3" | "4" | "5" | "all" | 3 | 4 | 5; + +export const IS_UUID = "isUuid"; + +/** + * Checks if the string is a UUID (version 3, 4 or 5). + * If given value is not a string, then it returns false. + */ +export function isUUID(value: unknown, version?: UUIDVersion): boolean { + return typeof value === "string" && validator.isUUID(value, version); +} + +/** + * Checks if the string is a UUID (version 3, 4 or 5). + * If given value is not a string, then it returns false. + */ +export function IsUUID(version?: UUIDVersion, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_UUID, + constraints: [version], + validator: { + validate: (value, args) => isUUID(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an UUID", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsUppercase.ts b/src/decorator/string/IsUppercase.ts new file mode 100644 index 000000000..f4e20d8b2 --- /dev/null +++ b/src/decorator/string/IsUppercase.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_UPPERCASE = "isUppercase"; + +/** + * Checks if the string is uppercase. + * If given value is not a string, then it returns false. + */ +export function isUppercase(value: unknown): boolean { + return typeof value === "string" && validator.isUppercase(value); +} + +/** + * Checks if the string is uppercase. + * If given value is not a string, then it returns false. + */ +export function IsUppercase(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_UPPERCASE, + validator: { + validate: (value, args) => isUppercase(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be uppercase", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsUrl.ts b/src/decorator/string/IsUrl.ts new file mode 100644 index 000000000..4442115ff --- /dev/null +++ b/src/decorator/string/IsUrl.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import ValidatorJS from "validator"; + +export const IS_URL = "isUrl"; + +/** + * Checks if the string is an url. + * If given value is not a string, then it returns false. + */ +export function isURL(value: string, options?: ValidatorJS.IsURLOptions): boolean { + return typeof value === "string" && ValidatorJS.isURL(value, options); +} + +/** + * Checks if the string is an url. + * If given value is not a string, then it returns false. + */ +export function IsUrl(options?: ValidatorJS.IsURLOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_URL, + constraints: [options], + validator: { + validate: (value, args) => isURL(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an URL address", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsVariableWidth.ts b/src/decorator/string/IsVariableWidth.ts new file mode 100644 index 000000000..429af136a --- /dev/null +++ b/src/decorator/string/IsVariableWidth.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const IS_VARIABLE_WIDTH = "isVariableWidth"; + +/** + * Checks if the string contains variable-width chars. + * If given value is not a string, then it returns false. + */ +export function isVariableWidth(value: unknown): boolean { + return typeof value === "string" && validator.isVariableWidth(value); +} + +/** + * Checks if the string contains variable-width chars. + * If given value is not a string, then it returns false. + */ +export function IsVariableWidth(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_VARIABLE_WIDTH, + validator: { + validate: (value, args) => isVariableWidth(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must contain a full-width and half-width characters", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/Length.ts b/src/decorator/string/Length.ts new file mode 100644 index 000000000..f4181d37f --- /dev/null +++ b/src/decorator/string/Length.ts @@ -0,0 +1,43 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const LENGTH = "length"; + +/** + * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function length(value: unknown, min: number, max?: number): boolean { + return typeof value === "string" && validator.isLength(value, { min, max }); +} + +/** + * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function Length(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: LENGTH, + constraints: [min, max], + validator: { + validate: (value, args) => length(value, args.constraints[0], args.constraints[1]), + defaultMessage: buildMessage( + (eachPrefix, args) => { + const isMinLength = args.constraints[0] !== null && args.constraints[0] !== undefined; + const isMaxLength = args.constraints[1] !== null && args.constraints[1] !== undefined; + if (isMinLength && (!args.value || args.value.length < args.constraints[0])) { + return eachPrefix + "$property must be longer than or equal to $constraint1 characters"; + } else if (isMaxLength && (args.value.length > args.constraints[1])) { + return eachPrefix + "$property must be shorter than or equal to $constraint2 characters"; + } + return eachPrefix + "$property must be longer than or equal to $constraint1 and shorter than or equal to $constraint2 characters"; + }, + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/Matches.ts b/src/decorator/string/Matches.ts new file mode 100644 index 000000000..3d2abd1b6 --- /dev/null +++ b/src/decorator/string/Matches.ts @@ -0,0 +1,45 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const MATCHES = "matches"; + +/** + * Checks if string matches the pattern. Either matches('foo', /foo/i). + * If given value is not a string, then it returns false. + */ +export function matches(value: string, pattern: RegExp): boolean; +export function matches(value: string, pattern: string, modifiers: string): boolean; +export function matches(value: string, pattern: RegExp | string, modifiers?: string): boolean { + return typeof value === "string" && validator.matches(value, pattern as unknown as any, modifiers); +} + +/** + * Checks if string matches the pattern. Either matches('foo', /foo/i) + * If given value is not a string, then it returns false. + */ +export function Matches(pattern: RegExp, validationOptions?: ValidationOptions): PropertyDecorator; +export function Matches(pattern: string, modifiers?: string, validationOptions?: ValidationOptions): PropertyDecorator; +export function Matches(pattern: RegExp | string, modifiersOrAnnotationOptions?: string | ValidationOptions, validationOptions?: ValidationOptions): PropertyDecorator { + let modifiers: string; + if (modifiersOrAnnotationOptions && modifiersOrAnnotationOptions instanceof Object && !validationOptions) { + validationOptions = modifiersOrAnnotationOptions as ValidationOptions; + } else { + modifiers = modifiersOrAnnotationOptions as string; + } + + return ValidateBy( + { + name: MATCHES, + constraints: [pattern, modifiers], + validator: { + validate: (value, args) => matches(value, args.constraints[0], args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix, args) => eachPrefix + "$property must match $constraint1 regular expression", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/MaxLength.ts b/src/decorator/string/MaxLength.ts new file mode 100644 index 000000000..04a0b42a2 --- /dev/null +++ b/src/decorator/string/MaxLength.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const MAX_LENGTH = "maxLength"; + +/** + * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function maxLength(value: unknown, max: number) { + return typeof value === "string" && validator.isLength(value, { min: 0, max }); +} + +/** + * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function MaxLength(min: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MAX_LENGTH, + constraints: [min], + validator: { + validate: (value, args) => maxLength(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be shorter than or equal to $constraint1 characters", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/MinLength.ts b/src/decorator/string/MinLength.ts new file mode 100644 index 000000000..6f6d8ff61 --- /dev/null +++ b/src/decorator/string/MinLength.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const MIN_LENGTH = "minLength"; + +/** + * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function minLength(value: unknown, min: number) { + return typeof value === "string" && validator.isLength(value, { min }); +} + +/** + * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function MinLength(min: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MIN_LENGTH, + constraints: [min], + validator: { + validate: (value, args) => minLength(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be longer than or equal to $constraint1 characters", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/string/NotContains.ts b/src/decorator/string/NotContains.ts new file mode 100644 index 000000000..bb7589fc2 --- /dev/null +++ b/src/decorator/string/NotContains.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; +import validator from "validator"; + +export const NOT_CONTAINS = "notContains"; + +/** + * Checks if the string does not contain the seed. + * If given value is not a string, then it returns false. + */ +export function notContains(value: unknown, seed: string): boolean { + return typeof value === "string" && !validator.contains(value, seed); +} + +/** + * Checks if the string does not contain the seed. + * If given value is not a string, then it returns false. + */ +export function NotContains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: NOT_CONTAINS, + constraints: [seed], + validator: { + validate: (value, args) => notContains(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property should not contain a $constraint1 string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsArray.ts b/src/decorator/typechecker/IsArray.ts new file mode 100644 index 000000000..544de15dd --- /dev/null +++ b/src/decorator/typechecker/IsArray.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_ARRAY = "isArray"; + +/** + * Checks if a given value is an array + */ +export function isArray(value: unknown): boolean { + return value instanceof Array; +} + +/** + * Checks if a given value is an array + */ +export function IsArray(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ARRAY, + validator: { + validate: (value, args) => isArray(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an array", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsBoolean.ts b/src/decorator/typechecker/IsBoolean.ts new file mode 100644 index 000000000..7763a2f3b --- /dev/null +++ b/src/decorator/typechecker/IsBoolean.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_BOOLEAN = "isBoolean"; + +/** + * Checks if a given value is a number. + */ +export function isBoolean(value: unknown): boolean { + return value instanceof Boolean || typeof value === "boolean"; +} + +/** + * Checks if a value is a number. + */ +export function IsBoolean(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BOOLEAN, + validator: { + validate: (value, args) => isBoolean(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a boolean value", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsDate.ts b/src/decorator/typechecker/IsDate.ts new file mode 100644 index 000000000..a5360af6c --- /dev/null +++ b/src/decorator/typechecker/IsDate.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_DATE = "isDate"; + +/** + * Checks if a given value is a number. + */ +export function isDate(value: unknown): boolean { + return value instanceof Date && !isNaN(value.getTime()); +} + +/** + * Checks if a value is a number. + */ +export function IsDate(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DATE, + validator: { + validate: (value, args) => isDate(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a Date instance", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsEnum.ts b/src/decorator/typechecker/IsEnum.ts new file mode 100644 index 000000000..9e16f655e --- /dev/null +++ b/src/decorator/typechecker/IsEnum.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_ENUM = "isEnum"; + +/** + * Checks if a given value is an enum + */ +export function isEnum(value: unknown, entity: any): boolean { + const enumValues = Object.keys(entity) + .map(k => entity[k]); + return enumValues.indexOf(value) >= 0; +} + +/** + * Checks if a given value is an enum + */ +export function IsEnum(entity: Object, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ENUM, + constraints: [entity], + validator: { + validate: (value, args) => isEnum(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a valid enum value", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsInt.ts b/src/decorator/typechecker/IsInt.ts new file mode 100644 index 000000000..2cf5ac3ac --- /dev/null +++ b/src/decorator/typechecker/IsInt.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_INT = "isInt"; + +/** + * Checks if value is an integer. + */ +export function isInt(val: unknown): boolean { + return typeof val === "number" && Number.isInteger(val); +} + +/** + * Checks if value is an integer. + */ +export function IsInt(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_INT, + validator: { + validate: (value, args) => isInt(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an integer number", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsNumber.ts b/src/decorator/typechecker/IsNumber.ts new file mode 100644 index 000000000..65c5d28f5 --- /dev/null +++ b/src/decorator/typechecker/IsNumber.ts @@ -0,0 +1,62 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_NUMBER = "isNumber"; + +/** + * Options to be passed to IsNumber decorator. + */ +export interface IsNumberOptions { + allowNaN?: boolean; + allowInfinity?: boolean; + maxDecimalPlaces?: number; +} + +/** + * Checks if a given value is a number. + */ +export function isNumber(value: unknown, options: IsNumberOptions = {}): boolean { + if (typeof value !== "number") { + return false; + } + + if (value === Infinity || value === -Infinity) { + return options.allowInfinity; + } + + if (Number.isNaN(value)) { + return options.allowNaN; + } + + if (options.maxDecimalPlaces !== undefined) { + let decimalPlaces = 0; + if ((value % 1) !== 0) { + decimalPlaces = value.toString().split(".")[1].length; + } + if (decimalPlaces > options.maxDecimalPlaces) { + return false; + } + } + + return Number.isFinite(value); +} + +/** + * Checks if a value is a number. + */ +export function IsNumber(options: IsNumberOptions = {}, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NUMBER, + constraints: [options], + validator: { + validate: (value, args) => isNumber(value, args.constraints[0]), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a number conforming to the specified constraints", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsObject.ts b/src/decorator/typechecker/IsObject.ts new file mode 100644 index 000000000..5c7c0e2ff --- /dev/null +++ b/src/decorator/typechecker/IsObject.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_OBJECT = "isObject"; + +/** + * Checks if the value is valid Object. + * Returns false if the value is not an object. + */ +export function isObject(value: unknown): value is object { + return value != null && (typeof value === "object" || typeof value === "function") && !Array.isArray(value); +} + +/** + * Checks if the value is valid Object. + * Returns false if the value is not an object. + */ +export function IsObject(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_OBJECT, + validator: { + validate: (value, args) => isObject(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be an object", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsString.ts b/src/decorator/typechecker/IsString.ts new file mode 100644 index 000000000..b843f3888 --- /dev/null +++ b/src/decorator/typechecker/IsString.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from "../ValidationOptions"; +import { buildMessage, ValidateBy } from "../common/ValidateBy"; + +export const IS_STRING = "isString"; + +/** +* Checks if a given value is a real string. +*/ +export function isString(value: unknown): value is string { + return value instanceof String || typeof value === "string"; +} + +/** +* Checks if a given value is a real string. +*/ +export function IsString(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_STRING, + validator: { + validate: (value, args) => isString(value), + defaultMessage: buildMessage( + (eachPrefix) => eachPrefix + "$property must be a string", + validationOptions + ) + } + }, + validationOptions + ); +} diff --git a/src/index.ts b/src/index.ts index 0ec6447b4..dcec7876a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,10 @@ import {ValidationError} from "./validation/ValidationError"; import {ValidatorOptions} from "./validation/ValidatorOptions"; import {ValidationSchema} from "./validation-schema/ValidationSchema"; -import {MetadataStorage} from "./metadata/MetadataStorage"; +import {getMetadataStorage} from "./metadata/MetadataStorage"; import {Validator} from "./validation/Validator"; import {getFromContainer} from "./container"; -/** - * Gets metadata storage. - * Metadata storage follows the best practices and stores metadata in a global variable. - */ -export function getMetadataStorage(): MetadataStorage { - if (!(global as any).classValidatorMetadataStorage) - (global as any).classValidatorMetadataStorage = new MetadataStorage(); - - return (global as any).classValidatorMetadataStorage; -} - // ------------------------------------------------------------------------- // Export everything api users needs // ------------------------------------------------------------------------- @@ -25,7 +14,6 @@ export * from "./decorator/decorators"; export * from "./decorator/ValidationOptions"; export * from "./validation/ValidatorConstraintInterface"; export * from "./validation/ValidationError"; -export * from "./validation/ValidationTypeOptions"; export * from "./validation/ValidatorOptions"; export * from "./validation/ValidationArguments"; export * from "./validation/ValidationTypes"; diff --git a/src/metadata/MetadataStorage.ts b/src/metadata/MetadataStorage.ts index 4e2bd4c8a..be7488330 100644 --- a/src/metadata/MetadataStorage.ts +++ b/src/metadata/MetadataStorage.ts @@ -3,6 +3,20 @@ import {ConstraintMetadata} from "./ConstraintMetadata"; import {ValidationSchema} from "../validation-schema/ValidationSchema"; import {ValidationSchemaToMetadataTransformer} from "../validation-schema/ValidationSchemaToMetadataTransformer"; +/** + * Gets metadata storage. + * Metadata storage follows the best practices and stores metadata in a global variable. + */ +export function getMetadataStorage(): MetadataStorage { + if (typeof window !== "undefined") { + (window as any).global = window; + } + if (!(global as any).classValidatorMetadataStorage) + (global as any).classValidatorMetadataStorage = new MetadataStorage(); + + return (global as any).classValidatorMetadataStorage; +} + /** * Storage all metadatas. */ @@ -30,7 +44,7 @@ export class MetadataStorage { const validationMetadatas = new ValidationSchemaToMetadataTransformer().transform(schema); validationMetadatas.forEach(validationMetadata => this.addValidationMetadata(validationMetadata)); } - + /** * Adds a new validation metadata. */ @@ -62,19 +76,19 @@ export class MetadataStorage { * Gets all validation metadatas for the given object with the given groups. */ getTargetValidationMetadatas(targetConstructor: Function, targetSchema: string, groups?: string[]): ValidationMetadata[] { - + // get directly related to a target metadatas const originalMetadatas = this.validationMetadatas.filter(metadata => { if (metadata.target !== targetConstructor && metadata.target !== targetSchema) return false; - if (metadata.always) + if (metadata.always) return true; if (groups && groups.length > 0) return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1); - + return true; }); - + // get metadatas for inherited classes const inheritedMetadatas = this.validationMetadatas.filter(metadata => { // if target is a string it's means we validate agains a schema, and there is no inheritance support for schemas @@ -85,18 +99,18 @@ export class MetadataStorage { if (metadata.target instanceof Function && !(targetConstructor.prototype instanceof (metadata.target as Function))) return false; - if (metadata.always) + if (metadata.always) return true; if (groups && groups.length > 0) return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1); - + return true; }); // filter out duplicate metadatas, prefer original metadatas instead of inherited metadatas const uniqueInheritedMetadatas = inheritedMetadatas.filter(inheritedMetadata => { return !originalMetadatas.find(originalMetadata => { - return originalMetadata.propertyName === inheritedMetadata.propertyName && + return originalMetadata.propertyName === inheritedMetadata.propertyName && originalMetadata.type === inheritedMetadata.type; }); }); @@ -111,4 +125,4 @@ export class MetadataStorage { return this.constraintMetadatas.filter(metadata => metadata.target === target); } -} \ No newline at end of file +} diff --git a/src/register-decorator.ts b/src/register-decorator.ts index 593c819d5..04d06cc0c 100644 --- a/src/register-decorator.ts +++ b/src/register-decorator.ts @@ -5,7 +5,8 @@ import {ValidationMetadata} from "./metadata/ValidationMetadata"; import {ValidationMetadataArgs} from "./metadata/ValidationMetadataArgs"; import {ValidationTypes} from "./validation/ValidationTypes"; import {ValidationArguments} from "./validation/ValidationArguments"; -import {getMetadataStorage} from "."; +import { getFromContainer } from "./container"; +import { MetadataStorage, getMetadataStorage } from "./metadata/MetadataStorage"; export interface ValidationDecoratorOptions { @@ -53,6 +54,10 @@ export function registerDecorator(options: ValidationDecoratorOptions): void { let constraintCls: Function; if (options.validator instanceof Function) { constraintCls = options.validator as Function; + const constraintClasses = getFromContainer(MetadataStorage).getTargetValidatorConstraints(options.validator); + if (constraintClasses.length > 1) { + throw `More than one implementation of ValidatorConstraintInterface found for validator on: ${options.target}:${options.propertyName}`; + } } else { const validator = options.validator as ValidatorConstraintInterface; constraintCls = class CustomConstraint implements ValidatorConstraintInterface { @@ -72,7 +77,7 @@ export function registerDecorator(options: ValidationDecoratorOptions): void { } const validationMetadataArgs: ValidationMetadataArgs = { - type: ValidationTypes.CUSTOM_VALIDATION, + type: options.name && ValidationTypes.isValid(options.name) ? options.name : ValidationTypes.CUSTOM_VALIDATION, target: options.target, propertyName: options.propertyName, validationOptions: options.options, diff --git a/src/types.d.ts b/src/types.d.ts index 67607af4e..879048a75 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,3 +1,4 @@ declare var window: any; -declare module "ansicolor"; \ No newline at end of file +declare module "ansicolor"; +declare module "google-libphonenumber"; diff --git a/src/validation-schema/ValidationSchemaToMetadataTransformer.ts b/src/validation-schema/ValidationSchemaToMetadataTransformer.ts index fcc9eed5e..dd45ff54b 100644 --- a/src/validation-schema/ValidationSchemaToMetadataTransformer.ts +++ b/src/validation-schema/ValidationSchemaToMetadataTransformer.ts @@ -13,9 +13,6 @@ export class ValidationSchemaToMetadataTransformer { const metadatas: ValidationMetadata[] = []; Object.keys(schema.properties).forEach(property => { schema.properties[property].forEach(validation => { - if (!ValidationTypes.isValid(validation.type)) - throw new Error(`Validation schema ${schema.name}#${property} as incorrect type ${validation.type}`); - const validationOptions: ValidationOptions = { message: validation.message, groups: validation.groups, @@ -36,4 +33,4 @@ export class ValidationSchemaToMetadataTransformer { return metadatas; } -} \ No newline at end of file +} diff --git a/src/validation/ValidationExecutor.ts b/src/validation/ValidationExecutor.ts index 181fc4b6d..2d14ec627 100644 --- a/src/validation/ValidationExecutor.ts +++ b/src/validation/ValidationExecutor.ts @@ -7,7 +7,7 @@ import {ConstraintMetadata} from "../metadata/ConstraintMetadata"; import {ValidationArguments} from "./ValidationArguments"; import {ValidationUtils} from "./ValidationUtils"; import {isPromise, convertToArray} from "../utils"; -import {getMetadataStorage} from ".."; +import { getMetadataStorage } from "../metadata/MetadataStorage"; /** * Executes validation over given object. @@ -166,7 +166,7 @@ export class ValidationExecutor { } // handle IS_DEFINED validation type the special way - it should work no matter skipUndefinedProperties/skipMissingProperties is set or not - this.defaultValidations(object, value, definedMetadatas, validationError.constraints); + this.customValidations(object, value, definedMetadatas, validationError); this.mapContexts(object, value, definedMetadatas, validationError); if (value === undefined && this.validatorOptions && this.validatorOptions.skipUndefinedProperties === true) { @@ -181,7 +181,6 @@ export class ValidationExecutor { return; } - this.defaultValidations(object, value, metadatas, validationError.constraints); this.customValidations(object, value, customValidationMetadatas, validationError); this.nestedValidations(value, nestedValidationMetadatas, validationError.children, definedMetadatas, metadatas); @@ -219,28 +218,6 @@ export class ValidationExecutor { .reduce((resultA, resultB) => resultA && resultB, true); } - private defaultValidations(object: Object, - value: any, - metadatas: ValidationMetadata[], - errorMap: { [key: string]: string }) { - return metadatas - .filter(metadata => { - if (metadata.each) { - if (value instanceof Array || value instanceof Set || value instanceof Map) { - const arrayValue = convertToArray(value); - return !arrayValue.every((subValue: any) => this.validator.validateValueByMetadata(subValue, metadata)); - } - - } else { - return !this.validator.validateValueByMetadata(value, metadata); - } - }) - .forEach(metadata => { - const [key, message] = this.createValidationError(object, value, metadata); - errorMap[key] = message; - }); - } - private customValidations(object: Object, value: any, metadatas: ValidationMetadata[], @@ -408,15 +385,12 @@ export class ValidationExecutor { constraints: metadata.constraints }; - let message = metadata.message; + let message = metadata.message || ""; if (!metadata.message && (!this.validatorOptions || (this.validatorOptions && !this.validatorOptions.dismissDefaultMessages))) { if (customValidatorMetadata && customValidatorMetadata.instance.defaultMessage instanceof Function) { message = customValidatorMetadata.instance.defaultMessage(validationArguments); } - - if (!message) - message = ValidationTypes.getMessage(type, metadata.each); } const messageString = ValidationUtils.replaceMessageSpecialTokens(message, validationArguments); diff --git a/src/validation/ValidationTypeOptions.ts b/src/validation/ValidationTypeOptions.ts deleted file mode 100644 index 89408932c..000000000 --- a/src/validation/ValidationTypeOptions.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Options to be passed to IsNumber decorator. - */ -export interface IsNumberOptions { - allowNaN?: boolean; - allowInfinity?: boolean; - maxDecimalPlaces?: number; -} diff --git a/src/validation/ValidationTypes.ts b/src/validation/ValidationTypes.ts index 7a594cb67..1aa4f95d8 100644 --- a/src/validation/ValidationTypes.ts +++ b/src/validation/ValidationTypes.ts @@ -1,112 +1,15 @@ -import { ValidationArguments } from "./ValidationArguments"; - /** * Validation types. */ export class ValidationTypes { /* system */ - static CUSTOM_VALIDATION = "customValidation"; - static NESTED_VALIDATION = "nestedValidation"; - static PROMISE_VALIDATION = "promiseValidation"; - static CONDITIONAL_VALIDATION = "conditionalValidation"; - static WHITELIST = "whitelistValidation"; - - /* common checkers */ - static IS_DEFINED = "isDefined"; - static EQUALS = "equals"; - static NOT_EQUALS = "notEquals"; - static IS_EMPTY = "isEmpty"; - static IS_NOT_EMPTY = "isNotEmpty"; - static IS_IN = "isIn"; - static IS_NOT_IN = "isNotIn"; - - /* type checkers */ - static IS_BOOLEAN = "isBoolean"; - static IS_DATE = "isDate"; - static IS_NUMBER = "isNumber"; - static IS_LATLONG = "isLatLong"; - static IS_LATITUDE = "isLatitude"; - static IS_LONGITUDE = "isLongitude"; - static IS_STRING = "isString"; - static IS_DATE_STRING = "isDateString"; - static IS_ARRAY = "isArray"; - static IS_INT = "isInt"; - static IS_ENUM = "isEnum"; - - /* number checkers */ - static IS_DIVISIBLE_BY = "isDivisibleBy"; - static IS_POSITIVE = "isPositive"; - static IS_NEGATIVE = "isNegative"; - static MIN = "min"; - static MAX = "max"; - - /* date checkers */ - static MIN_DATE = "minDate"; - static MAX_DATE = "maxDate"; - - /* string-as-type checkers */ - static IS_BOOLEAN_STRING = "isBooleanString"; - static IS_NUMBER_STRING = "isNumberString"; - - /* string checkers */ - static CONTAINS = "contains"; - static NOT_CONTAINS = "notContains"; - static IS_ALPHA = "isAlpha"; - static IS_ALPHANUMERIC = "isAlphanumeric"; - static IS_DECIMAL = "isDecimal"; - static IS_ASCII = "isAscii"; - static IS_BASE64 = "isBase64"; - static IS_BYTE_LENGTH = "isByteLength"; - static IS_CREDIT_CARD = "isCreditCard"; - static IS_CURRENCY = "isCurrency"; - static IS_EMAIL = "isEmail"; - static IS_FQDN = "isFqdn"; - static IS_FULL_WIDTH = "isFullWidth"; - static IS_HALF_WIDTH = "isHalfWidth"; - static IS_VARIABLE_WIDTH = "isVariableWidth"; - static IS_HEX_COLOR = "isHexColor"; - static IS_HEXADECIMAL = "isHexadecimal"; - static IS_MAC_ADDRESS = "isMacAddress"; - static IS_IP = "isIp"; - static IS_PORT = "isPort"; - static IS_ISBN = "isIsbn"; - static IS_ISIN = "isIsin"; - static IS_ISO8601 = "isIso8601"; - static IS_JSON = "isJson"; - static IS_JWT = "isJwt"; - static IS_OBJECT = "isObject"; - static IS_NOT_EMPTY_OBJECT = "isNotEmptyObject"; - static IS_LOWERCASE = "isLowercase"; - static IS_MOBILE_PHONE = "isMobilePhone"; - static IS_PHONE_NUMBER = "isPhoneNumber"; - static IS_ISO31661_ALPHA_2 = "isISO31661Alpha2"; - static IS_ISO31661_ALPHA_3 = "isISO31661Alpha3"; - static IS_MONGO_ID = "isMongoId"; - static IS_MULTIBYTE = "isMultibyte"; - static IS_SURROGATE_PAIR = "isSurrogatePair"; - static IS_URL = "isUrl"; - static IS_UUID = "isUuid"; - static IS_FIREBASE_PUSH_ID = "IsFirebasePushId"; - static LENGTH = "length"; - static IS_UPPERCASE = "isUppercase"; - static MIN_LENGTH = "minLength"; - static MAX_LENGTH = "maxLength"; - static MATCHES = "matches"; - static IS_MILITARY_TIME = "isMilitaryTime"; - static IS_HASH = "isHash"; - static IS_ISSN = "isISSN"; - - /* array checkers */ - static ARRAY_CONTAINS = "arrayContains"; - static ARRAY_NOT_CONTAINS = "arrayNotContains"; - static ARRAY_NOT_EMPTY = "arrayNotEmpty"; - static ARRAY_MIN_SIZE = "arrayMinSize"; - static ARRAY_MAX_SIZE = "arrayMaxSize"; - static ARRAY_UNIQUE = "arrayUnique"; - - /* object chekers */ - static IS_INSTANCE = "isInstance"; + static CUSTOM_VALIDATION = "customValidation"; // done + static NESTED_VALIDATION = "nestedValidation"; // done + static PROMISE_VALIDATION = "promiseValidation"; // done + static CONDITIONAL_VALIDATION = "conditionalValidation"; // done + static WHITELIST = "whitelistValidation"; // done + static IS_DEFINED = "isDefined"; // done /** * Checks if validation type is valid. @@ -117,208 +20,4 @@ export class ValidationTypes { Object.keys(this).map(key => (this as any)[key]).indexOf(type) !== -1; } - /** - * Gets default validation error message for the given validation type. - */ - static getMessage(type: string, isEach: boolean): string | ((args: ValidationArguments) => string) { - const eachPrefix = isEach ? "each value in " : ""; - switch (type) { - - /* system chceck */ - case this.NESTED_VALIDATION: - return eachPrefix + "nested property $property must be either object or array"; - /* common checkers */ - case this.IS_DEFINED: - return eachPrefix + "$property should not be null or undefined"; - case this.EQUALS: - return eachPrefix + "$property must be equal to $constraint1"; - case this.NOT_EQUALS: - return eachPrefix + "$property should not be equal to $constraint1"; - case this.IS_EMPTY: - return eachPrefix + "$property must be empty"; - case this.IS_NOT_EMPTY: - return eachPrefix + "$property should not be empty"; - case this.IS_IN: - return eachPrefix + "$property must be one of the following values: $constraint1"; - case this.IS_NOT_IN: - return eachPrefix + "$property should not be one of the following values: $constraint1"; - case this.IS_PORT: - return eachPrefix + "$property must be a port"; - - /* type checkers */ - case this.IS_BOOLEAN: - return eachPrefix + "$property must be a boolean value"; - case this.IS_DATE: - return eachPrefix + "$property must be a Date instance"; - case this.IS_NUMBER: - return eachPrefix + "$property must be a number conforming to the specified constraints"; - case this.IS_INT: - return eachPrefix + "$property must be an integer number"; - case this.IS_STRING: - return eachPrefix + "$property must be a string"; - case this.IS_DATE_STRING: - return eachPrefix + "$property must be a ISOString"; - case this.IS_ARRAY: - return eachPrefix + "$property must be an array"; - case this.IS_ENUM: - return eachPrefix + "$property must be a valid enum value"; - - /* number checkers */ - case this.IS_DIVISIBLE_BY: - return eachPrefix + "$property must be divisible by $constraint1"; - case this.IS_POSITIVE: - return eachPrefix + "$property must be a positive number"; - case this.IS_NEGATIVE: - return eachPrefix + "$property must be a negative number"; - case this.MIN: - return eachPrefix + "$property must not be less than $constraint1"; - case this.MAX: - return eachPrefix + "$property must not be greater than $constraint1"; - - /* date checkers */ - case this.MIN_DATE: - return "minimal allowed date for " + eachPrefix + "$property is $constraint1"; - case this.MAX_DATE: - return "maximal allowed date for " + eachPrefix + "$property is $constraint1"; - - /* string-as-type checkers */ - case this.IS_BOOLEAN_STRING: - return eachPrefix + "$property must be a boolean string"; - case this.IS_NUMBER_STRING: - return eachPrefix + "$property must be a number string"; - - /* string checkers */ - case this.CONTAINS: - return eachPrefix + "$property must contain a $constraint1 string"; - case this.NOT_CONTAINS: - return eachPrefix + "$property should not contain a $constraint1 string"; - case this.IS_ALPHA: - return eachPrefix + "$property must contain only letters (a-zA-Z)"; - case this.IS_ALPHANUMERIC: - return eachPrefix + "$property must contain only letters and numbers"; - case this.IS_DECIMAL: - return eachPrefix + "$property is not a valid decimal number."; - case this.IS_ASCII: - return eachPrefix + "$property must contain only ASCII characters"; - case this.IS_BASE64: - return eachPrefix + "$property must be base64 encoded"; - case this.IS_BYTE_LENGTH: - return eachPrefix + "$property's byte length must fall into ($constraint1, $constraint2) range"; - case this.IS_CREDIT_CARD: - return eachPrefix + "$property must be a credit card"; - case this.IS_CURRENCY: - return eachPrefix + "$property must be a currency"; - case this.IS_EMAIL: - return eachPrefix + "$property must be an email"; - case this.IS_FQDN: - return eachPrefix + "$property must be a valid domain name"; - case this.IS_FULL_WIDTH: - return eachPrefix + "$property must contain a full-width characters"; - case this.IS_HALF_WIDTH: - return eachPrefix + "$property must contain a half-width characters"; - case this.IS_VARIABLE_WIDTH: - return eachPrefix + "$property must contain a full-width and half-width characters"; - case this.IS_HEX_COLOR: - return eachPrefix + "$property must be a hexadecimal color"; - case this.IS_HEXADECIMAL: - return eachPrefix + "$property must be a hexadecimal number"; - case this.IS_MAC_ADDRESS: - return eachPrefix + "$property must be a MAC Address"; - case this.IS_IP: - return eachPrefix + "$property must be an ip address"; - case this.IS_ISBN: - return eachPrefix + "$property must be an ISBN"; - case this.IS_ISIN: - return eachPrefix + "$property must be an ISIN (stock/security identifier)"; - case this.IS_ISO8601: - return eachPrefix + "$property must be a valid ISO 8601 date string"; - case this.IS_JSON: - return eachPrefix + "$property must be a json string"; - case this.IS_JWT: - return eachPrefix + "$property must be a jwt string"; - case this.IS_OBJECT: - return eachPrefix + "$property must be an object"; - case this.IS_NOT_EMPTY_OBJECT: - return eachPrefix + "$property must be a non-empty object"; - case this.IS_LOWERCASE: - return eachPrefix + "$property must be a lowercase string"; - case this.IS_MOBILE_PHONE: - return eachPrefix + "$property must be a phone number"; - case this.IS_PHONE_NUMBER: - return eachPrefix + "$property must be a valid phone number"; - case this.IS_ISO31661_ALPHA_2: - return eachPrefix + "$property must be a valid ISO31661 Alpha2 code"; - case this.IS_ISO31661_ALPHA_3: - return eachPrefix + "$property must be a valid ISO31661 Alpha3 code"; - case this.IS_LATLONG: - return eachPrefix + "$property must be a latitude,longitude string"; - case this.IS_LATITUDE: - return eachPrefix + "$property must be a latitude string or number"; - case this.IS_LONGITUDE: - return eachPrefix + "$property must be a longitude string or number"; - case this.IS_MONGO_ID: - return eachPrefix + "$property must be a mongodb id"; - case this.IS_MULTIBYTE: - return eachPrefix + "$property must contain one or more multibyte chars"; - case this.IS_SURROGATE_PAIR: - return eachPrefix + "$property must contain any surrogate pairs chars"; - case this.IS_URL: - return eachPrefix + "$property must be an URL address"; - case this.IS_UUID: - return eachPrefix + "$property must be an UUID"; - case this.IS_FIREBASE_PUSH_ID: - return eachPrefix + "$property must be a Firebase Push Id"; - case this.IS_UPPERCASE: - return eachPrefix + "$property must be uppercase"; - case this.LENGTH: - return (args: ValidationArguments) => { - const isMinLength = args.constraints[0] !== null && args.constraints[0] !== undefined; - const isMaxLength = args.constraints[1] !== null && args.constraints[1] !== undefined; - if (isMinLength && (!args.value || args.value.length < args.constraints[0])) { - return eachPrefix + "$property must be longer than or equal to $constraint1 characters"; - } else if (isMaxLength && (args.value.length > args.constraints[1])) { - return eachPrefix + "$property must be shorter than or equal to $constraint2 characters"; - } - return eachPrefix + "$property must be longer than or equal to $constraint1 and shorter than or equal to $constraint2 characters"; - }; - case this.MIN_LENGTH: - return eachPrefix + "$property must be longer than or equal to $constraint1 characters"; - case this.MAX_LENGTH: - return eachPrefix + "$property must be shorter than or equal to $constraint1 characters"; - case this.MATCHES: - return eachPrefix + "$property must match $constraint1 regular expression"; - case this.IS_MILITARY_TIME: - return eachPrefix + "$property must be a valid representation of military time in the format HH:MM"; - case this.IS_HASH: - return eachPrefix + "$property must be a hash of type $constraint1"; - case this.IS_ISSN: - return eachPrefix + "$property must be a ISSN"; - - /* array checkers */ - case this.ARRAY_CONTAINS: - return eachPrefix + "$property must contain $constraint1 values"; - case this.ARRAY_NOT_CONTAINS: - return eachPrefix + "$property should not contain $constraint1 values"; - case this.ARRAY_NOT_EMPTY: - return eachPrefix + "$property should not be empty"; - case this.ARRAY_MIN_SIZE: - return eachPrefix + "$property must contain at least $constraint1 elements"; - case this.ARRAY_MAX_SIZE: - return eachPrefix + "$property must contain not more than $constraint1 elements"; - case this.ARRAY_UNIQUE: - return eachPrefix + "All $property's elements must be unique"; - - case this.IS_INSTANCE: - return (args: ValidationArguments) => { - if (args.constraints[0]) { - return eachPrefix + `$property must be an instance of ${args.constraints[0].name}`; - } else { - return eachPrefix + `${this.IS_INSTANCE} decorator expects and object as value, but got falsy value.`; - } - }; - } - - return ""; - } - } diff --git a/src/validation/Validator.ts b/src/validation/Validator.ts index bb6117f59..daf39e4de 100644 --- a/src/validation/Validator.ts +++ b/src/validation/Validator.ts @@ -1,7 +1,6 @@ import {ValidationMetadata} from "../metadata/ValidationMetadata"; import {ValidationTypes} from "./ValidationTypes"; import {ValidationError} from "./ValidationError"; -import {IsNumberOptions} from "./ValidationTypeOptions"; import {ValidatorOptions} from "./ValidatorOptions"; import {ValidationExecutor} from "./ValidationExecutor"; import {ValidationOptions} from "../decorator/ValidationOptions"; @@ -16,20 +15,6 @@ export class Validator { // Private Properties // ------------------------------------------------------------------------- - private webSafeRegex = /^[a-zA-Z0-9_-]*$/; - private validatorJs = validator; - private libPhoneNumber = { - phoneUtil: require("google-libphonenumber").PhoneNumberUtil.getInstance(), - }; - private _isEmptyObject = function(object: object) { - for (const key in object) { - if (object.hasOwnProperty(key)) { - return false; - } - } - - return true; - }; /** * Performs validation of the given object based on decorators or validation schema. @@ -115,878 +100,4 @@ export class Validator { return executor.stripEmptyErrors(validationErrors); } - /** - * Performs validation of the given object based on the given ValidationMetadata object. - */ - validateValueByMetadata(value: any, metadata: ValidationMetadata): boolean { - switch (metadata.type) { - /* common checkers */ - case ValidationTypes.IS_DEFINED: - return this.isDefined(value); - case ValidationTypes.EQUALS: - return this.equals(value, metadata.constraints[0]); - case ValidationTypes.NOT_EQUALS: - return this.notEquals(value, metadata.constraints[0]); - case ValidationTypes.IS_EMPTY: - return this.isEmpty(value); - case ValidationTypes.IS_NOT_EMPTY: - return this.isNotEmpty(value); - case ValidationTypes.IS_IN: - return this.isIn(value, metadata.constraints[0]); - case ValidationTypes.IS_NOT_IN: - return this.isNotIn(value, metadata.constraints[0]); - - /* type checkers */ - case ValidationTypes.IS_LATLONG: - return this.isLatLong(value); - case ValidationTypes.IS_LATITUDE: - return this.isLatitude(value); - case ValidationTypes.IS_LONGITUDE: - return this.isLongitude(value); - case ValidationTypes.IS_BOOLEAN: - return this.isBoolean(value); - case ValidationTypes.IS_DATE: - return this.isDate(value); - case ValidationTypes.IS_STRING: - return this.isString(value); - case ValidationTypes.IS_DATE_STRING: - return this.isDateString(value); - case ValidationTypes.IS_ARRAY: - return this.isArray(value); - case ValidationTypes.IS_NUMBER: - return this.isNumber(value, metadata.constraints[0]); - case ValidationTypes.IS_INT: - return this.isInt(value); - case ValidationTypes.IS_ENUM: - return this.isEnum(value, metadata.constraints[0]); - - /* number checkers */ - case ValidationTypes.IS_DIVISIBLE_BY: - return this.isDivisibleBy(value, metadata.constraints[0]); - case ValidationTypes.IS_POSITIVE: - return this.isPositive(value); - case ValidationTypes.IS_NEGATIVE: - return this.isNegative(value); - case ValidationTypes.MIN: - return this.min(value, metadata.constraints[0]); - case ValidationTypes.MAX: - return this.max(value, metadata.constraints[0]); - - /* date checkers */ - case ValidationTypes.MIN_DATE: - return this.minDate(value, metadata.constraints[0]); - case ValidationTypes.MAX_DATE: - return this.maxDate(value, metadata.constraints[0]); - - /* string-as-type checkers */ - case ValidationTypes.IS_BOOLEAN_STRING: - return this.isBooleanString(value); - case ValidationTypes.IS_NUMBER_STRING: - return this.isNumberString(value, metadata.constraints[0]); - - /* string checkers */ - case ValidationTypes.CONTAINS: - return this.contains(value, metadata.constraints[0]); - case ValidationTypes.NOT_CONTAINS: - return this.notContains(value, metadata.constraints[0]); - case ValidationTypes.IS_ALPHA: - return this.isAlpha(value, metadata.constraints[0]); - case ValidationTypes.IS_ALPHANUMERIC: - return this.isAlphanumeric(value, metadata.constraints[0]); - case ValidationTypes.IS_DECIMAL: - return this.isDecimal(value, metadata.constraints[0]); - case ValidationTypes.IS_ASCII: - return this.isAscii(value); - case ValidationTypes.IS_BASE64: - return this.isBase64(value); - case ValidationTypes.IS_BYTE_LENGTH: - return this.isByteLength(value, metadata.constraints[0], metadata.constraints[1]); - case ValidationTypes.IS_CREDIT_CARD: - return this.isCreditCard(value); - case ValidationTypes.IS_CURRENCY: - return this.isCurrency(value, metadata.constraints[0]); - case ValidationTypes.IS_EMAIL: - return this.isEmail(value, metadata.constraints[0]); - case ValidationTypes.IS_FQDN: - return this.isFQDN(value, metadata.constraints[0]); - case ValidationTypes.IS_FULL_WIDTH: - return this.isFullWidth(value); - case ValidationTypes.IS_HALF_WIDTH: - return this.isHalfWidth(value); - case ValidationTypes.IS_VARIABLE_WIDTH: - return this.isVariableWidth(value); - case ValidationTypes.IS_HEX_COLOR: - return this.isHexColor(value); - case ValidationTypes.IS_HEXADECIMAL: - return this.isHexadecimal(value); - case ValidationTypes.IS_MAC_ADDRESS: - return this.isMACAddress(value); - case ValidationTypes.IS_IP: - return this.isIP(value, metadata.constraints[0]); - case ValidationTypes.IS_PORT: - return this.isPort(value); - case ValidationTypes.IS_ISBN: - return this.isISBN(value, metadata.constraints[0]); - case ValidationTypes.IS_ISIN: - return this.isISIN(value); - case ValidationTypes.IS_ISO8601: - return this.isISO8601(value, metadata.constraints[0]); - case ValidationTypes.IS_JSON: - return this.isJSON(value); - case ValidationTypes.IS_JWT: - return this.isJWT(value); - case ValidationTypes.IS_OBJECT: - return this.isObject(value); - case ValidationTypes.IS_NOT_EMPTY_OBJECT: - return this.isNotEmptyObject(value); - case ValidationTypes.IS_LOWERCASE: - return this.isLowercase(value); - case ValidationTypes.IS_MOBILE_PHONE: - return this.isMobilePhone(value, metadata.constraints[0]); - case ValidationTypes.IS_PHONE_NUMBER: - return this.isPhoneNumber(value, metadata.constraints[0]); - case ValidationTypes.IS_ISO31661_ALPHA_2: - return this.isISO31661Alpha2(value); - case ValidationTypes.IS_ISO31661_ALPHA_3: - return this.isISO31661Alpha3(value); - case ValidationTypes.IS_MONGO_ID: - return this.isMongoId(value); - case ValidationTypes.IS_MULTIBYTE: - return this.isMultibyte(value); - case ValidationTypes.IS_SURROGATE_PAIR: - return this.isSurrogatePair(value); - case ValidationTypes.IS_URL: - return this.isURL(value, metadata.constraints[0]); - case ValidationTypes.IS_UUID: - return this.isUUID(value, metadata.constraints[0]); - case ValidationTypes.IS_FIREBASE_PUSH_ID: - return this.IsFirebasePushId(value); - case ValidationTypes.IS_UPPERCASE: - return this.isUppercase(value); - case ValidationTypes.LENGTH: - return this.length(value, metadata.constraints[0], metadata.constraints[1]); - case ValidationTypes.MIN_LENGTH: - return this.minLength(value, metadata.constraints[0]); - case ValidationTypes.MAX_LENGTH: - return this.maxLength(value, metadata.constraints[0]); - case ValidationTypes.MATCHES: - return this.matches(value, metadata.constraints[0], metadata.constraints[1]); - case ValidationTypes.IS_MILITARY_TIME: - return this.isMilitaryTime(value); - case ValidationTypes.IS_HASH: - return this.isHash(value, metadata.constraints[0]); - case ValidationTypes.IS_ISSN: - return this.isISSN(value, metadata.constraints[0]); - - /* array checkers */ - case ValidationTypes.ARRAY_CONTAINS: - return this.arrayContains(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_NOT_CONTAINS: - return this.arrayNotContains(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_NOT_EMPTY: - return this.arrayNotEmpty(value); - case ValidationTypes.ARRAY_MIN_SIZE: - return this.arrayMinSize(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_MAX_SIZE: - return this.arrayMaxSize(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_UNIQUE: - return this.arrayUnique(value); - - case ValidationTypes.IS_INSTANCE: - return this.isInstance(value, metadata.constraints[0]); - } - return true; - } - - // ------------------------------------------------------------------------- - // Validation Methods: common checkers - // ------------------------------------------------------------------------- - - /** - * Checks if value is defined (!== undefined, !== null). - */ - isDefined(value: unknown): boolean { - return value !== undefined && value !== null; - } - - /** - * Checks if value matches ("===") the comparison. - */ - equals(value: unknown, comparison: unknown): boolean { - return value === comparison; - } - - /** - * Checks if value does not match ("!==") the comparison. - */ - notEquals(value: unknown, comparison: unknown): boolean { - return value !== comparison; - } - - /** - * Checks if given value is empty (=== '', === null, === undefined). - */ - isEmpty(value: unknown): boolean { - return value === "" || value === null || value === undefined; - } - - /** - * Checks if given value is not empty (!== '', !== null, !== undefined). - */ - isNotEmpty(value: unknown): boolean { - return value !== "" && value !== null && value !== undefined; - } - - /** - * Checks if given value is in a array of allowed values. - */ - isIn(value: unknown, possibleValues: unknown[]): boolean { - return !(possibleValues instanceof Array) || possibleValues.some(possibleValue => possibleValue === value); - } - - /** - * Checks if given value not in a array of allowed values. - */ - isNotIn(value: unknown, possibleValues: unknown[]): boolean { - return !(possibleValues instanceof Array) || !possibleValues.some(possibleValue => possibleValue === value); - } - - // ------------------------------------------------------------------------- - // Validation Methods: type checkers - // ------------------------------------------------------------------------- - - /** - * Checks if a given value is a real boolean. - */ - isBoolean(value: unknown): boolean { - return value instanceof Boolean || typeof value === "boolean"; - } - - - /** - * Checks if a given value is a latitude. - */ - isLatLong(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isLatLong(value); - } - - /** - * Checks if a given value is a latitude. - */ - isLatitude(value: unknown): boolean { - return (typeof value === "number" || this.isString(value)) && this.isLatLong(`${value},0`); - } - - /** - * Checks if a given value is a longitude. - */ - isLongitude(value: unknown): boolean { - return (typeof value === "number" || this.isString(value)) && this.isLatLong(`0,${value}`); - } - - /** - * Checks if a given value is a real date. - */ - isDate(value: unknown): boolean { - return value instanceof Date && !isNaN(value.getTime()); - } - - /** - * Checks if a given value is a real string. - */ - isString(value: unknown): value is string { - return value instanceof String || typeof value === "string"; - } - - /** - * Checks if a given value is a ISOString date. - */ - isDateString(value: unknown): boolean { - const regex = /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[\+\-][0-2]\d(?:\:[0-5]\d)?)?$/g; - return this.isString(value) && regex.test(value); - } - - /** - * Checks if a given value is an array - */ - isArray(value: unknown): boolean { - return value instanceof Array; - } - - /** - * Checks if a given value is an enum - */ - isEnum(value: unknown, entity: any): boolean { - const enumValues = Object.keys(entity) - .map(k => entity[k]); - return enumValues.indexOf(value) >= 0; - } - - /** - * Checks if a given value is a number. - */ - isNumber(value: unknown, options: IsNumberOptions = {}): boolean { - if (typeof value !== "number") { - return false; - } - - if (value === Infinity || value === -Infinity) { - return options.allowInfinity; - } - - if (Number.isNaN(value)) { - return options.allowNaN; - } - - if (options.maxDecimalPlaces !== undefined) { - let decimalPlaces = 0; - if ((value % 1) !== 0) { - decimalPlaces = value.toString().split(".")[1].length; - } - if (decimalPlaces > options.maxDecimalPlaces) { - return false; - } - } - - return Number.isFinite(value); - } - - /** - * Checks if value is an integer. - */ - isInt(val: unknown): boolean { - return typeof val === "number" && Number.isInteger(val); - } - - // ------------------------------------------------------------------------- - // Validation Methods: number checkers - // ------------------------------------------------------------------------- - - /** - * Checks if value is a number that's divisible by another. - */ - isDivisibleBy(value: unknown, num: number): boolean { - return typeof value === "number" && - typeof num === "number" && - this.validatorJs.isDivisibleBy(String(value), num); - } - - /** - * Checks if the value is a positive number. - */ - isPositive(value: unknown): boolean { - return typeof value === "number" && value > 0; - } - - /** - * Checks if the value is a negative number. - */ - isNegative(value: unknown): boolean { - return typeof value === "number" && value < 0; - } - - /** - * Checks if the first number is greater than or equal to the second. - */ - min(num: unknown, min: number): boolean { - return typeof num === "number" && typeof min === "number" && num >= min; - } - - /** - * Checks if the first number is less than or equal to the second. - */ - max(num: unknown, max: number): boolean { - return typeof num === "number" && typeof max === "number" && num <= max; - } - - // ------------------------------------------------------------------------- - // Validation Methods: date checkers - // ------------------------------------------------------------------------- - - /** - * Checks if the value is a date that's after the specified date. - */ - minDate(date: unknown, minDate: Date): boolean { - return date instanceof Date && date.getTime() >= minDate.getTime(); - } - - /** - * Checks if the value is a date that's before the specified date. - */ - maxDate(date: unknown, maxDate: Date): boolean { - return date instanceof Date && date.getTime() <= maxDate.getTime(); - } - - // ------------------------------------------------------------------------- - // Validation Methods: string-as-type checkers - // ------------------------------------------------------------------------- - - /** - * Checks if a string is a boolean. - * If given value is not a string, then it returns false. - */ - isBooleanString(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isBoolean(value); - } - - /** - * Checks if the string is numeric. - * If given value is not a string, then it returns false. - */ - isNumberString(value: unknown, options?: ValidatorJS.IsNumericOptions): boolean { - return typeof value === "string" && this.validatorJs.isNumeric(value, options); - } - - // ------------------------------------------------------------------------- - // Validation Methods: string checkers - // ------------------------------------------------------------------------- - - /** - * Checks if the string contains the seed. - * If given value is not a string, then it returns false. - */ - contains(value: unknown, seed: string): boolean { - return typeof value === "string" && this.validatorJs.contains(value, seed); - } - - /** - * Checks if the string does not contain the seed. - * If given value is not a string, then it returns false. - */ - notContains(value: unknown, seed: string): boolean { - return typeof value === "string" && !this.validatorJs.contains(value, seed); - } - - /** - * Checks if the string contains only letters (a-zA-Z). - * If given value is not a string, then it returns false. - */ - isAlpha(value: unknown, locale?: ValidatorJS.AlphaLocale): boolean { - return typeof value === "string" && this.validatorJs.isAlpha(value, locale); - } - - /** - * Checks if the string contains only letters and numbers. - * If given value is not a string, then it returns false. - */ - isAlphanumeric(value: unknown, locale?: ValidatorJS.AlphanumericLocale): boolean { - return typeof value === "string" && this.validatorJs.isAlphanumeric(value, locale); - } - - /** - * Checks if the string is a valid decimal. - * If given value is not a string, then it returns false. - */ - isDecimal(value: unknown, options?: ValidatorJS.IsDecimalOptions): boolean { - return typeof value === "string" && this.validatorJs.isDecimal(value, options); - } - - - /** - * Checks if the string contains ASCII chars only. - * If given value is not a string, then it returns false. - */ - isAscii(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isAscii(value); - } - - /** - * Checks if a string is base64 encoded. - * If given value is not a string, then it returns false. - */ - isBase64(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isBase64(value); - } - - /** - * Checks if the string's length (in bytes) falls in a range. - * If given value is not a string, then it returns false. - */ - isByteLength(value: unknown, min: number, max?: number): boolean { - return typeof value === "string" && this.validatorJs.isByteLength(value, min, max); - } - - /** - * Checks if the string is a credit card. - * If given value is not a string, then it returns false. - */ - isCreditCard(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isCreditCard(value); - } - - /** - * Checks if the string is a valid currency amount. - * If given value is not a string, then it returns false. - */ - isCurrency(value: unknown, options?: ValidatorJS.IsCurrencyOptions): boolean { - return typeof value === "string" && this.validatorJs.isCurrency(value, options); - } - - /** - * Checks if the string is an email. - * If given value is not a string, then it returns false. - */ - isEmail(value: unknown, options?: ValidatorJS.IsEmailOptions): boolean { - return typeof value === "string" && this.validatorJs.isEmail(value, options); - } - - /** - * Checks if the string is a fully qualified domain name (e.g. domain.com). - * If given value is not a string, then it returns false. - */ - isFQDN(value: unknown, options?: ValidatorJS.IsFQDNOptions): boolean { - return typeof value === "string" && this.validatorJs.isFQDN(value, options); - } - - /** - * Checks if the string contains any full-width chars. - * If given value is not a string, then it returns false. - */ - isFullWidth(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isFullWidth(value); - } - - /** - * Checks if the string contains any half-width chars. - * If given value is not a string, then it returns false. - */ - isHalfWidth(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isHalfWidth(value); - } - - /** - * Checks if the string contains variable-width chars. - * If given value is not a string, then it returns false. - */ - isVariableWidth(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isVariableWidth(value); - } - - /** - * Checks if the string is a hexadecimal color. - * If given value is not a string, then it returns false. - */ - isHexColor(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isHexColor(value); - } - - /** - * Checks if the string is a hexadecimal number. - * If given value is not a string, then it returns false. - */ - isHexadecimal(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isHexadecimal(value); - } - - /** - * Check if the string is a MAC address. - * If given value is not a string, then it returns false. - */ - isMACAddress(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isMACAddress(value); - } - - /** - * Checks if the string is an IP (version 4 or 6). - * If given value is not a string, then it returns false. - */ - isIP(value: unknown, version?: number): boolean { - return typeof value === "string" && this.validatorJs.isIP(value, version); - } - - /** - * Check if the string is a valid port number. - */ - isPort(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isPort(value); - } - - /** - * Checks if the string is an ISBN (version 10 or 13). - * If given value is not a string, then it returns false. - */ - isISBN(value: unknown, version?: number): boolean { - return typeof value === "string" && this.validatorJs.isISBN(value, version); - } - - /** - * Checks if the string is an ISIN (stock/security identifier). - * If given value is not a string, then it returns false. - */ - isISIN(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isISIN(value); - } - - /** - * Checks if the string is a valid ISO 8601 date. - * If given value is not a string, then it returns false. - * Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. - */ - isISO8601(value: unknown, options?: ValidatorJS.IsISO8601Options): boolean { - return typeof value === "string" && this.validatorJs.isISO8601(value, options); - } - - /** - * Checks if the string is valid JSON (note: uses JSON.parse). - * If given value is not a string, then it returns false. - */ - isJSON(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isJSON(value); - } - - /** - * Checks if the string is valid JWT token. - * If given value is not a string, then it returns false. - */ - isJWT(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isJWT(value); - } - - /** - * Checks if the value is valid Object. - * Returns false if the value is not an object. - */ - isObject(value: unknown): value is object { - return value != null && (typeof value === "object" || typeof value === "function") && !Array.isArray(value); - } - - /** - * Checks if the value is valid Object & not empty. - * Returns false if the value is not an object or an empty valid object. - */ - isNotEmptyObject(value: unknown): boolean { - return this.isObject(value) && !this._isEmptyObject(value); - } - - /** - * Checks if the string is lowercase. - * If given value is not a string, then it returns false. - */ - isLowercase(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isLowercase(value); - } - - /** - * Checks if the string is a mobile phone number (locale is one of ['zh-CN', 'zh-TW', 'en-ZA', 'en-AU', 'en-HK', - * 'pt-PT', 'fr-FR', 'el-GR', 'en-GB', 'en-US', 'en-ZM', 'ru-RU', 'nb-NO', 'nn-NO', 'vi-VN', 'en-NZ']). - * If given value is not a string, then it returns false. - */ - isMobilePhone(value: unknown, locale: ValidatorJS.MobilePhoneLocale): boolean { - return typeof value === "string" && this.validatorJs.isMobilePhone(value, locale); - } - - /** - * Checks if the string is a valid phone number. - * @param value the potential phone number string to test - * @param {string} region 2 characters uppercase country code (e.g. DE, US, CH). - * If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region. - * See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33} - */ - isPhoneNumber(value: unknown, region: string): boolean { - try { - const phoneNum = this.libPhoneNumber.phoneUtil.parseAndKeepRawInput(value, region); - return this.libPhoneNumber.phoneUtil.isValidNumber(phoneNum); - } catch (error) { - // logging? - return false; - } - } - - /** - * 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. - */ - isISO31661Alpha2(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isISO31661Alpha2(value); - } - - /** - * 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. - */ - isISO31661Alpha3(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isISO31661Alpha3(value); - } - - /** - * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. - * If given value is not a string, then it returns false. - */ - isMongoId(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isMongoId(value); - } - - /** - * Checks if the string contains one or more multibyte chars. - * If given value is not a string, then it returns false. - */ - isMultibyte(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isMultibyte(value); - } - - /** - * Checks if the string contains any surrogate pairs chars. - * If given value is not a string, then it returns false. - */ - isSurrogatePair(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isSurrogatePair(value); - } - - /** - * Checks if the string is an url. - * If given value is not a string, then it returns false. - */ - isURL(value: unknown, options?: ValidatorJS.IsURLOptions): boolean { - return typeof value === "string" && this.validatorJs.isURL(value, options); - } - - /** - * Checks if the string is a UUID (version 3, 4 or 5). - * If given value is not a string, then it returns false. - */ - isUUID(value: unknown, version?: "3"|"4"|"5"|"all"): boolean { - return typeof value === "string" && this.validatorJs.isUUID(value, version); - } - - /** - * Checks if the string is a Firebase Push Id - * If given value is not a Firebase Push Id, it returns false - */ - IsFirebasePushId(value: unknown): boolean { - return typeof value === "string" && value.length === 20 && this.webSafeRegex.test(value); - } - /** - * Checks if the string is uppercase. - * If given value is not a string, then it returns false. - */ - isUppercase(value: unknown): boolean { - return typeof value === "string" && this.validatorJs.isUppercase(value); - } - - /** - * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. - * If given value is not a string, then it returns false. - */ - length(value: unknown, min: number, max?: number): boolean { - return typeof value === "string" && this.validatorJs.isLength(value, min, max); - } - - /** - * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. - * If given value is not a string, then it returns false. - */ - minLength(value: unknown, min: number) { - return typeof value === "string" && this.length(value, min); - } - - /** - * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. - * If given value is not a string, then it returns false. - */ - maxLength(value: unknown, max: number) { - return typeof value === "string" && this.length(value, 0, max); - } - - /** - * Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). - * If given value is not a string, then it returns false. - */ - matches(value: unknown, pattern: RegExp, modifiers?: string): boolean { - return typeof value === "string" && this.validatorJs.matches(value, pattern, modifiers); - } - - /** - * Checks if the string represents a time without a given timezone in the format HH:MM (military) - * If the given value does not match the pattern HH:MM, then it returns false. - */ - isMilitaryTime(value: unknown): boolean { - return this.matches(value, /^([01]\d|2[0-3]):?([0-5]\d)$/); - } - - /** - * check if the string is a hash of type algorithm. - * Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', - * 'tiger160', 'tiger192', 'crc32', 'crc32b'] - */ - isHash(value: unknown, algorithm: ValidatorJS.HashAlgorithm): boolean { - return typeof value === "string" && this.validatorJs.isHash(value, algorithm); - } - - /** - * Checks if the string is a ISSN. - * If given value is not a string, then it returns false. - */ - isISSN(value: unknown, options?: ValidatorJS.IsISSNOptions): boolean { - return typeof value === "string" && this.validatorJs.isISSN(value, options); - } - - // ------------------------------------------------------------------------- - // Validation Methods: array checkers - // ------------------------------------------------------------------------- - - /** - * Checks if array contains all values from the given array of values. - * If null or undefined is given then this function returns false. - */ - arrayContains(array: unknown, values: any[]) { - if (!(array instanceof Array)) - return false; - - return values.every(value => array.indexOf(value) !== -1); - } - - /** - * Checks if array does not contain any of the given values. - * If null or undefined is given then this function returns false. - */ - arrayNotContains(array: unknown, values: any[]) { - if (!(array instanceof Array)) - return false; - - return values.every(value => array.indexOf(value) === -1); - } - - /** - * Checks if given array is not empty. - * If null or undefined is given then this function returns false. - */ - arrayNotEmpty(array: unknown) { - return array instanceof Array && array.length > 0; - } - - /** - * Checks if array's length is as minimal this number. - * If null or undefined is given then this function returns false. - */ - arrayMinSize(array: unknown, min: number) { - return array instanceof Array && array.length >= min; - } - - /** - * Checks if array's length is as maximal this number. - * If null or undefined is given then this function returns false. - */ - arrayMaxSize(array: unknown, max: number) { - return array instanceof Array && array.length <= max; - } - - /** - * Checks if all array's values are unique. Comparison for objects is reference-based. - * If null or undefined is given then this function returns false. - */ - arrayUnique(array: unknown) { - if (!(array instanceof Array)) - return false; - - const uniqueItems = array.filter((a, b, c) => c.indexOf(a) === b); - return array.length === uniqueItems.length; - } - - /** - * Checks if the value is an instance of the specified object. - */ - isInstance(object: unknown, targetTypeConstructor: new (...args: any[]) => any) { - return targetTypeConstructor - && typeof targetTypeConstructor === "function" - && object instanceof targetTypeConstructor; - } - } diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index 58e84b900..a97159212 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -78,6 +78,76 @@ import { IsMACAddress, IsISSN, IsFirebasePushId, + isDefined, + isNumber, + isURL, + isBoolean, + isString, + isInt, + isArray, + isEnum, + contains, + isObject, + isNotEmptyObject, + isInstance, + notContains, + isAlpha, + isAlphanumeric, + isAscii, + isDecimal, + isBase64, + isByteLength, + isCreditCard, + isCurrency, + isEmail, + isFQDN, + isFullWidth, + isHalfWidth, + isVariableWidth, + isHexColor, + isHexadecimal, + isMACAddress, + isISBN, + isISO8601, + isIP, + isJSON, + isJWT, + isLowercase, + isMongoId, + isMultibyte, + isSurrogatePair, + isUUID, + isUppercase, + length, + minLength, + maxLength, + isFirebasePushId, + equals, + notEquals, + isEmpty, + isNotEmpty, + isIn, + isNotIn, + isDateString, + isDivisibleBy, + isPositive, + isNegative, + min, + max, + isBooleanString, + isNumberString, + matches, + isHash, + isISSN, + arrayContains, + arrayNotContains, + arrayMinSize, + arrayMaxSize, + arrayUnique, + arrayNotEmpty, + minDate, + maxDate, + isDate } from "../../src/decorator/decorators"; import {Validator} from "../../src/validation/Validator"; import {ValidatorOptions} from "../../src/validation/ValidatorOptions"; @@ -85,6 +155,7 @@ import {ValidatorOptions} from "../../src/validation/ValidatorOptions"; import {should, use } from "chai"; import * as chaiAsPromised from "chai-as-promised"; +import ValidatorJS from "validator"; import IsDecimalOptions = ValidatorJS.IsDecimalOptions; should(); @@ -192,11 +263,11 @@ describe("IsDefined", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isDefined(value).should.be.true); + validValues.forEach(value => isDefined(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isDefined(value).should.be.false); + invalidValues.forEach(value => isDefined(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -227,11 +298,11 @@ describe("Equals", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.equals(value, constraint).should.be.true); + validValues.forEach(value => equals(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.equals(value, constraint).should.be.false); + invalidValues.forEach(value => equals(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -262,11 +333,11 @@ describe("NotEquals", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.notEquals(value, constraint).should.be.true); + validValues.forEach(value => notEquals(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.notEquals(value, constraint).should.be.false); + invalidValues.forEach(value => notEquals(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -296,11 +367,11 @@ describe("IsEmpty", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isEmpty(value).should.be.true); + validValues.forEach(value => isEmpty(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isEmpty(value).should.be.false); + invalidValues.forEach(value => isEmpty(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -330,11 +401,11 @@ describe("IsNotEmpty", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNotEmpty(value).should.be.true); + validValues.forEach(value => isNotEmpty(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNotEmpty(value).should.be.false); + invalidValues.forEach(value => isNotEmpty(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -365,11 +436,11 @@ describe("IsIn", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isIn(value, constraint).should.be.true); + validValues.forEach(value => isIn(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isIn(value, constraint).should.be.false); + invalidValues.forEach(value => isIn(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -400,11 +471,11 @@ describe("IsNotIn", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNotIn(value, constraint).should.be.true); + validValues.forEach(value => isNotIn(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNotIn(value, constraint).should.be.false); + invalidValues.forEach(value => isNotIn(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -438,11 +509,11 @@ describe("IsBoolean", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isBoolean(value).should.be.true); + validValues.forEach(value => isBoolean(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isBoolean(value).should.be.false); + invalidValues.forEach(value => isBoolean(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -534,11 +605,11 @@ describe("IsDate", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isDate(value).should.be.true); + validValues.forEach(value => isDate(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isDate(value).should.be.false); + invalidValues.forEach(value => isDate(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -604,11 +675,11 @@ describe("IsNumber", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNumber(value).should.be.true); + validValues.forEach(value => isNumber(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNumber(value).should.be.false); + invalidValues.forEach(value => isNumber(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -664,11 +735,11 @@ describe("IsInt", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isInt(value).should.be.true); + validValues.forEach(value => isInt(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isInt(value as any).should.be.false); + invalidValues.forEach(value => isInt(value as any).should.be.false); }); it("should return error object with proper data", function(done) { @@ -705,11 +776,11 @@ describe("IsString", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isString(value).should.be.true); + validValues.forEach(value => isString(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isString(value as any).should.be.false); + invalidValues.forEach(value => isString(value as any).should.be.false); }); it("should return error object with proper data", function(done) { @@ -758,11 +829,11 @@ describe("IsDateString", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => expect(validator.isDateString(value)).be.true); + validValues.forEach(value => expect(isDateString(value)).be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => expect(validator.isDateString(value as any)).be.false); + invalidValues.forEach(value => expect(isDateString(value as any)).be.false); }); it("should return error object with proper data", function(done) { @@ -800,11 +871,11 @@ describe("IsArray", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isArray(value).should.be.true); + validValues.forEach(value => isArray(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isArray(value as any).should.be.false); + invalidValues.forEach(value => isArray(value as any).should.be.false); }); it("should return error object with proper data", function(done) { @@ -867,19 +938,19 @@ describe("IsEnum", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isEnum(value, MyEnum).should.be.true); + validValues.forEach(value => isEnum(value, MyEnum).should.be.true); }); it("should not fail if method in validator said that its valid (string enum)", function() { - validStringValues.forEach(value => validator.isEnum(value, MyStringEnum).should.be.true); + validStringValues.forEach(value => isEnum(value, MyStringEnum).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isEnum(value, MyEnum).should.be.false); + invalidValues.forEach(value => isEnum(value, MyEnum).should.be.false); }); it("should fail if method in validator said that its invalid (string enum)", function() { - invalidValues.forEach(value => validator.isEnum(value, MyStringEnum).should.be.false); + invalidValues.forEach(value => isEnum(value, MyStringEnum).should.be.false); }); it("should return error object with proper data", function(done) { @@ -921,11 +992,11 @@ describe("IsDivisibleBy", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isDivisibleBy(value, constraint).should.be.true); + validValues.forEach(value => isDivisibleBy(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isDivisibleBy(value as any, constraint).should.be.false); + invalidValues.forEach(value => isDivisibleBy(value as any, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -977,11 +1048,11 @@ describe("IsPositive", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isPositive(value).should.be.true); + validValues.forEach(value => isPositive(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isPositive(value as any).should.be.false); + invalidValues.forEach(value => isPositive(value as any).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1034,11 +1105,11 @@ describe("IsNegative", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNegative(value).should.be.true); + validValues.forEach(value => isNegative(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNegative(value as any).should.be.false); + invalidValues.forEach(value => isNegative(value as any).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1069,11 +1140,11 @@ describe("Min", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.min(value, constraint).should.be.true); + validValues.forEach(value => min(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.min(value, constraint).should.be.false); + invalidValues.forEach(value => min(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1104,11 +1175,11 @@ describe("Max", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.max(value, constraint).should.be.true); + validValues.forEach(value => max(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.max(value, constraint).should.be.false); + invalidValues.forEach(value => max(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1143,11 +1214,11 @@ describe("MinDate", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.minDate(value, constraint).should.be.true); + validValues.forEach(value => minDate(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.minDate(value, constraint).should.be.false); + invalidValues.forEach(value => minDate(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1178,11 +1249,11 @@ describe("MaxDate", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.maxDate(value, constraint).should.be.true); + validValues.forEach(value => maxDate(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.maxDate(value, constraint).should.be.false); + invalidValues.forEach(value => maxDate(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1225,11 +1296,11 @@ describe("IsBooleanString", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isBooleanString(value).should.be.true); + validValues.forEach(value => isBooleanString(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isBooleanString(value).should.be.false); + invalidValues.forEach(value => isBooleanString(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1270,11 +1341,11 @@ describe("IsNumberString", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNumberString(value).should.be.true); + validValues.forEach(value => isNumberString(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNumberString(value).should.be.false); + invalidValues.forEach(value => isNumberString(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1309,11 +1380,11 @@ describe("Contains", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.contains(value, constraint).should.be.true); + validValues.forEach(value => contains(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.contains(value, constraint).should.be.false); + invalidValues.forEach(value => contains(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1344,11 +1415,11 @@ describe("NotContains", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.notContains(value, constraint).should.be.true); + validValues.forEach(value => notContains(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.notContains(value, constraint).should.be.false); + invalidValues.forEach(value => notContains(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1379,11 +1450,11 @@ describe("IsAlpha", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isAlpha(value, constraint).should.be.true); + validValues.forEach(value => isAlpha(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isAlpha(value, constraint).should.be.false); + invalidValues.forEach(value => isAlpha(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1414,11 +1485,11 @@ describe("IsAlphanumeric", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isAlphanumeric(value).should.be.true); + validValues.forEach(value => isAlphanumeric(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isAlphanumeric(value).should.be.false); + invalidValues.forEach(value => isAlphanumeric(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1449,11 +1520,11 @@ describe("IsAscii", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isAscii(value).should.be.true); + validValues.forEach(value => isAscii(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isAscii(value).should.be.false); + invalidValues.forEach(value => isAscii(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1518,11 +1589,11 @@ describe("IsDecimal", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isDecimal(value, IsDecimalOptions).should.be.true); + validValues.forEach(value => isDecimal(value, IsDecimalOptions).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isDecimal(value, IsDecimalOptions).should.be.false); + invalidValues.forEach(value => isDecimal(value, IsDecimalOptions).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1552,11 +1623,11 @@ describe("IsBase64", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isBase64(value).should.be.true); + validValues.forEach(value => isBase64(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isBase64(value).should.be.false); + invalidValues.forEach(value => isBase64(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1588,11 +1659,11 @@ describe("IsByteLength", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isByteLength(value, constraint1, constraint2).should.be.true); + validValues.forEach(value => isByteLength(value, constraint1, constraint2).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isByteLength(value, constraint1, constraint2).should.be.false); + invalidValues.forEach(value => isByteLength(value, constraint1, constraint2).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1629,11 +1700,11 @@ describe("IsCreditCard", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isCreditCard(value).should.be.true); + validValues.forEach(value => isCreditCard(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isCreditCard(value).should.be.false); + invalidValues.forEach(value => isCreditCard(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1707,11 +1778,11 @@ describe("IsCurrency", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isCurrency(value).should.be.true); + validValues.forEach(value => isCurrency(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isCurrency(value).should.be.false); + invalidValues.forEach(value => isCurrency(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1765,12 +1836,12 @@ describe("IsEmail", function() { it("should not fail if method in validator said that its valid", function() { validValues.forEach(value => { - return validator.isEmail(value).should.be.true; + return isEmail(value).should.be.true; }); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isEmail(value).should.be.false); + invalidValues.forEach(value => isEmail(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1817,11 +1888,11 @@ describe("IsFQDN", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isFQDN(value).should.be.true); + validValues.forEach(value => isFQDN(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isFQDN(value).should.be.false); + invalidValues.forEach(value => isFQDN(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1861,11 +1932,11 @@ describe("IsFullWidth", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isFullWidth(value).should.be.true); + validValues.forEach(value => isFullWidth(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isFullWidth(value).should.be.false); + invalidValues.forEach(value => isFullWidth(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1904,11 +1975,11 @@ describe("IsHalfWidth", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isHalfWidth(value).should.be.true); + validValues.forEach(value => isHalfWidth(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isHalfWidth(value).should.be.false); + invalidValues.forEach(value => isHalfWidth(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1952,11 +2023,11 @@ describe("IsVariableWidth", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isVariableWidth(value).should.be.true); + validValues.forEach(value => isVariableWidth(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isVariableWidth(value).should.be.false); + invalidValues.forEach(value => isVariableWidth(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -1973,13 +2044,13 @@ describe("IsHexColor", function() { "#ff0034" , "#CCCCCC" , "fff" + , "fff0" , "#f00" ]; const invalidValues = [ null , undefined , "#ff" - , "fff0" , "#ff12FG" ]; @@ -1997,11 +2068,11 @@ describe("IsHexColor", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isHexColor(value).should.be.true); + validValues.forEach(value => isHexColor(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isHexColor(value).should.be.false); + invalidValues.forEach(value => isHexColor(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2040,11 +2111,11 @@ describe("IsHexadecimal", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isHexadecimal(value).should.be.true); + validValues.forEach(value => isHexadecimal(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isHexadecimal(value).should.be.false); + invalidValues.forEach(value => isHexadecimal(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2089,11 +2160,11 @@ describe("IsMACAddress", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isMACAddress(value).should.be.true); + validValues.forEach(value => isMACAddress(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isMACAddress(value).should.be.false); + invalidValues.forEach(value => isMACAddress(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2160,11 +2231,11 @@ describe("IsIP", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isIP(value).should.be.true); + validValues.forEach(value => isIP(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isIP(value).should.be.false); + invalidValues.forEach(value => isIP(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2204,11 +2275,11 @@ describe("IsISBN version 10", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISBN(value, 10).should.be.true); + validValues.forEach(value => isISBN(value, "10").should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISBN(value, 10).should.be.false); + invalidValues.forEach(value => isISBN(value, "10").should.be.false); }); it("should return error object with proper data", function(done) { @@ -2246,11 +2317,11 @@ describe("IsISBN version 13", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISBN(value, 13).should.be.true); + validValues.forEach(value => isISBN(value, "13").should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISBN(value, 13).should.be.false); + invalidValues.forEach(value => isISBN(value, "13").should.be.false); }); it("should return error object with proper data", function(done) { @@ -2347,11 +2418,11 @@ describe("IsISO8601", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISO8601(value).should.be.true); + validValues.forEach(value => isISO8601(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISO8601(value).should.be.false); + invalidValues.forEach(value => isISO8601(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2381,11 +2452,11 @@ describe("IsJSON", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isJSON(value).should.be.true); + validValues.forEach(value => isJSON(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isJSON(value).should.be.false); + invalidValues.forEach(value => isJSON(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2425,11 +2496,11 @@ describe("IsJWT", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isJWT(value).should.be.true); + validValues.forEach(value => isJWT(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isJWT(value).should.be.false); + invalidValues.forEach(value => isJWT(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2459,11 +2530,11 @@ describe("IsObject", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isObject(value).should.be.true); + validValues.forEach(value => isObject(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isObject(value).should.be.false); + invalidValues.forEach(value => isObject(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2493,11 +2564,11 @@ describe("IsNotEmptyObject", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNotEmptyObject(value).should.be.true); + validValues.forEach(value => isNotEmptyObject(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNotEmptyObject(value).should.be.false); + invalidValues.forEach(value => isNotEmptyObject(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2537,11 +2608,11 @@ describe("IsLowercase", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isLowercase(value).should.be.true); + validValues.forEach(value => isLowercase(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isLowercase(value).should.be.false); + invalidValues.forEach(value => isLowercase(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2580,11 +2651,11 @@ describe("IsMongoId", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isMongoId(value).should.be.true); + validValues.forEach(value => isMongoId(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isMongoId(value).should.be.false); + invalidValues.forEach(value => isMongoId(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2627,11 +2698,11 @@ describe("IsMultibyte", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isMultibyte(value).should.be.true); + validValues.forEach(value => isMultibyte(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isMultibyte(value).should.be.false); + invalidValues.forEach(value => isMultibyte(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2671,11 +2742,11 @@ describe("IsSurrogatePair", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isSurrogatePair(value).should.be.true); + validValues.forEach(value => isSurrogatePair(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isSurrogatePair(value).should.be.false); + invalidValues.forEach(value => isSurrogatePair(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2770,19 +2841,19 @@ describe("IsUrl", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isURL(value).should.be.true); + validValues.forEach(value => isURL(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isURL(value).should.be.false); + invalidValues.forEach(value => isURL(value).should.be.false); }); it("should fail on localhost without require_tld option", function () { - validator.isURL("http://localhost:3000/").should.be.false; + isURL("http://localhost:3000/").should.be.false; }); it("should pass on localhost with require_tld option", function () { - validator.isURL("http://localhost:3000/", { require_tld: false }).should.be.true; + isURL("http://localhost:3000/", { require_tld: false }).should.be.true; }); it("should return error object with proper data", function(done) { @@ -2826,11 +2897,11 @@ describe("IsUUID", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value).should.be.true); + validValues.forEach(value => isUUID(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value).should.be.false); + invalidValues.forEach(value => isUUID(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -2871,11 +2942,11 @@ describe("IsUUID v3", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value, "3").should.be.true); + validValues.forEach(value => isUUID(value, "3").should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value, "3").should.be.false); + invalidValues.forEach(value => isUUID(value, "3").should.be.false); }); it("should return error object with proper data", function(done) { @@ -2919,11 +2990,11 @@ describe("IsUUID v4", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value, "4").should.be.true); + validValues.forEach(value => isUUID(value, "4").should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value, "4").should.be.false); + invalidValues.forEach(value => isUUID(value, "4").should.be.false); }); it("should return error object with proper data", function(done) { @@ -2967,11 +3038,11 @@ describe("IsUUID v5", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value, "5").should.be.true); + validValues.forEach(value => isUUID(value, "5").should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value, "5").should.be.false); + invalidValues.forEach(value => isUUID(value, "5").should.be.false); }); it("should return error object with proper data", function(done) { @@ -3015,11 +3086,11 @@ describe("IsFirebasePushId", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.IsFirebasePushId(value).should.be.true); + validValues.forEach(value => isFirebasePushId(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.IsFirebasePushId(value).should.be.false); + invalidValues.forEach(value => isFirebasePushId(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3058,11 +3129,11 @@ describe("IsUppercase", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUppercase(value).should.be.true); + validValues.forEach(value => isUppercase(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUppercase(value).should.be.false); + invalidValues.forEach(value => isUppercase(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3094,11 +3165,11 @@ describe("Length", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.length(value, constraint1, constraint2).should.be.true); + validValues.forEach(value => length(value, constraint1, constraint2).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.length(value, constraint1, constraint2).should.be.false); + invalidValues.forEach(value => length(value, constraint1, constraint2).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3135,11 +3206,11 @@ describe("MinLength", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.minLength(value, constraint1).should.be.true); + validValues.forEach(value => minLength(value, constraint1).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.minLength(value, constraint1).should.be.false); + invalidValues.forEach(value => minLength(value, constraint1).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3170,11 +3241,11 @@ describe("MaxLength", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.maxLength(value, constraint1).should.be.true); + validValues.forEach(value => maxLength(value, constraint1).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.maxLength(value, constraint1).should.be.false); + invalidValues.forEach(value => maxLength(value, constraint1).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3205,11 +3276,11 @@ describe("Matches", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.matches(value, constraint).should.be.true); + validValues.forEach(value => matches(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.matches(value, constraint).should.be.false); + invalidValues.forEach(value => matches(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3350,11 +3421,11 @@ describe("isHash", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isHash(value, algorithm).should.be.true); + validValues.forEach(value => isHash(value, algorithm).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isHash(value, algorithm).should.be.false); + invalidValues.forEach(value => isHash(value, algorithm).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3536,11 +3607,11 @@ describe("IsISSN", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISSN(value).should.be.true); + validValues.forEach(value => isISSN(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISSN(value).should.be.false); + invalidValues.forEach(value => isISSN(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3582,11 +3653,11 @@ describe("IsISSN with options", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISSN(value, options).should.be.true); + validValues.forEach(value => isISSN(value, options).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISSN(value, options).should.be.false); + invalidValues.forEach(value => isISSN(value, options).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3625,11 +3696,11 @@ describe("ArrayContains", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayContains(value, constraint).should.be.true); + validValues.forEach(value => arrayContains(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayContains(value, constraint).should.be.false); + invalidValues.forEach(value => arrayContains(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3660,11 +3731,11 @@ describe("ArrayNotContains", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayNotContains(value, constraint).should.be.true); + validValues.forEach(value => arrayNotContains(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayNotContains(value, constraint).should.be.false); + invalidValues.forEach(value => arrayNotContains(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3694,11 +3765,11 @@ describe("ArrayNotEmpty", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayNotEmpty(value).should.be.true); + validValues.forEach(value => arrayNotEmpty(value).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayNotEmpty(value).should.be.false); + invalidValues.forEach(value => arrayNotEmpty(value).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3729,11 +3800,11 @@ describe("ArrayMinSize", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayMinSize(value, constraint).should.be.true); + validValues.forEach(value => arrayMinSize(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayMinSize(value, constraint).should.be.false); + invalidValues.forEach(value => arrayMinSize(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3764,11 +3835,11 @@ describe("ArrayMaxSize", function() { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayMaxSize(value, constraint).should.be.true); + validValues.forEach(value => arrayMaxSize(value, constraint).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayMaxSize(value, constraint).should.be.false); + invalidValues.forEach(value => arrayMaxSize(value, constraint).should.be.false); }); it("should return error object with proper data", function(done) { @@ -3798,11 +3869,11 @@ describe("ArrayUnique", function () { }); it("should not fail if method in validator said that its valid", function () { - validValues.forEach(value => validator.arrayUnique(value).should.be.true); + validValues.forEach(value => arrayUnique(value).should.be.true); }); it("should fail if method in validator said that its invalid", function () { - invalidValues.forEach(value => validator.arrayUnique(value).should.be.false); + invalidValues.forEach(value => arrayUnique(value).should.be.false); }); it("should return error object with proper data", function (done) { @@ -3835,11 +3906,11 @@ describe("isInstance", function () { }); it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isInstance(value, MySubClass).should.be.true); + validValues.forEach(value => isInstance(value, MySubClass).should.be.true); }); it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isInstance(value, MySubClass).should.be.false); + invalidValues.forEach(value => isInstance(value, MySubClass).should.be.false); }); it("should return error object with proper data", function(done) { diff --git a/tsconfig.json b/tsconfig.json index 0efa191ac..a08e39dd4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,25 @@ { "version": "2.0.3", "compilerOptions": { - "outDir": "build/compiled", + "outDir": "dist/esm5", "target": "es5", "module": "commonjs", "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, "sourceMap": true, + "stripInternal": true, "noImplicitAny": true, "declaration": true, - "lib": ["es6"] + "declarationMap": true, + "declarationDir": "dist/types", + "lib": ["es6"], + "resolveJsonModule": true, + "importHelpers": true, }, "exclude": [ "build", + "dist", "node_modules" ] }