fast-json-stringify 2.7.10 → 3.0.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/.github/dependabot.yml +4 -0
- package/.github/workflows/benchmark.yml +111 -0
- package/.github/workflows/ci.yml +7 -3
- package/README.md +33 -59
- package/bench.js +44 -0
- package/build-schema-validator.js +14 -9
- package/index.js +101 -62
- package/package.json +3 -4
- package/schema-validator.js +1132 -1748
- package/test/allof.test.js +53 -1
- package/test/bigint.test.js +76 -0
- package/test/debug-mode.test.js +26 -0
- package/test/if-then-else.test.js +16 -1
- package/test/integer.test.js +1 -10
- package/test/nullable.test.js +9 -4
- package/test/recursion.test.js +180 -0
- package/test/side-effect.test.js +39 -0
- package/test/types/tsconfig.json +3 -1
- package/test/typesArray.test.js +2 -2
- package/test/bigint.js +0 -74
- package/test/long.test.js +0 -85
package/.github/dependabot.yml
CHANGED
|
@@ -2,6 +2,10 @@ version: 2
|
|
|
2
2
|
updates:
|
|
3
3
|
- package-ecosystem: github-actions
|
|
4
4
|
directory: '/'
|
|
5
|
+
ignore:
|
|
6
|
+
- dependency-name: "actions/*"
|
|
7
|
+
update-types:
|
|
8
|
+
["version-update:semver-minor", "version-update:semver-patch"]
|
|
5
9
|
schedule:
|
|
6
10
|
interval: daily
|
|
7
11
|
open-pull-requests-limit: 10
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
name: Benchmark
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
benchmark:
|
|
9
|
+
if: ${{ github.event.label.name == 'benchmark' }}
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
outputs:
|
|
12
|
+
PR-BENCH-12: ${{ steps.benchmark-pr.outputs.BENCH_RESULT12 }}
|
|
13
|
+
PR-BENCH-14: ${{ steps.benchmark-pr.outputs.BENCH_RESULT14 }}
|
|
14
|
+
PR-BENCH-16: ${{ steps.benchmark-pr.outputs.BENCH_RESULT16 }}
|
|
15
|
+
MAIN-BENCH-12: ${{ steps.benchmark-main.outputs.BENCH_RESULT12 }}
|
|
16
|
+
MAIN-BENCH-14: ${{ steps.benchmark-main.outputs.BENCH_RESULT14 }}
|
|
17
|
+
MAIN-BENCH-16: ${{ steps.benchmark-main.outputs.BENCH_RESULT16 }}
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
node-version: [12, 14, 16]
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v2
|
|
23
|
+
with:
|
|
24
|
+
ref: ${{github.event.pull_request.head.ref}}
|
|
25
|
+
repository: ${{github.event.pull_request.head.repo.full_name}}
|
|
26
|
+
|
|
27
|
+
- uses: actions/setup-node@v2
|
|
28
|
+
with:
|
|
29
|
+
node-version: ${{ matrix.node-version }}
|
|
30
|
+
|
|
31
|
+
- name: Install
|
|
32
|
+
run: |
|
|
33
|
+
npm install --ignore-scripts
|
|
34
|
+
|
|
35
|
+
- name: Run benchmark
|
|
36
|
+
id: benchmark-pr
|
|
37
|
+
run: |
|
|
38
|
+
npm run --silent benchmark > ./bench-result
|
|
39
|
+
content=$(cat ./bench-result)
|
|
40
|
+
content="${content//'%'/'%25'}"
|
|
41
|
+
content="${content//$'\n'/'%0A'}"
|
|
42
|
+
content="${content//$'\r'/'%0D'}"
|
|
43
|
+
echo "::set-output name=BENCH_RESULT${{matrix.node-version}}::$content"
|
|
44
|
+
|
|
45
|
+
# main benchmark
|
|
46
|
+
- uses: actions/checkout@v2
|
|
47
|
+
with:
|
|
48
|
+
ref: 'master'
|
|
49
|
+
|
|
50
|
+
- name: Install
|
|
51
|
+
run: |
|
|
52
|
+
npm install --ignore-scripts
|
|
53
|
+
|
|
54
|
+
- name: Run benchmark
|
|
55
|
+
id: benchmark-master
|
|
56
|
+
run: |
|
|
57
|
+
npm run --silent benchmark > ./bench-result
|
|
58
|
+
content=$(cat ./bench-result)
|
|
59
|
+
content="${content//'%'/'%25'}"
|
|
60
|
+
content="${content//$'\n'/'%0A'}"
|
|
61
|
+
content="${content//$'\r'/'%0D'}"
|
|
62
|
+
echo "::set-output name=BENCH_RESULT${{matrix.node-version}}::$content"
|
|
63
|
+
|
|
64
|
+
output-benchmark:
|
|
65
|
+
needs: [benchmark]
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
steps:
|
|
68
|
+
- name: Comment PR
|
|
69
|
+
uses: thollander/actions-comment-pull-request@v1
|
|
70
|
+
with:
|
|
71
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
72
|
+
message: |
|
|
73
|
+
**Node**: 12
|
|
74
|
+
**PR**:
|
|
75
|
+
```
|
|
76
|
+
${{ needs.benchmark.outputs.PR-BENCH-12 }}
|
|
77
|
+
```
|
|
78
|
+
**MAIN**:
|
|
79
|
+
```
|
|
80
|
+
${{ needs.benchmark.outputs.MAIN-BENCH-12 }}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
**Node**: 14
|
|
86
|
+
**PR**:
|
|
87
|
+
```
|
|
88
|
+
${{ needs.benchmark.outputs.PR-BENCH-14 }}
|
|
89
|
+
```
|
|
90
|
+
**MAIN**:
|
|
91
|
+
```
|
|
92
|
+
${{ needs.benchmark.outputs.MAIN-BENCH-14 }}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
**Node**: 16
|
|
98
|
+
**PR**:
|
|
99
|
+
```
|
|
100
|
+
${{ needs.benchmark.outputs.PR-BENCH-16 }}
|
|
101
|
+
```
|
|
102
|
+
**MAIN**:
|
|
103
|
+
```
|
|
104
|
+
${{ needs.benchmark.outputs.MAIN-BENCH-16 }}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- uses: actions-ecosystem/action-remove-labels@v1
|
|
108
|
+
with:
|
|
109
|
+
labels: |
|
|
110
|
+
benchmark
|
|
111
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -19,10 +19,10 @@ jobs:
|
|
|
19
19
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
|
20
20
|
|
|
21
21
|
steps:
|
|
22
|
-
- uses: actions/checkout@v2
|
|
22
|
+
- uses: actions/checkout@v2
|
|
23
23
|
|
|
24
24
|
- name: Use Node.js
|
|
25
|
-
uses: actions/setup-node@v2
|
|
25
|
+
uses: actions/setup-node@v2
|
|
26
26
|
with:
|
|
27
27
|
node-version: ${{ matrix.node-version }}
|
|
28
28
|
|
|
@@ -37,7 +37,11 @@ jobs:
|
|
|
37
37
|
automerge:
|
|
38
38
|
needs: test
|
|
39
39
|
runs-on: ubuntu-latest
|
|
40
|
+
permissions:
|
|
41
|
+
pull-requests: write
|
|
42
|
+
contents: write
|
|
40
43
|
steps:
|
|
41
|
-
- uses: fastify/github-action-merge-dependabot@
|
|
44
|
+
- uses: fastify/github-action-merge-dependabot@v3
|
|
42
45
|
with:
|
|
43
46
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
47
|
+
target: minor
|
package/README.md
CHANGED
|
@@ -7,7 +7,14 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/fast-json-stringify)
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
__fast-json-stringify__ is significantly faster than `JSON.stringify()` for small payloads.
|
|
10
|
+
__fast-json-stringify__ is significantly faster than `JSON.stringify()` for small payloads.
|
|
11
|
+
Its performance advantage shrinks as your payload grows.
|
|
12
|
+
It pairs well with [__flatstr__](https://www.npmjs.com/package/flatstr), which triggers a V8 optimization that improves performance when eventually converting the string to a `Buffer`.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### How it works
|
|
16
|
+
|
|
17
|
+
fast-json-stringify requires a [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7) input to generate a fast `stringify` function.
|
|
11
18
|
|
|
12
19
|
##### Benchmarks
|
|
13
20
|
|
|
@@ -15,22 +22,28 @@ __fast-json-stringify__ is significantly faster than `JSON.stringify()` for smal
|
|
|
15
22
|
- Node.js `v16.9.1`
|
|
16
23
|
|
|
17
24
|
```
|
|
18
|
-
FJS creation x
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
JSON.stringify
|
|
30
|
-
fast-json-stringify
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
FJS creation x 8,443 ops/sec ±1.01% (90 runs sampled)
|
|
26
|
+
CJS creation x 183,219 ops/sec ±0.13% (96 runs sampled)
|
|
27
|
+
AJV Serialize creation x 83,541,848 ops/sec ±0.24% (98 runs sampled)
|
|
28
|
+
JSON.stringify array x 5,363 ops/sec ±0.11% (100 runs sampled)
|
|
29
|
+
fast-json-stringify array x 6,747 ops/sec ±0.13% (98 runs sampled)
|
|
30
|
+
compile-json-stringify array x 7,121 ops/sec ±0.42% (98 runs sampled)
|
|
31
|
+
AJV Serialize array x 7,533 ops/sec ±0.13% (98 runs sampled)
|
|
32
|
+
JSON.stringify long string x 16,461 ops/sec ±0.12% (98 runs sampled)
|
|
33
|
+
fast-json-stringify long string x 16,443 ops/sec ±0.37% (99 runs sampled)
|
|
34
|
+
compile-json-stringify long string x 16,458 ops/sec ±0.09% (98 runs sampled)
|
|
35
|
+
AJV Serialize long string x 21,433 ops/sec ±0.08% (95 runs sampled)
|
|
36
|
+
JSON.stringify short string x 12,035,664 ops/sec ±0.62% (96 runs sampled)
|
|
37
|
+
fast-json-stringify short string x 38,281,060 ops/sec ±0.24% (98 runs sampled)
|
|
38
|
+
compile-json-stringify short string x 32,388,037 ops/sec ±0.27% (97 runs sampled)
|
|
39
|
+
AJV Serialize short string x 32,288,612 ops/sec ±0.32% (95 runs sampled)
|
|
40
|
+
JSON.stringify obj x 3,068,185 ops/sec ±0.16% (98 runs sampled)
|
|
41
|
+
fast-json-stringify obj x 10,082,694 ops/sec ±0.10% (97 runs sampled)
|
|
42
|
+
compile-json-stringify obj x 17,037,963 ops/sec ±1.17% (97 runs sampled)
|
|
43
|
+
AJV Serialize obj x 9,660,041 ops/sec ±0.11% (97 runs sampled)
|
|
44
|
+
JSON stringify date x 1,084,008 ops/sec ±0.16% (98 runs sampled)
|
|
45
|
+
fast-json-stringify date format x 1,781,044 ops/sec ±0.48% (99 runs sampled)
|
|
46
|
+
compile-json-stringify date format x 1,086,187 ops/sec ±0.16% (99 runs sampled)
|
|
34
47
|
```
|
|
35
48
|
|
|
36
49
|
#### Table of contents:
|
|
@@ -102,7 +115,7 @@ const stringify = fastJson(mySchema, {
|
|
|
102
115
|
```
|
|
103
116
|
|
|
104
117
|
- `schema`: external schemas references by $ref property. [More details](#ref)
|
|
105
|
-
- `ajv`: ajv instance's settings for those properties that require `ajv`. [More details](#anyof)
|
|
118
|
+
- `ajv`: [ajv v8 instance's settings](https://ajv.js.org/options.html) for those properties that require `ajv`. [More details](#anyof)
|
|
106
119
|
- `rounding`: setup how the `integer` types will be rounded when not integers. [More details](#integer)
|
|
107
120
|
|
|
108
121
|
|
|
@@ -111,7 +124,7 @@ const stringify = fastJson(mySchema, {
|
|
|
111
124
|
<a name="fastJsonStringify"></a>
|
|
112
125
|
### fastJsonStringify(schema)
|
|
113
126
|
|
|
114
|
-
Build a `stringify()` function based on [jsonschema](https://json-schema.org/).
|
|
127
|
+
Build a `stringify()` function based on [jsonschema draft 7 spec](https://json-schema.org/specification-links.html#draft-7).
|
|
115
128
|
|
|
116
129
|
Supported types:
|
|
117
130
|
|
|
@@ -521,46 +534,7 @@ const stringify = fastJson(schema, { schema: externalSchema })
|
|
|
521
534
|
|
|
522
535
|
<a name="long"></a>
|
|
523
536
|
#### Long integers
|
|
524
|
-
By default the library will automatically
|
|
525
|
-
If you can't use BigInts in your environment, long integers (64-bit) are also supported using the [long](https://github.com/dcodeIO/long.js) module.
|
|
526
|
-
Example:
|
|
527
|
-
```javascript
|
|
528
|
-
// => using native BigInt
|
|
529
|
-
const stringify = fastJson({
|
|
530
|
-
title: 'Example Schema',
|
|
531
|
-
type: 'object',
|
|
532
|
-
properties: {
|
|
533
|
-
id: {
|
|
534
|
-
type: 'integer'
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
const obj = {
|
|
540
|
-
id: 18446744073709551615n
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
console.log(stringify(obj)) // '{"id":18446744073709551615}'
|
|
544
|
-
|
|
545
|
-
// => using the long library
|
|
546
|
-
const Long = require('long')
|
|
547
|
-
|
|
548
|
-
const stringify = fastJson({
|
|
549
|
-
title: 'Example Schema',
|
|
550
|
-
type: 'object',
|
|
551
|
-
properties: {
|
|
552
|
-
id: {
|
|
553
|
-
type: 'integer'
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
})
|
|
557
|
-
|
|
558
|
-
const obj = {
|
|
559
|
-
id: Long.fromString('18446744073709551615', true)
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
console.log(stringify(obj)) // '{"id":18446744073709551615}'
|
|
563
|
-
```
|
|
537
|
+
By default the library will handle automatically [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).
|
|
564
538
|
|
|
565
539
|
<a name="integer"></a>
|
|
566
540
|
#### Integers
|
package/bench.js
CHANGED
|
@@ -38,6 +38,21 @@ const schemaCJS = {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const schemaAJVJTD = {
|
|
42
|
+
properties: {
|
|
43
|
+
firstName: {
|
|
44
|
+
type: 'string'
|
|
45
|
+
},
|
|
46
|
+
lastName: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
nullable: true
|
|
49
|
+
},
|
|
50
|
+
age: {
|
|
51
|
+
type: 'uint8'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
41
56
|
const arraySchema = {
|
|
42
57
|
title: 'array schema',
|
|
43
58
|
type: 'array',
|
|
@@ -50,6 +65,10 @@ const arraySchemaCJS = {
|
|
|
50
65
|
items: schemaCJS
|
|
51
66
|
}
|
|
52
67
|
|
|
68
|
+
const arraySchemaAJVJTD = {
|
|
69
|
+
elements: schemaAJVJTD
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
const dateFormatSchema = {
|
|
54
73
|
description: 'Date of birth',
|
|
55
74
|
type: 'string',
|
|
@@ -85,6 +104,12 @@ const stringifyDate = FJS(dateFormatSchema)
|
|
|
85
104
|
const stringifyString = FJS({ type: 'string' })
|
|
86
105
|
let str = ''
|
|
87
106
|
|
|
107
|
+
const Ajv = require('ajv/dist/jtd')
|
|
108
|
+
const ajv = new Ajv()
|
|
109
|
+
const ajvSerialize = ajv.compileSerializer(schemaAJVJTD)
|
|
110
|
+
const ajvSerializeArray = ajv.compileSerializer(arraySchemaAJVJTD)
|
|
111
|
+
const ajvSerializeString = ajv.compileSerializer({ type: 'string' })
|
|
112
|
+
|
|
88
113
|
// eslint-disable-next-line
|
|
89
114
|
for (var i = 0; i < 10000; i++) {
|
|
90
115
|
str += i
|
|
@@ -105,6 +130,9 @@ suite.add('FJS creation', function () {
|
|
|
105
130
|
suite.add('CJS creation', function () {
|
|
106
131
|
CJS(schemaCJS)
|
|
107
132
|
})
|
|
133
|
+
suite.add('AJV Serialize creation', function () {
|
|
134
|
+
ajv.compileSerializer(schemaAJVJTD)
|
|
135
|
+
})
|
|
108
136
|
|
|
109
137
|
suite.add('JSON.stringify array', function () {
|
|
110
138
|
JSON.stringify(multiArray)
|
|
@@ -118,6 +146,10 @@ suite.add('compile-json-stringify array', function () {
|
|
|
118
146
|
CJSStringifyArray(multiArray)
|
|
119
147
|
})
|
|
120
148
|
|
|
149
|
+
suite.add('AJV Serialize array', function () {
|
|
150
|
+
ajvSerializeArray(multiArray)
|
|
151
|
+
})
|
|
152
|
+
|
|
121
153
|
suite.add('JSON.stringify long string', function () {
|
|
122
154
|
JSON.stringify(str)
|
|
123
155
|
})
|
|
@@ -130,6 +162,10 @@ suite.add('compile-json-stringify long string', function () {
|
|
|
130
162
|
CJSStringifyString(str)
|
|
131
163
|
})
|
|
132
164
|
|
|
165
|
+
suite.add('AJV Serialize long string', function () {
|
|
166
|
+
ajvSerializeString(str)
|
|
167
|
+
})
|
|
168
|
+
|
|
133
169
|
suite.add('JSON.stringify short string', function () {
|
|
134
170
|
JSON.stringify('hello world')
|
|
135
171
|
})
|
|
@@ -142,6 +178,10 @@ suite.add('compile-json-stringify short string', function () {
|
|
|
142
178
|
CJSStringifyString('hello world')
|
|
143
179
|
})
|
|
144
180
|
|
|
181
|
+
suite.add('AJV Serialize short string', function () {
|
|
182
|
+
ajvSerializeString('hello world')
|
|
183
|
+
})
|
|
184
|
+
|
|
145
185
|
suite.add('JSON.stringify obj', function () {
|
|
146
186
|
JSON.stringify(obj)
|
|
147
187
|
})
|
|
@@ -154,6 +194,10 @@ suite.add('compile-json-stringify obj', function () {
|
|
|
154
194
|
CJSStringify(obj)
|
|
155
195
|
})
|
|
156
196
|
|
|
197
|
+
suite.add('AJV Serialize obj', function () {
|
|
198
|
+
ajvSerialize(obj)
|
|
199
|
+
})
|
|
200
|
+
|
|
157
201
|
suite.add('JSON stringify date', function () {
|
|
158
202
|
JSON.stringify(date)
|
|
159
203
|
})
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const Ajv = require('ajv')
|
|
4
|
+
const standaloneCode = require('ajv/dist/standalone').default
|
|
5
|
+
const ajvFormats = require('ajv-formats')
|
|
4
6
|
const fs = require('fs')
|
|
5
7
|
const path = require('path')
|
|
6
|
-
const pack = require('ajv-pack')
|
|
7
8
|
|
|
8
9
|
const ajv = new Ajv({
|
|
9
|
-
|
|
10
|
+
addUsedSchema: false,
|
|
11
|
+
allowUnionTypes: true,
|
|
12
|
+
code: {
|
|
13
|
+
source: true,
|
|
14
|
+
lines: true,
|
|
15
|
+
optimize: 3
|
|
16
|
+
}
|
|
10
17
|
})
|
|
18
|
+
ajvFormats(ajv)
|
|
11
19
|
|
|
12
|
-
const
|
|
20
|
+
const schema = require('ajv/lib/refs/json-schema-draft-07.json')
|
|
21
|
+
const validate = ajv.compile(schema)
|
|
22
|
+
const validationCode = standaloneCode(ajv, validate)
|
|
13
23
|
|
|
14
|
-
const moduleCode =
|
|
15
|
-
|
|
16
|
-
function nop () { return true }
|
|
17
|
-
|
|
18
|
-
${pack(ajv, validate).replace(/root\.refVal\[0\]/gm, 'nop')}
|
|
19
|
-
`
|
|
24
|
+
const moduleCode = `/* CODE GENERATED BY '${path.basename(__filename)}' DO NOT EDIT! */\n${validationCode}`
|
|
20
25
|
|
|
21
26
|
fs.writeFileSync(path.join(__dirname, 'schema-validator.js'), moduleCode)
|