@kubb/plugin-react-query 5.0.0-beta.10 → 5.0.0-beta.100

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 (49) hide show
  1. package/README.md +22 -26
  2. package/dist/index.cjs +2288 -191
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +494 -6
  5. package/dist/index.js +2258 -185
  6. package/dist/index.js.map +1 -1
  7. package/package.json +11 -34
  8. package/dist/components-Dow6tde8.js +0 -1459
  9. package/dist/components-Dow6tde8.js.map +0 -1
  10. package/dist/components-HwdCDefj.cjs +0 -1603
  11. package/dist/components-HwdCDefj.cjs.map +0 -1
  12. package/dist/components.cjs +0 -13
  13. package/dist/components.d.ts +0 -268
  14. package/dist/components.js +0 -2
  15. package/dist/generators-CcOmnTPa.cjs +0 -1454
  16. package/dist/generators-CcOmnTPa.cjs.map +0 -1
  17. package/dist/generators-yfZr_qfT.js +0 -1412
  18. package/dist/generators-yfZr_qfT.js.map +0 -1
  19. package/dist/generators.cjs +0 -9
  20. package/dist/generators.d.ts +0 -27
  21. package/dist/generators.js +0 -2
  22. package/dist/types-DG_OxOym.d.ts +0 -363
  23. package/extension.yaml +0 -911
  24. package/src/components/InfiniteQuery.tsx +0 -161
  25. package/src/components/InfiniteQueryOptions.tsx +0 -178
  26. package/src/components/Mutation.tsx +0 -121
  27. package/src/components/MutationKey.tsx +0 -1
  28. package/src/components/MutationOptions.tsx +0 -103
  29. package/src/components/Query.tsx +0 -118
  30. package/src/components/QueryKey.tsx +0 -1
  31. package/src/components/QueryOptions.tsx +0 -99
  32. package/src/components/SuspenseInfiniteQuery.tsx +0 -161
  33. package/src/components/SuspenseInfiniteQueryOptions.tsx +0 -178
  34. package/src/components/SuspenseQuery.tsx +0 -118
  35. package/src/components/index.ts +0 -11
  36. package/src/generators/customHookOptionsFileGenerator.tsx +0 -84
  37. package/src/generators/hookOptionsGenerator.tsx +0 -133
  38. package/src/generators/index.ts +0 -7
  39. package/src/generators/infiniteQueryGenerator.tsx +0 -194
  40. package/src/generators/mutationGenerator.tsx +0 -168
  41. package/src/generators/queryGenerator.tsx +0 -180
  42. package/src/generators/suspenseInfiniteQueryGenerator.tsx +0 -203
  43. package/src/generators/suspenseQueryGenerator.tsx +0 -181
  44. package/src/index.ts +0 -2
  45. package/src/plugin.ts +0 -186
  46. package/src/resolvers/resolverReactQuery.ts +0 -107
  47. package/src/types.ts +0 -328
  48. package/src/utils.ts +0 -10
  49. /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.js CHANGED
@@ -1,128 +1,2261 @@
1
- import "./chunk--u3MIqq1.js";
2
- import { _ as mutationKeyTransformer, d as queryKeyTransformer, v as camelCase } from "./components-Dow6tde8.js";
3
- import { a as infiniteQueryGenerator, i as mutationGenerator, n as suspenseInfiniteQueryGenerator, o as hookOptionsGenerator, r as queryGenerator, s as customHookOptionsFileGenerator, t as suspenseQueryGenerator } from "./generators-yfZr_qfT.js";
1
+ import "./rolldown-runtime-C0LytTxp.js";
2
+ import { Resolver, Url, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
4
3
  import path from "node:path";
5
- import { ast, definePlugin, defineResolver } from "@kubb/core";
6
- import { pluginClientName } from "@kubb/plugin-client";
7
- import { source } from "@kubb/plugin-client/templates/clients/axios.source";
8
- import { source as source$1 } from "@kubb/plugin-client/templates/clients/fetch.source";
9
- import { source as source$2 } from "@kubb/plugin-client/templates/config.source";
10
- import { pluginTsName } from "@kubb/plugin-ts";
11
- import { pluginZodName } from "@kubb/plugin-zod";
12
- //#region src/resolvers/resolverReactQuery.ts
13
- function capitalize(name) {
14
- return `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
4
+ import { createFunctionParameter, createFunctionParameters, createObjectBindingPattern, createTypeLiteral, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
5
+ import { File, Function, Type, jsxRenderer } from "kubb/jsx";
6
+ import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
7
+ import fs from "node:fs";
8
+ //#region ../../internals/shared/src/params.ts
9
+ /**
10
+ * Drops parameters that share the same name, keeping the first.
11
+ *
12
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
13
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
14
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
15
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
16
+ */
17
+ function dedupeParams(params) {
18
+ const seen = /* @__PURE__ */ new Set();
19
+ return params.filter((param) => {
20
+ if (seen.has(param.name)) return false;
21
+ seen.add(param.name);
22
+ return true;
23
+ });
15
24
  }
25
+ //#endregion
26
+ //#region ../../internals/shared/src/operation.ts
16
27
  /**
17
- * Naming convention resolver for React Query plugin.
28
+ * Builds the `ResolverFileParams` every operation generator passes to
29
+ * `resolver.file`: a file named `name`, tagged by the operation's first
30
+ * tag (or `'default'`), at the operation's path. Centralizes the entry object
31
+ * that was repeated at dozens of call sites across the client and query plugins.
18
32
  *
19
- * Provides default naming helpers using camelCase for functions and file paths.
33
+ * @example
34
+ * ```ts
35
+ * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
36
+ * ```
37
+ */
38
+ function operationFileEntry(node, name, extname = ".ts") {
39
+ return {
40
+ name,
41
+ extname,
42
+ tag: node.tags[0] ?? "default",
43
+ path: node.path
44
+ };
45
+ }
46
+ function getOperationLink(node, link) {
47
+ if (!link) return null;
48
+ if (typeof link === "function") return link(node) ?? null;
49
+ return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
50
+ }
51
+ /**
52
+ * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
53
+ * are present and the union, default, and form-data flags the client uses to pick one.
54
+ */
55
+ function buildContentTypeInfo(contentTypes) {
56
+ const isMultipleContentTypes = contentTypes.length > 1;
57
+ return {
58
+ contentTypes,
59
+ isMultipleContentTypes,
60
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
61
+ defaultContentType: contentTypes[0] ?? "application/json",
62
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
63
+ };
64
+ }
65
+ function getContentTypeInfo(node) {
66
+ return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? []);
67
+ }
68
+ /**
69
+ * The request-body counterpart for the primary success response: the content types it documents and
70
+ * whether several are present, so the client can let a caller pick which one to accept.
71
+ */
72
+ function getResponseContentTypeInfo(node) {
73
+ return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
74
+ }
75
+ function buildRequestConfigType(node) {
76
+ const request = getContentTypeInfo(node);
77
+ const response = getResponseContentTypeInfo(node);
78
+ const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
79
+ const members = [request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null, response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null].filter(Boolean);
80
+ return members.length ? `${configType} & { contentType?: { ${members.join("; ")} } }` : configType;
81
+ }
82
+ /**
83
+ * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,
84
+ * `useInfiniteQuery`, `useSWR`, ...). Unlike {@link buildRequestConfigType}, it never adds a
85
+ * `contentType?:` member: query hooks wrap GET operations, which carry no request body to select a
86
+ * content type for.
87
+ */
88
+ function buildClientOptionType() {
89
+ return `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
90
+ }
91
+ /**
92
+ * Which of the grouped request options an operation carries.
93
+ */
94
+ function getRequestGroups(node) {
95
+ const { path, query, header } = getOperationParameters(node);
96
+ return {
97
+ path: path.length > 0,
98
+ query: query.length > 0,
99
+ body: Boolean(node.requestBody?.content?.[0]?.schema),
100
+ headers: header.length > 0
101
+ };
102
+ }
103
+ /**
104
+ * Resolves which grouped request options an operation carries together with whether each group
105
+ * holds a required member. The grouped parameter stays optional only when nothing inside it is
106
+ * required, matching the generated `RequestConfig` type.
107
+ */
108
+ function getRequestGroupOptionality(node) {
109
+ const groups = getRequestGroups(node);
110
+ const { path, query, header } = getOperationParameters(node);
111
+ const hasRequiredPath = path.some((param) => param.required);
112
+ const hasRequiredQuery = query.some((param) => param.required);
113
+ const hasRequiredHeader = header.some((param) => param.required);
114
+ return {
115
+ groups,
116
+ hasRequiredPath,
117
+ hasRequiredQuery,
118
+ hasRequiredHeader,
119
+ isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body
120
+ };
121
+ }
122
+ function buildOperationComments(node, options = {}) {
123
+ const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
124
+ const linkComment = getOperationLink(node, link);
125
+ const filteredComments = (linkPosition === "beforeDeprecated" ? [
126
+ node.description && `@description ${node.description}`,
127
+ node.summary && `@summary ${node.summary}`,
128
+ linkComment,
129
+ node.deprecated && "@deprecated"
130
+ ] : [
131
+ node.description && `@description ${node.description}`,
132
+ node.summary && `@summary ${node.summary}`,
133
+ node.deprecated && "@deprecated",
134
+ linkComment
135
+ ]).filter((comment) => Boolean(comment));
136
+ if (!splitLines) return filteredComments;
137
+ return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
138
+ }
139
+ function getOperationParameters(node) {
140
+ return {
141
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
142
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
143
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
144
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
145
+ };
146
+ }
147
+ function getStatusCodeNumber(statusCode) {
148
+ const code = Number(statusCode);
149
+ return Number.isNaN(code) ? null : code;
150
+ }
151
+ function isSuccessStatusCode(statusCode) {
152
+ const code = getStatusCodeNumber(statusCode);
153
+ return code !== null && code >= 200 && code < 300;
154
+ }
155
+ function isErrorStatusCode(statusCode) {
156
+ const code = getStatusCodeNumber(statusCode);
157
+ return code !== null && code >= 400;
158
+ }
159
+ function getSuccessResponses(responses) {
160
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
161
+ }
162
+ function getOperationSuccessResponses(node) {
163
+ return getSuccessResponses(node.responses);
164
+ }
165
+ function getPrimarySuccessResponse(node) {
166
+ return getOperationSuccessResponses(node)[0] ?? null;
167
+ }
168
+ function resolveErrorNames(node, resolver) {
169
+ return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode));
170
+ }
171
+ function resolveSuccessNames(node, resolver) {
172
+ return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode));
173
+ }
174
+ function resolveStatusCodeNames(node, resolver) {
175
+ return node.responses.map((response) => resolver.response.status(node, response.statusCode));
176
+ }
177
+ const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
178
+ function resolveOperationTypeNames(node, resolver, options = {}) {
179
+ const cacheKey = `${node.operationId}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
180
+ let byResolver = typeNamesByResolver.get(resolver);
181
+ if (byResolver) {
182
+ const cached = byResolver.get(cacheKey);
183
+ if (cached) return cached;
184
+ } else {
185
+ byResolver = /* @__PURE__ */ new Map();
186
+ typeNamesByResolver.set(resolver, byResolver);
187
+ }
188
+ const { path, query, header } = getOperationParameters(node);
189
+ const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
190
+ const exclude = new Set(options.exclude ?? []);
191
+ const paramNames = options.includeParams === false ? [] : [
192
+ ...path.map((param) => resolver.param.path(node, param)),
193
+ ...query.map((param) => resolver.param.query(node, param)),
194
+ ...header.map((param) => resolver.param.headers(node, param))
195
+ ];
196
+ const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.response.body(node) : null, resolver.response.response(node)];
197
+ const result = (options.order === "body-response-first" ? [
198
+ ...bodyAndResponseNames,
199
+ ...paramNames,
200
+ ...responseStatusNames
201
+ ] : [
202
+ ...paramNames,
203
+ ...bodyAndResponseNames,
204
+ ...responseStatusNames
205
+ ]).filter((name) => Boolean(name) && !exclude.has(name));
206
+ byResolver.set(cacheKey, result);
207
+ return result;
208
+ }
209
+ //#endregion
210
+ //#region ../../internals/utils/src/casing.ts
211
+ /**
212
+ * Shared implementation for camelCase and PascalCase conversion.
213
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
214
+ * and capitalizes each word according to `pascal`.
215
+ *
216
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
217
+ */
218
+ function toCamelOrPascal(text, pascal) {
219
+ return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
220
+ if (word.length > 1 && word === word.toUpperCase()) return word;
221
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
222
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
223
+ }
224
+ /**
225
+ * Converts `text` to camelCase.
226
+ *
227
+ * @example Word boundaries
228
+ * `camelCase('hello-world') // 'helloWorld'`
229
+ *
230
+ * @example With a prefix
231
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
232
+ */
233
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
234
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
235
+ }
236
+ /**
237
+ * Uppercases only the first character of `text`, leaving the rest untouched.
238
+ * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
20
239
  *
21
240
  * @example
22
- * `resolverReactQuery.default('list pets', 'function') // → 'listPets'`
241
+ * `capitalize('getPetById') // 'GetPetById'`
23
242
  */
