fastify 4.19.2 → 4.21.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.
Files changed (58) hide show
  1. package/.c8rc.json +8 -0
  2. package/.taprc +3 -2
  3. package/README.md +2 -1
  4. package/SECURITY.md +9 -0
  5. package/docs/Guides/Prototype-Poisoning.md +2 -2
  6. package/docs/Reference/Errors.md +39 -17
  7. package/docs/Reference/Logging.md +1 -1
  8. package/docs/Reference/Plugins.md +4 -0
  9. package/docs/Reference/Routes.md +8 -0
  10. package/docs/Reference/Server.md +230 -178
  11. package/docs/Reference/TypeScript.md +1 -1
  12. package/fastify.d.ts +3 -2
  13. package/fastify.js +36 -17
  14. package/lib/context.js +6 -0
  15. package/lib/errors.js +51 -20
  16. package/lib/fourOhFour.js +5 -9
  17. package/lib/handleRequest.js +3 -5
  18. package/lib/hooks.js +91 -25
  19. package/lib/logger.js +40 -3
  20. package/lib/reply.js +19 -13
  21. package/lib/reqIdGenFactory.js +18 -3
  22. package/lib/route.js +14 -61
  23. package/lib/schema-controller.js +2 -0
  24. package/lib/server.js +23 -8
  25. package/lib/symbols.js +1 -0
  26. package/package.json +8 -10
  27. package/test/500s.test.js +22 -0
  28. package/test/async-await.test.js +1 -1
  29. package/test/childLoggerFactory.test.js +91 -0
  30. package/test/encapsulated-child-logger-factory.test.js +69 -0
  31. package/test/fastify-instance.test.js +43 -10
  32. package/test/inject.test.js +1 -2
  33. package/test/internals/errors.test.js +843 -0
  34. package/test/internals/hookRunner.test.js +22 -8
  35. package/test/internals/initialConfig.test.js +9 -2
  36. package/test/internals/reply.test.js +82 -45
  37. package/test/internals/reqIdGenFactory.test.js +129 -0
  38. package/test/internals/request-validate.test.js +40 -1
  39. package/test/internals/request.test.js +14 -4
  40. package/test/reply-error.test.js +25 -0
  41. package/test/request-id.test.js +131 -0
  42. package/test/route.test.js +135 -0
  43. package/test/serial/logger.0.test.js +6 -1
  44. package/test/server.test.js +64 -2
  45. package/test/stream.test.js +4 -4
  46. package/test/types/errors.test-d.ts +82 -0
  47. package/test/types/fastify.test-d.ts +4 -0
  48. package/test/types/instance.test-d.ts +37 -0
  49. package/test/types/reply.test-d.ts +26 -0
  50. package/test/types/route.test-d.ts +3 -0
  51. package/test/types/type-provider.test-d.ts +56 -0
  52. package/types/errors.d.ts +29 -23
  53. package/types/instance.d.ts +33 -7
  54. package/types/logger.d.ts +25 -0
  55. package/types/reply.d.ts +8 -6
  56. package/types/route.d.ts +2 -1
  57. package/types/type-provider.d.ts +2 -1
  58. package/types/utils.d.ts +9 -0
