@kubb/plugin-client 5.0.0-alpha.8 → 5.0.0-beta.3

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 (60) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +4 -4
  3. package/dist/clients/axios.cjs +2 -2
  4. package/dist/clients/axios.cjs.map +1 -1
  5. package/dist/clients/axios.d.ts +4 -4
  6. package/dist/clients/axios.js +1 -1
  7. package/dist/clients/axios.js.map +1 -1
  8. package/dist/clients/fetch.cjs +1 -1
  9. package/dist/clients/fetch.cjs.map +1 -1
  10. package/dist/clients/fetch.d.ts +2 -2
  11. package/dist/clients/fetch.js +1 -1
  12. package/dist/clients/fetch.js.map +1 -1
  13. package/dist/index.cjs +1739 -97
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +324 -4
  16. package/dist/index.js +1725 -95
  17. package/dist/index.js.map +1 -1
  18. package/dist/templates/clients/axios.source.cjs +1 -1
  19. package/dist/templates/clients/axios.source.js +1 -1
  20. package/dist/templates/clients/fetch.source.cjs +1 -1
  21. package/dist/templates/clients/fetch.source.js +1 -1
  22. package/package.json +67 -84
  23. package/src/clients/axios.ts +5 -1
  24. package/src/clients/fetch.ts +5 -1
  25. package/src/components/ClassClient.tsx +45 -142
  26. package/src/components/Client.tsx +90 -129
  27. package/src/components/Operations.tsx +10 -10
  28. package/src/components/StaticClassClient.tsx +44 -138
  29. package/src/components/Url.tsx +38 -48
  30. package/src/components/WrapperClient.tsx +3 -3
  31. package/src/functionParams.ts +118 -0
  32. package/src/generators/classClientGenerator.tsx +148 -171
  33. package/src/generators/clientGenerator.tsx +95 -82
  34. package/src/generators/groupedClientGenerator.tsx +50 -52
  35. package/src/generators/operationsGenerator.tsx +11 -18
  36. package/src/generators/staticClassClientGenerator.tsx +178 -183
  37. package/src/index.ts +9 -2
  38. package/src/plugin.ts +115 -145
  39. package/src/resolvers/resolverClient.ts +22 -0
  40. package/src/types.ts +104 -44
  41. package/src/utils.ts +180 -0
  42. package/templates/clients/axios.ts +5 -2
  43. package/templates/clients/fetch.ts +5 -2
  44. package/dist/StaticClassClient-By-aMAe4.cjs +0 -677
  45. package/dist/StaticClassClient-By-aMAe4.cjs.map +0 -1
  46. package/dist/StaticClassClient-CCn9g9eF.js +0 -636
  47. package/dist/StaticClassClient-CCn9g9eF.js.map +0 -1
  48. package/dist/components.cjs +0 -7
  49. package/dist/components.d.ts +0 -216
  50. package/dist/components.js +0 -2
  51. package/dist/generators-BYUJaeZP.js +0 -723
  52. package/dist/generators-BYUJaeZP.js.map +0 -1
  53. package/dist/generators-DTxD9FDY.cjs +0 -753
  54. package/dist/generators-DTxD9FDY.cjs.map +0 -1
  55. package/dist/generators.cjs +0 -7
  56. package/dist/generators.d.ts +0 -488
  57. package/dist/generators.js +0 -2
  58. package/dist/types-DBQdg-BV.d.ts +0 -169
  59. package/src/components/index.ts +0 -5
  60. package/src/generators/index.ts +0 -5
package/dist/index.js CHANGED
@@ -1,124 +1,1754 @@
1
- import "./chunk--u3MIqq1.js";
2
- import { o as camelCase } from "./StaticClassClient-CCn9g9eF.js";
3
- import { a as classClientGenerator, i as clientGenerator, n as operationsGenerator, r as groupedClientGenerator, t as staticClassClientGenerator } from "./generators-BYUJaeZP.js";
1
+ import { t as __name } from "./chunk--u3MIqq1.js";
4
2
  import { source } from "./templates/clients/axios.source.js";
5
3
  import { source as source$1 } from "./templates/clients/fetch.source.js";
6
4
  import { source as source$2 } from "./templates/config.source.js";
7
5
  import path from "node:path";
8
- import { createPlugin, getBarrelFiles, getMode } from "@kubb/core";
9
- import { OperationGenerator, pluginOasName } from "@kubb/plugin-oas";
6
+ import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
7
+ import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
8
+ import { Const, File, Function, jsxRenderer } from "@kubb/renderer-jsx";
9
+ import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
10
10
  import { pluginZodName } from "@kubb/plugin-zod";
