@kubb/plugin-client 5.0.0-beta.3 → 5.0.0-beta.31

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 (43) hide show
  1. package/README.md +24 -4
  2. package/dist/clients/axios.cjs +25 -3
  3. package/dist/clients/axios.cjs.map +1 -1
  4. package/dist/clients/axios.d.ts +9 -2
  5. package/dist/clients/axios.js +25 -3
  6. package/dist/clients/axios.js.map +1 -1
  7. package/dist/clients/fetch.cjs +76 -8
  8. package/dist/clients/fetch.cjs.map +1 -1
  9. package/dist/clients/fetch.d.ts +9 -2
  10. package/dist/clients/fetch.js +76 -8
  11. package/dist/clients/fetch.js.map +1 -1
  12. package/dist/index.cjs +627 -353
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +153 -86
  15. package/dist/index.js +628 -354
  16. package/dist/index.js.map +1 -1
  17. package/dist/templates/clients/axios.source.cjs +1 -1
  18. package/dist/templates/clients/axios.source.js +1 -1
  19. package/dist/templates/clients/fetch.source.cjs +1 -1
  20. package/dist/templates/clients/fetch.source.js +1 -1
  21. package/extension.yaml +1293 -0
  22. package/package.json +11 -17
  23. package/src/clients/axios.ts +41 -7
  24. package/src/clients/fetch.ts +106 -6
  25. package/src/components/ClassClient.tsx +19 -20
  26. package/src/components/Client.tsx +74 -53
  27. package/src/components/Operations.tsx +2 -1
  28. package/src/components/StaticClassClient.tsx +19 -20
  29. package/src/components/Url.tsx +8 -9
  30. package/src/components/WrapperClient.tsx +9 -5
  31. package/src/functionParams.ts +8 -8
  32. package/src/generators/classClientGenerator.tsx +51 -47
  33. package/src/generators/clientGenerator.tsx +37 -48
  34. package/src/generators/groupedClientGenerator.tsx +14 -8
  35. package/src/generators/operationsGenerator.tsx +14 -8
  36. package/src/generators/staticClassClientGenerator.tsx +45 -41
  37. package/src/plugin.ts +27 -26
  38. package/src/resolvers/resolverClient.ts +31 -8
  39. package/src/types.ts +93 -55
  40. package/src/utils.ts +35 -56
  41. package/templates/clients/axios.ts +0 -73
  42. package/templates/clients/fetch.ts +0 -96
  43. package/templates/config.ts +0 -43
@@ -1,27 +1,33 @@
1
- import { defineGenerator } from '@kubb/core'
2
- import { File, jsxRenderer } from '@kubb/renderer-jsx'
1
+ import { ast, defineGenerator } from '@kubb/core'
2
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
3
3
  import { Operations } from '../components/Operations'
4
4
  import type { PluginClient } from '../types'
5
5
 
6
+ /**
7
+ * Generates an `operations.ts` file that re-exports every operation grouped
8
+ * by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
9
+ * for building meta-tooling on top of the generated client (route
10
+ * registries, API explorers).
11
+ */
6
12
  export const operationsGenerator = defineGenerator<PluginClient>({
7
13
  name: 'client',
8
- renderer: jsxRenderer,
14
+ renderer: jsxRendererSync,
9
15
  operations(nodes, ctx) {
10
- const { config, resolver, adapter, root } = ctx
16
+ const { config, resolver, root } = ctx
11
17
  const { output, group } = ctx.options
12
18
 
13
19
  const name = 'operations'
14
- const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group })
20
+ const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
15
21
 
16
22
  return (
17
23
  <File
18
24
  baseName={file.baseName}
19
25
  path={file.path}
20
26
  meta={file.meta}
21
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
22
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
27
+ banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
28
+ footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
23
29
  >
24
- <Operations name={name} nodes={nodes} />
30
+ <Operations name={name} nodes={nodes.filter(ast.isHttpOperationNode)} />
25
31
  </File>
26
32
  )
27
33
  },
@@ -1,12 +1,12 @@
1
1
  import path from 'node:path'
2
- import { camelCase, pascalCase } from '@internals/utils'
3
- import type { ast } from '@kubb/core'
4
- import { defineGenerator } from '@kubb/core'
2
+ import { operationFileEntry, resolveOperationTypeNames } from '@internals/shared'
3
+ import { camelCase } from '@internals/utils'
4
+ import { ast, defineGenerator } from '@kubb/core'
5
5
  import type { ResolverTs } from '@kubb/plugin-ts'
