@kubb/plugin-react-query 5.0.0-beta.98 → 5.0.0

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.
package/dist/index.cjs CHANGED
@@ -19,337 +19,33 @@ var __copyProps = (to, from, except, desc) => {
19
19
  }
20
20
  return to;
21
21
  };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
23
23
  value: mod,
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
+ let kubb_kit = require("kubb/kit");
27
28
  let node_path = require("node:path");
28
29
  node_path = __toESM(node_path, 1);
29
- let kubb_kit = require("kubb/kit");
30
30
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
31
31
  let kubb_jsx = require("kubb/jsx");
32
32
  let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
33
33
  let node_fs = require("node:fs");
34
34
  node_fs = __toESM(node_fs, 1);
35
- //#region ../../internals/utils/src/casing.ts
36
- /**
37
- * Shared implementation for camelCase and PascalCase conversion.
38
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
39
- * and capitalizes each word according to `pascal`.
40
- *
41
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
42
- */
43
- function toCamelOrPascal(text, pascal) {
44
- 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) => {
45
- if (word.length > 1 && word === word.toUpperCase()) return word;
46
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
47
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
48
- }
49
- /**
50
- * Converts `text` to camelCase.
51
- *
52
- * @example Word boundaries
53
- * `camelCase('hello-world') // 'helloWorld'`
54
- *
55
- * @example With a prefix
56
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
57
- */
58
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
59
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
60
- }
61
- /**
62
- * Uppercases only the first character of `text`, leaving the rest untouched.
63
- * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
64
- *
65
- * @example
66
- * `capitalize('getPetById') // 'GetPetById'`
67
- */
68
- function capitalize(text) {
69
- return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
70
- }
71
- //#endregion
72
- //#region ../../internals/utils/src/reserved.ts
73
- /**
74
- * JavaScript and Java reserved words.
75
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
76
- */
77
- const reservedWords = /* @__PURE__ */ new Set([
78
- "abstract",
79
- "arguments",
80
- "boolean",
81
- "break",
82
- "byte",
83
- "case",
84
- "catch",
85
- "char",
86
- "class",
87
- "const",
88
- "continue",
89
- "debugger",
90
- "default",
91
- "delete",
92
- "do",
93
- "double",
94
- "else",
95
- "enum",
96
- "eval",
97
- "export",
98
- "extends",
99
- "false",
100
- "final",
101
- "finally",
102
- "float",
103
- "for",
104
- "function",
105
- "goto",
106
- "if",
107
- "implements",
108
- "import",
109
- "in",
110
- "instanceof",
111
- "int",
112
- "interface",
113
- "let",
114
- "long",
115
- "native",
116
- "new",
117
- "null",
118
- "package",
119
- "private",
120
- "protected",
121
- "public",
122
- "return",
123
- "short",
124
- "static",
125
- "super",
126
- "switch",
127
- "synchronized",
128
- "this",
129
- "throw",
130
- "throws",
131
- "transient",
132
- "true",
133
- "try",
134
- "typeof",
135
- "var",
136
- "void",
137
- "volatile",
138
- "while",
139
- "with",
140
- "yield",
141
- "Array",
142
- "Date",
143
- "hasOwnProperty",
144
- "Infinity",
145
- "isFinite",
146
- "isNaN",
147
- "isPrototypeOf",
148
- "length",
149
- "Math",
150
- "name",
151
- "NaN",
152
- "Number",
153
- "Object",
154
- "prototype",
155
- "String",
156
- "toString",
157
- "undefined",
158
- "valueOf"
159
- ]);
160
- /**
161
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
162
- *
163
- * @example
164
- * ```ts
165
- * isValidVarName('status') // true
166
- * isValidVarName('class') // false (reserved word)
167
- * isValidVarName('42foo') // false (starts with digit)
168
- * ```
169
- */
170
- function isValidVarName(name) {
171
- if (!name || reservedWords.has(name)) return false;
172
- return isIdentifier(name);
173
- }
174
- /**
175
- * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
176
- *
177
- * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
178
- * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
179
- * deciding whether an object key needs quoting.
180
- *
181
- * @example
182
- * ```ts
183
- * isIdentifier('name') // true
184
- * isIdentifier('x-total')// false
185
- * ```
186
- */
187
- function isIdentifier(name) {
188
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
189
- }
190
- //#endregion
191
- //#region ../../internals/utils/src/strings.ts
192
- /**
193
- * Renders a dotted path or string array as an optional-chaining accessor expression rooted at
194
- * `accessor`. Returns `null` for an empty path.
195
- *
196
- * @example
197
- * ```ts
198
- * getNestedAccessor('pagination.next.id', 'lastPage')
199
- * // "lastPage?.['pagination']?.['next']?.['id']"
200
- * ```
201
- */
202
- function getNestedAccessor(param, accessor) {
203
- const parts = Array.isArray(param) ? param : param.split(".");
204
- if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
205
- return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
206
- }
207
- //#endregion
208
- //#region ../../internals/utils/src/url.ts
209
- function transformParam(raw, casing) {
210
- const param = isValidVarName(raw) ? raw : camelCase(raw);
211
- return casing === "camelcase" ? camelCase(param) : param;
212
- }
213
- function toParamsObject(path, { replacer, casing } = {}) {
214
- const params = {};
215
- for (const match of path.matchAll(/\{([^}]+)\}/g)) {
216
- const param = transformParam(match[1], casing);
217
- const key = replacer ? replacer(param) : param;
218
- params[key] = key;
219
- }
220
- return Object.keys(params).length > 0 ? params : null;
221
- }
222
- /**
223
- * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
224
- */
225
- var Url = class Url {
226
- /**
227
- * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
228
- *
229
- * @example
230
- * Url.canParse('https://petstore.swagger.io/v2') // true
231
- * Url.canParse('/pet/{petId}') // false
232
- */
233
- static canParse(url, base) {
234
- return URL.canParse(url, base);
235
- }
236
- /**
237
- * Converts an OpenAPI/Swagger path to Express-style colon syntax.
238
- *
239
- * @example
240
- * Url.toPath('/pet/{petId}') // '/pet/:petId'
241
- */
242
- static toPath(path) {
243
- return path.replace(/\{([^}]+)\}/g, ":$1");
244
- }
245
- /**
246
- * Rewrites OpenAPI placeholder names while keeping the `{...}` braces, so the generated `url`
247
- * literal aligns with the grouped `path` request option that the runtime client interpolates by
248
- * key.
249
- *
250
- * @example
251
- * Url.toCasedTemplate('/projects/{project_id}', { casing: 'camelcase' }) // '/projects/{projectId}'
252
- */
253
- static toCasedTemplate(path, { casing } = {}) {
254
- return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name, casing)}}`);
255
- }
256
- /**
257
- * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
258
- * `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
259
- * and `casing` controls parameter identifier casing.
260
- *
261
- * @example
262
- * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
263
- *
264
- * @example
265
- * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
266
- */
267
- static toTemplateString(path, { prefix, replacer, casing } = {}) {
268
- const result = path.split(/\{([^}]+)\}/).map((part, i) => {
269
- if (i % 2 === 0) return part;
270
- const param = transformParam(part, casing);
271
- return `\${${replacer ? replacer(param) : param}}`;
272
- }).join("");
273
- return `\`${prefix ?? ""}${result}\``;
274
- }
275
- /**
276
- * Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
277
- * grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
278
- * names are camelCased to match the generated `path` type, and `prefix` is prepended inside the
279
- * literal. Shared by the client and cypress generators that pass a grouped `path` object.
280
- *
281
- * @example
282
- * Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
283
- */
284
- static toGroupedTemplateString(path, { prefix } = {}) {
285
- return Url.toTemplateString(path, {
286
- prefix,
287
- casing: "camelcase",
288
- replacer: (name) => `path.${name}`
289
- });
290
- }
291
- /**
292
- * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
293
- * expression when `stringify` is set.
294
- *
295
- * @example
296
- * Url.toObject('/pet/{petId}')
297
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
298
- */
299
- static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
300
- const object = {
301
- url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
302
- replacer,
303
- casing
304
- }),
305
- params: toParamsObject(path, {
306
- replacer,
307
- casing
308
- })
309
- };
310
- if (stringify) {
311
- if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
312
- if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
313
- return `{ url: '${object.url}' }`;
314
- }
315
- return object;
316
- }
317
- };
318
- //#endregion
319
35
  //#region ../../internals/shared/src/params.ts
