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

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,
@@ -317,438 +537,101 @@ function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
317
537
  required: index > 0,
318
538
  schema: _kubb_kit.ast.factory.createSchema({
319
539
  type: "union",
320
- members: [contentType, _kubb_kit.ast.factory.createSchema({
321
- type: "object",
322
- primitive: "object",
323
- properties: [_kubb_kit.ast.factory.createProperty({
324
- name: "request",
325
- required: true,
326
- schema: contentType
327
- })]
328
- })]
329
- })
330
- })]);
331
- })
332
- });
333
- }
334
- /**
335
- * The schema a status occupies in the `<Name>Responses` record. A status that documents several
336
- * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
337
- * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
338
- * query hooks and `result.data` use.
339
- */
340
- function buildResponseRecordEntry(node, res, resolver) {
341
- const statusName = resolver.response.status(node, res.statusCode);
342
- const variants = (res.content ?? []).filter((entry) => entry.schema);
343
- if (variants.length <= 1) return _kubb_kit.ast.factory.createSchema({
344
- type: "ref",
345
- name: statusName
346
- });
347
- return _kubb_kit.ast.factory.createSchema({
348
- type: "union",
349
- members: resolveContentTypeVariants(variants, statusName).map((variant) => _kubb_kit.ast.factory.createSchema({
350
- type: "object",
351
- primitive: "object",
352
- properties: [_kubb_kit.ast.factory.createProperty({
353
- name: "contentType",
354
- required: true,
355
- schema: _kubb_kit.ast.factory.createSchema({
356
- type: "enum",
357
- enumValues: [baseContentType(variant.contentType)]
358
- })
359
- }), _kubb_kit.ast.factory.createProperty({
360
- name: "data",
361
- required: true,
362
- schema: _kubb_kit.ast.factory.createSchema({
363
- type: "ref",
364
- name: variant.name
365
- })
366
- })]
367
- }))
368
- });
369
- }
370
- /**
371
- * Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved
372
- * `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s
373
- * inferred responses schema, so both emit the same shape from the same inputs.
374
- *
375
- * Always emits the keyed record, even when an operation declares no responses. An operation with no
376
- * responses renders as an empty object, which keeps every consumer's import (for example the axios
377
- * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
378
- */
379
- function buildResponses(node, resolver) {
380
- return _kubb_kit.ast.factory.createSchema({
381
- type: "object",
382
- primitive: "object",
383
- properties: node.responses.map((res) => _kubb_kit.ast.factory.createProperty({
384
- name: String(res.statusCode),
385
- required: true,
386
- schema: buildResponseRecordEntry(node, res, resolver)
387
- }))
388
- });
389
- }
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
- // ../../internals/shared/src/adapter.ts
402
- /**
403
- * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
404
- * so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
405
- *
406
- * Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
407
- * clear, actionable error at the point of use.
408
- *
409
- * @example
410
- * ```ts
411
- * const { dateType } = getOasAdapter(ctx.adapter).options
412
- * ```
413
- */
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;
540
+ members: [contentType, _kubb_kit.ast.factory.createSchema({
541
+ type: "object",
542
+ primitive: "object",
543
+ properties: [_kubb_kit.ast.factory.createProperty({
544
+ name: "request",
545
+ required: true,
546
+ schema: contentType
547
+ })]
548
+ })]
549
+ })
550
+ })]);
551
+ })
626
552
  });
627
553
  }
628
554
  /**
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.
555
+ * The schema a status occupies in the `<Name>Responses` record. A status that documents several
556
+ * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
557
+ * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
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.
664
560
  */
665
- function indentLines(text) {
666
- if (!text) return "";
667
- return text.split("\n").map((line) => line.trim() ? `${INDENT}${line}` : "").join("\n");
561
+ function buildResponseRecordEntry(node, res, resolver, streamData) {
562
+ const statusName = resolver.response.status(node, res.statusCode);
563
+ const variants = (res.content ?? []).filter((entry) => entry.schema);
564
+ if (variants.length <= 1) return _kubb_kit.ast.factory.createSchema({
565
+ type: "ref",
566
+ name: statusName
567
+ });
568
+ return _kubb_kit.ast.factory.createSchema({
569
+ type: "union",
570
+ members: resolveContentTypeVariants(variants, statusName).map((variant) => _kubb_kit.ast.factory.createSchema({
571
+ type: "object",
572
+ primitive: "object",
573
+ properties: [_kubb_kit.ast.factory.createProperty({
574
+ name: "contentType",
575
+ required: true,
576
+ schema: _kubb_kit.ast.factory.createSchema({
577
+ type: "enum",
578
+ enumValues: [_kubb_kit.ast.mediaType.getBase(variant)]
579
+ })
580
+ }), _kubb_kit.ast.factory.createProperty({
581
+ name: "data",
582
+ required: true,
583
+ schema: streamData && _kubb_kit.ast.http.status.isSuccess(res) && _kubb_kit.ast.mediaType.isStream(variant) ? streamData : _kubb_kit.ast.factory.createSchema({
584
+ type: "ref",
585
+ name: variant.name
586
+ })
587
+ })]
588
+ }))
589
+ });
668
590
  }
669
591
  /**
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.
592
+ * Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved
593
+ * `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s
594
+ * inferred responses schema, so both emit the same shape from the same inputs.
672
595
  *
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.
596
+ * Always emits the keyed record, even when an operation declares no responses. An operation with no
597
+ * responses renders as an empty object, which keeps every consumer's import (for example the axios
598
+ * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
686
599
  *
687
- * @example
688
- * ```ts
689
- * buildObject(['id: z.number()', 'name: z.string()'])
690
- * // '{\n id: z.number(),\n name: z.string(),\n}'
691
- * ```
600
+ * `streamData` is the type of a stream variant's `data`, such as a `ReadableStream<Uint8Array>` reference.
692
601
  */
693
- function buildObject(entries) {
694
- if (entries.length === 0) return "{}";
695
- return `{\n${entries.map((entry) => `${indentLines(entry)},`).join("\n")}\n}`;
602
+ function buildResponses(node, resolver, { streamData } = {}) {
603
+ return _kubb_kit.ast.factory.createSchema({
604
+ type: "object",
605
+ primitive: "object",
606
+ properties: node.responses.map((res) => _kubb_kit.ast.factory.createProperty({
607
+ name: String(res.statusCode),
608
+ required: true,
609
+ schema: buildResponseRecordEntry(node, res, resolver, streamData)
610
+ }))
611
+ });
696
612
  }
613
+ // ../../internals/shared/src/adapter.ts
697
614
  /**
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([…])`.
615
+ * Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
616
+ * so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
702
617
  *
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.
618
+ * Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
619
+ * clear, actionable error at the point of use.
719
620
  *
720
621
  * @example
721
622
  * ```ts
722
- * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
723
- * // "get parent() { return z.lazy(() => Pet) }"
623
+ * const { dateType } = getOasAdapter(ctx.adapter).options
724
624
  * ```
725
625
  */
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,75 +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
- }));
772
+ return ctx.memo("circularNames:set", () => _kubb_kit.ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []));
951
773
  }