6
6
  import { pluginTsName } from '@kubb/plugin-ts'
7
7
  import type { ResolverZod } from '@kubb/plugin-zod'
8
8
  import { pluginZodName } from '@kubb/plugin-zod'
9
- import { File, jsxRenderer } from '@kubb/renderer-jsx'
9
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
10
10
  import { StaticClassClient } from '../components/StaticClassClient'
11
11
  import type { PluginClient } from '../types'
12
12
 
@@ -14,9 +14,9 @@ type OperationData = {
14
14
  node: ast.OperationNode
15
15
  name: string
16
16
  tsResolver: ResolverTs
17
- zodResolver: ResolverZod | undefined
17
+ zodResolver: ResolverZod | null
18
18
  typeFile: ast.FileNode
19
- zodFile: ast.FileNode | undefined
19
+ zodFile: ast.FileNode | null
20
20
  }
21
21
 
22
22
  type Controller = {
@@ -26,53 +26,53 @@ type Controller = {
26
26
  }
27
27
 
28
28
  function resolveTypeImportNames(node: ast.OperationNode, tsResolver: ResolverTs): Array<string> {
29
- const names: Array<string | undefined> = [
30
- node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined,
31
- tsResolver.resolveResponseName(node),
32
- ...node.parameters.filter((p) => p.in === 'path').map((p) => tsResolver.resolvePathParamsName(node, p)),
33
- ...node.parameters.filter((p) => p.in === 'query').map((p) => tsResolver.resolveQueryParamsName(node, p)),
34
- ...node.parameters.filter((p) => p.in === 'header').map((p) => tsResolver.resolveHeaderParamsName(node, p)),
35
- ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
36
- ]
37
- return names.filter((n): n is string => Boolean(n))
29
+ return resolveOperationTypeNames(node, tsResolver, { order: 'body-response-first' })
38
30
  }
39
31
 
40
32
  function resolveZodImportNames(node: ast.OperationNode, zodResolver: ResolverZod): Array<string> {
41
- const names: Array<string | undefined> = [
33
+ const names: Array<string | null | undefined> = [
42
34
  zodResolver.resolveResponseName?.(node),
43
- node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined,
35
+ node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
44
36
  ]
45
37
  return names.filter((n): n is string => Boolean(n))
46
38
  }
47
39
 
40
+ /**
41
+ * Built-in `operations` generator for `@kubb/plugin-client` when
42
+ * `clientType: 'staticClass'`. Emits one class per tag, with a static method
43
+ * per operation so callers can use `Pet.getPetById(...)` without
44
+ * instantiating the class.
45
+ */
48
46
  export const staticClassClientGenerator = defineGenerator<PluginClient>({
49
47
  name: 'staticClassClient',
50
- renderer: jsxRenderer,
48
+ renderer: jsxRendererSync,
51
49
  operations(nodes, ctx) {
52
- const { adapter, config, driver, resolver, root } = ctx
50
+ const { config, driver, resolver, root } = ctx
53
51
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options
54
- const baseURL = ctx.options.baseURL ?? adapter.inputNode?.meta?.baseURL
52
+ const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL
55
53
 
56
54
  const pluginTs = driver.getPlugin(pluginTsName)
57
55
  if (!pluginTs) return null
58
56
 
59
57
  const tsResolver = driver.getResolver(pluginTsName)
60
58
  const tsPluginOptions = pluginTs.options
61
- const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
62
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
59
+ const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
60
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
63
61
 
64
62
  function buildOperationData(node: ast.OperationNode): OperationData {
65
- const typeFile = tsResolver.resolveFile(
66
- { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
67
- { root, output: tsPluginOptions?.output ?? output, group: tsPluginOptions?.group },
68
- )
63
+ const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
64
+ root,
65
+ output: tsPluginOptions?.output ?? output,
66
+ group: tsPluginOptions?.group,
67
+ })
69
68
  const zodFile =
70
69
  zodResolver && pluginZod?.options
71
- ? zodResolver.resolveFile(
72
- { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
73
- { root, output: pluginZod.options?.output ?? output, group: pluginZod.options?.group },
74
- )
75
- : undefined
70
+ ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
71
+ root,
72
+ output: pluginZod.options?.output ?? output,
73
+ group: pluginZod.options?.group ?? undefined,
74
+ })
75
+ : null
76
76
 
77
77
  return {
78
78
  node: node,
@@ -85,12 +85,13 @@ export const staticClassClientGenerator = defineGenerator<PluginClient>({
85
85
  }
86
86
 
87
87
  const controllers = nodes.reduce((acc, operationNode) => {
88
+ if (!ast.isHttpOperationNode(operationNode)) return acc
88
89
  const tag = operationNode.tags[0]
89
- const groupName = tag ? (group?.name?.({ group: camelCase(tag) }) ?? pascalCase(tag)) : 'Client'
90
+ const groupName = tag ? (group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag)) : resolver.resolveGroupName('Client')
90
91
 
91
92
  if (!tag && !group) {
92
- const name = 'ApiClient'
93
- const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group })
93
+ const name = resolver.resolveClassName('ApiClient')
94
+ const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
94
95
  const operationData = buildOperationData(operationNode)
95
96
  const previous = acc.find((item) => item.file.path === file.path)
96
97
 
@@ -99,9 +100,12 @@ export const staticClassClientGenerator = defineGenerator<PluginClient>({
99
100
  } else {
100
101
  acc.push({ name, file, operations: [operationData] })
101
102
  }
102
- } else if (tag) {
103
+ return acc
104
+ }
105
+
106
+ if (tag) {
103
107
  const name = groupName
104
- const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group })
108
+ const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group: group ?? undefined })
105
109
  const operationData = buildOperationData(operationNode)
106
110
  const previous = acc.find((item) => item.file.path === file.path)
107
111
 
@@ -160,7 +164,7 @@ export const staticClassClientGenerator = defineGenerator<PluginClient>({
160
164
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops)
161
165
  const { zodImportsByFile, zodFilesByPath } =
162
166
  parser === 'zod' ? collectZodImports(ops) : { zodImportsByFile: new Map<string, Set<string>>(), zodFilesByPath: new Map<string, ast.FileNode>() }
163
- const hasFormData = ops.some((op) => op.node.requestBody?.content?.[0]?.contentType === 'multipart/form-data')
167
+ const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false)
164
168
 
165
169
  return (
166
170
  <File
@@ -168,18 +172,18 @@ export const staticClassClientGenerator = defineGenerator<PluginClient>({
168
172
  baseName={file.baseName}
169
173
  path={file.path}
170
174
  meta={file.meta}
171
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
172
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
175
+ banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
176
+ footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
173
177
  >
174
178
  {importPath ? (
175
179
  <>
176
- <File.Import name={'fetch'} path={importPath} />
180
+ <File.Import name={'client'} path={importPath} />
177
181
  <File.Import name={['mergeConfig']} path={importPath} />
178
182
  <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={importPath} isTypeOnly />
179
183
  </>
180
184
  ) : (
181
185
  <>
182
- <File.Import name={['fetch']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
186
+ <File.Import name={['client']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
183
187
  <File.Import name={['mergeConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
184
188
  <File.Import
185
189
  name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
package/src/plugin.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path'
2
- import { camelCase } from '@internals/utils'
2
+ import { createGroupConfig } from '@internals/shared'
3
3
 
4
- import { ast, definePlugin, type Group } from '@kubb/core'
4
+ import { ast, definePlugin } from '@kubb/core'
5
5
  import { pluginTsName } from '@kubb/plugin-ts'
6
6
  import { pluginZodName } from '@kubb/plugin-zod'
7
7
  import { classClientGenerator } from './generators/classClientGenerator.tsx'
@@ -16,20 +16,33 @@ import { source as configSource } from './templates/config.source.ts'
16
16
  import type { PluginClient } from './types.ts'
17
17
 
18
18
  /**
19
- * Canonical plugin name for `@kubb/plugin-client`, used in driver lookups and warnings.
19
+ * Canonical plugin name for `@kubb/plugin-client`. Used for driver lookups and
20
+ * cross-plugin dependency references.
20
21
  */
21
22
  export const pluginClientName = 'plugin-client' satisfies PluginClient['name']
22
23
 
23
24
  /**
24
- * Generates type-safe HTTP client functions or classes from an OpenAPI specification.
25
- * Creates client APIs by walking operations and delegating to generators.
26
- * Writes barrel files based on the configured `barrelType`.
25
+ * Generates one HTTP client function per OpenAPI operation. Each function has
26
+ * typed path params, query params, body, and response, so callers use the API
27
+ * like any other typed function. Ships with `axios` and `fetch` runtimes; bring
28
+ * your own by setting `importPath`.
27
29
  *
28
- * @example Client generator
30
+ * @example
29
31
  * ```ts
30
- * import pluginClient from '@kubb/plugin-client'
32
+ * import { defineConfig } from 'kubb'
33
+ * import { pluginTs } from '@kubb/plugin-ts'
34
+ * import { pluginClient } from '@kubb/plugin-client'
35
+ *
31
36
  * export default defineConfig({
32
- * plugins: [pluginClient({ output: { path: 'clients' } })]
37
+ * input: { path: './petStore.yaml' },
38
+ * output: { path: './src/gen' },
39
+ * plugins: [
40
+ * pluginTs(),
41
+ * pluginClient({
42
+ * output: { path: './clients' },
43
+ * client: 'fetch',
44
+ * }),
45
+ * ],
33
46
  * })
34
47
  * ```
35
48
  */
@@ -47,7 +60,7 @@ export const pluginClient = definePlugin<PluginClient>((options) => {
47
60
  operations = false,
48
61
  paramsCasing,
49
62
  clientType = options.sdk ? 'class' : 'function',
50
- parser = 'client',
63
+ parser = false,
51
64
  client = 'axios',
52
65
  importPath,
53
66
  bundle = false,
@@ -63,28 +76,16 @@ export const pluginClient = definePlugin<PluginClient>((options) => {
63
76
  options.generators ??
64
77
  [
65
78
  clientType === 'staticClass' ? staticClassClientGenerator : clientType === 'class' ? classClientGenerator : clientGenerator,
66
- group && clientType === 'function' ? groupedClientGenerator : undefined,
67
- operations ? operationsGenerator : undefined,
79
+ group && clientType === 'function' ? groupedClientGenerator : null,
80
+ operations ? operationsGenerator : null,
68
81
  ].filter((x): x is NonNullable<typeof x> => Boolean(x))
69
82
 
70
- const groupConfig = group
71
- ? ({
72
- ...group,
73
- name: group.name
74
- ? group.name
75
- : (ctx: { group: string }) => {
76
- if (group.type === 'path') {
77
- return `${ctx.group.split('/')[1]}`
78
- }
79
- return `${camelCase(ctx.group)}Controller`
80
- },
81
- } satisfies Group)
82
- : undefined
83
+ const groupConfig = createGroupConfig(group, { suffix: 'Controller', honorName: true })
83
84
 
84
85
  return {
85
86
  name: pluginClientName,
86
87
  options,
87
- dependencies: [pluginTsName, parser === 'zod' ? pluginZodName : undefined].filter(Boolean),
88
+ dependencies: [pluginTsName, parser === 'zod' ? pluginZodName : null].filter((dependency): dependency is string => Boolean(dependency)),
88
89
  hooks: {
89
90
  'kubb:plugin:setup'(ctx) {
90
91
  const resolver = userResolver ? { ...resolverClient, ...userResolver } : resolverClient
@@ -1,22 +1,45 @@
1
- import { camelCase } from '@internals/utils'
1
+ import { camelCase, ensureValidVarName, pascalCase } from '@internals/utils'
2
2
  import { defineResolver } from '@kubb/core'
3
3
  import type { PluginClient } from '../types.ts'
4
4
 
5
5
  /**
6
- * Naming convention resolver for client plugin.
6
+ * Default resolver used by `@kubb/plugin-client`. Decides the names and file
7
+ * paths for every generated client function or class. Functions and files use
8
+ * camelCase; classes and tag groups use PascalCase.
7
9
  *
8
- * Provides default naming helpers using camelCase for functions and file paths.
10
+ * @example Resolve client function and class names
11
+ * ```ts
12
+ * import { resolverClient } from '@kubb/plugin-client'
9
13
  *
10
- * @example
11
- * `resolverClient.default('list pets', 'function') // 'listPets'`
14
+ * resolverClient.default('list pets', 'function') // 'listPets'
15
+ * resolverClient.resolveClassName('pet') // 'Pet'
16
+ * resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
17
+ * ```
12
18
  */
13
- export const resolverClient = defineResolver<PluginClient>((ctx) => ({
19
+ export const resolverClient = defineResolver<PluginClient>(() => ({
14
20
  name: 'default',
15
21
  pluginName: 'plugin-client',
16
22
  default(name, type) {
17
- return camelCase(name, { isFile: type === 'file' })
23
+ const resolved = camelCase(name, { isFile: type === 'file' })
24
+ return type === 'file' ? resolved : ensureValidVarName(resolved)
18
25
  },
19
26
  resolveName(name) {
20
- return ctx.default(name, 'function')
27
+ return this.default(name, 'function')
28
+ },
29
+ resolvePathName(name, type) {
30
+ return this.default(name, type)
31
+ },
32
+ resolveClassName(name) {
33
+ return ensureValidVarName(pascalCase(name))
34
+ },
35
+ resolveGroupName(name) {
36
+ return ensureValidVarName(pascalCase(name))
37
+ },
38
+ resolveClientPropertyName(name) {
39
+ return ensureValidVarName(camelCase(name))
40
+ },
41
+ resolveUrlName(node) {
42
+ const name = this.resolveName(node.operationId)
43
+ return `get${name.charAt(0).toUpperCase()}${name.slice(1)}Url`
21
44
  },
22
45
  }))
package/src/types.ts CHANGED
@@ -7,10 +7,34 @@ import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginF
7
7
  export type ResolverClient = Resolver & {
8
8
  /**
9
9
  * Resolves the function name for a given raw operation name.
10
+ *
10
11
  * @example Resolving operation names
11
12
  * `resolver.resolveName('show pet by id') // -> 'showPetById'`
12
13
  */
13
14
  resolveName(this: ResolverClient, name: string): string
15
+ /**
16
+ * Resolves the output file name for a client module.
17
+ */
18
+ resolvePathName(this: ResolverClient, name: string, type?: 'file' | 'function' | 'type' | 'const'): string
19
+ /**
20
+ * Resolves the generated class name for class-based clients.
21
+ */
22
+ resolveClassName(this: ResolverClient, name: string): string
23
+ /**
24
+ * Resolves the generated class name for tag-based client groups.
25
+ */
26
+ resolveGroupName(this: ResolverClient, name: string): string
27
+ /**
28
+ * Resolves the generated SDK facade property name for a client class.
29
+ */
30
+ resolveClientPropertyName(this: ResolverClient, name: string): string
31
+ /**
32
+ * Resolves the URL helper function name for an operation.
33
+ *
34
+ * @example Resolving URL helper names
35
+ * `resolver.resolveUrlName(node) // -> 'getShowPetByIdUrl'`
36
+ */
37
+ resolveUrlName(this: ResolverClient, node: ast.OperationNode): string
14
38
  }
15
39
 
16
40
  /**
@@ -22,9 +46,10 @@ export type ResolverClient = Resolver & {
22
46
  export type ClientImportPath =
23
47
  | {
24
48
  /**
25
- * Which client should be used to do the HTTP calls.
26
- * - 'axios' uses axios client for HTTP requests.
27
- * - 'fetch' uses native fetch API for HTTP requests.
49
+ * HTTP client used by the generated code.
50
+ * - `'axios'` imports from `@kubb/plugin-client/clients/axios`. Requires `axios` at runtime.
51
+ * - `'fetch'` imports from `@kubb/plugin-client/clients/fetch`. Uses the global `fetch`.
52
+ *
28
53
  * @default 'axios'
29
54
  */
30
55
  client?: 'axios' | 'fetch'
@@ -33,9 +58,12 @@ export type ClientImportPath =
33
58
  | {
34
59
  client?: never
35
60
  /**
36
- * Client import path for API calls.
37
- * Used as `import client from '${importPath}'`.
38
- * Accepts relative and absolute paths; path changes are not performed.
61
+ * Path to a custom client module. Generated files import their HTTP runtime from here
62
+ * instead of `@kubb/plugin-client/clients/{client}`. Accepts both relative paths and
63
+ * bare module specifiers; the value is used as-is.
64
+ *
65
+ * @note When combined with a query plugin, the module must export `Client`,
66
+ * `RequestConfig`, and `ResponseErrorConfig` types.
39
67
  */
40
68
  importPath: string
41
69
  /**
@@ -57,29 +85,28 @@ export type ClientImportPath =
57
85
  type ParamsTypeOptions =
58
86
  | {
59
87
  /**
60
- * All parameters path, query, headers, and body are merged into a single
88
+ * Every operation parameter (path, query, headers, body) is wrapped in a single
61
89
  * destructured object argument.
62
- * - 'object' returns the params and pathParams as an object.
63
- * @default 'inline'
64
90
  */
65
91
  paramsType: 'object'
66
92
  /**
67
93
  * `pathParamsType` has no effect when `paramsType` is `'object'`.
68
- * Path params are already inside the single destructured object.
94
+ * Path params already live inside the single destructured object.
69
95
  */
70
96
  pathParamsType?: never
71
97
  }
72
98
  | {
73
99
  /**
74
- * Each parameter group is emitted as a separate function argument.
75
- * - 'inline' returns the params as comma separated params.
100
+ * Each parameter group is emitted as a separate positional function argument.
101
+ *
76
102
  * @default 'inline'
77
103
  */
78
104
  paramsType?: 'inline'
79
105
  /**
80
- * Controls how path parameters are arranged within the inline argument list.
81
- * - 'object' groups path params into a destructured object: `{ petId }: PathParams`.
82
- * - 'inline' emits each path param as its own argument: `petId: string`.
106
+ * How URL path parameters are arranged inside the inline argument list.
107
+ * - `'object'` groups them into one destructured object: `{ petId }: PathParams`.
108
+ * - `'inline'` emits each path param as its own argument: `petId: string`.
109
+ *
83
110
  * @default 'inline'
84
111
  */
85
112
  pathParamsType?: 'object' | 'inline'
@@ -87,87 +114,97 @@ type ParamsTypeOptions =
87
114
 
88
115
  export type Options = {
89
116
  /**
90
- * Specify the export location for the files and define the behavior of the output.
91
- * @default { path: 'clients', barrelType: 'named' }
117
+ * Where the generated client files are written and how they are exported.
118
+ *
119
+ * @default { path: 'clients', barrel: { type: 'named' } }
92
120
  */
93
121
  output?: Output
94
122
  /**
95
- * Group the clients based on the provided name.
123
+ * Split generated files into subfolders based on the operation's tag.
96
124
  */
97
125
  group?: Group
98
126
  /**
99
- * Array containing exclude parameters to exclude/skip tags/operations/methods/paths.
127
+ * Skip operations matching at least one entry in the list.
100
128
  */
101
129
  exclude?: Array<Exclude>
102
130
  /**
103
- * Array containing include parameters to include tags/operations/methods/paths.
131
+ * Restrict generation to operations matching at least one entry in the list.
104
132
  */
105
133
  include?: Array<Include>
106
134
  /**
107
- * Array containing override parameters to override `options` based on tags/operations/methods/paths.
135
+ * Apply a different options object to operations matching a pattern.
108
136
  */
109
137
  override?: Array<Override<ResolvedOptions>>
110
138
  /**
111
- * Create `operations.ts` file with all operations grouped by methods.
139
+ * Emit an `operations.ts` file that re-exports every generated function grouped by HTTP method.
140
+ *
112
141
  * @default false
113
142
  */
114
143
  operations?: boolean
115
144
  /**
116
- * Export urls that are used by operation x.
117
- * - 'export' makes them part of your barrel file.
118
- * - false does not make them exportable.
145
+ * Whether to also export the URL builder helpers (`get<Operation>Url`).
146
+ * - `'export'` exposes them via the barrel.
147
+ * - `false` keeps them private.
148
+ *
119
149
  * @default false
120
- * @example getGetPetByIdUrl
121
150
  */
122
151
  urlType?: 'export' | false
123
152
  /**
124
- * Allows you to set a custom base url for all generated calls.
153
+ * Base URL prepended to every request. When omitted, falls back to the adapter's
154
+ * server URL (typically `servers[0].url`).
125
155
  */
126
156
  baseURL?: string
127
157
  /**
128
- * ReturnType that is used when calling the client.
129
- * - 'data' returns ResponseConfig[data].
130
- * - 'full' returns ResponseConfig.
158
+ * Shape of the value returned by each generated client function.
159
+ * - `'data'` only the response body.
160
+ * - `'full'` the full response config (body, status, headers, request).
161
+ *
131
162
  * @default 'data'
132
163
  */
133
164
  dataReturnType?: 'data' | 'full'
134
165
  /**
135
- * How to style your params, by default no casing is applied.
136
- * - 'camelcase' uses camelCase for pathParams, queryParams and headerParams names
137
- * @note response types (data/body) are not affected by this option
166
+ * Rename parameter properties in the generated client (path, query, headers).
167
+ * The HTTP request still uses the original spec names; Kubb writes the mapping for you.
168
+ *
169
+ * @note Use the same value on `@kubb/plugin-ts` so types stay compatible.
138
170
  */
139
171
  paramsCasing?: 'camelcase'
140
172
  /**
141
- * Which parser can be used before returning the data.
142
- * - 'client' returns the data as-is from the client.
143
- * - 'zod' uses @kubb/plugin-zod to parse the data.
144
- * @default 'client'
173
+ * Validator applied to response bodies before they are returned to the caller.
174
+ * - `false` (default) — no validation. The client has no runtime parser; the response is
175
+ * returned as-is, cast to the generated TypeScript type.
176
+ * - `'zod'` — pipes responses through schemas from `@kubb/plugin-zod`.
177
+ *
178
+ * @default false
145
179
  */
146
- parser?: 'client' | 'zod'
180
+ parser?: false | 'zod'
147
181
  /**
148
- * How to generate the client code.
149
- * - 'function' generates standalone functions for each operation.
150
- * - 'class' generates a class with methods for each operation.
151
- * - 'staticClass' generates a class with static methods for each operation.
182
+ * Shape of the generated client.
183
+ * - `'function'` one standalone async function per operation.
184
+ * - `'class'` one class per tag with instance methods.
185
+ * - `'staticClass'` one class per tag with static methods.
186
+ *
152
187
  * @default 'function'
188
+ * @note Only `'function'` is compatible with query plugins.
153
189
  */
154
190
  clientType?: 'function' | 'class' | 'staticClass'
155
191
  /**
156
- * Bundle the selected client into the generated `.kubb` directory.
157
- * When disabled the generated clients will import the shared runtime from `@kubb/plugin-client/clients/*`.
192
+ * Copy the HTTP client runtime into the generated output so consumers do not need
193
+ * `@kubb/plugin-client` at runtime. When `false`, generated files import from
194
+ * `@kubb/plugin-client/clients/{client}`.
195
+ *
158
196
  * @default false
159
- * In version 5 of Kubb this is by default true
160
197
  */
161
198
  bundle?: boolean
162
199
  /**
163
- * Generate an SDK facade class that composes all tag-based client classes into a single entry point.
164
- * Setting this option automatically enables `clientType: 'class'`.
200
+ * Generate a single SDK class composing every tag-based client into one entry point.
201
+ * Automatically enables `clientType: 'class'`.
202
+ *
165
203
  * @example
166
204
  * ```ts
167
205
  * pluginClient({
168
206
  * sdk: { className: 'PetStoreSDK' },
169
207
  * })
170
- * // Generates a class with a shared constructor config and one property per tag:
171
208
  * // class PetStoreSDK {
172
209
  * // readonly petController: petController
173
210
  * // readonly storeController: storeController
@@ -177,22 +214,23 @@ export type Options = {
177
214
  */
178
215
  sdk?: {
179
216
  /**
180
- * Name of the generated SDK facade class.
217
+ * Name of the generated SDK facade class. Also the file name.
181
218
  */
182
219
  className: string
183
220
  }
184
221
  /**
185
- * Override individual resolver methods. Any method you omit falls back to the
186
- * preset resolver's implementation. Use `this.default(...)` to call it.
222
+ * Override how names and file paths are built for the generated client.
223
+ * Methods you omit fall back to the default resolver. `this` is bound to the
224
+ * full resolver, so `this.default(name)` delegates to the built-in implementation.
187
225
  */
188
226
  resolver?: Partial<ResolverClient> & ThisType<ResolverClient>
189
227
  /**
190
- * Single AST visitor applied to each node before printing.
191
- * Return `null` or `undefined` from a method to leave the node unchanged.
228
+ * AST visitor applied to each operation node before code is printed.
229
+ * Return `null` or `undefined` to leave the node unchanged.
192
230
  */
193
231
  transformer?: ast.Visitor
194
232
  /**
195
- * Define some generators next to the client generators.
233
+ * Custom generators that run alongside the built-in client generators.
196
234
  */
197
235
  generators?: Array<Generator<PluginClient>>
198
236
  } & ClientImportPath &
@@ -203,7 +241,7 @@ type ResolvedOptions = {
203
241
  exclude: Array<Exclude>
204
242
  include: Array<Include> | undefined
205
243
  override: Array<Override<ResolvedOptions>>
206
- group: Group | undefined
244
+ group: Group | null
207
245
  client: Options['client']
208
246
  clientType: NonNullable<Options['clientType']>
209
247
  bundle: NonNullable<Options['bundle']>