fast-json-stringify 4.1.0 → 5.0.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.
package/index.js CHANGED
@@ -12,7 +12,6 @@ const Serializer = require('./serializer')
12
12
  const buildAjv = require('./ajv')
13
13
 
14
14
  let largeArraySize = 2e4
15
- let stringSimilarity = null
16
15
  let largeArrayMechanism = 'default'
17
16
  const validLargeArrayMechanisms = [
18
17
  'default',
@@ -41,36 +40,83 @@ function isValidSchema (schema, name) {
41
40
  }
42
41
  }
43
42
 
44
- function mergeLocation (source, dest) {
43
+ function mergeLocation (location, key) {
45
44
  return {
46
- schema: dest.schema || source.schema,
47
- root: dest.root || source.root,
48
- externalSchema: dest.externalSchema || source.externalSchema
45
+ schema: location.schema[key],
46
+ schemaId: location.schemaId,
47
+ jsonPointer: location.jsonPointer + '/' + key
49
48
  }
50
49
  }
51
50
 
51
+ function resolveRef (location, ref) {
52
+ let hashIndex = ref.indexOf('#')
53
+ if (hashIndex === -1) {
54
+ hashIndex = ref.length
55
+ }
56
+
57
+ const schemaId = ref.slice(0, hashIndex) || location.schemaId
58
+ const jsonPointer = ref.slice(hashIndex)
59
+
60
+ const schemaRef = schemaId + jsonPointer
61
+
62
+ let ajvSchema
63
+ try {
64
+ ajvSchema = ajvInstance.getSchema(schemaRef)
65
+ } catch (error) {
66
+ throw new Error(`Cannot find reference "${ref}"`)
67
+ }
68
+
69
+ if (ajvSchema === undefined) {
70
+ throw new Error(`Cannot find reference "${ref}"`)
71
+ }
72
+
73
+ const schema = ajvSchema.schema
74
+ if (schema.$ref !== undefined) {
75
+ return resolveRef({ schema, schemaId, jsonPointer }, schema.$ref)
76
+ }
77
+
78
+ return { schema, schemaId, jsonPointer }
79
+ }
80
+
52
81
  const arrayItemsReferenceSerializersMap = new Map()
53
82
  const objectReferenceSerializersMap = new Map()
54
- const schemaReferenceMap = new Map()
55
83
 
84
+ let rootSchemaId = null
56
85
  let ajvInstance = null
57
86
  let contextFunctions = null
58
87
 
59
88
  function build (schema, options) {
89
+ schema = clone(schema)
90
+
60
91
  arrayItemsReferenceSerializersMap.clear()
61
92
  objectReferenceSerializersMap.clear()
62
- schemaReferenceMap.clear()
63
93
 
64
94
  contextFunctions = []
65
95
  options = options || {}
66
96
 
67
97
  ajvInstance = buildAjv(options.ajv)
98
+ rootSchemaId = schema.$id || randomUUID()
68
99
 
69
100
  isValidSchema(schema)
101
+ extendDateTimeType(schema)
102
+ ajvInstance.addSchema(schema, rootSchemaId)
103
+
70
104
  if (options.schema) {
71
- // eslint-disable-next-line
72
- for (var key of Object.keys(options.schema)) {
73
- isValidSchema(options.schema[key], key)
105
+ const externalSchemas = clone(options.schema)
106
+
107
+ for (const key of Object.keys(externalSchemas)) {
108
+ const externalSchema = externalSchemas[key]
109
+ isValidSchema(externalSchema, key)
110
+ extendDateTimeType(externalSchema)
111
+
112
+ let schemaKey = externalSchema.$id || key
113
+ if (externalSchema.$id !== undefined && externalSchema.$id[0] === '#') {
114
+ schemaKey = key + externalSchema.$id // relative URI
115
+ }
116
+
117
+ if (ajvInstance.getSchema(schemaKey) === undefined) {
118
+ ajvInstance.addSchema(externalSchema, schemaKey)
119
+ }
74
120
  }
75
121
  }
76
122
 
@@ -98,22 +144,8 @@ function build (schema, options) {
98
144
 
99
145
  const serializer = new Serializer(options)
100
146
 
101
- let location = {
102
- schema,
103
- root: schema,
104
- externalSchema: options.schema
105
- }
106
-
107
- if (schema.$ref) {
108
- location = refFinder(schema.$ref, location)
109
- schema = location.schema
110
- }
111
-
112
- if (schema.type === undefined) {
113
- schema.type = inferTypeByKeyword(schema)
114
- }
115
-
116
- const code = buildValue('main', 'input', location)
147
+ const location = { schema, schemaId: rootSchemaId, jsonPointer: '#' }
148
+ const code = buildValue(location, 'input')
117
149
 
118
150
  const contextFunctionCode = `
119
151
  function main (input) {
@@ -136,19 +168,9 @@ function build (schema, options) {
136
168
  }
137
169
 
138
170
  if (options.mode === 'standalone') {
139
- return `
140
- 'use strict'
141
-
142
- const Serializer = require('fast-json-stringify/serializer')
143
- const buildAjv = require('fast-json-stringify/ajv')
144
-
145
- const serializer = new Serializer(${JSON.stringify(options || {})})
146
- const ajv = buildAjv(${JSON.stringify(options.ajv || {})})
147
-
148
- ${contextFunctionCode.replace('return main', '')}
149
-
150
- module.exports = main
151
- `
171
+ // lazy load
172
+ const buildStandaloneCode = require('./standalone')
173
+ return buildStandaloneCode(options, ajvInstance, contextFunctionCode)
152
174
  }
153
175
 
154
176
  /* eslint no-new-func: "off" */
@@ -156,10 +178,10 @@ module.exports = main
156
178
  const stringifyFunc = contextFunc(ajvInstance, serializer)
157
179
 
158
180
  ajvInstance = null
181
+ rootSchemaId = null
159
182
  contextFunctions = null
160
183
  arrayItemsReferenceSerializersMap.clear()
161
184
  objectReferenceSerializersMap.clear()
162
- schemaReferenceMap.clear()
163
185
 
164
186
  return stringifyFunc
165
187
  }
@@ -240,10 +262,11 @@ function addPatternProperties (location) {
240
262
  if (properties[keys[i]]) continue
241
263
  `
242
264
 
243
- Object.keys(pp).forEach((regex, index) => {
244
- let ppLocation = mergeLocation(location, { schema: pp[regex] })
265
+ const patternPropertiesLocation = mergeLocation(location, 'patternProperties')
266
+ Object.keys(pp).forEach((regex) => {
267
+ let ppLocation = mergeLocation(patternPropertiesLocation, regex)
245
268
  if (pp[regex].$ref) {
246
- ppLocation = refFinder(pp[regex].$ref, location)
269
+ ppLocation = resolveRef(ppLocation, pp[regex].$ref)
247
270
  pp[regex] = ppLocation.schema
248
271
  }
249
272
 
@@ -253,7 +276,7 @@ function addPatternProperties (location) {
253
276
  throw new Error(`${err.message}. Found at ${regex} matching ${JSON.stringify(pp[regex])}`)
254
277
  }
255
278
 
256
- const valueCode = buildValue('', 'obj[keys[i]]', ppLocation)
279
+ const valueCode = buildValue(ppLocation, 'obj[keys[i]]')
257
280
  code += `
258
281
  if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {
259
282
  ${addComma}
@@ -286,13 +309,13 @@ function additionalProperty (location) {
286
309
 
287
310
  return code
288
311
  }
289
- let apLocation = mergeLocation(location, { schema: ap })
312
+ let apLocation = mergeLocation(location, 'additionalProperties')
290
313
  if (ap.$ref) {
291
- apLocation = refFinder(ap.$ref, location)
314
+ apLocation = resolveRef(location, ap.$ref)
292
315
  ap = apLocation.schema
293
316
  }
294
317
 
295
- const valueCode = buildValue('', 'obj[keys[i]]', apLocation)
318
+ const valueCode = buildValue(apLocation, 'obj[keys[i]]')
296
319
 
297
320
  code += `
298
321
  ${addComma}
@@ -314,140 +337,9 @@ function addAdditionalProperties (location) {
314
337
  `
315
338
  }
316
339
 
317
- function idFinder (schema, searchedId) {
318
- let objSchema
319
- const explore = (schema, searchedId) => {
320
- Object.keys(schema || {}).forEach((key, i, a) => {
321
- if (key === '$id' && schema[key] === searchedId) {
322
- objSchema = schema
323
- } else if (objSchema === undefined && typeof schema[key] === 'object') {
324
- explore(schema[key], searchedId)
325
- }
326
- })
327
- }
328
- explore(schema, searchedId)
329
- return objSchema
330
- }
331
-
332
- function refFinder (ref, location) {
333
- const externalSchema = location.externalSchema
334
- let root = location.root
335
- let schema = location.schema
336
-
337
- if (externalSchema && externalSchema[ref]) {
338
- return {
339
- schema: externalSchema[ref],
340
- root: externalSchema[ref],
341
- externalSchema
342
- }
343
- }
344
-
345
- // Split file from walk
346
- ref = ref.split('#')
347
-
348
- // Check schemaReferenceMap for $id entry
349
- if (ref[0] && schemaReferenceMap.has(ref[0])) {
350
- schema = schemaReferenceMap.get(ref[0])
351
- root = schemaReferenceMap.get(ref[0])
352
- if (schema.$ref) {
353
- return refFinder(schema.$ref, {
354
- schema,
355
- root,
356
- externalSchema
357
- })
358
- }
359
- } else if (ref[0]) { // If external file
360
- schema = externalSchema[ref[0]]
361
- root = externalSchema[ref[0]]
362
-
363
- if (schema === undefined) {
364
- findBadKey(externalSchema, [ref[0]])
365
- }
366
-
367
- if (schema.$ref) {
368
- return refFinder(schema.$ref, {
369
- schema,
370
- root,
371
- externalSchema
372
- })
373
- }
374
- }
375
-
376
- let code = 'return schema'
377
- // If it has a path
378
- if (ref[1]) {
379
- // ref[1] could contain a JSON pointer - ex: /definitions/num
380
- // or plain name fragment id without suffix # - ex: customId
381
- const walk = ref[1].split('/')
382
- if (walk.length === 1) {
383
- const targetId = `#${ref[1]}`
384
- let dereferenced = idFinder(schema, targetId)
385
- if (dereferenced === undefined && !ref[0]) {
386
- // eslint-disable-next-line
387
- for (var key of Object.keys(externalSchema)) {
388
- dereferenced = idFinder(externalSchema[key], targetId)
389
- if (dereferenced !== undefined) {
390
- root = externalSchema[key]
391
- break
392
- }
393
- }
394
- }
395
-
396
- return {
397
- schema: dereferenced,
398
- root,
399
- externalSchema
400
- }
401
- } else {
402
- // eslint-disable-next-line
403
- for (var i = 1; i < walk.length; i++) {
404
- code += `[${JSON.stringify(walk[i])}]`
405
- }
406
- }
407
- }
408
- let result
409
- try {
410
- result = (new Function('schema', code))(root)
411
- } catch (err) {}
412
-
413
- if (result === undefined && ref[1]) {
414
- const walk = ref[1].split('/')
415
- findBadKey(schema, walk.slice(1))
416
- }
417
-
418
- if (result.$ref) {
419
- return refFinder(result.$ref, {
420
- schema,
421
- root,
422
- externalSchema
423
- })
424
- }
425
-
426
- return {
427
- schema: result,
428
- root,
429
- externalSchema
430
- }
431
-
432
- function findBadKey (obj, keys) {
433
- if (keys.length === 0) return null
434
- const key = keys.shift()
435
- if (obj[key] === undefined) {
436
- stringSimilarity = stringSimilarity || require('string-similarity')
437
- const { bestMatch } = stringSimilarity.findBestMatch(key, Object.keys(obj))
438
- if (bestMatch.rating >= 0.5) {
439
- throw new Error(`Cannot find reference ${JSON.stringify(key)}, did you mean ${JSON.stringify(bestMatch.target)}?`)
440
- } else {
441
- throw new Error(`Cannot find reference ${JSON.stringify(key)}`)
442
- }
443
- }
444
- return findBadKey(obj[key], keys)
445
- }
446
- }
447
-
448
- function buildCode (location, locationPath) {
340
+ function buildCode (location) {
449
341
  if (location.schema.$ref) {
450
- location = refFinder(location.schema.$ref, location)
342
+ location = resolveRef(location, location.schema.$ref)
451
343
  }
452
344
 
453
345
  const schema = location.schema
@@ -455,10 +347,11 @@ function buildCode (location, locationPath) {
455
347
 
456
348
  let code = ''
457
349
 
350
+ const propertiesLocation = mergeLocation(location, 'properties')
458
351
  Object.keys(schema.properties || {}).forEach((key) => {
459
- let propertyLocation = mergeLocation(location, { schema: schema.properties[key] })
352
+ let propertyLocation = mergeLocation(propertiesLocation, key)
460
353
  if (schema.properties[key].$ref) {
461
- propertyLocation = refFinder(schema.properties[key].$ref, location)
354
+ propertyLocation = resolveRef(location, schema.properties[key].$ref)
462
355
  schema.properties[key] = propertyLocation.schema
463
356
  }
464
357
 
@@ -474,7 +367,7 @@ function buildCode (location, locationPath) {
474
367
  json += ${asString} + ':'
475
368
  `
476
369
 
477
- code += buildValue(locationPath + key, `obj[${JSON.stringify(key)}]`, mergeLocation(propertyLocation, { schema: schema.properties[key] }))
370
+ code += buildValue(propertyLocation, `obj[${JSON.stringify(key)}]`)
478
371
 
479
372
  const defaultValue = schema.properties[key].default
480
373
  if (defaultValue !== undefined) {
@@ -504,9 +397,14 @@ function buildCode (location, locationPath) {
504
397
  }
505
398
 
506
399
  function mergeAllOfSchema (location, schema, mergedSchema) {
507
- for (let allOfSchema of schema.allOf) {
400
+ const allOfLocation = mergeLocation(location, 'allOf')
401
+
402
+ for (let i = 0; i < schema.allOf.length; i++) {
403
+ let allOfSchema = schema.allOf[i]
404
+
508
405
  if (allOfSchema.$ref) {
509
- allOfSchema = refFinder(allOfSchema.$ref, mergeLocation(location, { schema: allOfSchema })).schema
406
+ const allOfSchemaLocation = mergeLocation(allOfLocation, i)
407
+ allOfSchema = resolveRef(allOfSchemaLocation, allOfSchema.$ref).schema
510
408
  }
511
409
 
512
410
  let allOfSchemaType = allOfSchema.type
@@ -593,9 +491,9 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
593
491
  delete mergedSchema.allOf
594
492
  }
595
493
 
596
- function buildInnerObject (location, locationPath) {
494
+ function buildInnerObject (location) {
597
495
  const schema = location.schema
598
- let code = buildCode(location, locationPath)
496
+ let code = buildCode(location)
599
497
  if (schema.patternProperties) {
600
498
  code += addPatternProperties(location)
601
499
  } else if (schema.additionalProperties && !schema.patternProperties) {
@@ -604,49 +502,44 @@ function buildInnerObject (location, locationPath) {
604
502
  return code
605
503
  }
606
504
 
607
- function addIfThenElse (location, locationPath) {
608
- let code = ''
505
+ function addIfThenElse (location) {
506
+ const schema = merge({}, location.schema)
507
+ const thenSchema = schema.then
508
+ const elseSchema = schema.else || { additionalProperties: true }
609
509
 
610
- const schema = location.schema
611
- const copy = merge({}, schema)
612
- const i = copy.if
613
- const then = copy.then
614
- const e = copy.else ? copy.else : { additionalProperties: true }
615
- delete copy.if
616
- delete copy.then
617
- delete copy.else
618
- let merged = merge(copy, then)
619
- let mergedLocation = mergeLocation(location, { schema: merged })
620
-
621
- const schemaKey = i.$id || randomUUID()
622
- ajvInstance.addSchema(i, schemaKey)
510
+ delete schema.if
511
+ delete schema.then
512
+ delete schema.else
623
513
 
624
- code += `
625
- valid = ajv.validate("${schemaKey}", obj)
626
- if (valid) {
514
+ const ifLocation = mergeLocation(location, 'if')
515
+ const ifSchemaRef = ifLocation.schemaId + ifLocation.jsonPointer
516
+
517
+ let code = `
518
+ if (ajv.validate("${ifSchemaRef}", obj)) {
627
519
  `
628
- if (merged.if && merged.then) {
629
- code += addIfThenElse(mergedLocation, locationPath + 'Then')
630
- }
631
520
 
632
- code += buildInnerObject(mergedLocation, locationPath + 'Then')
521
+ const thenLocation = mergeLocation(location, 'then')
522
+ thenLocation.schema = merge(schema, thenSchema)
633
523
 
524
+ if (thenSchema.if && thenSchema.then) {
525
+ code += addIfThenElse(thenLocation)
526
+ }
527
+ code += buildInnerObject(thenLocation)
634
528
  code += `
635
529
  }
636
530
  `
637
- merged = merge(copy, e)
638
- mergedLocation = mergeLocation(mergedLocation, { schema: merged })
531
+
532
+ const elseLocation = mergeLocation(location, 'else')
533
+ elseLocation.schema = merge(schema, elseSchema)
639
534
 
640
535
  code += `
641
536
  else {
642
537
  `
643
538
 
644
- if (merged.if && merged.then) {
645
- code += addIfThenElse(mergedLocation, locationPath + 'Else')
539
+ if (elseSchema.if && elseSchema.then) {
540
+ code += addIfThenElse(elseLocation)
646
541
  }
647
-
648
- code += buildInnerObject(mergedLocation, locationPath + 'Else')
649
-
542
+ code += buildInnerObject(elseLocation)
650
543
  code += `
651
544
  }
652
545
  `
@@ -660,11 +553,8 @@ function toJSON (variableName) {
660
553
  `
661
554
  }
662
555
 
663
- function buildObject (location, locationPath) {
556
+ function buildObject (location) {
664
557
  const schema = location.schema
665
- if (schema.$id !== undefined) {
666
- schemaReferenceMap.set(schema.$id, schema)
667
- }
668
558
 
669
559
  if (objectReferenceSerializersMap.has(schema)) {
670
560
  return objectReferenceSerializersMap.get(schema)
@@ -673,9 +563,10 @@ function buildObject (location, locationPath) {
673
563
  const functionName = generateFuncName()
674
564
  objectReferenceSerializersMap.set(schema, functionName)
675
565
 
566
+ const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
676
567
  let functionCode = `
677
568
  function ${functionName} (input) {
678
- // ${locationPath}
569
+ // ${schemaId + location.jsonPointer}
679
570
  `
680
571
  if (schema.nullable) {
681
572
  functionCode += `
@@ -693,12 +584,9 @@ function buildObject (location, locationPath) {
693
584
 
694
585
  let rCode
695
586
  if (schema.if && schema.then) {
696
- functionCode += `
697
- var valid
698
- `
699
- rCode = addIfThenElse(location, locationPath)
587
+ rCode = addIfThenElse(location)
700
588
  } else {
701
- rCode = buildInnerObject(location, locationPath)
589
+ rCode = buildInnerObject(location)
702
590
  }
703
591
 
704
592
  // Removes the comma if is the last element of the string (in case there are not properties)
@@ -712,17 +600,16 @@ function buildObject (location, locationPath) {
712
600
  return functionName
713
601
  }
714
602
 
715
- function buildArray (location, locationPath) {
603
+ function buildArray (location) {
716
604
  let schema = location.schema
717
- if (schema.$id !== undefined) {
718
- schemaReferenceMap.set(schema.$id, schema)
719
- }
720
605
 
721
606
  // default to any items type
722
607
  if (!schema.items) {
723
608
  schema.items = {}
724
609
  }
725
610
 
611
+ let itemsLocation = mergeLocation(location, 'items')
612
+
726
613
  if (schema.items.$ref) {
727
614
  if (!schema[fjsCloned]) {
728
615
  location.schema = clone(location.schema)
@@ -730,7 +617,8 @@ function buildArray (location, locationPath) {
730
617
  schema[fjsCloned] = true
731
618
  }
732
619
 
733
- location = refFinder(schema.items.$ref, location)
620
+ location = resolveRef(location, schema.items.$ref)
621
+ itemsLocation = location
734
622
  schema.items = location.schema
735
623
  }
736
624
 
@@ -741,9 +629,10 @@ function buildArray (location, locationPath) {
741
629
  const functionName = generateFuncName()
742
630
  arrayItemsReferenceSerializersMap.set(schema.items, functionName)
743
631
 
632
+ const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
744
633
  let functionCode = `
745
634
  function ${functionName} (obj) {
746
- // ${locationPath}
635
+ // ${schemaId + location.jsonPointer}
747
636
  `
748
637
 
749
638
  if (schema.nullable) {
@@ -781,11 +670,10 @@ function buildArray (location, locationPath) {
781
670
  let jsonOutput = ''
782
671
  `
783
672
 
784
- const accessor = '[i]'
785
673
  if (Array.isArray(schema.items)) {
786
674
  for (let i = 0; i < schema.items.length; i++) {
787
675
  const item = schema.items[i]
788
- const tmpRes = buildValue(locationPath + accessor + i, `obj[${i}]`, mergeLocation(location, { schema: item }))
676
+ const tmpRes = buildValue(mergeLocation(itemsLocation, i), `obj[${i}]`)
789
677
  functionCode += `
790
678
  if (${i} < arrayLength) {
791
679
  if (${buildArrayTypeCondition(item.type, `[${i}]`)}) {
@@ -813,7 +701,7 @@ function buildArray (location, locationPath) {
813
701
  }`
814
702
  }
815
703
  } else {
816
- const code = buildValue(locationPath + accessor, 'obj[i]', mergeLocation(location, { schema: schema.items }))
704
+ const code = buildValue(itemsLocation, 'obj[i]')
817
705
  functionCode += `
818
706
  for (let i = 0; i < arrayLength; i++) {
819
707
  let json = ''
@@ -870,40 +758,17 @@ function buildArrayTypeCondition (type, accessor) {
870
758
  return condition
871
759
  }
872
760
 
873
- function dereferenceOfRefs (location, type) {
874
- if (!location.schema[fjsCloned]) {
875
- const schemaClone = clone(location.schema)
876
- schemaClone[fjsCloned] = true
877
- location.schema = schemaClone
878
- }
879
-
880
- const schema = location.schema
881
- const locations = []
882
-
883
- schema[type].forEach((s, index) => {
884
- // follow the refs
885
- let sLocation = mergeLocation(location, { schema: s })
886
- while (s.$ref) {
887
- sLocation = refFinder(s.$ref, sLocation)
888
- schema[type][index] = sLocation.schema
889
- s = schema[type][index]
890
- }
891
- locations[index] = sLocation
892
- })
893
-
894
- return locations
895
- }
896
-
897
761
  let genFuncNameCounter = 0
898
762
  function generateFuncName () {
899
763
  return 'anonymous' + genFuncNameCounter++
900
764
  }
901
765
 
902
- function buildValue (locationPath, input, location) {
766
+ function buildValue (location, input) {
903
767
  let schema = location.schema
904
768
 
905
769
  if (schema.$ref) {
906
- schema = refFinder(schema.$ref, location)
770
+ location = resolveRef(location, schema.$ref)
771
+ schema = location.schema
907
772
  }
908
773
 
909
774
  if (schema.type === undefined) {
@@ -950,41 +815,32 @@ function buildValue (locationPath, input, location) {
950
815
  code += `json += ${funcName}(${input})`
951
816
  break
952
817
  case 'object':
953
- funcName = buildObject(location, locationPath)
818
+ funcName = buildObject(location)
954
819
  code += `json += ${funcName}(${input})`
955
820
  break
956
821
  case 'array':
957
- funcName = buildArray(location, locationPath)
822
+ funcName = buildArray(location)
958
823
  code += `json += ${funcName}(${input})`
959
824
  break
960
825
  case undefined:
961
- if (schema.anyOf || schema.oneOf) {
826
+ if (schema.fjs_date_type) {
827
+ funcName = getStringSerializer(schema.fjs_date_type, nullable)
828
+ code += `json += ${funcName}(${input})`
829
+ break
830
+ } else if (schema.anyOf || schema.oneOf) {
962
831
  // beware: dereferenceOfRefs has side effects and changes schema.anyOf
963
- const locations = dereferenceOfRefs(location, schema.anyOf ? 'anyOf' : 'oneOf')
964
- locations.forEach((location, index) => {
965
- const nestedResult = buildValue(locationPath + 'i' + index, input, location)
966
- // Since we are only passing the relevant schema to ajv.validate, it needs to be full dereferenced
967
- // otherwise any $ref pointing to an external schema would result in an error.
968
- // Full dereference of the schema happens as side effect of two functions:
969
- // 1. `dereferenceOfRefs` loops through the `schema.anyOf`` array and replaces any top level reference
970
- // with the actual schema
971
- // 2. `buildValue`, through `buildCode`, replaces any reference in object properties with the actual schema
972
- // (see https://github.com/fastify/fast-json-stringify/blob/6da3b3e8ac24b1ca5578223adedb4083b7adf8db/index.js#L631)
973
-
974
- // Ajv does not support js date format. In order to properly validate objects containing a date,
975
- // it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
976
- // (see https://github.com/fastify/fast-json-stringify/pull/441)
977
- const extendedSchema = clone(location.schema)
978
- extendDateTimeType(extendedSchema)
979
-
980
- const schemaKey = location.schema.$id || randomUUID()
981
- ajvInstance.addSchema(extendedSchema, schemaKey)
832
+ const type = schema.anyOf ? 'anyOf' : 'oneOf'
833
+ const anyOfLocation = mergeLocation(location, type)
982
834
 
835
+ for (let index = 0; index < location.schema[type].length; index++) {
836
+ const optionLocation = mergeLocation(anyOfLocation, index)
837
+ const schemaRef = optionLocation.schemaId + optionLocation.jsonPointer
838
+ const nestedResult = buildValue(optionLocation, input)
983
839
  code += `
984
- ${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaKey}", ${input}))
840
+ ${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaRef}", ${input}))
985
841
  ${nestedResult}
986
842
  `
987
- })
843
+ }
988
844
 
989
845
  code += `
990
846
  else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
@@ -1021,10 +877,11 @@ function buildValue (locationPath, input, location) {
1021
877
  } else {`
1022
878
  }
1023
879
 
880
+ const locationClone = clone(location)
1024
881
  sortedTypes.forEach((type, index) => {
1025
882
  const statement = index === 0 ? 'if' : 'else if'
1026
- const tempSchema = Object.assign({}, schema, { type })
1027
- const nestedResult = buildValue(locationPath, input, mergeLocation(location, { schema: tempSchema }))
883
+ locationClone.schema.type = type
884
+ const nestedResult = buildValue(locationClone, input)
1028
885
  switch (type) {
1029
886
  case 'string': {
1030
887
  code += `
@@ -1073,7 +930,12 @@ function buildValue (locationPath, input, location) {
1073
930
  return code
1074
931
  }
1075
932
 
933
+ // Ajv does not support js date format. In order to properly validate objects containing a date,
934
+ // it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
935
+ // (see https://github.com/fastify/fast-json-stringify/pull/441)
1076
936
  function extendDateTimeType (schema) {
937
+ if (schema === null) return
938
+
1077
939
  if (schema.type === 'string' && ['date-time', 'date', 'time'].includes(schema.format)) {
1078
940
  schema.fjs_date_type = schema.format
1079
941
  delete schema.type