952
774
  // src/components/Zod.tsx
953
775
  function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
@@ -955,9 +777,9 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
955
777
  const printerImports = printer.drainImports();
956
778
  if (!output) return;
957
779
  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;
780
+ const isAlias = _kubb_kit.ast.ref.isAlias(node, printer.options.keysToOmit);
781
+ const isCyclic = Boolean(cyclic || printer.options.cyclicSchemas && _kubb_kit.ast.graph.containsCircular(node, { circularSchemas: printer.options.cyclicSchemas }));
782
+ const value = Boolean(compile) && !isAlias && !isCyclic ? typeof compile === "object" && compile.strict ? `z.compile(${output}, { strict: true })` : `z.compile(${output})` : output;
961
783
  const targetType = inferTypeName ?? `z.infer<typeof ${name}>`;
962
784
  const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
963
785
  const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
@@ -980,7 +802,7 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
980
802
  children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
981
803
  export: true,
982
804
  name,
983
- type: needsAnnotation ? "z.ZodType" : void 0,
805
+ type: needsAnnotation ? mini ? "z.ZodMiniType" : "z.ZodType" : void 0,
984
806
  children: value
985
807
  })
986
808
  }),
@@ -1040,7 +862,7 @@ function shouldCoerce(coercion, type) {
1040
862
  */
1041
863
  function isClosedKeySchema(node) {
1042
864
  if (!node) return false;
1043
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
865
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
1044
866
  if (resolved.type === "enum") return true;
1045
867
  if (resolved.type === "union") {
1046
868
  const members = resolved.members ?? [];
@@ -1050,12 +872,12 @@ function isClosedKeySchema(node) {
1050
872
  }
1051
873
  /**
1052
874
  * 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
875
+ * argument. Input checks, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
1054
876
  * on `.and(…)`.
1055
877
  */
1056
878
  function isPlainInlineObject(node) {
1057
879
  if (node.type !== "object") return false;
1058
- if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
880
+ if (hasInputChecks(node)) return false;
1059
881
  if (node.nullable || node.optional || node.nullish) return false;
1060
882
  const hasAdditionalProperties = node.additionalProperties !== void 0;
1061
883
  const hasPatternProperties = Boolean(node.patternProperties);
@@ -1066,21 +888,21 @@ function isPlainInlineObject(node) {
1066
888
  * `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
1067
889
  * unresolved) objects, single-member unions of an object, and object-composable `allOf`.
1068
890
  *
1069
- * Property counts render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
891
+ * Input checks, such as property counts and `conditionals`, render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
1070
892
  */
1071
893
  function isObjectSchemaNode(node, cyclicSchemas) {
1072
894
  if (node.nullable || node.optional || node.nullish) return false;
895
+ if (hasInputChecks(node)) return false;
1073
896
  if (node.type === "object") {
1074
- if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
1075
897
  const hasProperties = Boolean(node.properties?.length);
1076
898
  const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
1077
899
  const hasPatternProperties = Boolean(node.patternProperties);
1078
900
  return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties || node.propertyNames));
1079
901
  }
1080
902
  if (node.type === "ref") {
1081
- const refName = _kubb_kit.ast.resolveRefName(node);
903
+ const refName = _kubb_kit.ast.ref.getName(node);
1082
904
  if (refName && cyclicSchemas?.has(refName)) return false;
1083
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
905
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
1084
906
  return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
1085
907
  }
1086
908
  if (node.type === "union") {
@@ -1103,23 +925,23 @@ function preserveRequiredProperties(members) {
1103
925
  const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
1104
926
  if (seen.has(node)) return false;
1105
927
  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);
928
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
929
+ 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
930
  if (composition) {
1109
- if (composition.type === _kubb_kit.ast.schemaTypes.union && composition.members?.length !== 1) return false;
931
+ if (composition.type === _kubb_kit.ast.schema.types.union && composition.members?.length !== 1) return false;
1110
932
  return composition.members?.some((child) => hasRequiredProperty({
1111
933
  node: child,
1112
934
  propertyName,
1113
935
  seen
1114
936
  })) ?? false;
1115
937
  }
1116
- return _kubb_kit.ast.resolveSchemaProperties({
938
+ return _kubb_kit.ast.schema.findProperties({
1117
939
  node: resolved,
1118
940
  propertyName
1119
941
  }).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
1120
942
  };
1121
943
  return members.map((member, index) => {
1122
- const object = _kubb_kit.ast.narrowSchema(member, _kubb_kit.ast.schemaTypes.object);
944
+ const object = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.object);
1123
945
  if (!object || index === 0) return member;
1124
946
  return {
1125
947
  ...object,
@@ -1148,7 +970,7 @@ function preserveRequiredProperties(members) {
1148
970
  * representation, strings are quoted.
1149
971
  */
1150
972
  function formatDefault(value) {
1151
- if (typeof value === "string") return stringify(value);
973
+ if (typeof value === "string") return _kubb_kit.text.quote(value);
1152
974
  if (typeof value === "object" && value !== null) return JSON.stringify(value);
1153
975
  return String(value ?? "");
1154
976
  }
@@ -1163,16 +985,17 @@ function formatDefault(value) {
1163
985
  */
1164
986
  function defaultLiteral(node, value) {
1165
987
  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")) {
988
+ const resolved = node ? _kubb_kit.ast.ref.mergeSchema(node) : void 0;
989
+ if (resolved?.type === "blob") return null;
990
+ if (resolved && _kubb_kit.ast.schema.narrow(resolved, "bigint")) {
1168
991
  if (typeof value === "bigint") return `BigInt(${value})`;
1169
992
  if (typeof value === "number" && Number.isInteger(value)) return `BigInt(${value})`;
1170
993
  return null;
1171
994
  }
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;
995
+ if (resolved && _kubb_kit.ast.schema.narrow(resolved, "array")) return Array.isArray(value) ? JSON.stringify(value) : null;
996
+ const enumNode = resolved ? _kubb_kit.ast.schema.narrow(resolved, "enum") : void 0;
1174
997
  if (enumNode) {
1175
- const values = enumNode.namedEnumValues?.map((member) => member.value) ?? enumNode.enumValues ?? [];
998
+ const values = _kubb_kit.ast.schema.getLiterals(enumNode);
1176
999
  if (values.length) {
1177
1000
  const match = values.find((member) => member === value || String(member) === String(value));
1178
1001
  return match != null ? formatDefault(match) : null;
@@ -1241,13 +1064,13 @@ function patternKeySchemaMini({ patterns, regexType }) {
1241
1064
  * Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
1242
1065
  * using the standard chainable Zod v4 API.
1243
1066
  */
1244
- function numberConstraints({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
1067
+ function numberConstraints({ min, max, exclusive, multipleOf }, suffix = "") {
1245
1068
  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})` : ""
1069
+ min !== void 0 ? `.min(${min}${suffix})` : "",
1070
+ max !== void 0 ? `.max(${max}${suffix})` : "",
1071
+ exclusive?.min !== void 0 ? `.gt(${exclusive.min}${suffix})` : "",
1072
+ exclusive?.max !== void 0 ? `.lt(${exclusive.max}${suffix})` : "",
1073
+ multipleOf !== void 0 ? `.multipleOf(${multipleOf}${suffix})` : ""
1251
1074
  ].join("");
1252
1075
  }
1253
1076
  /**
@@ -1262,36 +1085,71 @@ function lengthConstraints({ min, max, pattern, regexType }) {
1262
1085
  ].join("");
1263
1086
  }
1264
1087
  /**
1088
+ * Numeric bounds for a bigint: the inclusive, whole, safe bounds faker mocks against, and a whole, safe `multipleOf`.
1089
+ */
1090
+ function bigintConstraints({ min, max, exclusive, multipleOf }) {
1091
+ return {
1092
+ ..._kubb_kit.ast.schema.getNumberBounds({
1093
+ min,
1094
+ max,
1095
+ exclusive,
1096
+ integer: true
1097
+ }),
1098
+ multipleOf: Number.isSafeInteger(multipleOf) ? multipleOf : void 0
1099
+ };
1100
+ }
1101
+ /**
1265
1102
  * Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
1266
1103
  */
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})`);
1104
+ function numberChecksMini({ min, max, exclusive, multipleOf }, suffix = "") {
1105
+ const checks = [
1106
+ min !== void 0 && `z.minimum(${min}${suffix})`,
1107
+ max !== void 0 && `z.maximum(${max}${suffix})`,
1108
+ exclusive?.min !== void 0 && `z.gt(${exclusive.min}${suffix})`,
1109
+ exclusive?.max !== void 0 && `z.lt(${exclusive.max}${suffix})`,
1110
+ multipleOf !== void 0 && `z.multipleOf(${multipleOf}${suffix})`
1111
+ ].filter(Boolean);
1274
1112
  return checks.length ? `.check(${checks.join(", ")})` : "";
1275
1113
  }
1276
1114
  /**
1277
1115
  * Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
1278
1116
  */
1279
1117
  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)})`);
