fast-json-stringify 5.5.0 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -71,7 +71,7 @@ jobs:
71
71
  pull-requests: write
72
72
  steps:
73
73
  - name: Comment PR
74
- uses: thollander/actions-comment-pull-request@v1
74
+ uses: thollander/actions-comment-pull-request@v2
75
75
  with:
76
76
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
77
77
  message: |
package/.taprc CHANGED
@@ -1,9 +1,14 @@
1
1
  ts: false
2
2
  jsx: false
3
+
3
4
  branches: 65
4
5
  functions: 100
5
6
  lines: 89
6
7
  statements: 89
8
+
7
9
  nyc-arg:
8
10
  - "--exclude=.cache/*"
9
11
  - "--exclude=lib/schema-validator.js"
12
+
13
+ files:
14
+ - test/**/*.test.js
package/README.md CHANGED
@@ -548,7 +548,7 @@ By default the library will handle automatically [BigInt](https://developer.mozi
548
548
  <a name="integer"></a>
549
549
  #### Integers
550
550
  The `type: integer` property will be truncated if a floating point is provided.
551
- You can customize this behaviour with the `rounding` option that will accept [`round`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round), [`ceil`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil) or [`floor`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor):
551
+ You can customize this behaviour with the `rounding` option that will accept [`round`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round), [`ceil`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil), [`floor`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor) or [`trunc`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc). Default is `trunc`:
552
552
 
553
553
  ```js
554
554
  const stringify = fastJson(schema, { rounding: 'ceil' })
@@ -214,6 +214,27 @@ const benchmarks = [
214
214
  b5: true
215
215
  }
216
216
  },
217
+ {
218
+ name: 'simple object',
219
+ schema: {
220
+ title: 'Example Schema',
221
+ type: 'object',
222
+ properties: {
223
+ firstName: {
224
+ type: 'string'
225
+ },
226
+ lastName: {
227
+ type: ['string', 'null']
228
+ },
229
+ age: {
230
+ description: 'Age in years',
231
+ type: 'integer',
232
+ minimum: 0
233
+ }
234
+ }
235
+ },
236
+ input: { firstName: 'Max', lastName: 'Power', age: 22 }
237
+ },
217
238
  {
218
239
  name: 'object with const string property',
219
240
  schema: {
package/index.js CHANGED
@@ -14,18 +14,20 @@ const Location = require('./lib/location')
14
14
 
15
15
  let largeArraySize = 2e4
16
16
  let largeArrayMechanism = 'default'
17
+
18
+ const validRoundingMethods = [
19
+ 'floor',
20
+ 'ceil',
21
+ 'round',
22
+ 'trunc'
23
+ ]
24
+
17
25
  const validLargeArrayMechanisms = [
18
26
  'default',
19
27
  'json-stringify'
20
28
  ]
21
29
 
22
- const addComma = `
23
- if (addComma) {
24
- json += ','
25
- } else {
26
- addComma = true
27
- }
28
- `
30
+ const addComma = '!addComma && (addComma = true) || (json += \',\')'
29
31
 
30
32
  function isValidSchema (schema, name) {
31
33
  if (!validate(schema)) {
@@ -41,7 +43,7 @@ function isValidSchema (schema, name) {
41
43
  }
42
44
  }
43
45
 
44
- function resolveRef (location, ref) {
46
+ function resolveRef (context, location, ref) {
45
47
  let hashIndex = ref.indexOf('#')
46
48
  if (hashIndex === -1) {
47
49
  hashIndex = ref.length
@@ -50,7 +52,7 @@ function resolveRef (location, ref) {
50
52
  const schemaId = ref.slice(0, hashIndex) || location.getOriginSchemaId()
51
53
  const jsonPointer = ref.slice(hashIndex) || '#'
52
54
 
53
- const schema = refResolver.getSchema(schemaId, jsonPointer)
55
+ const schema = context.refResolver.getSchema(schemaId, jsonPointer)
54
56
 
55
57
  if (schema === undefined) {
56
58
  throw new Error(`Cannot find reference "${ref}"`)
@@ -58,42 +60,38 @@ function resolveRef (location, ref) {
58
60
 
59
61
  const newLocation = new Location(schema, schemaId, jsonPointer)
60
62
  if (schema.$ref !== undefined) {
61
- return resolveRef(newLocation, schema.$ref)
63
+ return resolveRef(context, newLocation, schema.$ref)
62
64
  }
63
65
 
64
66
  return newLocation
65
67
  }
66
68
 
67
- const contextFunctionsNamesBySchema = new Map()
68
-
69
- let rootSchemaId = null
70
- let refResolver = null
71
- let contextFunctions = null
72
- let validatorSchemasIds = null
73
-
74
69
  function build (schema, options) {
75
- contextFunctionsNamesBySchema.clear()
70
+ isValidSchema(schema)
76
71
 
77
- contextFunctions = []
78
- validatorSchemasIds = new Set()
79
72
  options = options || {}
80
73
 
81
- refResolver = new RefResolver()
82
-
83
- rootSchemaId = schema.$id || randomUUID()
74
+ const context = {
75
+ functions: [],
76
+ functionsCounter: 0,
77
+ functionsNamesBySchema: new Map(),
78
+ options,
79
+ refResolver: new RefResolver(),
80
+ rootSchemaId: schema.$id || randomUUID(),
81
+ validatorSchemasIds: new Set()
82
+ }
84
83
 
85
- isValidSchema(schema)
86
- refResolver.addSchema(schema, rootSchemaId)
84
+ context.refResolver.addSchema(schema, context.rootSchemaId)
87
85
 
88
86
  if (options.schema) {
89
87
  for (const key of Object.keys(options.schema)) {
90
88
  isValidSchema(options.schema[key], key)
91
- refResolver.addSchema(options.schema[key], key)
89
+ context.refResolver.addSchema(options.schema[key], key)
92
90
  }
93
91
  }
94
92
 
95
93
  if (options.rounding) {
96
- if (!['floor', 'ceil', 'round'].includes(options.rounding)) {
94
+ if (!validRoundingMethods.includes(options.rounding)) {
97
95
  throw new Error(`Unsupported integer rounding method ${options.rounding}`)
98
96
  }
99
97
  }
@@ -107,41 +105,58 @@ function build (schema, options) {
107
105
  }
108
106
 
109
107
  if (options.largeArraySize) {
110
- if (!Number.isNaN(Number.parseInt(options.largeArraySize, 10))) {
108
+ if (typeof options.largeArraySize === 'string' && Number.isFinite(Number.parseInt(options.largeArraySize, 10))) {
109
+ largeArraySize = Number.parseInt(options.largeArraySize, 10)
110
+ } else if (typeof options.largeArraySize === 'number' && Number.isInteger(options.largeArraySize)) {
111
111
  largeArraySize = options.largeArraySize
112
+ } else if (typeof options.largeArraySize === 'bigint') {
113
+ largeArraySize = Number(options.largeArraySize)
112
114
  } else {
113
- throw new Error(`Unsupported large array size. Expected integer-like, got ${options.largeArraySize}`)
115
+ throw new Error(`Unsupported large array size. Expected integer-like, got ${typeof options.largeArraySize} with value ${options.largeArraySize}`)
114
116
  }
115
117
  }
116
118
 
117
- const location = new Location(schema, rootSchemaId)
118
- const code = buildValue(location, 'input')
119
+ const location = new Location(schema, context.rootSchemaId)
120
+ const code = buildValue(context, location, 'input')
121
+
122
+ let contextFunctionCode
119
123
 
120
- const contextFunctionCode = `
124
+ // If we have only the invocation of the 'anonymous0' function, we would
125
+ // basically just wrap the 'anonymous0' function in the 'main' function and
126
+ // and the overhead of the intermediate variabe 'json'. We can avoid the
127
+ // wrapping and the unnecessary memory allocation by aliasing 'anonymous0' to
128
+ // 'main'
129
+ if (code === 'json += anonymous0(input)') {
130
+ contextFunctionCode = `
131
+ ${context.functions.join('\n')}
132
+ const main = anonymous0
133
+ return main
134
+ `
135
+ } else {
136
+ contextFunctionCode = `
121
137
  function main (input) {
122
138
  let json = ''
123
139
  ${code}
124
140
  return json
125
141
  }
126
- ${contextFunctions.join('\n')}
142
+ ${context.functions.join('\n')}
127
143
  return main
128
- `
144
+ `
145
+ }
129
146
 
130
147
  const serializer = new Serializer(options)
131
148
  const validator = new Validator(options.ajv)
132
149
 
133
- for (const schemaId of validatorSchemasIds) {
134
- const schema = refResolver.getSchema(schemaId)
150
+ for (const schemaId of context.validatorSchemasIds) {
151
+ const schema = context.refResolver.getSchema(schemaId)
135
152
  validator.addSchema(schema, schemaId)
136
153
 
137
- const dependencies = refResolver.getSchemaDependencies(schemaId)
154
+ const dependencies = context.refResolver.getSchemaDependencies(schemaId)
138
155
  for (const [schemaId, schema] of Object.entries(dependencies)) {
139
156
  validator.addSchema(schema, schemaId)
140
157
  }
141
158
  }
142
159
 
143
- const dependenciesName = ['validator', 'serializer', contextFunctionCode]
144
-
145
160
  if (options.debugMode) {
146
161
  options.mode = 'debug'
147
162
  }
@@ -150,14 +165,14 @@ function build (schema, options) {
150
165
  return {
151
166
  validator,
152
167
  serializer,
153
- code: dependenciesName.join('\n'),
168
+ code: `validator\nserializer\n${contextFunctionCode}`,
154
169
  ajv: validator.ajv
155
170
  }
156
171
  }
157
172
 
158
173
  if (options.mode === 'standalone') {
159
174
  // lazy load
160
- const isValidatorUsed = validatorSchemasIds.size > 0
175
+ const isValidatorUsed = context.validatorSchemasIds.size > 0
161
176
  const buildStandaloneCode = require('./lib/standalone')
162
177
  return buildStandaloneCode(options, validator, isValidatorUsed, contextFunctionCode)
163
178
  }
@@ -166,22 +181,16 @@ function build (schema, options) {
166
181
  const contextFunc = new Function('validator', 'serializer', contextFunctionCode)
167
182
  const stringifyFunc = contextFunc(validator, serializer)
168
183
 
169
- refResolver = null
170
- rootSchemaId = null
171
- contextFunctions = null
172
- validatorSchemasIds = null
173
- contextFunctionsNamesBySchema.clear()
174
-
175
184
  return stringifyFunc
176
185
  }
177
186
 
178
187
  const objectKeywords = [
179
- 'maxProperties',
180
- 'minProperties',
181
- 'required',
182
188
  'properties',
183
- 'patternProperties',
189
+ 'required',
184
190
  'additionalProperties',
191
+ 'patternProperties',
192
+ 'maxProperties',
193
+ 'minProperties',
185
194
  'dependencies'
186
195
  ]
187
196
 
@@ -232,7 +241,7 @@ function inferTypeByKeyword (schema) {
232
241
  return schema.type
233
242
  }
234
243
 
235
- function buildExtraObjectPropertiesSerializer (location) {
244
+ function buildExtraObjectPropertiesSerializer (context, location) {
236
245
  const schema = location.schema
237
246
  const propertiesKeys = Object.keys(schema.properties || {})
238
247
 
@@ -254,18 +263,11 @@ function buildExtraObjectPropertiesSerializer (location) {
254
263
  for (const propertyKey in patternPropertiesSchema) {
255
264
  const propertyLocation = patternPropertiesLocation.getPropertyLocation(propertyKey)
256
265
 
257
- try {
258
- RegExp(propertyKey)
259
- } catch (err) {
260
- const jsonPointer = propertyLocation.getSchemaRef()
261
- throw new Error(`${err.message}. Invalid pattern property regexp key ${propertyKey} at ${jsonPointer}`)
262
- }
263
-
264
266
  code += `
265
267
  if (/${propertyKey.replace(/\\*\//g, '\\/')}/.test(key)) {
266
268
  ${addComma}
267
269
  json += serializer.asString(key) + ':'
268
- ${buildValue(propertyLocation, 'value')}
270
+ ${buildValue(context, propertyLocation, 'value')}
269
271
  continue
270
272
  }
271
273
  `
@@ -286,7 +288,7 @@ function buildExtraObjectPropertiesSerializer (location) {
286
288
  code += `
287
289
  ${addComma}
288
290
  json += serializer.asString(key) + ':'
289
- ${buildValue(propertyLocation, 'value')}
291
+ ${buildValue(context, propertyLocation, 'value')}
290
292
  `
291
293
  }
292
294
  }
@@ -297,7 +299,7 @@ function buildExtraObjectPropertiesSerializer (location) {
297
299
  return code
298
300
  }
299
301
 
300
- function buildInnerObject (location) {
302
+ function buildInnerObject (context, location) {
301
303
  const schema = location.schema
302
304
  const required = schema.required || []
303
305
 
@@ -307,11 +309,10 @@ function buildInnerObject (location) {
307
309
  Object.keys(schema.properties || {}).forEach((key) => {
308
310
  let propertyLocation = propertiesLocation.getPropertyLocation(key)
309
311
  if (propertyLocation.schema.$ref) {
310
- propertyLocation = resolveRef(location, propertyLocation.schema.$ref)
312
+ propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
311
313
  }
312
314
 
313
315
  const sanitized = JSON.stringify(key)
314
- const asString = JSON.stringify(sanitized)
315
316
 
316
317
  // Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
317
318
  // see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
@@ -319,17 +320,17 @@ function buildInnerObject (location) {
319
320
  code += `
320
321
  if (obj[${sanitized}] !== undefined) {
321
322
  ${addComma}
322
- json += ${asString} + ':'
323
+ json += ${JSON.stringify(sanitized + ':')}
323
324
  `
324
325
 
325
- code += buildValue(propertyLocation, `obj[${JSON.stringify(key)}]`)
326
+ code += buildValue(context, propertyLocation, `obj[${sanitized}]`)
326
327
 
327
328
  const defaultValue = propertyLocation.schema.default
328
329
  if (defaultValue !== undefined) {
329
330
  code += `
330
331
  } else {
331
332
  ${addComma}
332
- json += ${asString} + ':' + ${JSON.stringify(JSON.stringify(defaultValue))}
333
+ json += ${JSON.stringify(sanitized + ':' + JSON.stringify(defaultValue))}
333
334
  `
334
335
  } else if (required.includes(key)) {
335
336
  code += `
@@ -349,13 +350,13 @@ function buildInnerObject (location) {
349
350
  }
350
351
 
351
352
  if (schema.patternProperties || schema.additionalProperties) {
352
- code += buildExtraObjectPropertiesSerializer(location)
353
+ code += buildExtraObjectPropertiesSerializer(context, location)
353
354
  }
354
355
 
355
356
  return code
356
357
  }
357
358
 
358
- function mergeAllOfSchema (location, schema, mergedSchema) {
359
+ function mergeAllOfSchema (context, location, schema, mergedSchema) {
359
360
  const allOfLocation = location.getPropertyLocation('allOf')
360
361
 
361
362
  for (let i = 0; i < schema.allOf.length; i++) {
@@ -363,7 +364,7 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
363
364
 
364
365
  if (allOfSchema.$ref) {
365
366
  const allOfSchemaLocation = allOfLocation.getPropertyLocation(i)
366
- allOfSchema = resolveRef(allOfSchemaLocation, allOfSchema.$ref).schema
367
+ allOfSchema = resolveRef(context, allOfSchemaLocation, allOfSchema.$ref).schema
367
368
  }
368
369
 
369
370
  let allOfSchemaType = allOfSchema.type
@@ -444,18 +445,18 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
444
445
  }
445
446
 
446
447
  if (allOfSchema.allOf !== undefined) {
447
- mergeAllOfSchema(location, allOfSchema, mergedSchema)
448
+ mergeAllOfSchema(context, location, allOfSchema, mergedSchema)
448
449
  }
449
450
  }
450
451
  delete mergedSchema.allOf
451
452
 
452
453
  mergedSchema.$id = `merged_${randomUUID()}`
453
- refResolver.addSchema(mergedSchema)
454
+ context.refResolver.addSchema(mergedSchema)
454
455
  location.addMergedSchema(mergedSchema, mergedSchema.$id)
455
456
  }
456
457
 
457
- function addIfThenElse (location, input) {
458
- validatorSchemasIds.add(location.getSchemaId())
458
+ function addIfThenElse (context, location, input) {
459
+ context.validatorSchemasIds.add(location.getSchemaId())
459
460
 
460
461
  const schema = merge({}, location.schema)
461
462
  const thenSchema = schema.then
@@ -476,9 +477,9 @@ function addIfThenElse (location, input) {
476
477
 
477
478
  return `
478
479
  if (validator.validate("${ifSchemaRef}", ${input})) {
479
- ${buildValue(thenLocation, input)}
480
+ ${buildValue(context, thenLocation, input)}
480
481
  } else {
481
- ${buildValue(elseLocation, input)}
482
+ ${buildValue(context, elseLocation, input)}
482
483
  }
483
484
  `
484
485
  }
@@ -490,19 +491,19 @@ function toJSON (variableName) {
490
491
  `
491
492
  }
492
493
 
493
- function buildObject (location) {
494
+ function buildObject (context, location) {
494
495
  const schema = location.schema
495
496
 
496
- if (contextFunctionsNamesBySchema.has(schema)) {
497
- return contextFunctionsNamesBySchema.get(schema)
497
+ if (context.functionsNamesBySchema.has(schema)) {
498
+ return context.functionsNamesBySchema.get(schema)
498
499
  }
499
500
 
500
- const functionName = generateFuncName()
501
- contextFunctionsNamesBySchema.set(schema, functionName)
501
+ const functionName = generateFuncName(context)
502
+ context.functionsNamesBySchema.set(schema, functionName)
502
503
 
503
504
  let schemaRef = location.getSchemaRef()
504
- if (schemaRef.startsWith(rootSchemaId)) {
505
- schemaRef = schemaRef.replace(rootSchemaId, '')
505
+ if (schemaRef.startsWith(context.rootSchemaId)) {
506
+ schemaRef = schemaRef.replace(context.rootSchemaId, '')
506
507
  }
507
508
 
508
509
  let functionCode = `
@@ -511,44 +512,43 @@ function buildObject (location) {
511
512
  `
512
513
 
513
514
  functionCode += `
514
- var obj = ${toJSON('input')}
515
- var json = '{'
516
- var addComma = false
515
+ const obj = ${toJSON('input')}
516
+ let json = '{'
517
+ let addComma = false
517
518
  `
518
519
 
519
- functionCode += buildInnerObject(location)
520
+ functionCode += buildInnerObject(context, location)
520
521
  functionCode += `
521
- json += '}'
522
- return json
522
+ return json + '}'
523
523
  }
524
524
  `
525
525
 
526
- contextFunctions.push(functionCode)
526
+ context.functions.push(functionCode)
527
527
  return functionName
528
528
  }
529
529
 
530
- function buildArray (location) {
530
+ function buildArray (context, location) {
531
531
  const schema = location.schema
532
532
 
533
533
  let itemsLocation = location.getPropertyLocation('items')
534
534
  itemsLocation.schema = itemsLocation.schema || {}
535
535
 
536
536
  if (itemsLocation.schema.$ref) {
537
- itemsLocation = resolveRef(itemsLocation, itemsLocation.schema.$ref)
537
+ itemsLocation = resolveRef(context, itemsLocation, itemsLocation.schema.$ref)
538
538
  }
539
539
 
540
540
  const itemsSchema = itemsLocation.schema
541
541
 
542
- if (contextFunctionsNamesBySchema.has(schema)) {
543
- return contextFunctionsNamesBySchema.get(schema)
542
+ if (context.functionsNamesBySchema.has(schema)) {
543
+ return context.functionsNamesBySchema.get(schema)
544
544
  }
545
545
 
546
- const functionName = generateFuncName()
547
- contextFunctionsNamesBySchema.set(schema, functionName)
546
+ const functionName = generateFuncName(context)
547
+ context.functionsNamesBySchema.set(schema, functionName)
548
548
 
549
549
  let schemaRef = location.getSchemaRef()
550
- if (schemaRef.startsWith(rootSchemaId)) {
551
- schemaRef = schemaRef.replace(rootSchemaId, '')
550
+ if (schemaRef.startsWith(context.rootSchemaId)) {
551
+ schemaRef = schemaRef.replace(context.rootSchemaId, '')
552
552
  }
553
553
 
554
554
  let functionCode = `
@@ -558,12 +558,12 @@ function buildArray (location) {
558
558
 
559
559
  functionCode += `
560
560
  if (!Array.isArray(obj)) {
561
- throw new TypeError(\`The value '$\{obj}' does not match schema definition.\`)
561
+ throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
562
562
  }
563
563
  const arrayLength = obj.length
564
564
  `
565
565
 
566
- if (!schema.additionalItems) {
566
+ if (!schema.additionalItems && Array.isArray(itemsSchema)) {
567
567
  functionCode += `
568
568
  if (arrayLength > ${itemsSchema.length}) {
569
569
  throw new Error(\`Item at ${itemsSchema.length} does not match schema definition.\`)
@@ -586,7 +586,7 @@ function buildArray (location) {
586
586
  if (Array.isArray(itemsSchema)) {
587
587
  for (let i = 0; i < itemsSchema.length; i++) {
588
588
  const item = itemsSchema[i]
589
- const tmpRes = buildValue(itemsLocation.getPropertyLocation(i), `obj[${i}]`)
589
+ const tmpRes = buildValue(context, itemsLocation.getPropertyLocation(i), `obj[${i}]`)
590
590
  functionCode += `
591
591
  if (${i} < arrayLength) {
592
592
  if (${buildArrayTypeCondition(item.type, `[${i}]`)}) {
@@ -606,15 +606,14 @@ function buildArray (location) {
606
606
  if (schema.additionalItems) {
607
607
  functionCode += `
608
608
  for (let i = ${itemsSchema.length}; i < arrayLength; i++) {
609
- let json = JSON.stringify(obj[i])
610
- jsonOutput += json
609
+ jsonOutput += JSON.stringify(obj[i])
611
610
  if (i < arrayLength - 1) {
612
611
  jsonOutput += ','
613
612
  }
614
613
  }`
615
614
  }
616
615
  } else {
617
- const code = buildValue(itemsLocation, 'obj[i]')
616
+ const code = buildValue(context, itemsLocation, 'obj[i]')
618
617
  functionCode += `
619
618
  for (let i = 0; i < arrayLength; i++) {
620
619
  let json = ''
@@ -630,7 +629,7 @@ function buildArray (location) {
630
629
  return \`[\${jsonOutput}]\`
631
630
  }`
632
631
 
633
- contextFunctions.push(functionCode)
632
+ context.functions.push(functionCode)
634
633
  return functionName
635
634
  }
636
635
 
@@ -671,12 +670,11 @@ function buildArrayTypeCondition (type, accessor) {
671
670
  return condition
672
671
  }
673
672
 
674
- let genFuncNameCounter = 0
675
- function generateFuncName () {
676
- return 'anonymous' + genFuncNameCounter++
673
+ function generateFuncName (context) {
674
+ return 'anonymous' + context.functionsCounter++
677
675
  }
678
676
 
679
- function buildMultiTypeSerializer (location, input) {
677
+ function buildMultiTypeSerializer (context, location, input) {
680
678
  const schema = location.schema
681
679
  const types = schema.type.sort(t1 => t1 === 'null' ? -1 : 1)
682
680
 
@@ -684,7 +682,7 @@ function buildMultiTypeSerializer (location, input) {
684
682
 
685
683
  types.forEach((type, index) => {
686
684
  location.schema = { ...location.schema, type }
687
- const nestedResult = buildSingleTypeSerializer(location, input)
685
+ const nestedResult = buildSingleTypeSerializer(context, location, input)
688
686
 
689
687
  const statement = index === 0 ? 'if' : 'else if'
690
688
  switch (type) {
@@ -735,14 +733,18 @@ function buildMultiTypeSerializer (location, input) {
735
733
  }
736
734
  }
737
735
  })
736
+ let schemaRef = location.getSchemaRef()
737
+ if (schemaRef.startsWith(context.rootSchemaId)) {
738
+ schemaRef = schemaRef.replace(context.rootSchemaId, '')
739
+ }
738
740
  code += `
739
- else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
741
+ else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
740
742
  `
741
743
 
742
744
  return code
743
745
  }
744
746
 
745
- function buildSingleTypeSerializer (location, input) {
747
+ function buildSingleTypeSerializer (context, location, input) {
746
748
  const schema = location.schema
747
749
 
748
750
  switch (schema.type) {
@@ -766,11 +768,11 @@ function buildSingleTypeSerializer (location, input) {
766
768
  case 'boolean':
767
769
  return `json += serializer.asBoolean(${input})`
768
770
  case 'object': {
769
- const funcName = buildObject(location)
771
+ const funcName = buildObject(context, location)
770
772
  return `json += ${funcName}(${input})`
771
773
  }
772
774
  case 'array': {
773
- const funcName = buildArray(location)
775
+ const funcName = buildArray(context, location)
774
776
  return `json += ${funcName}(${input})`
775
777
  }
776
778
  case undefined:
@@ -807,7 +809,7 @@ function buildConstSerializer (location, input) {
807
809
  return code
808
810
  }
809
811
 
810
- function buildValue (location, input) {
812
+ function buildValue (context, location, input) {
811
813
  let schema = location.schema
812
814
 
813
815
  if (typeof schema === 'boolean') {
@@ -815,7 +817,7 @@ function buildValue (location, input) {
815
817
  }
816
818
 
817
819
  if (schema.$ref) {
818
- location = resolveRef(location, schema.$ref)
820
+ location = resolveRef(context, location, schema.$ref)
819
821
  schema = location.schema
820
822
  }
821
823
 
@@ -827,11 +829,11 @@ function buildValue (location, input) {
827
829
  }
828
830
 
829
831
  if (schema.if && schema.then) {
830
- return addIfThenElse(location, input)
832
+ return addIfThenElse(context, location, input)
831
833
  }
832
834
 
833
835
  if (schema.allOf) {
834
- mergeAllOfSchema(location, schema, clone(schema))
836
+ mergeAllOfSchema(context, location, schema, clone(schema))
835
837
  schema = location.schema
836
838
  }
837
839
 
@@ -840,7 +842,7 @@ function buildValue (location, input) {
840
842
  let code = ''
841
843
 
842
844
  if (type === undefined && (schema.anyOf || schema.oneOf)) {
843
- validatorSchemasIds.add(location.getSchemaId())
845
+ context.validatorSchemasIds.add(location.getSchemaId())
844
846
 
845
847
  const type = schema.anyOf ? 'anyOf' : 'oneOf'
846
848
  const anyOfLocation = location.getPropertyLocation(type)
@@ -848,15 +850,20 @@ function buildValue (location, input) {
848
850
  for (let index = 0; index < location.schema[type].length; index++) {
849
851
  const optionLocation = anyOfLocation.getPropertyLocation(index)
850
852
  const schemaRef = optionLocation.getSchemaRef()
851
- const nestedResult = buildValue(optionLocation, input)
853
+ const nestedResult = buildValue(context, optionLocation, input)
852
854
  code += `
853
855
  ${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
854
856
  ${nestedResult}
855
857
  `
856
858
  }
857
859
 
860
+ let schemaRef = location.getSchemaRef()
861
+ if (schemaRef.startsWith(context.rootSchemaId)) {
862
+ schemaRef = schemaRef.replace(context.rootSchemaId, '')
863
+ }
864
+
858
865
  code += `
859
- else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
866
+ else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
860
867
  `
861
868
  return code
862
869
  }
@@ -873,9 +880,9 @@ function buildValue (location, input) {
873
880
  if (schema.const !== undefined) {
874
881
  code += buildConstSerializer(location, input)
875
882
  } else if (Array.isArray(type)) {
876
- code += buildMultiTypeSerializer(location, input)
883
+ code += buildMultiTypeSerializer(context, location, input)
877
884
  } else {
878
- code += buildSingleTypeSerializer(location, input)
885
+ code += buildSingleTypeSerializer(context, location, input)
879
886
  }
880
887
 
881
888
  if (nullable) {