@kubb/plugin-fetch 5.3.0-canary.20260907T104724 → 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.cjs +71 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.js +71 -31
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/plugin.ts +2 -0
- package/templates/fetch.ts +21 -0
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.
|
|
457
|
-
* to `{ data, error, request, response }`.
|
|
458
|
-
*
|
|
459
|
-
* `.unwrap()` for the bare success
|
|
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
|
|
462
|
-
* instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
|
|
463
|
-
* `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
|
|
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:
|
|
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
|
|
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
|