fastify 3.27.4 → 4.0.0-alpha.3

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 (168) hide show
  1. package/.taprc +3 -0
  2. package/README.md +7 -7
  3. package/build/build-error-serializer.js +27 -0
  4. package/build/build-validation.js +47 -35
  5. package/docs/Guides/Database.md +320 -0
  6. package/docs/Guides/Ecosystem.md +9 -0
  7. package/docs/Guides/Getting-Started.md +7 -7
  8. package/docs/Guides/Plugins-Guide.md +1 -1
  9. package/docs/Guides/Serverless.md +3 -3
  10. package/docs/Guides/Testing.md +2 -2
  11. package/docs/Migration-Guide-V4.md +12 -0
  12. package/docs/Reference/ContentTypeParser.md +4 -0
  13. package/docs/Reference/Decorators.md +2 -2
  14. package/docs/Reference/Encapsulation.md +2 -2
  15. package/docs/Reference/Errors.md +51 -6
  16. package/docs/Reference/HTTP2.md +3 -3
  17. package/docs/Reference/Hooks.md +4 -7
  18. package/docs/Reference/LTS.md +5 -4
  19. package/docs/Reference/Plugins.md +3 -3
  20. package/docs/Reference/Reply.md +73 -22
  21. package/docs/Reference/Request.md +1 -3
  22. package/docs/Reference/Routes.md +22 -15
  23. package/docs/Reference/Server.md +69 -119
  24. package/docs/Reference/TypeScript.md +20 -22
  25. package/docs/Reference/Validation-and-Serialization.md +30 -55
  26. package/docs/Type-Providers.md +257 -0
  27. package/examples/asyncawait.js +1 -1
  28. package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
  29. package/examples/benchmark/hooks-benchmark.js +1 -1
  30. package/examples/benchmark/simple.js +1 -1
  31. package/examples/hooks.js +2 -2
  32. package/examples/http2.js +1 -1
  33. package/examples/https.js +1 -1
  34. package/examples/parser.js +13 -3
  35. package/examples/route-prefix.js +1 -1
  36. package/examples/shared-schema.js +1 -1
  37. package/examples/simple-stream.js +18 -0
  38. package/examples/simple.js +1 -1
  39. package/examples/simple.mjs +1 -1
  40. package/examples/typescript-server.ts +1 -1
  41. package/examples/use-plugin.js +1 -1
  42. package/fastify.d.ts +34 -22
  43. package/fastify.js +40 -36
  44. package/lib/configValidator.js +902 -1023
  45. package/lib/contentTypeParser.js +6 -16
  46. package/lib/context.js +36 -10
  47. package/lib/decorate.js +3 -1
  48. package/lib/error-handler.js +158 -0
  49. package/lib/error-serializer.js +257 -0
  50. package/lib/errors.js +51 -9
  51. package/lib/fourOhFour.js +31 -20
  52. package/lib/handleRequest.js +10 -13
  53. package/lib/hooks.js +14 -9
  54. package/lib/pluginOverride.js +0 -3
  55. package/lib/pluginUtils.js +3 -2
  56. package/lib/reply.js +121 -175
  57. package/lib/request.js +13 -10
  58. package/lib/route.js +131 -138
  59. package/lib/schema-controller.js +2 -2
  60. package/lib/schemas.js +27 -1
  61. package/lib/server.js +242 -116
  62. package/lib/symbols.js +5 -3
  63. package/lib/validation.js +11 -9
  64. package/lib/warnings.js +4 -12
  65. package/lib/wrapThenable.js +4 -11
  66. package/package.json +37 -39
  67. package/test/404s.test.js +258 -125
  68. package/test/500s.test.js +3 -3
  69. package/test/als.test.js +1 -1
  70. package/test/async-await.test.js +20 -76
  71. package/test/bodyLimit.test.js +1 -1
  72. package/test/build-certificate.js +6 -7
  73. package/test/case-insensitive.test.js +4 -4
  74. package/test/close-pipelining.test.js +2 -2
  75. package/test/close.test.js +11 -11
  76. package/test/content-parser.test.js +32 -0
  77. package/test/context-config.test.js +52 -0
  78. package/test/custom-http-server.test.js +14 -7
  79. package/test/custom-parser-async.test.js +1 -66
  80. package/test/custom-parser.test.js +92 -159
  81. package/test/custom-querystring-parser.test.js +3 -3
  82. package/test/decorator.test.js +11 -13
  83. package/test/delete.test.js +6 -6
  84. package/test/encapsulated-error-handler.test.js +50 -0
  85. package/test/esm/index.test.js +0 -14
  86. package/test/fastify-instance.test.js +4 -4
  87. package/test/fluent-schema.test.js +4 -4
  88. package/test/genReqId.test.js +1 -1
  89. package/test/get.test.js +4 -4
  90. package/test/handler-context.test.js +2 -2
  91. package/test/head.test.js +1 -1
  92. package/test/helper.js +19 -4
  93. package/test/hooks-async.test.js +15 -48
  94. package/test/hooks.on-ready.test.js +10 -5
  95. package/test/hooks.test.js +78 -119
  96. package/test/http2/closing.test.js +10 -16
  97. package/test/http2/constraint.test.js +1 -1
  98. package/test/http2/head.test.js +1 -1
  99. package/test/http2/plain.test.js +1 -1
  100. package/test/http2/secure-with-fallback.test.js +1 -1
  101. package/test/http2/secure.test.js +1 -1
  102. package/test/http2/unknown-http-method.test.js +4 -10
  103. package/test/https/custom-https-server.test.js +12 -6
  104. package/test/https/https.test.js +1 -1
  105. package/test/input-validation.js +3 -3
  106. package/test/internals/handleRequest.test.js +6 -43
  107. package/test/internals/initialConfig.test.js +41 -12
  108. package/test/internals/logger.test.js +2 -2
  109. package/test/internals/reply.test.js +317 -48
  110. package/test/internals/request.test.js +13 -7
  111. package/test/internals/server.test.js +88 -0
  112. package/test/listen.deprecated.test.js +202 -0
  113. package/test/listen.test.js +140 -145
  114. package/test/logger.test.js +82 -42
  115. package/test/maxRequestsPerSocket.test.js +8 -6
  116. package/test/middleware.test.js +2 -25
  117. package/test/nullable-validation.test.js +53 -16
  118. package/test/output-validation.test.js +1 -1
  119. package/test/plugin.test.js +47 -21
  120. package/test/pretty-print.test.js +22 -10
  121. package/test/promises.test.js +1 -1
  122. package/test/proto-poisoning.test.js +6 -6
  123. package/test/register.test.js +3 -3
  124. package/test/reply-error.test.js +126 -15
  125. package/test/reply-trailers.test.js +270 -0
  126. package/test/request-error.test.js +3 -6
  127. package/test/route-hooks.test.js +18 -18
  128. package/test/route-prefix.test.js +2 -1
  129. package/test/route.test.js +206 -22
  130. package/test/router-options.test.js +2 -2
  131. package/test/schema-examples.test.js +11 -5
  132. package/test/schema-feature.test.js +25 -20
  133. package/test/schema-serialization.test.js +9 -9
  134. package/test/schema-special-usage.test.js +5 -153
  135. package/test/schema-validation.test.js +9 -9
  136. package/test/skip-reply-send.test.js +2 -2
  137. package/test/stream.test.js +82 -23
  138. package/test/throw.test.js +8 -5
  139. package/test/trust-proxy.test.js +6 -6
  140. package/test/type-provider.test.js +20 -0
  141. package/test/types/fastify.test-d.ts +10 -18
  142. package/test/types/hooks.test-d.ts +61 -5
  143. package/test/types/import.js +2 -0
  144. package/test/types/import.ts +1 -0
  145. package/test/types/instance.test-d.ts +68 -17
  146. package/test/types/logger.test-d.ts +44 -15
  147. package/test/types/reply.test-d.ts +2 -1
  148. package/test/types/request.test-d.ts +71 -1
  149. package/test/types/route.test-d.ts +8 -2
  150. package/test/types/schema.test-d.ts +2 -39
  151. package/test/types/type-provider.test-d.ts +424 -0
  152. package/test/url-rewriting.test.js +3 -3
  153. package/test/validation-error-handling.test.js +8 -8
  154. package/test/versioned-routes.test.js +30 -18
  155. package/test/wrapThenable.test.js +7 -6
  156. package/types/content-type-parser.d.ts +17 -8
  157. package/types/hooks.d.ts +182 -85
  158. package/types/instance.d.ts +286 -118
  159. package/types/logger.d.ts +18 -104
  160. package/types/plugin.d.ts +10 -4
  161. package/types/reply.d.ts +18 -12
  162. package/types/request.d.ts +13 -8
  163. package/types/route.d.ts +62 -34
  164. package/types/schema.d.ts +1 -1
  165. package/types/type-provider.d.ts +99 -0
  166. package/types/utils.d.ts +1 -1
  167. package/lib/schema-compilers.js +0 -12
  168. package/test/emit-warning.test.js +0 -166
