fatsify核心功能示例测试!!!

This commit is contained in:
2025-09-21 14:50:41 +08:00
commit 9145aea047
1958 changed files with 230098 additions and 0 deletions

2
node_modules/fast-json-stringify/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1,2 @@
# Set default behavior to automatically convert line endings
* text=auto eol=lf

View File

@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10

21
node_modules/fast-json-stringify/.github/stale.yml generated vendored Normal file
View File

@@ -0,0 +1,21 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 15
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "discussion"
- "feature request"
- "bug"
- "help wanted"
- "plugin suggestion"
- "good first issue"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -0,0 +1,85 @@
name: Benchmark PR
on:
pull_request_target:
types: [labeled]
jobs:
benchmark:
if: ${{ github.event.label.name == 'benchmark' }}
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
PR-BENCH: ${{ steps.benchmark-pr.outputs.BENCH_RESULT }}
MASTER-BENCH: ${{ steps.benchmark-master.outputs.BENCH_RESULT }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{github.event.pull_request.head.sha}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install
run: |
npm install --ignore-scripts
- name: Run benchmark
id: benchmark-pr
run: |
npm run --silent bench > ./bench-result
content=$(cat ./bench-result)
content="${content//'%'/'%25'}"
content="${content//$'\n'/'%0A'}"
content="${content//$'\r'/'%0D'}"
echo "::set-output name=BENCH_RESULT::$content"
# master benchmark
- uses: actions/checkout@v4
with:
ref: 'master'
- name: Install
run: |
npm install --ignore-scripts
- name: Run benchmark
id: benchmark-master
run: |
npm run --silent bench > ./bench-result
content=$(cat ./bench-result)
content="${content//'%'/'%25'}"
content="${content//$'\n'/'%0A'}"
content="${content//$'\r'/'%0D'}"
echo "::set-output name=BENCH_RESULT::$content"
output-benchmark:
if: "always()"
needs: [benchmark]
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Comment PR
uses: thollander/actions-comment-pull-request@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
message: |
**PR**:
```
${{ needs.benchmark.outputs.PR-BENCH }}
```
**MASTER**:
```
${{ needs.benchmark.outputs.MASTER-BENCH }}
```
- uses: actions-ecosystem/action-remove-labels@v1
with:
labels: |
benchmark
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,23 @@
name: CI
on:
push:
branches:
- main
- master
- next
- 'v*'
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
test:
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5
with:
license-check: true
lint: true

21
node_modules/fast-json-stringify/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Matteo Collina
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

739
node_modules/fast-json-stringify/README.md generated vendored Normal file
View File

@@ -0,0 +1,739 @@
# fast-json-stringify
[![CI](https://github.com/fastify/fast-json-stringify/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/fastify/fast-json-stringify/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/fast-json-stringify.svg?style=flat)](https://www.npmjs.com/package/fast-json-stringify)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)
[![NPM downloads](https://img.shields.io/npm/dm/fast-json-stringify.svg?style=flat)](https://www.npmjs.com/package/fast-json-stringify)
__fast-json-stringify__ is significantly faster than `JSON.stringify()` for small payloads.
Its performance advantage shrinks as your payload grows.
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`.
### How it works
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.
##### Benchmarks
- Machine: `EX41S-SSD, Intel Core i7, 4Ghz, 64GB RAM, 4C/8T, SSD`.
- Node.js `v18.12.1`
```
FJS creation x 4,129 ops/sec ±0.82% (92 runs sampled)
CJS creation x 184,196 ops/sec ±0.12% (97 runs sampled)
AJV Serialize creation x 61,130,591 ops/sec ±0.40% (92 runs sampled)
JSON.stringify array x 5,057 ops/sec ±0.10% (100 runs sampled)
fast-json-stringify array default x 6,243 ops/sec ±0.14% (98 runs sampled)
fast-json-stringify array json-stringify x 6,261 ops/sec ±0.30% (99 runs sampled)
compile-json-stringify array x 6,842 ops/sec ±0.18% (96 runs sampled)
AJV Serialize array x 6,964 ops/sec ±0.11% (95 runs sampled)
JSON.stringify large array x 248 ops/sec ±0.07% (90 runs sampled)
fast-json-stringify large array default x 99.96 ops/sec ±0.22% (74 runs sampled)
fast-json-stringify large array json-stringify x 248 ops/sec ±0.07% (90 runs sampled)
compile-json-stringify large array x 317 ops/sec ±0.09% (89 runs sampled)
AJV Serialize large array x 111 ops/sec ±0.07% (33 runs sampled)
JSON.stringify long string x 16,002 ops/sec ±0.09% (98 runs sampled)
fast-json-stringify long string x 15,979 ops/sec ±0.09% (96 runs sampled)
compile-json-stringify long string x 15,952 ops/sec ±0.31% (97 runs sampled)
AJV Serialize long string x 21,416 ops/sec ±0.08% (98 runs sampled)
JSON.stringify short string x 12,944,272 ops/sec ±0.09% (96 runs sampled)
fast-json-stringify short string x 30,585,790 ops/sec ±0.27% (97 runs sampled)
compile-json-stringify short string x 30,656,406 ops/sec ±0.12% (96 runs sampled)
AJV Serialize short string x 30,406,785 ops/sec ±0.37% (96 runs sampled)
JSON.stringify obj x 3,153,043 ops/sec ±0.33% (99 runs sampled)
fast-json-stringify obj x 6,866,434 ops/sec ±0.11% (100 runs sampled)
compile-json-stringify obj x 15,886,723 ops/sec ±0.15% (98 runs sampled)
AJV Serialize obj x 8,969,043 ops/sec ±0.36% (97 runs sampled)
JSON stringify date x 1,126,547 ops/sec ±0.09% (97 runs sampled)
fast-json-stringify date format x 1,836,188 ops/sec ±0.12% (99 runs sampled)
compile-json-stringify date format x 1,125,735 ops/sec ±0.19% (98 runs sampled)
```
#### Table of contents:
- <a href="#example">`Example`</a>
- <a href="#options">`Options`</a>
- <a href="#api">`API`</a>
- <a href="#fastJsonStringify">`fastJsonStringify`</a>
- <a href="#specific">`Specific use cases`</a>
- <a href="#required">`Required`</a>
- <a href="#missingFields">`Missing fields`</a>
- <a href="#patternProperties">`Pattern Properties`</a>
- <a href="#additionalProperties">`Additional Properties`</a>
- <a href="#AnyOf-and-OneOf">`AnyOf` and `OneOf`</a>
- <a href="#ref">`Reuse - $ref`</a>
- <a href="#long">`Long integers`</a>
- <a href="#integer">`Integers`</a>
- <a href="#nullable">`Nullable`</a>
- <a href="#largearrays">`Large Arrays`</a>
- <a href="#security">`Security Notice`</a>
- <a href="#debug">`Debug Mode`</a>
- <a href="#standalone">`Standalone Mode`</a>
- <a href="#acknowledgments">`Acknowledgments`</a>
- <a href="#license">`License`</a>
<a name="example"></a>
Try it out on RunKit: <a href="https://runkit.com/npm/fast-json-stringify">https://runkit.com/npm/fast-json-stringify</a>
## Example
```js
const fastJson = require('fast-json-stringify')
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer'
},
reg: {
type: 'string'
}
}
})
console.log(stringify({
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
reg: /"([^"]|\\")*"/
}))
```
<a name="options"></a>
## Options
Optionally, you may provide to `fast-json-stringify` an option object as the second parameter:
```js
const fastJson = require('fast-json-stringify')
const stringify = fastJson(mySchema, {
schema: { ... },
ajv: { ... },
rounding: 'ceil'
})
```
- `schema`: external schemas references by $ref property. [More details](#ref)
- `ajv`: [ajv v8 instance's settings](https://ajv.js.org/options.html) for those properties that require `ajv`. [More details](#anyof)
- `rounding`: setup how the `integer` types will be rounded when not integers. [More details](#integer)
- `largeArrayMechanism`: set the mechanism that should be used to handle large
(by default `20000` or more items) arrays. [More details](#largearrays)
<a name="api"></a>
## API
<a name="fastJsonStringify"></a>
### fastJsonStringify(schema)
Build a `stringify()` function based on [jsonschema draft 7 spec](https://json-schema.org/specification-links.html#draft-7).
Supported types:
* `'string'`
* `'integer'`
* `'number'`
* `'array'`
* `'object'`
* `'boolean'`
* `'null'`
And nested ones, too.
<a name="specific"></a>
#### Specific use cases
| Instance | Serialized as |
| -------- | ---------------------------- |
| `Date` | `string` via `toISOString()` |
| `RegExp` | `string` |
| `BigInt` | `integer` via `toString` |
[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:
| Format | Serialized format example |
| ----------- | -------------------------- |
| `date-time` | `2020-04-03T09:11:08.615Z` |
| `date` | `2020-04-03` |
| `time` | `09:11:08` |
**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.
Example with a Date object:
```javascript
const stringify = fastJson({
title: 'Example Schema with string date-time field',
type: 'string',
format: 'date-time'
})
const date = new Date()
console.log(stringify(date)) // '"YYYY-MM-DDTHH:mm:ss.sssZ"'
```
<a name="required"></a>
#### Required
You can set specific fields of an object as required in your schema by adding the field name inside the `required` array in your schema.
Example:
```javascript
const schema = {
title: 'Example Schema with required field',
type: 'object',
properties: {
nickname: {
type: 'string'
},
mail: {
type: 'string'
}
},
required: ['mail']
}
```
If the object to stringify is missing the required field(s), `fast-json-stringify` will throw an error.
<a name="missingFields"></a>
#### Missing fields
If a field *is present* in the schema (and is not required) but it *is not present* in the object to stringify, `fast-json-stringify` will not write it in the final string.
Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
nickname: {
type: 'string'
},
mail: {
type: 'string'
}
}
})
const obj = {
mail: 'mail@example.com'
}
console.log(stringify(obj)) // '{"mail":"mail@example.com"}'
```
<a name="defaults"></a>
#### Defaults
`fast-json-stringify` supports `default` jsonschema key in order to serialize a value
if it is `undefined` or not present.
Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
nickname: {
type: 'string',
default: 'the default string'
}
}
})
console.log(stringify({})) // '{"nickname":"the default string"}'
console.log(stringify({nickname: 'my-nickname'})) // '{"nickname":"my-nickname"}'
```
<a name="patternProperties"></a>
#### Pattern properties
`fast-json-stringify` supports pattern properties as defined by JSON schema.
*patternProperties* must be an object, where the key is a valid regex and the value is an object, declared in this way: `{ type: 'type' }`.
*patternProperties* will work only for the properties that are not explicitly listed in the properties object.
Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
nickname: {
type: 'string'
}
},
patternProperties: {
'num': {
type: 'number'
},
'.*foo$': {
type: 'string'
}
}
})
const obj = {
nickname: 'nick',
matchfoo: 42,
otherfoo: 'str',
matchnum: 3
}
console.log(stringify(obj)) // '{"matchfoo":"42","otherfoo":"str","matchnum":3,"nickname":"nick"}'
```
<a name="additionalProperties"></a>
#### Additional properties
`fast-json-stringify` supports additional properties as defined by JSON schema.
*additionalProperties* must be an object or a boolean, declared in this way: `{ type: 'type' }`.
*additionalProperties* will work only for the properties that are not explicitly listed in the *properties* and *patternProperties* objects.
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>.
Missing fields are ignored to avoid having to rewrite objects before serializing. However, other schema rules would throw in similar situations.
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.
The additional properties will always be serialized at the end of the object.
Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
nickname: {
type: 'string'
}
},
patternProperties: {
'num': {
type: 'number'
},
'.*foo$': {
type: 'string'
}
},
additionalProperties: {
type: 'string'
}
})
const obj = {
nickname: 'nick',
matchfoo: 42,
otherfoo: 'str',
matchnum: 3,
nomatchstr: 'valar morghulis',
nomatchint: 313
}
console.log(stringify(obj)) // '{"nickname":"nick","matchfoo":"42","otherfoo":"str","matchnum":3,"nomatchstr":"valar morghulis",nomatchint:"313"}'
```
#### AnyOf and OneOf
`fast-json-stringify` supports the **anyOf** and **oneOf** keywords as defined by JSON schema. Both must be an array of valid JSON schemas. The different schemas will be tested in the specified order. The more schemas `stringify` has to try before finding a match, the slower it will be.
*anyOf* and *oneOf* use [ajv](https://www.npmjs.com/package/ajv) as a JSON schema validator to find the schema that matches the data. This has an impact on performance—only use it as a last resort.
Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
'undecidedType': {
'anyOf': [{
type: 'string'
}, {
type: 'boolean'
}]
}
}
})
```
When specifying object JSON schemas for *anyOf*, add *required* validation keyword to match only the objects with the properties you want.
Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'array',
items: {
anyOf: [
{
type: 'object',
properties: {
savedId: { type: 'string' }
},
// without "required" validation any object will match
required: ['savedId']
},
{
type: 'object',
properties: {
error: { type: 'string' }
},
required: ['error']
}
]
}
})
```
<a name="if-then-else"></a>
#### If/then/else
`fast-json-stringify` supports `if/then/else` jsonschema feature. See [ajv documentation](https://ajv.js.org/keywords.html#ifthenelse).
Example:
```javascript
const stringify = fastJson({
'type': 'object',
'properties': {
},
'if': {
'properties': {
'kind': { 'type': 'string', 'enum': ['foobar'] }
}
},
'then': {
'properties': {
'kind': { 'type': 'string', 'enum': ['foobar'] },
'foo': { 'type': 'string' },
'bar': { 'type': 'number' }
}
},
'else': {
'properties': {
'kind': { 'type': 'string', 'enum': ['greeting'] },
'hi': { 'type': 'string' },
'hello': { 'type': 'number' }
}
}
})
console.log(stringify({
kind: 'greeting',
foo: 'FOO',
bar: 42,
hi: 'HI',
hello: 45
})) // {"kind":"greeting","hi":"HI","hello":45}
console.log(stringify({
kind: 'foobar',
foo: 'FOO',
bar: 42,
hi: 'HI',
hello: 45
})) // {"kind":"foobar","foo":"FOO","bar":42}
```
**NB** Do not declare the properties twice or you will print them twice!
<a name="ref"></a>
#### Reuse - $ref
If you want to reuse a definition of a value, you can use the property `$ref`.
The value of `$ref` must be a string in [JSON Pointer](https://tools.ietf.org/html/rfc6901) format.
Example:
```javascript
const schema = {
title: 'Example Schema',
definitions: {
num: {
type: 'object',
properties: {
int: {
type: 'integer'
}
}
},
str: {
type: 'string'
}
},
type: 'object',
properties: {
nickname: {
$ref: '#/definitions/str'
}
},
patternProperties: {
'num': {
$ref: '#/definitions/num'
}
},
additionalProperties: {
$ref: '#/definitions/def'
}
}
const stringify = fastJson(schema)
```
If you need to use an external definition, you can pass it as an option to `fast-json-stringify`.
Example:
```javascript
const schema = {
title: 'Example Schema',
type: 'object',
properties: {
nickname: {
$ref: 'strings#/definitions/str'
}
},
patternProperties: {
'num': {
$ref: 'numbers#/definitions/num'
}
},
additionalProperties: {
$ref: 'strings#/definitions/def'
}
}
const externalSchema = {
numbers: {
definitions: {
num: {
type: 'object',
properties: {
int: {
type: 'integer'
}
}
}
}
},
strings: require('./string-def.json')
}
const stringify = fastJson(schema, { schema: externalSchema })
```
External definitions can also reference each other.
Example:
```javascript
const schema = {
title: 'Example Schema',
type: 'object',
properties: {
foo: {
$ref: 'strings#/definitions/foo'
}
}
}
const externalSchema = {
strings: {
definitions: {
foo: {
$ref: 'things#/definitions/foo'
}
}
},
things: {
definitions: {
foo: {
type: 'string'
}
}
}
}
const stringify = fastJson(schema, { schema: externalSchema })
```
<a name="long"></a>
#### Long integers
By default, the library will handle automatically [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).
<a name="integer"></a>
#### Integers
The `type: integer` property will be truncated if a floating point is provided.
You can customize this behavior with the `rounding` option that will accept [`round`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round), [`ceil`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil), [`floor`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor), or [`trunc`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc). Default is `trunc`:
```js
const stringify = fastJson(schema, { rounding: 'ceil' })
```
<a name="nullable"></a>
#### Nullable
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`.
##### Nullable object
```javascript
const stringify = fastJson({
'title': 'Nullable schema',
'type': 'object',
'nullable': true,
'properties': {
'product': {
'nullable': true,
'type': 'object',
'properties': {
'name': {
'type': 'string'
}
}
}
}
})
console.log(stringify({product: {name: "hello"}})) // "{"product":{"name":"hello"}}"
console.log(stringify({product: null})) // "{"product":null}"
console.log(stringify(null)) // null
```
Otherwise, instead of raising an error, null values will be coerced as follows:
- `integer` -> `0`
- `number` -> `0`
- `string` -> `""`
- `boolean` -> `false`
- `object` -> `{}`
- `array` -> `[]`
<a name="largearrays"></a>
#### Large Arrays
Large arrays are, for the scope of this document, defined as arrays containing,
by default, `20000` elements or more. That value can be adjusted via the option
parameter `largeArraySize`.
At some point the overhead caused by the default mechanism used by
`fast-json-stringify` to handle arrays starts increasing exponentially, leading
to slow overall executions.
##### Settings
In order to improve that the user can set the `largeArrayMechanism` and
`largeArraySize` options.
`largeArrayMechanism`'s default value is `default`. Valid values for it are:
- `default` - This option is a compromise between performance and feature set by
still providing the expected functionality out of this lib but giving up some
possible performance gain. With this option set, **large arrays** would be
stringified by joining their stringified elements using `Array.join` instead of
string concatenation for better performance
- `json-stringify` - This option will remove support for schema validation
within **large arrays** completely. By doing so the overhead previously
mentioned is nulled, greatly improving execution time. Mind there's no change
in behavior for arrays not considered _large_
`largeArraySize`'s default value is `20000`. Valid values for it are
integer-like values, such as:
- `20000`
- `2e4`
- `'20000'`
- `'2e4'` - _note this will be converted to `2`, not `20000`_
- `1.5` - _note this will be converted to `1`_
<a name="unsafe"></a>
#### Unsafe string
By default, the library escapes all strings. With the 'unsafe' format, the string isn't escaped. This has a potentially dangerous security issue. You can use it only if you are sure that your data doesn't need escaping. The advantage is a significant performance improvement.
Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
'code': {
type: 'string',
format 'unsafe'
}
}
})
```
##### Benchmarks
For reference, here are some benchmarks for comparison over the three
mechanisms. Benchmarks were conducted on an old machine.
- Machine: `ST1000LM024 HN-M 1TB HDD, Intel Core i7-3610QM @ 2.3GHz, 12GB RAM, 4C/8T`.
- Node.js `v16.13.1`
```
JSON.stringify large array x 157 ops/sec ±0.73% (86 runs sampled)
fast-json-stringify large array default x 48.72 ops/sec ±4.92% (48 runs sampled)
fast-json-stringify large array json-stringify x 157 ops/sec ±0.76% (86 runs sampled)
compile-json-stringify large array x 175 ops/sec ±4.47% (79 runs sampled)
AJV Serialize large array x 58.76 ops/sec ±4.59% (60 runs sampled)
```
<a name="security"></a>
## Security notice
Treat the schema definition as application code, it
is not safe to use user-provided schemas.
To achieve low cost and high performance redaction `fast-json-stringify`
creates and compiles a function (using the `Function` constructor) on initialization.
While the `schema` is currently validated for any developer errors,
there is no guarantee that supplying user-generated schema could not
expose your application to remote attacks.
Users are responsible for sending trusted data. `fast-json-stringify` guarantees that you will get
a valid output only if your input matches the schema or can be coerced to the schema. If your input
doesn't match the schema, you will get undefined behavior.
<a name="debug"></a>
### Debug Mode
The debug mode can be activated during your development to understand what is going on when things do not
work as you expect.
```js
const debugCompiled = fastJson({
title: 'default string',
type: 'object',
properties: {
firstName: {
type: 'string'
}
}
}, { mode: 'debug' })
console.log(debugCompiled) // it is a object contain code, ajv instance
const rawString = debugCompiled.code // it is the generated code
console.log(rawString)
const stringify = fastJson.restore(debugCompiled) // use the generated string to get back the `stringify` function
console.log(stringify({ firstName: 'Foo', surname: 'bar' })) // '{"firstName":"Foo"}'
```
<a name="standalone"></a>
### Standalone Mode
The standalone mode is used to compile the code that can be directly run by `node`
itself. You need to have `fast-json-stringify` installed for the standalone code to work.
```js
const fs = require('fs')
const code = fastJson({
title: 'default string',
type: 'object',
properties: {
firstName: {
type: 'string'
}
}
}, { mode: 'standalone' })
fs.writeFileSync('stringify.js', code)
const stringify = require('stringify.js')
console.log(stringify({ firstName: 'Foo', surname: 'bar' })) // '{"firstName":"Foo"}'
```
<a name="acknowledgments"></a>
## Acknowledgments
This project was kindly sponsored by [nearForm](https://nearform.com).
<a name="license"></a>
## License
Licensed under [MIT](./LICENSE).

View File

@@ -0,0 +1,116 @@
'use strict'
const { spawn } = require('child_process')
const cliSelect = require('cli-select')
const simpleGit = require('simple-git')
const git = simpleGit(process.cwd())
const COMMAND = 'npm run bench'
const DEFAULT_BRANCH = 'master'
const PERCENT_THRESHOLD = 5
const greyColor = '\x1b[30m'
const redColor = '\x1b[31m'
const greenColor = '\x1b[32m'
const resetColor = '\x1b[0m'
async function selectBranchName (message, branches) {
console.log(message)
const result = await cliSelect({
type: 'list',
name: 'branch',
values: branches
})
console.log(result.value)
return result.value
}
async function executeCommandOnBranch (command, branch) {
console.log(`${greyColor}Checking out "${branch}"${resetColor}`)
await git.checkout(branch)
console.log(`${greyColor}Execute "${command}"${resetColor}`)
const childProcess = spawn(command, { stdio: 'pipe', shell: true })
let result = ''
childProcess.stdout.on('data', (data) => {
process.stdout.write(data.toString())
result += data.toString()
})
await new Promise(resolve => childProcess.on('close', resolve))
console.log()
return parseBenchmarksStdout(result)
}
function parseBenchmarksStdout (text) {
const results = []
const lines = text.split('\n')
for (const line of lines) {
const match = /^(.+?)(\.*) x (.+) ops\/sec .*$/.exec(line)
if (match !== null) {
results.push({
name: match[1],
alignedName: match[1] + match[2],
result: parseInt(match[3].split(',').join(''))
})
}
}
return results
}
function compareResults (featureBranch, mainBranch) {
for (const { name, alignedName, result: mainBranchResult } of mainBranch) {
const featureBranchBenchmark = featureBranch.find(result => result.name === name)
if (featureBranchBenchmark) {
const featureBranchResult = featureBranchBenchmark.result
const percent = (featureBranchResult - mainBranchResult) * 100 / mainBranchResult
const roundedPercent = Math.round(percent * 100) / 100
const percentString = roundedPercent > 0 ? `+${roundedPercent}%` : `${roundedPercent}%`
const message = alignedName + percentString.padStart(7, '.')
if (roundedPercent > PERCENT_THRESHOLD) {
console.log(`${greenColor}${message}${resetColor}`)
} else if (roundedPercent < -PERCENT_THRESHOLD) {
console.log(`${redColor}${message}${resetColor}`)
} else {
console.log(message)
}
}
}
}
(async function () {
const branches = await git.branch()
const currentBranch = branches.branches[branches.current]
let featureBranch = null
let mainBranch = null
if (process.argv[2] === '--ci') {
featureBranch = currentBranch.name
mainBranch = DEFAULT_BRANCH
} else {
featureBranch = await selectBranchName('Select the branch you want to compare (feature branch):', branches.all)
mainBranch = await selectBranchName('Select the branch you want to compare with (main branch):', branches.all)
}
try {
const featureBranchResult = await executeCommandOnBranch(COMMAND, featureBranch)
const mainBranchResult = await executeCommandOnBranch(COMMAND, mainBranch)
compareResults(featureBranchResult, mainBranchResult)
} catch (error) {
console.error('Switch to origin branch due to an error', error.message)
}
await git.checkout(currentBranch.commit)
await git.checkout(currentBranch.name)
console.log(`${greyColor}Back to ${currentBranch.name} ${currentBranch.commit}${resetColor}`)
})()

View File

@@ -0,0 +1,280 @@
'use strict'
const benchmark = require('benchmark')
const suite = new benchmark.Suite()
const STR_LEN = 1e4
const LARGE_ARRAY_SIZE = 2e4
const MULTI_ARRAY_LENGTH = 1e3
const schema = {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
}
}
}
const schemaCJS = {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'number',
minimum: 0
}
}
}
const schemaAJVJTD = {
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string',
nullable: true
},
age: {
type: 'uint8'
}
}
}
const arraySchema = {
title: 'array schema',
type: 'array',
items: schema
}
const arraySchemaCJS = {
title: 'array schema',
type: 'array',
items: schemaCJS
}
const arraySchemaAJVJTD = {
elements: schemaAJVJTD
}
const dateFormatSchema = {
description: 'Date of birth',
type: 'string',
format: 'date'
}
const dateFormatSchemaCJS = {
description: 'Date of birth',
type: 'string',
format: 'date'
}
const obj = {
firstName: 'Matteo',
lastName: 'Collina',
age: 32
}
const date = new Date()
const multiArray = new Array(MULTI_ARRAY_LENGTH)
const largeArray = new Array(LARGE_ARRAY_SIZE)
const CJS = require('compile-json-stringify')
const CJSStringify = CJS(schemaCJS)
const CJSStringifyArray = CJS(arraySchemaCJS)
const CJSStringifyDate = CJS(dateFormatSchemaCJS)
const CJSStringifyString = CJS({ type: 'string' })
const FJS = require('..')
const stringify = FJS(schema)
const stringifyArrayDefault = FJS(arraySchema)
const stringifyArrayJSONStringify = FJS(arraySchema, {
largeArrayMechanism: 'json-stringify'
})
const stringifyDate = FJS(dateFormatSchema)
const stringifyString = FJS({ type: 'string' })
let str = ''
const Ajv = require('ajv/dist/jtd')
const ajv = new Ajv()
const ajvSerialize = ajv.compileSerializer(schemaAJVJTD)
const ajvSerializeArray = ajv.compileSerializer(arraySchemaAJVJTD)
const ajvSerializeString = ajv.compileSerializer({ type: 'string' })
const getRandomString = (length) => {
if (!Number.isInteger(length)) {
throw new Error('Expected integer length')
}
const validCharacters = 'abcdefghijklmnopqrstuvwxyz'
const nValidCharacters = 26
let result = ''
for (let i = 0; i < length; ++i) {
result += validCharacters[Math.floor(Math.random() * nValidCharacters)]
}
return result[0].toUpperCase() + result.slice(1)
}
for (let i = 0; i < STR_LEN; i++) {
largeArray[i] = {
firstName: getRandomString(8),
lastName: getRandomString(6),
age: Math.ceil(Math.random() * 99)
}
str += i
if (i % 100 === 0) {
str += '"'
}
}
for (let i = STR_LEN; i < LARGE_ARRAY_SIZE; ++i) {
largeArray[i] = {
firstName: getRandomString(10),
lastName: getRandomString(4),
age: Math.ceil(Math.random() * 99)
}
}
Number(str)
for (let i = 0; i < MULTI_ARRAY_LENGTH; i++) {
multiArray[i] = obj
}
suite.add('FJS creation', function () {
FJS(schema)
})
suite.add('CJS creation', function () {
CJS(schemaCJS)
})
suite.add('AJV Serialize creation', function () {
ajv.compileSerializer(schemaAJVJTD)
})
suite.add('JSON.stringify array', function () {
JSON.stringify(multiArray)
})
suite.add('fast-json-stringify array default', function () {
stringifyArrayDefault(multiArray)
})
suite.add('fast-json-stringify array json-stringify', function () {
stringifyArrayJSONStringify(multiArray)
})
suite.add('compile-json-stringify array', function () {
CJSStringifyArray(multiArray)
})
suite.add('AJV Serialize array', function () {
ajvSerializeArray(multiArray)
})
suite.add('JSON.stringify large array', function () {
JSON.stringify(largeArray)
})
suite.add('fast-json-stringify large array default', function () {
stringifyArrayDefault(largeArray)
})
suite.add('fast-json-stringify large array json-stringify', function () {
stringifyArrayJSONStringify(largeArray)
})
suite.add('compile-json-stringify large array', function () {
CJSStringifyArray(largeArray)
})
suite.add('AJV Serialize large array', function () {
ajvSerializeArray(largeArray)
})
suite.add('JSON.stringify long string', function () {
JSON.stringify(str)
})
suite.add('fast-json-stringify long string', function () {
stringifyString(str)
})
suite.add('compile-json-stringify long string', function () {
CJSStringifyString(str)
})
suite.add('AJV Serialize long string', function () {
ajvSerializeString(str)
})
suite.add('JSON.stringify short string', function () {
JSON.stringify('hello world')
})
suite.add('fast-json-stringify short string', function () {
stringifyString('hello world')
})
suite.add('compile-json-stringify short string', function () {
CJSStringifyString('hello world')
})
suite.add('AJV Serialize short string', function () {
ajvSerializeString('hello world')
})
suite.add('JSON.stringify obj', function () {
JSON.stringify(obj)
})
suite.add('fast-json-stringify obj', function () {
stringify(obj)
})
suite.add('compile-json-stringify obj', function () {
CJSStringify(obj)
})
suite.add('AJV Serialize obj', function () {
ajvSerialize(obj)
})
suite.add('JSON stringify date', function () {
JSON.stringify(date)
})
suite.add('fast-json-stringify date format', function () {
stringifyDate(date)
})
suite.add('compile-json-stringify date format', function () {
CJSStringifyDate(date)
})
suite.on('cycle', cycle)
suite.run()
function cycle (e) {
console.log(e.target.toString())
}

View File

@@ -0,0 +1,21 @@
'use strict'
const { workerData: benchmark, parentPort } = require('worker_threads')
const Benchmark = require('benchmark')
Benchmark.options.minSamples = 100
const suite = Benchmark.Suite()
const FJS = require('..')
const stringify = FJS(benchmark.schema)
suite
.add(benchmark.name, () => {
stringify(benchmark.input)
})
.on('cycle', (event) => {
parentPort.postMessage(String(event.target))
})
.on('complete', () => {})
.run()

391
node_modules/fast-json-stringify/benchmark/bench.js generated vendored Normal file
View File

@@ -0,0 +1,391 @@
'use strict'
const path = require('path')
const { Worker } = require('worker_threads')
const BENCH_THREAD_PATH = path.join(__dirname, 'bench-thread.js')
const LONG_STRING_LENGTH = 1e4
const SHORT_ARRAY_SIZE = 1e3
const shortArrayOfNumbers = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfIntegers = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfShortStrings = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfLongStrings = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfMultiObject = new Array(SHORT_ARRAY_SIZE)
function getRandomInt (max) {
return Math.floor(Math.random() * max)
}
let longSimpleString = ''
for (let i = 0; i < LONG_STRING_LENGTH; i++) {
longSimpleString += i
}
let longString = ''
for (let i = 0; i < LONG_STRING_LENGTH; i++) {
longString += i
if (i % 100 === 0) {
longString += '"'
}
}
for (let i = 0; i < SHORT_ARRAY_SIZE; i++) {
shortArrayOfNumbers[i] = getRandomInt(1000)
shortArrayOfIntegers[i] = getRandomInt(1000)
shortArrayOfShortStrings[i] = 'hello world'
shortArrayOfLongStrings[i] = longString
shortArrayOfMultiObject[i] = { s: 'hello world', n: 42, b: true }
}
const benchmarks = [
{
name: 'short string',
schema: {
type: 'string'
},
input: 'hello world'
},
{
name: 'unsafe short string',
schema: {
type: 'string',
format: 'unsafe'
},
input: 'hello world'
},
{
name: 'short string with double quote',
schema: {
type: 'string'
},
input: 'hello " world'
},
{
name: 'long string without double quotes',
schema: {
type: 'string'
},
input: longSimpleString
},
{
name: 'unsafe long string without double quotes',
schema: {
type: 'string',
format: 'unsafe'
},
input: longSimpleString
},
{
name: 'long string',
schema: {
type: 'string'
},
input: longString
},
{
name: 'unsafe long string',
schema: {
type: 'string',
format: 'unsafe'
},
input: longString
},
{
name: 'number',
schema: {
type: 'number'
},
input: 42
},
{
name: 'integer',
schema: {
type: 'integer'
},
input: 42
},
{
name: 'formatted date-time',
schema: {
type: 'string',
format: 'date-time'
},
input: new Date()
},
{
name: 'formatted date',
schema: {
type: 'string',
format: 'date'
},
input: new Date()
},
{
name: 'formatted time',
schema: {
type: 'string',
format: 'time'
},
input: new Date()
},
{
name: 'short array of numbers',
schema: {
type: 'array',
items: { type: 'number' }
},
input: shortArrayOfNumbers
},
{
name: 'short array of integers',
schema: {
type: 'array',
items: { type: 'integer' }
},
input: shortArrayOfIntegers
},
{
name: 'short array of short strings',
schema: {
type: 'array',
items: { type: 'string' }
},
input: shortArrayOfShortStrings
},
{
name: 'short array of long strings',
schema: {
type: 'array',
items: { type: 'string' }
},
input: shortArrayOfShortStrings
},
{
name: 'short array of objects with properties of different types',
schema: {
type: 'array',
items: {
type: 'object',
properties: {
s: { type: 'string' },
n: { type: 'number' },
b: { type: 'boolean' }
}
}
},
input: shortArrayOfMultiObject
},
{
name: 'object with number property',
schema: {
type: 'object',
properties: {
a: { type: 'number' }
}
},
input: { a: 42 }
},
{
name: 'object with integer property',
schema: {
type: 'object',
properties: {
a: { type: 'integer' }
}
},
input: { a: 42 }
},
{
name: 'object with short string property',
schema: {
type: 'object',
properties: {
a: { type: 'string' }
}
},
input: { a: 'hello world' }
},
{
name: 'object with long string property',
schema: {
type: 'object',
properties: {
a: { type: 'string' }
}
},
input: { a: longString }
},
{
name: 'object with properties of different types',
schema: {
type: 'object',
properties: {
s1: { type: 'string' },
n1: { type: 'number' },
b1: { type: 'boolean' },
s2: { type: 'string' },
n2: { type: 'number' },
b2: { type: 'boolean' },
s3: { type: 'string' },
n3: { type: 'number' },
b3: { type: 'boolean' },
s4: { type: 'string' },
n4: { type: 'number' },
b4: { type: 'boolean' },
s5: { type: 'string' },
n5: { type: 'number' },
b5: { type: 'boolean' }
}
},
input: {
s1: 'hello world',
n1: 42,
b1: true,
s2: 'hello world',
n2: 42,
b2: true,
s3: 'hello world',
n3: 42,
b3: true,
s4: 'hello world',
n4: 42,
b4: true,
s5: 'hello world',
n5: 42,
b5: true
}
},
{
name: 'simple object',
schema: {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
}
}
},
input: { firstName: 'Max', lastName: 'Power', age: 22 }
},
{
name: 'simple object with required fields',
schema: {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
}
},
required: ['firstName', 'lastName', 'age']
},
input: { firstName: 'Max', lastName: 'Power', age: 22 }
},
{
name: 'object with const string property',
schema: {
type: 'object',
properties: {
a: { const: 'const string' }
}
},
input: { a: 'const string' }
},
{
name: 'object with const number property',
schema: {
type: 'object',
properties: {
a: { const: 1 }
}
},
input: { a: 1 }
},
{
name: 'object with const bool property',
schema: {
type: 'object',
properties: {
a: { const: true }
}
},
input: { a: true }
},
{
name: 'object with const object property',
schema: {
type: 'object',
properties: {
foo: { const: { bar: 'baz' } }
}
},
input: {
foo: { bar: 'baz' }
}
},
{
name: 'object with const null property',
schema: {
type: 'object',
properties: {
foo: { const: null }
}
},
input: {
foo: null
}
}
]
async function runBenchmark (benchmark) {
const worker = new Worker(BENCH_THREAD_PATH, { workerData: benchmark })
return new Promise((resolve, reject) => {
let result = null
worker.on('error', reject)
worker.on('message', (benchResult) => {
result = benchResult
})
worker.on('exit', (code) => {
if (code === 0) {
resolve(result)
} else {
reject(new Error(`Worker stopped with exit code ${code}`))
}
})
})
}
async function runBenchmarks () {
let maxNameLength = 0
for (const benchmark of benchmarks) {
maxNameLength = Math.max(benchmark.name.length, maxNameLength)
}
for (const benchmark of benchmarks) {
benchmark.name = benchmark.name.padEnd(maxNameLength, '.')
const resultMessage = await runBenchmark(benchmark)
console.log(resultMessage)
}
}
runBenchmarks()

View File

@@ -0,0 +1,26 @@
'use strict'
const Ajv = require('ajv')
const standaloneCode = require('ajv/dist/standalone').default
const ajvFormats = require('ajv-formats')
const fs = require('fs')
const path = require('path')
const ajv = new Ajv({
addUsedSchema: false,
allowUnionTypes: true,
code: {
source: true,
lines: true,
optimize: 3
}
})
ajvFormats(ajv)
const schema = require('ajv/lib/refs/json-schema-draft-07.json')
const validate = ajv.compile(schema)
const validationCode = standaloneCode(ajv, validate)
const moduleCode = `/* CODE GENERATED BY '${path.basename(__filename)}' DO NOT EDIT! */\n${validationCode}`
fs.writeFileSync(path.join(__dirname, '../lib/schema-validator.js'), moduleCode)

9
node_modules/fast-json-stringify/eslint.config.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
'use strict'
module.exports = require('neostandard')({
ignores: [
...require('neostandard').resolveIgnoresFromGitignore(),
'lib/schema-validator.js'
],
ts: true
})

81
node_modules/fast-json-stringify/examples/example.js generated vendored Normal file
View File

@@ -0,0 +1,81 @@
'use strict'
const fastJson = require('..')
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer'
},
now: {
type: 'string'
},
birthdate: {
type: ['string'],
format: 'date-time'
},
reg: {
type: 'string'
},
obj: {
type: 'object',
properties: {
bool: {
type: 'boolean'
}
}
},
arr: {
type: 'array',
items: {
type: 'object',
properties: {
str: {
type: 'string'
}
}
}
}
},
required: ['now'],
patternProperties: {
'.*foo$': {
type: 'string'
},
test: {
type: 'number'
},
date: {
type: 'string',
format: 'date-time'
}
},
additionalProperties: {
type: 'string'
}
})
console.log(stringify({
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
now: new Date(),
reg: /"([^"]|\\")*"/,
foo: 'hello',
numfoo: 42,
test: 42,
strtest: '23',
arr: [{ str: 'stark' }, { str: 'lannister' }],
obj: { bool: true },
notmatch: 'valar morghulis',
notmatchobj: { a: true },
notmatchnum: 42
}))

42
node_modules/fast-json-stringify/examples/server.js generated vendored Normal file
View File

@@ -0,0 +1,42 @@
'use strict'
const http = require('http')
const stringify = require('fast-json-stringify')({
type: 'object',
properties: {
hello: {
type: 'string'
},
data: {
type: 'number'
},
nested: {
type: 'object',
properties: {
more: {
type: 'string'
}
}
}
}
})
const server = http.createServer(handle)
function handle (req, res) {
const data = {
hello: 'world',
data: 42,
nested: {
more: 'data'
}
}
if (req.url === '/JSON') {
res.end(JSON.stringify(data))
} else {
res.end(stringify(data))
}
}
server.listen(3000)

1028
node_modules/fast-json-stringify/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

24
node_modules/fast-json-stringify/lib/location.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
'use strict'
class Location {
constructor (schema, schemaId, jsonPointer = '#') {
this.schema = schema
this.schemaId = schemaId
this.jsonPointer = jsonPointer
}
getPropertyLocation (propertyName) {
const propertyLocation = new Location(
this.schema[propertyName],
this.schemaId,
this.jsonPointer + '/' + propertyName
)
return propertyLocation
}
getSchemaRef () {
return this.schemaId + this.jsonPointer
}
}
module.exports = Location

View File

@@ -0,0 +1,9 @@
'use strict'
const { mergeSchemas: _mergeSchemas } = require('@fastify/merge-json-schemas')
function mergeSchemas (schemas) {
return _mergeSchemas(schemas, { onConflict: 'skip' })
}
module.exports = mergeSchemas

1134
node_modules/fast-json-stringify/lib/schema-validator.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

139
node_modules/fast-json-stringify/lib/serializer.js generated vendored Normal file
View File

@@ -0,0 +1,139 @@
'use strict'
// eslint-disable-next-line
const STR_ESCAPE = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]/
module.exports = class Serializer {
constructor (options) {
switch (options && options.rounding) {
case 'floor':
this.parseInteger = Math.floor
break
case 'ceil':
this.parseInteger = Math.ceil
break
case 'round':
this.parseInteger = Math.round
break
case 'trunc':
default:
this.parseInteger = Math.trunc
break
}
this._options = options
}
asInteger (i) {
if (Number.isInteger(i)) {
return '' + i
} else if (typeof i === 'bigint') {
return i.toString()
}
/* eslint no-undef: "off" */
const integer = this.parseInteger(i)
// check if number is Infinity or NaN
// eslint-disable-next-line no-self-compare
if (integer === Infinity || integer === -Infinity || integer !== integer) {
throw new Error(`The value "${i}" cannot be converted to an integer.`)
}
return '' + integer
}
asNumber (i) {
// fast cast to number
const num = Number(i)
// check if number is NaN
// eslint-disable-next-line no-self-compare
if (num !== num) {
throw new Error(`The value "${i}" cannot be converted to a number.`)
} else if (num === Infinity || num === -Infinity) {
return 'null'
} else {
return '' + num
}
}
asBoolean (bool) {
return bool && 'true' || 'false' // eslint-disable-line
}
asDateTime (date) {
if (date === null) return '""'
if (date instanceof Date) {
return '"' + date.toISOString() + '"'
}
if (typeof date === 'string') {
return '"' + date + '"'
}
throw new Error(`The value "${date}" cannot be converted to a date-time.`)
}
asDate (date) {
if (date === null) return '""'
if (date instanceof Date) {
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + '"'
}
if (typeof date === 'string') {
return '"' + date + '"'
}
throw new Error(`The value "${date}" cannot be converted to a date.`)
}
asTime (date) {
if (date === null) return '""'
if (date instanceof Date) {
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + '"'
}
if (typeof date === 'string') {
return '"' + date + '"'
}
throw new Error(`The value "${date}" cannot be converted to a time.`)
}
asString (str) {
const len = str.length
if (len < 42) {
// magically escape strings for json
// relying on their charCodeAt
// everything below 32 needs JSON.stringify()
// every string that contain surrogate needs JSON.stringify()
// 34 and 92 happens all the time, so we
// have a fast case for them
let result = ''
let last = -1
let point = 255
for (let i = 0; i < len; i++) {
point = str.charCodeAt(i)
if (
point === 0x22 || // '"'
point === 0x5c // '\'
) {
last === -1 && (last = 0)
result += str.slice(last, i) + '\\'
last = i
} else if (point < 32 || (point >= 0xD800 && point <= 0xDFFF)) {
// The current character is non-printable characters or a surrogate.
return JSON.stringify(str)
}
}
return (last === -1 && ('"' + str + '"')) || ('"' + result + str.slice(last) + '"')
} else if (len < 5000 && STR_ESCAPE.test(str) === false) {
// Only use the regular expression for shorter input. The overhead is otherwise too much.
return '"' + str + '"'
} else {
return JSON.stringify(str)
}
}
asUnsafeString (str) {
return '"' + str + '"'
}
getState () {
return this._options
}
static restoreFromState (state) {
return new Serializer(state)
}
}

34
node_modules/fast-json-stringify/lib/standalone.js generated vendored Normal file
View File

@@ -0,0 +1,34 @@
'use strict'
function buildStandaloneCode (contextFunc, context, serializer, validator) {
let ajvDependencyCode = ''
if (context.validatorSchemasIds.size > 0) {
ajvDependencyCode += 'const Validator = require(\'fast-json-stringify/lib/validator\')\n'
ajvDependencyCode += `const validatorState = ${JSON.stringify(validator.getState())}\n`
ajvDependencyCode += 'const validator = Validator.restoreFromState(validatorState)\n'
} else {
ajvDependencyCode += 'const validator = null\n'
}
// Don't need to keep external schemas once compiled
// validatorState will hold external schemas if it needs them
const { schema, ...serializerState } = serializer.getState()
return `
'use strict'
const Serializer = require('fast-json-stringify/lib/serializer')
const serializerState = ${JSON.stringify(serializerState)}
const serializer = Serializer.restoreFromState(serializerState)
${ajvDependencyCode}
module.exports = ${contextFunc.toString()}(validator, serializer)`
}
module.exports = buildStandaloneCode
module.exports.dependencies = {
Serializer: require('./serializer'),
Validator: require('./validator')
}

94
node_modules/fast-json-stringify/lib/validator.js generated vendored Normal file
View File

@@ -0,0 +1,94 @@
'use strict'
const Ajv = require('ajv')
const fastUri = require('fast-uri')
const ajvFormats = require('ajv-formats')
const clone = require('rfdc')({ proto: true })
class Validator {
constructor (ajvOptions) {
this.ajv = new Ajv({
...ajvOptions,
strictSchema: false,
validateSchema: false,
allowUnionTypes: true,
uriResolver: fastUri
})
ajvFormats(this.ajv)
this.ajv.addKeyword({
keyword: 'fjs_type',
type: 'object',
errors: false,
validate: (_type, date) => {
return date instanceof Date
}
})
this._ajvSchemas = {}
this._ajvOptions = ajvOptions || {}
}
addSchema (schema, schemaName) {
let schemaKey = schema.$id || schemaName
if (schema.$id !== undefined && schema.$id[0] === '#') {
schemaKey = schemaName + schema.$id // relative URI
}
if (
this.ajv.refs[schemaKey] === undefined &&
this.ajv.schemas[schemaKey] === undefined
) {
const ajvSchema = clone(schema)
this.convertSchemaToAjvFormat(ajvSchema)
this.ajv.addSchema(ajvSchema, schemaKey)
this._ajvSchemas[schemaKey] = schema
}
}
validate (schemaRef, data) {
return this.ajv.validate(schemaRef, data)
}
// Ajv does not support js date format. In order to properly validate objects containing a date,
// it needs to replace all occurrences of the string date format with a custom keyword fjs_type.
// (see https://github.com/fastify/fast-json-stringify/pull/441)
convertSchemaToAjvFormat (schema) {
if (schema === null) return
if (schema.type === 'string') {
schema.fjs_type = 'string'
schema.type = ['string', 'object']
} else if (
Array.isArray(schema.type) &&
schema.type.includes('string') &&
!schema.type.includes('object')
) {
schema.fjs_type = 'string'
schema.type.push('object')
}
for (const property in schema) {
if (typeof schema[property] === 'object') {
this.convertSchemaToAjvFormat(schema[property])
}
}
}
getState () {
return {
ajvOptions: this._ajvOptions,
ajvSchemas: this._ajvSchemas
}
}
static restoreFromState (state) {
const validator = new Validator(state.ajvOptions)
for (const [id, ajvSchema] of Object.entries(state.ajvSchemas)) {
validator.ajv.addSchema(ajvSchema, id)
}
return validator
}
}
module.exports = Validator

92
node_modules/fast-json-stringify/package.json generated vendored Normal file
View File

@@ -0,0 +1,92 @@
{
"name": "fast-json-stringify",
"version": "6.0.1",
"description": "Stringify your JSON at max speed",
"main": "index.js",
"type": "commonjs",
"types": "types/index.d.ts",
"scripts": {
"bench": "node ./benchmark/bench.js",
"bench:cmp": "node ./benchmark/bench-cmp-branch.js",
"bench:cmp:ci": "node ./benchmark/bench-cmp-branch.js --ci",
"benchmark": "node ./benchmark/bench-cmp-lib.js",
"lint": "eslint",
"lint:fix": "eslint --fix",
"test:typescript": "tsd",
"test:unit": "c8 node --test",
"test": "npm run test:unit && npm run test:typescript"
},
"precommit": [
"lint",
"test"
],
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/fast-json-stringify.git"
},
"keywords": [
"json",
"stringify",
"schema",
"fast"
],
"author": "Matteo Collina <hello@matteocollina.com>",
"contributors": [
{
"name": "Tomas Della Vedova",
"url": "http://delved.org"
},
{
"name": "Aras Abbasi",
"email": "aras.abbasi@gmail.com"
},
{
"name": "Manuel Spigolon",
"email": "behemoth89@gmail.com"
},
{
"name": "Frazer Smith",
"email": "frazer.dev@icloud.com",
"url": "https://github.com/fdawgs"
}
],
"license": "MIT",
"bugs": {
"url": "https://github.com/fastify/fast-json-stringify/issues"
},
"homepage": "https://github.com/fastify/fast-json-stringify#readme",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"devDependencies": {
"@fastify/pre-commit": "^2.1.0",
"@sinclair/typebox": "^0.34.3",
"benchmark": "^2.1.4",
"c8": "^10.1.2",
"cli-select": "^1.1.2",
"compile-json-stringify": "^0.1.2",
"eslint": "^9.17.0",
"fast-json-stringify": ".",
"is-my-json-valid": "^2.20.6",
"neostandard": "^0.12.0",
"simple-git": "^3.23.0",
"tsd": "^0.31.0",
"webpack": "^5.90.3"
},
"dependencies": {
"@fastify/merge-json-schemas": "^0.2.0",
"ajv": "^8.12.0",
"ajv-formats": "^3.0.1",
"fast-uri": "^3.0.0",
"json-schema-ref-resolver": "^2.0.0",
"rfdc": "^1.2.0"
},
"runkitExampleFilename": "./examples/example.js"
}

View File

@@ -0,0 +1,332 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('additionalProperties', (t) => {
t.plan(1)
const stringify = build({
title: 'additionalProperties',
type: 'object',
properties: {
str: {
type: 'string'
}
},
additionalProperties: {
type: 'string'
}
})
const obj = { str: 'test', foo: 42, ofoo: true, foof: 'string', objfoo: { a: true } }
t.assert.equal(stringify(obj), '{"str":"test","foo":"42","ofoo":"true","foof":"string","objfoo":"[object Object]"}')
})
test('additionalProperties should not change properties', (t) => {
t.plan(1)
const stringify = build({
title: 'patternProperties should not change properties',
type: 'object',
properties: {
foo: {
type: 'string'
}
},
additionalProperties: {
type: 'number'
}
})
const obj = { foo: '42', ofoo: 42 }
t.assert.equal(stringify(obj), '{"foo":"42","ofoo":42}')
})
test('additionalProperties should not change properties and patternProperties', (t) => {
t.plan(1)
const stringify = build({
title: 'patternProperties should not change properties',
type: 'object',
properties: {
foo: {
type: 'string'
}
},
patternProperties: {
foo: {
type: 'string'
}
},
additionalProperties: {
type: 'number'
}
})
const obj = { foo: '42', ofoo: 42, test: '42' }
t.assert.equal(stringify(obj), '{"foo":"42","ofoo":"42","test":42}')
})
test('additionalProperties set to true, use of fast-safe-stringify', (t) => {
t.plan(1)
const stringify = build({
title: 'check string coerce',
type: 'object',
properties: {},
additionalProperties: true
})
const obj = { foo: true, ofoo: 42, arrfoo: ['array', 'test'], objfoo: { a: 'world' } }
t.assert.equal(stringify(obj), '{"foo":true,"ofoo":42,"arrfoo":["array","test"],"objfoo":{"a":"world"}}')
})
test('additionalProperties - string coerce', (t) => {
t.plan(1)
const stringify = build({
title: 'check string coerce',
type: 'object',
properties: {},
additionalProperties: {
type: 'string'
}
})
const obj = { foo: true, ofoo: 42, arrfoo: ['array', 'test'], objfoo: { a: 'world' } }
t.assert.equal(stringify(obj), '{"foo":"true","ofoo":"42","arrfoo":"array,test","objfoo":"[object Object]"}')
})
test('additionalProperties - number skip', (t) => {
t.plan(1)
const stringify = build({
title: 'check number coerce',
type: 'object',
properties: {},
additionalProperties: {
type: 'number'
}
})
// const obj = { foo: true, ofoo: '42', xfoo: 'string', arrfoo: [1, 2], objfoo: { num: 42 } }
const obj = { foo: true, ofoo: '42' }
t.assert.equal(stringify(obj), '{"foo":1,"ofoo":42}')
})
test('additionalProperties - boolean coerce', (t) => {
t.plan(1)
const stringify = build({
title: 'check boolean coerce',
type: 'object',
properties: {},
additionalProperties: {
type: 'boolean'
}
})
const obj = { foo: 'true', ofoo: 0, arrfoo: [1, 2], objfoo: { a: true } }
t.assert.equal(stringify(obj), '{"foo":true,"ofoo":false,"arrfoo":true,"objfoo":true}')
})
test('additionalProperties - object coerce', (t) => {
t.plan(1)
const stringify = build({
title: 'check object coerce',
type: 'object',
properties: {},
additionalProperties: {
type: 'object',
properties: {
answer: {
type: 'number'
}
}
}
})
const obj = { objfoo: { answer: 42 } }
t.assert.equal(stringify(obj), '{"objfoo":{"answer":42}}')
})
test('additionalProperties - array coerce', (t) => {
t.plan(2)
const stringify = build({
title: 'check array coerce',
type: 'object',
properties: {},
additionalProperties: {
type: 'array',
items: {
type: 'string'
}
}
})
const coercibleValues = { arrfoo: [1, 2] }
t.assert.equal(stringify(coercibleValues), '{"arrfoo":["1","2"]}')
const incoercibleValues = { foo: 'true', ofoo: 0, objfoo: { tyrion: 'lannister' } }
t.assert.throws(() => stringify(incoercibleValues))
})
test('additionalProperties with empty schema', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
additionalProperties: {}
})
const obj = { a: 1, b: true, c: null }
t.assert.equal(stringify(obj), '{"a":1,"b":true,"c":null}')
})
test('additionalProperties with nested empty schema', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
data: { type: 'object', additionalProperties: {} }
},
required: ['data']
})
const obj = { data: { a: 1, b: true, c: null } }
t.assert.equal(stringify(obj), '{"data":{"a":1,"b":true,"c":null}}')
})
test('nested additionalProperties', (t) => {
t.plan(1)
const stringify = build({
title: 'additionalProperties',
type: 'array',
items: {
type: 'object',
properties: {
ap: {
type: 'object',
additionalProperties: { type: 'string' }
}
}
}
})
const obj = [{ ap: { value: 'string' } }]
t.assert.equal(stringify(obj), '[{"ap":{"value":"string"}}]')
})
test('very nested additionalProperties', (t) => {
t.plan(1)
const stringify = build({
title: 'additionalProperties',
type: 'array',
items: {
type: 'object',
properties: {
ap: {
type: 'object',
properties: {
nested: {
type: 'object',
properties: {
moarNested: {
type: 'object',
properties: {
finally: {
type: 'object',
additionalProperties: {
type: 'string'
}
}
}
}
}
}
}
}
}
}
})
const obj = [{ ap: { nested: { moarNested: { finally: { value: 'str' } } } } }]
t.assert.equal(stringify(obj), '[{"ap":{"nested":{"moarNested":{"finally":{"value":"str"}}}}}]')
})
test('nested additionalProperties set to true', (t) => {
t.plan(1)
const stringify = build({
title: 'nested additionalProperties=true',
type: 'object',
properties: {
ap: {
type: 'object',
additionalProperties: true
}
}
})
const obj = { ap: { value: 'string', someNumber: 42 } }
t.assert.equal(stringify(obj), '{"ap":{"value":"string","someNumber":42}}')
})
test('field passed to fastSafeStringify as undefined should be removed', (t) => {
t.plan(1)
const stringify = build({
title: 'nested additionalProperties=true',
type: 'object',
properties: {
ap: {
type: 'object',
additionalProperties: true
}
}
})
const obj = { ap: { value: 'string', someNumber: undefined } }
t.assert.equal(stringify(obj), '{"ap":{"value":"string"}}')
})
test('property without type but with enum, will acts as additionalProperties', (t) => {
t.plan(1)
const stringify = build({
title: 'automatic additionalProperties',
type: 'object',
properties: {
ap: {
enum: ['foobar', 42, ['foo', 'bar'], {}]
}
}
})
const obj = { ap: { additional: 'field' } }
t.assert.equal(stringify(obj), '{"ap":{"additional":"field"}}')
})
test('property without type but with enum, will acts as additionalProperties without overwriting', (t) => {
t.plan(1)
const stringify = build({
title: 'automatic additionalProperties',
type: 'object',
properties: {
ap: {
additionalProperties: false,
enum: ['foobar', 42, ['foo', 'bar'], {}]
}
}
})
const obj = { ap: { additional: 'field' } }
t.assert.equal(stringify(obj), '{"ap":{}}')
})
test('function and symbol references are not serialized as undefined', (t) => {
t.plan(1)
const stringify = build({
title: 'additionalProperties',
type: 'object',
additionalProperties: true,
properties: {
str: {
type: 'string'
}
}
})
const obj = { str: 'x', test: 'test', meth: () => 'x', sym: Symbol('x') }
t.assert.equal(stringify(obj), '{"str":"x","test":"test"}')
})

751
node_modules/fast-json-stringify/test/allof.test.js generated vendored Normal file
View File

@@ -0,0 +1,751 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
process.env.TZ = 'UTC'
test('allOf: combine type and format ', (t) => {
t.plan(1)
const schema = {
allOf: [
{ type: 'string' },
{ format: 'time' }
]
}
const stringify = build(schema)
const date = new Date(1674263005800)
const value = stringify(date)
t.assert.equal(value, '"01:03:25"')
})
test('allOf: combine additional properties ', (t) => {
t.plan(1)
const schema = {
allOf: [
{ type: 'object' },
{
type: 'object',
additionalProperties: { type: 'boolean' }
}
]
}
const stringify = build(schema)
const data = { property: true }
const value = stringify(data)
t.assert.equal(value, JSON.stringify(data))
})
test('allOf: combine pattern properties', (t) => {
t.plan(1)
const schema = {
allOf: [
{ type: 'object' },
{
type: 'object',
patternProperties: {
foo: {
type: 'number'
}
}
}
]
}
const stringify = build(schema)
const data = { foo: 42 }
const value = stringify(data)
t.assert.equal(value, JSON.stringify(data))
})
test('object with allOf and multiple schema on the allOf', (t) => {
t.plan(4)
const schema = {
title: 'object with allOf and multiple schema on the allOf',
type: 'object',
allOf: [
{
type: 'object',
required: [
'name'
],
properties: {
name: {
type: 'string'
},
tag: {
type: 'string'
}
}
},
{
required: [
'id'
],
type: 'object',
properties: {
id: {
type: 'integer'
}
}
}
]
}
const stringify = build(schema)
try {
stringify({
id: 1
})
} catch (e) {
t.assert.equal(e.message, '"name" is required!')
}
try {
stringify({
name: 'string'
})
} catch (e) {
t.assert.equal(e.message, '"id" is required!')
}
t.assert.equal(stringify({
id: 1,
name: 'string'
}), '{"name":"string","id":1}')
t.assert.equal(stringify({
id: 1,
name: 'string',
tag: 'otherString'
}), '{"name":"string","id":1,"tag":"otherString"}')
})
test('object with allOf and one schema on the allOf', (t) => {
t.plan(1)
const schema = {
title: 'object with allOf and one schema on the allOf',
type: 'object',
allOf: [
{
required: [
'id'
],
type: 'object',
properties: {
id: {
type: 'integer'
}
}
}
]
}
const stringify = build(schema)
const value = stringify({
id: 1
})
t.assert.equal(value, '{"id":1}')
})
test('object with allOf and no schema on the allOf', (t) => {
t.plan(1)
const schema = {
title: 'object with allOf and no schema on the allOf',
type: 'object',
allOf: []
}
try {
build(schema)
t.fail()
} catch (e) {
t.assert.equal(e.message, 'schema is invalid: data/allOf must NOT have fewer than 1 items')
}
})
test('object with nested allOfs', (t) => {
t.plan(1)
const schema = {
title: 'object with nested allOfs',
type: 'object',
allOf: [
{
required: [
'id1'
],
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
},
{
allOf: [
{
type: 'object',
properties: {
id2: {
type: 'integer'
}
}
},
{
type: 'object',
properties: {
id3: {
type: 'integer'
}
}
}
]
}
]
}
const stringify = build(schema)
const value = stringify({
id1: 1,
id2: 2,
id3: 3,
id4: 4 // extra prop shouldn't be in result
})
t.assert.equal(value, '{"id1":1,"id2":2,"id3":3}')
})
test('object with anyOf nested inside allOf', (t) => {
t.plan(1)
const schema = {
title: 'object with anyOf nested inside allOf',
type: 'object',
allOf: [
{
required: ['id1', 'obj'],
type: 'object',
properties: {
id1: {
type: 'integer'
},
obj: {
type: 'object',
properties: {
nested: { type: 'string' }
}
}
}
},
{
anyOf: [
{
type: 'object',
properties: {
id2: { type: 'string' }
},
required: ['id2']
},
{
type: 'object',
properties: {
id3: {
type: 'integer'
},
nestedObj: {
type: 'object',
properties: {
nested: { type: 'string' }
}
}
},
required: ['id3']
},
{
type: 'object',
properties: {
id4: { type: 'integer' }
},
required: ['id4']
}
]
}
]
}
const stringify = build(schema)
const value = stringify({
id1: 1,
id3: 3,
id4: 4, // extra prop shouldn't be in result
obj: { nested: 'yes' },
nestedObj: { nested: 'yes' }
})
t.assert.equal(value, '{"id1":1,"obj":{"nested":"yes"},"id3":3,"nestedObj":{"nested":"yes"}}')
})
test('object with $ref in allOf', (t) => {
t.plan(1)
const schema = {
title: 'object with $ref in allOf',
type: 'object',
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
}
},
allOf: [
{
$ref: '#/definitions/id1'
}
]
}
const stringify = build(schema)
const value = stringify({
id1: 1,
id2: 2 // extra prop shouldn't be in result
})
t.assert.equal(value, '{"id1":1}')
})
test('object with $ref and other object in allOf', (t) => {
t.plan(1)
const schema = {
title: 'object with $ref in allOf',
type: 'object',
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
}
},
allOf: [
{
$ref: '#/definitions/id1'
},
{
type: 'object',
properties: {
id2: {
type: 'integer'
}
}
}
]
}
const stringify = build(schema)
const value = stringify({
id1: 1,
id2: 2,
id3: 3 // extra prop shouldn't be in result
})
t.assert.equal(value, '{"id1":1,"id2":2}')
})
test('object with multiple $refs in allOf', (t) => {
t.plan(1)
const schema = {
title: 'object with $ref in allOf',
type: 'object',
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
},
id2: {
type: 'object',
properties: {
id2: {
type: 'integer'
}
}
}
},
allOf: [
{
$ref: '#/definitions/id1'
},
{
$ref: '#/definitions/id2'
}
]
}
const stringify = build(schema)
const value = stringify({
id1: 1,
id2: 2,
id3: 3 // extra prop shouldn't be in result
})
t.assert.equal(value, '{"id1":1,"id2":2}')
})
test('allOf with nested allOf in $ref', (t) => {
t.plan(1)
const schema = {
title: 'allOf with nested allOf in $ref',
type: 'object',
definitions: {
group: {
type: 'object',
allOf: [{
properties: {
id2: {
type: 'integer'
}
}
}, {
properties: {
id3: {
type: 'integer'
}
}
}]
}
},
allOf: [
{
type: 'object',
properties: {
id1: {
type: 'integer'
}
},
required: [
'id1'
]
},
{
$ref: '#/definitions/group'
}
]
}
const stringify = build(schema)
const value = stringify({
id1: 1,
id2: 2,
id3: 3,
id4: 4 // extra prop shouldn't be in result
})
t.assert.equal(value, '{"id1":1,"id2":2,"id3":3}')
})
test('object with external $refs in allOf', (t) => {
t.plan(1)
const externalSchema = {
first: {
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
}
}
},
second: {
definitions: {
id2: {
$id: '#id2',
type: 'object',
properties: {
id2: {
type: 'integer'
}
}
}
}
}
}
const schema = {
title: 'object with $ref in allOf',
type: 'object',
allOf: [
{
$ref: 'first#/definitions/id1'
},
{
$ref: 'second#/definitions/id2'
}
]
}
const stringify = build(schema, { schema: externalSchema })
const value = stringify({
id1: 1,
id2: 2,
id3: 3 // extra prop shouldn't be in result
})
t.assert.equal(value, '{"id1":1,"id2":2}')
})
test('allof with local anchor reference', (t) => {
t.plan(1)
const externalSchemas = {
Test: {
$id: 'Test',
definitions: {
Problem: {
type: 'object',
properties: {
type: {
type: 'string'
}
}
},
ValidationFragment: {
type: 'string'
},
ValidationErrorProblem: {
type: 'object',
allOf: [
{
$ref: '#/definitions/Problem'
},
{
type: 'object',
properties: {
validation: {
$ref: '#/definitions/ValidationFragment'
}
}
}
]
}
}
}
}
const schema = { $ref: 'Test#/definitions/ValidationErrorProblem' }
const stringify = build(schema, { schema: externalSchemas })
const data = { type: 'foo', validation: 'bar' }
t.assert.equal(stringify(data), JSON.stringify(data))
})
test('allOf: multiple nested $ref properties', (t) => {
t.plan(2)
const externalSchema1 = {
$id: 'externalSchema1',
oneOf: [
{ $ref: '#/definitions/id1' }
],
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
},
additionalProperties: false
}
}
}
const externalSchema2 = {
$id: 'externalSchema2',
oneOf: [
{ $ref: '#/definitions/id2' }
],
definitions: {
id2: {
type: 'object',
properties: {
id2: {
type: 'integer'
}
},
additionalProperties: false
}
}
}
const schema = {
anyOf: [
{ $ref: 'externalSchema1' },
{ $ref: 'externalSchema2' }
]
}
const stringify = build(schema, { schema: [externalSchema1, externalSchema2] })
t.assert.equal(stringify({ id1: 1 }), JSON.stringify({ id1: 1 }))
t.assert.equal(stringify({ id2: 2 }), JSON.stringify({ id2: 2 }))
})
test('allOf: throw Error if types mismatch ', (t) => {
t.plan(1)
const schema = {
allOf: [
{ type: 'string' },
{ type: 'number' }
]
}
t.assert.throws(() => {
build(schema)
}, {
message: 'Failed to merge "type" keyword schemas.',
schemas: [['string'], ['number']]
})
})
test('allOf: throw Error if format mismatch ', (t) => {
t.plan(1)
const schema = {
allOf: [
{ format: 'date' },
{ format: 'time' }
]
}
t.assert.throws(() => {
build(schema)
}, {
message: 'Failed to merge "format" keyword schemas.'
// schemas: ['date', 'time']
})
})
test('recursive nested allOfs', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
foo: {
additionalProperties: false,
allOf: [{ $ref: '#' }]
}
}
}
const data = { foo: {} }
const stringify = build(schema)
t.assert.equal(stringify(data), JSON.stringify(data))
})
test('recursive nested allOfs', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
foo: {
additionalProperties: false,
allOf: [{ allOf: [{ $ref: '#' }] }]
}
}
}
const data = { foo: {} }
const stringify = build(schema)
t.assert.equal(stringify(data), JSON.stringify(data))
})
test('external recursive allOfs', (t) => {
t.plan(1)
const externalSchema = {
type: 'object',
properties: {
foo: {
properties: {
bar: { type: 'string' }
},
allOf: [{ $ref: '#' }]
}
}
}
const schema = {
type: 'object',
properties: {
a: { $ref: 'externalSchema#/properties/foo' },
b: { $ref: 'externalSchema#/properties/foo' }
}
}
const data = {
a: {
foo: {},
bar: '42',
baz: 42
},
b: {
foo: {},
bar: '42',
baz: 42
}
}
const stringify = build(schema, { schema: { externalSchema } })
t.assert.equal(stringify(data), '{"a":{"bar":"42","foo":{}},"b":{"bar":"42","foo":{}}}')
})
test('do not crash with $ref prop', (t) => {
t.plan(1)
const schema = {
title: 'object with $ref',
type: 'object',
properties: {
outside: {
$ref: '#/$defs/outside'
}
},
$defs: {
inside: {
type: 'object',
properties: {
$ref: {
type: 'string'
}
}
},
outside: {
allOf: [{
$ref: '#/$defs/inside'
}]
}
}
}
const stringify = build(schema)
const value = stringify({
outside: {
$ref: 'true'
}
})
t.assert.equal(value, '{"outside":{"$ref":"true"}}')
})

231
node_modules/fast-json-stringify/test/any.test.js generated vendored Normal file
View File

@@ -0,0 +1,231 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('object with nested random property', (t) => {
t.plan(4)
const schema = {
title: 'empty schema to allow any object',
type: 'object',
properties: {
id: { type: 'number' },
name: {}
}
}
const stringify = build(schema)
t.assert.equal(stringify({
id: 1, name: 'string'
}), '{"id":1,"name":"string"}')
t.assert.equal(stringify({
id: 1, name: { first: 'name', last: 'last' }
}), '{"id":1,"name":{"first":"name","last":"last"}}')
t.assert.equal(stringify({
id: 1, name: null
}), '{"id":1,"name":null}')
t.assert.equal(stringify({
id: 1, name: ['first', 'last']
}), '{"id":1,"name":["first","last"]}')
})
// reference: https://github.com/fastify/fast-json-stringify/issues/259
test('object with empty schema with $id: undefined set', (t) => {
t.plan(1)
const schema = {
title: 'empty schema to allow any object with $id: undefined set',
type: 'object',
properties: {
name: { $id: undefined }
}
}
const stringify = build(schema)
t.assert.equal(stringify({
name: 'string'
}), '{"name":"string"}')
})
test('array with random items', (t) => {
t.plan(1)
const schema = {
title: 'empty schema to allow any object',
type: 'array',
items: {}
}
const stringify = build(schema)
const value = stringify([1, 'string', null])
t.assert.equal(value, '[1,"string",null]')
})
test('empty schema', (t) => {
t.plan(7)
const schema = { }
const stringify = build(schema)
t.assert.equal(stringify(null), 'null')
t.assert.equal(stringify(1), '1')
t.assert.equal(stringify(true), 'true')
t.assert.equal(stringify('hello'), '"hello"')
t.assert.equal(stringify({}), '{}')
t.assert.equal(stringify({ x: 10 }), '{"x":10}')
t.assert.equal(stringify([true, 1, 'hello']), '[true,1,"hello"]')
})
test('empty schema on nested object', (t) => {
t.plan(7)
const schema = {
type: 'object',
properties: {
x: {}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ x: null }), '{"x":null}')
t.assert.equal(stringify({ x: 1 }), '{"x":1}')
t.assert.equal(stringify({ x: true }), '{"x":true}')
t.assert.equal(stringify({ x: 'hello' }), '{"x":"hello"}')
t.assert.equal(stringify({ x: {} }), '{"x":{}}')
t.assert.equal(stringify({ x: { x: 10 } }), '{"x":{"x":10}}')
t.assert.equal(stringify({ x: [true, 1, 'hello'] }), '{"x":[true,1,"hello"]}')
})
test('empty schema on array', (t) => {
t.plan(1)
const schema = {
type: 'array',
items: {}
}
const stringify = build(schema)
t.assert.equal(stringify([1, true, 'hello', [], { x: 1 }]), '[1,true,"hello",[],{"x":1}]')
})
test('empty schema on anyOf', (t) => {
t.plan(4)
// any on Foo codepath.
const schema = {
anyOf: [
{
type: 'object',
properties: {
kind: {
type: 'string',
enum: ['Foo']
},
value: {}
}
},
{
type: 'object',
properties: {
kind: {
type: 'string',
enum: ['Bar']
},
value: {
type: 'number'
}
}
}
]
}
const stringify = build(schema)
t.assert.equal(stringify({ kind: 'Bar', value: 1 }), '{"kind":"Bar","value":1}')
t.assert.equal(stringify({ kind: 'Foo', value: 1 }), '{"kind":"Foo","value":1}')
t.assert.equal(stringify({ kind: 'Foo', value: true }), '{"kind":"Foo","value":true}')
t.assert.equal(stringify({ kind: 'Foo', value: 'hello' }), '{"kind":"Foo","value":"hello"}')
})
test('should throw a TypeError with the path to the key of the invalid value /1', (t) => {
t.plan(1)
// any on Foo codepath.
const schema = {
anyOf: [
{
type: 'object',
properties: {
kind: {
type: 'string',
enum: ['Foo']
},
value: {}
}
},
{
type: 'object',
properties: {
kind: {
type: 'string',
enum: ['Bar']
},
value: {
type: 'number'
}
}
}
]
}
const stringify = build(schema)
t.assert.throws(() => stringify({ kind: 'Baz', value: 1 }), new TypeError('The value of \'#\' does not match schema definition.'))
})
test('should throw a TypeError with the path to the key of the invalid value /2', (t) => {
t.plan(1)
// any on Foo codepath.
const schema = {
type: 'object',
properties: {
data: {
anyOf: [
{
type: 'object',
properties: {
kind: {
type: 'string',
enum: ['Foo']
},
value: {}
}
},
{
type: 'object',
properties: {
kind: {
type: 'string',
enum: ['Bar']
},
value: {
type: 'number'
}
}
}
]
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ data: { kind: 'Baz', value: 1 } }), new TypeError('The value of \'#/properties/data\' does not match schema definition.'))
})

792
node_modules/fast-json-stringify/test/anyof.test.js generated vendored Normal file
View File

@@ -0,0 +1,792 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
process.env.TZ = 'UTC'
test('object with multiple types field', (t) => {
t.plan(2)
const schema = {
title: 'object with multiple types field',
type: 'object',
properties: {
str: {
anyOf: [{
type: 'string'
}, {
type: 'boolean'
}]
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({
str: 'string'
}), '{"str":"string"}')
t.assert.equal(stringify({
str: true
}), '{"str":true}')
})
test('object with field of type object or null', (t) => {
t.plan(2)
const schema = {
title: 'object with field of type object or null',
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'object',
properties: {
str: {
type: 'string'
}
}
}, {
type: 'null'
}]
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({
prop: null
}), '{"prop":null}')
t.assert.equal(stringify({
prop: {
str: 'string'
}
}), '{"prop":{"str":"string"}}')
})
test('object with field of type object or array', (t) => {
t.plan(2)
const schema = {
title: 'object with field of type object or array',
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'object',
properties: {},
additionalProperties: true
}, {
type: 'array',
items: {
type: 'string'
}
}]
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({
prop: {
str: 'string'
}
}), '{"prop":{"str":"string"}}')
t.assert.equal(stringify({
prop: ['string']
}), '{"prop":["string"]}')
})
test('object with field of type string and coercion disable ', (t) => {
t.plan(1)
const schema = {
title: 'object with field of type string',
type: 'object',
properties: {
str: {
anyOf: [{
type: 'string'
}]
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ str: 1 }))
})
test('object with field of type string and coercion enable ', (t) => {
t.plan(1)
const schema = {
title: 'object with field of type string',
type: 'object',
properties: {
str: {
anyOf: [{
type: 'string'
}]
}
}
}
const options = {
ajv: {
coerceTypes: true
}
}
const stringify = build(schema, options)
const value = stringify({
str: 1
})
t.assert.equal(value, '{"str":"1"}')
})
test('object with field with type union of multiple objects', (t) => {
t.plan(2)
const schema = {
title: 'object with anyOf property value containing objects',
type: 'object',
properties: {
anyOfSchema: {
anyOf: [
{
type: 'object',
properties: {
baz: { type: 'number' }
},
required: ['baz']
},
{
type: 'object',
properties: {
bar: { type: 'string' }
},
required: ['bar']
}
]
}
},
required: ['anyOfSchema']
}
const stringify = build(schema)
t.assert.equal(stringify({ anyOfSchema: { baz: 5 } }), '{"anyOfSchema":{"baz":5}}')
t.assert.equal(stringify({ anyOfSchema: { bar: 'foo' } }), '{"anyOfSchema":{"bar":"foo"}}')
})
test('null value in schema', (t) => {
t.plan(0)
const schema = {
title: 'schema with null child',
type: 'string',
nullable: true,
enum: [null]
}
build(schema)
})
test('symbol value in schema', (t) => {
t.plan(4)
const ObjectKind = Symbol('LiteralKind')
const UnionKind = Symbol('UnionKind')
const LiteralKind = Symbol('LiteralKind')
const schema = {
kind: ObjectKind,
type: 'object',
properties: {
value: {
kind: UnionKind,
anyOf: [
{ kind: LiteralKind, type: 'string', enum: ['foo'] },
{ kind: LiteralKind, type: 'string', enum: ['bar'] },
{ kind: LiteralKind, type: 'string', enum: ['baz'] }
]
}
},
required: ['value']
}
const stringify = build(schema)
t.assert.equal(stringify({ value: 'foo' }), '{"value":"foo"}')
t.assert.equal(stringify({ value: 'bar' }), '{"value":"bar"}')
t.assert.equal(stringify({ value: 'baz' }), '{"value":"baz"}')
t.assert.throws(() => stringify({ value: 'qux' }))
})
test('anyOf and $ref together', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
cs: {
anyOf: [
{
$ref: '#/definitions/Option'
},
{
type: 'boolean'
}
]
}
},
definitions: {
Option: {
type: 'string'
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ cs: 'franco' }), '{"cs":"franco"}')
t.assert.equal(stringify({ cs: true }), '{"cs":true}')
})
test('anyOf and $ref: 2 levels are fine', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
cs: {
anyOf: [
{
$ref: '#/definitions/Option'
},
{
type: 'boolean'
}
]
}
},
definitions: {
Option: {
anyOf: [
{
type: 'number'
},
{
type: 'boolean'
}
]
}
}
}
const stringify = build(schema)
const value = stringify({ cs: 3 })
t.assert.equal(value, '{"cs":3}')
})
test('anyOf and $ref: multiple levels should throw at build.', (t) => {
t.plan(3)
const schema = {
type: 'object',
properties: {
cs: {
anyOf: [
{
$ref: '#/definitions/Option'
},
{
type: 'boolean'
}
]
}
},
definitions: {
Option: {
anyOf: [
{
$ref: '#/definitions/Option2'
},
{
type: 'string'
}
]
},
Option2: {
type: 'number'
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ cs: 3 }), '{"cs":3}')
t.assert.equal(stringify({ cs: true }), '{"cs":true}')
t.assert.equal(stringify({ cs: 'pippo' }), '{"cs":"pippo"}')
})
test('anyOf and $ref - multiple external $ref', (t) => {
t.plan(2)
const externalSchema = {
external: {
definitions: {
def: {
type: 'object',
properties: {
prop: { anyOf: [{ $ref: 'external2#/definitions/other' }] }
}
}
}
},
external2: {
definitions: {
internal: {
type: 'string'
},
other: {
type: 'object',
properties: {
prop2: { $ref: '#/definitions/internal' }
}
}
}
}
}
const schema = {
title: 'object with $ref',
type: 'object',
properties: {
obj: {
$ref: 'external#/definitions/def'
}
}
}
const object = {
obj: {
prop: {
prop2: 'test'
}
}
}
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
t.assert.doesNotThrow(() => JSON.parse(output))
t.assert.equal(output, '{"obj":{"prop":{"prop2":"test"}}}')
})
test('anyOf looks for all of the array items', (t) => {
t.plan(1)
const schema = {
title: 'type array that may have any of declared items',
type: 'array',
items: {
anyOf: [
{
type: 'object',
properties: {
savedId: {
type: 'string'
}
},
required: ['savedId']
},
{
type: 'object',
properties: {
error: {
type: 'string'
}
},
required: ['error']
}
]
}
}
const stringify = build(schema)
const value = stringify([{ savedId: 'great' }, { error: 'oops' }])
t.assert.equal(value, '[{"savedId":"great"},{"error":"oops"}]')
})
test('anyOf with enum with more than 100 entries', (t) => {
t.plan(1)
const schema = {
title: 'type array that may have any of declared items',
type: 'array',
items: {
anyOf: [
{
type: 'string',
enum: ['EUR', 'USD', ...(new Set([...new Array(200)].map(() => Math.random().toString(36).substr(2, 3)))).values()]
},
{ type: 'null' }
]
}
}
const stringify = build(schema)
const value = stringify(['EUR', 'USD', null])
t.assert.equal(value, '["EUR","USD",null]')
})
test('anyOf object with field date-time of type string with format or null', (t) => {
t.plan(1)
const toStringify = new Date()
const withOneOfSchema = {
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'string',
format: 'date-time'
}, {
type: 'null'
}]
}
}
}
const withOneOfStringify = build(withOneOfSchema)
t.assert.equal(withOneOfStringify({
prop: toStringify
}), `{"prop":"${toStringify.toISOString()}"}`)
})
test('anyOf object with nested field date-time of type string with format or null', (t) => {
t.plan(1)
const withOneOfSchema = {
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'object',
properties: {
nestedProp: {
type: 'string',
format: 'date-time'
}
}
}]
}
}
}
const withOneOfStringify = build(withOneOfSchema)
const data = {
prop: { nestedProp: new Date() }
}
t.assert.equal(withOneOfStringify(data), JSON.stringify(data))
})
test('anyOf object with nested field date of type string with format or null', (t) => {
t.plan(1)
const withOneOfSchema = {
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'object',
properties: {
nestedProp: {
type: 'string',
format: 'date'
}
}
}]
}
}
}
const withOneOfStringify = build(withOneOfSchema)
const data = {
prop: { nestedProp: new Date(1674263005800) }
}
t.assert.equal(withOneOfStringify(data), '{"prop":{"nestedProp":"2023-01-21"}}')
})
test('anyOf object with nested field time of type string with format or null', (t) => {
t.plan(1)
const withOneOfSchema = {
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'object',
properties: {
nestedProp: {
type: 'string',
format: 'time'
}
}
}]
}
}
}
const withOneOfStringify = build(withOneOfSchema)
const data = {
prop: { nestedProp: new Date(1674263005800) }
}
t.assert.equal(withOneOfStringify(data), '{"prop":{"nestedProp":"01:03:25"}}')
})
test('anyOf object with field date of type string with format or null', (t) => {
t.plan(1)
const toStringify = '2011-01-01'
const withOneOfSchema = {
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'string',
format: 'date'
}, {
type: 'null'
}]
}
}
}
const withOneOfStringify = build(withOneOfSchema)
t.assert.equal(withOneOfStringify({
prop: toStringify
}), '{"prop":"2011-01-01"}')
})
test('anyOf object with invalid field date of type string with format or null', (t) => {
t.plan(1)
const toStringify = 'foo bar'
const withOneOfSchema = {
type: 'object',
properties: {
prop: {
anyOf: [{
type: 'string',
format: 'date'
}, {
type: 'null'
}]
}
}
}
const withOneOfStringify = build(withOneOfSchema)
t.assert.throws(() => withOneOfStringify({ prop: toStringify }))
})
test('anyOf with a nested external schema', (t) => {
t.plan(1)
const externalSchemas = {
schema1: {
definitions: {
def1: {
$id: 'external',
type: 'string'
}
},
type: 'number'
}
}
const schema = { anyOf: [{ $ref: 'external' }] }
const stringify = build(schema, { schema: externalSchemas })
t.assert.equal(stringify('foo'), '"foo"')
})
test('object with ref and validated properties', (t) => {
t.plan(1)
const externalSchemas = {
RefSchema: {
$id: 'RefSchema',
type: 'string'
}
}
const schema = {
$id: 'root',
type: 'object',
properties: {
id: {
anyOf: [
{ type: 'string' },
{ type: 'number' }
]
},
reference: { $ref: 'RefSchema' }
}
}
const stringify = build(schema, { schema: externalSchemas })
t.assert.equal(stringify({ id: 1, reference: 'hi' }), '{"id":1,"reference":"hi"}')
})
test('anyOf required props', (t) => {
t.plan(3)
const schema = {
type: 'object',
properties: {
prop1: { type: 'string' },
prop2: { type: 'string' },
prop3: { type: 'string' }
},
required: ['prop1'],
anyOf: [{ required: ['prop2'] }, { required: ['prop3'] }]
}
const stringify = build(schema)
t.assert.equal(stringify({ prop1: 'test', prop2: 'test2' }), '{"prop1":"test","prop2":"test2"}')
t.assert.equal(stringify({ prop1: 'test', prop3: 'test3' }), '{"prop1":"test","prop3":"test3"}')
t.assert.equal(stringify({ prop1: 'test', prop2: 'test2', prop3: 'test3' }), '{"prop1":"test","prop2":"test2","prop3":"test3"}')
})
test('anyOf required props', (t) => {
t.plan(3)
const schema = {
type: 'object',
properties: {
prop1: { type: 'string' }
},
anyOf: [
{
properties: {
prop2: { type: 'string' }
}
},
{
properties: {
prop3: { type: 'string' }
}
}
]
}
const stringify = build(schema)
t.assert.equal(stringify({ prop1: 'test1' }), '{"prop1":"test1"}')
t.assert.equal(stringify({ prop2: 'test2' }), '{"prop2":"test2"}')
t.assert.equal(stringify({ prop1: 'test1', prop2: 'test2' }), '{"prop1":"test1","prop2":"test2"}')
})
test('recursive nested anyOfs', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
foo: {
additionalProperties: false,
anyOf: [{ $ref: '#' }]
}
}
}
const data = { foo: {} }
const stringify = build(schema)
t.assert.equal(stringify(data), JSON.stringify(data))
})
test('recursive nested anyOfs', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
foo: {
additionalProperties: false,
anyOf: [{ anyOf: [{ $ref: '#' }] }]
}
}
}
const data = { foo: {} }
const stringify = build(schema)
t.assert.equal(stringify(data), JSON.stringify(data))
})
test('external recursive anyOfs', (t) => {
t.plan(1)
const externalSchema = {
type: 'object',
properties: {
foo: {
properties: {
bar: { type: 'string' }
},
anyOf: [{ $ref: '#' }]
}
}
}
const schema = {
type: 'object',
properties: {
a: { $ref: 'externalSchema#/properties/foo' },
b: { $ref: 'externalSchema#/properties/foo' }
}
}
const data = {
a: {
foo: {},
bar: '42',
baz: 42
},
b: {
foo: {},
bar: '42',
baz: 42
}
}
const stringify = build(schema, { schema: { externalSchema } })
t.assert.equal(stringify(data), '{"a":{"bar":"42","foo":{}},"b":{"bar":"42","foo":{}}}')
})
test('should build merged schemas twice', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
enums: {
type: 'string',
anyOf: [
{ type: 'string', const: 'FOO' },
{ type: 'string', const: 'BAR' }
]
}
}
}
{
const stringify = build(schema)
t.assert.equal(stringify({ enums: 'FOO' }), '{"enums":"FOO"}')
}
{
const stringify = build(schema)
t.assert.equal(stringify({ enums: 'BAR' }), '{"enums":"BAR"}')
}
})

638
node_modules/fast-json-stringify/test/array.test.js generated vendored Normal file
View File

@@ -0,0 +1,638 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
const Ajv = require('ajv')
test('error on invalid largeArrayMechanism', (t) => {
t.plan(1)
t.assert.throws(() => build({
title: 'large array of null values with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'null' }
}
}
}, {
largeArraySize: 2e4,
largeArrayMechanism: 'invalid'
}), Error('Unsupported large array mechanism invalid'))
})
function buildTest (schema, toStringify, options) {
test(`render a ${schema.title} as JSON`, (t) => {
t.plan(3)
const validate = validator(schema)
const stringify = build(schema, options)
const output = stringify(toStringify)
t.assert.deepStrictEqual(JSON.parse(output), JSON.parse(JSON.stringify(toStringify)))
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
}
buildTest({
title: 'dates tuple',
type: 'object',
properties: {
dates: {
type: 'array',
minItems: 2,
maxItems: 2,
items: [
{
type: 'string',
format: 'date-time'
},
{
type: 'string',
format: 'date-time'
}
]
}
}
}, {
dates: [new Date(1), new Date(2)]
})
buildTest({
title: 'string array',
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'string'
}
}
}
}, {
ids: ['test']
})
buildTest({
title: 'number array',
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'number'
}
}
}
}, {
ids: [1]
})
buildTest({
title: 'mixed array',
type: 'object',
properties: {
ids: {
type: 'array',
items: [
{
type: 'null'
},
{
type: 'string'
},
{
type: 'integer'
},
{
type: 'number'
},
{
type: 'boolean'
},
{
type: 'object',
properties: {
a: {
type: 'string'
}
}
},
{
type: 'array',
items: {
type: 'string'
}
}
]
}
}
}, {
ids: [null, 'test', 1, 1.1, true, { a: 'test' }, ['test']]
})
buildTest({
title: 'repeated types',
type: 'object',
properties: {
ids: {
type: 'array',
items: [
{
type: 'number'
},
{
type: 'number'
}
]
}
}
}, { ids: [1, 2] })
buildTest({
title: 'pattern properties array',
type: 'object',
properties: {
args: {
type: 'array',
items: [
{
type: 'object',
patternProperties: {
'.*': {
type: 'string'
}
}
},
{
type: 'object',
patternProperties: {
'.*': {
type: 'number'
}
}
}
]
}
}
}, { args: [{ a: 'test' }, { b: 1 }] })
buildTest({
title: 'array with weird key',
type: 'object',
properties: {
'@data': {
type: 'array',
items: {
type: 'string'
}
}
}
}, {
'@data': ['test']
})
test('invalid items throw', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
args: {
type: 'array',
items: [
{
type: 'object',
patternProperties: {
'.*': {
type: 'string'
}
}
}
]
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ args: ['invalid'] }))
})
buildTest({
title: 'item types in array default to any',
type: 'object',
properties: {
foo: {
type: 'array'
}
}
}, {
foo: [1, 'string', {}, null]
})
test('array items is a list of schema and additionalItems is true, just the described item is validated', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
foo: {
type: 'array',
items: [
{
type: 'string'
}
],
additionalItems: true
}
}
}
const stringify = build(schema)
const result = stringify({
foo: [
'foo',
'bar',
1
]
})
t.assert.equal(result, '{"foo":["foo","bar",1]}')
})
test('array items is a list of schema and additionalItems is true, just the described item is validated', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
foo: {
type: 'array',
items: [
{
type: 'string'
},
{
type: 'number'
}
],
additionalItems: true
}
}
}
const stringify = build(schema)
const result = stringify({
foo: ['foo']
})
t.assert.equal(result, '{"foo":["foo"]}')
})
test('array items is a list of schema and additionalItems is false /1', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
foo: {
type: 'array',
items: [
{ type: 'string' }
],
additionalItems: false
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ foo: ['foo', 'bar'] }), new Error('Item at 1 does not match schema definition.'))
})
test('array items is a list of schema and additionalItems is false /2', (t) => {
t.plan(3)
const schema = {
type: 'object',
properties: {
foo: {
type: 'array',
items: [
{ type: 'string' },
{ type: 'string' }
],
additionalItems: false
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ foo: [1, 'bar'] }), new Error('Item at 0 does not match schema definition.'))
t.assert.throws(() => stringify({ foo: ['foo', 1] }), new Error('Item at 1 does not match schema definition.'))
t.assert.throws(() => stringify({ foo: ['foo', 'bar', 'baz'] }), new Error('Item at 2 does not match schema definition.'))
})
test('array items is a schema and additionalItems is false', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: {
type: 'array',
items: { type: 'string' },
additionalItems: false
}
}
}
const stringify = build(schema)
// ajv ignores additionalItems if items is not an Array
const ajv = new Ajv({ allErrors: true, strict: false })
const validate = ajv.compile(schema)
t.assert.equal(stringify({ foo: ['foo', 'bar'] }), '{"foo":["foo","bar"]}')
t.assert.equal(validate({ foo: ['foo', 'bar'] }), true)
})
// https://github.com/fastify/fast-json-stringify/issues/279
test('object array with anyOf and symbol', (t) => {
t.plan(1)
const ArrayKind = Symbol('ArrayKind')
const ObjectKind = Symbol('LiteralKind')
const UnionKind = Symbol('UnionKind')
const LiteralKind = Symbol('LiteralKind')
const StringKind = Symbol('StringKind')
const schema = {
kind: ArrayKind,
type: 'array',
items: {
kind: ObjectKind,
type: 'object',
properties: {
name: {
kind: StringKind,
type: 'string'
},
option: {
kind: UnionKind,
anyOf: [
{
kind: LiteralKind,
type: 'string',
enum: ['Foo']
},
{
kind: LiteralKind,
type: 'string',
enum: ['Bar']
}
]
}
},
required: ['name', 'option']
}
}
const stringify = build(schema)
const value = stringify([
{ name: 'name-0', option: 'Foo' },
{ name: 'name-1', option: 'Bar' }
])
t.assert.equal(value, '[{"name":"name-0","option":"Foo"},{"name":"name-1","option":"Bar"}]')
})
test('different arrays with same item schemas', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
array1: {
type: 'array',
items: [{ type: 'string' }],
additionalItems: false
},
array2: {
type: 'array',
items: { $ref: '#/properties/array1/items' },
additionalItems: true
}
}
}
const stringify = build(schema)
const data = { array1: ['bar'], array2: ['foo', 'bar'] }
t.assert.equal(stringify(data), '{"array1":["bar"],"array2":["foo","bar"]}')
})
const largeArray = new Array(2e4).fill({ a: 'test', b: 1 })
buildTest({
title: 'large array with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'object',
properties: {
a: { type: 'string' },
b: { type: 'number' }
}
}
}
}
}, {
ids: largeArray
}, {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
})
buildTest({
title: 'large array of objects with json-stringify mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'object',
properties: {
a: { type: 'string' },
b: { type: 'number' }
}
}
}
}
}, {
ids: largeArray
}, {
largeArrayMechanism: 'json-stringify'
})
buildTest({
title: 'large array of strings with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'string' }
}
}
}, {
ids: new Array(2e4).fill('string')
}, {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
})
buildTest({
title: 'large array of numbers with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'number' }
}
}
}, {
ids: new Array(2e4).fill(42)
}, {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
})
buildTest({
title: 'large array of integers with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'integer' }
}
}
}, {
ids: new Array(2e4).fill(42)
}, {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
})
buildTest({
title: 'large array of booleans with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'boolean' }
}
}
}, {
ids: new Array(2e4).fill(true)
}, {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
})
buildTest({
title: 'large array of null values with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'null' }
}
}
}, {
ids: new Array(2e4).fill(null)
}, {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
})
test('error on invalid value for largeArraySize /1', (t) => {
t.plan(1)
t.assert.throws(() => build({
title: 'large array of null values with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'null' }
}
}
}, {
largeArraySize: 'invalid'
}), Error('Unsupported large array size. Expected integer-like, got string with value invalid'))
})
test('error on invalid value for largeArraySize /2', (t) => {
t.plan(1)
t.assert.throws(() => build({
title: 'large array of null values with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'null' }
}
}
}, {
largeArraySize: Infinity
}), Error('Unsupported large array size. Expected integer-like, got number with value Infinity'))
})
test('error on invalid value for largeArraySize /3', (t) => {
t.plan(1)
t.assert.throws(() => build({
title: 'large array of null values with default mechanism',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'null' }
}
}
}, {
largeArraySize: [200]
}), Error('Unsupported large array size. Expected integer-like, got object with value 200'))
})
buildTest({
title: 'large array of integers with largeArraySize is bigint',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'integer' }
}
}
}, {
ids: new Array(2e4).fill(42)
}, {
largeArraySize: 20000n,
largeArrayMechanism: 'default'
})
buildTest({
title: 'large array of integers with largeArraySize is valid string',
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'integer' }
}
}
}, {
ids: new Array(1e4).fill(42)
}, {
largeArraySize: '10000',
largeArrayMechanism: 'default'
})

13
node_modules/fast-json-stringify/test/asNumber.test.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
'use strict'
const { test } = require('node:test')
test('asNumber should convert BigInt', (t) => {
t.plan(1)
const Serializer = require('../lib/serializer')
const serializer = new Serializer()
const number = serializer.asNumber(11753021440n)
t.assert.equal(number, '11753021440')
})

400
node_modules/fast-json-stringify/test/basic.test.js generated vendored Normal file
View File

@@ -0,0 +1,400 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
function buildTest (schema, toStringify) {
test(`render a ${schema.title} as JSON`, (t) => {
t.plan(3)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.deepStrictEqual(JSON.parse(output), toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
}
buildTest({
title: 'string',
type: 'string',
format: 'unsafe'
}, 'hello world')
buildTest({
title: 'basic',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
magic: {
type: 'number'
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
magic: 42.42
})
buildTest({
title: 'string',
type: 'string'
}, 'hello world')
buildTest({
title: 'string',
type: 'string'
}, 'hello\nworld')
buildTest({
title: 'string with quotes',
type: 'string'
}, 'hello """" world')
buildTest({
title: 'boolean true',
type: 'boolean'
}, true)
buildTest({
title: 'boolean false',
type: 'boolean'
}, false)
buildTest({
title: 'an integer',
type: 'integer'
}, 42)
buildTest({
title: 'a number',
type: 'number'
}, 42.42)
buildTest({
title: 'deep',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
more: {
description: 'more properties',
type: 'object',
properties: {
something: {
type: 'string'
}
}
}
}
}, {
firstName: 'Matteo',
lastName: 'Collina',
more: {
something: 'else'
}
})
buildTest({
title: 'null',
type: 'null'
}, null)
buildTest({
title: 'deep object with weird keys',
type: 'object',
properties: {
'@version': {
type: 'integer'
}
}
}, {
'@version': 1
})
buildTest({
title: 'deep object with weird keys of type object',
type: 'object',
properties: {
'@data': {
type: 'object',
properties: {
id: {
type: 'string'
}
}
}
}
}, {
'@data': {
id: 'string'
}
})
buildTest({
title: 'deep object with spaces in key',
type: 'object',
properties: {
'spaces in key': {
type: 'object',
properties: {
something: {
type: 'integer'
}
}
}
}
}, {
'spaces in key': {
something: 1
}
})
buildTest({
title: 'with null',
type: 'object',
properties: {
firstName: {
type: 'null'
}
}
}, {
firstName: null
})
buildTest({
title: 'array with objects',
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}, [{
name: 'Matteo'
}, {
name: 'Dave'
}])
buildTest({
title: 'array with strings',
type: 'array',
items: {
type: 'string'
}
}, [
'Matteo',
'Dave'
])
buildTest({
title: 'array with numbers',
type: 'array',
items: {
type: 'number'
}
}, [
42.42,
24
])
buildTest({
title: 'array with integers',
type: 'array',
items: {
type: 'number'
}
}, [
42,
24
])
buildTest({
title: 'nested array with objects',
type: 'object',
properties: {
data: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}
}
}, {
data: [{
name: 'Matteo'
}, {
name: 'Dave'
}]
})
buildTest({
title: 'object with boolean',
type: 'object',
properties: {
readonly: {
type: 'boolean'
}
}
}, {
readonly: true
})
test('throw an error or coerce numbers and integers that are not numbers', (t) => {
const stringify = build({
title: 'basic',
type: 'object',
properties: {
age: {
type: 'number'
},
distance: {
type: 'integer'
}
}
})
t.assert.throws(() => {
stringify({ age: 'hello ', distance: 'long' })
}, { message: 'The value "hello " cannot be converted to a number.' })
const result = stringify({
age: '42',
distance: true
})
t.assert.deepStrictEqual(JSON.parse(result), { age: 42, distance: 1 })
})
test('Should throw on invalid schema', t => {
t.plan(1)
t.assert.throws(() => {
build({
type: 'Dinosaur',
properties: {
claws: { type: 'sharp' }
}
})
}, { message: 'schema is invalid: data/properties/claws/type must be equal to one of the allowed values' })
})
test('additionalProperties - throw on unknown type', (t) => {
t.plan(1)
t.assert.throws(() => {
build({
title: 'check array coerce',
type: 'object',
properties: {},
additionalProperties: {
type: 'strangetype'
}
})
t.fail('should be an invalid schema')
}, { message: 'schema is invalid: data/additionalProperties/type must be equal to one of the allowed values' })
})
test('patternProperties - throw on unknown type', (t) => {
t.plan(1)
t.assert.throws(() => {
build({
title: 'check array coerce',
type: 'object',
properties: {},
patternProperties: {
foo: {
type: 'strangetype'
}
}
})
}, { message: 'schema is invalid: data/patternProperties/foo/type must be equal to one of the allowed values' })
})
test('render a double quote as JSON /1', (t) => {
t.plan(2)
const schema = {
type: 'string'
}
const toStringify = '" double quote'
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a double quote as JSON /2', (t) => {
t.plan(2)
const schema = {
type: 'string'
}
const toStringify = 'double quote " 2'
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a long string', (t) => {
t.plan(2)
const schema = {
type: 'string'
}
const toStringify = 'the Ultimate Question of Life, the Universe, and Everything.'
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('returns JSON.stringify if schema type is boolean', t => {
t.plan(1)
const schema = {
type: 'array',
items: true
}
const array = [1, true, 'test']
const stringify = build(schema)
t.assert.equal(stringify(array), JSON.stringify(array))
})

76
node_modules/fast-json-stringify/test/bigint.test.js generated vendored Normal file
View File

@@ -0,0 +1,76 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('render a bigint as JSON', (t) => {
t.plan(1)
const schema = {
title: 'bigint',
type: 'integer'
}
const stringify = build(schema)
const output = stringify(1615n)
t.assert.equal(output, '1615')
})
test('render an object with a bigint as JSON', (t) => {
t.plan(1)
const schema = {
title: 'object with bigint',
type: 'object',
properties: {
id: {
type: 'integer'
}
}
}
const stringify = build(schema)
const output = stringify({
id: 1615n
})
t.assert.equal(output, '{"id":1615}')
})
test('render an array with a bigint as JSON', (t) => {
t.plan(1)
const schema = {
title: 'array with bigint',
type: 'array',
items: {
type: 'integer'
}
}
const stringify = build(schema)
const output = stringify([1615n])
t.assert.equal(output, '[1615]')
})
test('render an object with an additionalProperty of type bigint as JSON', (t) => {
t.plan(1)
const schema = {
title: 'object with bigint',
type: 'object',
additionalProperties: {
type: 'integer'
}
}
const stringify = build(schema)
const output = stringify({
num: 1615n
})
t.assert.equal(output, '{"num":1615}')
})

View File

@@ -0,0 +1,47 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('Should clean the cache', (t) => {
t.plan(1)
const schema = {
$id: 'test',
type: 'string'
}
t.assert.doesNotThrow(() => {
build(schema)
build(schema)
})
})
test('Should clean the cache with external schemas', (t) => {
t.plan(1)
const schema = {
$id: 'test',
definitions: {
def: {
type: 'object',
properties: {
str: {
type: 'string'
}
}
}
},
type: 'object',
properties: {
obj: {
$ref: '#/definitions/def'
}
}
}
t.assert.doesNotThrow(() => {
build(schema)
build(schema)
})
})

314
node_modules/fast-json-stringify/test/const.test.js generated vendored Normal file
View File

@@ -0,0 +1,314 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
test('schema with const string', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: 'bar' }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: 'bar'
})
t.assert.equal(output, '{"foo":"bar"}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const string and different input', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: 'bar' }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: 'baz'
})
t.assert.equal(output, '{"foo":"bar"}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const string and different type input', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: 'bar' }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: 1
})
t.assert.equal(output, '{"foo":"bar"}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const string and no input', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: 'bar' }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({})
t.assert.equal(output, '{}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const string that contains \'', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: "'bar'" }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: "'bar'"
})
t.assert.equal(output, '{"foo":"\'bar\'"}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const number', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: 1 }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: 1
})
t.assert.equal(output, '{"foo":1}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const number and different input', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: 1 }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: 2
})
t.assert.equal(output, '{"foo":1}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const bool', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: true }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: true
})
t.assert.equal(output, '{"foo":true}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const number', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: 1 }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: 1
})
t.assert.equal(output, '{"foo":1}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const null', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: null }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: null
})
t.assert.equal(output, '{"foo":null}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const array', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: [1, 2, 3] }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: [1, 2, 3]
})
t.assert.equal(output, '{"foo":[1,2,3]}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const object', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: { bar: 'baz' } }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: { bar: 'baz' }
})
t.assert.equal(output, '{"foo":{"bar":"baz"}}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('schema with const and null as type', (t) => {
t.plan(4)
const schema = {
type: 'object',
properties: {
foo: { type: ['string', 'null'], const: 'baz' }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: null
})
t.assert.equal(output, '{"foo":null}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
const output2 = stringify({ foo: 'baz' })
t.assert.equal(output2, '{"foo":"baz"}')
t.assert.ok(validate(JSON.parse(output2)), 'valid schema')
})
test('schema with const as nullable', (t) => {
t.plan(4)
const schema = {
type: 'object',
properties: {
foo: { nullable: true, const: 'baz' }
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
foo: null
})
t.assert.equal(output, '{"foo":null}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
const output2 = stringify({
foo: 'baz'
})
t.assert.equal(output2, '{"foo":"baz"}')
t.assert.ok(validate(JSON.parse(output2)), 'valid schema')
})
test('schema with const and invalid object', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
foo: { const: { foo: 'bar' } }
},
required: ['foo']
}
const validate = validator(schema)
const stringify = build(schema)
const result = stringify({
foo: { foo: 'baz' }
})
t.assert.equal(result, '{"foo":{"foo":"bar"}}')
t.assert.ok(validate(JSON.parse(result)), 'valid schema')
})

639
node_modules/fast-json-stringify/test/date.test.js generated vendored Normal file
View File

@@ -0,0 +1,639 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
process.env.TZ = 'UTC'
test('render a date in a string as JSON', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string'
}
const toStringify = new Date(1674263005800)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a date in a string when format is date-format as ISOString', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date-time'
}
const toStringify = new Date(1674263005800)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a nullable date in a string when format is date-format as ISOString', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date-time',
nullable: true
}
const toStringify = new Date(1674263005800)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a date in a string when format is date as YYYY-MM-DD', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date'
}
const toStringify = new Date(1674263005800)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, '"2023-01-21"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a nullable date in a string when format is date as YYYY-MM-DD', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date',
nullable: true
}
const toStringify = new Date(1674263005800)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, '"2023-01-21"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('verify padding for rendered date in a string when format is date', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date'
}
const toStringify = new Date(2020, 0, 1, 0, 0, 0, 0)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, '"2020-01-01"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a date in a string when format is time as kk:mm:ss', (t) => {
t.plan(3)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'time'
}
const toStringify = new Date(1674263005800)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
validate(JSON.parse(output))
t.assert.equal(validate.errors, null)
t.assert.equal(output, '"01:03:25"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a nullable date in a string when format is time as kk:mm:ss', (t) => {
t.plan(3)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'time',
nullable: true
}
const toStringify = new Date(1674263005800)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
validate(JSON.parse(output))
t.assert.equal(validate.errors, null)
t.assert.equal(output, '"01:03:25"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a midnight time', (t) => {
t.plan(3)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'time'
}
const midnight = new Date(new Date(1674263005800).setHours(24))
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(midnight)
validate(JSON.parse(output))
t.assert.equal(validate.errors, null)
t.assert.equal(output, '"00:03:25"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('verify padding for rendered date in a string when format is time', (t) => {
t.plan(3)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'time'
}
const toStringify = new Date(2020, 0, 1, 1, 1, 1, 1)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
validate(JSON.parse(output))
t.assert.equal(validate.errors, null)
t.assert.equal(output, '"01:01:01"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a nested object in a string when type is date-format as ISOString', (t) => {
t.plan(2)
const schema = {
title: 'an object in a string',
type: 'object',
properties: {
date: {
type: 'string',
format: 'date-time'
}
}
}
const toStringify = { date: new Date(1674263005800) }
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('serializing null value', async t => {
const input = { updatedAt: null }
function createSchema (properties) {
return {
title: 'an object in a string',
type: 'object',
properties
}
}
function serialize (schema, input) {
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(input)
return {
validate,
output
}
}
t.plan(3)
await t.test('type::string', async t => {
t.plan(3)
await t.test('format::date-time', t => {
t.plan(2)
const prop = {
updatedAt: {
type: 'string',
format: 'date-time'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":""}')
t.assert.equal(validate(JSON.parse(output)), false, 'an empty string is not a date-time format')
})
await t.test('format::date', t => {
t.plan(2)
const prop = {
updatedAt: {
type: 'string',
format: 'date'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":""}')
t.assert.equal(validate(JSON.parse(output)), false, 'an empty string is not a date format')
})
await t.test('format::time', t => {
t.plan(2)
const prop = {
updatedAt: {
type: 'string',
format: 'time'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":""}')
t.assert.equal(validate(JSON.parse(output)), false, 'an empty string is not a time format')
})
})
await t.test('type::array', async t => {
t.plan(6)
await t.test('format::date-time', t => {
t.plan(2)
const prop = {
updatedAt: {
type: ['string'],
format: 'date-time'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":""}')
t.assert.equal(validate(JSON.parse(output)), false, 'an empty string is not a date-time format')
})
await t.test('format::date', t => {
t.plan(2)
const prop = {
updatedAt: {
type: ['string'],
format: 'date'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":""}')
t.assert.equal(validate(JSON.parse(output)), false, 'an empty string is not a date format')
})
await t.test('format::date', t => {
t.plan(2)
const prop = {
updatedAt: {
type: ['string'],
format: 'date'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":""}')
t.assert.equal(validate(JSON.parse(output)), false, 'an empty string is not a date format')
})
await t.test('format::time, Date object', t => {
t.plan(1)
const schema = {
oneOf: [
{
type: 'object',
properties: {
updatedAt: {
type: ['string', 'number'],
format: 'time'
}
}
}
]
}
const date = new Date(1674263005800)
const input = { updatedAt: date }
const { output } = serialize(schema, input)
t.assert.equal(output, JSON.stringify({ updatedAt: '01:03:25' }))
})
await t.test('format::time, Date object', t => {
t.plan(1)
const schema = {
oneOf: [
{
type: ['string', 'number'],
format: 'time'
}
]
}
const date = new Date(1674263005800)
const { output } = serialize(schema, date)
t.assert.equal(output, '"01:03:25"')
})
await t.test('format::time, Date object', t => {
t.plan(1)
const schema = {
oneOf: [
{
type: ['string', 'number'],
format: 'time'
}
]
}
const { output } = serialize(schema, 42)
t.assert.equal(output, JSON.stringify(42))
})
})
await t.test('type::array::nullable', async t => {
t.plan(3)
await t.test('format::date-time', t => {
t.plan(2)
const prop = {
updatedAt: {
type: ['string', 'null'],
format: 'date-time'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":null}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
await t.test('format::date', t => {
t.plan(2)
const prop = {
updatedAt: {
type: ['string', 'null'],
format: 'date'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":null}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
await t.test('format::time', t => {
t.plan(2)
const prop = {
updatedAt: {
type: ['string', 'null'],
format: 'time'
}
}
const {
output,
validate
} = serialize(createSchema(prop), input)
t.assert.equal(output, '{"updatedAt":null}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
})
})
test('Validate Date object as string type', (t) => {
t.plan(1)
const schema = {
oneOf: [
{ type: 'string' }
]
}
const toStringify = new Date(1674263005800)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
})
test('nullable date', (t) => {
t.plan(1)
const schema = {
anyOf: [
{
format: 'date',
type: 'string',
nullable: true
}
]
}
const stringify = build(schema)
const data = new Date(1674263005800)
const result = stringify(data)
t.assert.equal(result, '"2023-01-21"')
})
test('non-date format should not affect data serialization (issue #491)', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
hello: {
type: 'string',
format: 'int64',
pattern: '^[0-9]*$'
}
}
}
const stringify = build(schema)
const data = { hello: 123n }
t.assert.equal(stringify(data), '{"hello":"123"}')
})
test('should serialize also an invalid string value, even if it is not a valid date', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date-time',
nullable: true
}
const toStringify = 'invalid'
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.equal(validate(JSON.parse(output)), false, 'valid schema')
})
test('should throw an error if value can not be transformed to date-time', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date-time',
nullable: true
}
const toStringify = true
const validate = validator(schema)
const stringify = build(schema)
t.assert.throws(() => stringify(toStringify), new Error('The value "true" cannot be converted to a date-time.'))
t.assert.equal(validate(toStringify), false)
})
test('should throw an error if value can not be transformed to date', (t) => {
t.plan(2)
const schema = {
title: 'a date in a string',
type: 'string',
format: 'date',
nullable: true
}
const toStringify = true
const validate = validator(schema)
const stringify = build(schema)
t.assert.throws(() => stringify(toStringify), new Error('The value "true" cannot be converted to a date.'))
t.assert.equal(validate(toStringify), false)
})
test('should throw an error if value can not be transformed to time', (t) => {
t.plan(2)
const schema = {
title: 'a time in a string',
type: 'string',
format: 'time',
nullable: true
}
const toStringify = true
const validate = validator(schema)
const stringify = build(schema)
t.assert.throws(() => stringify(toStringify), new Error('The value "true" cannot be converted to a time.'))
t.assert.equal(validate(toStringify), false)
})
test('should serialize also an invalid string value, even if it is not a valid time', (t) => {
t.plan(2)
const schema = {
title: 'a time in a string',
type: 'string',
format: 'time',
nullable: true
}
const toStringify = 'invalid'
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.equal(validate(JSON.parse(output)), false, 'valid schema')
})

View File

@@ -0,0 +1,121 @@
'use strict'
const { test } = require('node:test')
const fjs = require('..')
const Ajv = require('ajv').default
const Validator = require('../lib/validator')
const Serializer = require('../lib/serializer')
function build (opts) {
return fjs({
title: 'default string',
type: 'object',
properties: {
firstName: {
type: 'string'
}
},
required: ['firstName']
}, opts)
}
test('activate debug mode', t => {
t.plan(5)
const debugMode = build({ debugMode: true })
t.assert.ok(typeof debugMode === 'object')
t.assert.ok(debugMode.ajv instanceof Ajv)
t.assert.ok(debugMode.validator instanceof Validator)
t.assert.ok(debugMode.serializer instanceof Serializer)
t.assert.ok(typeof debugMode.code === 'string')
})
test('activate debug mode truthy', t => {
t.plan(5)
const debugMode = build({ debugMode: 'yes' })
t.assert.ok(typeof debugMode === 'object')
t.assert.ok(typeof debugMode.code === 'string')
t.assert.ok(debugMode.ajv instanceof Ajv)
t.assert.ok(debugMode.validator instanceof Validator)
t.assert.ok(debugMode.serializer instanceof Serializer)
})
test('to string auto-consistent', t => {
t.plan(6)
const debugMode = build({ debugMode: 1 })
t.assert.ok(typeof debugMode === 'object')
t.assert.ok(typeof debugMode.code === 'string')
t.assert.ok(debugMode.ajv instanceof Ajv)
t.assert.ok(debugMode.serializer instanceof Serializer)
t.assert.ok(debugMode.validator instanceof Validator)
const compiled = fjs.restore(debugMode)
const tobe = JSON.stringify({ firstName: 'Foo' })
t.assert.equal(compiled({ firstName: 'Foo', surname: 'bar' }), tobe, 'surname evicted')
})
test('to string auto-consistent with ajv', t => {
t.plan(6)
const debugMode = fjs({
title: 'object with multiple types field',
type: 'object',
properties: {
str: {
anyOf: [{
type: 'string'
}, {
type: 'boolean'
}]
}
}
}, { debugMode: 1 })
t.assert.ok(typeof debugMode === 'object')
t.assert.ok(typeof debugMode.code === 'string')
t.assert.ok(debugMode.ajv instanceof Ajv)
t.assert.ok(debugMode.validator instanceof Validator)
t.assert.ok(debugMode.serializer instanceof Serializer)
const compiled = fjs.restore(debugMode)
const tobe = JSON.stringify({ str: 'Foo' })
t.assert.equal(compiled({ str: 'Foo', void: 'me' }), tobe)
})
test('to string auto-consistent with ajv-formats', t => {
t.plan(3)
const debugMode = fjs({
title: 'object with multiple types field and format keyword',
type: 'object',
properties: {
str: {
anyOf: [{
type: 'string',
format: 'email'
}, {
type: 'boolean'
}]
}
}
}, { debugMode: 1 })
t.assert.ok(typeof debugMode === 'object')
const compiled = fjs.restore(debugMode)
const tobe = JSON.stringify({ str: 'foo@bar.com' })
t.assert.equal(compiled({ str: 'foo@bar.com' }), tobe)
t.assert.throws(() => compiled({ str: 'foo' }))
})
test('debug should restore the same serializer instance', t => {
t.plan(1)
const debugMode = fjs({ type: 'integer' }, { debugMode: 1, rounding: 'ceil' })
const compiled = fjs.restore(debugMode)
t.assert.equal(compiled(3.95), 4)
})

376
node_modules/fast-json-stringify/test/defaults.test.js generated vendored Normal file
View File

@@ -0,0 +1,376 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
function buildTest (schema, toStringify, expected) {
test(`render a ${schema.title} with default as JSON`, (t) => {
t.plan(1)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.equal(output, JSON.stringify(expected))
})
}
buildTest({
title: 'default string',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string',
default: 'Collina'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
magic: {
type: 'number'
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
magic: 42,
age: 32
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
magic: 42
})
buildTest({
title: 'default string with value',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string',
default: 'Collina'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
magic: {
type: 'number'
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'collina',
magic: 42,
age: 32
}, {
firstName: 'Matteo',
lastName: 'collina',
age: 32,
magic: 42
})
buildTest({
title: 'default number',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
magic: {
type: 'number',
default: 42
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
magic: 42
})
buildTest({
title: 'default number with value',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
magic: {
type: 'number',
default: 42
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
magic: 66
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
magic: 66
})
buildTest({
title: 'default object',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
otherProps: {
type: 'object',
default: { foo: 'bar' }
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
otherProps: { foo: 'bar' }
})
buildTest({
title: 'default object with value',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
otherProps: {
type: 'object',
additionalProperties: true,
default: { foo: 'bar' }
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
otherProps: { hello: 'world' }
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
otherProps: { hello: 'world' }
})
buildTest({
title: 'default array',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
otherProps: {
type: 'array',
items: { type: 'string' },
default: ['FOO']
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
otherProps: ['FOO']
})
buildTest({
title: 'default array with value',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
},
otherProps: {
type: 'array',
items: { type: 'string' },
default: ['FOO']
}
},
required: ['firstName', 'lastName']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
otherProps: ['BAR']
}, {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
otherProps: ['BAR']
})
buildTest({
title: 'default deeper value',
type: 'object',
properties: {
level1: {
type: 'object',
properties: {
level2: {
type: 'object',
properties: {
level3: {
type: 'object',
properties: {
level4: {
type: 'object',
default: { foo: 'bar' }
}
}
}
}
}
}
}
}
}, {
level1: { level2: { level3: { } } }
}, {
level1: { level2: { level3: { level4: { foo: 'bar' } } } }
})
buildTest({
title: 'default deeper value with value',
type: 'object',
properties: {
level1: {
type: 'object',
properties: {
level2: {
type: 'object',
properties: {
level3: {
type: 'object',
properties: {
level4: {
type: 'object',
default: { foo: 'bar' }
}
}
}
}
}
}
}
}
}, {
level1: { level2: { level3: { level4: { } } } }
}, {
level1: { level2: { level3: { level4: { } } } }
})
buildTest({
type: 'object',
properties: {
name: {
type: 'string',
default: 'foo'
},
dev: {
type: 'boolean',
default: false
}
},
required: [
'name', 'dev'
]
}, {}, { name: 'foo', dev: false })
buildTest({
type: 'object',
properties: {
name: {
type: 'string',
default: 'foo'
},
dev: {
type: 'boolean'
},
job: {
type: 'string',
default: 'awesome'
}
},
required: [
'name', 'dev'
]
}, { dev: true }, { name: 'foo', dev: true, job: 'awesome' })

37
node_modules/fast-json-stringify/test/enum.test.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('use enum without type', (t) => {
t.plan(1)
const stringify = build({
title: 'Example Schema',
type: 'object',
properties: {
order: {
type: 'string',
enum: ['asc', 'desc']
}
}
})
const obj = { order: 'asc' }
t.assert.equal('{"order":"asc"}', stringify(obj))
})
test('use enum without type', (t) => {
t.plan(1)
const stringify = build({
title: 'Example Schema',
type: 'object',
properties: {
order: {
enum: ['asc', 'desc']
}
}
})
const obj = { order: 'asc' }
t.assert.equal('{"order":"asc"}', stringify(obj))
})

25
node_modules/fast-json-stringify/test/fix-604.test.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
'use strict'
const { test } = require('node:test')
const fjs = require('..')
test('fix-604', t => {
const schema = {
type: 'object',
properties: {
fullName: { type: 'string' },
phone: { type: 'number' }
}
}
const input = {
fullName: 'Jone',
phone: 'phone'
}
const render = fjs(schema)
t.assert.throws(() => {
render(input)
}, { message: 'The value "phone" cannot be converted to a number.' })
})

0
node_modules/fast-json-stringify/test/fixtures/.keep generated vendored Normal file
View File

View File

@@ -0,0 +1,468 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
process.env.TZ = 'UTC'
const schema = {
type: 'object',
properties: {
},
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] }
}
},
then: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] },
foo: { type: 'string' },
bar: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
},
else: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['greeting'] },
hi: { type: 'string' },
hello: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
}
}
const nestedIfSchema = {
type: 'object',
properties: { },
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar', 'greeting'] }
}
},
then: {
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] }
}
},
then: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] },
foo: { type: 'string' },
bar: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
},
else: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['greeting'] },
hi: { type: 'string' },
hello: { type: 'number' }
}
}
},
else: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['alphabet'] },
a: { type: 'string' },
b: { type: 'number' }
}
}
}
const nestedElseSchema = {
type: 'object',
properties: { },
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] }
}
},
then: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] },
foo: { type: 'string' },
bar: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
},
else: {
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['greeting'] }
}
},
then: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['greeting'] },
hi: { type: 'string' },
hello: { type: 'number' }
}
},
else: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['alphabet'] },
a: { type: 'string' },
b: { type: 'number' }
}
}
}
}
const nestedDeepElseSchema = {
type: 'object',
additionalProperties: schema
}
const noElseSchema = {
type: 'object',
properties: {
},
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] }
}
},
then: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] },
foo: { type: 'string' },
bar: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
}
}
const fooBarInput = {
kind: 'foobar',
foo: 'FOO',
list: [{
name: 'name',
value: 'foo'
}],
bar: 42,
hi: 'HI',
hello: 45,
a: 'A',
b: 35
}
const greetingInput = {
kind: 'greeting',
foo: 'FOO',
bar: 42,
hi: 'HI',
hello: 45,
a: 'A',
b: 35
}
const alphabetInput = {
kind: 'alphabet',
foo: 'FOO',
bar: 42,
hi: 'HI',
hello: 45,
a: 'A',
b: 35
}
const deepFoobarInput = {
foobar: fooBarInput
}
const foobarOutput = JSON.stringify({
kind: 'foobar',
foo: 'FOO',
bar: 42,
list: [{
name: 'name',
value: 'foo'
}]
})
const greetingOutput = JSON.stringify({
kind: 'greeting',
hi: 'HI',
hello: 45
})
const alphabetOutput = JSON.stringify({
kind: 'alphabet',
a: 'A',
b: 35
})
const deepFoobarOutput = JSON.stringify({
foobar: JSON.parse(foobarOutput)
})
const noElseGreetingOutput = JSON.stringify({})
test('if-then-else', async t => {
const tests = [
{
name: 'foobar',
schema,
input: fooBarInput,
expected: foobarOutput
},
{
name: 'greeting',
schema,
input: greetingInput,
expected: greetingOutput
},
{
name: 'if nested - then then',
schema: nestedIfSchema,
input: fooBarInput,
expected: foobarOutput
},
{
name: 'if nested - then else',
schema: nestedIfSchema,
input: greetingInput,
expected: greetingOutput
},
{
name: 'if nested - else',
schema: nestedIfSchema,
input: alphabetInput,
expected: alphabetOutput
},
{
name: 'else nested - then',
schema: nestedElseSchema,
input: fooBarInput,
expected: foobarOutput
},
{
name: 'else nested - else then',
schema: nestedElseSchema,
input: greetingInput,
expected: greetingOutput
},
{
name: 'else nested - else else',
schema: nestedElseSchema,
input: alphabetInput,
expected: alphabetOutput
},
{
name: 'deep then - else',
schema: nestedDeepElseSchema,
input: deepFoobarInput,
expected: deepFoobarOutput
},
{
name: 'no else',
schema: noElseSchema,
input: greetingInput,
expected: noElseGreetingOutput
}
]
for (const { name, schema, input, expected } of tests) {
await t.test(name + ' - normal', async t => {
t.plan(1)
const stringify = build(JSON.parse(JSON.stringify(schema)), { ajv: { strictTypes: false } })
const serialized = stringify(input)
t.assert.equal(serialized, expected)
})
}
})
test('nested if/then', t => {
t.plan(2)
const schema = {
type: 'object',
properties: { a: { type: 'string' } },
if: {
type: 'object',
properties: { foo: { type: 'string' } }
},
then: {
properties: { bar: { type: 'string' } },
if: {
type: 'object',
properties: { foo1: { type: 'string' } }
},
then: {
properties: { bar1: { type: 'string' } }
}
}
}
const stringify = build(schema)
t.assert.equal(
stringify({ a: 'A', foo: 'foo', bar: 'bar' }),
JSON.stringify({ a: 'A', bar: 'bar' })
)
t.assert.equal(
stringify({ a: 'A', foo: 'foo', bar: 'bar', foo1: 'foo1', bar1: 'bar1' }),
JSON.stringify({ a: 'A', bar: 'bar', bar1: 'bar1' })
)
})
test('if/else with string format', (t) => {
t.plan(2)
const schema = {
if: { type: 'string' },
then: { type: 'string', format: 'date' },
else: { const: 'Invalid' }
}
const stringify = build(schema)
const date = new Date(1674263005800)
t.assert.equal(stringify(date), '"2023-01-21"')
t.assert.equal(stringify('Invalid'), '"Invalid"')
})
test('if/else with const integers', (t) => {
t.plan(2)
const schema = {
type: 'number',
if: { type: 'number', minimum: 42 },
then: { const: 66 },
else: { const: 33 }
}
const stringify = build(schema)
t.assert.equal(stringify(100.32), '66')
t.assert.equal(stringify(10.12), '33')
})
test('if/else with array', (t) => {
t.plan(2)
const schema = {
type: 'array',
if: { type: 'array', maxItems: 1 },
then: { items: { type: 'string' } },
else: { items: { type: 'number' } }
}
const stringify = build(schema)
t.assert.equal(stringify(['1']), JSON.stringify(['1']))
t.assert.equal(stringify(['1', '2']), JSON.stringify([1, 2]))
})
test('external recursive if/then/else', (t) => {
t.plan(1)
const externalSchema = {
type: 'object',
properties: {
base: { type: 'string' },
self: { $ref: 'externalSchema#' }
},
if: {
type: 'object',
properties: {
foo: { type: 'string', const: '41' }
}
},
then: {
type: 'object',
properties: {
bar: { type: 'string', const: '42' }
}
},
else: {
type: 'object',
properties: {
baz: { type: 'string', const: '43' }
}
}
}
const schema = {
type: 'object',
properties: {
a: { $ref: 'externalSchema#/properties/self' },
b: { $ref: 'externalSchema#/properties/self' }
}
}
const data = {
a: {
base: 'a',
foo: '41',
bar: '42',
baz: '43',
ignore: 'ignored'
},
b: {
base: 'b',
foo: 'not-41',
bar: '42',
baz: '43',
ignore: 'ignored'
}
}
const stringify = build(schema, { schema: { externalSchema } })
t.assert.equal(stringify(data), '{"a":{"base":"a","bar":"42"},"b":{"base":"b","baz":"43"}}')
})

View File

@@ -0,0 +1,92 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
function buildTest (schema, toStringify) {
test(`render a ${schema.title} as JSON`, (t) => {
t.plan(3)
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(toStringify)
t.assert.deepStrictEqual(JSON.parse(output), toStringify)
t.assert.equal(output, JSON.stringify(toStringify))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
}
buildTest({
title: 'infer type object by keyword',
// 'type': 'object',
properties: {
name: {
type: 'string'
}
}
}, {
name: 'foo'
})
buildTest({
title: 'infer type of nested object by keyword',
// 'type': 'object',
properties: {
more: {
description: 'more properties',
// 'type': 'object',
properties: {
something: {
type: 'string'
}
}
}
}
}, {
more: {
something: 'else'
}
})
buildTest({
title: 'infer type array by keyword',
type: 'object',
properties: {
ids: {
// 'type': 'array',
items: {
type: 'string'
}
}
}
}, {
ids: ['test']
})
buildTest({
title: 'infer type string by keyword',
type: 'object',
properties: {
name: {
// 'type': 'string',
maxLength: 3
}
}
}, {
name: 'foo'
})
buildTest({
title: 'infer type number by keyword',
type: 'object',
properties: {
age: {
// 'type': 'number',
maximum: 18
}
}
}, {
age: 18
})

55
node_modules/fast-json-stringify/test/infinity.test.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('Finite numbers', t => {
const values = [-5, 0, -0, 1.33, 99, 100.0,
Math.E, Number.EPSILON,
Number.MAX_SAFE_INTEGER, Number.MAX_VALUE,
Number.MIN_SAFE_INTEGER, Number.MIN_VALUE]
t.plan(values.length)
const schema = {
type: 'number'
}
const stringify = build(schema)
values.forEach(v => t.assert.equal(stringify(v), JSON.stringify(v)))
})
test('Infinite integers', t => {
const values = [Infinity, -Infinity]
t.plan(values.length)
const schema = {
type: 'integer'
}
const stringify = build(schema)
values.forEach(v => {
try {
stringify(v)
} catch (err) {
t.assert.equal(err.message, `The value "${v}" cannot be converted to an integer.`)
}
})
})
test('Infinite numbers', t => {
const values = [Infinity, -Infinity]
t.plan(values.length)
const schema = {
type: 'number'
}
const stringify = build(schema)
values.forEach(v => t.assert.equal(stringify(v), JSON.stringify(v)))
})

194
node_modules/fast-json-stringify/test/integer.test.js generated vendored Normal file
View File

@@ -0,0 +1,194 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
const ROUNDING_TYPES = ['ceil', 'floor', 'round']
test('render an integer as JSON', (t) => {
t.plan(2)
const schema = {
title: 'integer',
type: 'integer'
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify(1615)
t.assert.equal(output, '1615')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a float as an integer', (t) => {
t.plan(2)
try {
build({
title: 'float as integer',
type: 'integer'
}, { rounding: 'foobar' })
} catch (error) {
t.assert.ok(error)
t.assert.equal(error.message, 'Unsupported integer rounding method foobar')
}
})
test('throws on NaN', (t) => {
t.plan(1)
const schema = {
title: 'integer',
type: 'integer'
}
const stringify = build(schema)
t.assert.throws(() => stringify(NaN), new Error('The value "NaN" cannot be converted to an integer.'))
})
test('render a float as an integer', (t) => {
const cases = [
{ input: Math.PI, output: '3' },
{ input: 5.0, output: '5' },
{ input: null, output: '0' },
{ input: 0, output: '0' },
{ input: 0.0, output: '0' },
{ input: 42, output: '42' },
{ input: 1.99999, output: '1' },
{ input: -45.05, output: '-45' },
{ input: 3333333333333333, output: '3333333333333333' },
{ input: Math.PI, output: '3', rounding: 'trunc' },
{ input: 5.0, output: '5', rounding: 'trunc' },
{ input: null, output: '0', rounding: 'trunc' },
{ input: 0, output: '0', rounding: 'trunc' },
{ input: 0.0, output: '0', rounding: 'trunc' },
{ input: 42, output: '42', rounding: 'trunc' },
{ input: 1.99999, output: '1', rounding: 'trunc' },
{ input: -45.05, output: '-45', rounding: 'trunc' },
{ input: 0.95, output: '1', rounding: 'ceil' },
{ input: 0.2, output: '1', rounding: 'ceil' },
{ input: 45.95, output: '45', rounding: 'floor' },
{ input: -45.05, output: '-46', rounding: 'floor' },
{ input: 45.44, output: '45', rounding: 'round' },
{ input: 45.95, output: '46', rounding: 'round' }
]
t.plan(cases.length * 2)
cases.forEach(checkInteger)
function checkInteger ({ input, output, rounding }) {
const schema = {
title: 'float as integer',
type: 'integer'
}
const validate = validator(schema)
const stringify = build(schema, { rounding })
const str = stringify(input)
t.assert.equal(str, output)
t.assert.ok(validate(JSON.parse(str)), 'valid schema')
}
})
test('render an object with an integer as JSON', (t) => {
t.plan(2)
const schema = {
title: 'object with integer',
type: 'object',
properties: {
id: {
type: 'integer'
}
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
id: 1615
})
t.assert.equal(output, '{"id":1615}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render an array with an integer as JSON', (t) => {
t.plan(2)
const schema = {
title: 'array with integer',
type: 'array',
items: {
type: 'integer'
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify([1615])
t.assert.equal(output, '[1615]')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render an object with an additionalProperty of type integer as JSON', (t) => {
t.plan(2)
const schema = {
title: 'object with integer',
type: 'object',
additionalProperties: {
type: 'integer'
}
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({
num: 1615
})
t.assert.equal(output, '{"num":1615}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('should round integer object parameter', t => {
t.plan(2)
const schema = { type: 'object', properties: { magic: { type: 'integer' } } }
const validate = validator(schema)
const stringify = build(schema, { rounding: 'ceil' })
const output = stringify({ magic: 4.2 })
t.assert.equal(output, '{"magic":5}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('should not stringify a property if it does not exist', t => {
t.plan(2)
const schema = { title: 'Example Schema', type: 'object', properties: { age: { type: 'integer' } } }
const validate = validator(schema)
const stringify = build(schema)
const output = stringify({})
t.assert.equal(output, '{}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
ROUNDING_TYPES.forEach((rounding) => {
test(`should not stringify a property if it does not exist (rounding: ${rounding})`, t => {
t.plan(2)
const schema = { type: 'object', properties: { magic: { type: 'integer' } } }
const validate = validator(schema)
const stringify = build(schema, { rounding })
const output = stringify({})
t.assert.equal(output, '{}')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
})

View File

@@ -0,0 +1,18 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
// Covers issue #139
test('Should throw on invalid schema', t => {
t.plan(1)
t.assert.throws(() => {
build({}, {
schema: {
invalid: {
type: 'Dinosaur'
}
}
})
}, { message: /^"invalid" schema is invalid:.*/ })
})

View File

@@ -0,0 +1,57 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('should validate anyOf after allOf merge', (t) => {
t.plan(1)
const schema = {
$id: 'schema',
type: 'object',
allOf: [
{
$id: 'base',
type: 'object',
properties: {
name: {
type: 'string'
}
},
required: [
'name'
]
},
{
$id: 'inner_schema',
type: 'object',
properties: {
union: {
$id: '#id',
anyOf: [
{
$id: 'guid',
type: 'string'
},
{
$id: 'email',
type: 'string'
}
]
}
},
required: [
'union'
]
}
]
}
const stringify = build(schema)
t.assert.equal(
stringify({ name: 'foo', union: 'a8f1cc50-5530-5c62-9109-5ba9589a6ae1' }),
'{"name":"foo","union":"a8f1cc50-5530-5c62-9109-5ba9589a6ae1"}')
})

View File

@@ -0,0 +1,10 @@
# JSON-Schema-Test-Suite
You can find all test cases [here](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
It contains a set of JSON objects that implementors of JSON Schema validation libraries can use to test their validators.
# How to add another test case?
1. Navigate to [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/master/tests)
2. Choose a draft `draft4`, `draft6` or `draft7`
3. Copy & paste the `test-case.json` to the project and add a test like in the `draft4.test.js`

View File

@@ -0,0 +1,12 @@
'use strict'
const { test } = require('node:test')
const { counTests, runTests } = require('./util')
const requiredTestSuite = require('./draft4/required.json')
test('required', async (t) => {
const skippedTests = ['ignores arrays', 'ignores strings', 'ignores other non-objects']
t.plan(counTests(requiredTestSuite, skippedTests))
await runTests(t, requiredTestSuite, skippedTests)
})

View File

@@ -0,0 +1,54 @@
[
{
"description": "required validation",
"schema": {
"properties": {
"foo": {},
"bar": {}
},
"required": ["foo"]
},
"tests": [
{
"description": "present required property is valid",
"data": {"foo": 1},
"valid": true
},
{
"description": "non-present required property is invalid",
"data": {"bar": 1},
"valid": false
},
{
"description": "ignores arrays",
"data": [],
"valid": true
},
{
"description": "ignores strings",
"data": "",
"valid": true
},
{
"description": "ignores other non-objects",
"data": 12,
"valid": true
}
]
},
{
"description": "required default validation",
"schema": {
"properties": {
"foo": {}
}
},
"tests": [
{
"description": "not required by default",
"data": {},
"valid": true
}
]
}
]

View File

@@ -0,0 +1,12 @@
'use strict'
const { test } = require('node:test')
const { counTests, runTests } = require('./util')
const requiredTestSuite = require('./draft6/required.json')
test('required', async (t) => {
const skippedTests = ['ignores arrays', 'ignores strings', 'ignores other non-objects']
t.plan(counTests(requiredTestSuite, skippedTests))
await runTests(t, requiredTestSuite, skippedTests)
})

View File

@@ -0,0 +1,70 @@
[
{
"description": "required validation",
"schema": {
"properties": {
"foo": {},
"bar": {}
},
"required": ["foo"]
},
"tests": [
{
"description": "present required property is valid",
"data": {"foo": 1},
"valid": true
},
{
"description": "non-present required property is invalid",
"data": {"bar": 1},
"valid": false
},
{
"description": "ignores arrays",
"data": [],
"valid": true
},
{
"description": "ignores strings",
"data": "",
"valid": true
},
{
"description": "ignores other non-objects",
"data": 12,
"valid": true
}
]
},
{
"description": "required default validation",
"schema": {
"properties": {
"foo": {}
}
},
"tests": [
{
"description": "not required by default",
"data": {},
"valid": true
}
]
},
{
"description": "required with empty array",
"schema": {
"properties": {
"foo": {}
},
"required": []
},
"tests": [
{
"description": "property not required",
"data": {},
"valid": true
}
]
}
]

View File

@@ -0,0 +1,12 @@
'use strict'
const { test } = require('node:test')
const { counTests, runTests } = require('./util')
const requiredTestSuite = require('./draft7/required.json')
test('required', async (t) => {
const skippedTests = ['ignores arrays', 'ignores strings', 'ignores other non-objects']
t.plan(counTests(requiredTestSuite, skippedTests))
await runTests(t, requiredTestSuite, skippedTests)
})

View File

@@ -0,0 +1,70 @@
[
{
"description": "required validation",
"schema": {
"properties": {
"foo": {},
"bar": {}
},
"required": ["foo"]
},
"tests": [
{
"description": "present required property is valid",
"data": {"foo": 1},
"valid": true
},
{
"description": "non-present required property is invalid",
"data": {"bar": 1},
"valid": false
},
{
"description": "ignores arrays",
"data": [],
"valid": true
},
{
"description": "ignores strings",
"data": "",
"valid": true
},
{
"description": "ignores other non-objects",
"data": 12,
"valid": true
}
]
},
{
"description": "required default validation",
"schema": {
"properties": {
"foo": {}
}
},
"tests": [
{
"description": "not required by default",
"data": {},
"valid": true
}
]
},
{
"description": "required with empty array",
"schema": {
"properties": {
"foo": {}
},
"required": []
},
"tests": [
{
"description": "property not required",
"data": {},
"valid": true
}
]
}
]

View File

@@ -0,0 +1,31 @@
'use strict'
const build = require('../..')
async function runTests (t, testsuite, skippedTests) {
for (const scenario of testsuite) {
const stringify = build(scenario.schema)
for (const test of scenario.tests) {
if (skippedTests.indexOf(test.description) !== -1) {
console.log(`skip ${test.description}`)
continue
}
await t.test(test.description, (t) => {
t.plan(1)
try {
const output = stringify(test.data)
t.assert.equal(output, JSON.stringify(test.data), 'compare payloads')
} catch (err) {
t.assert.ok(test.valid === false, 'payload should be valid: ' + err.message)
}
})
}
}
}
function counTests (ts, skippedTests) {
return ts.reduce((a, b) => a + b.tests.length, 0) - skippedTests.length
}
module.exports = { runTests, counTests }

View File

@@ -0,0 +1,88 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('missing values', (t) => {
t.plan(3)
const stringify = build({
title: 'object with missing values',
type: 'object',
properties: {
str: {
type: 'string'
},
num: {
type: 'number'
},
val: {
type: 'string'
}
}
})
t.assert.equal('{"val":"value"}', stringify({ val: 'value' }))
t.assert.equal('{"str":"string","val":"value"}', stringify({ str: 'string', val: 'value' }))
t.assert.equal('{"str":"string","num":42,"val":"value"}', stringify({ str: 'string', num: 42, val: 'value' }))
})
test('handle null when value should be string', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
str: {
type: 'string'
}
}
})
t.assert.equal('{"str":""}', stringify({ str: null }))
})
test('handle null when value should be integer', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
int: {
type: 'integer'
}
}
})
t.assert.equal('{"int":0}', stringify({ int: null }))
})
test('handle null when value should be number', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
num: {
type: 'number'
}
}
})
t.assert.equal('{"num":0}', stringify({ num: null }))
})
test('handle null when value should be boolean', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
bool: {
type: 'boolean'
}
}
})
t.assert.equal('{"bool":false}', stringify({ bool: null }))
})

View File

@@ -0,0 +1,19 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('should throw a TypeError with the path to the key of the invalid value', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
num: {
type: ['number']
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ num: { bla: 123 } }), new TypeError('The value of \'#/properties/num\' does not match schema definition.'))
})

View File

@@ -0,0 +1,63 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('nested objects with same properties', (t) => {
t.plan(1)
const schema = {
title: 'nested objects with same properties',
type: 'object',
properties: {
stringProperty: {
type: 'string'
},
objectProperty: {
type: 'object',
additionalProperties: true
}
}
}
const stringify = build(schema)
const value = stringify({
stringProperty: 'string1',
objectProperty: {
stringProperty: 'string2',
numberProperty: 42
}
})
t.assert.equal(value, '{"stringProperty":"string1","objectProperty":{"stringProperty":"string2","numberProperty":42}}')
})
test('names collision', (t) => {
t.plan(1)
const schema = {
title: 'nested objects with same properties',
type: 'object',
properties: {
test: {
type: 'object',
properties: {
a: { type: 'string' }
}
},
tes: {
type: 'object',
properties: {
b: { type: 'string' },
t: { type: 'object' }
}
}
}
}
const stringify = build(schema)
const data = {
test: { a: 'a' },
tes: { b: 'b', t: {} }
}
t.assert.equal(stringify(data), JSON.stringify(data))
})

543
node_modules/fast-json-stringify/test/nullable.test.js generated vendored Normal file
View File

@@ -0,0 +1,543 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
const nullable = true
const complexObject = {
type: 'object',
properties: {
nullableString: { type: 'string', nullable },
nullableNumber: { type: 'number', nullable },
nullableInteger: { type: 'integer', nullable },
nullableBoolean: { type: 'boolean', nullable },
nullableNull: { type: 'null', nullable },
nullableArray: {
type: 'array',
nullable: true,
items: {}
},
nullableObject: { type: 'object', nullable: true },
objectWithNullableProps: {
type: 'object',
nullable: false,
additionalProperties: true,
properties: {
nullableString: { type: 'string', nullable },
nullableNumber: { type: 'number', nullable },
nullableInteger: { type: 'integer', nullable },
nullableBoolean: { type: 'boolean', nullable },
nullableNull: { type: 'null', nullable },
nullableArray: {
type: 'array',
nullable: true,
items: {}
}
}
},
arrayWithNullableItems: {
type: 'array',
nullable: true,
items: { type: ['integer', 'string'], nullable: true }
}
}
}
const complexData = {
nullableString: null,
nullableNumber: null,
nullableInteger: null,
nullableBoolean: null,
nullableNull: null,
nullableArray: null,
nullableObject: null,
objectWithNullableProps: {
additionalProp: null,
nullableString: null,
nullableNumber: null,
nullableInteger: null,
nullableBoolean: null,
nullableNull: null,
nullableArray: null
},
arrayWithNullableItems: [1, 2, null]
}
const complexExpectedResult = {
nullableString: null,
nullableNumber: null,
nullableInteger: null,
nullableBoolean: null,
nullableNull: null,
nullableArray: null,
nullableObject: null,
objectWithNullableProps: {
additionalProp: null,
nullableString: null,
nullableNumber: null,
nullableInteger: null,
nullableBoolean: null,
nullableNull: null,
nullableArray: null
},
arrayWithNullableItems: [1, 2, null]
}
const testSet = {
nullableString: [{ type: 'string', nullable }, null, null],
nullableNumber: [{ type: 'number', nullable }, null, null],
nullableInteger: [{ type: 'integer', nullable }, null, null],
nullableBoolean: [{ type: 'boolean', nullable }, null, null],
nullableNull: [{ type: 'null', nullable }, null, null],
nullableArray: [{
type: 'array',
nullable: true,
items: {}
}, null, null],
nullableObject: [{ type: 'object', nullable: true }, null, null],
complexObject: [complexObject, complexData, complexExpectedResult, { ajv: { allowUnionTypes: true } }]
}
Object.keys(testSet).forEach(key => {
test(`handle nullable:true in ${key} correctly`, (t) => {
t.plan(1)
const [
schema,
data,
expected,
extraOptions
] = testSet[key]
const stringifier = build(schema, extraOptions)
const result = stringifier(data)
t.assert.deepStrictEqual(JSON.parse(result), expected)
})
})
test('handle nullable number correctly', (t) => {
t.plan(2)
const schema = {
type: 'number',
nullable: true
}
const stringify = build(schema)
const data = null
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.equal(JSON.parse(result), data)
})
test('handle nullable integer correctly', (t) => {
t.plan(2)
const schema = {
type: 'integer',
nullable: true
}
const stringify = build(schema)
const data = null
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.equal(JSON.parse(result), data)
})
test('handle nullable boolean correctly', (t) => {
t.plan(2)
const schema = {
type: 'boolean',
nullable: true
}
const stringify = build(schema)
const data = null
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.equal(JSON.parse(result), data)
})
test('handle nullable string correctly', (t) => {
t.plan(2)
const schema = {
type: 'string',
nullable: true
}
const stringify = build(schema)
const data = null
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.equal(JSON.parse(result), data)
})
test('handle nullable date-time correctly', (t) => {
t.plan(2)
const schema = {
type: 'string',
format: 'date-time',
nullable: true
}
const stringify = build(schema)
const data = null
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.equal(JSON.parse(result), data)
})
test('handle nullable date correctly', (t) => {
t.plan(2)
const schema = {
type: 'string',
format: 'date',
nullable: true
}
const stringify = build(schema)
const data = null
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.equal(JSON.parse(result), data)
})
test('handle nullable time correctly', (t) => {
t.plan(2)
const schema = {
type: 'string',
format: 'time',
nullable: true
}
const stringify = build(schema)
const data = null
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.equal(JSON.parse(result), data)
})
test('large array of nullable strings with default mechanism', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'string',
nullable: true
}
}
}
}
const options = {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
}
const stringify = build(schema, options)
const data = { ids: new Array(2e4).fill(null) }
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.deepStrictEqual(JSON.parse(result), data)
})
test('large array of nullable date-time strings with default mechanism', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'string',
format: 'date-time',
nullable: true
}
}
}
}
const options = {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
}
const stringify = build(schema, options)
const data = { ids: new Array(2e4).fill(null) }
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.deepStrictEqual(JSON.parse(result), data)
})
test('large array of nullable date-time strings with default mechanism', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'string',
format: 'date',
nullable: true
}
}
}
}
const options = {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
}
const stringify = build(schema, options)
const data = { ids: new Array(2e4).fill(null) }
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.deepStrictEqual(JSON.parse(result), data)
})
test('large array of nullable date-time strings with default mechanism', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'string',
format: 'time',
nullable: true
}
}
}
}
const options = {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
}
const stringify = build(schema, options)
const data = { ids: new Array(2e4).fill(null) }
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.deepStrictEqual(JSON.parse(result), data)
})
test('large array of nullable numbers with default mechanism', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'number',
nullable: true
}
}
}
}
const options = {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
}
const stringify = build(schema, options)
const data = { ids: new Array(2e4).fill(null) }
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.deepStrictEqual(JSON.parse(result), data)
})
test('large array of nullable integers with default mechanism', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'integer',
nullable: true
}
}
}
}
const options = {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
}
const stringify = build(schema, options)
const data = { ids: new Array(2e4).fill(null) }
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.deepStrictEqual(JSON.parse(result), data)
})
test('large array of nullable booleans with default mechanism', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
ids: {
type: 'array',
items: {
type: 'boolean',
nullable: true
}
}
}
}
const options = {
largeArraySize: 2e4,
largeArrayMechanism: 'default'
}
const stringify = build(schema, options)
const data = { ids: new Array(2e4).fill(null) }
const result = stringify(data)
t.assert.equal(result, JSON.stringify(data))
t.assert.deepStrictEqual(JSON.parse(result), data)
})
test('nullable type in the schema', (t) => {
t.plan(2)
const schema = {
type: ['object', 'null'],
properties: {
foo: {
type: 'string'
}
}
}
const stringify = build(schema)
const data = { foo: 'bar' }
t.assert.equal(stringify(data), JSON.stringify(data))
t.assert.equal(stringify(null), JSON.stringify(null))
})
test('throw an error if the value doesn\'t match the type', (t) => {
t.plan(2)
const schema = {
type: 'object',
additionalProperties: false,
required: ['data'],
properties: {
data: {
type: 'array',
minItems: 1,
items: {
oneOf: [
{
type: 'string'
},
{
type: 'number'
}
]
}
}
}
}
const stringify = build(schema)
const validData = { data: [1, 'testing'] }
t.assert.equal(stringify(validData), JSON.stringify(validData))
const invalidData = { data: [false, 'testing'] }
t.assert.throws(() => stringify(invalidData))
})
test('nullable value in oneOf', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
data: {
oneOf: [
{
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer', minimum: 1 }
},
additionalProperties: false,
required: ['id']
}
},
{
type: 'array',
items: {
type: 'object',
properties: {
job: { type: 'string', nullable: true }
},
additionalProperties: false,
required: ['job']
}
}
]
}
},
required: ['data'],
additionalProperties: false
}
const stringify = build(schema)
const data = { data: [{ job: null }] }
t.assert.equal(stringify(data), JSON.stringify(data))
})

490
node_modules/fast-json-stringify/test/oneof.test.js generated vendored Normal file
View File

@@ -0,0 +1,490 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('object with multiple types field', (t) => {
t.plan(2)
const schema = {
title: 'object with multiple types field',
type: 'object',
properties: {
str: {
oneOf: [{
type: 'string'
}, {
type: 'boolean'
}]
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ str: 'string' }), '{"str":"string"}')
t.assert.equal(stringify({ str: true }), '{"str":true}')
})
test('object with field of type object or null', (t) => {
t.plan(2)
const schema = {
title: 'object with field of type object or null',
type: 'object',
properties: {
prop: {
oneOf: [{
type: 'object',
properties: {
str: {
type: 'string'
}
}
}, {
type: 'null'
}]
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ prop: null }), '{"prop":null}')
t.assert.equal(stringify({
prop: {
str: 'string', remove: 'this'
}
}), '{"prop":{"str":"string"}}')
})
test('object with field of type object or array', (t) => {
t.plan(2)
const schema = {
title: 'object with field of type object or array',
type: 'object',
properties: {
prop: {
oneOf: [{
type: 'object',
properties: {},
additionalProperties: true
}, {
type: 'array',
items: {
type: 'string'
}
}]
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({
prop: { str: 'string' }
}), '{"prop":{"str":"string"}}')
t.assert.equal(stringify({
prop: ['string']
}), '{"prop":["string"]}')
})
test('object with field of type string and coercion disable ', (t) => {
t.plan(1)
const schema = {
title: 'object with field of type string',
type: 'object',
properties: {
str: {
oneOf: [{
type: 'string'
}]
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ str: 1 }))
})
test('object with field of type string and coercion enable ', (t) => {
t.plan(1)
const schema = {
title: 'object with field of type string',
type: 'object',
properties: {
str: {
oneOf: [{
type: 'string'
}]
}
}
}
const options = {
ajv: {
coerceTypes: true
}
}
const stringify = build(schema, options)
const value = stringify({
str: 1
})
t.assert.equal(value, '{"str":"1"}')
})
test('object with field with type union of multiple objects', (t) => {
t.plan(2)
const schema = {
title: 'object with oneOf property value containing objects',
type: 'object',
properties: {
oneOfSchema: {
oneOf: [
{
type: 'object',
properties: {
baz: { type: 'number' }
},
required: ['baz']
},
{
type: 'object',
properties: {
bar: { type: 'string' }
},
required: ['bar']
}
]
}
},
required: ['oneOfSchema']
}
const stringify = build(schema)
t.assert.equal(stringify({ oneOfSchema: { baz: 5 } }), '{"oneOfSchema":{"baz":5}}')
t.assert.equal(stringify({ oneOfSchema: { bar: 'foo' } }), '{"oneOfSchema":{"bar":"foo"}}')
})
test('null value in schema', (t) => {
t.plan(0)
const schema = {
title: 'schema with null child',
type: 'string',
nullable: true,
enum: [null]
}
build(schema)
})
test('oneOf and $ref together', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
cs: {
oneOf: [
{
$ref: '#/definitions/Option'
},
{
type: 'boolean'
}
]
}
},
definitions: {
Option: {
type: 'string'
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ cs: 'franco' }), '{"cs":"franco"}')
t.assert.equal(stringify({ cs: true }), '{"cs":true}')
})
test('oneOf and $ref: 2 levels are fine', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
cs: {
oneOf: [
{
$ref: '#/definitions/Option'
},
{
type: 'boolean'
}
]
}
},
definitions: {
Option: {
oneOf: [
{
type: 'number'
},
{
type: 'boolean'
}
]
}
}
}
const stringify = build(schema)
const value = stringify({
cs: 3
})
t.assert.equal(value, '{"cs":3}')
})
test('oneOf and $ref: multiple levels should throw at build.', (t) => {
t.plan(3)
const schema = {
type: 'object',
properties: {
cs: {
oneOf: [
{
$ref: '#/definitions/Option'
},
{
type: 'boolean'
}
]
}
},
definitions: {
Option: {
oneOf: [
{
$ref: '#/definitions/Option2'
},
{
type: 'string'
}
]
},
Option2: {
type: 'number'
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ cs: 3 }), '{"cs":3}')
t.assert.equal(stringify({ cs: true }), '{"cs":true}')
t.assert.equal(stringify({ cs: 'pippo' }), '{"cs":"pippo"}')
})
test('oneOf and $ref - multiple external $ref', (t) => {
t.plan(2)
const externalSchema = {
external: {
definitions: {
def: {
type: 'object',
properties: {
prop: { oneOf: [{ $ref: 'external2#/definitions/other' }] }
}
}
}
},
external2: {
definitions: {
internal: {
type: 'string'
},
other: {
type: 'object',
properties: {
prop2: { $ref: '#/definitions/internal' }
}
}
}
}
}
const schema = {
title: 'object with $ref',
type: 'object',
properties: {
obj: {
$ref: 'external#/definitions/def'
}
}
}
const object = {
obj: {
prop: {
prop2: 'test'
}
}
}
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
t.assert.doesNotThrow(() => JSON.parse(output))
t.assert.equal(output, '{"obj":{"prop":{"prop2":"test"}}}')
})
test('oneOf with enum with more than 100 entries', (t) => {
t.plan(1)
const schema = {
title: 'type array that may have one of declared items',
type: 'array',
items: {
oneOf: [
{
type: 'string',
enum: ['EUR', 'USD', ...(new Set([...new Array(200)].map(() => Math.random().toString(36).substr(2, 3)))).values()]
},
{ type: 'null' }
]
}
}
const stringify = build(schema)
const value = stringify(['EUR', 'USD', null])
t.assert.equal(value, '["EUR","USD",null]')
})
test('oneOf object with field of type string with format or null', (t) => {
t.plan(1)
const toStringify = new Date()
const withOneOfSchema = {
type: 'object',
properties: {
prop: {
oneOf: [{
type: 'string',
format: 'date-time'
}, {
type: 'null'
}]
}
}
}
const withOneOfStringify = build(withOneOfSchema)
t.assert.equal(withOneOfStringify({
prop: toStringify
}), `{"prop":"${toStringify.toISOString()}"}`)
})
test('one array item match oneOf types', (t) => {
t.plan(3)
const schema = {
type: 'object',
additionalProperties: false,
required: ['data'],
properties: {
data: {
type: 'array',
minItems: 1,
items: {
oneOf: [
{
type: 'string'
},
{
type: 'number'
}
]
}
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ data: ['foo'] }), '{"data":["foo"]}')
t.assert.equal(stringify({ data: [1] }), '{"data":[1]}')
t.assert.throws(() => stringify({ data: [false, 'foo'] }))
})
test('some array items match oneOf types', (t) => {
t.plan(2)
const schema = {
type: 'object',
additionalProperties: false,
required: ['data'],
properties: {
data: {
type: 'array',
minItems: 1,
items: {
oneOf: [
{
type: 'string'
},
{
type: 'number'
}
]
}
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({ data: ['foo', 5] }), '{"data":["foo",5]}')
t.assert.throws(() => stringify({ data: [false, 'foo', true, 5] }))
})
test('all array items does not match oneOf types', (t) => {
t.plan(1)
const schema = {
type: 'object',
additionalProperties: false,
required: ['data'],
properties: {
data: {
type: 'array',
minItems: 1,
items: {
oneOf: [
{
type: 'string'
},
{
type: 'number'
}
]
}
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ data: [null, false, true, undefined, [], {}] }))
})

View File

@@ -0,0 +1,168 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('patternProperties', (t) => {
t.plan(1)
const stringify = build({
title: 'patternProperties',
type: 'object',
properties: {
str: {
type: 'string'
}
},
patternProperties: {
foo: {
type: 'string'
}
}
})
const obj = { str: 'test', foo: 42, ofoo: true, foof: 'string', objfoo: { a: true }, notMe: false }
t.assert.equal(stringify(obj), '{"str":"test","foo":"42","ofoo":"true","foof":"string","objfoo":"[object Object]"}')
})
test('patternProperties should not change properties', (t) => {
t.plan(1)
const stringify = build({
title: 'patternProperties should not change properties',
type: 'object',
properties: {
foo: {
type: 'string'
}
},
patternProperties: {
foo: {
type: 'number'
}
}
})
const obj = { foo: '42', ofoo: 42 }
t.assert.equal(stringify(obj), '{"foo":"42","ofoo":42}')
})
test('patternProperties - string coerce', (t) => {
t.plan(1)
const stringify = build({
title: 'check string coerce',
type: 'object',
properties: {},
patternProperties: {
foo: {
type: 'string'
}
}
})
const obj = { foo: true, ofoo: 42, arrfoo: ['array', 'test'], objfoo: { a: 'world' } }
t.assert.equal(stringify(obj), '{"foo":"true","ofoo":"42","arrfoo":"array,test","objfoo":"[object Object]"}')
})
test('patternProperties - number coerce', (t) => {
t.plan(2)
const stringify = build({
title: 'check number coerce',
type: 'object',
properties: {},
patternProperties: {
foo: {
type: 'number'
}
}
})
const coercibleValues = { foo: true, ofoo: '42' }
t.assert.equal(stringify(coercibleValues), '{"foo":1,"ofoo":42}')
const incoercibleValues = { xfoo: 'string', arrfoo: [1, 2], objfoo: { num: 42 } }
try {
stringify(incoercibleValues)
t.fail('should throw an error')
} catch (err) {
t.assert.ok(err)
}
})
test('patternProperties - boolean coerce', (t) => {
t.plan(1)
const stringify = build({
title: 'check boolean coerce',
type: 'object',
properties: {},
patternProperties: {
foo: {
type: 'boolean'
}
}
})
const obj = { foo: 'true', ofoo: 0, arrfoo: [1, 2], objfoo: { a: true } }
t.assert.equal(stringify(obj), '{"foo":true,"ofoo":false,"arrfoo":true,"objfoo":true}')
})
test('patternProperties - object coerce', (t) => {
t.plan(1)
const stringify = build({
title: 'check object coerce',
type: 'object',
properties: {},
patternProperties: {
foo: {
type: 'object',
properties: {
answer: {
type: 'number'
}
}
}
}
})
const obj = { objfoo: { answer: 42 } }
t.assert.equal(stringify(obj), '{"objfoo":{"answer":42}}')
})
test('patternProperties - array coerce', (t) => {
t.plan(2)
const stringify = build({
title: 'check array coerce',
type: 'object',
properties: {},
patternProperties: {
foo: {
type: 'array',
items: {
type: 'string'
}
}
}
})
const coercibleValues = { arrfoo: [1, 2] }
t.assert.equal(stringify(coercibleValues), '{"arrfoo":["1","2"]}')
const incoercibleValues = { foo: 'true', ofoo: 0, objfoo: { tyrion: 'lannister' } }
t.assert.throws(() => stringify(incoercibleValues))
})
test('patternProperties - fail on invalid regex, handled by ajv', (t) => {
t.plan(1)
t.assert.throws(() => build({
title: 'check array coerce',
type: 'object',
properties: {},
patternProperties: {
'foo/\\': {
type: 'array',
items: {
type: 'string'
}
}
}
}), new Error('schema is invalid: data/patternProperties must match format "regex"'))
})

245
node_modules/fast-json-stringify/test/recursion.test.js generated vendored Normal file
View File

@@ -0,0 +1,245 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('can stringify recursive directory tree (issue #181)', (t) => {
t.plan(1)
const schema = {
definitions: {
directory: {
type: 'object',
properties: {
name: { type: 'string' },
subDirectories: {
type: 'array',
items: { $ref: '#/definitions/directory' },
default: []
}
}
}
},
type: 'array',
items: { $ref: '#/definitions/directory' }
}
const stringify = build(schema)
t.assert.equal(stringify([
{ name: 'directory 1', subDirectories: [] },
{
name: 'directory 2',
subDirectories: [
{ name: 'directory 2.1', subDirectories: [] },
{ name: 'directory 2.2', subDirectories: [] }
]
}
]), '[{"name":"directory 1","subDirectories":[]},{"name":"directory 2","subDirectories":[{"name":"directory 2.1","subDirectories":[]},{"name":"directory 2.2","subDirectories":[]}]}]')
})
test('can stringify when recursion in external schema', t => {
t.plan(1)
const referenceSchema = {
$id: 'person',
type: 'object',
properties: {
name: { type: 'string' },
children: {
type: 'array',
items: { $ref: '#' }
}
}
}
const schema = {
$id: 'mainSchema',
type: 'object',
properties: {
people: {
$ref: 'person'
}
}
}
const stringify = build(schema, {
schema: {
[referenceSchema.$id]: referenceSchema
}
})
const value = stringify({ people: { name: 'Elizabeth', children: [{ name: 'Charles' }] } })
t.assert.equal(value, '{"people":{"name":"Elizabeth","children":[{"name":"Charles"}]}}')
})
test('use proper serialize function', t => {
t.plan(1)
const personSchema = {
$id: 'person',
type: 'object',
properties: {
name: { type: 'string' },
children: {
type: 'array',
items: { $ref: '#' }
}
}
}
const directorySchema = {
$id: 'directory',
type: 'object',
properties: {
name: { type: 'string' },
subDirectories: {
type: 'array',
items: { $ref: '#' },
default: []
}
}
}
const schema = {
$id: 'mainSchema',
type: 'object',
properties: {
people: { $ref: 'person' },
directory: { $ref: 'directory' }
}
}
const stringify = build(schema, {
schema: {
[personSchema.$id]: personSchema,
[directorySchema.$id]: directorySchema
}
})
const value = stringify({
people: {
name: 'Elizabeth',
children: [{
name: 'Charles',
children: [{ name: 'William', children: [{ name: 'George' }, { name: 'Charlotte' }] }, { name: 'Harry' }]
}]
},
directory: {
name: 'directory 1',
subDirectories: [
{ name: 'directory 1.1', subDirectories: [] },
{
name: 'directory 1.2',
subDirectories: [{ name: 'directory 1.2.1' }, { name: 'directory 1.2.2' }]
}
]
}
})
t.assert.equal(value, '{"people":{"name":"Elizabeth","children":[{"name":"Charles","children":[{"name":"William","children":[{"name":"George"},{"name":"Charlotte"}]},{"name":"Harry"}]}]},"directory":{"name":"directory 1","subDirectories":[{"name":"directory 1.1","subDirectories":[]},{"name":"directory 1.2","subDirectories":[{"name":"directory 1.2.1","subDirectories":[]},{"name":"directory 1.2.2","subDirectories":[]}]}]}}')
})
test('can stringify recursive references in object types (issue #365)', t => {
t.plan(1)
const schema = {
type: 'object',
definitions: {
parentCategory: {
type: 'object',
properties: {
parent: {
$ref: '#/definitions/parentCategory'
}
}
}
},
properties: {
category: {
type: 'object',
properties: {
parent: {
$ref: '#/definitions/parentCategory'
}
}
}
}
}
const stringify = build(schema)
const data = {
category: {
parent: {
parent: {
parent: {
parent: {}
}
}
}
}
}
const value = stringify(data)
t.assert.equal(value, '{"category":{"parent":{"parent":{"parent":{"parent":{}}}}}}')
})
test('can stringify recursive inline $id references (issue #410)', t => {
t.plan(1)
const schema = {
$id: 'Node',
type: 'object',
properties: {
id: {
type: 'string'
},
nodes: {
type: 'array',
items: {
$ref: 'Node'
}
}
},
required: [
'id',
'nodes'
]
}
const stringify = build(schema)
const data = {
id: '0',
nodes: [
{
id: '1',
nodes: [{
id: '2',
nodes: [
{ id: '3', nodes: [] },
{ id: '4', nodes: [] },
{ id: '5', nodes: [] }
]
}]
},
{
id: '6',
nodes: [{
id: '7',
nodes: [
{ id: '8', nodes: [] },
{ id: '9', nodes: [] },
{ id: '10', nodes: [] }
]
}]
},
{
id: '11',
nodes: [{
id: '12',
nodes: [
{ id: '13', nodes: [] },
{ id: '14', nodes: [] },
{ id: '15', nodes: [] }
]
}]
}
]
}
const value = stringify(data)
t.assert.equal(value, '{"id":"0","nodes":[{"id":"1","nodes":[{"id":"2","nodes":[{"id":"3","nodes":[]},{"id":"4","nodes":[]},{"id":"5","nodes":[]}]}]},{"id":"6","nodes":[{"id":"7","nodes":[{"id":"8","nodes":[]},{"id":"9","nodes":[]},{"id":"10","nodes":[]}]}]},{"id":"11","nodes":[{"id":"12","nodes":[{"id":"13","nodes":[]},{"id":"14","nodes":[]},{"id":"15","nodes":[]}]}]}]}')
})

12
node_modules/fast-json-stringify/test/ref.json generated vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"definitions": {
"def": {
"type": "object",
"properties": {
"str": {
"type": "string"
}
}
}
}
}

2046
node_modules/fast-json-stringify/test/ref.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

32
node_modules/fast-json-stringify/test/regex.test.js generated vendored Normal file
View File

@@ -0,0 +1,32 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
test('object with RexExp', (t) => {
t.plan(3)
const schema = {
title: 'object with RegExp',
type: 'object',
properties: {
reg: {
type: 'string'
}
}
}
const obj = {
reg: /"([^"]|\\")*"/
}
const stringify = build(schema)
const validate = validator(schema)
const output = stringify(obj)
t.assert.doesNotThrow(() => JSON.parse(output))
t.assert.equal(obj.reg.source, new RegExp(JSON.parse(output).reg).source)
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})

204
node_modules/fast-json-stringify/test/required.test.js generated vendored Normal file
View File

@@ -0,0 +1,204 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('object with required field', (t) => {
t.plan(2)
const schema = {
title: 'object with required field',
type: 'object',
properties: {
str: {
type: 'string'
},
num: {
type: 'integer'
}
},
required: ['str']
}
const stringify = build(schema)
t.assert.doesNotThrow(() => {
stringify({
str: 'string'
})
})
t.assert.throws(() => {
stringify({
num: 42
})
}, { message: '"str" is required!' })
})
test('object with required field not in properties schema', (t) => {
t.plan(2)
const schema = {
title: 'object with required field',
type: 'object',
properties: {
num: {
type: 'integer'
}
},
required: ['str']
}
const stringify = build(schema)
t.assert.throws(() => {
stringify({})
}, { message: '"str" is required!' })
t.assert.throws(() => {
stringify({
num: 42
})
}, { message: '"str" is required!' })
})
test('object with required field not in properties schema with additional properties true', (t) => {
t.plan(2)
const schema = {
title: 'object with required field',
type: 'object',
properties: {
num: {
type: 'integer'
}
},
additionalProperties: true,
required: ['str']
}
const stringify = build(schema)
t.assert.throws(() => {
stringify({})
}, { message: '"str" is required!' })
t.assert.throws(() => {
stringify({
num: 42
})
}, { message: '"str" is required!' })
})
test('object with multiple required field not in properties schema', (t) => {
t.plan(3)
const schema = {
title: 'object with required field',
type: 'object',
properties: {
num: {
type: 'integer'
}
},
additionalProperties: true,
required: ['num', 'key1', 'key2']
}
const stringify = build(schema)
t.assert.throws(() => {
stringify({})
}, { message: '"key1" is required!' })
t.assert.throws(() => {
stringify({
key1: 42,
key2: 42
})
}, { message: '"num" is required!' })
t.assert.throws(() => {
stringify({
num: 42,
key1: 'some'
})
}, { message: '"key2" is required!' })
})
test('object with required bool', (t) => {
t.plan(2)
const schema = {
title: 'object with required field',
type: 'object',
properties: {
num: {
type: 'integer'
}
},
additionalProperties: true,
required: ['bool']
}
const stringify = build(schema)
t.assert.throws(() => {
stringify({})
}, { message: '"bool" is required!' })
t.assert.doesNotThrow(() => {
stringify({
bool: false
})
})
})
test('required nullable', (t) => {
t.plan(1)
const schema = {
title: 'object with required field',
type: 'object',
properties: {
num: {
type: ['integer']
}
},
additionalProperties: true,
required: ['null']
}
const stringify = build(schema)
t.assert.doesNotThrow(() => {
stringify({
null: null
})
})
})
test('required numbers', (t) => {
t.plan(2)
const schema = {
title: 'object with required field',
type: 'object',
properties: {
str: {
type: 'string'
},
num: {
type: 'integer'
}
},
required: ['num']
}
const stringify = build(schema)
t.assert.doesNotThrow(() => {
stringify({
num: 42
})
})
t.assert.throws(() => {
stringify({
num: 'aaa'
})
}, { message: 'The value "aaa" cannot be converted to an integer.' })
})

View File

@@ -0,0 +1,50 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('nested ref requires ajv', async t => {
t.test('nested ref requires ajv', async t => {
const schemaA = {
$id: 'urn:schema:a',
definitions: {
foo: { anyOf: [{ type: 'string' }, { type: 'null' }] }
}
}
const schemaB = {
$id: 'urn:schema:b',
type: 'object',
properties: {
results: {
type: 'object',
properties: {
items: {
type: 'object',
properties: {
bar: {
type: 'array',
items: { $ref: 'urn:schema:a#/definitions/foo' }
}
}
}
}
}
}
}
const stringify = build(schemaB, {
schema: {
[schemaA.$id]: schemaA
}
})
const result = stringify({
results: {
items: {
bar: ['baz']
}
}
})
t.assert.equal(result, '{"results":{"items":{"bar":["baz"]}}}')
})
})

141
node_modules/fast-json-stringify/test/sanitize.test.js generated vendored Normal file
View File

@@ -0,0 +1,141 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
const stringify = build({
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'Age in years"',
type: 'integer'
},
[(() => "phra'&& process.exit(1) ||'phra")()]: {},
now: {
type: 'string'
},
reg: {
type: 'string',
default: 'a\'&& process.exit(1) ||\''
},
obj: {
type: 'object',
properties: {
bool: {
type: 'boolean'
}
}
},
'"\'w00t': {
type: 'string',
default: '"\'w00t'
},
arr: {
type: 'array',
items: {
type: 'object',
properties: {
'phra\' && process.exit(1)//': {
type: 'number'
},
str: {
type: 'string'
}
}
}
}
},
required: ['now'],
patternProperties: {
'.*foo$': {
type: 'string'
},
test: {
type: 'number'
},
'phra\'/ && process.exit(1) && /\'': {
type: 'number'
},
'"\'w00t.*////': {
type: 'number'
}
},
additionalProperties: {
type: 'string'
}
})
const obj = {
firstName: 'Matteo',
lastName: 'Collina',
age: 32,
now: new Date(),
foo: 'hello"',
bar: "world'",
'fuzz"': 42,
"me'": 42,
numfoo: 42,
test: 42,
strtest: '23',
arr: [{ 'phra\' && process.exit(1)//': 42 }],
obj: { bool: true },
notmatch: 'valar morghulis',
notmatchobj: { a: true },
notmatchnum: 42
}
test('sanitize', t => {
const json = stringify(obj)
t.assert.doesNotThrow(() => JSON.parse(json))
const stringify2 = build({
title: 'Example Schema',
type: 'object',
patternProperties: {
'"\'w00t.*////': {
type: 'number'
}
}
})
t.assert.deepStrictEqual(JSON.parse(stringify2({
'"\'phra////': 42,
asd: 42
})), {
})
const stringify3 = build({
title: 'Example Schema',
type: 'object',
properties: {
"\"phra\\'&&(console.log(42))//||'phra": {}
}
})
// this verifies the escaping
JSON.parse(stringify3({
'"phra\'&&(console.log(42))//||\'phra': 42
}))
const stringify4 = build({
title: 'Example Schema',
type: 'object',
properties: {
'"\\\\\\\\\'w00t': {
type: 'string',
default: '"\'w00t'
}
}
})
t.assert.deepStrictEqual(JSON.parse(stringify4({})), {
'"\\\\\\\\\'w00t': '"\'w00t'
})
})

View File

@@ -0,0 +1,18 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('sanitize 2', t => {
const payload = '(throw "pwoned")'
const stringify = build({
properties: {
[`*///\\\\\\']);${payload};{/*`]: {
type: 'number'
}
}
})
t.assert.doesNotThrow(() => stringify({}))
})

