@kubb/plugin-swr 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
@@ -23,318 +23,31 @@ var __copyProps = (to, from, except, desc) => {
23
23
  }
24
24
  return to;
25
25
  };
26
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
27
27
  value: mod,
28
28
  enumerable: true
29
29
  }) : target, mod));
30
30
  //#endregion
31
+ let kubb_kit = require("kubb/kit");
31
32
  let node_path = require("node:path");
32
33
  node_path = __toESM(node_path, 1);
33
- let kubb_kit = require("kubb/kit");
34
34
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
35
35
  let kubb_jsx = require("kubb/jsx");
36
36
  let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
37
- //#region ../../internals/utils/src/casing.ts
38
- /**
39
- * Shared implementation for camelCase and PascalCase conversion.
40
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
41
- * and capitalizes each word according to `pascal`.
42
- *
43
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
44
- */
45
- function toCamelOrPascal(text, pascal) {
46
- 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) => {
47
- if (word.length > 1 && word === word.toUpperCase()) return word;
48
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
49
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
50
- }
51
- /**
52
- * Converts `text` to camelCase.
53
- *
54
- * @example Word boundaries
55
- * `camelCase('hello-world') // 'helloWorld'`
56
- *
57
- * @example With a prefix
58
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
59
- */
60
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
61
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
62
- }
63
- /**
64
- * Uppercases only the first character of `text`, leaving the rest untouched.
65
- * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
66
- *
67
- * @example
68
- * `capitalize('getPetById') // 'GetPetById'`
69
- */
70
- function capitalize(text) {
71
- return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
72
- }
73
- //#endregion
74
- //#region ../../internals/utils/src/reserved.ts
75
- /**
76
- * JavaScript and Java reserved words.
77
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
78
- */
79
- const reservedWords = /* @__PURE__ */ new Set([
80
- "abstract",
81
- "arguments",
82
- "boolean",
83
- "break",
84
- "byte",
85
- "case",
86
- "catch",
87
- "char",
88
- "class",
89
- "const",
90
- "continue",
91
- "debugger",
92
- "default",
93
- "delete",
94
- "do",
95
- "double",
96
- "else",
97
- "enum",
98
- "eval",
99
- "export",
100
- "extends",
101
- "false",
102
- "final",
103
- "finally",
104
- "float",
105
- "for",
106
- "function",
107
- "goto",
108
- "if",
109
- "implements",
110
- "import",
111
- "in",
112
- "instanceof",
113
- "int",
114
- "interface",
115
- "let",
116
- "long",
117
- "native",
118
- "new",
119
- "null",
120
- "package",
121
- "private",
122
- "protected",
123
- "public",
124
- "return",
125
- "short",
126
- "static",
127
- "super",
128
- "switch",
129
- "synchronized",
130
- "this",
131
- "throw",
132
- "throws",
133
- "transient",
134
- "true",
135
- "try",
136
- "typeof",
137
- "var",
138
- "void",
139
- "volatile",
140
- "while",
141
- "with",
142
- "yield",
143
- "Array",
144
- "Date",
145
- "hasOwnProperty",
146
- "Infinity",
147
- "isFinite",
148
- "isNaN",
149
- "isPrototypeOf",
150
- "length",
151
- "Math",
152
- "name",
153
- "NaN",
154
- "Number",
155
- "Object",
156
- "prototype",
157
- "String",
158
- "toString",
159
- "undefined",
160
- "valueOf"
161
- ]);
162
- /**
163
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
164
- *
165
- * @example
166
- * ```ts
167
- * isValidVarName('status') // true
168
- * isValidVarName('class') // false (reserved word)
169
- * isValidVarName('42foo') // false (starts with digit)
170
- * ```
171
- */
172
- function isValidVarName(name) {
173
- if (!name || reservedWords.has(name)) return false;
174
- return isIdentifier(name);
175
- }
176
- /**
177
- * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
178
- *
179
- * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
180
- * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
181
- * deciding whether an object key needs quoting.
182
- *
183
- * @example
184
- * ```ts
185
- * isIdentifier('name') // true
186
- * isIdentifier('x-total')// false
187
- * ```
188
- */
189
- function isIdentifier(name) {
190
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
191
- }
192
- //#endregion
193
- //#region ../../internals/utils/src/url.ts
194
- function transformParam(raw, casing) {
195
- const param = isValidVarName(raw) ? raw : camelCase(raw);
196
- return casing === "camelcase" ? camelCase(param) : param;
197
- }
198
- function toParamsObject(path, { replacer, casing } = {}) {
199
- const params = {};
200
- for (const match of path.matchAll(/\{([^}]+)\}/g)) {
201
- const param = transformParam(match[1], casing);
202
- const key = replacer ? replacer(param) : param;
203
- params[key] = key;
204
- }
205
- return Object.keys(params).length > 0 ? params : null;
206
- }
207
- /**
208
- * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
209
- */
210
- var Url = class Url {
211
- /**
212
- * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
213
- *
214
- * @example
215
- * Url.canParse('https://petstore.swagger.io/v2') // true
216
- * Url.canParse('/pet/{petId}') // false
217
- */
218
- static canParse(url, base) {
219
- return URL.canParse(url, base);
220
- }
221
- /**
222
- * Converts an OpenAPI/Swagger path to Express-style colon syntax.
223
- *
224
- * @example
225
- * Url.toPath('/pet/{petId}') // '/pet/:petId'
226
- */
227
- static toPath(path) {
228
- return path.replace(/\{([^}]+)\}/g, ":$1");
229
- }
230
- /**
231
- * Rewrites OpenAPI placeholder names while keeping the `{...}` braces, so the generated `url`
232
- * literal aligns with the grouped `path` request option that the runtime client interpolates by
233
- * key.
234
- *
235
- * @example
236
- * Url.toCasedTemplate('/projects/{project_id}', { casing: 'camelcase' }) // '/projects/{projectId}'
237
- */
238
- static toCasedTemplate(path, { casing } = {}) {
239
- return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name, casing)}}`);
240
- }
241
- /**
242
- * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
243
- * `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
244
- * and `casing` controls parameter identifier casing.
245
- *
246
- * @example
247
- * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
248
- *
249
- * @example
250
- * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
251
- */
252
- static toTemplateString(path, { prefix, replacer, casing } = {}) {
253
- const result = path.split(/\{([^}]+)\}/).map((part, i) => {
254
- if (i % 2 === 0) return part;
255
- const param = transformParam(part, casing);
256
- return `\${${replacer ? replacer(param) : param}}`;
257
- }).join("");
258
- return `\`${prefix ?? ""}${result}\``;
259
- }
260
- /**
261
- * Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
262
- * grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
263
- * names are camelCased to match the generated `path` type, and `prefix` is prepended inside the
264
- * literal. Shared by the client and cypress generators that pass a grouped `path` object.
265
- *
266
- * @example
267
- * Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
268
- */
269
- static toGroupedTemplateString(path, { prefix } = {}) {
270
- return Url.toTemplateString(path, {
271
- prefix,
272
- casing: "camelcase",
273
- replacer: (name) => `path.${name}`
274
- });
275
- }
276
- /**
277
- * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
278
- * expression when `stringify` is set.
279
- *
280
- * @example
281
- * Url.toObject('/pet/{petId}')
282
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
283
- */
284
- static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
285
- const object = {
286
- url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
287
- replacer,
288
- casing
289
- }),
290
- params: toParamsObject(path, {
291
- replacer,
292
- casing
293
- })
294
- };
295
- if (stringify) {
296
- if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
297
- if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
298
- return `{ url: '${object.url}' }`;
299
- }
300
- return object;
301
- }
302
- };
303
- //#endregion
304
37
  //#region ../../internals/shared/src/params.ts
