@kubb/plugin-zod 5.7.2 → 5.7.3
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 +425 -185
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -12
- package/dist/index.d.ts +0 -12
- package/dist/index.js +425 -185
- 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
1
|
import { Diagnostics, Resolver, addGenerator, ast, containsCircularRef, createResolver, defineExtension, defineGenerator, extractRefName, 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,8 +255,7 @@ function getOasAdapter(adapter) {
|
|
|
226
255
|
});
|
|
227
256
|
return adapter;
|
|
228
257
|
}
|
|
229
|
-
|
|
230
|
-
//#region ../../internals/utils/src/casing.ts
|
|
258
|
+
// ../../internals/utils/src/casing.ts
|
|
231
259
|
/**
|
|
232
260
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
233
261
|
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
@@ -276,8 +304,7 @@ function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
276
304
|
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
277
305
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
278
306
|
}
|
|
279
|
-
|
|
280
|
-
//#region ../../internals/utils/src/reserved.ts
|
|
307
|
+
// ../../internals/utils/src/reserved.ts
|
|
281
308
|
/**
|
|
282
309
|
* JavaScript and Java reserved words.
|
|
283
310
|
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
@@ -285,6 +312,7 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
285
312
|
const reservedWords = /* @__PURE__ */ new Set([
|
|
286
313
|
"abstract",
|
|
287
314
|
"arguments",
|
|
315
|
+
"await",
|
|
288
316
|
"boolean",
|
|
289
317
|
"break",
|
|
290
318
|
"byte",
|
|
@@ -415,8 +443,7 @@ function ensureValidVarName(name) {
|
|
|
415
443
|
function isIdentifier(name) {
|
|
416
444
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
417
445
|
}
|
|
418
|
-
|
|
419
|
-
//#region ../../internals/utils/src/strings.ts
|
|
446
|
+
// ../../internals/utils/src/strings.ts
|
|
420
447
|
/**
|
|
421
448
|
* Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
|
|
422
449
|
* any backslash or single quote in the content.
|
|
@@ -465,6 +492,34 @@ function stringify(value) {
|
|
|
465
492
|
if (value === void 0 || value === null) return "''";
|
|
466
493
|
return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
|
|
467
494
|
}
|
|
495
|
+
const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
|
|
496
|
+
const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
|
|
497
|
+
/**
|
|
498
|
+
* Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
|
|
499
|
+
* not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
|
|
500
|
+
*/
|
|
501
|
+
function dropIdentityEscapes(pattern) {
|
|
502
|
+
let inClass = false;
|
|
503
|
+
return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
|
|
504
|
+
if (escaped === void 0) {
|
|
505
|
+
inClass = match === "[";
|
|
506
|
+
return match;
|
|
507
|
+
}
|
|
508
|
+
return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Builds the `RegExp` for a pattern, adding the `u` flag when it uses `\p{...}`, `\P{...}` or `\u{...}`, or
|
|
513
|
+
* holds a character outside the BMP. A pattern that is not valid under `u` keeps the flags it came with.
|
|
514
|
+
*/
|
|
515
|
+
function toRegExp(pattern, flags) {
|
|
516
|
+
if (flags?.includes("u") || !NEEDS_UNICODE.test(pattern)) return new RegExp(pattern, flags);
|
|
517
|
+
try {
|
|
518
|
+
return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
|
|
519
|
+
} catch {
|
|
520
|
+
return new RegExp(pattern, flags);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
468
523
|
/**
|
|
469
524
|
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
|
|
470
525
|
* Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
|
|
@@ -481,13 +536,11 @@ function toRegExpString(text, func = "RegExp") {
|
|
|
481
536
|
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
|
|
482
537
|
const replacementTarget = match?.[1] ?? "";
|
|
483
538
|
const matchedFlags = match?.[2];
|
|
484
|
-
const
|
|
485
|
-
const { source, flags } = new RegExp(cleaned, matchedFlags);
|
|
539
|
+
const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
|
|
486
540
|
if (func === null) return `/${source}/${flags}`;
|
|
487
541
|
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
|
|
488
542
|
}
|
|
489
|
-
|
|
490
|
-
//#region ../../internals/utils/src/codegen.ts
|
|
543
|
+
// ../../internals/utils/src/codegen.ts
|
|
491
544
|
const INDENT = " ";
|
|
492
545
|
/**
|
|
493
546
|
* Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
|
|
@@ -556,8 +609,7 @@ function buildList(items, brackets = ["[", "]"]) {
|
|
|
556
609
|
function lazyGetter({ name, body }) {
|
|
557
610
|
return `get ${objectKey(name)}() { return ${body} }`;
|
|
558
611
|
}
|
|
559
|
-
|
|
560
|
-
//#region ../../internals/utils/src/fs.ts
|
|
612
|
+
// ../../internals/utils/src/fs.ts
|
|
561
613
|
/**
|
|
562
614
|
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
563
615
|
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
@@ -581,17 +633,24 @@ function toFilePath(name, caseLast = camelCase) {
|
|
|
581
633
|
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
582
634
|
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
583
635
|
}
|
|
584
|
-
|
|
585
|
-
//#region ../../internals/shared/src/resolver.ts
|
|
636
|
+
// ../../internals/shared/src/resolver.ts
|
|
586
637
|
/**
|
|
587
|
-
*
|
|
588
|
-
*
|
|
638
|
+
* Builds the allocation key for a declaration an operation owns. `scope` names which declaration
|
|
639
|
+
* it is, so two of them never share an allocation even when a custom resolver gives both the same
|
|
640
|
+
* name. Leave `scope` empty for the operation's own declaration.
|
|
589
641
|
*
|
|
590
642
|
* @example
|
|
591
|
-
*
|
|
643
|
+
* ```ts
|
|
644
|
+
* resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
|
|
645
|
+
* ```
|
|
592
646
|
*/
|
|
593
|
-
function
|
|
594
|
-
return
|
|
647
|
+
function operationKey(node, ...scope) {
|
|
648
|
+
return [
|
|
649
|
+
"operation",
|
|
650
|
+
node.method,
|
|
651
|
+
node.path,
|
|
652
|
+
...scope.map(String)
|
|
653
|
+
];
|
|
595
654
|
}
|
|
596
655
|
/**
|
|
597
656
|
* Builds the shared `param` namespace. Spread the result into `createResolver`
|
|
@@ -604,15 +663,33 @@ function operationParamName(node, param) {
|
|
|
604
663
|
*/
|
|
605
664
|
function createOperationParamResolver() {
|
|
606
665
|
return {
|
|
607
|
-
name
|
|
666
|
+
name(node, param) {
|
|
667
|
+
const name = this.name(`${node.operationId} ${param.in} ${param.name}`);
|
|
668
|
+
return this.resolveName({
|
|
669
|
+
key: operationKey(node, "param", "name", param.in, param.name),
|
|
670
|
+
name
|
|
671
|
+
});
|
|
672
|
+
},
|
|
608
673
|
path(node) {
|
|
609
|
-
|
|
674
|
+
const name = this.name(`${node.operationId} Path`);
|
|
675
|
+
return this.resolveName({
|
|
676
|
+
key: operationKey(node, "param", "path"),
|
|
677
|
+
name
|
|
678
|
+
});
|
|
610
679
|
},
|
|
611
680
|
query(node) {
|
|
612
|
-
|
|
681
|
+
const name = this.name(`${node.operationId} Query`);
|
|
682
|
+
return this.resolveName({
|
|
683
|
+
key: operationKey(node, "param", "query"),
|
|
684
|
+
name
|
|
685
|
+
});
|
|
613
686
|
},
|
|
614
687
|
headers(node) {
|
|
615
|
-
|
|
688
|
+
const name = this.name(`${node.operationId} Headers`);
|
|
689
|
+
return this.resolveName({
|
|
690
|
+
key: operationKey(node, "param", "headers"),
|
|
691
|
+
name
|
|
692
|
+
});
|
|
616
693
|
}
|
|
617
694
|
};
|
|
618
695
|
}
|
|
@@ -629,16 +706,32 @@ function createOperationParamResolver() {
|
|
|
629
706
|
function createOperationResponseResolver() {
|
|
630
707
|
return {
|
|
631
708
|
status(node, statusCode) {
|
|
632
|
-
|
|
709
|
+
const name = this.name(`${node.operationId} Status ${statusCode}`);
|
|
710
|
+
return this.resolveName({
|
|
711
|
+
key: operationKey(node, "response", "status", statusCode),
|
|
712
|
+
name
|
|
713
|
+
});
|
|
633
714
|
},
|
|
634
715
|
body(node) {
|
|
635
|
-
|
|
716
|
+
const name = this.name(`${node.operationId} Body`);
|
|
717
|
+
return this.resolveName({
|
|
718
|
+
key: operationKey(node, "response", "body"),
|
|
719
|
+
name
|
|
720
|
+
});
|
|
636
721
|
},
|
|
637
722
|
responses(node) {
|
|
638
|
-
|
|
723
|
+
const name = this.name(`${node.operationId} Responses`);
|
|
724
|
+
return this.resolveName({
|
|
725
|
+
key: operationKey(node, "response", "responses"),
|
|
726
|
+
name
|
|
727
|
+
});
|
|
639
728
|
},
|
|
640
729
|
response(node) {
|
|
641
|
-
|
|
730
|
+
const name = this.name(`${node.operationId} Response`);
|
|
731
|
+
return this.resolveName({
|
|
732
|
+
key: operationKey(node, "response", "response"),
|
|
733
|
+
name
|
|
734
|
+
});
|
|
642
735
|
}
|
|
643
736
|
};
|
|
644
737
|
}
|
|
@@ -656,8 +749,7 @@ function createCasedFile(caseLast) {
|
|
|
656
749
|
return `${toFilePath(name, caseLast)}${extname}`;
|
|
657
750
|
} };
|
|
658
751
|
}
|
|
659
|
-
|
|
660
|
-
//#region ../../internals/shared/src/group.ts
|
|
752
|
+
// ../../internals/shared/src/group.ts
|
|
661
753
|
/**
|
|
662
754
|
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
663
755
|
* shared default naming so every plugin groups output consistently:
|
|
@@ -687,8 +779,7 @@ function createGroupConfig(group) {
|
|
|
687
779
|
name: group.name ? group.name : defaultName
|
|
688
780
|
};
|
|
689
781
|
}
|
|
690
|
-
|
|
691
|
-
//#region ../../internals/shared/src/schemaTraversal.ts
|
|
782
|
+
// ../../internals/shared/src/schemaTraversal.ts
|
|
692
783
|
/**
|
|
693
784
|
* Maps each property of an object schema to its transformed output. Pairs every result with the
|
|
694
785
|
* original property so the printer keeps full control over modifiers, getters, and key syntax.
|
|
@@ -726,8 +817,7 @@ function mapSchemaItems(node, transform) {
|
|
|
726
817
|
output: transform(schema)
|
|
727
818
|
}));
|
|
728
819
|
}
|
|
729
|
-
|
|
730
|
-
//#region src/components/Zod.tsx
|
|
820
|
+
// src/components/Zod.tsx
|
|
731
821
|
function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
|
|
732
822
|
const output = printer.print(node);
|
|
733
823
|
const printerImports = printer.drainImports();
|
|
@@ -799,8 +889,7 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
|
|
|
799
889
|
})
|
|
800
890
|
] });
|
|
801
891
|
}
|
|
802
|
-
|
|
803
|
-
//#region src/utils.ts
|
|
892
|
+
// src/utils.ts
|
|
804
893
|
/**
|
|
805
894
|
* Returns `true` when the given coercion option enables coercion for the specified type.
|
|
806
895
|
*/
|
|
@@ -829,11 +918,12 @@ function isClosedKeySchema(node) {
|
|
|
829
918
|
}
|
|
830
919
|
/**
|
|
831
920
|
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
832
|
-
* argument.
|
|
921
|
+
* argument. Property counts, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
|
|
833
922
|
* on `.and(…)`.
|
|
834
923
|
*/
|
|
835
924
|
function isPlainInlineObject(node) {
|
|
836
925
|
if (node.type !== "object") return false;
|
|
926
|
+
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
837
927
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
838
928
|
const hasAdditionalProperties = node.additionalProperties !== void 0;
|
|
839
929
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
@@ -844,11 +934,12 @@ function isPlainInlineObject(node) {
|
|
|
844
934
|
* `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
|
|
845
935
|
* unresolved) objects, single-member unions of an object, and object-composable `allOf`.
|
|
846
936
|
*
|
|
847
|
-
*
|
|
937
|
+
* Property counts render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
|
|
848
938
|
*/
|
|
849
939
|
function isObjectSchemaNode(node, cyclicSchemas) {
|
|
850
940
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
851
941
|
if (node.type === "object") {
|
|
942
|
+
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
852
943
|
const hasProperties = Boolean(node.properties?.length);
|
|
853
944
|
const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
|
|
854
945
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
@@ -876,6 +967,49 @@ function isObjectComposableIntersection(node, cyclicSchemas) {
|
|
|
876
967
|
const [first, ...rest] = node.members ?? [];
|
|
877
968
|
return !!first && isObjectSchemaNode(first, cyclicSchemas) && rest.every(isPlainInlineObject);
|
|
878
969
|
}
|
|
970
|
+
function preserveRequiredProperties(members) {
|
|
971
|
+
const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
|
|
972
|
+
if (seen.has(node)) return false;
|
|
973
|
+
seen.add(node);
|
|
974
|
+
const resolved = syncSchemaRef(node);
|
|
975
|
+
const composition = ast.narrowSchema(resolved, ast.schemaTypes.intersection) ?? ast.narrowSchema(resolved, ast.schemaTypes.union);
|
|
976
|
+
if (composition) {
|
|
977
|
+
if (composition.type === ast.schemaTypes.union && composition.members?.length !== 1) return false;
|
|
978
|
+
return composition.members?.some((child) => hasRequiredProperty({
|
|
979
|
+
node: child,
|
|
980
|
+
propertyName,
|
|
981
|
+
seen
|
|
982
|
+
})) ?? false;
|
|
983
|
+
}
|
|
984
|
+
return ast.resolveSchemaProperties({
|
|
985
|
+
node: resolved,
|
|
986
|
+
propertyName
|
|
987
|
+
}).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
|
|
988
|
+
};
|
|
989
|
+
return members.map((member, index) => {
|
|
990
|
+
const object = ast.narrowSchema(member, ast.schemaTypes.object);
|
|
991
|
+
if (!object || index === 0) return member;
|
|
992
|
+
return {
|
|
993
|
+
...object,
|
|
994
|
+
properties: object.properties.map((property) => {
|
|
995
|
+
if (!members.slice(0, index).some((base) => hasRequiredProperty({
|
|
996
|
+
node: base,
|
|
997
|
+
propertyName: property.name
|
|
998
|
+
}))) return property;
|
|
999
|
+
return {
|
|
1000
|
+
...property,
|
|
1001
|
+
required: true,
|
|
1002
|
+
schema: {
|
|
1003
|
+
...property.schema,
|
|
1004
|
+
optional: false,
|
|
1005
|
+
nullish: false,
|
|
1006
|
+
nullable: property.schema.nullable || property.schema.nullish
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
})
|
|
1010
|
+
};
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
879
1013
|
/**
|
|
880
1014
|
* Format a default value as a code-level literal.
|
|
881
1015
|
* Arrays and objects keep their contents as a JSON literal, primitives become their string
|
|
@@ -922,11 +1056,20 @@ function defaultLiteral(node, value) {
|
|
|
922
1056
|
*/
|
|
923
1057
|
function buildEnum(values) {
|
|
924
1058
|
if (values.every((v) => typeof v === "string")) return `z.enum([${values.map((v) => formatDefault(v)).join(", ")}])`;
|
|
925
|
-
const literals = values.map((
|
|
1059
|
+
const literals = values.map((value) => buildLiteral(value));
|
|
926
1060
|
if (literals.length === 1) return literals[0];
|
|
927
1061
|
return `z.union([${literals.join(", ")}])`;
|
|
928
1062
|
}
|
|
929
1063
|
/**
|
|
1064
|
+
* Preserves the structure of JSON const values in both Zod APIs.
|
|
1065
|
+
*/
|
|
1066
|
+
function buildLiteral(value) {
|
|
1067
|
+
if (value === null) return "z.null()";
|
|
1068
|
+
if (Array.isArray(value)) return `z.tuple([${value.map((item) => buildLiteral(item)).join(", ")}])`;
|
|
1069
|
+
if (typeof value === "object") return `z.strictObject({${Object.entries(value).map(([name, item]) => `${JSON.stringify(name)}: ${buildLiteral(item)}`).join(", ")}})`;
|
|
1070
|
+
return `z.literal(${formatDefault(value)})`;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
930
1073
|
* Digit pattern for a `type: 'string'` schema that carries an integer `format`, the way ProtoJSON
|
|
931
1074
|
* encodes 64-bit integers. Returns `undefined` for every other format, and a `pattern` from the
|
|
932
1075
|
* spec takes precedence over this fallback.
|
|
@@ -1009,6 +1152,17 @@ function lengthChecksMini({ min, max, pattern, regexType }) {
|
|
|
1009
1152
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
1010
1153
|
}
|
|
1011
1154
|
/**
|
|
1155
|
+
* Checks property counts before object parsing strips unknown keys or inserts defaults.
|
|
1156
|
+
*/
|
|
1157
|
+
function objectConstraints({ value, node }) {
|
|
1158
|
+
if (node.type !== "object") return value;
|
|
1159
|
+
const checks = [];
|
|
1160
|
+
const skip = "typeof value !== \"object\" || value === null || Array.isArray(value)";
|
|
1161
|
+
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" })`);
|
|
1162
|
+
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" })`);
|
|
1163
|
+
return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1012
1166
|
* Apply nullable / optional / nullish modifiers, an optional `.describe()` call, and an
|
|
1013
1167
|
* optional `.meta({ examples })` call to a schema value string using the chainable Zod v4 API.
|
|
1014
1168
|
*/
|
|
@@ -1098,7 +1252,7 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
|
|
|
1098
1252
|
/**
|
|
1099
1253
|
* Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
|
|
1100
1254
|
*/
|
|
1101
|
-
function buildObjectShape(ctx, node,
|
|
1255
|
+
function buildObjectShape(ctx, node, renderProperty) {
|
|
1102
1256
|
const objectNode = ast.narrowSchema(node, "object");
|
|
1103
1257
|
if (!objectNode) return "{}";
|
|
1104
1258
|
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
@@ -1109,18 +1263,16 @@ function buildObjectShape(ctx, node, modify) {
|
|
|
1109
1263
|
const baseOutput = ctx.transform(schema) ?? ctx.transform(ast.factory.createSchema({ type: "unknown" }));
|
|
1110
1264
|
if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
|
|
1111
1265
|
return baseOutput;
|
|
1112
|
-
}).map(({ name
|
|
1266
|
+
}).map(({ name, property, output: value }) => {
|
|
1113
1267
|
const { schema } = property;
|
|
1114
|
-
|
|
1115
|
-
|
|
1268
|
+
return renderProperty({
|
|
1269
|
+
name,
|
|
1270
|
+
value,
|
|
1116
1271
|
schema,
|
|
1117
1272
|
required: property.required,
|
|
1118
|
-
meta: syncSchemaRef(schema)
|
|
1273
|
+
meta: syncSchemaRef(schema),
|
|
1274
|
+
cyclic: isCyclic(schema)
|
|
1119
1275
|
});
|
|
1120
|
-
return isCyclic(schema) ? lazyGetter({
|
|
1121
|
-
name: propName,
|
|
1122
|
-
body: value
|
|
1123
|
-
}) : `${objectKey(propName)}: ${value}`;
|
|
1124
1276
|
}));
|
|
1125
1277
|
}
|
|
1126
1278
|
/**
|
|
@@ -1135,22 +1287,28 @@ function printWithOmit(ctx, node, modify, mini = false) {
|
|
|
1135
1287
|
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
1136
1288
|
const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
|
|
1137
1289
|
const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
|
|
1138
|
-
const omit = (target) =>
|
|
1290
|
+
const omit = (target) => {
|
|
1291
|
+
const inner = `${target}${unwrap}`;
|
|
1292
|
+
const hasObjectConstraints = meta.type === "object" && (meta.minProperties !== void 0 || meta.maxProperties !== void 0);
|
|
1293
|
+
const pipe = mini ? `${inner}._zod.def` : inner;
|
|
1294
|
+
const object = hasObjectConstraints ? `${pipe}.out` : inner;
|
|
1295
|
+
const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
|
|
1296
|
+
return hasObjectConstraints ? `z.pipe(${pipe}.in, ${omitted})` : omitted;
|
|
1297
|
+
};
|
|
1139
1298
|
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
|
|
1140
1299
|
if (lazyMatch) return `z.lazy(() => ${omit(lazyMatch[1])})`;
|
|
1141
1300
|
return omit(transformed);
|
|
1142
1301
|
})(), meta);
|
|
1143
1302
|
}
|
|
1144
|
-
|
|
1145
|
-
//#region src/printers/printerZod.ts
|
|
1303
|
+
// src/printers/printerZod.ts
|
|
1146
1304
|
function strictOneOfMember(member, node, cyclicSchemas) {
|
|
1147
|
-
if (node.type ===
|
|
1305
|
+
if (node.type === ast.schemaTypes.ref) {
|
|
1148
1306
|
if (member.startsWith("z.lazy(")) return member;
|
|
1149
1307
|
const refName = ast.resolveRefName(node);
|
|
1150
1308
|
if (refName && cyclicSchemas?.has(refName)) return member;
|
|
1151
1309
|
const schema = syncSchemaRef(node);
|
|
1152
1310
|
if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
|
|
1153
|
-
if (schema.type ===
|
|
1311
|
+
if (schema.type === ast.schemaTypes.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
|
|
1154
1312
|
}
|
|
1155
1313
|
return member;
|
|
1156
1314
|
}
|
|
@@ -1158,28 +1316,70 @@ function strictOneOfMember(member, node, cyclicSchemas) {
|
|
|
1158
1316
|
* Builds the `{ key: value, … }` shape for an object node with the chainable modifiers.
|
|
1159
1317
|
*/
|
|
1160
1318
|
function buildZodObjectShape(ctx, node) {
|
|
1161
|
-
return buildObjectShape(ctx, node, ({ value, schema, required, meta }) =>
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1319
|
+
return buildObjectShape(ctx, node, ({ name, value, schema, required, meta, cyclic }) => {
|
|
1320
|
+
const body = applyModifiers({
|
|
1321
|
+
value,
|
|
1322
|
+
schema,
|
|
1323
|
+
nullable: meta.nullable,
|
|
1324
|
+
optional: schema.optional || required === false,
|
|
1325
|
+
nullish: schema.nullish,
|
|
1326
|
+
defaultValue: meta.default,
|
|
1327
|
+
description: schema.type !== ast.schemaTypes.ref && meta.type === ast.schemaTypes.ref ? void 0 : meta.description,
|
|
1328
|
+
examples: meta.examples
|
|
1329
|
+
});
|
|
1330
|
+
if (!cyclic) return `${objectKey(name)}: ${body}`;
|
|
1331
|
+
const returnType = recursiveGetterType({
|
|
1332
|
+
ctx,
|
|
1333
|
+
schema,
|
|
1334
|
+
required
|
|
1335
|
+
});
|
|
1336
|
+
return returnType ? `get ${objectKey(name)}(): ${returnType} { return ${body} }` : lazyGetter({
|
|
1337
|
+
name,
|
|
1338
|
+
body
|
|
1339
|
+
});
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Breaks getter inference cycles without widening recursive refs and arrays to `z.ZodType`.
|
|
1344
|
+
*/
|
|
1345
|
+
function recursiveGetterType({ ctx, schema, required }) {
|
|
1346
|
+
function baseType(schema) {
|
|
1347
|
+
if (ctx.options.nodes?.[schema.type]) return void 0;
|
|
1348
|
+
if (schema.type === ast.schemaTypes.ref) {
|
|
1349
|
+
const saved = ctx.options.cyclicSchemas;
|
|
1350
|
+
ctx.options.cyclicSchemas = void 0;
|
|
1351
|
+
const name = ctx.transform(schema);
|
|
1352
|
+
ctx.options.cyclicSchemas = saved;
|
|
1353
|
+
return name ? `typeof ${name}` : void 0;
|
|
1354
|
+
}
|
|
1355
|
+
if (schema.type === ast.schemaTypes.array && schema.items?.length === 1) {
|
|
1356
|
+
const item = baseType(schema.items[0]);
|
|
1357
|
+
return item ? `z.ZodArray<${item}>` : void 0;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
const base = baseType(schema);
|
|
1361
|
+
if (!base) return void 0;
|
|
1362
|
+
const meta = syncSchemaRef(schema);
|
|
1363
|
+
const optional = schema.optional || required === false;
|
|
1364
|
+
const wrapped = (() => {
|
|
1365
|
+
if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
|
|
1366
|
+
if (optional) return `z.ZodOptional<${base}>`;
|
|
1367
|
+
if (meta.nullable) return `z.ZodNullable<${base}>`;
|
|
1368
|
+
return base;
|
|
1369
|
+
})();
|
|
1370
|
+
return meta.default !== void 0 && defaultLiteral(schema, meta.default) !== null ? `z.ZodDefault<${wrapped}>` : wrapped;
|
|
1171
1371
|
}
|
|
1172
1372
|
/**
|
|
1173
1373
|
* Types the direction probe skips. They delegate to their children rather than reading
|
|
1174
1374
|
* `direction` themselves, so {@link containsDirectionalNode} walks into the children instead.
|
|
1175
1375
|
*/
|
|
1176
1376
|
const CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1377
|
+
ast.schemaTypes.object,
|
|
1378
|
+
ast.schemaTypes.array,
|
|
1379
|
+
ast.schemaTypes.tuple,
|
|
1380
|
+
ast.schemaTypes.union,
|
|
1381
|
+
ast.schemaTypes.intersection,
|
|
1382
|
+
ast.schemaTypes.ref
|
|
1183
1383
|
]);
|
|
1184
1384
|
/**
|
|
1185
1385
|
* Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
|
|
@@ -1211,7 +1411,7 @@ function variesByDirection({ node, printerOptions }) {
|
|
|
1211
1411
|
*/
|
|
1212
1412
|
function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */ new Set() }) {
|
|
1213
1413
|
if (!node) return false;
|
|
1214
|
-
if (node.type ===
|
|
1414
|
+
if (node.type === ast.schemaTypes.ref) {
|
|
1215
1415
|
if (!node.ref) return false;
|
|
1216
1416
|
const refName = extractRefName(node.ref);
|
|
1217
1417
|
if (refName) {
|
|
@@ -1219,7 +1419,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1219
1419
|
seen.add(refName);
|
|
1220
1420
|
}
|
|
1221
1421
|
const resolved = syncSchemaRef(node);
|
|
1222
|
-
if (resolved.type ===
|
|
1422
|
+
if (resolved.type === ast.schemaTypes.ref) return false;
|
|
1223
1423
|
return containsDirectionalNode({
|
|
1224
1424
|
node: resolved,
|
|
1225
1425
|
printerOptions,
|
|
@@ -1235,7 +1435,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1235
1435
|
if ("items" in node && node.items) children.push(...node.items);
|
|
1236
1436
|
if ("members" in node && node.members) children.push(...node.members);
|
|
1237
1437
|
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
1238
|
-
const propertyNames = node.type ===
|
|
1438
|
+
const propertyNames = node.type === ast.schemaTypes.object ? node.propertyNames : void 0;
|
|
1239
1439
|
if (propertyNames) children.push(propertyNames);
|
|
1240
1440
|
return children.some((child) => containsDirectionalNode({
|
|
1241
1441
|
node: child,
|
|
@@ -1247,7 +1447,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1247
1447
|
* Names of the `$ref` schemas the generator should route to their input (encode) variant.
|
|
1248
1448
|
*/
|
|
1249
1449
|
function collectDirectionalRefNames({ node, printerOptions }) {
|
|
1250
|
-
return ast.collectSync(node, { schema: (n) => n.type ===
|
|
1450
|
+
return ast.collectSync(node, { schema: (n) => n.type === ast.schemaTypes.ref && n.ref && containsDirectionalNode({
|
|
1251
1451
|
node: n,
|
|
1252
1452
|
printerOptions
|
|
1253
1453
|
}) ? ast.resolveRefName(n) ?? void 0 : void 0 });
|
|
@@ -1339,7 +1539,7 @@ const scalarNodes = {
|
|
|
1339
1539
|
enum(node) {
|
|
1340
1540
|
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1341
1541
|
if (node.namedEnumValues?.length) {
|
|
1342
|
-
const literals = nonNullValues.map((v) =>
|
|
1542
|
+
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1343
1543
|
if (literals.length === 1) return literals[0];
|
|
1344
1544
|
return `z.union([${literals.join(", ")}])`;
|
|
1345
1545
|
}
|
|
@@ -1360,6 +1560,7 @@ const scalarNodes = {
|
|
|
1360
1560
|
*/
|
|
1361
1561
|
const printerZod = ast.createPrinter((options) => {
|
|
1362
1562
|
const cyclicSchemaNames = options.cyclicSchemas;
|
|
1563
|
+
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1363
1564
|
return {
|
|
1364
1565
|
name: "zod",
|
|
1365
1566
|
options,
|
|
@@ -1385,37 +1586,40 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1385
1586
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1386
1587
|
const isLoose = node.additionalProperties === true;
|
|
1387
1588
|
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
|
-
|
|
1589
|
+
return objectConstraints({
|
|
1590
|
+
value: (() => {
|
|
1591
|
+
const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
|
|
1592
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
1593
|
+
const catchallType = this.transform(node.additionalProperties);
|
|
1594
|
+
if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
|
|
1595
|
+
return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
|
|
1596
|
+
}
|
|
1597
|
+
if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
|
|
1598
|
+
const unknownType = this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1599
|
+
return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
|
|
1600
|
+
}
|
|
1601
|
+
if (node.additionalProperties === true) return objectBase;
|
|
1602
|
+
if (isStrict) return objectBase;
|
|
1603
|
+
if (patterns.length > 0) {
|
|
1604
|
+
const values = patterns.map(([, valueSchema]) => {
|
|
1605
|
+
const valueType = this.transform(valueSchema) ?? this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1606
|
+
return valueSchema.nullable ? `${valueType}.nullable()` : valueType;
|
|
1607
|
+
});
|
|
1608
|
+
const distinct = [...new Set(values)];
|
|
1609
|
+
const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
|
|
1610
|
+
if (entries.length > 0) return `${objectBase}.catchall(${value})`;
|
|
1611
|
+
return `z.record(${patternKeySchema({
|
|
1612
|
+
patterns: patterns.map(([pattern]) => pattern),
|
|
1613
|
+
regexType: this.options.regexType
|
|
1614
|
+
})}, ${value})`;
|
|
1615
|
+
}
|
|
1616
|
+
return objectBase;
|
|
1617
|
+
})(),
|
|
1618
|
+
node
|
|
1619
|
+
});
|
|
1416
1620
|
},
|
|
1417
1621
|
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:
|
|
1622
|
+
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
1623
|
...node,
|
|
1420
1624
|
regexType: this.options.regexType
|
|
1421
1625
|
})}`;
|
|
@@ -1428,7 +1632,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1428
1632
|
const nodeMembers = node.members ?? [];
|
|
1429
1633
|
const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1430
1634
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1431
|
-
const isStrict = node.strategy === "one" && !isXor;
|
|
1635
|
+
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1432
1636
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ schema, output }) => output && isStrict ? strictOneOfMember(output, schema, cyclicSchemaNames) : output).filter(Boolean);
|
|
1433
1637
|
if (members.length === 0) return "";
|
|
1434
1638
|
if (members.length === 1) return members[0];
|
|
@@ -1439,11 +1643,21 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1439
1643
|
intersection(node) {
|
|
1440
1644
|
const members = node.members ?? [];
|
|
1441
1645
|
if (members.length === 0) return "";
|
|
1646
|
+
const hasSiblingProperties = members.some((member) => member.type === ast.schemaTypes.object && member.properties.length > 0);
|
|
1647
|
+
const transform = (member) => {
|
|
1648
|
+
if (!hasSiblingProperties) return this.transform(member);
|
|
1649
|
+
unionsWithSiblings.add(member);
|
|
1650
|
+
try {
|
|
1651
|
+
return this.transform(member);
|
|
1652
|
+
} finally {
|
|
1653
|
+
unionsWithSiblings.delete(member);
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1442
1656
|
const [first, ...rest] = members;
|
|
1443
1657
|
if (!first) return "";
|
|
1444
|
-
const firstBase =
|
|
1658
|
+
const firstBase = transform(first);
|
|
1445
1659
|
if (!firstBase) return "";
|
|
1446
|
-
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return
|
|
1660
|
+
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return preserveRequiredProperties(members).slice(1).reduce((acc, member) => `${acc}.extend(${buildZodObjectShape(this, member)})`, firstBase);
|
|
1447
1661
|
return rest.reduce((acc, member) => {
|
|
1448
1662
|
const constraint = getMemberConstraint({
|
|
1449
1663
|
member,
|
|
@@ -1452,7 +1666,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1452
1666
|
numbers: numberConstraints
|
|
1453
1667
|
});
|
|
1454
1668
|
if (constraint) return acc + constraint;
|
|
1455
|
-
const transformed =
|
|
1669
|
+
const transformed = transform(member);
|
|
1456
1670
|
return transformed ? `${acc}.and(${transformed})` : acc;
|
|
1457
1671
|
}, firstBase);
|
|
1458
1672
|
}
|
|
@@ -1472,20 +1686,25 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1472
1686
|
}
|
|
1473
1687
|
};
|
|
1474
1688
|
});
|
|
1475
|
-
|
|
1476
|
-
//#region src/printers/printerZodMini.ts
|
|
1689
|
+
// src/printers/printerZodMini.ts
|
|
1477
1690
|
/**
|
|
1478
1691
|
* Builds the `{ key: value, … }` shape for an object node with the functional `zod/mini` modifiers.
|
|
1479
1692
|
*/
|
|
1480
1693
|
function buildZodMiniObjectShape(ctx, node) {
|
|
1481
|
-
return buildObjectShape(ctx, node, ({ value, schema, required, meta }) =>
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1694
|
+
return buildObjectShape(ctx, node, ({ name, value, schema, required, meta, cyclic }) => {
|
|
1695
|
+
const body = applyMiniModifiers({
|
|
1696
|
+
value,
|
|
1697
|
+
schema,
|
|
1698
|
+
nullable: meta.nullable,
|
|
1699
|
+
optional: schema.optional || required === false,
|
|
1700
|
+
nullish: schema.nullish,
|
|
1701
|
+
defaultValue: meta.default
|
|
1702
|
+
});
|
|
1703
|
+
return cyclic ? lazyGetter({
|
|
1704
|
+
name,
|
|
1705
|
+
body
|
|
1706
|
+
}) : `${objectKey(name)}: ${body}`;
|
|
1707
|
+
});
|
|
1489
1708
|
}
|
|
1490
1709
|
/**
|
|
1491
1710
|
* Zod v4 Mini printer built with `definePrinter`.
|
|
@@ -1501,6 +1720,7 @@ function buildZodMiniObjectShape(ctx, node) {
|
|
|
1501
1720
|
*/
|
|
1502
1721
|
const printerZodMini = ast.createPrinter((options) => {
|
|
1503
1722
|
const cyclicSchemaNames = options.cyclicSchemas;
|
|
1723
|
+
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1504
1724
|
return {
|
|
1505
1725
|
name: "zod-mini",
|
|
1506
1726
|
options,
|
|
@@ -1573,7 +1793,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1573
1793
|
enum(node) {
|
|
1574
1794
|
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1575
1795
|
if (node.namedEnumValues?.length) {
|
|
1576
|
-
const literals = nonNullValues.map((v) =>
|
|
1796
|
+
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1577
1797
|
if (literals.length === 1) return literals[0];
|
|
1578
1798
|
return `z.union([${literals.join(", ")}])`;
|
|
1579
1799
|
}
|
|
@@ -1595,32 +1815,40 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1595
1815
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1596
1816
|
const isLoose = node.additionalProperties === true;
|
|
1597
1817
|
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
|
-
|
|
1818
|
+
return objectConstraints({
|
|
1819
|
+
value: (() => {
|
|
1820
|
+
const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
|
|
1821
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
1822
|
+
const catchallType = this.transform(node.additionalProperties);
|
|
1823
|
+
if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
|
|
1824
|
+
return catchallType ? `z.catchall(${objectBase}, ${catchallType})` : objectBase;
|
|
1825
|
+
}
|
|
1826
|
+
if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
|
|
1827
|
+
const unknownType = this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1828
|
+
return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
|
|
1829
|
+
}
|
|
1830
|
+
if (node.additionalProperties === true) return objectBase;
|
|
1831
|
+
if (isStrict) return objectBase;
|
|
1832
|
+
if (patterns.length > 0) {
|
|
1833
|
+
const values = patterns.map(([, valueSchema]) => {
|
|
1834
|
+
const valueType = this.transform(valueSchema) ?? this.transform(ast.factory.createSchema({ type: ast.schemaTypes.unknown }));
|
|
1835
|
+
return valueSchema.nullable ? `z.nullable(${valueType})` : valueType;
|
|
1836
|
+
});
|
|
1837
|
+
const distinct = [...new Set(values)];
|
|
1838
|
+
const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
|
|
1839
|
+
if (entries.length > 0) return `z.catchall(${objectBase}, ${value})`;
|
|
1840
|
+
return `z.record(${patternKeySchemaMini({
|
|
1841
|
+
patterns: patterns.map(([pattern]) => pattern),
|
|
1842
|
+
regexType: this.options.regexType
|
|
1843
|
+
})}, ${value})`;
|
|
1844
|
+
}
|
|
1845
|
+
return objectBase;
|
|
1846
|
+
})(),
|
|
1847
|
+
node
|
|
1848
|
+
});
|
|
1621
1849
|
},
|
|
1622
1850
|
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:
|
|
1851
|
+
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
1852
|
...node,
|
|
1625
1853
|
regexType: this.options.regexType
|
|
1626
1854
|
})}`;
|
|
@@ -1633,7 +1861,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1633
1861
|
const nodeMembers = node.members ?? [];
|
|
1634
1862
|
const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1635
1863
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1636
|
-
const isStrict = node.strategy === "one" && !isXor;
|
|
1864
|
+
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1637
1865
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ output }) => output).filter(Boolean);
|
|
1638
1866
|
if (members.length === 0) return "";
|
|
1639
1867
|
if (members.length === 1) return members[0];
|
|
@@ -1644,11 +1872,21 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1644
1872
|
intersection(node) {
|
|
1645
1873
|
const members = node.members ?? [];
|
|
1646
1874
|
if (members.length === 0) return "";
|
|
1875
|
+
const hasSiblingProperties = members.some((member) => member.type === ast.schemaTypes.object && member.properties.length > 0);
|
|
1876
|
+
const transform = (member) => {
|
|
1877
|
+
if (!hasSiblingProperties) return this.transform(member);
|
|
1878
|
+
unionsWithSiblings.add(member);
|
|
1879
|
+
try {
|
|
1880
|
+
return this.transform(member);
|
|
1881
|
+
} finally {
|
|
1882
|
+
unionsWithSiblings.delete(member);
|
|
1883
|
+
}
|
|
1884
|
+
};
|
|
1647
1885
|
const [first, ...rest] = members;
|
|
1648
1886
|
if (!first) return "";
|
|
1649
|
-
const firstBase =
|
|
1887
|
+
const firstBase = transform(first);
|
|
1650
1888
|
if (!firstBase) return "";
|
|
1651
|
-
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return
|
|
1889
|
+
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemaNames)) return preserveRequiredProperties(members).slice(1).reduce((acc, member) => `z.extend(${acc}, ${buildZodMiniObjectShape(this, member)})`, firstBase);
|
|
1652
1890
|
return rest.reduce((acc, member) => {
|
|
1653
1891
|
const constraint = getMemberConstraint({
|
|
1654
1892
|
member,
|
|
@@ -1657,7 +1895,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1657
1895
|
numbers: numberChecksMini
|
|
1658
1896
|
});
|
|
1659
1897
|
if (constraint) return acc + constraint;
|
|
1660
|
-
const transformed =
|
|
1898
|
+
const transformed = transform(member);
|
|
1661
1899
|
return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
|
|
1662
1900
|
}, firstBase);
|
|
1663
1901
|
}
|
|
@@ -1675,8 +1913,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1675
1913
|
}
|
|
1676
1914
|
};
|
|
1677
1915
|
});
|
|
1678
|
-
|
|
1679
|
-
//#region src/generators/zodGenerator.tsx
|
|
1916
|
+
// src/generators/zodGenerator.tsx
|
|
1680
1917
|
/**
|
|
1681
1918
|
* The helper an inferred alias is printed with. `'codec'` reads a request (encode) schema from its
|
|
1682
1919
|
* input and a response (decode) schema from its output; `true` keeps `z.infer` for both.
|
|
@@ -2137,17 +2374,14 @@ const zodGenerator = defineGenerator({
|
|
|
2137
2374
|
});
|
|
2138
2375
|
}
|
|
2139
2376
|
});
|
|
2140
|
-
|
|
2141
|
-
//#region kubb-extension.json
|
|
2377
|
+
// kubb-extension.json
|
|
2142
2378
|
var compatibility = {
|
|
2143
|
-
"kubb": ">=5.
|
|
2379
|
+
"kubb": ">=5.14.6",
|
|
2144
2380
|
"node": ">=22.22"
|
|
2145
2381
|
};
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
//#endregion
|
|
2150
|
-
//#region src/resolvers/resolverZod.ts
|
|
2382
|
+
// package.json
|
|
2383
|
+
var version = "5.7.3";
|
|
2384
|
+
// src/resolvers/resolverZod.ts
|
|
2151
2385
|
/**
|
|
2152
2386
|
* Default resolver used by `@kubb/plugin-zod`. Decides the names and file
|
|
2153
2387
|
* paths for every generated Zod schema. Schemas use camelCase with a
|
|
@@ -2193,15 +2427,22 @@ const resolverZod = createResolver({
|
|
|
2193
2427
|
response: {
|
|
2194
2428
|
...createOperationResponseResolver(),
|
|
2195
2429
|
error(node) {
|
|
2196
|
-
|
|
2430
|
+
const name = this.name(`${node.operationId} Error`);
|
|
2431
|
+
return this.resolveName({
|
|
2432
|
+
key: operationKey(node, "response", "error"),
|
|
2433
|
+
name
|
|
2434
|
+
});
|
|
2197
2435
|
},
|
|
2198
2436
|
options(node) {
|
|
2199
|
-
|
|
2437
|
+
const name = this.schema.type(this.name(`${node.operationId} Options`));
|
|
2438
|
+
return this.resolveName({
|
|
2439
|
+
key: operationKey(node, "response", "options"),
|
|
2440
|
+
name
|
|
2441
|
+
});
|
|
2200
2442
|
}
|
|
2201
2443
|
}
|
|
2202
2444
|
});
|
|
2203
|
-
|
|
2204
|
-
//#region src/plugin.ts
|
|
2445
|
+
// src/plugin.ts
|
|
2205
2446
|
/**
|
|
2206
2447
|
* Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
|
|
2207
2448
|
* cross-plugin dependency references.
|
|
@@ -2267,7 +2508,6 @@ const pluginZod = defineExtension({
|
|
|
2267
2508
|
};
|
|
2268
2509
|
}
|
|
2269
2510
|
});
|
|
2270
|
-
//#endregion
|
|
2271
2511
|
export { pluginZod as default, pluginZod, pluginZodName, printerZod, printerZodMini, resolverZod, zodGenerator };
|
|
2272
2512
|
|
|
2273
2513
|
//# sourceMappingURL=index.js.map
|