fastify 3.26.0 → 4.0.0-alpha.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 (129) hide show
  1. package/README.md +5 -4
  2. package/build/build-error-serializer.js +27 -0
  3. package/build/build-validation.js +49 -35
  4. package/docs/Guides/Ecosystem.md +2 -1
  5. package/docs/Guides/Prototype-Poisoning.md +3 -3
  6. package/docs/Migration-Guide-V4.md +12 -0
  7. package/docs/Reference/ContentTypeParser.md +8 -1
  8. package/docs/Reference/Errors.md +51 -6
  9. package/docs/Reference/Hooks.md +4 -7
  10. package/docs/Reference/LTS.md +5 -4
  11. package/docs/Reference/Reply.md +23 -22
  12. package/docs/Reference/Request.md +1 -3
  13. package/docs/Reference/Routes.md +17 -10
  14. package/docs/Reference/Server.md +98 -63
  15. package/docs/Reference/TypeScript.md +11 -13
  16. package/docs/Reference/Validation-and-Serialization.md +32 -54
  17. package/docs/Type-Providers.md +257 -0
  18. package/examples/hooks.js +1 -1
  19. package/examples/simple-stream.js +18 -0
  20. package/fastify.d.ts +36 -22
  21. package/fastify.js +72 -53
  22. package/lib/configValidator.js +902 -1023
  23. package/lib/contentTypeParser.js +6 -16
  24. package/lib/context.js +36 -10
  25. package/lib/decorate.js +5 -3
  26. package/lib/error-handler.js +158 -0
  27. package/lib/error-serializer.js +257 -0
  28. package/lib/errors.js +49 -10
  29. package/lib/fourOhFour.js +31 -20
  30. package/lib/handleRequest.js +10 -13
  31. package/lib/hooks.js +14 -9
  32. package/lib/noop-set.js +10 -0
  33. package/lib/pluginOverride.js +0 -3
  34. package/lib/pluginUtils.js +3 -2
  35. package/lib/reply.js +44 -163
  36. package/lib/request.js +13 -10
  37. package/lib/route.js +158 -139
  38. package/lib/schema-controller.js +3 -3
  39. package/lib/schemas.js +27 -1
  40. package/lib/server.js +219 -116
  41. package/lib/symbols.js +6 -4
  42. package/lib/validation.js +2 -1
  43. package/lib/warnings.js +2 -12
  44. package/lib/wrapThenable.js +4 -11
  45. package/package.json +40 -45
  46. package/test/404s.test.js +265 -108
  47. package/test/500s.test.js +2 -2
  48. package/test/async-await.test.js +15 -71
  49. package/test/close.test.js +39 -1
  50. package/test/content-parser.test.js +32 -0
  51. package/test/context-config.test.js +56 -4
  52. package/test/custom-http-server.test.js +14 -7
  53. package/test/custom-parser-async.test.js +0 -65
  54. package/test/custom-parser.test.js +54 -121
  55. package/test/decorator.test.js +1 -3
  56. package/test/delete.test.js +5 -5
  57. package/test/encapsulated-error-handler.test.js +50 -0
  58. package/test/esm/index.test.js +0 -14
  59. package/test/fastify-instance.test.js +4 -4
  60. package/test/fluent-schema.test.js +4 -4
  61. package/test/get.test.js +3 -3
  62. package/test/helper.js +18 -3
  63. package/test/hooks-async.test.js +14 -47
  64. package/test/hooks.on-ready.test.js +9 -4
  65. package/test/hooks.test.js +58 -99
  66. package/test/http2/closing.test.js +5 -11
  67. package/test/http2/unknown-http-method.test.js +3 -9
  68. package/test/https/custom-https-server.test.js +12 -6
  69. package/test/inject.test.js +1 -1
  70. package/test/input-validation.js +2 -2
  71. package/test/internals/all.test.js +2 -2
  72. package/test/internals/contentTypeParser.test.js +4 -4
  73. package/test/internals/handleRequest.test.js +9 -46
  74. package/test/internals/initialConfig.test.js +33 -12
  75. package/test/internals/logger.test.js +1 -1
  76. package/test/internals/reply.test.js +245 -3
  77. package/test/internals/request.test.js +13 -7
  78. package/test/internals/server.test.js +88 -0
  79. package/test/listen.test.js +84 -1
  80. package/test/logger.test.js +98 -58
  81. package/test/maxRequestsPerSocket.test.js +8 -6
  82. package/test/middleware.test.js +2 -25
  83. package/test/noop-set.test.js +19 -0
  84. package/test/nullable-validation.test.js +51 -14
  85. package/test/plugin.test.js +31 -5
  86. package/test/pretty-print.test.js +22 -10
  87. package/test/reply-error.test.js +123 -12
  88. package/test/request-error.test.js +2 -5
  89. package/test/route-hooks.test.js +17 -17
  90. package/test/route-prefix.test.js +2 -1
  91. package/test/route.test.js +216 -20
  92. package/test/router-options.test.js +1 -1
  93. package/test/schema-examples.test.js +11 -5
  94. package/test/schema-feature.test.js +24 -19
  95. package/test/schema-serialization.test.js +50 -9
  96. package/test/schema-special-usage.test.js +14 -81
  97. package/test/schema-validation.test.js +9 -9
  98. package/test/skip-reply-send.test.js +8 -8
  99. package/test/stream.test.js +23 -12
  100. package/test/throw.test.js +8 -5
  101. package/test/trust-proxy.test.js +1 -1
  102. package/test/type-provider.test.js +20 -0
  103. package/test/types/fastify.test-d.ts +12 -18
  104. package/test/types/hooks.test-d.ts +7 -3
  105. package/test/types/import.js +2 -0
  106. package/test/types/import.ts +1 -0
  107. package/test/types/instance.test-d.ts +61 -15
  108. package/test/types/logger.test-d.ts +44 -15
  109. package/test/types/route.test-d.ts +8 -2
  110. package/test/types/schema.test-d.ts +2 -39
  111. package/test/types/type-provider.test-d.ts +417 -0
  112. package/test/validation-error-handling.test.js +9 -9
  113. package/test/versioned-routes.test.js +29 -17
  114. package/test/wrapThenable.test.js +7 -6
  115. package/types/.eslintrc.json +1 -1
  116. package/types/content-type-parser.d.ts +17 -8
  117. package/types/hooks.d.ts +107 -60
  118. package/types/instance.d.ts +137 -105
  119. package/types/logger.d.ts +18 -104
  120. package/types/plugin.d.ts +10 -4
  121. package/types/register.d.ts +1 -1
  122. package/types/reply.d.ts +16 -11
  123. package/types/request.d.ts +10 -5
  124. package/types/route.d.ts +42 -31
  125. package/types/schema.d.ts +15 -1
  126. package/types/type-provider.d.ts +99 -0
  127. package/types/utils.d.ts +1 -1
  128. package/lib/schema-compilers.js +0 -12
  129. package/test/emit-warning.test.js +0 -166
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '3.26.0'
3
+ const VERSION = '4.0.0-alpha.1'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -10,6 +10,7 @@ let lightMyRequest
10
10
  const {
11
11
  kAvvioBoot,
12
12
  kChildren,
13
+ kServerBindings,
13
14
  kBodyLimit,
14
15
  kRoutePrefix,
15
16
  kLogLevel,
@@ -26,10 +27,12 @@ const {
26
27
  kOptions,
27
28
  kPluginNameChain,
28
29
  kSchemaErrorFormatter,
29
- kErrorHandler
30
+ kErrorHandler,
31
+ kKeepAliveConnections,
32
+ kFourOhFourContext
30
33
  } = require('./lib/symbols.js')
31
34
 
32
- const { createServer } = require('./lib/server')
35
+ const createServer = require('./lib/server')
33
36
  const Reply = require('./lib/reply')
34
37
  const Request = require('./lib/request')
35
38
  const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
@@ -45,18 +48,23 @@ const build404 = require('./lib/fourOhFour')
45
48
  const getSecuredInitialConfig = require('./lib/initialConfigValidation')
46
49
  const override = require('./lib/pluginOverride')
47
50
  const warning = require('./lib/warnings')
51
+ const noopSet = require('./lib/noop-set')
48
52
  const { defaultInitOptions } = getSecuredInitialConfig
49
53
 
50
54
  const {
51
55
  FST_ERR_BAD_URL,
52
- FST_ERR_MISSING_MIDDLEWARE
56
+ AVVIO_ERRORS_MAP,
57
+ appendStackTrace
53
58
  } = require('./lib/errors')
54
59
 
60
+ const { buildErrorHandler } = require('./lib/error-handler.js')
61
+
55
62
  const onBadUrlContext = {
56
63
  config: {
57
64
  },
58
65
  onSend: [],
59
- onError: []
66
+ onError: [],
67
+ [kFourOhFourContext]: null
60
68
  }
61
69
 
62
70
  function defaultBuildPrettyMeta (route) {
@@ -72,21 +80,6 @@ function defaultBuildPrettyMeta (route) {
72
80
  return Object.assign({}, cleanKeys)
73
81
  }
74
82
 
75
- function defaultErrorHandler (error, request, reply) {
76
- if (reply.statusCode < 500) {
77
- reply.log.info(
78
- { res: reply, err: error },
79
- error && error.message
80
- )
81
- } else {
82
- reply.log.error(
83
- { req: request, res: reply, err: error },
84
- error && error.message
85
- )
86
- }
87
- reply.send(error)
88
- }
89
-
90
83
  function fastify (options) {
91
84
  // Options validations
92
85
  options = options || {}
@@ -111,7 +104,6 @@ function fastify (options) {
111
104
  const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
112
105
  const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
113
106
  const disableRequestLogging = options.disableRequestLogging || false
114
- const exposeHeadRoutes = options.exposeHeadRoutes != null ? options.exposeHeadRoutes : false
115
107
 
116
108
  const ajvOptions = Object.assign({
117
109
  customOptions: {},
@@ -133,6 +125,7 @@ function fastify (options) {
133
125
  // Update the options with the fixed values
134
126
  options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout
135
127
  options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout
128
+ options.forceCloseConnections = typeof options.forceCloseConnections === 'boolean' ? options.forceCloseConnections : defaultInitOptions.forceCloseConnections
136
129
  options.maxRequestsPerSocket = options.maxRequestsPerSocket || defaultInitOptions.maxRequestsPerSocket
137
130
  options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout
138
131
  options.logger = logger
@@ -143,9 +136,12 @@ function fastify (options) {
143
136
  options.disableRequestLogging = disableRequestLogging
144
137
  options.ajv = ajvOptions
145
138
  options.clientErrorHandler = options.clientErrorHandler || defaultClientErrorHandler
146
- options.exposeHeadRoutes = exposeHeadRoutes
147
139
 
148
140
  const initialConfig = getSecuredInitialConfig(options)
141
+ const keepAliveConnections = options.forceCloseConnections === true ? new Set() : noopSet()
142
+
143
+ // exposeHeadRoutes have its defult set from the validatorfrom the validatorfrom the validatorfrom the validator
144
+ options.exposeHeadRoutes = initialConfig.exposeHeadRoutes
149
145
 
150
146
  let constraints = options.constraints
151
147
  if (options.versioning) {
@@ -169,14 +165,15 @@ function fastify (options) {
169
165
  // Default router
170
166
  const router = buildRouting({
171
167
  config: {
172
- defaultRoute: defaultRoute,
173
- onBadUrl: onBadUrl,
174
- constraints: constraints,
168
+ defaultRoute,
169
+ onBadUrl,
170
+ constraints,
175
171
  ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,
176
172
  maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
177
173
  caseSensitive: options.caseSensitive,
178
174
  buildPrettyMeta: defaultBuildPrettyMeta
179
- }
175
+ },
176
+ keepAliveConnections
180
177
  })
181
178
 
182
179
  // 404 router, used for handling encapsulated 404 handlers
@@ -200,8 +197,10 @@ function fastify (options) {
200
197
  closing: false,
201
198
  started: false
202
199
  },
200
+ [kKeepAliveConnections]: keepAliveConnections,
203
201
  [kOptions]: options,
204
202
  [kChildren]: [],
203
+ [kServerBindings]: [],
205
204
  [kBodyLimit]: bodyLimit,
206
205
  [kRoutePrefix]: '',
207
206
  [kLogLevel]: '',
@@ -209,7 +208,7 @@ function fastify (options) {
209
208
  [kHooks]: new Hooks(),
210
209
  [kSchemaController]: schemaController,
211
210
  [kSchemaErrorFormatter]: null,
212
- [kErrorHandler]: defaultErrorHandler,
211
+ [kErrorHandler]: buildErrorHandler(),
213
212
  [kReplySerializerDefault]: null,
214
213
  [kContentTypeParser]: new ContentTypeParser(
215
214
  bodyLimit,
@@ -259,17 +258,19 @@ function fastify (options) {
259
258
  },
260
259
  // expose logger instance
261
260
  log: logger,
261
+ // type provider
262
+ withTypeProvider,
262
263
  // hooks
263
- addHook: addHook,
264
+ addHook,
264
265
  // schemas
265
- addSchema: addSchema,
266
+ addSchema,
266
267
  getSchema: schemaController.getSchema.bind(schemaController),
267
268
  getSchemas: schemaController.getSchemas.bind(schemaController),
268
- setValidatorCompiler: setValidatorCompiler,
269
- setSerializerCompiler: setSerializerCompiler,
270
- setSchemaController: setSchemaController,
271
- setReplySerializer: setReplySerializer,
272
- setSchemaErrorFormatter: setSchemaErrorFormatter,
269
+ setValidatorCompiler,
270
+ setSerializerCompiler,
271
+ setSchemaController,
272
+ setReplySerializer,
273
+ setSchemaErrorFormatter,
273
274
  // custom parsers
274
275
  addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,
275
276
  hasContentTypeParser: ContentTypeParser.helpers.hasContentTypeParser,
@@ -285,8 +286,14 @@ function fastify (options) {
285
286
  close: null,
286
287
  printPlugins: null,
287
288
  // http server
288
- listen: listen,
289
- server: server,
289
+ listen,
290
+ server,
291
+ addresses: function () {
292
+ /* istanbul ignore next */
293
+ const binded = this[kServerBindings].map(b => b.address())
294
+ binded.push(this.server.address())
295
+ return binded.filter(adr => adr)
296
+ },
290
297
  // extend fastify objects
291
298
  decorate: decorator.add,
292
299
  hasDecorator: decorator.exist,
@@ -295,19 +302,16 @@ function fastify (options) {
295
302
  hasRequestDecorator: decorator.existRequest,
296
303
  hasReplyDecorator: decorator.existReply,
297
304
  // fake http injection
298
- inject: inject,
305
+ inject,
299
306
  // pretty print of the registered routes
300
307
  printRoutes,
301
308
  // custom error handling
302
- setNotFoundHandler: setNotFoundHandler,
303
- setErrorHandler: setErrorHandler,
309
+ setNotFoundHandler,
310
+ setErrorHandler,
304
311
  // Set fastify initial configuration options read-only object
305
312
  initialConfig
306
313
  }
307
314
 
308
- fastify[kReply].prototype.server = fastify
309
- fastify[kRequest].prototype.server = fastify
310
-
311
315
  Object.defineProperties(fastify, {
312
316
  pluginName: {
313
317
  get () {
@@ -331,16 +335,11 @@ function fastify (options) {
331
335
  },
332
336
  errorHandler: {
333
337
  get () {
334
- return this[kErrorHandler]
338
+ return this[kErrorHandler].func
335
339
  }
336
340
  }
337
341
  })
338
342
 
339
- // We are adding `use` to the fastify prototype so the user
340
- // can still access it (and get the expected error), but `decorate`
341
- // will not detect it, and allow the user to override it.
342
- Object.setPrototypeOf(fastify, { use })
343
-
344
343
  if (options.schemaErrorFormatter) {
345
344
  validateSchemaErrorFormatter(options.schemaErrorFormatter)
346
345
  fastify[kSchemaErrorFormatter] = options.schemaErrorFormatter.bind(fastify)
@@ -375,6 +374,15 @@ function fastify (options) {
375
374
  if (fastify[kState].listening) {
376
375
  // No new TCP connections are accepted
377
376
  instance.server.close(done)
377
+
378
+ for (const conn of fastify[kKeepAliveConnections]) {
379
+ // We must invoke the destroy method instead of merely unreffing
380
+ // the sockets. If we only unref, then the callback passed to
381
+ // `fastify.close` will never be invoked; nor will any of the
382
+ // registered `onClose` hooks.
383
+ conn.destroy()
384
+ fastify[kKeepAliveConnections].delete(conn)
385
+ }
378
386
  } else {
379
387
  done(null)
380
388
  }
@@ -483,6 +491,13 @@ function fastify (options) {
483
491
  }
484
492
 
485
493
  function manageErr (err) {
494
+ // If the error comes out of Avvio's Error codes
495
+ // We create a make and preserve the previous error
496
+ // as cause
497
+ err = err != null && AVVIO_ERRORS_MAP[err.code] != null
498
+ ? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))
499
+ : err
500
+
486
501
  if (cb) {
487
502
  if (err) {
488
503
  cb(err)
@@ -498,15 +513,16 @@ function fastify (options) {
498
513
  }
499
514
  }
500
515
 
501
- function use () {
502
- throw new FST_ERR_MISSING_MIDDLEWARE()
516
+ // Used exclusively in TypeScript contexts to enable auto type inference from JSON schema.
517
+ function withTypeProvider () {
518
+ return this
503
519
  }
504
520
 
505
521
  // wrapper that we expose to the user for hooks handling
506
522
  function addHook (name, fn) {
507
523
  throwIfAlreadyStarted('Cannot call "addHook" when fastify instance is already started!')
508
524
 
509
- if (name === 'onSend' || name === 'preSerialization' || name === 'onError') {
525
+ if (name === 'onSend' || name === 'preSerialization' || name === 'onError' || name === 'preParsing') {
510
526
  if (fn.constructor.name === 'AsyncFunction' && fn.length === 4) {
511
527
  throw new Error('Async function has too many arguments. Async hooks should not use the \'done\' argument.')
512
528
  }
@@ -514,7 +530,7 @@ function fastify (options) {
514
530
  if (fn.constructor.name === 'AsyncFunction' && fn.length !== 0) {
515
531
  throw new Error('Async function has too many arguments. Async hooks should not use the \'done\' argument.')
516
532
  }
517
- } else if (name !== 'preParsing') {
533
+ } else {
518
534
  if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
519
535
  throw new Error('Async function has too many arguments. Async hooks should not use the \'done\' argument.')
520
536
  }
@@ -524,6 +540,9 @@ function fastify (options) {
524
540
  this.onClose(fn)
525
541
  } else if (name === 'onReady') {
526
542
  this[kHooks].add(name, fn)
543
+ } else if (name === 'onRoute') {
544
+ this[kHooks].validate(name, fn)
545
+ this[kHooks].add(name, fn)
527
546
  } else {
528
547
  this.after((err, done) => {
529
548
  _addHook.call(this, name, fn)
@@ -652,7 +671,7 @@ function fastify (options) {
652
671
  function setErrorHandler (func) {
653
672
  throwIfAlreadyStarted('Cannot call "setErrorHandler" when fastify instance is already started!')
654
673
 
655
- this[kErrorHandler] = func.bind(this)
674
+ this[kErrorHandler] = buildErrorHandler(this[kErrorHandler], func.bind(this))
656
675
  return this
657
676
  }
658
677