fast-json-stringify 5.3.0 → 5.4.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.
@@ -15,3 +15,4 @@ jobs:
15
15
  uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
16
16
  with:
17
17
  license-check: true
18
+ lint: true
package/.taprc CHANGED
@@ -1,3 +1,9 @@
1
1
  ts: false
2
2
  jsx: false
3
- coverage: false
3
+ branches: 65
4
+ functions: 100
5
+ lines: 89
6
+ statements: 89
7
+ nyc-arg:
8
+ - "--exclude=.cache/*"
9
+ - "--exclude=lib/schema-validator.js"
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.schemaId
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,42 @@ function resolveRef (location, ref) {
63
56
  throw new Error(`Cannot find reference "${ref}"`)
64
57
  }
65
58
 
59
+ if (location.isValidated) {
60
+ validatorSchemasIds.add(schemaId)
61
+ }
62
+
63
+ const newLocation = new Location(schema, schemaId, jsonPointer, location.isValidated)
66
64
  if (schema.$ref !== undefined) {
67
- return resolveRef({ schema, schemaId, jsonPointer }, schema.$ref)
65
+ return resolveRef(newLocation, schema.$ref)
68
66
  }
69
67
 
70
- return { schema, schemaId, jsonPointer }
68
+ return newLocation
71
69
  }
72
70
 
73
71
  const contextFunctionsNamesBySchema = new Map()
74
72
 
75
73
  let rootSchemaId = null
76
74
  let refResolver = null
77
- let validator = null
78
75
  let contextFunctions = null
76
+ let validatorSchemasIds = null
79
77
 
80
78
  function build (schema, options) {
81
79
  contextFunctionsNamesBySchema.clear()
82
80
 
83
81
  contextFunctions = []
82
+ validatorSchemasIds = new Set()
84
83
  options = options || {}
85
84
 
86
85
  refResolver = new RefResolver()
87
- validator = new Validator(options.ajv)
88
86
 
89
87
  rootSchemaId = schema.$id || randomUUID()
90
88
 
91
89
  isValidSchema(schema)
92
- validator.addSchema(schema, rootSchemaId)
93
90
  refResolver.addSchema(schema, rootSchemaId)
94
91
 
95
92
  if (options.schema) {
96
93
  for (const key of Object.keys(options.schema)) {
97
94
  isValidSchema(options.schema[key], key)
98
- validator.addSchema(options.schema[key], key)
99
95
  refResolver.addSchema(options.schema[key], key)
100
96
  }
101
97
  }
@@ -110,7 +106,7 @@ function build (schema, options) {
110
106
  if (validLargeArrayMechanisms.includes(options.largeArrayMechanism)) {
111
107
  largeArrayMechanism = options.largeArrayMechanism
112
108
  } else {
113
- throw new Error(`Unsupported large array mechanism ${options.rounding}`)
109
+ throw new Error(`Unsupported large array mechanism ${options.largeArrayMechanism}`)
114
110
  }
115
111
  }
116
112
 
@@ -122,9 +118,7 @@ function build (schema, options) {
122
118
  }
123
119
  }
124
120
 
125
- const serializer = new Serializer(options)
126
-
127
- const location = { schema, schemaId: rootSchemaId, jsonPointer: '#' }
121
+ const location = new Location(schema, rootSchemaId)
128
122
  const code = buildValue(location, 'input')
129
123
 
