Change endpoint from persons to people

This commit is contained in:
xfarrow
2025-03-23 21:00:08 +01:00
parent 4ae263662c
commit d005193f63
7158 changed files with 700476 additions and 735 deletions

View File

@ -0,0 +1,3 @@
import { ErrorMessage, FieldMessageFactory, Location } from '../base';
import { ValidationChain } from '../chain';
export declare function check(fields?: string | string[], locations?: Location[], message?: FieldMessageFactory | ErrorMessage): ValidationChain;

View File

@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.check = check;
const chain_1 = require("../chain");
const context_builder_1 = require("../context-builder");
const utils_1 = require("../utils");
function check(fields = '', locations = [], message) {
const builder = new context_builder_1.ContextBuilder()
.setFields(Array.isArray(fields) ? fields : [fields])
.setLocations(locations)
.setMessage(message);
const runner = new chain_1.ContextRunnerImpl(builder);
const middleware = async (req, _res, next) => {
try {
await runner.run(req);
next();
}
catch (e) {
next(e);
}
};
return Object.assign(middleware, (0, utils_1.bindAll)(runner), (0, utils_1.bindAll)(new chain_1.SanitizersImpl(builder, middleware)), (0, utils_1.bindAll)(new chain_1.ValidatorsImpl(builder, middleware)), (0, utils_1.bindAll)(new chain_1.ContextHandlerImpl(builder, middleware)), { builder });
}

View File

@ -0,0 +1,29 @@
import { ErrorMessage, Location, Middleware, UnknownFieldMessageFactory } from '../base';
import { ContextRunner, ValidationChain } from '../chain';
type CheckExactOptions = {
/**
* The list of locations which `checkExact()` should check.
* @default ['body', 'params', 'query']
*/
locations?: readonly Location[];
message?: UnknownFieldMessageFactory | ErrorMessage;
};
type CheckExactInput = ValidationChain | ValidationChain[] | (ValidationChain | ValidationChain[])[];
/**
* Checks whether the request contains exactly only those fields that have been validated.
*
* Unknown fields, if found, will generate an error of type `unknown_fields`.
*
* @param chains either a single chain, an array of chains, or a mixed array of chains and array of chains.
* This means that all of the below are valid:
* ```
* checkExact(check('foo'))
* checkExact([check('foo'), check('bar')])
* checkExact([check('foo'), check('bar')])
* checkExact(checkSchema({ ... }))
* checkExact([checkSchema({ ... }), check('foo')])
* ```
* @param opts
*/
export declare function checkExact(chains?: CheckExactInput, opts?: CheckExactOptions): Middleware & ContextRunner;
export {};

View File

@ -0,0 +1,68 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkExact = checkExact;
const base_1 = require("../base");
const chain_1 = require("../chain");
const context_1 = require("../context");
const field_selection_1 = require("../field-selection");
const utils_1 = require("../utils");
/**
* Checks whether the request contains exactly only those fields that have been validated.
*
* Unknown fields, if found, will generate an error of type `unknown_fields`.
*
* @param chains either a single chain, an array of chains, or a mixed array of chains and array of chains.
* This means that all of the below are valid:
* ```
* checkExact(check('foo'))
* checkExact([check('foo'), check('bar')])
* checkExact([check('foo'), check('bar')])
* checkExact(checkSchema({ ... }))
* checkExact([checkSchema({ ... }), check('foo')])
* ```
* @param opts
*/
function checkExact(chains, opts) {
// Don't include all locations by default. Browsers will add cookies and headers that the user
// might not want to validate, which would be a footgun.
const locations = opts?.locations || ['body', 'params', 'query'];
const chainsArr = Array.isArray(chains) ? chains.flat() : chains ? [chains] : [];
const run = async (req) => {
const internalReq = req;
const fieldsByLocation = new Map();
await (0, utils_1.runAllChains)(req, chainsArr);
// The chains above will have added contexts to the request
(internalReq[base_1.contextsKey] || []).forEach(context => {
context.locations.forEach(location => {
if (!locations.includes(location)) {
return;
}
const locationFields = fieldsByLocation.get(location) || [];
locationFields.push(...context.fields);
fieldsByLocation.set(location, locationFields);
});
});
// when none of the chains matched anything, then everything is unknown.
if (!fieldsByLocation.size) {
locations.forEach(location => fieldsByLocation.set(location, []));
}
let unknownFields = [];
for (const [location, fields] of fieldsByLocation.entries()) {
unknownFields = unknownFields.concat((0, field_selection_1.selectUnknownFields)(req, fields, [location]));
}
const context = new context_1.Context([], [], [], false, false);
if (unknownFields.length) {
context.addError({
type: 'unknown_fields',
req,
message: opts?.message || 'Unknown field(s)',
fields: unknownFields,
});
}
internalReq[base_1.contextsKey] = internalReq[base_1.contextsKey] || [];
internalReq[base_1.contextsKey].push(context);
return new chain_1.ResultWithContextImpl(context);
};
const middleware = (req, _res, next) => run(req).then(() => next(), next);
return Object.assign(middleware, { run });
}

