@voznov/zod-dto-nestjs 0.2.1 → 0.2.2

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
@@ -1,6 +1,6 @@
1
1
  # @voznov/zod-dto-nestjs
2
2
 
3
- NestJS adapter for `@voznov/zod-dto` — validation pipe + automatic Swagger integration.
3
+ NestJS adapter for [`@voznov/zod-dto`](https://www.npmjs.com/package/@voznov/zod-dto) — validation pipe + automatic Swagger integration.
4
4
 
5
5
  ## Install
6
6
 
package/dist/index.cjs CHANGED
@@ -248,7 +248,7 @@ ZodValidationPipe = __decorateClass([
248
248
  ], ZodValidationPipe);
249
249
 
250
250
  // src/index.ts
251
- (0, import_zod_dto3.registerOnCreate)(applySwaggerDecorators);
251
+ (0, import_zod_dto3.registerOnCreate)((dto) => void Promise.resolve().then(() => applySwaggerDecorators(dto)));
252
252
  // Annotate the CommonJS export names for ESM import in node:
253
253
  0 && (module.exports = {
254
254
  ZodValidationPipe,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/swagger.ts","../src/utils.ts","../src/pipe.ts"],"sourcesContent":["import { registerOnCreate } from '@voznov/zod-dto';\nimport { applySwaggerDecorators } from './swagger';\n\n// Auto-register swagger decoration on every ZodDto() creation when this package is imported.\nregisterOnCreate(applySwaggerDecorators);\n\nexport { ZodValidationPipe } from './pipe';\nexport type { ZodValidationPipeOptions } from './pipe';\nexport { applySwaggerDecorators } from './swagger';\n","import { ApiExtraModels, ApiProperty, type ApiPropertyOptions, refs } from '@nestjs/swagger';\nimport { type SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';\nimport { isZodDtoClass, type ZodDtoClass } from '@voznov/zod-dto';\nimport { z } from 'zod';\nimport { mapValues } from './utils';\n\nconst schemaObjectToApiPropertyOptions = (so: SchemaObject, selfRequired: boolean): ApiPropertyOptions => {\n if ('oneOf' in so || 'anyOf' in so || 'allOf' in so) {\n return { ...so, type: Array, required: selfRequired };\n }\n\n return { ...so, required: selfRequired } as ApiPropertyOptions;\n};\n\nconst leaf = (so: SchemaObject): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => ({\n so,\n selfRequired: true,\n innerSchemas: new Set(),\n});\n\nconst decoratedDtoClasses = new Set<ZodDtoClass>();\n\n// Snapshot resolved default per schema instance — Zod's `defaultValue` is a getter that\n// re-invokes the thunk on every access. Without this cache, the same `ZodDefault` schema\n// surfaced through two paths (JS subclass, `.extend/.pick/.omit`, shared field reuse, ...)\n// would emit a different value into each occurrence in the spec.\nconst defaultValueCache = new WeakMap<z.ZodDefault, unknown>();\n\n// `z.lazy(() => self)` is the only way to introduce a real cycle in a Zod schema graph.\n// Track each ZodLazy currently being walked so we can break re-entry without overflowing.\nconst lazyInProgress = new Set<z.ZodLazy>();\n\nexport const applySwaggerDecorators = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n const result = applyDecoratorsImpl(schema);\n // `.describe(text)` → OpenAPI `description`. Read at every recursion level so the text\n // lands in the spec whether it's set on a wrapper (`.optional().describe(...)`) or on\n // the inner type (`.describe(...).optional()`).\n const description = (schema as z.ZodType).description;\n if (description) result.so = { ...result.so, description };\n\n return result;\n};\n\nconst applyDecoratorsImpl = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n // --- Objects ---\n\n if (schema instanceof z.ZodObject) {\n if (isZodDtoClass(schema) && decoratedDtoClasses.has(schema)) {\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n const properties: Record<string, SchemaObject> = {};\n const required: string[] = [];\n const innerSchemas = new Set<ZodDtoClass>();\n Object.entries<z.core.$ZodType>(schema.shape).forEach(([key, fieldSchema]) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(fieldSchema);\n properties[key] = so;\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n if (selfRequired) {\n required.push(key);\n }\n });\n\n if (isZodDtoClass(schema)) {\n decoratedDtoClasses.add(schema);\n for (const [key, propertySo] of Object.entries(properties)) {\n ApiProperty(schemaObjectToApiPropertyOptions(propertySo, required.includes(key)))(schema.prototype, key);\n }\n ApiExtraModels(...[...innerSchemas.values()].filter((innerSchema) => innerSchema !== schema))(schema);\n\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n return { so: { type: 'object', properties: mapValues(properties, (so) => (so.oneOf?.length === 1 ? so.oneOf[0] : so)), required }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodRecord) {\n const { so } = applySwaggerDecorators(schema._zod.def.valueType);\n\n return leaf({ type: 'object', additionalProperties: so.oneOf?.length === 1 ? so.oneOf[0] : so });\n }\n\n // --- Wrappers (unwrap to inner type) ---\n\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodExactOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: false };\n }\n\n if (schema instanceof z.ZodDefault) {\n const inner = applySwaggerDecorators(schema.unwrap());\n if (!defaultValueCache.has(schema)) defaultValueCache.set(schema, schema._zod.def.defaultValue);\n\n return { ...inner, so: { ...inner.so, default: defaultValueCache.get(schema) }, selfRequired: false };\n }\n\n if (schema instanceof z.ZodLazy) {\n // `z.lazy(() => self)` is the only way Zod schemas can carry an actual cycle.\n // Track each ZodLazy instance currently being walked; on re-entry, emit a $ref\n // back to the DTO that the lazy resolves to (if any), or a permissive `{}` as a\n // last resort for anonymous self-referential trees.\n if (lazyInProgress.has(schema)) {\n const inner = schema.unwrap();\n if (isZodDtoClass(inner)) return { so: { oneOf: refs(inner) }, selfRequired: true, innerSchemas: new Set([inner]) };\n\n return leaf({});\n }\n lazyInProgress.add(schema);\n try {\n return applySwaggerDecorators(schema.unwrap());\n } finally {\n lazyInProgress.delete(schema);\n }\n }\n\n if (schema instanceof z.ZodReadonly || schema instanceof z.ZodCatch) {\n return applySwaggerDecorators(schema.unwrap());\n }\n\n if (schema instanceof z.ZodNonOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: true };\n }\n\n if (schema instanceof z.ZodNullable) {\n const result = applySwaggerDecorators(schema.unwrap());\n\n return { ...result, so: { ...result.so, nullable: true } };\n }\n\n if (schema instanceof z.ZodPipe) {\n // z.preprocess() creates a Pipe(in: ZodTransform, out: schema).\n // For swagger, skip the transform and process the output schema directly.\n const inner = schema.in instanceof z.ZodTransform ? schema.out : schema.in;\n\n return applySwaggerDecorators(inner);\n }\n\n if (schema instanceof z.ZodTransform) {\n return leaf({});\n }\n\n // --- Arrays & tuples ---\n\n if (schema instanceof z.ZodArray) {\n const element = schema.unwrap();\n const { so, selfRequired: selfRequiredElement, innerSchemas } = applySwaggerDecorators(element);\n if (!selfRequiredElement) {\n throw new Error('Not required array item is not supported in Swagger. Use nullable instead.');\n }\n\n return { so: { type: 'array', items: so.oneOf?.length === 1 ? so.oneOf[0] : so }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodTuple) {\n const itemSchemas = schema._zod.def.items.map((item) => {\n const { so } = applySwaggerDecorators(item);\n\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n // Deduplicate by JSON representation to collapse identical types\n const unique = [...new Map(itemSchemas.map((s) => [JSON.stringify(s), s])).values()];\n const items = unique.length === 1 ? unique[0] : { oneOf: unique };\n\n return leaf({ type: 'array', items, minItems: itemSchemas.length, maxItems: itemSchemas.length });\n }\n\n // --- Scalars ---\n\n // ZodString: plain z.string(). ZodStringFormat: z.email(), z.uuid(), z.url(), z.ipv4(), etc.\n // Both share the same properties (minLength, maxLength, format).\n if (schema instanceof z.ZodString || schema instanceof z.ZodStringFormat) {\n const so: SchemaObject = { type: 'string' };\n if (schema.minLength !== null) so.minLength = schema.minLength;\n if (schema.maxLength !== null) so.maxLength = schema.maxLength;\n const fmt = schema.format;\n if (fmt !== null) {\n if (fmt === 'regex') {\n // Grab the first regex pattern from the internal bag.\n const patterns = schema._zod.bag.patterns;\n const first = patterns ? [...patterns][0] : undefined;\n if (first) so.pattern = first.source;\n } else {\n so.format = fmt;\n }\n }\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n // Number and integer subtypes (ZodInt, ZodNumberFormat, etc.) — all instanceof ZodNumber.\n if (schema instanceof z.ZodNumber) {\n const fmt = schema.format;\n const isInt = fmt === 'int32' || fmt === 'uint32' || fmt === 'safeint';\n const so: SchemaObject = { type: isInt ? 'integer' : 'number' };\n if (schema.minValue !== null && schema.minValue !== Number.MIN_SAFE_INTEGER && Number.isFinite(schema.minValue)) so.minimum = schema.minValue;\n if (schema.maxValue !== null && schema.maxValue !== Number.MAX_SAFE_INTEGER && Number.isFinite(schema.maxValue)) so.maximum = schema.maxValue;\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n if (schema instanceof z.ZodBigInt) {\n return leaf({ type: 'integer', format: 'int64' });\n }\n\n if (schema instanceof z.ZodBoolean) {\n return leaf({ type: 'boolean' });\n }\n\n if (schema instanceof z.ZodDate) {\n return leaf({ type: 'string', format: 'date-time' });\n }\n\n if (schema instanceof z.ZodNull) {\n return { so: { nullable: true }, selfRequired: true, innerSchemas: new Set() };\n }\n\n // --- Enums & literals ---\n\n if (schema instanceof z.ZodEnum) {\n return leaf({ enum: schema.options });\n }\n\n if (schema instanceof z.ZodLiteral) {\n return leaf({ enum: [...schema.values] });\n }\n\n // --- Unions & intersections ---\n\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const innerSchemas = new Set<ZodDtoClass>();\n const oneOf = schema.options.map((option) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(option);\n\n if (!selfRequired) {\n throw new Error('Not required option in oneOf is not supported in Swagger. Use nullable instead.');\n }\n\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n\n // Flatten { oneOf: [single_ref] } → just the ref, so union of DTO refs stays clean.\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n return { so: { oneOf }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodIntersection) {\n const left = applySwaggerDecorators(schema._zod.def.left as z.ZodType);\n const right = applySwaggerDecorators(schema._zod.def.right as z.ZodType);\n const innerSchemas = new Set([...left.innerSchemas, ...right.innerSchemas]);\n const leftSo = left.so.oneOf?.length === 1 ? left.so.oneOf[0] : left.so;\n const rightSo = right.so.oneOf?.length === 1 ? right.so.oneOf[0] : right.so;\n\n return { so: { allOf: [leftSo, rightSo] }, selfRequired: true, innerSchemas };\n }\n\n // --- Catch-all ---\n\n if (schema instanceof z.ZodAny || schema instanceof z.ZodUnknown) {\n return leaf({});\n }\n\n if (schema instanceof z.ZodUndefined || schema instanceof z.ZodVoid || schema instanceof z.ZodNever) {\n throw new Error(`applySwaggerDecorators: ${(schema as z.ZodType).def.type} cannot be represented in JSON`);\n }\n\n throw new Error(`applySwaggerDecorators: unsupported Zod type \"${(schema as z.ZodType).def.type}\"`);\n};\n","export const mapValues = <K extends string, V, R>(obj: Record<K, V>, fn: (value: V, key: K) => R): Record<K, R> =>\n Object.fromEntries(Object.entries<V>(obj).map(([key, value]) => [key, fn(value, key as K)])) as Record<K, R>;\n","import { BadRequestException, Injectable, type PipeTransform } from '@nestjs/common';\nimport { type ArgumentMetadata } from '@nestjs/common/interfaces';\nimport { isZodDtoClass, toDto, ZodDtoValidationError } from '@voznov/zod-dto';\n\nexport interface ZodValidationPipeOptions {\n createError?: (issues: string[]) => Error;\n}\n\n@Injectable()\nexport class ZodValidationPipe implements PipeTransform {\n private readonly createError: (issues: string[]) => Error;\n\n constructor(options?: ZodValidationPipeOptions) {\n this.createError = options?.createError ?? ((issues) => new BadRequestException(issues));\n }\n\n transform(value: unknown, { metatype }: ArgumentMetadata): unknown {\n if (!metatype || !isZodDtoClass(metatype)) {\n return value;\n }\n\n try {\n return toDto(metatype, value);\n } catch (error) {\n if (error instanceof ZodDtoValidationError) {\n throw this.createError(error.issues);\n }\n throw error;\n }\n }\n}\n"],"mappings":"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","names":["import_zod_dto","import_zod_dto"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/swagger.ts","../src/utils.ts","../src/pipe.ts"],"sourcesContent":["import { registerOnCreate } from '@voznov/zod-dto';\nimport { applySwaggerDecorators } from './swagger';\n\n// Deferred to a microtask so self-referential DTOs (`lazyDto<X>(() => X)`) resolve past TDZ.\nregisterOnCreate((dto) => void Promise.resolve().then(() => applySwaggerDecorators(dto)));\n\nexport { ZodValidationPipe } from './pipe';\nexport type { ZodValidationPipeOptions } from './pipe';\nexport { applySwaggerDecorators } from './swagger';\n","import { ApiExtraModels, ApiProperty, type ApiPropertyOptions, refs } from '@nestjs/swagger';\nimport { type SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';\nimport { isZodDtoClass, type ZodDtoClass } from '@voznov/zod-dto';\nimport { z } from 'zod';\nimport { mapValues } from './utils';\n\nconst schemaObjectToApiPropertyOptions = (so: SchemaObject, selfRequired: boolean): ApiPropertyOptions => {\n if ('oneOf' in so || 'anyOf' in so || 'allOf' in so) {\n return { ...so, type: Array, required: selfRequired };\n }\n\n return { ...so, required: selfRequired } as ApiPropertyOptions;\n};\n\nconst leaf = (so: SchemaObject): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => ({\n so,\n selfRequired: true,\n innerSchemas: new Set(),\n});\n\nconst decoratedDtoClasses = new Set<ZodDtoClass>();\n\n// Snapshot resolved default per schema instance — Zod's `defaultValue` is a getter that\n// re-invokes the thunk on every access. Without this cache, the same `ZodDefault` schema\n// surfaced through two paths (JS subclass, `.extend/.pick/.omit`, shared field reuse, ...)\n// would emit a different value into each occurrence in the spec.\nconst defaultValueCache = new WeakMap<z.ZodDefault, unknown>();\n\n// `z.lazy(() => self)` is the only way to introduce a real cycle in a Zod schema graph.\n// Track each ZodLazy currently being walked so we can break re-entry without overflowing.\nconst lazyInProgress = new Set<z.ZodLazy>();\n\nexport const applySwaggerDecorators = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n const result = applyDecoratorsImpl(schema);\n // `.describe(text)` → OpenAPI `description`. Read at every recursion level so the text\n // lands in the spec whether it's set on a wrapper (`.optional().describe(...)`) or on\n // the inner type (`.describe(...).optional()`).\n const description = (schema as z.ZodType).description;\n if (description) result.so = { ...result.so, description };\n\n return result;\n};\n\nconst applyDecoratorsImpl = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n // --- Objects ---\n\n if (schema instanceof z.ZodObject) {\n if (isZodDtoClass(schema) && decoratedDtoClasses.has(schema)) {\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n const properties: Record<string, SchemaObject> = {};\n const required: string[] = [];\n const innerSchemas = new Set<ZodDtoClass>();\n Object.entries<z.core.$ZodType>(schema.shape).forEach(([key, fieldSchema]) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(fieldSchema);\n properties[key] = so;\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n if (selfRequired) {\n required.push(key);\n }\n });\n\n if (isZodDtoClass(schema)) {\n decoratedDtoClasses.add(schema);\n for (const [key, propertySo] of Object.entries(properties)) {\n ApiProperty(schemaObjectToApiPropertyOptions(propertySo, required.includes(key)))(schema.prototype, key);\n }\n ApiExtraModels(...[...innerSchemas.values()].filter((innerSchema) => innerSchema !== schema))(schema);\n\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n return { so: { type: 'object', properties: mapValues(properties, (so) => (so.oneOf?.length === 1 ? so.oneOf[0] : so)), required }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodRecord) {\n const { so } = applySwaggerDecorators(schema._zod.def.valueType);\n\n return leaf({ type: 'object', additionalProperties: so.oneOf?.length === 1 ? so.oneOf[0] : so });\n }\n\n // --- Wrappers (unwrap to inner type) ---\n\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodExactOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: false };\n }\n\n if (schema instanceof z.ZodDefault) {\n const inner = applySwaggerDecorators(schema.unwrap());\n if (!defaultValueCache.has(schema)) defaultValueCache.set(schema, schema._zod.def.defaultValue);\n\n return { ...inner, so: { ...inner.so, default: defaultValueCache.get(schema) }, selfRequired: false };\n }\n\n if (schema instanceof z.ZodLazy) {\n // `z.lazy(() => self)` is the only way Zod schemas can carry an actual cycle.\n // Track each ZodLazy instance currently being walked; on re-entry, emit a $ref\n // back to the DTO that the lazy resolves to (if any), or a permissive `{}` as a\n // last resort for anonymous self-referential trees.\n if (lazyInProgress.has(schema)) {\n const inner = schema.unwrap();\n if (isZodDtoClass(inner)) return { so: { oneOf: refs(inner) }, selfRequired: true, innerSchemas: new Set([inner]) };\n\n return leaf({});\n }\n lazyInProgress.add(schema);\n try {\n return applySwaggerDecorators(schema.unwrap());\n } finally {\n lazyInProgress.delete(schema);\n }\n }\n\n if (schema instanceof z.ZodReadonly || schema instanceof z.ZodCatch) {\n return applySwaggerDecorators(schema.unwrap());\n }\n\n if (schema instanceof z.ZodNonOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: true };\n }\n\n if (schema instanceof z.ZodNullable) {\n const result = applySwaggerDecorators(schema.unwrap());\n\n return { ...result, so: { ...result.so, nullable: true } };\n }\n\n if (schema instanceof z.ZodPipe) {\n // z.preprocess() creates a Pipe(in: ZodTransform, out: schema).\n // For swagger, skip the transform and process the output schema directly.\n const inner = schema.in instanceof z.ZodTransform ? schema.out : schema.in;\n\n return applySwaggerDecorators(inner);\n }\n\n if (schema instanceof z.ZodTransform) {\n return leaf({});\n }\n\n // --- Arrays & tuples ---\n\n if (schema instanceof z.ZodArray) {\n const element = schema.unwrap();\n const { so, selfRequired: selfRequiredElement, innerSchemas } = applySwaggerDecorators(element);\n if (!selfRequiredElement) {\n throw new Error('Not required array item is not supported in Swagger. Use nullable instead.');\n }\n\n return { so: { type: 'array', items: so.oneOf?.length === 1 ? so.oneOf[0] : so }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodTuple) {\n const itemSchemas = schema._zod.def.items.map((item) => {\n const { so } = applySwaggerDecorators(item);\n\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n // Deduplicate by JSON representation to collapse identical types\n const unique = [...new Map(itemSchemas.map((s) => [JSON.stringify(s), s])).values()];\n const items = unique.length === 1 ? unique[0] : { oneOf: unique };\n\n return leaf({ type: 'array', items, minItems: itemSchemas.length, maxItems: itemSchemas.length });\n }\n\n // --- Scalars ---\n\n // ZodString: plain z.string(). ZodStringFormat: z.email(), z.uuid(), z.url(), z.ipv4(), etc.\n // Both share the same properties (minLength, maxLength, format).\n if (schema instanceof z.ZodString || schema instanceof z.ZodStringFormat) {\n const so: SchemaObject = { type: 'string' };\n if (schema.minLength !== null) so.minLength = schema.minLength;\n if (schema.maxLength !== null) so.maxLength = schema.maxLength;\n const fmt = schema.format;\n if (fmt !== null) {\n if (fmt === 'regex') {\n // Grab the first regex pattern from the internal bag.\n const patterns = schema._zod.bag.patterns;\n const first = patterns ? [...patterns][0] : undefined;\n if (first) so.pattern = first.source;\n } else {\n so.format = fmt;\n }\n }\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n // Number and integer subtypes (ZodInt, ZodNumberFormat, etc.) — all instanceof ZodNumber.\n if (schema instanceof z.ZodNumber) {\n const fmt = schema.format;\n const isInt = fmt === 'int32' || fmt === 'uint32' || fmt === 'safeint';\n const so: SchemaObject = { type: isInt ? 'integer' : 'number' };\n if (schema.minValue !== null && schema.minValue !== Number.MIN_SAFE_INTEGER && Number.isFinite(schema.minValue)) so.minimum = schema.minValue;\n if (schema.maxValue !== null && schema.maxValue !== Number.MAX_SAFE_INTEGER && Number.isFinite(schema.maxValue)) so.maximum = schema.maxValue;\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n if (schema instanceof z.ZodBigInt) {\n return leaf({ type: 'integer', format: 'int64' });\n }\n\n if (schema instanceof z.ZodBoolean) {\n return leaf({ type: 'boolean' });\n }\n\n if (schema instanceof z.ZodDate) {\n return leaf({ type: 'string', format: 'date-time' });\n }\n\n if (schema instanceof z.ZodNull) {\n return { so: { nullable: true }, selfRequired: true, innerSchemas: new Set() };\n }\n\n // --- Enums & literals ---\n\n if (schema instanceof z.ZodEnum) {\n return leaf({ enum: schema.options });\n }\n\n if (schema instanceof z.ZodLiteral) {\n return leaf({ enum: [...schema.values] });\n }\n\n // --- Unions & intersections ---\n\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const innerSchemas = new Set<ZodDtoClass>();\n const oneOf = schema.options.map((option) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(option);\n\n if (!selfRequired) {\n throw new Error('Not required option in oneOf is not supported in Swagger. Use nullable instead.');\n }\n\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n\n // Flatten { oneOf: [single_ref] } → just the ref, so union of DTO refs stays clean.\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n return { so: { oneOf }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodIntersection) {\n const left = applySwaggerDecorators(schema._zod.def.left as z.ZodType);\n const right = applySwaggerDecorators(schema._zod.def.right as z.ZodType);\n const innerSchemas = new Set([...left.innerSchemas, ...right.innerSchemas]);\n const leftSo = left.so.oneOf?.length === 1 ? left.so.oneOf[0] : left.so;\n const rightSo = right.so.oneOf?.length === 1 ? right.so.oneOf[0] : right.so;\n\n return { so: { allOf: [leftSo, rightSo] }, selfRequired: true, innerSchemas };\n }\n\n // --- Catch-all ---\n\n if (schema instanceof z.ZodAny || schema instanceof z.ZodUnknown) {\n return leaf({});\n }\n\n if (schema instanceof z.ZodUndefined || schema instanceof z.ZodVoid || schema instanceof z.ZodNever) {\n throw new Error(`applySwaggerDecorators: ${(schema as z.ZodType).def.type} cannot be represented in JSON`);\n }\n\n throw new Error(`applySwaggerDecorators: unsupported Zod type \"${(schema as z.ZodType).def.type}\"`);\n};\n","export const mapValues = <K extends string, V, R>(obj: Record<K, V>, fn: (value: V, key: K) => R): Record<K, R> =>\n Object.fromEntries(Object.entries<V>(obj).map(([key, value]) => [key, fn(value, key as K)])) as Record<K, R>;\n","import { BadRequestException, Injectable, type PipeTransform } from '@nestjs/common';\nimport { type ArgumentMetadata } from '@nestjs/common/interfaces';\nimport { isZodDtoClass, toDto, ZodDtoValidationError } from '@voznov/zod-dto';\n\nexport interface ZodValidationPipeOptions {\n createError?: (issues: string[]) => Error;\n}\n\n@Injectable()\nexport class ZodValidationPipe implements PipeTransform {\n private readonly createError: (issues: string[]) => Error;\n\n constructor(options?: ZodValidationPipeOptions) {\n this.createError = options?.createError ?? ((issues) => new BadRequestException(issues));\n }\n\n transform(value: unknown, { metatype }: ArgumentMetadata): unknown {\n if (!metatype || !isZodDtoClass(metatype)) {\n return value;\n }\n\n try {\n return toDto(metatype, value);\n } catch (error) {\n if (error instanceof ZodDtoValidationError) {\n throw this.createError(error.issues);\n }\n throw error;\n }\n }\n}\n"],"mappings":"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","names":["import_zod_dto","import_zod_dto"]}
package/dist/index.js CHANGED
@@ -226,7 +226,7 @@ ZodValidationPipe = __decorateClass([
226
226
  ], ZodValidationPipe);
227
227
 
228
228
  // src/index.ts
229
- registerOnCreate(applySwaggerDecorators);
229
+ registerOnCreate((dto) => void Promise.resolve().then(() => applySwaggerDecorators(dto)));
230
230
  export {
231
231
  ZodValidationPipe,
232
232
  applySwaggerDecorators
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/swagger.ts","../src/utils.ts","../src/pipe.ts"],"sourcesContent":["import { registerOnCreate } from '@voznov/zod-dto';\nimport { applySwaggerDecorators } from './swagger';\n\n// Auto-register swagger decoration on every ZodDto() creation when this package is imported.\nregisterOnCreate(applySwaggerDecorators);\n\nexport { ZodValidationPipe } from './pipe';\nexport type { ZodValidationPipeOptions } from './pipe';\nexport { applySwaggerDecorators } from './swagger';\n","import { ApiExtraModels, ApiProperty, type ApiPropertyOptions, refs } from '@nestjs/swagger';\nimport { type SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';\nimport { isZodDtoClass, type ZodDtoClass } from '@voznov/zod-dto';\nimport { z } from 'zod';\nimport { mapValues } from './utils';\n\nconst schemaObjectToApiPropertyOptions = (so: SchemaObject, selfRequired: boolean): ApiPropertyOptions => {\n if ('oneOf' in so || 'anyOf' in so || 'allOf' in so) {\n return { ...so, type: Array, required: selfRequired };\n }\n\n return { ...so, required: selfRequired } as ApiPropertyOptions;\n};\n\nconst leaf = (so: SchemaObject): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => ({\n so,\n selfRequired: true,\n innerSchemas: new Set(),\n});\n\nconst decoratedDtoClasses = new Set<ZodDtoClass>();\n\n// Snapshot resolved default per schema instance — Zod's `defaultValue` is a getter that\n// re-invokes the thunk on every access. Without this cache, the same `ZodDefault` schema\n// surfaced through two paths (JS subclass, `.extend/.pick/.omit`, shared field reuse, ...)\n// would emit a different value into each occurrence in the spec.\nconst defaultValueCache = new WeakMap<z.ZodDefault, unknown>();\n\n// `z.lazy(() => self)` is the only way to introduce a real cycle in a Zod schema graph.\n// Track each ZodLazy currently being walked so we can break re-entry without overflowing.\nconst lazyInProgress = new Set<z.ZodLazy>();\n\nexport const applySwaggerDecorators = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n const result = applyDecoratorsImpl(schema);\n // `.describe(text)` → OpenAPI `description`. Read at every recursion level so the text\n // lands in the spec whether it's set on a wrapper (`.optional().describe(...)`) or on\n // the inner type (`.describe(...).optional()`).\n const description = (schema as z.ZodType).description;\n if (description) result.so = { ...result.so, description };\n\n return result;\n};\n\nconst applyDecoratorsImpl = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n // --- Objects ---\n\n if (schema instanceof z.ZodObject) {\n if (isZodDtoClass(schema) && decoratedDtoClasses.has(schema)) {\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n const properties: Record<string, SchemaObject> = {};\n const required: string[] = [];\n const innerSchemas = new Set<ZodDtoClass>();\n Object.entries<z.core.$ZodType>(schema.shape).forEach(([key, fieldSchema]) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(fieldSchema);\n properties[key] = so;\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n if (selfRequired) {\n required.push(key);\n }\n });\n\n if (isZodDtoClass(schema)) {\n decoratedDtoClasses.add(schema);\n for (const [key, propertySo] of Object.entries(properties)) {\n ApiProperty(schemaObjectToApiPropertyOptions(propertySo, required.includes(key)))(schema.prototype, key);\n }\n ApiExtraModels(...[...innerSchemas.values()].filter((innerSchema) => innerSchema !== schema))(schema);\n\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n return { so: { type: 'object', properties: mapValues(properties, (so) => (so.oneOf?.length === 1 ? so.oneOf[0] : so)), required }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodRecord) {\n const { so } = applySwaggerDecorators(schema._zod.def.valueType);\n\n return leaf({ type: 'object', additionalProperties: so.oneOf?.length === 1 ? so.oneOf[0] : so });\n }\n\n // --- Wrappers (unwrap to inner type) ---\n\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodExactOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: false };\n }\n\n if (schema instanceof z.ZodDefault) {\n const inner = applySwaggerDecorators(schema.unwrap());\n if (!defaultValueCache.has(schema)) defaultValueCache.set(schema, schema._zod.def.defaultValue);\n\n return { ...inner, so: { ...inner.so, default: defaultValueCache.get(schema) }, selfRequired: false };\n }\n\n if (schema instanceof z.ZodLazy) {\n // `z.lazy(() => self)` is the only way Zod schemas can carry an actual cycle.\n // Track each ZodLazy instance currently being walked; on re-entry, emit a $ref\n // back to the DTO that the lazy resolves to (if any), or a permissive `{}` as a\n // last resort for anonymous self-referential trees.\n if (lazyInProgress.has(schema)) {\n const inner = schema.unwrap();\n if (isZodDtoClass(inner)) return { so: { oneOf: refs(inner) }, selfRequired: true, innerSchemas: new Set([inner]) };\n\n return leaf({});\n }\n lazyInProgress.add(schema);\n try {\n return applySwaggerDecorators(schema.unwrap());\n } finally {\n lazyInProgress.delete(schema);\n }\n }\n\n if (schema instanceof z.ZodReadonly || schema instanceof z.ZodCatch) {\n return applySwaggerDecorators(schema.unwrap());\n }\n\n if (schema instanceof z.ZodNonOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: true };\n }\n\n if (schema instanceof z.ZodNullable) {\n const result = applySwaggerDecorators(schema.unwrap());\n\n return { ...result, so: { ...result.so, nullable: true } };\n }\n\n if (schema instanceof z.ZodPipe) {\n // z.preprocess() creates a Pipe(in: ZodTransform, out: schema).\n // For swagger, skip the transform and process the output schema directly.\n const inner = schema.in instanceof z.ZodTransform ? schema.out : schema.in;\n\n return applySwaggerDecorators(inner);\n }\n\n if (schema instanceof z.ZodTransform) {\n return leaf({});\n }\n\n // --- Arrays & tuples ---\n\n if (schema instanceof z.ZodArray) {\n const element = schema.unwrap();\n const { so, selfRequired: selfRequiredElement, innerSchemas } = applySwaggerDecorators(element);\n if (!selfRequiredElement) {\n throw new Error('Not required array item is not supported in Swagger. Use nullable instead.');\n }\n\n return { so: { type: 'array', items: so.oneOf?.length === 1 ? so.oneOf[0] : so }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodTuple) {\n const itemSchemas = schema._zod.def.items.map((item) => {\n const { so } = applySwaggerDecorators(item);\n\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n // Deduplicate by JSON representation to collapse identical types\n const unique = [...new Map(itemSchemas.map((s) => [JSON.stringify(s), s])).values()];\n const items = unique.length === 1 ? unique[0] : { oneOf: unique };\n\n return leaf({ type: 'array', items, minItems: itemSchemas.length, maxItems: itemSchemas.length });\n }\n\n // --- Scalars ---\n\n // ZodString: plain z.string(). ZodStringFormat: z.email(), z.uuid(), z.url(), z.ipv4(), etc.\n // Both share the same properties (minLength, maxLength, format).\n if (schema instanceof z.ZodString || schema instanceof z.ZodStringFormat) {\n const so: SchemaObject = { type: 'string' };\n if (schema.minLength !== null) so.minLength = schema.minLength;\n if (schema.maxLength !== null) so.maxLength = schema.maxLength;\n const fmt = schema.format;\n if (fmt !== null) {\n if (fmt === 'regex') {\n // Grab the first regex pattern from the internal bag.\n const patterns = schema._zod.bag.patterns;\n const first = patterns ? [...patterns][0] : undefined;\n if (first) so.pattern = first.source;\n } else {\n so.format = fmt;\n }\n }\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n // Number and integer subtypes (ZodInt, ZodNumberFormat, etc.) — all instanceof ZodNumber.\n if (schema instanceof z.ZodNumber) {\n const fmt = schema.format;\n const isInt = fmt === 'int32' || fmt === 'uint32' || fmt === 'safeint';\n const so: SchemaObject = { type: isInt ? 'integer' : 'number' };\n if (schema.minValue !== null && schema.minValue !== Number.MIN_SAFE_INTEGER && Number.isFinite(schema.minValue)) so.minimum = schema.minValue;\n if (schema.maxValue !== null && schema.maxValue !== Number.MAX_SAFE_INTEGER && Number.isFinite(schema.maxValue)) so.maximum = schema.maxValue;\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n if (schema instanceof z.ZodBigInt) {\n return leaf({ type: 'integer', format: 'int64' });\n }\n\n if (schema instanceof z.ZodBoolean) {\n return leaf({ type: 'boolean' });\n }\n\n if (schema instanceof z.ZodDate) {\n return leaf({ type: 'string', format: 'date-time' });\n }\n\n if (schema instanceof z.ZodNull) {\n return { so: { nullable: true }, selfRequired: true, innerSchemas: new Set() };\n }\n\n // --- Enums & literals ---\n\n if (schema instanceof z.ZodEnum) {\n return leaf({ enum: schema.options });\n }\n\n if (schema instanceof z.ZodLiteral) {\n return leaf({ enum: [...schema.values] });\n }\n\n // --- Unions & intersections ---\n\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const innerSchemas = new Set<ZodDtoClass>();\n const oneOf = schema.options.map((option) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(option);\n\n if (!selfRequired) {\n throw new Error('Not required option in oneOf is not supported in Swagger. Use nullable instead.');\n }\n\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n\n // Flatten { oneOf: [single_ref] } → just the ref, so union of DTO refs stays clean.\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n return { so: { oneOf }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodIntersection) {\n const left = applySwaggerDecorators(schema._zod.def.left as z.ZodType);\n const right = applySwaggerDecorators(schema._zod.def.right as z.ZodType);\n const innerSchemas = new Set([...left.innerSchemas, ...right.innerSchemas]);\n const leftSo = left.so.oneOf?.length === 1 ? left.so.oneOf[0] : left.so;\n const rightSo = right.so.oneOf?.length === 1 ? right.so.oneOf[0] : right.so;\n\n return { so: { allOf: [leftSo, rightSo] }, selfRequired: true, innerSchemas };\n }\n\n // --- Catch-all ---\n\n if (schema instanceof z.ZodAny || schema instanceof z.ZodUnknown) {\n return leaf({});\n }\n\n if (schema instanceof z.ZodUndefined || schema instanceof z.ZodVoid || schema instanceof z.ZodNever) {\n throw new Error(`applySwaggerDecorators: ${(schema as z.ZodType).def.type} cannot be represented in JSON`);\n }\n\n throw new Error(`applySwaggerDecorators: unsupported Zod type \"${(schema as z.ZodType).def.type}\"`);\n};\n","export const mapValues = <K extends string, V, R>(obj: Record<K, V>, fn: (value: V, key: K) => R): Record<K, R> =>\n Object.fromEntries(Object.entries<V>(obj).map(([key, value]) => [key, fn(value, key as K)])) as Record<K, R>;\n","import { BadRequestException, Injectable, type PipeTransform } from '@nestjs/common';\nimport { type ArgumentMetadata } from '@nestjs/common/interfaces';\nimport { isZodDtoClass, toDto, ZodDtoValidationError } from '@voznov/zod-dto';\n\nexport interface ZodValidationPipeOptions {\n createError?: (issues: string[]) => Error;\n}\n\n@Injectable()\nexport class ZodValidationPipe implements PipeTransform {\n private readonly createError: (issues: string[]) => Error;\n\n constructor(options?: ZodValidationPipeOptions) {\n this.createError = options?.createError ?? ((issues) => new BadRequestException(issues));\n }\n\n transform(value: unknown, { metatype }: ArgumentMetadata): unknown {\n if (!metatype || !isZodDtoClass(metatype)) {\n return value;\n }\n\n try {\n return toDto(metatype, value);\n } catch (error) {\n if (error instanceof ZodDtoValidationError) {\n throw this.createError(error.issues);\n }\n throw error;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,wBAAwB;;;ACAjC,SAAS,gBAAgB,aAAsC,YAAY;AAC3E,OAAkC;AAClC,SAAS,qBAAuC;AAChD,SAAS,SAAS;;;ACHX,IAAM,YAAY,CAAyB,KAAmB,OACnE,OAAO,YAAY,OAAO,QAAW,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,GAAG,OAAO,GAAQ,CAAC,CAAC,CAAC;;;ADK7F,IAAM,mCAAmC,CAAC,IAAkB,iBAA8C;AACxG,MAAI,WAAW,MAAM,WAAW,MAAM,WAAW,IAAI;AACnD,WAAO,EAAE,GAAG,IAAI,MAAM,OAAO,UAAU,aAAa;AAAA,EACtD;AAEA,SAAO,EAAE,GAAG,IAAI,UAAU,aAAa;AACzC;AAEA,IAAM,OAAO,CAAC,QAAmG;AAAA,EAC/G;AAAA,EACA,cAAc;AAAA,EACd,cAAc,oBAAI,IAAI;AACxB;AAEA,IAAM,sBAAsB,oBAAI,IAAiB;AAMjD,IAAM,oBAAoB,oBAAI,QAA+B;AAI7D,IAAM,iBAAiB,oBAAI,IAAe;AAEnC,IAAM,yBAAyB,CAAC,WAAyG;AAC9I,QAAM,SAAS,oBAAoB,MAAM;AAIzC,QAAM,cAAe,OAAqB;AAC1C,MAAI,YAAa,QAAO,KAAK,EAAE,GAAG,OAAO,IAAI,YAAY;AAEzD,SAAO;AACT;AAEA,IAAM,sBAAsB,CAAC,WAAyG;AAGpI,MAAI,kBAAkB,EAAE,WAAW;AACjC,QAAI,cAAc,MAAM,KAAK,oBAAoB,IAAI,MAAM,GAAG;AAC5D,aAAO,EAAE,IAAI,EAAE,OAAO,KAAK,MAAM,EAAE,GAAG,cAAc,MAAM,cAAc,oBAAI,IAAI,CAAC,MAAM,CAAC,EAAE;AAAA,IAC5F;AAEA,UAAM,aAA2C,CAAC;AAClD,UAAM,WAAqB,CAAC;AAC5B,UAAM,eAAe,oBAAI,IAAiB;AAC1C,WAAO,QAAyB,OAAO,KAAK,EAAE,QAAQ,CAAC,CAAC,KAAK,WAAW,MAAM;AAC5E,YAAM,EAAE,IAAI,cAAc,cAAc,cAAc,IAAI,uBAAuB,WAAW;AAC5F,iBAAW,GAAG,IAAI;AAClB,oBAAc,QAAQ,CAAC,gBAAgB,aAAa,IAAI,WAAW,CAAC;AACpE,UAAI,cAAc;AAChB,iBAAS,KAAK,GAAG;AAAA,MACnB;AAAA,IACF,CAAC;AAED,QAAI,cAAc,MAAM,GAAG;AACzB,0BAAoB,IAAI,MAAM;AAC9B,iBAAW,CAAC,KAAK,UAAU,KAAK,OAAO,QAAQ,UAAU,GAAG;AAC1D,oBAAY,iCAAiC,YAAY,SAAS,SAAS,GAAG,CAAC,CAAC,EAAE,OAAO,WAAW,GAAG;AAAA,MACzG;AACA,qBAAe,GAAG,CAAC,GAAG,aAAa,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,gBAAgB,MAAM,CAAC,EAAE,MAAM;AAEpG,aAAO,EAAE,IAAI,EAAE,OAAO,KAAK,MAAM,EAAE,GAAG,cAAc,MAAM,cAAc,oBAAI,IAAI,CAAC,MAAM,CAAC,EAAE;AAAA,IAC5F;AAEA,WAAO,EAAE,IAAI,EAAE,MAAM,UAAU,YAAY,UAAU,YAAY,CAAC,OAAQ,GAAG,OAAO,WAAW,IAAI,GAAG,MAAM,CAAC,IAAI,EAAG,GAAG,SAAS,GAAG,cAAc,MAAM,aAAa;AAAA,EACtK;AAEA,MAAI,kBAAkB,EAAE,WAAW;AACjC,UAAM,EAAE,GAAG,IAAI,uBAAuB,OAAO,KAAK,IAAI,SAAS;AAE/D,WAAO,KAAK,EAAE,MAAM,UAAU,sBAAsB,GAAG,OAAO,WAAW,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,CAAC;AAAA,EACjG;AAIA,MAAI,kBAAkB,EAAE,eAAe,kBAAkB,EAAE,kBAAkB;AAC3E,WAAO,EAAE,GAAG,uBAAuB,OAAO,OAAO,CAAC,GAAG,cAAc,MAAM;AAAA,EAC3E;AAEA,MAAI,kBAAkB,EAAE,YAAY;AAClC,UAAM,QAAQ,uBAAuB,OAAO,OAAO,CAAC;AACpD,QAAI,CAAC,kBAAkB,IAAI,MAAM,EAAG,mBAAkB,IAAI,QAAQ,OAAO,KAAK,IAAI,YAAY;AAE9F,WAAO,EAAE,GAAG,OAAO,IAAI,EAAE,GAAG,MAAM,IAAI,SAAS,kBAAkB,IAAI,MAAM,EAAE,GAAG,cAAc,MAAM;AAAA,EACtG;AAEA,MAAI,kBAAkB,EAAE,SAAS;AAK/B,QAAI,eAAe,IAAI,MAAM,GAAG;AAC9B,YAAM,QAAQ,OAAO,OAAO;AAC5B,UAAI,cAAc,KAAK,EAAG,QAAO,EAAE,IAAI,EAAE,OAAO,KAAK,KAAK,EAAE,GAAG,cAAc,MAAM,cAAc,oBAAI,IAAI,CAAC,KAAK,CAAC,EAAE;AAElH,aAAO,KAAK,CAAC,CAAC;AAAA,IAChB;AACA,mBAAe,IAAI,MAAM;AACzB,QAAI;AACF,aAAO,uBAAuB,OAAO,OAAO,CAAC;AAAA,IAC/C,UAAE;AACA,qBAAe,OAAO,MAAM;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,kBAAkB,EAAE,eAAe,kBAAkB,EAAE,UAAU;AACnE,WAAO,uBAAuB,OAAO,OAAO,CAAC;AAAA,EAC/C;AAEA,MAAI,kBAAkB,EAAE,gBAAgB;AACtC,WAAO,EAAE,GAAG,uBAAuB,OAAO,OAAO,CAAC,GAAG,cAAc,KAAK;AAAA,EAC1E;AAEA,MAAI,kBAAkB,EAAE,aAAa;AACnC,UAAM,SAAS,uBAAuB,OAAO,OAAO,CAAC;AAErD,WAAO,EAAE,GAAG,QAAQ,IAAI,EAAE,GAAG,OAAO,IAAI,UAAU,KAAK,EAAE;AAAA,EAC3D;AAEA,MAAI,kBAAkB,EAAE,SAAS;AAG/B,UAAM,QAAQ,OAAO,cAAc,EAAE,eAAe,OAAO,MAAM,OAAO;AAExE,WAAO,uBAAuB,KAAK;AAAA,EACrC;AAEA,MAAI,kBAAkB,EAAE,cAAc;AACpC,WAAO,KAAK,CAAC,CAAC;AAAA,EAChB;AAIA,MAAI,kBAAkB,EAAE,UAAU;AAChC,UAAM,UAAU,OAAO,OAAO;AAC9B,UAAM,EAAE,IAAI,cAAc,qBAAqB,aAAa,IAAI,uBAAuB,OAAO;AAC9F,QAAI,CAAC,qBAAqB;AACxB,YAAM,IAAI,MAAM,4EAA4E;AAAA,IAC9F;AAEA,WAAO,EAAE,IAAI,EAAE,MAAM,SAAS,OAAO,GAAG,OAAO,WAAW,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,GAAG,cAAc,MAAM,aAAa;AAAA,EACrH;AAEA,MAAI,kBAAkB,EAAE,UAAU;AAChC,UAAM,cAAc,OAAO,KAAK,IAAI,MAAM,IAAI,CAAC,SAAS;AACtD,YAAM,EAAE,GAAG,IAAI,uBAAuB,IAAI;AAE1C,aAAO,GAAG,OAAO,WAAW,IAAI,GAAG,MAAM,CAAC,IAAI;AAAA,IAChD,CAAC;AAGD,UAAM,SAAS,CAAC,GAAG,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,CAAC,KAAK,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;AACnF,UAAM,QAAQ,OAAO,WAAW,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,OAAO;AAEhE,WAAO,KAAK,EAAE,MAAM,SAAS,OAAO,UAAU,YAAY,QAAQ,UAAU,YAAY,OAAO,CAAC;AAAA,EAClG;AAMA,MAAI,kBAAkB,EAAE,aAAa,kBAAkB,EAAE,iBAAiB;AACxE,UAAM,KAAmB,EAAE,MAAM,SAAS;AAC1C,QAAI,OAAO,cAAc,KAAM,IAAG,YAAY,OAAO;AACrD,QAAI,OAAO,cAAc,KAAM,IAAG,YAAY,OAAO;AACrD,UAAM,MAAM,OAAO;AACnB,QAAI,QAAQ,MAAM;AAChB,UAAI,QAAQ,SAAS;AAEnB,cAAM,WAAW,OAAO,KAAK,IAAI;AACjC,cAAM,QAAQ,WAAW,CAAC,GAAG,QAAQ,EAAE,CAAC,IAAI;AAC5C,YAAI,MAAO,IAAG,UAAU,MAAM;AAAA,MAChC,OAAO;AACL,WAAG,SAAS;AAAA,MACd;AAAA,IACF;AAEA,WAAO,EAAE,IAAI,cAAc,MAAM,cAAc,oBAAI,IAAI,EAAE;AAAA,EAC3D;AAGA,MAAI,kBAAkB,EAAE,WAAW;AACjC,UAAM,MAAM,OAAO;AACnB,UAAM,QAAQ,QAAQ,WAAW,QAAQ,YAAY,QAAQ;AAC7D,UAAM,KAAmB,EAAE,MAAM,QAAQ,YAAY,SAAS;AAC9D,QAAI,OAAO,aAAa,QAAQ,OAAO,aAAa,OAAO,oBAAoB,OAAO,SAAS,OAAO,QAAQ,EAAG,IAAG,UAAU,OAAO;AACrI,QAAI,OAAO,aAAa,QAAQ,OAAO,aAAa,OAAO,oBAAoB,OAAO,SAAS,OAAO,QAAQ,EAAG,IAAG,UAAU,OAAO;AAErI,WAAO,EAAE,IAAI,cAAc,MAAM,cAAc,oBAAI,IAAI,EAAE;AAAA,EAC3D;AAEA,MAAI,kBAAkB,EAAE,WAAW;AACjC,WAAO,KAAK,EAAE,MAAM,WAAW,QAAQ,QAAQ,CAAC;AAAA,EAClD;AAEA,MAAI,kBAAkB,EAAE,YAAY;AAClC,WAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAAA,EACjC;AAEA,MAAI,kBAAkB,EAAE,SAAS;AAC/B,WAAO,KAAK,EAAE,MAAM,UAAU,QAAQ,YAAY,CAAC;AAAA,EACrD;AAEA,MAAI,kBAAkB,EAAE,SAAS;AAC/B,WAAO,EAAE,IAAI,EAAE,UAAU,KAAK,GAAG,cAAc,MAAM,cAAc,oBAAI,IAAI,EAAE;AAAA,EAC/E;AAIA,MAAI,kBAAkB,EAAE,SAAS;AAC/B,WAAO,KAAK,EAAE,MAAM,OAAO,QAAQ,CAAC;AAAA,EACtC;AAEA,MAAI,kBAAkB,EAAE,YAAY;AAClC,WAAO,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,MAAM,EAAE,CAAC;AAAA,EAC1C;AAIA,MAAI,kBAAkB,EAAE,YAAY,kBAAkB,EAAE,uBAAuB;AAC7E,UAAM,eAAe,oBAAI,IAAiB;AAC1C,UAAM,QAAQ,OAAO,QAAQ,IAAI,CAAC,WAAW;AAC3C,YAAM,EAAE,IAAI,cAAc,cAAc,cAAc,IAAI,uBAAuB,MAAM;AAEvF,UAAI,CAAC,cAAc;AACjB,cAAM,IAAI,MAAM,iFAAiF;AAAA,MACnG;AAEA,oBAAc,QAAQ,CAAC,gBAAgB,aAAa,IAAI,WAAW,CAAC;AAGpE,aAAO,GAAG,OAAO,WAAW,IAAI,GAAG,MAAM,CAAC,IAAI;AAAA,IAChD,CAAC;AAED,WAAO,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,MAAM,aAAa;AAAA,EAC3D;AAEA,MAAI,kBAAkB,EAAE,iBAAiB;AACvC,UAAM,OAAO,uBAAuB,OAAO,KAAK,IAAI,IAAiB;AACrE,UAAM,QAAQ,uBAAuB,OAAO,KAAK,IAAI,KAAkB;AACvE,UAAM,eAAe,oBAAI,IAAI,CAAC,GAAG,KAAK,cAAc,GAAG,MAAM,YAAY,CAAC;AAC1E,UAAM,SAAS,KAAK,GAAG,OAAO,WAAW,IAAI,KAAK,GAAG,MAAM,CAAC,IAAI,KAAK;AACrE,UAAM,UAAU,MAAM,GAAG,OAAO,WAAW,IAAI,MAAM,GAAG,MAAM,CAAC,IAAI,MAAM;AAEzE,WAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,OAAO,EAAE,GAAG,cAAc,MAAM,aAAa;AAAA,EAC9E;AAIA,MAAI,kBAAkB,EAAE,UAAU,kBAAkB,EAAE,YAAY;AAChE,WAAO,KAAK,CAAC,CAAC;AAAA,EAChB;AAEA,MAAI,kBAAkB,EAAE,gBAAgB,kBAAkB,EAAE,WAAW,kBAAkB,EAAE,UAAU;AACnG,UAAM,IAAI,MAAM,2BAA4B,OAAqB,IAAI,IAAI,gCAAgC;AAAA,EAC3G;AAEA,QAAM,IAAI,MAAM,iDAAkD,OAAqB,IAAI,IAAI,GAAG;AACpG;;;AE3QA,SAAS,qBAAqB,kBAAsC;AACpE,OAAsC;AACtC,SAAS,iBAAAA,gBAAe,OAAO,6BAA6B;AAOrD,IAAM,oBAAN,MAAiD;AAAA,EACrC;AAAA,EAEjB,YAAY,SAAoC;AAC9C,SAAK,cAAc,SAAS,gBAAgB,CAAC,WAAW,IAAI,oBAAoB,MAAM;AAAA,EACxF;AAAA,EAEA,UAAU,OAAgB,EAAE,SAAS,GAA8B;AACjE,QAAI,CAAC,YAAY,CAACC,eAAc,QAAQ,GAAG;AACzC,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAO,MAAM,UAAU,KAAK;AAAA,IAC9B,SAAS,OAAO;AACd,UAAI,iBAAiB,uBAAuB;AAC1C,cAAM,KAAK,YAAY,MAAM,MAAM;AAAA,MACrC;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AArBa,oBAAN;AAAA,EADN,WAAW;AAAA,GACC;;;AHLb,iBAAiB,sBAAsB;","names":["isZodDtoClass","isZodDtoClass"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/swagger.ts","../src/utils.ts","../src/pipe.ts"],"sourcesContent":["import { registerOnCreate } from '@voznov/zod-dto';\nimport { applySwaggerDecorators } from './swagger';\n\n// Deferred to a microtask so self-referential DTOs (`lazyDto<X>(() => X)`) resolve past TDZ.\nregisterOnCreate((dto) => void Promise.resolve().then(() => applySwaggerDecorators(dto)));\n\nexport { ZodValidationPipe } from './pipe';\nexport type { ZodValidationPipeOptions } from './pipe';\nexport { applySwaggerDecorators } from './swagger';\n","import { ApiExtraModels, ApiProperty, type ApiPropertyOptions, refs } from '@nestjs/swagger';\nimport { type SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';\nimport { isZodDtoClass, type ZodDtoClass } from '@voznov/zod-dto';\nimport { z } from 'zod';\nimport { mapValues } from './utils';\n\nconst schemaObjectToApiPropertyOptions = (so: SchemaObject, selfRequired: boolean): ApiPropertyOptions => {\n if ('oneOf' in so || 'anyOf' in so || 'allOf' in so) {\n return { ...so, type: Array, required: selfRequired };\n }\n\n return { ...so, required: selfRequired } as ApiPropertyOptions;\n};\n\nconst leaf = (so: SchemaObject): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => ({\n so,\n selfRequired: true,\n innerSchemas: new Set(),\n});\n\nconst decoratedDtoClasses = new Set<ZodDtoClass>();\n\n// Snapshot resolved default per schema instance — Zod's `defaultValue` is a getter that\n// re-invokes the thunk on every access. Without this cache, the same `ZodDefault` schema\n// surfaced through two paths (JS subclass, `.extend/.pick/.omit`, shared field reuse, ...)\n// would emit a different value into each occurrence in the spec.\nconst defaultValueCache = new WeakMap<z.ZodDefault, unknown>();\n\n// `z.lazy(() => self)` is the only way to introduce a real cycle in a Zod schema graph.\n// Track each ZodLazy currently being walked so we can break re-entry without overflowing.\nconst lazyInProgress = new Set<z.ZodLazy>();\n\nexport const applySwaggerDecorators = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n const result = applyDecoratorsImpl(schema);\n // `.describe(text)` → OpenAPI `description`. Read at every recursion level so the text\n // lands in the spec whether it's set on a wrapper (`.optional().describe(...)`) or on\n // the inner type (`.describe(...).optional()`).\n const description = (schema as z.ZodType).description;\n if (description) result.so = { ...result.so, description };\n\n return result;\n};\n\nconst applyDecoratorsImpl = (schema: z.core.$ZodType): { so: SchemaObject; selfRequired: boolean; innerSchemas: Set<ZodDtoClass> } => {\n // --- Objects ---\n\n if (schema instanceof z.ZodObject) {\n if (isZodDtoClass(schema) && decoratedDtoClasses.has(schema)) {\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n const properties: Record<string, SchemaObject> = {};\n const required: string[] = [];\n const innerSchemas = new Set<ZodDtoClass>();\n Object.entries<z.core.$ZodType>(schema.shape).forEach(([key, fieldSchema]) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(fieldSchema);\n properties[key] = so;\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n if (selfRequired) {\n required.push(key);\n }\n });\n\n if (isZodDtoClass(schema)) {\n decoratedDtoClasses.add(schema);\n for (const [key, propertySo] of Object.entries(properties)) {\n ApiProperty(schemaObjectToApiPropertyOptions(propertySo, required.includes(key)))(schema.prototype, key);\n }\n ApiExtraModels(...[...innerSchemas.values()].filter((innerSchema) => innerSchema !== schema))(schema);\n\n return { so: { oneOf: refs(schema) }, selfRequired: true, innerSchemas: new Set([schema]) };\n }\n\n return { so: { type: 'object', properties: mapValues(properties, (so) => (so.oneOf?.length === 1 ? so.oneOf[0] : so)), required }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodRecord) {\n const { so } = applySwaggerDecorators(schema._zod.def.valueType);\n\n return leaf({ type: 'object', additionalProperties: so.oneOf?.length === 1 ? so.oneOf[0] : so });\n }\n\n // --- Wrappers (unwrap to inner type) ---\n\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodExactOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: false };\n }\n\n if (schema instanceof z.ZodDefault) {\n const inner = applySwaggerDecorators(schema.unwrap());\n if (!defaultValueCache.has(schema)) defaultValueCache.set(schema, schema._zod.def.defaultValue);\n\n return { ...inner, so: { ...inner.so, default: defaultValueCache.get(schema) }, selfRequired: false };\n }\n\n if (schema instanceof z.ZodLazy) {\n // `z.lazy(() => self)` is the only way Zod schemas can carry an actual cycle.\n // Track each ZodLazy instance currently being walked; on re-entry, emit a $ref\n // back to the DTO that the lazy resolves to (if any), or a permissive `{}` as a\n // last resort for anonymous self-referential trees.\n if (lazyInProgress.has(schema)) {\n const inner = schema.unwrap();\n if (isZodDtoClass(inner)) return { so: { oneOf: refs(inner) }, selfRequired: true, innerSchemas: new Set([inner]) };\n\n return leaf({});\n }\n lazyInProgress.add(schema);\n try {\n return applySwaggerDecorators(schema.unwrap());\n } finally {\n lazyInProgress.delete(schema);\n }\n }\n\n if (schema instanceof z.ZodReadonly || schema instanceof z.ZodCatch) {\n return applySwaggerDecorators(schema.unwrap());\n }\n\n if (schema instanceof z.ZodNonOptional) {\n return { ...applySwaggerDecorators(schema.unwrap()), selfRequired: true };\n }\n\n if (schema instanceof z.ZodNullable) {\n const result = applySwaggerDecorators(schema.unwrap());\n\n return { ...result, so: { ...result.so, nullable: true } };\n }\n\n if (schema instanceof z.ZodPipe) {\n // z.preprocess() creates a Pipe(in: ZodTransform, out: schema).\n // For swagger, skip the transform and process the output schema directly.\n const inner = schema.in instanceof z.ZodTransform ? schema.out : schema.in;\n\n return applySwaggerDecorators(inner);\n }\n\n if (schema instanceof z.ZodTransform) {\n return leaf({});\n }\n\n // --- Arrays & tuples ---\n\n if (schema instanceof z.ZodArray) {\n const element = schema.unwrap();\n const { so, selfRequired: selfRequiredElement, innerSchemas } = applySwaggerDecorators(element);\n if (!selfRequiredElement) {\n throw new Error('Not required array item is not supported in Swagger. Use nullable instead.');\n }\n\n return { so: { type: 'array', items: so.oneOf?.length === 1 ? so.oneOf[0] : so }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodTuple) {\n const itemSchemas = schema._zod.def.items.map((item) => {\n const { so } = applySwaggerDecorators(item);\n\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n // Deduplicate by JSON representation to collapse identical types\n const unique = [...new Map(itemSchemas.map((s) => [JSON.stringify(s), s])).values()];\n const items = unique.length === 1 ? unique[0] : { oneOf: unique };\n\n return leaf({ type: 'array', items, minItems: itemSchemas.length, maxItems: itemSchemas.length });\n }\n\n // --- Scalars ---\n\n // ZodString: plain z.string(). ZodStringFormat: z.email(), z.uuid(), z.url(), z.ipv4(), etc.\n // Both share the same properties (minLength, maxLength, format).\n if (schema instanceof z.ZodString || schema instanceof z.ZodStringFormat) {\n const so: SchemaObject = { type: 'string' };\n if (schema.minLength !== null) so.minLength = schema.minLength;\n if (schema.maxLength !== null) so.maxLength = schema.maxLength;\n const fmt = schema.format;\n if (fmt !== null) {\n if (fmt === 'regex') {\n // Grab the first regex pattern from the internal bag.\n const patterns = schema._zod.bag.patterns;\n const first = patterns ? [...patterns][0] : undefined;\n if (first) so.pattern = first.source;\n } else {\n so.format = fmt;\n }\n }\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n // Number and integer subtypes (ZodInt, ZodNumberFormat, etc.) — all instanceof ZodNumber.\n if (schema instanceof z.ZodNumber) {\n const fmt = schema.format;\n const isInt = fmt === 'int32' || fmt === 'uint32' || fmt === 'safeint';\n const so: SchemaObject = { type: isInt ? 'integer' : 'number' };\n if (schema.minValue !== null && schema.minValue !== Number.MIN_SAFE_INTEGER && Number.isFinite(schema.minValue)) so.minimum = schema.minValue;\n if (schema.maxValue !== null && schema.maxValue !== Number.MAX_SAFE_INTEGER && Number.isFinite(schema.maxValue)) so.maximum = schema.maxValue;\n\n return { so, selfRequired: true, innerSchemas: new Set() };\n }\n\n if (schema instanceof z.ZodBigInt) {\n return leaf({ type: 'integer', format: 'int64' });\n }\n\n if (schema instanceof z.ZodBoolean) {\n return leaf({ type: 'boolean' });\n }\n\n if (schema instanceof z.ZodDate) {\n return leaf({ type: 'string', format: 'date-time' });\n }\n\n if (schema instanceof z.ZodNull) {\n return { so: { nullable: true }, selfRequired: true, innerSchemas: new Set() };\n }\n\n // --- Enums & literals ---\n\n if (schema instanceof z.ZodEnum) {\n return leaf({ enum: schema.options });\n }\n\n if (schema instanceof z.ZodLiteral) {\n return leaf({ enum: [...schema.values] });\n }\n\n // --- Unions & intersections ---\n\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const innerSchemas = new Set<ZodDtoClass>();\n const oneOf = schema.options.map((option) => {\n const { so, selfRequired, innerSchemas: innerSchemas_ } = applySwaggerDecorators(option);\n\n if (!selfRequired) {\n throw new Error('Not required option in oneOf is not supported in Swagger. Use nullable instead.');\n }\n\n innerSchemas_.forEach((innerSchema) => innerSchemas.add(innerSchema));\n\n // Flatten { oneOf: [single_ref] } → just the ref, so union of DTO refs stays clean.\n return so.oneOf?.length === 1 ? so.oneOf[0] : so;\n });\n\n return { so: { oneOf }, selfRequired: true, innerSchemas };\n }\n\n if (schema instanceof z.ZodIntersection) {\n const left = applySwaggerDecorators(schema._zod.def.left as z.ZodType);\n const right = applySwaggerDecorators(schema._zod.def.right as z.ZodType);\n const innerSchemas = new Set([...left.innerSchemas, ...right.innerSchemas]);\n const leftSo = left.so.oneOf?.length === 1 ? left.so.oneOf[0] : left.so;\n const rightSo = right.so.oneOf?.length === 1 ? right.so.oneOf[0] : right.so;\n\n return { so: { allOf: [leftSo, rightSo] }, selfRequired: true, innerSchemas };\n }\n\n // --- Catch-all ---\n\n if (schema instanceof z.ZodAny || schema instanceof z.ZodUnknown) {\n return leaf({});\n }\n\n if (schema instanceof z.ZodUndefined || schema instanceof z.ZodVoid || schema instanceof z.ZodNever) {\n throw new Error(`applySwaggerDecorators: ${(schema as z.ZodType).def.type} cannot be represented in JSON`);\n }\n\n throw new Error(`applySwaggerDecorators: unsupported Zod type \"${(schema as z.ZodType).def.type}\"`);\n};\n","export const mapValues = <K extends string, V, R>(obj: Record<K, V>, fn: (value: V, key: K) => R): Record<K, R> =>\n Object.fromEntries(Object.entries<V>(obj).map(([key, value]) => [key, fn(value, key as K)])) as Record<K, R>;\n","import { BadRequestException, Injectable, type PipeTransform } from '@nestjs/common';\nimport { type ArgumentMetadata } from '@nestjs/common/interfaces';\nimport { isZodDtoClass, toDto, ZodDtoValidationError } from '@voznov/zod-dto';\n\nexport interface ZodValidationPipeOptions {\n createError?: (issues: string[]) => Error;\n}\n\n@Injectable()\nexport class ZodValidationPipe implements PipeTransform {\n private readonly createError: (issues: string[]) => Error;\n\n constructor(options?: ZodValidationPipeOptions) {\n this.createError = options?.createError ?? ((issues) => new BadRequestException(issues));\n }\n\n transform(value: unknown, { metatype }: ArgumentMetadata): unknown {\n if (!metatype || !isZodDtoClass(metatype)) {\n return value;\n }\n\n try {\n return toDto(metatype, value);\n } catch (error) {\n if (error instanceof ZodDtoValidationError) {\n throw this.createError(error.issues);\n }\n throw error;\n }\n }\n}\n"],"mappings":"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","names":["isZodDtoClass","isZodDtoClass"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voznov/zod-dto-nestjs",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "NestJS adapter for @voznov/zod-dto — validation pipe, Swagger generation",
5
5
  "keywords": [
6
6
  "zod",
@@ -48,7 +48,7 @@
48
48
  "@nestjs/common": ">=10.0.0",
49
49
  "@nestjs/swagger": ">=7.0.0",
50
50
  "zod": ">=4.0.0",
51
- "@voznov/zod-dto": "^0.2.1"
51
+ "@voznov/zod-dto": "^0.2.2"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"