fast-json-stringify 5.7.0 → 5.8.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/.github/workflows/benchmark.yml +13 -44
- package/.github/workflows/ci.yml +5 -0
- package/benchmark/bench-thread.js +1 -1
- package/benchmark/bench.js +41 -0
- package/index.js +93 -45
- package/lib/serializer.js +17 -18
- package/package.json +2 -2
- package/test/allof.test.js +69 -0
- package/test/basic.test.js +33 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name: Benchmark
|
|
1
|
+
name: Benchmark PR
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request_target:
|
|
@@ -11,15 +11,8 @@ jobs:
|
|
|
11
11
|
permissions:
|
|
12
12
|
contents: read
|
|
13
13
|
outputs:
|
|
14
|
-
PR-BENCH
|
|
15
|
-
|
|
16
|
-
PR-BENCH-18: ${{ steps.benchmark-pr.outputs.BENCH_RESULT18 }}
|
|
17
|
-
MAIN-BENCH-14: ${{ steps.benchmark-main.outputs.BENCH_RESULT14 }}
|
|
18
|
-
MAIN-BENCH-16: ${{ steps.benchmark-main.outputs.BENCH_RESULT16 }}
|
|
19
|
-
MAIN-BENCH-18: ${{ steps.benchmark-main.outputs.BENCH_RESULT18 }}
|
|
20
|
-
strategy:
|
|
21
|
-
matrix:
|
|
22
|
-
node-version: [14, 16, 18]
|
|
14
|
+
PR-BENCH: ${{ steps.benchmark-pr.outputs.BENCH_RESULT }}
|
|
15
|
+
MASTER-BENCH: ${{ steps.benchmark-master.outputs.BENCH_RESULT }}
|
|
23
16
|
steps:
|
|
24
17
|
- uses: actions/checkout@v3
|
|
25
18
|
with:
|
|
@@ -29,7 +22,7 @@ jobs:
|
|
|
29
22
|
|
|
30
23
|
- uses: actions/setup-node@v3
|
|
31
24
|
with:
|
|
32
|
-
node-version:
|
|
25
|
+
node-version: 18
|
|
33
26
|
|
|
34
27
|
- name: Install
|
|
35
28
|
run: |
|
|
@@ -38,14 +31,14 @@ jobs:
|
|
|
38
31
|
- name: Run benchmark
|
|
39
32
|
id: benchmark-pr
|
|
40
33
|
run: |
|
|
41
|
-
npm run --silent
|
|
34
|
+
npm run --silent bench > ./bench-result
|
|
42
35
|
content=$(cat ./bench-result)
|
|
43
36
|
content="${content//'%'/'%25'}"
|
|
44
37
|
content="${content//$'\n'/'%0A'}"
|
|
45
38
|
content="${content//$'\r'/'%0D'}"
|
|
46
|
-
echo "::set-output name=BENCH_RESULT
|
|
39
|
+
echo "::set-output name=BENCH_RESULT::$content"
|
|
47
40
|
|
|
48
|
-
#
|
|
41
|
+
# master benchmark
|
|
49
42
|
- uses: actions/checkout@v3
|
|
50
43
|
with:
|
|
51
44
|
ref: 'master'
|
|
@@ -57,14 +50,15 @@ jobs:
|
|
|
57
50
|
- name: Run benchmark
|
|
58
51
|
id: benchmark-master
|
|
59
52
|
run: |
|
|
60
|
-
npm run --silent
|
|
53
|
+
npm run --silent bench > ./bench-result
|
|
61
54
|
content=$(cat ./bench-result)
|
|
62
55
|
content="${content//'%'/'%25'}"
|
|
63
56
|
content="${content//$'\n'/'%0A'}"
|
|
64
57
|
content="${content//$'\r'/'%0D'}"
|
|
65
|
-
echo "::set-output name=BENCH_RESULT
|
|
58
|
+
echo "::set-output name=BENCH_RESULT::$content"
|
|
66
59
|
|
|
67
60
|
output-benchmark:
|
|
61
|
+
if: "always()"
|
|
68
62
|
needs: [benchmark]
|
|
69
63
|
runs-on: ubuntu-latest
|
|
70
64
|
permissions:
|
|
@@ -75,38 +69,13 @@ jobs:
|
|
|
75
69
|
with:
|
|
76
70
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
77
71
|
message: |
|
|
78
|
-
**Node**: 14
|
|
79
72
|
**PR**:
|
|
80
73
|
```
|
|
81
|
-
${{ needs.benchmark.outputs.PR-BENCH
|
|
74
|
+
${{ needs.benchmark.outputs.PR-BENCH }}
|
|
82
75
|
```
|
|
83
|
-
**
|
|
76
|
+
**MASTER**:
|
|
84
77
|
```
|
|
85
|
-
${{ needs.benchmark.outputs.
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
**Node**: 16
|
|
91
|
-
**PR**:
|
|
92
|
-
```
|
|
93
|
-
${{ needs.benchmark.outputs.PR-BENCH-16 }}
|
|
94
|
-
```
|
|
95
|
-
**MAIN**:
|
|
96
|
-
```
|
|
97
|
-
${{ needs.benchmark.outputs.MAIN-BENCH-16 }}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
**Node**: 18
|
|
103
|
-
**PR**:
|
|
104
|
-
```
|
|
105
|
-
${{ needs.benchmark.outputs.PR-BENCH-18 }}
|
|
106
|
-
```
|
|
107
|
-
**MAIN**:
|
|
108
|
-
```
|
|
109
|
-
${{ needs.benchmark.outputs.MAIN-BENCH-18 }}
|
|
78
|
+
${{ needs.benchmark.outputs.MASTER-BENCH }}
|
|
110
79
|
```
|
|
111
80
|
|
|
112
81
|
- uses: actions-ecosystem/action-remove-labels@v1
|
package/.github/workflows/ci.yml
CHANGED
package/benchmark/bench.js
CHANGED
|
@@ -18,6 +18,11 @@ function getRandomInt (max) {
|
|
|
18
18
|
return Math.floor(Math.random() * max)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
let longSimpleString = ''
|
|
22
|
+
for (let i = 0; i < LONG_STRING_LENGTH; i++) {
|
|
23
|
+
longSimpleString += i
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
let longString = ''
|
|
22
27
|
for (let i = 0; i < LONG_STRING_LENGTH; i++) {
|
|
23
28
|
longString += i
|
|
@@ -42,6 +47,20 @@ const benchmarks = [
|
|
|
42
47
|
},
|
|
43
48
|
input: 'hello world'
|
|
44
49
|
},
|
|
50
|
+
{
|
|
51
|
+
name: 'short string with double quote',
|
|
52
|
+
schema: {
|
|
53
|
+
type: 'string'
|
|
54
|
+
},
|
|
55
|
+
input: 'hello " world'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'long string without double quotes',
|
|
59
|
+
schema: {
|
|
60
|
+
type: 'string'
|
|
61
|
+
},
|
|
62
|
+
input: longSimpleString
|
|
63
|
+
},
|
|
45
64
|
{
|
|
46
65
|
name: 'long string',
|
|
47
66
|
schema: {
|
|
@@ -235,6 +254,28 @@ const benchmarks = [
|
|
|
235
254
|
},
|
|
236
255
|
input: { firstName: 'Max', lastName: 'Power', age: 22 }
|
|
237
256
|
},
|
|
257
|
+
{
|
|
258
|
+
name: 'simple object with required fields',
|
|
259
|
+
schema: {
|
|
260
|
+
title: 'Example Schema',
|
|
261
|
+
type: 'object',
|
|
262
|
+
properties: {
|
|
263
|
+
firstName: {
|
|
264
|
+
type: 'string'
|
|
265
|
+
},
|
|
266
|
+
lastName: {
|
|
267
|
+
type: ['string', 'null']
|
|
268
|
+
},
|
|
269
|
+
age: {
|
|
270
|
+
description: 'Age in years',
|
|
271
|
+
type: 'integer',
|
|
272
|
+
minimum: 0
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
required: ['firstName', 'lastName', 'age']
|
|
276
|
+
},
|
|
277
|
+
input: { firstName: 'Max', lastName: 'Power', age: 22 }
|
|
278
|
+
},
|
|
238
279
|
{
|
|
239
280
|
name: 'object with const string property',
|
|
240
281
|
schema: {
|
package/index.js
CHANGED
|
@@ -76,6 +76,7 @@ function build (schema, options) {
|
|
|
76
76
|
functionsCounter: 0,
|
|
77
77
|
functionsNamesBySchema: new Map(),
|
|
78
78
|
options,
|
|
79
|
+
wrapObjects: true,
|
|
79
80
|
refResolver: new RefResolver(),
|
|
80
81
|
rootSchemaId: schema.$id || randomUUID(),
|
|
81
82
|
validatorSchemasIds: new Set()
|
|
@@ -123,7 +124,7 @@ function build (schema, options) {
|
|
|
123
124
|
|
|
124
125
|
// If we have only the invocation of the 'anonymous0' function, we would
|
|
125
126
|
// basically just wrap the 'anonymous0' function in the 'main' function and
|
|
126
|
-
// and the overhead of the intermediate
|
|
127
|
+
// and the overhead of the intermediate variable 'json'. We can avoid the
|
|
127
128
|
// wrapping and the unnecessary memory allocation by aliasing 'anonymous0' to
|
|
128
129
|
// 'main'
|
|
129
130
|
if (code === 'json += anonymous0(input)') {
|
|
@@ -297,59 +298,96 @@ function buildExtraObjectPropertiesSerializer (context, location) {
|
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
function buildInnerObject (context, location) {
|
|
301
|
+
let code = ''
|
|
300
302
|
const schema = location.schema
|
|
301
303
|
const required = schema.required || []
|
|
302
304
|
|
|
303
|
-
let code = ''
|
|
304
|
-
|
|
305
305
|
const propertiesLocation = location.getPropertyLocation('properties')
|
|
306
|
-
Object.keys(schema.properties || {}).forEach((key) => {
|
|
307
|
-
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
308
|
-
if (propertyLocation.schema.$ref) {
|
|
309
|
-
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
310
|
-
}
|
|
311
306
|
|
|
312
|
-
|
|
307
|
+
const requiredWithDefault = []
|
|
308
|
+
const requiredWithoutDefault = []
|
|
309
|
+
if (schema.properties) {
|
|
310
|
+
for (const key of Object.keys(schema.properties)) {
|
|
311
|
+
if (required.indexOf(key) === -1) {
|
|
312
|
+
continue
|
|
313
|
+
}
|
|
314
|
+
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
315
|
+
if (propertyLocation.schema.$ref) {
|
|
316
|
+
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
317
|
+
}
|
|
313
318
|
|
|
314
|
-
|
|
315
|
-
// see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
|
|
319
|
+
const sanitizedKey = JSON.stringify(key)
|
|
316
320
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
321
|
+
// Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
|
|
322
|
+
// see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
|
|
323
|
+
const defaultValue = propertyLocation.schema.default
|
|
324
|
+
if (defaultValue === undefined) {
|
|
325
|
+
code += `if (obj[${sanitizedKey}] === undefined) throw new Error('${sanitizedKey} is required!')\n`
|
|
326
|
+
requiredWithoutDefault.push(key)
|
|
327
|
+
}
|
|
328
|
+
requiredWithDefault.push(key)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
322
331
|
|
|
323
|
-
|
|
332
|
+
// handle extraneous required fields
|
|
333
|
+
for (const requiredProperty of required) {
|
|
334
|
+
if (requiredWithDefault.indexOf(requiredProperty) !== -1) continue
|
|
335
|
+
code += `if (obj['${requiredProperty}'] === undefined) throw new Error('"${requiredProperty}" is required!')\n`
|
|
336
|
+
}
|
|
324
337
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
338
|
+
code += `
|
|
339
|
+
let addComma = false
|
|
340
|
+
let json = '${context.wrapObjects ? '{' : ''}'
|
|
341
|
+
`
|
|
342
|
+
const wrapObjects = context.wrapObjects
|
|
343
|
+
context.wrapObjects = true
|
|
344
|
+
|
|
345
|
+
if (schema.properties) {
|
|
346
|
+
for (const key of Object.keys(schema.properties)) {
|
|
347
|
+
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
348
|
+
if (propertyLocation.schema.$ref) {
|
|
349
|
+
propertyLocation = resolveRef(context, location, propertyLocation.schema.$ref)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const sanitizedKey = JSON.stringify(key)
|
|
353
|
+
|
|
354
|
+
if (requiredWithoutDefault.indexOf(key) !== -1) {
|
|
355
|
+
code += `
|
|
329
356
|
${addComma}
|
|
330
|
-
json += ${JSON.stringify(
|
|
357
|
+
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
358
|
+
${buildValue(context, propertyLocation, `obj[${sanitizedKey}]`)}
|
|
331
359
|
`
|
|
332
|
-
} else if (required.includes(key)) {
|
|
333
|
-
code += `
|
|
334
360
|
} else {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
361
|
+
// Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
|
|
362
|
+
// see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
|
|
363
|
+
code += `
|
|
364
|
+
if (obj[${sanitizedKey}] !== undefined) {
|
|
365
|
+
${addComma}
|
|
366
|
+
json += ${JSON.stringify(sanitizedKey + ':')}
|
|
367
|
+
${buildValue(context, propertyLocation, `obj[${sanitizedKey}]`)}
|
|
368
|
+
}
|
|
369
|
+
`
|
|
370
|
+
const defaultValue = propertyLocation.schema.default
|
|
371
|
+
if (defaultValue !== undefined) {
|
|
372
|
+
code += `
|
|
373
|
+
else {
|
|
374
|
+
${addComma}
|
|
375
|
+
json += ${JSON.stringify(sanitizedKey + ':' + JSON.stringify(defaultValue))}
|
|
376
|
+
}
|
|
377
|
+
`
|
|
378
|
+
}
|
|
340
379
|
}
|
|
341
|
-
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
for (const requiredProperty of required) {
|
|
345
|
-
if (schema.properties && schema.properties[requiredProperty] !== undefined) continue
|
|
346
|
-
code += `if (obj['${requiredProperty}'] === undefined) throw new Error('"${requiredProperty}" is required!')\n`
|
|
380
|
+
}
|
|
347
381
|
}
|
|
348
382
|
|
|
349
383
|
if (schema.patternProperties || schema.additionalProperties) {
|
|
350
384
|
code += buildExtraObjectPropertiesSerializer(context, location)
|
|
351
385
|
}
|
|
352
386
|
|
|
387
|
+
context.wrapObjects = wrapObjects
|
|
388
|
+
code += `
|
|
389
|
+
return json${context.wrapObjects ? ' + \'}\'' : ''}
|
|
390
|
+
`
|
|
353
391
|
return code
|
|
354
392
|
}
|
|
355
393
|
|
|
@@ -504,19 +542,13 @@ function buildObject (context, location) {
|
|
|
504
542
|
}
|
|
505
543
|
|
|
506
544
|
let functionCode = `
|
|
507
|
-
function ${functionName} (input) {
|
|
508
|
-
// ${schemaRef}
|
|
509
545
|
`
|
|
510
546
|
|
|
511
547
|
functionCode += `
|
|
548
|
+
// ${schemaRef}
|
|
549
|
+
function ${functionName} (input) {
|
|
512
550
|
const obj = ${toJSON('input')}
|
|
513
|
-
|
|
514
|
-
let addComma = false
|
|
515
|
-
`
|
|
516
|
-
|
|
517
|
-
functionCode += buildInnerObject(context, location)
|
|
518
|
-
functionCode += `
|
|
519
|
-
return json + '}'
|
|
551
|
+
${buildInnerObject(context, location)}
|
|
520
552
|
}
|
|
521
553
|
`
|
|
522
554
|
|
|
@@ -832,9 +864,19 @@ function buildValue (context, location, input) {
|
|
|
832
864
|
|
|
833
865
|
let code = ''
|
|
834
866
|
|
|
835
|
-
if (type === undefined && (schema.anyOf || schema.oneOf)) {
|
|
867
|
+
if ((type === undefined || type === 'object') && (schema.anyOf || schema.oneOf)) {
|
|
836
868
|
context.validatorSchemasIds.add(location.getSchemaId())
|
|
837
869
|
|
|
870
|
+
if (schema.type === 'object') {
|
|
871
|
+
context.wrapObjects = false
|
|
872
|
+
const funcName = buildObject(context, location)
|
|
873
|
+
code += `
|
|
874
|
+
json += '{'
|
|
875
|
+
json += ${funcName}(${input})
|
|
876
|
+
json += ','
|
|
877
|
+
`
|
|
878
|
+
}
|
|
879
|
+
|
|
838
880
|
const type = schema.anyOf ? 'anyOf' : 'oneOf'
|
|
839
881
|
const anyOfLocation = location.getPropertyLocation(type)
|
|
840
882
|
|
|
@@ -856,6 +898,12 @@ function buildValue (context, location, input) {
|
|
|
856
898
|
code += `
|
|
857
899
|
else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
|
|
858
900
|
`
|
|
901
|
+
if (schema.type === 'object') {
|
|
902
|
+
code += `
|
|
903
|
+
json += '}'
|
|
904
|
+
`
|
|
905
|
+
context.wrapObjects = true
|
|
906
|
+
}
|
|
859
907
|
return code
|
|
860
908
|
}
|
|
861
909
|
|
package/lib/serializer.js
CHANGED
|
@@ -113,11 +113,10 @@ module.exports = class Serializer {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
if (!STR_ESCAPE.test(str)) {
|
|
118
|
-
return '"' + str + '"'
|
|
119
|
-
} else if (str.length < 42) {
|
|
116
|
+
if (str.length < 42) {
|
|
120
117
|
return this.asStringSmall(str)
|
|
118
|
+
} else if (STR_ESCAPE.test(str) === false) {
|
|
119
|
+
return '"' + str + '"'
|
|
121
120
|
} else {
|
|
122
121
|
return JSON.stringify(str)
|
|
123
122
|
}
|
|
@@ -130,32 +129,32 @@ module.exports = class Serializer {
|
|
|
130
129
|
// 34 and 92 happens all the time, so we
|
|
131
130
|
// have a fast case for them
|
|
132
131
|
asStringSmall (str) {
|
|
133
|
-
const
|
|
132
|
+
const len = str.length
|
|
134
133
|
let result = ''
|
|
135
|
-
let last =
|
|
136
|
-
let found = false
|
|
137
|
-
let surrogateFound = false
|
|
134
|
+
let last = -1
|
|
138
135
|
let point = 255
|
|
136
|
+
|
|
139
137
|
// eslint-disable-next-line
|
|
140
|
-
for (var i = 0; i <
|
|
138
|
+
for (var i = 0; i < len; i++) {
|
|
141
139
|
point = str.charCodeAt(i)
|
|
140
|
+
if (point < 32) {
|
|
141
|
+
return JSON.stringify(str)
|
|
142
|
+
}
|
|
142
143
|
if (point >= 0xD800 && point <= 0xDFFF) {
|
|
143
144
|
// The current character is a surrogate.
|
|
144
|
-
|
|
145
|
+
return JSON.stringify(str)
|
|
145
146
|
}
|
|
146
|
-
if (
|
|
147
|
+
if (
|
|
148
|
+
point === 0x22 || // '"'
|
|
149
|
+
point === 0x5c // '\'
|
|
150
|
+
) {
|
|
151
|
+
last === -1 && (last = 0)
|
|
147
152
|
result += str.slice(last, i) + '\\'
|
|
148
153
|
last = i
|
|
149
|
-
found = true
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
|
|
154
|
-
result = str
|
|
155
|
-
} else {
|
|
156
|
-
result += str.slice(last)
|
|
157
|
-
}
|
|
158
|
-
return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"'
|
|
157
|
+
return (last === -1 && ('"' + str + '"')) || ('"' + result + str.slice(last) + '"')
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
getState () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-json-stringify",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "Stringify your JSON at max speed",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"homepage": "https://github.com/fastify/fast-json-stringify#readme",
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@fastify/pre-commit": "^2.0.2",
|
|
37
|
-
"@sinclair/typebox": "^0.
|
|
37
|
+
"@sinclair/typebox": "^0.29.1",
|
|
38
38
|
"benchmark": "^2.1.4",
|
|
39
39
|
"cli-select": "^1.1.2",
|
|
40
40
|
"compile-json-stringify": "^0.1.2",
|
package/test/allof.test.js
CHANGED
|
@@ -220,6 +220,75 @@ test('object with nested allOfs', (t) => {
|
|
|
220
220
|
t.equal(value, '{"id1":1,"id2":2,"id3":3}')
|
|
221
221
|
})
|
|
222
222
|
|
|
223
|
+
test('object with anyOf nested inside allOf', (t) => {
|
|
224
|
+
t.plan(1)
|
|
225
|
+
|
|
226
|
+
const schema = {
|
|
227
|
+
title: 'object with anyOf nested inside allOf',
|
|
228
|
+
type: 'object',
|
|
229
|
+
allOf: [
|
|
230
|
+
{
|
|
231
|
+
required: ['id1', 'obj'],
|
|
232
|
+
type: 'object',
|
|
233
|
+
properties: {
|
|
234
|
+
id1: {
|
|
235
|
+
type: 'integer'
|
|
236
|
+
},
|
|
237
|
+
obj: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
nested: { type: 'string' }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
anyOf: [
|
|
247
|
+
{
|
|
248
|
+
type: 'object',
|
|
249
|
+
properties: {
|
|
250
|
+
id2: { type: 'string' }
|
|
251
|
+
},
|
|
252
|
+
required: ['id2']
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
type: 'object',
|
|
256
|
+
properties: {
|
|
257
|
+
id3: {
|
|
258
|
+
type: 'integer'
|
|
259
|
+
},
|
|
260
|
+
nestedObj: {
|
|
261
|
+
type: 'object',
|
|
262
|
+
properties: {
|
|
263
|
+
nested: { type: 'string' }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
required: ['id3']
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
type: 'object',
|
|
271
|
+
properties: {
|
|
272
|
+
id4: { type: 'integer' }
|
|
273
|
+
},
|
|
274
|
+
required: ['id4']
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const stringify = build(schema)
|
|
282
|
+
const value = stringify({
|
|
283
|
+
id1: 1,
|
|
284
|
+
id3: 3,
|
|
285
|
+
id4: 4, // extra prop shouldn't be in result
|
|
286
|
+
obj: { nested: 'yes' },
|
|
287
|
+
nestedObj: { nested: 'yes' }
|
|
288
|
+
})
|
|
289
|
+
t.equal(value, '{"id1":1,"obj":{"nested":"yes"},"id3":3,"nestedObj":{"nested":"yes"}}')
|
|
290
|
+
})
|
|
291
|
+
|
|
223
292
|
test('object with $ref in allOf', (t) => {
|
|
224
293
|
t.plan(1)
|
|
225
294
|
|
package/test/basic.test.js
CHANGED
|
@@ -344,7 +344,7 @@ test('patternProperties - throw on unknown type', (t) => {
|
|
|
344
344
|
}
|
|
345
345
|
})
|
|
346
346
|
|
|
347
|
-
test('render a
|
|
347
|
+
test('render a double quote as JSON /1', (t) => {
|
|
348
348
|
t.plan(2)
|
|
349
349
|
|
|
350
350
|
const schema = {
|
|
@@ -360,6 +360,38 @@ test('render a single quote as JSON', (t) => {
|
|
|
360
360
|
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
361
361
|
})
|
|
362
362
|
|
|
363
|
+
test('render a double quote as JSON /2', (t) => {
|
|
364
|
+
t.plan(2)
|
|
365
|
+
|
|
366
|
+
const schema = {
|
|
367
|
+
type: 'string'
|
|
368
|
+
}
|
|
369
|
+
const toStringify = 'double quote " 2'
|
|
370
|
+
|
|
371
|
+
const validate = validator(schema)
|
|
372
|
+
const stringify = build(schema)
|
|
373
|
+
const output = stringify(toStringify)
|
|
374
|
+
|
|
375
|
+
t.equal(output, JSON.stringify(toStringify))
|
|
376
|
+
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
test('render a long string', (t) => {
|
|
380
|
+
t.plan(2)
|
|
381
|
+
|
|
382
|
+
const schema = {
|
|
383
|
+
type: 'string'
|
|
384
|
+
}
|
|
385
|
+
const toStringify = 'the Ultimate Question of Life, the Universe, and Everything.'
|
|
386
|
+
|
|
387
|
+
const validate = validator(schema)
|
|
388
|
+
const stringify = build(schema)
|
|
389
|
+
const output = stringify(toStringify)
|
|
390
|
+
|
|
391
|
+
t.equal(output, JSON.stringify(toStringify))
|
|
392
|
+
t.ok(validate(JSON.parse(output)), 'valid schema')
|
|
393
|
+
})
|
|
394
|
+
|
|
363
395
|
test('returns JSON.stringify if schema type is boolean', t => {
|
|
364
396
|
t.plan(1)
|
|
365
397
|
|