@kubb/plugin-vue-query 5.0.0-beta.77 → 5.0.0-beta.80

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.d.ts CHANGED
@@ -10,7 +10,7 @@ import { Exclude, Group, Include, Output, OutputOptions, Override, PluginFactory
10
10
  * body and query parameters before the call; `response` validates the success response body and,
11
11
  * on the non-throw path, the error body.
12
12
  */
13
- type ParserOptions = false | 'zod' | {
13
+ type ValidatorOptions = false | 'zod' | {
14
14
  request?: 'zod';
15
15
  response?: 'zod';
16
16
  };
@@ -82,7 +82,7 @@ type Options$1 = OutputOptions & {
82
82
  *
83
83
  * @default false
84
84
  */
85
- parser?: ParserOptions;
85
+ validator?: ValidatorOptions;
86
86
  /**
87
87
  * Generates a class-based SDK instead of the standalone functions. Each tag client is an instance
88
88
  * class whose constructor takes a client config and builds its own client, so every environment is
@@ -148,7 +148,7 @@ type ResolvedOptions$1 = {
148
148
  override: Array<Override<ResolvedOptions$1>>;
149
149
  group: Group | null;
150
150
  baseURL: Options$1['baseURL'];
151
- parser: NonNullable<Options$1['parser']>;
151
+ validator: NonNullable<Options$1['validator']>;
152
152
  sdk: {
153
153
  mode: Mode;
154
154
  name: string | undefined;
@@ -382,11 +382,18 @@ type Options = OutputOptions & {
382
382
  * - `'client'` — no validation.
383
383
  * - `'zod'` — pipes responses through schemas from `@kubb/plugin-zod`.
384
384
  */
385
- parser?: Options$1['parser'];
385
+ validator?: Options$1['validator'];
386
386
  /**
387
387
  * Override how composable names and file paths are built.
388
388
  */
389
389
  resolver?: Partial<ResolverVueQuery> & ThisType<ResolverVueQuery>;
390
+ /**
391
+ * Set to `false` to skip generating `use*` composable functions. `queryOptions`,
392
+ * `mutationOptions`, `queryKey`, and `mutationKey` helpers are still emitted.
393
+ *
394
+ * @default false
395
+ */
396
+ hooks?: boolean;
390
397
  /**
391
398
  * Macros applied to each operation node before printing.
392
399
  */
@@ -410,7 +417,7 @@ type ResolvedOptions = {
410
417
  * The resolved contract client the generators import and call.
411
418
  */
412
419
  client: ResolvedClient;
413
- parser: NonNullable<Options['parser']>;
420
+ validator: NonNullable<Options['validator']>;
414
421
  /**
415
422
  * Only used for infinite
416
423
  */
@@ -419,6 +426,7 @@ type ResolvedOptions = {
419
426
  query: NonNullable<Required<Query>> | false;
420
427
  mutationKey: MutationKey | null;
421
428
  mutation: NonNullable<Required<Mutation>> | false;
429
+ hooks: boolean;
422
430
  resolver: ResolverVueQuery;
423
431
  };
424
432
  type PluginVueQuery = PluginFactoryOptions<'plugin-vue-query', Options, ResolvedOptions, ResolverVueQuery>;
package/dist/index.js CHANGED
@@ -63,7 +63,7 @@ function toFilePath(name, caseLast = camelCase) {
63
63
  * JavaScript and Java reserved words.
64
64
  * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
65
65
  */
66
- const reservedWords = new Set([
66
+ const reservedWords = /* @__PURE__ */ new Set([
67
67
  "abstract",
68
68
  "arguments",
69
69
  "boolean",
@@ -292,6 +292,23 @@ function caseParams(params, casing) {
292
292
  caseParamsCache.set(params, result);
293
293
  return result;
294
294
  }
295
+ /**
296
+ * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
297
+ *
298
+ * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
299
+ * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
300
+ * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
301
+ * identity so the resulting group is collision-free regardless of the names each caller carries.
302
+ */
303
+ function dedupeByCasedName(params) {
304
+ const seen = /* @__PURE__ */ new Set();
305
+ return params.filter((param) => {
306
+ const key = camelCase(param.name);
307
+ if (seen.has(key)) return false;
308
+ seen.add(key);
309
+ return true;
310
+ });
311
+ }
295
312
  //#endregion
296
313
  //#region ../../internals/shared/src/operation.ts
297
314
  /**
@@ -330,11 +347,27 @@ function getContentTypeInfo(node) {
330
347
  hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
331
348
  };
332
349
  }
350
+ /**
351
+ * The request-body counterpart for the primary success response: the content types it documents and
352
+ * whether several are present, so the client can let a caller pick which one to accept.
353
+ */
354
+ function getResponseContentTypeInfo(node) {
355
+ const contentTypes = getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [];
356
+ const isMultipleContentTypes = contentTypes.length > 1;
357
+ return {
358
+ contentTypes,
359
+ isMultipleContentTypes,
360
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
361
+ defaultContentType: contentTypes[0] ?? "application/json",
362
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
363
+ };
364
+ }
333
365
  function buildRequestConfigType(node) {
334
- const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
366
+ const request = getContentTypeInfo(node);
367
+ const response = getResponseContentTypeInfo(node);
335
368
  const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
336
- const contentTypeProp = isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null;
337
- return contentTypeProp ? `${configType} & { ${contentTypeProp} }` : configType;
369
+ const members = [request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null, response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null].filter(Boolean);
370
+ return members.length ? `${configType} & { contentType?: { ${members.join("; ")} } }` : configType;
338
371
  }
339
372
  /**
340
373
  * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,
@@ -396,10 +429,10 @@ function buildOperationComments(node, options = {}) {
396
429
  function getOperationParameters(node, options = {}) {
397
430
  const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
398
431
  return {
399
- path: params.filter((param) => param.in === "path"),
400
- query: params.filter((param) => param.in === "query"),
401
- header: params.filter((param) => param.in === "header"),
402
- cookie: params.filter((param) => param.in === "cookie")
432
+ path: dedupeByCasedName(params.filter((param) => param.in === "path")),
433
+ query: dedupeByCasedName(params.filter((param) => param.in === "query")),
434
+ header: dedupeByCasedName(params.filter((param) => param.in === "header")),
435
+ cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
403
436
  };
404
437
  }
405
438
  function getStatusCodeNumber(statusCode) {
@@ -414,6 +447,15 @@ function isErrorStatusCode(statusCode) {
414
447
  const code = getStatusCodeNumber(statusCode);
415
448
  return code !== null && code >= 400;
416
449
  }
450
+ function getSuccessResponses(responses) {
451
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
452
+ }
453
+ function getOperationSuccessResponses(node) {
454
+ return getSuccessResponses(node.responses);
455
+ }
456
+ function getPrimarySuccessResponse(node) {
457
+ return getOperationSuccessResponses(node)[0] ?? null;
458
+ }
417
459
  function resolveErrorNames(node, resolver) {
418
460
  return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
419
461
  }
@@ -487,14 +529,14 @@ function createGroupConfig(group) {
487
529
  };
488
530
  }
489
531
  //#endregion
490
- //#region ../../internals/client/src/builders/parser.ts
532
+ //#region ../../internals/client/src/builders/validatorOptions.ts
491
533
  /**
492
- * Returns `true` when any direction of the parser uses zod (used for dependency checks).
534
+ * Returns `true` when any direction of the validator uses zod (used for dependency checks).
493
535
  */
494
- function isParserEnabled(parser) {
495
- if (!parser) return false;
496
- if (parser === "zod") return true;
497
- return Boolean(parser.request || parser.response);
536
+ function isValidatorEnabled(validator) {
537
+ if (!validator) return false;
538
+ if (validator === "zod") return true;
539
+ return Boolean(validator.request || validator.response);
498
540
  }
499
541
  //#endregion
500
542
  //#region ../../internals/client/src/resolveClient.ts
@@ -629,6 +671,19 @@ const requestGroupOrder$1 = [
629
671
  "headers"
630
672
  ];
631
673
  /**
674
+ * Widens a request-group member type so a generated TanStack hook accepts either the value or a
675
+ * deferred value. Both plugins apply this through the `memberTypeWrapper` of `buildGroupedRequestParam`;
676
+ * they only differ in how the deferred form is later resolved.
677
+ *
678
+ * `maybeRefOrGetter` is used by vue-query, which keeps the ref/getter live and unwraps it lazily with
679
+ * `toValue` because vue-query keys are reactive. `maybeValueOrGetter` is used by react-query, which
680
+ * resolves the getter once at the hook boundary and forwards a plain value because React Query hashes
681
+ * keys structurally and cannot hold a function.
682
+ */
683
+ function maybeRefOrGetter(type) {
684
+ return `MaybeRefOrGetter<${type}>`;
685
+ }
686
+ /**
632
687
  * Builds the grouped `{ path, query, body, headers }` parameter that mirrors the client
633
688
  * function signature. Only the groups the operation carries are emitted, typed from the
634
689
  * operation's `RequestConfig`. `keys` narrows the emitted groups, used by the query key which
@@ -750,12 +805,6 @@ const queryKeyTransformer = ({ node }) => {
750
805
  };
751
806
  //#endregion
752
807
  //#region src/utils.ts
753
- /**
754
- * Wraps a type string in `MaybeRefOrGetter<…>` so a vue-query signature accepts refs or getters.
755
- */
756
- function maybeRefOrGetter(type) {
757
- return `MaybeRefOrGetter<${type}>`;
758
- }
759
808
  const requestGroupOrder = [
760
809
  "path",
761
810
  "query",
@@ -1199,7 +1248,7 @@ const infiniteQueryGenerator = defineGenerator({
1199
1248
  operation(node, ctx) {
1200
1249
  if (!ast.isHttpOperationNode(node)) return null;
1201
1250
  const { config, driver, resolver, root } = ctx;
1202
- const { output, query, mutation, infinite, parser, client, group } = ctx.options;
1251
+ const { output, query, mutation, infinite, validator, client, group, hooks } = ctx.options;
1203
1252
  const pluginTs = driver.getPlugin(pluginTsName);
1204
1253
  if (!pluginTs) return null;
1205
1254
  const tsResolver = driver.getResolver(pluginTsName);
@@ -1249,14 +1298,14 @@ const infiniteQueryGenerator = defineGenerator({
1249
1298
  includeParams: false
1250
1299
  })
1251
1300
  ].filter((name) => Boolean(name));
1252
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
1301
+ const pluginZod = isValidatorEnabled(validator) ? driver.getPlugin(pluginZodName) : null;
1253
1302
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1254
1303
  const fileZod = zodResolver ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1255
1304
  root,
1256
1305
  output: pluginZod?.options?.output ?? output,
1257
1306
  group: pluginZod?.options?.group ?? void 0
1258
1307
  }) : null;
1259
- const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser);
1308
+ const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, validator);
1260
1309
  const calledClientName = contractOp.name;
1261
1310
  return /* @__PURE__ */ jsxs(File, {
1262
1311
  baseName: meta.file.baseName,
@@ -1338,30 +1387,32 @@ const infiniteQueryGenerator = defineGenerator({
1338
1387
  initialPageParam: infiniteOptions.initialPageParam,
1339
1388
  queryParam: infiniteOptions.queryParam
1340
1389
  }),
1341
- /* @__PURE__ */ jsx(File.Import, {
1342
- name: ["useInfiniteQuery"],
1343
- path: importPath
1344
- }),
1345
- /* @__PURE__ */ jsx(File.Import, {
1346
- name: [
1347
- "QueryKey",
1348
- "QueryClient",
1349
- "UseInfiniteQueryOptions",
1350
- "UseInfiniteQueryReturnType"
1351
- ],
1352
- path: importPath,
1353
- isTypeOnly: true
1354
- }),
1355
- /* @__PURE__ */ jsx(InfiniteQuery, {
1356
- name: queryName,
1357
- queryOptionsName,
1358
- queryKeyName,
1359
- queryKeyTypeName,
1360
- node,
1361
- tsResolver,
1362
- initialPageParam: infiniteOptions.initialPageParam,
1363
- queryParam: infiniteOptions.queryParam
1364
- })
1390
+ hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
1391
+ /* @__PURE__ */ jsx(File.Import, {
1392
+ name: ["useInfiniteQuery"],
1393
+ path: importPath
1394
+ }),
1395
+ /* @__PURE__ */ jsx(File.Import, {
1396
+ name: [
1397
+ "QueryKey",
1398
+ "QueryClient",
1399
+ "UseInfiniteQueryOptions",
1400
+ "UseInfiniteQueryReturnType"
1401
+ ],
1402
+ path: importPath,
1403
+ isTypeOnly: true
1404
+ }),
1405
+ /* @__PURE__ */ jsx(InfiniteQuery, {
1406
+ name: queryName,
1407
+ queryOptionsName,
1408
+ queryKeyName,
1409
+ queryKeyTypeName,
1410
+ node,
1411
+ tsResolver,
1412
+ initialPageParam: infiniteOptions.initialPageParam,
1413
+ queryParam: infiniteOptions.queryParam
1414
+ })
1415
+ ] })
1365
1416
  ]
1366
1417
  });
1367
1418
  }
@@ -1379,7 +1430,7 @@ const mutationGenerator = defineGenerator({
1379
1430
  operation(node, ctx) {
1380
1431
  if (!ast.isHttpOperationNode(node)) return null;
1381
1432
  const { config, driver, resolver, root } = ctx;
1382
- const { output, query, mutation, parser, client, group } = ctx.options;
1433
+ const { output, query, mutation, validator, client, group, hooks } = ctx.options;
1383
1434
  const pluginTs = driver.getPlugin(pluginTsName);
1384
1435
  if (!pluginTs) return null;
1385
1436
  const tsResolver = driver.getResolver(pluginTsName);
@@ -1414,14 +1465,14 @@ const mutationGenerator = defineGenerator({
1414
1465
  order: "body-response-first",
1415
1466
  includeParams: false
1416
1467
  })].filter((name) => Boolean(name));
1417
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
1468
+ const pluginZod = isValidatorEnabled(validator) ? driver.getPlugin(pluginZodName) : null;
1418
1469
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1419
1470
  const fileZod = zodResolver ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1420
1471
  root,
1421
1472
  output: pluginZod?.options?.output ?? output,
1422
1473
  group: pluginZod?.options?.group ?? void 0
1423
1474
  }) : null;
1424
- const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser);
1475
+ const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, validator);
1425
1476
  const calledClientName = contractOp.name;
1426
1477
  const groups = getRequestGroups(node);
1427
1478
  const hasRequestGroups = groups.path || groups.query || groups.body || groups.headers;
@@ -1482,7 +1533,7 @@ const mutationGenerator = defineGenerator({
1482
1533
  node,
1483
1534
  transformer: ctx.options.mutationKey
1484
1535
  }),
1485
- mutation && /* @__PURE__ */ jsxs(Fragment, { children: [
1536
+ mutation && hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
1486
1537
  /* @__PURE__ */ jsx(File.Import, {
1487
1538
  name: ["useMutation"],
1488
1539
  path: importPath
@@ -1518,7 +1569,7 @@ const queryGenerator = defineGenerator({
1518
1569
  operation(node, ctx) {
1519
1570
  if (!ast.isHttpOperationNode(node)) return null;
1520
1571
  const { config, driver, resolver, root } = ctx;
1521
- const { output, query, mutation, parser, client, group } = ctx.options;
1572
+ const { output, query, mutation, validator, client, group, hooks } = ctx.options;
1522
1573
  const pluginTs = driver.getPlugin(pluginTsName);
1523
1574
  if (!pluginTs) return null;
1524
1575
  const tsResolver = driver.getResolver(pluginTsName);
@@ -1556,14 +1607,14 @@ const queryGenerator = defineGenerator({
1556
1607
  order: "body-response-first",
1557
1608
  includeParams: false
1558
1609
  })].filter((name) => Boolean(name));
1559
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
1610
+ const pluginZod = isValidatorEnabled(validator) ? driver.getPlugin(pluginZodName) : null;
1560
1611
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1561
1612
  const fileZod = zodResolver ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1562
1613
  root,
1563
1614
  output: pluginZod?.options?.output ?? output,
1564
1615
  group: pluginZod?.options?.group ?? void 0
1565
1616
  }) : null;
1566
- const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser);
1617
+ const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, validator);
1567
1618
  const calledClientName = contractOp.name;
1568
1619
  return /* @__PURE__ */ jsxs(File, {
1569
1620
  baseName: meta.file.baseName,
@@ -1635,7 +1686,7 @@ const queryGenerator = defineGenerator({
1635
1686
  node,
1636
1687
  tsResolver
1637
1688
  }),
1638
- query && /* @__PURE__ */ jsxs(Fragment, { children: [
1689
+ query && hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
1639
1690
  /* @__PURE__ */ jsx(File.Import, {
1640
1691
  name: ["useQuery"],
1641
1692
  path: importPath
@@ -1771,7 +1822,7 @@ const pluginVueQuery = definePlugin((options) => {
1771
1822
  const { output = {
1772
1823
  path: "hooks",
1773
1824
  barrel: { type: "named" }
1774
- }, group, exclude = [], include, override = [], parser = false, infinite = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, client, resolver: userResolver, macros: userMacros } = options;
1825
+ }, group, exclude = [], include, override = [], validator = false, infinite = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, hooks = false, client, resolver: userResolver, macros: userMacros } = options;
1775
1826
  const selectedGenerators = [
1776
1827
  queryGenerator,
1777
1828
  infiniteQueryGenerator,
@@ -1781,7 +1832,7 @@ const pluginVueQuery = definePlugin((options) => {
1781
1832
  return {
1782
1833
  name: pluginVueQueryName,
1783
1834
  options,
1784
- dependencies: [pluginTsName, isParserEnabled(parser) ? pluginZodName : void 0].filter((dependency) => Boolean(dependency)),
1835
+ dependencies: [pluginTsName, isValidatorEnabled(validator) ? pluginZodName : void 0].filter((dependency) => Boolean(dependency)),
1785
1836
  hooks: { "kubb:plugin:setup"(ctx) {
1786
1837
  const resolver = userResolver ? {
1787
1838
  ...resolverVueQuery,
@@ -1824,7 +1875,8 @@ const pluginVueQuery = definePlugin((options) => {
1824
1875
  previousParam: null,
1825
1876
  ...infinite
1826
1877
  } : false,
1827
- parser,
1878
+ hooks,
1879
+ validator,
1828
1880
  group: groupConfig,
1829
1881
  exclude,
1830
1882
  include,