View File

@ -0,0 +1,28 @@
import { AlternativeMessageFactory, ErrorMessage, GroupedAlternativeMessageFactory, Middleware } from '../base';
import { ContextRunner, ValidationChain } from '../chain';
export type OneOfErrorType = 'grouped' | 'least_errored' | 'flat';
export 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';
};
/**
* 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.
*/
export declare function oneOf(chains: (ValidationChain | ValidationChain[])[], options?: OneOfOptions): Middleware & ContextRunner;

View File

@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.oneOf = oneOf;
const _ = require("lodash");
const chain_1 = require("../chain");
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() { } };
/**
* 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
const promises = chains.map(async (chain) => {
const group = Array.isArray(chain) ? chain : [chain];
const results = await (0, 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()
// if its entire group is valid.
if (!groupErrors.length) {
contexts.forEach(context => {
surrogateContext.addFieldInstances(context.getData());
});
}
return groupErrors;
});
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
return await new chain_1.ContextRunnerImpl(surrogateContext).run(req, opts);
};
const middleware = (req, _res, next) => run(req).then(() => next(), next);
return Object.assign(middleware, { run });
}

View File

@ -0,0 +1,108 @@
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';
type BaseValidatorSchemaOptions = {
/**
* The error message if there's a validation error,
* or a function for creating an error message dynamically.
*/
errorMessage?: FieldMessageFactory | ErrorMessage;
/**
* Whether the validation should be reversed.
*/
negated?: boolean;
/**
* Whether the validation should bail after running this validator
*/
bail?: boolean | BailOptions;
/**
* Specify a condition upon which this validator should run.
* Can either be a validation chain, or a custom validator function.
*/
if?: CustomValidator | ValidationChain;
};
type ValidatorSchemaOptions<K extends keyof Validators<any>> = boolean | (BaseValidatorSchemaOptions & {
/**
* Options to pass to the validator.
*/
options?: Parameters<Validators<any>[K]> | Parameters<Validators<any>[K]>[0];
});
type CustomValidatorSchemaOptions = BaseValidatorSchemaOptions & {
/**
* The implementation of a custom validator.
*/
custom: CustomValidator;
};
export type ExtensionValidatorSchemaOptions = boolean | BaseValidatorSchemaOptions;
export type ValidatorsSchema = {
[K in Exclude<keyof Validators<any>, 'not' | 'withMessage'>]?: ValidatorSchemaOptions<K>;
};
type SanitizerSchemaOptions<K extends keyof Sanitizers<any>> = boolean | {
/**
* Options to pass to the sanitizer.
*/
options?: Parameters<Sanitizers<any>[K]> | Parameters<Sanitizers<any>[K]>[0];
};
type CustomSanitizerSchemaOptions = {
/**
* The implementation of a custom sanitizer.
*/
customSanitizer: CustomSanitizer;
};
export type ExtensionSanitizerSchemaOptions = true;
export type SanitizersSchema = {
[K in keyof Sanitizers<any>]?: SanitizerSchemaOptions<K>;
};
type BaseParamSchema = {
/**
* Which request location(s) the field to validate is.
* If unset, the field will be checked in every request location.
*/
in?: Location | Location[];
/**
* The general error message in case a validator doesn't specify one,
* or a function for creating the error message dynamically.
*/
errorMessage?: FieldMessageFactory | any;
/**
* Whether this field should be considered optional
*/
optional?: boolean | {
options?: OptionalOptions;
};
};
export type DefaultSchemaKeys = keyof BaseParamSchema | keyof ValidatorsSchema | keyof SanitizersSchema;
/**
* Defines a schema of validations/sanitizations for a field
*/
export 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 type Schema<T extends string = DefaultSchemaKeys> = Record<string, ParamSchema<T>>;
/**
* Shortcut type for the return of a {@link checkSchema()}-like function.
*/
export 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
* a schema object.
*
* @param schema the schema to validate.
* @param defaultLocations
* @returns
*/
export declare const checkSchema: <T extends string = DefaultSchemaKeys>(schema: Schema<T>, defaultLocations?: Location[]) => RunnableValidationChains<ValidationChain>;
export {};

