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

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 (55) hide show
  1. package/README.md +8 -61
  2. package/dist/index.cjs +754 -1745
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +170 -349
  5. package/dist/index.js +733 -1733
  6. package/dist/index.js.map +1 -1
  7. package/package.json +10 -63
  8. package/src/generators/clientGenerator.tsx +6 -132
  9. package/src/index.ts +1 -9
  10. package/src/plugin.ts +32 -122
  11. package/src/types.ts +27 -249
  12. package/dist/chunk-Bx3C2hgW.cjs +0 -38
  13. package/dist/clients/axios.cjs +0 -68
  14. package/dist/clients/axios.cjs.map +0 -1
  15. package/dist/clients/axios.d.ts +0 -49
  16. package/dist/clients/axios.js +0 -58
  17. package/dist/clients/axios.js.map +0 -1
  18. package/dist/clients/fetch.cjs +0 -128
  19. package/dist/clients/fetch.cjs.map +0 -1
  20. package/dist/clients/fetch.d.ts +0 -50
  21. package/dist/clients/fetch.js +0 -120
  22. package/dist/clients/fetch.js.map +0 -1
  23. package/dist/templates/clients/axios.source.cjs +0 -7
  24. package/dist/templates/clients/axios.source.cjs.map +0 -1
  25. package/dist/templates/clients/axios.source.d.ts +0 -7
  26. package/dist/templates/clients/axios.source.js +0 -6
  27. package/dist/templates/clients/axios.source.js.map +0 -1
  28. package/dist/templates/clients/fetch.source.cjs +0 -7
  29. package/dist/templates/clients/fetch.source.cjs.map +0 -1
  30. package/dist/templates/clients/fetch.source.d.ts +0 -7
  31. package/dist/templates/clients/fetch.source.js +0 -6
  32. package/dist/templates/clients/fetch.source.js.map +0 -1
  33. package/dist/templates/config.source.cjs +0 -7
  34. package/dist/templates/config.source.cjs.map +0 -1
  35. package/dist/templates/config.source.d.ts +0 -7
  36. package/dist/templates/config.source.js +0 -6
  37. package/dist/templates/config.source.js.map +0 -1
  38. package/src/clients/axios.ts +0 -113
  39. package/src/clients/fetch.ts +0 -201
  40. package/src/components/ClassClient.tsx +0 -161
  41. package/src/components/Client.tsx +0 -281
  42. package/src/components/StaticClassClient.tsx +0 -153
  43. package/src/components/Url.tsx +0 -90
  44. package/src/components/WrapperClient.tsx +0 -33
  45. package/src/functionParams.ts +0 -118
  46. package/src/generators/classClientGenerator.tsx +0 -261
  47. package/src/generators/groupedClientGenerator.tsx +0 -82
  48. package/src/generators/operationsGenerator.ts +0 -47
  49. package/src/generators/staticClassClientGenerator.tsx +0 -240
  50. package/src/resolvers/resolverClient.ts +0 -46
  51. package/src/templates/clients/axios.source.ts +0 -4
  52. package/src/templates/clients/fetch.source.ts +0 -4
  53. package/src/templates/config.source.ts +0 -4
  54. package/src/utils.ts +0 -259
  55. /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.cjs CHANGED
@@ -2,19 +2,241 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_chunk = require("./chunk-Bx3C2hgW.cjs");
6
- const require_templates_clients_axios_source = require("./templates/clients/axios.source.cjs");
7
- const require_templates_clients_fetch_source = require("./templates/clients/fetch.source.cjs");
8
- const require_templates_config_source = require("./templates/config.source.cjs");
5
+ //#region \0rolldown/runtime.js
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14
+ key = keys[i];
15
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: ((k) => from[k]).bind(null, key),
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+ //#endregion
27
+ let kubb_kit = require("kubb/kit");
9
28
  let node_path = require("node:path");
10
- let node_path$1 = require_chunk.__toESM(node_path, 1);
11
- node_path = require_chunk.__toESM(node_path);
12
- let _kubb_core = require("@kubb/core");
13
- let _kubb_ast_utils = require("@kubb/ast/utils");
29
+ node_path = __toESM(node_path, 1);
14
30
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
15
- let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
16
- let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
17
31
  let _kubb_plugin_zod = require("@kubb/plugin-zod");
