Comparing version 12.0.0 to 12.0.1
@@ -63,3 +63,3 @@ import { ErrorRequestHandler, RequestHandler } from 'express'; | ||
*/ | ||
export declare function celebrate(schema: SchemaOptions, joiOptions?: ValidationOptions, opts?: CelebrateOptions): RequestHandler; | ||
export declare function celebrate(requestRules: SchemaOptions, joiOpts?: ValidationOptions, opts?: CelebrateOptions): RequestHandler; | ||
@@ -66,0 +66,0 @@ /** |
152
lib/index.js
@@ -5,6 +5,6 @@ const Assert = require('assert'); | ||
const { | ||
middlewareSchema, | ||
celebrateSchema, | ||
sourceSchema, | ||
optSchema, | ||
CELEBRATEERROROPTSSCHEMA, | ||
CELEBRATEOPTSSCHEMA, | ||
REQUESTSCHEMA, | ||
SEGMENTSCHEMA, | ||
} = require('./schema'); | ||
@@ -29,75 +29,90 @@ const { segments } = require('./constants'); | ||
internals.validateSource = (segment, spec) => ({ | ||
internals.validateSegment = (segment) => (spec, { | ||
config, | ||
req, | ||
}) => new Promise((resolve, reject) => { | ||
spec.validateAsync(req[segment], config).then(({ value }) => { | ||
resolve({ | ||
value, | ||
segment, | ||
}); | ||
}).catch((e) => reject(new internals.CelebrateError( | ||
e, | ||
segment, | ||
internals.DEFAULT_ERROR_ARGS, | ||
))); | ||
}); | ||
}) => spec.validateAsync(req[segment], config); | ||
internals.maybeValidateBody = (segment, spec) => (opts) => { | ||
const method = opts.req.method.toLowerCase(); | ||
internals.maybeValidateBody = (segment) => { | ||
const validateBody = internals.validateSegment(segment); | ||
return (spec, opts) => { | ||
const method = opts.req.method.toLowerCase(); | ||
if (method === 'get' || method === 'head') { | ||
return Promise.resolve({ | ||
value: null, | ||
segment, | ||
}); | ||
} | ||
if (method === 'get' || method === 'head') { | ||
// This resolve is to emulate how Joi validates when there isn't an error. I'm doing this to | ||
// standardize the resolve value. | ||
return Promise.resolve({ | ||
value: null, | ||
}); | ||
} | ||
return internals.validateSource(segment, spec)(opts); | ||
return validateBody(spec, opts); | ||
}; | ||
}; | ||
// Map of segments to validation functions. | ||
// The key order is the order validations will run in. | ||
internals.REQ_VALIDATIONS = new Map([ | ||
[segments.HEADERS, internals.validateSource], | ||
[segments.PARAMS, internals.validateSource], | ||
[segments.QUERY, internals.validateSource], | ||
[segments.COOKIES, internals.validateSource], | ||
[segments.SIGNEDCOOKIES, internals.validateSource], | ||
[segments.BODY, internals.maybeValidateBody], | ||
]); | ||
internals.REQ_VALIDATIONS = [ | ||
{ | ||
segment: segments.HEADERS, | ||
validate: internals.validateSegment(segments.HEADERS), | ||
}, | ||
{ | ||
segment: segments.PARAMS, | ||
validate: internals.validateSegment(segments.PARAMS), | ||
}, | ||
{ | ||
segment: segments.QUERY, | ||
validate: internals.validateSegment(segments.QUERY), | ||
}, | ||
{ | ||
segment: segments.COOKIES, | ||
validate: internals.validateSegment(segments.COOKIES), | ||
}, | ||
{ | ||
segment: segments.SIGNEDCOOKIES, | ||
validate: internals.validateSegment(segments.SIGNEDCOOKIES), | ||
}, | ||
{ | ||
segment: segments.BODY, | ||
validate: internals.maybeValidateBody(segments.BODY), | ||
}, | ||
]; | ||
// ⚠️ steps is mutated in this function | ||
internals.check = (steps, opts) => { | ||
const validateFn = steps.shift(); | ||
if (validateFn) { | ||
return validateFn(opts).then(({ value, segment }) => { | ||
// Lifted this idea from https://bit.ly/2vf3Xe0 | ||
internals.check = (steps, requestRules, opts) => steps.reduce((chain, { | ||
validate: stepValidate, | ||
segment: stepSegment, | ||
}) => chain.then(() => { | ||
// If there isn't a schema set up for this segment, early return | ||
const currentSegmentSchema = requestRules.get(stepSegment); | ||
if (!currentSegmentSchema) { | ||
return Promise.resolve(null); | ||
} | ||
return stepValidate(currentSegmentSchema, opts) | ||
.then(({ value }) => { | ||
if (value != null) { | ||
Object.defineProperty(opts.req, segment, { | ||
Object.defineProperty(opts.req, stepSegment, { | ||
value, | ||
}); | ||
} | ||
return internals.check(steps, opts); | ||
return null; | ||
}) | ||
.catch((e) => { | ||
throw new internals.CelebrateError( | ||
e, | ||
stepSegment, | ||
internals.DEFAULT_ERROR_ARGS, | ||
); | ||
}); | ||
} | ||
// If we get here, all the promises have resolved | ||
// and so we have a final resolve to end the recursion | ||
return Promise.resolve(null); | ||
}; | ||
}), Promise.resolve(null)); | ||
exports.celebrate = (schema, joiOpts = {}, opts = {}) => { | ||
Joi.assert(schema, middlewareSchema); | ||
Joi.assert(opts, celebrateSchema); | ||
exports.celebrate = (_requestRules, joiOpts = {}, opts = {}) => { | ||
Joi.assert(_requestRules, REQUESTSCHEMA); | ||
Joi.assert(opts, CELEBRATEOPTSSCHEMA); | ||
// Compile all schemas in advance and only do it once | ||
const requestRules = Object.entries(_requestRules) | ||
.reduce((memo, [key, value]) => memo.set(key, Joi.compile(value)), new Map()); | ||
const middleware = (req, res, next) => { | ||
// We want a fresh copy of steps since `internals.check` mutates the array | ||
const steps = []; | ||
internals.REQ_VALIDATIONS.forEach((validateFn, segment) => { | ||
const spec = schema[segment]; | ||
if (spec) { | ||
steps.push(validateFn(segment, Joi.compile(spec))); | ||
} | ||
}); | ||
const finalConfig = opts.reqContext ? { | ||
const config = opts.reqContext ? { | ||
...joiOpts, | ||
@@ -111,11 +126,10 @@ context: req, | ||
return internals.check(steps, { | ||
config: finalConfig, | ||
// This promise is not part of the public API; it's only here to make the tests cleaner | ||
return internals.check(internals.REQ_VALIDATIONS, requestRules, { | ||
config, | ||
req, | ||
}) | ||
.then(next) | ||
.catch(next); | ||
}).then(next).catch(next); | ||
}; | ||
middleware._schema = schema; | ||
middleware._schema = _requestRules; | ||
@@ -164,4 +178,4 @@ return middleware; | ||
Assert.ok(error && error.isJoi, '"error" must be a Joi error'); | ||
Joi.assert(segment, sourceSchema); | ||
Joi.assert(opts, optSchema); | ||
Joi.assert(segment, SEGMENTSCHEMA); | ||
Joi.assert(opts, CELEBRATEERROROPTSSCHEMA); | ||
return new internals.CelebrateError(error, segment, opts); | ||
@@ -168,0 +182,0 @@ }; |
const Joi = require('@hapi/joi'); | ||
const { segments } = require('./constants'); | ||
exports.middlewareSchema = Joi.object({ | ||
exports.REQUESTSCHEMA = Joi.object({ | ||
[segments.HEADERS]: Joi.any(), | ||
@@ -13,7 +13,7 @@ [segments.PARAMS]: Joi.any(), | ||
exports.celebrateSchema = Joi.object({ | ||
exports.CELEBRATEOPTSSCHEMA = Joi.object({ | ||
reqContext: Joi.boolean(), | ||
}); | ||
exports.sourceSchema = Joi.string().valid( | ||
exports.SEGMENTSCHEMA = Joi.string().valid( | ||
segments.HEADERS, | ||
@@ -27,4 +27,4 @@ segments.PARAMS, | ||
exports.optSchema = Joi.object({ | ||
exports.CELEBRATEERROROPTSSCHEMA = Joi.object({ | ||
celebrated: Joi.boolean().default(false), | ||
}); |
{ | ||
"name": "celebrate", | ||
"version": "12.0.0", | ||
"version": "12.0.1", | ||
"description": "A joi validation middleware for Express.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -50,3 +50,3 @@ [![celebrate](https://github.com/arb/celebrate/raw/master/images/logo.svg?sanitize=1)](https://www.npmjs.org/package/celebrate) | ||
- [`Segments`](#segments) | ||
- [`CelebrateError(err, segment, [opts])`](#celebrateerrorerr-segment-opts) | ||
- [`CelebrateError(error, segment, [opts])`](#celebrateerrorerror-segment-opts) | ||
- [`isCelebrate(err)`](#iscelebrateerr) | ||
@@ -121,4 +121,4 @@ - [Validation Order](#validation-order) | ||
- `schema` - an `object` where `key` can be one of the values from [`Segments`](#segments) and the `value` is a [joi](https://github.com/hapijs/joi/blob/master/API.md) validation schema. Only the keys specified will be validated against the incoming request object. If you omit a key, that part of the `req` object will not be validated. A schema must contain at least one valid key. | ||
- `[joiOptions]` - optional `object` containing joi [options](https://github.com/hapijs/joi/blob/master/API.md#anyvalidatevalue-options) that are passed directly into the `validate` function. Defaults to `{ warnings: true }`. | ||
- `requestRules` - an `object` where `key` can be one of the values from [`Segments`](#segments) and the `value` is a [joi](https://github.com/hapijs/joi/blob/master/API.md) validation schema. Only the keys specified will be validated against the incoming request object. If you omit a key, that part of the `req` object will not be validated. A schema must contain at least one valid key. | ||
- `[joiOpts]` - optional `object` containing joi [options](https://github.com/hapijs/joi/blob/master/API.md#anyvalidatevalue-options) that are passed directly into the `validate` function. Defaults to `{ warnings: true }`. | ||
- `[opts]` - an optional `object` with the following keys. Defaults to `{}`. | ||
@@ -154,3 +154,3 @@ - `reqContext` - `bool` value that instructs joi to use the incoming `req` object as the `context` value during joi validation. If set, this will trump the value of `joiOptions.context`. This is useful if you want to validate part of the request object against another part of the request object. See the tests for more details. | ||
### `CelebrateError(err, segment, [opts])` | ||
### `CelebrateError(error, segment, [opts])` | ||
@@ -157,0 +157,0 @@ A factory function for creating celebrate errors. |
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
19668
262