@@ -3,7 +3,7 @@ import fastify, { FastifyInstance, FastifyRequest, FastifySchema } from '../../f
3
3
  import { RouteGenericInterface } from '../../types/route'
4
4
  import { ContextConfigDefault } from '../../types/utils'
5
5
  import { FastifyReply } from '../../types/reply'
6
- import Ajv = require('ajv')
6
+ import Ajv from 'ajv'
7
7
 
8
8
  const server = fastify()
9
9
 
@@ -35,33 +35,6 @@ expectAssignable<FastifyInstance>(server.get(
35
35
  () => { }
36
36
  ))
37
37
 
38
- expectAssignable<FastifyInstance>(server.get<RouteGenericInterface, ContextConfigDefault, { validate:(data: any) => any }>(
39
- '/no-schema',
40
- {
41
- schema: {},
42
- validatorCompiler: ({ schema }) => {
43
- // Error: Property 'validate' does not exist on type 'FastifySchema'.
44
- return (data: any) => schema.validate(data)
45
- }
46
- },
47
- () => { }
48
- ))
49
-
50
- expectAssignable<FastifyInstance>(
51
- server.route<RouteGenericInterface, ContextConfigDefault, { validate:(data: any) => any }>(
52
- {
53
- schema: {},
54
- validatorCompiler: ({ schema }) => {
55
- // Error: Property 'validate' does not exist on type 'FastifySchema'.
56
- return (data: any) => schema.validate(data)
57
- },
58
- method: 'POST',
59
- url: '/',
60
- handler: async (_request: FastifyRequest, _reply: FastifyReply) => {}
61
- }
62
- )
63
- )
64
-
65
38
  expectAssignable<FastifyInstance>(server.setValidatorCompiler(({ schema }) => {
66
39
  return new Ajv().compile(schema)
67
40
  }))
