fastify 4.11.0 → 4.13.0

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.
@@ -256,27 +256,25 @@ test('Error thrown 415 from content type is null and make post request to server
256
256
 
257
257
  test('remove', t => {
258
258
  test('should remove default parser', t => {
259
- t.plan(2)
259
+ t.plan(3)
260
260
 
261
261
  const fastify = Fastify()
262
262
  const contentTypeParser = fastify[keys.kContentTypeParser]
263
263
 
264
- contentTypeParser.remove('application/json')
265
-
264
+ t.ok(contentTypeParser.remove('application/json'))
266
265
  t.notOk(contentTypeParser.customParsers['application/json'])
267
266
  t.notOk(contentTypeParser.parserList.find(parser => parser === 'application/json'))
268
267
  })
269
268
 
270
269
  test('should remove RegExp parser', t => {
271
- t.plan(2)
270
+ t.plan(3)
272
271
 
273
272
  const fastify = Fastify()
274
273
  fastify.addContentTypeParser(/^text\/*/, first)
275
274
 
276
275
  const contentTypeParser = fastify[keys.kContentTypeParser]
277
276
 
278
- contentTypeParser.remove(/^text\/*/)
279
-
277
+ t.ok(contentTypeParser.remove(/^text\/*/))
280
278
  t.notOk(contentTypeParser.customParsers[/^text\/*/])
281
279
  t.notOk(contentTypeParser.parserRegExpList.find(parser => parser.toString() === /^text\/*/.toString()))
282
280
  })
@@ -289,23 +287,22 @@ test('remove', t => {
289
287
  t.throws(() => fastify[keys.kContentTypeParser].remove(12), FST_ERR_CTP_INVALID_TYPE)
290
288
  })
291
289
 
292
- test('should not throw error if content type does not exist', t => {
290
+ test('should return false if content type does not exist', t => {
293
291
  t.plan(1)
294
292
 
295
293
  const fastify = Fastify()
296
294
 
297
- t.doesNotThrow(() => fastify[keys.kContentTypeParser].remove('image/png'))
295
+ t.notOk(fastify[keys.kContentTypeParser].remove('image/png'))
298
296
  })
299
297
 
300
298
  test('should not remove any content type parser if content type does not exist', t => {
301
- t.plan(1)
299
+ t.plan(2)
302
300
 
303
301
  const fastify = Fastify()
304
302
 
305
303
  const contentTypeParser = fastify[keys.kContentTypeParser]
306
304
 
307
- contentTypeParser.remove('image/png')
308
-
305
+ t.notOk(contentTypeParser.remove('image/png'))
309
306
  t.same(contentTypeParser.customParsers.size, 2)
310
307
  })
311
308
 
@@ -609,3 +606,46 @@ test('content-type fail when parameters not match - regexp', async t => {
609
606
 
610
607
  t.same(response.statusCode, 415)
611
608
  })
609
+
610
+ // Refs: https://github.com/fastify/fastify/issues/4495
611
+ test('content-type regexp list should be cloned when plugin override', async t => {
612
+ t.plan(6)
613
+
614
+ const fastify = Fastify()
615
+
616
+ fastify.addContentTypeParser(/^image\/.*/, { parseAs: 'buffer' }, (req, payload, done) => {
617
+ done(null, payload)
618
+ })
619
+
620
+ fastify.register(function plugin (fastify, options, done) {
621
+ fastify.post('/', function (request, reply) {
622
+ reply.type(request.headers['content-type']).send(request.body)
623
+ })
624
+
625
+ done()
626
+ })
627
+
628
+ {
629
+ const { payload, headers, statusCode } = await fastify.inject({
630
+ method: 'POST',
631
+ path: '/',
632
+ payload: 'jpeg',
633
+ headers: { 'content-type': 'image/jpeg' }
634
+ })
635
+ t.same(statusCode, 200)
636
+ t.same(headers['content-type'], 'image/jpeg')
637
+ t.same(payload, 'jpeg')
638
+ }
639
+
640
+ {
641
+ const { payload, headers, statusCode } = await fastify.inject({
642
+ method: 'POST',
643
+ path: '/',
644
+ payload: 'png',
645
+ headers: { 'content-type': 'image/png' }
646
+ })
647
+ t.same(statusCode, 200)
648
+ t.same(headers['content-type'], 'image/png')
649
+ t.same(payload, 'png')
650
+ }
651
+ })
@@ -372,3 +372,16 @@ t.test('ready return registered', t => {
372
372
  .then(instance => { t.same(instance, fastify) })
373
373
  .catch(err => { t.fail(err) })
374
374
  })
375
+
376
+ t.test('do not crash with error in follow up onReady hook', async t => {
377
+ const fastify = Fastify()
378
+
379
+ fastify.addHook('onReady', async function () {
380
+ })
381
+
382
+ fastify.addHook('onReady', function () {
383
+ throw new Error('kaboom')
384
+ })
385
+
386
+ await t.rejects(fastify.ready())
387
+ })
@@ -10,6 +10,7 @@ const fs = require('fs')
10
10
  const split = require('split2')
11
11
  const symbols = require('../lib/symbols.js')
12
12
  const payload = { hello: 'world' }
13
+ const proxyquire = require('proxyquire')
13
14
 
14
15
  process.removeAllListeners('warning')
15
16
 
@@ -1269,7 +1270,14 @@ test('clear payload', t => {
1269
1270
  })
1270
1271
 
1271
1272
  test('onSend hook throws', t => {
1272
- t.plan(9)
1273
+ t.plan(11)
1274
+ const Fastify = proxyquire('..', {
1275
+ './lib/schemas.js': {
1276
+ getSchemaSerializer: (param1, param2, param3) => {
1277
+ t.equal(param3, 'application/json; charset=utf-8', 'param3 should be "application/json; charset=utf-8"')
1278
+ }
1279
+ }
1280
+ })
1273
1281
  const fastify = Fastify()
1274
1282
  fastify.addHook('onSend', function (request, reply, payload, done) {
1275
1283
  if (request.raw.method === 'DELETE') {
@@ -1281,6 +1289,10 @@ test('onSend hook throws', t => {
1281
1289
  throw new Error('some error')
1282
1290
  }
1283
1291
 
1292
+ if (request.raw.method === 'POST') {
1293
+ throw new Error('some error')
1294
+ }
1295
+
1284
1296
  done()
1285
1297
  })
1286
1298
 
@@ -1288,6 +1300,26 @@ test('onSend hook throws', t => {
1288
1300
  reply.send({ hello: 'world' })
1289
1301
  })
1290
1302
 
1303
+ fastify.post('/', {
1304
+ schema: {
1305
+ response: {
1306
+ 200: {
1307
+ content: {
1308
+ 'application/json': {
1309
+ schema: {
1310
+ name: { type: 'string' },
1311
+ image: { type: 'string' },
1312
+ address: { type: 'string' }
1313
+ }
1314
+ }
1315
+ }
1316
+ }
1317
+ }
1318
+ }
1319
+ }, (req, reply) => {
1320
+ reply.send({ hello: 'world' })
1321
+ })
1322
+
1291
1323
  fastify.delete('/', (req, reply) => {
1292
1324
  reply.send({ hello: 'world' })
1293
1325
  })
@@ -1309,6 +1341,13 @@ test('onSend hook throws', t => {
1309
1341
  t.equal(response.headers['content-length'], '' + body.length)
1310
1342
  t.same(JSON.parse(body), { hello: 'world' })
1311
1343
  })
1344
+ sget({
1345
+ method: 'POST',
1346
+ url: 'http://localhost:' + fastify.server.address().port
1347
+ }, (err, response, body) => {
1348
+ t.error(err)
1349
+ t.equal(response.statusCode, 500)
1350
+ })
1312
1351
  sget({
1313
1352
  method: 'DELETE',
1314
1353
  url: 'http://localhost:' + fastify.server.address().port
@@ -27,6 +27,7 @@ fastify.listen({ port: 0 }, err => {
27
27
  t.equal(res.headers[':status'], 404)
28
28
  t.same(JSON.parse(res.body), {
29
29
  statusCode: 404,
30
+ code: 'FST_ERR_NOT_FOUND',
30
31
  error: 'Not Found',
31
32
  message: 'Not Found'
32
33
  })
@@ -1740,7 +1740,7 @@ test('cannot set the replySerializer when the server is running', t => {
1740
1740
  fastify.setReplySerializer(() => {})
1741
1741
  t.fail('this serializer should not be setup')
1742
1742
  } catch (e) {
1743
- t.equal(e.message, 'Cannot call "setReplySerializer" when fastify instance is already started!')
1743
+ t.equal(e.code, 'FST_ERR_INSTANCE_ALREADY_LISTENING')
1744
1744
  }
1745
1745
  })
1746
1746
  })
@@ -22,20 +22,20 @@ test('maxRequestsPerSocket on node version >= 16.10.0', { skip }, t => {
22
22
 
23
23
  const port = fastify.server.address().port
24
24
  const client = net.createConnection({ port }, () => {
25
- client.write('GET / HTTP/1.1\r\n\r\n')
25
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
26
26
 
27
27
  client.once('data', data => {
28
28
  t.match(data.toString(), /Connection:\s*keep-alive/i)
29
29
  t.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
30
30
  t.match(data.toString(), /200 OK/i)
31
31
 
32
- client.write('GET / HTTP/1.1\r\n\r\n')
32
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
33
33
 
34
34
  client.once('data', data => {
35
35
  t.match(data.toString(), /Connection:\s*close/i)
36
36
  t.match(data.toString(), /200 OK/i)
37
37
 
38
- client.write('GET / HTTP/1.1\r\n\r\n')
38
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
39
39
 
40
40
  client.once('data', data => {
41
41
  t.match(data.toString(), /Connection:\s*close/i)
@@ -63,21 +63,21 @@ test('maxRequestsPerSocket zero should behave same as null', { skip }, t => {
63
63
 
64
64
  const port = fastify.server.address().port
65
65
  const client = net.createConnection({ port }, () => {
66
- client.write('GET / HTTP/1.1\r\n\r\n')
66
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
67
67
 
68
68
  client.once('data', data => {
69
69
  t.match(data.toString(), /Connection:\s*keep-alive/i)
70
70
  t.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
71
71
  t.match(data.toString(), /200 OK/i)
72
72
 
73
- client.write('GET / HTTP/1.1\r\n\r\n')
73
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
74
74
 
75
75
  client.once('data', data => {
76
76
  t.match(data.toString(), /Connection:\s*keep-alive/i)
77
77
  t.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
78
78
  t.match(data.toString(), /200 OK/i)
79
79
 
80
- client.write('GET / HTTP/1.1\r\n\r\n')
80
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
81
81
 
82
82
  client.once('data', data => {
83
83
  t.match(data.toString(), /Connection:\s*keep-alive/i)
@@ -204,7 +204,7 @@ function testHandlerOrBeforeHandlerHook (test, hookOrHandler) {
204
204
  app.listen({ port: 0 }, err => {
205
205
  t.error(err)
206
206
  const client = net.createConnection({ port: (app.server.address()).port }, () => {
207
- client.write('GET / HTTP/1.1\r\n\r\n')
207
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
208
208
 
209
209
  let chunks = ''
210
210
  client.setEncoding('utf8')
@@ -25,6 +25,7 @@ import { Socket } from 'net'
25
25
  // http server
26
26
  expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify())
27
27
  expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({}))
28
+ expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({ http: {} }))
28
29
  // https server
29
30
  expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({ https: {} }))
30
31
  expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({ https: null }))
@@ -12,6 +12,7 @@ import { FastifyReply } from '../../types/reply'
12
12
  import { FastifyRequest } from '../../types/request'
13
13
  import { DefaultRoute } from '../../types/route'
14
14
  import { FastifySchemaControllerOptions, FastifySchemaCompiler, FastifySerializerCompiler } from '../../types/schema'
15
+ import { AddressInfo } from 'net'
15
16
 
16
17
  const server = fastify()
17
18
 
@@ -26,6 +27,7 @@ expectAssignable<FastifyInstance>(server.addSchema({
26
27
  }))
27
28
 
28
29
  expectType<Record<string, unknown>>(server.getSchemas())
30
+ expectType<AddressInfo[]>(server.addresses())
29
31
  expectType<unknown>(server.getSchema('SchemaId'))
30
32
  expectType<string>(server.printRoutes())
31
33
  expectType<string>(server.printPlugins())
@@ -324,6 +326,8 @@ const versionConstraintStrategy = {
324
326
  expectType<void>(server.addConstraintStrategy(versionConstraintStrategy))
325
327
  expectType<boolean>(server.hasConstraintStrategy(versionConstraintStrategy.name))
326
328
 
329
+ expectType<boolean>(server.hasPlugin(''))
330
+
327
331
  expectAssignable<DefaultRoute<RawRequestDefaultExpression, RawReplyDefaultExpression>>(server.getDefaultRoute())
328
332
 
329
333
  expectType<FastifySchemaCompiler<any> | undefined>(server.validatorCompiler)
@@ -18,7 +18,7 @@ t.test('Will return 505 HTTP error if HTTP version (2.0 when server is 1.1) is n
18
18
 
19
19
  const port = fastify.server.address().port
20
20
  const client = net.createConnection({ port }, () => {
21
- client.write('GET / HTTP/2.0\r\n\r\n')
21
+ client.write('GET / HTTP/2.0\r\nHost: example.com\r\n\r\n')
22
22
 
23
23
  client.once('data', data => {
24
24
  t.match(data.toString(), /505 HTTP Version Not Supported/i)
@@ -568,7 +568,7 @@ test('cannot create a fastify instance with wrong type of errorFormatter', t =>
568
568
  }
569
569
  })
570
570
  } catch (err) {
571
- t.equal(err.message, 'schemaErrorFormatter option should not be an async function')
571
+ t.equal(err.code, 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN')
572
572
  }
573
573
 
574
574
  try {
@@ -576,14 +576,14 @@ test('cannot create a fastify instance with wrong type of errorFormatter', t =>
576
576
  schemaErrorFormatter: 500
577
577
  })
578
578
  } catch (err) {
579
- t.equal(err.message, 'schemaErrorFormatter option should be a function, instead got number')
579
+ t.equal(err.code, 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN')
580
580
  }
581
581
 
582
582
  try {
583
583
  const fastify = Fastify()
584
584
  fastify.setSchemaErrorFormatter(500)
585
585
  } catch (err) {
586
- t.equal(err.message, 'schemaErrorFormatter option should be a function, instead got number')
586
+ t.equal(err.code, 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN')
587
587
  }
588
588
  })
589
589
 
package/types/errors.d.ts CHANGED
@@ -49,5 +49,25 @@ export type FastifyErrorCodes = Record<
49
49
  'FST_ERR_PLUGIN_NOT_VALID' |
50
50
  'FST_ERR_ROOT_PLG_BOOTED' |
51
51
  'FST_ERR_PARENT_PLUGIN_BOOTED' |
52
- 'FST_ERR_PLUGIN_TIMEOUT'
52
+ 'FST_ERR_PLUGIN_TIMEOUT' |
53
+ 'FST_ERR_OPTIONS_NOT_OBJ' |
54
+ 'FST_ERR_QSP_NOT_FN' |
55
+ 'FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN' |
56
+ 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN' |
57
+ 'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ' |
58
+ 'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR' |
59
+ 'FST_ERR_VERSION_CONSTRAINT_NOT_STR' |
60
+ 'FST_ERR_CTP_INSTANCE_ALREADY_STARTED' |
61
+ 'FST_ERR_HOOK_NOT_SUPPORTED' |
62
+ 'FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX' |
63
+ 'FST_ERR_ROUTE_OPTIONS_NOT_OBJ' |
64
+ 'FST_ERR_ROUTE_DUPLICATED_HANDLER' |
65
+ 'FST_ERR_ROUTE_HANDLER_NOT_FN' |
66
+ 'FST_ERR_ROUTE_MISSING_HANDLER' |
67
+ 'FST_ERR_ROUTE_METHOD_NOT_SUPPORTED' |
68
+ 'FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED' |
69
+ 'FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT' |
70
+ 'FST_ERR_ROUTE_REWRITE_NOT_STR' |
71
+ 'FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE' |
72
+ 'FST_ERR_INSTANCE_ALREADY_LISTENING'
53
73
  , FastifyErrorConstructor>
@@ -21,6 +21,7 @@ import {
21
21
  FastifyTypeProviderDefault
22
22
  } from './type-provider'
23
23
  import { ContextConfigDefault, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
24
+ import { AddressInfo } from 'net'
24
25
 
25
26
  export interface PrintRoutesOptions {
26
27
  includeMeta?: boolean | (string | symbol)[]
@@ -92,6 +93,7 @@ export interface FastifyInstance<
92
93
  version: string;
93
94
  log: Logger;
94
95
 
96
+ addresses(): AddressInfo[]
95
97
  withTypeProvider<Provider extends FastifyTypeProvider>(): FastifyInstance<RawServer, RawRequest, RawReply, Logger, Provider>;
96
98
 
97
99
  addSchema(schema: unknown): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
@@ -129,6 +131,7 @@ export interface FastifyInstance<
129
131
  hasDecorator(decorator: string | symbol): boolean;
130
132
  hasRequestDecorator(decorator: string | symbol): boolean;
131
133
  hasReplyDecorator(decorator: string | symbol): boolean;
134
+ hasPlugin(name: string): boolean;
132
135
 
133
136
  addConstraintStrategy(strategy: ConstraintStrategy<FindMyWayVersion<RawServer>, unknown>): void;
134
137
  hasConstraintStrategy(strategyName: string): boolean;