@kubb/plugin-client 5.0.0-beta.4 → 5.0.0-beta.56

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.
Files changed (56) hide show
  1. package/README.md +39 -24
  2. package/dist/clients/axios.cjs +26 -4
  3. package/dist/clients/axios.cjs.map +1 -1
  4. package/dist/clients/axios.d.ts +11 -5
  5. package/dist/clients/axios.js +26 -4
  6. package/dist/clients/axios.js.map +1 -1
  7. package/dist/clients/fetch.cjs +77 -9
  8. package/dist/clients/fetch.cjs.map +1 -1
  9. package/dist/clients/fetch.d.ts +10 -3
  10. package/dist/clients/fetch.js +77 -9
  11. package/dist/clients/fetch.js.map +1 -1
  12. package/dist/index.cjs +836 -514
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +206 -105
  15. package/dist/index.js +833 -515
  16. package/dist/index.js.map +1 -1
  17. package/dist/templates/clients/axios.source.cjs +1 -1
  18. package/dist/templates/clients/axios.source.cjs.map +1 -1
  19. package/dist/templates/clients/axios.source.d.ts +1 -1
  20. package/dist/templates/clients/axios.source.js +1 -1
  21. package/dist/templates/clients/axios.source.js.map +1 -1
  22. package/dist/templates/clients/fetch.source.cjs +1 -1
  23. package/dist/templates/clients/fetch.source.cjs.map +1 -1
  24. package/dist/templates/clients/fetch.source.d.ts +1 -1
  25. package/dist/templates/clients/fetch.source.js +1 -1
  26. package/dist/templates/clients/fetch.source.js.map +1 -1
  27. package/dist/templates/config.source.cjs.map +1 -1
  28. package/dist/templates/config.source.d.ts +1 -1
  29. package/dist/templates/config.source.js.map +1 -1
  30. package/package.json +14 -26
  31. package/src/clients/axios.ts +41 -7
  32. package/src/clients/fetch.ts +106 -6
  33. package/src/components/ClassClient.tsx +47 -24
  34. package/src/components/Client.tsx +100 -71
  35. package/src/components/StaticClassClient.tsx +47 -24
  36. package/src/components/Url.tsx +10 -12
  37. package/src/components/WrapperClient.tsx +9 -5
  38. package/src/functionParams.ts +8 -8
  39. package/src/generators/classClientGenerator.tsx +63 -51
  40. package/src/generators/clientGenerator.tsx +45 -48
  41. package/src/generators/groupedClientGenerator.tsx +12 -6
  42. package/src/generators/operationsGenerator.ts +47 -0
  43. package/src/generators/staticClassClientGenerator.tsx +57 -45
  44. package/src/index.ts +2 -1
  45. package/src/plugin.ts +30 -28
  46. package/src/resolvers/resolverClient.ts +32 -8
  47. package/src/types.ts +111 -65
  48. package/src/utils.ts +142 -63
  49. package/extension.yaml +0 -776
  50. package/src/components/Operations.tsx +0 -28
  51. package/src/generators/operationsGenerator.tsx +0 -28
  52. package/templates/clients/axios.ts +0 -73
  53. package/templates/clients/fetch.ts +0 -96
  54. package/templates/config.ts +0 -43
  55. /package/dist/{chunk-ByKO4r7w.cjs → chunk-Bx3C2hgW.cjs} +0 -0
  56. /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_chunk = require("./chunk-ByKO4r7w.cjs");
5
+ const require_chunk = require("./chunk-Bx3C2hgW.cjs");
6
6
  const require_templates_clients_axios_source = require("./templates/clients/axios.source.cjs");
7
7
  const require_templates_clients_fetch_source = require("./templates/clients/fetch.source.cjs");
8
8
  const require_templates_config_source = require("./templates/config.source.cjs");
@@ -10,6 +10,7 @@ let node_path = require("node:path");
10
10
  let node_path$1 = require_chunk.__toESM(node_path, 1);
11
11
  node_path = require_chunk.__toESM(node_path);
12
12
  let _kubb_core = require("@kubb/core");
13
+ let _kubb_ast_utils = require("@kubb/ast/utils");
13
14
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
14
15
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
15
16
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
@@ -25,68 +26,57 @@ let _kubb_plugin_zod = require("@kubb/plugin-zod");
25
26
  function toCamelOrPascal(text, pascal) {
26
27
  return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
27
28
  if (word.length > 1 && word === word.toUpperCase()) return word;
28
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
29
- return word.charAt(0).toUpperCase() + word.slice(1);
29
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
30
30
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
31
31
  }
32
32
  /**
33
- * Splits `text` on `.` and applies `transformPart` to each segment.
34
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
35
- * Segments are joined with `/` to form a file path.
36
- *
37
- * Only splits on dots followed by a letter so that version numbers
38
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
39
- */
40
- function applyToFileParts(text, transformPart) {
41
- const parts = text.split(/\.(?=[a-zA-Z])/);
42
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
43
- }
44
- /**
45
33
  * Converts `text` to camelCase.
46
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
47
34
  *
48
- * @example
49
- * camelCase('hello-world') // 'helloWorld'
50
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
35
+ * @example Word boundaries
36
+ * `camelCase('hello-world') // 'helloWorld'`
37
+ *
38
+ * @example With a prefix
39
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
51
40
  */
52
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
53
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
54
- prefix,
55
- suffix
56
- } : {}));
41
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
57
42
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
58
43
  }
59
44
  /**
60
45
  * Converts `text` to PascalCase.
61
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
62
46
  *
63
- * @example
64
- * pascalCase('hello-world') // 'HelloWorld'
65
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
47
+ * @example Word boundaries
48
+ * `pascalCase('hello-world') // 'HelloWorld'`
49
+ *
50
+ * @example With a suffix
51
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
66
52
  */
67
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
68
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
69
- prefix,
70
- suffix
71
- }) : camelCase(part));
53
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
72
54
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
73
55
  }
74
56
  //#endregion
75
- //#region ../../internals/utils/src/jsdoc.ts
57
+ //#region ../../internals/utils/src/fs.ts
76
58
  /**
77
- * Builds a JSDoc comment block from an array of lines.
78
- * Returns `fallback` when `comments` is empty so callers always get a usable string.
59
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
60
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
61
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
79
62
  *
80
- * @example
81
- * ```ts
82
- * buildJSDoc(['@type string', '@example hello'])
83
- * // '/**\n * @type string\n * @example hello\n *\/\n '
84
- * ```
63
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
64
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
65
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
66
+ * absolute path, letting generated files escape the configured output directory.
67
+ *
68
+ * @example Nested path from a dotted name
69
+ * `toFilePath('pet.petId') // 'pet/petId'`
70
+ *
71
+ * @example PascalCase the final segment
72
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
73
+ *
74
+ * @example Suffix applied to the final segment only
75
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
85
76
  */
86
- function buildJSDoc(comments, options = {}) {
87
- const { indent = " * ", suffix = "\n ", fallback = " " } = options;
88
- if (comments.length === 0) return fallback;
89
- return `/**\n${comments.map((c) => `${indent}${c}`).join("\n")}\n */${suffix}`;
77
+ function toFilePath(name, caseLast = camelCase) {
78
+ const parts = name.split(/\.(?=[a-zA-Z])/);
79
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
90
80
  }
91
81
  //#endregion
92
82
  //#region ../../internals/utils/src/reserved.ts
@@ -191,100 +181,104 @@ function isValidVarName(name) {
191
181
  if (!name || reservedWords.has(name)) return false;
192
182
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
193
183
  }
194
- //#endregion
195
- //#region ../../internals/utils/src/urlPath.ts
196
184
  /**
197
- * Parses and transforms an OpenAPI/Swagger path string into various URL formats.
185
+ * Returns `name` when it's a syntactically valid JavaScript variable name,
186
+ * otherwise prefixes it with `_` so the result is a valid identifier.
187
+ *
188
+ * Useful for sanitizing OpenAPI schema names or operation IDs that start with
189
+ * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
190
+ * variable, type, or function names.
198
191
  *
199
192
  * @example
200
- * const p = new URLPath('/pet/{petId}')
201
- * p.URL // '/pet/:petId'
202
- * p.template // '`/pet/${petId}`'
193
+ * ```ts
194
+ * ensureValidVarName('409') // '_409'
195
+ * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
196
+ * ensureValidVarName('Pet') // 'Pet'
197
+ * ensureValidVarName('class') // '_class'
198
+ * ```
203
199
  */
204
- var URLPath = class {
205
- /**
206
- * The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
207
- */
208
- path;
209
- #options;
210
- constructor(path, options = {}) {
211
- this.path = path;
212
- this.#options = options;
200
+ function ensureValidVarName(name) {
201
+ if (!name || isValidVarName(name)) return name;
202
+ return `_${name}`;
203
+ }
204
+ //#endregion
205
+ //#region ../../internals/utils/src/url.ts
206
+ function transformParam(raw, casing) {
207
+ const param = isValidVarName(raw) ? raw : camelCase(raw);
208
+ return casing === "camelcase" ? camelCase(param) : param;
209
+ }
210
+ function toParamsObject(path, { replacer, casing } = {}) {
211
+ const params = {};
212
+ for (const match of path.matchAll(/\{([^}]+)\}/g)) {
213
+ const param = transformParam(match[1], casing);
214
+ const key = replacer ? replacer(param) : param;
215
+ params[key] = key;
213
216
  }
214
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
215
- *
216
- * @example
217
- * ```ts
218
- * new URLPath('/pet/{petId}').URL // '/pet/:petId'
219
- * ```
220
- */
221
- get URL() {
222
- return this.toURLPath();
217
+ return Object.keys(params).length > 0 ? params : null;
218
+ }
219
+ /**
220
+ * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
221
+ */
222
+ var Url$1 = class Url$1 {
223
+ static {
224
+ require_chunk.__name(this, "Url");
223
225
  }
224
- /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
226
+ /**
227
+ * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
225
228
  *
226
229
  * @example
227
- * ```ts
228
- * new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
229
- * new URLPath('/pet/{petId}').isURL // false
230
- * ```
230
+ * Url.canParse('https://petstore.swagger.io/v2') // true
231
+ * Url.canParse('/pet/{petId}') // false
231
232
  */
232
- get isURL() {
233
- try {
234
- return !!new URL(this.path).href;
235
- } catch {
236
- return false;
237
- }
233
+ static canParse(url, base) {
234
+ return URL.canParse(url, base);
238
235
  }
239
236
  /**
240
- * Converts the OpenAPI path to a TypeScript template literal string.
237
+ * Converts an OpenAPI/Swagger path to Express-style colon syntax.
241
238
  *
242
239
  * @example
243
- * new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
244
- * new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
240
+ * Url.toPath('/pet/{petId}') // '/pet/:petId'
245
241
  */
246
- get template() {
247
- return this.toTemplateString();
242
+ static toPath(path) {
243
+ return path.replace(/\{([^}]+)\}/g, ":$1");
248
244
  }
249
- /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
245
+ /**
246
+ * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
247
+ * `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
248
+ * and `casing` controls parameter identifier casing.
250
249
  *
251
250
  * @example
252
- * ```ts
253
- * new URLPath('/pet/{petId}').object
254
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
255
- * ```
256
- */
257
- get object() {
258
- return this.toObject();
259
- }
260
- /** Returns a map of path parameter names, or `undefined` when the path has no parameters.
251
+ * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
261
252
  *
262
253
  * @example
263
- * ```ts
264
- * new URLPath('/pet/{petId}').params // { petId: 'petId' }
265
- * new URLPath('/pet').params // undefined
266
- * ```
254
+ * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
267
255
  */
268
- get params() {
269
- return this.getParams();
270
- }
271
- #transformParam(raw) {
272
- const param = isValidVarName(raw) ? raw : camelCase(raw);
273
- return this.#options.casing === "camelcase" ? camelCase(param) : param;
256
+ static toTemplateString(path, { prefix, replacer, casing } = {}) {
257
+ const result = path.split(/\{([^}]+)\}/).map((part, i) => {
258
+ if (i % 2 === 0) return part;
259
+ const param = transformParam(part, casing);
260
+ return `\${${replacer ? replacer(param) : param}}`;
261
+ }).join("");
262
+ return `\`${prefix ?? ""}${result}\``;
274
263
  }
275
264
  /**
276
- * Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
265
+ * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
266
+ * expression when `stringify` is set.
267
+ *
268
+ * @example
269
+ * Url.toObject('/pet/{petId}')
270
+ * // { url: '/pet/:petId', params: { petId: 'petId' } }
277
271
  */
278
- #eachParam(fn) {
279
- for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
280
- const raw = match[1];
281
- fn(raw, this.#transformParam(raw));
282
- }
283
- }
284
- toObject({ type = "path", replacer, stringify } = {}) {
272
+ static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
285
273
  const object = {
286
- url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
287
- params: this.getParams()
274
+ url: type === "path" ? Url$1.toPath(path) : Url$1.toTemplateString(path, {
275
+ replacer,
276
+ casing
277
+ }),
278
+ params: toParamsObject(path, {
279
+ replacer,
280
+ casing
281
+ })
288
282
  };
289
283
  if (stringify) {
290
284
  if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
@@ -293,51 +287,211 @@ var URLPath = class {
293
287
  }
294
288
  return object;
295
289
  }
296
- /**
297
- * Converts the OpenAPI path to a TypeScript template literal string.
298
- * An optional `replacer` can transform each extracted parameter name before interpolation.
299
- *
300
- * @example
301
- * new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
302
- */
303
- toTemplateString({ prefix = "", replacer } = {}) {
304
- return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
305
- if (i % 2 === 0) return part;
306
- const param = this.#transformParam(part);
307
- return `\${${replacer ? replacer(param) : param}}`;
308
- }).join("")}\``;
309
- }
310
- /**
311
- * Extracts all `{param}` segments from the path and returns them as a key-value map.
312
- * An optional `replacer` transforms each parameter name in both key and value positions.
313
- * Returns `undefined` when no path parameters are found.
314
- *
315
- * @example
316
- * ```ts
317
- * new URLPath('/pet/{petId}/tag/{tagId}').getParams()
318
- * // { petId: 'petId', tagId: 'tagId' }
319
- * ```
320
- */
321
- getParams(replacer) {
322
- const params = {};
323
- this.#eachParam((_raw, param) => {
324
- const key = replacer ? replacer(param) : param;
325
- params[key] = key;
326
- });
327
- return Object.keys(params).length > 0 ? params : void 0;
328
- }
329
- /** Converts the OpenAPI path to Express-style colon syntax.
330
- *
331
- * @example
332
- * ```ts
333
- * new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
334
- * ```
335
- */
336
- toURLPath() {
337
- return this.path.replace(/\{([^}]+)\}/g, ":$1");
338
- }
339
290
  };
