@kubb/plugin-client 5.0.0-beta.42 → 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.
package/dist/index.cjs CHANGED
@@ -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
@@ -212,99 +202,83 @@ function ensureValidVarName(name) {
212
202
  return `_${name}`;
213
203
  }
214
204
  //#endregion
215
- //#region ../../internals/utils/src/urlPath.ts
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;
216
+ }
217
+ return Object.keys(params).length > 0 ? params : null;
218
+ }
216
219
  /**
217
- * Parses and transforms an OpenAPI/Swagger path string into various URL formats.
218
- *
219
- * @example
220
- * const p = new URLPath('/pet/{petId}')
221
- * p.URL // '/pet/:petId'
222
- * p.template // '`/pet/${petId}`'
220
+ * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
223
221
  */
224
- var URLPath = class {
225
- /**
226
- * The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
227
- */
228
- path;
229
- #options;
230
- constructor(path, options = {}) {
231
- this.path = path;
232
- this.#options = options;
222
+ var Url$1 = class Url$1 {
223
+ static {
224
+ require_chunk.__name(this, "Url");
233
225
  }
234
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
226
+ /**
227
+ * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
235
228
  *
236
229
  * @example
237
- * ```ts
238
- * new URLPath('/pet/{petId}').URL // '/pet/:petId'
239
- * ```
230
+ * Url.canParse('https://petstore.swagger.io/v2') // true
231
+ * Url.canParse('/pet/{petId}') // false
240
232
  */
241
- get URL() {
242
- return this.toURLPath();
233
+ static canParse(url, base) {
234
+ return URL.canParse(url, base);
243
235
  }
244
- /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
236
+ /**
237
+ * Converts an OpenAPI/Swagger path to Express-style colon syntax.
245
238
  *
246
239
  * @example
247
- * ```ts
248
- * new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
249
- * new URLPath('/pet/{petId}').isURL // false
250
- * ```
240
+ * Url.toPath('/pet/{petId}') // '/pet/:petId'
251
241
  */
252
- get isURL() {
253
- try {
254
- return !!new URL(this.path).href;
255
- } catch {
256
- return false;
257
- }
242
+ static toPath(path) {
243
+ return path.replace(/\{([^}]+)\}/g, ":$1");
258
244
  }
259
245
  /**
260
- * Converts the OpenAPI path to a TypeScript template literal string.
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.
261
249
  *
262
250
  * @example
263
- * new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
264
- * new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
265
- */
266
- get template() {
267
- return this.toTemplateString();
268
- }
269
- /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
251
+ * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
270
252
  *
271
253
  * @example
272
- * ```ts
273
- * new URLPath('/pet/{petId}').object
274
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
275
- * ```
254
+ * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
276
255
  */
277
- get object() {
278
- return this.toObject();
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}\``;
279
263
  }
280
- /** Returns a map of path parameter names, or `null` when the path has no parameters.
264
+ /**
265
+ * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
266
+ * expression when `stringify` is set.
281
267
  *
282
268
  * @example
283
- * ```ts
284
- * new URLPath('/pet/{petId}').params // { petId: 'petId' }
285
- * new URLPath('/pet').params // null
286
- * ```
287
- */
288
- get params() {
289
- return this.toParamsObject();
290
- }
291
- #transformParam(raw) {
292
- const param = isValidVarName(raw) ? raw : camelCase(raw);
293
- return this.#options.casing === "camelcase" ? camelCase(param) : param;
294
- }
295
- /**
296
- * Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
269
+ * Url.toObject('/pet/{petId}')
270
+ * // { url: '/pet/:petId', params: { petId: 'petId' } }
297
271
  */
298
- #eachParam(fn) {
299
- for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
300
- const raw = match[1];
301
- fn(raw, this.#transformParam(raw));
302
- }
303
- }
304
- toObject({ type = "path", replacer, stringify } = {}) {
272
+ static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
305
273
  const object = {
306
- url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
307
- params: this.toParamsObject()
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
+ })
308
282
  };
309
283
  if (stringify) {
310
284
  if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
@@ -313,50 +287,6 @@ var URLPath = class {
313
287
  }
314
288
  return object;
315
289
  }
