@kubb/plugin-zod 5.7.1 → 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 +429 -192
- 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 +429 -192
- 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
|
*/
|
|
@@ -809,9 +898,6 @@ function shouldCoerce(coercion, type) {
|
|
|
809
898
|
if (coercion === true) return true;
|
|
810
899
|
return !!coercion[type];
|
|
811
900
|
}
|
|
812
|
-
function propertyNamesOf(node) {
|
|
813
|
-
return "propertyNames" in node ? node.propertyNames : void 0;
|
|
814
|
-
}
|
|
815
901
|
/**
|
|
816
902
|
* Whether the schema node represents a closed or finite set of keys (e.g. enum, literal,
|
|
817
903
|
* or union of enums/literals).
|
|
@@ -832,30 +918,32 @@ function isClosedKeySchema(node) {
|
|
|
832
918
|
}
|
|
833
919
|
/**
|
|
834
920
|
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
835
|
-
* argument.
|
|
921
|
+
* argument. Property counts, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
|
|
836
922
|
* on `.and(…)`.
|
|
837
923
|
*/
|
|
838
924
|
function isPlainInlineObject(node) {
|
|
839
925
|
if (node.type !== "object") return false;
|
|
926
|
+
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
840
927
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
841
928
|
const hasAdditionalProperties = node.additionalProperties !== void 0;
|
|
842
929
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
843
|
-
return !hasAdditionalProperties && !hasPatternProperties && !
|
|
930
|
+
return !hasAdditionalProperties && !hasPatternProperties && !node.propertyNames;
|
|
844
931
|
}
|
|
845
932
|
/**
|
|
846
933
|
* Whether a node renders as a bare Zod object — one that accepts `.extend(…)` and can be a
|
|
847
934
|
* `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
|
|
848
935
|
* unresolved) objects, single-member unions of an object, and object-composable `allOf`.
|
|
849
936
|
*
|
|
850
|
-
*
|
|
937
|
+
* Property counts render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
|
|
851
938
|
*/
|
|
852
939
|
function isObjectSchemaNode(node, cyclicSchemas) {
|
|
853
940
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
854
941
|
if (node.type === "object") {
|
|
942
|
+
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
855
943
|
const hasProperties = Boolean(node.properties?.length);
|
|
856
944
|
const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
|
|
857
945
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
858
|
-
return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties ||
|
|
946
|
+
return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties || node.propertyNames));
|
|
859
947
|
}
|
|
860
948
|
if (node.type === "ref") {
|
|
861
949
|
const refName = ast.resolveRefName(node);
|
|
@@ -879,6 +967,49 @@ function isObjectComposableIntersection(node, cyclicSchemas) {
|
|
|
879
967
|
const [first, ...rest] = node.members ?? [];
|
|
880
968
|
return !!first && isObjectSchemaNode(first, cyclicSchemas) && rest.every(isPlainInlineObject);
|
|
881
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
|
+
}
|
|
882
1013
|
/**
|
|
883
1014
|
* Format a default value as a code-level literal.
|
|
884
1015
|
* Arrays and objects keep their contents as a JSON literal, primitives become their string
|
|
@@ -925,11 +1056,20 @@ function defaultLiteral(node, value) {
|
|
|
925
1056
|
*/
|
|
926
1057
|
function buildEnum(values) {
|
|
927
1058
|
if (values.every((v) => typeof v === "string")) return `z.enum([${values.map((v) => formatDefault(v)).join(", ")}])`;
|
|
928
|
-
const literals = values.map((
|
|
1059
|
+
const literals = values.map((value) => buildLiteral(value));
|
|
929
1060
|
if (literals.length === 1) return literals[0];
|
|
930
1061
|
return `z.union([${literals.join(", ")}])`;
|
|
931
1062
|
}
|
|
932
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
|
+
/**
|
|
933
1073
|
* Digit pattern for a `type: 'string'` schema that carries an integer `format`, the way ProtoJSON
|
|
934
1074
|
* encodes 64-bit integers. Returns `undefined` for every other format, and a `pattern` from the
|
|
935
1075
|
* spec takes precedence over this fallback.
|
|
@@ -1012,6 +1152,17 @@ function lengthChecksMini({ min, max, pattern, regexType }) {
|
|
|
1012
1152
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
1013
1153
|
}
|
|
1014
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
|
+
/**
|
|
1015
1166
|
* Apply nullable / optional / nullish modifiers, an optional `.describe()` call, and an
|
|
1016
1167
|
* optional `.meta({ examples })` call to a schema value string using the chainable Zod v4 API.
|
|
1017
1168
|
*/
|
|
@@ -1101,7 +1252,7 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
|
|
|
1101
1252
|
/**
|
|
1102
1253
|
* Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
|
|
1103
1254
|
*/
|
|
1104
|
-
function buildObjectShape(ctx, node,
|
|
1255
|
+
function buildObjectShape(ctx, node, renderProperty) {
|
|
1105
1256
|
const objectNode = ast.narrowSchema(node, "object");
|
|
1106
1257
|
if (!objectNode) return "{}";
|
|
1107
1258
|
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && containsCircularRef(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
@@ -1112,18 +1263,16 @@ function buildObjectShape(ctx, node, modify) {
|
|
|
1112
1263
|
const baseOutput = ctx.transform(schema) ?? ctx.transform(ast.factory.createSchema({ type: "unknown" }));
|
|
1113
1264
|
if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
|
|
1114
1265
|
return baseOutput;
|
|
1115
|
-
}).map(({ name
|
|
1266
|
+
}).map(({ name, property, output: value }) => {
|
|
1116
1267
|
const { schema } = property;
|
|
1117
|
-
|
|
1118
|
-
|
|
1268
|
+
return renderProperty({
|
|
1269
|
+
name,
|
|
1270
|
+
value,
|
|
1119
1271
|
schema,
|
|
1120
1272
|
required: property.required,
|
|
1121
|
-
meta: syncSchemaRef(schema)
|
|
1273
|
+
meta: syncSchemaRef(schema),
|
|
1274
|
+
cyclic: isCyclic(schema)
|
|
1122
1275
|
});
|
|
1123
|
-
return isCyclic(schema) ? lazyGetter({
|
|
1124
|
-
name: propName,
|
|
1125
|
-
body: value
|
|
1126
|
-
}) : `${objectKey(propName)}: ${value}`;
|
|
1127
1276
|
}));
|
|
1128
1277
|
}
|
|
1129
1278
|
/**
|
|
@@ -1138,22 +1287,28 @@ function printWithOmit(ctx, node, modify, mini = false) {
|
|
|
1138
1287
|
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
1139
1288
|
const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
|
|
1140
1289
|
const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
|
|
1141
|
-
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
|
+
};
|
|
1142
1298
|
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
|
|
1143
1299
|
if (lazyMatch) return `z.lazy(() => ${omit(lazyMatch[1])})`;
|
|
1144
1300
|
return omit(transformed);
|
|
1145
1301
|
})(), meta);
|
|
1146
1302
|
}
|
|
1147
|
-
|
|
1148
|
-
//#region src/printers/printerZod.ts
|
|
1303
|
+
// src/printers/printerZod.ts
|
|
1149
1304
|
function strictOneOfMember(member, node, cyclicSchemas) {
|
|
1150
|
-
if (node.type ===
|
|
1305
|
+
if (node.type === ast.schemaTypes.ref) {
|
|
1151
1306
|
if (member.startsWith("z.lazy(")) return member;
|
|
1152
1307
|
const refName = ast.resolveRefName(node);
|
|
1153
1308
|
if (refName && cyclicSchemas?.has(refName)) return member;
|
|
1154
1309
|
const schema = syncSchemaRef(node);
|
|
1155
1310
|
if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
|
|
1156
|
-
if (schema.type ===
|
|
1311
|
+
if (schema.type === ast.schemaTypes.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
|
|
1157
1312
|
}
|
|
1158
1313
|
return member;
|
|
1159
1314
|
}
|
|
@@ -1161,28 +1316,70 @@ function strictOneOfMember(member, node, cyclicSchemas) {
|
|
|
1161
1316
|
* Builds the `{ key: value, … }` shape for an object node with the chainable modifiers.
|
|
1162
1317
|
*/
|
|
1163
1318
|
function buildZodObjectShape(ctx, node) {
|
|
1164
|
-
return buildObjectShape(ctx, node, ({ value, schema, required, meta }) =>
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
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;
|
|
1174
1371
|
}
|
|
1175
1372
|
/**
|
|
1176
1373
|
* Types the direction probe skips. They delegate to their children rather than reading
|
|
1177
1374
|
* `direction` themselves, so {@link containsDirectionalNode} walks into the children instead.
|
|
1178
1375
|
*/
|
|
1179
1376
|
const CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1377
|
+
ast.schemaTypes.object,
|
|
1378
|
+
ast.schemaTypes.array,
|
|
1379
|
+
ast.schemaTypes.tuple,
|
|
1380
|
+
ast.schemaTypes.union,
|
|
1381
|
+
ast.schemaTypes.intersection,
|
|
1382
|
+
ast.schemaTypes.ref
|
|
1186
1383
|
]);
|
|
1187
1384
|
/**
|
|
1188
1385
|
* Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
|
|
@@ -1214,7 +1411,7 @@ function variesByDirection({ node, printerOptions }) {
|
|
|
1214
1411
|
*/
|
|
1215
1412
|
function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */ new Set() }) {
|
|
1216
1413
|
if (!node) return false;
|
|
1217
|
-
if (node.type ===
|
|
1414
|
+
if (node.type === ast.schemaTypes.ref) {
|
|
1218
1415
|
if (!node.ref) return false;
|
|
1219
1416
|
const refName = extractRefName(node.ref);
|
|
1220
1417
|
if (refName) {
|
|
@@ -1222,7 +1419,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1222
1419
|
seen.add(refName);
|
|
1223
1420
|
}
|
|
1224
1421
|
const resolved = syncSchemaRef(node);
|
|
1225
|
-
if (resolved.type ===
|
|
1422
|
+
if (resolved.type === ast.schemaTypes.ref) return false;
|
|
1226
1423
|
return containsDirectionalNode({
|
|
1227
1424
|
node: resolved,
|
|
1228
1425
|
printerOptions,
|
|
@@ -1238,7 +1435,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1238
1435
|
if ("items" in node && node.items) children.push(...node.items);
|
|
1239
1436
|
if ("members" in node && node.members) children.push(...node.members);
|
|
1240
1437
|
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
1241
|
-
const propertyNames =
|
|
1438
|
+
const propertyNames = node.type === ast.schemaTypes.object ? node.propertyNames : void 0;
|
|
1242
1439
|
if (propertyNames) children.push(propertyNames);
|
|
1243
1440
|
return children.some((child) => containsDirectionalNode({
|
|
1244
1441
|
node: child,
|
|
@@ -1250,7 +1447,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
|
|
|
1250
1447
|
* Names of the `$ref` schemas the generator should route to their input (encode) variant.
|
|
1251
1448
|
*/
|
|
1252
1449
|
function collectDirectionalRefNames({ node, printerOptions }) {
|
|
1253
|
-
return ast.collectSync(node, { schema: (n) => n.type ===
|
|
1450
|
+
return ast.collectSync(node, { schema: (n) => n.type === ast.schemaTypes.ref && n.ref && containsDirectionalNode({
|
|
1254
1451
|
node: n,
|
|
1255
1452
|
printerOptions
|
|
1256
1453
|
}) ? ast.resolveRefName(n) ?? void 0 : void 0 });
|
|
@@ -1342,7 +1539,7 @@ const scalarNodes = {
|
|
|
1342
1539
|
enum(node) {
|
|
1343
1540
|
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1344
1541
|
if (node.namedEnumValues?.length) {
|
|
1345
|
-
const literals = nonNullValues.map((v) =>
|
|
1542
|
+
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1346
1543
|
if (literals.length === 1) return literals[0];
|
|
1347
1544
|
return `z.union([${literals.join(", ")}])`;
|
|
1348
1545
|
}
|
|
@@ -1363,6 +1560,7 @@ const scalarNodes = {
|
|
|
1363
1560
|
*/
|
|
1364
1561
|
const printerZod = ast.createPrinter((options) => {
|
|
1365
1562
|
const cyclicSchemaNames = options.cyclicSchemas;
|
|
1563
|
+
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1366
1564
|
return {
|
|
1367
1565
|
name: "zod",
|
|
1368
1566
|
options,
|
|
@@ -1383,42 +1581,45 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1383
1581
|
object(node) {
|
|
1384
1582
|
const entries = node.properties ?? [];
|
|
1385
1583
|
const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
|
|
1386
|
-
const propertyNamesNode =
|
|
1584
|
+
const propertyNamesNode = node.propertyNames;
|
|
1387
1585
|
const propertyNamesKeySchema = propertyNamesNode ? this.transform(propertyNamesNode) : null;
|
|
1388
1586
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1389
1587
|
const isLoose = node.additionalProperties === true;
|
|
1390
1588
|
const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodObjectShape(this, node)})`;
|
|
1391
|
-
return (
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
const
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
+
});
|
|
1419
1620
|
},
|
|
1420
1621
|
array(node) {
|
|
1421
|
-
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({
|
|
1422
1623
|
...node,
|
|
1423
1624
|
regexType: this.options.regexType
|
|
1424
1625
|
})}`;
|
|
@@ -1431,7 +1632,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1431
1632
|
const nodeMembers = node.members ?? [];
|
|
1432
1633
|
const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1433
1634
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1434
|
-
const isStrict = node.strategy === "one" && !isXor;
|
|
1635
|
+
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1435
1636
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ schema, output }) => output && isStrict ? strictOneOfMember(output, schema, cyclicSchemaNames) : output).filter(Boolean);
|
|
1436
1637
|
if (members.length === 0) return "";
|
|
1437
1638
|
if (members.length === 1) return members[0];
|
|
@@ -1442,11 +1643,21 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1442
1643
|
intersection(node) {
|
|
1443
1644
|
const members = node.members ?? [];
|
|
1444
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
|
+
};
|
|
1445
1656
|
const [first, ...rest] = members;
|
|
1446
1657
|
if (!first) return "";
|
|
1447
|
-
const firstBase =
|
|
1658
|
+
const firstBase = transform(first);
|
|
1448
1659
|
if (!firstBase) return "";
|
|
1449
|
-
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);
|
|
1450
1661
|
return rest.reduce((acc, member) => {
|
|
1451
1662
|
const constraint = getMemberConstraint({
|
|
1452
1663
|
member,
|
|
@@ -1455,7 +1666,7 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1455
1666
|
numbers: numberConstraints
|
|
1456
1667
|
});
|
|
1457
1668
|
if (constraint) return acc + constraint;
|
|
1458
|
-
const transformed =
|
|
1669
|
+
const transformed = transform(member);
|
|
1459
1670
|
return transformed ? `${acc}.and(${transformed})` : acc;
|
|
1460
1671
|
}, firstBase);
|
|
1461
1672
|
}
|
|
@@ -1475,20 +1686,25 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1475
1686
|
}
|
|
1476
1687
|
};
|
|
1477
1688
|
});
|
|
1478
|
-
|
|
1479
|
-
//#region src/printers/printerZodMini.ts
|
|
1689
|
+
// src/printers/printerZodMini.ts
|
|
1480
1690
|
/**
|
|
1481
1691
|
* Builds the `{ key: value, … }` shape for an object node with the functional `zod/mini` modifiers.
|
|
1482
1692
|
*/
|
|
1483
1693
|
function buildZodMiniObjectShape(ctx, node) {
|
|
1484
|
-
return buildObjectShape(ctx, node, ({ value, schema, required, meta }) =>
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
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
|
+
});
|
|
1492
1708
|
}
|
|
1493
1709
|
/**
|
|
1494
1710
|
* Zod v4 Mini printer built with `definePrinter`.
|
|
@@ -1504,6 +1720,7 @@ function buildZodMiniObjectShape(ctx, node) {
|
|
|
1504
1720
|
*/
|
|
1505
1721
|
const printerZodMini = ast.createPrinter((options) => {
|
|
1506
1722
|
const cyclicSchemaNames = options.cyclicSchemas;
|
|
1723
|
+
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1507
1724
|
return {
|
|
1508
1725
|
name: "zod-mini",
|
|
1509
1726
|
options,
|
|
@@ -1576,7 +1793,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1576
1793
|
enum(node) {
|
|
1577
1794
|
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1578
1795
|
if (node.namedEnumValues?.length) {
|
|
1579
|
-
const literals = nonNullValues.map((v) =>
|
|
1796
|
+
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1580
1797
|
if (literals.length === 1) return literals[0];
|
|
1581
1798
|
return `z.union([${literals.join(", ")}])`;
|
|
1582
1799
|
}
|
|
@@ -1593,37 +1810,45 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1593
1810
|
object(node) {
|
|
1594
1811
|
const entries = node.properties ?? [];
|
|
1595
1812
|
const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
|
|
1596
|
-
const propertyNamesNode =
|
|
1813
|
+
const propertyNamesNode = node.propertyNames;
|
|
1597
1814
|
const propertyNamesKeySchema = propertyNamesNode ? this.transform(propertyNamesNode) : null;
|
|
1598
1815
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1599
1816
|
const isLoose = node.additionalProperties === true;
|
|
1600
1817
|
const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodMiniObjectShape(this, node)})`;
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
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
|
+
});
|
|
1624
1849
|
},
|
|
1625
1850
|
array(node) {
|
|
1626
|
-
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({
|
|
1627
1852
|
...node,
|
|
1628
1853
|
regexType: this.options.regexType
|
|
1629
1854
|
})}`;
|
|
@@ -1636,7 +1861,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1636
1861
|
const nodeMembers = node.members ?? [];
|
|
1637
1862
|
const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
1638
1863
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1639
|
-
const isStrict = node.strategy === "one" && !isXor;
|
|
1864
|
+
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1640
1865
|
const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ output }) => output).filter(Boolean);
|
|
1641
1866
|
if (members.length === 0) return "";
|
|
1642
1867
|
if (members.length === 1) return members[0];
|
|
@@ -1647,11 +1872,21 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1647
1872
|
intersection(node) {
|
|
1648
1873
|
const members = node.members ?? [];
|
|
1649
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
|
+
};
|
|
1650
1885
|
const [first, ...rest] = members;
|
|
1651
1886
|
if (!first) return "";
|
|
1652
|
-
const firstBase =
|
|
1887
|
+
const firstBase = transform(first);
|
|
1653
1888
|
if (!firstBase) return "";
|
|
1654
|
-
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);
|
|
1655
1890
|
return rest.reduce((acc, member) => {
|
|
1656
1891
|
const constraint = getMemberConstraint({
|
|
1657
1892
|
member,
|
|
@@ -1660,7 +1895,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1660
1895
|
numbers: numberChecksMini
|
|
1661
1896
|
});
|
|
1662
1897
|
if (constraint) return acc + constraint;
|
|
1663
|
-
const transformed =
|
|
1898
|
+
const transformed = transform(member);
|
|
1664
1899
|
return transformed ? `z.intersection(${acc}, ${transformed})` : acc;
|
|
1665
1900
|
}, firstBase);
|
|
1666
1901
|
}
|
|
@@ -1678,8 +1913,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1678
1913
|
}
|
|
1679
1914
|
};
|
|
1680
1915
|
});
|
|
1681
|
-
|
|
1682
|
-
//#region src/generators/zodGenerator.tsx
|
|
1916
|
+
// src/generators/zodGenerator.tsx
|
|
1683
1917
|
/**
|
|
1684
1918
|
* The helper an inferred alias is printed with. `'codec'` reads a request (encode) schema from its
|
|
1685
1919
|
* input and a response (decode) schema from its output; `true` keeps `z.infer` for both.
|
|
@@ -2140,17 +2374,14 @@ const zodGenerator = defineGenerator({
|
|
|
2140
2374
|
});
|
|
2141
2375
|
}
|
|
2142
2376
|
});
|
|
2143
|
-
|
|
2144
|
-
//#region kubb-extension.json
|
|
2377
|
+
// kubb-extension.json
|
|
2145
2378
|
var compatibility = {
|
|
2146
|
-
"kubb": ">=5.
|
|
2379
|
+
"kubb": ">=5.14.6",
|
|
2147
2380
|
"node": ">=22.22"
|
|
2148
2381
|
};
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
//#endregion
|
|
2153
|
-
//#region src/resolvers/resolverZod.ts
|
|
2382
|
+
// package.json
|
|
2383
|
+
var version = "5.7.3";
|
|
2384
|
+
// src/resolvers/resolverZod.ts
|
|
2154
2385
|
/**
|
|
2155
2386
|
* Default resolver used by `@kubb/plugin-zod`. Decides the names and file
|
|
2156
2387
|
* paths for every generated Zod schema. Schemas use camelCase with a
|
|
@@ -2196,15 +2427,22 @@ const resolverZod = createResolver({
|
|
|
2196
2427
|
response: {
|
|
2197
2428
|
...createOperationResponseResolver(),
|
|
2198
2429
|
error(node) {
|
|
2199
|
-
|
|
2430
|
+
const name = this.name(`${node.operationId} Error`);
|
|
2431
|
+
return this.resolveName({
|
|
2432
|
+
key: operationKey(node, "response", "error"),
|
|
2433
|
+
name
|
|
2434
|
+
});
|
|
2200
2435
|
},
|
|
2201
2436
|
options(node) {
|
|
2202
|
-
|
|
2437
|
+
const name = this.schema.type(this.name(`${node.operationId} Options`));
|
|
2438
|
+
return this.resolveName({
|
|
2439
|
+
key: operationKey(node, "response", "options"),
|
|
2440
|
+
name
|
|
2441
|
+
});
|
|
2203
2442
|
}
|
|
2204
2443
|
}
|
|
2205
2444
|
});
|
|
2206
|
-
|
|
2207
|
-
//#region src/plugin.ts
|
|
2445
|
+
// src/plugin.ts
|
|
2208
2446
|
/**
|
|
2209
2447
|
* Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
|
|
2210
2448
|
* cross-plugin dependency references.
|
|
@@ -2270,7 +2508,6 @@ const pluginZod = defineExtension({
|
|
|
2270
2508
|
};
|
|
2271
2509
|
}
|
|
2272
2510
|
});
|
|
2273
|
-
//#endregion
|
|
2274
2511
|
export { pluginZod as default, pluginZod, pluginZodName, printerZod, printerZodMini, resolverZod, zodGenerator };
|
|
2275
2512
|
|
|
2276
2513
|
//# sourceMappingURL=index.js.map
|