View File

@@ -0,0 +1,17 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('sanitize 3', t => {
t.assert.throws(() => {
build({
$defs: {
type: 'foooo"bar'
},
patternProperties: {
x: { $ref: '#/$defs' }
}
})
}, { message: 'foooo"bar unsupported' })
})

View File

@@ -0,0 +1,16 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('sanitize 4', t => {
const payload = '(throw "pwoned")'
const stringify = build({
required: [`"];${payload}//`]
})
t.assert.throws(() => {
stringify({})
}, { message: '""];(throw "pwoned")//" is required!' })
})

View File

@@ -0,0 +1,16 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('sanitize 5', t => {
const payload = '(throw "pwoned")'
t.assert.throws(() => {
build({
patternProperties: {
'*': { type: `*/${payload}){//` }
}
})
}, { message: 'schema is invalid: data/patternProperties must match format "regex"' })
})

View File

@@ -0,0 +1,22 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('sanitize 6', t => {
const payload = '(throw "pwoned")'
const stringify = build({
type: 'object',
properties: {
'/*': { type: 'object' },
x: {
type: 'object',
properties: {
a: { type: 'string', default: `*/}${payload};{//` }
}
}
}
})
t.assert.doesNotThrow(() => { stringify({}) })
})

View File

@@ -0,0 +1,68 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('required property containing single quote, contains property', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
'\'': { type: 'string' }
},
required: [
'\''
]
})
t.assert.throws(() => stringify({}), new Error('"\'" is required!'))
})
test('required property containing double quote, contains property', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
'"': { type: 'string' }
},
required: [
'"'
]
})
t.assert.throws(() => stringify({}), new Error('""" is required!'))
})
test('required property containing single quote, does not contain property', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
a: { type: 'string' }
},
required: [
'\''
]
})
t.assert.throws(() => stringify({}), new Error('"\'" is required!'))
})
test('required property containing double quote, does not contain property', (t) => {
t.plan(1)
const stringify = build({
type: 'object',
properties: {
a: { type: 'string' }
},
required: [
'"'
]
})
t.assert.throws(() => stringify({}), new Error('""" is required!'))
})

