zod-nest 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -446,13 +446,26 @@ interface ZodResponseOptions {
446
446
  passthroughOnError?: boolean;
447
447
  }
448
448
  /**
449
- * Method-only decorator. Declares a typed response variant for the handler.
450
- * Stack multiple decorations to declare per-status types; lookup at runtime
451
- * is by `ZodSerializerInterceptor`'s two-pass matcher (exact numeric, then
452
- * `'NXX'` wildcard).
449
+ * Method-only decorator. Declares a typed response variant for the handler
450
+ * AND applies the equivalent `@ApiResponse(...)` from `@nestjs/swagger` so
451
+ * the OpenAPI document carries the response shape — no need for consumers
452
+ * to hand-write `@ApiResponse` alongside `@ZodResponse`.
453
453
  *
454
- * The wrapped Zod schema (array / tuple) is built once at decoration time
455
- * and stored on the variant record no per-request schema construction.
454
+ * Stack multiple decorations to declare per-status types; runtime lookup
455
+ * is by `ZodSerializerInterceptor`'s two-pass matcher (exact numeric, then
456
+ * `'NXX'` wildcard). The wrapped Zod schema (array / tuple) is built once
457
+ * at decoration time — no per-request schema construction.
458
+ *
459
+ * **Decorator-ordering note (implicit `status` only).** TypeScript decorators
460
+ * apply bottom-up, so `@ZodResponse` (typically written above `@Get` /
461
+ * `@HttpCode`) executes its factory body *first* — before sibling
462
+ * decorators have written their `HTTP_CODE_METADATA` / `METHOD_METADATA`.
463
+ * When `opts.status` is explicit, the `@ApiResponse(...)` call is applied
464
+ * synchronously — there's nothing to wait for. When `opts.status` is
465
+ * implicit (resolves to `@HttpCode` or the HTTP-method default), the
466
+ * `@ApiResponse(...)` call is deferred via `queueMicrotask` so the sibling
467
+ * metadata has settled by the time we read it. See `docs/responses.md →
468
+ * "Decorator ordering & the microtask trick"`.
456
469
  */
457
470
  declare const ZodResponse: (opts: ZodResponseOptions) => MethodDecorator;
458
471
 
package/dist/index.d.ts CHANGED
@@ -446,13 +446,26 @@ interface ZodResponseOptions {
446
446
  passthroughOnError?: boolean;
447
447
  }
448
448
  /**
449
- * Method-only decorator. Declares a typed response variant for the handler.
450
- * Stack multiple decorations to declare per-status types; lookup at runtime
451
- * is by `ZodSerializerInterceptor`'s two-pass matcher (exact numeric, then
452
- * `'NXX'` wildcard).
449
+ * Method-only decorator. Declares a typed response variant for the handler
450
+ * AND applies the equivalent `@ApiResponse(...)` from `@nestjs/swagger` so
451
+ * the OpenAPI document carries the response shape — no need for consumers
452
+ * to hand-write `@ApiResponse` alongside `@ZodResponse`.
453
453
  *
454
- * The wrapped Zod schema (array / tuple) is built once at decoration time
455
- * and stored on the variant record no per-request schema construction.
454
+ * Stack multiple decorations to declare per-status types; runtime lookup
455
+ * is by `ZodSerializerInterceptor`'s two-pass matcher (exact numeric, then
456
+ * `'NXX'` wildcard). The wrapped Zod schema (array / tuple) is built once
457
+ * at decoration time — no per-request schema construction.
458
+ *
459
+ * **Decorator-ordering note (implicit `status` only).** TypeScript decorators
460
+ * apply bottom-up, so `@ZodResponse` (typically written above `@Get` /
461
+ * `@HttpCode`) executes its factory body *first* — before sibling
462
+ * decorators have written their `HTTP_CODE_METADATA` / `METHOD_METADATA`.
463
+ * When `opts.status` is explicit, the `@ApiResponse(...)` call is applied
464
+ * synchronously — there's nothing to wait for. When `opts.status` is
465
+ * implicit (resolves to `@HttpCode` or the HTTP-method default), the
466
+ * `@ApiResponse(...)` call is deferred via `queueMicrotask` so the sibling
467
+ * metadata has settled by the time we read it. See `docs/responses.md →
468
+ * "Decorator ordering & the microtask trick"`.
456
469
  */
