fast-json-stringify 2.4.1 → 2.5.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.
@@ -1,8 +1,12 @@
1
1
  version: 2
2
2
  updates:
3
+ - package-ecosystem: github-actions
4
+ directory: '/'
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
3
8
  - package-ecosystem: npm
4
9
  directory: '/'
5
10
  schedule:
6
11
  interval: daily
7
12
  open-pull-requests-limit: 10
8
-
@@ -9,7 +9,7 @@ jobs:
9
9
  steps:
10
10
  - uses: actions/checkout@v2
11
11
  - name: Use Node.js ${{ matrix.node-version }}
12
- uses: actions/setup-node@v1
12
+ uses: actions/setup-node@v2.1.5
13
13
  with:
14
14
  node-version: ${{ matrix.node-version }}
15
15
  - name: Install Dependencies
@@ -21,7 +21,7 @@ jobs:
21
21
  needs: test
22
22
  runs-on: ubuntu-latest
23
23
  steps:
24
- - uses: fastify/github-action-merge-dependabot@v1
24
+ - uses: fastify/github-action-merge-dependabot@v1.2.1
25
25
  if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request' }}
26
26
  with:
27
27
  github-token: ${{secrets.github_token}}
package/README.md CHANGED
@@ -30,6 +30,7 @@ fast-json-stringify obj x 13,537,123 ops/sec ±0.19% (95 runs sampled)
30
30
 
31
31
  #### Table of contents:
32
32
  - <a href="#example">`Example`</a>
33
+ - <a href="#options">`Options`</a>
33
34
  - <a href="#api">`API`</a>
34
35
  - <a href="#fastJsonStringify">`fastJsonStringify`</a>
35
36
  - <a href="#specific">`Specific use cases`</a>
@@ -48,6 +49,7 @@ fast-json-stringify obj x 13,537,123 ops/sec ±0.19% (95 runs sampled)
48
49
 
49
50
 
50
51
  <a name="example"></a>
52
+ Try it out on RunKit: <a href="https://runkit.com/npm/fast-json-stringify">https://runkit.com/npm/fast-json-stringify</a>
51
53
  ## Example
52
54
 
53
55
  ```js
@@ -79,13 +81,32 @@ console.log(stringify({
79
81
  reg: /"([^"]|\\")*"/
80
82
  }))
81
83
  ```