View File

@@ -0,0 +1,196 @@
'use strict'
const { test } = require('node:test')
const clone = require('rfdc/default')
const build = require('..')
test('oneOf with $ref should not change the input schema', t => {
t.plan(2)
const referenceSchema = {
$id: 'externalId',
type: 'object',
properties: {
name: { type: 'string' }
}
}
const schema = {
$id: 'mainSchema',
type: 'object',
properties: {
people: {
oneOf: [{ $ref: 'externalId' }]
}
}
}
const clonedSchema = clone(schema)
const stringify = build(schema, {
schema: {
[referenceSchema.$id]: referenceSchema
}
})
const value = stringify({ people: { name: 'hello', foo: 'bar' } })
t.assert.equal(value, '{"people":{"name":"hello"}}')
t.assert.deepStrictEqual(schema, clonedSchema)
})
test('oneOf and anyOf with $ref should not change the input schema', t => {
t.plan(3)
const referenceSchema = {
$id: 'externalSchema',
type: 'object',
properties: {
name: { type: 'string' }
}
}
const schema = {
$id: 'rootSchema',
type: 'object',
properties: {
people: {
oneOf: [{ $ref: 'externalSchema' }]
},
love: {
anyOf: [
{ $ref: '#/definitions/foo' },
{ type: 'boolean' }
]
}
},
definitions: {
foo: { type: 'string' }
}
}
const clonedSchema = clone(schema)
const stringify = build(schema, {
schema: {
[referenceSchema.$id]: referenceSchema
}
})
const valueAny1 = stringify({ people: { name: 'hello', foo: 'bar' }, love: 'music' })
const valueAny2 = stringify({ people: { name: 'hello', foo: 'bar' }, love: true })
t.assert.equal(valueAny1, '{"people":{"name":"hello"},"love":"music"}')
t.assert.equal(valueAny2, '{"people":{"name":"hello"},"love":true}')
t.assert.deepStrictEqual(schema, clonedSchema)
})
test('multiple $ref tree', t => {
t.plan(2)
const referenceDeepSchema = {
$id: 'deepId',
type: 'number'
}
const referenceSchema = {
$id: 'externalId',
type: 'object',
properties: {
name: { $ref: '#/definitions/foo' },
age: { $ref: 'deepId' }
},
definitions: {
foo: { type: 'string' }
}
}
const schema = {
$id: 'mainSchema',
type: 'object',
properties: {
people: {
oneOf: [{ $ref: 'externalId' }]
}
}
}
const clonedSchema = clone(schema)
const stringify = build(schema, {
schema: {
[referenceDeepSchema.$id]: referenceDeepSchema,
[referenceSchema.$id]: referenceSchema
}
})
const value = stringify({ people: { name: 'hello', foo: 'bar', age: 42 } })
t.assert.equal(value, '{"people":{"name":"hello","age":42}}')
t.assert.deepStrictEqual(schema, clonedSchema)
})
test('must not mutate items $ref', t => {
t.plan(2)
const referenceSchema = {
$id: 'ShowSchema',
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
const schema = {
$id: 'ListSchema',
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'array',
items: {
$ref: 'ShowSchema#'
}
}
const clonedSchema = clone(schema)
const stringify = build(schema, {
schema: {
[referenceSchema.$id]: referenceSchema
}
})
const value = stringify([{ name: 'foo' }])
t.assert.equal(value, '[{"name":"foo"}]')
t.assert.deepStrictEqual(schema, clonedSchema)
})
test('must not mutate items referred by $ref', t => {
t.plan(2)
const firstSchema = {
$id: 'example1',
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
const reusedSchema = {
$id: 'example2',
type: 'object',
properties: {
name: {
oneOf: [
{
$ref: 'example1'
}
]
}
}
}
const clonedSchema = clone(firstSchema)
const stringify = build(reusedSchema, {
schema: {
[firstSchema.$id]: firstSchema
}
})
const value = stringify({ name: { name: 'foo' } })
t.assert.equal(value, '{"name":{"name":"foo"}}')
t.assert.deepStrictEqual(firstSchema, clonedSchema)
})

View File

@@ -0,0 +1,219 @@
'use strict'
const { test, after } = require('node:test')
const fjs = require('..')
const fs = require('fs')
const path = require('path')
function build (opts, schema) {
return fjs(schema || {
title: 'default string',
type: 'object',
properties: {
firstName: {
type: 'string'
}
},
required: ['firstName']
}, opts)
}
const tmpDir = 'test/fixtures'
test('activate standalone mode', async (t) => {
t.plan(3)
after(async () => {
await fs.promises.rm(destination, { force: true })
})
const code = build({ mode: 'standalone' })
t.assert.ok(typeof code === 'string')
t.assert.equal(code.indexOf('ajv'), -1)
const destination = path.resolve(tmpDir, 'standalone.js')
await fs.promises.writeFile(destination, code)
const standalone = require(destination)
t.assert.equal(standalone({ firstName: 'Foo', surname: 'bar' }), JSON.stringify({ firstName: 'Foo' }), 'surname evicted')
})
test('test ajv schema', async (t) => {
t.plan(3)
after(async () => {
await fs.promises.rm(destination, { force: true })
})
const code = build({ mode: 'standalone' }, {
type: 'object',
properties: {
},
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] }
}
},
then: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] },
foo: { type: 'string' },
bar: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
},
else: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['greeting'] },
hi: { type: 'string' },
hello: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
}
})
t.assert.ok(typeof code === 'string')
t.assert.equal(code.indexOf('ajv') > 0, true)
const destination = path.resolve(tmpDir, 'standalone2.js')
await fs.promises.writeFile(destination, code)
const standalone = require(destination)
t.assert.equal(standalone({
kind: 'foobar',
foo: 'FOO',
list: [{
name: 'name',
value: 'foo'
}],
bar: 42,
hi: 'HI',
hello: 45,
a: 'A',
b: 35
}), JSON.stringify({
kind: 'foobar',
foo: 'FOO',
bar: 42,
list: [{
name: 'name',
value: 'foo'
}]
}))
})
test('no need to keep external schemas once compiled', async (t) => {
t.plan(1)
after(async () => {
await fs.promises.rm(destination, { force: true })
})
const externalSchema = {
first: {
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
}
}
}
}
const code = fjs({
$ref: 'first#/definitions/id1'
}, {
mode: 'standalone',
schema: externalSchema
})
const destination = path.resolve(tmpDir, 'standalone3.js')
await fs.promises.writeFile(destination, code)
const standalone = require(destination)
t.assert.equal(standalone({ id1: 5 }), JSON.stringify({ id1: 5 }), 'serialization works with external schemas')
})
test('no need to keep external schemas once compiled - with oneOf validator', async (t) => {
t.plan(2)
after(async () => {
await fs.promises.rm(destination, { force: true })
})
const externalSchema = {
ext: {
definitions: {
oBaz: {
type: 'object',
properties: {
baz: { type: 'number' }
},
required: ['baz']
},
oBar: {
type: 'object',
properties: {
bar: { type: 'string' }
},
required: ['bar']
},
other: {
type: 'string',
const: 'other'
}
}
}
}
const schema = {
title: 'object with oneOf property value containing refs to external schema',
type: 'object',
properties: {
oneOfSchema: {
oneOf: [
{ $ref: 'ext#/definitions/oBaz' },
{ $ref: 'ext#/definitions/oBar' }
]
}
},
required: ['oneOfSchema']
}
const code = fjs(schema, {
mode: 'standalone',
schema: externalSchema
})
const destination = path.resolve(tmpDir, 'standalone-oneOf-ref.js')
await fs.promises.writeFile(destination, code)
const stringify = require(destination)
t.assert.equal(stringify({ oneOfSchema: { baz: 5 } }), '{"oneOfSchema":{"baz":5}}')
t.assert.equal(stringify({ oneOfSchema: { bar: 'foo' } }), '{"oneOfSchema":{"bar":"foo"}}')
})