340
291
  //#endregion
292
+ //#region ../../internals/shared/src/operation.ts
293
+ /**
294
+ * Builds the `ResolverFileParams` every operation generator passes to
295
+ * `resolver.resolveFile`: a file named `name`, tagged by the operation's first
296
+ * tag (or `'default'`), at the operation's path. Centralizes the entry object
297
+ * that was repeated at dozens of call sites across the client and query plugins.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * resolver.resolveFile(operationFileEntry(node, node.operationId), { root, output, group })
302
+ * ```
303
+ */
304
+ function operationFileEntry(node, name, extname = ".ts") {
305
+ return {
306
+ name,
307
+ extname,
308
+ tag: node.tags[0] ?? "default",
309
+ path: node.path
310
+ };
311
+ }
312
+ function getOperationLink(node, link) {
313
+ if (!link) return null;
314
+ if (typeof link === "function") return link(node) ?? null;
315
+ if (link === "urlPath") return node.path ? `{@link ${Url$1.toPath(node.path)}}` : null;
316
+ return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
317
+ }
318
+ function getContentTypeInfo(node) {
319
+ const contentTypes = node.requestBody?.content?.map((e) => e.contentType) ?? [];
320
+ const isMultipleContentTypes = contentTypes.length > 1;
321
+ return {
322
+ contentTypes,
323
+ isMultipleContentTypes,
324
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
325
+ defaultContentType: contentTypes[0] ?? "application/json",
326
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
327
+ };
328
+ }
329
+ /**
330
+ * Derives the default `responseType` for an operation from its primary success response.
331
+ *
332
+ * Returns a value only when that response declares a single non-JSON content type — a binary type
333
+ * (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`, `video/*`) maps to `'blob'`
334
+ * and other `text/*` maps to `'text'`. Otherwise `undefined`, leaving the runtime client's
335
+ * `Content-Type` auto-detection in charge.
336
+ */
337
+ function getResponseType(node) {
338
+ const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? [];
339
+ if (contentTypes.length !== 1) return void 0;
340
+ const baseType = contentTypes[0].split(";")[0].trim().toLowerCase();
341
+ if (baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json") return void 0;
342
+ if (baseType.startsWith("text/")) return "text";
343
+ if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
344
+ }
345
+ function buildRequestConfigType(node, resolver) {
346
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null;
347
+ const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
348
+ return `${requestName ? `Partial<RequestConfig<${requestName}>>` : "Partial<RequestConfig>"} & { ${["client?: Client", isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null].filter(Boolean).join("; ")} }`;
349
+ }
350
+ function buildOperationComments(node, options = {}) {
351
+ const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
352
+ const linkComment = getOperationLink(node, link);
353
+ const filteredComments = (linkPosition === "beforeDeprecated" ? [
354
+ node.description && `@description ${node.description}`,
355
+ node.summary && `@summary ${node.summary}`,
356
+ linkComment,
357
+ node.deprecated && "@deprecated"
358
+ ] : [
359
+ node.description && `@description ${node.description}`,
360
+ node.summary && `@summary ${node.summary}`,
361
+ node.deprecated && "@deprecated",
362
+ linkComment
363
+ ]).filter((comment) => Boolean(comment));
364
+ if (!splitLines) return filteredComments;
365
+ return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
366
+ }
367
+ function getOperationParameters(node, options = {}) {
368
+ const params = _kubb_core.ast.caseParams(node.parameters, options.paramsCasing);
369
+ return {
370
+ path: params.filter((param) => param.in === "path"),
371
+ query: params.filter((param) => param.in === "query"),
372
+ header: params.filter((param) => param.in === "header"),
373
+ cookie: params.filter((param) => param.in === "cookie")
374
+ };
375
+ }
376
+ function getStatusCodeNumber(statusCode) {
377
+ const code = Number(statusCode);
378
+ return Number.isNaN(code) ? null : code;
379
+ }
380
+ function isSuccessStatusCode(statusCode) {
381
+ const code = getStatusCodeNumber(statusCode);
382
+ return code !== null && code >= 200 && code < 300;
383
+ }
384
+ function isErrorStatusCode(statusCode) {
385
+ const code = getStatusCodeNumber(statusCode);
386
+ return code !== null && code >= 400;
387
+ }
388
+ function getSuccessResponses(responses) {
389
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
390
+ }
391
+ function getOperationSuccessResponses(node) {
392
+ return getSuccessResponses(node.responses);
393
+ }
394
+ function getPrimarySuccessResponse(node) {
395
+ return getOperationSuccessResponses(node)[0] ?? null;
396
+ }
397
+ function resolveErrorNames(node, resolver) {
398
+ return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
399
+ }
400
+ function resolveSuccessNames(node, resolver) {
401
+ return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
402
+ }
403
+ function resolveStatusCodeNames(node, resolver) {
404
+ return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
405
+ }
406
+ /**
407
+ * Builds the discriminated union type string for `dataReturnType: 'full'` return shapes.
408
+ * Each member is `{ status: N; data: StatusNType; statusText: string }`.
409
+ */
410
+ function buildStatusUnionType(node, resolver) {
411
+ const members = node.responses.map((r) => {
412
+ const typeName = resolver.resolveResponseStatusName(node, r.statusCode);
413
+ const statusCode = Number.parseInt(r.statusCode, 10);
414
+ return `{ status: ${Number.isNaN(statusCode) ? "number" : String(statusCode)}; data: ${typeName}; statusText: string }`;
415
+ });
416
+ if (members.length === 1) return members[0];
417
+ return `(${members.join(" | ")})`;
418
+ }
419
+ const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
420
+ function resolveOperationTypeNames(node, resolver, options = {}) {
421
+ const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
422
+ let byResolver = typeNamesByResolver.get(resolver);
423
+ if (byResolver) {
424
+ const cached = byResolver.get(cacheKey);
425
+ if (cached) return cached;
426
+ } else {
427
+ byResolver = /* @__PURE__ */ new Map();
428
+ typeNamesByResolver.set(resolver, byResolver);
429
+ }
430
+ const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
431
+ const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
432
+ const exclude = new Set(options.exclude ?? []);
433
+ const paramNames = [
434
+ ...path.map((param) => resolver.resolvePathParamsName(node, param)),
435
+ ...query.map((param) => resolver.resolveQueryParamsName(node, param)),
436
+ ...header.map((param) => resolver.resolveHeaderParamsName(node, param))
437
+ ];
438
+ const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
439
+ const result = (options.order === "body-response-first" ? [
440
+ ...bodyAndResponseNames,
441
+ ...paramNames,
442
+ ...responseStatusNames
443
+ ] : [
444
+ ...paramNames,
445
+ ...bodyAndResponseNames,
446
+ ...responseStatusNames
447
+ ]).filter((name) => Boolean(name) && !exclude.has(name));
448
+ byResolver.set(cacheKey, result);
449
+ return result;
450
+ }
451
+ //#endregion
452
+ //#region ../../internals/shared/src/group.ts
453
+ /**
454
+ * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
455
+ * shared default naming so every plugin groups output consistently:
456
+ *
457
+ * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
458
+ * - other groups use the camelCased group (`pet store` → `petStore`).
459
+ *
460
+ * A user-provided `group.name` always wins over the default namer, so callers stay in
461
+ * control of their output folders. Returns `null` when grouping is disabled, matching the
462
+ * per-plugin convention.
463
+ *
464
+ * @param group - The user-supplied group option, or `undefined` to disable grouping.
465
+ *
466
+ * @example
467
+ * ```ts
468
+ * createGroupConfig(group) // shared across every plugin
469
+ * ```
470
+ */
471
+ function createGroupConfig(group) {
472
+ if (!group) return null;
473
+ const defaultName = (ctx) => {
474
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
475
+ return camelCase(ctx.group);
476
+ };
477
+ return {
478
+ ...group,
479
+ name: group.name ? group.name : defaultName
480
+ };
481
+ }
482
+ //#endregion
483
+ //#region ../../internals/shared/src/params.ts
484
+ function buildParamsMapping(originalParams, mappedParams) {
485
+ const mapping = {};
486
+ let hasChanged = false;
487
+ originalParams.forEach((param, i) => {
488
+ const mappedName = mappedParams[i]?.name ?? param.name;
489
+ mapping[param.name] = mappedName;
490
+ if (param.name !== mappedName) hasChanged = true;
491
+ });
492
+ return hasChanged ? mapping : null;
493
+ }
494
+ //#endregion
341
495
  //#region src/functionParams.ts
342
496
  const declarationPrinter$4 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
343
497
  const callPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "call" });