View File

@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkSchema = void 0;
exports.createCheckSchema = createCheckSchema;
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', '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]) || !entry[1]) {
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) => (0, utils_1.runAllChains)(req, chains);
return Object.assign(chains, { run });
};
}
/**
* Creates an express middleware with validations for multiple fields at once in the form of
* a schema object.
*
* @param schema the schema to validate.
* @param defaultLocations
* @returns
*/
exports.checkSchema = createCheckSchema(check_1.check);
function ensureLocations(config, defaults) {
// .filter(Boolean) is done because in can be undefined -- which is not going away from the type
// See https://github.com/Microsoft/TypeScript/pull/29955 for details
const locations = Array.isArray(config.in)
? config.in
: [config.in].filter(Boolean);
const actualLocations = locations.length ? locations : defaults;
return actualLocations.filter(location => validLocations.includes(location));
}

View File

@ -0,0 +1,43 @@
import { ErrorMessage, FieldMessageFactory, Location } from '../base';
/**
* Creates a variant of `check()` that checks the given request locations.
*
* @example
* const checkBodyAndQuery = buildCheckFunction(['body', 'query']);
*/
export declare function buildCheckFunction(locations: Location[]): (fields?: string | string[], message?: FieldMessageFactory | ErrorMessage) => import("..").ValidationChain;
/**
* Creates a middleware/validation chain for one or more fields that may be located in
* any of the following:
*
* - `req.body`
* - `req.cookies`
* - `req.headers`
* - `req.params`
* - `req.query`
*
* @param fields a string or array of field names to validate/sanitize
* @param message an error message to use when failed validations don't specify a custom message.
* Defaults to `Invalid Value`.
*/
export declare const check: (fields?: string | string[], message?: FieldMessageFactory | ErrorMessage) => import("..").ValidationChain;
/**
* Same as {@link check()}, but only validates `req.body`.
*/
export declare const body: (fields?: string | string[], message?: FieldMessageFactory | ErrorMessage) => import("..").ValidationChain;
/**
* Same as {@link check()}, but only validates `req.cookies`.
*/
export declare const cookie: (fields?: string | string[], message?: FieldMessageFactory | ErrorMessage) => import("..").ValidationChain;
/**
* Same as {@link check()}, but only validates `req.headers`.
*/
export declare const header: (fields?: string | string[], message?: FieldMessageFactory | ErrorMessage) => import("..").ValidationChain;
/**
* Same as {@link check()}, but only validates `req.params`.
*/
export declare const param: (fields?: string | string[], message?: FieldMessageFactory | ErrorMessage) => import("..").ValidationChain;
/**
* Same as {@link check()}, but only validates `req.query`.
*/
export declare const query: (fields?: string | string[], message?: FieldMessageFactory | ErrorMessage) => import("..").ValidationChain;

View File

@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.query = exports.param = exports.header = exports.cookie = exports.body = exports.check = void 0;
exports.buildCheckFunction = buildCheckFunction;
const check_1 = require("./check");
/**
* Creates a variant of `check()` that checks the given request locations.
*
* @example
* const checkBodyAndQuery = buildCheckFunction(['body', 'query']);
*/
function buildCheckFunction(locations) {
return (fields, message) => (0, check_1.check)(fields, locations, message);
}
/**
* Creates a middleware/validation chain for one or more fields that may be located in
* any of the following:
*
* - `req.body`
* - `req.cookies`
* - `req.headers`
* - `req.params`
* - `req.query`
*
* @param fields a string or array of field names to validate/sanitize
* @param message an error message to use when failed validations don't specify a custom message.
* Defaults to `Invalid Value`.
*/
exports.check = buildCheckFunction(['body', 'cookies', 'headers', 'params', 'query']);
/**
* Same as {@link check()}, but only validates `req.body`.
*/
exports.body = buildCheckFunction(['body']);
/**
* Same as {@link check()}, but only validates `req.cookies`.
*/
exports.cookie = buildCheckFunction(['cookies']);
/**
* Same as {@link check()}, but only validates `req.headers`.
*/
exports.header = buildCheckFunction(['headers']);
/**
* Same as {@link check()}, but only validates `req.params`.
*/
exports.param = buildCheckFunction(['params']);
/**
* Same as {@link check()}, but only validates `req.query`.
*/
exports.query = buildCheckFunction(['query']);