fastify 4.0.0-rc.5 → 4.0.2
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/.markdownlint-cli2.yaml +22 -0
- package/GOVERNANCE.md +30 -20
- package/PROJECT_CHARTER.md +48 -17
- package/README.md +164 -77
- package/SECURITY.md +55 -44
- package/build/build-error-serializer.js +12 -7
- package/docs/Guides/Benchmarking.md +2 -0
- package/docs/Guides/Delay-Accepting-Requests.md +98 -90
- package/docs/Guides/Ecosystem.md +48 -38
- package/docs/Guides/Index.md +2 -0
- package/docs/Guides/Migration-Guide-V3.md +1 -1
- package/docs/Guides/Migration-Guide-V4.md +55 -0
- package/docs/Guides/Plugins-Guide.md +3 -3
- package/docs/Guides/Prototype-Poisoning.md +1 -1
- package/docs/Guides/Recommendations.md +2 -2
- package/docs/Guides/Serverless.md +17 -16
- package/docs/Guides/Write-Plugin.md +3 -3
- package/docs/Reference/ContentTypeParser.md +17 -13
- package/docs/Reference/Errors.md +6 -5
- package/docs/Reference/Middleware.md +3 -3
- package/docs/Reference/Plugins.md +8 -6
- package/docs/Reference/Reply.md +30 -16
- package/docs/Reference/Request.md +3 -3
- package/docs/Reference/Routes.md +113 -38
- package/docs/Reference/Server.md +109 -72
- package/docs/Reference/Type-Providers.md +28 -8
- package/docs/Reference/TypeScript.md +12 -6
- package/docs/Reference/Validation-and-Serialization.md +47 -35
- package/fastify.js +1 -1
- package/lib/error-serializer.js +32 -204
- package/lib/pluginUtils.js +10 -0
- package/lib/reply.js +1 -1
- package/lib/schemas.js +3 -0
- package/lib/validation.js +1 -1
- package/package.json +6 -4
- package/test/build/error-serializer.test.js +28 -0
- package/test/{internals → build}/version.test.js +1 -1
- package/test/plugin.test.js +32 -0
- package/test/reply-error.test.js +7 -1
- package/test/schema-serialization.test.js +30 -0
- package/docs/Migration-Guide-V4.md +0 -12
|
@@ -6,10 +6,12 @@ recommend using [JSON Schema](https://json-schema.org/) to validate your routes
|
|
|
6
6
|
and serialize your outputs. Internally, Fastify compiles the schema into a
|
|
7
7
|
highly performant function.
|
|
8
8
|
|
|
9
|
-
Validation will only be attempted if the content type is `application-json`,
|
|
10
|
-
|
|
9
|
+
Validation will only be attempted if the content type is `application-json`, as
|
|
10
|
+
described in the documentation for the [content type
|
|
11
|
+
parser](./ContentTypeParser.md).
|
|
11
12
|
|
|
12
|
-
All the examples in this section are using the [JSON Schema Draft
|
|
13
|
+
All the examples in this section are using the [JSON Schema Draft
|
|
14
|
+
7](https://json-schema.org/specification-links.html#draft-7) specification.
|
|
13
15
|
|
|
14
16
|
> ## ⚠ Security Notice
|
|
15
17
|
> Treat the schema definition as application code. Validation and serialization
|
|
@@ -147,10 +149,9 @@ fastify.register((instance, opts, done) => {
|
|
|
147
149
|
|
|
148
150
|
|
|
149
151
|
### Validation
|
|
150
|
-
The route validation internally relies upon
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Validating the input is very easy: just add the fields that you need
|
|
152
|
+
The route validation internally relies upon [Ajv
|
|
153
|
+
v8](https://www.npmjs.com/package/ajv) which is a high-performance JSON Schema
|
|
154
|
+
validator. Validating the input is very easy: just add the fields that you need
|
|
154
155
|
inside the route schema, and you are done!
|
|
155
156
|
|
|
156
157
|
The supported validations are:
|
|
@@ -233,13 +234,12 @@ const schema = {
|
|
|
233
234
|
fastify.post('/the/url', { schema }, handler)
|
|
234
235
|
```
|
|
235
236
|
|
|
236
|
-
*Note that Ajv will try to [coerce](https://ajv.js.org/coercion.html) the values
|
|
237
|
-
the types specified in your schema `type` keywords, both to pass the
|
|
238
|
-
and to use the correctly typed data afterwards.*
|
|
237
|
+
*Note that Ajv will try to [coerce](https://ajv.js.org/coercion.html) the values
|
|
238
|
+
to the types specified in your schema `type` keywords, both to pass the
|
|
239
|
+
validation and to use the correctly typed data afterwards.*
|
|
239
240
|
|
|
240
|
-
The Ajv default configuration in Fastify supports coercing array
|
|
241
|
-
|
|
242
|
-
Example:
|
|
241
|
+
The Ajv default configuration in Fastify supports coercing array parameters in
|
|
242
|
+
`querystring`. Example:
|
|
243
243
|
|
|
244
244
|
```js
|
|
245
245
|
const opts = {
|
|
@@ -318,8 +318,9 @@ For further information see [here](https://ajv.js.org/coercion.html)
|
|
|
318
318
|
#### Ajv Plugins
|
|
319
319
|
<a id="ajv-plugins"></a>
|
|
320
320
|
|
|
321
|
-
You can provide a list of plugins you want to use with the default `ajv`
|
|
322
|
-
Note that the plugin must be **compatible with the Ajv version shipped
|
|
321
|
+
You can provide a list of plugins you want to use with the default `ajv`
|
|
322
|
+
instance. Note that the plugin must be **compatible with the Ajv version shipped
|
|
323
|
+
within Fastify**.
|
|
323
324
|
|
|
324
325
|
> Refer to [`ajv options`](./Server.md#ajv) to check plugins format
|
|
325
326
|
|
|
@@ -388,7 +389,8 @@ body, URL parameters, headers, and query string. The default
|
|
|
388
389
|
[ajv](https://ajv.js.org/) validation interface. Fastify uses it internally to
|
|
389
390
|
speed the validation up.
|
|
390
391
|
|
|
391
|
-
Fastify's [baseline ajv
|
|
392
|
+
Fastify's [baseline ajv
|
|
393
|
+
configuration](https://github.com/fastify/ajv-compiler#ajv-configuration) is:
|
|
392
394
|
|
|
393
395
|
```js
|
|
394
396
|
{
|
|
@@ -469,7 +471,8 @@ fastify.post('/the/url', {
|
|
|
469
471
|
},
|
|
470
472
|
validatorCompiler: ({ schema, method, url, httpPart }) => {
|
|
471
473
|
return function (data) {
|
|
472
|
-
// with option strict = false, yup `validateSync` function returns the
|
|
474
|
+
// with option strict = false, yup `validateSync` function returns the
|
|
475
|
+
// coerced value if validation was successful, or throws if validation failed
|
|
473
476
|
try {
|
|
474
477
|
const result = schema.validateSync(data, yupOptions)
|
|
475
478
|
return { value: result }
|
|
@@ -487,15 +490,15 @@ fastify.post('/the/url', {
|
|
|
487
490
|
Fastify's validation error messages are tightly coupled to the default
|
|
488
491
|
validation engine: errors returned from `ajv` are eventually run through the
|
|
489
492
|
`schemaErrorFormatter` function which is responsible for building human-friendly
|
|
490
|
-
error messages. However, the `schemaErrorFormatter` function is written with
|
|
491
|
-
in mind. As a result, you may run into odd or incomplete error messages
|
|
492
|
-
using other validation libraries.
|
|
493
|
+
error messages. However, the `schemaErrorFormatter` function is written with
|
|
494
|
+
`ajv` in mind. As a result, you may run into odd or incomplete error messages
|
|
495
|
+
when using other validation libraries.
|
|
493
496
|
|
|
494
497
|
To circumvent this issue, you have 2 main options :
|
|
495
498
|
|
|
496
499
|
1. make sure your validation function (returned by your custom `schemaCompiler`)
|
|
497
|
-
returns errors in the same structure and format as `ajv` (although this
|
|
498
|
-
|
|
500
|
+
returns errors in the same structure and format as `ajv` (although this could
|
|
501
|
+
prove to be difficult and tricky due to differences between validation
|
|
499
502
|
engines)
|
|
500
503
|
2. or use a custom `errorHandler` to intercept and format your 'custom'
|
|
501
504
|
validation errors
|
|
@@ -503,8 +506,8 @@ To circumvent this issue, you have 2 main options :
|
|
|
503
506
|
To help you in writing a custom `errorHandler`, Fastify adds 2 properties to all
|
|
504
507
|
validation errors:
|
|
505
508
|
|
|
506
|
-
* `validation`: the content of the `error` property of the object returned by
|
|
507
|
-
validation function (returned by your custom `schemaCompiler`)
|
|
509
|
+
* `validation`: the content of the `error` property of the object returned by
|
|
510
|
+
the validation function (returned by your custom `schemaCompiler`)
|
|
508
511
|
* `validationContext`: the 'context' (body, params, query, headers) where the
|
|
509
512
|
validation error occurred
|
|
510
513
|
|
|
@@ -567,10 +570,20 @@ fastify.post('/the/url', { schema }, handler)
|
|
|
567
570
|
```
|
|
568
571
|
|
|
569
572
|
As you can see, the response schema is based on the status code. If you want to
|
|
570
|
-
use the same schema for multiple status codes, you can use `'2xx'
|
|
573
|
+
use the same schema for multiple status codes, you can use `'2xx'` or `default`,
|
|
574
|
+
for example:
|
|
571
575
|
```js
|
|
572
576
|
const schema = {
|
|
573
577
|
response: {
|
|
578
|
+
default: {
|
|
579
|
+
type: 'object',
|
|
580
|
+
properties: {
|
|
581
|
+
error: {
|
|
582
|
+
type: 'boolean',
|
|
583
|
+
default: true
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
},
|
|
574
587
|
'2xx': {
|
|
575
588
|
type: 'object',
|
|
576
589
|
properties: {
|
|
@@ -689,9 +702,8 @@ fastify.setSchemaErrorFormatter(function (errors, dataVar) {
|
|
|
689
702
|
})
|
|
690
703
|
```
|
|
691
704
|
|
|
692
|
-
You can also use
|
|
693
|
-
|
|
694
|
-
define a custom response for validation errors such as
|
|
705
|
+
You can also use [setErrorHandler](./Server.md#seterrorhandler) to define a
|
|
706
|
+
custom response for validation errors such as
|
|
695
707
|
|
|
696
708
|
```js
|
|
697
709
|
fastify.setErrorHandler(function (error, request, reply) {
|
|
@@ -701,9 +713,9 @@ fastify.setErrorHandler(function (error, request, reply) {
|
|
|
701
713
|
})
|
|
702
714
|
```
|
|
703
715
|
|
|
704
|
-
If you want a custom error response in the schema without headaches, and
|
|
705
|
-
look at
|
|
706
|
-
Check out the
|
|
716
|
+
If you want a custom error response in the schema without headaches, and
|
|
717
|
+
quickly, take a look at
|
|
718
|
+
[`ajv-errors`](https://github.com/epoberezkin/ajv-errors). Check out the
|
|
707
719
|
[example](https://github.com/fastify/example/blob/HEAD/validation-messages/custom-errors-messages.js)
|
|
708
720
|
usage.
|
|
709
721
|
> Make sure to install version 1.0.1 of `ajv-errors`, because later versions of
|
|
@@ -719,7 +731,8 @@ const fastify = Fastify({
|
|
|
719
731
|
ajv: {
|
|
720
732
|
customOptions: {
|
|
721
733
|
jsonPointers: true,
|
|
722
|
-
|
|
734
|
+
// Warning: Enabling this option may lead to this security issue https://www.cvedetails.com/cve/CVE-2020-8192/
|
|
735
|
+
allErrors: true
|
|
723
736
|
},
|
|
724
737
|
plugins: [
|
|
725
738
|
require('ajv-errors')
|
|
@@ -797,9 +810,8 @@ fastify.setErrorHandler(function (error, request, reply) {
|
|
|
797
810
|
|
|
798
811
|
### JSON Schema support
|
|
799
812
|
|
|
800
|
-
JSON Schema provides utilities to optimize your schemas that,
|
|
801
|
-
|
|
802
|
-
easily.
|
|
813
|
+
JSON Schema provides utilities to optimize your schemas that, in conjunction
|
|
814
|
+
with Fastify's shared schema, let you reuse all your schemas easily.
|
|
803
815
|
|
|
804
816
|
| Use Case | Validator | Serializer |
|
|
805
817
|
|-----------------------------------|-----------|------------|
|
package/fastify.js
CHANGED
package/lib/error-serializer.js
CHANGED
|
@@ -1,195 +1,23 @@
|
|
|
1
1
|
// This file is autogenerated by build/build-error-serializer.js, do not edit
|
|
2
2
|
/* istanbul ignore file */
|
|
3
|
-
module.exports = $main
|
|
4
|
-
'use strict'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function $pad2Zeros (num) {
|
|
8
|
-
const s = '00' + num
|
|
9
|
-
return s[s.length - 2] + s[s.length - 1]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function $asAny (i) {
|
|
13
|
-
return JSON.stringify(i)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function $asNull () {
|
|
17
|
-
return 'null'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function $asInteger (i) {
|
|
21
|
-
if (typeof i === 'bigint') {
|
|
22
|
-
return i.toString()
|
|
23
|
-
} else if (Number.isInteger(i)) {
|
|
24
|
-
return $asNumber(i)
|
|
25
|
-
} else {
|
|
26
|
-
/* eslint no-undef: "off" */
|
|
27
|
-
return $asNumber(parseInteger(i))
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function $asIntegerNullable (i) {
|
|
32
|
-
return i === null ? null : $asInteger(i)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function $asNumber (i) {
|
|
36
|
-
const num = Number(i)
|
|
37
|
-
if (isNaN(num)) {
|
|
38
|
-
return 'null'
|
|
39
|
-
} else {
|
|
40
|
-
return '' + num
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function $asNumberNullable (i) {
|
|
45
|
-
return i === null ? null : $asNumber(i)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function $asBoolean (bool) {
|
|
49
|
-
return bool && 'true' || 'false' // eslint-disable-line
|
|
50
|
-
}
|
|
51
3
|
|
|
52
|
-
|
|
53
|
-
return bool === null ? null : $asBoolean(bool)
|
|
54
|
-
}
|
|
4
|
+
'use strict'
|
|
55
5
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (date instanceof Date) {
|
|
59
|
-
return quotes + date.toISOString() + quotes
|
|
60
|
-
} else if (date && typeof date.toISOString === 'function') {
|
|
61
|
-
return quotes + date.toISOString() + quotes
|
|
62
|
-
} else {
|
|
63
|
-
return $asString(date, skipQuotes)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
6
|
+
const Serializer = require('fast-json-stringify/serializer')
|
|
7
|
+
const buildAjv = require('fast-json-stringify/ajv')
|
|
66
8
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (date instanceof Date) {
|
|
70
|
-
return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000 )).toISOString().slice(0, 10) + quotes
|
|
71
|
-
} else if (date && typeof date.format === 'function') {
|
|
72
|
-
return quotes + date.format('YYYY-MM-DD') + quotes
|
|
73
|
-
} else {
|
|
74
|
-
return $asString(date, skipQuotes)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function $asTime (date, skipQuotes) {
|
|
79
|
-
const quotes = skipQuotes === true ? '' : '"'
|
|
80
|
-
if (date instanceof Date) {
|
|
81
|
-
const hour = new Intl.DateTimeFormat('en', { hour: 'numeric', hour12: false }).format(date)
|
|
82
|
-
const minute = new Intl.DateTimeFormat('en', { minute: 'numeric' }).format(date)
|
|
83
|
-
const second = new Intl.DateTimeFormat('en', { second: 'numeric' }).format(date)
|
|
84
|
-
return quotes + $pad2Zeros(hour) + ':' + $pad2Zeros(minute) + ':' + $pad2Zeros(second) + quotes
|
|
85
|
-
} else if (date && typeof date.format === 'function') {
|
|
86
|
-
return quotes + date.format('HH:mm:ss') + quotes
|
|
87
|
-
} else {
|
|
88
|
-
return $asString(date, skipQuotes)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function $asString (str, skipQuotes) {
|
|
93
|
-
const quotes = skipQuotes === true ? '' : '"'
|
|
94
|
-
if (str instanceof Date) {
|
|
95
|
-
return quotes + str.toISOString() + quotes
|
|
96
|
-
} else if (str === null) {
|
|
97
|
-
return quotes + quotes
|
|
98
|
-
} else if (str instanceof RegExp) {
|
|
99
|
-
str = str.source
|
|
100
|
-
} else if (typeof str !== 'string') {
|
|
101
|
-
str = str.toString()
|
|
102
|
-
}
|
|
103
|
-
// If we skipQuotes it means that we are using it as test
|
|
104
|
-
// no need to test the string length for the render
|
|
105
|
-
if (skipQuotes) {
|
|
106
|
-
return str
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (str.length < 42) {
|
|
110
|
-
return $asStringSmall(str)
|
|
111
|
-
} else {
|
|
112
|
-
return JSON.stringify(str)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
9
|
+
const serializer = new Serializer({"mode":"standalone"})
|
|
10
|
+
const ajv = buildAjv({})
|
|
115
11
|
|
|
116
|
-
function $asStringNullable (str) {
|
|
117
|
-
return str === null ? null : $asString(str)
|
|
118
|
-
}
|
|
119
12
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// 34 and 92 happens all the time, so we
|
|
125
|
-
// have a fast case for them
|
|
126
|
-
function $asStringSmall (str) {
|
|
127
|
-
const l = str.length
|
|
128
|
-
let result = ''
|
|
129
|
-
let last = 0
|
|
130
|
-
let found = false
|
|
131
|
-
let surrogateFound = false
|
|
132
|
-
let point = 255
|
|
133
|
-
// eslint-disable-next-line
|
|
134
|
-
for (var i = 0; i < l && point >= 32; i++) {
|
|
135
|
-
point = str.charCodeAt(i)
|
|
136
|
-
if (point >= 0xD800 && point <= 0xDFFF) {
|
|
137
|
-
// The current character is a surrogate.
|
|
138
|
-
surrogateFound = true
|
|
139
|
-
}
|
|
140
|
-
if (point === 34 || point === 92) {
|
|
141
|
-
result += str.slice(last, i) + '\\'
|
|
142
|
-
last = i
|
|
143
|
-
found = true
|
|
13
|
+
function main (input) {
|
|
14
|
+
let json = ''
|
|
15
|
+
json += anonymous0(input)
|
|
16
|
+
return json
|
|
144
17
|
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (!found) {
|
|
148
|
-
result = str
|
|
149
|
-
} else {
|
|
150
|
-
result += str.slice(last)
|
|
151
|
-
}
|
|
152
|
-
return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"'
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Used by schemas that are dependant on calling 'ajv.validate' during runtime,
|
|
159
|
-
* it stores the value of the '$id' property of the schema (if it has it) inside
|
|
160
|
-
* a cache which is used to figure out if the schema was compiled into a validator
|
|
161
|
-
* by ajv on a previous call, if it was then the '$id' string will be used to
|
|
162
|
-
* invoke 'ajv.validate', this allows:
|
|
163
|
-
*
|
|
164
|
-
* 1. Schemas that depend on ajv.validate calls to leverage ajv caching system.
|
|
165
|
-
* 2. To avoid errors, since directly invoking 'ajv.validate' with the same
|
|
166
|
-
* schema (that contains an '$id' property) twice will throw an error.
|
|
167
|
-
*/
|
|
168
|
-
const $validateWithAjv = (function() {
|
|
169
|
-
const cache = new Set()
|
|
170
|
-
|
|
171
|
-
return function (schema, target) {
|
|
172
|
-
const id = schema.$id
|
|
173
|
-
|
|
174
|
-
if (!id) {
|
|
175
|
-
return ajv.validate(schema, target)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const cached = cache.has(id)
|
|
179
|
-
|
|
180
|
-
if (cached) {
|
|
181
|
-
return ajv.validate(id, target)
|
|
182
|
-
} else {
|
|
183
|
-
cache.add(id)
|
|
184
|
-
return ajv.validate(schema, target)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
})()
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
function parseInteger(int) { return Math.trunc(int) }
|
|
191
18
|
|
|
192
|
-
function
|
|
19
|
+
function anonymous0 (input) {
|
|
20
|
+
// main
|
|
193
21
|
|
|
194
22
|
var obj = (input && typeof input.toJSON === 'function')
|
|
195
23
|
? input.toJSON()
|
|
@@ -198,60 +26,60 @@ function $asStringSmall (str) {
|
|
|
198
26
|
var json = '{'
|
|
199
27
|
var addComma = false
|
|
200
28
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
29
|
+
if (obj["statusCode"] !== undefined) {
|
|
30
|
+
|
|
204
31
|
if (addComma) {
|
|
205
32
|
json += ','
|
|
206
33
|
} else {
|
|
207
34
|
addComma = true
|
|
208
35
|
}
|
|
209
36
|
|
|
210
|
-
|
|
211
|
-
|
|
37
|
+
json += "\"statusCode\"" + ':'
|
|
38
|
+
json += serializer.asNumber.bind(serializer)(obj["statusCode"])
|
|
212
39
|
}
|
|
213
40
|
|
|
214
|
-
|
|
215
|
-
|
|
41
|
+
if (obj["code"] !== undefined) {
|
|
42
|
+
|
|
216
43
|
if (addComma) {
|
|
217
44
|
json += ','
|
|
218
45
|
} else {
|
|
219
46
|
addComma = true
|
|
220
47
|
}
|
|
221
48
|
|
|
222
|
-
|
|
223
|
-
|
|
49
|
+
json += "\"code\"" + ':'
|
|
50
|
+
json += serializer.asString.bind(serializer)(obj["code"])
|
|
224
51
|
}
|
|
225
52
|
|
|
226
|
-
|
|
227
|
-
|
|
53
|
+
if (obj["error"] !== undefined) {
|
|
54
|
+
|
|
228
55
|
if (addComma) {
|
|
229
56
|
json += ','
|
|
230
57
|
} else {
|
|
231
58
|
addComma = true
|
|
232
59
|
}
|
|
233
60
|
|
|
234
|
-
|
|
235
|
-
|
|
61
|
+
json += "\"error\"" + ':'
|
|
62
|
+
json += serializer.asString.bind(serializer)(obj["error"])
|
|
236
63
|
}
|
|
237
64
|
|
|
238
|
-
|
|
239
|
-
|
|
65
|
+
if (obj["message"] !== undefined) {
|
|
66
|
+
|
|
240
67
|
if (addComma) {
|
|
241
68
|
json += ','
|
|
242
69
|
} else {
|
|
243
70
|
addComma = true
|
|
244
71
|
}
|
|
245
72
|
|
|
246
|
-
|
|
247
|
-
|
|
73
|
+
json += "\"message\"" + ':'
|
|
74
|
+
json += serializer.asString.bind(serializer)(obj["message"])
|
|
248
75
|
}
|
|
249
76
|
|
|
250
77
|
json += '}'
|
|
251
78
|
return json
|
|
252
79
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
;
|
|
256
|
-
return $main
|
|
257
80
|
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
module.exports = main
|
|
85
|
+
|
package/lib/pluginUtils.js
CHANGED
|
@@ -104,7 +104,17 @@ function checkVersion (fn) {
|
|
|
104
104
|
const requiredVersion = meta.fastify
|
|
105
105
|
|
|
106
106
|
const fastifyRc = /-rc.+$/.test(this.version)
|
|
107
|
+
if (fastifyRc === true && semver.gt(this.version, semver.coerce(requiredVersion)) === true) {
|
|
108
|
+
// A Fastify release candidate phase is taking place. In order to reduce
|
|
109
|
+
// the effort needed to test plugins with the RC, we allow plugins targeting
|
|
110
|
+
// the prior Fastify release to be loaded.
|
|
111
|
+
return
|
|
112
|
+
}
|
|
107
113
|
if (requiredVersion && semver.satisfies(this.version, requiredVersion, { includePrerelease: fastifyRc }) === false) {
|
|
114
|
+
// We are not in a release candidate phase. Thus, we must honor the semver
|
|
115
|
+
// ranges defined by the plugin's metadata. Which is to say, if the plugin
|
|
116
|
+
// expects an older version of Fastify than the _current_ version, we will
|
|
117
|
+
// throw an error.
|
|
108
118
|
throw new FST_ERR_PLUGIN_VERSION_MISMATCH(meta.name, requiredVersion, this.version)
|
|
109
119
|
}
|
|
110
120
|
}
|
package/lib/reply.js
CHANGED
|
@@ -291,7 +291,7 @@ Reply.prototype.removeTrailer = function (key) {
|
|
|
291
291
|
|
|
292
292
|
Reply.prototype.code = function (code) {
|
|
293
293
|
const intValue = parseInt(code)
|
|
294
|
-
if (isNaN(intValue) || intValue < 100 || intValue >
|
|
294
|
+
if (isNaN(intValue) || intValue < 100 || intValue > 599) {
|
|
295
295
|
throw new FST_ERR_BAD_STATUS_CODE(code || String(code))
|
|
296
296
|
}
|
|
297
297
|
|
package/lib/schemas.js
CHANGED
|
@@ -143,6 +143,9 @@ function getSchemaSerializer (context, statusCode) {
|
|
|
143
143
|
if (responseSchemaDef[fallbackStatusCode]) {
|
|
144
144
|
return responseSchemaDef[fallbackStatusCode]
|
|
145
145
|
}
|
|
146
|
+
if (responseSchemaDef.default) {
|
|
147
|
+
return responseSchemaDef.default
|
|
148
|
+
}
|
|
146
149
|
return false
|
|
147
150
|
}
|
|
148
151
|
|
package/lib/validation.js
CHANGED
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
kSchemaBody: bodySchema,
|
|
8
8
|
kSchemaResponse: responseSchema
|
|
9
9
|
} = require('./symbols')
|
|
10
|
-
const scChecker = /^[1-5]{1}[0-9]{2}$|^[1-5]xx$/
|
|
10
|
+
const scChecker = /^[1-5]{1}[0-9]{2}$|^[1-5]xx$|^default$/
|
|
11
11
|
|
|
12
12
|
function compileSchemasForSerialization (context, compile) {
|
|
13
13
|
if (!context.schema || !context.schema.response) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
"coverage:ci": "npm run unit -- --cov --coverage-report=html --no-browser --no-check-coverage -R terse",
|
|
13
13
|
"coverage:ci-check-coverage": "nyc check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
|
|
14
14
|
"license-checker": "license-checker --production --onlyAllow=\"MIT;ISC;BSD-3-Clause;BSD-2-Clause\"",
|
|
15
|
-
"lint": "npm run lint:standard && npm run lint:typescript",
|
|
15
|
+
"lint": "npm run lint:standard && npm run lint:typescript && npm run lint:markdown",
|
|
16
16
|
"lint:fix": "standard --fix",
|
|
17
|
+
"lint:markdown": "markdownlint-cli2",
|
|
17
18
|
"lint:standard": "standard | snazzy",
|
|
18
19
|
"lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
|
|
19
|
-
"prepublishOnly": "tap --no-check-coverage test/
|
|
20
|
+
"prepublishOnly": "tap --no-check-coverage test/build/**.test.js",
|
|
20
21
|
"test": "npm run lint && npm run unit && npm run test:typescript",
|
|
21
22
|
"test:ci": "npm run unit -- -R terse --cov --coverage-report=lcovonly && npm run test:typescript",
|
|
22
23
|
"test:report": "npm run lint && npm run unit:report && npm run test:typescript",
|
|
@@ -145,7 +146,6 @@
|
|
|
145
146
|
"eslint-plugin-n": "^15.2.0",
|
|
146
147
|
"eslint-plugin-promise": "^6.0.0",
|
|
147
148
|
"fast-json-body": "^1.1.0",
|
|
148
|
-
"fast-json-stringify": "^4.0.0",
|
|
149
149
|
"fastify-plugin": "^3.0.1",
|
|
150
150
|
"fluent-json-schema": "^3.1.0",
|
|
151
151
|
"form-data": "^4.0.0",
|
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
"json-schema-to-ts": "^2.5.3",
|
|
159
159
|
"JSONStream": "^1.3.5",
|
|
160
160
|
"license-checker": "^25.0.1",
|
|
161
|
+
"markdownlint-cli2": "^0.4.0",
|
|
161
162
|
"proxyquire": "^2.1.3",
|
|
162
163
|
"pump": "^3.0.0",
|
|
163
164
|
"self-cert": "^2.0.0",
|
|
@@ -180,6 +181,7 @@
|
|
|
180
181
|
"@fastify/fast-json-stringify-compiler": "^3.0.0",
|
|
181
182
|
"abstract-logging": "^2.0.1",
|
|
182
183
|
"avvio": "^8.1.3",
|
|
184
|
+
"fast-json-stringify": "^4.1.0",
|
|
183
185
|
"find-my-way": "^6.3.0",
|
|
184
186
|
"light-my-request": "^5.0.0",
|
|
185
187
|
"pino": "^8.0.0",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
|
|
8
|
+
const { code } = require('../../build/build-error-serializer')
|
|
9
|
+
|
|
10
|
+
test('check generated code syntax', async (t) => {
|
|
11
|
+
t.plan(1)
|
|
12
|
+
|
|
13
|
+
// standard is a esm, we import it like this
|
|
14
|
+
const { default: standard } = await import('standard')
|
|
15
|
+
const result = await standard.lintText(code)
|
|
16
|
+
|
|
17
|
+
// if there are any invalid syntax
|
|
18
|
+
// fatal count will be greater than 0
|
|
19
|
+
t.equal(result[0].fatalErrorCount, 0)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('ensure the current error serializer is latest', async (t) => {
|
|
23
|
+
t.plan(1)
|
|
24
|
+
|
|
25
|
+
const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
|
|
26
|
+
|
|
27
|
+
t.equal(current.toString(), code)
|
|
28
|
+
})
|
package/test/plugin.test.js
CHANGED
|
@@ -1057,6 +1057,38 @@ test('plugin metadata - release candidate', t => {
|
|
|
1057
1057
|
}
|
|
1058
1058
|
})
|
|
1059
1059
|
|
|
1060
|
+
test('fastify-rc loads prior version plugins', t => {
|
|
1061
|
+
t.plan(2)
|
|
1062
|
+
const fastify = Fastify()
|
|
1063
|
+
Object.defineProperty(fastify, 'version', {
|
|
1064
|
+
value: '99.0.0-rc.1'
|
|
1065
|
+
})
|
|
1066
|
+
|
|
1067
|
+
plugin[Symbol.for('plugin-meta')] = {
|
|
1068
|
+
name: 'plugin',
|
|
1069
|
+
fastify: '^98.1.0'
|
|
1070
|
+
}
|
|
1071
|
+
plugin2[Symbol.for('plugin-meta')] = {
|
|
1072
|
+
name: 'plugin2',
|
|
1073
|
+
fastify: '98.x'
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
fastify.register(plugin)
|
|
1077
|
+
|
|
1078
|
+
fastify.ready((err) => {
|
|
1079
|
+
t.error(err)
|
|
1080
|
+
t.pass('everything right')
|
|
1081
|
+
})
|
|
1082
|
+
|
|
1083
|
+
function plugin (instance, opts, done) {
|
|
1084
|
+
done()
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function plugin2 (instance, opts, done) {
|
|
1088
|
+
done()
|
|
1089
|
+
}
|
|
1090
|
+
})
|
|
1091
|
+
|
|
1060
1092
|
test('hasPlugin method exists as a function', t => {
|
|
1061
1093
|
t.plan(1)
|
|
1062
1094
|
|
package/test/reply-error.test.js
CHANGED
|
@@ -520,7 +520,13 @@ const invalidErrorCodes = [
|
|
|
520
520
|
undefined,
|
|
521
521
|
null,
|
|
522
522
|
'error_code',
|
|
523
|
-
|
|
523
|
+
|
|
524
|
+
// out of the 100-599 range:
|
|
525
|
+
0,
|
|
526
|
+
1,
|
|
527
|
+
99,
|
|
528
|
+
600,
|
|
529
|
+
700
|
|
524
530
|
]
|
|
525
531
|
invalidErrorCodes.forEach((invalidCode) => {
|
|
526
532
|
test(`should throw error if error code is ${invalidCode}`, t => {
|