@shadow-library/fastify 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +436 -0
- package/cjs/decorators/http-input.decorator.d.ts +2 -3
- package/cjs/decorators/http-output.decorator.d.ts +3 -0
- package/cjs/decorators/http-output.decorator.js +0 -4
- package/cjs/decorators/index.d.ts +1 -0
- package/cjs/decorators/index.js +1 -0
- package/cjs/decorators/transform.decorator.d.ts +27 -0
- package/cjs/decorators/transform.decorator.js +17 -0
- package/cjs/interfaces/server-metadata.interface.d.ts +2 -2
- package/cjs/module/data-transformers.d.ts +14 -0
- package/cjs/module/data-transformers.js +19 -0
- package/cjs/module/fastify-module.interface.d.ts +5 -0
- package/cjs/module/fastify-router.d.ts +17 -5
- package/cjs/module/fastify-router.js +136 -30
- package/cjs/module/fastify.utils.d.ts +9 -2
- package/cjs/module/fastify.utils.js +15 -10
- package/esm/decorators/http-input.decorator.d.ts +2 -3
- package/esm/decorators/http-output.decorator.d.ts +3 -0
- package/esm/decorators/http-output.decorator.js +0 -4
- package/esm/decorators/index.d.ts +1 -0
- package/esm/decorators/index.js +1 -0
- package/esm/decorators/transform.decorator.d.ts +27 -0
- package/esm/decorators/transform.decorator.js +14 -0
- package/esm/interfaces/server-metadata.interface.d.ts +2 -2
- package/esm/module/data-transformers.d.ts +14 -0
- package/esm/module/data-transformers.js +16 -0
- package/esm/module/fastify-module.interface.d.ts +5 -0
- package/esm/module/fastify-router.d.ts +17 -5
- package/esm/module/fastify-router.js +136 -30
- package/esm/module/fastify.utils.d.ts +9 -2
- package/esm/module/fastify.utils.js +15 -10
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ import assert from 'node:assert';
|
|
|
17
17
|
import { Inject, Injectable, Router } from '@shadow-library/app';
|
|
18
18
|
import { ClassSchema, TransformerFactory } from '@shadow-library/class-schema';
|
|
19
19
|
import { InternalError, Logger, utils } from '@shadow-library/common';
|
|
20
|
-
import
|
|
20
|
+
import { all as deepmerge } from 'deepmerge';
|
|
21
21
|
import findMyWay from 'find-my-way';
|
|
22
22
|
import stringify from 'json-stable-stringify';
|
|
23
23
|
/**
|
|
@@ -26,11 +26,13 @@ import stringify from 'json-stable-stringify';
|
|
|
26
26
|
import { FASTIFY_CONFIG, FASTIFY_INSTANCE, HTTP_CONTROLLER_INPUTS, HTTP_CONTROLLER_TYPE, NAMESPACE } from '../constants.js';
|
|
27
27
|
import { HttpMethod } from '../decorators/index.js';
|
|
28
28
|
import { ContextService } from '../services/index.js';
|
|
29
|
+
import { INBUILT_TRANSFORMERS } from './data-transformers.js';
|
|
29
30
|
/**
|
|
30
31
|
* Declaring the constants
|
|
31
32
|
*/
|
|
32
33
|
const httpMethods = Object.values(HttpMethod).filter(m => m !== HttpMethod.ALL);
|
|
33
|
-
const DEFAULT_ARTIFACTS = {
|
|
34
|
+
const DEFAULT_ARTIFACTS = { masks: {}, transformers: {} };
|
|
35
|
+
const isClassSchema = (schema) => typeof schema === 'function' || (Array.isArray(schema) && typeof schema[0] === 'function');
|
|
34
36
|
let FastifyRouter = class FastifyRouter extends Router {
|
|
35
37
|
config;
|
|
36
38
|
instance;
|
|
@@ -39,12 +41,17 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
39
41
|
logger = Logger.getLogger(NAMESPACE, 'FastifyRouter');
|
|
40
42
|
cachedDynamicMiddlewares = new Map();
|
|
41
43
|
childRouter = null;
|
|
44
|
+
transformers = { ...INBUILT_TRANSFORMERS };
|
|
42
45
|
sensitiveTransformer = new TransformerFactory(s => s['x-fastify']?.sensitive === true);
|
|
46
|
+
inputDataTransformer = new TransformerFactory(s => this.isTransformable('input', s));
|
|
47
|
+
outputDataTransformer = new TransformerFactory(s => this.isTransformable('output', s));
|
|
43
48
|
constructor(config, instance, context) {
|
|
44
49
|
super();
|
|
45
50
|
this.config = config;
|
|
46
51
|
this.instance = instance;
|
|
47
52
|
this.context = context;
|
|
53
|
+
if (config.transformers)
|
|
54
|
+
Object.assign(this.transformers, config.transformers);
|
|
48
55
|
if (config.enableChildRoutes) {
|
|
49
56
|
const options = utils.object.pickKeys(config, ['ignoreTrailingSlash', 'ignoreDuplicateSlashes', 'allowUnsafeRegex', 'caseSensitive', 'maxParamLength', 'querystringParser']);
|
|
50
57
|
this.childRouter = findMyWay(options);
|
|
@@ -53,6 +60,10 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
53
60
|
getInstance() {
|
|
54
61
|
return this.instance;
|
|
55
62
|
}
|
|
63
|
+
isTransformable(type, schema) {
|
|
64
|
+
const transformerType = schema['x-fastify']?.transform?.[type];
|
|
65
|
+
return typeof transformerType === 'string' && transformerType in this.transformers;
|
|
66
|
+
}
|
|
56
67
|
joinPaths(...parts) {
|
|
57
68
|
const path = parts
|
|
58
69
|
.filter(p => typeof p === 'string')
|
|
@@ -61,6 +72,13 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
61
72
|
.join('/');
|
|
62
73
|
return `/${path}`;
|
|
63
74
|
}
|
|
75
|
+
addRouteHandler(routeOptions, hook, handler, action = 'append') {
|
|
76
|
+
const existingHandlers = Array.isArray(routeOptions[hook]) ? routeOptions[hook] : routeOptions[hook] ? [routeOptions[hook]] : [];
|
|
77
|
+
if (action === 'prepend')
|
|
78
|
+
routeOptions[hook] = [handler, ...existingHandlers];
|
|
79
|
+
else
|
|
80
|
+
routeOptions[hook] = [...existingHandlers, handler];
|
|
81
|
+
}
|
|
64
82
|
registerRawBody() {
|
|
65
83
|
const opts = { parseAs: 'buffer' };
|
|
66
84
|
const parser = this.instance.getDefaultJsonParser('error', 'error');
|
|
@@ -82,7 +100,16 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
82
100
|
return utils.string.maskWords(stringified);
|
|
83
101
|
return '****';
|
|
84
102
|
}
|
|
103
|
+
generateDataTransformer(type) {
|
|
104
|
+
return (value, schema) => {
|
|
105
|
+
const transformType = schema['x-fastify']?.transform?.[type];
|
|
106
|
+
const transformer = this.transformers[transformType];
|
|
107
|
+
assert(transformer, `transformer '${transformType}' not found`);
|
|
108
|
+
return transformer(value);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
85
111
|
getRequestLogger() {
|
|
112
|
+
const mask = this.maskField.bind(this);
|
|
86
113
|
return (req, res, done) => {
|
|
87
114
|
const startTime = process.hrtime();
|
|
88
115
|
res.raw.on('finish', () => {
|
|
@@ -90,7 +117,7 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
90
117
|
if (isLoggingDisabled)
|
|
91
118
|
return;
|
|
92
119
|
const { url, config } = req.routeOptions;
|
|
93
|
-
const {
|
|
120
|
+
const { masks } = config.artifacts ?? DEFAULT_ARTIFACTS;
|
|
94
121
|
const metadata = {};
|
|
95
122
|
metadata.rid = this.context.getRID();
|
|
96
123
|
metadata.url = url ?? req.raw.url;
|
|
@@ -103,11 +130,11 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
103
130
|
const resTime = process.hrtime(startTime);
|
|
104
131
|
metadata.timeTaken = (resTime[0] * 1e3 + resTime[1] * 1e-6).toFixed(3); // Converting time to milliseconds
|
|
105
132
|
if (req.body)
|
|
106
|
-
metadata.body =
|
|
133
|
+
metadata.body = masks.body ? masks.body(structuredClone(req.body), mask) : req.body;
|
|
107
134
|
if (req.query)
|
|
108
|
-
metadata.query =
|
|
135
|
+
metadata.query = masks.query ? masks.query(structuredClone(req.query), mask) : req.query;
|
|
109
136
|
if (req.params)
|
|
110
|
-
metadata.params =
|
|
137
|
+
metadata.params = masks.params ? masks.params(structuredClone(req.params), mask) : req.params;
|
|
111
138
|
this.logger.http(`${req.method} ${metadata.url} -> ${res.statusCode} (${metadata.timeTaken}ms)`, metadata);
|
|
112
139
|
});
|
|
113
140
|
done();
|
|
@@ -200,6 +227,52 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
200
227
|
this.cachedDynamicMiddlewares.set(cacheKey, handler);
|
|
201
228
|
return handler;
|
|
202
229
|
}
|
|
230
|
+
transformResponseHandler() {
|
|
231
|
+
const transform = this.generateDataTransformer('output');
|
|
232
|
+
return async (request, reply, payload) => {
|
|
233
|
+
const statusCode = String(reply.statusCode);
|
|
234
|
+
const responseTransformers = request.routeOptions.config.artifacts?.transformers.response;
|
|
235
|
+
if (!responseTransformers)
|
|
236
|
+
return payload;
|
|
237
|
+
let transformer = responseTransformers[statusCode];
|
|
238
|
+
if (!transformer) {
|
|
239
|
+
const fallbackStatus = statusCode.charAt(0) + 'xx';
|
|
240
|
+
transformer = responseTransformers[fallbackStatus];
|
|
241
|
+
this.logger.debug(`using fallback response transformer for status code ${statusCode}`, { fallbackStatus });
|
|
242
|
+
}
|
|
243
|
+
if (transformer) {
|
|
244
|
+
this.logger.debug(`transforming response for status code ${statusCode}`);
|
|
245
|
+
const cloned = deepmerge([{}, payload]);
|
|
246
|
+
const data = transformer(cloned, transform);
|
|
247
|
+
this.logger.debug(`transformed response for status code ${statusCode}`, { data });
|
|
248
|
+
return data;
|
|
249
|
+
}
|
|
250
|
+
return payload;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
transformRequestHandler() {
|
|
254
|
+
const transform = this.generateDataTransformer('input');
|
|
255
|
+
return async (request) => {
|
|
256
|
+
const transformers = request.routeOptions.config.artifacts?.transformers;
|
|
257
|
+
if (!transformers)
|
|
258
|
+
return;
|
|
259
|
+
if (transformers.body && request.body) {
|
|
260
|
+
this.logger.debug('transforming request body', { body: request.body });
|
|
261
|
+
request.body = transformers.body(request.body, transform);
|
|
262
|
+
this.logger.debug('transformed request body', { body: request.body });
|
|
263
|
+
}
|
|
264
|
+
if (transformers.query && request.query) {
|
|
265
|
+
this.logger.debug('transforming request query', { query: request.query });
|
|
266
|
+
request.query = transformers.query(request.query, transform);
|
|
267
|
+
this.logger.debug('transformed request query', { query: request.query });
|
|
268
|
+
}
|
|
269
|
+
if (transformers.params && request.params) {
|
|
270
|
+
this.logger.debug('transforming request params', { params: request.params });
|
|
271
|
+
request.params = transformers.params(request.params, transform);
|
|
272
|
+
this.logger.debug('transformed request params', { params: request.params });
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
203
276
|
async register(controllers) {
|
|
204
277
|
const { middlewares, routes } = this.parseControllers(controllers);
|
|
205
278
|
const defaultResponseSchemas = this.config.responseSchema ?? {};
|
|
@@ -216,7 +289,7 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
216
289
|
assert(metadata.method, 'Route method is required');
|
|
217
290
|
this.logger.debug(`registering route ${metadata.method} ${metadata.path}`);
|
|
218
291
|
const fastifyRouteOptions = utils.object.omitKeys(metadata, ['path', 'method', 'schemas', 'rawBody', 'status', 'headers', 'redirect', 'render']);
|
|
219
|
-
const artifacts = {
|
|
292
|
+
const artifacts = { masks: {}, transformers: {} };
|
|
220
293
|
const routeOptions = { ...fastifyRouteOptions, config: { metadata, artifacts } };
|
|
221
294
|
routeOptions.url = metadata.path;
|
|
222
295
|
routeOptions.method = metadata.method === HttpMethod.ALL ? httpMethods : [metadata.method];
|
|
@@ -228,45 +301,78 @@ let FastifyRouter = class FastifyRouter extends Router {
|
|
|
228
301
|
const handler = await this.getMiddlewareHandler(middleware, metadata);
|
|
229
302
|
if (typeof handler === 'function') {
|
|
230
303
|
this.logger.debug(`applying '${type}' middleware '${name}'`);
|
|
231
|
-
|
|
232
|
-
if (middlewareHandler)
|
|
233
|
-
middlewareHandler.push(handler);
|
|
234
|
-
else
|
|
235
|
-
routeOptions[type] = [handler];
|
|
304
|
+
this.addRouteHandler(routeOptions, type, handler);
|
|
236
305
|
}
|
|
237
306
|
}
|
|
238
|
-
|
|
307
|
+
const responseSchemas = { ...defaultResponseSchemas };
|
|
308
|
+
routeOptions.schema = { response: responseSchemas };
|
|
239
309
|
routeOptions.attachValidation = metadata.silentValidation ?? false;
|
|
240
|
-
|
|
241
|
-
const { body: bodySchema, params: paramsSchema, query: querySchema } = metadata.schemas ?? {};
|
|
310
|
+
const { body: bodySchema, params: paramsSchema, query: querySchema, response: responseSchema } = metadata.schemas ?? {};
|
|
242
311
|
const isMaskEnabled = this.config.maskSensitiveData ?? true;
|
|
243
312
|
if (bodySchema) {
|
|
244
|
-
const schema =
|
|
313
|
+
const schema = isClassSchema(bodySchema) ? ClassSchema.generate(bodySchema) : bodySchema;
|
|
245
314
|
routeOptions.schema.body = schema;
|
|
246
|
-
if (ClassSchema.isBranded(schema)
|
|
247
|
-
const
|
|
248
|
-
if (
|
|
249
|
-
artifacts.
|
|
315
|
+
if (ClassSchema.isBranded(schema)) {
|
|
316
|
+
const bodyTransformer = this.inputDataTransformer.maybeCompile(schema);
|
|
317
|
+
if (bodyTransformer)
|
|
318
|
+
artifacts.transformers.body = bodyTransformer;
|
|
319
|
+
if (isMaskEnabled) {
|
|
320
|
+
const transformer = this.sensitiveTransformer.maybeCompile(schema);
|
|
321
|
+
if (transformer)
|
|
322
|
+
artifacts.masks.body = transformer;
|
|
323
|
+
}
|
|
250
324
|
}
|
|
251
325
|
}
|
|
252
326
|
if (paramsSchema) {
|
|
253
|
-
const schema =
|
|
327
|
+
const schema = isClassSchema(paramsSchema) ? ClassSchema.generate(paramsSchema) : paramsSchema;
|
|
254
328
|
routeOptions.schema.params = schema;
|
|
255
|
-
if (ClassSchema.isBranded(schema)
|
|
256
|
-
const
|
|
257
|
-
if (
|
|
258
|
-
artifacts.
|
|
329
|
+
if (ClassSchema.isBranded(schema)) {
|
|
330
|
+
const paramsTransformer = this.inputDataTransformer.maybeCompile(schema);
|
|
331
|
+
if (paramsTransformer)
|
|
332
|
+
artifacts.transformers.params = paramsTransformer;
|
|
333
|
+
if (isMaskEnabled) {
|
|
334
|
+
const transformer = this.sensitiveTransformer.maybeCompile(schema);
|
|
335
|
+
if (transformer)
|
|
336
|
+
artifacts.masks.params = transformer;
|
|
337
|
+
}
|
|
259
338
|
}
|
|
260
339
|
}
|
|
261
340
|
if (querySchema) {
|
|
262
|
-
const schema =
|
|
341
|
+
const schema = isClassSchema(querySchema) ? ClassSchema.generate(querySchema) : querySchema;
|
|
263
342
|
routeOptions.schema.querystring = schema;
|
|
264
|
-
if (ClassSchema.isBranded(schema)
|
|
265
|
-
const
|
|
266
|
-
if (
|
|
267
|
-
artifacts.
|
|
343
|
+
if (ClassSchema.isBranded(schema)) {
|
|
344
|
+
const queryTransformer = this.inputDataTransformer.maybeCompile(schema);
|
|
345
|
+
if (queryTransformer)
|
|
346
|
+
artifacts.transformers.query = queryTransformer;
|
|
347
|
+
if (isMaskEnabled) {
|
|
348
|
+
const transformer = this.sensitiveTransformer.maybeCompile(schema);
|
|
349
|
+
if (transformer)
|
|
350
|
+
artifacts.masks.query = transformer;
|
|
351
|
+
}
|
|
268
352
|
}
|
|
269
353
|
}
|
|
354
|
+
if (responseSchema) {
|
|
355
|
+
const responseTransformers = {};
|
|
356
|
+
artifacts.transformers.response = responseTransformers;
|
|
357
|
+
for (const [code, schemaDef] of Object.entries(responseSchema)) {
|
|
358
|
+
const statusCode = code.toLowerCase();
|
|
359
|
+
const schema = isClassSchema(schemaDef) ? ClassSchema.generate(schemaDef) : schemaDef;
|
|
360
|
+
responseSchemas[statusCode] = schema;
|
|
361
|
+
if (ClassSchema.isBranded(schema)) {
|
|
362
|
+
const transformer = this.outputDataTransformer.maybeCompile(schema);
|
|
363
|
+
if (transformer)
|
|
364
|
+
responseTransformers[statusCode] = transformer;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (Object.keys(responseTransformers).length > 0) {
|
|
368
|
+
const handler = this.transformResponseHandler();
|
|
369
|
+
this.addRouteHandler(routeOptions, 'preSerialization', handler);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if ('body' in artifacts.transformers || 'query' in artifacts.transformers || 'params' in artifacts.transformers) {
|
|
373
|
+
const handler = this.transformRequestHandler();
|
|
374
|
+
this.addRouteHandler(routeOptions, 'preHandler', handler, 'prepend');
|
|
375
|
+
}
|
|
270
376
|
this.logger.debug('route options', { options: routeOptions });
|
|
271
377
|
this.instance.route(routeOptions);
|
|
272
378
|
this.logger.info(`registered route ${metadata.method} ${routeOptions.url}`);
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { ValidationError } from '@shadow-library/common';
|
|
2
|
-
import { SchemaObject } from 'ajv';
|
|
2
|
+
import Ajv, { SchemaObject } from 'ajv';
|
|
3
3
|
import { FastifyInstance } from 'fastify';
|
|
4
4
|
import { FastifyRouteSchemaDef, FastifySchemaValidationError, FastifyValidationResult, SchemaErrorDataVar } from 'fastify/types/schema';
|
|
5
5
|
import { FastifyConfig, FastifyModuleOptions } from './fastify-module.interface.js';
|
|
6
|
+
/**
|
|
7
|
+
* Defining types
|
|
8
|
+
*/
|
|
9
|
+
export interface AjvValidators {
|
|
10
|
+
strictValidator: Ajv;
|
|
11
|
+
lenientValidator: Ajv;
|
|
12
|
+
}
|
|
6
13
|
export declare const notFoundHandler: () => never;
|
|
7
|
-
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject
|
|
14
|
+
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject>, validators: AjvValidators): FastifyValidationResult;
|
|
8
15
|
export declare function formatSchemaErrors(errors: FastifySchemaValidationError[], dataVar: SchemaErrorDataVar): ValidationError;
|
|
9
16
|
export declare function createFastifyInstance(config: FastifyConfig, fastifyFactory?: FastifyModuleOptions['fastifyFactory']): Promise<FastifyInstance>;
|
|
@@ -9,17 +9,13 @@ import { fastify } from 'fastify';
|
|
|
9
9
|
* Importing user defined packages
|
|
10
10
|
*/
|
|
11
11
|
import { ServerError, ServerErrorCode } from '../server.error.js';
|
|
12
|
-
/**
|
|
13
|
-
* Defining types
|
|
14
|
-
*/
|
|
15
12
|
/**
|
|
16
13
|
* Declaring the constants
|
|
17
14
|
*/
|
|
18
15
|
const keywords = ['x-fastify'];
|
|
19
16
|
const allowedHttpParts = ['body', 'params', 'querystring'];
|
|
20
|
-
const strictValidator = new Ajv({ allErrors: true, useDefaults: true, removeAdditional: true, strict: true, keywords });
|
|
21
|
-
const lenientValidator = new Ajv({ allErrors: true, coerceTypes: true, useDefaults: true, removeAdditional: true, strict: true, keywords });
|
|
22
17
|
const notFoundError = new ServerError(ServerErrorCode.S002);
|
|
18
|
+
const defaultAjvOptions = { allErrors: true, useDefaults: true, removeAdditional: true, strict: true, keywords };
|
|
23
19
|
export const notFoundHandler = () => throwError(notFoundError);
|
|
24
20
|
function compileSchema(ajv, schema) {
|
|
25
21
|
if (!schema.$id)
|
|
@@ -31,13 +27,13 @@ function compileSchema(ajv, schema) {
|
|
|
31
27
|
}
|
|
32
28
|
return ajv.getSchema(schema.$id);
|
|
33
29
|
}
|
|
34
|
-
export function compileValidator(routeSchema) {
|
|
30
|
+
export function compileValidator(routeSchema, validators) {
|
|
35
31
|
assert(allowedHttpParts.includes(routeSchema.httpPart), `Invalid httpPart: ${routeSchema.httpPart}`);
|
|
36
32
|
if (routeSchema.httpPart === 'body')
|
|
37
|
-
return compileSchema(strictValidator, routeSchema.schema);
|
|
33
|
+
return compileSchema(validators.strictValidator, routeSchema.schema);
|
|
38
34
|
if (routeSchema.httpPart === 'params')
|
|
39
|
-
return compileSchema(lenientValidator, routeSchema.schema);
|
|
40
|
-
const validate = compileSchema(lenientValidator, routeSchema.schema);
|
|
35
|
+
return compileSchema(validators.lenientValidator, routeSchema.schema);
|
|
36
|
+
const validate = compileSchema(validators.lenientValidator, routeSchema.schema);
|
|
41
37
|
return (data) => {
|
|
42
38
|
validate(data);
|
|
43
39
|
for (const error of validate.errors ?? []) {
|
|
@@ -68,10 +64,19 @@ export function formatSchemaErrors(errors, dataVar) {
|
|
|
68
64
|
export async function createFastifyInstance(config, fastifyFactory) {
|
|
69
65
|
const options = utils.object.omitKeys(config, ['port', 'host', 'errorHandler', 'responseSchema']);
|
|
70
66
|
const { errorHandler } = config;
|
|
67
|
+
const strictValidator = new Ajv({ ...defaultAjvOptions, ...config.ajv?.customOptions });
|
|
68
|
+
const lenientValidator = new Ajv({ ...defaultAjvOptions, coerceTypes: true, ...config.ajv?.customOptions });
|
|
69
|
+
for (let plugin of config.ajv?.plugins ?? []) {
|
|
70
|
+
if (typeof plugin === 'function')
|
|
71
|
+
plugin = [plugin, {}];
|
|
72
|
+
const [ajvPlugin, options] = plugin;
|
|
73
|
+
ajvPlugin(strictValidator, options);
|
|
74
|
+
ajvPlugin(lenientValidator, options);
|
|
75
|
+
}
|
|
71
76
|
const instance = fastify(options);
|
|
72
77
|
instance.setSchemaErrorFormatter(formatSchemaErrors);
|
|
73
78
|
instance.setNotFoundHandler(notFoundHandler);
|
|
74
79
|
instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
|
|
75
|
-
instance.setValidatorCompiler(compileValidator);
|
|
80
|
+
instance.setValidatorCompiler(routeSchema => compileValidator(routeSchema, { strictValidator, lenientValidator }));
|
|
76
81
|
return fastifyFactory ? await fastifyFactory(instance) : instance;
|
|
77
82
|
}
|
package/package.json
CHANGED