@kubb/plugin-zod 5.1.0 → 5.1.2
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 +228 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +18 -6
- package/dist/index.js +228 -168
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -73,14 +73,18 @@ type PrinterZodOptions = {
|
|
|
73
73
|
*/
|
|
74
74
|
cyclicSchemas?: ReadonlySet<string>;
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
* - `'
|
|
78
|
-
* - `'
|
|
76
|
+
* Which way a node converts between its wire type and its runtime type:
|
|
77
|
+
* - `'decode'` (default): wire into runtime, used by response schemas.
|
|
78
|
+
* - `'encode'`: runtime back to wire, used by request bodies and parameters.
|
|
79
79
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
80
|
+
* Named for the conversion rather than the slot, since Zod's own `z.input` and `z.output`
|
|
81
|
+
* describe a different axis and read inverted here: the `'decode'` schema is the one whose
|
|
82
|
+
* `z.input` is the wire type.
|
|
83
|
+
*
|
|
84
|
+
* A handler returning different output per direction makes the generator emit an
|
|
85
|
+
* `${name}InputSchema` variant for that component.
|
|
82
86
|
*/
|
|
83
|
-
direction?: '
|
|
87
|
+
direction?: 'encode' | 'decode';
|
|
84
88
|
/**
|
|
85
89
|
* Custom handler map for node type overrides.
|
|
86
90
|
*/
|
|
@@ -351,6 +355,9 @@ type Options = OutputOptions & {
|
|
|
351
355
|
* validate with `z.coerce.date()` instead of the string-to-Date codec. Fields
|
|
352
356
|
* kept as ISO strings (`z.iso.date()`, `z.iso.datetime()`) are never coerced.
|
|
353
357
|
*
|
|
358
|
+
* `bigint` fields (`format: int64`) always coerce, regardless of this option:
|
|
359
|
+
* `JSON.parse` hands back a `number`, which a plain `z.bigint()` rejects.
|
|
360
|
+
*
|
|
354
361
|
* @default false
|
|
355
362
|
* @see https://zod.dev/?id=coercion-for-primitives
|
|
356
363
|
*/
|
|
@@ -391,6 +398,11 @@ type Options = OutputOptions & {
|
|
|
391
398
|
/**
|
|
392
399
|
* Replace the Zod handler for a specific schema type (`'integer'`, `'date'`, ...).
|
|
393
400
|
* When `mini: true`, overrides target the Zod Mini printer instead.
|
|
401
|
+
*
|
|
402
|
+
* A handler that returns a different expression per `this.options.direction` declares a two-way
|
|
403
|
+
* conversion, such as a `time` field carried as an ISO string but modeled as a
|
|
404
|
+
* `Temporal.PlainTime`. The generator then emits an `${name}InputSchema` variant for request
|
|
405
|
+
* bodies to resolve to, `$ref` included. Ignore `direction` and only the printed output changes.
|
|
394
406
|
*/
|
|
395
407
|
printer?: {
|
|
396
408
|
nodes?: PrinterZodNodes | PrinterZodMiniNodes;
|
package/dist/index.js
CHANGED
|
@@ -779,68 +779,6 @@ function shouldCoerce(coercion, type) {
|
|
|
779
779
|
return !!coercion[type];
|
|
780
780
|
}
|
|
781
781
|
/**
|
|
782
|
-
* Registered codecs, checked in order.
|
|
783
|
-
*/
|
|
784
|
-
const codecs = [{
|
|
785
|
-
matches(node) {
|
|
786
|
-
return node.type === "date" && node.representation === "date";
|
|
787
|
-
},
|
|
788
|
-
decode(node) {
|
|
789
|
-
return node.format === "date" ? "z.iso.date().transform((value) => new Date(value))" : "z.iso.datetime().transform((value) => new Date(value))";
|
|
790
|
-
},
|
|
791
|
-
encode(node) {
|
|
792
|
-
return node.format === "date" ? "z.date().transform((value) => value.toISOString().slice(0, 10))" : "z.date().transform((value) => value.toISOString())";
|
|
793
|
-
}
|
|
794
|
-
}];
|
|
795
|
-
/**
|
|
796
|
-
* Returns the codec for this node, or `undefined` when the node needs no
|
|
797
|
-
* encode/decode (its wire and runtime types match).
|
|
798
|
-
*/
|
|
799
|
-
function getCodec(node) {
|
|
800
|
-
if (!node) return void 0;
|
|
801
|
-
return codecs.find((codec) => codec.matches(node));
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* Returns `true` when the node itself is encoded/decoded by a codec.
|
|
805
|
-
*/
|
|
806
|
-
function hasCodec(node) {
|
|
807
|
-
return getCodec(node) !== void 0;
|
|
808
|
-
}
|
|
809
|
-
/**
|
|
810
|
-
* Returns `true` when the schema transitively contains a codec node —
|
|
811
|
-
* a value whose runtime type differs from its wire type (see {@link hasCodec}),
|
|
812
|
-
* so it must be decoded (response) or encoded (request) at the validation boundary.
|
|
813
|
-
* `$ref`s are followed via their resolved schema; a `seen` set guards cycles.
|
|
814
|
-
*/
|
|
815
|
-
function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
|
|
816
|
-
if (!node) return false;
|
|
817
|
-
if (hasCodec(node)) return true;
|
|
818
|
-
if (node.type === "ref") {
|
|
819
|
-
if (!node.ref) return false;
|
|
820
|
-
const refName = extractRefName(node.ref);
|
|
821
|
-
if (refName) {
|
|
822
|
-
if (seen.has(refName)) return false;
|
|
823
|
-
seen.add(refName);
|
|
824
|
-
}
|
|
825
|
-
const resolved = syncSchemaRef(node);
|
|
826
|
-
if (resolved.type === "ref") return false;
|
|
827
|
-
return containsCodec(resolved, seen);
|
|
828
|
-
}
|
|
829
|
-
const children = [];
|
|
830
|
-
if ("properties" in node && node.properties) children.push(...node.properties.map((prop) => prop.schema));
|
|
831
|
-
if ("items" in node && node.items) children.push(...node.items);
|
|
832
|
-
if ("members" in node && node.members) children.push(...node.members);
|
|
833
|
-
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
834
|
-
return children.some((child) => containsCodec(child, seen));
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Collects the names of `$ref` schemas that transitively contain a codec, so the generator can route
|
|
838
|
-
* them to their input (encode) variant.
|
|
839
|
-
*/
|
|
840
|
-
function collectCodecRefNames(node) {
|
|
841
|
-
return ast.collectSync(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? ast.resolveRefName(n) ?? void 0 : void 0 });
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
782
|
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
845
783
|
* argument. A catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
|
|
846
784
|
* on `.and(…)`.
|
|
@@ -882,10 +820,12 @@ function isObjectComposableIntersection(node, cyclicSchemas) {
|
|
|
882
820
|
}
|
|
883
821
|
/**
|
|
884
822
|
* Format a default value as a code-level literal.
|
|
885
|
-
*
|
|
823
|
+
* Arrays keep their contents, other objects become `{}`, primitives become their string
|
|
824
|
+
* representation, strings are quoted.
|
|
886
825
|
*/
|
|
887
826
|
function formatDefault(value) {
|
|
888
827
|
if (typeof value === "string") return stringify(value);
|
|
828
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
889
829
|
if (typeof value === "object" && value !== null) return "{}";
|
|
890
830
|
return String(value ?? "");
|
|
891
831
|
}
|
|
@@ -900,13 +840,14 @@ function formatDefault(value) {
|
|
|
900
840
|
*/
|
|
901
841
|
function defaultLiteral(node, value) {
|
|
902
842
|
if (value === null) return null;
|
|
903
|
-
|
|
843
|
+
const resolved = node ? syncSchemaRef(node) : void 0;
|
|
844
|
+
if (resolved && ast.narrowSchema(resolved, "bigint")) {
|
|
904
845
|
if (typeof value === "bigint") return `BigInt(${value})`;
|
|
905
846
|
if (typeof value === "number" && Number.isInteger(value)) return `BigInt(${value})`;
|
|
906
847
|
return null;
|
|
907
848
|
}
|
|
908
|
-
if (
|
|
909
|
-
const enumNode =
|
|
849
|
+
if (resolved && ast.narrowSchema(resolved, "array")) return Array.isArray(value) ? JSON.stringify(value) : null;
|
|
850
|
+
const enumNode = resolved ? ast.narrowSchema(resolved, "enum") : void 0;
|
|
910
851
|
if (enumNode) {
|
|
911
852
|
const values = enumNode.namedEnumValues?.map((member) => member.value) ?? enumNode.enumValues ?? [];
|
|
912
853
|
if (values.length) {
|
|
@@ -1153,6 +1094,168 @@ function buildZodObjectShape(ctx, node) {
|
|
|
1153
1094
|
}));
|
|
1154
1095
|
}
|
|
1155
1096
|
/**
|
|
1097
|
+
* Types the direction probe skips. They delegate to their children rather than reading
|
|
1098
|
+
* `direction` themselves, so {@link containsDirectionalNode} walks into the children instead.
|
|
1099
|
+
*/
|
|
1100
|
+
const CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
1101
|
+
"object",
|
|
1102
|
+
"array",
|
|
1103
|
+
"tuple",
|
|
1104
|
+
"union",
|
|
1105
|
+
"intersection",
|
|
1106
|
+
"ref"
|
|
1107
|
+
]);
|
|
1108
|
+
/**
|
|
1109
|
+
* Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
|
|
1110
|
+
* direction and reports whether the two disagree. That difference is what makes a component
|
|
1111
|
+
* need an `${name}InputSchema` variant.
|
|
1112
|
+
*/
|
|
1113
|
+
function variesByDirection({ node, printerOptions }) {
|
|
1114
|
+
if (CONTAINER_TYPES.has(node.type)) return false;
|
|
1115
|
+
const handler = printerOptions.nodes?.[node.type] ?? scalarNodes[node.type];
|
|
1116
|
+
if (!handler) return false;
|
|
1117
|
+
const call = (direction) => {
|
|
1118
|
+
const context = {
|
|
1119
|
+
options: {
|
|
1120
|
+
...printerOptions,
|
|
1121
|
+
direction
|
|
1122
|
+
},
|
|
1123
|
+
transform: () => null,
|
|
1124
|
+
base: () => null
|
|
1125
|
+
};
|
|
1126
|
+
return handler.call(context, node);
|
|
1127
|
+
};
|
|
1128
|
+
return call("decode") !== call("encode");
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Whether the schema transitively contains a node that prints differently per direction, so it
|
|
1132
|
+
* must decode on responses and encode on requests. Follows `$ref`s through their resolved
|
|
1133
|
+
* schema, with `seen` guarding cycles.
|
|
1134
|
+
*/
|
|
1135
|
+
function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */ new Set() }) {
|
|
1136
|
+
if (!node) return false;
|
|
1137
|
+
if (node.type === "ref") {
|
|
1138
|
+
if (!node.ref) return false;
|
|
1139
|
+
const refName = extractRefName(node.ref);
|
|
1140
|
+
if (refName) {
|
|
1141
|
+
if (seen.has(refName)) return false;
|
|
1142
|
+
seen.add(refName);
|
|
1143
|
+
}
|
|
1144
|
+
const resolved = syncSchemaRef(node);
|
|
1145
|
+
if (resolved.type === "ref") return false;
|
|
1146
|
+
return containsDirectionalNode({
|
|
1147
|
+
node: resolved,
|
|
1148
|
+
printerOptions,
|
|
1149
|
+
seen
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
if (variesByDirection({
|
|
1153
|
+
node,
|
|
1154
|
+
printerOptions
|
|
1155
|
+
})) return true;
|
|
1156
|
+
const children = [];
|
|
1157
|
+
if ("properties" in node && node.properties) children.push(...node.properties.map((prop) => prop.schema));
|
|
1158
|
+
if ("items" in node && node.items) children.push(...node.items);
|
|
1159
|
+
if ("members" in node && node.members) children.push(...node.members);
|
|
1160
|
+
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
1161
|
+
return children.some((child) => containsDirectionalNode({
|
|
1162
|
+
node: child,
|
|
1163
|
+
printerOptions,
|
|
1164
|
+
seen
|
|
1165
|
+
}));
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Names of the `$ref` schemas the generator should route to their input (encode) variant.
|
|
1169
|
+
*/
|
|
1170
|
+
function collectDirectionalRefNames({ node, printerOptions }) {
|
|
1171
|
+
return ast.collectSync(node, { schema: (n) => n.type === "ref" && n.ref && containsDirectionalNode({
|
|
1172
|
+
node: n,
|
|
1173
|
+
printerOptions
|
|
1174
|
+
}) ? ast.resolveRefName(n) ?? void 0 : void 0 });
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Handlers that never recurse into children, so {@link variesByDirection} can call one directly
|
|
1178
|
+
* to probe both directions without building a printer.
|
|
1179
|
+
*
|
|
1180
|
+
* `date` is the built-in two-way conversion, decoding `string → Date` on responses and encoding
|
|
1181
|
+
* back on requests, keeping `date` and `date-time` precision apart. Only `representation: 'date'`
|
|
1182
|
+
* fields convert; ISO-string fields print `z.iso.date()` either way. A `printer.nodes.date`
|
|
1183
|
+
* override replaces the whole handler, direction branch included.
|
|
1184
|
+
*/
|
|
1185
|
+
const scalarNodes = {
|
|
1186
|
+
any: () => "z.any()",
|
|
1187
|
+
unknown: () => "z.unknown()",
|
|
1188
|
+
void: () => "z.void()",
|
|
1189
|
+
never: () => "z.never()",
|
|
1190
|
+
boolean: () => "z.boolean()",
|
|
1191
|
+
null: () => "z.null()",
|
|
1192
|
+
string(node) {
|
|
1193
|
+
const base = shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()";
|
|
1194
|
+
const pattern = node.pattern ?? integerFormatPattern(node.format);
|
|
1195
|
+
return `${base}${lengthConstraints({
|
|
1196
|
+
...node,
|
|
1197
|
+
pattern,
|
|
1198
|
+
regexType: this.options.regexType
|
|
1199
|
+
})}`;
|
|
1200
|
+
},
|
|
1201
|
+
number(node) {
|
|
1202
|
+
return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${numberConstraints(node)}`;
|
|
1203
|
+
},
|
|
1204
|
+
integer(node) {
|
|
1205
|
+
return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number().int()" : "z.int()"}${numberConstraints(node)}`;
|
|
1206
|
+
},
|
|
1207
|
+
bigint() {
|
|
1208
|
+
return "z.coerce.bigint()";
|
|
1209
|
+
},
|
|
1210
|
+
date(node) {
|
|
1211
|
+
if (node.representation !== "date") return "z.iso.date()";
|
|
1212
|
+
if (this.options.direction === "encode") return node.format === "date" ? "z.date().transform((value) => value.toISOString().slice(0, 10))" : "z.date().transform((value) => value.toISOString())";
|
|
1213
|
+
const decoded = node.format === "date" ? "z.iso.date().transform((value) => new Date(value))" : "z.iso.datetime().transform((value) => new Date(value))";
|
|
1214
|
+
return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : decoded;
|
|
1215
|
+
},
|
|
1216
|
+
datetime(node) {
|
|
1217
|
+
const offset = node.offset || this.options.dateType === "stringOffset";
|
|
1218
|
+
const local = node.local || this.options.dateType === "stringLocal";
|
|
1219
|
+
if (offset) return "z.iso.datetime({ offset: true })";
|
|
1220
|
+
if (local) return "z.iso.datetime({ local: true })";
|
|
1221
|
+
return "z.iso.datetime()";
|
|
1222
|
+
},
|
|
1223
|
+
time(node) {
|
|
1224
|
+
if (node.representation === "string") return "z.iso.time()";
|
|
1225
|
+
return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : "z.date()";
|
|
1226
|
+
},
|
|
1227
|
+
uuid(node) {
|
|
1228
|
+
return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthConstraints({
|
|
1229
|
+
...node,
|
|
1230
|
+
regexType: this.options.regexType
|
|
1231
|
+
})}`;
|
|
1232
|
+
},
|
|
1233
|
+
email(node) {
|
|
1234
|
+
return `z.email()${lengthConstraints({
|
|
1235
|
+
...node,
|
|
1236
|
+
regexType: this.options.regexType
|
|
1237
|
+
})}`;
|
|
1238
|
+
},
|
|
1239
|
+
url(node) {
|
|
1240
|
+
return `z.url()${lengthConstraints({
|
|
1241
|
+
...node,
|
|
1242
|
+
regexType: this.options.regexType
|
|
1243
|
+
})}`;
|
|
1244
|
+
},
|
|
1245
|
+
ipv4: () => "z.ipv4()",
|
|
1246
|
+
ipv6: () => "z.ipv6()",
|
|
1247
|
+
blob: () => "z.instanceof(File)",
|
|
1248
|
+
enum(node) {
|
|
1249
|
+
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1250
|
+
if (node.namedEnumValues?.length) {
|
|
1251
|
+
const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`);
|
|
1252
|
+
if (literals.length === 1) return literals[0];
|
|
1253
|
+
return `z.union([${literals.join(", ")}])`;
|
|
1254
|
+
}
|
|
1255
|
+
return buildEnum(nonNullValues);
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
/**
|
|
1156
1259
|
* Zod v4 printer built with `definePrinter`.
|
|
1157
1260
|
*
|
|
1158
1261
|
* Converts a `SchemaNode` AST into a Zod v4 code string using the chainable API
|
|
@@ -1170,84 +1273,15 @@ const printerZod = ast.createPrinter((options) => {
|
|
|
1170
1273
|
name: "zod",
|
|
1171
1274
|
options,
|
|
1172
1275
|
nodes: {
|
|
1173
|
-
|
|
1174
|
-
unknown: () => "z.unknown()",
|
|
1175
|
-
void: () => "z.void()",
|
|
1176
|
-
never: () => "z.never()",
|
|
1177
|
-
boolean: () => "z.boolean()",
|
|
1178
|
-
null: () => "z.null()",
|
|
1179
|
-
string(node) {
|
|
1180
|
-
const base = shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()";
|
|
1181
|
-
const pattern = node.pattern ?? integerFormatPattern(node.format);
|
|
1182
|
-
return `${base}${lengthConstraints({
|
|
1183
|
-
...node,
|
|
1184
|
-
pattern,
|
|
1185
|
-
regexType: this.options.regexType
|
|
1186
|
-
})}`;
|
|
1187
|
-
},
|
|
1188
|
-
number(node) {
|
|
1189
|
-
return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${numberConstraints(node)}`;
|
|
1190
|
-
},
|
|
1191
|
-
integer(node) {
|
|
1192
|
-
return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number().int()" : "z.int()"}${numberConstraints(node)}`;
|
|
1193
|
-
},
|
|
1194
|
-
bigint() {
|
|
1195
|
-
return shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.bigint()" : "z.bigint()";
|
|
1196
|
-
},
|
|
1197
|
-
date(node) {
|
|
1198
|
-
const codec = getCodec(node);
|
|
1199
|
-
if (codec) {
|
|
1200
|
-
if (this.options.direction === "input") return codec.encode(node);
|
|
1201
|
-
return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : codec.decode(node);
|
|
1202
|
-
}
|
|
1203
|
-
return "z.iso.date()";
|
|
1204
|
-
},
|
|
1205
|
-
datetime(node) {
|
|
1206
|
-
const offset = node.offset || this.options.dateType === "stringOffset";
|
|
1207
|
-
const local = node.local || this.options.dateType === "stringLocal";
|
|
1208
|
-
if (offset) return "z.iso.datetime({ offset: true })";
|
|
1209
|
-
if (local) return "z.iso.datetime({ local: true })";
|
|
1210
|
-
return "z.iso.datetime()";
|
|
1211
|
-
},
|
|
1212
|
-
time(node) {
|
|
1213
|
-
if (node.representation === "string") return "z.iso.time()";
|
|
1214
|
-
return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : "z.date()";
|
|
1215
|
-
},
|
|
1216
|
-
uuid(node) {
|
|
1217
|
-
return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthConstraints({
|
|
1218
|
-
...node,
|
|
1219
|
-
regexType: this.options.regexType
|
|
1220
|
-
})}`;
|
|
1221
|
-
},
|
|
1222
|
-
email(node) {
|
|
1223
|
-
return `z.email()${lengthConstraints({
|
|
1224
|
-
...node,
|
|
1225
|
-
regexType: this.options.regexType
|
|
1226
|
-
})}`;
|
|
1227
|
-
},
|
|
1228
|
-
url(node) {
|
|
1229
|
-
return `z.url()${lengthConstraints({
|
|
1230
|
-
...node,
|
|
1231
|
-
regexType: this.options.regexType
|
|
1232
|
-
})}`;
|
|
1233
|
-
},
|
|
1234
|
-
ipv4: () => "z.ipv4()",
|
|
1235
|
-
ipv6: () => "z.ipv6()",
|
|
1236
|
-
blob: () => "z.instanceof(File)",
|
|
1237
|
-
enum(node) {
|
|
1238
|
-
const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
|
|
1239
|
-
if (node.namedEnumValues?.length) {
|
|
1240
|
-
const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`);
|
|
1241
|
-
if (literals.length === 1) return literals[0];
|
|
1242
|
-
return `z.union([${literals.join(", ")}])`;
|
|
1243
|
-
}
|
|
1244
|
-
return buildEnum(nonNullValues);
|
|
1245
|
-
},
|
|
1276
|
+
...scalarNodes,
|
|
1246
1277
|
ref(node) {
|
|
1247
1278
|
if (!node.name) return null;
|
|
1248
1279
|
const refName = ast.resolveRefName(node);
|
|
1249
1280
|
if (!refName) return null;
|
|
1250
|
-
const useInputVariant = node.ref != null && this.options.direction === "
|
|
1281
|
+
const useInputVariant = node.ref != null && this.options.direction === "encode" && containsDirectionalNode({
|
|
1282
|
+
node,
|
|
1283
|
+
printerOptions: this.options
|
|
1284
|
+
});
|
|
1251
1285
|
const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
|
|
1252
1286
|
if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
|
|
1253
1287
|
return resolvedName;
|
|
@@ -1431,7 +1465,7 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1431
1465
|
return `z.int()${numberChecksMini(node)}`;
|
|
1432
1466
|
},
|
|
1433
1467
|
bigint(node) {
|
|
1434
|
-
return `z.bigint()${numberChecksMini(node)}`;
|
|
1468
|
+
return `z.coerce.bigint()${numberChecksMini(node)}`;
|
|
1435
1469
|
},
|
|
1436
1470
|
date(node) {
|
|
1437
1471
|
if (node.representation === "string") return "z.iso.date()";
|
|
@@ -1574,29 +1608,28 @@ const printerZodMini = ast.createPrinter((options) => {
|
|
|
1574
1608
|
const zodPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
1575
1609
|
const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
|
|
1576
1610
|
/**
|
|
1577
|
-
*
|
|
1578
|
-
*
|
|
1579
|
-
* `string → Date` for responses. Schemas without `dateType: 'date'` fields print identically.
|
|
1611
|
+
* Cached printer per direction for a resolver, built on first use. Schemas holding nothing that
|
|
1612
|
+
* converts print the same either way.
|
|
1580
1613
|
*/
|
|
1581
1614
|
function getStdPrinters(resolver, params) {
|
|
1582
1615
|
const cached = zodPrinterCache.get(resolver);
|
|
1583
1616
|
if (cached && cached.coercion === params.coercion && cached.guidType === params.guidType && cached.regexType === params.regexType && cached.dateType === params.dateType && cached.nodes === params.nodes) return {
|
|
1584
|
-
|
|
1585
|
-
|
|
1617
|
+
decode: cached.decode,
|
|
1618
|
+
encode: cached.encode
|
|
1586
1619
|
};
|
|
1587
|
-
const
|
|
1620
|
+
const decode = printerZod({
|
|
1588
1621
|
...params,
|
|
1589
1622
|
resolver,
|
|
1590
|
-
direction: "
|
|
1623
|
+
direction: "decode"
|
|
1591
1624
|
});
|
|
1592
|
-
const
|
|
1625
|
+
const encode = printerZod({
|
|
1593
1626
|
...params,
|
|
1594
1627
|
resolver,
|
|
1595
|
-
direction: "
|
|
1628
|
+
direction: "encode"
|
|
1596
1629
|
});
|
|
1597
1630
|
zodPrinterCache.set(resolver, {
|
|
1598
|
-
|
|
1599
|
-
|
|
1631
|
+
decode,
|
|
1632
|
+
encode,
|
|
1600
1633
|
coercion: params.coercion,
|
|
1601
1634
|
guidType: params.guidType,
|
|
1602
1635
|
regexType: params.regexType,
|
|
@@ -1604,8 +1637,8 @@ function getStdPrinters(resolver, params) {
|
|
|
1604
1637
|
nodes: params.nodes
|
|
1605
1638
|
});
|
|
1606
1639
|
return {
|
|
1607
|
-
|
|
1608
|
-
|
|
1640
|
+
decode,
|
|
1641
|
+
encode
|
|
1609
1642
|
};
|
|
1610
1643
|
}
|
|
1611
1644
|
function getMiniPrinter(resolver, params) {
|
|
@@ -1639,15 +1672,30 @@ const zodGenerator = defineGenerator({
|
|
|
1639
1672
|
if (!node.name) return;
|
|
1640
1673
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
1641
1674
|
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
1642
|
-
const
|
|
1643
|
-
|
|
1675
|
+
const printerOptions = {
|
|
1676
|
+
coercion,
|
|
1677
|
+
guidType,
|
|
1678
|
+
regexType,
|
|
1679
|
+
dateType,
|
|
1680
|
+
resolver,
|
|
1681
|
+
cyclicSchemas,
|
|
1682
|
+
nodes: printer?.nodes
|
|
1683
|
+
};
|
|
1684
|
+
const hasDirectionalNode = !mini && containsDirectionalNode({
|
|
1685
|
+
node,
|
|
1686
|
+
printerOptions
|
|
1687
|
+
});
|
|
1688
|
+
const directionalRefNames = new Set(hasDirectionalNode ? collectDirectionalRefNames({
|
|
1689
|
+
node,
|
|
1690
|
+
printerOptions
|
|
1691
|
+
}) : []);
|
|
1644
1692
|
const importEntries = resolver.imports({
|
|
1645
1693
|
node,
|
|
1646
1694
|
root,
|
|
1647
1695
|
output,
|
|
1648
1696
|
group: group ?? void 0
|
|
1649
1697
|
});
|
|
1650
|
-
const inputImportEntries =
|
|
1698
|
+
const inputImportEntries = hasDirectionalNode ? [...directionalRefNames].map((schemaName) => ({
|
|
1651
1699
|
name: [resolver.schema.inputName(schemaName)],
|
|
1652
1700
|
path: resolver.file({
|
|
1653
1701
|
name: schemaName,
|
|
@@ -1688,7 +1736,7 @@ const zodGenerator = defineGenerator({
|
|
|
1688
1736
|
regexType,
|
|
1689
1737
|
cyclicSchemas,
|
|
1690
1738
|
nodes: printer?.nodes
|
|
1691
|
-
}) : stdPrinters.
|
|
1739
|
+
}) : stdPrinters.decode;
|
|
1692
1740
|
return /* @__PURE__ */ jsxs(File, {
|
|
1693
1741
|
baseName: meta.file.baseName,
|
|
1694
1742
|
path: meta.file.path,
|
|
@@ -1731,10 +1779,10 @@ const zodGenerator = defineGenerator({
|
|
|
1731
1779
|
inferTypeName,
|
|
1732
1780
|
cyclic: cyclicSchemas.has(node.name)
|
|
1733
1781
|
}),
|
|
1734
|
-
|
|
1782
|
+
hasDirectionalNode && stdPrinters && /* @__PURE__ */ jsx(Zod, {
|
|
1735
1783
|
name: resolver.schema.inputName(node.name),
|
|
1736
1784
|
node,
|
|
1737
|
-
printer: stdPrinters.
|
|
1785
|
+
printer: stdPrinters.encode,
|
|
1738
1786
|
inferTypeName: inferred ? resolver.schema.inputTypeName(node.name) : null,
|
|
1739
1787
|
cyclic: cyclicSchemas.has(node.name)
|
|
1740
1788
|
})
|
|
@@ -1757,16 +1805,28 @@ const zodGenerator = defineGenerator({
|
|
|
1757
1805
|
group: group ?? void 0
|
|
1758
1806
|
}) };
|
|
1759
1807
|
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
1760
|
-
|
|
1808
|
+
const printerOptions = {
|
|
1809
|
+
coercion,
|
|
1810
|
+
guidType,
|
|
1811
|
+
regexType,
|
|
1812
|
+
dateType,
|
|
1813
|
+
resolver,
|
|
1814
|
+
cyclicSchemas,
|
|
1815
|
+
nodes: printer?.nodes
|
|
1816
|
+
};
|
|
1817
|
+
function renderSchemaEntry({ schema, name, keysToOmit, direction = "decode" }) {
|
|
1761
1818
|
if (!schema) return null;
|
|
1762
1819
|
const inferTypeName = inferred ? resolver.schema.type(name) : null;
|
|
1763
|
-
const
|
|
1820
|
+
const directionalRefNames = direction === "encode" && !mini ? new Set(collectDirectionalRefNames({
|
|
1821
|
+
node: schema,
|
|
1822
|
+
printerOptions
|
|
1823
|
+
})) : null;
|
|
1764
1824
|
const imports = resolver.imports({
|
|
1765
1825
|
node: schema,
|
|
1766
1826
|
root,
|
|
1767
1827
|
output,
|
|
1768
1828
|
group: group ?? void 0,
|
|
1769
|
-
name: (schemaName) =>
|
|
1829
|
+
name: (schemaName) => directionalRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName)
|
|
1770
1830
|
});
|
|
1771
1831
|
const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
|
|
1772
1832
|
guidType,
|
|
@@ -1855,7 +1915,7 @@ const zodGenerator = defineGenerator({
|
|
|
1855
1915
|
const paramSchemas = node.parameters.map((param) => renderSchemaEntry({
|
|
1856
1916
|
schema: param.schema,
|
|
1857
1917
|
name: resolver.param.name(node, param),
|
|
1858
|
-
direction: "
|
|
1918
|
+
direction: "encode"
|
|
1859
1919
|
}));
|
|
1860
1920
|
const responseSchemas = node.responses.map((res) => {
|
|
1861
1921
|
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
@@ -1893,13 +1953,13 @@ const zodGenerator = defineGenerator({
|
|
|
1893
1953
|
},
|
|
1894
1954
|
name: resolver.response.body(node),
|
|
1895
1955
|
keysToOmit: entry.keysToOmit,
|
|
1896
|
-
direction: "
|
|
1956
|
+
direction: "encode"
|
|
1897
1957
|
});
|
|
1898
1958
|
}
|
|
1899
1959
|
return buildContentTypeVariants(requestBodyContent, resolver.response.body(node), (schema) => ({
|
|
1900
1960
|
...schema,
|
|
1901
1961
|
description: node.requestBody.description ?? schema.description
|
|
1902
|
-
}), "
|
|
1962
|
+
}), "encode");
|
|
1903
1963
|
})();
|
|
1904
1964
|
const { path, query, header } = getOperationParameters(node);
|
|
1905
1965
|
const paramGroupSchemas = inferred ? [
|
|
@@ -1918,12 +1978,12 @@ const zodGenerator = defineGenerator({
|
|
|
1918
1978
|
].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
|
|
1919
1979
|
schema: buildGroupedParamsSchema({ params }),
|
|
1920
1980
|
name: resolver.param[kind](node, params[0]),
|
|
1921
|
-
direction: "
|
|
1981
|
+
direction: "encode"
|
|
1922
1982
|
})) : [];
|
|
1923
1983
|
const optionsSchema = inferred ? renderSchemaEntry({
|
|
1924
1984
|
schema: buildOptionsSchema(node, resolver),
|
|
1925
1985
|
name: resolver.name(`${node.operationId} Options`),
|
|
1926
|
-
direction: "
|
|
1986
|
+
direction: "encode"
|
|
1927
1987
|
}) : null;
|
|
1928
1988
|
const responsesSchema = inferred ? renderSchemaEntry({
|
|
1929
1989
|
schema: buildResponses(node, resolver),
|