fastify 3.25.2 → 3.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +1 -1
  2. package/build/build-validation.js +2 -0
  3. package/docs/Guides/Index.md +2 -0
  4. package/docs/Guides/Prototype-Poisoning.md +391 -0
  5. package/docs/Guides/Recommendations.md +1 -1
  6. package/docs/Reference/Plugins.md +4 -4
  7. package/docs/Reference/Request.md +3 -0
  8. package/docs/Reference/Server.md +16 -2
  9. package/docs/Reference/TypeScript.md +1 -1
  10. package/fastify.d.ts +2 -1
  11. package/fastify.js +23 -5
  12. package/lib/errors.js +5 -0
  13. package/lib/noop-set.js +10 -0
  14. package/lib/pluginUtils.js +5 -0
  15. package/lib/reply.js +16 -6
  16. package/lib/route.js +34 -1
  17. package/lib/symbols.js +3 -1
  18. package/lib/warnings.js +1 -1
  19. package/package.json +7 -7
  20. package/test/404s.test.js +1 -1
  21. package/test/bundler/esbuild/bundler-test.js +31 -0
  22. package/test/bundler/esbuild/package.json +10 -0
  23. package/test/bundler/esbuild/src/fail-plugin-version.js +12 -0
  24. package/test/bundler/esbuild/src/index.js +7 -0
  25. package/test/bundler/webpack/bundler-test.js +15 -4
  26. package/test/bundler/webpack/src/fail-plugin-version.js +1 -3
  27. package/test/bundler/webpack/src/index.js +1 -3
  28. package/test/close.test.js +38 -0
  29. package/test/custom-parser.test.js +30 -31
  30. package/test/noop-set.test.js +19 -0
  31. package/test/plugin.name.display.js +10 -0
  32. package/test/plugin.test.js +18 -0
  33. package/test/route.test.js +12 -0
  34. package/test/schema-serialization.test.js +41 -0
  35. package/test/types/fastify.test-d.ts +17 -0
  36. package/test/types/hooks.test-d.ts +28 -4
  37. package/test/types/instance.test-d.ts +25 -0
  38. package/test/versioned-routes.test.js +27 -3
  39. package/types/hooks.d.ts +19 -19
  40. package/types/instance.d.ts +13 -1
  41. package/types/schema.d.ts +14 -0
@@ -0,0 +1,10 @@
1
+ 'use strict'
2
+
3
+ module.exports = function noopSet () {
4
+ return {
5
+ [Symbol.iterator]: function * () {},
6
+ add () {},
7
+ delete () {},
8
+ has () { return true }
9
+ }
10
+ }
@@ -14,6 +14,11 @@ function getMeta (fn) {
14
14
  }
15
15
 
