fastify 4.13.0 → 4.14.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/docs/Guides/Ecosystem.md +2 -0
- package/docs/Reference/Hooks.md +21 -0
- package/docs/Reference/LTS.md +1 -1
- package/docs/Reference/Server.md +5 -2
- package/fastify.d.ts +9 -9
- package/fastify.js +5 -1
- package/lib/context.js +5 -4
- package/lib/error-handler.js +3 -6
- package/lib/error-serializer.js +49 -68
- package/lib/errors.js +4 -0
- package/lib/hooks.js +41 -1
- package/lib/reply.js +9 -9
- package/lib/request.js +9 -9
- package/lib/route.js +41 -18
- package/lib/symbols.js +2 -2
- package/package.json +3 -3
- package/test/close.test.js +35 -0
- package/test/hooks-async.test.js +12 -0
- package/test/hooks.test.js +234 -0
- package/test/internals/context.test.js +33 -0
- package/test/internals/reply-serialize.test.js +7 -7
- package/test/internals/reply.test.js +3 -1
- package/test/internals/request-validate.test.js +7 -7
- package/test/internals/request.test.js +2 -0
- package/test/logger.test.js +10 -8
- package/test/route.test.js +14 -0
- package/test/schema-serialization.test.js +3 -2
- package/test/types/fastify.test-d.ts +1 -1
- package/test/types/hooks.test-d.ts +13 -0
- package/test/types/schema.test-d.ts +37 -5
- package/types/errors.d.ts +1 -0
- package/types/hooks.d.ts +38 -0
- package/types/instance.d.ts +29 -4
- package/types/route.d.ts +4 -4
- package/types/schema.d.ts +9 -4
package/docs/Guides/Ecosystem.md
CHANGED
|
@@ -648,5 +648,7 @@ section.
|
|
|
648
648
|
Reusable workflows for use in the Fastify plugin
|
|
649
649
|
- [`fast-maker`](https://github.com/imjuni/fast-maker) route configuration
|
|
650
650
|
generator by directory structure.
|
|
651
|
+
- [`fastify-flux`](https://github.com/Jnig/fastify-flux) Tool for building
|
|
652
|
+
Fastify APIs using decorators and convert Typescript interface to JSON Schema.
|
|
651
653
|
- [`simple-tjscli`](https://github.com/imjuni/simple-tjscli) CLI tool to
|
|
652
654
|
generate JSON Schema from TypeScript interfaces.
|
package/docs/Reference/Hooks.md
CHANGED
|
@@ -19,6 +19,7 @@ are Request/Reply hooks and application hooks:
|
|
|
19
19
|
- [onSend](#onsend)
|
|
20
20
|
- [onResponse](#onresponse)
|
|
21
21
|
- [onTimeout](#ontimeout)
|
|
22
|
+
- [onRequestAbort](#onrequestabort)
|
|
22
23
|
- [Manage Errors from a hook](#manage-errors-from-a-hook)
|
|
23
24
|
- [Respond to a request from a hook](#respond-to-a-request-from-a-hook)
|
|
24
25
|
- [Application Hooks](#application-hooks)
|
|
@@ -267,6 +268,26 @@ service (if the `connectionTimeout` property is set on the Fastify instance).
|
|
|
267
268
|
The `onTimeout` hook is executed when a request is timed out and the HTTP socket
|
|
268
269
|
has been hanged up. Therefore, you will not be able to send data to the client.
|
|
269
270
|
|
|
271
|
+
### onRequestAbort
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
fastify.addHook('onRequestAbort', (request, reply, done) => {
|
|
275
|
+
// Some code
|
|
276
|
+
done()
|
|
277
|
+
})
|
|
278
|
+
```
|
|
279
|
+
Or `async/await`:
|
|
280
|
+
```js
|
|
281
|
+
fastify.addHook('onRequestAbort', async (request, reply) => {
|
|
282
|
+
// Some code
|
|
283
|
+
await asyncMethod()
|
|
284
|
+
})
|
|
285
|
+
```
|
|
286
|
+
The `onRequestAbort` hook is executed when a client closes the connection before
|
|
287
|
+
the entire request has been processed. Therefore, you will not be able to send
|
|
288
|
+
data to the client.
|
|
289
|
+
|
|
290
|
+
**Notice:** client abort detection is not completely reliable. See: [`Detecting-When-Clients-Abort.md`](../Guides/Detecting-When-Clients-Abort.md)
|
|
270
291
|
|
|
271
292
|
### Manage Errors from a hook
|
|
272
293
|
If you get an error during the execution of your hook, just pass it to `done()`
|
package/docs/Reference/LTS.md
CHANGED
|
@@ -62,7 +62,7 @@ YAML workflow labels below:
|
|
|
62
62
|
| OS | YAML Workflow Label | Package Manager | Node.js |
|
|
63
63
|
|---------|------------------------|---------------------------|--------------|
|
|
64
64
|
| Linux | `ubuntu-latest` | npm | 14,16,18 |
|
|
65
|
-
| Linux | `ubuntu-
|
|
65
|
+
| Linux | `ubuntu-latest` | yarn,pnpm | 14,16,18 |
|
|
66
66
|
| Windows | `windows-latest` | npm | 14,16,18 |
|
|
67
67
|
| MacOS | `macos-latest` | npm | 14,16,18 |
|
|
68
68
|
|
package/docs/Reference/Server.md
CHANGED
|
@@ -382,7 +382,9 @@ fastify.addHook('onResponse', (req, reply, done) => {
|
|
|
382
382
|
```
|
|
383
383
|
|
|
384
384
|
Please note that this setting will also disable an error log written by the
|
|
385
|
-
default `onResponse` hook on reply callback errors.
|
|
385
|
+
default `onResponse` hook on reply callback errors. Other log messages
|
|
386
|
+
emitted by Fastify will stay enabled, like deprecation warnings and messages
|
|
387
|
+
emitted when requests are received while the server is closing.
|
|
386
388
|
|
|
387
389
|
### `serverFactory`
|
|
388
390
|
<a id="custom-http-server"></a>
|
|
@@ -736,7 +738,8 @@ Fastify provides default error handlers for the most common use cases. It is
|
|
|
736
738
|
possible to override one or more of those handlers with custom code using this
|
|
737
739
|
option.
|
|
738
740
|
|
|
739
|
-
*Note: Only `FST_ERR_BAD_URL`
|
|
741
|
+
*Note: Only `FST_ERR_BAD_URL` and `FST_ERR_ASYNC_CONSTRAINT` are implemented at
|
|
742
|
+
the moment.*
|
|
740
743
|
|
|
741
744
|
```js
|
|
742
745
|
const fastify = require('fastify')({
|
package/fastify.d.ts
CHANGED
|
@@ -3,16 +3,16 @@ import * as http2 from 'http2'
|
|
|
3
3
|
import * as https from 'https'
|
|
4
4
|
import { Socket } from 'net'
|
|
5
5
|
|
|
6
|
-
import { Options as AjvOptions,
|
|
6
|
+
import { Options as AjvOptions, ValidatorFactory } from '@fastify/ajv-compiler'
|
|
7
7
|
import { FastifyError } from '@fastify/error'
|
|
8
|
-
import { Options as FJSOptions,
|
|
8
|
+
import { Options as FJSOptions, SerializerFactory } from '@fastify/fast-json-stringify-compiler'
|
|
9
9
|
import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
|
|
10
10
|
import { Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse, CallbackFunc as LightMyRequestCallback } from 'light-my-request'
|
|
11
11
|
|
|
12
12
|
import { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser, ProtoAction, ConstructorAction } from './types/content-type-parser'
|
|
13
13
|
import { FastifyContext, FastifyContextConfig } from './types/context'
|
|
14
14
|
import { FastifyErrorCodes } from './types/errors'
|
|
15
|
-
import { DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler } from './types/hooks'
|
|
15
|
+
import { DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler } from './types/hooks'
|
|
16
16
|
import { FastifyListenOptions, FastifyInstance, PrintRoutesOptions } from './types/instance'
|
|
17
17
|
import { FastifyBaseLogger, FastifyLoggerInstance, FastifyLoggerOptions, PinoLoggerOptions, FastifyLogFn, LogLevel } from './types/logger'
|
|
18
18
|
import { FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin } from './types/plugin'
|
|
@@ -20,7 +20,7 @@ import { FastifyRegister, FastifyRegisterOptions, RegisterOptions } from './type
|
|
|
20
20
|
import { FastifyReply } from './types/reply'
|
|
21
21
|
import { FastifyRequest, RequestGenericInterface } from './types/request'
|
|
22
22
|
import { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler, RouteGenericInterface } from './types/route'
|
|
23
|
-
import { FastifySchema, FastifySchemaCompiler,
|
|
23
|
+
import { FastifySchema, FastifySchemaCompiler, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema'
|
|
24
24
|
import { FastifyServerFactory, FastifyServerFactoryHandler } from './types/serverFactory'
|
|
25
25
|
import { FastifyTypeProvider, FastifyTypeProviderDefault } from './types/type-provider'
|
|
26
26
|
import { HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './types/utils'
|
|
@@ -28,7 +28,7 @@ import { HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaul
|
|
|
28
28
|
declare module '@fastify/error' {
|
|
29
29
|
interface FastifyError {
|
|
30
30
|
validation?: fastify.ValidationResult[];
|
|
31
|
-
validationContext?:
|
|
31
|
+
validationContext?: SchemaErrorDataVar;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -134,8 +134,8 @@ declare namespace fastify {
|
|
|
134
134
|
getSchemas(): Record<string, unknown>;
|
|
135
135
|
};
|
|
136
136
|
compilersFactory?: {
|
|
137
|
-
buildValidator?:
|
|
138
|
-
buildSerializer?:
|
|
137
|
+
buildValidator?: ValidatorFactory;
|
|
138
|
+
buildSerializer?: SerializerFactory;
|
|
139
139
|
};
|
|
140
140
|
};
|
|
141
141
|
return503OnClosing?: boolean,
|
|
@@ -149,7 +149,7 @@ declare namespace fastify {
|
|
|
149
149
|
res: FastifyReply<RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>, RequestGeneric, FastifyContextConfig, SchemaCompiler, TypeProvider>
|
|
150
150
|
) => void,
|
|
151
151
|
rewriteUrl?: (req: RawRequestDefaultExpression<RawServer>) => string,
|
|
152
|
-
schemaErrorFormatter?:
|
|
152
|
+
schemaErrorFormatter?: SchemaErrorFormatter,
|
|
153
153
|
/**
|
|
154
154
|
* listener to error events emitted by client connections
|
|
155
155
|
*/
|
|
@@ -179,7 +179,7 @@ declare namespace fastify {
|
|
|
179
179
|
FastifyError, // '@fastify/error'
|
|
180
180
|
FastifySchema, FastifySchemaCompiler, // './types/schema'
|
|
181
181
|
HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault, // './types/utils'
|
|
182
|
-
DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, // './types/hooks'
|
|
182
|
+
DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler, // './types/hooks'
|
|
183
183
|
FastifyServerFactory, FastifyServerFactoryHandler, // './types/serverFactory'
|
|
184
184
|
FastifyTypeProvider, FastifyTypeProviderDefault, // './types/type-provider'
|
|
185
185
|
FastifyErrorCodes, // './types/errors'
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '4.
|
|
3
|
+
const VERSION = '4.14.1'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('http')
|
|
@@ -588,6 +588,10 @@ function fastify (options) {
|
|
|
588
588
|
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 0) {
|
|
589
589
|
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
590
590
|
}
|
|
591
|
+
} else if (name === 'onRequestAbort') {
|
|
592
|
+
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 1) {
|
|
593
|
+
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
594
|
+
}
|
|
591
595
|
} else {
|
|
592
596
|
if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
|
|
593
597
|
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
package/lib/context.js
CHANGED
|
@@ -11,8 +11,8 @@ const {
|
|
|
11
11
|
kLogLevel,
|
|
12
12
|
kContentTypeParser,
|
|
13
13
|
kRouteByFastify,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
kRequestCacheValidateFns,
|
|
15
|
+
kReplyCacheSerializeFns,
|
|
16
16
|
kPublicRouteContext
|
|
17
17
|
} = require('./symbols.js')
|
|
18
18
|
|
|
@@ -48,6 +48,7 @@ function Context ({
|
|
|
48
48
|
this.preHandler = null
|
|
49
49
|
this.onResponse = null
|
|
50
50
|
this.preSerialization = null
|
|
51
|
+
this.onRequestAbort = null
|
|
51
52
|
this.config = config
|
|
52
53
|
this.errorHandler = errorHandler || server[kErrorHandler]
|
|
53
54
|
this._middie = null
|
|
@@ -67,8 +68,8 @@ function Context ({
|
|
|
67
68
|
defaultSchemaErrorFormatter
|
|
68
69
|
this[kRouteByFastify] = isFastify
|
|
69
70
|
|
|
70
|
-
this[
|
|
71
|
-
this[
|
|
71
|
+
this[kRequestCacheValidateFns] = null
|
|
72
|
+
this[kReplyCacheSerializeFns] = null
|
|
72
73
|
this.validatorCompiler = validatorCompiler || null
|
|
73
74
|
this.serializerCompiler = serializerCompiler || null
|
|
74
75
|
|
package/lib/error-handler.js
CHANGED
|
@@ -11,7 +11,8 @@ const {
|
|
|
11
11
|
} = require('./symbols.js')
|
|
12
12
|
|
|
13
13
|
const {
|
|
14
|
-
FST_ERR_REP_INVALID_PAYLOAD_TYPE
|
|
14
|
+
FST_ERR_REP_INVALID_PAYLOAD_TYPE,
|
|
15
|
+
FST_ERR_FAILED_ERROR_SERIALIZATION
|
|
15
16
|
} = require('./errors')
|
|
16
17
|
|
|
17
18
|
const { getSchemaSerializer } = require('./schemas')
|
|
@@ -113,11 +114,7 @@ function fallbackErrorHandler (error, reply, cb) {
|
|
|
113
114
|
// error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization
|
|
114
115
|
reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed')
|
|
115
116
|
reply.code(500)
|
|
116
|
-
payload = serializeError(
|
|
117
|
-
error: statusCodes['500'],
|
|
118
|
-
message: err.message,
|
|
119
|
-
statusCode: 500
|
|
120
|
-
})
|
|
117
|
+
payload = serializeError(new FST_ERR_FAILED_ERROR_SERIALIZATION(err.message, error.message))
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
|
package/lib/error-serializer.js
CHANGED
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
/* istanbul ignore file */
|
|
3
3
|
|
|
4
4
|
'use strict'
|
|
5
|
-
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
// eslint-disable-next-line
|
|
9
8
|
const STR_ESCAPE = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/
|
|
10
9
|
|
|
11
10
|
class Serializer {
|
|
12
|
-
constructor (options
|
|
13
|
-
switch (options.rounding) {
|
|
11
|
+
constructor (options) {
|
|
12
|
+
switch (options && options.rounding) {
|
|
14
13
|
case 'floor':
|
|
15
14
|
this.parseInteger = Math.floor
|
|
16
15
|
break
|
|
@@ -20,6 +19,7 @@ class Serializer {
|
|
|
20
19
|
case 'round':
|
|
21
20
|
this.parseInteger = Math.round
|
|
22
21
|
break
|
|
22
|
+
case 'trunc':
|
|
23
23
|
default:
|
|
24
24
|
this.parseInteger = Math.trunc
|
|
25
25
|
break
|
|
@@ -27,17 +27,28 @@ class Serializer {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
asInteger (i) {
|
|
30
|
-
if (typeof i === '
|
|
30
|
+
if (typeof i === 'number') {
|
|
31
|
+
if (i === Infinity || i === -Infinity) {
|
|
32
|
+
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
33
|
+
}
|
|
34
|
+
if (Number.isInteger(i)) {
|
|
35
|
+
return '' + i
|
|
36
|
+
}
|
|
37
|
+
if (Number.isNaN(i)) {
|
|
38
|
+
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
39
|
+
}
|
|
40
|
+
return this.parseInteger(i)
|
|
41
|
+
} else if (i === null) {
|
|
42
|
+
return '0'
|
|
43
|
+
} else if (typeof i === 'bigint') {
|
|
31
44
|
return i.toString()
|
|
32
|
-
} else if (Number.isInteger(i)) {
|
|
33
|
-
return '' + i
|
|
34
45
|
} else {
|
|
35
46
|
/* eslint no-undef: "off" */
|
|
36
47
|
const integer = this.parseInteger(i)
|
|
37
|
-
if (Number.
|
|
38
|
-
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
39
|
-
} else {
|
|
48
|
+
if (Number.isFinite(integer)) {
|
|
40
49
|
return '' + integer
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
41
52
|
}
|
|
42
53
|
}
|
|
43
54
|
}
|
|
@@ -91,23 +102,24 @@ class Serializer {
|
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
asString (str) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
if (typeof str !== 'string') {
|
|
106
|
+
if (str === null) {
|
|
107
|
+
return '""'
|
|
108
|
+
}
|
|
109
|
+
if (str instanceof Date) {
|
|
110
|
+
return '"' + str.toISOString() + '"'
|
|
111
|
+
}
|
|
112
|
+
if (str instanceof RegExp) {
|
|
113
|
+
str = str.source
|
|
114
|
+
} else {
|
|
115
|
+
str = str.toString()
|
|
116
|
+
}
|
|
103
117
|
}
|
|
104
118
|
|
|
105
119
|
// Fast escape chars check
|
|
106
120
|
if (!STR_ESCAPE.test(str)) {
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (str.length < 42) {
|
|
121
|
+
return '"' + str + '"'
|
|
122
|
+
} else if (str.length < 42) {
|
|
111
123
|
return this.asStringSmall(str)
|
|
112
124
|
} else {
|
|
113
125
|
return JSON.stringify(str)
|
|
@@ -151,81 +163,50 @@ class Serializer {
|
|
|
151
163
|
}
|
|
152
164
|
|
|
153
165
|
|
|
154
|
-
|
|
155
|
-
const serializer = new Serializer({"mode":"standalone"})
|
|
166
|
+
const serializer = new Serializer()
|
|
156
167
|
|
|
157
168
|
|
|
158
169
|
|
|
159
|
-
function main (input) {
|
|
160
|
-
let json = ''
|
|
161
|
-
json += anonymous0(input)
|
|
162
|
-
return json
|
|
163
|
-
}
|
|
164
170
|
|
|
165
171
|
function anonymous0 (input) {
|
|
166
172
|
// #
|
|
167
173
|
|
|
168
|
-
|
|
174
|
+
const obj = (input && typeof input.toJSON === 'function')
|
|
169
175
|
? input.toJSON()
|
|
170
176
|
: input
|
|
171
177
|
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
let json = '{'
|
|
179
|
+
let addComma = false
|
|
174
180
|
|
|
175
181
|
if (obj["statusCode"] !== undefined) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
json += ','
|
|
179
|
-
} else {
|
|
180
|
-
addComma = true
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
json += "\"statusCode\"" + ':'
|
|
182
|
+
!addComma && (addComma = true) || (json += ',')
|
|
183
|
+
json += "\"statusCode\":"
|
|
184
184
|
json += serializer.asNumber(obj["statusCode"])
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
if (obj["code"] !== undefined) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
json += ','
|
|
191
|
-
} else {
|
|
192
|
-
addComma = true
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
json += "\"code\"" + ':'
|
|
188
|
+
!addComma && (addComma = true) || (json += ',')
|
|
189
|
+
json += "\"code\":"
|
|
196
190
|
json += serializer.asString(obj["code"])
|
|
197
191
|
}
|
|
198
192
|
|
|
199
193
|
if (obj["error"] !== undefined) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
json += ','
|
|
203
|
-
} else {
|
|
204
|
-
addComma = true
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
json += "\"error\"" + ':'
|
|
194
|
+
!addComma && (addComma = true) || (json += ',')
|
|
195
|
+
json += "\"error\":"
|
|
208
196
|
json += serializer.asString(obj["error"])
|
|
209
197
|
}
|
|
210
198
|
|
|
211
199
|
if (obj["message"] !== undefined) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
json += ','
|
|
215
|
-
} else {
|
|
216
|
-
addComma = true
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
json += "\"message\"" + ':'
|
|
200
|
+
!addComma && (addComma = true) || (json += ',')
|
|
201
|
+
json += "\"message\":"
|
|
220
202
|
json += serializer.asString(obj["message"])
|
|
221
203
|
}
|
|
222
204
|
|
|
223
|
-
json
|
|
224
|
-
return json
|
|
205
|
+
return json + '}'
|
|
225
206
|
}
|
|
226
207
|
|
|
208
|
+
const main = anonymous0
|
|
209
|
+
|
|
227
210
|
|
|
228
|
-
|
|
229
211
|
|
|
230
212
|
module.exports = main
|
|
231
|
-
|
package/lib/errors.js
CHANGED
|
@@ -214,6 +214,10 @@ const codes = {
|
|
|
214
214
|
'FST_ERR_BAD_TRAILER_VALUE',
|
|
215
215
|
"Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function."
|
|
216
216
|
),
|
|
217
|
+
FST_ERR_FAILED_ERROR_SERIALIZATION: createError(
|
|
218
|
+
'FST_ERR_FAILED_ERROR_SERIALIZATION',
|
|
219
|
+
'Failed to serialize an error. Error: %s. Original error: %s'
|
|
220
|
+
),
|
|
217
221
|
FST_ERR_MISSING_SERIALIZATION_FN: createError(
|
|
218
222
|
'FST_ERR_MISSING_SERIALIZATION_FN',
|
|
219
223
|
'Missing serialization function. Key "%s"'
|
package/lib/hooks.js
CHANGED
|
@@ -15,7 +15,8 @@ const lifecycleHooks = [
|
|
|
15
15
|
'preHandler',
|
|
16
16
|
'onSend',
|
|
17
17
|
'onResponse',
|
|
18
|
-
'onError'
|
|
18
|
+
'onError',
|
|
19
|
+
'onRequestAbort'
|
|
19
20
|
]
|
|
20
21
|
const supportedHooks = lifecycleHooks.concat(applicationHooks)
|
|
21
22
|
const {
|
|
@@ -46,6 +47,7 @@ function Hooks () {
|
|
|
46
47
|
this.onRegister = []
|
|
47
48
|
this.onReady = []
|
|
48
49
|
this.onTimeout = []
|
|
50
|
+
this.onRequestAbort = []
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
Hooks.prototype.validate = function (hook, fn) {
|
|
@@ -74,6 +76,7 @@ function buildHooks (h) {
|
|
|
74
76
|
hooks.onRoute = h.onRoute.slice()
|
|
75
77
|
hooks.onRegister = h.onRegister.slice()
|
|
76
78
|
hooks.onTimeout = h.onTimeout.slice()
|
|
79
|
+
hooks.onRequestAbort = h.onRequestAbort.slice()
|
|
77
80
|
hooks.onReady = []
|
|
78
81
|
return hooks
|
|
79
82
|
}
|
|
@@ -246,6 +249,42 @@ function onSendHookRunner (functions, request, reply, payload, cb) {
|
|
|
246
249
|
next()
|
|
247
250
|
}
|
|
248
251
|
|
|
252
|
+
function onRequestAbortHookRunner (functions, runner, request, cb) {
|
|
253
|
+
let i = 0
|
|
254
|
+
|
|
255
|
+
function next (err) {
|
|
256
|
+
if (err || i === functions.length) {
|
|
257
|
+
cb(err, request)
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let result
|
|
262
|
+
try {
|
|
263
|
+
result = runner(functions[i++], request, next)
|
|
264
|
+
} catch (error) {
|
|
265
|
+
next(error)
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
if (result && typeof result.then === 'function') {
|
|
269
|
+
result.then(handleResolve, handleReject)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function handleResolve () {
|
|
274
|
+
next()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function handleReject (err) {
|
|
278
|
+
if (!err) {
|
|
279
|
+
err = new FST_ERR_SEND_UNDEFINED_ERR()
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
cb(err, request)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
next()
|
|
286
|
+
}
|
|
287
|
+
|
|
249
288
|
function hookIterator (fn, request, reply, next) {
|
|
250
289
|
if (reply.sent === true) return undefined
|
|
251
290
|
return fn(request, reply, next)
|
|
@@ -256,6 +295,7 @@ module.exports = {
|
|
|
256
295
|
buildHooks,
|
|
257
296
|
hookRunner,
|
|
258
297
|
onSendHookRunner,
|
|
298
|
+
onRequestAbortHookRunner,
|
|
259
299
|
hookIterator,
|
|
260
300
|
hookRunnerApplication,
|
|
261
301
|
lifecycleHooks,
|
package/lib/reply.js
CHANGED
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
kReplyNextErrorHandler,
|
|
19
19
|
kDisableRequestLogging,
|
|
20
20
|
kSchemaResponse,
|
|
21
|
-
|
|
21
|
+
kReplyCacheSerializeFns,
|
|
22
22
|
kSchemaController,
|
|
23
23
|
kOptions,
|
|
24
24
|
kRouteContext
|
|
@@ -323,7 +323,7 @@ Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType
|
|
|
323
323
|
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
|
|
324
324
|
}
|
|
325
325
|
} else if (typeof schemaOrStatus === 'object') {
|
|
326
|
-
serialize = this[kRouteContext][
|
|
326
|
+
serialize = this[kRouteContext][kReplyCacheSerializeFns]?.get(schemaOrStatus)
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
return serialize
|
|
@@ -334,8 +334,8 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
334
334
|
const { method, url } = request
|
|
335
335
|
|
|
336
336
|
// Check if serialize function already compiled
|
|
337
|
-
if (this[kRouteContext][
|
|
338
|
-
return this[kRouteContext][
|
|
337
|
+
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
|
338
|
+
return this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
const serializerCompiler = this[kRouteContext].serializerCompiler ||
|
|
@@ -360,11 +360,11 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
360
360
|
// if it is not used
|
|
361
361
|
// TODO: Explore a central cache for all the schemas shared across
|
|
362
362
|
// encapsulated contexts
|
|
363
|
-
if (this[kRouteContext][
|
|
364
|
-
this[kRouteContext][
|
|
363
|
+
if (this[kRouteContext][kReplyCacheSerializeFns] == null) {
|
|
364
|
+
this[kRouteContext][kReplyCacheSerializeFns] = new WeakMap()
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
this[kRouteContext][
|
|
367
|
+
this[kRouteContext][kReplyCacheSerializeFns].set(schema, serializeFn)
|
|
368
368
|
|
|
369
369
|
return serializeFn
|
|
370
370
|
}
|
|
@@ -393,8 +393,8 @@ Reply.prototype.serializeInput = function (input, schema, httpStatus, contentTyp
|
|
|
393
393
|
}
|
|
394
394
|
} else {
|
|
395
395
|
// Check if serialize function already compiled
|
|
396
|
-
if (this[kRouteContext][
|
|
397
|
-
serialize = this[kRouteContext][
|
|
396
|
+
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
|
397
|
+
serialize = this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
|
398
398
|
} else {
|
|
399
399
|
serialize = this.compileSerializationSchema(schema, httpStatus, contentType)
|
|
400
400
|
}
|
package/lib/request.js
CHANGED
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
kSchemaQuerystring,
|
|
12
12
|
kSchemaController,
|
|
13
13
|
kOptions,
|
|
14
|
-
|
|
14
|
+
kRequestCacheValidateFns,
|
|
15
15
|
kRouteContext,
|
|
16
16
|
kPublicRouteContext
|
|
17
17
|
} = require('./symbols')
|
|
@@ -254,7 +254,7 @@ Object.defineProperties(Request.prototype, {
|
|
|
254
254
|
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
|
|
255
255
|
return this[kRouteContext][symbol]
|
|
256
256
|
} else if (typeof httpPartOrSchema === 'object') {
|
|
257
|
-
return this[kRouteContext][
|
|
257
|
+
return this[kRouteContext][kRequestCacheValidateFns]?.get(httpPartOrSchema)
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
},
|
|
@@ -262,8 +262,8 @@ Object.defineProperties(Request.prototype, {
|
|
|
262
262
|
value: function (schema, httpPart = null) {
|
|
263
263
|
const { method, url } = this
|
|
264
264
|
|
|
265
|
-
if (this[kRouteContext][
|
|
266
|
-
return this[kRouteContext][
|
|
265
|
+
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
|
266
|
+
return this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
const validatorCompiler = this[kRouteContext].validatorCompiler ||
|
|
@@ -287,11 +287,11 @@ Object.defineProperties(Request.prototype, {
|
|
|
287
287
|
// if it is not used
|
|
288
288
|
// TODO: Explore a central cache for all the schemas shared across
|
|
289
289
|
// encapsulated contexts
|
|
290
|
-
if (this[kRouteContext][
|
|
291
|
-
this[kRouteContext][
|
|
290
|
+
if (this[kRouteContext][kRequestCacheValidateFns] == null) {
|
|
291
|
+
this[kRouteContext][kRequestCacheValidateFns] = new WeakMap()
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
this[kRouteContext][
|
|
294
|
+
this[kRouteContext][kRequestCacheValidateFns].set(schema, validateFn)
|
|
295
295
|
|
|
296
296
|
return validateFn
|
|
297
297
|
}
|
|
@@ -317,8 +317,8 @@ Object.defineProperties(Request.prototype, {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
if (validate == null) {
|
|
320
|
-
if (this[kRouteContext][
|
|
321
|
-
validate = this[kRouteContext][
|
|
320
|
+
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
|
321
|
+
validate = this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
|
322
322
|
} else {
|
|
323
323
|
// We proceed to compile if there's no validate function yet
|
|
324
324
|
validate = this.compileValidationSchema(schema, httpPart)
|