@@ -83,20 +56,10 @@ expectAssignable<FastifyInstance>(server.post('/test', {
83
56
 
84
57
  expectAssignable<FastifyInstance>(server.setValidatorCompiler<FastifySchema & { validate: Record<string, unknown> }>(
85
58
  function ({ schema }) {
86
- return new Ajv().compile(schema.validate)
59
+ return new Ajv().compile(schema)
87
60
  }
88
61
  ))
89
62
 
90
63
  expectAssignable<FastifyInstance>(server.setSerializerCompiler<FastifySchema & { validate: string }>(
91
64
  () => data => JSON.stringify(data)
92
65
  ))
93
-
94
- expectError(server.get(
95
- '/unknown-schema-prop',
96
- {
97
- schema: {
98
- unknown: { type: 'null' }
99
- }
100
- },
101
- () => { }
102
- ))
@@ -0,0 +1,424 @@
1
+ import fastify, {
2
+ ContextConfigDefault, FastifySchema,
3
+ FastifyTypeProvider, RawReplyDefaultExpression,
4
+ RawRequestDefaultExpression,
5
+ RawServerDefault,
6
+ RouteHandlerMethod
7
+ } from '../../fastify'
8
+ import { expectAssignable, expectError, expectType } from 'tsd'
9
+ import { IncomingHttpHeaders } from 'http'
10
+ import { Type, TSchema, Static } from '@sinclair/typebox'
11
+ import { FromSchema, JSONSchema } from 'json-schema-to-ts'
12
+ import { RouteGenericInterface } from '../../types/route'
13
+
14
+ const server = fastify()
15
+
16
+ // -------------------------------------------------------------------
17
+ // Remapping
18
+ // -------------------------------------------------------------------
19
+
20
+ interface NumberProvider extends FastifyTypeProvider { output: number } // remap all schemas to numbers
21
+
22
+ expectAssignable(server.withTypeProvider<NumberProvider>().get(
23
+ '/',
24
+ {
25
+ schema: {
26
+ body: { type: 'string' },
27
+ querystring: { type: 'string' },
28
+ headers: { type: 'string' },
29
+ params: { type: 'string' }
30
+ }
31
+ },
32
+ (req) => {
33
+ expectType<number & IncomingHttpHeaders>(req.headers)
34
+ expectType<number>(req.body)
35
+ expectType<number>(req.query)
36
+ expectType<number>(req.params)
37
+ }
38
+ ))
39
+
40
+ // -------------------------------------------------------------------
41
+ // Override
42
+ // -------------------------------------------------------------------
43
+
44
+ interface OverriddenProvider extends FastifyTypeProvider { output: 'inferenced' }
45
+
46
+ expectAssignable(server.withTypeProvider<OverriddenProvider>().get<{ Body: 'override' }>(
47
+ '/',
48
+ {
49
+ schema: {
50
+ body: Type.Object({
51
+ x: Type.Number(),
52
+ y: Type.Number(),
53
+ z: Type.Number()
54
+ })
55
+ }
56
+ },
57
+ (req) => {
58
+ expectType<'override'>(req.body)
59
+ }
60
+ ))
61
+
62
+ // -------------------------------------------------------------------
63
+ // TypeBox
64
+ // -------------------------------------------------------------------
65
+
66
+ interface TypeBoxProvider extends FastifyTypeProvider { output: this['input'] extends TSchema ? Static<this['input']> : never }
67
+
68
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
69
+ '/',
70
+ {
71
+ schema: {
72
+ body: Type.Object({
73
+ x: Type.Number(),
74
+ y: Type.Number(),
75
+ z: Type.Number()
76
+ })
77
+ }
78
+ },
79
+ (req) => {
80
+ expectType<number>(req.body.x)
81
+ expectType<number>(req.body.y)
82
+ expectType<number>(req.body.z)
83
+ }
84
+ ))
85
+
86
+ // -------------------------------------------------------------------
87
+ // JsonSchemaToTs
88
+ // -------------------------------------------------------------------
89
+
90
+ interface JsonSchemaToTsProvider extends FastifyTypeProvider { output: this['input'] extends JSONSchema ? FromSchema<this['input']> : never }
91
+
92
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
93
+ '/',
94
+ {
95
+ schema: {
96
+ body: {
97
+ type: 'object',
98
+ properties: {
99
+ x: { type: 'number' },
100
+ y: { type: 'string' },
101
+ z: { type: 'boolean' }
102
+ }
103
+ } as const
104
+ }
105
+ },
106
+ (req) => {
107
+ expectType<number | undefined>(req.body.x)
108
+ expectType<string | undefined>(req.body.y)
109
+ expectType<boolean | undefined>(req.body.z)
110
+ }
111
+ ))
112
+
113
+ // -------------------------------------------------------------------
114
+ // Instance Type Remappable
115
+ // -------------------------------------------------------------------
116
+
117
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().withTypeProvider<JsonSchemaToTsProvider>().get(
118
+ '/',
119
+ {
120
+ schema: {
121
+ body: {
122
+ type: 'object',
123
+ properties: {
124
+ x: { type: 'number' },
125
+ y: { type: 'string' },
126
+ z: { type: 'boolean' }
127
+ }
128
+ } as const
129
+ }
130
+ },
131
+ (req) => {
132
+ expectType<number | undefined>(req.body.x)
133
+ expectType<string | undefined>(req.body.y)
134
+ expectType<boolean | undefined>(req.body.z)
135
+ }
136
+ ))
137
+
138
+ // -------------------------------------------------------------------
139
+ // Request Hooks
140
+ // -------------------------------------------------------------------
141
+
142
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
143
+ '/',
144
+ {
145
+ schema: {
146
+ body: Type.Object({
147
+ x: Type.Number(),
148
+ y: Type.String(),
149
+ z: Type.Boolean()
150
+ })
151
+ },
152
+ preHandler: req => {
153
+ expectType<number>(req.body.x)
154
+ expectType<string>(req.body.y)
155
+ expectType<boolean>(req.body.z)
156
+ },
157
+ preParsing: req => {
158
+ expectType<number>(req.body.x)
159
+ expectType<string>(req.body.y)
160
+ expectType<boolean>(req.body.z)
161
+ },
162
+ preSerialization: req => {
163
+ expectType<number>(req.body.x)
164
+ expectType<string>(req.body.y)
165
+ expectType<boolean>(req.body.z)
166
+ },
167
+ preValidation: req => {
168
+ expectType<number>(req.body.x)
169
+ expectType<string>(req.body.y)
170
+ expectType<boolean>(req.body.z)
171
+ },
172
+ onError: req => {
173
+ expectType<number>(req.body.x)
174
+ expectType<string>(req.body.y)
175
+ expectType<boolean>(req.body.z)
176
+ },
177
+ onRequest: req => {
178
+ expectType<number>(req.body.x)
179
+ expectType<string>(req.body.y)
180
+ expectType<boolean>(req.body.z)
181
+ },
182
+ onResponse: req => {
183
+ expectType<number>(req.body.x)
184
+ expectType<string>(req.body.y)
185
+ expectType<boolean>(req.body.z)
186
+ },
187
+ onTimeout: req => {
188
+ expectType<number>(req.body.x)
189
+ expectType<string>(req.body.y)
190
+ expectType<boolean>(req.body.z)
191
+ },
192
+ onSend: req => {
193
+ expectType<number>(req.body.x)
194
+ expectType<string>(req.body.y)
195
+ expectType<boolean>(req.body.z)
196
+ }
197
+ },
198
+ req => {
199
+ expectType<number>(req.body.x)
200
+ expectType<string>(req.body.y)
201
+ expectType<boolean>(req.body.z)
202
+ }
203
+ ))
204
+
205
+ // -------------------------------------------------------------------
206
+ // TypeBox Reply Type
207
+ // -------------------------------------------------------------------
208
+
209
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
210
+ '/',
211
+ {
212
+ schema: {
213
+ response: {
214
+ 200: Type.String(),
215
+ 400: Type.Number(),
216
+ 500: Type.Object({
217
+ error: Type.String()
218
+ })
219
+ }
220
+ }
221
+ },
222
+ async (_, res) => {
223
+ res.send('hello')
224
+ res.send(42)
225
+ res.send({ error: 'error' })
226
+ }
227
+ ))
228
+
229
+ // -------------------------------------------------------------------
230
+ // TypeBox Reply Type: Non Assignable
231
+ // -------------------------------------------------------------------
232
+
233
+ expectError(server.withTypeProvider<TypeBoxProvider>().get(
234
+ '/',
235
+ {
236
+ schema: {
237
+ response: {
238
+ 200: Type.String(),
239
+ 400: Type.Number(),
240
+ 500: Type.Object({
241
+ error: Type.String()
242
+ })
243
+ }
244
+ }
245
+ },
246
+ async (_, res) => {
247
+ res.send(false)
248
+ }
249
+ ))
250
+
251
+ // -------------------------------------------------------------------
252
+ // TypeBox Reply Return Type
253
+ // -------------------------------------------------------------------
254
+
255
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
256
+ '/',
257
+ {
258
+ schema: {
259
+ response: {
260
+ 200: Type.String(),
261
+ 400: Type.Number(),
262
+ 500: Type.Object({
263
+ error: Type.String()
264
+ })
265
+ }
266
+ }
267
+ },
268
+ async (_, res) => {
269
+ const option = 1 as 1 | 2 | 3
270
+ switch (option) {
271
+ case 1: return 'hello'
272
+ case 2: return 42
273
+ case 3: return { error: 'error' }
274
+ }
275
+ }
276
+ ))
277
+
278
+ // -------------------------------------------------------------------
279
+ // TypeBox Reply Return Type: Non Assignable
280
+ // -------------------------------------------------------------------
281
+
282
+ expectError(server.withTypeProvider<TypeBoxProvider>().get(
283
+ '/',
284
+ {
285
+ schema: {
286
+ response: {
287
+ 200: Type.String(),
288
+ 400: Type.Number(),
289
+ 500: Type.Object({
290
+ error: Type.String()
291
+ })
292
+ }
293
+ }
294
+ },
295
+ async (_, res): Promise<RouteHandlerMethod<RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, RouteGenericInterface, ContextConfigDefault, FastifySchema, TypeBoxProvider>> => {
296
+ return false
297
+ }
298
+ ))
299
+
300
+ // -------------------------------------------------------------------
301
+ // JsonSchemaToTs Reply Type
302
+ // -------------------------------------------------------------------
303
+
304
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
305
+ '/',
306
+ {
307
+ schema: {
308
+ response: {
309
+ 200: { type: 'string' },
310
+ 400: { type: 'number' },
311
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
312
+ } as const
313
+ }
314
+ },
315
+ (_, res) => {
316
+ res.send('hello')
317
+ res.send(42)
318
+ res.send({ error: 'error' })
319
+ }
320
+ ))
321
+
322
+ // -------------------------------------------------------------------
323
+ // JsonSchemaToTs Reply Type: Non Assignable
324
+ // -------------------------------------------------------------------
325
+
326
+ expectError(server.withTypeProvider<JsonSchemaToTsProvider>().get(
327
+ '/',
328
+ {
329
+ schema: {
330
+ response: {
331
+ 200: { type: 'string' },
332
+ 400: { type: 'number' },
333
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
334
+ } as const
335
+ }
336
+ },
337
+ async (_, res) => {
338
+ res.send(false)
339
+ }
340
+ ))
341
+
342
+ // -------------------------------------------------------------------
343
+ // JsonSchemaToTs Reply Type Return
344
+ // -------------------------------------------------------------------
345
+
346
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
347
+ '/',
348
+ {
349
+ schema: {
350
+ response: {
351
+ 200: { type: 'string' },
352
+ 400: { type: 'number' },
353
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
354
+ } as const
355
+ }
356
+ },
357
+ async (_, res) => {
358
+ const option = 1 as 1 | 2 | 3
359
+ switch (option) {
360
+ case 1: return 'hello'
361
+ case 2: return 42
362
+ case 3: return { error: 'error' }
363
+ }
364
+ }
365
+ ))
366
+ // -------------------------------------------------------------------
367
+ // JsonSchemaToTs Reply Type Return: Non Assignable
368
+ // -------------------------------------------------------------------
369
+
370
+ expectError(server.withTypeProvider<JsonSchemaToTsProvider>().get(
371
+ '/',
372
+ {
373
+ schema: {
374
+ response: {
375
+ 200: { type: 'string' },
376
+ 400: { type: 'number' },
377
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
378
+ } as const
379
+ }
380
+ },
381
+ async (_, res): Promise<RouteHandlerMethod<RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, RouteGenericInterface, ContextConfigDefault, FastifySchema, TypeBoxProvider>> => {
382
+ return false
383
+ }
384
+ ))
385
+
386
+ // -------------------------------------------------------------------
387
+ // Reply Type Override
388
+ // -------------------------------------------------------------------
389
+
390
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get<{Reply: boolean}>(
391
+ '/',
392
+ {
393
+ schema: {
394
+ response: {
395
+ 200: { type: 'string' },
396
+ 400: { type: 'number' },
397
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
398
+ } as const
399
+ }
400
+ },
401
+ async (_, res) => {
402
+ res.send(true)
403
+ }
404
+ ))
405
+
406
+ // -------------------------------------------------------------------
407
+ // Reply Type Return Override
408
+ // -------------------------------------------------------------------
409
+
410
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get<{Reply: boolean}>(
411
+ '/',
412
+ {
413
+ schema: {
414
+ response: {
415
+ 200: { type: 'string' },
416
+ 400: { type: 'number' },
417
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
418
+ } as const
419
+ }
420
+ },
421
+ async (_, res) => {
422
+ return true
423
+ }
424
+ ))
@@ -22,7 +22,7 @@ test('Should rewrite url', t => {
22
22
  }
23
23
  })