package/lib/logger.js CHANGED
@@ -107,27 +107,64 @@ function createLogger (options) {
107
107
  * of a Fastify compatible logger.
108
108
  *
109
109
  * @param {object} logger Object to validate.
110
+ * @param {boolean?} strict `true` if the object must be a logger (always throw if any methods missing)
110
111
  *
111
112
  * @returns {boolean} `true` when the logger meets the requirements.
112
113
  *
113
114
  * @throws {FST_ERR_LOG_INVALID_LOGGER} When the logger object is
114
115
  * missing required methods.
115
116
  */
116
- function validateLogger (logger) {
117
+ function validateLogger (logger, strict) {
117
118
  const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child']
118
- const missingMethods = methods.filter(method => !logger[method] || typeof logger[method] !== 'function')
119
+ const missingMethods = logger
120
+ ? methods.filter(method => !logger[method] || typeof logger[method] !== 'function')
121
+ : methods
119
122
 
120
123
  if (!missingMethods.length) {
121
124
  return true
122
- } else if (missingMethods.length === methods.length) {
125
+ } else if ((missingMethods.length === methods.length) && !strict) {
123
126
  return false
124
127
  } else {
125
128
  throw FST_ERR_LOG_INVALID_LOGGER(missingMethods.join(','))
126
129
  }
127
130
  }
128
131
 
132
+ /**
133
+ * Utility for creating a child logger with the appropriate bindings, logger factory
134
+ * and validation.
135
+ * @param {object} context
136
+ * @param {import('../fastify').FastifyBaseLogger} logger
137
+ * @param {import('../fastify').RawRequestDefaultExpression<any>} req
138
+ * @param {string} reqId
139
+ * @param {import('../types/logger.js').ChildLoggerOptions?} loggerOpts
140
+ */
141
+ function createChildLogger (context, logger, req, reqId, loggerOpts) {
142
+ const loggerBindings = {
143
+ [context.requestIdLogLabel]: reqId
144
+ }
145
+ const child = context.childLoggerFactory.call(context.server, logger, loggerBindings, loggerOpts || {}, req)
146
+
147
+ // Optimisation: bypass validation if the factory is our own default factory
148
+ if (context.childLoggerFactory !== defaultChildLoggerFactory) {
149
+ validateLogger(child, true) // throw if the child is not a valid logger
150
+ }
151
+
152
+ return child
153
+ }
154
+
155
+ /**
156
+ * @param {import('../fastify.js').FastifyBaseLogger} logger
157
+ * @param {import('../types/logger.js').Bindings} bindings
158
+ * @param {import('../types/logger.js').ChildLoggerOptions} opts
159
+ */
160
+ function defaultChildLoggerFactory (logger, bindings, opts) {
161
+ return logger.child(bindings, opts)
162
+ }
163
+
129
164
  module.exports = {
130
165
  createLogger,
166
+ createChildLogger,
167
+ defaultChildLoggerFactory,
131
168
  serializers,
132
169
  now
133
170
  }
package/lib/reply.js CHANGED
@@ -23,7 +23,7 @@ const {
23
23
  kOptions,
24
24
  kRouteContext
25
25
  } = require('./symbols.js')
26
- const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks')
26
+ const { onSendHookRunner, onResponseHookRunner, onPreHandlerHookRunner } = require('./hooks')
27
27
 
28
28
  const internals = require('./handleRequest')[Symbol.for('internals')]
29
29
  const loggerUtils = require('./logger')
@@ -97,7 +97,7 @@ Object.defineProperties(Reply.prototype, {
97
97
 
98
98
  // We throw only if sent was overwritten from Fastify
99
99
  if (this.sent && this[kReplyHijacked]) {
100
- throw new FST_ERR_REP_ALREADY_SENT()
100
+ throw new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method)
101
101
  }
102
102
 
103
103
  this[kReplyHijacked] = true
@@ -124,7 +124,7 @@ Reply.prototype.send = function (payload) {
124
124
  }
125
125
 
126
126
  if (this.sent) {
127
- this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT() }, 'Reply already sent')
127
+ this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method) })
128
128
  return this
129
129
  }
130
130
 
@@ -539,6 +539,18 @@ function wrapOnSendEnd (err, request, reply, payload) {
539
539
  }
540
540
  }
541
541
 
542
+ function safeWriteHead (reply, statusCode) {
543
+ const res = reply.raw
544
+ try {
545
+ res.writeHead(statusCode, reply[kReplyHeaders])
546
+ } catch (err) {
547
+ if (err.code === 'ERR_HTTP_HEADERS_SENT') {
548
+ reply.log.warn(`Reply was already sent, did you forget to "return reply" in the "${reply.request.raw.url}" (${reply.request.raw.method}) route?`)
549
+ }
550
+ throw err
551
+ }
552
+ }
553
+
542
554
  function onSendEnd (reply, payload) {
543
555
  const res = reply.raw
544
556
  const req = reply.request
@@ -569,7 +581,7 @@ function onSendEnd (reply, payload) {
569
581
  reply[kReplyHeaders]['content-length'] = '0'
570
582
  }
571
583
 
572
- res.writeHead(statusCode, reply[kReplyHeaders])
584
+ safeWriteHead(reply, statusCode)
573
585
  sendTrailer(payload, res, reply)
574
586
  return
575
587
  }