32
+ let kubb_jsx = require("kubb/jsx");
33
+ let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
34
+ //#region ../../internals/shared/src/params.ts
35
+ /**
36
+ * Drops parameters that share the same name, keeping the first.
37
+ *
38
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
39
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
40
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
41
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
42
+ */
43
+ function dedupeParams(params) {
44
+ const seen = /* @__PURE__ */ new Set();
45
+ return params.filter((param) => {
46
+ if (seen.has(param.name)) return false;
47
+ seen.add(param.name);
48
+ return true;
49
+ });
50
+ }
51
+ //#endregion
52
+ //#region ../../internals/shared/src/operation.ts
53
+ /**
54
+ * Builds the `ResolverFileParams` every operation generator passes to
55
+ * `resolver.file`: a file named `name`, tagged by the operation's first
56
+ * tag (or `'default'`), at the operation's path. Centralizes the entry object
57
+ * that was repeated at dozens of call sites across the client and query plugins.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
62
+ * ```
63
+ */
64
+ function operationFileEntry(node, name, extname = ".ts") {
65
+ return {
66
+ name,
67
+ extname,
68
+ tag: node.tags[0] ?? "default",
69
+ path: node.path
70
+ };
71
+ }
72
+ /**
73
+ * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
74
+ * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
75
+ * same dependency for the same operation in one pass (a query plugin's several hook generators, the
76
+ * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
77
+ *
78
+ * @example Cache `plugin-ts`'s file for the current operation
79
+ * ```ts
80
+ * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
81
+ * ```
82
+ */
83
+ function resolveDependencyOperationFile(options) {
84
+ const { cache, node, resolver, root, output, group } = options;
85
+ return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
86
+ ...operationFileEntry(node, node.operationId),
87
+ root,
88
+ output,
89
+ group: group ?? void 0
90
+ }));
91
+ }
92
+ function getOperationLink(node, link) {
93
+ if (!link) return null;
94
+ if (typeof link === "function") return link(node) ?? null;
95
+ return node.path ? `{@link ${kubb_kit.Url.toPath(node.path)}}` : null;
96
+ }
97
+ /**
98
+ * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
99
+ * are present and the union, default, and form-data flags the client uses to pick one.
100
+ */
101
+ function buildContentTypeInfo(contentTypes) {
102
+ const isMultipleContentTypes = contentTypes.length > 1;
103
+ return {
104
+ contentTypes,
105
+ isMultipleContentTypes,
106
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
107
+ defaultContentType: contentTypes[0] ?? "application/json",
108
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
109
+ };
110
+ }
111
+ function getContentTypeInfo(node) {
112
+ return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? []);
113
+ }
114
+ /**
115
+ * The request-body counterpart for the primary success response: the content types it documents and
116
+ * whether several are present, so the client can let a caller pick which one to accept.
117
+ */
118
+ function getResponseContentTypeInfo(node) {
119
+ return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
120
+ }
121
+ /**
122
+ * Reads the single base content type of an operation's primary success response, lowercased and
123
+ * stripped of any `; charset=...` suffix. Returns `undefined` when the response declares zero or
124
+ * more than one content type, since neither case has a single type to act on.
125
+ */
126
+ function getPrimarySuccessContentType(node) {
127
+ const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? [];
128
+ if (contentTypes.length !== 1) return void 0;
129
+ return contentTypes[0].split(";")[0].trim().toLowerCase();
130
+ }
131
+ /**
132
+ * Whether an operation streams its primary success response as Server-Sent Events
133
+ * (`text/event-stream`). The client generator uses this to return a typed event stream instead of a
134
+ * one-shot `RequestResult`.
135
+ */
136
+ function isEventStream(node) {
137
+ return getPrimarySuccessContentType(node) === "text/event-stream";
138
+ }
139
+ /**
140
+ * Derives the default `responseType` for an operation from its primary success response.
141
+ *
142
+ * Returns a value only when that response declares a single non-JSON content type. `text/event-stream`
143
+ * and other binary types (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`,
144
+ * `video/*`) map to a stream or `'blob'`, and other `text/*` maps to `'text'`. Otherwise `undefined`,
145
+ * leaving the runtime client's `Content-Type` auto-detection in charge.
146
+ */
147
+ function getResponseType(node) {
148
+ const baseType = getPrimarySuccessContentType(node);
149
+ if (!baseType) return void 0;
150
+ if (baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json") return void 0;
151
+ if (baseType === "text/event-stream") return "stream";
152
+ if (baseType.startsWith("text/")) return "text";
153
+ if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
154
+ }
155
+ /**
156
+ * Which of the grouped request options an operation carries.
157
+ */
158
+ function getRequestGroups(node) {
159
+ const { path, query, header } = getOperationParameters(node);
160
+ return {
161
+ path: path.length > 0,
162
+ query: query.length > 0,
163
+ body: Boolean(node.requestBody?.content?.[0]?.schema),
164
+ headers: header.length > 0
165
+ };
166
+ }
167
+ /**
168
+ * Resolves which grouped request options an operation carries together with whether each group
169
+ * holds a required member. The grouped parameter stays optional only when nothing inside it is
170
+ * required, matching the generated `RequestConfig` type.
171
+ */
172
+ function getRequestGroupOptionality(node) {
173
+ const groups = getRequestGroups(node);
174
+ const { path, query, header } = getOperationParameters(node);
175
+ const hasRequiredPath = path.some((param) => param.required);
176
+ const hasRequiredQuery = query.some((param) => param.required);
177
+ const hasRequiredHeader = header.some((param) => param.required);
178
+ return {
179
+ groups,
180
+ hasRequiredPath,
181
+ hasRequiredQuery,
182
+ hasRequiredHeader,
183
+ isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body
184
+ };
185
+ }
186
+ function buildOperationComments(node, options = {}) {
187
+ const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
188
+ const linkComment = getOperationLink(node, link);
189
+ const filteredComments = (linkPosition === "beforeDeprecated" ? [
190
+ node.description && `@description ${node.description}`,
191
+ node.summary && `@summary ${node.summary}`,
192
+ linkComment,
193
+ node.deprecated && "@deprecated"
194
+ ] : [
195
+ node.description && `@description ${node.description}`,
196
+ node.summary && `@summary ${node.summary}`,
197
+ node.deprecated && "@deprecated",
198
+ linkComment
199
+ ]).filter((comment) => Boolean(comment));
200
+ if (!splitLines) return filteredComments;
201
+ return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
202
+ }
203
+ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
204
+ /**
205
+ * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
206
+ * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
207
+ * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
208
+ * same parameters once per plugin.
209
+ */
210
+ function getOperationParameters(node) {
211
+ const cached = operationParameterGroupsByNode.get(node);
212
+ if (cached) return cached;
213
+ const groups = {
214
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
215
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
216
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
217
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
218
+ };
219
+ operationParameterGroupsByNode.set(node, groups);
220
+ return groups;
221
+ }
222
+ function getStatusCodeNumber(statusCode) {
223
+ const code = Number(statusCode);
224
+ return Number.isNaN(code) ? null : code;
225
+ }
226
+ function isSuccessStatusCode(statusCode) {
227
+ const code = getStatusCodeNumber(statusCode);
228
+ return code !== null && code >= 200 && code < 300;
229
+ }
230
+ function getSuccessResponses(responses) {
231
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
232
+ }
233
+ function getOperationSuccessResponses(node) {
234
+ return getSuccessResponses(node.responses);
235
+ }
236
+ function getPrimarySuccessResponse(node) {
237
+ return getOperationSuccessResponses(node)[0] ?? null;
238
+ }
239
+ //#endregion
18
240
  //#region ../../internals/utils/src/casing.ts
19
241
  /**
20
242
  * Shared implementation for camelCase and PascalCase conversion.
@@ -54,37 +276,12 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
54
276
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
55
277
  }
56
278
  //#endregion
57
- //#region ../../internals/utils/src/fs.ts
58
- /**
59
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
60
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
61
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
62
- *
63
- * Empty segments are dropped before joining. They arise when the name starts with a dot
64
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
65
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
66
- * absolute path, letting generated files escape the configured output directory.
67
- *
68
- * @example Nested path from a dotted name
69
- * `toFilePath('pet.petId') // 'pet/petId'`
70
- *
71
- * @example PascalCase the final segment
72
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
73
- *
74
- * @example Suffix applied to the final segment only
75
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
76
- */
77
- function toFilePath(name, caseLast = camelCase) {
78
- const parts = name.split(/\.(?=[a-zA-Z])/);
79
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
80
- }
81
- //#endregion
82
279
  //#region ../../internals/utils/src/reserved.ts
83
280
  /**
84
281
  * JavaScript and Java reserved words.
85
282
  * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
86
283
  */
87
- const reservedWords = new Set([
284
+ const reservedWords = /* @__PURE__ */ new Set([
88
285
  "abstract",
89
286
  "arguments",
90
287
  "boolean",
@@ -179,7 +376,7 @@ const reservedWords = new Set([
179
376
  */
180
377
  function isValidVarName(name) {
181
378
  if (!name || reservedWords.has(name)) return false;
182
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
379
+ return isIdentifier(name);
183
380
  }
184
381
  /**
185
382
  * Returns `name` when it's a syntactically valid JavaScript variable name,
@@ -201,252 +398,21 @@ function ensureValidVarName(name) {
201
398
  if (!name || isValidVarName(name)) return name;
202
399
  return `_${name}`;
203
400
  }
204
- //#endregion
205
- //#region ../../internals/utils/src/url.ts
206
- function transformParam(raw, casing) {
207
- const param = isValidVarName(raw) ? raw : camelCase(raw);
208
- return casing === "camelcase" ? camelCase(param) : param;
209
- }
210
- function toParamsObject(path, { replacer, casing } = {}) {
211
- const params = {};
212
- for (const match of path.matchAll(/\{([^}]+)\}/g)) {
213
- const param = transformParam(match[1], casing);
214
- const key = replacer ? replacer(param) : param;
215
- params[key] = key;
216
- }
217
- return Object.keys(params).length > 0 ? params : null;
218
- }
219
- /**
220
- * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
221
- */
222
- var Url$1 = class Url$1 {
223
- static {
224
- require_chunk.__name(this, "Url");
225
- }
226
- /**
227
- * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
228
- *
229
- * @example
230
- * Url.canParse('https://petstore.swagger.io/v2') // true
231
- * Url.canParse('/pet/{petId}') // false
232
- */
233
- static canParse(url, base) {
234
- return URL.canParse(url, base);
235
- }
236
- /**
237
- * Converts an OpenAPI/Swagger path to Express-style colon syntax.
238
- *
239
- * @example
240
- * Url.toPath('/pet/{petId}') // '/pet/:petId'
241
- */
242
- static toPath(path) {
243
- return path.replace(/\{([^}]+)\}/g, ":$1");
244
- }
245
- /**
246
- * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
247
- * `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
248
- * and `casing` controls parameter identifier casing.
249
- *
250
- * @example
251
- * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
252
- *
253
- * @example
254
- * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
255
- */
256
- static toTemplateString(path, { prefix, replacer, casing } = {}) {
257
- const result = path.split(/\{([^}]+)\}/).map((part, i) => {
258
- if (i % 2 === 0) return part;
259
- const param = transformParam(part, casing);
260
- return `\${${replacer ? replacer(param) : param}}`;
261
- }).join("");
262
- return `\`${prefix ?? ""}${result}\``;
263
- }
264
- /**
265
- * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
266
- * expression when `stringify` is set.
267
- *
268
- * @example
269
- * Url.toObject('/pet/{petId}')
270
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
271
- */
272
- static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
273
- const object = {
274
- url: type === "path" ? Url$1.toPath(path) : Url$1.toTemplateString(path, {
275
- replacer,
276
- casing
277
- }),
278
- params: toParamsObject(path, {
279
- replacer,
280
- casing
281
- })
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
- //#endregion
292
- //#region ../../internals/shared/src/operation.ts
293
401
  /**
294
- * Builds the `ResolverFileParams` every operation generator passes to
295
- * `resolver.resolveFile`: a file named `name`, tagged by the operation's first
296
- * tag (or `'default'`), at the operation's path. Centralizes the entry object
297
- * that was repeated at dozens of call sites across the client and query plugins.
402
+ * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
403
+ *
404
+ * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
405
+ * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
406
+ * deciding whether an object key needs quoting.
298
407
  *
299
408
  * @example
300
409
  * ```ts
301
- * resolver.resolveFile(operationFileEntry(node, node.operationId), { root, output, group })
410
+ * isIdentifier('name') // true
411
+ * isIdentifier('x-total')// false
302
412
  * ```
303
413
  */
304
- function operationFileEntry(node, name, extname = ".ts") {
305
- return {
306
- name,
307
- extname,
308
- tag: node.tags[0] ?? "default",
309
- path: node.path
310
- };
311
- }
312
- function getOperationLink(node, link) {
313
- if (!link) return null;
314
- if (typeof link === "function") return link(node) ?? null;
315
- if (link === "urlPath") return node.path ? `{@link ${Url$1.toPath(node.path)}}` : null;
316
- return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
317
- }
318
- function getContentTypeInfo(node) {
319
- const contentTypes = node.requestBody?.content?.map((e) => e.contentType) ?? [];
320
- const isMultipleContentTypes = contentTypes.length > 1;
321
- return {
322
- contentTypes,
323
- isMultipleContentTypes,
324
- contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
325
- defaultContentType: contentTypes[0] ?? "application/json",
326
- hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
327
- };
328
- }
329
- /**
330
- * Derives the default `responseType` for an operation from its primary success response.
331
- *
332
- * Returns a value only when that response declares a single non-JSON content type — a binary type
333
- * (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`, `video/*`) maps to `'blob'`
334
- * and other `text/*` maps to `'text'`. Otherwise `undefined`, leaving the runtime client's
335
- * `Content-Type` auto-detection in charge.
336
- */
337
- function getResponseType(node) {
338
- const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? [];
339
- if (contentTypes.length !== 1) return void 0;
340
- const baseType = contentTypes[0].split(";")[0].trim().toLowerCase();
341
- if (baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json") return void 0;
342
- if (baseType.startsWith("text/")) return "text";
343
- if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
344
- }
345
- function buildRequestConfigType(node, resolver) {
346
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null;
347
- const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
348
- return `${requestName ? `Partial<RequestConfig<${requestName}>>` : "Partial<RequestConfig>"} & { ${["client?: Client", isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null].filter(Boolean).join("; ")} }`;
349
- }
350
- function buildOperationComments(node, options = {}) {
351
- const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
352
- const linkComment = getOperationLink(node, link);
353
- const filteredComments = (linkPosition === "beforeDeprecated" ? [
354
- node.description && `@description ${node.description}`,
355
- node.summary && `@summary ${node.summary}`,
356
- linkComment,
357
- node.deprecated && "@deprecated"
358
- ] : [
359
- node.description && `@description ${node.description}`,
360
- node.summary && `@summary ${node.summary}`,
361
- node.deprecated && "@deprecated",
362
- linkComment
363
- ]).filter((comment) => Boolean(comment));
364
- if (!splitLines) return filteredComments;
365
- return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
366
- }
367
- function getOperationParameters(node, options = {}) {
368
- const params = _kubb_core.ast.caseParams(node.parameters, options.paramsCasing);
369
- return {
370
- path: params.filter((param) => param.in === "path"),
371
- query: params.filter((param) => param.in === "query"),
372
- header: params.filter((param) => param.in === "header"),
373
- cookie: params.filter((param) => param.in === "cookie")
374
- };
375
- }
376
- function getStatusCodeNumber(statusCode) {
377
- const code = Number(statusCode);
378
- return Number.isNaN(code) ? null : code;
379
- }
380
- function isSuccessStatusCode(statusCode) {
381
- const code = getStatusCodeNumber(statusCode);
382
- return code !== null && code >= 200 && code < 300;
383
- }
384
- function isErrorStatusCode(statusCode) {
385
- const code = getStatusCodeNumber(statusCode);
386
- return code !== null && code >= 400;
387
- }
388
- function getSuccessResponses(responses) {
389
- return responses.filter((response) => isSuccessStatusCode(response.statusCode));
390
- }
391
- function getOperationSuccessResponses(node) {
392
- return getSuccessResponses(node.responses);
393
- }
394
- function getPrimarySuccessResponse(node) {
395
- return getOperationSuccessResponses(node)[0] ?? null;
396
- }
397
- function resolveErrorNames(node, resolver) {
398
- return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
399
- }
400
- function resolveSuccessNames(node, resolver) {
401
- return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
402
- }
403
- function resolveStatusCodeNames(node, resolver) {
404
- return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
405
- }
406
- /**
407
- * Builds the discriminated union type string for `dataReturnType: 'full'` return shapes.
408
- * Each member is `{ status: N; data: StatusNType; statusText: string }`.
409
- */
410
- function buildStatusUnionType(node, resolver) {
411
- const members = node.responses.map((r) => {
412
- const typeName = resolver.resolveResponseStatusName(node, r.statusCode);
413
- const statusCode = Number.parseInt(r.statusCode, 10);
414
- return `{ status: ${Number.isNaN(statusCode) ? "number" : String(statusCode)}; data: ${typeName}; statusText: string }`;
415
- });
416
- if (members.length === 1) return members[0];
417
- return `(${members.join(" | ")})`;
418
- }
419
- const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
420
- function resolveOperationTypeNames(node, resolver, options = {}) {
421
- const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
422
- let byResolver = typeNamesByResolver.get(resolver);
423
- if (byResolver) {
424
- const cached = byResolver.get(cacheKey);
425
- if (cached) return cached;
426
- } else {
427
- byResolver = /* @__PURE__ */ new Map();
428
- typeNamesByResolver.set(resolver, byResolver);
429
- }
430
- const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
431
- const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
432
- const exclude = new Set(options.exclude ?? []);
433
- const paramNames = [
434
- ...path.map((param) => resolver.resolvePathParamsName(node, param)),
435
- ...query.map((param) => resolver.resolveQueryParamsName(node, param)),
436
- ...header.map((param) => resolver.resolveHeaderParamsName(node, param))
437
- ];
438
- const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
439
- const result = (options.order === "body-response-first" ? [
440
- ...bodyAndResponseNames,
441
- ...paramNames,
442
- ...responseStatusNames
443
- ] : [
444
- ...paramNames,
445
- ...bodyAndResponseNames,
446
- ...responseStatusNames
447
- ]).filter((name) => Boolean(name) && !exclude.has(name));
448
- byResolver.set(cacheKey, result);
449
- return result;
414
+ function isIdentifier(name) {
415
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
450
416
  }
451
417
  //#endregion
452
418
  //#region ../../internals/shared/src/group.ts
@@ -480,1612 +446,655 @@ function createGroupConfig(group) {
480
446
  };
481
447
  }
482
448
  //#endregion
483
- //#region ../../internals/shared/src/params.ts
484
- function buildParamsMapping(originalParams, mappedParams) {
485
- const mapping = {};
486
- let hasChanged = false;
487
- originalParams.forEach((param, i) => {
488
- const mappedName = mappedParams[i]?.name ?? param.name;
489
- mapping[param.name] = mappedName;
490
- if (param.name !== mappedName) hasChanged = true;
449
+ //#region ../../internals/client/src/builders/generics.ts
450
+ /**
451
+ * Builds the `RequestResult` generic arguments for one operation: the per-status responses record
452
+ * plus the per-call `ThrowOnError` flag. `SuccessOf` / `ErrorOf` split the record inside the
453
+ * runtime, so this only names the record and threads `ThrowOnError`.
454
+ *
455
+ * @example
456
+ * `buildRequestResultGenerics({ node, types }) // 'AddPetResponses, ThrowOnError'`
457
+ */
458
+ function buildRequestResultGenerics({ node, types }) {
459
+ return `${types.response.responses(node)}, ThrowOnError`;
460
+ }
461
+ /**
462
+ * Builds the full return type an operation's function signature uses: `Unwrappable<RequestResult>`
463
+ * for the default `returnType: 'full'`, already a promise so no further wrapping is needed, a plain
464
+ * `Promise<RequestResult>` for the internal `'plain'` shape, or a
465
+ * `Promise` of the runtime's `UnwrappedResult` when `returnType: 'data'` narrows a resolved call
466
+ * down to the bare success body.
467
+ *
468
+ * @example
469
+ * `buildResultType({ node, types, returnType: 'data' }) // 'Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>'`
470
+ */
471
+ function buildResultType({ node, types, returnType }) {
472
+ const generics = buildRequestResultGenerics({
473
+ node,
474
+ types
491
475
  });
492
- return hasChanged ? mapping : null;
476
+ if (returnType === "data") return `Promise<UnwrappedResult<${generics}>>`;
477
+ if (returnType === "plain") return `Promise<RequestResult<${generics}>>`;
478
+ return `Unwrappable<RequestResult<${generics}>>`;
493
479
  }
494
480
  //#endregion
495
- //#region src/functionParams.ts
496
- const declarationPrinter$4 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
497
- const callPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "call" });
498
- function isGroup(spec) {
499
- return "children" in spec;
481
+ //#region ../../internals/client/src/builders/returnStatement.ts
482
+ /**
483
+ * Builds the return statement of a generated operation function. With the default
484
+ * `returnType: 'full'` the runtime call already resolves to `{ data, error, request, response }`.
485
+ * The generated code casts that result to the operation's `RequestResult`, then wraps it with
486
+ * `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
487
+ * body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
488
+ * which narrows the resolved value down to the bare success body the same way `RequestResult`
489
+ * already does, using the plugin default when the call leaves `throwOnError` unset.
490
+ * With `returnType: 'plain'` it returns the runtime call as is, cast to `RequestResult`, with no helper.
491
+ *
492
+ * Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
493
+ * as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
494
+ * straight to `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
495
+ * rejects that two-generic swap even where `A` and `B` on their own would satisfy it.
496
+ *
497
+ * @example
498
+ * `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
499
+ * @example
500
+ * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
501
+ * @example
502
+ * `return request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>`
503
+ */
504
+ function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
505
+ if (returnType === "data") return `return unwrapResult(request(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
506
+ node,
507
+ types,
508
+ returnType
509
+ })}`;
510
+ if (returnType === "plain") return `return request(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
511
+ node,
512
+ types
513
+ })}>>`;
514
+ return `return withUnwrap(request(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
515
+ node,
516
+ types
517
+ })}>>)`;
500
518
  }
501
- function createType(type) {
502
- return type ? _kubb_core.ast.createParamsType({
503
- variant: "reference",
504
- name: type
505
- }) : null;
506
- }
507
- function createDeclarationLeaf(name, spec) {
508
- if (spec.default !== void 0) return _kubb_core.ast.createFunctionParameter({
509
- name,
510
- type: createType(spec.type) ?? void 0,
511
- default: spec.default,
512
- rest: spec.mode === "inlineSpread"
513
- });
514
- return _kubb_core.ast.createFunctionParameter({
515
- name,
516
- type: createType(spec.type) ?? void 0,
517
- optional: !!spec.optional,
518
- rest: spec.mode === "inlineSpread"
519
- });
520
- }
521
- function createDeclarationParam(name, spec) {
522
- if (isGroup(spec)) return _kubb_core.ast.createParameterGroup({
523
- inline: spec.mode === "inlineSpread",
524
- default: spec.default,
525
- properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => createDeclarationLeaf(childName, child))
526
- });
527
- return createDeclarationLeaf(name, spec);
528
- }
529
- function createCallParam(name, spec) {
530
- if (isGroup(spec)) return _kubb_core.ast.createParameterGroup({
531
- inline: spec.mode === "inlineSpread",
532
- properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => _kubb_core.ast.createFunctionParameter({
533
- name: child?.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child?.value ? `${childName}: ${child.value}` : childName,
534
- rest: spec.mode === "inlineSpread" && child?.mode === "inlineSpread"
535
- }))
536
- });
537
- return _kubb_core.ast.createFunctionParameter({
538
- name: spec.value ?? name,
539
- rest: spec.mode === "inlineSpread"
540
- });
519
+ //#endregion
520
+ //#region ../../internals/client/src/builders/security.ts
521
+ function serializeAuth(auth) {
522
+ const parts = [`type: '${auth.type}'`];
523
+ if (auth.scheme) parts.push(`scheme: '${auth.scheme}'`);
524
+ if (auth.name) parts.push(`name: '${auth.name}'`);
525
+ if (auth.in) parts.push(`in: '${auth.in}'`);
526
+ return `{ ${parts.join(", ")} }`;
541
527
  }
542
528
  /**
543
- * Creates function parameter builders for generating function signatures and calls.
544
- * Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
529
+ * Maps an OpenAPI security scheme to the inline `Auth` object, or `null` when the runtime cannot
530
+ * place it (an unresolved `$ref`, or an `apiKey` without a name or outside `header` / `query` /
531
+ * `cookie`). `http` schemes other than `basic` are treated as bearer.
545
532
  */
546
- function createFunctionParams(params) {
547
- const entries = Object.entries(params).filter(([, spec]) => spec != null);
548
- return {
549
- toConstructor() {
550
- return declarationPrinter$4.print(_kubb_core.ast.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
551
- },
552
- toCall() {
553
- return callPrinter.print(_kubb_core.ast.createFunctionParameters({ params: entries.map(([name, spec]) => createCallParam(name, spec)) })) ?? "";
554
- }
533
+ function resolveSecurityScheme(scheme) {
534
+ if (!scheme || "$ref" in scheme) return null;
535
+ if (scheme.type === "apiKey") {
536
+ if (!scheme.name || scheme.in !== "header" && scheme.in !== "query" && scheme.in !== "cookie") return null;
537
+ return {
538
+ type: "apiKey",
539
+ name: scheme.name,
540
+ in: scheme.in
541
+ };
542
+ }
543
+ if (scheme.type === "http") return {
544
+ type: "http",
545
+ scheme: scheme.scheme?.toLowerCase() === "basic" ? "basic" : "bearer"
555
546
  };
547
+ if (scheme.type === "oauth2") return { type: "oauth2" };
548
+ if (scheme.type === "openIdConnect") return { type: "openIdConnect" };
549
+ return null;
556
550
  }
557
- //#endregion
558
- //#region src/utils.ts
559
551
  /**
560
- * Returns `true` when any direction of the parser uses Zod (used for dependency checks).
552
+ * Derives the per-operation security metadata from the OpenAPI document. The operation's own
553
+ * `security` overrides the global `security` (an explicit empty array disables auth), and every
554
+ * referenced scheme is resolved from `components.securitySchemes` into a flat, de-duplicated list of
555
+ * `Auth` objects the runtime walks in order.
556
+ *
557
+ * @example
558
+ * `getOperationSecurity({ document, method: 'POST', path: '/pet' })`
559
+ * `// [{ type: 'http', scheme: 'bearer' }]`
561
560
  */
562
- function isParserEnabled(parser) {
563
- if (!parser) return false;
564
- if (parser === "zod") return true;
565
- return !!(parser.request || parser.response);
561
+ function getOperationSecurity({ document, method, path }) {
562
+ if (!document) return void 0;
563
+ const requirements = (document.paths?.[path]?.[method.toLowerCase()])?.security ?? document.security;
564
+ if (!requirements?.length) return void 0;
565
+ const definitions = document.components?.securitySchemes ?? {};
566
+ const security = [];
567
+ const seen = /* @__PURE__ */ new Set();
568
+ for (const requirement of requirements) for (const schemeName of Object.keys(requirement)) {
569
+ if (seen.has(schemeName)) continue;
570
+ seen.add(schemeName);
571
+ const auth = resolveSecurityScheme(definitions[schemeName]);
572
+ if (auth) security.push(auth);
573
+ }
574
+ return security.length ? security : void 0;
566
575
  }
567
576
  /**
568
- * Returns `'zod'` when request body parsing is enabled, `null` otherwise.
569
- * The string shorthand `'zod'` also enables request body parsing (existing behavior).
577
+ * Serializes the per-operation security into the literal emitted on each generated call's `security`
578
+ * field. The runtime `resolveAuth` helper walks it, calling the configured `auth` resolver per entry.
579
+ *
580
+ * @example
581
+ * `buildSecurityMetadata({ security: [{ type: 'http', scheme: 'bearer' }] }) // "[{ type: 'http', scheme: 'bearer' }]"`
570
582
  */
571
- function resolveRequestParser(parser) {
572
- if (!parser) return null;
573
- if (parser === "zod") return "zod";
574
- return parser.request ?? null;
583
+ function buildSecurityMetadata({ security }) {
584
+ if (!security?.length) return null;
585
+ return `[${security.map(serializeAuth).join(", ")}]`;
575
586
  }
587
+ //#endregion
588
+ //#region ../../internals/client/src/builders/signature.ts
589
+ const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
576
590
  /**
577
- * Returns `'zod'` when query-parameters parsing is enabled, `null` otherwise.
578
- * Only the object form `{ request: 'zod' }` enables query-params parsing.
579
- * The string shorthand `'zod'` does not, preserving its existing behavior.
591
+ * Builds the grouped-options signature for one operation: a single `options` object whose `TData`
592
+ * is the `<Name>Options` type, and a `RequestResult` return type keyed to the per-status responses
593
+ * record. There are no positional arguments.
594
+ *
595
+ * The generated file imports `<Name>Options` and `<Name>Responses` and uses them directly, so no
596
+ * per-operation input type has to be emitted. Both names come from `types`, which is `plugin-ts` or
597
+ * `plugin-zod`'s inferred types (see `resolveOperationTypes`).
580
598
  */
581
- function resolveQueryParamsParser(parser) {
582
- if (!parser || parser === "zod") return null;
583
- return parser.request ?? null;
599
+ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault }) {
600
+ const optionsName = types.response.options(node);
601
+ const { isOptional } = getRequestGroupOptionality(node);
602
+ return {
603
+ paramsSignature: declarationPrinter.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
604
+ name: "options",
605
+ type: `Options<${optionsName}, ThrowOnError>`,
606
+ ...isOptional ? { default: "{}" } : {}
607
+ })] })) ?? "",
608
+ returnType: buildResultType({
609
+ node,
610
+ types,
611
+ returnType
612
+ }),
613
+ generics: [`ThrowOnError extends boolean = ${throwOnErrorDefault}`]
614
+ };
584
615
  }
616
+ //#endregion
617
+ //#region ../../internals/client/src/builders/validatorOptions.ts
585
618
  /**
586
- * Returns `'zod'` when response-direction parsing is enabled, `null` otherwise.
587
- * `parser: 'zod'` (string shorthand) maps to response parsing and returns `'zod'`.
619
+ * Returns `'zod'` when request body parsing is enabled, `null` otherwise. The string shorthand
620
+ * `'zod'` validates the response only, so it does not enable request parsing.
588
621
  */
589
- function resolveResponseParser(parser) {
590
- if (!parser) return null;
591
- if (parser === "zod") return "zod";
592
- return parser.response ?? null;
622
+ function resolveRequestValidator(validator) {
623
+ if (!validator || validator === "zod") return null;
624
+ return validator.request ?? null;
593
625
  }
594
626
  /**
595
- * Builds HTTP headers array for a client request.
596
- * Includes Content-Type (if not default) and spreads header parameters if present.
627
+ * Returns `'zod'` when response parsing is enabled, `null` otherwise. The string shorthand `'zod'`
628
+ * maps to response parsing.
597
629
  */
598
- function buildHeaders(contentType, hasHeaderParams) {
599
- return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
630
+ function resolveResponseValidator(validator) {
631
+ if (!validator) return null;
632
+ if (validator === "zod") return "zod";
633
+ return validator.response ?? null;
600
634
  }
601
635
  /**
602
- * Returns the generic type arguments — response, error, and request body — for a generated
603
- * client call.
604
- *
605
- * When `dataReturnType` is `'full'`, the response generic widens to a union of all documented
606
- * status types. When `parser` is `'zod'` and a request body schema exists, the request type
607
- * uses `z.output<typeof schema>` to reflect post-transform types (e.g. date coercion).
636
+ * Resolves the zod expression a generated client validates a success response with. Only success
637
+ * (2xx) bodies reach the parse under the throw-on-error contract, so the success-only
638
+ * `<operation>ResponseSchema` is used; error bodies are never zod-parsed.
608
639
  */
609
- function buildGenerics(node, tsResolver, options = {}) {
610
- const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
611
- const successNames = resolveSuccessNames(node, tsResolver);
612
- const responseName = options.dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : tsResolver.resolveResponseName(node) : successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
613
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
614
- const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
615
- const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
616
- const zodRequestName = options.parser === "zod" && options.zodResolver && node.requestBody?.content?.[0]?.schema ? options.zodResolver.resolveDataName?.(node) ?? null : null;
617
- return [
618
- responseName,
619
- TError,
620
- zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
621
- ].filter(Boolean);
640
+ function buildZodResponseParse(node, zodResolver) {
641
+ const name = zodResolver.response.response(node);
642
+ return name ? {
643
+ expression: name,
644
+ importNames: [name]
645
+ } : null;
622
646
  }
623
647
  /**
624
- * Builds the parameters object for a class-based client method.
625
- * Includes URL, method, base URL, headers, and request/response data.
648
+ * Resolves the zod expression a generated client validates an error body with on the non-throw path.
649
+ * Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
650
+ * operation documents no error responses with a schema.
626
651
  */
627
- function buildClassClientParams({ node, url, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers, zodQueryParamsName }) {
628
- const { query: queryParams } = getOperationParameters(node);
629
- const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
630
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
631
- const responseType = getResponseType(node);
632
- return createFunctionParams({ config: {
633
- mode: "object",
634
- children: {
635
- requestConfig: { mode: "inlineSpread" },
636
- method: { value: JSON.stringify(_kubb_core.ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
637
- url: { value: url },
638
- baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
639
- params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : {} : null,
640
- data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
641
- contentType: isMultipleContentTypes ? {} : null,
642
- responseType: responseType ? { value: JSON.stringify(responseType) } : null,
643
- headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
644
- }
645
- } });
652
+ function buildZodErrorParse(node, zodResolver) {
653
+ if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
654
+ const name = zodResolver.response.error?.(node);
655
+ return name ? {
656
+ expression: name,
657
+ importNames: [name]
658
+ } : null;
646
659
  }
660
+ //#endregion
661
+ //#region ../../internals/client/src/builders/validator.ts
647
662
  /**
648
- * Builds the request data parsing line for client methods.
649
- * Applies Zod validation when `parser.request === 'zod'`, otherwise assigns data directly.
663
+ * Builds the validator-hook references for one operation. Request validation runs before the send;
664
+ * response validation runs on the success body only. Returns `null` references when the matching
665
+ * direction is disabled or the schema is absent.
650
666
  */
651
- function buildRequestDataLine({ parser, node, zodResolver }) {
652
- const requestParser = resolveRequestParser(parser);
653
- const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
654
- if (requestParser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
655
- if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
656
- return "";
667
+ function buildValidatorHooks({ node, validator, zodResolver }) {
668
+ const importedZodNames = [];
669
+ const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
670
+ const zodRequestName = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) : null;
671
+ const request = zodRequestName ?? null;
672
+ if (zodRequestName) importedZodNames.push(zodRequestName);
673
+ const responseParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodResponseParse(node, zodResolver) : null;
674
+ const response = responseParse ? responseParse.expression : null;
675
+ if (responseParse) importedZodNames.push(...responseParse.importNames);
676
+ const errorParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver) : null;
677
+ const error = errorParse ? errorParse.expression : null;
678
+ if (errorParse) importedZodNames.push(...errorParse.importNames);
679
+ return {
680
+ request,
681
+ response,
682
+ error,
683
+ importedZodNames
684
+ };
657
685
  }
686
+ //#endregion
687
+ //#region ../../internals/client/src/builders/styles.ts
658
688
  /**
659
- * Builds the query parameters parsing line for client methods.
660
- * Returns an empty string when no query params exist or query-params parsing is not enabled.
661
- * Only the object form `parser: { request: 'zod' }` triggers this. `parser: 'zod'` does not.
689
+ * Renders a parameter name as an object-literal key, quoted when it is not a bare identifier.
690
+ * Path keys are camelCased to match the URL template placeholders. Query, header, and cookie keys
691
+ * keep the spec name, matching the remapped keys the runtime serializes.
662
692
  */
663
- function buildQueryParamsLine({ parser, node, zodResolver }) {
664
- if (resolveQueryParamsParser(parser) !== "zod" || !zodResolver) return "";
665
- const { query: queryParams } = getOperationParameters(node);
666
- if (queryParams.length === 0) return "";
667
- const zodQueryParamsName = zodResolver.resolveQueryParamsName?.(node, queryParams[0]);
668
- if (!zodQueryParamsName) return "";
669
- return `const requestParams = ${zodQueryParamsName}.parse(params)`;
693
+ function toKey(name, location) {
694
+ const key = location === "path" ? camelCase(name) : name;
695
+ return isValidVarName(key) ? key : JSON.stringify(key);
670
696
  }
671
697
  /**
672
- * Builds the form data conversion line for file upload requests.
673
- * Returns empty string if not applicable.
698
+ * Serializes one parameter's metadata into a `{ style, explode }` literal, or `null` when the
699
+ * parameter carries neither. Path and query carry the serialization `style`; header and cookie use a
700
+ * fixed style (`simple` and `form`), so only `explode` is emitted for them.
674
701
  */
675
- function buildFormDataLine(isFormData, hasRequest) {
676
- return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
702
+ function serializeParameter(parameter) {
703
+ const parts = [];
704
+ if ((parameter.in === "path" || parameter.in === "query") && parameter.style) parts.push(`style: '${parameter.style}'`);
705
+ if (parameter.explode !== void 0) parts.push(`explode: ${parameter.explode}`);
706
+ return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
677
707
  }
678
708
  /**
679
- * Builds the return statement for a client method.
680
- * When `dataReturnType` is `'full'`, casts the response to the status-discriminated union type.
681
- * When `parser.response` is `'zod'`, pipes the response body through the Zod schema before returning.
709
+ * Builds the per-operation `styles` literal from the operation's parameters, grouped by location.
710
+ * Path entries are keyed by the camelCased name to match the URL template placeholders; query,
711
+ * header, and cookie entries keep the spec name to match the keys the runtime serializes.
712
+ * Only parameters whose source defines `style` or `explode` are emitted, so calls without
713
+ * serialization metadata keep the runtime defaults and existing output is unchanged. Returns `null`
714
+ * when no parameter carries metadata.
715
+ *
716
+ * @example
717
+ * ```ts
718
+ * // a path param with { style: 'matrix', explode: true } and a query param with { explode: false }
719
+ * buildStyles({ node }) // "{ path: { id: { style: 'matrix', explode: true } }, query: { tags: { explode: false } } }"
720
+ * ```
682
721
  */
683
- function buildReturnStatement({ dataReturnType, parser, node, zodResolver, tsResolver }) {
684
- const responseParser = resolveResponseParser(parser);
685
- const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
686
- if (dataReturnType === "full" && tsResolver) {
687
- const unionType = buildStatusUnionType(node, tsResolver);
688
- if (responseParser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)} as ${unionType}`;
689
- return `return res as ${unionType}`;
722
+ function buildStyles({ node }) {
723
+ if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
724
+ const groups = {
725
+ path: [],
726
+ query: [],
727
+ header: [],
728
+ cookie: []
729
+ };
730
+ for (const parameter of node.parameters) {
731
+ const literal = serializeParameter(parameter);
732
+ if (!literal) continue;
733
+ groups[parameter.in].push(`${toKey(parameter.name, parameter.in)}: ${literal}`);
690
734
  }
691
- if (dataReturnType === "data" && responseParser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
692
- return "return res.data";
735
+ const locations = Object.keys(groups).filter((location) => groups[location].length > 0);
736
+ if (locations.length === 0) return null;
737
+ return `{ ${locations.map((location) => `${location}: { ${groups[location].join(", ")} }`).join(", ")} }`;
693
738
  }
694
739
  //#endregion
695
- //#region src/components/Url.tsx
696
- const declarationPrinter$3 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
697
- function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
698
- const urlNode = {
699
- ...node,
700
- parameters: node.parameters.filter((p) => p.in === "path"),
701
- requestBody: void 0
702
- };
703
- return _kubb_core.ast.createOperationParams(urlNode, {
704
- paramsType: paramsType === "object" ? "object" : "inline",
705
- pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
706
- paramsCasing,
707
- resolver: tsResolver
708
- });
709
- }
710
- function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
711
- if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
712
- const paramsNode = buildUrlParamsNode({
713
- paramsType,
714
- paramsCasing,
715
- pathParamsType,
740
+ //#region ../../internals/client/src/components/Operation.tsx
741
+ /**
742
+ * Renders one client operation: the grouped `<Name>Request` type and the function that forwards a
743
+ * single `options` object to the resolved client and returns the `Unwrappable<RequestResult>`. The
744
+ * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
745
+ * the source.
746
+ */
747
+ function Operation({ name, node, types, zodResolver, validator, returnType, throwOnErrorDefault, security, isExportable = true, isIndexable = true }) {
748
+ if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
749
+ const signature = buildGroupedOptionsSignature({
716
750
  node,
717
- tsResolver
718
- });
719
- const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
720
- const { path: originalPathParams } = getOperationParameters(node);
721
- const { path: casedPathParams } = getOperationParameters(node, { paramsCasing });
722
- const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : null;
723
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
724
- name,
725
- isExportable,
726
- isIndexable,
727
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.Function, {
728
- name,
729
- export: isExportable,
730
- params: paramsSignature,
731
- children: [
732
- pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
733
- pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
734
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
735
- name: "res",
736
- children: `{ method: '${node.method.toUpperCase()}', url: ${Url$1.toTemplateString(node.path, { prefix: baseURL })} as const }`
737
- }),
738
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
739
- "return res"
740
- ]
741
- })
751
+ types,
752
+ returnType,
753
+ throwOnErrorDefault
742
754
  });
743
- }
744
- //#endregion
745
- //#region src/components/Client.tsx
746
- const declarationPrinter$2 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
747
- function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable }) {
748
- return _kubb_core.ast.createOperationParams(node, {
749
- paramsType,
750
- pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
751
- paramsCasing,
752
- resolver: tsResolver,
753
- extraParams: [...isConfigurable ? [_kubb_core.ast.createFunctionParameter({
754
- name: "config",
755
- type: _kubb_core.ast.createParamsType({
756
- variant: "reference",
757
- name: buildRequestConfigType(node, tsResolver)
758
- }),
759
- default: "{}"
760
- })] : []]
755
+ const validators = buildValidatorHooks({
756
+ node,
757
+ validator,
758
+ zodResolver
761
759
  });
762
- }
763
- function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
764
- if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
765
- const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
766
- const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
760
+ const securityLiteral = buildSecurityMetadata({ security });
761
+ const stylesLiteral = buildStyles({ node });
762
+ const { defaultContentType } = getContentTypeInfo(node);
763
+ const bakedRequestContentType = Boolean(node.requestBody?.content?.[0]?.schema) && defaultContentType !== "application/json" ? defaultContentType : null;
764
+ const mergeContentType = Boolean(bakedRequestContentType) && getResponseContentTypeInfo(node).isMultipleContentTypes;
765
+ const contentTypeLiteral = !bakedRequestContentType ? null : mergeContentType ? `contentType: { request: '${bakedRequestContentType}', ...(typeof contentType === 'string' ? { request: contentType } : contentType) }` : `contentType: { request: '${bakedRequestContentType}' }`;
766
+ const eventStream = isEventStream(node);
767
767
  const responseType = getResponseType(node);
768
- const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node);
769
- const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing });
770
- const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : null;
771
- const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : null;
772
- const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : null;
773
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
774
- const successNames = resolveSuccessNames(node, tsResolver);
775
- const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
776
- const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
777
- const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
778
- const requestParser = resolveRequestParser(parser);
779
- const responseParser = resolveResponseParser(parser);
780
- const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
781
- const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
782
- const queryParamsParser = resolveQueryParamsParser(parser);
783
- const zodQueryParamsName = zodResolver && queryParamsParser === "zod" && originalQueryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, originalQueryParams[0]) : null;
784
- const errorNames = node.responses.filter((r) => {
785
- return Number.parseInt(r.statusCode, 10) >= 400;
786
- }).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
787
- const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null].filter(Boolean);
788
- const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
789
- const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
790
- const generics = [
791
- dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : responseName : responseName,
792
- TError,
793
- parser === "zod" && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
768
+ const responseTypeLiteral = responseType ? `responseType: '${responseType}'` : null;
769
+ const validatorEntries = [
770
+ validators.request ? `request: ${validators.request}` : null,
771
+ validators.response ? `response: ${validators.response}` : null,
772
+ validators.error ? `error: ${validators.error}` : null
794
773
  ].filter(Boolean);
795
- const paramsNode = buildClientParamsNode({
796
- paramsType,
797
- paramsCasing,
798
- pathParamsType,
774
+ const validatorLiteral = validatorEntries.length ? `validator: { ${validatorEntries.join(", ")} }` : null;
775
+ const callConfig = `{ ${[
776
+ `method: '${node.method.toUpperCase()}'`,
777
+ `url: '${node.path}'`,
778
+ securityLiteral ? `security: ${securityLiteral}` : null,
779
+ stylesLiteral ? `styles: ${stylesLiteral}` : null,
780
+ validatorLiteral,
781
+ contentTypeLiteral,
782
+ responseTypeLiteral,
783
+ "...config",
784
+ `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
785
+ ].filter(Boolean).join(", ")} }`;
786
+ const eventType = `SuccessOf<${types.response.responses(node)}>`;
787
+ const functionReturnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
788
+ const returnStatement = eventStream ? `return toEventStream<${eventType}>(request(${callConfig}))` : buildReturnStatement({
799
789
  node,
800
- tsResolver,
801
- isConfigurable
790
+ types,
791
+ callConfig,
792
+ returnType,
793
+ throwOnErrorDefault
802
794
  });
803
- const paramsSignature = declarationPrinter$2.print(paramsNode) ?? "";
804
- const urlParamsNode = buildUrlParamsNode({
805
- paramsType,
806
- paramsCasing,
807
- pathParamsType,
808
- node,
809
- tsResolver
810
- });
811
- const urlParamsCall = (0, _kubb_plugin_ts.functionPrinter)({ mode: "call" }).print(urlParamsNode) ?? "";
812
- const clientParams = createFunctionParams({ config: {
813
- mode: "object",
814
- children: {
815
- method: { value: (0, _kubb_ast_utils.stringify)(node.method.toUpperCase()) },
816
- url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url$1.toTemplateString(node.path) },
817
- baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
818
- params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : queryParamsMapping ? { value: "mappedParams" } : {} : null,
819
- data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
820
- contentType: isConfigurable && isMultipleContentTypes ? {} : null,
821
- responseType: responseType ? { value: (0, _kubb_ast_utils.stringify)(responseType) } : null,
822
- requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
823
- headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
824
- }
825
- } });
826
- const statusUnionType = dataReturnType === "full" ? buildStatusUnionType(node, tsResolver) : null;
827
- const childrenElement = children ? children : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
828
- dataReturnType === "full" && responseParser === "zod" && zodResponseName && statusUnionType && `return {...res, data: ${zodResponseName}.parse(res.data)} as ${statusUnionType}`,
829
- dataReturnType === "full" && responseParser !== "zod" && statusUnionType && `return res as ${statusUnionType}`,
830
- dataReturnType === "data" && responseParser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
831
- dataReturnType === "data" && responseParser !== "zod" && "return res.data"
832
- ] });
833
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
795
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
834
796
  name,
835
797
  isExportable,
836
798
  isIndexable,
837
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.Function, {
799
+ children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.Function, {
838
800
  name,
839
- async: true,
840
801
  export: isExportable,
841
- params: paramsSignature,
802
+ generics: signature.generics,
803
+ params: signature.paramsSignature,
804
+ returnType: functionReturnType,
842
805
  JSDoc: { comments: buildOperationComments(node, {
843
806
  link: "urlPath",
844
807
  linkPosition: "beforeDeprecated",
845
808
  splitLines: true
846
809
  }) },
847
- returnType,
848
810
  children: [
849
- isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = config` : "",
850
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
851
- pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
852
- pathParamsMapping && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
853
- queryParamsMapping && queryParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [`const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})] }),
854
- headerParamsMapping && headerParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})] }),
855
- requestParser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
856
- zodQueryParamsName && `const requestParams = ${zodQueryParamsName}.parse(params)`,
857
- (isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
858
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
859
- isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
860
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
861
- childrenElement
811
+ mergeContentType ? "const { client: request = client, contentType, ...config } = options" : "const { client: request = client, ...config } = options",
812
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)("br", {}),
813
+ returnStatement
862
814
  ]
863
815
  })
864
- })] });
865
- }
866
- //#endregion
867
- //#region src/components/ClassClient.tsx
868
- const declarationPrinter$1 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
869
- function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
870
- if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
871
- const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
872
- const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
873
- const { header: headerParams } = getOperationParameters(node);
874
- const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
875
- const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
876
- const generics = buildGenerics(node, tsResolver, {
877
- dataReturnType,
878
- zodResolver,
879
- parser
880
- });
881
- const paramsNode = buildClientParamsNode({
882
- paramsType,
883
- paramsCasing,
884
- pathParamsType,
885
- node,
886
- tsResolver,
887
- isConfigurable: true
888
- });
889
- const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
890
- const { query: queryParams } = getOperationParameters(node);
891
- const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
892
- const clientParams = buildClassClientParams({
893
- node,
894
- url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
895
- baseURL,
896
- tsResolver,
897
- isFormData,
898
- isMultipleContentTypes,
899
- hasFormData,
900
- headers,
901
- zodQueryParamsName
902
- });
903
- const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
904
- link: "urlPath",
905
- linkPosition: "beforeDeprecated",
906
- splitLines: true
907
- }));
908
- const requestDataLine = buildRequestDataLine({
909
- parser,
910
- node,
911
- zodResolver
912
- });
913
- const queryParamsLine = buildQueryParamsLine({
914
- parser,
915
- node,
916
- zodResolver
917
- });
918
- const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
919
- const returnStatement = buildReturnStatement({
920
- dataReturnType,
921
- parser,
922
- node,
923
- zodResolver,
924
- tsResolver
925
- });
926
- return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
927
- `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
928
- "",
929
- requestDataLine,
930
- queryParamsLine,
931
- formDataLine,
932
- `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
933
- returnStatement
934
- ].filter(Boolean).map((line) => ` ${line}`).join("\n")}\n }`;
935
- }
936
- require_chunk.__name(generateMethod$1, "generateMethod");
937
- function ClassClient({ name, isExportable = true, isIndexable = true, operations, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, children }) {
938
- const classCode = `export class ${name} {
939
- #config: Partial<RequestConfig> & { client?: Client }
940
-
941
- constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
942
- this.#config = config
943
- }
944
-
945
- ${operations.map(({ node, name: methodName, tsResolver, zodResolver }) => generateMethod$1({
946
- node,
947
- name: methodName,
948
- tsResolver,
949
- zodResolver,
950
- baseURL,
951
- dataReturnType,
952
- parser,
953
- paramsType,
954
- paramsCasing,
955
- pathParamsType
956
- })).join("\n\n")}
957
- }`;
958
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File.Source, {
959
- name,
960
- isExportable,
961
- isIndexable,
962
- children: [classCode, children]
963
816
  });
964
817
  }
965
818
  //#endregion
966
- //#region src/components/WrapperClient.tsx
967
- function WrapperClient({ name, controllers, isExportable = true, isIndexable = true }) {
968
- const classCode = `export class ${name} {
969
- ${controllers.map(({ className, propertyName }) => ` readonly ${propertyName}: ${className}`).join("\n")}
970
-
971
- constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
972
- ${controllers.map(({ className, propertyName }) => ` this.${propertyName} = new ${className}(config)`).join("\n")}
973
- }
974
- }`;
975
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
976
- name,
977
- isExportable,
978
- isIndexable,
979
- children: classCode
980
- });
981
- }
982
- //#endregion
983
- //#region src/generators/classClientGenerator.tsx
984
- function resolveTypeImportNames$1(node, tsResolver) {
985
- return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
986
- }
987
- require_chunk.__name(resolveTypeImportNames$1, "resolveTypeImportNames");
988
- function resolveZodImportNames$1(node, zodResolver, parser) {
989
- const { query: queryParams } = getOperationParameters(node);
990
- return [
991
- resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
992
- resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
993
- resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
994
- ].filter((n) => Boolean(n));
995
- }
996
- require_chunk.__name(resolveZodImportNames$1, "resolveZodImportNames");
819
+ //#region ../../internals/client/src/resolveOperationTypes.ts
997
820
  /**
998
- * Built-in `operations` generator for `@kubb/plugin-client` when
999
- * `clientType: 'class'`. Emits one class per tag, with one instance method
1000
- * per operation and a shared constructor for request configuration.
821
+ * Warning a client generator reports when neither plugin can supply the operation types, so the
822
+ * pipeline says why nothing was generated instead of silently emitting no files.
1001
823
  */
1002
- const classClientGenerator = (0, _kubb_core.defineGenerator)({
1003
- name: "classClient",
1004
- renderer: _kubb_renderer_jsx.jsxRenderer,
1005
- operations(nodes, ctx) {
1006
- const { config, driver, resolver, root } = ctx;
1007
- const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
1008
- const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
1009
- const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1010
- if (!pluginTs) return null;
1011
- const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1012
- const tsPluginOptions = pluginTs.options;
1013
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1014
- const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1015
- function buildOperationData(node) {
1016
- const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
1017
- root,
1018
- output: tsPluginOptions?.output ?? output,
1019
- group: tsPluginOptions?.group
1020
- });
1021
- const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1022
- root,
1023
- output: pluginZod.options?.output ?? output,
1024
- group: pluginZod.options?.group ?? void 0
1025
- }) : null;
1026
- return {
1027
- node,
1028
- name: resolver.resolveName(node.operationId),
1029
- tsResolver,
1030
- zodResolver,
1031
- typeFile,
1032
- zodFile
1033
- };
1034
- }
1035
- const controllers = nodes.reduce((acc, operationNode) => {
1036
- if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
1037
- const tag = operationNode.tags[0];
1038
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
1039
- if (!tag && !group) {
1040
- const name = resolver.resolveClassName("ApiClient");
1041
- const file = resolver.resolveFile({
1042
- name,
1043
- extname: ".ts"
1044
- }, {
1045
- root,
1046
- output,
1047
- group: group ?? void 0
1048
- });
1049
- const operationData = buildOperationData(operationNode);
1050
- const previous = acc.find((item) => item.file.path === file.path);
1051
- if (previous) previous.operations.push(operationData);
1052
- else acc.push({
1053
- name,
1054
- propertyName: resolver.resolveClientPropertyName(name),
1055
- file,
1056
- operations: [operationData]
1057
- });
1058
- return acc;
1059
- }
1060
- if (tag) {
1061
- const name = groupName;
1062
- const file = resolver.resolveFile({
1063
- name,
1064
- extname: ".ts",
1065
- tag
1066
- }, {
1067
- root,
1068
- output,
1069
- group: group ?? void 0
1070
- });
1071
- const operationData = buildOperationData(operationNode);
1072
- const previous = acc.find((item) => item.file.path === file.path);
1073
- if (previous) previous.operations.push(operationData);
1074
- else acc.push({
1075
- name,
1076
- propertyName: resolver.resolveClientPropertyName(name),
1077
- file,
1078
- operations: [operationData]
1079
- });
1080
- }
1081
- return acc;
1082
- }, []);
1083
- function collectTypeImports(ops) {
1084
- const typeImportsByFile = /* @__PURE__ */ new Map();
1085
- const typeFilesByPath = /* @__PURE__ */ new Map();
1086
- ops.forEach((op) => {
1087
- const names = resolveTypeImportNames$1(op.node, tsResolver);
1088
- if (!typeImportsByFile.has(op.typeFile.path)) typeImportsByFile.set(op.typeFile.path, /* @__PURE__ */ new Set());
1089
- const imports = typeImportsByFile.get(op.typeFile.path);
1090
- names.forEach((n) => {
1091
- imports.add(n);
1092
- });
1093
- typeFilesByPath.set(op.typeFile.path, op.typeFile);
1094
- });
1095
- return {
1096
- typeImportsByFile,
1097
- typeFilesByPath
1098
- };
1099
- }
1100
- function collectZodImports(ops) {
1101
- const zodImportsByFile = /* @__PURE__ */ new Map();
1102
- const zodFilesByPath = /* @__PURE__ */ new Map();
1103
- ops.forEach((op) => {
1104
- if (!op.zodFile || !zodResolver) return;
1105
- const names = resolveZodImportNames$1(op.node, zodResolver, parser);
1106
- if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1107
- const imports = zodImportsByFile.get(op.zodFile.path);
1108
- names.forEach((n) => {
1109
- imports.add(n);
1110
- });
1111
- zodFilesByPath.set(op.zodFile.path, op.zodFile);
1112
- });
1113
- return {
1114
- zodImportsByFile,
1115
- zodFilesByPath
1116
- };
1117
- }
1118
- const files = controllers.map(({ name, file, operations: ops }) => {
1119
- const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1120
- const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
1121
- zodImportsByFile: /* @__PURE__ */ new Map(),
1122
- zodFilesByPath: /* @__PURE__ */ new Map()
1123
- };
1124
- const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
1125
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1126
- baseName: file.baseName,
1127
- path: file.path,
1128
- meta: file.meta,
1129
- banner: resolver.resolveBanner(ctx.meta, {
1130
- output,
1131
- config,
1132
- file: {
1133
- path: file.path,
1134
- baseName: file.baseName
1135
- }
1136
- }),
1137
- footer: resolver.resolveFooter(ctx.meta, {
1138
- output,
1139
- config,
1140
- file: {
1141
- path: file.path,
1142
- baseName: file.baseName
1143
- }
1144
- }),
1145
- children: [
1146
- importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
1147
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1148
- name: "client",
1149
- path: importPath
1150
- }),
1151
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1152
- name: ["mergeConfig"],
1153
- path: importPath
1154
- }),
1155
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1156
- name: [
1157
- "Client",
1158
- "RequestConfig",
1159
- "ResponseErrorConfig"
1160
- ],
1161
- path: importPath,
1162
- isTypeOnly: true
1163
- })
1164
- ] }) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
1165
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1166
- name: ["client"],
1167
- root: file.path,
1168
- path: node_path.default.resolve(root, ".kubb/client.ts")
1169
- }),
1170
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1171
- name: ["mergeConfig"],
1172
- root: file.path,
1173
- path: node_path.default.resolve(root, ".kubb/client.ts")
1174
- }),
1175
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1176
- name: [
1177
- "Client",
1178
- "RequestConfig",
1179
- "ResponseErrorConfig"
1180
- ],
1181
- root: file.path,
1182
- path: node_path.default.resolve(root, ".kubb/client.ts"),
1183
- isTypeOnly: true
1184
- })
1185
- ] }),
1186
- hasFormData && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1187
- name: ["buildFormData"],
1188
- root: file.path,
1189
- path: node_path.default.resolve(root, ".kubb/config.ts")
1190
- }),
1191
- parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1192
- name: ["z"],
1193
- path: "zod",
1194
- isTypeOnly: true
1195
- }),
1196
- Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1197
- const typeFile = typeFilesByPath.get(filePath);
1198
- if (!typeFile) return null;
1199
- const importNames = Array.from(importSet).filter(Boolean);
1200
- if (importNames.length === 0) return null;
1201
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1202
- name: importNames,
1203
- root: file.path,
1204
- path: typeFile.path,
1205
- isTypeOnly: true
1206
- }, filePath);
1207
- }),
1208
- isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1209
- const zodFile = zodFilesByPath.get(filePath);
1210
- if (!zodFile) return null;
1211
- const importNames = Array.from(importSet).filter(Boolean);
1212
- if (importNames.length === 0) return null;
1213
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1214
- name: importNames,
1215
- root: file.path,
1216
- path: zodFile.path
1217
- }, filePath);
1218
- }),
1219
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(ClassClient, {
1220
- name,
1221
- operations: ops,
1222
- baseURL,
1223
- dataReturnType,
1224
- pathParamsType,
1225
- paramsCasing,
1226
- paramsType,
1227
- parser
1228
- })
1229
- ]
1230
- }, file.path);
1231
- });
1232
- if (sdk) {
1233
- const sdkFile = resolver.resolveFile({
1234
- name: sdk.className,
1235
- extname: ".ts"
1236
- }, {
1237
- root,
1238
- output,
1239
- group: group ?? void 0
1240
- });
1241
- files.push(/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1242
- baseName: sdkFile.baseName,
1243
- path: sdkFile.path,
1244
- meta: sdkFile.meta,
1245
- banner: resolver.resolveBanner(ctx.meta, {
1246
- output,
1247
- config,
1248
- file: {
1249
- path: sdkFile.path,
1250
- baseName: sdkFile.baseName
1251
- }
1252
- }),
1253
- footer: resolver.resolveFooter(ctx.meta, {
1254
- output,
1255
- config,
1256
- file: {
1257
- path: sdkFile.path,
1258
- baseName: sdkFile.baseName
1259
- }
1260
- }),
1261
- children: [
1262
- importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1263
- name: ["Client", "RequestConfig"],
1264
- path: importPath,
1265
- isTypeOnly: true
1266
- }) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1267
- name: ["Client", "RequestConfig"],
1268
- root: sdkFile.path,
1269
- path: node_path.default.resolve(root, ".kubb/client.ts"),
1270
- isTypeOnly: true
1271
- }),
1272
- controllers.map(({ name, file }) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1273
- name: [name],
1274
- root: sdkFile.path,
1275
- path: file.path
1276
- }, name)),
1277
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(WrapperClient, {
1278
- name: sdk.className,
1279
- controllers: controllers.map(({ name, propertyName }) => ({
1280
- className: name,
1281
- propertyName
1282
- }))
1283
- })
1284
- ]
1285
- }, sdkFile.path));
1286
- }
1287
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: files });
1288
- }
1289
- });
1290
- //#endregion
1291
- //#region src/generators/clientGenerator.tsx
824
+ const MISSING_OPERATION_TYPES_WARNING = "Skipped: no operation types available. Add `pluginTs()`, or `pluginZod({ inferred: true })` to type the client from its zod schemas.";
1292
825
  /**
1293
- * Built-in operation generator for `@kubb/plugin-client`. Emits one async
1294
- * function per OpenAPI operation, plus the matching URL helper. Used when
1295
- * `clientType: 'function'` (the default).
826
+ * Picks the plugin a generated client takes its `Options` and `Responses` types from.
827
+ *
828
+ * `plugin-ts` wins whenever it is in the pipeline, so existing configs keep the types they generate
829
+ * today. Without it, `pluginZod({ inferred: true })` takes over: its inferred options and responses
830
+ * types are built from the same shared schemas as the `plugin-ts` ones, so a client typed off
831
+ * `z.infer` carries what the codecs decode to (a `Temporal` instance, a `Date`) rather than the raw
832
+ * wire type. Returns `null` when neither is available, leaving the caller nothing to type against.
833
+ *
834
+ * @example
835
+ * ```ts
836
+ * const types = resolveOperationTypes(ctx.driver)
837
+ * if (!types) return null
838
+ * types.response.options(node) // 'GetPetByIdOptions', or 'GetPetByIdOptionsSchemaType' from zod
839
+ * ```
1296
840
  */
1297
- const clientGenerator = (0, _kubb_core.defineGenerator)({
1298
- name: "client",
1299
- renderer: _kubb_renderer_jsx.jsxRenderer,
1300
- operation(node, ctx) {
1301
- if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
1302
- const { config, driver, resolver, root } = ctx;
1303
- const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options;
1304
- const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
1305
- const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1306
- if (!pluginTs) return null;
1307
- const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1308
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1309
- const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1310
- const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
1311
- const { query: queryParams } = getOperationParameters(node);
1312
- const importedZodNames = zodResolver ? [
1313
- resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1314
- resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
1315
- resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
1316
- ].filter((name) => Boolean(name)) : [];
1317
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) ?? null : null;
1318
- const meta = {
1319
- name: resolver.resolveName(node.operationId),
1320
- urlName: resolver.resolveUrlName(node),
1321
- file: resolver.resolveFile(operationFileEntry(node, node.operationId), {
1322
- root,
1323
- output,
1324
- group: group ?? void 0
1325
- }),
1326
- fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
1327
- root,
1328
- output: pluginTs.options?.output ?? output,
1329
- group: pluginTs.options?.group ?? void 0
1330
- }),
1331
- fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1332
- root,
1333
- output: pluginZod.options.output ?? output,
1334
- group: pluginZod.options?.group ?? void 0
1335
- }) : null
841
+ function resolveOperationTypes(driver) {
842
+ const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
843
+ if (pluginTs) {
844
+ const resolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
845
+ return {
846
+ response: resolver.response,
847
+ resolver,
848
+ output: pluginTs.options?.output,
849
+ group: pluginTs.options?.group
1336
850
  };
1337
- const hasFormData = node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false;
1338
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1339
- baseName: meta.file.baseName,
1340
- path: meta.file.path,
1341
- meta: meta.file.meta,
1342
- banner: resolver.resolveBanner(ctx.meta, {
1343
- output,
1344
- config,
1345
- file: {
1346
- path: meta.file.path,
1347
- baseName: meta.file.baseName
1348
- }
1349
- }),
1350
- footer: resolver.resolveFooter(ctx.meta, {
1351
- output,
1352
- config,
1353
- file: {
1354
- path: meta.file.path,
1355
- baseName: meta.file.baseName
1356
- }
1357
- }),
1358
- children: [
1359
- importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1360
- name: "client",
1361
- path: importPath
1362
- }), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1363
- name: [
1364
- "Client",
1365
- "RequestConfig",
1366
- "ResponseErrorConfig"
1367
- ],
1368
- path: importPath,
1369
- isTypeOnly: true
1370
- })] }) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1371
- name: ["client"],
1372
- root: meta.file.path,
1373
- path: node_path.default.resolve(root, ".kubb/client.ts")
1374
- }), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1375
- name: [
1376
- "Client",
1377
- "RequestConfig",
1378
- "ResponseErrorConfig"
1379
- ],
1380
- root: meta.file.path,
1381
- path: node_path.default.resolve(root, ".kubb/client.ts"),
1382
- isTypeOnly: true
1383
- })] }),
1384
- hasFormData && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1385
- name: ["buildFormData"],
1386
- root: meta.file.path,
1387
- path: node_path.default.resolve(root, ".kubb/config.ts")
1388
- }),
1389
- zodRequestName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1390
- name: ["z"],
1391
- path: "zod",
1392
- isTypeOnly: true
1393
- }),
1394
- meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1395
- name: importedZodNames,
1396
- root: meta.file.path,
1397
- path: meta.fileZod.path
1398
- }),
1399
- meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1400
- name: Array.from(new Set(importedTypeNames)),
1401
- root: meta.file.path,
1402
- path: meta.fileTs.path,
1403
- isTypeOnly: true
1404
- }),
1405
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Url, {
1406
- name: meta.urlName,
1407
- baseURL,
1408
- pathParamsType,
1409
- paramsCasing,
1410
- paramsType,
1411
- node,
1412
- tsResolver,
1413
- isIndexable: urlType === "export",
1414
- isExportable: urlType === "export"
1415
- }),
1416
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Client, {
1417
- name: meta.name,
1418
- urlName: meta.urlName,
1419
- baseURL,
1420
- dataReturnType,
1421
- pathParamsType,
1422
- paramsCasing,
1423
- paramsType,
1424
- node,
1425
- tsResolver,
1426
- zodResolver,
1427
- parser
1428
- })
1429
- ]
1430
- });
1431
851
  }
1432
- });
852
+ const pluginZod = driver.getPlugin(_kubb_plugin_zod.pluginZodName);
853
+ if (!pluginZod?.options?.inferred) return null;
854
+ const resolver = driver.getResolver(_kubb_plugin_zod.pluginZodName);
855
+ return {
856
+ response: {
857
+ options: resolver.response.options,
858
+ responses: (node) => resolver.schema.type(resolver.response.responses(node))
859
+ },
860
+ resolver,
861
+ output: pluginZod.options.output,
862
+ group: pluginZod.options.group
863
+ };
864
+ }
1433
865
  //#endregion
1434
- //#region src/generators/groupedClientGenerator.tsx
866
+ //#region ../../internals/client/src/generators/clientGenerator.tsx
1435
867
  /**
1436
- * Emits one aggregate file per tag/group when `group` is configured. Each
1437
- * file re-exports every client function for that group, so callers can
1438
- * `import { petController } from './gen/clients'` instead of importing
1439
- * each operation individually.
868
+ * Builds the built-in per-operation generator shared by the client plugins (`@kubb/plugin-fetch`,
869
+ * `@kubb/plugin-axios`). Emits one async function per OpenAPI operation using the shared
870
+ * `Operation` component: a grouped `<Name>Request` type and a function that forwards a single
871
+ * `options` object to the bundled `client` and returns the `Unwrappable<RequestResult>`. Only the
872
+ * generator `name` differs between plugins. Every other resolution, import, and rendering step is
873
+ * identical.
1440
874
  */
1441
- const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
1442
- name: "groupedClient",
1443
- renderer: _kubb_renderer_jsx.jsxRenderer,
1444
- operations(nodes, ctx) {
1445
- const { config, resolver, root } = ctx;
1446
- const { output, group } = ctx.options;
1447
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: nodes.reduce((acc, operationNode) => {
1448
- if (group?.type === "tag") {
1449
- const tag = operationNode.tags[0];
1450
- const name = tag ? group?.name?.({ group: camelCase(tag) }) : null;
1451
- if (!tag || !name) return acc;
1452
- const file = resolver.resolveFile({
1453
- name,
1454
- extname: ".ts",
1455
- tag
1456
- }, {
1457
- root,
1458
- output,
1459
- group: group ?? void 0
1460
- });
1461
- const clientFile = resolver.resolveFile({
1462
- name: operationNode.operationId,
1463
- extname: ".ts",
1464
- tag: operationNode.tags[0] ?? "default",
1465
- path: operationNode.path
1466
- }, {
875
+ function createClientGenerator(name, resolveClientPath) {
876
+ return (0, kubb_kit.defineGenerator)({
877
+ name,
878
+ renderer: kubb_jsx.jsxRenderer,
879
+ operation(node, ctx) {
880
+ if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
881
+ const { config, driver, resolver, root } = ctx;
882
+ const { output, validator, returnType, throwOnErrorDefault, group } = ctx.options;
883
+ const types = resolveOperationTypes(driver);
884
+ if (!types) {
885
+ ctx.warn(MISSING_OPERATION_TYPES_WARNING);
886
+ return null;
887
+ }
888
+ const pluginZod = resolveResponseValidator(validator) === "zod" || resolveRequestValidator(validator) === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
889
+ const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
890
+ const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
891
+ const importedTypeNames = [types.response.options(node), types.response.responses(node)];
892
+ const importedZodNames = zodResolver ? [
893
+ resolveResponseValidator(validator) === "zod" ? zodResolver.response.response?.(node) : null,
894
+ resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
895
+ resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body?.(node) : null
896
+ ].filter((name) => Boolean(name)) : [];
897
+ const meta = {
898
+ name: resolver.name(node.operationId),
899
+ file: resolver.file({
900
+ ...operationFileEntry(node, node.operationId),
1467
901
  root,
1468
902
  output,
1469
903
  group: group ?? void 0
1470
- });
1471
- const client = {
1472
- name: resolver.resolveName(operationNode.operationId),
1473
- file: clientFile
1474
- };
1475
- const previous = acc.find((item) => item.file.path === file.path);
1476
- if (previous) previous.clients.push(client);
1477
- else acc.push({
1478
- name,
1479
- file,
1480
- clients: [client]
1481
- });
1482
- }
1483
- return acc;
1484
- }, []).map(({ name, file, clients }) => {
1485
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1486
- baseName: file.baseName,
1487
- path: file.path,
1488
- meta: file.meta,
1489
- banner: resolver.resolveBanner(ctx.meta, {
1490
- output,
1491
- config,
1492
- file: {
1493
- path: file.path,
1494
- baseName: file.baseName,
1495
- isAggregation: true
1496
- }
1497
- }),
1498
- footer: resolver.resolveFooter(ctx.meta, {
1499
- output,
1500
- config,
1501
- file: {
1502
- path: file.path,
1503
- baseName: file.baseName,
1504
- isAggregation: true
1505
- }
1506
904
  }),
1507
- children: [clients.map((client) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1508
- name: [client.name],
1509
- root: file.path,
1510
- path: client.file.path
1511
- }, client.name)), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
1512
- name,
1513
- isExportable: true,
1514
- isIndexable: true,
1515
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Function, {
1516
- export: true,
1517
- name,
1518
- children: `return { ${clients.map((client) => client.name).join(", ")} }`
1519
- })
1520
- })]
1521
- }, file.path);
1522
- }) });
1523
- }
1524
- });
1525
- //#endregion
1526
- //#region src/generators/operationsGenerator.ts
1527
- /**
1528
- * Generates an `operations.ts` file that re-exports every operation grouped
1529
- * by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
1530
- * for building meta-tooling on top of the generated client (route
1531
- * registries, API explorers).
1532
- */
1533
- const operationsGenerator = (0, _kubb_core.defineGenerator)({
1534
- name: "client",
1535
- operations(nodes, ctx) {
1536
- const { config, resolver, root } = ctx;
1537
- const { output, group } = ctx.options;
1538
- const name = "operations";
1539
- const file = resolver.resolveFile({
1540
- name,
1541
- extname: ".ts"
1542
- }, {
1543
- root,
1544
- output,
1545
- group: group ?? void 0
1546
- });
1547
- const operationsObject = {};
1548
- for (const node of nodes) {
1549
- if (!_kubb_core.ast.isHttpOperationNode(node)) continue;
1550
- operationsObject[node.operationId] = {
1551
- path: Url$1.toPath(node.path),
1552
- method: node.method.toLowerCase()
1553
- };
1554
- }
1555
- return [_kubb_core.ast.createFile({
1556
- baseName: file.baseName,
1557
- path: file.path,
1558
- meta: file.meta,
1559
- banner: resolver.resolveBanner(ctx.meta, {
1560
- output,
1561
- config,
1562
- file: {
1563
- path: file.path,
1564
- baseName: file.baseName
1565
- }
1566
- }),
1567
- footer: resolver.resolveFooter(ctx.meta, {
1568
- output,
1569
- config,
1570
- file: {
1571
- path: file.path,
1572
- baseName: file.baseName
1573
- }
1574
- }),
1575
- sources: [_kubb_core.ast.createSource({
1576
- name,
1577
- isExportable: true,
1578
- isIndexable: true,
1579
- nodes: [_kubb_core.ast.createConst({
1580
- name,
1581
- export: true,
1582
- nodes: [_kubb_core.ast.createText(JSON.stringify(operationsObject, void 0, 2))]
1583
- })]
1584
- })]
1585
- })];
1586
- }
1587
- });
1588
- //#endregion
1589
- //#region src/components/StaticClassClient.tsx
1590
- const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
1591
- function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
1592
- if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
1593
- const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
1594
- const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
1595
- const { header: headerParams } = getOperationParameters(node);
1596
- const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
1597
- const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
1598
- const generics = buildGenerics(node, tsResolver, {
1599
- dataReturnType,
1600
- zodResolver,
1601
- parser
1602
- });
1603
- const paramsNode = buildClientParamsNode({
1604
- paramsType,
1605
- paramsCasing,
1606
- pathParamsType,
1607
- node,
1608
- tsResolver,
1609
- isConfigurable: true
1610
- });
1611
- const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1612
- const { query: queryParams } = getOperationParameters(node);
1613
- const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
1614
- const clientParams = buildClassClientParams({
1615
- node,
1616
- url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
1617
- baseURL,
1618
- tsResolver,
1619
- isFormData,
1620
- isMultipleContentTypes,
1621
- hasFormData,
1622
- headers,
1623
- zodQueryParamsName
1624
- });
1625
- const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
1626
- link: "urlPath",
1627
- linkPosition: "beforeDeprecated",
1628
- splitLines: true
1629
- }));
1630
- const requestDataLine = buildRequestDataLine({
1631
- parser,
1632
- node,
1633
- zodResolver
1634
- });
1635
- const queryParamsLine = buildQueryParamsLine({
1636
- parser,
1637
- node,
1638
- zodResolver
1639
- });
1640
- const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
1641
- const returnStatement = buildReturnStatement({
1642
- dataReturnType,
1643
- parser,
1644
- node,
1645
- zodResolver,
1646
- tsResolver
1647
- });
1648
- return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
1649
- `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
1650
- "",
1651
- requestDataLine,
1652
- queryParamsLine,
1653
- formDataLine,
1654
- `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
1655
- returnStatement
1656
- ].filter(Boolean).map((line) => ` ${line}`).join("\n")}\n }`;
1657
- }
1658
- function StaticClassClient({ name, isExportable = true, isIndexable = true, operations, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, children }) {
1659
- const classCode = `export class ${name} {\n static #config: Partial<RequestConfig> & { client?: Client } = {}\n\n${operations.map(({ node, name: methodName, tsResolver, zodResolver }) => generateMethod({
1660
- node,
1661
- name: methodName,
1662
- tsResolver,
1663
- zodResolver,
1664
- baseURL,
1665
- dataReturnType,
1666
- parser,
1667
- paramsType,
1668
- paramsCasing,
1669
- pathParamsType
1670
- })).join("\n\n")}\n}`;
1671
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File.Source, {
1672
- name,
1673
- isExportable,
1674
- isIndexable,
1675
- children: [classCode, children]
1676
- });
1677
- }
1678
- //#endregion
1679
- //#region src/generators/staticClassClientGenerator.tsx
1680
- function resolveTypeImportNames(node, tsResolver) {
1681
- return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
1682
- }
1683
- function resolveZodImportNames(node, zodResolver, parser) {
1684
- const { query: queryParams } = getOperationParameters(node);
1685
- return [
1686
- resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1687
- resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
1688
- resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
1689
- ].filter((n) => Boolean(n));
1690
- }
1691
- /**
1692
- * Built-in `operations` generator for `@kubb/plugin-client` when
1693
- * `clientType: 'staticClass'`. Emits one class per tag, with a static method
1694
- * per operation so callers can use `Pet.getPetById(...)` without
1695
- * instantiating the class.
1696
- */
1697
- const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
1698
- name: "staticClassClient",
1699
- renderer: _kubb_renderer_jsx.jsxRenderer,
1700
- operations(nodes, ctx) {
1701
- const { config, driver, resolver, root } = ctx;
1702
- const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
1703
- const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
1704
- const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1705
- if (!pluginTs) return null;
1706
- const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1707
- const tsPluginOptions = pluginTs.options;
1708
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1709
- const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1710
- function buildOperationData(node) {
1711
- const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
1712
- root,
1713
- output: tsPluginOptions?.output ?? output,
1714
- group: tsPluginOptions?.group
1715
- });
1716
- const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1717
- root,
1718
- output: pluginZod.options?.output ?? output,
1719
- group: pluginZod.options?.group ?? void 0
1720
- }) : null;
1721
- return {
1722
- node,
1723
- name: resolver.resolveName(node.operationId),
1724
- tsResolver,
1725
- zodResolver,
1726
- typeFile,
1727
- zodFile
1728
- };
1729
- }
1730
- const controllers = nodes.reduce((acc, operationNode) => {
1731
- if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
1732
- const tag = operationNode.tags[0];
1733
- const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
1734
- if (!tag && !group) {
1735
- const name = resolver.resolveClassName("ApiClient");
1736
- const file = resolver.resolveFile({
1737
- name,
1738
- extname: ".ts"
1739
- }, {
905
+ fileTypes: resolveDependencyOperationFile({
906
+ cache: ctx.cache,
907
+ node,
908
+ resolver: types.resolver,
1740
909
  root,
1741
- output,
1742
- group: group ?? void 0
1743
- });
1744
- const operationData = buildOperationData(operationNode);
1745
- const previous = acc.find((item) => item.file.path === file.path);
1746
- if (previous) previous.operations.push(operationData);
1747
- else acc.push({
1748
- name,
1749
- file,
1750
- operations: [operationData]
1751
- });
1752
- return acc;
1753
- }
1754
- if (tag) {
1755
- const name = groupName;
1756
- const file = resolver.resolveFile({
1757
- name,
1758
- extname: ".ts",
1759
- tag
1760
- }, {
910
+ output: types.output ?? output,
911
+ group: types.group
912
+ }),
913
+ fileZod: zodResolver && pluginZod?.options ? zodResolver.file({
914
+ ...operationFileEntry(node, node.operationId),
1761
915
  root,
1762
- output,
1763
- group: group ?? void 0
1764
- });
1765
- const operationData = buildOperationData(operationNode);
1766
- const previous = acc.find((item) => item.file.path === file.path);
1767
- if (previous) previous.operations.push(operationData);
1768
- else acc.push({
1769
- name,
1770
- file,
1771
- operations: [operationData]
1772
- });
1773
- }
1774
- return acc;
1775
- }, []);
1776
- function collectTypeImports(ops) {
1777
- const typeImportsByFile = /* @__PURE__ */ new Map();
1778
- const typeFilesByPath = /* @__PURE__ */ new Map();
1779
- ops.forEach((op) => {
1780
- const names = resolveTypeImportNames(op.node, tsResolver);
1781
- if (!typeImportsByFile.has(op.typeFile.path)) typeImportsByFile.set(op.typeFile.path, /* @__PURE__ */ new Set());
1782
- const imports = typeImportsByFile.get(op.typeFile.path);
1783
- names.forEach((n) => {
1784
- imports.add(n);
1785
- });
1786
- typeFilesByPath.set(op.typeFile.path, op.typeFile);
1787
- });
1788
- return {
1789
- typeImportsByFile,
1790
- typeFilesByPath
916
+ output: pluginZod.options.output ?? output,
917
+ group: pluginZod.options?.group ?? void 0
918
+ }) : null
1791
919
  };
1792
- }
1793
- function collectZodImports(ops) {
1794
- const zodImportsByFile = /* @__PURE__ */ new Map();
1795
- const zodFilesByPath = /* @__PURE__ */ new Map();
1796
- ops.forEach((op) => {
1797
- if (!op.zodFile || !zodResolver) return;
1798
- const names = resolveZodImportNames(op.node, zodResolver, parser);
1799
- if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
1800
- const imports = zodImportsByFile.get(op.zodFile.path);
1801
- names.forEach((n) => {
1802
- imports.add(n);
1803
- });
1804
- zodFilesByPath.set(op.zodFile.path, op.zodFile);
920
+ const security = getOperationSecurity({
921
+ document: ctx.adapter.document,
922
+ method: node.method,
923
+ path: node.path
1805
924
  });
1806
- return {
1807
- zodImportsByFile,
1808
- zodFilesByPath
1809
- };
1810
- }
1811
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
1812
- const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
1813
- const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
1814
- zodImportsByFile: /* @__PURE__ */ new Map(),
1815
- zodFilesByPath: /* @__PURE__ */ new Map()
1816
- };
1817
- const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
1818
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1819
- baseName: file.baseName,
1820
- path: file.path,
1821
- meta: file.meta,
1822
- banner: resolver.resolveBanner(ctx.meta, {
925
+ const customClientPath = resolveClientPath?.(ctx.options, root);
926
+ const clientPath = customClientPath ?? node_path.default.resolve(root, ".kubb/client.ts");
927
+ const clientRoot = customClientPath === void 0 ? meta.file.path : void 0;
928
+ const eventStream = isEventStream(node);
929
+ const clientValueImports = (() => {
930
+ if (eventStream) return ["client", "toEventStream"];
931
+ if (returnType === "plain") return ["client"];
932
+ if (returnType === "data") return ["client", "unwrapResult"];
933
+ return ["client", "withUnwrap"];
934
+ })();
935
+ const clientTypeImports = (() => {
936
+ if (eventStream) return [
937
+ "Options",
938
+ "EventStreamResult",
939
+ "SuccessOf"
940
+ ];
941
+ if (returnType === "plain") return ["Options", "RequestResult"];
942
+ if (returnType === "data") return ["Options", "UnwrappedResult"];
943
+ return [
944
+ "Options",
945
+ "Unwrappable",
946
+ "RequestResult"
947
+ ];
948
+ })();
949
+ return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
950
+ baseName: meta.file.baseName,
951
+ path: meta.file.path,
952
+ meta: meta.file.meta,
953
+ banner: resolver.default.banner(ctx.meta, {
1823
954
  output,
1824
955
  config,
1825
956
  file: {
1826
- path: file.path,
1827
- baseName: file.baseName
957
+ path: meta.file.path,
958
+ baseName: meta.file.baseName
1828
959
  }
1829
960
  }),
1830
- footer: resolver.resolveFooter(ctx.meta, {
961
+ footer: resolver.default.footer(ctx.meta, {
1831
962
  output,
1832
963
  config,
1833
964
  file: {
1834
- path: file.path,
1835
- baseName: file.baseName
965
+ path: meta.file.path,
966
+ baseName: meta.file.baseName
1836
967
  }
1837
968
  }),
1838
969
  children: [
1839
- importPath ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
1840
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1841
- name: "client",
1842
- path: importPath
1843
- }),
1844
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1845
- name: ["mergeConfig"],
1846
- path: importPath
1847
- }),
1848
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1849
- name: [
1850
- "Client",
1851
- "RequestConfig",
1852
- "ResponseErrorConfig"
1853
- ],
1854
- path: importPath,
1855
- isTypeOnly: true
1856
- })
1857
- ] }) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
1858
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1859
- name: ["client"],
1860
- root: file.path,
1861
- path: node_path.default.resolve(root, ".kubb/client.ts")
1862
- }),
1863
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1864
- name: ["mergeConfig"],
1865
- root: file.path,
1866
- path: node_path.default.resolve(root, ".kubb/client.ts")
1867
- }),
1868
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1869
- name: [
1870
- "Client",
1871
- "RequestConfig",
1872
- "ResponseErrorConfig"
1873
- ],
1874
- root: file.path,
1875
- path: node_path.default.resolve(root, ".kubb/client.ts"),
1876
- isTypeOnly: true
1877
- })
1878
- ] }),
1879
- hasFormData && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1880
- name: ["buildFormData"],
1881
- root: file.path,
1882
- path: node_path.default.resolve(root, ".kubb/config.ts")
970
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
971
+ name: clientValueImports,
972
+ root: clientRoot,
973
+ path: clientPath
1883
974
  }),