316
- /**
317
- * Converts the OpenAPI path to a TypeScript template literal string.
318
- * An optional `replacer` can transform each extracted parameter name before interpolation.
319
- *
320
- * @example
321
- * new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
322
- */
323
- toTemplateString({ prefix, replacer } = {}) {
324
- const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
325
- if (i % 2 === 0) return part;
326
- const param = this.#transformParam(part);
327
- return `\${${replacer ? replacer(param) : param}}`;
328
- }).join("");
329
- return `\`${prefix ?? ""}${result}\``;
330
- }
331
- /**
332
- * Extracts all `{param}` segments from the path and returns them as a key-value map.
333
- * An optional `replacer` transforms each parameter name in both key and value positions.
334
- * Returns `undefined` when no path parameters are found.
335
- *
336
- * @example
337
- * ```ts
338
- * new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
339
- * // { petId: 'petId', tagId: 'tagId' }
340
- * ```
341
- */
342
- toParamsObject(replacer) {
343
- const params = {};
344
- this.#eachParam((_raw, param) => {
345
- const key = replacer ? replacer(param) : param;
346
- params[key] = key;
347
- });
348
- return Object.keys(params).length > 0 ? params : null;
349
- }
350
- /** Converts the OpenAPI path to Express-style colon syntax.
351
- *
352
- * @example
353
- * ```ts
354
- * new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
355
- * ```
356
- */
357
- toURLPath() {
358
- return this.path.replace(/\{([^}]+)\}/g, ":$1");
359
- }
360
290
  };
361
291
  //#endregion
362
292
  //#region ../../internals/shared/src/operation.ts
@@ -382,7 +312,7 @@ function operationFileEntry(node, name, extname = ".ts") {
382
312
  function getOperationLink(node, link) {
383
313
  if (!link) return null;
384
314
  if (typeof link === "function") return link(node) ?? null;
385
- if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : null;
315
+ if (link === "urlPath") return node.path ? `{@link ${Url$1.toPath(node.path)}}` : null;
386
316
  return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
387
317
  }
388
318
  function getContentTypeInfo(node) {
@@ -473,6 +403,19 @@ function resolveSuccessNames(node, resolver) {
473
403
  function resolveStatusCodeNames(node, resolver) {
474
404
  return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
475
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
+ }
476
419
  const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
477
420
  function resolveOperationTypeNames(node, resolver, options = {}) {
478
421
  const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
@@ -512,26 +455,24 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
512
455
  * shared default naming so every plugin groups output consistently:
513
456
  *
514
457
  * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
515
- * - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
458
+ * - other groups use the camelCased group (`pet store``petStore`).
516
459
  *
517
460
  * A user-provided `group.name` always wins over the default namer, so callers stay in
518
461
  * control of their output folders. Returns `null` when grouping is disabled, matching the
519
462
  * per-plugin convention.
520
463
  *
521
464
  * @param group - The user-supplied group option, or `undefined` to disable grouping.
522
- * @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
523
465
  *
524
466
  * @example
525
467
  * ```ts
526
- * createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-client, …
527
- * createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
468
+ * createGroupConfig(group) // shared across every plugin
528
469
  * ```
529
470
  */
530
- function createGroupConfig(group, options) {
471
+ function createGroupConfig(group) {
531
472
  if (!group) return null;
532
473
  const defaultName = (ctx) => {
533
474
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
534
- return `${camelCase(ctx.group)}${options.suffix}`;
475
+ return camelCase(ctx.group);
535
476
  };
536
477
  return {
537
478
  ...group,
@@ -614,6 +555,143 @@ function createFunctionParams(params) {
614
555
  };
615
556
  }
616
557
  //#endregion
558
+ //#region src/utils.ts
559
+ /**
560
+ * Returns `true` when any direction of the parser uses Zod (used for dependency checks).
561
+ */
562
+ function isParserEnabled(parser) {
563
+ if (!parser) return false;
564
+ if (parser === "zod") return true;
565
+ return !!(parser.request || parser.response);
566
+ }
567
+ /**
568
+ * Returns `'zod'` when request body parsing is enabled, `null` otherwise.
569
+ * The string shorthand `'zod'` also enables request body parsing (existing behavior).
570
+ */
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;
593
+ }
594
+ /**
595
+ * Builds HTTP headers array for a client request.
596
+ * Includes Content-Type (if not default) and spreads header parameters if present.
597
+ */
598
+ function buildHeaders(contentType, hasHeaderParams) {
599
+ return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
600
+ }
601
+ /**
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).
608
+ */
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;
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;
617
+ return [
618
+ responseName,
619
+ TError,
620
+ zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
621
+ ].filter(Boolean);
622
+ }
623
+ /**
624
+ * Builds the parameters object for a class-based client method.
625
+ * Includes URL, method, base URL, headers, and request/response data.
626
+ */
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);
632
+ return createFunctionParams({ config: {
633
+ mode: "object",
634
+ children: {
635
+ requestConfig: { mode: "inlineSpread" },
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
644
+ }
645
+ } });
646
+ }
647
+ /**
648
+ * Builds the request data parsing line for client methods.
649
+ * Applies Zod validation when `parser.request === 'zod'`, otherwise assigns data directly.
650
+ */
651
+ function buildRequestDataLine({ parser, node, zodResolver }) {
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)`;
655
+ if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
656
+ return "";
657
+ }
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
+ /**
672
+ * Builds the form data conversion line for file upload requests.
673
+ * Returns empty string if not applicable.
674
+ */
675
+ function buildFormDataLine(isFormData, hasRequest) {
676
+ return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
677
+ }
678
+ /**
679
+ * Builds the return statement for a client method.
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.
682
+ */
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)`;
692
+ return "return res.data";
693
+ }
694
+ //#endregion
617
695
  //#region src/components/Url.tsx
618
696
  const declarationPrinter$3 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
619
697
  function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
@@ -631,7 +709,6 @@ function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, ts
631
709
  }