@@ -594,7 +606,7 @@ function onSendEnd (reply, payload) {
594
606
  }
595
607
  }
596
608
 
597
- res.writeHead(statusCode, reply[kReplyHeaders])
609
+ safeWriteHead(reply, statusCode)
598
610
  // write payload first
599
611
  res.write(payload)
600
612
  // then send trailers
@@ -755,9 +767,8 @@ function setupResponseListeners (reply) {
755
767
  const ctx = reply[kRouteContext]
756
768
 
757
769
  if (ctx && ctx.onResponse !== null) {
758
- hookRunner(
770
+ onResponseHookRunner(
759
771
  ctx.onResponse,
760
- onResponseIterator,
761
772
  reply.request,
762
773
  reply,
763
774
  onResponseCallback
@@ -771,10 +782,6 @@ function setupResponseListeners (reply) {
771
782
  reply.raw.on('error', onResFinished)
772
783
  }
773
784
 
774
- function onResponseIterator (fn, request, reply, next) {
775
- return fn(request, reply, next)
776
- }
777
-
778
785
  function onResponseCallback (err, request, reply) {
779
786
  if (reply.log[kDisableRequestLogging]) {
780
787
  return
@@ -839,9 +846,8 @@ function notFound (reply) {
839
846
 
840
847
  // preHandler hook
841
848
  if (reply[kRouteContext].preHandler !== null) {
842
- hookRunner(
849
+ onPreHandlerHookRunner(
843
850
  reply[kRouteContext].preHandler,
844
- hookIterator,
845
851
  reply.request,
846
852
  reply,
847
853
  internals.preHandlerCallback
@@ -1,14 +1,26 @@
1
1
  'use strict'
2
2
 
3
- module.exports = function (requestIdHeader, optGenReqId) {
3
+ /**
4
+ * @callback GenerateRequestId
5
+ * @param {Object} req
6
+ * @returns {string}
7
+ */
8
+
9
+ /**
10
+ * @param {string} [requestIdHeader]
11
+ * @param {GenerateRequestId} [optGenReqId]
12
+ * @returns {GenerateRequestId}
13
+ */
14
+ function reqIdGenFactory (requestIdHeader, optGenReqId) {
4
15
  // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
5
16
  // With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days.
6
17
  // This is very likely to happen in real-world applications, hence the limit is enforced.
7
18
  // Growing beyond this value will make the id generation slower and cause a deopt.
8
19
  // In the worst cases, it will become a float, losing accuracy.
9
20
  const maxInt = 2147483647
21
+
10
22
  let nextReqId = 0
11
- function defaultGenReqId (req) {
23
+ function defaultGenReqId (_req) {
12
24
  nextReqId = (nextReqId + 1) & maxInt
13
25
  return `req-${nextReqId.toString(36)}`
14
26
  }
@@ -16,7 +28,6 @@ module.exports = function (requestIdHeader, optGenReqId) {
16
28
  const genReqId = optGenReqId || defaultGenReqId
17
29
 
18
30
  if (requestIdHeader) {
19
- // requestIdHeader = typeof requestIdHeader === 'string' ? requestIdHeader : 'request-id'
20
31
  return function (req) {
21
32
  return req.headers[requestIdHeader] || genReqId(req)
22
33
  }
@@ -24,3 +35,7 @@ module.exports = function (requestIdHeader, optGenReqId) {
24
35
 
25
36
  return genReqId
26
37
  }
38
+
39
+ module.exports = {
40
+ reqIdGenFactory
41
+ }
package/lib/route.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const FindMyWay = require('find-my-way')
4
4
  const Context = require('./context')
5
5
  const handleRequest = require('./handleRequest')
6
- const { hookRunner, hookIterator, onRequestAbortHookRunner, lifecycleHooks } = require('./hooks')
6
+ const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
7
7
  const { supportedMethods } = require('./httpMethods')
8
8
  const { normalizeSchema } = require('./schemas')
9
9
  const { parseHeadOnSendHandlers } = require('./headRoute')
@@ -20,7 +20,6 @@ const {
20
20
  FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
21
21
  FST_ERR_DUPLICATED_ROUTE,
22
22
  FST_ERR_INVALID_URL,
23
- FST_ERR_SEND_UNDEFINED_ERR,
24
23
  FST_ERR_HOOK_INVALID_HANDLER,
25
24
  FST_ERR_ROUTE_OPTIONS_NOT_OBJ,
26
25
  FST_ERR_ROUTE_DUPLICATED_HANDLER,
@@ -51,13 +50,13 @@ const {
51
50
  kRouteContext
52
51
  } = require('./symbols.js')
53
52
  const { buildErrorHandler } = require('./error-handler')
53
+ const { createChildLogger } = require('./logger')
54
54
 
55
55
  function buildRouting (options) {
56
56
  const router = FindMyWay(options.config)
57
57
 
58
58
  let avvio
59
59
  let fourOhFour
60
- let requestIdLogLabel
61
60
  let logger
62
61
  let hasLogger
63
62
  let setupResponseListeners
@@ -74,6 +73,10 @@ function buildRouting (options) {
74
73
  let closing = false
75
74
 
76
75
  return {
76
+ /**
77
+ * @param {import('../fastify').FastifyServerOptions} options
78
+ * @param {*} fastifyArgs
79
+ */
77
80
  setup (options, fastifyArgs) {
78
81
  avvio = fastifyArgs.avvio
79
82
  fourOhFour = fastifyArgs.fourOhFour
@@ -84,7 +87,6 @@ function buildRouting (options) {
84
87
  validateHTTPVersion = fastifyArgs.validateHTTPVersion
85
88
 
86
89
  globalExposeHeadRoutes = options.exposeHeadRoutes
87
- requestIdLogLabel = options.requestIdLogLabel
88
90
  genReqId = options.genReqId
89
91
  disableRequestLogging = options.disableRequestLogging
90
92
  ignoreTrailingSlash = options.ignoreTrailingSlash
@@ -168,7 +170,10 @@ function buildRouting (options) {
168
170
  ) !== null
169
171
  }
170
172
 
171
- // Route management
173
+ /**
174
+ * Route management
175
+ * @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
176
+ */
172
177
  function route ({ options, isFastify }) {
173
178
  // Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
174
179
  const opts = { ...options }
@@ -282,6 +287,7 @@ function buildRouting (options) {
282
287
  handler: opts.handler.bind(this),
283
288
  config,
284
289
  errorHandler: opts.errorHandler,
290
+ childLoggerFactory: opts.childLoggerFactory,
285
291
  bodyLimit: opts.bodyLimit,
286
292
  logLevel: opts.logLevel,
287
293
  logSerializers: opts.logSerializers,
@@ -399,10 +405,6 @@ function buildRouting (options) {
399
405
  function routeHandler (req, res, params, context, query) {
400
406
  const id = genReqId(req)
401
407
 
402
- const loggerBinding = {
403
- [requestIdLogLabel]: id
404
- }
405
-
406
408
  const loggerOpts = {
407
409
  level: context.logLevel
408
410
  }
@@ -410,7 +412,7 @@ function buildRouting (options) {
410
412
  if (context.logSerializers) {
411
413
  loggerOpts.serializers = context.logSerializers
412
414
  }
413
- const childLogger = logger.child(loggerBinding, loggerOpts)
415
+ const childLogger = createChildLogger(context, logger, req, id, loggerOpts)
414
416
  childLogger[kDisableRequestLogging] = disableRequestLogging
415
417
 
416
418
  // TODO: The check here should be removed once https://github.com/nodejs/node/issues/43115 resolve in core.
@@ -477,9 +479,8 @@ function buildRouting (options) {
477
479
  }
478
480
 
479
481
  if (context.onRequest !== null) {
480
- hookRunner(
482
+ onRequestHookRunner(
481
483
  context.onRequest,
482
- hookIterator,
483
484
  request,
484
485
  reply,
485
486
  runPreParsing
@@ -494,7 +495,6 @@ function buildRouting (options) {
494
495
  if (req.aborted) {
495
496
  onRequestAbortHookRunner(
496
497
  context.onRequestAbort,
497
- hookIterator,
498
498
  request,
499
499
  handleOnRequestAbortHooksErrors.bind(null, reply)
500
500
  )
@@ -519,9 +519,8 @@ function handleOnRequestAbortHooksErrors (reply, err) {
519
519
 
520
520
  function handleTimeout () {
521
521
  const { context, request, reply } = this._meta
522
- hookRunner(
522
+ onTimeoutHookRunner(
523
523
  context.onTimeout,
524
- hookIterator,
525
524
  request,
526
525
  reply,
527
526
  noop
@@ -570,52 +569,6 @@ function runPreParsing (err, request, reply) {
570
569
  }
571
570
  }
572
571
 
573
- function preParsingHookRunner (functions, request, reply, cb) {
574
- let i = 0
575
-
576
- function next (err, stream) {
577
- if (reply.sent) {
578
- return
579
- }
580
-
581
- if (typeof stream !== 'undefined') {
582
- request[kRequestPayloadStream] = stream
583
- }
584
-
585
- if (err || i === functions.length) {
586
- cb(err, request, reply)
587
- return
588
- }
589
-
590
- const fn = functions[i++]
591
- let result
592
- try {
593
- result = fn(request, reply, request[kRequestPayloadStream], next)
594
- } catch (error) {
595
- next(error)
596
- return
597
- }
598
-
599
- if (result && typeof result.then === 'function') {
600
- result.then(handleResolve, handleReject)
601
- }
602
- }
603
-
604
- function handleResolve (stream) {
605
- next(null, stream)
606
- }
607
-
608
- function handleReject (err) {
609
- if (!err) {
610
- err = new FST_ERR_SEND_UNDEFINED_ERR()
611
- }
612
-
613
- next(err)
614
- }
615
-
616
- next(null, request[kRequestPayloadStream])
617
- }
618
-
619
572
  /**
620
573
  * Used within the route handler as a `net.Socket.close` event handler.
621
574
  * The purpose is to remove a socket from the tracked sockets collection when
@@ -50,6 +50,8 @@ class SchemaController {
50
50
  this.schemaBucket = this.opts.bucket(parent.getSchemas())
51
51
  this.validatorCompiler = parent.getValidatorCompiler()
52
52
  this.serializerCompiler = parent.getSerializerCompiler()
53
+ this.isCustomValidatorCompiler = parent.isCustomValidatorCompiler
54
+ this.isCustomSerializerCompiler = parent.isCustomSerializerCompiler
53
55
  this.parent = parent
54
56
  } else {
55
57
  this.schemaBucket = this.opts.bucket()
package/lib/server.js CHANGED
@@ -9,7 +9,8 @@ const { kState, kOptions, kServerBindings } = require('./symbols')
9
9
  const {
10
10
  FST_ERR_HTTP2_INVALID_VERSION,
11
11
  FST_ERR_REOPENED_CLOSE_SERVER,
12
- FST_ERR_REOPENED_SERVER
12
+ FST_ERR_REOPENED_SERVER,
13
+ FST_ERR_LISTEN_OPTIONS_INVALID
13
14
  } = require('./errors')
14
15
 
15
16
  module.exports.createServer = createServer
@@ -22,8 +23,6 @@ function defaultResolveServerListeningText (address) {
22
23
  function createServer (options, httpHandler) {
23
24
  const server = getServerInstance(options, httpHandler)
24
25
 
25
- return { server, listen }
26
-
27
26
  // `this` is the Fastify object
28
27
  function listen (listenOptions, ...args) {
29
28
  let cb = args.slice(-1).pop()
@@ -48,6 +47,20 @@ function createServer (options, httpHandler) {
48
47
  } else {
49
48
  listenOptions.cb = cb
50
49
  }
50
+ if (listenOptions.signal) {
51
+ if (typeof listenOptions.signal.on !== 'function' && typeof listenOptions.signal.addEventListener !== 'function') {
52
+ throw new FST_ERR_LISTEN_OPTIONS_INVALID('Invalid options.signal')
53
+ }
54
+
55
+ if (listenOptions.signal.aborted) {
56
+ this.close()
57
+ } else {
58
+ const onAborted = () => {
59
+ this.close()
60
+ }
61
+ listenOptions.signal.addEventListener('abort', onAborted, { once: true })
62
+ }
63
+ }
51
64
 
52
65
  // If we have a path specified, don't default host to 'localhost' so we don't end up listening
53
66
  // on both path and host
@@ -99,6 +112,8 @@ function createServer (options, httpHandler) {
99
112
 
100
113
  this.ready(listenCallback.call(this, server, listenOptions))
101
114
  }
115
+
116
+ return { server, listen }
102
117
  }
103
118
 
104
119
  function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, onListen) {
@@ -138,17 +153,16 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
138
153
  // pace through the `onClose` hook.
139
154
  // It also allows them to handle possible connections already
140
155
  // attached to them if any.
156
+ /* c8 ignore next 20 */
141
157
  this.onClose((instance, done) => {
142
158
  if (instance[kState].listening) {
143
159
  // No new TCP connections are accepted
144
160
  // We swallow any error from the secondary
145
161
  // server
146
162
  secondaryServer.close(() => done())
147
- /* istanbul ignore next: Cannot test this without Node.js core support */
148
163
  if (serverOpts.forceCloseConnections === 'idle') {
149
164
  // Not needed in Node 19
150
165
  secondaryServer.closeIdleConnections()
151
- /* istanbul ignore next: Cannot test this without Node.js core support */
152
166
  } else if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections) {
153
167
  secondaryServer.closeAllConnections()
154
168
  }
@@ -216,9 +230,10 @@ function listenCallback (server, listenOptions) {
216
230
  }
217
231
 
218
232
  server.once('error', wrap)
219
- server.listen(listenOptions, wrap)
220
-
221
- this[kState].listening = true
233
+ if (!this[kState].closing) {
234
+ server.listen(listenOptions, wrap)
235
+ this[kState].listening = true
236
+ }
222
237
  }
223
238
  }
224
239
 
package/lib/symbols.js CHANGED
@@ -55,6 +55,7 @@ const keys = {
55
55
  // This symbol is only meant to be used for fastify tests and should not be used for any other purpose
56
56
  kTestInternals: Symbol('fastify.testInternals'),
57
57
  kErrorHandler: Symbol('fastify.errorHandler'),
58
+ kChildLoggerFactory: Symbol('fastify.childLoggerFactory'),
58
59
  kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
59
60
  kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
60
61
  kRouteByFastify: Symbol('fastify.routeByFastify')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.19.2",
3
+ "version": "4.21.0",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -10,23 +10,22 @@
10
10
  "benchmark": "npx concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"npx autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
11
11
  "benchmark:parser": "npx concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"npx autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
12
12
  "build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
13
- "coverage": "cross-env NODE_OPTIONS=no-network-family-autoselection npm run unit -- --cov --coverage-report=html",
14
- "coverage:ci": "npm run unit -- --cov --coverage-report=html --no-browser --no-check-coverage",
15
- "coverage:ci-check-coverage": "nyc check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
16
- "license-checker": "license-checker --production --onlyAllow=\"MIT;ISC;BSD-3-Clause;BSD-2-Clause\"",
13
+ "coverage": "npm run unit -- --coverage-report=html",
14
+ "coverage:ci": "c8 --reporter=lcov tap --coverage-report=html --no-browser --no-check-coverage",
15
+ "coverage:ci-check-coverage": "c8 check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
17
16
  "lint": "npm run lint:standard && npm run lint:typescript && npm run lint:markdown",
18
17
  "lint:fix": "standard --fix",
19
18
  "lint:markdown": "markdownlint-cli2",
20
19
  "lint:standard": "standard | snazzy",
21
20
  "lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
22
21
  "prepublishOnly": "cross-env PREPUBLISH=true tap --no-check-coverage test/build/**.test.js && npm run test:validator:integrity",
23
- "test": "npm run lint && cross-env NODE_OPTIONS=no-network-family-autoselection npm run unit && npm run test:typescript",
22
+ "test": "npm run lint && npm run unit && npm run test:typescript",
24
23
  "test:ci": "npm run unit -- --cov --coverage-report=lcovonly && npm run test:typescript",
25
24
  "test:report": "npm run lint && npm run unit:report && npm run test:typescript",
26
25
  "test:validator:integrity": "npm run build:validation && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/error-serializer.js && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/configValidator.js",
27
26
  "test:typescript": "tsc test/types/import.ts && tsd",
28
27
  "test:watch": "npm run unit -- -w --no-coverage-report -R terse",
29
- "unit": "tap",
28
+ "unit": "c8 tap",
30
29
  "unit:junit": "tap-mocha-reporter xunit < out.tap > test/junit-testresults.xml",
31
30
  "unit:report": "tap --cov --coverage-report=html --coverage-report=cobertura | tee out.tap"
32
31
  },
@@ -134,7 +133,7 @@
134
133
  "homepage": "https://www.fastify.io/",
135
134
  "devDependencies": {
136
135
  "@fastify/pre-commit": "^2.0.2",
137
- "@sinclair/typebox": "^0.28.9",
136
+ "@sinclair/typebox": "^0.29.1",
138
137
  "@sinonjs/fake-timers": "^11.0.0",
139
138
  "@types/node": "^20.1.0",
140
139
  "@typescript-eslint/eslint-plugin": "^5.59.2",
@@ -145,6 +144,7 @@
145
144
  "ajv-i18n": "^4.2.0",
146
145
  "ajv-merge-patch": "^5.0.1",
147
146
  "branch-comparer": "^1.1.0",
147
+ "c8": "^8.0.0",
148
148
  "cross-env": "^7.0.3",
149
149
  "eslint": "^8.39.0",
150
150
  "eslint-config-standard": "^17.0.0",
@@ -161,10 +161,8 @@
161
161
  "joi": "^17.9.2",
162
162
  "json-schema-to-ts": "^2.9.1",
163
163
  "JSONStream": "^1.3.5",
164
- "license-checker": "^25.0.1",
165
164
  "markdownlint-cli2": "^0.8.1",
166
165
  "proxyquire": "^2.1.3",
167
- "pump": "^3.0.0",
168
166
  "self-cert": "^2.0.0",
169
167
  "send": "^0.18.0",
170
168
  "simple-get": "^4.0.1",
package/test/500s.test.js CHANGED
@@ -9,6 +9,7 @@ test('default 500', t => {
9
9
  t.plan(4)
10
10
 
11
11
  const fastify = Fastify()
12
+ t.teardown(fastify.close.bind(fastify))
12
13
 
13
14
  fastify.get('/', function (req, reply) {
14
15
  reply.send(new Error('kaboom'))
@@ -33,6 +34,7 @@ test('custom 500', t => {
33
34
  t.plan(6)
34
35
 
35
36
  const fastify = Fastify()
37
+ t.teardown(fastify.close.bind(fastify))
36
38
 
37
39
  fastify.get('/', function (req, reply) {
38
40
  reply.send(new Error('kaboom'))
@@ -62,6 +64,7 @@ test('encapsulated 500', t => {
62
64
  t.plan(10)
63
65
 
64
66
  const fastify = Fastify()
67
+ t.teardown(fastify.close.bind(fastify))
65
68
 
66
69
  fastify.get('/', function (req, reply) {
67
70
  reply.send(new Error('kaboom'))
@@ -113,6 +116,7 @@ test('custom 500 with hooks', t => {
113
116
  t.plan(7)
114
117
 
115
118
  const fastify = Fastify()
119
+ t.teardown(fastify.close.bind(fastify))
116
120
 
117
121
  fastify.get('/', function (req, reply) {
118
122
  reply.send(new Error('kaboom'))
@@ -166,3 +170,21 @@ test('cannot set errorHandler after binding', t => {
166
170
  }
167
171
  })
168
172
  })
173
+
174
+ test('cannot set childLoggerFactory after binding', t => {
175
+ t.plan(2)
176
+
177
+ const fastify = Fastify()
178
+ t.teardown(fastify.close.bind(fastify))
179
+
180
+ fastify.listen({ port: 0 }, err => {
181
+ t.error(err)
182
+
183
+ try {
184
+ fastify.setChildLoggerFactory(() => { })
185
+ t.fail()
186
+ } catch (e) {
187
+ t.pass()
188
+ }
189
+ })
190
+ })
@@ -124,7 +124,7 @@ test('ignore the result of the promise if reply.send is called beforehand (objec
124
124
  })
125
125
 
126
126
  test('server logs an error if reply.send is called and a value is returned via async/await', t => {
127
- const lines = ['incoming request', 'request completed', 'Reply already sent']
127
+ const lines = ['incoming request', 'request completed', 'Reply was already sent, did you forget to "return reply" in "/" (GET)?']
128
128
  t.plan(lines.length + 2)
129
129
 
130
130
  const splitStream = split(JSON.parse)