@fastify/ajv-compiler
Advanced tools
Comparing version 3.5.0 to 3.6.0
@@ -35,2 +35,4 @@ 'use strict' | ||
options.onCreate?.(this.ajv) | ||
const sourceSchemas = Object.values(externalSchemas) | ||
@@ -37,0 +39,0 @@ for (const extSchema of sourceSchemas) { |
{ | ||
"name": "@fastify/ajv-compiler", | ||
"version": "3.5.0", | ||
"version": "3.6.0", | ||
"description": "Build and manage the AJV instances for the fastify framework", | ||
"main": "index.js", | ||
"type": "commonjs", | ||
"types": "types/index.d.ts", | ||
@@ -15,3 +16,2 @@ "directories": { | ||
"test": "npm run unit && npm run test:typescript", | ||
"posttest": "rimraf test/ajv-generated*.js", | ||
"test:typescript": "tsd", | ||
@@ -42,10 +42,9 @@ "ajv:compile": "ajv compile -s test/source.json -o test/validate_schema.js" | ||
"ajv-merge-patch": "^5.0.1", | ||
"cronometro": "^1.1.4", | ||
"cronometro": "^3.0.1", | ||
"fastify": "^4.0.0", | ||
"require-from-string": "^2.0.2", | ||
"rimraf": "^3.0.2", | ||
"sanitize-filename": "^1.6.3", | ||
"standard": "^17.0.0", | ||
"tap": "^16.2.0", | ||
"tsd": "^0.25.0" | ||
"tsd": "^0.31.0" | ||
}, | ||
@@ -52,0 +51,0 @@ "dependencies": { |
@@ -40,3 +40,3 @@ # @fastify/ajv-compiler | ||
To customize the `ajv`'s options, see how in the [Fastify official docs](https://www.fastify.io/docs/latest/Reference/Server/#ajv). | ||
To customize the `ajv`'s options, see how in the [Fastify official docs](https://fastify.dev/docs/latest/Reference/Server/#ajv). | ||
@@ -47,3 +47,3 @@ | ||
This module is already used as default by Fastify. | ||
If you need to provide to your server instance a different version, refer to [the official doc](https://www.fastify.io/docs/latest/Reference/Server/#schemacontroller). | ||
If you need to provide to your server instance a different version, refer to [the official doc](https://fastify.dev/docs/latest/Reference/Server/#schemacontroller). | ||
@@ -67,2 +67,18 @@ ### Customize the `ajv-formats` plugin | ||
### 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 syncronous 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 | ||
@@ -209,3 +225,3 @@ | ||
This module provide a factory function to produce [Validator Compilers](https://www.fastify.io/docs/latest/Reference/Server/#validatorcompiler) functions. | ||
This module provide a factory function to produce [Validator Compilers](https://fastify.dev/docs/latest/Reference/Server/#validatorcompiler) functions. | ||
@@ -218,3 +234,3 @@ 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. | ||
- the AJV configuration: it is [one per server](https://www.fastify.io/docs/latest/Reference/Server/#ajv) | ||
- 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 inizialization | ||
@@ -221,0 +237,0 @@ |
@@ -139,2 +139,33 @@ 'use strict' | ||
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 | ||
@@ -141,0 +172,0 @@ t.test('compile same $id when in external schema', t => { |
'use strict' | ||
const fs = require('fs') | ||
const path = require('path') | ||
const fs = require('node:fs') | ||
const path = require('node:path') | ||
const t = require('tap') | ||
@@ -15,176 +15,190 @@ const fastify = require('fastify') | ||
t.test('errors', t => { | ||
t.plan(2) | ||
t.throws(() => { | ||
AjvStandaloneValidator() | ||
}, 'missing restoreFunction') | ||
t.throws(() => { | ||
AjvStandaloneValidator({ readMode: false }) | ||
}, 'missing storeFunction') | ||
}) | ||
const generatedFileNames = [] | ||
t.test('generate standalone code', t => { | ||
t.plan(5) | ||
t.test('standalone', t => { | ||
t.plan(4) | ||
const base = { | ||
$id: 'urn:schema:base', | ||
definitions: { | ||
hello: { type: 'string' } | ||
}, | ||
type: 'object', | ||
properties: { | ||
hello: { $ref: '#/definitions/hello' } | ||
t.teardown(async () => { | ||
for (const fileName of generatedFileNames) { | ||
await fs.promises.unlink(path.join(__dirname, fileName)) | ||
} | ||
} | ||
}) | ||
const refSchema = { | ||
$id: 'urn:schema:ref', | ||
type: 'object', | ||
properties: { | ||
hello: { $ref: 'urn:schema:base#/definitions/hello' } | ||
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 endpointSchema = { | ||
schema: { | ||
$id: 'urn:schema:endpoint', | ||
$ref: 'urn:schema:ref' | ||
const refSchema = { | ||
$id: 'urn:schema:ref', | ||
type: 'object', | ||
properties: { | ||
hello: { $ref: 'urn:schema:base#/definitions/hello' } | ||
} | ||
} | ||
} | ||
const schemaMap = { | ||
[base.$id]: base, | ||
[refSchema.$id]: refSchema | ||
} | ||
const endpointSchema = { | ||
schema: { | ||
$id: 'urn:schema:endpoint', | ||
$ref: 'urn:schema:ref' | ||
} | ||
} | ||
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) | ||
t.pass('stored the validation function') | ||
const schemaMap = { | ||
[base.$id]: base, | ||
[refSchema.$id]: refSchema | ||
} | ||
}) | ||
const compiler = factory(schemaMap) | ||
compiler(endpointSchema) | ||
t.pass('compiled the endpoint schema') | ||
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') | ||
} | ||
}) | ||
t.test('usage standalone code', t => { | ||
t.plan(3) | ||
const standaloneValidate = require('./ajv-generated') | ||
const compiler = factory(schemaMap) | ||
compiler(endpointSchema) | ||
t.pass('compiled the endpoint schema') | ||
const valid = standaloneValidate({ hello: 'world' }) | ||
t.ok(valid) | ||
t.test('usage standalone code', t => { | ||
t.plan(3) | ||
const standaloneValidate = require('./ajv-generated') | ||
const invalid = standaloneValidate({ hello: [] }) | ||
t.notOk(invalid) | ||
const valid = standaloneValidate({ hello: 'world' }) | ||
t.ok(valid) | ||
t.ok(standaloneValidate) | ||
const invalid = standaloneValidate({ hello: [] }) | ||
t.notOk(invalid) | ||
t.ok(standaloneValidate) | ||
}) | ||
}) | ||
}) | ||
t.test('fastify integration - writeMode', async t => { | ||
t.plan(6) | ||
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') | ||
}, | ||
restoreFunction () { | ||
t.fail('write mode ON') | ||
} | ||
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() | ||
}) | ||
const app = buildApp(factory) | ||
await app.ready() | ||
}) | ||
t.test('fastify integration - readMode', async t => { | ||
t.plan(6) | ||
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 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() | ||
const app = buildApp(factory) | ||
await app.ready() | ||
let res = await app.inject({ | ||
url: '/foo', | ||
method: 'POST', | ||
payload: { hello: [] } | ||
}) | ||
t.equal(res.statusCode, 400) | ||
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=invalid', | ||
method: 'GET' | ||
res = await app.inject({ | ||
url: '/bar?lang=it', | ||
method: 'GET' | ||
}) | ||
t.equal(res.statusCode, 200) | ||
}) | ||
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 | ||
} | ||
} | ||
}) | ||
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.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' } | ||
app.post('/foo', { | ||
schema: { | ||
body: { | ||
$id: 'urn:schema:body', | ||
type: 'object', | ||
properties: { | ||
hello: { $ref: 'urn:schema:foo#/properties/name' } | ||
} | ||
} | ||
} | ||
} | ||
}, () => { return 'ok' }) | ||
}, () => { return 'ok' }) | ||
app.get('/bar', { | ||
schema: { | ||
query: { | ||
$id: 'urn:schema:query', | ||
type: 'object', | ||
properties: { | ||
lang: { type: 'string', enum: ['it', 'en'] } | ||
app.get('/bar', { | ||
schema: { | ||
query: { | ||
$id: 'urn:schema:query', | ||
type: 'object', | ||
properties: { | ||
lang: { type: 'string', enum: ['it', 'en'] } | ||
} | ||
} | ||
} | ||
} | ||
}, () => { return 'ok' }) | ||
}, () => { return 'ok' }) | ||
return app | ||
} | ||
return app | ||
} | ||
}) |
@@ -62,4 +62,4 @@ import { AnySchema, default as _ajv, Options as AjvOptions, ValidateFunction } from "ajv"; | ||
declare function buildCompilerFromPool(externalSchemas: { [key: string]: AnySchema | AnySchema[] }, options?: { mode: 'JTD'; customOptions?: JTDOptions }): AjvCompile | ||
declare function buildCompilerFromPool(externalSchemas: { [key: string]: AnySchema | AnySchema[] }, options?: { mode?: never; customOptions?: AjvOptions }): AjvCompile | ||
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 | ||
@@ -66,0 +66,0 @@ declare function buildSerializerFromPool(externalSchemas: any, serializerOpts?: { mode?: never; } & JTDOptions): AjvJTDCompile |
@@ -18,3 +18,14 @@ import { AnySchemaObject, ValidateFunction } from "ajv"; | ||
} | ||
{ | ||
const factory = AjvCompiler({ jtdSerializer: false }); | ||
expectType<BuildCompilerFromPool>(factory); | ||
factory({}, { | ||
onCreate(ajv) { | ||
expectType<import("ajv").default>(ajv) | ||
} | ||
}); | ||
} | ||
{ | ||
const compiler = AjvCompiler({ jtdSerializer: true}); | ||
@@ -21,0 +32,0 @@ expectType<BuildSerializerFromPool>(compiler); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
53075
11
23
1422
237
1