130
124
  const contextFunctionCode = `
@@ -135,7 +129,15 @@ function build (schema, options) {
135
129
  }
136
130
  ${contextFunctions.join('\n')}
137
131
  return main
138
- `
132
+ `
133
+
134
+ const serializer = new Serializer(options)
135
+ const validator = new Validator(options.ajv)
136
+
137
+ for (const schemaId of validatorSchemasIds) {
138
+ const schema = refResolver.getSchema(schemaId)
139
+ validator.addSchema(schema, schemaId)
140
+ }
139
141
 
140
142
  const dependenciesName = ['validator', 'serializer', contextFunctionCode]
141
143
 
@@ -154,8 +156,9 @@ function build (schema, options) {
154
156
 
155
157
  if (options.mode === 'standalone') {
156
158
  // lazy load
159
+ const isValidatorUsed = validatorSchemasIds.size > 0
157
160
  const buildStandaloneCode = require('./lib/standalone')
158
- return buildStandaloneCode(options, validator, contextFunctionCode)
161
+ return buildStandaloneCode(options, validator, isValidatorUsed, contextFunctionCode)
159
162
  }
160
163
 
161
164
  /* eslint no-new-func: "off" */
@@ -163,9 +166,9 @@ function build (schema, options) {
163
166
  const stringifyFunc = contextFunc(validator, serializer)
164
167
 
165
168
  refResolver = null
166
- validator = null
167
169
  rootSchemaId = null
168
170
  contextFunctions = null
171
+ validatorSchemasIds = null
169
172
  contextFunctionsNamesBySchema.clear()
170
173
 
171
174
  return stringifyFunc
@@ -228,106 +231,82 @@ function inferTypeByKeyword (schema) {
228
231
  return schema.type
229
232
  }
230
233
 
231
- function addPatternProperties (location) {
234
+ function buildExtraObjectPropertiesSerializer (location) {
232
235
  const schema = location.schema
233
- const pp = schema.patternProperties
236
+ const propertiesKeys = Object.keys(schema.properties || {})
237
+
234
238
  let code = `