1884
- parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1885
- name: ["z"],
1886
- path: "zod",
975
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
976
+ name: clientTypeImports,
977
+ root: clientRoot,
978
+ path: clientPath,
1887
979
  isTypeOnly: true
1888
980
  }),
1889
- Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
1890
- const typeFile = typeFilesByPath.get(filePath);
1891
- if (!typeFile) return null;
1892
- const importNames = Array.from(importSet).filter(Boolean);
1893
- if (importNames.length === 0) return null;
1894
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1895
- name: importNames,
1896
- root: file.path,
1897
- path: typeFile.path,
1898
- isTypeOnly: true
1899
- }, filePath);
981
+ meta.fileTypes && importedTypeNames.length > 0 && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
982
+ name: Array.from(new Set(importedTypeNames)),
983
+ root: meta.file.path,
984
+ path: meta.fileTypes.path,
985
+ isTypeOnly: true
1900
986
  }),
1901
- isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
1902
- const zodFile = zodFilesByPath.get(filePath);
1903
- if (!zodFile) return null;
1904
- const importNames = Array.from(importSet).filter(Boolean);
1905
- if (importNames.length === 0) return null;
1906
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1907
- name: importNames,
1908
- root: file.path,
1909
- path: zodFile.path
1910
- }, filePath);
987
+ meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
988
+ name: importedZodNames,
989
+ root: meta.file.path,
990
+ path: meta.fileZod.path
1911
991
  }),