305
- const caseParamsCache = /* @__PURE__ */ new WeakMap();
306
- /**
307
- * Applies camelCase to parameter names and returns a new array without mutating the input.
308
- *
309
- * Run it before handing parameters to schema builders so output property keys get the right casing
310
- * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
311
- * original array is returned unchanged. Results are cached per input array.
312
- */
313
- function caseParams(params, casing) {
314
- if (!casing) return params;
315
- const cached = caseParamsCache.get(params);
316
- if (cached) return cached;
317
- const result = params.map((param) => ({
318
- ...param,
319
- name: camelCase(param.name)
320
- }));
321
- caseParamsCache.set(params, result);
322
- return result;
323
- }
324
38
  /**
325
- * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
39
+ * Drops parameters that share the same name, keeping the first.
326
40
  *
327
- * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
328
- * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
329
- * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
330
- * identity so the resulting group is collision-free regardless of the names each caller carries.
41
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
42
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
43
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
44
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
331
45
  */
332
- function dedupeByCasedName(params) {
46
+ function dedupeParams(params) {
333
47
  const seen = /* @__PURE__ */ new Set();
334
48
  return params.filter((param) => {
335
- const key = camelCase(param.name);
336
- if (seen.has(key)) return false;
337
- seen.add(key);
49
+ if (seen.has(param.name)) return false;
50
+ seen.add(param.name);
338
51
  return true;
339
52
  });
340
53
  }
@@ -359,11 +72,30 @@ function operationFileEntry(node, name, extname = ".ts") {
359
72
  path: node.path
360
73
  };
361
74
  }
