fastify 4.0.3 → 4.2.1
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/.eslintrc +1 -0
- package/README.md +13 -14
- package/docs/Guides/Database.md +7 -7
- package/docs/Guides/Delay-Accepting-Requests.md +1 -1
- package/docs/Guides/Ecosystem.md +29 -16
- package/docs/Guides/Migration-Guide-V4.md +86 -1
- package/docs/Guides/Plugins-Guide.md +5 -0
- package/docs/Guides/Serverless.md +23 -10
- package/docs/Reference/Hooks.md +52 -0
- package/docs/Reference/Plugins.md +1 -1
- package/docs/Reference/Server.md +1 -1
- package/docs/Reference/Type-Providers.md +3 -17
- package/docs/Reference/TypeScript.md +65 -28
- package/docs/Reference/Validation-and-Serialization.md +11 -0
- package/docs/index.md +1 -1
- package/fastify.d.ts +3 -3
- package/fastify.js +19 -19
- package/integration/server.js +27 -0
- package/integration/test.sh +23 -0
- package/lib/context.js +5 -2
- package/lib/error-serializer.js +24 -27
- package/lib/handleRequest.js +1 -1
- package/lib/reply.js +22 -21
- package/lib/route.js +39 -29
- package/lib/symbols.js +2 -1
- package/lib/validation.js +2 -0
- package/package.json +10 -10
- package/test/404s.test.js +2 -2
- package/test/build/error-serializer.test.js +9 -2
- package/test/hooks.test.js +21 -0
- package/test/internals/reply.test.js +12 -0
- package/test/pretty-print.test.js +3 -3
- package/test/reply-error.test.js +1 -1
- package/test/schema-feature.test.js +2 -2
- package/test/schema-validation.test.js +71 -0
- package/test/stream.test.js +1 -1
- package/test/types/fastify.test-d.ts +24 -2
- package/test/types/instance.test-d.ts +5 -2
- package/test/types/register.test-d.ts +77 -2
- package/test/types/request.test-d.ts +8 -4
- package/test/types/type-provider.test-d.ts +11 -2
- package/test/validation-error-handling.test.js +38 -1
- package/types/instance.d.ts +59 -91
- package/types/register.d.ts +9 -7
- package/types/route.d.ts +10 -12
- package/types/schema.d.ts +5 -2
- package/types/type-provider.d.ts +12 -5
package/lib/route.js
CHANGED
|
@@ -8,7 +8,7 @@ const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTI
|
|
|
8
8
|
const { normalizeSchema } = require('./schemas')
|
|
9
9
|
const { parseHeadOnSendHandlers } = require('./headRoute')
|
|
10
10
|
const warning = require('./warnings')
|
|
11
|
-
const { kRequestAcceptVersion } = require('./symbols')
|
|
11
|
+
const { kRequestAcceptVersion, kRouteByFastify } = require('./symbols')
|
|
12
12
|
|
|
13
13
|
const {
|
|
14
14
|
compileSchemasForValidation,
|
|
@@ -113,7 +113,7 @@ function buildRouting (options) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// Convert shorthand to extended route declaration
|
|
116
|
-
function prepareRoute (method, url, options, handler) {
|
|
116
|
+
function prepareRoute ({ method, url, options, handler, isFastify }) {
|
|
117
117
|
if (typeof url !== 'string') {
|
|
118
118
|
throw new FST_ERR_INVALID_URL(typeof url)
|
|
119
119
|
}
|
|
@@ -140,11 +140,11 @@ function buildRouting (options) {
|
|
|
140
140
|
handler: handler || (options && options.handler)
|
|
141
141
|
})
|
|
142
142
|
|
|
143
|
-
return route.call(this, options)
|
|
143
|
+
return route.call(this, { options, isFastify })
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// Route management
|
|
147
|
-
function route (options) {
|
|
147
|
+
function route ({ options, isFastify }) {
|
|
148
148
|
// Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
|
|
149
149
|
const opts = { ...options }
|
|
150
150
|
|
|
@@ -176,30 +176,30 @@ function buildRouting (options) {
|
|
|
176
176
|
if (path === '/' && prefix.length > 0 && opts.method !== 'HEAD') {
|
|
177
177
|
switch (opts.prefixTrailingSlash) {
|
|
178
178
|
case 'slash':
|
|
179
|
-
addNewRoute.call(this, path)
|
|
179
|
+
addNewRoute.call(this, { path, isFastify })
|
|
180
180
|
break
|
|
181
181
|
case 'no-slash':
|
|
182
|
-
addNewRoute.call(this, '')
|
|
182
|
+
addNewRoute.call(this, { path: '', isFastify })
|
|
183
183
|
break
|
|
184
184
|
case 'both':
|
|
185
185
|
default:
|
|
186
|
-
addNewRoute.call(this, '')
|
|
186
|
+
addNewRoute.call(this, { path: '', isFastify })
|
|
187
187
|
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
|
|
188
188
|
if (ignoreTrailingSlash !== true && (ignoreDuplicateSlashes !== true || !prefix.endsWith('/'))) {
|
|
189
|
-
addNewRoute.call(this, path, true)
|
|
189
|
+
addNewRoute.call(this, { path, prefixing: true, isFastify })
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
} else if (path[0] === '/' && prefix.endsWith('/')) {
|
|
193
193
|
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
|
194
|
-
addNewRoute.call(this, path.slice(1))
|
|
194
|
+
addNewRoute.call(this, { path: path.slice(1), isFastify })
|
|
195
195
|
} else {
|
|
196
|
-
addNewRoute.call(this, path)
|
|
196
|
+
addNewRoute.call(this, { path, isFastify })
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
// chainable api
|
|
200
200
|
return this
|
|
201
201
|
|
|
202
|
-
function addNewRoute (path, prefixing = false) {
|
|
202
|
+
function addNewRoute ({ path, prefixing = false, isFastify = false }) {
|
|
203
203
|
const url = prefix + path
|
|
204
204
|
|
|
205
205
|
opts.url = url
|
|
@@ -241,7 +241,8 @@ function buildRouting (options) {
|
|
|
241
241
|
attachValidation: opts.attachValidation,
|
|
242
242
|
schemaErrorFormatter: opts.schemaErrorFormatter,
|
|
243
243
|
replySerializer: this[kReplySerializerDefault],
|
|
244
|
-
server: this
|
|
244
|
+
server: this,
|
|
245
|
+
isFastify
|
|
245
246
|
})
|
|
246
247
|
|
|
247
248
|
if (opts.version) {
|
|
@@ -249,13 +250,20 @@ function buildRouting (options) {
|
|
|
249
250
|
constraints.version = opts.version
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
const
|
|
253
|
+
const headHandler = router.find('HEAD', opts.url, constraints)
|
|
254
|
+
const hasHEADHandler = headHandler != null
|
|
253
255
|
|
|
254
|
-
//
|
|
255
|
-
if (!
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
// remove the head route created by fastify
|
|
257
|
+
if (hasHEADHandler && !context[kRouteByFastify] && headHandler.store[kRouteByFastify]) {
|
|
258
|
+
router.off(opts.method, opts.url, { constraints })
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
router.on(opts.method, opts.url, { constraints }, routeHandler, context)
|
|
263
|
+
} catch (error) {
|
|
264
|
+
// any route insertion error created by fastify can be safely ignore
|
|
265
|
+
// because it only duplicate route for head
|
|
266
|
+
if (!context[kRouteByFastify]) {
|
|
259
267
|
const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route '${opts.url}'`)
|
|
260
268
|
if (isDuplicatedRoute) {
|
|
261
269
|
throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
|
|
@@ -320,19 +328,21 @@ function buildRouting (options) {
|
|
|
320
328
|
}
|
|
321
329
|
})
|
|
322
330
|
|
|
323
|
-
const { exposeHeadRoute } = opts
|
|
324
|
-
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
|
325
|
-
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
|
326
|
-
|
|
327
|
-
if (shouldExposeHead && options.method === 'GET' && !headRouteExists) {
|
|
328
|
-
const onSendHandlers = parseHeadOnSendHandlers(opts.onSend)
|
|
329
|
-
prepareRoute.call(this, 'HEAD', path, { ...opts, onSend: onSendHandlers })
|
|
330
|
-
} else if (headRouteExists && exposeHeadRoute) {
|
|
331
|
-
warning.emit('FSTDEP007')
|
|
332
|
-
}
|
|
333
|
-
|
|
334
331
|
done(notHandledErr)
|
|
335
332
|
})
|
|
333
|
+
|
|
334
|
+
// register head route in sync
|
|
335
|
+
// we must place it after the `this.after`
|
|
336
|
+
const { exposeHeadRoute } = opts
|
|
337
|
+
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
|
338
|
+
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
|
339
|
+
|
|
340
|
+
if (shouldExposeHead && options.method === 'GET' && !hasHEADHandler) {
|
|
341
|
+
const onSendHandlers = parseHeadOnSendHandlers(opts.onSend)
|
|
342
|
+
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...opts, onSend: onSendHandlers }, isFastify: true })
|
|
343
|
+
} else if (hasHEADHandler && exposeHeadRoute) {
|
|
344
|
+
warning.emit('FSTDEP007')
|
|
345
|
+
}
|
|
336
346
|
}
|
|
337
347
|
}
|
|
338
348
|
|
package/lib/symbols.js
CHANGED
|
@@ -47,7 +47,8 @@ const keys = {
|
|
|
47
47
|
kTestInternals: Symbol('fastify.testInternals'),
|
|
48
48
|
kErrorHandler: Symbol('fastify.errorHandler'),
|
|
49
49
|
kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
|
|
50
|
-
kKeepAliveConnections: Symbol('fastify.keepAliveConnections')
|
|
50
|
+
kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
|
|
51
|
+
kRouteByFastify: Symbol('fastify.routeByFastify')
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
module.exports = keys
|
package/lib/validation.js
CHANGED
|
@@ -106,11 +106,13 @@ function validate (context, request) {
|
|
|
106
106
|
|
|
107
107
|
function wrapValidationError (result, dataVar, schemaErrorFormatter) {
|
|
108
108
|
if (result instanceof Error) {
|
|
109
|
+
result.statusCode = result.statusCode || 400
|
|
109
110
|
result.validationContext = result.validationContext || dataVar
|
|
110
111
|
return result
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
const error = schemaErrorFormatter(result, dataVar)
|
|
115
|
+
error.statusCode = error.statusCode || 400
|
|
114
116
|
error.validation = result
|
|
115
117
|
error.validationContext = dataVar
|
|
116
118
|
return error
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.1",
|
|
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,9 +126,9 @@
|
|
|
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
|
-
"@types/node": "^
|
|
131
|
+
"@types/node": "^18.0.0",
|
|
132
132
|
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
|
133
133
|
"@typescript-eslint/parser": "^5.27.0",
|
|
134
134
|
"ajv": "^8.11.0",
|
|
@@ -146,8 +146,8 @@
|
|
|
146
146
|
"eslint-plugin-n": "^15.2.0",
|
|
147
147
|
"eslint-plugin-promise": "^6.0.0",
|
|
148
148
|
"fast-json-body": "^1.1.0",
|
|
149
|
-
"fast-json-stringify": "^
|
|
150
|
-
"fastify-plugin": "^
|
|
149
|
+
"fast-json-stringify": "^5.0.0",
|
|
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",
|
|
@@ -170,19 +170,19 @@
|
|
|
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
|
-
"@fastify/fast-json-stringify-compiler": "^
|
|
182
|
+
"@fastify/fast-json-stringify-compiler": "^4.0.0",
|
|
183
183
|
"abstract-logging": "^2.0.1",
|
|
184
184
|
"avvio": "^8.1.3",
|
|
185
|
-
"find-my-way": "^
|
|
185
|
+
"find-my-way": "^7.0.0",
|
|
186
186
|
"light-my-request": "^5.0.0",
|
|
187
187
|
"pino": "^8.0.0",
|
|
188
188
|
"process-warning": "^2.0.0",
|
package/test/404s.test.js
CHANGED
|
@@ -1320,7 +1320,7 @@ test('preHandler option for setNotFoundHandler', t => {
|
|
|
1320
1320
|
|
|
1321
1321
|
// https://github.com/fastify/fastify/issues/2229
|
|
1322
1322
|
t.test('preHandler hook in setNotFoundHandler should be called when callNotFound', { timeout: 40000 }, t => {
|
|
1323
|
-
t.plan(
|
|
1323
|
+
t.plan(3)
|
|
1324
1324
|
const fastify = Fastify()
|
|
1325
1325
|
|
|
1326
1326
|
fastify.setNotFoundHandler({
|
|
@@ -1333,7 +1333,7 @@ test('preHandler option for setNotFoundHandler', t => {
|
|
|
1333
1333
|
})
|
|
1334
1334
|
|
|
1335
1335
|
fastify.post('/', function (req, reply) {
|
|
1336
|
-
reply.callNotFound()
|
|
1336
|
+
t.equal(reply.callNotFound(), reply)
|
|
1337
1337
|
})
|
|
1338
1338
|
|
|
1339
1339
|
fastify.inject({
|
|
@@ -7,6 +7,10 @@ const path = require('path')
|
|
|
7
7
|
|
|
8
8
|
const { code } = require('../../build/build-error-serializer')
|
|
9
9
|
|
|
10
|
+
function unifyLineBreak (str) {
|
|
11
|
+
return str.toString().replace(/\r\n/g, '\n')
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
test('check generated code syntax', async (t) => {
|
|
11
15
|
t.plan(1)
|
|
12
16
|
|
|
@@ -19,10 +23,13 @@ test('check generated code syntax', async (t) => {
|
|
|
19
23
|
t.equal(result[0].fatalErrorCount, 0)
|
|
20
24
|
})
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
const isPrebublish = !!process.env.PREPUBLISH
|
|
27
|
+
|
|
28
|
+
test('ensure the current error serializer is latest', { skip: !isPrebublish }, async (t) => {
|
|
23
29
|
t.plan(1)
|
|
24
30
|
|
|
25
31
|
const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
// line break should not be a problem depends on system
|
|
34
|
+
t.equal(unifyLineBreak(current), unifyLineBreak(code))
|
|
28
35
|
})
|
package/test/hooks.test.js
CHANGED
|
@@ -799,6 +799,27 @@ test('onRoute hook with many prefix', t => {
|
|
|
799
799
|
fastify.ready(err => { t.error(err) })
|
|
800
800
|
})
|
|
801
801
|
|
|
802
|
+
test('onRoute hook should not be called when it registered after route', t => {
|
|
803
|
+
t.plan(3)
|
|
804
|
+
const fastify = Fastify()
|
|
805
|
+
|
|
806
|
+
fastify.addHook('onRoute', () => {
|
|
807
|
+
t.pass()
|
|
808
|
+
})
|
|
809
|
+
|
|
810
|
+
fastify.get('/', function (req, reply) {
|
|
811
|
+
reply.send()
|
|
812
|
+
})
|
|
813
|
+
|
|
814
|
+
fastify.addHook('onRoute', () => {
|
|
815
|
+
t.fail('should not be called')
|
|
816
|
+
})
|
|
817
|
+
|
|
818
|
+
fastify.ready(err => {
|
|
819
|
+
t.error(err)
|
|
820
|
+
})
|
|
821
|
+
})
|
|
822
|
+
|
|
802
823
|
test('onResponse hook should log request error', t => {
|
|
803
824
|
t.plan(4)
|
|
804
825
|
|
|
@@ -233,6 +233,10 @@ test('within an instance', t => {
|
|
|
233
233
|
reply.redirect('/')
|
|
234
234
|
})
|
|
235
235
|
|
|
236
|
+
fastify.get('/redirect-async', async function (req, reply) {
|
|
237
|
+
return reply.redirect('/')
|
|
238
|
+
})
|
|
239
|
+
|
|
236
240
|
fastify.get('/redirect-code', function (req, reply) {
|
|
237
241
|
reply.redirect(301, '/')
|
|
238
242
|
})
|
|
@@ -412,6 +416,14 @@ test('within an instance', t => {
|
|
|
412
416
|
})
|
|
413
417
|
})
|
|
414
418
|
|
|
419
|
+
test('redirect with async function to `/` - 10', t => {
|
|
420
|
+
t.plan(1)
|
|
421
|
+
|
|
422
|
+
http.get('http://localhost:' + fastify.server.address().port + '/redirect-async', function (response) {
|
|
423
|
+
t.equal(response.statusCode, 302)
|
|
424
|
+
})
|
|
425
|
+
})
|
|
426
|
+
|
|
415
427
|
t.end()
|
|
416
428
|
})
|
|
417
429
|
})
|
|
@@ -144,7 +144,7 @@ test('pretty print - commonPrefix', t => {
|
|
|
144
144
|
`
|
|
145
145
|
const flatExpected = `└── / (-)
|
|
146
146
|
├── helicopter (GET, HEAD)
|
|
147
|
-
└── hello (GET,
|
|
147
|
+
└── hello (GET, HEAD, PUT)
|
|
148
148
|
`
|
|
149
149
|
t.equal(typeof radixTree, 'string')
|
|
150
150
|
t.equal(typeof flatTree, 'string')
|
|
@@ -200,7 +200,7 @@ test('pretty print - includeMeta, includeHooks', t => {
|
|
|
200
200
|
│ • (onTimeout) ["onTimeout()"]
|
|
201
201
|
│ • (onRequest) ["anonymous()"]
|
|
202
202
|
│ • (errorHandler) "defaultErrorHandler()"
|
|
203
|
-
└── hello (GET,
|
|
203
|
+
└── hello (GET, HEAD, PUT)
|
|
204
204
|
• (onTimeout) ["onTimeout()"]
|
|
205
205
|
• (onRequest) ["anonymous()"]
|
|
206
206
|
• (errorHandler) "defaultErrorHandler()"
|
|
@@ -210,7 +210,7 @@ test('pretty print - includeMeta, includeHooks', t => {
|
|
|
210
210
|
├── helicopter (GET, HEAD)
|
|
211
211
|
│ • (onTimeout) ["onTimeout()"]
|
|
212
212
|
│ • (onRequest) ["anonymous()"]
|
|
213
|
-
└── hello (GET,
|
|
213
|
+
└── hello (GET, HEAD, PUT)
|
|
214
214
|
• (onTimeout) ["onTimeout()"]
|
|
215
215
|
• (onRequest) ["anonymous()"]
|
|
216
216
|
`
|
package/test/reply-error.test.js
CHANGED
|
@@ -206,7 +206,7 @@ test('Should throw of the schema does not exists in output', t => {
|
|
|
206
206
|
|
|
207
207
|
fastify.ready(err => {
|
|
208
208
|
t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')
|
|
209
|
-
t.match(err.message, /^Failed building the serialization schema for GET: \/:id, due to error Cannot
|
|
209
|
+
t.match(err.message, /^Failed building the serialization schema for GET: \/:id, due to error Cannot find reference.*/) // error from fast-json-strinfigy
|
|
210
210
|
})
|
|
211
211
|
})
|
|
212
212
|
|
|
@@ -826,7 +826,7 @@ test('Validation context in validation result', t => {
|
|
|
826
826
|
t.equal(err instanceof Error, true)
|
|
827
827
|
t.ok(err.validation, 'detailed errors')
|
|
828
828
|
t.equal(err.validationContext, 'body')
|
|
829
|
-
reply.send()
|
|
829
|
+
reply.code(400).send()
|
|
830
830
|
})
|
|
831
831
|
fastify.post('/', {
|
|
832
832
|
handler: echoParams,
|
|
@@ -4,6 +4,7 @@ const { test } = require('tap')
|
|
|
4
4
|
const Fastify = require('..')
|
|
5
5
|
|
|
6
6
|
const AJV = require('ajv')
|
|
7
|
+
const Schema = require('fluent-json-schema')
|
|
7
8
|
|
|
8
9
|
const customSchemaCompilers = {
|
|
9
10
|
body: new AJV({
|
|
@@ -946,3 +947,73 @@ test('Custom AJV settings on different parameters - pt2', t => {
|
|
|
946
947
|
}
|
|
947
948
|
})
|
|
948
949
|
})
|
|
950
|
+
|
|
951
|
+
test("The same $id in route's schema must not overwrite others", t => {
|
|
952
|
+
t.plan(4)
|
|
953
|
+
const fastify = Fastify()
|
|
954
|
+
|
|
955
|
+
const UserSchema = Schema.object()
|
|
956
|
+
.id('http://mydomain.com/user')
|
|
957
|
+
.title('User schema')
|
|
958
|
+
.description('Contains all user fields')
|
|
959
|
+
.prop('id', Schema.integer())
|
|
960
|
+
.prop('username', Schema.string().minLength(4))
|
|
961
|
+
.prop('firstName', Schema.string().minLength(1))
|
|
962
|
+
.prop('lastName', Schema.string().minLength(1))
|
|
963
|
+
.prop('fullName', Schema.string().minLength(1))
|
|
964
|
+
.prop('email', Schema.string())
|
|
965
|
+
.prop('password', Schema.string().minLength(6))
|
|
966
|
+
.prop('bio', Schema.string())
|
|
967
|
+
|
|
968
|
+
const userCreateSchema = UserSchema.only([
|
|
969
|
+
'username',
|
|
970
|
+
'firstName',
|
|
971
|
+
'lastName',
|
|
972
|
+
'email',
|
|
973
|
+
'bio',
|
|
974
|
+
'password',
|
|
975
|
+
'password_confirm'
|
|
976
|
+
])
|
|
977
|
+
.required([
|
|
978
|
+
'username',
|
|
979
|
+
'firstName',
|
|
980
|
+
'lastName',
|
|
981
|
+
'email',
|
|
982
|
+
'bio',
|
|
983
|
+
'password'
|
|
984
|
+
])
|
|
985
|
+
|
|
986
|
+
const userPatchSchema = UserSchema.only([
|
|
987
|
+
'firstName',
|
|
988
|
+
'lastName',
|
|
989
|
+
'bio'
|
|
990
|
+
])
|
|
991
|
+
|
|
992
|
+
fastify
|
|
993
|
+
.patch('/user/:id', {
|
|
994
|
+
schema: { body: userPatchSchema },
|
|
995
|
+
handler: () => { return 'ok' }
|
|
996
|
+
})
|
|
997
|
+
.post('/user', {
|
|
998
|
+
schema: { body: userCreateSchema },
|
|
999
|
+
handler: () => { return 'ok' }
|
|
1000
|
+
})
|
|
1001
|
+
|
|
1002
|
+
fastify.inject({
|
|
1003
|
+
method: 'POST',
|
|
1004
|
+
url: '/user',
|
|
1005
|
+
body: {}
|
|
1006
|
+
}, (err, res) => {
|
|
1007
|
+
t.error(err)
|
|
1008
|
+
t.same(res.json().message, "body must have required property 'username'")
|
|
1009
|
+
})
|
|
1010
|
+
|
|
1011
|
+
fastify.inject({
|
|
1012
|
+
url: '/user/1',
|
|
1013
|
+
method: 'PATCH',
|
|
1014
|
+
body: {}
|
|
1015
|
+
}, (err, res) => {
|
|
1016
|
+
t.error(err)
|
|
1017
|
+
t.same(res.payload, 'ok')
|
|
1018
|
+
})
|
|
1019
|
+
})
|
package/test/stream.test.js
CHANGED
|
@@ -770,7 +770,7 @@ test('request terminated should not crash fastify', t => {
|
|
|
770
770
|
|
|
771
771
|
reply.send(stream)
|
|
772
772
|
|
|
773
|
-
await new Promise((resolve) => { setTimeout(resolve,
|
|
773
|
+
await new Promise((resolve) => { setTimeout(resolve, 100).unref() })
|
|
774
774
|
|
|
775
775
|
stream.push('<h1>should disply on second stream</h1>')
|
|
776
776
|
stream.push(null)
|
|
@@ -7,8 +7,10 @@ import fastify, {
|
|
|
7
7
|
LightMyRequestChain,
|
|
8
8
|
LightMyRequestResponse,
|
|
9
9
|
LightMyRequestCallback,
|
|
10
|
-
InjectOptions, FastifyBaseLogger
|
|
10
|
+
InjectOptions, FastifyBaseLogger,
|
|
11
|
+
ValidationResult
|
|
11
12
|
} from '../../fastify'
|
|
13
|
+
import { ErrorObject as AjvErrorObject } from 'ajv'
|
|
12
14
|
import * as http from 'http'
|
|
13
15
|
import * as https from 'https'
|
|
14
16
|
import * as http2 from 'http2'
|
|
@@ -192,7 +194,18 @@ expectAssignable<FastifyInstance>(fastify({ frameworkErrors: () => { } }))
|
|
|
192
194
|
expectAssignable<FastifyInstance>(fastify({
|
|
193
195
|
rewriteUrl: (req) => req.url === '/hi' ? '/hello' : req.url!
|
|
194
196
|
}))
|
|
195
|
-
expectAssignable<FastifyInstance>(fastify({
|
|
197
|
+
expectAssignable<FastifyInstance>(fastify({
|
|
198
|
+
schemaErrorFormatter: (errors, dataVar) => {
|
|
199
|
+
console.log(
|
|
200
|
+
errors[0].keyword.toLowerCase(),
|
|
201
|
+
errors[0].message?.toLowerCase(),
|
|
202
|
+
errors[0].params,
|
|
203
|
+
errors[0].instancePath.toLowerCase(),
|
|
204
|
+
errors[0].schemaPath.toLowerCase()
|
|
205
|
+
)
|
|
206
|
+
return new Error()
|
|
207
|
+
}
|
|
208
|
+
}))
|
|
196
209
|
expectAssignable<FastifyInstance>(fastify({
|
|
197
210
|
clientErrorHandler: (err, socket) => {
|
|
198
211
|
expectType<ConnectionError>(err)
|
|
@@ -208,3 +221,12 @@ fastify().then(fastifyInstance => expectAssignable<FastifyInstance>(fastifyInsta
|
|
|
208
221
|
expectAssignable<FastifyPluginAsync>(async () => {})
|
|
209
222
|
expectAssignable<FastifyPluginCallback>(() => {})
|
|
210
223
|
expectAssignable<FastifyPlugin>(() => {})
|
|
224
|
+
|
|
225
|
+
const ajvErrorObject: AjvErrorObject = {
|
|
226
|
+
keyword: '',
|
|
227
|
+
instancePath: '',
|
|
228
|
+
schemaPath: '',
|
|
229
|
+
params: {},
|
|
230
|
+
message: ''
|
|
231
|
+
}
|
|
232
|
+
expectAssignable<ValidationResult>(ajvErrorObject)
|
|
@@ -11,7 +11,7 @@ import { HookHandlerDoneFunction } from '../../types/hooks'
|
|
|
11
11
|
import { FastifyReply } from '../../types/reply'
|
|
12
12
|
import { FastifyRequest } from '../../types/request'
|
|
13
13
|
import { DefaultRoute } from '../../types/route'
|
|
14
|
-
import { FastifySchemaControllerOptions } from '../../types/schema'
|
|
14
|
+
import { FastifySchemaControllerOptions, FastifySchemaCompiler, FastifySerializerCompiler } from '../../types/schema'
|
|
15
15
|
|
|
16
16
|
const server = fastify()
|
|
17
17
|
|
|
@@ -133,7 +133,7 @@ expectError(server.setErrorHandler(invalidErrorHandler))
|
|
|
133
133
|
server.setSchemaController({
|
|
134
134
|
bucket: (parentSchemas: unknown) => {
|
|
135
135
|
return {
|
|
136
|
-
|
|
136
|
+
add (schema: unknown) {
|
|
137
137
|
expectType<unknown>(schema)
|
|
138
138
|
expectType<FastifyInstance>(server.addSchema({ type: 'null' }))
|
|
139
139
|
return server.addSchema({ type: 'null' })
|
|
@@ -325,3 +325,6 @@ expectType<void>(server.addConstraintStrategy(versionConstraintStrategy))
|
|
|
325
325
|
expectType<boolean>(server.hasConstraintStrategy(versionConstraintStrategy.name))
|
|
326
326
|
|
|
327
327
|
expectAssignable<DefaultRoute<RawRequestDefaultExpression, RawReplyDefaultExpression>>(server.getDefaultRoute())
|
|
328
|
+
|
|
329
|
+
expectType<FastifySchemaCompiler<any> | undefined>(server.validatorCompiler)
|
|
330
|
+
expectType<FastifySerializerCompiler<any> | undefined>(server.serializerCompiler)
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { expectAssignable, expectError, expectType } from 'tsd'
|
|
2
|
-
import
|
|
2
|
+
import { IncomingMessage, Server, ServerResponse } from 'http'
|
|
3
|
+
import { Http2Server, Http2ServerRequest, Http2ServerResponse } from 'http2'
|
|
4
|
+
import fastify, { FastifyInstance, FastifyError, FastifyLoggerInstance, FastifyPluginAsync, FastifyPluginCallback, FastifyPluginOptions, RawServerDefault } from '../../fastify'
|
|
3
5
|
|
|
4
|
-
const
|
|
6
|
+
const testPluginCallback: FastifyPluginCallback = function (instance, opts, done) { }
|
|
7
|
+
const testPluginAsync: FastifyPluginAsync = async function (instance, opts) { }
|
|
8
|
+
|
|
9
|
+
const testPluginOpts: FastifyPluginCallback = function (instance, opts, done) { }
|
|
10
|
+
const testPluginOptsAsync: FastifyPluginAsync = async function (instance, opts) { }
|
|
11
|
+
|
|
12
|
+
const testPluginOptsWithType = (instance: FastifyInstance, opts: FastifyPluginOptions, done: (error?: FastifyError) => void) => { }
|
|
13
|
+
const testPluginOptsWithTypeAsync = async (instance: FastifyInstance, opts: FastifyPluginOptions) => { }
|
|
14
|
+
|
|
15
|
+
interface TestOptions extends FastifyPluginOptions {
|
|
16
|
+
option1: string;
|
|
17
|
+
option2: boolean;
|
|
18
|
+
}
|
|
5
19
|
|
|
6
20
|
// Type validation
|
|
7
21
|
expectError(fastify().register(testPluginOptsAsync, { prefix: 1 }))
|
|
@@ -26,3 +40,64 @@ expectAssignable<FastifyInstance>(
|
|
|
26
40
|
expectType<FastifyInstance>(instance)
|
|
27
41
|
})
|
|
28
42
|
)
|
|
43
|
+
|
|
44
|
+
// With Http2
|
|
45
|
+
const serverWithHttp2 = fastify({ http2: true })
|
|
46
|
+
type ServerWithHttp2 = FastifyInstance<Http2Server, Http2ServerRequest, Http2ServerResponse>
|
|
47
|
+
const testPluginWithHttp2: FastifyPluginCallback<TestOptions, Http2Server> = function (instance, opts, done) { }
|
|
48
|
+
const testPluginWithHttp2Async: FastifyPluginAsync<TestOptions, Http2Server> = async function (instance, opts) { }
|
|
49
|
+
const testPluginWithHttp2WithType = (instance: ServerWithHttp2, opts: FastifyPluginOptions, done: (error?: FastifyError) => void) => { }
|
|
50
|
+
const testPluginWithHttp2WithTypeAsync = async (instance: ServerWithHttp2, opts: FastifyPluginOptions) => { }
|
|
51
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginCallback))
|
|
52
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginAsync))
|
|
53
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOpts))
|
|
54
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOptsAsync))
|
|
55
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOptsWithType))
|
|
56
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginOptsWithTypeAsync))
|
|
57
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2))
|
|
58
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2Async))
|
|
59
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2WithType))
|
|
60
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(testPluginWithHttp2WithTypeAsync))
|
|
61
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register((instance) => {
|
|
62
|
+
expectAssignable<FastifyInstance>(instance)
|
|
63
|
+
}))
|
|
64
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register((instance: ServerWithHttp2) => {
|
|
65
|
+
expectAssignable<ServerWithHttp2>(instance)
|
|
66
|
+
}))
|
|
67
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(async (instance) => {
|
|
68
|
+
expectAssignable<FastifyInstance>(instance)
|
|
69
|
+
}))
|
|
70
|
+
expectAssignable<ServerWithHttp2>(serverWithHttp2.register(async (instance: ServerWithHttp2) => {
|
|
71
|
+
expectAssignable<ServerWithHttp2>(instance)
|
|
72
|
+
}))
|
|
73
|
+
|
|
74
|
+
// With Type Provider
|
|
75
|
+
type TestTypeProvider = { input: 'test', output: 'test' }
|
|
76
|
+
const serverWithTypeProvider = fastify().withTypeProvider<TestTypeProvider>()
|
|
77
|
+
type ServerWithTypeProvider = FastifyInstance<Server, IncomingMessage, ServerResponse, FastifyLoggerInstance, TestTypeProvider>
|
|
78
|
+
const testPluginWithTypeProvider: FastifyPluginCallback<TestOptions, RawServerDefault, TestTypeProvider> = function (instance, opts, done) { }
|
|
79
|
+
const testPluginWithTypeProviderAsync: FastifyPluginAsync<TestOptions, RawServerDefault, TestTypeProvider> = async function (instance, opts) { }
|
|
80
|
+
const testPluginWithTypeProviderWithType = (instance: ServerWithTypeProvider, opts: FastifyPluginOptions, done: (error?: FastifyError) => void) => { }
|
|
81
|
+
const testPluginWithTypeProviderWithTypeAsync = async (instance: ServerWithTypeProvider, opts: FastifyPluginOptions) => { }
|
|
82
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginCallback))
|
|
83
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginAsync))
|
|
84
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOpts))
|
|
85
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOptsAsync))
|
|
86
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOptsWithType))
|
|
87
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginOptsWithTypeAsync))
|
|
88
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProvider))
|
|
89
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProviderAsync))
|
|
90
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProviderWithType))
|
|
91
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(testPluginWithTypeProviderWithTypeAsync))
|
|
92
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register((instance) => {
|
|
93
|
+
expectAssignable<FastifyInstance>(instance)
|
|
94
|
+
}))
|
|
95
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register((instance: ServerWithTypeProvider) => {
|
|
96
|
+
expectAssignable<ServerWithTypeProvider>(instance)
|
|
97
|
+
}))
|
|
98
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(async (instance) => {
|
|
99
|
+
expectAssignable<FastifyInstance>(instance)
|
|
100
|
+
}))
|
|
101
|
+
expectAssignable<ServerWithTypeProvider>(serverWithTypeProvider.register(async (instance: ServerWithTypeProvider) => {
|
|
102
|
+
expectAssignable<ServerWithTypeProvider>(instance)
|
|
103
|
+
}))
|