@stlmpp/nestjs-zod 1.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.
@@ -0,0 +1,61 @@
1
+ import { input, output } from "zod/v4/core";
2
+
3
+ //#region src/types.d.ts
4
+ interface UnknownSchema {
5
+ parse(input: unknown, options?: {
6
+ reportInput?: boolean;
7
+ [x: string | number | symbol]: unknown;
8
+ }): unknown;
9
+ encode?(input: unknown, options?: {
10
+ reportInput?: boolean;
11
+ [x: string | number | symbol]: unknown;
12
+ }): unknown;
13
+ array?: () => UnknownSchema;
14
+ }
15
+ /**
16
+ * RequiredBy
17
+ * @desc From `T` make a set of properties by key `K` become required
18
+ * @example
19
+ * type Props = {
20
+ * name?: string;
21
+ * age?: number;
22
+ * visible?: boolean;
23
+ * };
24
+ *
25
+ * // Expect: { name: string; age: number; visible: boolean; }
26
+ * type Props = RequiredBy<Props>;
27
+ *
28
+ * // Expect: { name?: string; age: number; visible: boolean; }
29
+ * type Props = RequiredBy<Props, 'age' | 'visible'>;
30
+ */
31
+ type RequiredBy<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Required<Pick<T, K>>;
32
+ //#endregion
33
+ //#region src/dto.d.ts
34
+ /**
35
+ * `input`/`output` from `zod/v4/core` fall back to `unknown` for schemas
36
+ * that don't expose v4-style `_zod` internals (zod v3 schemas, zod-mini
37
+ * schemas, or duck-typed `UnknownSchema` objects). For those, fall back to
38
+ * the schema's `parse` return type, same as before `Type` existed.
39
+ */
40
+ type ZodDtoInstance<TSchema extends UnknownSchema, Type extends 'input' | 'output'> = TSchema extends {
41
+ _zod: {
42
+ input: unknown;
43
+ output: unknown;
44
+ };
45
+ } ? Type extends 'output' ? output<TSchema> : input<TSchema> : ReturnType<TSchema['parse']>;
46
+ interface ZodDto<TSchema extends UnknownSchema = UnknownSchema, TCodec extends boolean = boolean, Type extends 'input' | 'output' = 'input'> {
47
+ new (): ZodDtoInstance<TSchema, Type>;
48
+ isZodDto: true;
49
+ schema: TSchema;
50
+ codec: TCodec;
51
+ create(input: unknown): ZodDtoInstance<TSchema, Type>;
52
+ Output: ZodDto<UnknownSchema, TCodec>;
53
+ _OPENAPI_METADATA_FACTORY(): unknown;
54
+ }
55
+ declare function createZodDto<TSchema extends UnknownSchema, TCodec extends boolean = false, Type extends 'input' | 'output' = 'input'>(schema: TSchema, options?: {
56
+ codec?: TCodec;
57
+ type?: Type;
58
+ }): ZodDto<TSchema, TCodec, Type>;
59
+ declare function isZodDto(metatype: unknown): metatype is ZodDto<UnknownSchema, boolean, 'input' | 'output'>;
60
+ //#endregion
61
+ export { UnknownSchema as a, RequiredBy as i, createZodDto as n, isZodDto as r, ZodDto as t };
@@ -0,0 +1,61 @@
1
+ import { input, output } from "zod/v4/core";
2
+
3
+ //#region src/types.d.ts
4
+ interface UnknownSchema {
5
+ parse(input: unknown, options?: {
6
+ reportInput?: boolean;
7
+ [x: string | number | symbol]: unknown;
8
+ }): unknown;
9
+ encode?(input: unknown, options?: {
10
+ reportInput?: boolean;
11
+ [x: string | number | symbol]: unknown;
12
+ }): unknown;
13
+ array?: () => UnknownSchema;
14
+ }
15
+ /**
16
+ * RequiredBy
17
+ * @desc From `T` make a set of properties by key `K` become required
18
+ * @example
19
+ * type Props = {
20
+ * name?: string;
21
+ * age?: number;
22
+ * visible?: boolean;
23
+ * };
24
+ *
25
+ * // Expect: { name: string; age: number; visible: boolean; }
26
+ * type Props = RequiredBy<Props>;
27
+ *
28
+ * // Expect: { name?: string; age: number; visible: boolean; }
29
+ * type Props = RequiredBy<Props, 'age' | 'visible'>;
30
+ */
31
+ type RequiredBy<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Required<Pick<T, K>>;
32
+ //#endregion
33
+ //#region src/dto.d.ts
34
+ /**
35
+ * `input`/`output` from `zod/v4/core` fall back to `unknown` for schemas
36
+ * that don't expose v4-style `_zod` internals (zod v3 schemas, zod-mini
37
+ * schemas, or duck-typed `UnknownSchema` objects). For those, fall back to
38
+ * the schema's `parse` return type, same as before `Type` existed.
39
+ */
40
+ type ZodDtoInstance<TSchema extends UnknownSchema, Type extends 'input' | 'output'> = TSchema extends {
41
+ _zod: {
42
+ input: unknown;
43
+ output: unknown;
44
+ };
45
+ } ? Type extends 'output' ? output<TSchema> : input<TSchema> : ReturnType<TSchema['parse']>;
46
+ interface ZodDto<TSchema extends UnknownSchema = UnknownSchema, TCodec extends boolean = boolean, Type extends 'input' | 'output' = 'input'> {
47
+ new (): ZodDtoInstance<TSchema, Type>;
48
+ isZodDto: true;
49
+ schema: TSchema;
50
+ codec: TCodec;
51
+ create(input: unknown): ZodDtoInstance<TSchema, Type>;
52
+ Output: ZodDto<UnknownSchema, TCodec>;
53
+ _OPENAPI_METADATA_FACTORY(): unknown;
54
+ }
55
+ declare function createZodDto<TSchema extends UnknownSchema, TCodec extends boolean = false, Type extends 'input' | 'output' = 'input'>(schema: TSchema, options?: {
56
+ codec?: TCodec;
57
+ type?: Type;
58
+ }): ZodDto<TSchema, TCodec, Type>;
59
+ declare function isZodDto(metatype: unknown): metatype is ZodDto<UnknownSchema, boolean, 'input' | 'output'>;
60
+ //#endregion
61
+ export { UnknownSchema as a, RequiredBy as i, createZodDto as n, isZodDto as r, ZodDto as t };
@@ -0,0 +1,433 @@
1
+ import { globalRegistry, toJSONSchema } from "zod/v4/core";
2
+ import deepmerge from "deepmerge";
3
+ import { z } from "zod/v3";
4
+ //#region src/assert.ts
5
+ function assert(condition, message = "Assertion failed") {
6
+ if (!condition) throw new Error(`[nestjs-zod] ${message}`);
7
+ }
8
+ //#endregion
9
+ //#region src/config.ts
10
+ const zodDtoConfig = { codec: false };
11
+ /**
12
+ * Overrides the library-wide defaults used by `createZodDto`. Any option
13
+ * not passed here keeps its previous value. Call this once, e.g. during
14
+ * app bootstrap, before any `createZodDto` calls that should observe it.
15
+ */
16
+ function configureZodDto(config) {
17
+ Object.assign(zodDtoConfig, config);
18
+ }
19
+ function getZodDtoConfig() {
20
+ return zodDtoConfig;
21
+ }
22
+ //#endregion
23
+ //#region src/const.ts
24
+ const PREFIX = "x-nestjs_zod";
25
+ const EMPTY_TYPE_KEY = `${PREFIX}-empty-type`;
26
+ const DEFS_KEY = `${PREFIX}-$defs`;
27
+ const PARENT_ID_KEY = `${PREFIX}-parent-id`;
28
+ const PARENT_ADDITIONAL_PROPERTIES_KEY = `${PREFIX}-parent-additional-properties`;
29
+ const PARENT_HAS_REFS_KEY = `${PREFIX}-parent-has-refs`;
30
+ const UNWRAP_ROOT_KEY = `${PREFIX}-unwrap-root`;
31
+ const USES_THREE_POINT_ONE_SYNTAX_KEY = `${PREFIX}-uses-3-point-1-syntax`;
32
+ const SELF_REQUIRED_KEY = `${PREFIX}-self-required`;
33
+ const PARENT_METADATA_KEY = `${PREFIX}-parent-metadata`;
34
+ //#endregion
35
+ //#region src/utils.ts
36
+ function fixAllRefs({ schema, defRenames, rootSchemaName }) {
37
+ return walkJsonSchema(schema, (s) => {
38
+ if (s.$ref) {
39
+ if (s.$ref.startsWith("#/$defs/")) {
40
+ const oldDefName = s.$ref.replace("#/$defs/", "");
41
+ const newDefName = defRenames?.[oldDefName];
42
+ if (newDefName) s.$ref = `#/$defs/${newDefName}`;
43
+ }
44
+ s.$ref = s.$ref.replace("#/$defs/", "#/components/schemas/");
45
+ if (s.$ref === "#") {
46
+ if (!rootSchemaName) throw new Error("[fixAllRefs] rootSchemaName is required when fixing a ref to #");
47
+ s.$ref = `#/components/schemas/${rootSchemaName}`;
48
+ }
49
+ }
50
+ return s;
51
+ }, { clone: true });
52
+ }
53
+ /**
54
+ * By default, zod generates openapi schemas that are compatible with OpenAPI
55
+ * 3.1. But OpenAPI 3.0 supports a weird flavour of JSONSchema they call the
56
+ * "subset superset"
57
+ *
58
+ * This function converts the schema to the OpenAPI 3.0 subset superset format.
59
+ *
60
+ * See more information here:
61
+ * https://www.apimatic.io/blog/2021/09/migrating-to-and-from-openapi-3-1
62
+ */
63
+ function convertToOpenApi3Point0(schema) {
64
+ return walkJsonSchema(schema, (s) => {
65
+ if ("id" in s) delete s.id;
66
+ if (s.anyOf) {
67
+ const nullSchema = s.anyOf.findIndex((subSchema) => subSchema.type === "null");
68
+ if (nullSchema === -1) return s;
69
+ s.anyOf.splice(nullSchema, 1);
70
+ const { anyOf, ...rest } = s;
71
+ if (anyOf.length === 1) {
72
+ const sole = anyOf[0];
73
+ if (sole.$ref && Object.keys(sole).length === 1) return {
74
+ allOf: [sole],
75
+ nullable: true,
76
+ ...rest
77
+ };
78
+ const enumValues = Array.isArray(sole.enum) ? sole.enum : void 0;
79
+ return {
80
+ ...sole,
81
+ ...rest,
82
+ ...enumValues && !enumValues.includes(null) && { enum: [...enumValues, null] },
83
+ nullable: true
84
+ };
85
+ }
86
+ return {
87
+ ...rest,
88
+ anyOf,
89
+ nullable: true
90
+ };
91
+ }
92
+ if (typeof s.const !== "undefined") {
93
+ s.enum = [s.const];
94
+ delete s.const;
95
+ }
96
+ if ("propertyNames" in s) delete s.propertyNames;
97
+ if (typeof s.exclusiveMinimum === "number") {
98
+ s.minimum = s.exclusiveMinimum;
99
+ s.exclusiveMinimum = true;
100
+ }
101
+ if (typeof s.exclusiveMaximum === "number") {
102
+ s.maximum = s.exclusiveMaximum;
103
+ s.exclusiveMaximum = true;
104
+ }
105
+ return s;
106
+ }, { clone: true });
107
+ }
108
+ function walkJsonSchema(schema, callback, options) {
109
+ schema = callback(options?.clone ? deepmerge(schema, {}) : schema);
110
+ if (schema.type === "object" && schema.properties) for (const key in schema.properties) schema.properties[key] = walkJsonSchema(schema.properties[key], callback);
111
+ if (schema.type === "array" && Array.isArray(schema.items)) schema.items = schema.items.map((item) => walkJsonSchema(item, callback));
112
+ if (schema.type === "array" && schema.items) schema.items = walkJsonSchema(schema.items, callback);
113
+ if (schema.oneOf) schema.oneOf = schema.oneOf.map((subSchema) => walkJsonSchema(subSchema, callback));
114
+ if (schema.anyOf) schema.anyOf = schema.anyOf.map((subSchema) => walkJsonSchema(subSchema, callback));
115
+ if (schema.allOf) schema.allOf = schema.allOf.map((subSchema) => walkJsonSchema(subSchema, callback));
116
+ if (typeof schema.additionalProperties === "object") schema.additionalProperties = walkJsonSchema(schema.additionalProperties, callback);
117
+ if (typeof schema.propertyNames === "object" && schema.propertyNames !== null) schema.propertyNames = walkJsonSchema(schema.propertyNames, callback);
118
+ return schema;
119
+ }
120
+ //#endregion
121
+ //#region src/zodV3ToOpenApi.ts
122
+ function is(input, factory) {
123
+ return factory === z[input._def.typeName];
124
+ }
125
+ /**
126
+ * @deprecated `zodToOpenAPI` will be removed in a future version, since zod
127
+ * v4 adds built-in support for generating OpenAPI schemas from zod schemas.
128
+ */
129
+ function zodV3ToOpenAPI(zodType, visited = /* @__PURE__ */ new Set()) {
130
+ const object = {};
131
+ if (zodType.description) object.description = zodType.description;
132
+ if (is(zodType, z.ZodString)) {
133
+ const { checks } = zodType._def;
134
+ object.type = "string";
135
+ for (const check of checks) if (check.kind === "min") object.minLength = check.value;
136
+ else if (check.kind === "max") object.maxLength = check.value;
137
+ else if (check.kind === "email") object.format = "email";
138
+ else if (check.kind === "url") object.format = "uri";
139
+ else if (check.kind === "uuid") object.format = "uuid";
140
+ else if (check.kind === "cuid") object.format = "cuid";
141
+ else if (check.kind === "regex") object.pattern = check.regex.source;
142
+ else if (check.kind === "datetime") object.format = "date-time";
143
+ }
144
+ if (is(zodType, z.ZodBoolean)) object.type = "boolean";
145
+ if (is(zodType, z.ZodNumber)) {
146
+ const { checks } = zodType._def;
147
+ object.type = "number";
148
+ for (const check of checks) if (check.kind === "int") object.type = "integer";
149
+ else if (check.kind === "min") {
150
+ object.minimum = check.value;
151
+ object.exclusiveMinimum = !check.inclusive;
152
+ } else if (check.kind === "max") {
153
+ object.maximum = check.value;
154
+ object.exclusiveMaximum = !check.inclusive;
155
+ } else if (check.kind === "multipleOf") object.multipleOf = check.value;
156
+ }
157
+ if (is(zodType, z.ZodBigInt)) {
158
+ object.type = "integer";
159
+ object.format = "int64";
160
+ }
161
+ if (is(zodType, z.ZodArray)) {
162
+ const { minLength, maxLength, type } = zodType._def;
163
+ object.type = "array";
164
+ if (minLength) object.minItems = minLength.value;
165
+ if (maxLength) object.maxItems = maxLength.value;
166
+ object.items = zodV3ToOpenAPI(type, visited);
167
+ }
168
+ if (is(zodType, z.ZodTuple)) {
169
+ const { items } = zodType._def;
170
+ object.type = "array";
171
+ object.items = { oneOf: items.map((item) => zodV3ToOpenAPI(item, visited)) };
172
+ }
173
+ if (is(zodType, z.ZodSet)) {
174
+ const { valueType, minSize, maxSize } = zodType._def;
175
+ object.type = "array";
176
+ if (minSize) object.minItems = minSize.value;
177
+ if (maxSize) object.maxItems = maxSize.value;
178
+ object.items = zodV3ToOpenAPI(valueType, visited);
179
+ object.uniqueItems = true;
180
+ }
181
+ if (is(zodType, z.ZodUnion)) {
182
+ const { options } = zodType._def;
183
+ object.oneOf = options.map((option) => zodV3ToOpenAPI(option, visited));
184
+ }
185
+ if (is(zodType, z.ZodDiscriminatedUnion)) {
186
+ const { options } = zodType._def;
187
+ object.oneOf = [];
188
+ for (const schema of options.values()) object.oneOf.push(zodV3ToOpenAPI(schema, visited));
189
+ }
190
+ if (is(zodType, z.ZodLiteral)) {
191
+ const { value } = zodType._def;
192
+ if (typeof value === "string") {
193
+ object.type = "string";
194
+ object.enum = [value];
195
+ }
196
+ if (typeof value === "number") {
197
+ object.type = "number";
198
+ object.minimum = value;
199
+ object.maximum = value;
200
+ }
201
+ if (typeof value === "boolean") object.type = "boolean";
202
+ }
203
+ if (is(zodType, z.ZodEnum)) {
204
+ const { values } = zodType._def;
205
+ object.type = "string";
206
+ object.enum = values;
207
+ }
208
+ if (is(zodType, z.ZodNativeEnum)) {
209
+ const { values } = zodType._def;
210
+ object.type = "string";
211
+ object.enum = Object.values(values);
212
+ object["x-enumNames"] = Object.keys(values);
213
+ }
214
+ if (is(zodType, z.ZodTransformer)) {
215
+ const { schema } = zodType._def;
216
+ Object.assign(object, zodV3ToOpenAPI(schema, visited));
217
+ }
218
+ if (is(zodType, z.ZodNullable)) {
219
+ const { innerType } = zodType._def;
220
+ Object.assign(object, zodV3ToOpenAPI(innerType, visited));
221
+ object.nullable = true;
222
+ }
223
+ if (is(zodType, z.ZodOptional)) {
224
+ const { innerType } = zodType._def;
225
+ Object.assign(object, zodV3ToOpenAPI(innerType, visited));
226
+ }
227
+ if (is(zodType, z.ZodDefault)) {
228
+ const { defaultValue, innerType } = zodType._def;
229
+ Object.assign(object, zodV3ToOpenAPI(innerType, visited));
230
+ object.default = defaultValue();
231
+ }
232
+ if (is(zodType, z.ZodObject)) {
233
+ const { shape } = zodType._def;
234
+ object.type = "object";
235
+ object.properties = {};
236
+ object.required = [];
237
+ for (const [key, schema] of Object.entries(shape())) {
238
+ object.properties[key] = zodV3ToOpenAPI(schema, visited);
239
+ if (![z.ZodOptional.name, z.ZodDefault.name].includes(schema.constructor.name)) object.required.push(key);
240
+ }
241
+ if (object.required.length === 0) delete object.required;
242
+ }
243
+ if (is(zodType, z.ZodRecord)) {
244
+ const { valueType } = zodType._def;
245
+ object.type = "object";
246
+ object.additionalProperties = zodV3ToOpenAPI(valueType, visited);
247
+ }
248
+ if (is(zodType, z.ZodIntersection)) {
249
+ const { left, right } = zodType._def;
250
+ const merged = deepmerge(zodV3ToOpenAPI(left, visited), zodV3ToOpenAPI(right, visited), { arrayMerge: (target, source) => {
251
+ const mergedSet = new Set([...target, ...source]);
252
+ return Array.from(mergedSet);
253
+ } });
254
+ Object.assign(object, merged);
255
+ }
256
+ if (is(zodType, z.ZodEffects)) {
257
+ const { schema } = zodType._def;
258
+ Object.assign(object, zodV3ToOpenAPI(schema, visited));
259
+ }
260
+ if (is(zodType, z.ZodLazy)) {
261
+ const { getter } = zodType._def;
262
+ if (visited.has(getter)) return object;
263
+ visited.add(getter);
264
+ Object.assign(object, zodV3ToOpenAPI(getter(), visited));
265
+ }
266
+ return object;
267
+ }
268
+ //#endregion
269
+ //#region src/symbols.ts
270
+ const ioSymbol = Symbol("io");
271
+ //#endregion
272
+ //#region src/dto.ts
273
+ function createZodDto(schema, options) {
274
+ class AugmentedZodDto {
275
+ static {
276
+ this.isZodDto = true;
277
+ }
278
+ static {
279
+ this.schema = schema;
280
+ }
281
+ static {
282
+ this.codec = options?.codec ?? getZodDtoConfig().codec;
283
+ }
284
+ static {
285
+ this[ioSymbol] = "input";
286
+ }
287
+ static create(input) {
288
+ return this.schema.parse(input);
289
+ }
290
+ static get Output() {
291
+ assert("_zod" in schema, "Output DTOs can only be created from zod v4 schemas");
292
+ class AugmentedZodDto {
293
+ static {
294
+ this.isZodDto = true;
295
+ }
296
+ static {
297
+ this.schema = schema;
298
+ }
299
+ static {
300
+ this[ioSymbol] = "output";
301
+ }
302
+ static create(input) {
303
+ return this.schema.parse(input);
304
+ }
305
+ static _OPENAPI_METADATA_FACTORY() {
306
+ return openApiMetadataFactory({
307
+ schema: this.schema,
308
+ io: "output"
309
+ });
310
+ }
311
+ }
312
+ Object.defineProperty(AugmentedZodDto, "name", { value: `${this.name}_Output` });
313
+ return AugmentedZodDto;
314
+ }
315
+ static _OPENAPI_METADATA_FACTORY() {
316
+ return openApiMetadataFactory({
317
+ schema: this.schema,
318
+ io: "input"
319
+ });
320
+ }
321
+ }
322
+ return AugmentedZodDto;
323
+ }
324
+ function openApiMetadataFactory({ schema, io }) {
325
+ if (!("_zod" in schema) && "_def" in schema && io === "output") throw new Error("[nestjs-zod] Output schemas are not supported for zod@v3");
326
+ if (!("_zod" in schema) && !("_def" in schema)) return {};
327
+ const { $defs, $schema: _$schema, ...generatedJsonSchema } = generateJsonSchema(schema, io);
328
+ const zodId = "_zod" in schema ? globalRegistry.get(schema)?.id : void 0;
329
+ const rootId = zodId && io === "output" ? `${zodId}_Output` : zodId;
330
+ /**
331
+ * nestjs expects us to return a record of properties
332
+ *
333
+ * However, in some cases, we can't return a record of properties. For
334
+ * example, arrays, intersections, and unions can not be represented like this
335
+ *
336
+ * As a workaround, we wrap the schema in a "root" object. Then in the
337
+ * `cleanupOpenApiDoc` function, we unwrap the root object.
338
+ */
339
+ const jsonSchema = !isObjectTypeWithProperties(generatedJsonSchema) ? {
340
+ type: "object",
341
+ title: generatedJsonSchema.title,
342
+ properties: { root: {
343
+ ...generatedJsonSchema,
344
+ [UNWRAP_ROOT_KEY]: true
345
+ } },
346
+ $defs
347
+ } : {
348
+ ...generatedJsonSchema,
349
+ $defs
350
+ };
351
+ const { hasRefs, usesThreePointOneSyntax } = getSchemaMetadata(jsonSchema);
352
+ const properties = {};
353
+ for (const [propertyKey, propertySchema] of Object.entries(jsonSchema.properties || {})) {
354
+ const newPropertySchema = {
355
+ ...propertySchema,
356
+ type: propertySchema.type || ""
357
+ };
358
+ if (usesThreePointOneSyntax) newPropertySchema[USES_THREE_POINT_ONE_SYNTAX_KEY] = true;
359
+ if (hasRefs) newPropertySchema[PARENT_HAS_REFS_KEY] = true;
360
+ if (typeof propertySchema.type !== "string") newPropertySchema[EMPTY_TYPE_KEY] = true;
361
+ const required = Boolean("required" in jsonSchema && jsonSchema.required?.includes(propertyKey));
362
+ if (newPropertySchema.type === "object") {
363
+ newPropertySchema.selfRequired = required;
364
+ newPropertySchema[SELF_REQUIRED_KEY] = required;
365
+ } else newPropertySchema.required = required;
366
+ if (jsonSchema.$defs) newPropertySchema[DEFS_KEY] = jsonSchema.$defs;
367
+ if (rootId) newPropertySchema[PARENT_ID_KEY] = rootId;
368
+ if (typeof jsonSchema.additionalProperties === "boolean") newPropertySchema[PARENT_ADDITIONAL_PROPERTIES_KEY] = jsonSchema.additionalProperties;
369
+ const reservedKeys = new Set([
370
+ "type",
371
+ "properties",
372
+ "required",
373
+ "additionalProperties",
374
+ "$defs",
375
+ "id"
376
+ ]);
377
+ const parentMetadata = {};
378
+ for (const [key, value] of Object.entries(jsonSchema)) if (!reservedKeys.has(key) && value !== void 0) parentMetadata[key] = value;
379
+ if (Object.keys(parentMetadata).length > 0) newPropertySchema[PARENT_METADATA_KEY] = parentMetadata;
380
+ properties[propertyKey] = newPropertySchema;
381
+ }
382
+ return properties;
383
+ }
384
+ function generateJsonSchema(schema, io) {
385
+ const generatedJsonSchema = "_zod" in schema ? toJSONSchema(schema, { io }) : zodV3ToOpenAPI(schema);
386
+ const $defs = "$defs" in generatedJsonSchema && generatedJsonSchema.$defs ? generatedJsonSchema.$defs : void 0;
387
+ const newSchema = cleanupRefs(generatedJsonSchema, io);
388
+ const newDefs = {};
389
+ Object.entries($defs || {}).forEach(([defKey, defValue]) => {
390
+ const newKey = io === "output" ? `${defKey}_Output` : defKey;
391
+ if (newDefs[newKey]) throw new Error(`[nestjs-zod] Duplicate id in $defs: ${newKey}`);
392
+ newDefs[newKey] = cleanupRefs(defValue, io);
393
+ });
394
+ if ($defs) newSchema.$defs = newDefs;
395
+ return newSchema;
396
+ }
397
+ /**
398
+ * Suffixes refs with `_Output` if `io` is `output`
399
+ *
400
+ * Also removes the `id` field from the schema, since this is not a valid
401
+ * openapi field. Some earlier versions of zod 4 included `id`
402
+ */
403
+ function cleanupRefs(rootSchema, io) {
404
+ return walkJsonSchema(rootSchema, (schema) => {
405
+ if (schema.$ref && schema.$ref.startsWith("#/$defs/")) {
406
+ const defKey = schema.$ref.replace("#/$defs/", "");
407
+ if (defKey && io === "output") schema.$ref = `#/$defs/${defKey}_Output`;
408
+ }
409
+ if ("id" in schema) delete schema["id"];
410
+ return schema;
411
+ }, { clone: true });
412
+ }
413
+ function getSchemaMetadata(jsonSchema) {
414
+ let hasRefs = false;
415
+ let usesThreePointOneSyntax = false;
416
+ walkJsonSchema(jsonSchema, (schema) => {
417
+ if (schema.type === "null" || schema.const || schema.id || "propertyNames" in schema || typeof schema.exclusiveMinimum === "number" || typeof schema.exclusiveMaximum === "number") usesThreePointOneSyntax = true;
418
+ if (schema.$ref) hasRefs = true;
419
+ return schema;
420
+ });
421
+ return {
422
+ hasRefs,
423
+ usesThreePointOneSyntax
424
+ };
425
+ }
426
+ function isZodDto(metatype) {
427
+ return Boolean(metatype && (typeof metatype === "object" || typeof metatype === "function") && "isZodDto" in metatype && metatype.isZodDto);
428
+ }
429
+ function isObjectTypeWithProperties(jsonSchema) {
430
+ return jsonSchema.type === "object" && !!jsonSchema.properties && Object.keys(jsonSchema.properties).length > 0;
431
+ }
432
+ //#endregion
433
+ export { assert as _, convertToOpenApi3Point0 as a, EMPTY_TYPE_KEY as c, PARENT_ID_KEY as d, PARENT_METADATA_KEY as f, configureZodDto as g, USES_THREE_POINT_ONE_SYNTAX_KEY as h, zodV3ToOpenAPI as i, PARENT_ADDITIONAL_PROPERTIES_KEY as l, UNWRAP_ROOT_KEY as m, isZodDto as n, fixAllRefs as o, SELF_REQUIRED_KEY as p, ioSymbol as r, DEFS_KEY as s, createZodDto as t, PARENT_HAS_REFS_KEY as u };