84
+
85
+ <a name="options"></a>
86
+ ## Options
87
+
88
+ Optionally, you may provide to `fast-json-stringify` an option object as second parameter:
89
+
90
+ ```js
91
+ const fastJson = require('fast-json-stringify')
92
+ const stringify = fastJson(mySchema, {
93
+ schema: { ... },
94
+ ajv: { ... },
95
+ rounding: 'ceil'
96
+ })
97
+ ```
98
+
99
+ - `schema`: external schemas references by $ref property. [More details](#ref)
100
+ - `ajv`: ajv instance's settings for those properties that require `ajv`. [More details](#anyof)
101
+ - `rounding`: setup how the `integer` types will be rounded when not integers. [More details](#integer)
102
+
103
+
82
104
  <a name="api"></a>
83
105
  ## API
84
106
  <a name="fastJsonStringify"></a>
85
107
  ### fastJsonStringify(schema)
86
108
 
87
- Build a `stringify()` function based on
88
- [jsonschema](http://json-schema.org/).
109
+ Build a `stringify()` function based on [jsonschema](http://json-schema.org/).
89
110
 
90
111
  Supported types:
91
112
 
package/index.js CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  const Ajv = require('ajv')
6
6
  const merge = require('deepmerge')
7
+ const clone = require('rfdc')({ proto: true })
8
+ const fjsCloned = Symbol('fast-json-stringify.cloned')
7
9
 
8
10
  const validate = require('./schema-validator')
9
11
  let stringSimilarity = null
@@ -442,6 +444,11 @@ function addPatternProperties (location) {
442
444
  ${addComma}
443
445
  json += $asString(keys[i]) + ':' + $asBoolean(obj[keys[i]])
444
446
  `
447
+ } else if (type === undefined) {
448
+ code += `
449
+ ${addComma}
450
+ json += $asString(keys[i]) + ':' + $asAny(obj[keys[i]])
451
+ `
445
452
  } else {
446
453
  code += `
447
454
  throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
@@ -468,7 +475,7 @@ function additionalProperty (location) {
468
475
  let code = ''
469
476
  if (ap === true) {
470
477
  return `
471
- if (obj[keys[i]] !== undefined) {
478
+ if (obj[keys[i]] !== undefined && typeof obj[keys[i]] !== 'function' && typeof obj[keys[i]] !== 'symbol') {
472
479
  ${addComma}
473
480
  json += $asString(keys[i]) + ':' + JSON.stringify(obj[keys[i]])
474
481
  }
@@ -535,6 +542,11 @@ function additionalProperty (location) {
535
542
  ${addComma}
536
543
  json += $asString(keys[i]) + ':' + $asBoolean(obj[keys[i]])
537
544
  `
545
+ } else if (type === undefined) {
546
+ code += `
547
+ ${addComma}
548
+ json += $asString(keys[i]) + ':' + $asAny(obj[keys[i]])
549
+ `
538
550
  } else {
539
551
  code += `
540
552
  throw new Error('Cannot coerce ' + obj[keys[i]] + ' to ' + ${JSON.stringify(type)})
@@ -945,7 +957,7 @@ function buildObject (location, code, name) {
945
957
  }
946
958
 
947
959
  function buildArray (location, code, name, key = null) {
948
- const schema = location.schema
960
+ let schema = location.schema
949
961
  code += `
950
962
  function ${name} (obj) {
951
963
  `
@@ -967,6 +979,10 @@ function buildArray (location, code, name, key = null) {
967
979
  }
968
980
 
969
981
  if (schema.items.$ref) {
982
+ if (!schema[fjsCloned]) {
983
+ schema = clone(location.schema)
984
+ schema[fjsCloned] = true
985
+ }
970
986
  location = refFinder(schema.items.$ref, location)
971
987
  schema.items = location.schema
972
988
  }
@@ -1059,6 +1075,12 @@ function buildArrayTypeCondition (type, accessor) {
1059
1075
  }
1060
1076
 
1061
1077
  function dereferenceOfRefs (location, type) {
1078
+ if (!location.schema[fjsCloned]) {
1079
+ const schemaClone = clone(location.schema)
1080
+ schemaClone[fjsCloned] = true
1081
+ location.schema = schemaClone
1082
+ }
1083
+
1062
1084
  const schema = location.schema
1063
1085
  const locations = []
1064
1086
 
@@ -1183,7 +1205,7 @@ function nested (laterCode, name, key, location, subKey, isArray) {
1183
1205
 
1184
1206
  // see comment on anyOf about derefencing the schema before calling ajv.validate
1185
1207
  code += `
1186
- ${index === 0 ? 'if' : 'else if'}(ajv.validate(${require('util').inspect(location.schema, { depth: null, maxArrayLength: null })}, obj${accessor}))
1208
+ ${index === 0 ? 'if' : 'else if'}(ajv.validate(${JSON.stringify(location.schema)}, obj${accessor}))
1187
1209
  ${nestedResult.code}
1188
1210
  `
1189
1211
  laterCode = nestedResult.laterCode
@@ -1197,7 +1219,7 @@ function nested (laterCode, name, key, location, subKey, isArray) {
1197
1219
  `
1198
1220
  } else if ('const' in schema) {
1199
1221
  code += `
1200
- if(ajv.validate(${require('util').inspect(schema, { depth: null })}, obj${accessor}))
1222
+ if(ajv.validate(${JSON.stringify(schema)}, obj${accessor}))
1201
1223
  json += '${JSON.stringify(schema.const)}'
1202
1224
  else
1203
1225
  throw new Error(\`Item $\{JSON.stringify(obj${accessor})} does not match schema definition.\`)
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "fast-json-stringify",
3
- "version": "2.4.1",
3
+ "version": "2.5.2",
4
4
  "description": "Stringify your JSON at max speed",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "scripts": {
8
8
  "benchmark": "node bench.js",
9
+ "lint:fix": "standard --fix",
9
10
  "test:lint": "standard",
10
11
  "test:typescript": "tsc --project ./test/types/tsconfig.json",
11
12
  "test:unit": "tap -J test/*.test.js test/**/*.test.js",
@@ -45,6 +46,7 @@
45
46
  "dependencies": {
46
47
  "ajv": "^6.11.0",
47
48
  "deepmerge": "^4.2.2",
49
+ "rfdc": "^1.2.0",
48
50
  "string-similarity": "^4.0.1"
49
51
  },
50
52
  "engines": {
@@ -54,5 +56,6 @@
54
56
  "ignore": [
55
57
  "schema-validator.js"
56
58
  ]
57
- }
59
+ },
60
+ "runkitExampleFilename": "example.js"
58
61
  }
@@ -161,6 +161,31 @@ test('additionalProperties - array coerce', (t) => {
161
161
  t.equal('{"foo":["t","r","u","e"],"ofoo":[],"arrfoo":["1","2"],"objfoo":[]}', stringify(obj))
162
162
  })
163
163
 
164
+ test('additionalProperties with empty schema', (t) => {
165
+ t.plan(1)
166
+ const stringify = build({
167
+ type: 'object',
168
+ additionalProperties: {}
169
+ })
170
+
171
+ const obj = { a: 1, b: true, c: null }
172
+ t.equal('{"a":1,"b":true,"c":null}', stringify(obj))
173
+ })
174
+
175
+ test('additionalProperties with nested empty schema', (t) => {
176
+ t.plan(1)
177
+ const stringify = build({
178
+ type: 'object',
179
+ properties: {
180
+ data: { type: 'object', additionalProperties: {} }
181
+ },
182
+ required: ['data']
183
+ })
184
+
185
+ const obj = { data: { a: 1, b: true, c: null } }
186
+ t.equal('{"data":{"a":1,"b":true,"c":null}}', stringify(obj))
187
+ })
188
+
164
189
  test('nested additionalProperties', (t) => {
165
190
  t.plan(1)
166
191
  const stringify = build({
@@ -284,3 +309,20 @@ test('property without type but with enum, will acts as additionalProperties wit
284
309
  const obj = { ap: { additional: 'field' } }
285
310
  t.equal('{"ap":{}}', stringify(obj))
286
311
  })
312
+
313
+ test('function and symbol references are not serialized as undefined', (t) => {
314
+ t.plan(1)
315
+ const stringify = build({
316
+ title: 'additionalProperties',
317
+ type: 'object',
318
+ additionalProperties: true,
319
+ properties: {
320
+ str: {
321
+ type: 'string'
322
+ }
323
+ }
324
+ })
325
+
326
+ const obj = { str: 'x', test: 'test', meth: () => 'x', sym: Symbol('x') }
327
+ t.equal('{"str":"x","test":"test"}', stringify(obj))
328
+ })
@@ -0,0 +1,157 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const clone = require('rfdc/default')
5
+ const build = require('..')
6
+
7
+ test('oneOf with $ref should not change the input schema', t => {
8
+ t.plan(2)
9
+
10
+ const referenceSchema = {
11
+ $id: 'externalId',
12
+ type: 'object',
13
+ properties: {
14
+ name: { type: 'string' }
15
+ }
16
+ }
17
+
18
+ const schema = {
19
+ $id: 'mainSchema',
20
+ type: 'object',
21
+ properties: {
22
+ people: {
23
+ oneOf: [{ $ref: 'externalId' }]
24
+ }
25
+ }
26
+ }
27
+ const clonedSchema = clone(schema)
28
+ const stringify = build(schema, {
29
+ schema: {
30
+ [referenceSchema.$id]: referenceSchema
31
+ }
32
+ })
33
+
34
+ const value = stringify({ people: { name: 'hello', foo: 'bar' } })
35
+ t.is(value, '{"people":{"name":"hello"}}')
36
+ t.deepEqual(schema, clonedSchema)
37
+ })
38
+
39
+ test('oneOf and anyOf with $ref should not change the input schema', t => {
40
+ t.plan(3)
41
+
42
+ const referenceSchema = {
43
+ $id: 'externalSchema',
44
+ type: 'object',
45
+ properties: {
46
+ name: { type: 'string' }
47
+ }
48
+ }
49
+
50
+ const schema = {
51
+ $id: 'rootSchema',
52
+ type: 'object',
53
+ properties: {
54
+ people: {
55
+ oneOf: [{ $ref: 'externalSchema' }]
56
+ },
57
+ love: {
58
+ anyOf: [
59
+ { $ref: '#/definitions/foo' },
60
+ { type: 'boolean' }
61
+ ]
62
+ }
63
+ },
64
+ definitions: {
65
+ foo: { type: 'string' }
66
+ }
67
+ }
68
+ const clonedSchema = clone(schema)
69
+ const stringify = build(schema, {
70
+ schema: {
71
+ [referenceSchema.$id]: referenceSchema
72
+ }
73
+ })
74
+
75
+ const valueAny1 = stringify({ people: { name: 'hello', foo: 'bar' }, love: 'music' })
76
+ const valueAny2 = stringify({ people: { name: 'hello', foo: 'bar' }, love: true })
77
+
78
+ t.equal(valueAny1, '{"people":{"name":"hello"},"love":"music"}')
79
+ t.equal(valueAny2, '{"people":{"name":"hello"},"love":true}')
80
+ t.deepEqual(schema, clonedSchema)
81
+ })
82
+
83
+ test('multiple $ref tree', t => {
84
+ t.plan(2)
85
+
86
+ const referenceDeepSchema = {
87
+ $id: 'deepId',
88
+ type: 'number'
89
+ }
90
+
91
+ const referenceSchema = {
92
+ $id: 'externalId',
93
+ type: 'object',
94
+ properties: {
95
+ name: { $ref: '#/definitions/foo' },
96
+ age: { $ref: 'deepId' }
97
+ },
98
+ definitions: {
99
+ foo: { type: 'string' }
100
+ }
101
+ }
102
+
103
+ const schema = {
104
+ $id: 'mainSchema',
105
+ type: 'object',
106
+ properties: {
107
+ people: {
108
+ oneOf: [{ $ref: 'externalId' }]
109
+ }
110
+ }
111
+ }
112
+ const clonedSchema = clone(schema)
113
+ const stringify = build(schema, {
114
+ schema: {
115
+ [referenceDeepSchema.$id]: referenceDeepSchema,
116
+ [referenceSchema.$id]: referenceSchema
117
+ }
118
+ })
119
+
120
+ const value = stringify({ people: { name: 'hello', foo: 'bar', age: 42 } })
121
+ t.is(value, '{"people":{"name":"hello","age":42}}')
122
+ t.deepEqual(schema, clonedSchema)
123
+ })
124
+
125
+ test('must not mutate items $ref', t => {
126
+ t.plan(2)
127
+
128
+ const referenceSchema = {
129
+ $id: 'ShowSchema',
130
+ $schema: 'http://json-schema.org/draft-07/schema#',
131
+ type: 'object',
132
+ properties: {
133
+ name: {
134
+ type: 'string'
135
+ }
136
+ }
137
+ }
138
+
139
+ const schema = {
140
+ $id: 'ListSchema',
141
+ $schema: 'http://json-schema.org/draft-07/schema#',
142
+ type: 'array',
143
+ items: {
144
+ $ref: 'ShowSchema#'
145
+ }
146
+ }
147
+ const clonedSchema = clone(schema)
148
+ const stringify = build(schema, {
149
+ schema: {
150
+ [referenceSchema.$id]: referenceSchema
151
+ }
152
+ })
153
+
154
+ const value = stringify([{ name: 'foo' }])
155
+ t.is(value, '[{"name":"foo"}]')
156
+ t.deepEqual(schema, clonedSchema)
157
+ })