fastify 2.10.0 → 2.11.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/fastify.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  /// <reference types="node" />
6
6
 
7
+ import * as ajv from 'ajv'
7
8
  import * as http from 'http'
8
9
  import * as http2 from 'http2'
9
10
  import * as https from 'https'
@@ -184,6 +185,9 @@ declare namespace fastify {
184
185
  request: FastifyRequest
185
186
  }
186
187
  type TrustProxyFunction = (addr: string, index: number) => boolean
188
+ type ServerFactoryHandlerFunction = (request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse) => void
189
+ type ServerFactoryFunction = (handler: ServerFactoryHandlerFunction, options: ServerOptions) => http.Server | http2.Http2Server
190
+
187
191
  interface ServerOptions {
188
192
  caseSensitive?: boolean,
189
193
  ignoreTrailingSlash?: boolean,
@@ -191,6 +195,7 @@ declare namespace fastify {
191
195
  pluginTimeout?: number,
192
196
  disableRequestLogging?: boolean,
193
197
  onProtoPoisoning?: 'error' | 'remove' | 'ignore',
198
+ onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
194
199
  logger?: any,
195
200
  trustProxy?: string | number | boolean | Array<string> | TrustProxyFunction,
196
201
  maxParamLength?: number,
@@ -205,7 +210,15 @@ declare namespace fastify {
205
210
  deriveVersion<Context>(req: Object, ctx?: Context) : String,
206
211
  },
207
212
  modifyCoreObjects?: boolean,
208
- return503OnClosing?: boolean
213
+ return503OnClosing?: boolean,
214
+ genReqId?: () => number | string,
215
+ requestIdHeader?: string,
216
+ requestIdLogLabel?: string,
217
+ serverFactory?: ServerFactoryFunction,
218
+ ajv?: {
219
+ customOptions?: ajv.Options,
220
+ plugins?: Array<Array<any>|String>
221
+ }
209
222
  }
210
223
  interface ServerOptionsAsSecure extends ServerOptions {
211
224
  https: http2.SecureServerOptions
@@ -247,6 +260,9 @@ declare namespace fastify {
247
260
  > {
248
261
  schema?: RouteSchema
249
262
  attachValidation?: boolean
263
+ onSend?:
264
+ | FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
265
+ | Array<FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
250
266
  onRequest?:
251
267
  | FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
252
268
  | Array<FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
@@ -269,7 +285,9 @@ declare namespace fastify {
269
285
  schemaCompiler?: SchemaCompiler
270
286
  bodyLimit?: number
271
287
  logLevel?: string
288
+ logSerializers?: Object
272
289
  config?: any
290
+ version?: string
273
291
  prefixTrailingSlash?: 'slash' | 'no-slash' | 'both'
274
292
  }
275
293
 
@@ -296,6 +314,7 @@ declare namespace fastify {
296
314
  interface RegisterOptions<HttpServer, HttpRequest, HttpResponse> {
297
315
  [key: string]: any,
298
316
  prefix?: string,
317
+ logSerializers?: Object
299
318
  }
300
319
 
301
320
  /**
@@ -542,6 +561,11 @@ declare namespace fastify {
542
561
  */
543
562
  use(path: string, middleware: Middleware<HttpServer, HttpRequest, HttpResponse>): void
544
563
 
564
+ /**
565
+ * Apply the given middleware to routes matching the given multiple path
566
+ */
567
+ use(path: string[], middleware: Middleware<HttpServer, HttpRequest, HttpResponse>): void
568
+
545
569
  /**
546
570
  * Registers a plugin
547
571
  */
package/fastify.js CHANGED
@@ -10,6 +10,7 @@ const {
10
10
  kBodyLimit,
11
11
  kRoutePrefix,
12
12
  kLogLevel,
13
+ kLogSerializers,
13
14
  kHooks,
14
15
  kSchemas,
15
16
  kSchemaCompiler,
@@ -68,6 +69,21 @@ function build (options) {
68
69
  const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
69
70
  const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
70
71
  const disableRequestLogging = options.disableRequestLogging || false
72
+ const ajvOptions = Object.assign({
73
+ customOptions: {},
74
+ plugins: []
75
+ }, options.ajv)
76
+
77
+ // Ajv options
78
+ if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
79
+ throw new Error(`ajv.customOptions option should be an object, instead got '${typeof ajvOptions.customOptions}'`)
80
+ }
81
+ if (!ajvOptions.plugins || !Array.isArray(ajvOptions.plugins)) {
82
+ throw new Error(`ajv.plugins option should be an array, instead got '${typeof ajvOptions.customOptions}'`)
83
+ }
84
+ ajvOptions.plugins = ajvOptions.plugins.map(plugin => {
85
+ return Array.isArray(plugin) ? plugin : [plugin]
86
+ })
71
87
 
72
88
  // Instance Fastify components
73
89
  const { logger, hasLogger } = createLogger(options)
@@ -81,11 +97,13 @@ function build (options) {
81
97
  options.requestIdLogLabel = requestIdLogLabel
82
98
  options.modifyCoreObjects = modifyCoreObjects
83
99
  options.disableRequestLogging = disableRequestLogging
100
+ options.ajv = ajvOptions
84
101
 
85
102
  // Default router
86
103
  const router = buildRouting({
87
104
  config: {
88
105
  defaultRoute: defaultRoute,
106
+ onBadUrl: onBadUrl,
89
107
  ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,
90
108
  maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
91
109
  caseSensitive: options.caseSensitive,
@@ -118,12 +136,17 @@ function build (options) {
118
136
  [kBodyLimit]: bodyLimit,
119
137
  [kRoutePrefix]: '',
120
138
  [kLogLevel]: '',
139
+ [kLogSerializers]: null,
121
140
  [kHooks]: new Hooks(),
122
141
  [kSchemas]: schemas,
123
142
  [kSchemaCompiler]: null,
124
143
  [kSchemaResolver]: null,
125
144
  [kReplySerializerDefault]: null,
126
- [kContentTypeParser]: new ContentTypeParser(bodyLimit, (options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning)),
145
+ [kContentTypeParser]: new ContentTypeParser(
146
+ bodyLimit,
147
+ (options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning),
148
+ (options.onConstructorPoisoning || defaultInitOptions.onConstructorPoisoning)
149
+ ),
127
150
  [kReply]: Reply.buildReply(Reply),
128
151
  [kRequest]: Request.buildRequest(Request),
129
152
  [kMiddlewares]: [],
@@ -404,6 +427,15 @@ function build (options) {
404
427
  fourOhFour.router.lookup(req, res)
405
428
  }
406
429
 
430
+ function onBadUrl (path, req, res) {
431
+ const body = `{"error":"Bad Request","message":"'${path}' is not a valid url component","statusCode":400}`
432
+ res.writeHead(400, {
433
+ 'Content-Type': 'application/json',
434
+ 'Content-Length': body.length
435
+ })
436
+ res.end(body)
437
+ }
438
+
407
439
  function setNotFoundHandler (opts, handler) {
408
440
  throwIfAlreadyStarted('Cannot call "setNotFoundHandler" when fastify instance is already started!')
409
441
 
@@ -467,11 +499,15 @@ function override (old, fn, opts) {
467
499
  instance[pluginUtils.registeredPlugins] = Object.create(instance[pluginUtils.registeredPlugins])
468
500
  instance[kPluginNameChain] = [pluginUtils.getPluginName(fn) || pluginUtils.getFuncPreview(fn)]
469
501
 
502
+ if (instance[kLogSerializers] || opts.logSerializers) {
503
+ instance[kLogSerializers] = Object.assign(Object.create(instance[kLogSerializers]), opts.logSerializers)
504
+ }
505
+
470
506
  if (opts.prefix) {
471
507
  instance[kFourOhFour].arrange404(instance)
472
508
  }
473
509
 
474
- for (const hook of instance[kGlobalHooks].onRegister) hook.call(this, instance)
510
+ for (const hook of instance[kGlobalHooks].onRegister) hook.call(this, instance, opts)
475
511
 
476
512
  return instance
477
513
  }
@@ -14,10 +14,11 @@ var validate = (function() {
14
14
  if ((data && typeof data === "object" && !Array.isArray(data))) {
15
15
  if (data.bodyLimit === undefined) data.bodyLimit = 1048576;
16
16
  if (data.caseSensitive === undefined) data.caseSensitive = true;
17
- if (data.disableRequestLogging === undefined) data.disableRequestLogging = false;
18
17
  if (data.ignoreTrailingSlash === undefined) data.ignoreTrailingSlash = false;
18
+ if (data.disableRequestLogging === undefined) data.disableRequestLogging = false;
19
19
  if (data.maxParamLength === undefined) data.maxParamLength = 100;
20
20
  if (data.onProtoPoisoning === undefined) data.onProtoPoisoning = "error";
21
+ if (data.onConstructorPoisoning === undefined) data.onConstructorPoisoning = "ignore";
21
22
  if (data.pluginTimeout === undefined) data.pluginTimeout = 10000;
22
23
  if (data.requestIdHeader === undefined) data.requestIdHeader = "request-id";
23
24
  if (data.requestIdLogLabel === undefined) data.requestIdLogLabel = "reqId";
@@ -321,80 +322,81 @@ var validate = (function() {
321
322
  }
322
323
  var valid1 = errors === errs_1;
323
324
  if (valid1) {
324
- var data1 = data.maxParamLength;
325
+ var data1 = data.disableRequestLogging;
325
326
  var errs_1 = errors;
326
- if ((typeof data1 !== "number" || (data1 % 1) || data1 !== data1)) {
327
+ if (typeof data1 !== "boolean") {
327
328
  var dataType1 = typeof data1;
328
329
  var coerced1 = undefined;
329
- if (dataType1 == 'boolean' || data1 === null || (dataType1 == 'string' && data1 && data1 == +data1 && !(data1 % 1))) coerced1 = +data1;
330
+ if (data1 === 'false' || data1 === 0 || data1 === null) coerced1 = false;
331
+ else if (data1 === 'true' || data1 === 1) coerced1 = true;
330
332
  if (coerced1 === undefined) {
331
333
  validate.errors = [{
332
334
  keyword: 'type',
333
- dataPath: (dataPath || '') + '.maxParamLength',
334
- schemaPath: '#/properties/maxParamLength/type',
335
+ dataPath: (dataPath || '') + '.disableRequestLogging',
336
+ schemaPath: '#/properties/disableRequestLogging/type',
335
337
  params: {
336
- type: 'integer'
338
+ type: 'boolean'
337
339
  },
338
- message: 'should be integer'
340
+ message: 'should be boolean'
339
341
  }];
340
342
  return false;
341
343
  } else {
342
344
  data1 = coerced1;
343
- data['maxParamLength'] = coerced1;
345
+ data['disableRequestLogging'] = coerced1;
344
346
  }
345
347
  }
346
348
  var valid1 = errors === errs_1;
347
349
  if (valid1) {
348
- var data1 = data.onProtoPoisoning;
350
+ var data1 = data.maxParamLength;
349
351
  var errs_1 = errors;
350
- if (typeof data1 !== "string") {
352
+ if ((typeof data1 !== "number" || (data1 % 1) || data1 !== data1)) {
351
353
  var dataType1 = typeof data1;
352
354
  var coerced1 = undefined;
353
- if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
354
- else if (data1 === null) coerced1 = '';
355
+ if (dataType1 == 'boolean' || data1 === null || (dataType1 == 'string' && data1 && data1 == +data1 && !(data1 % 1))) coerced1 = +data1;
355
356
  if (coerced1 === undefined) {
356
357
  validate.errors = [{
357
358
  keyword: 'type',
358
- dataPath: (dataPath || '') + '.onProtoPoisoning',
359
- schemaPath: '#/properties/onProtoPoisoning/type',
359
+ dataPath: (dataPath || '') + '.maxParamLength',
360
+ schemaPath: '#/properties/maxParamLength/type',
360
361
  params: {
361
- type: 'string'
362
+ type: 'integer'
362
363
  },
363
- message: 'should be string'
364
+ message: 'should be integer'
364
365
  }];
365
366
  return false;
366
367
  } else {
367
368
  data1 = coerced1;
368
- data['onProtoPoisoning'] = coerced1;
369
+ data['maxParamLength'] = coerced1;
369
370
  }
370
371
  }
371
372
  var valid1 = errors === errs_1;
372
373
  if (valid1) {
373
- var data1 = data.pluginTimeout;
374
+ var data1 = data.onProtoPoisoning;
374
375
  var errs_1 = errors;
375
- if ((typeof data1 !== "number" || (data1 % 1) || data1 !== data1)) {
376
+ if (typeof data1 !== "string") {
376
377
  var dataType1 = typeof data1;
377
378
  var coerced1 = undefined;
378
- if (dataType1 == 'boolean' || data1 === null || (dataType1 == 'string' && data1 && data1 == +data1 && !(data1 % 1))) coerced1 = +data1;
379
+ if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
380
+ else if (data1 === null) coerced1 = '';
379
381
  if (coerced1 === undefined) {
380
382
  validate.errors = [{
381
383
  keyword: 'type',
382
- dataPath: (dataPath || '') + '.pluginTimeout',
383
- schemaPath: '#/properties/pluginTimeout/type',
384
+ dataPath: (dataPath || '') + '.onProtoPoisoning',
385
+ schemaPath: '#/properties/onProtoPoisoning/type',
384
386
  params: {
385
- type: 'integer'
387
+ type: 'string'
386
388
  },
387
- message: 'should be integer'
389
+ message: 'should be string'
388
390
  }];
389
391
  return false;
390
392
  } else {
391
393
  data1 = coerced1;
392
- data['pluginTimeout'] = coerced1;
394
+ data['onProtoPoisoning'] = coerced1;
393
395
  }
394
396
  }
395
397
  var valid1 = errors === errs_1;
396
398
  if (valid1) {
397
- var data1 = data.requestIdHeader;
399
+ var data1 = data.onConstructorPoisoning;
398
400
  var errs_1 = errors;
399
401
  if (typeof data1 !== "string") {
400
402
  var dataType1 = typeof data1;
@@ -404,8 +406,8 @@ var validate = (function() {
404
406
  if (coerced1 === undefined) {
405
407
  validate.errors = [{
406
408
  keyword: 'type',
407
- dataPath: (dataPath || '') + '.requestIdHeader',
408
- schemaPath: '#/properties/requestIdHeader/type',
409
+ dataPath: (dataPath || '') + '.onConstructorPoisoning',
410
+ schemaPath: '#/properties/onConstructorPoisoning/type',
409
411
  params: {
410
412
  type: 'string'
411
413
  },
@@ -414,35 +416,86 @@ var validate = (function() {
414
416
  return false;
415
417
  } else {
416
418
  data1 = coerced1;
417
- data['requestIdHeader'] = coerced1;
419
+ data['onConstructorPoisoning'] = coerced1;
418
420
  }
419
421
  }
420
422
  var valid1 = errors === errs_1;
421
423
  if (valid1) {
422
- var data1 = data.requestIdLogLabel;
424
+ var data1 = data.pluginTimeout;
423
425
  var errs_1 = errors;
424
- if (typeof data1 !== "string") {
426
+ if ((typeof data1 !== "number" || (data1 % 1) || data1 !== data1)) {
425
427
  var dataType1 = typeof data1;
426
428
  var coerced1 = undefined;
427
- if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
428
- else if (data1 === null) coerced1 = '';
429
+ if (dataType1 == 'boolean' || data1 === null || (dataType1 == 'string' && data1 && data1 == +data1 && !(data1 % 1))) coerced1 = +data1;
429
430
  if (coerced1 === undefined) {
430
431
  validate.errors = [{
431
432
  keyword: 'type',
432
- dataPath: (dataPath || '') + '.requestIdLogLabel',
433
- schemaPath: '#/properties/requestIdLogLabel/type',
433
+ dataPath: (dataPath || '') + '.pluginTimeout',
434
+ schemaPath: '#/properties/pluginTimeout/type',
434
435
  params: {
435
- type: 'string'
436
+ type: 'integer'
436
437
  },
437
- message: 'should be string'
438
+ message: 'should be integer'
438
439
  }];
439
440
  return false;
440
441
  } else {
441
442
  data1 = coerced1;
442
- data['requestIdLogLabel'] = coerced1;
443
+ data['pluginTimeout'] = coerced1;
443
444
  }
444
445
  }
445
446
  var valid1 = errors === errs_1;
447
+ if (valid1) {
448
+ var data1 = data.requestIdHeader;
449
+ var errs_1 = errors;
450
+ if (typeof data1 !== "string") {
451
+ var dataType1 = typeof data1;
452
+ var coerced1 = undefined;
453
+ if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
454
+ else if (data1 === null) coerced1 = '';
455
+ if (coerced1 === undefined) {
456
+ validate.errors = [{
457
+ keyword: 'type',
458
+ dataPath: (dataPath || '') + '.requestIdHeader',
459
+ schemaPath: '#/properties/requestIdHeader/type',
460
+ params: {
461
+ type: 'string'
462
+ },
463
+ message: 'should be string'
464
+ }];
465
+ return false;
466
+ } else {
467
+ data1 = coerced1;
468
+ data['requestIdHeader'] = coerced1;
469
+ }
470
+ }
471
+ var valid1 = errors === errs_1;
472
+ if (valid1) {
473
+ var data1 = data.requestIdLogLabel;
474
+ var errs_1 = errors;
475
+ if (typeof data1 !== "string") {
476
+ var dataType1 = typeof data1;
477
+ var coerced1 = undefined;
478
+ if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
479
+ else if (data1 === null) coerced1 = '';
480
+ if (coerced1 === undefined) {
481
+ validate.errors = [{
482
+ keyword: 'type',
483
+ dataPath: (dataPath || '') + '.requestIdLogLabel',
484
+ schemaPath: '#/properties/requestIdLogLabel/type',
485
+ params: {
486
+ type: 'string'
487
+ },
488
+ message: 'should be string'
489
+ }];
490
+ return false;
491
+ } else {
492
+ data1 = coerced1;
493
+ data['requestIdLogLabel'] = coerced1;
494
+ }
495
+ }
496
+ var valid1 = errors === errs_1;
497
+ }
498
+ }
446
499
  }
447
500
  }
448
501
  }
@@ -507,11 +560,11 @@ validate.schema = {
507
560
  "setDefaultValue": true
508
561
  }
509
562
  },
510
- "disableRequestLogging": {
563
+ "ignoreTrailingSlash": {
511
564
  "type": "boolean",
512
565
  "default": false
513
566
  },
514
- "ignoreTrailingSlash": {
567
+ "disableRequestLogging": {
515
568
  "type": "boolean",
516
569
  "default": false
517
570
  },
@@ -523,6 +576,10 @@ validate.schema = {
523
576
  "type": "string",
524
577
  "default": "error"
525
578
  },
579
+ "onConstructorPoisoning": {
580
+ "type": "string",
581
+ "default": "ignore"
582
+ },
526
583
  "pluginTimeout": {
527
584
  "type": "integer",
528
585
  "default": 10000
@@ -545,14 +602,4 @@ function customRule0 (schemaParamValue, validatedParamValue, validationSchemaObj
545
602
  return true
546
603
  }
547
604
 
548
- module.exports.defaultInitOptions = {
549
- "bodyLimit":1048576,
550
- "caseSensitive":true,
551
- "disableRequestLogging": false,
552
- "ignoreTrailingSlash":false,
553
- "maxParamLength":100,
554
- "onProtoPoisoning":"error",
555
- "pluginTimeout":10000,
556
- "requestIdHeader":"request-id",
557
- "requestIdLogLabel":"reqId"
558
- }
605
+ module.exports.defaultInitOptions = {"bodyLimit":1048576,"caseSensitive":true,"disableRequestLogging":false,"ignoreTrailingSlash":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"ignore","pluginTimeout":10000,"requestIdHeader":"request-id","requestIdLogLabel":"reqId"}
@@ -22,8 +22,8 @@ const {
22
22
  }
23
23
  } = require('./errors')
24
24
 
25
- function ContentTypeParser (bodyLimit, onProtoPoisoning) {
26
- this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning)
25
+ function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
26
+ this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
27
27
  this.customParsers = {}
28
28
  this.customParsers['application/json'] = new Parser(true, false, bodyLimit, this[kDefaultJsonParse])
29
29
  this.customParsers['text/plain'] = new Parser(true, false, bodyLimit, defaultPlainTextParser)
@@ -189,7 +189,7 @@ function rawBody (request, reply, options, parser, done) {
189
189
  }
190
190
  }
191
191
 
192
- function getDefaultJsonParser (onProtoPoisoning) {
192
+ function getDefaultJsonParser (onProtoPoisoning, onConstructorPoisoning) {
193
193
  return defaultJsonParser
194
194
 
195
195
  function defaultJsonParser (req, body, done) {
@@ -198,7 +198,7 @@ function getDefaultJsonParser (onProtoPoisoning) {
198
198
  }
199
199
 
200
200
  try {
201
- var json = secureJson.parse(body, { protoAction: onProtoPoisoning })
201
+ var json = secureJson.parse(body, { protoAction: onProtoPoisoning, constructorAction: onConstructorPoisoning })
202
202
  } catch (err) {
203
203
  err.statusCode = 400
204
204
  return done(err, undefined)
package/lib/context.js CHANGED
@@ -4,7 +4,7 @@ const { kFourOhFourContext, kReplySerializerDefault } = require('./symbols.js')
4
4
 
5
5
  // Objects that holds the context of every request
6
6
  // Every route holds an instance of this object.
7
- function Context (schema, handler, Reply, Request, contentTypeParser, config, errorHandler, bodyLimit, logLevel, attachValidation, replySerializer) {
7
+ function Context (schema, handler, Reply, Request, contentTypeParser, config, errorHandler, bodyLimit, logLevel, logSerializers, attachValidation, replySerializer) {
8
8
  this.schema = schema
9
9
  this.handler = handler
10
10
  this.Reply = Reply
@@ -20,6 +20,7 @@ function Context (schema, handler, Reply, Request, contentTypeParser, config, er
20
20
  this._middie = null
21
21
  this._parserOptions = { limit: bodyLimit || null }
22
22
  this.logLevel = logLevel
23
+ this.logSerializers = logSerializers
23
24
  this[kFourOhFourContext] = null
24
25
  this.attachValidation = attachValidation
25
26
  this[kReplySerializerDefault] = replySerializer
package/lib/logger.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * License: MIT (https://raw.githubusercontent.com/pinojs/pino-http/master/LICENSE)
7
7
  */
8
8
 
9
- const abstractLogging = require('abstract-logging')
9
+ const nullLogger = require('abstract-logging')
10
10
  const pino = require('pino')
11
11
  const { serializersSym } = pino.symbols
12
12
  const { isValidLogger } = require('./validation')
@@ -82,7 +82,7 @@ function createLogger (options) {
82
82
  })
83
83
  return { logger, hasLogger: true }
84
84
  } else if (!options.logger) {
85
- const logger = Object.create(abstractLogging)
85
+ const logger = nullLogger
86
86
  logger.child = () => logger
87
87
  return { logger, hasLogger: false }
88
88
  } else {
package/lib/route.js CHANGED
@@ -6,7 +6,7 @@ const Context = require('./context')
6
6
  const { buildMiddie, onRunMiddlewares } = require('./middleware')
7
7
  const { hookRunner, hookIterator } = require('./hooks')
8
8
  const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
9
- const supportedHooks = ['preParsing', 'preValidation', 'onRequest', 'preHandler', 'preSerialization', 'onResponse']
9
+ const supportedHooks = ['preParsing', 'preValidation', 'onRequest', 'preHandler', 'preSerialization', 'onResponse', 'onSend']
10
10
  const validation = require('./validation')
11
11
  const buildSchema = validation.build
12
12
  const { buildSchemaCompiler } = validation
@@ -22,8 +22,10 @@ const {
22
22
  const {
23
23
  kRoutePrefix,
24
24
  kLogLevel,
25
+ kLogSerializers,
25
26
  kHooks,
26
27
  kSchemas,
28
+ kOptions,
27
29
  kSchemaCompiler,
28
30
  kSchemaResolver,
29
31
  kContentTypeParser,
@@ -180,6 +182,10 @@ function buildRouting (options) {
180
182
  opts.prefix = prefix
181
183
  opts.logLevel = opts.logLevel || this[kLogLevel]
182
184
 
185
+ if (this[kLogSerializers] || opts.logSerializers) {
186
+ opts.logSerializers = Object.assign(Object.create(this[kLogSerializers]), opts.logSerializers)
187
+ }
188
+
183
189
  if (opts.attachValidation == null) {
184
190
  opts.attachValidation = false
185
191
  }
@@ -207,6 +213,7 @@ function buildRouting (options) {
207
213
  this._errorHandler,
208
214
  opts.bodyLimit,
209
215
  opts.logLevel,
216
+ opts.logSerializers,
210
217
  opts.attachValidation,
211
218
  this[kReplySerializerDefault]
212
219
  )
@@ -223,7 +230,7 @@ function buildRouting (options) {
223
230
  try {
224
231
  if (opts.schemaCompiler == null && this[kSchemaCompiler] == null) {
225
232
  const externalSchemas = this[kSchemas].getJsonSchemas({ onlyAbsoluteUri: true })
226
- this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, schemaCache))
233
+ this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, this[kOptions].ajv, schemaCache))
227
234
  }
228
235
 
229
236
  buildSchema(context, opts.schemaCompiler || this[kSchemaCompiler], this[kSchemas], this[kSchemaResolver])
@@ -312,7 +319,15 @@ function buildRouting (options) {
312
319
  }
313
320
  }
314
321
 
315
- var childLogger = logger.child({ [requestIdLogLabel]: req.id, level: context.logLevel })
322
+ var loggerOpts = {
323
+ [requestIdLogLabel]: req.id,
324
+ level: context.logLevel
325
+ }
326
+
327
+ if (context.logSerializers) {
328
+ loggerOpts.serializers = context.logSerializers
329
+ }
330
+ var childLogger = logger.child(loggerOpts)
316
331
  childLogger[kDisableRequestLogging] = disableRequestLogging
317
332
 
318
333
  // added hostname, ip, and ips back to the Node req object to maintain backward compatibility
package/lib/schemas.js CHANGED
@@ -135,7 +135,7 @@ Schemas.prototype.cleanId = function (schema) {
135
135
  }
136
136
 
137
137
  Schemas.prototype.getSchemaAnyway = function (schema) {
138
- if (schema.oneOf || schema.allOf || schema.anyOf) return schema
138
+ if (schema.oneOf || schema.allOf || schema.anyOf || schema.$merge || schema.$patch) return schema
139
139
  if (!schema.type || !schema.properties) {
140
140
  return {
141
141
  type: 'object',
package/lib/symbols.js CHANGED
@@ -5,6 +5,7 @@ const keys = {
5
5
  kBodyLimit: Symbol('fastify.bodyLimit'),
6
6
  kRoutePrefix: Symbol('fastify.routePrefix'),
7
7
  kLogLevel: Symbol('fastify.logLevel'),
8
+ kLogSerializers: Symbol('fastify.logSerializers'),
8
9
  kHooks: Symbol('fastify.hooks'),
9
10
  kSchemas: Symbol('fastify.schemas'),
10
11
  kSchemaCompiler: Symbol('fastify.schemaCompiler'),
package/lib/validation.js CHANGED
@@ -163,17 +163,22 @@ function schemaErrorsText (errors, dataVar) {
163
163
  return text.slice(0, -separator.length)
164
164
  }
165
165
 
166
- function buildSchemaCompiler (externalSchemas, cache) {
166
+ function buildSchemaCompiler (externalSchemas, options, cache) {
167
167
  // This instance of Ajv is private
168
168
  // it should not be customized or used
169
- const ajv = new Ajv({
169
+ const ajv = new Ajv(Object.assign({
170
170
  coerceTypes: true,
171
171
  useDefaults: true,
172
172
  removeAdditional: true,
173
173
  allErrors: true,
174
- nullable: true,
175
- cache
176
- })
174
+ nullable: true
175
+ }, options.customOptions, { cache }))
176
+
177
+ if (options.plugins && options.plugins.length > 0) {
178
+ for (const plugin of options.plugins) {
179
+ plugin[0](ajv, plugin[1])
180
+ }
181
+ }
177
182
 
178
183
  if (Array.isArray(externalSchemas)) {
179
184
  externalSchemas.forEach(s => ajv.addSchema(s))