@digitaldefiance/node-express-suite 3.11.32 → 3.12.1

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.
Files changed (146) hide show
  1. package/README.md +874 -8
  2. package/package.json +5 -5
  3. package/src/controllers/openapi.d.ts +67 -0
  4. package/src/controllers/openapi.d.ts.map +1 -0
  5. package/src/controllers/openapi.js +89 -0
  6. package/src/controllers/openapi.js.map +1 -0
  7. package/src/decorators/auth.d.ts +128 -0
  8. package/src/decorators/auth.d.ts.map +1 -0
  9. package/src/decorators/auth.js +230 -0
  10. package/src/decorators/auth.js.map +1 -0
  11. package/src/decorators/base-controller.d.ts +144 -6
  12. package/src/decorators/base-controller.d.ts.map +1 -1
  13. package/src/decorators/base-controller.js +487 -31
  14. package/src/decorators/base-controller.js.map +1 -1
  15. package/src/decorators/controller.d.ts +63 -7
  16. package/src/decorators/controller.d.ts.map +1 -1
  17. package/src/decorators/controller.js +70 -39
  18. package/src/decorators/controller.js.map +1 -1
  19. package/src/decorators/handler-args.d.ts +68 -0
  20. package/src/decorators/handler-args.d.ts.map +1 -0
  21. package/src/decorators/handler-args.js +83 -0
  22. package/src/decorators/handler-args.js.map +1 -0
  23. package/src/decorators/http-methods.d.ts +143 -0
  24. package/src/decorators/http-methods.d.ts.map +1 -0
  25. package/src/decorators/http-methods.js +265 -0
  26. package/src/decorators/http-methods.js.map +1 -0
  27. package/src/decorators/index.d.ts +22 -0
  28. package/src/decorators/index.d.ts.map +1 -1
  29. package/src/decorators/index.js +56 -0
  30. package/src/decorators/index.js.map +1 -1
  31. package/src/decorators/lifecycle.d.ts +248 -0
  32. package/src/decorators/lifecycle.d.ts.map +1 -0
  33. package/src/decorators/lifecycle.js +301 -0
  34. package/src/decorators/lifecycle.js.map +1 -0
  35. package/src/decorators/metadata-collector.d.ts +175 -0
  36. package/src/decorators/metadata-collector.d.ts.map +1 -0
  37. package/src/decorators/metadata-collector.js +272 -0
  38. package/src/decorators/metadata-collector.js.map +1 -0
  39. package/src/decorators/metadata-keys.d.ts +121 -0
  40. package/src/decorators/metadata-keys.d.ts.map +1 -0
  41. package/src/decorators/metadata-keys.js +116 -0
  42. package/src/decorators/metadata-keys.js.map +1 -0
  43. package/src/decorators/middleware.d.ts +181 -0
  44. package/src/decorators/middleware.d.ts.map +1 -0
  45. package/src/decorators/middleware.js +400 -0
  46. package/src/decorators/middleware.js.map +1 -0
  47. package/src/decorators/openapi-params.d.ts +192 -0
  48. package/src/decorators/openapi-params.d.ts.map +1 -0
  49. package/src/decorators/openapi-params.js +332 -0
  50. package/src/decorators/openapi-params.js.map +1 -0
  51. package/src/decorators/openapi.d.ts +201 -0
  52. package/src/decorators/openapi.d.ts.map +1 -0
  53. package/src/decorators/openapi.js +334 -0
  54. package/src/decorators/openapi.js.map +1 -0
  55. package/src/decorators/params.d.ts +217 -0
  56. package/src/decorators/params.d.ts.map +1 -0
  57. package/src/decorators/params.js +323 -0
  58. package/src/decorators/params.js.map +1 -0
  59. package/src/decorators/response.d.ts +200 -0
  60. package/src/decorators/response.d.ts.map +1 -0
  61. package/src/decorators/response.js +315 -0
  62. package/src/decorators/response.js.map +1 -0
  63. package/src/decorators/schema.d.ts +99 -0
  64. package/src/decorators/schema.d.ts.map +1 -0
  65. package/src/decorators/schema.js +329 -0
  66. package/src/decorators/schema.js.map +1 -0
  67. package/src/decorators/transaction.d.ts +69 -0
  68. package/src/decorators/transaction.d.ts.map +1 -0
  69. package/src/decorators/transaction.js +80 -0
  70. package/src/decorators/transaction.js.map +1 -0
  71. package/src/decorators/validation.d.ts +188 -0
  72. package/src/decorators/validation.d.ts.map +1 -0
  73. package/src/decorators/validation.js +269 -0
  74. package/src/decorators/validation.js.map +1 -0
  75. package/src/decorators/zod-validation.d.ts +164 -4
  76. package/src/decorators/zod-validation.d.ts.map +1 -1
  77. package/src/decorators/zod-validation.js +692 -13
  78. package/src/decorators/zod-validation.js.map +1 -1
  79. package/src/index.d.ts +1 -0
  80. package/src/index.d.ts.map +1 -1
  81. package/src/index.js +1 -0
  82. package/src/index.js.map +1 -1
  83. package/src/interfaces/openApi/decoratorOptions.d.ts +760 -0
  84. package/src/interfaces/openApi/decoratorOptions.d.ts.map +1 -0
  85. package/src/interfaces/openApi/decoratorOptions.js +734 -0
  86. package/src/interfaces/openApi/decoratorOptions.js.map +1 -0
  87. package/src/interfaces/openApi/index.d.ts +1 -0
  88. package/src/interfaces/openApi/index.d.ts.map +1 -1
  89. package/src/interfaces/openApi/index.js +23 -0
  90. package/src/interfaces/openApi/index.js.map +1 -1
  91. package/src/interfaces/openApi/parameter.d.ts +2 -0
  92. package/src/interfaces/openApi/parameter.d.ts.map +1 -1
  93. package/src/interfaces/openApi/parameter.js +3 -1
  94. package/src/interfaces/openApi/parameter.js.map +1 -1
  95. package/src/interfaces/openApi/parameterSchema.d.ts +2 -0
  96. package/src/interfaces/openApi/parameterSchema.d.ts.map +1 -1
  97. package/src/interfaces/openApi/parameterSchema.js +3 -0
  98. package/src/interfaces/openApi/parameterSchema.js.map +1 -1
  99. package/src/openapi/builder.d.ts +249 -0
  100. package/src/openapi/builder.d.ts.map +1 -0
  101. package/src/openapi/builder.js +352 -0
  102. package/src/openapi/builder.js.map +1 -0
  103. package/src/openapi/controller.d.ts +153 -0
  104. package/src/openapi/controller.d.ts.map +1 -0
  105. package/src/openapi/controller.js +331 -0
  106. package/src/openapi/controller.js.map +1 -0
  107. package/src/openapi/index.d.ts +12 -0
  108. package/src/openapi/index.d.ts.map +1 -0
  109. package/src/openapi/index.js +20 -0
  110. package/src/openapi/index.js.map +1 -0
  111. package/src/openapi/markdown-generator.d.ts +52 -0
  112. package/src/openapi/markdown-generator.d.ts.map +1 -0
  113. package/src/openapi/markdown-generator.js +569 -0
  114. package/src/openapi/markdown-generator.js.map +1 -0
  115. package/src/openapi/middleware/index.d.ts +9 -0
  116. package/src/openapi/middleware/index.d.ts.map +1 -0
  117. package/src/openapi/middleware/index.js +15 -0
  118. package/src/openapi/middleware/index.js.map +1 -0
  119. package/src/openapi/middleware/redoc.d.ts +314 -0
  120. package/src/openapi/middleware/redoc.d.ts.map +1 -0
  121. package/src/openapi/middleware/redoc.js +181 -0
  122. package/src/openapi/middleware/redoc.js.map +1 -0
  123. package/src/openapi/middleware/swagger-ui.d.ts +123 -0
  124. package/src/openapi/middleware/swagger-ui.d.ts.map +1 -0
  125. package/src/openapi/middleware/swagger-ui.js +227 -0
  126. package/src/openapi/middleware/swagger-ui.js.map +1 -0
  127. package/src/openapi/schemas.d.ts +170 -0
  128. package/src/openapi/schemas.d.ts.map +1 -0
  129. package/src/openapi/schemas.js +340 -0
  130. package/src/openapi/schemas.js.map +1 -0
  131. package/src/registry/controller-registry.d.ts +78 -0
  132. package/src/registry/controller-registry.d.ts.map +1 -0
  133. package/src/registry/controller-registry.js +86 -0
  134. package/src/registry/controller-registry.js.map +1 -0
  135. package/src/registry/index.d.ts +2 -0
  136. package/src/registry/index.d.ts.map +1 -1
  137. package/src/registry/index.js +3 -1
  138. package/src/registry/index.js.map +1 -1
  139. package/src/routers/api.d.ts +2 -1
  140. package/src/routers/api.d.ts.map +1 -1
  141. package/src/routers/api.js +7 -1
  142. package/src/routers/api.js.map +1 -1
  143. package/src/types.d.ts +1 -0
  144. package/src/types.d.ts.map +1 -1
  145. package/src/types.js +1 -0
  146. package/src/types.js.map +1 -1