235
- var properties = ${JSON.stringify(schema.properties)} || {}
236
- var keys = Object.keys(obj)
237
- for (var i = 0; i < keys.length; i++) {
238
- if (properties[keys[i]]) continue
239
+ const propertiesKeys = ${JSON.stringify(propertiesKeys)}
240
+ for (const [key, value] of Object.entries(obj)) {
241
+ if (
242
+ propertiesKeys.includes(key) ||
243
+ value === undefined ||
244
+ typeof value === 'function' ||
245
+ typeof value === 'symbol'
246
+ ) continue
239
247
  `
240
248
 
241
- const patternPropertiesLocation = mergeLocation(location, 'patternProperties')
242
- Object.keys(pp).forEach((regex) => {
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
- }
249
+ const patternPropertiesLocation = location.getPropertyLocation('patternProperties')
250
+ const patternPropertiesSchema = patternPropertiesLocation.schema
254
251
 
255
- const valueCode = buildValue(ppLocation, 'obj[keys[i]]')
256
- code += `
257
- if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {
258
- ${addComma}
259
- json += serializer.asString(keys[i]) + ':'
260
- ${valueCode}
261
- continue
262
- }
263
- `
264
- })
265
- if (schema.additionalProperties) {
266
- code += additionalProperty(location)
267
- }
252
+ if (patternPropertiesSchema !== undefined) {
253
+ for (const propertyKey in patternPropertiesSchema) {
254
+ const propertyLocation = patternPropertiesLocation.getPropertyLocation(propertyKey)
268
255
 
269
- code += `
256
+ try {
257
+ RegExp(propertyKey)
258
+ } catch (err) {
259
+ const jsonPointer = propertyLocation.getSchemaRef()
260
+ throw new Error(`${err.message}. Invalid pattern property regexp key ${propertyKey} at ${jsonPointer}`)
270
261
  }
271
- `
272
- return code
273
- }
274
262
 
275
- function additionalProperty (location) {
276
- const ap = location.schema.additionalProperties
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') {
263
+ code += `
264
+ if (/${propertyKey.replace(/\\*\//g, '\\/')}/.test(key)) {
281
265
  ${addComma}
282
- json += serializer.asString(keys[i]) + ':' + JSON.stringify(obj[keys[i]])
266
+ json += serializer.asString(key) + ':'
267
+ ${buildValue(propertyLocation, 'value')}
268
+ continue
283
269
  }
284
- `
285
-
286
- return code
270
+ `
271
+ }
287
272
  }
288
273
 
289
- let apLocation = mergeLocation(location, 'additionalProperties')
290
- if (apLocation.schema.$ref) {
291
- apLocation = resolveRef(apLocation, apLocation.schema.$ref)
292
- }
274
+ const additionalPropertiesLocation = location.getPropertyLocation('additionalProperties')
275
+ const additionalPropertiesSchema = additionalPropertiesLocation.schema
293
276
 
294
- const valueCode = buildValue(apLocation, 'obj[keys[i]]')
277
+ if (additionalPropertiesSchema !== undefined) {
278
+ if (additionalPropertiesSchema === true) {
279
+ code += `
280
+ ${addComma}
281
+ json += serializer.asString(key) + ':' + JSON.stringify(value)
282
+ `
283
+ } else {
284
+ const propertyLocation = location.getPropertyLocation('additionalProperties')
285
+ code += `
286
+ ${addComma}
287
+ json += serializer.asString(key) + ':'
288
+ ${buildValue(propertyLocation, 'value')}
289
+ `
290
+ }
291
+ }
295
292
 
296
293
  code += `
297
- ${addComma}
298
- json += serializer.asString(keys[i]) + ':'
299
- ${valueCode}
294
+ }
300
295
  `
301
-
302
296
  return code
303
297
  }
304
298
 
305
- function addAdditionalProperties (location) {
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
-
299
+ function buildInnerObject (location) {
321
300
  const schema = location.schema
322
301
  const required = schema.required || []
323
302
 
324
303
  let code = ''
325
304
 
326
- const propertiesLocation = mergeLocation(location, 'properties')
305
+ const propertiesLocation = location.getPropertyLocation('properties')
327
306
  Object.keys(schema.properties || {}).forEach((key) => {
328
- let propertyLocation = mergeLocation(propertiesLocation, key)
329
- if (propertyLocation.$ref) {
330
- propertyLocation = resolveRef(location, propertyLocation.$ref)
307
+ let propertyLocation = propertiesLocation.getPropertyLocation(key)
308
+ if (propertyLocation.schema.$ref) {
309
+ propertyLocation = resolveRef(location, propertyLocation.schema.$ref)
331
310
  }
332
311
 
333
312
  const sanitized = JSON.stringify(key)
@@ -368,17 +347,21 @@ function buildCode (location) {
368
347
  code += `if (obj['${requiredProperty}'] === undefined) throw new Error('"${requiredProperty}" is required!')\n`
369
348
  }
370
349
 
350
+ if (schema.patternProperties || schema.additionalProperties) {
351
+ code += buildExtraObjectPropertiesSerializer(location)
352
+ }
353
+
371
354
  return code
372
355
  }
373
356
 
374
357
  function mergeAllOfSchema (location, schema, mergedSchema) {
375
- const allOfLocation = mergeLocation(location, 'allOf')
358
+ const allOfLocation = location.getPropertyLocation('allOf')
376
359
 
377
360
  for (let i = 0; i < schema.allOf.length; i++) {
378
361
  let allOfSchema = schema.allOf[i]
379
362
 
380
363
  if (allOfSchema.$ref) {
381
- const allOfSchemaLocation = mergeLocation(allOfLocation, i)
364
+ const allOfSchemaLocation = allOfLocation.getPropertyLocation(i)
382
365
  allOfSchema = resolveRef(allOfSchemaLocation, allOfSchema.$ref).schema
383
366
  }
384
367
 
@@ -466,24 +449,14 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
466
449
  delete mergedSchema.allOf
467
450
 
468
451
  mergedSchema.$id = `merged_${randomUUID()}`
469
- validator.addSchema(mergedSchema)
470
452
  refResolver.addSchema(mergedSchema)
471
- location.schemaId = mergedSchema.$id
472
- location.jsonPointer = '#'
453
+ location.addMergedSchema(mergedSchema, mergedSchema.$id)
473
454
  }
474
455
 
475
- function buildInnerObject (location) {
476
- const schema = location.schema
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
- }
456
+ function addIfThenElse (location, input) {
457
+ location.isValidated = true
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 = mergeLocation(location, 'if')
496
- const ifSchemaRef = ifLocation.schemaId + ifLocation.jsonPointer
468
+ const ifLocation = location.getPropertyLocation('if')
469
+ const ifSchemaRef = ifLocation.getSchemaRef()
497
470
 
498
- let code = `
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
- if (thenSchema.if && thenSchema.then) {
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
- code += `
518
- else {
519
- `
520
-
521
- if (elseSchema.if && elseSchema.then) {
522
- code += addIfThenElse(elseLocation)
523
- } else {
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
- const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
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
- // ${schemaId + location.jsonPointer}
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
- if (schema.if && schema.then) {
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 = mergeLocation(location, 'items')
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
- const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
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
- // ${schemaId + location.jsonPointer}
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(mergeLocation(itemsLocation, i), `obj[${i}]`)
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
- const mergedSchema = clone(schema)
874
- mergeAllOfSchema(location, schema, mergedSchema)
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,15 @@ function buildValue (location, input) {
881
840
  let code = ''
882
841
 
883
842
  if (type === undefined && (schema.anyOf || schema.oneOf)) {
843
+ location.isValidated = true
844
+ validatorSchemasIds.add(location.getSchemaId())
845
+
884
846
  const type = schema.anyOf ? 'anyOf' : 'oneOf'
885
- const anyOfLocation = mergeLocation(location, type)
847
+ const anyOfLocation = location.getPropertyLocation(type)
886
848
 
887
849
  for (let index = 0; index < location.schema[type].length; index++) {
888
- const optionLocation = mergeLocation(anyOfLocation, index)
889
- const schemaRef = optionLocation.schemaId + optionLocation.jsonPointer
850
+ const optionLocation = anyOfLocation.getPropertyLocation(index)
851
+ const schemaRef = optionLocation.getSchemaRef()
890
852
  const nestedResult = buildValue(optionLocation, input)
891
853
  code += `
892
854
  ${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
@@ -0,0 +1,55 @@
1
+ 'use strict'
2
+
3
+ class Location {
4
+ constructor (schema, schemaId, jsonPointer = '#', isValidated = false) {
5
+ this.schema = schema
6
+ this.schemaId = schemaId
7
+ this.jsonPointer = jsonPointer
8
+ this.isValidated = isValidated
9
+ this.mergedSchemaId = null
10
+ }
11
+
12
+ getPropertyLocation (propertyName) {
13
+ const propertyLocation = new Location(
14
+ this.schema[propertyName],
15
+ this.schemaId,
16
+ this.jsonPointer + '/' + propertyName,
17
+ this.isValidated
18
+ )
19
+
20
+ if (this.mergedSchemaId !== null) {
21
+ propertyLocation.addMergedSchema(
22
+ this.schema[propertyName],
23
+ this.mergedSchemaId,
24
+ this.jsonPointer + '/' + propertyName
25
+ )
26
+ }
27
+
28
+ return propertyLocation
29
+ }
30
+
31
+ // Use this method to get current schema location.
32
+ // Use it when you need to create reference to the current location.
33
+ getSchemaId () {
34
+ return this.mergedSchemaId || this.schemaId
35
+ }
36
+
37
+ // Use this method to get original schema id for resolving user schema $refs
38
+ // Don't join it with a JSON pointer to get the current location.
39
+ getOriginSchemaId () {
40
+ return this.schemaId
41
+ }
42
+
43
+ getSchemaRef () {
44
+ const schemaId = this.getSchemaId()
45
+ return schemaId + this.jsonPointer
46
+ }
47
+
48
+ addMergedSchema (mergedSchema, schemaId, jsonPointer = '#') {
49
+ this.schema = mergedSchema
50
+ this.mergedSchemaId = schemaId
51
+ this.jsonPointer = jsonPointer
52
+ }
53
+ }
54
+
55
+ module.exports = Location
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
- if (shouldUseAjv) {
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,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-json-stringify",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "description": "Stringify your JSON at max speed",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -9,13 +9,13 @@
9
9
  "bench:cmp": "node ./benchmark/bench-cmp-branch.js",
10
10
  "bench:cmp:ci": "node ./benchmark/bench-cmp-branch.js --ci",
11
11
  "benchmark": "node ./benchmark/bench-cmp-lib.js",
12
+ "lint": "standard",
12
13
  "lint:fix": "standard --fix",
13
- "test:lint": "standard",
14
14
  "test:typescript": "tsc --project ./test/types/tsconfig.json && tsd",
15
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"
16
+ "test": "npm run test:unit && npm run test:typescript"
17
17
  },
18
- "precommit": "test",
18
+ "precommit": ["lint", "test"],
19
19
  "repository": {
20
20
  "type": "git",
21
21
  "url": "git+https://github.com/fastify/fast-json-stringify.git"
@@ -45,7 +45,7 @@
45
45
  "simple-git": "^3.7.1",
46
46
  "standard": "^17.0.0",
47
47
  "tap": "^16.0.1",
48
- "tsd": "^0.22.0",
48
+ "tsd": "^0.24.1",
49
49
  "typescript": "^4.0.2",
50
50
  "webpack": "^5.40.0"
51
51
  },
@@ -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
+ })
@@ -596,3 +596,23 @@ 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
+ })
@@ -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
+ })