@kubb/plugin-vue-query 5.0.0-alpha.9 → 5.0.0-beta.4

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 (47) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +1 -3
  3. package/dist/components-D1UhYFgY.js +1277 -0
  4. package/dist/components-D1UhYFgY.js.map +1 -0
  5. package/dist/components-qfOFRSoM.cjs +1367 -0
  6. package/dist/components-qfOFRSoM.cjs.map +1 -0
  7. package/dist/components.cjs +1 -1
  8. package/dist/components.d.ts +118 -109
  9. package/dist/components.js +1 -1
  10. package/dist/generators-C4gs_P1i.cjs +726 -0
  11. package/dist/generators-C4gs_P1i.cjs.map +1 -0
  12. package/dist/generators-CbnIVBgY.js +709 -0
  13. package/dist/generators-CbnIVBgY.js.map +1 -0
  14. package/dist/generators.cjs +1 -1
  15. package/dist/generators.d.ts +5 -501
  16. package/dist/generators.js +1 -1
  17. package/dist/index.cjs +106 -121
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +102 -121
  21. package/dist/index.js.map +1 -1
  22. package/dist/types-nVDTfuS1.d.ts +194 -0
  23. package/extension.yaml +793 -0
  24. package/package.json +61 -64
  25. package/src/components/InfiniteQuery.tsx +104 -153
  26. package/src/components/InfiniteQueryOptions.tsx +122 -162
  27. package/src/components/Mutation.tsx +110 -136
  28. package/src/components/Query.tsx +102 -151
  29. package/src/components/QueryKey.tsx +68 -58
  30. package/src/components/QueryOptions.tsx +147 -139
  31. package/src/generators/infiniteQueryGenerator.tsx +165 -170
  32. package/src/generators/mutationGenerator.tsx +117 -124
  33. package/src/generators/queryGenerator.tsx +138 -136
  34. package/src/index.ts +1 -1
  35. package/src/plugin.ts +124 -175
  36. package/src/resolvers/resolverVueQuery.ts +19 -0
  37. package/src/types.ts +68 -48
  38. package/src/utils.ts +37 -0
  39. package/dist/components-Yjoe78Y7.cjs +0 -1119
  40. package/dist/components-Yjoe78Y7.cjs.map +0 -1
  41. package/dist/components-_AMBl0g-.js +0 -1029
  42. package/dist/components-_AMBl0g-.js.map +0 -1
  43. package/dist/generators-CR34GjVu.js +0 -661
  44. package/dist/generators-CR34GjVu.js.map +0 -1
  45. package/dist/generators-DH8VkK1q.cjs +0 -678
  46. package/dist/generators-DH8VkK1q.cjs.map +0 -1
  47. package/dist/types-CgDFUvfZ.d.ts +0 -211
@@ -1,18 +1,20 @@
1
1
  import { getNestedAccessor } from '@internals/utils'
2
- import { getDefaultValue, isOptional } from '@kubb/oas'
3
- import { Client } from '@kubb/plugin-client/components'
4
- import type { OperationSchemas } from '@kubb/plugin-oas'
5
- import { getPathParams } from '@kubb/plugin-oas/utils'
6
- import { File, Function, FunctionParams } from '@kubb/react-fabric'
7
- import type { FabricReactNode } from '@kubb/react-fabric/types'
2
+ import type { ast } from '@kubb/core'
3
+ import type { ResolverTs } from '@kubb/plugin-ts'
4
+ import { functionPrinter } from '@kubb/plugin-ts'
5
+ import { File, Function } from '@kubb/renderer-jsx'
6
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
8
7
  import type { Infinite, PluginVueQuery } from '../types.ts'
8
+ import { resolveErrorNames } from '../utils.ts'
9
9
  import { QueryKey } from './QueryKey.tsx'
10
+ import { buildEnabledCheck, getQueryOptionsParams } from './QueryOptions.tsx'
10
11
 