84
node_modules/fast-json-stringify/test/string.test.js generated vendored Normal file
View File

@@ -0,0 +1,84 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('serialize short string', (t) => {
t.plan(2)
const schema = {
type: 'string'
}
const input = 'abcd'
const stringify = build(schema)
const output = stringify(input)
t.assert.equal(output, '"abcd"')
t.assert.equal(JSON.parse(output), input)
})
test('serialize short string', (t) => {
t.plan(2)
const schema = {
type: 'string'
}
const input = '\x00'
const stringify = build(schema)
const output = stringify(input)
t.assert.equal(output, '"\\u0000"')
t.assert.equal(JSON.parse(output), input)
})
test('serialize long string', (t) => {
t.plan(2)
const schema = {
type: 'string'
}
const input = new Array(2e4).fill('\x00').join('')
const stringify = build(schema)
const output = stringify(input)
t.assert.equal(output, `"${new Array(2e4).fill('\\u0000').join('')}"`)
t.assert.equal(JSON.parse(output), input)
})
test('unsafe string', (t) => {
t.plan(2)
const schema = {
type: 'string',
format: 'unsafe'
}
const input = 'abcd'
const stringify = build(schema)
const output = stringify(input)
t.assert.equal(output, `"${input}"`)
t.assert.equal(JSON.parse(output), input)
})
test('unsafe unescaped string', (t) => {
t.plan(2)
const schema = {
type: 'string',
format: 'unsafe'
}
const input = 'abcd "abcd"'
const stringify = build(schema)
const output = stringify(input)
t.assert.equal(output, `"${input}"`)
t.assert.throws(function () {
JSON.parse(output)
})
})

