fast-json-stringify 4.2.0 → 5.0.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/ajv.js CHANGED
@@ -7,7 +7,7 @@ const ajvFormats = require('ajv-formats')
7
7
  module.exports = buildAjv
8
8
 
9
9
  function buildAjv (options) {
10
- const ajvInstance = new Ajv({ ...options, strictSchema: false, uriResolver: fastUri })
10
+ const ajvInstance = new Ajv({ ...options, strictSchema: false, validateSchema: false, uriResolver: fastUri })
11
11
  ajvFormats(ajvInstance)
12
12
 
13
13
  const validateDateTimeFormat = ajvFormats.get('date-time').validate
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
+ options.schema = clone(options.schema)
106
+
107
+ for (const key of Object.keys(options.schema)) {
108
+ const externalSchema = options.schema[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) {
@@ -146,10 +178,10 @@ function build (schema, options) {
146
178
  const stringifyFunc = contextFunc(ajvInstance, serializer)
147
179
 
148
180
  ajvInstance = null
181
+ rootSchemaId = null
149
182
  contextFunctions = null
150
183
  arrayItemsReferenceSerializersMap.clear()
151
184
  objectReferenceSerializersMap.clear()
152
- schemaReferenceMap.clear()
153
185
 
154
186
  return stringifyFunc
155
187
  }
@@ -230,10 +262,11 @@ function addPatternProperties (location) {
230
262
  if (properties[keys[i]]) continue
231
263
  `
232
264
 
233
- Object.keys(pp).forEach((regex, index) => {
234
- 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)
235
268
  if (pp[regex].$ref) {
236
- ppLocation = refFinder(pp[regex].$ref, location)
269
+ ppLocation = resolveRef(ppLocation, pp[regex].$ref)
237
270
  pp[regex] = ppLocation.schema
238
271
  }
239
272
 
@@ -243,7 +276,7 @@ function addPatternProperties (location) {
243
276
  throw new Error(`${err.message}. Found at ${regex} matching ${JSON.stringify(pp[regex])}`)
244
277
  }
245
278
 
246
- const valueCode = buildValue('', 'obj[keys[i]]', ppLocation)
279
+ const valueCode = buildValue(ppLocation, 'obj[keys[i]]')
247
280
  code += `
248
281
  if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {
249
282
  ${addComma}
@@ -276,13 +309,13 @@ function additionalProperty (location) {
276
309
 
277
310
  return code
278
311
  }
279
- let apLocation = mergeLocation(location, { schema: ap })
312
+ let apLocation = mergeLocation(location, 'additionalProperties')
280
313
  if (ap.$ref) {
281
- apLocation = refFinder(ap.$ref, location)
314
+ apLocation = resolveRef(location, ap.$ref)
282
315
  ap = apLocation.schema
283
316
  }
284
317
 
285
- const valueCode = buildValue('', 'obj[keys[i]]', apLocation)
318
+ const valueCode = buildValue(apLocation, 'obj[keys[i]]')
286
319
 
287
320
  code += `
288
321
  ${addComma}
@@ -304,140 +337,9 @@ function addAdditionalProperties (location) {
304
337
  `
305
338
  }
306
339
 
307
- function idFinder (schema, searchedId) {
308
- let objSchema
309
- const explore = (schema, searchedId) => {
310
- Object.keys(schema || {}).forEach((key, i, a) => {
311
- if (key === '$id' && schema[key] === searchedId) {
312
- objSchema = schema
313
- } else if (objSchema === undefined && typeof schema[key] === 'object') {
314
- explore(schema[key], searchedId)
315
- }
316
- })
317
- }
318
- explore(schema, searchedId)
319
- return objSchema
320
- }
321
-
322
- function refFinder (ref, location) {
323
- const externalSchema = location.externalSchema
324
- let root = location.root
325
- let schema = location.schema
326
-
327
- if (externalSchema && externalSchema[ref]) {
328
- return {
329
- schema: externalSchema[ref],
330
- root: externalSchema[ref],
331
- externalSchema
332
- }
333
- }
334
-
335
- // Split file from walk
336
- ref = ref.split('#')
337
-
338
- // Check schemaReferenceMap for $id entry
339
- if (ref[0] && schemaReferenceMap.has(ref[0])) {
340
- schema = schemaReferenceMap.get(ref[0])
341
- root = schemaReferenceMap.get(ref[0])
342
- if (schema.$ref) {
343
- return refFinder(schema.$ref, {
344
- schema,
345
- root,
346
- externalSchema
347
- })
348
- }
349
- } else if (ref[0]) { // If external file
350
- schema = externalSchema[ref[0]]
351
- root = externalSchema[ref[0]]
352
-
353
- if (schema === undefined) {
354
- findBadKey(externalSchema, [ref[0]])
355
- }
356
-
357
- if (schema.$ref) {
358
- return refFinder(schema.$ref, {
359
- schema,
360
- root,
361
- externalSchema
362
- })
363
- }
364
- }
365
-
366
- let code = 'return schema'
367
- // If it has a path
368
- if (ref[1]) {
369
- // ref[1] could contain a JSON pointer - ex: /definitions/num
370
- // or plain name fragment id without suffix # - ex: customId
371
- const walk = ref[1].split('/')
372
- if (walk.length === 1) {
373
- const targetId = `#${ref[1]}`
374
- let dereferenced = idFinder(schema, targetId)
375
- if (dereferenced === undefined && !ref[0]) {
376
- // eslint-disable-next-line
377
- for (var key of Object.keys(externalSchema)) {
378
- dereferenced = idFinder(externalSchema[key], targetId)
379
- if (dereferenced !== undefined) {
380
- root = externalSchema[key]
381
- break
382
- }
383
- }
384
- }
385
-
386
- return {
387
- schema: dereferenced,
388
- root,
389
- externalSchema
390
- }
391
- } else {
392
- // eslint-disable-next-line
393
- for (var i = 1; i < walk.length; i++) {
394
- code += `[${JSON.stringify(walk[i])}]`
395
- }
396
- }
397
- }
398
- let result
399
- try {
400
- result = (new Function('schema', code))(root)
401
- } catch (err) {}
402
-
403
- if (result === undefined && ref[1]) {
404
- const walk = ref[1].split('/')
405
- findBadKey(schema, walk.slice(1))
406
- }
407
-
408
- if (result.$ref) {
409
- return refFinder(result.$ref, {
410
- schema,
411
- root,
412
- externalSchema
413
- })
414
- }
415
-
416
- return {
417
- schema: result,
418
- root,
419
- externalSchema
420
- }
421
-
422
- function findBadKey (obj, keys) {
423
- if (keys.length === 0) return null
424
- const key = keys.shift()
425
- if (obj[key] === undefined) {
426
- stringSimilarity = stringSimilarity || require('string-similarity')
427
- const { bestMatch } = stringSimilarity.findBestMatch(key, Object.keys(obj))
428
- if (bestMatch.rating >= 0.5) {
429
- throw new Error(`Cannot find reference ${JSON.stringify(key)}, did you mean ${JSON.stringify(bestMatch.target)}?`)
430
- } else {
431
- throw new Error(`Cannot find reference ${JSON.stringify(key)}`)
432
- }
433
- }
434
- return findBadKey(obj[key], keys)
435
- }
436
- }
437
-
438
- function buildCode (location, locationPath) {
340
+ function buildCode (location) {
439
341
  if (location.schema.$ref) {
440
- location = refFinder(location.schema.$ref, location)
342
+ location = resolveRef(location, location.schema.$ref)
441
343
  }
442
344
 
443
345
  const schema = location.schema
@@ -445,10 +347,11 @@ function buildCode (location, locationPath) {
445
347
 
446
348
  let code = ''
447
349
 
350
+ const propertiesLocation = mergeLocation(location, 'properties')
448
351
  Object.keys(schema.properties || {}).forEach((key) => {
449
- let propertyLocation = mergeLocation(location, { schema: schema.properties[key] })
352
+ let propertyLocation = mergeLocation(propertiesLocation, key)
450
353
  if (schema.properties[key].$ref) {
451
- propertyLocation = refFinder(schema.properties[key].$ref, location)
354
+ propertyLocation = resolveRef(location, schema.properties[key].$ref)
452
355
  schema.properties[key] = propertyLocation.schema
453
356
  }
454
357
 
@@ -464,7 +367,7 @@ function buildCode (location, locationPath) {
464
367
  json += ${asString} + ':'
465
368
  `
466
369
 
467
- code += buildValue(locationPath + key, `obj[${JSON.stringify(key)}]`, mergeLocation(propertyLocation, { schema: schema.properties[key] }))
370
+ code += buildValue(propertyLocation, `obj[${JSON.stringify(key)}]`)
468
371
 
469
372
  const defaultValue = schema.properties[key].default
470
373
  if (defaultValue !== undefined) {
@@ -494,9 +397,14 @@ function buildCode (location, locationPath) {
494
397
  }
495
398
 
496
399
  function mergeAllOfSchema (location, schema, mergedSchema) {
497
- 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
+
498
405
  if (allOfSchema.$ref) {
499
- allOfSchema = refFinder(allOfSchema.$ref, mergeLocation(location, { schema: allOfSchema })).schema
406
+ const allOfSchemaLocation = mergeLocation(allOfLocation, i)
407
+ allOfSchema = resolveRef(allOfSchemaLocation, allOfSchema.$ref).schema
500
408
  }
501
409
 
502
410
  let allOfSchemaType = allOfSchema.type
@@ -583,9 +491,9 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
583
491
  delete mergedSchema.allOf
584
492
  }
585
493
 
586
- function buildInnerObject (location, locationPath) {
494
+ function buildInnerObject (location) {
587
495
  const schema = location.schema
588
- let code = buildCode(location, locationPath)
496
+ let code = buildCode(location)
589
497
  if (schema.patternProperties) {
590
498
  code += addPatternProperties(location)
591
499
  } else if (schema.additionalProperties && !schema.patternProperties) {
@@ -594,49 +502,44 @@ function buildInnerObject (location, locationPath) {
594
502
  return code
595
503
  }
596
504
 
597
- function addIfThenElse (location, locationPath) {
598
- let code = ''
505
+ function addIfThenElse (location) {
506
+ const schema = merge({}, location.schema)
507
+ const thenSchema = schema.then
508
+ const elseSchema = schema.else || { additionalProperties: true }
599
509
 
600
- const schema = location.schema
601
- const copy = merge({}, schema)
602
- const i = copy.if
603
- const then = copy.then
604
- const e = copy.else ? copy.else : { additionalProperties: true }
605
- delete copy.if
606
- delete copy.then
607
- delete copy.else
608
- let merged = merge(copy, then)
609
- let mergedLocation = mergeLocation(location, { schema: merged })
610
-
611
- const schemaKey = i.$id || randomUUID()
612
- ajvInstance.addSchema(i, schemaKey)
510
+ delete schema.if
511
+ delete schema.then
512
+ delete schema.else
613
513
 
614
- code += `
615
- valid = ajv.validate("${schemaKey}", obj)
616
- 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)) {
617
519
  `
618
- if (merged.if && merged.then) {
619
- code += addIfThenElse(mergedLocation, locationPath + 'Then')
620
- }
621
520
 
622
- code += buildInnerObject(mergedLocation, locationPath + 'Then')
521
+ const thenLocation = mergeLocation(location, 'then')
522
+ thenLocation.schema = merge(schema, thenSchema)
623
523
 
524
+ if (thenSchema.if && thenSchema.then) {
525
+ code += addIfThenElse(thenLocation)
526
+ }
527
+ code += buildInnerObject(thenLocation)
624
528
  code += `
625
529
  }
626
530
  `
627
- merged = merge(copy, e)
628
- mergedLocation = mergeLocation(mergedLocation, { schema: merged })
531
+
532
+ const elseLocation = mergeLocation(location, 'else')
533
+ elseLocation.schema = merge(schema, elseSchema)
629
534
 
630
535
  code += `
631
536
  else {
632
537
  `
633
538
 
634
- if (merged.if && merged.then) {
635
- code += addIfThenElse(mergedLocation, locationPath + 'Else')
539
+ if (elseSchema.if && elseSchema.then) {
540
+ code += addIfThenElse(elseLocation)
636
541
  }
637
-
638
- code += buildInnerObject(mergedLocation, locationPath + 'Else')
639
-
542
+ code += buildInnerObject(elseLocation)
640
543
  code += `
641
544
  }
642
545
  `
@@ -650,11 +553,8 @@ function toJSON (variableName) {
650
553
  `
651
554
  }
652
555
 
653
- function buildObject (location, locationPath) {
556
+ function buildObject (location) {
654
557
  const schema = location.schema
655
- if (schema.$id !== undefined) {
656
- schemaReferenceMap.set(schema.$id, schema)
657
- }
658
558
 
659
559
  if (objectReferenceSerializersMap.has(schema)) {
660
560
  return objectReferenceSerializersMap.get(schema)
@@ -663,9 +563,10 @@ function buildObject (location, locationPath) {
663
563
  const functionName = generateFuncName()
664
564
  objectReferenceSerializersMap.set(schema, functionName)
665
565
 
566
+ const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
666
567
  let functionCode = `
667
568
  function ${functionName} (input) {
668
- // ${locationPath}
569
+ // ${schemaId + location.jsonPointer}
669
570
  `
670
571
  if (schema.nullable) {
671
572
  functionCode += `
@@ -683,12 +584,9 @@ function buildObject (location, locationPath) {
683
584
 
684
585
  let rCode
685
586
  if (schema.if && schema.then) {
686
- functionCode += `
687
- var valid
688
- `
689
- rCode = addIfThenElse(location, locationPath)
587
+ rCode = addIfThenElse(location)
690
588
  } else {
691
- rCode = buildInnerObject(location, locationPath)
589
+ rCode = buildInnerObject(location)
692
590
  }
693
591
 
694
592
  // Removes the comma if is the last element of the string (in case there are not properties)
@@ -702,17 +600,16 @@ function buildObject (location, locationPath) {
702
600
  return functionName
703
601
  }
704
602
 
705
- function buildArray (location, locationPath) {
603
+ function buildArray (location) {
706
604
  let schema = location.schema
707
- if (schema.$id !== undefined) {
708
- schemaReferenceMap.set(schema.$id, schema)
709
- }
710
605
 
711
606
  // default to any items type
712
607
  if (!schema.items) {
713
608
  schema.items = {}
714
609
  }
715
610
 
611
+ let itemsLocation = mergeLocation(location, 'items')
612
+
716
613
  if (schema.items.$ref) {
717
614
  if (!schema[fjsCloned]) {
718
615
  location.schema = clone(location.schema)
@@ -720,7 +617,8 @@ function buildArray (location, locationPath) {
720
617
  schema[fjsCloned] = true
721
618
  }
722
619
 
723
- location = refFinder(schema.items.$ref, location)
620
+ location = resolveRef(location, schema.items.$ref)
621
+ itemsLocation = location
724
622
  schema.items = location.schema
725
623
  }
726
624
 
@@ -731,9 +629,10 @@ function buildArray (location, locationPath) {
731
629
  const functionName = generateFuncName()
732
630
  arrayItemsReferenceSerializersMap.set(schema.items, functionName)
733
631
 
632
+ const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
734
633
  let functionCode = `
735
634
  function ${functionName} (obj) {
736
- // ${locationPath}
635
+ // ${schemaId + location.jsonPointer}
737
636
  `
738
637
 
739
638
  if (schema.nullable) {
@@ -771,11 +670,10 @@ function buildArray (location, locationPath) {
771
670
  let jsonOutput = ''
772
671
  `
773
672
 
774
- const accessor = '[i]'
775
673
  if (Array.isArray(schema.items)) {
776
674
  for (let i = 0; i < schema.items.length; i++) {
777
675
  const item = schema.items[i]
778
- const tmpRes = buildValue(locationPath + accessor + i, `obj[${i}]`, mergeLocation(location, { schema: item }))
676
+ const tmpRes = buildValue(mergeLocation(itemsLocation, i), `obj[${i}]`)
779
677
  functionCode += `
780
678
  if (${i} < arrayLength) {
781
679
  if (${buildArrayTypeCondition(item.type, `[${i}]`)}) {
@@ -803,7 +701,7 @@ function buildArray (location, locationPath) {
803
701
  }`
804
702
  }
805
703
  } else {
806
- const code = buildValue(locationPath + accessor, 'obj[i]', mergeLocation(location, { schema: schema.items }))
704
+ const code = buildValue(itemsLocation, 'obj[i]')
807
705
  functionCode += `
808
706
  for (let i = 0; i < arrayLength; i++) {
809
707
  let json = ''
@@ -860,40 +758,17 @@ function buildArrayTypeCondition (type, accessor) {
860
758
  return condition
861
759
  }
862
760
 
863
- function dereferenceOfRefs (location, type) {
864
- if (!location.schema[fjsCloned]) {
865
- const schemaClone = clone(location.schema)
866
- schemaClone[fjsCloned] = true
867
- location.schema = schemaClone
868
- }
869
-
870
- const schema = location.schema
871
- const locations = []
872
-
873
- schema[type].forEach((s, index) => {
874
- // follow the refs
875
- let sLocation = mergeLocation(location, { schema: s })
876
- while (s.$ref) {
877
- sLocation = refFinder(s.$ref, sLocation)
878
- schema[type][index] = sLocation.schema
879
- s = schema[type][index]
880
- }
881
- locations[index] = sLocation
882
- })
883
-
884
- return locations
885
- }
886
-
887
761
  let genFuncNameCounter = 0
888
762
  function generateFuncName () {
889
763
  return 'anonymous' + genFuncNameCounter++
890
764
  }
891
765
 
892
- function buildValue (locationPath, input, location) {
766
+ function buildValue (location, input) {
893
767
  let schema = location.schema
894
768
 
895
769
  if (schema.$ref) {
896
- schema = refFinder(schema.$ref, location)
770
+ location = resolveRef(location, schema.$ref)
771
+ schema = location.schema
897
772
  }
898
773
 
899
774
  if (schema.type === undefined) {
@@ -940,41 +815,32 @@ function buildValue (locationPath, input, location) {
940
815
  code += `json += ${funcName}(${input})`
941
816
  break
942
817
  case 'object':
943
- funcName = buildObject(location, locationPath)
818
+ funcName = buildObject(location)
944
819
  code += `json += ${funcName}(${input})`
945
820
  break
946
821
  case 'array':
947
- funcName = buildArray(location, locationPath)
822
+ funcName = buildArray(location)
948
823
  code += `json += ${funcName}(${input})`
949
824
  break
950
825
  case undefined:
951
- 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) {
952
831
  // beware: dereferenceOfRefs has side effects and changes schema.anyOf
953
- const locations = dereferenceOfRefs(location, schema.anyOf ? 'anyOf' : 'oneOf')
954
- locations.forEach((location, index) => {
955
- const nestedResult = buildValue(locationPath + 'i' + index, input, location)
956
- // Since we are only passing the relevant schema to ajv.validate, it needs to be full dereferenced
957
- // otherwise any $ref pointing to an external schema would result in an error.
958
- // Full dereference of the schema happens as side effect of two functions:
959
- // 1. `dereferenceOfRefs` loops through the `schema.anyOf`` array and replaces any top level reference
960
- // with the actual schema
961
- // 2. `buildValue`, through `buildCode`, replaces any reference in object properties with the actual schema
962
- // (see https://github.com/fastify/fast-json-stringify/blob/6da3b3e8ac24b1ca5578223adedb4083b7adf8db/index.js#L631)
963
-
964
- // Ajv does not support js date format. In order to properly validate objects containing a date,
965
- // it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
966
- // (see https://github.com/fastify/fast-json-stringify/pull/441)
967
- const extendedSchema = clone(location.schema)
968
- extendDateTimeType(extendedSchema)
969
-
970
- const schemaKey = location.schema.$id || randomUUID()
971
- ajvInstance.addSchema(extendedSchema, schemaKey)
832
+ const type = schema.anyOf ? 'anyOf' : 'oneOf'
833
+ const anyOfLocation = mergeLocation(location, type)
972
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)
973
839
  code += `
974
- ${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaKey}", ${input}))
840
+ ${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaRef}", ${input}))
975
841
  ${nestedResult}
976
842
  `
977
- })
843
+ }
978
844
 
979
845
  code += `
980
846
  else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
@@ -1011,10 +877,11 @@ function buildValue (locationPath, input, location) {
1011
877
  } else {`
1012
878
  }
1013
879
 
880
+ const locationClone = clone(location)
1014
881
  sortedTypes.forEach((type, index) => {
1015
882
  const statement = index === 0 ? 'if' : 'else if'
1016
- const tempSchema = Object.assign({}, schema, { type })
1017
- const nestedResult = buildValue(locationPath, input, mergeLocation(location, { schema: tempSchema }))
883
+ locationClone.schema.type = type
884
+ const nestedResult = buildValue(locationClone, input)
1018
885
  switch (type) {
1019
886
  case 'string': {
1020
887
  code += `
@@ -1063,7 +930,12 @@ function buildValue (locationPath, input, location) {
1063
930
  return code
1064
931
  }
1065
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)
1066
936
  function extendDateTimeType (schema) {
937
+ if (schema === null) return
938
+
1067
939
  if (schema.type === 'string' && ['date-time', 'date', 'time'].includes(schema.format)) {
1068
940
  schema.fjs_date_type = schema.format
1069
941
  delete schema.type