fastify 4.2.1 → 4.3.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.
@@ -169,6 +169,8 @@ section.
169
169
  - [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware
170
170
  for CLS-based request ID generation. An out-of-the-box solution for adding
171
171
  request IDs into your logs.
172
+ - [`electron-server`](https://github.com/anonrig/electron-server) A plugin for
173
+ using Fastify without the need of consuming a port on Electron apps.
172
174
  - [`fast-water`](https://github.com/tswayne/fast-water) A Fastify plugin for
173
175
  waterline. Decorates Fastify with waterline models.
174
176
  - [`fastify-405`](https://github.com/Eomm/fastify-405) Fastify plugin that adds
@@ -2,9 +2,7 @@
2
2
 
3
3
  ## HTTP2
4
4
 
5
- _Fastify_ offers **experimental support** for HTTP2 starting from Node 8 LTS,
6
- which includes HTTP2 without a flag; HTTP2 is supported over either HTTPS or
7
- plaintext.
5
+ _Fastify_ supports HTTP2 over either HTTPS (h2) or plaintext (h2c).
8
6
 
9
7
  Currently, none of the HTTP2-specific APIs are available through _Fastify_, but
10
8
  Node's `req` and `res` can be accessed through our `Request` and `Reply`
@@ -20,6 +20,9 @@
20
20
  - [.callNotFound()](#callnotfound)
21
21
  - [.getResponseTime()](#getresponsetime)
22
22
  - [.type(contentType)](#typecontenttype)
23
+ - [.getSerializationFunction(schema | httpStatus)](#getserializationfunction)
24
+ - [.compileSerializationSchema(schema, httpStatus)](#compileserializationschema)
25
+ - [.serializeInput(data, [schema | httpStatus], [httpStatus])](#serializeinput)
23
26
  - [.serializer(func)](#serializerfunc)
24
27
  - [.raw](#raw)
25
28
  - [.sent](#sent)
@@ -60,6 +63,16 @@ object that exposes the following functions and properties:
60
63
  - `.serialize(payload)` - Serializes the specified payload using the default
61
64
  JSON serializer or using the custom serializer (if one is set) and returns the
62
65
  serialized payload.
66
+ - `.getSerializationFunction(schema | httpStatus)` - Returns the serialization
67
+ function for the specified schema or http status, if any of either are set.
68
+ - `.compileSerializationSchema(schema, httpStatus)` - Compiles the specified
69
+ schema and returns a serialization function using the default (or customized)
70
+ `SerializerCompiler`. The optional `httpStatus` is forwarded to the
71
+ `SerializerCompiler` if provided, default to `undefined`.
72
+ - `.serializeInput(data, schema, [,httpStatus])` - Serializes the specified data
73
+ using the specified schema and returns the serialized payload.
74
+ If the optional `httpStatus` is provided, the function will use the serializer
75
+ function given for that HTTP Status Code. Default to `undefined`.
63
76
  - `.serializer(function)` - Sets a custom serializer for the payload.
64
77
  - `.send(payload)` - Sends the payload to the user, could be a plain text, a
65
78
  buffer, JSON, stream, or an Error object.
@@ -326,6 +339,169 @@ reply.type('text/html')
326
339
  If the `Content-Type` has a JSON subtype, and the charset parameter is not set,
327
340
  `utf-8` will be used as the charset by default.
328
341
 
342
+ ### .getSerializationFunction(schema | httpStatus)
343
+ <a id="getserializationfunction"></a>
344
+
345
+ By calling this function using a provided `schema` or `httpStatus`,
346
+ it will return a `serialzation` function that can be used to
347
+ serialize diverse inputs. It returns `undefined` if no
348
+ serialization function was found using either of the provided inputs.
349
+
350
+ This heavily depends of the `schema#responses` attached to the route, or
351
+ the serialization functions compiled by using `compileSerializationSchema`.
352
+
353
+ ```js
354
+ const serialize = reply
355
+ .getSerializationFunction({
356
+ type: 'object',
357
+ properties: {
358
+ foo: {
359
+ type: 'string'
360
+ }
361
+ }
362
+ })
363
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
364
+
365
+ // or
366
+
367
+ const serialize = reply
368
+ .getSerializationFunction(200)
369
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
370
+ ```
371
+
372
+ See [.compileSerializationSchema(schema, [httpStatus])](#compileserializationschema)
373
+ for more information on how to compile serialization schemas.
374
+
375
+ ### .compileSerializationSchema(schema, httpStatus)
376
+ <a id="compileserializationschema"></a>
377
+
378
+ This function will compile a serialization schema and
379
+ return a function that can be used to serialize data.
380
+ The function returned (a.k.a. _serialization function_) returned is compiled
381
+ by using the provided `SerializerCompiler`. Also this is cached by using
382
+ a `WeakMap` for reducing compilation calls.
383
+
384
+ The optional paramater `httpStatus`, if provided, is forwarded directly
385
+ the `SerializerCompiler`, so it can be used to compile the serialization
386
+ function if a custom `SerializerCompiler` is used.
387
+
388
+ This heavily depends of the `schema#responses` attached to the route, or
389
+ the serialization functions compiled by using `compileSerializationSchema`.
390
+
391
+ ```js
392
+ const serialize = reply
393
+ .compileSerializationSchema({
394
+ type: 'object',
395
+ properties: {
396
+ foo: {
397
+ type: 'string'
398
+ }
399
+ }
400
+ })
401
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
402
+
403
+ // or
404
+
405
+ const serialize = reply
406
+ .compileSerializationSchema({
407
+ type: 'object',
408
+ properties: {
409
+ foo: {
410
+ type: 'string'
411
+ }
412
+ }
413
+ }, 200)
414
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
415
+ ```
416
+
417
+ Note that you should be careful when using this function, as it will cache
418
+ the compiled serialization functions based on the schema provided. If the
419
+ schemas provided is mutated or changed, the serialization functions will not
420
+ detect that the schema has been altered and for instance it will reuse the
421
+ previously compiled serialization function based on the reference of the schema
422
+ previously provided.
423
+
424
+ If there's a need to change the properties of a schema, always opt to create
425
+ a totally new object, otherwise the implementation won't benefit from the cache
426
+ mechanism.
427
+
428
+ :Using the following schema as example:
429
+ ```js
430
+ const schema1 = {
431
+ type: 'object',
432
+ properties: {
433
+ foo: {
434
+ type: 'string'
435
+ }
436
+ }
437
+ }
438
+ ```
439
+
440
+ *Not*
441
+ ```js
442
+ const serialize = reply.compileSerializationSchema(schema1)
443
+
444
+ // Later on...
445
+ schema1.properties.foo.type. = 'integer'
446
+ const newSerialize = reply.compileSerializationSchema(schema1)
447
+
448
+ console.log(newSerialize === serialize) // true
449
+ ```
450
+
451
+ *Instead*
452
+ ```js
453
+ const serialize = reply.compileSerializationSchema(schema1)
454
+
455
+ // Later on...
456
+ const newSchema = Object.assign({}, schema1)
457
+ newSchema.properties.foo.type = 'integer'
458
+
459
+ const newSerialize = reply.compileSerializationSchema(newSchema)
460
+
461
+ console.log(newSerialize === serialize) // false
462
+ ```
463
+
464
+ ### .serializeInput(data, [schema | httpStatus], [httpStatus])
465
+ <a id="serializeinput"></a>
466
+
467
+ This function will serialize the input data based on the provided schema,
468
+ or http status code. If both provided, the `httpStatus` will take presedence.
469
+
470
+ If there is not a serialization function for a given `schema`, a new serialization
471
+ function will be compiled forwarding the `httpStatus` if provided.
472
+
473
+ ```js
474
+ reply
475
+ .serializeInput({ foo: 'bar'}, {
476
+ type: 'object',
477
+ properties: {
478
+ foo: {
479
+ type: 'string'
480
+ }
481
+ }
482
+ }) // '{"foo":"bar"}'
483
+
484
+ // or
485
+
486
+ reply
487
+ .serializeInput({ foo: 'bar'}, {
488
+ type: 'object',
489
+ properties: {
490
+ foo: {
491
+ type: 'string'
492
+ }
493
+ }
494
+ }, 200) // '{"foo":"bar"}'
495
+
496
+ // or
497
+
498
+ reply
499
+ .serializeInput({ foo: 'bar'}, 200) // '{"foo":"bar"}'
500
+ ```
501
+
502
+ See [.compileSerializationSchema(schema, [httpStatus])](#compileserializationschema)
503
+ for more information on how to compile serialization schemas.
504
+
329
505
  ### .serializer(func)
330
506
  <a id="serializer"></a>
331
507
 
@@ -35,6 +35,19 @@ Request is a core Fastify object containing the following fields:
35
35
  - `connection` - Deprecated, use `socket` instead. The underlying connection of
36
36
  the incoming request.
37
37
  - `socket` - the underlying connection of the incoming request
38
+ - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) -
39
+ Returns a validation function for the specified schema or http part,
40
+ if any of either are set or cached.
41
+ - [.compileValidationSchema(schema, [httpPart])](#compilevalidationschema) -
42
+ Compiles the specified schema and returns a validation function
43
+ using the default (or customized) `ValidationCompiler`.
44
+ The optional `httpPart` is forwarded to the `ValidationCompiler`
45
+ if provided, defaults to `null`.
46
+ - [.validateInput(data, schema | httpPart, [httpPart])](#validate) -
47
+ Validates the specified input by using the specified
48
+ schema and returns the serialized payload. If the optional
49
+ `httpPart` is provided, the function will use the serializer
50
+ function given for that HTTP Status Code. Defaults to `null`.
38
51
  - `context` - A Fastify internal object. You should not use it directly or
39
52
  modify it. It is useful to access one special key:
40
53
  - `context.config` - The route [`config`](./Routes.md#routes-config) object.
@@ -77,3 +90,161 @@ fastify.post('/:params', options, function (request, reply) {
77
90
  request.log.info('some info')
78
91
  })
79
92
  ```
93
+ ### .getValidationFunction(schema | httpPart)
94
+ <a id="getvalidationfunction"></a>
95
+
96
+ By calling this function using a provided `schema` or `httpPart`,
97
+ it will return a `validation` function that can be used to
98
+ validate diverse inputs. It returns `undefined` if no
99
+ serialization function was found using either of the provided inputs.
100
+
101
+ ```js
102
+ const validate = request
103
+ .getValidationFunction({
104
+ type: 'object',
105
+ properties: {
106
+ foo: {
107
+ type: 'string'
108
+ }
109
+ }
110
+ })
111
+ validate({ foo: 'bar' }) // true
112
+
113
+ // or
114
+
115
+ const validate = request
116
+ .getValidationFunction('body')
117
+ validate({ foo: 0.5 }) // false
118
+ ```
119
+
120
+ See [.compilaValidationSchema(schema, [httpStatus])](#compilevalidationschema)
121
+ for more information on how to compile validation function.
122
+
123
+ ### .compileValidationSchema(schema, [httpPart])
124
+ <a id="compilevalidationschema"></a>
125
+
126
+ This function will compile a validation schema and
127
+ return a function that can be used to validate data.
128
+ The function returned (a.k.a. _validation function_) is compiled
129
+ by using the provided [`SchemaControler#ValidationCompiler`](./Server.md#schema-controller).
130
+ A `WeakMap` is used to cached this, reducing compilation calls.
131
+
132
+ The optional parameter `httpPart`, if provided, is forwarded directly
133
+ the `ValidationCompiler`, so it can be used to compile the validation
134
+ function if a custom `ValidationCompiler` is provided for the route.
135
+
136
+
137
+ ```js
138
+ const validate = request
139
+ .compileValidationSchema({
140
+ type: 'object',
141
+ properties: {
142
+ foo: {
143
+ type: 'string'
144
+ }
145
+ }
146
+ })
147
+ console.log(validate({ foo: 'bar' })) // true
148
+
149
+ // or
150
+
151
+ const validate = request
152
+ .compileValidationSchema({
153
+ type: 'object',
154
+ properties: {
155
+ foo: {
156
+ type: 'string'
157
+ }
158
+ }
159
+ }, 200)
160
+ console.log(validate({ hello: 'world' })) // false
161
+ ```
162
+
163
+ Note that you should be careful when using this function, as it will cache
164
+ the compiled validation functions based on the schema provided. If the
165
+ schemas provided are mutated or changed, the validation functions will not
166
+ detect that the schema has been altered and for instance it will reuse the
167
+ previously compiled validation function, as the cache is based on
168
+ the reference of the schema (Object) previously provided.
169
+
170
+ If there is a need to change the properties of a schema, always opt to create
171
+ a totally new schema (object), otherwise the implementation will not benefit from
172
+ the cache mechanism.
173
+
174
+ Using the following schema as an example:
175
+ ```js
176
+ const schema1 = {
177
+ type: 'object',
178
+ properties: {
179
+ foo: {
180
+ type: 'string'
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ *Not*
187
+ ```js
188
+ const validate = request.compileValidationSchema(schema1)
189
+
190
+ // Later on...
191
+ schema1.properties.foo.type. = 'integer'
192
+ const newValidate = request.compileValidationSchema(schema1)
193
+
194
+ console.log(newValidate === validate) // true
195
+ ```
196
+
197
+ *Instead*
198
+ ```js
199
+ const validate = request.compileValidationSchema(schema1)
200
+
201
+ // Later on...
202
+ const newSchema = Object.assign({}, schema1)
203
+ newSchema.properties.foo.type = 'integer'
204
+
205
+ const newValidate = request.compileValidationSchema(newSchema)
206
+
207
+ console.log(newValidate === validate) // false
208
+ ```
209
+
210
+ ### .validateInput(data, [schema | httpStatus], [httpStatus])
211
+ <a id="validate"></a>
212
+
213
+ This function will validate the input based on the provided schema,
214
+ or HTTP part passed. If both are provided, the `httpPart` parameter
215
+ will take precedence.
216
+
217
+ If there is not a validation function for a given `schema`, a new validation
218
+ function will be compiled, forwarding the `httpPart` if provided.
219
+
220
+ ```js
221
+ request
222
+ .validateInput({ foo: 'bar'}, {
223
+ type: 'object',
224
+ properties: {
225
+ foo: {
226
+ type: 'string'
227
+ }
228
+ }
229
+ }) // true
230
+
231
+ // or
232
+
233
+ request
234
+ .validateInput({ foo: 'bar'}, {
235
+ type: 'object',
236
+ properties: {
237
+ foo: {
238
+ type: 'string'
239
+ }
240
+ }
241
+ }, 'body') // true
242
+
243
+ // or
244
+
245
+ request
246
+ .validateInput({ hello: 'world'}, 'query') // false
247
+ ```
248
+
249
+ See [.compileValidationSchema(schema, [httpStatus])](#compileValidationSchema)
250
+ for more information on how to compile validation schemas.
package/fastify.d.ts CHANGED
@@ -194,7 +194,7 @@ export type { Chain as LightMyRequestChain, InjectOptions, Response as LightMyRe
194
194
  export { FastifyRequest, RequestGenericInterface } from './types/request'
195
195
  export { FastifyReply } from './types/reply'
196
196
  export { FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin } from './types/plugin'
197
- export { FastifyInstance, PrintRoutesOptions } from './types/instance'
197
+ export { FastifyListenOptions, FastifyInstance, PrintRoutesOptions } from './types/instance'
198
198
  export { FastifyLoggerOptions, FastifyBaseLogger, FastifyLoggerInstance, FastifyLogFn, LogLevel } from './types/logger'
199
199
  export { FastifyContext, FastifyContextConfig } from './types/context'
200
200
  export { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.2.1'
3
+ const VERSION = '4.3.0'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -621,7 +621,7 @@ function fastify (options) {
621
621
  // https://github.com/nodejs/node/blob/6ca23d7846cb47e84fd344543e394e50938540be/lib/_http_server.js#L666
622
622
 
623
623
  // If the socket is not writable, there is no reason to try to send data.
624
- if (socket.writable && socket.bytesWritten === 0) {
624
+ if (socket.writable) {
625
625
  socket.write(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)
626
626
  }
627
627
  socket.destroy(err)
@@ -92,10 +92,18 @@ ContentTypeParser.prototype.existingParser = function (contentType) {
92
92
  }
93
93
 
94
94
  ContentTypeParser.prototype.getParser = function (contentType) {
95
+ if (contentType in this.customParsers) {
96
+ return this.customParsers[contentType]
97
+ }
98
+
99
+ if (this.cache.has(contentType)) {
100
+ return this.cache.get(contentType)
101
+ }
102
+
95
103
  // eslint-disable-next-line no-var
96
104
  for (var i = 0; i !== this.parserList.length; ++i) {
97
105
  const parserName = this.parserList[i]
98
- if (contentType.indexOf(parserName) > -1) {
106
+ if (contentType.indexOf(parserName) !== -1) {
99
107
  const parser = this.customParsers[parserName]
100
108
  this.cache.set(contentType, parser)
101
109
  return parser
@@ -137,7 +145,7 @@ ContentTypeParser.prototype.remove = function (contentType) {
137
145
  }
138
146
 
139
147
  ContentTypeParser.prototype.run = function (contentType, handler, request, reply) {
140
- const parser = this.cache.get(contentType) || this.getParser(contentType)
148
+ const parser = this.getParser(contentType)
141
149
  const resource = new AsyncResource('content-type-parser:run', request)
142
150
 
143
151
  if (parser === undefined) {
package/lib/context.js CHANGED
@@ -10,7 +10,9 @@ const {
10
10
  kBodyLimit,
11
11
  kLogLevel,
12
12
  kContentTypeParser,
13
- kRouteByFastify
13
+ kRouteByFastify,
14
+ kRequestValidateWeakMap,
15
+ kReplySerializeWeakMap
14
16
  } = require('./symbols.js')
15
17
 
16
18
  // Objects that holds the context of every request
@@ -24,6 +26,8 @@ function Context ({
24
26
  logLevel,
25
27
  logSerializers,
26
28
  attachValidation,
29
+ validatorCompiler,
30
+ serializerCompiler,
27
31
  replySerializer,
28
32
  schemaErrorFormatter,
29
33
  server,
@@ -54,6 +58,11 @@ function Context ({
54
58
  this.schemaErrorFormatter = schemaErrorFormatter || server[kSchemaErrorFormatter] || defaultSchemaErrorFormatter
55
59
  this[kRouteByFastify] = isFastify
56
60
 
61
+ this[kRequestValidateWeakMap] = null
62
+ this[kReplySerializeWeakMap] = null
63
+ this.validatorCompiler = validatorCompiler || null
64
+ this.serializerCompiler = serializerCompiler || null
65
+
57
66
  this.server = server
58
67
  }
59
68
 
package/lib/errors.js CHANGED
@@ -160,6 +160,14 @@ const codes = {
160
160
  'FST_ERR_BAD_TRAILER_VALUE',
161
161
  "Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function."
162
162
  ),
163
+ FST_ERR_MISSING_SERIALIZATION_FN: createError(
164
+ 'FST_ERR_MISSING_SERIALIZATION_FN',
165
+ 'Missing serialization function. Key "%s"'
166
+ ),
167
+ FST_ERR_REQ_INVALID_VALIDATION_INVOCATION: createError(
168
+ 'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION',
169
+ 'Invalid validation invocation. Missing validation function for HTTP part "%s" nor schema provided.'
170
+ ),
163
171
 
164
172
  /**
165
173
  * schemas
package/lib/reply.js CHANGED
@@ -16,7 +16,11 @@ const {
16
16
  kReplyHasStatusCode,
17
17
  kReplyIsRunningOnErrorHook,
18
18
  kReplyNextErrorHandler,
19
- kDisableRequestLogging
19
+ kDisableRequestLogging,
20
+ kSchemaResponse,
21
+ kReplySerializeWeakMap,
22
+ kSchemaController,
23
+ kOptions
20
24
  } = require('./symbols.js')
21
25
  const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks')
22
26
 
@@ -38,7 +42,8 @@ const {
38
42
  FST_ERR_SEND_INSIDE_ONERR,
39
43
  FST_ERR_BAD_STATUS_CODE,
40
44
  FST_ERR_BAD_TRAILER_NAME,
41
- FST_ERR_BAD_TRAILER_VALUE
45
+ FST_ERR_BAD_TRAILER_VALUE,
46
+ FST_ERR_MISSING_SERIALIZATION_FN
42
47
  } = require('./errors')
43
48
  const warning = require('./warnings')
44
49
 
@@ -299,6 +304,79 @@ Reply.prototype.code = function (code) {
299
304
 
300
305
  Reply.prototype.status = Reply.prototype.code
301
306
 
307
+ Reply.prototype.getSerializationFunction = function (schemaOrStatus) {
308
+ let serialize
309
+
310
+ if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
311
+ serialize = this.context[kSchemaResponse]?.[schemaOrStatus]
312
+ } else if (typeof schemaOrStatus === 'object') {
313
+ serialize = this.context[kReplySerializeWeakMap]?.get(schemaOrStatus)
314
+ }
315
+
316
+ return serialize
317
+ }
318
+
319
+ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null) {
320
+ const { request } = this
321
+ const { method, url } = request
322
+
323
+ // Check if serialize function already compiled
324
+ if (this.context[kReplySerializeWeakMap]?.has(schema)) {
325
+ return this.context[kReplySerializeWeakMap].get(schema)
326
+ }
327
+
328
+ const serializerCompiler = this.context.serializerCompiler ||
329
+ this.server[kSchemaController].serializerCompiler ||
330
+ (
331
+ // We compile the schemas if no custom serializerCompiler is provided
332
+ // nor set
333
+ this.server[kSchemaController].setupSerializer(this.server[kOptions]) ||
334
+ this.server[kSchemaController].serializerCompiler
335
+ )
336
+
337
+ const serializeFn = serializerCompiler({
338
+ schema,
339
+ method,
340
+ url,
341
+ httpStatus
342
+ })
343
+
344
+ // We create a WeakMap to compile the schema only once
345
+ // Its done leazily to avoid add overhead by creating the WeakMap
346
+ // if it is not used
347
+ // TODO: Explore a central cache for all the schemas shared across
348
+ // encapsulated contexts
349
+ if (this.context[kReplySerializeWeakMap] == null) {
350
+ this.context[kReplySerializeWeakMap] = new WeakMap()
351
+ }
352
+
353
+ this.context[kReplySerializeWeakMap].set(schema, serializeFn)
354
+
355
+ return serializeFn
356
+ }
357
+
358
+ Reply.prototype.serializeInput = function (input, schema, httpStatus) {
359
+ let serialize
360
+ httpStatus = typeof schema === 'string' || typeof schema === 'number'
361
+ ? schema
362
+ : httpStatus
363
+
364
+ if (httpStatus != null) {
365
+ serialize = this.context[kSchemaResponse]?.[httpStatus]
366
+
367
+ if (serialize == null) throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
368
+ } else {
369
+ // Check if serialize function already compiled
370
+ if (this.context[kReplySerializeWeakMap]?.has(schema)) {
371
+ serialize = this.context[kReplySerializeWeakMap].get(schema)
372
+ } else {
373
+ serialize = this.compileSerializationSchema(schema, httpStatus)
374
+ }
375
+ }
376
+
377
+ return serialize(input)
378
+ }
379
+
302
380
  Reply.prototype.serialize = function (payload) {
303
381
  if (this[kReplySerializer] !== null) {
304
382
  return this[kReplySerializer](payload)