@kubb/plugin-zod 5.9.0-canary.20261111T115959 → 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,
@@ -318,437 +538,100 @@ function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
318
538
  schema: _kubb_kit.ast.factory.createSchema({
319
539
  type: "union",
320
540
  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;
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
  /**
@@ -756,6 +639,9 @@ function toFilePath(name, caseLast = _kubb_kit.camelCase) {
756
639
  * it is, so two of them never share an allocation even when a custom resolver gives both the same
757
640
  * name. Leave `scope` empty for the operation's own declaration.
758
641
  *
642
+ * The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro
643
+ * rewrites the method or path still resolves another plugin's names for the same operation.
644
+ *
759
645
  * @example
760
646
  * ```ts
761
647
  * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
@@ -764,8 +650,7 @@ function toFilePath(name, caseLast = _kubb_kit.camelCase) {
764
650
  function operationKey(node, ...scope) {
765
651
  return [
766
652
  "operation",
767
- node.method,
768
- node.path,
653
+ ...node.pointer ? [node.pointer] : [node.method, node.path],
769
654
  ...scope.map(String)
770
655
  ];
771
656
  }
@@ -836,6 +721,13 @@ function createOperationResponseResolver() {
836
721
  name
837
722
  });
838
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
+ },
839
731
  body(node) {
840
732
  const name = this.name(`${node.operationId} Body`);
841
733
  return this.resolveName({
@@ -877,75 +769,7 @@ function createCasedFile(caseLast) {
877
769
  * Names of the schemas in a circular reference chain, computed once per build.
878
770
  */
879
771
  function getCircularNames(ctx) {
880
- return ctx.memo("circularNames:set", () => _kubb_kit.ast.findCircularSchemas(ctx.driver.inputNode?.schemas ?? []));
881
- }
882
- // ../../internals/shared/src/group.ts
883
- /**
884
- * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
885
- * shared default naming so every plugin groups output consistently:
886
- *
887
- * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
888
- * - other groups use the camelCased group (`pet store` → `petStore`).
889
- *
890
- * A user-provided `group.name` always wins over the default namer, so callers stay in
891
- * control of their output folders. Returns `null` when grouping is disabled, matching the
892
- * per-plugin convention.
893
- *
894
- * @param group - The user-supplied group option, or `undefined` to disable grouping.
895
- *
896
- * @example
897
- * ```ts
898
- * createGroupConfig(group) // shared across every plugin
899
- * ```
900
- */
901
- function createGroupConfig(group) {
902
- if (!group) return null;
903
- const defaultName = (ctx) => {
904
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
905
- return (0, _kubb_kit.camelCase)(ctx.group);
906
- };
907
- return {
908
- ...group,
909
- name: group.name ? group.name : defaultName
910
- };
911
- }
912
- // ../../internals/shared/src/schemaTraversal.ts
913
- /**
914
- * Maps each property of an object schema to its transformed output. Pairs every result with the
915
- * original property so the printer keeps full control over modifiers, getters, and key syntax.
916
- *
917
- * @example
918
- * ```ts
919
- * const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
920
- * // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
921
- * ```
922
- */
923
- function mapSchemaProperties(node, transform) {
924
- return node.properties.map((property) => ({
925
- name: property.name,
926
- property,
927
- output: transform(property.schema)
928
- }));
929
- }
930
- /**
931
- * Maps each member of a union or intersection schema to its transformed output, pairing every
932
- * result with the original member.
933
- */
934
- function mapSchemaMembers(node, transform) {
935
- return (node.members ?? []).map((schema) => ({
936
- schema,
937
- output: transform(schema)
938
- }));
939
- }
940
- /**
941
- * Maps each item of an array or tuple schema to its transformed output, pairing every result with
942
- * the original item.
943
- */
944
- function mapSchemaItems(node, transform) {
945
- return (node.items ?? []).map((schema) => ({
946
- schema,
947
- output: transform(schema)
948
- }));
772
+ return ctx.memo("circularNames:set", () => _kubb_kit.ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []));
949
773
  }
950
774
  // src/components/Zod.tsx
951
775
  function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
@@ -953,9 +777,9 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
953
777
  const printerImports = printer.drainImports();
954
778
  if (!output) return;
955
779
  const needsAnnotation = cyclic && node.type !== "object";
956
- const isBare = _kubb_kit.ast.isBareRef(node, printer.options.keysToOmit);
957
- const isCyclic = Boolean(cyclic || printer.options.cyclicSchemas && (0, _kubb_kit.containsCircularRef)(node, { circularSchemas: printer.options.cyclicSchemas }));
958
- 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;
959
783
  const targetType = inferTypeName ?? `z.infer<typeof ${name}>`;
960
784
  const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
961
785
  const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
@@ -978,7 +802,7 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
978
802
  children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
979
803
  export: true,
980
804
  name,
981
- type: needsAnnotation ? "z.ZodType" : void 0,
805
+ type: needsAnnotation ? mini ? "z.ZodMiniType" : "z.ZodType" : void 0,
982
806
  children: value
983
807
  })
984
808
  }),
