@kubb/plugin-zod 5.9.0-canary.20261111T122006 → 5.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -5,143 +5,407 @@ Object.defineProperties(exports, {
5
5
  let _kubb_kit = require("@kubb/kit");
6
6
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
7
7
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
8
- // ../../internals/shared/src/operation.ts
8
+ // ../../internals/utils/src/reserved.ts
9
9
  /**
10
- * Builds the `ResolverFileParams` every operation generator passes to
11
- * `resolver.file`: a file named `name`, tagged by the operation's first
12
- * tag (or `'default'`), at the operation's path. Centralizes the entry object
13
- * that was repeated at dozens of call sites across the client and query plugins.
10
+ * JavaScript and Java reserved words.
11
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
12
+ */
13
+ const reservedWords = /* @__PURE__ */ new Set([
14
+ "abstract",
15
+ "arguments",
16
+ "await",
17
+ "boolean",
18
+ "break",
19
+ "byte",
20
+ "case",
21
+ "catch",
22
+ "char",
23
+ "class",
24
+ "const",
25
+ "continue",
26
+ "debugger",
27
+ "default",
28
+ "delete",
29
+ "do",
30
+ "double",
31
+ "else",
32
+ "enum",
33
+ "eval",
34
+ "export",
35
+ "extends",
36
+ "false",
37
+ "final",
38
+ "finally",
39
+ "float",
40
+ "for",
41
+ "function",
42
+ "goto",
43
+ "if",
44
+ "implements",
45
+ "import",
46
+ "in",
47
+ "instanceof",
48
+ "int",
49
+ "interface",
50
+ "let",
51
+ "long",
52
+ "native",
53
+ "new",
54
+ "null",
55
+ "package",
56
+ "private",
57
+ "protected",
58
+ "public",
59
+ "return",
60
+ "short",
61
+ "static",
62
+ "super",
63
+ "switch",
64
+ "synchronized",
65
+ "this",
66
+ "throw",
67
+ "throws",
68
+ "transient",
69
+ "true",
70
+ "try",
71
+ "typeof",
72
+ "var",
73
+ "void",
74
+ "volatile",
75
+ "while",
76
+ "with",
77
+ "yield",
78
+ "Array",
79
+ "Date",
80
+ "hasOwnProperty",
81
+ "Infinity",
82
+ "isFinite",
83
+ "isNaN",
84
+ "isPrototypeOf",
85
+ "length",
86
+ "Math",
87
+ "name",
88
+ "NaN",
89
+ "Number",
90
+ "Object",
91
+ "prototype",
92
+ "String",
93
+ "toString",
94
+ "undefined",
95
+ "valueOf"
96
+ ]);
97
+ /**
98
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
14
99
  *
15
100
  * @example
16
101
  * ```ts
17
- * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
102
+ * isValidVarName('status') // true
103
+ * isValidVarName('class') // false (reserved word)
104
+ * isValidVarName('42foo') // false (starts with digit)
18
105
  * ```
19
106
  */
20
- function operationFileEntry(node, name, extname = ".ts") {
21
- return {
22
- name,
23
- extname,
24
- tag: node.tags[0] ?? "default",
25
- path: node.path
26
- };
107
+ function isValidVarName(name) {
108
+ if (!name || reservedWords.has(name)) return false;
109
+ return isIdentifier(name);
27
110
  }
28
111
  /**
29
- * Resolves the file a schema-emitting plugin (`plugin-ts`, `plugin-zod`, `plugin-faker`) writes
30
- * `node` to, cached in `ctx.memo` under the resolver's plugin name and the
31
- * operation. The plugin itself and every plugin that imports from it call this, so they all get
32
- * the same path.
112
+ * Returns `name` when it's a syntactically valid JavaScript variable name,
113
+ * otherwise prefixes it with `_` so the result is a valid identifier.
33
114
  *
34
- * When that path matches the file of a component schema in `ctx.driver.inputNode`, ignoring case, the file name
35
- * gets an `Operation` suffix: operation `point` goes to `PointOperation.ts` next to schema `Point.ts`.
36
- * Otherwise the two merge into one file, or overwrite each other on a case-insensitive file system.
37
- * Only the file name changes. The declarations keep their names.
115
+ * Useful for sanitizing OpenAPI schema names or operation IDs that start with
116
+ * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
117
+ * variable, type, or function names.
38
118
  *
39
- * @example Resolve `plugin-ts`'s file for the current operation
119
+ * @example
40
120
  * ```ts
41
- * const fileTs = resolveOperationFile({ ctx, node, resolver: tsResolver, root, output })
121
+ * ensureValidVarName('409') // '_409'
122
+ * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
123
+ * ensureValidVarName('Pet') // 'Pet'
124
+ * ensureValidVarName('class') // '_class'
42
125
  * ```
43
126
  */
44
- function resolveOperationFile(options) {
45
- const { ctx, node, resolver, root, output } = options;
46
- const { memo } = ctx;
47
- const location = {
48
- root,
49
- output,
50
- group: options.group ?? void 0
51
- };
52
- return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {
53
- const schemaPaths = memo(`${resolver.pluginName}:schemaFilePaths`, () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => schema.name ? [resolver.file({
54
- name: schema.name,
55
- extname: ".ts",
56
- ...location
57
- }).path.toLowerCase()] : [])));
58
- let file = resolver.file({
59
- ...operationFileEntry(node, node.operationId),
60
- ...location
61
- });
62
- for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += "Operation") {
63
- const next = resolver.file({
64
- ...operationFileEntry(node, name),
65
- ...location
66
- });
67
- if (next.path === file.path) break;
68
- file = next;
69
- }
70
- return file;
71
- });
72
- }
73
- function baseContentType(contentType) {
74
- return contentType.split(";")[0].trim().toLowerCase();
127
+ function ensureValidVarName(name) {
128
+ if (!name || isValidVarName(name)) return name;
129
+ return `_${name}`;
75
130
  }
76
131
  /**
77
- * Whether a content entry is a sequence of JSON values the adapter marks as a stream, such as `application/jsonl`,
78
- * `application/x-ndjson`, `application/json-seq`, or any media type with an OpenAPI 3.2 `itemSchema`.
132
+ * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
133
+ *
134
+ * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
135
+ * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
136
+ * deciding whether an object key needs quoting.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * isIdentifier('name') // true
141
+ * isIdentifier('x-total')// false
142
+ * ```
79
143
  */
80
- function isJsonLines(entry) {
81
- const baseType = baseContentType(entry.contentType);
82
- return Boolean(entry.stream) && baseType !== "text/event-stream" && !baseType.startsWith("multipart/");
83
- }
84
- function isJsonContentType(baseType) {
85
- return baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json";
144
+ function isIdentifier(name) {
145
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
86
146
  }
147
+ // ../../internals/utils/src/codegen.ts
87
148
  /**
88
- * Parser for one response content entry.
149
+ * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
150
+ * level and closing the brace at column zero. Entries that are themselves multi-line objects indent
151
+ * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
89
152
  *
90
- * JSON stays unset so the runtime detects it. `text/event-stream` and JSON Lines are streams. A binary schema
91
- * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known
92
- * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,
93
- * `image/*`, `audio/*`, and `video/*` are blobs.
153
+ * @example
154
+ * ```ts
155
+ * buildObject(['id: z.number()', 'name: z.string()'])
156
+ * // '{\n id: z.number(),\n name: z.string(),\n}'
157
+ * ```
94
158
  */
95
- function responseTypeForContent(entry) {
96
- const baseType = baseContentType(entry.contentType);
97
- if (!baseType) return void 0;
98
- if (baseType === "text/event-stream" || isJsonLines(entry)) return "stream";
99
- if (isJsonContentType(baseType)) return void 0;
100
- if (entry.schema?.type === "blob") return "blob";
101
- if (baseType.startsWith("text/")) return "text";
102
- if (isBinaryContentType(baseType)) return "blob";
103
- }
104
- function isBinaryContentType(baseType) {
105
- return baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType);
159
+ function buildObject(entries) {
160
+ if (entries.length === 0) return "{}";
161
+ return `{\n${entries.map((entry) => `${_kubb_kit.text.indent({ text: entry })},`).join("\n")}\n}`;
106
162
  }
107
163
  /**
108
- * Maps a content type to the PascalCase suffix used to name per-content-type variants
109
- * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
164
+ * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on
165
+ * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented
166
+ * one level with a trailing comma and the closing bracket at column zero. Used for member lists such
167
+ * as `z.union([…])` and `z.array([…])`.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * buildList(['z.string()', 'z.number()'])
172
+ * // '[z.string(), z.number()]'
173
+ * ```
110
174
  */
111
- function getContentTypeSuffix(contentType) {
112
- const baseType = contentType.split(";")[0].trim();
113
- if (baseType === "application/json") return "Json";
114
- if (baseType === "multipart/form-data") return "FormData";
115
- if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
116
- const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
117
- if (parts.length === 0) return "Unknown";
118
- return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
175
+ function buildList(items, brackets = ["[", "]"]) {
176
+ const [open, close] = brackets;
177
+ if (items.length === 0) return `${open}${close}`;
178
+ if (!items.some((item) => item.includes("\n"))) return `${open}${items.join(", ")}${close}`;
179
+ return `${open}\n${items.map((item) => `${_kubb_kit.text.indent({ text: item })},`).join("\n")}\n${close}`;
119
180
  }
120
181
  /**
121
- * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
122
- * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
182
+ * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
183
+ * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
184
+ * of a recursive schema until first access.
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
189
+ * // "get parent() { return z.lazy(() => Pet) }"
190
+ * ```
123
191
  */
124
- function getPerContentTypeName(baseName, suffix) {
125
- if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
126
- return baseName + suffix;
192
+ function lazyGetter({ name, body }) {
193
+ return `get ${objectKey(name)}() { return ${body} }`;
127
194
  }
128
195
  /**
129
- * Returns the entry with the schema of the value the client returns: `string` for a body read as text, such as `Pet` on
130
- * `application/xml`, and `blob` for `image/*`, `audio/*`, `video/*`, PDF, and octet-stream. A binary schema elsewhere is a `Blob`
131
- * only through `parseAs: 'blob'`, which needs every entry in `entries` binary. JSON, vendor (`application/vnd.*`), stream,
132
- * schemaless, and wildcard entries keep their schema. `entries` is the response's full list of content types.
196
+ * An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * objectKey('x-total') // "'x-total'"
201
+ * ```
133
202
  */
134
- function toResponseContent(entry, entries = [entry]) {
135
- const { contentType, schema } = entry;
136
- if (!schema || entry.stream) return entry;
137
- const baseType = baseContentType(contentType);
138
- const resolved = _kubb_kit.ast.resolveRef(schema);
139
- if (isBinaryContentType(baseType)) return resolved.type === "blob" ? entry : {
140
- ...entry,
141
- schema: _kubb_kit.ast.factory.createSchema({ type: "blob" }),
142
- keysToOmit: null
143
- };
144
- const isPlainMediaType = Boolean(baseType) && !isJsonContentType(baseType) && !baseType.includes("*");
203
+ function objectKey(name) {
204
+ return isIdentifier(name) ? name : _kubb_kit.text.quote(name);
205
+ }
206
+ // ../../internals/utils/src/fs.ts
207
+ /**
208
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
209
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
210
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
211
+ *
212
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
213
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
214
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
215
+ * absolute path, letting generated files escape the configured output directory.
216
+ *
217
+ * @example Nested path from a dotted name
218
+ * `toFilePath('pet.petId') // 'pet/petId'`
219
+ *
220
+ * @example PascalCase the final segment
221
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
222
+ *
223
+ * @example Suffix applied to the final segment only
224
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
225
+ */
226
+ function toFilePath(name, caseLast = _kubb_kit.casing.camel) {
227
+ const parts = name.split(/\.(?=[a-zA-Z])/);
228
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : _kubb_kit.casing.camel(part)).filter(Boolean).join("/");
229
+ }
230
+ // ../../internals/utils/src/strings.ts
231
+ const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
232
+ const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
233
+ /**
234
+ * Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
235
+ * not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
236
+ */
237
+ function dropIdentityEscapes(pattern) {
238
+ let inClass = false;
239
+ return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
240
+ if (escaped === void 0) {
241
+ inClass = match === "[";
242
+ return match;
243
+ }
244
+ return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
245
+ });
246
+ }
247
+ /**
248
+ * Builds the `RegExp` for a pattern, adding the `u` flag when it uses `\p{...}`, `\P{...}` or `\u{...}`, or
249
+ * holds a character outside the BMP. A pattern that is not valid under `u` keeps the flags it came with.
250
+ */
251
+ function toRegExp(pattern, flags) {
252
+ if (flags?.includes("u") || !NEEDS_UNICODE.test(pattern)) return new RegExp(pattern, flags);
253
+ try {
254
+ return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
255
+ } catch {
256
+ return new RegExp(pattern, flags);
257
+ }
258
+ }
259
+ /**
260
+ * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
261
+ * Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
262
+ * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
267
+ * toRegExpString('^(?im)foo', null) // '/^foo/im'
268
+ * ```
269
+ */
270
+ function toRegExpString(pattern, func = "RegExp") {
271
+ const raw = _kubb_kit.text.unquote(pattern);
272
+ const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
273
+ const replacementTarget = match?.[1] ?? "";
274
+ const matchedFlags = match?.[2];
275
+ const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
276
+ if (func === null) return `/${source}/${flags}`;
277
+ return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
278
+ }
279
+ // ../../internals/shared/src/operation.ts
280
+ /**
281
+ * Builds the `ResolverFileParams` every operation generator passes to
282
+ * `resolver.file`: a file named `name`, tagged by the operation's first
283
+ * tag (or `'default'`), at the operation's path. Centralizes the entry object
284
+ * that was repeated at dozens of call sites across the client and query plugins.
285
+ *
286
+ * @example
287
+ * ```ts
288
+ * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
289
+ * ```
290
+ */
291
+ function operationFileEntry(node, name, extname = ".ts") {
292
+ return {
293
+ name,
294
+ extname,
295
+ tag: node.tags[0] ?? "default",
296
+ path: node.path
297
+ };
298
+ }
299
+ /**
300
+ * Resolves the file a schema-emitting plugin (`plugin-ts`, `plugin-zod`, `plugin-faker`) writes
301
+ * `node` to, cached in `ctx.memo` under the resolver's plugin name and the
302
+ * operation. The plugin itself and every plugin that imports from it call this, so they all get
303
+ * the same path.
304
+ *
305
+ * When that path matches the file of a component schema in `ctx.driver.inputNode`, ignoring case, the file name
306
+ * gets an `Operation` suffix: operation `point` goes to `PointOperation.ts` next to schema `Point.ts`.
307
+ * Otherwise the two merge into one file, or overwrite each other on a case-insensitive file system.
308
+ * Only the file name changes. The declarations keep their names.
309
+ *
310
+ * @example Resolve `plugin-ts`'s file for the current operation
311
+ * ```ts
312
+ * const fileTs = resolveOperationFile({ ctx, node, resolver: tsResolver, root, output })
313
+ * ```
314
+ */
315
+ function resolveOperationFile(options) {
316
+ const { ctx, node, resolver, root, output } = options;
317
+ const { memo } = ctx;
318
+ const location = {
319
+ root,
320
+ output,
321
+ group: options.group ?? void 0
322
+ };
323
+ return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {
324
+ const schemaPaths = memo(`${resolver.pluginName}:schemaFilePaths`, () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => schema.name ? [resolver.file({
325
+ name: schema.name,
326
+ extname: ".ts",
327
+ ...location
328
+ }).path.toLowerCase()] : [])));
329
+ let file = resolver.file({
330
+ ...operationFileEntry(node, node.operationId),
331
+ ...location
332
+ });
333
+ for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += "Operation") {
334
+ const next = resolver.file({
335
+ ...operationFileEntry(node, name),
336
+ ...location
337
+ });
338
+ if (next.path === file.path) break;
339
+ file = next;
340
+ }
341
+ return file;
342
+ });
343
+ }
344
+ /**
345
+ * How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.
346
+ */
347
+ function getStreamFraming(entry) {
348
+ if (_kubb_kit.ast.mediaType.getBase(entry) === "text/event-stream") return "events";
349
+ return entry.stream && _kubb_kit.ast.mediaType.isStream(entry) ? "lines" : void 0;
350
+ }
351
+ /**
352
+ * Parser for one response content entry.
353
+ *
354
+ * JSON stays unset so the runtime detects it. `text/event-stream` and JSON Lines are streams. A binary schema
355
+ * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known
356
+ * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,
357
+ * `image/*`, `audio/*`, and `video/*` are blobs.
358
+ */
359
+ function responseTypeForContent(entry) {
360
+ const baseType = _kubb_kit.ast.mediaType.getBase(entry);
361
+ if (!baseType) return void 0;
362
+ if (getStreamFraming(entry)) return "stream";
363
+ if (_kubb_kit.ast.mediaType.isJson(baseType)) return void 0;
364
+ if (entry.schema?.type === "blob") return "blob";
365
+ if (baseType.startsWith("text/")) return "text";
366
+ if (isBinaryContentType(baseType)) return "blob";
367
+ }
368
+ function isBinaryContentType(baseType) {
369
+ return baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType);
370
+ }
371
+ /**
372
+ * Maps a content type to the PascalCase suffix used to name per-content-type variants
373
+ * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
374
+ */
375
+ function getContentTypeSuffix(contentType) {
376
+ const baseType = contentType.split(";")[0].trim();
377
+ if (baseType === "application/json") return "Json";
378
+ if (baseType === "multipart/form-data") return "FormData";
379
+ if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
380
+ const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
381
+ if (parts.length === 0) return "Unknown";
382
+ return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
383
+ }
384
+ /**
385
+ * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
386
+ * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
387
+ */
388
+ function getPerContentTypeName(baseName, suffix) {
389
+ if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
390
+ return baseName + suffix;
391
+ }
392
+ /**
393
+ * Returns the entry with the schema of the value the client returns: `string` for a body read as text, such as `Pet` on
394
+ * `application/xml`, and `blob` for `image/*`, `audio/*`, `video/*`, PDF, and octet-stream. A binary schema elsewhere is a `Blob`
395
+ * only through `parseAs: 'blob'`, which needs every entry in `entries` binary. JSON, vendor (`application/vnd.*`), stream,
396
+ * schemaless, and wildcard entries keep their schema. `entries` is the response's full list of content types.
397
+ */
398
+ function toResponseContent(entry, entries = [entry]) {
399
+ const { contentType, schema } = entry;
400
+ if (!schema || entry.stream) return entry;
401
+ const baseType = _kubb_kit.ast.mediaType.getBase(contentType);
402
+ const resolved = _kubb_kit.ast.ref.getSchema(schema);
403
+ if (isBinaryContentType(baseType)) return resolved.type === "blob" ? entry : {
404
+ ...entry,
405
+ schema: _kubb_kit.ast.factory.createSchema({ type: "blob" }),
406
+ keysToOmit: null
407
+ };
408
+ const isPlainMediaType = Boolean(baseType) && !_kubb_kit.ast.mediaType.isJson(baseType) && !baseType.includes("*");
145
409
  if (resolved.type === "blob") return isPlainMediaType && !entries.every((item) => item.schema?.type === "blob") ? {
146
410
  ...entry,
147
411
  schema: _kubb_kit.ast.factory.createSchema({ type: "string" }),
@@ -184,50 +448,6 @@ function resolveContentTypeVariants(entries, baseName) {
184
448
  };
185
449
  });
