@efebia/fastify-zod-reply 1.4.2 → 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.
@@ -1,4 +1,61 @@
1
- export type StatusCode<TCode> = {
1
+ export type CodePayload<TReply, TCode extends number, TFallback = unknown> = TReply extends {
2
+ send(payload?: infer TMap): any;
3
+ } ? TMap extends Record<TCode, infer T> ? T : TFallback : TFallback;
4
+ export type ResponseParam<TReply, TCode extends number> = CodePayload<TReply, TCode> extends Record<PropertyKey, unknown> ? CodePayload<TReply, TCode> & Record<string, unknown> : CodePayload<TReply, TCode>;
5
+ export type ValidateResponseParam<TThis, TCode extends number> = CodePayload<TThis, TCode, never> extends never ? string : CodePayload<TThis, TCode, never> extends {
6
+ message: infer M;
7
+ } ? M & string : never;
8
+ export type ErrorMethodParam<TThis, TCode extends number> = ValidateResponseParam<TThis, TCode> | ResponseParam<TThis, TCode>;
9
+ export type ErrorCodePayload<TVal> = [TVal] extends [string] ? {
10
+ message: TVal;
11
+ } : TVal;
12
+ type DefaultArgs<TReply, TCode extends number, TParam> = CodePayload<TReply, TCode, never> extends never ? [val?: undefined] : [val: TParam];
13
+ declare module "fastify" {
14
+ interface FastifyReply {
15
+ ok(...args: DefaultArgs<this, 200, ResponseParam<this, 200>>): CodePayload<this, 200, {
16
+ message: "ok";
17
+ }>;
18
+ ok(val: ResponseParam<this, 200>): CodePayload<this, 200>;
19
+ created(...args: DefaultArgs<this, 201, ResponseParam<this, 201>>): CodePayload<this, 201, {
20
+ message: "created";
21
+ }>;
22
+ created(val: ResponseParam<this, 201>): CodePayload<this, 201>;
23
+ accepted(...args: DefaultArgs<this, 202, ResponseParam<this, 202>>): CodePayload<this, 202, {
24
+ message: "accepted";
25
+ }>;
26
+ accepted(val: ResponseParam<this, 202>): CodePayload<this, 202>;
27
+ noContent(): CodePayload<this, 204, void>;
28
+ badRequest(...args: DefaultArgs<this, 400, ErrorMethodParam<this, 400>>): CodePayload<this, 400, {
29
+ message: "badRequest";
30
+ }>;
31
+ badRequest<TVal extends ErrorMethodParam<this, 400>>(val: TVal): ErrorCodePayload<TVal>;
32
+ unauthorized(...args: DefaultArgs<this, 401, ErrorMethodParam<this, 401>>): CodePayload<this, 401, {
33
+ message: "unauthorized";
34
+ }>;
35
+ unauthorized<TVal extends ErrorMethodParam<this, 401>>(val: TVal): ErrorCodePayload<TVal>;
36
+ forbidden(...args: DefaultArgs<this, 403, ErrorMethodParam<this, 403>>): CodePayload<this, 403, {
37
+ message: "forbidden";
38
+ }>;
39
+ forbidden<TVal extends ErrorMethodParam<this, 403>>(val: TVal): ErrorCodePayload<TVal>;
40
+ notFound(...args: DefaultArgs<this, 404, ErrorMethodParam<this, 404>>): CodePayload<this, 404, {
41
+ message: "notFound";
42
+ }>;
43
+ notFound<TVal extends ErrorMethodParam<this, 404>>(val: TVal): ErrorCodePayload<TVal>;
44
+ notAcceptable(...args: DefaultArgs<this, 406, ErrorMethodParam<this, 406>>): CodePayload<this, 406, {
45
+ message: "notAcceptable";
46
+ }>;
47
+ notAcceptable<TVal extends ErrorMethodParam<this, 406>>(val: TVal): ErrorCodePayload<TVal>;
48
+ conflict(...args: DefaultArgs<this, 409, ErrorMethodParam<this, 409>>): CodePayload<this, 409, {
49
+ message: "conflict";
50
+ }>;
51
+ conflict<TVal extends ErrorMethodParam<this, 409>>(val: TVal): ErrorCodePayload<TVal>;
52
+ internalServerError(...args: DefaultArgs<this, 500, ErrorMethodParam<this, 500>>): CodePayload<this, 500, {
53
+ message: "internalServerError";
54
+ }>;
55
+ internalServerError<TVal extends ErrorMethodParam<this, 500>>(val: TVal): ErrorCodePayload<TVal>;
56
+ }
57
+ }
58
+ export type StatusCode<TCode extends number> = {
2
59
  statusCode: TCode;
3
60
  payload: any;
4
61
  };
@@ -18,15 +75,8 @@ export interface FastifyStatusCode {
18
75
  export type FastifyReplyPluginOptions = {
19
76
  statusCodes?: {
20
77
  [key in keyof FastifyStatusCode]?: FastifyStatusCode[key];
21
- };
22
- };
23
- export type DecoratedReply = {
24
- [key in keyof FastifyStatusCode]: <T>(val?: T) => T;
78
+ } & Record<string, StatusCode<any>>;
25
79
  };
26
- declare module "fastify" {
27
- interface FastifyReply extends DecoratedReply {
28
- }
29
- }
30
80
  declare const _default: import("fastify").FastifyPluginCallback<FastifyReplyPluginOptions, import("fastify").RawServerDefault, import("fastify").FastifyTypeProviderDefault, import("fastify").FastifyBaseLogger>;
31
81
  export default _default;
32
82
  export { buildHTTPErrorObject, FastifyZodReplyError } from "./error.js";
package/lib/cjs/index.js CHANGED
@@ -33,10 +33,7 @@ const defaultOptions = {
33
33
  notFound: { statusCode: 404, payload: { message: "notFound" } },
34
34
  notAcceptable: { statusCode: 406, payload: { message: "notAcceptable" } },
35
35
  conflict: { statusCode: 409, payload: { message: "conflict" } },
36
- internalServerError: {
37
- statusCode: 500,
38
- payload: { message: "internalServerError" },
39
- },
36
+ internalServerError: { statusCode: 500, payload: { message: "internalServerError" } },
40
37
  },
41
38
  };