75
+ /**
76
+ * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
77
+ * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
78
+ * same dependency for the same operation in one pass (a query plugin's several hook generators, the
79
+ * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
80
+ *
81
+ * @example Cache `plugin-ts`'s file for the current operation
82
+ * ```ts
83
+ * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
84
+ * ```
85
+ */
86
+ function resolveDependencyOperationFile(options) {
87
+ const { cache, node, resolver, root, output, group } = options;
88
+ return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
89
+ ...operationFileEntry(node, node.operationId),
90
+ root,
91
+ output,
92
+ group: group ?? void 0
93
+ }));
94
+ }
362
95
  function getOperationLink(node, link) {
363
96
  if (!link) return null;
364
97
  if (typeof link === "function") return link(node) ?? null;
365
- if (link === "urlPath") return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
366
- return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
98
+ return node.path ? `{@link ${kubb_kit.Url.toPath(node.path)}}` : null;
367
99
  }
368
100
  /**
369
101
  * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
@@ -453,14 +185,24 @@ function buildOperationComments(node, options = {}) {
453
185
  if (!splitLines) return filteredComments;
454
186
  return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
455
187
  }
456
- function getOperationParameters(node, options = {}) {
457
- const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
458
- return {
459
- path: dedupeByCasedName(params.filter((param) => param.in === "path")),
460
- query: dedupeByCasedName(params.filter((param) => param.in === "query")),
461
- header: dedupeByCasedName(params.filter((param) => param.in === "header")),
462
- cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
188
+ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
189
+ /**
190
+ * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
191
+ * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
192
+ * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
193
+ * same parameters once per plugin.
194
+ */
195
+ function getOperationParameters(node) {
196
+ const cached = operationParameterGroupsByNode.get(node);
197
+ if (cached) return cached;
198
+ const groups = {
199
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
200
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
201
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
202
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
463
203
  };
204
+ operationParameterGroupsByNode.set(node, groups);
205
+ return groups;
464
206
  }
