fast-json-stringify 5.0.6 → 5.3.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 +12 -12
- package/.github/workflows/ci.yml +2 -0
- package/benchmark/bench.js +54 -0
- package/{build-schema-validator.js → build/build-schema-validator.js} +1 -1
- package/{example.js → examples/example.js} +1 -1
- package/{server.js → examples/server.js} +1 -1
- package/index.d.ts +9 -8
- package/index.js +232 -270
- package/lib/ref-resolver.js +84 -0
- package/{schema-validator.js → lib/schema-validator.js} +0 -0
- package/{serializer.js → lib/serializer.js} +19 -46
- package/{standalone.js → lib/standalone.js} +8 -8
- package/lib/validator.js +75 -0
- package/package.json +11 -6
- package/test/array.test.js +25 -0
- package/test/basic.test.js +13 -0
- package/test/const.test.js +235 -8
- package/test/date.test.js +119 -4
- package/test/debug-mode.test.js +23 -4
- package/test/nullable.test.js +97 -0
- package/test/ref.test.js +28 -0
- package/test/types/schema-inference.test-d.ts +48 -0
- package/test/typesArray.test.js +29 -0
- package/ajv.js +0 -37
package/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
/* eslint no-prototype-builtins: 0 */
|
|
4
4
|
|
|
5
|
-
const merge = require('deepmerge')
|
|
5
|
+
const merge = require('@fastify/deepmerge')()
|
|
6
6
|
const clone = require('rfdc')({ proto: true })
|
|
7
|
-
const fjsCloned = Symbol('fast-json-stringify.cloned')
|
|
8
7
|
const { randomUUID } = require('crypto')
|
|
9
8
|
|
|
10
|
-
const validate = require('./schema-validator')
|
|
11
|
-
const Serializer = require('./serializer')
|
|
12
|
-
const
|
|
9
|
+
const validate = require('./lib/schema-validator')
|
|
10
|
+
const Serializer = require('./lib/serializer')
|
|
11
|
+
const Validator = require('./lib/validator')
|
|
12
|
+
const RefResolver = require('./lib/ref-resolver')
|
|
13
13
|
|
|
14
14
|
let largeArraySize = 2e4
|
|
15
15
|
let largeArrayMechanism = 'default'
|
|
@@ -57,20 +57,12 @@ function resolveRef (location, ref) {
|
|
|
57
57
|
const schemaId = ref.slice(0, hashIndex) || location.schemaId
|
|
58
58
|
const jsonPointer = ref.slice(hashIndex) || '#'
|
|
59
59
|
|
|
60
|
-
const
|
|
60
|
+
const schema = refResolver.getSchema(schemaId, jsonPointer)
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
ajvSchema = ajvInstance.getSchema(schemaRef)
|
|
65
|
-
} catch (error) {
|
|
62
|
+
if (schema === undefined) {
|
|
66
63
|
throw new Error(`Cannot find reference "${ref}"`)
|
|
67
64
|
}
|
|
68
65
|
|
|
69
|
-
if (ajvSchema === undefined) {
|
|
70
|
-
throw new Error(`Cannot find reference "${ref}"`)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const schema = ajvSchema.schema
|
|
74
66
|
if (schema.$ref !== undefined) {
|
|
75
67
|
return resolveRef({ schema, schemaId, jsonPointer }, schema.$ref)
|
|
76
68
|
}
|
|
@@ -78,45 +70,33 @@ function resolveRef (location, ref) {
|
|
|
78
70
|
return { schema, schemaId, jsonPointer }
|
|
79
71
|
}
|
|
80
72
|
|
|
81
|
-
const
|
|
82
|
-
const objectReferenceSerializersMap = new Map()
|
|
73
|
+
const contextFunctionsNamesBySchema = new Map()
|
|
83
74
|
|
|
84
75
|
let rootSchemaId = null
|
|
85
|
-
let
|
|
76
|
+
let refResolver = null
|
|
77
|
+
let validator = null
|
|
86
78
|
let contextFunctions = null
|
|
87
79
|
|
|
88
80
|
function build (schema, options) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
arrayItemsReferenceSerializersMap.clear()
|
|
92
|
-
objectReferenceSerializersMap.clear()
|
|
81
|
+
contextFunctionsNamesBySchema.clear()
|
|
93
82
|
|
|
94
83
|
contextFunctions = []
|
|
95
84
|
options = options || {}
|
|
96
85
|
|
|
97
|
-
|
|
86
|
+
refResolver = new RefResolver()
|
|
87
|
+
validator = new Validator(options.ajv)
|
|
88
|
+
|
|
98
89
|
rootSchemaId = schema.$id || randomUUID()
|
|
99
90
|
|
|
100
91
|
isValidSchema(schema)
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
validator.addSchema(schema, rootSchemaId)
|
|
93
|
+
refResolver.addSchema(schema, rootSchemaId)
|
|
103
94
|
|
|
104
95
|
if (options.schema) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
isValidSchema(externalSchema, key)
|
|
110
|
-
extendDateTimeType(externalSchema)
|
|
111
|
-
|
|
112
|
-
let schemaKey = externalSchema.$id || key
|
|
113
|
-
if (externalSchema.$id !== undefined && externalSchema.$id[0] === '#') {
|
|
114
|
-
schemaKey = key + externalSchema.$id // relative URI
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (ajvInstance.getSchema(schemaKey) === undefined) {
|
|
118
|
-
ajvInstance.addSchema(externalSchema, schemaKey)
|
|
119
|
-
}
|
|
96
|
+
for (const key of Object.keys(options.schema)) {
|
|
97
|
+
isValidSchema(options.schema[key], key)
|
|
98
|
+
validator.addSchema(options.schema[key], key)
|
|
99
|
+
refResolver.addSchema(options.schema[key], key)
|
|
120
100
|
}
|
|
121
101
|
}
|
|
122
102
|
|
|
@@ -157,31 +137,36 @@ function build (schema, options) {
|
|
|
157
137
|
return main
|
|
158
138
|
`
|
|
159
139
|
|
|
160
|
-
const dependenciesName = ['
|
|
140
|
+
const dependenciesName = ['validator', 'serializer', contextFunctionCode]
|
|
161
141
|
|
|
162
142
|
if (options.debugMode) {
|
|
163
143
|
options.mode = 'debug'
|
|
164
144
|
}
|
|
165
145
|
|
|
166
146
|
if (options.mode === 'debug') {
|
|
167
|
-
return {
|
|
147
|
+
return {
|
|
148
|
+
validator,
|
|
149
|
+
serializer,
|
|
150
|
+
code: dependenciesName.join('\n'),
|
|
151
|
+
ajv: validator.ajv
|
|
152
|
+
}
|
|
168
153
|
}
|
|
169
154
|
|
|
170
155
|
if (options.mode === 'standalone') {
|
|
171
156
|
// lazy load
|
|
172
|
-
const buildStandaloneCode = require('./standalone')
|
|
173
|
-
return buildStandaloneCode(options,
|
|
157
|
+
const buildStandaloneCode = require('./lib/standalone')
|
|
158
|
+
return buildStandaloneCode(options, validator, contextFunctionCode)
|
|
174
159
|
}
|
|
175
160
|
|
|
176
161
|
/* eslint no-new-func: "off" */
|
|
177
|
-
const contextFunc = new Function('
|
|
178
|
-
const stringifyFunc = contextFunc(
|
|
162
|
+
const contextFunc = new Function('validator', 'serializer', contextFunctionCode)
|
|
163
|
+
const stringifyFunc = contextFunc(validator, serializer)
|
|
179
164
|
|
|
180
|
-
|
|
165
|
+
refResolver = null
|
|
166
|
+
validator = null
|
|
181
167
|
rootSchemaId = null
|
|
182
168
|
contextFunctions = null
|
|
183
|
-
|
|
184
|
-
objectReferenceSerializersMap.clear()
|
|
169
|
+
contextFunctionsNamesBySchema.clear()
|
|
185
170
|
|
|
186
171
|
return stringifyFunc
|
|
187
172
|
}
|
|
@@ -243,15 +228,6 @@ function inferTypeByKeyword (schema) {
|
|
|
243
228
|
return schema.type
|
|
244
229
|
}
|
|
245
230
|
|
|
246
|
-
function getStringSerializer (format, nullable) {
|
|
247
|
-
switch (format) {
|
|
248
|
-
case 'date-time': return nullable ? 'serializer.asDatetimeNullable.bind(serializer)' : 'serializer.asDatetime.bind(serializer)'
|
|
249
|
-
case 'date': return nullable ? 'serializer.asDateNullable.bind(serializer)' : 'serializer.asDate.bind(serializer)'
|
|
250
|
-
case 'time': return nullable ? 'serializer.asTimeNullable.bind(serializer)' : 'serializer.asTime.bind(serializer)'
|
|
251
|
-
default: return nullable ? 'serializer.asStringNullable.bind(serializer)' : 'serializer.asString.bind(serializer)'
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
231
|
function addPatternProperties (location) {
|
|
256
232
|
const schema = location.schema
|
|
257
233
|
const pp = schema.patternProperties
|
|
@@ -350,17 +326,16 @@ function buildCode (location) {
|
|
|
350
326
|
const propertiesLocation = mergeLocation(location, 'properties')
|
|
351
327
|
Object.keys(schema.properties || {}).forEach((key) => {
|
|
352
328
|
let propertyLocation = mergeLocation(propertiesLocation, key)
|
|
353
|
-
if (
|
|
354
|
-
propertyLocation = resolveRef(location,
|
|
355
|
-
schema.properties[key] = propertyLocation.schema
|
|
329
|
+
if (propertyLocation.$ref) {
|
|
330
|
+
propertyLocation = resolveRef(location, propertyLocation.$ref)
|
|
356
331
|
}
|
|
357
332
|
|
|
358
|
-
// Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
|
|
359
|
-
// see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
|
|
360
|
-
|
|
361
333
|
const sanitized = JSON.stringify(key)
|
|
362
334
|
const asString = JSON.stringify(sanitized)
|
|
363
335
|
|
|
336
|
+
// Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
|
|
337
|
+
// see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
|
|
338
|
+
|
|
364
339
|
code += `
|
|
365
340
|
if (obj[${sanitized}] !== undefined) {
|
|
366
341
|
${addComma}
|
|
@@ -369,7 +344,7 @@ function buildCode (location) {
|
|
|
369
344
|
|
|
370
345
|
code += buildValue(propertyLocation, `obj[${JSON.stringify(key)}]`)
|
|
371
346
|
|
|
372
|
-
const defaultValue = schema.
|
|
347
|
+
const defaultValue = propertyLocation.schema.default
|
|
373
348
|
if (defaultValue !== undefined) {
|
|
374
349
|
code += `
|
|
375
350
|
} else {
|
|
@@ -491,7 +466,8 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
|
|
|
491
466
|
delete mergedSchema.allOf
|
|
492
467
|
|
|
493
468
|
mergedSchema.$id = `merged_${randomUUID()}`
|
|
494
|
-
|
|
469
|
+
validator.addSchema(mergedSchema)
|
|
470
|
+
refResolver.addSchema(mergedSchema)
|
|
495
471
|
location.schemaId = mergedSchema.$id
|
|
496
472
|
location.jsonPointer = '#'
|
|
497
473
|
}
|
|
@@ -520,7 +496,7 @@ function addIfThenElse (location) {
|
|
|
520
496
|
const ifSchemaRef = ifLocation.schemaId + ifLocation.jsonPointer
|
|
521
497
|
|
|
522
498
|
let code = `
|
|
523
|
-
if (
|
|
499
|
+
if (validator.validate("${ifSchemaRef}", obj)) {
|
|
524
500
|
`
|
|
525
501
|
|
|
526
502
|
const thenLocation = mergeLocation(location, 'then')
|
|
@@ -563,25 +539,18 @@ function toJSON (variableName) {
|
|
|
563
539
|
function buildObject (location) {
|
|
564
540
|
const schema = location.schema
|
|
565
541
|
|
|
566
|
-
if (
|
|
567
|
-
return
|
|
542
|
+
if (contextFunctionsNamesBySchema.has(schema)) {
|
|
543
|
+
return contextFunctionsNamesBySchema.get(schema)
|
|
568
544
|
}
|
|
569
545
|
|
|
570
546
|
const functionName = generateFuncName()
|
|
571
|
-
|
|
547
|
+
contextFunctionsNamesBySchema.set(schema, functionName)
|
|
572
548
|
|
|
573
549
|
const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
|
|
574
550
|
let functionCode = `
|
|
575
551
|
function ${functionName} (input) {
|
|
576
552
|
// ${schemaId + location.jsonPointer}
|
|
577
553
|
`
|
|
578
|
-
if (schema.nullable) {
|
|
579
|
-
functionCode += `
|
|
580
|
-
if (input === null) {
|
|
581
|
-
return 'null';
|
|
582
|
-
}
|
|
583
|
-
`
|
|
584
|
-
}
|
|
585
554
|
|
|
586
555
|
functionCode += `
|
|
587
556
|
var obj = ${toJSON('input')}
|
|
@@ -606,33 +575,23 @@ function buildObject (location) {
|
|
|
606
575
|
}
|
|
607
576
|
|
|
608
577
|
function buildArray (location) {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
// default to any items type
|
|
612
|
-
if (!schema.items) {
|
|
613
|
-
schema.items = {}
|
|
614
|
-
}
|
|
578
|
+
const schema = location.schema
|
|
615
579
|
|
|
616
580
|
let itemsLocation = mergeLocation(location, 'items')
|
|
581
|
+
itemsLocation.schema = itemsLocation.schema || {}
|
|
617
582
|
|
|
618
|
-
if (schema
|
|
619
|
-
|
|
620
|
-
location.schema = clone(location.schema)
|
|
621
|
-
schema = location.schema
|
|
622
|
-
schema[fjsCloned] = true
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
location = resolveRef(location, schema.items.$ref)
|
|
626
|
-
itemsLocation = location
|
|
627
|
-
schema.items = location.schema
|
|
583
|
+
if (itemsLocation.schema.$ref) {
|
|
584
|
+
itemsLocation = resolveRef(itemsLocation, itemsLocation.schema.$ref)
|
|
628
585
|
}
|
|
629
586
|
|
|
630
|
-
|
|
631
|
-
|
|
587
|
+
const itemsSchema = itemsLocation.schema
|
|
588
|
+
|
|
589
|
+
if (contextFunctionsNamesBySchema.has(schema)) {
|
|
590
|
+
return contextFunctionsNamesBySchema.get(schema)
|
|
632
591
|
}
|
|
633
592
|
|
|
634
593
|
const functionName = generateFuncName()
|
|
635
|
-
|
|
594
|
+
contextFunctionsNamesBySchema.set(schema, functionName)
|
|
636
595
|
|
|
637
596
|
const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
|
|
638
597
|
let functionCode = `
|
|
@@ -640,14 +599,6 @@ function buildArray (location) {
|
|
|
640
599
|
// ${schemaId + location.jsonPointer}
|
|
641
600
|
`
|
|
642
601
|
|
|
643
|
-
if (schema.nullable) {
|
|
644
|
-
functionCode += `
|
|
645
|
-
if (obj === null) {
|
|
646
|
-
return 'null';
|
|
647
|
-
}
|
|
648
|
-
`
|
|
649
|
-
}
|
|
650
|
-
|
|
651
602
|
functionCode += `
|
|
652
603
|
if (!Array.isArray(obj)) {
|
|
653
604
|
throw new TypeError(\`The value '$\{obj}' does not match schema definition.\`)
|
|
@@ -657,8 +608,8 @@ function buildArray (location) {
|
|
|
657
608
|
|
|
658
609
|
if (!schema.additionalItems) {
|
|
659
610
|
functionCode += `
|
|
660
|
-
if (arrayLength > ${
|
|
661
|
-
throw new Error(\`Item at ${
|
|
611
|
+
if (arrayLength > ${itemsSchema.length}) {
|
|
612
|
+
throw new Error(\`Item at ${itemsSchema.length} does not match schema definition.\`)
|
|
662
613
|
}
|
|
663
614
|
`
|
|
664
615
|
}
|
|
@@ -675,9 +626,9 @@ function buildArray (location) {
|
|
|
675
626
|
let jsonOutput = ''
|
|
676
627
|
`
|
|
677
628
|
|
|
678
|
-
if (Array.isArray(
|
|
679
|
-
for (let i = 0; i <
|
|
680
|
-
const item =
|
|
629
|
+
if (Array.isArray(itemsSchema)) {
|
|
630
|
+
for (let i = 0; i < itemsSchema.length; i++) {
|
|
631
|
+
const item = itemsSchema[i]
|
|
681
632
|
const tmpRes = buildValue(mergeLocation(itemsLocation, i), `obj[${i}]`)
|
|
682
633
|
functionCode += `
|
|
683
634
|
if (${i} < arrayLength) {
|
|
@@ -697,7 +648,7 @@ function buildArray (location) {
|
|
|
697
648
|
|
|
698
649
|
if (schema.additionalItems) {
|
|
699
650
|
functionCode += `
|
|
700
|
-
for (let i = ${
|
|
651
|
+
for (let i = ${itemsSchema.length}; i < arrayLength; i++) {
|
|
701
652
|
let json = JSON.stringify(obj[i])
|
|
702
653
|
jsonOutput += json
|
|
703
654
|
if (i < arrayLength - 1) {
|
|
@@ -768,9 +719,144 @@ function generateFuncName () {
|
|
|
768
719
|
return 'anonymous' + genFuncNameCounter++
|
|
769
720
|
}
|
|
770
721
|
|
|
722
|
+
function buildMultiTypeSerializer (location, input) {
|
|
723
|
+
const schema = location.schema
|
|
724
|
+
const types = schema.type.sort(t1 => t1 === 'null' ? -1 : 1)
|
|
725
|
+
|
|
726
|
+
let code = ''
|
|
727
|
+
|
|
728
|
+
const locationClone = clone(location)
|
|
729
|
+
types.forEach((type, index) => {
|
|
730
|
+
const statement = index === 0 ? 'if' : 'else if'
|
|
731
|
+
locationClone.schema.type = type
|
|
732
|
+
const nestedResult = buildSingleTypeSerializer(locationClone, input)
|
|
733
|
+
switch (type) {
|
|
734
|
+
case 'null':
|
|
735
|
+
code += `
|
|
736
|
+
${statement} (${input} === null)
|
|
737
|
+
${nestedResult}
|
|
738
|
+
`
|
|
739
|
+
break
|
|
740
|
+
case 'string': {
|
|
741
|
+
code += `
|
|
742
|
+
${statement}(
|
|
743
|
+
typeof ${input} === "string" ||
|
|
744
|
+
${input} === null ||
|
|
745
|
+
${input} instanceof Date ||
|
|
746
|
+
${input} instanceof RegExp ||
|
|
747
|
+
(
|
|
748
|
+
typeof ${input} === "object" &&
|
|
749
|
+
typeof ${input}.toString === "function" &&
|
|
750
|
+
${input}.toString !== Object.prototype.toString &&
|
|
751
|
+
!(${input} instanceof Date)
|
|
752
|
+
)
|
|
753
|
+
)
|
|
754
|
+
${nestedResult}
|
|
755
|
+
`
|
|
756
|
+
break
|
|
757
|
+
}
|
|
758
|
+
case 'array': {
|
|
759
|
+
code += `
|
|
760
|
+
${statement}(Array.isArray(${input}))
|
|
761
|
+
${nestedResult}
|
|
762
|
+
`
|
|
763
|
+
break
|
|
764
|
+
}
|
|
765
|
+
case 'integer': {
|
|
766
|
+
code += `
|
|
767
|
+
${statement}(Number.isInteger(${input}) || ${input} === null)
|
|
768
|
+
${nestedResult}
|
|
769
|
+
`
|
|
770
|
+
break
|
|
771
|
+
}
|
|
772
|
+
default: {
|
|
773
|
+
code += `
|
|
774
|
+
${statement}(typeof ${input} === "${type}" || ${input} === null)
|
|
775
|
+
${nestedResult}
|
|
776
|
+
`
|
|
777
|
+
break
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
})
|
|
781
|
+
code += `
|
|
782
|
+
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
|
|
783
|
+
`
|
|
784
|
+
|
|
785
|
+
return code
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function buildSingleTypeSerializer (location, input) {
|
|
789
|
+
const schema = location.schema
|
|
790
|
+
|
|
791
|
+
switch (schema.type) {
|
|
792
|
+
case 'null':
|
|
793
|
+
return 'json += \'null\''
|
|
794
|
+
case 'string': {
|
|
795
|
+
if (schema.format === 'date-time') {
|
|
796
|
+
return `json += serializer.asDateTime(${input})`
|
|
797
|
+
} else if (schema.format === 'date') {
|
|
798
|
+
return `json += serializer.asDate(${input})`
|
|
799
|
+
} else if (schema.format === 'time') {
|
|
800
|
+
return `json += serializer.asTime(${input})`
|
|
801
|
+
} else {
|
|
802
|
+
return `json += serializer.asString(${input})`
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
case 'integer':
|
|
806
|
+
return `json += serializer.asInteger(${input})`
|
|
807
|
+
case 'number':
|
|
808
|
+
return `json += serializer.asNumber(${input})`
|
|
809
|
+
case 'boolean':
|
|
810
|
+
return `json += serializer.asBoolean(${input})`
|
|
811
|
+
case 'object': {
|
|
812
|
+
const funcName = buildObject(location)
|
|
813
|
+
return `json += ${funcName}(${input})`
|
|
814
|
+
}
|
|
815
|
+
case 'array': {
|
|
816
|
+
const funcName = buildArray(location)
|
|
817
|
+
return `json += ${funcName}(${input})`
|
|
818
|
+
}
|
|
819
|
+
case undefined:
|
|
820
|
+
return `json += JSON.stringify(${input})`
|
|
821
|
+
default:
|
|
822
|
+
throw new Error(`${schema.type} unsupported`)
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function buildConstSerializer (location, input) {
|
|
827
|
+
const schema = location.schema
|
|
828
|
+
const type = schema.type
|
|
829
|
+
|
|
830
|
+
const hasNullType = Array.isArray(type) && type.includes('null')
|
|
831
|
+
|
|
832
|
+
let code = ''
|
|
833
|
+
|
|
834
|
+
if (hasNullType) {
|
|
835
|
+
code += `
|
|
836
|
+
if (${input} === null) {
|
|
837
|
+
json += 'null'
|
|
838
|
+
} else {
|
|
839
|
+
`
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
code += `json += '${JSON.stringify(schema.const)}'`
|
|
843
|
+
|
|
844
|
+
if (hasNullType) {
|
|
845
|
+
code += `
|
|
846
|
+
}
|
|
847
|
+
`
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return code
|
|
851
|
+
}
|
|
852
|
+
|
|
771
853
|
function buildValue (location, input) {
|
|
772
854
|
let schema = location.schema
|
|
773
855
|
|
|
856
|
+
if (typeof schema === 'boolean') {
|
|
857
|
+
return `json += JSON.stringify(${input})`
|
|
858
|
+
}
|
|
859
|
+
|
|
774
860
|
if (schema.$ref) {
|
|
775
861
|
location = resolveRef(location, schema.$ref)
|
|
776
862
|
schema = location.schema
|
|
@@ -791,185 +877,61 @@ function buildValue (location, input) {
|
|
|
791
877
|
}
|
|
792
878
|
|
|
793
879
|
const type = schema.type
|
|
794
|
-
const nullable = schema.nullable === true
|
|
795
880
|
|
|
796
881
|
let code = ''
|
|
797
|
-
let funcName
|
|
798
882
|
|
|
799
|
-
|
|
800
|
-
|
|
883
|
+
if (type === undefined && (schema.anyOf || schema.oneOf)) {
|
|
884
|
+
const type = schema.anyOf ? 'anyOf' : 'oneOf'
|
|
885
|
+
const anyOfLocation = mergeLocation(location, type)
|
|
886
|
+
|
|
887
|
+
for (let index = 0; index < location.schema[type].length; index++) {
|
|
888
|
+
const optionLocation = mergeLocation(anyOfLocation, index)
|
|
889
|
+
const schemaRef = optionLocation.schemaId + optionLocation.jsonPointer
|
|
890
|
+
const nestedResult = buildValue(optionLocation, input)
|
|
801
891
|
code += `
|
|
802
|
-
|
|
892
|
+
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
|
|
893
|
+
${nestedResult}
|
|
803
894
|
`
|
|
804
|
-
break
|
|
805
|
-
case 'string': {
|
|
806
|
-
funcName = getStringSerializer(schema.format, nullable)
|
|
807
|
-
code += `json += ${funcName}(${input})`
|
|
808
|
-
break
|
|
809
895
|
}
|
|
810
|
-
case 'integer':
|
|
811
|
-
funcName = nullable ? 'serializer.asIntegerNullable.bind(serializer)' : 'serializer.asInteger.bind(serializer)'
|
|
812
|
-
code += `json += ${funcName}(${input})`
|
|
813
|
-
break
|
|
814
|
-
case 'number':
|
|
815
|
-
funcName = nullable ? 'serializer.asNumberNullable.bind(serializer)' : 'serializer.asNumber.bind(serializer)'
|
|
816
|
-
code += `json += ${funcName}(${input})`
|
|
817
|
-
break
|
|
818
|
-
case 'boolean':
|
|
819
|
-
funcName = nullable ? 'serializer.asBooleanNullable.bind(serializer)' : 'serializer.asBoolean.bind(serializer)'
|
|
820
|
-
code += `json += ${funcName}(${input})`
|
|
821
|
-
break
|
|
822
|
-
case 'object':
|
|
823
|
-
funcName = buildObject(location)
|
|
824
|
-
code += `json += ${funcName}(${input})`
|
|
825
|
-
break
|
|
826
|
-
case 'array':
|
|
827
|
-
funcName = buildArray(location)
|
|
828
|
-
code += `json += ${funcName}(${input})`
|
|
829
|
-
break
|
|
830
|
-
case undefined:
|
|
831
|
-
if (schema.fjs_date_type) {
|
|
832
|
-
funcName = getStringSerializer(schema.fjs_date_type, nullable)
|
|
833
|
-
code += `json += ${funcName}(${input})`
|
|
834
|
-
break
|
|
835
|
-
} else if (schema.anyOf || schema.oneOf) {
|
|
836
|
-
// beware: dereferenceOfRefs has side effects and changes schema.anyOf
|
|
837
|
-
const type = schema.anyOf ? 'anyOf' : 'oneOf'
|
|
838
|
-
const anyOfLocation = mergeLocation(location, type)
|
|
839
|
-
|
|
840
|
-
for (let index = 0; index < location.schema[type].length; index++) {
|
|
841
|
-
const optionLocation = mergeLocation(anyOfLocation, index)
|
|
842
|
-
const schemaRef = optionLocation.schemaId + optionLocation.jsonPointer
|
|
843
|
-
const nestedResult = buildValue(optionLocation, input)
|
|
844
|
-
code += `
|
|
845
|
-
${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaRef}", ${input}))
|
|
846
|
-
${nestedResult}
|
|
847
|
-
`
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
code += `
|
|
851
|
-
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
|
|
852
|
-
`
|
|
853
|
-
} else if (isEmpty(schema)) {
|
|
854
|
-
code += `
|
|
855
|
-
json += JSON.stringify(${input})
|
|
856
|
-
`
|
|
857
|
-
} else if ('const' in schema) {
|
|
858
|
-
code += `
|
|
859
|
-
if(ajv.validate(${JSON.stringify(schema)}, ${input}))
|
|
860
|
-
json += '${JSON.stringify(schema.const)}'
|
|
861
|
-
else
|
|
862
|
-
throw new Error(\`Item $\{JSON.stringify(${input})} does not match schema definition.\`)
|
|
863
|
-
`
|
|
864
|
-
} else if (schema.type === undefined) {
|
|
865
|
-
code += `
|
|
866
|
-
json += JSON.stringify(${input})
|
|
867
|
-
`
|
|
868
|
-
} else {
|
|
869
|
-
throw new Error(`${schema.type} unsupported`)
|
|
870
|
-
}
|
|
871
|
-
break
|
|
872
|
-
default:
|
|
873
|
-
if (Array.isArray(type)) {
|
|
874
|
-
let sortedTypes = type
|
|
875
|
-
const nullable = schema.nullable === true || type.includes('null')
|
|
876
|
-
|
|
877
|
-
if (nullable) {
|
|
878
|
-
sortedTypes = sortedTypes.filter(type => type !== 'null')
|
|
879
|
-
code += `
|
|
880
|
-
if (${input} === null) {
|
|
881
|
-
json += null
|
|
882
|
-
} else {`
|
|
883
|
-
}
|
|
884
896
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
switch (type) {
|
|
891
|
-
case 'string': {
|
|
892
|
-
code += `
|
|
893
|
-
${statement}(${input} === null || typeof ${input} === "${type}" || ${input} instanceof Date || ${input} instanceof RegExp || (typeof ${input} === "object" && Object.hasOwnProperty.call(${input}, "toString")))
|
|
894
|
-
${nestedResult}
|
|
895
|
-
`
|
|
896
|
-
break
|
|
897
|
-
}
|
|
898
|
-
case 'array': {
|
|
899
|
-
code += `
|
|
900
|
-
${statement}(Array.isArray(${input}))
|
|
901
|
-
${nestedResult}
|
|
902
|
-
`
|
|
903
|
-
break
|
|
904
|
-
}
|
|
905
|
-
case 'integer': {
|
|
906
|
-
code += `
|
|
907
|
-
${statement}(Number.isInteger(${input}) || ${input} === null)
|
|
908
|
-
${nestedResult}
|
|
909
|
-
`
|
|
910
|
-
break
|
|
911
|
-
}
|
|
912
|
-
default: {
|
|
913
|
-
code += `
|
|
914
|
-
${statement}(typeof ${input} === "${type}" || ${input} === null)
|
|
915
|
-
${nestedResult}
|
|
916
|
-
`
|
|
917
|
-
break
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
})
|
|
921
|
-
code += `
|
|
922
|
-
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
|
|
923
|
-
`
|
|
897
|
+
code += `
|
|
898
|
+
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
|
|
899
|
+
`
|
|
900
|
+
return code
|
|
901
|
+
}
|
|
924
902
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
903
|
+
const nullable = schema.nullable === true
|
|
904
|
+
if (nullable) {
|
|
905
|
+
code += `
|
|
906
|
+
if (${input} === null) {
|
|
907
|
+
json += 'null'
|
|
930
908
|
} else {
|
|
931
|
-
|
|
932
|
-
}
|
|
909
|
+
`
|
|
933
910
|
}
|
|
934
911
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
function extendDateTimeType (schema) {
|
|
942
|
-
if (schema === null) return
|
|
943
|
-
|
|
944
|
-
if (schema.type === 'string' && ['date-time', 'date', 'time'].includes(schema.format)) {
|
|
945
|
-
schema.fjs_date_type = schema.format
|
|
946
|
-
delete schema.type
|
|
947
|
-
delete schema.format
|
|
948
|
-
}
|
|
949
|
-
for (const property in schema) {
|
|
950
|
-
if (typeof schema[property] === 'object') {
|
|
951
|
-
extendDateTimeType(schema[property])
|
|
952
|
-
}
|
|
912
|
+
if (schema.const !== undefined) {
|
|
913
|
+
code += buildConstSerializer(location, input)
|
|
914
|
+
} else if (Array.isArray(type)) {
|
|
915
|
+
code += buildMultiTypeSerializer(location, input)
|
|
916
|
+
} else {
|
|
917
|
+
code += buildSingleTypeSerializer(location, input)
|
|
953
918
|
}
|
|
954
|
-
}
|
|
955
919
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
return false
|
|
961
|
-
}
|
|
920
|
+
if (nullable) {
|
|
921
|
+
code += `
|
|
922
|
+
}
|
|
923
|
+
`
|
|
962
924
|
}
|
|
963
|
-
|
|
925
|
+
|
|
926
|
+
return code
|
|
964
927
|
}
|
|
965
928
|
|
|
966
929
|
module.exports = build
|
|
967
930
|
|
|
968
931
|
module.exports.validLargeArrayMechanisms = validLargeArrayMechanisms
|
|
969
932
|
|
|
970
|
-
module.exports.restore = function ({ code,
|
|
971
|
-
const serializer = new Serializer()
|
|
933
|
+
module.exports.restore = function ({ code, validator, serializer }) {
|
|
972
934
|
// eslint-disable-next-line
|
|
973
|
-
return (Function.apply(null, ['
|
|
974
|
-
.apply(null, [
|
|
935
|
+
return (Function.apply(null, ['validator', 'serializer', code])
|
|
936
|
+
.apply(null, [validator, serializer]))
|
|
975
937
|
}
|