fastify 3.27.4 → 4.0.0-alpha.3

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.
Files changed (168) hide show
  1. package/.taprc +3 -0
  2. package/README.md +7 -7
  3. package/build/build-error-serializer.js +27 -0
  4. package/build/build-validation.js +47 -35
  5. package/docs/Guides/Database.md +320 -0
  6. package/docs/Guides/Ecosystem.md +9 -0
  7. package/docs/Guides/Getting-Started.md +7 -7
  8. package/docs/Guides/Plugins-Guide.md +1 -1
  9. package/docs/Guides/Serverless.md +3 -3
  10. package/docs/Guides/Testing.md +2 -2
  11. package/docs/Migration-Guide-V4.md +12 -0
  12. package/docs/Reference/ContentTypeParser.md +4 -0
  13. package/docs/Reference/Decorators.md +2 -2
  14. package/docs/Reference/Encapsulation.md +2 -2
  15. package/docs/Reference/Errors.md +51 -6
  16. package/docs/Reference/HTTP2.md +3 -3
  17. package/docs/Reference/Hooks.md +4 -7
  18. package/docs/Reference/LTS.md +5 -4
  19. package/docs/Reference/Plugins.md +3 -3
  20. package/docs/Reference/Reply.md +73 -22
  21. package/docs/Reference/Request.md +1 -3
  22. package/docs/Reference/Routes.md +22 -15
  23. package/docs/Reference/Server.md +69 -119
  24. package/docs/Reference/TypeScript.md +20 -22
  25. package/docs/Reference/Validation-and-Serialization.md +30 -55
  26. package/docs/Type-Providers.md +257 -0
  27. package/examples/asyncawait.js +1 -1
  28. package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
  29. package/examples/benchmark/hooks-benchmark.js +1 -1
  30. package/examples/benchmark/simple.js +1 -1
  31. package/examples/hooks.js +2 -2
  32. package/examples/http2.js +1 -1
  33. package/examples/https.js +1 -1
  34. package/examples/parser.js +13 -3
  35. package/examples/route-prefix.js +1 -1
  36. package/examples/shared-schema.js +1 -1
  37. package/examples/simple-stream.js +18 -0
  38. package/examples/simple.js +1 -1
  39. package/examples/simple.mjs +1 -1
  40. package/examples/typescript-server.ts +1 -1
  41. package/examples/use-plugin.js +1 -1
  42. package/fastify.d.ts +34 -22
  43. package/fastify.js +40 -36
  44. package/lib/configValidator.js +902 -1023
  45. package/lib/contentTypeParser.js +6 -16
  46. package/lib/context.js +36 -10
  47. package/lib/decorate.js +3 -1
  48. package/lib/error-handler.js +158 -0
  49. package/lib/error-serializer.js +257 -0
  50. package/lib/errors.js +51 -9
  51. package/lib/fourOhFour.js +31 -20
  52. package/lib/handleRequest.js +10 -13
  53. package/lib/hooks.js +14 -9
  54. package/lib/pluginOverride.js +0 -3
  55. package/lib/pluginUtils.js +3 -2
  56. package/lib/reply.js +121 -175
  57. package/lib/request.js +13 -10
  58. package/lib/route.js +131 -138
  59. package/lib/schema-controller.js +2 -2
  60. package/lib/schemas.js +27 -1
  61. package/lib/server.js +242 -116
  62. package/lib/symbols.js +5 -3
  63. package/lib/validation.js +11 -9
  64. package/lib/warnings.js +4 -12
  65. package/lib/wrapThenable.js +4 -11
  66. package/package.json +37 -39
  67. package/test/404s.test.js +258 -125
  68. package/test/500s.test.js +3 -3
  69. package/test/als.test.js +1 -1
  70. package/test/async-await.test.js +20 -76
  71. package/test/bodyLimit.test.js +1 -1
  72. package/test/build-certificate.js +6 -7
  73. package/test/case-insensitive.test.js +4 -4
  74. package/test/close-pipelining.test.js +2 -2
  75. package/test/close.test.js +11 -11
  76. package/test/content-parser.test.js +32 -0
  77. package/test/context-config.test.js +52 -0
  78. package/test/custom-http-server.test.js +14 -7
  79. package/test/custom-parser-async.test.js +1 -66
  80. package/test/custom-parser.test.js +92 -159
  81. package/test/custom-querystring-parser.test.js +3 -3
  82. package/test/decorator.test.js +11 -13
  83. package/test/delete.test.js +6 -6
  84. package/test/encapsulated-error-handler.test.js +50 -0
  85. package/test/esm/index.test.js +0 -14
  86. package/test/fastify-instance.test.js +4 -4
  87. package/test/fluent-schema.test.js +4 -4
  88. package/test/genReqId.test.js +1 -1
  89. package/test/get.test.js +4 -4
  90. package/test/handler-context.test.js +2 -2
  91. package/test/head.test.js +1 -1
  92. package/test/helper.js +19 -4
  93. package/test/hooks-async.test.js +15 -48
  94. package/test/hooks.on-ready.test.js +10 -5
  95. package/test/hooks.test.js +78 -119
  96. package/test/http2/closing.test.js +10 -16
  97. package/test/http2/constraint.test.js +1 -1
  98. package/test/http2/head.test.js +1 -1
  99. package/test/http2/plain.test.js +1 -1
  100. package/test/http2/secure-with-fallback.test.js +1 -1
  101. package/test/http2/secure.test.js +1 -1
  102. package/test/http2/unknown-http-method.test.js +4 -10
  103. package/test/https/custom-https-server.test.js +12 -6
  104. package/test/https/https.test.js +1 -1
  105. package/test/input-validation.js +3 -3
  106. package/test/internals/handleRequest.test.js +6 -43
  107. package/test/internals/initialConfig.test.js +41 -12
  108. package/test/internals/logger.test.js +2 -2
  109. package/test/internals/reply.test.js +317 -48
  110. package/test/internals/request.test.js +13 -7
  111. package/test/internals/server.test.js +88 -0
  112. package/test/listen.deprecated.test.js +202 -0
  113. package/test/listen.test.js +140 -145
  114. package/test/logger.test.js +82 -42
  115. package/test/maxRequestsPerSocket.test.js +8 -6
  116. package/test/middleware.test.js +2 -25
  117. package/test/nullable-validation.test.js +53 -16
  118. package/test/output-validation.test.js +1 -1
  119. package/test/plugin.test.js +47 -21
  120. package/test/pretty-print.test.js +22 -10
  121. package/test/promises.test.js +1 -1
  122. package/test/proto-poisoning.test.js +6 -6
  123. package/test/register.test.js +3 -3
  124. package/test/reply-error.test.js +126 -15
  125. package/test/reply-trailers.test.js +270 -0
  126. package/test/request-error.test.js +3 -6
  127. package/test/route-hooks.test.js +18 -18
  128. package/test/route-prefix.test.js +2 -1
  129. package/test/route.test.js +206 -22
  130. package/test/router-options.test.js +2 -2
  131. package/test/schema-examples.test.js +11 -5
  132. package/test/schema-feature.test.js +25 -20
  133. package/test/schema-serialization.test.js +9 -9
  134. package/test/schema-special-usage.test.js +5 -153
  135. package/test/schema-validation.test.js +9 -9
  136. package/test/skip-reply-send.test.js +2 -2
  137. package/test/stream.test.js +82 -23
  138. package/test/throw.test.js +8 -5
  139. package/test/trust-proxy.test.js +6 -6
  140. package/test/type-provider.test.js +20 -0
  141. package/test/types/fastify.test-d.ts +10 -18
  142. package/test/types/hooks.test-d.ts +61 -5
  143. package/test/types/import.js +2 -0
  144. package/test/types/import.ts +1 -0
  145. package/test/types/instance.test-d.ts +68 -17
  146. package/test/types/logger.test-d.ts +44 -15
  147. package/test/types/reply.test-d.ts +2 -1
  148. package/test/types/request.test-d.ts +71 -1
  149. package/test/types/route.test-d.ts +8 -2
  150. package/test/types/schema.test-d.ts +2 -39
  151. package/test/types/type-provider.test-d.ts +424 -0
  152. package/test/url-rewriting.test.js +3 -3
  153. package/test/validation-error-handling.test.js +8 -8
  154. package/test/versioned-routes.test.js +30 -18
  155. package/test/wrapThenable.test.js +7 -6
  156. package/types/content-type-parser.d.ts +17 -8
  157. package/types/hooks.d.ts +182 -85
  158. package/types/instance.d.ts +286 -118
  159. package/types/logger.d.ts +18 -104
  160. package/types/plugin.d.ts +10 -4
  161. package/types/reply.d.ts +18 -12
  162. package/types/request.d.ts +13 -8
  163. package/types/route.d.ts +62 -34
  164. package/types/schema.d.ts +1 -1
  165. package/types/type-provider.d.ts +99 -0
  166. package/types/utils.d.ts +1 -1
  167. package/lib/schema-compilers.js +0 -12
  168. package/test/emit-warning.test.js +0 -166
