fastify 4.13.0 → 4.14.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/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 +3 -1
- package/fastify.d.ts +9 -9
- package/fastify.js +5 -1
- package/lib/context.js +1 -0
- 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/route.js +40 -18
- package/lib/symbols.js +1 -0
- 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 +211 -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 received. 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>
|
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.0'
|
|
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
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/route.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const FindMyWay = require('find-my-way')
|
|
4
4
|
const Context = require('./context')
|
|
5
5
|
const handleRequest = require('./handleRequest')
|
|
6
|
-
const { hookRunner, hookIterator, lifecycleHooks } = require('./hooks')
|
|
6
|
+
const { hookRunner, hookIterator, onRequestAbortHookRunner, lifecycleHooks } = require('./hooks')
|
|
7
7
|
const { supportedMethods } = require('./httpMethods')
|
|
8
8
|
const { normalizeSchema } = require('./schemas')
|
|
9
9
|
const { parseHeadOnSendHandlers } = require('./headRoute')
|
|
@@ -303,7 +303,7 @@ function buildRouting (options) {
|
|
|
303
303
|
|
|
304
304
|
// remove the head route created by fastify
|
|
305
305
|
if (hasHEADHandler && !context[kRouteByFastify] && headHandler.store[kRouteByFastify]) {
|
|
306
|
-
router.off(
|
|
306
|
+
router.off('HEAD', opts.url, { constraints })
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
try {
|
|
@@ -394,8 +394,25 @@ function buildRouting (options) {
|
|
|
394
394
|
|
|
395
395
|
// HTTP request entry point, the routing has already been executed
|
|
396
396
|
function routeHandler (req, res, params, context, query) {
|
|
397
|
+
const id = genReqId(req)
|
|
398
|
+
|
|
399
|
+
const loggerBinding = {
|
|
400
|
+
[requestIdLogLabel]: id
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const loggerOpts = {
|
|
404
|
+
level: context.logLevel
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (context.logSerializers) {
|
|
408
|
+
loggerOpts.serializers = context.logSerializers
|
|
409
|
+
}
|
|
410
|
+
const childLogger = logger.child(loggerBinding, loggerOpts)
|
|
411
|
+
childLogger[kDisableRequestLogging] = disableRequestLogging
|
|
412
|
+
|
|
397
413
|
// TODO: The check here should be removed once https://github.com/nodejs/node/issues/43115 resolve in core.
|
|
398
414
|
if (!validateHTTPVersion(req.httpVersion)) {
|
|
415
|
+
childLogger.info({ res: { statusCode: 505 } }, 'request aborted - invalid HTTP version')
|
|
399
416
|
const message = '{"error":"HTTP Version Not Supported","message":"HTTP Version Not Supported","statusCode":505}'
|
|
400
417
|
const headers = {
|
|
401
418
|
'Content-Type': 'application/json',
|
|
@@ -424,6 +441,7 @@ function buildRouting (options) {
|
|
|
424
441
|
}
|
|
425
442
|
res.writeHead(503, headers)
|
|
426
443
|
res.end('{"error":"Service Unavailable","message":"Service Unavailable","statusCode":503}')
|
|
444
|
+
childLogger.info({ res: { statusCode: 503 } }, 'request aborted - refusing to accept new requests as server is closing')
|
|
427
445
|
return
|
|
428
446
|
}
|
|
429
447
|
}
|
|
@@ -445,22 +463,6 @@ function buildRouting (options) {
|
|
|
445
463
|
req.headers[kRequestAcceptVersion] = undefined
|
|
446
464
|
}
|
|
447
465
|
|
|
448
|
-
const id = genReqId(req)
|
|
449
|
-
|
|
450
|
-
const loggerBinding = {
|
|
451
|
-
[requestIdLogLabel]: id
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
const loggerOpts = {
|
|
455
|
-
level: context.logLevel
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (context.logSerializers) {
|
|
459
|
-
loggerOpts.serializers = context.logSerializers
|
|
460
|
-
}
|
|
461
|
-
const childLogger = logger.child(loggerBinding, loggerOpts)
|
|
462
|
-
childLogger[kDisableRequestLogging] = disableRequestLogging
|
|
463
|
-
|
|
464
466
|
const request = new context.Request(id, params, req, query, childLogger, context)
|
|
465
467
|
const reply = new context.Reply(res, request, childLogger)
|
|
466
468
|
|
|
@@ -484,6 +486,20 @@ function buildRouting (options) {
|
|
|
484
486
|
runPreParsing(null, request, reply)
|
|
485
487
|
}
|
|
486
488
|
|
|
489
|
+
if (context.onRequestAbort !== null) {
|
|
490
|
+
req.on('close', () => {
|
|
491
|
+
/* istanbul ignore else */
|
|
492
|
+
if (req.aborted) {
|
|
493
|
+
onRequestAbortHookRunner(
|
|
494
|
+
context.onRequestAbort,
|
|
495
|
+
hookIterator,
|
|
496
|
+
request,
|
|
497
|
+
handleOnRequestAbortHooksErrors.bind(null, reply)
|
|
498
|
+
)
|
|
499
|
+
}
|
|
500
|
+
})
|
|
501
|
+
}
|
|
502
|
+
|
|
487
503
|
if (context.onTimeout !== null) {
|
|
488
504
|
if (!request.raw.socket._meta) {
|
|
489
505
|
request.raw.socket.on('timeout', handleTimeout)
|
|
@@ -493,6 +509,12 @@ function buildRouting (options) {
|
|
|
493
509
|
}
|
|
494
510
|
}
|
|
495
511
|
|
|
512
|
+
function handleOnRequestAbortHooksErrors (reply, err) {
|
|
513
|
+
if (err) {
|
|
514
|
+
reply.log.error({ err }, 'onRequestAborted hook failed')
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
496
518
|
function handleTimeout () {
|
|
497
519
|
const { context, request, reply } = this._meta
|
|
498
520
|
hookRunner(
|
package/lib/symbols.js
CHANGED
|
@@ -30,6 +30,7 @@ const keys = {
|
|
|
30
30
|
kRequestValidateFns: Symbol('fastify.request.cache.validateFns'),
|
|
31
31
|
kRequestPayloadStream: Symbol('fastify.RequestPayloadStream'),
|
|
32
32
|
kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'),
|
|
33
|
+
kRequestValidateWeakMap: Symbol('fastify.request.cache.validators'),
|
|
33
34
|
// 404
|
|
34
35
|
kFourOhFour: Symbol('fastify.404'),
|
|
35
36
|
kCanSetNotFoundHandler: Symbol('fastify.canSetNotFoundHandler'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -168,10 +168,10 @@
|
|
|
168
168
|
"typescript": "^4.8.3",
|
|
169
169
|
"undici": "^5.10.0",
|
|
170
170
|
"vary": "^1.1.2",
|
|
171
|
-
"yup": "^0.
|
|
171
|
+
"yup": "^1.0.0"
|
|
172
172
|
},
|
|
173
173
|
"dependencies": {
|
|
174
|
-
"@fastify/ajv-compiler": "^3.
|
|
174
|
+
"@fastify/ajv-compiler": "^3.5.0",
|
|
175
175
|
"@fastify/error": "^3.0.0",
|
|
176
176
|
"@fastify/fast-json-stringify-compiler": "^4.1.0",
|
|
177
177
|
"abstract-logging": "^2.0.1",
|
package/test/close.test.js
CHANGED
|
@@ -7,6 +7,7 @@ const test = t.test
|
|
|
7
7
|
const Fastify = require('..')
|
|
8
8
|
const { Client } = require('undici')
|
|
9
9
|
const semver = require('semver')
|
|
10
|
+
const split = require('split2')
|
|
10
11
|
|
|
11
12
|
test('close callback', t => {
|
|
12
13
|
t.plan(4)
|
|
@@ -303,6 +304,40 @@ t.test('Current opened connection should not accept new incoming connections', t
|
|
|
303
304
|
})
|
|
304
305
|
})
|
|
305
306
|
|
|
307
|
+
t.test('rejected incoming connections should be logged', t => {
|
|
308
|
+
t.plan(2)
|
|
309
|
+
const stream = split(JSON.parse)
|
|
310
|
+
const fastify = Fastify({
|
|
311
|
+
forceCloseConnections: false,
|
|
312
|
+
logger: {
|
|
313
|
+
stream,
|
|
314
|
+
level: 'info'
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
const messages = []
|
|
319
|
+
stream.on('data', message => {
|
|
320
|
+
messages.push(message)
|
|
321
|
+
})
|
|
322
|
+
fastify.get('/', (req, reply) => {
|
|
323
|
+
fastify.close()
|
|
324
|
+
setTimeout(() => {
|
|
325
|
+
reply.send({ hello: 'world' })
|
|
326
|
+
}, 250)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
fastify.listen({ port: 0 }, err => {
|
|
330
|
+
t.error(err)
|
|
331
|
+
const instance = new Client('http://localhost:' + fastify.server.address().port)
|
|
332
|
+
// initial request to trigger close
|
|
333
|
+
instance.request({ path: '/', method: 'GET' })
|
|
334
|
+
// subsequent request should be rejected
|
|
335
|
+
instance.request({ path: '/', method: 'GET' }).then(() => {
|
|
336
|
+
t.ok(messages.find(message => message.msg.includes('request aborted')))
|
|
337
|
+
})
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
306
341
|
test('Cannot be reopened the closed server without listen callback', async t => {
|
|
307
342
|
t.plan(2)
|
|
308
343
|
const fastify = Fastify()
|
package/test/hooks-async.test.js
CHANGED
|
@@ -584,6 +584,18 @@ test('preHandler respond with a stream', t => {
|
|
|
584
584
|
})
|
|
585
585
|
|
|
586
586
|
test('Should log a warning if is an async function with `done`', t => {
|
|
587
|
+
t.test('2 arguments', t => {
|
|
588
|
+
t.plan(2)
|
|
589
|
+
const fastify = Fastify()
|
|
590
|
+
|
|
591
|
+
try {
|
|
592
|
+
fastify.addHook('onRequestAbort', async (req, done) => {})
|
|
593
|
+
} catch (e) {
|
|
594
|
+
t.ok(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
|
|
595
|
+
t.ok(e.message === 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
|
|
596
|
+
}
|
|
597
|
+
})
|
|
598
|
+
|
|
587
599
|
t.test('3 arguments', t => {
|
|
588
600
|
t.plan(2)
|
|
589
601
|
const fastify = Fastify()
|
package/test/hooks.test.js
CHANGED
|
@@ -11,6 +11,10 @@ const split = require('split2')
|
|
|
11
11
|
const symbols = require('../lib/symbols.js')
|
|
12
12
|
const payload = { hello: 'world' }
|
|
13
13
|
const proxyquire = require('proxyquire')
|
|
14
|
+
const { promisify } = require('util')
|
|
15
|
+
const { connect } = require('net')
|
|
16
|
+
|
|
17
|
+
const sleep = promisify(setTimeout)
|
|
14
18
|
|
|
15
19
|
process.removeAllListeners('warning')
|
|
16
20
|
|
|
@@ -3404,3 +3408,210 @@ test('registering invalid hooks should throw an error', async t => {
|
|
|
3404
3408
|
})
|
|
3405
3409
|
}, new Error('onSend hook should be a function, instead got [object Undefined]'))
|
|
3406
3410
|
})
|
|
3411
|
+
|
|
3412
|
+
test('onRequestAbort should be triggered', t => {
|
|
3413
|
+
const fastify = Fastify()
|
|
3414
|
+
let order = 0
|
|
3415
|
+
|
|
3416
|
+
t.plan(3)
|
|
3417
|
+
t.teardown(() => fastify.close())
|
|
3418
|
+
|
|
3419
|
+
fastify.addHook('onRequestAbort', function (req, done) {
|
|
3420
|
+
t.equal(++order, 1, 'called in hook')
|
|
3421
|
+
done()
|
|
3422
|
+
})
|
|
3423
|
+
|
|
3424
|
+
fastify.route({
|
|
3425
|
+
method: 'GET',
|
|
3426
|
+
path: '/',
|
|
3427
|
+
async handler (request, reply) {
|
|
3428
|
+
await sleep(1000)
|
|
3429
|
+
return { hello: 'world' }
|
|
3430
|
+
},
|
|
3431
|
+
async onRequestAbort (req) {
|
|
3432
|
+
t.equal(++order, 2, 'called in route')
|
|
3433
|
+
}
|
|
3434
|
+
})
|
|
3435
|
+
|
|
3436
|
+
fastify.listen({ port: 0 }, err => {
|
|
3437
|
+
t.error(err)
|
|
3438
|
+
|
|
3439
|
+
const socket = connect(fastify.server.address().port)
|
|
3440
|
+
|
|
3441
|
+
socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
|
|
3442
|
+
|
|
3443
|
+
sleep(500).then(() => socket.destroy())
|
|
3444
|
+
})
|
|
3445
|
+
})
|
|
3446
|
+
|
|
3447
|
+
test('onRequestAbort should support encapsulation', t => {
|
|
3448
|
+
const fastify = Fastify()
|
|
3449
|
+
let order = 0
|
|
3450
|
+
let child
|
|
3451
|
+
|
|
3452
|
+
t.plan(6)
|
|
3453
|
+
t.teardown(() => fastify.close())
|
|
3454
|
+
|
|
3455
|
+
fastify.addHook('onRequestAbort', function (req, done) {
|
|
3456
|
+
t.equal(++order, 1, 'called in root')
|
|
3457
|
+
t.strictSame(this.pluginName, child.pluginName)
|
|
3458
|
+
done()
|
|
3459
|
+
})
|
|
3460
|
+
|
|
3461
|
+
fastify.register(async function (_child, _, done) {
|
|
3462
|
+
child = _child
|
|
3463
|
+
|
|
3464
|
+
fastify.addHook('onRequestAbort', async function (req) {
|
|
3465
|
+
t.equal(++order, 2, 'called in child')
|
|
3466
|
+
t.strictSame(this.pluginName, child.pluginName)
|
|
3467
|
+
})
|
|
3468
|
+
|
|
3469
|
+
child.route({
|
|
3470
|
+
method: 'GET',
|
|
3471
|
+
path: '/',
|
|
3472
|
+
async handler (request, reply) {
|
|
3473
|
+
await sleep(1000)
|
|
3474
|
+
return { hello: 'world' }
|
|
3475
|
+
},
|
|
3476
|
+
async onRequestAbort (_req) {
|
|
3477
|
+
t.equal(++order, 3, 'called in route')
|
|
3478
|
+
}
|
|
3479
|
+
})
|
|
3480
|
+
|
|
3481
|
+
done()
|
|
3482
|
+
})
|
|
3483
|
+
|
|
3484
|
+
fastify.listen({ port: 0 }, err => {
|
|
3485
|
+
t.error(err)
|
|
3486
|
+
|
|
3487
|
+
const socket = connect(fastify.server.address().port)
|
|
3488
|
+
|
|
3489
|
+
socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
|
|
3490
|
+
|
|
3491
|
+
sleep(500).then(() => socket.destroy())
|
|
3492
|
+
})
|
|
3493
|
+
})
|
|
3494
|
+
|
|
3495
|
+
test('onRequestAbort should handle errors / 1', t => {
|
|
3496
|
+
const fastify = Fastify()
|
|
3497
|
+
|
|
3498
|
+
t.plan(2)
|
|
3499
|
+
t.teardown(() => fastify.close())
|
|
3500
|
+
|
|
3501
|
+
fastify.addHook('onRequestAbort', function (req, done) {
|
|
3502
|
+
process.nextTick(() => t.pass())
|
|
3503
|
+
done(new Error('KABOOM!'))
|
|
3504
|
+
})
|
|
3505
|
+
|
|
3506
|
+
fastify.route({
|
|
3507
|
+
method: 'GET',
|
|
3508
|
+
path: '/',
|
|
3509
|
+
async handler (request, reply) {
|
|
3510
|
+
await sleep(1000)
|
|
3511
|
+
return { hello: 'world' }
|
|
3512
|
+
}
|
|
3513
|
+
})
|
|
3514
|
+
|
|
3515
|
+
fastify.listen({ port: 0 }, err => {
|
|
3516
|
+
t.error(err)
|
|
3517
|
+
|
|
3518
|
+
const socket = connect(fastify.server.address().port)
|
|
3519
|
+
|
|
3520
|
+
socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
|
|
3521
|
+
|
|
3522
|
+
sleep(500).then(() => socket.destroy())
|
|
3523
|
+
})
|
|
3524
|
+
})
|
|
3525
|
+
|
|
3526
|
+
test('onRequestAbort should handle errors / 2', t => {
|
|
3527
|
+
const fastify = Fastify()
|
|
3528
|
+
|
|
3529
|
+
t.plan(2)
|
|
3530
|
+
t.teardown(() => fastify.close())
|
|
3531
|
+
|
|
3532
|
+
fastify.addHook('onRequestAbort', function (req, done) {
|
|
3533
|
+
process.nextTick(() => t.pass())
|
|
3534
|
+
throw new Error('KABOOM!')
|
|
3535
|
+
})
|
|
3536
|
+
|
|
3537
|
+
fastify.route({
|
|
3538
|
+
method: 'GET',
|
|
3539
|
+
path: '/',
|
|
3540
|
+
async handler (request, reply) {
|
|
3541
|
+
await sleep(1000)
|
|
3542
|
+
return { hello: 'world' }
|
|
3543
|
+
}
|
|
3544
|
+
})
|
|
3545
|
+
|
|
3546
|
+
fastify.listen({ port: 0 }, err => {
|
|
3547
|
+
t.error(err)
|
|
3548
|
+
|
|
3549
|
+
const socket = connect(fastify.server.address().port)
|
|
3550
|
+
|
|
3551
|
+
socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
|
|
3552
|
+
|
|
3553
|
+
sleep(500).then(() => socket.destroy())
|
|
3554
|
+
})
|
|
3555
|
+
})
|
|
3556
|
+
|
|
3557
|
+
test('onRequestAbort should handle async errors / 1', t => {
|
|
3558
|
+
const fastify = Fastify()
|
|
3559
|
+
|
|
3560
|
+
t.plan(2)
|
|
3561
|
+
t.teardown(() => fastify.close())
|
|
3562
|
+
|
|
3563
|
+
fastify.addHook('onRequestAbort', async function (req) {
|
|
3564
|
+
process.nextTick(() => t.pass())
|
|
3565
|
+
throw new Error('KABOOM!')
|
|
3566
|
+
})
|
|
3567
|
+
|
|
3568
|
+
fastify.route({
|
|
3569
|
+
method: 'GET',
|
|
3570
|
+
path: '/',
|
|
3571
|
+
async handler (request, reply) {
|
|
3572
|
+
await sleep(1000)
|
|
3573
|
+
return { hello: 'world' }
|
|
3574
|
+
}
|
|
3575
|
+
})
|
|
3576
|
+
|
|
3577
|
+
fastify.listen({ port: 0 }, err => {
|
|
3578
|
+
t.error(err)
|
|
3579
|
+
|
|
3580
|
+
const socket = connect(fastify.server.address().port)
|
|
3581
|
+
|
|
3582
|
+
socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
|
|
3583
|
+
|
|
3584
|
+
sleep(500).then(() => socket.destroy())
|
|
3585
|
+
})
|
|
3586
|
+
})
|
|
3587
|
+
|
|
3588
|
+
test('onRequestAbort should handle async errors / 2', t => {
|
|
3589
|
+
const fastify = Fastify()
|
|
3590
|
+
|
|
3591
|
+
t.plan(2)
|
|
3592
|
+
t.teardown(() => fastify.close())
|
|
3593
|
+
|
|
3594
|
+
fastify.addHook('onRequestAbort', async function (req) {
|
|
3595
|
+
process.nextTick(() => t.pass())
|
|
3596
|
+
return Promise.reject()
|
|
3597
|
+
})
|
|
3598
|
+
|
|
3599
|
+
fastify.route({
|
|
3600
|
+
method: 'GET',
|
|
3601
|
+
path: '/',
|
|
3602
|
+
async handler (request, reply) {
|
|
3603
|
+
await sleep(1000)
|
|
3604
|
+
return { hello: 'world' }
|
|
3605
|
+
}
|
|
3606
|
+
})
|
|
3607
|
+
|
|
3608
|
+
fastify.listen({ port: 0 }, err => {
|
|
3609
|
+
t.error(err)
|
|
3610
|
+
|
|
3611
|
+
const socket = connect(fastify.server.address().port)
|
|
3612
|
+
|
|
3613
|
+
socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
|
|
3614
|
+
|
|
3615
|
+
sleep(500).then(() => socket.destroy())
|
|
3616
|
+
})
|
|
3617
|
+
})
|
package/test/logger.test.js
CHANGED
|
@@ -28,15 +28,17 @@ before(async function () {
|
|
|
28
28
|
[localhost, localhostForURL] = await helper.getLoopbackHost()
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
if (process.env.CI) {
|
|
32
|
+
teardown(() => {
|
|
33
|
+
files.forEach((file) => {
|
|
34
|
+
try {
|
|
35
|
+
fs.unlinkSync(file)
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.log(e)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
38
40
|
})
|
|
39
|
-
}
|
|
41
|
+
}
|
|
40
42
|
|
|
41
43
|
test('defaults to info level', t => {
|
|
42
44
|
let fastify = null
|
package/test/route.test.js
CHANGED
|
@@ -1493,3 +1493,17 @@ test('exposeHeadRoute should not reuse the same route option', async t => {
|
|
|
1493
1493
|
}
|
|
1494
1494
|
})
|
|
1495
1495
|
})
|
|
1496
|
+
|
|
1497
|
+
test('using fastify.all when a catchall is defined does not degrade performance', { timeout: 5000 }, async t => {
|
|
1498
|
+
t.plan(1)
|
|
1499
|
+
|
|
1500
|
+
const fastify = Fastify()
|
|
1501
|
+
|
|
1502
|
+
fastify.get('/*', async (_, reply) => reply.json({ ok: true }))
|
|
1503
|
+
|
|
1504
|
+
for (let i = 0; i < 100; i++) {
|
|
1505
|
+
fastify.all(`/${i}`, async (_, reply) => reply.json({ ok: true }))
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
t.pass()
|
|
1509
|
+
})
|
|
@@ -833,8 +833,9 @@ test('do not crash if status code serializer errors', async t => {
|
|
|
833
833
|
t.equal(res.statusCode, 500)
|
|
834
834
|
t.same(res.json(), {
|
|
835
835
|
statusCode: 500,
|
|
836
|
-
|
|
837
|
-
message: '"code" is required
|
|
836
|
+
code: 'FST_ERR_FAILED_ERROR_SERIALIZATION',
|
|
837
|
+
message: 'Failed to serialize an error. Error: "code" is required!. ' +
|
|
838
|
+
'Original error: querystring must have required property \'foo\''
|
|
838
839
|
})
|
|
839
840
|
})
|
|
840
841
|
|
|
@@ -240,7 +240,7 @@ expectAssignable<ValidationResult>(ajvErrorObject)
|
|
|
240
240
|
expectAssignable<FastifyError['validation']>([ajvErrorObject])
|
|
241
241
|
expectAssignable<FastifyError['validationContext']>('body')
|
|
242
242
|
expectAssignable<FastifyError['validationContext']>('headers')
|
|
243
|
-
expectAssignable<FastifyError['validationContext']>('
|
|
243
|
+
expectAssignable<FastifyError['validationContext']>('params')
|
|
244
244
|
expectAssignable<FastifyError['validationContext']>('querystring')
|
|
245
245
|
|
|
246
246
|
const routeGeneric: RouteGenericInterface = {}
|
|
@@ -114,6 +114,14 @@ server.addHook('onError', function (request, reply, error, done) {
|
|
|
114
114
|
expectType<void>(done())
|
|
115
115
|
})
|
|
116
116
|
|
|
117
|
+
server.addHook('onRequestAbort', function (request, done) {
|
|
118
|
+
expectType<FastifyInstance>(this)
|
|
119
|
+
expectType<FastifyRequest>(request)
|
|
120
|
+
expectAssignable<(err?: FastifyError) => void>(done)
|
|
121
|
+
expectAssignable<(err?: NodeJS.ErrnoException) => void>(done)
|
|
122
|
+
expectType<void>(done(new Error()))
|
|
123
|
+
})
|
|
124
|
+
|
|
117
125
|
server.addHook('onRoute', function (opts) {
|
|
118
126
|
expectType<FastifyInstance>(this)
|
|
119
127
|
expectType<RouteOptions & { routePath: string; path: string; prefix: string}>(opts)
|
|
@@ -201,6 +209,11 @@ server.addHook('onError', async function (request, reply, error) {
|
|
|
201
209
|
expectType<FastifyError>(error)
|
|
202
210
|
})
|
|
203
211
|
|
|
212
|
+
server.addHook('onRequestAbort', async function (request) {
|
|
213
|
+
expectType<FastifyInstance>(this)
|
|
214
|
+
expectType<FastifyRequest>(request)
|
|
215
|
+
})
|
|
216
|
+
|
|
204
217
|
server.addHook('onRegister', async (instance, opts) => {
|
|
205
218
|
expectType<FastifyInstance>(instance)
|
|
206
219
|
expectType<RegisterOptions & FastifyPluginOptions>(opts)
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { expectAssignable
|
|
2
|
-
import fastify, { FastifyInstance,
|
|
3
|
-
import { RouteGenericInterface } from '../../types/route'
|
|
4
|
-
import { ContextConfigDefault } from '../../types/utils'
|
|
5
|
-
import { FastifyReply } from '../../types/reply'
|
|
1
|
+
import { expectAssignable } from 'tsd'
|
|
2
|
+
import fastify, { FastifyInstance, FastifySchema } from '../../fastify'
|
|
6
3
|
import Ajv from 'ajv'
|
|
4
|
+
import { StandaloneValidator } from '@fastify/ajv-compiler'
|
|
5
|
+
import { StandaloneSerializer } from '@fastify/fast-json-stringify-compiler'
|
|
7
6
|
|
|
8
7
|
const server = fastify()
|
|
9
8
|
|
|
@@ -63,3 +62,36 @@ expectAssignable<FastifyInstance>(server.setValidatorCompiler<FastifySchema & {
|
|
|
63
62
|
expectAssignable<FastifyInstance>(server.setSerializerCompiler<FastifySchema & { validate: string }>(
|
|
64
63
|
() => data => JSON.stringify(data)
|
|
65
64
|
))
|
|
65
|
+
|
|
66
|
+
// https://github.com/fastify/ajv-compiler/issues/95
|
|
67
|
+
{
|
|
68
|
+
const factory = StandaloneValidator({
|
|
69
|
+
readMode: false,
|
|
70
|
+
storeFunction (routeOpts, schemaValidationCode) { }
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const app = fastify({
|
|
74
|
+
jsonShorthand: false,
|
|
75
|
+
schemaController: {
|
|
76
|
+
compilersFactory: {
|
|
77
|
+
buildValidator: factory
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
{
|
|
84
|
+
const factory = StandaloneSerializer({
|
|
85
|
+
readMode: false,
|
|
86
|
+
storeFunction (routeOpts, schemaValidationCode) { }
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const app = fastify({
|
|
90
|
+
jsonShorthand: false,
|
|
91
|
+
schemaController: {
|
|
92
|
+
compilersFactory: {
|
|
93
|
+
buildSerializer: factory
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
package/types/errors.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export type FastifyErrorCodes = Record<
|
|
|
28
28
|
'FST_ERR_BAD_STATUS_CODE'|
|
|
29
29
|
'FST_ERR_BAD_TRAILER_NAME' |
|
|
30
30
|
'FST_ERR_BAD_TRAILER_VALUE' |
|
|
31
|
+
'FST_ERR_FAILED_ERROR_SERIALIZATION' |
|
|
31
32
|
'FST_ERR_MISSING_SERIALIZATION_FN' |
|
|
32
33
|
'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION' |
|
|
33
34
|
'FST_ERR_SCH_MISSING_ID' |
|
package/types/hooks.d.ts
CHANGED
|
@@ -394,6 +394,44 @@ export interface onErrorAsyncHookHandler<
|
|
|
394
394
|
): Promise<unknown>;
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
/**
|
|
398
|
+
* `onRequestAbort` is useful if you need to monitor the if the client aborts the request (if the `request.raw.aborted` property is set to `true`).
|
|
399
|
+
* The `onRequestAbort` hook is executed when a client closes the connection before the entire request has been received. Therefore, you will not be able to send data to the client.
|
|
400
|
+
* Notice: client abort detection is not completely reliable. See: https://github.com/fastify/fastify/blob/main/docs/Guides/Detecting-When-Clients-Abort.md
|
|
401
|
+
*/
|
|
402
|
+
export interface onRequestAbortHookHandler<
|
|
403
|
+
RawServer extends RawServerBase = RawServerDefault,
|
|
404
|
+
RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,
|
|
405
|
+
RawReply extends RawReplyDefaultExpression<RawServer> = RawReplyDefaultExpression<RawServer>,
|
|
406
|
+
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
|
|
407
|
+
ContextConfig = ContextConfigDefault,
|
|
408
|
+
SchemaCompiler extends FastifySchema = FastifySchema,
|
|
409
|
+
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
|
|
410
|
+
Logger extends FastifyBaseLogger = FastifyBaseLogger
|
|
411
|
+
> {
|
|
412
|
+
(
|
|
413
|
+
this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>,
|
|
414
|
+
request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider, ContextConfig, Logger>,
|
|
415
|
+
done: HookHandlerDoneFunction
|
|
416
|
+
): void;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export interface onRequestAbortAsyncHookHandler<
|
|
420
|
+
RawServer extends RawServerBase = RawServerDefault,
|
|
421
|
+
RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,
|
|
422
|
+
RawReply extends RawReplyDefaultExpression<RawServer> = RawReplyDefaultExpression<RawServer>,
|
|
423
|
+
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
|
|
424
|
+
ContextConfig = ContextConfigDefault,
|
|
425
|
+
SchemaCompiler extends FastifySchema = FastifySchema,
|
|
426
|
+
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
|
|
427
|
+
Logger extends FastifyBaseLogger = FastifyBaseLogger
|
|
428
|
+
> {
|
|
429
|
+
(
|
|
430
|
+
this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>,
|
|
431
|
+
request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider, ContextConfig, Logger>,
|
|
432
|
+
): Promise<unknown>;
|
|
433
|
+
}
|
|
434
|
+
|
|
397
435
|
// Application Hooks
|
|
398
436
|
|
|
399
437
|
/**
|
package/types/instance.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
|
|
|
3
3
|
import * as http from 'http'
|
|
4
4
|
import { CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse } from 'light-my-request'
|
|
5
5
|
import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, getDefaultJsonParser, hasContentTypeParser, ProtoAction, removeAllContentTypeParsers, removeContentTypeParser } from './content-type-parser'
|
|
6
|
-
import { 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 './hooks'
|
|
6
|
+
import { onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onRequestAbortAsyncHookHandler, onRequestAbortHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler } from './hooks'
|
|
7
7
|
import { FastifyBaseLogger } from './logger'
|
|
8
8
|
import { FastifyRegister } from './register'
|
|
9
9
|
import { FastifyReply } from './reply'
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
FastifySchema,
|
|
14
14
|
FastifySchemaCompiler,
|
|
15
15
|
FastifySchemaControllerOptions,
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
FastifySerializerCompiler,
|
|
17
|
+
SchemaErrorFormatter
|
|
18
18
|
} from './schema'
|
|
19
19
|
import {
|
|
20
20
|
FastifyTypeProvider,
|
|
@@ -393,6 +393,31 @@ export interface FastifyInstance<
|
|
|
393
393
|
hook: onTimeoutAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>
|
|
394
394
|
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
|
|
395
395
|
|
|
396
|
+
/**
|
|
397
|
+
* `onRequestAbort` is useful if you need to monitor the if the client aborts the request (if the `request.raw.aborted` property is set to `true`).
|
|
398
|
+
* The `onRequestAbort` hook is executed when a client closes the connection before the entire request has been received. Therefore, you will not be able to send data to the client.
|
|
399
|
+
* Notice: client abort detection is not completely reliable. See: https://github.com/fastify/fastify/blob/main/docs/Guides/Detecting-When-Clients-Abort.md
|
|
400
|
+
*/
|
|
401
|
+
addHook<
|
|
402
|
+
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
|
|
403
|
+
ContextConfig = ContextConfigDefault,
|
|
404
|
+
SchemaCompiler extends FastifySchema = FastifySchema,
|
|
405
|
+
Logger extends FastifyBaseLogger = FastifyBaseLogger
|
|
406
|
+
>(
|
|
407
|
+
name: 'onRequestAbort',
|
|
408
|
+
hook: onRequestAbortHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>
|
|
409
|
+
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
|
|
410
|
+
|
|
411
|
+
addHook<
|
|
412
|
+
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
|
|
413
|
+
ContextConfig = ContextConfigDefault,
|
|
414
|
+
SchemaCompiler extends FastifySchema = FastifySchema,
|
|
415
|
+
Logger extends FastifyBaseLogger = FastifyBaseLogger
|
|
416
|
+
>(
|
|
417
|
+
name: 'onRequestAbort',
|
|
418
|
+
hook: onRequestAbortAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>
|
|
419
|
+
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
|
|
420
|
+
|
|
396
421
|
/**
|
|
397
422
|
* This hook is useful if you need to do some custom error logging or add some specific header in case of error.
|
|
398
423
|
* It is not intended for changing the error, and calling reply.send will throw an exception.
|
|
@@ -530,7 +555,7 @@ export interface FastifyInstance<
|
|
|
530
555
|
/*
|
|
531
556
|
* Set the schema error formatter for all routes.
|
|
532
557
|
*/
|
|
533
|
-
setSchemaErrorFormatter(errorFormatter:
|
|
558
|
+
setSchemaErrorFormatter(errorFormatter: SchemaErrorFormatter): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
|
|
534
559
|
/**
|
|
535
560
|
* Add a content type parser
|
|
536
561
|
*/
|
package/types/route.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FastifyInstance } from './instance'
|
|
2
2
|
import { FastifyRequest, RequestGenericInterface } from './request'
|
|
3
3
|
import { FastifyReply, ReplyGenericInterface } from './reply'
|
|
4
|
-
import { FastifySchema, FastifySchemaCompiler,
|
|
4
|
+
import { FastifySchema, FastifySchemaCompiler, FastifySerializerCompiler, SchemaErrorFormatter } from './schema'
|
|
5
5
|
import { HTTPMethods, RawServerBase, RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, ContextConfigDefault } from './utils'
|
|
6
|
-
import { preValidationHookHandler, preHandlerHookHandler, preSerializationHookHandler, onRequestHookHandler, preParsingHookHandler, onResponseHookHandler, onSendHookHandler, onErrorHookHandler, onTimeoutHookHandler } from './hooks'
|
|
6
|
+
import { preValidationHookHandler, preHandlerHookHandler, preSerializationHookHandler, onRequestHookHandler, preParsingHookHandler, onResponseHookHandler, onSendHookHandler, onErrorHookHandler, onTimeoutHookHandler, onRequestAbortHookHandler } from './hooks'
|
|
7
7
|
import { FastifyError } from '@fastify/error'
|
|
8
8
|
import { FastifyContext } from './context'
|
|
9
9
|
import {
|
|
@@ -41,8 +41,7 @@ export interface RouteShorthandOptions<
|
|
|
41
41
|
constraints?: { [name: string]: any },
|
|
42
42
|
prefixTrailingSlash?: 'slash'|'no-slash'|'both';
|
|
43
43
|
errorHandler?: (this: FastifyInstance, error: FastifyError, request: FastifyRequest, reply: FastifyReply) => void;
|
|
44
|
-
|
|
45
|
-
schemaErrorFormatter?: (errors: FastifySchemaValidationError[], dataVar: string) => Error;
|
|
44
|
+
schemaErrorFormatter?: SchemaErrorFormatter;
|
|
46
45
|
|
|
47
46
|
// hooks
|
|
48
47
|
onRequest?: onRequestHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger> | onRequestHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>[];
|
|
@@ -54,6 +53,7 @@ export interface RouteShorthandOptions<
|
|
|
54
53
|
onResponse?: onResponseHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger> | onResponseHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>[];
|
|
55
54
|
onTimeout?: onTimeoutHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger> | onTimeoutHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>[];
|
|
56
55
|
onError?: onErrorHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, FastifyError, SchemaCompiler, TypeProvider, Logger> | onErrorHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, FastifyError, SchemaCompiler, TypeProvider, Logger>[];
|
|
56
|
+
onRequestAbort?: onRequestAbortHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger> | onRequestAbortHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>[];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
package/types/schema.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ValidatorFactory } from '@fastify/ajv-compiler'
|
|
2
|
+
import { SerializerFactory } from '@fastify/fast-json-stringify-compiler'
|
|
3
|
+
import { FastifyInstance } from '../fastify'
|
|
3
4
|
/**
|
|
4
5
|
* Schemas in Fastify follow the JSON-Schema standard. For this reason
|
|
5
6
|
* we have opted to not ship strict schema based types. Instead we provide
|
|
@@ -50,7 +51,11 @@ export interface FastifySchemaControllerOptions{
|
|
|
50
51
|
getSchemas(): Record<string, unknown>;
|
|
51
52
|
};
|
|
52
53
|
compilersFactory?: {
|
|
53
|
-
buildValidator?:
|
|
54
|
-
buildSerializer?:
|
|
54
|
+
buildValidator?: ValidatorFactory;
|
|
55
|
+
buildSerializer?: SerializerFactory;
|
|
55
56
|
};
|
|
56
57
|
}
|
|
58
|
+
|
|
59
|
+
export type SchemaErrorDataVar = 'body' | 'headers' | 'params' | 'querystring'
|
|
60
|
+
|
|
61
|
+
export type SchemaErrorFormatter = (errors: FastifySchemaValidationError[], dataVar: SchemaErrorDataVar) => Error
|