@kubb/plugin-zod 5.7.2 → 5.7.4
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/dist/index.cjs +434 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -12
- package/dist/index.d.ts +4 -12
- package/dist/index.js +429 -238
- package/dist/index.js.map +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { Diagnostics, Resolver, addGenerator, ast, containsCircularRef, createResolver, defineExtension, defineGenerator, extractRefName, setMacros, setResolver, syncSchemaRef } from "kubb/kit";
|
|
1
|
+
import { Diagnostics, Resolver, addGenerator, ast, camelCase, containsCircularRef, createResolver, defineExtension, defineGenerator, extractRefName, pascalCase, setMacros, setResolver, syncSchemaRef } from "kubb/kit";
|
|
2
2
|
import { Const, File, Function, Type, jsxRenderer } from "kubb/jsx";
|
|
3
3
|
import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
|
|
4
|
-
|
|
5
|
-
//#region ../../internals/shared/src/operation.ts
|
|
4
|
+
// ../../internals/shared/src/operation.ts
|
|
6
5
|
/**
|
|
7
6
|
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
8
7
|
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
@@ -85,7 +84,10 @@ function getOperationParameters(node) {
|
|
|
85
84
|
* shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
|
|
86
85
|
* printer to emit `z.object(…)` rather than a record.
|
|
87
86
|
*/
|
|
88
|
-
function
|
|
87
|
+
function hasRequestContentTypeChoice(node) {
|
|
88
|
+
return (node.requestBody?.content ?? []).filter((entry) => entry.schema).length > 1;
|
|
89
|
+
}
|
|
90
|
+
function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
|
|
89
91
|
const { path, query, header } = getOperationParameters(node);
|
|
90
92
|
const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
91
93
|
const createNever = () => ast.factory.createSchema({
|
|
@@ -110,29 +112,57 @@ function buildOptionsSchema(node, resolver) {
|
|
|
110
112
|
resolve: resolver.param.headers
|
|
111
113
|
}
|
|
112
114
|
];
|
|
113
|
-
|
|
115
|
+
const createOptions = (body, extra = []) => ast.factory.createSchema({
|
|
114
116
|
type: "object",
|
|
115
117
|
primitive: "object",
|
|
116
118
|
deprecated: node.deprecated,
|
|
117
|
-
properties: [
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
119
|
+
properties: [
|
|
120
|
+
ast.factory.createProperty({
|
|
121
|
+
name: "body",
|
|
122
|
+
required: hasBody,
|
|
123
|
+
schema: body
|
|
124
|
+
}),
|
|
125
|
+
...groups.map(({ name, params, resolve }) => {
|
|
126
|
+
const required = params.some((param) => param.required);
|
|
127
|
+
return ast.factory.createProperty({
|
|
128
|
+
name,
|
|
129
|
+
required,
|
|
130
|
+
schema: params.length > 0 ? ast.factory.createSchema({
|
|
131
|
+
type: "ref",
|
|
132
|
+
name: resolve.call(resolver.param, node, params[0]),
|
|
133
|
+
optional: !required
|
|
134
|
+
}) : createNever()
|
|
135
|
+
});
|
|
136
|
+
}),
|
|
137
|
+
...extra
|
|
138
|
+
]
|
|
139
|
+
});
|
|
140
|
+
const variants = pairContentTypes && hasRequestContentTypeChoice(node) ? resolveContentTypeVariants(node.requestBody?.content ?? [], resolver.response.body(node)) : [];
|
|
141
|
+
if (variants.length < 2) return createOptions(hasBody ? ast.factory.createSchema({
|
|
142
|
+
type: "ref",
|
|
143
|
+
name: resolver.response.body(node)
|
|
144
|
+
}) : createNever());
|
|
145
|
+
return ast.factory.createSchema({
|
|
146
|
+
type: "union",
|
|
147
|
+
members: variants.map((variant, index) => createOptions(ast.factory.createSchema({
|
|
148
|
+
type: "ref",
|
|
149
|
+
name: variant.name
|
|
150
|
+
}), [ast.factory.createProperty({
|
|
151
|
+
name: "contentType",
|
|
152
|
+
required: index > 0,
|
|
153
|
+
schema: ast.factory.createSchema({
|
|
154
|
+
type: "object",
|
|
155
|
+
primitive: "object",
|
|
156
|
+
properties: [ast.factory.createProperty({
|
|
157
|
+
name: "request",
|
|
158
|
+
required: true,
|
|
159
|
+
schema: ast.factory.createSchema({
|
|
160
|
+
type: "enum",
|
|
161
|
+
enumValues: [variant.contentType]
|
|
162
|
+
})
|
|
163
|
+
})]
|
|
164
|
+
})
|
|
165
|
+
})]))
|
|
136
166
|
});
|
|
137
167
|
}
|
|
138
168
|
/**
|
|
@@ -202,8 +232,7 @@ function isSuccessStatusCode(statusCode) {
|
|
|
202
232
|
function getSuccessResponses(responses) {
|
|
203
233
|
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
204
234
|
}
|
|
205
|
-
|
|
206
|
-
//#region ../../internals/shared/src/adapter.ts
|
|
235
|
+
// ../../internals/shared/src/adapter.ts
|
|
207
236
|
/**
|
|
208
237
|
* Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
|
|
209
238
|
* so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
|
|
@@ -226,58 +255,7 @@ function getOasAdapter(adapter) {
|
|
|
226
255
|
});
|
|
227
256
|
return adapter;
|
|
228
257
|
}
|
|
229
|
-
|
|
230
|
-
//#region ../../internals/utils/src/casing.ts
|
|
231
|
-
/**
|
|
232
|
-
* Shared implementation for camelCase and PascalCase conversion.
|
|
233
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
234
|
-
* and capitalizes each word according to `pascal`.
|
|
235
|
-
*
|
|
236
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
237
|
-
*/
|
|
238
|
-
function convert(text, pascal) {
|
|
239
|
-
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
240
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
241
|
-
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
242
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
243
|
-
}
|
|
244
|
-
const cache = /* @__PURE__ */ new Map();
|
|
245
|
-
const cacheLimit = 1e4;
|
|
246
|
-
function toCamelOrPascal(text, pascal) {
|
|
247
|
-
const key = `${pascal ? "P" : "c"}${text}`;
|
|
248
|
-
const cached = cache.get(key);
|
|
249
|
-
if (cached !== void 0) return cached;
|
|
250
|
-
const result = convert(text, pascal);
|
|
251
|
-
if (cache.size >= cacheLimit) cache.clear();
|
|
252
|
-
cache.set(key, result);
|
|
253
|
-
return result;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Converts `text` to camelCase.
|
|
257
|
-
*
|
|
258
|
-
* @example Word boundaries
|
|
259
|
-
* `camelCase('hello-world') // 'helloWorld'`
|
|
260
|
-
*
|
|
261
|
-
* @example With a prefix
|
|
262
|
-
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
263
|
-
*/
|
|
264
|
-
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
265
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Converts `text` to PascalCase.
|
|
269
|
-
*
|
|
270
|
-
* @example Word boundaries
|
|
271
|
-
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
272
|
-
*
|
|
273
|
-
* @example With a suffix
|
|
274
|
-
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
275
|
-
*/
|
|
276
|
-
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
277
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
278
|
-
}
|
|
279
|
-
//#endregion
|
|
280
|
-
//#region ../../internals/utils/src/reserved.ts
|
|
258
|
+
// ../../internals/utils/src/reserved.ts
|
|
281
259
|
/**
|
|
282
260
|
* JavaScript and Java reserved words.
|
|
283
261
|
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
@@ -285,6 +263,7 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
285
263
|
const reservedWords = /* @__PURE__ */ new Set([
|
|
286
264
|
"abstract",
|
|
287
265
|
"arguments",
|
|
266
|
+
"await",
|
|
288
267
|
"boolean",
|
|
289
268
|
"break",
|
|
290
269
|
"byte",
|
|
@@ -415,8 +394,7 @@ function ensureValidVarName(name) {
|
|
|
415
394
|
function isIdentifier(name) {
|
|
416
395
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
417
396
|
}
|
|
418
|
-
|
|
419
|
-
//#region ../../internals/utils/src/strings.ts
|
|
397
|
+
// ../../internals/utils/src/strings.ts
|
|
420
398
|
/**
|
|
421
399
|
* Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
|
|
422
400
|
* any backslash or single quote in the content.
|
|
@@ -465,6 +443,34 @@ function stringify(value) {
|
|
|
465
443
|
if (value === void 0 || value === null) return "''";
|
|
466
444
|
return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
|
|
467
445
|
}
|
|
446
|
+
const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
|
|
447
|
+
const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
|
|
448
|
+
/**
|
|
449
|
+
* Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
|
|
450
|
+
* not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
|
|
451
|
+
*/
|
|
452
|
+
function dropIdentityEscapes(pattern) {
|
|
453
|
+
let inClass = false;
|
|
454
|
+
return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
|
|
455
|
+
if (escaped === void 0) {
|
|
456
|
+
inClass = match === "[";
|
|
457
|
+
return match;
|
|
458
|
+
}
|
|
459
|
+
return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Builds the `RegExp` for a pattern, adding the `u` flag when it uses `\p{...}`, `\P{...}` or `\u{...}`, or
|
|
464
|
+
* holds a character outside the BMP. A pattern that is not valid under `u` keeps the flags it came with.
|
|
465
|
+
*/
|
|
466
|
+
function toRegExp(pattern, flags) {
|
|
467
|
+
if (flags?.includes("u") || !NEEDS_UNICODE.test(pattern)) return new RegExp(pattern, flags);
|
|
468
|
+
try {
|
|
469
|
+
return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
|
|
470
|
+
} catch {
|
|
471
|
+
return new RegExp(pattern, flags);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
468
474
|
/**
|
|
469
475
|
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
|
|
470
476
|
* Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
|
|
@@ -481,13 +487,11 @@ function toRegExpString(text, func = "RegExp") {
|
|
|
481
487
|
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
|
|
482
488
|
const replacementTarget = match?.[1] ?? "";
|
|
483
489
|
const matchedFlags = match?.[2];
|
|
484
|
-
const
|
|
485
|
-
const { source, flags } = new RegExp(cleaned, matchedFlags);
|
|
490
|
+
const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
|
|
486
491
|
if (func === null) return `/${source}/${flags}`;
|
|
487
492
|
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
|
|
488
493
|
}
|
|
489
|
-
|
|
490
|
-
//#region ../../internals/utils/src/codegen.ts
|
|
494
|
+
// ../../internals/utils/src/codegen.ts
|
|
491
495
|
const INDENT = " ";
|
|
492
496
|
/**
|
|
493
497
|
* Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
|
|
@@ -556,8 +560,7 @@ function buildList(items, brackets = ["[", "]"]) {
|
|
|
556
560
|
function lazyGetter({ name, body }) {
|
|
557
561
|
return `get ${objectKey(name)}() { return ${body} }`;
|
|
558
562
|
}
|
|
559
|
-
|
|
560
|
-
//#region ../../internals/utils/src/fs.ts
|
|
563
|
+
// ../../internals/utils/src/fs.ts
|
|
561
564
|
/**
|
|
562
565
|
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
563
566
|
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
@@ -581,17 +584,24 @@ function toFilePath(name, caseLast = camelCase) {
|
|
|
581
584
|
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
582
585
|
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
583
586
|
}
|
|
584
|
-
|
|
585
|
-
//#region ../../internals/shared/src/resolver.ts
|
|
587
|
+
// ../../internals/shared/src/resolver.ts
|
|
586
588
|
/**
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
+
* Builds the allocation key for a declaration an operation owns. `scope` names which declaration
|
|
590
|
+
* it is, so two of them never share an allocation even when a custom resolver gives both the same
|
|
591
|
+
* name. Leave `scope` empty for the operation's own declaration.
|
|
589
592
|
*
|
|
590
593
|
* @example
|
|
591
|
-
*
|
|
594
|
+
* ```ts
|
|
595
|
+
* resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
|
|
596
|
+
* ```
|
|
592
597
|
*/
|
|
593
|
-
function
|
|
594
|
-
return
|
|
598
|
+
function operationKey(node, ...scope) {
|
|
599
|
+
return [
|
|
600
|
+
"operation",
|
|
601
|
+
node.method,
|
|
602
|
+
node.path,
|
|
603
|
+
...scope.map(String)
|
|
604
|
+
];
|
|
595
605
|
}
|
|
596
606
|
/**
|
|
597
607
|
* Builds the shared `param` namespace. Spread the result into `createResolver`
|
|
@@ -604,15 +614,33 @@ function operationParamName(node, param) {
|
|
|
604
614
|
*/
|
|
605
615
|
function createOperationParamResolver() {
|
|
606
616
|
return {
|
|
607
|
-
name
|
|
617
|
+
name(node, param) {
|
|
618
|
+
const name = this.name(`${node.operationId} ${param.in} ${param.name}`);
|
|
619
|
+
return this.resolveName({
|
|
620
|
+
key: operationKey(node, "param", "name", param.in, param.name),
|
|
621
|
+
name
|
|
622
|
+
});
|
|
623
|
+
},
|
|
608
624
|
path(node) {
|
|
609
|
-
|
|
625
|
+
const name = this.name(`${node.operationId} Path`);
|
|
626
|
+
return this.resolveName({
|
|
627
|
+
key: operationKey(node, "param", "path"),
|
|
628
|
+
name
|
|
629
|
+
});
|
|
610
630
|
},
|
|
611
631
|
query(node) {
|
|
612
|
-
|
|
632
|
+
const name = this.name(`${node.operationId} Query`);
|
|
633
|
+
return this.resolveName({
|
|
634
|
+
key: operationKey(node, "param", "query"),
|
|
635
|
+
name
|
|
636
|
+
});
|
|
613
637
|
},
|
|
614
638
|
headers(node) {
|
|
615
|
-
|
|
639
|
+
const name = this.name(`${node.operationId} Headers`);
|
|
640
|
+
return this.resolveName({
|
|
641
|
+
key: operationKey(node, "param", "headers"),
|
|
642
|
+
name
|
|
643
|
+
});
|
|
616
644
|
}
|
|
617
645
|
};
|
|
618
646
|
}
|
|
@@ -629,16 +657,32 @@ function createOperationParamResolver() {
|
|
|
629
657
|
function createOperationResponseResolver() {
|
|
630
658
|
return {
|
|
631
659
|
status(node, statusCode) {
|
|
632
|
-
|
|
660
|
+
const name = this.name(`${node.operationId} Status ${statusCode}`);
|
|
661
|
+
return this.resolveName({
|
|
662
|
+
key: operationKey(node, "response", "status", statusCode),
|
|
663
|
+
name
|
|
664
|
+
});
|
|
633
665
|
},
|
|
634
666
|
body(node) {
|
|
635
|
-
|
|
667
|
+
const name = this.name(`${node.operationId} Body`);
|
|
668
|
+
return this.resolveName({
|
|
669
|
+
key: operationKey(node, "response", "body"),
|
|
670
|
+
name
|
|
671
|
+
});
|
|
636
672
|
},
|
|
637
673
|
responses(node) {
|
|
638
|
-
|
|
674
|
+
const name = this.name(`${node.operationId} Responses`);
|
|
675
|
+
return this.resolveName({
|
|
676
|
+
key: operationKey(node, "response", "responses"),
|
|
677
|
+
name
|
|
678
|
+
});
|
|
639
679
|
},
|
|
640
680
|
response(node) {
|
|
641
|
-
|
|
681
|
+
const name = this.name(`${node.operationId} Response`);
|
|
682
|
+
return this.resolveName({
|
|
683
|
+
key: operationKey(node, "response", "response"),
|
|
684
|
+
name
|
|
685
|
+
});
|
|
642
686
|
}
|
|
643
687
|
};
|
|
644
688
|
}
|
|
@@ -653,11 +697,10 @@ function createOperationResponseResolver() {
|
|
|
653
697
|
*/
|
|
654
698
|
function createCasedFile(caseLast) {
|
|
655
699
|
return { baseName({ name, extname }) {
|
|
656
|
-
return `${toFilePath(name, caseLast)}${extname}
|
|
700
|
+
return this.memo(`file:${name}\0${extname}`, () => `${toFilePath(name, caseLast)}${extname}`);
|
|
657
701
|
} };
|
|
658
702
|
}
|
|
659
|
-
|
|
660
|
-
//#region ../../internals/shared/src/group.ts
|
|
703
|
+
// ../../internals/shared/src/group.ts
|
|
661
704
|
/**
|
|
662
705
|
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
663
706
|
* shared default naming so every plugin groups output consistently:
|
|
@@ -687,8 +730,7 @@ function createGroupConfig(group) {
|
|
|
687
730
|
name: group.name ? group.name : defaultName
|
|
688
731
|
};
|
|
689
732
|
}
|
|
690
|
-
|
|
691
|
-
//#region ../../internals/shared/src/schemaTraversal.ts
|
|
733
|
+
// ../../internals/shared/src/schemaTraversal.ts
|
|
692
734
|
/**
|
|
693
735
|
* Maps each property of an object schema to its transformed output. Pairs every result with the
|
|
694
736
|
* original property so the printer keeps full control over modifiers, getters, and key syntax.
|
|
@@ -726,8 +768,7 @@ function mapSchemaItems(node, transform) {
|
|
|
726
768
|
output: transform(schema)
|
|
727
769
|
}));
|
|
728
770
|
}
|
|
729
|
-
|
|
730
|
-
//#region src/components/Zod.tsx
|
|
771
|
+
// src/components/Zod.tsx
|
|
731
772
|
function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
|
|
732
773
|
const output = printer.print(node);
|
|
733
774
|
const printerImports = printer.drainImports();
|
|
@@ -799,8 +840,7 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
|
|
|
799
840
|
})
|
|
800
841
|
] });
|
|
801
842
|
}
|
|
802
|
-
|
|
803
|
-
//#region src/utils.ts
|
|
843
|
+
// src/utils.ts
|
|
804
844
|
/**
|
|
805
845
|
* Returns `true` when the given coercion option enables coercion for the specified type.
|
|
806
846
|
*/
|
|
@@ -829,11 +869,12 @@ function isClosedKeySchema(node) {
|
|
|
829
869
|
}
|
|
830
870
|
/**
|
|
831
871
|
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
832
|
-
* argument.
|
|
872
|
+
* argument. Property counts, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
|
|
833
873
|
* on `.and(…)`.
|
|
834
874
|
*/
|
|
835
875
|
function isPlainInlineObject(node) {
|
|
836
876
|
if (node.type !== "object") return false;
|
|
877
|
+
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
837
878
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
838
879
|
const hasAdditionalProperties = node.additionalProperties !== void 0;
|
|
839
880
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
@@ -844,11 +885,12 @@ function isPlainInlineObject(node) {
|
|
|
844
885
|
* `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
|
|
845
886
|
* unresolved) objects, single-member unions of an object, and object-composable `allOf`.
|
|
846
887
|
*
|
|
847
|
-
*
|
|
888
|
+
* Property counts render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
|
|
848
889
|
*/
|
|
849
890
|
function isObjectSchemaNode(node, cyclicSchemas) {
|
|
850
891
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
851
892
|
if (node.type === "object") {
|
|
893
|
+
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
852
894
|
const hasProperties = Boolean(node.properties?.length);
|
|
853
895
|
const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
|
|
854
896
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
@@ -876,6 +918,49 @@ function isObjectComposableIntersection(node, cyclicSchemas) {
|
|
|
876
918
|
const [first, ...rest] = node.members ?? [];
|
|
877
919
|
return !!first && isObjectSchemaNode(first, cyclicSchemas) && rest.every(isPlainInlineObject);
|
|
878
920
|
}
|
|
921
|
+
function preserveRequiredProperties(members) {
|
|
922
|
+
const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
|
|
923
|
+
if (seen.has(node)) return false;
|
|
924
|
+
seen.add(node);
|
|
925
|
+
const resolved = syncSchemaRef(node);
|
|
926
|
+
const composition = ast.narrowSchema(resolved, ast.schemaTypes.intersection) ?? ast.narrowSchema(resolved, ast.schemaTypes.union);
|
|
927
|
+
if (composition) {
|
|
928
|
+
if (composition.type === ast.schemaTypes.union && composition.members?.length !== 1) return false;
|
|
929
|
+
return composition.members?.some((child) => hasRequiredProperty({
|
|
930
|
+
node: child,
|
|
931
|
+
propertyName,
|
|
932
|
+
seen
|
|
933
|
+
})) ?? false;
|
|
934
|
+
}
|
|
935
|
+
return ast.resolveSchemaProperties({
|
|
936
|
+
node: resolved,
|
|
937
|
+
propertyName
|
|
938
|
+
}).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
|
|
939
|
+
};
|
|
940
|
+
return members.map((member, index) => {
|
|
941
|
+
const object = ast.narrowSchema(member, ast.schemaTypes.object);
|
|
942
|
+
if (!object || index === 0) return member;
|
|
943
|
+
return {
|
|
944
|
+
...object,
|
|
945
|
+
properties: object.properties.map((property) => {
|
|
946
|
+
if (!members.slice(0, index).some((base) => hasRequiredProperty({
|
|
947
|
+
node: base,
|
|
948
|
+
propertyName: property.name
|
|
949
|
+
}))) return property;
|
|
950
|
+
return {
|
|
951
|
+
...property,
|
|
952
|
+
required: true,
|
|
953
|
+
schema: {
|
|
954
|
+
...property.schema,
|
|
955
|
+
optional: false,
|
|
956
|
+
nullish: false,
|
|
957
|
+
nullable: property.schema.nullable || property.schema.nullish
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
})
|
|
961
|
+
};
|
|
962
|
+
});
|
|
963
|
+
}
|
|
879
964
|
/**
|
|
880
965
|
* Format a default value as a code-level literal.
|
|
881
966
|
* Arrays and objects keep their contents as a JSON literal, primitives become their string
|
|
@@ -922,11 +1007,20 @@ function defaultLiteral(node, value) {
|
|
|
922
1007
|
*/
|
|
923
1008
|
function buildEnum(values) {
|
|
924
1009
|
if (values.every((v) => typeof v === "string")) return `z.enum([${values.map((v) => formatDefault(v)).join(", ")}])`;
|
|
925
|
-
const literals = values.map((
|
|
1010
|
+
const literals = values.map((value) => buildLiteral(value));
|
|
926
1011
|
if (literals.length === 1) return literals[0];
|
|
927
1012
|
return `z.union([${literals.join(", ")}])`;
|
|
928
1013
|
}
|
|
929
1014
|
/**
|
|
1015
|
+
* Preserves the structure of JSON const values in both Zod APIs.
|
|
1016
|
+
*/
|
|
1017
|
+
function buildLiteral(value) {
|
|
1018
|
+
if (value === null) return "z.null()";
|
|
1019
|
+
if (Array.isArray(value)) return `z.tuple([${value.map((item) => buildLiteral(item)).join(", ")}])`;
|
|
1020
|
+
if (typeof value === "object") return `z.strictObject({${Object.entries(value).map(([name, item]) => `${JSON.stringify(name)}: ${buildLiteral(item)}`).join(", ")}})`;
|
|
1021
|
+
return `z.literal(${formatDefault(value)})`;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
930
1024
|
* Digit pattern for a `type: 'string'` schema that carries an integer `format`, the way ProtoJSON
|
|
931
1025
|
* encodes 64-bit integers. Returns `undefined` for every other format, and a `pattern` from the
|
|
932
1026
|
* spec takes precedence over this fallback.
|
|
@@ -1009,6 +1103,17 @@ function lengthChecksMini({ min, max, pattern, regexType }) {
|
|
|
1009
1103
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
1010
1104
|
}
|
|
1011
1105
|
/**
|
|
1106
|
+
* Checks property counts before object parsing strips unknown keys or inserts defaults.
|
|
1107
|
+
*/
|
|
1108
|
+
function objectConstraints({ value, node }) {
|
|
1109
|
+
if (node.type !== "object") return value;
|
|
1110
|
+
const checks = [];
|
|
1111
|
+
const skip = "typeof value !== \"object\" || value === null || Array.isArray(value)";
|
|
1112
|
+
if (node.minProperties !== void 0) checks.push(`z.refine(value => ${skip} || Object.keys(value).length >= ${node.minProperties}, { message: "Object must have at least ${node.minProperties} properties" })`);
|
|
1113
|
+
if (node.maxProperties !== void 0) checks.push(`z.refine(value => ${skip} || Object.keys(value).length <= ${node.maxProperties}, { message: "Object must have at most ${node.maxProperties} properties" })`);
|
|
1114
|
+
return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1012
1117
|
* Apply nullable / optional / nullish modifiers, an optional `.describe()` call, and an
|
|
1013
1118
|
* optional `.meta({ examples })` call to a schema value string using the chainable Zod v4 API.
|
|
1014
1119
|
*/
|
|
@@ -1098,7 +1203,7 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
|
|
|
1098
1203
|
/**
|
|
1099
1204
|
* Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
|
|
1100
1205
|
*/
|
|
1101
|
-
function buildObjectShape(ctx, node,
|
|
1206
|
+
function buildObjectShape(ctx, node, renderProperty) {
|
|
1102
1207
|
const objectNode = ast.narrowSchema(node, "object");
|
|
1103
1208
|
if (!objectNode) return "{}";
|
|
1104
1209
|
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
@@ -1109,18 +1214,16 @@ function buildObjectShape(ctx, node, modify) {
|
|
|
1109
1214
|
const baseOutput = ctx.transform(schema) ?? ctx.transform(ast.factory.createSchema({ type: "unknown" }));
|
|
1110
1215
|
if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
|
|
1111
1216
|
return baseOutput;
|
|
1112
|
-
}).map(({ name
|
|
1217
|
+
}).map(({ name, property, output: value }) => {
|
|
1113
1218
|
const { schema } = property;
|
|
1114
|
-
|
|
1115
|
-
|
|
1219
|
+
return renderProperty({
|
|
1220
|
+
name,
|
|
1221
|
+
value,
|
|
1116
1222
|
schema,
|
|
1117
1223
|
required: property.required,
|
|
1118
|
-
meta: syncSchemaRef(schema)
|
|
1224
|
+
meta: syncSchemaRef(schema),
|
|
1225
|
+
cyclic: isCyclic(schema)
|
|
1119
1226
|
});
|
|
1120
|
-
return isCyclic(schema) ? lazyGetter({
|
|
1121
|
-
name: propName,
|
|
1122
|
-
body: value
|
|
1123
|
-
}) : `${objectKey(propName)}: ${value}`;
|
|
1124
1227
|
}));
|
|
1125
1228
|
}
|
|
1126
1229
|
/**
|
|
@@ -1135,22 +1238,28 @@ function printWithOmit(ctx, node, modify, mini = false) {
|
|
|
1135
1238
|
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
1136
1239
|
const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
|
|
1137
1240
|
const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
|
|
1138
|
-
const omit = (target) =>
|
|
1241
|
+
const omit = (target) => {
|
|
1242
|
+
const inner = `${target}${unwrap}`;
|
|
1243
|
+
const hasObjectConstraints = meta.type === "object" && (meta.minProperties !== void 0 || meta.maxProperties !== void 0);
|
|
1244
|
+
const pipe = mini ? `${inner}._zod.def` : inner;
|
|
1245
|
+
const object = hasObjectConstraints ? `${pipe}.out` : inner;
|
|
1246
|
+
const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
|
|
1247
|
+
return hasObjectConstraints ? `z.pipe(${pipe}.in, ${omitted})` : omitted;
|
|
1248
|
+
};
|
|
1139
1249
|
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
|
|
1140
1250
|
if (lazyMatch) return `z.lazy(() => ${omit(lazyMatch[1])})`;
|
|
1141
1251
|
return omit(transformed);
|
|
1142
1252
|
})(), meta);
|
|
1143
1253
|
}
|
|
1144
|
-
|
|
1145
|
-
//#region src/printers/printerZod.ts
|
|
1254
|
+
// src/printers/printerZod.ts
|
|
1146
1255
|
function strictOneOfMember(member, node, cyclicSchemas) {
|
|
1147
|
-
if (node.type ===
|
|
1256
|
+
if (node.type === ast.schemaTypes.ref) {
|
|
1148
1257
|
if (member.startsWith("z.lazy(")) return member;
|
|
1149
1258
|
const refName = ast.resolveRefName(node);
|
|
1150
1259
|
if (refName && cyclicSchemas?.has(refName)) return member;
|
|
1151
1260
|
const schema = syncSchemaRef(node);
|
|
1152
1261
|
if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
|
|
1153
|
-
if (schema.type ===
|
|
1262
|
+
if (schema.type === ast.schemaTypes.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
|
|
1154
1263
|
}
|
|
1155
1264
|
return member;
|
|
1156
1265
|
}
|
|
@@ -1158,28 +1267,70 @@ function strictOneOfMember(member, node, cyclicSchemas) {
|
|
|
1158
1267
|
* Builds the `{ key: value, … }` shape for an object node with the chainable modifiers.
|
|
1159
1268
|
*/
|
|
1160
1269
|
function buildZodObjectShape(ctx, node) {
|
|
1161
|
-
return buildObjectShape(ctx, node, ({ value, schema, required, meta }) =>
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1270
|
+
return buildObjectShape(ctx, node, ({ name, value, schema, required, meta, cyclic }) => {
|
|
1271
|
+
const body = applyModifiers({
|
|
1272
|
+
value,
|
|
1273
|
+
schema,
|
|
1274
|
+
nullable: meta.nullable,
|
|
1275
|
+
optional: schema.optional || required === false,
|
|
1276
|
+
nullish: schema.nullish,
|
|
1277
|
+
defaultValue: meta.default,
|
|
1278
|
+
description: schema.type !== ast.schemaTypes.ref && meta.type === ast.schemaTypes.ref ? void 0 : meta.description,
|
|
1279
|
+
examples: meta.examples
|
|
1280
|
+
});
|
|
1281
|
+
if (!cyclic) return `${objectKey(name)}: ${body}`;
|
|
1282
|
+
const returnType = recursiveGetterType({
|
|
1283
|
+
ctx,
|
|
1284
|
+
schema,
|
|
1285
|
+
required
|
|
1286
|
+
});
|
|
1287
|
+
return returnType ? `get ${objectKey(name)}(): ${returnType} { return ${body} }` : lazyGetter({
|
|
1288
|
+
name,
|
|
1289
|
+
body
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Breaks getter inference cycles without widening recursive refs and arrays to `z.ZodType`.
|
|
1295
|
+
*/
|
|
1296
|
+
function recursiveGetterType({ ctx, schema, required }) {
|
|
1297
|
+
function baseType(schema) {
|
|
1298
|
+
if (ctx.options.nodes?.[schema.type]) return void 0;
|
|
1299
|
+
if (schema.type === ast.schemaTypes.ref) {
|
|
1300
|
+
const saved = ctx.options.cyclicSchemas;
|
|
1301
|
+
ctx.options.cyclicSchemas = void 0;
|
|
1302
|
+
const name = ctx.transform(schema);
|
|
1303
|
+
ctx.options.cyclicSchemas = saved;
|
|
1304
|
+
return name ? `typeof ${name}` : void 0;
|
|
1305
|
+
}
|
|
1306
|
+
if (schema.type === ast.schemaTypes.array && schema.items?.length === 1) {
|
|
1307
|
+
const item = baseType(schema.items[0]);
|
|
1308
|
+
return item ? `z.ZodArray<${item}>` : void 0;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
const base = baseType(schema);
|
|
1312
|
+
if (!base) return void 0;
|
|
1313
|
+
const meta = syncSchemaRef(schema);
|
|
1314
|
+
const optional = schema.optional || required === false;
|
|
1315
|
+
const wrapped = (() => {
|
|
1316
|
+
if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
|
|
1317
|
+
if (optional) return `z.ZodOptional<${base}>`;
|
|
1318
|
+
if (meta.nullable) return `z.ZodNullable<${base}>`;
|
|
1319
|
+
return base;
|
|
1320
|
+
})();
|
|
1321
|
+
return meta.default !== void 0 && defaultLiteral(schema, meta.default) !== null ? `z.ZodDefault<${wrapped}>` : wrapped;
|
|
1171
1322
|
}
|
|
1172
1323
|
/**
|
|
1173
1324
|
* Types the direction probe skips. They delegate to their children rather than reading
|
|
1174
1325
|
* `direction` themselves, so {@link containsDirectionalNode} walks into the children instead.
|
|
1175
1326
|
*/
|
|
1176
1327
|
const CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1328
|
+
ast.schemaTypes.object,
|
|
1329
|
+
ast.schemaTypes.array,
|
|
1330
|
+
ast.schemaTypes.tuple,
|
|
1331
|
+
ast.schemaTypes.union,
|
|
1332
|
+
ast.schemaTypes.intersection,
|
|
1333
|
+
ast.schemaTypes.ref
|
|
1183
1334
|
]);
|
|
1184
1335
|
/**
|
|
1185
1336
|
* Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
|
|
@@ -1211,7 +1362,7 @@ function variesByDirection({ node, printerOptions }) {
|
|
|
1211
1362
|
*/
|
|
1212
1363
|
function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */ new Set() }) {
|
|
1213
1364
|
if (!node) return false;
|
|
1214
|
-
if (node.type ===
|
|
1365
|
+
if (node.type === ast.schemaTypes.ref) {
|
|
1215
1366
|
if (!node.ref) return false;
|
|
1216
1367
|
const refName = extractRefName(node.ref);
|
|
1217
1368
|
if (refName) {
|
|
@@ -1219,7 +1370,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1219
1370
|
seen.add(refName);
|
|
1220
1371
|
}
|
|
1221
1372
|
const resolved = syncSchemaRef(node);
|
|
1222
|
-
if (resolved.type ===
|
|
1373
|
+
if (resolved.type === ast.schemaTypes.ref) return false;
|
|
1223
1374
|
return containsDirectionalNode({
|
|
1224
1375
|
node: resolved,
|
|
1225
1376
|
printerOptions,
|
|
@@ -1235,7 +1386,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1235
1386
|
if ("items" in node && node.items) children.push(...node.items);
|
|
1236
1387
|
if ("members" in node && node.members) children.push(...node.members);
|
|
1237
1388
|
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
1238
|
-
const propertyNames = node.type ===
|
|
1389
|
+
const propertyNames = node.type === ast.schemaTypes.object ? node.propertyNames : void 0;
|
|
1239
1390
|
if (propertyNames) children.push(propertyNames);
|
|
1240
1391
|
return children.some((child) => containsDirectionalNode({
|
|
1241
1392
|
node: child,
|
|
@@ -1247,7 +1398,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1247
1398
|
* Names of the `$ref` schemas the generator should route to their input (encode) variant.
|
|
1248
1399
|
*/
|
|
1249
1400
|
function collectDirectionalRefNames({ node, printerOptions }) {
|
|
1250
|
-
return ast.collectSync(node, { schema: (n) => n.type ===
|
|
1401
|
+
return ast.collectSync(node, { schema: (n) => n.type === ast.schemaTypes.ref && n.ref && containsDirectionalNode({
|
|
1251
1402
|
node: n,
|
|
1252
1403
|
printerOptions
|
|
1253
1404
|
}) ? ast.resolveRefName(n) ?? void 0 : void 0 });
|
|
@@ -1339,7 +1490,7 @@ const scalarNodes = {
|
|
|
1339
1490
|
enum(node) {
|
|
1340
1491
|
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1341
1492
|
if (node.namedEnumValues?.length) {
|
|
1342
|
-
const literals = nonNullValues.map((v) =>
|
|
1493
|
+
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1343
1494
|
if (literals.length === 1) return literals[0];
|
|
1344
1495
|
return `z.union([${literals.join(", ")}])`;
|
|
1345
1496
|
}
|
|
@@ -1360,6 +1511,7 @@ const scalarNodes = {
|
|
|
1360
1511
|
*/
|
|
1361
1512
|
const printerZod = ast.createPrinter((options) => {
|
|
1362
1513
|
const cyclicSchemaNames = options.cyclicSchemas;
|
|
1514
|
+
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1363
1515
|
return {
|
|
1364
1516
|
name: "zod",
|
|
1365
1517
|
options,
|
|
@@ -1385,37 +1537,40 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1385
1537
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1386
1538
|
const isLoose = node.additionalProperties === true;
|
|
1387
1539
|
const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodObjectShape(this, node)})`;
|
|
1388
|
-
return (
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1540
|
+
return objectConstraints({
|
|
1541
|
+
value: (() => {
|
|
1542
|
+
const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
|
|
1543
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
1544
|
+
const catchallType = this.transform(node.additionalProperties);
|
|
1545
|
+
if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
|
|
1546
|
+
return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
|
|
1547
|
+
}
|
|
1548
|
+
if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
|
|
1549
|
+
const unknownType = this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1550
|
+
return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
|
|
1551
|
+
}
|
|
1552
|
+
if (node.additionalProperties === true) return objectBase;
|
|
1553
|
+
if (isStrict) return objectBase;
|
|
1554
|
+
if (patterns.length > 0) {
|
|
1555
|
+
const values = patterns.map(([, valueSchema]) => {
|
|
1556
|
+
const valueType = this.transform(valueSchema) ?? this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1557
|
+
return valueSchema.nullable ? `${valueType}.nullable()` : valueType;
|
|
1558
|
+
});
|
|
1559
|
+
const distinct = [...new Set(values)];
|
|
1560
|
+
const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
|
|
1561
|
+
if (entries.length > 0) return `${objectBase}.catchall(${value})`;
|
|
1562
|
+
return `z.record(${patternKeySchema({
|
|
1563
|
+
patterns: patterns.map(([pattern]) => pattern),
|
|
1564
|
+
regexType: this.options.regexType
|
|
1565
|
+
})}, ${value})`;
|
|
1566
|
+
}
|
|
1567
|
+
return objectBase;
|
|
1568
|
+
})(),
|
|
1569
|
+
node
|
|
1570
|
+
});
|
|
1416
1571
|
},
|
|
1417
1572
|
array(node) {
|
|
1418
|
-
const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type:
|
|
1573
|
+
const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }))})${lengthConstraints({
|
|
1419
1574
|
...node,
|
|
1420
1575
|
regexType: this.options.regexType
|
|
1421
1576
|
})}`;
|
|
@@ -1428,7 +1583,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1428
1583
|
const nodeMembers = node.members ?? [];
|
|
1429
1584
|
const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1430
1585
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1431
|
-
const isStrict = node.strategy === "one" && !isXor;
|
|
1586
|
+
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1432
1587
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ schema, output }) => output && isStrict ? strictOneOfMember(output, schema, cyclicSchemaNames) : output).filter(Boolean);
|
|
1433
1588
|
if (members.length === 0) return "";
|
|
1434
1589
|
if (members.length === 1) return members[0];
|
|
@@ -1439,11 +1594,21 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1439
1594
|
intersection(node) {
|
|
1440
1595
|
const members = node.members ?? [];
|
|
1441
1596
|
if (members.length === 0) return "";
|
|
1597
|
+
const hasSiblingProperties = members.some((member) => member.type === ast.schemaTypes.object && member.properties.length > 0);
|
|
1598
|
+
const transform = (member) => {
|
|
1599
|
+
if (!hasSiblingProperties) return this.transform(member);
|
|
1600
|
+
unionsWithSiblings.add(member);
|
|
1601
|
+
try {
|
|
1602
|
+
return this.transform(member);
|
|
1603
|
+
} finally {
|
|
1604
|
+
unionsWithSiblings.delete(member);
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1442
1607
|
const [first, ...rest] = members;
|
|
1443
1608
|
if (!first) return "";
|
|
1444
|
-
const firstBase =
|
|
1609
|
+
const firstBase = transform(first);
|
|
1445
1610
|
if (!firstBase) return "";
|
|
1446
|
-
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return
|
|
1611
|
+
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return preserveRequiredProperties(members).slice(1).reduce((acc, member) => `${acc}.extend(${buildZodObjectShape(this, member)})`, firstBase);
|
|
1447
1612
|
return rest.reduce((acc, member) => {
|
|
1448
1613
|
const constraint = getMemberConstraint({
|
|
1449
1614
|
member,
|
|
@@ -1452,7 +1617,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1452
1617
|
numbers: numberConstraints
|
|
1453
1618
|
});
|
|
1454
1619
|
if (constraint) return acc + constraint;
|
|
1455
|
-
const transformed =
|
|
1620
|
+
const transformed = transform(member);
|
|
1456
1621
|
return transformed ? `${acc}.and(${transformed})` : acc;
|
|
1457
1622
|
}, firstBase);
|
|
1458
1623
|
}
|
|
@@ -1472,20 +1637,25 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1472
1637
|
}
|
|
1473
1638
|
};
|
|
1474
1639
|
});
|
|
1475
|
-
|
|
1476
|
-
//#region src/printers/printerZodMini.ts
|
|
1640
|
+
// src/printers/printerZodMini.ts
|
|
1477
1641
|
/**
|
|
1478
1642
|
* Builds the `{ key: value, … }` shape for an object node with the functional `zod/mini` modifiers.
|
|
1479
1643
|
*/
|
|
1480
1644
|
function buildZodMiniObjectShape(ctx, node) {
|
|
1481
|
-
return buildObjectShape(ctx, node, ({ value, schema, required, meta }) =>
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1645
|
+
return buildObjectShape(ctx, node, ({ name, value, schema, required, meta, cyclic }) => {
|
|
1646
|
+
const body = applyMiniModifiers({
|
|
1647
|
+
value,
|
|
1648
|
+
schema,
|
|
1649
|
+
nullable: meta.nullable,
|
|
1650
|
+
optional: schema.optional || required === false,
|
|
1651
|
+
nullish: schema.nullish,
|
|
1652
|
+
defaultValue: meta.default
|
|
1653
|
+
});
|
|
1654
|
+
return cyclic ? lazyGetter({
|
|
1655
|
+
name,
|
|
1656
|
+
body
|
|
1657
|
+
}) : `${objectKey(name)}: ${body}`;
|
|
1658
|
+
});
|
|
1489
1659
|
}
|
|
1490
1660
|
/**
|
|
1491
1661
|
* Zod v4 Mini printer built with `definePrinter`.
|
|
@@ -1501,6 +1671,7 @@ function buildZodMiniObjectShape(ctx, node) {
|
|
|
1501
1671
|
*/
|
|
1502
1672
|
const printerZodMini = ast.createPrinter((options) => {
|
|
1503
1673
|
const cyclicSchemaNames = options.cyclicSchemas;
|
|
1674
|
+
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1504
1675
|
return {
|
|
1505
1676
|
name: "zod-mini",
|
|
1506
1677
|
options,
|
|
@@ -1573,7 +1744,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1573
1744
|
enum(node) {
|
|
1574
1745
|
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1575
1746
|
if (node.namedEnumValues?.length) {
|
|
1576
|
-
const literals = nonNullValues.map((v) =>
|
|
1747
|
+
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1577
1748
|
if (literals.length === 1) return literals[0];
|
|
1578
1749
|
return `z.union([${literals.join(", ")}])`;
|
|
1579
1750
|
}
|
|
@@ -1595,32 +1766,40 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1595
1766
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1596
1767
|
const isLoose = node.additionalProperties === true;
|
|
1597
1768
|
const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodMiniObjectShape(this, node)})`;
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1769
|
+
return objectConstraints({
|
|
1770
|
+
value: (() => {
|
|
1771
|
+
const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
|
|
1772
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
1773
|
+
const catchallType = this.transform(node.additionalProperties);
|
|
1774
|
+
if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
|
|
1775
|
+
return catchallType ? `z.catchall(${objectBase}, ${catchallType})` : objectBase;
|
|
1776
|
+
}
|
|
1777
|
+
if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
|
|
1778
|
+
const unknownType = this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1779
|
+
return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
|
|
1780
|
+
}
|
|
1781
|
+
if (node.additionalProperties === true) return objectBase;
|
|
1782
|
+
if (isStrict) return objectBase;
|
|
1783
|
+
if (patterns.length > 0) {
|
|
1784
|
+
const values = patterns.map(([, valueSchema]) => {
|
|
1785
|
+
const valueType = this.transform(valueSchema) ?? this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1786
|
+
return valueSchema.nullable ? `z.nullable(${valueType})` : valueType;
|
|
1787
|
+
});
|
|
1788
|
+
const distinct = [...new Set(values)];
|
|
1789
|
+
const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
|
|
1790
|
+
if (entries.length > 0) return `z.catchall(${objectBase}, ${value})`;
|
|
1791
|
+
return `z.record(${patternKeySchemaMini({
|
|
1792
|
+
patterns: patterns.map(([pattern]) => pattern),
|
|
1793
|
+
regexType: this.options.regexType
|
|
1794
|
+
})}, ${value})`;
|
|
1795
|
+
}
|
|
1796
|
+
return objectBase;
|
|
1797
|
+
})(),
|
|
1798
|
+
node
|
|
1799
|
+
});
|
|
1621
1800
|
},
|
|
1622
1801
|
array(node) {
|
|
1623
|
-
const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type:
|
|
1802
|
+
const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }))})${lengthChecksMini({
|
|
1624
1803
|
...node,
|
|
1625
1804
|
regexType: this.options.regexType
|
|
1626
1805
|
})}`;
|
|
@@ -1633,7 +1812,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1633
1812
|
const nodeMembers = node.members ?? [];
|
|
1634
1813
|
const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1635
1814
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1636
|
-
const isStrict = node.strategy === "one" && !isXor;
|
|
1815
|
+
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1637
1816
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ output }) => output).filter(Boolean);
|
|
1638
1817
|
if (members.length === 0) return "";
|
|
1639
1818
|
if (members.length === 1) return members[0];
|
|
@@ -1644,11 +1823,21 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1644
1823
|
intersection(node) {
|
|
1645
1824
|
const members = node.members ?? [];
|
|
1646
1825
|
if (members.length === 0) return "";
|
|
1826
|
+
const hasSiblingProperties = members.some((member) => member.type === ast.schemaTypes.object && member.properties.length > 0);
|
|
1827
|
+
const transform = (member) => {
|
|
1828
|
+
if (!hasSiblingProperties) return this.transform(member);
|
|
1829
|
+
unionsWithSiblings.add(member);
|
|
1830
|
+
try {
|
|
1831
|
+
return this.transform(member);
|
|
1832
|
+
} finally {
|
|
1833
|
+
unionsWithSiblings.delete(member);
|
|
1834
|
+
}
|
|
1835
|
+
};
|
|
1647
1836
|
const [first, ...rest] = members;
|
|
1648
1837
|
if (!first) return "";
|
|
1649
|
-
const firstBase =
|
|
1838
|
+
const firstBase = transform(first);
|
|
1650
1839
|
if (!firstBase) return "";
|
|
1651
|
-
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return
|
|
1840
|
+
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return preserveRequiredProperties(members).slice(1).reduce((acc, member) => `z.extend(${acc}, ${buildZodMiniObjectShape(this, member)})`, firstBase);
|
|
1652
1841
|
return rest.reduce((acc, member) => {
|
|
1653
1842
|
const constraint = getMemberConstraint({
|
|
1654
1843
|
member,
|
|
@@ -1657,7 +1846,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1657
1846
|
numbers: numberChecksMini
|
|
1658
1847
|
});
|
|
1659
1848
|
if (constraint) return acc + constraint;
|
|
1660
|
-
const transformed =
|
|
1849
|
+
const transformed = transform(member);
|
|
1661
1850
|
return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
|
|
1662
1851
|
}, firstBase);
|
|
1663
1852
|
}
|
|
@@ -1675,8 +1864,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1675
1864
|
}
|
|
1676
1865
|
};
|
|
1677
1866
|
});
|
|
1678
|
-
|
|
1679
|
-
//#region src/generators/zodGenerator.tsx
|
|
1867
|
+
// src/generators/zodGenerator.tsx
|
|
1680
1868
|
/**
|
|
1681
1869
|
* The helper an inferred alias is printed with. `'codec'` reads a request (encode) schema from its
|
|
1682
1870
|
* input and a response (decode) schema from its output; `true` keeps `z.infer` for both.
|
|
@@ -2137,17 +2325,14 @@ const zodGenerator = defineGenerator({
|
|
|
2137
2325
|
});
|
|
2138
2326
|
}
|
|
2139
2327
|
});
|
|
2140
|
-
|
|
2141
|
-
//#region kubb-extension.json
|
|
2328
|
+
// kubb-extension.json
|
|
2142
2329
|
var compatibility = {
|
|
2143
|
-
"kubb": ">=5.
|
|
2330
|
+
"kubb": ">=5.14.6",
|
|
2144
2331
|
"node": ">=22.22"
|
|
2145
2332
|
};
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
//#endregion
|
|
2150
|
-
//#region src/resolvers/resolverZod.ts
|
|
2333
|
+
// package.json
|
|
2334
|
+
var version = "5.7.4";
|
|
2335
|
+
// src/resolvers/resolverZod.ts
|
|
2151
2336
|
/**
|
|
2152
2337
|
* Default resolver used by `@kubb/plugin-zod`. Decides the names and file
|
|
2153
2338
|
* paths for every generated Zod schema. Schemas use camelCase with a
|
|
@@ -2166,15 +2351,15 @@ var version = "5.7.2";
|
|
|
2166
2351
|
const resolverZod = createResolver({
|
|
2167
2352
|
pluginName: "plugin-zod",
|
|
2168
2353
|
name(name) {
|
|
2169
|
-
return ensureValidVarName(camelCase(name, { suffix: "schema" }));
|
|
2354
|
+
return this.memo(`name:${name}`, () => ensureValidVarName(camelCase(name, { suffix: "schema" })));
|
|
2170
2355
|
},
|
|
2171
2356
|
file: createCasedFile((part) => camelCase(part, { suffix: "schema" })),
|
|
2172
2357
|
schema: {
|
|
2173
2358
|
typeName(name) {
|
|
2174
|
-
return ensureValidVarName(pascalCase(name, { suffix: "schema type" }));
|
|
2359
|
+
return this.memo(`typeName:${name}`, () => ensureValidVarName(pascalCase(name, { suffix: "schema type" })));
|
|
2175
2360
|
},
|
|
2176
2361
|
type(name) {
|
|
2177
|
-
return ensureValidVarName(pascalCase(name, { suffix: "type" }));
|
|
2362
|
+
return this.memo(`type:${name}`, () => ensureValidVarName(pascalCase(name, { suffix: "type" })));
|
|
2178
2363
|
},
|
|
2179
2364
|
inputName(name) {
|
|
2180
2365
|
return this.name(`${name} input`);
|
|
@@ -2193,15 +2378,22 @@ const resolverZod = createResolver({
|
|
|
2193
2378
|
response: {
|
|
2194
2379
|
...createOperationResponseResolver(),
|
|
2195
2380
|
error(node) {
|
|
2196
|
-
|
|
2381
|
+
const name = this.name(`${node.operationId} Error`);
|
|
2382
|
+
return this.resolveName({
|
|
2383
|
+
key: operationKey(node, "response", "error"),
|
|
2384
|
+
name
|
|
2385
|
+
});
|
|
2197
2386
|
},
|
|
2198
2387
|
options(node) {
|
|
2199
|
-
|
|
2388
|
+
const name = this.schema.type(this.name(`${node.operationId} Options`));
|
|
2389
|
+
return this.resolveName({
|
|
2390
|
+
key: operationKey(node, "response", "options"),
|
|
2391
|
+
name
|
|
2392
|
+
});
|
|
2200
2393
|
}
|
|
2201
2394
|
}
|
|
2202
2395
|
});
|
|
2203
|
-
|
|
2204
|
-
//#region src/plugin.ts
|
|
2396
|
+
// src/plugin.ts
|
|
2205
2397
|
/**
|
|
2206
2398
|
* Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
|
|
2207
2399
|
* cross-plugin dependency references.
|
|
@@ -2267,7 +2459,6 @@ const pluginZod = defineExtension({
|
|
|
2267
2459
|
};
|
|
2268
2460
|
}
|
|
2269
2461
|
});
|
|
2270
|
-
//#endregion
|
|
2271
2462
|
export { pluginZod as default, pluginZod, pluginZodName, printerZod, printerZodMini, resolverZod, zodGenerator };
|
|
2272
2463
|
|
|
2273
2464
|
//# sourceMappingURL=index.js.map
|