fast-json-stringify 1.20.2 → 1.21.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 +83 -45
- package/package.json +1 -1
- package/test/allof.test.js +200 -3
- package/test/ref.test.js +8 -8
- package/test/required.test.js +177 -2
- package/test/sanitize2.test.js +18 -0
- package/test/sanitize3.test.js +17 -0
- package/test/sanitize4.test.js +14 -0
- package/test/sanitize5.test.js +16 -0
- package/test/sanitize6.test.js +22 -0
package/index.js
CHANGED
|
@@ -383,6 +383,11 @@ function addPatternProperties (schema, externalSchema, fullSchema) {
|
|
|
383
383
|
var type = pp[regex].type
|
|
384
384
|
var format = pp[regex].format
|
|
385
385
|
var stringSerializer = getStringSerializer(format)
|
|
386
|
+
try {
|
|
387
|
+
RegExp(regex)
|
|
388
|
+
} catch (err) {
|
|
389
|
+
throw new Error(`${err.message}. Found at ${regex} matching ${JSON.stringify(pp[regex])}`)
|
|
390
|
+
}
|
|
386
391
|
code += `
|
|
387
392
|
if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {
|
|
388
393
|
`
|
|
@@ -425,7 +430,7 @@ function addPatternProperties (schema, externalSchema, fullSchema) {
|
|
|
425
430
|
`
|
|
426
431
|
} else {
|
|
427
432
|
code += `
|
|
428
|
-
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ${type}
|
|
433
|
+
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
|
|
429
434
|
`
|
|
430
435
|
}
|
|
431
436
|
|
|
@@ -518,7 +523,7 @@ function additionalProperty (schema, externalSchema, fullSchema) {
|
|
|
518
523
|
`
|
|
519
524
|
} else {
|
|
520
525
|
code += `
|
|
521
|
-
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ${type}
|
|
526
|
+
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
|
|
522
527
|
`
|
|
523
528
|
}
|
|
524
529
|
return code
|
|
@@ -587,7 +592,7 @@ function refFinder (ref, schema, externalSchema) {
|
|
|
587
592
|
return dereferenced
|
|
588
593
|
} else {
|
|
589
594
|
for (var i = 1; i < walk.length; i++) {
|
|
590
|
-
code += `[
|
|
595
|
+
code += `[${JSON.stringify(walk[i])}]`
|
|
591
596
|
}
|
|
592
597
|
}
|
|
593
598
|
}
|
|
@@ -603,35 +608,27 @@ function refFinder (ref, schema, externalSchema) {
|
|
|
603
608
|
|
|
604
609
|
function findBadKey (obj, keys) {
|
|
605
610
|
if (keys.length === 0) return null
|
|
606
|
-
const key =
|
|
611
|
+
const key = keys.shift()
|
|
607
612
|
if (obj[key] === undefined) {
|
|
608
613
|
stringSimilarity = stringSimilarity || require('string-similarity')
|
|
609
614
|
const { bestMatch } = stringSimilarity.findBestMatch(key, Object.keys(obj))
|
|
610
615
|
if (bestMatch.rating >= 0.5) {
|
|
611
|
-
throw new Error(`Cannot find reference
|
|
616
|
+
throw new Error(`Cannot find reference ${JSON.stringify(key)}, did you mean ${JSON.stringify(bestMatch.target)}?`)
|
|
612
617
|
} else {
|
|
613
|
-
throw new Error(`Cannot find reference
|
|
618
|
+
throw new Error(`Cannot find reference ${JSON.stringify(key)}`)
|
|
614
619
|
}
|
|
615
620
|
}
|
|
616
621
|
return findBadKey(obj[key], keys)
|
|
617
622
|
}
|
|
618
623
|
}
|
|
619
624
|
|
|
620
|
-
function sanitizeKey (key) {
|
|
621
|
-
const rep = key.replace(/(\\*)'/g, function (match, p1) {
|
|
622
|
-
var base = ''
|
|
623
|
-
if (p1.length % 2 === 1) {
|
|
624
|
-
base = p1.slice(2)
|
|
625
|
-
} else {
|
|
626
|
-
base = p1
|
|
627
|
-
}
|
|
628
|
-
var rep = base + '\\\''
|
|
629
|
-
return rep
|
|
630
|
-
})
|
|
631
|
-
return rep
|
|
632
|
-
}
|
|
633
|
-
|
|
634
625
|
function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
626
|
+
if (schema.$ref) {
|
|
627
|
+
schema = refFinder(schema.$ref, fullSchema, externalSchema)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
var required = schema.required
|
|
631
|
+
|
|
635
632
|
Object.keys(schema.properties || {}).forEach((key, i, a) => {
|
|
636
633
|
if (schema.properties[key].$ref) {
|
|
637
634
|
// if the schema object is deep in the tree, we must resolve the ref in the parent scope
|
|
@@ -644,14 +641,14 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
644
641
|
|
|
645
642
|
var type = schema.properties[key].type
|
|
646
643
|
var nullable = schema.properties[key].nullable
|
|
647
|
-
var sanitized =
|
|
648
|
-
var asString =
|
|
644
|
+
var sanitized = JSON.stringify(key)
|
|
645
|
+
var asString = JSON.stringify(sanitized)
|
|
649
646
|
|
|
650
647
|
if (nullable) {
|
|
651
648
|
code += `
|
|
652
|
-
if (obj[
|
|
649
|
+
if (obj[${sanitized}] === null) {
|
|
653
650
|
${addComma}
|
|
654
|
-
json +=
|
|
651
|
+
json += ${asString} + ':null'
|
|
655
652
|
var rendered = true
|
|
656
653
|
} else {
|
|
657
654
|
`
|
|
@@ -659,10 +656,10 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
659
656
|
|
|
660
657
|
if (type === 'number') {
|
|
661
658
|
code += `
|
|
662
|
-
var t = Number(obj[
|
|
659
|
+
var t = Number(obj[${sanitized}])
|
|
663
660
|
if (!isNaN(t)) {
|
|
664
661
|
${addComma}
|
|
665
|
-
json +=
|
|
662
|
+
json += ${asString} + ':' + t
|
|
666
663
|
`
|
|
667
664
|
} else if (type === 'integer') {
|
|
668
665
|
code += `
|
|
@@ -670,25 +667,25 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
670
667
|
`
|
|
671
668
|
if (isLong) {
|
|
672
669
|
code += `
|
|
673
|
-
if (isLong(obj[
|
|
670
|
+
if (isLong(obj[${sanitized}])) {
|
|
674
671
|
${addComma}
|
|
675
|
-
json +=
|
|
672
|
+
json += ${asString} + ':' + obj[${sanitized}].toString()
|
|
676
673
|
rendered = true
|
|
677
674
|
} else {
|
|
678
|
-
var t = Number(obj[
|
|
675
|
+
var t = Number(obj[${sanitized}])
|
|
679
676
|
if (!isNaN(t)) {
|
|
680
677
|
${addComma}
|
|
681
|
-
json +=
|
|
678
|
+
json += ${asString} + ':' + t
|
|
682
679
|
rendered = true
|
|
683
680
|
}
|
|
684
681
|
}
|
|
685
682
|
`
|
|
686
683
|
} else {
|
|
687
684
|
code += `
|
|
688
|
-
var t = Number(obj[
|
|
685
|
+
var t = Number(obj[${sanitized}])
|
|
689
686
|
if (!isNaN(t)) {
|
|
690
687
|
${addComma}
|
|
691
|
-
json +=
|
|
688
|
+
json += ${asString} + ':' + t
|
|
692
689
|
rendered = true
|
|
693
690
|
}
|
|
694
691
|
`
|
|
@@ -698,24 +695,26 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
698
695
|
`
|
|
699
696
|
} else {
|
|
700
697
|
code += `
|
|
701
|
-
if (obj[
|
|
698
|
+
if (obj[${sanitized}] !== undefined) {
|
|
702
699
|
${addComma}
|
|
703
|
-
json +=
|
|
700
|
+
json += ${asString} + ':'
|
|
704
701
|
`
|
|
705
702
|
|
|
706
|
-
var result = nested(laterCode, name, key, schema.properties[key], externalSchema, fullSchema)
|
|
703
|
+
var result = nested(laterCode, name, key, schema.properties[key], externalSchema, fullSchema, undefined, false)
|
|
707
704
|
code += result.code
|
|
708
705
|
laterCode = result.laterCode
|
|
709
706
|
}
|
|
710
707
|
|
|
711
708
|
var defaultValue = schema.properties[key].default
|
|
712
709
|
if (defaultValue !== undefined) {
|
|
710
|
+
required = filterRequired(schema.required, key)
|
|
713
711
|
code += `
|
|
714
712
|
} else {
|
|
715
713
|
${addComma}
|
|
716
|
-
json +=
|
|
714
|
+
json += ${asString} + ':' + ${JSON.stringify(JSON.stringify(defaultValue))}
|
|
717
715
|
`
|
|
718
716
|
} else if (schema.required && schema.required.indexOf(key) !== -1) {
|
|
717
|
+
required = filterRequired(schema.required, key)
|
|
719
718
|
code += `
|
|
720
719
|
} else {
|
|
721
720
|
throw new Error('${sanitized} is required!')
|
|
@@ -732,9 +731,33 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
732
731
|
`
|
|
733
732
|
}
|
|
734
733
|
})
|
|
734
|
+
|
|
735
|
+
if (required && required.length > 0) {
|
|
736
|
+
code += 'var required = ['
|
|
737
|
+
for (var i = 0; i < required.length; i++) {
|
|
738
|
+
if (i > 0) {
|
|
739
|
+
code += ','
|
|
740
|
+
}
|
|
741
|
+
code += `${JSON.stringify(required[i])}`
|
|
742
|
+
}
|
|
743
|
+
code += ']'
|
|
744
|
+
code += `
|
|
745
|
+
for (var i = 0; i < required.length; i++) {
|
|
746
|
+
if (obj[required[i]] === undefined) throw new Error('"' + required[i] + '" is required!')
|
|
747
|
+
}
|
|
748
|
+
`
|
|
749
|
+
}
|
|
750
|
+
|
|
735
751
|
return { code: code, laterCode: laterCode }
|
|
736
752
|
}
|
|
737
753
|
|
|
754
|
+
function filterRequired (required, key) {
|
|
755
|
+
if (!required) {
|
|
756
|
+
return required
|
|
757
|
+
}
|
|
758
|
+
return required.filter(k => k !== key)
|
|
759
|
+
}
|
|
760
|
+
|
|
738
761
|
function buildCodeWithAllOfs (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
739
762
|
if (schema.allOf) {
|
|
740
763
|
schema.allOf.forEach((ss) => {
|
|
@@ -890,7 +913,7 @@ function buildArray (schema, code, name, externalSchema, fullSchema) {
|
|
|
890
913
|
if (Array.isArray(schema.items)) {
|
|
891
914
|
result = schema.items.reduce((res, item, i) => {
|
|
892
915
|
var accessor = '[i]'
|
|
893
|
-
const tmpRes = nested(laterCode, name, accessor, item, externalSchema, fullSchema, i)
|
|
916
|
+
const tmpRes = nested(laterCode, name, accessor, item, externalSchema, fullSchema, i, true)
|
|
894
917
|
var condition = `i === ${i} && ${buildArrayTypeCondition(item.type, accessor)}`
|
|
895
918
|
return {
|
|
896
919
|
code: `${res.code}
|
|
@@ -907,7 +930,7 @@ function buildArray (schema, code, name, externalSchema, fullSchema) {
|
|
|
907
930
|
}
|
|
908
931
|
`
|
|
909
932
|
} else {
|
|
910
|
-
result = nested(laterCode, name, '[i]', schema.items, externalSchema, fullSchema)
|
|
933
|
+
result = nested(laterCode, name, '[i]', schema.items, externalSchema, fullSchema, undefined, true)
|
|
911
934
|
}
|
|
912
935
|
|
|
913
936
|
code += `
|
|
@@ -981,7 +1004,21 @@ function dereferenceOfRefs (schema, externalSchema, fullSchema, type) {
|
|
|
981
1004
|
})
|
|
982
1005
|
}
|
|
983
1006
|
|
|
984
|
-
|
|
1007
|
+
var strNameCounter = 0
|
|
1008
|
+
function asFuncName (str) {
|
|
1009
|
+
// only allow chars that can work
|
|
1010
|
+
var rep = str.replace(/[^a-zA-Z0-9$_]/g, '')
|
|
1011
|
+
|
|
1012
|
+
if (rep.length === 0) {
|
|
1013
|
+
return 'anan' + strNameCounter++
|
|
1014
|
+
} else if (rep !== str) {
|
|
1015
|
+
rep += strNameCounter++
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
return rep
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKey, isArray) {
|
|
985
1022
|
var code = ''
|
|
986
1023
|
var funcName
|
|
987
1024
|
|
|
@@ -1001,7 +1038,8 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1001
1038
|
var type = schema.type
|
|
1002
1039
|
var nullable = schema.nullable === true
|
|
1003
1040
|
|
|
1004
|
-
var accessor =
|
|
1041
|
+
var accessor = isArray ? key : `[${JSON.stringify(key)}]`
|
|
1042
|
+
|
|
1005
1043
|
switch (type) {
|
|
1006
1044
|
case 'null':
|
|
1007
1045
|
code += `
|
|
@@ -1022,14 +1060,14 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1022
1060
|
code += nullable ? `json += obj${accessor} === null ? null : $asBoolean(obj${accessor})` : `json += $asBoolean(obj${accessor})`
|
|
1023
1061
|
break
|
|
1024
1062
|
case 'object':
|
|
1025
|
-
funcName = (name + key + subKey)
|
|
1063
|
+
funcName = asFuncName(name + key + subKey)
|
|
1026
1064
|
laterCode = buildObject(schema, laterCode, funcName, externalSchema, fullSchema)
|
|
1027
1065
|
code += `
|
|
1028
1066
|
json += ${funcName}(obj${accessor})
|
|
1029
1067
|
`
|
|
1030
1068
|
break
|
|
1031
1069
|
case 'array':
|
|
1032
|
-
funcName = '$arr' +
|
|
1070
|
+
funcName = asFuncName('$arr' + name + key + subKey) // eslint-disable-line
|
|
1033
1071
|
laterCode = buildArray(schema, laterCode, funcName, externalSchema, fullSchema)
|
|
1034
1072
|
code += `
|
|
1035
1073
|
json += ${funcName}(obj${accessor})
|
|
@@ -1039,7 +1077,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1039
1077
|
if ('anyOf' in schema) {
|
|
1040
1078
|
dereferenceOfRefs(schema, externalSchema, fullSchema, 'anyOf')
|
|
1041
1079
|
schema.anyOf.forEach((s, index) => {
|
|
1042
|
-
var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index)
|
|
1080
|
+
var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index, isArray)
|
|
1043
1081
|
code += `
|
|
1044
1082
|
${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(s, { depth: null })}, obj${accessor}))
|
|
1045
1083
|
${nestedResult.code}
|
|
@@ -1052,7 +1090,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1052
1090
|
} else if ('oneOf' in schema) {
|
|
1053
1091
|
dereferenceOfRefs(schema, externalSchema, fullSchema, 'oneOf')
|
|
1054
1092
|
schema.oneOf.forEach((s, index) => {
|
|
1055
|
-
var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index)
|
|
1093
|
+
var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index, isArray)
|
|
1056
1094
|
code += `
|
|
1057
1095
|
${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(s, { depth: null })}, obj${accessor}))
|
|
1058
1096
|
${nestedResult.code}
|
|
@@ -1076,7 +1114,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1076
1114
|
const sortedTypes = nullIndex !== -1 ? [type[nullIndex]].concat(type.slice(0, nullIndex)).concat(type.slice(nullIndex + 1)) : type
|
|
1077
1115
|
sortedTypes.forEach((type, index) => {
|
|
1078
1116
|
var tempSchema = Object.assign({}, schema, { type })
|
|
1079
|
-
var nestedResult = nested(laterCode, name, key, tempSchema, externalSchema, fullSchema, subKey)
|
|
1117
|
+
var nestedResult = nested(laterCode, name, key, tempSchema, externalSchema, fullSchema, subKey, isArray)
|
|
1080
1118
|
|
|
1081
1119
|
if (type === 'string') {
|
|
1082
1120
|
code += `
|
package/package.json
CHANGED
package/test/allof.test.js
CHANGED
|
@@ -44,7 +44,7 @@ test('object with allOf and multiple schema on the allOf', (t) => {
|
|
|
44
44
|
id: 1
|
|
45
45
|
})
|
|
46
46
|
} catch (e) {
|
|
47
|
-
t.is(e.message, 'name is required!')
|
|
47
|
+
t.is(e.message, '"name" is required!')
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
try {
|
|
@@ -52,7 +52,7 @@ test('object with allOf and multiple schema on the allOf', (t) => {
|
|
|
52
52
|
name: 'string'
|
|
53
53
|
})
|
|
54
54
|
} catch (e) {
|
|
55
|
-
t.is(e.message, 'id is required!')
|
|
55
|
+
t.is(e.message, '"id" is required!')
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
try {
|
|
@@ -135,7 +135,7 @@ test('object with nested allOfs', (t) => {
|
|
|
135
135
|
allOf: [
|
|
136
136
|
{
|
|
137
137
|
required: [
|
|
138
|
-
'
|
|
138
|
+
'id1'
|
|
139
139
|
],
|
|
140
140
|
type: 'object',
|
|
141
141
|
properties: {
|
|
@@ -184,3 +184,200 @@ test('object with nested allOfs', (t) => {
|
|
|
184
184
|
t.fail()
|
|
185
185
|
}
|
|
186
186
|
})
|
|
187
|
+
|
|
188
|
+
test('object with $ref in allOf', (t) => {
|
|
189
|
+
t.plan(1)
|
|
190
|
+
|
|
191
|
+
const schema = {
|
|
192
|
+
title: 'object with $ref in allOf',
|
|
193
|
+
type: 'object',
|
|
194
|
+
definitions: {
|
|
195
|
+
id1: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
id1: {
|
|
199
|
+
type: 'integer'
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
allOf: [
|
|
205
|
+
{
|
|
206
|
+
$ref: '#/definitions/id1'
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const stringify = build(schema)
|
|
213
|
+
try {
|
|
214
|
+
const value = stringify({
|
|
215
|
+
id1: 1,
|
|
216
|
+
id2: 2 // extra prop shouldn't be in result
|
|
217
|
+
})
|
|
218
|
+
t.is(value, '{"id1":1}')
|
|
219
|
+
} catch (e) {
|
|
220
|
+
t.fail()
|
|
221
|
+
}
|
|
222
|
+
} catch (e) {
|
|
223
|
+
t.fail()
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('object with $ref and other object in allOf', (t) => {
|
|
228
|
+
t.plan(1)
|
|
229
|
+
|
|
230
|
+
const schema = {
|
|
231
|
+
title: 'object with $ref in allOf',
|
|
232
|
+
type: 'object',
|
|
233
|
+
definitions: {
|
|
234
|
+
id1: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
properties: {
|
|
237
|
+
id1: {
|
|
238
|
+
type: 'integer'
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
allOf: [
|
|
244
|
+
{
|
|
245
|
+
$ref: '#/definitions/id1'
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: 'object',
|
|
249
|
+
properties: {
|
|
250
|
+
id2: {
|
|
251
|
+
type: 'integer'
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const stringify = build(schema)
|
|
260
|
+
try {
|
|
261
|
+
const value = stringify({
|
|
262
|
+
id1: 1,
|
|
263
|
+
id2: 2,
|
|
264
|
+
id3: 3 // extra prop shouldn't be in result
|
|
265
|
+
})
|
|
266
|
+
t.is(value, '{"id1":1,"id2":2}')
|
|
267
|
+
} catch (e) {
|
|
268
|
+
t.fail()
|
|
269
|
+
}
|
|
270
|
+
} catch (e) {
|
|
271
|
+
t.fail()
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
test('object with multiple $refs in allOf', (t) => {
|
|
276
|
+
t.plan(1)
|
|
277
|
+
|
|
278
|
+
const schema = {
|
|
279
|
+
title: 'object with $ref in allOf',
|
|
280
|
+
type: 'object',
|
|
281
|
+
definitions: {
|
|
282
|
+
id1: {
|
|
283
|
+
type: 'object',
|
|
284
|
+
properties: {
|
|
285
|
+
id1: {
|
|
286
|
+
type: 'integer'
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
id2: {
|
|
291
|
+
type: 'object',
|
|
292
|
+
properties: {
|
|
293
|
+
id2: {
|
|
294
|
+
type: 'integer'
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
allOf: [
|
|
300
|
+
{
|
|
301
|
+
$ref: '#/definitions/id1'
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
$ref: '#/definitions/id2'
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
const stringify = build(schema)
|
|
311
|
+
try {
|
|
312
|
+
const value = stringify({
|
|
313
|
+
id1: 1,
|
|
314
|
+
id2: 2,
|
|
315
|
+
id3: 3 // extra prop shouldn't be in result
|
|
316
|
+
})
|
|
317
|
+
t.is(value, '{"id1":1,"id2":2}')
|
|
318
|
+
} catch (e) {
|
|
319
|
+
t.fail()
|
|
320
|
+
}
|
|
321
|
+
} catch (e) {
|
|
322
|
+
t.fail()
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
test('object with external $refs in allOf', (t) => {
|
|
327
|
+
t.plan(1)
|
|
328
|
+
|
|
329
|
+
const externalSchema = {
|
|
330
|
+
first: {
|
|
331
|
+
definitions: {
|
|
332
|
+
id1: {
|
|
333
|
+
type: 'object',
|
|
334
|
+
properties: {
|
|
335
|
+
id1: {
|
|
336
|
+
type: 'integer'
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
second: {
|
|
343
|
+
id2: {
|
|
344
|
+
$id: '#id2',
|
|
345
|
+
type: 'object',
|
|
346
|
+
properties: {
|
|
347
|
+
id2: {
|
|
348
|
+
type: 'integer'
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const schema = {
|
|
356
|
+
title: 'object with $ref in allOf',
|
|
357
|
+
type: 'object',
|
|
358
|
+
allOf: [
|
|
359
|
+
{
|
|
360
|
+
$ref: 'first#/definitions/id1'
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
$ref: 'second#id2'
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
const stringify = build(schema, { schema: externalSchema })
|
|
370
|
+
try {
|
|
371
|
+
const value = stringify({
|
|
372
|
+
id1: 1,
|
|
373
|
+
id2: 2,
|
|
374
|
+
id3: 3 // extra prop shouldn't be in result
|
|
375
|
+
})
|
|
376
|
+
t.is(value, '{"id1":1,"id2":2}')
|
|
377
|
+
} catch (e) {
|
|
378
|
+
t.fail()
|
|
379
|
+
}
|
|
380
|
+
} catch (e) {
|
|
381
|
+
t.fail()
|
|
382
|
+
}
|
|
383
|
+
})
|
package/test/ref.test.js
CHANGED
|
@@ -820,7 +820,7 @@ test('ref external to relative definition', (t) => {
|
|
|
820
820
|
|
|
821
821
|
const schema = {
|
|
822
822
|
type: 'object',
|
|
823
|
-
required: ['
|
|
823
|
+
required: ['fooParent'],
|
|
824
824
|
properties: {
|
|
825
825
|
fooParent: { $ref: 'relative:to:local' }
|
|
826
826
|
}
|
|
@@ -934,7 +934,7 @@ test('Bad key', t => {
|
|
|
934
934
|
})
|
|
935
935
|
t.fail('Should throw')
|
|
936
936
|
} catch (err) {
|
|
937
|
-
t.is(err.message,
|
|
937
|
+
t.is(err.message, 'Cannot find reference "porjectId", did you mean "projectId"?')
|
|
938
938
|
}
|
|
939
939
|
})
|
|
940
940
|
|
|
@@ -959,7 +959,7 @@ test('Bad key', t => {
|
|
|
959
959
|
})
|
|
960
960
|
t.fail('Should throw')
|
|
961
961
|
} catch (err) {
|
|
962
|
-
t.is(err.message,
|
|
962
|
+
t.is(err.message, 'Cannot find reference "foobar"')
|
|
963
963
|
}
|
|
964
964
|
})
|
|
965
965
|
|
|
@@ -989,7 +989,7 @@ test('Bad key', t => {
|
|
|
989
989
|
})
|
|
990
990
|
t.fail('Should throw')
|
|
991
991
|
} catch (err) {
|
|
992
|
-
t.is(err.message,
|
|
992
|
+
t.is(err.message, 'Cannot find reference "porjectId", did you mean "projectId"?')
|
|
993
993
|
}
|
|
994
994
|
})
|
|
995
995
|
|
|
@@ -1019,7 +1019,7 @@ test('Bad key', t => {
|
|
|
1019
1019
|
})
|
|
1020
1020
|
t.fail('Should throw')
|
|
1021
1021
|
} catch (err) {
|
|
1022
|
-
t.is(err.message,
|
|
1022
|
+
t.is(err.message, 'Cannot find reference "foobar"')
|
|
1023
1023
|
}
|
|
1024
1024
|
})
|
|
1025
1025
|
|
|
@@ -1049,7 +1049,7 @@ test('Bad key', t => {
|
|
|
1049
1049
|
})
|
|
1050
1050
|
t.fail('Should throw')
|
|
1051
1051
|
} catch (err) {
|
|
1052
|
-
t.is(err.message,
|
|
1052
|
+
t.is(err.message, 'Cannot find reference "deifnitions", did you mean "definitions"?')
|
|
1053
1053
|
}
|
|
1054
1054
|
})
|
|
1055
1055
|
|
|
@@ -1074,7 +1074,7 @@ test('Bad key', t => {
|
|
|
1074
1074
|
})
|
|
1075
1075
|
t.fail('Should throw')
|
|
1076
1076
|
} catch (err) {
|
|
1077
|
-
t.is(err.message,
|
|
1077
|
+
t.is(err.message, 'Cannot find reference "deifnitions", did you mean "definitions"?')
|
|
1078
1078
|
}
|
|
1079
1079
|
})
|
|
1080
1080
|
|
|
@@ -1104,7 +1104,7 @@ test('Bad key', t => {
|
|
|
1104
1104
|
})
|
|
1105
1105
|
t.fail('Should throw')
|
|
1106
1106
|
} catch (err) {
|
|
1107
|
-
t.is(err.message,
|
|
1107
|
+
t.is(err.message, 'Cannot find reference "extrenal", did you mean "external"?')
|
|
1108
1108
|
}
|
|
1109
1109
|
})
|
|
1110
1110
|
|
package/test/required.test.js
CHANGED
|
@@ -36,11 +36,186 @@ test('object with required field', (t) => {
|
|
|
36
36
|
})
|
|
37
37
|
t.fail()
|
|
38
38
|
} catch (e) {
|
|
39
|
-
t.is(e.message, 'str is required!')
|
|
39
|
+
t.is(e.message, '"str" is required!')
|
|
40
40
|
t.pass()
|
|
41
41
|
}
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
+
test('object with required field not in properties schema', (t) => {
|
|
45
|
+
t.plan(4)
|
|
46
|
+
|
|
47
|
+
const schema = {
|
|
48
|
+
title: 'object with required field',
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
num: {
|
|
52
|
+
type: 'integer'
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
required: ['str']
|
|
56
|
+
}
|
|
57
|
+
const stringify = build(schema)
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
stringify({})
|
|
61
|
+
t.fail()
|
|
62
|
+
} catch (e) {
|
|
63
|
+
t.is(e.message, '"str" is required!')
|
|
64
|
+
t.pass()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
stringify({
|
|
69
|
+
num: 42
|
|
70
|
+
})
|
|
71
|
+
t.fail()
|
|
72
|
+
} catch (e) {
|
|
73
|
+
t.is(e.message, '"str" is required!')
|
|
74
|
+
t.pass()
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('object with required field not in properties schema with additional properties true', (t) => {
|
|
79
|
+
t.plan(4)
|
|
80
|
+
|
|
81
|
+
const schema = {
|
|
82
|
+
title: 'object with required field',
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
num: {
|
|
86
|
+
type: 'integer'
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
additionalProperties: true,
|
|
90
|
+
required: ['str']
|
|
91
|
+
}
|
|
92
|
+
const stringify = build(schema)
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
stringify({})
|
|
96
|
+
t.fail()
|
|
97
|
+
} catch (e) {
|
|
98
|
+
t.is(e.message, '"str" is required!')
|
|
99
|
+
t.pass()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
stringify({
|
|
104
|
+
num: 42
|
|
105
|
+
})
|
|
106
|
+
t.fail()
|
|
107
|
+
} catch (e) {
|
|
108
|
+
t.is(e.message, '"str" is required!')
|
|
109
|
+
t.pass()
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('object with multiple required field not in properties schema', (t) => {
|
|
114
|
+
t.plan(6)
|
|
115
|
+
|
|
116
|
+
const schema = {
|
|
117
|
+
title: 'object with required field',
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: {
|
|
120
|
+
num: {
|
|
121
|
+
type: 'integer'
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
additionalProperties: true,
|
|
125
|
+
required: ['num', 'key1', 'key2']
|
|
126
|
+
}
|
|
127
|
+
const stringify = build(schema)
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
stringify({})
|
|
131
|
+
t.fail()
|
|
132
|
+
} catch (e) {
|
|
133
|
+
t.is(e.message, '"num" is required!')
|
|
134
|
+
t.pass()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
stringify({
|
|
139
|
+
num: 42
|
|
140
|
+
})
|
|
141
|
+
t.fail()
|
|
142
|
+
} catch (e) {
|
|
143
|
+
t.is(e.message, '"key1" is required!')
|
|
144
|
+
t.pass()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
stringify({
|
|
149
|
+
num: 42,
|
|
150
|
+
key1: 'some'
|
|
151
|
+
})
|
|
152
|
+
t.fail()
|
|
153
|
+
} catch (e) {
|
|
154
|
+
t.is(e.message, '"key2" is required!')
|
|
155
|
+
t.pass()
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('object with required bool', (t) => {
|
|
160
|
+
t.plan(3)
|
|
161
|
+
|
|
162
|
+
const schema = {
|
|
163
|
+
title: 'object with required field',
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
num: {
|
|
167
|
+
type: 'integer'
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
additionalProperties: true,
|
|
171
|
+
required: ['bool']
|
|
172
|
+
}
|
|
173
|
+
const stringify = build(schema)
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
stringify({})
|
|
177
|
+
t.fail()
|
|
178
|
+
} catch (e) {
|
|
179
|
+
t.is(e.message, '"bool" is required!')
|
|
180
|
+
t.pass()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
stringify({
|
|
185
|
+
bool: false
|
|
186
|
+
})
|
|
187
|
+
t.pass()
|
|
188
|
+
} catch (e) {
|
|
189
|
+
t.fail(e.message)
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('required nullable', (t) => {
|
|
194
|
+
t.plan(1)
|
|
195
|
+
|
|
196
|
+
const schema = {
|
|
197
|
+
title: 'object with required field',
|
|
198
|
+
type: 'object',
|
|
199
|
+
properties: {
|
|
200
|
+
num: {
|
|
201
|
+
type: ['integer']
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
additionalProperties: true,
|
|
205
|
+
required: ['null']
|
|
206
|
+
}
|
|
207
|
+
const stringify = build(schema)
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
stringify({
|
|
211
|
+
null: null
|
|
212
|
+
})
|
|
213
|
+
t.pass()
|
|
214
|
+
} catch (e) {
|
|
215
|
+
t.fail(e.message)
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
|
|
44
219
|
test('required numbers', (t) => {
|
|
45
220
|
t.plan(3)
|
|
46
221
|
|
|
@@ -74,7 +249,7 @@ test('required numbers', (t) => {
|
|
|
74
249
|
})
|
|
75
250
|
t.fail()
|
|
76
251
|
} catch (e) {
|
|
77
|
-
t.is(e.message, 'num is required!')
|
|
252
|
+
t.is(e.message, '"num" is required!')
|
|
78
253
|
t.pass()
|
|
79
254
|
}
|
|
80
255
|
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const build = require('..')
|
|
5
|
+
|
|
6
|
+
const payload = '(throw "pwoned")'
|
|
7
|
+
|
|
8
|
+
const stringify = build({
|
|
9
|
+
properties: {
|
|
10
|
+
[`*///\\\\\\']);${payload};{/*`]: {
|
|
11
|
+
type: 'number'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
stringify({})
|
|
17
|
+
|
|
18
|
+
t.pass('no crashes')
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const build = require('..')
|
|
5
|
+
|
|
6
|
+
const stringify = build({
|
|
7
|
+
$defs: {
|
|
8
|
+
type: 'foooo"bar'
|
|
9
|
+
},
|
|
10
|
+
patternProperties: {
|
|
11
|
+
x: { $ref: '#/$defs' }
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
t.throws(() => {
|
|
16
|
+
stringify({ x: 0 })
|
|
17
|
+
}, 'Cannot coerce 0 to "foo"bar"')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const build = require('..')
|
|
5
|
+
|
|
6
|
+
const payload = '(throw "pwoned")'
|
|
7
|
+
|
|
8
|
+
const stringify = build({
|
|
9
|
+
required: [`"];${payload}//`]
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
t.throws(() => {
|
|
13
|
+
stringify({})
|
|
14
|
+
}, 'Error: ""];(throw "pwoned")//" is required!')
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const build = require('..')
|
|
5
|
+
|
|
6
|
+
const payload = '(throw "pwoned")'
|
|
7
|
+
|
|
8
|
+
const expected = 'Error: Invalid regular expression: /*/: Nothing to repeat. Found at * matching {"type":"*/(throw \\"pwoned\\")){//"}'
|
|
9
|
+
|
|
10
|
+
t.throws(() => {
|
|
11
|
+
build({
|
|
12
|
+
patternProperties: {
|
|
13
|
+
'*': { type: `*/${payload}){//` }
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
}, expected)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const build = require('..')
|
|
5
|
+
|
|
6
|
+
const payload = '(throw "pwoned")'
|
|
7
|
+
|
|
8
|
+
const stringify = build({
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
'/*': { type: 'object' },
|
|
12
|
+
x: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
a: { type: 'string', default: `*/}${payload};{//` }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
stringify({})
|
|
21
|
+
|
|
22
|
+
t.pass('no crashes')
|