@@ -14,7 +14,8 @@ const {
14
14
  kBodyLimit,
15
15
  kRequestPayloadStream,
16
16
  kState,
17
- kTestInternals
17
+ kTestInternals,
18
+ kReplyIsError
18
19
  } = require('./symbols')
19
20
 
20
21
  const {
@@ -28,7 +29,6 @@ const {
28
29
  FST_ERR_CTP_INVALID_CONTENT_LENGTH,
29
30
  FST_ERR_CTP_EMPTY_JSON_BODY
30
31
  } = require('./errors')
31
- const warning = require('./warnings')
32
32
 
33
33
  function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
34
34
  this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
@@ -151,13 +151,7 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
151
151
  done
152
152
  )
153
153
  } else {
154
- let result
155
-
156
- if (parser.isDeprecatedSignature) {
157
- result = parser.fn(request[kRequestPayloadStream], done)
158
- } else {
159
- result = parser.fn(request, request[kRequestPayloadStream], done)
160
- }
154
+ const result = parser.fn(request, request[kRequestPayloadStream], done)
161
155
 
162
156
  if (result && typeof result.then === 'function') {
163
157
  result.then(body => done(null, body), done)
@@ -165,9 +159,10 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
165
159
  }
166
160
 
167
161
  function done (error, body) {
168
- // We cannot use resource.bind() because it is broken in node v12.
162
+ // We cannot use resource.bind() because it is broken in node v12 and v14
169
163
  resource.runInAsyncScope(() => {
170
164
  if (error) {
165
+ reply[kReplyIsError] = true
171
166
  reply.send(error)
172
167
  } else {
173
168
  request.body = body
@@ -228,6 +223,7 @@ function rawBody (request, reply, options, parser, done) {
228
223
 
229
224
  if (err !== undefined) {
230
225
  err.statusCode = 400
226
+ reply[kReplyIsError] = true
231
227
  reply.code(err.statusCode).send(err)
232
228
  return
233
229
  }
@@ -279,12 +275,6 @@ function Parser (asString, asBuffer, bodyLimit, fn) {
279
275
  this.asBuffer = asBuffer
280
276
  this.bodyLimit = bodyLimit
281
277
  this.fn = fn
282
-
283
- // Check for deprecation syntax
284
- if (fn.length === (fn.constructor.name === 'AsyncFunction' ? 1 : 2)) {
285
- warning.emit('FSTDEP003')
286
- this.isDeprecatedSignature = true
287
- }
288
278
  }
289
279
 
290
280
  function buildContentTypeParser (c) {
package/lib/context.js CHANGED
@@ -1,15 +1,37 @@
1
1
  'use strict'
2
2
 
3
- const { kFourOhFourContext, kReplySerializerDefault } = require('./symbols.js')
3
+ const {
4
+ kFourOhFourContext,
5
+ kReplySerializerDefault,
6
+ kSchemaErrorFormatter,
7
+ kErrorHandler,
8
+ kReply,
9
+ kRequest,
10
+ kBodyLimit,
11
+ kLogLevel,
12
+ kContentTypeParser
13
+ } = require('./symbols.js')
4
14
 
5
15
  // Objects that holds the context of every request
6
16
  // Every route holds an instance of this object.
7
- function Context (schema, handler, Reply, Request, contentTypeParser, config, errorHandler, bodyLimit, logLevel, logSerializers, attachValidation, replySerializer, schemaErrorFormatter) {
17
+ function Context ({
18
+ schema,
19
+ handler,
20
+ config,
21
+ errorHandler,
22
+ bodyLimit,
23
+ logLevel,
24
+ logSerializers,
25
+ attachValidation,
26
+ replySerializer,
27
+ schemaErrorFormatter,
28
+ server
29
+ }) {
8
30
  this.schema = schema
9
31
  this.handler = handler
10
- this.Reply = Reply
11
- this.Request = Request
12
- this.contentTypeParser = contentTypeParser
32
+ this.Reply = server[kReply]
33
+ this.Request = server[kRequest]
34
+ this.contentTypeParser = server[kContentTypeParser]
13
35
  this.onRequest = null
14
36
  this.onSend = null
15
37
  this.onError = null
@@ -17,15 +39,19 @@ function Context (schema, handler, Reply, Request, contentTypeParser, config, er
17
39
  this.preHandler = null
18
40
  this.onResponse = null
19
41
  this.config = config
20
- this.errorHandler = errorHandler
42
+ this.errorHandler = errorHandler || server[kErrorHandler]
21
43
  this._middie = null
22
- this._parserOptions = { limit: bodyLimit || null }
23
- this.logLevel = logLevel
44
+ this._parserOptions = {
45
+ limit: bodyLimit || server[kBodyLimit]
46
+ }
47
+ this.logLevel = logLevel || server[kLogLevel]
24
48
  this.logSerializers = logSerializers
25
49
  this[kFourOhFourContext] = null
26
50
  this.attachValidation = attachValidation
27
51
  this[kReplySerializerDefault] = replySerializer
28
- this.schemaErrorFormatter = schemaErrorFormatter || defaultSchemaErrorFormatter
52
+ this.schemaErrorFormatter = schemaErrorFormatter || server[kSchemaErrorFormatter] || defaultSchemaErrorFormatter
53
+
54
+ this.server = server
29
55
  }
30
56
 
31
57
  function defaultSchemaErrorFormatter (errors, dataVar) {
@@ -35,7 +61,7 @@ function defaultSchemaErrorFormatter (errors, dataVar) {
35
61
  // eslint-disable-next-line no-var
36
62
  for (var i = 0; i !== errors.length; ++i) {
37
63
  const e = errors[i]
38
- text += dataVar + (e.dataPath || '') + ' ' + e.message + separator
64
+ text += dataVar + (e.instancePath || '') + ' ' + e.message + separator
39
65
  }
40
66
  return new Error(text.slice(0, -separator.length))
41
67
  }
package/lib/decorate.js CHANGED
@@ -5,7 +5,8 @@
5
5
  const {
6
6
  kReply,
7
7
  kRequest,
8
- kState
8
+ kState,
9
+ kHasBeenDecorated
9
10
  } = require('./symbols.js')
10
11
 
11
12
  const {
@@ -40,6 +41,7 @@ function decorateConstructor (konstructor, name, fn, dependencies) {
40
41
  throw new FST_ERR_DEC_ALREADY_PRESENT(name)
41
42
  }
42
43
 
44
+ konstructor[kHasBeenDecorated] = true
43
45
  checkDependencies(konstructor, name, dependencies)
44
46
 
45
47
  if (fn && (typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
@@ -0,0 +1,158 @@
1
+ 'use strict'
2
+
3
+ const statusCodes = require('http').STATUS_CODES
4
+ const wrapThenable = require('./wrapThenable')
5
+ const {
6
+ kReplyHeaders, kReplyNextErrorHandler, kReplyIsRunningOnErrorHook, kReplyHasStatusCode
7
+ } = require('./symbols.js')
8
+
9
+ const {
10
+ FST_ERR_REP_INVALID_PAYLOAD_TYPE
11
+ } = require('./errors')
12
+
13
+ const { getSchemaSerializer } = require('./schemas')
14
+
15
+ const serializeError = require('./error-serializer')
16
+
17
+ const rootErrorHandler = {
18
+ func: defaultErrorHandler,
19
+ toJSON () {
20
+ return this.func.name.toString() + '()'
21
+ }
22
+ }
23
+
24
+ function handleError (reply, error, cb) {
25
+ reply[kReplyIsRunningOnErrorHook] = false
26
+
27
+ const context = reply.context
28
+ if (reply[kReplyNextErrorHandler] === false) {
29
+ fallbackErrorHandler(error, reply, function (reply, payload) {
30
+ try {
31
+ reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders])
32
+ } catch (error) {
33
+ reply.log.warn(
34
+ { req: reply.request, res: reply, err: error },
35
+ error && error.message
36
+ )
37
+ reply.raw.writeHead(reply.raw.statusCode)
38
+ }
39
+ reply.raw.end(payload)
40
+ })
41
+ return
42
+ }
43
+ const errorHandler = reply[kReplyNextErrorHandler] || context.errorHandler
44
+
45
+ // In case the error handler throws, we set the next errorHandler so we can error again
46
+ reply[kReplyNextErrorHandler] = Object.getPrototypeOf(errorHandler)
47
+
48
+ delete reply[kReplyHeaders]['content-length']
49
+
50
+ const func = errorHandler.func
51
+
52
+ if (!func) {
53
+ reply[kReplyNextErrorHandler] = false
54
+ fallbackErrorHandler(error, reply, cb)
55
+ return
56
+ }
57
+
58
+ const result = func(error, reply.request, reply)
59
+ if (result !== undefined) {
60
+ if (result !== null && typeof result.then === 'function') {
61
+ wrapThenable(result, reply)
62
+ } else {
63
+ reply.send(result)
64
+ }
65
+ }
66
+ }
67
+
68
+ function defaultErrorHandler (error, request, reply) {
69
+ setErrorHeaders(error, reply)
70
+ if (!reply[kReplyHasStatusCode] || reply.statusCode === 200) {
71
+ const statusCode = error.statusCode || error.status
72
+ reply.code(statusCode >= 400 ? statusCode : 500)
73
+ }
74
+ if (reply.statusCode < 500) {
75
+ reply.log.info(
76
+ { res: reply, err: error },
77
+ error && error.message
78
+ )
79
+ } else {
80
+ reply.log.error(
81
+ { req: request, res: reply, err: error },
82
+ error && error.message
83
+ )
84
+ }
85
+ reply.send(error)
86
+ }
87
+
88
+ function fallbackErrorHandler (error, reply, cb) {
89
+ const res = reply.raw
90
+ const statusCode = reply.statusCode
91
+ let payload
92
+ try {
93
+ const serializerFn = getSchemaSerializer(reply.context, statusCode)
94
+ payload = (serializerFn === false)
95
+ ? serializeError({
96
+ error: statusCodes[statusCode + ''],
97
+ code: error.code,
98
+ message: error.message,
99
+ statusCode
100
+ })
101
+ : serializerFn(Object.create(error, {
102
+ error: { value: statusCodes[statusCode + ''] },
103
+ message: { value: error.message },
104
+ statusCode: { value: statusCode }
105
+ }))
106
+ } catch (err) {
107
+ // error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization
108
+ reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed')
109
+ reply.code(500)
110
+ payload = serializeError({
111
+ error: statusCodes['500'],
112
+ message: err.message,
113
+ statusCode: 500
114
+ })
115
+ }
116
+
117
+ if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
118
+ payload = serializeError(new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload))
119
+ }
120
+
121
+ reply[kReplyHeaders]['content-type'] = 'application/json; charset=utf-8'
122
+ reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
123
+
124
+ cb(reply, payload)
125
+ }
126
+
127
+ function buildErrorHandler (parent = rootErrorHandler, func) {
128
+ if (!func) {
129
+ return parent
130
+ }
131
+
132
+ const errorHandler = Object.create(parent)
133
+ errorHandler.func = func
134
+ return errorHandler
135
+ }
136
+
137
+ function setErrorHeaders (error, reply) {
138
+ const res = reply.raw
139
+ let statusCode = res.statusCode
140
+ statusCode = (statusCode >= 400) ? statusCode : 500
141
+ // treat undefined and null as same
142
+ if (error != null) {
143
+ if (error.headers !== undefined) {
144
+ reply.headers(error.headers)
145
+ }
146
+ if (error.status >= 400) {
147
+ statusCode = error.status
148
+ } else if (error.statusCode >= 400) {
149
+ statusCode = error.statusCode
150
+ }
151
+ }
152
+ res.statusCode = statusCode
153
+ }
154
+
155
+ module.exports = {
156
+ buildErrorHandler,
157
+ handleError
158
+ }
@@ -0,0 +1,257 @@
1
+ // This file is autogenerated by build/build-error-serializer.js, do not edit
2
+ /* istanbul ignore file */
3
+ module.exports = $main
4
+ 'use strict'
5
+
6
+
7
+ function $pad2Zeros (num) {
8
+ const s = '00' + num
9
+ return s[s.length - 2] + s[s.length - 1]
10
+ }
11
+
12
+ function $asAny (i) {
13
+ return JSON.stringify(i)
14
+ }
15
+
16
+ function $asNull () {
17
+ return 'null'
18
+ }
19
+
20
+ function $asInteger (i) {
21
+ if (typeof i === 'bigint') {
22
+ return i.toString()
23
+ } else if (Number.isInteger(i)) {
24
+ return $asNumber(i)
25
+ } else {
26
+ /* eslint no-undef: "off" */
27
+ return $asNumber(parseInteger(i))
28
+ }
29
+ }
30
+
31
+ function $asIntegerNullable (i) {
32
+ return i === null ? null : $asInteger(i)
33
+ }
34
+
35
+ function $asNumber (i) {
36
+ const num = Number(i)
37
+ if (isNaN(num)) {
38
+ return 'null'
39
+ } else {
40
+ return '' + num
41
+ }
42
+ }
43
+
44
+ function $asNumberNullable (i) {
45
+ return i === null ? null : $asNumber(i)
46
+ }
47
+
48
+ function $asBoolean (bool) {
49
+ return bool && 'true' || 'false' // eslint-disable-line
50
+ }
51
+
52
+ function $asBooleanNullable (bool) {
53
+ return bool === null ? null : $asBoolean(bool)
54
+ }
55
+
56
+ function $asDatetime (date, skipQuotes) {
57
+ const quotes = skipQuotes === true ? '' : '"'
58
+ if (date instanceof Date) {
59
+ return quotes + date.toISOString() + quotes
60
+ } else if (date && typeof date.toISOString === 'function') {
61
+ return quotes + date.toISOString() + quotes
62
+ } else {
63
+ return $asString(date, skipQuotes)
64
+ }
65
+ }
66
+
67
+ function $asDate (date, skipQuotes) {
68
+ const quotes = skipQuotes === true ? '' : '"'
69
+ if (date instanceof Date) {
70
+ return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000 )).toISOString().slice(0, 10) + quotes
71
+ } else if (date && typeof date.format === 'function') {
72
+ return quotes + date.format('YYYY-MM-DD') + quotes
73
+ } else {
74
+ return $asString(date, skipQuotes)
75
+ }
76
+ }
77
+
78
+ function $asTime (date, skipQuotes) {
79
+ const quotes = skipQuotes === true ? '' : '"'
80
+ if (date instanceof Date) {
81
+ const hour = new Intl.DateTimeFormat('en', { hour: 'numeric', hour12: false }).format(date)
82
+ const minute = new Intl.DateTimeFormat('en', { minute: 'numeric' }).format(date)
83
+ const second = new Intl.DateTimeFormat('en', { second: 'numeric' }).format(date)
84
+ return quotes + $pad2Zeros(hour) + ':' + $pad2Zeros(minute) + ':' + $pad2Zeros(second) + quotes
85
+ } else if (date && typeof date.format === 'function') {
86
+ return quotes + date.format('HH:mm:ss') + quotes
87
+ } else {
88
+ return $asString(date, skipQuotes)
89
+ }
90
+ }
91
+
92
+ function $asString (str, skipQuotes) {
93
+ const quotes = skipQuotes === true ? '' : '"'
94
+ if (str instanceof Date) {
95
+ return quotes + str.toISOString() + quotes
96
+ } else if (str === null) {
97
+ return quotes + quotes
98
+ } else if (str instanceof RegExp) {
99
+ str = str.source
100
+ } else if (typeof str !== 'string') {
101
+ str = str.toString()
102
+ }
103
+ // If we skipQuotes it means that we are using it as test
104
+ // no need to test the string length for the render
105
+ if (skipQuotes) {
106
+ return str
107
+ }
108
+
109
+ if (str.length < 42) {
110
+ return $asStringSmall(str)
111
+ } else {
112
+ return JSON.stringify(str)
113
+ }
114
+ }
115
+
116
+ function $asStringNullable (str) {
117
+ return str === null ? null : $asString(str)
118
+ }
119
+
120
+ // magically escape strings for json
121
+ // relying on their charCodeAt
122
+ // everything below 32 needs JSON.stringify()
123
+ // every string that contain surrogate needs JSON.stringify()
124
+ // 34 and 92 happens all the time, so we
125
+ // have a fast case for them
126
+ function $asStringSmall (str) {
127
+ const l = str.length
128
+ let result = ''
129
+ let last = 0
130
+ let found = false
131
+ let surrogateFound = false
132
+ let point = 255
133
+ // eslint-disable-next-line
134
+ for (var i = 0; i < l && point >= 32; i++) {
135
+ point = str.charCodeAt(i)
136
+ if (point >= 0xD800 && point <= 0xDFFF) {
137
+ // The current character is a surrogate.
138
+ surrogateFound = true
139
+ }
140
+ if (point === 34 || point === 92) {
141
+ result += str.slice(last, i) + '\\'
142
+ last = i
143
+ found = true
144
+ }
145
+ }
146
+
147
+ if (!found) {
148
+ result = str
149
+ } else {
150
+ result += str.slice(last)
151
+ }
152
+ return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"'
153
+ }
154
+
155
+
156
+
157
+ /**
158
+ * Used by schemas that are dependant on calling 'ajv.validate' during runtime,
159
+ * it stores the value of the '$id' property of the schema (if it has it) inside
160
+ * a cache which is used to figure out if the schema was compiled into a validator
161
+ * by ajv on a previous call, if it was then the '$id' string will be used to
162
+ * invoke 'ajv.validate', this allows:
163
+ *
164
+ * 1. Schemas that depend on ajv.validate calls to leverage ajv caching system.
165
+ * 2. To avoid errors, since directly invoking 'ajv.validate' with the same
166
+ * schema (that contains an '$id' property) twice will throw an error.
167
+ */
168
+ const $validateWithAjv = (function() {
169
+ const cache = new Set()
170
+
171
+ return function (schema, target) {
172
+ const id = schema.$id
173
+
174
+ if (!id) {
175
+ return ajv.validate(schema, target)
176
+ }
177
+
178
+ const cached = cache.has(id)
179
+
180
+ if (cached) {
181
+ return ajv.validate(id, target)
182
+ } else {
183
+ cache.add(id)
184
+ return ajv.validate(schema, target)
185
+ }
186
+ }
187
+ })()
188
+
189
+
190
+ function parseInteger(int) { return Math.trunc(int) }
191
+
192
+ function $main (input) {
193
+
194
+ var obj = (input && typeof input.toJSON === 'function')
195
+ ? input.toJSON()
196
+ : input
197
+
198
+ var json = '{'
199
+ var addComma = false
200
+
201
+ var t = Number(obj["statusCode"])
202
+ if (!isNaN(t)) {
203
+
204
+ if (addComma) {
205
+ json += ','
206
+ } else {
207
+ addComma = true
208
+ }
209
+
210
+ json += "\"statusCode\"" + ':' + t
211
+
212
+ }
213
+
214
+ if (obj["code"] !== undefined) {
215
+
216
+ if (addComma) {
217
+ json += ','
218
+ } else {
219
+ addComma = true
220
+ }
221
+
222
+ json += "\"code\"" + ':'
223
+ json += $asString(obj["code"])
224
+ }
225
+
226
+ if (obj["error"] !== undefined) {
227
+
228
+ if (addComma) {
229
+ json += ','
230
+ } else {
231
+ addComma = true
232
+ }
233
+
234
+ json += "\"error\"" + ':'
235
+ json += $asString(obj["error"])
236
+ }
237
+
238
+ if (obj["message"] !== undefined) {
239
+
240
+ if (addComma) {
241
+ json += ','
242
+ } else {
243
+ addComma = true
244
+ }
245
+
246
+ json += "\"message\"" + ':'
247
+ json += $asString(obj["message"])
248
+ }
249
+
250
+ json += '}'
251
+ return json
252
+ }
253
+
254
+
255
+ ;
256
+ return $main
257
+
package/lib/errors.js CHANGED
@@ -110,6 +110,11 @@ const codes = {
110
110
  500
111
111
  ),
112
112
 
113
+ FST_ERR_HOOK_TIMEOUT: createError(
114
+ 'FST_ERR_HOOK_TIMEOUT',
115
+ "A callback for '%s' hook timed out. You may have forgotten to call 'done' function or to resolve a Promise"
116
+ ),
117
+
113
118
  /**
114
119
  * logger
115
120
  */
@@ -147,6 +152,14 @@ const codes = {
147
152
  'FST_ERR_BAD_STATUS_CODE',
148
153
  'Called reply with an invalid status code: %s'
149
154
  ),
155
+ FST_ERR_BAD_TRAILER_NAME: createError(
156
+ 'FST_ERR_BAD_TRAILER_NAME',
157
+ 'Called reply.trailer with an invalid header name: %s'
158
+ ),
159
+ FST_ERR_BAD_TRAILER_VALUE: createError(
160
+ 'FST_ERR_BAD_TRAILER_VALUE',
161
+ "Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function."
162
+ ),
150
163
 
151
164
  /**
152
165
  * schemas
@@ -172,14 +185,6 @@ const codes = {
172
185
  'Failed building the serialization schema for %s: %s, due to error %s'
173
186
  ),
174
187
 
175
- /**
176
- * wrapThenable
177
- */
178
- FST_ERR_PROMISE_NOT_FULFILLED: createError(
179
- 'FST_ERR_PROMISE_NOT_FULFILLED',
180
- "Promise may not be fulfilled with 'undefined' when statusCode is not 204"
181
- ),
182
-
183
188
  /**
184
189
  * http2
185
190
  */
@@ -199,6 +204,10 @@ const codes = {
199
204
  /**
200
205
  * router
201
206
  */
207
+ FST_ERR_DUPLICATED_ROUTE: createError(
208
+ 'FST_ERR_DUPLICATED_ROUTE',
209
+ "Method '%s' already declared for route '%s'"
210
+ ),
202
211
  FST_ERR_BAD_URL: createError(
203
212
  'FST_ERR_BAD_URL',
204
213
  "'%s' is not a valid url component",
@@ -234,7 +243,40 @@ const codes = {
234
243
  FST_ERR_PLUGIN_VERSION_MISMATCH: createError(
235
244
  'FST_ERR_PLUGIN_VERSION_MISMATCH',
236
245
  "fastify-plugin: %s - expected '%s' fastify version, '%s' is installed"
237
- )
246
+ ),
247
+
248
+ /**
249
+ * Avvio Errors map
250
+ */
251
+ AVVIO_ERRORS_MAP: {
252
+ AVV_ERR_CALLBACK_NOT_FN: createError(
253
+ 'FST_ERR_PLUGIN_CALLBACK_NOT_FN',
254
+ 'fastify-plugin: %s'
255
+ ),
256
+ AVV_ERR_PLUGIN_NOT_VALID: createError(
257
+ 'FST_ERR_PLUGIN_NOT_VALID',
258
+ 'fastify-plugin: %s'
259
+ ),
260
+ AVV_ERR_ROOT_PLG_BOOTED: createError(
261
+ 'FST_ERR_ROOT_PLG_BOOTED',
262
+ 'fastify-plugin: %s'
263
+ ),
264
+ AVV_ERR_PARENT_PLG_LOADED: createError(
265
+ 'FST_ERR_PARENT_PLUGIN_BOOTED',
266
+ 'fastify-plugin: %s'
267
+ ),
268
+ AVV_ERR_READY_TIMEOUT: createError(
269
+ 'FST_ERR_PLUGIN_TIMEOUT',
270
+ 'fastify-plugin: %s'
271
+ )
272
+ },
273
+
274
+ // Util function
275
+ appendStackTrace (oldErr, newErr) {
276
+ newErr.cause = oldErr
277
+
278
+ return newErr
279
+ }
238
280
  }
239
281
 
240
282
  module.exports = codes