fast-json-stringify 3.2.0 → 4.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.
@@ -12,36 +12,6 @@ on:
12
12
 
13
13
  jobs:
14
14
  test:
15
- runs-on: ${{ matrix.os }}
16
- strategy:
17
- matrix:
18
- node-version: [14, 16, 17]
19
- os: [macos-latest, ubuntu-latest, windows-latest]
20
-
21
- steps:
22
- - uses: actions/checkout@v3
23
-
24
- - name: Use Node.js
25
- uses: actions/setup-node@v3
26
- with:
27
- node-version: ${{ matrix.node-version }}
28
-
29
- - name: Install Dependencies
30
- run: |
31
- npm install --ignore-scripts
32
-
33
- - name: Run Tests
34
- run: |
35
- npm test
36
-
37
- automerge:
38
- needs: test
39
- runs-on: ubuntu-latest
40
- permissions:
41
- pull-requests: write
42
- contents: write
43
- steps:
44
- - uses: fastify/github-action-merge-dependabot@v3
45
- with:
46
- github-token: ${{ secrets.GITHUB_TOKEN }}
47
- target: minor
15
+ uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
16
+ with:
17
+ auto-merge-exclude: 'chalk'
package/README.md CHANGED
@@ -2,13 +2,12 @@
2
2
 
