zod-nest 0.11.0 → 0.13.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
@@ -39,7 +39,7 @@ For the long-form motivation, see [`docs/why-this-exists.md`](docs/why-this-exis
39
39
  A short list of behavioural differences you'll hit on day one. Full migration table is in [`MIGRATION.md`](MIGRATION.md).
40
40
 
41
41
  - **Multi-status `@ZodResponse`** — stack the decorator per status code. In `nestjs-zod`, multi-status required mixing `@ZodSerializerDto` with hand-rolled `@ApiResponse({ status: ... })` calls.
42
- - **No internal `@HttpCode`** — `@ZodResponse` does **not** call `@HttpCode` under the hood. Status resolution precedence: `@ZodResponse({ status })` → `@HttpCode(...)` on the handler → method default (`POST → 201`, others → `200`). The caller controls `201` vs `200` vs `204` via standard NestJS decorators.
42
+ - **No internal `@HttpCode`** — `@ZodResponse` does **not** call `@HttpCode` under the hood. Status resolution precedence: `@ZodResponse({ status })` → `@HttpCode(...)` on the handler → method default (`POST → 201`, others → `200`). The caller controls `201` vs `200` vs `204` via standard NestJS decorators. `status` accepts numeric codes plus the OpenAPI 3.1 range keys (`'1XX'`…`'5XX'`) and `'default'` (sugar for the resolved method default).
43
43
  - **I/O suffix only when needed** — `<Id>Output` is only emitted when the input and output JSON Schemas actually differ. `nestjs-zod` always emitted `_Output`.
44
44
  - **OpenAPI 3.1 only** — no `3.0` fallback. `$ref`s emit to the final location; `cleanupOpenApiDoc` is unnecessary.
45
45
  - **Validation-failure logging out of the box** — `nestjs-zod` has none.
@@ -52,7 +52,6 @@ A short list of behavioural differences you'll hit on day one. Full migration ta
52
52
 
53
53
  - **Zod v3 support** — Zod v4 only. Migrate first.
54
54
  - **`class-validator` / `class-transformer` coexistence** — `zod-nest` is Zod-native. Mixing class-validator decorators on a `createZodDto` result is not supported.
55
- - **Status wildcards** in `@ZodResponse` (`'2XX'`, `'default'`) — deferred to v1.
56
55
  - **Hybrid DTO projects** — mixing `createZodDto` DTOs with plain `@ApiProperty` classes in the same controller is not tested.
57
56
  - **Non-HTTP contexts** — WebSocket gateways, GraphQL resolvers, microservice handlers are out of scope.
58
57
 
@@ -423,7 +422,7 @@ A compact, link-out index. Type signatures and detailed semantics live in the co
423
422
  - `ZodValidationPipe`, `ZodValidationException`, `ZodValidationPipeOptions`, `CreateValidationException`
424
423
 
425
424
  **Response** — [`docs/responses.md`](docs/responses.md)
426
- - `@ZodResponse({ status?, type, description?, passthroughOnError? })`, `ZodSerializerInterceptor`, `ZodSerializationException`, `defaultStatusFor`, `resolveEffectiveStatus`, `ResponseVariant`, `ZOD_RESPONSES_METADATA_KEY`
425
+ - `@ZodResponse({ status?, type, description?, passthroughOnError? })`, `ZodSerializerInterceptor`, `ZodSerializationException`, `defaultStatusFor`, `resolveEffectiveStatus`, `ResponseStatusInput`, `ResponseStatusWildcard`, `ResponseVariant`, `ZOD_RESPONSES_METADATA_KEY`
427
426
 
428
427
  **Document** — [`docs/swagger-integration.md`](docs/swagger-integration.md)
429
428
  - `applyZodNest(rawDoc, options)`, `ApplyZodNestOptions`, `ZodNestDocumentError`
@@ -432,7 +431,7 @@ A compact, link-out index. Type signatures and detailed semantics live in the co
432
431
  - `ZodNestModule.forRoot(options?)`, `ZodNestModuleOptions`, `DEFAULT_REDACT_KEYS`, `DEFAULT_MAX_LOGGED_VALUE_BYTES`, `ZOD_NEST_OPTIONS`
433
432
 
434
433
  **Schema engine** — single-schema mode and extension points
435
- - `toOpenApi(schema, opts)`, `createRegistry()`, `defaultRegistry`, `ZodNestRegistry`, `Override`, `OverrideContext`, `ZodNestError`, `ZodNestUnrepresentableError`, `extend`, `getLineage`, `LineageEntry`
434
+ - `toOpenApi(schema, opts)`, `createRegistry()`, `defaultRegistry`, `ZodNestRegistry`, `Override`, `OverrideContext`, `overrideJSONSchema(schema, fragment)`, `ZodNestError`, `ZodNestUnrepresentableError`, `extend`, `getLineage`, `LineageEntry`
436
435
 
437
436
  ## Documentation
438
437
 
@@ -470,7 +469,6 @@ If you're coming from `nestjs-zod`, the headline changes are:
470
469
  - Replace `@ApiOkResponse({ type: Dto }) + @ZodSerializerDto(Dto)` pairs with `@ZodResponse({ type: Dto })`.
471
470
  - Drop `class-validator` / `class-transformer` if they were installed only for `nestjs-zod` interop.
472
471
  - Check any `MyDto.isZodDto` reflection — the discriminator is now `Symbol.for('zod-nest.dto') in MyDto`.
473
- - Status wildcards (`'2XX'`, `'default'`) aren't supported in v0 — use explicit statuses.
474
472
 
475
473
  Full guide with side-by-side diffs and a 19-row breaking-changes table in [`MIGRATION.md`](MIGRATION.md).
476
474
 
package/dist/index.d.mts CHANGED
@@ -79,6 +79,28 @@ declare const extend: <P extends z.ZodObject, S extends z.ZodObject>(parent: P,
79
79
  */
80
80
  declare const getLineage: (schema: z.ZodType) => LineageEntry | undefined;
81
81
 
82
+ /**
83
+ * Register a fixed JSON Schema fragment for a specific Zod schema instance.
84
+ *
85
+ * Designed for shapes JSON Schema can't model directly — `z.custom<T>()` and
86
+ * `z.instanceof(...)` (e.g. multipart `File` fields) — which Zod emits as `{}`,
87
+ * tripping `ZodNestUnrepresentableError` in strict mode. After registration
88
+ * the engine writes the supplied fragment in-place wherever that schema
89
+ * instance is emitted (single-schema `toOpenApi`, bulk emission, nested
90
+ * inside `z.object({...})`, anywhere).
91
+ *
92
+ * Idempotent: subsequent calls for the same schema overwrite the prior
93
+ * registration (last-write-wins).
94
+ *
95
+ * @example
96
+ * const FileSchema = z.instanceof(File);
97
+ * overrideJSONSchema(FileSchema, { type: 'string', format: 'binary' });
98
+ *
99
+ * class UploadDto extends createZodDto(z.object({ file: FileSchema })) {}
100
+ * // OpenAPI doc emits `properties.file = { type: 'string', format: 'binary' }`
101
+ */
102
+ declare const overrideJSONSchema: (schema: z.ZodType, jsonSchema: SchemaObject) => void;
103
+
82
104
  interface ToOpenApiOptions {
83
105
  io: 'input' | 'output';
84
106
  registry: ZodNestRegistry;
@@ -321,6 +343,12 @@ declare class ZodValidationPipe implements PipeTransform {
321
343
  */
322
344
  declare const ZOD_RESPONSES_METADATA_KEY: unique symbol;
323
345
  type ResponseVariantKind = 'single' | 'array' | 'tuple';
346
+ /**
347
+ * OpenAPI 3.1 range keys accepted by `@ZodResponse({ status })`. Matched
348
+ * by `ZodSerializerInterceptor` after exact numeric matches fail —
349
+ * `'2XX'` covers 200–299, `'4XX'` covers 400–499, and so on.
350
+ */
351
+ type ResponseStatusWildcard = '1XX' | '2XX' | '3XX' | '4XX' | '5XX';
324
352
  /**
325
353
  * Description payload accepted by `@ZodResponse(...)` and passed through to
326
354
  * `applyZodNest`'s `@ApiResponse(...)` emitter. String form is shorthand for
@@ -337,14 +365,20 @@ type ZodResponseDescription = string | {
337
365
  * `validationSchema` so `applyZodNest` can emit `@ApiResponse({ type })`
338
366
  * without unwrapping the runtime-only `z.array(...)` / `z.tuple([...])` wrapper.
339
367
  *
340
- * `status` is `undefined` when the user didn't pass one explicitly the
341
- * effective status is resolved lazily by `resolveEffectiveStatus(variant,
342
- * handler)` because `@ZodResponse` runs *before* NestJS' route decorators
343
- * (`@Get`, `@Post`, ...) under TypeScript's bottom-up application order,
344
- * so `METHOD_METADATA` is not yet set when the decorator evaluates.
368
+ * `status` is `undefined` when the user didn't pass one explicitly OR when
369
+ * the user wrote `'default'` (the decorator collapses both to `undefined` —
370
+ * they mean the same thing: "resolve to the method's default status at
371
+ * request time"). The effective status is resolved lazily by
372
+ * `resolveEffectiveStatus(variant, handler)` because `@ZodResponse` runs
373
+ * *before* NestJS' route decorators (`@Get`, `@Post`, ...) under
374
+ * TypeScript's bottom-up application order, so `METHOD_METADATA` is not
375
+ * yet set when the decorator evaluates.
376
+ *
377
+ * A wildcard string (`'2XX'` / `'4XX'` / ...) is kept verbatim — the
378
+ * matcher in `ZodSerializerInterceptor` handles range comparison.
345
379
  */
346
380
  interface ResponseVariant {
347
- status: number | undefined;
381
+ status: number | ResponseStatusWildcard | undefined;
348
382
  kind: ResponseVariantKind;
349
383
  dto: ZodDto | readonly ZodDto[];
350
384
  validationSchema: z.ZodType;
@@ -365,8 +399,22 @@ interface ResponseVariant {
365
399
  * so typos surface at module load, not the first request.
366
400
  */
367
401
  type ZodResponseType = ZodDto | readonly [ZodDto, ...ZodDto[]];
402
+ /**
403
+ * Accepted shapes for `@ZodResponse({ status })`:
404
+ * - `number` — exact match against `response.statusCode` (most common case).
405
+ * - `'1XX'` / `'2XX'` / `'3XX'` / `'4XX'` / `'5XX'` — OpenAPI 3.1 range key;
406
+ * `'2XX'` matches 200–299, etc. Considered only after no exact numeric
407
+ * variant matches the observed status.
408
+ * - `'default'` — sugar for the handler's method default status. Collapsed
409
+ * to `undefined` when the variant is built, so `resolveEffectiveStatus`
410
+ * walks the same `@HttpCode → method-default` chain as an omitted `status`.
411
+ * This deliberately does NOT implement a catch-all-fallback semantic; it
412
+ * names the canonical-success card explicitly, matching what consumers
413
+ * already write in `@ApiResponse({ status: 'default' })`.
414
+ */
415
+ type ResponseStatusInput = number | ResponseStatusWildcard | 'default';
368
416
  interface ZodResponseOptions {
369
- status?: number;
417
+ status?: ResponseStatusInput;
370
418
  type: ZodResponseType;
371
419
  description?: ZodResponseDescription;
372
420
  passthroughOnError?: boolean;
@@ -374,7 +422,8 @@ interface ZodResponseOptions {
374
422
  /**
375
423
  * Method-only decorator. Declares a typed response variant for the handler.
376
424
  * Stack multiple decorations to declare per-status types; lookup at runtime
377
- * is by `response.statusCode === variant.status`.
425
+ * is by `ZodSerializerInterceptor`'s two-pass matcher (exact numeric, then
426
+ * `'NXX'` wildcard).
378
427
  *
379
428
  * The wrapped Zod schema (array / tuple) is built once at decoration time
380
429
  * and stored on the variant record — no per-request schema construction.
@@ -404,17 +453,19 @@ declare class ZodSerializerInterceptor implements NestInterceptor {
404
453
  */
405
454
  declare const defaultStatusFor: (handler: object) => number;
406
455
  /**
407
- * Resolve the effective status code for a variant. Precedence chain:
456
+ * Resolve the effective status for a variant. Precedence chain:
408
457
  *
409
- * 1. Explicit `@ZodResponse({ status })` on the decorator call — `variant.status`.
410
- * 2. `@HttpCode(n)` on the handlerread via `defaultStatusFor()` at runtime.
411
- * 3. HTTP method default (POST → 201, others → 200) — also via `defaultStatusFor()`.
458
+ * 1. Explicit numeric `@ZodResponse({ status })` returned verbatim.
459
+ * 2. Wildcard `@ZodResponse({ status: 'NXX' })`returned verbatim; the
460
+ * interceptor matcher handles range comparison against `response.statusCode`.
461
+ * 3. Omitted status (or `'default'` collapsed at decoration time) —
462
+ * `@HttpCode(n)` then HTTP-method default via `defaultStatusFor()`.
412
463
  *
413
464
  * Resolution is deferred to request time because `@ZodResponse` runs before
414
465
  * NestJS' route + `@HttpCode` decorators — none of their metadata is set
415
466
  * when the decorator evaluates.
416
467
  */
417
- declare const resolveEffectiveStatus: (variant: ResponseVariant, handler: object) => number;
468
+ declare const resolveEffectiveStatus: (variant: ResponseVariant, handler: object) => number | ResponseStatusWildcard;
418
469
 
419
470
  /**
420
471
  * Central wiring for the zod-nest pipe + interceptor. Call
@@ -498,4 +549,4 @@ declare class ZodNestDocumentError extends ZodNestError {
498
549
  constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
499
550
  }
500
551
 
501
- 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 ResponseVariant, type ResponseVariantKind, type 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, resolveEffectiveStatus, toOpenApi };
552
+ 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 ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, type 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, resolveEffectiveStatus, toOpenApi };
package/dist/index.d.ts CHANGED
@@ -79,6 +79,28 @@ declare const extend: <P extends z.ZodObject, S extends z.ZodObject>(parent: P,
79
79
  */
80
80
  declare const getLineage: (schema: z.ZodType) => LineageEntry | undefined;
81
81
 
82
+ /**
83
+ * Register a fixed JSON Schema fragment for a specific Zod schema instance.
84
+ *
85
+ * Designed for shapes JSON Schema can't model directly — `z.custom<T>()` and
86
+ * `z.instanceof(...)` (e.g. multipart `File` fields) — which Zod emits as `{}`,
87
+ * tripping `ZodNestUnrepresentableError` in strict mode. After registration
88
+ * the engine writes the supplied fragment in-place wherever that schema
89
+ * instance is emitted (single-schema `toOpenApi`, bulk emission, nested
90
+ * inside `z.object({...})`, anywhere).
91
+ *
92
+ * Idempotent: subsequent calls for the same schema overwrite the prior
93
+ * registration (last-write-wins).
94
+ *
95
+ * @example
96
+ * const FileSchema = z.instanceof(File);
97
+ * overrideJSONSchema(FileSchema, { type: 'string', format: 'binary' });
98
+ *
99
+ * class UploadDto extends createZodDto(z.object({ file: FileSchema })) {}
100
+ * // OpenAPI doc emits `properties.file = { type: 'string', format: 'binary' }`
101
+ */
102
+ declare const overrideJSONSchema: (schema: z.ZodType, jsonSchema: SchemaObject) => void;
103
+
82
104
  interface ToOpenApiOptions {
83
105
  io: 'input' | 'output';
84
106
  registry: ZodNestRegistry;
@@ -321,6 +343,12 @@ declare class ZodValidationPipe implements PipeTransform {
321
343
  */
322
344
  declare const ZOD_RESPONSES_METADATA_KEY: unique symbol;
323
345
  type ResponseVariantKind = 'single' | 'array' | 'tuple';
346
+ /**
347
+ * OpenAPI 3.1 range keys accepted by `@ZodResponse({ status })`. Matched
348
+ * by `ZodSerializerInterceptor` after exact numeric matches fail —
349
+ * `'2XX'` covers 200–299, `'4XX'` covers 400–499, and so on.
350
+ */
351
+ type ResponseStatusWildcard = '1XX' | '2XX' | '3XX' | '4XX' | '5XX';
324
352
  /**
325
353
  * Description payload accepted by `@ZodResponse(...)` and passed through to
326
354
  * `applyZodNest`'s `@ApiResponse(...)` emitter. String form is shorthand for
@@ -337,14 +365,20 @@ type ZodResponseDescription = string | {
337
365
  * `validationSchema` so `applyZodNest` can emit `@ApiResponse({ type })`
338
366
  * without unwrapping the runtime-only `z.array(...)` / `z.tuple([...])` wrapper.
339
367
  *
340
- * `status` is `undefined` when the user didn't pass one explicitly the
341
- * effective status is resolved lazily by `resolveEffectiveStatus(variant,
342
- * handler)` because `@ZodResponse` runs *before* NestJS' route decorators
343
- * (`@Get`, `@Post`, ...) under TypeScript's bottom-up application order,
344
- * so `METHOD_METADATA` is not yet set when the decorator evaluates.
368
+ * `status` is `undefined` when the user didn't pass one explicitly OR when
369
+ * the user wrote `'default'` (the decorator collapses both to `undefined` —
370
+ * they mean the same thing: "resolve to the method's default status at
371
+ * request time"). The effective status is resolved lazily by
372
+ * `resolveEffectiveStatus(variant, handler)` because `@ZodResponse` runs
373
+ * *before* NestJS' route decorators (`@Get`, `@Post`, ...) under
374
+ * TypeScript's bottom-up application order, so `METHOD_METADATA` is not
375
+ * yet set when the decorator evaluates.
376
+ *
377
+ * A wildcard string (`'2XX'` / `'4XX'` / ...) is kept verbatim — the
378
+ * matcher in `ZodSerializerInterceptor` handles range comparison.
345
379
  */
346
380
  interface ResponseVariant {
347
- status: number | undefined;
381
+ status: number | ResponseStatusWildcard | undefined;
348
382
  kind: ResponseVariantKind;
349
383
  dto: ZodDto | readonly ZodDto[];
350
384
  validationSchema: z.ZodType;
@@ -365,8 +399,22 @@ interface ResponseVariant {
365
399
  * so typos surface at module load, not the first request.
366
400
  */
367
401
  type ZodResponseType = ZodDto | readonly [ZodDto, ...ZodDto[]];
402
+ /**
403
+ * Accepted shapes for `@ZodResponse({ status })`:
404
+ * - `number` — exact match against `response.statusCode` (most common case).
405
+ * - `'1XX'` / `'2XX'` / `'3XX'` / `'4XX'` / `'5XX'` — OpenAPI 3.1 range key;
406
+ * `'2XX'` matches 200–299, etc. Considered only after no exact numeric
407
+ * variant matches the observed status.
408
+ * - `'default'` — sugar for the handler's method default status. Collapsed
409
+ * to `undefined` when the variant is built, so `resolveEffectiveStatus`
410
+ * walks the same `@HttpCode → method-default` chain as an omitted `status`.
411
+ * This deliberately does NOT implement a catch-all-fallback semantic; it
412
+ * names the canonical-success card explicitly, matching what consumers
413
+ * already write in `@ApiResponse({ status: 'default' })`.
414
+ */
415
+ type ResponseStatusInput = number | ResponseStatusWildcard | 'default';
368
416
  interface ZodResponseOptions {
369
- status?: number;
417
+ status?: ResponseStatusInput;
370
418
  type: ZodResponseType;
371
419
  description?: ZodResponseDescription;
372
420
  passthroughOnError?: boolean;
@@ -374,7 +422,8 @@ interface ZodResponseOptions {
374
422
  /**
375
423
  * Method-only decorator. Declares a typed response variant for the handler.
376
424
  * Stack multiple decorations to declare per-status types; lookup at runtime
377
- * is by `response.statusCode === variant.status`.
425
+ * is by `ZodSerializerInterceptor`'s two-pass matcher (exact numeric, then
426
+ * `'NXX'` wildcard).
378
427
  *
379
428
  * The wrapped Zod schema (array / tuple) is built once at decoration time
380
429
  * and stored on the variant record — no per-request schema construction.
@@ -404,17 +453,19 @@ declare class ZodSerializerInterceptor implements NestInterceptor {
404
453
  */
405
454
  declare const defaultStatusFor: (handler: object) => number;
406
455
  /**
407
- * Resolve the effective status code for a variant. Precedence chain:
456
+ * Resolve the effective status for a variant. Precedence chain:
408
457
  *
409
- * 1. Explicit `@ZodResponse({ status })` on the decorator call — `variant.status`.
410
- * 2. `@HttpCode(n)` on the handlerread via `defaultStatusFor()` at runtime.
411
- * 3. HTTP method default (POST → 201, others → 200) — also via `defaultStatusFor()`.
458
+ * 1. Explicit numeric `@ZodResponse({ status })` returned verbatim.
459
+ * 2. Wildcard `@ZodResponse({ status: 'NXX' })`returned verbatim; the
460
+ * interceptor matcher handles range comparison against `response.statusCode`.
461
+ * 3. Omitted status (or `'default'` collapsed at decoration time) —
462
+ * `@HttpCode(n)` then HTTP-method default via `defaultStatusFor()`.
412
463
  *
413
464
  * Resolution is deferred to request time because `@ZodResponse` runs before
414
465
  * NestJS' route + `@HttpCode` decorators — none of their metadata is set
415
466
  * when the decorator evaluates.
416
467
  */
417
- declare const resolveEffectiveStatus: (variant: ResponseVariant, handler: object) => number;
468
+ declare const resolveEffectiveStatus: (variant: ResponseVariant, handler: object) => number | ResponseStatusWildcard;
418
469
 
419
470
  /**
420
471
  * Central wiring for the zod-nest pipe + interceptor. Call
@@ -498,4 +549,4 @@ declare class ZodNestDocumentError extends ZodNestError {
498
549
  constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
499
550
  }
500
551
 
501
- 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 ResponseVariant, type ResponseVariantKind, type 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, resolveEffectiveStatus, toOpenApi };
552
+ 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 ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, type 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, resolveEffectiveStatus, toOpenApi };
package/dist/index.js CHANGED
@@ -104,6 +104,22 @@ var createCompositionOverride = /* @__PURE__ */ __name((opts) => {
104
104
  };
105
105
  }, "createCompositionOverride");
106
106
 
107
+ // src/schema/custom-override.ts
108
+ var customOverrideMap = /* @__PURE__ */ new WeakMap();
109
+ var overrideJSONSchema = /* @__PURE__ */ __name((schema, jsonSchema) => {
110
+ customOverrideMap.set(schema, jsonSchema);
111
+ }, "overrideJSONSchema");
112
+ var customOverride = /* @__PURE__ */ __name(({ zodSchema, jsonSchema }) => {
113
+ const fragment = customOverrideMap.get(zodSchema);
114
+ if (fragment === void 0) {
115
+ return;
116
+ }
117
+ for (const key of Object.keys(jsonSchema)) {
118
+ Reflect.deleteProperty(jsonSchema, key);
119
+ }
120
+ Object.assign(jsonSchema, fragment);
121
+ }, "customOverride");
122
+
107
123
  // src/schema/errors.ts
108
124
  var ZodNestError = class extends Error {
109
125
  static {
@@ -232,7 +248,7 @@ var buildToJsonSchemaOptions = /* @__PURE__ */ __name((params) => {
232
248
  buildRef: params.uri ?? DEFAULT_BUILD_REF,
233
249
  registry: params.registry
234
250
  });
235
- const merged = combine(primitiveOverride, compositionOverride, params.override);
251
+ const merged = combine(primitiveOverride, compositionOverride, customOverride, params.override);
236
252
  const unrepresentableHits = [];
237
253
  const wrapped = /* @__PURE__ */ __name((ctx) => {
238
254
  merged(ctx);
@@ -801,15 +817,22 @@ var buildKind = /* @__PURE__ */ __name((type) => {
801
817
  validationSchema: type.schema
802
818
  };
803
819
  }, "buildKind");
820
+ var normaliseStatus = /* @__PURE__ */ __name((status) => {
821
+ if (status === "default") {
822
+ return void 0;
823
+ }
824
+ return status;
825
+ }, "normaliseStatus");
804
826
  var ZodResponse = /* @__PURE__ */ __name((opts) => {
805
827
  const built = buildKind(opts.type);
828
+ const status = normaliseStatus(opts.status);
806
829
  return (_target, _propertyKey, descriptor) => {
807
830
  const handler = descriptor.value;
808
831
  if (typeof handler !== "function") {
809
832
  throw new TypeError("[zod-nest] @ZodResponse can only be applied to methods.");
810
833
  }
811
834
  const variant = {
812
- status: opts.status,
835
+ status,
813
836
  kind: built.kind,
814
837
  dto: built.dto,
815
838
  validationSchema: built.validationSchema,
@@ -866,6 +889,22 @@ var formatDtoLabel = /* @__PURE__ */ __name((variant) => {
866
889
  return `[${dtos.map((d) => d.name).join(", ")}]`;
867
890
  }, "formatDtoLabel");
868
891
  var formatHandlerLabel = /* @__PURE__ */ __name((context) => `${context.getClass().name}.${context.getHandler().name}`, "formatHandlerLabel");
892
+ var matchesWildcard = /* @__PURE__ */ __name((wildcard, status) => wildcard.charCodeAt(0) - 48 === Math.floor(status / 100), "matchesWildcard");
893
+ var selectVariant = /* @__PURE__ */ __name((variants, status, handler) => {
894
+ for (const variant of variants) {
895
+ const effective = resolveEffectiveStatus(variant, handler);
896
+ if (typeof effective === "number" && effective === status) {
897
+ return variant;
898
+ }
899
+ }
900
+ for (const variant of variants) {
901
+ const effective = resolveEffectiveStatus(variant, handler);
902
+ if (typeof effective === "string" && matchesWildcard(effective, status)) {
903
+ return variant;
904
+ }
905
+ }
906
+ return void 0;
907
+ }, "selectVariant");
869
908
  exports.ZodSerializerInterceptor = class ZodSerializerInterceptor {
870
909
  static {
871
910
  __name(this, "ZodSerializerInterceptor");
@@ -895,7 +934,7 @@ exports.ZodSerializerInterceptor = class ZodSerializerInterceptor {
895
934
  if (status === void 0) {
896
935
  return value;
897
936
  }
898
- const variant = variants.find((v) => resolveEffectiveStatus(v, handler) === status);
937
+ const variant = selectVariant(variants, status, handler);
899
938
  if (variant === void 0) {
900
939
  return value;
901
940
  }
@@ -1459,6 +1498,7 @@ exports.getLineage = getLineage;
1459
1498
  exports.isZodDto = isZodDto;
1460
1499
  exports.isZodDtoMarker = isZodDtoMarker;
1461
1500
  exports.makeZodDtoMarker = makeZodDtoMarker;
1501
+ exports.overrideJSONSchema = overrideJSONSchema;
1462
1502
  exports.resolveEffectiveStatus = resolveEffectiveStatus;
1463
1503
  exports.toOpenApi = toOpenApi;
1464
1504
  //# sourceMappingURL=index.js.map