fastify 4.0.3 → 4.2.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 (47) hide show
  1. package/.eslintrc +1 -0
  2. package/README.md +13 -14
  3. package/docs/Guides/Database.md +7 -7
  4. package/docs/Guides/Delay-Accepting-Requests.md +1 -1
  5. package/docs/Guides/Ecosystem.md +29 -16
  6. package/docs/Guides/Migration-Guide-V4.md +86 -1
  7. package/docs/Guides/Plugins-Guide.md +5 -0
  8. package/docs/Guides/Serverless.md +23 -10
  9. package/docs/Reference/Hooks.md +52 -0
  10. package/docs/Reference/Plugins.md +1 -1
  11. package/docs/Reference/Server.md +1 -1
  12. package/docs/Reference/Type-Providers.md +3 -17
  13. package/docs/Reference/TypeScript.md +65 -28
  14. package/docs/Reference/Validation-and-Serialization.md +11 -0
  15. package/docs/index.md +1 -1
  16. package/fastify.d.ts +3 -3
  17. package/fastify.js +19 -19
  18. package/integration/server.js +27 -0
  19. package/integration/test.sh +23 -0
  20. package/lib/context.js +5 -2
  21. package/lib/error-serializer.js +24 -27
  22. package/lib/handleRequest.js +1 -1
  23. package/lib/reply.js +22 -21
  24. package/lib/route.js +39 -29
  25. package/lib/symbols.js +2 -1
  26. package/lib/validation.js +2 -0
  27. package/package.json +10 -10
  28. package/test/404s.test.js +2 -2
  29. package/test/build/error-serializer.test.js +9 -2
  30. package/test/hooks.test.js +21 -0
  31. package/test/internals/reply.test.js +12 -0
  32. package/test/pretty-print.test.js +3 -3
  33. package/test/reply-error.test.js +1 -1
  34. package/test/schema-feature.test.js +2 -2
  35. package/test/schema-validation.test.js +71 -0
  36. package/test/stream.test.js +1 -1
  37. package/test/types/fastify.test-d.ts +24 -2
  38. package/test/types/instance.test-d.ts +5 -2
  39. package/test/types/register.test-d.ts +77 -2
  40. package/test/types/request.test-d.ts +8 -4
  41. package/test/types/type-provider.test-d.ts +11 -2
  42. package/test/validation-error-handling.test.js +38 -1
  43. package/types/instance.d.ts +59 -91
  44. package/types/register.d.ts +9 -7
  45. package/types/route.d.ts +10 -12
  46. package/types/schema.d.ts +5 -2
  47. package/types/type-provider.d.ts +12 -5
package/lib/route.js CHANGED
@@ -8,7 +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
+ const { kRequestAcceptVersion, kRouteByFastify } = require('./symbols')
12
12
 