24
- const resolverReactQuery = defineResolver(() => ({
25
- name: "default",
243
+ function capitalize(text) {
244
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
245
+ }
246
+ //#endregion
247
+ //#region ../../internals/utils/src/strings.ts
248
+ /**
249
+ * Renders a dotted path or string array as an optional-chaining accessor expression rooted at
250
+ * `accessor`. Returns `null` for an empty path.
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * getNestedAccessor('pagination.next.id', 'lastPage')
255
+ * // "lastPage?.['pagination']?.['next']?.['id']"
256
+ * ```
257
+ */
258
+ function getNestedAccessor(param, accessor) {
259
+ const parts = Array.isArray(param) ? param : param.split(".");
260
+ if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
261
+ return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
262
+ }
263
+ //#endregion
264
+ //#region ../../internals/shared/src/group.ts
265
+ /**
266
+ * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
267
+ * shared default naming so every plugin groups output consistently:
268
+ *
269
+ * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
270
+ * - other groups use the camelCased group (`pet store` → `petStore`).
271
+ *
272
+ * A user-provided `group.name` always wins over the default namer, so callers stay in
273
+ * control of their output folders. Returns `null` when grouping is disabled, matching the
274
+ * per-plugin convention.
275
+ *
276
+ * @param group - The user-supplied group option, or `undefined` to disable grouping.
277
+ *
278
+ * @example
279
+ * ```ts
280
+ * createGroupConfig(group) // shared across every plugin
281
+ * ```
282
+ */
283
+ function createGroupConfig(group) {
284
+ if (!group) return null;
285
+ const defaultName = (ctx) => {
286
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
287
+ return camelCase(ctx.group);
288
+ };
289
+ return {
290
+ ...group,
291
+ name: group.name ? group.name : defaultName
292
+ };
293
+ }
294
+ //#endregion
295
+ //#region ../../internals/client/src/resolveClient.ts
296
+ /**
297
+ * Resolves which client plugin a consumer (react-query, vue-query, swr, mcp) should call for an
298
+ * operation, shared so the selection rules and diagnostics stay in one place.
299
+ *
300
+ * Every client runtime lives in a dedicated client plugin, so a consumer always calls a registered
301
+ * contract client plugin (plugin-fetch or plugin-axios) and never emits its own inline client:
302
+ *
303
+ * - `contract` — a registered contract client plugin owns the `<op>` functions and the consumer
304
+ * imports and calls them.
305
+ * - `error` — no client plugin is registered, several are registered without a selector, or the
306
+ * requested one is missing.
307
+ *
308
+ * The `client` string selects explicitly (`'fetch'` / `'axios'`); when it is unset a lone registered
309
+ * contract client plugin is picked up automatically.
310
+ */
311
+ const pluginFetchName = "plugin-fetch";
312
+ const pluginAxiosName = "plugin-axios";
313
+ const selectorToPlugin = {
314
+ fetch: pluginFetchName,
315
+ axios: pluginAxiosName
316
+ };
317
+ const contractPlugins = [pluginFetchName, pluginAxiosName];
318
+ /**
319
+ * Applies the `client` resolution rules. See the module comment for the strategy shape.
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * resolveClient({ client: 'fetch', pluginNames: ['plugin-ts', 'plugin-fetch'] })
324
+ * // { kind: 'contract', pluginName: 'plugin-fetch' }
325
+ * ```
326
+ *
327
+ * @example
328
+ * ```ts
329
+ * resolveClient({ client: undefined, pluginNames: ['plugin-ts'] })
330
+ * // { kind: 'error', message: 'No client plugin is registered…' }
331
+ * ```
332
+ */
333
+ function resolveClient(options) {
334
+ const { client, pluginNames } = options;
335
+ const has = (name) => pluginNames.includes(name);
336
+ if (client === "fetch" || client === "axios") {
337
+ const pluginName = selectorToPlugin[client];
338
+ if (!has(pluginName)) return {
339
+ kind: "error",
340
+ message: `\`client: '${client}'\` is set but \`@kubb/plugin-${client}\` is not registered in \`plugins\`. Add it, or drop \`client\` to use a different client plugin.`
341
+ };
342
+ return {
343
+ kind: "contract",
344
+ pluginName
345
+ };
346
+ }
347
+ const registered = contractPlugins.filter(has);
348
+ if (registered.length === 1) return {
349
+ kind: "contract",
350
+ pluginName: registered[0]
351
+ };
352
+ if (registered.length > 1) return {
353
+ kind: "error",
354
+ message: `Multiple client plugins are registered (${registered.map((name) => `\`@kubb/${name}\``).join(", ")}). Set \`client: 'fetch' | 'axios'\` to choose which client the hooks call, or register a single client plugin.`
355
+ };
356
+ return {
357
+ kind: "error",
358
+ message: "No client plugin is registered. Add `@kubb/plugin-axios` or `@kubb/plugin-fetch` to `plugins` so the generated code has an HTTP client to call."
359
+ };
360
+ }
361
+ /**
362
+ * Resolves the contract client during a consumer plugin's setup hook. Extracts the plugin names
363
+ * from the raw `plugins` config, applies {@link resolveClient}, and throws the diagnostic on a
364
+ * misconfiguration so every consumer fails fast with the same message.
365
+ */
366
+ function resolveContractClient(options) {
367
+ const { client, plugins = [] } = options;
368
+ const resolved = resolveClient({
369
+ client,
370
+ pluginNames: plugins.map((plugin) => plugin.name).filter((name) => Boolean(name))
371
+ });
372
+ if (resolved.kind === "error") throw new Error(resolved.message);
373
+ return resolved;
374
+ }
375
+ //#endregion
376
+ //#region ../../internals/client/src/resolveClientOperation.ts
377
+ /**
378
+ * Resolves the contract client `<op>` a consumer (query hook, MCP handler) imports, by looking up
379
+ * the registered contract client plugin's resolver and output. Works for any contract client plugin
380
+ * (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
381
+ * path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
382
+ * read `RequestConfig` / `ResponseErrorConfig` from.
383
+ */
384
+ function resolveClientOperation(options) {
385
+ const { clientPlugin, driver, node, root, output } = options;
386
+ if (!clientPlugin) return null;
387
+ const resolver = driver.getResolver(clientPlugin.pluginName);
388
+ const plugin = driver.getPlugin(clientPlugin.pluginName);
389
+ const file = resolver.file({
390
+ ...operationFileEntry(node, node.operationId),
391
+ root,
392
+ output: plugin?.options?.output ?? output,
393
+ group: plugin?.options?.group ?? void 0
394
+ });
395
+ return {
396
+ name: resolver.name(node.operationId),
397
+ path: file.path,
398
+ clientPath: path.resolve(root, ".kubb/client.ts")
399
+ };
400
+ }
401
+ //#endregion
402
+ //#region ../../internals/tanstack-query/src/utils.ts
403
+ /**
404
+ * The grouped request options, ordered for both the destructured signature and the
405
+ * call passed to the underlying client.
406
+ */
407
+ const requestGroupOrder$1 = [
408
+ "path",
409
+ "query",
410
+ "body",
411
+ "headers"
412
+ ];
413
+ function maybeValueOrGetter(type) {
414
+ return `${type} | (() => ${type})`;
415
+ }
416
+ /**
417
+ * Builds the grouped `{ path, query, body, headers }` parameter that mirrors the client
418
+ * function signature. Only the groups the operation carries are emitted, typed from the
419
+ * operation's `Options`. `keys` narrows the emitted groups, used by the query key which
420
+ * never carries `headers`.
421
+ *
422
+ * By default the whole group is typed as the single `Options` reference. When
423
+ * `memberTypeWrapper` is set, each group is emitted as its own member typed from the matching
424
+ * `Options['<group>']` slice and wrapped, used by vue-query to apply
425
+ * `MaybeRefOrGetter` per group.
426
+ */
427
+ function buildGroupedRequestParam(node, options) {
428
+ const { resolver, keys = requestGroupOrder$1, memberTypeWrapper } = options;
429
+ const { groups, hasRequiredPath, hasRequiredQuery, hasRequiredHeader } = getRequestGroupOptionality(node);
430
+ const names = keys.filter((key) => groups[key]);
431
+ if (names.length === 0) return null;
432
+ const requiredByGroup = {
433
+ path: hasRequiredPath,
434
+ query: hasRequiredQuery,
435
+ body: groups.body,
436
+ headers: hasRequiredHeader
437
+ };
438
+ const isOptional = names.every((name) => !requiredByGroup[name]);
439
+ const optionsName = resolver.response.options(node);
440
+ const omittedKeys = requestGroupOrder$1.filter((key) => !keys.includes(key));
441
+ const optionsType = omittedKeys.length > 0 ? `Omit<${optionsName}, ${omittedKeys.map((key) => `'${key}'`).join(" | ")}>` : optionsName;
442
+ if (memberTypeWrapper) {
443
+ const members = names.map((name) => ({
444
+ name,
445
+ type: memberTypeWrapper(`${optionsType}['${name}']`),
446
+ optional: !requiredByGroup[name]
447
+ }));
448
+ return createFunctionParameter({
449
+ name: createObjectBindingPattern({ elements: names.map((name) => ({ name })) }),
450
+ type: createTypeLiteral({ members }),
451
+ optional: false,
452
+ ...isOptional ? { default: "{}" } : {}
453
+ });
454
+ }
455
+ return createFunctionParameter({
456
+ name: createObjectBindingPattern({ elements: names.map((name) => ({ name })) }),
457
+ type: optionsType,
458
+ optional: false,
459
+ ...isOptional ? { default: "{}" } : {}
460
+ });
461
+ }
462
+ /**
463
+ * Builds the shared `({ path, query, body, headers }, config = {})` parameter list for a
464
+ * TanStack query-options function. The leading parameter mirrors the client signature, and the
465
+ * trailing `config` is a partial `RequestConfig` minus the grouped data-shape keys, which are passed
466
+ * explicitly. Framework plugins wrap the result when needed, for example vue-query applies `MaybeRefOrGetter`.
467
+ */
468
+ function buildQueryOptionsParams(node, options) {
469
+ const { resolver, memberTypeWrapper } = options;
470
+ return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
471
+ resolver,
472
+ memberTypeWrapper
473
+ }), createFunctionParameter({
474
+ name: "config",
475
+ type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
476
+ default: "{}"
477
+ })].filter((param) => param !== null) });
478
+ }
479
+ /**
480
+ * Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
481
+ * a single grouped options object, so the operation's request groups are passed as
482
+ * shorthand alongside the spread `config`, with `throwOnError: true` pinned last so a caller's config
483
+ * can't flip the query's error semantics. Queries thread the TanStack `signal`; mutations omit it.
484
+ *
485
+ * When `unwrapName` is set, each request group is passed as an explicit member unwrapped through it,
486
+ * used by vue-query to resolve refs and getters with `toValue(...)` at call time.
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * buildClientCall(node, { clientName: 'getPetById', signal: true })
491
+ * // getPetById({ path, ...config, signal: config.signal ?? signal, throwOnError: true })
492
+ * ```
493
+ */
494
+ function buildClientCall(node, options) {
495
+ const { clientName, signal = false, unwrapName } = options;
496
+ const { groups } = getRequestGroupOptionality(node);
497
+ return `${clientName}({ ${[
498
+ "...config",
499
+ ...requestGroupOrder$1.filter((key) => groups[key]).map((name) => unwrapName ? `${name}: ${unwrapName(name)}` : name),
500
+ signal ? "signal: config.signal ?? signal" : null,
501
+ "throwOnError: true"
502
+ ].filter((part) => part !== null).join(", ")} })`;
503
+ }
504
+ /**
505
+ * Builds the `TData` / `TError` type expressions shared by every generated hook, joining the resolved
506
+ * success responses into `TData` and wrapping the error responses in `ResponseErrorConfig<...>`.
507
+ */
508
+ function buildResponseTypes(node, resolver) {
509
+ const successNames = resolveSuccessNames(node, resolver);
510
+ const responseName = successNames.length > 0 ? successNames.join(" | ") : resolver.response.response(node);
511
+ const errorNames = resolveErrorNames(node, resolver);
512
+ return {
513
+ TData: responseName,
514
+ TError: `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`
515
+ };
516
+ }
517
+ function resolveFallbackPageParamType(initialPageParam) {
518
+ if (typeof initialPageParam === "number") return "number";
519
+ if (typeof initialPageParam === "boolean") return "boolean";
520
+ if (typeof initialPageParam !== "string") return "unknown";
521
+ if (!initialPageParam.includes(" as ")) return "string";
522
+ return initialPageParam.split(" as ").at(-1) ?? "unknown";
523
+ }
524
+ /**
525
+ * Resolves the `TPageParam` generic for the infinite-query hooks. Prefers the type read from the
526
+ * configured `queryParam` on the operation's query object, and falls back to the type inferred from
527
+ * `initialPageParam` when that parameter type is unavailable. Also returns the query params type name
528
+ * so callers can reuse it when rewriting the paginated request.
529
+ */
530
+ function resolvePageParamType(node, { resolver, initialPageParam, queryParam }) {
531
+ const firstQueryParam = getOperationParameters(node).query[0];
532
+ const groupName = firstQueryParam ? resolver.param.query(node, firstQueryParam) : null;
533
+ const queryParamsTypeName = groupName !== (firstQueryParam ? resolver.param.name(node, firstQueryParam) : null) ? groupName : null;
534
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null;
535
+ if (!queryParamType) return {
536
+ queryParamsTypeName,
537
+ pageParamType: resolveFallbackPageParamType(initialPageParam)
538
+ };
539
+ return {
540
+ queryParamsTypeName,
541
+ pageParamType: initialPageParam !== void 0 && initialPageParam !== null ? `NonNullable<${queryParamType}>` : queryParamType
542
+ };
543
+ }
544
+ /**
545
+ * Applies the shared query defaults during plugin setup: `false` disables query generation, and an
546
+ * object merges over `methods: ['GET']` and the plugin's runtime `importPath`.
547
+ */
548
+ function resolveQueryConfig(query, options) {
549
+ if (query === false) return false;
550
+ return {
551
+ importPath: options.importPath,
552
+ methods: ["GET"],
553
+ ...query
554
+ };
555
+ }
556
+ /**
557
+ * Applies the shared mutation defaults during plugin setup: `false` disables mutation generation,
558
+ * and an object merges over `methods: ['POST', 'PUT', 'PATCH', 'DELETE']` and the plugin's runtime
559
+ * `importPath`.
560
+ */
561
+ function resolveMutationConfig(mutation, options) {
562
+ if (mutation === false) return false;
563
+ return {
564
+ importPath: options.importPath,
565
+ methods: [
566
+ "POST",
567
+ "PUT",
568
+ "PATCH",
569
+ "DELETE"
570
+ ],
571
+ ...mutation
572
+ };
573
+ }
574
+ /**
575
+ * Applies the shared infinite-query defaults during plugin setup: a falsy value disables infinite
576
+ * queries, and an object merges over `queryParam: 'id'` and `initialPageParam: 0` with the cursor
577
+ * paths cleared.
578
+ */
579
+ function resolveInfiniteConfig(infinite) {
580
+ if (!infinite) return false;
581
+ return {
582
+ queryParam: "id",
583
+ initialPageParam: 0,
584
+ cursorParam: null,
585
+ nextParam: null,
586
+ previousParam: null,
587
+ ...infinite
588
+ };
589
+ }
590
+ function matchesPattern(node, ov) {
591
+ const { type, pattern } = ov;
592
+ const matches = (value) => typeof pattern === "string" ? value === pattern : pattern.test(value);
593
+ if (type === "operationId") return matches(node.operationId);
594
+ if (type === "tag") return node.tags.some((t) => matches(t));
595
+ if (type === "path") return node.path !== void 0 && matches(node.path);
596
+ if (type === "method") return node.method !== void 0 && matches(node.method);
597
+ return false;
598
+ }
599
+ /**
600
+ * Resolves per-operation overrides (first matching override wins).
601
+ *
602
+ * @example
603
+ * ```ts
604
+ * const opts = resolveOperationOverrides(node, override)
605
+ * const queryOpts = 'query' in opts ? opts.query : defaultQuery
606
+ * ```
607
+ */
608
+ function resolveOperationOverrides(node, override) {
609
+ if (!override) return {};
610
+ return override.find((ov) => matchesPattern(node, ov))?.options ?? {};
611
+ }
612
+ /**
613
+ * Build QueryKey params as the grouped `{ path, query, body }` object (NO headers, NO config),
614
+ * typed from the operation's `Options` minus `url`. The query key transformer reads the
615
+ * grouped `path`/`query`/`body` bindings.
616
+ */
617
+ function buildQueryKeyParams(node, options) {
618
+ const { resolver } = options;
619
+ const groupedParam = buildGroupedRequestParam(node, {
620
+ resolver,
621
+ keys: [
622
+ "path",
623
+ "query",
624
+ "body"
625
+ ]
626
+ });
627
+ return createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
628
+ }
629
+ //#endregion
630
+ //#region ../../internals/tanstack-query/src/components/InfiniteQueryOptions.tsx
631
+ const declarationPrinter$7 = functionPrinter({ mode: "declaration" });
632
+ const callPrinter$3 = functionPrinter({ mode: "call" });
633
+ function InfiniteQueryOptions({ name, clientName, initialPageParam, cursorParam, nextParam, previousParam, node, tsResolver, queryParam, queryKeyName, queryKeyType = "typeof queryKey", memberTypeWrapper, unwrapName }) {
634
+ const { TData: queryFnDataType, TError: errorType } = buildResponseTypes(node, tsResolver);
635
+ const { queryParamsTypeName, pageParamType } = resolvePageParamType(node, {
636
+ resolver: tsResolver,
637
+ initialPageParam,
638
+ queryParam
639
+ });
640
+ const groupedKeyParam = buildGroupedRequestParam(node, {
641
+ resolver: tsResolver,
642
+ keys: [
643
+ "path",
644
+ "query",
645
+ "body"
646
+ ],
647
+ memberTypeWrapper
648
+ });
649
+ const queryKeyParamsNode = createFunctionParameters({ params: groupedKeyParam ? [groupedKeyParam] : [] });
650
+ const queryKeyParamsCall = callPrinter$3.print(queryKeyParamsNode) ?? "";
651
+ const paramsNode = buildQueryOptionsParams(node, {
652
+ resolver: tsResolver,
653
+ memberTypeWrapper
654
+ });
655
+ const paramsSignature = declarationPrinter$7.print(paramsNode) ?? "";
656
+ const queryFnBody = `const { data } = await ${buildClientCall(node, {
657
+ clientName,
658
+ signal: true,
659
+ unwrapName
660
+ })}
661
+ return data`;
662
+ const hasNewParams = nextParam != null || previousParam != null;
663
+ const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
664
+ if (hasNewParams) {
665
+ const nextAccessor = nextParam ? getNestedAccessor(nextParam, "lastPage") : null;
666
+ const prevAccessor = previousParam ? getNestedAccessor(previousParam, "firstPage") : null;
667
+ return [nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null, prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null];
668
+ }
669
+ if (cursorParam) return [`getNextPageParam: (lastPage) => lastPage['${cursorParam}']`, `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`];
670
+ return ["getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1", "getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1"];
671
+ })();
672
+ const queryOptionsArr = [
673
+ `initialPageParam: ${typeof initialPageParam === "string" ? JSON.stringify(initialPageParam) : initialPageParam}`,
674
+ getNextPageParamExpr,
675
+ getPreviousPageParamExpr
676
+ ].filter(Boolean);
677
+ const infiniteOverrideParams = queryParam && queryParamsTypeName ? `query = {
678
+ ...(query ?? {}),
679
+ ['${queryParam}']: pageParam as unknown as ${queryParamsTypeName}['${queryParam}'],
680
+ } as ${queryParamsTypeName}` : "";
681
+ const queryFnArgs = infiniteOverrideParams ? "{ signal, pageParam }" : "{ signal }";
682
+ const queryFnStatements = infiniteOverrideParams ? `${infiniteOverrideParams}\n ${queryFnBody}` : queryFnBody;
683
+ return /* @__PURE__ */ jsx(File.Source, {
684
+ name,
685
+ isExportable: true,
686
+ isIndexable: true,
687
+ children: /* @__PURE__ */ jsx(Function, {
688
+ name,
689
+ export: true,
690
+ params: paramsSignature,
691
+ children: `
692
+ const queryKey = ${queryKeyName}(${queryKeyParamsCall})
693
+ return infiniteQueryOptions<${queryFnDataType}, ${errorType}, InfiniteData<${queryFnDataType}>, ${queryKeyType}, ${pageParamType}>({
694
+ queryKey,
695
+ queryFn: async (${queryFnArgs}) => {
696
+ ${queryFnStatements}
697
+ },
698
+ ${queryOptionsArr.join(",\n ")}
699
+ })
700
+ `
701
+ })
702
+ });
703
+ }
704
+ //#endregion
705
+ //#region ../../internals/tanstack-query/src/components/MutationKey.tsx
706
+ const declarationPrinter$6 = functionPrinter({ mode: "declaration" });
707
+ const mutationKeyTransformer = ({ node }) => {
708
+ if (!node.path) return [];
709
+ return [`{ url: '${Url.toPath(node.path)}' }`];
710
+ };
711
+ function MutationKey({ name, node, transformer }) {
712
+ const paramsNode = createFunctionParameters({ params: [] });
713
+ const paramsSignature = declarationPrinter$6.print(paramsNode) ?? "";
714
+ const keys = (transformer ?? mutationKeyTransformer)({
715
+ node,
716
+ casing: "camelcase"
717
+ });
718
+ return /* @__PURE__ */ jsx(File.Source, {
719
+ name,
720
+ isExportable: true,
721
+ isIndexable: true,
722
+ children: /* @__PURE__ */ jsx(Function.Arrow, {
723
+ name,
724
+ export: true,
725
+ params: paramsSignature,
726
+ singleLine: true,
727
+ children: `[${keys.join(", ")}] as const`
728
+ })
729
+ });
730
+ }
731
+ //#endregion
732
+ //#region ../../internals/tanstack-query/src/resolver.ts
733
+ /**
734
+ * Builds the shared query namespace for a variant. Spread the result into
735
+ * `createResolver` once per variant the plugin supports.
736
+ *
737
+ * @example
738
+ * ```ts
739
+ * createResolver<PluginReactQuery>({
740
+ * query: createQueryResolver(),
741
+ * suspenseQuery: createQueryResolver('Suspense'),
742
+ * })
743
+ * ```
744
+ */
745
+ function createQueryResolver(variant = "") {
746
+ return {
747
+ name(node) {
748
+ return `use${capitalize(this.name(node.operationId))}${variant}`;
749
+ },
750
+ optionsName(node) {
751
+ return `${this.name(node.operationId)}${variant}QueryOptions`;
752
+ },
753
+ keyName(node) {
754
+ return `${this.name(node.operationId)}${variant}QueryKey`;
755
+ },
756
+ keyTypeName(node) {
757
+ return `${capitalize(this.name(node.operationId))}${variant}QueryKey`;
758
+ },
759
+ clientName(node) {
760
+ return `${this.name(node.operationId)}${variant}`;
761
+ }
762
+ };
763
+ }
764
+ /**
765
+ * Builds the shared mutation namespace. Spread the result into
766
+ * `createResolver` and add plugin-specific methods (`optionsName`,
767
+ * `typeName`, `argTypeName`) next to it.
768
+ *
769
+ * @example
770
+ * ```ts
771
+ * createResolver<PluginSwr>({ mutation: { ...createMutationResolver(), argTypeName(node) {...} } })
772
+ * ```
773
+ */
774
+ function createMutationResolver() {
775
+ return {
776
+ name(node) {
777
+ return `use${capitalize(this.name(node.operationId))}`;
778
+ },
779
+ keyName(node) {
780
+ return `${this.name(node.operationId)}MutationKey`;
781
+ }
782
+ };
783
+ }
784
+ //#endregion
785
+ //#region ../../internals/tanstack-query/src/components/QueryKey.tsx
786
+ const declarationPrinter$5 = functionPrinter({ mode: "declaration" });
787
+ const queryKeyTransformer = ({ node }) => {
788
+ if (!node.path) return [];
789
+ const hasPathParams = getOperationParameters(node).path.length > 0;
790
+ const hasQueryParams = getOperationParameters(node).query.length > 0;
791
+ const hasRequestBody = !!node.requestBody?.content?.[0]?.schema;
792
+ return [
793
+ hasPathParams ? `{ url: '${Url.toPath(node.path)}', params: path }` : `{ url: '${Url.toPath(node.path)}' }`,
794
+ hasQueryParams ? "...(query ? [query] : [])" : null,
795
+ hasRequestBody ? "...(body ? [body] : [])" : null
796
+ ].filter(Boolean);
797
+ };
798
+ function QueryKey({ name, node, tsResolver, typeName, transformer }) {
799
+ const paramsNode = buildQueryKeyParams(node, { resolver: tsResolver });
800
+ const paramsSignature = declarationPrinter$5.print(paramsNode) ?? "";
801
+ const keys = (transformer ?? queryKeyTransformer)({
802
+ node,
803
+ casing: "camelcase"
804
+ });
805
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Source, {
806
+ name,
807
+ isExportable: true,
808
+ isIndexable: true,
809
+ children: /* @__PURE__ */ jsx(Function.Arrow, {
810
+ name,
811
+ export: true,
812
+ params: paramsSignature,
813
+ singleLine: true,
814
+ children: `[${keys.join(", ")}] as const`
815
+ })
816
+ }), /* @__PURE__ */ jsx(File.Source, {
817
+ name: typeName,
818
+ isTypeOnly: true,
819
+ children: /* @__PURE__ */ jsx(Type, {
820
+ name: typeName,
821
+ children: `ReturnType<typeof ${name}>`
822
+ })
823
+ })] });
824
+ }
825
+ //#endregion
826
+ //#region src/generators/customHookOptionsFileGenerator.tsx
827
+ /**
828
+ * Scaffolds the user-editable `useCustomHookOptions` file when
829
+ * `pluginReactQuery({ customOptions: { ... } })` is configured. The file is
830
+ * only created when it does not already exist, so user edits persist across
831
+ * regeneration.
832
+ */
833
+ const customHookOptionsFileGenerator = defineGenerator({
834
+ name: "react-query-custom-hook-options-file",
835
+ renderer: jsxRenderer,
836
+ operations(nodes, ctx) {
837
+ const { resolver, root } = ctx;
838
+ const { output, customOptions, query, group } = ctx.options;
839
+ if (!customOptions) return null;
840
+ const { importPath, name } = customOptions;
841
+ const hookOptionsName = resolver.hook.optionsName();
842
+ const customHookOptionsName = resolver.hook.customOptionsName();
843
+ const reactQueryImportPath = query ? query.importPath : "@tanstack/react-query";
844
+ let hookFilePath;
845
+ const firstNode = nodes[0];
846
+ if (firstNode) {
847
+ const hookName = resolver.query.name(firstNode);
848
+ hookFilePath = resolver.file({
849
+ name: hookName,
850
+ extname: ".ts",
851
+ tag: firstNode.tags[0] ?? "default",
852
+ path: firstNode.path,
853
+ root,
854
+ output,
855
+ group: group ?? void 0
856
+ }).path;
857
+ } else hookFilePath = path.resolve(root, "index.ts");
858
+ const ensureExtension = (filePath, extname) => {
859
+ if (path.extname(filePath) === "") return filePath + extname;
860
+ return filePath;
861
+ };
862
+ const basePath = path.dirname(hookFilePath);
863
+ const actualFilePath = ensureExtension(importPath, ".ts");
864
+ const file = {
865
+ baseName: path.basename(actualFilePath),
866
+ name: path.basename(actualFilePath, path.extname(actualFilePath)),
867
+ path: path.resolve(basePath, actualFilePath)
868
+ };
869
+ if (fs.existsSync(file.path)) return null;
870
+ return /* @__PURE__ */ jsxs(File, {
871
+ baseName: file.baseName,
872
+ path: file.path,
873
+ children: [
874
+ /* @__PURE__ */ jsx(File.Import, {
875
+ name: ["QueryClient"],
876
+ path: reactQueryImportPath,
877
+ isTypeOnly: true
878
+ }),
879
+ /* @__PURE__ */ jsx(File.Import, {
880
+ name: ["useQueryClient"],
881
+ path: reactQueryImportPath
882
+ }),
883
+ /* @__PURE__ */ jsx(File.Import, {
884
+ name: [hookOptionsName],
885
+ root: file.path,
886
+ path: path.resolve(root, "./index.ts")
887
+ }),
888
+ /* @__PURE__ */ jsxs(File.Source, {
889
+ name: file.name,
890
+ isExportable: true,
891
+ isIndexable: true,
892
+ children: [/* @__PURE__ */ jsx(Function, {
893
+ name: customHookOptionsName,
894
+ params: "{ queryClient }: { queryClient: QueryClient }",
895
+ returnType: `Partial<${hookOptionsName}>`,
896
+ children: `return {
897
+ // TODO: Define custom hook options here
898
+ // Example:
899
+ // useUpdatePetHook: {
900
+ // onSuccess: () => {
901
+ // void queryClient.invalidateQueries({ queryKey: ['pet'] })
902
+ // }
903
+ // }
904
+ }`
905
+ }), /* @__PURE__ */ jsx(Function, {
906
+ name,
907
+ generics: `T extends keyof ${hookOptionsName}`,
908
+ params: "{ hookName, operationId }: { hookName: T, operationId: string }",
909
+ returnType: `${hookOptionsName}[T]`,
910
+ export: true,
911
+ children: `const queryClient = useQueryClient()
912
+ const customOptions = ${customHookOptionsName}({ queryClient })
913
+ return customOptions[hookName] ?? {}`
914
+ })]
915
+ })
916
+ ]
917
+ });
918
+ }
919
+ });
920
+ //#endregion
921
+ //#region src/utils.ts
922
+ const requestGroupOrder = [
923
+ "path",
924
+ "query",
925
+ "body",
926
+ "headers"
927
+ ];
928
+ /**
929
+ * Builds the object literal that resolves each request group to a concrete value, unwrapping a getter
930
+ * once before the value reaches the plain `queryKey`/`queryOptions` helpers. A getter must never reach a
931
+ * React Query key, which is hashed structurally. Returns `null` when the operation carries no request group.
932
+ *
933
+ * @example
934
+ * ```ts
935
+ * buildResolvedRequestParams(node) // { path: typeof path === 'function' ? path() : path }
936
+ * ```
937
+ */
938
+ function buildResolvedRequestParams(node) {
939
+ const groups = getRequestGroups(node);
940
+ const names = requestGroupOrder.filter((key) => groups[key]);
941
+ if (names.length === 0) return null;
942
+ return `{ ${names.map((name) => `${name}: typeof ${name} === 'function' ? ${name}() : ${name}`).join(", ")} }`;
943
+ }
944
+ /**
945
+ * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
946
+ * `query: false` still marks the operation as a query so the query-family generators keep matching it,
947
+ * and a method already claimed by `query` never counts as a mutation.
948
+ */
949
+ function classifyOperation(node, { query, mutation }) {
950
+ const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
951
+ const queryMethods = new Set(query ? query.methods : []);
952
+ return {
953
+ isQuery,
954
+ isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
955
+ };
956
+ }
957
+ //#endregion
958
+ //#region src/generators/hookOptionsGenerator.tsx
959
+ /**
960
+ * Emits the `HookOptions` type used by `customOptions`. Enabled when
961
+ * `pluginReactQuery({ customOptions: { ... } })`. The generated type lists
962
+ * every hook keyed by name so user-supplied options stay in sync with the
963
+ * generated hooks at compile time.
964
+ */
965
+ const hookOptionsGenerator = defineGenerator({
966
+ name: "react-query-hook-options",
967
+ renderer: jsxRenderer,
968
+ operations(nodes, ctx) {
969
+ const { resolver, config, root } = ctx;
970
+ const { output, customOptions, query, mutation, suspense, infinite, group, override, hooks } = ctx.options;
971
+ if (!customOptions || !hooks) return null;
972
+ const name = resolver.hook.optionsName();
973
+ const resolvedFile = resolver.file({
974
+ name,
975
+ extname: ".ts",
976
+ root,
977
+ output,
978
+ group: group ?? void 0
979
+ });
980
+ const hookOptionsFile = {
981
+ ...resolvedFile,
982
+ baseName: `${name}.ts`,
983
+ path: resolvedFile.path.replace(/[^/\\]+\.ts$/, `${name}.ts`)
984
+ };
985
+ const imports = [];
986
+ const hookOptions = {};
987
+ for (const node of nodes) {
988
+ if (!ast.isHttpOperationNode(node)) continue;
989
+ const opOverrides = resolveOperationOverrides(node, override);
990
+ const nodeQuery = "query" in opOverrides ? opOverrides.query : query;
991
+ const nodeMutation = "mutation" in opOverrides ? opOverrides.mutation : mutation;
992
+ const nodeInfinite = "infinite" in opOverrides ? opOverrides.infinite : infinite;
993
+ const nodeInfiniteOptions = nodeInfinite && typeof nodeInfinite === "object" ? nodeInfinite : null;
994
+ const isQueryOp = nodeQuery === false ? !!query && query.methods.some((m) => node.method.toLowerCase() === m.toLowerCase()) : !!nodeQuery && nodeQuery.methods.some((m) => node.method.toLowerCase() === m.toLowerCase());
995
+ const nodeQueryMethods = new Set(nodeQuery ? nodeQuery.methods : []);
996
+ const isMutationOp = nodeMutation !== false && !isQueryOp && (nodeMutation ? nodeMutation.methods : []).some((m) => !nodeQueryMethods.has(m) && node.method.toLowerCase() === m.toLowerCase());
997
+ const isSuspenseOp = !!suspense;
998
+ const isInfiniteOp = !!nodeInfiniteOptions;
999
+ if (isQueryOp) {
1000
+ const queryOptionsName = resolver.query.optionsName(node);
1001
+ const queryHookName = resolver.query.name(node);
1002
+ const queryHookFile = resolver.file({
1003
+ ...operationFileEntry(node, queryHookName),
1004
+ root,
1005
+ output,
1006
+ group: group ?? void 0
1007
+ });
1008
+ imports.push(/* @__PURE__ */ jsx(File.Import, {
1009
+ name: [queryOptionsName],
1010
+ root: hookOptionsFile.path,
1011
+ path: queryHookFile.path
1012
+ }));
1013
+ hookOptions[queryHookName] = `Partial<ReturnType<typeof ${queryOptionsName}>>`;
1014
+ if (isSuspenseOp) {
1015
+ const suspenseOptionsName = resolver.suspenseQuery.optionsName(node);
1016
+ const suspenseHookName = resolver.suspenseQuery.name(node);
1017
+ const suspenseHookFile = resolver.file({
1018
+ ...operationFileEntry(node, suspenseHookName),
1019
+ root,
1020
+ output,
1021
+ group: group ?? void 0
1022
+ });
1023
+ imports.push(/* @__PURE__ */ jsx(File.Import, {
1024
+ name: [suspenseOptionsName],
1025
+ root: hookOptionsFile.path,
1026
+ path: suspenseHookFile.path
1027
+ }));
1028
+ hookOptions[suspenseHookName] = `Partial<ReturnType<typeof ${suspenseOptionsName}>>`;
1029
+ }
1030
+ if (isInfiniteOp) {
1031
+ const normalizeKey = (key) => key.replace(/\?$/, "");
1032
+ const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1033
+ if (nodeInfiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === nodeInfiniteOptions.queryParam) : false) {
1034
+ const infiniteOptionsName = resolver.infiniteQuery.optionsName(node);
1035
+ const infiniteHookName = resolver.infiniteQuery.name(node);
1036
+ const infiniteHookFile = resolver.file({
1037
+ ...operationFileEntry(node, infiniteHookName),
1038
+ root,
1039
+ output,
1040
+ group: group ?? void 0
1041
+ });
1042
+ imports.push(/* @__PURE__ */ jsx(File.Import, {
1043
+ name: [infiniteOptionsName],
1044
+ root: hookOptionsFile.path,
1045
+ path: infiniteHookFile.path
1046
+ }));
1047
+ hookOptions[infiniteHookName] = `Partial<ReturnType<typeof ${infiniteOptionsName}>>`;
1048
+ if (isSuspenseOp) {
1049
+ const suspenseInfiniteOptionsName = resolver.suspenseInfiniteQuery.optionsName(node);
1050
+ const suspenseInfiniteHookName = resolver.suspenseInfiniteQuery.name(node);
1051
+ const suspenseInfiniteHookFile = resolver.file({
1052
+ ...operationFileEntry(node, suspenseInfiniteHookName),
1053
+ root,
1054
+ output,
1055
+ group: group ?? void 0
1056
+ });
1057
+ imports.push(/* @__PURE__ */ jsx(File.Import, {
1058
+ name: [suspenseInfiniteOptionsName],
1059
+ root: hookOptionsFile.path,
1060
+ path: suspenseInfiniteHookFile.path
1061
+ }));
1062
+ hookOptions[suspenseInfiniteHookName] = `Partial<ReturnType<typeof ${suspenseInfiniteOptionsName}>>`;
1063
+ }
1064
+ }
1065
+ }
1066
+ }
1067
+ if (isMutationOp) {
1068
+ const mutationOptionsName = resolver.mutation.optionsName(node);
1069
+ const mutationHookName = resolver.mutation.name(node);
1070
+ const mutationHookFile = resolver.file({
1071
+ ...operationFileEntry(node, mutationHookName),
1072
+ root,
1073
+ output,
1074
+ group: group ?? void 0
1075
+ });
1076
+ imports.push(/* @__PURE__ */ jsx(File.Import, {
1077
+ name: [mutationOptionsName],
1078
+ root: hookOptionsFile.path,
1079
+ path: mutationHookFile.path
1080
+ }));
1081
+ hookOptions[mutationHookName] = `Partial<ReturnType<typeof ${mutationOptionsName}>>`;
1082
+ }
1083
+ }
1084
+ return /* @__PURE__ */ jsxs(File, {
1085
+ baseName: hookOptionsFile.baseName,
1086
+ path: hookOptionsFile.path,
1087
+ meta: hookOptionsFile.meta,
1088
+ banner: resolver.default.banner(ctx.meta, {
1089
+ output,
1090
+ config,
1091
+ file: {
1092
+ path: hookOptionsFile.path,
1093
+ baseName: hookOptionsFile.baseName
1094
+ }
1095
+ }),
1096
+ footer: resolver.default.footer(ctx.meta, {
1097
+ output,
1098
+ config,
1099
+ file: {
1100
+ path: hookOptionsFile.path,
1101
+ baseName: hookOptionsFile.baseName
1102
+ }
1103
+ }),
1104
+ children: [imports, /* @__PURE__ */ jsx(File.Source, {
1105
+ name,
1106
+ isExportable: true,
1107
+ isIndexable: true,
1108
+ isTypeOnly: true,
1109
+ children: /* @__PURE__ */ jsx(Type, {
1110
+ export: true,
1111
+ name,
1112
+ children: `{ ${Object.keys(hookOptions).map((key) => `${JSON.stringify(key)}: ${hookOptions[key]}`).join(", ")} }`
1113
+ })
1114
+ })]
1115
+ });
1116
+ }
1117
+ });
1118
+ //#endregion
1119
+ //#region src/components/InfiniteQuery.tsx
1120
+ const declarationPrinter$4 = functionPrinter({ mode: "declaration" });
1121
+ function buildInfiniteQueryParamsNode(node, options) {
1122
+ const { resolver, pageParamGeneric, observerOptionsName } = options;
1123
+ const optionsParam = createFunctionParameter({
1124
+ name: "options",
1125
+ type: `{
1126
+ query?: Partial<${observerOptionsName}<TQueryFnData, TError, TData, TQueryKey, ${pageParamGeneric}>> & { client?: QueryClient },
1127
+ client?: ${buildClientOptionType()}
1128
+ }`,
1129
+ default: "{}"
1130
+ });
1131
+ return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
1132
+ resolver,
1133
+ memberTypeWrapper: maybeValueOrGetter
1134
+ }), optionsParam].filter((param) => param !== null) });
1135
+ }
1136
+ function InfiniteQuery({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver, initialPageParam, queryParam, customOptions, suspense }) {
1137
+ const { TData: responseType, TError: errorType } = buildResponseTypes(node, tsResolver);
1138
+ const { pageParamType } = resolvePageParamType(node, {
1139
+ resolver: tsResolver,
1140
+ initialPageParam,
1141
+ queryParam
1142
+ });
1143
+ const hookName = suspense ? "useSuspenseInfiniteQuery" : "useInfiniteQuery";
1144
+ const observerOptionsName = suspense ? "UseSuspenseInfiniteQueryOptions" : "InfiniteQueryObserverOptions";
1145
+ const returnType = `${suspense ? "UseSuspenseInfiniteQueryResult" : "UseInfiniteQueryResult"}<TData, TError> & { queryKey: TQueryKey }`;
1146
+ const generics = [
1147
+ `TQueryFnData = ${responseType}`,
1148
+ `TError = ${errorType}`,
1149
+ "TData = InfiniteData<TQueryFnData>",
1150
+ `TQueryKey extends QueryKey = ${queryKeyTypeName}`,
1151
+ `TPageParam = ${pageParamType}`
1152
+ ];
1153
+ const resolvedParams = buildResolvedRequestParams(node);
1154
+ const queryKeyArgs = resolvedParams ? "resolvedParams" : "";
1155
+ const queryOptionsArgs = resolvedParams ? "resolvedParams, config" : "config";
1156
+ const paramsNode = buildInfiniteQueryParamsNode(node, {
1157
+ resolver: tsResolver,
1158
+ pageParamGeneric: "TPageParam",
1159
+ observerOptionsName
1160
+ });
1161
+ const paramsSignature = declarationPrinter$4.print(paramsNode) ?? "";
1162
+ return /* @__PURE__ */ jsx(File.Source, {
1163
+ name,
1164
+ isExportable: true,
1165
+ isIndexable: true,
1166
+ children: /* @__PURE__ */ jsx(Function, {
1167
+ name,
1168
+ export: true,
1169
+ generics: generics.join(", "),
1170
+ params: paramsSignature,
1171
+ returnType: void 0,
1172
+ JSDoc: { comments: buildOperationComments(node) },
1173
+ children: `
1174
+ const { query: queryConfig = {}, client: config = {} } = options ?? {}
1175
+ const { client: queryClient, ...resolvedOptions } = queryConfig${resolvedParams ? `\n const resolvedParams = ${resolvedParams}` : ""}
1176
+ const queryKey = resolvedOptions?.queryKey ?? ${queryKeyName}(${queryKeyArgs})${customOptions ? `\n const customOptions = ${customOptions.name}({ hookName: '${name}', operationId: '${node.operationId}' })` : ""}
1177
+
1178
+ const queryResult = ${hookName}({
1179
+ ...${queryOptionsName}(${queryOptionsArgs}),${customOptions ? "\n ...customOptions," : ""}
1180
+ ...resolvedOptions,
1181
+ queryKey,
1182
+ } as unknown as ${observerOptionsName}<TQueryFnData, TError, TData, TQueryKey, TPageParam>, queryClient) as ${returnType}
1183
+
1184
+ queryResult.queryKey = queryKey as TQueryKey
1185
+
1186
+ return queryResult
1187
+ `
1188
+ })
1189
+ });
1190
+ }
1191
+ //#endregion
1192
+ //#region src/components/MutationOptions.tsx
1193
+ const declarationPrinter$3 = functionPrinter({ mode: "declaration" });
1194
+ const callPrinter$2 = functionPrinter({ mode: "call" });
1195
+ function buildMutationConfigParamsNode(node) {
1196
+ return createFunctionParameters({ params: [createFunctionParameter({
1197
+ name: "config",
1198
+ type: buildRequestConfigType(node),
1199
+ default: "{}"
1200
+ })] });
1201
+ }
1202
+ function MutationOptions({ name, clientName, node, tsResolver, mutationKeyName }) {
1203
+ const { TData, TError } = buildResponseTypes(node, tsResolver);
1204
+ const configParamsNode = buildMutationConfigParamsNode(node);
1205
+ const paramsSignature = declarationPrinter$3.print(configParamsNode) ?? "";
1206
+ const groupedParam = buildGroupedRequestParam(node, { resolver: tsResolver });
1207
+ const hasMutationParams = groupedParam !== null;
1208
+ const groupedParamsNode = createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
1209
+ const TRequest = hasMutationParams ? tsResolver.response.options(node) : "undefined";
1210
+ const argBindingStr = hasMutationParams ? callPrinter$2.print(groupedParamsNode) ?? "" : "_";
1211
+ const mutationFnBody = `const { data } = await ${buildClientCall(node, {
1212
+ clientName,
1213
+ signal: false
1214
+ })}
1215
+ return data`;
1216
+ return /* @__PURE__ */ jsx(File.Source, {
1217
+ name,
1218
+ isExportable: true,
1219
+ isIndexable: true,
1220
+ children: /* @__PURE__ */ jsx(Function, {
1221
+ name,
1222
+ export: true,
1223
+ params: paramsSignature,
1224
+ generics: ["TContext = unknown"],
1225
+ children: `
1226
+ const mutationKey = ${mutationKeyName}()
1227
+ return mutationOptions<${TData}, ${TError}, ${TRequest}, TContext>({
1228
+ mutationKey,
1229
+ mutationFn: async(${argBindingStr}) => {
1230
+ ${mutationFnBody}
1231
+ },
1232
+ })
1233
+ `
1234
+ })
1235
+ });
1236
+ }
1237
+ //#endregion
1238
+ //#region src/components/Mutation.tsx
1239
+ const declarationPrinter$2 = functionPrinter({ mode: "declaration" });
1240
+ const callPrinter$1 = functionPrinter({ mode: "call" });
1241
+ function resolveMutationRequestType(node, resolver) {
1242
+ return buildGroupedRequestParam(node, { resolver }) ? resolver.response.options(node) : "undefined";
1243
+ }
1244
+ function buildMutationParamsNode(node, options) {
1245
+ const { resolver } = options;
1246
+ const { TData, TError } = buildResponseTypes(node, resolver);
1247
+ return createFunctionParameters({ params: [createFunctionParameter({
1248
+ name: "options",
1249
+ type: `{
1250
+ mutation?: UseMutationOptions<${[
1251
+ TData,
1252
+ TError,
1253
+ resolveMutationRequestType(node, resolver),
1254
+ "TContext"
1255
+ ].join(", ")}> & { client?: QueryClient },
1256
+ client?: ${buildRequestConfigType(node)},
1257
+ }`,
1258
+ default: "{}"
1259
+ })] });
1260
+ }
1261
+ function Mutation({ name, mutationOptionsName, node, tsResolver, mutationKeyName, customOptions }) {
1262
+ const { TData, TError } = buildResponseTypes(node, tsResolver);
1263
+ const generics = [
1264
+ TData,
1265
+ TError,
1266
+ resolveMutationRequestType(node, tsResolver),
1267
+ "TContext"
1268
+ ].join(", ");
1269
+ const returnType = `UseMutationResult<${generics}>`;
1270
+ const mutationOptionsConfigNode = buildMutationConfigParamsNode(node);
1271
+ const mutationOptionsParamsCall = callPrinter$1.print(mutationOptionsConfigNode) ?? "";
1272
+ const paramsNode = buildMutationParamsNode(node, { resolver: tsResolver });
1273
+ const paramsSignature = declarationPrinter$2.print(paramsNode) ?? "";
1274
+ return /* @__PURE__ */ jsx(File.Source, {
1275
+ name,
1276
+ isExportable: true,
1277
+ isIndexable: true,
1278
+ children: /* @__PURE__ */ jsx(Function, {
1279
+ name,
1280
+ export: true,
1281
+ params: paramsSignature,
1282
+ JSDoc: { comments: buildOperationComments(node) },
1283
+ generics: ["TContext"],
1284
+ children: `
1285
+ const { mutation = {}, client: config = {} } = options ?? {}
1286
+ const { client: queryClient, ...mutationOptions } = mutation;
1287
+ const mutationKey = mutationOptions.mutationKey ?? ${mutationKeyName}()
1288
+
1289
+ const baseOptions = ${mutationOptionsName}(${mutationOptionsParamsCall}) as UseMutationOptions<${generics}>${customOptions ? `\n const customOptions = ${customOptions.name}({ hookName: '${name}', operationId: '${node.operationId}' }) as UseMutationOptions<${generics}>` : ""}
1290
+
1291
+ return useMutation<${generics}>({
1292
+ ...baseOptions,${customOptions ? "\n ...customOptions," : ""}
1293
+ mutationKey,
1294
+ ...mutationOptions,
1295
+ }, queryClient) as ${returnType}
1296
+ `
1297
+ })
1298
+ });
1299
+ }
1300
+ //#endregion
1301
+ //#region src/components/Query.tsx
1302
+ const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
1303
+ function buildQueryParamsNode(node, options) {
1304
+ const { resolver, observerOptionsType } = options;
1305
+ const optionsParam = createFunctionParameter({
1306
+ name: "options",
1307
+ type: `{
1308
+ query?: Partial<${observerOptionsType}> & { client?: QueryClient },
1309
+ client?: ${buildClientOptionType()}
1310
+ }`,
1311
+ default: "{}"
1312
+ });
1313
+ return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
1314
+ resolver,
1315
+ memberTypeWrapper: maybeValueOrGetter
1316
+ }), optionsParam].filter((param) => param !== null) });
1317
+ }
1318
+ function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver, customOptions, suspense }) {
1319
+ const { TData, TError } = buildResponseTypes(node, tsResolver);
1320
+ const hookName = suspense ? "useSuspenseQuery" : "useQuery";
1321
+ const observerOptionsName = suspense ? "UseSuspenseQueryOptions" : "QueryObserverOptions";
1322
+ const observerOptionsType = suspense ? `${observerOptionsName}<${[
1323
+ TData,
1324
+ TError,
1325
+ "TData",
1326
+ "TQueryKey"
1327
+ ].join(", ")}>` : `${observerOptionsName}<${[
1328
+ TData,
1329
+ TError,
1330
+ "TData",
1331
+ "TQueryData",
1332
+ "TQueryKey"
1333
+ ].join(", ")}>`;
1334
+ const returnType = `${suspense ? "UseSuspenseQueryResult" : "UseQueryResult"}<TData, ${TError}> & { queryKey: TQueryKey }`;
1335
+ const generics = suspense ? [`TData = ${TData}`, `TQueryKey extends QueryKey = ${queryKeyTypeName}`] : [
1336
+ `TData = ${TData}`,
1337
+ `TQueryData = ${TData}`,
1338
+ `TQueryKey extends QueryKey = ${queryKeyTypeName}`
1339
+ ];
1340
+ const resolvedParams = buildResolvedRequestParams(node);
1341
+ const queryKeyArgs = resolvedParams ? "resolvedParams" : "";
1342
+ const queryOptionsArgs = resolvedParams ? "resolvedParams, config" : "config";
1343
+ const paramsNode = buildQueryParamsNode(node, {
1344
+ resolver: tsResolver,
1345
+ observerOptionsType
1346
+ });
1347
+ const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
1348
+ return /* @__PURE__ */ jsx(File.Source, {
1349
+ name,
1350
+ isExportable: true,
1351
+ isIndexable: true,
1352
+ children: /* @__PURE__ */ jsx(Function, {
1353
+ name,
1354
+ export: true,
1355
+ generics: generics.join(", "),
1356
+ params: paramsSignature,
1357
+ returnType: void 0,
1358
+ JSDoc: { comments: buildOperationComments(node) },
1359
+ children: `
1360
+ const { query: queryConfig = {}, client: config = {} } = options ?? {}
1361
+ const { client: queryClient, ...resolvedOptions } = queryConfig${resolvedParams ? `\n const resolvedParams = ${resolvedParams}` : ""}
1362
+ const queryKey = resolvedOptions?.queryKey ?? ${queryKeyName}(${queryKeyArgs})${customOptions ? `\n const customOptions = ${customOptions.name}({ hookName: '${name}', operationId: '${node.operationId}' })` : ""}
1363
+
1364
+ const queryResult = ${hookName}({
1365
+ ...${queryOptionsName}(${queryOptionsArgs}),${customOptions ? "\n ...customOptions," : ""}
1366
+ ...resolvedOptions,
1367
+ queryKey,
1368
+ } as unknown as ${observerOptionsName}, queryClient) as ${returnType}
1369
+
1370
+ queryResult.queryKey = queryKey as TQueryKey
1371
+
1372
+ return queryResult
1373
+ `
1374
+ })
1375
+ });
1376
+ }
1377
+ //#endregion
1378
+ //#region src/components/QueryOptions.tsx
1379
+ const declarationPrinter = functionPrinter({ mode: "declaration" });
1380
+ const callPrinter = functionPrinter({ mode: "call" });
1381
+ function getQueryOptionsParams(node, options) {
1382
+ return buildQueryOptionsParams(node, options);
1383
+ }
1384
+ function QueryOptions({ name, clientName, node, tsResolver, queryKeyName }) {
1385
+ const { TData, TError } = buildResponseTypes(node, tsResolver);
1386
+ const queryKeyParamsNode = buildQueryKeyParams(node, { resolver: tsResolver });
1387
+ const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? "";
1388
+ const paramsNode = getQueryOptionsParams(node, { resolver: tsResolver });
1389
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1390
+ const queryFnBody = `const { data } = await ${buildClientCall(node, {
1391
+ clientName,
1392
+ signal: true
1393
+ })}
1394
+ return data`;
1395
+ return /* @__PURE__ */ jsx(File.Source, {
1396
+ name,
1397
+ isExportable: true,
1398
+ isIndexable: true,
1399
+ children: /* @__PURE__ */ jsx(Function, {
1400
+ name,
1401
+ export: true,
1402
+ params: paramsSignature,
1403
+ children: `
1404
+ const queryKey = ${queryKeyName}(${queryKeyParamsCall})
1405
+ return queryOptions<${TData}, ${TError}, ${TData}, typeof queryKey>({
1406
+ queryKey,
1407
+ queryFn: async ({ signal }) => {
1408
+ ${queryFnBody}
1409
+ },
1410
+ })
1411
+ `
1412
+ })
1413
+ });
1414
+ }
1415
+ //#endregion
1416
+ //#region src/generators/infiniteQueryGenerator.tsx
1417
+ /**
1418
+ * Built-in generator for `useInfiniteQuery` hooks. Enabled when
1419
+ * `pluginReactQuery({ infinite: { ... }, hooks: true })`. Emits one
1420
+ * `useFooInfiniteQuery` hook per query operation, wiring the configured
1421
+ * `nextParam` / `previousParam` paths into TanStack Query's cursor-based
1422
+ * pagination.
1423
+ */
1424
+ const infiniteQueryGenerator = defineGenerator({
1425
+ name: "react-infinite-query",
1426
+ renderer: jsxRenderer,
1427
+ operation(node, ctx) {
1428
+ if (!ast.isHttpOperationNode(node)) return null;
1429
+ const { config, driver, resolver, root } = ctx;
1430
+ const { output, query, mutation, infinite, client, group, customOptions, hooks } = ctx.options;
1431
+ const pluginTs = driver.getPlugin(pluginTsName);
1432
+ if (!pluginTs) return null;
1433
+ const tsResolver = driver.getResolver(pluginTsName);
1434
+ const { isQuery, isMutation } = classifyOperation(node, {
1435
+ query,
1436
+ mutation
1437
+ });
1438
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1439
+ if (!isQuery || isMutation || !infiniteOptions || !hooks) return null;
1440
+ const normalizeKey = (key) => key.replace(/\?$/, "");
1441
+ const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1442
+ if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1443
+ const importPath = query ? query.importPath : "@tanstack/react-query";
1444
+ const contractOp = resolveClientOperation({
1445
+ clientPlugin: { pluginName: client.pluginName },
1446
+ driver,
1447
+ node,
1448
+ root,
1449
+ output
1450
+ });
1451
+ if (!contractOp) return null;
1452
+ const queryName = resolver.infiniteQuery.name(node);
1453
+ const queryOptionsName = resolver.infiniteQuery.optionsName(node);
1454
+ const queryKeyName = resolver.infiniteQuery.keyName(node);
1455
+ const queryKeyTypeName = resolver.infiniteQuery.keyTypeName(node);
1456
+ const meta = {
1457
+ file: resolver.file({
1458
+ ...operationFileEntry(node, queryName),
1459
+ root,
1460
+ output,
1461
+ group: group ?? void 0
1462
+ }),
1463
+ fileTs: tsResolver.file({
1464
+ ...operationFileEntry(node, node.operationId),
1465
+ root,
1466
+ output: pluginTs.options?.output ?? output,
1467
+ group: pluginTs.options?.group ?? void 0
1468
+ })
1469
+ };
1470
+ const rawQueryParams = getOperationParameters(node).query;
1471
+ const queryParamsTypeName = rawQueryParams.length > 0 && tsResolver.param.query(node, rawQueryParams[0]) !== tsResolver.param.name(node, rawQueryParams[0]) ? tsResolver.param.query(node, rawQueryParams[0]) : null;
1472
+ const importedTypeNames = [
1473
+ tsResolver.response.options(node),
1474
+ queryParamsTypeName,
1475
+ ...resolveOperationTypeNames(node, tsResolver, {
1476
+ exclude: [queryKeyTypeName],
1477
+ order: "body-response-first",
1478
+ includeParams: false
1479
+ })
1480
+ ].filter((name) => Boolean(name));
1481
+ const calledClientName = contractOp.name;
1482
+ return /* @__PURE__ */ jsxs(File, {
1483
+ baseName: meta.file.baseName,
1484
+ path: meta.file.path,
1485
+ meta: meta.file.meta,
1486
+ banner: resolver.default.banner(ctx.meta, {
1487
+ output,
1488
+ config,
1489
+ file: {
1490
+ path: meta.file.path,
1491
+ baseName: meta.file.baseName
1492
+ }
1493
+ }),
1494
+ footer: resolver.default.footer(ctx.meta, {
1495
+ output,
1496
+ config,
1497
+ file: {
1498
+ path: meta.file.path,
1499
+ baseName: meta.file.baseName
1500
+ }
1501
+ }),
1502
+ children: [
1503
+ /* @__PURE__ */ jsx(File.Import, {
1504
+ name: [contractOp.name],
1505
+ root: meta.file.path,
1506
+ path: contractOp.path
1507
+ }),
1508
+ /* @__PURE__ */ jsx(File.Import, {
1509
+ name: ["RequestConfig", "ResponseErrorConfig"],
1510
+ root: meta.file.path,
1511
+ path: contractOp.clientPath,
1512
+ isTypeOnly: true
1513
+ }),
1514
+ customOptions && /* @__PURE__ */ jsx(File.Import, {
1515
+ name: [customOptions.name],
1516
+ path: customOptions.importPath
1517
+ }),
1518
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1519
+ name: Array.from(new Set(importedTypeNames)),
1520
+ root: meta.file.path,
1521
+ path: meta.fileTs.path,
1522
+ isTypeOnly: true
1523
+ }),
1524
+ /* @__PURE__ */ jsx(QueryKey, {
1525
+ name: queryKeyName,
1526
+ typeName: queryKeyTypeName,
1527
+ node,
1528
+ tsResolver,
1529
+ transformer: ctx.options.queryKey
1530
+ }),
1531
+ /* @__PURE__ */ jsx(File.Import, {
1532
+ name: ["InfiniteData"],
1533
+ isTypeOnly: true,
1534
+ path: importPath
1535
+ }),
1536
+ /* @__PURE__ */ jsx(File.Import, {
1537
+ name: ["infiniteQueryOptions"],
1538
+ path: importPath
1539
+ }),
1540
+ /* @__PURE__ */ jsx(InfiniteQueryOptions, {
1541
+ name: queryOptionsName,
1542
+ clientName: calledClientName,
1543
+ queryKeyName,
1544
+ node,
1545
+ tsResolver,
1546
+ cursorParam: infiniteOptions.cursorParam,
1547
+ nextParam: infiniteOptions.nextParam,
1548
+ previousParam: infiniteOptions.previousParam,
1549
+ initialPageParam: infiniteOptions.initialPageParam,
1550
+ queryParam: infiniteOptions.queryParam
1551
+ }),
1552
+ /* @__PURE__ */ jsx(File.Import, {
1553
+ name: ["useInfiniteQuery"],
1554
+ path: importPath
1555
+ }),
1556
+ /* @__PURE__ */ jsx(File.Import, {
1557
+ name: [
1558
+ "QueryKey",
1559
+ "QueryClient",
1560
+ "InfiniteQueryObserverOptions",
1561
+ "UseInfiniteQueryResult"
1562
+ ],
1563
+ path: importPath,
1564
+ isTypeOnly: true
1565
+ }),
1566
+ /* @__PURE__ */ jsx(InfiniteQuery, {
1567
+ name: queryName,
1568
+ queryOptionsName,
1569
+ queryKeyName,
1570
+ queryKeyTypeName,
1571
+ node,
1572
+ tsResolver,
1573
+ initialPageParam: infiniteOptions.initialPageParam,
1574
+ queryParam: infiniteOptions.queryParam,
1575
+ customOptions
1576
+ })
1577
+ ]
1578
+ });
1579
+ }
1580
+ });
1581
+ //#endregion
1582
+ //#region src/generators/mutationGenerator.tsx
1583
+ /**
1584
+ * Built-in generator for `useMutation` hooks. Emits one `useFooMutation` hook
1585
+ * per POST/PUT/DELETE operation (configurable via `mutation.methods`) plus
1586
+ * the matching `fooMutationKey` / `fooMutationOptions` helpers.
1587
+ */
1588
+ const mutationGenerator = defineGenerator({
1589
+ name: "react-query-mutation",
1590
+ renderer: jsxRenderer,
1591
+ operation(node, ctx) {
1592
+ if (!ast.isHttpOperationNode(node)) return null;
1593
+ const { config, driver, resolver, root } = ctx;
1594
+ const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1595
+ const pluginTs = driver.getPlugin(pluginTsName);
1596
+ if (!pluginTs) return null;
1597
+ const tsResolver = driver.getResolver(pluginTsName);
1598
+ const { isMutation } = classifyOperation(node, {
1599
+ query,
1600
+ mutation
1601
+ });
1602
+ if (!isMutation) return null;
1603
+ const importPath = mutation ? mutation.importPath : "@tanstack/react-query";
1604
+ const contractOp = resolveClientOperation({
1605
+ clientPlugin: { pluginName: client.pluginName },
1606
+ driver,
1607
+ node,
1608
+ root,
1609
+ output
1610
+ });
1611
+ if (!contractOp) return null;
1612
+ const mutationHookName = resolver.mutation.name(node);
1613
+ const mutationTypeName = resolver.mutation.typeName(node);
1614
+ const mutationOptionsName = resolver.mutation.optionsName(node);
1615
+ const mutationKeyName = resolver.mutation.keyName(node);
1616
+ const meta = {
1617
+ file: resolver.file({
1618
+ ...operationFileEntry(node, mutationHookName),
1619
+ root,
1620
+ output,
1621
+ group: group ?? void 0
1622
+ }),
1623
+ fileTs: tsResolver.file({
1624
+ ...operationFileEntry(node, node.operationId),
1625
+ root,
1626
+ output: pluginTs.options?.output ?? output,
1627
+ group: pluginTs.options?.group ?? void 0
1628
+ })
1629
+ };
1630
+ const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
1631
+ order: "body-response-first",
1632
+ includeParams: false
1633
+ })].filter((name) => Boolean(name));
1634
+ const calledClientName = contractOp.name;
1635
+ return /* @__PURE__ */ jsxs(File, {
1636
+ baseName: meta.file.baseName,
1637
+ path: meta.file.path,
1638
+ meta: meta.file.meta,
1639
+ banner: resolver.default.banner(ctx.meta, {
1640
+ output,
1641
+ config,
1642
+ file: {
1643
+ path: meta.file.path,
1644
+ baseName: meta.file.baseName
1645
+ }
1646
+ }),
1647
+ footer: resolver.default.footer(ctx.meta, {
1648
+ output,
1649
+ config,
1650
+ file: {
1651
+ path: meta.file.path,
1652
+ baseName: meta.file.baseName
1653
+ }
1654
+ }),
1655
+ children: [
1656
+ /* @__PURE__ */ jsx(File.Import, {
1657
+ name: [contractOp.name],
1658
+ root: meta.file.path,
1659
+ path: contractOp.path
1660
+ }),
1661
+ /* @__PURE__ */ jsx(File.Import, {
1662
+ name: ["RequestConfig", "ResponseErrorConfig"],
1663
+ root: meta.file.path,
1664
+ path: contractOp.clientPath,
1665
+ isTypeOnly: true
1666
+ }),
1667
+ customOptions && /* @__PURE__ */ jsx(File.Import, {
1668
+ name: [customOptions.name],
1669
+ path: customOptions.importPath
1670
+ }),
1671
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1672
+ name: Array.from(new Set(importedTypeNames)),
1673
+ root: meta.file.path,
1674
+ path: meta.fileTs.path,
1675
+ isTypeOnly: true
1676
+ }),
1677
+ /* @__PURE__ */ jsx(MutationKey, {
1678
+ name: mutationKeyName,
1679
+ node,
1680
+ transformer: ctx.options.mutationKey
1681
+ }),
1682
+ /* @__PURE__ */ jsx(File.Import, {
1683
+ name: ["mutationOptions"],
1684
+ path: importPath
1685
+ }),
1686
+ /* @__PURE__ */ jsx(MutationOptions, {
1687
+ name: mutationOptionsName,
1688
+ clientName: calledClientName,
1689
+ mutationKeyName,
1690
+ node,
1691
+ tsResolver
1692
+ }),
1693
+ mutation && hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
1694
+ /* @__PURE__ */ jsx(File.Import, {
1695
+ name: ["useMutation"],
1696
+ path: importPath
1697
+ }),
1698
+ /* @__PURE__ */ jsx(File.Import, {
1699
+ name: [
1700
+ "UseMutationOptions",
1701
+ "UseMutationResult",
1702
+ "QueryClient"
1703
+ ],
1704
+ path: importPath,
1705
+ isTypeOnly: true
1706
+ }),
1707
+ /* @__PURE__ */ jsx(Mutation, {
1708
+ name: mutationHookName,
1709
+ typeName: mutationTypeName,
1710
+ mutationOptionsName,
1711
+ mutationKeyName,
1712
+ node,
1713
+ tsResolver,
1714
+ customOptions
1715
+ })
1716
+ ] })
1717
+ ]
1718
+ });
1719
+ }
1720
+ });
1721
+ //#endregion
1722
+ //#region src/generators/queryGenerator.tsx
1723
+ /**
1724
+ * Built-in generator for `useQuery` hooks. Emits one `useFooQuery` hook per
1725
+ * GET operation (configurable via `query.methods`) plus the matching
1726
+ * `fooQueryKey` / `fooQueryOptions` helpers.
1727
+ */
1728
+ const queryGenerator = defineGenerator({
1729
+ name: "react-query",
1730
+ renderer: jsxRenderer,
1731
+ operation(node, ctx) {
1732
+ if (!ast.isHttpOperationNode(node)) return null;
1733
+ const { config, driver, resolver, root } = ctx;
1734
+ const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1735
+ const pluginTs = driver.getPlugin(pluginTsName);
1736
+ if (!pluginTs) return null;
1737
+ const tsResolver = driver.getResolver(pluginTsName);
1738
+ const { isQuery, isMutation } = classifyOperation(node, {
1739
+ query,
1740
+ mutation
1741
+ });
1742
+ if (!isQuery || isMutation) return null;
1743
+ const importPath = query ? query.importPath : "@tanstack/react-query";
1744
+ const contractOp = resolveClientOperation({
1745
+ clientPlugin: { pluginName: client.pluginName },
1746
+ driver,
1747
+ node,
1748
+ root,
1749
+ output
1750
+ });
1751
+ if (!contractOp) return null;
1752
+ const queryName = resolver.query.name(node);
1753
+ const queryOptionsName = resolver.query.optionsName(node);
1754
+ const queryKeyName = resolver.query.keyName(node);
1755
+ const queryKeyTypeName = resolver.query.keyTypeName(node);
1756
+ const meta = {
1757
+ file: resolver.file({
1758
+ ...operationFileEntry(node, queryName),
1759
+ root,
1760
+ output,
1761
+ group: group ?? void 0
1762
+ }),
1763
+ fileTs: tsResolver.file({
1764
+ ...operationFileEntry(node, node.operationId),
1765
+ root,
1766
+ output: pluginTs.options?.output ?? output,
1767
+ group: pluginTs.options?.group ?? void 0
1768
+ })
1769
+ };
1770
+ const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
1771
+ exclude: [queryKeyTypeName],
1772
+ order: "body-response-first",
1773
+ includeParams: false
1774
+ })].filter((name) => Boolean(name));
1775
+ const calledClientName = contractOp.name;
1776
+ return /* @__PURE__ */ jsxs(File, {
1777
+ baseName: meta.file.baseName,
1778
+ path: meta.file.path,
1779
+ meta: meta.file.meta,
1780
+ banner: resolver.default.banner(ctx.meta, {
1781
+ output,
1782
+ config,
1783
+ file: {
1784
+ path: meta.file.path,
1785
+ baseName: meta.file.baseName
1786
+ }
1787
+ }),
1788
+ footer: resolver.default.footer(ctx.meta, {
1789
+ output,
1790
+ config,
1791
+ file: {
1792
+ path: meta.file.path,
1793
+ baseName: meta.file.baseName
1794
+ }
1795
+ }),
1796
+ children: [
1797
+ /* @__PURE__ */ jsx(File.Import, {
1798
+ name: [contractOp.name],
1799
+ root: meta.file.path,
1800
+ path: contractOp.path
1801
+ }),
1802
+ /* @__PURE__ */ jsx(File.Import, {
1803
+ name: ["RequestConfig", "ResponseErrorConfig"],
1804
+ root: meta.file.path,
1805
+ path: contractOp.clientPath,
1806
+ isTypeOnly: true
1807
+ }),
1808
+ customOptions && /* @__PURE__ */ jsx(File.Import, {
1809
+ name: [customOptions.name],
1810
+ path: customOptions.importPath
1811
+ }),
1812
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1813
+ name: Array.from(new Set(importedTypeNames)),
1814
+ root: meta.file.path,
1815
+ path: meta.fileTs.path,
1816
+ isTypeOnly: true
1817
+ }),
1818
+ /* @__PURE__ */ jsx(QueryKey, {
1819
+ name: queryKeyName,
1820
+ typeName: queryKeyTypeName,
1821
+ node,
1822
+ tsResolver,
1823
+ transformer: ctx.options.queryKey
1824
+ }),
1825
+ /* @__PURE__ */ jsx(File.Import, {
1826
+ name: ["queryOptions"],
1827
+ path: importPath
1828
+ }),
1829
+ /* @__PURE__ */ jsx(QueryOptions, {
1830
+ name: queryOptionsName,
1831
+ clientName: calledClientName,
1832
+ queryKeyName,
1833
+ node,
1834
+ tsResolver
1835
+ }),
1836
+ query && hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
1837
+ /* @__PURE__ */ jsx(File.Import, {
1838
+ name: ["useQuery"],
1839
+ path: importPath
1840
+ }),
1841
+ /* @__PURE__ */ jsx(File.Import, {
1842
+ name: [
1843
+ "QueryKey",
1844
+ "QueryClient",
1845
+ "QueryObserverOptions",
1846
+ "UseQueryResult"
1847
+ ],
1848
+ path: importPath,
1849
+ isTypeOnly: true
1850
+ }),
1851
+ /* @__PURE__ */ jsx(Query, {
1852
+ name: queryName,
1853
+ queryOptionsName,
1854
+ queryKeyName,
1855
+ queryKeyTypeName,
1856
+ node,
1857
+ tsResolver,
1858
+ customOptions
1859
+ })
1860
+ ] })
1861
+ ]
1862
+ });
1863
+ }
1864
+ });
1865
+ //#endregion
1866
+ //#region src/generators/suspenseInfiniteQueryGenerator.tsx
1867
+ /**
1868
+ * Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when
1869
+ * `suspense`, `infinite`, and `hooks` are all configured. Combines suspense
1870
+ * semantics with cursor-based pagination. Handlers throw promises while
1871
+ * loading and pull additional pages on demand.
1872
+ */
1873
+ const suspenseInfiniteQueryGenerator = defineGenerator({
1874
+ name: "react-suspense-infinite-query",
1875
+ renderer: jsxRenderer,
1876
+ operation(node, ctx) {
1877
+ if (!ast.isHttpOperationNode(node)) return null;
1878
+ const { config, driver, resolver, root } = ctx;
1879
+ const { output, query, mutation, infinite, suspense, client, group, customOptions, hooks } = ctx.options;
1880
+ const pluginTs = driver.getPlugin(pluginTsName);
1881
+ if (!pluginTs) return null;
1882
+ const tsResolver = driver.getResolver(pluginTsName);
1883
+ const { isQuery, isMutation } = classifyOperation(node, {
1884
+ query,
1885
+ mutation
1886
+ });
1887
+ const isSuspense = !!suspense;
1888
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1889
+ if (!isQuery || isMutation || !isSuspense || !infiniteOptions || !hooks) return null;
1890
+ const normalizeKey = (key) => key.replace(/\?$/, "");
1891
+ const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1892
+ if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1893
+ const importPath = query ? query.importPath : "@tanstack/react-query";
1894
+ const contractOp = resolveClientOperation({
1895
+ clientPlugin: { pluginName: client.pluginName },
1896
+ driver,
1897
+ node,
1898
+ root,
1899
+ output
1900
+ });
1901
+ if (!contractOp) return null;
1902
+ const queryName = resolver.suspenseInfiniteQuery.name(node);
1903
+ const queryOptionsName = resolver.suspenseInfiniteQuery.optionsName(node);
1904
+ const queryKeyName = resolver.suspenseInfiniteQuery.keyName(node);
1905
+ const queryKeyTypeName = resolver.suspenseInfiniteQuery.keyTypeName(node);
1906
+ const meta = {
1907
+ file: resolver.file({
1908
+ ...operationFileEntry(node, queryName),
1909
+ root,
1910
+ output,
1911
+ group: group ?? void 0
1912
+ }),
1913
+ fileTs: tsResolver.file({
1914
+ ...operationFileEntry(node, node.operationId),
1915
+ root,
1916
+ output: pluginTs.options?.output ?? output,
1917
+ group: pluginTs.options?.group ?? void 0
1918
+ })
1919
+ };
1920
+ const rawQueryParams = getOperationParameters(node).query;
1921
+ const queryParamsTypeName = rawQueryParams.length > 0 && tsResolver.param.query(node, rawQueryParams[0]) !== tsResolver.param.name(node, rawQueryParams[0]) ? tsResolver.param.query(node, rawQueryParams[0]) : null;
1922
+ const importedTypeNames = [
1923
+ tsResolver.response.options(node),
1924
+ queryParamsTypeName,
1925
+ ...resolveOperationTypeNames(node, tsResolver, {
1926
+ order: "body-response-first",
1927
+ includeParams: false
1928
+ })
1929
+ ].filter((name) => Boolean(name));
1930
+ const calledClientName = contractOp.name;
1931
+ return /* @__PURE__ */ jsxs(File, {
1932
+ baseName: meta.file.baseName,
1933
+ path: meta.file.path,
1934
+ meta: meta.file.meta,
1935
+ banner: resolver.default.banner(ctx.meta, {
1936
+ output,
1937
+ config,
1938
+ file: {
1939
+ path: meta.file.path,
1940
+ baseName: meta.file.baseName
1941
+ }
1942
+ }),
1943
+ footer: resolver.default.footer(ctx.meta, {
1944
+ output,
1945
+ config,
1946
+ file: {
1947
+ path: meta.file.path,
1948
+ baseName: meta.file.baseName
1949
+ }
1950
+ }),
1951
+ children: [
1952
+ /* @__PURE__ */ jsx(File.Import, {
1953
+ name: [contractOp.name],
1954
+ root: meta.file.path,
1955
+ path: contractOp.path
1956
+ }),
1957
+ /* @__PURE__ */ jsx(File.Import, {
1958
+ name: ["RequestConfig", "ResponseErrorConfig"],
1959
+ root: meta.file.path,
1960
+ path: contractOp.clientPath,
1961
+ isTypeOnly: true
1962
+ }),
1963
+ customOptions && /* @__PURE__ */ jsx(File.Import, {
1964
+ name: [customOptions.name],
1965
+ path: customOptions.importPath
1966
+ }),
1967
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1968
+ name: Array.from(new Set(importedTypeNames)),
1969
+ root: meta.file.path,
1970
+ path: meta.fileTs.path,
1971
+ isTypeOnly: true
1972
+ }),
1973
+ /* @__PURE__ */ jsx(QueryKey, {
1974
+ name: queryKeyName,
1975
+ typeName: queryKeyTypeName,
1976
+ node,
1977
+ tsResolver,
1978
+ transformer: ctx.options.queryKey
1979
+ }),
1980
+ /* @__PURE__ */ jsx(File.Import, {
1981
+ name: ["InfiniteData"],
1982
+ isTypeOnly: true,
1983
+ path: importPath
1984
+ }),
1985
+ /* @__PURE__ */ jsx(File.Import, {
1986
+ name: ["infiniteQueryOptions"],
1987
+ path: importPath
1988
+ }),
1989
+ /* @__PURE__ */ jsx(InfiniteQueryOptions, {
1990
+ name: queryOptionsName,
1991
+ clientName: calledClientName,
1992
+ queryKeyName,
1993
+ node,
1994
+ tsResolver,
1995
+ cursorParam: infiniteOptions.cursorParam,
1996
+ nextParam: infiniteOptions.nextParam,
1997
+ previousParam: infiniteOptions.previousParam,
1998
+ initialPageParam: infiniteOptions.initialPageParam,
1999
+ queryParam: infiniteOptions.queryParam
2000
+ }),
2001
+ /* @__PURE__ */ jsx(File.Import, {
2002
+ name: ["useSuspenseInfiniteQuery"],
2003
+ path: importPath
2004
+ }),
2005
+ /* @__PURE__ */ jsx(File.Import, {
2006
+ name: [
2007
+ "QueryKey",
2008
+ "QueryClient",
2009
+ "UseSuspenseInfiniteQueryOptions",
2010
+ "UseSuspenseInfiniteQueryResult"
2011
+ ],
2012
+ path: importPath,
2013
+ isTypeOnly: true
2014
+ }),
2015
+ /* @__PURE__ */ jsx(InfiniteQuery, {
2016
+ name: queryName,
2017
+ queryOptionsName,
2018
+ queryKeyName,
2019
+ queryKeyTypeName,
2020
+ node,
2021
+ tsResolver,
2022
+ initialPageParam: infiniteOptions.initialPageParam,
2023
+ queryParam: infiniteOptions.queryParam,
2024
+ customOptions,
2025
+ suspense: true
2026
+ })
2027
+ ]
2028
+ });
2029
+ }
2030
+ });
2031
+ //#endregion
2032
+ //#region src/generators/suspenseQueryGenerator.tsx
2033
+ /**
2034
+ * Built-in generator for `useSuspenseQuery` hooks. Enabled when
2035
+ * `pluginReactQuery({ suspense: {}, hooks: true })`. Emits one
2036
+ * `useFooSuspenseQuery` hook per query operation. Suspense queries throw
2037
+ * promises while loading and require a `<Suspense>` boundary in the React
2038
+ * tree. TanStack Query v5+ only.
2039
+ */
2040
+ const suspenseQueryGenerator = defineGenerator({
2041
+ name: "react-suspense-query",
2042
+ renderer: jsxRenderer,
2043
+ operation(node, ctx) {
2044
+ if (!ast.isHttpOperationNode(node)) return null;
2045
+ const { config, driver, resolver, root } = ctx;
2046
+ const { output, query, mutation, suspense, client, group, customOptions, hooks } = ctx.options;
2047
+ const pluginTs = driver.getPlugin(pluginTsName);
2048
+ if (!pluginTs) return null;
2049
+ const tsResolver = driver.getResolver(pluginTsName);
2050
+ const { isQuery, isMutation } = classifyOperation(node, {
2051
+ query,
2052
+ mutation
2053
+ });
2054
+ if (!isQuery || isMutation || !!!suspense || !hooks) return null;
2055
+ const importPath = query ? query.importPath : "@tanstack/react-query";
2056
+ const contractOp = resolveClientOperation({
2057
+ clientPlugin: { pluginName: client.pluginName },
2058
+ driver,
2059
+ node,
2060
+ root,
2061
+ output
2062
+ });
2063
+ if (!contractOp) return null;
2064
+ const queryName = resolver.suspenseQuery.name(node);
2065
+ const queryOptionsName = resolver.suspenseQuery.optionsName(node);
2066
+ const queryKeyName = resolver.suspenseQuery.keyName(node);
2067
+ const queryKeyTypeName = resolver.suspenseQuery.keyTypeName(node);
2068
+ const meta = {
2069
+ file: resolver.file({
2070
+ ...operationFileEntry(node, queryName),
2071
+ root,
2072
+ output,
2073
+ group: group ?? void 0
2074
+ }),
2075
+ fileTs: tsResolver.file({
2076
+ ...operationFileEntry(node, node.operationId),
2077
+ root,
2078
+ output: pluginTs.options?.output ?? output,
2079
+ group: pluginTs.options?.group ?? void 0
2080
+ })
2081
+ };
2082
+ const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
2083
+ exclude: [queryKeyTypeName],
2084
+ order: "body-response-first",
2085
+ includeParams: false
2086
+ })].filter((name) => Boolean(name));
2087
+ const calledClientName = contractOp.name;
2088
+ return /* @__PURE__ */ jsxs(File, {
2089
+ baseName: meta.file.baseName,
2090
+ path: meta.file.path,
2091
+ meta: meta.file.meta,
2092
+ banner: resolver.default.banner(ctx.meta, {
2093
+ output,
2094
+ config,
2095
+ file: {
2096
+ path: meta.file.path,
2097
+ baseName: meta.file.baseName
2098
+ }
2099
+ }),
2100
+ footer: resolver.default.footer(ctx.meta, {
2101
+ output,
2102
+ config,
2103
+ file: {
2104
+ path: meta.file.path,
2105
+ baseName: meta.file.baseName
2106
+ }
2107
+ }),
2108
+ children: [
2109
+ /* @__PURE__ */ jsx(File.Import, {
2110
+ name: [contractOp.name],
2111
+ root: meta.file.path,
2112
+ path: contractOp.path
2113
+ }),
2114
+ /* @__PURE__ */ jsx(File.Import, {
2115
+ name: ["RequestConfig", "ResponseErrorConfig"],
2116
+ root: meta.file.path,
2117
+ path: contractOp.clientPath,
2118
+ isTypeOnly: true
2119
+ }),
2120
+ customOptions && /* @__PURE__ */ jsx(File.Import, {
2121
+ name: [customOptions.name],
2122
+ path: customOptions.importPath
2123
+ }),
2124
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
2125
+ name: Array.from(new Set(importedTypeNames)),
2126
+ root: meta.file.path,
2127
+ path: meta.fileTs.path,
2128
+ isTypeOnly: true
2129
+ }),
2130
+ /* @__PURE__ */ jsx(QueryKey, {
2131
+ name: queryKeyName,
2132
+ typeName: queryKeyTypeName,
2133
+ node,
2134
+ tsResolver,
2135
+ transformer: ctx.options.queryKey
2136
+ }),
2137
+ /* @__PURE__ */ jsx(File.Import, {
2138
+ name: ["queryOptions"],
2139
+ path: importPath
2140
+ }),
2141
+ /* @__PURE__ */ jsx(QueryOptions, {
2142
+ name: queryOptionsName,
2143
+ clientName: calledClientName,
2144
+ queryKeyName,
2145
+ node,
2146
+ tsResolver
2147
+ }),
2148
+ /* @__PURE__ */ jsx(File.Import, {
2149
+ name: ["useSuspenseQuery"],
2150
+ path: importPath
2151
+ }),
2152
+ /* @__PURE__ */ jsx(File.Import, {
2153
+ name: [
2154
+ "QueryKey",
2155
+ "QueryClient",
2156
+ "UseSuspenseQueryOptions",
2157
+ "UseSuspenseQueryResult"
2158
+ ],
2159
+ path: importPath,
2160
+ isTypeOnly: true
2161
+ }),
2162
+ /* @__PURE__ */ jsx(Query, {
2163
+ name: queryName,
2164
+ queryOptionsName,
2165
+ queryKeyName,
2166
+ queryKeyTypeName,
2167
+ node,
2168
+ tsResolver,
2169
+ customOptions,
2170
+ suspense: true
2171
+ })
2172
+ ]
2173
+ });
2174
+ }
2175
+ });
2176
+ //#endregion
2177
+ //#region src/resolvers/resolverReactQuery.ts
2178
+ /**
2179
+ * Default resolver used by `@kubb/plugin-react-query`. Decides the names and
2180
+ * file paths for every generated TanStack Query hook (`useFoo`,
2181
+ * `useFooSuspense`, `useFooInfinite`, ...) and its companion helpers
2182
+ * (`fooQueryKey`, `fooQueryOptions`).
2183
+ *
2184
+ * Functions and files use the built-in camelCase casing; hooks get the `use`
2185
+ * prefix; suspense and infinite variants are suffixed with `Suspense`/`Infinite`.
2186
+ *
2187
+ * @example Resolve hook and helper names
2188
+ * ```ts
2189
+ * import { resolverReactQuery } from '@kubb/plugin-react-query'
2190
+ *
2191
+ * resolverReactQuery.query.name(operationNode) // 'useGetPetById'
2192
+ * resolverReactQuery.mutation.name(operationNode) // 'useUpdatePet'
2193
+ * resolverReactQuery.query.keyName(operationNode) // 'getPetByIdQueryKey'
2194
+ * resolverReactQuery.query.optionsName(operationNode) // 'getPetByIdQueryOptions'
2195
+ * ```
2196
+ */
2197
+ const resolverReactQuery = createResolver({
26
2198
  pluginName: "plugin-react-query",
27
- default(name, type) {
28
- return camelCase(name, { isFile: type === "file" });
29
- },
30
- resolveName(name) {
31
- return this.default(name, "function");
32
- },
33
- resolvePathName(name, type) {
34
- return this.default(name, type);
35
- },
36
- resolveQueryName(node) {
37
- return `use${capitalize(this.resolveName(node.operationId))}`;
38
- },
39
- resolveSuspenseQueryName(node) {
40
- return `use${capitalize(this.resolveName(node.operationId))}Suspense`;
41
- },
42
- resolveInfiniteQueryName(node) {
43
- return `use${capitalize(this.resolveName(node.operationId))}Infinite`;
44
- },
45
- resolveSuspenseInfiniteQueryName(node) {
46
- return `use${capitalize(this.resolveName(node.operationId))}SuspenseInfinite`;
47
- },
48
- resolveMutationName(node) {
49
- return `use${capitalize(this.resolveName(node.operationId))}`;
50
- },
51
- resolveQueryOptionsName(node) {
52
- return `${this.resolveName(node.operationId)}QueryOptions`;
53
- },
54
- resolveSuspenseQueryOptionsName(node) {
55
- return `${this.resolveName(node.operationId)}SuspenseQueryOptions`;
56
- },
57
- resolveInfiniteQueryOptionsName(node) {
58
- return `${this.resolveName(node.operationId)}InfiniteQueryOptions`;
59
- },
60
- resolveSuspenseInfiniteQueryOptionsName(node) {
61
- return `${this.resolveName(node.operationId)}SuspenseInfiniteQueryOptions`;
62
- },
63
- resolveMutationOptionsName(node) {
64
- return `${this.resolveName(node.operationId)}MutationOptions`;
65
- },
66
- resolveQueryKeyName(node) {
67
- return `${this.resolveName(node.operationId)}QueryKey`;
68
- },
69
- resolveSuspenseQueryKeyName(node) {
70
- return `${this.resolveName(node.operationId)}SuspenseQueryKey`;
71
- },
72
- resolveInfiniteQueryKeyName(node) {
73
- return `${this.resolveName(node.operationId)}InfiniteQueryKey`;
74
- },
75
- resolveSuspenseInfiniteQueryKeyName(node) {
76
- return `${this.resolveName(node.operationId)}SuspenseInfiniteQueryKey`;
77
- },
78
- resolveMutationKeyName(node) {
79
- return `${this.resolveName(node.operationId)}MutationKey`;
80
- },
81
- resolveQueryKeyTypeName(node) {
82
- return `${capitalize(this.resolveName(node.operationId))}QueryKey`;
83
- },
84
- resolveSuspenseQueryKeyTypeName(node) {
85
- return `${capitalize(this.resolveName(node.operationId))}SuspenseQueryKey`;
86
- },
87
- resolveInfiniteQueryKeyTypeName(node) {
88
- return `${capitalize(this.resolveName(node.operationId))}InfiniteQueryKey`;
89
- },
90
- resolveSuspenseInfiniteQueryKeyTypeName(node) {
91
- return `${capitalize(this.resolveName(node.operationId))}SuspenseInfiniteQueryKey`;
92
- },
93
- resolveMutationTypeName(node) {
94
- return capitalize(this.resolveName(node.operationId));
95
- },
96
- resolveClientName(node) {
97
- return this.resolveName(node.operationId);
98
- },
99
- resolveSuspenseClientName(node) {
100
- return `${this.resolveName(node.operationId)}Suspense`;
101
- },
102
- resolveInfiniteClientName(node) {
103
- return `${this.resolveName(node.operationId)}Infinite`;
104
- },
105
- resolveSuspenseInfiniteClientName(node) {
106
- return `${this.resolveName(node.operationId)}SuspenseInfinite`;
107
- },
108
- resolveHookOptionsName() {
109
- return "HookOptions";
2199
+ query: createQueryResolver(),
2200
+ suspenseQuery: createQueryResolver("Suspense"),
2201
+ infiniteQuery: createQueryResolver("Infinite"),
2202
+ suspenseInfiniteQuery: createQueryResolver("SuspenseInfinite"),
2203
+ mutation: {
2204
+ ...createMutationResolver(),
2205
+ optionsName(node) {
2206
+ return `${this.name(node.operationId)}MutationOptions`;
2207
+ },
2208
+ typeName(node) {
2209
+ return capitalize(this.name(node.operationId));
2210
+ }
110
2211
  },
111
- resolveCustomHookOptionsName() {
112
- return "getCustomHookOptions";
2212
+ hook: {
2213
+ optionsName() {
2214
+ return "HookOptions";
2215
+ },
2216
+ customOptionsName() {
2217
+ return "getCustomHookOptions";
2218
+ }
113
2219
  }
114
- }));
2220
+ });
115
2221
  //#endregion