186
450
  }
187
- const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
188
- /**
189
- * An OpenAPI 3.2 `querystring` parameter is the whole query string, so a form-encoded object becomes one query parameter
190
- * per property, typed and serialized like any other.
191
- * Another content type, such as JSON, stays one parameter named after it, and the runtime sends its value as the whole query string.
192
- */
193
- function spreadQuerystring(param) {
194
- const schema = param.schema.type === "ref" ? param.schema.schema : param.schema;
195
- const formEncoded = !param.contentType || param.contentType.split(";")[0].trim().toLowerCase() === "application/x-www-form-urlencoded";
196
- if (schema?.type !== "object" || !formEncoded) return [param];
197
- return schema.properties.map((property) => _kubb_kit.ast.factory.createParameter({
198
- name: property.name,
199
- in: "query",
200
- schema: property.schema,
201
- required: param.required && property.required
202
- }));
203
- }
204
- /**
205
- * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
206
- * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
207
- * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
208
- * same parameters once per plugin.
209
- */
210
- function getOperationParameters(node) {
211
- const cached = operationParameterGroupsByNode.get(node);
212
- if (cached) return cached;
213
- const dedupe = (params) => {
214
- const seen = /* @__PURE__ */ new Set();
215
- return params.filter((param) => {
216
- if (seen.has(param.name)) return false;
217
- seen.add(param.name);
218
- return true;
219
- });
220
- };
221
- const group = (location) => dedupe(node.parameters.filter((param) => param.in === location));
222
- const groups = {
223
- path: group("path"),
224
- query: dedupe([...group("query"), ...group("querystring").flatMap(spreadQuerystring)]),
225
- header: group("header"),
226
- cookie: group("cookie")
227
- };
228
- operationParameterGroupsByNode.set(node, groups);
229
- return groups;
230
- }
231
451
  /**
232
452
  * Whether the operation declares more than one request media type with a schema, so a caller must pick one on `contentType`.
233
453
  */
