zod-nest 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,6 +24,7 @@ For the long-form motivation, see [`docs/why-this-exists.md`](docs/why-this-exis
24
24
  - **`createZodDto`** — class wrapper around a Zod schema with introspectable `schema`, `id`, `io`, and a sibling `Output` class when input/output diverge.
25
25
  - **`ZodValidationPipe`** — auto-detects DTO from handler-arg metatype, accepts an explicit DTO or raw Zod schema, customizable exception factory.
26
26
  - **`@ZodResponse`** — stackable per status code, accepts a single DTO, an array (`[Dto]`), or a tuple (`[A, B, …]`). No internal `@HttpCode` — caller controls status.
27
+ - **`@ZodBody` / `@ZodQuery` / `@ZodHeaders` / `@ZodCookies`** — method-level decorators that wire OpenAPI docs for schemas whose `z.infer<>` is a union (intersection-of-union, discriminated unions, etc.) — the ones that can't be wrapped in `createZodDto` because TS refuses unions as class bases (TS2509). Schema validation stays via `@Body(new ZodValidationPipe(schema))`; the type at the handler arg stays `z.infer<>`.
27
28
  - **`ZodSerializerInterceptor`** — response validation with a `passthroughOnError` escape hatch for untrusted upstream shapes.
28
29
  - **`applyZodNest`** — one call after `SwaggerModule.createDocument(...)` replaces the entire `cleanupOpenApiDoc` ritual.
29
30
  - **`ZodNestModule.forRoot`** — global pipe + interceptor + logging configuration in one place; optional (everything works standalone).
@@ -149,6 +150,37 @@ The class returned by `createZodDto(schema)` carries `schema`, `id`, `io: 'input
149
150
 
150
151
  See [`docs/dto.md`](docs/dto.md) for the full surface.
151
152
 
153
+ ### Schemas that don't fit a class
154
+
155
+ A schema whose `z.infer<>` is a TypeScript union — `z.union`, `z.discriminatedUnion`, or `z.intersection(obj, union)` — can't be used as a class base because TS rejects unions as constructor return types (TS2509: *Base constructor return type ... is not an object type*). For these, skip `createZodDto` and pair the raw schema with parameter-level decorators that handle OpenAPI emission directly:
156
+
157
+ ```ts
158
+ const IntersectionWithUnion = z
159
+ .intersection(
160
+ z.union([z.object({ a: z.string() }), z.object({ b: z.string() })]),
161
+ z.union([z.object({ c: z.string() }), z.object({ d: z.string() })]),
162
+ )
163
+ .meta({ id: 'IntersectionWithUnion' });
164
+
165
+ type IntersectionWithUnionType = z.infer<typeof IntersectionWithUnion>;
166
+
167
+ @Controller()
168
+ export class Controller {
169
+ @Post()
170
+ @ZodBody(IntersectionWithUnion)
171
+ async post(
172
+ @Body(new ZodValidationPipe(IntersectionWithUnion))
173
+ body: IntersectionWithUnionType,
174
+ ): Promise<IntersectionWithUnionType> {
175
+ return body;
176
+ }
177
+ }
178
+ ```
179
+
180
+ The decorator set: `@ZodBody`, `@ZodQuery`, `@ZodHeaders`, `@ZodCookies`. All are method-level. They register the schema in the registry (so it lands in `components.schemas` when named) and apply the matching OpenAPI parameter metadata — `@ZodBody` writes the request body's `$ref`/inline schema; `@ZodQuery` / `@ZodHeaders` / `@ZodCookies` expand a `z.object` into one OpenAPI parameter per property. Validation stays manual via `@Body(new ZodValidationPipe(schema))` so the handler arg keeps its precise `z.infer<>` type.
181
+
182
+ See [`docs/recipes/intersection-with-union.md`](docs/recipes/intersection-with-union.md) for the full pattern.
183
+
152
184
  ### I/O suffix rules
153
185
 
154
186
  If a schema's input and output JSON Schemas are byte-equal (the common case), the OpenAPI doc emits a single `components.schemas[Id]`. If they differ (e.g. a `transform`, a `pipe`, an `.optional().default(x)` field), the doc emits two: `Id` for input, `IdOutput` for output. Response refs are rewritten to `IdOutput` automatically.
@@ -424,6 +456,9 @@ A compact, link-out index. Type signatures and detailed semantics live in the co
424
456
  **Response** — [`docs/responses.md`](docs/responses.md)
425
457
  - `@ZodResponse({ status?, type, description?, passthroughOnError? })`, `ZodSerializerInterceptor`, `ZodSerializationException`, `defaultStatusFor`, `resolveEffectiveStatus`, `ResponseStatusInput`, `ResponseStatusWildcard`, `ResponseVariant`, `ZOD_RESPONSES_METADATA_KEY`
426
458
 
459
+ **Parameter decorators for raw schemas** — [`docs/recipes/intersection-with-union.md`](docs/recipes/intersection-with-union.md)
460
+ - `@ZodBody(schema, options?)`, `@ZodQuery(schema, options?)`, `@ZodHeaders(schema, options?)`, `@ZodCookies(schema, options?)`, `ZodBodyOptions`, `ZodQueryOptions`, `ZodHeadersOptions`, `ZodCookiesOptions`
461
+
427
462
  **Document** — [`docs/swagger-integration.md`](docs/swagger-integration.md)
428
463
  - `applyZodNest(rawDoc, options)`, `ApplyZodNestOptions`, `ZodNestDocumentError`
429
464
 
package/dist/index.d.mts CHANGED
@@ -487,6 +487,92 @@ interface ZodResponseOptions {
487
487
  */
488
488
  declare const ZodResponse: (opts: ZodResponseOptions) => MethodDecorator;
489
489
 
490
+ interface ZodBodyOptions {
491
+ /** Forces this id, overriding any `.meta({ id })` already on the schema. */
492
+ readonly id?: string;
493
+ /** Registry to register into. Defaults to `defaultRegistry`. */
494
+ readonly registry?: ZodNestRegistry;
495
+ /** OpenAPI `description` for the request body. */
496
+ readonly description?: string;
497
+ /** Whether the body is required. Defaults to `true`. */
498
+ readonly required?: boolean;
499
+ }
500
+ /**
501
+ * Method-level decorator that wires the OpenAPI `requestBody` for a handler
502
+ * whose body schema doesn't fit a `createZodDto` class — typically a
503
+ * `z.intersection` that contains a `z.union`, a `z.discriminatedUnion`, or
504
+ * any schema whose `z.infer<>` is a TypeScript union (which TS refuses as a
505
+ * class base with TS2509).
506
+ *
507
+ * Pair with `@Body(new ZodValidationPipe(schema))` at the parameter to keep
508
+ * the handler arg precisely typed as `z.infer<typeof schema>`:
509
+ *
510
+ * ```ts
511
+ * @Post()
512
+ * @ZodBody(IntersectionWithUnion)
513
+ * async post(
514
+ * @Body(new ZodValidationPipe(IntersectionWithUnion))
515
+ * body: z.infer<typeof IntersectionWithUnion>,
516
+ * ) {}
517
+ * ```
518
+ *
519
+ * The schema is registered in the registry when it has an id (via
520
+ * `.meta({ id })` or `options.id`) so its body is emitted into
521
+ * `components.schemas`. Anonymous schemas are inlined into the operation.
522
+ */
523
+ declare const ZodBody: (schema: z.ZodType, options?: ZodBodyOptions) => MethodDecorator;
524
+
525
+ interface ZodQueryOptions {
526
+ /** Forces this id on the root schema, overriding any `.meta({ id })`. */
527
+ readonly id?: string;
528
+ /** Registry to register into. Defaults to `defaultRegistry`. */
529
+ readonly registry?: ZodNestRegistry;
530
+ }
531
+ /**
532
+ * Method-level decorator that expands a `z.object` schema into one
533
+ * `@ApiQuery` entry per property. Each property's schema is independently
534
+ * resolved — named properties (`.meta({ id })`) become `$ref`s,
535
+ * anonymous properties inline.
536
+ *
537
+ * The user remains responsible for binding the actual query values to the
538
+ * handler, typically via `@Query(new ZodValidationPipe(schema)) q: z.infer<typeof schema>`.
539
+ */
540
+ declare const ZodQuery: (schema: z.ZodType, options?: ZodQueryOptions) => MethodDecorator;
541
+
542
+ interface ZodHeadersOptions {
543
+ /** Forces this id on the root schema, overriding any `.meta({ id })`. */
544
+ readonly id?: string;
545
+ /** Registry to register into. Defaults to `defaultRegistry`. */
546
+ readonly registry?: ZodNestRegistry;
547
+ }
548
+ /**
549
+ * Method-level decorator that expands a `z.object` schema into one
550
+ * header-parameter entry per property. Required-ness derives from each
551
+ * property's Zod optionality.
552
+ *
553
+ * Writes directly to the `swagger/apiParameters` metadata key the explorer
554
+ * reads. We bypass `@ApiHeader` because it injects a default `type: 'string'`
555
+ * onto the schema (`api-header.decorator.js`), which pollutes our `$ref`
556
+ * objects and inline JSON Schemas. The raw-metadata path keeps our schema
557
+ * bodies intact.
558
+ */
559
+ declare const ZodHeaders: (schema: z.ZodType, options?: ZodHeadersOptions) => MethodDecorator;
560
+
561
+ interface ZodCookiesOptions {
562
+ /** Forces this id on the root schema, overriding any `.meta({ id })`. */
563
+ readonly id?: string;
564
+ /** Registry to register into. Defaults to `defaultRegistry`. */
565
+ readonly registry?: ZodNestRegistry;
566
+ }
567
+ /**
568
+ * Method-level decorator that expands a `z.object` schema into one
569
+ * cookie-parameter entry per property in the OpenAPI operation. There is no
570
+ * `@ApiCookie` decorator in `@nestjs/swagger`, so we write directly to the
571
+ * `swagger/apiParameters` metadata key the explorer reads — the same key
572
+ * `@ApiQuery` / `@ApiParam` / `@ApiHeader` populate via their helper.
573
+ */
574
+ declare const ZodCookies: (schema: z.ZodType, options?: ZodCookiesOptions) => MethodDecorator;
575
+
490
576
  declare class ZodSerializerInterceptor implements NestInterceptor {
491
577
  private readonly reflector;
492
578
  private readonly logOutputFailure;
@@ -619,4 +705,4 @@ declare class ZodNestDocumentError extends ZodNestError {
619
705
  constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
620
706
  }
621
707
 
622
- export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterSchemaOptions, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, type ZodDto, type ZodDtoMarker, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
708
+ export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterSchemaOptions, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, ZodBody, type ZodBodyOptions, ZodCookies, type ZodCookiesOptions, type ZodDto, type ZodDtoMarker, ZodHeaders, type ZodHeadersOptions, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodQuery, type ZodQueryOptions, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
package/dist/index.d.ts CHANGED
@@ -487,6 +487,92 @@ interface ZodResponseOptions {
487
487
  */
488
488
  declare const ZodResponse: (opts: ZodResponseOptions) => MethodDecorator;
489
489
 
490
+ interface ZodBodyOptions {
491
+ /** Forces this id, overriding any `.meta({ id })` already on the schema. */
492
+ readonly id?: string;
493
+ /** Registry to register into. Defaults to `defaultRegistry`. */
494
+ readonly registry?: ZodNestRegistry;
495
+ /** OpenAPI `description` for the request body. */
496
+ readonly description?: string;
497
+ /** Whether the body is required. Defaults to `true`. */
498
+ readonly required?: boolean;
499
+ }
500
+ /**
501
+ * Method-level decorator that wires the OpenAPI `requestBody` for a handler
502
+ * whose body schema doesn't fit a `createZodDto` class — typically a
503
+ * `z.intersection` that contains a `z.union`, a `z.discriminatedUnion`, or
504
+ * any schema whose `z.infer<>` is a TypeScript union (which TS refuses as a
505
+ * class base with TS2509).
506
+ *
507
+ * Pair with `@Body(new ZodValidationPipe(schema))` at the parameter to keep
508
+ * the handler arg precisely typed as `z.infer<typeof schema>`:
509
+ *
510
+ * ```ts
511
+ * @Post()
512
+ * @ZodBody(IntersectionWithUnion)
513
+ * async post(
514
+ * @Body(new ZodValidationPipe(IntersectionWithUnion))
515
+ * body: z.infer<typeof IntersectionWithUnion>,
516
+ * ) {}
517
+ * ```
518
+ *
519
+ * The schema is registered in the registry when it has an id (via
520
+ * `.meta({ id })` or `options.id`) so its body is emitted into
521
+ * `components.schemas`. Anonymous schemas are inlined into the operation.
522
+ */
523
+ declare const ZodBody: (schema: z.ZodType, options?: ZodBodyOptions) => MethodDecorator;
524
+
525
+ interface ZodQueryOptions {
526
+ /** Forces this id on the root schema, overriding any `.meta({ id })`. */
527
+ readonly id?: string;
528
+ /** Registry to register into. Defaults to `defaultRegistry`. */
529
+ readonly registry?: ZodNestRegistry;
530
+ }
531
+ /**
532
+ * Method-level decorator that expands a `z.object` schema into one
533
+ * `@ApiQuery` entry per property. Each property's schema is independently
534
+ * resolved — named properties (`.meta({ id })`) become `$ref`s,
535
+ * anonymous properties inline.
536
+ *
537
+ * The user remains responsible for binding the actual query values to the
538
+ * handler, typically via `@Query(new ZodValidationPipe(schema)) q: z.infer<typeof schema>`.
539
+ */
540
+ declare const ZodQuery: (schema: z.ZodType, options?: ZodQueryOptions) => MethodDecorator;
541
+
542
+ interface ZodHeadersOptions {
543
+ /** Forces this id on the root schema, overriding any `.meta({ id })`. */
544
+ readonly id?: string;
545
+ /** Registry to register into. Defaults to `defaultRegistry`. */
546
+ readonly registry?: ZodNestRegistry;
547
+ }
548
+ /**
549
+ * Method-level decorator that expands a `z.object` schema into one
550
+ * header-parameter entry per property. Required-ness derives from each
551
+ * property's Zod optionality.
552
+ *
553
+ * Writes directly to the `swagger/apiParameters` metadata key the explorer
554
+ * reads. We bypass `@ApiHeader` because it injects a default `type: 'string'`
555
+ * onto the schema (`api-header.decorator.js`), which pollutes our `$ref`
556
+ * objects and inline JSON Schemas. The raw-metadata path keeps our schema
557
+ * bodies intact.
558
+ */
559
+ declare const ZodHeaders: (schema: z.ZodType, options?: ZodHeadersOptions) => MethodDecorator;
560
+
561
+ interface ZodCookiesOptions {
562
+ /** Forces this id on the root schema, overriding any `.meta({ id })`. */
563
+ readonly id?: string;
564
+ /** Registry to register into. Defaults to `defaultRegistry`. */
565
+ readonly registry?: ZodNestRegistry;
566
+ }
567
+ /**
568
+ * Method-level decorator that expands a `z.object` schema into one
569
+ * cookie-parameter entry per property in the OpenAPI operation. There is no
570
+ * `@ApiCookie` decorator in `@nestjs/swagger`, so we write directly to the
571
+ * `swagger/apiParameters` metadata key the explorer reads — the same key
572
+ * `@ApiQuery` / `@ApiParam` / `@ApiHeader` populate via their helper.
573
+ */
574
+ declare const ZodCookies: (schema: z.ZodType, options?: ZodCookiesOptions) => MethodDecorator;
575
+
490
576
  declare class ZodSerializerInterceptor implements NestInterceptor {
491
577
  private readonly reflector;
492
578
  private readonly logOutputFailure;
@@ -619,4 +705,4 @@ declare class ZodNestDocumentError extends ZodNestError {
619
705
  constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
620
706
  }
621
707
 
622
- export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterSchemaOptions, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, type ZodDto, type ZodDtoMarker, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
708
+ export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterSchemaOptions, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, ZodBody, type ZodBodyOptions, ZodCookies, type ZodCookiesOptions, type ZodDto, type ZodDtoMarker, ZodHeaders, type ZodHeadersOptions, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodQuery, type ZodQueryOptions, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
package/dist/index.js CHANGED
@@ -1129,6 +1129,144 @@ var ZodResponse = /* @__PURE__ */ __name((opts) => {
1129
1129
  });
1130
1130
  };
1131
1131
  }, "ZodResponse");
1132
+
1133
+ // src/decorators/internal/zod-schema-ref.ts
1134
+ var resolveSchemaRef = /* @__PURE__ */ __name((schema, options) => {
1135
+ const registry = options?.registry ?? defaultRegistry;
1136
+ const id = registerSchema(schema, registry, {
1137
+ id: options?.id
1138
+ });
1139
+ if (id !== void 0) {
1140
+ return {
1141
+ kind: "ref",
1142
+ ref: {
1143
+ $ref: `#/components/schemas/${id}`
1144
+ }
1145
+ };
1146
+ }
1147
+ for (const [child, childId] of discoverDependents(schema)) {
1148
+ registry.register(child, childId);
1149
+ }
1150
+ const { schema: body } = toOpenApi(schema, {
1151
+ io: "input",
1152
+ registry
1153
+ });
1154
+ return {
1155
+ kind: "inline",
1156
+ schema: body
1157
+ };
1158
+ }, "resolveSchemaRef");
1159
+
1160
+ // src/decorators/zod-body.decorator.ts
1161
+ var ZodBody = /* @__PURE__ */ __name((schema, options) => {
1162
+ const resolution = resolveSchemaRef(schema, {
1163
+ id: options?.id,
1164
+ registry: options?.registry
1165
+ });
1166
+ const apiBodySchema = resolution.kind === "ref" ? resolution.ref : resolution.schema;
1167
+ return swagger.ApiBody({
1168
+ schema: apiBodySchema,
1169
+ ...options?.description !== void 0 ? {
1170
+ description: options.description
1171
+ } : {},
1172
+ required: options?.required ?? true
1173
+ });
1174
+ }, "ZodBody");
1175
+
1176
+ // src/decorators/internal/zod-param-expand.ts
1177
+ var isZodObject = /* @__PURE__ */ __name((schema) => schema._zod.def.type === "object", "isZodObject");
1178
+ var expandObjectSchema = /* @__PURE__ */ __name((schema, options) => {
1179
+ if (!isZodObject(schema)) {
1180
+ throw new ZodNestError(`${options.decoratorName} requires a \`z.object({...})\` schema (got \`${schema._zod.def.type}\`). Each property of the object becomes one OpenAPI parameter. For non-object body shapes (intersections, unions, primitives), use \`@ZodBody\` instead.`);
1181
+ }
1182
+ const registry = options.registry ?? defaultRegistry;
1183
+ registerSchema(schema, registry, {
1184
+ id: options.id
1185
+ });
1186
+ const result = [];
1187
+ for (const [name, propSchema] of Object.entries(schema.shape)) {
1188
+ const resolution = resolveSchemaRef(propSchema, {
1189
+ registry
1190
+ });
1191
+ const required = options.forceRequired === true || !isOptionalProp(propSchema);
1192
+ result.push({
1193
+ name,
1194
+ required,
1195
+ resolution
1196
+ });
1197
+ }
1198
+ return result;
1199
+ }, "expandObjectSchema");
1200
+ var paramSchemaBody = /* @__PURE__ */ __name((resolution) => resolution.kind === "ref" ? resolution.ref : resolution.schema, "paramSchemaBody");
1201
+
1202
+ // src/decorators/zod-query.decorator.ts
1203
+ var ZodQuery = /* @__PURE__ */ __name((schema, options) => {
1204
+ const expanded = expandObjectSchema(schema, {
1205
+ id: options?.id,
1206
+ registry: options?.registry,
1207
+ decoratorName: "@ZodQuery"
1208
+ });
1209
+ return common.applyDecorators(...expanded.map(({ name, required, resolution }) => swagger.ApiQuery({
1210
+ name,
1211
+ schema: paramSchemaBody(resolution),
1212
+ required
1213
+ })));
1214
+ }, "ZodQuery");
1215
+
1216
+ // src/decorators/internal/constants.ts
1217
+ var API_PARAMETERS_KEY = "swagger/apiParameters";
1218
+
1219
+ // src/decorators/zod-headers.decorator.ts
1220
+ var ZodHeaders = /* @__PURE__ */ __name((schema, options) => {
1221
+ const expanded = expandObjectSchema(schema, {
1222
+ id: options?.id,
1223
+ registry: options?.registry,
1224
+ decoratorName: "@ZodHeaders"
1225
+ });
1226
+ const newEntries = expanded.map(({ name, required, resolution }) => ({
1227
+ name,
1228
+ in: "header",
1229
+ required,
1230
+ schema: paramSchemaBody(resolution)
1231
+ }));
1232
+ return (_target, _propertyKey, descriptor) => {
1233
+ const handler = descriptor.value;
1234
+ if (typeof handler !== "function") {
1235
+ throw new TypeError("[zod-nest] @ZodHeaders can only be applied to methods.");
1236
+ }
1237
+ const existing = Reflect.getMetadata(API_PARAMETERS_KEY, handler) ?? [];
1238
+ Reflect.defineMetadata(API_PARAMETERS_KEY, [
1239
+ ...existing,
1240
+ ...newEntries
1241
+ ], handler);
1242
+ };
1243
+ }, "ZodHeaders");
1244
+
1245
+ // src/decorators/zod-cookies.decorator.ts
1246
+ var ZodCookies = /* @__PURE__ */ __name((schema, options) => {
1247
+ const expanded = expandObjectSchema(schema, {
1248
+ id: options?.id,
1249
+ registry: options?.registry,
1250
+ decoratorName: "@ZodCookies"
1251
+ });
1252
+ const newEntries = expanded.map(({ name, required, resolution }) => ({
1253
+ name,
1254
+ in: "cookie",
1255
+ required,
1256
+ schema: paramSchemaBody(resolution)
1257
+ }));
1258
+ return (_target, _propertyKey, descriptor) => {
1259
+ const handler = descriptor.value;
1260
+ if (typeof handler !== "function") {
1261
+ throw new TypeError("[zod-nest] @ZodCookies can only be applied to methods.");
1262
+ }
1263
+ const existing = Reflect.getMetadata(API_PARAMETERS_KEY, handler) ?? [];
1264
+ Reflect.defineMetadata(API_PARAMETERS_KEY, [
1265
+ ...existing,
1266
+ ...newEntries
1267
+ ], handler);
1268
+ };
1269
+ }, "ZodCookies");
1132
1270
  function _ts_decorate2(decorators, target, key, desc) {
1133
1271
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1134
1272
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1322,9 +1460,10 @@ var forEachOperation = /* @__PURE__ */ __name((doc, fn) => {
1322
1460
 
1323
1461
  // src/document/collect-usage.ts
1324
1462
  var isPlainRecord2 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isPlainRecord");
1325
- var collectUsage = /* @__PURE__ */ __name((doc, app) => {
1463
+ var collectUsage = /* @__PURE__ */ __name((doc, app, registry) => {
1326
1464
  const classToDtoId = buildClassToDtoIdMap(doc);
1327
- const inputExposedIds = collectInputExposedIds(doc, classToDtoId);
1465
+ const knownIds = new Set(registry.ids());
1466
+ const inputExposedIds = collectInputExposedIds(doc, classToDtoId, knownIds);
1328
1467
  const outputExposedIds = collectOutputExposedIds(app);
1329
1468
  return {
1330
1469
  inputExposedIds,
@@ -1359,7 +1498,7 @@ var readMarker = /* @__PURE__ */ __name((schema) => {
1359
1498
  dtoId: marker.dtoId
1360
1499
  };
1361
1500
  }, "readMarker");
1362
- var collectInputExposedIds = /* @__PURE__ */ __name((doc, classToDtoId) => {
1501
+ var collectInputExposedIds = /* @__PURE__ */ __name((doc, classToDtoId, knownIds) => {
1363
1502
  const ids = /* @__PURE__ */ new Set();
1364
1503
  const paths = doc.paths ?? {};
1365
1504
  for (const pathItem of Object.values(paths)) {
@@ -1367,7 +1506,7 @@ var collectInputExposedIds = /* @__PURE__ */ __name((doc, classToDtoId) => {
1367
1506
  continue;
1368
1507
  }
1369
1508
  for (const operation of operationsOf(pathItem)) {
1370
- collectRefsFromOperation(operation, classToDtoId, ids);
1509
+ collectRefsFromOperation(operation, classToDtoId, knownIds, ids);
1371
1510
  }
1372
1511
  }
1373
1512
  return ids;
@@ -1382,9 +1521,9 @@ var operationsOf = /* @__PURE__ */ __name((pathItem) => {
1382
1521
  }
1383
1522
  return out;
1384
1523
  }, "operationsOf");
1385
- var collectRefsFromOperation = /* @__PURE__ */ __name((operation, classToDtoId, ids) => {
1524
+ var collectRefsFromOperation = /* @__PURE__ */ __name((operation, classToDtoId, knownIds, ids) => {
1386
1525
  if (isPlainRecord2(operation.requestBody)) {
1387
- collectRefsFromContent(operation.requestBody.content, classToDtoId, ids);
1526
+ collectRefsFromContent(operation.requestBody.content, classToDtoId, knownIds, ids);
1388
1527
  }
1389
1528
  const parameters = operation.parameters;
1390
1529
  if (Array.isArray(parameters)) {
@@ -1392,7 +1531,7 @@ var collectRefsFromOperation = /* @__PURE__ */ __name((operation, classToDtoId,
1392
1531
  if (!isPlainRecord2(param)) {
1393
1532
  continue;
1394
1533
  }
1395
- collectRefFromSchema(param.schema, classToDtoId, ids);
1534
+ collectRefFromSchema(param.schema, classToDtoId, knownIds, ids);
1396
1535
  collectIdFromMarkerParam(param, ids);
1397
1536
  }
1398
1537
  }
@@ -1407,7 +1546,7 @@ var collectIdFromMarkerParam = /* @__PURE__ */ __name((param, ids) => {
1407
1546
  }
1408
1547
  ids.add(dtoId);
1409
1548
  }, "collectIdFromMarkerParam");
1410
- var collectRefsFromContent = /* @__PURE__ */ __name((content, classToDtoId, ids) => {
1549
+ var collectRefsFromContent = /* @__PURE__ */ __name((content, classToDtoId, knownIds, ids) => {
1411
1550
  if (!isPlainRecord2(content)) {
1412
1551
  return;
1413
1552
  }
@@ -1415,10 +1554,10 @@ var collectRefsFromContent = /* @__PURE__ */ __name((content, classToDtoId, ids)
1415
1554
  if (!isPlainRecord2(mediaType)) {
1416
1555
  continue;
1417
1556
  }
1418
- collectRefFromSchema(mediaType.schema, classToDtoId, ids);
1557
+ collectRefFromSchema(mediaType.schema, classToDtoId, knownIds, ids);
1419
1558
  }
1420
1559
  }, "collectRefsFromContent");
1421
- var collectRefFromSchema = /* @__PURE__ */ __name((schema, classToDtoId, ids) => {
1560
+ var collectRefFromSchema = /* @__PURE__ */ __name((schema, classToDtoId, knownIds, ids) => {
1422
1561
  if (!isPlainRecord2(schema)) {
1423
1562
  return;
1424
1563
  }
@@ -1427,9 +1566,13 @@ var collectRefFromSchema = /* @__PURE__ */ __name((schema, classToDtoId, ids) =>
1427
1566
  return;
1428
1567
  }
1429
1568
  const className = ref.slice(COMPONENTS_SCHEMAS_PREFIX.length);
1430
- const dtoId = classToDtoId.get(className);
1431
- if (dtoId !== void 0) {
1432
- ids.add(dtoId);
1569
+ const renamed = classToDtoId.get(className);
1570
+ if (renamed !== void 0) {
1571
+ ids.add(renamed);
1572
+ return;
1573
+ }
1574
+ if (knownIds.has(className)) {
1575
+ ids.add(className);
1433
1576
  }
1434
1577
  }, "collectRefFromSchema");
1435
1578
  var collectOutputExposedIds = /* @__PURE__ */ __name((app) => {
@@ -1913,7 +2056,7 @@ var stripMarkerFromSchema = /* @__PURE__ */ __name((schema) => {
1913
2056
  var OPENAPI_VERSION = "3.1.0";
1914
2057
  var applyZodNest = /* @__PURE__ */ __name((doc, opts) => {
1915
2058
  const registry = opts.registry ?? defaultRegistry;
1916
- const collected = collectUsage(doc, opts.app);
2059
+ const collected = collectUsage(doc, opts.app, registry);
1917
2060
  const { inputSchemas, outputSchemas } = bulkEmit({
1918
2061
  registry,
1919
2062
  override: opts.override,
@@ -1955,10 +2098,14 @@ exports.ZOD_NEST_ERROR_DUPLICATE_ID = ZOD_NEST_ERROR_DUPLICATE_ID;
1955
2098
  exports.ZOD_NEST_ERROR_EXTENSION = ZOD_NEST_ERROR_EXTENSION;
1956
2099
  exports.ZOD_NEST_OPTIONS = ZOD_NEST_OPTIONS;
1957
2100
  exports.ZOD_RESPONSES_METADATA_KEY = ZOD_RESPONSES_METADATA_KEY;
2101
+ exports.ZodBody = ZodBody;
2102
+ exports.ZodCookies = ZodCookies;
2103
+ exports.ZodHeaders = ZodHeaders;
1958
2104
  exports.ZodNestDocumentError = ZodNestDocumentError;
1959
2105
  exports.ZodNestError = ZodNestError;
1960
2106
  exports.ZodNestModule = ZodNestModule;
1961
2107
  exports.ZodNestUnrepresentableError = ZodNestUnrepresentableError;
2108
+ exports.ZodQuery = ZodQuery;
1962
2109
  exports.ZodResponse = ZodResponse;
1963
2110
  exports.ZodSerializationException = ZodSerializationException;
1964
2111
  exports.ZodValidationException = ZodValidationException;