11
+ //#region ../../internals/utils/src/casing.ts
12
+ /**
13
+ * Shared implementation for camelCase and PascalCase conversion.
14
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
15
+ * and capitalizes each word according to `pascal`.
16
+ *
17
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
18
+ */
19
+ function toCamelOrPascal(text, pascal) {
20
+ 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
+ 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);
24
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
25
+ }
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
+ * Converts `text` to camelCase.
40
+ * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
41
+ *
42
+ * @example
43
+ * camelCase('hello-world') // 'helloWorld'
44
+ * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
45
+ */
46
+ function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
47
+ if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
48
+ prefix,
49
+ suffix
50
+ } : {}));
51
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
52
+ }
53
+ /**
54
+ * Converts `text` to PascalCase.
55
+ * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
56
+ *
57
+ * @example
58
+ * pascalCase('hello-world') // 'HelloWorld'
59
+ * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
60
+ */
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));
66
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
67
+ }
68
+ //#endregion
69
+ //#region ../../internals/utils/src/jsdoc.ts
70
+ /**
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.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * buildJSDoc(['@type string', '@example hello'])
77
+ * // '/**\n * @type string\n * @example hello\n *\/\n '
78
+ * ```
79
+ */
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}`;
84
+ }
85
+ //#endregion
86
+ //#region ../../internals/utils/src/reserved.ts
87
+ /**
88
+ * JavaScript and Java reserved words.
89
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
90
+ */
91
+ const reservedWords = new Set([
92
+ "abstract",
93
+ "arguments",
94
+ "boolean",
95
+ "break",
96
+ "byte",
97
+ "case",
98
+ "catch",
99
+ "char",
100
+ "class",
101
+ "const",
102
+ "continue",
103
+ "debugger",
104
+ "default",
105
+ "delete",
106
+ "do",
107
+ "double",
108
+ "else",
109
+ "enum",
110
+ "eval",
111
+ "export",
112
+ "extends",
113
+ "false",
114
+ "final",
115
+ "finally",
116
+ "float",
117
+ "for",
118
+ "function",
119
+ "goto",
120
+ "if",
121
+ "implements",
122
+ "import",
123
+ "in",
124
+ "instanceof",
125
+ "int",
126
+ "interface",
127
+ "let",
128
+ "long",
129
+ "native",
130
+ "new",
131
+ "null",
132
+ "package",
133
+ "private",
134
+ "protected",
135
+ "public",
136
+ "return",
137
+ "short",
138
+ "static",
139
+ "super",
140
+ "switch",
141
+ "synchronized",
142
+ "this",
143
+ "throw",
144
+ "throws",
145
+ "transient",
146
+ "true",
147
+ "try",
148
+ "typeof",
149
+ "var",
150
+ "void",
151
+ "volatile",
152
+ "while",
153
+ "with",
154
+ "yield",
155
+ "Array",
156
+ "Date",
157
+ "hasOwnProperty",
158
+ "Infinity",
159
+ "isFinite",
160
+ "isNaN",
161
+ "isPrototypeOf",
162
+ "length",
163
+ "Math",
164
+ "name",
165
+ "NaN",
166
+ "Number",
167
+ "Object",
168
+ "prototype",
169
+ "String",
170
+ "toString",
171
+ "undefined",
172
+ "valueOf"
173
+ ]);
174
+ /**
175
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * isValidVarName('status') // true
180
+ * isValidVarName('class') // false (reserved word)
181
+ * isValidVarName('42foo') // false (starts with digit)
182
+ * ```
183
+ */
184
+ function isValidVarName(name) {
185
+ if (!name || reservedWords.has(name)) return false;
186
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
187
+ }
188
+ //#endregion
189
+ //#region ../../internals/utils/src/urlPath.ts
190
+ /**
191
+ * Parses and transforms an OpenAPI/Swagger path string into various URL formats.
192
+ *
193
+ * @example
194
+ * const p = new URLPath('/pet/{petId}')
195
+ * p.URL // '/pet/:petId'
196
+ * p.template // '`/pet/${petId}`'
197
+ */
198
+ var URLPath = class {
199
+ /**
200
+ * The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
201
+ */
202
+ path;
203
+ #options;
204
+ constructor(path, options = {}) {
205
+ this.path = path;
206
+ this.#options = options;
207
+ }
208
+ /** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * new URLPath('/pet/{petId}').URL // '/pet/:petId'
213
+ * ```
214
+ */
215
+ get URL() {
216
+ return this.toURLPath();
217
+ }
218
+ /** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
223
+ * new URLPath('/pet/{petId}').isURL // false
224
+ * ```
225
+ */
226
+ get isURL() {
227
+ try {
228
+ return !!new URL(this.path).href;
229
+ } catch {
230
+ return false;
231
+ }
232
+ }
233
+ /**
234
+ * Converts the OpenAPI path to a TypeScript template literal string.
235
+ *
236
+ * @example
237
+ * new URLPath('/pet/{petId}').template // '`/pet/${petId}`'
238
+ * new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
239
+ */
240
+ get template() {
241
+ return this.toTemplateString();
242
+ }
243
+ /** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
244
+ *
245
+ * @example
246
+ * ```ts
247
+ * new URLPath('/pet/{petId}').object
248
+ * // { url: '/pet/:petId', params: { petId: 'petId' } }
249
+ * ```
250
+ */
251
+ get object() {
252
+ return this.toObject();
253
+ }
254
+ /** Returns a map of path parameter names, or `undefined` when the path has no parameters.
255
+ *
256
+ * @example
257
+ * ```ts
258
+ * new URLPath('/pet/{petId}').params // { petId: 'petId' }
259
+ * new URLPath('/pet').params // undefined
260
+ * ```
261
+ */
262
+ get params() {
263
+ return this.getParams();
264
+ }
265
+ #transformParam(raw) {
266
+ const param = isValidVarName(raw) ? raw : camelCase(raw);
267
+ return this.#options.casing === "camelcase" ? camelCase(param) : param;
268
+ }
269
+ /**
270
+ * Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
271
+ */
272
+ #eachParam(fn) {
273
+ for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
274
+ const raw = match[1];
275
+ fn(raw, this.#transformParam(raw));
276
+ }
277
+ }
278
+ toObject({ type = "path", replacer, stringify } = {}) {
279
+ const object = {
280
+ url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
281
+ params: this.getParams()
282
+ };
283
+ if (stringify) {
284
+ if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
285
+ if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
286
+ return `{ url: '${object.url}' }`;
287
+ }
288
+ return object;
289
+ }
290
+ /**
291
+ * Converts the OpenAPI path to a TypeScript template literal string.
292
+ * An optional `replacer` can transform each extracted parameter name before interpolation.
293
+ *
294
+ * @example
295
+ * new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
296
+ */
297
+ toTemplateString({ prefix = "", replacer } = {}) {
298
+ return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
299
+ if (i % 2 === 0) return part;
300
+ const param = this.#transformParam(part);
301
+ return `\${${replacer ? replacer(param) : param}}`;
302
+ }).join("")}\``;
303
+ }
304
+ /**
305
+ * Extracts all `{param}` segments from the path and returns them as a key-value map.
306
+ * An optional `replacer` transforms each parameter name in both key and value positions.
307
+ * Returns `undefined` when no path parameters are found.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * new URLPath('/pet/{petId}/tag/{tagId}').getParams()
312
+ * // { petId: 'petId', tagId: 'tagId' }
313
+ * ```
314
+ */
315
+ getParams(replacer) {
316
+ const params = {};
317
+ this.#eachParam((_raw, param) => {
318
+ const key = replacer ? replacer(param) : param;
319
+ params[key] = key;
320
+ });
321
+ return Object.keys(params).length > 0 ? params : void 0;
322
+ }
323
+ /** Converts the OpenAPI path to Express-style colon syntax.
324
+ *
325
+ * @example
326
+ * ```ts
327
+ * new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
328
+ * ```
329
+ */
330
+ toURLPath() {
331
+ return this.path.replace(/\{([^}]+)\}/g, ":$1");
332
+ }
333
+ };
334
+ //#endregion
335
+ //#region src/functionParams.ts
336
+ const declarationPrinter$4 = functionPrinter({ mode: "declaration" });
337
+ const callPrinter = functionPrinter({ mode: "call" });
338
+ function isGroup(spec) {
339
+ return "children" in spec;
340
+ }
341
+ function createType(type) {
342
+ return type ? ast.createParamsType({
343
+ variant: "reference",
344
+ name: type
345
+ }) : void 0;
346
+ }
347
+ function createDeclarationLeaf(name, spec) {
348
+ if (spec.default !== void 0) return ast.createFunctionParameter({
349
+ name,
350
+ type: createType(spec.type),
351
+ default: spec.default,
352
+ rest: spec.mode === "inlineSpread"
353
+ });
354
+ return ast.createFunctionParameter({
355
+ name,
356
+ type: createType(spec.type),
357
+ optional: !!spec.optional,
358
+ rest: spec.mode === "inlineSpread"
359
+ });
360
+ }
361
+ function createDeclarationParam(name, spec) {
362
+ if (isGroup(spec)) return ast.createParameterGroup({
363
+ inline: spec.mode === "inlineSpread",
364
+ default: spec.default,
365
+ properties: Object.entries(spec.children).filter(([, child]) => child !== void 0).map(([childName, child]) => createDeclarationLeaf(childName, child))
366
+ });
367
+ return createDeclarationLeaf(name, spec);
368
+ }
369
+ function createCallParam(name, spec) {
370
+ if (isGroup(spec)) return ast.createParameterGroup({
371
+ inline: spec.mode === "inlineSpread",
372
+ properties: Object.entries(spec.children).filter(([, child]) => child !== void 0).map(([childName, child]) => ast.createFunctionParameter({
373
+ name: child?.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child?.value ? `${childName}: ${child.value}` : childName,
374
+ rest: spec.mode === "inlineSpread" && child?.mode === "inlineSpread"
375
+ }))
376
+ });
377
+ return ast.createFunctionParameter({
378
+ name: spec.value ?? name,
379
+ rest: spec.mode === "inlineSpread"
380
+ });
381
+ }
382
+ /**
383
+ * Creates function parameter builders for generating function signatures and calls.
384
+ * Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
385
+ */
386
+ function createFunctionParams(params) {
387
+ const entries = Object.entries(params).filter(([, spec]) => spec !== void 0);
388
+ return {
389
+ toConstructor() {
390
+ return declarationPrinter$4.print(ast.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
391
+ },
392
+ toCall() {
393
+ return callPrinter.print(ast.createFunctionParameters({ params: entries.map(([name, spec]) => createCallParam(name, spec)) })) ?? "";
394
+ }
395
+ };
396
+ }
397
+ //#endregion
398
+ //#region src/utils.ts
399
+ /**
400
+ * Extracts documentation comments from an operation node.
401
+ * Includes description, summary, link, and deprecation information.
402
+ */
403
+ function getComments(node) {
404
+ return [
405
+ node.description && `@description ${node.description}`,
406
+ node.summary && `@summary ${node.summary}`,
407
+ node.path && `{@link ${new URLPath(node.path).URL}}`,
408
+ node.deprecated && "@deprecated"
409
+ ].filter((x) => Boolean(x)).flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((x) => Boolean(x));
410
+ }
411
+ /**
412
+ * Builds a mapping of original parameter names to their transformed (cased) names.
413
+ * Returns undefined if no names have changed.
414
+ */
415
+ function buildParamsMapping(originalParams, casedParams) {
416
+ const mapping = {};
417
+ let hasChanged = false;
418
+ originalParams.forEach((param, i) => {
419
+ const casedName = casedParams[i]?.name ?? param.name;
420
+ mapping[param.name] = casedName;
421
+ if (param.name !== casedName) hasChanged = true;
422
+ });
423
+ return hasChanged ? mapping : void 0;
424
+ }
425
+ /**
426
+ * Builds HTTP headers array for a client request.
427
+ * Includes Content-Type (if not default) and spreads header parameters if present.
428
+ */
429
+ function buildHeaders(contentType, hasHeaderParams) {
430
+ return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0, hasHeaderParams ? "...headers" : void 0].filter(Boolean);
431
+ }
432
+ /**
433
+ * Builds TypeScript generic parameters for a client method.
434
+ * Includes response type, error type, and optional request type.
435
+ */
436
+ function buildGenerics(node, tsResolver) {
437
+ const responseName = tsResolver.resolveResponseName(node);
438
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
439
+ const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
440
+ return [
441
+ responseName,
442
+ `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
443
+ requestName || "unknown"
444
+ ].filter(Boolean);
445
+ }
446
+ /**
447
+ * Builds the parameters object for a class-based client method.
448
+ * Includes URL, method, base URL, headers, and request/response data.
449
+ */
450
+ function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, headers }) {
451
+ const queryParamsName = node.parameters.filter((p) => p.in === "query").length > 0 ? tsResolver.resolveQueryParamsName(node, node.parameters.filter((p) => p.in === "query")[0]) : void 0;
452
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
453
+ return createFunctionParams({ config: {
454
+ mode: "object",
455
+ children: {
456
+ requestConfig: { mode: "inlineSpread" },
457
+ method: { value: JSON.stringify(node.method.toUpperCase()) },
458
+ url: { value: path.template },
459
+ baseURL: baseURL ? { value: JSON.stringify(baseURL) } : void 0,
460
+ params: queryParamsName ? {} : void 0,
461
+ data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } : void 0,
462
+ headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : void 0
463
+ }
464
+ } });
465
+ }
466
+ /**
467
+ * Builds the request data parsing line for client methods.
468
+ * Applies Zod validation if configured, otherwise uses data directly.
469
+ */
470
+ function buildRequestDataLine({ parser, node, zodResolver }) {
471
+ const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
472
+ if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
473
+ if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
474
+ return "";
475
+ }
476
+ /**
477
+ * Builds the form data conversion line for file upload requests.
478
+ * Returns empty string if not applicable.
479
+ */
480
+ function buildFormDataLine(isFormData, hasRequest) {
481
+ return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
482
+ }
483
+ /**
484
+ * Builds the return statement for a client method.
485
+ * Applies Zod validation to response data if configured, otherwise returns raw response.
486
+ */
487
+ function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
488
+ const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
489
+ if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
490
+ if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
491
+ if (dataReturnType === "full" && parser === "client") return "return res";
492
+ return "return res.data";
493
+ }
494
+ //#endregion
495
+ //#region src/components/Url.tsx
496
+ const declarationPrinter$3 = functionPrinter({ mode: "declaration" });
497
+ function getParams$1({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
498
+ const urlNode = {
499
+ ...node,
500
+ parameters: node.parameters.filter((p) => p.in === "path"),
501
+ requestBody: void 0
502
+ };
503
+ return ast.createOperationParams(urlNode, {
504
+ paramsType: paramsType === "object" ? "object" : "inline",
505
+ pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
506
+ paramsCasing,
507
+ resolver: tsResolver
508
+ });
509
+ }
510
+ __name(getParams$1, "getParams");
511
+ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
512
+ const path = new URLPath(node.path);
513
+ const paramsNode = getParams$1({
514
+ paramsType,
515
+ paramsCasing,
516
+ pathParamsType,
517
+ node,
518
+ tsResolver
519
+ });
520
+ const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
521
+ const originalPathParams = node.parameters.filter((p) => p.in === "path");
522
+ const casedPathParams = ast.caseParams(originalPathParams, paramsCasing);
523
+ const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : void 0;
524
+ return /* @__PURE__ */ jsx(File.Source, {
525
+ name,
526
+ isExportable,
527
+ isIndexable,
528
+ children: /* @__PURE__ */ jsxs(Function, {
529
+ name,
530
+ export: isExportable,
531
+ params: paramsSignature,
532
+ children: [
533
+ pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
534
+ pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ jsx("br", {}),
535
+ /* @__PURE__ */ jsx(Const, {
536
+ name: "res",
537
+ children: `{ method: '${node.method.toUpperCase()}', url: ${path.toTemplateString({ prefix: baseURL })} as const }`
538
+ }),
539
+ /* @__PURE__ */ jsx("br", {}),
540
+ "return res"
541
+ ]
542
+ })
543
+ });
544
+ }
545
+ Url.getParams = getParams$1;
546
+ //#endregion
547
+ //#region src/components/Client.tsx
548
+ const declarationPrinter$2 = functionPrinter({ mode: "declaration" });
549
+ function getParams({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable }) {
550
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
551
+ return ast.createOperationParams(node, {
552
+ paramsType,
553
+ pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
554
+ paramsCasing,
555
+ resolver: tsResolver,
556
+ extraParams: isConfigurable ? [ast.createFunctionParameter({
557
+ name: "config",
558
+ type: ast.createParamsType({
559
+ variant: "reference",
560
+ name: requestName ? `Partial<RequestConfig<${requestName}>> & { client?: Client }` : "Partial<RequestConfig> & { client?: Client }"
561
+ }),
562
+ default: "{}"
563
+ })] : []
564
+ });
565
+ }
566
+ function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
567
+ const path = new URLPath(node.path);
568
+ const contentType = node.requestBody?.content?.[0]?.contentType ?? "application/json";
569
+ const isFormData = contentType === "multipart/form-data";
570
+ const originalPathParams = node.parameters.filter((p) => p.in === "path");
571
+ const casedPathParams = ast.caseParams(originalPathParams, paramsCasing);
572
+ const originalQueryParams = node.parameters.filter((p) => p.in === "query");
573
+ const casedQueryParams = ast.caseParams(originalQueryParams, paramsCasing);
574
+ const originalHeaderParams = node.parameters.filter((p) => p.in === "header");
575
+ const casedHeaderParams = ast.caseParams(originalHeaderParams, paramsCasing);
576
+ const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : void 0;
577
+ const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : void 0;
578
+ const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : void 0;
579
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
580
+ const responseName = tsResolver.resolveResponseName(node);
581
+ const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : void 0;
582
+ const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : void 0;
583
+ const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
584
+ const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
585
+ const errorNames = node.responses.filter((r) => {
586
+ return Number.parseInt(r.statusCode, 10) >= 400;
587
+ }).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
588
+ const headers = [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : void 0].filter(Boolean);
589
+ const generics = [
590
+ responseName,
591
+ `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
592
+ requestName || "unknown"
593
+ ].filter(Boolean);
594
+ const paramsNode = getParams({
595
+ paramsType,
596
+ paramsCasing,
597
+ pathParamsType,
598
+ node,
599
+ tsResolver,
600
+ isConfigurable
601
+ });
602
+ const paramsSignature = declarationPrinter$2.print(paramsNode) ?? "";
603
+ const urlParamsNode = Url.getParams({
604
+ paramsType,
605
+ paramsCasing,
606
+ pathParamsType,
607
+ node,
608
+ tsResolver
609
+ });
610
+ const urlParamsCall = functionPrinter({ mode: "call" }).print(urlParamsNode) ?? "";
611
+ const clientParams = createFunctionParams({ config: {
612
+ mode: "object",
613
+ children: {
614
+ method: { value: JSON.stringify(node.method.toUpperCase()) },
615
+ url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path.template },
616
+ baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : void 0,
617
+ params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : void 0,
618
+ data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } : void 0,
619
+ requestConfig: isConfigurable ? { mode: "inlineSpread" } : void 0,
620
+ headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : void 0
621
+ }
622
+ } });
623
+ const childrenElement = children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
624
+ dataReturnType === "full" && parser === "zod" && zodResponseName && `return {...res, data: ${zodResponseName}.parse(res.data)}`,
625
+ dataReturnType === "data" && parser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
626
+ dataReturnType === "full" && parser === "client" && "return res",
627
+ dataReturnType === "data" && parser === "client" && "return res.data"
628
+ ] });
629
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx(File.Source, {
630
+ name,
631
+ isExportable,
632
+ isIndexable,
633
+ children: /* @__PURE__ */ jsxs(Function, {
634
+ name,
635
+ async: true,
636
+ export: isExportable,
637
+ params: paramsSignature,
638
+ JSDoc: { comments: getComments(node) },
639
+ returnType,
640
+ children: [
641
+ isConfigurable ? "const { client: request = fetch, ...requestConfig } = config" : "",
642
+ /* @__PURE__ */ jsx("br", {}),
643
+ /* @__PURE__ */ jsx("br", {}),
644
+ pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
645
+ pathParamsMapping && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx("br", {})] }),
646
+ queryParamsMapping && queryParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
647
+ `const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`,
648
+ /* @__PURE__ */ jsx("br", {}),
649
+ /* @__PURE__ */ jsx("br", {})
650
+ ] }),
651
+ headerParamsMapping && headerParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
652
+ `const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`,
653
+ /* @__PURE__ */ jsx("br", {}),
654
+ /* @__PURE__ */ jsx("br", {})
655
+ ] }),
656
+ parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
657
+ /* @__PURE__ */ jsx("br", {}),
658
+ isFormData && requestName && "const formData = buildFormData(requestData)",
659
+ /* @__PURE__ */ jsx("br", {}),
660
+ isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await fetch<${generics.join(", ")}>(${clientParams.toCall()})`,
661
+ /* @__PURE__ */ jsx("br", {}),
662
+ childrenElement
663
+ ]
664
+ })
665
+ })] });
666
+ }
667
+ Client.getParams = getParams;
668
+ //#endregion
669
+ //#region src/components/ClassClient.tsx
670
+ const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
671
+ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
672
+ const path = new URLPath(node.path, { casing: paramsCasing });
673
+ const contentType = node.requestBody?.content?.[0]?.contentType ?? "application/json";
674
+ const isFormData = contentType === "multipart/form-data";
675
+ const headers = buildHeaders(contentType, !!(node.parameters.filter((p) => p.in === "header").length > 0 ? tsResolver.resolveHeaderParamsName(node, node.parameters.filter((p) => p.in === "header")[0]) : void 0));
676
+ const generics = buildGenerics(node, tsResolver);
677
+ const paramsNode = ClassClient.getParams({
678
+ paramsType,
679
+ paramsCasing,
680
+ pathParamsType,
681
+ node,
682
+ tsResolver,
683
+ isConfigurable: true
684
+ });
685
+ const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
686
+ const clientParams = buildClassClientParams({
687
+ node,
688
+ path,
689
+ baseURL,
690
+ tsResolver,
691
+ isFormData,
692
+ headers
693
+ });
694
+ const jsdoc = buildJSDoc(getComments(node));
695
+ const requestDataLine = buildRequestDataLine({
696
+ parser,
697
+ node,
698
+ zodResolver
699
+ });
700
+ const formDataLine = buildFormDataLine(isFormData, !!node.requestBody?.content?.[0]?.schema);
701
+ const returnStatement = buildReturnStatement({
702
+ dataReturnType,
703
+ parser,
704
+ node,
705
+ zodResolver
706
+ });
707
+ return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
708
+ "const { client: request = fetch, ...requestConfig } = mergeConfig(this.#config, config)",
709
+ "",
710
+ requestDataLine,
711
+ formDataLine,
712
+ `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
713
+ returnStatement
714
+ ].filter(Boolean).map((line) => ` ${line}`).join("\n")}\n }`;
715
+ }
716
+ __name(generateMethod$1, "generateMethod");
717
+ function ClassClient({ name, isExportable = true, isIndexable = true, operations, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, children }) {
718
+ const classCode = `export class ${name} {
719
+ #config: Partial<RequestConfig> & { client?: Client }
720
+
721
+ constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
722
+ this.#config = config
723
+ }
724
+
725
+ ${operations.map(({ node, name: methodName, tsResolver, zodResolver }) => generateMethod$1({
726
+ node,
727
+ name: methodName,
728
+ tsResolver,
729
+ zodResolver,
730
+ baseURL,
731
+ dataReturnType,
732
+ parser,
733
+ paramsType,
734
+ paramsCasing,
735
+ pathParamsType
736
+ })).join("\n\n")}
737
+ }`;
738
+ return /* @__PURE__ */ jsxs(File.Source, {
739
+ name,
740
+ isExportable,
741
+ isIndexable,
742
+ children: [classCode, children]
743
+ });
744
+ }
745
+ ClassClient.getParams = Client.getParams;
746
+ //#endregion
747
+ //#region src/components/WrapperClient.tsx
748
+ function WrapperClient({ name, classNames, isExportable = true, isIndexable = true }) {
749
+ const classCode = `export class ${name} {
750
+ ${classNames.map((className) => ` readonly ${camelCase(className)}: ${className}`).join("\n")}
751
+
752
+ constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
753
+ ${classNames.map((className) => ` this.${camelCase(className)} = new ${className}(config)`).join("\n")}
754
+ }
755
+ }`;
756
+ return /* @__PURE__ */ jsx(File.Source, {
757
+ name,
758
+ isExportable,
759
+ isIndexable,
760
+ children: classCode
761
+ });
762
+ }
763
+ //#endregion
764
+ //#region src/generators/classClientGenerator.tsx
765
+ function resolveTypeImportNames$1(node, tsResolver) {
766
+ return [
767
+ node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
768
+ tsResolver.resolveResponseName(node),
769
+ ...node.parameters.filter((p) => p.in === "path").map((p) => tsResolver.resolvePathParamsName(node, p)),
770
+ ...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
771
+ ...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
772
+ ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
773
+ ].filter((n) => Boolean(n));
774
+ }
775
+ __name(resolveTypeImportNames$1, "resolveTypeImportNames");
776
+ function resolveZodImportNames$1(node, zodResolver) {
777
+ return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter((n) => Boolean(n));
778
+ }
779
+ __name(resolveZodImportNames$1, "resolveZodImportNames");
780
+ const classClientGenerator = defineGenerator({
781
+ name: "classClient",
782
+ renderer: jsxRenderer,
783
+ operations(nodes, ctx) {
784
+ const { adapter, config, driver, resolver, root } = ctx;
785
+ const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
786
+ const baseURL = ctx.options.baseURL ?? adapter.inputNode?.meta?.baseURL;
787
+ const pluginTs = driver.getPlugin(pluginTsName);
788
+ if (!pluginTs) return null;
789
+ const tsResolver = driver.getResolver(pluginTsName);
790
+ const tsPluginOptions = pluginTs.options;
791
+ const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : void 0;
792
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
793
+ function buildOperationData(node) {
794
+ const typeFile = tsResolver.resolveFile({
795
+ name: node.operationId,
796
+ extname: ".ts",
797
+ tag: node.tags[0] ?? "default",
798
+ path: node.path
799
+ }, {
800
+ root,
801
+ output: tsPluginOptions?.output ?? output,
802
+ group: tsPluginOptions?.group
803
+ });
804
+ const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
805
+ name: node.operationId,
806
+ extname: ".ts",
807
+ tag: node.tags[0] ?? "default",
808
+ path: node.path
809
+ }, {
810
+ root,
811
+ output: pluginZod.options?.output ?? output,
812
+ group: pluginZod.options?.group
813
+ }) : void 0;
814
+ return {
815
+ node,
816
+ name: resolver.resolveName(node.operationId),
817
+ tsResolver,
818
+ zodResolver,
819
+ typeFile,
820
+ zodFile
821
+ };
822
+ }
823
+ const controllers = nodes.reduce((acc, operationNode) => {
824
+ const tag = operationNode.tags[0];
825
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? pascalCase(tag) : "Client";
826
+ if (!tag && !group) {
827
+ const name = "ApiClient";
828
+ const file = resolver.resolveFile({
829
+ name,
830
+ extname: ".ts"
831
+ }, {
832
+ root,
833
+ output,
834
+ group
835
+ });
836
+ const operationData = buildOperationData(operationNode);
837
+ const previous = acc.find((item) => item.file.path === file.path);
838
+ if (previous) previous.operations.push(operationData);
839
+ else acc.push({
840
+ name,
841
+ file,
842
+ operations: [operationData]
843
+ });
844
+ } else if (tag) {
845
+ const name = groupName;
846
+ const file = resolver.resolveFile({
847
+ name,
848
+ extname: ".ts",
849
+ tag
850
+ }, {
851
+ root,
852
+ output,
853
+ group
854
+ });
855
+ const operationData = buildOperationData(operationNode);
856
+ const previous = acc.find((item) => item.file.path === file.path);
857
+ if (previous) previous.operations.push(operationData);
858
+ else acc.push({
859
+ name,
860
+ file,
861
+ operations: [operationData]
862
+ });
863
+ }
864
+ return acc;
865
+ }, []);
866
+ function collectTypeImports(ops) {
867
+ const typeImportsByFile = /* @__PURE__ */ new Map();
868
+ const typeFilesByPath = /* @__PURE__ */ new Map();
869
+ ops.forEach((op) => {
870
+ const names = resolveTypeImportNames$1(op.node, tsResolver);
871
+ if (!typeImportsByFile.has(op.typeFile.path)) typeImportsByFile.set(op.typeFile.path, /* @__PURE__ */ new Set());
872
+ const imports = typeImportsByFile.get(op.typeFile.path);
873
+ names.forEach((n) => {
874
+ imports.add(n);
875
+ });
876
+ typeFilesByPath.set(op.typeFile.path, op.typeFile);
877
+ });
878
+ return {
879
+ typeImportsByFile,
880
+ typeFilesByPath
881
+ };
882
+ }
883
+ function collectZodImports(ops) {
884
+ const zodImportsByFile = /* @__PURE__ */ new Map();
885
+ const zodFilesByPath = /* @__PURE__ */ new Map();
886
+ ops.forEach((op) => {
887
+ if (!op.zodFile || !zodResolver) return;
888
+ const names = resolveZodImportNames$1(op.node, zodResolver);
889
+ if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
890
+ const imports = zodImportsByFile.get(op.zodFile.path);
891
+ names.forEach((n) => {
892
+ imports.add(n);
893
+ });
894
+ zodFilesByPath.set(op.zodFile.path, op.zodFile);
895
+ });
896
+ return {
897
+ zodImportsByFile,
898
+ zodFilesByPath
899
+ };
900
+ }
901
+ const files = controllers.map(({ name, file, operations: ops }) => {
902
+ const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
903
+ const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
904
+ zodImportsByFile: /* @__PURE__ */ new Map(),
905
+ zodFilesByPath: /* @__PURE__ */ new Map()
906
+ };
907
+ const hasFormData = ops.some((op) => op.node.requestBody?.content?.[0]?.contentType === "multipart/form-data");
908
+ return /* @__PURE__ */ jsxs(File, {
909
+ baseName: file.baseName,
910
+ path: file.path,
911
+ meta: file.meta,
912
+ banner: resolver.resolveBanner(adapter.inputNode, {
913
+ output,
914
+ config
915
+ }),
916
+ footer: resolver.resolveFooter(adapter.inputNode, {
917
+ output,
918
+ config
919
+ }),
920
+ children: [
921
+ importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [
922
+ /* @__PURE__ */ jsx(File.Import, {
923
+ name: "fetch",
924
+ path: importPath
925
+ }),
926
+ /* @__PURE__ */ jsx(File.Import, {
927
+ name: ["mergeConfig"],
928
+ path: importPath
929
+ }),
930
+ /* @__PURE__ */ jsx(File.Import, {
931
+ name: [
932
+ "Client",
933
+ "RequestConfig",
934
+ "ResponseErrorConfig"
935
+ ],
936
+ path: importPath,
937
+ isTypeOnly: true
938
+ })
939
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
940
+ /* @__PURE__ */ jsx(File.Import, {
941
+ name: ["fetch"],
942
+ root: file.path,
943
+ path: path.resolve(root, ".kubb/client.ts")
944
+ }),
945
+ /* @__PURE__ */ jsx(File.Import, {
946
+ name: ["mergeConfig"],
947
+ root: file.path,
948
+ path: path.resolve(root, ".kubb/client.ts")
949
+ }),
950
+ /* @__PURE__ */ jsx(File.Import, {
951
+ name: [
952
+ "Client",
953
+ "RequestConfig",
954
+ "ResponseErrorConfig"
955
+ ],
956
+ root: file.path,
957
+ path: path.resolve(root, ".kubb/client.ts"),
958
+ isTypeOnly: true
959
+ })
960
+ ] }),
961
+ hasFormData && /* @__PURE__ */ jsx(File.Import, {
962
+ name: ["buildFormData"],
963
+ root: file.path,
964
+ path: path.resolve(root, ".kubb/config.ts")
965
+ }),
966
+ Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
967
+ const typeFile = typeFilesByPath.get(filePath);
968
+ if (!typeFile) return null;
969
+ const importNames = Array.from(importSet).filter(Boolean);
970
+ if (importNames.length === 0) return null;
971
+ return /* @__PURE__ */ jsx(File.Import, {
972
+ name: importNames,
973
+ root: file.path,
974
+ path: typeFile.path,
975
+ isTypeOnly: true
976
+ }, filePath);
977
+ }),
978
+ parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
979
+ const zodFile = zodFilesByPath.get(filePath);
980
+ if (!zodFile) return null;
981
+ const importNames = Array.from(importSet).filter(Boolean);
982
+ if (importNames.length === 0) return null;
983
+ return /* @__PURE__ */ jsx(File.Import, {
984
+ name: importNames,
985
+ root: file.path,
986
+ path: zodFile.path
987
+ }, filePath);
988
+ }),
989
+ /* @__PURE__ */ jsx(ClassClient, {
990
+ name,
991
+ operations: ops,
992
+ baseURL,
993
+ dataReturnType,
994
+ pathParamsType,
995
+ paramsCasing,
996
+ paramsType,
997
+ parser
998
+ })
999
+ ]
1000
+ }, file.path);
1001
+ });
1002
+ if (sdk) {
1003
+ const sdkFile = resolver.resolveFile({
1004
+ name: sdk.className,
1005
+ extname: ".ts"
1006
+ }, {
1007
+ root,
1008
+ output,
1009
+ group
1010
+ });
1011
+ files.push(/* @__PURE__ */ jsxs(File, {
1012
+ baseName: sdkFile.baseName,
1013
+ path: sdkFile.path,
1014
+ meta: sdkFile.meta,
1015
+ banner: resolver.resolveBanner(adapter.inputNode, {
1016
+ output,
1017
+ config
1018
+ }),
1019
+ footer: resolver.resolveFooter(adapter.inputNode, {
1020
+ output,
1021
+ config
1022
+ }),
1023
+ children: [
1024
+ importPath ? /* @__PURE__ */ jsx(File.Import, {
1025
+ name: ["Client", "RequestConfig"],
1026
+ path: importPath,
1027
+ isTypeOnly: true
1028
+ }) : /* @__PURE__ */ jsx(File.Import, {
1029
+ name: ["Client", "RequestConfig"],
1030
+ root: sdkFile.path,
1031
+ path: path.resolve(root, ".kubb/client.ts"),
1032
+ isTypeOnly: true
1033
+ }),
1034
+ controllers.map(({ name, file }) => /* @__PURE__ */ jsx(File.Import, {
1035
+ name: [name],
1036
+ root: sdkFile.path,
1037
+ path: file.path
1038
+ }, name)),
1039
+ /* @__PURE__ */ jsx(WrapperClient, {
1040
+ name: sdk.className,
1041
+ classNames: controllers.map(({ name }) => name)
1042
+ })
1043
+ ]
1044
+ }, sdkFile.path));
1045
+ }
1046
+ return /* @__PURE__ */ jsx(Fragment, { children: files });
1047
+ }
1048
+ });
1049
+ //#endregion
1050
+ //#region src/generators/clientGenerator.tsx
1051
+ const clientGenerator = defineGenerator({
1052
+ name: "client",
1053
+ renderer: jsxRenderer,
1054
+ operation(node, ctx) {
1055
+ const { adapter, config, driver, resolver, root } = ctx;
1056
+ const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options;
1057
+ const baseURL = ctx.options.baseURL ?? adapter.inputNode?.meta?.baseURL;
1058
+ const pluginTs = driver.getPlugin(pluginTsName);
1059
+ if (!pluginTs) return null;
1060
+ const tsResolver = driver.getResolver(pluginTsName);
1061
+ const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : void 0;
1062
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
1063
+ const casedParams = ast.caseParams(node.parameters, paramsCasing);
1064
+ const pathParams = casedParams.filter((p) => p.in === "path");
1065
+ const queryParams = casedParams.filter((p) => p.in === "query");
1066
+ const headerParams = casedParams.filter((p) => p.in === "header");
1067
+ const importedTypeNames = [
1068
+ ...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
1069
+ ...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
1070
+ ...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
1071
+ node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
1072
+ tsResolver.resolveResponseName(node),
1073
+ ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
1074
+ ].filter(Boolean);
1075
+ const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter(Boolean) : [];
1076
+ const meta = {
1077
+ name: resolver.resolveName(node.operationId),
1078
+ urlName: `get${resolver.resolveName(node.operationId).charAt(0).toUpperCase()}${resolver.resolveName(node.operationId).slice(1)}Url`,
1079
+ file: resolver.resolveFile({
1080
+ name: node.operationId,
1081
+ extname: ".ts",
1082
+ tag: node.tags[0] ?? "default",
1083
+ path: node.path
1084
+ }, {
1085
+ root,
1086
+ output,
1087
+ group
1088
+ }),
1089
+ fileTs: tsResolver.resolveFile({
1090
+ name: node.operationId,
1091
+ extname: ".ts",
1092
+ tag: node.tags[0] ?? "default",
1093
+ path: node.path
1094
+ }, {
1095
+ root,
1096
+ output: pluginTs.options?.output ?? output,
1097
+ group: pluginTs.options?.group
1098
+ }),
1099
+ fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile({
1100
+ name: node.operationId,
1101
+ extname: ".ts",
1102
+ tag: node.tags[0] ?? "default",
1103
+ path: node.path
1104
+ }, {
1105
+ root,
1106
+ output: pluginZod.options.output ?? output,
1107
+ group: pluginZod.options?.group
1108
+ }) : void 0
1109
+ };
1110
+ const isFormData = node.requestBody?.content?.[0]?.contentType === "multipart/form-data";
1111
+ return /* @__PURE__ */ jsxs(File, {
1112
+ baseName: meta.file.baseName,
1113
+ path: meta.file.path,
1114
+ meta: meta.file.meta,
1115
+ banner: resolver.resolveBanner(adapter.inputNode, {
1116
+ output,
1117
+ config
1118
+ }),
1119
+ footer: resolver.resolveFooter(adapter.inputNode, {
1120
+ output,
1121
+ config
1122
+ }),
1123
+ children: [
1124
+ importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Import, {
1125
+ name: "fetch",
1126
+ path: importPath
1127
+ }), /* @__PURE__ */ jsx(File.Import, {
1128
+ name: [
1129
+ "Client",
1130
+ "RequestConfig",
1131
+ "ResponseErrorConfig"
1132
+ ],
1133
+ path: importPath,
1134
+ isTypeOnly: true
1135
+ })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Import, {
1136
+ name: ["fetch"],
1137
+ root: meta.file.path,
1138
+ path: path.resolve(root, ".kubb/client.ts")
1139
+ }), /* @__PURE__ */ jsx(File.Import, {
1140
+ name: [
1141
+ "Client",
1142
+ "RequestConfig",
1143
+ "ResponseErrorConfig"
1144
+ ],
1145
+ root: meta.file.path,
1146
+ path: path.resolve(root, ".kubb/client.ts"),
1147
+ isTypeOnly: true
1148
+ })] }),
1149
+ isFormData && node.requestBody?.content?.[0]?.schema && /* @__PURE__ */ jsx(File.Import, {
1150
+ name: ["buildFormData"],
1151
+ root: meta.file.path,
1152
+ path: path.resolve(root, ".kubb/config.ts")
1153
+ }),
1154
+ meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1155
+ name: importedZodNames,
1156
+ root: meta.file.path,
1157
+ path: meta.fileZod.path
1158
+ }),
1159
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1160
+ name: Array.from(new Set(importedTypeNames)),
1161
+ root: meta.file.path,
1162
+ path: meta.fileTs.path,
1163
+ isTypeOnly: true
1164
+ }),
1165
+ /* @__PURE__ */ jsx(Url, {
1166
+ name: meta.urlName,
1167
+ baseURL,
1168
+ pathParamsType,
1169
+ paramsCasing,
1170
+ paramsType,
1171
+ node,
1172
+ tsResolver,
1173
+ isIndexable: urlType === "export",
1174
+ isExportable: urlType === "export"
1175
+ }),
1176
+ /* @__PURE__ */ jsx(Client, {
1177
+ name: meta.name,
1178
+ urlName: meta.urlName,
1179
+ baseURL,
1180
+ dataReturnType,
1181
+ pathParamsType,
1182
+ paramsCasing,
1183
+ paramsType,
1184
+ node,
1185
+ tsResolver,
1186
+ zodResolver,
1187
+ parser
1188
+ })
1189
+ ]
1190
+ });
1191
+ }
1192
+ });
1193
+ //#endregion
1194
+ //#region src/generators/groupedClientGenerator.tsx
1195
+ const groupedClientGenerator = defineGenerator({
1196
+ name: "groupedClient",
1197
+ renderer: jsxRenderer,
1198
+ operations(nodes, ctx) {
1199
+ const { config, resolver, adapter, root } = ctx;
1200
+ const { output, group } = ctx.options;
1201
+ return /* @__PURE__ */ jsx(Fragment, { children: nodes.reduce((acc, operationNode) => {
1202
+ if (group?.type === "tag") {
1203
+ const tag = operationNode.tags[0];
1204
+ const name = tag ? group?.name?.({ group: camelCase(tag) }) : void 0;
1205
+ if (!tag || !name) return acc;
1206
+ const file = resolver.resolveFile({
1207
+ name,
1208
+ extname: ".ts",
1209
+ tag
1210
+ }, {
1211
+ root,
1212
+ output,
1213
+ group
1214
+ });
1215
+ const clientFile = resolver.resolveFile({
1216
+ name: operationNode.operationId,
1217
+ extname: ".ts",
1218
+ tag: operationNode.tags[0] ?? "default",
1219
+ path: operationNode.path
1220
+ }, {
1221
+ root,
1222
+ output,
1223
+ group
1224
+ });
1225
+ const client = {
1226
+ name: resolver.resolveName(operationNode.operationId),
1227
+ file: clientFile
1228
+ };
1229
+ const previous = acc.find((item) => item.file.path === file.path);
1230
+ if (previous) previous.clients.push(client);
1231
+ else acc.push({
1232
+ name,
1233
+ file,
1234
+ clients: [client]
1235
+ });
1236
+ }
1237
+ return acc;
1238
+ }, []).map(({ name, file, clients }) => {
1239
+ return /* @__PURE__ */ jsxs(File, {
1240
+ baseName: file.baseName,
1241
+ path: file.path,
1242
+ meta: file.meta,
1243
+ banner: resolver.resolveBanner(adapter.inputNode, {
1244
+ output,
1245
+ config
1246
+ }),
1247
+ footer: resolver.resolveFooter(adapter.inputNode, {
1248
+ output,
1249
+ config
1250
+ }),
1251
+ children: [clients.map((client) => /* @__PURE__ */ jsx(File.Import, {
1252
+ name: [client.name],
1253
+ root: file.path,
1254
+ path: client.file.path
1255
+ }, client.name)), /* @__PURE__ */ jsx(File.Source, {
1256
+ name,
1257
+ isExportable: true,
1258
+ isIndexable: true,
1259
+ children: /* @__PURE__ */ jsx(Function, {
1260
+ export: true,
1261
+ name,
1262
+ children: `return { ${clients.map((client) => client.name).join(", ")} }`
1263
+ })
1264
+ })]
1265
+ }, file.path);
1266
+ }) });
1267
+ }
1268
+ });
1269
+ //#endregion
1270
+ //#region src/components/Operations.tsx
1271
+ function Operations({ name, nodes }) {
1272
+ const operationsObject = {};
1273
+ nodes.forEach((node) => {
1274
+ operationsObject[node.operationId] = {
1275
+ path: new URLPath(node.path).URL,
1276
+ method: node.method.toLowerCase()
1277
+ };
1278
+ });
1279
+ return /* @__PURE__ */ jsx(File.Source, {
1280
+ name,
1281
+ isExportable: true,
1282
+ isIndexable: true,
1283
+ children: /* @__PURE__ */ jsx(Const, {
1284
+ name,
1285
+ export: true,
1286
+ children: JSON.stringify(operationsObject, void 0, 2)
1287
+ })
1288
+ });
1289
+ }
1290
+ //#endregion
1291
+ //#region src/generators/operationsGenerator.tsx
1292
+ const operationsGenerator = defineGenerator({
1293
+ name: "client",
1294
+ renderer: jsxRenderer,
1295
+ operations(nodes, ctx) {
1296
+ const { config, resolver, adapter, root } = ctx;
1297
+ const { output, group } = ctx.options;
1298
+ const name = "operations";
1299
+ const file = resolver.resolveFile({
1300
+ name,
1301
+ extname: ".ts"
1302
+ }, {
1303
+ root,
1304
+ output,
1305
+ group
1306
+ });
1307
+ return /* @__PURE__ */ jsx(File, {
1308
+ baseName: file.baseName,
1309
+ path: file.path,
1310
+ meta: file.meta,
1311
+ banner: resolver.resolveBanner(adapter.inputNode, {
1312
+ output,
1313
+ config
1314
+ }),
1315
+ footer: resolver.resolveFooter(adapter.inputNode, {
1316
+ output,
1317
+ config
1318
+ }),
1319
+ children: /* @__PURE__ */ jsx(Operations, {
1320
+ name,
1321
+ nodes
1322
+ })
1323
+ });
1324
+ }
1325
+ });
1326
+ //#endregion
1327
+ //#region src/components/StaticClassClient.tsx
1328
+ const declarationPrinter = functionPrinter({ mode: "declaration" });
1329
+ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
1330
+ const path = new URLPath(node.path, { casing: paramsCasing });
1331
+ const contentType = node.requestBody?.content?.[0]?.contentType ?? "application/json";
1332
+ const isFormData = contentType === "multipart/form-data";
1333
+ const headers = buildHeaders(contentType, !!(node.parameters.filter((p) => p.in === "header").length > 0 ? tsResolver.resolveHeaderParamsName(node, node.parameters.filter((p) => p.in === "header")[0]) : void 0));
1334
+ const generics = buildGenerics(node, tsResolver);
1335
+ const paramsNode = Client.getParams({
1336
+ paramsType,
1337
+ paramsCasing,
1338
+ pathParamsType,
1339
+ node,
1340
+ tsResolver,
1341
+ isConfigurable: true
1342
+ });
1343
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1344
+ const clientParams = buildClassClientParams({
1345
+ node,
1346
+ path,
1347
+ baseURL,
1348
+ tsResolver,
1349
+ isFormData,
1350
+ headers
1351
+ });
1352
+ const jsdoc = buildJSDoc(getComments(node));
1353
+ const requestDataLine = buildRequestDataLine({
1354
+ parser,
1355
+ node,
1356
+ zodResolver
1357
+ });
1358
+ const formDataLine = buildFormDataLine(isFormData, !!node.requestBody?.content?.[0]?.schema);
1359
+ const returnStatement = buildReturnStatement({
1360
+ dataReturnType,
1361
+ parser,
1362
+ node,
1363
+ zodResolver
1364
+ });
1365
+ return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
1366
+ "const { client: request = fetch, ...requestConfig } = mergeConfig(this.#config, config)",
1367
+ "",
1368
+ requestDataLine,
1369
+ formDataLine,
1370
+ `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
1371
+ returnStatement
1372
+ ].filter(Boolean).map((line) => ` ${line}`).join("\n")}\n }`;
1373
+ }
1374
+ function StaticClassClient({ name, isExportable = true, isIndexable = true, operations, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, children }) {
1375
+ const classCode = `export class ${name} {\n static #config: Partial<RequestConfig> & { client?: Client } = {}\n\n${operations.map(({ node, name: methodName, tsResolver, zodResolver }) => generateMethod({
1376
+ node,
1377
+ name: methodName,
1378
+ tsResolver,
1379
+ zodResolver,
1380
+ baseURL,
1381
+ dataReturnType,
1382
+ parser,
1383
+ paramsType,
1384
+ paramsCasing,
1385
+ pathParamsType
1386
+ })).join("\n\n")}\n}`;
1387
+ return /* @__PURE__ */ jsxs(File.Source, {
1388
+ name,
1389
+ isExportable,
1390
+ isIndexable,
1391
+ children: [classCode, children]
1392
+ });
1393
+ }
1394
+ StaticClassClient.getParams = Client.getParams;
1395
+ //#endregion
1396
+ //#region src/generators/staticClassClientGenerator.tsx
1397
+ function resolveTypeImportNames(node, tsResolver) {
1398
+ return [
1399
+ node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
1400
+ tsResolver.resolveResponseName(node),
1401
+ ...node.parameters.filter((p) => p.in === "path").map((p) => tsResolver.resolvePathParamsName(node, p)),
1402
+ ...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
1403
+ ...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
1404
+ ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
1405
+ ].filter((n) => Boolean(n));
1406
+ }
1407
+ function resolveZodImportNames(node, zodResolver) {
1408
+ return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter((n) => Boolean(n));
1409
+ }
1410
+ const staticClassClientGenerator = defineGenerator({
1411
+ name: "staticClassClient",
1412
+ renderer: jsxRenderer,
1413
+ operations(nodes, ctx) {
1414
+ const { adapter, config, driver, resolver, root } = ctx;
1415
+ const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
1416
+ const baseURL = ctx.options.baseURL ?? adapter.inputNode?.meta?.baseURL;
1417
+ const pluginTs = driver.getPlugin(pluginTsName);
1418
+ if (!pluginTs) return null;
1419
+ const tsResolver = driver.getResolver(pluginTsName);
1420
+ const tsPluginOptions = pluginTs.options;
1421
+ const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : void 0;
1422
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
1423
+ function buildOperationData(node) {
1424
+ const typeFile = tsResolver.resolveFile({
1425
+ name: node.operationId,
1426
+ extname: ".ts",
1427
+ tag: node.tags[0] ?? "default",
1428
+ path: node.path
1429
+ }, {
1430
+ root,
1431
+ output: tsPluginOptions?.output ?? output,
1432
+ group: tsPluginOptions?.group
1433
+ });
1434
+ const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
1435
+ name: node.operationId,
1436
+ extname: ".ts",
1437
+ tag: node.tags[0] ?? "default",
1438
+ path: node.path
1439
+ }, {
1440
+ root,
1441
+ output: pluginZod.options?.output ?? output,
1442
+ group: pluginZod.options?.group
1443
+ }) : void 0;
1444
+ return {
1445
+ node,
1446
+ name: resolver.resolveName(node.operationId),
1447
+ tsResolver,
1448
+ zodResolver,
1449
+ typeFile,
1450
+ zodFile
1451
+ };
1452
+ }
1453
+ const controllers = nodes.reduce((acc, operationNode) => {
1454
+ const tag = operationNode.tags[0];
1455
+ const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? pascalCase(tag) : "Client";
1456
+ if (!tag && !group) {
1457
+ const name = "ApiClient";
1458
+ const file = resolver.resolveFile({
1459
+ name,
1460
+ extname: ".ts"
1461
+ }, {
1462
+ root,
1463
+ output,
1464
+ group
1465
+ });
1466
+ const operationData = buildOperationData(operationNode);
1467
+ const previous = acc.find((item) => item.file.path === file.path);
1468
+ if (previous) previous.operations.push(operationData);
1469
+ else acc.push({
1470
+ name,
1471
+ file,
1472
+ operations: [operationData]
1473
+ });
1474
+ } else if (tag) {
1475
+ const name = groupName;
1476
+ const file = resolver.resolveFile({
1477
+ name,
1478
+ extname: ".ts",
1479
+ tag
1480
+ }, {
1481
+ root,
1482
+ output,
1483
+ group
1484
+ });
1485
+ const operationData = buildOperationData(operationNode);
1486
+ const previous = acc.find((item) => item.file.path === file.path);
1487
+ if (previous) previous.operations.push(operationData);
1488
+ else acc.push({
1489
+ name,
1490
+ file,
1491
+ operations: [operationData]
1492
+ });
1493
+ }
1494
+ return acc;
1495
+ }, []);
1496
+ function collectTypeImports(ops) {
1497
+ const typeImportsByFile = /* @__PURE__ */ new Map();
1498
+ const typeFilesByPath = /* @__PURE__ */ new Map();
1499
+ ops.forEach((op) => {
1500
+ const names = resolveTypeImportNames(op.node, tsResolver);
1501
+ if (!typeImportsByFile.has(op.typeFile.path)) typeImportsByFile.set(op.typeFile.path, /* @__PURE__ */ new Set());
1502
+ const imports = typeImportsByFile.get(op.typeFile.path);
1503
+ names.forEach((n) => {
1504
+ imports.add(n);
1505
+ });
1506
+ typeFilesByPath.set(op.typeFile.path, op.typeFile);
1507
+ });
1508
+ return {
1509
+ typeImportsByFile,
1510
+ typeFilesByPath
1511
+ };
1512
+ }
1513
+ function collectZodImports(ops) {
1514
+ const zodImportsByFile = /* @__PURE__ */ new Map();
1515
+ const zodFilesByPath = /* @__PURE__ */ new Map();
1516
+ ops.forEach((op) => {
1517
+ if (!op.zodFile || !zodResolver) return;
1518
+ const names = resolveZodImportNames(op.node, zodResolver);
1519
+ if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1520
+ const imports = zodImportsByFile.get(op.zodFile.path);
1521
+ names.forEach((n) => {
1522
+ imports.add(n);
1523
+ });
1524
+ zodFilesByPath.set(op.zodFile.path, op.zodFile);
1525
+ });
1526
+ return {
1527
+ zodImportsByFile,
1528
+ zodFilesByPath
1529
+ };
1530
+ }
1531
+ return /* @__PURE__ */ jsx(Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
1532
+ const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1533
+ const { zodImportsByFile, zodFilesByPath } = parser === "zod" ? collectZodImports(ops) : {
1534
+ zodImportsByFile: /* @__PURE__ */ new Map(),
1535
+ zodFilesByPath: /* @__PURE__ */ new Map()
1536
+ };
1537
+ const hasFormData = ops.some((op) => op.node.requestBody?.content?.[0]?.contentType === "multipart/form-data");
1538
+ return /* @__PURE__ */ jsxs(File, {
1539
+ baseName: file.baseName,
1540
+ path: file.path,
1541
+ meta: file.meta,
1542
+ banner: resolver.resolveBanner(adapter.inputNode, {
1543
+ output,
1544
+ config
1545
+ }),
1546
+ footer: resolver.resolveFooter(adapter.inputNode, {
1547
+ output,
1548
+ config
1549
+ }),
1550
+ children: [
1551
+ importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [
1552
+ /* @__PURE__ */ jsx(File.Import, {
1553
+ name: "fetch",
1554
+ path: importPath
1555
+ }),
1556
+ /* @__PURE__ */ jsx(File.Import, {
1557
+ name: ["mergeConfig"],
1558
+ path: importPath
1559
+ }),
1560
+ /* @__PURE__ */ jsx(File.Import, {
1561
+ name: [
1562
+ "Client",
1563
+ "RequestConfig",
1564
+ "ResponseErrorConfig"
1565
+ ],
1566
+ path: importPath,
1567
+ isTypeOnly: true
1568
+ })
1569
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1570
+ /* @__PURE__ */ jsx(File.Import, {
1571
+ name: ["fetch"],
1572
+ root: file.path,
1573
+ path: path.resolve(root, ".kubb/client.ts")
1574
+ }),
1575
+ /* @__PURE__ */ jsx(File.Import, {
1576
+ name: ["mergeConfig"],
1577
+ root: file.path,
1578
+ path: path.resolve(root, ".kubb/client.ts")
1579
+ }),
1580
+ /* @__PURE__ */ jsx(File.Import, {
1581
+ name: [
1582
+ "Client",
1583
+ "RequestConfig",
1584
+ "ResponseErrorConfig"
1585
+ ],
1586
+ root: file.path,
1587
+ path: path.resolve(root, ".kubb/client.ts"),
1588
+ isTypeOnly: true
1589
+ })
1590
+ ] }),
1591
+ hasFormData && /* @__PURE__ */ jsx(File.Import, {
1592
+ name: ["buildFormData"],
1593
+ root: file.path,
1594
+ path: path.resolve(root, ".kubb/config.ts")
1595
+ }),
1596
+ Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1597
+ const typeFile = typeFilesByPath.get(filePath);
1598
+ if (!typeFile) return null;
1599
+ const importNames = Array.from(importSet).filter(Boolean);
1600
+ if (importNames.length === 0) return null;
1601
+ return /* @__PURE__ */ jsx(File.Import, {
1602
+ name: importNames,
1603
+ root: file.path,
1604
+ path: typeFile.path,
1605
+ isTypeOnly: true
1606
+ }, filePath);
1607
+ }),
1608
+ parser === "zod" && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1609
+ const zodFile = zodFilesByPath.get(filePath);
1610
+ if (!zodFile) return null;
1611
+ const importNames = Array.from(importSet).filter(Boolean);
1612
+ if (importNames.length === 0) return null;
1613
+ return /* @__PURE__ */ jsx(File.Import, {
1614
+ name: importNames,
1615
+ root: file.path,
1616
+ path: zodFile.path
1617
+ }, filePath);
1618
+ }),
1619
+ /* @__PURE__ */ jsx(StaticClassClient, {
1620
+ name,
1621
+ operations: ops,
1622
+ baseURL,
1623
+ dataReturnType,
1624
+ pathParamsType,
1625
+ paramsCasing,
1626
+ paramsType,
1627
+ parser
1628
+ })
1629
+ ]
1630
+ }, file.path);
1631
+ }) });
1632
+ }
1633
+ });
1634
+ //#endregion
1635
+ //#region src/resolvers/resolverClient.ts
1636
+ /**
1637
+ * Naming convention resolver for client plugin.
1638
+ *
1639
+ * Provides default naming helpers using camelCase for functions and file paths.
1640
+ *
1641
+ * @example
1642
+ * `resolverClient.default('list pets', 'function') // → 'listPets'`
1643
+ */
1644
+ const resolverClient = defineResolver((ctx) => ({
1645
+ name: "default",
1646
+ pluginName: "plugin-client",
1647
+ default(name, type) {
1648
+ return camelCase(name, { isFile: type === "file" });
1649
+ },
1650
+ resolveName(name) {
1651
+ return ctx.default(name, "function");
1652
+ }
1653
+ }));
1654
+ //#endregion
11
1655
  //#region src/plugin.ts
