zod-openapi 2.15.2 → 2.17.0-beta.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.
- package/README.md +32 -13
- package/lib-commonjs/extend.js +59 -0
- package/lib-commonjs/index.js +115 -78
- package/lib-esm/extend.mjs +42 -0
- package/lib-esm/index.mjs +115 -78
- package/lib-types/create/schema/parsers/string.d.ts +2 -2
- package/lib-types/extend.d.ts +1 -0
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -29,12 +29,25 @@ pnpm install zod zod-openapi
|
|
|
29
29
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### Extend Zod
|
|
33
33
|
|
|
34
34
|
This mutates Zod to add an extra `.openapi()` method. Call this at the top of your entry point(s).
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
#### Automatic
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import 'zod-openapi/extend';
|
|
37
40
|
import { z } from 'zod';
|
|
41
|
+
|
|
42
|
+
z.string().openapi({ description: 'hello world!', example: 'hello world' });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Manual
|
|
46
|
+
|
|
47
|
+
This is useful if you have a different instance of Zod that you would like to extend.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { z } from 'another-lib';
|
|
38
51
|
import { extendZodWithOpenApi } from 'zod-openapi';
|
|
39
52
|
|
|
40
53
|
extendZodWithOpenApi(z);
|
|
@@ -65,11 +78,10 @@ Creates an OpenAPI documentation object
|
|
|
65
78
|
import { z } from 'zod';
|
|
66
79
|
import { createDocument, extendZodWithOpenApi } from 'zod-openapi';
|
|
67
80
|
|
|
68
|
-
extendZodWithOpenApi(z);
|
|
69
|
-
|
|
70
81
|
const jobId = z.string().openapi({
|
|
71
|
-
description: '
|
|
82
|
+
description: 'A unique identifier for a job',
|
|
72
83
|
example: '12345',
|
|
84
|
+
ref: 'jobId',
|
|
73
85
|
});
|
|
74
86
|
|
|
75
87
|
const title = z.string().openapi({
|
|
@@ -122,10 +134,9 @@ Generates the following object:
|
|
|
122
134
|
{
|
|
123
135
|
"in": "path",
|
|
124
136
|
"name": "jobId",
|
|
137
|
+
"description": "A unique identifier for a job",
|
|
125
138
|
"schema": {
|
|
126
|
-
"
|
|
127
|
-
"description": "Job ID",
|
|
128
|
-
"example": "12345"
|
|
139
|
+
"$ref": "#/components/schemas/jobId"
|
|
129
140
|
}
|
|
130
141
|
}
|
|
131
142
|
],
|
|
@@ -155,9 +166,7 @@ Generates the following object:
|
|
|
155
166
|
"type": "object",
|
|
156
167
|
"properties": {
|
|
157
168
|
"jobId": {
|
|
158
|
-
"
|
|
159
|
-
"description": "Job ID",
|
|
160
|
-
"example": "12345"
|
|
169
|
+
"$ref": "#/components/schemas/jobId"
|
|
161
170
|
},
|
|
162
171
|
"title": {
|
|
163
172
|
"type": "string",
|
|
@@ -173,6 +182,15 @@ Generates the following object:
|
|
|
173
182
|
}
|
|
174
183
|
}
|
|
175
184
|
}
|
|
185
|
+
},
|
|
186
|
+
"components": {
|
|
187
|
+
"schemas": {
|
|
188
|
+
"jobId": {
|
|
189
|
+
"type": "string",
|
|
190
|
+
"description": "A unique identifier for a job",
|
|
191
|
+
"example": "12345"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
176
194
|
}
|
|
177
195
|
}
|
|
178
196
|
```
|
|
@@ -512,7 +530,7 @@ For example in `z.string().nullable()` will be rendered differently
|
|
|
512
530
|
- `string` `type` mapping by default
|
|
513
531
|
- ZodDefault
|
|
514
532
|
- ZodDiscriminatedUnion
|
|
515
|
-
- `discriminator` mapping when all schemas in the union
|
|
533
|
+
- `discriminator` mapping when all schemas in the union are [registered](#creating-components). The discriminator must be a `ZodLiteral`, `ZodEnum` or `ZodNativeEnum` with string values. Only values wrapped in `ZodBranded`, `ZodReadOnly` and `ZodCatch` are supported.
|
|
516
534
|
- ZodEffects
|
|
517
535
|
- `transform` support for request schemas. See [Zod Effects](#zod-effects) for how to enable response schema support
|
|
518
536
|
- `pre-process` support. We assume that the input type is the same as the output type. Otherwise pipe and transform can be used instead.
|
|
@@ -541,9 +559,10 @@ For example in `z.string().nullable()` will be rendered differently
|
|
|
541
559
|
- ZodSet
|
|
542
560
|
- Treated as an array with `uniqueItems` (you may need to add a pre-process)
|
|
543
561
|
- ZodString
|
|
544
|
-
- `format` mapping for `.url()`, `.uuid()`, `.email()`, `.datetime()`
|
|
562
|
+
- `format` mapping for `.url()`, `.uuid()`, `.email()`, `.datetime()`, `.date()`, `.time()`, `.duration()`
|
|
545
563
|
- `minLength`/`maxLength` mapping for `.length()`, `.min()`, `.max()`
|
|
546
564
|
- `pattern` mapping for `.regex()`, `.startsWith()`, `.endsWith()`, `.includes()`
|
|
565
|
+
- `contentEncoding` mapping for `.base64()` for OpenAPI 3.1.0+
|
|
547
566
|
- ZodTuple
|
|
548
567
|
- `items` mapping for `.rest()`
|
|
549
568
|
- `prefixItems` mapping for OpenAPI 3.1.0+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/extend.ts
|
|
17
|
+
var extend_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(extend_exports);
|
|
19
|
+
var import_zod = require("zod");
|
|
20
|
+
|
|
21
|
+
// src/extendZod.ts
|
|
22
|
+
function extendZodWithOpenApi(zod) {
|
|
23
|
+
if (typeof zod.ZodType.prototype.openapi !== "undefined") {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
zod.ZodType.prototype.openapi = function(openapi) {
|
|
27
|
+
const result = new this.constructor({
|
|
28
|
+
...this._def,
|
|
29
|
+
openapi
|
|
30
|
+
});
|
|
31
|
+
return result;
|
|
32
|
+
};
|
|
33
|
+
const zodObjectExtend = zod.ZodObject.prototype.extend;
|
|
34
|
+
zod.ZodObject.prototype.extend = function(...args) {
|
|
35
|
+
const extendResult = zodObjectExtend.apply(this, args);
|
|
36
|
+
extendResult._def.extendMetadata = {
|
|
37
|
+
extends: this
|
|
38
|
+
};
|
|
39
|
+
delete extendResult._def.openapi;
|
|
40
|
+
return extendResult;
|
|
41
|
+
};
|
|
42
|
+
const zodObjectOmit = zod.ZodObject.prototype.omit;
|
|
43
|
+
zod.ZodObject.prototype.omit = function(...args) {
|
|
44
|
+
const omitResult = zodObjectOmit.apply(this, args);
|
|
45
|
+
delete omitResult._def.extendMetadata;
|
|
46
|
+
delete omitResult._def.openapi;
|
|
47
|
+
return omitResult;
|
|
48
|
+
};
|
|
49
|
+
const zodObjectPick = zod.ZodObject.prototype.pick;
|
|
50
|
+
zod.ZodObject.prototype.pick = function(...args) {
|
|
51
|
+
const pickResult = zodObjectPick.apply(this, args);
|
|
52
|
+
delete pickResult._def.extendMetadata;
|
|
53
|
+
delete pickResult._def.openapi;
|
|
54
|
+
return pickResult;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/extend.ts
|
|
59
|
+
extendZodWithOpenApi(import_zod.z);
|
package/lib-commonjs/index.js
CHANGED
|
@@ -108,6 +108,67 @@ var createDefaultSchema = (zodDefault, state) => {
|
|
|
108
108
|
});
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
+
// src/openapi.ts
|
|
112
|
+
var openApiVersions = [
|
|
113
|
+
"3.0.0",
|
|
114
|
+
"3.0.1",
|
|
115
|
+
"3.0.2",
|
|
116
|
+
"3.0.3",
|
|
117
|
+
"3.1.0"
|
|
118
|
+
];
|
|
119
|
+
var satisfiesVersion = (test, against) => openApiVersions.indexOf(test) >= openApiVersions.indexOf(against);
|
|
120
|
+
|
|
121
|
+
// src/create/schema/parsers/nativeEnum.ts
|
|
122
|
+
var createNativeEnumSchema = (zodEnum, state) => {
|
|
123
|
+
const enumValues = getValidEnumValues(zodEnum._def.values);
|
|
124
|
+
const { numbers, strings } = sortStringsAndNumbers(enumValues);
|
|
125
|
+
if (strings.length && numbers.length) {
|
|
126
|
+
if (satisfiesVersion(state.components.openapi, "3.1.0"))
|
|
127
|
+
return {
|
|
128
|
+
type: "schema",
|
|
129
|
+
schema: {
|
|
130
|
+
type: ["string", "number"],
|
|
131
|
+
enum: [...strings, ...numbers]
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
type: "schema",
|
|
136
|
+
schema: {
|
|
137
|
+
oneOf: [
|
|
138
|
+
{ type: "string", enum: strings },
|
|
139
|
+
{ type: "number", enum: numbers }
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (strings.length) {
|
|
145
|
+
return {
|
|
146
|
+
type: "schema",
|
|
147
|
+
schema: {
|
|
148
|
+
type: "string",
|
|
149
|
+
enum: strings
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
type: "schema",
|
|
155
|
+
schema: {
|
|
156
|
+
type: "number",
|
|
157
|
+
enum: numbers
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
var getValidEnumValues = (enumValues) => {
|
|
162
|
+
const keys = Object.keys(enumValues).filter(
|
|
163
|
+
(key) => typeof enumValues[enumValues[key]] !== "number"
|
|
164
|
+
);
|
|
165
|
+
return keys.map((key) => enumValues[key]);
|
|
166
|
+
};
|
|
167
|
+
var sortStringsAndNumbers = (values) => ({
|
|
168
|
+
strings: values.filter((value) => typeof value === "string"),
|
|
169
|
+
numbers: values.filter((value) => typeof value === "number")
|
|
170
|
+
});
|
|
171
|
+
|
|
111
172
|
// src/create/schema/parsers/transform.ts
|
|
112
173
|
var createTransformSchema = (zodTransform, state) => {
|
|
113
174
|
if (zodTransform._def.openapi?.effectType === "output") {
|
|
@@ -290,6 +351,33 @@ var createDiscriminatedUnionSchema = (zodDiscriminatedUnion, state) => {
|
|
|
290
351
|
effects: flattenEffects(schemas.map((schema) => schema.effects))
|
|
291
352
|
};
|
|
292
353
|
};
|
|
354
|
+
var unwrapLiterals = (zodType, state) => {
|
|
355
|
+
if (isZodType(zodType, "ZodLiteral")) {
|
|
356
|
+
if (typeof zodType._def.value !== "string") {
|
|
357
|
+
return void 0;
|
|
358
|
+
}
|
|
359
|
+
return [zodType._def.value];
|
|
360
|
+
}
|
|
361
|
+
if (isZodType(zodType, "ZodNativeEnum")) {
|
|
362
|
+
const schema = createNativeEnumSchema(zodType, state);
|
|
363
|
+
if (schema.type === "schema" && schema.schema.type === "string") {
|
|
364
|
+
return schema.schema.enum;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (isZodType(zodType, "ZodEnum")) {
|
|
368
|
+
return zodType._def.values;
|
|
369
|
+
}
|
|
370
|
+
if (isZodType(zodType, "ZodBranded")) {
|
|
371
|
+
return unwrapLiterals(zodType._def.type, state);
|
|
372
|
+
}
|
|
373
|
+
if (isZodType(zodType, "ZodReadonly")) {
|
|
374
|
+
return unwrapLiterals(zodType._def.innerType, state);
|
|
375
|
+
}
|
|
376
|
+
if (isZodType(zodType, "ZodCatch")) {
|
|
377
|
+
return unwrapLiterals(zodType._def.innerType, state);
|
|
378
|
+
}
|
|
379
|
+
return void 0;
|
|
380
|
+
};
|
|
293
381
|
var mapDiscriminator = (schemas, zodObjects, discriminator, state) => {
|
|
294
382
|
if (typeof discriminator !== "string") {
|
|
295
383
|
return void 0;
|
|
@@ -302,21 +390,13 @@ var mapDiscriminator = (schemas, zodObjects, discriminator, state) => {
|
|
|
302
390
|
return void 0;
|
|
303
391
|
}
|
|
304
392
|
const value = zodObject.shape[discriminator];
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
continue;
|
|
393
|
+
const literals = unwrapLiterals(value, state);
|
|
394
|
+
if (!literals) {
|
|
395
|
+
return void 0;
|
|
310
396
|
}
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
throw new Error(
|
|
314
|
-
`Discriminator ${discriminator} could not be found in on index ${index} of a discriminated union at ${state.path.join(
|
|
315
|
-
" > "
|
|
316
|
-
)}`
|
|
317
|
-
);
|
|
397
|
+
for (const enumValue of literals) {
|
|
398
|
+
mapping[enumValue] = componentSchemaRef;
|
|
318
399
|
}
|
|
319
|
-
mapping[literalValue] = componentSchemaRef;
|
|
320
400
|
}
|
|
321
401
|
return {
|
|
322
402
|
propertyName: discriminator,
|
|
@@ -356,16 +436,6 @@ var createLazySchema = (zodLazy, state) => {
|
|
|
356
436
|
return createSchemaObject(innerSchema, state, ["lazy schema"]);
|
|
357
437
|
};
|
|
358
438
|
|
|
359
|
-
// src/openapi.ts
|
|
360
|
-
var openApiVersions = [
|
|
361
|
-
"3.0.0",
|
|
362
|
-
"3.0.1",
|
|
363
|
-
"3.0.2",
|
|
364
|
-
"3.0.3",
|
|
365
|
-
"3.1.0"
|
|
366
|
-
];
|
|
367
|
-
var satisfiesVersion = (test, against) => openApiVersions.indexOf(test) >= openApiVersions.indexOf(against);
|
|
368
|
-
|
|
369
439
|
// src/create/schema/parsers/null.ts
|
|
370
440
|
var createNullSchema = () => ({
|
|
371
441
|
type: "schema",
|
|
@@ -415,57 +485,6 @@ var createManualTypeSchema = (zodSchema, state) => {
|
|
|
415
485
|
};
|
|
416
486
|
};
|
|
417
487
|
|
|
418
|
-
// src/create/schema/parsers/nativeEnum.ts
|
|
419
|
-
var createNativeEnumSchema = (zodEnum, state) => {
|
|
420
|
-
const enumValues = getValidEnumValues(zodEnum._def.values);
|
|
421
|
-
const { numbers, strings } = sortStringsAndNumbers(enumValues);
|
|
422
|
-
if (strings.length && numbers.length) {
|
|
423
|
-
if (satisfiesVersion(state.components.openapi, "3.1.0"))
|
|
424
|
-
return {
|
|
425
|
-
type: "schema",
|
|
426
|
-
schema: {
|
|
427
|
-
type: ["string", "number"],
|
|
428
|
-
enum: [...strings, ...numbers]
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
return {
|
|
432
|
-
type: "schema",
|
|
433
|
-
schema: {
|
|
434
|
-
oneOf: [
|
|
435
|
-
{ type: "string", enum: strings },
|
|
436
|
-
{ type: "number", enum: numbers }
|
|
437
|
-
]
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
if (strings.length) {
|
|
442
|
-
return {
|
|
443
|
-
type: "schema",
|
|
444
|
-
schema: {
|
|
445
|
-
type: "string",
|
|
446
|
-
enum: strings
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
return {
|
|
451
|
-
type: "schema",
|
|
452
|
-
schema: {
|
|
453
|
-
type: "number",
|
|
454
|
-
enum: numbers
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
};
|
|
458
|
-
var getValidEnumValues = (enumValues) => {
|
|
459
|
-
const keys = Object.keys(enumValues).filter(
|
|
460
|
-
(key) => typeof enumValues[enumValues[key]] !== "number"
|
|
461
|
-
);
|
|
462
|
-
return keys.map((key) => enumValues[key]);
|
|
463
|
-
};
|
|
464
|
-
var sortStringsAndNumbers = (values) => ({
|
|
465
|
-
strings: values.filter((value) => typeof value === "string"),
|
|
466
|
-
numbers: values.filter((value) => typeof value === "number")
|
|
467
|
-
});
|
|
468
|
-
|
|
469
488
|
// src/create/schema/parsers/nullable.ts
|
|
470
489
|
var createNullableSchema = (zodNullable, state) => {
|
|
471
490
|
const schemaObject = createSchemaObject(zodNullable.unwrap(), state, [
|
|
@@ -971,12 +990,13 @@ var createSetSchema = (zodSet, state) => {
|
|
|
971
990
|
};
|
|
972
991
|
|
|
973
992
|
// src/create/schema/parsers/string.ts
|
|
974
|
-
var createStringSchema = (zodString) => {
|
|
993
|
+
var createStringSchema = (zodString, state) => {
|
|
975
994
|
const zodStringChecks = getZodStringChecks(zodString);
|
|
976
995
|
const format = mapStringFormat(zodStringChecks);
|
|
977
996
|
const patterns = mapPatterns(zodStringChecks);
|
|
978
997
|
const minLength = zodStringChecks.length?.[0]?.value ?? zodStringChecks.min?.[0]?.value;
|
|
979
998
|
const maxLength = zodStringChecks.length?.[0]?.value ?? zodStringChecks.max?.[0]?.value;
|
|
999
|
+
const contentEncoding = satisfiesVersion(state.components.openapi, "3.1.0") ? mapContentEncoding(zodStringChecks) : void 0;
|
|
980
1000
|
if (patterns.length <= 1) {
|
|
981
1001
|
return {
|
|
982
1002
|
type: "schema",
|
|
@@ -985,7 +1005,8 @@ var createStringSchema = (zodString) => {
|
|
|
985
1005
|
...format && { format },
|
|
986
1006
|
...patterns[0] && { pattern: patterns[0] },
|
|
987
1007
|
...minLength !== void 0 && { minLength },
|
|
988
|
-
...maxLength !== void 0 && { maxLength }
|
|
1008
|
+
...maxLength !== void 0 && { maxLength },
|
|
1009
|
+
...contentEncoding && { contentEncoding }
|
|
989
1010
|
}
|
|
990
1011
|
};
|
|
991
1012
|
}
|
|
@@ -998,7 +1019,8 @@ var createStringSchema = (zodString) => {
|
|
|
998
1019
|
...format && { format },
|
|
999
1020
|
...patterns[0] && { pattern: patterns[0] },
|
|
1000
1021
|
...minLength !== void 0 && { minLength },
|
|
1001
|
-
...maxLength !== void 0 && { maxLength }
|
|
1022
|
+
...maxLength !== void 0 && { maxLength },
|
|
1023
|
+
...contentEncoding && { contentEncoding }
|
|
1002
1024
|
},
|
|
1003
1025
|
...patterns.slice(1).map(
|
|
1004
1026
|
(pattern) => ({
|
|
@@ -1064,6 +1086,15 @@ var mapStringFormat = (zodStringChecks) => {
|
|
|
1064
1086
|
if (zodStringChecks.datetime) {
|
|
1065
1087
|
return "date-time";
|
|
1066
1088
|
}
|
|
1089
|
+
if (zodStringChecks.date) {
|
|
1090
|
+
return "date";
|
|
1091
|
+
}
|
|
1092
|
+
if (zodStringChecks.time) {
|
|
1093
|
+
return "time";
|
|
1094
|
+
}
|
|
1095
|
+
if (zodStringChecks.duration) {
|
|
1096
|
+
return "duration";
|
|
1097
|
+
}
|
|
1067
1098
|
if (zodStringChecks.email) {
|
|
1068
1099
|
return "email";
|
|
1069
1100
|
}
|
|
@@ -1072,6 +1103,12 @@ var mapStringFormat = (zodStringChecks) => {
|
|
|
1072
1103
|
}
|
|
1073
1104
|
return void 0;
|
|
1074
1105
|
};
|
|
1106
|
+
var mapContentEncoding = (zodStringChecks) => {
|
|
1107
|
+
if (zodStringChecks.base64) {
|
|
1108
|
+
return "base64";
|
|
1109
|
+
}
|
|
1110
|
+
return void 0;
|
|
1111
|
+
};
|
|
1075
1112
|
|
|
1076
1113
|
// src/create/schema/parsers/tuple.ts
|
|
1077
1114
|
var createTupleSchema = (zodTuple, state) => {
|
|
@@ -1191,7 +1228,7 @@ var createSchemaSwitch = (zodSchema, state) => {
|
|
|
1191
1228
|
return createManualTypeSchema(zodSchema, state);
|
|
1192
1229
|
}
|
|
1193
1230
|
if (isZodType(zodSchema, "ZodString")) {
|
|
1194
|
-
return createStringSchema(zodSchema);
|
|
1231
|
+
return createStringSchema(zodSchema, state);
|
|
1195
1232
|
}
|
|
1196
1233
|
if (isZodType(zodSchema, "ZodNumber")) {
|
|
1197
1234
|
return createNumberSchema(zodSchema, state);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/extend.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/extendZod.ts
|
|
5
|
+
function extendZodWithOpenApi(zod) {
|
|
6
|
+
if (typeof zod.ZodType.prototype.openapi !== "undefined") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
zod.ZodType.prototype.openapi = function(openapi) {
|
|
10
|
+
const result = new this.constructor({
|
|
11
|
+
...this._def,
|
|
12
|
+
openapi
|
|
13
|
+
});
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
const zodObjectExtend = zod.ZodObject.prototype.extend;
|
|
17
|
+
zod.ZodObject.prototype.extend = function(...args) {
|
|
18
|
+
const extendResult = zodObjectExtend.apply(this, args);
|
|
19
|
+
extendResult._def.extendMetadata = {
|
|
20
|
+
extends: this
|
|
21
|
+
};
|
|
22
|
+
delete extendResult._def.openapi;
|
|
23
|
+
return extendResult;
|
|
24
|
+
};
|
|
25
|
+
const zodObjectOmit = zod.ZodObject.prototype.omit;
|
|
26
|
+
zod.ZodObject.prototype.omit = function(...args) {
|
|
27
|
+
const omitResult = zodObjectOmit.apply(this, args);
|
|
28
|
+
delete omitResult._def.extendMetadata;
|
|
29
|
+
delete omitResult._def.openapi;
|
|
30
|
+
return omitResult;
|
|
31
|
+
};
|
|
32
|
+
const zodObjectPick = zod.ZodObject.prototype.pick;
|
|
33
|
+
zod.ZodObject.prototype.pick = function(...args) {
|
|
34
|
+
const pickResult = zodObjectPick.apply(this, args);
|
|
35
|
+
delete pickResult._def.extendMetadata;
|
|
36
|
+
delete pickResult._def.openapi;
|
|
37
|
+
return pickResult;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/extend.ts
|
|
42
|
+
extendZodWithOpenApi(z);
|
package/lib-esm/index.mjs
CHANGED
|
@@ -84,6 +84,67 @@ var createDefaultSchema = (zodDefault, state) => {
|
|
|
84
84
|
});
|
|
85
85
|
};
|
|
86
86
|
|
|
87
|
+
// src/openapi.ts
|
|
88
|
+
var openApiVersions = [
|
|
89
|
+
"3.0.0",
|
|
90
|
+
"3.0.1",
|
|
91
|
+
"3.0.2",
|
|
92
|
+
"3.0.3",
|
|
93
|
+
"3.1.0"
|
|
94
|
+
];
|
|
95
|
+
var satisfiesVersion = (test, against) => openApiVersions.indexOf(test) >= openApiVersions.indexOf(against);
|
|
96
|
+
|
|
97
|
+
// src/create/schema/parsers/nativeEnum.ts
|
|
98
|
+
var createNativeEnumSchema = (zodEnum, state) => {
|
|
99
|
+
const enumValues = getValidEnumValues(zodEnum._def.values);
|
|
100
|
+
const { numbers, strings } = sortStringsAndNumbers(enumValues);
|
|
101
|
+
if (strings.length && numbers.length) {
|
|
102
|
+
if (satisfiesVersion(state.components.openapi, "3.1.0"))
|
|
103
|
+
return {
|
|
104
|
+
type: "schema",
|
|
105
|
+
schema: {
|
|
106
|
+
type: ["string", "number"],
|
|
107
|
+
enum: [...strings, ...numbers]
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
type: "schema",
|
|
112
|
+
schema: {
|
|
113
|
+
oneOf: [
|
|
114
|
+
{ type: "string", enum: strings },
|
|
115
|
+
{ type: "number", enum: numbers }
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (strings.length) {
|
|
121
|
+
return {
|
|
122
|
+
type: "schema",
|
|
123
|
+
schema: {
|
|
124
|
+
type: "string",
|
|
125
|
+
enum: strings
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
type: "schema",
|
|
131
|
+
schema: {
|
|
132
|
+
type: "number",
|
|
133
|
+
enum: numbers
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
var getValidEnumValues = (enumValues) => {
|
|
138
|
+
const keys = Object.keys(enumValues).filter(
|
|
139
|
+
(key) => typeof enumValues[enumValues[key]] !== "number"
|
|
140
|
+
);
|
|
141
|
+
return keys.map((key) => enumValues[key]);
|
|
142
|
+
};
|
|
143
|
+
var sortStringsAndNumbers = (values) => ({
|
|
144
|
+
strings: values.filter((value) => typeof value === "string"),
|
|
145
|
+
numbers: values.filter((value) => typeof value === "number")
|
|
146
|
+
});
|
|
147
|
+
|
|
87
148
|
// src/create/schema/parsers/transform.ts
|
|
88
149
|
var createTransformSchema = (zodTransform, state) => {
|
|
89
150
|
if (zodTransform._def.openapi?.effectType === "output") {
|
|
@@ -266,6 +327,33 @@ var createDiscriminatedUnionSchema = (zodDiscriminatedUnion, state) => {
|
|
|
266
327
|
effects: flattenEffects(schemas.map((schema) => schema.effects))
|
|
267
328
|
};
|
|
268
329
|
};
|
|
330
|
+
var unwrapLiterals = (zodType, state) => {
|
|
331
|
+
if (isZodType(zodType, "ZodLiteral")) {
|
|
332
|
+
if (typeof zodType._def.value !== "string") {
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
return [zodType._def.value];
|
|
336
|
+
}
|
|
337
|
+
if (isZodType(zodType, "ZodNativeEnum")) {
|
|
338
|
+
const schema = createNativeEnumSchema(zodType, state);
|
|
339
|
+
if (schema.type === "schema" && schema.schema.type === "string") {
|
|
340
|
+
return schema.schema.enum;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (isZodType(zodType, "ZodEnum")) {
|
|
344
|
+
return zodType._def.values;
|
|
345
|
+
}
|
|
346
|
+
if (isZodType(zodType, "ZodBranded")) {
|
|
347
|
+
return unwrapLiterals(zodType._def.type, state);
|
|
348
|
+
}
|
|
349
|
+
if (isZodType(zodType, "ZodReadonly")) {
|
|
350
|
+
return unwrapLiterals(zodType._def.innerType, state);
|
|
351
|
+
}
|
|
352
|
+
if (isZodType(zodType, "ZodCatch")) {
|
|
353
|
+
return unwrapLiterals(zodType._def.innerType, state);
|
|
354
|
+
}
|
|
355
|
+
return void 0;
|
|
356
|
+
};
|
|
269
357
|
var mapDiscriminator = (schemas, zodObjects, discriminator, state) => {
|
|
270
358
|
if (typeof discriminator !== "string") {
|
|
271
359
|
return void 0;
|
|
@@ -278,21 +366,13 @@ var mapDiscriminator = (schemas, zodObjects, discriminator, state) => {
|
|
|
278
366
|
return void 0;
|
|
279
367
|
}
|
|
280
368
|
const value = zodObject.shape[discriminator];
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
continue;
|
|
369
|
+
const literals = unwrapLiterals(value, state);
|
|
370
|
+
if (!literals) {
|
|
371
|
+
return void 0;
|
|
286
372
|
}
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
throw new Error(
|
|
290
|
-
`Discriminator ${discriminator} could not be found in on index ${index} of a discriminated union at ${state.path.join(
|
|
291
|
-
" > "
|
|
292
|
-
)}`
|
|
293
|
-
);
|
|
373
|
+
for (const enumValue of literals) {
|
|
374
|
+
mapping[enumValue] = componentSchemaRef;
|
|
294
375
|
}
|
|
295
|
-
mapping[literalValue] = componentSchemaRef;
|
|
296
376
|
}
|
|
297
377
|
return {
|
|
298
378
|
propertyName: discriminator,
|
|
@@ -332,16 +412,6 @@ var createLazySchema = (zodLazy, state) => {
|
|
|
332
412
|
return createSchemaObject(innerSchema, state, ["lazy schema"]);
|
|
333
413
|
};
|
|
334
414
|
|
|
335
|
-
// src/openapi.ts
|
|
336
|
-
var openApiVersions = [
|
|
337
|
-
"3.0.0",
|
|
338
|
-
"3.0.1",
|
|
339
|
-
"3.0.2",
|
|
340
|
-
"3.0.3",
|
|
341
|
-
"3.1.0"
|
|
342
|
-
];
|
|
343
|
-
var satisfiesVersion = (test, against) => openApiVersions.indexOf(test) >= openApiVersions.indexOf(against);
|
|
344
|
-
|
|
345
415
|
// src/create/schema/parsers/null.ts
|
|
346
416
|
var createNullSchema = () => ({
|
|
347
417
|
type: "schema",
|
|
@@ -391,57 +461,6 @@ var createManualTypeSchema = (zodSchema, state) => {
|
|
|
391
461
|
};
|
|
392
462
|
};
|
|
393
463
|
|
|
394
|
-
// src/create/schema/parsers/nativeEnum.ts
|
|
395
|
-
var createNativeEnumSchema = (zodEnum, state) => {
|
|
396
|
-
const enumValues = getValidEnumValues(zodEnum._def.values);
|
|
397
|
-
const { numbers, strings } = sortStringsAndNumbers(enumValues);
|
|
398
|
-
if (strings.length && numbers.length) {
|
|
399
|
-
if (satisfiesVersion(state.components.openapi, "3.1.0"))
|
|
400
|
-
return {
|
|
401
|
-
type: "schema",
|
|
402
|
-
schema: {
|
|
403
|
-
type: ["string", "number"],
|
|
404
|
-
enum: [...strings, ...numbers]
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
return {
|
|
408
|
-
type: "schema",
|
|
409
|
-
schema: {
|
|
410
|
-
oneOf: [
|
|
411
|
-
{ type: "string", enum: strings },
|
|
412
|
-
{ type: "number", enum: numbers }
|
|
413
|
-
]
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
if (strings.length) {
|
|
418
|
-
return {
|
|
419
|
-
type: "schema",
|
|
420
|
-
schema: {
|
|
421
|
-
type: "string",
|
|
422
|
-
enum: strings
|
|
423
|
-
}
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
return {
|
|
427
|
-
type: "schema",
|
|
428
|
-
schema: {
|
|
429
|
-
type: "number",
|
|
430
|
-
enum: numbers
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
};
|
|
434
|
-
var getValidEnumValues = (enumValues) => {
|
|
435
|
-
const keys = Object.keys(enumValues).filter(
|
|
436
|
-
(key) => typeof enumValues[enumValues[key]] !== "number"
|
|
437
|
-
);
|
|
438
|
-
return keys.map((key) => enumValues[key]);
|
|
439
|
-
};
|
|
440
|
-
var sortStringsAndNumbers = (values) => ({
|
|
441
|
-
strings: values.filter((value) => typeof value === "string"),
|
|
442
|
-
numbers: values.filter((value) => typeof value === "number")
|
|
443
|
-
});
|
|
444
|
-
|
|
445
464
|
// src/create/schema/parsers/nullable.ts
|
|
446
465
|
var createNullableSchema = (zodNullable, state) => {
|
|
447
466
|
const schemaObject = createSchemaObject(zodNullable.unwrap(), state, [
|
|
@@ -947,12 +966,13 @@ var createSetSchema = (zodSet, state) => {
|
|
|
947
966
|
};
|
|
948
967
|
|
|
949
968
|
// src/create/schema/parsers/string.ts
|
|
950
|
-
var createStringSchema = (zodString) => {
|
|
969
|
+
var createStringSchema = (zodString, state) => {
|
|
951
970
|
const zodStringChecks = getZodStringChecks(zodString);
|
|
952
971
|
const format = mapStringFormat(zodStringChecks);
|
|
953
972
|
const patterns = mapPatterns(zodStringChecks);
|
|
954
973
|
const minLength = zodStringChecks.length?.[0]?.value ?? zodStringChecks.min?.[0]?.value;
|
|
955
974
|
const maxLength = zodStringChecks.length?.[0]?.value ?? zodStringChecks.max?.[0]?.value;
|
|
975
|
+
const contentEncoding = satisfiesVersion(state.components.openapi, "3.1.0") ? mapContentEncoding(zodStringChecks) : void 0;
|
|
956
976
|
if (patterns.length <= 1) {
|
|
957
977
|
return {
|
|
958
978
|
type: "schema",
|
|
@@ -961,7 +981,8 @@ var createStringSchema = (zodString) => {
|
|
|
961
981
|
...format && { format },
|
|
962
982
|
...patterns[0] && { pattern: patterns[0] },
|
|
963
983
|
...minLength !== void 0 && { minLength },
|
|
964
|
-
...maxLength !== void 0 && { maxLength }
|
|
984
|
+
...maxLength !== void 0 && { maxLength },
|
|
985
|
+
...contentEncoding && { contentEncoding }
|
|
965
986
|
}
|
|
966
987
|
};
|
|
967
988
|
}
|
|
@@ -974,7 +995,8 @@ var createStringSchema = (zodString) => {
|
|
|
974
995
|
...format && { format },
|
|
975
996
|
...patterns[0] && { pattern: patterns[0] },
|
|
976
997
|
...minLength !== void 0 && { minLength },
|
|
977
|
-
...maxLength !== void 0 && { maxLength }
|
|
998
|
+
...maxLength !== void 0 && { maxLength },
|
|
999
|
+
...contentEncoding && { contentEncoding }
|
|
978
1000
|
},
|
|
979
1001
|
...patterns.slice(1).map(
|
|
980
1002
|
(pattern) => ({
|
|
@@ -1040,6 +1062,15 @@ var mapStringFormat = (zodStringChecks) => {
|
|
|
1040
1062
|
if (zodStringChecks.datetime) {
|
|
1041
1063
|
return "date-time";
|
|
1042
1064
|
}
|
|
1065
|
+
if (zodStringChecks.date) {
|
|
1066
|
+
return "date";
|
|
1067
|
+
}
|
|
1068
|
+
if (zodStringChecks.time) {
|
|
1069
|
+
return "time";
|
|
1070
|
+
}
|
|
1071
|
+
if (zodStringChecks.duration) {
|
|
1072
|
+
return "duration";
|
|
1073
|
+
}
|
|
1043
1074
|
if (zodStringChecks.email) {
|
|
1044
1075
|
return "email";
|
|
1045
1076
|
}
|
|
@@ -1048,6 +1079,12 @@ var mapStringFormat = (zodStringChecks) => {
|
|
|
1048
1079
|
}
|
|
1049
1080
|
return void 0;
|
|
1050
1081
|
};
|
|
1082
|
+
var mapContentEncoding = (zodStringChecks) => {
|
|
1083
|
+
if (zodStringChecks.base64) {
|
|
1084
|
+
return "base64";
|
|
1085
|
+
}
|
|
1086
|
+
return void 0;
|
|
1087
|
+
};
|
|
1051
1088
|
|
|
1052
1089
|
// src/create/schema/parsers/tuple.ts
|
|
1053
1090
|
var createTupleSchema = (zodTuple, state) => {
|
|
@@ -1167,7 +1204,7 @@ var createSchemaSwitch = (zodSchema, state) => {
|
|
|
1167
1204
|
return createManualTypeSchema(zodSchema, state);
|
|
1168
1205
|
}
|
|
1169
1206
|
if (isZodType(zodSchema, "ZodString")) {
|
|
1170
|
-
return createStringSchema(zodSchema);
|
|
1207
|
+
return createStringSchema(zodSchema, state);
|
|
1171
1208
|
}
|
|
1172
1209
|
if (isZodType(zodSchema, "ZodNumber")) {
|
|
1173
1210
|
return createNumberSchema(zodSchema, state);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ZodString } from 'zod';
|
|
2
|
-
import type { Schema } from '..';
|
|
3
|
-
export declare const createStringSchema: (zodString: ZodString) => Schema;
|
|
2
|
+
import type { Schema, SchemaState } from '..';
|
|
3
|
+
export declare const createStringSchema: (zodString: ZodString, state: SchemaState) => Schema;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type * from './extendZod';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-openapi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.0-beta.1",
|
|
4
4
|
"description": "Convert Zod Schemas to OpenAPI v3.x documentation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -20,12 +20,21 @@
|
|
|
20
20
|
"url": "git+ssh://git@github.com/samchungy/zod-openapi.git"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
|
-
"sideEffects":
|
|
23
|
+
"sideEffects": [
|
|
24
|
+
"./src/extend.ts",
|
|
25
|
+
"./lib-esm/extend.mjs",
|
|
26
|
+
"./lib-commonjs/extend.js"
|
|
27
|
+
],
|
|
24
28
|
"exports": {
|
|
25
29
|
".": {
|
|
26
30
|
"import": "./lib-esm/index.mjs",
|
|
27
31
|
"require": "./lib-commonjs/index.js",
|
|
28
32
|
"types": "./lib-types/index.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./extend": {
|
|
35
|
+
"import": "./lib-esm/extend.mjs",
|
|
36
|
+
"require": "./lib-commonjs/extend.js",
|
|
37
|
+
"types": "./lib-types/extend.d.ts"
|
|
29
38
|
}
|
|
30
39
|
},
|
|
31
40
|
"main": "./lib-commonjs/index.js",
|
|
@@ -52,9 +61,9 @@
|
|
|
52
61
|
"@types/node": "^20.3.0",
|
|
53
62
|
"eslint-plugin-zod-openapi": "^0.1.0",
|
|
54
63
|
"openapi3-ts": "4.3.1",
|
|
55
|
-
"skuba": "8.0.
|
|
64
|
+
"skuba": "8.0.1",
|
|
56
65
|
"yaml": "2.4.1",
|
|
57
|
-
"zod": "3.
|
|
66
|
+
"zod": "3.23.3"
|
|
58
67
|
},
|
|
59
68
|
"peerDependencies": {
|
|
60
69
|
"zod": "^3.21.4"
|