@kubb/plugin-vue-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,18 @@
1
+ # Vue Query
2
+
3
+ Generates TanStack Query helpers for Vue from your OpenAPI spec. By default you get `queryOptions` factories with their query and mutation keys. Set `hooks: true` to also get `useQuery`, `useInfiniteQuery`, and `useMutation` composables with reactive parameters.
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/vue-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*` composables on top of the option factories.
17
+ - `infinite` adds infinite queries for pagination. Set `infinite.queryParam` to the query parameter that holds the page cursor.
18
+ - `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.
package/dist/index.cjs CHANGED
@@ -352,8 +352,14 @@ function createGroupConfig(group) {
352
352
  };
353
353
  }
354
354
  //#endregion
355
+ //#region kubb-extension.json
356
+ var compatibility = {
357
+ "kubb": ">=5.8.0",
358
+ "node": ">=22"
359
+ };
360
+ //#endregion
355
361
  //#region package.json
356
- var version = "5.3.0-canary.20261012T190229";
362
+ var version = "5.3.1";
357
363
  //#endregion
358
364
  //#region ../../internals/tanstack-query/src/utils.ts
359
365
  /**
@@ -571,22 +577,61 @@ function classifyOperation(node, { query, mutation }) {
571
577
  };
572
578
  }
573
579
  /**
580
+ * Helper to define a typed page param function for `infinite.getNextPageParam` or
581
+ * `infinite.getPreviousPageParam`. Serializes the function to a string for inlining in generated code.
582
+ *
583
+ * Note: Because the function is inlined verbatim into the generated file via `.toString()`,
584
+ * it must be self-contained and cannot reference local closures or external variables from
585
+ * the config file.
586
+ */
587
+ function definePageParam(fn) {
588
+ return fn.toString();
589
+ }
590
+ /**
574
591
  * Applies the shared infinite-query defaults during plugin setup: a falsy value disables infinite
575
592
  * queries, and an object merges over `queryParam: 'id'` and `initialPageParam: 0` with the cursor
576
593
  * paths cleared.
577
594
  */
578
595
  function resolveInfiniteConfig(infinite) {
579
596
  if (!infinite) return false;
597
+ const hasExplicitInitialPageParam = infinite.hasExplicitInitialPageParam ?? infinite.initialPageParam !== void 0;
598
+ const getNextPageParam = infinite.getNextPageParam ? typeof infinite.getNextPageParam === "function" ? infinite.getNextPageParam.toString() : infinite.getNextPageParam : null;
599
+ const getPreviousPageParam = infinite.getPreviousPageParam ? typeof infinite.getPreviousPageParam === "function" ? infinite.getPreviousPageParam.toString() : infinite.getPreviousPageParam : null;
580
600
  return {
581
601
  queryParam: "id",
582
602
  initialPageParam: 0,
603
+ hasExplicitInitialPageParam,
583
604
  cursorParam: null,
584
605
  nextParam: null,
585
606
  previousParam: null,
586
- ...infinite
607
+ getNextPageParam,
608
+ getPreviousPageParam,
609
+ ...infinite,
610
+ ...getNextPageParam ? { getNextPageParam } : {},
611
+ ...getPreviousPageParam ? { getPreviousPageParam } : {}
587
612
  };
588
613
  }
