fast-json-stringify 1.20.2 → 1.21.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/index.js CHANGED
@@ -383,6 +383,11 @@ function addPatternProperties (schema, externalSchema, fullSchema) {
383
383
  var type = pp[regex].type
384
384
  var format = pp[regex].format
385
385
  var stringSerializer = getStringSerializer(format)
386
+ try {
387
+ RegExp(regex)
388
+ } catch (err) {
389
+ throw new Error(`${err.message}. Found at ${regex} matching ${JSON.stringify(pp[regex])}`)
390
+ }
386
391
  code += `
387
392
  if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {
388
393
  `
@@ -425,7 +430,7 @@ function addPatternProperties (schema, externalSchema, fullSchema) {
425
430
  `
426
431
  } else {
427
432
  code += `
428
- throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ${type}')
433
+ throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
429
434
  `
430
435
  }
431
436
 
@@ -518,7 +523,7 @@ function additionalProperty (schema, externalSchema, fullSchema) {
518
523
  `
519
524
  } else {
520
525
  code += `
521
- throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ${type}')
526
+ throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
522
527
  `
523
528
  }
524
529
  return code
@@ -587,7 +592,7 @@ function refFinder (ref, schema, externalSchema) {
587
592
  return dereferenced
588
593
  } else {
589
594
  for (var i = 1; i < walk.length; i++) {
590
- code += `['${sanitizeKey(walk[i])}']`
595
+ code += `[${JSON.stringify(walk[i])}]`
591
596
  }
592
597
  }
593
598
  }
@@ -603,35 +608,27 @@ function refFinder (ref, schema, externalSchema) {
603
608
 
604
609
  function findBadKey (obj, keys) {
605
610
  if (keys.length === 0) return null
606
- const key = sanitizeKey(keys.shift())
611
+ const key = keys.shift()
607
612
  if (obj[key] === undefined) {
608
613
  stringSimilarity = stringSimilarity || require('string-similarity')
609
614
  const { bestMatch } = stringSimilarity.findBestMatch(key, Object.keys(obj))
610
615
  if (bestMatch.rating >= 0.5) {
611
- throw new Error(`Cannot find reference '${key}', did you mean '${bestMatch.target}'?`)
616
+ throw new Error(`Cannot find reference ${JSON.stringify(key)}, did you mean ${JSON.stringify(bestMatch.target)}?`)
612
617
  } else {
613
- throw new Error(`Cannot find reference '${key}'`)
618
+ throw new Error(`Cannot find reference ${JSON.stringify(key)}`)
614
619
  }
615
620
  }
616
621
  return findBadKey(obj[key], keys)
617
622
  }
618
623
  }
619
624
 
620
- function sanitizeKey (key) {
621
- const rep = key.replace(/(\\*)'/g, function (match, p1) {
622
- var base = ''
623
- if (p1.length % 2 === 1) {
624
- base = p1.slice(2)
625
- } else {
626
- base = p1
627
- }
628
- var rep = base + '\\\''
629
- return rep
630
- })
631
- return rep
632
- }
633
-
634
625
  function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
