fast-json-stringify 7.0.0 → 7.1.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.
Files changed (39) hide show
  1. package/.github/dependabot.yml +43 -2
  2. package/.github/workflows/ci.yml +1 -1
  3. package/.github/workflows/lock-threads.yml +1 -1
  4. package/.github/workflows/release.yml +24 -0
  5. package/README.md +25 -8
  6. package/benchmark/bench-thread.js +6 -2
  7. package/benchmark/bench.js +86 -0
  8. package/index.js +360 -94
  9. package/lib/location.js +3 -1
  10. package/lib/serializer.js +41 -37
  11. package/lib/standalone.js +22 -3
  12. package/lib/validator.js +22 -3
  13. package/package.json +6 -7
  14. package/test/additionalProperties.test.js +87 -0
  15. package/test/anyof.test.js +42 -0
  16. package/test/array.test.js +1 -1
  17. package/test/basic.test.js +1 -1
  18. package/test/code-generation-fallbacks.test.js +158 -0
  19. package/test/compile-validators.test.js +110 -0
  20. package/test/const.test.js +27 -6
  21. package/test/date.test.js +28 -1
  22. package/test/debug-mode.test.js +8 -0
  23. package/test/deopt.test.js +251 -0
  24. package/test/if-then-else.test.js +145 -0
  25. package/test/inferType.test.js +1 -1
  26. package/test/integer.test.js +1 -1
  27. package/test/oneof.test.js +242 -0
  28. package/test/optional-property-prefix.test.js +119 -0
  29. package/test/patternProperties.test.js +24 -0
  30. package/test/property-names.test.js +41 -0
  31. package/test/regex.test.js +1 -1
  32. package/test/schema-depth.test.js +227 -0
  33. package/test/standalone-mode.test.js +109 -2
  34. package/test/string.test.js +8 -0
  35. package/test/surrogate.test.js +1 -1
  36. package/test/typesArray.test.js +59 -0
  37. package/test/utils/validator.js +20 -0
  38. package/types/index.d.ts +20 -0
  39. package/types/index.tst.ts +13 -0
@@ -2,12 +2,53 @@ version: 2
2
2
  updates:
3
3
  - package-ecosystem: "github-actions"
4
4
  directory: "/"
5
+ commit-message:
6
+ # Prefix all commit messages with "chore: "
7
+ prefix: "chore"
5
8
  schedule:
6
- interval: "monthly"
9
+ interval: "weekly"
10
+ cooldown:
11
+ default-days: 7
12
+ allow:
13
+ - dependency-name: "*"
14
+ update-types:
15
+ - "version-update:semver-major"
7
16
  open-pull-requests-limit: 10
8
17
 
9
18
  - package-ecosystem: "npm"
10
19
  directory: "/"
20
+ commit-message:
21
+ # Prefix all commit messages with "chore: "
22
+ prefix: "chore"
11
23
  schedule:
12
- interval: "monthly"
24
+ interval: "weekly"
25
+ cooldown:
26
+ default-days: 7
27
+ versioning-strategy: "increase-if-necessary"
28
+ allow:
29
+ - dependency-name: "*"
30
+ update-types:
31
+ - "version-update:semver-major"
32
+ ignore:
33
+ # TODO: remove ignore until neostandard support ESLint 10
34
+ - dependency-name: "eslint"
35
+ - dependency-name: "neostandard"
36
+ - dependency-name: "@stylistic/*"
37
+ - dependency-name: "typescript"
13
38
  open-pull-requests-limit: 10
39
+ groups:
40
+ # Production dependencies with breaking changes
41
+ dependencies:
42
+ dependency-type: "production"
43
+ # ESLint related dependencies
44
+ dev-dependencies-eslint:
45
+ patterns:
46
+ - "eslint"
47
+ - "neostandard"
48
+ - "@stylistic/*"
49
+ # TypeScript related dependencies
50
+ dev-dependencies-typescript:
51
+ patterns:
52
+ - "@types/*"
53
+ - "tstyche"
54
+ - "typescript"
@@ -27,7 +27,7 @@ jobs:
27
27
  permissions:
28
28
  contents: write
29
29
  pull-requests: write
30
- uses: fastify/workflows/.github/workflows/plugins-ci.yml@v6
30
+ uses: fastify/workflows/.github/workflows/plugins-ci.yml@ef591e2186785d5ab36b9fe6a79c7ce2f1d94e57 # v7.0.0
31
31
  with:
32
32
  license-check: true
33
33
  lint: true
@@ -16,4 +16,4 @@ jobs:
16
16
  permissions:
17
17
  issues: write
18
18
  pull-requests: write