589
614
  /**
615
+ * Whether the infinite hook for `node` should be generated: the operation takes the configured
616
+ * `queryParam`.
617
+ */
618
+ function matchesInfinite(node, infinite) {
619
+ if (!infinite.queryParam) return false;
620
+ return getOperationParameters(node).query.some((param) => param.name.replace(/\?$/, "") === infinite.queryParam);
621
+ }
622
+ /**
623
+ * Warning for an infinite hook that falls back to the built-in page params on a response that
624
+ * isn't an array. Those params only stop on an empty array, so `hasNextPage` never turns false.
625
+ */
626
+ function getDefaultPageParamsWarning(node, infinite) {
627
+ if (!(infinite.getNextPageParam == null && infinite.getPreviousPageParam == null && infinite.nextParam == null && infinite.previousParam == null && !infinite.cursorParam)) return null;
628
+ const content = getPrimarySuccessResponse(node)?.content;
629
+ let resolved = (content?.find((item) => item.contentType?.includes("json")) ?? content?.[0])?.schema;
630
+ while (resolved?.type === "ref" || resolved?.type === "intersection" && resolved.members?.length === 1) resolved = resolved.type === "ref" ? resolved.schema : resolved.members?.[0];
631
+ if (!resolved || resolved.type === "array") return null;
632
+ 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.`;
633
+ }
634
+ /**
590
635
  * The request groups a query key is built from, matching `buildQueryKeyParams`. Headers are
591
636
  * deliberately absent: they do not identify a cache entry, so the key factory takes no parameter
592
637
  * for an operation that only carries headers.
@@ -600,7 +645,7 @@ const queryKeyGroupOrder = [
600
645
  //#region ../../internals/tanstack-query/src/components/InfiniteQueryOptions.tsx
601
646
  const declarationPrinter$7 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
602
647
  const callPrinter$4 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "call" });
603
- function InfiniteQueryOptions$1({ name, clientName, initialPageParam, cursorParam, nextParam, previousParam, node, tsResolver, queryParam, queryKeyName, queryKeyType = "typeof queryKey", memberTypeWrapper, unwrapName, returnType = "full" }) {
648
+ function InfiniteQueryOptions$1({ name, clientName, initialPageParam, hasExplicitInitialPageParam, cursorParam, nextParam, previousParam, getNextPageParam, getPreviousPageParam, node, tsResolver, queryParam, queryKeyName, queryKeyType = "typeof queryKey", memberTypeWrapper, unwrapName, returnType = "full" }) {
604
649
  const { TData: queryFnDataType, TError: errorType } = buildResponseTypes(node, tsResolver);
605
650
  const { queryParamsTypeName, pageParamType } = resolvePageParamType(node, {
606
651
  resolver: tsResolver,
@@ -624,18 +669,25 @@ function InfiniteQueryOptions$1({ name, clientName, initialPageParam, cursorPara
624
669
  signal: true,
625
670
  unwrapName
626
671
  }), { returnType });
627
- const hasNewParams = nextParam != null || previousParam != null;
628
- const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
629
- if (hasNewParams) {
630
- const nextAccessor = nextParam ? getNestedAccessor(nextParam, "lastPage") : null;
631
- const prevAccessor = previousParam ? getNestedAccessor(previousParam, "firstPage") : null;
632
- return [nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null, prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null];
633
- }
634
- if (cursorParam) return [`getNextPageParam: (lastPage) => lastPage['${cursorParam}']`, `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`];
635
- return ["getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1", "getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1"];
672
+ const initialPageParamLiteral = typeof initialPageParam === "string" || typeof initialPageParam === "object" && initialPageParam !== null ? JSON.stringify(initialPageParam) : String(initialPageParam === void 0 ? 0 : initialPageParam);
673
+ const serializedGetNextPageParam = typeof getNextPageParam === "function" ? getNextPageParam.toString() : getNextPageParam;
674
+ const getNextPageParamExpr = (() => {
675
+ if (serializedGetNextPageParam) return `getNextPageParam: ${serializedGetNextPageParam}`;
676
+ if (nextParam) return `getNextPageParam: (lastPage) => ${getNestedAccessor(nextParam, "lastPage")}`;
677
+ if (cursorParam) return `getNextPageParam: (lastPage) => lastPage['${cursorParam}']`;
678
+ return "getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1";
679
+ })();
680
+ const serializedGetPreviousPageParam = typeof getPreviousPageParam === "function" ? getPreviousPageParam.toString() : getPreviousPageParam;
681
+ const defaultPreviousThreshold = hasExplicitInitialPageParam ? initialPageParamLiteral : "1";
682
+ const getPreviousPageParamExpr = (() => {
683
+ if (serializedGetPreviousPageParam) return `getPreviousPageParam: ${serializedGetPreviousPageParam}`;
684
+ if (previousParam) return `getPreviousPageParam: (firstPage) => ${getNestedAccessor(previousParam, "firstPage")}`;
685
+ if (cursorParam) return `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`;
686
+ if (serializedGetNextPageParam || nextParam) return null;
687
+ return `getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= ${defaultPreviousThreshold} ? undefined : firstPageParam - 1`;
636
688
  })();
637
689
  const queryOptionsArr = [
638
- `initialPageParam: ${typeof initialPageParam === "string" ? JSON.stringify(initialPageParam) : initialPageParam}`,
690
+ `initialPageParam: ${initialPageParamLiteral}`,
639
691
  getNextPageParamExpr,
640
692
  getPreviousPageParamExpr
641
693
  ].filter(Boolean);
@@ -1225,21 +1277,28 @@ function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, t
1225
1277
  const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1226
1278
  name: "vue-query-infinite",
1227
1279
  renderer: kubb_jsx.jsxRenderer,
1280
+ match(node, ctx) {
1281
+ const operationNode = node;
1282
+ if (!kubb_kit.ast.isHttpOperationNode(operationNode)) return false;
1283
+ const { query, mutation, infinite, hooks } = ctx.options;
1284
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1285
+ query,
1286
+ mutation
1287
+ });
1288
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1289
+ if (!isQuery || isMutation || !infiniteOptions || !hooks) return false;
1290
+ return matchesInfinite(operationNode, infiniteOptions);
1291
+ },
1228
1292
  operation(node, ctx) {
1229
1293
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
1230
1294
  const { config, driver, resolver, root } = ctx;
1231
- const { output, query, mutation, infinite, client, group, hooks } = ctx.options;
1295
+ const { output, query, infinite, client, group } = ctx.options;
1296
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1297
+ if (!infiniteOptions) return null;
1298
+ const pageParamsWarning = getDefaultPageParamsWarning(node, infiniteOptions);
1299
+ if (pageParamsWarning) ctx.warn(pageParamsWarning);
1232
1300
  const pluginTs = ctx.requirePlugin(_kubb_plugin_ts.pluginTsName);
1233
1301
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1234
- const { isQuery, isMutation } = classifyOperation(node, {
1235
- query,
1236
- mutation
1237
- });
1238
- const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1239
- if (!isQuery || isMutation || !infiniteOptions || !hooks) return null;
1240
- const normalizeKey = (key) => key.replace(/\?$/, "");
1241
- const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1242
- if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1243
1302
  const importPath = query ? query.importPath : "@tanstack/vue-query";
1244
1303
  const contractOp = resolveClientOperation({
1245
1304
  clientPlugin: { pluginName: client.pluginName },
@@ -1358,7 +1417,10 @@ const infiniteQueryGenerator = (0, kubb_kit.defineGenerator)({
1358
1417
  cursorParam: infiniteOptions.cursorParam,
1359
1418
  nextParam: infiniteOptions.nextParam,
1360
1419
  previousParam: infiniteOptions.previousParam,
1420
+ getNextPageParam: infiniteOptions.getNextPageParam,
1421
+ getPreviousPageParam: infiniteOptions.getPreviousPageParam,
1361
1422
  initialPageParam: infiniteOptions.initialPageParam,
1423
+ hasExplicitInitialPageParam: infiniteOptions.hasExplicitInitialPageParam,
1362
1424
  queryParam: infiniteOptions.queryParam,
1363
1425
  returnType: contractOp.returnType
1364
1426
  }),
@@ -1741,7 +1803,7 @@ const pluginVueQuery = (0, kubb_kit.definePlugin)({
1741
1803
  meta: {
1742
1804
  name: pluginVueQueryName,
1743
1805
  version,
1744
- compatibility: { kubb: ">=5.8.0" }
1806
+ compatibility
1745
1807
  },
1746
1808
  dependencies: { [_kubb_plugin_ts.pluginTsName]: {} },
1747
1809
  setup(options) {
@@ -1775,6 +1837,7 @@ const pluginVueQuery = (0, kubb_kit.definePlugin)({
1775
1837
  });
1776
1838
  //#endregion
1777
1839
  exports.default = pluginVueQuery;
1840
+ exports.definePageParam = definePageParam;
1778
1841
  exports.pluginVueQuery = pluginVueQuery;
1779
1842
  exports.pluginVueQueryName = pluginVueQueryName;
1780
1843
  exports.resolverVueQuery = resolverVueQuery;