11
12
  type Props = {
12
13
  name: string
13
14
  clientName: string
14
15
  queryKeyName: string
15
- typeSchemas: OperationSchemas
16
+ node: ast.OperationNode
17
+ tsResolver: ResolverTs
16
18
  paramsCasing: PluginVueQuery['resolvedOptions']['paramsCasing']
17
19
  paramsType: PluginVueQuery['resolvedOptions']['paramsType']
18
20
  pathParamsType: PluginVueQuery['resolvedOptions']['pathParamsType']
@@ -24,105 +26,8 @@ type Props = {
24
26
  queryParam: Infinite['queryParam']
25
27
  }
26
28
 
27
- type GetParamsProps = {
28
- paramsCasing: PluginVueQuery['resolvedOptions']['paramsCasing']
29
- paramsType: PluginVueQuery['resolvedOptions']['paramsType']
30
- pathParamsType: PluginVueQuery['resolvedOptions']['pathParamsType']
31
- typeSchemas: OperationSchemas
32
- }
33
-
34
- function getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas }: GetParamsProps) {
35
- if (paramsType === 'object') {
36
- const children = {
37
- ...getPathParams(typeSchemas.pathParams, {
38
- typed: true,
39
- casing: paramsCasing,
40
- override(item) {
41
- return {
42
- ...item,
43
- type: `MaybeRefOrGetter<${item.type}>`,
44
- }
45
- },
46
- }),
47
- data: typeSchemas.request?.name
48
- ? {
49
- type: `MaybeRefOrGetter<${typeSchemas.request?.name}>`,
50
- optional: isOptional(typeSchemas.request?.schema),
51
- }
52
- : undefined,
53
- params: typeSchemas.queryParams?.name
54
- ? {
55
- type: `MaybeRefOrGetter<${typeSchemas.queryParams?.name}>`,
56
- optional: isOptional(typeSchemas.queryParams?.schema),
57
- }
58
- : undefined,
59
- headers: typeSchemas.headerParams?.name
60
- ? {
61
- type: `MaybeRefOrGetter<${typeSchemas.queryParams?.name}>`,
62
- optional: isOptional(typeSchemas.headerParams?.schema),
63
- }
64
- : undefined,
65
- }
66
-
67
- // Check if all children are optional or undefined
68
- const allChildrenAreOptional = Object.values(children).every((child) => !child || child.optional)
69
-
70
- return FunctionParams.factory({
71
- data: {
72
- mode: 'object',
73
- children,
74
- default: allChildrenAreOptional ? '{}' : undefined,
75
- },
76
- config: {
77
- type: typeSchemas.request?.name
78
- ? `Partial<RequestConfig<${typeSchemas.request?.name}>> & { client?: Client }`
79
- : 'Partial<RequestConfig> & { client?: Client }',
80
- default: '{}',
81
- },
82
- })
83
- }
84
-
85
- return FunctionParams.factory({
86
- pathParams: {
87
- mode: pathParamsType === 'object' ? 'object' : 'inlineSpread',
88
- children: getPathParams(typeSchemas.pathParams, {
89
- typed: true,
90
- casing: paramsCasing,
91
- override(item) {
92
- return {
93
- ...item,
94
- type: `MaybeRefOrGetter<${item.type}>`,
95
- }
96
- },
97
- }),
98
- default: getDefaultValue(typeSchemas.pathParams?.schema),
99
- },
100
- data: typeSchemas.request?.name
101
- ? {
102
- type: `MaybeRefOrGetter<${typeSchemas.request?.name}>`,
103
- optional: isOptional(typeSchemas.request?.schema),
104
- }
105
- : undefined,
106
- params: typeSchemas.queryParams?.name
107
- ? {
108
- type: `MaybeRefOrGetter<${typeSchemas.queryParams?.name}>`,
109
- optional: isOptional(typeSchemas.queryParams?.schema),
110
- }
111
- : undefined,
112
- headers: typeSchemas.headerParams?.name
113
- ? {
114
- type: `MaybeRefOrGetter<${typeSchemas.headerParams?.name}>`,
115
- optional: isOptional(typeSchemas.headerParams?.schema),
116
- }
117
- : undefined,
118
- config: {
119
- type: typeSchemas.request?.name
120
- ? `Partial<RequestConfig<${typeSchemas.request?.name}>> & { client?: Client }`
121
- : 'Partial<RequestConfig> & { client?: Client }',
122
- default: '{}',
123
- },
124
- })
125
- }
29
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
30
+ const callPrinter = functionPrinter({ mode: 'call' })
126
31
 