@@ -1,14 +1,626 @@
1
1
  "use strict";
2
2
  /**
3
- * @fileoverview Zod schema validation decorator.
4
- * Converts Zod schemas to express-validator chains.
3
+ * @fileoverview Zod schema validation decorator and OpenAPI conversion utilities.
4
+ * Converts Zod schemas to express-validator chains and OpenAPI schemas.
5
5
  * @module decorators/zod-validation
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.extractZodMetadata = extractZodMetadata;
9
+ exports.zodToOpenAPI = zodToOpenAPI;
10
+ exports.getZodDescription = getZodDescription;
11
+ exports.getZodExample = getZodExample;
12
+ exports.getZodExamples = getZodExamples;
13
+ exports.getZodTitle = getZodTitle;
14
+ exports.isZodDeprecated = isZodDeprecated;
8
15
  exports.zodToExpressValidator = zodToExpressValidator;
9
16
  exports.ZodValidate = ZodValidate;
10
17
  const express_validator_1 = require("express-validator");
11
18
  const zod_1 = require("zod");
19
+ /**
20
+ * Extracts OpenAPI metadata from a Zod schema.
21
+ * Checks for description via .describe() and custom metadata via ._def.
22
+ *
23
+ * @param schema - The Zod schema to extract metadata from
24
+ * @returns The extracted metadata object
25
+ */
26
+ function extractZodMetadata(schema) {
27
+ const metadata = {};
28
+ // Extract description from .describe()
29
+ if (schema._def.description) {
30
+ metadata.description = schema._def.description;
31
+ }
32
+ // Check for custom openapi metadata stored in _def
33
+ const def = schema._def;
34
+ if (def.openapi && typeof def.openapi === 'object') {
35
+ const openapi = def.openapi;
36
+ if (openapi.description)
37
+ metadata.description = openapi.description;
38
+ if (openapi.example !== undefined)
39
+ metadata.example = openapi.example;
40
+ if (openapi.examples)
41
+ metadata.examples = openapi.examples;
42
+ if (openapi.title)
43
+ metadata.title = openapi.title;
44
+ if (openapi.deprecated !== undefined)
45
+ metadata.deprecated = openapi.deprecated;
46
+ if (openapi.readOnly !== undefined)
47
+ metadata.readOnly = openapi.readOnly;
48
+ if (openapi.writeOnly !== undefined)
49
+ metadata.writeOnly = openapi.writeOnly;
50
+ if (openapi.format)
51
+ metadata.format = openapi.format;
52
+ if (openapi.default !== undefined)
53
+ metadata.default = openapi.default;
54
+ }
55
+ return metadata;
56
+ }
57
+ /**
58
+ * Applies extracted metadata to an OpenAPI schema.
59
+ *
60
+ * @param schema - The OpenAPI schema to apply metadata to
61
+ * @param metadata - The metadata to apply
62
+ * @returns The schema with metadata applied
63
+ */
64
+ function applyMetadata(schema, metadata) {
65
+ const result = { ...schema };
66
+ if (metadata.description) {
67
+ Object.assign(result, { description: metadata.description });
68
+ }
69
+ if (metadata.example !== undefined) {
70
+ Object.assign(result, { example: metadata.example });
71
+ }
72
+ if (metadata.title) {
73
+ Object.assign(result, { title: metadata.title });
74
+ }
75
+ if (metadata.deprecated !== undefined) {
76
+ Object.assign(result, { deprecated: metadata.deprecated });
77
+ }
78
+ if (metadata.readOnly !== undefined) {
79
+ Object.assign(result, { readOnly: metadata.readOnly });
80
+ }
81
+ if (metadata.writeOnly !== undefined) {
82
+ Object.assign(result, { writeOnly: metadata.writeOnly });
83
+ }
84
+ if (metadata.format && 'type' in result && result.type === 'string') {
85
+ Object.assign(result, { format: metadata.format });
86
+ }
87
+ return result;
88
+ }
89
+ /**
90
+ * Converts a Zod schema to an OpenAPI schema.
91
+ * Supports nested objects, arrays, unions, enums, and various Zod types.
92
+ * Extracts descriptions and examples from Zod schema metadata.
93
+ *
94
+ * @param schema - The Zod schema to convert
95
+ * @returns The OpenAPI schema representation
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const UserSchema = z.object({
100
+ * id: z.string().uuid().describe('Unique user identifier'),
101
+ * name: z.string().min(1).max(100).describe('User display name'),
102
+ * email: z.string().email().describe('User email address'),
103
+ * age: z.number().int().positive().optional().describe('User age in years'),
104
+ * roles: z.array(z.enum(['admin', 'user', 'guest'])).describe('User roles'),
105
+ * });
106
+ *
107
+ * const openApiSchema = zodToOpenAPI(UserSchema);
108
+ * // Returns:
109
+ * // {
110
+ * // type: 'object',
111
+ * // properties: {
112
+ * // id: { type: 'string', format: 'uuid', description: 'Unique user identifier' },
113
+ * // name: { type: 'string', minLength: 1, maxLength: 100, description: 'User display name' },
114
+ * // email: { type: 'string', format: 'email', description: 'User email address' },
115
+ * // age: { type: 'integer', minimum: 1, description: 'User age in years' },
116
+ * // roles: { type: 'array', items: { type: 'string', enum: ['admin', 'user', 'guest'] }, description: 'User roles' },
117
+ * // },
118
+ * // required: ['id', 'name', 'email', 'roles'],
119
+ * // }
120
+ * ```
121
+ */
122
+ function zodToOpenAPI(schema) {
123
+ return convertZodType(schema);
124
+ }
125
+ /**
126
+ * Internal function to convert a Zod type to OpenAPI schema.
127
+ * Handles all Zod type variants recursively.
128
+ * Extracts and applies metadata (description, example, etc.) from Zod schemas.
129
+ */
130
+ function convertZodType(zodType) {
131
+ // Extract metadata from the schema
132
+ const metadata = extractZodMetadata(zodType);
133
+ // Handle ZodOptional - unwrap and mark as not required
134
+ if (zodType instanceof zod_1.z.ZodOptional) {
135
+ const innerSchema = convertZodType(zodType._def.innerType);
136
+ return applyMetadata(innerSchema, metadata);
137
+ }
138
+ // Handle ZodNullable - add nullable: true
139
+ if (zodType instanceof zod_1.z.ZodNullable) {
140
+ const innerSchema = convertZodType(zodType._def.innerType);
141
+ return applyMetadata({ ...innerSchema, nullable: true }, metadata);
142
+ }
143
+ // Handle ZodDefault - unwrap and add default value
144
+ if (zodType instanceof zod_1.z.ZodDefault) {
145
+ const innerSchema = convertZodType(zodType._def.innerType);
146
+ const defaultValue = zodType._def.defaultValue();
147
+ return applyMetadata({ ...innerSchema, default: defaultValue }, metadata);
148
+ }
149
+ // Handle ZodEffects (refinements, transforms) - unwrap
150
+ if (zodType instanceof zod_1.z.ZodEffects) {
151
+ const innerSchema = convertZodType(zodType._def.schema);
152
+ return applyMetadata(innerSchema, metadata);
153
+ }
154
+ // Handle ZodCatch - unwrap
155
+ if (zodType instanceof zod_1.z.ZodCatch) {
156
+ const innerSchema = convertZodType(zodType._def.innerType);
157
+ return applyMetadata(innerSchema, metadata);
158
+ }
159
+ // Handle ZodBranded - unwrap
160
+ if (zodType instanceof zod_1.z.ZodBranded) {
161
+ const innerSchema = convertZodType(zodType._def.type);
162
+ return applyMetadata(innerSchema, metadata);
163
+ }
164
+ // Handle ZodReadonly - unwrap and mark as readOnly
165
+ if (zodType instanceof zod_1.z.ZodReadonly) {
166
+ const innerSchema = convertZodType(zodType._def.innerType);
167
+ return applyMetadata({ ...innerSchema, readOnly: true }, metadata);
168
+ }
169
+ // Handle ZodString
170
+ if (zodType instanceof zod_1.z.ZodString) {
171
+ return applyMetadata(convertZodString(zodType), metadata);
172
+ }
173
+ // Handle ZodNumber
174
+ if (zodType instanceof zod_1.z.ZodNumber) {
175
+ return applyMetadata(convertZodNumber(zodType), metadata);
176
+ }
177
+ // Handle ZodBigInt
178
+ if (zodType instanceof zod_1.z.ZodBigInt) {
179
+ return applyMetadata({ type: 'integer', format: 'int64' }, metadata);
180
+ }
181
+ // Handle ZodBoolean
182
+ if (zodType instanceof zod_1.z.ZodBoolean) {
183
+ return applyMetadata({ type: 'boolean' }, metadata);
184
+ }
185
+ // Handle ZodDate
186
+ if (zodType instanceof zod_1.z.ZodDate) {
187
+ return applyMetadata({ type: 'string', format: 'date-time' }, metadata);
188
+ }
189
+ // Handle ZodEnum
190
+ if (zodType instanceof zod_1.z.ZodEnum) {
191
+ return applyMetadata({
192
+ type: 'string',
193
+ enum: zodType._def.values,
194
+ }, metadata);
195
+ }
196
+ // Handle ZodNativeEnum
197
+ if (zodType instanceof zod_1.z.ZodNativeEnum) {
198
+ const enumValues = Object.values(zodType._def.values);
199
+ // Filter out numeric keys for numeric enums
200
+ const stringValues = enumValues.filter((v) => typeof v === 'string');
201
+ if (stringValues.length > 0) {
202
+ return applyMetadata({ type: 'string', enum: stringValues }, metadata);
203
+ }
204
+ // Numeric enum
205
+ return applyMetadata({
206
+ type: 'integer',
207
+ enum: enumValues
208
+ .filter((v) => typeof v === 'number')
209
+ .map(String),
210
+ }, metadata);
211
+ }
212
+ // Handle ZodLiteral
213
+ if (zodType instanceof zod_1.z.ZodLiteral) {
214
+ const value = zodType._def.value;
215
+ if (typeof value === 'string') {
216
+ return applyMetadata({ type: 'string', enum: [value] }, metadata);
217
+ }
218
+ if (typeof value === 'number') {
219
+ return applyMetadata({ type: 'number', enum: [String(value)] }, metadata);
220
+ }
221
+ if (typeof value === 'boolean') {
222
+ return applyMetadata({ type: 'boolean' }, metadata);
223
+ }
224
+ return applyMetadata({ type: 'string' }, metadata);
225
+ }
226
+ // Handle ZodArray
227
+ if (zodType instanceof zod_1.z.ZodArray) {
228
+ return applyMetadata(convertZodArray(zodType), metadata);
229
+ }
230
+ // Handle ZodObject
231
+ if (zodType instanceof zod_1.z.ZodObject) {
232
+ return applyMetadata(convertZodObject(zodType), metadata);
233
+ }
234
+ // Handle ZodUnion
235
+ if (zodType instanceof zod_1.z.ZodUnion) {
236
+ const options = zodType._def.options;
237
+ return applyMetadata({
238
+ oneOf: options.map((opt) => convertZodType(opt)),
239
+ }, metadata);
240
+ }
241
+ // Handle ZodDiscriminatedUnion
242
+ if (zodType instanceof zod_1.z.ZodDiscriminatedUnion) {
243
+ const discriminator = zodType._def.discriminator;
244
+ const options = Array.from(zodType._def.optionsMap.values());
245
+ const result = {
246
+ oneOf: options.map((opt) => convertZodType(opt)),
247
+ discriminator: {
248
+ propertyName: discriminator,
249
+ },
250
+ };
251
+ return applyMetadata(result, metadata);
252
+ }
253
+ // Handle ZodIntersection
254
+ if (zodType instanceof zod_1.z.ZodIntersection) {
255
+ const left = convertZodType(zodType._def.left);
256
+ const right = convertZodType(zodType._def.right);
257
+ // Merge the two schemas if they're both objects
258
+ if (isObjectSchema(left) && isObjectSchema(right)) {
259
+ return applyMetadata({
260
+ type: 'object',
261
+ properties: { ...left.properties, ...right.properties },
262
+ required: [...(left.required ?? []), ...(right.required ?? [])],
263
+ }, metadata);
264
+ }
265
+ return applyMetadata({
266
+ anyOf: [left, right],
267
+ }, metadata);
268
+ }
269
+ // Handle ZodRecord
270
+ if (zodType instanceof zod_1.z.ZodRecord) {
271
+ const valueSchema = convertZodType(zodType._def.valueType);
272
+ return applyMetadata({
273
+ type: 'object',
274
+ additionalProperties: valueSchema,
275
+ }, metadata);
276
+ }
277
+ // Handle ZodMap - convert to object with additionalProperties
278
+ if (zodType instanceof zod_1.z.ZodMap) {
279
+ const valueSchema = convertZodType(zodType._def.valueType);
280
+ return applyMetadata({
281
+ type: 'object',
282
+ additionalProperties: valueSchema,
283
+ }, metadata);
284
+ }
285
+ // Handle ZodSet - convert to array with uniqueItems
286
+ if (zodType instanceof zod_1.z.ZodSet) {
287
+ const valueSchema = convertZodType(zodType._def.valueType);
288
+ return applyMetadata({
289
+ type: 'array',
290
+ items: valueSchema,
291
+ uniqueItems: true,
292
+ }, metadata);
293
+ }
294
+ // Handle ZodTuple
295
+ if (zodType instanceof zod_1.z.ZodTuple) {
296
+ const items = zodType._def.items;
297
+ return applyMetadata({
298
+ type: 'array',
299
+ items: items.length === 1
300
+ ? convertZodType(items[0])
301
+ : { oneOf: items.map((item) => convertZodType(item)) },
302
+ minItems: items.length,
303
+ maxItems: items.length,
304
+ }, metadata);
305
+ }
306
+ // Handle ZodAny, ZodUnknown
307
+ if (zodType instanceof zod_1.z.ZodAny || zodType instanceof zod_1.z.ZodUnknown) {
308
+ return applyMetadata({ type: 'object' }, metadata);
309
+ }
310
+ // Handle ZodVoid, ZodUndefined, ZodNull, ZodNever
311
+ if (zodType instanceof zod_1.z.ZodVoid || zodType instanceof zod_1.z.ZodUndefined) {
312
+ return applyMetadata({ type: 'string' }, metadata);
313
+ }
314
+ if (zodType instanceof zod_1.z.ZodNull) {
315
+ return applyMetadata({ type: 'string', nullable: true }, metadata);
316
+ }
317
+ if (zodType instanceof zod_1.z.ZodNever) {
318
+ // ZodNever represents an impossible type - return empty schema
319
+ return applyMetadata({ type: 'string' }, metadata);
320
+ }
321
+ // Handle ZodNaN - represents NaN value
322
+ if (zodType instanceof zod_1.z.ZodNaN) {
323
+ return applyMetadata({ type: 'number' }, metadata);
324
+ }
325
+ // Handle ZodLazy - evaluate and convert
326
+ if (zodType instanceof zod_1.z.ZodLazy) {
327
+ const innerSchema = convertZodType(zodType._def.getter());
328
+ return applyMetadata(innerSchema, metadata);
329
+ }
330
+ // Handle ZodPipeline
331
+ if (zodType instanceof zod_1.z.ZodPipeline) {
332
+ const innerSchema = convertZodType(zodType._def.out);
333
+ return applyMetadata(innerSchema, metadata);
334
+ }
335
+ // Handle ZodPromise - unwrap the inner type
336
+ if (zodType instanceof zod_1.z.ZodPromise) {
337
+ const innerSchema = convertZodType(zodType._def.type);
338
+ return applyMetadata(innerSchema, metadata);
339
+ }
340
+ // Handle ZodFunction - not directly representable in OpenAPI
341
+ if (zodType instanceof zod_1.z.ZodFunction) {
342
+ return applyMetadata({ type: 'object' }, metadata);
343
+ }
344
+ // Handle ZodSymbol - not directly representable in OpenAPI
345
+ if (zodType instanceof zod_1.z.ZodSymbol) {
346
+ return applyMetadata({ type: 'string' }, metadata);
347
+ }
348
+ // Default fallback
349
+ return applyMetadata({ type: 'string' }, metadata);
350
+ }
351
+ /**
352
+ * Converts a ZodString to OpenAPI schema with all string validations.
353
+ */
354
+ function convertZodString(zodString) {
355
+ const schema = { type: 'string' };
356
+ if (zodString._def.checks) {
357
+ for (const check of zodString._def.checks) {
358
+ switch (check.kind) {
359
+ case 'min':
360
+ schema.minLength = check.value;
361
+ break;
362
+ case 'max':
363
+ schema.maxLength = check.value;
364
+ break;
365
+ case 'length':
366
+ schema.minLength = check.value;
367
+ schema.maxLength = check.value;
368
+ break;
369
+ case 'email':
370
+ schema.format = 'email';
371
+ break;
372
+ case 'url':
373
+ schema.format = 'uri';
374
+ break;
375
+ case 'uuid':
376
+ schema.format = 'uuid';
377
+ break;
378
+ case 'cuid':
379
+ case 'cuid2':
380
+ schema.format = 'cuid';
381
+ break;
382
+ case 'ulid':
383
+ schema.format = 'ulid';
384
+ break;
385
+ case 'datetime':
386
+ schema.format = 'date-time';
387
+ break;
388
+ case 'date':
389
+ schema.format = 'date';
390
+ break;
391
+ case 'time':
392
+ schema.format = 'time';
393
+ break;
394
+ case 'duration':
395
+ schema.format = 'duration';
396
+ break;
397
+ case 'ip':
398
+ schema.format = 'ip';
399
+ break;
400
+ case 'emoji':
401
+ // No direct OpenAPI equivalent
402
+ break;
403
+ case 'base64':
404
+ schema.format = 'byte';
405
+ break;
406
+ case 'nanoid':
407
+ schema.format = 'nanoid';
408
+ break;
409
+ case 'regex':
410
+ if (check.regex) {
411
+ schema.pattern = check.regex.source;
412
+ }
413
+ break;
414
+ case 'startsWith':
415
+ case 'endsWith':
416
+ case 'includes':
417
+ case 'toLowerCase':
418
+ case 'toUpperCase':
419
+ case 'trim':
420
+ // These don't have direct OpenAPI equivalents
421
+ break;
422
+ }
423
+ }
424
+ }
425
+ return schema;
426
+ }
427
+ /**
428
+ * Converts a ZodNumber to OpenAPI schema with all number validations.
429
+ */
430
+ function convertZodNumber(zodNumber) {
431
+ const schema = { type: 'number' };
432
+ if (zodNumber._def.checks) {
433
+ for (const check of zodNumber._def.checks) {
434
+ switch (check.kind) {
435
+ case 'int':
436
+ schema.type = 'integer';
437
+ break;
438
+ case 'min':
439
+ schema.minimum = check.value;
440
+ if (check.inclusive === false) {
441
+ schema.exclusiveMinimum = true;
442
+ }
443
+ break;
444
+ case 'max':
445
+ schema.maximum = check.value;
446
+ if (check.inclusive === false) {
447
+ schema.exclusiveMaximum = true;
448
+ }
449
+ break;
450
+ case 'multipleOf':
451
+ schema.multipleOf = check.value;
452
+ break;
453
+ case 'finite':
454
+ case 'safe':
455
+ // These don't have direct OpenAPI equivalents
456
+ break;
457
+ }
458
+ }
459
+ }
460
+ return schema;
461
+ }
462
+ /**
463
+ * Converts a ZodArray to OpenAPI array schema.
464
+ */
465
+ function convertZodArray(zodArray) {
466
+ const itemSchema = convertZodType(zodArray._def.type);
467
+ const schema = {
468
+ type: 'array',
469
+ items: itemSchema,
470
+ };
471
+ if (zodArray._def.minLength !== null &&
472
+ zodArray._def.minLength !== undefined) {
473
+ schema.minItems = zodArray._def.minLength.value;
474
+ }
475
+ if (zodArray._def.maxLength !== null &&
476
+ zodArray._def.maxLength !== undefined) {
477
+ schema.maxItems = zodArray._def.maxLength.value;
478
+ }
479
+ if (zodArray._def.exactLength !== null &&
480
+ zodArray._def.exactLength !== undefined) {
481
+ schema.minItems = zodArray._def.exactLength.value;
482
+ schema.maxItems = zodArray._def.exactLength.value;
483
+ }
484
+ return schema;
485
+ }
486
+ /**
487
+ * Converts a ZodObject to OpenAPI object schema.
488
+ */
489
+ function convertZodObject(zodObject) {
490
+ const properties = {};
491
+ const required = [];
492
+ const shape = zodObject._def.shape();
493
+ for (const [key, value] of Object.entries(shape)) {
494
+ const zodValue = value;
495
+ properties[key] = convertZodType(zodValue);
496
+ // Check if the field is required (not optional)
497
+ if (!isOptionalType(zodValue)) {
498
+ required.push(key);
499
+ }
500
+ }
501
+ const schema = {
502
+ type: 'object',
503
+ properties,
504
+ };
505
+ if (required.length > 0) {
506
+ schema.required = required;
507
+ }
508
+ return schema;
509
+ }
510
+ /**
511
+ * Checks if a Zod type is optional.
512
+ */
513
+ function isOptionalType(zodType) {
514
+ if (zodType instanceof zod_1.z.ZodOptional) {
515
+ return true;
516
+ }
517
+ if (zodType instanceof zod_1.z.ZodDefault) {
518
+ return true;
519
+ }
520
+ if (zodType instanceof zod_1.z.ZodNullable) {
521
+ return isOptionalType(zodType._def.innerType);
522
+ }
523
+ return false;
524
+ }
525
+ /**
526
+ * Type guard to check if a schema is an object schema.
527
+ */
528
+ function isObjectSchema(schema) {
529
+ return 'type' in schema && schema.type === 'object' && 'properties' in schema;
530
+ }
531
+ /**
532
+ * Extracts description from a Zod schema if available.
533
+ *
534
+ * @param schema - The Zod schema
535
+ * @returns The description or undefined
536
+ */
537
+ function getZodDescription(schema) {
538
+ // First check the direct description
539
+ if (schema._def.description) {
540
+ return schema._def.description;
541
+ }
542
+ // Check for custom openapi metadata
543
+ const def = schema._def;
544
+ if (def.openapi && typeof def.openapi === 'object') {
545
+ const openapi = def.openapi;
546
+ if (openapi.description) {
547
+ return openapi.description;
548
+ }
549
+ }
550
+ return undefined;
551
+ }
552
+ /**
553
+ * Extracts example from a Zod schema if available.
554
+ * Checks for custom openapi metadata that may contain examples.
555
+ *
556
+ * @param schema - The Zod schema
557
+ * @returns The example or undefined
558
+ */
559
+ function getZodExample(schema) {
560
+ // Check for custom openapi metadata
561
+ const def = schema._def;
562
+ if (def.openapi && typeof def.openapi === 'object') {
563
+ const openapi = def.openapi;
564
+ if (openapi.example !== undefined) {
565
+ return openapi.example;
566
+ }
567
+ if (openapi.examples && openapi.examples.length > 0) {
568
+ return openapi.examples[0];
569
+ }
570
+ }
571
+ // Check for default value as a fallback example
572
+ if (schema instanceof zod_1.z.ZodDefault) {
573
+ return schema._def.defaultValue();
574
+ }
575
+ return undefined;
576
+ }
577
+ /**
578
+ * Extracts all examples from a Zod schema if available.
579
+ *
580
+ * @param schema - The Zod schema
581
+ * @returns Array of examples or undefined
582
+ */
583
+ function getZodExamples(schema) {
584
+ const def = schema._def;
585
+ if (def.openapi && typeof def.openapi === 'object') {
586
+ const openapi = def.openapi;
587
+ if (openapi.examples && openapi.examples.length > 0) {
588
+ return openapi.examples;
589
+ }
590
+ if (openapi.example !== undefined) {
591
+ return [openapi.example];
592
+ }
593
+ }
594
+ return undefined;
595
+ }
596
+ /**
597
+ * Extracts the title from a Zod schema if available.
598
+ *
599
+ * @param schema - The Zod schema
600
+ * @returns The title or undefined
601
+ */
602
+ function getZodTitle(schema) {
603
+ const def = schema._def;
604
+ if (def.openapi && typeof def.openapi === 'object') {
605
+ const openapi = def.openapi;
606
+ return openapi.title;
607
+ }
608
+ return undefined;
609
+ }
610
+ /**
611
+ * Checks if a Zod schema is marked as deprecated.
612
+ *
613
+ * @param schema - The Zod schema
614
+ * @returns True if deprecated, false otherwise
615
+ */
616
+ function isZodDeprecated(schema) {
617
+ const def = schema._def;
618
+ if (def.openapi && typeof def.openapi === 'object') {
619
+ const openapi = def.openapi;
620
+ return openapi.deprecated === true;
621
+ }
622
+ return false;
623
+ }
12
624
  // Convert Zod schema to express-validator chains