@@ -348,18 +502,18 @@ function createType(type) {
348
502
  return type ? _kubb_core.ast.createParamsType({
349
503
  variant: "reference",
350
504
  name: type
351
- }) : void 0;
505
+ }) : null;
352
506
  }
353
507
  function createDeclarationLeaf(name, spec) {
354
508
  if (spec.default !== void 0) return _kubb_core.ast.createFunctionParameter({
355
509
  name,
356
- type: createType(spec.type),
510
+ type: createType(spec.type) ?? void 0,
357
511
  default: spec.default,
358
512
  rest: spec.mode === "inlineSpread"
359
513
  });
360
514
  return _kubb_core.ast.createFunctionParameter({
361
515
  name,
362
- type: createType(spec.type),
516
+ type: createType(spec.type) ?? void 0,
363
517
  optional: !!spec.optional,
364
518
  rest: spec.mode === "inlineSpread"
365
519
  });
@@ -368,14 +522,14 @@ function createDeclarationParam(name, spec) {
368
522
  if (isGroup(spec)) return _kubb_core.ast.createParameterGroup({
369
523
  inline: spec.mode === "inlineSpread",
370
524
  default: spec.default,
371
- properties: Object.entries(spec.children).filter(([, child]) => child !== void 0).map(([childName, child]) => createDeclarationLeaf(childName, child))
525
+ properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => createDeclarationLeaf(childName, child))
372
526
  });
373
527
  return createDeclarationLeaf(name, spec);
374
528
  }
