fastify 4.2.0 → 4.4.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/README.md +13 -14
- package/docs/Guides/Database.md +2 -2
- package/docs/Guides/Ecosystem.md +42 -18
- package/docs/Guides/Migration-Guide-V4.md +29 -0
- package/docs/Guides/Plugins-Guide.md +5 -0
- package/docs/Reference/HTTP2.md +1 -3
- package/docs/Reference/Hooks.md +4 -1
- package/docs/Reference/Logging.md +1 -1
- package/docs/Reference/Reply.md +177 -0
- package/docs/Reference/Request.md +171 -0
- package/docs/Reference/Routes.md +4 -2
- package/docs/Reference/Server.md +4 -1
- package/docs/Reference/Type-Providers.md +1 -15
- package/docs/Reference/TypeScript.md +27 -3
- package/fastify.d.ts +1 -1
- package/fastify.js +3 -3
- package/lib/contentTypeParser.js +10 -2
- package/lib/context.js +10 -1
- package/lib/error-serializer.js +12 -12
- package/lib/errors.js +8 -0
- package/lib/handleRequest.js +2 -2
- package/lib/httpMethods.js +22 -0
- package/lib/reply.js +101 -24
- package/lib/request.js +97 -1
- package/lib/route.js +3 -1
- package/lib/symbols.js +15 -9
- package/package.json +7 -7
- package/test/build/error-serializer.test.js +3 -1
- package/test/content-parser.test.js +15 -0
- package/test/copy.test.js +41 -0
- package/test/internals/all.test.js +8 -2
- package/test/internals/reply-serialize.test.js +583 -0
- package/test/internals/reply.test.js +4 -1
- package/test/internals/request-validate.test.js +1269 -0
- package/test/internals/request.test.js +11 -2
- package/test/lock.test.js +73 -0
- package/test/mkcol.test.js +38 -0
- package/test/move.test.js +45 -0
- package/test/propfind.test.js +108 -0
- package/test/proppatch.test.js +78 -0
- package/test/request-error.test.js +44 -1
- package/test/schema-validation.test.js +71 -0
- package/test/search.test.js +100 -0
- package/test/trace.test.js +21 -0
- package/test/types/fastify.test-d.ts +12 -1
- package/test/types/hooks.test-d.ts +1 -2
- package/test/types/import.ts +1 -1
- package/test/types/instance.test-d.ts +4 -1
- package/test/types/logger.test-d.ts +4 -5
- package/test/types/reply.test-d.ts +44 -3
- package/test/types/request.test-d.ts +10 -29
- package/test/types/type-provider.test-d.ts +79 -7
- package/test/unlock.test.js +41 -0
- package/test/validation-error-handling.test.js +6 -1
- package/types/hooks.d.ts +19 -39
- package/types/instance.d.ts +78 -130
- package/types/logger.d.ts +7 -4
- package/types/reply.d.ts +7 -1
- package/types/request.d.ts +16 -3
- package/types/route.d.ts +23 -29
- package/types/schema.d.ts +4 -1
- package/types/type-provider.d.ts +8 -20
- package/types/utils.d.ts +2 -1
package/lib/reply.js
CHANGED
|
@@ -16,7 +16,11 @@ const {
|
|
|
16
16
|
kReplyHasStatusCode,
|
|
17
17
|
kReplyIsRunningOnErrorHook,
|
|
18
18
|
kReplyNextErrorHandler,
|
|
19
|
-
kDisableRequestLogging
|
|
19
|
+
kDisableRequestLogging,
|
|
20
|
+
kSchemaResponse,
|
|
21
|
+
kReplySerializeWeakMap,
|
|
22
|
+
kSchemaController,
|
|
23
|
+
kOptions
|
|
20
24
|
} = require('./symbols.js')
|
|
21
25
|
const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks')
|
|
22
26
|
|
|
@@ -38,7 +42,8 @@ const {
|
|
|
38
42
|
FST_ERR_SEND_INSIDE_ONERR,
|
|
39
43
|
FST_ERR_BAD_STATUS_CODE,
|
|
40
44
|
FST_ERR_BAD_TRAILER_NAME,
|
|
41
|
-
FST_ERR_BAD_TRAILER_VALUE
|
|
45
|
+
FST_ERR_BAD_TRAILER_VALUE,
|
|
46
|
+
FST_ERR_MISSING_SERIALIZATION_FN
|
|
42
47
|
} = require('./errors')
|
|
43
48
|
const warning = require('./warnings')
|
|
44
49
|
|
|
@@ -203,10 +208,8 @@ Reply.prototype.getHeaders = function () {
|
|
|
203
208
|
|
|
204
209
|
Reply.prototype.hasHeader = function (key) {
|
|
205
210
|
key = key.toLowerCase()
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
return this.raw.hasHeader(key)
|
|
211
|
+
|
|
212
|
+
return this[kReplyHeaders][key] !== undefined || this.raw.hasHeader(key)
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
Reply.prototype.removeHeader = function (key) {
|
|
@@ -216,25 +219,24 @@ Reply.prototype.removeHeader = function (key) {
|
|
|
216
219
|
return this
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
Reply.prototype.header = function (key, value) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// default the value to ''
|
|
223
|
-
value = value === undefined ? '' : value
|
|
222
|
+
Reply.prototype.header = function (key, value = '') {
|
|
223
|
+
key = key.toLowerCase()
|
|
224
224
|
|
|
225
|
-
if (this[kReplyHeaders][
|
|
225
|
+
if (this[kReplyHeaders][key] && key === 'set-cookie') {
|
|
226
226
|
// https://tools.ietf.org/html/rfc7230#section-3.2.2
|
|
227
|
-
if (typeof this[kReplyHeaders][
|
|
228
|
-
this[kReplyHeaders][
|
|
227
|
+
if (typeof this[kReplyHeaders][key] === 'string') {
|
|
228
|
+
this[kReplyHeaders][key] = [this[kReplyHeaders][key]]
|
|
229
229
|
}
|
|
230
|
+
|
|
230
231
|
if (Array.isArray(value)) {
|
|
231
|
-
|
|
232
|
+
this[kReplyHeaders][key].push(...value)
|
|
232
233
|
} else {
|
|
233
|
-
this[kReplyHeaders][
|
|
234
|
+
this[kReplyHeaders][key].push(value)
|
|
234
235
|
}
|
|
235
236
|
} else {
|
|
236
|
-
this[kReplyHeaders][
|
|
237
|
+
this[kReplyHeaders][key] = value
|
|
237
238
|
}
|
|
239
|
+
|
|
238
240
|
return this
|
|
239
241
|
}
|
|
240
242
|
|
|
@@ -245,6 +247,7 @@ Reply.prototype.headers = function (headers) {
|
|
|
245
247
|
const key = keys[i]
|
|
246
248
|
this.header(key, headers[key])
|
|
247
249
|
}
|
|
250
|
+
|
|
248
251
|
return this
|
|
249
252
|
}
|
|
250
253
|
|
|
@@ -279,8 +282,7 @@ Reply.prototype.trailer = function (key, fn) {
|
|
|
279
282
|
}
|
|
280
283
|
|
|
281
284
|
Reply.prototype.hasTrailer = function (key) {
|
|
282
|
-
|
|
283
|
-
return this[kReplyTrailers][key.toLowerCase()] !== undefined
|
|
285
|
+
return this[kReplyTrailers]?.[key.toLowerCase()] !== undefined
|
|
284
286
|
}
|
|
285
287
|
|
|
286
288
|
Reply.prototype.removeTrailer = function (key) {
|
|
@@ -302,6 +304,79 @@ Reply.prototype.code = function (code) {
|
|
|
302
304
|
|
|
303
305
|
Reply.prototype.status = Reply.prototype.code
|
|
304
306
|
|
|
307
|
+
Reply.prototype.getSerializationFunction = function (schemaOrStatus) {
|
|
308
|
+
let serialize
|
|
309
|
+
|
|
310
|
+
if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
|
|
311
|
+
serialize = this.context[kSchemaResponse]?.[schemaOrStatus]
|
|
312
|
+
} else if (typeof schemaOrStatus === 'object') {
|
|
313
|
+
serialize = this.context[kReplySerializeWeakMap]?.get(schemaOrStatus)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return serialize
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null) {
|
|
320
|
+
const { request } = this
|
|
321
|
+
const { method, url } = request
|
|
322
|
+
|
|
323
|
+
// Check if serialize function already compiled
|
|
324
|
+
if (this.context[kReplySerializeWeakMap]?.has(schema)) {
|
|
325
|
+
return this.context[kReplySerializeWeakMap].get(schema)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const serializerCompiler = this.context.serializerCompiler ||
|
|
329
|
+
this.server[kSchemaController].serializerCompiler ||
|
|
330
|
+
(
|
|
331
|
+
// We compile the schemas if no custom serializerCompiler is provided
|
|
332
|
+
// nor set
|
|
333
|
+
this.server[kSchemaController].setupSerializer(this.server[kOptions]) ||
|
|
334
|
+
this.server[kSchemaController].serializerCompiler
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
const serializeFn = serializerCompiler({
|
|
338
|
+
schema,
|
|
339
|
+
method,
|
|
340
|
+
url,
|
|
341
|
+
httpStatus
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
// We create a WeakMap to compile the schema only once
|
|
345
|
+
// Its done leazily to avoid add overhead by creating the WeakMap
|
|
346
|
+
// if it is not used
|
|
347
|
+
// TODO: Explore a central cache for all the schemas shared across
|
|
348
|
+
// encapsulated contexts
|
|
349
|
+
if (this.context[kReplySerializeWeakMap] == null) {
|
|
350
|
+
this.context[kReplySerializeWeakMap] = new WeakMap()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
this.context[kReplySerializeWeakMap].set(schema, serializeFn)
|
|
354
|
+
|
|
355
|
+
return serializeFn
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
Reply.prototype.serializeInput = function (input, schema, httpStatus) {
|
|
359
|
+
let serialize
|
|
360
|
+
httpStatus = typeof schema === 'string' || typeof schema === 'number'
|
|
361
|
+
? schema
|
|
362
|
+
: httpStatus
|
|
363
|
+
|
|
364
|
+
if (httpStatus != null) {
|
|
365
|
+
serialize = this.context[kSchemaResponse]?.[httpStatus]
|
|
366
|
+
|
|
367
|
+
if (serialize == null) throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
|
|
368
|
+
} else {
|
|
369
|
+
// Check if serialize function already compiled
|
|
370
|
+
if (this.context[kReplySerializeWeakMap]?.has(schema)) {
|
|
371
|
+
serialize = this.context[kReplySerializeWeakMap].get(schema)
|
|
372
|
+
} else {
|
|
373
|
+
serialize = this.compileSerializationSchema(schema, httpStatus)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return serialize(input)
|
|
378
|
+
}
|
|
379
|
+
|
|
305
380
|
Reply.prototype.serialize = function (payload) {
|
|
306
381
|
if (this[kReplySerializer] !== null) {
|
|
307
382
|
return this[kReplySerializer](payload)
|
|
@@ -330,8 +405,7 @@ Reply.prototype.redirect = function (code, url) {
|
|
|
330
405
|
code = this[kReplyHasStatusCode] ? this.raw.statusCode : 302
|
|
331
406
|
}
|
|
332
407
|
|
|
333
|
-
this.header('location', url).code(code).send()
|
|
334
|
-
return this
|
|
408
|
+
return this.header('location', url).code(code).send()
|
|
335
409
|
}
|
|
336
410
|
|
|
337
411
|
Reply.prototype.callNotFound = function () {
|
|
@@ -485,9 +559,12 @@ function onSendEnd (reply, payload) {
|
|
|
485
559
|
}
|
|
486
560
|
|
|
487
561
|
if (reply[kReplyTrailers] === null) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
562
|
+
const contentLength = reply[kReplyHeaders]['content-length']
|
|
563
|
+
if (!contentLength ||
|
|
564
|
+
(req.raw.method !== 'HEAD' &&
|
|
565
|
+
parseInt(contentLength, 10) !== Buffer.byteLength(payload)
|
|
566
|
+
)
|
|
567
|
+
) {
|
|
491
568
|
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
|
|
492
569
|
}
|
|
493
570
|
}
|
package/lib/request.js
CHANGED
|
@@ -4,8 +4,24 @@ const proxyAddr = require('proxy-addr')
|
|
|
4
4
|
const semver = require('semver')
|
|
5
5
|
const warning = require('./warnings')
|
|
6
6
|
const {
|
|
7
|
-
kHasBeenDecorated
|
|
7
|
+
kHasBeenDecorated,
|
|
8
|
+
kSchemaBody,
|
|
9
|
+
kSchemaHeaders,
|
|
10
|
+
kSchemaParams,
|
|
11
|
+
kSchemaQuerystring,
|
|
12
|
+
kSchemaController,
|
|
13
|
+
kOptions,
|
|
14
|
+
kRequestValidateWeakMap
|
|
8
15
|
} = require('./symbols')
|
|
16
|
+
const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
|
|
17
|
+
|
|
18
|
+
const HTTP_PART_SYMBOL_MAP = {
|
|
19
|
+
body: kSchemaBody,
|
|
20
|
+
headers: kSchemaHeaders,
|
|
21
|
+
params: kSchemaParams,
|
|
22
|
+
querystring: kSchemaQuerystring,
|
|
23
|
+
query: kSchemaQuerystring
|
|
24
|
+
}
|
|
9
25
|
|
|
10
26
|
function Request (id, params, req, query, log, context) {
|
|
11
27
|
this.id = id
|
|
@@ -194,6 +210,86 @@ Object.defineProperties(Request.prototype, {
|
|
|
194
210
|
set (headers) {
|
|
195
211
|
this.additionalHeaders = headers
|
|
196
212
|
}
|
|
213
|
+
},
|
|
214
|
+
getValidationFunction: {
|
|
215
|
+
value: function (httpPartOrSchema) {
|
|
216
|
+
if (typeof httpPartOrSchema === 'string') {
|
|
217
|
+
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
|
|
218
|
+
return this.context[symbol]
|
|
219
|
+
} else if (typeof httpPartOrSchema === 'object') {
|
|
220
|
+
return this.context[kRequestValidateWeakMap]?.get(httpPartOrSchema)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
compileValidationSchema: {
|
|
225
|
+
value: function (schema, httpPart = null) {
|
|
226
|
+
const { method, url } = this
|
|
227
|
+
|
|
228
|
+
if (this.context[kRequestValidateWeakMap]?.has(schema)) {
|
|
229
|
+
return this.context[kRequestValidateWeakMap].get(schema)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const validatorCompiler = this.context.validatorCompiler ||
|
|
233
|
+
this.server[kSchemaController].validatorCompiler ||
|
|
234
|
+
(
|
|
235
|
+
// We compile the schemas if no custom validatorCompiler is provided
|
|
236
|
+
// nor set
|
|
237
|
+
this.server[kSchemaController].setupValidator(this.server[kOptions]) ||
|
|
238
|
+
this.server[kSchemaController].validatorCompiler
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
const validateFn = validatorCompiler({
|
|
242
|
+
schema,
|
|
243
|
+
method,
|
|
244
|
+
url,
|
|
245
|
+
httpPart
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// We create a WeakMap to compile the schema only once
|
|
249
|
+
// Its done leazily to avoid add overhead by creating the WeakMap
|
|
250
|
+
// if it is not used
|
|
251
|
+
// TODO: Explore a central cache for all the schemas shared across
|
|
252
|
+
// encapsulated contexts
|
|
253
|
+
if (this.context[kRequestValidateWeakMap] == null) {
|
|
254
|
+
this.context[kRequestValidateWeakMap] = new WeakMap()
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.context[kRequestValidateWeakMap].set(schema, validateFn)
|
|
258
|
+
|
|
259
|
+
return validateFn
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
validateInput: {
|
|
263
|
+
value: function (input, schema, httpPart) {
|
|
264
|
+
httpPart = typeof schema === 'string' ? schema : httpPart
|
|
265
|
+
|
|
266
|
+
const symbol = (httpPart != null && typeof httpPart === 'string') && HTTP_PART_SYMBOL_MAP[httpPart]
|
|
267
|
+
let validate
|
|
268
|
+
|
|
269
|
+
if (symbol) {
|
|
270
|
+
// Validate using the HTTP Request Part schema
|
|
271
|
+
validate = this.context[symbol]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// We cannot compile if the schema is missed
|
|
275
|
+
if (validate == null && (schema == null ||
|
|
276
|
+
typeof schema !== 'object' ||
|
|
277
|
+
Array.isArray(schema))
|
|
278
|
+
) {
|
|
279
|
+
throw new FST_ERR_REQ_INVALID_VALIDATION_INVOCATION(httpPart)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (validate == null) {
|
|
283
|
+
if (this.context[kRequestValidateWeakMap]?.has(schema)) {
|
|
284
|
+
validate = this.context[kRequestValidateWeakMap].get(schema)
|
|
285
|
+
} else {
|
|
286
|
+
// We proceed to compile if there's no validate function yet
|
|
287
|
+
validate = this.compileValidationSchema(schema, httpPart)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return validate(input)
|
|
292
|
+
}
|
|
197
293
|
}
|
|
198
294
|
})
|
|
199
295
|
|
package/lib/route.js
CHANGED
|
@@ -4,7 +4,7 @@ const FindMyWay = require('find-my-way')
|
|
|
4
4
|
const Context = require('./context')
|
|
5
5
|
const handleRequest = require('./handleRequest')
|
|
6
6
|
const { hookRunner, hookIterator, lifecycleHooks } = require('./hooks')
|
|
7
|
-
const supportedMethods =
|
|
7
|
+
const { supportedMethods } = require('./httpMethods')
|
|
8
8
|
const { normalizeSchema } = require('./schemas')
|
|
9
9
|
const { parseHeadOnSendHandlers } = require('./headRoute')
|
|
10
10
|
const warning = require('./warnings')
|
|
@@ -241,6 +241,8 @@ function buildRouting (options) {
|
|
|
241
241
|
attachValidation: opts.attachValidation,
|
|
242
242
|
schemaErrorFormatter: opts.schemaErrorFormatter,
|
|
243
243
|
replySerializer: this[kReplySerializerDefault],
|
|
244
|
+
validatorCompiler: opts.validatorCompiler,
|
|
245
|
+
serializerCompiler: opts.serializerCompiler,
|
|
244
246
|
server: this,
|
|
245
247
|
isFastify
|
|
246
248
|
})
|
package/lib/symbols.js
CHANGED
|
@@ -9,6 +9,12 @@ const keys = {
|
|
|
9
9
|
kLogLevel: Symbol('fastify.logLevel'),
|
|
10
10
|
kLogSerializers: Symbol('fastify.logSerializers'),
|
|
11
11
|
kHooks: Symbol('fastify.hooks'),
|
|
12
|
+
kContentTypeParser: Symbol('fastify.contentTypeParser'),
|
|
13
|
+
kState: Symbol('fastify.state'),
|
|
14
|
+
kOptions: Symbol('fastify.options'),
|
|
15
|
+
kDisableRequestLogging: Symbol('fastify.disableRequestLogging'),
|
|
16
|
+
kPluginNameChain: Symbol('fastify.pluginNameChain'),
|
|
17
|
+
// Schema
|
|
12
18
|
kSchemaController: Symbol('fastify.schemaController'),
|
|
13
19
|
kSchemaHeaders: Symbol('headers-schema'),
|
|
14
20
|
kSchemaParams: Symbol('params-schema'),
|
|
@@ -16,17 +22,20 @@ const keys = {
|
|
|
16
22
|
kSchemaBody: Symbol('body-schema'),
|
|
17
23
|
kSchemaResponse: Symbol('response-schema'),
|
|
18
24
|
kSchemaErrorFormatter: Symbol('fastify.schemaErrorFormatter'),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
kReply: Symbol('fastify.Reply'),
|
|
25
|
+
kSchemaVisited: Symbol('fastify.schemas.visited'),
|
|
26
|
+
// Request
|
|
22
27
|
kRequest: Symbol('fastify.Request'),
|
|
28
|
+
kRequestValidateFns: Symbol('fastify.request.cache.validateFns'),
|
|
23
29
|
kRequestPayloadStream: Symbol('fastify.RequestPayloadStream'),
|
|
24
30
|
kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'),
|
|
25
|
-
|
|
31
|
+
// 404
|
|
26
32
|
kFourOhFour: Symbol('fastify.404'),
|
|
33
|
+
kCanSetNotFoundHandler: Symbol('fastify.canSetNotFoundHandler'),
|
|
27
34
|
kFourOhFourLevelInstance: Symbol('fastify.404LogLevelInstance'),
|
|
28
35
|
kFourOhFourContext: Symbol('fastify.404ContextKey'),
|
|
29
36
|
kDefaultJsonParse: Symbol('fastify.defaultJSONParse'),
|
|
37
|
+
// Reply
|
|
38
|
+
kReply: Symbol('fastify.Reply'),
|
|
30
39
|
kReplySerializer: Symbol('fastify.reply.serializer'),
|
|
31
40
|
kReplyIsError: Symbol('fastify.reply.isError'),
|
|
32
41
|
kReplyHeaders: Symbol('fastify.reply.headers'),
|
|
@@ -38,11 +47,8 @@ const keys = {
|
|
|
38
47
|
kReplyEndTime: Symbol('fastify.reply.endTime'),
|
|
39
48
|
kReplyErrorHandlerCalled: Symbol('fastify.reply.errorHandlerCalled'),
|
|
40
49
|
kReplyIsRunningOnErrorHook: Symbol('fastify.reply.isRunningOnErrorHook'),
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
kOptions: Symbol('fastify.options'),
|
|
44
|
-
kDisableRequestLogging: Symbol('fastify.disableRequestLogging'),
|
|
45
|
-
kPluginNameChain: Symbol('fastify.pluginNameChain'),
|
|
50
|
+
kReplySerializerDefault: Symbol('fastify.replySerializerDefault'),
|
|
51
|
+
kReplySerializeWeakMap: Symbol('fastify.reply.cache.serializeFns'),
|
|
46
52
|
// This symbol is only meant to be used for fastify tests and should not be used for any other purpose
|
|
47
53
|
kTestInternals: Symbol('fastify.testInternals'),
|
|
48
54
|
kErrorHandler: Symbol('fastify.errorHandler'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
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,7 +126,7 @@
|
|
|
126
126
|
"homepage": "https://www.fastify.io/",
|
|
127
127
|
"devDependencies": {
|
|
128
128
|
"@fastify/pre-commit": "^2.0.2",
|
|
129
|
-
"@sinclair/typebox": "^0.
|
|
129
|
+
"@sinclair/typebox": "^0.24.9",
|
|
130
130
|
"@sinonjs/fake-timers": "^9.1.2",
|
|
131
131
|
"@types/node": "^18.0.0",
|
|
132
132
|
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"eslint-plugin-promise": "^6.0.0",
|
|
148
148
|
"fast-json-body": "^1.1.0",
|
|
149
149
|
"fast-json-stringify": "^5.0.0",
|
|
150
|
-
"fastify-plugin": "^
|
|
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",
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
"json-schema-to-ts": "^2.5.3",
|
|
160
160
|
"JSONStream": "^1.3.5",
|
|
161
161
|
"license-checker": "^25.0.1",
|
|
162
|
-
"markdownlint-cli2": "^0.
|
|
162
|
+
"markdownlint-cli2": "^0.5.0",
|
|
163
163
|
"proxyquire": "^2.1.3",
|
|
164
164
|
"pump": "^3.0.0",
|
|
165
165
|
"self-cert": "^2.0.0",
|
|
@@ -170,14 +170,14 @@
|
|
|
170
170
|
"split2": "^4.1.0",
|
|
171
171
|
"standard": "^17.0.0-2",
|
|
172
172
|
"tap": "^16.2.0",
|
|
173
|
-
"tsd": "^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.
|
|
180
|
+
"@fastify/ajv-compiler": "^3.1.1",
|
|
181
181
|
"@fastify/error": "^3.0.0",
|
|
182
182
|
"@fastify/fast-json-stringify-compiler": "^4.0.0",
|
|
183
183
|
"abstract-logging": "^2.0.1",
|
|
@@ -23,7 +23,9 @@ test('check generated code syntax', async (t) => {
|
|
|
23
23
|
t.equal(result[0].fatalErrorCount, 0)
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
const isPrebublish = !!process.env.PREPUBLISH
|
|
27
|
+
|
|
28
|
+
test('ensure the current error serializer is latest', { skip: !isPrebublish }, async (t) => {
|
|
27
29
|
t.plan(1)
|
|
28
30
|
|
|
29
31
|
const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
|
|
@@ -58,6 +58,21 @@ test('getParser', t => {
|
|
|
58
58
|
t.equal(fastify[keys.kContentTypeParser].getParser('text/html').fn, third)
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
+
test('should return matching parser with caching', t => {
|
|
62
|
+
t.plan(6)
|
|
63
|
+
|
|
64
|
+
const fastify = Fastify()
|
|
65
|
+
|
|
66
|
+
fastify.addContentTypeParser('text/html', first)
|
|
67
|
+
|
|
68
|
+
t.equal(fastify[keys.kContentTypeParser].getParser('text/html').fn, first)
|
|
69
|
+
t.equal(fastify[keys.kContentTypeParser].cache.size, 0)
|
|
70
|
+
t.equal(fastify[keys.kContentTypeParser].getParser('text/html ').fn, first)
|
|
71
|
+
t.equal(fastify[keys.kContentTypeParser].cache.size, 1)
|
|
72
|
+
t.equal(fastify[keys.kContentTypeParser].getParser('text/html ').fn, first)
|
|
73
|
+
t.equal(fastify[keys.kContentTypeParser].cache.size, 1)
|
|
74
|
+
})
|
|
75
|
+
|
|
61
76
|
test('should prefer content type parser with string value', t => {
|
|
62
77
|
t.plan(2)
|
|
63
78
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const sget = require('simple-get').concat
|
|
6
|
+
const fastify = require('..')()
|
|
7
|
+
|
|
8
|
+
test('can be created - copy', t => {
|
|
9
|
+
t.plan(1)
|
|
10
|
+
try {
|
|
11
|
+
fastify.route({
|
|
12
|
+
method: 'COPY',
|
|
13
|
+
url: '*',
|
|
14
|
+
handler: function (req, reply) {
|
|
15
|
+
reply.code(204).send()
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
t.pass()
|
|
19
|
+
} catch (e) {
|
|
20
|
+
t.fail()
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
fastify.listen({ port: 0 }, err => {
|
|
25
|
+
t.error(err)
|
|
26
|
+
t.teardown(() => { fastify.close() })
|
|
27
|
+
|
|
28
|
+
test('request - copy', t => {
|
|
29
|
+
t.plan(2)
|
|
30
|
+
sget({
|
|
31
|
+
url: `http://localhost:${fastify.server.address().port}/test.txt`,
|
|
32
|
+
method: 'COPY',
|
|
33
|
+
headers: {
|
|
34
|
+
Destination: '/test2.txt'
|
|
35
|
+
}
|
|
36
|
+
}, (err, response, body) => {
|
|
37
|
+
t.error(err)
|
|
38
|
+
t.equal(response.statusCode, 204)
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
const t = require('tap')
|
|
4
4
|
const test = t.test
|
|
5
5
|
const Fastify = require('../..')
|
|
6
|
-
const supportedMethods =
|
|
6
|
+
const { supportedMethods } = require('../../lib/httpMethods')
|
|
7
7
|
|
|
8
8
|
test('fastify.all should add all the methods to the same url', t => {
|
|
9
|
+
const requirePayload = [
|
|
10
|
+
'POST',
|
|
11
|
+
'PUT',
|
|
12
|
+
'PATCH'
|
|
13
|
+
]
|
|
14
|
+
|
|
9
15
|
t.plan(supportedMethods.length * 2)
|
|
10
16
|
|
|
11
17
|
const fastify = Fastify()
|
|
@@ -22,7 +28,7 @@ test('fastify.all should add all the methods to the same url', t => {
|
|
|
22
28
|
method
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
if (method
|
|
31
|
+
if (requirePayload.includes(method)) {
|
|
26
32
|
options.payload = { hello: 'world' }
|
|
27
33
|
}
|
|
28
34
|
|