Socket
Socket
Sign inDemoInstall

fastify

Package Overview
Dependencies
Maintainers
3
Versions
288
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fastify - npm Package Compare versions

Comparing version 4.6.0 to 4.7.0

test/reply-code.test.js

27

docs/Guides/Ecosystem.md

@@ -83,2 +83,4 @@ <h1 align="center">Fastify</h1>

[`simple-oauth2`](https://github.com/lelylan/simple-oauth2).
- [`@fastify/one-line-logger`](https://github.com/fastify/one-line-logger) Formats
Fastify's logs into a nice one-line message.
- [`@fastify/postgres`](https://github.com/fastify/fastify-postgres) Fastify

@@ -123,6 +125,6 @@ PostgreSQL connection plugin, with this you can share the same PostgreSQL

[type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
for [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts).
for [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts).
- [`@fastify/type-provider-typebox`](https://github.com/fastify/fastify-type-provider-typebox)
Fastify
[type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
[type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
for [Typebox](https://github.com/sinclairzx81/typebox).

@@ -172,3 +174,3 @@ - [`@fastify/under-pressure`](https://github.com/fastify/under-pressure) Measure

- [`@mateonunez/fastify-lyra`](https://github.com/mateonunez/fastify-lyra)
A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
on Fastify

@@ -197,3 +199,3 @@ - [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit)

request IDs into your logs.
- [`electron-server`](https://github.com/anonrig/electron-server) A plugin for
- [`electron-server`](https://github.com/anonrig/electron-server) A plugin for
using Fastify without the need of consuming a port on Electron apps.

@@ -453,3 +455,3 @@ - [`fast-water`](https://github.com/tswayne/fast-water) A Fastify plugin for

- [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify
OSM plugin to run overpass queries by OpenStreetMap.
OSM plugin to run overpass queries by OpenStreetMap.
- [`fastify-peekaboo`](https://github.com/simone-sanfratello/fastify-peekaboo)

@@ -479,2 +481,5 @@ Fastify plugin for memoize responses by expressive settings.

plugin that adds support to handle an aborted request asynchronous.
- [`fastify-ravendb`](https://github.com/nearform/fastify-ravendb) RavenDB
connection plugin. It exposes the same `DocumentStore` (or multiple ones)
across the whole Fastify application.
- [`fastify-raw-body`](https://github.com/Eomm/fastify-raw-body) Add the

@@ -505,3 +510,5 @@ `request.rawBody` field.

- [`fastify-route-group`](https://github.com/TakNePoidet/fastify-route-group)
Convenient grouping and inheritance of routes
Convenient grouping and inheritance of routes.
- [`fastify-s3-buckets`](https://github.com/kibertoad/fastify-s3-buckets)
Ensure the existence of defined S3 buckets on the application startup.
- [`fastify-schema-constraint`](https://github.com/Eomm/fastify-schema-constraint)

@@ -522,2 +529,4 @@ Choose the JSON schema to use based on request parameters.

plugin, with this you can use slonik in every part of your server.
- [`fastify-slow-down`](https://github.com/nearform/fastify-slow-down) A plugin
to delay the response from the server.
- [`fastify-socket.io`](https://github.com/alemagio/fastify-socket.io) a

@@ -528,2 +537,4 @@ Socket.io plugin for Fastify.

HTTP part of the request.
- [`fastify-sqlite`](https://github.com/Eomm/fastify-sqlite) connects your
application to a sqlite3 database.
- [`fastify-sse`](https://github.com/lolo32/fastify-sse) to provide Server-Sent

@@ -553,4 +564,4 @@ Events with `reply.sse( … )` to Fastify.

- [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
Fastify
[type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
Fastify
[type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
for [zod](https://github.com/colinhacks/zod).

@@ -557,0 +568,0 @@ - [`fastify-typeorm-plugin`](https://github.com/inthepocket/fastify-typeorm-plugin)

@@ -311,2 +311,46 @@ <h1 align="center">Fastify</h1>

An alternative approach is to make use of the [onRoute hook](../Reference/Hooks.md#onroute)
to customize application routes dynamically from inside the plugin. Every time
a new route is registered, you can read and modify the route options. For example,
based on a [route config option](../Reference/Routes.md#routes-options):
```js
fastify.register((instance, opts, done) => {
instance.decorate('util', (request, key, value) => { request[key] = value })
function handler(request, reply, done) {
instance.util(request, 'timestamp', new Date())
done()
}
instance.addHook('onRoute', (routeOptions) => {
if (routeOptions.config && routeOptions.config.useUtil === true) {
// set or add our handler to the route preHandler hook
if (!routeOptions.preHandler) {
routeOptions.preHandler = [handler]
return
}
if (Array.isArray(routeOptions.preHandler)) {
routeOptions.preHandler.push(handler)
return
}
routeOptions.preHandler = [routeOptions.preHandler, handler]
}
})
fastify.get('/plugin1', {config: {useUtil: true}}, (request, reply) => {
reply.send(request)
})
fastify.get('/plugin2', (request, reply) => {
reply.send(request)
})
done()
})
```
This variant becomes extremely useful if you plan to distribute your plugin, as
described in the next section.
As you probably noticed by now, `request` and `reply` are not the standard

@@ -313,0 +357,0 @@ Nodejs *request* and *response* objects, but Fastify's objects.

@@ -10,6 +10,6 @@ <h1 align="center">Fastify</h1>

- [.header(key, value)](#headerkey-value)
- [set-cookie](#set-cookie)
- [.headers(object)](#headersobject)
- [.getHeader(key)](#getheaderkey)
- [.getHeaders()](#getheaders)
- [set-cookie](#set-cookie)
- [.removeHeader(key)](#removeheaderkey)

@@ -20,9 +20,9 @@ - [.hasHeader(key)](#hasheaderkey)

- [.removeTrailer(key)](#removetrailerkey)
- [.redirect([code,] dest)](#redirectcode--dest)
- [.redirect([code ,] dest)](#redirectcode--dest)
- [.callNotFound()](#callnotfound)
- [.getResponseTime()](#getresponsetime)
- [.type(contentType)](#typecontenttype)
- [.getSerializationFunction(schema | httpStatus)](#getserializationfunction)
- [.compileSerializationSchema(schema, httpStatus)](#compileserializationschema)
- [.serializeInput(data, [schema | httpStatus], [httpStatus])](#serializeinput)
- [.getSerializationFunction(schema | httpStatus)](#getserializationfunctionschema--httpstatus)
- [.compileSerializationSchema(schema, httpStatus)](#compileserializationschemaschema-httpstatus)
- [.serializeInput(data, [schema | httpStatus], [httpStatus])](#serializeinputdata-schema--httpstatus-httpstatus)
- [.serializer(func)](#serializerfunc)

@@ -29,0 +29,0 @@ - [.raw](#raw)

@@ -38,2 +38,9 @@ <h1 align="center">Fastify</h1>

- `socket` - the underlying connection of the incoming request
- `context` - A Fastify internal object. You should not use it directly or
modify it. It is useful to access one special key:
- `context.config` - The route [`config`](./Routes.md#routes-config) object.
- `routeSchema` - the scheme definition set for the router that is
handling the request
- `routeConfig` - The route [`config`](./Routes.md#routes-config)
object.
- [.getValidationFunction(schema | httpPart)](#getvalidationfunction) -

@@ -52,5 +59,2 @@ Returns a validation function for the specified schema or http part,

function given for that HTTP Status Code. Defaults to `null`.
- `context` - A Fastify internal object. You should not use it directly or
modify it. It is useful to access one special key:
- `context.config` - The route [`config`](./Routes.md#routes-config) object.

@@ -103,2 +107,5 @@ ### Headers

This function has property errors. Errors encountered during the last validation
are assigned to errors
```js

@@ -114,3 +121,4 @@ const validate = request

})
validate({ foo: 'bar' }) // true
console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null

@@ -121,3 +129,4 @@ // or

.getValidationFunction('body')
validate({ foo: 0.5 }) // false
console.log(validate({ foo: 0.5 })) // false
console.log(validate.errors) // validation errors
```

@@ -141,2 +150,4 @@

This function has property errors. Errors encountered during the last validation
are assigned to errors

@@ -154,2 +165,3 @@ ```js

console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null

@@ -168,2 +180,3 @@ // or

console.log(validate({ hello: 'world' })) // false
console.log(validate.errors) // validation errors
```

@@ -258,2 +271,2 @@

See [.compileValidationSchema(schema, [httpStatus])](#compileValidationSchema)
for more information on how to compile validation schemas.
for more information on how to compile validation schemas.

@@ -44,4 +44,4 @@ <h1 align="center">Fastify</h1>

* `body`: validates the body of the request if it is a POST, PUT, or PATCH
method.
* `body`: validates the body of the request if it is a POST, PUT, PATCH,
TRACE, or SEARCH method.
* `querystring` or `query`: validates the querystring. This can be a complete

@@ -48,0 +48,0 @@ JSON Schema object, with the property `type` of `object` and `properties`

@@ -151,3 +151,3 @@ <h1 align="center">Fastify</h1>

_currently_ is not possible to avoid multiple registrations on routes when
dealing with several scopes, see bellow:
dealing with several scopes, see below:

@@ -222,3 +222,3 @@ ```ts

FastifyInstance,
FastifyLoggerInstance,
FastifyBaseLogger,
RawReplyDefaultExpression,

@@ -234,3 +234,3 @@ RawRequestDefaultExpression,

RawReplyDefaultExpression<RawServerDefault>,
FastifyLoggerInstance,
FastifyBaseLogger,
TypeBoxTypeProvider

@@ -237,0 +237,0 @@ >;

'use strict'
const VERSION = '4.6.0'
const VERSION = '4.7.0'

@@ -5,0 +5,0 @@ const Avvio = require('avvio')

'use strict'
const { AsyncResource } = require('async_hooks')
let lru = require('tiny-lru')
// Needed to handle Webpack and faux modules
// See https://github.com/fastify/fastify/issues/2356
// and https://github.com/fastify/fastify/discussions/2907.
lru = typeof lru === 'function' ? lru : lru.default
const lru = require('tiny-lru').lru

@@ -18,3 +14,4 @@ const secureJson = require('secure-json-parse')

kTestInternals,
kReplyIsError
kReplyIsError,
kRouteContext
} = require('./symbols')

@@ -153,3 +150,7 @@

if (parser === undefined) {
reply.send(new FST_ERR_CTP_INVALID_MEDIA_TYPE(contentType || undefined))
if (request.is404) {
handler(request, reply)
} else {
reply.send(new FST_ERR_CTP_INVALID_MEDIA_TYPE(contentType || undefined))
}
} else if (parser.asString === true || parser.asBuffer === true) {

@@ -159,3 +160,3 @@ rawBody(

reply,
reply.context._parserOptions,
reply[kRouteContext]._parserOptions,
parser,

@@ -191,3 +192,3 @@ done

? NaN
: Number.parseInt(request.headers['content-length'], 10)
: Number(request.headers['content-length'])

@@ -194,0 +195,0 @@ if (contentLength > limit) {

@@ -15,6 +15,7 @@ 'use strict'

kRequestValidateWeakMap,
kReplySerializeWeakMap
kReplySerializeWeakMap,
kPublicRouteContext
} = require('./symbols.js')
// Objects that holds the context of every request
// Object that holds the context of every request
// Every route holds an instance of this object.

@@ -60,3 +61,6 @@ function Context ({

this[kReplySerializerDefault] = replySerializer
this.schemaErrorFormatter = schemaErrorFormatter || server[kSchemaErrorFormatter] || defaultSchemaErrorFormatter
this.schemaErrorFormatter =
schemaErrorFormatter ||
server[kSchemaErrorFormatter] ||
defaultSchemaErrorFormatter
this[kRouteByFastify] = isFastify

@@ -69,5 +73,25 @@

// Route + Userland configurations for the route
this[kPublicRouteContext] = getPublicRouteContext(this)
this.server = server
}
function getPublicRouteContext (context) {
return Object.create(null, {
schema: {
enumerable: true,
get () {
return context.schema
}
},
config: {
enumerable: true,
get () {
return context.config
}
}
})
}
function defaultSchemaErrorFormatter (errors, dataVar) {

@@ -74,0 +98,0 @@ let text = ''

@@ -6,3 +6,7 @@ 'use strict'

const {
kReplyHeaders, kReplyNextErrorHandler, kReplyIsRunningOnErrorHook, kReplyHasStatusCode
kReplyHeaders,
kReplyNextErrorHandler,
kReplyIsRunningOnErrorHook,
kReplyHasStatusCode,
kRouteContext
} = require('./symbols.js')

@@ -28,3 +32,3 @@

const context = reply.context
const context = reply[kRouteContext]
if (reply[kReplyNextErrorHandler] === false) {

@@ -95,3 +99,3 @@ fallbackErrorHandler(error, reply, function (reply, payload) {

try {
const serializerFn = getSchemaSerializer(reply.context, statusCode)
const serializerFn = getSchemaSerializer(reply[kRouteContext], statusCode)
payload = (serializerFn === false)

@@ -98,0 +102,0 @@ ? serializeError({

@@ -7,3 +7,4 @@ 'use strict'

const {
kReplyIsError
kReplyIsError,
kRouteContext
} = require('./symbols')

@@ -21,4 +22,5 @@

const headers = request.headers
const context = request[kRouteContext]
if (method === 'GET' || method === 'HEAD' || method === 'SEARCH') {
if (method === 'GET' || method === 'HEAD') {
handler(request, reply)

@@ -30,3 +32,3 @@ return

if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE') {
if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE' || method === 'SEARCH') {
if (contentType === undefined) {

@@ -39,6 +41,6 @@ if (

} else {
reply.context.contentTypeParser.run('', handler, request, reply)
context.contentTypeParser.run('', handler, request, reply)
}
} else {
reply.context.contentTypeParser.run(contentType, handler, request, reply)
context.contentTypeParser.run(contentType, handler, request, reply)
}

@@ -56,3 +58,3 @@ return

) {
reply.context.contentTypeParser.run(contentType, handler, request, reply)
context.contentTypeParser.run(contentType, handler, request, reply)
} else {

@@ -70,5 +72,5 @@ handler(request, reply)

try {
if (reply.context.preValidation !== null) {
if (request[kRouteContext].preValidation !== null) {
hookRunner(
reply.context.preValidation,
request[kRouteContext].preValidation,
hookIterator,

@@ -96,5 +98,5 @@ request,

const result = validateSchema(reply.context, request)
const result = validateSchema(reply[kRouteContext], request)
if (result) {
if (reply.context.attachValidation === false) {
if (reply[kRouteContext].attachValidation === false) {
reply.send(result)

@@ -108,5 +110,5 @@ return

// preHandler hook
if (reply.context.preHandler !== null) {
if (request[kRouteContext].preHandler !== null) {
hookRunner(
reply.context.preHandler,
request[kRouteContext].preHandler,
hookIterator,

@@ -134,3 +136,3 @@ request,

try {
result = reply.context.handler(request, reply)
result = request[kRouteContext].handler(request, reply)
} catch (err) {

@@ -137,0 +139,0 @@ reply[kReplyIsError] = true

@@ -23,3 +23,4 @@ 'use strict'

kSchemaController,
kOptions
kOptions,
kRouteContext
} = require('./symbols.js')

@@ -67,5 +68,12 @@ const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks')

Object.defineProperties(Reply.prototype, {
[kRouteContext]: {
get () {
return this.request[kRouteContext]
}
},
// TODO: remove once v5 is done
// Is temporary to avoid constant conflicts between `next` and `main`
context: {
get () {
return this.request.context
return this.request[kRouteContext]
}

@@ -75,3 +83,3 @@ },

get () {
return this.request.context.server
return this.request[kRouteContext].server
}

@@ -298,3 +306,3 @@ },

Reply.prototype.code = function (code) {
const intValue = parseInt(code)
const intValue = Number(code)
if (isNaN(intValue) || intValue < 100 || intValue > 599) {

@@ -315,5 +323,5 @@ throw new FST_ERR_BAD_STATUS_CODE(code || String(code))

if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
serialize = this.context[kSchemaResponse]?.[schemaOrStatus]
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
} else if (typeof schemaOrStatus === 'object') {
serialize = this.context[kReplySerializeWeakMap]?.get(schemaOrStatus)
serialize = this[kRouteContext][kReplySerializeWeakMap]?.get(schemaOrStatus)
}

@@ -329,7 +337,7 @@

// Check if serialize function already compiled
if (this.context[kReplySerializeWeakMap]?.has(schema)) {
return this.context[kReplySerializeWeakMap].get(schema)
if (this[kRouteContext][kReplySerializeWeakMap]?.has(schema)) {
return this[kRouteContext][kReplySerializeWeakMap].get(schema)
}
const serializerCompiler = this.context.serializerCompiler ||
const serializerCompiler = this[kRouteContext].serializerCompiler ||
this.server[kSchemaController].serializerCompiler ||

@@ -355,7 +363,7 @@ (

// encapsulated contexts
if (this.context[kReplySerializeWeakMap] == null) {
this.context[kReplySerializeWeakMap] = new WeakMap()
if (this[kRouteContext][kReplySerializeWeakMap] == null) {
this[kRouteContext][kReplySerializeWeakMap] = new WeakMap()
}
this.context[kReplySerializeWeakMap].set(schema, serializeFn)
this[kRouteContext][kReplySerializeWeakMap].set(schema, serializeFn)

@@ -372,3 +380,3 @@ return serializeFn

if (httpStatus != null) {
serialize = this.context[kSchemaResponse]?.[httpStatus]
serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]

@@ -378,4 +386,4 @@ if (serialize == null) throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)

// Check if serialize function already compiled
if (this.context[kReplySerializeWeakMap]?.has(schema)) {
serialize = this.context[kReplySerializeWeakMap].get(schema)
if (this[kRouteContext][kReplySerializeWeakMap]?.has(schema)) {
serialize = this[kRouteContext][kReplySerializeWeakMap].get(schema)
} else {

@@ -393,6 +401,6 @@ serialize = this.compileSerializationSchema(schema, httpStatus)

} else {
if (this.context && this.context[kReplySerializerDefault]) {
return this.context[kReplySerializerDefault](payload, this.raw.statusCode)
if (this[kRouteContext] && this[kRouteContext][kReplySerializerDefault]) {
return this[kRouteContext][kReplySerializerDefault](payload, this.raw.statusCode)
} else {
return serialize(this.context, payload, this.raw.statusCode)
return serialize(this[kRouteContext], payload, this.raw.statusCode)
}

@@ -463,5 +471,5 @@ }

function preserializeHook (reply, payload) {
if (reply.context.preSerialization !== null) {
if (reply[kRouteContext].preSerialization !== null) {
onSendHookRunner(
reply.context.preSerialization,
reply[kRouteContext].preSerialization,
reply.request,

@@ -486,6 +494,6 @@ reply,

payload = reply[kReplySerializer](payload)
} else if (reply.context && reply.context[kReplySerializerDefault]) {
payload = reply.context[kReplySerializerDefault](payload, reply.raw.statusCode)
} else if (reply[kRouteContext] && reply[kRouteContext][kReplySerializerDefault]) {
payload = reply[kRouteContext][kReplySerializerDefault](payload, reply.raw.statusCode)
} else {
payload = serialize(reply.context, payload, reply.raw.statusCode)
payload = serialize(reply[kRouteContext], payload, reply.raw.statusCode)
}

@@ -502,9 +510,9 @@ } catch (e) {

function wrapSeralizationError (error, reply) {
error.serialization = reply.context.config
error.serialization = reply[kRouteContext].config
}
function onSendHook (reply, payload) {
if (reply.context.onSend !== null) {
if (reply[kRouteContext].onSend !== null) {
onSendHookRunner(
reply.context.onSend,
reply[kRouteContext].onSend,
reply.request,

@@ -578,3 +586,3 @@ reply,

(req.raw.method !== 'HEAD' &&
parseInt(contentLength, 10) !== Buffer.byteLength(payload)
Number(contentLength) !== Buffer.byteLength(payload)
)

@@ -677,6 +685,6 @@ ) {

function onErrorHook (reply, error, cb) {
if (reply.context.onError !== null && !reply[kReplyNextErrorHandler]) {
if (reply[kRouteContext].onError !== null && !reply[kReplyNextErrorHandler]) {
reply[kReplyIsRunningOnErrorHook] = true
onSendHookRunner(
reply.context.onError,
reply[kRouteContext].onError,
reply.request,

@@ -700,3 +708,3 @@ reply,

const ctx = reply.context
const ctx = reply[kRouteContext]

@@ -778,3 +786,3 @@ if (ctx && ctx.onResponse !== null) {

function notFound (reply) {
if (reply.context[kFourOhFourContext] === null) {
if (reply[kRouteContext][kFourOhFourContext] === null) {
reply.log.warn('Trying to send a NotFound error inside a 404 handler. Sending basic 404 response.')

@@ -785,8 +793,8 @@ reply.code(404).send('404 Not Found')

reply.request.context = reply.context[kFourOhFourContext]
reply.request[kRouteContext] = reply[kRouteContext][kFourOhFourContext]
// preHandler hook
if (reply.context.preHandler !== null) {
if (reply[kRouteContext].preHandler !== null) {
hookRunner(
reply.context.preHandler,
reply[kRouteContext].preHandler,
hookIterator,

@@ -793,0 +801,0 @@ reply.request,

@@ -14,3 +14,5 @@ 'use strict'

kOptions,
kRequestValidateWeakMap
kRequestValidateWeakMap,
kRouteContext,
kPublicRouteContext
} = require('./symbols')

@@ -29,3 +31,3 @@ const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')

this.id = id
this.context = context
this[kRouteContext] = context
this.params = params

@@ -71,3 +73,3 @@ this.raw = req

this.id = id
this.context = context
this[kRouteContext] = context
this.params = params

@@ -145,3 +147,3 @@ this.raw = req

get () {
return this.context.server
return this[kRouteContext].server
}

@@ -159,5 +161,11 @@ },

},
context: {
get () {
warning.emit('FSTDEP012')
return this[kRouteContext]
}
},
routerPath: {
get () {
return this.context.config.url
return this[kRouteContext].config.url
}

@@ -167,8 +175,18 @@ },

get () {
return this.context.config.method
return this[kRouteContext].config.method
}
},
routeConfig: {
get () {
return this[kRouteContext][kPublicRouteContext].config
}
},
routeSchema: {
get () {
return this[kRouteContext][kPublicRouteContext].schema
}
},
is404: {
get () {
return this.context.config.url === undefined
return this[kRouteContext].config.url === undefined
}

@@ -224,5 +242,5 @@ },

const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
return this.context[symbol]
return this[kRouteContext][symbol]
} else if (typeof httpPartOrSchema === 'object') {
return this.context[kRequestValidateWeakMap]?.get(httpPartOrSchema)
return this[kRouteContext][kRequestValidateWeakMap]?.get(httpPartOrSchema)
}

@@ -235,7 +253,7 @@ }

if (this.context[kRequestValidateWeakMap]?.has(schema)) {
return this.context[kRequestValidateWeakMap].get(schema)
if (this[kRouteContext][kRequestValidateWeakMap]?.has(schema)) {
return this[kRouteContext][kRequestValidateWeakMap].get(schema)
}
const validatorCompiler = this.context.validatorCompiler ||
const validatorCompiler = this[kRouteContext].validatorCompiler ||
this.server[kSchemaController].validatorCompiler ||

@@ -261,7 +279,7 @@ (

// encapsulated contexts
if (this.context[kRequestValidateWeakMap] == null) {
this.context[kRequestValidateWeakMap] = new WeakMap()
if (this[kRouteContext][kRequestValidateWeakMap] == null) {
this[kRouteContext][kRequestValidateWeakMap] = new WeakMap()
}
this.context[kRequestValidateWeakMap].set(schema, validateFn)
this[kRouteContext][kRequestValidateWeakMap].set(schema, validateFn)

@@ -280,3 +298,3 @@ return validateFn

// Validate using the HTTP Request Part schema
validate = this.context[symbol]
validate = this[kRouteContext][symbol]
}

@@ -293,4 +311,4 @@

if (validate == null) {
if (this.context[kRequestValidateWeakMap]?.has(schema)) {
validate = this.context[kRequestValidateWeakMap].get(schema)
if (this[kRouteContext][kRequestValidateWeakMap]?.has(schema)) {
validate = this[kRouteContext][kRequestValidateWeakMap].get(schema)
} else {

@@ -297,0 +315,0 @@ // We proceed to compile if there's no validate function yet

@@ -11,3 +11,2 @@ 'use strict'

const warning = require('./warnings')
const { kRequestAcceptVersion, kRouteByFastify } = require('./symbols')

@@ -41,3 +40,6 @@ const {

kErrorHandler,
kHasBeenDecorated
kHasBeenDecorated,
kRequestAcceptVersion,
kRouteByFastify,
kRouteContext
} = require('./symbols.js')

@@ -160,2 +162,8 @@ const { buildErrorHandler } = require('./error-handler')

const { exposeHeadRoute } = opts
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
// we need to clone a set of initial options for HEAD route
const headOpts = shouldExposeHead && options.method === 'GET' ? { ...options } : null
throwIfAlreadyStarted('Cannot add route when fastify instance is already started!')

@@ -327,3 +335,4 @@

try {
compileSchemasForValidation(context, opts.validatorCompiler || schemaController.validatorCompiler)
const isCustom = typeof opts?.validatorCompiler === 'function' || schemaController.isCustomValidatorCompiler
compileSchemasForValidation(context, opts.validatorCompiler || schemaController.validatorCompiler, isCustom)
} catch (error) {

@@ -349,9 +358,6 @@ throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)

// we must place it after the `this.after`
const { exposeHeadRoute } = opts
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
if (shouldExposeHead && options.method === 'GET' && !hasHEADHandler) {
const onSendHandlers = parseHeadOnSendHandlers(opts.onSend)
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...opts, onSend: onSendHandlers }, isFastify: true })
const onSendHandlers = parseHeadOnSendHandlers(headOpts.onSend)
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...headOpts, onSend: onSendHandlers }, isFastify: true })
} else if (hasHEADHandler && exposeHeadRoute) {

@@ -498,4 +504,4 @@ warning.emit('FSTDEP007')

if (reply.context.preParsing !== null) {
preParsingHookRunner(reply.context.preParsing, request, reply, handleRequest)
if (request[kRouteContext].preParsing !== null) {
preParsingHookRunner(request[kRouteContext].preParsing, request, reply, handleRequest)
} else {

@@ -502,0 +508,0 @@ handleRequest(null, request, reply)

@@ -28,3 +28,5 @@ 'use strict'

bucket: (opts && opts.bucket) || buildSchemas,
compilersFactory
compilersFactory,
isCustomValidatorCompiler: typeof opts?.compilersFactory?.buildValidator === 'function',
isCustomSerializerCompiler: typeof opts?.compilersFactory?.buildValidator === 'function'
}

@@ -41,2 +43,4 @@

this.compilersFactory = this.opts.compilersFactory
this.isCustomValidatorCompiler = this.opts.isCustomValidatorCompiler || false
this.isCustomSerializerCompiler = this.opts.isCustomSerializerCompiler || false

@@ -70,2 +74,3 @@ if (parent) {

this.validatorCompiler = validatorCompiler
this.isCustomValidatorCompiler = true
}

@@ -75,2 +80,3 @@

this.serializerCompiler = serializerCompiler
this.isCustomSerializerCompiler = true
}

@@ -77,0 +83,0 @@

@@ -334,4 +334,4 @@ 'use strict'

function normalizePort (firstArg) {
const port = parseInt(firstArg, 10)
return port >= 0 && !Number.isNaN(port) ? port : 0
const port = Number(firstArg)
return port >= 0 && !Number.isNaN(port) && Number.isInteger(port) ? port : 0
}

@@ -338,0 +338,0 @@

@@ -17,2 +17,4 @@ 'use strict'

kPluginNameChain: Symbol('fastify.pluginNameChain'),
kRouteContext: Symbol('fastify.context'),
kPublicRouteContext: Symbol('fastify.routeOptions'),
// Schema

@@ -19,0 +21,0 @@ kSchemaController: Symbol('fastify.schemaController'),

@@ -35,3 +35,3 @@ 'use strict'

function compileSchemasForValidation (context, compile) {
function compileSchemasForValidation (context, compile, isCustom) {
const { schema } = context

@@ -45,4 +45,5 @@ if (!schema) {

const headers = schema.headers
if (headers && Object.getPrototypeOf(headers) !== Object.prototype) {
// do not mess with non-literals, e.g. Joi schemas
// the or part is used for backward compatibility
if (headers && (isCustom || Object.getPrototypeOf(headers) !== Object.prototype)) {
// do not mess with schema when custom validator applied, e.g. Joi, Typebox
context[headersSchema] = compile({ schema: headers, method, url, httpPart: 'headers' })

@@ -49,0 +50,0 @@ } else if (headers) {

@@ -24,2 +24,4 @@ 'use strict'

warning.create('FastifyDeprecation', 'FSTDEP012', 'Request#context property access is deprecated. Please use "Request#routeConfig" or "Request#routeSchema" instead for accessing Route settings. The "Request#context" will be removed in `fastify@5`.')
module.exports = warning
{
"name": "fastify",
"version": "4.6.0",
"version": "4.7.0",
"description": "Fast and low overhead web framework, for Node.js",

@@ -129,7 +129,7 @@ "main": "fastify.js",

"@fastify/pre-commit": "^2.0.2",
"@sinclair/typebox": "^0.24.9",
"@sinclair/typebox": "^0.24.41",
"@sinonjs/fake-timers": "^9.1.2",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"@types/node": "^18.7.18",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"ajv": "^8.11.0",

@@ -141,11 +141,11 @@ "ajv-errors": "^3.0.0",

"branch-comparer": "^1.1.0",
"eslint": "^8.16.0",
"eslint-config-standard": "^17.0.0-1",
"eslint": "^8.23.1",
"eslint-config-standard": "^17.0.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.2.0",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-n": "^15.2.5",
"eslint-plugin-promise": "^6.0.1",
"fast-json-body": "^1.1.0",
"fast-json-stringify": "^5.0.0",
"fastify-plugin": "^4.0.0",
"fast-json-stringify": "^5.3.0",
"fastify-plugin": "^4.2.1",
"fluent-json-schema": "^3.1.0",

@@ -156,6 +156,6 @@ "form-data": "^4.0.0",

"joi": "^17.6.0",
"json-schema-to-ts": "^2.5.3",
"json-schema-to-ts": "^2.5.5",
"JSONStream": "^1.3.5",
"license-checker": "^25.0.1",
"markdownlint-cli2": "^0.5.0",
"markdownlint-cli2": "^0.5.1",
"proxyquire": "^2.1.3",

@@ -168,7 +168,7 @@ "pump": "^3.0.0",

"split2": "^4.1.0",
"standard": "^17.0.0-2",
"tap": "^16.2.0",
"tsd": "^0.23.0",
"typescript": "^4.7.2",
"undici": "^5.4.0",
"standard": "^17.0.0",
"tap": "^16.3.0",
"tsd": "^0.24.1",
"typescript": "^4.8.3",
"undici": "^5.10.0",
"vary": "^1.1.2",

@@ -178,16 +178,16 @@ "yup": "^0.32.11"

"dependencies": {
"@fastify/ajv-compiler": "^3.1.1",
"@fastify/ajv-compiler": "^3.3.1",
"@fastify/error": "^3.0.0",
"@fastify/fast-json-stringify-compiler": "^4.0.0",
"@fastify/fast-json-stringify-compiler": "^4.1.0",
"abstract-logging": "^2.0.1",
"avvio": "^8.1.3",
"find-my-way": "^7.0.0",
"light-my-request": "^5.5.1",
"pino": "^8.0.0",
"avvio": "^8.2.0",
"find-my-way": "^7.2.0",
"light-my-request": "^5.6.1",
"pino": "^8.5.0",
"process-warning": "^2.0.0",
"proxy-addr": "^2.0.7",
"rfdc": "^1.3.0",
"secure-json-parse": "^2.4.0",
"secure-json-parse": "^2.5.0",
"semver": "^7.3.7",
"tiny-lru": "^8.0.2"
"tiny-lru": "^9.0.2"
},

@@ -194,0 +194,0 @@ "standard": {

@@ -9,2 +9,3 @@ 'use strict'

const split = require('split2')
const FormData = require('form-data')
const Fastify = require('..')

@@ -22,3 +23,3 @@

test('default 404', t => {
t.plan(4)
t.plan(5)

@@ -79,2 +80,19 @@ const test = t.test

})
test('using post method and multipart/formdata', t => {
t.plan(3)
const form = FormData()
form.append('test-field', 'just some field')
sget({
method: 'POST',
url: getUrl(fastify) + '/notSupported',
body: form,
json: false
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 404)
t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
})
})
})

@@ -81,0 +99,0 @@ })

@@ -5,2 +5,3 @@ 'use strict'

const test = t.test
const { kRouteContext } = require('../lib/symbols')
const Fastify = require('..')

@@ -17,3 +18,3 @@

function handler (req, reply) {
reply.send(reply.context.config)
reply.send(reply[kRouteContext].config)
}

@@ -20,0 +21,0 @@

'use strict'
const http = require('http')
const test = require('tap').test
const { kRouteContext } = require('../lib/symbols')
const fastify = require('../')
function getUrl (app) {
const { address, port } = app.server.address()
if (address === '::1') {
return `http://[${address}]:${port}`
} else {
return `http://${address}:${port}`
}
}
test('handlers receive correct `this` context', (t) => {
test('handlers receive correct `this` context', async (t) => {
t.plan(4)

@@ -35,13 +25,9 @@

instance.listen({ port: 0 }, (err) => {
instance.server.unref()
if (err) t.threw(err)
t.ok(instance.foo)
t.equal(instance.foo, 'foo')
await instance.inject('/')
http.get(getUrl(instance), () => {}).on('error', t.threw)
})
t.ok(instance.foo)
t.equal(instance.foo, 'foo')
})
test('handlers have access to the internal context', (t) => {
test('handlers have access to the internal context', async (t) => {
t.plan(5)

@@ -51,15 +37,11 @@

instance.get('/', { config: { foo: 'bar' } }, function (req, reply) {
t.ok(reply.context)
t.ok(reply.context.config)
t.type(reply.context.config, Object)
t.ok(reply.context.config.foo)
t.equal(reply.context.config.foo, 'bar')
t.ok(reply[kRouteContext])
t.ok(reply[kRouteContext].config)
t.type(reply[kRouteContext].config, Object)
t.ok(reply[kRouteContext].config.foo)
t.equal(reply[kRouteContext].config.foo, 'bar')
reply.send()
})
instance.listen({ port: 0 }, (err) => {
instance.server.unref()
if (err) t.threw(err)
http.get(getUrl(instance), () => {}).on('error', t.threw)
})
await instance.inject('/')
})

@@ -7,3 +7,3 @@ 'use strict'

const { Readable } = require('stream')
const { kTestInternals } = require('../../lib/symbols')
const { kTestInternals, kRouteContext } = require('../../lib/symbols')
const Request = require('../../lib/request')

@@ -54,3 +54,3 @@ const Reply = require('../../lib/reply')

reply,
reply.context._parserOptions,
reply[kRouteContext]._parserOptions,
parser,

@@ -108,3 +108,3 @@ done

reply,
reply.context._parserOptions,
reply[kRouteContext]._parserOptions,
parser,

@@ -111,0 +111,0 @@ done

@@ -8,2 +8,3 @@ 'use strict'

const Reply = require('../../lib/reply')
const { kRouteContext } = require('../../lib/symbols')
const buildSchema = require('../../lib/validation').compileSchemasForValidation

@@ -73,3 +74,3 @@ const sget = require('simple-get').concat

body: { hello: 'world' },
context
[kRouteContext]: context
}

@@ -101,3 +102,3 @@ internals.handler(request, new Reply(res, request))

buildSchema(context, schemaValidator)
internals.handler({}, new Reply(res, { context }))
internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
})

@@ -127,3 +128,3 @@

buildSchema(context, schemaValidator)
internals.handler({}, new Reply(res, { context }))
internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
})

@@ -130,0 +131,0 @@

'use strict'
const { test } = require('tap')
const { kReplySerializeWeakMap } = require('../../lib/symbols')
const { kReplySerializeWeakMap, kRouteContext } = require('../../lib/symbols')
const Fastify = require('../../fastify')

@@ -175,5 +175,5 @@

t.equal(reply.context[kReplySerializeWeakMap], null)
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
t.type(reply.context[kReplySerializeWeakMap], WeakMap)
t.type(reply[kRouteContext][kReplySerializeWeakMap], WeakMap)
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))

@@ -235,3 +235,3 @@

if (parseInt(id) === 1) {
if (Number(id) === 1) {
const serialize4xx = reply.getSerializationFunction('4xx')

@@ -313,3 +313,3 @@ const serialize201 = reply.getSerializationFunction(201)

if (parseInt(id) === 1) {
if (Number(id) === 1) {
const serialize = reply.compileSerializationSchema(schemaObj)

@@ -358,5 +358,5 @@

t.notOk(reply.getSerializationFunction(getDefaultSchema()))
t.equal(reply.context[kReplySerializeWeakMap], null)
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
t.notOk(reply.getSerializationFunction('200'))
t.equal(reply.context[kReplySerializeWeakMap], null)
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)

@@ -575,5 +575,5 @@ reply.send({ hello: 'world' })

const input = { hello: 'world' }
t.equal(reply.context[kReplySerializeWeakMap], null)
t.equal(reply[kRouteContext][kReplySerializeWeakMap], null)
t.equal(reply.serializeInput(input, getDefaultSchema()), JSON.stringify(input))
t.type(reply.context[kReplySerializeWeakMap], WeakMap)
t.type(reply[kRouteContext][kReplySerializeWeakMap], WeakMap)

@@ -580,0 +580,0 @@ reply.send({ hello: 'world' })

@@ -15,3 +15,4 @@ 'use strict'

kReplyIsError,
kReplySerializerDefault
kReplySerializerDefault,
kRouteContext
} = require('../../lib/symbols')

@@ -38,3 +39,3 @@ const fs = require('fs')

const context = {}
const request = { context }
const request = { [kRouteContext]: context }
const reply = new Reply(response, request)

@@ -52,3 +53,3 @@ t.equal(typeof reply, 'object')

t.same(reply.raw, response)
t.equal(reply.context, context)
t.equal(reply[kRouteContext], context)
t.equal(reply.request, request)

@@ -82,3 +83,3 @@ })

const reply = new Reply(response, { context: { onSend: null } }, log)
const reply = new Reply(response, { [kRouteContext]: { onSend: null } }, log)
reply.send(payload)

@@ -100,3 +101,3 @@ payload.destroy(new Error('stream error'))

}
const reply = new Reply(response, { context: { onSend: [] } })
const reply = new Reply(response, { [kRouteContext]: { onSend: [] } })
t.throws(() => {

@@ -119,3 +120,3 @@ const obj = {}

}
const reply = new Reply(response, { context: { onSend: [] } })
const reply = new Reply(response, { [kRouteContext]: { onSend: [] } })
t.equal(reply.send('hello'), reply)

@@ -161,3 +162,3 @@ })

const context = {}
const reply = new Reply(response, { context })
const reply = new Reply(response, { [kRouteContext]: context })
t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')

@@ -171,3 +172,3 @@ })

const context = {}
const reply = new Reply(response, { context })
const reply = new Reply(response, { [kRouteContext]: context })
reply.serializer((x) => (customSerializerCalled = true) && JSON.stringify(x))

@@ -183,3 +184,3 @@ t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')

const context = { [kReplySerializerDefault]: (x) => (customSerializerCalled = true) && JSON.stringify(x) }
const reply = new Reply(response, { context })
const reply = new Reply(response, { [kRouteContext]: context })
t.equal(reply.serialize({ foo: 'bar' }), '{"foo":"bar"}')

@@ -186,0 +187,0 @@ t.equal(customSerializerCalled, true, 'custom serializer not called')

@@ -5,3 +5,3 @@ 'use strict'

const Ajv = require('ajv')
const { kRequestValidateWeakMap } = require('../../lib/symbols')
const { kRequestValidateWeakMap, kRouteContext } = require('../../lib/symbols')
const Fastify = require('../../fastify')

@@ -40,3 +40,3 @@

test('#compileValidationSchema', subtest => {
subtest.plan(5)
subtest.plan(7)

@@ -64,2 +64,45 @@ subtest.test('Should return a function - Route without schema', async t => {

subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
const fastify = Fastify()
t.plan(3)
fastify.get('/', (req, reply) => {
const validate = req.compileValidationSchema(defaultSchema)
t.ok(validate({ hello: 'world' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.equal(validate.errors, null)
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
const fastify = Fastify()
t.plan(4)
fastify.get('/', (req, reply) => {
const validate = req.compileValidationSchema(defaultSchema)
t.notOk(validate({ world: 'foo' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.ok(Array.isArray(validate.errors))
t.ok(validate.errors.length > 0)
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
subtest.test(

@@ -194,7 +237,7 @@ 'Should reuse the validate fn across multiple invocations - Route without schema',

fastify.get('/', (req, reply) => {
t.equal(req.context[kRequestValidateWeakMap], null)
t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
t.type(req.compileValidationSchema(defaultSchema), Function)
t.type(req.context[kRequestValidateWeakMap], WeakMap)
t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)
t.type(req.compileValidationSchema(Object.assign({}, defaultSchema)), Function)
t.type(req.context[kRequestValidateWeakMap], WeakMap)
t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)

@@ -213,3 +256,3 @@ reply.send({ hello: 'world' })

test('#getValidationFunction', subtest => {
subtest.plan(4)
subtest.plan(6)

@@ -236,2 +279,47 @@ subtest.test('Should return a validation function', async t => {

subtest.test('Validate function errors property should be null after validation when input is valid', async t => {
const fastify = Fastify()
t.plan(3)
fastify.get('/', (req, reply) => {
req.compileValidationSchema(defaultSchema)
const validate = req.getValidationFunction(defaultSchema)
t.ok(validate({ hello: 'world' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.equal(validate.errors, null)
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
subtest.test('Validate function errors property should be an array of errors after validation when input is valid', async t => {
const fastify = Fastify()
t.plan(4)
fastify.get('/', (req, reply) => {
req.compileValidationSchema(defaultSchema)
const validate = req.getValidationFunction(defaultSchema)
t.notOk(validate({ world: 'foo' }))
t.ok(Object.prototype.hasOwnProperty.call(validate, 'errors'))
t.ok(Array.isArray(validate.errors))
t.ok(validate.errors.length > 0)
reply.send({ hello: 'world' })
})
await fastify.inject({
path: '/',
method: 'GET'
})
})
subtest.test('Should return undefined if no schema compiled', async t => {

@@ -345,3 +433,3 @@ const fastify = Fastify()

t.equal(req.context[kRequestValidateWeakMap], null)
t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
reply.send({ hello: 'world' })

@@ -646,5 +734,5 @@ })

fastify.get('/', (req, reply) => {
t.equal(req.context[kRequestValidateWeakMap], null)
t.equal(req[kRouteContext][kRequestValidateWeakMap], null)
t.equal(req.validateInput({ hello: 'world' }, defaultSchema), true)
t.type(req.context[kRequestValidateWeakMap], WeakMap)
t.type(req[kRouteContext][kRequestValidateWeakMap], WeakMap)

@@ -651,0 +739,0 @@ reply.send({ hello: 'world' })

@@ -6,2 +6,8 @@ 'use strict'

const Request = require('../../lib/request')
const Context = require('../../lib/context')
const {
kPublicRouteContext,
kReply,
kRequest
} = require('../../lib/symbols')

@@ -20,4 +26,24 @@ process.removeAllListeners('warning')

}
const context = new Context({
schema: {
body: {
type: 'object',
required: ['hello'],
properties: {
hello: { type: 'string' }
}
}
},
config: {
some: 'config',
url: req.url,
method: req.method
},
server: {
[kReply]: {},
[kRequest]: Request
}
})
req.connection = req.socket
const request = new Request('id', 'params', req, 'query', 'log')
const request = new Request('id', 'params', req, 'query', 'log', context)
t.type(request, Request)

@@ -41,2 +67,6 @@ t.type(request.validateInput, Function)

t.equal(request.protocol, 'http')
t.equal(request.routerPath, context.config.url)
t.equal(request.routerMethod, context.config.method)
t.equal(request.routeConfig, context[kPublicRouteContext].config)
t.equal(request.routeSchema, context[kPublicRouteContext].schema)

@@ -83,3 +113,3 @@ // This will be removed, it's deprecated

test('Request with trust proxy', t => {
t.plan(18)
t.plan(22)
const headers = {

@@ -95,5 +125,25 @@ 'x-forwarded-for': '2.2.2.2, 1.1.1.1',

}
const context = new Context({
schema: {
body: {
type: 'object',
required: ['hello'],
properties: {
hello: { type: 'string' }
}
}
},
config: {
some: 'config',
url: req.url,
method: req.method
},
server: {
[kReply]: {},
[kRequest]: Request
}
})
const TpRequest = Request.buildRequest(Request, true)
const request = new TpRequest('id', 'params', req, 'query', 'log')
const request = new TpRequest('id', 'params', req, 'query', 'log', context)
t.type(request, TpRequest)

@@ -117,2 +167,6 @@ t.equal(request.id, 'id')

t.type(request.compileValidationSchema, Function)
t.equal(request.routerPath, context.config.url)
t.equal(request.routerMethod, context.config.method)
t.equal(request.routeConfig, context[kPublicRouteContext].config)
t.equal(request.routeSchema, context[kPublicRouteContext].schema)
})

@@ -119,0 +173,0 @@

@@ -271,2 +271,17 @@ 'use strict'

test('build schema - headers are not lowercased in case of custom validator provided', t => {
t.plan(1)
class Headers {}
const opts = {
schema: {
headers: new Headers()
}
}
validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => {
t.type(schema, Headers)
return () => {}
}, true)
})
test('build schema - uppercased headers are not included', t => {

@@ -273,0 +288,0 @@ t.plan(1)

@@ -888,3 +888,3 @@ 'use strict'

test('pluginTimeout - named function', { only: true }, t => {
test('pluginTimeout - named function', t => {
t.plan(5)

@@ -891,0 +891,0 @@ const fastify = Fastify({

@@ -1467,1 +1467,30 @@ 'use strict'

})
test('exposeHeadRoute should not reuse the same route option', async t => {
t.plan(2)
const fastify = Fastify()
// we update the onRequest hook in onRoute hook
// if we reuse the same route option
// that means we will append another function inside the array
fastify.addHook('onRoute', function (routeOption) {
if (Array.isArray(routeOption.onRequest)) {
routeOption.onRequest.push(() => {})
} else {
routeOption.onRequest = [() => {}]
}
})
fastify.addHook('onRoute', function (routeOption) {
t.equal(routeOption.onRequest.length, 1)
})
fastify.route({
method: 'GET',
path: '/more-coffee',
async handler () {
return 'hello world'
}
})
})
'use strict'
const test = require('tap').test
const sget = require('simple-get')
const Fastify = require('../')
const { FST_ERR_BAD_URL } = require('../lib/errors')
function getUrl (app) {
const { address, port } = app.server.address()
if (address === '::1') {
return `http://[${address}]:${port}`
} else {
return `http://${address}:${port}`
}
}
test('Should honor ignoreTrailingSlash option', t => {
test('Should honor ignoreTrailingSlash option', async t => {
t.plan(4)

@@ -27,23 +17,12 @@ const fastify = Fastify({

fastify.listen({ port: 0 }, (err) => {
t.teardown(() => { fastify.close() })
if (err) t.threw(err)
let res = await fastify.inject('/test')
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), 'test')
const baseUrl = getUrl(fastify)
sget.concat(baseUrl + '/test', (err, res, data) => {
if (err) t.threw(err)
t.equal(res.statusCode, 200)
t.equal(data.toString(), 'test')
})
sget.concat(baseUrl + '/test/', (err, res, data) => {
if (err) t.threw(err)
t.equal(res.statusCode, 200)
t.equal(data.toString(), 'test')
})
})
res = await fastify.inject('/test/')
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), 'test')
})
test('Should honor ignoreDuplicateSlashes option', t => {
test('Should honor ignoreDuplicateSlashes option', async t => {
t.plan(4)

@@ -58,23 +37,12 @@ const fastify = Fastify({

fastify.listen({ port: 0 }, (err) => {
t.teardown(() => { fastify.close() })
if (err) t.threw(err)
let res = await fastify.inject('/test/test/test')
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), 'test')
const baseUrl = getUrl(fastify)
sget.concat(baseUrl + '/test/test/test', (err, res, data) => {
if (err) t.threw(err)
t.equal(res.statusCode, 200)
t.equal(data.toString(), 'test')
})
sget.concat(baseUrl + '/test//test///test', (err, res, data) => {
if (err) t.threw(err)
t.equal(res.statusCode, 200)
t.equal(data.toString(), 'test')
})
})
res = await fastify.inject('/test//test///test')
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), 'test')
})
test('Should honor ignoreTrailingSlash and ignoreDuplicateSlashes options', t => {
test('Should honor ignoreTrailingSlash and ignoreDuplicateSlashes options', async t => {
t.plan(4)

@@ -90,20 +58,9 @@ const fastify = Fastify({

fastify.listen({ port: 0 }, (err) => {
t.teardown(() => { fastify.close() })
if (err) t.threw(err)
let res = await fastify.inject('/test/test/test/')
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), 'test')
const baseUrl = getUrl(fastify)
sget.concat(baseUrl + '/test/test/test/', (err, res, data) => {
if (err) t.threw(err)
t.equal(res.statusCode, 200)
t.equal(data.toString(), 'test')
})
sget.concat(baseUrl + '/test//test///test//', (err, res, data) => {
if (err) t.threw(err)
t.equal(res.statusCode, 200)
t.equal(data.toString(), 'test')
})
})
res = await fastify.inject('/test//test///test//')
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), 'test')
})

@@ -137,8 +94,18 @@

test('Should expose router options via getters on request and reply', t => {
t.plan(7)
t.plan(10)
const fastify = Fastify()
const expectedSchema = {
params: {
id: { type: 'integer' }
}
}
fastify.get('/test/:id', (req, reply) => {
fastify.get('/test/:id', {
schema: expectedSchema
}, (req, reply) => {
t.equal(reply.context.config.url, '/test/:id')
t.equal(reply.context.config.method, 'GET')
t.equal(req.routeConfig.url, '/test/:id')
t.equal(req.routeConfig.method, 'GET')
t.same(req.routeSchema, expectedSchema)
t.equal(req.routerPath, '/test/:id')

@@ -145,0 +112,0 @@ t.equal(req.routerMethod, 'GET')

@@ -1782,1 +1782,26 @@ 'use strict'

})
test('setSchemaController: custom validator instance should not mutate headers schema', async t => {
t.plan(2)
class Headers {}
const fastify = Fastify()
fastify.setSchemaController({
compilersFactory: {
buildValidator: function () {
return ({ schema, method, url, httpPart }) => {
t.type(schema, Headers)
return () => {}
}
}
}
})
fastify.get('/', {
schema: {
headers: new Headers()
}
}, () => {})
await fastify.ready()
})

@@ -1020,1 +1020,20 @@ 'use strict'

})
test('Custom validator compiler should not mutate schema', async t => {
t.plan(2)
class Headers {}
const fastify = Fastify()
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
t.type(schema, Headers)
return () => {}
})
fastify.get('/', {
schema: {
headers: new Headers()
}
}, () => {})
await fastify.ready()
})
'use strict'
const t = require('tap')
const sget = require('simple-get').concat
const test = t.test

@@ -8,10 +9,8 @@ const fastify = require('..')()

const schema = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}

@@ -24,9 +23,7 @@ }

const querySchema = {
schema: {
querystring: {
type: 'object',
properties: {
hello: {
type: 'integer'
}
querystring: {
type: 'object',
properties: {
hello: {
type: 'integer'
}

@@ -38,12 +35,10 @@ }

const paramsSchema = {
schema: {
params: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
params: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}

@@ -54,3 +49,17 @@ }

test('shorthand - search', t => {
const bodySchema = {
body: {
type: 'object',
properties: {
foo: {
type: 'string'
},
test: {
type: 'integer'
}
}
}
}
test('search', t => {
t.plan(1)

@@ -72,3 +81,3 @@ try {

test('shorthand - search params', t => {
test('search, params schema', t => {
t.plan(1)

@@ -79,3 +88,3 @@ try {

url: '/params/:foo/:test',
paramsSchema,
schema: paramsSchema,
handler: function (request, reply) {

@@ -91,3 +100,3 @@ reply.code(200).send(request.params)

test('shorthand - get, querystring schema', t => {
test('search, querystring schema', t => {
t.plan(1)

@@ -98,3 +107,3 @@ try {

url: '/query',
querySchema,
schema: querySchema,
handler: function (request, reply) {

@@ -109,1 +118,131 @@ reply.code(200).send(request.query)

})
test('search, body schema', t => {
t.plan(1)
try {
fastify.route({
method: 'SEARCH',
url: '/body',
schema: bodySchema,
handler: function (request, reply) {
reply.code(200).send(request.body)
}
})
t.pass()
} catch (e) {
t.fail()
}
})
fastify.listen({ port: 0 }, err => {
t.error(err)
t.teardown(() => { fastify.close() })
const url = `http://localhost:${fastify.server.address().port}`
test('request - search', t => {
t.plan(4)
sget({
method: 'SEARCH',
url
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 'world' })
})
})
test('request search params schema', t => {
t.plan(4)
sget({
method: 'SEARCH',
url: `${url}/params/world/123`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { foo: 'world', test: 123 })
})
})
test('request search params schema error', t => {
t.plan(3)
sget({
method: 'SEARCH',
url: `${url}/params/world/string`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
message: 'params/test must be integer',
statusCode: 400
})
})
})
test('request search querystring schema', t => {
t.plan(4)
sget({
method: 'SEARCH',
url: `${url}/query?hello=123`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), { hello: 123 })
})
})
test('request search querystring schema error', t => {
t.plan(3)
sget({
method: 'SEARCH',
url: `${url}/query?hello=world`
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.same(JSON.parse(body), {
error: 'Bad Request',
message: 'querystring/hello must be integer',
statusCode: 400
})
})
})
test('request search body schema', t => {
t.plan(4)
const replyBody = { foo: 'bar', test: 5 }
sget({
method: 'SEARCH',
url: `${url}/body`,
body: JSON.stringify(replyBody),
headers: { 'content-type': 'application/json' }
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), replyBody)
})
})
test('request search body schema error', t => {
t.plan(4)
sget({
method: 'SEARCH',
url: `${url}/body`,
body: JSON.stringify({ foo: 'bar', test: 'test' }),
headers: { 'content-type': 'application/json' }
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
t.equal(response.headers['content-length'], '' + body.length)
t.same(JSON.parse(body), {
error: 'Bad Request',
message: 'body/test must be integer',
statusCode: 400
})
})
})
})

@@ -0,1 +1,2 @@

import { ErrorObject } from '@fastify/ajv-compiler'
import { FastifyBaseLogger } from './logger'

@@ -17,2 +18,7 @@ import { ContextConfigDefault, RawServerBase, RawServerDefault, RawRequestDefaultExpression, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './utils'

export interface ValidationFunction {
(input: any): boolean
errors?: null | ErrorObject[];
}
/**

@@ -65,5 +71,5 @@ * FastifyRequest is an instance of the standard http or http2 request objects.

getValidationFunction(httpPart: HTTPRequestPart): (input: any) => boolean
getValidationFunction(schema: {[key: string]: any}): (input: any) => boolean
compileValidationSchema(schema: {[key: string]: any}, httpPart?: HTTPRequestPart): (input: any) => boolean
getValidationFunction(httpPart: HTTPRequestPart): ValidationFunction
getValidationFunction(schema: {[key: string]: any}): ValidationFunction
compileValidationSchema(schema: {[key: string]: any}, httpPart?: HTTPRequestPart): ValidationFunction
validateInput(input: any, schema: {[key: string]: any}, httpPart?: HTTPRequestPart): boolean

@@ -70,0 +76,0 @@ validateInput(input: any, httpPart?: HTTPRequestPart): boolean

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc