@orval/query 8.9.0 → 8.10.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.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { GetterPropType, OutputClient, OutputHttpClient, TEMPLATE_TAG_REGEX, Verbs, camel, compareVersions, generateFormDataAndUrlEncodedFunction, generateMutator, generateMutatorConfig, generateMutatorRequestOptions, generateOptions, generateVerbImports, getAngularFilteredParamsCallExpression, getAngularFilteredParamsHelperBody, getRouteAsArray, getSuccessResponseType, isObject, isString, isSyntheticDefaultImportsAllow, jsDoc, kebab, mergeDeep, pascal, stringify, toObjectString } from "@orval/core";
1
+ import { GetterPropType, OutputClient, OutputHttpClient, TEMPLATE_TAG_REGEX, Verbs, camel, compareVersions, generateFormDataAndUrlEncodedFunction, generateMutator, generateMutatorConfig, generateMutatorRequestOptions, generateOptions, generateVerbImports, getAngularFilteredParamsCallExpression, getAngularFilteredParamsHelperBody, getRouteAsArray, getSuccessResponseType, isObject, isString, isSyntheticDefaultImportsAllow, jsDoc, kebab, logWarning, mergeDeep, pascal, stringify, toObjectString } from "@orval/core";
2
2
  import { generateFetchHeader, generateRequestFunction } from "@orval/fetch";
3
3
  import nodePath from "node:path";
4
4
  import { styleText } from "node:util";
@@ -10,7 +10,10 @@ const normalizeQueryOptions = (queryOptions = {}, outputWorkspace) => {
10
10
  ...queryOptions.useInvalidate ? { useInvalidate: true } : {},
11
11
  ...queryOptions.useSetQueryData ? { useSetQueryData: true } : {},
12
12
  ...queryOptions.useGetQueryData ? { useGetQueryData: true } : {},
13
- ...queryOptions.useQuery ? { useQuery: true } : {},
13
+ ...queryOptions.useQuery === void 0 ? {} : { useQuery: queryOptions.useQuery },
14
+ ...queryOptions.useMutation === void 0 ? {} : { useMutation: queryOptions.useMutation },
15
+ ...queryOptions.useSuspenseQuery ? { useSuspenseQuery: true } : {},
16
+ ...queryOptions.useSuspenseInfiniteQuery ? { useSuspenseInfiniteQuery: true } : {},
14
17
  ...queryOptions.useInfinite ? { useInfinite: true } : {},
15
18
  ...queryOptions.useInfiniteQueryParam ? { useInfiniteQueryParam: queryOptions.useInfiniteQueryParam } : {},
16
19
  ...queryOptions.options ? { options: queryOptions.options } : {},
@@ -901,7 +904,7 @@ const generateQueryOptions = ({ params, options, type, adapter }) => {
901
904
  if (options) return `${queryConfig} ...queryOptions`;
902
905
  return "...queryOptions";
903
906
  }
904
- return `${adapter ? adapter.generateEnabledOption(params, options) : !isObject(options) || !Object.hasOwn(options, "enabled") ? `enabled: !!(${params.map(({ name }) => name).join(" && ")}),` : ""}${queryConfig} ...queryOptions`;
907
+ return `${adapter ? adapter.generateEnabledOption(params, options) : !isObject(options) || !Object.hasOwn(options, "enabled") ? `enabled: ${params.map(({ name }) => `${name} != null`).join(" && ")},` : ""}${queryConfig} ...queryOptions`;
905
908
  };
906
909
  const isSuspenseQuery = (type) => {
907
910
  return [QueryType.SUSPENSE_INFINITE, QueryType.SUSPENSE_QUERY].includes(type);
@@ -1328,7 +1331,8 @@ const createVueAdapter = ({ hasVueQueryV4, hasQueryV5, hasQueryV5WithDataTagErro
1328
1331
  return vueUnRefParams(props.filter((prop) => prop.type === GetterPropType.NAMED_PATH_PARAMS));
1329
1332
  },
1330
1333
  generateEnabledOption(params, options) {
1331
- if (!isObject(options) || !Object.hasOwn(options, "enabled")) return `enabled: computed(() => !!(${params.map(({ name }) => `unref(${name})`).join(" && ")})),`;
1334
+ if (params.length === 0) return "";
1335
+ if (!isObject(options) || !Object.hasOwn(options, "enabled")) return `enabled: computed(() => ${params.map(({ name }) => `unref(${name}) !== null && unref(${name}) !== undefined`).join(" && ")}),`;
1332
1336
  return "";
1333
1337
  },
1334
1338
  getQueryKeyPrefix() {
@@ -1378,7 +1382,8 @@ const withDefaults = (adapter) => ({
1378
1382
  return `\`${route}\``;
1379
1383
  },
1380
1384
  generateEnabledOption(params, options) {
1381
- if (!isObject(options) || !Object.hasOwn(options, "enabled")) return `enabled: !!(${params.map(({ name }) => name).join(" && ")}),`;
1385
+ if (params.length === 0) return "";
1386
+ if (!isObject(options) || !Object.hasOwn(options, "enabled")) return `enabled: ${params.map(({ name }) => `${name} !== null && ${name} !== undefined`).join(" && ")},`;
1382
1387
  return "";
1383
1388
  },
1384
1389
  getQueryPropertyForProp(prop, body) {
@@ -1545,10 +1550,12 @@ const findOperationInfo = (spec, operationName) => {
1545
1550
  if (opId !== operationName && camel(opId) !== operationName) continue;
1546
1551
  if (!routePath.includes("{")) return {
1547
1552
  route: routePath,
1553
+ method,
1548
1554
  hasRequiredPathParams: false
1549
1555
  };
1550
1556
  return {
1551
1557
  route: routePath,
1558
+ method,
1552
1559
  hasRequiredPathParams: [...Array.isArray(pathItem.parameters) ? pathItem.parameters : [], ...Array.isArray(operation.parameters) ? operation.parameters : []].filter((p) => p.in === "path").some((p) => p.schema?.default === void 0 && p.default === void 0)
1553
1560
  };
1554
1561
  }
@@ -1597,7 +1604,7 @@ const generateParamArgs = (params) => {
1597
1604
  * Create a generateInvalidateCall function that has access to the OpenAPI spec
1598
1605
  * for intelligent route-based invalidation when params are not specified.
1599
1606
  */
1600
- const createGenerateInvalidateCall = (spec, shouldSplitQueryKey) => {
1607
+ const createGenerateInvalidateCall = (spec, shouldSplitQueryKey, useOperationIdAsQueryKey) => {
1601
1608
  return (target) => {
1602
1609
  const method = target.invalidateMode === "reset" ? "resetQueries" : "invalidateQueries";
1603
1610
  const queryKeyFn = camel(`get-${target.query}-query-key`);
@@ -1606,7 +1613,15 @@ const createGenerateInvalidateCall = (spec, shouldSplitQueryKey) => {
1606
1613
  if (info?.hasRequiredPathParams) {
1607
1614
  const prefix = getStaticRoutePrefix(info.route);
1608
1615
  if (prefix !== void 0) {
1609
- if (shouldSplitQueryKey) return ` queryClient.${method}({ queryKey: [${prefix.split("/").filter((s) => s !== "").map((s) => `'${s}'`).join(", ")}] });`;
1616
+ const verbPrefix = getQueryKeyVerbPrefix({
1617
+ verb: info.method,
1618
+ useOperationIdAsQueryKey
1619
+ });
1620
+ if (shouldSplitQueryKey) {
1621
+ const segments = prefix.split("/").filter((s) => s !== "").map((s) => `'${s}'`).join(", ");
1622
+ return ` queryClient.${method}({ queryKey: ${verbPrefix ? `['${verbPrefix}', ${segments}]` : `[${segments}]`} });`;
1623
+ }
1624
+ if (verbPrefix) return ` queryClient.${method}({ predicate: (query) => query.queryKey[0] === '${verbPrefix}' && typeof query.queryKey[1] === 'string' && query.queryKey[1].startsWith('${prefix}') });`;
1610
1625
  return ` queryClient.${method}({ predicate: (query) => typeof query.queryKey[0] === 'string' && query.queryKey[0].startsWith('${prefix}') });`;
1611
1626
  }
1612
1627
  }
@@ -1688,7 +1703,7 @@ ${hasInvalidation ? adapter.generateMutationOnSuccess({
1688
1703
  operationName,
1689
1704
  definitions,
1690
1705
  isRequestOptions,
1691
- generateInvalidateCall: createGenerateInvalidateCall(context.spec, !!query.shouldSplitQueryKey),
1706
+ generateInvalidateCall: createGenerateInvalidateCall(context.spec, !!query.shouldSplitQueryKey, !!query.useOperationIdAsQueryKey),
1692
1707
  uniqueInvalidates
1693
1708
  }) : ""}
1694
1709
 
@@ -1741,6 +1756,70 @@ ${mutationHookBody}
1741
1756
  };
1742
1757
  //#endregion
1743
1758
  //#region src/query-generator.ts
1759
+ /**
1760
+ * Decide whether the current operation's configuration conflicts with a
1761
+ * `mutationInvalidates` rule. The rule wires its invalidation through the
1762
+ * Mutation hook's `onSuccess`, so referencing an operation that is not
1763
+ * generated as a Mutation (either forced into a Query via per-operation
1764
+ * `useQuery: true`, or suppressed entirely) makes the rule a silent no-op.
1765
+ *
1766
+ * Returns the warning message when the conflict applies, or `undefined`
1767
+ * when the configuration is consistent.
1768
+ */
1769
+ const getMutationInvalidatesConflictWarning = ({ operationName, isMutation, isQuery, mutationInvalidates }) => {
1770
+ if (isMutation) return void 0;
1771
+ if (!mutationInvalidates?.length) return void 0;
1772
+ if (!mutationInvalidates.find((rule) => rule.onMutations.includes(operationName))) return void 0;
1773
+ return `mutationInvalidates rule references '${operationName}', but that operation is generated as a ${isQuery ? "Query hook" : "plain function (no hook)"}, not a Mutation. The invalidation will not fire. Either remove '${operationName}' from the rule's onMutations list, or configure '${operationName}' so that it is generated as a Mutation hook.`;
1774
+ };
1775
+ const escapeRegExpMetaChars = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
1776
+ /**
1777
+ * Wraps the body parameter's type in a property string with the mutator's
1778
+ * `BodyType<T>` envelope so that user-facing Query helpers (hook signature,
1779
+ * `getXxxQueryOptions`, `getXxxQueryKey`, prefetch / invalidate / set+get
1780
+ * QueryData) match the request function's signature, which is already
1781
+ * wrapped by `client.ts`. Without this, callers that pass a plain body to
1782
+ * a non-GET Query hook (possible after #2376 routes non-GET verbs to
1783
+ * Query hooks) would hit a type mismatch against the underlying request
1784
+ * function.
1785
+ *
1786
+ * The pattern handles three prop shapes that the various
1787
+ * `toObjectString(props, ...)` callers can emit:
1788
+ * - `name: T` — required body
1789
+ * - `name?: T` — optional body
1790
+ * - `name: undefined | T` — `definedInitialData` overload transform
1791
+ *
1792
+ * `body.definition` is fully regex-escaped so types containing metachars
1793
+ * (e.g. `Pet[]`, `Foo | Bar`, anonymous object types) are matched
1794
+ * verbatim rather than reinterpreted as regex syntax.
1795
+ *
1796
+ * No-op when the operation has no body or the mutator does not export a
1797
+ * `BodyType<T>` wrapper, so existing GET-only Query keys are unchanged.
1798
+ */
1799
+ const wrapPropsBodyWithMutatorBodyType = ({ propsString, body, mutator }) => {
1800
+ if (!mutator?.bodyTypeName || !body.definition) return propsString;
1801
+ const bodyDefinitionPattern = escapeRegExpMetaChars(body.definition);
1802
+ return propsString.replace(new RegExp(String.raw`(\w+\??:\s*(?:undefined\s*\|\s*)?)${bodyDefinitionPattern}`), `$1${mutator.bodyTypeName}<${body.definition}>`);
1803
+ };
1804
+ /**
1805
+ * Computes a verb prefix segment for query keys when a non-GET operation is
1806
+ * routed to a Query hook. Without this prefix, two operations sharing a path
1807
+ * (e.g. `GET /pets` and `POST /pets`) would generate cache keys that both
1808
+ * begin with `'/pets'`, so TanStack Query would mix their cached data and
1809
+ * `invalidateQueries({ queryKey: ['/pets'] })` would match both.
1810
+ *
1811
+ * Skipped for GET (preserves existing keys) and when
1812
+ * `useOperationIdAsQueryKey` is enabled (operation IDs are already unique
1813
+ * across verb + path, so the prefix would be redundant).
1814
+ *
1815
+ * Returns the uppercased verb when a prefix should be inserted, or
1816
+ * `undefined` when no prefix is needed.
1817
+ */
1818
+ const getQueryKeyVerbPrefix = ({ verb, useOperationIdAsQueryKey }) => {
1819
+ if (useOperationIdAsQueryKey) return void 0;
1820
+ if (verb === Verbs.GET) return void 0;
1821
+ return verb.toUpperCase();
1822
+ };
1744
1823
  const getQueryFnArguments = ({ hasQueryParam, hasSignal, hasSignalParam = false }) => {
1745
1824
  if (!hasQueryParam && !hasSignal) return "";
1746
1825
  const signalDestructure = hasSignalParam ? "signal: querySignal" : "signal";
@@ -1771,20 +1850,32 @@ const generatePrefetch = ({ usePrefetch, type, useQuery, useInfinite, operationN
1771
1850
  return queryClient;
1772
1851
  }\n`;
1773
1852
  };
1774
- const generateQueryImplementation = ({ queryOption: { name, queryParam, options, type, queryKeyFnName }, operationName, queryProperties, queryKeyProperties, queryParams, params, props, mutator, queryOptionsMutator, queryKeyMutator, isRequestOptions, response, httpClient, isExactOptionalPropertyTypes, hasSignal, useRuntimeFetcher, route, doc, usePrefetch, useQuery, useInfinite, useInvalidate, useSetQueryData, useGetQueryData, adapter }) => {
1853
+ const generateQueryImplementation = ({ queryOption: { name, queryParam, options, type, queryKeyFnName }, operationName, queryProperties, queryKeyProperties, queryParams, params, props, body, mutator, queryOptionsMutator, queryKeyMutator, isRequestOptions, response, httpClient, isExactOptionalPropertyTypes, hasSignal, useRuntimeFetcher, route, doc, usePrefetch, useQuery, useInfinite, useInvalidate, useSetQueryData, useGetQueryData, adapter }) => {
1775
1854
  const { hasQueryV5, hasQueryV5WithDataTagError, hasQueryV5WithInfiniteQueryOptionsError } = adapter;
1776
1855
  const hasSignalParam = props.some((prop) => prop.name === "signal");
1777
- const queryPropDefinitions = toObjectString(props, "definition");
1778
- const definedInitialDataQueryPropsDefinitions = toObjectString(props.map((prop) => {
1779
- const regex = new RegExp(String.raw`^${prop.name}\s*\?:`);
1780
- if (!regex.test(prop.definition)) return prop;
1781
- const definitionWithUndefined = prop.definition.replace(regex, `${prop.name}: undefined | `);
1782
- return {
1783
- ...prop,
1784
- definition: definitionWithUndefined
1785
- };
1786
- }), "definition");
1787
- const queryProps = toObjectString(props, "implementation");
1856
+ const queryPropDefinitions = wrapPropsBodyWithMutatorBodyType({
1857
+ propsString: toObjectString(props, "definition"),
1858
+ body,
1859
+ mutator
1860
+ });
1861
+ const definedInitialDataQueryPropsDefinitions = wrapPropsBodyWithMutatorBodyType({
1862
+ propsString: toObjectString(props.map((prop) => {
1863
+ const regex = new RegExp(String.raw`^${prop.name}\s*\?:`);
1864
+ if (!regex.test(prop.definition)) return prop;
1865
+ const definitionWithUndefined = prop.definition.replace(regex, `${prop.name}: undefined | `);
1866
+ return {
1867
+ ...prop,
1868
+ definition: definitionWithUndefined
1869
+ };
1870
+ }), "definition"),
1871
+ body,
1872
+ mutator
1873
+ });
1874
+ const queryProps = wrapPropsBodyWithMutatorBodyType({
1875
+ propsString: toObjectString(props, "implementation"),
1876
+ body,
1877
+ mutator
1878
+ });
1788
1879
  const hasInfiniteQueryParam = queryParam && queryParams?.schema.name;
1789
1880
  const httpFunctionProps = queryParam ? adapter.getInfiniteQueryHttpProps(props, queryParam, !!mutator) : adapter.getHttpFunctionQueryProps(queryProperties, httpClient, !!mutator);
1790
1881
  const definedInitialDataReturnType = adapter.getQueryReturnType({
@@ -1937,13 +2028,20 @@ export function ${queryHookName}<TData = ${TData}, TError = ${errorType}>(\n ${q
1937
2028
  };
1938
2029
  const prefetch = adapter.generatePrefetch ? adapter.generatePrefetch(prefetchContext) : generatePrefetch(prefetchContext);
1939
2030
  const isPrimaryQueryType = type === QueryType.QUERY || type === QueryType.INFINITE || type === QueryType.SUSPENSE_QUERY && !useQuery || type === QueryType.SUSPENSE_INFINITE && !useInfinite;
2031
+ const buildBaseQueryKeyExpr = () => queryKeyMutator ? `${queryKeyMutator.name}({ ${queryProperties} }${queryKeyMutator.hasSecondArg ? `, { url: \`${route}\` }` : ""})` : `${queryKeyFnName}(${queryKeyProperties})`;
2032
+ const applyQueryOptionsMutator = (baseExpr) => queryOptionsMutator && !queryOptionsMutator.isHook ? `${queryOptionsMutator.name}({ queryKey: ${baseExpr} }${queryOptionsMutator.hasSecondArg ? `, { ${queryProperties} }` : ""}${queryOptionsMutator.hasThirdArg ? `, { url: \`${route}\` }` : ""}).queryKey` : baseExpr;
1940
2033
  const shouldGenerateInvalidate = useInvalidate && isPrimaryQueryType;
1941
2034
  const invalidateFnName = camel(`invalidate-${name}`);
2035
+ const invalidateQueryKeyExpr = applyQueryOptionsMutator(buildBaseQueryKeyExpr());
1942
2036
  const shouldGenerateSetQueryData = useSetQueryData && isPrimaryQueryType;
1943
2037
  const isReactQuery = adapter.outputClient === OutputClient.REACT_QUERY;
1944
2038
  const setQueryDataFnName = isReactQuery ? camel(`use-set-${name}-query-data`) : camel(`set-${name}-query-data`);
1945
- const setQueryDataKeyExpr = queryKeyMutator ? `${queryKeyMutator.name}({ ${queryProperties} }${queryKeyMutator.hasSecondArg ? `, { url: \`${route}\` }` : ""})` : `${queryKeyFnName}(${queryKeyProperties})`;
1946
- const setQueryDataProps = toObjectString(props.filter((prop) => prop.type !== GetterPropType.HEADER), "implementation").replaceAll("?:", ":");
2039
+ const setQueryDataKeyExpr = buildBaseQueryKeyExpr();
2040
+ const setQueryDataProps = wrapPropsBodyWithMutatorBodyType({
2041
+ propsString: toObjectString(props.filter((prop) => prop.type !== GetterPropType.HEADER), "implementation").replaceAll("?:", ":"),
2042
+ body,
2043
+ mutator
2044
+ });
1947
2045
  const shouldGenerateGetQueryData = useGetQueryData && isPrimaryQueryType;
1948
2046
  const getQueryDataFnName = isReactQuery ? camel(`use-get-${name}-query-data`) : camel(`get-${name}-query-data`);
1949
2047
  const getQueryDataKeyExpr = setQueryDataKeyExpr;
@@ -1974,7 +2072,11 @@ export type ${pascal(name)}QueryError = ${errorType}
1974
2072
 
1975
2073
  ${adapter.shouldGenerateOverrideTypes() ? overrideTypes : ""}
1976
2074
  ${doc}
1977
- export function ${queryHookName}<TData = ${TData}, TError = ${errorType}>(\n ${adapter.getHookPropsDefinitions(props)} ${queryArguments} ${optionalQueryClientArgument} \n ): ${returnType} {
2075
+ export function ${queryHookName}<TData = ${TData}, TError = ${errorType}>(\n ${wrapPropsBodyWithMutatorBodyType({
2076
+ propsString: adapter.getHookPropsDefinitions(props),
2077
+ body,
2078
+ mutator
2079
+ })} ${queryArguments} ${optionalQueryClientArgument} \n ): ${returnType} {
1978
2080
 
1979
2081
  ${queryInit}
1980
2082
 
@@ -1990,7 +2092,7 @@ export function ${queryHookName}<TData = ${TData}, TError = ${errorType}>(\n ${a
1990
2092
  ${prefetch}
1991
2093
  ${shouldGenerateInvalidate ? `${doc}export const ${invalidateFnName} = async (\n queryClient: QueryClient, ${queryProps} options?: InvalidateOptions\n ): Promise<QueryClient> => {
1992
2094
 
1993
- await queryClient.invalidateQueries({ queryKey: ${queryKeyFnName}(${queryKeyProperties}) }, options);
2095
+ await queryClient.invalidateQueries({ queryKey: ${invalidateQueryKeyExpr} }, options);
1994
2096
 
1995
2097
  return queryClient;
1996
2098
  }\n` : ""}
@@ -2026,22 +2128,23 @@ const generateQueryHook = async (verbOptions, options, outputClient, adapter) =>
2026
2128
  });
2027
2129
  let implementation = "";
2028
2130
  let mutators;
2029
- const hasOperationQueryOption = [
2030
- operationQueryOptions?.useQuery,
2031
- operationQueryOptions?.useSuspenseQuery,
2032
- operationQueryOptions?.useInfinite,
2033
- operationQueryOptions?.useSuspenseInfiniteQuery
2034
- ].some(Boolean);
2035
- let isQuery = Verbs.GET === verb && [
2036
- override.query.useQuery,
2037
- override.query.useSuspenseQuery,
2038
- override.query.useInfinite,
2039
- override.query.useSuspenseInfiniteQuery
2040
- ].some(Boolean) || hasOperationQueryOption;
2041
- let isMutation = override.query.useMutation && verb !== Verbs.GET;
2042
- if (operationQueryOptions?.useMutation !== void 0) isMutation = operationQueryOptions.useMutation;
2131
+ const effectiveUseQuery = operationQueryOptions?.useQuery ?? override.query.useQuery ?? verb === Verbs.GET;
2132
+ const effectiveUseMutation = operationQueryOptions?.useMutation ?? override.query.useMutation ?? verb !== Verbs.GET;
2133
+ const globalSuspenseOrInfiniteOnlyForGet = (flag) => flag === true && verb === Verbs.GET;
2134
+ const effectiveUseSuspenseQuery = operationQueryOptions?.useSuspenseQuery ?? globalSuspenseOrInfiniteOnlyForGet(override.query.useSuspenseQuery);
2135
+ const effectiveUseInfinite = operationQueryOptions?.useInfinite ?? globalSuspenseOrInfiniteOnlyForGet(override.query.useInfinite);
2136
+ const effectiveUseSuspenseInfiniteQuery = operationQueryOptions?.useSuspenseInfiniteQuery ?? globalSuspenseOrInfiniteOnlyForGet(override.query.useSuspenseInfiniteQuery);
2137
+ let isQuery = effectiveUseQuery || effectiveUseSuspenseQuery || effectiveUseInfinite || effectiveUseSuspenseInfiniteQuery;
2138
+ let isMutation = effectiveUseMutation && verb !== Verbs.GET;
2043
2139
  if (verb !== Verbs.GET && isQuery) isMutation = false;
2044
2140
  if (verb === Verbs.GET && isMutation) isQuery = false;
2141
+ const conflictWarning = getMutationInvalidatesConflictWarning({
2142
+ operationName,
2143
+ isMutation,
2144
+ isQuery,
2145
+ mutationInvalidates: override.query.mutationInvalidates
2146
+ });
2147
+ if (conflictWarning) logWarning(conflictWarning);
2045
2148
  if (isQuery) {
2046
2149
  const queryKeyMutator = query.queryKey ? await generateMutator({
2047
2150
  output,
@@ -2064,26 +2167,26 @@ const generateQueryHook = async (verbOptions, options, outputClient, adapter) =>
2064
2167
  return adapter.getQueryPropertyForProp(param, body);
2065
2168
  }).join(",");
2066
2169
  const queries = [
2067
- ...query.useInfinite || operationQueryOptions?.useInfinite ? [{
2170
+ ...effectiveUseInfinite ? [{
2068
2171
  name: camel(`${operationName}-infinite`),
2069
2172
  options: query.options,
2070
2173
  type: QueryType.INFINITE,
2071
2174
  queryParam: query.useInfiniteQueryParam,
2072
2175
  queryKeyFnName: camel(`get-${operationName}-infinite-query-key`)
2073
2176
  }] : [],
2074
- ...query.useQuery || operationQueryOptions?.useQuery ? [{
2177
+ ...effectiveUseQuery ? [{
2075
2178
  name: operationName,
2076
2179
  options: query.options,
2077
2180
  type: QueryType.QUERY,
2078
2181
  queryKeyFnName: camel(`get-${operationName}-query-key`)
2079
2182
  }] : [],
2080
- ...query.useSuspenseQuery || operationQueryOptions?.useSuspenseQuery ? [{
2183
+ ...effectiveUseSuspenseQuery ? [{
2081
2184
  name: camel(`${operationName}-suspense`),
2082
2185
  options: query.options,
2083
2186
  type: QueryType.SUSPENSE_QUERY,
2084
2187
  queryKeyFnName: camel(`get-${operationName}-query-key`)
2085
2188
  }] : [],
2086
- ...query.useSuspenseInfiniteQuery || operationQueryOptions?.useSuspenseInfiniteQuery ? [{
2189
+ ...effectiveUseSuspenseInfiniteQuery ? [{
2087
2190
  name: camel(`${operationName}-suspense-infinite`),
2088
2191
  options: query.options,
2089
2192
  type: QueryType.SUSPENSE_INFINITE,
@@ -2098,18 +2201,27 @@ const generateQueryHook = async (verbOptions, options, outputClient, adapter) =>
2098
2201
  if (impl.includes("=")) return impl;
2099
2202
  return impl.replace(/^(\w+):\s*/, "$1?: ");
2100
2203
  };
2101
- const queryKeyProps = toObjectString(props.filter((prop) => prop.type !== GetterPropType.HEADER).map((prop) => ({
2102
- ...prop,
2103
- implementation: prop.type === GetterPropType.PARAM || prop.type === GetterPropType.NAMED_PATH_PARAMS ? prop.implementation : makeOptionalParam(prop.implementation)
2104
- })), "implementation");
2204
+ const queryKeyProps = wrapPropsBodyWithMutatorBodyType({
2205
+ propsString: toObjectString(props.filter((prop) => prop.type !== GetterPropType.HEADER).map((prop) => ({
2206
+ ...prop,
2207
+ implementation: prop.type === GetterPropType.PARAM || prop.type === GetterPropType.NAMED_PATH_PARAMS ? prop.implementation : makeOptionalParam(prop.implementation)
2208
+ })), "implementation"),
2209
+ body,
2210
+ mutator
2211
+ });
2105
2212
  const routeString = adapter.getQueryKeyRouteString(route, !!override.query.shouldSplitQueryKey);
2106
2213
  const queryKeyIdentifier = override.query.useOperationIdAsQueryKey ? `"${operationName}"` : routeString;
2107
2214
  const queryKeyParams = props.filter((p) => override.query.useOperationIdAsQueryKey ? true : p.type === GetterPropType.QUERY_PARAM).toSorted((a) => a.required ? -1 : 1).map((p) => `...(${p.name} ? [${p.name}] : [])`).join(", ");
2215
+ const verbPrefix = getQueryKeyVerbPrefix({
2216
+ verb,
2217
+ useOperationIdAsQueryKey: override.query.useOperationIdAsQueryKey
2218
+ });
2108
2219
  queryKeyFns += `
2109
2220
  ${override.query.shouldExportQueryKey ? "export " : ""}const ${queryOption.queryKeyFnName} = (${queryKeyProps}) => {
2110
2221
  return [
2111
2222
  ${[
2112
2223
  queryOption.type === QueryType.INFINITE || queryOption.type === QueryType.SUSPENSE_INFINITE ? `'infinite'` : "",
2224
+ verbPrefix ? `'${verbPrefix}'` : "",
2113
2225
  queryKeyIdentifier,
2114
2226
  queryKeyParams,
2115
2227
  body.implementation
@@ -2128,6 +2240,7 @@ ${queryKeyFns}`;
2128
2240
  queryKeyProperties,
2129
2241
  params,
2130
2242
  props,
2243
+ body,
2131
2244
  mutator,
2132
2245
  isRequestOptions,
2133
2246
  queryParams,
@@ -2141,8 +2254,8 @@ ${queryKeyFns}`;
2141
2254
  route,
2142
2255
  doc,
2143
2256
  usePrefetch: query.usePrefetch,
2144
- useQuery: query.useQuery,
2145
- useInfinite: query.useInfinite,
2257
+ useQuery: effectiveUseQuery,
2258
+ useInfinite: effectiveUseInfinite,
2146
2259
  useInvalidate: query.useInvalidate,
2147
2260
  useSetQueryData: operationQueryOptions?.useSetQueryData ?? query.useSetQueryData,
2148
2261
  useGetQueryData: operationQueryOptions?.useGetQueryData ?? query.useGetQueryData,
@@ -2213,10 +2326,12 @@ const generateQuery = async (verbOptions, options, outputClient) => {
2213
2326
  const imports = generateVerbImports(normalizedVerbOptions);
2214
2327
  const functionImplementation = adapter.generateRequestFunction(normalizedVerbOptions, options);
2215
2328
  const { implementation: hookImplementation, imports: hookImports, mutators } = await generateQueryHook(normalizedVerbOptions, options, outputClient, adapter);
2329
+ const isFetchHttpClient = options.context.output.httpClient !== OutputHttpClient.AXIOS;
2216
2330
  return {
2217
2331
  implementation: `${functionImplementation}\n\n${hookImplementation}`,
2218
2332
  imports: [...imports, ...hookImports],
2219
- mutators
2333
+ mutators,
2334
+ ...isFetchHttpClient && { docComment: "" }
2220
2335
  };
2221
2336
  };
2222
2337
  const dependenciesBuilder = {