320
- const caseParamsCache = /* @__PURE__ */ new WeakMap();
321
- /**
322
- * Applies camelCase to parameter names and returns a new array without mutating the input.
323
- *
324
- * Run it before handing parameters to schema builders so output property keys get the right casing
325
- * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
326
- * original array is returned unchanged. Results are cached per input array.
327
- */
328
- function caseParams(params, casing) {
329
- if (!casing) return params;
330
- const cached = caseParamsCache.get(params);
331
- if (cached) return cached;
332
- const result = params.map((param) => ({
333
- ...param,
334
- name: camelCase(param.name)
335
- }));
336
- caseParamsCache.set(params, result);
337
- return result;
338
- }
339
36
  /**
340
- * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
37
+ * Drops parameters that share the same name, keeping the first.
341
38
  *
342
- * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
343
- * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
344
- * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
345
- * identity so the resulting group is collision-free regardless of the names each caller carries.
39
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
40
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
41
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
42
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
346
43
  */
347
- function dedupeByCasedName(params) {
44
+ function dedupeParams(params) {
348
45
  const seen = /* @__PURE__ */ new Set();
349
46
  return params.filter((param) => {
350
- const key = camelCase(param.name);
351
- if (seen.has(key)) return false;
352
- seen.add(key);
47
+ if (seen.has(param.name)) return false;
48
+ seen.add(param.name);
353
49
  return true;
354
50
  });
355
51
  }
@@ -374,11 +70,30 @@ function operationFileEntry(node, name, extname = ".ts") {
374
70
  path: node.path
375
71
  };
376
72
  }
73
+ /**
74
+ * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
75
+ * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
76
+ * same dependency for the same operation in one pass (a query plugin's several hook generators, the
77
+ * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
78
+ *
79
+ * @example Cache `plugin-ts`'s file for the current operation
80
+ * ```ts
81
+ * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
82
+ * ```
83
+ */
84
+ function resolveDependencyOperationFile(options) {
85
+ const { cache, node, resolver, root, output, group } = options;
86
+ return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
87
+ ...operationFileEntry(node, node.operationId),
88
+ root,
89
+ output,
90
+ group: group ?? void 0
91
+ }));
92
+ }
377
93
  function getOperationLink(node, link) {
378
94
  if (!link) return null;
379
95
  if (typeof link === "function") return link(node) ?? null;
380
- if (link === "urlPath") return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
381
- return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
96
+ return node.path ? `{@link ${kubb_kit.Url.toPath(node.path)}}` : null;
382
97
  }
383
98
  /**
384
99
  * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
@@ -468,14 +183,24 @@ function buildOperationComments(node, options = {}) {
468
183
  if (!splitLines) return filteredComments;
469
184
  return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
470
185
  }
471
- function getOperationParameters(node, options = {}) {
472
- const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
473
- return {
474
- path: dedupeByCasedName(params.filter((param) => param.in === "path")),
475
- query: dedupeByCasedName(params.filter((param) => param.in === "query")),
476
- header: dedupeByCasedName(params.filter((param) => param.in === "header")),
477
- cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
186
+ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
187
+ /**
188
+ * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
189
+ * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
190
+ * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
191
+ * same parameters once per plugin.
192
+ */
193
+ function getOperationParameters(node) {
194
+ const cached = operationParameterGroupsByNode.get(node);
195
+ if (cached) return cached;
196
+ const groups = {
197
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
198
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
199
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
200
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
478
201
  };
202
+ operationParameterGroupsByNode.set(node, groups);
203
+ return groups;
479
204
  }
