fastify 5.5.0 → 5.6.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/SECURITY.md +12 -0
- package/SPONSORS.md +1 -0
- package/docs/Reference/Decorators.md +36 -169
- package/docs/Reference/Reply.md +1 -1
- package/docs/Reference/Routes.md +1 -2
- package/docs/Reference/Server.md +7 -1
- package/docs/Reference/TypeScript.md +138 -2
- package/fastify.d.ts +17 -0
- package/fastify.js +6 -1
- package/lib/server.js +56 -3
- package/lib/symbols.js +1 -0
- package/lib/warnings.js +1 -1
- package/package.json +1 -1
- package/test/decorator-namespace.test._js_ +1 -1
- package/test/http2/closing.test.js +88 -0
- package/test/set-error-handler.test.js +1 -1
- package/test/skip-reply-send.test.js +2 -2
- package/test/types/instance.test-d.ts +18 -1
- package/test/types/schema.test-d.ts +21 -0
- package/types/instance.d.ts +2 -0
- package/types/logger.d.ts +16 -14
- package/types/schema.d.ts +1 -1
package/SECURITY.md
CHANGED
|
@@ -110,6 +110,18 @@ Within HackerOne, this is handled through a "public disclosure request".
|
|
|
110
110
|
Reference: [HackerOne:
|
|
111
111
|
Disclosure](https://docs.hackerone.com/hackers/disclosure.html)
|
|
112
112
|
|
|
113
|
+
### Secondary Contact
|
|
114
|
+
|
|
115
|
+
If you do not receive an acknowledgment of your report within 6 business days,
|
|
116
|
+
or if you cannot find a private security contact for the project, you may
|
|
117
|
+
contact the OpenJS Foundation CNA at `security@lists.openjsf.org` for
|
|
118
|
+
assistance.
|
|
119
|
+
|
|
120
|
+
The CNA can help ensure your report is properly acknowledged, assist with
|
|
121
|
+
coordinating disclosure timelines, and assign CVEs when necessary. This is a
|
|
122
|
+
support mechanism to ensure security reports are handled appropriately across
|
|
123
|
+
all OpenJS Foundation projects.
|
|
124
|
+
|
|
113
125
|
## The Fastify Security team
|
|
114
126
|
|
|
115
127
|
The core team is responsible for the management of the security program and
|
package/SPONSORS.md
CHANGED
|
@@ -17,6 +17,7 @@ _Be the first!_
|
|
|
17
17
|
- [Val Town, Inc.](https://opencollective.com/valtown)
|
|
18
18
|
- [Handsontable - JavaScript Data Grid](https://handsontable.com/docs/react-data-grid/?utm_source=Fastify_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024)
|
|
19
19
|
- [Lokalise - A Localization and Translation Software Tool](https://lokalise.com/?utm_source=Fastify_GH&utm_medium=sponsorship)
|
|
20
|
+
- [Lambdatest](https://www.lambdatest.com/)
|
|
20
21
|
|
|
21
22
|
## Tier 2
|
|
22
23
|
|
|
@@ -366,201 +366,68 @@ Will define the `foo` property on the Fastify instance:
|
|
|
366
366
|
console.log(fastify.foo) // 'a getter'
|
|
367
367
|
```
|
|
368
368
|
|
|
369
|
-
|
|
369
|
+
#### `getDecorator(name)`
|
|
370
|
+
<a id="get-decorator"></a>
|
|
370
371
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
`FST_ERR_DEC_UNDECLARED` error is thrown.
|
|
372
|
+
Used to retrieve an existing decorator from the Fastify instance, `Request`, or `Reply`.
|
|
373
|
+
If the decorator is not defined, an `FST_ERR_DEC_UNDECLARED` error is thrown.
|
|
374
374
|
|
|
375
|
-
|
|
375
|
+
```js
|
|
376
|
+
// Get a decorator from the Fastify instance
|
|
377
|
+
const utility = fastify.getDecorator('utility')
|
|
376
378
|
|
|
377
|
-
|
|
379
|
+
// Get a decorator from the request object
|
|
380
|
+
const user = request.getDecorator('user')
|
|
378
381
|
|
|
379
|
-
|
|
380
|
-
|
|
382
|
+
// Get a decorator from the reply object
|
|
383
|
+
const helper = reply.getDecorator('helper')
|
|
384
|
+
```
|
|
381
385
|
|
|
382
|
-
|
|
386
|
+
The `getDecorator` method is useful for dependency validation - it can be used to
|
|
387
|
+
check for required decorators at registration time. If any are missing, it fails
|
|
388
|
+
at boot, ensuring dependencies are available during the request lifecycle.
|
|
383
389
|
|
|
384
390
|
```js
|
|
385
391
|
fastify.register(async function (fastify) {
|
|
392
|
+
// Verify the decorator exists before using it
|
|
386
393
|
const usersRepository = fastify.getDecorator('usersRepository')
|
|
387
394
|
|
|
388
395
|
fastify.get('/users', async function (request, reply) {
|
|
389
|
-
// We are sure `usersRepository` exists at runtime
|
|
390
396
|
return usersRepository.findAll()
|
|
391
397
|
})
|
|
392
398
|
})
|
|
393
399
|
```
|
|
394
400
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
```ts
|
|
400
|
-
const user = request.user;
|
|
401
|
-
if (user && user.isAdmin) {
|
|
402
|
-
// Execute admin tasks.
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
If `request.user` doesn't exist, then `user` will be set to `undefined`.
|
|
407
|
-
This makes it unclear whether the user is unauthenticated or the decorator is missing.
|
|
408
|
-
|
|
409
|
-
Using `getDecorator` enforces runtime safety:
|
|
410
|
-
|
|
411
|
-
```ts
|
|
412
|
-
// If the decorator is missing, an explicit `FST_ERR_DEC_UNDECLARED`
|
|
413
|
-
// error is thrown immediately.
|
|
414
|
-
const user = request.getDecorator('user');
|
|
415
|
-
if (user && user.isAdmin) {
|
|
416
|
-
// Execute admin tasks.
|
|
417
|
-
}
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
**Alternative to Module Augmentation**
|
|
421
|
-
|
|
422
|
-
Decorators are typically typed via module augmentation:
|
|
423
|
-
|
|
424
|
-
```ts
|
|
425
|
-
declare module 'fastify' {
|
|
426
|
-
interface FastifyInstance {
|
|
427
|
-
usersRepository: IUsersRepository
|
|
428
|
-
}
|
|
429
|
-
interface FastifyRequest {
|
|
430
|
-
session: ISession
|
|
431
|
-
}
|
|
432
|
-
interface FastifyReply {
|
|
433
|
-
sendSuccess: SendSuccessFn
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
This approach modifies the Fastify instance globally, which may lead to
|
|
439
|
-
conflicts and inconsistent behavior in multi-server setups or with plugin
|
|
440
|
-
encapsulation.
|
|
441
|
-
|
|
442
|
-
Using `getDecorator<T>` allows to limit types scope:
|
|
443
|
-
|
|
444
|
-
```ts
|
|
445
|
-
serverOne.register(async function (fastify) {
|
|
446
|
-
const usersRepository = fastify.getDecorator<PostgreUsersRepository>(
|
|
447
|
-
'usersRepository'
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
fastify.decorateRequest('session', null)
|
|
451
|
-
fastify.addHook('onRequest', async (req, reply) => {
|
|
452
|
-
// Yes, the request object has a setDecorator method.
|
|
453
|
-
// More information will be provided soon.
|
|
454
|
-
req.setDecorator('session', { user: 'Jean' })
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
fastify.get('/me', (request, reply) => {
|
|
458
|
-
const session = request.getDecorator<ISession>('session')
|
|
459
|
-
reply.send(session)
|
|
460
|
-
})
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
serverTwo.register(async function (fastify) {
|
|
464
|
-
const usersRepository = fastify.getDecorator<SqlLiteUsersRepository>(
|
|
465
|
-
'usersRepository'
|
|
466
|
-
)
|
|
467
|
-
|
|
468
|
-
fastify.decorateReply('sendSuccess', function (data) {
|
|
469
|
-
return this.send({ success: true })
|
|
470
|
-
})
|
|
471
|
-
|
|
472
|
-
fastify.get('/success', async (request, reply) => {
|
|
473
|
-
const sendSuccess = reply.getDecorator<SendSuccessFn>('sendSuccess')
|
|
474
|
-
await sendSuccess()
|
|
475
|
-
})
|
|
476
|
-
})
|
|
477
|
-
```
|
|
401
|
+
> ℹ️ Note: For TypeScript users, `getDecorator` supports generic type parameters.
|
|
402
|
+
> See the [TypeScript documentation](/docs/latest/Reference/TypeScript/) for
|
|
403
|
+
> advanced typing examples.
|
|
478
404
|
|
|
479
|
-
####
|
|
405
|
+
#### `setDecorator(name, value)`
|
|
406
|
+
<a id="set-decorator"></a>
|
|
480
407
|
|
|
481
|
-
|
|
482
|
-
|
|
408
|
+
Used to safely update the value of a `Request` decorator.
|
|
409
|
+
If the decorator does not exist, a `FST_ERR_DEC_UNDECLARED` error is thrown.
|
|
483
410
|
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
return this.send({ success: true })
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
export type SendSuccess = typeof sendSuccess
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
However, `getDecorator` returns functions with the `this`
|
|
493
|
-
context already **bound**, meaning the `this` parameter disappears
|
|
494
|
-
from the function signature.
|
|
495
|
-
|
|
496
|
-
To correctly type it, you should use `OmitThisParameter` utility:
|
|
497
|
-
|
|
498
|
-
```ts
|
|
499
|
-
function sendSuccess (this: FastifyReply) {
|
|
500
|
-
return this.send({ success: true })
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
type BoundSendSuccess = OmitThisParameter<typeof sendSuccess>
|
|
504
|
-
|
|
505
|
-
fastify.decorateReply('sendSuccess', sendSuccess)
|
|
506
|
-
fastify.get('/success', async (request, reply) => {
|
|
507
|
-
const sendSuccess = reply.getDecorator<BoundSendSuccess>('sendSuccess')
|
|
508
|
-
await sendSuccess()
|
|
509
|
-
})
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
### `Request.setDecorator<T>` Method
|
|
513
|
-
|
|
514
|
-
The `setDecorator<T>` method provides a safe and convenient way to
|
|
515
|
-
update the value of a `Request` decorator.
|
|
516
|
-
If the decorator does not exist, a `FST_ERR_DEC_UNDECLARED` error
|
|
517
|
-
is thrown.
|
|
518
|
-
|
|
519
|
-
#### Use Cases
|
|
520
|
-
|
|
521
|
-
**Runtime Safety**
|
|
522
|
-
|
|
523
|
-
A typical way to set a `Request` decorator looks like this:
|
|
524
|
-
|
|
525
|
-
```ts
|
|
526
|
-
fastify.decorateRequest('user', '')
|
|
527
|
-
fastify.addHook('preHandler', async (req, reply) => {
|
|
528
|
-
req.user = 'Bob Dylan'
|
|
529
|
-
})
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
However, there is no guarantee that the decorator actually exists
|
|
533
|
-
unless you manually check beforehand.
|
|
534
|
-
Additionally, typos are common, e.g. `account`, `acount`, or `accout`.
|
|
535
|
-
|
|
536
|
-
By using `setDecorator`, you are always sure that the decorator exists:
|
|
411
|
+
```js
|
|
412
|
+
fastify.decorateRequest('user', null)
|
|
537
413
|
|
|
538
|
-
```ts
|
|
539
|
-
fastify.decorateRequest('user', '')
|
|
540
414
|
fastify.addHook('preHandler', async (req, reply) => {
|
|
541
|
-
//
|
|
542
|
-
req.setDecorator('user
|
|
415
|
+
// Safely set the decorator value
|
|
416
|
+
req.setDecorator('user', 'Bob Dylan')
|
|
543
417
|
})
|
|
544
418
|
```
|
|
545
419
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
**Type Safety**
|
|
420
|
+
The `setDecorator` method provides runtime safety by ensuring the decorator exists
|
|
421
|
+
before setting its value, preventing errors from typos in decorator names.
|
|
549
422
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
```ts
|
|
423
|
+
```js
|
|
424
|
+
fastify.decorateRequest('account', null)
|
|
554
425
|
fastify.addHook('preHandler', async (req, reply) => {
|
|
555
|
-
|
|
426
|
+
// This will throw FST_ERR_DEC_UNDECLARED due to typo in decorator name
|
|
427
|
+
req.setDecorator('acount', { id: 123 })
|
|
556
428
|
})
|
|
557
429
|
```
|
|
558
430
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
```ts
|
|
563
|
-
fastify.addHook('preHandler', async (req, reply) => {
|
|
564
|
-
req.setDecorator<string>('user', 'Bob Dylan')
|
|
565
|
-
})
|
|
566
|
-
```
|
|
431
|
+
> ℹ️ Note: For TypeScript users, see the
|
|
432
|
+
> [TypeScript documentation](/docs/latest/Reference/TypeScript/) for advanced
|
|
433
|
+
> typing examples using `setDecorator<T>`.
|
package/docs/Reference/Reply.md
CHANGED
|
@@ -369,7 +369,7 @@ charset must be set explicitly.
|
|
|
369
369
|
<a id="getserializationfunction"></a>
|
|
370
370
|
|
|
371
371
|
By calling this function using a provided `schema` or `httpStatus`,
|
|
372
|
-
and the optional `contentType`, it will return a `
|
|
372
|
+
and the optional `contentType`, it will return a `serialization` function
|
|
373
373
|
that can be used to serialize diverse inputs. It returns `undefined` if no
|
|
374
374
|
serialization function was found using either of the provided inputs.
|
|
375
375
|
|
package/docs/Reference/Routes.md
CHANGED
|
@@ -636,8 +636,7 @@ has a version set, and will prefer a versioned route to a non-versioned route
|
|
|
636
636
|
for the same path. Advanced version ranges and pre-releases currently are not
|
|
637
637
|
supported.
|
|
638
638
|
|
|
639
|
-
|
|
640
|
-
performances of the router.*
|
|
639
|
+
> **Note:** using this feature can degrade the router’s performance.
|
|
641
640
|
|
|
642
641
|
```js
|
|
643
642
|
fastify.route({
|
package/docs/Reference/Server.md
CHANGED
|
@@ -170,6 +170,12 @@ When set to `true`, upon [`close`](#close) the server will iterate the current
|
|
|
170
170
|
persistent connections and [destroy their
|
|
171
171
|
sockets](https://nodejs.org/dist/latest-v16.x/docs/api/net.html#socketdestroyerror).
|
|
172
172
|
|
|
173
|
+
When used with HTTP/2 server, it will also close all active HTTP/2 sessions.
|
|
174
|
+
|
|
175
|
+
> ℹ️ Note:
|
|
176
|
+
> Since Node.js v24 active sessions are closed by default
|
|
177
|
+
|
|
178
|
+
|
|
173
179
|
> ⚠ Warning:
|
|
174
180
|
> Connections are not inspected to determine if requests have
|
|
175
181
|
> been completed.
|
|
@@ -966,7 +972,7 @@ Fastify uses [find-my-way](https://github.com/delvedor/find-my-way) which suppor
|
|
|
966
972
|
separating the path and query string with a `;` character (code 59), e.g. `/dev;foo=bar`.
|
|
967
973
|
This decision originated from [delvedor/find-my-way#76]
|
|
968
974
|
(https://github.com/delvedor/find-my-way/issues/76). Thus, this option will support
|
|
969
|
-
backwards
|
|
975
|
+
backwards compatibility for the need to split on `;`. To enable support for splitting
|
|
970
976
|
on `;` set `useSemicolonDelimiter` to `true`.
|
|
971
977
|
|
|
972
978
|
```js
|
|
@@ -147,7 +147,7 @@ route-level `request` object.
|
|
|
147
147
|
reply.code(200).send('uh-oh');
|
|
148
148
|
// it even works for wildcards
|
|
149
149
|
reply.code(404).send({ error: 'Not found' });
|
|
150
|
-
return
|
|
150
|
+
return { success: true }
|
|
151
151
|
})
|
|
152
152
|
```
|
|
153
153
|
|
|
@@ -173,7 +173,7 @@ route-level `request` object.
|
|
|
173
173
|
}, async (request, reply) => {
|
|
174
174
|
const customerHeader = request.headers['h-Custom']
|
|
175
175
|
// do something with request data
|
|
176
|
-
return
|
|
176
|
+
return { success: true }
|
|
177
177
|
})
|
|
178
178
|
```
|
|
179
179
|
7. Build and run and query with the `username` query string option set to
|
|
@@ -687,6 +687,142 @@ Or even explicit config on tsconfig
|
|
|
687
687
|
}
|
|
688
688
|
```
|
|
689
689
|
|
|
690
|
+
#### `getDecorator<T>`
|
|
691
|
+
|
|
692
|
+
Fastify's `getDecorator<T>` method retrieves decorators with enhanced type safety.
|
|
693
|
+
|
|
694
|
+
The `getDecorator<T>` method supports generic type parameters for enhanced type safety:
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
// Type-safe decorator retrieval
|
|
698
|
+
const usersRepository = fastify.getDecorator<IUsersRepository>('usersRepository')
|
|
699
|
+
const session = request.getDecorator<ISession>('session')
|
|
700
|
+
const sendSuccess = reply.getDecorator<SendSuccessFn>('sendSuccess')
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**Alternative to Module Augmentation**
|
|
704
|
+
|
|
705
|
+
Decorators are typically typed via module augmentation:
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
declare module 'fastify' {
|
|
709
|
+
interface FastifyInstance {
|
|
710
|
+
usersRepository: IUsersRepository
|
|
711
|
+
}
|
|
712
|
+
interface FastifyRequest {
|
|
713
|
+
session: ISession
|
|
714
|
+
}
|
|
715
|
+
interface FastifyReply {
|
|
716
|
+
sendSuccess: SendSuccessFn
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
This approach modifies the Fastify instance globally, which may lead to conflicts
|
|
722
|
+
and inconsistent behavior in multi-server setups or with plugin encapsulation.
|
|
723
|
+
|
|
724
|
+
Using `getDecorator<T>` allows limiting types scope:
|
|
725
|
+
|
|
726
|
+
```typescript
|
|
727
|
+
serverOne.register(async function (fastify) {
|
|
728
|
+
const usersRepository = fastify.getDecorator<PostgreUsersRepository>(
|
|
729
|
+
'usersRepository'
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
fastify.decorateRequest('session', null)
|
|
733
|
+
fastify.addHook('onRequest', async (req, reply) => {
|
|
734
|
+
req.setDecorator('session', { user: 'Jean' })
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
fastify.get('/me', (request, reply) => {
|
|
738
|
+
const session = request.getDecorator<ISession>('session')
|
|
739
|
+
reply.send(session)
|
|
740
|
+
})
|
|
741
|
+
})
|
|
742
|
+
|
|
743
|
+
serverTwo.register(async function (fastify) {
|
|
744
|
+
const usersRepository = fastify.getDecorator<SqlLiteUsersRepository>(
|
|
745
|
+
'usersRepository'
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
fastify.decorateReply('sendSuccess', function (data) {
|
|
749
|
+
return this.send({ success: true })
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
fastify.get('/success', async (request, reply) => {
|
|
753
|
+
const sendSuccess = reply.getDecorator<SendSuccessFn>('sendSuccess')
|
|
754
|
+
await sendSuccess()
|
|
755
|
+
})
|
|
756
|
+
})
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
**Bound Functions Inference**
|
|
760
|
+
|
|
761
|
+
To save time, it is common to infer function types instead of writing them manually:
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
function sendSuccess (this: FastifyReply) {
|
|
765
|
+
return this.send({ success: true })
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
export type SendSuccess = typeof sendSuccess
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
However, `getDecorator` returns functions with the `this` context already **bound**,
|
|
772
|
+
meaning the `this` parameter disappears from the function signature.
|
|
773
|
+
|
|
774
|
+
To correctly type it, use the `OmitThisParameter` utility:
|
|
775
|
+
|
|
776
|
+
```typescript
|
|
777
|
+
function sendSuccess (this: FastifyReply) {
|
|
778
|
+
return this.send({ success: true })
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
type BoundSendSuccess = OmitThisParameter<typeof sendSuccess>
|
|
782
|
+
|
|
783
|
+
fastify.decorateReply('sendSuccess', sendSuccess)
|
|
784
|
+
fastify.get('/success', async (request, reply) => {
|
|
785
|
+
const sendSuccess = reply.getDecorator<BoundSendSuccess>('sendSuccess')
|
|
786
|
+
await sendSuccess()
|
|
787
|
+
})
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
#### `setDecorator<T>`
|
|
791
|
+
|
|
792
|
+
Fastify's `setDecorator<T>` method provides enhanced type safety for updating request
|
|
793
|
+
decorators.
|
|
794
|
+
|
|
795
|
+
The `setDecorator<T>` method provides enhanced type safety for updating request
|
|
796
|
+
decorators:
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
fastify.decorateRequest('user', '')
|
|
800
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
801
|
+
// Type-safe decorator setting
|
|
802
|
+
req.setDecorator<string>('user', 'Bob Dylan')
|
|
803
|
+
})
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
**Type Safety Benefits**
|
|
807
|
+
|
|
808
|
+
If the `FastifyRequest` interface does not declare the decorator, type assertions
|
|
809
|
+
are typically needed:
|
|
810
|
+
|
|
811
|
+
```typescript
|
|
812
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
813
|
+
(req as typeof req & { user: string }).user = 'Bob Dylan'
|
|
814
|
+
})
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
The `setDecorator<T>` method eliminates the need for explicit type assertions
|
|
818
|
+
while providing type safety:
|
|
819
|
+
|
|
820
|
+
```typescript
|
|
821
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
822
|
+
req.setDecorator<string>('user', 'Bob Dylan')
|
|
823
|
+
})
|
|
824
|
+
```
|
|
825
|
+
|
|
690
826
|
## Code Completion In Vanilla JavaScript
|
|
691
827
|
|
|
692
828
|
Vanilla JavaScript can use the published types to provide code completion (e.g.
|
package/fastify.d.ts
CHANGED
|
@@ -89,6 +89,22 @@ declare namespace fastify {
|
|
|
89
89
|
|
|
90
90
|
type TrustProxyFunction = (address: string, hop: number) => boolean
|
|
91
91
|
|
|
92
|
+
export type FastifyRouterOptions<RawServer extends RawServerBase> = {
|
|
93
|
+
allowUnsafeRegex?: boolean,
|
|
94
|
+
buildPrettyMeta?: (route: { [k: string]: unknown, store: { [k: string]: unknown } }) => object,
|
|
95
|
+
caseSensitive?: boolean,
|
|
96
|
+
constraints?: {
|
|
97
|
+
[name: string]: ConstraintStrategy<FindMyWayVersion<RawServer>, unknown>,
|
|
98
|
+
},
|
|
99
|
+
defaultRoute?: (req: FastifyRequest, res: FastifyReply) => void,
|
|
100
|
+
ignoreDuplicateSlashes?: boolean,
|
|
101
|
+
ignoreTrailingSlash?: boolean,
|
|
102
|
+
maxParamLength?: number,
|
|
103
|
+
onBadUrl?: (path: string, req: FastifyRequest, res: FastifyReply) => void,
|
|
104
|
+
querystringParser?: (str: string) => { [key: string]: unknown },
|
|
105
|
+
useSemicolonDelimiter?: boolean,
|
|
106
|
+
}
|
|
107
|
+
|
|
92
108
|
/**
|
|
93
109
|
* Options for a fastify server instance. Utilizes conditional logic on the generic server parameter to enforce certain https and http2
|
|
94
110
|
*/
|
|
@@ -159,6 +175,7 @@ declare namespace fastify {
|
|
|
159
175
|
clientErrorHandler?: (error: ConnectionError, socket: Socket) => void,
|
|
160
176
|
childLoggerFactory?: FastifyChildLoggerFactory,
|
|
161
177
|
allowErrorHandlerOverride?: boolean
|
|
178
|
+
routerOptions?: FastifyRouterOptions<RawServer>,
|
|
162
179
|
}
|
|
163
180
|
|
|
164
181
|
/**
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '5.
|
|
3
|
+
const VERSION = '5.6.1'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('node:http')
|
|
@@ -194,6 +194,7 @@ function fastify (options) {
|
|
|
194
194
|
|
|
195
195
|
const serverHasCloseAllConnections = typeof server.closeAllConnections === 'function'
|
|
196
196
|
const serverHasCloseIdleConnections = typeof server.closeIdleConnections === 'function'
|
|
197
|
+
const serverHasCloseHttp2Sessions = typeof server.closeHttp2Sessions === 'function'
|
|
197
198
|
|
|
198
199
|
let forceCloseConnections = options.forceCloseConnections
|
|
199
200
|
if (forceCloseConnections === 'idle' && !serverHasCloseIdleConnections) {
|
|
@@ -491,6 +492,10 @@ function fastify (options) {
|
|
|
491
492
|
}
|
|
492
493
|
}
|
|
493
494
|
|
|
495
|
+
if (serverHasCloseHttp2Sessions) {
|
|
496
|
+
instance.server.closeHttp2Sessions()
|
|
497
|
+
}
|
|
498
|
+
|
|
494
499
|
// No new TCP connections are accepted.
|
|
495
500
|
// We must call close on the server even if we are not listening
|
|
496
501
|
// otherwise memory will be leaked.
|
package/lib/server.js
CHANGED
|
@@ -6,7 +6,7 @@ const http2 = require('node:http2')
|
|
|
6
6
|
const dns = require('node:dns')
|
|
7
7
|
const os = require('node:os')
|
|
8
8
|
|
|
9
|
-
const { kState, kOptions, kServerBindings } = require('./symbols')
|
|
9
|
+
const { kState, kOptions, kServerBindings, kHttp2ServerSessions } = require('./symbols')
|
|
10
10
|
const { FSTWRN003 } = require('./warnings')
|
|
11
11
|
const { onListenHookRunner } = require('./hooks')
|
|
12
12
|
const {
|
|
@@ -175,6 +175,9 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
|
|
|
175
175
|
if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections === true) {
|
|
176
176
|
secondaryServer.closeAllConnections()
|
|
177
177
|
}
|
|
178
|
+
if (typeof secondaryServer.closeHttp2Sessions === 'function') {
|
|
179
|
+
secondaryServer.closeHttp2Sessions()
|
|
180
|
+
}
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
secondaryServer.on('upgrade', mainServer.emit.bind(mainServer, 'upgrade'))
|
|
@@ -283,10 +286,16 @@ function getServerInstance (options, httpHandler) {
|
|
|
283
286
|
|
|
284
287
|
if (options.http2) {
|
|
285
288
|
const server = typeof httpsOptions === 'object' ? http2.createSecureServer(httpsOptions, httpHandler) : http2.createServer(options.http, httpHandler)
|
|
286
|
-
server.on('session', (session) => session.setTimeout(options.http2SessionTimeout,
|
|
287
|
-
|
|
289
|
+
server.on('session', (session) => session.setTimeout(options.http2SessionTimeout, () => {
|
|
290
|
+
session.close()
|
|
288
291
|
}))
|
|
289
292
|
|
|
293
|
+
// This is only needed for Node.js versions < 24.0.0 since Node.js added native
|
|
294
|
+
// closeAllSessions() on server.close() support for HTTP/2 servers in v24.0.0
|
|
295
|
+
if (options.forceCloseConnections === true) {
|
|
296
|
+
server.closeHttp2Sessions = createCloseHttp2SessionsByHttp2Server(server)
|
|
297
|
+
}
|
|
298
|
+
|
|
290
299
|
server.setTimeout(options.connectionTimeout)
|
|
291
300
|
|
|
292
301
|
return server
|
|
@@ -353,3 +362,47 @@ function logServerAddress (server, listenTextResolver) {
|
|
|
353
362
|
}
|
|
354
363
|
return addresses[0]
|
|
355
364
|
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @param {http2.Http2Server} http2Server
|
|
368
|
+
* @returns {() => void}
|
|
369
|
+
*/
|
|
370
|
+
function createCloseHttp2SessionsByHttp2Server (http2Server) {
|
|
371
|
+
/**
|
|
372
|
+
* @type {Set<http2.Http2Session>}
|
|
373
|
+
*/
|
|
374
|
+
http2Server[kHttp2ServerSessions] = new Set()
|
|
375
|
+
|
|
376
|
+
http2Server.on('session', function (session) {
|
|
377
|
+
session.once('connect', function () {
|
|
378
|
+
http2Server[kHttp2ServerSessions].add(session)
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
session.once('close', function () {
|
|
382
|
+
http2Server[kHttp2ServerSessions].delete(session)
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
session.once('frameError', function (type, code, streamId) {
|
|
386
|
+
if (streamId === 0) {
|
|
387
|
+
// The stream ID is 0, which means that the error is related to the session itself.
|
|
388
|
+
// If the event is not associated with a stream, the Http2Session will be shut down immediately
|
|
389
|
+
http2Server[kHttp2ServerSessions].delete(session)
|
|
390
|
+
}
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
session.once('goaway', function () {
|
|
394
|
+
// The Http2Session instance will be shut down automatically when the 'goaway' event is emitted.
|
|
395
|
+
http2Server[kHttp2ServerSessions].delete(session)
|
|
396
|
+
})
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
return function closeHttp2Sessions () {
|
|
400
|
+
if (http2Server[kHttp2ServerSessions].size === 0) {
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const session of http2Server[kHttp2ServerSessions]) {
|
|
405
|
+
session.close()
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
package/lib/symbols.js
CHANGED
|
@@ -17,6 +17,7 @@ const keys = {
|
|
|
17
17
|
kPluginNameChain: Symbol('fastify.pluginNameChain'),
|
|
18
18
|
kRouteContext: Symbol('fastify.context'),
|
|
19
19
|
kGenReqId: Symbol('fastify.genReqId'),
|
|
20
|
+
kHttp2ServerSessions: Symbol('fastify.http2ServerSessions'),
|
|
20
21
|
// Schema
|
|
21
22
|
kSchemaController: Symbol('fastify.schemaController'),
|
|
22
23
|
kSchemaHeaders: Symbol('headers-schema'),
|
package/lib/warnings.js
CHANGED
|
@@ -43,7 +43,7 @@ const FSTSEC001 = createWarning({
|
|
|
43
43
|
|
|
44
44
|
const FSTDEP022 = createWarning({
|
|
45
45
|
name: 'FastifyWarning',
|
|
46
|
-
code: '
|
|
46
|
+
code: 'FSTDEP022',
|
|
47
47
|
message: 'The router options for %s property access is deprecated. Please use "options.routerOptions" instead for accessing router options. The router options will be removed in `fastify@6`.',
|
|
48
48
|
unlimited: true
|
|
49
49
|
})
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ test('plugin namespace', async t => {
|
|
|
20
20
|
// ! the plugin does not know about the namespace
|
|
21
21
|
reply.send({ utility: app.utility() })
|
|
22
22
|
})
|
|
23
|
-
}, {
|
|
23
|
+
}, { namespace: 'foo' })
|
|
24
24
|
|
|
25
25
|
// ! but outside the plugin the decorator would be app.foo.utility()
|
|
26
26
|
t.assert.ok(app.foo.utility)
|
|
@@ -8,6 +8,7 @@ const connect = promisify(http2.connect)
|
|
|
8
8
|
const { once } = require('node:events')
|
|
9
9
|
const { buildCertificate } = require('../build-certificate')
|
|
10
10
|
const { getServerUrl } = require('../helper')
|
|
11
|
+
const { kHttp2ServerSessions } = require('../../lib/symbols')
|
|
11
12
|
|
|
12
13
|
test.before(buildCertificate)
|
|
13
14
|
|
|
@@ -180,3 +181,90 @@ test('http/2 server side session emits a timeout event', async t => {
|
|
|
180
181
|
await p
|
|
181
182
|
await fastify.close()
|
|
182
183
|
})
|
|
184
|
+
|
|
185
|
+
test('http/2 sessions closed after closing server', async t => {
|
|
186
|
+
t.plan(1)
|
|
187
|
+
const fastify = Fastify({
|
|
188
|
+
http2: true,
|
|
189
|
+
http2SessionTimeout: 100
|
|
190
|
+
})
|
|
191
|
+
await fastify.listen()
|
|
192
|
+
const url = getServerUrl(fastify)
|
|
193
|
+
const waitSessionConnect = once(fastify.server, 'session')
|
|
194
|
+
const session = http2.connect(url)
|
|
195
|
+
await once(session, 'connect')
|
|
196
|
+
await waitSessionConnect
|
|
197
|
+
const waitSessionClosed = once(session, 'close')
|
|
198
|
+
await fastify.close()
|
|
199
|
+
await waitSessionClosed
|
|
200
|
+
t.assert.strictEqual(session.closed, true)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('http/2 sessions should be closed when setting forceClosedConnections to true', async t => {
|
|
204
|
+
t.plan(2)
|
|
205
|
+
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
|
|
206
|
+
fastify.get('/', () => 'hello world')
|
|
207
|
+
await fastify.listen()
|
|
208
|
+
const client = await connect(getServerUrl(fastify))
|
|
209
|
+
const req = client.request({
|
|
210
|
+
[http2.HTTP2_HEADER_PATH]: '/',
|
|
211
|
+
[http2.HTTP2_HEADER_METHOD]: 'GET'
|
|
212
|
+
})
|
|
213
|
+
await once(req, 'response')
|
|
214
|
+
fastify.close()
|
|
215
|
+
const r2 = client.request({
|
|
216
|
+
[http2.HTTP2_HEADER_PATH]: '/',
|
|
217
|
+
[http2.TTP2_HEADER_METHOD]: 'GET'
|
|
218
|
+
})
|
|
219
|
+
r2.on('error', (err) => {
|
|
220
|
+
t.assert.strictEqual(err.toString(), 'Error [ERR_HTTP2_STREAM_ERROR]: Stream closed with error code NGHTTP2_REFUSED_STREAM')
|
|
221
|
+
})
|
|
222
|
+
await once(r2, 'error')
|
|
223
|
+
r2.end()
|
|
224
|
+
t.assert.strictEqual(client.closed, true)
|
|
225
|
+
client.destroy()
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('http/2 sessions should be removed from server[kHttp2ServerSessions] Set on goaway', async t => {
|
|
229
|
+
t.plan(2)
|
|
230
|
+
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
|
|
231
|
+
await fastify.listen()
|
|
232
|
+
const waitSession = once(fastify.server, 'session')
|
|
233
|
+
const client = http2.connect(getServerUrl(fastify))
|
|
234
|
+
const [session] = await waitSession
|
|
235
|
+
const waitGoaway = once(session, 'goaway')
|
|
236
|
+
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
|
|
237
|
+
client.goaway()
|
|
238
|
+
await waitGoaway
|
|
239
|
+
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 0)
|
|
240
|
+
client.destroy()
|
|
241
|
+
await fastify.close()
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test('http/2 sessions should be removed from server[kHttp2ServerSessions] Set on frameError', async t => {
|
|
245
|
+
t.plan(2)
|
|
246
|
+
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
|
|
247
|
+
await fastify.listen()
|
|
248
|
+
const waitSession = once(fastify.server, 'session')
|
|
249
|
+
const client = http2.connect(getServerUrl(fastify))
|
|
250
|
+
const [session] = await waitSession
|
|
251
|
+
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
|
|
252
|
+
session.emit('frameError', 0, 0, 0)
|
|
253
|
+
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 0)
|
|
254
|
+
client.destroy()
|
|
255
|
+
await fastify.close()
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
test('http/2 sessions should not be removed from server[kHttp2ServerSessions] from Set if stream id passed on frameError', async t => {
|
|
259
|
+
t.plan(2)
|
|
260
|
+
const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
|
|
261
|
+
await fastify.listen()
|
|
262
|
+
const waitSession = once(fastify.server, 'session')
|
|
263
|
+
const client = http2.connect(getServerUrl(fastify))
|
|
264
|
+
const [session] = await waitSession
|
|
265
|
+
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
|
|
266
|
+
session.emit('frameError', 0, 0, 1)
|
|
267
|
+
t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
|
|
268
|
+
client.destroy()
|
|
269
|
+
await fastify.close()
|
|
270
|
+
})
|
|
@@ -24,7 +24,7 @@ test('setErrorHandler can be set independently in parent and child scopes', asyn
|
|
|
24
24
|
})
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
test('setErrorHandler can be
|
|
27
|
+
test('setErrorHandler can be overridden if allowErrorHandlerOverride is set to true', async t => {
|
|
28
28
|
t.plan(2)
|
|
29
29
|
|
|
30
30
|
const fastify = Fastify()
|
|
@@ -244,12 +244,12 @@ function testHandlerOrBeforeHandlerHook (test, hookOrHandler) {
|
|
|
244
244
|
if (hookOrHandler === 'handler') {
|
|
245
245
|
app.get('/', (req, reply) => {
|
|
246
246
|
reply.hijack()
|
|
247
|
-
throw new Error('This
|
|
247
|
+
throw new Error('This will be skipped')
|
|
248
248
|
})
|
|
249
249
|
} else {
|
|
250
250
|
app.addHook(hookOrHandler, async (req, reply) => {
|
|
251
251
|
reply.hijack()
|
|
252
|
-
throw new Error('This
|
|
252
|
+
throw new Error('This will be skipped')
|
|
253
253
|
})
|
|
254
254
|
app.get('/', (req, reply) => t.assert.fail('Handler should not be called'))
|
|
255
255
|
}
|
|
@@ -15,6 +15,8 @@ import { FastifyRequest } from '../../types/request'
|
|
|
15
15
|
import { FastifySchemaControllerOptions, FastifySchemaCompiler, FastifySerializerCompiler } from '../../types/schema'
|
|
16
16
|
import { AddressInfo } from 'node:net'
|
|
17
17
|
import { Bindings, ChildLoggerOptions } from '../../types/logger'
|
|
18
|
+
import { ConstraintStrategy } from 'find-my-way'
|
|
19
|
+
import { FindMyWayVersion } from '../../types/instance'
|
|
18
20
|
|
|
19
21
|
const server = fastify()
|
|
20
22
|
|
|
@@ -309,7 +311,22 @@ type InitialConfig = Readonly<{
|
|
|
309
311
|
requestIdHeader?: string | false,
|
|
310
312
|
requestIdLogLabel?: string,
|
|
311
313
|
http2SessionTimeout?: number,
|
|
312
|
-
useSemicolonDelimiter?: boolean
|
|
314
|
+
useSemicolonDelimiter?: boolean,
|
|
315
|
+
routerOptions?: {
|
|
316
|
+
allowUnsafeRegex?: boolean,
|
|
317
|
+
buildPrettyMeta?: (route: { [k: string]: unknown, store: { [k: string]: unknown } }) => object,
|
|
318
|
+
caseSensitive?: boolean,
|
|
319
|
+
constraints?: {
|
|
320
|
+
[name: string]: ConstraintStrategy<FindMyWayVersion<RawServerDefault>, unknown>
|
|
321
|
+
}
|
|
322
|
+
defaultRoute?: (req: FastifyRequest, res: FastifyReply) => void,
|
|
323
|
+
ignoreDuplicateSlashes?: boolean,
|
|
324
|
+
ignoreTrailingSlash?: boolean,
|
|
325
|
+
maxParamLength?: number,
|
|
326
|
+
onBadUrl?: (path: string, req: FastifyRequest, res: FastifyReply) => void,
|
|
327
|
+
querystringParser?: (str: string) => { [key: string]: unknown },
|
|
328
|
+
useSemicolonDelimiter?: boolean,
|
|
329
|
+
}
|
|
313
330
|
}>
|
|
314
331
|
|
|
315
332
|
expectType<InitialConfig>(fastify().initialConfig)
|
|
@@ -72,6 +72,27 @@ expectAssignable<FastifyInstance>(server.post('/test', {
|
|
|
72
72
|
}
|
|
73
73
|
}, async req => req.body))
|
|
74
74
|
|
|
75
|
+
expectAssignable<FastifyInstance>(server.post('/test', {
|
|
76
|
+
validatorCompiler: ({ schema }) => {
|
|
77
|
+
return data => {
|
|
78
|
+
if (!data || data.constructor !== Object) {
|
|
79
|
+
return {
|
|
80
|
+
error: [
|
|
81
|
+
{
|
|
82
|
+
keyword: 'type',
|
|
83
|
+
instancePath: '',
|
|
84
|
+
schemaPath: '#/type',
|
|
85
|
+
params: { type: 'object' },
|
|
86
|
+
message: 'value is not an object'
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { value: data }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, async req => req.body))
|
|
95
|
+
|
|
75
96
|
expectAssignable<FastifyInstance>(server.setValidatorCompiler<FastifySchema & { validate: Record<string, unknown> }>(
|
|
76
97
|
function ({ schema }) {
|
|
77
98
|
return new Ajv().compile(schema)
|
package/types/instance.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
SafePromiseLike
|
|
24
24
|
} from './type-provider'
|
|
25
25
|
import { ContextConfigDefault, HTTPMethods, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
|
|
26
|
+
import { FastifyRouterOptions } from '../fastify'
|
|
26
27
|
|
|
27
28
|
export interface PrintRoutesOptions {
|
|
28
29
|
method?: HTTPMethods;
|
|
@@ -603,5 +604,6 @@ export interface FastifyInstance<
|
|
|
603
604
|
requestIdLogLabel?: string,
|
|
604
605
|
http2SessionTimeout?: number,
|
|
605
606
|
useSemicolonDelimiter?: boolean,
|
|
607
|
+
routerOptions?: FastifyRouterOptions<RawServer>
|
|
606
608
|
}>
|
|
607
609
|
}
|
package/types/logger.d.ts
CHANGED
|
@@ -7,20 +7,24 @@ import { FastifySchema } from './schema'
|
|
|
7
7
|
import { FastifyTypeProvider, FastifyTypeProviderDefault } from './type-provider'
|
|
8
8
|
import { ContextConfigDefault, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import type {
|
|
11
|
+
BaseLogger,
|
|
12
|
+
LogFn as FastifyLogFn,
|
|
13
|
+
LevelWithSilent as LogLevel,
|
|
14
|
+
Bindings,
|
|
15
|
+
ChildLoggerOptions,
|
|
16
|
+
LoggerOptions as PinoLoggerOptions
|
|
17
|
+
} from 'pino'
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export type Bindings = pino.Bindings
|
|
20
|
-
|
|
21
|
-
export type ChildLoggerOptions = pino.ChildLoggerOptions
|
|
19
|
+
export type {
|
|
20
|
+
FastifyLogFn,
|
|
21
|
+
LogLevel,
|
|
22
|
+
Bindings,
|
|
23
|
+
ChildLoggerOptions,
|
|
24
|
+
PinoLoggerOptions
|
|
25
|
+
}
|
|
22
26
|
|
|
23
|
-
export interface FastifyBaseLogger extends
|
|
27
|
+
export interface FastifyBaseLogger extends Pick<BaseLogger, 'level' | 'info' | 'error' | 'debug' | 'fatal' | 'warn' | 'trace' | 'silent'> {
|
|
24
28
|
child(bindings: Bindings, options?: ChildLoggerOptions): FastifyBaseLogger
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -34,8 +38,6 @@ export interface FastifyLoggerStreamDestination {
|
|
|
34
38
|
write(msg: string): void;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
export type PinoLoggerOptions = pino.LoggerOptions
|
|
38
|
-
|
|
39
41
|
// TODO: once node 18 is EOL, this type can be replaced with plain FastifyReply.
|
|
40
42
|
/**
|
|
41
43
|
* Specialized reply type used for the `res` log serializer, since only `statusCode` is passed in certain cases.
|
package/types/schema.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export interface FastifySchemaValidationError {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export interface FastifyValidationResult {
|
|
36
|
-
(data: any): boolean | SafePromiseLike<any> | { error?: Error, value?: any }
|
|
36
|
+
(data: any): boolean | SafePromiseLike<any> | { error?: Error | FastifySchemaValidationError[], value?: any }
|
|
37
37
|
errors?: FastifySchemaValidationError[] | null;
|
|
38
38
|
}
|
|
39
39
|
|