我的node项目首次提交!!!

This commit is contained in:
heiye111
2025-09-20 22:05:58 +08:00
commit 48600edffc
2539 changed files with 365006 additions and 0 deletions

2
node_modules/@fastify/ajv-compiler/.gitattributes generated vendored Normal file
View File

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

21
node_modules/@fastify/ajv-compiler/.github/.stale.yml generated vendored Normal file
View File

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

View File

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

View File

@@ -0,0 +1,8 @@
comment: |
Hello! Thank you for contributing!
It appears that you have changed the code, but the tests that verify your change are missing. Could you please add them?
fileExtensions:
- '.ts'
- '.js'
testDir: 'test'

View File

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

2
node_modules/@fastify/ajv-compiler/.taprc generated vendored Normal file
View File

@@ -0,0 +1,2 @@
files:
- test/**/*.test.js

24
node_modules/@fastify/ajv-compiler/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,24 @@
MIT License
Copyright (c) The Fastify Team
The Fastify team members are listed at https://github.com/fastify/fastify#team
and in the README file.
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.

237
node_modules/@fastify/ajv-compiler/README.md generated vendored Normal file
View File

@@ -0,0 +1,237 @@
# @fastify/ajv-compiler
[![CI](https://github.com/fastify/ajv-compiler/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/ajv-compiler/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/@fastify/ajv-compiler.svg?style=flat)](https://www.npmjs.com/package/@fastify/ajv-compiler)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)
This module manages the [`ajv`](https://www.npmjs.com/package/ajv) instances for the Fastify framework.
It isolates the `ajv` dependency so that the AJV version is not tightly coupled to the Fastify version.
This allows the user to decide which version of AJV to use in their Fastify-based application.
## Versions
| `@fastify/ajv-compiler` | `ajv` | Default in `fastify` |
|------------------------:|------:|---------------------:|
| v4.x | v8.x | ^5.x |
| v3.x | v8.x | ^4.x |
| v2.x | v8.x | - |
| v1.x | v6.x | ^3.14 |
### AJV Configuration
The Fastify's default [`ajv` options](https://github.com/ajv-validator/ajv/tree/v6#options) are:
```js
{
coerceTypes: 'array',
useDefaults: true,
removeAdditional: true,
uriResolver: require('fast-uri'),
addUsedSchema: false,
// Explicitly set allErrors to `false`.
// When set to `true`, a DoS attack is possible.
allErrors: false
}
```
Moreover, the [`ajv-formats`](https://www.npmjs.com/package/ajv-formats) module is included by default.
If you need to customize it, check the _usage_ section below.
To customize the `ajv` options, see how in the [Fastify documentation](https://fastify.dev/docs/latest/Reference/Server/#ajv).
## Usage
This module is already used as default by Fastify.
If you need to provide your server instance with a different version, refer to [the Fastify docs](https://fastify.dev/docs/latest/Reference/Server/#schemacontroller).
### Customize the `ajv-formats` plugin
The `format` keyword is not part of the official `ajv` module since v7. To use it, you need to install the `ajv-formats` module and this module
does it for you with the default configuration.
If you need to configure the `ajv-formats` plugin you can do it using the standard Fastify configuration:
```js
const app = fastify({
ajv: {
plugins: [[require('ajv-formats'), { mode: 'fast' }]]
}
})
```
In this way, your setup will have precedence over the `@fastify/ajv-compiler` default configuration.
### Customize the `ajv` instance
If you need to customize the `ajv` instance and take full control of its configuration, you can do it by
using the `onCreate` option in the Fastify configuration that accepts a synchronous function that receives the `ajv` instance:
```js
const app = fastify({
ajv: {
onCreate: (ajv) => {
// Modify the ajv instance as you need.
ajv.addFormat('myFormat', (data) => typeof data === 'string')
}
}
})
```
### Fastify with JTD
The [JSON Type Definition](https://jsontypedef.com/) feature is supported by AJV v8.x and you can benefit from it in your Fastify application.
With Fastify v3.20.x and higher, you can use the `@fastify/ajv-compiler` module to load JSON Type Definitions like so:
```js
const factory = require('@fastify/ajv-compiler')()
const app = fastify({
jsonShorthand: false,
ajv: {
customOptions: { }, // additional JTD options
mode: 'JTD'
},
schemaController: {
compilersFactory: {
buildValidator: factory
}
}
})
```
The default AJV JTD options are the same as [Fastify's default options](#AJV-Configuration).
#### Fastify with JTD and serialization
You can use JTD Schemas to serialize your response object too:
```js
const factoryValidator = require('@fastify/ajv-compiler')()
const factorySerializer = require('@fastify/ajv-compiler')({ jtdSerializer: true })
const app = fastify({
jsonShorthand: false,
ajv: {
customOptions: { }, // additional JTD options
mode: 'JTD'
},
schemaController: {
compilersFactory: {
buildValidator: factoryValidator,
buildSerializer: factorySerializer
}
}
})
```
### AJV Standalone
AJV v8 introduced a [standalone feature](https://ajv.js.org/standalone.html) that lets you pre-compile your schemas and use them in your application for a faster startup.
To use this feature, you must be aware of the following:
1. You must generate and save the application's compiled schemas.
2. Read the compiled schemas from the file and provide them back to your Fastify application.
#### Generate and save the compiled schemas
Fastify helps you to generate the validation schemas functions and it is your choice to save them where you want.
To accomplish this, you must use a new compiler: `StandaloneValidator`.
You must provide 2 parameters to this compiler:
- `readMode: false`: a boolean to indicate that you want to generate the schemas functions string.
- `storeFunction`" a sync function that must store the source code of the schemas functions. You may provide an async function too, but you must manage errors.
When `readMode: false`, **the compiler is meant to be used in development ONLY**.
```js
const { StandaloneValidator } = require('@fastify/ajv-compiler')
const factory = StandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
// routeOpts is like: { schema, method, url, httpPart }
// schemaValidationCode is a string source code that is the compiled schema function
const fileName = generateFileName(routeOpts)
fs.writeFileSync(path.join(__dirname, fileName), schemaValidationCode)
}
})
const app = fastify({
jsonShorthand: false,
schemaController: {
compilersFactory: {
buildValidator: factory
}
}
})
// ... add all your routes with schemas ...
app.ready().then(() => {
// at this stage all your schemas are compiled and stored in the file system
// now it is important to turn off the readMode
})
```
#### Read the compiled schemas functions
At this stage, you should have a file for every route's schema.
To use them, you must use the `StandaloneValidator` with the parameters:
- `readMode: true`: a boolean to indicate that you want to read and use the schemas functions string.
- `restoreFunction`" a sync function that must return a function to validate the route.
Important keep away before you continue reading the documentation:
- when you use the `readMode: true`, the application schemas are not compiled (they are ignored). So, if you change your schemas, you must recompile them!
- as you can see, you must relate the route's schema to the file name using the `routeOpts` object. You may use the `routeOpts.schema.$id` field to do so, it is up to you to define a unique schema identifier.
```js
const { StandaloneValidator } = require('@fastify/ajv-compiler')
const factory = StandaloneValidator({
readMode: true,
restoreFunction (routeOpts) {
// routeOpts is like: { schema, method, url, httpPart }
const fileName = generateFileName(routeOpts)
return require(path.join(__dirname, fileName))
}
})
const app = fastify({
jsonShorthand: false,
schemaController: {
compilersFactory: {
buildValidator: factory
}
}
})
// ... add all your routes with schemas as before...
app.listen({ port: 3000 })
```
### How it works
This module provides a factory function to produce [Validator Compilers](https://fastify.dev/docs/latest/Reference/Server/#validatorcompiler) functions.
The Fastify factory function is just one per server instance and it is called for every encapsulated context created by the application through the `fastify.register()` call.
Every Validator Compiler produced has a dedicated AJV instance, so this factory will try to produce as less as possible AJV instances to reduce the memory footprint and the startup time.
The variables involved to choose if a Validator Compiler can be reused are:
- the AJV configuration: it is [one per server](https://fastify.dev/docs/latest/Reference/Server/#ajv)
- the external JSON schemas: once a new schema is added to a fastify's context, calling `fastify.addSchema()`, it will cause a new AJV initialization
## License
Licensed under [MIT](./LICENSE).

View File

@@ -0,0 +1,37 @@
import cronometro from 'cronometro'
import fjs from 'fast-json-stringify'
import AjvCompiler from '../index.js'
const fjsSerialize = buildFJSSerializerFunction({
type: 'object',
properties: {
hello: { type: 'string' },
name: { type: 'string' }
}
})
const ajvSerialize = buildAJVSerializerFunction({
properties: {
hello: { type: 'string' },
name: { type: 'string' }
}
})
await cronometro({
'fast-json-stringify': function () {
fjsSerialize({ hello: 'Ciao', name: 'Manuel' })
},
'ajv serializer': function () {
ajvSerialize({ hello: 'Ciao', name: 'Manuel' })
}
})
function buildFJSSerializerFunction (schema) {
return fjs(schema)
}
function buildAJVSerializerFunction (schema) {
const factory = AjvCompiler({ jtdSerializer: true })
const compiler = factory({}, { customOptions: {} })
return compiler({ schema })
}

6
node_modules/@fastify/ajv-compiler/eslint.config.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
'use strict'
module.exports = require('neostandard')({
ignores: require('neostandard').resolveIgnoresFromGitignore(),
ts: true
})

53
node_modules/@fastify/ajv-compiler/index.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
'use strict'
const AjvReference = Symbol.for('fastify.ajv-compiler.reference')
const ValidatorCompiler = require('./lib/validator-compiler')
const SerializerCompiler = require('./lib/serializer-compiler')
function AjvCompiler (opts) {
const validatorPool = new Map()
const serializerPool = new Map()
if (opts && opts.jtdSerializer === true) {
return function buildSerializerFromPool (externalSchemas, serializerOpts) {
const uniqueAjvKey = getPoolKey({}, serializerOpts)
if (serializerPool.has(uniqueAjvKey)) {
return serializerPool.get(uniqueAjvKey)
}
const compiler = new SerializerCompiler(externalSchemas, serializerOpts)
const ret = compiler.buildSerializerFunction.bind(compiler)
serializerPool.set(uniqueAjvKey, ret)
return ret
}
}
return function buildCompilerFromPool (externalSchemas, options) {
const uniqueAjvKey = getPoolKey(externalSchemas, options.customOptions)
if (validatorPool.has(uniqueAjvKey)) {
return validatorPool.get(uniqueAjvKey)
}
const compiler = new ValidatorCompiler(externalSchemas, options)
const ret = compiler.buildValidatorFunction.bind(compiler)
validatorPool.set(uniqueAjvKey, ret)
if (options.customOptions.code !== undefined) {
ret[AjvReference] = compiler
}
return ret
}
}
function getPoolKey (externalSchemas, options) {
const externals = JSON.stringify(externalSchemas)
const ajvConfig = JSON.stringify(options)
return `${externals}${ajvConfig}`
}
module.exports = AjvCompiler
module.exports.default = AjvCompiler
module.exports.AjvCompiler = AjvCompiler
module.exports.AjvReference = AjvReference
module.exports.StandaloneValidator = require('./standalone')

View File

@@ -0,0 +1,14 @@
'use strict'
const fastUri = require('fast-uri')
module.exports = Object.freeze({
coerceTypes: 'array',
useDefaults: true,
removeAdditional: true,
uriResolver: fastUri,
addUsedSchema: false,
// Explicitly set allErrors to `false`.
// When set to `true`, a DoS attack is possible.
allErrors: false
})

View File

@@ -0,0 +1,27 @@
'use strict'
const AjvJTD = require('ajv/dist/jtd')
const defaultAjvOptions = require('./default-ajv-options')
class SerializerCompiler {
constructor (_externalSchemas, options) {
this.ajv = new AjvJTD(Object.assign({}, defaultAjvOptions, options))
/**
* https://ajv.js.org/json-type-definition.html#ref-form
* Unlike JSON Schema, JTD does not allow to reference:
* - any schema fragment other than root level definitions member
* - root of the schema - there is another way to define a self-recursive schema (see Example 2)
* - another schema file (but you can still combine schemas from multiple files using JavaScript).
*
* So we ignore the externalSchemas parameter.
*/
}
buildSerializerFunction ({ schema/*, method, url, httpStatus */ }) {
return this.ajv.compileSerializer(schema)
}
}
module.exports = SerializerCompiler

View File

@@ -0,0 +1,58 @@
'use strict'
const Ajv = require('ajv').default
const AjvJTD = require('ajv/dist/jtd')
const defaultAjvOptions = require('./default-ajv-options')
class ValidatorCompiler {
constructor (externalSchemas, options) {
// This instance of Ajv is private
// it should not be customized or used
if (options.mode === 'JTD') {
this.ajv = new AjvJTD(Object.assign({}, defaultAjvOptions, options.customOptions))
} else {
this.ajv = new Ajv(Object.assign({}, defaultAjvOptions, options.customOptions))
}
let addFormatPlugin = true
if (options.plugins && options.plugins.length > 0) {
for (const plugin of options.plugins) {
if (Array.isArray(plugin)) {
addFormatPlugin = addFormatPlugin && plugin[0].name !== 'formatsPlugin'
plugin[0](this.ajv, plugin[1])
} else {
addFormatPlugin = addFormatPlugin && plugin.name !== 'formatsPlugin'
plugin(this.ajv)
}
}
}
if (addFormatPlugin) {
require('ajv-formats')(this.ajv)
}
options.onCreate?.(this.ajv)
const sourceSchemas = Object.values(externalSchemas)
for (const extSchema of sourceSchemas) {
this.ajv.addSchema(extSchema)
}
}
buildValidatorFunction ({ schema/*, method, url, httpPart */ }) {
// Ajv does not support compiling two schemas with the same
// id inside the same instance. Therefore if we have already
// compiled the schema with the given id, we just return it.
if (schema.$id) {
const stored = this.ajv.getSchema(schema.$id)
if (stored) {
return stored
}
}
return this.ajv.compile(schema)
}
}
module.exports = ValidatorCompiler

84
node_modules/@fastify/ajv-compiler/package.json generated vendored Normal file
View File

@@ -0,0 +1,84 @@
{
"name": "@fastify/ajv-compiler",
"version": "4.0.2",
"description": "Build and manage the AJV instances for the fastify framework",
"main": "index.js",
"type": "commonjs",
"types": "types/index.d.ts",
"directories": {
"test": "test"
},
"scripts": {
"lint": "eslint",
"lint:fix": "eslint --fix",
"unit": "tap",
"test": "npm run unit && npm run test:typescript",
"test:typescript": "tsd",
"ajv:compile": "ajv compile -s test/source.json -o test/validate_schema.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/ajv-compiler.git"
},
"keywords": [
"ajv",
"validator",
"schema",
"compiler",
"fastify"
],
"author": "Manuel Spigolon <behemoth89@gmail.com> (https://github.com/Eomm)",
"contributors": [
{
"name": "Matteo Collina",
"email": "hello@matteocollina.com"
},
{
"name": "Aras Abbasi",
"email": "aras.abbasi@gmail.com"
},
{
"name": "James Sumners",
"url": "https://james.sumners.info"
},
{
"name": "Frazer Smith",
"email": "frazer.dev@icloud.com",
"url": "https://github.com/fdawgs"
}
],
"license": "MIT",
"bugs": {
"url": "https://github.com/fastify/ajv-compiler/issues"
},
"homepage": "https://github.com/fastify/ajv-compiler#readme",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"devDependencies": {
"ajv-cli": "^5.0.0",
"ajv-errors": "^3.0.0",
"ajv-i18n": "^4.2.0",
"ajv-merge-patch": "^5.0.1",
"cronometro": "^4.0.0",
"eslint": "^9.17.0",
"fastify": "^5.0.0",
"neostandard": "^0.12.0",
"require-from-string": "^2.0.2",
"sanitize-filename": "^1.6.3",
"tap": "^19.0.0",
"tsd": "^0.31.0"
},
"dependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^3.0.1",
"fast-uri": "^3.0.0"
}
}

44
node_modules/@fastify/ajv-compiler/standalone.js generated vendored Normal file
View File

@@ -0,0 +1,44 @@
'use strict'
const ValidatorSelector = require('./index')
const standaloneCode = require('ajv/dist/standalone').default
function StandaloneValidator (options = { readMode: true }) {
if (options.readMode === true && !options.restoreFunction) {
throw new Error('You must provide a restoreFunction options when readMode ON')
}
if (options.readMode !== true && !options.storeFunction) {
throw new Error('You must provide a storeFunction options when readMode OFF')
}
if (options.readMode === true) {
// READ MODE: it behalf only in the restore function provided by the user
return function wrapper () {
return function (opts) {
return options.restoreFunction(opts)
}
}
}
// WRITE MODE: it behalf on the default ValidatorSelector, wrapping the API to run the Ajv Standalone code generation
const factory = ValidatorSelector()
return function wrapper (externalSchemas, ajvOptions = {}) {
if (!ajvOptions.customOptions || !ajvOptions.customOptions.code) {
// to generate the validation source code, these options are mandatory
ajvOptions.customOptions = Object.assign({}, ajvOptions.customOptions, { code: { source: true } })
}
const compiler = factory(externalSchemas, ajvOptions)
return function (opts) { // { schema/*, method, url, httpPart */ }
const validationFunc = compiler(opts)
const schemaValidationCode = standaloneCode(compiler[ValidatorSelector.AjvReference].ajv, validationFunc)
options.storeFunction(opts, schemaValidationCode)
return validationFunc
}
}
}
module.exports = StandaloneValidator

0
node_modules/@fastify/ajv-compiler/test/.gitkeep generated vendored Normal file
View File

View File

@@ -0,0 +1,59 @@
'use strict'
const t = require('tap')
const AjvCompiler = require('../index')
const postSchema = Object.freeze({
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
$id: 'http://mydomain.com/user',
title: 'User schema',
description: 'Contains all user fields',
properties: {
username: { type: 'string', minLength: 4 },
firstName: { type: 'string', minLength: 1 },
lastName: { type: 'string', minLength: 1 },
email: { type: 'string' },
password: { type: 'string', minLength: 6 },
bio: { type: 'string' }
},
required: ['username', 'firstName', 'lastName', 'email', 'bio', 'password']
})
const patchSchema = Object.freeze({
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
$id: 'http://mydomain.com/user',
title: 'User schema',
description: 'Contains all user fields',
properties: {
firstName: { type: 'string', minLength: 1 },
lastName: { type: 'string', minLength: 1 },
bio: { type: 'string' }
}
})
const fastifyAjvOptionsDefault = Object.freeze({
customOptions: {}
})
t.test('must not store schema on compile', t => {
t.plan(4)
const factory = AjvCompiler()
const compiler = factory({}, fastifyAjvOptionsDefault)
const postFn = compiler({ schema: postSchema })
const patchFn = compiler({ schema: patchSchema })
const resultForPost = postFn({})
t.equal(resultForPost, false)
t.has(postFn.errors, [
{
keyword: 'required',
message: "must have required property 'username'"
}
])
const resultForPatch = patchFn({})
t.ok(resultForPatch)
t.notOk(patchFn.errors)
})

307
node_modules/@fastify/ajv-compiler/test/index.test.js generated vendored Normal file
View File

@@ -0,0 +1,307 @@
'use strict'
const t = require('tap')
const fastify = require('fastify')
const AjvCompiler = require('../index')
const sym = Symbol.for('fastify.ajv-compiler.reference')
const sampleSchema = Object.freeze({
$id: 'example1',
type: 'object',
properties: {
name: { type: 'string' }
}
})
const externalSchemas1 = Object.freeze({})
const externalSchemas2 = Object.freeze({
foo: {
$id: 'foo',
type: 'object',
properties: {
name: { type: 'string' }
}
}
})
const fastifyAjvOptionsDefault = Object.freeze({
customOptions: {}
})
const fastifyJtdDefault = Object.freeze({
customOptions: { },
mode: 'JTD'
})
const fastifyAjvOptionsCustom = Object.freeze({
customOptions: {
allErrors: true,
removeAdditional: false
},
plugins: [
require('ajv-formats'),
[require('ajv-errors'), { singleError: false }]
]
})
t.test('basic usage', t => {
t.plan(1)
const factory = AjvCompiler()
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
const validatorFunc = compiler({ schema: sampleSchema })
const result = validatorFunc({ name: 'hello' })
t.equal(result, true)
})
t.test('array coercion', t => {
t.plan(2)
const factory = AjvCompiler()
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
const arraySchema = {
$id: 'example1',
type: 'object',
properties: {
name: { type: 'array', items: { type: 'string' } }
}
}
const validatorFunc = compiler({ schema: arraySchema })
const inputObj = { name: 'hello' }
t.equal(validatorFunc(inputObj), true)
t.same(inputObj, { name: ['hello'] }, 'the name property should be coerced to an array')
})
t.test('nullable default', t => {
t.plan(2)
const factory = AjvCompiler()
const compiler = factory({}, fastifyAjvOptionsDefault)
const validatorFunc = compiler({
schema: {
type: 'object',
properties: {
nullable: { type: 'string', nullable: true },
notNullable: { type: 'string' }
}
}
})
const input = { nullable: null, notNullable: null }
const result = validatorFunc(input)
t.equal(result, true)
t.same(input, { nullable: null, notNullable: '' }, 'the notNullable field has been coerced')
})
t.test('plugin loading', t => {
t.plan(3)
const factory = AjvCompiler()
const compiler = factory(externalSchemas1, fastifyAjvOptionsCustom)
const validatorFunc = compiler({
schema: {
type: 'object',
properties: {
q: {
type: 'string',
format: 'date',
formatMinimum: '2016-02-06',
formatExclusiveMaximum: '2016-12-27'
}
},
required: ['q'],
errorMessage: 'hello world'
}
})
const result = validatorFunc({ q: '2016-10-02' })
t.equal(result, true)
const resultFail = validatorFunc({})
t.equal(resultFail, false)
t.equal(validatorFunc.errors[0].message, 'hello world')
})
t.test('optimization - cache ajv instance', t => {
t.plan(5)
const factory = AjvCompiler()
const compiler1 = factory(externalSchemas1, fastifyAjvOptionsDefault)
const compiler2 = factory(externalSchemas1, fastifyAjvOptionsDefault)
t.equal(compiler1, compiler2, 'same instance')
t.same(compiler1, compiler2, 'same instance')
const compiler3 = factory(externalSchemas2, fastifyAjvOptionsDefault)
t.not(compiler3, compiler1, 'new ajv instance when externa schema change')
const compiler4 = factory(externalSchemas1, fastifyAjvOptionsCustom)
t.not(compiler4, compiler1, 'new ajv instance when externa schema change')
t.not(compiler4, compiler3, 'new ajv instance when externa schema change')
})
t.test('the onCreate callback can enhance the ajv instance', t => {
t.plan(2)
const factory = AjvCompiler()
const fastifyAjvCustomOptionsFormats = Object.freeze({
onCreate (ajv) {
for (const [formatName, format] of Object.entries(this.customOptions.formats)) {
ajv.addFormat(formatName, format)
}
},
customOptions: {
formats: {
date: /foo/
}
}
})
const compiler1 = factory(externalSchemas1, fastifyAjvCustomOptionsFormats)
const validatorFunc = compiler1({
schema: {
type: 'string',
format: 'date'
}
})
const result = validatorFunc('foo')
t.equal(result, true)
const resultFail = validatorFunc('2016-10-02')
t.equal(resultFail, false)
})
// https://github.com/fastify/fastify/pull/2969
t.test('compile same $id when in external schema', t => {
t.plan(3)
const factory = AjvCompiler()
const base = {
$id: 'urn:schema:base',
definitions: {
hello: { type: 'string' }
},
type: 'object',
properties: {
hello: { $ref: '#/definitions/hello' }
}
}
const refSchema = {
$id: 'urn:schema:ref',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:base#/definitions/hello' }
}
}
const compiler = factory({
[base.$id]: base,
[refSchema.$id]: refSchema
}, fastifyAjvOptionsDefault)
t.notOk(compiler[sym], 'the ajv reference do not exists if code is not activated')
const validatorFunc1 = compiler({
schema: {
$id: 'urn:schema:ref'
}
})
const validatorFunc2 = compiler({
schema: {
$id: 'urn:schema:ref'
}
})
t.pass('the compile does not fail if the schema compiled is already in the external schemas')
t.equal(validatorFunc1, validatorFunc2, 'the returned function is the same')
})
t.test('JTD MODE', t => {
t.plan(2)
t.test('compile jtd schema', t => {
t.plan(4)
const factory = AjvCompiler()
const jtdSchema = {
discriminator: 'version',
mapping: {
1: {
properties: {
foo: { type: 'uint8' }
}
},
2: {
properties: {
foo: { type: 'string' }
}
}
}
}
const compiler = factory({}, fastifyJtdDefault)
const validatorFunc = compiler({ schema: jtdSchema })
t.pass('generated validation function for JTD SCHEMA')
const result = validatorFunc({
version: '2',
foo: []
})
t.notOk(result, 'failed validation')
t.type(validatorFunc.errors, 'Array')
const success = validatorFunc({
version: '1',
foo: 42
})
t.ok(success)
})
t.test('fastify integration', async t => {
const factory = AjvCompiler()
const app = fastify({
jsonShorthand: false,
ajv: {
customOptions: { },
mode: 'JTD'
},
schemaController: {
compilersFactory: {
buildValidator: factory
}
}
})
app.post('/', {
schema: {
body: {
discriminator: 'version',
mapping: {
1: {
properties: {
foo: { type: 'uint8' }
}
},
2: {
properties: {
foo: { type: 'string' }
}
}
}
}
}
}, () => {})
const res = await app.inject({
url: '/',
method: 'POST',
payload: {
version: '1',
foo: 'this is not a number'
}
})
t.equal(res.statusCode, 400)
t.equal(res.json().message, 'body/foo must be uint8')
})
})

264
node_modules/@fastify/ajv-compiler/test/plugins.test.js generated vendored Normal file
View File

@@ -0,0 +1,264 @@
'use strict'
const t = require('tap')
const fastify = require('fastify')
const AjvCompiler = require('../index')
const ajvFormats = require('ajv-formats')
const ajvErrors = require('ajv-errors')
const localize = require('ajv-i18n')
t.test('Format Baseline test', async (t) => {
const app = buildApplication({
customOptions: {
validateFormats: false
}
})
const res = await app.inject({
url: '/hello',
headers: {
'x-foo': 'hello',
'x-date': 'not a date',
'x-email': 'not an email'
},
query: {
foo: 'hello',
date: 'not a date',
email: 'not an email'
}
})
t.equal(res.statusCode, 200, 'format validation does not apply as configured')
t.equal(res.payload, 'hello')
})
t.test('Custom Format plugin loading test', (t) => {
t.plan(6)
const app = buildApplication({
customOptions: {
validateFormats: true
},
plugins: [[ajvFormats, { mode: 'fast' }]]
})
app.inject('/hello', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
app.inject({
url: '/2ad0612c-7578-4b18-9a6f-579863f40e0b',
headers: {
'x-foo': 'hello',
'x-date': new Date().toISOString(),
'x-email': 'foo@bar.baz'
},
query: {
foo: 'hello',
date: new Date().toISOString(),
email: 'foo@bar.baz'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
})
})
t.test('Format plugin set by default test', (t) => {
t.plan(6)
const app = buildApplication({})
app.inject('/hello', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b', (err, res) => {
t.error(err)
t.equal(res.statusCode, 400, 'format validation applies')
})
app.inject({
url: '/2ad0612c-7578-4b18-9a6f-579863f40e0b',
headers: {
'x-foo': 'hello',
'x-date': new Date().toISOString(),
'x-email': 'foo@bar.baz'
},
query: {
foo: 'hello',
date: new Date().toISOString(),
email: 'foo@bar.baz'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
})
})
t.test('Custom error messages', (t) => {
t.plan(9)
const app = buildApplication({
customOptions: {
removeAdditional: false,
allErrors: true
},
plugins: [ajvFormats, ajvErrors]
})
const errorMessage = {
required: 'custom miss',
type: 'custom type', // will not replace internal "type" error for the property "foo"
_: 'custom type', // this prop will do it
additionalProperties: 'custom too many params'
}
app.post('/', {
handler: () => { t.fail('dont call me') },
schema: {
body: {
type: 'object',
required: ['foo'],
properties: {
foo: { type: 'integer' }
},
additionalProperties: false,
errorMessage
}
}
})
app.inject({
url: '/',
method: 'post',
payload: {}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.match(res.json().message, errorMessage.required)
})
app.inject({
url: '/',
method: 'post',
payload: { foo: 'not a number' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.match(res.json().message, errorMessage.type)
})
app.inject({
url: '/',
method: 'post',
payload: { foo: 3, bar: 'ops' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.match(res.json().message, errorMessage.additionalProperties)
})
})
t.test('Custom i18n error messages', (t) => {
t.plan(3)
const app = buildApplication({
customOptions: {
allErrors: true,
messages: false
},
plugins: [ajvFormats]
})
app.post('/', {
handler: () => { t.fail('dont call me') },
schema: {
body: {
type: 'object',
required: ['foo'],
properties: {
foo: { type: 'integer' }
}
}
}
})
app.setErrorHandler((error, request, reply) => {
t.pass('Error handler executed')
if (error.validation) {
localize.ru(error.validation)
reply.status(400).send(error.validation)
return
}
t.fail('not other errors')
})
app.inject({
method: 'POST',
url: '/',
payload: {
foo: 'string'
}
}, (err, res) => {
t.error(err)
t.equal(res.json()[0].message, 'должно быть integer')
})
})
function buildApplication (ajvOptions) {
const factory = AjvCompiler()
const app = fastify({
ajv: ajvOptions,
schemaController: {
compilersFactory: {
buildValidator: factory
}
}
})
app.get('/:id', {
schema: {
headers: {
type: 'object',
required: [
'x-foo',
'x-date',
'x-email'
],
properties: {
'x-foo': { type: 'string' },
'x-date': { type: 'string', format: 'date-time' },
'x-email': { type: 'string', format: 'email' }
}
},
query: {
type: 'object',
required: [
'foo',
'date',
'email'
],
properties: {
foo: { type: 'string' },
date: { type: 'string', format: 'date-time' },
email: { type: 'string', format: 'email' }
}
},
params: {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' }
}
}
}
}, async () => 'hello')
return app
}

View File

@@ -0,0 +1,279 @@
'use strict'
const t = require('tap')
const fastify = require('fastify')
const AjvCompiler = require('../index')
const jtdSchema = {
discriminator: 'version',
mapping: {
1: {
properties: {
foo: { type: 'uint8' }
}
},
2: {
properties: {
foo: { type: 'string' }
}
}
}
}
const externalSchemas1 = Object.freeze({})
const externalSchemas2 = Object.freeze({
foo: {
definitions: {
coordinates: {
properties: {
lat: { type: 'float32' },
lng: { type: 'float32' }
}
}
}
}
})
const fastifyAjvOptionsDefault = Object.freeze({
customOptions: {}
})
t.test('basic serializer usage', t => {
t.plan(4)
const factory = AjvCompiler({ jtdSerializer: true })
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
const serializeFunc = compiler({ schema: jtdSchema })
t.equal(serializeFunc({ version: '1', foo: 42 }), '{"version":"1","foo":42}')
t.equal(serializeFunc({ version: '2', foo: 'hello' }), '{"version":"2","foo":"hello"}')
t.equal(serializeFunc({ version: '3', foo: 'hello' }), '{"version":"3"}')
t.equal(serializeFunc({ version: '2', foo: ['not', 1, { string: 'string' }] }), '{"version":"2","foo":"not,1,[object Object]"}')
})
t.test('external schemas are ignored', t => {
t.plan(1)
const factory = AjvCompiler({ jtdSerializer: true })
const compiler = factory(externalSchemas2, fastifyAjvOptionsDefault)
const serializeFunc = compiler({
schema: {
definitions: {
coordinates: {
properties: {
lat: { type: 'float32' },
lng: { type: 'float32' }
}
}
},
properties: {
userLoc: { ref: 'coordinates' },
serverLoc: { ref: 'coordinates' }
}
}
})
t.equal(serializeFunc(
{ userLoc: { lat: 50, lng: -90 }, serverLoc: { lat: -15, lng: 50 } }),
'{"userLoc":{"lat":50,"lng":-90},"serverLoc":{"lat":-15,"lng":50}}'
)
})
t.test('fastify integration within JTD serializer', async t => {
const factoryValidator = AjvCompiler()
const factorySerializer = AjvCompiler({ jtdSerializer: true })
const app = fastify({
jsonShorthand: false,
ajv: {
customOptions: { },
mode: 'JTD'
},
schemaController: {
compilersFactory: {
buildValidator: factoryValidator,
buildSerializer: factorySerializer
}
}
})
app.post('/', {
schema: {
body: jtdSchema,
response: {
200: {
properties: {
id: { type: 'string' },
createdAt: { type: 'timestamp' },
karma: { type: 'int32' },
isAdmin: { type: 'boolean' }
}
},
400: jtdSchema
}
}
}, async () => {
return {
id: '123',
createdAt: new Date('1999-01-31T23:00:00.000Z'),
karma: 42,
isAdmin: true,
remove: 'me'
}
})
{
const res = await app.inject({
url: '/',
method: 'POST',
payload: {
version: '1',
foo: 'not a number'
}
})
t.equal(res.statusCode, 400)
t.same(res.json(), { version: 'undefined' })
}
{
const res = await app.inject({
url: '/',
method: 'POST',
payload: {
version: '1',
foo: 32
}
})
t.equal(res.statusCode, 200)
t.same(res.json(), {
id: '123',
createdAt: '1999-01-31T23:00:00.000Z',
karma: 42,
isAdmin: true
})
}
})
t.test('fastify integration and cached serializer', async t => {
const factoryValidator = AjvCompiler()
const factorySerializer = AjvCompiler({ jtdSerializer: true })
const app = fastify({
jsonShorthand: false,
ajv: {
customOptions: { },
mode: 'JTD'
},
schemaController: {
compilersFactory: {
buildValidator: factoryValidator,
buildSerializer: factorySerializer
}
}
})
app.register(async function plugin (app, opts) {
app.post('/', {
schema: {
body: jtdSchema,
response: {
200: {
properties: {
id: { type: 'string' },
createdAt: { type: 'timestamp' },
karma: { type: 'int32' },
isAdmin: { type: 'boolean' }
}
},
400: jtdSchema
}
}
}, async () => {
return {
id: '123',
createdAt: new Date('1999-01-31T23:00:00.000Z'),
karma: 42,
isAdmin: true,
remove: 'me'
}
})
})
app.register(async function plugin (app, opts) {
app.post('/two', {
schema: {
body: jtdSchema,
response: {
400: jtdSchema
}
}
}, () => {})
})
{
const res = await app.inject({
url: '/',
method: 'POST',
payload: {
version: '1',
foo: 'not a number'
}
})
t.equal(res.statusCode, 400)
t.same(res.json(), { version: 'undefined' })
}
{
const res = await app.inject({
url: '/',
method: 'POST',
payload: {
version: '1',
foo: 32
}
})
t.equal(res.statusCode, 200)
t.same(res.json(), {
id: '123',
createdAt: '1999-01-31T23:00:00.000Z',
karma: 42,
isAdmin: true
})
}
})
t.test('fastify integration within JTD serializer and custom options', async t => {
const factorySerializer = AjvCompiler({ jtdSerializer: true })
const app = fastify({
jsonShorthand: false,
serializerOpts: {
allErrors: true,
logger: 'wrong-value'
},
schemaController: {
compilersFactory: {
buildSerializer: factorySerializer
}
}
})
app.post('/', {
schema: {
response: {
200: {
properties: {
test: { type: 'boolean' }
}
}
}
}
}, async () => { })
try {
await app.ready()
t.fail('should throw')
} catch (error) {
t.equal(error.message, 'logger must implement log, warn and error methods', 'the wrong setting is forwarded to ajv/jtd')
}
})

View File

@@ -0,0 +1,203 @@
'use strict'
const fs = require('node:fs')
const path = require('node:path')
const t = require('tap')
const fastify = require('fastify')
const sanitize = require('sanitize-filename')
const { StandaloneValidator: AjvStandaloneValidator } = require('../')
function generateFileName (routeOpts) {
return `/ajv-generated-${sanitize(routeOpts.schema.$id)}-${routeOpts.method}-${routeOpts.httpPart}-${sanitize(routeOpts.url)}.js`
}
const generatedFileNames = []
t.test('standalone', t => {
t.plan(4)
t.teardown(async () => {
for (const fileName of generatedFileNames) {
await fs.promises.unlink(path.join(__dirname, fileName))
}
})
t.test('errors', t => {
t.plan(2)
t.throws(() => {
AjvStandaloneValidator()
}, 'missing restoreFunction')
t.throws(() => {
AjvStandaloneValidator({ readMode: false })
}, 'missing storeFunction')
})
t.test('generate standalone code', t => {
t.plan(5)
const base = {
$id: 'urn:schema:base',
definitions: {
hello: { type: 'string' }
},
type: 'object',
properties: {
hello: { $ref: '#/definitions/hello' }
}
}
const refSchema = {
$id: 'urn:schema:ref',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:base#/definitions/hello' }
}
}
const endpointSchema = {
schema: {
$id: 'urn:schema:endpoint',
$ref: 'urn:schema:ref'
}
}
const schemaMap = {
[base.$id]: base,
[refSchema.$id]: refSchema
}
const factory = AjvStandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
t.same(routeOpts, endpointSchema)
t.type(schemaValidationCode, 'string')
fs.writeFileSync(path.join(__dirname, '/ajv-generated.js'), schemaValidationCode)
generatedFileNames.push('/ajv-generated.js')
t.pass('stored the validation function')
}
})
const compiler = factory(schemaMap)
compiler(endpointSchema)
t.pass('compiled the endpoint schema')
t.test('usage standalone code', t => {
t.plan(3)
const standaloneValidate = require('./ajv-generated')
const valid = standaloneValidate({ hello: 'world' })
t.ok(valid)
const invalid = standaloneValidate({ hello: [] })
t.notOk(invalid)
t.ok(standaloneValidate)
})
})
t.test('fastify integration - writeMode', async t => {
t.plan(6)
const factory = AjvStandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
const fileName = generateFileName(routeOpts)
t.ok(routeOpts)
fs.writeFileSync(path.join(__dirname, fileName), schemaValidationCode)
t.pass('stored the validation function')
generatedFileNames.push(fileName)
},
restoreFunction () {
t.fail('write mode ON')
}
})
const app = buildApp(factory)
await app.ready()
})
t.test('fastify integration - readMode', async t => {
t.plan(6)
const factory = AjvStandaloneValidator({
readMode: true,
storeFunction () {
t.fail('read mode ON')
},
restoreFunction (routeOpts) {
t.pass('restore the validation function')
const fileName = generateFileName(routeOpts)
return require(path.join(__dirname, fileName))
}
})
const app = buildApp(factory)
await app.ready()
let res = await app.inject({
url: '/foo',
method: 'POST',
payload: { hello: [] }
})
t.equal(res.statusCode, 400)
res = await app.inject({
url: '/bar?lang=invalid',
method: 'GET'
})
t.equal(res.statusCode, 400)
res = await app.inject({
url: '/bar?lang=it',
method: 'GET'
})
t.equal(res.statusCode, 200)
})
function buildApp (factory) {
const app = fastify({
jsonShorthand: false,
schemaController: {
compilersFactory: {
buildValidator: factory
}
}
})
app.addSchema({
$id: 'urn:schema:foo',
type: 'object',
properties: {
name: { type: 'string' },
id: { type: 'integer' }
}
})
app.post('/foo', {
schema: {
body: {
$id: 'urn:schema:body',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:foo#/properties/name' }
}
}
}
}, () => { return 'ok' })
app.get('/bar', {
schema: {
query: {
$id: 'urn:schema:query',
type: 'object',
properties: {
lang: { type: 'string', enum: ['it', 'en'] }
}
}
}
}, () => { return 'ok' })
return app
}
})

72
node_modules/@fastify/ajv-compiler/types/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import _ajv, { AnySchema, Options as AjvOptions, ValidateFunction } from 'ajv'
import AjvJTD, { JTDOptions } from 'ajv/dist/jtd'
import type { Options, ErrorObject } from 'ajv'
import { AnyValidateFunction } from 'ajv/dist/core'
type Ajv = _ajv
type AjvSerializerGenerator = typeof AjvCompiler
type AjvJTDCompile = AjvJTD['compileSerializer']
type AjvCompile = (schema: AnySchema, _meta?: boolean) => AnyValidateFunction
declare function buildCompilerFromPool (externalSchemas: { [key: string]: AnySchema | AnySchema[] }, options?: { mode: 'JTD'; customOptions?: JTDOptions; onCreate?: (ajvInstance: Ajv) => void }): AjvCompile
declare function buildCompilerFromPool (externalSchemas: { [key: string]: AnySchema | AnySchema[] }, options?: { mode?: never; customOptions?: AjvOptions; onCreate?: (ajvInstance: Ajv) => void }): AjvCompile
declare function buildSerializerFromPool (externalSchemas: any, serializerOpts?: { mode?: never; } & JTDOptions): AjvJTDCompile
declare function AjvCompiler (opts: { jtdSerializer: true }): AjvCompiler.BuildSerializerFromPool
declare function AjvCompiler (opts?: { jtdSerializer?: false }): AjvCompiler.BuildCompilerFromPool
declare function StandaloneValidator (options: AjvCompiler.StandaloneOptions): AjvCompiler.BuildCompilerFromPool
declare namespace AjvCompiler {
export type { Options, ErrorObject }
export { Ajv }
export type BuildSerializerFromPool = typeof buildSerializerFromPool
export type BuildCompilerFromPool = typeof buildCompilerFromPool
export const AjvReference: Symbol
export enum HttpParts {
Body = 'body',
Headers = 'headers',
Params = 'params',
Query = 'querystring',
}
export type RouteDefinition = {
method: string,
url: string,
httpPart: HttpParts,
schema?: unknown,
}
export type StandaloneRestoreFunction = (opts: RouteDefinition) => ValidateFunction
export type StandaloneStoreFunction = (opts: RouteDefinition, schemaValidationCode: string) => void
export type StandaloneOptionsReadModeOn = {
readMode: true;
restoreFunction?: StandaloneRestoreFunction
}
export type StandaloneOptionsReadModeOff = {
readMode?: false | undefined;
storeFunction?: StandaloneStoreFunction;
}
export type StandaloneOptions = StandaloneOptionsReadModeOn | StandaloneOptionsReadModeOff
export type ValidatorFactory = BuildCompilerFromPool | BuildSerializerFromPool
export type ValidatorCompiler = ReturnType<ValidatorFactory>
export { StandaloneValidator }
export const AjvCompiler: AjvSerializerGenerator
export { AjvCompiler as default }
}
export = AjvCompiler

View File

@@ -0,0 +1,226 @@
import { AnySchemaObject, ValidateFunction } from 'ajv'
import { AnyValidateFunction } from 'ajv/dist/core'
import { expectAssignable, expectType } from 'tsd'
import AjvCompiler, { AjvReference, ValidatorFactory, StandaloneValidator, RouteDefinition, ErrorObject, BuildCompilerFromPool, BuildSerializerFromPool, ValidatorCompiler } from '..'
{
const compiler = AjvCompiler({})
expectType<BuildCompilerFromPool>(compiler)
}
{
const compiler = AjvCompiler()
expectType<BuildCompilerFromPool>(compiler)
}
{
const compiler = AjvCompiler({ jtdSerializer: false })
expectType<BuildCompilerFromPool>(compiler)
}
{
const factory = AjvCompiler({ jtdSerializer: false })
expectType<BuildCompilerFromPool>(factory)
factory({}, {
onCreate (ajv) {
expectType<import('ajv').default>(ajv)
}
})
}
{
const compiler = AjvCompiler({ jtdSerializer: true })
expectType<BuildSerializerFromPool>(compiler)
}
const reader = StandaloneValidator({
readMode: true,
restoreFunction: (route: RouteDefinition) => {
expectAssignable<RouteDefinition>(route)
return {} as ValidateFunction
},
})
expectAssignable<ValidatorFactory>(reader)
const writer = StandaloneValidator({
readMode: false,
storeFunction: (route: RouteDefinition, code: string) => {
expectAssignable<RouteDefinition>(route)
expectAssignable<string>(code)
},
})
expectAssignable<ValidatorFactory>(writer)
expectType<unknown>(({} as ErrorObject).data)
expectType<string>(({} as ErrorObject).instancePath)
expectType<string>(({} as ErrorObject).keyword)
expectType<string | undefined>(({} as ErrorObject).message)
expectType<Record<string, any>>(({} as ErrorObject).params)
expectType<AnySchemaObject | undefined>(({} as ErrorObject).parentSchema)
expectType<string | undefined>(({} as ErrorObject).propertyName)
expectType<unknown>(({} as ErrorObject).schema)
expectType<string>(({} as ErrorObject).schemaPath)
expectType<Symbol>(AjvReference)
{
const jtdSchema = {
discriminator: 'version',
mapping: {
1: {
properties: {
foo: { type: 'uint8' }
}
},
2: {
properties: {
foo: { type: 'string' }
}
}
}
}
const externalSchemas1 = {
foo: {
definitions: {
coordinates: {
properties: {
lat: { type: 'float32' },
lng: { type: 'float32' }
}
}
}
}
}
const factory = AjvCompiler({ jtdSerializer: true })
expectType<BuildSerializerFromPool>(factory)
const compiler = factory(externalSchemas1, {})
expectAssignable<Function>(compiler)
const serializeFunc = compiler({ schema: jtdSchema })
expectType<(data: unknown) => string>(serializeFunc)
expectType<string>(serializeFunc({ version: '1', foo: 42 }))
}
// JTD
{
const factory = AjvCompiler()
expectType<BuildCompilerFromPool>(factory)
const jtdSchema = {
discriminator: 'version',
mapping: {
1: {
properties: {
foo: { type: 'uint8' }
}
},
2: {
properties: {
foo: { type: 'string' }
}
}
}
}
const compiler = factory({}, {
customOptions: {},
mode: 'JTD'
})
expectAssignable<ValidatorCompiler>(compiler)
const validatorFunc = compiler({ schema: jtdSchema })
expectAssignable<ValidateFunction>(validatorFunc)
expectType<boolean | Promise<any>>(validatorFunc({
version: '2',
foo: []
}))
}
// generate standalone code
{
const base = {
$id: 'urn:schema:base',
definitions: {
hello: { type: 'string' }
},
type: 'object',
properties: {
hello: { $ref: '#/definitions/hello' }
}
}
const refSchema = {
$id: 'urn:schema:ref',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:base#/definitions/hello' }
}
}
const endpointSchema = {
schema: {
$id: 'urn:schema:endpoint',
$ref: 'urn:schema:ref'
}
}
const schemaMap = {
[base.$id]: base,
[refSchema.$id]: refSchema
}
const factory = StandaloneValidator({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
expectType<RouteDefinition>(routeOpts)
expectType<string>(schemaValidationCode)
}
})
expectAssignable<ValidatorFactory>(factory)
const compiler = factory(schemaMap)
expectAssignable<ValidatorCompiler>(compiler)
expectAssignable<Function>(compiler(endpointSchema))
}
{
const base = {
$id: 'urn:schema:base',
definitions: {
hello: { type: 'string' }
},
type: 'object',
properties: {
hello: { $ref: '#/definitions/hello' }
}
}
const refSchema = {
$id: 'urn:schema:ref',
type: 'object',
properties: {
hello: { $ref: 'urn:schema:base#/definitions/hello' }
}
}
const endpointSchema = {
schema: {
$id: 'urn:schema:endpoint',
$ref: 'urn:schema:ref'
}
}
const schemaMap = {
[base.$id]: base,
[refSchema.$id]: refSchema
}
const factory = StandaloneValidator({
readMode: true,
restoreFunction (routeOpts) {
expectType<RouteDefinition>(routeOpts)
return {} as ValidateFunction
}
})
expectAssignable<ValidatorFactory>(factory)
const compiler = factory(schemaMap)
expectAssignable<ValidatorCompiler>(compiler)
expectType<AnyValidateFunction<any>>(compiler(endpointSchema))
}