480
205
  function getStatusCodeNumber(statusCode) {
481
206
  const code = Number(statusCode);
@@ -509,7 +234,7 @@ function resolveStatusCodeNames(node, resolver) {
509
234
  }
510
235
  const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
511
236
  function resolveOperationTypeNames(node, resolver, options = {}) {
512
- const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
237
+ const cacheKey = `${node.operationId}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
513
238
  let byResolver = typeNamesByResolver.get(resolver);
514
239
  if (byResolver) {
515
240
  const cached = byResolver.get(cacheKey);
@@ -518,7 +243,7 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
518
243
  byResolver = /* @__PURE__ */ new Map();
519
244
  typeNamesByResolver.set(resolver, byResolver);
520
245
  }
521
- const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
246
+ const { path, query, header } = getOperationParameters(node);
522
247
  const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
523
248
  const exclude = new Set(options.exclude ?? []);
524
249
  const paramNames = options.includeParams === false ? [] : [
@@ -540,6 +265,60 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
540
265
  return result;
541
266
  }
542
267
  //#endregion
268
+ //#region ../../internals/utils/src/casing.ts
269
+ /**
270
+ * Shared implementation for camelCase and PascalCase conversion.
271
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
272
+ * and capitalizes each word according to `pascal`.
273
+ *
274
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
275
+ */
276
+ function toCamelOrPascal(text, pascal) {
277
+ 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) => {
278
+ if (word.length > 1 && word === word.toUpperCase()) return word;
279
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
280
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
281
+ }
282
+ /**
283
+ * Converts `text` to camelCase.
284
+ *
285
+ * @example Word boundaries
286
+ * `camelCase('hello-world') // 'helloWorld'`
287
+ *
288
+ * @example With a prefix
289
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
290
+ */
291
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
292
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
293
+ }
294
+ /**
295
+ * Uppercases only the first character of `text`, leaving the rest untouched.
296
+ * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
297
+ *
298
+ * @example
299
+ * `capitalize('getPetById') // 'GetPetById'`
300
+ */
301
+ function capitalize(text) {
302
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
303
+ }
304
+ //#endregion
305
+ //#region ../../internals/utils/src/strings.ts
306
+ /**
307
+ * Renders a dotted path or string array as an optional-chaining accessor expression rooted at
308
+ * `accessor`. Returns `null` for an empty path.
309
+ *
310
+ * @example
311
+ * ```ts
312
+ * getNestedAccessor('pagination.next.id', 'lastPage')
313
+ * // "lastPage?.['pagination']?.['next']?.['id']"
314
+ * ```
315
+ */
316
+ function getNestedAccessor(param, accessor) {
317
+ const parts = Array.isArray(param) ? param : param.split(".");
318
+ if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
319
+ return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
320
+ }
321
+ //#endregion
543
322
  //#region ../../internals/shared/src/group.ts
544
323
  /**
545
324
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
@@ -659,23 +438,30 @@ function resolveContractClient(options) {
659
438
  * (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
660
439
  * path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
661
440
  * read `RequestConfig` / `ResponseErrorConfig` from.
441
+ *
442
+ * The result is cached in the current node's `cache` (`ctx.cache`) under the client plugin's name,
443
+ * so several dependents reading the same client plugin for one operation in a single pass
444
+ * (react-query's query/mutation/infinite generators, vue-query, swr, the MCP handler, ...) share
445
+ * one computed result instead of each re-deriving the name and path.
662
446
  */
663
447
  function resolveClientOperation(options) {
664
- const { clientPlugin, driver, node, root, output } = options;
448
+ const { clientPlugin, driver, node, root, output, cache } = options;
665
449
  if (!clientPlugin) return null;
666
- const resolver = driver.getResolver(clientPlugin.pluginName);
667
- const plugin = driver.getPlugin(clientPlugin.pluginName);
668
- const file = resolver.file({
669
- ...operationFileEntry(node, node.operationId),
670
- root,
671
- output: plugin?.options?.output ?? output,
672
- group: plugin?.options?.group ?? void 0
450
+ return cache.ensureItem(`${clientPlugin.pluginName}:clientOperation`, () => {
451
+ const resolver = driver.getResolver(clientPlugin.pluginName);
452
+ const plugin = driver.getPlugin(clientPlugin.pluginName);
453
+ const file = resolver.file({
454
+ ...operationFileEntry(node, node.operationId),
455
+ root,
456
+ output: plugin?.options?.output ?? output,
457
+ group: plugin?.options?.group ?? void 0
458
+ });
459
+ return {
460
+ name: resolver.name(node.operationId),
461
+ path: file.path,
462
+ clientPath: node_path.default.resolve(root, ".kubb/client.ts")
463
+ };
673
464
  });
674
- return {
675
- name: resolver.name(node.operationId),
676
- path: file.path,
677
- clientPath: node_path.default.resolve(root, ".kubb/client.ts")
678
- };
679
465
  }
680
466
  //#endregion
681
467
  //#region ../../internals/tanstack-query/src/utils.ts
@@ -746,14 +532,16 @@ function buildGroupedRequestParam(node, options) {
746
532
  */
747
533
  function buildQueryOptionsParams(node, options) {
748
534
  const { resolver, memberTypeWrapper } = options;
749
- return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [buildGroupedRequestParam(node, {
535
+ const groupedParam = buildGroupedRequestParam(node, {
750
536
  resolver,
751
537
  memberTypeWrapper
752
- }), (0, _kubb_plugin_ts.createFunctionParameter)({
538
+ });
539
+ const configParam = (0, _kubb_plugin_ts.createFunctionParameter)({
753
540
  name: "config",
754
541
  type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
755
542
  default: "{}"
756
- })].filter((param) => param !== null) });
543
+ });
544
+ return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [groupedParam, configParam].filter((param) => param !== null) });
757
545
  }
758
546
  /**
759
547
  * Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
@@ -807,7 +595,7 @@ function resolveFallbackPageParamType(initialPageParam) {
807
595
  * so callers can reuse it when rewriting the paginated request.
808
596
  */
