fast-json-stringify 3.0.1 → 3.0.2
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 +29 -56
- package/package.json +1 -1
- package/test/debug-mode.test.js +28 -17
package/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const ajvFormats = require('ajv-formats')
|
|
|
7
7
|
const merge = require('deepmerge')
|
|
8
8
|
const clone = require('rfdc')({ proto: true })
|
|
9
9
|
const fjsCloned = Symbol('fast-json-stringify.cloned')
|
|
10
|
+
const { randomUUID } = require('crypto')
|
|
10
11
|
|
|
11
12
|
const validate = require('./schema-validator')
|
|
12
13
|
let stringSimilarity = null
|
|
@@ -43,11 +44,17 @@ function mergeLocation (source, dest) {
|
|
|
43
44
|
|
|
44
45
|
const arrayItemsReferenceSerializersMap = new Map()
|
|
45
46
|
const objectReferenceSerializersMap = new Map()
|
|
47
|
+
let ajvInstance = null
|
|
46
48
|
|
|
47
49
|
function build (schema, options) {
|
|
48
50
|
arrayItemsReferenceSerializersMap.clear()
|
|
49
51
|
objectReferenceSerializersMap.clear()
|
|
52
|
+
|
|
50
53
|
options = options || {}
|
|
54
|
+
|
|
55
|
+
ajvInstance = new Ajv({ ...options.ajv, strictSchema: false })
|
|
56
|
+
ajvFormats(ajvInstance)
|
|
57
|
+
|
|
51
58
|
isValidSchema(schema)
|
|
52
59
|
if (options.schema) {
|
|
53
60
|
// eslint-disable-next-line
|
|
@@ -72,41 +79,6 @@ function build (schema, options) {
|
|
|
72
79
|
|
|
73
80
|
code += `
|
|
74
81
|
${asFunctions}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Used by schemas that are dependant on calling 'ajv.validate' during runtime,
|
|
79
|
-
* it stores the value of the '$id' property of the schema (if it has it) inside
|
|
80
|
-
* a cache which is used to figure out if the schema was compiled into a validator
|
|
81
|
-
* by ajv on a previous call, if it was then the '$id' string will be used to
|
|
82
|
-
* invoke 'ajv.validate', this allows:
|
|
83
|
-
*
|
|
84
|
-
* 1. Schemas that depend on ajv.validate calls to leverage ajv caching system.
|
|
85
|
-
* 2. To avoid errors, since directly invoking 'ajv.validate' with the same
|
|
86
|
-
* schema (that contains an '$id' property) twice will throw an error.
|
|
87
|
-
*/
|
|
88
|
-
const $validateWithAjv = (function() {
|
|
89
|
-
const cache = new Set()
|
|
90
|
-
|
|
91
|
-
return function (schema, target) {
|
|
92
|
-
const id = schema.$id
|
|
93
|
-
|
|
94
|
-
if (!id) {
|
|
95
|
-
return ajv.validate(schema, target)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const cached = cache.has(id)
|
|
99
|
-
|
|
100
|
-
if (cached) {
|
|
101
|
-
return ajv.validate(id, target)
|
|
102
|
-
} else {
|
|
103
|
-
cache.add(id)
|
|
104
|
-
return ajv.validate(schema, target)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
})()
|
|
108
|
-
|
|
109
|
-
|
|
110
82
|
function parseInteger(int) { return Math.${intParseFunctionName}(int) }
|
|
111
83
|
`
|
|
112
84
|
|
|
@@ -163,18 +135,17 @@ function build (schema, options) {
|
|
|
163
135
|
;
|
|
164
136
|
return ${main}
|
|
165
137
|
`
|
|
166
|
-
|
|
167
|
-
const ajvInstance = new Ajv(options.ajv)
|
|
168
|
-
ajvFormats(ajvInstance)
|
|
169
138
|
const dependencies = [ajvInstance]
|
|
170
139
|
const dependenciesName = ['ajv']
|
|
140
|
+
ajvInstance = null
|
|
141
|
+
|
|
171
142
|
dependenciesName.push(code)
|
|
172
143
|
|
|
173
144
|
if (options.debugMode) {
|
|
174
|
-
|
|
175
|
-
|
|
145
|
+
return {
|
|
146
|
+
code: dependenciesName.join('\n'),
|
|
147
|
+
ajv: dependencies[0]
|
|
176
148
|
}
|
|
177
|
-
return dependenciesName
|
|
178
149
|
}
|
|
179
150
|
|
|
180
151
|
arrayItemsReferenceSerializersMap.clear()
|
|
@@ -874,8 +845,11 @@ function addIfThenElse (location, name) {
|
|
|
874
845
|
let merged = merge(copy, then)
|
|
875
846
|
let mergedLocation = mergeLocation(location, { schema: merged })
|
|
876
847
|
|
|
848
|
+
const schemaKey = i.$id || randomUUID()
|
|
849
|
+
ajvInstance.addSchema(i, schemaKey)
|
|
850
|
+
|
|
877
851
|
code += `
|
|
878
|
-
valid =
|
|
852
|
+
valid = ajv.validate(${JSON.stringify(schemaKey)}, obj)
|
|
879
853
|
if (valid) {
|
|
880
854
|
`
|
|
881
855
|
if (merged.if && merged.then) {
|
|
@@ -1224,8 +1198,12 @@ function nested (laterCode, name, key, location, subKey, isArray) {
|
|
|
1224
1198
|
// with the actual schema
|
|
1225
1199
|
// 2. `nested`, through `buildCode`, replaces any reference in object properties with the actual schema
|
|
1226
1200
|
// (see https://github.com/fastify/fast-json-stringify/blob/6da3b3e8ac24b1ca5578223adedb4083b7adf8db/index.js#L631)
|
|
1201
|
+
|
|
1202
|
+
const schemaKey = location.schema.$id || randomUUID()
|
|
1203
|
+
ajvInstance.addSchema(location.schema, schemaKey)
|
|
1204
|
+
|
|
1227
1205
|
code += `
|
|
1228
|
-
${index === 0 ? 'if' : 'else if'}(
|
|
1206
|
+
${index === 0 ? 'if' : 'else if'}(ajv.validate(${JSON.stringify(schemaKey)}, ${testValue}))
|
|
1229
1207
|
${nestedResult.code}
|
|
1230
1208
|
`
|
|
1231
1209
|
laterCode = nestedResult.laterCode
|
|
@@ -1241,8 +1219,12 @@ function nested (laterCode, name, key, location, subKey, isArray) {
|
|
|
1241
1219
|
const testSerializer = getTestSerializer(location.schema.format)
|
|
1242
1220
|
const testValue = testSerializer !== undefined ? `${testSerializer}(obj${accessor}, true)` : `obj${accessor}`
|
|
1243
1221
|
// see comment on anyOf about dereferencing the schema before calling ajv.validate
|
|
1222
|
+
|
|
1223
|
+
const schemaKey = location.schema.$id || randomUUID()
|
|
1224
|
+
ajvInstance.addSchema(location.schema, schemaKey)
|
|
1225
|
+
|
|
1244
1226
|
code += `
|
|
1245
|
-
${index === 0 ? 'if' : 'else if'}(
|
|
1227
|
+
${index === 0 ? 'if' : 'else if'}(ajv.validate(${JSON.stringify(schemaKey)}, ${testValue}))
|
|
1246
1228
|
${nestedResult.code}
|
|
1247
1229
|
`
|
|
1248
1230
|
laterCode = nestedResult.laterCode
|
|
@@ -1352,17 +1334,8 @@ function isEmpty (schema) {
|
|
|
1352
1334
|
|
|
1353
1335
|
module.exports = build
|
|
1354
1336
|
|
|
1355
|
-
module.exports.restore = function (
|
|
1356
|
-
const dependencies = [debugModeStr]
|
|
1357
|
-
const args = []
|
|
1358
|
-
if (debugModeStr.startsWith('ajv')) {
|
|
1359
|
-
dependencies.unshift('ajv')
|
|
1360
|
-
const ajvInstance = new Ajv(options.ajv)
|
|
1361
|
-
ajvFormats(ajvInstance)
|
|
1362
|
-
args.push(ajvInstance)
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1337
|
+
module.exports.restore = function ({ code, ajv }) {
|
|
1365
1338
|
// eslint-disable-next-line
|
|
1366
|
-
return (Function.apply(null, ['ajv',
|
|
1367
|
-
.apply(null,
|
|
1339
|
+
return (Function.apply(null, ['ajv', code])
|
|
1340
|
+
.apply(null, [ajv]))
|
|
1368
1341
|
}
|
package/package.json
CHANGED
package/test/debug-mode.test.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const test = require('tap').test
|
|
4
4
|
const fjs = require('..')
|
|
5
|
+
const Ajv = require('ajv').default
|
|
5
6
|
|
|
6
7
|
function build (opts) {
|
|
7
8
|
return fjs({
|
|
@@ -17,32 +18,40 @@ function build (opts) {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
test('activate debug mode', t => {
|
|
20
|
-
t.plan(
|
|
21
|
+
t.plan(3)
|
|
21
22
|
const debugMode = build({ debugMode: true })
|
|
22
|
-
|
|
23
|
-
t.
|
|
23
|
+
|
|
24
|
+
t.type(debugMode, 'object')
|
|
25
|
+
t.ok(debugMode.ajv instanceof Ajv)
|
|
26
|
+
t.type(debugMode.code, 'string')
|
|
24
27
|
})
|
|
25
28
|
|
|
26
29
|
test('activate debug mode truthy', t => {
|
|
27
|
-
t.plan(
|
|
30
|
+
t.plan(3)
|
|
31
|
+
|
|
28
32
|
const debugMode = build({ debugMode: 'yes' })
|
|
29
|
-
|
|
30
|
-
t.
|
|
33
|
+
|
|
34
|
+
t.type(debugMode, 'object')
|
|
35
|
+
t.type(debugMode.code, 'string')
|
|
36
|
+
t.ok(debugMode.ajv instanceof Ajv)
|
|
31
37
|
})
|
|
32
38
|
|
|
33
39
|
test('to string auto-consistent', t => {
|
|
34
|
-
t.plan(
|
|
40
|
+
t.plan(4)
|
|
35
41
|
const debugMode = build({ debugMode: 1 })
|
|
36
|
-
t.type(debugMode, Array)
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
t.type(debugMode, 'object')
|
|
44
|
+
t.type(debugMode.code, 'string')
|
|
45
|
+
t.ok(debugMode.ajv instanceof Ajv)
|
|
46
|
+
|
|
47
|
+
const compiled = fjs.restore(debugMode)
|
|
40
48
|
const tobe = JSON.stringify({ firstName: 'Foo' })
|
|
41
49
|
t.same(compiled({ firstName: 'Foo', surname: 'bar' }), tobe, 'surname evicted')
|
|
42
50
|
})
|
|
43
51
|
|
|
44
52
|
test('to string auto-consistent with ajv', t => {
|
|
45
|
-
t.plan(
|
|
53
|
+
t.plan(4)
|
|
54
|
+
|
|
46
55
|
const debugMode = fjs({
|
|
47
56
|
title: 'object with multiple types field',
|
|
48
57
|
type: 'object',
|
|
@@ -56,16 +65,19 @@ test('to string auto-consistent with ajv', t => {
|
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
}, { debugMode: 1 })
|
|
59
|
-
t.type(debugMode, Array)
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
t.type(debugMode, 'object')
|
|
70
|
+
t.type(debugMode.code, 'string')
|
|
71
|
+
t.ok(debugMode.ajv instanceof Ajv)
|
|
72
|
+
|
|
73
|
+
const compiled = fjs.restore(debugMode)
|
|
63
74
|
const tobe = JSON.stringify({ str: 'Foo' })
|
|
64
75
|
t.same(compiled({ str: 'Foo', void: 'me' }), tobe)
|
|
65
76
|
})
|
|
66
77
|
|
|
67
78
|
test('to string auto-consistent with ajv-formats', t => {
|
|
68
79
|
t.plan(3)
|
|
80
|
+
|
|
69
81
|
const debugMode = fjs({
|
|
70
82
|
title: 'object with multiple types field and format keyword',
|
|
71
83
|
type: 'object',
|
|
@@ -80,11 +92,10 @@ test('to string auto-consistent with ajv-formats', t => {
|
|
|
80
92
|
}
|
|
81
93
|
}
|
|
82
94
|
}, { debugMode: 1 })
|
|
83
|
-
t.type(debugMode, Array)
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
t.type(debugMode, 'object')
|
|
86
97
|
|
|
87
|
-
const compiled = fjs.restore(
|
|
98
|
+
const compiled = fjs.restore(debugMode)
|
|
88
99
|
const tobe = JSON.stringify({ str: 'foo@bar.com' })
|
|
89
100
|
t.same(compiled({ str: 'foo@bar.com' }), tobe)
|
|
90
101
|
t.same(compiled({ str: 'foo' }), JSON.stringify({ str: null }), 'invalid format is ignored')
|