fastify 3.17.0 → 3.18.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.
package/GOVERNANCE.md CHANGED
@@ -80,7 +80,7 @@ The consensus to grant a new candidate Collaborator status is reached when:
80
80
  - at least one of the Lead Maintainers approve
81
81
  - at least two of the Team Members approve
82
82
 
83
- After these conditions are satisfied, the [onboarding process](#onboarding-collaborators) may start.
83
+ After these conditions are satisfied, the [onboarding process](CONTRIBUTING.md#onboarding-collaborators) may start.
84
84
 
85
85
 
86
86
  ## Lead Maintainers nominations
package/docs/Ecosystem.md CHANGED
@@ -63,6 +63,7 @@ Plugins maintained by the Fastify team are listed under [Core](#core) while plug
63
63
  - [`@mgcrea/fastify-session-sodium-crypto`](https://github.com/mgcrea/fastify-session-sodium-crypto) Fast sodium-based crypto for @mgcrea/fastify-session
64
64
  - [`@mgcrea/fastify-session`](https://github.com/mgcrea/fastify-session) Session plugin for Fastify that supports both stateless and stateful sessions
65
65
  - [`@mgcrea/pino-pretty-compact`](https://github.com/mgcrea/pino-pretty-compact) A custom compact pino-base prettifier
66
+ - [`@trubavuong/fastify-seaweedfs`](https://github.com/trubavuong/fastify-seaweedfs) SeaweedFS for Fastify
66
67
  - [`apollo-server-fastify`](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-fastify) Run an [Apollo Server](https://github.com/apollographql/apollo-server) to serve GraphQL with Fastify.
67
68
  - [`arecibo`](https://github.com/nucleode/arecibo) Fastify ping responder for Kubernetes Liveness and Readiness Probes.
68
69
  - [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware for CLS-based request ID generation. An out-of-the-box solution for adding request IDs into your logs.
package/docs/Server.md CHANGED
@@ -288,7 +288,7 @@ const fastify = Fastify({ trustProxy: true })
288
288
  }
289
289
  ```
290
290
 
291
- For more examples, refer to the [`@fastify/proxy-addr`](https://www.npmjs.com/package/@fastify/proxy-addr) package.
291
+ For more examples, refer to the [`proxy-addr`](https://www.npmjs.com/package/proxy-addr) package.
292
292
 
293
293
  You may access the `ip`, `ips`, `hostname` and `protocol` values on the [`request`](Request.md) object.
294
294
 
@@ -967,6 +967,8 @@ fastify.register(function (instance, options, done) {
967
967
  }, { prefix: '/v1' })
968
968
  ```
969
969
 
970
+ Fastify calls setNotFoundHandler to add a default 404 handler at startup before plugins are registered. If you would like to augment the behavior of the default 404 handler, for example with plugins, you can call setNotFoundHandler with no arguments `fastify.setNotFoundHandler()` within the context of these registered plugins.
971
+
970
972
  <a name="set-error-handler"></a>
971
973
  #### setErrorHandler
972
974
 
@@ -1026,6 +1028,31 @@ fastify.ready(() => {
1026
1028
  })
1027
1029
  ```
1028
1030
 
1031
+ `fastify.printRoutes({ includeMeta: (true | []) })` will display properties from the `route.store` object for each displayed route. This can be an `array` of keys (e.g. `['onRequest', Symbol('key')]`), or `true` to display all properties. A shorthand option, `fastify.printRoutes({ includeHooks: true })` will include all [hooks](https://www.fastify.io/docs/latest/Hooks/).
1032
+
1033
+ ```js
1034
+ console.log(fastify.printRoutes({ includeHooks: true, includeMeta: ['metaProperty'] }))
1035
+ // └── /
1036
+ // ├── test (GET)
1037
+ // │ • (onRequest) ["anonymous()","namedFunction()"]
1038
+ // │ • (metaProperty) "value"
1039
+ // │ └── /hello (GET)
1040
+ // └── hel
1041
+ // ├── lo/world (GET)
1042
+ // │ • (onTimeout) ["anonymous()"]
1043
+ // └── licopter (GET)
1044
+
1045
+ console.log(fastify.printRoutes({ includeHooks: true }))
1046
+ // └── /
1047
+ // ├── test (GET)
1048
+ // │ • (onRequest) ["anonymous()","namedFunction()"]
1049
+ // │ └── /hello (GET)
1050
+ // └── hel
1051
+ // ├── lo/world (GET)
1052
+ // │ • (onTimeout) ["anonymous()"]
1053
+ // └── licopter (GET)
1054
+ ```
1055
+
1029
1056
  <a name="print-plugins"></a>
1030
1057
  #### printPlugins
1031
1058
 
@@ -1042,9 +1069,9 @@ fastify.ready(() => {
1042
1069
  console.error(fastify.printPlugins())
1043
1070
  // will output the following to stderr:
1044
1071
  // └── root
1045
- // ├── foo
1046
- // │ └── bar
1047
- // └── baz
1072
+ // ├── foo
1073
+ // │ └── bar
1074
+ // └── baz
1048
1075
  })
1049
1076
  ```
1050
1077
 
@@ -265,6 +265,11 @@ In the last example we used interfaces to define the types for the request query
265
265
  const { username, password } = request.query
266
266
  done(username !== 'admin' ? new Error('Must be admin') : undefined)
267
267
  }
268
+ // or if using async
269
+ // preValidation: async (request, reply) => {
270
+ // const { username, password } = request.query
271
+ // return username !== "admin" ? new Error("Must be admin") : undefined;
272
+ // }
268
273
  }, async (request, reply) => {
269
274
  const customerHeader = request.headers['h-Custom']
270
275
  // do something with request data
@@ -281,13 +286,15 @@ In the last example we used interfaces to define the types for the request query
281
286
  querystring: QuerystringSchema,
282
287
  headers: HeadersSchema
283
288
  },
284
- preHandler: (request, reply) => {
289
+ preHandler: (request, reply, done) => {
285
290
  const { username, password } = request.query
286
291
  const customerHeader = request.headers['h-Custom']
292
+ done()
287
293
  },
288
294
  handler: (request, reply) => {
289
295
  const { username, password } = request.query
290
296
  const customerHeader = request.headers['h-Custom']
297
+ reply.status(200).send({username});
291
298
  }
292
299
  })
293
300
 
@@ -331,6 +338,7 @@ const todo = {
331
338
  With the provided type `FromSchema` you can build a type from your schema and use it in your handler.
332
339
 
333
340
  ```typescript
341
+ import { FromSchema } from "json-schema-to-ts";
334
342
  fastify.post<{ Body: FromSchema<typeof todo> }>(
335
343
  '/todo',
336
344
  {
package/fastify.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import * as http from 'http'
2
2
  import * as http2 from 'http2'
3
3
  import * as https from 'https'
4
- import * as LightMyRequest from 'light-my-request'
5
4
  import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
6
5
 
7
6
  import { FastifyRequest, RequestGenericInterface } from './types/request'
@@ -159,6 +158,7 @@ export interface ValidationResult {
159
158
  }
160
159
 
161
160
  /* Export all additional types */
161
+ export type { Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse, CallbackFunc as LightMyRequestCallback } from 'light-my-request'
162
162
  export { FastifyRequest, RequestGenericInterface } from './types/request'
163
163
  export { FastifyReply } from './types/reply'
164
164
  export { FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin } from './types/plugin'
package/fastify.js CHANGED
@@ -4,9 +4,8 @@ const Avvio = require('avvio')
4
4
  const http = require('http')
5
5
  const querystring = require('querystring')
6
6
  let lightMyRequest
7
- let version
8
- let versionLoaded = false
9
7
 
8
+ const { version } = require('./package.json')
10
9
  const {
11
10
  kAvvioBoot,
12
11
  kChildren,
@@ -35,7 +34,7 @@ const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTI
35
34
  const decorator = require('./lib/decorate')
36
35
  const ContentTypeParser = require('./lib/contentTypeParser')
37
36
  const SchemaController = require('./lib/schema-controller')
38
- const { Hooks, hookRunnerApplication } = require('./lib/hooks')
37
+ const { Hooks, hookRunnerApplication, supportedHooks } = require('./lib/hooks')
39
38
  const { createLogger } = require('./lib/logger')
40
39
  const pluginUtils = require('./lib/pluginUtils')
41
40
  const reqIdGenFactory = require('./lib/reqIdGenFactory')
@@ -58,6 +57,19 @@ const onBadUrlContext = {
58
57
  onError: []
59
58
  }
60
59
 
60
+ function defaultBuildPrettyMeta (route) {
61
+ // return a shallow copy of route's sanitized context
62
+
63
+ const cleanKeys = {}
64
+ const allowedProps = ['errorHandler', 'logLevel', 'logSerializers']
65
+
66
+ allowedProps.concat(supportedHooks).forEach(k => {
67
+ cleanKeys[k] = route.store[k]
68
+ })
69
+
70
+ return Object.assign({}, cleanKeys)
71
+ }
72
+
61
73
  function defaultErrorHandler (error, request, reply) {
62
74
  if (reply.statusCode < 500) {
63
75
  reply.log.info(
@@ -158,7 +170,8 @@ function fastify (options) {
158
170
  constraints: constraints,
159
171
  ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,
160
172
  maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
161
- caseSensitive: options.caseSensitive
173
+ caseSensitive: options.caseSensitive,
174
+ buildPrettyMeta: defaultBuildPrettyMeta
162
175
  }
163
176
  })
164
177
 
@@ -278,7 +291,7 @@ function fastify (options) {
278
291
  // fake http injection
279
292
  inject: inject,
280
293
  // pretty print of the registered routes
281
- printRoutes: router.printRoutes,
294
+ printRoutes,
282
295
  // custom error handling
283
296
  setNotFoundHandler: setNotFoundHandler,
284
297
  setErrorHandler: setErrorHandler,
@@ -309,9 +322,6 @@ function fastify (options) {
309
322
  },
310
323
  version: {
311
324
  get () {
312
- if (versionLoaded === false) {
313
- version = loadVersion()
314
- }
315
325
  return version
316
326
  }
317
327
  },
@@ -626,6 +636,12 @@ function fastify (options) {
626
636
  this[kErrorHandler] = func.bind(this)
627
637
  return this
628
638
  }
639
+
640
+ function printRoutes (opts = {}) {
641
+ // includeHooks:true - shortcut to include all supported hooks exported by fastify.Hooks
642
+ opts.includeMeta = opts.includeHooks ? opts.includeMeta ? supportedHooks.concat(opts.includeMeta) : supportedHooks : opts.includeMeta
643
+ return router.printRoutes(opts)
644
+ }
629
645
  }
630
646
 
631
647
  function validateSchemaErrorFormatter (schemaErrorFormatter) {
@@ -655,20 +671,6 @@ function wrapRouting (httpHandler, { rewriteUrl, logger }) {
655
671
  }
656
672
  }
657
673
 
658
- function loadVersion () {
659
- versionLoaded = true
660
- const fs = require('fs')
661
- const path = require('path')
662
- try {
663
- const pkgPath = path.join(__dirname, 'package.json')
664
- fs.accessSync(pkgPath, fs.constants.R_OK)
665
- const pkg = JSON.parse(fs.readFileSync(pkgPath))
666
- return pkg.name === 'fastify' ? pkg.version : undefined
667
- } catch (e) {
668
- return undefined
669
- }
670
- }
671
-
672
674
  /**
673
675
  * These export configurations enable JS and TS developers
674
676
  * to consumer fastify in whatever way best suits their needs.
package/lib/hooks.js CHANGED
@@ -244,5 +244,6 @@ module.exports = {
244
244
  onSendHookRunner,
245
245
  hookIterator,
246
246
  hookRunnerApplication,
247
- lifecycleHooks
247
+ lifecycleHooks,
248
+ supportedHooks
248
249
  }
@@ -109,9 +109,7 @@ function registerPluginName (fn) {
109
109
 
110
110
  function registerPlugin (fn) {
111
111
  registerPluginName.call(this, fn)
112
- if (this.version !== undefined) {
113
- checkVersion.call(this, fn)
114
- }
112
+ checkVersion.call(this, fn)
115
113
  checkDecorators.call(this, fn)
116
114
  checkDependencies.call(this, fn)
117
115
  return shouldSkipOverride(fn)
package/lib/request.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const proxyAddr = require('@fastify/proxy-addr')
3
+ const proxyAddr = require('proxy-addr')
4
4
  const semver = require('semver')
5
5
  const warning = require('./warnings')
6
6
 
package/lib/route.js CHANGED
@@ -227,7 +227,7 @@ function buildRouting (options) {
227
227
  }
228
228
  const constraints = opts.constraints || {}
229
229
  if (opts.version) {
230
- warning.emit('FSTDEP006')
230
+ warning.emit('FSTDEP008')
231
231
  constraints.version = opts.version
232
232
  }
233
233
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "3.17.0",
3
+ "version": "3.18.0",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -177,7 +177,6 @@
177
177
  },
178
178
  "dependencies": {
179
179
  "@fastify/ajv-compiler": "^1.0.0",
180
- "@fastify/proxy-addr": "^3.0.0",
181
180
  "abstract-logging": "^2.0.0",
182
181
  "avvio": "^7.1.2",
183
182
  "fast-json-stringify": "^2.5.2",
@@ -187,6 +186,7 @@
187
186
  "flatstr": "^1.0.12",
188
187
  "light-my-request": "^4.2.0",
189
188
  "pino": "^6.2.1",
189
+ "proxy-addr": "^2.0.7",
190
190
  "readable-stream": "^3.4.0",
191
191
  "rfdc": "^1.1.4",
192
192
  "secure-json-parse": "^2.0.0",
@@ -121,3 +121,92 @@ test('pretty print - nested plugins', t => {
121
121
  t.match(tree, 'baz')
122
122
  })
123
123
  })
124
+
125
+ test('pretty print - commonPrefix', t => {
126
+ t.plan(4)
127
+
128
+ const fastify = Fastify()
129
+ fastify.get('/hello', () => {})
130
+ fastify.put('/hello', () => {})
131
+ fastify.get('/helicopter', () => {})
132
+
133
+ fastify.ready(() => {
134
+ const radixTree = fastify.printRoutes()
135
+ const flatTree = fastify.printRoutes({ commonPrefix: false })
136
+
137
+ const radixExpected = `└── /
138
+ ├── hel
139
+ │ ├── lo (GET)
140
+ │ └── icopter (GET)
141
+ └── hello (PUT)
142
+ `
143
+ const flatExpected = `└── / (-)
144
+ ├── helicopter (GET)
145
+ └── hello (GET, PUT)
146
+ `
147
+ t.equal(typeof radixTree, 'string')
148
+ t.equal(typeof flatTree, 'string')
149
+ t.equal(radixTree, radixExpected)
150
+ t.equal(flatTree, flatExpected)
151
+ })
152
+ })
153
+
154
+ test('pretty print - includeMeta, includeHooks', t => {
155
+ t.plan(6)
156
+
157
+ const fastify = Fastify()
158
+ const onTimeout = () => {}
159
+ fastify.get('/hello', () => {})
160
+ fastify.put('/hello', () => {})
161
+ fastify.get('/helicopter', () => {})
162
+
163
+ fastify.addHook('onRequest', () => {})
164
+ fastify.addHook('onTimeout', onTimeout)
165
+
166
+ fastify.ready(() => {
167
+ const radixTree = fastify.printRoutes({ includeHooks: true, includeMeta: ['errorHandler'] })
168
+ const flatTree = fastify.printRoutes({ commonPrefix: false, includeHooks: true, includeMeta: ['errorHandler'] })
169
+ const hooksOnly = fastify.printRoutes({ commonPrefix: false, includeHooks: true })
170
+
171
+ const radixExpected = `└── /
172
+ ├── hel
173
+ │ ├── lo (GET)
174
+ │ │ • (onTimeout) ["onTimeout()"]
175
+ │ │ • (onRequest) ["anonymous()"]
176
+ │ │ • (errorHandler) "defaultErrorHandler()"
177
+ │ └── icopter (GET)
178
+ │ • (onTimeout) ["onTimeout()"]
179
+ │ • (onRequest) ["anonymous()"]
180
+ │ • (errorHandler) "defaultErrorHandler()"
181
+ └── hello (PUT)
182
+ • (onTimeout) ["onTimeout()"]
183
+ • (onRequest) ["anonymous()"]
184
+ • (errorHandler) "defaultErrorHandler()"
185
+ `
186
+ const flatExpected = `└── / (-)
187
+ ├── helicopter (GET)
188
+ │ • (onTimeout) ["onTimeout()"]
189
+ │ • (onRequest) ["anonymous()"]
190
+ │ • (errorHandler) "defaultErrorHandler()"
191
+ └── hello (GET, PUT)
192
+ • (onTimeout) ["onTimeout()"]
193
+ • (onRequest) ["anonymous()"]
194
+ • (errorHandler) "defaultErrorHandler()"
195
+ `
196
+
197
+ const hooksOnlyExpected = `└── / (-)
198
+ ├── helicopter (GET)
199
+ │ • (onTimeout) ["onTimeout()"]
200
+ │ • (onRequest) ["anonymous()"]
201
+ └── hello (GET, PUT)
202
+ • (onTimeout) ["onTimeout()"]
203
+ • (onRequest) ["anonymous()"]
204
+ `
205
+ t.equal(typeof radixTree, 'string')
206
+ t.equal(typeof flatTree, 'string')
207
+ t.equal(typeof hooksOnlyExpected, 'string')
208
+ t.equal(radixTree, radixExpected)
209
+ t.equal(flatTree, flatExpected)
210
+ t.equal(hooksOnly, hooksOnlyExpected)
211
+ })
212
+ })
@@ -3,12 +3,15 @@ import fastify, {
3
3
  FastifyInstance,
4
4
  FastifyPlugin,
5
5
  FastifyPluginAsync,
6
- FastifyPluginCallback
6
+ FastifyPluginCallback,
7
+ LightMyRequestChain,
8
+ LightMyRequestResponse,
9
+ LightMyRequestCallback,
10
+ InjectOptions
7
11
  } from '../../fastify'
8
12
  import * as http from 'http'
9
13
  import * as https from 'https'
10
14
  import * as http2 from 'http2'
11
- import { Chain as LightMyRequestChain } from 'light-my-request'
12
15
  import { expectType, expectError, expectAssignable } from 'tsd'
13
16
  import { FastifyLoggerInstance } from '../../types/logger'
14
17
  import { Socket } from 'net'
@@ -27,6 +30,14 @@ expectType<LightMyRequestChain>(fastify({ http2: true, https: {} }).inject())
27
30
  expectError(fastify<http2.Http2Server>({ http2: false })) // http2 option must be true
28
31
  expectError(fastify<http2.Http2SecureServer>({ http2: false })) // http2 option must be true
29
32
 
33
+ // light-my-request
34
+ expectAssignable<InjectOptions>({ query: '' })
35
+ fastify({ http2: true, https: {} }).inject().then((resp) => {
36
+ expectAssignable<LightMyRequestResponse>(resp)
37
+ })
38
+ const lightMyRequestCallback: LightMyRequestCallback = (err: Error, response: LightMyRequestResponse) => {}
39
+ fastify({ http2: true, https: {} }).inject({}, lightMyRequestCallback)
40
+
30
41
  // server options
31
42
  expectAssignable<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true }))
32
43
  expectAssignable<FastifyInstance>(fastify({ ignoreTrailingSlash: true }))
@@ -632,7 +632,7 @@ test('Should trigger a warning when a versioned route is registered via version
632
632
  t.plan(4)
633
633
 
634
634
  function onWarning (code) {
635
- t.equal(code, 'FSTDEP006')
635
+ t.equal(code, 'FSTDEP008')
636
636
  }
637
637
  const warning = {
638
638
  emit: onWarning
@@ -1,43 +0,0 @@
1
- 'use strict'
2
-
3
- const t = require('tap')
4
- const test = t.test
5
- const proxyquire = require('proxyquire')
6
-
7
- test('should output an undefined version in case of package.json not available', t => {
8
- const Fastify = proxyquire('../..', { fs: { accessSync: () => { throw Error('error') } } })
9
- t.plan(1)
10
- const srv = Fastify()
11
- t.equal(srv.version, undefined)
12
- })
13
-
14
- test('should output an undefined version in case of package.json is not the fastify one', t => {
15
- const Fastify = proxyquire('../..', { fs: { accessSync: () => { }, readFileSync: () => JSON.stringify({ name: 'foo', version: '6.6.6' }) } })
16
- t.plan(1)
17
- const srv = Fastify()
18
- t.equal(srv.version, undefined)
19
- })
20
-
21
- test('should skip the version check if the version is undefined', t => {
22
- const Fastify = proxyquire('../..', { fs: { accessSync: () => { }, readFileSync: () => JSON.stringify({ name: 'foo', version: '6.6.6' }) } })
23
- t.plan(3)
24
- const srv = Fastify()
25
- t.equal(srv.version, undefined)
26
-
27
- plugin[Symbol.for('skip-override')] = false
28
- plugin[Symbol.for('plugin-meta')] = {
29
- name: 'plugin',
30
- fastify: '>=99.0.0'
31
- }
32
-
33
- srv.register(plugin)
34
-
35
- srv.ready((err) => {
36
- t.error(err)
37
- t.pass('everything right')
38
- })
39
-
40
- function plugin (instance, opts, done) {
41
- done()
42
- }
43
- })