fastify 5.8.4 → 5.9.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/SECURITY.md +1 -1
- package/SPONSORS.md +6 -4
- package/docs/Guides/Database.md +0 -28
- package/docs/Guides/Ecosystem.md +17 -2
- package/docs/Guides/Serverless.md +2 -2
- package/docs/Guides/Write-Plugin.md +1 -1
- package/docs/Reference/Encapsulation.md +27 -26
- package/docs/Reference/Errors.md +10 -4
- package/docs/Reference/HTTP2.md +10 -10
- package/docs/Reference/Hooks.md +4 -4
- package/docs/Reference/Index.md +14 -16
- package/docs/Reference/LTS.md +12 -13
- package/docs/Reference/Lifecycle.md +9 -8
- package/docs/Reference/Logging.md +44 -39
- package/docs/Reference/Middleware.md +21 -25
- package/docs/Reference/Principles.md +2 -2
- package/docs/Reference/Reply.md +6 -1
- package/docs/Reference/Request.md +27 -16
- package/docs/Reference/Routes.md +5 -2
- package/docs/Reference/Server.md +31 -3
- package/docs/Reference/Type-Providers.md +29 -5
- package/docs/Reference/Validation-and-Serialization.md +15 -2
- package/docs/Reference/Warnings.md +7 -6
- package/eslint.config.js +7 -2
- package/fastify.d.ts +8 -3
- package/fastify.js +43 -14
- package/lib/content-type-parser.js +13 -1
- package/lib/decorate.js +11 -3
- package/lib/error-handler.js +4 -3
- package/lib/error-serializer.js +59 -59
- package/lib/errors.js +16 -1
- package/lib/four-oh-four.js +14 -9
- package/lib/handle-request.js +11 -5
- package/lib/plugin-override.js +2 -1
- package/lib/plugin-utils.js +5 -5
- package/lib/reply.js +63 -8
- package/lib/request.js +23 -19
- package/lib/route.js +20 -6
- package/lib/schema-controller.js +1 -1
- package/lib/schemas.js +37 -30
- package/lib/symbols.js +3 -1
- package/lib/validation.js +1 -13
- package/lib/warnings.js +3 -3
- package/package.json +16 -18
- package/scripts/validate-ecosystem-links.js +1 -0
- package/test/bundler/esbuild/package.json +1 -1
- package/test/close-pipelining.test.js +1 -2
- package/test/custom-http-server.test.js +38 -0
- package/test/decorator-instance-properties.test.js +63 -0
- package/test/diagnostics-channel/async-error-handler.test.js +74 -0
- package/test/hooks.test.js +23 -0
- package/test/http-methods/get.test.js +1 -1
- package/test/http2/plain.test.js +135 -0
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/https/https.test.js +1 -2
- package/test/internals/errors.test.js +31 -1
- package/test/internals/plugin.test.js +3 -1
- package/test/internals/request.test.js +27 -3
- package/test/internals/schema-controller-perf.test.js +33 -0
- package/test/logger/logging.test.js +18 -1
- package/test/logger/options.test.js +38 -1
- package/test/reply-error.test.js +1 -1
- package/test/reply-trailers.test.js +70 -0
- package/test/request-media-type.test.js +105 -0
- package/test/request-port.test.js +72 -0
- package/test/route-prefix.test.js +34 -0
- package/test/router-options.test.js +222 -11
- package/test/schema-serialization.test.js +108 -0
- package/test/schema-validation.test.js +45 -0
- package/test/scripts/validate-ecosystem-links.test.js +40 -57
- package/test/throw.test.js +14 -0
- package/test/trust-proxy.test.js +74 -0
- package/test/types/content-type-parser.tst.ts +70 -0
- package/test/types/{decorate-request-reply.test-d.ts → decorate-request-reply.tst.ts} +4 -4
- package/test/types/{dummy-plugin.ts → dummy-plugin.mts} +1 -1
- package/test/types/errors.tst.ts +91 -0
- package/test/types/fastify.tst.ts +351 -0
- package/test/types/hooks.tst.ts +578 -0
- package/test/types/instance.tst.ts +597 -0
- package/test/types/{logger.test-d.ts → logger.tst.ts} +61 -62
- package/test/types/plugin.tst.ts +96 -0
- package/test/types/register.tst.ts +245 -0
- package/test/types/reply.tst.ts +297 -0
- package/test/types/request.tst.ts +199 -0
- package/test/types/route.tst.ts +576 -0
- package/test/types/{schema.test-d.ts → schema.tst.ts} +22 -22
- package/test/types/{serverFactory.test-d.ts → serverFactory.tst.ts} +4 -4
- package/test/types/tsconfig.json +9 -0
- package/test/types/{type-provider.test-d.ts → type-provider.tst.ts} +256 -250
- package/test/types/using.tst.ts +14 -0
- package/types/errors.d.ts +3 -0
- package/types/request.d.ts +23 -2
- package/AGENTS.md +0 -290
- package/test/types/content-type-parser.test-d.ts +0 -72
- package/test/types/errors.test-d.ts +0 -90
- package/test/types/fastify.test-d.ts +0 -352
- package/test/types/hooks.test-d.ts +0 -550
- package/test/types/import.ts +0 -2
- package/test/types/instance.test-d.ts +0 -588
- package/test/types/plugin.test-d.ts +0 -97
- package/test/types/register.test-d.ts +0 -237
- package/test/types/reply.test-d.ts +0 -254
- package/test/types/request.test-d.ts +0 -188
- package/test/types/route.test-d.ts +0 -553
- package/test/types/using.test-d.ts +0 -17
|
@@ -6,6 +6,7 @@ const Fastify = require('..')
|
|
|
6
6
|
const AJV = require('ajv')
|
|
7
7
|
const Schema = require('fluent-json-schema')
|
|
8
8
|
const { waitForCb } = require('./toolkit')
|
|
9
|
+
const { kRequestContentType } = require('../lib/symbols')
|
|
9
10
|
|
|
10
11
|
const customSchemaCompilers = {
|
|
11
12
|
body: new AJV({
|
|
@@ -1374,6 +1375,7 @@ test('Schema validation when no content type is provided', async t => {
|
|
|
1374
1375
|
},
|
|
1375
1376
|
preValidation: async (request) => {
|
|
1376
1377
|
request.headers['content-type'] = undefined
|
|
1378
|
+
request[kRequestContentType] = undefined
|
|
1377
1379
|
}
|
|
1378
1380
|
}, async () => 'ok')
|
|
1379
1381
|
|
|
@@ -1460,6 +1462,27 @@ test('Schema validation will not be bypass by different content type', async t =
|
|
|
1460
1462
|
t.assert.strictEqual(found.status, 400)
|
|
1461
1463
|
t.assert.strictEqual((await found.json()).code, 'FST_ERR_VALIDATION')
|
|
1462
1464
|
|
|
1465
|
+
let injected = await fastify.inject({
|
|
1466
|
+
method: 'POST',
|
|
1467
|
+
url: '/',
|
|
1468
|
+
headers: {
|
|
1469
|
+
'content-type': ' application/json'
|
|
1470
|
+
},
|
|
1471
|
+
payload: JSON.stringify({ invalid: 'string' })
|
|
1472
|
+
})
|
|
1473
|
+
t.assert.strictEqual(injected.statusCode, 400)
|
|
1474
|
+
t.assert.strictEqual(injected.json().code, 'FST_ERR_VALIDATION')
|
|
1475
|
+
|
|
1476
|
+
injected = await fastify.inject({
|
|
1477
|
+
method: 'POST',
|
|
1478
|
+
url: '/',
|
|
1479
|
+
headers: {
|
|
1480
|
+
'content-type': ' application/json'
|
|
1481
|
+
},
|
|
1482
|
+
payload: JSON.stringify({ foo: 'string' })
|
|
1483
|
+
})
|
|
1484
|
+
t.assert.strictEqual(injected.statusCode, 200)
|
|
1485
|
+
|
|
1463
1486
|
found = await fetch(address, {
|
|
1464
1487
|
method: 'POST',
|
|
1465
1488
|
url: '/',
|
|
@@ -1570,3 +1593,25 @@ test('Schema validation will not be bypass by different content type', async t =
|
|
|
1570
1593
|
t.assert.strictEqual(found.status, 415)
|
|
1571
1594
|
t.assert.strictEqual((await found.json()).code, 'FST_ERR_CTP_INVALID_MEDIA_TYPE')
|
|
1572
1595
|
})
|
|
1596
|
+
test('coercion of empty string to null with nullable types', async t => {
|
|
1597
|
+
const assert = require('node:assert')
|
|
1598
|
+
const fastify = Fastify()
|
|
1599
|
+
fastify.get('/', {
|
|
1600
|
+
schema: {
|
|
1601
|
+
querystring: {
|
|
1602
|
+
type: 'object',
|
|
1603
|
+
properties: {
|
|
1604
|
+
param: { type: ['integer', 'null'] }
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}, async (req, reply) => {
|
|
1609
|
+
return { param: req.query.param }
|
|
1610
|
+
})
|
|
1611
|
+
|
|
1612
|
+
const res = await fastify.inject({
|
|
1613
|
+
method: 'GET',
|
|
1614
|
+
url: '/?param='
|
|
1615
|
+
})
|
|
1616
|
+
assert.strictEqual(JSON.parse(res.payload).param, null)
|
|
1617
|
+
})
|
|
@@ -127,7 +127,6 @@ Some description [inline link](https://github.com/a/b).
|
|
|
127
127
|
describe('checkGitHubRepo', () => {
|
|
128
128
|
let originalDispatcher
|
|
129
129
|
let mockAgent
|
|
130
|
-
let originalFetch
|
|
131
130
|
let originalSetTimeout
|
|
132
131
|
|
|
133
132
|
beforeEach(() => {
|
|
@@ -136,12 +135,10 @@ describe('checkGitHubRepo', () => {
|
|
|
136
135
|
mockAgent = new MockAgent()
|
|
137
136
|
mockAgent.disableNetConnect()
|
|
138
137
|
setGlobalDispatcher(mockAgent)
|
|
139
|
-
originalFetch = global.fetch
|
|
140
138
|
originalSetTimeout = global.setTimeout
|
|
141
139
|
})
|
|
142
140
|
|
|
143
141
|
afterEach(async () => {
|
|
144
|
-
global.fetch = originalFetch
|
|
145
142
|
global.setTimeout = originalSetTimeout
|
|
146
143
|
setGlobalDispatcher(originalDispatcher)
|
|
147
144
|
await mockAgent.close()
|
|
@@ -180,16 +177,9 @@ describe('checkGitHubRepo', () => {
|
|
|
180
177
|
|
|
181
178
|
it('returns invalid status for malformed owner or repository names', async () => {
|
|
182
179
|
const { checkGitHubRepo } = loadValidateEcosystemLinksModule()
|
|
183
|
-
let called = false
|
|
184
|
-
|
|
185
|
-
global.fetch = async () => {
|
|
186
|
-
called = true
|
|
187
|
-
return { status: 200 }
|
|
188
|
-
}
|
|
189
180
|
|
|
190
181
|
const result = await checkGitHubRepo('owner/evil', 'repo', 1)
|
|
191
182
|
|
|
192
|
-
assert.strictEqual(called, false)
|
|
193
183
|
assert.strictEqual(result.exists, false)
|
|
194
184
|
assert.strictEqual(result.status, 'invalid')
|
|
195
185
|
assert.strictEqual(result.error, 'Invalid GitHub repository identifier')
|
|
@@ -199,17 +189,17 @@ describe('checkGitHubRepo', () => {
|
|
|
199
189
|
const { checkGitHubRepo } = loadValidateEcosystemLinksModule()
|
|
200
190
|
let attempts = 0
|
|
201
191
|
|
|
202
|
-
global.setTimeout = (fn) => {
|
|
203
|
-
fn()
|
|
204
|
-
return 0
|
|
205
|
-
}
|
|
192
|
+
global.setTimeout = (fn) => { fn(); return 0 }
|
|
206
193
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
194
|
+
const mockPool = mockAgent.get('https://api.github.com')
|
|
195
|
+
mockPool.intercept({
|
|
196
|
+
path: '/repos/owner/repo',
|
|
197
|
+
method: 'HEAD'
|
|
198
|
+
}).reply(() => { attempts++; return { statusCode: 403 } })
|
|
199
|
+
mockPool.intercept({
|
|
200
|
+
path: '/repos/owner/repo',
|
|
201
|
+
method: 'HEAD'
|
|
202
|
+
}).reply(() => { attempts++; return { statusCode: 200 } })
|
|
213
203
|
|
|
214
204
|
const result = await checkGitHubRepo('owner', 'repo', 1)
|
|
215
205
|
|
|
@@ -221,23 +211,25 @@ describe('checkGitHubRepo', () => {
|
|
|
221
211
|
it('adds authorization header when GITHUB_TOKEN is set', async () => {
|
|
222
212
|
process.env.GITHUB_TOKEN = 'my-token'
|
|
223
213
|
const { checkGitHubRepo } = loadValidateEcosystemLinksModule()
|
|
224
|
-
let authorization
|
|
225
214
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
215
|
+
const mockPool = mockAgent.get('https://api.github.com')
|
|
216
|
+
mockPool.intercept({
|
|
217
|
+
path: '/repos/owner/repo',
|
|
218
|
+
method: 'HEAD',
|
|
219
|
+
headers: {
|
|
220
|
+
authorization: 'token my-token'
|
|
230
221
|
}
|
|
231
|
-
}
|
|
222
|
+
}).reply(200)
|
|
232
223
|
|
|
233
224
|
const result = await checkGitHubRepo('owner', 'repo')
|
|
234
225
|
|
|
235
|
-
assert.strictEqual(authorization, 'token my-token')
|
|
236
226
|
assert.strictEqual(result.exists, true)
|
|
227
|
+
assert.strictEqual(result.status, 200)
|
|
237
228
|
})
|
|
238
229
|
|
|
239
230
|
it('handles network errors', async () => {
|
|
240
231
|
const { checkGitHubRepo } = loadValidateEcosystemLinksModule()
|
|
232
|
+
|
|
241
233
|
const mockPool = mockAgent.get('https://api.github.com')
|
|
242
234
|
mockPool.intercept({
|
|
243
235
|
path: '/repos/owner/repo',
|
|
@@ -254,37 +246,41 @@ describe('checkGitHubRepo', () => {
|
|
|
254
246
|
|
|
255
247
|
describe('validateAllLinks', () => {
|
|
256
248
|
let originalReadFileSync
|
|
257
|
-
let originalFetch
|
|
258
249
|
let originalSetTimeout
|
|
259
250
|
let originalConsoleLog
|
|
260
251
|
let originalStdoutWrite
|
|
252
|
+
let mockAgent
|
|
253
|
+
let originalDispatcher
|
|
261
254
|
|
|
262
255
|
beforeEach(() => {
|
|
263
256
|
originalReadFileSync = fs.readFileSync
|
|
264
|
-
originalFetch = global.fetch
|
|
265
257
|
originalSetTimeout = global.setTimeout
|
|
266
258
|
originalConsoleLog = console.log
|
|
267
259
|
originalStdoutWrite = process.stdout.write
|
|
260
|
+
originalDispatcher = getGlobalDispatcher()
|
|
261
|
+
|
|
262
|
+
mockAgent = new MockAgent()
|
|
263
|
+
mockAgent.disableNetConnect()
|
|
264
|
+
setGlobalDispatcher(mockAgent)
|
|
268
265
|
|
|
269
266
|
console.log = () => {}
|
|
270
267
|
process.stdout.write = () => true
|
|
271
268
|
|
|
272
|
-
global.setTimeout = (fn) => {
|
|
273
|
-
fn()
|
|
274
|
-
return 0
|
|
275
|
-
}
|
|
269
|
+
global.setTimeout = (fn) => { fn(); return 0 }
|
|
276
270
|
})
|
|
277
271
|
|
|
278
|
-
afterEach(() => {
|
|
272
|
+
afterEach(async () => {
|
|
279
273
|
fs.readFileSync = originalReadFileSync
|
|
280
|
-
global.fetch = originalFetch
|
|
281
274
|
global.setTimeout = originalSetTimeout
|
|
282
275
|
console.log = originalConsoleLog
|
|
283
276
|
process.stdout.write = originalStdoutWrite
|
|
277
|
+
setGlobalDispatcher(originalDispatcher)
|
|
278
|
+
await mockAgent.close()
|
|
284
279
|
})
|
|
285
280
|
|
|
286
281
|
it('validates links, deduplicates repositories and groups inaccessible links', async () => {
|
|
287
282
|
const { validateAllLinks } = loadValidateEcosystemLinksModule()
|
|
283
|
+
let requests = 0
|
|
288
284
|
|
|
289
285
|
fs.readFileSync = () => `
|
|
290
286
|
- [repo one](https://github.com/owner/repo)
|
|
@@ -292,21 +288,15 @@ describe('validateAllLinks', () => {
|
|
|
292
288
|
- [repo two](https://github.com/another/project)
|
|
293
289
|
`
|
|
294
290
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (pathname === '/repos/another/project') {
|
|
305
|
-
return { status: 200 }
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
throw new Error(`Unexpected url: ${url}`)
|
|
309
|
-
}
|
|
291
|
+
const mockPool = mockAgent.get('https://api.github.com')
|
|
292
|
+
mockPool.intercept({
|
|
293
|
+
path: '/repos/owner/repo',
|
|
294
|
+
method: 'HEAD'
|
|
295
|
+
}).reply(() => { requests++; return { statusCode: 404 } })
|
|
296
|
+
mockPool.intercept({
|
|
297
|
+
path: '/repos/another/project',
|
|
298
|
+
method: 'HEAD'
|
|
299
|
+
}).reply(() => { requests++; return { statusCode: 200 } })
|
|
310
300
|
|
|
311
301
|
const result = await validateAllLinks()
|
|
312
302
|
|
|
@@ -324,15 +314,8 @@ describe('validateAllLinks', () => {
|
|
|
324
314
|
|
|
325
315
|
fs.readFileSync = () => '# Ecosystem\nNo links here.'
|
|
326
316
|
|
|
327
|
-
let requests = 0
|
|
328
|
-
global.fetch = async () => {
|
|
329
|
-
requests++
|
|
330
|
-
return { status: 200 }
|
|
331
|
-
}
|
|
332
|
-
|
|
333
317
|
const result = await validateAllLinks()
|
|
334
318
|
|
|
335
|
-
assert.strictEqual(requests, 0)
|
|
336
319
|
assert.strictEqual(result.notFound.length, 0)
|
|
337
320
|
assert.strictEqual(result.found.length, 0)
|
|
338
321
|
})
|
package/test/throw.test.js
CHANGED
|
@@ -28,6 +28,20 @@ test('Fastify should throw on multiple assignment to the same route', (t) => {
|
|
|
28
28
|
}
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
+
test('Fastify should throw on multiple assignment to the same route with array method', (t) => {
|
|
32
|
+
t.plan(1)
|
|
33
|
+
const fastify = Fastify()
|
|
34
|
+
|
|
35
|
+
fastify.route({ method: ['GET', 'POST'], url: '/', handler: () => {} })
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
fastify.route({ method: ['GET', 'POST'], url: '/', handler: () => {} })
|
|
39
|
+
t.assert.fail('Should throw fastify duplicated route declaration')
|
|
40
|
+
} catch (error) {
|
|
41
|
+
t.assert.strictEqual(error.code, 'FST_ERR_DUPLICATED_ROUTE')
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
31
45
|
test('Fastify should throw for an invalid schema, printing the error route - headers', async (t) => {
|
|
32
46
|
t.plan(1)
|
|
33
47
|
|
package/test/trust-proxy.test.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
const { test, before } = require('node:test')
|
|
4
4
|
const fastify = require('..')
|
|
5
5
|
const helper = require('./helper')
|
|
6
|
+
const Request = require('../lib/request')
|
|
7
|
+
const buildRequest = Request.buildRequest
|
|
6
8
|
|
|
7
9
|
const fetchForwardedRequest = async (fastifyServer, forHeader, path, protoHeader) => {
|
|
8
10
|
const headers = {
|
|
@@ -219,3 +221,75 @@ test('trust proxy reads forwarded headers from trusted connections', async t =>
|
|
|
219
221
|
}
|
|
220
222
|
})
|
|
221
223
|
})
|
|
224
|
+
|
|
225
|
+
test('trust proxy with number and undefined socket remoteAddress', t => {
|
|
226
|
+
t.plan(3)
|
|
227
|
+
|
|
228
|
+
// Test case for issue #6606: trustProxy: 1 with undefined/null socket.remoteAddress
|
|
229
|
+
// This simulates IISNode on Windows where socket.remoteAddress may be undefined
|
|
230
|
+
const headers = {
|
|
231
|
+
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
232
|
+
'x-forwarded-host': 'fastify.test',
|
|
233
|
+
'x-forwarded-proto': 'https'
|
|
234
|
+
}
|
|
235
|
+
// socket must exist but remoteAddress can be undefined
|
|
236
|
+
// This is what happens in IISNode with enableXFF="true"
|
|
237
|
+
const req = {
|
|
238
|
+
method: 'GET',
|
|
239
|
+
url: '/',
|
|
240
|
+
socket: { remoteAddress: undefined },
|
|
241
|
+
headers
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const TpRequest = buildRequest(Request, 1)
|
|
245
|
+
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
246
|
+
// Even with undefined socket.remoteAddress, req.ip should be populated from X-Forwarded-For
|
|
247
|
+
t.assert.ok(request.ip, 'ip is defined')
|
|
248
|
+
// With trustProxy: 1, we trust 1 hop from socket. Since socket.remoteAddress is undefined,
|
|
249
|
+
// the hop count check should skip it and we get 1.1.1.1 (the first trusted address from X-Forwarded-For)
|
|
250
|
+
t.assert.strictEqual(request.ip, '1.1.1.1', 'gets ip from x-forwarded-for')
|
|
251
|
+
// The host should also work correctly
|
|
252
|
+
t.assert.strictEqual(request.host, 'fastify.test', 'gets host from x-forwarded-host')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test('trust proxy with number and null socket remoteAddress', t => {
|
|
256
|
+
t.plan(2)
|
|
257
|
+
|
|
258
|
+
// Test case for trustProxy: 1 with null socket.remoteAddress
|
|
259
|
+
const headers = {
|
|
260
|
+
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
261
|
+
'x-forwarded-host': 'fastify.test'
|
|
262
|
+
}
|
|
263
|
+
const req = {
|
|
264
|
+
method: 'GET',
|
|
265
|
+
url: '/',
|
|
266
|
+
socket: { remoteAddress: null },
|
|
267
|
+
headers
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const TpRequest = buildRequest(Request, 1)
|
|
271
|
+
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
272
|
+
t.assert.ok(request.ip, 'ip is defined')
|
|
273
|
+
t.assert.strictEqual(request.ip, '1.1.1.1', 'gets ip from x-forwarded-for')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
test('trust proxy does not trust x-forwarded-host/proto when socket is null', t => {
|
|
277
|
+
t.plan(2)
|
|
278
|
+
|
|
279
|
+
const headers = {
|
|
280
|
+
host: 'real.test',
|
|
281
|
+
'x-forwarded-host': 'spoofed.test',
|
|
282
|
+
'x-forwarded-proto': 'https'
|
|
283
|
+
}
|
|
284
|
+
const req = {
|
|
285
|
+
method: 'GET',
|
|
286
|
+
url: '/',
|
|
287
|
+
socket: null,
|
|
288
|
+
headers
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const TpRequest = buildRequest(Request, true)
|
|
292
|
+
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
293
|
+
t.assert.strictEqual(request.host, 'real.test', 'falls back to host header')
|
|
294
|
+
t.assert.strictEqual(request.protocol, undefined, 'does not trust x-forwarded-proto')
|
|
295
|
+
})
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { IncomingMessage } from 'node:http'
|
|
2
|
+
import { expect } from 'tstyche'
|
|
3
|
+
import fastify, { FastifyBodyParser } from '../../fastify.js'
|
|
4
|
+
import { FastifyRequest } from '../../types/request.js'
|
|
5
|
+
|
|
6
|
+
expect(fastify().addContentTypeParser('contentType', function (request, payload, done) {
|
|
7
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
8
|
+
expect(payload).type.toBe<IncomingMessage>()
|
|
9
|
+
done(null)
|
|
10
|
+
})).type.toBe<void>()
|
|
11
|
+
|
|
12
|
+
// Body limit options
|
|
13
|
+
|
|
14
|
+
expect(fastify().addContentTypeParser('contentType', { bodyLimit: 99 }, function (request, payload, done) {
|
|
15
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
16
|
+
expect(payload).type.toBe<IncomingMessage>()
|
|
17
|
+
done(null)
|
|
18
|
+
})).type.toBe<void>()
|
|
19
|
+
|
|
20
|
+
// Array for contentType
|
|
21
|
+
|
|
22
|
+
expect(fastify().addContentTypeParser(['contentType'], function (request, payload, done) {
|
|
23
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
24
|
+
expect(payload).type.toBe<IncomingMessage>()
|
|
25
|
+
done(null)
|
|
26
|
+
})).type.toBe<void>()
|
|
27
|
+
|
|
28
|
+
// Body Parser - the generic after addContentTypeParser enforces the type of the `body` parameter as well as the value of the `parseAs` property
|
|
29
|
+
|
|
30
|
+
expect(fastify().addContentTypeParser<string>('bodyContentType', { parseAs: 'string' }, function (request, body, done) {
|
|
31
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
32
|
+
expect(body).type.toBe<string>()
|
|
33
|
+
done(null)
|
|
34
|
+
})).type.toBe<void>()
|
|
35
|
+
|
|
36
|
+
expect(fastify().addContentTypeParser<Buffer>('bodyContentType', { parseAs: 'buffer' }, function (request, body, done) {
|
|
37
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
38
|
+
expect(body).type.toBe<Buffer>()
|
|
39
|
+
done(null)
|
|
40
|
+
})).type.toBe<void>()
|
|
41
|
+
|
|
42
|
+
expect(fastify().addContentTypeParser('contentType', async function (request: FastifyRequest, payload: IncomingMessage) {
|
|
43
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
44
|
+
expect(payload).type.toBe<IncomingMessage>()
|
|
45
|
+
return null
|
|
46
|
+
})).type.toBe<void>()
|
|
47
|
+
|
|
48
|
+
expect(fastify().addContentTypeParser<string>('bodyContentType', { parseAs: 'string' }, async function (request: FastifyRequest, body: string) {
|
|
49
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
50
|
+
expect(body).type.toBe<string>()
|
|
51
|
+
return null
|
|
52
|
+
})).type.toBe<void>()
|
|
53
|
+
|
|
54
|
+
expect(fastify().addContentTypeParser<Buffer>('bodyContentType', { parseAs: 'buffer' }, async function (request: FastifyRequest, body: Buffer) {
|
|
55
|
+
expect(request).type.toBe<FastifyRequest>()
|
|
56
|
+
expect(body).type.toBe<Buffer>()
|
|
57
|
+
return null
|
|
58
|
+
})).type.toBe<void>()
|
|
59
|
+
|
|
60
|
+
expect(fastify().getDefaultJsonParser('error', 'ignore')).type.toBe<FastifyBodyParser<string>>()
|
|
61
|
+
expect(fastify().getDefaultJsonParser).type.not.toBeCallableWith('error', 'skip')
|
|
62
|
+
expect(fastify().getDefaultJsonParser).type.not.toBeCallableWith('nothing', 'ignore')
|
|
63
|
+
|
|
64
|
+
expect(fastify().removeAllContentTypeParsers()).type.toBe<void>()
|
|
65
|
+
expect(fastify().removeAllContentTypeParsers).type.not.toBeCallableWith('contentType')
|
|
66
|
+
|
|
67
|
+
expect(fastify().removeContentTypeParser('contentType')).type.toBe<void>()
|
|
68
|
+
expect(fastify().removeContentTypeParser(/contentType+.*/)).type.toBe<void>()
|
|
69
|
+
expect(fastify().removeContentTypeParser(['contentType', /contentType+.*/])).type.toBe<void>()
|
|
70
|
+
expect(fastify().removeContentTypeParser).type.not.toBeCallableWith({})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { expect } from 'tstyche'
|
|
2
|
+
import fastify from '../../fastify.js'
|
|
3
3
|
|
|
4
4
|
type TestType = void
|
|
5
5
|
|
|
@@ -13,6 +13,6 @@ declare module '../../fastify' {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
fastify().get('/', (req, res) => {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
expect(req.testProp).type.toBe<TestType>()
|
|
17
|
+
expect(res.testProp).type.toBe<TestType>()
|
|
18
18
|
})
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { FastifyErrorConstructor } from '@fastify/error'
|
|
2
|
+
import { expect } from 'tstyche'
|
|
3
|
+
import { errorCodes } from '../../fastify.js'
|
|
4
|
+
|
|
5
|
+
expect(errorCodes.FST_ERR_NOT_FOUND).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
6
|
+
expect(errorCodes.FST_ERR_OPTIONS_NOT_OBJ).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
7
|
+
expect(errorCodes.FST_ERR_QSP_NOT_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
8
|
+
expect(errorCodes.FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
9
|
+
expect(errorCodes.FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
10
|
+
expect(errorCodes.FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
11
|
+
expect(errorCodes.FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
12
|
+
expect(errorCodes.FST_ERR_VALIDATION).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
13
|
+
expect(errorCodes.FST_ERR_LISTEN_OPTIONS_INVALID).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
14
|
+
expect(errorCodes.FST_ERR_ERROR_HANDLER_NOT_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
15
|
+
expect(errorCodes.FST_ERR_ERROR_HANDLER_ALREADY_SET).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
16
|
+
expect(errorCodes.FST_ERR_CTP_ALREADY_PRESENT).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
17
|
+
expect(errorCodes.FST_ERR_CTP_INVALID_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
18
|
+
expect(errorCodes.FST_ERR_CTP_EMPTY_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
19
|
+
expect(errorCodes.FST_ERR_CTP_INVALID_HANDLER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
20
|
+
expect(errorCodes.FST_ERR_CTP_INVALID_PARSE_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
21
|
+
expect(errorCodes.FST_ERR_CTP_BODY_TOO_LARGE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
22
|
+
expect(errorCodes.FST_ERR_CTP_INVALID_MEDIA_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
23
|
+
expect(errorCodes.FST_ERR_CTP_INVALID_CONTENT_LENGTH).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
24
|
+
expect(errorCodes.FST_ERR_CTP_EMPTY_JSON_BODY).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
25
|
+
expect(errorCodes.FST_ERR_CTP_INVALID_JSON_BODY).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
26
|
+
expect(errorCodes.FST_ERR_CTP_INSTANCE_ALREADY_STARTED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
27
|
+
expect(errorCodes.FST_ERR_DEC_ALREADY_PRESENT).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
28
|
+
expect(errorCodes.FST_ERR_DEC_DEPENDENCY_INVALID_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
29
|
+
expect(errorCodes.FST_ERR_DEC_MISSING_DEPENDENCY).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
30
|
+
expect(errorCodes.FST_ERR_DEC_AFTER_START).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
31
|
+
expect(errorCodes.FST_ERR_DEC_REFERENCE_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
32
|
+
expect(errorCodes.FST_ERR_DEC_UNDECLARED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
33
|
+
expect(errorCodes.FST_ERR_HOOK_INVALID_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
34
|
+
expect(errorCodes.FST_ERR_HOOK_INVALID_HANDLER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
35
|
+
expect(errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
36
|
+
expect(errorCodes.FST_ERR_HOOK_NOT_SUPPORTED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
37
|
+
expect(errorCodes.FST_ERR_MISSING_MIDDLEWARE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
38
|
+
expect(errorCodes.FST_ERR_HOOK_TIMEOUT).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
39
|
+
expect(errorCodes.FST_ERR_LOG_INVALID_DESTINATION).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
40
|
+
expect(errorCodes.FST_ERR_LOG_INVALID_LOGGER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
41
|
+
expect(errorCodes.FST_ERR_LOG_INVALID_LOGGER_INSTANCE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
42
|
+
expect(errorCodes.FST_ERR_LOG_INVALID_LOGGER_CONFIG).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
43
|
+
expect(errorCodes.FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
44
|
+
expect(errorCodes.FST_ERR_REP_INVALID_PAYLOAD_TYPE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
45
|
+
expect(errorCodes.FST_ERR_REP_RESPONSE_BODY_CONSUMED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
46
|
+
expect(errorCodes.FST_ERR_REP_READABLE_STREAM_LOCKED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
47
|
+
expect(errorCodes.FST_ERR_REP_ALREADY_SENT).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
48
|
+
expect(errorCodes.FST_ERR_REP_SENT_VALUE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
49
|
+
expect(errorCodes.FST_ERR_SEND_INSIDE_ONERR).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
50
|
+
expect(errorCodes.FST_ERR_SEND_UNDEFINED_ERR).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
51
|
+
expect(errorCodes.FST_ERR_BAD_STATUS_CODE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
52
|
+
expect(errorCodes.FST_ERR_BAD_TRAILER_NAME).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
53
|
+
expect(errorCodes.FST_ERR_BAD_TRAILER_VALUE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
54
|
+
expect(errorCodes.FST_ERR_FAILED_ERROR_SERIALIZATION).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
55
|
+
expect(errorCodes.FST_ERR_MISSING_SERIALIZATION_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
56
|
+
expect(errorCodes.FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
57
|
+
expect(errorCodes.FST_ERR_REQ_INVALID_VALIDATION_INVOCATION).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
58
|
+
expect(errorCodes.FST_ERR_SCH_MISSING_ID).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
59
|
+
expect(errorCodes.FST_ERR_SCH_ALREADY_PRESENT).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
60
|
+
expect(errorCodes.FST_ERR_SCH_CONTENT_MISSING_SCHEMA).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
61
|
+
expect(errorCodes.FST_ERR_SCH_DUPLICATE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
62
|
+
expect(errorCodes.FST_ERR_SCH_VALIDATION_BUILD).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
63
|
+
expect(errorCodes.FST_ERR_SCH_SERIALIZATION_BUILD).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
64
|
+
expect(errorCodes.FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
65
|
+
expect(errorCodes.FST_ERR_INIT_OPTS_INVALID).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
66
|
+
expect(errorCodes.FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
67
|
+
expect(errorCodes.FST_ERR_DUPLICATED_ROUTE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
68
|
+
expect(errorCodes.FST_ERR_BAD_URL).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
69
|
+
expect(errorCodes.FST_ERR_ASYNC_CONSTRAINT).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
70
|
+
expect(errorCodes.FST_ERR_INVALID_URL).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
71
|
+
expect(errorCodes.FST_ERR_ROUTE_OPTIONS_NOT_OBJ).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
72
|
+
expect(errorCodes.FST_ERR_ROUTE_DUPLICATED_HANDLER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
73
|
+
expect(errorCodes.FST_ERR_ROUTE_HANDLER_NOT_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
74
|
+
expect(errorCodes.FST_ERR_ROUTE_MISSING_HANDLER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
75
|
+
expect(errorCodes.FST_ERR_ROUTE_METHOD_INVALID).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
76
|
+
expect(errorCodes.FST_ERR_ROUTE_METHOD_NOT_SUPPORTED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
77
|
+
expect(errorCodes.FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
78
|
+
expect(errorCodes.FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
79
|
+
expect(errorCodes.FST_ERR_ROUTE_REWRITE_NOT_STR).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
80
|
+
expect(errorCodes.FST_ERR_REOPENED_CLOSE_SERVER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
81
|
+
expect(errorCodes.FST_ERR_REOPENED_SERVER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
82
|
+
expect(errorCodes.FST_ERR_INSTANCE_ALREADY_LISTENING).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
83
|
+
expect(errorCodes.FST_ERR_PLUGIN_VERSION_MISMATCH).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
84
|
+
expect(errorCodes.FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
85
|
+
expect(errorCodes.FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
86
|
+
expect(errorCodes.FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
87
|
+
expect(errorCodes.FST_ERR_PLUGIN_CALLBACK_NOT_FN).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
88
|
+
expect(errorCodes.FST_ERR_PLUGIN_NOT_VALID).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
89
|
+
expect(errorCodes.FST_ERR_ROOT_PLG_BOOTED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
90
|
+
expect(errorCodes.FST_ERR_PARENT_PLUGIN_BOOTED).type.toBeAssignableTo<FastifyErrorConstructor>()
|
|
91
|
+
expect(errorCodes.FST_ERR_PLUGIN_TIMEOUT).type.toBeAssignableTo<FastifyErrorConstructor>()
|