1118
+ const checks = [
1119
+ min !== void 0 && `z.minLength(${min})`,
1120
+ max !== void 0 && `z.maxLength(${max})`,
1121
+ pattern !== void 0 && `z.regex(${regexSource(pattern, regexType)})`
1122
+ ].filter(Boolean);
1284
1123
  return checks.length ? `.check(${checks.join(", ")})` : "";
1285
1124
  }
1286
1125
  /**
1287
- * Checks property counts before object parsing strips unknown keys or inserts defaults.
1126
+ * Whether `propertyNames` needs its own check: `z.record` checks the keys of a map, but `z.object` and a record keyed by
1127
+ * `patternProperties` do not.
1128
+ */
1129
+ function hasKeyCheck(node) {
1130
+ if (node.type !== "object" || !node.propertyNames) return false;
1131
+ const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
1132
+ return node.properties.length > 0 || Boolean(node.patternProperties) && !hasTypedAdditionalProperties;
1133
+ }
1134
+ /**
1135
+ * Whether the node prints inside `z.pipe(z.unknown().check(…), …)`, see {@link inputChecks}.
1288
1136
  */
1289
- function objectConstraints({ value, node }) {
1290
- if (node.type !== "object") return value;
1291
- const checks = [];
1137
+ function hasInputChecks(node) {
1138
+ return Boolean(node.conditionals?.length) || node.type === "object" && (node.min !== void 0 || node.max !== void 0) || hasKeyCheck(node);
1139
+ }
1140
+ /**
1141
+ * Checks the raw input, before parsing strips unknown keys or inserts defaults: property counts, key names, then the
1142
+ * printed `conditionals`.
1143
+ */
1144
+ function inputChecks({ value, node, keySchema, conditionals }) {
1292
1145
  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" })`);
1146
+ const { min, max } = node.type === "object" ? node : {};
1147
+ const checks = [
1148
+ min !== void 0 && `z.refine(value => ${skip} || Object.keys(value).length >= ${min}, { message: "Object must have at least ${min} properties" })`,
1149
+ max !== void 0 && `z.refine(value => ${skip} || Object.keys(value).length <= ${max}, { message: "Object must have at most ${max} properties" })`,
1150
+ keySchema && `z.refine(value => ${skip} || Object.keys(value).every((key) => ${keySchema}.safeParse(key).success), { message: "Object keys must match the property names schema" })`,
1151
+ ...conditionals
1152
+ ].filter(Boolean);
1295
1153
  return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
1296
1154
  }
1297
1155
  /**
@@ -1307,8 +1165,8 @@ function applyModifiers({ value, schema, nullable, optional, nullish, defaultVal
1307
1165
  })();
1308
1166
  const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
1309
1167
  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;
1168
+ const withDescription = description ? `${withDefault}.describe(${_kubb_kit.text.quote(description)})` : withDefault;
1169
+ return examples?.length ? `${withDescription}.meta({ examples: [${examples.map((example) => example === null ? "null" : formatDefault(example)).join(", ")}] })` : withDescription;
1312
1170
  }
1313
1171
  function modifierDepth(schema) {
1314
1172
  if (schema.nullish || schema.nullable && schema.optional) return 2;
@@ -1326,7 +1184,7 @@ function modifierDepth(schema) {
1326
1184
  * `.unwrap()`, so its printer passes `._zod.def.innerType` as the `step`.
1327
1185
  */
1328
1186
  function omitUnwrapChain(node, step = ".unwrap()") {
1329
- const ref = _kubb_kit.ast.narrowSchema(node, "ref");
1187
+ const ref = _kubb_kit.ast.schema.narrow(node, "ref");
1330
1188
  if (!ref) return "";
1331
1189
  const target = ref.schema ?? ref;
1332
1190
  const depth = modifierDepth(target) + (target.default !== void 0 ? 1 : 0);
@@ -1348,7 +1206,7 @@ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaul
1348
1206
  /**
1349
1207
  * Calls for string `format`s with a dedicated Zod constructor; a `Map` so `constructor` cannot hit `Object.prototype`.
1350
1208
  */
1351
- const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
1209
+ const stringFormatCalls = /* @__PURE__ */ new Map([
1352
1210
  ["byte", "z.base64()"],
1353
1211
  ["base64", "z.base64()"],
1354
1212
  ["base64url", "z.base64url()"],
@@ -1359,6 +1217,16 @@ const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
1359
1217
  ["hostname", "z.hostname()"]
1360
1218
  ]);
1361
1219
  /**
1220
+ * Calls for a string's `contentEncoding` with a dedicated Zod constructor.
1221
+ */
1222
+ const contentEncodingCalls = /* @__PURE__ */ new Map([["base64", "z.base64()"], ["base64url", "z.base64url()"]]);
1223
+ /**
1224
+ * The check for a string that holds a JSON document: it must parse, and the parsed value must pass `schema`.
1225
+ */
1226
+ function jsonContentCheck(schema) {
1227
+ return `(value) => { try { return ${schema}.safeParse(JSON.parse(value)).success } catch { return false } }, { message: "String must hold JSON that matches its content schema" }`;
1228
+ }
1229
+ /**
1362
1230
  * Marks an inline `oneOf` object strict, so `object()` builds `z.strictObject(...)` and no eager `.shape` read hits a deferred getter.
1363
1231
  */
1364
1232
  function strictOneOfNode(node) {
@@ -1371,13 +1239,14 @@ function strictOneOfNode(node) {
1371
1239
  * The constraint chain an `allOf` member adds to a bare string, number or array; each printer passes its own renderers.
1372
1240
  */
1373
1241
  function getMemberConstraint({ member, regexType, lengths, numbers }) {
1242
+ if (hasInputChecks(member) || _kubb_kit.ast.schema.narrow(member, "array")?.contains) return void 0;
1374
1243
  if (member.primitive === "string") return lengths({
1375
- ..._kubb_kit.ast.narrowSchema(member, "string") ?? {},
1244
+ ..._kubb_kit.ast.schema.narrow(member, "string") ?? {},
1376
1245
  regexType
1377
1246
  }) || 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;
1247
+ 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
1248
  if (member.primitive === "array") return lengths({
1380
- ..._kubb_kit.ast.narrowSchema(member, "array") ?? {},
1249
+ ..._kubb_kit.ast.schema.narrow(member, "array") ?? {},
1381
1250
  regexType
1382
1251
  }) || void 0;
1383
1252
  }
@@ -1385,24 +1254,26 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
1385
1254
  * Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
1386
1255
  */
1387
1256
  function buildObjectShape(ctx, node, renderProperty) {
1388
- const objectNode = _kubb_kit.ast.narrowSchema(node, "object");
1257
+ const objectNode = _kubb_kit.ast.schema.narrow(node, "object");
1389
1258
  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) => {
1259
+ const isCyclic = (schema) => ctx.options.cyclicSchemas != null && _kubb_kit.ast.graph.containsCircular(schema, { circularSchemas: ctx.options.cyclicSchemas });
1260
+ const transformed = objectNode.properties.map(({ schema }) => {
1392
1261
  const hasSelfRef = isCyclic(schema);
1393
1262
  const savedCyclicSchemas = ctx.options.cyclicSchemas;
1394
1263
  if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
1395
1264
  const baseOutput = ctx.transform(schema) ?? ctx.transform(_kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1396
1265
  if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
1397
1266
  return baseOutput;
1398
- }).map(({ name, property, output: value }) => {
1399
- const { schema } = property;
1267
+ });
1268
+ return buildObject(objectNode.properties.map((property, index) => {
1269
+ const { name, schema } = property;
1270
+ const value = transformed[index];
1400
1271
  return renderProperty({
1401
1272
  name,
1402
1273
  value,
1403
1274
  schema,
1404
1275
  required: property.required,
1405
- meta: (0, _kubb_kit.syncSchemaRef)(schema),
1276
+ meta: _kubb_kit.ast.ref.mergeSchema(schema),
1406
1277
  cyclic: isCyclic(schema)
1407
1278
  });
1408
1279
  }));
@@ -1414,14 +1285,14 @@ function printWithOmit(ctx, node, modify, mini = false) {
1414
1285
  const { keysToOmit } = ctx.options;
1415
1286
  const transformed = ctx.transform(node);
1416
1287
  if (!transformed) return null;
1417
- const meta = (0, _kubb_kit.syncSchemaRef)(node);
1288
+ const meta = _kubb_kit.ast.ref.mergeSchema(node);
1418
1289
  return modify((() => {
1419
1290
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
1420
1291
  const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
1421
1292
  const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
1422
1293
  const omit = (target) => {
1423
1294
  const inner = `${target}${unwrap}`;
1424
- const hasObjectConstraints = meta.type === "object" && (meta.minProperties !== void 0 || meta.maxProperties !== void 0);
1295
+ const hasObjectConstraints = hasInputChecks(meta);
1425
1296
  const pipe = mini ? `${inner}._zod.def` : inner;
1426
1297
  const object = hasObjectConstraints ? `${pipe}.out` : inner;
1427
1298
  const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
@@ -1440,7 +1311,7 @@ function printWithOmit(ctx, node, modify, mini = false) {
1440
1311
  function getLiteralUnion({ node, options }) {
1441
1312
  if (node.strategy === "one" && options.oneOfType === "xor" || options.nodes?.enum) return void 0;
1442
1313
  const values = (node.members ?? []).map((member) => {
1443
- const enumNode = _kubb_kit.ast.narrowSchema(member, _kubb_kit.ast.schemaTypes.enum);
1314
+ const enumNode = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.enum);
1444
1315
  return enumNode && !enumNode.name && !enumNode.nullable && !enumNode.namedEnumValues && enumNode.enumValues?.length === 1 ? enumNode.enumValues[0] : void 0;
1445
1316
  });
1446
1317
  if (!values.length || !values.every((value) => value !== void 0 && value !== null)) return void 0;
@@ -1453,19 +1324,26 @@ function getLiteralUnion({ node, options }) {
1453
1324
  * Whether a discriminated union member's discriminator property prints as a coerced literal union.
1454
1325
  */
1455
1326
  function hasCoercedDiscriminator({ member, propertyName, options }) {
1456
- return _kubb_kit.ast.resolveSchemaProperties({
1327
+ return _kubb_kit.ast.schema.findProperties({
1457
1328
  node: member,
1458
1329
  propertyName
1459
1330
  }).some(({ schema }) => {
1460
- const resolved = (0, _kubb_kit.syncSchemaRef)(schema);
1331
+ const resolved = _kubb_kit.ast.ref.mergeSchema(schema);
1461
1332
  return resolved.type === "union" && !!getLiteralUnion({
1462
1333
  node: resolved,
1463
1334
  options
1464
1335
  })?.coerce;
1465
1336
  });
1466
1337
  }
1338
+ function hasPlainDiscriminator({ member, propertyName }) {
1339
+ const schemas = _kubb_kit.ast.schema.findProperties({
1340
+ node: member,
1341
+ propertyName
1342
+ });
1343
+ return schemas.length > 0 && !schemas.some(({ schema }) => _kubb_kit.ast.schema.getLiterals(_kubb_kit.ast.ref.mergeSchema(schema)).length > 0);
1344
+ }
1467
1345
  /**
1468
- * Builds the scalar handlers both dialects share. They never recurse into children. The temporal
1346
+ * Builds the scalar handlers both dialects share. Only a string with a `contentSchema` recurses into a child. The temporal
1469
1347
  * scalars and `bigint` differ per dialect, so each printer adds its own.
1470
1348
  */
1471
1349
  function buildScalarNodes(dialect) {
@@ -1478,18 +1356,20 @@ function buildScalarNodes(dialect) {
1478
1356
  null: () => "z.null()",
1479
1357
  string(node) {
1480
1358
  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()";
1359
+ const base = (coerce ? void 0 : stringFormatCalls.get(node.format) ?? contentEncodingCalls.get(node.content?.encoding?.toLowerCase())) ?? (coerce ? "z.coerce.string()" : "z.string()");
1487
1360
  const pattern = node.pattern ?? integerFormatPattern(node.format);
1488
- return `${base}${dialect.lengths({
1361
+ const value = `${base}${dialect.lengths({
1489
1362
  ...node,
1490
1363
  pattern,
1491
1364
  regexType: this.options.regexType
1492
1365
  })}`;
1366
+ const contentNode = _kubb_kit.ast.schema.getEmbeddedJson(node);
1367
+ if (!contentNode) return value;
1368
+ const direction = this.options.direction;
1369
+ this.options.direction = "decode";
1370
+ const contentSchema = this.transform(contentNode);
1371
+ this.options.direction = direction;
1372
+ return contentSchema ? dialect.refine(value, jsonContentCheck(contentSchema)) : value;
1493
1373
  },
1494
1374
  number(node) {
1495
1375
  return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${dialect.numbers(node)}`;
@@ -1522,7 +1402,7 @@ function buildScalarNodes(dialect) {
1522
1402
  ipv6: () => "z.ipv6()",
1523
1403
  blob: () => "z.instanceof(Blob)",
1524
1404
  enum(node) {
1525
- const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
1405
+ const nonNullValues = _kubb_kit.ast.schema.getLiterals(node).filter((v) => v !== null);
1526
1406
  if (node.namedEnumValues?.length) {
1527
1407
  const literals = nonNullValues.map((v) => buildLiteral(v));
1528
1408
  if (literals.length === 1) return literals[0];
@@ -1541,11 +1421,12 @@ function buildScalarNodes(dialect) {
1541
1421
  */
1542
1422
  function buildZodNodes(dialect, cyclicSchemas) {
1543
1423
  const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
1424
+ const printNullable = (schema, output) => output && schema.nullable ? dialect.nullable(output) : output;
1544
1425
  return {
1545
1426
  ...buildScalarNodes(dialect),
1546
1427
  ref(node) {
1547
1428
  if (!node.name) return null;
1548
- const refName = _kubb_kit.ast.resolveRefName(node);
1429
+ const refName = _kubb_kit.ast.ref.getName(node);
1549
1430
  if (!refName) return null;
1550
1431
  const useInputVariant = node.ref != null && (dialect.useInputVariant?.(node, this.options) ?? false);
1551
1432
  const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
@@ -1560,47 +1441,50 @@ function buildZodNodes(dialect, cyclicSchemas) {
1560
1441
  const isStrict = node.additionalProperties === false && patterns.length === 0;
1561
1442
  const isLoose = node.additionalProperties === true;
1562
1443
  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
- });
1444
+ return (() => {
1445
+ const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1446
+ if (node.additionalProperties && node.additionalProperties !== true) {
1447
+ const catchallType = this.transform(node.additionalProperties);
1448
+ if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1449
+ return catchallType ? dialect.catchall(objectBase, catchallType) : objectBase;
1450
+ }
1451
+ if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1452
+ const unknownType = this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }));
1453
+ return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1454
+ }
1455
+ if (node.additionalProperties === true) return objectBase;
1456
+ if (isStrict) return objectBase;
1457
+ if (patterns.length > 0) {
1458
+ const values = patterns.map(([, valueSchema]) => {
1459
+ const valueType = this.transform(valueSchema) ?? this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }));
1460
+ return valueSchema.nullable ? dialect.nullable(valueType) : valueType;
1461
+ });
1462
+ const distinct = [...new Set(values)];
1463
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1464
+ if (entries.length > 0) return dialect.catchall(objectBase, value);
1465
+ return `z.record(${dialect.patternKey({
1466
+ patterns: patterns.map(([pattern]) => pattern),
1467
+ regexType: this.options.regexType
1468
+ })}, ${value})`;
1469
+ }
1470
+ return objectBase;
1471
+ })();
1594
1472
  },
1595
1473
  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({
1474
+ 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
1475
  ...node,
1598
1476
  regexType: this.options.regexType
1599
1477
  })}`;
1600
- return node.unique ? dialect.unique(base) : base;
1478
+ const unique = node.unique ? dialect.unique(base) : base;
1479
+ const contains = node.contains && containsCheck(this, node.contains);
1480
+ return contains ? dialect.refine(unique, contains) : unique;
1601
1481
  },
1602
1482
  tuple(node) {
1603
- return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1483
+ const items = (node.items ?? []).map((item) => printNullable(item, this.transform(item))).filter(Boolean);
1484
+ const rest = node.rest ? this.transform(node.rest) : void 0;
1485
+ const tuple = `z.tuple(${buildList(items)}${rest ? `, ${rest}` : ""})`;
1486
+ const contains = node.contains && containsCheck(this, node.contains);
1487
+ return contains ? dialect.refine(tuple, contains) : tuple;
1604
1488
  },
1605
1489
  union(node) {
1606
1490
  const literalUnion = getLiteralUnion({
@@ -1613,24 +1497,30 @@ function buildZodNodes(dialect, cyclicSchemas) {
1613
1497
  }
1614
1498
  const nodeMembers = node.members ?? [];
1615
1499
  const { discriminatorPropertyName } = node;
1616
- const isDiscriminated = !!discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas)) && !nodeMembers.some((m) => hasCoercedDiscriminator({
1500
+ const isDiscriminated = !!discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas) && !hasPlainDiscriminator({
1501
+ member: m,
1502
+ propertyName: discriminatorPropertyName
1503
+ })) && !nodeMembers.some((m) => hasCoercedDiscriminator({
1617
1504
  member: m,
1618
1505
  propertyName: discriminatorPropertyName,
1619
1506
  options: this.options
1620
1507
  }));
1621
1508
  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);
1509
+ const isStrict = node.strategy === "one" && !isXor && !isDiscriminated && !unionsWithSiblings.has(node);
1510
+ const members = (node.members ?? []).map((memberNode) => {
1511
+ const output = this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode);
1512
+ return output && isStrict && dialect.strictMember ? dialect.strictMember(output, memberNode, cyclicSchemas) : output;
1513
+ }).map((output, index) => printNullable(nodeMembers[index], output)).filter(Boolean);
1624
1514
  if (members.length === 0) return "";
1625
1515
  if (members.length === 1) return members[0];
1626
- if (isDiscriminated) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
1516
+ if (isDiscriminated) return `z.discriminatedUnion(${_kubb_kit.text.quote(node.discriminatorPropertyName)}, ${buildList(members)})`;
1627
1517
  if (isXor) return `z.xor(${buildList(members)})`;
1628
1518
  return `z.union(${buildList(members)})`;
1629
1519
  },
1630
1520
  intersection(node) {
1631
1521
  const members = node.members ?? [];
1632
1522
  if (members.length === 0) return "";
1633
- const hasSiblingProperties = members.some((member) => member.type === _kubb_kit.ast.schemaTypes.object && member.properties.length > 0);
1523
+ const hasSiblingProperties = members.some((member) => member.type === _kubb_kit.ast.schema.types.object && member.properties.length > 0);
1634
1524
  const transform = (member) => {
1635
1525
  if (!hasSiblingProperties) return this.transform(member);
1636
1526
  unionsWithSiblings.add(member);
@@ -1644,7 +1534,18 @@ function buildZodNodes(dialect, cyclicSchemas) {
1644
1534
  if (!first) return "";
1645
1535
  const firstBase = transform(first);
1646
1536
  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);
1537
+ if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemas)) {
1538
+ const discriminator = _kubb_kit.ast.schema.narrow(first, _kubb_kit.ast.schema.types.union)?.discriminatorPropertyName;
1539
+ return preserveRequiredProperties(members).slice(1).reduce((acc, member) => {
1540
+ const object = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.object);
1541
+ if (!discriminator || !object?.properties.some((property) => property.name === discriminator)) return dialect.extend(acc, dialect.objectShape(this, member));
1542
+ const properties = object.properties.filter((property) => property.name !== discriminator);
1543
+ return properties.length ? dialect.extend(acc, dialect.objectShape(this, {
1544
+ ...object,
1545
+ properties
1546
+ })) : acc;
1547
+ }, firstBase);
1548
+ }
1648
1549
  return rest.reduce((acc, member) => {
1649
1550
  const constraint = getMemberConstraint({
1650
1551
  member,
@@ -1659,15 +1560,57 @@ function buildZodNodes(dialect, cyclicSchemas) {
1659
1560
  }
1660
1561
  };
1661
1562
  }
1563
+ function containsCheck(ctx, { schema, min = 1, max }) {
1564
+ if (min === 0 && max === void 0) return null;
1565
+ const countBounds = [`count >= ${min}`, max !== void 0 && `count <= ${max}`].filter(Boolean).join(" && ");
1566
+ const message = max !== void 0 ? `Array must contain ${min} to ${max} matching items` : `Array must contain at least ${min} matching items`;
1567
+ return `(items) => { const count = items.filter((item) => ${ctx.transform(schema) ?? "z.unknown()"}.safeParse(item).success).length; return ${countBounds} }, { message: ${_kubb_kit.text.quote(message)} }`;
1568
+ }
1569
+ function conditionExpression(ctx, schema) {
1570
+ if (!schema) return "true";
1571
+ const object = _kubb_kit.ast.schema.narrow(schema, _kubb_kit.ast.schema.types.object);
1572
+ const keyOnlyNames = object?.properties.filter((property) => property.required && property.schema.type === _kubb_kit.ast.schema.types.unknown).map((property) => property.name) ?? [];
1573
+ const restSchema = object ? {
1574
+ ...object,
1575
+ properties: object.properties.filter((property) => !keyOnlyNames.includes(property.name))
1576
+ } : schema;
1577
+ const isOnlyKeys = keyOnlyNames.length > 0 && restSchema.type === "object" && restSchema.properties.length === 0 && restSchema.additionalProperties === void 0 && !restSchema.patternProperties && !restSchema.propertyNames && !hasInputChecks(restSchema);
1578
+ 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(" && ");
1579
+ }
1580
+ /**
1581
+ * Wraps each handler so its output first checks the raw input, see {@link inputChecks}.
1582
+ */
1583
+ function withInputChecks(nodes) {
1584
+ const wrapHandler = (type) => {
1585
+ const handler = nodes[type];
1586
+ if (!handler) return [];
1587
+ const checkedHandler = function(node) {
1588
+ const output = handler.call(this, node);
1589
+ if (!output || !hasInputChecks(node)) return output;
1590
+ return inputChecks({
1591
+ value: output,
1592
+ node,
1593
+ keySchema: hasKeyCheck(node) ? this.transform(node.propertyNames) : void 0,
1594
+ 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" })`)
1595
+ });
1596
+ };
1597
+ return [[type, checkedHandler]];
1598
+ };
1599
+ return Object.fromEntries(Object.values(_kubb_kit.ast.schema.types).flatMap(wrapHandler));
1600
+ }
1662
1601
  // src/printers/printerZod.ts
1663
1602
  function strictOneOfMember(member, node, cyclicSchemas) {
1664
- if (node.type === _kubb_kit.ast.schemaTypes.ref) {
1603
+ if (node.type === _kubb_kit.ast.schema.types.ref) {
1665
1604
  if (member.startsWith("z.lazy(")) return member;
1666
- const refName = _kubb_kit.ast.resolveRefName(node);
1605
+ const refName = _kubb_kit.ast.ref.getName(node);
1667
1606
  if (refName && cyclicSchemas?.has(refName)) return member;
1668
- const schema = (0, _kubb_kit.syncSchemaRef)(node);
1607
+ const schema = _kubb_kit.ast.ref.mergeSchema(node);
1669
1608
  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()`;
1609
+ if (schema.type === _kubb_kit.ast.schema.types.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) {
1610
+ const checks = [node, node.schema].filter((entry) => entry && hasInputChecks(entry)).length;
1611
+ if (checks === 2) return member;
1612
+ return checks ? `z.pipe(${member}.in, ${member}.out.strict())` : `${member}.strict()`;
1613
+ }
1671
1614
  }
1672
1615
  return member;
1673
1616
  }
@@ -1683,7 +1626,7 @@ function buildZodObjectShape(ctx, node) {
1683
1626
  optional: schema.optional || required === false,
1684
1627
  nullish: schema.nullish,
1685
1628
  defaultValue: meta.default,
1686
- description: schema.type !== _kubb_kit.ast.schemaTypes.ref && meta.type === _kubb_kit.ast.schemaTypes.ref ? void 0 : meta.description,
1629
+ description: schema.type !== _kubb_kit.ast.schema.types.ref && meta.type === _kubb_kit.ast.schema.types.ref ? void 0 : meta.description,
1687
1630
  examples: meta.examples
1688
1631
  });
1689
1632
  if (!cyclic) return `${objectKey(name)}: ${body}`;
@@ -1704,21 +1647,22 @@ function buildZodObjectShape(ctx, node) {
1704
1647
  function recursiveGetterType({ ctx, schema, required }) {
1705
1648
  function baseType(schema) {
1706
1649
  if (ctx.options.nodes?.[schema.type]) return void 0;
1707
- if (schema.type === _kubb_kit.ast.schemaTypes.ref) {
1650
+ if (schema.type === _kubb_kit.ast.schema.types.ref) {
1708
1651
  const saved = ctx.options.cyclicSchemas;
1709
1652
  ctx.options.cyclicSchemas = void 0;
1710
1653
  const name = ctx.transform(schema);
1711
1654
  ctx.options.cyclicSchemas = saved;
1712
1655
  return name ? `typeof ${name}` : void 0;
1713
1656
  }
1714
- if (schema.type === _kubb_kit.ast.schemaTypes.array && schema.items?.length === 1) {
1657
+ if (schema.type === _kubb_kit.ast.schema.types.array && schema.items?.length === 1) {
1715
1658
  const item = baseType(schema.items[0]);
1716
- return item ? `z.ZodArray<${item}>` : void 0;
1659
+ if (!item) return void 0;
1660
+ return `z.ZodArray<${schema.items[0].nullable ? `z.ZodNullable<${item}>` : item}>`;
1717
1661
  }
1718
1662
  }
1719
1663
  const base = baseType(schema);
1720
1664
  if (!base) return void 0;
1721
- const meta = (0, _kubb_kit.syncSchemaRef)(schema);
1665
+ const meta = _kubb_kit.ast.ref.mergeSchema(schema);
1722
1666
  const optional = schema.optional || required === false;
1723
1667
  const wrapped = (() => {
1724
1668
  if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
@@ -1733,12 +1677,12 @@ function recursiveGetterType({ ctx, schema, required }) {
1733
1677
  * `direction` themselves, so {@link containsDirectionalNode} probes their children instead.
1734
1678
  */
1735
1679
  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
1680
+ _kubb_kit.ast.schema.types.object,
1681
+ _kubb_kit.ast.schema.types.array,
1682
+ _kubb_kit.ast.schema.types.tuple,
1683
+ _kubb_kit.ast.schema.types.union,
1684
+ _kubb_kit.ast.schema.types.intersection,
1685
+ _kubb_kit.ast.schema.types.ref
1742
1686
  ]);
1743
1687
  /**
1744
1688
  * Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
@@ -1769,7 +1713,7 @@ function variesByDirection({ node, printerOptions }) {
1769
1713
  */
1770
1714
  function containsDirectionalNode({ node, printerOptions }) {
1771
1715
  if (!node) return false;
1772
- return _kubb_kit.ast.findSchema(node, {
1716
+ return _kubb_kit.ast.schema.find(node, {
1773
1717
  predicate: (schema) => variesByDirection({
1774
1718
  node: schema,
1775
1719
  printerOptions
@@ -1781,10 +1725,10 @@ function containsDirectionalNode({ node, printerOptions }) {
1781
1725
  * Names of the `$ref` schemas the generator should route to their input (encode) variant.
1782
1726
  */
1783
1727
  function collectDirectionalRefNames({ node, printerOptions }) {
1784
- return _kubb_kit.ast.collectSync(node, { schema: (n) => n.type === _kubb_kit.ast.schemaTypes.ref && n.ref && containsDirectionalNode({
1728
+ return _kubb_kit.ast.node.collect(node, { schema: (n) => n.type === _kubb_kit.ast.schema.types.ref && n.ref && containsDirectionalNode({
1785
1729
  node: n,
1786
1730
  printerOptions
1787
- }) ? _kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
1731
+ }) ? _kubb_kit.ast.ref.getName(n) ?? void 0 : void 0 });
1788
1732
  }
1789
1733
  /**
1790
1734
  * Handlers that never recurse into children, so {@link variesByDirection} can call one directly
@@ -1806,6 +1750,7 @@ const standardDialect = {
1806
1750
  coercedNumber: (schema) => `z.coerce.number().pipe(${schema})`,
1807
1751
  catchall: (object, value) => `${object}.catchall(${value})`,
1808
1752
  nullable: (value) => `${value}.nullable()`,
1753
+ refine: (value, check) => `${value}.refine(${check})`,
1809
1754
  unique: (array) => `${array}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`,
1810
1755
  extend: (base, shape) => `${base}.extend(${shape})`,
1811
1756
  intersection: (left, right) => `${left}.and(${right})`,
@@ -1817,8 +1762,8 @@ const standardDialect = {
1817
1762
  };
1818
1763
  const scalarNodes = {
1819
1764
  ...buildScalarNodes(standardDialect),
1820
- bigint() {
1821
- return "z.coerce.bigint()";
1765
+ bigint(node) {
1766
+ return `z.coerce.bigint()${numberConstraints(bigintConstraints(node), "n")}`;
1822
1767
  },
1823
1768
  date(node) {
1824
1769
  if (node.representation !== "date") return "z.iso.date()";
@@ -1851,14 +1796,14 @@ const scalarNodes = {
1851
1796
  * const code = printer.print(stringNode) // "z.string()"
1852
1797
  * ```
1853
1798
  */
1854
- const printerZod = _kubb_kit.ast.createPrinter((options) => {
1799
+ const printerZod = (0, _kubb_kit.createPrinter)((options) => {
1855
1800
  return {
1856
1801
  name: "zod",
1857
1802
  options,
1858
- nodes: {
1803
+ nodes: withInputChecks({
1859
1804
  ...buildZodNodes(standardDialect, options.cyclicSchemas),
1860
1805
  ...scalarNodes
1861
- },
1806
+ }),
1862
1807
  overrides: options.nodes,
1863
1808
  print(node) {
1864
1809
  return printWithOmit(this, node, (value, meta) => applyModifiers({
@@ -1915,18 +1860,19 @@ const miniDialect = {
1915
1860
  coercedNumber: (schema) => `z.pipe(z.coerce.number(), ${schema})`,
1916
1861
  catchall: (object, value) => `z.catchall(${object}, ${value})`,
1917
1862
  nullable: (value) => `z.nullable(${value})`,
1863
+ refine: (value, check) => `${value}.check(z.refine(${check}))`,
1918
1864
  unique: (array) => `${array}.check(z.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" }))`,
1919
1865
  extend: (base, shape) => `z.extend(${base}, ${shape})`,
1920
1866
  intersection: (left, right) => `z.intersection(${left}, ${right})`
1921
1867
  };
1922
- const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
1868
+ const printerZodMini = (0, _kubb_kit.createPrinter)((options) => {
1923
1869
  return {
1924
1870
  name: "zod-mini",
1925
1871
  options,
1926
- nodes: {
1872
+ nodes: withInputChecks({
1927
1873
  ...buildZodNodes(miniDialect, options.cyclicSchemas),
1928
1874
  bigint(node) {
1929
- return `z.coerce.bigint()${numberChecksMini(node)}`;
1875
+ return `z.coerce.bigint()${numberChecksMini(bigintConstraints(node), "n")}`;
1930
1876
  },
1931
1877
  date(node) {
1932
1878
  if (node.representation === "string") return "z.iso.date()";
@@ -1939,7 +1885,7 @@ const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
1939
1885
  if (node.representation === "string") return "z.iso.time()";
1940
1886
  return "z.date()";
1941
1887
  }
1942
- },
1888
+ }),
1943
1889
  overrides: options.nodes,
1944
1890
  print(node) {
1945
1891
  return printWithOmit(this, node, (value, meta) => applyMiniModifiers({
@@ -2026,7 +1972,8 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2026
1972
  node,
2027
1973
  root,
2028
1974
  output,
2029
- group: group ?? void 0
1975
+ group: group ?? void 0,
1976
+ checks: true
2030
1977
  });
2031
1978
  const inputImportEntries = hasDirectionalNode ? [...directionalRefNames].map((schemaName) => ({
2032
1979
  name: [resolver.schema.inputName(schemaName)],
@@ -2117,7 +2064,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2117
2064
  });
2118
2065
  },
2119
2066
  operation(node, ctx) {
2120
- if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
2067
+ if (!_kubb_kit.ast.http.isOperation(node)) return null;
2121
2068
  const { adapter, config, resolver, root } = ctx;
2122
2069
  const { output, coercion, guidType, regexType, oneOfType, compile, mini, inferred, importPath, group, printer } = ctx.options;
2123
2070
  const dateType = getOasAdapter(adapter).options.dateType;
@@ -2146,17 +2093,18 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2146
2093
  node: schema,
2147
2094
  printerOptions
2148
2095
  })) : null;
2096
+ const schemaPrinter = getPrinter(resolver, {
2097
+ ...printerOptions,
2098
+ keysToOmit
2099
+ }, mini, direction);
2149
2100
  const imports = resolver.imports({
2150
2101
  node: schema,
2151
2102
  root,
2152
2103
  output,
2153
2104
  group: group ?? void 0,
2105
+ checks: true,
2154
2106
  name: (schemaName) => directionalRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName)
2155
2107
  });
2156
- const schemaPrinter = getPrinter(resolver, {
2157
- ...printerOptions,
2158
- keysToOmit
2159
- }, mini, direction);
2160
2108
  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
2109
  root: meta.file.path,
2162
2110
  path: imp.path,
@@ -2196,7 +2144,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2196
2144
  })] });
2197
2145
  }
2198
2146
  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;
2147
+ if (new Set(_kubb_kit.ast.http.responses.getImportedNames(responses).map((refName) => resolver.name(refName))).has(name)) return null;
2200
2148
  const members = responses.map((res) => _kubb_kit.ast.factory.createSchema({
2201
2149
  type: "ref",
2202
2150
  name: resolver.response.status(node, res.statusCode)
@@ -2229,14 +2177,27 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2229
2177
  keysToOmit: primary?.keysToOmit
2230
2178
  });
2231
2179
  });
2180
+ const toParamsSchema = (params) => _kubb_kit.ast.factory.createSchema({
2181
+ type: "object",
2182
+ primitive: "object",
2183
+ properties: params.map((param) => _kubb_kit.ast.factory.createProperty({
2184
+ name: param.name,
2185
+ required: param.required,
2186
+ schema: param.schema
2187
+ }))
2188
+ });
2189
+ const responseHeaderSchemas = node.responses.map((res) => res.headers?.length ? renderSchemaEntry({
2190
+ schema: toParamsSchema(res.headers),
2191
+ name: resolver.response.headers(node, res.statusCode)
2192
+ }) : null);
2232
2193
  const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
2233
- const successResponsesWithSchema = getSuccessResponses(responsesWithSchema);
2194
+ const successResponsesWithSchema = responsesWithSchema.filter((res) => _kubb_kit.ast.http.status.isSuccess(res));
2234
2195
  const responseUnionSchema = responsesWithSchema.length > 0 ? buildResponseUnion({
2235
2196
  responses: successResponsesWithSchema,
2236
2197
  name: resolver.response.response(node),
2237
2198
  fallbackUnknown: true
2238
2199
  }) : null;
2239
- const errorResponsesWithSchema = responsesWithSchema.filter((res) => !isSuccessStatusCode(res.statusCode));
2200
+ const errorResponsesWithSchema = responsesWithSchema.filter((res) => !_kubb_kit.ast.http.status.isSuccess(res));
2240
2201
  const errorUnionSchema = errorResponsesWithSchema.length > 0 ? buildResponseUnion({
2241
2202
  responses: errorResponsesWithSchema,
2242
2203
  name: resolver.response.error(node),
@@ -2263,7 +2224,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2263
2224
  description: node.requestBody.description ?? schema.description
2264
2225
  }), "encode");
2265
2226
  })();
2266
- const { path, query, header, cookie } = getOperationParameters(node);
2227
+ const { path, query, header, cookie } = _kubb_kit.ast.http.groupParameters(node);
2267
2228
  const paramGroupSchemas = [
2268
2229
  {
2269
2230
  kind: "path",
@@ -2282,15 +2243,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2282
2243
  params: cookie
2283
2244
  }
2284
2245
  ].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
- }),
2246
+ schema: toParamsSchema(params),
2294
2247
  name: resolver.param[kind](node, params[0]),
2295
2248
  direction: "encode"
2296
2249
  }));
@@ -2331,6 +2284,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2331
2284
  }),
2332
2285
  paramSchemas,
2333
2286
  responseSchemas,
2287
+ responseHeaderSchemas,
2334
2288
  responseUnionSchema,
2335
2289
  errorUnionSchema,
2336
2290
  requestSchema,
@@ -2343,11 +2297,11 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2343
2297
  });
2344
2298
  // kubb-extension.json
2345
2299
  var compatibility = {
2346
- "kubb": ">=5.16.0",
2300
+ "kubb": ">=5.18.4",
2347
2301
  "node": ">=22.22"
2348
2302
  };
2349
2303
  // package.json
2350
- var version = "5.9.0-canary.20261111T122006";
2304
+ var version = "5.9.0";
2351
2305
  // src/resolvers/resolverZod.ts
2352
2306
  /**
2353
2307
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
@@ -2367,15 +2321,15 @@ var version = "5.9.0-canary.20261111T122006";
2367
2321
  const resolverZod = (0, _kubb_kit.createResolver)({
2368
2322
  pluginName: "plugin-zod",
2369
2323
  name(name) {
2370
- return this.memo(`name:${name}`, () => ensureValidVarName((0, _kubb_kit.camelCase)(name, { suffix: "schema" })));
2324
+ return this.memo(`name:${name}`, () => ensureValidVarName(_kubb_kit.casing.camel(name, { suffix: "schema" })));
2371
2325
  },
2372
- file: createCasedFile((part) => (0, _kubb_kit.camelCase)(part, { suffix: "schema" })),
2326
+ file: createCasedFile((part) => _kubb_kit.casing.camel(part, { suffix: "schema" })),
2373
2327
  schema: {
2374
2328
  typeName(name) {
2375
- return this.memo(`typeName:${name}`, () => ensureValidVarName((0, _kubb_kit.pascalCase)(name, { suffix: "schema type" })));
2329
+ return this.memo(`typeName:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "schema type" })));
2376
2330
  },
2377
2331
  type(name) {
2378
- return this.memo(`type:${name}`, () => ensureValidVarName((0, _kubb_kit.pascalCase)(name, { suffix: "type" })));
2332
+ return this.memo(`type:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "type" })));
2379
2333
  },
2380
2334
  inputName(name) {
2381
2335
  return this.name(`${name} input`);
@@ -2384,10 +2338,10 @@ const resolverZod = (0, _kubb_kit.createResolver)({
2384
2338
  return this.schema.typeName(`${name} input`);
2385
2339
  },
2386
2340
  isName(name) {
2387
- return ensureValidVarName((0, _kubb_kit.camelCase)(name, { prefix: "is" }));
2341
+ return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "is" }));
2388
2342
  },
2389
2343
  assertName(name) {
2390
- return ensureValidVarName((0, _kubb_kit.camelCase)(name, { prefix: "assert" }));
2344
+ return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "assert" }));
2391
2345
  }
2392
2346
  },
2393
2347
  param: createOperationParamResolver(),
@@ -2461,7 +2415,7 @@ const pluginZod = (0, _kubb_kit.defineExtension)({
2461
2415
  exclude,
2462
2416
  include,
2463
2417
  override,
2464
- group: createGroupConfig(group),
2418
+ group: group ?? null,
2465
2419
  importPath,
2466
2420
  coercion,
2467
2421
  inferred: inferred === "direction" ? "codec" : inferred,