809
597
  function resolvePageParamType(node, { resolver, initialPageParam, queryParam }) {
810
- const firstQueryParam = getOperationParameters(node, { paramsCasing: "original" }).query[0];
598
+ const firstQueryParam = getOperationParameters(node).query[0];
811
599
  const groupName = firstQueryParam ? resolver.param.query(node, firstQueryParam) : null;
812
600
  const queryParamsTypeName = groupName !== (firstQueryParam ? resolver.param.name(node, firstQueryParam) : null) ? groupName : null;
813
601
  const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null;
@@ -851,6 +639,19 @@ function resolveMutationConfig(mutation, options) {
851
639
  };
852
640
  }
853
641
  /**
642
+ * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
643
+ * `query: false` still marks the operation as a query so the query-family generators keep matching it,
644
+ * and a method already claimed by `query` never counts as a mutation.
645
+ */
646
+ function classifyOperation(node, { query, mutation }) {
647
+ const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
648
+ const queryMethods = new Set(query ? query.methods : []);
649
+ return {
650
+ isQuery,
651
+ isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
652
+ };
653
+ }
654
+ /**
854
655
  * Applies the shared infinite-query defaults during plugin setup: a falsy value disables infinite
855
656
  * queries, and an object merges over `queryParam: 'id'` and `initialPageParam: 0` with the cursor
856
657
  * paths cleared.
@@ -985,7 +786,7 @@ return infiniteQueryOptions<${queryFnDataType}, ${errorType}, InfiniteData<${que
985
786
  const declarationPrinter$6 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
986
787
  const mutationKeyTransformer = ({ node }) => {
987
788
  if (!node.path) return [];
988
- return [`{ url: '${Url.toPath(node.path)}' }`];
789
+ return [`{ url: '${kubb_kit.Url.toPath(node.path)}' }`];
989
790
  };
990
791
  function MutationKey({ name, node, transformer }) {
991
792
  const paramsNode = (0, _kubb_plugin_ts.createFunctionParameters)({ params: [] });
@@ -1069,7 +870,7 @@ const queryKeyTransformer = ({ node }) => {
1069
870
  const hasQueryParams = getOperationParameters(node).query.length > 0;
1070
871
  const hasRequestBody = !!node.requestBody?.content?.[0]?.schema;
1071
872
  return [
1072
- hasPathParams ? `{ url: '${Url.toPath(node.path)}', params: path }` : `{ url: '${Url.toPath(node.path)}' }`,
873
+ hasPathParams ? `{ url: '${kubb_kit.Url.toPath(node.path)}', params: path }` : `{ url: '${kubb_kit.Url.toPath(node.path)}' }`,
1073
874
  hasQueryParams ? "...(query ? [query] : [])" : null,
1074
875
  hasRequestBody ? "...(body ? [body] : [])" : null
1075
876
  ].filter(Boolean);
@@ -1220,19 +1021,6 @@ function buildResolvedRequestParams(node) {
1220
1021
  if (names.length === 0) return null;
1221
1022
  return `{ ${names.map((name) => `${name}: typeof ${name} === 'function' ? ${name}() : ${name}`).join(", ")} }`;
1222
1023
  }
1223
- /**
1224
- * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
1225
- * `query: false` still marks the operation as a query so the query-family generators keep matching it,
1226
- * and a method already claimed by `query` never counts as a mutation.
1227
- */
1228
- function classifyOperation(node, { query, mutation }) {
1229
- const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
1230
- const queryMethods = new Set(query ? query.methods : []);
1231
- return {
1232
- isQuery,
1233
- isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
1234
- };
1235
- }
1236
1024
  //#endregion
1237
1025
  //#region src/generators/hookOptionsGenerator.tsx
1238
1026
  /**
@@ -1308,7 +1096,7 @@ const hookOptionsGenerator = (0, kubb_kit.defineGenerator)({
1308
1096
  }
1309
1097
  if (isInfiniteOp) {
1310
1098
  const normalizeKey = (key) => key.replace(/\?$/, "");
1311
- const queryParamKeys = getOperationParameters(node, { paramsCasing: "original" }).query.map((p) => p.name);
1099
+ const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1312
1100
  if (nodeInfiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === nodeInfiniteOptions.queryParam) : false) {
1313
1101
  const infiniteOptionsName = resolver.infiniteQuery.optionsName(node);
1314
1102
  const infiniteHookName = resolver.infiniteQuery.name(node);
@@ -1407,10 +1195,11 @@ function buildInfiniteQueryParamsNode(node, options) {
1407
1195
  }`,
1408
1196
  default: "{}"
1409
1197
  });
1410
- return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [buildGroupedRequestParam(node, {
1198
+ const groupedParam = buildGroupedRequestParam(node, {
1411
1199
  resolver,
1412
1200
  memberTypeWrapper: maybeValueOrGetter
1413
- }), optionsParam].filter((param) => param !== null) });
1201
+ });
1202
+ return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
1414
1203
  }
1415
1204
  function InfiniteQuery({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver, initialPageParam, queryParam, customOptions, suspense }) {
1416
1205
  const { TData: responseType, TError: errorType } = buildResponseTypes(node, tsResolver);
@@ -1523,15 +1312,16 @@ function resolveMutationRequestType(node, resolver) {
1523
1312
  function buildMutationParamsNode(node, options) {
1524
1313
  const { resolver } = options;
1525
1314
  const { TData, TError } = buildResponseTypes(node, resolver);
1315
+ const generics = [
1316
+ TData,
1317
+ TError,
1318
+ resolveMutationRequestType(node, resolver),
1319
+ "TContext"
1320
+ ].join(", ");
1526
1321
  return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
1527
1322
  name: "options",
1528
1323
  type: `{
1529
- mutation?: UseMutationOptions<${[
1530
- TData,
1531
- TError,
1532
- resolveMutationRequestType(node, resolver),
1533
- "TContext"
1534
- ].join(", ")}> & { client?: QueryClient },
1324
+ mutation?: UseMutationOptions<${generics}> & { client?: QueryClient },
1535
1325
  client?: ${buildRequestConfigType(node)},
1536
1326
  }`,
1537
1327
  default: "{}"
@@ -1589,10 +1379,11 @@ function buildQueryParamsNode(node, options) {
1589
1379
  }`,
1590
1380
  default: "{}"
1591
1381
  });
1592
- return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [buildGroupedRequestParam(node, {
1382
+ const groupedParam = buildGroupedRequestParam(node, {
1593
1383
  resolver,
1594
1384
  memberTypeWrapper: maybeValueOrGetter
1595
- }), optionsParam].filter((param) => param !== null) });
1385
+ });
1386
+ return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
1596
1387
  }
1597
1388
  function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver, customOptions, suspense }) {
1598
1389
  const { TData, TError } = buildResponseTypes(node, tsResolver);
@@ -1657,14 +1448,11 @@ function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, t
1657
1448
  //#region src/components/QueryOptions.tsx
1658
1449
  const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
1659
1450
  const callPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "call" });
1660
- function getQueryOptionsParams(node, options) {
1661
- return buildQueryOptionsParams(node, options);
1662
- }
1663
1451
  function QueryOptions({ name, clientName, node, tsResolver, queryKeyName }) {
1664
1452
  const { TData, TError } = buildResponseTypes(node, tsResolver);
1665
1453
  const queryKeyParamsNode = buildQueryKeyParams(node, { resolver: tsResolver });
1666
1454
  const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? "";
1667
- const paramsNode = getQueryOptionsParams(node, { resolver: tsResolver });
1455
+ const paramsNode = buildQueryOptionsParams(node, { resolver: tsResolver });
1668
1456
  const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1669
1457
  const queryFnBody = `const { data } = await ${buildClientCall(node, {
1670
1458
  clientName,
@@ -1695,36 +1483,44 @@ function QueryOptions({ name, clientName, node, tsResolver, queryKeyName }) {
1695
1483
  //#region src/generators/infiniteQueryGenerator.tsx
1696
1484
  /**
1697
1485
  * Built-in generator for `useInfiniteQuery` hooks. Enabled when
1698
- * `pluginReactQuery({ infinite: { ... } })`. Emits one `useFooInfiniteQuery`
1699
- * hook per query operation, wiring the configured `nextParam` /
1700
- * `previousParam` paths into TanStack Query's cursor-based pagination.
1486
+ * `pluginReactQuery({ infinite: { ... }, hooks: true })`. Emits one
1487
+ * `useFooInfiniteQuery` hook per query operation, wiring the configured
1488
+ * `nextParam` / `previousParam` paths into TanStack Query's cursor-based
1489
+ * pagination.
1701
1490
  */