24
24
 
25
- fastify.listen(0, function (err) {
25
+ fastify.listen({ port: 0 }, function (err) {
26
26
  t.error(err)
27
27
 
28
28
  sget({
@@ -55,7 +55,7 @@ test('Should not rewrite if the url is the same', t => {
55
55
  }
56
56
  })
57
57
 
58
- fastify.listen(0, function (err) {
58
+ fastify.listen({ port: 0 }, function (err) {
59
59
  t.error(err)
60
60
 
61
61
  sget({
@@ -86,7 +86,7 @@ test('Should throw an error', t => {
86
86
  }
87
87
  })
88
88
 
89
- fastify.listen(0, function (err) {
89
+ fastify.listen({ port: 0 }, function (err) {
90
90
  t.error(err)
91
91
 
92
92
  sget({
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('tap')
4
- const Joi = require('@hapi/joi')
4
+ const Joi = require('joi')
5
5
  const Fastify = require('..')
6
6
 
7
7
  const schema = {
@@ -58,7 +58,7 @@ test('should fail immediately with invalid payload', t => {
58
58
  t.same(res.json(), {
59
59
  statusCode: 400,
60
60
  error: 'Bad Request',
61
- message: "body should have required property 'name'"
61
+ message: "body must have required property 'name'"
62
62
  })
63
63
  t.equal(res.statusCode, 400)
64
64
  })
@@ -183,10 +183,10 @@ test('should be able to attach validation to request', t => {
183
183
 
184
184
  t.same(res.json(), [{
185
185
  keyword: 'required',
186
- dataPath: '',
186
+ instancePath: '',
187
187
  schemaPath: '#/required',
188
188
  params: { missingProperty: 'name' },
189
- message: 'should have required property \'name\''
189
+ message: 'must have required property \'name\''
190
190
  }])
191
191
  t.equal(res.statusCode, 400)
192
192
  })
@@ -213,7 +213,7 @@ test('should respect when attachValidation is explicitly set to false', t => {
213
213
  t.same(JSON.parse(res.payload), {
214
214
  statusCode: 400,
215
215
  error: 'Bad Request',
216
- message: "body should have required property 'name'"
216
+ message: "body must have required property 'name'"
217
217
  })
218
218
  t.equal(res.statusCode, 400)
219
219
  })
@@ -243,7 +243,7 @@ test('Attached validation error should take precedence over setErrorHandler', t
243
243
  url: '/'
244
244
  }, (err, res) => {
245
245
  t.error(err)
246
- t.same(res.payload, "Attached: Error: body should have required property 'name'")
246
+ t.same(res.payload, "Attached: Error: body must have required property 'name'")
247
247
  t.equal(res.statusCode, 400)
248
248
  })
249
249
  })
@@ -336,7 +336,7 @@ test('should return a defined output message parsing AJV errors', t => {
336
336
  url: '/'
337
337
  }, (err, res) => {
338
338
  t.error(err)
339
- t.equal(res.payload, '{"statusCode":400,"error":"Bad Request","message":"body should have required property \'name\'"}')
339
+ t.equal(res.payload, '{"statusCode":400,"error":"Bad Request","message":"body must have required property \'name\'"}')
340
340
  })
341
341
  })
342
342
 
@@ -467,7 +467,7 @@ test('should call custom error formatter', t => {
467
467
  const fastify = Fastify({
468
468
  schemaErrorFormatter: (errors, dataVar) => {
469
469
  t.equal(errors.length, 1)
470
- t.equal(errors[0].message, "should have required property 'name'")
470
+ t.equal(errors[0].message, "must have required property 'name'")
471
471
  t.equal(dataVar, 'body')
472
472
  return new Error('my error')
473
473
  }
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
- const test = t.test
3
+ const { test, before } = require('tap')
4
+ const helper = require('./helper')
5
5
  const Fastify = require('..')
6
6
  const sget = require('simple-get').concat
7
7
  const http = require('http')
@@ -9,6 +9,13 @@ const split = require('split2')
9
9
  const append = require('vary').append
10
10
  const proxyquire = require('proxyquire')
11
11
 
12
+ process.removeAllListeners('warning')
13
+
14
+ let localhost
15
+ before(async function () {
16
+ [localhost] = await helper.getLoopbackHost()
17
+ })
18
+
12
19
  test('Should register a versioned route', t => {
13
20
  t.plan(11)
14
21
  const fastify = Fastify()
@@ -235,7 +242,7 @@ test('Should register a versioned route', t => {
235
242
  }
236
243
  })
237
244
 
238
- fastify.listen(0, err => {
245
+ fastify.listen({ port: 0 }, err => {
239
246
  t.error(err)
240
247
  fastify.server.unref()
241
248
 
@@ -400,7 +407,7 @@ test('Bas accept version (server)', t => {
400
407
  }
401
408
  })
402
409
 
403
- fastify.listen(0, err => {
410
+ fastify.listen({ port: 0 }, err => {
404
411
  t.error(err)
405
412
  fastify.server.unref()
406
413
 
@@ -442,12 +449,12 @@ test('test log stream', t => {
442
449
  reply.send(new Error('kaboom'))
443
450
  })
444
451
 
445
- fastify.listen(0, err => {
452
+ fastify.listen({ port: 0, host: localhost }, err => {
446
453
  t.error(err)
447
454
  fastify.server.unref()
448
455
 
449
456
  http.get({
450
- hostname: 'localhost',
457
+ hostname: fastify.server.address().hostname,
451
458
  port: fastify.server.address().port,
452
459
  path: '/',
453
460
  method: 'GET',
@@ -549,7 +556,7 @@ test('Should register a versioned route with custom versioning strategy', t => {
549
556
  })
550
557
 
551
558
  test('Should get error using an invalid a versioned route, using default validation (deprecated versioning option)', t => {
552
- t.plan(1)
559
+ t.plan(3)
553
560
 
554
561
  const fastify = Fastify({
555
562
  versioning: {
@@ -577,15 +584,19 @@ test('Should get error using an invalid a versioned route, using default validat
577
584
  }
578
585
  })
579
586
 
580
- fastify.route({
581
- method: 'GET',
582
- url: '/',
583
- // not a string version
584
- constraints: { version: 2 },
585
- handler: (req, reply) => {
586
- reply.send({ hello: 'cant match route v2' })
587
- }
588
- })
587
+ try {
588
+ fastify.route({
589
+ method: 'GET',
590
+ url: '/',
591
+ // not a string version
592
+ constraints: { version: 2 },
593
+ handler: (req, reply) => {
594
+ reply.send({ hello: 'cant match route v2' })
595
+ }
596
+ })
597
+ } catch (err) {
598
+ t.equal(err.message, 'Version constraint should be a string.')
599
+ }
589
600
 
590
601
  fastify.inject({
591
602
  method: 'GET',
@@ -594,7 +605,8 @@ test('Should get error using an invalid a versioned route, using default validat
594
605
  Accept: 'application/vnd.example.api+json;version=2'
595
606
  }
596
607
  }, (err, res) => {
597
- t.equal(err.message, 'Version constraint should be a string.')
608
+ t.error(err)
609
+ t.equal(res.statusCode, 404)
598
610
  })
599
611
  })
600
612
 
@@ -663,7 +675,7 @@ test('Should trigger a warning when a versioned route is registered via version
663
675
  }
664
676
 
665
677
  const route = proxyquire('../lib/route', { './warnings': warning })
666
- const fastify = proxyquire('..', { './lib/route.js': route })()
678
+ const fastify = proxyquire('..', { './lib/route.js': route })({ exposeHeadRoutes: false })
667
679
 
668
680
  fastify.route({
669
681
  method: 'GET',