@bram-dc/fastify-type-provider-zod 5.0.0 → 5.0.3
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 +53 -46
- package/dist/cjs/core.cjs +137 -0
- package/dist/cjs/core.cjs.map +1 -0
- package/dist/cjs/core.d.cts +68 -0
- package/dist/cjs/errors.cjs +57 -0
- package/dist/cjs/errors.cjs.map +1 -0
- package/dist/cjs/errors.d.cts +30 -0
- package/dist/cjs/index.cjs +16 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/json-to-oas.cjs +85 -0
- package/dist/cjs/json-to-oas.cjs.map +1 -0
- package/dist/cjs/json-to-oas.d.cts +9 -0
- package/dist/cjs/zod-to-json.cjs +98 -0
- package/dist/cjs/zod-to-json.cjs.map +1 -0
- package/dist/cjs/zod-to-json.d.cts +8 -0
- package/dist/esm/core.d.ts +68 -0
- package/dist/esm/core.js +137 -0
- package/dist/esm/core.js.map +1 -0
- package/dist/esm/errors.d.ts +30 -0
- package/dist/esm/errors.js +57 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/json-to-oas.d.ts +9 -0
- package/dist/esm/json-to-oas.js +85 -0
- package/dist/esm/json-to-oas.js.map +1 -0
- package/dist/esm/zod-to-json.d.ts +8 -0
- package/dist/esm/zod-to-json.js +98 -0
- package/dist/esm/zod-to-json.js.map +1 -0
- package/package.json +73 -58
- package/src/core.ts +244 -0
- package/src/errors.ts +99 -0
- package/src/index.ts +21 -0
- package/src/json-to-oas.ts +109 -0
- package/src/zod-to-json.ts +150 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -16
- package/dist/src/core.d.ts +0 -58
- package/dist/src/core.js +0 -114
- package/dist/src/errors.d.ts +0 -35
- package/dist/src/errors.js +0 -43
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const core = require("zod/v4/core");
|
|
4
|
+
const getSchemaId = (id, io) => {
|
|
5
|
+
return io === "input" ? `${id}Input` : id;
|
|
6
|
+
};
|
|
7
|
+
const getReferenceUri = (id, io) => {
|
|
8
|
+
return `#/components/schemas/${getSchemaId(id, io)}`;
|
|
9
|
+
};
|
|
10
|
+
function isZodDate(entity) {
|
|
11
|
+
return entity instanceof core.$ZodType && entity._zod.def.type === "date";
|
|
12
|
+
}
|
|
13
|
+
function isZodUnion(entity) {
|
|
14
|
+
return entity instanceof core.$ZodType && entity._zod.def.type === "union";
|
|
15
|
+
}
|
|
16
|
+
function isZodUndefined(entity) {
|
|
17
|
+
return entity instanceof core.$ZodType && entity._zod.def.type === "undefined";
|
|
18
|
+
}
|
|
19
|
+
const getOverride = (ctx, io) => {
|
|
20
|
+
if (isZodUnion(ctx.zodSchema)) {
|
|
21
|
+
ctx.jsonSchema.anyOf = ctx.jsonSchema.anyOf?.filter((schema) => Object.keys(schema).length > 0);
|
|
22
|
+
}
|
|
23
|
+
if (isZodDate(ctx.zodSchema)) {
|
|
24
|
+
if (io === "output") {
|
|
25
|
+
ctx.jsonSchema.type = "string";
|
|
26
|
+
ctx.jsonSchema.format = "date-time";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (isZodUndefined(ctx.zodSchema)) {
|
|
30
|
+
if (io === "output") {
|
|
31
|
+
ctx.jsonSchema.type = "null";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const zodSchemaToJson = (zodSchema, registry, io) => {
|
|
36
|
+
const schemaRegistryEntry = registry.get(zodSchema);
|
|
37
|
+
if (schemaRegistryEntry?.id) {
|
|
38
|
+
return {
|
|
39
|
+
$ref: getReferenceUri(schemaRegistryEntry.id, io)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const tempID = "GEN";
|
|
43
|
+
const tempRegistry = new core.$ZodRegistry();
|
|
44
|
+
tempRegistry.add(zodSchema, { id: tempID });
|
|
45
|
+
const {
|
|
46
|
+
schemas: { [tempID]: result }
|
|
47
|
+
} = core.toJSONSchema(tempRegistry, {
|
|
48
|
+
target: "draft-2020-12",
|
|
49
|
+
metadata: registry,
|
|
50
|
+
io,
|
|
51
|
+
unrepresentable: "any",
|
|
52
|
+
cycles: "ref",
|
|
53
|
+
reused: "inline",
|
|
54
|
+
/**
|
|
55
|
+
* The uri option only allows customizing the base path of the `$ref`, and it automatically appends a path to it.
|
|
56
|
+
* As a workaround, we set a placeholder that looks something like this:
|
|
57
|
+
*
|
|
58
|
+
* | marker | always added by zod | meta.id |
|
|
59
|
+
* |__SCHEMA__PLACEHOLDER__| #/$defs/ | User |
|
|
60
|
+
*
|
|
61
|
+
* @example `__SCHEMA__PLACEHOLDER__#/$defs/User"`
|
|
62
|
+
* @example `__SCHEMA__PLACEHOLDER__#/$defs/Group"`
|
|
63
|
+
*
|
|
64
|
+
* @see jsonSchemaReplaceRef
|
|
65
|
+
* @see https://github.com/colinhacks/zod/issues/4750
|
|
66
|
+
*/
|
|
67
|
+
uri: () => `__SCHEMA__PLACEHOLDER__`,
|
|
68
|
+
override: (ctx) => getOverride(ctx, io)
|
|
69
|
+
});
|
|
70
|
+
const jsonSchema = { ...result };
|
|
71
|
+
delete jsonSchema.id;
|
|
72
|
+
const jsonSchemaReplaceRef = JSON.stringify(jsonSchema).replaceAll(
|
|
73
|
+
/"__SCHEMA__PLACEHOLDER__#\/\$defs\/(.+?)"/g,
|
|
74
|
+
(_, id) => `"${getReferenceUri(id, io)}"`
|
|
75
|
+
);
|
|
76
|
+
return JSON.parse(jsonSchemaReplaceRef);
|
|
77
|
+
};
|
|
78
|
+
const zodRegistryToJson = (registry, io) => {
|
|
79
|
+
const result = core.toJSONSchema(registry, {
|
|
80
|
+
target: "draft-2020-12",
|
|
81
|
+
io,
|
|
82
|
+
unrepresentable: "any",
|
|
83
|
+
cycles: "ref",
|
|
84
|
+
reused: "inline",
|
|
85
|
+
uri: (id) => getReferenceUri(id, io),
|
|
86
|
+
override: (ctx) => getOverride(ctx, io)
|
|
87
|
+
}).schemas;
|
|
88
|
+
const jsonSchemas = {};
|
|
89
|
+
for (const id in result) {
|
|
90
|
+
const jsonSchema = { ...result[id] };
|
|
91
|
+
delete jsonSchema.id;
|
|
92
|
+
jsonSchemas[getSchemaId(id, io)] = jsonSchema;
|
|
93
|
+
}
|
|
94
|
+
return jsonSchemas;
|
|
95
|
+
};
|
|
96
|
+
exports.zodRegistryToJson = zodRegistryToJson;
|
|
97
|
+
exports.zodSchemaToJson = zodSchemaToJson;
|
|
98
|
+
//# sourceMappingURL=zod-to-json.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-to-json.cjs","sources":["../../src/zod-to-json.ts"],"sourcesContent":["import type { $ZodDate, $ZodUndefined, $ZodUnion, JSONSchema } from 'zod/v4/core'\nimport { $ZodRegistry, $ZodType, toJSONSchema } from 'zod/v4/core'\n\nconst getSchemaId = (id: string, io: 'input' | 'output') => {\n return io === 'input' ? `${id}Input` : id\n}\n\nconst getReferenceUri = (id: string, io: 'input' | 'output') => {\n return `#/components/schemas/${getSchemaId(id, io)}`\n}\n\nfunction isZodDate(entity: unknown): entity is $ZodDate {\n return entity instanceof $ZodType && entity._zod.def.type === 'date'\n}\n\nfunction isZodUnion(entity: unknown): entity is $ZodUnion {\n return entity instanceof $ZodType && entity._zod.def.type === 'union'\n}\n\nfunction isZodUndefined(entity: unknown): entity is $ZodUndefined {\n return entity instanceof $ZodType && entity._zod.def.type === 'undefined'\n}\n\nconst getOverride = (\n ctx: {\n zodSchema: $ZodType\n jsonSchema: JSONSchema.BaseSchema\n },\n io: 'input' | 'output',\n) => {\n if (isZodUnion(ctx.zodSchema)) {\n // Filter unrepresentable types in unions\n // TODO: Should be fixed upstream and not merged in this plugin.\n // Remove when passed: https://github.com/colinhacks/zod/pull/5013\n ctx.jsonSchema.anyOf = ctx.jsonSchema.anyOf?.filter((schema) => Object.keys(schema).length > 0)\n }\n\n if (isZodDate(ctx.zodSchema)) {\n // Allow dates to be represented as strings in output schemas\n if (io === 'output') {\n ctx.jsonSchema.type = 'string'\n ctx.jsonSchema.format = 'date-time'\n }\n }\n\n if (isZodUndefined(ctx.zodSchema)) {\n // Allow undefined to be represented as null in output schemas\n if (io === 'output') {\n ctx.jsonSchema.type = 'null'\n }\n }\n}\n\nexport const zodSchemaToJson: (\n zodSchema: $ZodType,\n registry: $ZodRegistry<{ id?: string }>,\n io: 'input' | 'output',\n) => JSONSchema.BaseSchema = (zodSchema, registry, io) => {\n const schemaRegistryEntry = registry.get(zodSchema)\n\n /**\n * Checks whether the provided schema is registered in the given registry.\n * If it is present and has an `id`, it can be referenced as component.\n *\n * @see https://github.com/turkerdev/fastify-type-provider-zod/issues/173\n */\n if (schemaRegistryEntry?.id) {\n return {\n $ref: getReferenceUri(schemaRegistryEntry.id, io),\n }\n }\n\n /**\n * Unfortunately, at the time of writing, there is no way to generate a schema with `$ref`\n * using `toJSONSchema` and a zod schema.\n *\n * As a workaround, we create a zod registry containing only the specific schema we want to convert.\n *\n * @see https://github.com/colinhacks/zod/issues/4281\n */\n const tempID = 'GEN'\n const tempRegistry = new $ZodRegistry<{ id?: string }>()\n tempRegistry.add(zodSchema, { id: tempID })\n\n const {\n schemas: { [tempID]: result },\n } = toJSONSchema(tempRegistry, {\n target: 'draft-2020-12',\n metadata: registry,\n io,\n unrepresentable: 'any',\n cycles: 'ref',\n reused: 'inline',\n\n /**\n * The uri option only allows customizing the base path of the `$ref`, and it automatically appends a path to it.\n * As a workaround, we set a placeholder that looks something like this:\n *\n * | marker | always added by zod | meta.id |\n * |__SCHEMA__PLACEHOLDER__| #/$defs/ | User |\n *\n * @example `__SCHEMA__PLACEHOLDER__#/$defs/User\"`\n * @example `__SCHEMA__PLACEHOLDER__#/$defs/Group\"`\n *\n * @see jsonSchemaReplaceRef\n * @see https://github.com/colinhacks/zod/issues/4750\n */\n uri: () => `__SCHEMA__PLACEHOLDER__`,\n override: (ctx) => getOverride(ctx, io),\n })\n\n const jsonSchema = { ...result }\n delete jsonSchema.id\n\n /**\n * Replace the previous generated placeholders with the final `$ref` value\n */\n const jsonSchemaReplaceRef = JSON.stringify(jsonSchema).replaceAll(\n /\"__SCHEMA__PLACEHOLDER__#\\/\\$defs\\/(.+?)\"/g,\n (_, id) => `\"${getReferenceUri(id, io)}\"`,\n )\n\n return JSON.parse(jsonSchemaReplaceRef) as typeof result\n}\n\nexport const zodRegistryToJson: (\n registry: $ZodRegistry<{ id?: string }>,\n io: 'input' | 'output',\n) => Record<string, JSONSchema.BaseSchema> = (registry, io) => {\n const result = toJSONSchema(registry, {\n target: 'draft-2020-12',\n io,\n unrepresentable: 'any',\n cycles: 'ref',\n reused: 'inline',\n uri: (id) => getReferenceUri(id, io),\n override: (ctx) => getOverride(ctx, io),\n }).schemas\n\n const jsonSchemas: Record<string, JSONSchema.BaseSchema> = {}\n for (const id in result) {\n const jsonSchema = { ...result[id] }\n\n delete jsonSchema.id\n\n jsonSchemas[getSchemaId(id, io)] = jsonSchema\n }\n\n return jsonSchemas\n}\n"],"names":["$ZodType","$ZodRegistry","toJSONSchema"],"mappings":";;;AAGA,MAAM,cAAc,CAAC,IAAY,OAA2B;AAC1D,SAAO,OAAO,UAAU,GAAG,EAAE,UAAU;AACzC;AAEA,MAAM,kBAAkB,CAAC,IAAY,OAA2B;AAC9D,SAAO,wBAAwB,YAAY,IAAI,EAAE,CAAC;AACpD;AAEA,SAAS,UAAU,QAAqC;AACtD,SAAO,kBAAkBA,KAAAA,YAAY,OAAO,KAAK,IAAI,SAAS;AAChE;AAEA,SAAS,WAAW,QAAsC;AACxD,SAAO,kBAAkBA,KAAAA,YAAY,OAAO,KAAK,IAAI,SAAS;AAChE;AAEA,SAAS,eAAe,QAA0C;AAChE,SAAO,kBAAkBA,KAAAA,YAAY,OAAO,KAAK,IAAI,SAAS;AAChE;AAEA,MAAM,cAAc,CAClB,KAIA,OACG;AACH,MAAI,WAAW,IAAI,SAAS,GAAG;AAI7B,QAAI,WAAW,QAAQ,IAAI,WAAW,OAAO,OAAO,CAAC,WAAW,OAAO,KAAK,MAAM,EAAE,SAAS,CAAC;AAAA,EAChG;AAEA,MAAI,UAAU,IAAI,SAAS,GAAG;AAE5B,QAAI,OAAO,UAAU;AACnB,UAAI,WAAW,OAAO;AACtB,UAAI,WAAW,SAAS;AAAA,IAC1B;AAAA,EACF;AAEA,MAAI,eAAe,IAAI,SAAS,GAAG;AAEjC,QAAI,OAAO,UAAU;AACnB,UAAI,WAAW,OAAO;AAAA,IACxB;AAAA,EACF;AACF;AAEO,MAAM,kBAIgB,CAAC,WAAW,UAAU,OAAO;AACxD,QAAM,sBAAsB,SAAS,IAAI,SAAS;AAQlD,MAAI,qBAAqB,IAAI;AAC3B,WAAO;AAAA,MACL,MAAM,gBAAgB,oBAAoB,IAAI,EAAE;AAAA,IAAA;AAAA,EAEpD;AAUA,QAAM,SAAS;AACf,QAAM,eAAe,IAAIC,kBAAA;AACzB,eAAa,IAAI,WAAW,EAAE,IAAI,QAAQ;AAE1C,QAAM;AAAA,IACJ,SAAS,EAAE,CAAC,MAAM,GAAG,OAAA;AAAA,EAAO,IAC1BC,KAAAA,aAAa,cAAc;AAAA,IAC7B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV;AAAA,IACA,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeR,KAAK,MAAM;AAAA,IACX,UAAU,CAAC,QAAQ,YAAY,KAAK,EAAE;AAAA,EAAA,CACvC;AAED,QAAM,aAAa,EAAE,GAAG,OAAA;AACxB,SAAO,WAAW;AAKlB,QAAM,uBAAuB,KAAK,UAAU,UAAU,EAAE;AAAA,IACtD;AAAA,IACA,CAAC,GAAG,OAAO,IAAI,gBAAgB,IAAI,EAAE,CAAC;AAAA,EAAA;AAGxC,SAAO,KAAK,MAAM,oBAAoB;AACxC;AAEO,MAAM,oBAGgC,CAAC,UAAU,OAAO;AAC7D,QAAM,SAASA,KAAAA,aAAa,UAAU;AAAA,IACpC,QAAQ;AAAA,IACR;AAAA,IACA,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK,CAAC,OAAO,gBAAgB,IAAI,EAAE;AAAA,IACnC,UAAU,CAAC,QAAQ,YAAY,KAAK,EAAE;AAAA,EAAA,CACvC,EAAE;AAEH,QAAM,cAAqD,CAAA;AAC3D,aAAW,MAAM,QAAQ;AACvB,UAAM,aAAa,EAAE,GAAG,OAAO,EAAE,EAAA;AAEjC,WAAO,WAAW;AAElB,gBAAY,YAAY,IAAI,EAAE,CAAC,IAAI;AAAA,EACrC;AAEA,SAAO;AACT;;;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSONSchema } from "zod/v4/core";
|
|
2
|
+
import { $ZodRegistry, $ZodType } from "zod/v4/core";
|
|
3
|
+
export declare const zodSchemaToJson: (zodSchema: $ZodType, registry: $ZodRegistry<{
|
|
4
|
+
id?: string;
|
|
5
|
+
}>, io: "input" | "output") => JSONSchema.BaseSchema;
|
|
6
|
+
export declare const zodRegistryToJson: (registry: $ZodRegistry<{
|
|
7
|
+
id?: string;
|
|
8
|
+
}>, io: "input" | "output") => Record<string, JSONSchema.BaseSchema>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { SwaggerTransform, SwaggerTransformObject } from "@fastify/swagger";
|
|
2
|
+
import type { FastifyPluginAsync, FastifyPluginCallback, FastifyPluginOptions, FastifySchema, FastifySchemaCompiler, FastifyTypeProvider, RawServerBase, RawServerDefault } from "fastify";
|
|
3
|
+
import type { FastifySerializerCompiler } from "fastify/types/schema";
|
|
4
|
+
import type { $ZodRegistry, input, output } from "zod/v4/core";
|
|
5
|
+
import { $ZodType } from "zod/v4/core";
|
|
6
|
+
export interface ZodTypeProvider extends FastifyTypeProvider {
|
|
7
|
+
validator: this["schema"] extends $ZodType ? output<this["schema"]> : unknown;
|
|
8
|
+
serializer: this["schema"] extends $ZodType ? input<this["schema"]> : unknown;
|
|
9
|
+
}
|
|
10
|
+
interface Schema extends FastifySchema {
|
|
11
|
+
hide?: boolean;
|
|
12
|
+
}
|
|
13
|
+
type CreateJsonSchemaTransformOptions = {
|
|
14
|
+
skipList?: readonly string[];
|
|
15
|
+
schemaRegistry?: $ZodRegistry<{
|
|
16
|
+
id?: string | undefined;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
export declare const createJsonSchemaTransform: ({ skipList, schemaRegistry }: CreateJsonSchemaTransformOptions) => SwaggerTransform<Schema>;
|
|
20
|
+
export declare const jsonSchemaTransform: SwaggerTransform<Schema>;
|
|
21
|
+
type CreateJsonSchemaTransformObjectOptions = {
|
|
22
|
+
schemaRegistry?: $ZodRegistry<{
|
|
23
|
+
id?: string | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
export declare const createJsonSchemaTransformObject: ({ schemaRegistry }: CreateJsonSchemaTransformObjectOptions) => SwaggerTransformObject;
|
|
27
|
+
export declare const jsonSchemaTransformObject: SwaggerTransformObject;
|
|
28
|
+
export declare const validatorCompiler: FastifySchemaCompiler<$ZodType>;
|
|
29
|
+
type ReplacerFunction = (this: any, key: string, value: any) => any;
|
|
30
|
+
export type ZodSerializerCompilerOptions = {
|
|
31
|
+
replacer?: ReplacerFunction;
|
|
32
|
+
};
|
|
33
|
+
export declare const createSerializerCompiler: (options?: ZodSerializerCompilerOptions) => FastifySerializerCompiler<$ZodType | {
|
|
34
|
+
properties: $ZodType;
|
|
35
|
+
}>;
|
|
36
|
+
export declare const serializerCompiler: ReturnType<typeof createSerializerCompiler>;
|
|
37
|
+
/**
|
|
38
|
+
* FastifyPluginCallbackZod with Zod automatic type inference
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { FastifyPluginCallbackZod } from "fastify-type-provider-zod"
|
|
43
|
+
*
|
|
44
|
+
* const plugin: FastifyPluginCallbackZod = (fastify, options, done) => {
|
|
45
|
+
* done()
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export type FastifyPluginCallbackZod<
|
|
50
|
+
Options extends FastifyPluginOptions = Record<never, never>,
|
|
51
|
+
Server extends RawServerBase = RawServerDefault
|
|
52
|
+
> = FastifyPluginCallback<Options, Server, ZodTypeProvider>;
|
|
53
|
+
/**
|
|
54
|
+
* FastifyPluginAsyncZod with Zod automatic type inference
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import { FastifyPluginAsyncZod } from "fastify-type-provider-zod"
|
|
59
|
+
*
|
|
60
|
+
* const plugin: FastifyPluginAsyncZod = async (fastify, options) => {
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export type FastifyPluginAsyncZod<
|
|
65
|
+
Options extends FastifyPluginOptions = Record<never, never>,
|
|
66
|
+
Server extends RawServerBase = RawServerDefault
|
|
67
|
+
> = FastifyPluginAsync<Options, Server, ZodTypeProvider>;
|
|
68
|
+
export {};
|
package/dist/esm/core.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { globalRegistry, safeParse, $ZodType } from "zod/v4/core";
|
|
2
|
+
import { ResponseSerializationError, createValidationError, InvalidSchemaError } from "./errors.js";
|
|
3
|
+
import { getOASVersion, jsonSchemaToOAS } from "./json-to-oas.js";
|
|
4
|
+
import { zodSchemaToJson, zodRegistryToJson } from "./zod-to-json.js";
|
|
5
|
+
const defaultSkipList = [
|
|
6
|
+
"/documentation/",
|
|
7
|
+
"/documentation/initOAuth",
|
|
8
|
+
"/documentation/json",
|
|
9
|
+
"/documentation/uiConfig",
|
|
10
|
+
"/documentation/yaml",
|
|
11
|
+
"/documentation/*",
|
|
12
|
+
"/documentation/static/*"
|
|
13
|
+
];
|
|
14
|
+
const createJsonSchemaTransform = ({
|
|
15
|
+
skipList = defaultSkipList,
|
|
16
|
+
schemaRegistry = globalRegistry
|
|
17
|
+
}) => {
|
|
18
|
+
return (input) => {
|
|
19
|
+
if ("swaggerObject" in input) {
|
|
20
|
+
throw new Error("OpenAPI 2.0 is not supported");
|
|
21
|
+
}
|
|
22
|
+
const { schema, url } = input;
|
|
23
|
+
if (!schema) {
|
|
24
|
+
return {
|
|
25
|
+
schema,
|
|
26
|
+
url
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const { response, headers, querystring, body, params, hide, ...rest } = schema;
|
|
30
|
+
const transformed = {};
|
|
31
|
+
if (skipList.includes(url) || hide) {
|
|
32
|
+
transformed.hide = true;
|
|
33
|
+
return { schema: transformed, url };
|
|
34
|
+
}
|
|
35
|
+
const zodSchemas = { headers, querystring, body, params };
|
|
36
|
+
const oasVersion = getOASVersion(input);
|
|
37
|
+
for (const prop in zodSchemas) {
|
|
38
|
+
const zodSchema = zodSchemas[prop];
|
|
39
|
+
if (zodSchema) {
|
|
40
|
+
const jsonSchema = zodSchemaToJson(zodSchema, schemaRegistry, "input");
|
|
41
|
+
const oasSchema = jsonSchemaToOAS(jsonSchema, oasVersion);
|
|
42
|
+
transformed[prop] = oasSchema;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (response) {
|
|
46
|
+
transformed.response = {};
|
|
47
|
+
for (const prop in response) {
|
|
48
|
+
const zodSchema = resolveSchema(response[prop]);
|
|
49
|
+
const jsonSchema = zodSchemaToJson(zodSchema, schemaRegistry, "output");
|
|
50
|
+
if (jsonSchema.type === "null") {
|
|
51
|
+
transformed.response[prop] = jsonSchema;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const oasSchema = jsonSchemaToOAS(jsonSchema, oasVersion);
|
|
55
|
+
transformed.response[prop] = oasSchema;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
for (const prop in rest) {
|
|
59
|
+
const meta = rest[prop];
|
|
60
|
+
if (meta) {
|
|
61
|
+
transformed[prop] = meta;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { schema: transformed, url };
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const jsonSchemaTransform = createJsonSchemaTransform({});
|
|
68
|
+
const createJsonSchemaTransformObject = ({
|
|
69
|
+
schemaRegistry = globalRegistry
|
|
70
|
+
}) => (input) => {
|
|
71
|
+
if ("swaggerObject" in input) {
|
|
72
|
+
throw new Error("OpenAPI 2.0 is not supported");
|
|
73
|
+
}
|
|
74
|
+
const oasVersion = getOASVersion(input);
|
|
75
|
+
const inputSchemas = zodRegistryToJson(schemaRegistry, "input");
|
|
76
|
+
const outputSchemas = zodRegistryToJson(schemaRegistry, "output");
|
|
77
|
+
for (const key in outputSchemas) {
|
|
78
|
+
if (inputSchemas[key]) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Collision detected for schema "${key}". The is already an input schema with the same name.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const jsonSchemas = {
|
|
85
|
+
...inputSchemas,
|
|
86
|
+
...outputSchemas
|
|
87
|
+
};
|
|
88
|
+
const oasSchemas = Object.fromEntries(
|
|
89
|
+
Object.entries(jsonSchemas).map(([key, value]) => [key, jsonSchemaToOAS(value, oasVersion)])
|
|
90
|
+
);
|
|
91
|
+
return {
|
|
92
|
+
...input.openapiObject,
|
|
93
|
+
components: {
|
|
94
|
+
...input.openapiObject.components,
|
|
95
|
+
schemas: {
|
|
96
|
+
...input.openapiObject.components?.schemas,
|
|
97
|
+
...oasSchemas
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
const jsonSchemaTransformObject = createJsonSchemaTransformObject({});
|
|
103
|
+
const validatorCompiler = ({ schema }) => (data) => {
|
|
104
|
+
const result = safeParse(schema, data);
|
|
105
|
+
if (result.error) {
|
|
106
|
+
return { error: createValidationError(result.error) };
|
|
107
|
+
}
|
|
108
|
+
return { value: result.data };
|
|
109
|
+
};
|
|
110
|
+
function resolveSchema(maybeSchema) {
|
|
111
|
+
if (maybeSchema instanceof $ZodType) {
|
|
112
|
+
return maybeSchema;
|
|
113
|
+
}
|
|
114
|
+
if ("properties" in maybeSchema && maybeSchema.properties instanceof $ZodType) {
|
|
115
|
+
return maybeSchema.properties;
|
|
116
|
+
}
|
|
117
|
+
throw new InvalidSchemaError(JSON.stringify(maybeSchema));
|
|
118
|
+
}
|
|
119
|
+
const createSerializerCompiler = (options) => ({ schema: maybeSchema, method, url }) => (data) => {
|
|
120
|
+
const schema = resolveSchema(maybeSchema);
|
|
121
|
+
const result = safeParse(schema, data);
|
|
122
|
+
if (result.error) {
|
|
123
|
+
throw new ResponseSerializationError(method, url, { cause: result.error });
|
|
124
|
+
}
|
|
125
|
+
return JSON.stringify(result.data, options?.replacer);
|
|
126
|
+
};
|
|
127
|
+
const serializerCompiler = createSerializerCompiler({});
|
|
128
|
+
export {
|
|
129
|
+
createJsonSchemaTransform,
|
|
130
|
+
createJsonSchemaTransformObject,
|
|
131
|
+
createSerializerCompiler,
|
|
132
|
+
jsonSchemaTransform,
|
|
133
|
+
jsonSchemaTransformObject,
|
|
134
|
+
serializerCompiler,
|
|
135
|
+
validatorCompiler
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sources":["../../src/core.ts"],"sourcesContent":["import type { SwaggerTransform, SwaggerTransformObject } from '@fastify/swagger'\nimport type {\n FastifyPluginAsync,\n FastifyPluginCallback,\n FastifyPluginOptions,\n FastifySchema,\n FastifySchemaCompiler,\n FastifyTypeProvider,\n RawServerBase,\n RawServerDefault,\n} from 'fastify'\nimport type { FastifySerializerCompiler } from 'fastify/types/schema'\nimport type { $ZodRegistry, input, output } from 'zod/v4/core'\nimport { $ZodType, globalRegistry, safeParse } from 'zod/v4/core'\nimport { createValidationError, InvalidSchemaError, ResponseSerializationError } from './errors'\nimport { getOASVersion, jsonSchemaToOAS } from './json-to-oas'\nimport { zodRegistryToJson, zodSchemaToJson } from './zod-to-json'\n\ntype FreeformRecord = Record<string, any>\n\nconst defaultSkipList = [\n '/documentation/',\n '/documentation/initOAuth',\n '/documentation/json',\n '/documentation/uiConfig',\n '/documentation/yaml',\n '/documentation/*',\n '/documentation/static/*',\n]\n\nexport interface ZodTypeProvider extends FastifyTypeProvider {\n validator: this['schema'] extends $ZodType ? output<this['schema']> : unknown\n serializer: this['schema'] extends $ZodType ? input<this['schema']> : unknown\n}\n\ninterface Schema extends FastifySchema {\n hide?: boolean\n}\n\ntype CreateJsonSchemaTransformOptions = {\n skipList?: readonly string[]\n schemaRegistry?: $ZodRegistry<{ id?: string | undefined }>\n}\n\nexport const createJsonSchemaTransform = ({\n skipList = defaultSkipList,\n schemaRegistry = globalRegistry,\n}: CreateJsonSchemaTransformOptions): SwaggerTransform<Schema> => {\n return (input) => {\n if ('swaggerObject' in input) {\n throw new Error('OpenAPI 2.0 is not supported')\n }\n\n const { schema, url } = input\n\n if (!schema) {\n return {\n schema,\n url,\n }\n }\n\n const { response, headers, querystring, body, params, hide, ...rest } = schema\n\n const transformed: FreeformRecord = {}\n\n if (skipList.includes(url) || hide) {\n transformed.hide = true\n return { schema: transformed, url }\n }\n\n const zodSchemas: FreeformRecord = { headers, querystring, body, params }\n\n const oasVersion = getOASVersion(input)\n\n for (const prop in zodSchemas) {\n const zodSchema = zodSchemas[prop]\n if (zodSchema) {\n const jsonSchema = zodSchemaToJson(zodSchema, schemaRegistry, 'input')\n const oasSchema = jsonSchemaToOAS(jsonSchema, oasVersion)\n\n transformed[prop] = oasSchema\n }\n }\n\n if (response) {\n transformed.response = {}\n\n for (const prop in response as any) {\n const zodSchema = resolveSchema((response as any)[prop])\n const jsonSchema = zodSchemaToJson(zodSchema, schemaRegistry, 'output')\n\n // Check is the JSON schema is null then return as it is since fastify-swagger will handle it\n if (jsonSchema.type === 'null') {\n transformed.response[prop] = jsonSchema\n continue\n }\n\n const oasSchema = jsonSchemaToOAS(jsonSchema, oasVersion)\n\n transformed.response[prop] = oasSchema\n }\n }\n\n for (const prop in rest) {\n const meta = rest[prop as keyof typeof rest]\n if (meta) {\n transformed[prop] = meta\n }\n }\n\n return { schema: transformed, url }\n }\n}\n\nexport const jsonSchemaTransform: SwaggerTransform<Schema> = createJsonSchemaTransform({})\n\ntype CreateJsonSchemaTransformObjectOptions = {\n schemaRegistry?: $ZodRegistry<{ id?: string | undefined }>\n}\n\nexport const createJsonSchemaTransformObject =\n ({\n schemaRegistry = globalRegistry,\n }: CreateJsonSchemaTransformObjectOptions): SwaggerTransformObject =>\n (input) => {\n if ('swaggerObject' in input) {\n throw new Error('OpenAPI 2.0 is not supported')\n }\n\n const oasVersion = getOASVersion(input)\n\n const inputSchemas = zodRegistryToJson(schemaRegistry, 'input')\n const outputSchemas = zodRegistryToJson(schemaRegistry, 'output')\n\n for (const key in outputSchemas) {\n if (inputSchemas[key]) {\n throw new Error(\n `Collision detected for schema \"${key}\". The is already an input schema with the same name.`,\n )\n }\n }\n\n const jsonSchemas = {\n ...inputSchemas,\n ...outputSchemas,\n }\n\n const oasSchemas = Object.fromEntries(\n Object.entries(jsonSchemas).map(([key, value]) => [key, jsonSchemaToOAS(value, oasVersion)]),\n )\n\n return {\n ...input.openapiObject,\n components: {\n ...input.openapiObject.components,\n schemas: {\n ...input.openapiObject.components?.schemas,\n ...oasSchemas,\n },\n },\n } as ReturnType<SwaggerTransformObject>\n }\n\nexport const jsonSchemaTransformObject: SwaggerTransformObject = createJsonSchemaTransformObject({})\n\nexport const validatorCompiler: FastifySchemaCompiler<$ZodType> =\n ({ schema }) =>\n (data) => {\n const result = safeParse(schema, data)\n if (result.error) {\n return { error: createValidationError(result.error) as unknown as Error }\n }\n\n return { value: result.data }\n }\n\nfunction resolveSchema(maybeSchema: $ZodType | { properties: $ZodType }): $ZodType {\n if (maybeSchema instanceof $ZodType) {\n return maybeSchema\n }\n if ('properties' in maybeSchema && maybeSchema.properties instanceof $ZodType) {\n return maybeSchema.properties\n }\n throw new InvalidSchemaError(JSON.stringify(maybeSchema))\n}\n\ntype ReplacerFunction = (this: any, key: string, value: any) => any\n\nexport type ZodSerializerCompilerOptions = {\n replacer?: ReplacerFunction\n}\n\nexport const createSerializerCompiler =\n (\n options?: ZodSerializerCompilerOptions,\n ): FastifySerializerCompiler<$ZodType | { properties: $ZodType }> =>\n ({ schema: maybeSchema, method, url }) =>\n (data) => {\n const schema = resolveSchema(maybeSchema)\n\n const result = safeParse(schema, data)\n if (result.error) {\n throw new ResponseSerializationError(method, url, { cause: result.error })\n }\n\n return JSON.stringify(result.data, options?.replacer)\n }\n\nexport const serializerCompiler: ReturnType<typeof createSerializerCompiler> =\n createSerializerCompiler({})\n\n/**\n * FastifyPluginCallbackZod with Zod automatic type inference\n *\n * @example\n * ```typescript\n * import { FastifyPluginCallbackZod } from \"fastify-type-provider-zod\"\n *\n * const plugin: FastifyPluginCallbackZod = (fastify, options, done) => {\n * done()\n * }\n * ```\n */\nexport type FastifyPluginCallbackZod<\n Options extends FastifyPluginOptions = Record<never, never>,\n Server extends RawServerBase = RawServerDefault,\n> = FastifyPluginCallback<Options, Server, ZodTypeProvider>\n\n/**\n * FastifyPluginAsyncZod with Zod automatic type inference\n *\n * @example\n * ```typescript\n * import { FastifyPluginAsyncZod } from \"fastify-type-provider-zod\"\n *\n * const plugin: FastifyPluginAsyncZod = async (fastify, options) => {\n * }\n * ```\n */\nexport type FastifyPluginAsyncZod<\n Options extends FastifyPluginOptions = Record<never, never>,\n Server extends RawServerBase = RawServerDefault,\n> = FastifyPluginAsync<Options, Server, ZodTypeProvider>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type FastifyErrorConstructor } from "@fastify/error";
|
|
2
|
+
import type { FastifyError } from "fastify";
|
|
3
|
+
import type { FastifySchemaValidationError } from "fastify/types/schema";
|
|
4
|
+
import type { $ZodError } from "zod/v4/core";
|
|
5
|
+
export declare const InvalidSchemaError: FastifyErrorConstructor<{
|
|
6
|
+
code: string;
|
|
7
|
+
}, [string]>;
|
|
8
|
+
declare const ZodFastifySchemaValidationErrorSymbol: symbol;
|
|
9
|
+
export type ZodFastifySchemaValidationError = FastifySchemaValidationError & {
|
|
10
|
+
[ZodFastifySchemaValidationErrorSymbol]: true;
|
|
11
|
+
};
|
|
12
|
+
declare const ResponseSerializationBase: FastifyErrorConstructor<{
|
|
13
|
+
code: string;
|
|
14
|
+
}, [{
|
|
15
|
+
cause: $ZodError;
|
|
16
|
+
}]>;
|
|
17
|
+
export declare class ResponseSerializationError extends ResponseSerializationBase {
|
|
18
|
+
method: string;
|
|
19
|
+
url: string;
|
|
20
|
+
cause: $ZodError;
|
|
21
|
+
constructor(method: string, url: string, options: {
|
|
22
|
+
cause: $ZodError;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export declare function isResponseSerializationError(value: unknown): value is ResponseSerializationError;
|
|
26
|
+
export declare function hasZodFastifySchemaValidationErrors(error: unknown): error is Omit<FastifyError, "validation"> & {
|
|
27
|
+
validation: ZodFastifySchemaValidationError[];
|
|
28
|
+
};
|
|
29
|
+
export declare function createValidationError(error: $ZodError): ZodFastifySchemaValidationError[];
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import createError from "@fastify/error";
|
|
2
|
+
const InvalidSchemaError = createError("FST_ERR_INVALID_SCHEMA", "Invalid schema passed: %s", 500);
|
|
3
|
+
const ZodFastifySchemaValidationErrorSymbol = Symbol.for("ZodFastifySchemaValidationError");
|
|
4
|
+
const ResponseSerializationBase = createError(
|
|
5
|
+
"FST_ERR_RESPONSE_SERIALIZATION",
|
|
6
|
+
"Response doesn't match the schema",
|
|
7
|
+
500
|
|
8
|
+
);
|
|
9
|
+
class ResponseSerializationError extends ResponseSerializationBase {
|
|
10
|
+
constructor(method, url, options) {
|
|
11
|
+
super({ cause: options.cause });
|
|
12
|
+
this.method = method;
|
|
13
|
+
this.url = url;
|
|
14
|
+
this.cause = options.cause;
|
|
15
|
+
}
|
|
16
|
+
cause;
|
|
17
|
+
}
|
|
18
|
+
function isResponseSerializationError(value) {
|
|
19
|
+
return "method" in value;
|
|
20
|
+
}
|
|
21
|
+
function isZodFastifySchemaValidationError(error) {
|
|
22
|
+
return typeof error === "object" && error !== null && error[ZodFastifySchemaValidationErrorSymbol] === true;
|
|
23
|
+
}
|
|
24
|
+
function hasZodFastifySchemaValidationErrors(error) {
|
|
25
|
+
return typeof error === "object" && error !== null && "validation" in error && Array.isArray(error.validation) && error.validation.length > 0 && isZodFastifySchemaValidationError(error.validation[0]);
|
|
26
|
+
}
|
|
27
|
+
function omit(obj, keys) {
|
|
28
|
+
const result = {};
|
|
29
|
+
for (const key of Object.keys(obj)) {
|
|
30
|
+
if (!keys.includes(key)) {
|
|
31
|
+
result[key] = obj[key];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
function createValidationError(error) {
|
|
37
|
+
return error.issues.map((issue) => {
|
|
38
|
+
return {
|
|
39
|
+
[ZodFastifySchemaValidationErrorSymbol]: true,
|
|
40
|
+
keyword: issue.code,
|
|
41
|
+
instancePath: `/${issue.path.join("/")}`,
|
|
42
|
+
schemaPath: `#/${issue.path.join("/")}/${issue.code}`,
|
|
43
|
+
message: issue.message,
|
|
44
|
+
params: {
|
|
45
|
+
...omit(issue, ["path", "code", "message"])
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
InvalidSchemaError,
|
|
52
|
+
ResponseSerializationError,
|
|
53
|
+
createValidationError,
|
|
54
|
+
hasZodFastifySchemaValidationErrors,
|
|
55
|
+
isResponseSerializationError
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sources":["../../src/errors.ts"],"sourcesContent":["import createError, { type FastifyErrorConstructor } from '@fastify/error'\nimport type { FastifyError } from 'fastify'\nimport type { FastifySchemaValidationError } from 'fastify/types/schema'\nimport type { $ZodError } from 'zod/v4/core'\n\nexport const InvalidSchemaError: FastifyErrorConstructor<\n {\n code: string\n },\n [string]\n> = createError<[string]>('FST_ERR_INVALID_SCHEMA', 'Invalid schema passed: %s', 500)\n\nconst ZodFastifySchemaValidationErrorSymbol: symbol = Symbol.for('ZodFastifySchemaValidationError')\n\nexport type ZodFastifySchemaValidationError = FastifySchemaValidationError & {\n [ZodFastifySchemaValidationErrorSymbol]: true\n}\n\nconst ResponseSerializationBase: FastifyErrorConstructor<\n {\n code: string\n },\n [\n {\n cause: $ZodError\n },\n ]\n> = createError<[{ cause: $ZodError }]>(\n 'FST_ERR_RESPONSE_SERIALIZATION',\n \"Response doesn't match the schema\",\n 500,\n)\n\nexport class ResponseSerializationError extends ResponseSerializationBase {\n cause!: $ZodError\n\n constructor(\n public method: string,\n public url: string,\n options: { cause: $ZodError },\n ) {\n super({ cause: options.cause })\n\n this.cause = options.cause\n }\n}\n\nexport function isResponseSerializationError(value: unknown): value is ResponseSerializationError {\n return 'method' in (value as ResponseSerializationError)\n}\n\nfunction isZodFastifySchemaValidationError(\n error: unknown,\n): error is ZodFastifySchemaValidationError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as ZodFastifySchemaValidationError)[ZodFastifySchemaValidationErrorSymbol] === true\n )\n}\n\nexport function hasZodFastifySchemaValidationErrors(\n error: unknown,\n): error is Omit<FastifyError, 'validation'> & { validation: ZodFastifySchemaValidationError[] } {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'validation' in error &&\n Array.isArray(error.validation) &&\n error.validation.length > 0 &&\n isZodFastifySchemaValidationError(error.validation[0])\n )\n}\n\nfunction omit<T extends object, K extends keyof T>(obj: T, keys: readonly K[]): Omit<T, K> {\n const result = {} as Omit<T, K>\n for (const key of Object.keys(obj) as Array<keyof T>) {\n if (!keys.includes(key as K)) {\n // @ts-expect-error\n result[key] = obj[key]\n }\n }\n return result\n}\n\nexport function createValidationError(error: $ZodError): ZodFastifySchemaValidationError[] {\n return error.issues.map((issue) => {\n return {\n [ZodFastifySchemaValidationErrorSymbol]: true,\n keyword: issue.code,\n instancePath: `/${issue.path.join('/')}`,\n schemaPath: `#/${issue.path.join('/')}/${issue.code}`,\n message: issue.message,\n params: {\n ...omit(issue, ['path', 'code', 'message']),\n },\n }\n })\n}\n"],"names":[],"mappings":";AAKO,MAAM,qBAKT,YAAsB,0BAA0B,6BAA6B,GAAG;AAEpF,MAAM,wCAAgD,OAAO,IAAI,iCAAiC;AAMlG,MAAM,4BASF;AAAA,EACF;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,mCAAmC,0BAA0B;AAAA,EAGxE,YACS,QACA,KACP,SACA;AACA,UAAM,EAAE,OAAO,QAAQ,MAAA,CAAO;AAJvB,SAAA,SAAA;AACA,SAAA,MAAA;AAKP,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAVA;AAWF;AAEO,SAAS,6BAA6B,OAAqD;AAChG,SAAO,YAAa;AACtB;AAEA,SAAS,kCACP,OAC0C;AAC1C,SACE,OAAO,UAAU,YACjB,UAAU,QACT,MAA0C,qCAAqC,MAAM;AAE1F;AAEO,SAAS,oCACd,OAC+F;AAC/F,SACE,OAAO,UAAU,YACjB,UAAU,QACV,gBAAgB,SAChB,MAAM,QAAQ,MAAM,UAAU,KAC9B,MAAM,WAAW,SAAS,KAC1B,kCAAkC,MAAM,WAAW,CAAC,CAAC;AAEzD;AAEA,SAAS,KAA0C,KAAQ,MAAgC;AACzF,QAAM,SAAS,CAAA;AACf,aAAW,OAAO,OAAO,KAAK,GAAG,GAAqB;AACpD,QAAI,CAAC,KAAK,SAAS,GAAQ,GAAG;AAE5B,aAAO,GAAG,IAAI,IAAI,GAAG;AAAA,IACvB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,sBAAsB,OAAqD;AACzF,SAAO,MAAM,OAAO,IAAI,CAAC,UAAU;AACjC,WAAO;AAAA,MACL,CAAC,qCAAqC,GAAG;AAAA,MACzC,SAAS,MAAM;AAAA,MACf,cAAc,IAAI,MAAM,KAAK,KAAK,GAAG,CAAC;AAAA,MACtC,YAAY,KAAK,MAAM,KAAK,KAAK,GAAG,CAAC,IAAI,MAAM,IAAI;AAAA,MACnD,SAAS,MAAM;AAAA,MACf,QAAQ;AAAA,QACN,GAAG,KAAK,OAAO,CAAC,QAAQ,QAAQ,SAAS,CAAC;AAAA,MAAA;AAAA,IAC5C;AAAA,EAEJ,CAAC;AACH;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { createJsonSchemaTransform, createJsonSchemaTransformObject, createSerializerCompiler, type FastifyPluginAsyncZod, type FastifyPluginCallbackZod, jsonSchemaTransform, jsonSchemaTransformObject, serializerCompiler, validatorCompiler, type ZodSerializerCompilerOptions, type ZodTypeProvider } from "../esm/core.js";
|
|
2
|
+
export { hasZodFastifySchemaValidationErrors, InvalidSchemaError, isResponseSerializationError, ResponseSerializationError, type ZodFastifySchemaValidationError } from "../esm/errors.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createJsonSchemaTransform, createJsonSchemaTransformObject, createSerializerCompiler, jsonSchemaTransform, jsonSchemaTransformObject, serializerCompiler, validatorCompiler } from "./core.js";
|
|
2
|
+
import { InvalidSchemaError, ResponseSerializationError, hasZodFastifySchemaValidationErrors, isResponseSerializationError } from "./errors.js";
|
|
3
|
+
export {
|
|
4
|
+
InvalidSchemaError,
|
|
5
|
+
ResponseSerializationError,
|
|
6
|
+
createJsonSchemaTransform,
|
|
7
|
+
createJsonSchemaTransformObject,
|
|
8
|
+
createSerializerCompiler,
|
|
9
|
+
hasZodFastifySchemaValidationErrors,
|
|
10
|
+
isResponseSerializationError,
|
|
11
|
+
jsonSchemaTransform,
|
|
12
|
+
jsonSchemaTransformObject,
|
|
13
|
+
serializerCompiler,
|
|
14
|
+
validatorCompiler
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OpenAPIV3, OpenAPIV3_1 } from "openapi-types";
|
|
2
|
+
import type { JSONSchema } from "zod/v4/core";
|
|
3
|
+
type OASVersion = "3.0" | "3.1";
|
|
4
|
+
export declare const getOASVersion: (documentObject: {
|
|
5
|
+
openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>;
|
|
6
|
+
}) => OASVersion;
|
|
7
|
+
export declare const jsonSchemaToOAS_3_0: (jsonSchema: JSONSchema.BaseSchema) => OpenAPIV3.SchemaObject;
|
|
8
|
+
export declare const jsonSchemaToOAS: (jsonSchema: JSONSchema.BaseSchema, oasVersion: OASVersion) => OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const getOASVersion = (documentObject) => {
|
|
2
|
+
const openapiVersion = documentObject.openapiObject.openapi || "3.0.3";
|
|
3
|
+
if (openapiVersion.startsWith("3.1")) {
|
|
4
|
+
return "3.1";
|
|
5
|
+
}
|
|
6
|
+
if (openapiVersion.startsWith("3.0")) {
|
|
7
|
+
return "3.0";
|
|
8
|
+
}
|
|
9
|
+
throw new Error("Unsupported OpenAPI document object");
|
|
10
|
+
};
|
|
11
|
+
const jsonSchemaToOAS_3_0 = (jsonSchema) => {
|
|
12
|
+
const clone = { ...jsonSchema };
|
|
13
|
+
if (clone.type === "null") {
|
|
14
|
+
clone.nullable = true;
|
|
15
|
+
delete clone.type;
|
|
16
|
+
clone.enum = [null];
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(clone.prefixItems)) {
|
|
19
|
+
const tuple = clone.prefixItems;
|
|
20
|
+
clone.minItems ??= tuple.length;
|
|
21
|
+
clone.maxItems ??= tuple.length;
|
|
22
|
+
clone.items = {
|
|
23
|
+
oneOf: tuple.map(jsonSchemaToOAS_3_0)
|
|
24
|
+
};
|
|
25
|
+
delete clone.prefixItems;
|
|
26
|
+
}
|
|
27
|
+
if ("const" in clone && clone.const !== void 0) {
|
|
28
|
+
clone.enum = [clone.const];
|
|
29
|
+
delete clone.const;
|
|
30
|
+
}
|
|
31
|
+
if (typeof clone.exclusiveMinimum === "number") {
|
|
32
|
+
clone.minimum = clone.exclusiveMinimum;
|
|
33
|
+
clone.exclusiveMinimum = true;
|
|
34
|
+
}
|
|
35
|
+
if (typeof clone.exclusiveMaximum === "number") {
|
|
36
|
+
clone.maximum = clone.exclusiveMaximum;
|
|
37
|
+
clone.exclusiveMaximum = true;
|
|
38
|
+
}
|
|
39
|
+
for (const key of [
|
|
40
|
+
"$schema",
|
|
41
|
+
"$id",
|
|
42
|
+
"unevaluatedProperties",
|
|
43
|
+
"dependentSchemas",
|
|
44
|
+
"patternProperties",
|
|
45
|
+
"propertyNames",
|
|
46
|
+
"contentEncoding",
|
|
47
|
+
"contentMediaType"
|
|
48
|
+
]) {
|
|
49
|
+
delete clone[key];
|
|
50
|
+
}
|
|
51
|
+
const recursive = (v) => Array.isArray(v) ? v.map(jsonSchemaToOAS_3_0) : jsonSchemaToOAS_3_0(v);
|
|
52
|
+
if (clone.properties) {
|
|
53
|
+
for (const [k, v] of Object.entries(clone.properties)) {
|
|
54
|
+
clone.properties[k] = jsonSchemaToOAS_3_0(v);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (clone.items && !Array.isArray(clone.items)) {
|
|
58
|
+
clone.items = recursive(clone.items);
|
|
59
|
+
}
|
|
60
|
+
for (const key of ["allOf", "anyOf", "oneOf", "not", "then", "else", "if", "contains"]) {
|
|
61
|
+
if (clone[key]) {
|
|
62
|
+
clone[key] = recursive(clone[key]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return clone;
|
|
66
|
+
};
|
|
67
|
+
const jsonSchemaToOAS_3_1 = (jsonSchema) => {
|
|
68
|
+
return jsonSchema;
|
|
69
|
+
};
|
|
70
|
+
const jsonSchemaToOAS = (jsonSchema, oasVersion) => {
|
|
71
|
+
switch (oasVersion) {
|
|
72
|
+
case "3.0":
|
|
73
|
+
return jsonSchemaToOAS_3_0(jsonSchema);
|
|
74
|
+
case "3.1":
|
|
75
|
+
return jsonSchemaToOAS_3_1(jsonSchema);
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Unsupported OpenAPI version: ${oasVersion}`);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
export {
|
|
81
|
+
getOASVersion,
|
|
82
|
+
jsonSchemaToOAS,
|
|
83
|
+
jsonSchemaToOAS_3_0
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=json-to-oas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-to-oas.js","sources":["../../src/json-to-oas.ts"],"sourcesContent":["import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'\nimport type { JSONSchema } from 'zod/v4/core'\n\ntype OASVersion = '3.0' | '3.1'\n\nexport const getOASVersion = (documentObject: {\n openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>\n}): OASVersion => {\n const openapiVersion = documentObject.openapiObject.openapi || '3.0.3'\n\n if (openapiVersion.startsWith('3.1')) {\n return '3.1'\n }\n\n if (openapiVersion.startsWith('3.0')) {\n return '3.0'\n }\n\n throw new Error('Unsupported OpenAPI document object')\n}\n\nexport const jsonSchemaToOAS_3_0 = (jsonSchema: JSONSchema.BaseSchema): OpenAPIV3.SchemaObject => {\n const clone: any = { ...jsonSchema }\n\n if (clone.type === 'null') {\n clone.nullable = true\n delete clone.type\n clone.enum = [null]\n }\n\n if (Array.isArray(clone.prefixItems)) {\n const tuple = clone.prefixItems as JSONSchema.BaseSchema[]\n\n clone.minItems ??= tuple.length\n clone.maxItems ??= tuple.length\n\n clone.items = {\n oneOf: tuple.map(jsonSchemaToOAS_3_0),\n }\n\n delete clone.prefixItems\n }\n\n if ('const' in clone && clone.const !== undefined) {\n clone.enum = [clone.const]\n delete clone.const\n }\n\n if (typeof clone.exclusiveMinimum === 'number') {\n clone.minimum = clone.exclusiveMinimum\n clone.exclusiveMinimum = true\n }\n if (typeof clone.exclusiveMaximum === 'number') {\n clone.maximum = clone.exclusiveMaximum\n clone.exclusiveMaximum = true\n }\n\n for (const key of [\n '$schema',\n '$id',\n 'unevaluatedProperties',\n 'dependentSchemas',\n 'patternProperties',\n 'propertyNames',\n 'contentEncoding',\n 'contentMediaType',\n ]) {\n delete clone[key]\n }\n\n const recursive = (v: any): any =>\n Array.isArray(v) ? v.map(jsonSchemaToOAS_3_0) : jsonSchemaToOAS_3_0(v)\n\n if (clone.properties) {\n for (const [k, v] of Object.entries(clone.properties)) {\n clone.properties![k] = jsonSchemaToOAS_3_0(v as any)\n }\n }\n\n if (clone.items && !Array.isArray(clone.items)) {\n clone.items = recursive(clone.items)\n }\n\n for (const key of ['allOf', 'anyOf', 'oneOf', 'not', 'then', 'else', 'if', 'contains']) {\n if (clone[key]) {\n clone[key] = recursive(clone[key])\n }\n }\n\n return clone as OpenAPIV3.SchemaObject\n}\n\nconst jsonSchemaToOAS_3_1 = (jsonSchema: JSONSchema.BaseSchema): OpenAPIV3_1.SchemaObject => {\n return jsonSchema as OpenAPIV3_1.SchemaObject\n}\n\nexport const jsonSchemaToOAS = (\n jsonSchema: JSONSchema.BaseSchema,\n oasVersion: OASVersion,\n): OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject => {\n switch (oasVersion) {\n case '3.0':\n return jsonSchemaToOAS_3_0(jsonSchema)\n case '3.1':\n return jsonSchemaToOAS_3_1(jsonSchema)\n default:\n throw new Error(`Unsupported OpenAPI version: ${oasVersion}`)\n }\n}\n"],"names":[],"mappings":"AAKO,MAAM,gBAAgB,CAAC,mBAEZ;AAChB,QAAM,iBAAiB,eAAe,cAAc,WAAW;AAE/D,MAAI,eAAe,WAAW,KAAK,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,WAAW,KAAK,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,IAAI,MAAM,qCAAqC;AACvD;AAEO,MAAM,sBAAsB,CAAC,eAA8D;AAChG,QAAM,QAAa,EAAE,GAAG,WAAA;AAExB,MAAI,MAAM,SAAS,QAAQ;AACzB,UAAM,WAAW;AACjB,WAAO,MAAM;AACb,UAAM,OAAO,CAAC,IAAI;AAAA,EACpB;AAEA,MAAI,MAAM,QAAQ,MAAM,WAAW,GAAG;AACpC,UAAM,QAAQ,MAAM;AAEpB,UAAM,aAAa,MAAM;AACzB,UAAM,aAAa,MAAM;AAEzB,UAAM,QAAQ;AAAA,MACZ,OAAO,MAAM,IAAI,mBAAmB;AAAA,IAAA;AAGtC,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,WAAW,SAAS,MAAM,UAAU,QAAW;AACjD,UAAM,OAAO,CAAC,MAAM,KAAK;AACzB,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,OAAO,MAAM,qBAAqB,UAAU;AAC9C,UAAM,UAAU,MAAM;AACtB,UAAM,mBAAmB;AAAA,EAC3B;AACA,MAAI,OAAO,MAAM,qBAAqB,UAAU;AAC9C,UAAM,UAAU,MAAM;AACtB,UAAM,mBAAmB;AAAA,EAC3B;AAEA,aAAW,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,GACC;AACD,WAAO,MAAM,GAAG;AAAA,EAClB;AAEA,QAAM,YAAY,CAAC,MACjB,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,mBAAmB,IAAI,oBAAoB,CAAC;AAEvE,MAAI,MAAM,YAAY;AACpB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,UAAU,GAAG;AACrD,YAAM,WAAY,CAAC,IAAI,oBAAoB,CAAQ;AAAA,IACrD;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG;AAC9C,UAAM,QAAQ,UAAU,MAAM,KAAK;AAAA,EACrC;AAEA,aAAW,OAAO,CAAC,SAAS,SAAS,SAAS,OAAO,QAAQ,QAAQ,MAAM,UAAU,GAAG;AACtF,QAAI,MAAM,GAAG,GAAG;AACd,YAAM,GAAG,IAAI,UAAU,MAAM,GAAG,CAAC;AAAA,IACnC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,sBAAsB,CAAC,eAAgE;AAC3F,SAAO;AACT;AAEO,MAAM,kBAAkB,CAC7B,YACA,eACsD;AACtD,UAAQ,YAAA;AAAA,IACN,KAAK;AACH,aAAO,oBAAoB,UAAU;AAAA,IACvC,KAAK;AACH,aAAO,oBAAoB,UAAU;AAAA,IACvC;AACE,YAAM,IAAI,MAAM,gCAAgC,UAAU,EAAE;AAAA,EAAA;AAElE;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSONSchema } from "zod/v4/core";
|
|
2
|
+
import { $ZodRegistry, $ZodType } from "zod/v4/core";
|
|
3
|
+
export declare const zodSchemaToJson: (zodSchema: $ZodType, registry: $ZodRegistry<{
|
|
4
|
+
id?: string;
|
|
5
|
+
}>, io: "input" | "output") => JSONSchema.BaseSchema;
|
|
6
|
+
export declare const zodRegistryToJson: (registry: $ZodRegistry<{
|
|
7
|
+
id?: string;
|
|
8
|
+
}>, io: "input" | "output") => Record<string, JSONSchema.BaseSchema>;
|