127
32
  export function InfiniteQueryOptions({
128
33
  name,
@@ -131,39 +36,65 @@ export function InfiniteQueryOptions({
131
36
  cursorParam,
132
37
  nextParam,
133
38
  previousParam,
134
- typeSchemas,
135
- paramsType,
39
+ node,
40
+ tsResolver,
136
41
  paramsCasing,
42
+ paramsType,
137
43
  dataReturnType,
138
44
  pathParamsType,
139
45
  queryParam,
140
46
  queryKeyName,
141
- }: Props): FabricReactNode {
142
- const TData = dataReturnType === 'data' ? typeSchemas.response.name : `ResponseConfig<${typeSchemas.response.name}>`
143
- const TError = `ResponseErrorConfig<${typeSchemas.errors?.map((item) => item.name).join(' | ') || 'Error'}>`
144
-
145
- const params = getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas })
146
- const clientParams = Client.getParams({
147
- paramsType,
148
- paramsCasing,
149
- typeSchemas,
150
- pathParamsType,
151
- isConfigurable: true,
152
- })
153
- const queryKeyParams = QueryKey.getParams({
154
- paramsCasing,
155
- pathParamsType,
156
- typeSchemas,
157
- })
47
+ }: Props): KubbReactNode {
48
+ const responseName = tsResolver.resolveResponseName(node)
49
+ const queryFnDataType = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
50
+ const errorNames = resolveErrorNames(node, tsResolver)
51
+ const errorType = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
52
+
53
+ const isInitialPageParamDefined = initialPageParam !== undefined && initialPageParam !== null
54
+ const fallbackPageParamType =
55
+ typeof initialPageParam === 'number'
56
+ ? 'number'
57
+ : typeof initialPageParam === 'string'
58
+ ? initialPageParam.includes(' as ')
59
+ ? (() => {
60
+ const parts = initialPageParam.split(' as ')
61
+ return parts[parts.length - 1] ?? 'unknown'
62
+ })()
63
+ : 'string'
64
+ : typeof initialPageParam === 'boolean'
65
+ ? 'boolean'
66
+ : 'unknown'
67
+
68
+ const rawQueryParams = node.parameters.filter((p) => p.in === 'query')
69
+ const queryParamsTypeName =
70
+ rawQueryParams.length > 0
71
+ ? (() => {
72
+ const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
73
+ const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
74
+ return groupName !== individualName ? groupName : undefined
75
+ })()
76
+ : undefined
77
+
78
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
79
+ const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
80
+
81
+ const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
82
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
83
+ const rawParamsCall = callPrinter.print(paramsNode) ?? ''
84
+ const clientCallStr = rawParamsCall.replace(/\bconfig\b(?=[^,]*$)/, '{ ...config, signal: config.signal ?? signal }')
85
+
86
+ const queryKeyParamsNode = QueryKey.getParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
87
+ const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
88
+
89
+ const enabledSource = buildEnabledCheck(queryKeyParamsNode)
90
+ const enabledText = enabledSource ? `enabled: () => !!(${enabledSource}),` : ''
158
91
 
159
- // Determine if we should use the new nextParam/previousParam or fall back to legacy cursorParam behavior
160
92
  const hasNewParams = nextParam !== undefined || previousParam !== undefined
161
93
 
162
94
  let getNextPageParamExpr: string | undefined
163
95
  let getPreviousPageParamExpr: string | undefined
164
96
 
165
97
  if (hasNewParams) {
166
- // Use the new nextParam and previousParam
167
98
  if (nextParam) {
168
99
  const accessor = getNestedAccessor(nextParam, 'lastPage')
169
100
  if (accessor) {
@@ -177,11 +108,9 @@ export function InfiniteQueryOptions({
177
108
  }
178
109
  }
179
110
  } else if (cursorParam) {
180
- // Legacy behavior: use cursorParam for both next and previous
181
111
  getNextPageParamExpr = `getNextPageParam: (lastPage) => lastPage['${cursorParam}']`
182
112
  getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`
183
113
  } else {
184
- // Fallback behavior: page-based pagination
185
114
  if (dataReturnType === 'full') {
186
115
  getNextPageParamExpr =
187
116
  'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
@@ -192,53 +121,54 @@ export function InfiniteQueryOptions({
192
121
  getPreviousPageParamExpr = 'getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1'
193
122
  }
194
123
 
195
- const queryOptions = [
124
+ const queryOptionsArr = [
196
125
  `initialPageParam: ${typeof initialPageParam === 'string' ? JSON.stringify(initialPageParam) : initialPageParam}`,
197
126
  getNextPageParamExpr,
198
127
  getPreviousPageParamExpr,
199
128
  ].filter(Boolean)
200
129
 
201
130
  const infiniteOverrideParams =
202
- queryParam && typeSchemas.queryParams?.name
131
+ queryParam && queryParamsTypeName
203
132
  ? `
204
- if (!params) {
205
- params = { }
206
- }
207
- params['${queryParam}'] = pageParam as unknown as ${typeSchemas.queryParams?.name}['${queryParam}']`
133
+ params = {
134
+ ...(params ?? {}),
135
+ ['${queryParam}']: pageParam as unknown as ${queryParamsTypeName}['${queryParam}'],
136
+ } as ${queryParamsTypeName}`
208
137
  : ''
209
138
 
210
- const enabled = Object.entries(queryKeyParams.flatParams)
211
- .map(([key, item]) => {
212
- // Only include if the parameter exists and is NOT optional
213
- // This ensures we only check required parameters
214
- return item && !item.optional && !item.default ? key : undefined
215
- })
216
- .filter(Boolean)
217
- .join('&& ')
218
-
219
- const enabledText = enabled ? `enabled: !!(${enabled}),` : ''
139
+ if (infiniteOverrideParams) {
140
+ return (
141
+ <File.Source name={name} isExportable isIndexable>
142
+ <Function name={name} export params={paramsSignature}>
143
+ {`
144
+ const queryKey = ${queryKeyName}(${queryKeyParamsCall})
145
+ return infiniteQueryOptions<${queryFnDataType}, ${errorType}, InfiniteData<${queryFnDataType}>, QueryKey, ${pageParamType}>({
146
+ ${enabledText}
147
+ queryKey,
148
+ queryFn: async ({ signal, pageParam }) => {
149
+ ${infiniteOverrideParams}
150
+ return ${clientName}(${addToValueCalls(clientCallStr)})
151
+ },
152
+ ${queryOptionsArr.join(',\n')}
153
+ })
154
+ `}
155
+ </Function>
156
+ </File.Source>
157
+ )
158
+ }
220
159
 
221
160
  return (
222
161
  <File.Source name={name} isExportable isIndexable>
223
- <Function name={name} export params={params.toConstructor()}>
162
+ <Function name={name} export params={paramsSignature}>
224
163
  {`
225
- const queryKey = ${queryKeyName}(${queryKeyParams.toCall()})
226
- return infiniteQueryOptions<${TData}, ${TError}, ${TData}, typeof queryKey, number>({
164
+ const queryKey = ${queryKeyName}(${queryKeyParamsCall})
165
+ return infiniteQueryOptions<${queryFnDataType}, ${errorType}, InfiniteData<${queryFnDataType}>, QueryKey, ${pageParamType}>({
227
166
  ${enabledText}
228
167
  queryKey,
229
- queryFn: async ({ signal, pageParam }) => {
230
- ${infiniteOverrideParams}
231
- return ${clientName}(${clientParams.toCall({
232
- transformName(name) {
233
- if (name === 'config') {
234
- return '{ ...config, signal: config.signal ?? signal }'
235
- }
236
-
237
- return `toValue(${name})`
238
- },
239
- })})
240
- },
241
- ${queryOptions.join(',\n')}
168
+ queryFn: async ({ signal }) => {
169
+ return ${clientName}(${addToValueCalls(clientCallStr)})
170
+ },
171
+ ${queryOptionsArr.join(',\n')}
242
172
  })
243
173
  `}
244
174
  </Function>
@@ -246,4 +176,34 @@ export function InfiniteQueryOptions({
246
176
  )
247
177
  }
248
178
 
249
- InfiniteQueryOptions.getParams = getParams
179
+ function addToValueCalls(callStr: string): string {
180
+ // Step 1: Transform shorthand object params like { petId } → { petId: toValue(petId) }
181
+ let result = callStr.replace(/\{\s*([\w,\s]+)\s*\}(?=\s*,)/g, (match, inner: string) => {
182
+ if (inner.includes(':') || inner.includes('...')) return match
183
+ const keys = inner
184
+ .split(',')
185
+ .map((k: string) => k.trim())
186
+ .filter(Boolean)
187
+ const wrapped = keys.map((k: string) => `${k}: toValue(${k})`).join(', ')
188
+ return `{ ${wrapped} }`
189
+ })
190
+
191
+ // Step 2: Handle standalone identifiers like `data, params`
192
+ result = result.replace(/(?<![{.:?])\b(\w+)\b(?=\s*,)/g, (match, name: string) => {
193
+ if (name === 'config' || name === 'signal' || name === 'undefined') return match
194
+ if (match.includes('toValue(')) return match
195
+ return `toValue(${name})`
196
+ })
197
+
198
+ return result
199
+ }
200
+
201
+ InfiniteQueryOptions.getParams = (
202
+ node: ast.OperationNode,
203
+ options: {
204
+ paramsType: PluginVueQuery['resolvedOptions']['paramsType']
205
+ paramsCasing: PluginVueQuery['resolvedOptions']['paramsCasing']
206
+ pathParamsType: PluginVueQuery['resolvedOptions']['pathParamsType']
207
+ resolver: ResolverTs
208
+ },
209
+ ) => getQueryOptionsParams(node, options)
@@ -1,84 +1,90 @@
1
- import { isOptional, type Operation } from '@kubb/oas'
2
- import { Client } from '@kubb/plugin-client/components'
3
- import type { OperationSchemas } from '@kubb/plugin-oas'
4
- import { getComments, getPathParams } from '@kubb/plugin-oas/utils'
5
- import { File, Function, FunctionParams } from '@kubb/react-fabric'
6
- import type { FabricReactNode, Params } from '@kubb/react-fabric/types'
1
+ import { ast } from '@kubb/core'
2
+ import type { ResolverTs } from '@kubb/plugin-ts'
3
+ import { functionPrinter } from '@kubb/plugin-ts'
4
+ import { File, Function } from '@kubb/renderer-jsx'
5
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
6
  import type { PluginVueQuery } from '../types.ts'
7
+ import { buildMutationArgParams, getComments, resolveErrorNames } from '../utils.ts'
8
8
  import { MutationKey } from './MutationKey.tsx'
9
9
 
10
10
  type Props = {
11
- /**
12
- * Name of the function
13
- */
14
11
  name: string
15
12
  typeName: string
16
13
  clientName: string
17
14
  mutationKeyName: string
18
- typeSchemas: OperationSchemas
19
- operation: Operation
15
+ node: ast.OperationNode
16
+ tsResolver: ResolverTs
20
17
  paramsCasing: PluginVueQuery['resolvedOptions']['paramsCasing']
21
18
  paramsType: PluginVueQuery['resolvedOptions']['paramsType']
22
19
  dataReturnType: PluginVueQuery['resolvedOptions']['client']['dataReturnType']
23
20
  pathParamsType: PluginVueQuery['resolvedOptions']['pathParamsType']
24
21
  }
25
22
 
26
- type GetParamsProps = {
27
- paramsCasing: PluginVueQuery['resolvedOptions']['paramsCasing']
28
- pathParamsType: PluginVueQuery['resolvedOptions']['pathParamsType']
29
- dataReturnType: PluginVueQuery['resolvedOptions']['client']['dataReturnType']
30
- typeSchemas: OperationSchemas
31
- }
23
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
24
+ const callPrinter = functionPrinter({ mode: 'call' })
25
+ const keysPrinter = functionPrinter({ mode: 'keys' })
26
+
27
+ function getParams(
28
+ node: ast.OperationNode,
29
+ options: {
30
+ paramsCasing: PluginVueQuery['resolvedOptions']['paramsCasing']
31
+ dataReturnType: PluginVueQuery['resolvedOptions']['client']['dataReturnType']
32
+ resolver: ResolverTs
33
+ },
34
+ ): ast.FunctionParametersNode {
35
+ const { paramsCasing, dataReturnType, resolver } = options
36
+ const responseName = resolver.resolveResponseName(node)
37
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
38
+ const errorNames = resolveErrorNames(node, resolver)
39
+
40
+ const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
41
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
42
+
43
+ const mutationArgParamsNode = buildMutationArgParams(node, { paramsCasing, resolver })
32
44
 
33
- function getParams({ paramsCasing, dataReturnType, typeSchemas }: GetParamsProps) {
34
- const TData = dataReturnType === 'data' ? typeSchemas.response.name : `ResponseConfig<${typeSchemas.response.name}>`
35
- const TError = `ResponseErrorConfig<${typeSchemas.errors?.map((item) => item.name).join(' | ') || 'Error'}>`
36
-
37
- const mutationParams = FunctionParams.factory({
38
- ...getPathParams(typeSchemas.pathParams, {
39
- typed: true,
40
- casing: paramsCasing,
41
- override(item) {
42
- return {
43
- ...item,
44
- type: `MaybeRefOrGetter<${item.type}>`,
45
- }
46
- },
47
- }),
48
- data: typeSchemas.request?.name
49
- ? {
50
- type: `MaybeRefOrGetter<${typeSchemas.request?.name}>`,
51
- optional: isOptional(typeSchemas.request?.schema),
52
- }
53
- : undefined,
54
- params: typeSchemas.queryParams?.name
55
- ? {
56
- type: `MaybeRefOrGetter<${typeSchemas.queryParams?.name}>`,
57
- optional: isOptional(typeSchemas.queryParams?.schema),
58
- }
59
- : undefined,
60
- headers: typeSchemas.headerParams?.name
61
- ? {
62
- type: `MaybeRefOrGetter<${typeSchemas.headerParams?.name}>`,
63
- optional: isOptional(typeSchemas.headerParams?.schema),
64
- }
65
- : undefined,
45
+ // Vue-query uses MutationObserverOptions instead of UseMutationOptions, and wraps params with MaybeRefOrGetter
46
+ const mutationArgWrapped = mutationArgParamsNode.params.map((param) => {
47
+ const fp = param as ast.FunctionParameterNode
48
+ return {
49
+ ...fp,
50
+ type: fp.type ? ast.createParamsType({ variant: 'reference', name: `MaybeRefOrGetter<${printType(fp.type)}>` }) : fp.type,
51
+ }
66
52
  })
67
- const TRequest = mutationParams.toConstructor()
68
-
69
- return FunctionParams.factory({
70
- options: {
71
- type: `
72
- {
73
- mutation?: MutationObserverOptions<${[TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')}> & { client?: QueryClient },
74
- client?: ${typeSchemas.request?.name ? `Partial<RequestConfig<${typeSchemas.request?.name}>> & { client?: Client }` : 'Partial<RequestConfig> & { client?: Client }'},
75
- }
76
- `,
77
- default: '{}',
78
- },
53
+ const wrappedParamsNode = ast.createFunctionParameters({ params: mutationArgWrapped })
54
+ const TRequestWrapped = wrappedParamsNode.params.length > 0 ? (declarationPrinter.print(wrappedParamsNode) ?? '') : ''
55
+
56
+ return ast.createFunctionParameters({
57
+ params: [
58
+ ast.createFunctionParameter({
59
+ name: 'options',
60
+ type: ast.createParamsType({
61
+ variant: 'reference',
62
+ name: `{
63
+ mutation?: MutationObserverOptions<${[TData, TError, TRequestWrapped ? `{${TRequestWrapped}}` : 'void', 'TContext'].join(', ')}> & { client?: QueryClient },
64
+ client?: ${requestName ? `Partial<RequestConfig<${requestName}>> & { client?: Client }` : 'Partial<RequestConfig> & { client?: Client }'},
65
+ }`,
66
+ }),
67
+ default: '{}',
68
+ }),
69
+ ],
79
70
  })
80
71
  }
81
72
 
73
+ function printType(typeNode: ast.ParamsTypeNode | undefined): string {
74
+ if (!typeNode) return 'unknown'
75
+ if (typeNode.variant === 'reference') return typeNode.name
76
+ if (typeNode.variant === 'member') return `${typeNode.base}['${typeNode.key}']`
77
+ if (typeNode.variant === 'struct') {
78
+ const parts = typeNode.properties.map((p) => {
79
+ const typeStr = printType(p.type)
80
+ const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(p.name) ? p.name : JSON.stringify(p.name)
81
+ return p.optional ? `${key}?: ${typeStr}` : `${key}: ${typeStr}`
82
+ })
83
+ return `{ ${parts.join('; ')} }`
84
+ }
85
+ return 'unknown'
86
+ }
87
+
82
88
  export function Mutation({
83
89
  name,
84
90
  clientName,
@@ -86,94 +92,60 @@ export function Mutation({
86
92
  paramsType,
87
93
  pathParamsType,
88
94
  dataReturnType,
89
- typeSchemas,
90
- operation,
95
+ node,
96
+ tsResolver,
91
97
  mutationKeyName,
92
- }: Props): FabricReactNode {
93
- const mutationKeyParams = MutationKey.getParams({
94
- pathParamsType,
95
- typeSchemas,
96
- })
98
+ }: Props): KubbReactNode {
99
+ const responseName = tsResolver.resolveResponseName(node)
100
+ const errorNames = resolveErrorNames(node, tsResolver)
97
101
 
98
- const params = getParams({
99
- paramsCasing,
100
- pathParamsType,
101
- dataReturnType,
102
- typeSchemas,
103
- })
102
+ const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
103
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
104
104
 
105
- const clientParams = Client.getParams({
106
- paramsCasing,
107
- paramsType,
108
- typeSchemas,
109
- pathParamsType,
110
- isConfigurable: true,
111
- })
105
+ const mutationArgParamsNode = buildMutationArgParams(node, { paramsCasing, resolver: tsResolver })
106
+ const hasMutationParams = mutationArgParamsNode.params.length > 0
107
+ const TRequest = hasMutationParams ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
108
+ const argKeysStr = hasMutationParams ? (keysPrinter.print(mutationArgParamsNode) ?? '') : ''
112
109
 
113
- const mutationParams = FunctionParams.factory({
114
- ...getPathParams(typeSchemas.pathParams, { typed: true, casing: paramsCasing }),
115
- data: typeSchemas.request?.name
116
- ? {
117
- type: typeSchemas.request?.name,
118
- optional: isOptional(typeSchemas.request?.schema),
119
- }
120
- : undefined,
121
- params: typeSchemas.queryParams?.name
122
- ? {
123
- type: typeSchemas.queryParams?.name,
124
- optional: isOptional(typeSchemas.queryParams?.schema),
125
- }
126
- : undefined,
127
- headers: typeSchemas.headerParams?.name
128
- ? {
129
- type: typeSchemas.headerParams?.name,
130
- optional: isOptional(typeSchemas.headerParams?.schema),
131
- }
132
- : undefined,
133
- })
134
- const dataParams = FunctionParams.factory({
135
- data: {
136
- // No use of pathParams because useMutation can only take one argument in object form,
137
- // see https://tanstack.com/query/latest/docs/framework/react/reference/useMutation#usemutation
138
- mode: 'object',
139
- children: Object.entries(mutationParams.params).reduce((acc, [key, value]) => {
140
- if (value) {
141
- acc[key] = {
142
- ...value,
143
- type: undefined,
144
- }
145
- }
146
-
147
- return acc
148
- }, {} as Params),
149
- },
150
- })
110
+ const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
151
111
 
152
- const TRequest = mutationParams.toConstructor()
153
- const TData = dataReturnType === 'data' ? typeSchemas.response.name : `ResponseConfig<${typeSchemas.response.name}>`
154
- const TError = `ResponseErrorConfig<${typeSchemas.errors?.map((item) => item.name).join(' | ') || 'Error'}>`
112
+ const mutationKeyParamsNode = MutationKey.getParams()
113
+ const mutationKeyParamsCall = callPrinter.print(mutationKeyParamsNode) ?? ''
155
114
 
156
- const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
115
+ const clientCallParamsNode = ast.createOperationParams(node, {
116
+ paramsType,
117
+ pathParamsType: paramsType === 'object' ? 'object' : pathParamsType === 'object' ? 'object' : 'inline',
118
+ paramsCasing,
119
+ resolver: tsResolver,
120
+ extraParams: [
121
+ ast.createFunctionParameter({
122
+ name: 'config',
123
+ type: ast.createParamsType({
124
+ variant: 'reference',
125
+ name: node.requestBody?.content?.[0]?.schema
126
+ ? `Partial<RequestConfig<${tsResolver.resolveDataName(node)}>> & { client?: Client }`
127
+ : 'Partial<RequestConfig> & { client?: Client }',
128
+ }),
129
+ default: '{}',
130
+ }),
131
+ ],
132
+ })
133
+ const clientCallStr = callPrinter.print(clientCallParamsNode) ?? ''
134
+
135
+ const paramsNode = getParams(node, { paramsCasing, dataReturnType, resolver: tsResolver })
136
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
157
137
 
158
138
  return (
159
139
  <File.Source name={name} isExportable isIndexable>
160
- <Function
161
- name={name}
162
- export
163
- params={params.toConstructor()}
164
- JSDoc={{
165
- comments: getComments(operation),
166
- }}
167
- generics={['TContext']}
168
- >
140
+ <Function name={name} export params={paramsSignature} JSDoc={{ comments: getComments(node) }} generics={['TContext']}>
169
141
  {`
170
142
  const { mutation = {}, client: config = {} } = options ?? {}
171
143
  const { client: queryClient, ...mutationOptions } = mutation;
172
- const mutationKey = mutationOptions?.mutationKey ?? ${mutationKeyName}(${mutationKeyParams.toCall()})
144
+ const mutationKey = mutationOptions?.mutationKey ?? ${mutationKeyName}(${mutationKeyParamsCall})
173
145
 
174
146
  return useMutation<${generics}>({
175
- mutationFn: async(${dataParams.toConstructor()}) => {
176
- return ${clientName}(${clientParams.toCall()})
147
+ mutationFn: async(${hasMutationParams ? `{ ${argKeysStr} }` : ''}) => {
148
+ return ${clientName}(${clientCallStr})
177
149
  },
178
150
  mutationKey,
179
151
  ...mutationOptions
@@ -183,3 +155,5 @@ export function Mutation({
183
155
  </File.Source>
184
156
  )
185
157
  }
158
+
159
+ Mutation.getParams = getParams