Showing preview only (280K chars total). Download the full file or copy to clipboard to get everything.
Repository: fastify/fluent-json-schema
Branch: main
Commit: 09a1e3e3ab56
Files: 44
Total size: 265.6 KB
Directory structure:
gitextract_nx7y_40v/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ └── lock-threads.yml
├── .gitignore
├── .husky/
│ └── pre-commit
├── .npmignore
├── .npmrc
├── .nvmrc
├── LICENSE
├── README.md
├── docs/
│ └── API.md
├── eslint.config.js
├── package.json
├── src/
│ ├── ArraySchema.js
│ ├── ArraySchema.test.js
│ ├── BaseSchema.js
│ ├── BaseSchema.test.js
│ ├── BooleanSchema.js
│ ├── BooleanSchema.test.js
│ ├── FluentJSONSchema.js
│ ├── FluentSchema.integration.test.js
│ ├── FluentSchema.test.js
│ ├── IntegerSchema.js
│ ├── IntegerSchema.test.js
│ ├── MixedSchema.js
│ ├── MixedSchema.test.js
│ ├── NullSchema.js
│ ├── NullSchema.test.js
│ ├── NumberSchema.js
│ ├── NumberSchema.test.js
│ ├── ObjectSchema.js
│ ├── ObjectSchema.test.js
│ ├── RawSchema.js
│ ├── RawSchema.test.js
│ ├── StringSchema.js
│ ├── StringSchema.test.js
│ ├── example.js
│ ├── schemas/
│ │ └── basic.json
│ ├── utils.js
│ └── utils.test.js
└── types/
├── FluentJSONSchema.d.ts
└── FluentJSONSchema.test-d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
# Set default behavior to automatically convert line endings
* text=auto eol=lf
================================================
FILE: .github/dependabot.yml
================================================
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
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- main
- next
- 'v*'
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
permissions:
contents: write
pull-requests: write
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v6
with:
lint: true
license-check: true
================================================
FILE: .github/workflows/lock-threads.yml
================================================
name: Lock Threads
on:
schedule:
- cron: '0 0 1 * *'
workflow_dispatch:
concurrency:
group: lock
permissions:
contents: read
jobs:
lock-threads:
permissions:
issues: write
pull-requests: write
uses: fastify/workflows/.github/workflows/lock-threads.yml@v6
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Vim swap files
*.swp
# macOS files
.DS_Store
# Clinic
.clinic
# lock files
bun.lockb
package-lock.json
pnpm-lock.yaml
yarn.lock
# editor files
.vscode
.idea
#tap files
.tap/
================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .npmignore
================================================
.editorconfig
.gitignore
.husky
.npmignore
.nvmrc
.prettierrc
.travis.yml
yarn.lock
.idea
coverage
================================================
FILE: .npmrc
================================================
ignore-scripts=true
package-lock=false
================================================
FILE: .nvmrc
================================================
v14.19.0
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018-present The Fastify team <https://github.com/fastify/fastify#team>
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.
================================================
FILE: README.md
================================================
# fluent-json-schema
A fluent API to generate JSON schemas (draft-07) for Node.js and browser. Framework agnostic.
[](https://www.npmjs.org/package/fluent-json-schema)
[](https://github.com/fastify/fluent-json-schema/actions/workflows/ci.yml)
[](https://github.com/neostandard/neostandard)
## Features
- Fluent schema implements JSON Schema draft-07 standards
- Faster and shorter way to write a JSON Schema via a [fluent API](https://en.wikipedia.org/wiki/Fluent_interface)
- Runtime errors for invalid options or keywords misuse
- JavaScript constants can be used in the JSON schema (e.g. _enum_, _const_, _default_ ) avoiding discrepancies between model and schema
- TypeScript definitions
- Coverage 100%
## Install
npm i fluent-json-schema
or
yarn add fluent-json-schema
## Usage
```javascript
const S = require('fluent-json-schema')
const ROLES = {
ADMIN: 'ADMIN',
USER: 'USER',
}
const schema = S.object()
.id('http://foo/user')
.title('My First Fluent JSON Schema')
.description('A simple user')
.prop('email', S.string().format(S.FORMATS.EMAIL).required())
.prop('password', S.string().minLength(8).required())
.prop('role', S.string().enum(Object.values(ROLES)).default(ROLES.USER))
.prop(
'birthday',
S.raw({ type: 'string', format: 'date', formatMaximum: '2020-01-01' }) // formatMaximum is an AJV custom keywords
)
.definition(
'address',
S.object()
.id('#address')
.prop('line1', S.anyOf([S.string(), S.null()])) // JSON Schema nullable
.prop('line2', S.string().raw({ nullable: true })) // Open API / Swagger nullable
.prop('country', S.string())
.prop('city', S.string())
.prop('zipcode', S.string())
.required(['line1', 'country', 'city', 'zipcode'])
)
.prop('address', S.ref('#address'))
console.log(JSON.stringify(schema.valueOf(), undefined, 2))
```
Schema generated:
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"address": {
"type": "object",
"$id": "#address",
"properties": {
"line1": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"line2": {
"type": "string",
"nullable": true
},
"country": {
"type": "string"
},
"city": {
"type": "string"
},
"zipcode": {
"type": "string"
}
},
"required": ["line1", "country", "city", "zipcode"]
}
},
"type": "object",
"$id": "http://foo/user",
"title": "My First Fluent JSON Schema",
"description": "A simple user",
"properties": {
"email": {
"type": "string",
"format": "email"
},
"password": {
"type": "string",
"minLength": 8
},
"birthday": {
"type": "string",
"format": "date",
"formatMaximum": "2020-01-01"
},
"role": {
"type": "string",
"enum": ["ADMIN", "USER"],
"default": "USER"
},
"address": {
"$ref": "#address"
}
},
"required": ["email", "password"]
}
```
## TypeScript
### CommonJS
With `"esModuleInterop": true` activated in the `tsconfig.json`:
```typescript
import S from 'fluent-json-schema'
const schema = S.object()
.prop('foo', S.string())
.prop('bar', S.number())
.valueOf()
```
With `"esModuleInterop": false` in the `tsconfig.json`:
```typescript
import * as S from 'fluent-json-schema'
const schema = S.object()
.prop('foo', S.string())
.prop('bar', S.number())
.valueOf()
```
### ESM
A named export is also available to work with native ESM modules:
```typescript
import { S } from 'fluent-json-schema'
const schema = S.object()
.prop('foo', S.string())
.prop('bar', S.number())
.valueOf()
```
## Validation
Fluent schema **does not** validate a JSON schema. However, many libraries can do that for you.
Below are a few examples using [AJV](https://ajv.js.org/):
npm i ajv
or
yarn add ajv
### Validate an empty model
Snippet:
```javascript
const ajv = new Ajv({ allErrors: true })
const validate = ajv.compile(schema.valueOf())
let user = {}
let valid = validate(user)
console.log({ valid }) //=> {valid: false}
console.log(validate.errors) //=> {valid: false}
```
Output:
```
{valid: false}
errors: [
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'email' },
message: "should have required property 'email'",
},
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'password' },
message: "should have required property 'password'",
},
]
```
### Validate a partially filled model
Snippet:
```javascript
user = { email: 'test', password: 'password' }
valid = validate(user)
console.log({ valid })
console.log(validate.errors)
```
Output:
```
{valid: false}
errors:
[ { keyword: 'format',
dataPath: '.email',
schemaPath: '#/properties/email/format',
params: { format: 'email' },
message: 'should match format "email"' } ]
```
### Validate a model with a wrong format attribute
Snippet:
```javascript
user = { email: 'test@foo.com', password: 'password' }
valid = validate(user)
console.log({ valid })
console.log('errors:', validate.errors)
```
Output:
```
{valid: false}
errors: [ { keyword: 'required',
dataPath: '.address',
schemaPath: '#definitions/address/required',
params: { missingProperty: 'country' },
message: 'should have required property \'country\'' },
{ keyword: 'required',
dataPath: '.address',
schemaPath: '#definitions/address/required',
params: { missingProperty: 'city' },
message: 'should have required property \'city\'' },
{ keyword: 'required',
dataPath: '.address',
schemaPath: '#definitions/address/required',
params: { missingProperty: 'zipcoce' },
message: 'should have required property \'zipcode\'' } ]
```
### Valid model
Snippet:
```javascript
user = { email: 'test@foo.com', password: 'password' }
valid = validate(user)
console.log({ valid })
```
Output:
{valid: true}
## Extend schema
Normally inheritance with JSON Schema is achieved with `allOf`. However, when `.additionalProperties(false)` is used the validator won't
understand which properties come from the base schema. `S.extend` creates a schema merging the base into the new one so
that the validator knows all the properties because it evaluates only a single schema.
For example, in a CRUD API `POST /users` could use the `userBaseSchema` rather than `GET /users` or `PATCH /users` use the `userSchema`
which contains the `id`, `createdAt`, and `updatedAt` generated server side.
```js
const S = require('fluent-json-schema')
const userBaseSchema = S.object()
.additionalProperties(false)
.prop('username', S.string())
.prop('password', S.string())
const userSchema = S.object()
.prop('id', S.string().format('uuid'))
.prop('createdAt', S.string().format('time'))
.prop('updatedAt', S.string().format('time'))
.extend(userBaseSchema)
console.log(userSchema)
```
## Selecting certain properties of your schema
In addition to extending schemas, it is also possible to reduce them into smaller schemas. This comes in handy
when you have a large Fluent Schema, and would like to re-use some of its properties.
Select only properties you want to keep.
```js
const S = require('fluent-json-schema')
const userSchema = S.object()
.prop('username', S.string())
.prop('password', S.string())
.prop('id', S.string().format('uuid'))
.prop('createdAt', S.string().format('time'))
.prop('updatedAt', S.string().format('time'))
const loginSchema = userSchema.only(['username', 'password'])
```
Or remove properties you dont want to keep.
```js
const S = require('fluent-json-schema')
const personSchema = S.object()
.prop('name', S.string())
.prop('age', S.number())
.prop('id', S.string().format('uuid'))
.prop('createdAt', S.string().format('time'))
.prop('updatedAt', S.string().format('time'))
const bodySchema = personSchema.without(['createdAt', 'updatedAt'])
```
### Detect Fluent Schema objects
Every Fluent Schema object contains a boolean `isFluentSchema`. In this way, you can write your own utilities that understand the Fluent Schema API and improve the user experience of your tool.
```js
const S = require('fluent-json-schema')
const schema = S.object().prop('foo', S.string()).prop('bar', S.number())
console.log(schema.isFluentSchema) // true
```
## Documentation
- [Full API Documentation](./docs/API.md).
- [JSON schema draft-07 reference](https://json-schema.org/draft-07/draft-handrews-json-schema-01).
## Acknowledgments
Thanks to [Matteo Collina](https://twitter.com/matteocollina) for pushing me to implement this utility! 🙏
## Related projects
- JSON Schema [Draft 7](http://json-schema.org/specification-links.html#draft-7)
- [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/) (despite referring to draft 6 the guide is still good for grasping the main concepts)
- [AJV](https://ajv.js.org/) JSON Schema validator
- [jsonschema.net](https://www.jsonschema.net/) an online JSON Schema visual editor (it does not support advanced features)
## License
Licensed under [MIT](./LICENSE).
================================================
FILE: docs/API.md
================================================
## Functions
<dl>
<dt><a href="#ArraySchema">ArraySchema([options])</a> ⇒ <code><a href="#ArraySchema">ArraySchema</a></code></dt>
<dd><p>Represents a ArraySchema.</p>
</dd>
<dt><a href="#items">items(items)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>This keyword determines how child instances validate for arrays, and does not directly validate the immediate instance itself.
If "items" is a schema, validation succeeds if all elements in the array successfully validate against that schema.
If "items" is an array of schemas, validation succeeds if each element of the instance validates against the schema at the same position, if any.
Omitting this keyword has the same behavior as an empty schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.1">reference</a></p>
</dd>
<dt><a href="#additionalItems">additionalItems(items)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>This keyword determines how child instances validate for arrays, and does not directly validate the immediate instance itself.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.2">reference</a></p>
</dd>
<dt><a href="#contains">contains(value)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>An array instance is valid against "contains" if at least one of its elements is valid against the given schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.6">reference</a></p>
</dd>
<dt><a href="#uniqueItems">uniqueItems(boolean)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>If this keyword has boolean value false, the instance validates successfully.
If it has boolean value true, the instance validates successfully if all of its elements are unique.
Omitting this keyword has the same behavior as a value of false.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.5">reference</a></p>
</dd>
<dt><a href="#minItems">minItems(min)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
Omitting this keyword has the same behavior as a value of 0.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.4">reference</a></p>
</dd>
<dt><a href="#maxItems">maxItems(max)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
Omitting this keyword has the same behavior as a value of 0.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.3">reference</a></p>
</dd>
<dt><a href="#BaseSchema">BaseSchema([options])</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>Represents a BaseSchema.</p>
</dd>
<dt><a href="#id">id(id)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.</p>
<p><a href="https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2">reference</a></p>
</dd>
<dt><a href="#title">title(title)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>It can be used to decorate a user interface with information about the data produced by this user interface. A title will preferably be short.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.1">reference</a></p>
</dd>
<dt><a href="#description">description(description)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>It can be used to decorate a user interface with information about the data
produced by this user interface. A description provides explanation about
the purpose of the instance described by the schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.1">reference</a></p>
</dd>
<dt><a href="#examples">examples(examples)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>The value of this keyword MUST be an array.
There are no restrictions placed on the values within the array.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.4">reference</a></p>
</dd>
<dt><a href="#ref">ref(ref)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>The value must be a valid id e.g. #properties/foo</p>
</dd>
<dt><a href="#enum">enum(values)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>The value of this keyword MUST be an array. This array SHOULD have at least one element. Elements in the array SHOULD be unique.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.2">reference</a></p>
</dd>
<dt><a href="#const">const(value)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>The value of this keyword MAY be of any type, including null.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.3">reference</a></p>
</dd>
<dt><a href="#default">default(defaults)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>There are no restrictions placed on the value of this keyword.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.2">reference</a></p>
</dd>
<dt><a href="#readOnly">readOnly(isReadOnly)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>The value of readOnly can be left empty to indicate the property is readOnly.
It takes an optional boolean which can be used to explicitly set readOnly true/false.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.3">reference</a></p>
</dd>
<dt><a href="#writeOnly">writeOnly(isWriteOnly)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>The value of writeOnly can be left empty to indicate the property is writeOnly.
It takes an optional boolean which can be used to explicitly set writeOnly true/false.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.3">reference</a></p>
</dd>
<dt><a href="#deprecated">deprecated(isDeprecated)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>The value of deprecated can be left empty to indicate the property is deprecated.
It takes an optional boolean which can be used to explicitly set deprecated true/false.</p>
<p><a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.3">reference</a></p>
</dd>
<dt><a href="#required">required()</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>Required has to be chained to a property:
Examples:</p>
<ul>
<li>S.prop('prop').required()</li>
<li>S.prop('prop', S.number()).required()</li>
<li>S.required(['foo', 'bar'])</li>
</ul>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.3">reference</a></p>
</dd>
<dt><a href="#not">not(not)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>This keyword's value MUST be a valid JSON Schema.
An instance is valid against this keyword if it fails to validate successfully against the schema defined by this keyword.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.4">reference</a></p>
</dd>
<dt><a href="#anyOf">anyOf(schemas)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.2">reference</a></p>
</dd>
<dt><a href="#allOf">allOf(schemas)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.1">reference</a></p>
</dd>
<dt><a href="#oneOf">oneOf(schemas)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.3">reference</a></p>
</dd>
<dt><a href="#ifThen">ifThen(ifClause, thenClause)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>This validation outcome of this keyword's subschema has no direct effect on the overall validation result.
Rather, it controls which of the "then" or "else" keywords are evaluated.
When "if" is present, and the instance successfully validates against its subschema, then
validation succeeds against this keyword if the instance also successfully validates against this keyword's subschema.</p>
</dd>
<dt><a href="#ifThenElse">ifThenElse(ifClause, thenClause, elseClause)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>When "if" is present, and the instance fails to validate against its subschema,
then validation succeeds against this keyword if the instance successfully validates against this keyword's subschema.</p>
</dd>
<dt><a href="#raw">raw(fragment)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>Because the differences between JSON Schemas and Open API (Swagger)
it can be handy to arbitrary modify the schema injecting a fragment</p>
<ul>
<li>Examples:</li>
</ul>
<ul>
<li>S.number().raw({ nullable:true })</li>
<li>S.string().format('date').raw({ formatMaximum: '2020-01-01' })</li>
</ul>
</dd>
<dt><a href="#valueOf">valueOf([options])</a> ⇒ <code><a href="#object">object</a></code></dt>
<dd><p>It returns all the schema values</p>
</dd>
<dt><a href="#BooleanSchema">BooleanSchema([options])</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>Represents a BooleanSchema.</p>
</dd>
<dt><a href="#S">S([options])</a> ⇒ <code><a href="#S">S</a></code></dt>
<dd><p>Represents a S.</p>
</dd>
<dt><a href="#string">string()</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>Set a property to type string</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3">reference</a></p>
</dd>
<dt><a href="#number">number()</a> ⇒ <code><a href="#NumberSchema">NumberSchema</a></code></dt>
<dd><p>Set a property to type number</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#numeric">reference</a></p>
</dd>
<dt><a href="#integer">integer()</a> ⇒ <code><a href="#IntegerSchema">IntegerSchema</a></code></dt>
<dd><p>Set a property to type integer</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#numeric">reference</a></p>
</dd>
<dt><a href="#boolean">boolean()</a> ⇒ <code><a href="#BooleanSchema">BooleanSchema</a></code></dt>
<dd><p>Set a property to type boolean</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7">reference</a></p>
</dd>
<dt><a href="#array">array()</a> ⇒ <code><a href="#ArraySchema">ArraySchema</a></code></dt>
<dd><p>Set a property to type array</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4">reference</a></p>
</dd>
<dt><a href="#object">object()</a> ⇒ <code><a href="#ObjectSchema">ObjectSchema</a></code></dt>
<dd><p>Set a property to type object</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5">reference</a></p>
</dd>
<dt><a href="#null">null()</a> ⇒ <code><a href="#NullSchema">NullSchema</a></code></dt>
<dd><p>Set a property to type null</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#general">reference</a></p>
</dd>
<dt><a href="#mixed">mixed(types)</a> ⇒ <code><a href="#MixedSchema">MixedSchema</a></code></dt>
<dd><p>A mixed schema is the union of multiple types (e.g. ['string', 'integer']</p>
</dd>
<dt><a href="#raw">raw(fragment)</a> ⇒ <code><a href="#BaseSchema">BaseSchema</a></code></dt>
<dd><p>Because the differences between JSON Schemas and Open API (Swagger)
it can be handy to arbitrary modify the schema injecting a fragment</p>
<ul>
<li>Examples:</li>
</ul>
<ul>
<li>S.raw({ nullable:true, format: 'date', formatMaximum: '2020-01-01' })</li>
<li>S.string().format('date').raw({ formatMaximum: '2020-01-01' })</li>
</ul>
</dd>
<dt><a href="#IntegerSchema">IntegerSchema([options])</a> ⇒ <code><a href="#NumberSchema">NumberSchema</a></code></dt>
<dd><p>Represents a NumberSchema.</p>
</dd>
<dt><a href="#MixedSchema">MixedSchema([options])</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>Represents a MixedSchema.</p>
</dd>
<dt><a href="#NullSchema">NullSchema([options])</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>Represents a NullSchema.</p>
</dd>
<dt><a href="#null">null()</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>Set a property to type null</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.1">reference</a></p>
</dd>
<dt><a href="#NumberSchema">NumberSchema([options])</a> ⇒ <code><a href="#NumberSchema">NumberSchema</a></code></dt>
<dd><p>Represents a NumberSchema.</p>
</dd>
<dt><a href="#minimum">minimum(min)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>It represents an inclusive lower limit for a numeric instance.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.4">reference</a></p>
</dd>
<dt><a href="#exclusiveMinimum">exclusiveMinimum(min)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>It represents an exclusive lower limit for a numeric instance.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.5">reference</a></p>
</dd>
<dt><a href="#maximum">maximum(max)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>It represents an inclusive upper limit for a numeric instance.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.2">reference</a></p>
</dd>
<dt><a href="#exclusiveMaximum">exclusiveMaximum(max)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>It represents an exclusive upper limit for a numeric instance.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.3">reference</a></p>
</dd>
<dt><a href="#multipleOf">multipleOf(multiple)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>It's strictly greater than 0.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.1">reference</a></p>
</dd>
<dt><a href="#ObjectSchema">ObjectSchema([options])</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>Represents a ObjectSchema.</p>
</dd>
<dt><a href="#id">id(id)</a></dt>
<dd><p>It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
Calling <code>id</code> on an ObjectSchema will alway set the id on the root of the object rather than in its "properties", which
differs from other schema types.</p>
<p><a href="https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2">reference</a></p>
</dd>
<dt><a href="#additionalProperties">additionalProperties(value)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
Validation with "additionalProperties" applies only to the child values of instance names that do not match any names in "properties",
and do not match any regular expression in "patternProperties".
For all such properties, validation succeeds if the child instance validates against the "additionalProperties" schema.
Omitting this keyword has the same behavior as an empty schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.6">reference</a></p>
</dd>
<dt><a href="#maxProperties">maxProperties(max)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.1">reference</a></p>
</dd>
<dt><a href="#minProperties">minProperties(min)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.2">reference</a></p>
</dd>
<dt><a href="#patternProperties">patternProperties(opts)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>Each property name of this object SHOULD be a valid regular expression, according to the ECMA 262 regular expression dialect.
Each property value of this object MUST be a valid JSON Schema.
This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
Validation of the primitive instance type against this keyword always succeeds.
Validation succeeds if, for each instance name that matches any regular expressions that appear as a property name in this keyword's value, the child instance for that name successfully validates against each schema that corresponds to a matching regular expression.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.5">reference</a></p>
</dd>
<dt><a href="#dependencies">dependencies(opts)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>This keyword specifies rules that are evaluated if the instance is an object and contains a certain property.
This keyword's value MUST be an object. Each property specifies a dependency. Each dependency value MUST be an array or a valid JSON Schema.
If the dependency value is a subschema, and the dependency key is a property in the instance, the entire instance must validate against the dependency value.
If the dependency value is an array, each element in the array, if any, MUST be a string, and MUST be unique. If the dependency key is a property in the instance, each of the items in the dependency value must be a property that exists in the instance.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.7">reference</a></p>
</dd>
<dt><a href="#dependentRequired">dependentRequired(opts)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>The value of "properties" MUST be an object. Each dependency value MUST be an array.
Each element in the array MUST be a string and MUST be unique. If the dependency key is a property in the instance, each of the items in the dependency value must be a property that exists in the instance.</p>
<p><a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.5.4">reference</a></p>
</dd>
<dt><a href="#dependentSchemas">dependentSchemas(opts)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>The value of "properties" MUST be an object. The dependency value MUST be a valid JSON Schema.
Each dependency key is a property in the instance and the entire instance must validate against the dependency value.</p>
<p><a href="https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.2.2.4">reference</a></p>
</dd>
<dt><a href="#propertyNames">propertyNames(value)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>If the instance is an object, this keyword validates if every property name in the instance validates against the provided schema.
Note the property name that the schema is testing will always be a string.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.8">reference</a></p>
</dd>
<dt><a href="#prop">prop(name, props)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>The value of "properties" MUST be an object. Each value of this object MUST be a valid JSON Schema.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.4">reference</a></p>
</dd>
<dt><a href="#only">only(properties)</a> ⇒ <code><a href="#ObjectSchema">ObjectSchema</a></code></dt>
<dd><p>Returns an object schema with only a subset of keys provided. If called on an ObjectSchema with an
<code>$id</code>, it will be removed and the return value will be considered a new schema.</p>
</dd>
<dt><a href="#without">without(properties)</a> ⇒ <code><a href="#ObjectSchema">ObjectSchema</a></code></dt>
<dd><p>Returns an object schema without a subset of keys provided. If called on an ObjectSchema with an
<code>$id</code>, it will be removed and the return value will be considered a new schema.</p>
</dd>
<dt><a href="#definition">definition(name, props)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>The "definitions" keywords provides a standardized location for schema authors to inline re-usable JSON Schemas into a more general schema.
There are no restrictions placed on the values within the array.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.9">reference</a></p>
</dd>
<dt><a href="#RawSchema">RawSchema(schema)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>Represents a raw JSON Schema that will be parsed</p>
</dd>
<dt><a href="#StringSchema">StringSchema([options])</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>Represents a StringSchema.</p>
</dd>
<dt><a href="#minLength">minLength(min)</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.2">reference</a></p>
</dd>
<dt><a href="#maxLength">maxLength(max)</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.1">reference</a></p>
</dd>
<dt><a href="#format">format(format)</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>A string value can be RELATIVE_JSON_POINTER, JSON_POINTER, UUID, REGEX, IPV6, IPV4, HOSTNAME, EMAIL, URL, URI_TEMPLATE, URI_REFERENCE, URI, TIME, DATE, DATE_TIME, ISO_TIME, ISO_DATE_TIME.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.7.3">reference</a></p>
</dd>
<dt><a href="#pattern">pattern(pattern)</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>This string SHOULD be a valid regular expression, according to the ECMA 262 regular expression dialect.
A string instance is considered valid if the regular expression matches the instance successfully.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.3">reference</a></p>
</dd>
<dt><a href="#contentEncoding">contentEncoding(encoding)</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>If the instance value is a string, this property defines that the string SHOULD
be interpreted as binary data and decoded using the encoding named by this property.
RFC 2045, Sec 6.1 [RFC2045] lists the possible values for this property.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.8.3">reference</a></p>
</dd>
<dt><a href="#contentMediaType">contentMediaType(mediaType)</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>The value of this property must be a media type, as defined by RFC 2046 [RFC2046].
This property defines the media type of instances which this schema defines.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.8.4">reference</a></p>
</dd>
</dl>
<a name="ArraySchema"></a>
## ArraySchema([options]) ⇒ [<code>ArraySchema</code>](#ArraySchema)
Represents a ArraySchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>StringSchema</code>](#StringSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="items"></a>
## items(items) ⇒ <code>FluentSchema</code>
This keyword determines how child instances validate for arrays, and does not directly validate the immediate instance itself.
If "items" is a schema, validation succeeds if all elements in the array successfully validate against that schema.
If "items" is an array of schemas, validation succeeds if each element of the instance validates against the schema at the same position, if any.
Omitting this keyword has the same behavior as an empty schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.1)
**Kind**: global function
| Param | Type |
| --- | --- |
| items | <code>FluentSchema</code> \| <code>Array.<FluentSchema></code> |
<a name="additionalItems"></a>
## additionalItems(items) ⇒ <code>FluentSchema</code>
This keyword determines how child instances validate for arrays, and does not directly validate the immediate instance itself.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.2)
**Kind**: global function
| Param | Type |
| --- | --- |
| items | <code>FluentSchema</code> \| [<code>boolean</code>](#boolean) |
<a name="contains"></a>
## contains(value) ⇒ <code>FluentSchema</code>
An array instance is valid against "contains" if at least one of its elements is valid against the given schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.6)
**Kind**: global function
| Param | Type |
| --- | --- |
| value | <code>FluentSchema</code> |
<a name="uniqueItems"></a>
## uniqueItems(boolean) ⇒ <code>FluentSchema</code>
If this keyword has boolean value false, the instance validates successfully.
If it has boolean value true, the instance validates successfully if all of its elements are unique.
Omitting this keyword has the same behavior as a value of false.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.5)
**Kind**: global function
| Param | Type |
| --- | --- |
| boolean | [<code>boolean</code>](#boolean) |
<a name="minItems"></a>
## minItems(min) ⇒ <code>FluentSchema</code>
An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
Omitting this keyword has the same behavior as a value of 0.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| min | [<code>number</code>](#number) |
<a name="maxItems"></a>
## maxItems(max) ⇒ <code>FluentSchema</code>
An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
Omitting this keyword has the same behavior as a value of 0.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| max | [<code>number</code>](#number) |
<a name="BaseSchema"></a>
## BaseSchema([options]) ⇒ [<code>BaseSchema</code>](#BaseSchema)
Represents a BaseSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>BaseSchema</code>](#BaseSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="id"></a>
## id(id) ⇒ [<code>BaseSchema</code>](#BaseSchema)
It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
[reference](https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2)
**Kind**: global function
| Param | Type | Description |
| --- | --- | --- |
| id | [<code>string</code>](#string) | an #id |
<a name="title"></a>
## title(title) ⇒ [<code>BaseSchema</code>](#BaseSchema)
It can be used to decorate a user interface with information about the data produced by this user interface. A title will preferably be short.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.1)
**Kind**: global function
| Param | Type |
| --- | --- |
| title | [<code>string</code>](#string) |
<a name="description"></a>
## description(description) ⇒ [<code>BaseSchema</code>](#BaseSchema)
It can be used to decorate a user interface with information about the data
produced by this user interface. A description provides explanation about
the purpose of the instance described by the schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.1)
**Kind**: global function
| Param | Type |
| --- | --- |
| description | [<code>string</code>](#string) |
<a name="examples"></a>
## examples(examples) ⇒ [<code>BaseSchema</code>](#BaseSchema)
The value of this keyword MUST be an array.
There are no restrictions placed on the values within the array.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| examples | [<code>string</code>](#string) |
<a name="ref"></a>
## ref(ref) ⇒ [<code>BaseSchema</code>](#BaseSchema)
The value must be a valid id e.g. #properties/foo
**Kind**: global function
| Param | Type |
| --- | --- |
| ref | [<code>string</code>](#string) |
<a name="enum"></a>
## enum(values) ⇒ [<code>BaseSchema</code>](#BaseSchema)
The value of this keyword MUST be an array. This array SHOULD have at least one element. Elements in the array SHOULD be unique.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.2)
**Kind**: global function
| Param | Type |
| --- | --- |
| values | [<code>array</code>](#array) |
<a name="const"></a>
## const(value) ⇒ [<code>BaseSchema</code>](#BaseSchema)
The value of this keyword MAY be of any type, including null.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.3)
**Kind**: global function
| Param |
| --- |
| value |
<a name="default"></a>
## default(defaults) ⇒ [<code>BaseSchema</code>](#BaseSchema)
There are no restrictions placed on the value of this keyword.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.2)
**Kind**: global function
| Param |
| --- |
| defaults |
<a name="readOnly"></a>
## readOnly(isReadOnly) ⇒ [<code>BaseSchema</code>](#BaseSchema)
The value of readOnly can be left empty to indicate the property is readOnly.
It takes an optional boolean which can be used to explicitly set readOnly true/false.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| isReadOnly | [<code>boolean</code>](#boolean) \| <code>undefined</code> |
<a name="writeOnly"></a>
## writeOnly(isWriteOnly) ⇒ [<code>BaseSchema</code>](#BaseSchema)
The value of writeOnly can be left empty to indicate the property is writeOnly.
It takes an optional boolean which can be used to explicitly set writeOnly true/false.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| isWriteOnly | [<code>boolean</code>](#boolean) \| <code>undefined</code> |
<a name="deprecated"></a>
## deprecated(isDeprecated) ⇒ [<code>BaseSchema</code>](#BaseSchema)
The value of deprecated can be left empty to indicate the property is deprecated.
It takes an optional boolean which can be used to explicitly set deprecated true/false.
[reference](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| isDeprecated | <code>Boolean</code> |
<a name="required"></a>
## required() ⇒ <code>FluentSchema</code>
Required has to be chained to a property:
Examples:
- S.prop('prop').required()
- S.prop('prop', S.number()).required()
- S.required(['foo', 'bar'])
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.3)
**Kind**: global function
<a name="not"></a>
## not(not) ⇒ [<code>BaseSchema</code>](#BaseSchema)
This keyword's value MUST be a valid JSON Schema.
An instance is valid against this keyword if it fails to validate successfully against the schema defined by this keyword.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| not | <code>FluentSchema</code> |
<a name="anyOf"></a>
## anyOf(schemas) ⇒ [<code>BaseSchema</code>](#BaseSchema)
It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.2)
**Kind**: global function
| Param | Type |
| --- | --- |
| schemas | [<code>array</code>](#array) |
<a name="allOf"></a>
## allOf(schemas) ⇒ [<code>BaseSchema</code>](#BaseSchema)
It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.1)
**Kind**: global function
| Param | Type |
| --- | --- |
| schemas | [<code>array</code>](#array) |
<a name="oneOf"></a>
## oneOf(schemas) ⇒ [<code>BaseSchema</code>](#BaseSchema)
It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| schemas | [<code>array</code>](#array) |
<a name="ifThen"></a>
## ifThen(ifClause, thenClause) ⇒ [<code>BaseSchema</code>](#BaseSchema)
This validation outcome of this keyword's subschema has no direct effect on the overall validation result.
Rather, it controls which of the "then" or "else" keywords are evaluated.
When "if" is present, and the instance successfully validates against its subschema, then
validation succeeds against this keyword if the instance also successfully validates against this keyword's subschema.
**Kind**: global function
| Param | Type | Description |
| --- | --- | --- |
| ifClause | [<code>BaseSchema</code>](#BaseSchema) | [reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.1) |
| thenClause | [<code>BaseSchema</code>](#BaseSchema) | [reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.2) |
<a name="ifThenElse"></a>
## ifThenElse(ifClause, thenClause, elseClause) ⇒ [<code>BaseSchema</code>](#BaseSchema)
When "if" is present, and the instance fails to validate against its subschema,
then validation succeeds against this keyword if the instance successfully validates against this keyword's subschema.
**Kind**: global function
| Param | Type | Description |
| --- | --- | --- |
| ifClause | [<code>BaseSchema</code>](#BaseSchema) | [reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.1) |
| thenClause | [<code>BaseSchema</code>](#BaseSchema) | [reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.2) |
| elseClause | [<code>BaseSchema</code>](#BaseSchema) | [reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.3) |
<a name="raw"></a>
## raw(fragment) ⇒ [<code>BaseSchema</code>](#BaseSchema)
Because the differences between JSON Schemas and Open API (Swagger)
it can be handy to arbitrary modify the schema injecting a fragment
* Examples:
- S.number().raw({ nullable:true })
- S.string().format('date').raw({ formatMaximum: '2020-01-01' })
**Kind**: global function
| Param | Type | Description |
| --- | --- | --- |
| fragment | [<code>string</code>](#string) | an arbitrary JSON Schema to inject [reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.3) |
<a name="valueOf"></a>
## valueOf([options]) ⇒ [<code>object</code>](#object)
It returns all the schema values
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.isRoot] | [<code>boolean</code>](#boolean) | <code>true</code> | Is a root level schema |
<a name="BooleanSchema"></a>
## BooleanSchema([options]) ⇒ [<code>StringSchema</code>](#StringSchema)
Represents a BooleanSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>StringSchema</code>](#StringSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="S"></a>
## S([options]) ⇒ [<code>S</code>](#S)
Represents a S.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>S</code>](#S) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="string"></a>
## string() ⇒ [<code>StringSchema</code>](#StringSchema)
Set a property to type string
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3)
**Kind**: global function
<a name="number"></a>
## number() ⇒ [<code>NumberSchema</code>](#NumberSchema)
Set a property to type number
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#numeric)
**Kind**: global function
<a name="integer"></a>
## integer() ⇒ [<code>IntegerSchema</code>](#IntegerSchema)
Set a property to type integer
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#numeric)
**Kind**: global function
<a name="boolean"></a>
## boolean() ⇒ [<code>BooleanSchema</code>](#BooleanSchema)
Set a property to type boolean
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7)
**Kind**: global function
<a name="array"></a>
## array() ⇒ [<code>ArraySchema</code>](#ArraySchema)
Set a property to type array
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4)
**Kind**: global function
<a name="object"></a>
## object() ⇒ [<code>ObjectSchema</code>](#ObjectSchema)
Set a property to type object
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5)
**Kind**: global function
<a name="null"></a>
## null() ⇒ [<code>NullSchema</code>](#NullSchema)
Set a property to type null
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#general)
**Kind**: global function
<a name="mixed"></a>
## mixed(types) ⇒ [<code>MixedSchema</code>](#MixedSchema)
A mixed schema is the union of multiple types (e.g. ['string', 'integer']
**Kind**: global function
| Param | Type |
| --- | --- |
| types | [<code>Array.<string></code>](#string) |
<a name="raw"></a>
## raw(fragment) ⇒ [<code>BaseSchema</code>](#BaseSchema)
Because the differences between JSON Schemas and Open API (Swagger)
it can be handy to arbitrary modify the schema injecting a fragment
* Examples:
- S.raw({ nullable:true, format: 'date', formatMaximum: '2020-01-01' })
- S.string().format('date').raw({ formatMaximum: '2020-01-01' })
**Kind**: global function
| Param | Type | Description |
| --- | --- | --- |
| fragment | [<code>string</code>](#string) | an arbitrary JSON Schema to inject |
<a name="IntegerSchema"></a>
## IntegerSchema([options]) ⇒ [<code>NumberSchema</code>](#NumberSchema)
Represents a NumberSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>NumberSchema</code>](#NumberSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="MixedSchema"></a>
## MixedSchema([options]) ⇒ [<code>StringSchema</code>](#StringSchema)
Represents a MixedSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>MixedSchema</code>](#MixedSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="NullSchema"></a>
## NullSchema([options]) ⇒ [<code>StringSchema</code>](#StringSchema)
Represents a NullSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>StringSchema</code>](#StringSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="null"></a>
## null() ⇒ <code>FluentSchema</code>
Set a property to type null
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.1)
**Kind**: global function
<a name="NumberSchema"></a>
## NumberSchema([options]) ⇒ [<code>NumberSchema</code>](#NumberSchema)
Represents a NumberSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>NumberSchema</code>](#NumberSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="minimum"></a>
## minimum(min) ⇒ <code>FluentSchema</code>
It represents an inclusive lower limit for a numeric instance.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| min | [<code>number</code>](#number) |
<a name="exclusiveMinimum"></a>
## exclusiveMinimum(min) ⇒ <code>FluentSchema</code>
It represents an exclusive lower limit for a numeric instance.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.5)
**Kind**: global function
| Param | Type |
| --- | --- |
| min | [<code>number</code>](#number) |
<a name="maximum"></a>
## maximum(max) ⇒ <code>FluentSchema</code>
It represents an inclusive upper limit for a numeric instance.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.2)
**Kind**: global function
| Param | Type |
| --- | --- |
| max | [<code>number</code>](#number) |
<a name="exclusiveMaximum"></a>
## exclusiveMaximum(max) ⇒ <code>FluentSchema</code>
It represents an exclusive upper limit for a numeric instance.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| max | [<code>number</code>](#number) |
<a name="multipleOf"></a>
## multipleOf(multiple) ⇒ <code>FluentSchema</code>
It's strictly greater than 0.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.1)
**Kind**: global function
| Param | Type |
| --- | --- |
| multiple | [<code>number</code>](#number) |
<a name="ObjectSchema"></a>
## ObjectSchema([options]) ⇒ [<code>StringSchema</code>](#StringSchema)
Represents a ObjectSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>StringSchema</code>](#StringSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="id"></a>
## id(id)
It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
Calling `id` on an ObjectSchema will alway set the id on the root of the object rather than in its "properties", which
differs from other schema types.
[reference](https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2)
**Kind**: global function
| Param | Type | Description |
| --- | --- | --- |
| id | [<code>string</code>](#string) | an #id |
<a name="additionalProperties"></a>
## additionalProperties(value) ⇒ <code>FluentSchema</code>
This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
Validation with "additionalProperties" applies only to the child values of instance names that do not match any names in "properties",
and do not match any regular expression in "patternProperties".
For all such properties, validation succeeds if the child instance validates against the "additionalProperties" schema.
Omitting this keyword has the same behavior as an empty schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.6)
**Kind**: global function
| Param | Type |
| --- | --- |
| value | <code>FluentSchema</code> \| [<code>boolean</code>](#boolean) |
<a name="maxProperties"></a>
## maxProperties(max) ⇒ <code>FluentSchema</code>
An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.1)
**Kind**: global function
| Param | Type |
| --- | --- |
| max | [<code>number</code>](#number) |
<a name="minProperties"></a>
## minProperties(min) ⇒ <code>FluentSchema</code>
An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.2)
**Kind**: global function
| Param | Type |
| --- | --- |
| min | [<code>number</code>](#number) |
<a name="patternProperties"></a>
## patternProperties(opts) ⇒ <code>FluentSchema</code>
Each property name of this object SHOULD be a valid regular expression, according to the ECMA 262 regular expression dialect.
Each property value of this object MUST be a valid JSON Schema.
This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
Validation of the primitive instance type against this keyword always succeeds.
Validation succeeds if, for each instance name that matches any regular expressions that appear as a property name in this keyword's value, the child instance for that name successfully validates against each schema that corresponds to a matching regular expression.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.5)
**Kind**: global function
| Param | Type |
| --- | --- |
| opts | [<code>object</code>](#object) |
<a name="dependencies"></a>
## dependencies(opts) ⇒ <code>FluentSchema</code>
This keyword specifies rules that are evaluated if the instance is an object and contains a certain property.
This keyword's value MUST be an object. Each property specifies a dependency. Each dependency value MUST be an array or a valid JSON Schema.
If the dependency value is a subschema, and the dependency key is a property in the instance, the entire instance must validate against the dependency value.
If the dependency value is an array, each element in the array, if any, MUST be a string, and MUST be unique. If the dependency key is a property in the instance, each of the items in the dependency value must be a property that exists in the instance.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.7)
**Kind**: global function
| Param | Type |
| --- | --- |
| opts | [<code>object</code>](#object) |
<a name="dependentRequired"></a>
## dependentRequired(opts) ⇒ <code>FluentSchema</code>
The value of "properties" MUST be an object. Each dependency value MUST be an array.
Each element in the array MUST be a string and MUST be unique. If the dependency key is a property in the instance, each of the items in the dependency value must be a property that exists in the instance.
[reference](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.5.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| opts | [<code>object</code>](#object) |
<a name="dependentSchemas"></a>
## dependentSchemas(opts) ⇒ <code>FluentSchema</code>
The value of "properties" MUST be an object. The dependency value MUST be a valid JSON Schema.
Each dependency key is a property in the instance and the entire instance must validate against the dependency value.
[reference](https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.2.2.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| opts | [<code>object</code>](#object) |
<a name="propertyNames"></a>
## propertyNames(value) ⇒ <code>FluentSchema</code>
If the instance is an object, this keyword validates if every property name in the instance validates against the provided schema.
Note the property name that the schema is testing will always be a string.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.8)
**Kind**: global function
| Param | Type |
| --- | --- |
| value | <code>FluentSchema</code> |
<a name="prop"></a>
## prop(name, props) ⇒ <code>FluentSchema</code>
The value of "properties" MUST be an object. Each value of this object MUST be a valid JSON Schema.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| name | [<code>string</code>](#string) |
| props | <code>FluentSchema</code> |
<a name="only"></a>
## only(properties) ⇒ [<code>ObjectSchema</code>](#ObjectSchema)
Returns an object schema with only a subset of keys provided. If called on an ObjectSchema with an
`$id`, it will be removed and the return value will be considered a new schema.
**Kind**: global function
| Param | Description |
| --- | --- |
| properties | a list of properties you want to keep |
<a name="without"></a>
## without(properties) ⇒ [<code>ObjectSchema</code>](#ObjectSchema)
Returns an object schema without a subset of keys provided. If called on an ObjectSchema with an
`$id`, it will be removed and the return value will be considered a new schema.
**Kind**: global function
| Param | Description |
| --- | --- |
| properties | a list of properties you dont want to keep |
<a name="definition"></a>
## definition(name, props) ⇒ <code>FluentSchema</code>
The "definitions" keywords provides a standardized location for schema authors to inline re-usable JSON Schemas into a more general schema.
There are no restrictions placed on the values within the array.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.9)
**Kind**: global function
| Param | Type |
| --- | --- |
| name | [<code>string</code>](#string) |
| props | <code>FluentSchema</code> |
<a name="RawSchema"></a>
## RawSchema(schema) ⇒ <code>FluentSchema</code>
Represents a raw JSON Schema that will be parsed
**Kind**: global function
| Param | Type |
| --- | --- |
| schema | <code>Object</code> |
<a name="StringSchema"></a>
## StringSchema([options]) ⇒ [<code>StringSchema</code>](#StringSchema)
Represents a StringSchema.
**Kind**: global function
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | Options |
| [options.schema] | [<code>StringSchema</code>](#StringSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |
<a name="minLength"></a>
## minLength(min) ⇒ [<code>StringSchema</code>](#StringSchema)
A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.2)
**Kind**: global function
| Param | Type |
| --- | --- |
| min | [<code>number</code>](#number) |
<a name="maxLength"></a>
## maxLength(max) ⇒ [<code>StringSchema</code>](#StringSchema)
A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.1)
**Kind**: global function
| Param | Type |
| --- | --- |
| max | [<code>number</code>](#number) |
<a name="format"></a>
## format(format) ⇒ [<code>StringSchema</code>](#StringSchema)
A string value can be RELATIVE_JSON_POINTER, JSON_POINTER, UUID, REGEX, IPV6, IPV4, HOSTNAME, EMAIL, URL, URI_TEMPLATE, URI_REFERENCE, URI, TIME, DATE, DATE_TIME, ISO_TIME, ISO_DATE_TIME.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.7.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| format | [<code>string</code>](#string) |
<a name="pattern"></a>
## pattern(pattern) ⇒ [<code>StringSchema</code>](#StringSchema)
This string SHOULD be a valid regular expression, according to the ECMA 262 regular expression dialect.
A string instance is considered valid if the regular expression matches the instance successfully.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| pattern | [<code>string</code>](#string) |
<a name="contentEncoding"></a>
## contentEncoding(encoding) ⇒ [<code>StringSchema</code>](#StringSchema)
If the instance value is a string, this property defines that the string SHOULD
be interpreted as binary data and decoded using the encoding named by this property.
RFC 2045, Sec 6.1 [RFC2045] lists the possible values for this property.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.8.3)
**Kind**: global function
| Param | Type |
| --- | --- |
| encoding | [<code>string</code>](#string) |
<a name="contentMediaType"></a>
## contentMediaType(mediaType) ⇒ [<code>StringSchema</code>](#StringSchema)
The value of this property must be a media type, as defined by RFC 2046 [RFC2046].
This property defines the media type of instances which this schema defines.
[reference](https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.8.4)
**Kind**: global function
| Param | Type |
| --- | --- |
| mediaType | [<code>string</code>](#string) |
================================================
FILE: eslint.config.js
================================================
'use strict'
module.exports = require('neostandard')({
ignores: require('neostandard').resolveIgnoresFromGitignore(),
ts: true
})
================================================
FILE: package.json
================================================
{
"name": "fluent-json-schema",
"version": "6.0.0",
"description": "JSON Schema fluent API",
"main": "src/FluentJSONSchema.js",
"type": "commonjs",
"types": "types/FluentJSONSchema.d.ts",
"keywords": [
"JSON",
"schema",
"jsonschema",
"json schema",
"validation",
"json schema builder",
"json schema validation"
],
"license": "MIT",
"author": "Lorenzo Sicilia <lorenzo.sicilia@gmail.com>",
"contributors": [
"Matteo Collina <hello@matteocollina.com>"
],
"homepage": "https://github.com/fastify/fluent-json-schema#readme",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"bugs": {
"url": "https://github.com/fastify/fluent-json-schema/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/fluent-json-schema.git"
},
"scripts": {
"lint": "eslint",
"lint:fix": "eslint --fix",
"test": "npm run test:unit && npm run test:typescript",
"test:unit": "c8 --100 node --test",
"test:typescript": "tsd",
"doc": "jsdoc2md ./src/*.js > docs/API.md"
},
"devDependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^3.0.1",
"c8": "^11.0.0",
"eslint": "^9.17.0",
"jsdoc-to-markdown": "^9.0.0",
"lodash.merge": "^4.6.2",
"neostandard": "^0.13.0",
"tsd": "^0.33.0"
},
"dependencies": {
"@fastify/deepmerge": "^3.0.0"
}
}
================================================
FILE: src/ArraySchema.js
================================================
'use strict'
const { BaseSchema } = require('./BaseSchema')
const { setAttribute, isFluentSchema, FluentSchemaError } = require('./utils')
const initialState = {
// $schema: 'http://json-schema.org/draft-07/schema#',
type: 'array',
definitions: [],
properties: [],
required: []
}
/**
* Represents a ArraySchema.
* @param {Object} [options] - Options
* @param {StringSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {ArraySchema}
*/
// https://medium.com/javascript-scene/javascript-factory-functions-with-es6-4d224591a8b1
// Factory Functions for Mixin Composition withBaseSchema
const ArraySchema = ({ schema = initialState, ...options } = {}) => {
options = {
generateIds: false,
factory: ArraySchema,
...options
}
return {
...BaseSchema({ ...options, schema }),
/**
* This keyword determines how child instances validate for arrays, and does not directly validate the immediate instance itself.
* If "items" is a schema, validation succeeds if all elements in the array successfully validate against that schema.
* If "items" is an array of schemas, validation succeeds if each element of the instance validates against the schema at the same position, if any.
* Omitting this keyword has the same behavior as an empty schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.1|reference}
* @param {FluentSchema|FluentSchema[]} items
* @returns {FluentSchema}
*/
items: items => {
if (
!isFluentSchema(items) &&
!(
Array.isArray(items) &&
items.filter(v => isFluentSchema(v)).length > 0
)
) { throw new FluentSchemaError("'items' must be a S or an array of S") }
if (Array.isArray(items)) {
const values = items.map(v => {
const { $schema, ...rest } = v.valueOf()
return rest
})
return setAttribute({ schema, ...options }, ['items', values, 'array'])
}
const { $schema, ...rest } = items.valueOf()
return setAttribute({ schema, ...options }, [
'items',
{ ...rest },
'array'
])
},
/**
* This keyword determines how child instances validate for arrays, and does not directly validate the immediate instance itself.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.2|reference}
* @param {FluentSchema|boolean} items
* @returns {FluentSchema}
*/
additionalItems: items => {
if (typeof items !== 'boolean' && !isFluentSchema(items)) {
throw new FluentSchemaError(
"'additionalItems' must be a boolean or a S"
)
}
if (items === false) {
return setAttribute({ schema, ...options }, [
'additionalItems',
false,
'array'
])
}
const { $schema, ...rest } = items.valueOf()
return setAttribute({ schema, ...options }, [
'additionalItems',
{ ...rest },
'array'
])
},
/**
* An array instance is valid against "contains" if at least one of its elements is valid against the given schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.6|reference}
* @param {FluentSchema} value
* @returns {FluentSchema}
*/
contains: value => {
if (!isFluentSchema(value)) { throw new FluentSchemaError("'contains' must be a S") }
const {
$schema,
definitions,
properties,
required,
...rest
} = value.valueOf()
return setAttribute({ schema, ...options }, [
'contains',
{ ...rest },
'array'
])
},
/**
* If this keyword has boolean value false, the instance validates successfully.
* If it has boolean value true, the instance validates successfully if all of its elements are unique.
* Omitting this keyword has the same behavior as a value of false.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.5|reference}
* @param {boolean} boolean
* @returns {FluentSchema}
*/
uniqueItems: boolean => {
if (typeof boolean !== 'boolean') { throw new FluentSchemaError("'uniqueItems' must be a boolean") }
return setAttribute({ schema, ...options }, [
'uniqueItems',
boolean,
'array'
])
},
/**
* An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
* Omitting this keyword has the same behavior as a value of 0.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.4|reference}
* @param {number} min
* @returns {FluentSchema}
*/
minItems: min => {
if (!Number.isInteger(min)) { throw new FluentSchemaError("'minItems' must be a integer") }
return setAttribute({ schema, ...options }, ['minItems', min, 'array'])
},
/**
* An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
* Omitting this keyword has the same behavior as a value of 0.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4.3|reference}
* @param {number} max
* @returns {FluentSchema}
*/
maxItems: max => {
if (!Number.isInteger(max)) { throw new FluentSchemaError("'maxItems' must be a integer") }
return setAttribute({ schema, ...options }, ['maxItems', max, 'array'])
}
}
}
module.exports = {
ArraySchema,
default: ArraySchema
}
================================================
FILE: src/ArraySchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const { ArraySchema } = require('./ArraySchema')
const S = require('./FluentJSONSchema')
describe('ArraySchema', () => {
it('defined', () => {
assert.notStrictEqual(ArraySchema, undefined)
})
it('Expose symbol', () => {
assert.notStrictEqual(
ArraySchema()[Symbol.for('fluent-schema-object')],
undefined
)
})
describe('constructor', () => {
it('without params', () => {
assert.deepStrictEqual(ArraySchema().valueOf(), {
type: 'array'
})
})
it('from S', () => {
assert.deepStrictEqual(S.array().valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'array'
})
})
})
describe('keywords:', () => {
describe('items', () => {
it('valid object', () => {
assert.deepStrictEqual(ArraySchema().items(S.number()).valueOf(), {
type: 'array',
items: { type: 'number' }
})
})
it('valid array', () => {
assert.deepStrictEqual(
ArraySchema().items([S.number(), S.string()]).valueOf(),
{
type: 'array',
items: [{ type: 'number' }, { type: 'string' }]
}
)
})
it('invalid', () => {
assert.throws(
() => ArraySchema().items(''),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'items' must be a S or an array of S"
)
})
})
describe('additionalItems', () => {
it('valid', () => {
assert.deepStrictEqual(
ArraySchema()
.items([S.number(), S.string()])
.additionalItems(S.string())
.valueOf(),
{
type: 'array',
items: [{ type: 'number' }, { type: 'string' }],
additionalItems: { type: 'string' }
}
)
})
it('false', () => {
assert.deepStrictEqual(
ArraySchema()
.items([S.number(), S.string()])
.additionalItems(false)
.valueOf(),
{
type: 'array',
items: [{ type: 'number' }, { type: 'string' }],
additionalItems: false
}
)
})
it('invalid', () => {
assert.throws(
() => ArraySchema().additionalItems(''),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'additionalItems' must be a boolean or a S"
)
})
})
describe('contains', () => {
it('valid', () => {
assert.deepStrictEqual(ArraySchema().contains(S.string()).valueOf(), {
type: 'array',
contains: { type: 'string' }
})
})
it('invalid', () => {
assert.throws(
() => ArraySchema().contains('').valueOf(),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'contains' must be a S"
)
})
})
describe('uniqueItems', () => {
it('valid', () => {
assert.deepStrictEqual(ArraySchema().uniqueItems(true).valueOf(), {
type: 'array',
uniqueItems: true
})
})
it('invalid', () => {
assert.throws(
() => ArraySchema().uniqueItems('invalid').valueOf(),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'uniqueItems' must be a boolean"
)
})
})
describe('minItems', () => {
it('valid', () => {
assert.deepStrictEqual(ArraySchema().minItems(3).valueOf(), {
type: 'array',
minItems: 3
})
})
it('invalid', () => {
assert.throws(
() => ArraySchema().minItems('3').valueOf(),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'minItems' must be a integer"
)
})
})
describe('maxItems', () => {
it('valid', () => {
assert.deepStrictEqual(ArraySchema().maxItems(5).valueOf(), {
type: 'array',
maxItems: 5
})
})
it('invalid', () => {
assert.throws(
() => ArraySchema().maxItems('5').valueOf(),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'maxItems' must be a integer"
)
})
})
describe('raw', () => {
it('allows to add a custom attribute', () => {
const schema = ArraySchema().raw({ customKeyword: true }).valueOf()
assert.deepStrictEqual(schema, {
type: 'array',
customKeyword: true
})
})
})
describe('default array in an object', () => {
it('valid', () => {
const value = []
assert.deepStrictEqual(
S.object().prop('p1', ArraySchema().default(value)).valueOf()
.properties.p1.default,
value
)
})
})
})
})
================================================
FILE: src/BaseSchema.js
================================================
'use strict'
const {
flat,
omit,
isFluentSchema,
last,
isBoolean,
isUniq,
patchIdsWithParentId,
REQUIRED,
setAttribute,
setRaw,
setComposeType,
FluentSchemaError,
FLUENT_SCHEMA
} = require('./utils')
const initialState = {
properties: [],
required: []
}
/**
* Represents a BaseSchema.
* @param {Object} [options] - Options
* @param {BaseSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {BaseSchema}
*/
const BaseSchema = (
{ schema = initialState, ...options } = {
generateIds: false,
factory: BaseSchema
}
) => ({
[FLUENT_SCHEMA]: true,
isFluentSchema: true,
isFluentJSONSchema: true,
/**
* It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
*
* {@link https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2|reference}
* @param {string} id - an #id
* @returns {BaseSchema}
*/
id: id => {
if (!id) {
throw new FluentSchemaError(
'id should not be an empty fragment <#> or an empty string <> (e.g. #myId)'
)
}
return setAttribute({ schema, ...options }, ['$id', id, 'any'])
},
/**
* It can be used to decorate a user interface with information about the data produced by this user interface. A title will preferably be short.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.1|reference}
* @param {string} title
* @returns {BaseSchema}
*/
title: title => {
return setAttribute({ schema, ...options }, ['title', title, 'any'])
},
/**
* It can be used to decorate a user interface with information about the data
* produced by this user interface. A description provides explanation about
* the purpose of the instance described by the schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.1|reference}
* @param {string} description
* @returns {BaseSchema}
*/
description: description => {
return setAttribute({ schema, ...options }, [
'description',
description,
'any'
])
},
/**
* The value of this keyword MUST be an array.
* There are no restrictions placed on the values within the array.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.4|reference}
* @param {string} examples
* @returns {BaseSchema}
*/
examples: examples => {
if (!Array.isArray(examples)) {
throw new FluentSchemaError(
"'examples' must be an array e.g. ['1', 'one', 'foo']"
)
}
return setAttribute({ schema, ...options }, ['examples', examples, 'any'])
},
/**
* The value must be a valid id e.g. #properties/foo
*
* @param {string} ref
* @returns {BaseSchema}
*/
ref: ref => {
return setAttribute({ schema, ...options }, ['$ref', ref, 'any'])
},
/**
* The value of this keyword MUST be an array. This array SHOULD have at least one element. Elements in the array SHOULD be unique.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.2|reference}
* @param {array} values
* @returns {BaseSchema}
*/
enum: values => {
if (!Array.isArray(values)) {
throw new FluentSchemaError(
"'enums' must be an array with at least an element e.g. ['1', 'one', 'foo']"
)
}
return setAttribute({ schema, ...options }, ['enum', values, 'any'])
},
/**
* The value of this keyword MAY be of any type, including null.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.3|reference}
* @param value
* @returns {BaseSchema}
*/
const: value => {
return setAttribute({ schema, ...options }, ['const', value, 'any'])
},
/**
* There are no restrictions placed on the value of this keyword.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.2|reference}
* @param defaults
* @returns {BaseSchema}
*/
default: defaults => {
return setAttribute({ schema, ...options }, ['default', defaults, 'any'])
},
/**
* The value of readOnly can be left empty to indicate the property is readOnly.
* It takes an optional boolean which can be used to explicitly set readOnly true/false.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.3|reference}
* @param {boolean|undefined} isReadOnly
* @returns {BaseSchema}
*/
readOnly: isReadOnly => {
const value = isReadOnly !== undefined ? isReadOnly : true
return setAttribute({ schema, ...options }, ['readOnly', value, 'boolean'])
},
/**
* The value of writeOnly can be left empty to indicate the property is writeOnly.
* It takes an optional boolean which can be used to explicitly set writeOnly true/false.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.10.3|reference}
* @param {boolean|undefined} isWriteOnly
* @returns {BaseSchema}
*/
writeOnly: isWriteOnly => {
const value = isWriteOnly !== undefined ? isWriteOnly : true
return setAttribute({ schema, ...options }, ['writeOnly', value, 'boolean'])
},
/**
* The value of deprecated can be left empty to indicate the property is deprecated.
* It takes an optional boolean which can be used to explicitly set deprecated true/false.
*
* {@link https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.3|reference}
* @param {Boolean} isDeprecated
* @returns {BaseSchema}
*/
deprecated: (isDeprecated) => {
if (isDeprecated && !isBoolean(isDeprecated)) throw new FluentSchemaError("'deprecated' must be a boolean value")
const value = isDeprecated !== undefined ? isDeprecated : true
return setAttribute({ schema, ...options }, ['deprecated', value, 'boolean'])
},
/**
* Required has to be chained to a property:
* Examples:
* - S.prop('prop').required()
* - S.prop('prop', S.number()).required()
* - S.required(['foo', 'bar'])
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.3|reference}
* @returns {FluentSchema}
*/
required: props => {
const currentProp = last(schema.properties)
const required = Array.isArray(props)
? [...schema.required, ...props]
: currentProp
? [...schema.required, currentProp.name]
: [REQUIRED]
if (!isUniq(required)) {
throw new FluentSchemaError("'required' has repeated keys, check your calls to .required()")
}
return options.factory({
schema: { ...schema, required },
...options
})
},
/**
* This keyword's value MUST be a valid JSON Schema.
* An instance is valid against this keyword if it fails to validate successfully against the schema defined by this keyword.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.4|reference}
* @param {FluentSchema} not
* @returns {BaseSchema}
*/
not: not => {
if (!isFluentSchema(not)) { throw new FluentSchemaError("'not' must be a BaseSchema") }
const notSchema = omit(not.valueOf(), ['$schema', 'definitions'])
return BaseSchema({
schema: {
...schema,
not: patchIdsWithParentId({
schema: notSchema,
...options,
parentId: '#not'
})
},
...options
})
},
// return setAttribute({ schema, ...options }, ['defaults', defaults, 'any'])
/**
* It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.2|reference}
* @param {array} schemas
* @returns {BaseSchema}
*/
anyOf: schemas => setComposeType({ prop: 'anyOf', schemas, schema, options }),
/**
* It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.1|reference}
* @param {array} schemas
* @returns {BaseSchema}
*/
allOf: schemas => setComposeType({ prop: 'allOf', schemas, schema, options }),
/**
* It MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7.3|reference}
* @param {array} schemas
* @returns {BaseSchema}
*/
oneOf: schemas => setComposeType({ prop: 'oneOf', schemas, schema, options }),
/**
* @private set a property to a type. Use string number etc.
* @returns {BaseSchema}
*/
as: type => setAttribute({ schema, ...options }, ['type', type]),
/**
* This validation outcome of this keyword's subschema has no direct effect on the overall validation result.
* Rather, it controls which of the "then" or "else" keywords are evaluated.
* When "if" is present, and the instance successfully validates against its subschema, then
* validation succeeds against this keyword if the instance also successfully validates against this keyword's subschema.
*
* @param {BaseSchema} ifClause
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.1|reference}
* @param {BaseSchema} thenClause
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.2|reference}
* @returns {BaseSchema}
*/
ifThen: (ifClause, thenClause) => {
if (!isFluentSchema(ifClause)) { throw new FluentSchemaError("'ifClause' must be a BaseSchema") }
if (!isFluentSchema(thenClause)) { throw new FluentSchemaError("'thenClause' must be a BaseSchema") }
const ifClauseSchema = omit(ifClause.valueOf(), [
'$schema',
'definitions',
'type'
])
const thenClauseSchema = omit(thenClause.valueOf(), [
'$schema',
'definitions',
'type'
])
return options.factory({
schema: {
...schema,
if: patchIdsWithParentId({
schema: ifClauseSchema,
...options,
parentId: '#if'
}),
then: patchIdsWithParentId({
schema: thenClauseSchema,
...options,
parentId: '#then'
})
},
...options
})
},
/**
* When "if" is present, and the instance fails to validate against its subschema,
* then validation succeeds against this keyword if the instance successfully validates against this keyword's subschema.
*
* @param {BaseSchema} ifClause
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.1|reference}
* @param {BaseSchema} thenClause
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.2|reference}
* @param {BaseSchema} elseClause
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.6.3|reference}
* @returns {BaseSchema}
*/
ifThenElse: (ifClause, thenClause, elseClause) => {
if (!isFluentSchema(ifClause)) { throw new FluentSchemaError("'ifClause' must be a BaseSchema") }
if (!isFluentSchema(thenClause)) { throw new FluentSchemaError("'thenClause' must be a BaseSchema") }
if (!isFluentSchema(elseClause)) {
throw new FluentSchemaError(
"'elseClause' must be a BaseSchema or a false boolean value"
)
}
const ifClauseSchema = omit(ifClause.valueOf(), [
'$schema',
'definitions',
'type'
])
const thenClauseSchema = omit(thenClause.valueOf(), [
'$schema',
'definitions',
'type'
])
const elseClauseSchema = omit(elseClause.valueOf(), [
'$schema',
'definitions',
'type'
])
return options.factory({
schema: {
...schema,
if: patchIdsWithParentId({
schema: ifClauseSchema,
...options,
parentId: '#if'
}),
then: patchIdsWithParentId({
schema: thenClauseSchema,
...options,
parentId: '#then'
}),
else: patchIdsWithParentId({
schema: elseClauseSchema,
...options,
parentId: '#else'
})
},
...options
})
},
/**
* Because the differences between JSON Schemas and Open API (Swagger)
* it can be handy to arbitrary modify the schema injecting a fragment
*
* * Examples:
* - S.number().raw({ nullable:true })
* - S.string().format('date').raw({ formatMaximum: '2020-01-01' })
*
* @param {string} fragment an arbitrary JSON Schema to inject
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3.3|reference}
* @returns {BaseSchema}
*/
raw: fragment => {
return setRaw({ schema, ...options }, fragment)
},
/**
* @private It returns the internal schema data structure
* @returns {object}
*/
// TODO LS if we implement S.raw() we can drop this hack because from a JSON we can rebuild a fluent-json-schema
_getState: () => {
return schema
},
/**
* It returns all the schema values
*
* @param {Object} [options] - Options
* @param {boolean} [options.isRoot = true] - Is a root level schema
* @returns {object}
*/
valueOf: ({ isRoot } = { isRoot: true }) => {
const { properties, definitions, required, $schema, ...rest } = schema
if (isRoot && required && !required.every((v) => typeof v === 'string')) {
throw new FluentSchemaError("'required' has called on root-level schema, check your calls to .required()")
}
return Object.assign(
$schema ? { $schema } : {},
Object.keys(definitions || []).length > 0
? { definitions: flat(definitions) }
: undefined,
{ ...omit(rest, ['if', 'then', 'else']) },
Object.keys(properties || []).length > 0
? { properties: flat(properties) }
: undefined,
required && required.length > 0 ? { required } : undefined,
schema.if ? { if: schema.if } : undefined,
schema.then ? { then: schema.then } : undefined,
schema.else ? { else: schema.else } : undefined
)
}
})
module.exports = {
BaseSchema,
default: BaseSchema
}
================================================
FILE: src/BaseSchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const { BaseSchema } = require('./BaseSchema')
const S = require('./FluentJSONSchema')
describe('BaseSchema', () => {
it('defined', () => {
assert.notStrictEqual(BaseSchema, undefined)
})
it('Expose symbol', () => {
assert.notStrictEqual(
BaseSchema()[Symbol.for('fluent-schema-object')],
undefined
)
})
it('Expose legacy plain boolean', () => {
assert.notStrictEqual(BaseSchema().isFluentSchema, undefined)
})
it('Expose plain boolean', () => {
assert.notStrictEqual(BaseSchema().isFluentJSONSchema, undefined)
})
describe('factory', () => {
it('without params', () => {
assert.deepStrictEqual(BaseSchema().valueOf(), {})
})
describe('factory', () => {
it('default', () => {
const title = 'title'
assert.deepStrictEqual(BaseSchema().title(title).valueOf(), {
title
})
})
it('override', () => {
const title = 'title'
assert.deepStrictEqual(
BaseSchema({ factory: BaseSchema }).title(title).valueOf(),
{
title
}
)
})
})
})
describe('keywords (any):', () => {
describe('id', () => {
const value = 'customId'
it('to root', () => {
assert.strictEqual(BaseSchema().id(value).valueOf().$id, value)
})
it('nested', () => {
assert.strictEqual(
S.object().prop('foo', BaseSchema().id(value).required()).valueOf()
.properties.foo.$id,
value
)
})
it('invalid', () => {
assert.throws(
() => BaseSchema().id(''),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
'id should not be an empty fragment <#> or an empty string <> (e.g. #myId)'
)
})
})
describe('title', () => {
const value = 'title'
it('adds to root', () => {
assert.strictEqual(BaseSchema().title(value).valueOf().title, value)
})
})
describe('description', () => {
it('add to root', () => {
const value = 'description'
assert.strictEqual(
BaseSchema().description(value).valueOf().description,
value
)
})
})
describe('examples', () => {
it('adds to root', () => {
const value = ['example']
assert.deepStrictEqual(
BaseSchema().examples(value).valueOf().examples,
value
)
})
it('invalid', () => {
const value = 'examples'
assert.throws(
() => BaseSchema().examples(value).valueOf().examples,
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'examples' must be an array e.g. ['1', 'one', 'foo']"
)
})
})
describe('required', () => {
it('in line valid', () => {
const prop = 'foo'
assert.deepStrictEqual(
S.object().prop(prop).required().valueOf().required,
[prop]
)
})
it('nested valid', () => {
const prop = 'foo'
assert.deepStrictEqual(
S.object().prop(prop, S.string().required().minLength(3)).valueOf()
.required,
[prop]
)
})
describe('unique keys on required', () => {
it('repeated calls to required()', () => {
assert.throws(
() => S.object().prop('A', S.string()).required().required(),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'required' has repeated keys, check your calls to .required()"
)
})
it('repeated props on appendRequired()', () => {
assert.throws(
() =>
S.object()
.prop('A', S.string().required())
.prop('A', S.string().required()),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'required' has repeated keys, check your calls to .required()"
)
})
})
it('root-level required', () => {
assert.throws(
() => S.object().required().valueOf(),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'required' has called on root-level schema, check your calls to .required()"
)
})
describe('array', () => {
it('simple', () => {
const required = ['foo', 'bar']
assert.deepStrictEqual(S.required(required).valueOf(), {
required
})
})
it('nested', () => {
assert.deepStrictEqual(
S.object()
.prop('foo', S.string())
.prop('bar', S.string().required())
.required(['foo'])
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: { bar: { type: 'string' }, foo: { type: 'string' } },
required: ['bar', 'foo'],
type: 'object'
}
)
})
})
})
describe('deprecated', () => {
it('valid', () => {
assert.strictEqual(
BaseSchema().deprecated(true).valueOf().deprecated,
true
)
})
it('invalid', () => {
assert.throws(
() =>
BaseSchema().deprecated('somethingNotBoolean').valueOf().deprecated,
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'deprecated' must be a boolean value"
)
})
it('valid with no value', () => {
assert.strictEqual(BaseSchema().deprecated().valueOf().deprecated, true)
})
it('can be set to false', () => {
assert.strictEqual(
BaseSchema().deprecated(false).valueOf().deprecated,
false
)
})
it('property', () => {
assert.deepStrictEqual(
S.object()
.prop('foo', S.string())
.prop('bar', S.string().deprecated())
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
bar: { type: 'string', deprecated: true },
foo: { type: 'string' }
},
type: 'object'
}
)
})
it('object', () => {
assert.deepStrictEqual(
S.object()
.prop('foo', S.string())
.prop(
'bar',
S.object()
.deprecated()
.prop('raz', S.string())
.prop('iah', S.number())
)
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
foo: { type: 'string' },
bar: {
type: 'object',
deprecated: true,
properties: {
raz: { $id: undefined, type: 'string' },
iah: { $id: undefined, type: 'number' }
}
}
},
type: 'object'
}
)
})
it('object property', () => {
assert.deepStrictEqual(
S.object()
.prop('foo', S.string())
.prop(
'bar',
S.object()
.prop('raz', S.string().deprecated())
.prop('iah', S.number())
)
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
foo: { type: 'string' },
bar: {
type: 'object',
properties: {
raz: { $id: undefined, type: 'string', deprecated: true },
iah: { $id: undefined, type: 'number' }
}
}
},
type: 'object'
}
)
})
it('array', () => {
assert.deepStrictEqual(
S.object()
.prop('foo', S.string())
.prop('bar', S.array().deprecated().items(S.number()))
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
foo: { type: 'string' },
bar: {
type: 'array',
deprecated: true,
items: { type: 'number' }
}
}
}
)
})
it('array item', () => {
assert.deepStrictEqual(
S.object()
.prop('foo', S.string())
.prop(
'bar',
S.array().items([
S.object().prop('zoo', S.string()).prop('biz', S.string()),
S.object()
.deprecated()
.prop('zal', S.string())
.prop('boz', S.string())
])
)
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
foo: { type: 'string' },
bar: {
type: 'array',
items: [
{
type: 'object',
properties: {
zoo: { type: 'string' },
biz: { type: 'string' }
}
},
{
type: 'object',
deprecated: true,
properties: {
zal: { type: 'string' },
boz: { type: 'string' }
}
}
]
}
}
}
)
})
})
describe('enum', () => {
it('valid', () => {
const value = ['VALUE']
assert.deepStrictEqual(BaseSchema().enum(value).valueOf().enum, value)
})
it('invalid', () => {
const value = 'VALUE'
assert.throws(
() => BaseSchema().enum(value).valueOf().examples,
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'enums' must be an array with at least an element e.g. ['1', 'one', 'foo']"
)
})
})
describe('const', () => {
it('valid', () => {
const value = 'VALUE'
assert.strictEqual(BaseSchema().const(value).valueOf().const, value)
})
})
describe('default', () => {
it('valid', () => {
const value = 'VALUE'
assert.strictEqual(BaseSchema().default(value).valueOf().default, value)
})
})
describe('readOnly', () => {
it('valid', () => {
assert.strictEqual(BaseSchema().readOnly(true).valueOf().readOnly, true)
})
it('valid with no value', () => {
assert.strictEqual(BaseSchema().readOnly().valueOf().readOnly, true)
})
it('can be set to false', () => {
assert.strictEqual(
BaseSchema().readOnly(false).valueOf().readOnly,
false
)
})
})
describe('writeOnly', () => {
it('valid', () => {
assert.strictEqual(
BaseSchema().writeOnly(true).valueOf().writeOnly,
true
)
})
it('valid with no value', () => {
assert.strictEqual(BaseSchema().writeOnly().valueOf().writeOnly, true)
})
it('can be set to false', () => {
assert.strictEqual(
BaseSchema().writeOnly(false).valueOf().writeOnly,
false
)
})
})
describe('ref', () => {
it('base', () => {
const ref = 'myRef'
assert.deepStrictEqual(BaseSchema().ref(ref).valueOf(), { $ref: ref })
})
it('S', () => {
const ref = 'myRef'
assert.deepStrictEqual(S.ref(ref).valueOf(), {
$ref: ref
})
})
})
})
describe('combining keywords:', () => {
describe('allOf', () => {
it('base', () => {
assert.deepStrictEqual(
BaseSchema()
.allOf([BaseSchema().id('foo')])
.valueOf(),
{
allOf: [{ $id: 'foo' }]
}
)
})
it('S', () => {
assert.deepStrictEqual(S.allOf([S.id('foo')]).valueOf(), {
allOf: [{ $id: 'foo' }]
})
})
describe('invalid', () => {
it('not an array', () => {
assert.throws(
() => BaseSchema().allOf('test'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'allOf' must be a an array of FluentSchema rather than a 'string'"
)
})
it('not an array of FluentSchema', () => {
assert.throws(
() => BaseSchema().allOf(['test']),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'allOf' must be a an array of FluentSchema rather than a 'object'"
)
})
})
})
describe('anyOf', () => {
it('valid', () => {
assert.deepStrictEqual(
BaseSchema()
.anyOf([BaseSchema().id('foo')])
.valueOf(),
{
anyOf: [{ $id: 'foo' }]
}
)
})
it('S nested', () => {
assert.deepStrictEqual(
S.object()
.prop('prop', S.anyOf([S.string(), S.null()]))
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: { anyOf: [{ type: 'string' }, { type: 'null' }] }
},
type: 'object'
}
)
})
it('S nested required', () => {
assert.deepStrictEqual(
S.object().prop('prop', S.anyOf([]).required()).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: { prop: {} },
required: ['prop'],
type: 'object'
}
)
})
describe('invalid', () => {
it('not an array', () => {
assert.throws(
() => BaseSchema().anyOf('test'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'anyOf' must be a an array of FluentSchema rather than a 'string'"
)
})
it('not an array of FluentSchema', () => {
assert.throws(
() => BaseSchema().anyOf(['test']),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'anyOf' must be a an array of FluentSchema rather than a 'object'"
)
})
})
})
describe('oneOf', () => {
it('valid', () => {
assert.deepStrictEqual(
BaseSchema()
.oneOf([BaseSchema().id('foo')])
.valueOf(),
{
oneOf: [{ $id: 'foo' }]
}
)
})
describe('invalid', () => {
it('not an array', () => {
assert.throws(
() => BaseSchema().oneOf('test'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'oneOf' must be a an array of FluentSchema rather than a 'string'"
)
})
it('not an array of FluentSchema', () => {
assert.throws(
() => BaseSchema().oneOf(['test']),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'oneOf' must be a an array of FluentSchema rather than a 'object'"
)
})
})
})
describe('not', () => {
describe('valid', () => {
it('simple', () => {
assert.deepStrictEqual(
BaseSchema().not(S.string().maxLength(10)).valueOf(),
{
not: { type: 'string', maxLength: 10 }
}
)
})
it('complex', () => {
assert.deepStrictEqual(
BaseSchema()
.not(BaseSchema().anyOf([BaseSchema().id('foo')]))
.valueOf(),
{
not: { anyOf: [{ $id: 'foo' }] }
}
)
})
// .prop('notTypeKey', S.not(S.string().maxLength(10))) => notTypeKey: { not: { type: 'string', "maxLength": 10 } }
})
it('invalid', () => {
assert.throws(
() => BaseSchema().not(undefined),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'not' must be a BaseSchema"
)
})
})
})
describe('ifThen', () => {
describe('valid', () => {
it('returns a schema', () => {
const id = 'http://foo.com/user'
const schema = BaseSchema()
.id(id)
.title('A User')
.ifThen(BaseSchema().id(id), BaseSchema().description('A User desc'))
.valueOf()
assert.deepStrictEqual(schema, {
$id: 'http://foo.com/user',
title: 'A User',
if: { $id: 'http://foo.com/user' },
then: { description: 'A User desc' }
})
})
it('appends a prop after the clause', () => {
const id = 'http://foo.com/user'
const schema = S.object()
.id(id)
.title('A User')
.prop('bar')
.ifThen(
S.object().prop('foo', S.null()),
S.object().prop('bar', S.string().required())
)
.prop('foo')
.valueOf()
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
$id: 'http://foo.com/user',
title: 'A User',
properties: { bar: {}, foo: {} },
if: { properties: { foo: { $id: undefined, type: 'null' } } },
then: {
properties: { bar: { $id: undefined, type: 'string' } },
required: ['bar']
}
})
})
})
describe('invalid', () => {
it('ifClause', () => {
assert.throws(
() =>
BaseSchema().ifThen(
undefined,
BaseSchema().description('A User desc')
),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'ifClause' must be a BaseSchema"
)
})
it('thenClause', () => {
assert.throws(
() => BaseSchema().ifThen(BaseSchema().id('id'), undefined),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'thenClause' must be a BaseSchema"
)
})
})
})
describe('ifThenElse', () => {
describe('valid', () => {
it('returns a schema', () => {
const id = 'http://foo.com/user'
const schema = BaseSchema()
.id(id)
.title('A User')
.ifThenElse(
BaseSchema().id(id),
BaseSchema().description('then'),
BaseSchema().description('else')
)
.valueOf()
assert.deepStrictEqual(schema, {
$id: 'http://foo.com/user',
title: 'A User',
if: { $id: 'http://foo.com/user' },
then: { description: 'then' },
else: { description: 'else' }
})
})
it('appends a prop after the clause', () => {
const id = 'http://foo.com/user'
const schema = S.object()
.id(id)
.title('A User')
.prop('bar')
.ifThenElse(
S.object().prop('foo', S.null()),
S.object().prop('bar', S.string().required()),
S.object().prop('bar', S.string())
)
.prop('foo')
.valueOf()
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
$id: 'http://foo.com/user',
title: 'A User',
properties: { bar: {}, foo: {} },
if: { properties: { foo: { $id: undefined, type: 'null' } } },
then: {
properties: { bar: { $id: undefined, type: 'string' } },
required: ['bar']
},
else: { properties: { bar: { $id: undefined, type: 'string' } } }
})
})
describe('invalid', () => {
it('ifClause', () => {
assert.throws(
() =>
BaseSchema().ifThenElse(
undefined,
BaseSchema().description('then'),
BaseSchema().description('else')
),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'ifClause' must be a BaseSchema"
)
})
it('thenClause', () => {
assert.throws(
() =>
BaseSchema().ifThenElse(
BaseSchema().id('id'),
undefined,
BaseSchema().description('else')
),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'thenClause' must be a BaseSchema"
)
})
it('elseClause', () => {
assert.throws(
() =>
BaseSchema().ifThenElse(
BaseSchema().id('id'),
BaseSchema().description('then'),
undefined
),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"'elseClause' must be a BaseSchema or a false boolean value"
)
})
})
})
})
describe('raw', () => {
it('allows to add a custom attribute', () => {
const schema = BaseSchema()
.title('foo')
.raw({ customKeyword: true })
.valueOf()
assert.deepStrictEqual(schema, {
title: 'foo',
customKeyword: true
})
})
})
})
================================================
FILE: src/BooleanSchema.js
================================================
'use strict'
const { BaseSchema } = require('./BaseSchema')
const initialState = {
type: 'boolean'
}
/**
* Represents a BooleanSchema.
* @param {Object} [options] - Options
* @param {StringSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {StringSchema}
*/
const BooleanSchema = ({ schema = initialState, ...options } = {}) => {
options = {
generateIds: false,
factory: BaseSchema,
...options
}
return {
...BaseSchema({ ...options, schema })
}
}
module.exports = {
BooleanSchema,
default: BooleanSchema
}
================================================
FILE: src/BooleanSchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const { BooleanSchema } = require('./BooleanSchema')
const S = require('./FluentJSONSchema')
describe('BooleanSchema', () => {
it('defined', () => {
assert.notStrictEqual(BooleanSchema, undefined)
})
it('Expose symbol', () => {
assert.notStrictEqual(
BooleanSchema()[Symbol.for('fluent-schema-object')],
undefined
)
})
describe('constructor', () => {
it('without params', () => {
assert.deepStrictEqual(BooleanSchema().valueOf(), {
type: 'boolean'
})
})
it('from S', () => {
assert.deepStrictEqual(S.boolean().valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'boolean'
})
})
})
it('sets a null type to the prop', () => {
assert.strictEqual(
S.object().prop('prop', S.boolean()).valueOf().properties.prop.type,
'boolean'
)
})
describe('raw', () => {
it('allows to add a custom attribute', () => {
const schema = BooleanSchema().raw({ customKeyword: true }).valueOf()
assert.deepStrictEqual(schema, {
type: 'boolean',
customKeyword: true
})
})
})
})
================================================
FILE: src/FluentJSONSchema.js
================================================
'use strict'
const { FORMATS, TYPES, FluentSchemaError } = require('./utils')
const { BaseSchema } = require('./BaseSchema')
const { NullSchema } = require('./NullSchema')
const { BooleanSchema } = require('./BooleanSchema')
const { StringSchema } = require('./StringSchema')
const { NumberSchema } = require('./NumberSchema')
const { IntegerSchema } = require('./IntegerSchema')
const { ObjectSchema } = require('./ObjectSchema')
const { ArraySchema } = require('./ArraySchema')
const { MixedSchema } = require('./MixedSchema')
const { RawSchema } = require('./RawSchema')
const initialState = {
$schema: 'http://json-schema.org/draft-07/schema#',
definitions: [],
properties: [],
required: []
}
/**
* Represents a S.
* @param {Object} [options] - Options
* @param {S} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {S}
*/
const S = (
{ schema = initialState, ...options } = {
generateIds: false,
factory: BaseSchema
}
) => ({
...BaseSchema({ ...options, schema }),
/**
* Set a property to type string
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.3|reference}
* @returns {StringSchema}
*/
string: () =>
StringSchema({
...options,
schema,
factory: StringSchema
}).as('string'),
/**
* Set a property to type number
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#numeric|reference}
* @returns {NumberSchema}
*/
number: () =>
NumberSchema({
...options,
schema,
factory: NumberSchema
}).as('number'),
/**
* Set a property to type integer
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#numeric|reference}
* @returns {IntegerSchema}
*/
integer: () =>
IntegerSchema({
...options,
schema,
factory: IntegerSchema
}).as('integer'),
/**
* Set a property to type boolean
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.7|reference}
* @returns {BooleanSchema}
*/
boolean: () =>
BooleanSchema({
...options,
schema,
factory: BooleanSchema
}).as('boolean'),
/**
* Set a property to type array
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.4|reference}
* @returns {ArraySchema}
*/
array: () =>
ArraySchema({
...options,
schema,
factory: ArraySchema
}).as('array'),
/**
* Set a property to type object
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5|reference}
* @returns {ObjectSchema}
*/
object: baseSchema =>
ObjectSchema({
...options,
schema: baseSchema || schema,
factory: ObjectSchema
}).as('object'),
/**
* Set a property to type null
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#general|reference}
* @returns {NullSchema}
*/
null: () =>
NullSchema({
...options,
schema,
factory: NullSchema
}).null(),
/**
* A mixed schema is the union of multiple types (e.g. ['string', 'integer']
*
* @param {Array.<string>} types
* @returns {MixedSchema}
*/
mixed: types => {
if (
!Array.isArray(types) ||
(Array.isArray(types) &&
types.filter(t => !Object.values(TYPES).includes(t)).length > 0)
) {
throw new FluentSchemaError(
`Invalid 'types'. It must be an array of types. Valid types are ${Object.values(
TYPES
).join(' | ')}`
)
}
return MixedSchema({
...options,
schema: {
...schema,
type: types
},
factory: MixedSchema
})
},
/**
* Because the differences between JSON Schemas and Open API (Swagger)
* it can be handy to arbitrary modify the schema injecting a fragment
*
* * Examples:
* - S.raw({ nullable:true, format: 'date', formatMaximum: '2020-01-01' })
* - S.string().format('date').raw({ formatMaximum: '2020-01-01' })
*
* @param {string} fragment an arbitrary JSON Schema to inject
* @returns {BaseSchema}
*/
raw: fragment => {
return RawSchema(fragment)
}
})
const fluentSchema = {
...BaseSchema(),
FORMATS,
TYPES,
FluentSchemaError,
withOptions: S,
string: () => S().string(),
mixed: types => S().mixed(types),
object: () => S().object(),
array: () => S().array(),
boolean: () => S().boolean(),
integer: () => S().integer(),
number: () => S().number(),
null: () => S().null(),
raw: fragment => S().raw(fragment)
}
module.exports = fluentSchema
module.exports.default = fluentSchema
module.exports.S = fluentSchema
================================================
FILE: src/FluentSchema.integration.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const Ajv = require('ajv')
const basic = require('./schemas/basic')
const S = require('./FluentJSONSchema')
// TODO pick some ideas from here:https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/main/tests/draft7
describe('S', () => {
it('compiles', () => {
const ajv = new Ajv()
const schema = S.valueOf()
const validate = ajv.compile(schema)
const valid = validate({})
assert.ok(valid)
})
describe('basic', () => {
const ajv = new Ajv()
const schema = S.object()
.prop('username', S.string())
.prop('password', S.string())
.valueOf()
const validate = ajv.compile(schema)
it('valid', () => {
const valid = validate({
username: 'username',
password: 'password'
})
assert.ok(valid)
})
it('invalid', () => {
const valid = validate({
username: 'username',
password: 1
})
assert.deepStrictEqual(validate.errors, [
{
instancePath: '/password',
keyword: 'type',
message: 'must be string',
params: { type: 'string' },
schemaPath: '#/properties/password/type'
}
])
assert.ok(!valid)
})
})
describe('ifThen', () => {
const ajv = new Ajv()
const schema = S.object()
.prop('prop', S.string().maxLength(5))
.ifThen(
S.object().prop('prop', S.string().maxLength(5)),
S.object().prop('extraProp', S.string()).required()
)
.valueOf()
const validate = ajv.compile(schema)
it('valid', () => {
const valid = validate({
prop: '12345',
extraProp: 'foo'
})
assert.ok(valid)
})
it('invalid', () => {
const valid = validate({
prop: '12345'
})
assert.deepStrictEqual(validate.errors, [
{
instancePath: '',
keyword: 'required',
message: "must have required property 'extraProp'",
params: { missingProperty: 'extraProp' },
schemaPath: '#/then/required'
}
])
assert.ok(!valid)
})
})
describe('ifThenElse', () => {
const ajv = new Ajv()
const VALUES = ['ONE', 'TWO']
const schema = S.object()
.prop('ifProp')
.ifThenElse(
S.object().prop('ifProp', S.string().enum([VALUES[0]])),
S.object().prop('thenProp', S.string()).required(),
S.object().prop('elseProp', S.string()).required()
)
.valueOf()
const validate = ajv.compile(schema)
it('then', () => {
const valid = validate({
ifProp: 'ONE',
thenProp: 'foo'
})
assert.ok(valid)
})
it('else', () => {
const valid = validate({
prop: '123456'
})
assert.deepStrictEqual(validate.errors, [
{
instancePath: '',
keyword: 'required',
message: "must have required property 'thenProp'",
params: { missingProperty: 'thenProp' },
schemaPath: '#/then/required'
}
])
assert.ok(!valid)
})
})
describe('combine and definition', () => {
const ajv = new Ajv()
const schema = S.object() // FIXME LS it shouldn't be object()
.definition(
'address',
S.object()
.id('#/definitions/address')
.prop('street_address', S.string())
.required()
.prop('city', S.string())
.required()
.prop('state', S.string().required())
)
.allOf([
S.ref('#/definitions/address'),
S.object().prop('type', S.string()).enum(['residential', 'business'])
])
.valueOf()
const validate = ajv.compile(schema)
it('matches', () => {
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
definitions: {
address: {
$id: '#/definitions/address',
type: 'object',
properties: {
street_address: { type: 'string' },
city: { type: 'string' },
state: { type: 'string' }
},
required: ['street_address', 'city', 'state']
}
},
allOf: [
{ $ref: '#/definitions/address' },
{
type: 'object',
properties: {
type: { type: 'string', enum: ['residential', 'business'] }
}
}
]
})
})
it('valid', () => {
const valid = validate({
street_address: 'via Paolo Rossi',
city: 'Topolinia',
state: 'Disney World',
type: 'business'
})
assert.strictEqual(validate.errors, null)
assert.ok(valid)
})
})
// https://github.com/fastify/fluent-json-schema/pull/40
describe('cloning objects retains boolean', () => {
const ajv = new Ajv()
const config = {
schema: S.object().prop('foo', S.string().enum(['foo']))
}
const _config = require('lodash.merge')({}, config)
const schema = _config.schema.valueOf()
const validate = ajv.compile(schema)
it('matches', () => {
assert.notStrictEqual(
config.schema[Symbol.for('fluent-schema-object')],
undefined
)
assert.ok(_config.schema.isFluentJSONSchema)
assert.strictEqual(
_config.schema[Symbol.for('fluent-schema-object')],
undefined
)
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
foo: {
type: 'string',
enum: ['foo']
}
}
})
})
it('valid', () => {
const valid = validate({ foo: 'foo' })
assert.strictEqual(validate.errors, null)
assert.ok(valid)
})
})
describe('compose keywords', () => {
const ajv = new Ajv()
const schema = S.object()
.prop('foo', S.anyOf([S.string()]))
.prop('bar', S.not(S.anyOf([S.integer()])))
.prop('prop', S.allOf([S.string(), S.boolean()]))
.prop('anotherProp', S.oneOf([S.string(), S.boolean()]))
.required()
.valueOf()
const validate = ajv.compile(schema)
it('valid', () => {
const valid = validate({
foo: 'foo',
anotherProp: true
})
assert.ok(valid)
})
it('invalid', () => {
const valid = validate({
foo: 'foo',
bar: 1
})
assert.ok(!valid)
})
})
describe('compose ifThen', () => {
const ajv = new Ajv()
const schema = S.object()
.prop('foo', S.string().default(false).required())
.prop('bar', S.string().default(false).required())
.prop('thenFooA', S.string())
.prop('thenFooB', S.string())
.allOf([
S.ifThen(
S.object().prop('foo', S.string()).enum(['foo']),
S.required(['thenFooA', 'thenFooB'])
),
S.ifThen(
S.object().prop('bar', S.string()).enum(['BAR']),
S.required(['thenBarA', 'thenBarB'])
)
])
.valueOf()
const validate = ajv.compile(schema)
it('matches', () => {
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
allOf: [
{
if: {
properties: {
foo: { $id: undefined, enum: ['foo'], type: 'string' }
}
},
then: { required: ['thenFooA', 'thenFooB'] }
},
{
if: {
properties: {
bar: { $id: undefined, enum: ['BAR'], type: 'string' }
}
},
then: { required: ['thenBarA', 'thenBarB'] }
}
],
properties: {
bar: { default: false, type: 'string' },
foo: { default: false, type: 'string' },
thenFooA: { type: 'string' },
thenFooB: { type: 'string' }
},
required: ['foo', 'bar'],
type: 'object'
})
})
it('valid', () => {
const valid = validate({
foo: 'foo',
thenFooA: 'thenFooA',
thenFooB: 'thenFooB',
bar: 'BAR',
thenBarA: 'thenBarA',
thenBarB: 'thenBarB'
})
assert.strictEqual(validate.errors, null)
assert.ok(valid)
})
})
describe('complex', () => {
const ajv = new Ajv()
const schema = S.object()
.id('http://foo.com/user')
.title('A User')
.description('A User desc')
.definition(
'address',
S.object()
.id('#address')
.prop('country', S.string())
.prop('city', S.string())
.prop('zipcode', S.string())
)
.prop('username', S.string())
.required()
.prop('password', S.string().required())
.prop('address', S.object().ref('#address'))
.required()
.prop(
'role',
S.object()
.id('http://foo.com/role')
.required()
.prop('name', S.string())
.prop('permissions')
)
.prop('age', S.number())
.valueOf()
const validate = ajv.compile(schema)
it('valid', () => {
const valid = validate({
username: 'aboutlo',
password: 'pwsd',
address: {
country: 'Italy',
city: 'Milan',
zipcode: '20100'
},
role: {
name: 'admin',
permissions: 'read:write'
},
age: 30
})
assert.ok(valid)
})
describe('invalid', () => {
const model = {
username: 'aboutlo',
password: 'pswd',
address: {
country: 'Italy',
city: 'Milan',
zipcode: '20100'
},
role: {
name: 'admin',
permissions: 'read:write'
},
age: 30
}
it('password', () => {
const { password, ...data } = model
const valid = validate(data)
assert.deepStrictEqual(validate.errors, [
{
instancePath: '',
keyword: 'required',
message: "must have required property 'password'",
params: { missingProperty: 'password' },
schemaPath: '#/required'
}
])
assert.ok(!valid)
})
it('address', () => {
const { address, ...data } = model
const valid = validate({
...data,
address: {
...address,
city: 1234
}
})
assert.deepStrictEqual(validate.errors, [
{
instancePath: '/address/city',
keyword: 'type',
message: 'must be string',
params: { type: 'string' },
schemaPath: '#address/properties/city/type'
}
])
assert.ok(!valid)
})
})
})
describe('basic.json', () => {
it('generate', () => {
const [step] = basic
assert.deepStrictEqual(
S.array()
.title('Product set')
.items(
S.object()
.title('Product')
.prop(
'uuid',
S.number()
.description('The unique identifier for a product')
.required()
)
.prop('name', S.string())
.required()
.prop('price', S.number().exclusiveMinimum(0).required())
.prop(
'tags',
S.array().items(S.string()).minItems(1).uniqueItems(true)
)
.prop(
'dimensions',
S.object()
.prop('length', S.number().required())
.prop('width', S.number().required())
.prop('height', S.number().required())
)
.prop(
'warehouseLocation',
S.string().description(
'Coordinates of the warehouse with the product'
)
)
)
.valueOf(),
{
...step.schema,
items: {
...step.schema.items,
properties: {
...step.schema.items.properties,
dimensions: {
...step.schema.items.properties.dimensions,
properties: {
length: { $id: undefined, type: 'number' },
width: { $id: undefined, type: 'number' },
height: { $id: undefined, type: 'number' }
}
}
}
}
}
)
})
})
describe('raw', () => {
describe('swaggger', () => {
describe('nullable', () => {
it('allows nullable', () => {
const ajv = new Ajv()
const schema = S.object()
.prop('foo', S.raw({ nullable: true, type: 'string' }))
.valueOf()
const validate = ajv.compile(schema)
const valid = validate({
test: null
})
assert.strictEqual(validate.errors, null)
assert.ok(valid)
})
})
})
describe('ajv', () => {
describe('formatMaximum', () => {
it('checks custom keyword formatMaximum', () => {
const ajv = new Ajv()
require('ajv-formats')(ajv)
/* const schema = S.string()
.raw({ nullable: false })
.valueOf() */
// { type: 'number', nullable: true }
const schema = S.object()
.prop(
'birthday',
S.raw({
format: 'date',
formatMaximum: '2020-01-01',
type: 'string'
})
)
.valueOf()
const validate = ajv.compile(schema)
const valid = validate({
birthday: '2030-01-01'
})
assert.deepStrictEqual(validate.errors, [
{
instancePath: '/birthday',
keyword: 'formatMaximum',
message: 'should be <= 2020-01-01',
params: {
comparison: '<=',
limit: '2020-01-01'
},
schemaPath: '#/properties/birthday/formatMaximum'
}
])
assert.ok(!valid)
})
it('checks custom keyword larger with $data', () => {
const ajv = new Ajv({ $data: true })
require('ajv-formats')(ajv)
/* const schema = S.string()
.raw({ nullable: false })
.valueOf() */
// { type: 'number', nullable: true }
const schema = S.object()
.prop('smaller', S.number().raw({ maximum: { $data: '1/larger' } }))
.prop('larger', S.number())
.valueOf()
const validate = ajv.compile(schema)
const valid = validate({
smaller: 10,
larger: 7
})
assert.deepStrictEqual(validate.errors, [
{
instancePath: '/smaller',
keyword: 'maximum',
message: 'must be <= 7',
params: {
comparison: '<=',
limit: 7
},
schemaPath: '#/properties/smaller/maximum'
}
])
assert.ok(!valid)
})
})
})
})
})
================================================
FILE: src/FluentSchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const S = require('./FluentJSONSchema')
describe('S', () => {
it('defined', () => {
assert.notStrictEqual(S, undefined)
})
describe('factory', () => {
it('without params', () => {
assert.deepStrictEqual(S.object().valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object'
})
})
describe('generatedIds', () => {
describe('properties', () => {
it('true', () => {
assert.deepStrictEqual(
S.withOptions({ generateIds: true })
.object()
.prop('prop', S.string())
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: { prop: { $id: '#properties/prop', type: 'string' } },
type: 'object'
}
)
})
it('false', () => {
assert.deepStrictEqual(
S.object().prop('prop', S.string()).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: { prop: { type: 'string' } },
type: 'object'
}
)
})
describe('nested', () => {
it('true', () => {
assert.deepStrictEqual(
S.withOptions({ generateIds: true })
.object()
.prop('foo', S.object().prop('bar', S.string()).required())
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
foo: {
$id: '#properties/foo',
properties: {
bar: {
$id: '#properties/foo/properties/bar',
type: 'string'
}
},
required: ['bar'],
type: 'object'
}
},
type: 'object'
}
)
})
it('false', () => {
const id = 'myId'
assert.deepStrictEqual(
S.object()
.prop(
'foo',
S.object()
.prop('bar', S.string().id(id))
.required()
)
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
foo: {
properties: {
bar: { $id: 'myId', type: 'string' }
},
required: ['bar'],
type: 'object'
}
},
type: 'object'
}
)
})
})
})
// TODO LS not sure the test makes sense
describe('definitions', () => {
it('true', () => {
assert.deepStrictEqual(
S.withOptions({ generateIds: true })
.object()
.definition(
'entity',
S.object().prop('foo', S.string()).prop('bar', S.string())
)
.prop('prop')
.ref('entity')
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
definitions: {
entity: {
$id: '#definitions/entity',
properties: {
bar: {
type: 'string'
},
foo: {
type: 'string'
}
},
type: 'object'
}
},
properties: {
prop: {
$ref: 'entity'
}
},
type: 'object'
}
)
})
it('false', () => {
assert.deepStrictEqual(
S.withOptions({ generateIds: false })
.object()
.definition(
'entity',
S.object().id('myCustomId').prop('foo', S.string())
)
.prop('prop')
.ref('entity')
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
definitions: {
entity: {
$id: 'myCustomId',
properties: {
foo: { type: 'string' }
},
type: 'object'
}
},
properties: {
prop: {
$ref: 'entity'
}
},
type: 'object'
}
)
})
it('nested', () => {
const id = 'myId'
assert.deepStrictEqual(
S.object()
.prop('foo', S.object().prop('bar', S.string().id(id)).required())
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
foo: {
properties: {
bar: { $id: 'myId', type: 'string' }
},
required: ['bar'],
type: 'object'
}
},
type: 'object'
}
)
})
})
})
})
describe('composition', () => {
it('anyOf', () => {
const schema = S.object()
.prop('foo', S.anyOf([S.string()]))
.valueOf()
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
properties: { foo: { anyOf: [{ type: 'string' }] } },
type: 'object'
})
})
it('oneOf', () => {
const schema = S.object()
.prop(
'multipleRestrictedTypesKey',
S.oneOf([S.string(), S.number().minimum(10)])
)
.prop('notTypeKey', S.not(S.oneOf([S.string().pattern('js$')])))
.valueOf()
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
multipleRestrictedTypesKey: {
oneOf: [{ type: 'string' }, { minimum: 10, type: 'number' }]
},
notTypeKey: { not: { oneOf: [{ pattern: 'js$', type: 'string' }] } }
},
type: 'object'
})
})
})
it('valueOf', () => {
assert.deepStrictEqual(S.object().prop('foo', S.string()).valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
properties: { foo: { type: 'string' } },
type: 'object'
})
})
it('works', () => {
const schema = S.object()
.id('http://foo.com/user')
.title('A User')
.description('A User desc')
.definition(
'address',
S.object()
.id('#address')
.prop('country', S.string())
.prop('city', S.string())
.prop('zipcode', S.string())
)
.prop('username', S.string())
.required()
.prop('password', S.string())
.required()
.prop('address', S.ref('#address'))
.required()
.prop(
'role',
S.object()
.id('http://foo.com/role')
.prop('name', S.string())
.prop('permissions', S.string())
)
.required()
.prop('age', S.number())
.valueOf()
assert.deepStrictEqual(schema, {
definitions: {
address: {
type: 'object',
$id: '#address',
properties: {
country: {
type: 'string'
},
city: {
type: 'string'
},
zipcode: {
type: 'string'
}
}
}
},
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
required: ['username', 'password', 'address', 'role'],
$id: 'http://foo.com/user',
title: 'A User',
description: 'A User desc',
properties: {
username: {
type: 'string'
},
password: {
type: 'string'
},
address: {
$ref: '#address'
},
age: {
type: 'number'
},
role: {
type: 'object',
$id: 'http://foo.com/role',
properties: {
name: {
$id: undefined,
type: 'string'
},
permissions: {
$id: undefined,
type: 'string'
}
}
}
}
})
})
describe('raw', () => {
describe('base', () => {
it('parses type', () => {
const input = S.enum(['foo']).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
it('adds an attribute', () => {
const input = S.enum(['foo']).valueOf()
const schema = S.raw(input)
const attribute = 'title'
const modified = schema.title(attribute)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(modified.valueOf(), {
...input,
title: attribute
})
})
})
describe('string', () => {
it('parses type', () => {
const input = S.string().valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
it('adds an attribute', () => {
const input = S.string().valueOf()
const schema = S.raw(input)
const modified = schema.minLength(3)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(modified.valueOf(), {
minLength: 3,
...input
})
})
it('parses a prop', () => {
const input = S.string().minLength(5).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
})
describe('number', () => {
it('parses type', () => {
const input = S.number().valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
it('adds an attribute', () => {
const input = S.number().valueOf()
const schema = S.raw(input)
const modified = schema.maximum(3)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(modified.valueOf(), {
maximum: 3,
...input
})
})
it('parses a prop', () => {
const input = S.number().maximum(5).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
})
describe('integer', () => {
it('parses type', () => {
const input = S.integer().valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
it('adds an attribute', () => {
const input = S.integer().valueOf()
const schema = S.raw(input)
const modified = schema.maximum(3)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(modified.valueOf(), {
maximum: 3,
...input
})
})
it('parses a prop', () => {
const input = S.integer().maximum(5).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
})
describe('boolean', () => {
it('parses type', () => {
const input = S.boolean().valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
})
describe('object', () => {
it('parses type', () => {
const input = S.object().valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
it('parses properties', () => {
const input = S.object().prop('foo').prop('bar', S.string()).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
it('parses nested properties', () => {
const input = S.object()
.prop('foo', S.object().prop('bar', S.string().minLength(3)))
.valueOf()
const schema = S.raw(input)
const modified = schema.prop('boom')
assert.ok(modified.isFluentSchema)
assert.deepStrictEqual(modified.valueOf(), {
...input,
properties: {
...input.properties,
boom: {}
}
})
})
it('parses definitions', () => {
const input = S.object().definition('foo', S.string()).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
})
describe('array', () => {
it('parses type', () => {
const input = S.array().items(S.string()).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
it('parses properties', () => {
const input = S.array().items(S.string()).valueOf()
const schema = S.raw(input).maxItems(1)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input,
maxItems: 1
})
})
it('parses nested properties', () => {
const input = S.array()
.items(
S.object().prop(
'foo',
S.object().prop('bar', S.string().minLength(3))
)
)
.valueOf()
const schema = S.raw(input)
const modified = schema.maxItems(1)
assert.ok(modified.isFluentSchema)
assert.deepStrictEqual(modified.valueOf(), {
...input,
maxItems: 1
})
})
it('parses definitions', () => {
const input = S.object().definition('foo', S.string()).valueOf()
const schema = S.raw(input)
assert.ok(schema.isFluentSchema)
assert.deepStrictEqual(schema.valueOf(), {
...input
})
})
})
})
})
================================================
FILE: src/IntegerSchema.js
================================================
'use strict'
const { NumberSchema } = require('./NumberSchema')
const initialState = {
type: 'integer'
}
/**
* Represents a NumberSchema.
* @param {Object} [options] - Options
* @param {NumberSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {NumberSchema}
*/
// https://medium.com/javascript-scene/javascript-factory-functions-with-es6-4d224591a8b1
// Factory Functions for Mixin Composition withBaseSchema
const IntegerSchema = (
{ schema, ...options } = {
schema: initialState,
generateIds: false,
factory: IntegerSchema
}
) => ({
...NumberSchema({ ...options, schema })
})
module.exports = {
IntegerSchema,
default: IntegerSchema
}
================================================
FILE: src/IntegerSchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const { IntegerSchema } = require('./IntegerSchema')
const S = require('./FluentJSONSchema')
describe('IntegerSchema', () => {
it('defined', () => {
assert.notStrictEqual(IntegerSchema, undefined)
})
it('Expose symbol', () => {
assert.notStrictEqual(
IntegerSchema()[Symbol.for('fluent-schema-object')],
undefined
)
})
describe('constructor', () => {
it('without params', () => {
assert.deepStrictEqual(IntegerSchema().valueOf(), {
type: 'integer'
})
})
it('from S', () => {
assert.deepStrictEqual(S.integer().valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'integer'
})
})
})
describe('keywords:', () => {
describe('minimum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.integer().minimum(5)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'integer',
minimum: 5
}
},
type: 'object'
}
)
})
it('invalid number', () => {
assert.throws(
() => S.integer().minimum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'minimum' must be a Number"
)
})
it('invalid integer', () => {
assert.throws(
() => S.integer().minimum(5.1),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'minimum' must be an Integer"
)
})
})
describe('maximum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.integer().maximum(5)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'integer',
maximum: 5
}
},
type: 'object'
}
)
})
it('invalid number', () => {
assert.throws(
() => S.integer().maximum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'maximum' must be a Number"
)
})
it('invalid float', () => {
assert.throws(
() => S.integer().maximum(5.1),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'maximum' must be an Integer"
)
})
})
describe('multipleOf', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.integer().multipleOf(5)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'integer',
multipleOf: 5
}
},
type: 'object'
}
)
})
it('invalid value', () => {
assert.throws(
() => S.integer().multipleOf('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'multipleOf' must be a Number"
)
})
it('invalid integer', () => {
assert.throws(
() => S.integer().multipleOf(5.1),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'multipleOf' must be an Integer"
)
})
})
describe('exclusiveMinimum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.integer().exclusiveMinimum(5)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'integer',
exclusiveMinimum: 5
}
},
type: 'object'
}
)
})
it('invalid number', () => {
assert.throws(
() => S.integer().exclusiveMinimum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'exclusiveMinimum' must be a Number"
)
})
it('invalid integer', () => {
assert.throws(
() => S.integer().exclusiveMinimum(5.1),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'exclusiveMinimum' must be an Integer"
)
})
})
describe('exclusiveMaximum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.integer().exclusiveMaximum(5)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'integer',
exclusiveMaximum: 5
}
},
type: 'object'
}
)
})
it('invalid number', () => {
assert.throws(
() => S.integer().exclusiveMaximum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'exclusiveMaximum' must be a Number"
)
})
it('invalid integer', () => {
assert.throws(
() => S.integer().exclusiveMaximum(5.1),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'exclusiveMaximum' must be an Integer"
)
})
})
})
describe('raw', () => {
it('allows to add a custom attribute', () => {
const schema = IntegerSchema().raw({ customKeyword: true }).valueOf()
assert.deepStrictEqual(schema, {
type: 'integer',
customKeyword: true
})
})
})
it('works', () => {
const schema = S.object()
.id('http://foo.com/user')
.title('A User')
.description('A User desc')
.prop('age', S.integer().maximum(10))
.valueOf()
assert.deepStrictEqual(schema, {
$id: 'http://foo.com/user',
$schema: 'http://json-schema.org/draft-07/schema#',
description: 'A User desc',
properties: { age: { maximum: 10, type: 'integer' } },
title: 'A User',
type: 'object'
})
})
})
================================================
FILE: src/MixedSchema.js
================================================
'use strict'
const { NullSchema } = require('./NullSchema')
const { BooleanSchema } = require('./BooleanSchema')
const { StringSchema } = require('./StringSchema')
const { NumberSchema } = require('./NumberSchema')
const { IntegerSchema } = require('./IntegerSchema')
const { ObjectSchema } = require('./ObjectSchema')
const { ArraySchema } = require('./ArraySchema')
const { TYPES, FLUENT_SCHEMA } = require('./utils')
const initialState = {
type: [],
definitions: [],
properties: [],
required: []
}
/**
* Represents a MixedSchema.
* @param {Object} [options] - Options
* @param {MixedSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {StringSchema}
*/
const MixedSchema = ({ schema = initialState, ...options } = {}) => {
options = {
generateIds: false,
factory: MixedSchema,
...options
}
return {
[FLUENT_SCHEMA]: true,
...(schema.type.includes(TYPES.STRING)
? StringSchema({ ...options, schema, factory: MixedSchema })
: {}),
...(schema.type.includes(TYPES.NUMBER)
? NumberSchema({ ...options, schema, factory: MixedSchema })
: {}),
...(schema.type.includes(TYPES.BOOLEAN)
? BooleanSchema({ ...options, schema, factory: MixedSchema })
: {}),
...(schema.type.includes(TYPES.INTEGER)
? IntegerSchema({ ...options, schema, factory: MixedSchema })
: {}),
...(schema.type.includes(TYPES.OBJECT)
? ObjectSchema({ ...options, schema, factory: MixedSchema })
: {}),
...(schema.type.includes(TYPES.ARRAY)
? ArraySchema({ ...options, schema, factory: MixedSchema })
: {}),
...(schema.type.includes(TYPES.NULL)
? NullSchema({ ...options, schema, factory: MixedSchema })
: {})
}
}
module.exports = {
MixedSchema,
default: MixedSchema
}
================================================
FILE: src/MixedSchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const { MixedSchema } = require('./MixedSchema')
const S = require('./FluentJSONSchema')
describe('MixedSchema', () => {
it('defined', () => {
assert.notStrictEqual(MixedSchema, undefined)
})
it('Expose symbol / 1', () => {
assert.notStrictEqual(
MixedSchema()[Symbol.for('fluent-schema-object')],
undefined
)
})
it('Expose symbol / 2', () => {
const types = [
S.TYPES.STRING,
S.TYPES.NUMBER,
S.TYPES.BOOLEAN,
S.TYPES.INTEGER,
S.TYPES.OBJECT,
S.TYPES.ARRAY,
S.TYPES.NULL
]
assert.notStrictEqual(
MixedSchema(types)[Symbol.for('fluent-schema-object')],
undefined
)
})
describe('factory', () => {
it('without params', () => {
assert.deepStrictEqual(MixedSchema().valueOf(), {
[Symbol.for('fluent-schema-object')]: true
})
})
})
describe('from S', () => {
it('valid', () => {
const types = [
S.TYPES.STRING,
S.TYPES.NUMBER,
S.TYPES.BOOLEAN,
S.TYPES.INTEGER,
S.TYPES.OBJECT,
S.TYPES.ARRAY,
S.TYPES.NULL
]
assert.deepStrictEqual(S.mixed(types).valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
type: types
})
})
it('invalid param', () => {
const types = ''
assert.throws(
() => S.mixed(types),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"Invalid 'types'. It must be an array of types. Valid types are string | number | boolean | integer | object | array | null"
)
})
it('invalid type', () => {
const types = ['string', 'invalid']
assert.throws(
() => S.mixed(types),
(err) =>
err instanceof S.FluentSchemaError &&
err.message ===
"Invalid 'types'. It must be an array of types. Valid types are string | number | boolean | integer | object | array | null"
)
})
})
it('sets a type object to the prop', () => {
assert.deepStrictEqual(
S.object()
.prop(
'prop',
S.mixed([S.TYPES.STRING, S.TYPES.NUMBER]).minimum(10).maxLength(5)
)
.valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: { maxLength: 5, minimum: 10, type: ['string', 'number'] }
},
type: 'object'
}
)
})
describe('raw', () => {
it('allows to add a custom attribute', () => {
const types = [S.TYPES.STRING, S.TYPES.NUMBER]
const schema = S.mixed(types).raw({ customKeyword: true }).valueOf()
assert.deepStrictEqual(schema, {
$schema: 'http://json-schema.org/draft-07/schema#',
type: ['string', 'number'],
customKeyword: true
})
})
})
})
================================================
FILE: src/NullSchema.js
================================================
'use strict'
const { BaseSchema } = require('./BaseSchema')
const { setAttribute, FLUENT_SCHEMA } = require('./utils')
const initialState = {
type: 'null'
}
/**
* Represents a NullSchema.
* @param {Object} [options] - Options
* @param {StringSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {StringSchema}
*/
const NullSchema = ({ schema = initialState, ...options } = {}) => {
options = {
generateIds: false,
factory: NullSchema,
...options
}
const { valueOf, raw } = BaseSchema({ ...options, schema })
return {
valueOf,
raw,
[FLUENT_SCHEMA]: true,
isFluentSchema: true,
/**
* Set a property to type null
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.1.1|reference}
* @returns {FluentSchema}
*/
null: () => setAttribute({ schema, ...options }, ['type', 'null'])
}
}
module.exports = {
NullSchema,
default: NullSchema
}
================================================
FILE: src/NullSchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const { NullSchema } = require('./NullSchema')
const S = require('./FluentJSONSchema')
describe('NullSchema', () => {
it('defined', () => {
assert.notStrictEqual(NullSchema, undefined)
})
it('Expose symbol', () => {
assert.notStrictEqual(
NullSchema()[Symbol.for('fluent-schema-object')],
undefined
)
})
describe('constructor', () => {
it('without params', () => {
assert.deepStrictEqual(NullSchema().valueOf(), {
type: 'null'
})
})
it('from S', () => {
assert.deepStrictEqual(S.null().valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'null'
})
})
})
it('sets a null type to the prop', () => {
assert.strictEqual(
S.object().prop('prop', S.null()).valueOf().properties.prop.type,
'null'
)
})
describe('raw', () => {
it('allows to add a custom attribute', () => {
const schema = NullSchema().raw({ customKeyword: true }).valueOf()
assert.deepStrictEqual(schema, {
type: 'null',
customKeyword: true
})
})
})
})
================================================
FILE: src/NumberSchema.js
================================================
'use strict'
const { BaseSchema } = require('./BaseSchema')
const { setAttribute, FluentSchemaError } = require('./utils')
const initialState = {
type: 'number'
}
/**
* Represents a NumberSchema.
* @param {Object} [options] - Options
* @param {NumberSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {NumberSchema}
*/
// https://medium.com/javascript-scene/javascript-factory-functions-with-es6-4d224591a8b1
// Factory Functions for Mixin Composition withBaseSchema
const NumberSchema = (
{ schema, ...options } = {
schema: initialState,
generateIds: false,
factory: NumberSchema
}
) => ({
...BaseSchema({ ...options, schema }),
/**
* It represents an inclusive lower limit for a numeric instance.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.4|reference}
* @param {number} min
* @returns {FluentSchema}
*/
minimum: min => {
if (typeof min !== 'number') { throw new FluentSchemaError("'minimum' must be a Number") }
if (schema.type === 'integer' && !Number.isInteger(min)) { throw new FluentSchemaError("'minimum' must be an Integer") }
return setAttribute({ schema, ...options }, ['minimum', min, 'number'])
},
/**
* It represents an exclusive lower limit for a numeric instance.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.5|reference}
* @param {number} min
* @returns {FluentSchema}
*/
exclusiveMinimum: min => {
if (typeof min !== 'number') { throw new FluentSchemaError("'exclusiveMinimum' must be a Number") }
if (schema.type === 'integer' && !Number.isInteger(min)) { throw new FluentSchemaError("'exclusiveMinimum' must be an Integer") }
return setAttribute({ schema, ...options }, [
'exclusiveMinimum',
min,
'number'
])
},
/**
* It represents an inclusive upper limit for a numeric instance.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.2|reference}
* @param {number} max
* @returns {FluentSchema}
*/
maximum: max => {
if (typeof max !== 'number') { throw new FluentSchemaError("'maximum' must be a Number") }
if (schema.type === 'integer' && !Number.isInteger(max)) { throw new FluentSchemaError("'maximum' must be an Integer") }
return setAttribute({ schema, ...options }, ['maximum', max, 'number'])
},
/**
* It represents an exclusive upper limit for a numeric instance.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.3|reference}
* @param {number} max
* @returns {FluentSchema}
*/
exclusiveMaximum: max => {
if (typeof max !== 'number') { throw new FluentSchemaError("'exclusiveMaximum' must be a Number") }
if (schema.type === 'integer' && !Number.isInteger(max)) { throw new FluentSchemaError("'exclusiveMaximum' must be an Integer") }
return setAttribute({ schema, ...options }, [
'exclusiveMaximum',
max,
'number'
])
},
/**
* It's strictly greater than 0.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.2.1|reference}
* @param {number} multiple
* @returns {FluentSchema}
*/
multipleOf: multiple => {
if (typeof multiple !== 'number') { throw new FluentSchemaError("'multipleOf' must be a Number") }
if (schema.type === 'integer' && !Number.isInteger(multiple)) { throw new FluentSchemaError("'multipleOf' must be an Integer") }
return setAttribute({ schema, ...options }, [
'multipleOf',
multiple,
'number'
])
}
})
module.exports = {
NumberSchema,
default: NumberSchema
}
================================================
FILE: src/NumberSchema.test.js
================================================
'use strict'
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const { NumberSchema } = require('./NumberSchema')
const S = require('./FluentJSONSchema')
describe('NumberSchema', () => {
it('defined', () => {
assert.notStrictEqual(NumberSchema, undefined)
})
it('Expose symbol', () => {
assert.notStrictEqual(
NumberSchema()[Symbol.for('fluent-schema-object')],
undefined
)
})
describe('constructor', () => {
it('without params', () => {
assert.deepStrictEqual(NumberSchema().valueOf(), {
type: 'number'
})
})
it('from S', () => {
assert.deepStrictEqual(S.number().valueOf(), {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'number'
})
})
})
describe('keywords:', () => {
describe('minimum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.number().minimum(5.1)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'number',
minimum: 5.1
}
},
type: 'object'
}
)
})
it('invalid value', () => {
assert.throws(
() => S.number().minimum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'minimum' must be a Number"
)
})
})
describe('maximum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.number().maximum(5.1)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'number',
maximum: 5.1
}
},
type: 'object'
}
)
})
it('invalid value', () => {
assert.throws(
() => S.number().maximum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'maximum' must be a Number"
)
})
})
describe('multipleOf', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.number().multipleOf(5.1)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'number',
multipleOf: 5.1
}
},
type: 'object'
}
)
})
it('invalid value', () => {
assert.throws(
() => S.number().multipleOf('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'multipleOf' must be a Number"
)
})
})
describe('exclusiveMinimum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.number().exclusiveMinimum(5.1)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'number',
exclusiveMinimum: 5.1
}
},
type: 'object'
}
)
})
it('invalid value', () => {
assert.throws(
() => S.number().exclusiveMinimum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'exclusiveMinimum' must be a Number"
)
})
})
describe('exclusiveMaximum', () => {
it('valid', () => {
const prop = 'prop'
assert.deepStrictEqual(
S.object().prop(prop, S.number().exclusiveMaximum(5.1)).valueOf(),
{
$schema: 'http://json-schema.org/draft-07/schema#',
properties: {
prop: {
type: 'number',
exclusiveMaximum: 5.1
}
},
type: 'object'
}
)
})
it('invalid value', () => {
assert.throws(
() => S.number().exclusiveMaximum('5.1'),
(err) =>
err instanceof S.FluentSchemaError &&
err.message === "'exclusiveMaximum' must be a Number"
)
})
})
describe('raw', () => {
it('allows to add a custom attribute', () => {
const schema = NumberSchema().raw({ customKeyword: true }).valueOf()
assert.deepStrictEqual(schema, {
type: 'number',
customKeyword: true
})
})
})
})
it('works', () => {
const schema = S.object()
.id('http://foo.com/user')
.title('A User')
.description('A User desc')
.prop('age', S.number().maximum(10))
.valueOf()
assert.deepStrictEqual(schema, {
$id: 'http://foo.com/user',
$schema: 'http://json-schema.org/draft-07/schema#',
description: 'A User desc',
properties: { age: { maximum: 10, type: 'number' } },
title: 'A User',
type: 'object'
})
})
})
================================================
FILE: src/ObjectSchema.js
================================================
'use strict'
const { BaseSchema } = require('./BaseSchema')
const {
omit,
setAttribute,
isFluentSchema,
hasCombiningKeywords,
patchIdsWithParentId,
appendRequired,
FluentSchemaError,
combineDeepmerge
} = require('./utils')
const initialState = {
type: 'object',
definitions: [],
properties: [],
required: []
}
/**
* Represents a ObjectSchema.
* @param {Object} [options] - Options
* @param {StringSchema} [options.schema] - Default schema
* @param {boolean} [options.generateIds = false] - generate the id automatically e.g. #properties.foo
* @returns {StringSchema}
*/
const ObjectSchema = ({ schema = initialState, ...options } = {}) => {
// TODO LS think about default values and how pass all of them through the functions
options = {
generateIds: false,
factory: ObjectSchema,
...options
}
return {
...BaseSchema({ ...options, schema }),
/**
* It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
* Calling `id` on an ObjectSchema will alway set the id on the root of the object rather than in its "properties", which
* differs from other schema types.
*
* {@link https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2|reference}
* @param {string} id - an #id
**/
id: id => {
if (!id) {
throw new FluentSchemaError(
'id should not be an empty fragment <#> or an empty string <> (e.g. #myId)'
)
}
return options.factory({ schema: { ...schema, $id: id }, ...options })
},
/**
* This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
* Validation with "additionalProperties" applies only to the child values of instance names that do not match any names in "properties",
* and do not match any regular expression in "patternProperties".
* For all such properties, validation succeeds if the child instance validates against the "additionalProperties" schema.
* Omitting this keyword has the same behavior as an empty schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.6|reference}
* @param {FluentSchema|boolean} value
* @returns {FluentSchema}
*/
additionalProperties: value => {
if (typeof value === 'boolean') {
return setAttribute({ schema, ...options }, [
'additionalProperties',
value,
'object'
])
}
if (isFluentSchema(value)) {
const { $schema, ...rest } = value.valueOf({ isRoot: false })
return setAttribute({ schema, ...options }, [
'additionalProperties',
{ ...rest },
'array'
])
}
throw new FluentSchemaError(
"'additionalProperties' must be a boolean or a S"
)
},
/**
* An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.1|reference}
* @param {number} max
* @returns {FluentSchema}
*/
maxProperties: max => {
if (!Number.isInteger(max)) { throw new FluentSchemaError("'maxProperties' must be a Integer") }
return setAttribute({ schema, ...options }, [
'maxProperties',
max,
'object'
])
},
/**
* An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.2|reference}
* @param {number} min
* @returns {FluentSchema}
*/
minProperties: min => {
if (!Number.isInteger(min)) { throw new FluentSchemaError("'minProperties' must be a Integer") }
return setAttribute({ schema, ...options }, [
'minProperties',
min,
'object'
])
},
/**
* Each property name of this object SHOULD be a valid regular expression, according to the ECMA 262 regular expression dialect.
* Each property value of this object MUST be a valid JSON Schema.
* This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
* Validation of the primitive instance type against this keyword always succeeds.
* Validation succeeds if, for each instance name that matches any regular expressions that appear as a property name in this keyword's value, the child instance for that name successfully validates against each schema that corresponds to a matching regular expression.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.5|reference}
* @param {object} opts
* @returns {FluentSchema}
*/
patternProperties: opts => {
const values = Object.entries(opts).reduce((memo, [pattern, schema]) => {
if (!isFluentSchema(schema)) {
throw new FluentSchemaError(
"'patternProperties' invalid options. Provide a valid map e.g. { '^fo.*$': S.string() }"
)
}
memo[pattern] = omit(schema.valueOf({ isRoot: false }), ['$schema'])
return memo
}, {})
return setAttribute({ schema, ...options }, [
'patternProperties',
values,
'object'
])
},
/**
* This keyword specifies rules that are evaluated if the instance is an object and contains a certain property.
* This keyword's value MUST be an object. Each property specifies a dependency. Each dependency value MUST be an array or a valid JSON Schema.
* If the dependency value is a subschema, and the dependency key is a property in the instance, the entire instance must validate against the dependency value.
* If the dependency value is an array, each element in the array, if any, MUST be a string, and MUST be unique. If the dependency key is a property in the instance, each of the items in the dependency value must be a property that exists in the instance.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.7|reference}
* @param {object} opts
* @returns {FluentSchema}
*/
dependencies: opts => {
const values = Object.entries(opts).reduce((memo, [prop, schema]) => {
if (!isFluentSchema(schema) && !Array.isArray(schema)) {
throw new FluentSchemaError(
"'dependencies' invalid options. Provide a valid map e.g. { 'foo': ['bar'] } or { 'foo': S.string() }"
)
}
memo[prop] = Array.isArray(schema)
? schema
: omit(schema.valueOf({ isRoot: false }), ['$schema', 'type', 'definitions'])
return memo
}, {})
return setAttribute({ schema, ...options }, [
'dependencies',
values,
'object'
])
},
/**
* The value of "properties" MUST be an object. Each dependency value MUST be an array.
* Each element in the array MUST be a string and MUST be unique. If the dependency key is a property in the instance, each of the items in the dependency value must be a property that exists in the instance.
*
* {@link https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.5.4|reference}
* @param {object} opts
* @returns {FluentSchema}
*/
dependentRequired: opts => {
const values = Object.entries(opts).reduce((memo, [prop, schema]) => {
if (!Array.isArray(schema)) {
throw new FluentSchemaError(
"'dependentRequired' invalid options. Provide a valid array e.g. { 'foo': ['bar'] }"
)
}
memo[prop] = schema
return memo
}, {})
return setAttribute({ schema, ...options }, [
'dependentRequired',
values,
'object'
])
},
/**
* The value of "properties" MUST be an object. The dependency value MUST be a valid JSON Schema.
* Each dependency key is a property in the instance and the entire instance must validate against the dependency value.
*
* {@link https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.2.2.4|reference}
* @param {object} opts
* @returns {FluentSchema}
*/
dependentSchemas: opts => {
const values = Object.entries(opts).reduce((memo, [prop, schema]) => {
if (!isFluentSchema(schema)) {
throw new FluentSchemaError(
"'dependentSchemas' invalid options. Provide a valid schema e.g. { 'foo': S.string() }"
)
}
memo[prop] = omit(schema.valueOf({ isRoot: false }), ['$schema', 'type', 'definitions'])
return memo
}, {})
return setAttribute({ schema, ...options }, [
'dependentSchemas',
values,
'object'
])
},
/**
* If the instance is an object, this keyword validates if every property name in the instance validates against the provided schema.
* Note the property name that the schema is testing will always be a string.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.8|reference}
* @param {FluentSchema} value
* @returns {FluentSchema}
*/
propertyNames: value => {
if (!isFluentSchema(value)) { throw new FluentSchemaError("'propertyNames' must be a S") }
return setAttribute({ schema, ...options }, [
'propertyNames',
omit(value.valueOf({ isRoot: false }), ['$schema']),
'object'
])
},
/**
* The value of "properties" MUST be an object. Each value of this object MUST be a valid JSON Schema.
*
* {@link https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.4|reference}
* @param {string} name
* @param {FluentSchema} props
* @returns {FluentSchema}
*/
prop: (name, props = {}) => {
if (Array.isArray(props) || typeof props !== 'object') {
throw new FluentSchemaError(
`'${name}' doesn't support value '${JSON.stringify(
props
)}'. Pass a FluentSchema object`
)
}
const target = props.def ? 'definitions' : 'properties'
let attributes = props.valueOf({ isRoot: false })
const { $ref, $id: attributeId, required, ...restAttributes } = attributes
const $id =
attributeId ||
(options.generateIds ? `#${target}/${name}` : undefined)
if (isFluentSchema(props)) {
attributes = patchIdsWithParentId({
schema: attributes,
parentId: $id,
...options
})
const [schemaPatched, attributesPatched] = appendRequired({
schema,
attributes: {
...attributes,
name
}
})
schema = schemaPatched
attributes = attributesPatched
}
const type = hasCombiningKeywords(attributes)
? undefined
: attributes.type
// strip undefined values or empty arrays or internals
attributes = Object.entries({ ...attributes, $id, type }).reduce(
(memo, [key, value]) => {
if (
key !== '$schema' &&
key !== 'def' &&
value !== undefined &&
!(Array.isArray(value) && value.length === 0 && key !== 'default')
) {
memo[key] = value
}
return memo
},
{}
)
return ObjectSchema({
schema: {
...schema,
[target]: [
...schema[target],
$ref ? { name, $ref, ...restAttributes } : { name, ...attributes }
]
},
...options
})
},
extend: base => {
if (!base) {
throw new FluentSchemaError("Schema can't be null or undefined")
}
if (!base.isFluentSchema) {
throw new FluentSchemaError("Schema isn't FluentSchema type")
}
const src = base._getState()
const extended = combineDeepmerge(src, schema)
const {
valueOf,
isFluentSchema,
FLUENT_SCHEMA,
_getState,
extend
} = ObjectSchema({ schema: extended, ...options })
return { valueOf, isFluentSchema, FLUENT_SCHEMA, _getState, extend }
},
/**
* Returns an object schema with only a subset of keys provided. If called on an ObjectSchema with an
* `$id`, it will be removed and the return value will be considered a new schema.
*
* @param properties a list of properties you want to keep
* @returns {ObjectSchema}
*/
only: properties => {
return ObjectSchema({
schema: {
...omit(schema, ['$id', 'properties']),
properties: schema.properties.filter(({ name }) => properties.includes(name)),
required: schema.required.filter(p => properties.includes(p))
},
...options
})
},
/**
* Returns an object schema without a subset of keys provided. If called on an ObjectSchema with an
* `$id`, it will be removed and the return value will be considered a new schema.
*
* @param properties a list of properties you dont want to keep
* @returns {ObjectSchema}
*/
without: properties => {
return ObjectSchema({
schema: {
...omit(schema, ['$id', 'properties']),
properties: schema.properties.filter(({ name }) => !properties.includes(name)),
required: schema.required.filter(p => !properties.includes(p))
},
...options
})
},
/**
* The "definitions" keywords provides a standardized location for schema authors to inline re-usable JSON Schemas into a more general schema.
* There are no restrictions placed on the values within the array.
*
* {@link https://tools.ietf.org/id/draft-handrews-j
gitextract_nx7y_40v/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.yml
│ └── lock-threads.yml
├── .gitignore
├── .husky/
│ └── pre-commit
├── .npmignore
├── .npmrc
├── .nvmrc
├── LICENSE
├── README.md
├── docs/
│ └── API.md
├── eslint.config.js
├── package.json
├── src/
│ ├── ArraySchema.js
│ ├── ArraySchema.test.js
│ ├── BaseSchema.js
│ ├── BaseSchema.test.js
│ ├── BooleanSchema.js
│ ├── BooleanSchema.test.js
│ ├── FluentJSONSchema.js
│ ├── FluentSchema.integration.test.js
│ ├── FluentSchema.test.js
│ ├── IntegerSchema.js
│ ├── IntegerSchema.test.js
│ ├── MixedSchema.js
│ ├── MixedSchema.test.js
│ ├── NullSchema.js
│ ├── NullSchema.test.js
│ ├── NumberSchema.js
│ ├── NumberSchema.test.js
│ ├── ObjectSchema.js
│ ├── ObjectSchema.test.js
│ ├── RawSchema.js
│ ├── RawSchema.test.js
│ ├── StringSchema.js
│ ├── StringSchema.test.js
│ ├── example.js
│ ├── schemas/
│ │ └── basic.json
│ ├── utils.js
│ └── utils.test.js
└── types/
├── FluentJSONSchema.d.ts
└── FluentJSONSchema.test-d.ts
SYMBOL INDEX (56 symbols across 4 files)
FILE: src/example.js
constant ROLES (line 5) | const ROLES = {
FILE: src/utils.js
class FluentSchemaError (line 8) | class FluentSchemaError extends Error {
method constructor (line 9) | constructor (message) {
constant REQUIRED (line 66) | const REQUIRED = Symbol('required')
constant FLUENT_SCHEMA (line 67) | const FLUENT_SCHEMA = Symbol.for('fluent-schema-object')
constant RELATIVE_JSON_POINTER (line 69) | const RELATIVE_JSON_POINTER = 'relative-json-pointer'
constant JSON_POINTER (line 70) | const JSON_POINTER = 'json-pointer'
constant UUID (line 71) | const UUID = 'uuid'
constant REGEX (line 72) | const REGEX = 'regex'
constant IPV6 (line 73) | const IPV6 = 'ipv6'
constant IPV4 (line 74) | const IPV4 = 'ipv4'
constant HOSTNAME (line 75) | const HOSTNAME = 'hostname'
constant EMAIL (line 76) | const EMAIL = 'email'
constant URL (line 77) | const URL = 'url'
constant URI_TEMPLATE (line 78) | const URI_TEMPLATE = 'uri-template'
constant URI_REFERENCE (line 79) | const URI_REFERENCE = 'uri-reference'
constant URI (line 80) | const URI = 'uri'
constant TIME (line 81) | const TIME = 'time'
constant DATE (line 82) | const DATE = 'date'
constant DATE_TIME (line 83) | const DATE_TIME = 'date-time'
constant ISO_TIME (line 84) | const ISO_TIME = 'iso-time'
constant ISO_DATE_TIME (line 85) | const ISO_DATE_TIME = 'iso-date-time'
constant FORMATS (line 87) | const FORMATS = {
constant STRING (line 107) | const STRING = 'string'
constant NUMBER (line 108) | const NUMBER = 'number'
constant BOOLEAN (line 109) | const BOOLEAN = 'boolean'
constant INTEGER (line 110) | const INTEGER = 'integer'
constant OBJECT (line 111) | const OBJECT = 'object'
constant ARRAY (line 112) | const ARRAY = 'array'
constant NULL (line 113) | const NULL = 'null'
constant TYPES (line 115) | const TYPES = {
FILE: types/FluentJSONSchema.d.ts
type BaseSchema (line 1) | interface BaseSchema<T> {
type TYPE (line 29) | type TYPE =
type FORMATS (line 38) | type FORMATS = {
type JSONSchema (line 58) | type JSONSchema =
class FluentSchemaError (line 68) | class FluentSchemaError extends Error {
type SchemaOptions (line 72) | interface SchemaOptions {
type StringSchema (line 77) | interface StringSchema extends BaseSchema<StringSchema> {
type NullSchema (line 86) | interface NullSchema {
type BooleanSchema (line 90) | interface BooleanSchema extends BaseSchema<BooleanSchema> {
type NumberSchema (line 94) | interface NumberSchema extends BaseSchema<NumberSchema> {
type IntegerSchema (line 102) | interface IntegerSchema extends BaseSchema<IntegerSchema> {
type ArraySchema (line 110) | interface ArraySchema extends BaseSchema<ArraySchema> {
type ObjectSchema (line 119) | interface ObjectSchema<T extends Record<string, any> = Record<string, an...
type ExtendedSchema (line 135) | type ExtendedSchema = Pick<ObjectSchema, 'isFluentSchema' | 'extend'>
type InferSchemaMap (line 137) | type InferSchemaMap = {
type MixedSchema (line 147) | type MixedSchema<T extends TYPE[]> =
type PatternPropertiesOptions (line 152) | interface PatternPropertiesOptions {
type DependenciesOptions (line 156) | interface DependenciesOptions {
type Key (line 160) | type Key<T> = keyof T | (string & {})
type DependentSchemaOptions (line 162) | type DependentSchemaOptions<T extends Partial<Record<string, JSONSchema>...
type DependentRequiredOptions (line 164) | type DependentRequiredOptions<T extends Partial<Record<string, string[]>...
type ObjectPlaceholder (line 168) | type ObjectPlaceholder = Record<string | number | symbol, any>
type S (line 170) | interface S extends BaseSchema<S> {
FILE: types/FluentJSONSchema.test-d.ts
type Foo (line 148) | type Foo = {
type ReallyLongType (line 179) | type ReallyLongType = {
Condensed preview — 44 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (285K chars).
[
{
"path": ".editorconfig",
"chars": 213,
"preview": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_tr"
},
{
"path": ".gitattributes",
"chars": 80,
"preview": "# Set default behavior to automatically convert line endings\n* text=auto eol=lf\n"
},
{
"path": ".github/dependabot.yml",
"chars": 274,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"monthly\"\n "
},
{
"path": ".github/workflows/ci.yml",
"chars": 651,
"preview": "name: CI\n\non:\n push:\n branches:\n - main\n - next\n - 'v*'\n paths-ignore:\n - 'docs/**'\n - '*.m"
},
{
"path": ".github/workflows/lock-threads.yml",
"chars": 293,
"preview": "name: Lock Threads\n\non:\n schedule:\n - cron: '0 0 1 * *'\n workflow_dispatch:\n\nconcurrency:\n group: lock\n\npermission"
},
{
"path": ".gitignore",
"chars": 2229,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports"
},
{
"path": ".husky/pre-commit",
"chars": 68,
"preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx lint-staged"
},
{
"path": ".npmignore",
"chars": 99,
"preview": ".editorconfig\n.gitignore\n.husky\n.npmignore\n.nvmrc\n.prettierrc\n.travis.yml\nyarn.lock\n.idea\ncoverage\n"
},
{
"path": ".npmrc",
"chars": 39,
"preview": "ignore-scripts=true\npackage-lock=false\n"
},
{
"path": ".nvmrc",
"chars": 9,
"preview": "v14.19.0\n"
},
{
"path": "LICENSE",
"chars": 1123,
"preview": "MIT License\n\nCopyright (c) 2018-present The Fastify team <https://github.com/fastify/fastify#team>\n\nPermission is hereby"
},
{
"path": "README.md",
"chars": 9646,
"preview": "# fluent-json-schema\n\nA fluent API to generate JSON schemas (draft-07) for Node.js and browser. Framework agnostic.\n\n[!["
},
{
"path": "docs/API.md",
"chars": 56800,
"preview": "## Functions\n\n<dl>\n<dt><a href=\"#ArraySchema\">ArraySchema([options])</a> ⇒ <code><a href=\"#ArraySchema\">ArraySchema</a><"
},
{
"path": "eslint.config.js",
"chars": 135,
"preview": "'use strict'\n\nmodule.exports = require('neostandard')({\n ignores: require('neostandard').resolveIgnoresFromGitignore(),"
},
{
"path": "package.json",
"chars": 1527,
"preview": "{\n \"name\": \"fluent-json-schema\",\n \"version\": \"6.0.0\",\n \"description\": \"JSON Schema fluent API\",\n \"main\": \"src/Fluent"
},
{
"path": "src/ArraySchema.js",
"chars": 5879,
"preview": "'use strict'\nconst { BaseSchema } = require('./BaseSchema')\nconst { setAttribute, isFluentSchema, FluentSchemaError } = "
},
{
"path": "src/ArraySchema.test.js",
"chars": 5044,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { ArrayS"
},
{
"path": "src/BaseSchema.js",
"chars": 14542,
"preview": "'use strict'\nconst {\n flat,\n omit,\n isFluentSchema,\n last,\n isBoolean,\n isUniq,\n patchIdsWithParentId,\n REQUIRED"
},
{
"path": "src/BaseSchema.test.js",
"chars": 22242,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { BaseSc"
},
{
"path": "src/BooleanSchema.js",
"chars": 650,
"preview": "'use strict'\nconst { BaseSchema } = require('./BaseSchema')\n\nconst initialState = {\n type: 'boolean'\n}\n\n/**\n * Represen"
},
{
"path": "src/BooleanSchema.test.js",
"chars": 1252,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { Boolea"
},
{
"path": "src/FluentJSONSchema.js",
"chars": 4894,
"preview": "'use strict'\n\nconst { FORMATS, TYPES, FluentSchemaError } = require('./utils')\n\nconst { BaseSchema } = require('./BaseSc"
},
{
"path": "src/FluentSchema.integration.test.js",
"chars": 15453,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst Ajv = re"
},
{
"path": "src/FluentSchema.test.js",
"chars": 15005,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst S = requ"
},
{
"path": "src/IntegerSchema.js",
"chars": 774,
"preview": "'use strict'\nconst { NumberSchema } = require('./NumberSchema')\n\nconst initialState = {\n type: 'integer'\n}\n\n/**\n * Repr"
},
{
"path": "src/IntegerSchema.test.js",
"chars": 6549,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { Intege"
},
{
"path": "src/MixedSchema.js",
"chars": 1892,
"preview": "'use strict'\nconst { NullSchema } = require('./NullSchema')\nconst { BooleanSchema } = require('./BooleanSchema')\nconst {"
},
{
"path": "src/MixedSchema.test.js",
"chars": 2944,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { MixedS"
},
{
"path": "src/NullSchema.js",
"chars": 1065,
"preview": "'use strict'\nconst { BaseSchema } = require('./BaseSchema')\nconst { setAttribute, FLUENT_SCHEMA } = require('./utils')\n\n"
},
{
"path": "src/NullSchema.test.js",
"chars": 1213,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { NullSc"
},
{
"path": "src/NumberSchema.js",
"chars": 3851,
"preview": "'use strict'\nconst { BaseSchema } = require('./BaseSchema')\nconst { setAttribute, FluentSchemaError } = require('./utils"
},
{
"path": "src/NumberSchema.test.js",
"chars": 5275,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { Number"
},
{
"path": "src/ObjectSchema.js",
"chars": 14646,
"preview": "'use strict'\nconst { BaseSchema } = require('./BaseSchema')\nconst {\n omit,\n setAttribute,\n isFluentSchema,\n hasCombi"
},
{
"path": "src/ObjectSchema.test.js",
"chars": 27143,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { Object"
},
{
"path": "src/RawSchema.js",
"chars": 2030,
"preview": "'use strict'\nconst { BaseSchema } = require('./BaseSchema')\nconst { BooleanSchema } = require('./BooleanSchema')\nconst {"
},
{
"path": "src/RawSchema.test.js",
"chars": 6586,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { RawSch"
},
{
"path": "src/StringSchema.js",
"chars": 5349,
"preview": "'use strict'\nconst { BaseSchema } = require('./BaseSchema')\nconst { FORMATS, setAttribute, FluentSchemaError } = require"
},
{
"path": "src/StringSchema.test.js",
"chars": 7024,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { String"
},
{
"path": "src/example.js",
"chars": 3410,
"preview": "'use strict'\nconst S = require('./FluentJSONSchema')\nconst Ajv = require('ajv')\n\nconst ROLES = {\n ADMIN: 'ADMIN',\n USE"
},
{
"path": "src/schemas/basic.json",
"chars": 4060,
"preview": "[\n {\n \"description\": \"basic schema from z-schema benchmark (https://github.com/zaggino/z-schema)\",\n \"schema\": {\n "
},
{
"path": "src/utils.js",
"chars": 5851,
"preview": "'use strict'\nconst deepmerge = require('@fastify/deepmerge')\nconst isFluentSchema = (obj) => obj?.isFluentSchema\n\nconst "
},
{
"path": "src/utils.test.js",
"chars": 2852,
"preview": "'use strict'\n\nconst { describe, it } = require('node:test')\nconst assert = require('node:assert/strict')\n\nconst { setRaw"
},
{
"path": "types/FluentJSONSchema.d.ts",
"chars": 5657,
"preview": "export interface BaseSchema<T> {\n id: (id: string) => T\n title: (title: string) => T\n description: (description: stri"
},
{
"path": "types/FluentJSONSchema.test-d.ts",
"chars": 5622,
"preview": "// This file will be passed to the TypeScript CLI to verify our typings compile\n\nimport S, { FluentSchemaError } from '."
}
]
About this extraction
This page contains the full source code of the fastify/fluent-json-schema GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 44 files (265.6 KB), approximately 68.6k tokens, and a symbol index with 56 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.