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.
- package/.taprc +3 -0
- package/README.md +7 -7
- package/build/build-error-serializer.js +27 -0
- package/build/build-validation.js +47 -35
- package/docs/Guides/Database.md +320 -0
- package/docs/Guides/Ecosystem.md +9 -0
- package/docs/Guides/Getting-Started.md +7 -7
- package/docs/Guides/Plugins-Guide.md +1 -1
- package/docs/Guides/Serverless.md +3 -3
- package/docs/Guides/Testing.md +2 -2
- package/docs/Migration-Guide-V4.md +12 -0
- package/docs/Reference/ContentTypeParser.md +4 -0
- package/docs/Reference/Decorators.md +2 -2
- package/docs/Reference/Encapsulation.md +2 -2
- package/docs/Reference/Errors.md +51 -6
- package/docs/Reference/HTTP2.md +3 -3
- package/docs/Reference/Hooks.md +4 -7
- package/docs/Reference/LTS.md +5 -4
- package/docs/Reference/Plugins.md +3 -3
- package/docs/Reference/Reply.md +73 -22
- package/docs/Reference/Request.md +1 -3
- package/docs/Reference/Routes.md +22 -15
- package/docs/Reference/Server.md +69 -119
- package/docs/Reference/TypeScript.md +20 -22
- package/docs/Reference/Validation-and-Serialization.md +30 -55
- package/docs/Type-Providers.md +257 -0
- package/examples/asyncawait.js +1 -1
- package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
- package/examples/benchmark/hooks-benchmark.js +1 -1
- package/examples/benchmark/simple.js +1 -1
- package/examples/hooks.js +2 -2
- package/examples/http2.js +1 -1
- package/examples/https.js +1 -1
- package/examples/parser.js +13 -3
- package/examples/route-prefix.js +1 -1
- package/examples/shared-schema.js +1 -1
- package/examples/simple-stream.js +18 -0
- package/examples/simple.js +1 -1
- package/examples/simple.mjs +1 -1
- package/examples/typescript-server.ts +1 -1
- package/examples/use-plugin.js +1 -1
- package/fastify.d.ts +34 -22
- package/fastify.js +40 -36
- package/lib/configValidator.js +902 -1023
- package/lib/contentTypeParser.js +6 -16
- package/lib/context.js +36 -10
- package/lib/decorate.js +3 -1
- package/lib/error-handler.js +158 -0
- package/lib/error-serializer.js +257 -0
- package/lib/errors.js +51 -9
- package/lib/fourOhFour.js +31 -20
- package/lib/handleRequest.js +10 -13
- package/lib/hooks.js +14 -9
- package/lib/pluginOverride.js +0 -3
- package/lib/pluginUtils.js +3 -2
- package/lib/reply.js +121 -175
- package/lib/request.js +13 -10
- package/lib/route.js +131 -138
- package/lib/schema-controller.js +2 -2
- package/lib/schemas.js +27 -1
- package/lib/server.js +242 -116
- package/lib/symbols.js +5 -3
- package/lib/validation.js +11 -9
- package/lib/warnings.js +4 -12
- package/lib/wrapThenable.js +4 -11
- package/package.json +37 -39
- package/test/404s.test.js +258 -125
- package/test/500s.test.js +3 -3
- package/test/als.test.js +1 -1
- package/test/async-await.test.js +20 -76
- package/test/bodyLimit.test.js +1 -1
- package/test/build-certificate.js +6 -7
- package/test/case-insensitive.test.js +4 -4
- package/test/close-pipelining.test.js +2 -2
- package/test/close.test.js +11 -11
- package/test/content-parser.test.js +32 -0
- package/test/context-config.test.js +52 -0
- package/test/custom-http-server.test.js +14 -7
- package/test/custom-parser-async.test.js +1 -66
- package/test/custom-parser.test.js +92 -159
- package/test/custom-querystring-parser.test.js +3 -3
- package/test/decorator.test.js +11 -13
- package/test/delete.test.js +6 -6
- package/test/encapsulated-error-handler.test.js +50 -0
- package/test/esm/index.test.js +0 -14
- package/test/fastify-instance.test.js +4 -4
- package/test/fluent-schema.test.js +4 -4
- package/test/genReqId.test.js +1 -1
- package/test/get.test.js +4 -4
- package/test/handler-context.test.js +2 -2
- package/test/head.test.js +1 -1
- package/test/helper.js +19 -4
- package/test/hooks-async.test.js +15 -48
- package/test/hooks.on-ready.test.js +10 -5
- package/test/hooks.test.js +78 -119
- package/test/http2/closing.test.js +10 -16
- package/test/http2/constraint.test.js +1 -1
- package/test/http2/head.test.js +1 -1
- package/test/http2/plain.test.js +1 -1
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/http2/secure.test.js +1 -1
- package/test/http2/unknown-http-method.test.js +4 -10
- package/test/https/custom-https-server.test.js +12 -6
- package/test/https/https.test.js +1 -1
- package/test/input-validation.js +3 -3
- package/test/internals/handleRequest.test.js +6 -43
- package/test/internals/initialConfig.test.js +41 -12
- package/test/internals/logger.test.js +2 -2
- package/test/internals/reply.test.js +317 -48
- package/test/internals/request.test.js +13 -7
- package/test/internals/server.test.js +88 -0
- package/test/listen.deprecated.test.js +202 -0
- package/test/listen.test.js +140 -145
- package/test/logger.test.js +82 -42
- package/test/maxRequestsPerSocket.test.js +8 -6
- package/test/middleware.test.js +2 -25
- package/test/nullable-validation.test.js +53 -16
- package/test/output-validation.test.js +1 -1
- package/test/plugin.test.js +47 -21
- package/test/pretty-print.test.js +22 -10
- package/test/promises.test.js +1 -1
- package/test/proto-poisoning.test.js +6 -6
- package/test/register.test.js +3 -3
- package/test/reply-error.test.js +126 -15
- package/test/reply-trailers.test.js +270 -0
- package/test/request-error.test.js +3 -6
- package/test/route-hooks.test.js +18 -18
- package/test/route-prefix.test.js +2 -1
- package/test/route.test.js +206 -22
- package/test/router-options.test.js +2 -2
- package/test/schema-examples.test.js +11 -5
- package/test/schema-feature.test.js +25 -20
- package/test/schema-serialization.test.js +9 -9
- package/test/schema-special-usage.test.js +5 -153
- package/test/schema-validation.test.js +9 -9
- package/test/skip-reply-send.test.js +2 -2
- package/test/stream.test.js +82 -23
- package/test/throw.test.js +8 -5
- package/test/trust-proxy.test.js +6 -6
- package/test/type-provider.test.js +20 -0
- package/test/types/fastify.test-d.ts +10 -18
- package/test/types/hooks.test-d.ts +61 -5
- package/test/types/import.js +2 -0
- package/test/types/import.ts +1 -0
- package/test/types/instance.test-d.ts +68 -17
- package/test/types/logger.test-d.ts +44 -15
- package/test/types/reply.test-d.ts +2 -1
- package/test/types/request.test-d.ts +71 -1
- package/test/types/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +424 -0
- package/test/url-rewriting.test.js +3 -3
- package/test/validation-error-handling.test.js +8 -8
- package/test/versioned-routes.test.js +30 -18
- package/test/wrapThenable.test.js +7 -6
- package/types/content-type-parser.d.ts +17 -8
- package/types/hooks.d.ts +182 -85
- package/types/instance.d.ts +286 -118
- package/types/logger.d.ts +18 -104
- package/types/plugin.d.ts +10 -4
- package/types/reply.d.ts +18 -12
- package/types/request.d.ts +13 -8
- package/types/route.d.ts +62 -34
- package/types/schema.d.ts +1 -1
- package/types/type-provider.d.ts +99 -0
- package/types/utils.d.ts +1 -1
- package/lib/schema-compilers.js +0 -12
- 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
|
|
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
|
|
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('
|
|
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
|
|
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
|
-
|
|
186
|
+
instancePath: '',
|
|
187
187
|
schemaPath: '#/required',
|
|
188
188
|
params: { missingProperty: 'name' },
|
|
189
|
-
message: '
|
|
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
|
|
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
|
|
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
|
|
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, "
|
|
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
|
|
4
|
-
const
|
|
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:
|
|
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(
|
|
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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
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.
|
|
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',
|