1702
1491
  const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1703
1492
  name: "react-infinite-query",
1704
1493
  renderer: kubb_jsx.jsxRenderer,
1494
+ match(node, ctx) {
1495
+ const operationNode = node;
1496
+ if (!kubb_kit.ast.isHttpOperationNode(operationNode)) return false;
1497
+ const { query, mutation, infinite, hooks } = ctx.options;
1498
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1499
+ query,
1500
+ mutation
1501
+ });
1502
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1503
+ if (!isQuery || isMutation || !infiniteOptions || !hooks) return false;
1504
+ const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
1505
+ return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
1506
+ },
1705
1507
  operation(node, ctx) {
1706
1508
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
1707
1509
  const { config, driver, resolver, root } = ctx;
1708
- const { output, query, mutation, infinite, client, group, customOptions, hooks } = ctx.options;
1510
+ const { output, query, infinite, client, group, customOptions } = ctx.options;
1709
1511
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1710
1512
  if (!pluginTs) return null;
1711
1513
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1712
- const { isQuery, isMutation } = classifyOperation(node, {
1713
- query,
1714
- mutation
1715
- });
1716
1514
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1717
- if (!isQuery || isMutation || !infiniteOptions) return null;
1718
- const normalizeKey = (key) => key.replace(/\?$/, "");
1719
- const queryParamKeys = getOperationParameters(node, { paramsCasing: "original" }).query.map((p) => p.name);
1720
- if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1515
+ if (!infiniteOptions) return null;
1721
1516
  const importPath = query ? query.importPath : "@tanstack/react-query";
1722
1517
  const contractOp = resolveClientOperation({
1723
1518
  clientPlugin: { pluginName: client.pluginName },
1724
1519
  driver,
1725
1520
  node,
1726
1521
  root,
1727
- output
1522
+ output,
1523
+ cache: ctx.cache
1728
1524
  });
1729
1525
  if (!contractOp) return null;
1730
1526
  const queryName = resolver.infiniteQuery.name(node);
@@ -1738,15 +1534,20 @@ const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1738
1534
  output,
1739
1535
  group: group ?? void 0
1740
1536
  }),
1741
- fileTs: tsResolver.file({
1742
- ...operationFileEntry(node, node.operationId),
1537
+ fileTs: resolveDependencyOperationFile({
1538
+ cache: ctx.cache,
1539
+ node,
1540
+ resolver: tsResolver,
1743
1541
  root,
1744
1542
  output: pluginTs.options?.output ?? output,
1745
- group: pluginTs.options?.group ?? void 0
1543
+ group: pluginTs.options?.group
1746
1544
  })
1747
1545
  };
1748
- const rawQueryParams = getOperationParameters(node, { paramsCasing: "original" }).query;
1749
- const queryParamsTypeName = rawQueryParams.length > 0 && tsResolver.param.query(node, rawQueryParams[0]) !== tsResolver.param.name(node, rawQueryParams[0]) ? tsResolver.param.query(node, rawQueryParams[0]) : null;
1546
+ const { queryParamsTypeName } = resolvePageParamType(node, {
1547
+ resolver: tsResolver,
1548
+ initialPageParam: infiniteOptions.initialPageParam,
1549
+ queryParam: infiniteOptions.queryParam
1550
+ });
1750
1551
  const importedTypeNames = [
1751
1552
  tsResolver.response.options(node),
1752
1553
  queryParamsTypeName,
@@ -1827,33 +1628,31 @@ const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1827
1628
  initialPageParam: infiniteOptions.initialPageParam,
1828
1629
  queryParam: infiniteOptions.queryParam
1829
1630
  }),
