fast-json-stringify 5.3.0 → 5.4.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/.github/workflows/ci.yml +1 -0
- package/.taprc +7 -1
- package/README.md +29 -23
- package/index.js +126 -165
- package/lib/location.js +53 -0
- package/lib/ref-resolver.js +21 -1
- package/lib/serializer.js +8 -0
- package/lib/standalone.js +12 -11
- package/package.json +19 -16
- package/test/allof.test.js +45 -0
- package/test/anyof.test.js +48 -0
- package/test/array.test.js +18 -0
- package/test/if-then-else.test.js +50 -0
- package/test/ref.test.js +24 -0
package/.github/workflows/ci.yml
CHANGED
package/.taprc
CHANGED
package/README.md
CHANGED
|
@@ -18,31 +18,37 @@ fast-json-stringify requires a [JSON Schema Draft 7](https://json-schema.org/spe
|
|
|
18
18
|
##### Benchmarks
|
|
19
19
|
|
|
20
20
|
- Machine: `EX41S-SSD, Intel Core i7, 4Ghz, 64GB RAM, 4C/8T, SSD`.
|
|
21
|
-
- Node.js `
|
|
21
|
+
- Node.js `v18.12.1`
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
FJS creation x
|
|
25
|
-
CJS creation x
|
|
26
|
-
AJV Serialize creation x
|
|
27
|
-
JSON.stringify array x 5,
|
|
28
|
-
fast-json-stringify array x 6,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
24
|
+
FJS creation x 4,129 ops/sec ±0.82% (92 runs sampled)
|
|
25
|
+
CJS creation x 184,196 ops/sec ±0.12% (97 runs sampled)
|
|
26
|
+
AJV Serialize creation x 61,130,591 ops/sec ±0.40% (92 runs sampled)
|
|
27
|
+
JSON.stringify array x 5,057 ops/sec ±0.10% (100 runs sampled)
|
|
28
|
+
fast-json-stringify array default x 6,243 ops/sec ±0.14% (98 runs sampled)
|
|
29
|
+
fast-json-stringify array json-stringify x 6,261 ops/sec ±0.30% (99 runs sampled)
|
|
30
|
+
compile-json-stringify array x 6,842 ops/sec ±0.18% (96 runs sampled)
|
|
31
|
+
AJV Serialize array x 6,964 ops/sec ±0.11% (95 runs sampled)
|
|
32
|
+
JSON.stringify large array x 248 ops/sec ±0.07% (90 runs sampled)
|
|
33
|
+
fast-json-stringify large array default x 99.96 ops/sec ±0.22% (74 runs sampled)
|
|
34
|
+
fast-json-stringify large array json-stringify x 248 ops/sec ±0.07% (90 runs sampled)
|
|
35
|
+
compile-json-stringify large array x 317 ops/sec ±0.09% (89 runs sampled)
|
|
36
|
+
AJV Serialize large array x 111 ops/sec ±0.07% (33 runs sampled)
|
|
37
|
+
JSON.stringify long string x 16,002 ops/sec ±0.09% (98 runs sampled)
|
|
38
|
+
fast-json-stringify long string x 15,979 ops/sec ±0.09% (96 runs sampled)
|
|
39
|
+
compile-json-stringify long string x 15,952 ops/sec ±0.31% (97 runs sampled)
|
|
40
|
+
AJV Serialize long string x 21,416 ops/sec ±0.08% (98 runs sampled)
|
|
41
|
+
JSON.stringify short string x 12,944,272 ops/sec ±0.09% (96 runs sampled)
|
|
42
|
+
fast-json-stringify short string x 30,585,790 ops/sec ±0.27% (97 runs sampled)
|
|
43
|
+
compile-json-stringify short string x 30,656,406 ops/sec ±0.12% (96 runs sampled)
|
|
44
|
+
AJV Serialize short string x 30,406,785 ops/sec ±0.37% (96 runs sampled)
|
|
45
|
+
JSON.stringify obj x 3,153,043 ops/sec ±0.33% (99 runs sampled)
|
|
46
|
+
fast-json-stringify obj x 6,866,434 ops/sec ±0.11% (100 runs sampled)
|
|
47
|
+
compile-json-stringify obj x 15,886,723 ops/sec ±0.15% (98 runs sampled)
|
|
48
|
+
AJV Serialize obj x 8,969,043 ops/sec ±0.36% (97 runs sampled)
|
|
49
|
+
JSON stringify date x 1,126,547 ops/sec ±0.09% (97 runs sampled)
|
|
50
|
+
fast-json-stringify date format x 1,836,188 ops/sec ±0.12% (99 runs sampled)
|
|
51
|
+
compile-json-stringify date format x 1,125,735 ops/sec ±0.19% (98 runs sampled)
|
|
46
52
|
```
|
|
47
53
|
|
|
48
54
|
#### Table of contents:
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const validate = require('./lib/schema-validator')
|
|
|
10
10
|
const Serializer = require('./lib/serializer')
|
|
11
11
|
const Validator = require('./lib/validator')
|
|
12
12
|
const RefResolver = require('./lib/ref-resolver')
|
|
13
|
+
const Location = require('./lib/location')
|
|
13
14
|
|
|
14
15
|
let largeArraySize = 2e4
|
|
15
16
|
let largeArrayMechanism = 'default'
|
|
@@ -40,21 +41,13 @@ function isValidSchema (schema, name) {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function mergeLocation (location, key) {
|
|
44
|
-
return {
|
|
45
|
-
schema: location.schema[key],
|
|
46
|
-
schemaId: location.schemaId,
|
|
47
|
-
jsonPointer: location.jsonPointer + '/' + key
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
44
|
function resolveRef (location, ref) {
|
|
52
45
|
let hashIndex = ref.indexOf('#')
|
|
53
46
|
if (hashIndex === -1) {
|
|
54
47
|
hashIndex = ref.length
|
|
55
48
|
}
|
|
56
49
|
|
|
57
|
-
const schemaId = ref.slice(0, hashIndex) || location.
|
|
50
|
+
const schemaId = ref.slice(0, hashIndex) || location.getOriginSchemaId()
|
|
58
51
|
const jsonPointer = ref.slice(hashIndex) || '#'
|
|
59
52
|
|
|
60
53
|
const schema = refResolver.getSchema(schemaId, jsonPointer)
|
|
@@ -63,39 +56,38 @@ function resolveRef (location, ref) {
|
|
|
63
56
|
throw new Error(`Cannot find reference "${ref}"`)
|
|
64
57
|
}
|
|
65
58
|
|
|
59
|
+
const newLocation = new Location(schema, schemaId, jsonPointer)
|
|
66
60
|
if (schema.$ref !== undefined) {
|
|
67
|
-
return resolveRef(
|
|
61
|
+
return resolveRef(newLocation, schema.$ref)
|
|
68
62
|
}
|
|
69
63
|
|
|
70
|
-
return
|
|
64
|
+
return newLocation
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
const contextFunctionsNamesBySchema = new Map()
|
|
74
68
|
|
|
75
69
|
let rootSchemaId = null
|
|
76
70
|
let refResolver = null
|
|
77
|
-
let validator = null
|
|
78
71
|
let contextFunctions = null
|
|
72
|
+
let validatorSchemasIds = null
|
|
79
73
|
|
|
80
74
|
function build (schema, options) {
|
|
81
75
|
contextFunctionsNamesBySchema.clear()
|
|
82
76
|
|
|
83
77
|
contextFunctions = []
|
|
78
|
+
validatorSchemasIds = new Set()
|
|
84
79
|
options = options || {}
|
|
85
80
|
|
|
86
81
|
refResolver = new RefResolver()
|
|
87
|
-
validator = new Validator(options.ajv)
|
|
88
82
|
|
|
89
83
|
rootSchemaId = schema.$id || randomUUID()
|
|
90
84
|
|
|
91
85
|
isValidSchema(schema)
|
|
92
|
-
validator.addSchema(schema, rootSchemaId)
|
|
93
86
|
refResolver.addSchema(schema, rootSchemaId)
|
|
94
87
|
|
|
95
88
|
if (options.schema) {
|
|
96
89
|
for (const key of Object.keys(options.schema)) {
|
|
97
90
|
isValidSchema(options.schema[key], key)
|
|
98
|
-
validator.addSchema(options.schema[key], key)
|
|
99
91
|
refResolver.addSchema(options.schema[key], key)
|
|
100
92
|
}
|
|
101
93
|
}
|
|
@@ -110,7 +102,7 @@ function build (schema, options) {
|
|
|
110
102
|
if (validLargeArrayMechanisms.includes(options.largeArrayMechanism)) {
|
|
111
103
|
largeArrayMechanism = options.largeArrayMechanism
|
|
112
104
|
} else {
|
|
113
|
-
throw new Error(`Unsupported large array mechanism ${options.
|
|
105
|
+
throw new Error(`Unsupported large array mechanism ${options.largeArrayMechanism}`)
|
|
114
106
|
}
|
|
115
107
|
}
|
|
116
108
|
|
|
@@ -122,9 +114,7 @@ function build (schema, options) {
|
|
|
122
114
|
}
|
|
123
115
|
}
|
|
124
116
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
const location = { schema, schemaId: rootSchemaId, jsonPointer: '#' }
|
|
117
|
+
const location = new Location(schema, rootSchemaId)
|
|
128
118
|
const code = buildValue(location, 'input')
|
|
129
119
|
|
|
130
120
|
const contextFunctionCode = `
|
|
@@ -135,7 +125,20 @@ function build (schema, options) {
|
|
|
135
125
|
}
|
|
136
126
|
${contextFunctions.join('\n')}
|
|
137
127
|
return main
|
|
138
|
-
|
|
128
|
+
`
|
|
129
|
+
|
|
130
|
+
const serializer = new Serializer(options)
|
|
131
|
+
const validator = new Validator(options.ajv)
|
|
132
|
+
|
|
133
|
+
for (const schemaId of validatorSchemasIds) {
|
|
134
|
+
const schema = refResolver.getSchema(schemaId)
|
|
135
|
+
validator.addSchema(schema, schemaId)
|
|
136
|
+
|
|
137
|
+
const dependencies = refResolver.getSchemaDependencies(schemaId)
|
|
138
|
+
for (const [schemaId, schema] of Object.entries(dependencies)) {
|
|
139
|
+
validator.addSchema(schema, schemaId)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
139
142
|
|
|
140
143
|
const dependenciesName = ['validator', 'serializer', contextFunctionCode]
|
|
141
144
|
|
|
@@ -154,8 +157,9 @@ function build (schema, options) {
|
|
|
154
157
|
|
|
155
158
|
if (options.mode === 'standalone') {
|
|
156
159
|
// lazy load
|
|
160
|
+
const isValidatorUsed = validatorSchemasIds.size > 0
|
|
157
161
|
const buildStandaloneCode = require('./lib/standalone')
|
|
158
|
-
return buildStandaloneCode(options, validator, contextFunctionCode)
|
|
162
|
+
return buildStandaloneCode(options, validator, isValidatorUsed, contextFunctionCode)
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
/* eslint no-new-func: "off" */
|
|
@@ -163,9 +167,9 @@ function build (schema, options) {
|
|
|
163
167
|
const stringifyFunc = contextFunc(validator, serializer)
|
|
164
168
|
|
|
165
169
|
refResolver = null
|
|
166
|
-
validator = null
|
|
167
170
|
rootSchemaId = null
|
|
168
171
|
contextFunctions = null
|
|
172
|
+
validatorSchemasIds = null
|
|
169
173
|
contextFunctionsNamesBySchema.clear()
|
|
170
174
|
|
|
171
175
|
return stringifyFunc
|
|
@@ -228,106 +232,82 @@ function inferTypeByKeyword (schema) {
|
|
|
228
232
|
return schema.type
|
|
229
233
|
}
|
|
230
234
|
|
|
231
|
-
function
|
|
235
|
+
function buildExtraObjectPropertiesSerializer (location) {
|
|
232
236
|
const schema = location.schema
|
|
233
|
-
const
|
|
237
|
+
const propertiesKeys = Object.keys(schema.properties || {})
|
|
238
|
+
|
|
234
239
|
let code = `
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
const propertiesKeys = ${JSON.stringify(propertiesKeys)}
|
|
241
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
242
|
+
if (
|
|
243
|
+
propertiesKeys.includes(key) ||
|
|
244
|
+
value === undefined ||
|
|
245
|
+
typeof value === 'function' ||
|
|
246
|
+
typeof value === 'symbol'
|
|
247
|
+
) continue
|
|
239
248
|
`
|
|
240
249
|
|
|
241
|
-
const patternPropertiesLocation =
|
|
242
|
-
|
|
243
|
-
let ppLocation = mergeLocation(patternPropertiesLocation, regex)
|
|
244
|
-
if (pp[regex].$ref) {
|
|
245
|
-
ppLocation = resolveRef(ppLocation, pp[regex].$ref)
|
|
246
|
-
pp[regex] = ppLocation.schema
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
RegExp(regex)
|
|
251
|
-
} catch (err) {
|
|
252
|
-
throw new Error(`${err.message}. Found at ${regex} matching ${JSON.stringify(pp[regex])}`)
|
|
253
|
-
}
|
|
250
|
+
const patternPropertiesLocation = location.getPropertyLocation('patternProperties')
|
|
251
|
+
const patternPropertiesSchema = patternPropertiesLocation.schema
|
|
254
252
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
${addComma}
|
|
259
|
-
json += serializer.asString(keys[i]) + ':'
|
|
260
|
-
${valueCode}
|
|
261
|
-
continue
|
|
262
|
-
}
|
|
263
|
-
`
|
|
264
|
-
})
|
|
265
|
-
if (schema.additionalProperties) {
|
|
266
|
-
code += additionalProperty(location)
|
|
267
|
-
}
|
|
253
|
+
if (patternPropertiesSchema !== undefined) {
|
|
254
|
+
for (const propertyKey in patternPropertiesSchema) {
|
|
255
|
+
const propertyLocation = patternPropertiesLocation.getPropertyLocation(propertyKey)
|
|
268
256
|
|
|
269
|
-
|
|
257
|
+
try {
|
|
258
|
+
RegExp(propertyKey)
|
|
259
|
+
} catch (err) {
|
|
260
|
+
const jsonPointer = propertyLocation.getSchemaRef()
|
|
261
|
+
throw new Error(`${err.message}. Invalid pattern property regexp key ${propertyKey} at ${jsonPointer}`)
|
|
270
262
|
}
|
|
271
|
-
`
|
|
272
|
-
return code
|
|
273
|
-
}
|
|
274
263
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
let code = ''
|
|
278
|
-
if (ap === true) {
|
|
279
|
-
code += `
|
|
280
|
-
if (obj[keys[i]] !== undefined && typeof obj[keys[i]] !== 'function' && typeof obj[keys[i]] !== 'symbol') {
|
|
264
|
+
code += `
|
|
265
|
+
if (/${propertyKey.replace(/\\*\//g, '\\/')}/.test(key)) {
|
|
281
266
|
${addComma}
|
|
282
|
-
json += serializer.asString(
|
|
267
|
+
json += serializer.asString(key) + ':'
|
|
268
|
+
${buildValue(propertyLocation, 'value')}
|
|
269
|
+
continue
|
|
283
270
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return code
|
|
271
|
+
`
|
|
272
|
+
}
|
|
287
273
|
}
|
|
288
274
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
apLocation = resolveRef(apLocation, apLocation.schema.$ref)
|
|
292
|
-
}
|
|
275
|
+
const additionalPropertiesLocation = location.getPropertyLocation('additionalProperties')
|
|
276
|
+
const additionalPropertiesSchema = additionalPropertiesLocation.schema
|
|
293
277
|
|
|
294
|
-
|
|
278
|
+
if (additionalPropertiesSchema !== undefined) {
|
|
279
|
+
if (additionalPropertiesSchema === true) {
|
|
280
|
+
code += `
|
|
281
|
+
${addComma}
|
|
282
|
+
json += serializer.asString(key) + ':' + JSON.stringify(value)
|
|
283
|
+
`
|
|
284
|
+
} else {
|
|
285
|
+
const propertyLocation = location.getPropertyLocation('additionalProperties')
|
|
286
|
+
code += `
|
|
287
|
+
${addComma}
|
|
288
|
+
json += serializer.asString(key) + ':'
|
|
289
|
+
${buildValue(propertyLocation, 'value')}
|
|
290
|
+
`
|
|
291
|
+
}
|
|
292
|
+
}
|
|
295
293
|
|
|
296
294
|
code += `
|
|
297
|
-
|
|
298
|
-
json += serializer.asString(keys[i]) + ':'
|
|
299
|
-
${valueCode}
|
|
295
|
+
}
|
|
300
296
|
`
|
|
301
|
-
|
|
302
297
|
return code
|
|
303
298
|
}
|
|
304
299
|
|
|
305
|
-
function
|
|
306
|
-
return `
|
|
307
|
-
var properties = ${JSON.stringify(location.schema.properties)} || {}
|
|
308
|
-
var keys = Object.keys(obj)
|
|
309
|
-
for (var i = 0; i < keys.length; i++) {
|
|
310
|
-
if (properties[keys[i]]) continue
|
|
311
|
-
${additionalProperty(location)}
|
|
312
|
-
}
|
|
313
|
-
`
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function buildCode (location) {
|
|
317
|
-
if (location.schema.$ref) {
|
|
318
|
-
location = resolveRef(location, location.schema.$ref)
|
|
319
|
-
}
|
|
320
|
-
|
|
300
|
+
function buildInnerObject (location) {
|
|
321
301
|
const schema = location.schema
|
|
322
302
|
const required = schema.required || []
|
|
323
303
|
|
|
324
304
|
let code = ''
|
|
325
305
|
|
|
326
|
-
const propertiesLocation =
|
|
306
|
+
const propertiesLocation = location.getPropertyLocation('properties')
|
|
327
307
|
Object.keys(schema.properties || {}).forEach((key) => {
|
|
328
|
-
let propertyLocation =
|
|
329
|
-
if (propertyLocation.$ref) {
|
|
330
|
-
propertyLocation = resolveRef(location, propertyLocation.$ref)
|
|
308
|
+
let propertyLocation = propertiesLocation.getPropertyLocation(key)
|
|
309
|
+
if (propertyLocation.schema.$ref) {
|
|
310
|
+
propertyLocation = resolveRef(location, propertyLocation.schema.$ref)
|
|
331
311
|
}
|
|
332
312
|
|
|
333
313
|
const sanitized = JSON.stringify(key)
|
|
@@ -368,17 +348,21 @@ function buildCode (location) {
|
|
|
368
348
|
code += `if (obj['${requiredProperty}'] === undefined) throw new Error('"${requiredProperty}" is required!')\n`
|
|
369
349
|
}
|
|
370
350
|
|
|
351
|
+
if (schema.patternProperties || schema.additionalProperties) {
|
|
352
|
+
code += buildExtraObjectPropertiesSerializer(location)
|
|
353
|
+
}
|
|
354
|
+
|
|
371
355
|
return code
|
|
372
356
|
}
|
|
373
357
|
|
|
374
358
|
function mergeAllOfSchema (location, schema, mergedSchema) {
|
|
375
|
-
const allOfLocation =
|
|
359
|
+
const allOfLocation = location.getPropertyLocation('allOf')
|
|
376
360
|
|
|
377
361
|
for (let i = 0; i < schema.allOf.length; i++) {
|
|
378
362
|
let allOfSchema = schema.allOf[i]
|
|
379
363
|
|
|
380
364
|
if (allOfSchema.$ref) {
|
|
381
|
-
const allOfSchemaLocation =
|
|
365
|
+
const allOfSchemaLocation = allOfLocation.getPropertyLocation(i)
|
|
382
366
|
allOfSchema = resolveRef(allOfSchemaLocation, allOfSchema.$ref).schema
|
|
383
367
|
}
|
|
384
368
|
|
|
@@ -466,24 +450,13 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
|
|
|
466
450
|
delete mergedSchema.allOf
|
|
467
451
|
|
|
468
452
|
mergedSchema.$id = `merged_${randomUUID()}`
|
|
469
|
-
validator.addSchema(mergedSchema)
|
|
470
453
|
refResolver.addSchema(mergedSchema)
|
|
471
|
-
location.
|
|
472
|
-
location.jsonPointer = '#'
|
|
454
|
+
location.addMergedSchema(mergedSchema, mergedSchema.$id)
|
|
473
455
|
}
|
|
474
456
|
|
|
475
|
-
function
|
|
476
|
-
|
|
477
|
-
let code = buildCode(location)
|
|
478
|
-
if (schema.patternProperties) {
|
|
479
|
-
code += addPatternProperties(location)
|
|
480
|
-
} else if (schema.additionalProperties && !schema.patternProperties) {
|
|
481
|
-
code += addAdditionalProperties(location)
|
|
482
|
-
}
|
|
483
|
-
return code
|
|
484
|
-
}
|
|
457
|
+
function addIfThenElse (location, input) {
|
|
458
|
+
validatorSchemasIds.add(location.getSchemaId())
|
|
485
459
|
|
|
486
|
-
function addIfThenElse (location) {
|
|
487
460
|
const schema = merge({}, location.schema)
|
|
488
461
|
const thenSchema = schema.then
|
|
489
462
|
const elseSchema = schema.else || { additionalProperties: true }
|
|
@@ -492,41 +465,22 @@ function addIfThenElse (location) {
|
|
|
492
465
|
delete schema.then
|
|
493
466
|
delete schema.else
|
|
494
467
|
|
|
495
|
-
const ifLocation =
|
|
496
|
-
const ifSchemaRef = ifLocation.
|
|
468
|
+
const ifLocation = location.getPropertyLocation('if')
|
|
469
|
+
const ifSchemaRef = ifLocation.getSchemaRef()
|
|
497
470
|
|
|
498
|
-
|
|
499
|
-
if (validator.validate("${ifSchemaRef}", obj)) {
|
|
500
|
-
`
|
|
501
|
-
|
|
502
|
-
const thenLocation = mergeLocation(location, 'then')
|
|
471
|
+
const thenLocation = location.getPropertyLocation('then')
|
|
503
472
|
thenLocation.schema = merge(schema, thenSchema)
|
|
504
473
|
|
|
505
|
-
|
|
506
|
-
code += addIfThenElse(thenLocation)
|
|
507
|
-
} else {
|
|
508
|
-
code += buildInnerObject(thenLocation)
|
|
509
|
-
}
|
|
510
|
-
code += `
|
|
511
|
-
}
|
|
512
|
-
`
|
|
513
|
-
|
|
514
|
-
const elseLocation = mergeLocation(location, 'else')
|
|
474
|
+
const elseLocation = location.getPropertyLocation('else')
|
|
515
475
|
elseLocation.schema = merge(schema, elseSchema)
|
|
516
476
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
code += buildInnerObject(elseLocation)
|
|
525
|
-
}
|
|
526
|
-
code += `
|
|
527
|
-
}
|
|
528
|
-
`
|
|
529
|
-
return code
|
|
477
|
+
return `
|
|
478
|
+
if (validator.validate("${ifSchemaRef}", ${input})) {
|
|
479
|
+
${buildValue(thenLocation, input)}
|
|
480
|
+
} else {
|
|
481
|
+
${buildValue(elseLocation, input)}
|
|
482
|
+
}
|
|
483
|
+
`
|
|
530
484
|
}
|
|
531
485
|
|
|
532
486
|
function toJSON (variableName) {
|
|
@@ -546,10 +500,14 @@ function buildObject (location) {
|
|
|
546
500
|
const functionName = generateFuncName()
|
|
547
501
|
contextFunctionsNamesBySchema.set(schema, functionName)
|
|
548
502
|
|
|
549
|
-
|
|
503
|
+
let schemaRef = location.getSchemaRef()
|
|
504
|
+
if (schemaRef.startsWith(rootSchemaId)) {
|
|
505
|
+
schemaRef = schemaRef.replace(rootSchemaId, '')
|
|
506
|
+
}
|
|
507
|
+
|
|
550
508
|
let functionCode = `
|
|
551
509
|
function ${functionName} (input) {
|
|
552
|
-
// ${
|
|
510
|
+
// ${schemaRef}
|
|
553
511
|
`
|
|
554
512
|
|
|
555
513
|
functionCode += `
|
|
@@ -558,12 +516,7 @@ function buildObject (location) {
|
|
|
558
516
|
var addComma = false
|
|
559
517
|
`
|
|
560
518
|
|
|
561
|
-
|
|
562
|
-
functionCode += addIfThenElse(location)
|
|
563
|
-
} else {
|
|
564
|
-
functionCode += buildInnerObject(location)
|
|
565
|
-
}
|
|
566
|
-
|
|
519
|
+
functionCode += buildInnerObject(location)
|
|
567
520
|
functionCode += `
|
|
568
521
|
json += '}'
|
|
569
522
|
return json
|
|
@@ -577,7 +530,7 @@ function buildObject (location) {
|
|
|
577
530
|
function buildArray (location) {
|
|
578
531
|
const schema = location.schema
|
|
579
532
|
|
|
580
|
-
let itemsLocation =
|
|
533
|
+
let itemsLocation = location.getPropertyLocation('items')
|
|
581
534
|
itemsLocation.schema = itemsLocation.schema || {}
|
|
582
535
|
|
|
583
536
|
if (itemsLocation.schema.$ref) {
|
|
@@ -593,10 +546,14 @@ function buildArray (location) {
|
|
|
593
546
|
const functionName = generateFuncName()
|
|
594
547
|
contextFunctionsNamesBySchema.set(schema, functionName)
|
|
595
548
|
|
|
596
|
-
|
|
549
|
+
let schemaRef = location.getSchemaRef()
|
|
550
|
+
if (schemaRef.startsWith(rootSchemaId)) {
|
|
551
|
+
schemaRef = schemaRef.replace(rootSchemaId, '')
|
|
552
|
+
}
|
|
553
|
+
|
|
597
554
|
let functionCode = `
|
|
598
555
|
function ${functionName} (obj) {
|
|
599
|
-
// ${
|
|
556
|
+
// ${schemaRef}
|
|
600
557
|
`
|
|
601
558
|
|
|
602
559
|
functionCode += `
|
|
@@ -629,7 +586,7 @@ function buildArray (location) {
|
|
|
629
586
|
if (Array.isArray(itemsSchema)) {
|
|
630
587
|
for (let i = 0; i < itemsSchema.length; i++) {
|
|
631
588
|
const item = itemsSchema[i]
|
|
632
|
-
const tmpRes = buildValue(
|
|
589
|
+
const tmpRes = buildValue(itemsLocation.getPropertyLocation(i), `obj[${i}]`)
|
|
633
590
|
functionCode += `
|
|
634
591
|
if (${i} < arrayLength) {
|
|
635
592
|
if (${buildArrayTypeCondition(item.type, `[${i}]`)}) {
|
|
@@ -725,11 +682,11 @@ function buildMultiTypeSerializer (location, input) {
|
|
|
725
682
|
|
|
726
683
|
let code = ''
|
|
727
684
|
|
|
728
|
-
const locationClone = clone(location)
|
|
729
685
|
types.forEach((type, index) => {
|
|
686
|
+
location.schema = { ...location.schema, type }
|
|
687
|
+
const nestedResult = buildSingleTypeSerializer(location, input)
|
|
688
|
+
|
|
730
689
|
const statement = index === 0 ? 'if' : 'else if'
|
|
731
|
-
locationClone.schema.type = type
|
|
732
|
-
const nestedResult = buildSingleTypeSerializer(locationClone, input)
|
|
733
690
|
switch (type) {
|
|
734
691
|
case 'null':
|
|
735
692
|
code += `
|
|
@@ -869,11 +826,13 @@ function buildValue (location, input) {
|
|
|
869
826
|
}
|
|
870
827
|
}
|
|
871
828
|
|
|
829
|
+
if (schema.if && schema.then) {
|
|
830
|
+
return addIfThenElse(location, input)
|
|
831
|
+
}
|
|
832
|
+
|
|
872
833
|
if (schema.allOf) {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
schema = mergedSchema
|
|
876
|
-
location.schema = mergedSchema
|
|
834
|
+
mergeAllOfSchema(location, schema, clone(schema))
|
|
835
|
+
schema = location.schema
|
|
877
836
|
}
|
|
878
837
|
|
|
879
838
|
const type = schema.type
|
|
@@ -881,12 +840,14 @@ function buildValue (location, input) {
|
|
|
881
840
|
let code = ''
|
|
882
841
|
|
|
883
842
|
if (type === undefined && (schema.anyOf || schema.oneOf)) {
|
|
843
|
+
validatorSchemasIds.add(location.getSchemaId())
|
|
844
|
+
|
|
884
845
|
const type = schema.anyOf ? 'anyOf' : 'oneOf'
|
|
885
|
-
const anyOfLocation =
|
|
846
|
+
const anyOfLocation = location.getPropertyLocation(type)
|
|
886
847
|
|
|
887
848
|
for (let index = 0; index < location.schema[type].length; index++) {
|
|
888
|
-
const optionLocation =
|
|
889
|
-
const schemaRef = optionLocation.
|
|
849
|
+
const optionLocation = anyOfLocation.getPropertyLocation(index)
|
|
850
|
+
const schemaRef = optionLocation.getSchemaRef()
|
|
890
851
|
const nestedResult = buildValue(optionLocation, input)
|
|
891
852
|
code += `
|
|
892
853
|
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
|
package/lib/location.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
class Location {
|
|
4
|
+
constructor (schema, schemaId, jsonPointer = '#') {
|
|
5
|
+
this.schema = schema
|
|
6
|
+
this.schemaId = schemaId
|
|
7
|
+
this.jsonPointer = jsonPointer
|
|
8
|
+
this.mergedSchemaId = null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getPropertyLocation (propertyName) {
|
|
12
|
+
const propertyLocation = new Location(
|
|
13
|
+
this.schema[propertyName],
|
|
14
|
+
this.schemaId,
|
|
15
|
+
this.jsonPointer + '/' + propertyName
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if (this.mergedSchemaId !== null) {
|
|
19
|
+
propertyLocation.addMergedSchema(
|
|
20
|
+
this.schema[propertyName],
|
|
21
|
+
this.mergedSchemaId,
|
|
22
|
+
this.jsonPointer + '/' + propertyName
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return propertyLocation
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Use this method to get current schema location.
|
|
30
|
+
// Use it when you need to create reference to the current location.
|
|
31
|
+
getSchemaId () {
|
|
32
|
+
return this.mergedSchemaId || this.schemaId
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Use this method to get original schema id for resolving user schema $refs
|
|
36
|
+
// Don't join it with a JSON pointer to get the current location.
|
|
37
|
+
getOriginSchemaId () {
|
|
38
|
+
return this.schemaId
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getSchemaRef () {
|
|
42
|
+
const schemaId = this.getSchemaId()
|
|
43
|
+
return schemaId + this.jsonPointer
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
addMergedSchema (mergedSchema, schemaId, jsonPointer = '#') {
|
|
47
|
+
this.schema = mergedSchema
|
|
48
|
+
this.mergedSchemaId = schemaId
|
|
49
|
+
this.jsonPointer = jsonPointer
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = Location
|
package/lib/ref-resolver.js
CHANGED
|
@@ -28,6 +28,18 @@ class RefResolver {
|
|
|
28
28
|
return getDataByJSONPointer(schema.schema, jsonPointer)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
getSchemaDependencies (schemaId, dependencies = {}) {
|
|
32
|
+
const schema = this.schemas[schemaId]
|
|
33
|
+
|
|
34
|
+
for (const dependencySchemaId of schema.dependencies) {
|
|
35
|
+
if (dependencies[dependencySchemaId] !== undefined) continue
|
|
36
|
+
dependencies[dependencySchemaId] = this.getSchema(dependencySchemaId)
|
|
37
|
+
this.getSchemaDependencies(dependencySchemaId, dependencies)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return dependencies
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
insertSchemaBySchemaId (schema, schemaId) {
|
|
32
44
|
if (
|
|
33
45
|
this.schemas[schemaId] !== undefined &&
|
|
@@ -35,7 +47,7 @@ class RefResolver {
|
|
|
35
47
|
) {
|
|
36
48
|
throw new Error(`There is already another schema with id ${schemaId}`)
|
|
37
49
|
}
|
|
38
|
-
this.schemas[schemaId] = { schema, anchors: {} }
|
|
50
|
+
this.schemas[schemaId] = { schema, anchors: {}, dependencies: [] }
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
insertSchemaByAnchor (schema, schemaId, anchor) {
|
|
@@ -60,6 +72,14 @@ class RefResolver {
|
|
|
60
72
|
}
|
|
61
73
|
}
|
|
62
74
|
|
|
75
|
+
const ref = schema.$ref
|
|
76
|
+
if (ref !== undefined && typeof ref === 'string') {
|
|
77
|
+
if (ref.charAt(0) !== '#') {
|
|
78
|
+
const dependencySchemaId = ref.split('#')[0]
|
|
79
|
+
this.schemas[rootSchemaId].dependencies.push(dependencySchemaId)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
for (const key in schema) {
|
|
64
84
|
if (typeof schema[key] === 'object' && schema[key] !== null) {
|
|
65
85
|
this.insertSchemaSubschemas(schema[key], rootSchemaId)
|
package/lib/serializer.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// eslint-disable-next-line
|
|
4
|
+
const STR_ESCAPE = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/
|
|
5
|
+
|
|
3
6
|
module.exports = class Serializer {
|
|
4
7
|
constructor (options = {}) {
|
|
5
8
|
switch (options.rounding) {
|
|
@@ -94,6 +97,11 @@ module.exports = class Serializer {
|
|
|
94
97
|
str = str.toString()
|
|
95
98
|
}
|
|
96
99
|
|
|
100
|
+
// Fast escape chars check
|
|
101
|
+
if (!STR_ESCAPE.test(str)) {
|
|
102
|
+
return quotes + str + quotes
|
|
103
|
+
}
|
|
104
|
+
|
|
97
105
|
if (str.length < 42) {
|
|
98
106
|
return this.asStringSmall(str)
|
|
99
107
|
} else {
|
package/lib/standalone.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
|
|
4
|
-
function buildStandaloneCode (options, validator, contextFunctionCode) {
|
|
4
|
+
function buildStandaloneCode (options, validator, isValidatorUsed, contextFunctionCode) {
|
|
5
5
|
const serializerCode = fs.readFileSync(path.join(__dirname, 'serializer.js')).toString()
|
|
6
6
|
let buildAjvCode = ''
|
|
7
|
-
let defaultAjvSchema = ''
|
|
8
|
-
const defaultMeta = validator.ajv.defaultMeta()
|
|
9
|
-
if (typeof defaultMeta === 'string') {
|
|
10
|
-
defaultAjvSchema = defaultMeta
|
|
11
|
-
} else {
|
|
12
|
-
defaultAjvSchema = defaultMeta.$id || defaultMeta.id
|
|
13
|
-
}
|
|
14
|
-
const shouldUseAjv = contextFunctionCode.indexOf('validator') !== -1
|
|
15
|
-
// we need to export the custom json schema
|
|
16
7
|
let ajvSchemasCode = ''
|
|
17
|
-
|
|
8
|
+
|
|
9
|
+
if (isValidatorUsed) {
|
|
10
|
+
let defaultAjvSchema = ''
|
|
11
|
+
// we need to export the custom json schema
|
|
12
|
+
const defaultMeta = validator.ajv.defaultMeta()
|
|
13
|
+
if (typeof defaultMeta === 'string') {
|
|
14
|
+
defaultAjvSchema = defaultMeta
|
|
15
|
+
} else {
|
|
16
|
+
defaultAjvSchema = defaultMeta.$id || defaultMeta.id
|
|
17
|
+
}
|
|
18
|
+
|
|
18
19
|
ajvSchemasCode += `const validator = new Validator(${JSON.stringify(options.ajv || {})})\n`
|
|
19
20
|
for (const [id, schema] of Object.entries(validator.ajv.schemas)) {
|
|
20
21
|
// should skip ajv default schema
|
package/package.json
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-json-stringify",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.1",
|
|
4
4
|
"description": "Stringify your JSON at max speed",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"benchmark": "node ./benchmark/bench-cmp-lib.js",
|
|
12
|
-
"lint:fix": "standard --fix",
|
|
13
|
-
"test:lint": "standard",
|
|
14
|
-
"test:typescript": "tsc --project ./test/types/tsconfig.json && tsd",
|
|
15
|
-
"test:unit": "tap -J test/*.test.js test/**/*.test.js",
|
|
16
|
-
"test": "npm run test:lint && npm run test:unit && npm run test:typescript"
|
|
17
|
-
},
|
|
18
|
-
"precommit": "test",
|
|
7
|
+
"precommit": [
|
|
8
|
+
"lint",
|
|
9
|
+
"test"
|
|
10
|
+
],
|
|
19
11
|
"repository": {
|
|
20
12
|
"type": "git",
|
|
21
13
|
"url": "git+https://github.com/fastify/fast-json-stringify.git"
|
|
@@ -34,7 +26,7 @@
|
|
|
34
26
|
"homepage": "https://github.com/fastify/fast-json-stringify#readme",
|
|
35
27
|
"devDependencies": {
|
|
36
28
|
"@fastify/pre-commit": "^2.0.2",
|
|
37
|
-
"@sinclair/typebox": "^0.
|
|
29
|
+
"@sinclair/typebox": "^0.25.2",
|
|
38
30
|
"benchmark": "^2.1.4",
|
|
39
31
|
"cli-select": "^1.1.2",
|
|
40
32
|
"compile-json-stringify": "^0.1.2",
|
|
@@ -45,7 +37,7 @@
|
|
|
45
37
|
"simple-git": "^3.7.1",
|
|
46
38
|
"standard": "^17.0.0",
|
|
47
39
|
"tap": "^16.0.1",
|
|
48
|
-
"tsd": "^0.
|
|
40
|
+
"tsd": "^0.24.1",
|
|
49
41
|
"typescript": "^4.0.2",
|
|
50
42
|
"webpack": "^5.40.0"
|
|
51
43
|
},
|
|
@@ -65,5 +57,16 @@
|
|
|
65
57
|
"runkitExampleFilename": "./examples/example.js",
|
|
66
58
|
"tsd": {
|
|
67
59
|
"directory": "test/types"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"bench": "node ./benchmark/bench.js",
|
|
63
|
+
"bench:cmp": "node ./benchmark/bench-cmp-branch.js",
|
|
64
|
+
"bench:cmp:ci": "node ./benchmark/bench-cmp-branch.js --ci",
|
|
65
|
+
"benchmark": "node ./benchmark/bench-cmp-lib.js",
|
|
66
|
+
"lint": "standard",
|
|
67
|
+
"lint:fix": "standard --fix",
|
|
68
|
+
"test:typescript": "tsc --project ./test/types/tsconfig.json && tsd",
|
|
69
|
+
"test:unit": "tap -J test/*.test.js test/**/*.test.js",
|
|
70
|
+
"test": "npm run test:unit && npm run test:typescript"
|
|
68
71
|
}
|
|
69
|
-
}
|
|
72
|
+
}
|
package/test/allof.test.js
CHANGED
|
@@ -437,3 +437,48 @@ test('object with external $refs in allOf', (t) => {
|
|
|
437
437
|
})
|
|
438
438
|
t.equal(value, '{"id1":1,"id2":2}')
|
|
439
439
|
})
|
|
440
|
+
|
|
441
|
+
test('allof with local anchor reference', (t) => {
|
|
442
|
+
t.plan(1)
|
|
443
|
+
|
|
444
|
+
const externalSchemas = {
|
|
445
|
+
Test: {
|
|
446
|
+
$id: 'Test',
|
|
447
|
+
definitions: {
|
|
448
|
+
Problem: {
|
|
449
|
+
type: 'object',
|
|
450
|
+
properties: {
|
|
451
|
+
type: {
|
|
452
|
+
type: 'string'
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
ValidationFragment: {
|
|
457
|
+
type: 'string'
|
|
458
|
+
},
|
|
459
|
+
ValidationErrorProblem: {
|
|
460
|
+
type: 'object',
|
|
461
|
+
allOf: [
|
|
462
|
+
{
|
|
463
|
+
$ref: '#/definitions/Problem'
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
type: 'object',
|
|
467
|
+
properties: {
|
|
468
|
+
validation: {
|
|
469
|
+
$ref: '#/definitions/ValidationFragment'
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const schema = { $ref: 'Test#/definitions/ValidationErrorProblem' }
|
|
480
|
+
const stringify = build(schema, { schema: externalSchemas })
|
|
481
|
+
const data = { type: 'foo', validation: 'bar' }
|
|
482
|
+
|
|
483
|
+
t.equal(stringify(data), JSON.stringify(data))
|
|
484
|
+
})
|
package/test/anyof.test.js
CHANGED
|
@@ -596,3 +596,51 @@ test('anyOf object with invalid field date of type string with format or null',
|
|
|
596
596
|
const withOneOfStringify = build(withOneOfSchema)
|
|
597
597
|
t.throws(() => withOneOfStringify({ prop: toStringify }))
|
|
598
598
|
})
|
|
599
|
+
|
|
600
|
+
test('anyOf with a nested external schema', (t) => {
|
|
601
|
+
t.plan(1)
|
|
602
|
+
|
|
603
|
+
const externalSchemas = {
|
|
604
|
+
schema1: {
|
|
605
|
+
definitions: {
|
|
606
|
+
def1: {
|
|
607
|
+
$id: 'external',
|
|
608
|
+
type: 'string'
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
type: 'number'
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const schema = { anyOf: [{ $ref: 'external' }] }
|
|
615
|
+
|
|
616
|
+
const stringify = build(schema, { schema: externalSchemas })
|
|
617
|
+
t.equal(stringify('foo'), '"foo"')
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
test('object with ref and validated properties', (t) => {
|
|
621
|
+
t.plan(1)
|
|
622
|
+
|
|
623
|
+
const externalSchemas = {
|
|
624
|
+
RefSchema: {
|
|
625
|
+
$id: 'RefSchema',
|
|
626
|
+
type: 'string'
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const schema = {
|
|
631
|
+
$id: 'root',
|
|
632
|
+
type: 'object',
|
|
633
|
+
properties: {
|
|
634
|
+
id: {
|
|
635
|
+
anyOf: [
|
|
636
|
+
{ type: 'string' },
|
|
637
|
+
{ type: 'number' }
|
|
638
|
+
]
|
|
639
|
+
},
|
|
640
|
+
reference: { $ref: 'RefSchema' }
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const stringify = build(schema, { schema: externalSchemas })
|
|
645
|
+
t.equal(stringify({ id: 1, reference: 'hi' }), '{"id":1,"reference":"hi"}')
|
|
646
|
+
})
|
package/test/array.test.js
CHANGED
|
@@ -4,6 +4,24 @@ const test = require('tap').test
|
|
|
4
4
|
const validator = require('is-my-json-valid')
|
|
5
5
|
const build = require('..')
|
|
6
6
|
|
|
7
|
+
test('error on invalid largeArrayMechanism', (t) => {
|
|
8
|
+
t.plan(1)
|
|
9
|
+
|
|
10
|
+
t.throws(() => build({
|
|
11
|
+
title: 'large array of null values with default mechanism',
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
ids: {
|
|
15
|
+
type: 'array',
|
|
16
|
+
items: { type: 'null' }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}, {
|
|
20
|
+
largeArraySize: 2e4,
|
|
21
|
+
largeArrayMechanism: 'invalid'
|
|
22
|
+
}), Error('Unsupported large array mechanism invalid'))
|
|
23
|
+
})
|
|
24
|
+
|
|
7
25
|
function buildTest (schema, toStringify, options) {
|
|
8
26
|
test(`render a ${schema.title} as JSON`, (t) => {
|
|
9
27
|
t.plan(3)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const t = require('tap')
|
|
4
|
+
const { DateTime } = require('luxon')
|
|
4
5
|
const build = require('..')
|
|
5
6
|
|
|
6
7
|
const schema = {
|
|
@@ -368,3 +369,52 @@ t.test('nested if/then', t => {
|
|
|
368
369
|
JSON.stringify({ a: 'A', bar: 'bar', bar1: 'bar1' })
|
|
369
370
|
)
|
|
370
371
|
})
|
|
372
|
+
|
|
373
|
+
t.test('if/else with string format', (t) => {
|
|
374
|
+
t.plan(2)
|
|
375
|
+
|
|
376
|
+
const schema = {
|
|
377
|
+
if: { type: 'string' },
|
|
378
|
+
then: { type: 'string', format: 'date' },
|
|
379
|
+
else: { const: 'Invalid' }
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const stringify = build(schema)
|
|
383
|
+
|
|
384
|
+
const date = new Date()
|
|
385
|
+
|
|
386
|
+
t.equal(stringify(date), `"${DateTime.fromJSDate(date).toISODate()}"`)
|
|
387
|
+
t.equal(stringify('Invalid'), '"Invalid"')
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
t.test('if/else with const integers', (t) => {
|
|
391
|
+
t.plan(2)
|
|
392
|
+
|
|
393
|
+
const schema = {
|
|
394
|
+
type: 'number',
|
|
395
|
+
if: { type: 'number', minimum: 42 },
|
|
396
|
+
then: { const: 66 },
|
|
397
|
+
else: { const: 33 }
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const stringify = build(schema)
|
|
401
|
+
|
|
402
|
+
t.equal(stringify(100.32), '66')
|
|
403
|
+
t.equal(stringify(10.12), '33')
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
t.test('if/else with array', (t) => {
|
|
407
|
+
t.plan(2)
|
|
408
|
+
|
|
409
|
+
const schema = {
|
|
410
|
+
type: 'array',
|
|
411
|
+
if: { type: 'array', maxItems: 1 },
|
|
412
|
+
then: { items: { type: 'string' } },
|
|
413
|
+
else: { items: { type: 'number' } }
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const stringify = build(schema)
|
|
417
|
+
|
|
418
|
+
t.equal(stringify(['1']), JSON.stringify(['1']))
|
|
419
|
+
t.equal(stringify(['1', '2']), JSON.stringify([1, 2]))
|
|
420
|
+
})
|
package/test/ref.test.js
CHANGED
|
@@ -1981,3 +1981,27 @@ test('nested schema should overwrite anchor scope', (t) => {
|
|
|
1981
1981
|
t.equal(output, JSON.stringify(data))
|
|
1982
1982
|
t.throws(() => build({ $ref: 'root#anchor' }, { schema: externalSchema }))
|
|
1983
1983
|
})
|
|
1984
|
+
|
|
1985
|
+
test('object property reference with default value', (t) => {
|
|
1986
|
+
t.plan(1)
|
|
1987
|
+
|
|
1988
|
+
const schema = {
|
|
1989
|
+
definitions: {
|
|
1990
|
+
prop: {
|
|
1991
|
+
type: 'string',
|
|
1992
|
+
default: 'foo'
|
|
1993
|
+
}
|
|
1994
|
+
},
|
|
1995
|
+
type: 'object',
|
|
1996
|
+
properties: {
|
|
1997
|
+
prop: {
|
|
1998
|
+
$ref: '#/definitions/prop'
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
const stringify = build(schema)
|
|
2004
|
+
const output = stringify({})
|
|
2005
|
+
|
|
2006
|
+
t.equal(output, '{"prop":"foo"}')
|
|
2007
|
+
})
|