457
470
  declare const ZodResponse: (opts: ZodResponseOptions) => MethodDecorator;
458
471
 
package/dist/index.js CHANGED
@@ -2,10 +2,11 @@
2
2
 
3
3
  var zod = require('zod');
4
4
  var common = require('@nestjs/common');
5
+ var constants_js = require('@nestjs/common/constants.js');
6
+ var swagger = require('@nestjs/swagger');
5
7
  var core = require('@nestjs/core');
6
8
  var rxjs = require('rxjs');
7
9
  var operators = require('rxjs/operators');
8
- var constants_js = require('@nestjs/common/constants.js');
9
10
  var stringify = require('fast-json-stable-stringify');
10
11
 
11
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -924,6 +925,25 @@ exports.ZodValidationPipe = _ts_decorate([
924
925
  typeof NormalizedZodNestOptions === "undefined" ? Object : NormalizedZodNestOptions
925
926
  ])
926
927
  ], exports.ZodValidationPipe);
928
+ var POST_DEFAULT_STATUS = 201;
929
+ var GENERIC_DEFAULT_STATUS = 200;
930
+ var defaultStatusFor = /* @__PURE__ */ __name((handler) => {
931
+ const httpCode = Reflect.getMetadata(constants_js.HTTP_CODE_METADATA, handler);
932
+ if (typeof httpCode === "number") {
933
+ return httpCode;
934
+ }
935
+ const method = Reflect.getMetadata(constants_js.METHOD_METADATA, handler);
936
+ if (method === common.RequestMethod.POST) {
937
+ return POST_DEFAULT_STATUS;
938
+ }
939
+ return GENERIC_DEFAULT_STATUS;
940
+ }, "defaultStatusFor");
941
+ var resolveEffectiveStatus = /* @__PURE__ */ __name((variant, handler) => {
942
+ if (variant.status !== void 0) {
943
+ return variant.status;
944
+ }
945
+ return defaultStatusFor(handler);
946
+ }, "resolveEffectiveStatus");
927
947
 
928
948
  // src/response/metadata.ts
929
949
  var ZOD_RESPONSES_METADATA_KEY = /* @__PURE__ */ Symbol.for("zod-nest.responses");
@@ -938,6 +958,67 @@ var appendResponseVariant = /* @__PURE__ */ __name((handler, variant) => {
938
958
  ...existing
939
959
  ], handler);
940
960
  }, "appendResponseVariant");
961
+ var extractDescriptionFields = /* @__PURE__ */ __name((desc) => {
962
+ if (desc === void 0) {
963
+ return {};
964
+ }
965
+ if (typeof desc === "string") {
966
+ return {
967
+ description: desc
968
+ };
969
+ }
970
+ const out = {
971
+ description: desc.description
972
+ };
973
+ if (desc.headers !== void 0) {
974
+ out.headers = desc.headers;
975
+ }
976
+ if (desc.links !== void 0) {
977
+ out.links = desc.links;
978
+ }
979
+ return out;
980
+ }, "extractDescriptionFields");
981
+ var asDtoFunction = /* @__PURE__ */ __name((dto) => dto, "asDtoFunction");
982
+ var buildApiResponseOptions = /* @__PURE__ */ __name((base, body) => {
983
+ return {
984
+ ...base,
985
+ ...body
986
+ };
987
+ }, "buildApiResponseOptions");
988
+ var applySwaggerResponseDecorator = /* @__PURE__ */ __name((variant, effectiveStatus, target, propertyKey, descriptor) => {
989
+ const base = {
990
+ status: effectiveStatus,
991
+ ...extractDescriptionFields(variant.description)
992
+ };
993
+ if (variant.kind === "single") {
994
+ const dto = variant.dto;
995
+ swagger.ApiResponse(buildApiResponseOptions(base, {
996
+ type: asDtoFunction(dto)
997
+ }))(target, propertyKey, descriptor);
998
+ return;
999
+ }
1000
+ if (variant.kind === "array") {
1001
+ const dtos2 = variant.dto;
1002
+ const dto = dtos2[0];
1003
+ swagger.ApiResponse(buildApiResponseOptions(base, {
1004
+ type: asDtoFunction(dto),
1005
+ isArray: true
1006
+ }))(target, propertyKey, descriptor);
1007
+ return;
1008
+ }
1009
+ const dtos = variant.dto;
1010
+ swagger.ApiExtraModels(...dtos.map(asDtoFunction))(target, propertyKey, descriptor);
1011
+ const schema = {
1012
+ type: "array",
1013
+ prefixItems: dtos.map((d) => ({
1014
+ $ref: swagger.getSchemaPath(asDtoFunction(d))
1015
+ })),
1016
+ items: false
1017
+ };
1018
+ swagger.ApiResponse(buildApiResponseOptions(base, {
1019
+ schema
1020
+ }))(target, propertyKey, descriptor);
1021
+ }, "applySwaggerResponseDecorator");
941
1022
 