@@ -243,8 +463,8 @@ function hasRequestContentTypeChoice(node) {
243
463
  * than a record.
244
464
  */
245
465
  function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
246
- const { path, query, header, cookie } = getOperationParameters(node);
247
- const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
466
+ const { path, query, header, cookie } = _kubb_kit.ast.http.groupParameters(node);
467
+ const hasBody = Boolean(_kubb_kit.ast.http.requestBody.getSchema(node));
248
468
  const createNever = () => _kubb_kit.ast.factory.createSchema({
249
469
  type: "never",
250
470
  primitive: void 0,
@@ -335,9 +555,10 @@ function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
335
555
  * The schema a status occupies in the `<Name>Responses` record. A status that documents several
336
556
  * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
337
557
  * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
338
- * query hooks and `result.data` use.
558
+ * query hooks and `result.data` use. With `streamData`, the stream variant of a 2xx status holds that schema, since the
559
+ * client returns its body unread.
339
560
  */
340
- function buildResponseRecordEntry(node, res, resolver) {
561
+ function buildResponseRecordEntry(node, res, resolver, streamData) {
341
562
  const statusName = resolver.response.status(node, res.statusCode);
342
563
  const variants = (res.content ?? []).filter((entry) => entry.schema);
343
564
  if (variants.length <= 1) return _kubb_kit.ast.factory.createSchema({
@@ -354,12 +575,12 @@ function buildResponseRecordEntry(node, res, resolver) {
354
575
  required: true,
355
576
  schema: _kubb_kit.ast.factory.createSchema({
356
577
  type: "enum",
357
- enumValues: [baseContentType(variant.contentType)]
578
+ enumValues: [_kubb_kit.ast.mediaType.getBase(variant)]
358
579
  })
359
580
  }), _kubb_kit.ast.factory.createProperty({
360
581
  name: "data",
361
582
  required: true,
362
- schema: _kubb_kit.ast.factory.createSchema({
583
+ schema: streamData && _kubb_kit.ast.http.status.isSuccess(res) && _kubb_kit.ast.mediaType.isStream(variant) ? streamData : _kubb_kit.ast.factory.createSchema({
363
584
  type: "ref",
364
585
  name: variant.name
365
586
  })
@@ -375,29 +596,20 @@ function buildResponseRecordEntry(node, res, resolver) {
375
596
  * Always emits the keyed record, even when an operation declares no responses. An operation with no
376
597
  * responses renders as an empty object, which keeps every consumer's import (for example the axios
377
598
  * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
599
+ *
600
+ * `streamData` is the type of a stream variant's `data`, such as a `ReadableStream<Uint8Array>` reference.
378
601
  */
379
- function buildResponses(node, resolver) {
602
+ function buildResponses(node, resolver, { streamData } = {}) {
380
603
  return _kubb_kit.ast.factory.createSchema({
381
604
  type: "object",
382
605
  primitive: "object",
383
606
  properties: node.responses.map((res) => _kubb_kit.ast.factory.createProperty({
384
607
  name: String(res.statusCode),
385
608
  required: true,
386
- schema: buildResponseRecordEntry(node, res, resolver)
609
+ schema: buildResponseRecordEntry(node, res, resolver, streamData)
387
610
  }))
388
611
  });
389
612
  }
390
- function getStatusCodeNumber(statusCode) {
391
- const code = Number(statusCode);
392
- return Number.isNaN(code) ? null : code;
393
- }
394
- function isSuccessStatusCode(statusCode) {
395
- const code = getStatusCodeNumber(statusCode);
396
- return code !== null && code >= 200 && code < 300;
397
- }
398
- function getSuccessResponses(responses) {
399
- return responses.filter((response) => isSuccessStatusCode(response.statusCode));
400
- }
401
613
  // ../../internals/shared/src/adapter.ts
402
614
  /**
403
615
  * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
@@ -411,344 +623,15 @@ function getSuccessResponses(responses) {
411
623
  * const { dateType } = getOasAdapter(ctx.adapter).options
412
624
  * ```
413
625
  */
414
- function getOasAdapter(adapter) {
415
- if (adapter.name !== "oas") throw new _kubb_kit.Diagnostics.Error({
416
- code: _kubb_kit.Diagnostics.code.adapterRequired,
417
- severity: "error",
418
- message: `Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}".`,
419
- help: "Set `adapter: adapterOas()` in kubb.config.ts.",
420
- location: { kind: "config" }
421
- });
422
- return adapter;
423
- }
424
- // ../../internals/utils/src/reserved.ts
425
- /**
426
- * JavaScript and Java reserved words.
427
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
428
- */
429
- const reservedWords = /* @__PURE__ */ new Set([
430
- "abstract",
431
- "arguments",
432
- "await",
433
- "boolean",
434
- "break",
435
- "byte",
436
- "case",
437
- "catch",
438
- "char",
439
- "class",
440
- "const",
441
- "continue",
442
- "debugger",
443
- "default",
444
- "delete",
445
- "do",
446
- "double",
447
- "else",
448
- "enum",
449
- "eval",
450
- "export",
451
- "extends",
452
- "false",
453
- "final",
454
- "finally",
455
- "float",
456
- "for",
457
- "function",
458
- "goto",
459
- "if",
460
- "implements",
461
- "import",
462
- "in",
463
- "instanceof",
464
- "int",
465
- "interface",
466
- "let",
467
- "long",
468
- "native",
469
- "new",
470
- "null",
471
- "package",
472
- "private",
473
- "protected",
474
- "public",
475
- "return",
476
- "short",
477
- "static",
478
- "super",
479
- "switch",
480
- "synchronized",
481
- "this",
482
- "throw",
483
- "throws",
484
- "transient",
485
- "true",
486
- "try",
487
- "typeof",
488
- "var",
489
- "void",
490
- "volatile",
491
- "while",
492
- "with",
493
- "yield",
494
- "Array",
495
- "Date",
496
- "hasOwnProperty",
497
- "Infinity",
498
- "isFinite",
499
- "isNaN",
500
- "isPrototypeOf",
501
- "length",
502
- "Math",
503
- "name",
504
- "NaN",
505
- "Number",
506
- "Object",
507
- "prototype",
508
- "String",
509
- "toString",
510
- "undefined",
511
- "valueOf"
512
- ]);
513
- /**
514
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
515
- *
516
- * @example
517
- * ```ts
518
- * isValidVarName('status') // true
519
- * isValidVarName('class') // false (reserved word)
520
- * isValidVarName('42foo') // false (starts with digit)
521
- * ```
522
- */
523
- function isValidVarName(name) {
524
- if (!name || reservedWords.has(name)) return false;
525
- return isIdentifier(name);
526
- }
527
- /**
528
- * Returns `name` when it's a syntactically valid JavaScript variable name,
529
- * otherwise prefixes it with `_` so the result is a valid identifier.
530
- *
531
- * Useful for sanitizing OpenAPI schema names or operation IDs that start with
532
- * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
533
- * variable, type, or function names.
534
- *
535
- * @example
536
- * ```ts
537
- * ensureValidVarName('409') // '_409'
538
- * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
539
- * ensureValidVarName('Pet') // 'Pet'
540
- * ensureValidVarName('class') // '_class'
541
- * ```
542
- */
543
- function ensureValidVarName(name) {
544
- if (!name || isValidVarName(name)) return name;
545
- return `_${name}`;
546
- }
547
- /**
548
- * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
549
- *
550
- * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
551
- * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
552
- * deciding whether an object key needs quoting.
553
- *
554
- * @example
555
- * ```ts
556
- * isIdentifier('name') // true
557
- * isIdentifier('x-total')// false
558
- * ```
559
- */
560
- function isIdentifier(name) {
561
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
562
- }
563
- // ../../internals/utils/src/strings.ts
564
- /**
565
- * Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
566
- * any backslash or single quote in the content.
567
- *
568
- * @example
569
- * ```ts
570
- * singleQuote('foo') // "'foo'"
571
- * singleQuote("o'clock") // "'o\\'clock'"
572
- * ```
573
- */
574
- function singleQuote(value) {
575
- if (value === void 0 || value === null) return "''";
576
- return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
577
- }
578
- /**
579
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
580
- * Returns the string unchanged when no balanced quote pair is found.
581
- *
582
- * @example
583
- * ```ts
584
- * trimQuotes('"hello"') // 'hello'
585
- * trimQuotes('hello') // 'hello'
586
- * ```
587
- */
588
- function trimQuotes(text) {
589
- if (text.length >= 2) {
590
- const first = text[0];
591
- const last = text[text.length - 1];
592
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
593
- }
594
- return text;
595
- }
596
- /**
597
- * Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
598
- *
599
- * Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
600
- * code matches the repo style without a formatter.
601
- *
602
- * @example
603
- * ```ts
604
- * stringify('hello') // "'hello'"
605
- * stringify('"hello"') // "'hello'"
606
- * ```
607
- */
608
- function stringify(value) {
609
- if (value === void 0 || value === null) return "''";
610
- return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
611
- }
612
- const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
613
- const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
614
- /**
615
- * Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
616
- * not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
617
- */
618
- function dropIdentityEscapes(pattern) {
619
- let inClass = false;
620
- return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
621
- if (escaped === void 0) {
622
- inClass = match === "[";
623
- return match;
624
- }
625
- return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
626
- });
627
- }
628
- /**
629
- * Builds the `RegExp` for a pattern, adding the `u` flag when it uses `\p{...}`, `\P{...}` or `\u{...}`, or
630
- * holds a character outside the BMP. A pattern that is not valid under `u` keeps the flags it came with.
631
- */
632
- function toRegExp(pattern, flags) {
633
- if (flags?.includes("u") || !NEEDS_UNICODE.test(pattern)) return new RegExp(pattern, flags);
634
- try {
635
- return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
636
- } catch {
637
- return new RegExp(pattern, flags);
638
- }
639
- }
640
- /**
641
- * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
642
- * Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
643
- * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
644
- *
645
- * @example
646
- * ```ts
647
- * toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
648
- * toRegExpString('^(?im)foo', null) // '/^foo/im'
649
- * ```
650
- */
651
- function toRegExpString(text, func = "RegExp") {
652
- const raw = trimQuotes(text);
653
- const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
654
- const replacementTarget = match?.[1] ?? "";
655
- const matchedFlags = match?.[2];
656
- const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
657
- if (func === null) return `/${source}/${flags}`;
658
- return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
659
- }
660
- // ../../internals/utils/src/codegen.ts
661
- const INDENT = " ";
662
- /**
663
- * Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
664
- */
665
- function indentLines(text) {
666
- if (!text) return "";
667
- return text.split("\n").map((line) => line.trim() ? `${INDENT}${line}` : "").join("\n");
668
- }
669
- /**
670
- * Renders an object key, quoting it with single quotes only when it is not a valid identifier.
671
- * Reserved words and globals (`name`, `class`, …) are valid bare keys and stay unquoted.
672
- *
673
- * @example
674
- * ```ts
675
- * objectKey('name') // 'name'
676
- * objectKey('x-total') // "'x-total'"
677
- * ```
678
- */
679
- function objectKey(name) {
680
- return isIdentifier(name) ? name : singleQuote(name);
681
- }
682
- /**
683
- * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
684
- * level and closing the brace at column zero. Entries that are themselves multi-line objects indent
685
- * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
686
- *
687
- * @example
688
- * ```ts
689
- * buildObject(['id: z.number()', 'name: z.string()'])
690
- * // '{\n id: z.number(),\n name: z.string(),\n}'
691
- * ```
692
- */
693
- function buildObject(entries) {
694
- if (entries.length === 0) return "{}";
695
- return `{\n${entries.map((entry) => `${indentLines(entry)},`).join("\n")}\n}`;
696
- }
697
- /**
698
- * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on
699
- * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented
700
- * one level with a trailing comma and the closing bracket at column zero. Used for member lists such
701
- * as `z.union([…])` and `z.array([…])`.
702
- *
703
- * @example
704
- * ```ts
705
- * buildList(['z.string()', 'z.number()'])
706
- * // '[z.string(), z.number()]'
707
- * ```
708
- */
709
- function buildList(items, brackets = ["[", "]"]) {
710
- const [open, close] = brackets;
711
- if (items.length === 0) return `${open}${close}`;
712
- if (!items.some((item) => item.includes("\n"))) return `${open}${items.join(", ")}${close}`;
713
- return `${open}\n${items.map((item) => `${indentLines(item)},`).join("\n")}\n${close}`;
714
- }
715
- /**
716
- * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
717
- * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
718
- * of a recursive schema until first access.
719
- *
720
- * @example
721
- * ```ts
722
- * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
723
- * // "get parent() { return z.lazy(() => Pet) }"
724
- * ```
725
- */
726
- function lazyGetter({ name, body }) {
727
- return `get ${objectKey(name)}() { return ${body} }`;
728
- }
729
- // ../../internals/utils/src/fs.ts
730
- /**
731
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
732
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
733
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
734
- *
735
- * Empty segments are dropped before joining. They arise when the name starts with a dot
736
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
737
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
738
- * absolute path, letting generated files escape the configured output directory.
739
- *
740
- * @example Nested path from a dotted name
741
- * `toFilePath('pet.petId') // 'pet/petId'`
742
- *
743
- * @example PascalCase the final segment
744
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
745
- *
746
- * @example Suffix applied to the final segment only
747
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
748
- */
749
- function toFilePath(name, caseLast = _kubb_kit.camelCase) {
750
- const parts = name.split(/\.(?=[a-zA-Z])/);
751
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : (0, _kubb_kit.camelCase)(part)).filter(Boolean).join("/");
626
+ function getOasAdapter(adapter) {
627
+ if (adapter.name !== "oas") throw new _kubb_kit.Diagnostics.Error({
628
+ code: _kubb_kit.Diagnostics.code.adapterRequired,
629
+ severity: "error",
630
+ message: `Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}".`,
631
+ help: "Set `adapter: adapterOas()` in kubb.config.ts.",
632
+ location: { kind: "config" }
633
+ });
634
+ return adapter;
752
635
  }
753
636
  // ../../internals/shared/src/resolver.ts
754
637
  /**
@@ -838,6 +721,13 @@ function createOperationResponseResolver() {
838
721
  name
839
722
  });
840
723
  },
724
+ headers(node, statusCode) {
725
+ const name = this.name(`${node.operationId} Status ${statusCode} Headers`);
726
+ return this.resolveName({
727
+ key: operationKey(node, "response", "headers", statusCode),
728
+ name
729
+ });
730
+ },
841
731
  body(node) {
842
732
  const name = this.name(`${node.operationId} Body`);
843
733
  return this.resolveName({
@@ -879,147 +769,7 @@ function createCasedFile(caseLast) {
879
769
  * Names of the schemas in a circular reference chain, computed once per build.
880
770
  */
881
771
  function getCircularNames(ctx) {
882
- return ctx.memo("circularNames:set", () => _kubb_kit.ast.findCircularSchemas(ctx.driver.inputNode?.schemas ?? []));
883
- }
884
- // ../../internals/shared/src/group.ts
885
- /**
886
- * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
887
- * shared default naming so every plugin groups output consistently:
888
- *
889
- * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
890
- * - other groups use the camelCased group (`pet store` → `petStore`).
891
- *
892
- * A user-provided `group.name` always wins over the default namer, so callers stay in
893
- * control of their output folders. Returns `null` when grouping is disabled, matching the
894
- * per-plugin convention.
895
- *
896
- * @param group - The user-supplied group option, or `undefined` to disable grouping.
897
- *
898
- * @example
899
- * ```ts
900
- * createGroupConfig(group) // shared across every plugin
901
- * ```
902
- */
903
- function createGroupConfig(group) {
904
- if (!group) return null;
905
- const defaultName = (ctx) => {
906
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
907
- return (0, _kubb_kit.camelCase)(ctx.group);
908
- };
909
- return {
910
- ...group,
911
- name: group.name ? group.name : defaultName
912
- };
913
- }
914
- // ../../internals/shared/src/schemaTraversal.ts
915
- /**
916
- * Maps each property of an object schema to its transformed output. Pairs every result with the
917
- * original property so the printer keeps full control over modifiers, getters, and key syntax.
918
- *
919
- * @example
920
- * ```ts
921
- * const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
922
- * // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
923
- * ```
924
- */
925
- function mapSchemaProperties(node, transform) {
926
- return node.properties.map((property) => ({
927
- name: property.name,
928
- property,
929
- output: transform(property.schema)
930
- }));
931
- }
932
- /**
933
- * Maps each member of a union or intersection schema to its transformed output, pairing every
934
- * result with the original member.
935
- */
936
- function mapSchemaMembers(node, transform) {
937
- return (node.members ?? []).map((schema) => ({
938
- schema,
939
- output: transform(schema)
940
- }));
941
- }
942
- /**
943
- * Maps each item of an array or tuple schema to its transformed output, pairing every result with
944
- * the original item.
945
- */
946
- function mapSchemaItems(node, transform) {
947
- return (node.items ?? []).map((schema) => ({
948
- schema,
949
- output: transform(schema)
950
- }));
951
- }
952
- // src/components/Zod.tsx
953
- function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
954
- const output = printer.print(node);
955
- const printerImports = printer.drainImports();
956
- if (!output) return;
957
- const needsAnnotation = cyclic && node.type !== "object";
958
- const isBare = _kubb_kit.ast.isBareRef(node, printer.options.keysToOmit);
959
- const isCyclic = Boolean(cyclic || printer.options.cyclicSchemas && (0, _kubb_kit.containsCircularRef)(node, { circularSchemas: printer.options.cyclicSchemas }));
960
- const value = Boolean(compile) && !isBare && !isCyclic ? typeof compile === "object" && compile.strict ? `z.compile(${output}, { strict: true })` : `z.compile(${output})` : output;
961
- const targetType = inferTypeName ?? `z.infer<typeof ${name}>`;
962
- const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
963
- const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
964
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
965
- printerImports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
966
- name: imp.name,
967
- path: imp.path,
968
- root: imp.root,
969
- isTypeOnly: imp.isTypeOnly,
970
- isNameSpace: imp.isNameSpace
971
- }, [
972
- name,
973
- imp.path,
974
- imp.name
975
- ].join("-"))),
976
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
977
- name,
978
- isExportable: true,
979
- isIndexable: true,
980
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
981
- export: true,
982
- name,
983
- type: needsAnnotation ? "z.ZodType" : void 0,
984
- children: value
985
- })
986
- }),
987
- inferTypeName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
988
- name: inferTypeName,
989
- isExportable: true,
990
- isIndexable: true,
991
- isTypeOnly: true,
992
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Type, {
993
- export: true,
994
- name: inferTypeName,
995
- children: `z.${inferKind}<typeof ${name}>`
996
- })
997
- }),
998
- shouldGenerateIs && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
999
- name: isName,
1000
- isExportable: true,
1001
- isIndexable: true,
1002
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
1003
- export: true,
1004
- name: isName,
1005
- JSDoc: { comments: [`Type guard for {@link ${name}}`] },
1006
- children: mini ? `(data: unknown): data is ${targetType} => z.validate(${name}, data)` : `(data: unknown): data is ${targetType} => ${name}.validate(data)`
1007
- })
1008
- }),
1009
- shouldGenerateAssert && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1010
- name: assertName,
1011
- isExportable: true,
1012
- isIndexable: true,
1013
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Function, {
1014
- export: true,
1015
- name: assertName,
1016
- params: "data: unknown",
1017
- returnType: `asserts data is ${targetType}`,
1018
- JSDoc: { comments: [`Asserter for {@link ${name}}`, "@throws {z.ZodError} If data is invalid"] },
1019
- children: mini ? `if (!z.validate(${name}, data)) {\n z.parse(${name}, data)\n}` : `if (!${name}.validate(data)) {\n ${name}.parse(data)\n}`
1020
- })
1021
- })
1022
- ] });
772
+ return ctx.memo("circularNames:set", () => _kubb_kit.ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []));
1023
773
  }