13
625
  function zodToExpressValidator(schema) {
14
626
  return (_lang) => {
@@ -17,25 +629,92 @@ function zodToExpressValidator(schema) {
17
629
  if (!(schema instanceof zod_1.z.ZodObject)) {
18
630
  return chains;
19
631
  }
20
- Object.entries(schema.shape).forEach(([key, zodType]) => {
632
+ const shape = schema._def.shape();
633
+ Object.entries(shape).forEach(([key, zodType]) => {
21
634
  let chain = (0, express_validator_1.body)(key);
635
+ let currentType = zodType;
22
636
  // Handle optional fields
23
- if (zodType instanceof zod_1.z.ZodOptional) {
637
+ if (currentType instanceof zod_1.z.ZodOptional) {
638
+ chain = chain.optional();
639
+ currentType = currentType._def.innerType;
640
+ }
641
+ // Handle nullable fields
642
+ if (currentType instanceof zod_1.z.ZodNullable) {
643
+ chain = chain.optional({ values: 'null' });
644
+ currentType = currentType._def.innerType;
645
+ }
646
+ // Handle default fields
647
+ if (currentType instanceof zod_1.z.ZodDefault) {
24
648
  chain = chain.optional();
25
- zodType = zodType._def.innerType;
649
+ currentType = currentType._def.innerType;
26
650
  }
27
651
  // Handle string validations
28
- if (zodType instanceof zod_1.z.ZodString) {
652
+ if (currentType instanceof zod_1.z.ZodString) {
29
653
  chain = chain.isString();
30
- // Handle min length
31
- if (zodType._def.checks) {
32
- zodType._def.checks.forEach((check) => {
33
- const checkObj = check;
34
- if (checkObj.kind === 'min') {
35
- chain = chain.isLength({ min: checkObj.value });
654
+ if (currentType._def.checks) {
655
+ for (const check of currentType._def.checks) {
656
+ switch (check.kind) {
657
+ case 'min':
658
+ chain = chain.isLength({ min: check.value });
659
+ break;
660
+ case 'max':
661
+ chain = chain.isLength({ max: check.value });
662
+ break;
663
+ case 'length':
664
+ chain = chain.isLength({ min: check.value, max: check.value });
665
+ break;
666
+ case 'email':
667
+ chain = chain.isEmail();
668
+ break;
669
+ case 'url':
670
+ chain = chain.isURL();
671
+ break;
672
+ case 'uuid':
673
+ chain = chain.isUUID();
674
+ break;
675
+ }
676
+ }
677
+ }
678
+ }
679
+ // Handle number validations
680
+ if (currentType instanceof zod_1.z.ZodNumber) {
681
+ let isInt = false;
682
+ const numericOptions = {};
683
+ if (currentType._def.checks) {
684
+ for (const check of currentType._def.checks) {
685
+ switch (check.kind) {
686
+ case 'int':
687
+ isInt = true;
688
+ break;
689
+ case 'min':
690
+ numericOptions.min = check.value;
691
+ break;
692
+ case 'max':
693
+ numericOptions.max = check.value;
694
+ break;
36
695
  }
37
- });
696
+ }
38
697
  }
698
+ if (isInt) {
699
+ chain = chain.isInt(numericOptions);
700
+ }
701
+ else {
702
+ chain = chain.isNumeric();
703
+ if (numericOptions.min !== undefined) {
704
+ chain = chain.custom((value) => Number(value) >= numericOptions.min);
705
+ }
706
+ if (numericOptions.max !== undefined) {
707
+ chain = chain.custom((value) => Number(value) <= numericOptions.max);
708
+ }
709
+ }
710
+ }
711
+ // Handle boolean validations
712
+ if (currentType instanceof zod_1.z.ZodBoolean) {
713
+ chain = chain.isBoolean();
714
+ }
715
+ // Handle array validations
716
+ if (currentType instanceof zod_1.z.ZodArray) {
717
+ chain = chain.isArray();
39
718
  }
40
719
  chains.push(chain);
41
720
  });