42
39
  exports.default = (0, fastify_plugin_1.default)(async (fastify, opts) => {
@@ -15,3 +15,14 @@ export declare const strictifySchema: (schema: z.ZodType, strict: boolean) => an
15
15
  export declare const parseStrict: (tag: keyof Exclude<NonNullable<RouteV4Options["strict"]>, boolean>, value: NonNullable<RouteV4Options["strict"]>) => boolean;
16
16
  export declare const findStatusCode: (statusCode: number, availableStatusCodes: [string | number, any][]) => [string | number, any] | undefined;
17
17
  export declare const mapZodError: (zodError: z.ZodError, prefix: string) => string;
18
+ export declare const buildZodJsonSchemaParams: (io: "input" | "output") => {
19
+ io: "input" | "output";
20
+ reused: "inline";
21
+ target: "draft-7";
22
+ unrepresentable: "any";
23
+ override(ctx: {
24
+ zodSchema: z.core.$ZodTypes;
25
+ jsonSchema: z.core.JSONSchema.BaseSchema;
26
+ path: (string | number)[];
27
+ }): void;
28
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mapZodError = exports.findStatusCode = exports.parseStrict = exports.strictifySchema = exports.parse = void 0;
3
+ exports.buildZodJsonSchemaParams = exports.mapZodError = exports.findStatusCode = exports.parseStrict = exports.strictifySchema = exports.parse = void 0;
4
4
  const parse = async (schema, payload, tag) => {
5
5
  const result = await schema.safeParseAsync(payload);
6
6
  return Object.assign(Object.assign({}, result), { tag });
@@ -9,9 +9,7 @@ exports.parse = parse;
9
9
  const strictifySchema = (schema, strict) => {
10
10
  if (!strict)
11
11
  return schema;
12
- return "strict" in schema && typeof schema["strict"] === "function"
13
- ? schema.strict()
14
- : schema;
12
+ return "strict" in schema && typeof schema["strict"] === "function" ? schema.strict() : schema;
15
13
  };
16
14
  exports.strictifySchema = strictifySchema;
17
15
  const parseStrict = (tag, value) => {
@@ -42,3 +40,18 @@ const mapZodError = (zodError, prefix) => {
42
40
  .join("\n");
43
41
  };
44
42
  exports.mapZodError = mapZodError;
43
+ const buildZodJsonSchemaParams = (io) => {
44
+ return {
45
+ io,
46
+ reused: "inline",
47
+ target: "draft-7",
48
+ unrepresentable: "any",
49
+ override(ctx) {
50
+ const def = ctx.zodSchema._zod.def;
51
+ if (def.type === "date") {
52
+ ctx.jsonSchema.type = "string";
53
+ }
54
+ },
55
+ };
56
+ };
57
+ exports.buildZodJsonSchemaParams = buildZodJsonSchemaParams;
@@ -14,11 +14,16 @@ export type BaseZodV4Schema = {
14
14
  Summary?: string;
15
15
  Notes?: string;
16
16
  };
17
+ type ErrorReplyFallback<TReply> = {
18
+ [K in Exclude<400 | 401 | 403 | 404 | 406 | 409 | 500, keyof TReply>]?: {
19
+ message: string;
20
+ };
21
+ };
17
22
  export type FastifyZodV4Schema<TZodSchema extends BaseZodV4Schema> = {
18
23
  Body: TZodSchema["Body"] extends z.ZodTypeAny ? z.output<TZodSchema["Body"]> : undefined;
19
24
  Params: TZodSchema["Params"] extends z.ZodTypeAny ? z.output<TZodSchema["Params"]> : undefined;
20
25
  Querystring: TZodSchema["Query"] extends z.ZodTypeAny ? z.output<TZodSchema["Query"]> : undefined;
21
- Reply: TZodSchema["Reply"] extends z.ZodTypeAny ? z.input<TZodSchema["Reply"]>[keyof z.input<TZodSchema["Reply"]>] : undefined;
26
+ Reply: TZodSchema["Reply"] extends z.ZodTypeAny ? z.input<TZodSchema["Reply"]> & ErrorReplyFallback<z.input<TZodSchema["Reply"]>> : undefined;
22
27
  };
23
28
  export type RouteV4Options = {
24
29
  strict?: boolean | {
@@ -28,9 +33,10 @@ export type RouteV4Options = {
28
33
  headers: boolean;
29
34
  };
30
35
  };
31
- export declare const createRouteV4: <RequestAugmentation extends object = {}, ReplyAugmentation extends object = {}>({ strict: globalStrict }?: RouteV4Options) => <TSchema extends BaseZodV4Schema, FastifySchema extends FastifyZodV4Schema<TSchema> = FastifyZodV4Schema<TSchema>>(schema: TSchema, handler: NoInfer<APIHandler<FastifySchema, RequestAugmentation, ReplyAugmentation>>, options?: RouteV4Options) => APIOptions<FastifySchema> & {
36
+ export declare const createRouteV4: <RequestAugmentation extends object = {}, ReplyAugmentation extends object = {}>({ strict: globalStrict, }?: RouteV4Options) => <TSchema extends BaseZodV4Schema, FastifySchema extends FastifyZodV4Schema<TSchema> = FastifyZodV4Schema<TSchema>>(schema: TSchema, handler: NoInfer<APIHandler<FastifySchema, RequestAugmentation, ReplyAugmentation>>, options?: RouteV4Options) => APIOptions<FastifySchema> & {
32
37
  handler: APIHandler<FastifySchema>;
33
38
  };
34
39
  export declare const routeV4: <TSchema extends BaseZodV4Schema, FastifySchema extends FastifyZodV4Schema<TSchema> = FastifyZodV4Schema<TSchema>>(schema: TSchema, handler: NoInfer<APIHandler<FastifySchema, {}, {}>>, options?: RouteV4Options) => APIOptions<FastifySchema> & {
35
40
  handler: APIHandler<FastifySchema>;
36
41
  };
42
+ export {};
@@ -4,20 +4,17 @@ exports.routeV4 = exports.createRouteV4 = void 0;
4
4
  const v4_1 = require("zod/v4");
5
5
  const error_js_1 = require("./error.js");
6
6
  const routeHelpers_js_1 = require("./routeHelpers.js");
7
- const createRouteV4 = ({ strict: globalStrict = false } = {}) => (schema, handler, options) => {
7
+ const createRouteV4 = ({ strict: globalStrict = false, } = {}) => (schema, handler, options) => {
8
8
  const strict = typeof (options === null || options === void 0 ? void 0 : options.strict) !== "undefined" ? options === null || options === void 0 ? void 0 : options.strict : globalStrict;
9
9
  const finalResult = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (schema.Body && {
10
- body: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Body, (0, routeHelpers_js_1.parseStrict)("body", strict)), { reused: "inline", target: "draft-7", io: "input" }),
10
+ body: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Body, (0, routeHelpers_js_1.parseStrict)("body", strict)), (0, routeHelpers_js_1.buildZodJsonSchemaParams)('input')),
11
11
  })), (schema.Params && {
12
- params: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Params, (0, routeHelpers_js_1.parseStrict)("params", strict)), { reused: "inline", target: "draft-7", io: "input" }),
12
+ params: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Params, (0, routeHelpers_js_1.parseStrict)("params", strict)), (0, routeHelpers_js_1.buildZodJsonSchemaParams)('input')),
13
13
  })), (schema.Query && {
14
- querystring: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Query, (0, routeHelpers_js_1.parseStrict)("query", strict)), { reused: "inline", target: "draft-7", io: "input" }),
14
+ querystring: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Query, (0, routeHelpers_js_1.parseStrict)("query", strict)), (0, routeHelpers_js_1.buildZodJsonSchemaParams)('input')),
15
15
  })), (schema.Headers && {
16
- headers: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Headers, (0, routeHelpers_js_1.parseStrict)("headers", strict)), { reused: "inline", target: "draft-7", io: "input" }),
17
- })), { response: v4_1.z.toJSONSchema(schema.Reply.partial(), {
18
- reused: "inline",
19
- target: "draft-7",
20
- })["properties"] }), (schema.Security && { security: schema.Security })), (schema.Tags && { tags: schema.Tags })), (schema.Description && { description: schema.Description })), (schema.Summary && { summary: schema.Summary })), (schema.Notes && { notes: schema.Notes }));
16
+ headers: v4_1.z.toJSONSchema((0, routeHelpers_js_1.strictifySchema)(schema.Headers, (0, routeHelpers_js_1.parseStrict)("headers", strict)), (0, routeHelpers_js_1.buildZodJsonSchemaParams)('input')),
17
+ })), { response: v4_1.z.toJSONSchema(schema.Reply.partial(), (0, routeHelpers_js_1.buildZodJsonSchemaParams)('output'))["properties"] }), (schema.Security && { security: schema.Security })), (schema.Tags && { tags: schema.Tags })), (schema.Description && { description: schema.Description })), (schema.Summary && { summary: schema.Summary })), (schema.Notes && { notes: schema.Notes }));
21
18
  return {
22
19
  schema: finalResult,
23
20
  handler,
@@ -25,12 +22,8 @@ const createRouteV4 = ({ strict: globalStrict = false } = {}) => (schema, handle
25
22
  var _a, _b, _c;
26
23
  const results = await Promise.all([
27
24
  ...(schema.Body ? [(0, routeHelpers_js_1.parse)(schema.Body, request.body, "body")] : []),
28
- ...(schema.Params
29
- ? [(0, routeHelpers_js_1.parse)(schema.Params, request.params, "params")]
30
- : []),
31
- ...(schema.Query
32
- ? [(0, routeHelpers_js_1.parse)(schema.Query, request.query, "query")]
33
- : []),
25
+ ...(schema.Params ? [(0, routeHelpers_js_1.parse)(schema.Params, request.params, "params")] : []),
26
+ ...(schema.Query ? [(0, routeHelpers_js_1.parse)(schema.Query, request.query, "query")] : []),
34
27
  ]);
35
28
  for (const result of results) {
36
29
  if (!result.success) {
@@ -42,12 +35,9 @@ const createRouteV4 = ({ strict: globalStrict = false } = {}) => (schema, handle
42
35
  });
43
36
  }
44
37
  }
45
- request.body =
46
- ((_a = results.find((r) => r.tag === "body")) === null || _a === void 0 ? void 0 : _a.data) || {};
47
- request.params =
48
- ((_b = results.find((r) => r.tag === "params")) === null || _b === void 0 ? void 0 : _b.data) || {};
49
- request.query =
50
- ((_c = results.find((r) => r.tag === "query")) === null || _c === void 0 ? void 0 : _c.data) || {};
38
+ request.body = ((_a = results.find((r) => r.tag === "body")) === null || _a === void 0 ? void 0 : _a.data) || {};
39
+ request.params = ((_b = results.find((r) => r.tag === "params")) === null || _b === void 0 ? void 0 : _b.data) || {};
40
+ request.query = ((_c = results.find((r) => r.tag === "query")) === null || _c === void 0 ? void 0 : _c.data) || {};
51
41
  },
