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.
- package/.github/dependabot.yml +5 -1
- package/.github/workflows/ci.yml +2 -2
- package/README.md +23 -2
- package/index.js +26 -4
- package/package.json +5 -2
- package/test/additionalProperties.test.js +42 -0
- package/test/side-effect.test.js +157 -0
package/.github/dependabot.yml
CHANGED
package/.github/workflows/ci.yml
CHANGED
|
@@ -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@
|
|
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
|
-
|
|
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(${
|
|
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(${
|
|
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.
|
|
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
|
+
})
|