13
13
  const {
14
14
  compileSchemasForValidation,
@@ -113,7 +113,7 @@ function buildRouting (options) {
113
113
  }
114
114
 
115
115
  // Convert shorthand to extended route declaration
116
- function prepareRoute (method, url, options, handler) {
116
+ function prepareRoute ({ method, url, options, handler, isFastify }) {
117
117
  if (typeof url !== 'string') {
118
118
  throw new FST_ERR_INVALID_URL(typeof url)
119
119
  }
@@ -140,11 +140,11 @@ function buildRouting (options) {
140
140
  handler: handler || (options && options.handler)
141
141
  })
142
142
 
143
- return route.call(this, options)
143
+ return route.call(this, { options, isFastify })
144
144
  }
145
145
 
146
146
  // Route management
147
- function route (options) {
147
+ function route ({ options, isFastify }) {
148
148
  // Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
149
149
  const opts = { ...options }
150
150
 
@@ -176,30 +176,30 @@ function buildRouting (options) {
176
176
  if (path === '/' && prefix.length > 0 && opts.method !== 'HEAD') {
177
177
  switch (opts.prefixTrailingSlash) {
178
178
  case 'slash':
179
- addNewRoute.call(this, path)
179
+ addNewRoute.call(this, { path, isFastify })
180
180
  break
181
181
  case 'no-slash':
182
- addNewRoute.call(this, '')
182
+ addNewRoute.call(this, { path: '', isFastify })
183
183
  break
184
184
  case 'both':
185
185
  default:
186
- addNewRoute.call(this, '')
186
+ addNewRoute.call(this, { path: '', isFastify })
187
187
  // If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
188
188
  if (ignoreTrailingSlash !== true && (ignoreDuplicateSlashes !== true || !prefix.endsWith('/'))) {
189
- addNewRoute.call(this, path, true)
189
+ addNewRoute.call(this, { path, prefixing: true, isFastify })
190
190
  }
191
191
  }
192
192
  } else if (path[0] === '/' && prefix.endsWith('/')) {
193
193
  // Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
194
- addNewRoute.call(this, path.slice(1))
194
+ addNewRoute.call(this, { path: path.slice(1), isFastify })
195
195
  } else {
196
- addNewRoute.call(this, path)
196
+ addNewRoute.call(this, { path, isFastify })
197
197
  }
198
198
 
199
199
  // chainable api
200
200
  return this
201
201
 
202
- function addNewRoute (path, prefixing = false) {
202
+ function addNewRoute ({ path, prefixing = false, isFastify = false }) {
203
203
  const url = prefix + path
204
204
 
205
205
  opts.url = url
@@ -241,7 +241,8 @@ function buildRouting (options) {
241
241
  attachValidation: opts.attachValidation,
242
242
  schemaErrorFormatter: opts.schemaErrorFormatter,
243
243
  replySerializer: this[kReplySerializerDefault],
244
- server: this
244
+ server: this,
245
+ isFastify
245
246
  })
246
247
 
247
248
  if (opts.version) {
@@ -249,13 +250,20 @@ function buildRouting (options) {
249
250
  constraints.version = opts.version
250
251
  }
251
252
 
252
- const headRouteExists = opts.method === 'HEAD' && router.find(opts.method, opts.url, constraints) != null
253
+ const headHandler = router.find('HEAD', opts.url, constraints)
254
+ const hasHEADHandler = headHandler != null
253
255
 
254
- // Check if the current route is not for a sibling HEAD one
255
- if (!headRouteExists) {
256
- try {
257
- router.on(opts.method, opts.url, { constraints }, routeHandler, context)
258
- } catch (error) {
256
+ // remove the head route created by fastify
257
+ if (hasHEADHandler && !context[kRouteByFastify] && headHandler.store[kRouteByFastify]) {
258
+ router.off(opts.method, opts.url, { constraints })
259
+ }
260
+
261
+ try {
262
+ router.on(opts.method, opts.url, { constraints }, routeHandler, context)
263
+ } catch (error) {
264
+ // any route insertion error created by fastify can be safely ignore
265
+ // because it only duplicate route for head
266
+ if (!context[kRouteByFastify]) {
259
267
  const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route '${opts.url}'`)
260
268
  if (isDuplicatedRoute) {
261
269
  throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
@@ -320,19 +328,21 @@ function buildRouting (options) {
320
328
  }
321
329
  })
322
330
 
323
- const { exposeHeadRoute } = opts
324
- const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
325
- const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
326
-
327
- if (shouldExposeHead && options.method === 'GET' && !headRouteExists) {
328
- const onSendHandlers = parseHeadOnSendHandlers(opts.onSend)
329
- prepareRoute.call(this, 'HEAD', path, { ...opts, onSend: onSendHandlers })
330
- } else if (headRouteExists && exposeHeadRoute) {
331
- warning.emit('FSTDEP007')
332
- }
333
-
334
331
  done(notHandledErr)
335
332
  })
333
+
334
+ // register head route in sync
335
+ // we must place it after the `this.after`
336
+ const { exposeHeadRoute } = opts
337
+ const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
338
+ const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
339
+
340
+ if (shouldExposeHead && options.method === 'GET' && !hasHEADHandler) {
341
+ const onSendHandlers = parseHeadOnSendHandlers(opts.onSend)
342
+ prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...opts, onSend: onSendHandlers }, isFastify: true })
343
+ } else if (hasHEADHandler && exposeHeadRoute) {
344
+ warning.emit('FSTDEP007')
345
+ }
336
346
  }
337
347
  }
338
348
 
package/lib/symbols.js CHANGED
@@ -47,7 +47,8 @@ const keys = {
47
47
  kTestInternals: Symbol('fastify.testInternals'),
48
48
  kErrorHandler: Symbol('fastify.errorHandler'),
49
49
  kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
50
- kKeepAliveConnections: Symbol('fastify.keepAliveConnections')
50
+ kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
51
+ kRouteByFastify: Symbol('fastify.routeByFastify')
51
52
  }
52
53
 
53
54
  module.exports = keys
package/lib/validation.js CHANGED
@@ -106,11 +106,13 @@ function validate (context, request) {
106
106
 
107
107
  function wrapValidationError (result, dataVar, schemaErrorFormatter) {
108
108
  if (result instanceof Error) {
109
+ result.statusCode = result.statusCode || 400
109
110
  result.validationContext = result.validationContext || dataVar
110
111
  return result
111
112
  }
112
113
 
113
114
  const error = schemaErrorFormatter(result, dataVar)
115
+ error.statusCode = error.statusCode || 400
114
116
  error.validation = result
115
117
  error.validationContext = dataVar
116
118
  return error
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.0.3",
3
+ "version": "4.2.1",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -17,7 +17,7 @@
17
17
  "lint:markdown": "markdownlint-cli2",
18
18
  "lint:standard": "standard | snazzy",
19
19
  "lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
20
- "prepublishOnly": "tap --no-check-coverage test/build/**.test.js",
20
+ "prepublishOnly": "PREPUBLISH=true tap --no-check-coverage test/build/**.test.js",
21
21
  "test": "npm run lint && npm run unit && npm run test:typescript",
22
22
  "test:ci": "npm run unit -- -R terse --cov --coverage-report=lcovonly && npm run test:typescript",
23
23
  "test:report": "npm run lint && npm run unit:report && npm run test:typescript",
@@ -126,9 +126,9 @@
126
126
  "homepage": "https://www.fastify.io/",
127
127
  "devDependencies": {
128
128
  "@fastify/pre-commit": "^2.0.2",
129
- "@sinclair/typebox": "^0.23.5",
129
+ "@sinclair/typebox": "^0.24.9",
130
130
  "@sinonjs/fake-timers": "^9.1.2",
131
- "@types/node": "^17.0.38",
131
+ "@types/node": "^18.0.0",
132
132
  "@typescript-eslint/eslint-plugin": "^5.27.0",
133
133
  "@typescript-eslint/parser": "^5.27.0",
134
134
  "ajv": "^8.11.0",
@@ -146,8 +146,8 @@
146
146
  "eslint-plugin-n": "^15.2.0",
147
147
  "eslint-plugin-promise": "^6.0.0",
148
148
  "fast-json-body": "^1.1.0",
149
- "fast-json-stringify": "^4.2.0",
150
- "fastify-plugin": "^3.0.1",
149
+ "fast-json-stringify": "^5.0.0",
150
+ "fastify-plugin": "^4.0.0",
151
151
  "fluent-json-schema": "^3.1.0",
152
152
  "form-data": "^4.0.0",
153
153
  "frameguard": "^4.0.0",
@@ -170,19 +170,19 @@
170
170
  "split2": "^4.1.0",
171
171
  "standard": "^17.0.0-2",
172
172
  "tap": "^16.2.0",
173
- "tsd": "^0.21.0",
173
+ "tsd": "^0.22.0",
174
174
  "typescript": "^4.7.2",
175
175
  "undici": "^5.4.0",
176
176
  "x-xss-protection": "^2.0.0",
177
177
  "yup": "^0.32.11"
178
178
  },
179
179
  "dependencies": {
180
- "@fastify/ajv-compiler": "^3.1.0",
180
+ "@fastify/ajv-compiler": "^3.1.1",
181
181
  "@fastify/error": "^3.0.0",
182
- "@fastify/fast-json-stringify-compiler": "^3.0.1",
182
+ "@fastify/fast-json-stringify-compiler": "^4.0.0",
183
183
  "abstract-logging": "^2.0.1",
184
184
  "avvio": "^8.1.3",
185
- "find-my-way": "^6.3.0",
185
+ "find-my-way": "^7.0.0",
186
186
  "light-my-request": "^5.0.0",
187
187
  "pino": "^8.0.0",
188
188
  "process-warning": "^2.0.0",
package/test/404s.test.js CHANGED
@@ -1320,7 +1320,7 @@ test('preHandler option for setNotFoundHandler', t => {
1320
1320
 
1321
1321
  // https://github.com/fastify/fastify/issues/2229
1322
1322
  t.test('preHandler hook in setNotFoundHandler should be called when callNotFound', { timeout: 40000 }, t => {
1323
- t.plan(2)
1323
+ t.plan(3)
1324
1324
  const fastify = Fastify()
1325
1325
 
1326
1326
  fastify.setNotFoundHandler({
@@ -1333,7 +1333,7 @@ test('preHandler option for setNotFoundHandler', t => {
1333
1333
  })
1334
1334
 
1335
1335
  fastify.post('/', function (req, reply) {
1336
- reply.callNotFound()
1336
+ t.equal(reply.callNotFound(), reply)
1337
1337
  })
1338
1338
 
1339
1339
  fastify.inject({
@@ -7,6 +7,10 @@ const path = require('path')
7
7
 
8
8
  const { code } = require('../../build/build-error-serializer')
9
9
 
10
+ function unifyLineBreak (str) {
11
+ return str.toString().replace(/\r\n/g, '\n')
12
+ }
13
+
10
14
  test('check generated code syntax', async (t) => {
11
15
  t.plan(1)
12
16
 
@@ -19,10 +23,13 @@ test('check generated code syntax', async (t) => {
19
23
  t.equal(result[0].fatalErrorCount, 0)
20
24
  })
21
25
 
22
- test('ensure the current error serializer is latest', async (t) => {
26
+ const isPrebublish = !!process.env.PREPUBLISH
27
+
28
+ test('ensure the current error serializer is latest', { skip: !isPrebublish }, async (t) => {
23
29
  t.plan(1)
24
30
 
25
31
  const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
26
32
 
27
- t.equal(current.toString(), code)
33
+ // line break should not be a problem depends on system
34
+ t.equal(unifyLineBreak(current), unifyLineBreak(code))
28
35
  })
@@ -799,6 +799,27 @@ test('onRoute hook with many prefix', t => {
799
799
  fastify.ready(err => { t.error(err) })
800
800
  })
801
801
 
802
+ test('onRoute hook should not be called when it registered after route', t => {
803
+ t.plan(3)
804
+ const fastify = Fastify()
805
+
806
+ fastify.addHook('onRoute', () => {
807
+ t.pass()
808
+ })
809
+
810
+ fastify.get('/', function (req, reply) {
811
+ reply.send()
812
+ })
813
+
814
+ fastify.addHook('onRoute', () => {
815
+ t.fail('should not be called')
816
+ })
817
+
818
+ fastify.ready(err => {
819
+ t.error(err)
820
+ })
821
+ })
822
+
802
823
  test('onResponse hook should log request error', t => {
803
824
  t.plan(4)
804
825
 
@@ -233,6 +233,10 @@ test('within an instance', t => {
233
233
  reply.redirect('/')
234
234
  })
235
235
 
236
+ fastify.get('/redirect-async', async function (req, reply) {
237
+ return reply.redirect('/')
238
+ })
239
+
236
240
  fastify.get('/redirect-code', function (req, reply) {
237
241
  reply.redirect(301, '/')
238
242
  })
@@ -412,6 +416,14 @@ test('within an instance', t => {
412
416
  })
413
417
  })
414
418
 
419
+ test('redirect with async function to `/` - 10', t => {
420
+ t.plan(1)
421
+
422
+ http.get('http://localhost:' + fastify.server.address().port + '/redirect-async', function (response) {
423
+ t.equal(response.statusCode, 302)
424
+ })
425
+ })
426
+
415
427
  t.end()
416
428
  })
417
429
  })
@@ -144,7 +144,7 @@ test('pretty print - commonPrefix', t => {
144
144
  `
145
145
  const flatExpected = `└── / (-)
146
146
  ├── helicopter (GET, HEAD)
147
- └── hello (GET, PUT, HEAD)
147
+ └── hello (GET, HEAD, PUT)
148
148
  `
149
149
  t.equal(typeof radixTree, 'string')
150
150
  t.equal(typeof flatTree, 'string')
@@ -200,7 +200,7 @@ test('pretty print - includeMeta, includeHooks', t => {
200
200
  │ • (onTimeout) ["onTimeout()"]
201
201
  │ • (onRequest) ["anonymous()"]
202
202
  │ • (errorHandler) "defaultErrorHandler()"
203
- └── hello (GET, PUT, HEAD)
203
+ └── hello (GET, HEAD, PUT)
204
204
  • (onTimeout) ["onTimeout()"]
205
205
  • (onRequest) ["anonymous()"]
206
206
  • (errorHandler) "defaultErrorHandler()"
@@ -210,7 +210,7 @@ test('pretty print - includeMeta, includeHooks', t => {
210
210
  ├── helicopter (GET, HEAD)
211
211
  │ • (onTimeout) ["onTimeout()"]
212
212
  │ • (onRequest) ["anonymous()"]
213
- └── hello (GET, PUT, HEAD)
213
+ └── hello (GET, HEAD, PUT)
214
214
  • (onTimeout) ["onTimeout()"]
215
215
  • (onRequest) ["anonymous()"]
216
216
  `
@@ -324,7 +324,7 @@ test('invalid schema - ajv', t => {
324
324
 
325
325
  fastify.setErrorHandler((err, request, reply) => {
326
326
  t.ok(Array.isArray(err.validation))
327
- reply.send('error')
327
+ reply.code(400).send('error')
328
328
  })
329
329
 
330
330
  fastify.inject({
@@ -206,7 +206,7 @@ test('Should throw of the schema does not exists in output', t => {
206
206
 
207
207
  fastify.ready(err => {
208
208
  t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')
209
- t.match(err.message, /^Failed building the serialization schema for GET: \/:id, due to error Cannot read propert.*/) // error from fast-json-strinfigy
209
+ t.match(err.message, /^Failed building the serialization schema for GET: \/:id, due to error Cannot find reference.*/) // error from fast-json-strinfigy
210
210
  })
211
211
  })
212
212
 
@@ -826,7 +826,7 @@ test('Validation context in validation result', t => {
826
826
  t.equal(err instanceof Error, true)
827
827
  t.ok(err.validation, 'detailed errors')
828
828
  t.equal(err.validationContext, 'body')
829
- reply.send()
829
+ reply.code(400).send()
830
830
  })
831
831
  fastify.post('/', {
832
832
  handler: echoParams,
@@ -4,6 +4,7 @@ const { test } = require('tap')
4
4
  const Fastify = require('..')
5
5
 
6
6
  const AJV = require('ajv')
7
+ const Schema = require('fluent-json-schema')
7
8
 
8
9
  const customSchemaCompilers = {
9
10
  body: new AJV({
@@ -946,3 +947,73 @@ test('Custom AJV settings on different parameters - pt2', t => {
946
947
  }
947
948
  })
948
949
  })
950
+
951
+ test("The same $id in route's schema must not overwrite others", t => {
952
+ t.plan(4)
953
+ const fastify = Fastify()
954
+
955
+ const UserSchema = Schema.object()
956
+ .id('http://mydomain.com/user')
957
+ .title('User schema')
958
+ .description('Contains all user fields')
959
+ .prop('id', Schema.integer())
960
+ .prop('username', Schema.string().minLength(4))
961
+ .prop('firstName', Schema.string().minLength(1))
962
+ .prop('lastName', Schema.string().minLength(1))
963
+ .prop('fullName', Schema.string().minLength(1))
964
+ .prop('email', Schema.string())
965
+ .prop('password', Schema.string().minLength(6))
966
+ .prop('bio', Schema.string())
967
+
968
+ const userCreateSchema = UserSchema.only([
969
+ 'username',
970
+ 'firstName',
971
+ 'lastName',
972
+ 'email',
973
+ 'bio',
974
+ 'password',
975
+ 'password_confirm'
976
+ ])
977
+ .required([
978
+ 'username',
979
+ 'firstName',
980
+ 'lastName',
981
+ 'email',
982
+ 'bio',
983
+ 'password'
984
+ ])
985
+
986
+ const userPatchSchema = UserSchema.only([
987
+ 'firstName',
988
+ 'lastName',
989
+ 'bio'
990
+ ])
991
+
992
+ fastify
993
+ .patch('/user/:id', {
994
+ schema: { body: userPatchSchema },
995
+ handler: () => { return 'ok' }
996
+ })
997
+ .post('/user', {
998
+ schema: { body: userCreateSchema },
999
+ handler: () => { return 'ok' }
1000
+ })
1001
+
1002
+ fastify.inject({
1003
+ method: 'POST',
1004
+ url: '/user',
1005
+ body: {}
1006
+ }, (err, res) => {
1007
+ t.error(err)
1008
+ t.same(res.json().message, "body must have required property 'username'")
1009
+ })
1010
+
1011
+ fastify.inject({
1012
+ url: '/user/1',
1013
+ method: 'PATCH',
1014
+ body: {}
1015
+ }, (err, res) => {
1016
+ t.error(err)
1017
+ t.same(res.payload, 'ok')
1018
+ })
1019
+ })
@@ -770,7 +770,7 @@ test('request terminated should not crash fastify', t => {
770
770
 
771
771
  reply.send(stream)
772
772
 
773
- await new Promise((resolve) => { setTimeout(resolve, 6).unref() })
773
+ await new Promise((resolve) => { setTimeout(resolve, 100).unref() })
774
774
 
775
775
  stream.push('<h1>should disply on second stream</h1>')
776
776
  stream.push(null)
@@ -7,8 +7,10 @@ import fastify, {
7
7
  LightMyRequestChain,
8
8
  LightMyRequestResponse,
9
9
  LightMyRequestCallback,
10
- InjectOptions, FastifyBaseLogger
10
+ InjectOptions, FastifyBaseLogger,
11
+ ValidationResult
11
12
  } from '../../fastify'
13
+ import { ErrorObject as AjvErrorObject } from 'ajv'
12
14
  import * as http from 'http'
13
15
  import * as https from 'https'
14
16
  import * as http2 from 'http2'
@@ -192,7 +194,18 @@ expectAssignable<FastifyInstance>(fastify({ frameworkErrors: () => { } }))
192
194
  expectAssignable<FastifyInstance>(fastify({
193
195
  rewriteUrl: (req) => req.url === '/hi' ? '/hello' : req.url!
194
196
  }))
195
- expectAssignable<FastifyInstance>(fastify({ schemaErrorFormatter: (errors, dataVar) => new Error() }))
197
+ expectAssignable<FastifyInstance>(fastify({
198
+ schemaErrorFormatter: (errors, dataVar) => {
199
+ console.log(
200
+ errors[0].keyword.toLowerCase(),
201
+ errors[0].message?.toLowerCase(),
202
+ errors[0].params,
203
+ errors[0].instancePath.toLowerCase(),
204
+ errors[0].schemaPath.toLowerCase()
205
+ )
206
+ return new Error()
207
+ }
208
+ }))
196
209
  expectAssignable<FastifyInstance>(fastify({
197
210
  clientErrorHandler: (err, socket) => {
198
211
  expectType<ConnectionError>(err)
@@ -208,3 +221,12 @@ fastify().then(fastifyInstance => expectAssignable<FastifyInstance>(fastifyInsta
208
221
  expectAssignable<FastifyPluginAsync>(async () => {})
209
222
  expectAssignable<FastifyPluginCallback>(() => {})
210
223
  expectAssignable<FastifyPlugin>(() => {})
224
+
225
+ const ajvErrorObject: AjvErrorObject = {
226
+ keyword: '',
227
+ instancePath: '',
228
+ schemaPath: '',
229
+ params: {},
230
+ message: ''
231
+ }
232
+ expectAssignable<ValidationResult>(ajvErrorObject)
@@ -11,7 +11,7 @@ import { HookHandlerDoneFunction } from '../../types/hooks'
11
11
  import { FastifyReply } from '../../types/reply'
12
12
  import { FastifyRequest } from '../../types/request'
13
13
  import { DefaultRoute } from '../../types/route'
14
- import { FastifySchemaControllerOptions } from '../../types/schema'
14
+ import { FastifySchemaControllerOptions, FastifySchemaCompiler, FastifySerializerCompiler } from '../../types/schema'
15
15
 
16
16
  const server = fastify()
17
17
 
@@ -133,7 +133,7 @@ expectError(server.setErrorHandler(invalidErrorHandler))
133
133
  server.setSchemaController({
134
134
  bucket: (parentSchemas: unknown) => {
135
135
  return {
136
- addSchema (schema: unknown) {
136
+ add (schema: unknown) {
137
137
  expectType<unknown>(schema)
138
138
  expectType<FastifyInstance>(server.addSchema({ type: 'null' }))
139
139
  return server.addSchema({ type: 'null' })
@@ -325,3 +325,6 @@ expectType<void>(server.addConstraintStrategy(versionConstraintStrategy))
325
325
  expectType<boolean>(server.hasConstraintStrategy(versionConstraintStrategy.name))
326
326
 
327
327
  expectAssignable<DefaultRoute<RawRequestDefaultExpression, RawReplyDefaultExpression>>(server.getDefaultRoute())
328
+
329
+ expectType<FastifySchemaCompiler<any> | undefined>(server.validatorCompiler)
330
+ expectType<FastifySerializerCompiler<any> | undefined>(server.serializerCompiler)
@@ -1,7 +1,21 @@
1
1
  import { expectAssignable, expectError, expectType } from 'tsd'
2
- import fastify, { FastifyInstance, FastifyPluginAsync } from '../../fastify'
2
+ import { IncomingMessage, Server, ServerResponse } from 'http'
3
+ import { Http2Server, Http2ServerRequest, Http2ServerResponse } from 'http2'
4
+ import fastify, { FastifyInstance, FastifyError, FastifyLoggerInstance, FastifyPluginAsync, FastifyPluginCallback, FastifyPluginOptions, RawServerDefault } from '../../fastify'
3
5
 
4
- const testPluginOptsAsync: FastifyPluginAsync = async function (_instance, _opts) { }
6
+ const testPluginCallback: FastifyPluginCallback = function (instance, opts, done) { }
7
+ const testPluginAsync: FastifyPluginAsync = async function (instance, opts) { }
8
+
9
+ const testPluginOpts: FastifyPluginCallback = function (instance, opts, done) { }
10
+ const testPluginOptsAsync: FastifyPluginAsync = async function (instance, opts) { }
11
+
12
+ const testPluginOptsWithType = (instance: FastifyInstance, opts: FastifyPluginOptions, done: (error?: FastifyError) => void) => { }
13
+ const testPluginOptsWithTypeAsync = async (instance: FastifyInstance, opts: FastifyPluginOptions) => { }
14
+
15
+ interface TestOptions extends FastifyPluginOptions {
16
+ option1: string;
17
+ option2: boolean;
18
+ }
5
19
 
6
20
  // Type validation
7
21
  expectError(fastify().register(testPluginOptsAsync, { prefix: 1 }))
@@ -26,3 +40,64 @@ expectAssignable<FastifyInstance>(
26
40
  expectType<FastifyInstance>(instance)
27
41
  })
28
42
  )
43
+
44
+ // With Http2
45
+ const serverWithHttp2 = fastify({ http2: true })
46
+ type ServerWithHttp2 = FastifyInstance<Http2Server, Http2ServerRequest, Http2ServerResponse>
47
+ const testPluginWithHttp2: FastifyPluginCallback<TestOptions, Http2Server> = function (instance, opts, done) { }
48
+ const testPluginWithHttp2Async: FastifyPluginAsync<TestOptions, Http2Server> = async function (instance, opts) { }
49
+ const testPluginWithHttp2WithType = (instance: ServerWithHttp2, opts: FastifyPluginOptions, done: (error?: FastifyError) => void) => { }
50
+ const testPluginWithHttp2WithTypeAsync = async (instance: ServerWithHttp2, opts: FastifyPluginOptions) => { }
51
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginCallback))
52
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginAsync))
53
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOpts))
54
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOptsAsync))
55
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOptsWithType))
56
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOptsWithTypeAsync))
57
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2))
58
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2Async))
59
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2WithType))
60
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2WithTypeAsync))
61
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register((instance) => {
62
+ expectAssignable<FastifyInstance>(instance)
63
+ }))
64
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register((instance: ServerWithHttp2) => {
65
+ expectAssignable<ServerWithHttp2>(instance)
66
+ }))
67
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(async (instance) => {
68
+ expectAssignable<FastifyInstance>(instance)
69
+ }))
70
+ expectAssignable<ServerWithHttp2>(serverWithHttp2.register(async (instance: ServerWithHttp2) => {
71
+ expectAssignable<ServerWithHttp2>(instance)
72
+ }))
73
+
74
+ // With Type Provider
75
+ type TestTypeProvider = { input: 'test', output: 'test' }
76
+ const serverWithTypeProvider = fastify().withTypeProvider<TestTypeProvider>()
77
+ type ServerWithTypeProvider = FastifyInstance<Server, IncomingMessage, ServerResponse, FastifyLoggerInstance, TestTypeProvider>
78
+ const testPluginWithTypeProvider: FastifyPluginCallback<TestOptions, RawServerDefault, TestTypeProvider> = function (instance, opts, done) { }
79
+ const testPluginWithTypeProviderAsync: FastifyPluginAsync<TestOptions, RawServerDefault, TestTypeProvider> = async function (instance, opts) { }
80
+ const testPluginWithTypeProviderWithType = (instance: ServerWithTypeProvider, opts: FastifyPluginOptions, done: (error?: FastifyError) => void) => { }
81
+ const testPluginWithTypeProviderWithTypeAsync = async (instance: ServerWithTypeProvider, opts: FastifyPluginOptions) => { }
82
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginCallback))
83
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginAsync))
84
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOpts))
85
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOptsAsync))
86
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOptsWithType))
87
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOptsWithTypeAsync))
88
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProvider))
89
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProviderAsync))
90
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProviderWithType))
91
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProviderWithTypeAsync))
92
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register((instance) => {
93
+ expectAssignable<FastifyInstance>(instance)
94
+ }))
95
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register((instance: ServerWithTypeProvider) => {
96
+ expectAssignable<ServerWithTypeProvider>(instance)
97
+ }))
98
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(async (instance) => {
99
+ expectAssignable<FastifyInstance>(instance)
100
+ }))
101
+ expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(async (instance: ServerWithTypeProvider) => {
102
+ expectAssignable<ServerWithTypeProvider>(instance)
103
+ }))