fast-json-stringify 5.14.1 → 5.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +62 -49
- package/lib/serializer.js +41 -56
- package/package.json +1 -1
- package/test/sanitize7.test.js +68 -0
package/index.js
CHANGED
|
@@ -140,7 +140,19 @@ function build (schema, options) {
|
|
|
140
140
|
const location = new Location(schema, context.rootSchemaId)
|
|
141
141
|
const code = buildValue(context, location, 'input')
|
|
142
142
|
|
|
143
|
-
let contextFunctionCode
|
|
143
|
+
let contextFunctionCode = `
|
|
144
|
+
const JSON_STR_BEGIN_OBJECT = '{'
|
|
145
|
+
const JSON_STR_END_OBJECT = '}'
|
|
146
|
+
const JSON_STR_BEGIN_ARRAY = '['
|
|
147
|
+
const JSON_STR_END_ARRAY = ']'
|
|
148
|
+
const JSON_STR_COMMA = ','
|
|
149
|
+
const JSON_STR_COLONS = ':'
|
|
150
|
+
const JSON_STR_QUOTE = '"'
|
|
151
|
+
const JSON_STR_EMPTY_OBJECT = JSON_STR_BEGIN_OBJECT + JSON_STR_END_OBJECT
|
|
152
|
+
const JSON_STR_EMPTY_ARRAY = JSON_STR_BEGIN_ARRAY + JSON_STR_END_ARRAY
|
|
153
|
+
const JSON_STR_EMPTY_STRING = JSON_STR_QUOTE + JSON_STR_QUOTE
|
|
154
|
+
const JSON_STR_NULL = 'null'
|
|
155
|
+
`
|
|
144
156
|
|
|
145
157
|
// If we have only the invocation of the 'anonymous0' function, we would
|
|
146
158
|
// basically just wrap the 'anonymous0' function in the 'main' function and
|
|
@@ -148,13 +160,13 @@ function build (schema, options) {
|
|
|
148
160
|
// wrapping and the unnecessary memory allocation by aliasing 'anonymous0' to
|
|
149
161
|
// 'main'
|
|
150
162
|
if (code === 'json += anonymous0(input)') {
|
|
151
|
-
contextFunctionCode
|
|
163
|
+
contextFunctionCode += `
|
|
152
164
|
${context.functions.join('\n')}
|
|
153
165
|
const main = anonymous0
|
|
154
166
|
return main
|
|
155
167
|
`
|
|
156
168
|
} else {
|
|
157
|
-
contextFunctionCode
|
|
169
|
+
contextFunctionCode += `
|
|
158
170
|
function main (input) {
|
|
159
171
|
let json = ''
|
|
160
172
|
${code}
|
|
@@ -284,7 +296,7 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) {
|
|
|
284
296
|
code += `
|
|
285
297
|
if (/${propertyKey.replace(/\\*\//g, '\\/')}/.test(key)) {
|
|
286
298
|
${addComma}
|
|
287
|
-
json += serializer.asString(key) +
|
|
299
|
+
json += serializer.asString(key) + JSON_STR_COLONS
|
|
288
300
|
${buildValue(context, propertyLocation, 'value')}
|
|
289
301
|
continue
|
|
290
302
|
}
|
|
@@ -299,13 +311,13 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) {
|
|
|
299
311
|
if (additionalPropertiesSchema === true) {
|
|
300
312
|
code += `
|
|
301
313
|
${addComma}
|
|
302
|
-
json += serializer.asString(key) +
|
|
314
|
+
json += serializer.asString(key) + JSON_STR_COLONS + JSON.stringify(value)
|
|
303
315
|
`
|
|
304
316
|
} else {
|
|
305
317
|
const propertyLocation = location.getPropertyLocation('additionalProperties')
|
|
306
318
|
code += `
|
|
307
319
|
${addComma}
|
|
308
|
-
json += serializer.asString(key) +
|
|
320
|
+
json += serializer.asString(key) + JSON_STR_COLONS
|
|
309
321
|
${buildValue(context, propertyLocation, 'value')}
|
|
310
322
|
`
|
|
311
323
|
}
|
|
@@ -333,20 +345,21 @@ function buildInnerObject (context, location) {
|
|
|
333
345
|
)
|
|
334
346
|
const hasRequiredProperties = requiredProperties.includes(propertiesKeys[0])
|
|
335
347
|
|
|
336
|
-
let code = ''
|
|
348
|
+
let code = 'let value\n'
|
|
337
349
|
|
|
338
350
|
for (const key of requiredProperties) {
|
|
339
351
|
if (!propertiesKeys.includes(key)) {
|
|
340
|
-
|
|
352
|
+
const sanitizedKey = JSON.stringify(key)
|
|
353
|
+
code += `if (obj[${sanitizedKey}] === undefined) throw new Error('${sanitizedKey.replace(/'/g, '\\\'')} is required!')\n`
|
|
341
354
|
}
|
|
342
355
|
}
|
|
343
356
|
|
|
344
|
-
code += 'let json = \
|
|
357
|
+
code += 'let json = JSON_STR_BEGIN_OBJECT\n'
|
|
345
358
|
|
|
346
359
|
let addComma = ''
|
|
347
360
|
if (!hasRequiredProperties) {
|
|
348
361
|
code += 'let addComma = false\n'
|
|
349
|
-
addComma = '!addComma && (addComma = true) || (json +=
|
|
362
|
+
addComma = '!addComma && (addComma = true) || (json += JSON_STR_COMMA)'
|
|
350
363
|
}
|
|
351
364
|
|
|
352
365
|
for (const key of propertiesKeys) {
|
|
@@ -360,10 +373,11 @@ function buildInnerObject (context, location) {
|
|
|
360
373
|
const isRequired = requiredProperties.includes(key)
|
|
361
374
|
|
|
362
375
|
code += `
|
|
363
|
-
|
|
376
|
+
value = obj[${sanitizedKey}]
|
|
377
|
+
if (value !== undefined) {
|
|
364
378
|
${addComma}
|
|
365
379
|
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
366
|
-
${buildValue(context, propertyLocation,
|
|
380
|
+
${buildValue(context, propertyLocation, 'value')}
|
|
367
381
|
}`
|
|
368
382
|
|
|
369
383
|
if (defaultValue !== undefined) {
|
|
@@ -374,7 +388,7 @@ function buildInnerObject (context, location) {
|
|
|
374
388
|
`
|
|
375
389
|
} else if (isRequired) {
|
|
376
390
|
code += ` else {
|
|
377
|
-
throw new Error('${sanitizedKey} is required!')
|
|
391
|
+
throw new Error('${sanitizedKey.replace(/'/g, '\\\'')} is required!')
|
|
378
392
|
}
|
|
379
393
|
`
|
|
380
394
|
} else {
|
|
@@ -391,7 +405,7 @@ function buildInnerObject (context, location) {
|
|
|
391
405
|
}
|
|
392
406
|
|
|
393
407
|
code += `
|
|
394
|
-
return json +
|
|
408
|
+
return json + JSON_STR_END_OBJECT
|
|
395
409
|
`
|
|
396
410
|
return code
|
|
397
411
|
}
|
|
@@ -482,7 +496,7 @@ function buildObject (context, location) {
|
|
|
482
496
|
// ${schemaRef}
|
|
483
497
|
function ${functionName} (input) {
|
|
484
498
|
const obj = ${toJSON('input')}
|
|
485
|
-
${!nullable ? 'if (obj === null) return
|
|
499
|
+
${!nullable ? 'if (obj === null) return JSON_STR_EMPTY_OBJECT' : ''}
|
|
486
500
|
|
|
487
501
|
${buildInnerObject(context, location)}
|
|
488
502
|
}
|
|
@@ -523,7 +537,7 @@ function buildArray (context, location) {
|
|
|
523
537
|
|
|
524
538
|
const nullable = schema.nullable === true
|
|
525
539
|
functionCode += `
|
|
526
|
-
${!nullable ? 'if (obj === null) return
|
|
540
|
+
${!nullable ? 'if (obj === null) return JSON_STR_EMPTY_ARRAY' : ''}
|
|
527
541
|
if (!Array.isArray(obj)) {
|
|
528
542
|
throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
|
|
529
543
|
}
|
|
@@ -539,25 +553,26 @@ function buildArray (context, location) {
|
|
|
539
553
|
}
|
|
540
554
|
|
|
541
555
|
if (largeArrayMechanism === 'json-stringify') {
|
|
542
|
-
functionCode += `if (arrayLength
|
|
556
|
+
functionCode += `if (arrayLength >= ${largeArraySize}) return JSON.stringify(obj)\n`
|
|
543
557
|
}
|
|
544
558
|
|
|
545
559
|
functionCode += `
|
|
546
|
-
|
|
560
|
+
const arrayEnd = arrayLength - 1
|
|
561
|
+
let value
|
|
562
|
+
let json = ''
|
|
547
563
|
`
|
|
548
564
|
|
|
549
565
|
if (Array.isArray(itemsSchema)) {
|
|
550
566
|
for (let i = 0; i < itemsSchema.length; i++) {
|
|
551
567
|
const item = itemsSchema[i]
|
|
552
|
-
|
|
568
|
+
functionCode += `value = obj[${i}]`
|
|
569
|
+
const tmpRes = buildValue(context, itemsLocation.getPropertyLocation(i), 'value')
|
|
553
570
|
functionCode += `
|
|
554
571
|
if (${i} < arrayLength) {
|
|
555
572
|
if (${buildArrayTypeCondition(item.type, `[${i}]`)}) {
|
|
556
|
-
let json = ''
|
|
557
573
|
${tmpRes}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
jsonOutput += ','
|
|
574
|
+
if (${i} < arrayEnd) {
|
|
575
|
+
json += JSON_STR_COMMA
|
|
561
576
|
}
|
|
562
577
|
} else {
|
|
563
578
|
throw new Error(\`Item at ${i} does not match schema definition.\`)
|
|
@@ -569,9 +584,9 @@ function buildArray (context, location) {
|
|
|
569
584
|
if (schema.additionalItems) {
|
|
570
585
|
functionCode += `
|
|
571
586
|
for (let i = ${itemsSchema.length}; i < arrayLength; i++) {
|
|
572
|
-
|
|
573
|
-
if (i <
|
|
574
|
-
|
|
587
|
+
json += JSON.stringify(obj[i])
|
|
588
|
+
if (i < arrayEnd) {
|
|
589
|
+
json += JSON_STR_COMMA
|
|
575
590
|
}
|
|
576
591
|
}`
|
|
577
592
|
}
|
|
@@ -579,17 +594,15 @@ function buildArray (context, location) {
|
|
|
579
594
|
const code = buildValue(context, itemsLocation, 'obj[i]')
|
|
580
595
|
functionCode += `
|
|
581
596
|
for (let i = 0; i < arrayLength; i++) {
|
|
582
|
-
let json = ''
|
|
583
597
|
${code}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
jsonOutput += ','
|
|
598
|
+
if (i < arrayEnd) {
|
|
599
|
+
json += JSON_STR_COMMA
|
|
587
600
|
}
|
|
588
601
|
}`
|
|
589
602
|
}
|
|
590
603
|
|
|
591
604
|
functionCode += `
|
|
592
|
-
return
|
|
605
|
+
return JSON_STR_BEGIN_ARRAY + json + JSON_STR_END_ARRAY
|
|
593
606
|
}`
|
|
594
607
|
|
|
595
608
|
context.functions.push(functionCode)
|
|
@@ -600,33 +613,33 @@ function buildArrayTypeCondition (type, accessor) {
|
|
|
600
613
|
let condition
|
|
601
614
|
switch (type) {
|
|
602
615
|
case 'null':
|
|
603
|
-
condition =
|
|
616
|
+
condition = 'value === null'
|
|
604
617
|
break
|
|
605
618
|
case 'string':
|
|
606
|
-
condition = `typeof
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
619
|
+
condition = `typeof value === 'string' ||
|
|
620
|
+
value === null ||
|
|
621
|
+
value instanceof Date ||
|
|
622
|
+
value instanceof RegExp ||
|
|
610
623
|
(
|
|
611
|
-
typeof
|
|
612
|
-
typeof
|
|
613
|
-
|
|
624
|
+
typeof value === "object" &&
|
|
625
|
+
typeof value.toString === "function" &&
|
|
626
|
+
value.toString !== Object.prototype.toString
|
|
614
627
|
)`
|
|
615
628
|
break
|
|
616
629
|
case 'integer':
|
|
617
|
-
condition =
|
|
630
|
+
condition = 'Number.isInteger(value)'
|
|
618
631
|
break
|
|
619
632
|
case 'number':
|
|
620
|
-
condition =
|
|
633
|
+
condition = 'Number.isFinite(value)'
|
|
621
634
|
break
|
|
622
635
|
case 'boolean':
|
|
623
|
-
condition =
|
|
636
|
+
condition = 'typeof value === \'boolean\''
|
|
624
637
|
break
|
|
625
638
|
case 'object':
|
|
626
|
-
condition =
|
|
639
|
+
condition = 'value && typeof value === \'object\' && value.constructor === Object'
|
|
627
640
|
break
|
|
628
641
|
case 'array':
|
|
629
|
-
condition =
|
|
642
|
+
condition = 'Array.isArray(value)'
|
|
630
643
|
break
|
|
631
644
|
default:
|
|
632
645
|
if (Array.isArray(type)) {
|
|
@@ -717,7 +730,7 @@ function buildSingleTypeSerializer (context, location, input) {
|
|
|
717
730
|
|
|
718
731
|
switch (schema.type) {
|
|
719
732
|
case 'null':
|
|
720
|
-
return 'json +=
|
|
733
|
+
return 'json += JSON_STR_NULL'
|
|
721
734
|
case 'string': {
|
|
722
735
|
if (schema.format === 'date-time') {
|
|
723
736
|
return `json += serializer.asDateTime(${input})`
|
|
@@ -731,9 +744,9 @@ function buildSingleTypeSerializer (context, location, input) {
|
|
|
731
744
|
return `
|
|
732
745
|
if (typeof ${input} !== 'string') {
|
|
733
746
|
if (${input} === null) {
|
|
734
|
-
json +=
|
|
747
|
+
json += JSON_STR_EMPTY_STRING
|
|
735
748
|
} else if (${input} instanceof Date) {
|
|
736
|
-
json +=
|
|
749
|
+
json += JSON_STR_QUOTE + ${input}.toISOString() + JSON_STR_QUOTE
|
|
737
750
|
} else if (${input} instanceof RegExp) {
|
|
738
751
|
json += serializer.asString(${input}.source)
|
|
739
752
|
} else {
|
|
@@ -777,7 +790,7 @@ function buildConstSerializer (location, input) {
|
|
|
777
790
|
if (hasNullType) {
|
|
778
791
|
code += `
|
|
779
792
|
if (${input} === null) {
|
|
780
|
-
json +=
|
|
793
|
+
json += JSON_STR_NULL
|
|
781
794
|
} else {
|
|
782
795
|
`
|
|
783
796
|
}
|
|
@@ -984,7 +997,7 @@ function buildValue (context, location, input) {
|
|
|
984
997
|
if (nullable) {
|
|
985
998
|
code += `
|
|
986
999
|
if (${input} === null) {
|
|
987
|
-
json +=
|
|
1000
|
+
json += JSON_STR_NULL
|
|
988
1001
|
} else {
|
|
989
1002
|
`
|
|
990
1003
|
}
|
package/lib/serializer.js
CHANGED
|
@@ -24,38 +24,29 @@ module.exports = class Serializer {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
asInteger (i) {
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
return '' + i
|
|
30
|
-
}
|
|
31
|
-
// check if number is Infinity or NaN
|
|
32
|
-
// eslint-disable-next-line no-self-compare
|
|
33
|
-
if (i === Infinity || i === -Infinity || i !== i) {
|
|
34
|
-
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
35
|
-
}
|
|
36
|
-
return this.parseInteger(i)
|
|
37
|
-
} else if (i === null) {
|
|
38
|
-
return '0'
|
|
27
|
+
if (Number.isInteger(i)) {
|
|
28
|
+
return '' + i
|
|
39
29
|
} else if (typeof i === 'bigint') {
|
|
40
30
|
return i.toString()
|
|
41
|
-
} else {
|
|
42
|
-
/* eslint no-undef: "off" */
|
|
43
|
-
const integer = this.parseInteger(i)
|
|
44
|
-
if (Number.isFinite(integer)) {
|
|
45
|
-
return '' + integer
|
|
46
|
-
} else {
|
|
47
|
-
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
48
|
-
}
|
|
49
31
|
}
|
|
32
|
+
/* eslint no-undef: "off" */
|
|
33
|
+
const integer = this.parseInteger(i)
|
|
34
|
+
// check if number is Infinity or NaN
|
|
35
|
+
// eslint-disable-next-line no-self-compare
|
|
36
|
+
if (integer === Infinity || integer === -Infinity || integer !== integer) {
|
|
37
|
+
throw new Error(`The value "${i}" cannot be converted to an integer.`)
|
|
38
|
+
}
|
|
39
|
+
return '' + integer
|
|
50
40
|
}
|
|
51
41
|
|
|
52
42
|
asNumber (i) {
|
|
53
|
-
|
|
43
|
+
// fast cast to number
|
|
44
|
+
const num = +i
|
|
54
45
|
// check if number is NaN
|
|
55
46
|
// eslint-disable-next-line no-self-compare
|
|
56
47
|
if (num !== num) {
|
|
57
48
|
throw new Error(`The value "${i}" cannot be converted to a number.`)
|
|
58
|
-
} else if (
|
|
49
|
+
} else if (num === Infinity || num === -Infinity) {
|
|
59
50
|
return 'null'
|
|
60
51
|
} else {
|
|
61
52
|
return '' + num
|
|
@@ -100,9 +91,34 @@ module.exports = class Serializer {
|
|
|
100
91
|
}
|
|
101
92
|
|
|
102
93
|
asString (str) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
const len = str.length
|
|
95
|
+
if (len < 42) {
|
|
96
|
+
// magically escape strings for json
|
|
97
|
+
// relying on their charCodeAt
|
|
98
|
+
// everything below 32 needs JSON.stringify()
|
|
99
|
+
// every string that contain surrogate needs JSON.stringify()
|
|
100
|
+
// 34 and 92 happens all the time, so we
|
|
101
|
+
// have a fast case for them
|
|
102
|
+
let result = ''
|
|
103
|
+
let last = -1
|
|
104
|
+
let point = 255
|
|
105
|
+
// eslint-disable-next-line
|
|
106
|
+
for (var i = 0; i < len; i++) {
|
|
107
|
+
point = str.charCodeAt(i)
|
|
108
|
+
if (
|
|
109
|
+
point === 0x22 || // '"'
|
|
110
|
+
point === 0x5c // '\'
|
|
111
|
+
) {
|
|
112
|
+
last === -1 && (last = 0)
|
|
113
|
+
result += str.slice(last, i) + '\\'
|
|
114
|
+
last = i
|
|
115
|
+
} else if (point < 32 || (point >= 0xD800 && point <= 0xDFFF)) {
|
|
116
|
+
// The current character is non-printable characters or a surrogate.
|
|
117
|
+
return JSON.stringify(str)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return (last === -1 && ('"' + str + '"')) || ('"' + result + str.slice(last) + '"')
|
|
121
|
+
} else if (len < 5000 && STR_ESCAPE.test(str) === false) {
|
|
106
122
|
// Only use the regular expression for shorter input. The overhead is otherwise too much.
|
|
107
123
|
return '"' + str + '"'
|
|
108
124
|
} else {
|
|
@@ -114,37 +130,6 @@ module.exports = class Serializer {
|
|
|
114
130
|
return '"' + str + '"'
|
|
115
131
|
}
|
|
116
132
|
|
|
117
|
-
// magically escape strings for json
|
|
118
|
-
// relying on their charCodeAt
|
|
119
|
-
// everything below 32 needs JSON.stringify()
|
|
120
|
-
// every string that contain surrogate needs JSON.stringify()
|
|
121
|
-
// 34 and 92 happens all the time, so we
|
|
122
|
-
// have a fast case for them
|
|
123
|
-
asStringSmall (str) {
|
|
124
|
-
const len = str.length
|
|
125
|
-
let result = ''
|
|
126
|
-
let last = -1
|
|
127
|
-
let point = 255
|
|
128
|
-
|
|
129
|
-
// eslint-disable-next-line
|
|
130
|
-
for (var i = 0; i < len; i++) {
|
|
131
|
-
point = str.charCodeAt(i)
|
|
132
|
-
if (
|
|
133
|
-
point === 0x22 || // '"'
|
|
134
|
-
point === 0x5c // '\'
|
|
135
|
-
) {
|
|
136
|
-
last === -1 && (last = 0)
|
|
137
|
-
result += str.slice(last, i) + '\\'
|
|
138
|
-
last = i
|
|
139
|
-
} else if (point < 32 || (point >= 0xD800 && point <= 0xDFFF)) {
|
|
140
|
-
// The current character is non-printable characters or a surrogate.
|
|
141
|
-
return JSON.stringify(str)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return (last === -1 && ('"' + str + '"')) || ('"' + result + str.slice(last) + '"')
|
|
146
|
-
}
|
|
147
|
-
|
|
148
133
|
getState () {
|
|
149
134
|
return this._options
|
|
150
135
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const test = require('tap').test
|
|
4
|
+
const build = require('..')
|
|
5
|
+
|
|
6
|
+
test('required property containing single quote, contains property', (t) => {
|
|
7
|
+
t.plan(1)
|
|
8
|
+
|
|
9
|
+
const stringify = build({
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
'\'': { type: 'string' }
|
|
13
|
+
},
|
|
14
|
+
required: [
|
|
15
|
+
'\''
|
|
16
|
+
]
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
t.throws(() => stringify({}), new Error('"\'" is required!'))
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('required property containing double quote, contains property', (t) => {
|
|
23
|
+
t.plan(1)
|
|
24
|
+
|
|
25
|
+
const stringify = build({
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
'"': { type: 'string' }
|
|
29
|
+
},
|
|
30
|
+
required: [
|
|
31
|
+
'"'
|
|
32
|
+
]
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
t.throws(() => stringify({}), new Error('""" is required!'))
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('required property containing single quote, does not contain property', (t) => {
|
|
39
|
+
t.plan(1)
|
|
40
|
+
|
|
41
|
+
const stringify = build({
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
a: { type: 'string' }
|
|
45
|
+
},
|
|
46
|
+
required: [
|
|
47
|
+
'\''
|
|
48
|
+
]
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
t.throws(() => stringify({}), new Error('"\'" is required!'))
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('required property containing double quote, does not contain property', (t) => {
|
|
55
|
+
t.plan(1)
|
|
56
|
+
|
|
57
|
+
const stringify = build({
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
a: { type: 'string' }
|
|
61
|
+
},
|
|
62
|
+
required: [
|
|
63
|
+
'"'
|
|
64
|
+
]
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
t.throws(() => stringify({}), new Error('""" is required!'))
|
|
68
|
+
})
|