465
207
  function getStatusCodeNumber(statusCode) {
466
208
  const code = Number(statusCode);
@@ -491,7 +233,7 @@ function resolveStatusCodeNames(node, resolver) {
491
233
  }
492
234
  const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
493
235
  function resolveOperationTypeNames(node, resolver, options = {}) {
494
- const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
236
+ const cacheKey = `${node.operationId}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
495
237
  let byResolver = typeNamesByResolver.get(resolver);
496
238
  if (byResolver) {
497
239
  const cached = byResolver.get(cacheKey);
@@ -500,7 +242,7 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
500
242
  byResolver = /* @__PURE__ */ new Map();
501
243
  typeNamesByResolver.set(resolver, byResolver);
502
244
  }
503
- const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
245
+ const { path, query, header } = getOperationParameters(node);
504
246
  const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
505
247
  const exclude = new Set(options.exclude ?? []);
506
248
  const paramNames = options.includeParams === false ? [] : [
@@ -522,6 +264,43 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
522
264
  return result;
523
265
  }
524
266
  //#endregion
267
+ //#region ../../internals/utils/src/casing.ts
268
+ /**
269
+ * Shared implementation for camelCase and PascalCase conversion.
270
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
271
+ * and capitalizes each word according to `pascal`.
272
+ *
273
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
274
+ */
275
+ function toCamelOrPascal(text, pascal) {
276
+ 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) => {
277
+ if (word.length > 1 && word === word.toUpperCase()) return word;
278
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
279
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
280
+ }
281
+ /**
282
+ * Converts `text` to camelCase.
283
+ *
284
+ * @example Word boundaries
285
+ * `camelCase('hello-world') // 'helloWorld'`
286
+ *
287
+ * @example With a prefix
288
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
289
+ */
290
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
291
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
292
+ }
293
+ /**
294
+ * Uppercases only the first character of `text`, leaving the rest untouched.
295
+ * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
296
+ *
297
+ * @example
298
+ * `capitalize('getPetById') // 'GetPetById'`
299
+ */
300
+ function capitalize(text) {
301
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
302
+ }
303
+ //#endregion
525
304
  //#region ../../internals/shared/src/group.ts
526
305
  /**
527
306
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
@@ -641,23 +420,30 @@ function resolveContractClient(options) {
641
420
  * (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
642
421
  * path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
643
422
  * read `RequestConfig` / `ResponseErrorConfig` from.
423
+ *
424
+ * The result is cached in the current node's `cache` (`ctx.cache`) under the client plugin's name,
425
+ * so several dependents reading the same client plugin for one operation in a single pass
426
+ * (react-query's query/mutation/infinite generators, vue-query, swr, the MCP handler, ...) share
427
+ * one computed result instead of each re-deriving the name and path.
644
428
  */
645
429
  function resolveClientOperation(options) {
646
- const { clientPlugin, driver, node, root, output } = options;
430
+ const { clientPlugin, driver, node, root, output, cache } = options;
647
431
  if (!clientPlugin) return null;
648
- const resolver = driver.getResolver(clientPlugin.pluginName);
649
- const plugin = driver.getPlugin(clientPlugin.pluginName);
650
- const file = resolver.file({
651
- ...operationFileEntry(node, node.operationId),
652
- root,
653
- output: plugin?.options?.output ?? output,
654
- group: plugin?.options?.group ?? void 0
432
+ return cache.ensureItem(`${clientPlugin.pluginName}:clientOperation`, () => {
433
+ const resolver = driver.getResolver(clientPlugin.pluginName);
434
+ const plugin = driver.getPlugin(clientPlugin.pluginName);
435
+ const file = resolver.file({
436
+ ...operationFileEntry(node, node.operationId),
437
+ root,
438
+ output: plugin?.options?.output ?? output,
439
+ group: plugin?.options?.group ?? void 0
440
+ });
441
+ return {
442
+ name: resolver.name(node.operationId),
443
+ path: file.path,
444
+ clientPath: node_path.default.resolve(root, ".kubb/client.ts")
445
+ };
655
446
  });
656
- return {
657
- name: resolver.name(node.operationId),
658
- path: file.path,
659
- clientPath: node_path.default.resolve(root, ".kubb/client.ts")
660
- };
661
447
  }
662
448
  //#endregion
663
449
  //#region ../../internals/tanstack-query/src/utils.ts
@@ -725,14 +511,16 @@ function buildGroupedRequestParam(node, options) {
725
511
  */
726
512
  function buildQueryOptionsParams(node, options) {
727
513
  const { resolver, memberTypeWrapper } = options;
728
- return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [buildGroupedRequestParam(node, {
514
+ const groupedParam = buildGroupedRequestParam(node, {
729
515
  resolver,
730
516
  memberTypeWrapper
731
- }), (0, _kubb_plugin_ts.createFunctionParameter)({
517
+ });
518
+ const configParam = (0, _kubb_plugin_ts.createFunctionParameter)({
732
519
  name: "config",
733
520
  type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
734
521
  default: "{}"
735
- })].filter((param) => param !== null) });
522
+ });
523
+ return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [groupedParam, configParam].filter((param) => param !== null) });
736
524
  }