3
3
  ![CI](https://github.com/fastify/fast-json-stringify/workflows/CI/badge.svg)
4
4
  [![NPM version](https://img.shields.io/npm/v/fast-json-stringify.svg?style=flat)](https://www.npmjs.com/package/fast-json-stringify)
5
- [![Known Vulnerabilities](https://snyk.io/test/github/fastify/fast-json-stringify/badge.svg)](https://snyk.io/test/github/fastify/fast-json-stringify)
6
5
  [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)
7
6
  [![NPM downloads](https://img.shields.io/npm/dm/fast-json-stringify.svg?style=flat)](https://www.npmjs.com/package/fast-json-stringify)
8
7
 
9
8
 
10
- __fast-json-stringify__ is significantly faster than `JSON.stringify()` for small payloads.
11
- Its performance advantage shrinks as your payload grows.
9
+ __fast-json-stringify__ is significantly faster than `JSON.stringify()` for small payloads.
10
+ Its performance advantage shrinks as your payload grows.
12
11
  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
12
 
14
13
 
@@ -160,18 +159,17 @@ And nested ones, too.
160
159
 
161
160
  **Note**: In the case of string formatted Date and not Date Object, there will be no manipulation on it. It should be properly formatted.
162
161
 
163
- Example with a MomentJS object:
162
+ Example with a Date object:
164
163
 
165
164
  ```javascript
166
- const moment = require('moment')
167
-
168
165
  const stringify = fastJson({
169
166
  title: 'Example Schema with string date-time field',
170
167
  type: 'string',
171
168
  format: 'date-time'
172
169
  })
173
170
 
174
- console.log(stringify(moment())) // '"YYYY-MM-DDTHH:mm:ss.sssZ"'
171
+ const date = new Date()
172
+ console.log(stringify(date)) // '"YYYY-MM-DDTHH:mm:ss.sssZ"'
175
173
  ```
176
174
 
177
175
 
@@ -287,7 +285,7 @@ console.log(stringify(obj)) // '{"matchfoo":"42","otherfoo":"str","matchnum":3,"
287
285
  If *additionalProperties* is not present or is set to `false`, every property that is not explicitly listed in the *properties* and *patternProperties* objects,will be ignored, as described in <a href="#missingFields">Missing fields</a>.
288
286
  Missing fields are ignored to avoid having to rewrite objects before serializing. However, other schema rules would throw in similar situations.
289
287
  If *additionalProperties* is set to `true`, it will be used by `JSON.stringify` to stringify the additional properties. If you want to achieve maximum performance, we strongly encourage you to use a fixed schema where possible.
290
- The additional properties will always be serialzied at the end of the object.
288
+ The additional properties will always be serialized at the end of the object.
291
289
  Example:
292
290
  ```javascript
293
291
  const stringify = fastJson({
@@ -0,0 +1,114 @@
1
+ 'use strict'
2
+
3
+ const { spawn } = require('child_process')
4
+
5
+ const chalk = require('chalk')
6
+ const inquirer = require('inquirer')
7
+ const simpleGit = require('simple-git')
8
+
9
+ const git = simpleGit(process.cwd())
10
+
11
+ const COMMAND = 'npm run bench'
12
+ const DEFAULT_BRANCH = 'master'
13
+ const PERCENT_THRESHOLD = 5
14
+
15
+ async function selectBranchName (message, branches) {
16
+ const result = await inquirer.prompt([{
17
+ type: 'list',
18
+ name: 'branch',
19
+ choices: branches,
20
+ loop: false,
21
+ pageSize: 20,
22
+ message
23
+ }])
24
+ return result.branch
25
+ }
26
+
27
+ async function executeCommandOnBranch (command, branch) {
28
+ console.log(chalk.grey(`Checking out "${branch}"`))
29
+ await git.checkout(branch)
30
+
31
+ console.log(chalk.grey(`Execute "${command}"`))
32
+ const childProcess = spawn(command, { stdio: 'pipe', shell: true })
33
+
34
+ let result = ''
35
+ childProcess.stdout.on('data', (data) => {
36
+ process.stdout.write(data.toString())
37
+ result += data.toString()
38
+ })
39
+
40
+ await new Promise(resolve => childProcess.on('close', resolve))
41
+
42
+ console.log()
43
+
44
+ return parseBenchmarksStdout(result)
45
+ }
46
+
47
+ function parseBenchmarksStdout (text) {
48
+ const results = []
49
+
50
+ const lines = text.split('\n')
51
+ for (const line of lines) {
52
+ const match = /^(.+?)(\.*) x (.+) ops\/sec .*$/.exec(line)
53
+ if (match !== null) {
54
+ results.push({
55
+ name: match[1],
56
+ alignedName: match[1] + match[2],
57
+ result: parseInt(match[3].split(',').join(''))
58
+ })
59
+ }
60
+ }
61
+
62
+ return results
63
+ }
64
+
65
+ function compareResults (featureBranch, mainBranch) {
66
+ for (const { name, alignedName, result: mainBranchResult } of mainBranch) {
67
+ const featureBranchBenchmark = featureBranch.find(result => result.name === name)
68
+ if (featureBranchBenchmark) {
69
+ const featureBranchResult = featureBranchBenchmark.result
70
+ const percent = (featureBranchResult - mainBranchResult) * 100 / mainBranchResult
71
+ const roundedPercent = Math.round(percent * 100) / 100
72
+
73
+ const percentString = roundedPercent > 0 ? `+${roundedPercent}%` : `${roundedPercent}%`
74
+ const message = alignedName + percentString.padStart(7, '.')
75
+
76
+ if (roundedPercent > PERCENT_THRESHOLD) {
77
+ console.log(chalk.green(message))
78
+ } else if (roundedPercent < -PERCENT_THRESHOLD) {
79
+ console.log(chalk.red(message))
80
+ } else {
81
+ console.log(message)
82
+ }
83
+ }
84
+ }
85
+ }
86
+
87
+ (async function () {
88
+ const branches = await git.branch()
89
+ const currentBranch = branches.branches[branches.current]
90
+
91
+ let featureBranch = null
92
+ let mainBranch = null
93
+
94
+ if (process.argv[2] === '--ci') {
95
+ featureBranch = currentBranch.name
96
+ mainBranch = DEFAULT_BRANCH
97
+ } else {
98
+ featureBranch = await selectBranchName('Select the branch you want to compare (feature branch):', branches.all)
99
+ mainBranch = await selectBranchName('Select the branch you want to compare with (main branch):', branches.all)
100
+ }
101
+
102
+ try {
103
+ const featureBranchResult = await executeCommandOnBranch(COMMAND, featureBranch)
104
+ const mainBranchResult = await executeCommandOnBranch(COMMAND, mainBranch)
105
+ compareResults(featureBranchResult, mainBranchResult)
106
+ } catch (error) {
107
+ console.error('Switch to origin branch due to an error', error.message)
108
+ }
109
+
110
+ await git.checkout(currentBranch.commit)
111
+ await git.checkout(currentBranch.name)
112
+
113
+ console.log(chalk.gray(`Back to ${currentBranch.name} ${currentBranch.commit}`))
114
+ })()
@@ -5,7 +5,7 @@ const suite = new benchmark.Suite()
5
5
 
6
6
  const STR_LEN = 1e4
7
7
  const LARGE_ARRAY_SIZE = 2e4
8
- const MULTI_ARRAY_LENGHT = 1e3
8
+ const MULTI_ARRAY_LENGTH = 1e3
9
9
 
10
10
  const schema = {
11
11
  title: 'Example Schema',
@@ -93,7 +93,7 @@ const obj = {
93
93
 
94
94
  const date = new Date()
95
95
 
96
- const multiArray = new Array(MULTI_ARRAY_LENGHT)
96
+ const multiArray = new Array(MULTI_ARRAY_LENGTH)
97
97
  const largeArray = new Array(LARGE_ARRAY_SIZE)
98
98
 
99
99
  const CJS = require('compile-json-stringify')
@@ -102,7 +102,7 @@ const CJSStringifyArray = CJS(arraySchemaCJS)
102
102
  const CJSStringifyDate = CJS(dateFormatSchemaCJS)
103
103
  const CJSStringifyString = CJS({ type: 'string' })
104
104
 
105
- const FJS = require('.')
105
+ const FJS = require('..')
106
106
  const stringify = FJS(schema)
107
107
  const stringifyArrayDefault = FJS(arraySchema)
108
108
  const stringifyArrayJSONStringify = FJS(arraySchema, {
@@ -158,7 +158,7 @@ for (let i = STR_LEN; i < LARGE_ARRAY_SIZE; ++i) {
158
158
 
159
159
  Number(str)
160
160
 
161
- for (let i = 0; i < MULTI_ARRAY_LENGHT; i++) {
161
+ for (let i = 0; i < MULTI_ARRAY_LENGTH; i++) {
162
162
  multiArray[i] = obj
163
163
  }
164
164
 
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ const { workerData: benchmark, parentPort } = require('worker_threads')
4
+
5
+ const Benchmark = require('benchmark')
6
+ Benchmark.options.minSamples = 500
7
+
8
+ const suite = Benchmark.Suite()
9
+
10
+ const FJS = require('..')
11
+ const stringify = FJS(benchmark.schema)
12
+
13
+ suite
14
+ .add(benchmark.name, () => {
15
+ stringify(benchmark.input)
16
+ })
17
+ .on('cycle', (event) => {
18
+ parentPort.postMessage(String(event.target))
19
+ })
20
+ .on('complete', () => {})
21
+ .run()
@@ -0,0 +1,251 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+ const { Worker } = require('worker_threads')
5
+
6
+ const BENCH_THREAD_PATH = path.join(__dirname, 'bench-thread.js')
7
+
8
+ const LONG_STRING_LENGTH = 1e4
9
+ const SHORT_ARRAY_SIZE = 1e3
10
+
11
+ const shortArrayOfNumbers = new Array(SHORT_ARRAY_SIZE)
12
+ const shortArrayOfIntegers = new Array(SHORT_ARRAY_SIZE)
13
+ const shortArrayOfShortStrings = new Array(SHORT_ARRAY_SIZE)
14
+ const shortArrayOfLongStrings = new Array(SHORT_ARRAY_SIZE)
15
+ const shortArrayOfMultiObject = new Array(SHORT_ARRAY_SIZE)
16
+
17
+ function getRandomInt (max) {
18
+ return Math.floor(Math.random() * max)
19
+ }
20
+
21
+ let longString = ''
22
+ for (let i = 0; i < LONG_STRING_LENGTH; i++) {
23
+ longString += i
24
+ if (i % 100 === 0) {
25
+ longString += '"'
26
+ }
27
+ }
28
+
29
+ for (let i = 0; i < SHORT_ARRAY_SIZE; i++) {
30
+ shortArrayOfNumbers[i] = getRandomInt(1000)
31
+ shortArrayOfIntegers[i] = getRandomInt(1000)
32
+ shortArrayOfShortStrings[i] = 'hello world'
33
+ shortArrayOfLongStrings[i] = longString
34
+ shortArrayOfMultiObject[i] = { s: 'hello world', n: 42, b: true }
35
+ }
36
+
37
+ const benchmarks = [
38
+ {
39
+ name: 'short string',
40
+ schema: {
41
+ type: 'string'
42
+ },
43
+ input: 'hello world'
44
+ },
45
+ {
46
+ name: 'long string',
47
+ schema: {
48
+ type: 'string'
49
+ },
50
+ input: longString
51
+ },
52
+ {
53
+ name: 'number',
54
+ schema: {
55
+ type: 'number'
56
+ },
57
+ input: 42
58
+ },
59
+ {
60
+ name: 'integer',
61
+ schema: {
62
+ type: 'integer'
63
+ },
64
+ input: 42
65
+ },
66
+ {
67
+ name: 'formatted date-time',
68
+ schema: {
69
+ type: 'string',
70
+ format: 'date-time'
71
+ },
72
+ input: new Date()
73
+ },
74
+ {
75
+ name: 'formatted date',
76
+ schema: {
77
+ type: 'string',
78
+ format: 'date'
79
+ },
80
+ input: new Date()
81
+ },
82
+ {
83
+ name: 'formatted time',
84
+ schema: {
85
+ type: 'string',
86
+ format: 'time'
87
+ },
88
+ input: new Date()
89
+ },
90
+ {
91
+ name: 'short array of numbers',
92
+ schema: {
93
+ type: 'array',
94
+ items: { type: 'number' }
95
+ },
96
+ input: shortArrayOfNumbers
97
+ },
98
+ {
99
+ name: 'short array of integers',
100
+ schema: {
101
+ type: 'array',
102
+ items: { type: 'integer' }
103
+ },
104
+ input: shortArrayOfIntegers
105
+ },
106
+ {
107
+ name: 'short array of short strings',
108
+ schema: {
109
+ type: 'array',
110
+ items: { type: 'string' }
111
+ },
112
+ input: shortArrayOfShortStrings
113
+ },
114
+ {
115
+ name: 'short array of long strings',
116
+ schema: {
117
+ type: 'array',
118
+ items: { type: 'string' }
119
+ },
120
+ input: shortArrayOfShortStrings
121
+ },
122
+ {
123
+ name: 'short array of objects with properties of different types',
124
+ schema: {
125
+ type: 'array',
126
+ items: {
127
+ type: 'object',
128
+ properties: {
129
+ s: { type: 'string' },
130
+ n: { type: 'number' },
131
+ b: { type: 'boolean' }
132
+ }
133
+ }
134
+ },
135
+ input: shortArrayOfMultiObject
136
+ },
137
+ {
138
+ name: 'object with number property',
139
+ schema: {
140
+ type: 'object',
141
+ properties: {
142
+ a: { type: 'number' }
143
+ }
144
+ },
145
+ input: { a: 42 }
146
+ },
147
+ {
148
+ name: 'object with integer property',
149
+ schema: {
150
+ type: 'object',
151
+ properties: {
152
+ a: { type: 'integer' }
153
+ }
154
+ },
155
+ input: { a: 42 }
156
+ },
157
+ {
158
+ name: 'object with short string property',
159
+ schema: {
160
+ type: 'object',
161
+ properties: {
162
+ a: { type: 'string' }
163
+ }
164
+ },
165
+ input: { a: 'hello world' }
166
+ },
167
+ {
168
+ name: 'object with long string property',
169
+ schema: {
170
+ type: 'object',
171
+ properties: {
172
+ a: { type: 'string' }
173
+ }
174
+ },
175
+ input: { a: longString }
176
+ },
177
+ {
178
+ name: 'object with properties of different types',
179
+ schema: {
180
+ type: 'object',
181
+ properties: {
182
+ s1: { type: 'string' },
183
+ n1: { type: 'number' },
184
+ b1: { type: 'boolean' },
185
+ s2: { type: 'string' },
186
+ n2: { type: 'number' },
187
+ b2: { type: 'boolean' },
188
+ s3: { type: 'string' },
189
+ n3: { type: 'number' },
190
+ b3: { type: 'boolean' },
191
+ s4: { type: 'string' },
192
+ n4: { type: 'number' },
193
+ b4: { type: 'boolean' },
194
+ s5: { type: 'string' },
195
+ n5: { type: 'number' },
196
+ b5: { type: 'boolean' }
197
+ }
198
+ },
199
+ input: {
200
+ s1: 'hello world',
201
+ n1: 42,
202
+ b1: true,
203
+ s2: 'hello world',
204
+ n2: 42,
205
+ b2: true,
206
+ s3: 'hello world',
207
+ n3: 42,
208
+ b3: true,
209
+ s4: 'hello world',
210
+ n4: 42,
211
+ b4: true,
212
+ s5: 'hello world',
213
+ n5: 42,
214
+ b5: true
215
+ }
216
+ }
217
+ ]
218
+
219
+ async function runBenchmark (benchmark) {
220
+ const worker = new Worker(BENCH_THREAD_PATH, { workerData: benchmark })
221
+
222
+ return new Promise((resolve, reject) => {
223
+ let result = null
224
+ worker.on('error', reject)
225
+ worker.on('message', (benchResult) => {
226
+ result = benchResult
227
+ })
228
+ worker.on('exit', (code) => {
229
+ if (code === 0) {
230
+ resolve(result)
231
+ } else {
232
+ reject(new Error(`Worker stopped with exit code ${code}`))
233
+ }
234
+ })
235
+ })
236
+ }
237
+
238
+ async function runBenchmarks () {
239
+ let maxNameLength = 0
240
+ for (const benchmark of benchmarks) {
241
+ maxNameLength = Math.max(benchmark.name.length, maxNameLength)
242
+ }
243
+
244
+ for (const benchmark of benchmarks) {
245
+ benchmark.name = benchmark.name.padEnd(maxNameLength, '.')
246
+ const resultMessage = await runBenchmark(benchmark)
247
+ console.log(resultMessage)
248
+ }
249
+ }
250
+
251
+ runBenchmarks()
package/example.js CHANGED
@@ -1,4 +1,3 @@
1
- const moment = require('moment')
2
1
  const fastJson = require('.')
3
2
  const stringify = fastJson({
4
3
  title: 'Example Schema',
@@ -67,12 +66,10 @@ console.log(stringify({
67
66
  lastName: 'Collina',
68
67
  age: 32,
69
68
  now: new Date(),
70
- birthdate: moment(),
71
69
  reg: /"([^"]|\\")*"/,
72
70
  foo: 'hello',
73
71
  numfoo: 42,
74
72
  test: 42,
75
- date: moment(),
76
73
  strtest: '23',
77
74
  arr: [{ str: 'stark' }, { str: 'lannister' }],
78
75
  obj: { bool: true },