1656
+ /**
1657
+ * Canonical plugin name for `@kubb/plugin-client`, used in driver lookups and warnings.
1658
+ */
12
1659
  const pluginClientName = "plugin-client";
13
- const pluginClient = createPlugin((options) => {
1660
+ /**
1661
+ * Generates type-safe HTTP client functions or classes from an OpenAPI specification.
1662
+ * Creates client APIs by walking operations and delegating to generators.
1663
+ * Writes barrel files based on the configured `barrelType`.
1664
+ *
1665
+ * @example Client generator
1666
+ * ```ts
1667
+ * import pluginClient from '@kubb/plugin-client'
1668
+ * export default defineConfig({
1669
+ * plugins: [pluginClient({ output: { path: 'clients' } })]
1670
+ * })
1671
+ * ```
1672
+ */
1673
+ const pluginClient = definePlugin((options) => {
14
1674
  const { output = {
15
1675
  path: "clients",
16
1676
  barrelType: "named"
17
- }, group, urlType = false, exclude = [], include, override = [], transformers = {}, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, baseURL, paramsCasing, clientType = "function", parser = "client", client = "axios", importPath, contentType, bundle = false, wrapper } = options;
1677
+ }, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser = "client", client = "axios", importPath, bundle = false, sdk, baseURL, resolver: userResolver, transformer: userTransformer } = options;
18
1678
  const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
19
- const defaultGenerators = [
1679
+ const selectedGenerators = options.generators ?? [
20
1680
  clientType === "staticClass" ? staticClassClientGenerator : clientType === "class" ? classClientGenerator : clientGenerator,
21
1681
  group && clientType === "function" ? groupedClientGenerator : void 0,
22
1682
  operations ? operationsGenerator : void 0
23
1683
  ].filter((x) => Boolean(x));
24
- const generators = options.generators ?? defaultGenerators;
1684
+ const groupConfig = group ? {
1685
+ ...group,
1686
+ name: group.name ? group.name : (ctx) => {
1687
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1688
+ return `${camelCase(ctx.group)}Controller`;
1689
+ }
1690
+ } : void 0;
25
1691
  return {
26
1692
  name: pluginClientName,
27
- options: {
28
- client,
29
- clientType,
30
- bundle,
31
- output,
32
- group,
33
- parser,
34
- dataReturnType,
35
- importPath: resolvedImportPath,
36
- paramsType,
37
- paramsCasing,
38
- pathParamsType,
39
- baseURL,
40
- urlType,
41
- wrapper
42
- },
43
- pre: [pluginOasName, parser === "zod" ? pluginZodName : void 0].filter(Boolean),
44
- resolvePath(baseName, pathMode, options) {
45
- const root = path.resolve(this.config.root, this.config.output.path);
46
- if ((pathMode ?? getMode(path.resolve(root, output.path))) === "single")
47
- /**
48
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
49
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
50
- */
51
- return path.resolve(root, output.path);
52
- if (group && (options?.group?.path || options?.group?.tag)) {
53
- const groupName = group?.name ? group.name : (ctx) => {
54
- if (group?.type === "path") return `${ctx.group.split("/")[1]}`;
55
- return `${camelCase(ctx.group)}Controller`;
56
- };
57
- return path.resolve(root, output.path, groupName({ group: group.type === "path" ? options.group.path : options.group.tag }), baseName);
58
- }
59
- return path.resolve(root, output.path, baseName);
60
- },
61
- resolveName(name, type) {
62
- const resolvedName = camelCase(name, { isFile: type === "file" });
63
- if (type) return transformers?.name?.(resolvedName, type) || resolvedName;
64
- return resolvedName;
65
- },
66
- async install() {
67
- const root = path.resolve(this.config.root, this.config.output.path);
68
- const mode = getMode(path.resolve(root, output.path));
69
- const oas = await this.getOas();
70
- const baseURL = await this.getBaseURL();
71
- if (bundle && !this.plugin.options.importPath) await this.addFile({
72
- baseName: "fetch.ts",
73
- path: path.resolve(root, ".kubb/fetch.ts"),
74
- sources: [{
75
- name: "fetch",
76
- value: this.plugin.options.client === "fetch" ? source$1 : source,
77
- isExportable: true,
78
- isIndexable: true
79
- }],
80
- imports: [],
81
- exports: []
1693
+ options,
1694
+ dependencies: [pluginTsName, parser === "zod" ? pluginZodName : void 0].filter(Boolean),
1695
+ hooks: { "kubb:plugin:setup"(ctx) {
1696
+ const resolver = userResolver ? {
1697
+ ...resolverClient,
1698
+ ...userResolver
1699
+ } : resolverClient;
1700
+ ctx.setOptions({
1701
+ client,
1702
+ clientType,
1703
+ bundle,
1704
+ output,
1705
+ exclude,
1706
+ include,
1707
+ override,
1708
+ group: groupConfig,
1709
+ parser,
1710
+ dataReturnType,
1711
+ importPath: resolvedImportPath,
1712
+ baseURL,
1713
+ paramsType,
1714
+ paramsCasing,
1715
+ pathParamsType,
1716
+ urlType,
1717
+ sdk,
1718
+ resolver
82
1719
  });
83
- await this.addFile({
1720
+ ctx.setResolver(resolver);
1721
+ if (userTransformer) ctx.setTransformer(userTransformer);
1722
+ for (const gen of selectedGenerators) ctx.addGenerator(gen);
1723
+ const root = path.resolve(ctx.config.root, ctx.config.output.path);
1724
+ if (!resolvedImportPath?.startsWith(".")) {
1725
+ const isInlineSource = bundle && !resolvedImportPath;
1726
+ ctx.injectFile({
1727
+ baseName: "client.ts",
1728
+ path: path.resolve(root, ".kubb/client.ts"),
1729
+ sources: [ast.createSource({
1730
+ name: "client",
1731
+ nodes: isInlineSource ? [ast.createText(client === "fetch" ? source$1 : source)] : [],
1732
+ isExportable: true,
1733
+ isIndexable: true
1734
+ })],
1735
+ exports: !isInlineSource && resolvedImportPath ? [ast.createExport({ path: resolvedImportPath })] : []
1736
+ });
1737
+ }
1738
+ ctx.injectFile({
84
1739
  baseName: "config.ts",
85
1740
  path: path.resolve(root, ".kubb/config.ts"),
86
- sources: [{
1741
+ sources: [ast.createSource({
87
1742
  name: "config",
88
- value: source$2,
1743
+ nodes: [ast.createText(source$2)],
89
1744
  isExportable: false,
90
1745
  isIndexable: false
91
- }],
92
- imports: [],
93
- exports: []
1746
+ })]
94
1747
  });
95
- const files = await new OperationGenerator(baseURL ? {
96
- ...this.plugin.options,
97
- baseURL
98
- } : this.plugin.options, {
99
- fabric: this.fabric,
100
- oas,
101
- driver: this.driver,
102
- events: this.events,
103
- plugin: this.plugin,
104
- contentType,
105
- exclude,
106
- include,
107
- override,
108
- mode
109
- }).build(...generators);
110
- await this.upsertFile(...files);
111
- const barrelFiles = await getBarrelFiles(this.fabric.files, {
112
- type: output.barrelType ?? "named",
113
- root,
114
- output,
115
- meta: { pluginName: this.plugin.name }
116
- });
117
- await this.upsertFile(...barrelFiles);
118
- }
1748
+ } }
119
1749
  };
120
1750
  });
121
1751
  //#endregion
122
- export { pluginClient, pluginClientName };
1752
+ export { Client, classClientGenerator, clientGenerator, pluginClient as default, pluginClient, groupedClientGenerator, operationsGenerator, pluginClientName, resolverClient, staticClassClientGenerator };
123
1753
 
124
1754
  //# sourceMappingURL=index.js.map