626
+ if (schema.$ref) {
627
+ schema = refFinder(schema.$ref, fullSchema, externalSchema)
628
+ }
629
+
630
+ var required = schema.required
631
+
635
632
  Object.keys(schema.properties || {}).forEach((key, i, a) => {
636
633
  if (schema.properties[key].$ref) {
637
634
  // if the schema object is deep in the tree, we must resolve the ref in the parent scope
@@ -644,14 +641,14 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
644
641
 
645
642
  var type = schema.properties[key].type
646
643
  var nullable = schema.properties[key].nullable
647
- var sanitized = sanitizeKey(key)
648
- var asString = sanitizeKey($asString(key).replace(/\\/g, '\\\\'))
644
+ var sanitized = JSON.stringify(key)
645
+ var asString = JSON.stringify(sanitized)
649
646
 
650
647
  if (nullable) {
651
648
  code += `
652
- if (obj['${sanitized}'] === null) {
649
+ if (obj[${sanitized}] === null) {
653
650
  ${addComma}
654
- json += '${asString}:null'
651
+ json += ${asString} + ':null'
655
652
  var rendered = true
656
653
  } else {
657
654
  `
@@ -659,10 +656,10 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
659
656
 
660
657
  if (type === 'number') {
661
658
  code += `
662
- var t = Number(obj['${sanitized}'])
659
+ var t = Number(obj[${sanitized}])
663
660
  if (!isNaN(t)) {
664
661
  ${addComma}
665
- json += '${asString}:' + t
662
+ json += ${asString} + ':' + t
666
663
  `
667
664
  } else if (type === 'integer') {
668
665
  code += `
@@ -670,25 +667,25 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
670
667
  `
671
668
  if (isLong) {
672
669
  code += `
673
- if (isLong(obj['${sanitized}'])) {
670
+ if (isLong(obj[${sanitized}])) {
674
671
  ${addComma}
675
- json += '${asString}:' + obj['${sanitized}'].toString()
672
+ json += ${asString} + ':' + obj[${sanitized}].toString()
676
673
  rendered = true
677
674
  } else {
678
- var t = Number(obj['${sanitized}'])
675
+ var t = Number(obj[${sanitized}])
679
676
  if (!isNaN(t)) {
680
677
  ${addComma}
681
- json += '${asString}:' + t
678
+ json += ${asString} + ':' + t
682
679
  rendered = true
683
680
  }
684
681
  }
685
682
  `
686
683
  } else {
687
684
  code += `
688
- var t = Number(obj['${sanitized}'])
685
+ var t = Number(obj[${sanitized}])
689
686
  if (!isNaN(t)) {
690
687
  ${addComma}
691
- json += '${asString}:' + t
688
+ json += ${asString} + ':' + t
692
689
  rendered = true
693
690
  }
694
691
  `
@@ -698,24 +695,26 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
698
695
  `
699
696
  } else {
700
697
  code += `
701
- if (obj['${sanitized}'] !== undefined) {
698
+ if (obj[${sanitized}] !== undefined) {
702
699
  ${addComma}
703
- json += '${asString}:'
700
+ json += ${asString} + ':'
704
701
  `
705
702
 
706
- var result = nested(laterCode, name, key, schema.properties[key], externalSchema, fullSchema)
703
+ var result = nested(laterCode, name, key, schema.properties[key], externalSchema, fullSchema, undefined, false)
707
704
  code += result.code
708
705
  laterCode = result.laterCode
709
706
  }
710
707
 
711
708
  var defaultValue = schema.properties[key].default
712
709
  if (defaultValue !== undefined) {
710
+ required = filterRequired(schema.required, key)
713
711
  code += `
714
712
  } else {
715
713
  ${addComma}
716
- json += '${asString}:${sanitizeKey(JSON.stringify(defaultValue).replace(/\\/g, '\\\\'))}'
714
+ json += ${asString} + ':' + ${JSON.stringify(JSON.stringify(defaultValue))}
717
715
  `
718
716
  } else if (schema.required && schema.required.indexOf(key) !== -1) {
717
+ required = filterRequired(schema.required, key)
719
718
  code += `
720
719
  } else {
721
720
  throw new Error('${sanitized} is required!')
@@ -732,9 +731,33 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) {
732
731
  `
733
732
  }
734
733
  })
734
+
735
+ if (required && required.length > 0) {
736
+ code += 'var required = ['
737
+ for (var i = 0; i < required.length; i++) {
738
+ if (i > 0) {
739
+ code += ','
740
+ }
741
+ code += `${JSON.stringify(required[i])}`
742
+ }
743
+ code += ']'
744
+ code += `
745
+ for (var i = 0; i < required.length; i++) {
746
+ if (obj[required[i]] === undefined) throw new Error('"' + required[i] + '" is required!')
747
+ }
748
+ `
749
+ }
750
+
735
751
  return { code: code, laterCode: laterCode }
736
752
  }
737
753
 
754
+ function filterRequired (required, key) {
755
+ if (!required) {
756
+ return required
757
+ }
758
+ return required.filter(k => k !== key)
759
+ }
760
+
738
761
  function buildCodeWithAllOfs (schema, code, laterCode, name, externalSchema, fullSchema) {
739
762
  if (schema.allOf) {
740
763
  schema.allOf.forEach((ss) => {
@@ -890,7 +913,7 @@ function buildArray (schema, code, name, externalSchema, fullSchema) {
890
913
  if (Array.isArray(schema.items)) {
891
914
  result = schema.items.reduce((res, item, i) => {
892
915
  var accessor = '[i]'
893
- const tmpRes = nested(laterCode, name, accessor, item, externalSchema, fullSchema, i)
916
+ const tmpRes = nested(laterCode, name, accessor, item, externalSchema, fullSchema, i, true)
894
917
  var condition = `i === ${i} && ${buildArrayTypeCondition(item.type, accessor)}`
895
918
  return {
896
919
  code: `${res.code}
@@ -907,7 +930,7 @@ function buildArray (schema, code, name, externalSchema, fullSchema) {
907
930
  }
908
931
  `
909
932
  } else {
910
- result = nested(laterCode, name, '[i]', schema.items, externalSchema, fullSchema)
933
+ result = nested(laterCode, name, '[i]', schema.items, externalSchema, fullSchema, undefined, true)
911
934
  }
912
935
 
913
936
  code += `
@@ -981,7 +1004,21 @@ function dereferenceOfRefs (schema, externalSchema, fullSchema, type) {
981
1004
  })
982
1005
  }
983
1006
 
984
- function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKey) {
1007
+ var strNameCounter = 0
1008
+ function asFuncName (str) {
1009
+ // only allow chars that can work
1010
+ var rep = str.replace(/[^a-zA-Z0-9$_]/g, '')
1011
+
1012
+ if (rep.length === 0) {
1013
+ return 'anan' + strNameCounter++
1014
+ } else if (rep !== str) {
1015
+ rep += strNameCounter++
1016
+ }
1017
+
1018
+ return rep
1019
+ }
1020
+
1021
+ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKey, isArray) {
985
1022
  var code = ''
986
1023
  var funcName
987
1024
 
@@ -1001,7 +1038,8 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
1001
1038
  var type = schema.type
1002
1039
  var nullable = schema.nullable === true
1003
1040
 
1004
- var accessor = key.indexOf('[') === 0 ? sanitizeKey(key) : `['${sanitizeKey(key)}']`
1041
+ var accessor = isArray ? key : `[${JSON.stringify(key)}]`
1042
+
1005
1043
  switch (type) {
1006
1044
  case 'null':
1007
1045
  code += `
@@ -1022,14 +1060,14 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
1022
1060
  code += nullable ? `json += obj${accessor} === null ? null : $asBoolean(obj${accessor})` : `json += $asBoolean(obj${accessor})`
1023
1061
  break
1024
1062
  case 'object':
1025
- funcName = (name + key + subKey).replace(/[-.\[\] ]/g, '').replace(/[@]/g, 'AT_SYMBOL') // eslint-disable-line
1063
+ funcName = asFuncName(name + key + subKey)
1026
1064
  laterCode = buildObject(schema, laterCode, funcName, externalSchema, fullSchema)
1027
1065
  code += `
1028
1066
  json += ${funcName}(obj${accessor})
1029
1067
  `
1030
1068
  break
1031
1069
  case 'array':
1032
- funcName = '$arr' + (name + key + subKey).replace(/[-.\[\] ]/g, '').replace(/[@]/g, 'AT_SYMBOL') // eslint-disable-line
1070
+ funcName = asFuncName('$arr' + name + key + subKey) // eslint-disable-line
1033
1071
  laterCode = buildArray(schema, laterCode, funcName, externalSchema, fullSchema)
1034
1072
  code += `
1035
1073
  json += ${funcName}(obj${accessor})
@@ -1039,7 +1077,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
1039
1077
  if ('anyOf' in schema) {
1040
1078
  dereferenceOfRefs(schema, externalSchema, fullSchema, 'anyOf')
1041
1079
  schema.anyOf.forEach((s, index) => {
1042
- var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index)
1080
+ var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index, isArray)
1043
1081
  code += `
1044
1082
  ${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(s, { depth: null })}, obj${accessor}))
1045
1083
  ${nestedResult.code}
@@ -1052,7 +1090,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
1052
1090
  } else if ('oneOf' in schema) {
1053
1091
  dereferenceOfRefs(schema, externalSchema, fullSchema, 'oneOf')
1054
1092
  schema.oneOf.forEach((s, index) => {
1055
- var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index)
1093
+ var nestedResult = nested(laterCode, name, key, s, externalSchema, fullSchema, subKey !== '' ? subKey : 'i' + index, isArray)
1056
1094
  code += `
1057
1095
  ${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(s, { depth: null })}, obj${accessor}))
1058
1096
  ${nestedResult.code}
@@ -1076,7 +1114,7 @@ function nested (laterCode, name, key, schema, externalSchema, fullSchema, subKe
1076
1114
  const sortedTypes = nullIndex !== -1 ? [type[nullIndex]].concat(type.slice(0, nullIndex)).concat(type.slice(nullIndex + 1)) : type
1077
1115
  sortedTypes.forEach((type, index) => {
1078
1116
  var tempSchema = Object.assign({}, schema, { type })
1079
- var nestedResult = nested(laterCode, name, key, tempSchema, externalSchema, fullSchema, subKey)
1117
+ var nestedResult = nested(laterCode, name, key, tempSchema, externalSchema, fullSchema, subKey, isArray)
1080
1118
 
1081
1119
  if (type === 'string') {
1082
1120
  code += `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-json-stringify",
3
- "version": "1.20.2",
3
+ "version": "1.21.0",
4
4
  "description": "Stringify your JSON at max speed",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -44,7 +44,7 @@ test('object with allOf and multiple schema on the allOf', (t) => {
44
44
  id: 1
45
45
  })
46
46
  } catch (e) {
47
- t.is(e.message, 'name is required!')
47
+ t.is(e.message, '"name" is required!')
48
48
  }
49
49
 
50
50
  try {
@@ -52,7 +52,7 @@ test('object with allOf and multiple schema on the allOf', (t) => {
52
52
  name: 'string'
53
53
  })
54
54
  } catch (e) {
55
- t.is(e.message, 'id is required!')
55
+ t.is(e.message, '"id" is required!')
56
56
  }
57
57
 
58
58
  try {
@@ -135,7 +135,7 @@ test('object with nested allOfs', (t) => {
135
135
  allOf: [
136
136
  {
137
137
  required: [
138
- 'id'
138
+ 'id1'
139
139
  ],
140
140
  type: 'object',
141
141
  properties: {
@@ -184,3 +184,200 @@ test('object with nested allOfs', (t) => {
184
184
  t.fail()
185
185
  }
186
186
  })
187
+
188
+ test('object with $ref in allOf', (t) => {
189
+ t.plan(1)
190
+
191
+ const schema = {
192
+ title: 'object with $ref in allOf',
193
+ type: 'object',
194
+ definitions: {
195
+ id1: {
196
+ type: 'object',
197
+ properties: {
198
+ id1: {
199
+ type: 'integer'
200
+ }
201
+ }
202
+ }
203
+ },
204
+ allOf: [
205
+ {
206
+ $ref: '#/definitions/id1'
207
+ }
208
+ ]
209
+ }
210
+
211
+ try {
212
+ const stringify = build(schema)
213
+ try {
214
+ const value = stringify({
215
+ id1: 1,
216
+ id2: 2 // extra prop shouldn't be in result
217
+ })
218
+ t.is(value, '{"id1":1}')
219
+ } catch (e) {
220
+ t.fail()
221
+ }
222
+ } catch (e) {
223
+ t.fail()
224
+ }
225
+ })
226
+
227
+ test('object with $ref and other object in allOf', (t) => {
228
+ t.plan(1)
229
+
230
+ const schema = {
231
+ title: 'object with $ref in allOf',
232
+ type: 'object',
233
+ definitions: {
234
+ id1: {
235
+ type: 'object',
236
+ properties: {
237
+ id1: {
238
+ type: 'integer'
239
+ }
240
+ }
241
+ }
242
+ },
243
+ allOf: [
244
+ {
245
+ $ref: '#/definitions/id1'
246
+ },
247
+ {
248
+ type: 'object',
249
+ properties: {
250
+ id2: {
251
+ type: 'integer'
252
+ }
253
+ }
254
+ }
255
+ ]
256
+ }
257
+
258
+ try {
259
+ const stringify = build(schema)
260
+ try {
261
+ const value = stringify({
262
+ id1: 1,
263
+ id2: 2,
264
+ id3: 3 // extra prop shouldn't be in result
265
+ })
266
+ t.is(value, '{"id1":1,"id2":2}')
267
+ } catch (e) {
268
+ t.fail()
269
+ }
270
+ } catch (e) {
271
+ t.fail()
272
+ }
273
+ })
274
+
275
+ test('object with multiple $refs in allOf', (t) => {
276
+ t.plan(1)
277
+
278
+ const schema = {
279
+ title: 'object with $ref in allOf',
280
+ type: 'object',
281
+ definitions: {
282
+ id1: {
283
+ type: 'object',
284
+ properties: {
285
+ id1: {
286
+ type: 'integer'
287
+ }
288
+ }
289
+ },
290
+ id2: {
291
+ type: 'object',
292
+ properties: {
293
+ id2: {
294
+ type: 'integer'
295
+ }
296
+ }
297
+ }
298
+ },
299
+ allOf: [
300
+ {
301
+ $ref: '#/definitions/id1'
302
+ },
303
+ {
304
+ $ref: '#/definitions/id2'
305
+ }
306
+ ]
307
+ }
308
+
309
+ try {
310
+ const stringify = build(schema)
311
+ try {
312
+ const value = stringify({
313
+ id1: 1,
314
+ id2: 2,
315
+ id3: 3 // extra prop shouldn't be in result
316
+ })
317
+ t.is(value, '{"id1":1,"id2":2}')
318
+ } catch (e) {
319
+ t.fail()
320
+ }
321
+ } catch (e) {
322
+ t.fail()
323
+ }
324
+ })
325
+
326
+ test('object with external $refs in allOf', (t) => {
327
+ t.plan(1)
328
+
329
+ const externalSchema = {
330
+ first: {
331
+ definitions: {
332
+ id1: {
333
+ type: 'object',
334
+ properties: {
335
+ id1: {
336
+ type: 'integer'
337
+ }
338
+ }
339
+ }
340
+ }
341
+ },
342
+ second: {
343
+ id2: {
344
+ $id: '#id2',
345
+ type: 'object',
346
+ properties: {
347
+ id2: {
348
+ type: 'integer'
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ const schema = {
356
+ title: 'object with $ref in allOf',
357
+ type: 'object',
358
+ allOf: [
359
+ {
360
+ $ref: 'first#/definitions/id1'
361
+ },
362
+ {
363
+ $ref: 'second#id2'
364
+ }
365
+ ]
366
+ }
367
+
368
+ try {
369
+ const stringify = build(schema, { schema: externalSchema })
370
+ try {
371
+ const value = stringify({
372
+ id1: 1,
373
+ id2: 2,
374
+ id3: 3 // extra prop shouldn't be in result
375
+ })
376
+ t.is(value, '{"id1":1,"id2":2}')
377
+ } catch (e) {
378
+ t.fail()
379
+ }
380
+ } catch (e) {
381
+ t.fail()
382
+ }
383
+ })
package/test/ref.test.js CHANGED
@@ -820,7 +820,7 @@ test('ref external to relative definition', (t) => {
820
820
 
821
821
  const schema = {
822
822
  type: 'object',
823
- required: ['foo'],
823
+ required: ['fooParent'],
824
824
  properties: {
825
825
  fooParent: { $ref: 'relative:to:local' }
826
826
  }
@@ -934,7 +934,7 @@ test('Bad key', t => {
934
934
  })
935
935
  t.fail('Should throw')
936
936
  } catch (err) {
937
- t.is(err.message, "Cannot find reference 'porjectId', did you mean 'projectId'?")
937
+ t.is(err.message, 'Cannot find reference "porjectId", did you mean "projectId"?')
938
938
  }
939
939
  })
940
940
 
@@ -959,7 +959,7 @@ test('Bad key', t => {
959
959
  })
960
960
  t.fail('Should throw')
961
961
  } catch (err) {
962
- t.is(err.message, "Cannot find reference 'foobar'")
962
+ t.is(err.message, 'Cannot find reference "foobar"')
963
963
  }
964
964
  })
965
965
 
@@ -989,7 +989,7 @@ test('Bad key', t => {
989
989
  })
990
990
  t.fail('Should throw')
991
991
  } catch (err) {
992
- t.is(err.message, "Cannot find reference 'porjectId', did you mean 'projectId'?")
992
+ t.is(err.message, 'Cannot find reference "porjectId", did you mean "projectId"?')
993
993
  }
994
994
  })
995
995
 
@@ -1019,7 +1019,7 @@ test('Bad key', t => {
1019
1019
  })
1020
1020
  t.fail('Should throw')
1021
1021
  } catch (err) {
1022
- t.is(err.message, "Cannot find reference 'foobar'")
1022
+ t.is(err.message, 'Cannot find reference "foobar"')
1023
1023
  }
1024
1024
  })
1025
1025
 
@@ -1049,7 +1049,7 @@ test('Bad key', t => {
1049
1049
  })
1050
1050
  t.fail('Should throw')
1051
1051
  } catch (err) {
1052
- t.is(err.message, "Cannot find reference 'deifnitions', did you mean 'definitions'?")
1052
+ t.is(err.message, 'Cannot find reference "deifnitions", did you mean "definitions"?')
1053
1053
  }
1054
1054
  })
1055
1055
 
@@ -1074,7 +1074,7 @@ test('Bad key', t => {
1074
1074
  })
1075
1075
  t.fail('Should throw')
1076
1076
  } catch (err) {
1077
- t.is(err.message, "Cannot find reference 'deifnitions', did you mean 'definitions'?")
1077
+ t.is(err.message, 'Cannot find reference "deifnitions", did you mean "definitions"?')
1078
1078
  }
1079
1079
  })
1080
1080
 
@@ -1104,7 +1104,7 @@ test('Bad key', t => {
1104
1104
  })
1105
1105
  t.fail('Should throw')
1106
1106
  } catch (err) {
1107
- t.is(err.message, "Cannot find reference 'extrenal', did you mean 'external'?")
1107
+ t.is(err.message, 'Cannot find reference "extrenal", did you mean "external"?')
1108
1108
  }
1109
1109
  })
1110
1110
 
@@ -36,11 +36,186 @@ test('object with required field', (t) => {
36
36
  })
37
37
  t.fail()
38
38
  } catch (e) {
39
- t.is(e.message, 'str is required!')
39
+ t.is(e.message, '"str" is required!')
40
40
  t.pass()
41
41
  }
42
42
  })
43
43
 
44
+ test('object with required field not in properties schema', (t) => {
45
+ t.plan(4)
46
+
47
+ const schema = {
48
+ title: 'object with required field',
49
+ type: 'object',
50
+ properties: {
51
+ num: {
52
+ type: 'integer'
53
+ }
54
+ },
55
+ required: ['str']
56
+ }
57
+ const stringify = build(schema)
58
+
59
+ try {
60
+ stringify({})
61
+ t.fail()
62
+ } catch (e) {
63
+ t.is(e.message, '"str" is required!')
64
+ t.pass()
65
+ }
66
+
67
+ try {
68
+ stringify({
69
+ num: 42
70
+ })
71
+ t.fail()
72
+ } catch (e) {
73
+ t.is(e.message, '"str" is required!')
74
+ t.pass()
75
+ }
76
+ })
77
+
78
+ test('object with required field not in properties schema with additional properties true', (t) => {
79
+ t.plan(4)
80
+
81
+ const schema = {
82
+ title: 'object with required field',
83
+ type: 'object',
84
+ properties: {
85
+ num: {
86
+ type: 'integer'
87
+ }
88
+ },
89
+ additionalProperties: true,
90
+ required: ['str']
91
+ }
92
+ const stringify = build(schema)
93
+
94
+ try {
95
+ stringify({})
96
+ t.fail()
97
+ } catch (e) {
98
+ t.is(e.message, '"str" is required!')
99
+ t.pass()
100
+ }
101
+
102
+ try {
103
+ stringify({
104
+ num: 42
105
+ })
106
+ t.fail()
107
+ } catch (e) {
108
+ t.is(e.message, '"str" is required!')
109
+ t.pass()
110
+ }
111
+ })
112
+
113
+ test('object with multiple required field not in properties schema', (t) => {
114
+ t.plan(6)
115
+
116
+ const schema = {
117
+ title: 'object with required field',
118
+ type: 'object',
119
+ properties: {
120
+ num: {
121
+ type: 'integer'
122
+ }
123
+ },
124
+ additionalProperties: true,
125
+ required: ['num', 'key1', 'key2']
126
+ }
127
+ const stringify = build(schema)
128
+
129
+ try {
130
+ stringify({})
131
+ t.fail()
132
+ } catch (e) {
133
+ t.is(e.message, '"num" is required!')
134
+ t.pass()
135
+ }
136
+
137
+ try {
138
+ stringify({
139
+ num: 42
140
+ })
141
+ t.fail()
142
+ } catch (e) {
143
+ t.is(e.message, '"key1" is required!')
144
+ t.pass()
145
+ }
146
+
147
+ try {
148
+ stringify({
149
+ num: 42,
150
+ key1: 'some'
151
+ })
152
+ t.fail()
153
+ } catch (e) {
154
+ t.is(e.message, '"key2" is required!')
155
+ t.pass()
156
+ }
157
+ })
158
+
159
+ test('object with required bool', (t) => {
160
+ t.plan(3)
161
+
162
+ const schema = {
163
+ title: 'object with required field',
164
+ type: 'object',
165
+ properties: {
166
+ num: {
167
+ type: 'integer'
168
+ }
169
+ },
170
+ additionalProperties: true,
171
+ required: ['bool']
172
+ }
173
+ const stringify = build(schema)
174
+
175
+ try {
176
+ stringify({})
177
+ t.fail()
178
+ } catch (e) {
179
+ t.is(e.message, '"bool" is required!')
180
+ t.pass()
181
+ }
182
+
183
+ try {
184
+ stringify({
185
+ bool: false
186
+ })
187
+ t.pass()
188
+ } catch (e) {
189
+ t.fail(e.message)
190
+ }
191
+ })
192
+
193
+ test('required nullable', (t) => {
194
+ t.plan(1)
195
+
196
+ const schema = {
197
+ title: 'object with required field',
198
+ type: 'object',
199
+ properties: {
200
+ num: {
201
+ type: ['integer']
202
+ }
203
+ },
204
+ additionalProperties: true,
205
+ required: ['null']
206
+ }
207
+ const stringify = build(schema)
208
+
209
+ try {
210
+ stringify({
211
+ null: null
212
+ })
213
+ t.pass()
214
+ } catch (e) {
215
+ t.fail(e.message)
216
+ }
217
+ })
218
+
44
219
  test('required numbers', (t) => {
45
220
  t.plan(3)
46
221
 
@@ -74,7 +249,7 @@ test('required numbers', (t) => {
74
249
  })
75
250
  t.fail()
76
251
  } catch (e) {
77
- t.is(e.message, 'num is required!')
252
+ t.is(e.message, '"num" is required!')
78
253
  t.pass()
79
254
  }
80
255
  })
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const build = require('..')
5
+
6
+ const payload = '(throw "pwoned")'
7
+
8
+ const stringify = build({
9
+ properties: {
10
+ [`*///\\\\\\']);${payload};{/*`]: {
11
+ type: 'number'
12
+ }
13
+ }
14
+ })
15
+
16
+ stringify({})
17
+
18
+ t.pass('no crashes')
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const build = require('..')
5
+
6
+ const stringify = build({
7
+ $defs: {
8
+ type: 'foooo"bar'
9
+ },
10
+ patternProperties: {
11
+ x: { $ref: '#/$defs' }
12
+ }
13
+ })
14
+
15
+ t.throws(() => {
16
+ stringify({ x: 0 })
17
+ }, 'Cannot coerce 0 to "foo"bar"')
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const build = require('..')
5
+
6
+ const payload = '(throw "pwoned")'
7
+
8
+ const stringify = build({
9
+ required: [`"];${payload}//`]
10
+ })
11
+
12
+ t.throws(() => {
13
+ stringify({})
14
+ }, 'Error: ""];(throw "pwoned")//" is required!')
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const build = require('..')
5
+
6
+ const payload = '(throw "pwoned")'
7
+
8
+ const expected = 'Error: Invalid regular expression: /*/: Nothing to repeat. Found at * matching {"type":"*/(throw \\"pwoned\\")){//"}'
9
+
10
+ t.throws(() => {
11
+ build({
12
+ patternProperties: {
13
+ '*': { type: `*/${payload}){//` }
14
+ }
15
+ })
16
+ }, expected)
@@ -0,0 +1,22 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const build = require('..')
5
+
6
+ const payload = '(throw "pwoned")'
7
+
8
+ const stringify = build({
9
+ type: 'object',
10
+ properties: {
11
+ '/*': { type: 'object' },
12
+ x: {
13
+ type: 'object',
14
+ properties: {
15
+ a: { type: 'string', default: `*/}${payload};{//` }
16
+ }
17
+ }
18
+ }
19
+ })
20
+ stringify({})
21
+
22
+ t.pass('no crashes')