632
710
  function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
633
711
  if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
634
- const path = new URLPath(node.path);
635
712
  const paramsNode = buildUrlParamsNode({
636
713
  paramsType,
637
714
  paramsCasing,
@@ -656,7 +733,7 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
656
733
  pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
657
734
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
658
735
  name: "res",
659
- 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 }`
660
737
  }),
661
738
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
662
739
  "return res"
@@ -685,7 +762,6 @@ function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node,
685
762
  }
686
763
  function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
687
764
  if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
688
- const path = new URLPath(node.path);
689
765
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
690
766
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
691
767
  const responseType = getResponseType(node);
@@ -699,16 +775,22 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
699
775
  const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
700
776
  const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
701
777
  const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
702
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
703
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : 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;
704
784
  const errorNames = node.responses.filter((r) => {
705
785
  return Number.parseInt(r.statusCode, 10) >= 400;
706
786
  }).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
707
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));
708
790
  const generics = [
709
- responseName,
710
- `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
711
- requestName || "unknown"
791
+ dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : responseName : responseName,
792
+ TError,
793
+ parser === "zod" && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
712
794
  ].filter(Boolean);
713
795
  const paramsNode = buildClientParamsNode({
714
796
  paramsType,
@@ -730,22 +812,23 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
730
812
  const clientParams = createFunctionParams({ config: {
731
813
  mode: "object",
732
814
  children: {
733
- method: { value: JSON.stringify(node.method.toUpperCase()) },
734
- url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path.template },
815
+ method: { value: (0, _kubb_ast_utils.stringify)(node.method.toUpperCase()) },
816
+ url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url$1.toTemplateString(node.path) },
735
817
  baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
736
- params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : null,
818
+ params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : queryParamsMapping ? { value: "mappedParams" } : {} : null,
737
819
  data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
738
820
  contentType: isConfigurable && isMultipleContentTypes ? {} : null,
739
- responseType: responseType ? { value: JSON.stringify(responseType) } : null,
821
+ responseType: responseType ? { value: (0, _kubb_ast_utils.stringify)(responseType) } : null,
740
822
  requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
741
823
  headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
742
824
  }
743
825
  } });
826
+ const statusUnionType = dataReturnType === "full" ? buildStatusUnionType(node, tsResolver) : null;
744
827
  const childrenElement = children ? children : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
745
- dataReturnType === "full" && parser === "zod" && zodResponseName && `return {...res, data: ${zodResponseName}.parse(res.data)}`,
746
- dataReturnType === "data" && parser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
747
- dataReturnType === "full" && parser !== "zod" && "return res",
748
- dataReturnType === "data" && parser !== "zod" && "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"
749
832
  ] });
750
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, {
751
834
  name,
@@ -763,23 +846,14 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
763
846
  }) },
764
847
  returnType,
