fast-json-stringify 5.11.1 → 5.12.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/index.js +267 -238
- package/lib/location.js +1 -30
- package/lib/merge-schemas.js +9 -0
- package/package.json +3 -3
- package/test/allof.test.js +134 -17
- package/test/anyof.test.js +148 -0
- package/test/if-then-else.test.js +58 -9
- package/test/required.test.js +4 -3
package/index.js
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
/* eslint no-prototype-builtins: 0 */
|
|
4
4
|
|
|
5
|
-
const merge = require('@fastify/deepmerge')()
|
|
6
|
-
const clone = require('rfdc')({ proto: true })
|
|
7
5
|
const { RefResolver } = require('json-schema-ref-resolver')
|
|
8
6
|
|
|
9
|
-
const validate = require('./lib/schema-validator')
|
|
10
7
|
const Serializer = require('./lib/serializer')
|
|
11
8
|
const Validator = require('./lib/validator')
|
|
12
9
|
const Location = require('./lib/location')
|
|
10
|
+
const validate = require('./lib/schema-validator')
|
|
11
|
+
const mergeSchemas = require('./lib/merge-schemas')
|
|
13
12
|
|
|
14
13
|
const SINGLE_TICK = /'/g
|
|
15
14
|
|
|
@@ -28,8 +27,6 @@ const validLargeArrayMechanisms = [
|
|
|
28
27
|
'json-stringify'
|
|
29
28
|
]
|
|
30
29
|
|
|
31
|
-
const addComma = '!addComma && (addComma = true) || (json += \',\')'
|
|
32
|
-
|
|
33
30
|
let schemaIdCounter = 0
|
|
34
31
|
|
|
35
32
|
function isValidSchema (schema, name) {
|
|
@@ -46,13 +43,15 @@ function isValidSchema (schema, name) {
|
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
function resolveRef (context, location
|
|
46
|
+
function resolveRef (context, location) {
|
|
47
|
+
const ref = location.schema.$ref
|
|
48
|
+
|
|
50
49
|
let hashIndex = ref.indexOf('#')
|
|
51
50
|
if (hashIndex === -1) {
|
|
52
51
|
hashIndex = ref.length
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
const schemaId = ref.slice(0, hashIndex) || location.
|
|
54
|
+
const schemaId = ref.slice(0, hashIndex) || location.schemaId
|
|
56
55
|
const jsonPointer = ref.slice(hashIndex) || '#'
|
|
57
56
|
|
|
58
57
|
const schema = context.refResolver.getSchema(schemaId, jsonPointer)
|
|
@@ -62,12 +61,17 @@ function resolveRef (context, location, ref) {
|
|
|
62
61
|
|
|
63
62
|
const newLocation = new Location(schema, schemaId, jsonPointer)
|
|
64
63
|
if (schema.$ref !== undefined) {
|
|
65
|
-
return resolveRef(context, newLocation
|
|
64
|
+
return resolveRef(context, newLocation)
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
return newLocation
|
|
69
68
|
}
|
|
70
69
|
|
|
70
|
+
function getMergedLocation (context, mergedSchemaId) {
|
|
71
|
+
const mergedSchema = context.refResolver.getSchema(mergedSchemaId, '#')
|
|
72
|
+
return new Location(mergedSchema, mergedSchemaId, '#')
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
function getSchemaId (schema, rootSchemaId) {
|
|
72
76
|
if (schema.$id && schema.$id.charAt(0) !== '#') {
|
|
73
77
|
return schema.$id
|
|
@@ -85,10 +89,10 @@ function build (schema, options) {
|
|
|
85
89
|
functionsCounter: 0,
|
|
86
90
|
functionsNamesBySchema: new Map(),
|
|
87
91
|
options,
|
|
88
|
-
wrapObjects: true,
|
|
89
92
|
refResolver: new RefResolver(),
|
|
90
93
|
rootSchemaId: schema.$id || `__fjs_root_${schemaIdCounter++}`,
|
|
91
|
-
validatorSchemasIds: new Set()
|
|
94
|
+
validatorSchemasIds: new Set(),
|
|
95
|
+
mergedSchemasIds: new Map()
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
const schemaId = getSchemaId(schema, context.rootSchemaId)
|
|
@@ -255,7 +259,7 @@ function inferTypeByKeyword (schema) {
|
|
|
255
259
|
return schema.type
|
|
256
260
|
}
|
|
257
261
|
|
|
258
|
-
function buildExtraObjectPropertiesSerializer (context, location) {
|
|
262
|
+
function buildExtraObjectPropertiesSerializer (context, location, addComma) {
|
|
259
263
|
const schema = location.schema
|
|
260
264
|
const propertiesKeys = Object.keys(schema.properties || {})
|
|
261
265
|
|
|
@@ -314,225 +318,138 @@ function buildExtraObjectPropertiesSerializer (context, location) {
|
|
|
314
318
|
}
|
|
315
319
|
|
|
316
320
|
function buildInnerObject (context, location) {
|
|
317
|
-
let code = ''
|
|
318
321
|
const schema = location.schema
|
|
319
|
-
const required = schema.required || []
|
|
320
322
|
|
|
321
323
|
const propertiesLocation = location.getPropertyLocation('properties')
|
|
324
|
+
const requiredProperties = schema.required || []
|
|
322
325
|
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
333
|
-
}
|
|
326
|
+
// Should serialize required properties first
|
|
327
|
+
const propertiesKeys = Object.keys(schema.properties || {}).sort(
|
|
328
|
+
(key1, key2) => {
|
|
329
|
+
const required1 = requiredProperties.includes(key1)
|
|
330
|
+
const required2 = requiredProperties.includes(key2)
|
|
331
|
+
return required1 === required2 ? 0 : required1 ? -1 : 1
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
const hasRequiredProperties = requiredProperties.includes(propertiesKeys[0])
|
|
334
335
|
|
|
335
|
-
|
|
336
|
+
let code = ''
|
|
336
337
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (defaultValue === undefined) {
|
|
341
|
-
code += `if (obj[${sanitizedKey}] === undefined) throw new Error('${sanitizedKey} is required!')\n`
|
|
342
|
-
requiredWithoutDefault.push(key)
|
|
343
|
-
}
|
|
344
|
-
requiredWithDefault.push(key)
|
|
338
|
+
for (const key of requiredProperties) {
|
|
339
|
+
if (!propertiesKeys.includes(key)) {
|
|
340
|
+
code += `if (obj['${key}'] === undefined) throw new Error('"${key}" is required!')\n`
|
|
345
341
|
}
|
|
346
342
|
}
|
|
347
343
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
344
|
+
code += 'let json = \'{\'\n'
|
|
345
|
+
|
|
346
|
+
let addComma = ''
|
|
347
|
+
if (!hasRequiredProperties) {
|
|
348
|
+
code += 'let addComma = false\n'
|
|
349
|
+
addComma = '!addComma && (addComma = true) || (json += \',\')'
|
|
352
350
|
}
|
|
353
351
|
|
|
354
|
-
|
|
355
|
-
let
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
context.wrapObjects = true
|
|
360
|
-
|
|
361
|
-
if (schema.properties) {
|
|
362
|
-
for (const key of Object.keys(schema.properties)) {
|
|
363
|
-
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
364
|
-
if (propertyLocation.schema.$ref) {
|
|
365
|
-
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
366
|
-
}
|
|
352
|
+
for (const key of propertiesKeys) {
|
|
353
|
+
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
354
|
+
if (propertyLocation.schema.$ref) {
|
|
355
|
+
propertyLocation = resolveRef(context, propertyLocation)
|
|
356
|
+
}
|
|
367
357
|
|
|
368
|
-
|
|
358
|
+
const sanitizedKey = JSON.stringify(key)
|
|
359
|
+
const defaultValue = propertyLocation.schema.default
|
|
360
|
+
const isRequired = requiredProperties.includes(key)
|
|
369
361
|
|
|
370
|
-
|
|
371
|
-
|
|
362
|
+
code += `
|
|
363
|
+
if (obj[${sanitizedKey}] !== undefined) {
|
|
372
364
|
${addComma}
|
|
373
365
|
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
374
366
|
${buildValue(context, propertyLocation, `obj[${sanitizedKey}]`)}
|
|
367
|
+
}`
|
|
368
|
+
|
|
369
|
+
if (defaultValue !== undefined) {
|
|
370
|
+
code += ` else {
|
|
371
|
+
${addComma}
|
|
372
|
+
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
|
|
373
|
+
}
|
|
375
374
|
`
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
code += `
|
|
380
|
-
if (obj[${sanitizedKey}] !== undefined) {
|
|
381
|
-
${addComma}
|
|
382
|
-
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
383
|
-
${buildValue(context, propertyLocation, `obj[${sanitizedKey}]`)}
|
|
384
|
-
}
|
|
385
|
-
`
|
|
386
|
-
const defaultValue = propertyLocation.schema.default
|
|
387
|
-
if (defaultValue !== undefined) {
|
|
388
|
-
code += `
|
|
389
|
-
else {
|
|
390
|
-
${addComma}
|
|
391
|
-
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
|
|
392
|
-
}
|
|
393
|
-
`
|
|
394
|
-
}
|
|
375
|
+
} else if (isRequired) {
|
|
376
|
+
code += ` else {
|
|
377
|
+
throw new Error('${sanitizedKey} is required!')
|
|
395
378
|
}
|
|
379
|
+
`
|
|
380
|
+
} else {
|
|
381
|
+
code += '\n'
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (hasRequiredProperties) {
|
|
385
|
+
addComma = 'json += \',\''
|
|
396
386
|
}
|
|
397
387
|
}
|
|
398
388
|
|
|
399
389
|
if (schema.patternProperties || schema.additionalProperties) {
|
|
400
|
-
code += buildExtraObjectPropertiesSerializer(context, location)
|
|
390
|
+
code += buildExtraObjectPropertiesSerializer(context, location, addComma)
|
|
401
391
|
}
|
|
402
392
|
|
|
403
|
-
context.wrapObjects = wrapObjects
|
|
404
393
|
code += `
|
|
405
|
-
|
|
394
|
+
return json + '}'
|
|
406
395
|
`
|
|
407
396
|
return code
|
|
408
397
|
}
|
|
409
398
|
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (allOfSchema.$ref) {
|
|
417
|
-
const allOfSchemaLocation = allOfLocation.getPropertyLocation(i)
|
|
418
|
-
allOfSchema = resolveRef(context, allOfSchemaLocation, allOfSchema.$ref).schema
|
|
399
|
+
function mergeLocations (context, mergedSchemaId, mergedLocations) {
|
|
400
|
+
for (let i = 0; i < mergedLocations.length; i++) {
|
|
401
|
+
const location = mergedLocations[i]
|
|
402
|
+
const schema = location.schema
|
|
403
|
+
if (schema.$ref) {
|
|
404
|
+
mergedLocations[i] = resolveRef(context, location)
|
|
419
405
|
}
|
|
406
|
+
}
|
|
420
407
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
408
|
+
const mergedSchemas = []
|
|
409
|
+
for (const location of mergedLocations) {
|
|
410
|
+
const schema = cloneOriginSchema(context, location.schema, location.schemaId)
|
|
411
|
+
delete schema.$id
|
|
425
412
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
mergedSchema.type !== undefined &&
|
|
429
|
-
mergedSchema.type !== allOfSchemaType
|
|
430
|
-
) {
|
|
431
|
-
throw new Error('allOf schemas have different type values')
|
|
432
|
-
}
|
|
433
|
-
mergedSchema.type = allOfSchemaType
|
|
434
|
-
}
|
|
413
|
+
mergedSchemas.push(schema)
|
|
414
|
+
}
|
|
435
415
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
mergedSchema.format !== undefined &&
|
|
439
|
-
mergedSchema.format !== allOfSchema.format
|
|
440
|
-
) {
|
|
441
|
-
throw new Error('allOf schemas have different format values')
|
|
442
|
-
}
|
|
443
|
-
mergedSchema.format = allOfSchema.format
|
|
444
|
-
}
|
|
416
|
+
const mergedSchema = mergeSchemas(mergedSchemas)
|
|
417
|
+
const mergedLocation = new Location(mergedSchema, mergedSchemaId)
|
|
445
418
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
mergedSchema.nullable !== allOfSchema.nullable
|
|
450
|
-
) {
|
|
451
|
-
throw new Error('allOf schemas have different nullable values')
|
|
452
|
-
}
|
|
453
|
-
mergedSchema.nullable = allOfSchema.nullable
|
|
454
|
-
}
|
|
419
|
+
context.refResolver.addSchema(mergedSchema, mergedSchemaId)
|
|
420
|
+
return mergedLocation
|
|
421
|
+
}
|
|
455
422
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
mergedSchema.properties = {}
|
|
459
|
-
}
|
|
460
|
-
Object.assign(mergedSchema.properties, allOfSchema.properties)
|
|
461
|
-
}
|
|
423
|
+
function cloneOriginSchema (context, schema, schemaId) {
|
|
424
|
+
const clonedSchema = Array.isArray(schema) ? [] : {}
|
|
462
425
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
426
|
+
if (
|
|
427
|
+
schema.$id !== undefined &&
|
|
428
|
+
schema.$id.charAt(0) !== '#'
|
|
429
|
+
) {
|
|
430
|
+
schemaId = schema.$id
|
|
431
|
+
}
|
|
469
432
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
Object.assign(mergedSchema.patternProperties, allOfSchema.patternProperties)
|
|
475
|
-
}
|
|
433
|
+
const mergedSchemaRef = context.mergedSchemasIds.get(schema)
|
|
434
|
+
if (mergedSchemaRef) {
|
|
435
|
+
context.mergedSchemasIds.set(clonedSchema, mergedSchemaRef)
|
|
436
|
+
}
|
|
476
437
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
mergedSchema.required = []
|
|
480
|
-
}
|
|
481
|
-
mergedSchema.required.push(...allOfSchema.required)
|
|
482
|
-
}
|
|
438
|
+
for (const key in schema) {
|
|
439
|
+
let value = schema[key]
|
|
483
440
|
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
mergedSchema.oneOf = []
|
|
487
|
-
}
|
|
488
|
-
mergedSchema.oneOf.push(...allOfSchema.oneOf)
|
|
441
|
+
if (key === '$ref' && value.charAt(0) === '#') {
|
|
442
|
+
value = schemaId + value
|
|
489
443
|
}
|
|
490
444
|
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
mergedSchema.anyOf = []
|
|
494
|
-
}
|
|
495
|
-
mergedSchema.anyOf.push(...allOfSchema.anyOf)
|
|
445
|
+
if (typeof value === 'object' && value !== null) {
|
|
446
|
+
value = cloneOriginSchema(context, value, schemaId)
|
|
496
447
|
}
|
|
497
448
|
|
|
498
|
-
|
|
499
|
-
mergeAllOfSchema(context, location, allOfSchema, mergedSchema)
|
|
500
|
-
}
|
|
449
|
+
clonedSchema[key] = value
|
|
501
450
|
}
|
|
502
|
-
delete mergedSchema.allOf
|
|
503
451
|
|
|
504
|
-
|
|
505
|
-
context.refResolver.addSchema(mergedSchema)
|
|
506
|
-
location.addMergedSchema(mergedSchema, mergedSchema.$id)
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function addIfThenElse (context, location, input) {
|
|
510
|
-
context.validatorSchemasIds.add(location.getSchemaId())
|
|
511
|
-
|
|
512
|
-
const schema = merge({}, location.schema)
|
|
513
|
-
const thenSchema = schema.then
|
|
514
|
-
const elseSchema = schema.else || { additionalProperties: true }
|
|
515
|
-
|
|
516
|
-
delete schema.if
|
|
517
|
-
delete schema.then
|
|
518
|
-
delete schema.else
|
|
519
|
-
|
|
520
|
-
const ifLocation = location.getPropertyLocation('if')
|
|
521
|
-
const ifSchemaRef = ifLocation.getSchemaRef()
|
|
522
|
-
|
|
523
|
-
const thenLocation = location.getPropertyLocation('then')
|
|
524
|
-
thenLocation.schema = merge(schema, thenSchema)
|
|
525
|
-
|
|
526
|
-
const elseLocation = location.getPropertyLocation('else')
|
|
527
|
-
elseLocation.schema = merge(schema, elseSchema)
|
|
528
|
-
|
|
529
|
-
return `
|
|
530
|
-
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
531
|
-
${buildValue(context, thenLocation, input)}
|
|
532
|
-
} else {
|
|
533
|
-
${buildValue(context, elseLocation, input)}
|
|
534
|
-
}
|
|
535
|
-
`
|
|
452
|
+
return clonedSchema
|
|
536
453
|
}
|
|
537
454
|
|
|
538
455
|
function toJSON (variableName) {
|
|
@@ -582,7 +499,7 @@ function buildArray (context, location) {
|
|
|
582
499
|
itemsLocation.schema = itemsLocation.schema || {}
|
|
583
500
|
|
|
584
501
|
if (itemsLocation.schema.$ref) {
|
|
585
|
-
itemsLocation = resolveRef(context, itemsLocation
|
|
502
|
+
itemsLocation = resolveRef(context, itemsLocation)
|
|
586
503
|
}
|
|
587
504
|
|
|
588
505
|
const itemsSchema = itemsLocation.schema
|
|
@@ -860,81 +777,193 @@ function buildConstSerializer (location, input) {
|
|
|
860
777
|
return code
|
|
861
778
|
}
|
|
862
779
|
|
|
863
|
-
function
|
|
864
|
-
|
|
780
|
+
function buildAllOf (context, location, input) {
|
|
781
|
+
const schema = location.schema
|
|
865
782
|
|
|
866
|
-
|
|
867
|
-
|
|
783
|
+
let mergedSchemaId = context.mergedSchemasIds.get(schema)
|
|
784
|
+
if (mergedSchemaId) {
|
|
785
|
+
const mergedLocation = getMergedLocation(context, mergedSchemaId)
|
|
786
|
+
return buildValue(context, mergedLocation, input)
|
|
868
787
|
}
|
|
869
788
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
789
|
+
mergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
790
|
+
context.mergedSchemasIds.set(schema, mergedSchemaId)
|
|
791
|
+
|
|
792
|
+
const { allOf, ...schemaWithoutAllOf } = location.schema
|
|
793
|
+
const locations = [
|
|
794
|
+
new Location(
|
|
795
|
+
schemaWithoutAllOf,
|
|
796
|
+
location.schemaId,
|
|
797
|
+
location.jsonPointer
|
|
798
|
+
)
|
|
799
|
+
]
|
|
800
|
+
|
|
801
|
+
const allOfsLocation = location.getPropertyLocation('allOf')
|
|
802
|
+
for (let i = 0; i < allOf.length; i++) {
|
|
803
|
+
locations.push(allOfsLocation.getPropertyLocation(i))
|
|
873
804
|
}
|
|
874
805
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
806
|
+
const mergedLocation = mergeLocations(context, mergedSchemaId, locations)
|
|
807
|
+
return buildValue(context, mergedLocation, input)
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function buildOneOf (context, location, input) {
|
|
811
|
+
context.validatorSchemasIds.add(location.schemaId)
|
|
812
|
+
|
|
813
|
+
const schema = location.schema
|
|
814
|
+
|
|
815
|
+
const type = schema.anyOf ? 'anyOf' : 'oneOf'
|
|
816
|
+
const { [type]: oneOfs, ...schemaWithoutAnyOf } = location.schema
|
|
817
|
+
|
|
818
|
+
const locationWithoutOneOf = new Location(
|
|
819
|
+
schemaWithoutAnyOf,
|
|
820
|
+
location.schemaId,
|
|
821
|
+
location.jsonPointer
|
|
822
|
+
)
|
|
823
|
+
const oneOfsLocation = location.getPropertyLocation(type)
|
|
824
|
+
|
|
825
|
+
let code = ''
|
|
826
|
+
|
|
827
|
+
for (let index = 0; index < oneOfs.length; index++) {
|
|
828
|
+
const optionLocation = oneOfsLocation.getPropertyLocation(index)
|
|
829
|
+
const optionSchema = optionLocation.schema
|
|
830
|
+
|
|
831
|
+
let mergedSchemaId = context.mergedSchemasIds.get(optionSchema)
|
|
832
|
+
let mergedLocation = null
|
|
833
|
+
if (mergedSchemaId) {
|
|
834
|
+
mergedLocation = getMergedLocation(context, mergedSchemaId)
|
|
835
|
+
} else {
|
|
836
|
+
mergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
837
|
+
context.mergedSchemasIds.set(optionSchema, mergedSchemaId)
|
|
838
|
+
|
|
839
|
+
mergedLocation = mergeLocations(context, mergedSchemaId, [
|
|
840
|
+
locationWithoutOneOf,
|
|
841
|
+
optionLocation
|
|
842
|
+
])
|
|
879
843
|
}
|
|
844
|
+
|
|
845
|
+
const nestedResult = buildValue(context, mergedLocation, input)
|
|
846
|
+
const schemaRef = optionLocation.getSchemaRef()
|
|
847
|
+
code += `
|
|
848
|
+
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
|
|
849
|
+
${nestedResult}
|
|
850
|
+
`
|
|
880
851
|
}
|
|
881
852
|
|
|
882
|
-
|
|
883
|
-
|
|
853
|
+
let schemaRef = location.getSchemaRef()
|
|
854
|
+
if (schemaRef.startsWith(context.rootSchemaId)) {
|
|
855
|
+
schemaRef = schemaRef.replace(context.rootSchemaId, '')
|
|
884
856
|
}
|
|
885
857
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
858
|
+
code += `
|
|
859
|
+
else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
|
|
860
|
+
`
|
|
861
|
+
|
|
862
|
+
return code
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function buildIfThenElse (context, location, input) {
|
|
866
|
+
context.validatorSchemasIds.add(location.schemaId)
|
|
867
|
+
|
|
868
|
+
const {
|
|
869
|
+
if: ifSchema,
|
|
870
|
+
then: thenSchema,
|
|
871
|
+
else: elseSchema,
|
|
872
|
+
...schemaWithoutIfThenElse
|
|
873
|
+
} = location.schema
|
|
874
|
+
|
|
875
|
+
const rootLocation = new Location(
|
|
876
|
+
schemaWithoutIfThenElse,
|
|
877
|
+
location.schemaId,
|
|
878
|
+
location.jsonPointer
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
const ifLocation = location.getPropertyLocation('if')
|
|
882
|
+
const ifSchemaRef = ifLocation.getSchemaRef()
|
|
883
|
+
|
|
884
|
+
const thenLocation = location.getPropertyLocation('then')
|
|
885
|
+
let thenMergedSchemaId = context.mergedSchemasIds.get(thenSchema)
|
|
886
|
+
let thenMergedLocation = null
|
|
887
|
+
if (thenMergedSchemaId) {
|
|
888
|
+
thenMergedLocation = getMergedLocation(context, thenMergedSchemaId)
|
|
889
|
+
} else {
|
|
890
|
+
thenMergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
891
|
+
context.mergedSchemasIds.set(thenSchema, thenMergedSchemaId)
|
|
892
|
+
|
|
893
|
+
thenMergedLocation = mergeLocations(context, thenMergedSchemaId, [
|
|
894
|
+
rootLocation,
|
|
895
|
+
thenLocation
|
|
896
|
+
])
|
|
889
897
|
}
|
|
890
898
|
|
|
891
|
-
|
|
899
|
+
if (!elseSchema) {
|
|
900
|
+
return `
|
|
901
|
+
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
902
|
+
${buildValue(context, thenMergedLocation, input)}
|
|
903
|
+
} else {
|
|
904
|
+
${buildValue(context, rootLocation, input)}
|
|
905
|
+
}
|
|
906
|
+
`
|
|
907
|
+
}
|
|
892
908
|
|
|
893
|
-
|
|
909
|
+
const elseLocation = location.getPropertyLocation('else')
|
|
910
|
+
let elseMergedSchemaId = context.mergedSchemasIds.get(elseSchema)
|
|
911
|
+
let elseMergedLocation = null
|
|
912
|
+
if (elseMergedSchemaId) {
|
|
913
|
+
elseMergedLocation = getMergedLocation(context, elseMergedSchemaId)
|
|
914
|
+
} else {
|
|
915
|
+
elseMergedSchemaId = `__fjs_merged_${schemaIdCounter++}`
|
|
916
|
+
context.mergedSchemasIds.set(elseSchema, elseMergedSchemaId)
|
|
894
917
|
|
|
895
|
-
|
|
896
|
-
|
|
918
|
+
elseMergedLocation = mergeLocations(context, elseMergedSchemaId, [
|
|
919
|
+
rootLocation,
|
|
920
|
+
elseLocation
|
|
921
|
+
])
|
|
922
|
+
}
|
|
897
923
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
json += ${funcName}(${input})
|
|
904
|
-
json += ','
|
|
905
|
-
`
|
|
924
|
+
return `
|
|
925
|
+
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
926
|
+
${buildValue(context, thenMergedLocation, input)}
|
|
927
|
+
} else {
|
|
928
|
+
${buildValue(context, elseMergedLocation, input)}
|
|
906
929
|
}
|
|
930
|
+
`
|
|
931
|
+
}
|
|
907
932
|
|
|
908
|
-
|
|
909
|
-
|
|
933
|
+
function buildValue (context, location, input) {
|
|
934
|
+
let schema = location.schema
|
|
910
935
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const nestedResult = buildValue(context, optionLocation, input)
|
|
915
|
-
code += `
|
|
916
|
-
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
|
|
917
|
-
${nestedResult}
|
|
918
|
-
`
|
|
919
|
-
}
|
|
936
|
+
if (typeof schema === 'boolean') {
|
|
937
|
+
return `json += JSON.stringify(${input})`
|
|
938
|
+
}
|
|
920
939
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
940
|
+
if (schema.$ref) {
|
|
941
|
+
location = resolveRef(context, location)
|
|
942
|
+
schema = location.schema
|
|
943
|
+
}
|
|
925
944
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
945
|
+
if (schema.allOf) {
|
|
946
|
+
return buildAllOf(context, location, input)
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (schema.anyOf || schema.oneOf) {
|
|
950
|
+
return buildOneOf(context, location, input)
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
if (schema.if && schema.then) {
|
|
954
|
+
return buildIfThenElse(context, location, input)
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
if (schema.type === undefined) {
|
|
958
|
+
const inferredType = inferTypeByKeyword(schema)
|
|
959
|
+
if (inferredType) {
|
|
960
|
+
schema.type = inferredType
|
|
934
961
|
}
|
|
935
|
-
return code
|
|
936
962
|
}
|
|
937
963
|
|
|
964
|
+
let code = ''
|
|
965
|
+
|
|
966
|
+
const type = schema.type
|
|
938
967
|
const nullable = schema.nullable === true
|
|
939
968
|
if (nullable) {
|
|
940
969
|
code += `
|
package/lib/location.js
CHANGED
|
@@ -5,7 +5,6 @@ class Location {
|
|
|
5
5
|
this.schema = schema
|
|
6
6
|
this.schemaId = schemaId
|
|
7
7
|
this.jsonPointer = jsonPointer
|
|
8
|
-
this.mergedSchemaId = null
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
getPropertyLocation (propertyName) {
|
|
@@ -14,39 +13,11 @@ class Location {
|
|
|
14
13
|
this.schemaId,
|
|
15
14
|
this.jsonPointer + '/' + propertyName
|
|
16
15
|
)
|
|
17
|
-
|
|
18
|
-
if (this.mergedSchemaId !== null) {
|
|
19
|
-
propertyLocation.addMergedSchema(
|
|
20
|
-
this.schema[propertyName],
|
|
21
|
-
this.mergedSchemaId,
|
|
22
|
-
this.jsonPointer + '/' + propertyName
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
16
|
return propertyLocation
|
|
27
17
|
}
|
|
28
18
|
|
|
29
|
-
// Use this method to get current schema location.
|
|
30
|
-
// Use it when you need to create reference to the current location.
|
|
31
|
-
getSchemaId () {
|
|
32
|
-
return this.mergedSchemaId || this.schemaId
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Use this method to get original schema id for resolving user schema $refs
|
|
36
|
-
// Don't join it with a JSON pointer to get the current location.
|
|
37
|
-
getOriginSchemaId () {
|
|
38
|
-
return this.schemaId
|
|
39
|
-
}
|
|
40
|
-
|
|
41
19
|
getSchemaRef () {
|
|
42
|
-
|
|
43
|
-
return schemaId + this.jsonPointer
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
addMergedSchema (mergedSchema, schemaId, jsonPointer = '#') {
|
|
47
|
-
this.schema = mergedSchema
|
|
48
|
-
this.mergedSchemaId = schemaId
|
|
49
|
-
this.jsonPointer = jsonPointer
|
|
20
|
+
return this.schemaId + this.jsonPointer
|
|
50
21
|
}
|
|
51
22
|
}
|
|
52
23
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-json-stringify",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
4
4
|
"description": "Stringify your JSON at max speed",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -51,13 +51,13 @@
|
|
|
51
51
|
"fast-json-stringify": "."
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@fastify/deepmerge": "^1.0.0",
|
|
55
54
|
"ajv": "^8.10.0",
|
|
56
55
|
"ajv-formats": "^2.1.1",
|
|
57
56
|
"fast-deep-equal": "^3.1.3",
|
|
58
57
|
"fast-uri": "^2.1.0",
|
|
59
58
|
"rfdc": "^1.2.0",
|
|
60
|
-
"json-schema-ref-resolver": "^1.0.1"
|
|
59
|
+
"json-schema-ref-resolver": "^1.0.1",
|
|
60
|
+
"@fastify/merge-json-schemas": "^0.1.0"
|
|
61
61
|
},
|
|
62
62
|
"standard": {
|
|
63
63
|
"ignore": [
|
package/test/allof.test.js
CHANGED
|
@@ -121,7 +121,7 @@ test('object with allOf and multiple schema on the allOf', (t) => {
|
|
|
121
121
|
id: 1,
|
|
122
122
|
name: 'string',
|
|
123
123
|
tag: 'otherString'
|
|
124
|
-
}), '{"name":"string","tag":"otherString"
|
|
124
|
+
}), '{"name":"string","id":1,"tag":"otherString"}')
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
test('object with allOf and one schema on the allOf', (t) => {
|
|
@@ -553,8 +553,60 @@ test('allof with local anchor reference', (t) => {
|
|
|
553
553
|
t.equal(stringify(data), JSON.stringify(data))
|
|
554
554
|
})
|
|
555
555
|
|
|
556
|
+
test('allOf: multiple nested $ref properties', (t) => {
|
|
557
|
+
t.plan(2)
|
|
558
|
+
|
|
559
|
+
const externalSchema1 = {
|
|
560
|
+
$id: 'externalSchema1',
|
|
561
|
+
oneOf: [
|
|
562
|
+
{ $ref: '#/definitions/id1' }
|
|
563
|
+
],
|
|
564
|
+
definitions: {
|
|
565
|
+
id1: {
|
|
566
|
+
type: 'object',
|
|
567
|
+
properties: {
|
|
568
|
+
id1: {
|
|
569
|
+
type: 'integer'
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
additionalProperties: false
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const externalSchema2 = {
|
|
578
|
+
$id: 'externalSchema2',
|
|
579
|
+
oneOf: [
|
|
580
|
+
{ $ref: '#/definitions/id2' }
|
|
581
|
+
],
|
|
582
|
+
definitions: {
|
|
583
|
+
id2: {
|
|
584
|
+
type: 'object',
|
|
585
|
+
properties: {
|
|
586
|
+
id2: {
|
|
587
|
+
type: 'integer'
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
additionalProperties: false
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const schema = {
|
|
596
|
+
anyOf: [
|
|
597
|
+
{ $ref: 'externalSchema1' },
|
|
598
|
+
{ $ref: 'externalSchema2' }
|
|
599
|
+
]
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const stringify = build(schema, { schema: [externalSchema1, externalSchema2] })
|
|
603
|
+
|
|
604
|
+
t.equal(stringify({ id1: 1 }), JSON.stringify({ id1: 1 }))
|
|
605
|
+
t.equal(stringify({ id2: 2 }), JSON.stringify({ id2: 2 }))
|
|
606
|
+
})
|
|
607
|
+
|
|
556
608
|
test('allOf: throw Error if types mismatch ', (t) => {
|
|
557
|
-
t.plan(
|
|
609
|
+
t.plan(3)
|
|
558
610
|
|
|
559
611
|
const schema = {
|
|
560
612
|
allOf: [
|
|
@@ -562,11 +614,18 @@ test('allOf: throw Error if types mismatch ', (t) => {
|
|
|
562
614
|
{ type: 'number' }
|
|
563
615
|
]
|
|
564
616
|
}
|
|
565
|
-
|
|
617
|
+
try {
|
|
618
|
+
build(schema)
|
|
619
|
+
t.fail('should throw the MergeError')
|
|
620
|
+
} catch (error) {
|
|
621
|
+
t.ok(error instanceof Error)
|
|
622
|
+
t.equal(error.message, 'Failed to merge "type" keyword schemas.')
|
|
623
|
+
t.same(error.schemas, [['string'], ['number']])
|
|
624
|
+
}
|
|
566
625
|
})
|
|
567
626
|
|
|
568
627
|
test('allOf: throw Error if format mismatch ', (t) => {
|
|
569
|
-
t.plan(
|
|
628
|
+
t.plan(3)
|
|
570
629
|
|
|
571
630
|
const schema = {
|
|
572
631
|
allOf: [
|
|
@@ -574,29 +633,87 @@ test('allOf: throw Error if format mismatch ', (t) => {
|
|
|
574
633
|
{ format: 'time' }
|
|
575
634
|
]
|
|
576
635
|
}
|
|
577
|
-
|
|
636
|
+
try {
|
|
637
|
+
build(schema)
|
|
638
|
+
t.fail('should throw the MergeError')
|
|
639
|
+
} catch (error) {
|
|
640
|
+
t.ok(error instanceof Error)
|
|
641
|
+
t.equal(error.message, 'Failed to merge "format" keyword schemas.')
|
|
642
|
+
t.same(error.schemas, ['date', 'time'])
|
|
643
|
+
}
|
|
578
644
|
})
|
|
579
645
|
|
|
580
|
-
test('
|
|
646
|
+
test('recursive nested allOfs', (t) => {
|
|
581
647
|
t.plan(1)
|
|
582
648
|
|
|
583
649
|
const schema = {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
650
|
+
type: 'object',
|
|
651
|
+
properties: {
|
|
652
|
+
foo: {
|
|
653
|
+
additionalProperties: false,
|
|
654
|
+
allOf: [{ $ref: '#' }]
|
|
655
|
+
}
|
|
656
|
+
}
|
|
588
657
|
}
|
|
589
|
-
|
|
658
|
+
|
|
659
|
+
const data = { foo: {} }
|
|
660
|
+
const stringify = build(schema)
|
|
661
|
+
t.equal(stringify(data), JSON.stringify(data))
|
|
590
662
|
})
|
|
591
663
|
|
|
592
|
-
test('
|
|
664
|
+
test('recursive nested allOfs', (t) => {
|
|
593
665
|
t.plan(1)
|
|
594
666
|
|
|
595
667
|
const schema = {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
668
|
+
type: 'object',
|
|
669
|
+
properties: {
|
|
670
|
+
foo: {
|
|
671
|
+
additionalProperties: false,
|
|
672
|
+
allOf: [{ allOf: [{ $ref: '#' }] }]
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const data = { foo: {} }
|
|
678
|
+
const stringify = build(schema)
|
|
679
|
+
t.equal(stringify(data), JSON.stringify(data))
|
|
680
|
+
})
|
|
681
|
+
|
|
682
|
+
test('external recursive allOfs', (t) => {
|
|
683
|
+
t.plan(1)
|
|
684
|
+
|
|
685
|
+
const externalSchema = {
|
|
686
|
+
type: 'object',
|
|
687
|
+
properties: {
|
|
688
|
+
foo: {
|
|
689
|
+
properties: {
|
|
690
|
+
bar: { type: 'string' }
|
|
691
|
+
},
|
|
692
|
+
allOf: [{ $ref: '#' }]
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const schema = {
|
|
698
|
+
type: 'object',
|
|
699
|
+
properties: {
|
|
700
|
+
a: { $ref: 'externalSchema#/properties/foo' },
|
|
701
|
+
b: { $ref: 'externalSchema#/properties/foo' }
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const data = {
|
|
706
|
+
a: {
|
|
707
|
+
foo: {},
|
|
708
|
+
bar: '42',
|
|
709
|
+
baz: 42
|
|
710
|
+
},
|
|
711
|
+
b: {
|
|
712
|
+
foo: {},
|
|
713
|
+
bar: '42',
|
|
714
|
+
baz: 42
|
|
715
|
+
}
|
|
600
716
|
}
|
|
601
|
-
|
|
717
|
+
const stringify = build(schema, { schema: { externalSchema } })
|
|
718
|
+
t.equal(stringify(data), '{"a":{"bar":"42","foo":{}},"b":{"bar":"42","foo":{}}}')
|
|
602
719
|
})
|
package/test/anyof.test.js
CHANGED
|
@@ -644,3 +644,151 @@ test('object with ref and validated properties', (t) => {
|
|
|
644
644
|
const stringify = build(schema, { schema: externalSchemas })
|
|
645
645
|
t.equal(stringify({ id: 1, reference: 'hi' }), '{"id":1,"reference":"hi"}')
|
|
646
646
|
})
|
|
647
|
+
|
|
648
|
+
test('anyOf required props', (t) => {
|
|
649
|
+
t.plan(3)
|
|
650
|
+
|
|
651
|
+
const schema = {
|
|
652
|
+
type: 'object',
|
|
653
|
+
properties: {
|
|
654
|
+
prop1: { type: 'string' },
|
|
655
|
+
prop2: { type: 'string' },
|
|
656
|
+
prop3: { type: 'string' }
|
|
657
|
+
},
|
|
658
|
+
required: ['prop1'],
|
|
659
|
+
anyOf: [{ required: ['prop2'] }, { required: ['prop3'] }]
|
|
660
|
+
}
|
|
661
|
+
const stringify = build(schema)
|
|
662
|
+
t.equal(stringify({ prop1: 'test', prop2: 'test2' }), '{"prop1":"test","prop2":"test2"}')
|
|
663
|
+
t.equal(stringify({ prop1: 'test', prop3: 'test3' }), '{"prop1":"test","prop3":"test3"}')
|
|
664
|
+
t.equal(stringify({ prop1: 'test', prop2: 'test2', prop3: 'test3' }), '{"prop1":"test","prop2":"test2","prop3":"test3"}')
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
test('anyOf required props', (t) => {
|
|
668
|
+
t.plan(3)
|
|
669
|
+
|
|
670
|
+
const schema = {
|
|
671
|
+
type: 'object',
|
|
672
|
+
properties: {
|
|
673
|
+
prop1: { type: 'string' }
|
|
674
|
+
},
|
|
675
|
+
anyOf: [
|
|
676
|
+
{
|
|
677
|
+
properties: {
|
|
678
|
+
prop2: { type: 'string' }
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
properties: {
|
|
683
|
+
prop3: { type: 'string' }
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
]
|
|
687
|
+
}
|
|
688
|
+
const stringify = build(schema)
|
|
689
|
+
t.equal(stringify({ prop1: 'test1' }), '{"prop1":"test1"}')
|
|
690
|
+
t.equal(stringify({ prop2: 'test2' }), '{"prop2":"test2"}')
|
|
691
|
+
t.equal(stringify({ prop1: 'test1', prop2: 'test2' }), '{"prop1":"test1","prop2":"test2"}')
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
test('recursive nested anyOfs', (t) => {
|
|
695
|
+
t.plan(1)
|
|
696
|
+
|
|
697
|
+
const schema = {
|
|
698
|
+
type: 'object',
|
|
699
|
+
properties: {
|
|
700
|
+
foo: {
|
|
701
|
+
additionalProperties: false,
|
|
702
|
+
anyOf: [{ $ref: '#' }]
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const data = { foo: {} }
|
|
708
|
+
const stringify = build(schema)
|
|
709
|
+
t.equal(stringify(data), JSON.stringify(data))
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
test('recursive nested anyOfs', (t) => {
|
|
713
|
+
t.plan(1)
|
|
714
|
+
|
|
715
|
+
const schema = {
|
|
716
|
+
type: 'object',
|
|
717
|
+
properties: {
|
|
718
|
+
foo: {
|
|
719
|
+
additionalProperties: false,
|
|
720
|
+
anyOf: [{ anyOf: [{ $ref: '#' }] }]
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const data = { foo: {} }
|
|
726
|
+
const stringify = build(schema)
|
|
727
|
+
t.equal(stringify(data), JSON.stringify(data))
|
|
728
|
+
})
|
|
729
|
+
|
|
730
|
+
test('external recursive anyOfs', (t) => {
|
|
731
|
+
t.plan(1)
|
|
732
|
+
|
|
733
|
+
const externalSchema = {
|
|
734
|
+
type: 'object',
|
|
735
|
+
properties: {
|
|
736
|
+
foo: {
|
|
737
|
+
properties: {
|
|
738
|
+
bar: { type: 'string' }
|
|
739
|
+
},
|
|
740
|
+
anyOf: [{ $ref: '#' }]
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const schema = {
|
|
746
|
+
type: 'object',
|
|
747
|
+
properties: {
|
|
748
|
+
a: { $ref: 'externalSchema#/properties/foo' },
|
|
749
|
+
b: { $ref: 'externalSchema#/properties/foo' }
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const data = {
|
|
754
|
+
a: {
|
|
755
|
+
foo: {},
|
|
756
|
+
bar: '42',
|
|
757
|
+
baz: 42
|
|
758
|
+
},
|
|
759
|
+
b: {
|
|
760
|
+
foo: {},
|
|
761
|
+
bar: '42',
|
|
762
|
+
baz: 42
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
const stringify = build(schema, { schema: { externalSchema } })
|
|
766
|
+
t.equal(stringify(data), '{"a":{"bar":"42","foo":{}},"b":{"bar":"42","foo":{}}}')
|
|
767
|
+
})
|
|
768
|
+
|
|
769
|
+
test('should build merged schemas twice', (t) => {
|
|
770
|
+
t.plan(2)
|
|
771
|
+
|
|
772
|
+
const schema = {
|
|
773
|
+
type: 'object',
|
|
774
|
+
properties: {
|
|
775
|
+
enums: {
|
|
776
|
+
type: 'string',
|
|
777
|
+
anyOf: [
|
|
778
|
+
{ type: 'string', const: 'FOO' },
|
|
779
|
+
{ type: 'string', const: 'BAR' }
|
|
780
|
+
]
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
{
|
|
786
|
+
const stringify = build(schema)
|
|
787
|
+
t.equal(stringify({ enums: 'FOO' }), '{"enums":"FOO"}')
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
{
|
|
791
|
+
const stringify = build(schema)
|
|
792
|
+
t.equal(stringify({ enums: 'BAR' }), '{"enums":"BAR"}')
|
|
793
|
+
}
|
|
794
|
+
})
|
|
@@ -249,15 +249,7 @@ const alphabetOutput = JSON.stringify({
|
|
|
249
249
|
const deepFoobarOutput = JSON.stringify({
|
|
250
250
|
foobar: JSON.parse(foobarOutput)
|
|
251
251
|
})
|
|
252
|
-
const noElseGreetingOutput = JSON.stringify({
|
|
253
|
-
kind: 'greeting',
|
|
254
|
-
foo: 'FOO',
|
|
255
|
-
bar: 42,
|
|
256
|
-
hi: 'HI',
|
|
257
|
-
hello: 45,
|
|
258
|
-
a: 'A',
|
|
259
|
-
b: 35
|
|
260
|
-
})
|
|
252
|
+
const noElseGreetingOutput = JSON.stringify({})
|
|
261
253
|
|
|
262
254
|
t.test('if-then-else', t => {
|
|
263
255
|
const tests = [
|
|
@@ -419,3 +411,60 @@ t.test('if/else with array', (t) => {
|
|
|
419
411
|
t.equal(stringify(['1']), JSON.stringify(['1']))
|
|
420
412
|
t.equal(stringify(['1', '2']), JSON.stringify([1, 2]))
|
|
421
413
|
})
|
|
414
|
+
|
|
415
|
+
t.test('external recursive if/then/else', (t) => {
|
|
416
|
+
t.plan(1)
|
|
417
|
+
|
|
418
|
+
const externalSchema = {
|
|
419
|
+
type: 'object',
|
|
420
|
+
properties: {
|
|
421
|
+
base: { type: 'string' },
|
|
422
|
+
self: { $ref: 'externalSchema#' }
|
|
423
|
+
},
|
|
424
|
+
if: {
|
|
425
|
+
type: 'object',
|
|
426
|
+
properties: {
|
|
427
|
+
foo: { type: 'string', const: '41' }
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
then: {
|
|
431
|
+
type: 'object',
|
|
432
|
+
properties: {
|
|
433
|
+
bar: { type: 'string', const: '42' }
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
else: {
|
|
437
|
+
type: 'object',
|
|
438
|
+
properties: {
|
|
439
|
+
baz: { type: 'string', const: '43' }
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const schema = {
|
|
445
|
+
type: 'object',
|
|
446
|
+
properties: {
|
|
447
|
+
a: { $ref: 'externalSchema#/properties/self' },
|
|
448
|
+
b: { $ref: 'externalSchema#/properties/self' }
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const data = {
|
|
453
|
+
a: {
|
|
454
|
+
base: 'a',
|
|
455
|
+
foo: '41',
|
|
456
|
+
bar: '42',
|
|
457
|
+
baz: '43',
|
|
458
|
+
ignore: 'ignored'
|
|
459
|
+
},
|
|
460
|
+
b: {
|
|
461
|
+
base: 'b',
|
|
462
|
+
foo: 'not-41',
|
|
463
|
+
bar: '42',
|
|
464
|
+
baz: '43',
|
|
465
|
+
ignore: 'ignored'
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const stringify = build(schema, { schema: { externalSchema } })
|
|
469
|
+
t.equal(stringify(data), '{"a":{"base":"a","bar":"42"},"b":{"base":"b","baz":"43"}}')
|
|
470
|
+
})
|
package/test/required.test.js
CHANGED
|
@@ -126,17 +126,18 @@ test('object with multiple required field not in properties schema', (t) => {
|
|
|
126
126
|
stringify({})
|
|
127
127
|
t.fail()
|
|
128
128
|
} catch (e) {
|
|
129
|
-
t.equal(e.message, '"
|
|
129
|
+
t.equal(e.message, '"key1" is required!')
|
|
130
130
|
t.pass()
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
try {
|
|
134
134
|
stringify({
|
|
135
|
-
|
|
135
|
+
key1: 42,
|
|
136
|
+
key2: 42
|
|
136
137
|
})
|
|
137
138
|
t.fail()
|
|
138
139
|
} catch (e) {
|
|
139
|
-
t.equal(e.message, '"
|
|
140
|
+
t.equal(e.message, '"num" is required!')
|
|
140
141
|
t.pass()
|
|
141
142
|
}
|
|
142
143
|
|