View File

@@ -0,0 +1,67 @@
'use strict'
const { test } = require('node:test')
const validator = require('is-my-json-valid')
const build = require('..')
test('render a string with surrogate pairs as JSON:test 1', (t) => {
t.plan(2)
const schema = {
title: 'surrogate',
type: 'string'
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify('𝌆')
t.assert.equal(output, '"𝌆"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a string with surrogate pairs as JSON: test 2', (t) => {
t.plan(2)
const schema = {
title: 'long',
type: 'string'
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify('\uD834\uDF06')
t.assert.equal(output, '"𝌆"')
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a string with Unpaired surrogate code as JSON', (t) => {
t.plan(2)
const schema = {
title: 'surrogate',
type: 'string'
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify('\uDF06\uD834')
t.assert.equal(output, JSON.stringify('\uDF06\uD834'))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})
test('render a string with lone surrogate code as JSON', (t) => {
t.plan(2)
const schema = {
title: 'surrogate',
type: 'string'
}
const validate = validator(schema)
const stringify = build(schema)
const output = stringify('\uDEAD')
t.assert.equal(output, JSON.stringify('\uDEAD'))
t.assert.ok(validate(JSON.parse(output)), 'valid schema')
})

203
node_modules/fast-json-stringify/test/toJSON.test.js generated vendored Normal file
View File

@@ -0,0 +1,203 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('use toJSON method on object types', (t) => {
t.plan(1)
const stringify = build({
title: 'simple object',
type: 'object',
properties: {
productName: {
type: 'string'
}
}
})
const object = {
product: { name: 'cola' },
toJSON: function () {
return { productName: this.product.name }
}
}
t.assert.equal('{"productName":"cola"}', stringify(object))
})
test('use toJSON method on nested object types', (t) => {
t.plan(1)
const stringify = build({
title: 'simple array',
type: 'array',
items: {
type: 'object',
properties: {
productName: {
type: 'string'
}
}
}
})
const array = [
{
product: { name: 'cola' },
toJSON: function () {
return { productName: this.product.name }
}
},
{
product: { name: 'sprite' },
toJSON: function () {
return { productName: this.product.name }
}
}
]
t.assert.equal('[{"productName":"cola"},{"productName":"sprite"}]', stringify(array))
})
test('not use toJSON if does not exist', (t) => {
t.plan(1)
const stringify = build({
title: 'simple object',
type: 'object',
properties: {
product: {
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}
})
const object = {
product: { name: 'cola' }
}
t.assert.equal('{"product":{"name":"cola"}}', stringify(object))
})
test('not fail on null object declared nullable', (t) => {
t.plan(1)
const stringify = build({
title: 'simple object',
type: 'object',
nullable: true,
properties: {
product: {
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}
})
t.assert.equal('null', stringify(null))
})
test('not fail on null sub-object declared nullable', (t) => {
t.plan(1)
const stringify = build({
title: 'simple object',
type: 'object',
properties: {
product: {
nullable: true,
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}
})
const object = {
product: null
}
t.assert.equal('{"product":null}', stringify(object))
})
test('on non nullable null sub-object it should coerce to {}', (t) => {
t.plan(1)
const stringify = build({
title: 'simple object',
type: 'object',
properties: {
product: {
nullable: false,
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}
})
const object = {
product: null
}
const result = stringify(object)
t.assert.equal(result, JSON.stringify({ product: {} }))
})
test('on non nullable null object it should coerce to {}', (t) => {
t.plan(1)
const stringify = build({
title: 'simple object',
nullable: false,
type: 'object',
properties: {
product: {
nullable: false,
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
}
})
const result = stringify(null)
t.assert.equal(result, '{}')
})
test('on non-nullable null object it should skip rendering, skipping required fields checks', (t) => {
t.plan(1)
const stringify = build({
title: 'simple object',
nullable: false,
type: 'object',
properties: {
product: {
nullable: false,
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
},
required: ['product']
})
const result = stringify(null)
t.assert.equal(result, '{}')
})

36
node_modules/fast-json-stringify/test/typebox.test.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('nested object in pattern properties for typebox', (t) => {
const { Type } = require('@sinclair/typebox')
t.plan(1)
const nestedSchema = Type.Object({
nestedKey1: Type.String()
})
const RootSchema = Type.Object({
key1: Type.Record(Type.String(), nestedSchema),
key2: Type.Record(Type.String(), nestedSchema)
})
const schema = RootSchema
const stringify = build(schema)
const value = stringify({
key1: {
nestedKey: {
nestedKey1: 'value1'
}
},
key2: {
nestedKey: {
nestedKey1: 'value2'
}
}
})
t.assert.equal(value, '{"key1":{"nestedKey":{"nestedKey1":"value1"}},"key2":{"nestedKey":{"nestedKey1":"value2"}}}')
})

View File

@@ -0,0 +1,550 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('possibly nullable integer primitive alternative', (t) => {
t.plan(1)
const schema = {
title: 'simple object with multi-type nullable primitive',
type: 'object',
properties: {
data: {
type: ['integer']
}
}
}
const stringify = build(schema, { ajv: { allowUnionTypes: true } })
const value = stringify({
data: 4
})
t.assert.equal(value, '{"data":4}')
})
test('possibly nullable number primitive alternative', (t) => {
t.plan(1)
const schema = {
title: 'simple object with multi-type nullable primitive',
type: 'object',
properties: {
data: {
type: ['number']
}
}
}
const stringify = build(schema)
const value = stringify({
data: 4
})
t.assert.equal(value, '{"data":4}')
})
test('possibly nullable integer primitive alternative with null value', (t) => {
t.plan(1)
const schema = {
title: 'simple object with multi-type nullable primitive',
type: 'object',
properties: {
data: {
type: ['integer']
}
}
}
const stringify = build(schema)
const value = stringify({
data: null
})
t.assert.equal(value, '{"data":0}')
})
test('possibly nullable number primitive alternative with null value', (t) => {
t.plan(1)
const schema = {
title: 'simple object with multi-type nullable primitive',
type: 'object',
properties: {
data: {
type: ['number']
}
}
}
const stringify = build(schema)
const value = stringify({
data: null
})
t.assert.equal(value, '{"data":0}')
})
test('possibly nullable number primitive alternative with null value', (t) => {
t.plan(1)
const schema = {
title: 'simple object with multi-type nullable primitive',
type: 'object',
properties: {
data: {
type: ['boolean']
}
}
}
const stringify = build(schema)
const value = stringify({
data: null
})
t.assert.equal(value, '{"data":false}')
})
test('nullable integer primitive', (t) => {
t.plan(1)
const schema = {
title: 'simple object with nullable primitive',
type: 'object',
properties: {
data: {
type: ['integer', 'null']
}
}
}
const stringify = build(schema)
const value = stringify({
data: 4
})
t.assert.equal(value, '{"data":4}')
})
test('nullable number primitive', (t) => {
t.plan(1)
const schema = {
title: 'simple object with nullable primitive',
type: 'object',
properties: {
data: {
type: ['number', 'null']
}
}
}
const stringify = build(schema)
const value = stringify({
data: 4
})
t.assert.equal(value, '{"data":4}')
})
test('nullable primitive with null value', (t) => {
t.plan(1)
const schema = {
title: 'simple object with nullable primitive',
type: 'object',
properties: {
data: {
type: ['integer', 'null']
}
}
}
const stringify = build(schema)
const value = stringify({
data: null
})
t.assert.equal(value, '{"data":null}')
})
test('nullable number primitive with null value', (t) => {
t.plan(1)
const schema = {
title: 'simple object with nullable primitive',
type: 'object',
properties: {
data: {
type: ['number', 'null']
}
}
}
const stringify = build(schema)
const value = stringify({
data: null
})
t.assert.equal(value, '{"data":null}')
})
test('possibly null object with multi-type property', (t) => {
t.plan(3)
const schema = {
title: 'simple object with multi-type property',
type: 'object',
properties: {
objectOrNull: {
type: ['object', 'null'],
properties: {
stringOrNumber: {
type: ['string', 'number']
}
}
}
}
}
const stringify = build(schema)
t.assert.equal(stringify({
objectOrNull: {
stringOrNumber: 'string'
}
}), '{"objectOrNull":{"stringOrNumber":"string"}}')
t.assert.equal(stringify({
objectOrNull: {
stringOrNumber: 42
}
}), '{"objectOrNull":{"stringOrNumber":42}}')
t.assert.equal(stringify({
objectOrNull: null
}), '{"objectOrNull":null}')
})
test('object with possibly null array of multiple types', (t) => {
t.plan(5)
const schema = {
title: 'object with array of multiple types',
type: 'object',
properties: {
arrayOfStringsAndNumbers: {
type: ['array', 'null'],
items: {
type: ['string', 'number', 'null']
}
}
}
}
const stringify = build(schema)
try {
const value = stringify({
arrayOfStringsAndNumbers: null
})
t.assert.equal(value, '{"arrayOfStringsAndNumbers":null}')
} catch (e) {
console.log(e)
t.fail()
}
try {
const value = stringify({
arrayOfStringsAndNumbers: ['string1', 'string2']
})
t.assert.equal(value, '{"arrayOfStringsAndNumbers":["string1","string2"]}')
} catch (e) {
console.log(e)
t.fail()
}
t.assert.equal(stringify({
arrayOfStringsAndNumbers: [42, 7]
}), '{"arrayOfStringsAndNumbers":[42,7]}')
t.assert.equal(stringify({
arrayOfStringsAndNumbers: ['string1', 42, 7, 'string2']
}), '{"arrayOfStringsAndNumbers":["string1",42,7,"string2"]}')
t.assert.equal(stringify({
arrayOfStringsAndNumbers: ['string1', null, 42, 7, 'string2', null]
}), '{"arrayOfStringsAndNumbers":["string1",null,42,7,"string2",null]}')
})
test('object with tuple of multiple types', (t) => {
t.plan(2)
const schema = {
title: 'object with array of multiple types',
type: 'object',
properties: {
fixedTupleOfStringsAndNumbers: {
type: 'array',
items: [
{
type: 'string'
},
{
type: 'number'
},
{
type: ['string', 'number']
}
]
}
}
}
const stringify = build(schema)
try {
const value = stringify({
fixedTupleOfStringsAndNumbers: ['string1', 42, 7]
})
t.assert.equal(value, '{"fixedTupleOfStringsAndNumbers":["string1",42,7]}')
} catch (e) {
console.log(e)
t.fail()
}
try {
const value = stringify({
fixedTupleOfStringsAndNumbers: ['string1', 42, 'string2']
})
t.assert.equal(value, '{"fixedTupleOfStringsAndNumbers":["string1",42,"string2"]}')
} catch (e) {
console.log(e)
t.fail()
}
})
test('object with anyOf and multiple types', (t) => {
t.plan(3)
const schema = {
title: 'object with anyOf and multiple types',
type: 'object',
properties: {
objectOrBoolean: {
anyOf: [
{
type: 'object',
properties: {
stringOrNumber: {
type: ['string', 'number']
}
}
},
{
type: 'boolean'
}
]
}
}
}
const stringify = build(schema, { ajv: { allowUnionTypes: true } })
try {
const value = stringify({
objectOrBoolean: { stringOrNumber: 'string' }
})
t.assert.equal(value, '{"objectOrBoolean":{"stringOrNumber":"string"}}')
} catch (e) {
console.log(e)
t.fail()
}
t.assert.equal(stringify({
objectOrBoolean: { stringOrNumber: 42 }
}), '{"objectOrBoolean":{"stringOrNumber":42}}')
t.assert.equal(stringify({
objectOrBoolean: true
}), '{"objectOrBoolean":true}')
})
test('string type array can handle dates', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
date: { type: ['string'] },
dateObject: { type: ['string'], format: 'date-time' }
}
}
const stringify = build(schema)
const value = stringify({
date: new Date('2018-04-20T07:52:31.017Z'),
dateObject: new Date('2018-04-21T07:52:31.017Z')
})
t.assert.equal(value, '{"date":"2018-04-20T07:52:31.017Z","dateObject":"2018-04-21T07:52:31.017Z"}')
})
test('object that is simultaneously a string and a json', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
simultaneously: {
type: ['string', 'object'],
properties: {
foo: { type: 'string' }
}
}
}
}
const likeObjectId = {
toString () { return 'hello' }
}
const stringify = build(schema)
const valueStr = stringify({ simultaneously: likeObjectId })
t.assert.equal(valueStr, '{"simultaneously":"hello"}')
const valueObj = stringify({ simultaneously: { foo: likeObjectId } })
t.assert.equal(valueObj, '{"simultaneously":{"foo":"hello"}}')
})
test('object that is simultaneously a string and a json switched', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
simultaneously: {
type: ['object', 'string'],
properties: {
foo: { type: 'string' }
}
}
}
}
const likeObjectId = {
toString () { return 'hello' }
}
const stringify = build(schema)
const valueStr = stringify({ simultaneously: likeObjectId })
t.assert.equal(valueStr, '{"simultaneously":{}}')
const valueObj = stringify({ simultaneously: { foo: likeObjectId } })
t.assert.equal(valueObj, '{"simultaneously":{"foo":"hello"}}')
})
test('class instance that is simultaneously a string and a json', (t) => {
t.plan(2)
const schema = {
type: 'object',
properties: {
simultaneously: {
type: ['string', 'object'],
properties: {
foo: { type: 'string' }
}
}
}
}
class Test {
toString () { return 'hello' }
}
const likeObjectId = new Test()
const stringify = build(schema)
const valueStr = stringify({ simultaneously: likeObjectId })
t.assert.equal(valueStr, '{"simultaneously":"hello"}')
const valueObj = stringify({ simultaneously: { foo: likeObjectId } })
t.assert.equal(valueObj, '{"simultaneously":{"foo":"hello"}}')
})
test('should not throw an error when type is array and object is null, it should instead coerce to []', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
arr: {
type: 'array',
items: {
type: 'number'
}
}
}
}
const stringify = build(schema)
const result = stringify({ arr: null })
t.assert.equal(result, JSON.stringify({ arr: [] }))
})
test('should throw an error when type is array and object is not an array', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
arr: {
type: 'array',
items: {
type: 'number'
}
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ arr: { foo: 'hello' } }), new TypeError('The value of \'#/properties/arr\' does not match schema definition.'))
})
test('should throw an error when type is array and object is not an array with external schema', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
arr: {
$ref: 'arrayOfNumbers#/definitions/arr'
}
}
}
const externalSchema = {
arrayOfNumbers: {
definitions: {
arr: {
type: 'array',
items: {
type: 'number'
}
}
}
}
}
const stringify = build(schema, { schema: externalSchema })
t.assert.throws(() => stringify({ arr: { foo: 'hello' } }), new TypeError('The value of \'arrayOfNumbers#/definitions/arr\' does not match schema definition.'))
})
test('throw an error if none of types matches', (t) => {
t.plan(1)
const schema = {
title: 'simple object with multi-type nullable primitive',
type: 'object',
properties: {
data: {
type: ['number', 'boolean']
}
}
}
const stringify = build(schema)
t.assert.throws(() => stringify({ data: 'string' }), 'The value "string" does not match schema definition.')
})

