fastify 5.2.2 → 5.3.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/SPONSORS.md +0 -1
- package/docs/Guides/Ecosystem.md +2 -0
- package/docs/Reference/Decorators.md +199 -0
- package/docs/Reference/Errors.md +2 -0
- package/fastify.js +3 -2
- package/lib/decorate.js +18 -3
- package/lib/errors.js +4 -0
- package/lib/reply.js +17 -2
- package/lib/request.js +28 -2
- package/package.json +3 -3
- package/test/build/error-serializer.test.js +1 -2
- package/test/custom-parser.4.test.js +55 -38
- package/test/decorator.test.js +174 -4
- package/test/fastify-instance.test.js +12 -2
- package/test/hooks.on-listen.test.js +17 -14
- package/test/internals/errors.test.js +14 -1
- package/test/listen.2.test.js +4 -1
- package/test/server.test.js +175 -0
- package/test/types/instance.test-d.ts +3 -0
- package/test/types/reply.test-d.ts +1 -0
- package/test/types/request.test-d.ts +4 -0
- package/test/types/type-provider.test-d.ts +40 -0
- package/test/upgrade.test.js +3 -6
- package/types/instance.d.ts +2 -0
- package/types/reply.d.ts +1 -0
- package/types/request.d.ts +2 -0
- package/types/type-provider.d.ts +12 -3
- package/.vscode/settings.json +0 -22
package/SPONSORS.md
CHANGED
|
@@ -16,7 +16,6 @@ _Be the first!_
|
|
|
16
16
|
- [Mercedes-Benz Group](https://github.com/mercedes-benz)
|
|
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
|
-
- [Jspreadsheet](https://jspreadsheet.com/)
|
|
20
19
|
- [Lokalise - A Localization and Translation Software Tool](https://lokalise.com/?utm_source=Fastify_GH&utm_medium=sponsorship)
|
|
21
20
|
|
|
22
21
|
## Tier 2
|
package/docs/Guides/Ecosystem.md
CHANGED
|
@@ -460,6 +460,8 @@ middlewares into Fastify plugins
|
|
|
460
460
|
Lightweight cache plugin
|
|
461
461
|
- [`fastify-list-routes`](https://github.com/chuongtrh/fastify-list-routes)
|
|
462
462
|
A simple plugin for Fastify to list all available routes.
|
|
463
|
+
- [`fastify-lm`](https://github.com/galiprandi/fastify-lm#readme)
|
|
464
|
+
Use OpenAI, Claude, Google, Deepseek, and others LMs with one Fastify plugin.
|
|
463
465
|
- [`fastify-loader`](https://github.com/TheNoim/fastify-loader) Load routes from
|
|
464
466
|
a directory and inject the Fastify instance in each file.
|
|
465
467
|
- [`fastify-log-controller`](https://github.com/Eomm/fastify-log-controller/)
|
|
@@ -365,3 +365,202 @@ Will define the `foo` property on the Fastify instance:
|
|
|
365
365
|
```js
|
|
366
366
|
console.log(fastify.foo) // 'a getter'
|
|
367
367
|
```
|
|
368
|
+
|
|
369
|
+
### `getDecorator<T>` API
|
|
370
|
+
|
|
371
|
+
Fastify's `getDecorator<T>` API retrieves an existing decorator from the
|
|
372
|
+
Fastify instance, `Request`, or `Reply`. If the decorator is not defined, an
|
|
373
|
+
`FST_ERR_DEC_UNDECLARED` error is thrown.
|
|
374
|
+
|
|
375
|
+
#### Use cases
|
|
376
|
+
|
|
377
|
+
**Early Plugin Dependency Validation**
|
|
378
|
+
|
|
379
|
+
`getDecorator<T>` on Fastify instance verifies that required decorators are
|
|
380
|
+
available at registration time.
|
|
381
|
+
|
|
382
|
+
For example:
|
|
383
|
+
|
|
384
|
+
```js
|
|
385
|
+
fastify.register(async function (fastify) {
|
|
386
|
+
const usersRepository = fastify.getDecorator('usersRepository')
|
|
387
|
+
|
|
388
|
+
fastify.get('/users', async function (request, reply) {
|
|
389
|
+
// We are sure `usersRepository` exists at runtime
|
|
390
|
+
return usersRepository.findAll()
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Handling Missing Decorators**
|
|
396
|
+
|
|
397
|
+
Directly accessing a decorator may lead to unexpected behavior if it is not declared:
|
|
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
|
+
```
|
|
478
|
+
|
|
479
|
+
#### Bound functions inference
|
|
480
|
+
|
|
481
|
+
To save time, it's common to infer function types instead of
|
|
482
|
+
writing them manually:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
function sendSuccess (this: FastifyReply) {
|
|
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:
|
|
537
|
+
|
|
538
|
+
```ts
|
|
539
|
+
fastify.decorateRequest('user', '')
|
|
540
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
541
|
+
// Throws FST_ERR_DEC_UNDECLARED if the decorator does not exist
|
|
542
|
+
req.setDecorator('user-with-typo', 'Bob Dylan')
|
|
543
|
+
})
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
**Type Safety**
|
|
549
|
+
|
|
550
|
+
If the `FastifyRequest` interface does not declare the decorator, you
|
|
551
|
+
would typically need to use type assertions:
|
|
552
|
+
|
|
553
|
+
```ts
|
|
554
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
555
|
+
(req as typeof req & { user: string }).user = 'Bob Dylan'
|
|
556
|
+
})
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
The `setDecorator<T>` method eliminates the need for explicit type
|
|
560
|
+
assertions while allowing type safety:
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
564
|
+
req.setDecorator<string>('user', 'Bob Dylan')
|
|
565
|
+
})
|
|
566
|
+
```
|
package/docs/Reference/Errors.md
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
- [FST_ERR_DEC_MISSING_DEPENDENCY](#fst_err_dec_missing_dependency)
|
|
37
37
|
- [FST_ERR_DEC_AFTER_START](#fst_err_dec_after_start)
|
|
38
38
|
- [FST_ERR_DEC_REFERENCE_TYPE](#fst_err_dec_reference_type)
|
|
39
|
+
- [FST_ERR_DEC_UNDECLARED](#fst_err_dec_undeclared)
|
|
39
40
|
- [FST_ERR_HOOK_INVALID_TYPE](#fst_err_hook_invalid_type)
|
|
40
41
|
- [FST_ERR_HOOK_INVALID_HANDLER](#fst_err_hook_invalid_handler)
|
|
41
42
|
- [FST_ERR_HOOK_INVALID_ASYNC_HANDLER](#fst_err_hook_invalid_async_handler)
|
|
@@ -306,6 +307,7 @@ Below is a table with all the error codes used by Fastify.
|
|
|
306
307
|
| <a id="fst_err_dec_missing_dependency">FST_ERR_DEC_MISSING_DEPENDENCY</a> | The decorator cannot be registered due to a missing dependency. | Register the missing dependency. | [#1168](https://github.com/fastify/fastify/pull/1168) |
|
|
307
308
|
| <a id="fst_err_dec_after_start">FST_ERR_DEC_AFTER_START</a> | The decorator cannot be added after start. | Add the decorator before starting the server. | [#2128](https://github.com/fastify/fastify/pull/2128) |
|
|
308
309
|
| <a id="fst_err_dec_reference_type">FST_ERR_DEC_REFERENCE_TYPE</a> | The decorator cannot be a reference type. | Define the decorator with a getter/setter interface or an empty decorator with a hook. | [#5462](https://github.com/fastify/fastify/pull/5462) |
|
|
310
|
+
| <a id="fst_err_dec_undeclared">FST_ERR_DEC_UNDECLARED</a> | An attempt was made to access a decorator that has not been declared. | Declare the decorator before using it. | [#](https://github.com/fastify/fastify/pull/)
|
|
309
311
|
| <a id="fst_err_hook_invalid_type">FST_ERR_HOOK_INVALID_TYPE</a> | The hook name must be a string. | Use a string for the hook name. | [#1168](https://github.com/fastify/fastify/pull/1168) |
|
|
310
312
|
| <a id="fst_err_hook_invalid_handler">FST_ERR_HOOK_INVALID_HANDLER</a> | The hook callback must be a function. | Use a function for the hook callback. | [#1168](https://github.com/fastify/fastify/pull/1168) |
|
|
311
313
|
| <a id="fst_err_hook_invalid_async_handler">FST_ERR_HOOK_INVALID_ASYNC_HANDLER</a> | Async function has too many arguments. Async hooks should not use the `done` argument. | Remove the `done` argument from the async hook. | [#4367](https://github.com/fastify/fastify/pull/4367) |
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '5.
|
|
3
|
+
const VERSION = '5.3.0'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('node:http')
|
|
@@ -343,6 +343,7 @@ function fastify (options) {
|
|
|
343
343
|
decorateRequest: decorator.decorateRequest,
|
|
344
344
|
hasRequestDecorator: decorator.existRequest,
|
|
345
345
|
hasReplyDecorator: decorator.existReply,
|
|
346
|
+
getDecorator: decorator.getInstanceDecorator,
|
|
346
347
|
addHttpMethod,
|
|
347
348
|
// fake http injection
|
|
348
349
|
inject,
|
|
@@ -639,7 +640,7 @@ function fastify (options) {
|
|
|
639
640
|
resolveReady(fastify)
|
|
640
641
|
fastify[kState].booting = false
|
|
641
642
|
fastify[kState].ready = true
|
|
642
|
-
fastify[kState].
|
|
643
|
+
fastify[kState].readyPromise = null
|
|
643
644
|
}
|
|
644
645
|
}
|
|
645
646
|
|
package/lib/decorate.js
CHANGED
|
@@ -4,7 +4,7 @@ const {
|
|
|
4
4
|
kReply,
|
|
5
5
|
kRequest,
|
|
6
6
|
kState,
|
|
7
|
-
kHasBeenDecorated
|
|
7
|
+
kHasBeenDecorated,
|
|
8
8
|
} = require('./symbols.js')
|
|
9
9
|
|
|
10
10
|
const {
|
|
@@ -12,7 +12,8 @@ const {
|
|
|
12
12
|
FST_ERR_DEC_MISSING_DEPENDENCY,
|
|
13
13
|
FST_ERR_DEC_AFTER_START,
|
|
14
14
|
FST_ERR_DEC_REFERENCE_TYPE,
|
|
15
|
-
FST_ERR_DEC_DEPENDENCY_INVALID_TYPE
|
|
15
|
+
FST_ERR_DEC_DEPENDENCY_INVALID_TYPE,
|
|
16
|
+
FST_ERR_DEC_UNDECLARED,
|
|
16
17
|
} = require('./errors')
|
|
17
18
|
|
|
18
19
|
function decorate (instance, name, fn, dependencies) {
|
|
@@ -32,6 +33,18 @@ function decorate (instance, name, fn, dependencies) {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
function getInstanceDecorator (name) {
|
|
37
|
+
if (!checkExistence(this, name)) {
|
|
38
|
+
throw new FST_ERR_DEC_UNDECLARED(name, 'instance')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof this[name] === 'function') {
|
|
42
|
+
return this[name].bind(this)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return this[name]
|
|
46
|
+
}
|
|
47
|
+
|
|
35
48
|
function decorateConstructor (konstructor, name, fn, dependencies) {
|
|
36
49
|
const instance = konstructor.prototype
|
|
37
50
|
if (Object.hasOwn(instance, name) || hasKey(konstructor, name)) {
|
|
@@ -133,5 +146,7 @@ module.exports = {
|
|
|
133
146
|
existReply: checkReplyExistence,
|
|
134
147
|
dependencies: checkDependencies,
|
|
135
148
|
decorateReply,
|
|
136
|
-
decorateRequest
|
|
149
|
+
decorateRequest,
|
|
150
|
+
getInstanceDecorator,
|
|
151
|
+
hasKey
|
|
137
152
|
}
|
package/lib/errors.js
CHANGED
|
@@ -149,6 +149,10 @@ const codes = {
|
|
|
149
149
|
'FST_ERR_DEC_REFERENCE_TYPE',
|
|
150
150
|
"The decorator '%s' of type '%s' is a reference type. Use the { getter, setter } interface instead."
|
|
151
151
|
),
|
|
152
|
+
FST_ERR_DEC_UNDECLARED: createError(
|
|
153
|
+
'FST_ERR_DEC_UNDECLARED',
|
|
154
|
+
"No decorator '%s' has been declared on %s."
|
|
155
|
+
),
|
|
152
156
|
|
|
153
157
|
/**
|
|
154
158
|
* hooks
|
package/lib/reply.js
CHANGED
|
@@ -22,7 +22,7 @@ const {
|
|
|
22
22
|
kReplyCacheSerializeFns,
|
|
23
23
|
kSchemaController,
|
|
24
24
|
kOptions,
|
|
25
|
-
kRouteContext
|
|
25
|
+
kRouteContext,
|
|
26
26
|
} = require('./symbols.js')
|
|
27
27
|
const {
|
|
28
28
|
onSendHookRunner,
|
|
@@ -52,8 +52,10 @@ const {
|
|
|
52
52
|
FST_ERR_BAD_TRAILER_NAME,
|
|
53
53
|
FST_ERR_BAD_TRAILER_VALUE,
|
|
54
54
|
FST_ERR_MISSING_SERIALIZATION_FN,
|
|
55
|
-
FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN
|
|
55
|
+
FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN,
|
|
56
|
+
FST_ERR_DEC_UNDECLARED,
|
|
56
57
|
} = require('./errors')
|
|
58
|
+
const decorators = require('./decorate')
|
|
57
59
|
|
|
58
60
|
const toString = Object.prototype.toString
|
|
59
61
|
|
|
@@ -475,6 +477,19 @@ Reply.prototype.then = function (fulfilled, rejected) {
|
|
|
475
477
|
})
|
|
476
478
|
}
|
|
477
479
|
|
|
480
|
+
Reply.prototype.getDecorator = function (name) {
|
|
481
|
+
if (!decorators.hasKey(this, name) && !decorators.exist(this, name)) {
|
|
482
|
+
throw new FST_ERR_DEC_UNDECLARED(name, 'reply')
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const decorator = this[name]
|
|
486
|
+
if (typeof decorator === 'function') {
|
|
487
|
+
return decorator.bind(this)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return decorator
|
|
491
|
+
}
|
|
492
|
+
|
|
478
493
|
function preSerializationHook (reply, payload) {
|
|
479
494
|
if (reply[kRouteContext].preSerialization !== null) {
|
|
480
495
|
preSerializationHookRunner(
|
package/lib/request.js
CHANGED
|
@@ -11,9 +11,10 @@ const {
|
|
|
11
11
|
kOptions,
|
|
12
12
|
kRequestCacheValidateFns,
|
|
13
13
|
kRouteContext,
|
|
14
|
-
kRequestOriginalUrl
|
|
14
|
+
kRequestOriginalUrl,
|
|
15
15
|
} = require('./symbols')
|
|
16
|
-
const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
|
|
16
|
+
const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION, FST_ERR_DEC_UNDECLARED } = require('./errors')
|
|
17
|
+
const decorators = require('./decorate')
|
|
17
18
|
|
|
18
19
|
const HTTP_PART_SYMBOL_MAP = {
|
|
19
20
|
body: kSchemaBody,
|
|
@@ -141,6 +142,12 @@ function buildRequestWithTrustProxy (R, trustProxy) {
|
|
|
141
142
|
return _Request
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
function assertsRequestDecoration (request, name) {
|
|
146
|
+
if (!decorators.hasKey(request, name) && !decorators.exist(request, name)) {
|
|
147
|
+
throw new FST_ERR_DEC_UNDECLARED(name, 'request')
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
144
151
|
Object.defineProperties(Request.prototype, {
|
|
145
152
|
server: {
|
|
146
153
|
get () {
|
|
@@ -343,6 +350,25 @@ Object.defineProperties(Request.prototype, {
|
|
|
343
350
|
|
|
344
351
|
return validate(input)
|
|
345
352
|
}
|
|
353
|
+
},
|
|
354
|
+
getDecorator: {
|
|
355
|
+
value: function (name) {
|
|
356
|
+
assertsRequestDecoration(this, name)
|
|
357
|
+
|
|
358
|
+
const decorator = this[name]
|
|
359
|
+
if (typeof decorator === 'function') {
|
|
360
|
+
return decorator.bind(this)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return decorator
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
setDecorator: {
|
|
367
|
+
value: function (name, value) {
|
|
368
|
+
assertsRequestDecoration(this, name)
|
|
369
|
+
|
|
370
|
+
this[name] = value
|
|
371
|
+
}
|
|
346
372
|
}
|
|
347
373
|
})
|
|
348
374
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -209,9 +209,9 @@
|
|
|
209
209
|
"find-my-way": "^9.0.0",
|
|
210
210
|
"light-my-request": "^6.0.0",
|
|
211
211
|
"pino": "^9.0.0",
|
|
212
|
-
"process-warning": "^
|
|
212
|
+
"process-warning": "^5.0.0",
|
|
213
213
|
"rfdc": "^1.3.1",
|
|
214
|
-
"secure-json-parse": "^
|
|
214
|
+
"secure-json-parse": "^4.0.0",
|
|
215
215
|
"semver": "^7.6.0",
|
|
216
216
|
"toad-cache": "^3.7.0"
|
|
217
217
|
},
|
|
@@ -32,6 +32,5 @@ test('ensure the current error serializer is latest', { skip: !isPrepublish }, a
|
|
|
32
32
|
const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
|
|
33
33
|
|
|
34
34
|
// line break should not be a problem depends on system
|
|
35
|
-
|
|
36
|
-
t.assert.ok(current)
|
|
35
|
+
t.assert.strictEqual(unifyLineBreak(current), unifyLineBreak(code))
|
|
37
36
|
})
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const test = t.test
|
|
3
|
+
const { test } = require('node:test')
|
|
5
4
|
const sget = require('simple-get').concat
|
|
6
5
|
const Fastify = require('../fastify')
|
|
7
6
|
const jsonParser = require('fast-json-body')
|
|
8
7
|
const { getServerUrl } = require('./helper')
|
|
8
|
+
const { waitForCb } = require('./toolkit')
|
|
9
9
|
|
|
10
10
|
process.removeAllListeners('warning')
|
|
11
11
|
|
|
12
|
-
test('should prefer string content types over RegExp ones', t => {
|
|
12
|
+
test('should prefer string content types over RegExp ones', (t, testDone) => {
|
|
13
13
|
t.plan(7)
|
|
14
14
|
const fastify = Fastify()
|
|
15
|
-
t.
|
|
16
|
-
|
|
15
|
+
t.after(() => { fastify.close() })
|
|
17
16
|
fastify.post('/', (req, reply) => {
|
|
18
17
|
reply.send(req.body)
|
|
19
18
|
})
|
|
@@ -33,7 +32,8 @@ test('should prefer string content types over RegExp ones', t => {
|
|
|
33
32
|
})
|
|
34
33
|
|
|
35
34
|
fastify.listen({ port: 0 }, err => {
|
|
36
|
-
t.
|
|
35
|
+
t.assert.ifError(err)
|
|
36
|
+
const completion = waitForCb({ steps: 2 })
|
|
37
37
|
|
|
38
38
|
sget({
|
|
39
39
|
method: 'POST',
|
|
@@ -43,9 +43,10 @@ test('should prefer string content types over RegExp ones', t => {
|
|
|
43
43
|
'Content-Type': 'application/json'
|
|
44
44
|
}
|
|
45
45
|
}, (err, response, body) => {
|
|
46
|
-
t.
|
|
47
|
-
t.
|
|
48
|
-
t.
|
|
46
|
+
t.assert.ifError(err)
|
|
47
|
+
t.assert.strictEqual(response.statusCode, 200)
|
|
48
|
+
t.assert.equal(body.toString(), JSON.stringify({ k1: 'myValue', k2: 'myValue' }))
|
|
49
|
+
completion.stepIn()
|
|
49
50
|
})
|
|
50
51
|
|
|
51
52
|
sget({
|
|
@@ -56,18 +57,21 @@ test('should prefer string content types over RegExp ones', t => {
|
|
|
56
57
|
'Content-Type': 'application/javascript'
|
|
57
58
|
}
|
|
58
59
|
}, (err, response, body) => {
|
|
59
|
-
t.
|
|
60
|
-
t.
|
|
61
|
-
t.
|
|
60
|
+
t.assert.ifError(err)
|
|
61
|
+
t.assert.strictEqual(response.statusCode, 200)
|
|
62
|
+
t.assert.equal(body.toString(), 'javascript')
|
|
63
|
+
completion.stepIn()
|
|
62
64
|
})
|
|
65
|
+
|
|
66
|
+
completion.patience.then(testDone)
|
|
63
67
|
})
|
|
64
68
|
})
|
|
65
69
|
|
|
66
|
-
test('removeContentTypeParser should support arrays of content types to remove', t => {
|
|
70
|
+
test('removeContentTypeParser should support arrays of content types to remove', (t, testDone) => {
|
|
67
71
|
t.plan(8)
|
|
68
72
|
|
|
69
73
|
const fastify = Fastify()
|
|
70
|
-
t.
|
|
74
|
+
t.after(() => fastify.close())
|
|
71
75
|
|
|
72
76
|
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
|
|
73
77
|
payload.on('data', () => {})
|
|
@@ -90,7 +94,8 @@ test('removeContentTypeParser should support arrays of content types to remove',
|
|
|
90
94
|
})
|
|
91
95
|
|
|
92
96
|
fastify.listen({ port: 0 }, err => {
|
|
93
|
-
t.
|
|
97
|
+
t.assert.ifError(err)
|
|
98
|
+
const completion = waitForCb({ steps: 3 })
|
|
94
99
|
|
|
95
100
|
sget({
|
|
96
101
|
method: 'POST',
|
|
@@ -100,9 +105,10 @@ test('removeContentTypeParser should support arrays of content types to remove',
|
|
|
100
105
|
'Content-Type': 'application/xml'
|
|
101
106
|
}
|
|
102
107
|
}, (err, response, body) => {
|
|
103
|
-
t.
|
|
104
|
-
t.
|
|
105
|
-
t.
|
|
108
|
+
t.assert.ifError(err)
|
|
109
|
+
t.assert.strictEqual(response.statusCode, 200)
|
|
110
|
+
t.assert.equal(body.toString(), 'xml')
|
|
111
|
+
completion.stepIn()
|
|
106
112
|
})
|
|
107
113
|
|
|
108
114
|
sget({
|
|
@@ -113,8 +119,9 @@ test('removeContentTypeParser should support arrays of content types to remove',
|
|
|
113
119
|
'Content-Type': 'image/png'
|
|
114
120
|
}
|
|
115
121
|
}, (err, response, body) => {
|
|
116
|
-
t.
|
|
117
|
-
t.
|
|
122
|
+
t.assert.ifError(err)
|
|
123
|
+
t.assert.strictEqual(response.statusCode, 415)
|
|
124
|
+
completion.stepIn()
|
|
118
125
|
})
|
|
119
126
|
|
|
120
127
|
sget({
|
|
@@ -125,16 +132,19 @@ test('removeContentTypeParser should support arrays of content types to remove',
|
|
|
125
132
|
'Content-Type': 'application/json'
|
|
126
133
|
}
|
|
127
134
|
}, (err, response, body) => {
|
|
128
|
-
t.
|
|
129
|
-
t.
|
|
135
|
+
t.assert.ifError(err)
|
|
136
|
+
t.assert.strictEqual(response.statusCode, 415)
|
|
137
|
+
completion.stepIn()
|
|
130
138
|
})
|
|
139
|
+
completion.patience.then(testDone)
|
|
131
140
|
})
|
|
132
141
|
})
|
|
133
142
|
|
|
134
|
-
test('removeContentTypeParser should support encapsulation', t => {
|
|
143
|
+
test('removeContentTypeParser should support encapsulation', (t, testDone) => {
|
|
135
144
|
t.plan(6)
|
|
136
145
|
|
|
137
146
|
const fastify = Fastify()
|
|
147
|
+
t.after(() => fastify.close())
|
|
138
148
|
|
|
139
149
|
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
|
|
140
150
|
payload.on('data', () => {})
|
|
@@ -158,7 +168,8 @@ test('removeContentTypeParser should support encapsulation', t => {
|
|
|
158
168
|
})
|
|
159
169
|
|
|
160
170
|
fastify.listen({ port: 0 }, err => {
|
|
161
|
-
t.
|
|
171
|
+
t.assert.ifError(err)
|
|
172
|
+
const completion = waitForCb({ steps: 2 })
|
|
162
173
|
|
|
163
174
|
sget({
|
|
164
175
|
method: 'POST',
|
|
@@ -168,8 +179,9 @@ test('removeContentTypeParser should support encapsulation', t => {
|
|
|
168
179
|
'Content-Type': 'application/xml'
|
|
169
180
|
}
|
|
170
181
|
}, (err, response, body) => {
|
|
171
|
-
t.
|
|
172
|
-
t.
|
|
182
|
+
t.assert.ifError(err)
|
|
183
|
+
t.assert.strictEqual(response.statusCode, 415)
|
|
184
|
+
completion.stepIn()
|
|
173
185
|
})
|
|
174
186
|
|
|
175
187
|
sget({
|
|
@@ -180,18 +192,20 @@ test('removeContentTypeParser should support encapsulation', t => {
|
|
|
180
192
|
'Content-Type': 'application/xml'
|
|
181
193
|
}
|
|
182
194
|
}, (err, response, body) => {
|
|
183
|
-
t.
|
|
184
|
-
t.
|
|
185
|
-
t.
|
|
186
|
-
|
|
195
|
+
t.assert.ifError(err)
|
|
196
|
+
t.assert.strictEqual(response.statusCode, 200)
|
|
197
|
+
t.assert.equal(body.toString(), 'xml')
|
|
198
|
+
completion.stepIn()
|
|
187
199
|
})
|
|
200
|
+
completion.patience.then(testDone)
|
|
188
201
|
})
|
|
189
202
|
})
|
|
190
203
|
|
|
191
|
-
test('removeAllContentTypeParsers should support encapsulation', t => {
|
|
204
|
+
test('removeAllContentTypeParsers should support encapsulation', (t, testDone) => {
|
|
192
205
|
t.plan(6)
|
|
193
206
|
|
|
194
207
|
const fastify = Fastify()
|
|
208
|
+
t.after(() => fastify.close())
|
|
195
209
|
|
|
196
210
|
fastify.post('/', (req, reply) => {
|
|
197
211
|
reply.send(req.body)
|
|
@@ -208,7 +222,8 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
|
|
|
208
222
|
})
|
|
209
223
|
|
|
210
224
|
fastify.listen({ port: 0 }, err => {
|
|
211
|
-
t.
|
|
225
|
+
t.assert.ifError(err)
|
|
226
|
+
const completion = waitForCb({ steps: 2 })
|
|
212
227
|
|
|
213
228
|
sget({
|
|
214
229
|
method: 'POST',
|
|
@@ -218,8 +233,9 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
|
|
|
218
233
|
'Content-Type': 'application/json'
|
|
219
234
|
}
|
|
220
235
|
}, (err, response, body) => {
|
|
221
|
-
t.
|
|
222
|
-
t.
|
|
236
|
+
t.assert.ifError(err)
|
|
237
|
+
t.assert.strictEqual(response.statusCode, 415)
|
|
238
|
+
completion.stepIn()
|
|
223
239
|
})
|
|
224
240
|
|
|
225
241
|
sget({
|
|
@@ -230,10 +246,11 @@ test('removeAllContentTypeParsers should support encapsulation', t => {
|
|
|
230
246
|
'Content-Type': 'application/json'
|
|
231
247
|
}
|
|
232
248
|
}, (err, response, body) => {
|
|
233
|
-
t.
|
|
234
|
-
t.
|
|
235
|
-
t.
|
|
236
|
-
|
|
249
|
+
t.assert.ifError(err)
|
|
250
|
+
t.assert.strictEqual(response.statusCode, 200)
|
|
251
|
+
t.assert.equal(JSON.parse(body.toString()).test, 1)
|
|
252
|
+
completion.stepIn()
|
|
237
253
|
})
|
|
254
|
+
completion.patience.then(testDone)
|
|
238
255
|
})
|
|
239
256
|
})
|