@kubb/plugin-client 5.0.0-beta.42 → 5.0.0-beta.64

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 (39) hide show
  1. package/dist/clients/axios.cjs.map +1 -1
  2. package/dist/clients/axios.js.map +1 -1
  3. package/dist/clients/fetch.cjs.map +1 -1
  4. package/dist/clients/fetch.js.map +1 -1
  5. package/dist/index.cjs +439 -383
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.ts +60 -25
  8. package/dist/index.js +435 -383
  9. package/dist/index.js.map +1 -1
  10. package/dist/templates/clients/axios.source.cjs.map +1 -1
  11. package/dist/templates/clients/axios.source.js.map +1 -1
  12. package/dist/templates/clients/fetch.source.cjs.map +1 -1
  13. package/dist/templates/clients/fetch.source.js.map +1 -1
  14. package/dist/templates/config.source.cjs.map +1 -1
  15. package/dist/templates/config.source.js.map +1 -1
  16. package/package.json +10 -18
  17. package/extension.yaml +0 -1267
  18. package/src/clients/axios.ts +0 -113
  19. package/src/clients/fetch.ts +0 -201
  20. package/src/components/ClassClient.tsx +0 -137
  21. package/src/components/Client.tsx +0 -273
  22. package/src/components/Operations.tsx +0 -29
  23. package/src/components/StaticClassClient.tsx +0 -129
  24. package/src/components/Url.tsx +0 -91
  25. package/src/components/WrapperClient.tsx +0 -33
  26. package/src/functionParams.ts +0 -118
  27. package/src/generators/classClientGenerator.tsx +0 -253
  28. package/src/generators/clientGenerator.tsx +0 -127
  29. package/src/generators/groupedClientGenerator.tsx +0 -82
  30. package/src/generators/operationsGenerator.tsx +0 -34
  31. package/src/generators/staticClassClientGenerator.tsx +0 -232
  32. package/src/index.ts +0 -9
  33. package/src/plugin.ts +0 -160
  34. package/src/resolvers/resolverClient.ts +0 -45
  35. package/src/templates/clients/axios.source.ts +0 -4
  36. package/src/templates/clients/fetch.source.ts +0 -4
  37. package/src/templates/config.source.ts +0 -4
  38. package/src/types.ts +0 -268
  39. package/src/utils.ts +0 -159
package/dist/index.js CHANGED
@@ -4,8 +4,9 @@ import { source as source$1 } from "./templates/clients/fetch.source.js";
4
4
  import { source as source$2 } from "./templates/config.source.js";
5
5
  import path from "node:path";
6
6
  import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
7
+ import { buildJSDoc, caseParams, createOperationParams, stringify } from "@kubb/ast/utils";
7
8
  import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
8
- import { Const, File, Function, jsxRendererSync } from "@kubb/renderer-jsx";
9
+ import { Const, File, Function, jsxRenderer } from "@kubb/renderer-jsx";
9
10
  import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
10
11
  import { pluginZodName } from "@kubb/plugin-zod";
11
12
  //#region ../../internals/utils/src/casing.ts
@@ -19,68 +20,57 @@ import { pluginZodName } from "@kubb/plugin-zod";
19
20
  function toCamelOrPascal(text, pascal) {
20
21
  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) => {
21
22
  if (word.length > 1 && word === word.toUpperCase()) return word;
22
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
23
- return word.charAt(0).toUpperCase() + word.slice(1);
23
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
24
24
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
25
25
  }
26
26
  /**
27
- * Splits `text` on `.` and applies `transformPart` to each segment.
28
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
29
- * Segments are joined with `/` to form a file path.
30
- *
31
- * Only splits on dots followed by a letter so that version numbers
32
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
33
- */
34
- function applyToFileParts(text, transformPart) {
35
- const parts = text.split(/\.(?=[a-zA-Z])/);
36
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
37
- }
38
- /**
39
27
  * Converts `text` to camelCase.
40
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
41
28
  *
42
- * @example
43
- * camelCase('hello-world') // 'helloWorld'
44
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
29
+ * @example Word boundaries
30
+ * `camelCase('hello-world') // 'helloWorld'`
31
+ *
32
+ * @example With a prefix
33
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
45
34
  */
46
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
47
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
48
- prefix,
49
- suffix
50
- } : {}));
35
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
51
36
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
52
37
  }
53
38
  /**
54
39
  * Converts `text` to PascalCase.
55
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
56
40
  *
57
- * @example
58
- * pascalCase('hello-world') // 'HelloWorld'
59
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
41
+ * @example Word boundaries
42
+ * `pascalCase('hello-world') // 'HelloWorld'`
43
+ *
44
+ * @example With a suffix
45
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
60
46
  */
61
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
62
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
63
- prefix,
64
- suffix
65
- }) : camelCase(part));
47
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
66
48
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
67
49
  }
68
50
  //#endregion
69
- //#region ../../internals/utils/src/jsdoc.ts
51
+ //#region ../../internals/utils/src/fs.ts
70
52
  /**
71
- * Builds a JSDoc comment block from an array of lines.
72
- * Returns `fallback` when `comments` is empty so callers always get a usable string.
53
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
54
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
55
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
73
56
  *
74
- * @example
75
- * ```ts
76
- * buildJSDoc(['@type string', '@example hello'])
77
- * // '/**\n * @type string\n * @example hello\n *\/\n '
78
- * ```
57
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
58
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
59
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
60
+ * absolute path, letting generated files escape the configured output directory.
61
+ *
62
+ * @example Nested path from a dotted name
63
+ * `toFilePath('pet.petId') // 'pet/petId'`
64
+ *
65
+ * @example PascalCase the final segment
66
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
67
+ *
68
+ * @example Suffix applied to the final segment only
69
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
79
70
  */
80
- function buildJSDoc(comments, options = {}) {
81
- const { indent = " * ", suffix = "\n ", fallback = " " } = options;
82
- if (comments.length === 0) return fallback;
83
- return `/**\n${comments.map((c) => `${indent}${c}`).join("\n")}\n */${suffix}`;
71
+ function toFilePath(name, caseLast = camelCase) {
72
+ const parts = name.split(/\.(?=[a-zA-Z])/);
73
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
84
74
  }
85
75
  //#endregion
86
76
  //#region ../../internals/utils/src/reserved.ts
@@ -206,99 +196,83 @@ function ensureValidVarName(name) {
206
196
  return `_${name}`;
207
197
  }
208
198
  //#endregion
209
- //#region ../../internals/utils/src/urlPath.ts
199
+ //#region ../../internals/utils/src/url.ts
200
+ function transformParam(raw, casing) {
201
+ const param = isValidVarName(raw) ? raw : camelCase(raw);
202
+ return casing === "camelcase" ? camelCase(param) : param;
203
+ }
204
+ function toParamsObject(path, { replacer, casing } = {}) {
205
+ const params = {};
206
+ for (const match of path.matchAll(/\{([^}]+)\}/g)) {
207
+ const param = transformParam(match[1], casing);
208
+ const key = replacer ? replacer(param) : param;
209
+ params[key] = key;
210
+ }
211
+ return Object.keys(params).length > 0 ? params : null;
212
+ }
210
213
  /**
211
- * Parses and transforms an OpenAPI/Swagger path string into various URL formats.
212
- *
213
- * @example
214
- * const p = new URLPath('/pet/{petId}')
215
- * p.URL // '/pet/:petId'
216
- * p.template // '`/pet/${petId}`'
214
+ * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
217
215
  */
