@bram-dc/fastify-type-provider-zod 3.1.0 → 3.1.2
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 +6 -3
- package/dist/src/ResponseValidationError.d.ts +10 -0
- package/dist/src/ResponseValidationError.js +15 -0
- package/dist/src/core.d.ts +78 -0
- package/dist/src/core.js +94 -0
- package/dist/src/errors.d.ts +18 -0
- package/dist/src/errors.js +30 -0
- package/dist/src/ref.d.ts +3 -0
- package/dist/src/ref.js +56 -0
- package/dist/src/zod-to-json.d.ts +7 -0
- package/dist/src/zod-to-json.js +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
## Please use the original package [fastify-type-provider-zod](https://www.npmjs.com/package/fastify-type-provider-zod)
|
|
2
|
+
This repository is no longer maintained since all the changed have been merged into the original package.
|
|
3
|
+
|
|
1
4
|
# Fastify Type Provider Zod
|
|
2
5
|
|
|
3
6
|
[](https://www.npmjs.com/package/@bram-dc/fastify-type-provider-zod)
|
|
@@ -8,7 +11,7 @@
|
|
|
8
11
|
|
|
9
12
|
```js
|
|
10
13
|
import Fastify from "fastify";
|
|
11
|
-
import { ZodSerializerCompiler, ZodValidatorCompiler, ZodTypeProvider } from "fastify-type-provider-zod";
|
|
14
|
+
import { ZodSerializerCompiler, ZodValidatorCompiler, ZodTypeProvider } from "@bram-dc/fastify-type-provider-zod";
|
|
12
15
|
import z from "zod";
|
|
13
16
|
|
|
14
17
|
const app = Fastify()
|
|
@@ -51,7 +54,7 @@ import {
|
|
|
51
54
|
ZodSerializerCompiler,
|
|
52
55
|
ZodValidatorCompiler,
|
|
53
56
|
ZodTypeProvider,
|
|
54
|
-
} from "fastify-type-provider-zod";
|
|
57
|
+
} from "@bram-dc/fastify-type-provider-zod";
|
|
55
58
|
|
|
56
59
|
const app = fastify();
|
|
57
60
|
app.setValidatorCompiler(ZodValidatorCompiler);
|
|
@@ -122,7 +125,7 @@ run();
|
|
|
122
125
|
|
|
123
126
|
```ts
|
|
124
127
|
import { z } from "zod";
|
|
125
|
-
import { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
|
|
128
|
+
import { FastifyPluginAsyncZod } from "@bram-dc/fastify-type-provider-zod";
|
|
126
129
|
|
|
127
130
|
const plugin: FastifyPluginAsyncZod = async function (fastify, _opts) {
|
|
128
131
|
fastify.route({
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { z, ZodIssue } from 'zod';
|
|
2
|
+
export type ResponseValidationErrorDetails = {
|
|
3
|
+
error: ZodIssue[];
|
|
4
|
+
method: string;
|
|
5
|
+
url: string;
|
|
6
|
+
};
|
|
7
|
+
export declare class ResponseValidationError extends Error {
|
|
8
|
+
details: ResponseValidationErrorDetails;
|
|
9
|
+
constructor(validationResult: z.SafeParseReturnType<unknown, unknown>, method: string, url: string);
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResponseValidationError = void 0;
|
|
4
|
+
class ResponseValidationError extends Error {
|
|
5
|
+
constructor(validationResult, method, url) {
|
|
6
|
+
super("Response doesn't match the schema");
|
|
7
|
+
this.name = 'ResponseValidationError';
|
|
8
|
+
this.details = {
|
|
9
|
+
error: validationResult.error?.issues ?? [],
|
|
10
|
+
method,
|
|
11
|
+
url,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.ResponseValidationError = ResponseValidationError;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { FastifyPluginAsync, FastifyPluginCallback, FastifyPluginOptions, FastifySchema, FastifySchemaCompiler, FastifyTypeProvider, RawServerBase, RawServerDefault } from 'fastify';
|
|
2
|
+
import type { FastifySerializerCompiler } from 'fastify/types/schema';
|
|
3
|
+
import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
|
|
4
|
+
import type { z } from 'zod';
|
|
5
|
+
type FreeformRecord = Record<string, any>;
|
|
6
|
+
export interface ZodTypeProvider extends FastifyTypeProvider {
|
|
7
|
+
validator: this['schema'] extends z.ZodTypeAny ? z.output<this['schema']> : unknown;
|
|
8
|
+
serializer: this['schema'] extends z.ZodTypeAny ? z.input<this['schema']> : unknown;
|
|
9
|
+
}
|
|
10
|
+
interface Schema extends FastifySchema {
|
|
11
|
+
hide?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const createJsonSchemaTransform: ({ skipList }: {
|
|
14
|
+
skipList: readonly string[];
|
|
15
|
+
}) => ({ schema, url }: {
|
|
16
|
+
schema: Schema;
|
|
17
|
+
url: string;
|
|
18
|
+
}) => {
|
|
19
|
+
schema: Schema;
|
|
20
|
+
url: string;
|
|
21
|
+
} | {
|
|
22
|
+
schema: FreeformRecord;
|
|
23
|
+
url: string;
|
|
24
|
+
};
|
|
25
|
+
export declare const jsonSchemaTransform: ({ schema, url }: {
|
|
26
|
+
schema: Schema;
|
|
27
|
+
url: string;
|
|
28
|
+
}) => {
|
|
29
|
+
schema: Schema;
|
|
30
|
+
url: string;
|
|
31
|
+
} | {
|
|
32
|
+
schema: FreeformRecord;
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
35
|
+
export declare const createJsonSchemaTransformObject: ({ schemas }: {
|
|
36
|
+
schemas: Record<string, z.ZodTypeAny>;
|
|
37
|
+
}) => (input: {
|
|
38
|
+
swaggerObject: Partial<OpenAPIV2.Document>;
|
|
39
|
+
} | {
|
|
40
|
+
openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>;
|
|
41
|
+
}) => any;
|
|
42
|
+
export declare const validatorCompiler: FastifySchemaCompiler<z.ZodTypeAny>;
|
|
43
|
+
type ReplacerFunction = (this: any, key: string, value: any) => any;
|
|
44
|
+
type ZodSerializerCompilerOptions = {
|
|
45
|
+
replacer?: ReplacerFunction;
|
|
46
|
+
};
|
|
47
|
+
export declare const createSerializerCompiler: (options?: ZodSerializerCompilerOptions) => FastifySerializerCompiler<z.ZodTypeAny | {
|
|
48
|
+
properties: z.ZodTypeAny;
|
|
49
|
+
}>;
|
|
50
|
+
export declare const serializerCompiler: FastifySerializerCompiler<z.ZodTypeAny | {
|
|
51
|
+
properties: z.ZodTypeAny;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* FastifyPluginCallbackZod with Zod automatic type inference
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import { FastifyPluginCallbackZod } from "fastify-type-provider-zod"
|
|
59
|
+
*
|
|
60
|
+
* const plugin: FastifyPluginCallbackZod = (fastify, options, done) => {
|
|
61
|
+
* done()
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export type FastifyPluginCallbackZod<Options extends FastifyPluginOptions = Record<never, never>, Server extends RawServerBase = RawServerDefault> = FastifyPluginCallback<Options, Server, ZodTypeProvider>;
|
|
66
|
+
/**
|
|
67
|
+
* FastifyPluginAsyncZod with Zod automatic type inference
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import { FastifyPluginAsyncZod } from "fastify-type-provider-zod"
|
|
72
|
+
*
|
|
73
|
+
* const plugin: FastifyPluginAsyncZod = async (fastify, options) => {
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export type FastifyPluginAsyncZod<Options extends FastifyPluginOptions = Record<never, never>, Server extends RawServerBase = RawServerDefault> = FastifyPluginAsync<Options, Server, ZodTypeProvider>;
|
|
78
|
+
export {};
|
package/dist/src/core.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serializerCompiler = exports.createSerializerCompiler = exports.validatorCompiler = exports.createJsonSchemaTransformObject = exports.jsonSchemaTransform = exports.createJsonSchemaTransform = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
const ref_1 = require("./ref");
|
|
6
|
+
const zod_to_json_1 = require("./zod-to-json");
|
|
7
|
+
const defaultSkipList = [
|
|
8
|
+
'/documentation/',
|
|
9
|
+
'/documentation/initOAuth',
|
|
10
|
+
'/documentation/json',
|
|
11
|
+
'/documentation/uiConfig',
|
|
12
|
+
'/documentation/yaml',
|
|
13
|
+
'/documentation/*',
|
|
14
|
+
'/documentation/static/*',
|
|
15
|
+
];
|
|
16
|
+
const createJsonSchemaTransform = ({ skipList }) => {
|
|
17
|
+
return ({ schema, url }) => {
|
|
18
|
+
if (!schema) {
|
|
19
|
+
return {
|
|
20
|
+
schema,
|
|
21
|
+
url,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const { response, headers, querystring, body, params, hide, ...rest } = schema;
|
|
25
|
+
const transformed = {};
|
|
26
|
+
if (skipList.includes(url) || hide) {
|
|
27
|
+
transformed.hide = true;
|
|
28
|
+
return { schema: transformed, url };
|
|
29
|
+
}
|
|
30
|
+
const zodSchemas = { headers, querystring, body, params };
|
|
31
|
+
for (const prop in zodSchemas) {
|
|
32
|
+
const zodSchema = zodSchemas[prop];
|
|
33
|
+
if (zodSchema) {
|
|
34
|
+
transformed[prop] = (0, zod_to_json_1.convertZodToJsonSchema)(zodSchema);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (response) {
|
|
38
|
+
transformed.response = {};
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
for (const prop in response) {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
const schema = resolveSchema(response[prop]);
|
|
43
|
+
const transformedResponse = (0, zod_to_json_1.convertZodToJsonSchema)(schema);
|
|
44
|
+
transformed.response[prop] = transformedResponse;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const prop in rest) {
|
|
48
|
+
const meta = rest[prop];
|
|
49
|
+
if (meta) {
|
|
50
|
+
transformed[prop] = meta;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { schema: transformed, url };
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
exports.createJsonSchemaTransform = createJsonSchemaTransform;
|
|
57
|
+
exports.jsonSchemaTransform = (0, exports.createJsonSchemaTransform)({
|
|
58
|
+
skipList: defaultSkipList,
|
|
59
|
+
});
|
|
60
|
+
const createJsonSchemaTransformObject = ({ schemas }) => (input) => {
|
|
61
|
+
if ('swaggerObject' in input) {
|
|
62
|
+
console.warn('This package currently does not support component references for Swagger 2.0');
|
|
63
|
+
return input.swaggerObject;
|
|
64
|
+
}
|
|
65
|
+
return (0, ref_1.resolveRefs)(input.openapiObject, schemas);
|
|
66
|
+
};
|
|
67
|
+
exports.createJsonSchemaTransformObject = createJsonSchemaTransformObject;
|
|
68
|
+
const validatorCompiler = ({ schema, method, url }) => (data) => {
|
|
69
|
+
const result = schema.safeParse(data);
|
|
70
|
+
if (result.error) {
|
|
71
|
+
return { error: (0, errors_1.createValidationError)(result.error, method, url) };
|
|
72
|
+
}
|
|
73
|
+
return { value: result.data };
|
|
74
|
+
};
|
|
75
|
+
exports.validatorCompiler = validatorCompiler;
|
|
76
|
+
function resolveSchema(maybeSchema) {
|
|
77
|
+
if ('safeParse' in maybeSchema) {
|
|
78
|
+
return maybeSchema;
|
|
79
|
+
}
|
|
80
|
+
if ('properties' in maybeSchema) {
|
|
81
|
+
return maybeSchema.properties;
|
|
82
|
+
}
|
|
83
|
+
throw new errors_1.InvalidSchemaError(JSON.stringify(maybeSchema));
|
|
84
|
+
}
|
|
85
|
+
const createSerializerCompiler = (options) => ({ schema: maybeSchema, method, url }) => (data) => {
|
|
86
|
+
const schema = resolveSchema(maybeSchema);
|
|
87
|
+
const result = schema.safeParse(data);
|
|
88
|
+
if (result.error) {
|
|
89
|
+
throw new errors_1.ResponseSerializationError(result.error, method, url);
|
|
90
|
+
}
|
|
91
|
+
return JSON.stringify(result.data, options?.replacer);
|
|
92
|
+
};
|
|
93
|
+
exports.createSerializerCompiler = createSerializerCompiler;
|
|
94
|
+
exports.serializerCompiler = (0, exports.createSerializerCompiler)({});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FastifySchemaValidationError } from 'fastify/types/schema';
|
|
2
|
+
import type { ZodError } from 'zod';
|
|
3
|
+
declare const ResponseSerializationError_base: import("@fastify/error").FastifyErrorConstructor<{
|
|
4
|
+
code: string;
|
|
5
|
+
}, [{
|
|
6
|
+
cause: ZodError;
|
|
7
|
+
}]>;
|
|
8
|
+
export declare class ResponseSerializationError extends ResponseSerializationError_base {
|
|
9
|
+
cause: ZodError;
|
|
10
|
+
method: string;
|
|
11
|
+
url: string;
|
|
12
|
+
constructor(cause: ZodError, method: string, url: string);
|
|
13
|
+
}
|
|
14
|
+
export declare const InvalidSchemaError: import("@fastify/error").FastifyErrorConstructor<{
|
|
15
|
+
code: string;
|
|
16
|
+
}, [string]>;
|
|
17
|
+
export declare const createValidationError: (error: ZodError, method: string, url: string) => FastifySchemaValidationError[];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createValidationError = exports.InvalidSchemaError = exports.ResponseSerializationError = void 0;
|
|
7
|
+
const error_1 = __importDefault(require("@fastify/error"));
|
|
8
|
+
class ResponseSerializationError extends (0, error_1.default)('FST_ERR_RESPONSE_SERIALIZATION', "Response doesn't match the schema", 500) {
|
|
9
|
+
constructor(cause, method, url) {
|
|
10
|
+
super({ cause });
|
|
11
|
+
this.cause = cause;
|
|
12
|
+
this.method = method;
|
|
13
|
+
this.url = url;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.ResponseSerializationError = ResponseSerializationError;
|
|
17
|
+
exports.InvalidSchemaError = (0, error_1.default)('FST_ERR_INVALID_SCHEMA', 'Invalid schema passed: %s', 500);
|
|
18
|
+
const createValidationError = (error, method, url) => error.errors.map((issue) => ({
|
|
19
|
+
keyword: issue.code,
|
|
20
|
+
instancePath: `/${issue.path.join('/')}`,
|
|
21
|
+
schemaPath: `#/${issue.path.join('/')}/${issue.code}`,
|
|
22
|
+
params: {
|
|
23
|
+
issue,
|
|
24
|
+
zodError: error,
|
|
25
|
+
method,
|
|
26
|
+
url,
|
|
27
|
+
},
|
|
28
|
+
message: error.message,
|
|
29
|
+
}));
|
|
30
|
+
exports.createValidationError = createValidationError;
|
package/dist/src/ref.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveRefs = void 0;
|
|
4
|
+
const zod_to_json_1 = require("./zod-to-json");
|
|
5
|
+
const createComponentMap = (schemas) => {
|
|
6
|
+
const map = new Map();
|
|
7
|
+
Object.entries(schemas).forEach(([key, value]) => map.set(JSON.stringify(value), key));
|
|
8
|
+
return map;
|
|
9
|
+
};
|
|
10
|
+
const createComponentReplacer = (componentMapVK, schemasObject) =>
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
function componentReplacer(key, value) {
|
|
13
|
+
if (typeof value !== 'object')
|
|
14
|
+
return value;
|
|
15
|
+
// Check if the parent is the schemas object, if so, return the value as is. This is where the schemas are defined.
|
|
16
|
+
if (this === schemasObject)
|
|
17
|
+
return value;
|
|
18
|
+
const stringifiedValue = JSON.stringify(value);
|
|
19
|
+
if (componentMapVK.has(stringifiedValue))
|
|
20
|
+
return { $ref: `#/components/schemas/${componentMapVK.get(stringifiedValue)}` };
|
|
21
|
+
if (value.nullable === true) {
|
|
22
|
+
const nonNullableValue = { ...value };
|
|
23
|
+
delete nonNullableValue.nullable;
|
|
24
|
+
const stringifiedNonNullableValue = JSON.stringify(nonNullableValue);
|
|
25
|
+
if (componentMapVK.has(stringifiedNonNullableValue))
|
|
26
|
+
return {
|
|
27
|
+
anyOf: [
|
|
28
|
+
{ $ref: `#/components/schemas/${componentMapVK.get(stringifiedNonNullableValue)}` },
|
|
29
|
+
],
|
|
30
|
+
nullable: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
const resolveRefs = (openapiObject, zodSchemas) => {
|
|
36
|
+
const schemas = {};
|
|
37
|
+
for (const key in zodSchemas) {
|
|
38
|
+
schemas[key] = (0, zod_to_json_1.convertZodToJsonSchema)(zodSchemas[key]);
|
|
39
|
+
}
|
|
40
|
+
const document = {
|
|
41
|
+
...openapiObject,
|
|
42
|
+
components: {
|
|
43
|
+
...openapiObject.components,
|
|
44
|
+
schemas: {
|
|
45
|
+
...openapiObject.components?.schemas,
|
|
46
|
+
...schemas,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
const componentMapVK = createComponentMap(schemas);
|
|
51
|
+
const componentReplacer = createComponentReplacer(componentMapVK, document.components.schemas);
|
|
52
|
+
// Using the componentReplacer function we deep check if the document has any schemas that are the same as the zod schemas provided
|
|
53
|
+
// When a match is found replace them with a $ref.
|
|
54
|
+
return JSON.parse(JSON.stringify(document, componentReplacer));
|
|
55
|
+
};
|
|
56
|
+
exports.resolveRefs = resolveRefs;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertZodToJsonSchema = void 0;
|
|
4
|
+
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
5
|
+
const zodToJsonSchemaOptions = {
|
|
6
|
+
target: 'openApi3',
|
|
7
|
+
$refStrategy: 'none',
|
|
8
|
+
};
|
|
9
|
+
const convertZodToJsonSchema = (zodSchema) => {
|
|
10
|
+
return (0, zod_to_json_schema_1.zodToJsonSchema)(zodSchema, zodToJsonSchemaOptions);
|
|
11
|
+
};
|
|
12
|
+
exports.convertZodToJsonSchema = convertZodToJsonSchema;
|