express-validator
Advanced tools
Comparing version 6.15.0 to 7.0.0
@@ -10,3 +10,3 @@ { | ||
], | ||
"version": "6.15.0", | ||
"version": "7.0.0", | ||
"homepage": "https://express-validator.github.io", | ||
@@ -22,4 +22,2 @@ "license": "MIT", | ||
"src", | ||
"check", | ||
"filter", | ||
"!**/*.spec.ts", | ||
@@ -33,5 +31,6 @@ "!**/*.ts", | ||
"build": "tsc", | ||
"clean": "git clean -Xf src check filter", | ||
"clean": "git clean -Xf src", | ||
"docs:build": "npm --prefix ./website run build", | ||
"docs:publish": "USE_SSH=true DEPLOYMENT_BRANCH=master npm --prefix ./website run publish-gh-pages", | ||
"docs:regenerate-api": "npm --prefix ./website run regenerate-api", | ||
"docs:serve": "npm --prefix ./website run serve", | ||
@@ -38,0 +37,0 @@ "docs:start": "npm --prefix ./website start", |
# express-validator | ||
[![npm version](https://img.shields.io/npm/v/express-validator.svg)](https://www.npmjs.com/package/express-validator) | ||
[![Build Status](https://img.shields.io/travis/express-validator/express-validator.svg)](http://travis-ci.org/express-validator/express-validator) | ||
[![Build status](https://github.com/express-validator/express-validator/actions/workflows/ci.yml/badge.svg)](https://github.com/express-validator/express-validator/actions/workflows/ci.yml) | ||
[![Coverage Status](https://img.shields.io/coveralls/express-validator/express-validator.svg)](https://coveralls.io/github/express-validator/express-validator?branch=master) | ||
@@ -6,0 +6,0 @@ |
@@ -44,9 +44,2 @@ import { ReadonlyContext } from './context'; | ||
export declare type StandardSanitizer = (input: string, ...options: any[]) => any; | ||
/** | ||
* A function which returns an error message based on a field's value. | ||
* | ||
* @param input the field value | ||
* @param meta metadata about the field that was validated | ||
*/ | ||
export declare type DynamicMessageCreator = (value: any, meta: Meta) => any; | ||
export interface FieldInstance { | ||
@@ -57,18 +50,10 @@ path: string; | ||
value: any; | ||
originalValue: any; | ||
} | ||
export declare type ValidationError = { | ||
param: '_error'; | ||
export declare type UnknownFieldInstance = Omit<FieldInstance, 'originalPath'>; | ||
export declare type FieldValidationError = { | ||
/** | ||
* The error message | ||
* Indicates that the error occurred because a field had an invalid value | ||
*/ | ||
msg: any; | ||
type: 'field'; | ||
/** | ||
* The list of underlying validation errors returned by validation chains in `oneOf()` | ||
*/ | ||
nestedErrors: ValidationError[]; | ||
location?: undefined; | ||
value?: undefined; | ||
} | { | ||
/** | ||
* The location within the request where this field is | ||
@@ -78,5 +63,5 @@ */ | ||
/** | ||
* The name of the field which has a validation error | ||
* The path to the field which has a validation error | ||
*/ | ||
param: string; | ||
path: string; | ||
/** | ||
@@ -90,4 +75,101 @@ * The value of the field | ||
msg: any; | ||
nestedErrors?: unknown[]; | ||
}; | ||
export declare type UnknownFieldsError = { | ||
/** | ||
* Indicates that the error occurred because one or more fields are unknown in the request | ||
*/ | ||
type: 'unknown_fields'; | ||
/** | ||
* The error message | ||
*/ | ||
msg: any; | ||
/** | ||
* The list of fields that are unknown | ||
*/ | ||
fields: UnknownFieldInstance[]; | ||
}; | ||
export declare type AlternativeValidationError = { | ||
/** | ||
* Indicates that the error occurred because all alternatives (e.g. in `oneOf()`) were invalid | ||
*/ | ||
type: 'alternative'; | ||
/** | ||
* The error message | ||
*/ | ||
msg: any; | ||
/** | ||
* The list of underlying validation errors returned by validation chains in `oneOf()` | ||
*/ | ||
nestedErrors: FieldValidationError[]; | ||
}; | ||
export declare type GroupedAlternativeValidationError = { | ||
/** | ||
* Indicates that the error occurred because all alternatives (e.g. in `oneOf()`) were invalid, | ||
* and the nested errors are grouped per alternative. | ||
*/ | ||
type: 'alternative_grouped'; | ||
/** | ||
* The error message | ||
*/ | ||
msg: any; | ||
/** | ||
* The list of underlying validation errors returned by validation chains in `oneOf()` | ||
*/ | ||
nestedErrors: FieldValidationError[][]; | ||
}; | ||
/** | ||
* A validation error as reported by a middleware. | ||
* The properties available in the error object vary according to the type. | ||
* | ||
* @example | ||
* if (error.type === 'alternative') { | ||
* console.log(`There are ${error.nestedErrors.length} errors under this alternative list`); | ||
* } else if (error.type === 'field') { | ||
* console.log(`There's an error with field ${error.path) in the request ${error.location}`); | ||
* } | ||
* | ||
*/ | ||
export declare type ValidationError = AlternativeValidationError | GroupedAlternativeValidationError | UnknownFieldsError | FieldValidationError; | ||
/** | ||
* An error message that's not a function, as these are treated as message factories | ||
* by all validation middlewares. | ||
*/ | ||
export declare type ErrorMessage = string | number | symbol | boolean | object | any[]; | ||
/** | ||
* A function which creates an error message based on a field's value. | ||
* | ||
* @param input the field value | ||
* @param meta metadata about the field that was validated | ||
*/ | ||
export declare type FieldMessageFactory = (value: any, meta: Meta) => any; | ||
/** | ||
* A function which creates an error message based on an alternative's nested errors. | ||
* | ||
* @see `oneOf()` | ||
* @param nestedErrors The errors from the invalid alternative(s). | ||
* @param opts | ||
*/ | ||
export declare type AlternativeMessageFactory = (nestedErrors: FieldValidationError[], opts: { | ||
req: Request; | ||
}) => any; | ||
/** | ||
* A function which creates an error message based on a group of alternatives nested errors. | ||
* | ||
* @see `oneOf()` | ||
* @param nestedErrors The errors from the invalid alternative groups. | ||
* @param opts | ||
*/ | ||
export declare type GroupedAlternativeMessageFactory = (nestedErrors: FieldValidationError[][], opts: { | ||
req: Request; | ||
}) => any; | ||
/** | ||
* A function which creates an error message based on unknown fields. | ||
* | ||
* @see `checkExact()` | ||
* @param unknownFields The unknown fields found in the request | ||
* @param opts | ||
*/ | ||
export declare type UnknownFieldMessageFactory = (unknownFields: UnknownFieldInstance[], opts: { | ||
req: Request; | ||
}) => any; | ||
export declare const contextsKey = "express-validator#contexts"; | ||
@@ -94,0 +176,0 @@ export interface InternalRequest extends Request { |
import { ContextBuilder } from '../context-builder'; | ||
import { Optional } from '../context'; | ||
import { CustomValidator } from '../base'; | ||
import { ContextHandler } from './context-handler'; | ||
import { ValidationChain } from './validation-chain'; | ||
import { BailOptions, ContextHandler, OptionalOptions } from './context-handler'; | ||
import { ContextRunner } from './context-runner'; | ||
export declare class ContextHandlerImpl<Chain> implements ContextHandler<Chain> { | ||
@@ -10,5 +9,5 @@ private readonly builder; | ||
constructor(builder: ContextBuilder, chain: Chain); | ||
bail(): Chain; | ||
if(condition: CustomValidator | ValidationChain): Chain; | ||
optional(options?: Optional | true): Chain; | ||
bail(opts?: BailOptions): Chain; | ||
if(condition: CustomValidator | ContextRunner): Chain; | ||
optional(options?: OptionalOptions | boolean): Chain; | ||
} |
@@ -11,3 +11,6 @@ "use strict"; | ||
} | ||
bail() { | ||
bail(opts) { | ||
if (opts?.level === 'request') { | ||
this.builder.setRequestBail(); | ||
} | ||
this.builder.addItem(new bail_1.Bail()); | ||
@@ -29,11 +32,16 @@ return this.chain; | ||
optional(options = true) { | ||
let value; | ||
if (typeof options === 'boolean') { | ||
this.builder.setOptional(options ? { checkFalsy: false, nullable: false } : false); | ||
value = options ? 'undefined' : false; | ||
} | ||
else { | ||
this.builder.setOptional({ | ||
checkFalsy: !!options.checkFalsy, | ||
nullable: !!options.nullable, | ||
}); | ||
value = options.values | ||
? options.values | ||
: options.checkFalsy | ||
? 'falsy' | ||
: options.nullable | ||
? 'null' | ||
: 'undefined'; | ||
} | ||
this.builder.setOptional(value); | ||
return this.chain; | ||
@@ -40,0 +48,0 @@ } |
import { CustomValidator } from '../base'; | ||
import { Optional } from '../context'; | ||
import { ValidationChain } from './validation-chain'; | ||
import { ContextRunner } from './context-runner'; | ||
export interface BailOptions { | ||
/** | ||
* Defines the level at which to stop running further validations: | ||
* - When set to `chain`, further validations won't be run for this validation chain if there | ||
* are any errors. | ||
* - When set to `request`, no further validations on the same request will be run either if | ||
* there are any errors. | ||
* | ||
* @default 'chain' | ||
*/ | ||
level?: 'chain' | 'request'; | ||
} | ||
export interface OptionalOptions { | ||
/** | ||
* Defines which kind of value makes a field optional. | ||
* | ||
* - `undefined`: only `undefined` values; equivalent to `value === undefined` | ||
* - `null`: only `undefined` and `null` values; equivalent to `value == null` | ||
* - `falsy`: all falsy values; equivalent to `!value` | ||
* | ||
* @default 'undefined' | ||
*/ | ||
values?: Exclude<Optional, false>; | ||
/** | ||
* Whether a field whose value is `null` or `undefined` is to be considered optional. | ||
* @default false | ||
* @deprecated Use `values` instead. | ||
*/ | ||
nullable?: boolean; | ||
/** | ||
* Whether a field whose value is falsy (that is, `0`, `false`, `null`, `undefined` or an empty | ||
* string) is to be considered optional. | ||
* @default false | ||
* @deprecated Use `values` instead. | ||
*/ | ||
checkFalsy?: boolean; | ||
} | ||
export interface ContextHandler<Chain> { | ||
@@ -25,3 +62,3 @@ /** | ||
*/ | ||
bail(): Chain; | ||
bail(opts?: BailOptions): Chain; | ||
/** | ||
@@ -42,3 +79,3 @@ * Adds a condition on whether the validation should continue on a field or not. | ||
*/ | ||
if(condition: CustomValidator | ValidationChain): Chain; | ||
if(condition: CustomValidator | ContextRunner): Chain; | ||
/** | ||
@@ -52,3 +89,13 @@ * Marks the field(s) of the validation chain as optional. | ||
*/ | ||
optional(options?: Partial<Optional> | true): Chain; | ||
optional(options?: { | ||
values?: Optional; | ||
/** | ||
* @deprecated use `values` instead | ||
*/ | ||
checkFalsy?: boolean; | ||
/** | ||
* @deprecated use `values` instead | ||
*/ | ||
nullable?: boolean; | ||
} | boolean): Chain; | ||
} |
import { Request } from '../base'; | ||
import { Context, ReadonlyContext } from '../context'; | ||
import { ContextBuilder } from '../context-builder'; | ||
import { SelectFields } from '../select-fields'; | ||
import { SelectFields } from '../field-selection'; | ||
import { Result } from '../validation-result'; | ||
import { ContextRunner } from './context-runner'; | ||
export declare class ResultWithContext extends Result { | ||
import { ContextRunner, ResultWithContext } from './context-runner'; | ||
export declare class ResultWithContextImpl extends Result implements ResultWithContext { | ||
readonly context: ReadonlyContext; | ||
@@ -17,3 +17,3 @@ constructor(context: ReadonlyContext); | ||
dryRun?: boolean; | ||
}): Promise<ResultWithContext>; | ||
}): Promise<ResultWithContextImpl>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ContextRunnerImpl = exports.ResultWithContext = void 0; | ||
exports.ContextRunnerImpl = exports.ResultWithContextImpl = void 0; | ||
const _ = require("lodash"); | ||
const base_1 = require("../base"); | ||
const context_1 = require("../context"); | ||
const select_fields_1 = require("../select-fields"); | ||
const field_selection_1 = require("../field-selection"); | ||
const validation_result_1 = require("../validation-result"); | ||
class ResultWithContext extends validation_result_1.Result { | ||
class ResultWithContextImpl extends validation_result_1.Result { | ||
constructor(context) { | ||
@@ -15,5 +15,5 @@ super(error => error, context.errors); | ||
} | ||
exports.ResultWithContext = ResultWithContext; | ||
exports.ResultWithContextImpl = ResultWithContextImpl; | ||
class ContextRunnerImpl { | ||
constructor(builderOrContext, selectFields = select_fields_1.selectFields) { | ||
constructor(builderOrContext, selectFields = field_selection_1.selectFields) { | ||
this.builderOrContext = builderOrContext; | ||
@@ -26,2 +26,7 @@ this.selectFields = selectFields; | ||
: this.builderOrContext.build(); | ||
const internalReq = req; | ||
const bail = internalReq[base_1.contextsKey]?.some(context => context.bail && context.errors.length > 0); | ||
if (bail) { | ||
return new ResultWithContextImpl(context); | ||
} | ||
const instances = this.selectFields(req, context.fields, context.locations); | ||
@@ -63,8 +68,7 @@ context.addFieldInstances(instances); | ||
if (!options.dryRun) { | ||
const internalReq = req; | ||
internalReq[base_1.contextsKey] = (internalReq[base_1.contextsKey] || []).concat(context); | ||
} | ||
return new ResultWithContext(context); | ||
return new ResultWithContextImpl(context); | ||
} | ||
} | ||
exports.ContextRunnerImpl = ContextRunnerImpl; |
@@ -1,5 +0,5 @@ | ||
import { Request } from '../base'; | ||
import { Request, ValidationError } from '../base'; | ||
import { ReadonlyContext } from '../context'; | ||
import { Result } from '../validation-result'; | ||
declare type ContextRunningOptions = { | ||
export declare type ContextRunningOptions = { | ||
/** | ||
@@ -11,2 +11,5 @@ * Defines whether errors and sanitization should be persisted to `req`. | ||
}; | ||
export interface ResultWithContext extends Result<ValidationError> { | ||
readonly context: ReadonlyContext; | ||
} | ||
export interface ContextRunner { | ||
@@ -19,6 +22,3 @@ /** | ||
*/ | ||
run(req: Request, options?: ContextRunningOptions): Promise<Result & { | ||
context: ReadonlyContext; | ||
}>; | ||
run(req: Request, options?: ContextRunningOptions): Promise<ResultWithContext>; | ||
} | ||
export {}; |
@@ -9,3 +9,2 @@ export * from './sanitizers'; | ||
export * from './validators-impl'; | ||
export * from './sanitization-chain'; | ||
export * from './validation-chain'; |
@@ -21,3 +21,2 @@ "use strict"; | ||
__exportStar(require("./validators-impl"), exports); | ||
__exportStar(require("./sanitization-chain"), exports); | ||
__exportStar(require("./validation-chain"), exports); |
@@ -11,1 +11,9 @@ import { Request } from '../base'; | ||
} | ||
/** | ||
* A copy of `ValidationChain` where methods that would return the chain itself can return any other | ||
* value. | ||
* Useful for typing functions which accept either standard or custom validation chains. | ||
*/ | ||
export declare type ValidationChainLike = { | ||
[K in keyof ValidationChain]: ValidationChain[K] extends (...args: infer A) => ValidationChain ? (...args: A) => any : ValidationChain[K]; | ||
}; |
@@ -1,5 +0,5 @@ | ||
import { CustomValidator } from '../base'; | ||
import { CustomValidator, ErrorMessage, FieldMessageFactory } from '../base'; | ||
import { ContextBuilder } from '../context-builder'; | ||
import * as Options from '../options'; | ||
import { Validators } from './validators'; | ||
import { ExistsOptions, Validators } from './validators'; | ||
export declare class ValidatorsImpl<Chain> implements Validators<Chain> { | ||
@@ -13,8 +13,5 @@ private readonly builder; | ||
not(): Chain; | ||
withMessage(message: any): Chain; | ||
withMessage(message: FieldMessageFactory | ErrorMessage): Chain; | ||
custom(validator: CustomValidator): Chain; | ||
exists(options?: { | ||
checkFalsy?: boolean; | ||
checkNull?: boolean; | ||
}): Chain; | ||
exists(options?: ExistsOptions): Chain; | ||
isArray(options?: { | ||
@@ -21,0 +18,0 @@ min?: number; |
@@ -34,6 +34,6 @@ "use strict"; | ||
let validator; | ||
if (options.checkFalsy) { | ||
if (options.checkFalsy || options.values === 'falsy') { | ||
validator = value => !!value; | ||
} | ||
else if (options.checkNull) { | ||
else if (options.checkNull || options.values === 'null') { | ||
validator = value => value != null; | ||
@@ -53,3 +53,3 @@ } | ||
return this.custom(value => typeof value === 'object' && | ||
(options.strict ? value !== null && !Array.isArray(value) : true)); | ||
(options.strict == null || options.strict ? value !== null && !Array.isArray(value) : true)); | ||
} | ||
@@ -78,4 +78,4 @@ isString() { | ||
// TODO(v7): remove string[] support | ||
const ignore = Array.isArray(options === null || options === void 0 ? void 0 : options.ignore) ? options === null || options === void 0 ? void 0 : options.ignore.join('') : options === null || options === void 0 ? void 0 : options.ignore; | ||
return this.addStandardValidation(validator.isAlpha, locale, Object.assign(Object.assign({}, options), { ignore })); | ||
const ignore = Array.isArray(options?.ignore) ? options?.ignore.join('') : options?.ignore; | ||
return this.addStandardValidation(validator.isAlpha, locale, { ...options, ignore }); | ||
} | ||
@@ -109,3 +109,3 @@ isAlphanumeric(locale, options) { | ||
isBoolean(options) { | ||
if (options === null || options === void 0 ? void 0 : options.strict) { | ||
if (options?.strict) { | ||
return this.custom(value => { | ||
@@ -112,0 +112,0 @@ return value === true || value === false; |
@@ -1,3 +0,27 @@ | ||
import { CustomValidator, DynamicMessageCreator } from '../base'; | ||
import { CustomValidator, ErrorMessage, FieldMessageFactory } from '../base'; | ||
import * as Options from '../options'; | ||
export declare type ExistsOptions = { | ||
/** | ||
* Defines which kind of value makes a field _NOT_ exist. | ||
* | ||
* - `undefined`: only `undefined` values; equivalent to `value !== undefined` | ||
* - `null`: only `undefined` and `null` values; equivalent to `value != null` | ||
* - `falsy`: all falsy values; equivalent to `!!value` | ||
* | ||
* @default 'undefined' | ||
*/ | ||
values?: 'undefined' | 'null' | 'falsy'; | ||
/** | ||
* Whether a field whose value is falsy should be considered non-existent. | ||
* @default false | ||
* @deprecated Use `values` instead | ||
*/ | ||
checkFalsy?: boolean; | ||
/** | ||
* Whether a field whose value is `null` or `undefined` should be considered non-existent. | ||
* @default false | ||
* @deprecated Use `values` instead | ||
*/ | ||
checkNull?: boolean; | ||
}; | ||
export interface Validators<Return> { | ||
@@ -14,14 +38,8 @@ /** | ||
* | ||
* @param message a function for dynamically creating the error message based on the field value | ||
* @param message the message, which can be any value, or a function for dynamically creating the | ||
* error message based on the field value | ||
* @returns the current validation chain | ||
*/ | ||
withMessage(message: DynamicMessageCreator): Return; | ||
withMessage(message: FieldMessageFactory | ErrorMessage): Return; | ||
/** | ||
* Sets the error message for the previous validator. | ||
* | ||
* @param message the error message | ||
* @returns the current validation chain | ||
*/ | ||
withMessage(message: any): Return; | ||
/** | ||
* Adds a custom validator to the validation chain. | ||
@@ -41,6 +59,3 @@ * | ||
*/ | ||
exists(options?: { | ||
checkFalsy?: boolean; | ||
checkNull?: boolean; | ||
}): Return; | ||
exists(options?: ExistsOptions): Return; | ||
/** | ||
@@ -47,0 +62,0 @@ * Adds a validator to check if a value is an array. |
@@ -10,2 +10,3 @@ import { ContextItem } from './context-items'; | ||
private optional; | ||
private requestBail; | ||
setFields(fields: string[]): this; | ||
@@ -16,3 +17,4 @@ setLocations(locations: Location[]): this; | ||
setOptional(options: Optional): this; | ||
setRequestBail(): this; | ||
build(): Context; | ||
} |
@@ -11,2 +11,3 @@ "use strict"; | ||
this.optional = false; | ||
this.requestBail = false; | ||
} | ||
@@ -33,6 +34,10 @@ setFields(fields) { | ||
} | ||
setRequestBail() { | ||
this.requestBail = true; | ||
return this; | ||
} | ||
build() { | ||
return new context_1.Context(this.fields, this.locations, this.stack, this.optional, this.message); | ||
return new context_1.Context(this.fields, this.locations, this.stack, this.optional, this.requestBail, this.message); | ||
} | ||
} | ||
exports.ContextBuilder = ContextBuilder; |
@@ -1,3 +0,3 @@ | ||
import { ValidationChain } from '../chain'; | ||
import { Meta } from '../base'; | ||
import { ContextRunner } from '../chain'; | ||
import { Context } from '../context'; | ||
@@ -7,4 +7,4 @@ import { ContextItem } from './context-item'; | ||
private readonly chain; | ||
constructor(chain: ValidationChain); | ||
constructor(chain: ContextRunner); | ||
run(_context: Context, _value: any, meta: Meta): Promise<void>; | ||
} |
@@ -18,3 +18,3 @@ "use strict"; | ||
if ((!isPromise && failed) || (isPromise && this.negated)) { | ||
context.addError(this.message, value, meta); | ||
context.addError({ type: 'field', message: this.message, value, meta }); | ||
} | ||
@@ -26,3 +26,8 @@ } | ||
} | ||
context.addError(this.message || (err instanceof Error ? err.message : err), value, meta); | ||
context.addError({ | ||
type: 'field', | ||
message: this.message || (err instanceof Error ? err.message : err), | ||
value, | ||
meta, | ||
}); | ||
} | ||
@@ -29,0 +34,0 @@ } |
import { Context } from '../context'; | ||
import { CustomSanitizer, Meta, StandardSanitizer } from '../base'; | ||
import { toString as toStringImpl } from '../utils'; | ||
import { ContextItem } from './context-item'; | ||
@@ -8,4 +9,5 @@ export declare class Sanitization implements ContextItem { | ||
private readonly options; | ||
constructor(sanitizer: StandardSanitizer | CustomSanitizer, custom: boolean, options?: any[]); | ||
private readonly stringify; | ||
constructor(sanitizer: StandardSanitizer | CustomSanitizer, custom: boolean, options?: any[], stringify?: typeof toStringImpl); | ||
run(context: Context, value: any, meta: Meta): Promise<void>; | ||
} |
@@ -6,6 +6,10 @@ "use strict"; | ||
class Sanitization { | ||
constructor(sanitizer, custom, options = []) { | ||
constructor(sanitizer, custom, options = [], | ||
// For testing only. | ||
// Deliberately not calling it `toString` in order to not override `Object.prototype.toString`. | ||
stringify = utils_1.toString) { | ||
this.sanitizer = sanitizer; | ||
this.custom = custom; | ||
this.options = options; | ||
this.stringify = stringify; | ||
} | ||
@@ -18,8 +22,15 @@ async run(context, value, meta) { | ||
}; | ||
const newValue = this.custom | ||
? await runCustomSanitizer() | ||
: this.sanitizer(utils_1.toString(value), ...this.options); | ||
context.setData(path, newValue, location); | ||
if (this.custom) { | ||
const newValue = await runCustomSanitizer(); | ||
context.setData(path, newValue, location); | ||
return; | ||
} | ||
const values = Array.isArray(value) ? value : [value]; | ||
const newValues = values.map(value => { | ||
return this.sanitizer(this.stringify(value), ...this.options); | ||
}); | ||
// We get only the first value of the array if the orginal value was wrapped. | ||
context.setData(path, values !== value ? newValues[0] : newValues, location); | ||
} | ||
} | ||
exports.Sanitization = Sanitization; |
import { Meta, StandardValidator } from '../base'; | ||
import { toString as toStringImpl } from '../utils'; | ||
import { Context } from '../context'; | ||
@@ -8,5 +9,6 @@ import { ContextItem } from './context-item'; | ||
private readonly options; | ||
private readonly stringify; | ||
message: any; | ||
constructor(validator: StandardValidator, negated: boolean, options?: any[]); | ||
constructor(validator: StandardValidator, negated: boolean, options?: any[], stringify?: typeof toStringImpl); | ||
run(context: Context, value: any, meta: Meta): Promise<void>; | ||
} |
@@ -6,14 +6,21 @@ "use strict"; | ||
class StandardValidation { | ||
constructor(validator, negated, options = []) { | ||
constructor(validator, negated, options = [], | ||
// For testing only. | ||
// Deliberately not calling it `toString` in order to not override `Object.prototype.toString`. | ||
stringify = utils_1.toString) { | ||
this.validator = validator; | ||
this.negated = negated; | ||
this.options = options; | ||
this.stringify = stringify; | ||
} | ||
async run(context, value, meta) { | ||
const result = this.validator(utils_1.toString(value), ...this.options); | ||
if (this.negated ? result : !result) { | ||
context.addError(this.message, value, meta); | ||
} | ||
const values = Array.isArray(value) ? value : [value]; | ||
values.forEach(value => { | ||
const result = this.validator(this.stringify(value), ...this.options); | ||
if (this.negated ? result : !result) { | ||
context.addError({ type: 'field', message: this.message, value, meta }); | ||
} | ||
}); | ||
} | ||
} | ||
exports.StandardValidation = StandardValidation; |
@@ -1,16 +0,33 @@ | ||
import { FieldInstance, Location, Meta, ValidationError } from './base'; | ||
import { FieldInstance, FieldValidationError, Location, Meta, Request, UnknownFieldInstance, ValidationError } from './base'; | ||
import { ContextItem } from './context-items'; | ||
export declare type Optional = { | ||
/** | ||
* Whether a field whose value is `null` or `undefined` is to be considered optional. | ||
* @default false | ||
*/ | ||
nullable: boolean; | ||
/** | ||
* Whether a field whose value is falsy (that is, `0`, `false`, `null`, `undefined` or an empty | ||
* string) is to be considered optional. | ||
* @default false | ||
*/ | ||
checkFalsy: boolean; | ||
} | false; | ||
/** | ||
* Defines which kind of value makes a field optional. | ||
* | ||
* - `undefined`: only `undefined` values; equivalent to `value === undefined` | ||
* - `null`: only `undefined` and `null` values; equivalent to `value == null` | ||
* - `falsy`: all falsy values; equivalent to `!value` | ||
* - `false`: not optional. | ||
*/ | ||
export declare type Optional = 'undefined' | 'null' | 'falsy' | false; | ||
declare type AddErrorOptions = { | ||
type: 'field'; | ||
message?: any; | ||
value: any; | ||
meta: Meta; | ||
} | { | ||
type: 'unknown_fields'; | ||
req: Request; | ||
message?: any; | ||
fields: UnknownFieldInstance[]; | ||
} | { | ||
type: 'alternative'; | ||
req: Request; | ||
message?: any; | ||
nestedErrors: FieldValidationError[]; | ||
} | { | ||
type: 'alternative_grouped'; | ||
req: Request; | ||
message?: any; | ||
nestedErrors: FieldValidationError[][]; | ||
}; | ||
export declare class Context { | ||
@@ -21,2 +38,3 @@ readonly fields: string[]; | ||
readonly optional: Optional; | ||
readonly bail: boolean; | ||
readonly message?: any; | ||
@@ -26,3 +44,3 @@ private readonly _errors; | ||
private readonly dataMap; | ||
constructor(fields: string[], locations: Location[], stack: ReadonlyArray<ContextItem>, optional: Optional, message?: any); | ||
constructor(fields: string[], locations: Location[], stack: ReadonlyArray<ContextItem>, optional: Optional, bail: boolean, message?: any); | ||
getData(options?: { | ||
@@ -33,5 +51,5 @@ requiredOnly: boolean; | ||
setData(path: string, value: any, location: Location): void; | ||
addError(message: any, value: any, meta: Meta): void; | ||
addError(message: any, nestedErrors: ValidationError[]): void; | ||
addError(opts: AddErrorOptions): void; | ||
} | ||
export declare type ReadonlyContext = Pick<Context, Exclude<keyof Context, 'setData' | 'addFieldInstances' | 'addError'>>; | ||
export {}; |
@@ -9,3 +9,3 @@ "use strict"; | ||
class Context { | ||
constructor(fields, locations, stack, optional, message) { | ||
constructor(fields, locations, stack, optional, bail, message) { | ||
this.fields = fields; | ||
@@ -15,2 +15,3 @@ this.locations = locations; | ||
this.optional = optional; | ||
this.bail = bail; | ||
this.message = message; | ||
@@ -24,4 +25,2 @@ this._errors = []; | ||
getData(options = { requiredOnly: false }) { | ||
// Have to store this.optional in a const otherwise TS thinks the value could have changed | ||
// when the functions below run | ||
const { optional } = this; | ||
@@ -31,4 +30,4 @@ const checks = options.requiredOnly && optional | ||
(value) => value !== undefined, | ||
(value) => (optional.nullable ? value != null : true), | ||
(value) => (optional.checkFalsy ? value : true), | ||
(value) => (optional === 'null' ? value != null : true), | ||
(value) => (optional === 'falsy' ? value : true), | ||
] | ||
@@ -55,3 +54,3 @@ : []; | ||
instances.forEach(instance => { | ||
this.dataMap.set(getDataMapKey(instance.path, instance.location), Object.assign({}, instance)); | ||
this.dataMap.set(getDataMapKey(instance.path, instance.location), { ...instance }); | ||
}); | ||
@@ -66,21 +65,42 @@ } | ||
} | ||
addError(message, valueOrNestedErrors, meta) { | ||
const msg = message || this.message || 'Invalid value'; | ||
if (meta) { | ||
this._errors.push({ | ||
value: valueOrNestedErrors, | ||
msg: typeof msg === 'function' ? msg(valueOrNestedErrors, meta) : msg, | ||
param: meta.path, | ||
location: meta.location, | ||
}); | ||
addError(opts) { | ||
const msg = opts.message || this.message || 'Invalid value'; | ||
let error; | ||
switch (opts.type) { | ||
case 'field': | ||
error = { | ||
type: 'field', | ||
value: opts.value, | ||
msg: typeof msg === 'function' ? msg(opts.value, opts.meta) : msg, | ||
path: opts.meta?.path, | ||
location: opts.meta?.location, | ||
}; | ||
break; | ||
case 'unknown_fields': | ||
error = { | ||
type: 'unknown_fields', | ||
msg: typeof msg === 'function' ? msg(opts.fields, { req: opts.req }) : msg, | ||
fields: opts.fields, | ||
}; | ||
break; | ||
case 'alternative': | ||
error = { | ||
type: 'alternative', | ||
msg: typeof msg === 'function' ? msg(opts.nestedErrors, { req: opts.req }) : msg, | ||
nestedErrors: opts.nestedErrors, | ||
}; | ||
break; | ||
case 'alternative_grouped': | ||
error = { | ||
type: 'alternative_grouped', | ||
msg: typeof msg === 'function' ? msg(opts.nestedErrors, { req: opts.req }) : msg, | ||
nestedErrors: opts.nestedErrors, | ||
}; | ||
break; | ||
default: | ||
throw new Error(`Unhandled addError case`); | ||
} | ||
else { | ||
this._errors.push({ | ||
msg, | ||
param: '_error', | ||
nestedErrors: valueOrNestedErrors, | ||
}); | ||
} | ||
this._errors.push(error); | ||
} | ||
} | ||
exports.Context = Context; |
@@ -1,9 +0,9 @@ | ||
export { Location, Meta, CustomValidator, CustomSanitizer, DynamicMessageCreator, ValidationError, } from './base'; | ||
export { SanitizationChain, ValidationChain } from './chain'; | ||
export { Location, Meta, CustomValidator, CustomSanitizer, AlternativeMessageFactory, FieldMessageFactory, GroupedAlternativeMessageFactory, UnknownFieldMessageFactory, FieldValidationError, AlternativeValidationError, GroupedAlternativeValidationError, UnknownFieldsError, ValidationError, } from './base'; | ||
export { ContextRunner, ValidationChain } from './chain'; | ||
export * from './middlewares/exact'; | ||
export * from './middlewares/one-of'; | ||
export * from './middlewares/sanitization-chain-builders'; | ||
export * from './middlewares/validation-chain-builders'; | ||
export { checkSchema, Schema, ValidationSchema, // Deprecated | ||
ParamSchema, ValidationParamSchema, } from './middlewares/schema'; | ||
export { checkSchema, Schema, ParamSchema } from './middlewares/schema'; | ||
export * from './matched-data'; | ||
export * from './validation-result'; | ||
export * from './express-validator'; |
@@ -14,4 +14,4 @@ "use strict"; | ||
exports.checkSchema = void 0; | ||
__exportStar(require("./middlewares/exact"), exports); | ||
__exportStar(require("./middlewares/one-of"), exports); | ||
__exportStar(require("./middlewares/sanitization-chain-builders"), exports); | ||
__exportStar(require("./middlewares/validation-chain-builders"), exports); | ||
@@ -22,1 +22,2 @@ var schema_1 = require("./middlewares/schema"); | ||
__exportStar(require("./validation-result"), exports); | ||
__exportStar(require("./express-validator"), exports); |
@@ -37,3 +37,5 @@ "use strict"; | ||
: (field) => { | ||
const hasError = field.context.errors.some(error => error.location === field.instance.location && error.param === field.instance.path); | ||
const hasError = field.context.errors.some(error => error.type === 'field' && | ||
error.location === field.instance.location && | ||
error.path === field.instance.path); | ||
return !hasError; | ||
@@ -40,0 +42,0 @@ }; |
@@ -0,3 +1,3 @@ | ||
import { ErrorMessage, FieldMessageFactory, Location } from '../base'; | ||
import { ValidationChain } from '../chain'; | ||
import { Location } from '../base'; | ||
export declare function check(fields?: string | string[], locations?: Location[], message?: any): ValidationChain; | ||
export declare function check(fields?: string | string[], locations?: Location[], message?: FieldMessageFactory | ErrorMessage): ValidationChain; |
@@ -5,4 +5,4 @@ "use strict"; | ||
const chain_1 = require("../chain"); | ||
const context_builder_1 = require("../context-builder"); | ||
const utils_1 = require("../utils"); | ||
const context_builder_1 = require("../context-builder"); | ||
function check(fields = '', locations = [], message) { | ||
@@ -9,0 +9,0 @@ const builder = new context_builder_1.ContextBuilder() |
@@ -1,21 +0,16 @@ | ||
import { ValidationChain } from '../chain'; | ||
import { Middleware, Request } from '../base'; | ||
import { Result } from '../validation-result'; | ||
export declare type OneOfCustomMessageBuilder = (options: { | ||
req: Request; | ||
}) => any; | ||
/** | ||
* Creates a middleware that will ensure that at least one of the given validation chains | ||
* or validation chain groups are valid. | ||
* | ||
* If none are, a single error for a pseudo-field `_error` is added to the request, | ||
* with the errors of each chain made available under the `nestedErrors` property. | ||
* | ||
* @param chains an array of validation chains to check if are valid. | ||
* If any of the items of `chains` is an array of validation chains, then all of them | ||
* must be valid together for the request to be considered valid. | ||
* @param message a function for creating a custom error message in case none of the chains are valid | ||
*/ | ||
export declare function oneOf(chains: (ValidationChain | ValidationChain[])[], message?: OneOfCustomMessageBuilder): Middleware & { | ||
run: (req: Request) => Promise<Result>; | ||
import { AlternativeMessageFactory, ErrorMessage, GroupedAlternativeMessageFactory, Middleware } from '../base'; | ||
import { ContextRunner, ValidationChain } from '../chain'; | ||
export declare type OneOfErrorType = 'grouped' | 'least_errored' | 'flat'; | ||
export declare type OneOfOptions = { | ||
/** | ||
* The error message to use in case none of the chains are valid. | ||
*/ | ||
message?: AlternativeMessageFactory | ErrorMessage; | ||
errorType?: Exclude<OneOfErrorType, 'grouped'>; | ||
} | { | ||
/** | ||
* The error message to use in case none of the chain groups are valid. | ||
*/ | ||
message?: GroupedAlternativeMessageFactory | ErrorMessage; | ||
errorType?: 'grouped'; | ||
}; | ||
@@ -26,4 +21,4 @@ /** | ||
* | ||
* If none are, a single error for a pseudo-field `_error` is added to the request, | ||
* with the errors of each chain made available under the `nestedErrors` property. | ||
* If none are, a single `AlternativeValidationError` or `GroupedAlternativeValidationError` | ||
* is added to the request, with the errors of each chain made available under the `nestedErrors` property. | ||
* | ||
@@ -33,6 +28,3 @@ * @param chains an array of validation chains to check if are valid. | ||
* must be valid together for the request to be considered valid. | ||
* @param message an error message to use in case none of the chains are valid | ||
*/ | ||
export declare function oneOf(chains: (ValidationChain | ValidationChain[])[], message?: any): Middleware & { | ||
run: (req: Request) => Promise<Result>; | ||
}; | ||
export declare function oneOf(chains: (ValidationChain | ValidationChain[])[], options?: OneOfOptions): Middleware & ContextRunner; |
@@ -7,14 +7,33 @@ "use strict"; | ||
const context_builder_1 = require("../context-builder"); | ||
const utils_1 = require("../utils"); | ||
// A dummy context item that gets added to surrogate contexts just to make them run | ||
const dummyItem = { async run() { } }; | ||
function oneOf(chains, message) { | ||
let result; | ||
const middleware = async (req, _res, next) => { | ||
/** | ||
* Creates a middleware that will ensure that at least one of the given validation chains | ||
* or validation chain groups are valid. | ||
* | ||
* If none are, a single `AlternativeValidationError` or `GroupedAlternativeValidationError` | ||
* is added to the request, with the errors of each chain made available under the `nestedErrors` property. | ||
* | ||
* @param chains an array of validation chains to check if are valid. | ||
* If any of the items of `chains` is an array of validation chains, then all of them | ||
* must be valid together for the request to be considered valid. | ||
*/ | ||
function oneOf(chains, options = {}) { | ||
const run = async (req, opts) => { | ||
const surrogateContext = new context_builder_1.ContextBuilder().addItem(dummyItem).build(); | ||
// Run each group of chains in parallel, and within each group, run each chain in parallel too. | ||
// Run each group of chains in parallel | ||
const promises = chains.map(async (chain) => { | ||
const group = Array.isArray(chain) ? chain : [chain]; | ||
const results = await Promise.all(group.map(chain => chain.run(req, { dryRun: true }))); | ||
const contexts = results.map(result => result.context); | ||
const groupErrors = _.flatMap(contexts, 'errors'); | ||
const results = await utils_1.runAllChains(req, group, { dryRun: true }); | ||
const { contexts, groupErrors } = results.reduce(({ contexts, groupErrors }, result) => { | ||
const { context } = result; | ||
contexts.push(context); | ||
const fieldErrors = context.errors.filter((error) => error.type === 'field'); | ||
groupErrors.push(...fieldErrors); | ||
return { contexts, groupErrors }; | ||
}, { | ||
contexts: [], | ||
groupErrors: [], | ||
}); | ||
// #536: The data from a chain within oneOf() can only be made available to e.g. matchedData() | ||
@@ -29,26 +48,47 @@ // if its entire group is valid. | ||
}); | ||
try { | ||
const allErrors = await Promise.all(promises); | ||
const success = allErrors.some(groupErrors => groupErrors.length === 0); | ||
if (!success) { | ||
// Only add an error to the context if no group of chains had success. | ||
surrogateContext.addError(typeof message === 'function' ? message({ req }) : message || 'Invalid value(s)', _.flatMap(allErrors)); | ||
const allErrors = await Promise.all(promises); | ||
const success = allErrors.some(groupErrors => groupErrors.length === 0); | ||
if (!success) { | ||
const message = options.message || 'Invalid value(s)'; | ||
switch (options.errorType) { | ||
case 'flat': | ||
surrogateContext.addError({ | ||
type: 'alternative', | ||
req, | ||
message, | ||
nestedErrors: _.flatMap(allErrors), | ||
}); | ||
break; | ||
case 'least_errored': | ||
let leastErroredIndex = 0; | ||
for (let i = 1; i < allErrors.length; i++) { | ||
if (allErrors[i].length < allErrors[leastErroredIndex].length) { | ||
leastErroredIndex = i; | ||
} | ||
} | ||
surrogateContext.addError({ | ||
type: 'alternative', | ||
req, | ||
message, | ||
nestedErrors: allErrors[leastErroredIndex], | ||
}); | ||
break; | ||
case 'grouped': | ||
default: | ||
// grouped | ||
surrogateContext.addError({ | ||
type: 'alternative_grouped', | ||
req, | ||
message, | ||
nestedErrors: allErrors, | ||
}); | ||
break; | ||
} | ||
// Final context running pass to ensure contexts are added and values are modified properly | ||
result = await new chain_1.ContextRunnerImpl(surrogateContext).run(req); | ||
next(); | ||
} | ||
catch (e) { | ||
next(e); | ||
} | ||
// Final context running pass to ensure contexts are added and values are modified properly | ||
return await new chain_1.ContextRunnerImpl(surrogateContext).run(req, opts); | ||
}; | ||
const run = async (req) => { | ||
return new Promise((resolve, reject) => { | ||
middleware(req, {}, (e) => { | ||
e ? reject(e) : resolve(result); | ||
}); | ||
}); | ||
}; | ||
const middleware = (req, _res, next) => run(req).then(() => next(), next); | ||
return Object.assign(middleware, { run }); | ||
} | ||
exports.oneOf = oneOf; |
@@ -0,18 +1,12 @@ | ||
import { CustomSanitizer, CustomValidator, ErrorMessage, FieldMessageFactory, Location, Request } from '../base'; | ||
import { BailOptions, OptionalOptions, ValidationChain, ValidationChainLike } from '../chain'; | ||
import { ResultWithContext } from '../chain/context-runner'; | ||
import { Sanitizers } from '../chain/sanitizers'; | ||
import { Validators } from '../chain/validators'; | ||
import { CustomValidator, DynamicMessageCreator, Location, Request } from '../base'; | ||
import { ValidationChain } from '../chain'; | ||
import { Optional } from '../context'; | ||
import { ResultWithContext } from '../chain/context-runner-impl'; | ||
declare type ValidatorSchemaOptions<K extends keyof Validators<any>> = true | { | ||
declare type BaseValidatorSchemaOptions = { | ||
/** | ||
* Options to pass to the validator. | ||
* Not used with custom validators. | ||
*/ | ||
options?: Parameters<Validators<any>[K]> | Parameters<Validators<any>[K]>[0]; | ||
/** | ||
* The error message if there's a validation error, | ||
* or a function for creating an error message dynamically. | ||
*/ | ||
errorMessage?: DynamicMessageCreator | any; | ||
errorMessage?: FieldMessageFactory | ErrorMessage; | ||
/** | ||
@@ -25,3 +19,3 @@ * Whether the validation should be reversed. | ||
*/ | ||
bail?: boolean; | ||
bail?: boolean | BailOptions; | ||
/** | ||
@@ -33,4 +27,17 @@ * Specify a condition upon which this validator should run. | ||
}; | ||
declare type ValidatorSchemaOptions<K extends keyof Validators<any>> = true | (BaseValidatorSchemaOptions & { | ||
/** | ||
* Options to pass to the validator. | ||
*/ | ||
options?: Parameters<Validators<any>[K]> | Parameters<Validators<any>[K]>[0]; | ||
}); | ||
declare type CustomValidatorSchemaOptions = BaseValidatorSchemaOptions & { | ||
/** | ||
* The implementation of a custom validator. | ||
*/ | ||
custom: CustomValidator; | ||
}; | ||
export declare type ExtensionValidatorSchemaOptions = true | BaseValidatorSchemaOptions; | ||
export declare type ValidatorsSchema = { | ||
[K in keyof Validators<any>]?: ValidatorSchemaOptions<K>; | ||
[K in Exclude<keyof Validators<any>, 'not' | 'withMessage'>]?: ValidatorSchemaOptions<K>; | ||
}; | ||
@@ -40,14 +47,16 @@ declare type SanitizerSchemaOptions<K extends keyof Sanitizers<any>> = true | { | ||
* Options to pass to the sanitizer. | ||
* Not used with custom sanitizers. | ||
*/ | ||
options?: Parameters<Sanitizers<any>[K]> | Parameters<Sanitizers<any>[K]>[0]; | ||
}; | ||
declare type CustomSanitizerSchemaOptions = { | ||
/** | ||
* The implementation of a custom sanitizer. | ||
*/ | ||
customSanitizer: CustomSanitizer; | ||
}; | ||
export declare type ExtensionSanitizerSchemaOptions = true; | ||
export declare type SanitizersSchema = { | ||
[K in keyof Sanitizers<any>]?: SanitizerSchemaOptions<K>; | ||
}; | ||
declare type InternalParamSchema = ValidatorsSchema & SanitizersSchema; | ||
/** | ||
* Defines a schema of validations/sanitizations for a field | ||
*/ | ||
export declare type ParamSchema = InternalParamSchema & { | ||
declare type BaseParamSchema = { | ||
/** | ||
@@ -62,3 +71,3 @@ * Which request location(s) the field to validate is. | ||
*/ | ||
errorMessage?: DynamicMessageCreator | any; | ||
errorMessage?: FieldMessageFactory | any; | ||
/** | ||
@@ -68,18 +77,29 @@ * Whether this field should be considered optional | ||
optional?: true | { | ||
options?: Partial<Optional>; | ||
options?: OptionalOptions; | ||
}; | ||
}; | ||
export declare type DefaultSchemaKeys = keyof BaseParamSchema | keyof ValidatorsSchema | keyof SanitizersSchema; | ||
/** | ||
* @deprecated Only here for v5 compatibility. Please use ParamSchema instead. | ||
* Defines a schema of validations/sanitizations for a field | ||
*/ | ||
export declare type ValidationParamSchema = ParamSchema; | ||
export declare type ParamSchema<T extends string = DefaultSchemaKeys> = BaseParamSchema & ValidatorsSchema & SanitizersSchema & { | ||
[K in T]?: K extends keyof BaseParamSchema ? BaseParamSchema[K] : K extends keyof ValidatorsSchema ? ValidatorsSchema[K] : K extends keyof SanitizersSchema ? SanitizersSchema[K] : CustomValidatorSchemaOptions | CustomSanitizerSchemaOptions; | ||
}; | ||
/** | ||
* Defines a mapping from field name to a validations/sanitizations schema. | ||
*/ | ||
export declare type Schema = Record<string, ParamSchema>; | ||
export declare type Schema<T extends string = DefaultSchemaKeys> = Record<string, ParamSchema<T>>; | ||
/** | ||
* @deprecated Only here for v5 compatibility. Please use Schema instead. | ||
* Shortcut type for the return of a {@link checkSchema()}-like function. | ||
*/ | ||
export declare type ValidationSchema = Schema; | ||
export declare type RunnableValidationChains<C extends ValidationChainLike> = C[] & { | ||
run(req: Request): Promise<ResultWithContext[]>; | ||
}; | ||
/** | ||
* Factory for a {@link checkSchema()} function which can have extension validators and sanitizers. | ||
* | ||
* @see {@link checkSchema()} | ||
*/ | ||
export declare function createCheckSchema<C extends ValidationChainLike>(createChain: (fields?: string | string[], locations?: Location[], errorMessage?: any) => C, extraValidators?: (keyof C)[], extraSanitizers?: (keyof C)[]): <T extends string = DefaultSchemaKeys>(schema: Schema<T>, defaultLocations?: Location[]) => RunnableValidationChains<C>; | ||
/** | ||
* Creates an express middleware with validations for multiple fields at once in the form of | ||
@@ -92,5 +112,3 @@ * a schema object. | ||
*/ | ||
export declare function checkSchema(schema: Schema, defaultLocations?: Location[]): ValidationChain[] & { | ||
run: (req: Request) => Promise<ResultWithContext[]>; | ||
}; | ||
export declare const checkSchema: <T extends string = DefaultSchemaKeys>(schema: Schema<T>, defaultLocations?: Location[] | undefined) => RunnableValidationChains<ValidationChain>; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.checkSchema = void 0; | ||
exports.checkSchema = exports.createCheckSchema = void 0; | ||
const _ = require("lodash"); | ||
const chain_1 = require("../chain"); | ||
const utils_1 = require("../utils"); | ||
const check_1 = require("./check"); | ||
const validLocations = ['body', 'cookies', 'headers', 'params', 'query']; | ||
const protectedNames = ['errorMessage', 'in']; | ||
const protectedNames = ['errorMessage', 'in', 'optional']; | ||
/** | ||
* Factory for a {@link checkSchema()} function which can have extension validators and sanitizers. | ||
* | ||
* @see {@link checkSchema()} | ||
*/ | ||
function createCheckSchema(createChain, extraValidators = [], extraSanitizers = []) { | ||
/** Type guard for an object entry for a standard validator. */ | ||
function isStandardValidator(entry) { | ||
return ( | ||
// #664 - explicitly exclude properties which should be set per validator | ||
!['not', 'withMessage'].includes(entry[0]) && | ||
(entry[0] in chain_1.ValidatorsImpl.prototype || extraValidators.includes(entry[0])) && | ||
entry[1]); | ||
} | ||
/** Type guard for an object entry for a standard sanitizer. */ | ||
function isStandardSanitizer(entry) { | ||
return ((entry[0] in chain_1.SanitizersImpl.prototype || extraSanitizers.includes(entry[0])) && | ||
entry[1]); | ||
} | ||
/** Type guard for an object entry for a custom validator. */ | ||
function isCustomValidator(entry) { | ||
return (!isStandardValidator(entry) && | ||
!isStandardSanitizer(entry) && | ||
typeof entry[1] === 'object' && | ||
entry[1] && | ||
typeof entry[1].custom === 'function'); | ||
} | ||
/** Type guard for an object entry for a custom sanitizer. */ | ||
function isCustomSanitizer(entry) { | ||
return (!isStandardValidator(entry) && | ||
!isStandardSanitizer(entry) && | ||
typeof entry[1] === 'object' && | ||
entry[1] && | ||
typeof entry[1].customSanitizer === 'function'); | ||
} | ||
return (schema, defaultLocations = validLocations) => { | ||
const chains = Object.keys(schema).map(field => { | ||
const config = schema[field]; | ||
const chain = createChain(field, ensureLocations(config, defaultLocations), config.errorMessage); | ||
// optional doesn't matter where it happens in the chain | ||
if (config.optional) { | ||
chain.optional(config.optional === true ? true : config.optional.options); | ||
} | ||
for (const entry of Object.entries(config)) { | ||
if (protectedNames.includes(entry[0])) { | ||
continue; | ||
} | ||
if (!isStandardValidator(entry) && | ||
!isStandardSanitizer(entry) && | ||
!isCustomValidator(entry) && | ||
!isCustomSanitizer(entry)) { | ||
console.warn(`express-validator: schema of "${field}" has unknown validator/sanitizer "${entry[0]}"`); | ||
continue; | ||
} | ||
// For validators, stuff that must come _before_ the validator itself in the chain. | ||
if ((isStandardValidator(entry) || isCustomValidator(entry)) && entry[1] !== true) { | ||
const [, validatorConfig] = entry; | ||
validatorConfig.if && chain.if(validatorConfig.if); | ||
validatorConfig.negated && chain.not(); | ||
} | ||
if (isStandardValidator(entry) || isStandardSanitizer(entry)) { | ||
const options = entry[1] ? (entry[1] === true ? [] : _.castArray(entry[1].options)) : []; | ||
chain[entry[0]](...options); | ||
} | ||
if (isCustomValidator(entry)) { | ||
chain.custom(entry[1].custom); | ||
} | ||
if (isCustomSanitizer(entry)) { | ||
chain.customSanitizer(entry[1].customSanitizer); | ||
} | ||
// For validators, stuff that must come _after_ the validator itself in the chain. | ||
if ((isStandardValidator(entry) || isCustomValidator(entry)) && entry[1] !== true) { | ||
const [, validatorConfig] = entry; | ||
validatorConfig.bail && | ||
chain.bail(validatorConfig.bail === true ? {} : validatorConfig.bail); | ||
validatorConfig.errorMessage && chain.withMessage(validatorConfig.errorMessage); | ||
} | ||
} | ||
return chain; | ||
}); | ||
const run = async (req) => utils_1.runAllChains(req, chains); | ||
return Object.assign(chains, { run }); | ||
}; | ||
} | ||
exports.createCheckSchema = createCheckSchema; | ||
/** | ||
* Creates an express middleware with validations for multiple fields at once in the form of | ||
@@ -16,47 +103,3 @@ * a schema object. | ||
*/ | ||
function checkSchema(schema, defaultLocations = validLocations) { | ||
const chains = Object.keys(schema).map(field => { | ||
const config = schema[field]; | ||
const chain = check_1.check(field, ensureLocations(config, defaultLocations), config.errorMessage); | ||
Object.keys(config) | ||
.filter((method) => { | ||
return config[method] && !protectedNames.includes(method); | ||
}) | ||
.forEach(method => { | ||
var _a; | ||
if (typeof chain[method] !== 'function') { | ||
console.warn(`express-validator: a validator/sanitizer with name ${method} does not exist`); | ||
return; | ||
} | ||
// Using "!" because typescript doesn't know it isn't undefined. | ||
const methodCfg = config[method]; | ||
let options = methodCfg === true ? [] : (_a = methodCfg.options) !== null && _a !== void 0 ? _a : []; | ||
if (options != null && !Array.isArray(options)) { | ||
options = [options]; | ||
} | ||
if (isValidatorOptions(method, methodCfg) && methodCfg.if) { | ||
chain.if(methodCfg.if); | ||
} | ||
if (isValidatorOptions(method, methodCfg) && methodCfg.negated) { | ||
chain.not(); | ||
} | ||
chain[method](...options); | ||
if (isValidatorOptions(method, methodCfg) && methodCfg.errorMessage) { | ||
chain.withMessage(methodCfg.errorMessage); | ||
} | ||
if (isValidatorOptions(method, methodCfg) && methodCfg.bail) { | ||
chain.bail(); | ||
} | ||
}); | ||
return chain; | ||
}); | ||
const run = async (req) => { | ||
return await Promise.all(chains.map(chain => chain.run(req))); | ||
}; | ||
return Object.assign(chains, { run }); | ||
} | ||
exports.checkSchema = checkSchema; | ||
function isValidatorOptions(method, methodCfg) { | ||
return methodCfg !== true && method in chain_1.ValidatorsImpl.prototype; | ||
} | ||
exports.checkSchema = createCheckSchema(check_1.check); | ||
function ensureLocations(config, defaults) { | ||
@@ -63,0 +106,0 @@ // .filter(Boolean) is done because in can be undefined -- which is not going away from the type |
@@ -1,2 +0,2 @@ | ||
import { Location } from '../base'; | ||
import { ErrorMessage, FieldMessageFactory, Location } from '../base'; | ||
/** | ||
@@ -8,3 +8,3 @@ * Creates a variant of `check()` that checks the given request locations. | ||
*/ | ||
export declare function buildCheckFunction(locations: Location[]): (fields?: string | string[] | undefined, message?: any) => import("..").ValidationChain; | ||
export declare function buildCheckFunction(locations: Location[]): (fields?: string | string[] | undefined, message?: FieldMessageFactory | ErrorMessage | undefined) => import("..").ValidationChain; | ||
/** | ||
@@ -24,22 +24,22 @@ * Creates a middleware/validation chain for one or more fields that may be located in | ||
*/ | ||
export declare const check: (fields?: string | string[] | undefined, message?: any) => import("..").ValidationChain; | ||
export declare const check: (fields?: string | string[] | undefined, message?: FieldMessageFactory | ErrorMessage | undefined) => import("..").ValidationChain; | ||
/** | ||
* Same as {@link check()}, but only validates `req.body`. | ||
*/ | ||
export declare const body: (fields?: string | string[] | undefined, message?: any) => import("..").ValidationChain; | ||
export declare const body: (fields?: string | string[] | undefined, message?: FieldMessageFactory | ErrorMessage | undefined) => import("..").ValidationChain; | ||
/** | ||
* Same as {@link check()}, but only validates `req.cookies`. | ||
*/ | ||
export declare const cookie: (fields?: string | string[] | undefined, message?: any) => import("..").ValidationChain; | ||
export declare const cookie: (fields?: string | string[] | undefined, message?: FieldMessageFactory | ErrorMessage | undefined) => import("..").ValidationChain; | ||
/** | ||
* Same as {@link check()}, but only validates `req.headers`. | ||
*/ | ||
export declare const header: (fields?: string | string[] | undefined, message?: any) => import("..").ValidationChain; | ||
export declare const header: (fields?: string | string[] | undefined, message?: FieldMessageFactory | ErrorMessage | undefined) => import("..").ValidationChain; | ||
/** | ||
* Same as {@link check()}, but only validates `req.params`. | ||
*/ | ||
export declare const param: (fields?: string | string[] | undefined, message?: any) => import("..").ValidationChain; | ||
export declare const param: (fields?: string | string[] | undefined, message?: FieldMessageFactory | ErrorMessage | undefined) => import("..").ValidationChain; | ||
/** | ||
* Same as {@link check()}, but only validates `req.query`. | ||
*/ | ||
export declare const query: (fields?: string | string[] | undefined, message?: any) => import("..").ValidationChain; | ||
export declare const query: (fields?: string | string[] | undefined, message?: FieldMessageFactory | ErrorMessage | undefined) => import("..").ValidationChain; |
@@ -6,3 +6,3 @@ export declare type URLProtocol = 'http' | 'https' | 'ftp' | string; | ||
export declare type AlphanumericLocale = 'ar' | 'ar-AE' | 'ar-BH' | 'ar-DZ' | 'ar-EG' | 'ar-IQ' | 'ar-JO' | 'ar-KW' | 'ar-LB' | 'ar-LY' | 'ar-MA' | 'ar-QA' | 'ar-QM' | 'ar-SA' | 'ar-SD' | 'ar-SY' | 'ar-TN' | 'ar-YE' | 'az-AZ' | 'bg-BG' | 'bn-BD' | 'cs-CZ' | 'da-DK' | 'de-DE' | 'el-GR' | 'en-AU' | 'en-GB' | 'en-HK' | 'en-IN' | 'en-NZ' | 'en-US' | 'en-ZA' | 'en-ZM' | 'es-ES' | 'fa-AF' | 'fa-IR' | 'fi-FI' | 'fr-FR' | 'fr-BE' | 'he' | 'hi-IN' | 'hu-HU' | 'it-IT' | 'id-ID' | 'ja-JP' | 'ko-KR' | 'ku-IQ' | 'nb-NO' | 'nl-BE' | 'nl-NL' | 'nn-NO' | 'pl-PL' | 'pt-BR' | 'pt-PT' | 'ru-RU' | 'si-LK' | 'sk-SK' | 'sl-SI' | 'sr-RS' | 'sr-RS@latin' | 'sv-SE' | 'th-TH' | 'tr-TR' | 'uk-UA' | 'vi-VN'; | ||
export declare type MobilePhoneLocale = 'any' | 'am-AM' | 'ar-AE' | 'ar-BH' | 'ar-DZ' | 'ar-EG' | 'ar-EH' | 'ar-IQ' | 'ar-JO' | 'ar-KW' | 'ar-LB' | 'ar-LY' | 'ar-MA' | 'ar-OM' | 'ar-PS' | 'ar-SA' | 'ar-SY' | 'ar-TN' | 'ar-YE' | 'az-AZ' | 'be-BY' | 'bg-BG' | 'bn-BD' | 'bs-BA' | 'cs-CZ' | 'de-AT' | 'de-CH' | 'de-DE' | 'de-LU' | 'da-DK' | 'dv-MV' | 'dz-BT' | 'el-CY' | 'el-GR' | 'en-AG' | 'en-AI' | 'en-AU' | 'en-BM' | 'en-BS' | 'en-BW' | 'en-CA' | 'en-GB' | 'en-GG' | 'en-GH' | 'en-GY' | 'en-HK' | 'en-HN' | 'en-IE' | 'en-IN' | 'en-JM' | 'en-KE' | 'en-KI' | 'en-KN' | 'en-LS' | 'en-MT' | 'en-MU' | 'en-NA' | 'en-NG' | 'en-NZ' | 'en-PG' | 'en-PH' | 'en-PK' | 'en-RW' | 'en-SG' | 'en-SL' | 'en-SS' | 'en-TZ' | 'en-UG' | 'en-US' | 'en-ZA' | 'en-ZM' | 'en-ZW' | 'es-AR' | 'es-BO' | 'es-CL' | 'es-CO' | 'es-CR' | 'es-CU' | 'es-DO' | 'es-EC' | 'es-ES' | 'es-HN' | 'es-MX' | 'es-NI' | 'es-PA' | 'es-PE' | 'es-PY' | 'es-SV' | 'es-UY' | 'es-VE' | 'et-EE' | 'fa-AF' | 'fa-IR' | 'fi-FI' | 'fj-FJ' | 'fo-FO' | 'fr-BE' | 'fr-BF' | 'fr-BJ' | 'fr-CD' | 'fr-CH' | 'fr-CM' | 'fr-FR' | 'fr-GF' | 'fr-GP' | 'fr-MQ' | 'fr-PF' | 'fr-RE' | 'ga-IE' | 'he-IL' | 'hu-HU' | 'id-ID' | 'ir-IR' | 'it-CH' | 'it-IT' | 'it-SM' | 'ja-JP' | 'ka-GE' | 'kk-KZ' | 'kl-GL' | 'ky-KG' | 'lt-LT' | 'lv-LV' | 'mg-MG' | 'mn-MN' | 'ms-MY' | 'my-MM' | 'mz-MZ' | 'nb-NO' | 'nl-AW' | 'nl-BE' | 'nl-NL' | 'ne-NP' | 'nn-NO' | 'pl-PL' | 'pt-AO' | 'pt-BR' | 'pt-PT' | 'ro-MD' | 'ro-RO' | 'ru-RU' | 'si-LK' | 'sk-SK' | 'sl-SI' | 'sq-AL' | 'sr-RS' | 'sv-SE' | 'tg-TJ' | 'th-TH' | 'tk-TM' | 'tr-TR' | 'uk-UA' | 'uz-Uz' | 'vi-VN' | 'zh-CN' | 'zh-HK' | 'zh-TW'; | ||
export declare type MobilePhoneLocale = 'any' | 'am-AM' | 'ar-AE' | 'ar-BH' | 'ar-DZ' | 'ar-EG' | 'ar-EH' | 'ar-IQ' | 'ar-JO' | 'ar-KW' | 'ar-LB' | 'ar-LY' | 'ar-MA' | 'ar-OM' | 'ar-PS' | 'ar-SA' | 'ar-SY' | 'ar-TN' | 'ar-YE' | 'az-AZ' | 'be-BY' | 'bg-BG' | 'bn-BD' | 'bs-BA' | 'cs-CZ' | 'de-AT' | 'de-CH' | 'de-DE' | 'de-LU' | 'da-DK' | 'dv-MV' | 'dz-BT' | 'el-CY' | 'el-GR' | 'en-AG' | 'en-AI' | 'en-AU' | 'en-BM' | 'en-BS' | 'en-BW' | 'en-CA' | 'en-GB' | 'en-GG' | 'en-GH' | 'en-GY' | 'en-HK' | 'en-HN' | 'en-IE' | 'en-IN' | 'en-JM' | 'en-KE' | 'en-KI' | 'en-KN' | 'en-LS' | 'en-MT' | 'en-MU' | 'en-NA' | 'en-NG' | 'en-NZ' | 'en-PG' | 'en-PH' | 'en-PK' | 'en-RW' | 'en-SG' | 'en-SL' | 'en-SS' | 'en-TZ' | 'en-UG' | 'en-US' | 'en-ZA' | 'en-ZM' | 'en-ZW' | 'es-AR' | 'es-BO' | 'es-CL' | 'es-CO' | 'es-CR' | 'es-CU' | 'es-DO' | 'es-EC' | 'es-ES' | 'es-HN' | 'es-MX' | 'es-NI' | 'es-PA' | 'es-PE' | 'es-PY' | 'es-SV' | 'es-UY' | 'es-VE' | 'et-EE' | 'fa-AF' | 'fa-IR' | 'fi-FI' | 'fj-FJ' | 'fo-FO' | 'fr-BE' | 'fr-BF' | 'fr-BJ' | 'fr-CD' | 'fr-CH' | 'fr-CM' | 'fr-FR' | 'fr-GF' | 'fr-GP' | 'fr-MQ' | 'fr-PF' | 'fr-RE' | 'ga-IE' | 'he-IL' | 'hu-HU' | 'id-ID' | 'ir-IR' | 'it-CH' | 'it-IT' | 'it-SM' | 'ja-JP' | 'ka-GE' | 'kk-KZ' | 'kl-GL' | 'ko-KR' | 'ky-KG' | 'lt-LT' | 'lv-LV' | 'mg-MG' | 'mn-MN' | 'ms-MY' | 'my-MM' | 'mz-MZ' | 'nb-NO' | 'nl-AW' | 'nl-BE' | 'nl-NL' | 'ne-NP' | 'nn-NO' | 'pl-PL' | 'pt-AO' | 'pt-BR' | 'pt-PT' | 'ro-MD' | 'ro-RO' | 'ru-RU' | 'si-LK' | 'sk-SK' | 'sl-SI' | 'sq-AL' | 'sr-RS' | 'sv-SE' | 'tg-TJ' | 'th-TH' | 'tk-TM' | 'tr-TR' | 'uk-UA' | 'uz-Uz' | 'vi-VN' | 'zh-CN' | 'zh-HK' | 'zh-TW'; | ||
export declare type PostalCodeLocale = 'any' | 'AD' | 'AT' | 'AU' | 'AZ' | 'BA' | 'BE' | 'BG' | 'BR' | 'BY' | 'CA' | 'CH' | 'CN' | 'CZ' | 'DE' | 'DK' | 'DO' | 'DZ' | 'EE' | 'ES' | 'FI' | 'FR' | 'GB' | 'GR' | 'HR' | 'HT' | 'HU' | 'ID' | 'IL' | 'IN' | 'IR' | 'IS' | 'IT' | 'JP' | 'KE' | 'KR' | 'LI' | 'LK' | 'LT' | 'LU' | 'LV' | 'MT' | 'MX' | 'MY' | 'NL' | 'NO' | 'NP' | 'NZ' | 'PL' | 'PR' | 'PT' | 'RO' | 'RU' | 'SA' | 'SE' | 'SG' | 'SI' | 'SK' | 'TH' | 'TN' | 'TW' | 'UA' | 'US' | 'ZA' | 'ZM'; | ||
@@ -9,0 +9,0 @@ export declare type HashAlgorithm = 'md4' | 'md5' | 'sha1' | 'sha256' | 'sha384' | 'sha512' | 'ripemd128' | 'ripemd160' | 'tiger128' | 'tiger160' | 'tiger192' | 'crc32' | 'crc32b'; |
@@ -0,2 +1,12 @@ | ||
import { Request } from './base'; | ||
import { ContextRunningOptions, ResultWithContext, ValidationChainLike } from './chain'; | ||
export declare const bindAll: <T>(object: T) => { [K in keyof T]: T[K]; }; | ||
export declare function toString(value: any, deep?: boolean): string; | ||
export declare function toString(value: any): string; | ||
/** | ||
* Runs all validation chains, and returns their results. | ||
* | ||
* If one of them has a request-level bail set, the previous chains will be awaited on so that | ||
* results are not skewed, which can be slow. | ||
* If this same chain also contains errors, no further chains are run. | ||
*/ | ||
export declare function runAllChains(req: Request, chains: readonly ValidationChainLike[], runOpts?: ContextRunningOptions): Promise<ResultWithContext[]>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.toString = exports.bindAll = void 0; | ||
exports.runAllChains = exports.toString = exports.bindAll = void 0; | ||
const bindAll = (object) => { | ||
@@ -15,7 +15,4 @@ const protoKeys = Object.getOwnPropertyNames(Object.getPrototypeOf(object)); | ||
exports.bindAll = bindAll; | ||
function toString(value, deep = true) { | ||
if (Array.isArray(value) && value.length && deep) { | ||
return toString(value[0], false); | ||
} | ||
else if (value instanceof Date) { | ||
function toString(value) { | ||
if (value instanceof Date) { | ||
return value.toISOString(); | ||
@@ -35,1 +32,27 @@ } | ||
exports.toString = toString; | ||
/** | ||
* Runs all validation chains, and returns their results. | ||
* | ||
* If one of them has a request-level bail set, the previous chains will be awaited on so that | ||
* results are not skewed, which can be slow. | ||
* If this same chain also contains errors, no further chains are run. | ||
*/ | ||
async function runAllChains(req, chains, runOpts) { | ||
const promises = []; | ||
for (const chain of chains) { | ||
const bails = chain.builder.build().bail; | ||
if (bails) { | ||
await Promise.all(promises); | ||
} | ||
const resultPromise = chain.run(req, runOpts); | ||
promises.push(resultPromise); | ||
if (bails) { | ||
const result = await resultPromise; | ||
if (!result.isEmpty()) { | ||
break; | ||
} | ||
} | ||
} | ||
return Promise.all(promises); | ||
} | ||
exports.runAllChains = runAllChains; |
@@ -37,4 +37,5 @@ "use strict"; | ||
return this.errors.reduce((mapping, error) => { | ||
if (!mapping[error.param]) { | ||
mapping[error.param] = this.formatter(error); | ||
const key = error.type === 'field' ? error.path : `_${error.type}`; | ||
if (!mapping[key]) { | ||
mapping[key] = this.formatter(error); | ||
} | ||
@@ -41,0 +42,0 @@ return mapping; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
151501
3636
71