@kubb/plugin-react-query 5.3.0-canary.20261012T190229 → 5.3.1

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/OVERVIEW.md ADDED
@@ -0,0 +1,20 @@
1
+ # React Query
2
+
3
+ Generates TanStack Query helpers for React from your OpenAPI spec. By default you get `queryOptions` and `mutationOptions` factories with their cache keys. Set `hooks: true` to also get `useQuery`, `useSuspenseQuery`, `useInfiniteQuery`, and `useMutation` hooks.
4
+
5
+ The plugin ships no HTTP client. Each helper calls a function generated by a client plugin.
6
+
7
+ ## Setup
8
+
9
+ - Add `@kubb/plugin-ts` and a client plugin: `@kubb/plugin-fetch` or `@kubb/plugin-axios`.
10
+ - Install `@tanstack/react-query` v5 or higher in the app that uses the generated code.
11
+ - Configure validation on the client plugin, not here.
12
+
13
+ ## Options worth knowing
14
+
15
+ - `client` picks `fetch` or `axios` when both client plugins are registered. A single client is detected automatically.
16
+ - `hooks` adds the `use*` hooks on top of the option factories.
17
+ - `suspense` adds `useSuspenseQuery` hooks.
18
+ - `infinite` adds infinite queries for pagination. Set `infinite.queryParam` to the query parameter that holds the page cursor.
19
+ - `query` and `mutation` set which HTTP methods become queries (`GET`) and mutations (`POST`, `PUT`, `PATCH`, `DELETE`), and where the TanStack Query runtime is imported from. Set either to `false` to skip it.
20
+ - `customOptions` routes every hook through your own function that returns extra options.
package/dist/index.cjs CHANGED
@@ -350,8 +350,14 @@ function createGroupConfig(group) {
350
350
  };
351
351
  }
352
352
  //#endregion
353
+ //#region kubb-extension.json
354
+ var compatibility = {
355
+ "kubb": ">=5.8.0",
356
+ "node": ">=22"
357
+ };
358
+ //#endregion
353
359
  //#region package.json
354
- var version = "5.3.0-canary.20261012T190229";
360
+ var version = "5.3.1";
355
361
  //#endregion
356
362
  //#region ../../internals/client/src/resolveClient.ts
357
363
  /**
@@ -691,21 +697,60 @@ function classifyOperation(node, { query, mutation }) {
691
697
  };
692
698
  }
693
699
  /**
700
+ * Helper to define a typed page param function for `infinite.getNextPageParam` or
701
+ * `infinite.getPreviousPageParam`. Serializes the function to a string for inlining in generated code.
702
+ *
703
+ * Note: Because the function is inlined verbatim into the generated file via `.toString()`,
704
+ * it must be self-contained and cannot reference local closures or external variables from
705
+ * the config file.
706
+ */
707
+ function definePageParam(fn) {
708
+ return fn.toString();
709
+ }
710
+ /**
694
711
  * Applies the shared infinite-query defaults during plugin setup: a falsy value disables infinite
695
712
  * queries, and an object merges over `queryParam: 'id'` and `initialPageParam: 0` with the cursor
696
713
  * paths cleared.
697
714
  */
698
715
  function resolveInfiniteConfig(infinite) {
699
716
  if (!infinite) return false;
717
+ const hasExplicitInitialPageParam = infinite.hasExplicitInitialPageParam ?? infinite.initialPageParam !== void 0;
718
+ const getNextPageParam = infinite.getNextPageParam ? typeof infinite.getNextPageParam === "function" ? infinite.getNextPageParam.toString() : infinite.getNextPageParam : null;
719
+ const getPreviousPageParam = infinite.getPreviousPageParam ? typeof infinite.getPreviousPageParam === "function" ? infinite.getPreviousPageParam.toString() : infinite.getPreviousPageParam : null;
700
720
  return {
701
721
  queryParam: "id",
702
722
  initialPageParam: 0,
723
+ hasExplicitInitialPageParam,
703
724
  cursorParam: null,
704
725
  nextParam: null,
705
726
  previousParam: null,
706
- ...infinite
727
+ getNextPageParam,
728
+ getPreviousPageParam,
729
+ ...infinite,
730
+ ...getNextPageParam ? { getNextPageParam } : {},
731
+ ...getPreviousPageParam ? { getPreviousPageParam } : {}
707
732
  };
708
733
  }
734
+ /**
735
+ * Whether the infinite hook for `node` should be generated: the operation takes the configured
736
+ * `queryParam`.
737
+ */
738
+ function matchesInfinite(node, infinite) {
739
+ if (!infinite.queryParam) return false;
740
+ return getOperationParameters(node).query.some((param) => param.name.replace(/\?$/, "") === infinite.queryParam);
741
+ }
742
+ /**
743
+ * Warning for an infinite hook that falls back to the built-in page params on a response that
744
+ * isn't an array. Those params only stop on an empty array, so `hasNextPage` never turns false.
745
+ */
746
+ function getDefaultPageParamsWarning(node, infinite) {
747
+ if (!(infinite.getNextPageParam == null && infinite.getPreviousPageParam == null && infinite.nextParam == null && infinite.previousParam == null && !infinite.cursorParam)) return null;
748
+ const content = getPrimarySuccessResponse(node)?.content;
749
+ let resolved = (content?.find((item) => item.contentType?.includes("json")) ?? content?.[0])?.schema;
750
+ while (resolved?.type === "ref" || resolved?.type === "intersection" && resolved.members?.length === 1) resolved = resolved.type === "ref" ? resolved.schema : resolved.members?.[0];
751
+ if (!resolved || resolved.type === "array") return null;
752
+ return `${node.operationId}: the default infinite page params expect an array response and never end for this one. Set \`infinite.getNextPageParam\` (or \`nextParam\`) to read the next page from the response.`;
753
+ }
709
754
  function matchesPattern(node, ov) {
710
755
  const { type, pattern } = ov;
711
756
  const matches = (value) => typeof pattern === "string" ? value === pattern : pattern.test(value);
@@ -763,7 +808,7 @@ function buildQueryKeyParams(node, options) {
763
808
  //#region ../../internals/tanstack-query/src/components/InfiniteQueryOptions.tsx
764
809
  const declarationPrinter$7 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
765
810
  const callPrinter$3 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "call" });
766
- function InfiniteQueryOptions({ name, clientName, initialPageParam, cursorParam, nextParam, previousParam, node, tsResolver, queryParam, queryKeyName, queryKeyType = "typeof queryKey", memberTypeWrapper, unwrapName, returnType = "full" }) {
811
+ function InfiniteQueryOptions({ name, clientName, initialPageParam, hasExplicitInitialPageParam, cursorParam, nextParam, previousParam, getNextPageParam, getPreviousPageParam, node, tsResolver, queryParam, queryKeyName, queryKeyType = "typeof queryKey", memberTypeWrapper, unwrapName, returnType = "full" }) {
767
812
  const { TData: queryFnDataType, TError: errorType } = buildResponseTypes(node, tsResolver);
768
813
  const { queryParamsTypeName, pageParamType } = resolvePageParamType(node, {
769
814
  resolver: tsResolver,
@@ -787,18 +832,25 @@ function InfiniteQueryOptions({ name, clientName, initialPageParam, cursorParam,
787
832
  signal: true,
788
833
  unwrapName
789
834
  }), { returnType });
790
- const hasNewParams = nextParam != null || previousParam != null;
791
- const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
792
- if (hasNewParams) {
793
- const nextAccessor = nextParam ? getNestedAccessor(nextParam, "lastPage") : null;
794
- const prevAccessor = previousParam ? getNestedAccessor(previousParam, "firstPage") : null;
795
- return [nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null, prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null];
796
- }
797
- if (cursorParam) return [`getNextPageParam: (lastPage) => lastPage['${cursorParam}']`, `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`];
798
- return ["getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1", "getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1"];
835
+ const initialPageParamLiteral = typeof initialPageParam === "string" || typeof initialPageParam === "object" && initialPageParam !== null ? JSON.stringify(initialPageParam) : String(initialPageParam === void 0 ? 0 : initialPageParam);
836
+ const serializedGetNextPageParam = typeof getNextPageParam === "function" ? getNextPageParam.toString() : getNextPageParam;
837
+ const getNextPageParamExpr = (() => {
838
+ if (serializedGetNextPageParam) return `getNextPageParam: ${serializedGetNextPageParam}`;
839
+ if (nextParam) return `getNextPageParam: (lastPage) => ${getNestedAccessor(nextParam, "lastPage")}`;
840
+ if (cursorParam) return `getNextPageParam: (lastPage) => lastPage['${cursorParam}']`;
841
+ return "getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1";
842
+ })();
843
+ const serializedGetPreviousPageParam = typeof getPreviousPageParam === "function" ? getPreviousPageParam.toString() : getPreviousPageParam;
844
+ const defaultPreviousThreshold = hasExplicitInitialPageParam ? initialPageParamLiteral : "1";
845
+ const getPreviousPageParamExpr = (() => {
846
+ if (serializedGetPreviousPageParam) return `getPreviousPageParam: ${serializedGetPreviousPageParam}`;
847
+ if (previousParam) return `getPreviousPageParam: (firstPage) => ${getNestedAccessor(previousParam, "firstPage")}`;
848
+ if (cursorParam) return `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`;
849
+ if (serializedGetNextPageParam || nextParam) return null;
850
+ return `getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= ${defaultPreviousThreshold} ? undefined : firstPageParam - 1`;
799
851
  })();
800
852
  const queryOptionsArr = [
801
- `initialPageParam: ${typeof initialPageParam === "string" ? JSON.stringify(initialPageParam) : initialPageParam}`,
853
+ `initialPageParam: ${initialPageParamLiteral}`,
802
854
  getNextPageParamExpr,
803
855
  getPreviousPageParamExpr
804
856
  ].filter(Boolean);
@@ -1549,8 +1601,7 @@ const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1549
1601
  });
1550
1602
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1551
1603
  if (!isQuery || isMutation || !infiniteOptions || !hooks) return false;
1552
- const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
1553
- return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
1604
+ return matchesInfinite(operationNode, infiniteOptions);
1554
1605
  },
1555
1606
  operation(node, ctx) {
1556
1607
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
@@ -1560,6 +1611,8 @@ const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1560
1611
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1561
1612
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1562
1613
  if (!infiniteOptions) return null;
1614
+ const pageParamsWarning = getDefaultPageParamsWarning(node, infiniteOptions);
1615
+ if (pageParamsWarning) ctx.warn(pageParamsWarning);
1563
1616
  const importPath = query ? query.importPath : "@tanstack/react-query";
1564
1617
  const contractOp = resolveClientOperation({
1565
1618
  clientPlugin: { pluginName: client.pluginName },
@@ -1673,7 +1726,10 @@ const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1673
1726
  cursorParam: infiniteOptions.cursorParam,
1674
1727
  nextParam: infiniteOptions.nextParam,
1675
1728
  previousParam: infiniteOptions.previousParam,
1729
+ getNextPageParam: infiniteOptions.getNextPageParam,
1730
+ getPreviousPageParam: infiniteOptions.getPreviousPageParam,
1676
1731
  initialPageParam: infiniteOptions.initialPageParam,
1732
+ hasExplicitInitialPageParam: infiniteOptions.hasExplicitInitialPageParam,
1677
1733
  queryParam: infiniteOptions.queryParam,
1678
1734
  returnType: contractOp.returnType
1679
1735
  }),
@@ -2027,8 +2083,7 @@ const suspenseInfiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
2027
2083
  });
2028
2084
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
2029
2085
  if (!isQuery || isMutation || !suspense || !infiniteOptions || !hooks) return false;
2030
- const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
2031
- return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
2086
+ return matchesInfinite(operationNode, infiniteOptions);
2032
2087
  },
2033
2088
  operation(node, ctx) {
2034
2089
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
@@ -2068,12 +2123,16 @@ const suspenseInfiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
2068
2123
  group: pluginTs.options?.group
2069
2124
  })
2070
2125
  };
2071
- const rawQueryParams = getOperationParameters(node).query;
2072
- const queryParamsTypeName = rawQueryParams.length > 0 && tsResolver.param.query(node, rawQueryParams[0]) !== tsResolver.param.name(node, rawQueryParams[0]) ? tsResolver.param.query(node, rawQueryParams[0]) : null;
2126
+ const { queryParamsTypeName } = resolvePageParamType(node, {
2127
+ resolver: tsResolver,
2128
+ initialPageParam: infiniteOptions.initialPageParam,
2129
+ queryParam: infiniteOptions.queryParam
2130
+ });
2073
2131
  const importedTypeNames = [
2074
2132
  tsResolver.response.options(node),
2075
2133
  queryParamsTypeName,
2076
2134
  ...resolveOperationTypeNames(node, tsResolver, {
2135
+ exclude: [queryKeyTypeName],
2077
2136
  order: "body-response-first",
2078
2137
  includeParams: false
2079
2138
  })
@@ -2147,7 +2206,10 @@ const suspenseInfiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
2147
2206
  cursorParam: infiniteOptions.cursorParam,
2148
2207
  nextParam: infiniteOptions.nextParam,
2149
2208
  previousParam: infiniteOptions.previousParam,
2209
+ getNextPageParam: infiniteOptions.getNextPageParam,
2210
+ getPreviousPageParam: infiniteOptions.getPreviousPageParam,
2150
2211
  initialPageParam: infiniteOptions.initialPageParam,
2212
+ hasExplicitInitialPageParam: infiniteOptions.hasExplicitInitialPageParam,
2151
2213
  queryParam: infiniteOptions.queryParam,
2152
2214
  returnType: contractOp.returnType
2153
2215
  }),
@@ -2416,7 +2478,7 @@ const pluginReactQuery = (0, kubb_kit.definePlugin)({
2416
2478
  meta: {
2417
2479
  name: pluginReactQueryName,
2418
2480
  version,
2419
- compatibility: { kubb: ">=5.8.0" }
2481
+ compatibility
2420
2482
  },
2421
2483
  dependencies: { [_kubb_plugin_ts.pluginTsName]: {} },
2422
2484
  setup(options) {
@@ -2455,6 +2517,7 @@ const pluginReactQuery = (0, kubb_kit.definePlugin)({
2455
2517
  });
2456
2518
  //#endregion
2457
2519
  exports.default = pluginReactQuery;
2520
+ exports.definePageParam = definePageParam;
2458
2521
  exports.pluginReactQuery = pluginReactQuery;
2459
2522
  exports.pluginReactQueryName = pluginReactQueryName;
2460
2523
  exports.resolverReactQuery = resolverReactQuery;