942
1023
  // src/decorators/zod-response.decorator.ts
943
1024
  var buildArrayKind = /* @__PURE__ */ __name((dtos) => {
@@ -991,7 +1072,8 @@ var normaliseStatus = /* @__PURE__ */ __name((status) => {
991
1072
  var ZodResponse = /* @__PURE__ */ __name((opts) => {
992
1073
  const built = buildKind(opts.type);
993
1074
  const status = normaliseStatus(opts.status);
994
- return (_target, _propertyKey, descriptor) => {
1075
+ const statusExplicit = status !== void 0;
1076
+ return (target, propertyKey, descriptor) => {
995
1077
  const handler = descriptor.value;
996
1078
  if (typeof handler !== "function") {
997
1079
  throw new TypeError("[zod-nest] @ZodResponse can only be applied to methods.");
@@ -1005,29 +1087,17 @@ var ZodResponse = /* @__PURE__ */ __name((opts) => {
1005
1087
  passthroughOnError: opts.passthroughOnError ?? false
1006
1088
  };
1007
1089
  appendResponseVariant(handler, variant);
1090
+ const swaggerDescriptor = descriptor;
1091
+ if (statusExplicit) {
1092
+ applySwaggerResponseDecorator(variant, status, target, propertyKey, swaggerDescriptor);
1093
+ return;
1094
+ }
1095
+ queueMicrotask(() => {
1096
+ const effective = resolveEffectiveStatus(variant, handler);
1097
+ applySwaggerResponseDecorator(variant, effective, target, propertyKey, swaggerDescriptor);
1098
+ });
1008
1099
  };
1009
1100
  }, "ZodResponse");
1010
- var POST_DEFAULT_STATUS = 201;
1011
- var GENERIC_DEFAULT_STATUS = 200;
1012
- var defaultStatusFor = /* @__PURE__ */ __name((handler) => {
1013
- const httpCode = Reflect.getMetadata(constants_js.HTTP_CODE_METADATA, handler);
1014
- if (typeof httpCode === "number") {
1015
- return httpCode;
1016
- }
1017
- const method = Reflect.getMetadata(constants_js.METHOD_METADATA, handler);
1018
- if (method === common.RequestMethod.POST) {
1019
- return POST_DEFAULT_STATUS;
1020
- }
1021
- return GENERIC_DEFAULT_STATUS;
1022
- }, "defaultStatusFor");
1023
- var resolveEffectiveStatus = /* @__PURE__ */ __name((variant, handler) => {
1024
- if (variant.status !== void 0) {
1025
- return variant.status;
1026
- }
1027
- return defaultStatusFor(handler);
1028
- }, "resolveEffectiveStatus");
1029
-
1030
- // src/interceptors/serializer.interceptor.ts
1031
1101
  function _ts_decorate2(decorators, target, key, desc) {
1032
1102
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1033
1103
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);