View File

@@ -0,0 +1,27 @@
'use strict'
const { test } = require('node:test')
const build = require('..')
test('object with custom format field', (t) => {
t.plan(1)
const schema = {
title: 'object with custom format field',
type: 'object',
properties: {
str: {
type: 'string',
format: 'test-format'
}
}
}
const stringify = build(schema)
t.assert.doesNotThrow(() => {
stringify({
str: 'string'
})
})
})

50
node_modules/fast-json-stringify/test/webpack.test.js generated vendored Normal file
View File

@@ -0,0 +1,50 @@
'use strict'
const { test } = require('node:test')
const webpack = require('webpack')
const path = require('path')
test('the library should work with webpack', async (t) => {
t.plan(1)
const targetdir = path.resolve(__dirname, '..', '.cache')
const targetname = path.join(targetdir, 'webpacktest.js')
const wopts = {
entry: path.resolve(__dirname, '..', 'index.js'),
mode: 'production',
target: 'node',
output: {
path: targetdir,
filename: 'webpacktest.js',
library: {
name: 'fastJsonStringify',
type: 'umd'
}
}
}
await new Promise((resolve, reject) => {
webpack(wopts, (err, stats) => {
if (err) { reject(err) } else { resolve(stats) };
})
})
const build = require(targetname)
const stringify = build({
title: 'webpack should not rename code to be executed',
type: 'object',
properties: {
foo: {
type: 'string'
},
bar: {
type: 'boolean'
}
},
patternProperties: {
foo: {
type: 'number'
}
}
})
const obj = { foo: '42', bar: true }
t.assert.equal(stringify(obj), '{"foo":"42","bar":true}')
})

