fastify 4.6.0 → 4.7.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/docs/Guides/Ecosystem.md +19 -8
- package/docs/Guides/Plugins-Guide.md +44 -0
- package/docs/Reference/Reply.md +5 -5
- package/docs/Reference/Request.md +19 -6
- package/docs/Reference/Routes.md +2 -2
- package/docs/Reference/Type-Providers.md +3 -3
- package/fastify.js +1 -1
- package/lib/contentTypeParser.js +10 -9
- package/lib/context.js +27 -3
- package/lib/error-handler.js +7 -3
- package/lib/handleRequest.js +15 -13
- package/lib/reply.js +42 -34
- package/lib/request.js +36 -18
- package/lib/route.js +16 -10
- package/lib/schema-controller.js +7 -1
- package/lib/server.js +2 -2
- package/lib/symbols.js +2 -0
- package/lib/validation.js +4 -3
- package/lib/warnings.js +2 -0
- package/package.json +26 -26
- package/test/404s.test.js +19 -1
- package/test/context-config.test.js +2 -1
- package/test/handler-context.test.js +12 -30
- package/test/internals/contentTypeParser.test.js +3 -3
- package/test/internals/handleRequest.test.js +4 -3
- package/test/internals/reply-serialize.test.js +9 -9
- package/test/internals/reply.test.js +10 -9
- package/test/internals/request-validate.test.js +97 -9
- package/test/internals/request.test.js +57 -3
- package/test/internals/validation.test.js +15 -0
- package/test/plugin.test.js +1 -1
- package/test/reply-code.test.js +59 -0
- package/test/route.test.js +29 -0
- package/test/router-options.test.js +33 -66
- package/test/schema-feature.test.js +25 -0
- package/test/schema-validation.test.js +19 -0
- package/test/search.test.js +169 -30
- package/test/server.test.js +54 -0
- package/types/request.d.ts +9 -3
package/lib/reply.js
CHANGED
|
@@ -20,7 +20,8 @@ const {
|
|
|
20
20
|
kSchemaResponse,
|
|
21
21
|
kReplySerializeWeakMap,
|
|
22
22
|
kSchemaController,
|
|
23
|
-
kOptions
|
|
23
|
+
kOptions,
|
|
24
|
+
kRouteContext
|
|
24
25
|
} = require('./symbols.js')
|
|
25
26
|
const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks')
|
|
26
27
|
|
|
@@ -63,14 +64,21 @@ function Reply (res, request, log) {
|
|
|
63
64
|
Reply.props = []
|
|
64
65
|
|
|
65
66
|
Object.defineProperties(Reply.prototype, {
|
|
67
|
+
[kRouteContext]: {
|
|
68
|
+
get () {
|
|
69
|
+
return this.request[kRouteContext]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
// TODO: remove once v5 is done
|
|
73
|
+
// Is temporary to avoid constant conflicts between `next` and `main`
|
|
66
74
|
context: {
|
|
67
75
|
get () {
|
|
68
|
-
return this.request
|
|
76
|
+
return this.request[kRouteContext]
|
|
69
77
|
}
|
|
70
78
|
},
|
|
71
79
|
server: {
|
|
72
80
|
get () {
|
|
73
|
-
return this.request.
|
|
81
|
+
return this.request[kRouteContext].server
|
|
74
82
|
}
|
|
75
83
|
},
|
|
76
84
|
sent: {
|
|
@@ -292,7 +300,7 @@ Reply.prototype.removeTrailer = function (key) {
|
|
|
292
300
|
}
|
|
293
301
|
|
|
294
302
|
Reply.prototype.code = function (code) {
|
|
295
|
-
const intValue =
|
|
303
|
+
const intValue = Number(code)
|
|
296
304
|
if (isNaN(intValue) || intValue < 100 || intValue > 599) {
|
|
297
305
|
throw new FST_ERR_BAD_STATUS_CODE(code || String(code))
|
|
298
306
|
}
|
|
@@ -308,9 +316,9 @@ Reply.prototype.getSerializationFunction = function (schemaOrStatus) {
|
|
|
308
316
|
let serialize
|
|
309
317
|
|
|
310
318
|
if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
|
|
311
|
-
serialize = this
|
|
319
|
+
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
|
|
312
320
|
} else if (typeof schemaOrStatus === 'object') {
|
|
313
|
-
serialize = this
|
|
321
|
+
serialize = this[kRouteContext][kReplySerializeWeakMap]?.get(schemaOrStatus)
|
|
314
322
|
}
|
|
315
323
|
|
|
316
324
|
return serialize
|
|
@@ -321,11 +329,11 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
321
329
|
const { method, url } = request
|
|
322
330
|
|
|
323
331
|
// Check if serialize function already compiled
|
|
324
|
-
if (this
|
|
325
|
-
return this
|
|
332
|
+
if (this[kRouteContext][kReplySerializeWeakMap]?.has(schema)) {
|
|
333
|
+
return this[kRouteContext][kReplySerializeWeakMap].get(schema)
|
|
326
334
|
}
|
|
327
335
|
|
|
328
|
-
const serializerCompiler = this.
|
|
336
|
+
const serializerCompiler = this[kRouteContext].serializerCompiler ||
|
|
329
337
|
this.server[kSchemaController].serializerCompiler ||
|
|
330
338
|
(
|
|
331
339
|
// We compile the schemas if no custom serializerCompiler is provided
|
|
@@ -346,11 +354,11 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
346
354
|
// if it is not used
|
|
347
355
|
// TODO: Explore a central cache for all the schemas shared across
|
|
348
356
|
// encapsulated contexts
|
|
349
|
-
if (this
|
|
350
|
-
this
|
|
357
|
+
if (this[kRouteContext][kReplySerializeWeakMap] == null) {
|
|
358
|
+
this[kRouteContext][kReplySerializeWeakMap] = new WeakMap()
|
|
351
359
|
}
|
|
352
360
|
|
|
353
|
-
this
|
|
361
|
+
this[kRouteContext][kReplySerializeWeakMap].set(schema, serializeFn)
|
|
354
362
|
|
|
355
363
|
return serializeFn
|
|
356
364
|
}
|
|
@@ -362,13 +370,13 @@ Reply.prototype.serializeInput = function (input, schema, httpStatus) {
|
|
|
362
370
|
: httpStatus
|
|
363
371
|
|
|
364
372
|
if (httpStatus != null) {
|
|
365
|
-
serialize = this
|
|
373
|
+
serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]
|
|
366
374
|
|
|
367
375
|
if (serialize == null) throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
|
|
368
376
|
} else {
|
|
369
377
|
// Check if serialize function already compiled
|
|
370
|
-
if (this
|
|
371
|
-
serialize = this
|
|
378
|
+
if (this[kRouteContext][kReplySerializeWeakMap]?.has(schema)) {
|
|
379
|
+
serialize = this[kRouteContext][kReplySerializeWeakMap].get(schema)
|
|
372
380
|
} else {
|
|
373
381
|
serialize = this.compileSerializationSchema(schema, httpStatus)
|
|
374
382
|
}
|
|
@@ -381,10 +389,10 @@ Reply.prototype.serialize = function (payload) {
|
|
|
381
389
|
if (this[kReplySerializer] !== null) {
|
|
382
390
|
return this[kReplySerializer](payload)
|
|
383
391
|
} else {
|
|
384
|
-
if (this
|
|
385
|
-
return this
|
|
392
|
+
if (this[kRouteContext] && this[kRouteContext][kReplySerializerDefault]) {
|
|
393
|
+
return this[kRouteContext][kReplySerializerDefault](payload, this.raw.statusCode)
|
|
386
394
|
} else {
|
|
387
|
-
return serialize(this
|
|
395
|
+
return serialize(this[kRouteContext], payload, this.raw.statusCode)
|
|
388
396
|
}
|
|
389
397
|
}
|
|
390
398
|
}
|
|
@@ -450,9 +458,9 @@ Reply.prototype.then = function (fulfilled, rejected) {
|
|
|
450
458
|
}
|
|
451
459
|
|
|
452
460
|
function preserializeHook (reply, payload) {
|
|
453
|
-
if (reply.
|
|
461
|
+
if (reply[kRouteContext].preSerialization !== null) {
|
|
454
462
|
onSendHookRunner(
|
|
455
|
-
reply.
|
|
463
|
+
reply[kRouteContext].preSerialization,
|
|
456
464
|
reply.request,
|
|
457
465
|
reply,
|
|
458
466
|
payload,
|
|
@@ -472,10 +480,10 @@ function preserializeHookEnd (err, request, reply, payload) {
|
|
|
472
480
|
try {
|
|
473
481
|
if (reply[kReplySerializer] !== null) {
|
|
474
482
|
payload = reply[kReplySerializer](payload)
|
|
475
|
-
} else if (reply
|
|
476
|
-
payload = reply
|
|
483
|
+
} else if (reply[kRouteContext] && reply[kRouteContext][kReplySerializerDefault]) {
|
|
484
|
+
payload = reply[kRouteContext][kReplySerializerDefault](payload, reply.raw.statusCode)
|
|
477
485
|
} else {
|
|
478
|
-
payload = serialize(reply
|
|
486
|
+
payload = serialize(reply[kRouteContext], payload, reply.raw.statusCode)
|
|
479
487
|
}
|
|
480
488
|
} catch (e) {
|
|
481
489
|
wrapSeralizationError(e, reply)
|
|
@@ -487,13 +495,13 @@ function preserializeHookEnd (err, request, reply, payload) {
|
|
|
487
495
|
}
|
|
488
496
|
|
|
489
497
|
function wrapSeralizationError (error, reply) {
|
|
490
|
-
error.serialization = reply.
|
|
498
|
+
error.serialization = reply[kRouteContext].config
|
|
491
499
|
}
|
|
492
500
|
|
|
493
501
|
function onSendHook (reply, payload) {
|
|
494
|
-
if (reply.
|
|
502
|
+
if (reply[kRouteContext].onSend !== null) {
|
|
495
503
|
onSendHookRunner(
|
|
496
|
-
reply.
|
|
504
|
+
reply[kRouteContext].onSend,
|
|
497
505
|
reply.request,
|
|
498
506
|
reply,
|
|
499
507
|
payload,
|
|
@@ -562,7 +570,7 @@ function onSendEnd (reply, payload) {
|
|
|
562
570
|
const contentLength = reply[kReplyHeaders]['content-length']
|
|
563
571
|
if (!contentLength ||
|
|
564
572
|
(req.raw.method !== 'HEAD' &&
|
|
565
|
-
|
|
573
|
+
Number(contentLength) !== Buffer.byteLength(payload)
|
|
566
574
|
)
|
|
567
575
|
) {
|
|
568
576
|
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
|
|
@@ -660,10 +668,10 @@ function sendStreamTrailer (payload, res, reply) {
|
|
|
660
668
|
}
|
|
661
669
|
|
|
662
670
|
function onErrorHook (reply, error, cb) {
|
|
663
|
-
if (reply.
|
|
671
|
+
if (reply[kRouteContext].onError !== null && !reply[kReplyNextErrorHandler]) {
|
|
664
672
|
reply[kReplyIsRunningOnErrorHook] = true
|
|
665
673
|
onSendHookRunner(
|
|
666
|
-
reply.
|
|
674
|
+
reply[kRouteContext].onError,
|
|
667
675
|
reply.request,
|
|
668
676
|
reply,
|
|
669
677
|
error,
|
|
@@ -682,7 +690,7 @@ function setupResponseListeners (reply) {
|
|
|
682
690
|
reply.raw.removeListener('finish', onResFinished)
|
|
683
691
|
reply.raw.removeListener('error', onResFinished)
|
|
684
692
|
|
|
685
|
-
const ctx = reply
|
|
693
|
+
const ctx = reply[kRouteContext]
|
|
686
694
|
|
|
687
695
|
if (ctx && ctx.onResponse !== null) {
|
|
688
696
|
hookRunner(
|
|
@@ -759,18 +767,18 @@ function buildReply (R) {
|
|
|
759
767
|
}
|
|
760
768
|
|
|
761
769
|
function notFound (reply) {
|
|
762
|
-
if (reply
|
|
770
|
+
if (reply[kRouteContext][kFourOhFourContext] === null) {
|
|
763
771
|
reply.log.warn('Trying to send a NotFound error inside a 404 handler. Sending basic 404 response.')
|
|
764
772
|
reply.code(404).send('404 Not Found')
|
|
765
773
|
return
|
|
766
774
|
}
|
|
767
775
|
|
|
768
|
-
reply.request
|
|
776
|
+
reply.request[kRouteContext] = reply[kRouteContext][kFourOhFourContext]
|
|
769
777
|
|
|
770
778
|
// preHandler hook
|
|
771
|
-
if (reply.
|
|
779
|
+
if (reply[kRouteContext].preHandler !== null) {
|
|
772
780
|
hookRunner(
|
|
773
|
-
reply.
|
|
781
|
+
reply[kRouteContext].preHandler,
|
|
774
782
|
hookIterator,
|
|
775
783
|
reply.request,
|
|
776
784
|
reply,
|
package/lib/request.js
CHANGED
|
@@ -11,7 +11,9 @@ const {
|
|
|
11
11
|
kSchemaQuerystring,
|
|
12
12
|
kSchemaController,
|
|
13
13
|
kOptions,
|
|
14
|
-
kRequestValidateWeakMap
|
|
14
|
+
kRequestValidateWeakMap,
|
|
15
|
+
kRouteContext,
|
|
16
|
+
kPublicRouteContext
|
|
15
17
|
} = require('./symbols')
|
|
16
18
|
const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
|
|
17
19
|
|
|
@@ -25,7 +27,7 @@ const HTTP_PART_SYMBOL_MAP = {
|
|
|
25
27
|
|
|
26
28
|
function Request (id, params, req, query, log, context) {
|
|
27
29
|
this.id = id
|
|
28
|
-
this
|
|
30
|
+
this[kRouteContext] = context
|
|
29
31
|
this.params = params
|
|
30
32
|
this.raw = req
|
|
31
33
|
this.query = query
|
|
@@ -66,7 +68,7 @@ function buildRegularRequest (R) {
|
|
|
66
68
|
const props = [...R.props]
|
|
67
69
|
function _Request (id, params, req, query, log, context) {
|
|
68
70
|
this.id = id
|
|
69
|
-
this
|
|
71
|
+
this[kRouteContext] = context
|
|
70
72
|
this.params = params
|
|
71
73
|
this.raw = req
|
|
72
74
|
this.query = query
|
|
@@ -139,7 +141,7 @@ function buildRequestWithTrustProxy (R, trustProxy) {
|
|
|
139
141
|
Object.defineProperties(Request.prototype, {
|
|
140
142
|
server: {
|
|
141
143
|
get () {
|
|
142
|
-
return this.
|
|
144
|
+
return this[kRouteContext].server
|
|
143
145
|
}
|
|
144
146
|
},
|
|
145
147
|
url: {
|
|
@@ -152,19 +154,35 @@ Object.defineProperties(Request.prototype, {
|
|
|
152
154
|
return this.raw.method
|
|
153
155
|
}
|
|
154
156
|
},
|
|
157
|
+
context: {
|
|
158
|
+
get () {
|
|
159
|
+
warning.emit('FSTDEP012')
|
|
160
|
+
return this[kRouteContext]
|
|
161
|
+
}
|
|
162
|
+
},
|
|
155
163
|
routerPath: {
|
|
156
164
|
get () {
|
|
157
|
-
return this.
|
|
165
|
+
return this[kRouteContext].config.url
|
|
158
166
|
}
|
|
159
167
|
},
|
|
160
168
|
routerMethod: {
|
|
161
169
|
get () {
|
|
162
|
-
return this.
|
|
170
|
+
return this[kRouteContext].config.method
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
routeConfig: {
|
|
174
|
+
get () {
|
|
175
|
+
return this[kRouteContext][kPublicRouteContext].config
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
routeSchema: {
|
|
179
|
+
get () {
|
|
180
|
+
return this[kRouteContext][kPublicRouteContext].schema
|
|
163
181
|
}
|
|
164
182
|
},
|
|
165
183
|
is404: {
|
|
166
184
|
get () {
|
|
167
|
-
return this.
|
|
185
|
+
return this[kRouteContext].config.url === undefined
|
|
168
186
|
}
|
|
169
187
|
},
|
|
170
188
|
connection: {
|
|
@@ -215,9 +233,9 @@ Object.defineProperties(Request.prototype, {
|
|
|
215
233
|
value: function (httpPartOrSchema) {
|
|
216
234
|
if (typeof httpPartOrSchema === 'string') {
|
|
217
235
|
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
|
|
218
|
-
return this
|
|
236
|
+
return this[kRouteContext][symbol]
|
|
219
237
|
} else if (typeof httpPartOrSchema === 'object') {
|
|
220
|
-
return this
|
|
238
|
+
return this[kRouteContext][kRequestValidateWeakMap]?.get(httpPartOrSchema)
|
|
221
239
|
}
|
|
222
240
|
}
|
|
223
241
|
},
|
|
@@ -225,11 +243,11 @@ Object.defineProperties(Request.prototype, {
|
|
|
225
243
|
value: function (schema, httpPart = null) {
|
|
226
244
|
const { method, url } = this
|
|
227
245
|
|
|
228
|
-
if (this
|
|
229
|
-
return this
|
|
246
|
+
if (this[kRouteContext][kRequestValidateWeakMap]?.has(schema)) {
|
|
247
|
+
return this[kRouteContext][kRequestValidateWeakMap].get(schema)
|
|
230
248
|
}
|
|
231
249
|
|
|
232
|
-
const validatorCompiler = this.
|
|
250
|
+
const validatorCompiler = this[kRouteContext].validatorCompiler ||
|
|
233
251
|
this.server[kSchemaController].validatorCompiler ||
|
|
234
252
|
(
|
|
235
253
|
// We compile the schemas if no custom validatorCompiler is provided
|
|
@@ -250,11 +268,11 @@ Object.defineProperties(Request.prototype, {
|
|
|
250
268
|
// if it is not used
|
|
251
269
|
// TODO: Explore a central cache for all the schemas shared across
|
|
252
270
|
// encapsulated contexts
|
|
253
|
-
if (this
|
|
254
|
-
this
|
|
271
|
+
if (this[kRouteContext][kRequestValidateWeakMap] == null) {
|
|
272
|
+
this[kRouteContext][kRequestValidateWeakMap] = new WeakMap()
|
|
255
273
|
}
|
|
256
274
|
|
|
257
|
-
this
|
|
275
|
+
this[kRouteContext][kRequestValidateWeakMap].set(schema, validateFn)
|
|
258
276
|
|
|
259
277
|
return validateFn
|
|
260
278
|
}
|
|
@@ -268,7 +286,7 @@ Object.defineProperties(Request.prototype, {
|
|
|
268
286
|
|
|
269
287
|
if (symbol) {
|
|
270
288
|
// Validate using the HTTP Request Part schema
|
|
271
|
-
validate = this
|
|
289
|
+
validate = this[kRouteContext][symbol]
|
|
272
290
|
}
|
|
273
291
|
|
|
274
292
|
// We cannot compile if the schema is missed
|
|
@@ -280,8 +298,8 @@ Object.defineProperties(Request.prototype, {
|
|
|
280
298
|
}
|
|
281
299
|
|
|
282
300
|
if (validate == null) {
|
|
283
|
-
if (this
|
|
284
|
-
validate = this
|
|
301
|
+
if (this[kRouteContext][kRequestValidateWeakMap]?.has(schema)) {
|
|
302
|
+
validate = this[kRouteContext][kRequestValidateWeakMap].get(schema)
|
|
285
303
|
} else {
|
|
286
304
|
// We proceed to compile if there's no validate function yet
|
|
287
305
|
validate = this.compileValidationSchema(schema, httpPart)
|
package/lib/route.js
CHANGED
|
@@ -8,7 +8,6 @@ const { supportedMethods } = require('./httpMethods')
|
|
|
8
8
|
const { normalizeSchema } = require('./schemas')
|
|
9
9
|
const { parseHeadOnSendHandlers } = require('./headRoute')
|
|
10
10
|
const warning = require('./warnings')
|
|
11
|
-
const { kRequestAcceptVersion, kRouteByFastify } = require('./symbols')
|
|
12
11
|
|
|
13
12
|
const {
|
|
14
13
|
compileSchemasForValidation,
|
|
@@ -37,7 +36,10 @@ const {
|
|
|
37
36
|
kDisableRequestLogging,
|
|
38
37
|
kSchemaErrorFormatter,
|
|
39
38
|
kErrorHandler,
|
|
40
|
-
kHasBeenDecorated
|
|
39
|
+
kHasBeenDecorated,
|
|
40
|
+
kRequestAcceptVersion,
|
|
41
|
+
kRouteByFastify,
|
|
42
|
+
kRouteContext
|
|
41
43
|
} = require('./symbols.js')
|
|
42
44
|
const { buildErrorHandler } = require('./error-handler')
|
|
43
45
|
|
|
@@ -155,6 +157,12 @@ function buildRouting (options) {
|
|
|
155
157
|
// Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
|
|
156
158
|
const opts = { ...options }
|
|
157
159
|
|
|
160
|
+
const { exposeHeadRoute } = opts
|
|
161
|
+
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
|
162
|
+
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
|
163
|
+
// we need to clone a set of initial options for HEAD route
|
|
164
|
+
const headOpts = shouldExposeHead && options.method === 'GET' ? { ...options } : null
|
|
165
|
+
|
|
158
166
|
throwIfAlreadyStarted('Cannot add route when fastify instance is already started!')
|
|
159
167
|
|
|
160
168
|
const path = opts.url || opts.path || ''
|
|
@@ -321,7 +329,8 @@ function buildRouting (options) {
|
|
|
321
329
|
schemaController.setupValidator(this[kOptions])
|
|
322
330
|
}
|
|
323
331
|
try {
|
|
324
|
-
|
|
332
|
+
const isCustom = typeof opts?.validatorCompiler === 'function' || schemaController.isCustomValidatorCompiler
|
|
333
|
+
compileSchemasForValidation(context, opts.validatorCompiler || schemaController.validatorCompiler, isCustom)
|
|
325
334
|
} catch (error) {
|
|
326
335
|
throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)
|
|
327
336
|
}
|
|
@@ -342,13 +351,10 @@ function buildRouting (options) {
|
|
|
342
351
|
|
|
343
352
|
// register head route in sync
|
|
344
353
|
// we must place it after the `this.after`
|
|
345
|
-
const { exposeHeadRoute } = opts
|
|
346
|
-
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
|
347
|
-
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
|
348
354
|
|
|
349
355
|
if (shouldExposeHead && options.method === 'GET' && !hasHEADHandler) {
|
|
350
|
-
const onSendHandlers = parseHeadOnSendHandlers(
|
|
351
|
-
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...
|
|
356
|
+
const onSendHandlers = parseHeadOnSendHandlers(headOpts.onSend)
|
|
357
|
+
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...headOpts, onSend: onSendHandlers }, isFastify: true })
|
|
352
358
|
} else if (hasHEADHandler && exposeHeadRoute) {
|
|
353
359
|
warning.emit('FSTDEP007')
|
|
354
360
|
}
|
|
@@ -490,8 +496,8 @@ function runPreParsing (err, request, reply) {
|
|
|
490
496
|
|
|
491
497
|
request[kRequestPayloadStream] = request.raw
|
|
492
498
|
|
|
493
|
-
if (
|
|
494
|
-
preParsingHookRunner(
|
|
499
|
+
if (request[kRouteContext].preParsing !== null) {
|
|
500
|
+
preParsingHookRunner(request[kRouteContext].preParsing, request, reply, handleRequest)
|
|
495
501
|
} else {
|
|
496
502
|
handleRequest(null, request, reply)
|
|
497
503
|
}
|
package/lib/schema-controller.js
CHANGED
|
@@ -25,7 +25,9 @@ function buildSchemaController (parentSchemaCtrl, opts) {
|
|
|
25
25
|
|
|
26
26
|
const option = {
|
|
27
27
|
bucket: (opts && opts.bucket) || buildSchemas,
|
|
28
|
-
compilersFactory
|
|
28
|
+
compilersFactory,
|
|
29
|
+
isCustomValidatorCompiler: typeof opts?.compilersFactory?.buildValidator === 'function',
|
|
30
|
+
isCustomSerializerCompiler: typeof opts?.compilersFactory?.buildValidator === 'function'
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
return new SchemaController(undefined, option)
|
|
@@ -37,6 +39,8 @@ class SchemaController {
|
|
|
37
39
|
this.addedSchemas = false
|
|
38
40
|
|
|
39
41
|
this.compilersFactory = this.opts.compilersFactory
|
|
42
|
+
this.isCustomValidatorCompiler = this.opts.isCustomValidatorCompiler || false
|
|
43
|
+
this.isCustomSerializerCompiler = this.opts.isCustomSerializerCompiler || false
|
|
40
44
|
|
|
41
45
|
if (parent) {
|
|
42
46
|
this.schemaBucket = this.opts.bucket(parent.getSchemas())
|
|
@@ -65,10 +69,12 @@ class SchemaController {
|
|
|
65
69
|
// Schema Controller compilers holder
|
|
66
70
|
setValidatorCompiler (validatorCompiler) {
|
|
67
71
|
this.validatorCompiler = validatorCompiler
|
|
72
|
+
this.isCustomValidatorCompiler = true
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
setSerializerCompiler (serializerCompiler) {
|
|
71
76
|
this.serializerCompiler = serializerCompiler
|
|
77
|
+
this.isCustomSerializerCompiler = true
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
getValidatorCompiler () {
|
package/lib/server.js
CHANGED
|
@@ -331,8 +331,8 @@ function normalizeListenArgs (args) {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
function normalizePort (firstArg) {
|
|
334
|
-
const port =
|
|
335
|
-
return port >= 0 && !Number.isNaN(port) ? port : 0
|
|
334
|
+
const port = Number(firstArg)
|
|
335
|
+
return port >= 0 && !Number.isNaN(port) && Number.isInteger(port) ? port : 0
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
function logServerAddress (server) {
|
package/lib/symbols.js
CHANGED
|
@@ -14,6 +14,8 @@ const keys = {
|
|
|
14
14
|
kOptions: Symbol('fastify.options'),
|
|
15
15
|
kDisableRequestLogging: Symbol('fastify.disableRequestLogging'),
|
|
16
16
|
kPluginNameChain: Symbol('fastify.pluginNameChain'),
|
|
17
|
+
kRouteContext: Symbol('fastify.context'),
|
|
18
|
+
kPublicRouteContext: Symbol('fastify.routeOptions'),
|
|
17
19
|
// Schema
|
|
18
20
|
kSchemaController: Symbol('fastify.schemaController'),
|
|
19
21
|
kSchemaHeaders: Symbol('headers-schema'),
|
package/lib/validation.js
CHANGED
|
@@ -32,7 +32,7 @@ function compileSchemasForSerialization (context, compile) {
|
|
|
32
32
|
}, {})
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function compileSchemasForValidation (context, compile) {
|
|
35
|
+
function compileSchemasForValidation (context, compile, isCustom) {
|
|
36
36
|
const { schema } = context
|
|
37
37
|
if (!schema) {
|
|
38
38
|
return
|
|
@@ -41,8 +41,9 @@ function compileSchemasForValidation (context, compile) {
|
|
|
41
41
|
const { method, url } = context.config || {}
|
|
42
42
|
|
|
43
43
|
const headers = schema.headers
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// the or part is used for backward compatibility
|
|
45
|
+
if (headers && (isCustom || Object.getPrototypeOf(headers) !== Object.prototype)) {
|
|
46
|
+
// do not mess with schema when custom validator applied, e.g. Joi, Typebox
|
|
46
47
|
context[headersSchema] = compile({ schema: headers, method, url, httpPart: 'headers' })
|
|
47
48
|
} else if (headers) {
|
|
48
49
|
// The header keys are case insensitive
|
package/lib/warnings.js
CHANGED
|
@@ -21,4 +21,6 @@ warning.create('FastifyDeprecation', 'FSTDEP010', 'Modifying the "reply.sent" pr
|
|
|
21
21
|
|
|
22
22
|
warning.create('FastifyDeprecation', 'FSTDEP011', 'Variadic listen method is deprecated. Please use ".listen(optionsObject)" instead. The variadic signature will be removed in `fastify@5`.')
|
|
23
23
|
|
|
24
|
+
warning.create('FastifyDeprecation', 'FSTDEP012', 'Request#context property access is deprecated. Please use "Request#routeConfig" or "Request#routeSchema" instead for accessing Route settings. The "Request#context" will be removed in `fastify@5`.')
|
|
25
|
+
|
|
24
26
|
module.exports = warning
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -126,35 +126,35 @@
|
|
|
126
126
|
"homepage": "https://www.fastify.io/",
|
|
127
127
|
"devDependencies": {
|
|
128
128
|
"@fastify/pre-commit": "^2.0.2",
|
|
129
|
-
"@sinclair/typebox": "^0.24.
|
|
129
|
+
"@sinclair/typebox": "^0.24.41",
|
|
130
130
|
"@sinonjs/fake-timers": "^9.1.2",
|
|
131
|
-
"@types/node": "^18.
|
|
132
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
133
|
-
"@typescript-eslint/parser": "^5.
|
|
131
|
+
"@types/node": "^18.7.18",
|
|
132
|
+
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
|
133
|
+
"@typescript-eslint/parser": "^5.37.0",
|
|
134
134
|
"ajv": "^8.11.0",
|
|
135
135
|
"ajv-errors": "^3.0.0",
|
|
136
136
|
"ajv-formats": "^2.1.1",
|
|
137
137
|
"ajv-i18n": "^4.2.0",
|
|
138
138
|
"ajv-merge-patch": "^5.0.1",
|
|
139
139
|
"branch-comparer": "^1.1.0",
|
|
140
|
-
"eslint": "^8.
|
|
141
|
-
"eslint-config-standard": "^17.0.0
|
|
140
|
+
"eslint": "^8.23.1",
|
|
141
|
+
"eslint-config-standard": "^17.0.0",
|
|
142
142
|
"eslint-import-resolver-node": "^0.3.6",
|
|
143
143
|
"eslint-plugin-import": "^2.26.0",
|
|
144
|
-
"eslint-plugin-n": "^15.2.
|
|
145
|
-
"eslint-plugin-promise": "^6.0.
|
|
144
|
+
"eslint-plugin-n": "^15.2.5",
|
|
145
|
+
"eslint-plugin-promise": "^6.0.1",
|
|
146
146
|
"fast-json-body": "^1.1.0",
|
|
147
|
-
"fast-json-stringify": "^5.
|
|
148
|
-
"fastify-plugin": "^4.
|
|
147
|
+
"fast-json-stringify": "^5.3.0",
|
|
148
|
+
"fastify-plugin": "^4.2.1",
|
|
149
149
|
"fluent-json-schema": "^3.1.0",
|
|
150
150
|
"form-data": "^4.0.0",
|
|
151
151
|
"h2url": "^0.2.0",
|
|
152
152
|
"http-errors": "^2.0.0",
|
|
153
153
|
"joi": "^17.6.0",
|
|
154
|
-
"json-schema-to-ts": "^2.5.
|
|
154
|
+
"json-schema-to-ts": "^2.5.5",
|
|
155
155
|
"JSONStream": "^1.3.5",
|
|
156
156
|
"license-checker": "^25.0.1",
|
|
157
|
-
"markdownlint-cli2": "^0.5.
|
|
157
|
+
"markdownlint-cli2": "^0.5.1",
|
|
158
158
|
"proxyquire": "^2.1.3",
|
|
159
159
|
"pump": "^3.0.0",
|
|
160
160
|
"self-cert": "^2.0.0",
|
|
@@ -162,29 +162,29 @@
|
|
|
162
162
|
"simple-get": "^4.0.1",
|
|
163
163
|
"snazzy": "^9.0.0",
|
|
164
164
|
"split2": "^4.1.0",
|
|
165
|
-
"standard": "^17.0.0
|
|
166
|
-
"tap": "^16.
|
|
167
|
-
"tsd": "^0.
|
|
168
|
-
"typescript": "^4.
|
|
169
|
-
"undici": "^5.
|
|
165
|
+
"standard": "^17.0.0",
|
|
166
|
+
"tap": "^16.3.0",
|
|
167
|
+
"tsd": "^0.24.1",
|
|
168
|
+
"typescript": "^4.8.3",
|
|
169
|
+
"undici": "^5.10.0",
|
|
170
170
|
"vary": "^1.1.2",
|
|
171
171
|
"yup": "^0.32.11"
|
|
172
172
|
},
|
|
173
173
|
"dependencies": {
|
|
174
|
-
"@fastify/ajv-compiler": "^3.
|
|
174
|
+
"@fastify/ajv-compiler": "^3.3.1",
|
|
175
175
|
"@fastify/error": "^3.0.0",
|
|
176
|
-
"@fastify/fast-json-stringify-compiler": "^4.
|
|
176
|
+
"@fastify/fast-json-stringify-compiler": "^4.1.0",
|
|
177
177
|
"abstract-logging": "^2.0.1",
|
|
178
|
-
"avvio": "^8.
|
|
179
|
-
"find-my-way": "^7.
|
|
180
|
-
"light-my-request": "^5.
|
|
181
|
-
"pino": "^8.
|
|
178
|
+
"avvio": "^8.2.0",
|
|
179
|
+
"find-my-way": "^7.2.0",
|
|
180
|
+
"light-my-request": "^5.6.1",
|
|
181
|
+
"pino": "^8.5.0",
|
|
182
182
|
"process-warning": "^2.0.0",
|
|
183
183
|
"proxy-addr": "^2.0.7",
|
|
184
184
|
"rfdc": "^1.3.0",
|
|
185
|
-
"secure-json-parse": "^2.
|
|
185
|
+
"secure-json-parse": "^2.5.0",
|
|
186
186
|
"semver": "^7.3.7",
|
|
187
|
-
"tiny-lru": "^
|
|
187
|
+
"tiny-lru": "^9.0.2"
|
|
188
188
|
},
|
|
189
189
|
"standard": {
|
|
190
190
|
"ignore": [
|
package/test/404s.test.js
CHANGED
|
@@ -6,6 +6,7 @@ const fp = require('fastify-plugin')
|
|
|
6
6
|
const sget = require('simple-get').concat
|
|
7
7
|
const errors = require('http-errors')
|
|
8
8
|
const split = require('split2')
|
|
9
|
+
const FormData = require('form-data')
|
|
9
10
|
const Fastify = require('..')
|
|
10
11
|
|
|
11
12
|
function getUrl (app) {
|
|
@@ -18,7 +19,7 @@ function getUrl (app) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
test('default 404', t => {
|
|
21
|
-
t.plan(
|
|
22
|
+
t.plan(5)
|
|
22
23
|
|
|
23
24
|
const test = t.test
|
|
24
25
|
const fastify = Fastify()
|
|
@@ -74,6 +75,23 @@ test('default 404', t => {
|
|
|
74
75
|
t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
|
|
75
76
|
})
|
|
76
77
|
})
|
|
78
|
+
|
|
79
|
+
test('using post method and multipart/formdata', t => {
|
|
80
|
+
t.plan(3)
|
|
81
|
+
const form = FormData()
|
|
82
|
+
form.append('test-field', 'just some field')
|
|
83
|
+
|
|
84
|
+
sget({
|
|
85
|
+
method: 'POST',
|
|
86
|
+
url: getUrl(fastify) + '/notSupported',
|
|
87
|
+
body: form,
|
|
88
|
+
json: false
|
|
89
|
+
}, (err, response, body) => {
|
|
90
|
+
t.error(err)
|
|
91
|
+
t.equal(response.statusCode, 404)
|
|
92
|
+
t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
|
|
93
|
+
})
|
|
94
|
+
})
|
|
77
95
|
})
|
|
78
96
|
})
|
|
79
97
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const t = require('tap')
|
|
4
4
|
const test = t.test
|
|
5
|
+
const { kRouteContext } = require('../lib/symbols')
|
|
5
6
|
const Fastify = require('..')
|
|
6
7
|
|
|
7
8
|
const schema = {
|
|
@@ -13,7 +14,7 @@ const schema = {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function handler (req, reply) {
|
|
16
|
-
reply.send(reply.
|
|
17
|
+
reply.send(reply[kRouteContext].config)
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
test('config', t => {
|