52
42
  preSerialization: (request, reply, payload, done) => {
53
43
  const foundSchema = (0, routeHelpers_js_1.findStatusCode)(reply.statusCode, Object.entries(schema.Reply.shape));
@@ -1,4 +1,61 @@
1
- export type StatusCode<TCode> = {
1
+ export type CodePayload<TReply, TCode extends number, TFallback = unknown> = TReply extends {
2
+ send(payload?: infer TMap): any;
3
+ } ? TMap extends Record<TCode, infer T> ? T : TFallback : TFallback;
4
+ export type ResponseParam<TReply, TCode extends number> = CodePayload<TReply, TCode> extends Record<PropertyKey, unknown> ? CodePayload<TReply, TCode> & Record<string, unknown> : CodePayload<TReply, TCode>;
5
+ export type ValidateResponseParam<TThis, TCode extends number> = CodePayload<TThis, TCode, never> extends never ? string : CodePayload<TThis, TCode, never> extends {
6
+ message: infer M;
7
+ } ? M & string : never;
8
+ export type ErrorMethodParam<TThis, TCode extends number> = ValidateResponseParam<TThis, TCode> | ResponseParam<TThis, TCode>;
9
+ export type ErrorCodePayload<TVal> = [TVal] extends [string] ? {
10
+ message: TVal;
11
+ } : TVal;
12
+ type DefaultArgs<TReply, TCode extends number, TParam> = CodePayload<TReply, TCode, never> extends never ? [val?: undefined] : [val: TParam];
13
+ declare module "fastify" {
14
+ interface FastifyReply {
15
+ ok(...args: DefaultArgs<this, 200, ResponseParam<this, 200>>): CodePayload<this, 200, {
16
+ message: "ok";
17
+ }>;
18
+ ok(val: ResponseParam<this, 200>): CodePayload<this, 200>;
19
+ created(...args: DefaultArgs<this, 201, ResponseParam<this, 201>>): CodePayload<this, 201, {
20
+ message: "created";
21
+ }>;
22
+ created(val: ResponseParam<this, 201>): CodePayload<this, 201>;
23
+ accepted(...args: DefaultArgs<this, 202, ResponseParam<this, 202>>): CodePayload<this, 202, {
24
+ message: "accepted";
25
+ }>;
26
+ accepted(val: ResponseParam<this, 202>): CodePayload<this, 202>;
27
+ noContent(): CodePayload<this, 204, void>;
28
+ badRequest(...args: DefaultArgs<this, 400, ErrorMethodParam<this, 400>>): CodePayload<this, 400, {
29
+ message: "badRequest";
30
+ }>;
31
+ badRequest<TVal extends ErrorMethodParam<this, 400>>(val: TVal): ErrorCodePayload<TVal>;
32
+ unauthorized(...args: DefaultArgs<this, 401, ErrorMethodParam<this, 401>>): CodePayload<this, 401, {
33
+ message: "unauthorized";
34
+ }>;
35
+ unauthorized<TVal extends ErrorMethodParam<this, 401>>(val: TVal): ErrorCodePayload<TVal>;
36
+ forbidden(...args: DefaultArgs<this, 403, ErrorMethodParam<this, 403>>): CodePayload<this, 403, {
37
+ message: "forbidden";
38
+ }>;
39
+ forbidden<TVal extends ErrorMethodParam<this, 403>>(val: TVal): ErrorCodePayload<TVal>;
40
+ notFound(...args: DefaultArgs<this, 404, ErrorMethodParam<this, 404>>): CodePayload<this, 404, {
41
+ message: "notFound";
42
+ }>;
43
+ notFound<TVal extends ErrorMethodParam<this, 404>>(val: TVal): ErrorCodePayload<TVal>;
44
+ notAcceptable(...args: DefaultArgs<this, 406, ErrorMethodParam<this, 406>>): CodePayload<this, 406, {
45
+ message: "notAcceptable";
46
+ }>;
47
+ notAcceptable<TVal extends ErrorMethodParam<this, 406>>(val: TVal): ErrorCodePayload<TVal>;
48
+ conflict(...args: DefaultArgs<this, 409, ErrorMethodParam<this, 409>>): CodePayload<this, 409, {
49
+ message: "conflict";
50
+ }>;
51
+ conflict<TVal extends ErrorMethodParam<this, 409>>(val: TVal): ErrorCodePayload<TVal>;
52
+ internalServerError(...args: DefaultArgs<this, 500, ErrorMethodParam<this, 500>>): CodePayload<this, 500, {
53
+ message: "internalServerError";
54
+ }>;
55
+ internalServerError<TVal extends ErrorMethodParam<this, 500>>(val: TVal): ErrorCodePayload<TVal>;
56
+ }
57
+ }
58
+ export type StatusCode<TCode extends number> = {
2
59
  statusCode: TCode;
3
60
  payload: any;
4
61
  };
@@ -18,15 +75,8 @@ export interface FastifyStatusCode {
18
75
  export type FastifyReplyPluginOptions = {
19
76
  statusCodes?: {
20
77
  [key in keyof FastifyStatusCode]?: FastifyStatusCode[key];
21
- };
22
- };
23
- export type DecoratedReply = {
24
- [key in keyof FastifyStatusCode]: <T>(val?: T) => T;
78
+ } & Record<string, StatusCode<any>>;
25
79
  };
26
- declare module "fastify" {
27
- interface FastifyReply extends DecoratedReply {
28
- }
29
- }
30
80
  declare const _default: import("fastify").FastifyPluginCallback<FastifyReplyPluginOptions, import("fastify").RawServerDefault, import("fastify").FastifyTypeProviderDefault, import("fastify").FastifyBaseLogger>;
31
81
  export default _default;
32
82
  export { buildHTTPErrorObject, FastifyZodReplyError } from "./error.js";
package/lib/esm/index.js CHANGED
@@ -13,10 +13,7 @@ const defaultOptions = {
13
13
  notFound: { statusCode: 404, payload: { message: "notFound" } },
14
14
  notAcceptable: { statusCode: 406, payload: { message: "notAcceptable" } },
15
15
  conflict: { statusCode: 409, payload: { message: "conflict" } },
16
- internalServerError: {
17
- statusCode: 500,
18
- payload: { message: "internalServerError" },
19
- },
16
+ internalServerError: { statusCode: 500, payload: { message: "internalServerError" } },
20
17
  },
21
18
  };
22
19
  export default fp(async (fastify, opts) => {
@@ -15,3 +15,14 @@ export declare const strictifySchema: (schema: z.ZodType, strict: boolean) => an
15
15
  export declare const parseStrict: (tag: keyof Exclude<NonNullable<RouteV4Options["strict"]>, boolean>, value: NonNullable<RouteV4Options["strict"]>) => boolean;
16
16
  export declare const findStatusCode: (statusCode: number, availableStatusCodes: [string | number, any][]) => [string | number, any] | undefined;
17
17
  export declare const mapZodError: (zodError: z.ZodError, prefix: string) => string;
18
+ export declare const buildZodJsonSchemaParams: (io: "input" | "output") => {
19
+ io: "input" | "output";
20
+ reused: "inline";
21
+ target: "draft-7";
22
+ unrepresentable: "any";
23
+ override(ctx: {
24
+ zodSchema: z.core.$ZodTypes;
25
+ jsonSchema: z.core.JSONSchema.BaseSchema;
26
+ path: (string | number)[];
27
+ }): void;
28
+ };
@@ -5,9 +5,7 @@ export const parse = async (schema, payload, tag) => {
5
5
  export const strictifySchema = (schema, strict) => {
6
6
  if (!strict)
7
7
  return schema;
8
- return "strict" in schema && typeof schema["strict"] === "function"
9
- ? schema.strict()
10
- : schema;
8
+ return "strict" in schema && typeof schema["strict"] === "function" ? schema.strict() : schema;
11
9
  };
12
10
  export const parseStrict = (tag, value) => {
13
11
  if (typeof value === "boolean")
@@ -34,3 +32,17 @@ export const mapZodError = (zodError, prefix) => {
34
32
  })
35
33
  .join("\n");
36
34
  };
35
+ export const buildZodJsonSchemaParams = (io) => {
36
+ return {
37
+ io,
38
+ reused: "inline",
39
+ target: "draft-7",
40
+ unrepresentable: "any",
41
+ override(ctx) {
42
+ const def = ctx.zodSchema._zod.def;
43
+ if (def.type === "date") {
44
+ ctx.jsonSchema.type = "string";
45
+ }
46
+ },
47
+ };
48
+ };
@@ -14,11 +14,16 @@ export type BaseZodV4Schema = {
14
14
  Summary?: string;
15
15
  Notes?: string;
16
16
  };
17
+ type ErrorReplyFallback<TReply> = {
18
+ [K in Exclude<400 | 401 | 403 | 404 | 406 | 409 | 500, keyof TReply>]?: {
19
+ message: string;
20
+ };
21
+ };
17
22
  export type FastifyZodV4Schema<TZodSchema extends BaseZodV4Schema> = {
18
23
  Body: TZodSchema["Body"] extends z.ZodTypeAny ? z.output<TZodSchema["Body"]> : undefined;
19
24
  Params: TZodSchema["Params"] extends z.ZodTypeAny ? z.output<TZodSchema["Params"]> : undefined;
20
25
  Querystring: TZodSchema["Query"] extends z.ZodTypeAny ? z.output<TZodSchema["Query"]> : undefined;
21
- Reply: TZodSchema["Reply"] extends z.ZodTypeAny ? z.input<TZodSchema["Reply"]>[keyof z.input<TZodSchema["Reply"]>] : undefined;
26
+ Reply: TZodSchema["Reply"] extends z.ZodTypeAny ? z.input<TZodSchema["Reply"]> & ErrorReplyFallback<z.input<TZodSchema["Reply"]>> : undefined;
22
27
  };
23
28
  export type RouteV4Options = {
24
29
  strict?: boolean | {
@@ -28,9 +33,10 @@ export type RouteV4Options = {
28
33
  headers: boolean;
29
34
  };
30
35
  };
31
- export declare const createRouteV4: <RequestAugmentation extends object = {}, ReplyAugmentation extends object = {}>({ strict: globalStrict }?: RouteV4Options) => <TSchema extends BaseZodV4Schema, FastifySchema extends FastifyZodV4Schema<TSchema> = FastifyZodV4Schema<TSchema>>(schema: TSchema, handler: NoInfer<APIHandler<FastifySchema, RequestAugmentation, ReplyAugmentation>>, options?: RouteV4Options) => APIOptions<FastifySchema> & {
36
+ export declare const createRouteV4: <RequestAugmentation extends object = {}, ReplyAugmentation extends object = {}>({ strict: globalStrict, }?: RouteV4Options) => <TSchema extends BaseZodV4Schema, FastifySchema extends FastifyZodV4Schema<TSchema> = FastifyZodV4Schema<TSchema>>(schema: TSchema, handler: NoInfer<APIHandler<FastifySchema, RequestAugmentation, ReplyAugmentation>>, options?: RouteV4Options) => APIOptions<FastifySchema> & {
32
37
  handler: APIHandler<FastifySchema>;
33
38
  };
34
39
  export declare const routeV4: <TSchema extends BaseZodV4Schema, FastifySchema extends FastifyZodV4Schema<TSchema> = FastifyZodV4Schema<TSchema>>(schema: TSchema, handler: NoInfer<APIHandler<FastifySchema, {}, {}>>, options?: RouteV4Options) => APIOptions<FastifySchema> & {
35
40
  handler: APIHandler<FastifySchema>;
36
41
  };
42
+ export {};
@@ -1,20 +1,17 @@
1
1
  import { z } from "zod/v4";
2
2
  import { FastifyZodReplyError } from "./error.js";
3
- import { findStatusCode, mapZodError, parse, parseStrict, strictifySchema, } from "./routeHelpers.js";
4
- export const createRouteV4 = ({ strict: globalStrict = false } = {}) => (schema, handler, options) => {
3
+ import { buildZodJsonSchemaParams, findStatusCode, mapZodError, parse, parseStrict, strictifySchema } from "./routeHelpers.js";
4
+ export const createRouteV4 = ({ strict: globalStrict = false, } = {}) => (schema, handler, options) => {
5
5
  const strict = typeof (options === null || options === void 0 ? void 0 : options.strict) !== "undefined" ? options === null || options === void 0 ? void 0 : options.strict : globalStrict;
6
6
  const finalResult = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (schema.Body && {
7
- body: z.toJSONSchema(strictifySchema(schema.Body, parseStrict("body", strict)), { reused: "inline", target: "draft-7", io: "input" }),
7
+ body: z.toJSONSchema(strictifySchema(schema.Body, parseStrict("body", strict)), buildZodJsonSchemaParams('input')),
8
8
  })), (schema.Params && {
9
- params: z.toJSONSchema(strictifySchema(schema.Params, parseStrict("params", strict)), { reused: "inline", target: "draft-7", io: "input" }),
9
+ params: z.toJSONSchema(strictifySchema(schema.Params, parseStrict("params", strict)), buildZodJsonSchemaParams('input')),
10
10
  })), (schema.Query && {
11
- querystring: z.toJSONSchema(strictifySchema(schema.Query, parseStrict("query", strict)), { reused: "inline", target: "draft-7", io: "input" }),
11
+ querystring: z.toJSONSchema(strictifySchema(schema.Query, parseStrict("query", strict)), buildZodJsonSchemaParams('input')),
12
12
  })), (schema.Headers && {
13
- headers: z.toJSONSchema(strictifySchema(schema.Headers, parseStrict("headers", strict)), { reused: "inline", target: "draft-7", io: "input" }),
14
- })), { response: z.toJSONSchema(schema.Reply.partial(), {
15
- reused: "inline",
16
- target: "draft-7",
17
- })["properties"] }), (schema.Security && { security: schema.Security })), (schema.Tags && { tags: schema.Tags })), (schema.Description && { description: schema.Description })), (schema.Summary && { summary: schema.Summary })), (schema.Notes && { notes: schema.Notes }));
13
+ headers: z.toJSONSchema(strictifySchema(schema.Headers, parseStrict("headers", strict)), buildZodJsonSchemaParams('input')),
14
+ })), { response: z.toJSONSchema(schema.Reply.partial(), buildZodJsonSchemaParams('output'))["properties"] }), (schema.Security && { security: schema.Security })), (schema.Tags && { tags: schema.Tags })), (schema.Description && { description: schema.Description })), (schema.Summary && { summary: schema.Summary })), (schema.Notes && { notes: schema.Notes }));
18
15
  return {
19
16
  schema: finalResult,
20
17
  handler,
@@ -22,12 +19,8 @@ export const createRouteV4 = ({ strict: globalStrict = false } = {}) => (schema,
22
19
  var _a, _b, _c;
23
20
  const results = await Promise.all([
24
21
  ...(schema.Body ? [parse(schema.Body, request.body, "body")] : []),
25
- ...(schema.Params
26
- ? [parse(schema.Params, request.params, "params")]
27
- : []),
28
- ...(schema.Query
29
- ? [parse(schema.Query, request.query, "query")]
30
- : []),
22
+ ...(schema.Params ? [parse(schema.Params, request.params, "params")] : []),
23
+ ...(schema.Query ? [parse(schema.Query, request.query, "query")] : []),
31
24
  ]);
32
25
  for (const result of results) {
33
26
  if (!result.success) {
@@ -39,12 +32,9 @@ export const createRouteV4 = ({ strict: globalStrict = false } = {}) => (schema,
39
32
  });
40
33
  }
41
34
  }
42
- request.body =
43
- ((_a = results.find((r) => r.tag === "body")) === null || _a === void 0 ? void 0 : _a.data) || {};
44
- request.params =
45
- ((_b = results.find((r) => r.tag === "params")) === null || _b === void 0 ? void 0 : _b.data) || {};
46
- request.query =
47
- ((_c = results.find((r) => r.tag === "query")) === null || _c === void 0 ? void 0 : _c.data) || {};
35
+ request.body = ((_a = results.find((r) => r.tag === "body")) === null || _a === void 0 ? void 0 : _a.data) || {};
36
+ request.params = ((_b = results.find((r) => r.tag === "params")) === null || _b === void 0 ? void 0 : _b.data) || {};
37
+ request.query = ((_c = results.find((r) => r.tag === "query")) === null || _c === void 0 ? void 0 : _c.data) || {};
48
38
  },
49
39
  preSerialization: (request, reply, payload, done) => {
50
40
  const foundSchema = findStatusCode(reply.statusCode, Object.entries(schema.Reply.shape));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@efebia/fastify-zod-reply",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "license": "MIT",
5
5
  "dependencies": {
6
6
  "fastify": "^5.3.0",
@@ -14,7 +14,8 @@
14
14
  "build:esm": "tsc -p ./tsconfig.build.json --module nodenext --moduleResolution nodenext --outDir lib/esm",
15
15
  "build:cjs": "tsc -p ./tsconfig.build.json --module commonjs --moduleResolution node --outDir lib/cjs",
16
16
  "build": "yarn build:esm && yarn build:cjs",
17
- "test": "node --import tsx --test ./**/*.test.ts"
17
+ "test": "node --import tsx --test ./**/*.test.ts",
18
+ "test:only": "node --import tsx --test-only ./**/*.test.ts"
18
19
  },
19
20
  "main": "./lib/esm/index.js",
20
21
  "types": "./lib/esm/index.d.ts",