231
node_modules/fast-json-stringify/types/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,231 @@
import Ajv, { Options as AjvOptions } from 'ajv'
type Build = typeof build
declare namespace build {
interface BaseSchema {
/**
* Schema id
*/
$id?: string
/**
* Schema title
*/
title?: string;
/**
* Schema description
*/
description?: string;
/**
* A comment to be added to the schema
*/
$comment?: string;
/**
* Default value to be assigned when no value is given in the document
*/
default?: any;
/**
* A list of example values that match this schema
*/
examples?: any[];
/**
* Additional schema definition to reference from within the schema
*/
definitions?: Record<string, Schema>
/**
* A set of schemas of which at least one must match
*/
anyOf?: Partial<Schema>[];
/**
* A set of schemas which must all match
*/
allOf?: Partial<Schema>[];
/**
* A conditional schema to check, controls schemas defined in `then` and `else`
*/
if?: Partial<Schema>;
/**
* A schema to apply if the conditional schema from `if` passes
*/
then?: Partial<Schema>;
/**
* A schema to apply if the conditional schema from `if` fails
*/
else?: Partial<Schema>;
/**
* Open API 3.0 spec states that any value that can be null must be declared `nullable`
* @default false
*/
nullable?: boolean;
}
export interface RefSchema {
/**
* A json-pointer to a schema to use as a reference
*/
$ref: string;
}
export interface AnySchema extends BaseSchema {
}
export interface StringSchema extends BaseSchema {
type: 'string';
format?: string;
}
export interface IntegerSchema extends BaseSchema {
type: 'integer';
}
export interface NumberSchema extends BaseSchema {
type: 'number';
}
export interface NullSchema extends BaseSchema {
type: 'null';
}
export interface BooleanSchema extends BaseSchema {
type: 'boolean';
}
export interface ArraySchema extends BaseSchema {
type: 'array';
/**
* The schema for the items in the array
*/
items: Schema | {}
}
export interface TupleSchema extends BaseSchema {
type: 'array';
/**
* The schemas for the items in the tuple
*/
items: Schema[];
}
type ObjectProperties = Record<string, Partial<Schema>> & {
anyOf?: ObjectProperties[];
allOf?: ObjectProperties[];
if?: ObjectProperties;
then?: ObjectProperties;
else?: ObjectProperties;
}
export interface ObjectSchema extends BaseSchema {
type: 'object';
/**
* Describe the properties of the object
*/
properties?: ObjectProperties;
/**
* The required properties of the object
*/
required?: string[];
/**
* Describe properties that have keys following a given pattern
*/
patternProperties?: ObjectProperties;
/**
* Specifies whether additional properties on the object are allowed, and optionally what schema they should
* adhere to
* @default false
*/
additionalProperties?: Schema | boolean;
}
export type Schema =
| RefSchema
| StringSchema
| IntegerSchema
| NumberSchema
| NullSchema
| BooleanSchema
| ArraySchema
| TupleSchema
| ObjectSchema
export interface Options {
/**
* Optionally add an external definition to reference from your schema
*/
schema?: Record<string, Schema>
/**
* Configure Ajv, which is used to evaluate conditional schemas and combined (anyOf) schemas
*/
ajv?: AjvOptions
/**
* Optionally configure how the integer will be rounded
*
* @default 'trunc'
*/
rounding?: 'ceil' | 'floor' | 'round' | 'trunc'
/**
* @deprecated
* Enable debug mode. Please use `mode: "debug"` instead
*/
debugMode?: boolean
/**
* Running mode of fast-json-stringify
*/
mode?: 'debug' | 'standalone'
/**
* Large arrays are defined as arrays containing, by default, `20000`
* elements or more. That value can be adjusted via the option parameter
* `largeArraySize`.
*
* @default 20000
*/
largeArraySize?: number | string | BigInt
/**
* Specify the function on how large Arrays should be stringified.
*
* @default 'default'
*/
largeArrayMechanism?: 'default' | 'json-stringify'
}
export const validLargeArrayMechanisms: string[]
export function restore (value: <TDoc extends object = object>(doc: TDoc) => string): ReturnType<Build>
export const build: Build
export { build as default }
}
interface DebugOption extends build.Options {
mode: 'debug'
}
interface DeprecateDebugOption extends build.Options {
debugMode: true
}
interface StandaloneOption extends build.Options {
mode: 'standalone'
}
type StringCoercible = string | Date | RegExp
type IntegerCoercible = number | BigInt
/**
* Build a stringify function using a schema of the documents that should be stringified
* @param schema The schema used to stringify values
* @param options The options to use (optional)
*/
declare function build (schema: build.AnySchema, options: DebugOption): { code: string, ajv: Ajv }
declare function build (schema: build.AnySchema, options: DeprecateDebugOption): { code: string, ajv: Ajv }
declare function build (schema: build.AnySchema, options: StandaloneOption): string
declare function build (schema: build.AnySchema, options?: build.Options): <TDoc = any>(doc: TDoc) => any
declare function build (schema: build.StringSchema, options?: build.Options): <TDoc extends StringCoercible = StringCoercible>(doc: TDoc) => string
declare function build (schema: build.IntegerSchema | build.NumberSchema, options?: build.Options): <TDoc extends IntegerCoercible = IntegerCoercible>(doc: TDoc) => string
declare function build (schema: build.NullSchema, options?: build.Options): <TDoc extends null = null>(doc: TDoc) => 'null'
declare function build (schema: build.BooleanSchema, options?: build.Options): <TDoc extends boolean = boolean>(doc: TDoc) => string
declare function build (schema: build.ArraySchema | build.TupleSchema, options?: build.Options): <TDoc extends any[]= any[]>(doc: TDoc) => string
declare function build (schema: build.ObjectSchema, options?: build.Options): <TDoc extends object = object>(doc: TDoc) => string
declare function build (schema: build.Schema, options?: build.Options): <TDoc = object | any[] | string | number | boolean | null> (doc: TDoc) => string
export = build

