fastify 4.0.0 → 4.0.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/.markdownlint-cli2.yaml +22 -0
- package/GOVERNANCE.md +30 -20
- package/PROJECT_CHARTER.md +48 -17
- package/README.md +165 -78
- 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 +43 -30
- package/docs/Guides/Index.md +2 -0
- package/docs/Guides/Migration-Guide-V4.md +55 -0
- package/docs/Guides/Serverless.md +13 -12
- package/docs/Reference/ContentTypeParser.md +17 -13
- package/docs/Reference/Errors.md +6 -5
- package/docs/Reference/LTS.md +2 -2
- 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 +112 -37
- package/docs/Reference/Server.md +109 -72
- package/docs/Reference/Type-Providers.md +30 -9
- package/docs/Reference/TypeScript.md +12 -6
- package/docs/Reference/Validation-and-Serialization.md +39 -37
- package/fastify.js +1 -1
- package/lib/error-serializer.js +171 -175
- package/lib/pluginUtils.js +10 -0
- package/lib/reply.js +1 -1
- package/lib/server.js +9 -1
- package/lib/wrapThenable.js +8 -3
- package/package.json +8 -6
- package/test/build/error-serializer.test.js +28 -0
- package/test/{internals → build}/version.test.js +1 -1
- package/test/listen.test.js +16 -2
- package/test/plugin.test.js +32 -0
- package/test/reply-error.test.js +7 -1
- package/test/stream.test.js +73 -0
- package/docs/Migration-Guide-V4.md +0 -12
package/lib/error-serializer.js
CHANGED
|
@@ -1,195 +1,191 @@
|
|
|
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
3
|
|
|
12
|
-
|
|
13
|
-
return JSON.stringify(i)
|
|
14
|
-
}
|
|
4
|
+
'use strict'
|
|
15
5
|
|
|
16
|
-
|
|
17
|
-
return 'null'
|
|
18
|
-
}
|
|
6
|
+
|
|
19
7
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
class Serializer {
|
|
9
|
+
constructor (options = {}) {
|
|
10
|
+
switch (options.rounding) {
|
|
11
|
+
case 'floor':
|
|
12
|
+
this.parseInteger = Math.floor
|
|
13
|
+
break
|
|
14
|
+
case 'ceil':
|
|
15
|
+
this.parseInteger = Math.ceil
|
|
16
|
+
break
|
|
17
|
+
case 'round':
|
|
18
|
+
this.parseInteger = Math.round
|
|
19
|
+
break
|
|
20
|
+
default:
|
|
21
|
+
this.parseInteger = Math.trunc
|
|
22
|
+
break
|
|
23
|
+
}
|
|
28
24
|
}
|
|
29
|
-
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
26
|
+
asAny (i) {
|
|
27
|
+
return JSON.stringify(i)
|
|
28
|
+
}
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
const num = Number(i)
|
|
37
|
-
if (isNaN(num)) {
|
|
30
|
+
asNull () {
|
|
38
31
|
return 'null'
|
|
39
|
-
} else {
|
|
40
|
-
return '' + num
|
|
41
32
|
}
|
|
42
|
-
}
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
asInteger (i) {
|
|
35
|
+
if (typeof i === 'bigint') {
|
|
36
|
+
return i.toString()
|
|
37
|
+
} else if (Number.isInteger(i)) {
|
|
38
|
+
return '' + i
|
|
39
|
+
} else {
|
|
40
|
+
/* eslint no-undef: "off" */
|
|
41
|
+
const integer = this.parseInteger(i)
|
|
42
|
+
if (Number.isNaN(integer)) {
|
|
43
|
+
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
44
|
+
} else {
|
|
45
|
+
return '' + integer
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
50
|
+
asIntegerNullable (i) {
|
|
51
|
+
return i === null ? 'null' : this.asInteger(i)
|
|
52
|
+
}
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
asNumber (i) {
|
|
55
|
+
const num = Number(i)
|
|
56
|
+
if (Number.isNaN(num)) {
|
|
57
|
+
throw new Error(`The value "${i}" cannot be converted to a number.`)
|
|
58
|
+
} else {
|
|
59
|
+
return '' + num
|
|
60
|
+
}
|
|
61
|
+
}
|
|
55
62
|
|
|
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)
|
|
63
|
+
asNumberNullable (i) {
|
|
64
|
+
return i === null ? 'null' : this.asNumber(i)
|
|
64
65
|
}
|
|
65
|
-
}
|
|
66
66
|
|
|
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)
|
|
67
|
+
asBoolean (bool) {
|
|
68
|
+
return bool && 'true' || 'false' // eslint-disable-line
|
|
75
69
|
}
|
|
76
|
-
}
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
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)
|
|
71
|
+
asBooleanNullable (bool) {
|
|
72
|
+
return bool === null ? 'null' : this.asBoolean(bool)
|
|
89
73
|
}
|
|
90
|
-
}
|
|
91
74
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return
|
|
98
|
-
} else if (str instanceof RegExp) {
|
|
99
|
-
str = str.source
|
|
100
|
-
} else if (typeof str !== 'string') {
|
|
101
|
-
str = str.toString()
|
|
75
|
+
asDatetime (date, skipQuotes) {
|
|
76
|
+
const quotes = skipQuotes === true ? '' : '"'
|
|
77
|
+
if (date instanceof Date) {
|
|
78
|
+
return quotes + date.toISOString() + quotes
|
|
79
|
+
}
|
|
80
|
+
return this.asString(date, skipQuotes)
|
|
102
81
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return str
|
|
82
|
+
|
|
83
|
+
asDatetimeNullable (date, skipQuotes) {
|
|
84
|
+
return date === null ? 'null' : this.asDatetime(date, skipQuotes)
|
|
107
85
|
}
|
|
108
86
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
87
|
+
asDate (date, skipQuotes) {
|
|
88
|
+
const quotes = skipQuotes === true ? '' : '"'
|
|
89
|
+
if (date instanceof Date) {
|
|
90
|
+
return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + quotes
|
|
91
|
+
}
|
|
92
|
+
return this.asString(date, skipQuotes)
|
|
113
93
|
}
|
|
114
|
-
}
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
95
|
+
asDateNullable (date, skipQuotes) {
|
|
96
|
+
return date === null ? 'null' : this.asDate(date, skipQuotes)
|
|
97
|
+
}
|
|
119
98
|
|
|
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
|
|
99
|
+
asTime (date, skipQuotes) {
|
|
100
|
+
const quotes = skipQuotes === true ? '' : '"'
|
|
101
|
+
if (date instanceof Date) {
|
|
102
|
+
return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + quotes
|
|
144
103
|
}
|
|
104
|
+
return this.asString(date, skipQuotes)
|
|
145
105
|
}
|
|
146
106
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} else {
|
|
150
|
-
result += str.slice(last)
|
|
107
|
+
asTimeNullable (date, skipQuotes) {
|
|
108
|
+
return date === null ? 'null' : this.asTime(date, skipQuotes)
|
|
151
109
|
}
|
|
152
|
-
return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"'
|
|
153
|
-
}
|
|
154
110
|
|
|
111
|
+
asString (str, skipQuotes) {
|
|
112
|
+
const quotes = skipQuotes === true ? '' : '"'
|
|
113
|
+
if (str instanceof Date) {
|
|
114
|
+
return quotes + str.toISOString() + quotes
|
|
115
|
+
} else if (str === null) {
|
|
116
|
+
return quotes + quotes
|
|
117
|
+
} else if (str instanceof RegExp) {
|
|
118
|
+
str = str.source
|
|
119
|
+
} else if (typeof str !== 'string') {
|
|
120
|
+
str = str.toString()
|
|
121
|
+
}
|
|
122
|
+
// If we skipQuotes it means that we are using it as test
|
|
123
|
+
// no need to test the string length for the render
|
|
124
|
+
if (skipQuotes) {
|
|
125
|
+
return str
|
|
126
|
+
}
|
|
155
127
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
}
|
|
128
|
+
if (str.length < 42) {
|
|
129
|
+
return this.asStringSmall(str)
|
|
130
|
+
} else {
|
|
131
|
+
return JSON.stringify(str)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
177
134
|
|
|
178
|
-
|
|
135
|
+
asStringNullable (str) {
|
|
136
|
+
return str === null ? 'null' : this.asString(str)
|
|
137
|
+
}
|
|
179
138
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
139
|
+
// magically escape strings for json
|
|
140
|
+
// relying on their charCodeAt
|
|
141
|
+
// everything below 32 needs JSON.stringify()
|
|
142
|
+
// every string that contain surrogate needs JSON.stringify()
|
|
143
|
+
// 34 and 92 happens all the time, so we
|
|
144
|
+
// have a fast case for them
|
|
145
|
+
asStringSmall (str) {
|
|
146
|
+
const l = str.length
|
|
147
|
+
let result = ''
|
|
148
|
+
let last = 0
|
|
149
|
+
let found = false
|
|
150
|
+
let surrogateFound = false
|
|
151
|
+
let point = 255
|
|
152
|
+
// eslint-disable-next-line
|
|
153
|
+
for (var i = 0; i < l && point >= 32; i++) {
|
|
154
|
+
point = str.charCodeAt(i)
|
|
155
|
+
if (point >= 0xD800 && point <= 0xDFFF) {
|
|
156
|
+
// The current character is a surrogate.
|
|
157
|
+
surrogateFound = true
|
|
186
158
|
}
|
|
187
|
-
|
|
159
|
+
if (point === 34 || point === 92) {
|
|
160
|
+
result += str.slice(last, i) + '\\'
|
|
161
|
+
last = i
|
|
162
|
+
found = true
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!found) {
|
|
167
|
+
result = str
|
|
168
|
+
} else {
|
|
169
|
+
result += str.slice(last)
|
|
170
|
+
}
|
|
171
|
+
return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"'
|
|
172
|
+
}
|
|
173
|
+
}
|
|
188
174
|
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
const serializer = new Serializer({"mode":"standalone"})
|
|
178
|
+
|
|
189
179
|
|
|
190
|
-
|
|
180
|
+
|
|
181
|
+
function main (input) {
|
|
182
|
+
let json = ''
|
|
183
|
+
json += anonymous0(input)
|
|
184
|
+
return json
|
|
185
|
+
}
|
|
191
186
|
|
|
192
|
-
function
|
|
187
|
+
function anonymous0 (input) {
|
|
188
|
+
// main
|
|
193
189
|
|
|
194
190
|
var obj = (input && typeof input.toJSON === 'function')
|
|
195
191
|
? input.toJSON()
|
|
@@ -198,60 +194,60 @@ function $asStringSmall (str) {
|
|
|
198
194
|
var json = '{'
|
|
199
195
|
var addComma = false
|
|
200
196
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
if (obj["statusCode"] !== undefined) {
|
|
198
|
+
|
|
204
199
|
if (addComma) {
|
|
205
200
|
json += ','
|
|
206
201
|
} else {
|
|
207
202
|
addComma = true
|
|
208
203
|
}
|
|
209
204
|
|
|
210
|
-
|
|
211
|
-
|
|
205
|
+
json += "\"statusCode\"" + ':'
|
|
206
|
+
json += serializer.asNumber.bind(serializer)(obj["statusCode"])
|
|
212
207
|
}
|
|
213
208
|
|
|
214
|
-
|
|
215
|
-
|
|
209
|
+
if (obj["code"] !== undefined) {
|
|
210
|
+
|
|
216
211
|
if (addComma) {
|
|
217
212
|
json += ','
|
|
218
213
|
} else {
|
|
219
214
|
addComma = true
|
|
220
215
|
}
|
|
221
216
|
|
|
222
|
-
|
|
223
|
-
|
|
217
|
+
json += "\"code\"" + ':'
|
|
218
|
+
json += serializer.asString.bind(serializer)(obj["code"])
|
|
224
219
|
}
|
|
225
220
|
|
|
226
|
-
|
|
227
|
-
|
|
221
|
+
if (obj["error"] !== undefined) {
|
|
222
|
+
|
|
228
223
|
if (addComma) {
|
|
229
224
|
json += ','
|
|
230
225
|
} else {
|
|
231
226
|
addComma = true
|
|
232
227
|
}
|
|
233
228
|
|
|
234
|
-
|
|
235
|
-
|
|
229
|
+
json += "\"error\"" + ':'
|
|
230
|
+
json += serializer.asString.bind(serializer)(obj["error"])
|
|
236
231
|
}
|
|
237
232
|
|
|
238
|
-
|
|
239
|
-
|
|
233
|
+
if (obj["message"] !== undefined) {
|
|
234
|
+
|
|
240
235
|
if (addComma) {
|
|
241
236
|
json += ','
|
|
242
237
|
} else {
|
|
243
238
|
addComma = true
|
|
244
239
|
}
|
|
245
240
|
|
|
246
|
-
|
|
247
|
-
|
|
241
|
+
json += "\"message\"" + ':'
|
|
242
|
+
json += serializer.asString.bind(serializer)(obj["message"])
|
|
248
243
|
}
|
|
249
244
|
|
|
250
245
|
json += '}'
|
|
251
246
|
return json
|
|
252
247
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
;
|
|
256
|
-
return $main
|
|
257
248
|
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
module.exports = main
|
|
253
|
+
|
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/server.js
CHANGED
|
@@ -41,7 +41,15 @@ function createServer (options, httpHandler) {
|
|
|
41
41
|
listenOptions.cb = cb
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// If we have a path specified, don't default host to 'localhost' so we don't end up listening
|
|
45
|
+
// on both path and host
|
|
46
|
+
// See https://github.com/fastify/fastify/issues/4007
|
|
47
|
+
let host
|
|
48
|
+
if (listenOptions.path == null) {
|
|
49
|
+
host = listenOptions.host ?? 'localhost'
|
|
50
|
+
} else {
|
|
51
|
+
host = listenOptions.host
|
|
52
|
+
}
|
|
45
53
|
if (Object.prototype.hasOwnProperty.call(listenOptions, 'host') === false) {
|
|
46
54
|
listenOptions.host = host
|
|
47
55
|
}
|
package/lib/wrapThenable.js
CHANGED
|
@@ -11,9 +11,14 @@ function wrapThenable (thenable, reply) {
|
|
|
11
11
|
return
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// this is for async functions that
|
|
15
|
-
//
|
|
16
|
-
|
|
14
|
+
// this is for async functions that are using reply.send directly
|
|
15
|
+
//
|
|
16
|
+
// since wrap-thenable will be called when using reply.send directly
|
|
17
|
+
// without actual return. the response can be sent already or
|
|
18
|
+
// the request may be terminated during the reply. in this situation,
|
|
19
|
+
// it require an extra checking of request.aborted to see whether
|
|
20
|
+
// the request is killed by client.
|
|
21
|
+
if (payload !== undefined || (reply.sent === false && reply.raw.headersSent === false && reply.request.raw.aborted === false)) {
|
|
17
22
|
// we use a try-catch internally to avoid adding a catch to another
|
|
18
23
|
// promise, increase promise perf by 10%
|
|
19
24
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
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,7 @@
|
|
|
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.
|
|
149
|
+
"fast-json-stringify": "^4.2.0",
|
|
149
150
|
"fastify-plugin": "^3.0.1",
|
|
150
151
|
"fluent-json-schema": "^3.1.0",
|
|
151
152
|
"form-data": "^4.0.0",
|
|
@@ -158,6 +159,7 @@
|
|
|
158
159
|
"json-schema-to-ts": "^2.5.3",
|
|
159
160
|
"JSONStream": "^1.3.5",
|
|
160
161
|
"license-checker": "^25.0.1",
|
|
162
|
+
"markdownlint-cli2": "^0.4.0",
|
|
161
163
|
"proxyquire": "^2.1.3",
|
|
162
164
|
"pump": "^3.0.0",
|
|
163
165
|
"self-cert": "^2.0.0",
|
|
@@ -168,7 +170,7 @@
|
|
|
168
170
|
"split2": "^4.1.0",
|
|
169
171
|
"standard": "^17.0.0-2",
|
|
170
172
|
"tap": "^16.2.0",
|
|
171
|
-
"tsd": "^0.
|
|
173
|
+
"tsd": "^0.21.0",
|
|
172
174
|
"typescript": "^4.7.2",
|
|
173
175
|
"undici": "^5.4.0",
|
|
174
176
|
"x-xss-protection": "^2.0.0",
|
|
@@ -177,7 +179,7 @@
|
|
|
177
179
|
"dependencies": {
|
|
178
180
|
"@fastify/ajv-compiler": "^3.1.0",
|
|
179
181
|
"@fastify/error": "^3.0.0",
|
|
180
|
-
"@fastify/fast-json-stringify-compiler": "^3.0.
|
|
182
|
+
"@fastify/fast-json-stringify-compiler": "^3.0.1",
|
|
181
183
|
"abstract-logging": "^2.0.1",
|
|
182
184
|
"avvio": "^8.1.3",
|
|
183
185
|
"find-my-way": "^6.3.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/listen.test.js
CHANGED
|
@@ -196,14 +196,28 @@ if (os.platform() !== 'win32') {
|
|
|
196
196
|
const fastify = Fastify()
|
|
197
197
|
t.teardown(fastify.close.bind(fastify))
|
|
198
198
|
|
|
199
|
-
const sockFile = path.join(os.tmpdir(), `${(Math.random().toString(16) + '0000000').
|
|
199
|
+
const sockFile = path.join(os.tmpdir(), `${(Math.random().toString(16) + '0000000').slice(2, 10)}-server.sock`)
|
|
200
200
|
try {
|
|
201
201
|
fs.unlinkSync(sockFile)
|
|
202
202
|
} catch (e) { }
|
|
203
203
|
|
|
204
204
|
fastify.listen({ path: sockFile }, (err, address) => {
|
|
205
205
|
t.error(err)
|
|
206
|
-
t.
|
|
206
|
+
t.strictSame(fastify.addresses(), [sockFile])
|
|
207
|
+
t.equal(address, sockFile)
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
} else {
|
|
211
|
+
test('listen on socket', t => {
|
|
212
|
+
t.plan(3)
|
|
213
|
+
const fastify = Fastify()
|
|
214
|
+
t.teardown(fastify.close.bind(fastify))
|
|
215
|
+
|
|
216
|
+
const sockFile = `\\\\.\\pipe\\${(Math.random().toString(16) + '0000000').slice(2, 10)}-server-sock`
|
|
217
|
+
|
|
218
|
+
fastify.listen({ path: sockFile }, (err, address) => {
|
|
219
|
+
t.error(err)
|
|
220
|
+
t.strictSame(fastify.addresses(), [sockFile])
|
|
207
221
|
t.equal(address, sockFile)
|
|
208
222
|
})
|
|
209
223
|
})
|
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 => {
|