1830
- hooks && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [
1831
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1832
- name: ["useInfiniteQuery"],
1833
- path: importPath
1834
- }),
1835
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1836
- name: [
1837
- "QueryKey",
1838
- "QueryClient",
1839
- "InfiniteQueryObserverOptions",
1840
- "UseInfiniteQueryResult"
1841
- ],
1842
- path: importPath,
1843
- isTypeOnly: true
1844
- }),
1845
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(InfiniteQuery, {
1846
- name: queryName,
1847
- queryOptionsName,
1848
- queryKeyName,
1849
- queryKeyTypeName,
1850
- node,
1851
- tsResolver,
1852
- initialPageParam: infiniteOptions.initialPageParam,
1853
- queryParam: infiniteOptions.queryParam,
1854
- customOptions
1855
- })
1856
- ] })
1631
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1632
+ name: ["useInfiniteQuery"],
1633
+ path: importPath
1634
+ }),
1635
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
1636
+ name: [
1637
+ "QueryKey",
1638
+ "QueryClient",
1639
+ "InfiniteQueryObserverOptions",
1640
+ "UseInfiniteQueryResult"
1641
+ ],
1642
+ path: importPath,
1643
+ isTypeOnly: true
1644
+ }),
1645
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(InfiniteQuery, {
1646
+ name: queryName,
1647
+ queryOptionsName,
1648
+ queryKeyName,
1649
+ queryKeyTypeName,
1650
+ node,
1651
+ tsResolver,
1652
+ initialPageParam: infiniteOptions.initialPageParam,
1653
+ queryParam: infiniteOptions.queryParam,
1654
+ customOptions
1655
+ })
1857
1656
  ]
1858
1657
  });
1859
1658
  }