16
16
  function getPluginName (func) {
17
+ const display = getDisplayName(func)
18
+ if (display) {
19
+ return display
20
+ }
21
+
17
22
  // let's see if this is a file, and in that case use that
18
23
  // this is common for plugins
19
24
  const cache = require.cache
package/lib/reply.js CHANGED
@@ -365,12 +365,18 @@ function preserializeHookEnd (err, request, reply, payload) {
365
365
  return
366
366
  }
367
367
 
368
- if (reply[kReplySerializer] !== null) {
369
- payload = reply[kReplySerializer](payload)
370
- } else if (reply.context && reply.context[kReplySerializerDefault]) {
371
- payload = reply.context[kReplySerializerDefault](payload, reply.raw.statusCode)
372
- } else {
373
- payload = serialize(reply.context, payload, reply.raw.statusCode)
368
+ try {
369
+ if (reply[kReplySerializer] !== null) {
370
+ payload = reply[kReplySerializer](payload)
371
+ } else if (reply.context && reply.context[kReplySerializerDefault]) {
372
+ payload = reply.context[kReplySerializerDefault](payload, reply.raw.statusCode)
373
+ } else {
374
+ payload = serialize(reply.context, payload, reply.raw.statusCode)
375
+ }
376
+ } catch (e) {
377
+ wrapSeralizationError(e, reply)
378
+ onErrorHook(reply, e)
379
+ return
374
380
  }
375
381
 
376
382
  flatstr(payload)
@@ -378,6 +384,10 @@ function preserializeHookEnd (err, request, reply, payload) {
378
384
  onSendHook(reply, payload)
379
385
  }
380
386
 
387
+ function wrapSeralizationError (error, reply) {
388
+ error.serialization = reply.context.config
389
+ }
390
+
381
391
  function onSendHook (reply, payload) {
382
392
  if (reply.context.onSend !== null) {
383
393
  reply[kReplySent] = true
package/lib/route.js CHANGED
@@ -8,6 +8,7 @@ const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTI
8
8
  const { normalizeSchema } = require('./schemas')
9
9
  const { parseHeadOnSendHandlers } = require('./headRoute')
10
10
  const warning = require('./warnings')
11
+ const { kRequestAcceptVersion } = require('./symbols')
11
12
 
12
13
  const {
13
14
  compileSchemasForValidation,
@@ -17,7 +18,8 @@ const {
17
18
  const {
18
19
  FST_ERR_SCH_VALIDATION_BUILD,
19
20
  FST_ERR_SCH_SERIALIZATION_BUILD,
20
- FST_ERR_DEFAULT_ROUTE_INVALID_TYPE
21
+ FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
22
+ FST_ERR_INVALID_URL
21
23
  } = require('./errors')
22
24
 
23
25
  const {
@@ -40,6 +42,7 @@ const {
40
42
  } = require('./symbols.js')
41
43
 
42
44
  function buildRouting (options) {
45
+ const { keepAliveConnections } = options
43
46
  const router = FindMyWay(options.config)
44
47
 
45
48
  let avvio
@@ -97,6 +100,10 @@ function buildRouting (options) {
97
100
 
98
101
  // Convert shorthand to extended route declaration
99
102
  function prepareRoute (method, url, options, handler) {
103
+ if (typeof url !== 'string') {
104
+ throw new FST_ERR_INVALID_URL(typeof url)
105
+ }
106
+
100
107
  if (!handler && typeof options === 'function') {
101
108
  handler = options // for support over direct function calls such as fastify.get() options are reused as the handler
102
109
  options = {}
@@ -344,6 +351,23 @@ function buildRouting (options) {
344
351
  }
345
352
  }
346
353
 
354
+ // When server.forceCloseConnections is true, we will collect any requests
355
+ // that have indicated they want persistence so that they can be reaped
356
+ // on server close. Otherwise, the container is a noop container.
357
+ const connHeader = String.prototype.toLowerCase.call(req.headers.connection || '')
358
+ if (connHeader === 'keep-alive') {
359
+ if (keepAliveConnections.has(req.socket) === false) {
360
+ keepAliveConnections.add(req.socket)
361
+ req.socket.on('close', removeTrackedSocket.bind({ keepAliveConnections, socket: req.socket }))
362
+ }
363
+ }
364
+
365
+ // we revert the changes in defaultRoute
366
+ if (req.headers[kRequestAcceptVersion] !== undefined) {
367
+ req.headers['accept-version'] = req.headers[kRequestAcceptVersion]
368
+ req.headers[kRequestAcceptVersion] = undefined
369
+ }
370
+
347
371
  const id = req.headers[requestIdHeader] || genReqId(req)
348
372
 
349
373
  const loggerBinding = {
@@ -478,6 +502,15 @@ function preParsingHookRunner (functions, request, reply, cb) {
478
502
  next(null, request[kRequestPayloadStream])
479
503
  }
480
504
 
505
+ /**
506
+ * Used within the route handler as a `net.Socket.close` event handler.
507
+ * The purpose is to remove a socket from the tracked sockets collection when
508
+ * the socket has naturally timed out.
509
+ */
510
+ function removeTrackedSocket () {
511
+ this.keepAliveConnections.delete(this.socket)
512
+ }
513
+
481
514
  function noop () { }
482
515
 
483
516
  module.exports = { buildRouting, validateBodyLimitOption }
package/lib/symbols.js CHANGED
@@ -21,6 +21,7 @@ const keys = {
21
21
  kReply: Symbol('fastify.Reply'),
22
22
  kRequest: Symbol('fastify.Request'),
23
23
  kRequestPayloadStream: Symbol('fastify.RequestPayloadStream'),
24
+ kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'),
24
25
  kCanSetNotFoundHandler: Symbol('fastify.canSetNotFoundHandler'),
25
26
  kFourOhFour: Symbol('fastify.404'),
26
27
  kFourOhFourLevelInstance: Symbol('fastify.404LogLevelInstance'),
@@ -43,7 +44,8 @@ const keys = {
43
44
  kPluginNameChain: Symbol('fastify.pluginNameChain'),
44
45
  // This symbol is only meant to be used for fastify tests and should not be used for any other purpose
45
46
  kTestInternals: Symbol('fastify.testInternals'),
46
- kErrorHandler: Symbol('fastify.errorHandler')
47
+ kErrorHandler: Symbol('fastify.errorHandler'),
48
+ kKeepAliveConnections: Symbol('fastify.keepAliveConnections')
47
49
  }
48
50
 
49
51
  module.exports = keys
package/lib/warnings.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const warning = require('fastify-warning')()
3
+ const warning = require('process-warning')()
4
4
 
5
5
  /**
6
6
  * Deprecation codes:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "3.25.2",
3
+ "version": "3.27.1",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -16,7 +16,7 @@
16
16
  "lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
17
17
  "prepublishOnly": "tap --no-check-coverage test/internals/version.test.js",
18
18
  "test": "npm run lint && npm run unit && npm run test:typescript",
19
- "test:ci": "npm run lint && npm run unit -- --cov --coverage-report=lcovonly && npm run test:typescript",
19
+ "test:ci": "npm run unit -- -R terse --cov --coverage-report=lcovonly && npm run test:typescript",
20
20
  "test:report": "npm run lint && npm run unit:report && npm run test:typescript",
21
21
  "test:typescript": "tsd",
22
22
  "unit": "tap -J test/*.test.js test/*/*.test.js",
@@ -129,6 +129,7 @@
129
129
  "@types/pino": "^6.0.1",
130
130
  "@typescript-eslint/eslint-plugin": "^4.5.0",
131
131
  "@typescript-eslint/parser": "^4.5.0",
132
+ "JSONStream": "^1.3.5",
132
133
  "ajv": "^6.0.0",
133
134
  "ajv-errors": "^1.0.1",
134
135
  "ajv-formats": "^2.1.1",
@@ -152,12 +153,11 @@
152
153
  "form-data": "^4.0.0",
153
154
  "frameguard": "^4.0.0",
154
155
  "h2url": "^0.2.0",
155
- "helmet": "^4.0.0",
156
+ "helmet": "^5.0.1",
156
157
  "hide-powered-by": "^1.1.0",
157
158
  "hsts": "^2.2.0",
158
- "http-errors": "^1.7.1",
159
+ "http-errors": "^2.0.0",
159
160
  "ienoopen": "^1.1.0",
160
- "JSONStream": "^1.3.5",
161
161
  "license-checker": "^25.0.1",
162
162
  "pem": "^1.14.4",
163
163
  "proxyquire": "^2.1.3",
@@ -173,7 +173,7 @@
173
173
  "then-sleep": "^1.0.1",
174
174
  "tsd": "^0.19.0",
175
175
  "typescript": "^4.0.2",
176
- "undici": "^3.3.5",
176
+ "undici": "^3.3.6",
177
177
  "x-xss-protection": "^2.0.0",
178
178
  "yup": "^0.32.0"
179
179
  },
@@ -183,7 +183,7 @@
183
183
  "avvio": "^7.1.2",
184
184
  "fast-json-stringify": "^2.5.2",
185
185
  "fastify-error": "^0.3.0",
186
- "fastify-warning": "^0.2.0",
186
+ "process-warning": "^1.0.0",
187
187
  "find-my-way": "^4.5.0",
188
188
  "flatstr": "^1.0.12",
189
189
  "light-my-request": "^4.2.0",
package/test/404s.test.js CHANGED
@@ -1221,7 +1221,7 @@ test('preHandler option for setNotFoundHandler', t => {
1221
1221
  })
1222
1222
 
1223
1223
  // https://github.com/fastify/fastify/issues/2229
1224
- t.test('preHandler hook in setNotFoundHandler should be called when callNotFound', t => {
1224
+ t.test('preHandler hook in setNotFoundHandler should be called when callNotFound', { timeout: 40000 }, t => {
1225
1225
  t.plan(2)
1226
1226
  const fastify = Fastify()
1227
1227
 
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const fastifySuccess = require('./dist/success')
6
+ const fastifyFailPlugin = require('./dist/failPlugin')
7
+
8
+ test('Bundled package should work', (t) => {
9
+ t.plan(4)
10
+ fastifySuccess.ready((err) => {
11
+ t.error(err)
12
+ fastifySuccess.inject(
13
+ {
14
+ method: 'GET',
15
+ url: '/'
16
+ },
17
+ (error, res) => {
18
+ t.error(error)
19
+ t.equal(res.statusCode, 200)
20
+ t.same(res.json(), { hello: 'world' })
21
+ }
22
+ )
23
+ })
24
+ })
25
+
26
+ test('Bundled package should not work with bad plugin version', (t) => {
27
+ t.plan(1)
28
+ fastifyFailPlugin.ready((err) => {
29
+ t.match(err.message, /expected '9.x' fastify version/i)
30
+ })
31
+ })
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": "0.0.1",
3
+ "scripts": {
4
+ "bundle": "esbuild success=src/index.js failPlugin=src/fail-plugin-version.js --bundle --outdir=dist --platform=node",
5
+ "test": "npm run bundle && node bundler-test.js"
6
+ },
7
+ "devDependencies": {
8
+ "esbuild": "^0.14.11"
9
+ }
10
+ }
@@ -0,0 +1,12 @@
1
+ const fp = require('fastify-plugin')
2
+ const fastify = require('../../../../')()
3
+
4
+ fastify.get('/', function (request, reply) {
5
+ reply.send({ hello: 'world' })
6
+ })
7
+
8
+ fastify.register(fp((instance, opts, done) => {
9
+ done()
10
+ }, { fastify: '9.x' }))
11
+
12
+ module.exports = fastify
@@ -0,0 +1,7 @@
1
+ const fastify = require('../../../../')()
2
+ // Declare a route
3
+ fastify.get('/', function (request, reply) {
4
+ reply.send({ hello: 'world' })
5
+ })
6
+
7
+ module.exports = fastify
@@ -5,16 +5,27 @@ const test = t.test
5
5
  const fastifySuccess = require('./dist/success')
6
6
  const fastifyFailPlugin = require('./dist/failPlugin')
7
7
 
8
- test('Bundled package should work', t => {
9
- t.plan(1)
8
+ test('Bundled package should work', (t) => {
9
+ t.plan(4)
10
10
  fastifySuccess.ready((err) => {
11
11
  t.error(err)
12
+ fastifySuccess.inject(
13
+ {
14
+ method: 'GET',
15
+ url: '/'
16
+ },
17
+ (error, res) => {
18
+ t.error(error)
19
+ t.equal(res.statusCode, 200)
20
+ t.same(res.json(), { hello: 'world' })
21
+ }
22
+ )
12
23
  })
13
24
  })
14
25
 
15
- test('Bundled package should not work with bad plugin version', t => {
26
+ test('Bundled package should not work with bad plugin version', (t) => {
16
27
  t.plan(1)
17
28
  fastifyFailPlugin.ready((err) => {
18
- t.ok(err)
29
+ t.match(err.message, /expected '9.x' fastify version/i)
19
30
  })
20
31
  })
@@ -1,7 +1,5 @@
1
1
  const fp = require('fastify-plugin')
2
- const fastify = require('../../../../')({
3
- logger: true
4
- })
2
+ const fastify = require('../../../../')()
5
3
 
6
4
  fastify.get('/', function (request, reply) {
7
5
  reply.send({ hello: 'world' })
@@ -1,6 +1,4 @@
1
- const fastify = require('../../../../')({
2
- logger: true
3
- })
1
+ const fastify = require('../../../../')()
4
2
  // Declare a route
5
3
  fastify.get('/', function (request, reply) {
6
4
  reply.send({ hello: 'world' })
@@ -291,3 +291,41 @@ test('Cannot be reopened the closed server has listen callback', async t => {
291
291
  t.ok(err)
292
292
  })
293
293
  })
294
+
295
+ test('shutsdown while keep-alive connections are active (non-async)', t => {
296
+ t.plan(5)
297
+
298
+ const timeoutTime = 2 * 60 * 1000
299
+ const fastify = Fastify({ forceCloseConnections: true })
300
+
301
+ fastify.server.setTimeout(timeoutTime)
302
+ fastify.server.keepAliveTimeout = timeoutTime
303
+
304
+ fastify.get('/', (req, reply) => {
305
+ reply.send({ hello: 'world' })
306
+ })
307
+
308
+ fastify.listen(0, (err, address) => {
309
+ t.error(err)
310
+
311
+ const client = new Client(
312
+ 'http://localhost:' + fastify.server.address().port,
313
+ { keepAliveTimeout: 1 * 60 * 1000 }
314
+ )
315
+ client.request({ path: '/', method: 'GET' }, (err, response) => {
316
+ t.error(err)
317
+ t.equal(client.closed, false)
318
+
319
+ fastify.close((err) => {
320
+ t.error(err)
321
+
322
+ // Due to the nature of the way we reap these keep-alive connections,
323
+ // there hasn't been enough time before the server fully closed in order
324
+ // for the client to have seen the socket get destroyed. The mere fact
325
+ // that we have reached this callback is enough indication that the
326
+ // feature being tested works as designed.
327
+ t.equal(client.closed, false)
328
+ })
329
+ })
330
+ })
331
+ })
@@ -1314,9 +1314,10 @@ test('contentTypeParser should add a custom parser with RegExp value', t => {
1314
1314
  })
1315
1315
  })
1316
1316
 
1317
- test('contentTypeParser should add multiple custom parsers with RegExp values', t => {
1318
- t.plan(10)
1317
+ test('contentTypeParser should add multiple custom parsers with RegExp values', async t => {
1318
+ t.plan(6)
1319
1319
  const fastify = Fastify()
1320
+ t.teardown(fastify.close.bind(fastify))
1320
1321
 
1321
1322
  fastify.post('/', (req, reply) => {
1322
1323
  reply.send(req.body)
@@ -1340,48 +1341,46 @@ test('contentTypeParser should add multiple custom parsers with RegExp values',
1340
1341
  })
1341
1342
  })
1342
1343
 
1343
- fastify.listen(0, err => {
1344
- t.error(err)
1344
+ await fastify.ready()
1345
1345
 
1346
- sget({
1346
+ {
1347
+ const response = await fastify.inject({
1347
1348
  method: 'POST',
1348
- url: 'http://localhost:' + fastify.server.address().port,
1349
- body: '{"hello":"world"}',
1349
+ path: '/',
1350
+ payload: '{"hello":"world"}',
1350
1351
  headers: {
1351
1352
  'Content-Type': 'application/vnd.hello+json'
1352
1353
  }
1353
- }, (err, response, body) => {
1354
- t.error(err)
1355
- t.equal(response.statusCode, 200)
1356
- t.same(body.toString(), JSON.stringify({ hello: 'world' }))
1357
1354
  })
1355
+ t.equal(response.statusCode, 200)
1356
+ t.same(response.payload.toString(), '{"hello":"world"}')
1357
+ }
1358
1358
 
1359
- sget({
1359
+ {
1360
+ const response = await fastify.inject({
1360
1361
  method: 'POST',
1361
- url: 'http://localhost:' + fastify.server.address().port,
1362
- body: '{"hello":"world"}',
1362
+ path: '/',
1363
+ payload: '{"hello":"world"}',
1363
1364
  headers: {
1364
1365
  'Content-Type': 'application/test+xml'
1365
1366
  }
1366
- }, (err, response, body) => {
1367
- t.error(err)
1368
- t.equal(response.statusCode, 200)
1369
- t.same(body.toString(), 'xml')
1370
1367
  })
1368
+ t.equal(response.statusCode, 200)
1369
+ t.same(response.payload.toString(), 'xml')
1370
+ }
1371
1371
 
1372
- sget({
1373
- method: 'POST',
1374
- url: 'http://localhost:' + fastify.server.address().port,
1375
- body: 'abcdefg',
1376
- headers: {
1377
- 'Content-Type': 'application/+myExtension'
1378
- }
1379
- }, (err, response, body) => {
1380
- t.error(err)
1381
- t.equal(response.statusCode, 200)
1382
- t.same(body.toString(), 'abcdefgmyExtension')
1383
- fastify.close()
1384
- })
1372
+ await fastify.inject({
1373
+ method: 'POST',
1374
+ path: '/',
1375
+ payload: 'abcdefg',
1376
+ headers: {
1377
+ 'Content-Type': 'application/+myExtension'
1378
+ }
1379
+ }).then((response) => {
1380
+ t.equal(response.statusCode, 200)
1381
+ t.same(response.payload.toString(), 'abcdefgmyExtension')
1382
+ }).catch((err) => {
1383
+ t.error(err)
1385
1384
  })
1386
1385
  })
1387
1386
 
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+
3
+ const tap = require('tap')
4
+ const noopSet = require('../lib/noop-set')
5
+
6
+ tap.test('does a lot of nothing', async t => {
7
+ const aSet = noopSet()
8
+ t.type(aSet, 'object')
9
+
10
+ const item = {}
11
+ aSet.add(item)
12
+ aSet.add({ another: 'item' })
13
+ aSet.delete(item)
14
+ t.equal(aSet.has(item), true)
15
+
16
+ for (const i of aSet) {
17
+ t.fail('should not have any items', i)
18
+ }
19
+ })
@@ -0,0 +1,10 @@
1
+ 'use strict'
2
+
3
+ const assert = require('assert')
4
+
5
+ module.exports = function (fastify, opts, done) {
6
+ assert.strictEqual(fastify.pluginName, 'test-plugin')
7
+ done()
8
+ }
9
+
10
+ module.exports[Symbol.for('fastify.display-name')] = 'test-plugin'
@@ -41,6 +41,24 @@ test('plugin metadata - ignore prefix', t => {
41
41
  }
42
42
  })
43
43
 
44
+ test('plugin metadata - naming plugins', async t => {
45
+ t.plan(2)
46
+ const fastify = Fastify()
47
+
48
+ fastify.register(require('./plugin.name.display'))
49
+ fastify.register(function (fastify, opts, done) {
50
+ // one line
51
+ t.equal(fastify.pluginName, 'function (fastify, opts, done) { -- // one line')
52
+ done()
53
+ })
54
+ fastify.register(function fooBar (fastify, opts, done) {
55
+ t.equal(fastify.pluginName, 'fooBar')
56
+ done()
57
+ })
58
+
59
+ await fastify.ready()
60
+ })
61
+
44
62
  test('fastify.register with fastify-plugin should not encapsulate his code', t => {
45
63
  t.plan(10)
46
64
  const fastify = Fastify()
@@ -8,6 +8,7 @@ const sget = require('simple-get').concat
8
8
  const joi = require('@hapi/joi')
9
9
  const Fastify = require('..')
10
10
  const proxyquire = require('proxyquire')
11
+ const { FST_ERR_INVALID_URL } = require('../lib/errors')
11
12
 
12
13
  test('route', t => {
13
14
  t.plan(9)
@@ -1268,3 +1269,14 @@ test('Correct error message is produced if "path" option is used', t => {
1268
1269
  })
1269
1270
  }, new Error('Error Handler for POST:/test route, if defined, must be a function'))
1270
1271
  })
1272
+
1273
+ test('invalid url attribute - non string URL', t => {
1274
+ t.plan(1)
1275
+ const fastify = Fastify()
1276
+
1277
+ try {
1278
+ fastify.get(/^\/(donations|skills|blogs)/, () => {})
1279
+ } catch (error) {
1280
+ t.equal(error.code, FST_ERR_INVALID_URL().code)
1281
+ }
1282
+ })
@@ -670,3 +670,44 @@ test('error in custom schema serialize compiler, throw FST_ERR_SCH_SERIALIZATION
670
670
  t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')
671
671
  })
672
672
  })
673
+
674
+ test('Errors in searilizer sended to errorHandler', async t => {
675
+ let savedError
676
+
677
+ const fastify = Fastify()
678
+ fastify.get('/', {
679
+ schema: {
680
+ response: {
681
+ 200: {
682
+ type: 'object',
683
+ properties: {
684
+ name: { type: 'string' },
685
+ power: { type: 'string' }
686
+ },
687
+ required: ['name']
688
+ }
689
+ }
690
+ }
691
+
692
+ }, function (req, reply) {
693
+ reply.code(200).send({ no: 'thing' })
694
+ })
695
+ fastify.setErrorHandler((error, request, reply) => {
696
+ savedError = error
697
+ reply.code(500).send(error)
698
+ })
699
+
700
+ const res = await fastify.inject('/')
701
+
702
+ t.equal(res.statusCode, 500)
703
+
704
+ // t.same(savedError, new Error('"name" is required!'));
705
+ t.same(res.json(), {
706
+ statusCode: 500,
707
+ error: 'Internal Server Error',
708
+ message: '"name" is required!'
709
+ })
710
+ t.ok(savedError, 'error presents')
711
+ t.ok(savedError.serialization, 'Serialization sign presents')
712
+ t.end()
713
+ })
@@ -57,6 +57,7 @@ fastify({ http2: true, https: {} }).inject({}, lightMyRequestCallback)
57
57
  expectAssignable<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true }))
58
58
  expectAssignable<FastifyInstance>(fastify({ ignoreTrailingSlash: true }))
59
59
  expectAssignable<FastifyInstance>(fastify({ connectionTimeout: 1000 }))
60
+ expectAssignable<FastifyInstance>(fastify({ forceCloseConnections: true }))
60
61
  expectAssignable<FastifyInstance>(fastify({ keepAliveTimeout: 1000 }))
61
62
  expectAssignable<FastifyInstance>(fastify({ pluginTimeout: 1000 }))
62
63
  expectAssignable<FastifyInstance>(fastify({ bodyLimit: 100 }))
@@ -178,6 +179,22 @@ expectAssignable<FastifyInstance>(fastify({
178
179
  plugins: [() => { }]
179
180
  }
180
181
  }))
182
+ expectAssignable<FastifyInstance>(fastify({
183
+ ajv: {
184
+ customOptions: {
185
+ nullable: false
186
+ },
187
+ plugins: [[() => { }, 'keyword']]
188
+ }
189
+ }))
190
+ expectAssignable<FastifyInstance>(fastify({
191
+ ajv: {
192
+ customOptions: {
193
+ nullable: false
194
+ },
195
+ plugins: [[() => { }, ['keyword1', 'keyword2']]]
196
+ }
197
+ }))
181
198
  expectAssignable<FastifyInstance>(fastify({ frameworkErrors: () => { } }))
182
199
  expectAssignable<FastifyInstance>(fastify({
183
200
  rewriteUrl: (req) => req.url === '/hi' ? '/hello' : req.url!