1024
774
  // src/utils.ts
1025
775
  /**
@@ -1040,7 +790,7 @@ function shouldCoerce(coercion, type) {
1040
790
  */
1041
791
  function isClosedKeySchema(node) {
1042
792
  if (!node) return false;
1043
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
793
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
1044
794
  if (resolved.type === "enum") return true;
1045
795
  if (resolved.type === "union") {
1046
796
  const members = resolved.members ?? [];
@@ -1050,12 +800,12 @@ function isClosedKeySchema(node) {
1050
800
  }
1051
801
  /**
1052
802
  * Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
1053
- * argument. Property counts, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
803
+ * argument. Input checks, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
1054
804
  * on `.and(…)`.
1055
805
  */
1056
806
  function isPlainInlineObject(node) {
1057
807
  if (node.type !== "object") return false;
1058
- if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
808
+ if (hasInputChecks(node)) return false;
1059
809
  if (node.nullable || node.optional || node.nullish) return false;
1060
810
  const hasAdditionalProperties = node.additionalProperties !== void 0;
1061
811
  const hasPatternProperties = Boolean(node.patternProperties);
@@ -1066,21 +816,21 @@ function isPlainInlineObject(node) {
1066
816
  * `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
1067
817
  * unresolved) objects, single-member unions of an object, and object-composable `allOf`.
1068
818
  *
1069
- * Property counts render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
819
+ * Input checks, such as property counts and `conditionals`, render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
1070
820
  */
1071
821
  function isObjectSchemaNode(node, cyclicSchemas) {
1072
822
  if (node.nullable || node.optional || node.nullish) return false;
823
+ if (hasInputChecks(node)) return false;
1073
824
  if (node.type === "object") {
1074
- if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
1075
825
  const hasProperties = Boolean(node.properties?.length);
1076
826
  const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
1077
827
  const hasPatternProperties = Boolean(node.patternProperties);
1078
828
  return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties || node.propertyNames));
1079
829
  }
1080
830
  if (node.type === "ref") {
1081
- const refName = _kubb_kit.ast.resolveRefName(node);
831
+ const refName = _kubb_kit.ast.ref.getName(node);
1082
832
  if (refName && cyclicSchemas?.has(refName)) return false;
1083
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
833
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
1084
834
  return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
1085
835
  }
1086
836
  if (node.type === "union") {
@@ -1103,23 +853,23 @@ function preserveRequiredProperties(members) {
1103
853
  const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
1104
854
  if (seen.has(node)) return false;
1105
855
  seen.add(node);
1106
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
1107
- const composition = _kubb_kit.ast.narrowSchema(resolved, _kubb_kit.ast.schemaTypes.intersection) ?? _kubb_kit.ast.narrowSchema(resolved, _kubb_kit.ast.schemaTypes.union);
856
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
857
+ const composition = _kubb_kit.ast.schema.narrow(resolved, _kubb_kit.ast.schema.types.intersection) ?? _kubb_kit.ast.schema.narrow(resolved, _kubb_kit.ast.schema.types.union);
1108
858
  if (composition) {
1109
- if (composition.type === _kubb_kit.ast.schemaTypes.union && composition.members?.length !== 1) return false;
859
+ if (composition.type === _kubb_kit.ast.schema.types.union && composition.members?.length !== 1) return false;
1110
860
  return composition.members?.some((child) => hasRequiredProperty({
1111
861
  node: child,
1112
862
  propertyName,
1113
863
  seen
1114
864
  })) ?? false;
1115
865
  }
1116
- return _kubb_kit.ast.resolveSchemaProperties({
866
+ return _kubb_kit.ast.schema.findProperties({
1117
867
  node: resolved,
1118
868
  propertyName
1119
869
  }).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
1120
870
  };
1121
871
  return members.map((member, index) => {
1122
- const object = _kubb_kit.ast.narrowSchema(member, _kubb_kit.ast.schemaTypes.object);
872
+ const object = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.object);
1123
873
  if (!object || index === 0) return member;
1124
874
  return {
1125
875
  ...object,
@@ -1148,7 +898,7 @@ function preserveRequiredProperties(members) {
1148
898
  * representation, strings are quoted.
1149
899
  */
1150
900
  function formatDefault(value) {
1151
- if (typeof value === "string") return stringify(value);
901
+ if (typeof value === "string") return _kubb_kit.text.quote(value);
1152
902
  if (typeof value === "object" && value !== null) return JSON.stringify(value);
1153
903
  return String(value ?? "");
1154
904
  }
@@ -1163,16 +913,17 @@ function formatDefault(value) {
1163
913
  */
1164
914
  function defaultLiteral(node, value) {
1165
915
  if (value === null) return null;
1166
- const resolved = node ? (0, _kubb_kit.syncSchemaRef)(node) : void 0;
1167
- if (resolved && _kubb_kit.ast.narrowSchema(resolved, "bigint")) {
916
+ const resolved = node ? _kubb_kit.ast.ref.mergeSchema(node) : void 0;
917
+ if (resolved?.type === "blob") return null;
918
+ if (resolved && _kubb_kit.ast.schema.narrow(resolved, "bigint")) {
1168
919
  if (typeof value === "bigint") return `BigInt(${value})`;
1169
920
  if (typeof value === "number" && Number.isInteger(value)) return `BigInt(${value})`;
1170
921
  return null;
1171
922
  }
1172
- if (resolved && _kubb_kit.ast.narrowSchema(resolved, "array")) return Array.isArray(value) ? JSON.stringify(value) : null;
1173
- const enumNode = resolved ? _kubb_kit.ast.narrowSchema(resolved, "enum") : void 0;
923
+ if (resolved && _kubb_kit.ast.schema.narrow(resolved, "array")) return Array.isArray(value) ? JSON.stringify(value) : null;
924
+ const enumNode = resolved ? _kubb_kit.ast.schema.narrow(resolved, "enum") : void 0;
1174
925
  if (enumNode) {
1175
- const values = enumNode.namedEnumValues?.map((member) => member.value) ?? enumNode.enumValues ?? [];
926
+ const values = _kubb_kit.ast.schema.getLiterals(enumNode);
1176
927
  if (values.length) {
1177
928
  const match = values.find((member) => member === value || String(member) === String(value));
1178
929
  return match != null ? formatDefault(match) : null;
@@ -1241,13 +992,13 @@ function patternKeySchemaMini({ patterns, regexType }) {
1241
992
  * Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
1242
993
  * using the standard chainable Zod v4 API.
1243
994
  */
1244
- function numberConstraints({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
995
+ function numberConstraints({ min, max, exclusive, multipleOf }, suffix = "") {
1245
996
  return [
1246
- min !== void 0 ? `.min(${min})` : "",
1247
- max !== void 0 ? `.max(${max})` : "",
1248
- exclusiveMinimum !== void 0 ? `.gt(${exclusiveMinimum})` : "",
1249
- exclusiveMaximum !== void 0 ? `.lt(${exclusiveMaximum})` : "",
1250
- multipleOf !== void 0 ? `.multipleOf(${multipleOf})` : ""
997
+ min !== void 0 ? `.min(${min}${suffix})` : "",
998
+ max !== void 0 ? `.max(${max}${suffix})` : "",
999
+ exclusive?.min !== void 0 ? `.gt(${exclusive.min}${suffix})` : "",
1000
+ exclusive?.max !== void 0 ? `.lt(${exclusive.max}${suffix})` : "",
1001
+ multipleOf !== void 0 ? `.multipleOf(${multipleOf}${suffix})` : ""
1251
1002
  ].join("");
1252
1003
  }
1253
1004
  /**
@@ -1262,36 +1013,71 @@ function lengthConstraints({ min, max, pattern, regexType }) {
1262
1013
  ].join("");
1263
1014
  }
1264
1015
  /**
1016
+ * Numeric bounds for a bigint: the inclusive, whole, safe bounds faker mocks against, and a whole, safe `multipleOf`.
1017
+ */
1018
+ function bigintConstraints({ min, max, exclusive, multipleOf }) {
1019
+ return {
1020
+ ..._kubb_kit.ast.schema.getNumberBounds({
1021
+ min,
1022
+ max,
1023
+ exclusive,
1024
+ integer: true
1025
+ }),
1026
+ multipleOf: Number.isSafeInteger(multipleOf) ? multipleOf : void 0
1027
+ };
1028
+ }
1029
+ /**
1265
1030
  * Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
1266
1031
  */
1267
- function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
1268
- const checks = [];
1269
- if (min !== void 0) checks.push(`z.minimum(${min})`);
1270
- if (max !== void 0) checks.push(`z.maximum(${max})`);
1271
- if (exclusiveMinimum !== void 0) checks.push(`z.minimum(${exclusiveMinimum}, { exclusive: true })`);
1272
- if (exclusiveMaximum !== void 0) checks.push(`z.maximum(${exclusiveMaximum}, { exclusive: true })`);
1273
- if (multipleOf !== void 0) checks.push(`z.multipleOf(${multipleOf})`);
1032
+ function numberChecksMini({ min, max, exclusive, multipleOf }, suffix = "") {
1033
+ const checks = [
1034
+ min !== void 0 && `z.minimum(${min}${suffix})`,
1035
+ max !== void 0 && `z.maximum(${max}${suffix})`,
1036
+ exclusive?.min !== void 0 && `z.gt(${exclusive.min}${suffix})`,
1037
+ exclusive?.max !== void 0 && `z.lt(${exclusive.max}${suffix})`,
1038
+ multipleOf !== void 0 && `z.multipleOf(${multipleOf}${suffix})`
1039
+ ].filter(Boolean);
1274
1040
  return checks.length ? `.check(${checks.join(", ")})` : "";
1275
1041
  }
1276
1042
  /**
1277
1043
  * Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
1278
1044
  */
1279
1045
  function lengthChecksMini({ min, max, pattern, regexType }) {
1280
- const checks = [];
1281
- if (min !== void 0) checks.push(`z.minLength(${min})`);
1282
- if (max !== void 0) checks.push(`z.maxLength(${max})`);
1283
- if (pattern !== void 0) checks.push(`z.regex(${regexSource(pattern, regexType)})`);
1046
+ const checks = [
1047
+ min !== void 0 && `z.minLength(${min})`,
1048
+ max !== void 0 && `z.maxLength(${max})`,
1049
+ pattern !== void 0 && `z.regex(${regexSource(pattern, regexType)})`
1050
+ ].filter(Boolean);
1284
1051
  return checks.length ? `.check(${checks.join(", ")})` : "";
1285
1052
  }
1286
1053
  /**
1287
- * Checks property counts before object parsing strips unknown keys or inserts defaults.
1054
+ * Whether `propertyNames` needs its own check: `z.record` checks the keys of a map, but `z.object` and a record keyed by
1055
+ * `patternProperties` do not.
1056
+ */
1057
+ function hasKeyCheck(node) {
1058
+ if (node.type !== "object" || !node.propertyNames) return false;
1059
+ const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
1060
+ return node.properties.length > 0 || Boolean(node.patternProperties) && !hasTypedAdditionalProperties;
1061
+ }
1062
+ /**
1063
+ * Whether the node prints inside `z.pipe(z.unknown().check(…), …)`, see {@link inputChecks}.
1064
+ */
1065
+ function hasInputChecks(node) {
1066
+ return Boolean(node.conditionals?.length) || node.type === "object" && (node.min !== void 0 || node.max !== void 0) || hasKeyCheck(node);
1067
+ }
1068
+ /**
1069
+ * Checks the raw input, before parsing strips unknown keys or inserts defaults: property counts, key names, then the
1070
+ * printed `conditionals`.
1288
1071
  */
1289
- function objectConstraints({ value, node }) {
1290
- if (node.type !== "object") return value;
1291
- const checks = [];
1072
+ function inputChecks({ value, node, keySchema, conditionals }) {
1292
1073
  const skip = "typeof value !== \"object\" || value === null || Array.isArray(value)";
1293
- 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" })`);
1294
- 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" })`);
1074
+ const { min, max } = node.type === "object" ? node : {};
1075
+ const checks = [
1076
+ min !== void 0 && `z.refine(value => ${skip} || Object.keys(value).length >= ${min}, { message: "Object must have at least ${min} properties" })`,
1077
+ max !== void 0 && `z.refine(value => ${skip} || Object.keys(value).length <= ${max}, { message: "Object must have at most ${max} properties" })`,
1078
+ keySchema && `z.refine(value => ${skip} || Object.keys(value).every((key) => ${keySchema}.safeParse(key).success), { message: "Object keys must match the property names schema" })`,
1079
+ ...conditionals
1080
+ ].filter(Boolean);
1295
1081
  return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
1296
1082
  }
1297
1083
  /**
@@ -1307,8 +1093,8 @@ function applyModifiers({ value, schema, nullable, optional, nullish, defaultVal
1307
1093
  })();
1308
1094
  const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
1309
1095
  const withDefault = literal !== null ? `${withModifier}.default(${literal})` : withModifier;
1310
- const withDescription = description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
1311
- return examples?.length ? `${withDescription}.meta({ examples: [${examples.map(formatDefault).join(", ")}] })` : withDescription;
1096
+ const withDescription = description ? `${withDefault}.describe(${_kubb_kit.text.quote(description)})` : withDefault;
1097
+ return examples?.length ? `${withDescription}.meta({ examples: [${examples.map((example) => example === null ? "null" : formatDefault(example)).join(", ")}] })` : withDescription;
1312
1098
  }
1313
1099
  function modifierDepth(schema) {
1314
1100
  if (schema.nullish || schema.nullable && schema.optional) return 2;
@@ -1326,7 +1112,7 @@ function modifierDepth(schema) {
1326
1112
  * `.unwrap()`, so its printer passes `._zod.def.innerType` as the `step`.
1327
1113
  */
1328
1114
  function omitUnwrapChain(node, step = ".unwrap()") {
1329
- const ref = _kubb_kit.ast.narrowSchema(node, "ref");
1115
+ const ref = _kubb_kit.ast.schema.narrow(node, "ref");
1330
1116
  if (!ref) return "";
1331
1117
  const target = ref.schema ?? ref;
1332
1118
  const depth = modifierDepth(target) + (target.default !== void 0 ? 1 : 0);
@@ -1348,7 +1134,7 @@ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaul
1348
1134
  /**
1349
1135
  * Calls for string `format`s with a dedicated Zod constructor; a `Map` so `constructor` cannot hit `Object.prototype`.
1350
1136
  */
1351
- const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
1137
+ const stringFormatCalls = /* @__PURE__ */ new Map([
1352
1138
  ["byte", "z.base64()"],
1353
1139
  ["base64", "z.base64()"],
1354
1140
  ["base64url", "z.base64url()"],
@@ -1359,6 +1145,16 @@ const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
1359
1145
  ["hostname", "z.hostname()"]
1360
1146
  ]);
1361
1147
  /**
1148
+ * Calls for a string's `contentEncoding` with a dedicated Zod constructor.
1149
+ */
1150
+ const contentEncodingCalls = /* @__PURE__ */ new Map([["base64", "z.base64()"], ["base64url", "z.base64url()"]]);
1151
+ /**
1152
+ * The check for a string that holds a JSON document: it must parse, and the parsed value must pass `schema`.
1153
+ */
1154
+ function jsonContentCheck(schema) {
1155
+ return `(value) => { try { return ${schema}.safeParse(JSON.parse(value)).success } catch { return false } }, { message: "String must hold JSON that matches its content schema" }`;
1156
+ }
1157
+ /**
1362
1158
  * Marks an inline `oneOf` object strict, so `object()` builds `z.strictObject(...)` and no eager `.shape` read hits a deferred getter.
1363
1159
  */
1364
1160
  function strictOneOfNode(node) {
@@ -1371,13 +1167,14 @@ function strictOneOfNode(node) {
1371
1167
  * The constraint chain an `allOf` member adds to a bare string, number or array; each printer passes its own renderers.
1372
1168
  */
1373
1169
  function getMemberConstraint({ member, regexType, lengths, numbers }) {
1170
+ if (hasInputChecks(member) || _kubb_kit.ast.schema.narrow(member, "array")?.contains) return void 0;
1374
1171
  if (member.primitive === "string") return lengths({
1375
- ..._kubb_kit.ast.narrowSchema(member, "string") ?? {},
1172
+ ..._kubb_kit.ast.schema.narrow(member, "string") ?? {},
1376
1173
  regexType
1377
1174
  }) || void 0;
1378
- if (member.primitive === "number" || member.primitive === "integer") return numbers(_kubb_kit.ast.narrowSchema(member, "number") ?? _kubb_kit.ast.narrowSchema(member, "integer") ?? {}) || void 0;
1175
+ if (member.primitive === "number" || member.primitive === "integer") return numbers(_kubb_kit.ast.schema.narrow(member, "number") ?? _kubb_kit.ast.schema.narrow(member, "integer") ?? {}) || void 0;
1379
1176
  if (member.primitive === "array") return lengths({
1380
- ..._kubb_kit.ast.narrowSchema(member, "array") ?? {},
1177
+ ..._kubb_kit.ast.schema.narrow(member, "array") ?? {},
1381
1178
  regexType
1382
1179
  }) || void 0;
1383
1180
  }
@@ -1385,24 +1182,26 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
1385
1182
  * Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
1386
1183
  */
1387
1184
  function buildObjectShape(ctx, node, renderProperty) {
1388
- const objectNode = _kubb_kit.ast.narrowSchema(node, "object");
1185
+ const objectNode = _kubb_kit.ast.schema.narrow(node, "object");
1389
1186
  if (!objectNode) return "{}";
1390
- const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, _kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
1391
- return buildObject(mapSchemaProperties(objectNode, (schema) => {
1187
+ const isCyclic = (schema) => ctx.options.cyclicSchemas != null && _kubb_kit.ast.graph.containsCircular(schema, { circularSchemas: ctx.options.cyclicSchemas });
1188
+ const transformed = objectNode.properties.map(({ schema }) => {
1392
1189
  const hasSelfRef = isCyclic(schema);
1393
1190
  const savedCyclicSchemas = ctx.options.cyclicSchemas;
1394
1191
  if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
1395
1192
  const baseOutput = ctx.transform(schema) ?? ctx.transform(_kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1396
1193
  if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
1397
1194
  return baseOutput;
1398
- }).map(({ name, property, output: value }) => {
1399
- const { schema } = property;
1195
+ });
1196
+ return buildObject(objectNode.properties.map((property, index) => {
1197
+ const { name, schema } = property;
1198
+ const value = transformed[index];
1400
1199
  return renderProperty({
1401
1200
  name,
1402
1201
  value,
1403
1202
  schema,
1404
1203
  required: property.required,
1405
- meta: (0, _kubb_kit.syncSchemaRef)(schema),
1204
+ meta: _kubb_kit.ast.ref.mergeSchema(schema),
1406
1205
  cyclic: isCyclic(schema)
1407
1206
  });
1408
1207
  }));
@@ -1414,14 +1213,14 @@ function printWithOmit(ctx, node, modify, mini = false) {
1414
1213
  const { keysToOmit } = ctx.options;
1415
1214
  const transformed = ctx.transform(node);
1416
1215
  if (!transformed) return null;
1417
- const meta = (0, _kubb_kit.syncSchemaRef)(node);
1216
+ const meta = _kubb_kit.ast.ref.mergeSchema(node);
1418
1217
  return modify((() => {
1419
1218
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
1420
1219
  const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
1421
1220
  const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
1422
1221
  const omit = (target) => {
1423
1222
  const inner = `${target}${unwrap}`;
1424
- const hasObjectConstraints = meta.type === "object" && (meta.minProperties !== void 0 || meta.maxProperties !== void 0);
1223
+ const hasObjectConstraints = hasInputChecks(meta);
1425
1224
  const pipe = mini ? `${inner}._zod.def` : inner;
1426
1225
  const object = hasObjectConstraints ? `${pipe}.out` : inner;
1427
1226
  const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
@@ -1440,7 +1239,7 @@ function printWithOmit(ctx, node, modify, mini = false) {
1440
1239
  function getLiteralUnion({ node, options }) {
1441
1240
  if (node.strategy === "one" && options.oneOfType === "xor" || options.nodes?.enum) return void 0;
1442
1241
  const values = (node.members ?? []).map((member) => {
1443
- const enumNode = _kubb_kit.ast.narrowSchema(member, _kubb_kit.ast.schemaTypes.enum);
1242
+ const enumNode = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.enum);
1444
1243
  return enumNode && !enumNode.name && !enumNode.nullable && !enumNode.namedEnumValues && enumNode.enumValues?.length === 1 ? enumNode.enumValues[0] : void 0;
1445
1244
  });
1446
1245
  if (!values.length || !values.every((value) => value !== void 0 && value !== null)) return void 0;
@@ -1453,11 +1252,11 @@ function getLiteralUnion({ node, options }) {
1453
1252
  * Whether a discriminated union member's discriminator property prints as a coerced literal union.
1454
1253
  */
1455
1254
  function hasCoercedDiscriminator({ member, propertyName, options }) {
1456
- return _kubb_kit.ast.resolveSchemaProperties({
1255
+ return _kubb_kit.ast.schema.findProperties({
1457
1256
  node: member,
1458
1257
  propertyName
1459
1258
  }).some(({ schema }) => {
1460
- const resolved = (0, _kubb_kit.syncSchemaRef)(schema);
1259
+ const resolved = _kubb_kit.ast.ref.mergeSchema(schema);
1461
1260
  return resolved.type === "union" && !!getLiteralUnion({
1462
1261
  node: resolved,
1463
1262
  options
@@ -1465,7 +1264,48 @@ function hasCoercedDiscriminator({ member, propertyName, options }) {
1465
1264
  });
1466
1265
  }
1467
1266
  /**
1468
- * Builds the scalar handlers both dialects share. They never recurse into children. The temporal
1267
+ * Whether `z.compile(schema, { strict: true })` throws for the node. Zod runs an unsupported schema below an object
1268
+ * property or an array, tuple or record item on its runtime parser, so only a coerced scalar or a `z.xor` that compiles
1269
+ * inline with the root throws: the node itself, or one reached through refs and union or intersection members.
1270
+ * `siblings` resolves a generated ref, which names another schema of the same file and carries no target.
1271
+ *
1272
+ * Mirrors the built-in handlers, so a type with a `printer.nodes` override is judged as if it printed the built-in output.
1273
+ */
1274
+ function throwsOnStrictCompile({ node, options, siblings, seen = /* @__PURE__ */ new Set() }) {
1275
+ if (!node || seen.has(node)) return false;
1276
+ seen.add(node);
1277
+ const throws = (child) => throwsOnStrictCompile({
1278
+ node: child,
1279
+ options,
1280
+ siblings,
1281
+ seen
1282
+ });
1283
+ switch (node.type) {
1284
+ case _kubb_kit.ast.schema.types.ref: return throws(node.schema ?? (node.ref || !node.name ? void 0 : siblings?.get(node.name)));
1285
+ case _kubb_kit.ast.schema.types.union:
1286
+ if (node.strategy === "one" && options.oneOfType === "xor") return true;
1287
+ return !!getLiteralUnion({
1288
+ node,
1289
+ options
1290
+ })?.coerce || (node.members ?? []).some(throws);
1291
+ case _kubb_kit.ast.schema.types.intersection: return (node.members ?? []).some(throws);
1292
+ case _kubb_kit.ast.schema.types.bigint: return true;
1293
+ case _kubb_kit.ast.schema.types.string: return shouldCoerce(options.coercion, "strings");
1294
+ case _kubb_kit.ast.schema.types.number:
1295
+ case _kubb_kit.ast.schema.types.integer: return shouldCoerce(options.coercion, "numbers");
1296
+ case _kubb_kit.ast.schema.types.date: return node.representation === "date" && options.direction !== "encode" && shouldCoerce(options.coercion, "dates");
1297
+ default: return false;
1298
+ }
1299
+ }
1300
+ function hasPlainDiscriminator({ member, propertyName }) {
1301
+ const schemas = _kubb_kit.ast.schema.findProperties({
1302
+ node: member,
1303
+ propertyName
1304
+ });
1305
+ return schemas.length > 0 && !schemas.some(({ schema }) => _kubb_kit.ast.schema.getLiterals(_kubb_kit.ast.ref.mergeSchema(schema)).length > 0);
1306
+ }
1307
+ /**
1308
+ * Builds the scalar handlers both dialects share. Only a string with a `contentSchema` recurses into a child. The temporal
1469
1309
  * scalars and `bigint` differ per dialect, so each printer adds its own.
1470
1310
  */
1471
1311
  function buildScalarNodes(dialect) {
@@ -1478,18 +1318,20 @@ function buildScalarNodes(dialect) {
1478
1318
  null: () => "z.null()",
1479
1319
  string(node) {
1480
1320
  const coerce = shouldCoerce(this.options.coercion, "strings");
1481
- const call = coerce ? void 0 : STRING_FORMAT_CALLS.get(node.format);
1482
- if (call) return `${call}${dialect.lengths({
1483
- ...node,
1484
- regexType: this.options.regexType
1485
- })}`;
1486
- const base = coerce ? "z.coerce.string()" : "z.string()";
1321
+ const base = (coerce ? void 0 : stringFormatCalls.get(node.format) ?? contentEncodingCalls.get(node.content?.encoding?.toLowerCase())) ?? (coerce ? "z.coerce.string()" : "z.string()");
1487
1322
  const pattern = node.pattern ?? integerFormatPattern(node.format);
1488
- return `${base}${dialect.lengths({
1323
+ const value = `${base}${dialect.lengths({
1489
1324
  ...node,
1490
1325
  pattern,
1491
1326
  regexType: this.options.regexType
1492
1327
  })}`;
1328
+ const contentNode = _kubb_kit.ast.schema.getEmbeddedJson(node);
1329
+ if (!contentNode) return value;
1330
+ const direction = this.options.direction;
1331
+ this.options.direction = "decode";
1332
+ const contentSchema = this.transform(contentNode);
1333
+ this.options.direction = direction;
1334
+ return contentSchema ? dialect.refine(value, jsonContentCheck(contentSchema)) : value;
1493
1335
  },
1494
1336
  number(node) {
1495
1337
  return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${dialect.numbers(node)}`;
@@ -1522,7 +1364,7 @@ function buildScalarNodes(dialect) {
1522
1364
  ipv6: () => "z.ipv6()",
1523
1365
  blob: () => "z.instanceof(Blob)",
1524
1366
  enum(node) {
1525
- const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
1367
+ const nonNullValues = _kubb_kit.ast.schema.getLiterals(node).filter((v) => v !== null);
1526
1368
  if (node.namedEnumValues?.length) {
1527
1369
  const literals = nonNullValues.map((v) => buildLiteral(v));
1528
1370
  if (literals.length === 1) return literals[0];
@@ -1541,11 +1383,12 @@ function buildScalarNodes(dialect) {
1541
1383
  */
1542
1384
  function buildZodNodes(dialect, cyclicSchemas) {
1543
1385
  const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
1386
+ const printNullable = (schema, output) => output && schema.nullable ? dialect.nullable(output) : output;
1544
1387
  return {
1545
1388
  ...buildScalarNodes(dialect),
1546
1389
  ref(node) {
1547
1390
  if (!node.name) return null;
1548
- const refName = _kubb_kit.ast.resolveRefName(node);
1391
+ const refName = _kubb_kit.ast.ref.getName(node);
1549
1392
  if (!refName) return null;
1550
1393
  const useInputVariant = node.ref != null && (dialect.useInputVariant?.(node, this.options) ?? false);
1551
1394
  const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
@@ -1560,47 +1403,50 @@ function buildZodNodes(dialect, cyclicSchemas) {
1560
1403
  const isStrict = node.additionalProperties === false && patterns.length === 0;
1561
1404
  const isLoose = node.additionalProperties === true;
1562
1405
  const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${dialect.objectShape(this, node)})`;
1563
- return objectConstraints({
1564
- value: (() => {
1565
- const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1566
- if (node.additionalProperties && node.additionalProperties !== true) {
1567
- const catchallType = this.transform(node.additionalProperties);
1568
- if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1569
- return catchallType ? dialect.catchall(objectBase, catchallType) : objectBase;
1570
- }
1571
- if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1572
- const unknownType = this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schemaTypes.unknown }));
1573
- return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1574
- }
1575
- if (node.additionalProperties === true) return objectBase;
1576
- if (isStrict) return objectBase;
1577
- if (patterns.length > 0) {
1578
- const values = patterns.map(([, valueSchema]) => {
1579
- const valueType = this.transform(valueSchema) ?? this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schemaTypes.unknown }));
1580
- return valueSchema.nullable ? dialect.nullable(valueType) : valueType;
1581
- });
1582
- const distinct = [...new Set(values)];
1583
- const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1584
- if (entries.length > 0) return dialect.catchall(objectBase, value);
1585
- return `z.record(${dialect.patternKey({
1586
- patterns: patterns.map(([pattern]) => pattern),
1587
- regexType: this.options.regexType
1588
- })}, ${value})`;
1589
- }
1590
- return objectBase;
1591
- })(),
1592
- node
1593
- });
1406
+ return (() => {
1407
+ const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1408
+ if (node.additionalProperties && node.additionalProperties !== true) {
1409
+ const catchallType = this.transform(node.additionalProperties);
1410
+ if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1411
+ return catchallType ? dialect.catchall(objectBase, catchallType) : objectBase;
1412
+ }
1413
+ if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1414
+ const unknownType = this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }));
1415
+ return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1416
+ }
1417
+ if (node.additionalProperties === true) return objectBase;
1418
+ if (isStrict) return objectBase;
1419
+ if (patterns.length > 0) {
1420
+ const values = patterns.map(([, valueSchema]) => {
1421
+ const valueType = this.transform(valueSchema) ?? this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }));
1422
+ return valueSchema.nullable ? dialect.nullable(valueType) : valueType;
1423
+ });
1424
+ const distinct = [...new Set(values)];
1425
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1426
+ if (entries.length > 0) return dialect.catchall(objectBase, value);
1427
+ return `z.record(${dialect.patternKey({
1428
+ patterns: patterns.map(([pattern]) => pattern),
1429
+ regexType: this.options.regexType
1430
+ })}, ${value})`;
1431
+ }
1432
+ return objectBase;
1433
+ })();
1594
1434
  },
1595
1435
  array(node) {
1596
- const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schemaTypes.unknown }))})${dialect.lengths({
1436
+ const base = `z.array(${(node.items ?? []).map((item) => printNullable(item, this.transform(item))).filter(Boolean).join(", ") || this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }))})${dialect.lengths({
1597
1437
  ...node,
1598
1438
  regexType: this.options.regexType
1599
1439
  })}`;
1600
- return node.unique ? dialect.unique(base) : base;
1440
+ const unique = node.unique ? dialect.unique(base) : base;
1441
+ const contains = node.contains && containsCheck(this, node.contains);
1442
+ return contains ? dialect.refine(unique, contains) : unique;
1601
1443
  },
1602
1444
  tuple(node) {
1603
- return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1445
+ const items = (node.items ?? []).map((item) => printNullable(item, this.transform(item))).filter(Boolean);
1446
+ const rest = node.rest ? this.transform(node.rest) : void 0;
1447
+ const tuple = `z.tuple(${buildList(items)}${rest ? `, ${rest}` : ""})`;
1448
+ const contains = node.contains && containsCheck(this, node.contains);
1449
+ return contains ? dialect.refine(tuple, contains) : tuple;
1604
1450
  },
1605
1451
  union(node) {
1606
1452
  const literalUnion = getLiteralUnion({
@@ -1613,24 +1459,30 @@ function buildZodNodes(dialect, cyclicSchemas) {
1613
1459
  }
1614
1460
  const nodeMembers = node.members ?? [];
1615
1461
  const { discriminatorPropertyName } = node;
1616
- const isDiscriminated = !!discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas)) && !nodeMembers.some((m) => hasCoercedDiscriminator({
1462
+ const isDiscriminated = !!discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas) && !hasPlainDiscriminator({
1463
+ member: m,
1464
+ propertyName: discriminatorPropertyName
1465
+ })) && !nodeMembers.some((m) => hasCoercedDiscriminator({
1617
1466
  member: m,
1618
1467
  propertyName: discriminatorPropertyName,
1619
1468
  options: this.options
1620
1469
  }));
1621
1470
  const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
1622
- const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
1623
- const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ schema, output }) => output && isStrict && dialect.strictMember ? dialect.strictMember(output, schema, cyclicSchemas) : output).filter(Boolean);
1471
+ const isStrict = node.strategy === "one" && !isXor && !isDiscriminated && !unionsWithSiblings.has(node);
1472
+ const members = (node.members ?? []).map((memberNode) => {
1473
+ const output = this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode);
1474
+ return output && isStrict && dialect.strictMember ? dialect.strictMember(output, memberNode, cyclicSchemas) : output;
1475
+ }).map((output, index) => printNullable(nodeMembers[index], output)).filter(Boolean);
1624
1476
  if (members.length === 0) return "";
1625
1477
  if (members.length === 1) return members[0];
1626
- if (isDiscriminated) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
1478
+ if (isDiscriminated) return `z.discriminatedUnion(${_kubb_kit.text.quote(node.discriminatorPropertyName)}, ${buildList(members)})`;
1627
1479
  if (isXor) return `z.xor(${buildList(members)})`;
1628
1480
  return `z.union(${buildList(members)})`;
1629
1481
  },
1630
1482
  intersection(node) {
1631
1483
  const members = node.members ?? [];
1632
1484
  if (members.length === 0) return "";
1633
- const hasSiblingProperties = members.some((member) => member.type === _kubb_kit.ast.schemaTypes.object && member.properties.length > 0);
1485
+ const hasSiblingProperties = members.some((member) => member.type === _kubb_kit.ast.schema.types.object && member.properties.length > 0);
1634
1486
  const transform = (member) => {
1635
1487
  if (!hasSiblingProperties) return this.transform(member);
1636
1488
  unionsWithSiblings.add(member);
@@ -1644,7 +1496,18 @@ function buildZodNodes(dialect, cyclicSchemas) {
1644
1496
  if (!first) return "";
1645
1497
  const firstBase = transform(first);
1646
1498
  if (!firstBase) return "";
1647
- if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemas)) return preserveRequiredProperties(members).slice(1).reduce((acc, member) => dialect.extend(acc, dialect.objectShape(this, member)), firstBase);
1499
+ if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemas)) {
1500
+ const discriminator = _kubb_kit.ast.schema.narrow(first, _kubb_kit.ast.schema.types.union)?.discriminatorPropertyName;
1501
+ return preserveRequiredProperties(members).slice(1).reduce((acc, member) => {
1502
+ const object = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.object);
1503
+ if (!discriminator || !object?.properties.some((property) => property.name === discriminator)) return dialect.extend(acc, dialect.objectShape(this, member));
1504
+ const properties = object.properties.filter((property) => property.name !== discriminator);
1505
+ return properties.length ? dialect.extend(acc, dialect.objectShape(this, {
1506
+ ...object,
1507
+ properties
1508
+ })) : acc;
1509
+ }, firstBase);
1510
+ }
1648
1511
  return rest.reduce((acc, member) => {
1649
1512
  const constraint = getMemberConstraint({
1650
1513
  member,
@@ -1659,15 +1522,162 @@ function buildZodNodes(dialect, cyclicSchemas) {
1659
1522
  }
1660
1523
  };
1661
1524
  }
1525
+ function containsCheck(ctx, { schema, min = 1, max }) {
1526
+ if (min === 0 && max === void 0) return null;
1527
+ const countBounds = [`count >= ${min}`, max !== void 0 && `count <= ${max}`].filter(Boolean).join(" && ");
1528
+ const message = max !== void 0 ? `Array must contain ${min} to ${max} matching items` : `Array must contain at least ${min} matching items`;
1529
+ return `(items) => { const count = items.filter((item) => ${ctx.transform(schema) ?? "z.unknown()"}.safeParse(item).success).length; return ${countBounds} }, { message: ${_kubb_kit.text.quote(message)} }`;
1530
+ }
1531
+ function conditionExpression(ctx, schema) {
1532
+ if (!schema) return "true";
1533
+ const object = _kubb_kit.ast.schema.narrow(schema, _kubb_kit.ast.schema.types.object);
1534
+ const keyOnlyNames = object?.properties.filter((property) => property.required && property.schema.type === _kubb_kit.ast.schema.types.unknown).map((property) => property.name) ?? [];
1535
+ const restSchema = object ? {
1536
+ ...object,
1537
+ properties: object.properties.filter((property) => !keyOnlyNames.includes(property.name))
1538
+ } : schema;
1539
+ const isOnlyKeys = keyOnlyNames.length > 0 && restSchema.type === "object" && restSchema.properties.length === 0 && restSchema.additionalProperties === void 0 && !restSchema.patternProperties && !restSchema.propertyNames && !hasInputChecks(restSchema);
1540
+ return [keyOnlyNames.length > 0 && `typeof value === "object" && value !== null && ${JSON.stringify(keyOnlyNames)}.every((key) => key in value)`, !isOnlyKeys && `${ctx.transform(restSchema) ?? "z.unknown()"}.safeParse(value).success`].filter(Boolean).join(" && ");
1541
+ }
1542
+ /**
1543
+ * Wraps each handler so its output first checks the raw input, see {@link inputChecks}.
1544
+ */
1545
+ function withInputChecks(nodes) {
1546
+ const wrapHandler = (type) => {
1547
+ const handler = nodes[type];
1548
+ if (!handler) return [];
1549
+ const checkedHandler = function(node) {
1550
+ const output = handler.call(this, node);
1551
+ if (!output || !hasInputChecks(node)) return output;
1552
+ return inputChecks({
1553
+ value: output,
1554
+ node,
1555
+ keySchema: hasKeyCheck(node) ? this.transform(node.propertyNames) : void 0,
1556
+ conditionals: (node.conditionals ?? []).map((entry) => `z.refine((value) => (${conditionExpression(this, entry.if)} ? ${conditionExpression(this, entry.then)} : ${conditionExpression(this, entry.else)}), { message: "Value does not meet a conditional schema" })`)
1557
+ });
1558
+ };
1559
+ return [[type, checkedHandler]];
1560
+ };
1561
+ return Object.fromEntries(Object.values(_kubb_kit.ast.schema.types).flatMap(wrapHandler));
1562
+ }
1563
+ // src/components/Zod.tsx
1564
+ /**
1565
+ * Whether the schema reaches a circular schema through its refs. `containsCircular` reads the refs the node holds,
1566
+ * while Zod refuses to compile a cycle anywhere below the root. A generated ref carries no target, so `siblings` resolves it.
1567
+ */
1568
+ function reachesCycle({ node, siblings, cyclicSchemas, seen = /* @__PURE__ */ new Set() }) {
1569
+ if (!cyclicSchemas?.size || seen.has(node)) return false;
1570
+ seen.add(node);
1571
+ return _kubb_kit.ast.schema.find(node, {
1572
+ followRefs: true,
1573
+ predicate(schema) {
1574
+ if (schema.type !== _kubb_kit.ast.schema.types.ref) return false;
1575
+ const name = _kubb_kit.ast.ref.getName(schema);
1576
+ if (name && cyclicSchemas.has(name)) return true;
1577
+ const sibling = schema.ref || !schema.name ? void 0 : siblings?.get(schema.name);
1578
+ return !!sibling && reachesCycle({
1579
+ node: sibling,
1580
+ siblings,
1581
+ cyclicSchemas,
1582
+ seen
1583
+ });
1584
+ }
1585
+ }) !== void 0;
1586
+ }
1587
+ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile, siblings }) {
1588
+ const output = printer.print(node);
1589
+ const printerImports = printer.drainImports();
1590
+ if (!output) return;
1591
+ const needsAnnotation = cyclic && node.type !== "object";
1592
+ const isAlias = _kubb_kit.ast.ref.isAlias(node, printer.options.keysToOmit);
1593
+ const isCyclic = Boolean(cyclic || printer.options.cyclicSchemas && _kubb_kit.ast.graph.containsCircular(node, { circularSchemas: printer.options.cyclicSchemas }));
1594
+ const isStrict = typeof compile === "object" && Boolean(compile.strict);
1595
+ const throwsOnCompile = isStrict && (throwsOnStrictCompile({
1596
+ node,
1597
+ options: printer.options,
1598
+ siblings
1599
+ }) || reachesCycle({
1600
+ node,
1601
+ siblings,
1602
+ cyclicSchemas: printer.options.cyclicSchemas
1603
+ }));
1604
+ const value = Boolean(compile) && !isAlias && !isCyclic && !throwsOnCompile ? isStrict ? `z.compile(${output}, { strict: true })` : `z.compile(${output})` : output;
1605
+ const targetType = inferTypeName ?? `z.infer<typeof ${name}>`;
1606
+ const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
1607
+ const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
1608
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
1609
+ printerImports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1610
+ name: imp.name,
1611
+ path: imp.path,
1612
+ root: imp.root,
1613
+ isTypeOnly: imp.isTypeOnly,
1614
+ isNameSpace: imp.isNameSpace
1615
+ }, [
1616
+ name,
1617
+ imp.path,
1618
+ imp.name
1619
+ ].join("-"))),
1620
+ /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1621
+ name,
1622
+ isExportable: true,
1623
+ isIndexable: true,
1624
+ children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
1625
+ export: true,
1626
+ name,
1627
+ type: needsAnnotation ? mini ? "z.ZodMiniType" : "z.ZodType" : void 0,
1628
+ children: value
1629
+ })
1630
+ }),
1631
+ inferTypeName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1632
+ name: inferTypeName,
1633
+ isExportable: true,
1634
+ isIndexable: true,
1635
+ isTypeOnly: true,
1636
+ children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Type, {
1637
+ export: true,
1638
+ name: inferTypeName,
1639
+ children: `z.${inferKind}<typeof ${name}>`
1640
+ })
1641
+ }),
1642
+ shouldGenerateIs && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1643
+ name: isName,
1644
+ isExportable: true,
1645
+ isIndexable: true,
1646
+ children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
1647
+ export: true,
1648
+ name: isName,
1649
+ JSDoc: { comments: [`Type guard for {@link ${name}}`] },
1650
+ children: mini ? `(data: unknown): data is ${targetType} => z.validate(${name}, data)` : `(data: unknown): data is ${targetType} => ${name}.validate(data)`
1651
+ })
1652
+ }),
1653
+ shouldGenerateAssert && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1654
+ name: assertName,
1655
+ isExportable: true,
1656
+ isIndexable: true,
1657
+ children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Function, {
1658
+ export: true,
1659
+ name: assertName,
1660
+ params: "data: unknown",
1661
+ returnType: `asserts data is ${targetType}`,
1662
+ JSDoc: { comments: [`Asserter for {@link ${name}}`, "@throws {z.ZodError} If data is invalid"] },
1663
+ children: mini ? `if (!z.validate(${name}, data)) {\n z.parse(${name}, data)\n}` : `if (!${name}.validate(data)) {\n ${name}.parse(data)\n}`
1664
+ })
1665
+ })
1666
+ ] });
1667
+ }
1662
1668
  // src/printers/printerZod.ts
1663
1669
  function strictOneOfMember(member, node, cyclicSchemas) {
1664
- if (node.type === _kubb_kit.ast.schemaTypes.ref) {
1670
+ if (node.type === _kubb_kit.ast.schema.types.ref) {
1665
1671
  if (member.startsWith("z.lazy(")) return member;
1666
- const refName = _kubb_kit.ast.resolveRefName(node);
1672
+ const refName = _kubb_kit.ast.ref.getName(node);
1667
1673
  if (refName && cyclicSchemas?.has(refName)) return member;
1668
- const schema = (0, _kubb_kit.syncSchemaRef)(node);
1674
+ const schema = _kubb_kit.ast.ref.mergeSchema(node);
1669
1675
  if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
1670
- if (schema.type === _kubb_kit.ast.schemaTypes.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
1676
+ if (schema.type === _kubb_kit.ast.schema.types.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) {
1677
+ const checks = [node, node.schema].filter((entry) => entry && hasInputChecks(entry)).length;
1678
+ if (checks === 2) return member;
1679
+ return checks ? `z.pipe(${member}.in, ${member}.out.strict())` : `${member}.strict()`;
1680
+ }
1671
1681
  }
1672
1682
  return member;
1673
1683
  }
@@ -1683,7 +1693,7 @@ function buildZodObjectShape(ctx, node) {
1683
1693
  optional: schema.optional || required === false,
1684
1694
  nullish: schema.nullish,
1685
1695
  defaultValue: meta.default,
1686
- description: schema.type !== _kubb_kit.ast.schemaTypes.ref && meta.type === _kubb_kit.ast.schemaTypes.ref ? void 0 : meta.description,
1696
+ description: schema.type !== _kubb_kit.ast.schema.types.ref && meta.type === _kubb_kit.ast.schema.types.ref ? void 0 : meta.description,
1687
1697
  examples: meta.examples
1688
1698
  });
1689
1699
  if (!cyclic) return `${objectKey(name)}: ${body}`;
@@ -1704,21 +1714,22 @@ function buildZodObjectShape(ctx, node) {
1704
1714
  function recursiveGetterType({ ctx, schema, required }) {
1705
1715
  function baseType(schema) {
1706
1716
  if (ctx.options.nodes?.[schema.type]) return void 0;
1707
- if (schema.type === _kubb_kit.ast.schemaTypes.ref) {
1717
+ if (schema.type === _kubb_kit.ast.schema.types.ref) {
1708
1718
  const saved = ctx.options.cyclicSchemas;
1709
1719
  ctx.options.cyclicSchemas = void 0;
1710
1720
  const name = ctx.transform(schema);
1711
1721
  ctx.options.cyclicSchemas = saved;
1712
1722
  return name ? `typeof ${name}` : void 0;
1713
1723
  }
1714
- if (schema.type === _kubb_kit.ast.schemaTypes.array && schema.items?.length === 1) {
1724
+ if (schema.type === _kubb_kit.ast.schema.types.array && schema.items?.length === 1) {
1715
1725
  const item = baseType(schema.items[0]);
1716
- return item ? `z.ZodArray<${item}>` : void 0;
1726
+ if (!item) return void 0;
1727
+ return `z.ZodArray<${schema.items[0].nullable ? `z.ZodNullable<${item}>` : item}>`;
1717
1728
  }
1718
1729
  }
1719
1730
  const base = baseType(schema);
1720
1731
  if (!base) return void 0;
1721
- const meta = (0, _kubb_kit.syncSchemaRef)(schema);
1732
+ const meta = _kubb_kit.ast.ref.mergeSchema(schema);
1722
1733
  const optional = schema.optional || required === false;
1723
1734
  const wrapped = (() => {
1724
1735
  if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
@@ -1733,12 +1744,12 @@ function recursiveGetterType({ ctx, schema, required }) {
1733
1744
  * `direction` themselves, so {@link containsDirectionalNode} probes their children instead.
1734
1745
  */
1735
1746
  const CONTAINER_TYPES = /* @__PURE__ */ new Set([
1736
- _kubb_kit.ast.schemaTypes.object,
1737
- _kubb_kit.ast.schemaTypes.array,
1738
- _kubb_kit.ast.schemaTypes.tuple,
1739
- _kubb_kit.ast.schemaTypes.union,
1740
- _kubb_kit.ast.schemaTypes.intersection,
1741
- _kubb_kit.ast.schemaTypes.ref
1747
+ _kubb_kit.ast.schema.types.object,
1748
+ _kubb_kit.ast.schema.types.array,
1749
+ _kubb_kit.ast.schema.types.tuple,
1750
+ _kubb_kit.ast.schema.types.union,
1751
+ _kubb_kit.ast.schema.types.intersection,
1752
+ _kubb_kit.ast.schema.types.ref
1742
1753
  ]);
1743
1754
  /**
1744
1755
  * Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
@@ -1769,22 +1780,31 @@ function variesByDirection({ node, printerOptions }) {
1769
1780
  */
1770
1781
  function containsDirectionalNode({ node, printerOptions }) {
1771
1782
  if (!node) return false;
1772
- return _kubb_kit.ast.findSchema(node, {
1783
+ return _kubb_kit.ast.schema.find(node, {
1773
1784
  predicate: (schema) => variesByDirection({
1774
1785
  node: schema,
1775
1786
  printerOptions
1776
1787
  }),
1777
- followRefs: true
1788
+ followRefs: true,
1789
+ cache: getDirectionalCache(printerOptions)
1778
1790
  }) !== void 0;
1779
1791
  }
1780
1792
  /**
1793
+ * The `find` cache for {@link containsDirectionalNode}, one per plugin per build and per option set
1794
+ * the probe reads. A `printer.nodes` override has no string key, so it searches without a cache.
1795
+ */
1796
+ function getDirectionalCache({ resolver, nodes, coercion, dateType }) {
1797
+ if (!resolver || nodes) return void 0;
1798
+ return resolver.memo(`zodDirectionalNodes:${shouldCoerce(coercion, "dates")}:${dateType}`, () => /* @__PURE__ */ new WeakMap());
1799
+ }
1800
+ /**
1781
1801
  * Names of the `$ref` schemas the generator should route to their input (encode) variant.
1782
1802
  */
1783
1803
  function collectDirectionalRefNames({ node, printerOptions }) {
1784
- return _kubb_kit.ast.collectSync(node, { schema: (n) => n.type === _kubb_kit.ast.schemaTypes.ref && n.ref && containsDirectionalNode({
1804
+ return _kubb_kit.ast.node.collect(node, { schema: (n) => n.type === _kubb_kit.ast.schema.types.ref && n.ref && containsDirectionalNode({
1785
1805
  node: n,
1786
1806
  printerOptions
1787
- }) ? _kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
1807
+ }) ? _kubb_kit.ast.ref.getName(n) ?? void 0 : void 0 });
1788
1808
  }
1789
1809
  /**
1790
1810
  * Handlers that never recurse into children, so {@link variesByDirection} can call one directly
@@ -1806,6 +1826,7 @@ const standardDialect = {
1806
1826
  coercedNumber: (schema) => `z.coerce.number().pipe(${schema})`,
1807
1827
  catchall: (object, value) => `${object}.catchall(${value})`,
1808
1828
  nullable: (value) => `${value}.nullable()`,
1829
+ refine: (value, check) => `${value}.refine(${check})`,
1809
1830
  unique: (array) => `${array}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`,
1810
1831
  extend: (base, shape) => `${base}.extend(${shape})`,
1811
1832
  intersection: (left, right) => `${left}.and(${right})`,
@@ -1817,8 +1838,8 @@ const standardDialect = {
1817
1838
  };
1818
1839
  const scalarNodes = {
1819
1840
  ...buildScalarNodes(standardDialect),
1820
- bigint() {
1821
- return "z.coerce.bigint()";
1841
+ bigint(node) {
1842
+ return `z.coerce.bigint()${numberConstraints(bigintConstraints(node), "n")}`;
1822
1843
  },
1823
1844
  date(node) {
1824
1845
  if (node.representation !== "date") return "z.iso.date()";
@@ -1851,14 +1872,14 @@ const scalarNodes = {
1851
1872
  * const code = printer.print(stringNode) // "z.string()"
1852
1873
  * ```
1853
1874
  */
1854
- const printerZod = _kubb_kit.ast.createPrinter((options) => {
1875
+ const printerZod = (0, _kubb_kit.createPrinter)((options) => {
1855
1876
  return {
1856
1877
  name: "zod",
1857
1878
  options,
1858
- nodes: {
1879
+ nodes: withInputChecks({
1859
1880
  ...buildZodNodes(standardDialect, options.cyclicSchemas),
1860
1881
  ...scalarNodes
1861
- },
1882
+ }),
1862
1883
  overrides: options.nodes,
1863
1884
  print(node) {
1864
1885
  return printWithOmit(this, node, (value, meta) => applyModifiers({
@@ -1915,18 +1936,19 @@ const miniDialect = {
1915
1936
  coercedNumber: (schema) => `z.pipe(z.coerce.number(), ${schema})`,
1916
1937
  catchall: (object, value) => `z.catchall(${object}, ${value})`,
1917
1938
  nullable: (value) => `z.nullable(${value})`,
1939
+ refine: (value, check) => `${value}.check(z.refine(${check}))`,
1918
1940
  unique: (array) => `${array}.check(z.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" }))`,
1919
1941
  extend: (base, shape) => `z.extend(${base}, ${shape})`,
1920
1942
  intersection: (left, right) => `z.intersection(${left}, ${right})`
1921
1943
  };
1922
- const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
1944
+ const printerZodMini = (0, _kubb_kit.createPrinter)((options) => {
1923
1945
  return {
1924
1946
  name: "zod-mini",
1925
1947
  options,
1926
- nodes: {
1948
+ nodes: withInputChecks({
1927
1949
  ...buildZodNodes(miniDialect, options.cyclicSchemas),
1928
1950
  bigint(node) {
1929
- return `z.coerce.bigint()${numberChecksMini(node)}`;
1951
+ return `z.coerce.bigint()${numberChecksMini(bigintConstraints(node), "n")}`;
1930
1952
  },
1931
1953
  date(node) {
1932
1954
  if (node.representation === "string") return "z.iso.date()";
@@ -1939,7 +1961,7 @@ const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
1939
1961
  if (node.representation === "string") return "z.iso.time()";
1940
1962
  return "z.date()";
1941
1963
  }
1942
- },
1964
+ }),
1943
1965
  overrides: options.nodes,
1944
1966
  print(node) {
1945
1967
  return printWithOmit(this, node, (value, meta) => applyMiniModifiers({
@@ -2026,7 +2048,8 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2026
2048
  node,
2027
2049
  root,
2028
2050
  output,
2029
- group: group ?? void 0
2051
+ group: group ?? void 0,
2052
+ checks: true
2030
2053
  });
2031
2054
  const inputImportEntries = hasDirectionalNode ? [...directionalRefNames].map((schemaName) => ({
2032
2055
  name: [resolver.schema.inputName(schemaName)],
@@ -2117,7 +2140,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2117
2140
  });
2118
2141
  },
2119
2142
  operation(node, ctx) {
2120
- if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
2143
+ if (!_kubb_kit.ast.http.isOperation(node)) return null;
2121
2144
  const { adapter, config, resolver, root } = ctx;
2122
2145
  const { output, coercion, guidType, regexType, oneOfType, compile, mini, inferred, importPath, group, printer } = ctx.options;
2123
2146
  const dateType = getOasAdapter(adapter).options.dateType;
@@ -2140,23 +2163,26 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2140
2163
  cyclicSchemas: getCircularNames(ctx),
2141
2164
  nodes: printer?.nodes
2142
2165
  };
2166
+ const siblings = /* @__PURE__ */ new Map();
2143
2167
  function renderSchemaEntry({ schema, name, keysToOmit, direction = "decode" }) {
2144
2168
  if (!schema) return null;
2169
+ siblings.set(name, schema);
2145
2170
  const directionalRefNames = direction === "encode" && !mini ? new Set(collectDirectionalRefNames({
2146
2171
  node: schema,
2147
2172
  printerOptions
2148
2173
  })) : null;
2174
+ const schemaPrinter = getPrinter(resolver, {
2175
+ ...printerOptions,
2176
+ keysToOmit
2177
+ }, mini, direction);
2149
2178
  const imports = resolver.imports({
2150
2179
  node: schema,
2151
2180
  root,
2152
2181
  output,
2153
2182
  group: group ?? void 0,
2183
+ checks: true,
2154
2184
  name: (schemaName) => directionalRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName)
2155
2185
  });
2156
- const schemaPrinter = getPrinter(resolver, {
2157
- ...printerOptions,
2158
- keysToOmit
2159
- }, mini, direction);
2160
2186
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
2161
2187
  root: meta.file.path,
2162
2188
  path: imp.path,
@@ -2172,7 +2198,8 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2172
2198
  inferTypeName: inferred ? resolver.schema.type(name) : null,
2173
2199
  inferKind: aliasKind(inferred, direction),
2174
2200
  cyclic: false,
2175
- compile
2201
+ compile,
2202
+ siblings
2176
2203
  })] });
2177
2204
  }
2178
2205
  function buildContentTypeVariants(entries, baseName, decorate, direction) {
@@ -2196,7 +2223,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2196
2223
  })] });
2197
2224
  }
2198
2225
  function buildResponseUnion({ responses, name, fallbackUnknown }) {
2199
- if (new Set(responses.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? _kubb_kit.ast.collectImportedRefNames(entry.schema).map((refName) => resolver.name(refName)) : []))).has(name)) return null;
2226
+ if (new Set(_kubb_kit.ast.http.responses.getImportedNames(responses).map((refName) => resolver.name(refName))).has(name)) return null;
2200
2227
  const members = responses.map((res) => _kubb_kit.ast.factory.createSchema({
2201
2228
  type: "ref",
2202
2229
  name: resolver.response.status(node, res.statusCode)
@@ -2229,14 +2256,27 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2229
2256
  keysToOmit: primary?.keysToOmit
2230
2257
  });
2231
2258
  });
2259
+ const toParamsSchema = (params) => _kubb_kit.ast.factory.createSchema({
2260
+ type: "object",
2261
+ primitive: "object",
2262
+ properties: params.map((param) => _kubb_kit.ast.factory.createProperty({
2263
+ name: param.name,
2264
+ required: param.required,
2265
+ schema: param.schema
2266
+ }))
2267
+ });
2268
+ const responseHeaderSchemas = node.responses.map((res) => res.headers?.length ? renderSchemaEntry({
2269
+ schema: toParamsSchema(res.headers),
2270
+ name: resolver.response.headers(node, res.statusCode)
2271
+ }) : null);
2232
2272
  const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
2233
- const successResponsesWithSchema = getSuccessResponses(responsesWithSchema);
2273
+ const successResponsesWithSchema = responsesWithSchema.filter((res) => _kubb_kit.ast.http.status.isSuccess(res));
2234
2274
  const responseUnionSchema = responsesWithSchema.length > 0 ? buildResponseUnion({
2235
2275
  responses: successResponsesWithSchema,
2236
2276
  name: resolver.response.response(node),
2237
2277
  fallbackUnknown: true
2238
2278
  }) : null;
2239
- const errorResponsesWithSchema = responsesWithSchema.filter((res) => !isSuccessStatusCode(res.statusCode));
2279
+ const errorResponsesWithSchema = responsesWithSchema.filter((res) => !_kubb_kit.ast.http.status.isSuccess(res));
2240
2280
  const errorUnionSchema = errorResponsesWithSchema.length > 0 ? buildResponseUnion({
2241
2281
  responses: errorResponsesWithSchema,
2242
2282
  name: resolver.response.error(node),
@@ -2263,7 +2303,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2263
2303
  description: node.requestBody.description ?? schema.description
2264
2304
  }), "encode");
2265
2305
  })();
2266
- const { path, query, header, cookie } = getOperationParameters(node);
2306
+ const { path, query, header, cookie } = _kubb_kit.ast.http.groupParameters(node);
2267
2307
  const paramGroupSchemas = [
2268
2308
  {
2269
2309
  kind: "path",
@@ -2282,15 +2322,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2282
2322
  params: cookie
2283
2323
  }
2284
2324
  ].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
2285
- schema: _kubb_kit.ast.factory.createSchema({
2286
- type: "object",
2287
- primitive: "object",
2288
- properties: params.map((param) => _kubb_kit.ast.factory.createProperty({
2289
- name: param.name,
2290
- required: param.required,
2291
- schema: param.schema
2292
- }))
2293
- }),
2325
+ schema: toParamsSchema(params),
2294
2326
  name: resolver.param[kind](node, params[0]),
2295
2327
  direction: "encode"
2296
2328
  }));
@@ -2331,6 +2363,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2331
2363
  }),
2332
2364
  paramSchemas,
2333
2365
  responseSchemas,
2366
+ responseHeaderSchemas,
2334
2367
  responseUnionSchema,
2335
2368
  errorUnionSchema,
2336
2369
  requestSchema,
@@ -2343,11 +2376,11 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2343
2376
  });
2344
2377
  // kubb-extension.json
2345
2378
  var compatibility = {
2346
- "kubb": ">=5.16.0",
2379
+ "kubb": ">=5.18.4",
2347
2380
  "node": ">=22.22"
2348
2381
  };
2349
2382
  // package.json
2350
- var version = "5.9.0-canary.20261111T122006";
2383
+ var version = "5.9.1";
2351
2384
  // src/resolvers/resolverZod.ts
2352
2385
  /**
2353
2386
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
@@ -2367,15 +2400,15 @@ var version = "5.9.0-canary.20261111T122006";
2367
2400
  const resolverZod = (0, _kubb_kit.createResolver)({
2368
2401
  pluginName: "plugin-zod",
2369
2402
  name(name) {
2370
- return this.memo(`name:${name}`, () => ensureValidVarName((0, _kubb_kit.camelCase)(name, { suffix: "schema" })));
2403
+ return this.memo(`name:${name}`, () => ensureValidVarName(_kubb_kit.casing.camel(name, { suffix: "schema" })));
2371
2404
  },
2372
- file: createCasedFile((part) => (0, _kubb_kit.camelCase)(part, { suffix: "schema" })),
2405
+ file: createCasedFile((part) => _kubb_kit.casing.camel(part, { suffix: "schema" })),
2373
2406
  schema: {
2374
2407
  typeName(name) {
2375
- return this.memo(`typeName:${name}`, () => ensureValidVarName((0, _kubb_kit.pascalCase)(name, { suffix: "schema type" })));
2408
+ return this.memo(`typeName:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "schema type" })));
2376
2409
  },
2377
2410
  type(name) {
2378
- return this.memo(`type:${name}`, () => ensureValidVarName((0, _kubb_kit.pascalCase)(name, { suffix: "type" })));
2411
+ return this.memo(`type:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "type" })));
2379
2412
  },
2380
2413
  inputName(name) {
2381
2414
  return this.name(`${name} input`);
@@ -2384,10 +2417,10 @@ const resolverZod = (0, _kubb_kit.createResolver)({
2384
2417
  return this.schema.typeName(`${name} input`);
2385
2418
  },
2386
2419
  isName(name) {
2387
- return ensureValidVarName((0, _kubb_kit.camelCase)(name, { prefix: "is" }));
2420
+ return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "is" }));
2388
2421
  },
2389
2422
  assertName(name) {
2390
- return ensureValidVarName((0, _kubb_kit.camelCase)(name, { prefix: "assert" }));
2423
+ return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "assert" }));
2391
2424
  }
2392
2425
  },
2393
2426
  param: createOperationParamResolver(),
@@ -2461,7 +2494,7 @@ const pluginZod = (0, _kubb_kit.defineExtension)({
2461
2494
  exclude,
2462
2495
  include,
2463
2496
  override,
2464
- group: createGroupConfig(group),
2497
+ group: group ?? null,
2465
2498
  importPath,
2466
2499
  coercion,
2467
2500
  inferred: inferred === "direction" ? "codec" : inferred,