@@ -1868,25 +1667,30 @@ const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1868
1667
  const mutationGenerator = (0, kubb_kit.defineGenerator)({
1869
1668
  name: "react-query-mutation",
1870
1669
  renderer: kubb_jsx.jsxRenderer,
1670
+ match(node, ctx) {
1671
+ const operationNode = node;
1672
+ if (!kubb_kit.ast.isHttpOperationNode(operationNode)) return false;
1673
+ const { query, mutation } = ctx.options;
1674
+ return classifyOperation(operationNode, {
1675
+ query,
1676
+ mutation
1677
+ }).isMutation;
1678
+ },
1871
1679
  operation(node, ctx) {
1872
1680
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
1873
1681
  const { config, driver, resolver, root } = ctx;
1874
- const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1682
+ const { output, mutation, client, group, customOptions, hooks } = ctx.options;
1875
1683
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1876
1684
  if (!pluginTs) return null;
1877
1685
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1878
- const { isMutation } = classifyOperation(node, {
1879
- query,
1880
- mutation
1881
- });
1882
- if (!isMutation) return null;
1883
1686
  const importPath = mutation ? mutation.importPath : "@tanstack/react-query";
1884
1687
  const contractOp = resolveClientOperation({
1885
1688
  clientPlugin: { pluginName: client.pluginName },
1886
1689
  driver,
1887
1690
  node,
1888
1691
  root,
1889
- output
1692
+ output,
1693
+ cache: ctx.cache
1890
1694
  });
1891
1695
  if (!contractOp) return null;
1892
1696
  const mutationHookName = resolver.mutation.name(node);
@@ -1900,11 +1704,13 @@ const mutationGenerator = (0, kubb_kit.defineGenerator)({
1900
1704
  output,
1901
1705
  group: group ?? void 0
1902
1706
  }),
1903
- fileTs: tsResolver.file({
1904
- ...operationFileEntry(node, node.operationId),
1707
+ fileTs: resolveDependencyOperationFile({
1708
+ cache: ctx.cache,
1709
+ node,
1710
+ resolver: tsResolver,
1905
1711
  root,
1906
1712
  output: pluginTs.options?.output ?? output,
1907
- group: pluginTs.options?.group ?? void 0
1713
+ group: pluginTs.options?.group
1908
1714
  })
1909
1715
  };
1910
1716
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -2008,25 +1814,31 @@ const mutationGenerator = (0, kubb_kit.defineGenerator)({
2008
1814
  const queryGenerator = (0, kubb_kit.defineGenerator)({
2009
1815
  name: "react-query",
2010
1816
  renderer: kubb_jsx.jsxRenderer,
1817
+ match(node, ctx) {
1818
+ const operationNode = node;
1819
+ if (!kubb_kit.ast.isHttpOperationNode(operationNode)) return false;
1820
+ const { query, mutation } = ctx.options;
1821
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1822
+ query,
1823
+ mutation
1824
+ });
1825
+ return isQuery && !isMutation;
1826
+ },
2011
1827
  operation(node, ctx) {
2012
1828
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
2013
1829
  const { config, driver, resolver, root } = ctx;
2014
- const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1830
+ const { output, query, client, group, customOptions, hooks } = ctx.options;
2015
1831
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
2016
1832
  if (!pluginTs) return null;
2017
1833
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
2018
- const { isQuery, isMutation } = classifyOperation(node, {
2019
- query,
2020
- mutation
2021
- });
2022
- if (!isQuery || isMutation) return null;
2023
1834
  const importPath = query ? query.importPath : "@tanstack/react-query";
2024
1835
  const contractOp = resolveClientOperation({
2025
1836
  clientPlugin: { pluginName: client.pluginName },
2026
1837
  driver,
2027
1838
  node,
2028
1839
  root,
2029
- output
1840
+ output,
1841
+ cache: ctx.cache
2030
1842
  });
2031
1843
  if (!contractOp) return null;
2032
1844
  const queryName = resolver.query.name(node);
@@ -2040,11 +1852,13 @@ const queryGenerator = (0, kubb_kit.defineGenerator)({
2040
1852
  output,
2041
1853
  group: group ?? void 0
2042
1854
  }),
2043
- fileTs: tsResolver.file({
2044
- ...operationFileEntry(node, node.operationId),
1855
+ fileTs: resolveDependencyOperationFile({
1856
+ cache: ctx.cache,
1857
+ node,
1858
+ resolver: tsResolver,
2045
1859
  root,
2046
1860
  output: pluginTs.options?.output ?? output,
2047
- group: pluginTs.options?.group ?? void 0
1861
+ group: pluginTs.options?.group
2048
1862
  })
2049
1863
  };
2050
1864
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -2145,38 +1959,44 @@ const queryGenerator = (0, kubb_kit.defineGenerator)({
2145
1959
  //#endregion
2146
1960
  //#region src/generators/suspenseInfiniteQueryGenerator.tsx
2147
1961
  /**
2148
- * Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when both
2149
- * `suspense` and `infinite` are configured. Combines suspense semantics with
2150
- * cursor-based pagination. Handlers throw promises while loading and pull
2151
- * additional pages on demand.
1962
+ * Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when
1963
+ * `suspense`, `infinite`, and `hooks` are all configured. Combines suspense
1964
+ * semantics with cursor-based pagination. Handlers throw promises while
1965
+ * loading and pull additional pages on demand.
2152
1966
  */
2153
1967
  const suspenseInfiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
2154
1968
  name: "react-suspense-infinite-query",
2155
1969
  renderer: kubb_jsx.jsxRenderer,
1970
+ match(node, ctx) {
1971
+ const operationNode = node;
1972
+ if (!kubb_kit.ast.isHttpOperationNode(operationNode)) return false;
1973
+ const { query, mutation, infinite, suspense, hooks } = ctx.options;
1974
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1975
+ query,
1976
+ mutation
1977
+ });
1978
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1979
+ if (!isQuery || isMutation || !suspense || !infiniteOptions || !hooks) return false;
1980
+ const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
1981
+ return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
1982
+ },
2156
1983
  operation(node, ctx) {
2157
1984
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
2158
1985
  const { config, driver, resolver, root } = ctx;
2159
- const { output, query, mutation, infinite, suspense, client, group, customOptions, hooks } = ctx.options;
1986
+ const { output, query, infinite, client, group, customOptions } = ctx.options;
2160
1987
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
2161
1988
  if (!pluginTs) return null;
2162
1989
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
2163
- const { isQuery, isMutation } = classifyOperation(node, {
2164
- query,
2165
- mutation
2166
- });
2167
- const isSuspense = !!suspense;
2168
1990
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
2169
- if (!isQuery || isMutation || !isSuspense || !infiniteOptions) return null;
2170
- const normalizeKey = (key) => key.replace(/\?$/, "");
2171
- const queryParamKeys = getOperationParameters(node, { paramsCasing: "original" }).query.map((p) => p.name);
2172
- if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1991
+ if (!infiniteOptions) return null;
2173
1992
  const importPath = query ? query.importPath : "@tanstack/react-query";
2174
1993
  const contractOp = resolveClientOperation({
2175
1994
  clientPlugin: { pluginName: client.pluginName },
2176
1995
  driver,
2177
1996
  node,
2178
1997
  root,
2179
- output
1998
+ output,
1999
+ cache: ctx.cache
2180
2000
  });
2181
2001
  if (!contractOp) return null;
2182
2002
  const queryName = resolver.suspenseInfiniteQuery.name(node);
@@ -2190,14 +2010,16 @@ const suspenseInfiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
2190
2010
  output,
2191
2011
  group: group ?? void 0
2192
2012
  }),
2193
- fileTs: tsResolver.file({
2194
- ...operationFileEntry(node, node.operationId),
2013
+ fileTs: resolveDependencyOperationFile({
2014
+ cache: ctx.cache,
2015
+ node,
2016
+ resolver: tsResolver,
2195
2017
  root,
2196
2018
  output: pluginTs.options?.output ?? output,
2197
- group: pluginTs.options?.group ?? void 0
2019
+ group: pluginTs.options?.group
2198
2020
  })
2199
2021
  };
2200
- const rawQueryParams = getOperationParameters(node, { paramsCasing: "original" }).query;
2022
+ const rawQueryParams = getOperationParameters(node).query;
2201
2023
  const queryParamsTypeName = rawQueryParams.length > 0 && tsResolver.param.query(node, rawQueryParams[0]) !== tsResolver.param.name(node, rawQueryParams[0]) ? tsResolver.param.query(node, rawQueryParams[0]) : null;
2202
2024
  const importedTypeNames = [
2203
2025
  tsResolver.response.options(node),
@@ -2278,34 +2100,32 @@ const suspenseInfiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
2278
2100
  initialPageParam: infiniteOptions.initialPageParam,
2279
2101
  queryParam: infiniteOptions.queryParam
2280
2102
  }),
2281
- hooks && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [
2282
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2283
- name: ["useSuspenseInfiniteQuery"],
2284
- path: importPath
2285
- }),
2286
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2287
- name: [
2288
- "QueryKey",
2289
- "QueryClient",
2290
- "UseSuspenseInfiniteQueryOptions",
2291
- "UseSuspenseInfiniteQueryResult"
2292
- ],
2293
- path: importPath,
2294
- isTypeOnly: true
2295
- }),
2296
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(InfiniteQuery, {
2297
- name: queryName,
2298
- queryOptionsName,
2299
- queryKeyName,
2300
- queryKeyTypeName,
2301
- node,
2302
- tsResolver,
2303
- initialPageParam: infiniteOptions.initialPageParam,
2304
- queryParam: infiniteOptions.queryParam,
2305
- customOptions,
2306
- suspense: true
2307
- })
2308
- ] })
2103
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2104
+ name: ["useSuspenseInfiniteQuery"],
2105
+ path: importPath
2106
+ }),
2107
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2108
+ name: [
2109
+ "QueryKey",
2110
+ "QueryClient",
2111
+ "UseSuspenseInfiniteQueryOptions",
2112
+ "UseSuspenseInfiniteQueryResult"
2113
+ ],
2114
+ path: importPath,
2115
+ isTypeOnly: true
2116
+ }),
2117
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(InfiniteQuery, {
2118
+ name: queryName,
2119
+ queryOptionsName,
2120
+ queryKeyName,
2121
+ queryKeyTypeName,
2122
+ node,
2123
+ tsResolver,
2124
+ initialPageParam: infiniteOptions.initialPageParam,
2125
+ queryParam: infiniteOptions.queryParam,
2126
+ customOptions,
2127
+ suspense: true
2128
+ })
2309
2129
  ]