375
529
  function createCallParam(name, spec) {
376
530
  if (isGroup(spec)) return _kubb_core.ast.createParameterGroup({
377
531
  inline: spec.mode === "inlineSpread",
378
- properties: Object.entries(spec.children).filter(([, child]) => child !== void 0).map(([childName, child]) => _kubb_core.ast.createFunctionParameter({
532
+ properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => _kubb_core.ast.createFunctionParameter({
379
533
  name: child?.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child?.value ? `${childName}: ${child.value}` : childName,
380
534
  rest: spec.mode === "inlineSpread" && child?.mode === "inlineSpread"
381
535
  }))
@@ -390,7 +544,7 @@ function createCallParam(name, spec) {
390
544
  * Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
391
545
  */
392
546
  function createFunctionParams(params) {
393
- const entries = Object.entries(params).filter(([, spec]) => spec !== void 0);
547
+ const entries = Object.entries(params).filter(([, spec]) => spec != null);
394
548
  return {
395
549
  toConstructor() {
396
550
  return declarationPrinter$4.print(_kubb_core.ast.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
@@ -403,83 +557,118 @@ function createFunctionParams(params) {
403
557
  //#endregion
404
558
  //#region src/utils.ts
405
559
  /**
406
- * Extracts documentation comments from an operation node.
407
- * Includes description, summary, link, and deprecation information.
560
+ * Returns `true` when any direction of the parser uses Zod (used for dependency checks).
408
561
  */
409
- function getComments(node) {
410
- return [
411
- node.description && `@description ${node.description}`,
412
- node.summary && `@summary ${node.summary}`,
413
- node.path && `{@link ${new URLPath(node.path).URL}}`,
414
- node.deprecated && "@deprecated"
415
- ].filter((x) => Boolean(x)).flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((x) => Boolean(x));
562
+ function isParserEnabled(parser) {
563
+ if (!parser) return false;
564
+ if (parser === "zod") return true;
565
+ return !!(parser.request || parser.response);
416
566
  }
417
567
  /**
418
- * Builds a mapping of original parameter names to their transformed (cased) names.
419
- * Returns undefined if no names have changed.
568
+ * Returns `'zod'` when request body parsing is enabled, `null` otherwise.
569
+ * The string shorthand `'zod'` also enables request body parsing (existing behavior).
420
570
  */
421
- function buildParamsMapping(originalParams, casedParams) {
422
- const mapping = {};
423
- let hasChanged = false;
424
- originalParams.forEach((param, i) => {
425
- const casedName = casedParams[i]?.name ?? param.name;
426
- mapping[param.name] = casedName;
427
- if (param.name !== casedName) hasChanged = true;
428
- });
429
- return hasChanged ? mapping : void 0;
571
+ function resolveRequestParser(parser) {
572
+ if (!parser) return null;
573
+ if (parser === "zod") return "zod";
574
+ return parser.request ?? null;
575
+ }
576
+ /**
577
+ * Returns `'zod'` when query-parameters parsing is enabled, `null` otherwise.
578
+ * Only the object form `{ request: 'zod' }` enables query-params parsing.
579
+ * The string shorthand `'zod'` does not, preserving its existing behavior.
580
+ */
581
+ function resolveQueryParamsParser(parser) {
582
+ if (!parser || parser === "zod") return null;
583
+ return parser.request ?? null;
584
+ }
585
+ /**
586
+ * Returns `'zod'` when response-direction parsing is enabled, `null` otherwise.
587
+ * `parser: 'zod'` (string shorthand) maps to response parsing and returns `'zod'`.
588
+ */
589
+ function resolveResponseParser(parser) {
590
+ if (!parser) return null;
591
+ if (parser === "zod") return "zod";
592
+ return parser.response ?? null;
430
593
  }
431
594
  /**
432
595
  * Builds HTTP headers array for a client request.
433
596
  * Includes Content-Type (if not default) and spreads header parameters if present.
434
597
  */
435
598
  function buildHeaders(contentType, hasHeaderParams) {
436
- return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0, hasHeaderParams ? "...headers" : void 0].filter(Boolean);
599
+ return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
437
600
  }
438
601
  /**
439
- * Builds TypeScript generic parameters for a client method.
440
- * Includes response type, error type, and optional request type.
602
+ * Returns the generic type arguments — response, error, and request body — for a generated
603
+ * client call.
604
+ *
605
+ * When `dataReturnType` is `'full'`, the response generic widens to a union of all documented
606
+ * status types. When `parser` is `'zod'` and a request body schema exists, the request type
607
+ * uses `z.output<typeof schema>` to reflect post-transform types (e.g. date coercion).
441
608
  */
442
- function buildGenerics(node, tsResolver) {
443
- const responseName = tsResolver.resolveResponseName(node);
444
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
609
+ function buildGenerics(node, tsResolver, options = {}) {
610
+ const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
611
+ const successNames = resolveSuccessNames(node, tsResolver);
612
+ const responseName = options.dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : tsResolver.resolveResponseName(node) : successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
613
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
445
614
  const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
615
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
616
+ const zodRequestName = options.parser === "zod" && options.zodResolver && node.requestBody?.content?.[0]?.schema ? options.zodResolver.resolveDataName?.(node) ?? null : null;
446
617
  return [
447
618
  responseName,
448
- `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
449
- requestName || "unknown"
619
+ TError,
620
+ zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
450
621
  ].filter(Boolean);
451
622
  }
452
623
  /**
453
624
  * Builds the parameters object for a class-based client method.
454
625
  * Includes URL, method, base URL, headers, and request/response data.
455
626
  */
456
- function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, headers }) {
457
- const queryParamsName = node.parameters.filter((p) => p.in === "query").length > 0 ? tsResolver.resolveQueryParamsName(node, node.parameters.filter((p) => p.in === "query")[0]) : void 0;
458
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
627
+ function buildClassClientParams({ node, url, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers, zodQueryParamsName }) {
628
+ const { query: queryParams } = getOperationParameters(node);
629
+ const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
630
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
631
+ const responseType = getResponseType(node);
459
632
  return createFunctionParams({ config: {
460
633
  mode: "object",
461
634
  children: {
462
635
  requestConfig: { mode: "inlineSpread" },
463
- method: { value: JSON.stringify(node.method.toUpperCase()) },
464
- url: { value: path.template },
465
- baseURL: baseURL ? { value: JSON.stringify(baseURL) } : void 0,
466
- params: queryParamsName ? {} : void 0,
467
- data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } : void 0,
468
- headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : void 0
636
+ method: { value: JSON.stringify(_kubb_core.ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
637
+ url: { value: url },
638
+ baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
639
+ params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : {} : null,
640
+ data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
641
+ contentType: isMultipleContentTypes ? {} : null,
642
+ responseType: responseType ? { value: JSON.stringify(responseType) } : null,
643
+ headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
469
644
  }
470
645
  } });
471
646
  }
472
647
  /**
473
648
  * Builds the request data parsing line for client methods.
474
- * Applies Zod validation if configured, otherwise uses data directly.
649
+ * Applies Zod validation when `parser.request === 'zod'`, otherwise assigns data directly.
475
650
  */
476
651
  function buildRequestDataLine({ parser, node, zodResolver }) {
477
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
478
- if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
652
+ const requestParser = resolveRequestParser(parser);
653
+ const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
654
+ if (requestParser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
479
655
  if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
480
656
  return "";
481
657
  }
482
658
  /**
659
+ * Builds the query parameters parsing line for client methods.
660
+ * Returns an empty string when no query params exist or query-params parsing is not enabled.
661
+ * Only the object form `parser: { request: 'zod' }` triggers this. `parser: 'zod'` does not.
662
+ */
663
+ function buildQueryParamsLine({ parser, node, zodResolver }) {
664
+ if (resolveQueryParamsParser(parser) !== "zod" || !zodResolver) return "";
665
+ const { query: queryParams } = getOperationParameters(node);
666
+ if (queryParams.length === 0) return "";
667
+ const zodQueryParamsName = zodResolver.resolveQueryParamsName?.(node, queryParams[0]);
668
+ if (!zodQueryParamsName) return "";
669
+ return `const requestParams = ${zodQueryParamsName}.parse(params)`;
670
+ }
671
+ /**
483
672
  * Builds the form data conversion line for file upload requests.
484
673
  * Returns empty string if not applicable.
485
674
  */
@@ -488,19 +677,24 @@ function buildFormDataLine(isFormData, hasRequest) {
488
677
  }
489
678
  /**
490
679
  * Builds the return statement for a client method.
491
- * Applies Zod validation to response data if configured, otherwise returns raw response.
680
+ * When `dataReturnType` is `'full'`, casts the response to the status-discriminated union type.
681
+ * When `parser.response` is `'zod'`, pipes the response body through the Zod schema before returning.
492
682
  */
493
- function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
494
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
495
- if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
496
- if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
497
- if (dataReturnType === "full" && parser === "client") return "return res";
683
+ function buildReturnStatement({ dataReturnType, parser, node, zodResolver, tsResolver }) {
684
+ const responseParser = resolveResponseParser(parser);
685
+ const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
686
+ if (dataReturnType === "full" && tsResolver) {
687
+ const unionType = buildStatusUnionType(node, tsResolver);
688
+ if (responseParser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)} as ${unionType}`;
689
+ return `return res as ${unionType}`;
690
+ }
691
+ if (dataReturnType === "data" && responseParser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
498
692
  return "return res.data";
499
693
  }
500
694
  //#endregion
501
695
  //#region src/components/Url.tsx
502
696
  const declarationPrinter$3 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
503
- function getParams$1({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
697
+ function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
504
698
  const urlNode = {
505
699
  ...node,
506
700
  parameters: node.parameters.filter((p) => p.in === "path"),
@@ -513,10 +707,9 @@ function getParams$1({ paramsType, paramsCasing, pathParamsType, node, tsResolve
513
707
  resolver: tsResolver
514
708
  });
515
709
  }
516
- require_chunk.__name(getParams$1, "getParams");
517
710
  function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
518
- const path = new URLPath(node.path);
519
- const paramsNode = getParams$1({
711
+ if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
712
+ const paramsNode = buildUrlParamsNode({
520
713
  paramsType,
521
714
  paramsCasing,
522
715
  pathParamsType,
@@ -524,9 +717,9 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
524
717
  tsResolver
525
718
  });
526
719
  const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
527
- const originalPathParams = node.parameters.filter((p) => p.in === "path");
528
- const casedPathParams = _kubb_core.ast.caseParams(originalPathParams, paramsCasing);
529
- const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : void 0;
720
+ const { path: originalPathParams } = getOperationParameters(node);
721
+ const { path: casedPathParams } = getOperationParameters(node, { paramsCasing });
722
+ const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : null;
530
723
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
531
724
  name,
532
725
  isExportable,
@@ -540,7 +733,7 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
540
733
  pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
541
734
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
542
735
  name: "res",
543
- children: `{ method: '${node.method.toUpperCase()}', url: ${path.toTemplateString({ prefix: baseURL })} as const }`
736
+ children: `{ method: '${node.method.toUpperCase()}', url: ${Url$1.toTemplateString(node.path, { prefix: baseURL })} as const }`
544
737
  }),
545
738
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
546
739
  "return res"
@@ -548,56 +741,58 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
548
741
  })
549
742
  });
550
743
  }
551
- Url.getParams = getParams$1;
552
744
  //#endregion
553
745
  //#region src/components/Client.tsx
554
746
  const declarationPrinter$2 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
555
- function getParams({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable }) {
556
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
747
+ function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable }) {
557
748
  return _kubb_core.ast.createOperationParams(node, {
558
749
  paramsType,
559
750
  pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
560
751
  paramsCasing,
561
752
  resolver: tsResolver,
562
- extraParams: isConfigurable ? [_kubb_core.ast.createFunctionParameter({
753
+ extraParams: [...isConfigurable ? [_kubb_core.ast.createFunctionParameter({
563
754
  name: "config",
564
755
  type: _kubb_core.ast.createParamsType({
565
756
  variant: "reference",
566
- name: requestName ? `Partial<RequestConfig<${requestName}>> & { client?: Client }` : "Partial<RequestConfig> & { client?: Client }"
757
+ name: buildRequestConfigType(node, tsResolver)
567
758
  }),
568
759
  default: "{}"
569
- })] : []
760
+ })] : []]
570
761
  });
571
762
  }
572
763
  function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
573
- const path = new URLPath(node.path);
574
- const contentType = node.requestBody?.content?.[0]?.contentType ?? "application/json";
575
- const isFormData = contentType === "multipart/form-data";
576
- const originalPathParams = node.parameters.filter((p) => p.in === "path");
577
- const casedPathParams = _kubb_core.ast.caseParams(originalPathParams, paramsCasing);
578
- const originalQueryParams = node.parameters.filter((p) => p.in === "query");
579
- const casedQueryParams = _kubb_core.ast.caseParams(originalQueryParams, paramsCasing);
580
- const originalHeaderParams = node.parameters.filter((p) => p.in === "header");
581
- const casedHeaderParams = _kubb_core.ast.caseParams(originalHeaderParams, paramsCasing);
582
- const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : void 0;
583
- const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : void 0;
584
- const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : void 0;
585
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
586
- const responseName = tsResolver.resolveResponseName(node);
587
- const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : void 0;
588
- const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : void 0;
589
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
590
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
764
+ if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
765
+ const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
766
+ const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
767
+ const responseType = getResponseType(node);
768
+ const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node);
769
+ const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing });
770
+ const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : null;
771
+ const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : null;
772
+ const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : null;
773
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
774
+ const successNames = resolveSuccessNames(node, tsResolver);
775
+ const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
776
+ const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
777
+ const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
778
+ const requestParser = resolveRequestParser(parser);
779
+ const responseParser = resolveResponseParser(parser);
780
+ const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
781
+ const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
782
+ const queryParamsParser = resolveQueryParamsParser(parser);
783
+ const zodQueryParamsName = zodResolver && queryParamsParser === "zod" && originalQueryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, originalQueryParams[0]) : null;
591
784
  const errorNames = node.responses.filter((r) => {
592
785
  return Number.parseInt(r.statusCode, 10) >= 400;
593
786
  }).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
594
- const headers = [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : void 0].filter(Boolean);
787
+ const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null].filter(Boolean);
788
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
789
+ const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
595
790
  const generics = [
596
- responseName,
597
- `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
598
- requestName || "unknown"
791
+ dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : responseName : responseName,
792
+ TError,
793
+ parser === "zod" && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
599
794
  ].filter(Boolean);
600
- const paramsNode = getParams({
795
+ const paramsNode = buildClientParamsNode({
601
796
  paramsType,
602
797
  paramsCasing,
603
798
  pathParamsType,
@@ -606,7 +801,7 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
606
801
  isConfigurable
607
802
  });
608
803
  const paramsSignature = declarationPrinter$2.print(paramsNode) ?? "";
609
- const urlParamsNode = Url.getParams({
804
+ const urlParamsNode = buildUrlParamsNode({
610
805
  paramsType,
611
806
  paramsCasing,
612
807
  pathParamsType,
@@ -617,20 +812,23 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
617
812
  const clientParams = createFunctionParams({ config: {
618
813
  mode: "object",
619
814
  children: {
620
- method: { value: JSON.stringify(node.method.toUpperCase()) },
621
- url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path.template },
622
- baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : void 0,
623
- params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : void 0,
624
- data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } : void 0,
625
- requestConfig: isConfigurable ? { mode: "inlineSpread" } : void 0,
626
- headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : void 0
815
+ method: { value: (0, _kubb_ast_utils.stringify)(node.method.toUpperCase()) },
816
+ url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url$1.toTemplateString(node.path) },
817
+ baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
818
+ params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : queryParamsMapping ? { value: "mappedParams" } : {} : null,
819
+ data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
820
+ contentType: isConfigurable && isMultipleContentTypes ? {} : null,
821
+ responseType: responseType ? { value: (0, _kubb_ast_utils.stringify)(responseType) } : null,
822
+ requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
823
+ headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
627
824
  }
628
825
  } });
826
+ const statusUnionType = dataReturnType === "full" ? buildStatusUnionType(node, tsResolver) : null;
629
827
  const childrenElement = children ? children : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
630
- dataReturnType === "full" && parser === "zod" && zodResponseName && `return {...res, data: ${zodResponseName}.parse(res.data)}`,
631
- dataReturnType === "data" && parser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
632
- dataReturnType === "full" && parser === "client" && "return res",
633
- dataReturnType === "data" && parser === "client" && "return res.data"
828
+ dataReturnType === "full" && responseParser === "zod" && zodResponseName && statusUnionType && `return {...res, data: ${zodResponseName}.parse(res.data)} as ${statusUnionType}`,
829
+ dataReturnType === "full" && responseParser !== "zod" && statusUnionType && `return res as ${statusUnionType}`,
830
+ dataReturnType === "data" && responseParser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
831
+ dataReturnType === "data" && responseParser !== "zod" && "return res.data"
634
832
  ] });
635
833
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
636
834
  name,
@@ -641,46 +839,46 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
641
839
  async: true,
642
840
  export: isExportable,
643
841
  params: paramsSignature,
644
- JSDoc: { comments: getComments(node) },
842
+ JSDoc: { comments: buildOperationComments(node, {
843
+ link: "urlPath",
844
+ linkPosition: "beforeDeprecated",
845
+ splitLines: true
846
+ }) },
645
847
  returnType,
646
848
  children: [
647
- isConfigurable ? "const { client: request = fetch, ...requestConfig } = config" : "",
648
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
849
+ isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = config` : "",
649
850
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
650
851
  pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
651
- pathParamsMapping && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})] }),
652
- queryParamsMapping && queryParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
653
- `const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`,
654
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
655
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})
656
- ] }),
657
- headerParamsMapping && headerParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
658
- `const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`,
659
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
660
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})
661
- ] }),
662
- parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
663
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
664
- isFormData && requestName && "const formData = buildFormData(requestData)",
852
+ pathParamsMapping && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
853
+ queryParamsMapping && queryParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [`const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})] }),
854
+ headerParamsMapping && headerParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})] }),
855
+ requestParser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
856
+ zodQueryParamsName && `const requestParams = ${zodQueryParamsName}.parse(params)`,
857
+ (isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
665
858
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
666
- isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await fetch<${generics.join(", ")}>(${clientParams.toCall()})`,
859
+ isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
667
860
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
668
861
  childrenElement
669
862
  ]
670
863
  })
671
864
  })] });
672
865
  }
673
- Client.getParams = getParams;
674
866
  //#endregion
675
867
  //#region src/components/ClassClient.tsx
676
868
  const declarationPrinter$1 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
677
869
  function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