218
- var URLPath = class {
219
- /**
220
- * The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
221
- */
222
- path;
223
- #options;
224
- constructor(path, options = {}) {
225
- this.path = path;
226
- this.#options = options;
216
+ var Url$1 = class Url$1 {
217
+ static {
218
+ __name(this, "Url");
227
219
  }
228
- /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
220
+ /**
221
+ * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
229
222
  *
230
223
  * @example
231
- * ```ts
232
- * new URLPath('/pet/{petId}').URL // '/pet/:petId'
233
- * ```
224
+ * Url.canParse('https://petstore.swagger.io/v2') // true
225
+ * Url.canParse('/pet/{petId}') // false
234
226
  */
235
- get URL() {
236
- return this.toURLPath();
227
+ static canParse(url, base) {
228
+ return URL.canParse(url, base);
237
229
  }
238
- /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
230
+ /**
231
+ * Converts an OpenAPI/Swagger path to Express-style colon syntax.
239
232
  *
240
233
  * @example
241
- * ```ts
242
- * new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
243
- * new URLPath('/pet/{petId}').isURL // false
244
- * ```
234
+ * Url.toPath('/pet/{petId}') // '/pet/:petId'
245
235
  */
246
- get isURL() {
247
- try {
248
- return !!new URL(this.path).href;
249
- } catch {
250
- return false;
251
- }
236
+ static toPath(path) {
237
+ return path.replace(/\{([^}]+)\}/g, ":$1");
252
238
  }
253
239
  /**
254
- * Converts the OpenAPI path to a TypeScript template literal string.
240
+ * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
241
+ * `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
242
+ * and `casing` controls parameter identifier casing.
255
243
  *
256
244
  * @example
257
- * new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
258
- * new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
259
- */
260
- get template() {
261
- return this.toTemplateString();
262
- }
263
- /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
245
+ * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
264
246
  *
265
247
  * @example
266
- * ```ts
267
- * new URLPath('/pet/{petId}').object
268
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
269
- * ```
248
+ * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
270
249
  */
271
- get object() {
272
- return this.toObject();
250
+ static toTemplateString(path, { prefix, replacer, casing } = {}) {
251
+ const result = path.split(/\{([^}]+)\}/).map((part, i) => {
252
+ if (i % 2 === 0) return part;
253
+ const param = transformParam(part, casing);
254
+ return `\${${replacer ? replacer(param) : param}}`;
255
+ }).join("");
256
+ return `\`${prefix ?? ""}${result}\``;
273
257
  }
274
- /** Returns a map of path parameter names, or `null` when the path has no parameters.
258
+ /**
259
+ * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
260
+ * expression when `stringify` is set.
275
261
  *
276
262
  * @example
277
- * ```ts
278
- * new URLPath('/pet/{petId}').params // { petId: 'petId' }
279
- * new URLPath('/pet').params // null
280
- * ```
281
- */
282
- get params() {
283
- return this.toParamsObject();
284
- }
285
- #transformParam(raw) {
286
- const param = isValidVarName(raw) ? raw : camelCase(raw);
287
- return this.#options.casing === "camelcase" ? camelCase(param) : param;
288
- }
289
- /**
290
- * Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
263
+ * Url.toObject('/pet/{petId}')
264
+ * // { url: '/pet/:petId', params: { petId: 'petId' } }
291
265
  */
292
- #eachParam(fn) {
293
- for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
294
- const raw = match[1];
295
- fn(raw, this.#transformParam(raw));
296
- }
297
- }
298
- toObject({ type = "path", replacer, stringify } = {}) {
266
+ static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
299
267
  const object = {
300
- url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
301
- params: this.toParamsObject()
268
+ url: type === "path" ? Url$1.toPath(path) : Url$1.toTemplateString(path, {
269
+ replacer,
270
+ casing
271
+ }),
272
+ params: toParamsObject(path, {
273
+ replacer,
274
+ casing
275
+ })
302
276
  };
303
277
  if (stringify) {
304
278
  if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
@@ -307,50 +281,6 @@ var URLPath = class {
307
281
  }
308
282
  return object;
309
283
  }
310
- /**
311
- * Converts the OpenAPI path to a TypeScript template literal string.
312
- * An optional `replacer` can transform each extracted parameter name before interpolation.
313
- *
314
- * @example
315
- * new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
316
- */
317
- toTemplateString({ prefix, replacer } = {}) {
318
- const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
319
- if (i % 2 === 0) return part;
320
- const param = this.#transformParam(part);
321
- return `\${${replacer ? replacer(param) : param}}`;
322
- }).join("");
323
- return `\`${prefix ?? ""}${result}\``;
324
- }
325
- /**
326
- * Extracts all `{param}` segments from the path and returns them as a key-value map.
327
- * An optional `replacer` transforms each parameter name in both key and value positions.
328
- * Returns `undefined` when no path parameters are found.
329
- *
330
- * @example
331
- * ```ts
332
- * new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
333
- * // { petId: 'petId', tagId: 'tagId' }
334
- * ```
335
- */
336
- toParamsObject(replacer) {
337
- const params = {};
338
- this.#eachParam((_raw, param) => {
339
- const key = replacer ? replacer(param) : param;
340
- params[key] = key;
341
- });
342
- return Object.keys(params).length > 0 ? params : null;
343
- }
344
- /** Converts the OpenAPI path to Express-style colon syntax.
345
- *
346
- * @example
347
- * ```ts
348
- * new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
349
- * ```
350
- */
351
- toURLPath() {
352
- return this.path.replace(/\{([^}]+)\}/g, ":$1");
353
- }
354
284
  };
355
285
  //#endregion
356
286
  //#region ../../internals/shared/src/operation.ts
