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 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
- dependenciesName.toString = function () {
175
- return dependenciesName.join('\n')
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 = $validateWithAjv(${JSON.stringify(i)}, obj)
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'}($validateWithAjv(${JSON.stringify(location.schema)}, ${testValue}))
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'}($validateWithAjv(${JSON.stringify(location.schema)}, ${testValue}))
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 (debugModeStr, options = {}) {
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', debugModeStr])
1367
- .apply(null, args))
1339
+ return (Function.apply(null, ['ajv', code])
1340
+ .apply(null, [ajv]))
1368
1341
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-json-stringify",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Stringify your JSON at max speed",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -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(2)
21
+ t.plan(3)
21
22
  const debugMode = build({ debugMode: true })
22
- t.type(debugMode, Array)
23
- t.match(debugMode.toString.toString(), 'join', 'to string override')
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(2)
30
+ t.plan(3)
31
+
28
32
  const debugMode = build({ debugMode: 'yes' })
29
- t.type(debugMode, Array)
30
- t.match(debugMode.toString.toString(), 'join', 'to string override')
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(2)
40
+ t.plan(4)
35
41
  const debugMode = build({ debugMode: 1 })
36
- t.type(debugMode, Array)
37
42
 
38
- const str = debugMode.toString()
39
- const compiled = fjs.restore(str)
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(2)
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
- const str = debugMode.toString()
62
- const compiled = fjs.restore(str)
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
- const str = debugMode.toString()
96
+ t.type(debugMode, 'object')
86
97
 
87
- const compiled = fjs.restore(str)
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')