678
- const path = new URLPath(node.path, { casing: paramsCasing });
679
- const contentType = node.requestBody?.content?.[0]?.contentType ?? "application/json";
680
- const isFormData = contentType === "multipart/form-data";
681
- const headers = buildHeaders(contentType, !!(node.parameters.filter((p) => p.in === "header").length > 0 ? tsResolver.resolveHeaderParamsName(node, node.parameters.filter((p) => p.in === "header")[0]) : void 0));
682
- const generics = buildGenerics(node, tsResolver);
683
- const paramsNode = ClassClient.getParams({
870
+ if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
871
+ const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
872
+ const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
873
+ const { header: headerParams } = getOperationParameters(node);
874
+ const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
875
+ const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
876
+ const generics = buildGenerics(node, tsResolver, {
877
+ dataReturnType,
878
+ zodResolver,
879
+ parser
880
+ });
881
+ const paramsNode = buildClientParamsNode({
684
882
  paramsType,
685
883
  paramsCasing,
686
884
  pathParamsType,
@@ -689,31 +887,47 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
689
887
  isConfigurable: true
690
888
  });
691
889
  const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
890
+ const { query: queryParams } = getOperationParameters(node);
891
+ const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
692
892
  const clientParams = buildClassClientParams({
693
893
  node,
694
- path,
894
+ url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
695
895
  baseURL,
696
896
  tsResolver,
697
897
  isFormData,
698
- headers
898
+ isMultipleContentTypes,
899
+ hasFormData,
900
+ headers,
901
+ zodQueryParamsName
699
902
  });
700
- const jsdoc = buildJSDoc(getComments(node));
903
+ const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
904
+ link: "urlPath",
905
+ linkPosition: "beforeDeprecated",
906
+ splitLines: true
907
+ }));
701
908
  const requestDataLine = buildRequestDataLine({
702
909
  parser,
703
910
  node,
704
911
  zodResolver
705
912
  });
706
- const formDataLine = buildFormDataLine(isFormData, !!node.requestBody?.content?.[0]?.schema);
913
+ const queryParamsLine = buildQueryParamsLine({
914
+ parser,
915
+ node,
916
+ zodResolver
917
+ });
918
+ const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
707
919
  const returnStatement = buildReturnStatement({
708
920
  dataReturnType,
709
921
  parser,
710
922
  node,
711
- zodResolver
923
+ zodResolver,
924
+ tsResolver
712
925
  });
713
926
  return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
714
- "const { client: request = fetch, ...requestConfig } = mergeConfig(this.#config, config)",
927
+ `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
715
928
  "",
716
929
  requestDataLine,
930
+ queryParamsLine,
717
931
  formDataLine,
718
932
  `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
719
933
  returnStatement
@@ -748,15 +962,14 @@ ${operations.map(({ node, name: methodName, tsResolver, zodResolver }) => genera
748
962
  children: [classCode, children]
749
963
  });
750
964
  }
751
- ClassClient.getParams = Client.getParams;
752
965
  //#endregion
753
966
  //#region src/components/WrapperClient.tsx