765
848
  children: [
766
- isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = config` : "",
767
- /* @__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` : "",
768
850
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
769
851
  pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
770
- 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", {})] }),
771
- queryParamsMapping && queryParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
772
- `const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`,
773
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
774
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})
775
- ] }),
776
- headerParamsMapping && headerParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
777
- `const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`,
778
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
779
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})
780
- ] }),
781
- parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
782
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
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)`,
783
857
  (isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
784
858
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
785
859
  isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
@@ -790,93 +864,20 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
790
864
  })] });
791
865
  }
792
866
  //#endregion
793
- //#region src/utils.ts
794
- /**
795
- * Builds HTTP headers array for a client request.
796
- * Includes Content-Type (if not default) and spreads header parameters if present.
797
- */
798
- function buildHeaders(contentType, hasHeaderParams) {
799
- return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
800
- }
801
- /**
802
- * Builds TypeScript generic parameters for a client method.
803
- * Includes response type, error type, and optional request type.
804
- */
805
- function buildGenerics(node, tsResolver) {
806
- const successNames = resolveSuccessNames(node, tsResolver);
807
- const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
808
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
809
- const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
810
- return [
811
- responseName,
812
- `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
813
- requestName || "unknown"
814
- ].filter(Boolean);
815
- }
816
- /**
817
- * Builds the parameters object for a class-based client method.
818
- * Includes URL, method, base URL, headers, and request/response data.
819
- */
820
- function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers }) {
821
- const { query: queryParams } = getOperationParameters(node);
822
- const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
823
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
824
- const responseType = getResponseType(node);
825
- return createFunctionParams({ config: {
826
- mode: "object",
827
- children: {
828
- requestConfig: { mode: "inlineSpread" },
829
- method: { value: JSON.stringify(_kubb_core.ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
830
- url: { value: path.template },
831
- baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
832
- params: queryParamsName ? {} : null,
833
- data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
834
- contentType: isMultipleContentTypes ? {} : null,
835
- responseType: responseType ? { value: JSON.stringify(responseType) } : null,
836
- headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
837
- }
838
- } });
839
- }
840
- /**
841
- * Builds the request data parsing line for client methods.
842
- * Applies Zod validation if configured, otherwise uses data directly.
843
- */
844
- function buildRequestDataLine({ parser, node, zodResolver }) {
845
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
846
- if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
847
- if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
848
- return "";
849
- }
850
- /**
851
- * Builds the form data conversion line for file upload requests.
852
- * Returns empty string if not applicable.
853
- */
854
- function buildFormDataLine(isFormData, hasRequest) {
855
- return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
856
- }
857
- /**
858
- * Builds the return statement for a client method.
859
- * Applies Zod validation to response data if configured, otherwise returns raw response.
860
- */
861
- function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
862
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
863
- if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
864
- if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
865
- if (dataReturnType === "full" && parser !== "zod") return "return res";
866
- return "return res.data";
867
- }
868
- //#endregion
869
867
  //#region src/components/ClassClient.tsx
870
868
  const declarationPrinter$1 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
871
869
  function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
872
870
  if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
873
- const path = new URLPath(node.path, { casing: paramsCasing });
874
871
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
875
872
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
876
873
  const { header: headerParams } = getOperationParameters(node);
877
874
  const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
878
875
  const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
879
- const generics = buildGenerics(node, tsResolver);
876
+ const generics = buildGenerics(node, tsResolver, {
877
+ dataReturnType,
878
+ zodResolver,
879
+ parser
880
+ });
880
881
  const paramsNode = buildClientParamsNode({
881
882
  paramsType,
882
883
  paramsCasing,
@@ -886,17 +887,20 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
886
887
  isConfigurable: true
887
888
  });
888
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;
889
892
  const clientParams = buildClassClientParams({
890
893
  node,
891
- path,
894
+ url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
892
895
  baseURL,
893
896
  tsResolver,
894
897
  isFormData,
895
898
  isMultipleContentTypes,
896
899
  hasFormData,
897
- headers
900
+ headers,
901
+ zodQueryParamsName
898
902
  });
899
- const jsdoc = buildJSDoc(buildOperationComments(node, {
903
+ const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
900
904
  link: "urlPath",
901
905
  linkPosition: "beforeDeprecated",
902
906
  splitLines: true
@@ -906,17 +910,24 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
906
910
  node,
907
911
  zodResolver
908
912
  });
913
+ const queryParamsLine = buildQueryParamsLine({
914
+ parser,
915
+ node,
916
+ zodResolver
917
+ });
909
918
  const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
910
919
  const returnStatement = buildReturnStatement({
911
920
  dataReturnType,
912
921
  parser,
913
922
  node,
914
- zodResolver
923
+ zodResolver,
924
+ tsResolver
915
925
  });
916
926
  return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
917
- `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
927
+ `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
918
928
  "",
919
929
  requestDataLine,
930
+ queryParamsLine,
920
931
  formDataLine,
921
932
  `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
922
933
  returnStatement
@@ -974,8 +985,13 @@ function resolveTypeImportNames$1(node, tsResolver) {
974
985
  return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
975
986
  }
976
987
  require_chunk.__name(resolveTypeImportNames$1, "resolveTypeImportNames");
977
- function resolveZodImportNames$1(node, zodResolver) {
978
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].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));
979
995
  }
980
996
  require_chunk.__name(resolveZodImportNames$1, "resolveZodImportNames");
981
997
  /**
@@ -985,7 +1001,7 @@ require_chunk.__name(resolveZodImportNames$1, "resolveZodImportNames");
985
1001
  */
986
1002
  const classClientGenerator = (0, _kubb_core.defineGenerator)({
987
1003
  name: "classClient",
988
- renderer: _kubb_renderer_jsx.jsxRendererSync,
1004
+ renderer: _kubb_renderer_jsx.jsxRenderer,
989
1005
  operations(nodes, ctx) {
990
1006
  const { config, driver, resolver, root } = ctx;
991
1007
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
@@ -994,7 +1010,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
994
1010
  if (!pluginTs) return null;
995
1011
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
996
1012
  const tsPluginOptions = pluginTs.options;
997
- const pluginZod = parser === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1013
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
998
1014
  const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
999
1015
  function buildOperationData(node) {
1000
1016
  const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
@@ -1019,7 +1035,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1019
1035
  const controllers = nodes.reduce((acc, operationNode) => {
1020
1036
  if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
1021
1037
  const tag = operationNode.tags[0];
1022
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveGroupName("Client");
1038
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
1023
1039
  if (!tag && !group) {
1024
1040
  const name = resolver.resolveClassName("ApiClient");
1025
1041
  const file = resolver.resolveFile({
@@ -1086,7 +1102,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1086
1102
  const zodFilesByPath = /* @__PURE__ */ new Map();
1087
1103
  ops.forEach((op) => {
1088
1104
  if (!op.zodFile || !zodResolver) return;
1089
- const names = resolveZodImportNames$1(op.node, zodResolver);
1105
+ const names = resolveZodImportNames$1(op.node, zodResolver, parser);
1090
1106
  if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1091
1107
  const imports = zodImportsByFile.get(op.zodFile.path);
1092
1108
  names.forEach((n) => {
@@ -1101,7 +1117,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1101
1117
  }
1102
1118
  const files = controllers.map(({ name, file, operations: ops }) => {
1103
1119
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1104
- const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
1120
+ const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
1105
1121
  zodImportsByFile: /* @__PURE__ */ new Map(),
1106
1122
  zodFilesByPath: /* @__PURE__ */ new Map()
1107
1123
  };
@@ -1172,6 +1188,11 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1172
1188
  root: file.path,
1173
1189
  path: node_path.default.resolve(root, ".kubb/config.ts")
1174
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
+ }),
1175
1196
  Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1176
1197
  const typeFile = typeFilesByPath.get(filePath);
1177
1198
  if (!typeFile) return null;
@@ -1184,7 +1205,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1184
1205
  isTypeOnly: true
1185
1206
  }, filePath);
1186
1207
  }),
1187
- parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1208
+ isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1188
1209
  const zodFile = zodFilesByPath.get(filePath);
1189
1210
  if (!zodFile) return null;
1190
1211
  const importNames = Array.from(importSet).filter(Boolean);
@@ -1275,7 +1296,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
1275
1296
  */
1276
1297
  const clientGenerator = (0, _kubb_core.defineGenerator)({
1277
1298
  name: "client",
1278
- renderer: _kubb_renderer_jsx.jsxRendererSync,
1299
+ renderer: _kubb_renderer_jsx.jsxRenderer,
1279
1300
  operation(node, ctx) {
1280
1301
  if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
1281
1302
  const { config, driver, resolver, root } = ctx;
@@ -1284,10 +1305,16 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1284
1305
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1285
1306
  if (!pluginTs) return null;
1286
1307
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1287
- const pluginZod = parser === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1308
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1288
1309
  const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1289
1310
  const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
1290
- const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((name) => Boolean(name)) : [];
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;
1291
1318
  const meta = {
1292
1319
  name: resolver.resolveName(node.operationId),
1293
1320
  urlName: resolver.resolveUrlName(node),
@@ -1359,6 +1386,11 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1359
1386
  root: meta.file.path,
1360
1387
  path: node_path.default.resolve(root, ".kubb/config.ts")
1361
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
+ }),
1362
1394
  meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1363
1395
  name: importedZodNames,
1364
1396
  root: meta.file.path,
@@ -1408,7 +1440,7 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1408
1440
  */
1409
1441
  const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1410
1442
  name: "groupedClient",
1411
- renderer: _kubb_renderer_jsx.jsxRendererSync,
1443
+ renderer: _kubb_renderer_jsx.jsxRenderer,
1412
1444
  operations(nodes, ctx) {
1413
1445
  const { config, resolver, root } = ctx;
1414
1446
  const { output, group } = ctx.options;
@@ -1491,29 +1523,7 @@ const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1491
1523
  }
1492
1524
  });
1493
1525
  //#endregion
1494
- //#region src/components/Operations.tsx
1495
- function Operations({ name, nodes }) {
1496
- const operationsObject = {};
1497
- nodes.forEach((node) => {
1498
- if (!_kubb_core.ast.isHttpOperationNode(node)) return;
1499
- operationsObject[node.operationId] = {
1500
- path: new URLPath(node.path).URL,
1501
- method: node.method.toLowerCase()
1502
- };
1503
- });
1504
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1505
- name,
1506
- isExportable: true,
1507
- isIndexable: true,
1508
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
1509
- name,
1510
- export: true,
1511
- children: JSON.stringify(operationsObject, void 0, 2)
1512
- })
1513
- });
1514
- }
1515
- //#endregion
1516
- //#region src/generators/operationsGenerator.tsx
1526
+ //#region src/generators/operationsGenerator.ts
1517
1527
  /**
1518
1528
  * Generates an `operations.ts` file that re-exports every operation grouped
1519
1529
  * by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
@@ -1522,7 +1532,6 @@ function Operations({ name, nodes }) {
1522
1532
  */
1523
1533
  const operationsGenerator = (0, _kubb_core.defineGenerator)({
1524
1534
  name: "client",
1525
- renderer: _kubb_renderer_jsx.jsxRendererSync,
1526
1535
  operations(nodes, ctx) {
1527
1536
  const { config, resolver, root } = ctx;
1528
1537
  const { output, group } = ctx.options;
@@ -1535,7 +1544,15 @@ const operationsGenerator = (0, _kubb_core.defineGenerator)({
1535
1544
  output,
1536
1545
  group: group ?? void 0
1537
1546
  });
1538
- 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({
1539
1556
  baseName: file.baseName,
1540
1557
  path: file.path,
1541
1558
  meta: file.meta,
@@ -1555,11 +1572,17 @@ const operationsGenerator = (0, _kubb_core.defineGenerator)({
1555
1572
  baseName: file.baseName
1556
1573
  }
1557
1574
  }),
1558
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Operations, {
1575
+ sources: [_kubb_core.ast.createSource({
1559
1576
  name,
1560
- nodes: nodes.filter(_kubb_core.ast.isHttpOperationNode)
1561
- })
1562
- });
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
+ })];
1563
1586
  }
1564
1587
  });
1565
1588
  //#endregion
@@ -1567,13 +1590,16 @@ const operationsGenerator = (0, _kubb_core.defineGenerator)({
1567
1590
  const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
1568
1591
  function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
1569
1592
  if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
1570
- const path = new URLPath(node.path, { casing: paramsCasing });
1571
1593
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
1572
1594
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
1573
1595
  const { header: headerParams } = getOperationParameters(node);
1574
1596
  const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
1575
1597
  const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
1576
- const generics = buildGenerics(node, tsResolver);
1598
+ const generics = buildGenerics(node, tsResolver, {
1599
+ dataReturnType,
1600
+ zodResolver,
1601
+ parser
1602
+ });
1577
1603
  const paramsNode = buildClientParamsNode({
1578
1604
  paramsType,
1579
1605
  paramsCasing,
@@ -1583,17 +1609,20 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
1583
1609
  isConfigurable: true
1584
1610
  });
1585
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;
1586
1614
  const clientParams = buildClassClientParams({
1587
1615
  node,
1588
- path,
1616
+ url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
1589
1617
  baseURL,
1590
1618
  tsResolver,
1591
1619
  isFormData,
1592
1620
  isMultipleContentTypes,
1593
1621
  hasFormData,
1594
- headers
1622
+ headers,
1623
+ zodQueryParamsName
1595
1624
  });
1596
- const jsdoc = buildJSDoc(buildOperationComments(node, {
1625
+ const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
1597
1626
  link: "urlPath",
1598
1627
  linkPosition: "beforeDeprecated",
1599
1628
  splitLines: true
@@ -1603,17 +1632,24 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
1603
1632
  node,
1604
1633
  zodResolver
1605
1634
  });
1635
+ const queryParamsLine = buildQueryParamsLine({
1636
+ parser,
1637
+ node,
1638
+ zodResolver
1639
+ });
1606
1640
  const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
1607
1641
  const returnStatement = buildReturnStatement({
1608
1642
  dataReturnType,
1609
1643
  parser,
1610
1644
  node,
1611
- zodResolver
1645
+ zodResolver,
1646
+ tsResolver
1612
1647
  });
1613
1648
  return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
1614
- `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
1649
+ `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
1615
1650
  "",
1616
1651
  requestDataLine,
1652
+ queryParamsLine,
1617
1653
  formDataLine,
1618
1654
  `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
1619
1655
  returnStatement
@@ -1644,8 +1680,13 @@ function StaticClassClient({ name, isExportable = true, isIndexable = true, oper
1644
1680
  function resolveTypeImportNames(node, tsResolver) {
1645
1681
  return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
1646
1682
  }
1647
- function resolveZodImportNames(node, zodResolver) {
1648
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((n) => Boolean(n));
1683
+ function resolveZodImportNames(node, zodResolver, parser) {
1684
+ const { query: queryParams } = getOperationParameters(node);
1685
+ return [
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
1689
+ ].filter((n) => Boolean(n));
1649
1690
  }
1650
1691
  /**
1651
1692
  * Built-in `operations` generator for `@kubb/plugin-client` when
@@ -1655,7 +1696,7 @@ function resolveZodImportNames(node, zodResolver) {
1655
1696
  */
1656
1697
  const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1657
1698
  name: "staticClassClient",
1658
- renderer: _kubb_renderer_jsx.jsxRendererSync,
1699
+ renderer: _kubb_renderer_jsx.jsxRenderer,
1659
1700
  operations(nodes, ctx) {
1660
1701
  const { config, driver, resolver, root } = ctx;
1661
1702
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
@@ -1664,7 +1705,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1664
1705
  if (!pluginTs) return null;
1665
1706
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1666
1707
  const tsPluginOptions = pluginTs.options;
1667
- const pluginZod = parser === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1708
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1668
1709
  const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1669
1710
  function buildOperationData(node) {
1670
1711
  const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
@@ -1689,7 +1730,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1689
1730
  const controllers = nodes.reduce((acc, operationNode) => {
1690
1731
  if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
1691
1732
  const tag = operationNode.tags[0];
1692
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveGroupName("Client");
1733
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
1693
1734
  if (!tag && !group) {
1694
1735
  const name = resolver.resolveClassName("ApiClient");
1695
1736
  const file = resolver.resolveFile({
@@ -1754,7 +1795,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1754
1795
  const zodFilesByPath = /* @__PURE__ */ new Map();
1755
1796
  ops.forEach((op) => {
1756
1797
  if (!op.zodFile || !zodResolver) return;
1757
- const names = resolveZodImportNames(op.node, zodResolver);
1798
+ const names = resolveZodImportNames(op.node, zodResolver, parser);
1758
1799
  if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1759
1800
  const imports = zodImportsByFile.get(op.zodFile.path);
1760
1801
  names.forEach((n) => {
@@ -1769,7 +1810,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1769
1810
  }
1770
1811
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
1771
1812
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1772
- const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
1813
+ const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
1773
1814
  zodImportsByFile: /* @__PURE__ */ new Map(),
1774
1815
  zodFilesByPath: /* @__PURE__ */ new Map()
1775
1816
  };
@@ -1840,6 +1881,11 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1840
1881
  root: file.path,
1841
1882
  path: node_path.default.resolve(root, ".kubb/config.ts")
1842
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
+ }),
1843
1889
  Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1844
1890
  const typeFile = typeFilesByPath.get(filePath);
1845
1891
  if (!typeFile) return null;
@@ -1852,7 +1898,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1852
1898
  isTypeOnly: true
1853
1899
  }, filePath);
1854
1900
  }),
1855
- parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1901
+ isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1856
1902
  const zodFile = zodFilesByPath.get(filePath);
1857
1903
  if (!zodFile) return null;
1858
1904
  const importNames = Array.from(importSet).filter(Boolean);
@@ -1891,6 +1937,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1891
1937
  *
1892
1938
  * resolverClient.default('list pets', 'function') // 'listPets'
1893
1939
  * resolverClient.resolveClassName('pet') // 'Pet'
1940
+ * resolverClient.resolveGroupName('pet') // 'PetClient'
1894
1941
  * resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
1895
1942
  * ```
1896
1943
  */
@@ -1898,8 +1945,8 @@ const resolverClient = (0, _kubb_core.defineResolver)(() => ({
1898
1945
  name: "default",
1899
1946
  pluginName: "plugin-client",
1900
1947
  default(name, type) {
1901
- const resolved = camelCase(name, { isFile: type === "file" });
1902
- return type === "file" ? resolved : ensureValidVarName(resolved);
1948
+ if (type === "file") return toFilePath(name);
1949
+ return ensureValidVarName(camelCase(name));
1903
1950
  },
1904
1951
  resolveName(name) {
1905
1952
  return this.default(name, "function");
@@ -1911,7 +1958,7 @@ const resolverClient = (0, _kubb_core.defineResolver)(() => ({
1911
1958
  return ensureValidVarName(pascalCase(name));
1912
1959
  },
1913
1960
  resolveGroupName(name) {
1914
- return ensureValidVarName(pascalCase(name));
1961
+ return ensureValidVarName(pascalCase(`${name} Client`));
1915
1962
  },
1916
1963
  resolveClientPropertyName(name) {
1917
1964
  return ensureValidVarName(camelCase(name));
@@ -1956,7 +2003,7 @@ const pluginClientName = "plugin-client";
1956
2003
  const pluginClient = (0, _kubb_core.definePlugin)((options) => {
1957
2004
  const { output = {
1958
2005
  path: "clients",
1959
- barrelType: "named"
2006
+ barrel: { type: "named" }
1960
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;
1961
2008
  const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
1962
2009
  const selectedGenerators = options.generators ?? [
@@ -1964,11 +2011,11 @@ const pluginClient = (0, _kubb_core.definePlugin)((options) => {
1964
2011
  group && clientType === "function" ? groupedClientGenerator : null,
1965
2012
  operations ? operationsGenerator : null
1966
2013
  ].filter((x) => Boolean(x));
1967
- const groupConfig = createGroupConfig(group, { suffix: "Controller" });
2014
+ const groupConfig = createGroupConfig(group);
1968
2015
  return {
1969
2016
  name: pluginClientName,
1970
2017
  options,
1971
- dependencies: [_kubb_plugin_ts.pluginTsName, parser === "zod" ? _kubb_plugin_zod.pluginZodName : null].filter((dependency) => Boolean(dependency)),
2018
+ dependencies: [_kubb_plugin_ts.pluginTsName, isParserEnabled(parser) ? _kubb_plugin_zod.pluginZodName : null].filter((dependency) => Boolean(dependency)),
1972
2019
  hooks: { "kubb:plugin:setup"(ctx) {
1973
2020
  const resolver = userResolver ? {
1974
2021
  ...resolverClient,
@@ -2031,9 +2078,13 @@ exports.classClientGenerator = classClientGenerator;
2031
2078
  exports.clientGenerator = clientGenerator;
2032
2079
  exports.default = pluginClient;
2033
2080
  exports.groupedClientGenerator = groupedClientGenerator;
2081
+ exports.isParserEnabled = isParserEnabled;
2034
2082
  exports.operationsGenerator = operationsGenerator;
2035
2083
  exports.pluginClient = pluginClient;
2036
2084
  exports.pluginClientName = pluginClientName;
2085
+ exports.resolveQueryParamsParser = resolveQueryParamsParser;
2086
+ exports.resolveRequestParser = resolveRequestParser;
2087
+ exports.resolveResponseParser = resolveResponseParser;
2037
2088
  exports.resolverClient = resolverClient;
2038
2089
  exports.staticClassClientGenerator = staticClassClientGenerator;
2039
2090