2310
2130
  });
2311
2131
  }
@@ -2314,32 +2134,39 @@ const suspenseInfiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
2314
2134
  //#region src/generators/suspenseQueryGenerator.tsx
2315
2135
  /**
2316
2136
  * Built-in generator for `useSuspenseQuery` hooks. Enabled when
2317
- * `pluginReactQuery({ suspense: {} })`. Emits one `useFooSuspenseQuery` hook
2318
- * per query operation. Suspense queries throw promises while loading and
2319
- * require a `<Suspense>` boundary in the React tree. TanStack Query v5+ only.
2137
+ * `pluginReactQuery({ suspense: {}, hooks: true })`. Emits one
2138
+ * `useFooSuspenseQuery` hook per query operation. Suspense queries throw
2139
+ * promises while loading and require a `<Suspense>` boundary in the React
2140
+ * tree. TanStack Query v5+ only.
2320
2141
  */
2321
2142
  const suspenseQueryGenerator = (0, kubb_kit.defineGenerator)({
2322
2143
  name: "react-suspense-query",
2323
2144
  renderer: kubb_jsx.jsxRenderer,
2145
+ match(node, ctx) {
2146
+ const operationNode = node;
2147
+ if (!kubb_kit.ast.isHttpOperationNode(operationNode)) return false;
2148
+ const { query, mutation, suspense, hooks } = ctx.options;
2149
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
2150
+ query,
2151
+ mutation
2152
+ });
2153
+ return isQuery && !isMutation && !!suspense && hooks;
2154
+ },
2324
2155
  operation(node, ctx) {
2325
2156
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
2326
2157
  const { config, driver, resolver, root } = ctx;
2327
- const { output, query, mutation, suspense, client, group, customOptions, hooks } = ctx.options;
2158
+ const { output, query, client, group, customOptions } = ctx.options;
2328
2159
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
2329
2160
  if (!pluginTs) return null;
2330
2161
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
2331
- const { isQuery, isMutation } = classifyOperation(node, {
2332
- query,
2333
- mutation
2334
- });
2335
- if (!isQuery || isMutation || !!!suspense) return null;
2336
2162
  const importPath = query ? query.importPath : "@tanstack/react-query";
2337
2163
  const contractOp = resolveClientOperation({
2338
2164
  clientPlugin: { pluginName: client.pluginName },
2339
2165
  driver,
2340
2166
  node,
2341
2167
  root,
2342
- output
2168
+ output,
2169
+ cache: ctx.cache
2343
2170
  });
2344
2171
  if (!contractOp) return null;
2345
2172
  const queryName = resolver.suspenseQuery.name(node);
@@ -2353,11 +2180,13 @@ const suspenseQueryGenerator = (0, kubb_kit.defineGenerator)({
2353
2180
  output,
2354
2181
  group: group ?? void 0
2355
2182
  }),
2356
- fileTs: tsResolver.file({
2357
- ...operationFileEntry(node, node.operationId),
2183
+ fileTs: resolveDependencyOperationFile({
2184
+ cache: ctx.cache,
2185
+ node,
2186
+ resolver: tsResolver,
2358
2187
  root,
2359
2188
  output: pluginTs.options?.output ?? output,
2360
- group: pluginTs.options?.group ?? void 0
2189
+ group: pluginTs.options?.group
2361
2190
  })
2362
2191
  };
2363
2192
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -2426,32 +2255,30 @@ const suspenseQueryGenerator = (0, kubb_kit.defineGenerator)({
2426
2255
  node,
2427
2256
  tsResolver
2428
2257
  }),
2429
- suspense && hooks && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [
2430
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2431
- name: ["useSuspenseQuery"],
2432
- path: importPath
2433
- }),
2434
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2435
- name: [
2436
- "QueryKey",
2437
- "QueryClient",
2438
- "UseSuspenseQueryOptions",
2439
- "UseSuspenseQueryResult"
2440
- ],
2441
- path: importPath,
2442
- isTypeOnly: true
2443
- }),
2444
- /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Query, {
2445
- name: queryName,
2446
- queryOptionsName,
2447
- queryKeyName,
2448
- queryKeyTypeName,
2449
- node,
2450
- tsResolver,
2451
- customOptions,
2452
- suspense: true
2453
- })
2454
- ] })
2258
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2259
+ name: ["useSuspenseQuery"],
2260
+ path: importPath
2261
+ }),
2262
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
2263
+ name: [
2264
+ "QueryKey",
2265
+ "QueryClient",
2266
+ "UseSuspenseQueryOptions",
2267
+ "UseSuspenseQueryResult"
2268
+ ],
2269
+ path: importPath,
2270
+ isTypeOnly: true
2271
+ }),
2272
+ /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Query, {
2273
+ name: queryName,
2274
+ queryOptionsName,
2275
+ queryKeyName,
2276
+ queryKeyTypeName,
2277
+ node,
2278
+ tsResolver,
2279
+ customOptions,
2280
+ suspense: true
2281
+ })
2455
2282
  ]
2456
2283
  });
2457
2284
  }
@@ -2537,7 +2364,7 @@ const pluginReactQuery = (0, kubb_kit.definePlugin)((options) => {
2537
2364
  const { output = {
2538
2365
  path: "hooks",
2539
2366
  barrel: { type: "named" }
2540
- }, group, exclude = [], include, override = [], suspense = {}, infinite = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, customOptions, hooks = false, client, resolver: userResolver, macros: userMacros } = options;
2367
+ }, group, exclude = [], include, override = [], suspense = false, infinite = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, customOptions, hooks = false, client, resolver: userResolver, macros: userMacros } = options;
2541
2368
  const selectedGenerators = [
2542
2369
  queryGenerator,
2543
2370
  suspenseQueryGenerator,