@@ -1038,7 +862,7 @@ function shouldCoerce(coercion, type) {
1038
862
  */
1039
863
  function isClosedKeySchema(node) {
1040
864
  if (!node) return false;
1041
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
865
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
1042
866
  if (resolved.type === "enum") return true;
1043
867
  if (resolved.type === "union") {
1044
868
  const members = resolved.members ?? [];
@@ -1048,12 +872,12 @@ function isClosedKeySchema(node) {
1048
872
  }
1049
873
  /**
1050
874
  * Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
1051
- * 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
1052
876
  * on `.and(…)`.
1053
877
  */
1054
878
  function isPlainInlineObject(node) {
1055
879
  if (node.type !== "object") return false;
1056
- if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
880
+ if (hasInputChecks(node)) return false;
1057
881
  if (node.nullable || node.optional || node.nullish) return false;
1058
882
  const hasAdditionalProperties = node.additionalProperties !== void 0;
1059
883
  const hasPatternProperties = Boolean(node.patternProperties);
@@ -1064,21 +888,21 @@ function isPlainInlineObject(node) {
1064
888
  * `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
1065
889
  * unresolved) objects, single-member unions of an object, and object-composable `allOf`.
1066
890
  *
1067
- * 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(…)`.
1068
892
  */
1069
893
  function isObjectSchemaNode(node, cyclicSchemas) {
1070
894
  if (node.nullable || node.optional || node.nullish) return false;
895
+ if (hasInputChecks(node)) return false;
1071
896
  if (node.type === "object") {
1072
- if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
1073
897
  const hasProperties = Boolean(node.properties?.length);
1074
898
  const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
1075
899
  const hasPatternProperties = Boolean(node.patternProperties);
1076
900
  return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties || node.propertyNames));
1077
901
  }
1078
902
  if (node.type === "ref") {
1079
- const refName = _kubb_kit.ast.resolveRefName(node);
903
+ const refName = _kubb_kit.ast.ref.getName(node);
1080
904
  if (refName && cyclicSchemas?.has(refName)) return false;
1081
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
905
+ const resolved = _kubb_kit.ast.ref.mergeSchema(node);
1082
906
  return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
1083
907
  }
1084
908
  if (node.type === "union") {
@@ -1101,23 +925,23 @@ function preserveRequiredProperties(members) {
1101
925
  const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
1102
926
  if (seen.has(node)) return false;
1103
927
  seen.add(node);
1104
- const resolved = (0, _kubb_kit.syncSchemaRef)(node);
1105
- 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);
1106
930
  if (composition) {
1107
- 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;
1108
932
  return composition.members?.some((child) => hasRequiredProperty({
1109
933
  node: child,
1110
934
  propertyName,
1111
935
  seen
1112
936
  })) ?? false;
1113
937
  }
1114
- return _kubb_kit.ast.resolveSchemaProperties({
938
+ return _kubb_kit.ast.schema.findProperties({
1115
939
  node: resolved,
1116
940
  propertyName
1117
941
  }).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
1118
942
  };
1119
943
  return members.map((member, index) => {
1120
- 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);
1121
945
  if (!object || index === 0) return member;
1122
946
  return {
1123
947
  ...object,
@@ -1146,7 +970,7 @@ function preserveRequiredProperties(members) {
1146
970
  * representation, strings are quoted.
1147
971
  */
1148
972
  function formatDefault(value) {
1149
- if (typeof value === "string") return stringify(value);
973
+ if (typeof value === "string") return _kubb_kit.text.quote(value);
1150
974
  if (typeof value === "object" && value !== null) return JSON.stringify(value);
1151
975
  return String(value ?? "");
1152
976
  }
@@ -1161,16 +985,17 @@ function formatDefault(value) {
1161
985
  */
1162
986
  function defaultLiteral(node, value) {
1163
987
  if (value === null) return null;
1164
- const resolved = node ? (0, _kubb_kit.syncSchemaRef)(node) : void 0;
1165
- 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")) {
1166
991
  if (typeof value === "bigint") return `BigInt(${value})`;
1167
992
  if (typeof value === "number" && Number.isInteger(value)) return `BigInt(${value})`;
1168
993
  return null;
1169
994
  }
1170
- if (resolved && _kubb_kit.ast.narrowSchema(resolved, "array")) return Array.isArray(value) ? JSON.stringify(value) : null;
1171
- 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;
1172
997
  if (enumNode) {
1173
- const values = enumNode.namedEnumValues?.map((member) => member.value) ?? enumNode.enumValues ?? [];
998
+ const values = _kubb_kit.ast.schema.getLiterals(enumNode);
1174
999
  if (values.length) {
1175
1000
  const match = values.find((member) => member === value || String(member) === String(value));
1176
1001
  return match != null ? formatDefault(match) : null;
@@ -1239,13 +1064,13 @@ function patternKeySchemaMini({ patterns, regexType }) {
1239
1064
  * Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
1240
1065
  * using the standard chainable Zod v4 API.
1241
1066
  */
1242
- function numberConstraints({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
1067
+ function numberConstraints({ min, max, exclusive, multipleOf }, suffix = "") {
1243
1068
  return [
1244
- min !== void 0 ? `.min(${min})` : "",
1245
- max !== void 0 ? `.max(${max})` : "",
1246
- exclusiveMinimum !== void 0 ? `.gt(${exclusiveMinimum})` : "",
1247
- exclusiveMaximum !== void 0 ? `.lt(${exclusiveMaximum})` : "",
1248
- 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})` : ""
1249
1074
  ].join("");
1250
1075
  }
1251
1076
  /**
@@ -1260,36 +1085,71 @@ function lengthConstraints({ min, max, pattern, regexType }) {
1260
1085
  ].join("");
1261
1086
  }
1262
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
+ /**
1263
1102
  * Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
1264
1103
  */
1265
- function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
1266
- const checks = [];
1267
- if (min !== void 0) checks.push(`z.minimum(${min})`);
1268
- if (max !== void 0) checks.push(`z.maximum(${max})`);
1269
- if (exclusiveMinimum !== void 0) checks.push(`z.minimum(${exclusiveMinimum}, { exclusive: true })`);
1270
- if (exclusiveMaximum !== void 0) checks.push(`z.maximum(${exclusiveMaximum}, { exclusive: true })`);
1271
- 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);
1272
1112
  return checks.length ? `.check(${checks.join(", ")})` : "";
1273
1113
  }
1274
1114
  /**
1275
1115
  * Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
1276
1116
  */
1277
1117
  function lengthChecksMini({ min, max, pattern, regexType }) {
1278
- const checks = [];
1279
- if (min !== void 0) checks.push(`z.minLength(${min})`);
1280
- if (max !== void 0) checks.push(`z.maxLength(${max})`);
1281
- 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);
1282
1123
  return checks.length ? `.check(${checks.join(", ")})` : "";
1283
1124
  }
1284
1125
  /**
1285
- * 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}.
1286
1136
  */
1287
- function objectConstraints({ value, node }) {
1288
- if (node.type !== "object") return value;
1289
- 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 }) {
1290
1145
  const skip = "typeof value !== \"object\" || value === null || Array.isArray(value)";
1291
- 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" })`);
1292
- 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);
1293
1153
  return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
1294
1154
  }
1295
1155
  /**
@@ -1305,8 +1165,8 @@ function applyModifiers({ value, schema, nullable, optional, nullish, defaultVal
1305
1165
  })();
1306
1166
  const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
1307
1167
  const withDefault = literal !== null ? `${withModifier}.default(${literal})` : withModifier;
1308
- const withDescription = description ? `${withDefault}.describe(${stringify(description)})` : withDefault;
1309
- 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;
1310
1170
  }
1311
1171
  function modifierDepth(schema) {
1312
1172
  if (schema.nullish || schema.nullable && schema.optional) return 2;
@@ -1324,7 +1184,7 @@ function modifierDepth(schema) {
1324
1184
  * `.unwrap()`, so its printer passes `._zod.def.innerType` as the `step`.
1325
1185
  */
1326
1186
  function omitUnwrapChain(node, step = ".unwrap()") {
1327
- const ref = _kubb_kit.ast.narrowSchema(node, "ref");
1187
+ const ref = _kubb_kit.ast.schema.narrow(node, "ref");
1328
1188
  if (!ref) return "";
1329
1189
  const target = ref.schema ?? ref;
1330
1190
  const depth = modifierDepth(target) + (target.default !== void 0 ? 1 : 0);
@@ -1346,7 +1206,7 @@ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaul
1346
1206
  /**
1347
1207
  * Calls for string `format`s with a dedicated Zod constructor; a `Map` so `constructor` cannot hit `Object.prototype`.
1348
1208
  */
1349
- const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
1209
+ const stringFormatCalls = /* @__PURE__ */ new Map([
1350
1210
  ["byte", "z.base64()"],
1351
1211
  ["base64", "z.base64()"],
1352
1212
  ["base64url", "z.base64url()"],
@@ -1357,6 +1217,16 @@ const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
1357
1217
  ["hostname", "z.hostname()"]
1358
1218
  ]);
1359
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
+ /**
1360
1230
  * Marks an inline `oneOf` object strict, so `object()` builds `z.strictObject(...)` and no eager `.shape` read hits a deferred getter.
1361
1231
  */
1362
1232
  function strictOneOfNode(node) {
@@ -1369,13 +1239,14 @@ function strictOneOfNode(node) {
1369
1239
  * The constraint chain an `allOf` member adds to a bare string, number or array; each printer passes its own renderers.
1370
1240
  */
1371
1241
  function getMemberConstraint({ member, regexType, lengths, numbers }) {
1242
+ if (hasInputChecks(member) || _kubb_kit.ast.schema.narrow(member, "array")?.contains) return void 0;
1372
1243
  if (member.primitive === "string") return lengths({
1373
- ..._kubb_kit.ast.narrowSchema(member, "string") ?? {},
1244
+ ..._kubb_kit.ast.schema.narrow(member, "string") ?? {},
1374
1245
  regexType
1375
1246
  }) || void 0;
1376
- 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;
1377
1248
  if (member.primitive === "array") return lengths({
1378
- ..._kubb_kit.ast.narrowSchema(member, "array") ?? {},
1249
+ ..._kubb_kit.ast.schema.narrow(member, "array") ?? {},
1379
1250
  regexType
1380
1251
  }) || void 0;
1381
1252
  }
@@ -1383,24 +1254,26 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
1383
1254
  * Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
1384
1255
  */
1385
1256
  function buildObjectShape(ctx, node, renderProperty) {
1386
- const objectNode = _kubb_kit.ast.narrowSchema(node, "object");
1257
+ const objectNode = _kubb_kit.ast.schema.narrow(node, "object");
1387
1258
  if (!objectNode) return "{}";
1388
- const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, _kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
1389
- 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 }) => {
1390
1261
  const hasSelfRef = isCyclic(schema);
1391
1262
  const savedCyclicSchemas = ctx.options.cyclicSchemas;
1392
1263
  if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
1393
1264
  const baseOutput = ctx.transform(schema) ?? ctx.transform(_kubb_kit.ast.factory.createSchema({ type: "unknown" }));
1394
1265
  if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
1395
1266
  return baseOutput;
1396
- }).map(({ name, property, output: value }) => {
1397
- const { schema } = property;
1267
+ });
1268
+ return buildObject(objectNode.properties.map((property, index) => {
1269
+ const { name, schema } = property;
1270
+ const value = transformed[index];
1398
1271
  return renderProperty({
1399
1272
  name,
1400
1273
  value,
1401
1274
  schema,
1402
1275
  required: property.required,
1403
- meta: (0, _kubb_kit.syncSchemaRef)(schema),
1276
+ meta: _kubb_kit.ast.ref.mergeSchema(schema),
1404
1277
  cyclic: isCyclic(schema)
1405
1278
  });
1406
1279
  }));
@@ -1412,14 +1285,14 @@ function printWithOmit(ctx, node, modify, mini = false) {
1412
1285
  const { keysToOmit } = ctx.options;
1413
1286
  const transformed = ctx.transform(node);
1414
1287
  if (!transformed) return null;
1415
- const meta = (0, _kubb_kit.syncSchemaRef)(node);
1288
+ const meta = _kubb_kit.ast.ref.mergeSchema(node);
1416
1289
  return modify((() => {
1417
1290
  if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
1418
1291
  const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
1419
1292
  const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
1420
1293
  const omit = (target) => {
1421
1294
  const inner = `${target}${unwrap}`;
1422
- const hasObjectConstraints = meta.type === "object" && (meta.minProperties !== void 0 || meta.maxProperties !== void 0);
1295
+ const hasObjectConstraints = hasInputChecks(meta);
1423
1296
  const pipe = mini ? `${inner}._zod.def` : inner;
1424
1297
  const object = hasObjectConstraints ? `${pipe}.out` : inner;
1425
1298
  const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
@@ -1438,7 +1311,7 @@ function printWithOmit(ctx, node, modify, mini = false) {
1438
1311
  function getLiteralUnion({ node, options }) {
1439
1312
  if (node.strategy === "one" && options.oneOfType === "xor" || options.nodes?.enum) return void 0;
1440
1313
  const values = (node.members ?? []).map((member) => {
1441
- 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);
1442
1315
  return enumNode && !enumNode.name && !enumNode.nullable && !enumNode.namedEnumValues && enumNode.enumValues?.length === 1 ? enumNode.enumValues[0] : void 0;
1443
1316
  });
1444
1317
  if (!values.length || !values.every((value) => value !== void 0 && value !== null)) return void 0;
@@ -1451,19 +1324,26 @@ function getLiteralUnion({ node, options }) {
1451
1324
  * Whether a discriminated union member's discriminator property prints as a coerced literal union.
1452
1325
  */
1453
1326
  function hasCoercedDiscriminator({ member, propertyName, options }) {
1454
- return _kubb_kit.ast.resolveSchemaProperties({
1327
+ return _kubb_kit.ast.schema.findProperties({
1455
1328
  node: member,
1456
1329
  propertyName
1457
1330
  }).some(({ schema }) => {
1458
- const resolved = (0, _kubb_kit.syncSchemaRef)(schema);
1331
+ const resolved = _kubb_kit.ast.ref.mergeSchema(schema);
1459
1332
  return resolved.type === "union" && !!getLiteralUnion({
1460
1333
  node: resolved,
1461
1334
  options
1462
1335
  })?.coerce;
1463
1336
  });
1464
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
+ }
1465
1345
  /**
1466
- * 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
1467
1347
  * scalars and `bigint` differ per dialect, so each printer adds its own.
1468
1348
  */
1469
1349
  function buildScalarNodes(dialect) {
@@ -1476,18 +1356,20 @@ function buildScalarNodes(dialect) {
1476
1356
  null: () => "z.null()",
1477
1357
  string(node) {
1478
1358
  const coerce = shouldCoerce(this.options.coercion, "strings");
1479
- const call = coerce ? void 0 : STRING_FORMAT_CALLS.get(node.format);
1480
- if (call) return `${call}${dialect.lengths({
1481
- ...node,
1482
- regexType: this.options.regexType
1483
- })}`;
1484
- 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()");
1485
1360
  const pattern = node.pattern ?? integerFormatPattern(node.format);
1486
- return `${base}${dialect.lengths({
1361
+ const value = `${base}${dialect.lengths({
1487
1362
  ...node,
1488
1363
  pattern,
1489
1364
  regexType: this.options.regexType
1490
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;
1491
1373
  },
1492
1374
  number(node) {
1493
1375
  return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${dialect.numbers(node)}`;
@@ -1520,7 +1402,7 @@ function buildScalarNodes(dialect) {
1520
1402
  ipv6: () => "z.ipv6()",
1521
1403
  blob: () => "z.instanceof(Blob)",
1522
1404
  enum(node) {
1523
- 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);
1524
1406
  if (node.namedEnumValues?.length) {
1525
1407
  const literals = nonNullValues.map((v) => buildLiteral(v));
1526
1408
  if (literals.length === 1) return literals[0];
@@ -1539,11 +1421,12 @@ function buildScalarNodes(dialect) {
1539
1421
  */
1540
1422
  function buildZodNodes(dialect, cyclicSchemas) {
1541
1423
  const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
1424
+ const printNullable = (schema, output) => output && schema.nullable ? dialect.nullable(output) : output;
1542
1425
  return {
1543
1426
  ...buildScalarNodes(dialect),
1544
1427
  ref(node) {
1545
1428
  if (!node.name) return null;
1546
- const refName = _kubb_kit.ast.resolveRefName(node);
1429
+ const refName = _kubb_kit.ast.ref.getName(node);
1547
1430
  if (!refName) return null;
1548
1431
  const useInputVariant = node.ref != null && (dialect.useInputVariant?.(node, this.options) ?? false);
1549
1432
  const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
@@ -1558,47 +1441,50 @@ function buildZodNodes(dialect, cyclicSchemas) {
1558
1441
  const isStrict = node.additionalProperties === false && patterns.length === 0;
1559
1442
  const isLoose = node.additionalProperties === true;
1560
1443
  const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${dialect.objectShape(this, node)})`;
1561
- return objectConstraints({
1562
- value: (() => {
1563
- const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1564
- if (node.additionalProperties && node.additionalProperties !== true) {
1565
- const catchallType = this.transform(node.additionalProperties);
1566
- if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1567
- return catchallType ? dialect.catchall(objectBase, catchallType) : objectBase;
1568
- }
1569
- if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1570
- const unknownType = this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schemaTypes.unknown }));
1571
- return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1572
- }
1573
- if (node.additionalProperties === true) return objectBase;
1574
- if (isStrict) return objectBase;
1575
- if (patterns.length > 0) {
1576
- const values = patterns.map(([, valueSchema]) => {
1577
- const valueType = this.transform(valueSchema) ?? this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schemaTypes.unknown }));
1578
- return valueSchema.nullable ? dialect.nullable(valueType) : valueType;
1579
- });
1580
- const distinct = [...new Set(values)];
1581
- const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
1582
- if (entries.length > 0) return dialect.catchall(objectBase, value);
1583
- return `z.record(${dialect.patternKey({
1584
- patterns: patterns.map(([pattern]) => pattern),
1585
- regexType: this.options.regexType
1586
- })}, ${value})`;
1587
- }
1588
- return objectBase;
1589
- })(),
1590
- node
1591
- });
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
+ })();
1592
1472
  },
1593
1473
  array(node) {
1594
- 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({
1595
1475
  ...node,
1596
1476
  regexType: this.options.regexType
1597
1477
  })}`;
1598
- 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;
1599
1481
  },
1600
1482
  tuple(node) {
1601
- 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;
1602
1488
  },
1603
1489
  union(node) {
1604
1490
  const literalUnion = getLiteralUnion({
@@ -1611,24 +1497,30 @@ function buildZodNodes(dialect, cyclicSchemas) {
1611
1497
  }
1612
1498
  const nodeMembers = node.members ?? [];
1613
1499
  const { discriminatorPropertyName } = node;
1614
- 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({
1615
1504
  member: m,
1616
1505
  propertyName: discriminatorPropertyName,
1617
1506
  options: this.options
1618
1507
  }));
1619
1508
  const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
1620
- const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
1621
- 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);
1622
1514
  if (members.length === 0) return "";
1623
1515
  if (members.length === 1) return members[0];
1624
- if (isDiscriminated) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`;
1516
+ if (isDiscriminated) return `z.discriminatedUnion(${_kubb_kit.text.quote(node.discriminatorPropertyName)}, ${buildList(members)})`;
1625
1517
  if (isXor) return `z.xor(${buildList(members)})`;
1626
1518
  return `z.union(${buildList(members)})`;
1627
1519
  },
1628
1520
  intersection(node) {
1629
1521
  const members = node.members ?? [];
1630
1522
  if (members.length === 0) return "";
1631
- 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);
1632
1524
  const transform = (member) => {
1633
1525
  if (!hasSiblingProperties) return this.transform(member);
1634
1526
  unionsWithSiblings.add(member);
@@ -1642,7 +1534,18 @@ function buildZodNodes(dialect, cyclicSchemas) {
1642
1534
  if (!first) return "";
1643
1535
  const firstBase = transform(first);
1644
1536
  if (!firstBase) return "";
1645
- 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
+ }
1646
1549
  return rest.reduce((acc, member) => {
1647
1550
  const constraint = getMemberConstraint({
1648
1551
  member,
@@ -1657,15 +1560,57 @@ function buildZodNodes(dialect, cyclicSchemas) {
1657
1560
  }
1658
1561
  };
1659
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
+ }
1660
1601
  // src/printers/printerZod.ts
1661
1602
  function strictOneOfMember(member, node, cyclicSchemas) {
1662
- if (node.type === _kubb_kit.ast.schemaTypes.ref) {
1603
+ if (node.type === _kubb_kit.ast.schema.types.ref) {
1663
1604
  if (member.startsWith("z.lazy(")) return member;
1664
- const refName = _kubb_kit.ast.resolveRefName(node);
1605
+ const refName = _kubb_kit.ast.ref.getName(node);
1665
1606
  if (refName && cyclicSchemas?.has(refName)) return member;
1666
- const schema = (0, _kubb_kit.syncSchemaRef)(node);
1607
+ const schema = _kubb_kit.ast.ref.mergeSchema(node);
1667
1608
  if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
1668
- 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
+ }
1669
1614
  }
1670
1615
  return member;
1671
1616
  }
@@ -1681,7 +1626,7 @@ function buildZodObjectShape(ctx, node) {
1681
1626
  optional: schema.optional || required === false,
1682
1627
  nullish: schema.nullish,
1683
1628
  defaultValue: meta.default,
1684
- 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,
1685
1630
  examples: meta.examples
1686
1631
  });
1687
1632
  if (!cyclic) return `${objectKey(name)}: ${body}`;
@@ -1702,21 +1647,22 @@ function buildZodObjectShape(ctx, node) {
1702
1647
  function recursiveGetterType({ ctx, schema, required }) {
1703
1648
  function baseType(schema) {
1704
1649
  if (ctx.options.nodes?.[schema.type]) return void 0;
1705
- if (schema.type === _kubb_kit.ast.schemaTypes.ref) {
1650
+ if (schema.type === _kubb_kit.ast.schema.types.ref) {
1706
1651
  const saved = ctx.options.cyclicSchemas;
1707
1652
  ctx.options.cyclicSchemas = void 0;
1708
1653
  const name = ctx.transform(schema);
1709
1654
  ctx.options.cyclicSchemas = saved;
1710
1655
  return name ? `typeof ${name}` : void 0;
1711
1656
  }
1712
- 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) {
1713
1658
  const item = baseType(schema.items[0]);
1714
- 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}>`;
1715
1661
  }
1716
1662
  }
1717
1663
  const base = baseType(schema);
1718
1664
  if (!base) return void 0;
1719
- const meta = (0, _kubb_kit.syncSchemaRef)(schema);
1665
+ const meta = _kubb_kit.ast.ref.mergeSchema(schema);
1720
1666
  const optional = schema.optional || required === false;
1721
1667
  const wrapped = (() => {
1722
1668
  if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
@@ -1731,12 +1677,12 @@ function recursiveGetterType({ ctx, schema, required }) {
1731
1677
  * `direction` themselves, so {@link containsDirectionalNode} probes their children instead.
1732
1678
  */
1733
1679
  const CONTAINER_TYPES = /* @__PURE__ */ new Set([
1734
- _kubb_kit.ast.schemaTypes.object,
1735
- _kubb_kit.ast.schemaTypes.array,
1736
- _kubb_kit.ast.schemaTypes.tuple,
1737
- _kubb_kit.ast.schemaTypes.union,
1738
- _kubb_kit.ast.schemaTypes.intersection,
1739
- _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
1740
1686
  ]);
1741
1687
  /**
1742
1688
  * Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
@@ -1767,7 +1713,7 @@ function variesByDirection({ node, printerOptions }) {
1767
1713
  */
1768
1714
  function containsDirectionalNode({ node, printerOptions }) {
1769
1715
  if (!node) return false;
1770
- return _kubb_kit.ast.findSchema(node, {
1716
+ return _kubb_kit.ast.schema.find(node, {
1771
1717
  predicate: (schema) => variesByDirection({
1772
1718
  node: schema,
1773
1719
  printerOptions
@@ -1779,10 +1725,10 @@ function containsDirectionalNode({ node, printerOptions }) {
1779
1725
  * Names of the `$ref` schemas the generator should route to their input (encode) variant.
1780
1726
  */
1781
1727
  function collectDirectionalRefNames({ node, printerOptions }) {
1782
- 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({
1783
1729
  node: n,
1784
1730
  printerOptions
1785
- }) ? _kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
1731
+ }) ? _kubb_kit.ast.ref.getName(n) ?? void 0 : void 0 });
1786
1732
  }
1787
1733
  /**
1788
1734
  * Handlers that never recurse into children, so {@link variesByDirection} can call one directly
@@ -1804,6 +1750,7 @@ const standardDialect = {
1804
1750
  coercedNumber: (schema) => `z.coerce.number().pipe(${schema})`,
1805
1751
  catchall: (object, value) => `${object}.catchall(${value})`,
1806
1752
  nullable: (value) => `${value}.nullable()`,
1753
+ refine: (value, check) => `${value}.refine(${check})`,
1807
1754
  unique: (array) => `${array}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`,
1808
1755
  extend: (base, shape) => `${base}.extend(${shape})`,
1809
1756
  intersection: (left, right) => `${left}.and(${right})`,
@@ -1815,8 +1762,8 @@ const standardDialect = {
1815
1762
  };
1816
1763
  const scalarNodes = {
1817
1764
  ...buildScalarNodes(standardDialect),
1818
- bigint() {
1819
- return "z.coerce.bigint()";
1765
+ bigint(node) {
1766
+ return `z.coerce.bigint()${numberConstraints(bigintConstraints(node), "n")}`;
1820
1767
  },
1821
1768
  date(node) {
1822
1769
  if (node.representation !== "date") return "z.iso.date()";
@@ -1849,14 +1796,14 @@ const scalarNodes = {
1849
1796
  * const code = printer.print(stringNode) // "z.string()"
1850
1797
  * ```
1851
1798
  */
1852
- const printerZod = _kubb_kit.ast.createPrinter((options) => {
1799
+ const printerZod = (0, _kubb_kit.createPrinter)((options) => {
1853
1800
  return {
1854
1801
  name: "zod",
1855
1802
  options,
1856
- nodes: {
1803
+ nodes: withInputChecks({
1857
1804
  ...buildZodNodes(standardDialect, options.cyclicSchemas),
1858
1805
  ...scalarNodes
1859
- },
1806
+ }),
1860
1807
  overrides: options.nodes,
1861
1808
  print(node) {
1862
1809
  return printWithOmit(this, node, (value, meta) => applyModifiers({
@@ -1913,18 +1860,19 @@ const miniDialect = {
1913
1860
  coercedNumber: (schema) => `z.pipe(z.coerce.number(), ${schema})`,
1914
1861
  catchall: (object, value) => `z.catchall(${object}, ${value})`,
1915
1862
  nullable: (value) => `z.nullable(${value})`,
1863
+ refine: (value, check) => `${value}.check(z.refine(${check}))`,
1916
1864
  unique: (array) => `${array}.check(z.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" }))`,
1917
1865
  extend: (base, shape) => `z.extend(${base}, ${shape})`,
1918
1866
  intersection: (left, right) => `z.intersection(${left}, ${right})`
1919
1867
  };
1920
- const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
1868
+ const printerZodMini = (0, _kubb_kit.createPrinter)((options) => {
1921
1869
  return {
1922
1870
  name: "zod-mini",
1923
1871
  options,
1924
- nodes: {
1872
+ nodes: withInputChecks({
1925
1873
  ...buildZodNodes(miniDialect, options.cyclicSchemas),
1926
1874
  bigint(node) {
1927
- return `z.coerce.bigint()${numberChecksMini(node)}`;
1875
+ return `z.coerce.bigint()${numberChecksMini(bigintConstraints(node), "n")}`;
1928
1876
  },
1929
1877
  date(node) {
1930
1878
  if (node.representation === "string") return "z.iso.date()";
@@ -1937,7 +1885,7 @@ const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
1937
1885
  if (node.representation === "string") return "z.iso.time()";
1938
1886
  return "z.date()";
1939
1887
  }
1940
- },
1888
+ }),
1941
1889
  overrides: options.nodes,
1942
1890
  print(node) {
1943
1891
  return printWithOmit(this, node, (value, meta) => applyMiniModifiers({
@@ -2024,7 +1972,8 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2024
1972
  node,
2025
1973
  root,
2026
1974
  output,
2027
- group: group ?? void 0
1975
+ group: group ?? void 0,
1976
+ checks: true
2028
1977
  });
2029
1978
  const inputImportEntries = hasDirectionalNode ? [...directionalRefNames].map((schemaName) => ({
2030
1979
  name: [resolver.schema.inputName(schemaName)],
@@ -2115,7 +2064,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2115
2064
  });
2116
2065
  },
2117
2066
  operation(node, ctx) {
2118
- if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
2067
+ if (!_kubb_kit.ast.http.isOperation(node)) return null;
2119
2068
  const { adapter, config, resolver, root } = ctx;
2120
2069
  const { output, coercion, guidType, regexType, oneOfType, compile, mini, inferred, importPath, group, printer } = ctx.options;
2121
2070
  const dateType = getOasAdapter(adapter).options.dateType;
@@ -2144,17 +2093,18 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2144
2093
  node: schema,
2145
2094
  printerOptions
2146
2095
  })) : null;
2096
+ const schemaPrinter = getPrinter(resolver, {
2097
+ ...printerOptions,
2098
+ keysToOmit
2099
+ }, mini, direction);
2147
2100
  const imports = resolver.imports({
2148
2101
  node: schema,
2149
2102
  root,
2150
2103
  output,
2151
2104
  group: group ?? void 0,
2105
+ checks: true,
2152
2106
  name: (schemaName) => directionalRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName)
2153
2107
  });
2154
- const schemaPrinter = getPrinter(resolver, {
2155
- ...printerOptions,
2156
- keysToOmit
2157
- }, mini, direction);
2158
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, {
2159
2109
  root: meta.file.path,
2160
2110
  path: imp.path,
@@ -2194,7 +2144,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2194
2144
  })] });
2195
2145
  }
2196
2146
  function buildResponseUnion({ responses, name, fallbackUnknown }) {
2197
- 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;
2198
2148
  const members = responses.map((res) => _kubb_kit.ast.factory.createSchema({
2199
2149
  type: "ref",
2200
2150
  name: resolver.response.status(node, res.statusCode)
@@ -2227,14 +2177,27 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2227
2177
  keysToOmit: primary?.keysToOmit
2228
2178
  });
2229
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);
2230
2193
  const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
2231
- const successResponsesWithSchema = getSuccessResponses(responsesWithSchema);
2194
+ const successResponsesWithSchema = responsesWithSchema.filter((res) => _kubb_kit.ast.http.status.isSuccess(res));
2232
2195
  const responseUnionSchema = responsesWithSchema.length > 0 ? buildResponseUnion({
2233
2196
  responses: successResponsesWithSchema,
2234
2197
  name: resolver.response.response(node),
2235
2198
  fallbackUnknown: true
2236
2199
  }) : null;
2237
- const errorResponsesWithSchema = responsesWithSchema.filter((res) => !isSuccessStatusCode(res.statusCode));
2200
+ const errorResponsesWithSchema = responsesWithSchema.filter((res) => !_kubb_kit.ast.http.status.isSuccess(res));
2238
2201
  const errorUnionSchema = errorResponsesWithSchema.length > 0 ? buildResponseUnion({
2239
2202
  responses: errorResponsesWithSchema,
2240
2203
  name: resolver.response.error(node),
@@ -2261,7 +2224,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2261
2224
  description: node.requestBody.description ?? schema.description
2262
2225
  }), "encode");
2263
2226
  })();
2264
- const { path, query, header, cookie } = getOperationParameters(node);
2227
+ const { path, query, header, cookie } = _kubb_kit.ast.http.groupParameters(node);
2265
2228
  const paramGroupSchemas = [
2266
2229
  {
2267
2230
  kind: "path",
@@ -2280,15 +2243,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2280
2243
  params: cookie
2281
2244
  }
2282
2245
  ].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
2283
- schema: _kubb_kit.ast.factory.createSchema({
2284
- type: "object",
2285
- primitive: "object",
2286
- properties: params.map((param) => _kubb_kit.ast.factory.createProperty({
2287
- name: param.name,
2288
- required: param.required,
2289
- schema: param.schema
2290
- }))
2291
- }),
2246
+ schema: toParamsSchema(params),
2292
2247
  name: resolver.param[kind](node, params[0]),
2293
2248
  direction: "encode"
2294
2249
  }));
@@ -2329,6 +2284,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2329
2284
  }),
2330
2285
  paramSchemas,
2331
2286
  responseSchemas,
2287
+ responseHeaderSchemas,
2332
2288
  responseUnionSchema,
2333
2289
  errorUnionSchema,
2334
2290
  requestSchema,
@@ -2341,11 +2297,11 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2341
2297
  });
2342
2298
  // kubb-extension.json
2343
2299
  var compatibility = {
2344
- "kubb": ">=5.16.0",
2300
+ "kubb": ">=5.18.4",
2345
2301
  "node": ">=22.22"
2346
2302
  };
2347
2303
  // package.json
2348
- var version = "5.9.0-canary.20261111T115959";
2304
+ var version = "5.9.0";
2349
2305
  // src/resolvers/resolverZod.ts
2350
2306
  /**
2351
2307
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
@@ -2365,15 +2321,15 @@ var version = "5.9.0-canary.20261111T115959";
2365
2321
  const resolverZod = (0, _kubb_kit.createResolver)({
2366
2322
  pluginName: "plugin-zod",
2367
2323
  name(name) {
2368
- 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" })));
2369
2325
  },
2370
- file: createCasedFile((part) => (0, _kubb_kit.camelCase)(part, { suffix: "schema" })),
2326
+ file: createCasedFile((part) => _kubb_kit.casing.camel(part, { suffix: "schema" })),
2371
2327
  schema: {
2372
2328
  typeName(name) {
2373
- 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" })));
2374
2330
  },
2375
2331
  type(name) {
2376
- 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" })));
2377
2333
  },
2378
2334
  inputName(name) {
2379
2335
  return this.name(`${name} input`);
@@ -2382,10 +2338,10 @@ const resolverZod = (0, _kubb_kit.createResolver)({
2382
2338
  return this.schema.typeName(`${name} input`);
2383
2339
  },
2384
2340
  isName(name) {
2385
- return ensureValidVarName((0, _kubb_kit.camelCase)(name, { prefix: "is" }));
2341
+ return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "is" }));
2386
2342
  },
2387
2343
  assertName(name) {
2388
- return ensureValidVarName((0, _kubb_kit.camelCase)(name, { prefix: "assert" }));
2344
+ return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "assert" }));
2389
2345
  }
2390
2346
  },
2391
2347
  param: createOperationParamResolver(),
@@ -2459,7 +2415,7 @@ const pluginZod = (0, _kubb_kit.defineExtension)({
2459
2415
  exclude,
2460
2416
  include,
2461
2417
  override,
2462
- group: createGroupConfig(group),
2418
+ group: group ?? null,
2463
2419
  importPath,
2464
2420
  coercion,
2465
2421
  inferred: inferred === "direction" ? "codec" : inferred,