zod-nest 1.11.0 → 2.0.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 +6 -4
- package/dist/index.d.mts +116 -20
- package/dist/index.d.ts +116 -20
- package/dist/index.js +229 -79
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +230 -80
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,7 +130,7 @@ async function bootstrap() {
|
|
|
130
130
|
app,
|
|
131
131
|
new DocumentBuilder().setTitle('Users').setVersion('1').build(),
|
|
132
132
|
);
|
|
133
|
-
const doc = applyZodNest(raw
|
|
133
|
+
const doc = applyZodNest(raw);
|
|
134
134
|
SwaggerModule.setup('docs', app, doc);
|
|
135
135
|
|
|
136
136
|
await app.listen(3000);
|
|
@@ -151,7 +151,7 @@ Every DTO is one Zod schema wrapped in a class. The class exists so NestJS' intr
|
|
|
151
151
|
|
|
152
152
|
The class returned by `createZodDto(schema)` carries `schema`, `id`, `io: 'input'`, and a lazy `Output` sibling. `parse` / `safeParse` are static methods on the class. The class is tagged with `Symbol.for('zod-nest.dto')` so `ZodValidationPipe` and `ZodSerializerInterceptor` can discriminate it from plain constructors. The id comes from `schema.meta({ id })` when present (preferred) or from the second-argument options.
|
|
153
153
|
|
|
154
|
-
**
|
|
154
|
+
**Exposure is reachability-scoped.** A schema lands in `components.schemas` when an endpoint in the document references it — directly or transitively through a `$ref` (response inner deps included), or as a query/param/header/cookie DTO captured via its marker. A registered schema that no endpoint reaches is pruned; force it in with `{ expose: true }`. Anonymous schemas without an id stay inlined where used. This keeps several Swagger documents that share one registry from each carrying the whole catalog.
|
|
155
155
|
|
|
156
156
|
See [`docs/dto.md`](docs/dto.md) for the full surface.
|
|
157
157
|
|
|
@@ -186,6 +186,8 @@ The decorator set: `@ZodBody`, `@ZodQuery`, `@ZodHeaders`, `@ZodCookies`. All ar
|
|
|
186
186
|
|
|
187
187
|
See [`docs/recipes/intersection-with-union.md`](docs/recipes/intersection-with-union.md) for the full pattern.
|
|
188
188
|
|
|
189
|
+
Named query objects (both `@Query() dto` and `@ZodQuery`) expand to one parameter per field by default. Pass `applyZodNest(raw, { queryParamStyle: 'ref' })` — or `@ZodQuery(schema, { ref: true })` per handler — to instead emit a single schema-based query parameter that `$ref`s the shared component. Same wire format; see [`docs/swagger-integration.md → Query parameter style`](docs/swagger-integration.md#query-parameter-style).
|
|
190
|
+
|
|
189
191
|
### I/O suffix rules
|
|
190
192
|
|
|
191
193
|
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.
|
|
@@ -375,7 +377,7 @@ Use this sparingly — it bypasses the contract you declared. Logging at `warn`
|
|
|
375
377
|
import { applyZodNest } from 'zod-nest';
|
|
376
378
|
|
|
377
379
|
const raw = SwaggerModule.createDocument(app, config);
|
|
378
|
-
const doc = applyZodNest(raw
|
|
380
|
+
const doc = applyZodNest(raw);
|
|
379
381
|
SwaggerModule.setup('docs', app, doc);
|
|
380
382
|
```
|
|
381
383
|
|
|
@@ -540,7 +542,7 @@ Cell definitions live in [`.github/compat-matrix.json`](.github/compat-matrix.js
|
|
|
540
542
|
|
|
541
543
|
If you're coming from `nestjs-zod`, the headline changes are:
|
|
542
544
|
|
|
543
|
-
- Replace `cleanupOpenApiDoc(SwaggerModule.createDocument(app, config))` with `applyZodNest(SwaggerModule.createDocument(app, config)
|
|
545
|
+
- Replace `cleanupOpenApiDoc(SwaggerModule.createDocument(app, config))` with `applyZodNest(SwaggerModule.createDocument(app, config))`.
|
|
544
546
|
- Replace `@ApiOkResponse({ type: Dto }) + @ZodSerializerDto(Dto)` pairs with `@ZodResponse({ type: Dto })`.
|
|
545
547
|
- Drop `class-validator` / `class-transformer` if they were installed only for `nestjs-zod` interop.
|
|
546
548
|
- Check any `MyDto.isZodDto` reflection — the discriminator is now `Symbol.for('zod-nest.dto') in MyDto`.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { S as SchemaObject } from './openapi.types-CFBG3Zz9.mjs';
|
|
3
3
|
import { $ZodTypes } from 'zod/v4/core';
|
|
4
|
-
import { InternalServerErrorException, ExecutionContext, BadRequestException, ArgumentMetadata, LoggerService, PipeTransform, NestInterceptor, CallHandler, DynamicModule
|
|
4
|
+
import { InternalServerErrorException, ExecutionContext, BadRequestException, ArgumentMetadata, LoggerService, PipeTransform, NestInterceptor, CallHandler, DynamicModule } from '@nestjs/common';
|
|
5
5
|
import { Reflector } from '@nestjs/core';
|
|
6
6
|
import { Observable } from 'rxjs';
|
|
7
7
|
import { OpenAPIObject } from '@nestjs/swagger';
|
|
@@ -27,9 +27,31 @@ interface OverrideContext {
|
|
|
27
27
|
}
|
|
28
28
|
type Override = (ctx: OverrideContext) => void;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Per-registration flags carried alongside the id.
|
|
32
|
+
*
|
|
33
|
+
* - `expose` — force the id into the emitted document even when no endpoint
|
|
34
|
+
* references it. The author deliberately wants it in `components.schemas`
|
|
35
|
+
* (e.g. for out-of-band client codegen). Default exposure is otherwise
|
|
36
|
+
* reachability-scoped, so an unreferenced schema is pruned unless flagged.
|
|
37
|
+
* - `anonymous` — the id is a synthetic placeholder for a schema with no
|
|
38
|
+
* resolvable id (passed inline to `@ZodResponse` / `@ZodBody`). It exists
|
|
39
|
+
* only to carry the body through bulk emission under the document's
|
|
40
|
+
* `strict` / `override` options; `inlineAnonymousBodies` later inlines the
|
|
41
|
+
* body at each `$ref` site and prunes the component, so the synthetic id
|
|
42
|
+
* never reaches the final document.
|
|
43
|
+
*
|
|
44
|
+
* Both flags are sticky — once set for an id they stay set, so a later plain
|
|
45
|
+
* `register` of the same id (e.g. the idempotent re-register inside
|
|
46
|
+
* `createZodDto`) doesn't clear them.
|
|
47
|
+
*/
|
|
48
|
+
interface RegisterFlags {
|
|
49
|
+
readonly expose?: boolean;
|
|
50
|
+
readonly anonymous?: boolean;
|
|
51
|
+
}
|
|
30
52
|
interface ZodNestRegistry {
|
|
31
53
|
readonly zodRegistry: typeof z.globalRegistry;
|
|
32
|
-
register(schema: z.ZodType, id: string): void;
|
|
54
|
+
register(schema: z.ZodType, id: string, flags?: RegisterFlags): void;
|
|
33
55
|
hasCollision(id: string): boolean;
|
|
34
56
|
getCollisions(): ReadonlyMap<string, ReadonlySet<z.ZodType>>;
|
|
35
57
|
/**
|
|
@@ -42,6 +64,10 @@ interface ZodNestRegistry {
|
|
|
42
64
|
* of explicitly-registered schemas.
|
|
43
65
|
*/
|
|
44
66
|
ids(): readonly string[];
|
|
67
|
+
/** Ids registered with `{ expose: true }` — exposed regardless of usage. */
|
|
68
|
+
forceExposedIds(): readonly string[];
|
|
69
|
+
/** Ids registered with `{ anonymous: true }` — inlined + pruned by `applyZodNest`. */
|
|
70
|
+
anonymousIds(): readonly string[];
|
|
45
71
|
}
|
|
46
72
|
declare const createRegistry: () => ZodNestRegistry;
|
|
47
73
|
/** Process-wide default registry, used when no explicit `options.registry` is passed. */
|
|
@@ -49,6 +75,17 @@ declare const defaultRegistry: ZodNestRegistry;
|
|
|
49
75
|
interface RegisterSchemaOptions {
|
|
50
76
|
/** Forces this id, overriding any `.meta({ id })` already on the schema. */
|
|
51
77
|
readonly id?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Force the schema into the emitted document even when no endpoint
|
|
80
|
+
* references it. Default exposure is reachability-scoped — see
|
|
81
|
+
* {@link RegisterFlags.expose}.
|
|
82
|
+
*/
|
|
83
|
+
readonly expose?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Mark the resolved id as a synthetic anonymous placeholder — inlined and
|
|
86
|
+
* pruned by `applyZodNest`. See {@link RegisterFlags.anonymous}.
|
|
87
|
+
*/
|
|
88
|
+
readonly anonymous?: boolean;
|
|
52
89
|
}
|
|
53
90
|
/**
|
|
54
91
|
* Register a schema with the given registry, resolving its id from (in order):
|
|
@@ -175,6 +212,14 @@ interface CreateZodDtoOptions {
|
|
|
175
212
|
id?: string;
|
|
176
213
|
/** Registry to register this DTO's schema into. Defaults to `defaultRegistry`. */
|
|
177
214
|
registry?: ZodNestRegistry;
|
|
215
|
+
/**
|
|
216
|
+
* Force this DTO's schema into the emitted document even when no endpoint
|
|
217
|
+
* references it. Exposure is otherwise reachability-scoped — a registered
|
|
218
|
+
* schema that no exposed endpoint reaches is pruned. Set `true` to keep an
|
|
219
|
+
* intentionally-documented schema (e.g. for out-of-band client codegen).
|
|
220
|
+
* Defaults to `false`.
|
|
221
|
+
*/
|
|
222
|
+
expose?: boolean;
|
|
178
223
|
}
|
|
179
224
|
/**
|
|
180
225
|
* The class type returned by `createZodDto`. Instance type infers to the
|
|
@@ -616,12 +661,34 @@ interface ZodQueryOptions {
|
|
|
616
661
|
readonly id?: string;
|
|
617
662
|
/** Registry to register into. Defaults to `defaultRegistry`. */
|
|
618
663
|
readonly registry?: ZodNestRegistry;
|
|
664
|
+
/**
|
|
665
|
+
* Override how this query DTO is represented in the OpenAPI doc, taking
|
|
666
|
+
* precedence over `applyZodNest`'s `queryParamStyle`:
|
|
667
|
+
*
|
|
668
|
+
* - `true` — one single schema-based query parameter that `$ref`s the DTO's
|
|
669
|
+
* `components.schemas` entry (`style: 'form'`, `explode: true`).
|
|
670
|
+
* - `false` — one parameter per top-level property.
|
|
671
|
+
* - unset — follow the global `queryParamStyle` preference (default `'expand'`).
|
|
672
|
+
*
|
|
673
|
+
* Ref mode needs a named schema to reference: `ref: true` on an anonymous
|
|
674
|
+
* schema (no `.meta({ id })` and no `id` option) throws `ZodNestError`.
|
|
675
|
+
*/
|
|
676
|
+
readonly ref?: boolean;
|
|
619
677
|
}
|
|
620
678
|
/**
|
|
621
|
-
* Method-level decorator
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
679
|
+
* Method-level decorator describing a `@Query()` object schema as OpenAPI
|
|
680
|
+
* query parameters.
|
|
681
|
+
*
|
|
682
|
+
* When the schema is named (`.meta({ id })` or the `id` option), the decorator
|
|
683
|
+
* registers it and emits a single deferred marker, leaving the expand-vs-`$ref`
|
|
684
|
+
* decision to `applyZodNest` — so an unset `ref` follows the global
|
|
685
|
+
* `queryParamStyle` preference, `ref: true` collapses to one `$ref` query
|
|
686
|
+
* parameter, and `ref: false` expands per property. This mirrors the
|
|
687
|
+
* `@Query() dto` (`createZodDto`) path, which flows through the same marker.
|
|
688
|
+
*
|
|
689
|
+
* When the schema is anonymous, there is no component to reference, so the
|
|
690
|
+
* decorator expands one `@ApiQuery` per property immediately (and rejects
|
|
691
|
+
* `ref: true`).
|
|
625
692
|
*
|
|
626
693
|
* The user remains responsible for binding the actual query values to the
|
|
627
694
|
* handler, typically via `@Query(new ZodValidationPipe(schema)) q: z.infer<typeof schema>`.
|
|
@@ -720,13 +787,22 @@ declare class ZodNestModule {
|
|
|
720
787
|
static forRoot(options?: ZodNestModuleOptions): DynamicModule;
|
|
721
788
|
}
|
|
722
789
|
|
|
790
|
+
/**
|
|
791
|
+
* How a named `@Query()` / `@ZodQuery` DTO is represented in the OpenAPI doc:
|
|
792
|
+
*
|
|
793
|
+
* - `'expand'` (default) — one parameter per top-level property of the DTO
|
|
794
|
+
* schema, each inlined or `$ref`'d independently.
|
|
795
|
+
* - `'ref'` — a single schema-based query parameter that references the DTO's
|
|
796
|
+
* `components.schemas` entry (`style: 'form'`, `explode: true`,
|
|
797
|
+
* `schema: { $ref }`). The wire format is identical to `'expand'`; only the
|
|
798
|
+
* document representation collapses to the shared component.
|
|
799
|
+
*
|
|
800
|
+
* Query-only: path / header / cookie markers always expand regardless of this
|
|
801
|
+
* setting, since the form-exploded-object pattern is a query serialization.
|
|
802
|
+
*/
|
|
803
|
+
type QueryParamStyle = 'expand' | 'ref';
|
|
804
|
+
|
|
723
805
|
interface ApplyZodNestOptions {
|
|
724
|
-
/**
|
|
725
|
-
* The NestJS app instance. Required so `applyZodNest` can walk controllers
|
|
726
|
-
* via `DiscoveryService` to pick up `@ZodResponse` output-side DTO usage —
|
|
727
|
-
* `@nestjs/swagger` is currently anemic on response shapes.
|
|
728
|
-
*/
|
|
729
|
-
app: INestApplication;
|
|
730
806
|
/**
|
|
731
807
|
* `ZodNestRegistry` instance that holds the zod-nest DTOs. Defaults to
|
|
732
808
|
* `defaultRegistry` (the process-wide singleton populated by `createZodDto`).
|
|
@@ -741,6 +817,20 @@ interface ApplyZodNestOptions {
|
|
|
741
817
|
* Set to `false` to emit `{}` for those instead.
|
|
742
818
|
*/
|
|
743
819
|
strict?: boolean;
|
|
820
|
+
/**
|
|
821
|
+
* How named `@Query()` / `@ZodQuery` DTOs are represented in the document
|
|
822
|
+
* (default `'expand'`):
|
|
823
|
+
*
|
|
824
|
+
* - `'expand'` — one query parameter per top-level property of the DTO.
|
|
825
|
+
* - `'ref'` — a single schema-based query parameter referencing the DTO's
|
|
826
|
+
* `components.schemas` entry (`style: 'form'`, `explode: true`). The wire
|
|
827
|
+
* format is unchanged; only the spec representation collapses to the
|
|
828
|
+
* shared component. Note: Swagger UI renders the two forms differently.
|
|
829
|
+
*
|
|
830
|
+
* Query-only — path / header / cookie DTOs always expand. A per-handler
|
|
831
|
+
* `@ZodQuery({ ref })` override takes precedence over this preference.
|
|
832
|
+
*/
|
|
833
|
+
queryParamStyle?: QueryParamStyle;
|
|
744
834
|
}
|
|
745
835
|
/**
|
|
746
836
|
* Post-processor over the OpenAPI document emitted by
|
|
@@ -752,23 +842,29 @@ interface ApplyZodNestOptions {
|
|
|
752
842
|
* keyed by the marker's `dtoId` (renaming as needed).
|
|
753
843
|
* - Every `@Query()` / `@Param()` / `@Headers()` / `@Cookie()` marker
|
|
754
844
|
* parameter is expanded into one parameter per top-level property of the
|
|
755
|
-
* DTO's schema (`expandParamMarkers`)
|
|
756
|
-
*
|
|
757
|
-
*
|
|
845
|
+
* DTO's schema (`expandParamMarkers`) — except `@Query()` DTOs under
|
|
846
|
+
* `queryParamStyle: 'ref'` (or a `@ZodQuery({ ref: true })` override), which
|
|
847
|
+
* collapse to a single `$ref` schema-based query parameter. The synthetic
|
|
848
|
+
* `components.schemas.Object` that `@nestjs/swagger` materialises for the
|
|
849
|
+
* marker placeholder is pruned when it has no remaining referrers.
|
|
758
850
|
* - The I/O suffix truth table is applied — equal input/output bodies collapse
|
|
759
851
|
* to one `components.schemas[id]`; divergent bodies split as
|
|
760
852
|
* `id` (input) + `idOutput` (output), with response-side refs rewritten.
|
|
853
|
+
* - Anonymous body/response schemas (no `.meta({ id })`) are inlined at their
|
|
854
|
+
* `$ref` sites and their synthetic components pruned (`inlineAnonymousBodies`).
|
|
855
|
+
* - Only schemas reachable from this document's endpoints (plus their
|
|
856
|
+
* transitive `$ref` deps, plus any `{ expose: true }` opt-ins) are kept —
|
|
857
|
+
* unreferenced registered schemas are pruned. Exposure is document-scoped, so
|
|
858
|
+
* several documents sharing one registry each carry only what they use.
|
|
761
859
|
* - Every `$ref` whose target is missing throws `ZodNestDocumentError(DANGLING_REF)`.
|
|
762
860
|
* - `doc.openapi` is set to `'3.1.0'` — zod-nest emits OpenAPI 3.1 only; this
|
|
763
861
|
* guarantees the version string matches the emitted body regardless of the
|
|
764
862
|
* `DocumentBuilder` configuration on the caller side.
|
|
765
863
|
*
|
|
766
864
|
* Composable with other doc-transform passes — apply other mutations before
|
|
767
|
-
* or after this function.
|
|
768
|
-
* output-side DTO usage lives on controller-method metadata that the doc
|
|
769
|
-
* doesn't surface; `DiscoveryService` resolves it.
|
|
865
|
+
* or after this function.
|
|
770
866
|
*/
|
|
771
|
-
declare const applyZodNest: (doc: OpenAPIObject, opts
|
|
867
|
+
declare const applyZodNest: (doc: OpenAPIObject, opts?: ApplyZodNestOptions) => OpenAPIObject;
|
|
772
868
|
|
|
773
869
|
type ZodNestDocumentErrorCode = 'AMBIGUOUS_RENAME' | 'DANGLING_REF' | 'UNEXPANDABLE_PARAM_DTO';
|
|
774
870
|
/**
|
|
@@ -795,4 +891,4 @@ declare class ZodNestDocumentError extends ZodNestError {
|
|
|
795
891
|
constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
|
|
796
892
|
}
|
|
797
893
|
|
|
798
|
-
export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, DEFAULT_STREAM_CONTENT_TYPES, 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 ZodResponseEntry, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, isZodSchema, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
|
|
894
|
+
export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, DEFAULT_STREAM_CONTENT_TYPES, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterFlags, 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 ZodResponseEntry, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, isZodSchema, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { S as SchemaObject } from './openapi.types-CFBG3Zz9.js';
|
|
3
3
|
import { $ZodTypes } from 'zod/v4/core';
|
|
4
|
-
import { InternalServerErrorException, ExecutionContext, BadRequestException, ArgumentMetadata, LoggerService, PipeTransform, NestInterceptor, CallHandler, DynamicModule
|
|
4
|
+
import { InternalServerErrorException, ExecutionContext, BadRequestException, ArgumentMetadata, LoggerService, PipeTransform, NestInterceptor, CallHandler, DynamicModule } from '@nestjs/common';
|
|
5
5
|
import { Reflector } from '@nestjs/core';
|
|
6
6
|
import { Observable } from 'rxjs';
|
|
7
7
|
import { OpenAPIObject } from '@nestjs/swagger';
|
|
@@ -27,9 +27,31 @@ interface OverrideContext {
|
|
|
27
27
|
}
|
|
28
28
|
type Override = (ctx: OverrideContext) => void;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Per-registration flags carried alongside the id.
|
|
32
|
+
*
|
|
33
|
+
* - `expose` — force the id into the emitted document even when no endpoint
|
|
34
|
+
* references it. The author deliberately wants it in `components.schemas`
|
|
35
|
+
* (e.g. for out-of-band client codegen). Default exposure is otherwise
|
|
36
|
+
* reachability-scoped, so an unreferenced schema is pruned unless flagged.
|
|
37
|
+
* - `anonymous` — the id is a synthetic placeholder for a schema with no
|
|
38
|
+
* resolvable id (passed inline to `@ZodResponse` / `@ZodBody`). It exists
|
|
39
|
+
* only to carry the body through bulk emission under the document's
|
|
40
|
+
* `strict` / `override` options; `inlineAnonymousBodies` later inlines the
|
|
41
|
+
* body at each `$ref` site and prunes the component, so the synthetic id
|
|
42
|
+
* never reaches the final document.
|
|
43
|
+
*
|
|
44
|
+
* Both flags are sticky — once set for an id they stay set, so a later plain
|
|
45
|
+
* `register` of the same id (e.g. the idempotent re-register inside
|
|
46
|
+
* `createZodDto`) doesn't clear them.
|
|
47
|
+
*/
|
|
48
|
+
interface RegisterFlags {
|
|
49
|
+
readonly expose?: boolean;
|
|
50
|
+
readonly anonymous?: boolean;
|
|
51
|
+
}
|
|
30
52
|
interface ZodNestRegistry {
|
|
31
53
|
readonly zodRegistry: typeof z.globalRegistry;
|
|
32
|
-
register(schema: z.ZodType, id: string): void;
|
|
54
|
+
register(schema: z.ZodType, id: string, flags?: RegisterFlags): void;
|
|
33
55
|
hasCollision(id: string): boolean;
|
|
34
56
|
getCollisions(): ReadonlyMap<string, ReadonlySet<z.ZodType>>;
|
|
35
57
|
/**
|
|
@@ -42,6 +64,10 @@ interface ZodNestRegistry {
|
|
|
42
64
|
* of explicitly-registered schemas.
|
|
43
65
|
*/
|
|
44
66
|
ids(): readonly string[];
|
|
67
|
+
/** Ids registered with `{ expose: true }` — exposed regardless of usage. */
|
|
68
|
+
forceExposedIds(): readonly string[];
|
|
69
|
+
/** Ids registered with `{ anonymous: true }` — inlined + pruned by `applyZodNest`. */
|
|
70
|
+
anonymousIds(): readonly string[];
|
|
45
71
|
}
|
|
46
72
|
declare const createRegistry: () => ZodNestRegistry;
|
|
47
73
|
/** Process-wide default registry, used when no explicit `options.registry` is passed. */
|
|
@@ -49,6 +75,17 @@ declare const defaultRegistry: ZodNestRegistry;
|
|
|
49
75
|
interface RegisterSchemaOptions {
|
|
50
76
|
/** Forces this id, overriding any `.meta({ id })` already on the schema. */
|
|
51
77
|
readonly id?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Force the schema into the emitted document even when no endpoint
|
|
80
|
+
* references it. Default exposure is reachability-scoped — see
|
|
81
|
+
* {@link RegisterFlags.expose}.
|
|
82
|
+
*/
|
|
83
|
+
readonly expose?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Mark the resolved id as a synthetic anonymous placeholder — inlined and
|
|
86
|
+
* pruned by `applyZodNest`. See {@link RegisterFlags.anonymous}.
|
|
87
|
+
*/
|
|
88
|
+
readonly anonymous?: boolean;
|
|
52
89
|
}
|
|
53
90
|
/**
|
|
54
91
|
* Register a schema with the given registry, resolving its id from (in order):
|
|
@@ -175,6 +212,14 @@ interface CreateZodDtoOptions {
|
|
|
175
212
|
id?: string;
|
|
176
213
|
/** Registry to register this DTO's schema into. Defaults to `defaultRegistry`. */
|
|
177
214
|
registry?: ZodNestRegistry;
|
|
215
|
+
/**
|
|
216
|
+
* Force this DTO's schema into the emitted document even when no endpoint
|
|
217
|
+
* references it. Exposure is otherwise reachability-scoped — a registered
|
|
218
|
+
* schema that no exposed endpoint reaches is pruned. Set `true` to keep an
|
|
219
|
+
* intentionally-documented schema (e.g. for out-of-band client codegen).
|
|
220
|
+
* Defaults to `false`.
|
|
221
|
+
*/
|
|
222
|
+
expose?: boolean;
|
|
178
223
|
}
|
|
179
224
|
/**
|
|
180
225
|
* The class type returned by `createZodDto`. Instance type infers to the
|
|
@@ -616,12 +661,34 @@ interface ZodQueryOptions {
|
|
|
616
661
|
readonly id?: string;
|
|
617
662
|
/** Registry to register into. Defaults to `defaultRegistry`. */
|
|
618
663
|
readonly registry?: ZodNestRegistry;
|
|
664
|
+
/**
|
|
665
|
+
* Override how this query DTO is represented in the OpenAPI doc, taking
|
|
666
|
+
* precedence over `applyZodNest`'s `queryParamStyle`:
|
|
667
|
+
*
|
|
668
|
+
* - `true` — one single schema-based query parameter that `$ref`s the DTO's
|
|
669
|
+
* `components.schemas` entry (`style: 'form'`, `explode: true`).
|
|
670
|
+
* - `false` — one parameter per top-level property.
|
|
671
|
+
* - unset — follow the global `queryParamStyle` preference (default `'expand'`).
|
|
672
|
+
*
|
|
673
|
+
* Ref mode needs a named schema to reference: `ref: true` on an anonymous
|
|
674
|
+
* schema (no `.meta({ id })` and no `id` option) throws `ZodNestError`.
|
|
675
|
+
*/
|
|
676
|
+
readonly ref?: boolean;
|
|
619
677
|
}
|
|
620
678
|
/**
|
|
621
|
-
* Method-level decorator
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
679
|
+
* Method-level decorator describing a `@Query()` object schema as OpenAPI
|
|
680
|
+
* query parameters.
|
|
681
|
+
*
|
|
682
|
+
* When the schema is named (`.meta({ id })` or the `id` option), the decorator
|
|
683
|
+
* registers it and emits a single deferred marker, leaving the expand-vs-`$ref`
|
|
684
|
+
* decision to `applyZodNest` — so an unset `ref` follows the global
|
|
685
|
+
* `queryParamStyle` preference, `ref: true` collapses to one `$ref` query
|
|
686
|
+
* parameter, and `ref: false` expands per property. This mirrors the
|
|
687
|
+
* `@Query() dto` (`createZodDto`) path, which flows through the same marker.
|
|
688
|
+
*
|
|
689
|
+
* When the schema is anonymous, there is no component to reference, so the
|
|
690
|
+
* decorator expands one `@ApiQuery` per property immediately (and rejects
|
|
691
|
+
* `ref: true`).
|
|
625
692
|
*
|
|
626
693
|
* The user remains responsible for binding the actual query values to the
|
|
627
694
|
* handler, typically via `@Query(new ZodValidationPipe(schema)) q: z.infer<typeof schema>`.
|
|
@@ -720,13 +787,22 @@ declare class ZodNestModule {
|
|
|
720
787
|
static forRoot(options?: ZodNestModuleOptions): DynamicModule;
|
|
721
788
|
}
|
|
722
789
|
|
|
790
|
+
/**
|
|
791
|
+
* How a named `@Query()` / `@ZodQuery` DTO is represented in the OpenAPI doc:
|
|
792
|
+
*
|
|
793
|
+
* - `'expand'` (default) — one parameter per top-level property of the DTO
|
|
794
|
+
* schema, each inlined or `$ref`'d independently.
|
|
795
|
+
* - `'ref'` — a single schema-based query parameter that references the DTO's
|
|
796
|
+
* `components.schemas` entry (`style: 'form'`, `explode: true`,
|
|
797
|
+
* `schema: { $ref }`). The wire format is identical to `'expand'`; only the
|
|
798
|
+
* document representation collapses to the shared component.
|
|
799
|
+
*
|
|
800
|
+
* Query-only: path / header / cookie markers always expand regardless of this
|
|
801
|
+
* setting, since the form-exploded-object pattern is a query serialization.
|
|
802
|
+
*/
|
|
803
|
+
type QueryParamStyle = 'expand' | 'ref';
|
|
804
|
+
|
|
723
805
|
interface ApplyZodNestOptions {
|
|
724
|
-
/**
|
|
725
|
-
* The NestJS app instance. Required so `applyZodNest` can walk controllers
|
|
726
|
-
* via `DiscoveryService` to pick up `@ZodResponse` output-side DTO usage —
|
|
727
|
-
* `@nestjs/swagger` is currently anemic on response shapes.
|
|
728
|
-
*/
|
|
729
|
-
app: INestApplication;
|
|
730
806
|
/**
|
|
731
807
|
* `ZodNestRegistry` instance that holds the zod-nest DTOs. Defaults to
|
|
732
808
|
* `defaultRegistry` (the process-wide singleton populated by `createZodDto`).
|
|
@@ -741,6 +817,20 @@ interface ApplyZodNestOptions {
|
|
|
741
817
|
* Set to `false` to emit `{}` for those instead.
|
|
742
818
|
*/
|
|
743
819
|
strict?: boolean;
|
|
820
|
+
/**
|
|
821
|
+
* How named `@Query()` / `@ZodQuery` DTOs are represented in the document
|
|
822
|
+
* (default `'expand'`):
|
|
823
|
+
*
|
|
824
|
+
* - `'expand'` — one query parameter per top-level property of the DTO.
|
|
825
|
+
* - `'ref'` — a single schema-based query parameter referencing the DTO's
|
|
826
|
+
* `components.schemas` entry (`style: 'form'`, `explode: true`). The wire
|
|
827
|
+
* format is unchanged; only the spec representation collapses to the
|
|
828
|
+
* shared component. Note: Swagger UI renders the two forms differently.
|
|
829
|
+
*
|
|
830
|
+
* Query-only — path / header / cookie DTOs always expand. A per-handler
|
|
831
|
+
* `@ZodQuery({ ref })` override takes precedence over this preference.
|
|
832
|
+
*/
|
|
833
|
+
queryParamStyle?: QueryParamStyle;
|
|
744
834
|
}
|
|
745
835
|
/**
|
|
746
836
|
* Post-processor over the OpenAPI document emitted by
|
|
@@ -752,23 +842,29 @@ interface ApplyZodNestOptions {
|
|
|
752
842
|
* keyed by the marker's `dtoId` (renaming as needed).
|
|
753
843
|
* - Every `@Query()` / `@Param()` / `@Headers()` / `@Cookie()` marker
|
|
754
844
|
* parameter is expanded into one parameter per top-level property of the
|
|
755
|
-
* DTO's schema (`expandParamMarkers`)
|
|
756
|
-
*
|
|
757
|
-
*
|
|
845
|
+
* DTO's schema (`expandParamMarkers`) — except `@Query()` DTOs under
|
|
846
|
+
* `queryParamStyle: 'ref'` (or a `@ZodQuery({ ref: true })` override), which
|
|
847
|
+
* collapse to a single `$ref` schema-based query parameter. The synthetic
|
|
848
|
+
* `components.schemas.Object` that `@nestjs/swagger` materialises for the
|
|
849
|
+
* marker placeholder is pruned when it has no remaining referrers.
|
|
758
850
|
* - The I/O suffix truth table is applied — equal input/output bodies collapse
|
|
759
851
|
* to one `components.schemas[id]`; divergent bodies split as
|
|
760
852
|
* `id` (input) + `idOutput` (output), with response-side refs rewritten.
|
|
853
|
+
* - Anonymous body/response schemas (no `.meta({ id })`) are inlined at their
|
|
854
|
+
* `$ref` sites and their synthetic components pruned (`inlineAnonymousBodies`).
|
|
855
|
+
* - Only schemas reachable from this document's endpoints (plus their
|
|
856
|
+
* transitive `$ref` deps, plus any `{ expose: true }` opt-ins) are kept —
|
|
857
|
+
* unreferenced registered schemas are pruned. Exposure is document-scoped, so
|
|
858
|
+
* several documents sharing one registry each carry only what they use.
|
|
761
859
|
* - Every `$ref` whose target is missing throws `ZodNestDocumentError(DANGLING_REF)`.
|
|
762
860
|
* - `doc.openapi` is set to `'3.1.0'` — zod-nest emits OpenAPI 3.1 only; this
|
|
763
861
|
* guarantees the version string matches the emitted body regardless of the
|
|
764
862
|
* `DocumentBuilder` configuration on the caller side.
|
|
765
863
|
*
|
|
766
864
|
* Composable with other doc-transform passes — apply other mutations before
|
|
767
|
-
* or after this function.
|
|
768
|
-
* output-side DTO usage lives on controller-method metadata that the doc
|
|
769
|
-
* doesn't surface; `DiscoveryService` resolves it.
|
|
865
|
+
* or after this function.
|
|
770
866
|
*/
|
|
771
|
-
declare const applyZodNest: (doc: OpenAPIObject, opts
|
|
867
|
+
declare const applyZodNest: (doc: OpenAPIObject, opts?: ApplyZodNestOptions) => OpenAPIObject;
|
|
772
868
|
|
|
773
869
|
type ZodNestDocumentErrorCode = 'AMBIGUOUS_RENAME' | 'DANGLING_REF' | 'UNEXPANDABLE_PARAM_DTO';
|
|
774
870
|
/**
|
|
@@ -795,4 +891,4 @@ declare class ZodNestDocumentError extends ZodNestError {
|
|
|
795
891
|
constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
|
|
796
892
|
}
|
|
797
893
|
|
|
798
|
-
export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, DEFAULT_STREAM_CONTENT_TYPES, 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 ZodResponseEntry, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, isZodSchema, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
|
|
894
|
+
export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, DEFAULT_STREAM_CONTENT_TYPES, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterFlags, 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 ZodResponseEntry, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, isZodSchema, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
|