@kubb/plugin-fetch 5.3.0-canary.20260907T133426 → 5.3.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.d.ts CHANGED
@@ -22,6 +22,14 @@ type ValidatorOptions = false | 'zod' | {
22
22
  * - `'flat'`: one class with every operation as a direct method.
23
23
  */
24
24
  type Mode = 'tag' | 'flat';
25
+ /**
26
+ * Shape of the value a generated operation function resolves to.
27
+ * - `'full'`: the complete `{ status, data, error, contentType, request, response }` result.
28
+ * - `'data'`: the bare success body once `throwOnError` (on by default) narrows away the error
29
+ * branch, falling back to the full result when a call sets `throwOnError: false` and still
30
+ * needs `error` to discriminate a failed response.
31
+ */
32
+ type ReturnTypeOption = 'full' | 'data';
25
33
  /**
26
34
  * The resolver shared by the client plugins. Inherits the built-in camelCase `name` and `file`;
27
35
  * classes and tag groups use PascalCase (with a `Client` suffix for groups).
@@ -76,6 +84,20 @@ type Options = OutputOptions & {
76
84
  * @default false
77
85
  */
78
86
  validator?: ValidatorOptions;
87
+ /**
88
+ * Shape of the value a generated operation function resolves to. Applies to the standalone
89
+ * functions and the class-based SDK; not to the query-hook plugins (`@kubb/plugin-react-query`,
90
+ * `@kubb/plugin-vue-query`, `@kubb/plugin-swr`), which keep calling the client directly and
91
+ * expect the full result.
92
+ *
93
+ * @default 'full'
94
+ * @example
95
+ * ```ts
96
+ * pluginAxios({ returnType: 'data' })
97
+ * // const pet = await getPetById({ path: { petId: 1 } }) // Pet, not { status, data, ... }
98
+ * ```
99
+ */
100
+ returnType?: ReturnTypeOption;
79
101
  /**
80
102
  * Generates a class-based SDK instead of the standalone functions. Each tag client is an instance
81
103
  * class whose constructor takes a client config and builds its own client, so every environment is
@@ -142,6 +164,7 @@ type ResolvedOptions = {
142
164
  group: Group | null;
143
165
  baseURL: Options['baseURL'];
144
166
  validator: NonNullable<Options['validator']>;
167
+ returnType: ReturnTypeOption;
145
168
  sdk: {
146
169
  mode: Mode;
147
170
  name: string | undefined;
package/dist/index.js CHANGED
@@ -450,23 +450,49 @@ function createGroupConfig(group) {
450
450
  function buildRequestResultGenerics({ node, types }) {
451
451
  return `${types.response.responses(node)}, ThrowOnError`;
452
452
  }
453
+ /**
454
+ * Builds the full return type an operation's function signature uses: `Unwrappable<RequestResult>`
455
+ * for the default `returnType: 'full'`, already a promise so no further wrapping is needed, or a
456
+ * `Promise` of the runtime's `UnwrappedResult` when `returnType: 'data'` narrows a resolved call
457
+ * down to the bare success body.
458
+ *
459
+ * @example
460
+ * `buildResultType({ node, types, returnType: 'data' }) // 'Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>'`
461
+ */
462
+ function buildResultType({ node, types, returnType }) {
463
+ const generics = buildRequestResultGenerics({
464
+ node,
465
+ types
466
+ });
467
+ return returnType === "data" ? `Promise<UnwrappedResult<${generics}>>` : `Unwrappable<RequestResult<${generics}>>`;
468
+ }
453
469
  //#endregion
454
470
  //#region ../../internals/client/src/builders/returnStatement.ts
455
471
  /**
456
- * Builds the return statement of a generated operation function. The runtime call already resolves
457
- * to `{ data, error, request, response }`. The generated code casts that result to the operation's
458
- * `RequestResult`, then wraps it with `withUnwrap`, so the caller can `await` it directly or call
459
- * `.unwrap()` for the bare success body.
472
+ * Builds the return statement of a generated operation function. With the default
473
+ * `returnType: 'full'` the runtime call already resolves to `{ data, error, request, response }`.
474
+ * The generated code casts that result to the operation's `RequestResult`, then wraps it with
475
+ * `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
476
+ * body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
477
+ * which narrows the resolved value down to the bare success body the same way `RequestResult`
478
+ * already does, keeping the `throwOnError` default in one place instead of restating it per call.
460
479
  *
461
- * Cast first, wrap second. That order keeps `withUnwrap`'s generic inferred as `RequestResult`
462
- * instead of the runtime's own internal result type. Casting an `Unwrappable<A>` straight to
463
- * `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as` rejects that
464
- * two-generic swap even where `A` and `B` on their own would satisfy it.
480
+ * Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
481
+ * as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
482
+ * straight to `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
483
+ * rejects that two-generic swap even where `A` and `B` on their own would satisfy it.
465
484
  *
466
485
  * @example
467
486
  * `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
487
+ * @example
488
+ * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
468
489
  */
469
- function buildReturnStatement({ node, types, callConfig }) {
490
+ function buildReturnStatement({ node, types, callConfig, returnType }) {
491
+ if (returnType === "data") return `return unwrapResult(request(${callConfig}), config.throwOnError) as ${buildResultType({
492
+ node,
493
+ types,
494
+ returnType
495
+ })}`;
470
496
  return `return withUnwrap(request(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
471
497
  node,
472
498
  types
@@ -552,7 +578,7 @@ const declarationPrinter = functionPrinter({ mode: "declaration" });
552
578
  * per-operation input type has to be emitted. Both names come from `types`, which is `plugin-ts` or
553
579
  * `plugin-zod`'s inferred types (see `resolveOperationTypes`).
554
580
  */
555
- function buildGroupedOptionsSignature({ node, types }) {
581
+ function buildGroupedOptionsSignature({ node, types, returnType }) {
556
582
  const optionsName = types.response.options(node);
557
583
  const { isOptional } = getRequestGroupOptionality(node);
558
584
  return {
@@ -561,10 +587,11 @@ function buildGroupedOptionsSignature({ node, types }) {
561
587
  type: `Options<${optionsName}, ThrowOnError>`,
562
588
  ...isOptional ? { default: "{}" } : {}
563
589
  })] })) ?? "",
564
- returnType: `Unwrappable<RequestResult<${buildRequestResultGenerics({
590
+ returnType: buildResultType({
565
591
  node,
566
- types
567
- })}>>`,
592
+ types,
593
+ returnType
594
+ }),
568
595
  generics: ["ThrowOnError extends boolean = true"]
569
596
  };
570
597
  }
@@ -685,11 +712,12 @@ function buildCallConfig({ node, validator, zodResolver, security }) {
685
712
  * instance client, so one operation can be routed to a different environment without a new
686
713
  * instance.
687
714
  */
688
- function buildSdkMethod({ node, name, types, zodResolver, validator, security }) {
715
+ function buildSdkMethod({ node, name, types, zodResolver, validator, security, returnType }) {
689
716
  if (!ast.isHttpOperationNode(node)) return "";
690
717
  const signature = buildGroupedOptionsSignature({
691
718
  node,
692
- types
719
+ types,
720
+ returnType
693
721
  });
694
722
  const returnStatement = buildReturnStatement({
695
723
  node,
@@ -699,7 +727,8 @@ function buildSdkMethod({ node, name, types, zodResolver, validator, security })
699
727
  validator,
700
728
  zodResolver,
701
729
  security
702
- })
730
+ }),
731
+ returnType
703
732
  });
704
733
  const generics = signature.generics.length ? `<${signature.generics.join(", ")}>` : "";
705
734
  const jsdoc = buildJSDoc(buildOperationComments(node, {
@@ -775,11 +804,12 @@ function buildStyles({ node }) {
775
804
  * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
776
805
  * the source.
777
806
  */
778
- function Operation({ name, node, types, zodResolver, validator, security, isExportable = true, isIndexable = true }) {
807
+ function Operation({ name, node, types, zodResolver, validator, returnType, security, isExportable = true, isIndexable = true }) {
779
808
  if (!ast.isHttpOperationNode(node)) return null;
780
809
  const signature = buildGroupedOptionsSignature({
781
810
  node,
782
- types
811
+ types,
812
+ returnType
783
813
  });
784
814
  const validators = buildValidatorHooks({
785
815
  node,
@@ -812,11 +842,12 @@ function Operation({ name, node, types, zodResolver, validator, security, isExpo
812
842
  "...config"
813
843
  ].filter(Boolean).join(", ")} }`;
814
844
  const eventType = `SuccessOf<${types.response.responses(node)}>`;
815
- const returnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
845
+ const functionReturnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
816
846
  const returnStatement = eventStream ? `return toEventStream<${eventType}>(request(${callConfig}))` : buildReturnStatement({
817
847
  node,
818
848
  types,
819
- callConfig
849
+ callConfig,
850
+ returnType
820
851
  });
821
852
  return /* @__PURE__ */ jsx(File.Source, {
822
853
  name,
@@ -827,7 +858,7 @@ function Operation({ name, node, types, zodResolver, validator, security, isExpo
827
858
  export: isExportable,
828
859
  generics: signature.generics,
829
860
  params: signature.paramsSignature,
830
- returnType,
861
+ returnType: functionReturnType,
831
862
  JSDoc: { comments: buildOperationComments(node, {
832
863
  link: "urlPath",
833
864
  linkPosition: "beforeDeprecated",
@@ -849,14 +880,15 @@ function Operation({ name, node, types, zodResolver, validator, security, isExpo
849
880
  * instance: `const api = new PetClient({ baseURL }); api.getPetById(...)`. A per-call `client` option
850
881
  * still overrides the instance client for a one-off call.
851
882
  */
852
- function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, children }) {
883
+ function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, returnType, children }) {
853
884
  const methods = operations.map(({ node, name: methodName, types, zodResolver, security }) => buildSdkMethod({
854
885
  node,
855
886
  name: methodName,
856
887
  types,
857
888
  zodResolver,
858
889
  validator,
859
- security
890
+ security,
891
+ returnType
860
892
  }));
861
893
  const classCode = `export class ${name} {\n${[
862
894
  " private readonly client: ClientInstance",
@@ -936,7 +968,7 @@ function createClientGenerator(name) {
936
968
  operation(node, ctx) {
937
969
  if (!ast.isHttpOperationNode(node)) return null;
938
970
  const { config, driver, resolver, root } = ctx;
939
- const { output, validator, group } = ctx.options;
971
+ const { output, validator, returnType, group } = ctx.options;
940
972
  const types = resolveOperationTypes(driver);
941
973
  if (!types) {
942
974
  ctx.warn(MISSING_OPERATION_TYPES_WARNING);
@@ -1003,7 +1035,7 @@ function createClientGenerator(name) {
1003
1035
  }),
1004
1036
  children: [
1005
1037
  /* @__PURE__ */ jsx(File.Import, {
1006
- name: eventStream ? ["client", "toEventStream"] : ["client", "withUnwrap"],
1038
+ name: eventStream ? ["client", "toEventStream"] : ["client", returnType === "data" ? "unwrapResult" : "withUnwrap"],
1007
1039
  root: meta.file.path,
1008
1040
  path: clientPath
1009
1041
  }),
@@ -1012,7 +1044,7 @@ function createClientGenerator(name) {
1012
1044
  "Options",
1013
1045
  "EventStreamResult",
1014
1046
  "SuccessOf"
1015
- ] : [
1047
+ ] : returnType === "data" ? ["Options", "UnwrappedResult"] : [
1016
1048
  "Options",
1017
1049
  "Unwrappable",
1018
1050
  "RequestResult"
@@ -1038,6 +1070,7 @@ function createClientGenerator(name) {
1038
1070
  types,
1039
1071
  zodResolver,
1040
1072
  validator,
1073
+ returnType,
1041
1074
  security
1042
1075
  })
1043
1076
  ]
@@ -1178,7 +1211,7 @@ function createSdkGenerator() {
1178
1211
  renderer: jsxRenderer,
1179
1212
  operations(nodes, ctx) {
1180
1213
  const { config, resolver, root } = ctx;
1181
- const { output, group, validator, sdk } = ctx.options;
1214
+ const { output, group, validator, returnType, sdk } = ctx.options;
1182
1215
  if (!sdk) return null;
1183
1216
  const types = resolveOperationTypes(ctx.driver);
1184
1217
  if (!types) {
@@ -1223,12 +1256,17 @@ function createSdkGenerator() {
1223
1256
  footer: footer(file),
1224
1257
  children: [
1225
1258
  /* @__PURE__ */ jsx(File.Import, {
1226
- name: ["createClient", "withUnwrap"],
1259
+ name: ["createClient", returnType === "data" ? "unwrapResult" : "withUnwrap"],
1227
1260
  root: file.path,
1228
1261
  path: clientPath
1229
1262
  }),
1230
1263
  /* @__PURE__ */ jsx(File.Import, {
1231
- name: [
1264
+ name: returnType === "data" ? [
1265
+ "ClientConfig",
1266
+ "ClientInstance",
1267
+ "Options",
1268
+ "UnwrappedResult"
1269
+ ] : [
1232
1270
  "ClientConfig",
1233
1271
  "ClientInstance",
1234
1272
  "Options",
@@ -1258,7 +1296,8 @@ function createSdkGenerator() {
1258
1296
  /* @__PURE__ */ jsx(SdkClient, {
1259
1297
  name: className,
1260
1298
  operations: ops,
1261
- validator
1299
+ validator,
1300
+ returnType
1262
1301
  })
1263
1302
  ]
1264
1303
  }, file.path);
@@ -1395,7 +1434,7 @@ const pluginFetch = definePlugin((options) => {
1395
1434
  const { output = {
1396
1435
  path: "clients",
1397
1436
  barrel: { type: "named" }
1398
- }, exclude = [], include, override = [], baseURL, validator = false, group, sdk, resolver: userResolver } = options;
1437
+ }, exclude = [], include, override = [], baseURL, validator = false, returnType = "full", group, sdk, resolver: userResolver } = options;
1399
1438
  const resolved = {
1400
1439
  output,
1401
1440
  exclude,
@@ -1404,6 +1443,7 @@ const pluginFetch = definePlugin((options) => {
1404
1443
  group: createGroupConfig(group),
1405
1444
  baseURL,
1406
1445
  validator,
1446
+ returnType,
1407
1447
  sdk: sdk ? {
1408
1448
  mode: sdk.mode ?? "tag",
1409
1449
  name: sdk.name