737
525
  /**
738
526
  * Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
@@ -790,6 +578,19 @@ function resolveMutationConfig(mutation, options) {
790
578
  };
791
579
  }
792
580
  /**
581
+ * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
582
+ * `query: false` still marks the operation as a query so the query-family generators keep matching it,
583
+ * and a method already claimed by `query` never counts as a mutation.
584
+ */
585
+ function classifyOperation(node, { query, mutation }) {
586
+ const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
587
+ const queryMethods = new Set(query ? query.methods : []);
588
+ return {
589
+ isQuery,
590
+ isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
591
+ };
592
+ }
593
+ /**
793
594
  * Build QueryKey params as the grouped `{ path, query, body }` object (NO headers, NO config),
794
595
  * typed from the operation's `Options` minus `url`. The query key transformer reads the
795
596
  * grouped `path`/`query`/`body` bindings.
@@ -811,7 +612,7 @@ function buildQueryKeyParams(node, options) {
811
612
  const declarationPrinter$4 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
812
613
  const mutationKeyTransformer = ({ node }) => {
813
614
  if (!node.path) return [];
814
- return [`{ url: '${Url.toPath(node.path)}' }`];
615
+ return [`{ url: '${kubb_kit.Url.toPath(node.path)}' }`];
815
616
  };
816
617
  function MutationKey$1({ name, node, transformer }) {
817
618
  const paramsNode = (0, _kubb_plugin_ts.createFunctionParameters)({ params: [] });
@@ -896,7 +697,7 @@ const queryKeyTransformer = ({ node }) => {
896
697
  const hasQueryParams = getOperationParameters(node).query.length > 0;
897
698
  const hasRequestBody = !!node.requestBody?.content?.[0]?.schema;
898
699
  return [
899
- hasPathParams ? `{ url: '${Url.toPath(node.path)}', params: path }` : `{ url: '${Url.toPath(node.path)}' }`,
700
+ hasPathParams ? `{ url: '${kubb_kit.Url.toPath(node.path)}', params: path }` : `{ url: '${kubb_kit.Url.toPath(node.path)}' }`,
900
701
  hasQueryParams ? "...(query ? [query] : [])" : null,
901
702
  hasRequestBody ? "...(body ? [body] : [])" : null
902
703
  ].filter(Boolean);
@@ -936,10 +737,12 @@ function buildMutationParamsNode(node, options) {
936
737
  const { mutationKeyTypeName, mutationArgTypeName, resolver } = options;
937
738
  const responseName = resolver.response.response(node);
938
739
  const errorNames = resolveErrorNames(node, resolver);
740
+ const TData = responseName;
741
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
939
742
  return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
940
743
  name: "options",
941
744
  type: `{
942
- mutation?: SWRMutationConfiguration<${responseName}, ${`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
745
+ mutation?: SWRMutationConfiguration<${TData}, ${TError}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
943
746
  client?: ${buildRequestConfigType(node)},
944
747
  shouldFetch?: boolean,
945
748
  }`,
@@ -1034,17 +837,20 @@ function buildQueryParamsNode(node, options) {
1034
837
  const { resolver } = options;
1035
838
  const responseName = resolver.response.response(node);
1036
839
  const errorNames = resolveErrorNames(node, resolver);
840
+ const TData = responseName;
841
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
1037
842
  const optionsParam = (0, _kubb_plugin_ts.createFunctionParameter)({
1038
843
  name: "options",
1039
844
  type: `{
1040
- query?: SWRConfiguration<${[responseName, `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`].join(", ")}>,
845
+ query?: SWRConfiguration<${[TData, TError].join(", ")}>,
1041
846
  client?: ${buildClientOptionType()},
1042
847
  shouldFetch?: boolean,
1043
848
  immutable?: boolean
1044
849
  }`,
1045
850
  default: "{}"
1046
851
  });
1047
- return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [buildGroupedRequestParam(node, { resolver }), optionsParam].filter((param) => param !== null) });
852
+ const groupedParam = buildGroupedRequestParam(node, { resolver });
853
+ return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
1048
854
  }
1049
855
  function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
1050
856
  const responseName = tsResolver.response.response(node);
@@ -1136,16 +942,19 @@ const mutationGenerator = (0, kubb_kit.defineGenerator)({
1136
942
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1137
943
  if (!pluginTs) return null;
1138
944
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1139
- const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
1140
- const queryMethods = new Set(query ? query.methods : []);
1141
- if (!(mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase()))) return null;
945
+ const { isMutation } = classifyOperation(node, {
946
+ query,
947
+ mutation
948
+ });
949
+ if (!isMutation) return null;
1142
950
  const importPath = mutation ? mutation.importPath : "swr/mutation";
1143
951
  const contractOp = resolveClientOperation({
1144
952
  clientPlugin: { pluginName: client.pluginName },
1145
953
  driver,
1146
954
  node,
1147
955
  root,
1148
- output
956
+ output,
957
+ cache: ctx.cache
1149
958
  });
1150
959
  if (!contractOp) return null;
1151
960
  const mutationHookName = resolver.mutation.name(node);
@@ -1162,11 +971,10 @@ const mutationGenerator = (0, kubb_kit.defineGenerator)({
1162
971
  output,
1163
972
  group
1164
973
  }),
1165
- fileTs: tsResolver.file({
1166
- name: node.operationId,
1167
- extname: ".ts",
1168
- tag: node.tags[0] ?? "default",
1169
- path: node.path,
974
+ fileTs: resolveDependencyOperationFile({
975
+ cache: ctx.cache,
976
+ node,
977
+ resolver: tsResolver,
1170
978
  root,
1171
979
  output: pluginTs.options?.output ?? output,
1172
980
  group: pluginTs.options?.group
@@ -1257,18 +1065,23 @@ const queryGenerator = (0, kubb_kit.defineGenerator)({
1257
1065
  operation(node, ctx) {
1258
1066
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
1259
1067
  const { config, driver, resolver, root } = ctx;
1260
- const { output, query, client, group } = ctx.options;
1068
+ const { output, query, mutation, client, group } = ctx.options;
1261
1069
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1262
1070
  if (!pluginTs) return null;
1263
1071
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1264
- if (!(query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))) return null;
1072
+ const { isQuery } = classifyOperation(node, {
1073
+ query,
1074
+ mutation
1075
+ });
1076
+ if (!isQuery) return null;
1265
1077
  const importPath = query ? query.importPath : "swr";
1266
1078
  const contractOp = resolveClientOperation({
1267
1079
  clientPlugin: { pluginName: client.pluginName },
1268
1080
  driver,
1269
1081
  node,
1270
1082
  root,
1271
- output
1083
+ output,
1084
+ cache: ctx.cache
1272
1085
  });
1273
1086
  if (!contractOp) return null;
1274
1087
  const queryName = resolver.query.name(node);
@@ -1285,11 +1098,10 @@ const queryGenerator = (0, kubb_kit.defineGenerator)({
1285
1098
  output,
1286
1099
  group
1287
1100
  }),
1288
- fileTs: tsResolver.file({
1289
- name: node.operationId,
1290
- extname: ".ts",
1291
- tag: node.tags[0] ?? "default",
1292
- path: node.path,
1101
+ fileTs: resolveDependencyOperationFile({
1102
+ cache: ctx.cache,
1103
+ node,
1104
+ resolver: tsResolver,
1293
1105
  root,
1294
1106
  output: pluginTs.options?.output ?? output,
1295
1107
  group: pluginTs.options?.group