@@ -376,7 +306,7 @@ function operationFileEntry(node, name, extname = ".ts") {
376
306
  function getOperationLink(node, link) {
377
307
  if (!link) return null;
378
308
  if (typeof link === "function") return link(node) ?? null;
379
- if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : null;
309
+ if (link === "urlPath") return node.path ? `{@link ${Url$1.toPath(node.path)}}` : null;
380
310
  return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
381
311
  }
382
312
  function getContentTypeInfo(node) {
@@ -429,7 +359,7 @@ function buildOperationComments(node, options = {}) {
429
359
  return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
430
360
  }
431
361
  function getOperationParameters(node, options = {}) {
432
- const params = ast.caseParams(node.parameters, options.paramsCasing);
362
+ const params = caseParams(node.parameters, options.paramsCasing);
433
363
  return {
434
364
  path: params.filter((param) => param.in === "path"),
435
365
  query: params.filter((param) => param.in === "query"),
@@ -467,6 +397,19 @@ function resolveSuccessNames(node, resolver) {
467
397
  function resolveStatusCodeNames(node, resolver) {
468
398
  return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
469
399
  }
400
+ /**
401
+ * Builds the discriminated union type string for `dataReturnType: 'full'` return shapes.
402
+ * Each member is `{ status: N; data: StatusNType; statusText: string }`.
403
+ */
404
+ function buildStatusUnionType(node, resolver) {
405
+ const members = node.responses.map((r) => {
406
+ const typeName = resolver.resolveResponseStatusName(node, r.statusCode);
407
+ const statusCode = Number.parseInt(r.statusCode, 10);
408
+ return `{ status: ${Number.isNaN(statusCode) ? "number" : String(statusCode)}; data: ${typeName}; statusText: string }`;
409
+ });
410
+ if (members.length === 1) return members[0];
411
+ return `(${members.join(" | ")})`;
412
+ }
470
413
  const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
471
414
  function resolveOperationTypeNames(node, resolver, options = {}) {
472
415
  const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
@@ -506,26 +449,24 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
506
449
  * shared default naming so every plugin groups output consistently:
507
450
  *
508
451
  * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
509
- * - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
452
+ * - other groups use the camelCased group (`pet store``petStore`).
510
453
  *
511
454
  * A user-provided `group.name` always wins over the default namer, so callers stay in
512
455
  * control of their output folders. Returns `null` when grouping is disabled, matching the
513
456
  * per-plugin convention.
514
457
  *
515
458
  * @param group - The user-supplied group option, or `undefined` to disable grouping.
516
- * @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
517
459
  *
518
460
  * @example
519
461
  * ```ts
520
- * createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-client, …
521
- * createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
462
+ * createGroupConfig(group) // shared across every plugin
522
463
  * ```
523
464
  */
524
- function createGroupConfig(group, options) {
465
+ function createGroupConfig(group) {
525
466
  if (!group) return null;
526
467
  const defaultName = (ctx) => {
527
468
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
528
- return `${camelCase(ctx.group)}${options.suffix}`;
469
+ return camelCase(ctx.group);
529
470
  };
530
471
  return {
531
472
  ...group,
@@ -551,43 +492,52 @@ const callPrinter = functionPrinter({ mode: "call" });
551
492
  function isGroup(spec) {
552
493
  return "children" in spec;
553
494
  }
554
- function createType(type) {
555
- return type ? ast.createParamsType({
556
- variant: "reference",
557
- name: type
558
- }) : null;
495
+ function groupEntries(group) {
496
+ return Object.entries(group.children).filter(([, child]) => child != null);
497
+ }
498
+ /**
499
+ * Assembles a destructured group parameter from a binding pattern and an optional
500
+ * type literal. Built directly because `createFunctionParameter({ properties })`
501
+ * requires every member to carry a type, while these groups also hold untyped,
502
+ * value-only call entries.
503
+ */
504
+ function createGroupParam(elements, members, default_) {
505
+ return {
506
+ kind: "FunctionParameter",
507
+ name: ast.factory.createObjectBindingPattern({ elements }),
508
+ type: members.length ? ast.factory.createTypeLiteral({ members }) : void 0,
509
+ default: default_,
510
+ optional: false
511
+ };
559
512
  }
560
513
  function createDeclarationLeaf(name, spec) {
561
- if (spec.default !== void 0) return ast.createFunctionParameter({
514
+ if (spec.default !== void 0) return ast.factory.createFunctionParameter({
562
515
  name,
563
- type: createType(spec.type) ?? void 0,
516
+ type: spec.type,
564
517
  default: spec.default,
565
518
  rest: spec.mode === "inlineSpread"
566
519
  });
567
- return ast.createFunctionParameter({
520
+ return ast.factory.createFunctionParameter({
568
521
  name,
569
- type: createType(spec.type) ?? void 0,
522
+ type: spec.type,
570
523
  optional: !!spec.optional,
571
524
  rest: spec.mode === "inlineSpread"
572
525
  });
573
526
  }
574
527
  function createDeclarationParam(name, spec) {
575
- if (isGroup(spec)) return ast.createParameterGroup({
576
- inline: spec.mode === "inlineSpread",
577
- default: spec.default,
578
- properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => createDeclarationLeaf(childName, child))
579
- });
528
+ if (isGroup(spec)) {
529
+ const entries = groupEntries(spec);
530
+ return createGroupParam(entries.map(([childName]) => ({ name: childName })), entries.filter(([, child]) => child.type).map(([childName, child]) => ({
531
+ name: childName,
532
+ type: child.type,
533
+ optional: !!child.optional || child.default !== void 0
534
+ })), spec.default);
535
+ }
580
536
  return createDeclarationLeaf(name, spec);
581
537
  }
582
538
  function createCallParam(name, spec) {
583
- if (isGroup(spec)) return ast.createParameterGroup({
584
- inline: spec.mode === "inlineSpread",
585
- properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => ast.createFunctionParameter({
586
- name: child?.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child?.value ? `${childName}: ${child.value}` : childName,
587
- rest: spec.mode === "inlineSpread" && child?.mode === "inlineSpread"
588
- }))
589
- });
590
- return ast.createFunctionParameter({
539
+ if (isGroup(spec)) return createGroupParam(groupEntries(spec).map(([childName, child]) => ({ name: child.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child.value ? `${childName}: ${child.value}` : childName })), []);
540
+ return ast.factory.createFunctionParameter({
591
541
  name: spec.value ?? name,
592
542
  rest: spec.mode === "inlineSpread"
593
543
  });
@@ -600,23 +550,159 @@ function createFunctionParams(params) {
600
550
  const entries = Object.entries(params).filter(([, spec]) => spec != null);
601
551
  return {
602
552
  toConstructor() {
603
- return declarationPrinter$4.print(ast.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
553
+ return declarationPrinter$4.print(ast.factory.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
604
554
  },
605
555
  toCall() {
606
- return callPrinter.print(ast.createFunctionParameters({ params: entries.map(([name, spec]) => createCallParam(name, spec)) })) ?? "";
556
+ return callPrinter.print(ast.factory.createFunctionParameters({ params: entries.map(([name, spec]) => createCallParam(name, spec)) })) ?? "";
607
557
  }
608
558
  };
609
559
  }
610
560
  //#endregion
561
+ //#region src/utils.ts
562
+ /**
563
+ * Returns `true` when any direction of the parser uses Zod (used for dependency checks).
564
+ */
565
+ function isParserEnabled(parser) {
566
+ if (!parser) return false;
567
+ if (parser === "zod") return true;
568
+ return !!(parser.request || parser.response);
569
+ }
570
+ /**
571
+ * Returns `'zod'` when request body parsing is enabled, `null` otherwise.
572
+ * The string shorthand `'zod'` also enables request body parsing (existing behavior).
573
+ */
574
+ function resolveRequestParser(parser) {
575
+ if (!parser) return null;
576
+ if (parser === "zod") return "zod";
577
+ return parser.request ?? null;
578
+ }
579
+ /**
580
+ * Returns `'zod'` when query-parameters parsing is enabled, `null` otherwise.
581
+ * Only the object form `{ request: 'zod' }` enables query-params parsing.
582
+ * The string shorthand `'zod'` does not, preserving its existing behavior.
583
+ */
584
+ function resolveQueryParamsParser(parser) {
585
+ if (!parser || parser === "zod") return null;
586
+ return parser.request ?? null;
587
+ }
588
+ /**
589
+ * Returns `'zod'` when response-direction parsing is enabled, `null` otherwise.
590
+ * `parser: 'zod'` (string shorthand) maps to response parsing and returns `'zod'`.
591
+ */
592
+ function resolveResponseParser(parser) {
593
+ if (!parser) return null;
594
+ if (parser === "zod") return "zod";
595
+ return parser.response ?? null;
596
+ }
597
+ /**
598
+ * Builds HTTP headers array for a client request.
599
+ * Includes Content-Type (if not default) and spreads header parameters if present.
600
+ */
601
+ function buildHeaders(contentType, hasHeaderParams) {
602
+ return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
603
+ }
604
+ /**
605
+ * Returns the generic type arguments — response, error, and request body — for a generated
606
+ * client call.
607
+ *
608
+ * When `dataReturnType` is `'full'`, the response generic widens to a union of all documented
609
+ * status types. When `parser` is `'zod'` and a request body schema exists, the request type
610
+ * uses `z.output<typeof schema>` to reflect post-transform types (e.g. date coercion).
611
+ */
612
+ function buildGenerics(node, tsResolver, options = {}) {
613
+ const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
614
+ const successNames = resolveSuccessNames(node, tsResolver);
615
+ const responseName = options.dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : tsResolver.resolveResponseName(node) : successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
616
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
617
+ const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
618
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
619
+ const zodRequestName = options.parser === "zod" && options.zodResolver && node.requestBody?.content?.[0]?.schema ? options.zodResolver.resolveDataName?.(node) ?? null : null;
620
+ return [
621
+ responseName,
622
+ TError,
623
+ zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
624
+ ].filter(Boolean);
625
+ }
626
+ /**
627
+ * Builds the parameters object for a class-based client method.
628
+ * Includes URL, method, base URL, headers, and request/response data.
629
+ */
630
+ function buildClassClientParams({ node, url, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers, zodQueryParamsName }) {
631
+ const { query: queryParams } = getOperationParameters(node);
632
+ const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
633
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
634
+ const responseType = getResponseType(node);
635
+ return createFunctionParams({ config: {
636
+ mode: "object",
637
+ children: {
638
+ requestConfig: { mode: "inlineSpread" },
639
+ method: { value: JSON.stringify(ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
640
+ url: { value: url },
641
+ baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
642
+ params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : {} : null,
643
+ data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
644
+ contentType: isMultipleContentTypes ? {} : null,
645
+ responseType: responseType ? { value: JSON.stringify(responseType) } : null,
646
+ headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
647
+ }
648
+ } });
649
+ }
650
+ /**
651
+ * Builds the request data parsing line for client methods.
652
+ * Applies Zod validation when `parser.request === 'zod'`, otherwise assigns data directly.
653
+ */
654
+ function buildRequestDataLine({ parser, node, zodResolver }) {
655
+ const requestParser = resolveRequestParser(parser);
656
+ const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
657
+ if (requestParser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
658
+ if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
659
+ return "";
660
+ }
661
+ /**
662
+ * Builds the query parameters parsing line for client methods.
663
+ * Returns an empty string when no query params exist or query-params parsing is not enabled.
664
+ * Only the object form `parser: { request: 'zod' }` triggers this. `parser: 'zod'` does not.
665
+ */
666
+ function buildQueryParamsLine({ parser, node, zodResolver }) {
667
+ if (resolveQueryParamsParser(parser) !== "zod" || !zodResolver) return "";
668
+ const { query: queryParams } = getOperationParameters(node);
669
+ if (queryParams.length === 0) return "";
670
+ const zodQueryParamsName = zodResolver.resolveQueryParamsName?.(node, queryParams[0]);
671
+ if (!zodQueryParamsName) return "";
672
+ return `const requestParams = ${zodQueryParamsName}.parse(params)`;
673
+ }
674
+ /**
675
+ * Builds the form data conversion line for file upload requests.
676
+ * Returns empty string if not applicable.
677
+ */
678
+ function buildFormDataLine(isFormData, hasRequest) {
679
+ return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
680
+ }
681
+ /**
682
+ * Builds the return statement for a client method.
683
+ * When `dataReturnType` is `'full'`, casts the response to the status-discriminated union type.
684
+ * When `parser.response` is `'zod'`, pipes the response body through the Zod schema before returning.
685
+ */
686
+ function buildReturnStatement({ dataReturnType, parser, node, zodResolver, tsResolver }) {
687
+ const responseParser = resolveResponseParser(parser);
688
+ const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
689
+ if (dataReturnType === "full" && tsResolver) {
690
+ const unionType = buildStatusUnionType(node, tsResolver);
691
+ if (responseParser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)} as ${unionType}`;
692
+ return `return res as ${unionType}`;
693
+ }
694
+ if (dataReturnType === "data" && responseParser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
695
+ return "return res.data";
696
+ }
697
+ //#endregion
611
698
  //#region src/components/Url.tsx
612
699
  const declarationPrinter$3 = functionPrinter({ mode: "declaration" });
613
700
  function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
614
- const urlNode = {
701
+ return createOperationParams({
615
702
  ...node,
616
703
  parameters: node.parameters.filter((p) => p.in === "path"),
617
704
  requestBody: void 0
618
- };
619
- return ast.createOperationParams(urlNode, {
705
+ }, {
620
706
  paramsType: paramsType === "object" ? "object" : "inline",
621
707
  pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
622
708
  paramsCasing,
@@ -625,7 +711,6 @@ function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, ts
625
711
  }
626
712
  function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
627
713
  if (!ast.isHttpOperationNode(node)) return null;
628
- const path = new URLPath(node.path);
629
714
  const paramsNode = buildUrlParamsNode({
630
715
  paramsType,
631
716
  paramsCasing,
@@ -650,7 +735,7 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
650
735
  pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ jsx("br", {}),
651
736
  /* @__PURE__ */ jsx(Const, {
652
737
  name: "res",
653
- children: `{ method: '${node.method.toUpperCase()}', url: ${path.toTemplateString({ prefix: baseURL })} as const }`
738
+ children: `{ method: '${node.method.toUpperCase()}', url: ${Url$1.toTemplateString(node.path, { prefix: baseURL })} as const }`
654
739
  }),
655
740
  /* @__PURE__ */ jsx("br", {}),
656
741
  "return res"
@@ -662,24 +747,20 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
662
747
  //#region src/components/Client.tsx
663
748
  const declarationPrinter$2 = functionPrinter({ mode: "declaration" });
664
749
  function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable }) {
665
- return ast.createOperationParams(node, {
750
+ return createOperationParams(node, {
666
751
  paramsType,
667
752
  pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
668
753
  paramsCasing,
669
754
  resolver: tsResolver,
670
- extraParams: [...isConfigurable ? [ast.createFunctionParameter({
755
+ extraParams: [...isConfigurable ? [ast.factory.createFunctionParameter({
671
756
  name: "config",
672
- type: ast.createParamsType({
673
- variant: "reference",
674
- name: buildRequestConfigType(node, tsResolver)
675
- }),
757
+ type: buildRequestConfigType(node, tsResolver),
676
758
  default: "{}"
677
759
  })] : []]
678
760
  });
679
761
  }
680
762
  function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
681
763
  if (!ast.isHttpOperationNode(node)) return null;
682
- const path = new URLPath(node.path);
683
764
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
684
765
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
685
766
  const responseType = getResponseType(node);
@@ -693,16 +774,22 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
693
774
  const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
694
775
  const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
695
776
  const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
696
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
697
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
777
+ const requestParser = resolveRequestParser(parser);
778
+ const responseParser = resolveResponseParser(parser);
779
+ const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
780
+ const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
781
+ const queryParamsParser = resolveQueryParamsParser(parser);
782
+ const zodQueryParamsName = zodResolver && queryParamsParser === "zod" && originalQueryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, originalQueryParams[0]) : null;
698
783
  const errorNames = node.responses.filter((r) => {
699
784
  return Number.parseInt(r.statusCode, 10) >= 400;
700
785
  }).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
701
786
  const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null].filter(Boolean);
787
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
788
+ const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
702
789
  const generics = [
703
- responseName,
704
- `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
705
- requestName || "unknown"
790
+ dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : responseName : responseName,
791
+ TError,
792
+ parser === "zod" && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
706
793
  ].filter(Boolean);
707
794
  const paramsNode = buildClientParamsNode({
708
795
  paramsType,
@@ -724,22 +811,23 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
724
811
  const clientParams = createFunctionParams({ config: {
725
812
  mode: "object",
726
813
  children: {
727
- method: { value: JSON.stringify(node.method.toUpperCase()) },
728
- url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path.template },
814
+ method: { value: stringify(node.method.toUpperCase()) },
815
+ url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url$1.toTemplateString(node.path) },
729
816
  baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
730
- params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : null,
817
+ params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : queryParamsMapping ? { value: "mappedParams" } : {} : null,
731
818
  data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
732
819
  contentType: isConfigurable && isMultipleContentTypes ? {} : null,
733
- responseType: responseType ? { value: JSON.stringify(responseType) } : null,
820
+ responseType: responseType ? { value: stringify(responseType) } : null,
734
821
  requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
735
822
  headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
736
823
  }
737
824
  } });
825
+ const statusUnionType = dataReturnType === "full" ? buildStatusUnionType(node, tsResolver) : null;
738
826
  const childrenElement = children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
739
- dataReturnType === "full" && parser === "zod" && zodResponseName && `return {...res, data: ${zodResponseName}.parse(res.data)}`,
740
- dataReturnType === "data" && parser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
741
- dataReturnType === "full" && parser !== "zod" && "return res",
742
- dataReturnType === "data" && parser !== "zod" && "return res.data"
827
+ dataReturnType === "full" && responseParser === "zod" && zodResponseName && statusUnionType && `return {...res, data: ${zodResponseName}.parse(res.data)} as ${statusUnionType}`,
828
+ dataReturnType === "full" && responseParser !== "zod" && statusUnionType && `return res as ${statusUnionType}`,
829
+ dataReturnType === "data" && responseParser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
830
+ dataReturnType === "data" && responseParser !== "zod" && "return res.data"
743
831
  ] });
744
832
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx(File.Source, {
745
833
  name,
@@ -757,23 +845,14 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
757
845
  }) },
758
846
  returnType,
759
847
  children: [
760
- isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = config` : "",
761
- /* @__PURE__ */ jsx("br", {}),
848
+ isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = config` : "",
762
849
  /* @__PURE__ */ jsx("br", {}),
763
850
  pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
764
- pathParamsMapping && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx("br", {})] }),
765
- queryParamsMapping && queryParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
766
- `const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`,
767
- /* @__PURE__ */ jsx("br", {}),
768
- /* @__PURE__ */ jsx("br", {})
769
- ] }),
770
- headerParamsMapping && headerParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
771
- `const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`,
772
- /* @__PURE__ */ jsx("br", {}),
773
- /* @__PURE__ */ jsx("br", {})
774
- ] }),
775
- parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
776
- /* @__PURE__ */ jsx("br", {}),
851
+ pathParamsMapping && /* @__PURE__ */ jsx("br", {}),
852
+ queryParamsMapping && queryParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [`const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ jsx("br", {})] }),
853
+ headerParamsMapping && headerParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ jsx("br", {})] }),
854
+ requestParser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
855
+ zodQueryParamsName && `const requestParams = ${zodQueryParamsName}.parse(params)`,
777
856
  (isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
778
857
  /* @__PURE__ */ jsx("br", {}),
779
858
  isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
@@ -784,93 +863,20 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
784
863
  })] });
785
864
  }
786
865
  //#endregion
787
- //#region src/utils.ts
788
- /**
789
- * Builds HTTP headers array for a client request.
790
- * Includes Content-Type (if not default) and spreads header parameters if present.
791
- */
792
- function buildHeaders(contentType, hasHeaderParams) {
793
- return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
794
- }
795
- /**
796
- * Builds TypeScript generic parameters for a client method.
797
- * Includes response type, error type, and optional request type.
798
- */
799
- function buildGenerics(node, tsResolver) {
800
- const successNames = resolveSuccessNames(node, tsResolver);
801
- const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
802
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
803
- const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
804
- return [
805
- responseName,
806
- `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
807
- requestName || "unknown"
808
- ].filter(Boolean);
809
- }
810
- /**
811
- * Builds the parameters object for a class-based client method.
812
- * Includes URL, method, base URL, headers, and request/response data.
813
- */
814
- function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers }) {
815
- const { query: queryParams } = getOperationParameters(node);
816
- const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
817
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
818
- const responseType = getResponseType(node);
819
- return createFunctionParams({ config: {
820
- mode: "object",
821
- children: {
822
- requestConfig: { mode: "inlineSpread" },
823
- method: { value: JSON.stringify(ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
824
- url: { value: path.template },
825
- baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
826
- params: queryParamsName ? {} : null,
827
- data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
828
- contentType: isMultipleContentTypes ? {} : null,
829
- responseType: responseType ? { value: JSON.stringify(responseType) } : null,
830
- headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
831
- }
832
- } });
833
- }
834
- /**
835
- * Builds the request data parsing line for client methods.
836
- * Applies Zod validation if configured, otherwise uses data directly.
837
- */
838
- function buildRequestDataLine({ parser, node, zodResolver }) {
839
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
840
- if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
841
- if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
842
- return "";
843
- }
844
- /**
845
- * Builds the form data conversion line for file upload requests.
846
- * Returns empty string if not applicable.
847
- */
848
- function buildFormDataLine(isFormData, hasRequest) {
849
- return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
850
- }
851
- /**
852
- * Builds the return statement for a client method.
853
- * Applies Zod validation to response data if configured, otherwise returns raw response.
854
- */
855
- function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
856
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
857
- if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
858
- if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
859
- if (dataReturnType === "full" && parser !== "zod") return "return res";
860
- return "return res.data";
861
- }
862
- //#endregion
863
866
  //#region src/components/ClassClient.tsx
864
867
  const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
865
868
  function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
866
869
  if (!ast.isHttpOperationNode(node)) return "";
867
- const path = new URLPath(node.path, { casing: paramsCasing });
868
870
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
869
871
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
870
872
  const { header: headerParams } = getOperationParameters(node);
871
873
  const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
872
874
  const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
873
- const generics = buildGenerics(node, tsResolver);
875
+ const generics = buildGenerics(node, tsResolver, {
876
+ dataReturnType,
877
+ zodResolver,
878
+ parser
879
+ });
874
880
  const paramsNode = buildClientParamsNode({
875
881
  paramsType,
876
882
  paramsCasing,
@@ -880,15 +886,18 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
880
886
  isConfigurable: true
881
887
  });
882
888
  const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
889
+ const { query: queryParams } = getOperationParameters(node);
890
+ const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
883
891
  const clientParams = buildClassClientParams({
884
892
  node,
885
- path,
893
+ url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
886
894
  baseURL,
887
895
  tsResolver,
888
896
  isFormData,
889
897
  isMultipleContentTypes,
890
898
  hasFormData,
891
- headers
899
+ headers,
900
+ zodQueryParamsName
892
901
  });
893
902
  const jsdoc = buildJSDoc(buildOperationComments(node, {
894
903
  link: "urlPath",
@@ -900,17 +909,24 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
900
909
  node,
901
910
  zodResolver
902
911
  });
912
+ const queryParamsLine = buildQueryParamsLine({
913
+ parser,
914
+ node,
915
+ zodResolver
916
+ });
903
917
  const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
904
918
  const returnStatement = buildReturnStatement({
905
919
  dataReturnType,
906
920
  parser,
907
921
  node,
908
- zodResolver
922
+ zodResolver,
923
+ tsResolver
909
924
  });
910
925
  return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
911
- `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
926
+ `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
912
927
  "",
913
928
  requestDataLine,
929
+ queryParamsLine,
914
930
  formDataLine,
915
931
  `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
916
932
  returnStatement
@@ -968,8 +984,13 @@ function resolveTypeImportNames$1(node, tsResolver) {
968
984
  return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
969
985
  }
970
986
  __name(resolveTypeImportNames$1, "resolveTypeImportNames");
971
- function resolveZodImportNames$1(node, zodResolver) {
972
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((n) => Boolean(n));
987
+ function resolveZodImportNames$1(node, zodResolver, parser) {
988
+ const { query: queryParams } = getOperationParameters(node);
989
+ return [
990
+ resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
991
+ resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
992
+ resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
993
+ ].filter((n) => Boolean(n));
973
994
  }
974
995
  __name(resolveZodImportNames$1, "resolveZodImportNames");
975
996
  /**
@@ -979,7 +1000,7 @@ __name(resolveZodImportNames$1, "resolveZodImportNames");
979
1000
  */
980
1001
  const classClientGenerator = defineGenerator({
981
1002
  name: "classClient",
982
- renderer: jsxRendererSync,
1003
+ renderer: jsxRenderer,
983
1004
  operations(nodes, ctx) {
984
1005
  const { config, driver, resolver, root } = ctx;
985
1006
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
@@ -988,7 +1009,7 @@ const classClientGenerator = defineGenerator({
988
1009
  if (!pluginTs) return null;
989
1010
  const tsResolver = driver.getResolver(pluginTsName);
990
1011
  const tsPluginOptions = pluginTs.options;
991
- const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
1012
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
992
1013
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
993
1014
  function buildOperationData(node) {
994
1015
  const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
@@ -1013,7 +1034,7 @@ const classClientGenerator = defineGenerator({
1013
1034
  const controllers = nodes.reduce((acc, operationNode) => {
1014
1035
  if (!ast.isHttpOperationNode(operationNode)) return acc;
1015
1036
  const tag = operationNode.tags[0];
1016
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveGroupName("Client");
1037
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
1017
1038
  if (!tag && !group) {
1018
1039
  const name = resolver.resolveClassName("ApiClient");
1019
1040
  const file = resolver.resolveFile({
@@ -1080,7 +1101,7 @@ const classClientGenerator = defineGenerator({
1080
1101
  const zodFilesByPath = /* @__PURE__ */ new Map();
1081
1102
  ops.forEach((op) => {
1082
1103
  if (!op.zodFile || !zodResolver) return;
1083
- const names = resolveZodImportNames$1(op.node, zodResolver);
1104
+ const names = resolveZodImportNames$1(op.node, zodResolver, parser);
1084
1105
  if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1085
1106
  const imports = zodImportsByFile.get(op.zodFile.path);
1086
1107
  names.forEach((n) => {
@@ -1095,7 +1116,7 @@ const classClientGenerator = defineGenerator({
1095
1116
  }
1096
1117
  const files = controllers.map(({ name, file, operations: ops }) => {
1097
1118
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1098
- const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
1119
+ const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
1099
1120
  zodImportsByFile: /* @__PURE__ */ new Map(),
1100
1121
  zodFilesByPath: /* @__PURE__ */ new Map()
1101
1122
  };
@@ -1166,6 +1187,11 @@ const classClientGenerator = defineGenerator({
1166
1187
  root: file.path,
1167
1188
  path: path.resolve(root, ".kubb/config.ts")
1168
1189
  }),
1190
+ parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ jsx(File.Import, {
1191
+ name: ["z"],
1192
+ path: "zod",
1193
+ isTypeOnly: true
1194
+ }),
1169
1195
  Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1170
1196
  const typeFile = typeFilesByPath.get(filePath);
1171
1197
  if (!typeFile) return null;
@@ -1178,7 +1204,7 @@ const classClientGenerator = defineGenerator({
1178
1204
  isTypeOnly: true
1179
1205
  }, filePath);
1180
1206
  }),
1181
- parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1207
+ isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1182
1208
  const zodFile = zodFilesByPath.get(filePath);
1183
1209
  if (!zodFile) return null;
1184
1210
  const importNames = Array.from(importSet).filter(Boolean);
@@ -1269,7 +1295,7 @@ const classClientGenerator = defineGenerator({
1269
1295
  */
1270
1296
  const clientGenerator = defineGenerator({
1271
1297
  name: "client",
1272
- renderer: jsxRendererSync,
1298
+ renderer: jsxRenderer,
1273
1299
  operation(node, ctx) {
1274
1300
  if (!ast.isHttpOperationNode(node)) return null;
1275
1301
  const { config, driver, resolver, root } = ctx;
@@ -1278,10 +1304,16 @@ const clientGenerator = defineGenerator({
1278
1304
  const pluginTs = driver.getPlugin(pluginTsName);
1279
1305
  if (!pluginTs) return null;
1280
1306
  const tsResolver = driver.getResolver(pluginTsName);
1281
- const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
1307
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
1282
1308
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1283
1309
  const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
1284
- const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((name) => Boolean(name)) : [];
1310
+ const { query: queryParams } = getOperationParameters(node);
1311
+ const importedZodNames = zodResolver ? [
1312
+ resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1313
+ resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
1314
+ resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
1315
+ ].filter((name) => Boolean(name)) : [];
1316
+ const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) ?? null : null;
1285
1317
  const meta = {
1286
1318
  name: resolver.resolveName(node.operationId),
1287
1319
  urlName: resolver.resolveUrlName(node),
@@ -1353,6 +1385,11 @@ const clientGenerator = defineGenerator({
1353
1385
  root: meta.file.path,
1354
1386
  path: path.resolve(root, ".kubb/config.ts")
1355
1387
  }),
1388
+ zodRequestName && /* @__PURE__ */ jsx(File.Import, {
1389
+ name: ["z"],
1390
+ path: "zod",
1391
+ isTypeOnly: true
1392
+ }),
1356
1393
  meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1357
1394
  name: importedZodNames,
1358
1395
  root: meta.file.path,
@@ -1402,7 +1439,7 @@ const clientGenerator = defineGenerator({
1402
1439
  */
1403
1440
  const groupedClientGenerator = defineGenerator({
1404
1441
  name: "groupedClient",
1405
- renderer: jsxRendererSync,
1442
+ renderer: jsxRenderer,
1406
1443
  operations(nodes, ctx) {
1407
1444
  const { config, resolver, root } = ctx;
1408
1445
  const { output, group } = ctx.options;
@@ -1485,29 +1522,7 @@ const groupedClientGenerator = defineGenerator({
1485
1522
  }
1486
1523
  });
1487
1524
  //#endregion
1488
- //#region src/components/Operations.tsx
1489
- function Operations({ name, nodes }) {
1490
- const operationsObject = {};
1491
- nodes.forEach((node) => {
1492
- if (!ast.isHttpOperationNode(node)) return;
1493
- operationsObject[node.operationId] = {
1494
- path: new URLPath(node.path).URL,
1495
- method: node.method.toLowerCase()
1496
- };
1497
- });
1498
- return /* @__PURE__ */ jsx(File.Source, {
1499
- name,
1500
- isExportable: true,
1501
- isIndexable: true,
1502
- children: /* @__PURE__ */ jsx(Const, {
1503
- name,
1504
- export: true,
1505
- children: JSON.stringify(operationsObject, void 0, 2)
1506
- })
1507
- });
1508
- }
1509
- //#endregion
1510
- //#region src/generators/operationsGenerator.tsx
1525
+ //#region src/generators/operationsGenerator.ts
1511
1526
  /**
1512
1527
  * Generates an `operations.ts` file that re-exports every operation grouped
1513
1528
  * by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
@@ -1516,7 +1531,6 @@ function Operations({ name, nodes }) {
1516
1531
  */
1517
1532
  const operationsGenerator = defineGenerator({
1518
1533
  name: "client",
1519
- renderer: jsxRendererSync,
1520
1534
  operations(nodes, ctx) {
1521
1535
  const { config, resolver, root } = ctx;
1522
1536
  const { output, group } = ctx.options;
@@ -1529,7 +1543,15 @@ const operationsGenerator = defineGenerator({
1529
1543
  output,
1530
1544
  group: group ?? void 0
1531
1545
  });
1532
- return /* @__PURE__ */ jsx(File, {
1546
+ const operationsObject = {};
1547
+ for (const node of nodes) {
1548
+ if (!ast.isHttpOperationNode(node)) continue;
1549
+ operationsObject[node.operationId] = {
1550
+ path: Url$1.toPath(node.path),
1551
+ method: node.method.toLowerCase()
1552
+ };
1553
+ }
1554
+ return [ast.factory.createFile({
1533
1555
  baseName: file.baseName,
1534
1556
  path: file.path,
1535
1557
  meta: file.meta,
@@ -1549,11 +1571,17 @@ const operationsGenerator = defineGenerator({
1549
1571
  baseName: file.baseName
1550
1572
  }
1551
1573
  }),
1552
- children: /* @__PURE__ */ jsx(Operations, {
1574
+ sources: [ast.factory.createSource({
1553
1575
  name,
1554
- nodes: nodes.filter(ast.isHttpOperationNode)
1555
- })
1556
- });
1576
+ isExportable: true,
1577
+ isIndexable: true,
1578
+ nodes: [ast.factory.createConst({
1579
+ name,
1580
+ export: true,
1581
+ nodes: [ast.factory.createText(JSON.stringify(operationsObject, void 0, 2))]
1582
+ })]
1583
+ })]
1584
+ })];
1557
1585
  }
1558
1586
  });
1559
1587
  //#endregion
@@ -1561,13 +1589,16 @@ const operationsGenerator = defineGenerator({
1561
1589
  const declarationPrinter = functionPrinter({ mode: "declaration" });
1562
1590
  function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
1563
1591
  if (!ast.isHttpOperationNode(node)) return "";
1564
- const path = new URLPath(node.path, { casing: paramsCasing });
1565
1592
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
1566
1593
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
1567
1594
  const { header: headerParams } = getOperationParameters(node);
1568
1595
  const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
1569
1596
  const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
1570
- const generics = buildGenerics(node, tsResolver);
1597
+ const generics = buildGenerics(node, tsResolver, {
1598
+ dataReturnType,
1599
+ zodResolver,
1600
+ parser
1601
+ });
1571
1602
  const paramsNode = buildClientParamsNode({
1572
1603
  paramsType,
1573
1604
  paramsCasing,
@@ -1577,15 +1608,18 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
1577
1608
  isConfigurable: true
1578
1609
  });
1579
1610
  const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1611
+ const { query: queryParams } = getOperationParameters(node);
1612
+ const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
1580
1613
  const clientParams = buildClassClientParams({
1581
1614
  node,
1582
- path,
1615
+ url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
1583
1616
  baseURL,
1584
1617
  tsResolver,
1585
1618
  isFormData,
1586
1619
  isMultipleContentTypes,
1587
1620
  hasFormData,
1588
- headers
1621
+ headers,
1622
+ zodQueryParamsName
1589
1623
  });
1590
1624
  const jsdoc = buildJSDoc(buildOperationComments(node, {
1591
1625
  link: "urlPath",
@@ -1597,17 +1631,24 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
1597
1631
  node,
1598
1632
  zodResolver
1599
1633
  });
1634
+ const queryParamsLine = buildQueryParamsLine({
1635
+ parser,
1636
+ node,
1637
+ zodResolver
1638
+ });
1600
1639
  const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
1601
1640
  const returnStatement = buildReturnStatement({
1602
1641
  dataReturnType,
1603
1642
  parser,
1604
1643
  node,
1605
- zodResolver
1644
+ zodResolver,
1645
+ tsResolver
1606
1646
  });
1607
1647
  return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
1608
- `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
1648
+ `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
1609
1649
  "",
1610
1650
  requestDataLine,
1651
+ queryParamsLine,
1611
1652
  formDataLine,
1612
1653
  `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
1613
1654
  returnStatement
@@ -1638,8 +1679,13 @@ function StaticClassClient({ name, isExportable = true, isIndexable = true, oper
1638
1679
  function resolveTypeImportNames(node, tsResolver) {
1639
1680
  return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
1640
1681
  }
1641
- function resolveZodImportNames(node, zodResolver) {
1642
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((n) => Boolean(n));
1682
+ function resolveZodImportNames(node, zodResolver, parser) {
1683
+ const { query: queryParams } = getOperationParameters(node);
1684
+ return [
1685
+ resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1686
+ resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
1687
+ resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
1688
+ ].filter((n) => Boolean(n));
1643
1689
  }
1644
1690
  /**
1645
1691
  * Built-in `operations` generator for `@kubb/plugin-client` when
@@ -1649,7 +1695,7 @@ function resolveZodImportNames(node, zodResolver) {
1649
1695
  */
1650
1696
  const staticClassClientGenerator = defineGenerator({
1651
1697
  name: "staticClassClient",
1652
- renderer: jsxRendererSync,
1698
+ renderer: jsxRenderer,
1653
1699
  operations(nodes, ctx) {
1654
1700
  const { config, driver, resolver, root } = ctx;
1655
1701
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
@@ -1658,7 +1704,7 @@ const staticClassClientGenerator = defineGenerator({
1658
1704
  if (!pluginTs) return null;
1659
1705
  const tsResolver = driver.getResolver(pluginTsName);
1660
1706
  const tsPluginOptions = pluginTs.options;
1661
- const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
1707
+ const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
1662
1708
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1663
1709
  function buildOperationData(node) {
1664
1710
  const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
@@ -1683,7 +1729,7 @@ const staticClassClientGenerator = defineGenerator({
1683
1729
  const controllers = nodes.reduce((acc, operationNode) => {
1684
1730
  if (!ast.isHttpOperationNode(operationNode)) return acc;
1685
1731
  const tag = operationNode.tags[0];
1686
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveGroupName("Client");
1732
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
1687
1733
  if (!tag && !group) {
1688
1734
  const name = resolver.resolveClassName("ApiClient");
1689
1735
  const file = resolver.resolveFile({
@@ -1748,7 +1794,7 @@ const staticClassClientGenerator = defineGenerator({
1748
1794
  const zodFilesByPath = /* @__PURE__ */ new Map();
1749
1795
  ops.forEach((op) => {
1750
1796
  if (!op.zodFile || !zodResolver) return;
1751
- const names = resolveZodImportNames(op.node, zodResolver);
1797
+ const names = resolveZodImportNames(op.node, zodResolver, parser);
1752
1798
  if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1753
1799
  const imports = zodImportsByFile.get(op.zodFile.path);
1754
1800
  names.forEach((n) => {
@@ -1763,7 +1809,7 @@ const staticClassClientGenerator = defineGenerator({
1763
1809
  }
1764
1810
  return /* @__PURE__ */ jsx(Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
1765
1811
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1766
- const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
1812
+ const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
1767
1813
  zodImportsByFile: /* @__PURE__ */ new Map(),
1768
1814
  zodFilesByPath: /* @__PURE__ */ new Map()
1769
1815
  };
@@ -1834,6 +1880,11 @@ const staticClassClientGenerator = defineGenerator({
1834
1880
  root: file.path,
1835
1881
  path: path.resolve(root, ".kubb/config.ts")
1836
1882
  }),
1883
+ parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ jsx(File.Import, {
1884
+ name: ["z"],
1885
+ path: "zod",
1886
+ isTypeOnly: true
1887
+ }),
1837
1888
  Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1838
1889
  const typeFile = typeFilesByPath.get(filePath);
1839
1890
  if (!typeFile) return null;
@@ -1846,7 +1897,7 @@ const staticClassClientGenerator = defineGenerator({
1846
1897
  isTypeOnly: true
1847
1898
  }, filePath);
1848
1899
  }),
1849
- parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1900
+ isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1850
1901
  const zodFile = zodFilesByPath.get(filePath);
1851
1902
  if (!zodFile) return null;
1852
1903
  const importNames = Array.from(importSet).filter(Boolean);
@@ -1885,6 +1936,7 @@ const staticClassClientGenerator = defineGenerator({
1885
1936
  *
1886
1937
  * resolverClient.default('list pets', 'function') // 'listPets'
1887
1938
  * resolverClient.resolveClassName('pet') // 'Pet'
1939
+ * resolverClient.resolveGroupName('pet') // 'PetClient'
1888
1940
  * resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
1889
1941
  * ```
1890
1942
  */
@@ -1892,8 +1944,8 @@ const resolverClient = defineResolver(() => ({
1892
1944
  name: "default",
1893
1945
  pluginName: "plugin-client",
1894
1946
  default(name, type) {
1895
- const resolved = camelCase(name, { isFile: type === "file" });
1896
- return type === "file" ? resolved : ensureValidVarName(resolved);
1947
+ if (type === "file") return toFilePath(name);
1948
+ return ensureValidVarName(camelCase(name));
1897
1949
  },
1898
1950
  resolveName(name) {
1899
1951
  return this.default(name, "function");
@@ -1905,7 +1957,7 @@ const resolverClient = defineResolver(() => ({
1905
1957
  return ensureValidVarName(pascalCase(name));
1906
1958
  },
1907
1959
  resolveGroupName(name) {
1908
- return ensureValidVarName(pascalCase(name));
1960
+ return ensureValidVarName(pascalCase(`${name} Client`));
1909
1961
  },
1910
1962
  resolveClientPropertyName(name) {
1911
1963
  return ensureValidVarName(camelCase(name));
@@ -1950,19 +2002,19 @@ const pluginClientName = "plugin-client";
1950
2002
  const pluginClient = definePlugin((options) => {
1951
2003
  const { output = {
1952
2004
  path: "clients",
1953
- barrelType: "named"
1954
- }, 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;
2005
+ barrel: { type: "named" }
2006
+ }, 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, macros: userMacros } = options;
1955
2007
  const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
1956
2008
  const selectedGenerators = options.generators ?? [
1957
2009
  clientType === "staticClass" ? staticClassClientGenerator : clientType === "class" ? classClientGenerator : clientGenerator,
1958
2010
  group && clientType === "function" ? groupedClientGenerator : null,
1959
2011
  operations ? operationsGenerator : null
1960
2012
  ].filter((x) => Boolean(x));
1961
- const groupConfig = createGroupConfig(group, { suffix: "Controller" });
2013
+ const groupConfig = createGroupConfig(group);
1962
2014
  return {
1963
2015
  name: pluginClientName,
1964
2016
  options,
1965
- dependencies: [pluginTsName, parser === "zod" ? pluginZodName : null].filter((dependency) => Boolean(dependency)),
2017
+ dependencies: [pluginTsName, isParserEnabled(parser) ? pluginZodName : null].filter((dependency) => Boolean(dependency)),
1966
2018
  hooks: { "kubb:plugin:setup"(ctx) {
1967
2019
  const resolver = userResolver ? {
1968
2020
  ...resolverClient,
@@ -1989,7 +2041,7 @@ const pluginClient = definePlugin((options) => {
1989
2041
  resolver
1990
2042
  });
1991
2043
  ctx.setResolver(resolver);
1992
- if (userTransformer) ctx.setTransformer(userTransformer);
2044
+ if (userMacros?.length) ctx.setMacros(userMacros);
1993
2045
  for (const gen of selectedGenerators) ctx.addGenerator(gen);
1994
2046
  const root = path.resolve(ctx.config.root, ctx.config.output.path);
1995
2047
  if (!resolvedImportPath?.startsWith(".")) {
@@ -1997,21 +2049,21 @@ const pluginClient = definePlugin((options) => {
1997
2049
  ctx.injectFile({
1998
2050
  baseName: "client.ts",
1999
2051
  path: path.resolve(root, ".kubb/client.ts"),
2000
- sources: [ast.createSource({
2052
+ sources: [ast.factory.createSource({
2001
2053
  name: "client",
2002
- nodes: isInlineSource ? [ast.createText(client === "fetch" ? source$1 : source)] : [],
2054
+ nodes: isInlineSource ? [ast.factory.createText(client === "fetch" ? source$1 : source)] : [],
2003
2055
  isExportable: true,
2004
2056
  isIndexable: true
2005
2057
  })],
2006
- exports: !isInlineSource && resolvedImportPath ? [ast.createExport({ path: resolvedImportPath })] : []
2058
+ exports: !isInlineSource && resolvedImportPath ? [ast.factory.createExport({ path: resolvedImportPath })] : []
2007
2059
  });
2008
2060
  }
2009
2061
  ctx.injectFile({
2010
2062
  baseName: "config.ts",
2011
2063
  path: path.resolve(root, ".kubb/config.ts"),
2012
- sources: [ast.createSource({
2064
+ sources: [ast.factory.createSource({
2013
2065
  name: "config",
2014
- nodes: [ast.createText(source$2)],
2066
+ nodes: [ast.factory.createText(source$2)],
2015
2067
  isExportable: false,
2016
2068
  isIndexable: false
2017
2069
  })]
@@ -2020,6 +2072,6 @@ const pluginClient = definePlugin((options) => {
2020
2072
  };
2021
2073
  });
2022
2074
  //#endregion
2023
- export { Client, classClientGenerator, clientGenerator, pluginClient as default, pluginClient, groupedClientGenerator, operationsGenerator, pluginClientName, resolverClient, staticClassClientGenerator };
2075
+ export { Client, classClientGenerator, clientGenerator, pluginClient as default, pluginClient, groupedClientGenerator, isParserEnabled, operationsGenerator, pluginClientName, resolveQueryParamsParser, resolveRequestParser, resolveResponseParser, resolverClient, staticClassClientGenerator };
2024
2076
 
2025
2077
  //# sourceMappingURL=index.js.map