259
node_modules/fast-json-stringify/types/index.test-d.ts generated vendored Normal file
View File

@@ -0,0 +1,259 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- Test using this disabled, see https://github.com/fastify/fast-json-stringify/pull/683
import Ajv from 'ajv'
import build, { restore, Schema, validLargeArrayMechanisms } from '..'
import { expectError, expectType } from 'tsd'
// Number schemas
build({
type: 'number'
})(25)
build({
type: 'integer'
})(-5)
build({
type: 'integer'
})(5n)
build({
type: 'number'
}, { rounding: 'ceil' })
build({
type: 'number'
}, { rounding: 'floor' })
build({
type: 'number'
}, { rounding: 'round' })
build({
type: 'number'
}, { rounding: 'trunc' })
expectError(build({
type: 'number'
}, { rounding: 'invalid' }))
// String schema
build({
type: 'string'
})('foobar')
// Boolean schema
build({
type: 'boolean'
})(true)
// Null schema
build({
type: 'null'
})(null)
// Array schemas
build({
type: 'array',
items: { type: 'number' }
})([25])
build({
type: 'array',
items: [{ type: 'string' }, { type: 'integer' }]
})(['hello', 42])
// Object schemas
build({
type: 'object'
})({})
build({
type: 'object',
properties: {
foo: { type: 'string' },
bar: { type: 'integer' }
},
required: ['foo'],
patternProperties: {
'baz*': { type: 'null' }
},
additionalProperties: {
type: 'boolean'
}
})({ foo: 'bar' })
build({
type: 'object',
properties: {
foo: { type: 'string' },
bar: { type: 'integer' }
},
required: ['foo'],
patternProperties: {
'baz*': { type: 'null' }
},
additionalProperties: {
type: 'boolean'
}
}, { rounding: 'floor' })({ foo: 'bar' })
// Reference schemas
build({
title: 'Example Schema',
definitions: {
num: {
type: 'object',
properties: {
int: {
type: 'integer'
}
}
},
str: {
type: 'string'
},
def: {
type: 'null'
}
},
type: 'object',
properties: {
nickname: {
$ref: '#/definitions/str'
}
},
patternProperties: {
num: {
$ref: '#/definitions/num'
}
},
additionalProperties: {
$ref: '#/definitions/def'
}
})({ nickname: '', num: { int: 5 }, other: null })
// Conditional/Combined schemas
build({
title: 'Conditional/Combined Schema',
type: 'object',
properties: {
something: {
anyOf: [
{ type: 'string' },
{ type: 'boolean' }
]
}
},
if: {
properties: {
something: { type: 'string' }
}
},
then: {
properties: {
somethingElse: { type: 'number' }
}
},
else: {
properties: {
somethingElse: { type: 'null' }
}
}
})({ something: 'a string', somethingElse: 42 })
// String schema with format
build({
type: 'string',
format: 'date-time'
})(new Date())
/*
This overload doesn't work yet -
TypeScript chooses the generic for the schema
before it chooses the overload for the options
parameter.
let str: string, ajv: Ajv
str = build({
type: 'number'
}, { debugMode: true }).code
ajv = build({
type: 'number'
}, { debugMode: true }).ajv
str = build({
type: 'number'
}, { mode: 'debug' }).code
ajv = build({
type: 'number'
}, { mode: 'debug' }).ajv
str = build({
type: 'number'
}, { mode: 'standalone' })
*/
const debugCompiled = build({
title: 'default string',
type: 'object',
properties: {
firstName: {
type: 'string'
}
}
}, { mode: 'debug' })
expectType<ReturnType<typeof build>>(build.restore(debugCompiled))
expectType<ReturnType<typeof build>>(restore(debugCompiled))
expectType<string[]>(build.validLargeArrayMechanisms)
expectType<string[]>(validLargeArrayMechanisms)
/**
* Schema inference
*/
// With inference
interface InferenceSchema {
id: string;
a?: number;
}
const stringify3 = build({
type: 'object',
properties: { a: { type: 'string' } },
})
stringify3<InferenceSchema>({ id: '123' })
stringify3<InferenceSchema>({ a: 123, id: '123' })
expectError(stringify3<InferenceSchema>({ anotherOne: 'bar' }))
expectError(stringify3<Schema>({ a: 'bar' }))
// Without inference
const stringify4 = build({
type: 'object',
properties: { a: { type: 'string' } },
})
stringify4({ id: '123' })
stringify4({ a: 123, id: '123' })
stringify4({ anotherOne: 'bar' })
stringify4({ a: 'bar' })
// Without inference - string type
const stringify5 = build({
type: 'string',
})
stringify5('foo')
expectError(stringify5({ id: '123' }))
// Without inference - null type
const stringify6 = build({
type: 'null',
})
stringify6(null)
expectError(stringify6('a string'))
// Without inference - boolean type
const stringify7 = build({
type: 'boolean',
})
stringify7(true)
expectError(stringify7('a string'))
// largeArrayMechanism
build({}, { largeArrayMechanism: 'json-stringify' })
build({}, { largeArrayMechanism: 'default' })
expectError(build({} as Schema, { largeArrayMechanism: 'invalid' }))
build({}, { largeArraySize: 2000 })
build({}, { largeArraySize: '2e4' })
build({}, { largeArraySize: 2n })
expectError(build({} as Schema, { largeArraySize: ['asdf'] }))