19
- uses: fastify/workflows/.github/workflows/lock-threads.yml@v6
19
+ uses: fastify/workflows/.github/workflows/lock-threads.yml@ef591e2186785d5ab36b9fe6a79c7ce2f1d94e57 # v7.0.0
@@ -0,0 +1,24 @@
1
+ name: Release the package
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ semver:
7
+ description: 'Release bump type'
8
+ required: true
9
+ type: choice
10
+ options:
11
+ - patch
12
+ - minor
13
+ - major
14
+
15
+ permissions: {}
16
+
17
+ jobs:
18
+ release:
19
+ permissions:
20
+ id-token: write # required for npm provenance via OIDC
21
+ contents: write # required to push the release commit and the tag
22
+ uses: fastify/workflows/.github/workflows/reusable-release.yml@b1a11bce713bc857e52b2231fec3c89071ad3135
23
+ with:
24
+ semver: ${{ inputs.semver }}
package/README.md CHANGED
@@ -11,7 +11,7 @@ Its performance advantage shrinks as your payload grows.
11
11
 
12
12
  ### How it works
13
13
 
14
- 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.
14
+ fast-json-stringify requires a [JSON Schema Draft 7](https://json-schema.org/specification-links#draft-7) input to generate a fast `stringify` function.
15
15
 
16
16
  ##### Benchmarks
17
17
 
@@ -121,15 +121,24 @@ const fastJson = require('fast-json-stringify')
121
121
  const stringify = fastJson(mySchema, {
122
122
  schema: { ... },
123
123
  ajv: { ... },
124
- rounding: 'ceil'
124
+ rounding: 'ceil',
125
+ maxDepth: 100
125
126
  })
126
127
  ```
127
128
 
128
129
  - `schema`: external schemas references by $ref property. [More details](#ref)
129
130
  - `ajv`: [ajv v8 instance's settings](https://ajv.js.org/options.html) for those properties that require `ajv`. [More details](#anyof)
130
131
  - `rounding`: setup how the `integer` types will be rounded when not integers. [More details](#integer)
132
+ - `maxDepth`: maximum number of nested schema levels allowed during compilation.
133
+ Defaults to `100` and may be set from `0` to `100`. Schemas that exceed this limit
134
+ are rejected before compilation.
135
+ - `inlineValidators`: when using standalone mode, embed Ajv-generated validator functions in the output instead of compiling schemas at runtime. [More details](#standalone)
131
136
  - `largeArrayMechanism`: set the mechanism that should be used to handle large
132
137
  (by default `20000` or more items) arrays. [More details](#largearrays)
138
+ - `compileValidators`: when `true`, the `ajv` validators used by `anyOf`, `oneOf` and
139
+ `if/then/else` are compiled during `build()` instead of lazily, on the first serialization
140
+ that reaches them. This makes `build()` slower but removes a potentially large one-off cost
141
+ from the first `stringify()` call. Defaults to `false`.
133
142
 
134
143
 
135
144
  <a name="api"></a>
@@ -137,7 +146,7 @@ const stringify = fastJson(mySchema, {
137
146
  <a name="fastJsonStringify"></a>
138
147
  ### fastJsonStringify(schema)
139
148
 
140
- Build a `stringify()` function based on [jsonschema draft 7 spec](https://json-schema.org/specification-links.html#draft-7).
149
+ Build a `stringify()` function based on [jsonschema draft 7 spec](https://json-schema.org/specification-links#draft-7).
141
150
 
142
151
  Supported types:
143
152
 
@@ -160,7 +169,7 @@ And nested ones, too.
160
169
  | `RegExp` | `string` |
161
170
  | `BigInt` | `integer` via `toString` |
162
171
 
163
- [JSON Schema built-in formats](https://json-schema.org/understanding-json-schema/reference/string.html#built-in-formats) for dates are supported and will be serialized as:
172
+ [JSON Schema built-in formats](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.7.3.1) for dates are supported and will be serialized as:
164
173
 
165
174
  | Format | Serialized format example |
166
175
  | ----------- | -------------------------- |
@@ -168,7 +177,7 @@ And nested ones, too.
168
177
  | `date` | `2020-04-03` |
169
178
  | `time` | `09:11:08` |
170
179
 
171
- **Note**: In the case of a string formatted Date and not Date Object, there will be no manipulation on it. It should be properly formatted.
180
+ **Note**: In the case of a string formatted Date and not Date Object, the value is not reformatted nor validated: it should already be properly formatted. It is only escaped, so that the resulting document is always valid JSON.
172
181
 
173
182
  Example with a Date object:
174
183
 
@@ -436,7 +445,7 @@ console.log(stringify({
436
445
  <a name="ref"></a>
437
446
  #### Reuse - $ref
438
447
  If you want to reuse a definition of a value, you can use the property `$ref`.
439
- The value of `$ref` must be a string in [JSON Pointer](https://tools.ietf.org/html/rfc6901) format.
448
+ The value of `$ref` must be a string in [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) format.
440
449
  Example:
441
450
  ```javascript
442
451
  const schema = {
@@ -561,7 +570,7 @@ const stringify = fastJson(schema, { rounding: 'ceil' })
561
570
  <a name="nullable"></a>
562
571
  #### Nullable
563
572
 
564
- According to the [Open API 3.0 specification](https://swagger.io/docs/specification/data-models/data-types/#null), a value that can be null must be declared `nullable`.
573
+ According to the [Open API 3.0 specification](https://swagger.io/docs/specification/v3_0/data-models/data-types/#null), a value that can be null must be declared `nullable`.
565
574
 
566
575
  ##### Nullable object
567
576
  ```javascript
@@ -724,13 +733,21 @@ const code = fastJson({
724
733
  type: 'string'
725
734
  }
726
735
  }
727
- }, { mode: 'standalone' })
736
+ }, { mode: 'standalone', inlineValidators: true })
728
737
 
729
738
  fs.writeFileSync('stringify.js', code)
730
739
  const stringify = require('stringify.js')
731
740
  console.log(stringify({ firstName: 'Foo', surname: 'bar' })) // '{"firstName":"Foo"}'
732
741
  ```
733
742
 
743
+ Set `inlineValidators` to `true` to include the Ajv-generated functions used by
744
+ `anyOf`, `oneOf`, and `if/then/else` in the same output file. This avoids
745
+ rebuilding an Ajv instance and compiling their schemas at runtime. The
746
+ generated module still requires `fast-json-stringify` for its serializer and
747
+ may require Ajv runtime helpers used by the generated validation code. Custom
748
+ Ajv formats used by these schemas must support Ajv's standalone code
749
+ generation.
750
+
734
751
  <a name="acknowledgments"></a>
735
752
  ## Acknowledgments
736
753
 
@@ -15,10 +15,14 @@ const bench = new Bench({
15
15
  })
16
16
 
17
17
  const FJS = require('..')
18
- const stringify = FJS(benchmark.schema)
18
+ const stringify = benchmark.compile ? null : FJS(benchmark.schema, benchmark.options)
19
19
 
20
20
  bench.add(benchmark.name, () => {
21
- stringify(benchmark.input)
21
+ if (benchmark.compile) {
22
+ FJS(benchmark.schema, benchmark.options)(benchmark.input)
23
+ } else {
24
+ stringify(benchmark.input)
25
+ }
22
26
  }).run().then(() => {
23
27
  const task = bench.tasks[0]
24
28
  const hz = task.result.throughput.mean // ops/sec
@@ -300,6 +300,92 @@ const benchmarks = [
300
300
  },
301
301
  input: { firstName: 'Max', lastName: 'Power', age: 22 }
302
302
  },
303
+ {
304
+ name: 'oneOf external refs compile and first stringify',
305
+ compile: true,
306
+ schema: {
307
+ title: 'External refs oneOf schema',
308
+ type: 'object',
309
+ properties: {
310
+ content: {
311
+ oneOf: [
312
+ { $ref: 'BenchmarkFoo#' },
313
+ { $ref: 'BenchmarkBar#' },
314
+ { $ref: 'BenchmarkBaz#' }
315
+ ]
316
+ }
317
+ }
318
+ },
319
+ options: {
320
+ schema: {
321
+ BenchmarkFoo: {
322
+ type: 'object',
323
+ properties: {
324
+ kind: { const: 'foo' },
325
+ title: { type: 'string' },
326
+ count: { type: 'integer' },
327
+ flags: {
328
+ type: 'array',
329
+ items: { type: 'string' }
330
+ }
331
+ },
332
+ required: ['kind', 'title', 'count', 'flags'],
333
+ additionalProperties: false
334
+ },
335
+ BenchmarkBar: {
336
+ type: 'object',
337
+ properties: {
338
+ kind: { const: 'bar' },
339
+ name: { type: 'string' },
340
+ value: { type: 'number' },
341
+ meta: {
342
+ type: 'object',
343
+ properties: {
344
+ active: { type: 'boolean' },
345
+ tags: {
346
+ type: 'array',
347
+ items: { type: 'string' }
348
+ }
349
+ },
350
+ required: ['active', 'tags'],
351
+ additionalProperties: false
352
+ }
353
+ },
354
+ required: ['kind', 'name', 'value', 'meta'],
355
+ additionalProperties: false
356
+ },
357
+ BenchmarkBaz: {
358
+ type: 'object',
359
+ properties: {
360
+ kind: { const: 'baz' },
361
+ id: { type: 'string' },
362
+ nested: {
363
+ type: 'object',
364
+ properties: {
365
+ score: { type: 'number' },
366
+ note: { type: 'string' }
367
+ },
368
+ required: ['score', 'note'],
369
+ additionalProperties: false
370
+ }
371
+ },
372
+ required: ['kind', 'id', 'nested'],
373
+ additionalProperties: false
374
+ }
375
+ }
376
+ },
377
+ input: {
378
+ content: {
379
+ kind: 'bar',
380
+ name: 'benchmark',
381
+ value: 42.5,
382
+ meta: {
383
+ active: true,
384
+ tags: ['one', 'two', 'three']
385
+ }
386
+ }
387
+ }
388
+ },
303
389
  {
304
390
  name: 'object with const string property',
305
391
  schema: {