fastify 4.0.3 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +1 -0
- package/README.md +13 -14
- package/docs/Guides/Database.md +7 -7
- package/docs/Guides/Delay-Accepting-Requests.md +1 -1
- package/docs/Guides/Ecosystem.md +29 -16
- package/docs/Guides/Migration-Guide-V4.md +86 -1
- package/docs/Guides/Plugins-Guide.md +5 -0
- package/docs/Guides/Serverless.md +23 -10
- package/docs/Reference/Hooks.md +52 -0
- package/docs/Reference/Plugins.md +1 -1
- package/docs/Reference/Server.md +1 -1
- package/docs/Reference/Type-Providers.md +3 -17
- package/docs/Reference/TypeScript.md +65 -28
- package/docs/Reference/Validation-and-Serialization.md +11 -0
- package/docs/index.md +1 -1
- package/fastify.d.ts +3 -3
- package/fastify.js +19 -19
- package/integration/server.js +27 -0
- package/integration/test.sh +23 -0
- package/lib/context.js +5 -2
- package/lib/error-serializer.js +24 -27
- package/lib/handleRequest.js +1 -1
- package/lib/reply.js +22 -21
- package/lib/route.js +39 -29
- package/lib/symbols.js +2 -1
- package/lib/validation.js +2 -0
- package/package.json +10 -10
- package/test/404s.test.js +2 -2
- package/test/build/error-serializer.test.js +9 -2
- package/test/hooks.test.js +21 -0
- package/test/internals/reply.test.js +12 -0
- package/test/pretty-print.test.js +3 -3
- package/test/reply-error.test.js +1 -1
- package/test/schema-feature.test.js +2 -2
- package/test/schema-validation.test.js +71 -0
- package/test/stream.test.js +1 -1
- package/test/types/fastify.test-d.ts +24 -2
- package/test/types/instance.test-d.ts +5 -2
- package/test/types/register.test-d.ts +77 -2
- package/test/types/request.test-d.ts +8 -4
- package/test/types/type-provider.test-d.ts +11 -2
- package/test/validation-error-handling.test.js +38 -1
- package/types/instance.d.ts +59 -91
- package/types/register.d.ts +9 -7
- package/types/route.d.ts +10 -12
- package/types/schema.d.ts +5 -2
- package/types/type-provider.d.ts +12 -5
|
@@ -143,7 +143,7 @@ route-level `request` object.
|
|
|
143
143
|
curl localhost:8080/auth?username=admin&password=Password123!
|
|
144
144
|
```
|
|
145
145
|
And it should return back `logged in!`
|
|
146
|
-
6. But wait
|
|
146
|
+
6. But wait there's more! The generic interfaces are also available inside route
|
|
147
147
|
level hook methods. Modify the previous route by adding a `preValidation`
|
|
148
148
|
hook:
|
|
149
149
|
```typescript
|
|
@@ -199,17 +199,18 @@ Below is how to setup schema validation using vanilla `typebox` and
|
|
|
199
199
|
#### typebox
|
|
200
200
|
|
|
201
201
|
A useful library for building types and a schema at once is
|
|
202
|
-
[typebox](https://www.npmjs.com/package/@sinclair/typebox)
|
|
203
|
-
|
|
204
|
-
you
|
|
202
|
+
[typebox](https://www.npmjs.com/package/@sinclair/typebox) along with
|
|
203
|
+
[fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox).
|
|
204
|
+
With typebox you define your schema within your code and use them
|
|
205
|
+
directly as types or schemas as you need them.
|
|
205
206
|
|
|
206
207
|
When you want to use it for validation of some payload in a fastify route you
|
|
207
208
|
can do it as follows:
|
|
208
209
|
|
|
209
|
-
1. Install `typebox` in your project.
|
|
210
|
+
1. Install `typebox` and `fastify-type-provider-typebox` in your project.
|
|
210
211
|
|
|
211
212
|
```bash
|
|
212
|
-
npm i @sinclair/typebox
|
|
213
|
+
npm i @sinclair/typebox @fastify/type-provider-typebox
|
|
213
214
|
```
|
|
214
215
|
|
|
215
216
|
2. Define the schema you need with `Type` and create the respective type with
|
|
@@ -218,40 +219,52 @@ can do it as follows:
|
|
|
218
219
|
```typescript
|
|
219
220
|
import { Static, Type } from '@sinclair/typebox'
|
|
220
221
|
|
|
221
|
-
const User = Type.Object({
|
|
222
|
+
export const User = Type.Object({
|
|
222
223
|
name: Type.String(),
|
|
223
|
-
mail: Type.Optional(Type.String({ format:
|
|
224
|
-
})
|
|
225
|
-
|
|
224
|
+
mail: Type.Optional(Type.String({ format: 'email' })),
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
export type UserType = Static<typeof User>
|
|
226
228
|
```
|
|
227
229
|
|
|
228
230
|
3. Use the defined type and schema during the definition of your route
|
|
229
231
|
|
|
230
232
|
```typescript
|
|
231
|
-
|
|
233
|
+
import Fastify from 'fastify'
|
|
234
|
+
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
|
|
235
|
+
// ...
|
|
236
|
+
|
|
237
|
+
const fastify = Fastify().withTypeProvider<TypeBoxTypeProvider>()
|
|
232
238
|
|
|
233
|
-
app.post<{ Body: UserType
|
|
234
|
-
|
|
239
|
+
app.post<{ Body: UserType, Reply: UserType }>(
|
|
240
|
+
'/',
|
|
235
241
|
{
|
|
236
242
|
schema: {
|
|
237
243
|
body: User,
|
|
238
244
|
response: {
|
|
239
|
-
200: User
|
|
245
|
+
200: User
|
|
240
246
|
},
|
|
241
247
|
},
|
|
242
248
|
},
|
|
243
249
|
(request, reply) => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
// The `name` and `mail` types are automatically inferred
|
|
251
|
+
const { name, mail } = request.body;
|
|
252
|
+
reply.status(200).send({ name, mail });
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Note** For Ajv version 7 and above is required to use the `ajvTypeBoxPlugin`:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import Fastify from 'fastify'
|
|
261
|
+
import { ajvTypeBoxPlugin, TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
|
|
262
|
+
|
|
263
|
+
const fastify = Fastify({
|
|
264
|
+
ajv: {
|
|
265
|
+
plugins: [ajvTypeBoxPlugin]
|
|
253
266
|
}
|
|
254
|
-
)
|
|
267
|
+
}).withTypeProvider<TypeBoxTypeProvider>()
|
|
255
268
|
```
|
|
256
269
|
|
|
257
270
|
#### Schemas in JSON Files
|
|
@@ -390,7 +403,7 @@ definitions.
|
|
|
390
403
|
#### json-schema-to-ts
|
|
391
404
|
|
|
392
405
|
If you do not want to generate types from your schemas, but want to use them
|
|
393
|
-
|
|
406
|
+
directly from your code, you can use the package
|
|
394
407
|
[json-schema-to-ts](https://www.npmjs.com/package/json-schema-to-ts).
|
|
395
408
|
|
|
396
409
|
You can install it as dev-dependency.
|
|
@@ -648,6 +661,30 @@ However, there are a couple of suggestions to help improve this experience:
|
|
|
648
661
|
[npm-check](https://www.npmjs.com/package/npm-check) to verify plugin
|
|
649
662
|
dependencies are being used somewhere in your project.
|
|
650
663
|
|
|
664
|
+
Note that using `require` will not load the type definitions properly and may
|
|
665
|
+
cause type errors.
|
|
666
|
+
TypeScript can only identify the types that are directly imported into code,
|
|
667
|
+
which means that you can use require inline with import on top. For example:
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
import 'plugin' // here will trigger the type augmentation.
|
|
671
|
+
|
|
672
|
+
fastify.register(require('plugin'))
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
```typescript
|
|
676
|
+
import plugin from 'plugin' // here will trigger the type augmentation.
|
|
677
|
+
|
|
678
|
+
fastify.register(plugin)
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
Or even explicit config on tsconfig
|
|
682
|
+
```jsonc
|
|
683
|
+
{
|
|
684
|
+
"types": ["plugin"] // we force TypeScript to import the types
|
|
685
|
+
}
|
|
686
|
+
```
|
|
687
|
+
|
|
651
688
|
## Code Completion In Vanilla JavaScript
|
|
652
689
|
|
|
653
690
|
Vanilla JavaScript can use the published types to provide code completion (e.g.
|
|
@@ -1359,17 +1396,17 @@ A method for checking the existence of a type parser of a certain content type
|
|
|
1359
1396
|
|
|
1360
1397
|
##### fastify.FastifyError
|
|
1361
1398
|
|
|
1362
|
-
[src](https://github.com/fastify/fastify/blob/main/
|
|
1399
|
+
[src](https://github.com/fastify/fastify/blob/main/fastify.d.ts#L179)
|
|
1363
1400
|
|
|
1364
1401
|
FastifyError is a custom error object that includes status code and validation
|
|
1365
1402
|
results.
|
|
1366
1403
|
|
|
1367
1404
|
It extends the Node.js `Error` type, and adds two additional, optional
|
|
1368
|
-
properties: `statusCode: number` and `validation:
|
|
1405
|
+
properties: `statusCode: number` and `validation: ValidationResult[]`.
|
|
1369
1406
|
|
|
1370
1407
|
##### fastify.ValidationResult
|
|
1371
1408
|
|
|
1372
|
-
[src](https://github.com/fastify/fastify/blob/main/
|
|
1409
|
+
[src](https://github.com/fastify/fastify/blob/main/fastify.d.ts#L184)
|
|
1373
1410
|
|
|
1374
1411
|
The route validation internally relies upon Ajv, which is a high-performance
|
|
1375
1412
|
JSON schema validator.
|
|
@@ -484,6 +484,17 @@ fastify.post('/the/url', {
|
|
|
484
484
|
}, handler)
|
|
485
485
|
```
|
|
486
486
|
|
|
487
|
+
##### .statusCode property
|
|
488
|
+
|
|
489
|
+
All validation errors will be added a `.statusCode` property set to `400`. This guarantees
|
|
490
|
+
that the default error handler will set the status code of the response to `400`.
|
|
491
|
+
|
|
492
|
+
```js
|
|
493
|
+
fastify.setErrorHandler(function (error, request, reply) {
|
|
494
|
+
request.log.error(error, `This error has status code ${error.statusCode}`)
|
|
495
|
+
reply.status(error.statusCode).send(error)
|
|
496
|
+
})
|
|
497
|
+
```
|
|
487
498
|
|
|
488
499
|
##### Validation messages with other validation libraries
|
|
489
500
|
|
package/docs/index.md
CHANGED
|
@@ -16,7 +16,7 @@ Complete newcomers to Fastify should first read our [Getting
|
|
|
16
16
|
Started](./Guides/Getting-Started.md) guide.
|
|
17
17
|
|
|
18
18
|
Developers experienced with Fastify should consult the [reference
|
|
19
|
-
documentation](./Reference/
|
|
19
|
+
documentation](./Reference/Index.md) directly to find the topic they are seeking
|
|
20
20
|
more information about.
|
|
21
21
|
|
|
22
22
|
## Additional Documentation
|
package/fastify.d.ts
CHANGED
|
@@ -146,7 +146,7 @@ export type FastifyServerOptions<
|
|
|
146
146
|
},
|
|
147
147
|
schemaController?: {
|
|
148
148
|
bucket?: (parentSchemas?: unknown) => {
|
|
149
|
-
|
|
149
|
+
add(schema: unknown): FastifyInstance;
|
|
150
150
|
getSchema(schemaId: string): unknown;
|
|
151
151
|
getSchemas(): Record<string, unknown>;
|
|
152
152
|
};
|
|
@@ -183,10 +183,10 @@ declare module '@fastify/error' {
|
|
|
183
183
|
|
|
184
184
|
export interface ValidationResult {
|
|
185
185
|
keyword: string;
|
|
186
|
-
|
|
186
|
+
instancePath: string;
|
|
187
187
|
schemaPath: string;
|
|
188
188
|
params: Record<string, string | string[]>;
|
|
189
|
-
message
|
|
189
|
+
message?: string;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/* Export all additional types */
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '4.
|
|
3
|
+
const VERSION = '4.2.1'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('http')
|
|
@@ -237,35 +237,35 @@ function fastify (options) {
|
|
|
237
237
|
getDefaultRoute: router.getDefaultRoute.bind(router),
|
|
238
238
|
setDefaultRoute: router.setDefaultRoute.bind(router),
|
|
239
239
|
// routes shorthand methods
|
|
240
|
-
delete: function _delete (url,
|
|
241
|
-
return router.prepareRoute.call(this, 'DELETE', url,
|
|
240
|
+
delete: function _delete (url, options, handler) {
|
|
241
|
+
return router.prepareRoute.call(this, { method: 'DELETE', url, options, handler })
|
|
242
242
|
},
|
|
243
|
-
get: function _get (url,
|
|
244
|
-
return router.prepareRoute.call(this, 'GET', url,
|
|
243
|
+
get: function _get (url, options, handler) {
|
|
244
|
+
return router.prepareRoute.call(this, { method: 'GET', url, options, handler })
|
|
245
245
|
},
|
|
246
|
-
head: function _head (url,
|
|
247
|
-
return router.prepareRoute.call(this, 'HEAD', url,
|
|
246
|
+
head: function _head (url, options, handler) {
|
|
247
|
+
return router.prepareRoute.call(this, { method: 'HEAD', url, options, handler })
|
|
248
248
|
},
|
|
249
|
-
patch: function _patch (url,
|
|
250
|
-
return router.prepareRoute.call(this, 'PATCH', url,
|
|
249
|
+
patch: function _patch (url, options, handler) {
|
|
250
|
+
return router.prepareRoute.call(this, { method: 'PATCH', url, options, handler })
|
|
251
251
|
},
|
|
252
|
-
post: function _post (url,
|
|
253
|
-
return router.prepareRoute.call(this, 'POST', url,
|
|
252
|
+
post: function _post (url, options, handler) {
|
|
253
|
+
return router.prepareRoute.call(this, { method: 'POST', url, options, handler })
|
|
254
254
|
},
|
|
255
|
-
put: function _put (url,
|
|
256
|
-
return router.prepareRoute.call(this, 'PUT', url,
|
|
255
|
+
put: function _put (url, options, handler) {
|
|
256
|
+
return router.prepareRoute.call(this, { method: 'PUT', url, options, handler })
|
|
257
257
|
},
|
|
258
|
-
options: function _options (url,
|
|
259
|
-
return router.prepareRoute.call(this, 'OPTIONS', url,
|
|
258
|
+
options: function _options (url, options, handler) {
|
|
259
|
+
return router.prepareRoute.call(this, { method: 'OPTIONS', url, options, handler })
|
|
260
260
|
},
|
|
261
|
-
all: function _all (url,
|
|
262
|
-
return router.prepareRoute.call(this, supportedMethods, url,
|
|
261
|
+
all: function _all (url, options, handler) {
|
|
262
|
+
return router.prepareRoute.call(this, { method: supportedMethods, url, options, handler })
|
|
263
263
|
},
|
|
264
264
|
// extended route
|
|
265
|
-
route: function _route (
|
|
265
|
+
route: function _route (options) {
|
|
266
266
|
// we need the fastify object that we are producing so we apply a lazy loading of the function,
|
|
267
267
|
// otherwise we should bind it after the declaration
|
|
268
|
-
return router.route.call(this,
|
|
268
|
+
return router.route.call(this, { options })
|
|
269
269
|
},
|
|
270
270
|
// expose logger instance
|
|
271
271
|
log: logger,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const Fastify = require('../fastify')
|
|
2
|
+
|
|
3
|
+
const fastify = Fastify()
|
|
4
|
+
|
|
5
|
+
fastify.listen({
|
|
6
|
+
host: '::',
|
|
7
|
+
port: 3000
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
fastify.get('/', async function (request, reply) {
|
|
11
|
+
reply.code(200).send({ data: 'home page' })
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
fastify.post('/post/:id', async function (request, reply) {
|
|
15
|
+
const { id } = request.params
|
|
16
|
+
reply.code(201).send({ data: `${id}` })
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
fastify.put('/put/:id', async function (request, reply) {
|
|
20
|
+
const { id } = request.params
|
|
21
|
+
reply.code(200).send({ data: `${id}` })
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
fastify.delete('/delete/:id', async function (request, reply) {
|
|
25
|
+
const { id } = request.params
|
|
26
|
+
reply.code(204).send({ data: `${id}` })
|
|
27
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
NUMBER=$RANDOM
|
|
6
|
+
curl -i -X GET -H 'Content-Type: application/json' localhost:3000/ > GET
|
|
7
|
+
if [[ ! $(cat GET | head -1| cut -f2 -d" ") == "200" || ! $(cat GET | tail -1| cut -f4 -d"\"") == "home page" ]] ; then
|
|
8
|
+
exit 1
|
|
9
|
+
fi;
|
|
10
|
+
curl -i -X POST -H 'Content-Type: application/json' localhost:3000/post/$NUMBER --data {} > POST
|
|
11
|
+
if [[ ! $(cat POST | head -1| cut -f2 -d" ") == "201" || ! $(cat POST | tail -1| cut -f4 -d"\"") == $(echo $NUMBER) ]]; then
|
|
12
|
+
exit 1
|
|
13
|
+
fi;
|
|
14
|
+
curl -i -X PUT -H 'Content-Type: application/json' localhost:3000/put/$NUMBER --data {} > PUT
|
|
15
|
+
if [[ ! $(cat PUT | head -1| cut -f2 -d" ") == "200" || ! $(cat PUT | tail -1| cut -f4 -d"\"") == $(echo $NUMBER) ]]; then
|
|
16
|
+
exit 1
|
|
17
|
+
fi;
|
|
18
|
+
curl -i -X DELETE -H 'Content-Type: application/json' localhost:3000/delete/$NUMBER --data {} > DELETE
|
|
19
|
+
if [[ ! $(cat DELETE | head -1| cut -f2 -d" ") == "204" ]]; then
|
|
20
|
+
exit 1
|
|
21
|
+
fi;
|
|
22
|
+
|
|
23
|
+
rm -f GET POST PUT DELETE
|
package/lib/context.js
CHANGED
|
@@ -9,7 +9,8 @@ const {
|
|
|
9
9
|
kRequest,
|
|
10
10
|
kBodyLimit,
|
|
11
11
|
kLogLevel,
|
|
12
|
-
kContentTypeParser
|
|
12
|
+
kContentTypeParser,
|
|
13
|
+
kRouteByFastify
|
|
13
14
|
} = require('./symbols.js')
|
|
14
15
|
|
|
15
16
|
// Objects that holds the context of every request
|
|
@@ -25,7 +26,8 @@ function Context ({
|
|
|
25
26
|
attachValidation,
|
|
26
27
|
replySerializer,
|
|
27
28
|
schemaErrorFormatter,
|
|
28
|
-
server
|
|
29
|
+
server,
|
|
30
|
+
isFastify
|
|
29
31
|
}) {
|
|
30
32
|
this.schema = schema
|
|
31
33
|
this.handler = handler
|
|
@@ -50,6 +52,7 @@ function Context ({
|
|
|
50
52
|
this.attachValidation = attachValidation
|
|
51
53
|
this[kReplySerializerDefault] = replySerializer
|
|
52
54
|
this.schemaErrorFormatter = schemaErrorFormatter || server[kSchemaErrorFormatter] || defaultSchemaErrorFormatter
|
|
55
|
+
this[kRouteByFastify] = isFastify
|
|
53
56
|
|
|
54
57
|
this.server = server
|
|
55
58
|
}
|
package/lib/error-serializer.js
CHANGED
|
@@ -39,7 +39,7 @@ class Serializer {
|
|
|
39
39
|
} else {
|
|
40
40
|
/* eslint no-undef: "off" */
|
|
41
41
|
const integer = this.parseInteger(i)
|
|
42
|
-
if (Number.isNaN(integer)) {
|
|
42
|
+
if (Number.isNaN(integer) || !Number.isFinite(integer)) {
|
|
43
43
|
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
44
44
|
} else {
|
|
45
45
|
return '' + integer
|
|
@@ -55,6 +55,8 @@ class Serializer {
|
|
|
55
55
|
const num = Number(i)
|
|
56
56
|
if (Number.isNaN(num)) {
|
|
57
57
|
throw new Error(`The value "${i}" cannot be converted to a number.`)
|
|
58
|
+
} else if (!Number.isFinite(num)) {
|
|
59
|
+
return null
|
|
58
60
|
} else {
|
|
59
61
|
return '' + num
|
|
60
62
|
}
|
|
@@ -72,44 +74,44 @@ class Serializer {
|
|
|
72
74
|
return bool === null ? 'null' : this.asBoolean(bool)
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
asDateTime (date) {
|
|
78
|
+
if (date === null) return '""'
|
|
77
79
|
if (date instanceof Date) {
|
|
78
|
-
return
|
|
80
|
+
return '"' + date.toISOString() + '"'
|
|
79
81
|
}
|
|
80
|
-
|
|
82
|
+
throw new Error(`The value "${date}" cannot be converted to a date-time.`)
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
return date === null ? 'null' : this.
|
|
85
|
+
asDateTimeNullable (date) {
|
|
86
|
+
return date === null ? 'null' : this.asDateTime(date)
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
asDate (date
|
|
88
|
-
|
|
89
|
+
asDate (date) {
|
|
90
|
+
if (date === null) return '""'
|
|
89
91
|
if (date instanceof Date) {
|
|
90
|
-
return
|
|
92
|
+
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + '"'
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
throw new Error(`The value "${date}" cannot be converted to a date.`)
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
asDateNullable (date
|
|
96
|
-
return date === null ? 'null' : this.asDate(date
|
|
97
|
+
asDateNullable (date) {
|
|
98
|
+
return date === null ? 'null' : this.asDate(date)
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
asTime (date
|
|
100
|
-
|
|
101
|
+
asTime (date) {
|
|
102
|
+
if (date === null) return '""'
|
|
101
103
|
if (date instanceof Date) {
|
|
102
|
-
return
|
|
104
|
+
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + '"'
|
|
103
105
|
}
|
|
104
|
-
|
|
106
|
+
throw new Error(`The value "${date}" cannot be converted to a time.`)
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
asTimeNullable (date
|
|
108
|
-
return date === null ? 'null' : this.asTime(date
|
|
109
|
+
asTimeNullable (date) {
|
|
110
|
+
return date === null ? 'null' : this.asTime(date)
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
asString (str
|
|
112
|
-
const quotes =
|
|
113
|
+
asString (str) {
|
|
114
|
+
const quotes = '"'
|
|
113
115
|
if (str instanceof Date) {
|
|
114
116
|
return quotes + str.toISOString() + quotes
|
|
115
117
|
} else if (str === null) {
|
|
@@ -119,11 +121,6 @@ class Serializer {
|
|
|
119
121
|
} else if (typeof str !== 'string') {
|
|
120
122
|
str = str.toString()
|
|
121
123
|
}
|
|
122
|
-
// If we skipQuotes it means that we are using it as test
|
|
123
|
-
// no need to test the string length for the render
|
|
124
|
-
if (skipQuotes) {
|
|
125
|
-
return str
|
|
126
|
-
}
|
|
127
124
|
|
|
128
125
|
if (str.length < 42) {
|
|
129
126
|
return this.asStringSmall(str)
|
|
@@ -185,7 +182,7 @@ class Serializer {
|
|
|
185
182
|
}
|
|
186
183
|
|
|
187
184
|
function anonymous0 (input) {
|
|
188
|
-
//
|
|
185
|
+
// #
|
|
189
186
|
|
|
190
187
|
var obj = (input && typeof input.toJSON === 'function')
|
|
191
188
|
? input.toJSON()
|
package/lib/handleRequest.js
CHANGED
package/lib/reply.js
CHANGED
|
@@ -203,10 +203,8 @@ Reply.prototype.getHeaders = function () {
|
|
|
203
203
|
|
|
204
204
|
Reply.prototype.hasHeader = function (key) {
|
|
205
205
|
key = key.toLowerCase()
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
return this.raw.hasHeader(key)
|
|
206
|
+
|
|
207
|
+
return this[kReplyHeaders][key] !== undefined || this.raw.hasHeader(key)
|
|
210
208
|
}
|
|
211
209
|
|
|
212
210
|
Reply.prototype.removeHeader = function (key) {
|
|
@@ -216,25 +214,24 @@ Reply.prototype.removeHeader = function (key) {
|
|
|
216
214
|
return this
|
|
217
215
|
}
|
|
218
216
|
|
|
219
|
-
Reply.prototype.header = function (key, value) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// default the value to ''
|
|
223
|
-
value = value === undefined ? '' : value
|
|
217
|
+
Reply.prototype.header = function (key, value = '') {
|
|
218
|
+
key = key.toLowerCase()
|
|
224
219
|
|
|
225
|
-
if (this[kReplyHeaders][
|
|
220
|
+
if (this[kReplyHeaders][key] && key === 'set-cookie') {
|
|
226
221
|
// https://tools.ietf.org/html/rfc7230#section-3.2.2
|
|
227
|
-
if (typeof this[kReplyHeaders][
|
|
228
|
-
this[kReplyHeaders][
|
|
222
|
+
if (typeof this[kReplyHeaders][key] === 'string') {
|
|
223
|
+
this[kReplyHeaders][key] = [this[kReplyHeaders][key]]
|
|
229
224
|
}
|
|
225
|
+
|
|
230
226
|
if (Array.isArray(value)) {
|
|
231
|
-
|
|
227
|
+
this[kReplyHeaders][key].push(...value)
|
|
232
228
|
} else {
|
|
233
|
-
this[kReplyHeaders][
|
|
229
|
+
this[kReplyHeaders][key].push(value)
|
|
234
230
|
}
|
|
235
231
|
} else {
|
|
236
|
-
this[kReplyHeaders][
|
|
232
|
+
this[kReplyHeaders][key] = value
|
|
237
233
|
}
|
|
234
|
+
|
|
238
235
|
return this
|
|
239
236
|
}
|
|
240
237
|
|
|
@@ -245,6 +242,7 @@ Reply.prototype.headers = function (headers) {
|
|
|
245
242
|
const key = keys[i]
|
|
246
243
|
this.header(key, headers[key])
|
|
247
244
|
}
|
|
245
|
+
|
|
248
246
|
return this
|
|
249
247
|
}
|
|
250
248
|
|
|
@@ -279,8 +277,7 @@ Reply.prototype.trailer = function (key, fn) {
|
|
|
279
277
|
}
|
|
280
278
|
|
|
281
279
|
Reply.prototype.hasTrailer = function (key) {
|
|
282
|
-
|
|
283
|
-
return this[kReplyTrailers][key.toLowerCase()] !== undefined
|
|
280
|
+
return this[kReplyTrailers]?.[key.toLowerCase()] !== undefined
|
|
284
281
|
}
|
|
285
282
|
|
|
286
283
|
Reply.prototype.removeTrailer = function (key) {
|
|
@@ -330,11 +327,12 @@ Reply.prototype.redirect = function (code, url) {
|
|
|
330
327
|
code = this[kReplyHasStatusCode] ? this.raw.statusCode : 302
|
|
331
328
|
}
|
|
332
329
|
|
|
333
|
-
this.header('location', url).code(code).send()
|
|
330
|
+
return this.header('location', url).code(code).send()
|
|
334
331
|
}
|
|
335
332
|
|
|
336
333
|
Reply.prototype.callNotFound = function () {
|
|
337
334
|
notFound(this)
|
|
335
|
+
return this
|
|
338
336
|
}
|
|
339
337
|
|
|
340
338
|
Reply.prototype.getResponseTime = function () {
|
|
@@ -483,9 +481,12 @@ function onSendEnd (reply, payload) {
|
|
|
483
481
|
}
|
|
484
482
|
|
|
485
483
|
if (reply[kReplyTrailers] === null) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
484
|
+
const contentLength = reply[kReplyHeaders]['content-length']
|
|
485
|
+
if (!contentLength ||
|
|
486
|
+
(req.raw.method !== 'HEAD' &&
|
|
487
|
+
parseInt(contentLength, 10) !== Buffer.byteLength(payload)
|
|
488
|
+
)
|
|
489
|
+
) {
|
|
489
490
|
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
|
|
490
491
|
}
|
|
491
492
|
}
|