fast-json-stringify 1.19.0 → 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/README.md +25 -0
- package/index.js +109 -49
- package/package.json +1 -1
- package/test/allof.test.js +200 -3
- package/test/array.test.js +15 -0
- package/test/basic.test.js +19 -0
- package/test/date.test.js +208 -2
- package/test/debug-mode.test.js +65 -0
- 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/README.md
CHANGED
|
@@ -578,6 +578,31 @@ allowing user input to directly supply a schema.
|
|
|
578
578
|
It can't be guaranteed that allowing user input for the schema couldn't feasibly expose an attack
|
|
579
579
|
vector.
|
|
580
580
|
|
|
581
|
+
<a name="debug"></a>
|
|
582
|
+
### Debug Mode
|
|
583
|
+
|
|
584
|
+
The debug mode can be activated during your development to understand what is going on when things do not
|
|
585
|
+
work as you expect.
|
|
586
|
+
|
|
587
|
+
```js
|
|
588
|
+
const debugCompiled = fastJson({
|
|
589
|
+
title: 'default string',
|
|
590
|
+
type: 'object',
|
|
591
|
+
properties: {
|
|
592
|
+
firstName: {
|
|
593
|
+
type: 'string'
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}, { debugMode: true })
|
|
597
|
+
|
|
598
|
+
console.log(debugCompiled) // it is an array of functions that can create your `stringify` function
|
|
599
|
+
console.log(debugCompiled.toString()) // print a "ready to read" string function, you can save it to a file
|
|
600
|
+
|
|
601
|
+
const rawString = debugCompiled.toString()
|
|
602
|
+
const stringify = fastJson.restore(rawString) // use the generated string to get back the `stringify` function
|
|
603
|
+
console.log(stringify({ firstName: 'Foo', surname: 'bar' })) // '{"firstName":"Foo"}'
|
|
604
|
+
```
|
|
605
|
+
|
|
581
606
|
<a name="acknowledgements"></a>
|
|
582
607
|
## Acknowledgements
|
|
583
608
|
|
package/index.js
CHANGED
|
@@ -138,6 +138,14 @@ function build (schema, options) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
dependenciesName.push(code)
|
|
141
|
+
|
|
142
|
+
if (options.debugMode) {
|
|
143
|
+
dependenciesName.toString = function () {
|
|
144
|
+
return dependenciesName.join('\n')
|
|
145
|
+
}
|
|
146
|
+
return dependenciesName
|
|
147
|
+
}
|
|
148
|
+
|
|
141
149
|
return (Function.apply(null, dependenciesName).apply(null, dependencies))
|
|
142
150
|
}
|
|
143
151
|
|
|
@@ -271,7 +279,7 @@ function $asBooleanNullable (bool) {
|
|
|
271
279
|
function $asDatetime (date) {
|
|
272
280
|
if (date instanceof Date) {
|
|
273
281
|
return '"' + date.toISOString() + '"'
|
|
274
|
-
} else if (typeof date.toISOString === 'function') {
|
|
282
|
+
} else if (date && typeof date.toISOString === 'function') {
|
|
275
283
|
return '"' + date.toISOString() + '"'
|
|
276
284
|
} else {
|
|
277
285
|
return $asString(date)
|
|
@@ -284,7 +292,7 @@ function $asDate (date) {
|
|
|
284
292
|
var month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(date)
|
|
285
293
|
var day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date)
|
|
286
294
|
return '"' + year + '-' + month + '-' + day + '"'
|
|
287
|
-
} else if (typeof date.format === 'function') {
|
|
295
|
+
} else if (date && typeof date.format === 'function') {
|
|
288
296
|
return '"' + date.format('YYYY-MM-DD') + '"'
|
|
289
297
|
} else {
|
|
290
298
|
return $asString(date)
|
|
@@ -297,7 +305,7 @@ function $asTime (date) {
|
|
|
297
305
|
var minute = new Intl.DateTimeFormat('en', { minute: 'numeric' }).format(date)
|
|
298
306
|
var second = new Intl.DateTimeFormat('en', { second: 'numeric' }).format(date)
|
|
299
307
|
return '"' + $pad2Zeros(hour) + ':' + $pad2Zeros(minute) + ':' + $pad2Zeros(second) + '"'
|
|
300
|
-
} else if (typeof date.format === 'function') {
|
|
308
|
+
} else if (date && typeof date.format === 'function') {
|
|
301
309
|
return '"' + date.format('HH:mm:ss') + '"'
|
|
302
310
|
} else {
|
|
303
311
|
return $asString(date)
|
|
@@ -375,6 +383,11 @@ function addPatternProperties (schema, externalSchema, fullSchema) {
|
|
|
375
383
|
var type = pp[regex].type
|
|
376
384
|
var format = pp[regex].format
|
|
377
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
|
+
}
|
|
378
391
|
code += `
|
|
379
392
|
if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {
|
|
380
393
|
`
|
|
@@ -417,7 +430,7 @@ function addPatternProperties (schema, externalSchema, fullSchema) {
|
|
|
417
430
|
`
|
|
418
431
|
} else {
|
|
419
432
|
code += `
|
|
420
|
-
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ${type}
|
|
433
|
+
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
|
|
421
434
|
`
|
|
422
435
|
}
|
|
423
436
|
|
|
@@ -510,7 +523,7 @@ function additionalProperty (schema, externalSchema, fullSchema) {
|
|
|
510
523
|
`
|
|
511
524
|
} else {
|
|
512
525
|
code += `
|
|
513
|
-
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ${type}
|
|
526
|
+
throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
|
|
514
527
|
`
|
|
515
528
|
}
|
|
516
529
|
return code
|
|
@@ -579,7 +592,7 @@ function refFinder (ref, schema, externalSchema) {
|
|
|
579
592
|
return dereferenced
|
|
580
593
|
} else {
|
|
581
594
|
for (var i = 1; i < walk.length; i++) {
|
|
582
|
-
code += `[
|
|
595
|
+
code += `[${JSON.stringify(walk[i])}]`
|
|
583
596
|
}
|
|
584
597
|
}
|
|
585
598
|
}
|
|
@@ -595,35 +608,27 @@ function refFinder (ref, schema, externalSchema) {
|
|
|
595
608
|
|
|
596
609
|
function findBadKey (obj, keys) {
|
|
597
610
|
if (keys.length === 0) return null
|
|
598
|
-
const key =
|
|
611
|
+
const key = keys.shift()
|
|
599
612
|
if (obj[key] === undefined) {
|
|
600
613
|
stringSimilarity = stringSimilarity || require('string-similarity')
|
|
601
614
|
const { bestMatch } = stringSimilarity.findBestMatch(key, Object.keys(obj))
|
|
602
615
|
if (bestMatch.rating >= 0.5) {
|
|
603
|
-
throw new Error(`Cannot find reference
|
|
616
|
+
throw new Error(`Cannot find reference ${JSON.stringify(key)}, did you mean ${JSON.stringify(bestMatch.target)}?`)
|
|
604
617
|
} else {
|
|
605
|
-
throw new Error(`Cannot find reference
|
|
618
|
+
throw new Error(`Cannot find reference ${JSON.stringify(key)}`)
|
|
606
619
|
}
|
|
607
620
|
}
|
|
608
621
|
return findBadKey(obj[key], keys)
|
|
609
622
|
}
|
|
610
623
|
}
|
|
611
624
|
|
|
612
|
-
function sanitizeKey (key) {
|
|
613
|
-
const rep = key.replace(/(\\*)'/g, function (match, p1) {
|
|
614
|
-
var base = ''
|
|
615
|
-
if (p1.length % 2 === 1) {
|
|
616
|
-
base = p1.slice(2)
|
|
617
|
-
} else {
|
|
618
|
-
base = p1
|
|
619
|
-
}
|
|
620
|
-
var rep = base + '\\\''
|
|
621
|
-
return rep
|
|
622
|
-
})
|
|
623
|
-
return rep
|
|
624
|
-
}
|
|
625
|
-
|
|
626
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
|
+
|
|
627
632
|
Object.keys(schema.properties || {}).forEach((key, i, a) => {
|
|
628
633
|
if (schema.properties[key].$ref) {
|
|
629
634
|
// if the schema object is deep in the tree, we must resolve the ref in the parent scope
|
|
@@ -636,14 +641,14 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
636
641
|
|
|
637
642
|
var type = schema.properties[key].type
|
|
638
643
|
var nullable = schema.properties[key].nullable
|
|
639
|
-
var sanitized =
|
|
640
|
-
var asString =
|
|
644
|
+
var sanitized = JSON.stringify(key)
|
|
645
|
+
var asString = JSON.stringify(sanitized)
|
|
641
646
|
|
|
642
647
|
if (nullable) {
|
|
643
648
|
code += `
|
|
644
|
-
if (obj[
|
|
649
|
+
if (obj[${sanitized}] === null) {
|
|
645
650
|
${addComma}
|
|
646
|
-
json +=
|
|
651
|
+
json += ${asString} + ':null'
|
|
647
652
|
var rendered = true
|
|
648
653
|
} else {
|
|
649
654
|
`
|
|
@@ -651,10 +656,10 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
651
656
|
|
|
652
657
|
if (type === 'number') {
|
|
653
658
|
code += `
|
|
654
|
-
var t = Number(obj[
|
|
659
|
+
var t = Number(obj[${sanitized}])
|
|
655
660
|
if (!isNaN(t)) {
|
|
656
661
|
${addComma}
|
|
657
|
-
json +=
|
|
662
|
+
json += ${asString} + ':' + t
|
|
658
663
|
`
|
|
659
664
|
} else if (type === 'integer') {
|
|
660
665
|
code += `
|
|
@@ -662,25 +667,25 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
662
667
|
`
|
|
663
668
|
if (isLong) {
|
|
664
669
|
code += `
|
|
665
|
-
if (isLong(obj[
|
|
670
|
+
if (isLong(obj[${sanitized}])) {
|
|
666
671
|
${addComma}
|
|
667
|
-
json +=
|
|
672
|
+
json += ${asString} + ':' + obj[${sanitized}].toString()
|
|
668
673
|
rendered = true
|
|
669
674
|
} else {
|
|
670
|
-
var t = Number(obj[
|
|
675
|
+
var t = Number(obj[${sanitized}])
|
|
671
676
|
if (!isNaN(t)) {
|
|
672
677
|
${addComma}
|
|
673
|
-
json +=
|
|
678
|
+
json += ${asString} + ':' + t
|
|
674
679
|
rendered = true
|
|
675
680
|
}
|
|
676
681
|
}
|
|
677
682
|
`
|
|
678
683
|
} else {
|
|
679
684
|
code += `
|
|
680
|
-
var t = Number(obj[
|
|
685
|
+
var t = Number(obj[${sanitized}])
|
|
681
686
|
if (!isNaN(t)) {
|
|
682
687
|
${addComma}
|
|
683
|
-
json +=
|
|
688
|
+
json += ${asString} + ':' + t
|
|
684
689
|
rendered = true
|
|
685
690
|
}
|
|
686
691
|
`
|
|
@@ -690,24 +695,26 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
690
695
|
`
|
|
691
696
|
} else {
|
|
692
697
|
code += `
|
|
693
|
-
if (obj[
|
|
698
|
+
if (obj[${sanitized}] !== undefined) {
|
|
694
699
|
${addComma}
|
|
695
|
-
json +=
|
|
700
|
+
json += ${asString} + ':'
|
|
696
701
|
`
|
|
697
702
|
|
|
698
|
-
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)
|
|
699
704
|
code += result.code
|
|
700
705
|
laterCode = result.laterCode
|
|
701
706
|
}
|
|
702
707
|
|
|
703
708
|
var defaultValue = schema.properties[key].default
|
|
704
709
|
if (defaultValue !== undefined) {
|
|
710
|
+
required = filterRequired(schema.required, key)
|
|
705
711
|
code += `
|
|
706
712
|
} else {
|
|
707
713
|
${addComma}
|
|
708
|
-
json +=
|
|
714
|
+
json += ${asString} + ':' + ${JSON.stringify(JSON.stringify(defaultValue))}
|
|
709
715
|
`
|
|
710
716
|
} else if (schema.required && schema.required.indexOf(key) !== -1) {
|
|
717
|
+
required = filterRequired(schema.required, key)
|
|
711
718
|
code += `
|
|
712
719
|
} else {
|
|
713
720
|
throw new Error('${sanitized} is required!')
|
|
@@ -724,9 +731,33 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
|
724
731
|
`
|
|
725
732
|
}
|
|
726
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
|
+
|
|
727
751
|
return { code: code, laterCode: laterCode }
|
|
728
752
|
}
|
|
729
753
|
|
|
754
|
+
function filterRequired (required, key) {
|
|
755
|
+
if (!required) {
|
|
756
|
+
return required
|
|
757
|
+
}
|
|
758
|
+
return required.filter(k => k !== key)
|
|
759
|
+
}
|
|
760
|
+
|
|
730
761
|
function buildCodeWithAllOfs (schema, code, laterCode, name, externalSchema, fullSchema) {
|
|
731
762
|
if (schema.allOf) {
|
|
732
763
|
schema.allOf.forEach((ss) => {
|
|
@@ -882,7 +913,7 @@ function buildArray (schema, code, name, externalSchema, fullSchema) {
|
|
|
882
913
|
if (Array.isArray(schema.items)) {
|
|
883
914
|
result = schema.items.reduce((res, item, i) => {
|
|
884
915
|
var accessor = '[i]'
|
|
885
|
-
const tmpRes = nested(laterCode, name, accessor, item, externalSchema, fullSchema, i)
|
|
916
|
+
const tmpRes = nested(laterCode, name, accessor, item, externalSchema, fullSchema, i, true)
|
|
886
917
|
var condition = `i === ${i} && ${buildArrayTypeCondition(item.type, accessor)}`
|
|
887
918
|
return {
|
|
888
919
|
code: `${res.code}
|
|
@@ -899,7 +930,7 @@ function buildArray (schema, code, name, externalSchema, fullSchema) {
|
|
|
899
930
|
}
|
|
900
931
|
`
|
|
901
932
|
} else {
|
|
902
|
-
result = nested(laterCode, name, '[i]', schema.items, externalSchema, fullSchema)
|
|
933
|
+
result = nested(laterCode, name, '[i]', schema.items, externalSchema, fullSchema, undefined, true)
|
|
903
934
|
}
|
|
904
935
|
|
|
905
936
|
code += `
|
|
@@ -973,7 +1004,21 @@ function dereferenceOfRefs (schema, externalSchema, fullSchema, type) {
|
|
|
973
1004
|
})
|
|
974
1005
|
}
|
|
975
1006
|
|
|
976
|
-
|
|
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) {
|
|
977
1022
|
var code = ''
|
|
978
1023
|
var funcName
|
|
979
1024
|
|
|
@@ -993,7 +1038,8 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
993
1038
|
var type = schema.type
|
|
994
1039
|
var nullable = schema.nullable === true
|
|
995
1040
|
|
|
996
|
-
var accessor =
|
|
1041
|
+
var accessor = isArray ? key : `[${JSON.stringify(key)}]`
|
|
1042
|
+
|
|
997
1043
|
switch (type) {
|
|
998
1044
|
case 'null':
|
|
999
1045
|
code += `
|
|
@@ -1014,14 +1060,14 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1014
1060
|
code += nullable ? `json += obj${accessor} === null ? null : $asBoolean(obj${accessor})` : `json += $asBoolean(obj${accessor})`
|
|
1015
1061
|
break
|
|
1016
1062
|
case 'object':
|
|
1017
|
-
funcName = (name + key + subKey)
|
|
1063
|
+
funcName = asFuncName(name + key + subKey)
|
|
1018
1064
|
laterCode = buildObject(schema, laterCode, funcName, externalSchema, fullSchema)
|
|
1019
1065
|
code += `
|
|
1020
1066
|
json += ${funcName}(obj${accessor})
|
|
1021
1067
|
`
|
|
1022
1068
|
break
|
|
1023
1069
|
case 'array':
|
|
1024
|
-
funcName = '$arr' +
|
|
1070
|
+
funcName = asFuncName('$arr' + name + key + subKey) // eslint-disable-line
|
|
1025
1071
|
laterCode = buildArray(schema, laterCode, funcName, externalSchema, fullSchema)
|
|
1026
1072
|
code += `
|
|
1027
1073
|
json += ${funcName}(obj${accessor})
|
|
@@ -1031,7 +1077,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1031
1077
|
if ('anyOf' in schema) {
|
|
1032
1078
|
dereferenceOfRefs(schema, externalSchema, fullSchema, 'anyOf')
|
|
1033
1079
|
schema.anyOf.forEach((s, index) => {
|
|
1034
|
-
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)
|
|
1035
1081
|
code += `
|
|
1036
1082
|
${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(s, { depth: null })}, obj${accessor}))
|
|
1037
1083
|
${nestedResult.code}
|
|
@@ -1044,7 +1090,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1044
1090
|
} else if ('oneOf' in schema) {
|
|
1045
1091
|
dereferenceOfRefs(schema, externalSchema, fullSchema, 'oneOf')
|
|
1046
1092
|
schema.oneOf.forEach((s, index) => {
|
|
1047
|
-
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)
|
|
1048
1094
|
code += `
|
|
1049
1095
|
${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(s, { depth: null })}, obj${accessor}))
|
|
1050
1096
|
${nestedResult.code}
|
|
@@ -1068,10 +1114,11 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
|
|
|
1068
1114
|
const sortedTypes = nullIndex !== -1 ? [type[nullIndex]].concat(type.slice(0, nullIndex)).concat(type.slice(nullIndex + 1)) : type
|
|
1069
1115
|
sortedTypes.forEach((type, index) => {
|
|
1070
1116
|
var tempSchema = Object.assign({}, schema, { type })
|
|
1071
|
-
var nestedResult = nested(laterCode, name, key, tempSchema, externalSchema, fullSchema, subKey)
|
|
1117
|
+
var nestedResult = nested(laterCode, name, key, tempSchema, externalSchema, fullSchema, subKey, isArray)
|
|
1118
|
+
|
|
1072
1119
|
if (type === 'string') {
|
|
1073
1120
|
code += `
|
|
1074
|
-
${index === 0 ? 'if' : 'else if'}(typeof obj${accessor} === "${type}" || obj${accessor} instanceof Date || typeof obj${accessor}.toISOString === "function" || obj${accessor} instanceof RegExp)
|
|
1121
|
+
${index === 0 ? 'if' : 'else if'}(obj${accessor} === null || typeof obj${accessor} === "${type}" || obj${accessor} instanceof Date || typeof obj${accessor}.toISOString === "function" || obj${accessor} instanceof RegExp)
|
|
1075
1122
|
${nestedResult.code}
|
|
1076
1123
|
`
|
|
1077
1124
|
} else if (type === 'null') {
|
|
@@ -1156,3 +1203,16 @@ function isEmpty (schema) {
|
|
|
1156
1203
|
}
|
|
1157
1204
|
|
|
1158
1205
|
module.exports = build
|
|
1206
|
+
|
|
1207
|
+
module.exports.restore = function (debugModeStr, options = {}) {
|
|
1208
|
+
const dependencies = [debugModeStr]
|
|
1209
|
+
const args = []
|
|
1210
|
+
if (debugModeStr.startsWith('ajv')) {
|
|
1211
|
+
dependencies.unshift('ajv')
|
|
1212
|
+
args.push(new Ajv(options.ajv))
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// eslint-disable-next-line
|
|
1216
|
+
return (Function.apply(null, ['ajv', debugModeStr])
|
|
1217
|
+
.apply(null, args))
|
|
1218
|
+
}
|
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/array.test.js
CHANGED
|
@@ -142,6 +142,21 @@ buildTest({
|
|
|
142
142
|
}
|
|
143
143
|
}, { args: [{ a: 'test' }, { b: 1 }] })
|
|
144
144
|
|
|
145
|
+
buildTest({
|
|
146
|
+
title: 'array with weird key',
|
|
147
|
+
type: 'object',
|
|
148
|
+
properties: {
|
|
149
|
+
'@data': {
|
|
150
|
+
type: 'array',
|
|
151
|
+
items: {
|
|
152
|
+
type: 'string'
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}, {
|
|
157
|
+
'@data': ['test']
|
|
158
|
+
})
|
|
159
|
+
|
|
145
160
|
test('invalid items throw', (t) => {
|
|
146
161
|
t.plan(1)
|
|
147
162
|
const schema = {
|
package/test/basic.test.js
CHANGED
|
@@ -129,6 +129,25 @@ buildTest({
|
|
|
129
129
|
'@version': 1
|
|
130
130
|
})
|
|
131
131
|
|
|
132
|
+
buildTest({
|
|
133
|
+
title: 'deep object with weird keys of type object',
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
'@data': {
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
id: {
|
|
140
|
+
type: 'string'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}, {
|
|
146
|
+
'@data': {
|
|
147
|
+
id: 'string'
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
132
151
|
buildTest({
|
|
133
152
|
title: 'deep object with spaces in key',
|
|
134
153
|
type: 'object',
|
package/test/date.test.js
CHANGED
|
@@ -91,7 +91,6 @@ test('render a date in a string when format is time as HH:mm:ss', (t) => {
|
|
|
91
91
|
const output = stringify(toStringify)
|
|
92
92
|
|
|
93
93
|
validate(JSON.parse(output))
|
|
94
|
-
console.log(validate.errors)
|
|
95
94
|
|
|
96
95
|
t.equal(output, `"${moment(toStringify).format('HH:mm:ss')}"`)
|
|
97
96
|
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
@@ -112,7 +111,6 @@ test('verify padding for rendered date in a string when format is time', (t) =>
|
|
|
112
111
|
const output = stringify(toStringify)
|
|
113
112
|
|
|
114
113
|
validate(JSON.parse(output))
|
|
115
|
-
console.log(validate.errors)
|
|
116
114
|
|
|
117
115
|
t.equal(output, `"${moment(toStringify).format('HH:mm:ss')}"`)
|
|
118
116
|
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
@@ -194,3 +192,211 @@ test('render a nested object in a string when type is date-format as ISOString',
|
|
|
194
192
|
t.equal(output, JSON.stringify(toStringify))
|
|
195
193
|
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
196
194
|
})
|
|
195
|
+
|
|
196
|
+
test('serializing null value', t => {
|
|
197
|
+
const input = { updatedAt: null }
|
|
198
|
+
|
|
199
|
+
function createSchema (properties) {
|
|
200
|
+
return {
|
|
201
|
+
title: 'an object in a string',
|
|
202
|
+
type: 'object',
|
|
203
|
+
properties
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function serialize (schema, input) {
|
|
208
|
+
const validate = validator(schema)
|
|
209
|
+
const stringify = build(schema)
|
|
210
|
+
const output = stringify(input)
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
validate,
|
|
214
|
+
output
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
t.plan(3)
|
|
219
|
+
|
|
220
|
+
t.test('type::string', t => {
|
|
221
|
+
t.plan(3)
|
|
222
|
+
|
|
223
|
+
t.test('format::date-time', t => {
|
|
224
|
+
t.plan(2)
|
|
225
|
+
|
|
226
|
+
const prop = {
|
|
227
|
+
updatedAt: {
|
|
228
|
+
type: 'string',
|
|
229
|
+
format: 'date-time'
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const {
|
|
234
|
+
output,
|
|
235
|
+
validate
|
|
236
|
+
} = serialize(createSchema(prop), input)
|
|
237
|
+
|
|
238
|
+
t.equal(output, '{"updatedAt":""}')
|
|
239
|
+
t.notOk(validate(JSON.parse(output)), 'an empty string is not a date-time format')
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
t.test('format::date', t => {
|
|
243
|
+
t.plan(2)
|
|
244
|
+
|
|
245
|
+
const prop = {
|
|
246
|
+
updatedAt: {
|
|
247
|
+
type: 'string',
|
|
248
|
+
format: 'date'
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const {
|
|
253
|
+
output,
|
|
254
|
+
validate
|
|
255
|
+
} = serialize(createSchema(prop), input)
|
|
256
|
+
|
|
257
|
+
t.equal(output, '{"updatedAt":""}')
|
|
258
|
+
t.notOk(validate(JSON.parse(output)), 'an empty string is not a date format')
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
t.test('format::time', t => {
|
|
262
|
+
t.plan(2)
|
|
263
|
+
|
|
264
|
+
const prop = {
|
|
265
|
+
updatedAt: {
|
|
266
|
+
type: 'string',
|
|
267
|
+
format: 'time'
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const {
|
|
272
|
+
output,
|
|
273
|
+
validate
|
|
274
|
+
} = serialize(createSchema(prop), input)
|
|
275
|
+
|
|
276
|
+
t.equal(output, '{"updatedAt":""}')
|
|
277
|
+
t.notOk(validate(JSON.parse(output)), 'an empty string is not a time format')
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
t.test('type::array', t => {
|
|
282
|
+
t.plan(3)
|
|
283
|
+
|
|
284
|
+
t.test('format::date-time', t => {
|
|
285
|
+
t.plan(2)
|
|
286
|
+
|
|
287
|
+
const prop = {
|
|
288
|
+
updatedAt: {
|
|
289
|
+
type: ['string'],
|
|
290
|
+
format: 'date-time'
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const {
|
|
295
|
+
output,
|
|
296
|
+
validate
|
|
297
|
+
} = serialize(createSchema(prop), input)
|
|
298
|
+
|
|
299
|
+
t.equal(output, '{"updatedAt":""}')
|
|
300
|
+
t.notOk(validate(JSON.parse(output)), 'an empty string is not a date-time format')
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
t.test('format::date', t => {
|
|
304
|
+
t.plan(2)
|
|
305
|
+
|
|
306
|
+
const prop = {
|
|
307
|
+
updatedAt: {
|
|
308
|
+
type: ['string'],
|
|
309
|
+
format: 'date'
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const {
|
|
314
|
+
output,
|
|
315
|
+
validate
|
|
316
|
+
} = serialize(createSchema(prop), input)
|
|
317
|
+
|
|
318
|
+
t.equal(output, '{"updatedAt":""}')
|
|
319
|
+
t.notOk(validate(JSON.parse(output)), 'an empty string is not a date format')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
t.test('format::time', t => {
|
|
323
|
+
t.plan(2)
|
|
324
|
+
|
|
325
|
+
const prop = {
|
|
326
|
+
updatedAt: {
|
|
327
|
+
type: ['string'],
|
|
328
|
+
format: 'time'
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const {
|
|
333
|
+
output,
|
|
334
|
+
validate
|
|
335
|
+
} = serialize(createSchema(prop), input)
|
|
336
|
+
|
|
337
|
+
t.equal(output, '{"updatedAt":""}')
|
|
338
|
+
t.notOk(validate(JSON.parse(output)), 'an empty string is not a time format')
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
t.test('type::array::nullable', t => {
|
|
343
|
+
t.plan(3)
|
|
344
|
+
|
|
345
|
+
t.test('format::date-time', t => {
|
|
346
|
+
t.plan(2)
|
|
347
|
+
|
|
348
|
+
const prop = {
|
|
349
|
+
updatedAt: {
|
|
350
|
+
type: ['string', 'null'],
|
|
351
|
+
format: 'date-time'
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const {
|
|
356
|
+
output,
|
|
357
|
+
validate
|
|
358
|
+
} = serialize(createSchema(prop), input)
|
|
359
|
+
|
|
360
|
+
t.equal(output, '{"updatedAt":null}')
|
|
361
|
+
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
t.test('format::date', t => {
|
|
365
|
+
t.plan(2)
|
|
366
|
+
|
|
367
|
+
const prop = {
|
|
368
|
+
updatedAt: {
|
|
369
|
+
type: ['string', 'null'],
|
|
370
|
+
format: 'date'
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const {
|
|
375
|
+
output,
|
|
376
|
+
validate
|
|
377
|
+
} = serialize(createSchema(prop), input)
|
|
378
|
+
|
|
379
|
+
t.equal(output, '{"updatedAt":null}')
|
|
380
|
+
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
t.test('format::time', t => {
|
|
384
|
+
t.plan(2)
|
|
385
|
+
|
|
386
|
+
const prop = {
|
|
387
|
+
updatedAt: {
|
|
388
|
+
type: ['string', 'null'],
|
|
389
|
+
format: 'time'
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const {
|
|
394
|
+
output,
|
|
395
|
+
validate
|
|
396
|
+
} = serialize(createSchema(prop), input)
|
|
397
|
+
|
|
398
|
+
t.equal(output, '{"updatedAt":null}')
|
|
399
|
+
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
400
|
+
})
|
|
401
|
+
})
|
|
402
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const test = require('tap').test
|
|
4
|
+
const fjs = require('..')
|
|
5
|
+
|
|
6
|
+
function build (opts) {
|
|
7
|
+
return fjs({
|
|
8
|
+
title: 'default string',
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
firstName: {
|
|
12
|
+
type: 'string'
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
required: ['firstName']
|
|
16
|
+
}, opts)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test('activate debug mode', t => {
|
|
20
|
+
t.plan(2)
|
|
21
|
+
const debugMode = build({ debugMode: true })
|
|
22
|
+
t.type(debugMode, Array)
|
|
23
|
+
t.like(debugMode.toString.toString(), 'join', 'to string override')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('activate debug mode truthy', t => {
|
|
27
|
+
t.plan(2)
|
|
28
|
+
const debugMode = build({ debugMode: 'yes' })
|
|
29
|
+
t.type(debugMode, Array)
|
|
30
|
+
t.like(debugMode.toString.toString(), 'join', 'to string override')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('to string auto-consistent', t => {
|
|
34
|
+
t.plan(2)
|
|
35
|
+
const debugMode = build({ debugMode: 1 })
|
|
36
|
+
t.type(debugMode, Array)
|
|
37
|
+
|
|
38
|
+
const str = debugMode.toString()
|
|
39
|
+
const compiled = fjs.restore(str)
|
|
40
|
+
const tobe = JSON.stringify({ firstName: 'Foo' })
|
|
41
|
+
t.deepEquals(compiled({ firstName: 'Foo', surname: 'bar' }), tobe, 'surname evicted')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('to string auto-consistent with ajv', t => {
|
|
45
|
+
t.plan(2)
|
|
46
|
+
const debugMode = fjs({
|
|
47
|
+
title: 'object with multiple types field',
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
str: {
|
|
51
|
+
anyOf: [{
|
|
52
|
+
type: 'string'
|
|
53
|
+
}, {
|
|
54
|
+
type: 'boolean'
|
|
55
|
+
}]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}, { debugMode: 1 })
|
|
59
|
+
t.type(debugMode, Array)
|
|
60
|
+
|
|
61
|
+
const str = debugMode.toString()
|
|
62
|
+
const compiled = fjs.restore(str)
|
|
63
|
+
const tobe = JSON.stringify({ str: 'Foo' })
|
|
64
|
+
t.deepEquals(compiled({ str: 'Foo', void: 'me' }), tobe)
|
|
65
|
+
})
|
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')
|