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 +3 -5
- package/dist/index.d.mts +65 -14
- package/dist/index.d.ts +65 -14
- package/dist/index.js +43 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
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?:
|
|
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 `
|
|
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
|
|
456
|
+
* Resolve the effective status for a variant. Precedence chain:
|
|
408
457
|
*
|
|
409
|
-
* 1. Explicit `@ZodResponse({ status })`
|
|
410
|
-
* 2. `@
|
|
411
|
-
*
|
|
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
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
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?:
|
|
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 `
|
|
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
|
|
456
|
+
* Resolve the effective status for a variant. Precedence chain:
|
|
408
457
|
*
|
|
409
|
-
* 1. Explicit `@ZodResponse({ status })`
|
|
410
|
-
* 2. `@
|
|
411
|
-
*
|
|
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
|
|
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
|
|
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
|