116
2222
  //#region src/plugin.ts
2223
+ /**
2224
+ * Canonical plugin name for `@kubb/plugin-react-query`. Used for driver lookups
2225
+ * and cross-plugin dependency references.
2226
+ */
117
2227
  const pluginReactQueryName = "plugin-react-query";
2228
+ /**
2229
+ * Generates one TanStack Query hook per OpenAPI operation for React. Queries
2230
+ * become `useFoo`, with `useFooSuspense` and `useFooInfinite` variants.
2231
+ * Mutations become `useFoo`. Each hook is fully typed: query keys, input
2232
+ * variables, response data, and error shape all come from the spec.
2233
+ *
2234
+ * @example
2235
+ * ```ts
2236
+ * import { defineConfig } from 'kubb/config'
2237
+ * import { pluginTs } from '@kubb/plugin-ts'
2238
+ * import { pluginReactQuery } from '@kubb/plugin-react-query'
2239
+ *
2240
+ * export default defineConfig({
2241
+ * input: './petStore.yaml',
2242
+ * output: { path: './src/gen' },
2243
+ * plugins: [
2244
+ * pluginTs(),
2245
+ * pluginReactQuery({
2246
+ * output: { path: './hooks' },
2247
+ * suspense: {},
2248
+ * }),
2249
+ * ],
2250
+ * })
2251
+ * ```
2252
+ */
118
2253
  const pluginReactQuery = definePlugin((options) => {
119
2254
  const { output = {
120
2255
  path: "hooks",
121
- barrelType: "named"
122
- }, group, exclude = [], include, override = [], parser = "client", suspense = {}, infinite = false, paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, customOptions, paramsCasing, client, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
123
- const clientName = client?.client ?? "axios";
124
- const clientImportPath = client?.importPath ?? (!client?.bundle ? `@kubb/plugin-client/clients/${clientName}` : void 0);
125
- const selectedGenerators = options.generators ?? [
2256
+ barrel: { type: "named" }
2257
+ }, group, exclude = [], include, override = [], suspense = false, infinite = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, customOptions, hooks = false, client, resolver: userResolver, macros: userMacros } = options;
2258
+ const selectedGenerators = [
126
2259
  queryGenerator,
127
2260
  suspenseQueryGenerator,
128
2261
  infiniteQueryGenerator,
@@ -130,68 +2263,31 @@ const pluginReactQuery = definePlugin((options) => {
130
2263
  mutationGenerator,
131
2264
  hookOptionsGenerator,
132
2265
  customHookOptionsFileGenerator
133
- ].filter((generator) => Boolean(generator));
134
- const groupConfig = group ? {
135
- ...group,
136
- name: group.name ? group.name : (ctx) => {
137
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
138
- return `${camelCase(ctx.group)}Controller`;
139
- }
140
- } : void 0;
2266
+ ];
2267
+ const groupConfig = createGroupConfig(group);
141
2268
  return {
142
2269
  name: pluginReactQueryName,
143
2270
  options,
144
- dependencies: [pluginTsName, parser === "zod" ? pluginZodName : void 0].filter((dependency) => Boolean(dependency)),
2271
+ dependencies: [pluginTsName],
145
2272
  hooks: { "kubb:plugin:setup"(ctx) {
146
- const resolver = userResolver ? {
147
- ...resolverReactQuery,
148
- ...userResolver
149
- } : resolverReactQuery;
2273
+ const resolver = userResolver ? Resolver.merge(resolverReactQuery, userResolver) : resolverReactQuery;
150
2274
  ctx.setOptions({
151
2275
  output,
152
- client: {
153
- bundle: client?.bundle,
154
- baseURL: client?.baseURL,
155
- client: clientName,
156
- clientType: client?.clientType ?? "function",
157
- importPath: clientImportPath,
158
- dataReturnType: client?.dataReturnType ?? "data",
159
- paramsCasing
160
- },
2276
+ client: resolveContractClient({
2277
+ client,
2278
+ plugins: ctx.config.plugins
2279
+ }),
161
2280
  queryKey,
162
- query: query === false ? false : {
163
- importPath: "@tanstack/react-query",
164
- methods: ["get"],
165
- ...query
166
- },
2281
+ query: resolveQueryConfig(query, { importPath: "@tanstack/react-query" }),
167
2282
  mutationKey,
168
- mutation: mutation === false ? false : {
169
- importPath: "@tanstack/react-query",
170
- methods: [
171
- "post",
172
- "put",
173
- "patch",
174
- "delete"
175
- ],
176
- ...mutation
177
- },
178
- infinite: infinite ? {
179
- queryParam: "id",
180
- initialPageParam: 0,
181
- cursorParam: void 0,
182
- nextParam: void 0,
183
- previousParam: void 0,
184
- ...infinite
185
- } : false,
2283
+ mutation: resolveMutationConfig(mutation, { importPath: "@tanstack/react-query" }),
2284
+ infinite: resolveInfiniteConfig(infinite),
186
2285
  suspense,
187
2286
  customOptions: customOptions ? {
188
2287
  name: "useCustomHookOptions",
189
2288
  ...customOptions
190
- } : void 0,
191
- parser,
192
- paramsType,
193
- pathParamsType,
194
- paramsCasing,
2289
+ } : null,
2290
+ hooks,
195
2291
  group: groupConfig,
196
2292
  exclude,
197
2293
  include,
@@ -199,35 +2295,12 @@ const pluginReactQuery = definePlugin((options) => {
199
2295
  resolver
200
2296
  });
201
2297
  ctx.setResolver(resolver);
202
- if (userTransformer) ctx.setTransformer(userTransformer);
203
- for (const gen of selectedGenerators) ctx.addGenerator(gen);
204
- for (const gen of userGenerators) ctx.addGenerator(gen);
205
- const root = path.resolve(ctx.config.root, ctx.config.output.path);
206
- const hasClientPlugin = !!ctx.config.plugins?.some((p) => p.name === pluginClientName);
207
- if (client?.bundle && !hasClientPlugin && !clientImportPath) ctx.injectFile({
208
- baseName: "fetch.ts",
209
- path: path.resolve(root, ".kubb/fetch.ts"),
210
- sources: [ast.createSource({
211
- name: "fetch",
212
- nodes: [ast.createText(clientName === "fetch" ? source$1 : source)],
213
- isExportable: true,
214
- isIndexable: true
215
- })]
216
- });
217
- if (!hasClientPlugin) ctx.injectFile({
218
- baseName: "config.ts",
219
- path: path.resolve(root, ".kubb/config.ts"),
220
- sources: [ast.createSource({
221
- name: "config",
222
- nodes: [ast.createText(source$2)],
223
- isExportable: false,
224
- isIndexable: false
225
- })]
226
- });
2298
+ if (userMacros?.length) ctx.setMacros(userMacros);
2299
+ ctx.addGenerator(...selectedGenerators);
227
2300
  } }
228
2301
  };
229
2302
  });
230
2303
  //#endregion
231
- export { pluginReactQuery as default, pluginReactQuery, pluginReactQueryName };
2304
+ export { pluginReactQuery as default, pluginReactQuery, pluginReactQueryName, resolverReactQuery };
232
2305
 
233
2306
  //# sourceMappingURL=index.js.map