1912
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(StaticClassClient, {
1913
- name,
1914
- operations: ops,
1915
- baseURL,
1916
- dataReturnType,
1917
- pathParamsType,
1918
- paramsCasing,
1919
- paramsType,
1920
- parser
992
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Operation, {
993
+ name: meta.name,
994
+ node,
995
+ types,
996
+ zodResolver,
997
+ validator,
998
+ returnType,
999
+ throwOnErrorDefault,
1000
+ security
1921
1001
  })
1922
1002
  ]
1923
- }, file.path);
1924
- }) });
1925
- }
1926
- });
1003
+ });
1004
+ }
1005
+ });
1006
+ }
1007
+ //#endregion
1008
+ //#region ../../internals/client/src/macros.ts
1009
+ /**
1010
+ * Macros the client plugins apply by default, ahead of any user macros. `macroSimplifyUnion`
1011
+ * drops union members a broader scalar already covers, keeping the generated response and error
1012
+ * unions tidy. A plugin wires them with `ctx.setMacros([...defaultMacros, ...userMacros])`.
1013
+ */
1014
+ const defaultMacros = [kubb_kit.macroSimplifyUnion];
1927
1015
  //#endregion
1928
- //#region src/resolvers/resolverClient.ts
1016
+ //#region ../../internals/client/src/resolver.ts
1929
1017
  /**
1930
- * Default resolver used by `@kubb/plugin-client`. Decides the names and file
1931
- * paths for every generated client function or class. Functions and files use
1932
- * camelCase; classes and tag groups use PascalCase.
1018
+ * Default resolver shared by the client plugins. Functions and files inherit the built-in camelCase
1019
+ * `name` and `file`; classes and tag groups use PascalCase.
1933
1020
  *
1934
- * @example Resolve client function and class names
1021
+ * @example
1935
1022
  * ```ts
1936
- * import { resolverClient } from '@kubb/plugin-client'
1937
- *
1938
- * resolverClient.default('list pets', 'function') // 'listPets'
1939
- * resolverClient.resolveClassName('pet') // 'Pet'
1940
- * resolverClient.resolveGroupName('pet') // 'PetClient'
1941
- * resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
1023
+ * resolverClient.name('show pet by id') // 'showPetById'
1024
+ * resolverClient.groupName('pet') // 'PetClient'
1942
1025
  * ```
1943
1026
  */