754
- function WrapperClient({ name, classNames, isExportable = true, isIndexable = true }) {
967
+ function WrapperClient({ name, controllers, isExportable = true, isIndexable = true }) {
755
968
  const classCode = `export class ${name} {
756
- ${classNames.map((className) => ` readonly ${camelCase(className)}: ${className}`).join("\n")}
969
+ ${controllers.map(({ className, propertyName }) => ` readonly ${propertyName}: ${className}`).join("\n")}
757
970
 
758
971
  constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
759
- ${classNames.map((className) => ` this.${camelCase(className)} = new ${className}(config)`).join("\n")}
972
+ ${controllers.map(({ className, propertyName }) => ` this.${propertyName} = new ${className}(config)`).join("\n")}
760
973
  }
761
974
  }`;
762
975
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
@@ -769,54 +982,47 @@ ${classNames.map((className) => ` this.${camelCase(className)} = new ${classN
769
982
  //#endregion
770
983
  //#region src/generators/classClientGenerator.tsx
771
984
  function resolveTypeImportNames$1(node, tsResolver) {
772
- return [
773
- node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
774
- tsResolver.resolveResponseName(node),
775
- ...node.parameters.filter((p) => p.in === "path").map((p) => tsResolver.resolvePathParamsName(node, p)),
776
- ...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
777
- ...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
778
- ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
779
- ].filter((n) => Boolean(n));
985
+ return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
780
986
  }
781
987
  require_chunk.__name(resolveTypeImportNames$1, "resolveTypeImportNames");
782
- function resolveZodImportNames$1(node, zodResolver) {
783
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter((n) => Boolean(n));
988
+ function resolveZodImportNames$1(node, zodResolver, parser) {
989
+ const { query: queryParams } = getOperationParameters(node);
990
+ return [
991
+ resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
992
+ resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
993
+ resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
994
+ ].filter((n) => Boolean(n));
784
995
  }
785
996
  require_chunk.__name(resolveZodImportNames$1, "resolveZodImportNames");
997
+ /**
998
+ * Built-in `operations` generator for `@kubb/plugin-client` when
999
+ * `clientType: 'class'`. Emits one class per tag, with one instance method
1000
+ * per operation and a shared constructor for request configuration.
1001
+ */
786
1002
  const classClientGenerator = (0, _kubb_core.defineGenerator)({
787
1003
  name: "classClient",
788
1004
  renderer: _kubb_renderer_jsx.jsxRenderer,
789
1005
  operations(nodes, ctx) {
790
- const { adapter, config, driver, resolver, root } = ctx;
1006
+ const { config, driver, resolver, root } = ctx;
791
1007
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
792
- const baseURL = ctx.options.baseURL ?? adapter.inputNode?.meta?.baseURL;
1008
+ const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
793
1009
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
794
1010
  if (!pluginTs) return null;
795
1011
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
796
1012
  const tsPluginOptions = pluginTs.options;
797
- const pluginZod = parser === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : void 0;
798
- const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : void 0;
1013
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1014
+ const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
799
1015
  function buildOperationData(node) {
800
- const typeFile = tsResolver.resolveFile({
801
- name: node.operationId,
802
- extname: ".ts",
803
- tag: node.tags[0] ?? "default",
804
- path: node.path
805
- }, {
1016
+ const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
806
1017
  root,
807
1018
  output: tsPluginOptions?.output ?? output,
808
1019
  group: tsPluginOptions?.group
809
1020
  });
810
- const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
811
- name: node.operationId,
812
- extname: ".ts",
813
- tag: node.tags[0] ?? "default",
814
- path: node.path
815
- }, {
1021
+ const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
816
1022
  root,
817
1023
  output: pluginZod.options?.output ?? output,
818
- group: pluginZod.options?.group
819
- }) : void 0;
1024
+ group: pluginZod.options?.group ?? void 0
1025
+ }) : null;
820
1026
  return {
821
1027
  node,
822
1028
  name: resolver.resolveName(node.operationId),
@@ -827,27 +1033,31 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
827
1033
  };
828
1034
  }
829
1035
  const controllers = nodes.reduce((acc, operationNode) => {
1036
+ if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
830
1037
  const tag = operationNode.tags[0];
831
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? pascalCase(tag) : "Client";
1038
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
832
1039
  if (!tag && !group) {
833
- const name = "ApiClient";
1040
+ const name = resolver.resolveClassName("ApiClient");
834
1041
  const file = resolver.resolveFile({
835
1042
  name,
836
1043
  extname: ".ts"
837
1044
  }, {
838
1045
  root,
839
1046
  output,
840
- group
1047
+ group: group ?? void 0
841
1048
  });
842
1049
  const operationData = buildOperationData(operationNode);
843
1050
  const previous = acc.find((item) => item.file.path === file.path);
844
1051
  if (previous) previous.operations.push(operationData);
845
1052
  else acc.push({
846
1053
  name,
1054
+ propertyName: resolver.resolveClientPropertyName(name),
847
1055
  file,
848
1056
  operations: [operationData]
849
1057
  });
850
- } else if (tag) {
1058
+ return acc;
1059
+ }
1060
+ if (tag) {
851
1061
  const name = groupName;
852
1062
  const file = resolver.resolveFile({
853
1063
  name,
@@ -856,13 +1066,14 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
856
1066
  }, {
857
1067
  root,
858
1068
  output,
859
- group
1069
+ group: group ?? void 0
860
1070
  });
861
1071
  const operationData = buildOperationData(operationNode);
862
1072
  const previous = acc.find((item) => item.file.path === file.path);
863
1073
  if (previous) previous.operations.push(operationData);
864
1074
  else acc.push({
865
1075
  name,
1076
+ propertyName: resolver.resolveClientPropertyName(name),
866
1077
  file,
867
1078
  operations: [operationData]
868
1079
  });
@@ -891,7 +1102,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
891
1102
  const zodFilesByPath = /* @__PURE__ */ new Map();
892
1103
  ops.forEach((op) => {
893
1104
  if (!op.zodFile || !zodResolver) return;
894
- const names = resolveZodImportNames$1(op.node, zodResolver);
1105
+ const names = resolveZodImportNames$1(op.node, zodResolver, parser);
895
1106
  if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
896
1107
  const imports = zodImportsByFile.get(op.zodFile.path);
897
1108
  names.forEach((n) => {
@@ -906,27 +1117,35 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
906
1117
  }
907
1118
  const files = controllers.map(({ name, file, operations: ops }) => {
908
1119
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
909
- const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
1120
+ const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
910
1121
  zodImportsByFile: /* @__PURE__ */ new Map(),
911
1122
  zodFilesByPath: /* @__PURE__ */ new Map()
912
1123
  };
913
- const hasFormData = ops.some((op) => op.node.requestBody?.content?.[0]?.contentType === "multipart/form-data");
1124
+ const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
914
1125
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
915
1126
  baseName: file.baseName,
916
1127
  path: file.path,
917
1128
  meta: file.meta,
918
- banner: resolver.resolveBanner(adapter.inputNode, {
1129
+ banner: resolver.resolveBanner(ctx.meta, {
919
1130
  output,
920
- config
1131
+ config,
1132
+ file: {
1133
+ path: file.path,
1134
+ baseName: file.baseName
1135
+ }
921
1136
  }),
922
- footer: resolver.resolveFooter(adapter.inputNode, {
1137
+ footer: resolver.resolveFooter(ctx.meta, {
923
1138
  output,
924
- config
1139
+ config,
1140
+ file: {
1141
+ path: file.path,
1142
+ baseName: file.baseName
1143
+ }
925
1144
  }),
926
1145
  children: [
927
1146
  importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
928
1147
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
929
- name: "fetch",
1148
+ name: "client",
930
1149
  path: importPath
931
1150
  }),
932
1151
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
@@ -944,7 +1163,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
944
1163
  })
945
1164
  ] }) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
946
1165
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
947
- name: ["fetch"],
1166
+ name: ["client"],
948
1167
  root: file.path,
949
1168
  path: node_path.default.resolve(root, ".kubb/client.ts")
950
1169
  }),
@@ -969,6 +1188,11 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
969
1188
  root: file.path,
970
1189
  path: node_path.default.resolve(root, ".kubb/config.ts")
971
1190
  }),
1191
+ parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1192
+ name: ["z"],
1193
+ path: "zod",
1194
+ isTypeOnly: true
1195
+ }),
972
1196
  Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
973
1197
  const typeFile = typeFilesByPath.get(filePath);
974
1198
  if (!typeFile) return null;
@@ -981,7 +1205,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
981
1205
  isTypeOnly: true
982
1206
  }, filePath);
983
1207
  }),
984
- parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1208
+ isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
985
1209
  const zodFile = zodFilesByPath.get(filePath);
986
1210
  if (!zodFile) return null;
987
1211
  const importNames = Array.from(importSet).filter(Boolean);
@@ -1012,19 +1236,27 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1012
1236
  }, {
1013
1237
  root,
1014
1238
  output,
1015
- group
1239
+ group: group ?? void 0
1016
1240
  });
1017
1241
  files.push(/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1018
1242
  baseName: sdkFile.baseName,
1019
1243
  path: sdkFile.path,
1020
1244
  meta: sdkFile.meta,
1021
- banner: resolver.resolveBanner(adapter.inputNode, {
1245
+ banner: resolver.resolveBanner(ctx.meta, {
1022
1246
  output,
1023
- config
1247
+ config,
1248
+ file: {
1249
+ path: sdkFile.path,
1250
+ baseName: sdkFile.baseName
1251
+ }
1024
1252
  }),
1025
- footer: resolver.resolveFooter(adapter.inputNode, {
1253
+ footer: resolver.resolveFooter(ctx.meta, {
1026
1254
  output,
1027
- config
1255
+ config,
1256
+ file: {
1257
+ path: sdkFile.path,
1258
+ baseName: sdkFile.baseName
1259
+ }
1028
1260
  }),
1029
1261
  children: [
1030
1262
  importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
@@ -1044,7 +1276,10 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1044
1276
  }, name)),
1045
1277
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(WrapperClient, {
1046
1278
  name: sdk.className,
1047
- classNames: controllers.map(({ name }) => name)
1279
+ controllers: controllers.map(({ name, propertyName }) => ({
1280
+ className: name,
1281
+ propertyName
1282
+ }))
1048
1283
  })
1049
1284
  ]
1050
1285
  }, sdkFile.path));
@@ -1054,81 +1289,75 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1054
1289
  });
1055
1290
  //#endregion
1056
1291
  //#region src/generators/clientGenerator.tsx
1292
+ /**
1293
+ * Built-in operation generator for `@kubb/plugin-client`. Emits one async
1294
+ * function per OpenAPI operation, plus the matching URL helper. Used when
1295
+ * `clientType: 'function'` (the default).
1296
+ */
1057
1297
  const clientGenerator = (0, _kubb_core.defineGenerator)({
1058
1298
  name: "client",
1059
1299
  renderer: _kubb_renderer_jsx.jsxRenderer,
1060
1300
  operation(node, ctx) {
1061
- const { adapter, config, driver, resolver, root } = ctx;
1301
+ if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
1302
+ const { config, driver, resolver, root } = ctx;
1062
1303
  const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options;
1063
- const baseURL = ctx.options.baseURL ?? adapter.inputNode?.meta?.baseURL;
1304
+ const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
1064
1305
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1065
1306
  if (!pluginTs) return null;
1066
1307
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1067
- const pluginZod = parser === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : void 0;
1068
- const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : void 0;
1069
- const casedParams = _kubb_core.ast.caseParams(node.parameters, paramsCasing);
1070
- const pathParams = casedParams.filter((p) => p.in === "path");
1071
- const queryParams = casedParams.filter((p) => p.in === "query");
1072
- const headerParams = casedParams.filter((p) => p.in === "header");
1073
- const importedTypeNames = [
1074
- ...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
1075
- ...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
1076
- ...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
1077
- node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
1078
- tsResolver.resolveResponseName(node),
1079
- ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
1080
- ].filter(Boolean);
1081
- const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter(Boolean) : [];
1308
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1309
+ const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1310
+ const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
1311
+ const { query: queryParams } = getOperationParameters(node);
1312
+ const importedZodNames = zodResolver ? [
1313
+ resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1314
+ resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
1315
+ resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
1316
+ ].filter((name) => Boolean(name)) : [];
1317
+ const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) ?? null : null;
1082
1318
  const meta = {
1083
1319
  name: resolver.resolveName(node.operationId),
1084
- urlName: `get${resolver.resolveName(node.operationId).charAt(0).toUpperCase()}${resolver.resolveName(node.operationId).slice(1)}Url`,
1085
- file: resolver.resolveFile({
1086
- name: node.operationId,
1087
- extname: ".ts",
1088
- tag: node.tags[0] ?? "default",
1089
- path: node.path
1090
- }, {
1320
+ urlName: resolver.resolveUrlName(node),
1321
+ file: resolver.resolveFile(operationFileEntry(node, node.operationId), {
1091
1322
  root,
1092
1323
  output,
1093
- group
1324
+ group: group ?? void 0
1094
1325
  }),
1095
- fileTs: tsResolver.resolveFile({
1096
- name: node.operationId,
1097
- extname: ".ts",
1098
- tag: node.tags[0] ?? "default",
1099
- path: node.path
1100
- }, {
1326
+ fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
1101
1327
  root,
1102
1328
  output: pluginTs.options?.output ?? output,
1103
- group: pluginTs.options?.group
1329
+ group: pluginTs.options?.group ?? void 0
1104
1330
  }),
1105
- fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile({
1106
- name: node.operationId,
1107
- extname: ".ts",
1108
- tag: node.tags[0] ?? "default",
1109
- path: node.path
1110
- }, {
1331
+ fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1111
1332
  root,
1112
1333
  output: pluginZod.options.output ?? output,
1113
- group: pluginZod.options?.group
1114
- }) : void 0
1334
+ group: pluginZod.options?.group ?? void 0
1335
+ }) : null
1115
1336
  };
1116
- const isFormData = node.requestBody?.content?.[0]?.contentType === "multipart/form-data";
1337
+ const hasFormData = node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false;
1117
1338
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1118
1339
  baseName: meta.file.baseName,
1119
1340
  path: meta.file.path,
1120
1341
  meta: meta.file.meta,
1121
- banner: resolver.resolveBanner(adapter.inputNode, {
1342
+ banner: resolver.resolveBanner(ctx.meta, {
1122
1343
  output,
1123
- config
1344
+ config,
1345
+ file: {
1346
+ path: meta.file.path,
1347
+ baseName: meta.file.baseName
1348
+ }
1124
1349
  }),
1125
- footer: resolver.resolveFooter(adapter.inputNode, {
1350
+ footer: resolver.resolveFooter(ctx.meta, {
1126
1351
  output,
1127
- config
1352
+ config,
1353
+ file: {
1354
+ path: meta.file.path,
1355
+ baseName: meta.file.baseName
1356
+ }
1128
1357
  }),
1129
1358
  children: [
1130
1359
  importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1131
- name: "fetch",
1360
+ name: "client",
1132
1361
  path: importPath
1133
1362
  }), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1134
1363
  name: [
@@ -1139,7 +1368,7 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1139
1368
  path: importPath,
1140
1369
  isTypeOnly: true
1141
1370
  })] }) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1142
- name: ["fetch"],
1371
+ name: ["client"],
1143
1372
  root: meta.file.path,
1144
1373
  path: node_path.default.resolve(root, ".kubb/client.ts")
1145
1374
  }), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
@@ -1152,11 +1381,16 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1152
1381
  path: node_path.default.resolve(root, ".kubb/client.ts"),
1153
1382
  isTypeOnly: true
1154
1383
  })] }),
1155
- isFormData && node.requestBody?.content?.[0]?.schema && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1384
+ hasFormData && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1156
1385
  name: ["buildFormData"],
1157
1386
  root: meta.file.path,
1158
1387
  path: node_path.default.resolve(root, ".kubb/config.ts")
1159
1388
  }),
1389
+ zodRequestName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1390
+ name: ["z"],
1391
+ path: "zod",
1392
+ isTypeOnly: true
1393
+ }),
1160
1394
  meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1161
1395
  name: importedZodNames,
1162
1396
  root: meta.file.path,
@@ -1198,16 +1432,22 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1198
1432
  });
1199
1433
  //#endregion
1200
1434
  //#region src/generators/groupedClientGenerator.tsx
1435
+ /**
1436
+ * Emits one aggregate file per tag/group when `group` is configured. Each
1437
+ * file re-exports every client function for that group, so callers can
1438
+ * `import { petController } from './gen/clients'` instead of importing
1439
+ * each operation individually.
1440
+ */
1201
1441
  const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1202
1442
  name: "groupedClient",
1203
1443
  renderer: _kubb_renderer_jsx.jsxRenderer,
1204
1444
  operations(nodes, ctx) {
1205
- const { config, resolver, adapter, root } = ctx;
1445
+ const { config, resolver, root } = ctx;
1206
1446
  const { output, group } = ctx.options;
1207
1447
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: nodes.reduce((acc, operationNode) => {
1208
1448
  if (group?.type === "tag") {
1209
1449
  const tag = operationNode.tags[0];
1210
- const name = tag ? group?.name?.({ group: camelCase(tag) }) : void 0;
1450
+ const name = tag ? group?.name?.({ group: camelCase(tag) }) : null;
1211
1451
  if (!tag || !name) return acc;
1212
1452
  const file = resolver.resolveFile({
1213
1453
  name,
@@ -1216,7 +1456,7 @@ const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1216
1456
  }, {
1217
1457
  root,
1218
1458
  output,
1219
- group
1459
+ group: group ?? void 0
1220
1460
  });
1221
1461
  const clientFile = resolver.resolveFile({
1222
1462
  name: operationNode.operationId,
@@ -1226,7 +1466,7 @@ const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1226
1466
  }, {
1227
1467
  root,
1228
1468
  output,
1229
- group
1469
+ group: group ?? void 0
1230
1470
  });
1231
1471
  const client = {
1232
1472
  name: resolver.resolveName(operationNode.operationId),
@@ -1246,13 +1486,23 @@ const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1246
1486
  baseName: file.baseName,
1247
1487
  path: file.path,
1248
1488
  meta: file.meta,
1249
- banner: resolver.resolveBanner(adapter.inputNode, {
1489
+ banner: resolver.resolveBanner(ctx.meta, {
1250
1490
  output,
1251
- config
1491
+ config,
1492
+ file: {
1493
+ path: file.path,
1494
+ baseName: file.baseName,
1495
+ isAggregation: true
1496
+ }
1252
1497
  }),
1253
- footer: resolver.resolveFooter(adapter.inputNode, {
1498
+ footer: resolver.resolveFooter(ctx.meta, {
1254
1499
  output,
1255
- config
1500
+ config,
1501
+ file: {
1502
+ path: file.path,
1503
+ baseName: file.baseName,
1504
+ isAggregation: true
1505
+ }
1256
1506
  }),
1257
1507
  children: [clients.map((client) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1258
1508
  name: [client.name],
@@ -1273,33 +1523,17 @@ const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1273
1523
  }
1274
1524
  });
1275
1525
  //#endregion
1276
- //#region src/components/Operations.tsx
1277
- function Operations({ name, nodes }) {
1278
- const operationsObject = {};
1279
- nodes.forEach((node) => {
1280
- operationsObject[node.operationId] = {
1281
- path: new URLPath(node.path).URL,
1282
- method: node.method.toLowerCase()
1283
- };
1284
- });
1285
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1286
- name,
1287
- isExportable: true,
1288
- isIndexable: true,
1289
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
1290
- name,
1291
- export: true,
1292
- children: JSON.stringify(operationsObject, void 0, 2)
1293
- })
1294
- });
1295
- }
1296
- //#endregion
1297
- //#region src/generators/operationsGenerator.tsx
1526
+ //#region src/generators/operationsGenerator.ts
1527
+ /**
1528
+ * Generates an `operations.ts` file that re-exports every operation grouped
1529
+ * by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
1530
+ * for building meta-tooling on top of the generated client (route
1531
+ * registries, API explorers).
1532
+ */
1298
1533
  const operationsGenerator = (0, _kubb_core.defineGenerator)({
1299
1534
  name: "client",
1300
- renderer: _kubb_renderer_jsx.jsxRenderer,
1301
1535
  operations(nodes, ctx) {
1302
- const { config, resolver, adapter, root } = ctx;
1536
+ const { config, resolver, root } = ctx;
1303
1537
  const { output, group } = ctx.options;
1304
1538
  const name = "operations";
1305
1539
  const file = resolver.resolveFile({
@@ -1308,37 +1542,65 @@ const operationsGenerator = (0, _kubb_core.defineGenerator)({
1308
1542
  }, {
1309
1543
  root,
1310
1544
  output,
1311
- group
1545
+ group: group ?? void 0
1312
1546
  });
1313
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File, {
1547
+ const operationsObject = {};
1548
+ for (const node of nodes) {
1549
+ if (!_kubb_core.ast.isHttpOperationNode(node)) continue;
1550
+ operationsObject[node.operationId] = {
1551
+ path: Url$1.toPath(node.path),
1552
+ method: node.method.toLowerCase()
1553
+ };
1554
+ }
1555
+ return [_kubb_core.ast.createFile({
1314
1556
  baseName: file.baseName,
1315
1557
  path: file.path,
1316
1558
  meta: file.meta,
1317
- banner: resolver.resolveBanner(adapter.inputNode, {
1559
+ banner: resolver.resolveBanner(ctx.meta, {
1318
1560
  output,
1319
- config
1561
+ config,
1562
+ file: {
1563
+ path: file.path,
1564
+ baseName: file.baseName
1565
+ }
1320
1566
  }),
1321
- footer: resolver.resolveFooter(adapter.inputNode, {
1567
+ footer: resolver.resolveFooter(ctx.meta, {
1322
1568
  output,
1323
- config
1569
+ config,
1570
+ file: {
1571
+ path: file.path,
1572
+ baseName: file.baseName
1573
+ }
1324
1574
  }),
1325
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Operations, {
1575
+ sources: [_kubb_core.ast.createSource({
1326
1576
  name,
1327
- nodes
1328
- })
1329
- });
1577
+ isExportable: true,
1578
+ isIndexable: true,
1579
+ nodes: [_kubb_core.ast.createConst({
1580
+ name,
1581
+ export: true,
1582
+ nodes: [_kubb_core.ast.createText(JSON.stringify(operationsObject, void 0, 2))]
1583
+ })]
1584
+ })]
1585
+ })];
1330
1586
  }
1331
1587
  });
1332
1588
  //#endregion
1333
1589
  //#region src/components/StaticClassClient.tsx
1334
1590
  const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
1335
1591
  function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
1336
- const path = new URLPath(node.path, { casing: paramsCasing });
1337
- const contentType = node.requestBody?.content?.[0]?.contentType ?? "application/json";
1338
- const isFormData = contentType === "multipart/form-data";
1339
- const headers = buildHeaders(contentType, !!(node.parameters.filter((p) => p.in === "header").length > 0 ? tsResolver.resolveHeaderParamsName(node, node.parameters.filter((p) => p.in === "header")[0]) : void 0));
1340
- const generics = buildGenerics(node, tsResolver);
1341
- const paramsNode = Client.getParams({
1592
+ if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
1593
+ const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
1594
+ const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
1595
+ const { header: headerParams } = getOperationParameters(node);
1596
+ const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
1597
+ const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
1598
+ const generics = buildGenerics(node, tsResolver, {
1599
+ dataReturnType,
1600
+ zodResolver,
1601
+ parser
1602
+ });
1603
+ const paramsNode = buildClientParamsNode({
1342
1604
  paramsType,
1343
1605
  paramsCasing,
1344
1606
  pathParamsType,
@@ -1347,31 +1609,47 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
1347
1609
  isConfigurable: true
1348
1610
  });
1349
1611
  const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1612
+ const { query: queryParams } = getOperationParameters(node);
1613
+ const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
1350
1614
  const clientParams = buildClassClientParams({
1351
1615
  node,
1352
- path,
1616
+ url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
1353
1617
  baseURL,
1354
1618
  tsResolver,
1355
1619
  isFormData,
1356
- headers
1620
+ isMultipleContentTypes,
1621
+ hasFormData,
1622
+ headers,
1623
+ zodQueryParamsName
1357
1624
  });
1358
- const jsdoc = buildJSDoc(getComments(node));
1625
+ const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
1626
+ link: "urlPath",
1627
+ linkPosition: "beforeDeprecated",
1628
+ splitLines: true
1629
+ }));
1359
1630
  const requestDataLine = buildRequestDataLine({
1360
1631
  parser,
1361
1632
  node,
1362
1633
  zodResolver
1363
1634
  });
1364
- const formDataLine = buildFormDataLine(isFormData, !!node.requestBody?.content?.[0]?.schema);
1635
+ const queryParamsLine = buildQueryParamsLine({
1636
+ parser,
1637
+ node,
1638
+ zodResolver
1639
+ });
1640
+ const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
1365
1641
  const returnStatement = buildReturnStatement({
1366
1642
  dataReturnType,
1367
1643
  parser,
1368
1644
  node,
1369
- zodResolver
1645
+ zodResolver,
1646
+ tsResolver
1370
1647
  });
1371
1648
  return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
1372
- "const { client: request = fetch, ...requestConfig } = mergeConfig(this.#config, config)",
1649
+ `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
1373
1650
  "",
1374
1651
  requestDataLine,
1652
+ queryParamsLine,
1375
1653
  formDataLine,
1376
1654
  `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
1377
1655
  returnStatement
@@ -1397,56 +1675,49 @@ function StaticClassClient({ name, isExportable = true, isIndexable = true, oper
1397
1675
  children: [classCode, children]
1398
1676
  });
1399
1677
  }
1400
- StaticClassClient.getParams = Client.getParams;
1401
1678
  //#endregion
1402
1679
  //#region src/generators/staticClassClientGenerator.tsx
1403
1680
  function resolveTypeImportNames(node, tsResolver) {
1681
+ return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
1682
+ }
1683
+ function resolveZodImportNames(node, zodResolver, parser) {
1684
+ const { query: queryParams } = getOperationParameters(node);
1404
1685
  return [
1405
- node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
1406
- tsResolver.resolveResponseName(node),
1407
- ...node.parameters.filter((p) => p.in === "path").map((p) => tsResolver.resolvePathParamsName(node, p)),
1408
- ...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
1409
- ...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
1410
- ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
1686
+ resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1687
+ resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
1688
+ resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
1411
1689
  ].filter((n) => Boolean(n));
1412
1690
  }
1413
- function resolveZodImportNames(node, zodResolver) {
1414
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter((n) => Boolean(n));
1415
- }
1691
+ /**
1692
+ * Built-in `operations` generator for `@kubb/plugin-client` when
1693
+ * `clientType: 'staticClass'`. Emits one class per tag, with a static method
1694
+ * per operation so callers can use `Pet.getPetById(...)` without
1695
+ * instantiating the class.
1696
+ */
1416
1697
  const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1417
1698
  name: "staticClassClient",
1418
1699
  renderer: _kubb_renderer_jsx.jsxRenderer,
1419
1700
  operations(nodes, ctx) {
1420
- const { adapter, config, driver, resolver, root } = ctx;
1701
+ const { config, driver, resolver, root } = ctx;
1421
1702
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
1422
- const baseURL = ctx.options.baseURL ?? adapter.inputNode?.meta?.baseURL;
1703
+ const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
1423
1704
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1424
1705
  if (!pluginTs) return null;
1425
1706
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1426
1707
  const tsPluginOptions = pluginTs.options;
1427
- const pluginZod = parser === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : void 0;
1428
- const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : void 0;
1708
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1709
+ const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1429
1710
  function buildOperationData(node) {
1430
- const typeFile = tsResolver.resolveFile({
1431
- name: node.operationId,
1432
- extname: ".ts",
1433
- tag: node.tags[0] ?? "default",
1434
- path: node.path
1435
- }, {
1711
+ const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
1436
1712
  root,
1437
1713
  output: tsPluginOptions?.output ?? output,
1438
1714
  group: tsPluginOptions?.group
1439
1715
  });
1440
- const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
1441
- name: node.operationId,
1442
- extname: ".ts",
1443
- tag: node.tags[0] ?? "default",
1444
- path: node.path
1445
- }, {
1716
+ const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1446
1717
  root,
1447
1718
  output: pluginZod.options?.output ?? output,
1448
- group: pluginZod.options?.group
1449
- }) : void 0;
1719
+ group: pluginZod.options?.group ?? void 0
1720
+ }) : null;
1450
1721
  return {
1451
1722
  node,
1452
1723
  name: resolver.resolveName(node.operationId),
@@ -1457,17 +1728,18 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1457
1728
  };
1458
1729
  }
1459
1730
  const controllers = nodes.reduce((acc, operationNode) => {
1731
+ if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
1460
1732
  const tag = operationNode.tags[0];
1461
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? pascalCase(tag) : "Client";
1733
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
1462
1734
  if (!tag && !group) {
1463
- const name = "ApiClient";
1735
+ const name = resolver.resolveClassName("ApiClient");
1464
1736
  const file = resolver.resolveFile({
1465
1737
  name,
1466
1738
  extname: ".ts"
1467
1739
  }, {
1468
1740
  root,
1469
1741
  output,
1470
- group
1742
+ group: group ?? void 0
1471
1743
  });
1472
1744
  const operationData = buildOperationData(operationNode);
1473
1745
  const previous = acc.find((item) => item.file.path === file.path);
@@ -1477,7 +1749,9 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1477
1749
  file,
1478
1750
  operations: [operationData]
1479
1751
  });
1480
- } else if (tag) {
1752
+ return acc;
1753
+ }
1754
+ if (tag) {
1481
1755
  const name = groupName;
1482
1756
  const file = resolver.resolveFile({
1483
1757
  name,
@@ -1486,7 +1760,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1486
1760
  }, {
1487
1761
  root,
1488
1762
  output,
1489
- group
1763
+ group: group ?? void 0
1490
1764
  });
1491
1765
  const operationData = buildOperationData(operationNode);
1492
1766
  const previous = acc.find((item) => item.file.path === file.path);
@@ -1521,7 +1795,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1521
1795
  const zodFilesByPath = /* @__PURE__ */ new Map();
1522
1796
  ops.forEach((op) => {
1523
1797
  if (!op.zodFile || !zodResolver) return;
1524
- const names = resolveZodImportNames(op.node, zodResolver);
1798
+ const names = resolveZodImportNames(op.node, zodResolver, parser);
1525
1799
  if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1526
1800
  const imports = zodImportsByFile.get(op.zodFile.path);
1527
1801
  names.forEach((n) => {
@@ -1536,27 +1810,35 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1536
1810
  }
1537
1811
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
1538
1812
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1539
- const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
1813
+ const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
1540
1814
  zodImportsByFile: /* @__PURE__ */ new Map(),
1541
1815
  zodFilesByPath: /* @__PURE__ */ new Map()
1542
1816
  };
1543
- const hasFormData = ops.some((op) => op.node.requestBody?.content?.[0]?.contentType === "multipart/form-data");
1817
+ const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
1544
1818
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1545
1819
  baseName: file.baseName,
1546
1820
  path: file.path,
1547
1821
  meta: file.meta,
1548
- banner: resolver.resolveBanner(adapter.inputNode, {
1822
+ banner: resolver.resolveBanner(ctx.meta, {
1549
1823
  output,
1550
- config
1824
+ config,
1825
+ file: {
1826
+ path: file.path,
1827
+ baseName: file.baseName
1828
+ }
1551
1829
  }),
1552
- footer: resolver.resolveFooter(adapter.inputNode, {
1830
+ footer: resolver.resolveFooter(ctx.meta, {
1553
1831
  output,
1554
- config
1832
+ config,
1833
+ file: {
1834
+ path: file.path,
1835
+ baseName: file.baseName
1836
+ }
1555
1837
  }),
1556
1838
  children: [
1557
1839
  importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
1558
1840
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1559
- name: "fetch",
1841
+ name: "client",
1560
1842
  path: importPath
1561
1843
  }),
1562
1844
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
@@ -1574,7 +1856,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1574
1856
  })
1575
1857
  ] }) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
1576
1858
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1577
- name: ["fetch"],
1859
+ name: ["client"],
1578
1860
  root: file.path,
1579
1861
  path: node_path.default.resolve(root, ".kubb/client.ts")
1580
1862
  }),
@@ -1599,6 +1881,11 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1599
1881
  root: file.path,
1600
1882
  path: node_path.default.resolve(root, ".kubb/config.ts")
1601
1883
  }),
1884
+ parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1885
+ name: ["z"],
1886
+ path: "zod",
1887
+ isTypeOnly: true
1888
+ }),
1602
1889
  Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1603
1890
  const typeFile = typeFilesByPath.get(filePath);
1604
1891
  if (!typeFile) return null;
@@ -1611,7 +1898,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1611
1898
  isTypeOnly: true
1612
1899
  }, filePath);
1613
1900
  }),
1614
- parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1901
+ isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1615
1902
  const zodFile = zodFilesByPath.get(filePath);
1616
1903
  if (!zodFile) return null;
1617
1904
  const importNames = Array.from(importSet).filter(Boolean);
@@ -1640,64 +1927,95 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1640
1927
  //#endregion
1641
1928
  //#region src/resolvers/resolverClient.ts
1642
1929
  /**
1643
- * Naming convention resolver for client plugin.
1930
+ * Default resolver used by `@kubb/plugin-client`. Decides the names and file
1931
+ * paths for every generated client function or class. Functions and files use
1932
+ * camelCase; classes and tag groups use PascalCase.
1644
1933
  *
1645
- * Provides default naming helpers using camelCase for functions and file paths.
1934
+ * @example Resolve client function and class names
1935
+ * ```ts
1936
+ * import { resolverClient } from '@kubb/plugin-client'
1646
1937
  *
1647
- * @example
1648
- * `resolverClient.default('list pets', 'function') // 'listPets'`
1938
+ * resolverClient.default('list pets', 'function') // 'listPets'
1939
+ * resolverClient.resolveClassName('pet') // 'Pet'
1940
+ * resolverClient.resolveGroupName('pet') // 'PetClient'
1941
+ * resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
1942
+ * ```
1649
1943
  */
1650
- const resolverClient = (0, _kubb_core.defineResolver)((ctx) => ({
1944
+ const resolverClient = (0, _kubb_core.defineResolver)(() => ({
1651
1945
  name: "default",
1652
1946
  pluginName: "plugin-client",
1653
1947
  default(name, type) {
1654
- return camelCase(name, { isFile: type === "file" });
1948
+ if (type === "file") return toFilePath(name);
1949
+ return ensureValidVarName(camelCase(name));
1655
1950
  },
1656
1951
  resolveName(name) {
1657
- return ctx.default(name, "function");
1952
+ return this.default(name, "function");
1953
+ },
1954
+ resolvePathName(name, type) {
1955
+ return this.default(name, type);
1956
+ },
1957
+ resolveClassName(name) {
1958
+ return ensureValidVarName(pascalCase(name));
1959
+ },
1960
+ resolveGroupName(name) {
1961
+ return ensureValidVarName(pascalCase(`${name} Client`));
1962
+ },
1963
+ resolveClientPropertyName(name) {
1964
+ return ensureValidVarName(camelCase(name));
1965
+ },
1966
+ resolveUrlName(node) {
1967
+ const name = this.resolveName(node.operationId);
1968
+ return `get${name.charAt(0).toUpperCase()}${name.slice(1)}Url`;
1658
1969
  }
1659
1970
  }));
1660
1971
  //#endregion
1661
1972
  //#region src/plugin.ts
1662
1973
  /**
1663
- * Canonical plugin name for `@kubb/plugin-client`, used in driver lookups and warnings.
1974
+ * Canonical plugin name for `@kubb/plugin-client`. Used for driver lookups and
1975
+ * cross-plugin dependency references.
1664
1976
  */
1665
1977
  const pluginClientName = "plugin-client";
1666
1978
  /**
1667
- * Generates type-safe HTTP client functions or classes from an OpenAPI specification.
1668
- * Creates client APIs by walking operations and delegating to generators.
1669
- * Writes barrel files based on the configured `barrelType`.
1979
+ * Generates one HTTP client function per OpenAPI operation. Each function has
1980
+ * typed path params, query params, body, and response, so callers use the API
1981
+ * like any other typed function. Ships with `axios` and `fetch` runtimes; bring
1982
+ * your own by setting `importPath`.
1670
1983
  *
1671
- * @example Client generator
1984
+ * @example
1672
1985
  * ```ts
1673
- * import pluginClient from '@kubb/plugin-client'
1986
+ * import { defineConfig } from 'kubb'
1987
+ * import { pluginTs } from '@kubb/plugin-ts'
1988
+ * import { pluginClient } from '@kubb/plugin-client'
1989
+ *
1674
1990
  * export default defineConfig({
1675
- * plugins: [pluginClient({ output: { path: 'clients' } })]
1991
+ * input: { path: './petStore.yaml' },
1992
+ * output: { path: './src/gen' },
1993
+ * plugins: [
1994
+ * pluginTs(),
1995
+ * pluginClient({
1996
+ * output: { path: './clients' },
1997
+ * client: 'fetch',
1998
+ * }),
1999
+ * ],
1676
2000
  * })
1677
2001
  * ```
1678
2002
  */
1679
2003
  const pluginClient = (0, _kubb_core.definePlugin)((options) => {
1680
2004
  const { output = {
1681
2005
  path: "clients",
1682
- barrelType: "named"
1683
- }, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser = "client", client = "axios", importPath, bundle = false, sdk, baseURL, resolver: userResolver, transformer: userTransformer } = options;
2006
+ barrel: { type: "named" }
2007
+ }, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser = false, client = "axios", importPath, bundle = false, sdk, baseURL, resolver: userResolver, transformer: userTransformer } = options;
1684
2008
  const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
1685
2009
  const selectedGenerators = options.generators ?? [
1686
2010
  clientType === "staticClass" ? staticClassClientGenerator : clientType === "class" ? classClientGenerator : clientGenerator,
1687
- group && clientType === "function" ? groupedClientGenerator : void 0,
1688
- operations ? operationsGenerator : void 0
2011
+ group && clientType === "function" ? groupedClientGenerator : null,
2012
+ operations ? operationsGenerator : null
1689
2013
  ].filter((x) => Boolean(x));
1690
- const groupConfig = group ? {
1691
- ...group,
1692
- name: group.name ? group.name : (ctx) => {
1693
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1694
- return `${camelCase(ctx.group)}Controller`;
1695
- }
1696
- } : void 0;
2014
+ const groupConfig = createGroupConfig(group);
1697
2015
  return {
1698
2016
  name: pluginClientName,
1699
2017
  options,
1700
- dependencies: [_kubb_plugin_ts.pluginTsName, parser === "zod" ? _kubb_plugin_zod.pluginZodName : void 0].filter(Boolean),
2018
+ dependencies: [_kubb_plugin_ts.pluginTsName, isParserEnabled(parser) ? _kubb_plugin_zod.pluginZodName : null].filter((dependency) => Boolean(dependency)),
1701
2019
  hooks: { "kubb:plugin:setup"(ctx) {
1702
2020
  const resolver = userResolver ? {
1703
2021
  ...resolverClient,
@@ -1760,9 +2078,13 @@ exports.classClientGenerator = classClientGenerator;
1760
2078
  exports.clientGenerator = clientGenerator;
1761
2079
  exports.default = pluginClient;
1762
2080
  exports.groupedClientGenerator = groupedClientGenerator;
2081
+ exports.isParserEnabled = isParserEnabled;
1763
2082
  exports.operationsGenerator = operationsGenerator;
1764
2083
  exports.pluginClient = pluginClient;
1765
2084
  exports.pluginClientName = pluginClientName;
2085
+ exports.resolveQueryParamsParser = resolveQueryParamsParser;
2086
+ exports.resolveRequestParser = resolveRequestParser;
2087
+ exports.resolveResponseParser = resolveResponseParser;
1766
2088
  exports.resolverClient = resolverClient;
1767
2089
  exports.staticClassClientGenerator = staticClassClientGenerator;
1768
2090