1944
- const resolverClient = (0, _kubb_core.defineResolver)(() => ({
1945
- name: "default",
1946
- pluginName: "plugin-client",
1947
- default(name, type) {
1948
- if (type === "file") return toFilePath(name);
1949
- return ensureValidVarName(camelCase(name));
1950
- },
1951
- resolveName(name) {
1952
- return this.default(name, "function");
1953
- },
1954
- resolvePathName(name, type) {
1955
- return this.default(name, type);
1956
- },
1957
- resolveClassName(name) {
1027
+ const resolverClient = (0, kubb_kit.createResolver)({
1028
+ pluginName: "plugin-contract-client",
1029
+ className(name) {
1958
1030
  return ensureValidVarName(pascalCase(name));
1959
1031
  },
1960
- resolveGroupName(name) {
1032
+ groupName(name) {
1961
1033
  return ensureValidVarName(pascalCase(`${name} Client`));
1962
1034
  },
1963
- resolveClientPropertyName(name) {
1035
+ propertyName(name) {
1964
1036
  return ensureValidVarName(camelCase(name));
1965
- },
1966
- resolveUrlName(node) {
1967
- const name = this.resolveName(node.operationId);
1968
- return `get${name.charAt(0).toUpperCase()}${name.slice(1)}Url`;
1969
1037
  }
1970
- }));
1038
+ });
1039
+ //#endregion
1040
+ //#region src/generators/clientGenerator.tsx
1041
+ /**
1042
+ * Built-in operation generator for `@kubb/plugin-client`. Emits one async function per OpenAPI
1043
+ * operation using the shared `Operation` component: a grouped `<Name>Request` type and a function
1044
+ * that forwards a single `options` object to the user-provided client module and returns the `RequestResult`.
1045
+ */
1046
+ const clientGenerator = createClientGenerator("client", (options) => options.importPath);
1971
1047
  //#endregion
1972
1048
  //#region src/plugin.ts
1973
1049
  /**
1974
- * Canonical plugin name for `@kubb/plugin-client`. Used for driver lookups and
1975
- * cross-plugin dependency references.
1050
+ * Canonical plugin name for `@kubb/plugin-client`.
1976
1051
  */
1977
1052
  const pluginClientName = "plugin-client";
1978
1053
  /**
1979
- * Generates one HTTP client function per OpenAPI operation. Each function has
1980
- * typed path params, query params, body, and response, so callers use the API
1981
- * like any other typed function. Ships with `axios` and `fetch` runtimes; bring
1982
- * your own by setting `importPath`.
1054
+ * Generates typed operations that call a client module owned by your application.
1055
+ * The module at `importPath` must export the runtime helpers and types used by generated operations.
1056
+ * Kubb does not copy or inject a transport implementation.
1983
1057
  *
1984
1058
  * @example
1985
1059
  * ```ts
1986
- * import { defineConfig } from 'kubb'
1987
- * import { pluginTs } from '@kubb/plugin-ts'
1988
- * import { pluginClient } from '@kubb/plugin-client'
1989
- *
1990
- * export default defineConfig({
1991
- * input: { path: './petStore.yaml' },
1992
- * output: { path: './src/gen' },
1993
- * plugins: [
1994
- * pluginTs(),
1995
- * pluginClient({
1996
- * output: { path: './clients' },
1997
- * client: 'fetch',
1998
- * }),
1999
- * ],
2000
- * })
1060
+ * pluginClient({ importPath: '../api/client' })
2001
1061
  * ```
2002
1062
  */
2003
- const pluginClient = (0, _kubb_core.definePlugin)((options) => {
1063
+ const pluginClient = (0, kubb_kit.definePlugin)((options) => {
2004
1064
  const { output = {
2005
1065
  path: "clients",
2006
1066
  barrel: { type: "named" }
2007
- }, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser = false, client = "axios", importPath, bundle = false, sdk, baseURL, resolver: userResolver, transformer: userTransformer } = options;
2008
- const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
2009
- const selectedGenerators = options.generators ?? [
2010
- clientType === "staticClass" ? staticClassClientGenerator : clientType === "class" ? classClientGenerator : clientGenerator,
2011
- group && clientType === "function" ? groupedClientGenerator : null,
2012
- operations ? operationsGenerator : null
2013
- ].filter((x) => Boolean(x));
2014
- const groupConfig = createGroupConfig(group);
1067
+ }, exclude = [], include, override = [], importPath, throwOnErrorDefault = true, validator = false, group, resolver: userResolver } = options;
1068
+ const resolved = {
1069
+ output,
1070
+ exclude,
1071
+ include,
1072
+ override,
1073
+ group: createGroupConfig(group),
1074
+ importPath,
1075
+ baseURL: void 0,
1076
+ throwOnErrorDefault,
1077
+ validator,
1078
+ returnType: "plain",
1079
+ sdk: void 0,
1080
+ resolver: userResolver ? kubb_kit.Resolver.merge(resolverClient, userResolver) : resolverClient
1081
+ };
2015
1082
  return {
2016
1083
  name: pluginClientName,
2017
1084
  options,
2018
- dependencies: [_kubb_plugin_ts.pluginTsName, isParserEnabled(parser) ? _kubb_plugin_zod.pluginZodName : null].filter((dependency) => Boolean(dependency)),
1085
+ dependencies: [_kubb_plugin_ts.pluginTsName, _kubb_plugin_zod.pluginZodName],
2019
1086
  hooks: { "kubb:plugin:setup"(ctx) {
2020
- const resolver = userResolver ? {
2021
- ...resolverClient,
2022
- ...userResolver
2023
- } : resolverClient;
2024
- ctx.setOptions({
2025
- client,
2026
- clientType,
2027
- bundle,
2028
- output,
2029
- exclude,
2030
- include,
2031
- override,
2032
- group: groupConfig,
2033
- parser,
2034
- dataReturnType,
2035
- importPath: resolvedImportPath,
2036
- baseURL,
2037
- paramsType,
2038
- paramsCasing,
2039
- pathParamsType,
2040
- urlType,
2041
- sdk,
2042
- resolver
2043
- });
2044
- ctx.setResolver(resolver);
2045
- if (userTransformer) ctx.setTransformer(userTransformer);
2046
- for (const gen of selectedGenerators) ctx.addGenerator(gen);
2047
- const root = node_path$1.default.resolve(ctx.config.root, ctx.config.output.path);
2048
- if (!resolvedImportPath?.startsWith(".")) {
2049
- const isInlineSource = bundle && !resolvedImportPath;
2050
- ctx.injectFile({
2051
- baseName: "client.ts",
2052
- path: node_path$1.default.resolve(root, ".kubb/client.ts"),
2053
- sources: [_kubb_core.ast.createSource({
2054
- name: "client",
2055
- nodes: isInlineSource ? [_kubb_core.ast.createText(client === "fetch" ? require_templates_clients_fetch_source.source : require_templates_clients_axios_source.source)] : [],
2056
- isExportable: true,
2057
- isIndexable: true
2058
- })],
2059
- exports: !isInlineSource && resolvedImportPath ? [_kubb_core.ast.createExport({ path: resolvedImportPath })] : []
2060
- });
2061
- }
2062
- ctx.injectFile({
2063
- baseName: "config.ts",
2064
- path: node_path$1.default.resolve(root, ".kubb/config.ts"),
2065
- sources: [_kubb_core.ast.createSource({
2066
- name: "config",
2067
- nodes: [_kubb_core.ast.createText(require_templates_config_source.source)],
2068
- isExportable: false,
2069
- isIndexable: false
2070
- })]
2071
- });
1087
+ if (validator && !ctx.config.plugins?.some((plugin) => plugin.name === _kubb_plugin_zod.pluginZodName)) throw new Error(`${pluginClientName}: \`validator\` needs ${_kubb_plugin_zod.pluginZodName}. Add pluginZod() to the plugins or set \`validator: false\`.`);
1088
+ ctx.setOptions(resolved);
1089
+ ctx.setResolver(resolved.resolver);
1090
+ ctx.setMacros([...defaultMacros, ...options.macros ?? []]);
1091
+ ctx.addGenerator(clientGenerator);
2072
1092
  } }
2073
1093
  };
2074
1094
  });
2075
1095
  //#endregion
2076
- exports.Client = Client;
2077
- exports.classClientGenerator = classClientGenerator;
2078
- exports.clientGenerator = clientGenerator;
2079
1096
  exports.default = pluginClient;
2080
- exports.groupedClientGenerator = groupedClientGenerator;
2081
- exports.isParserEnabled = isParserEnabled;
2082
- exports.operationsGenerator = operationsGenerator;
2083
1097
  exports.pluginClient = pluginClient;
2084
1098
  exports.pluginClientName = pluginClientName;
2085
- exports.resolveQueryParamsParser = resolveQueryParamsParser;
2086
- exports.resolveRequestParser = resolveRequestParser;
2087
- exports.resolveResponseParser = resolveResponseParser;
2088
- exports.resolverClient = resolverClient;
2089
- exports.staticClassClientGenerator = staticClassClientGenerator;
2090
1099
 
2091
1100
  //# sourceMappingURL=index.cjs.map