@kubb/plugin-fetch 5.5.0-canary.20260924T110844 → 5.5.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
@@ -25,9 +25,8 @@ type Mode = 'tag' | 'flat';
25
25
  /**
26
26
  * Shape of the value a generated operation function resolves to.
27
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.
28
+ * - `'data'`: the bare success body when `throwOnError` is true, or the full result when it is false
29
+ * so callers can inspect `error`.
31
30
  */
32
31
  type ReturnTypeOption = 'full' | 'data';
33
32
  /**
@@ -71,13 +70,20 @@ type Options = OutputOptions & {
71
70
  /**
72
71
  * Apply a different options object to operations matching a pattern.
73
72
  */
74
- override?: Array<Override<ResolvedOptions>>;
73
+ override?: Array<Override<Omit<ResolvedOptions, 'throwOnErrorDefault'>>>;
75
74
  /**
76
75
  * Base URL prepended to every request. When omitted, falls back to the adapter's server URL.
77
76
  * Values containing a `${...}` interpolation are emitted as template literals in the generated
78
77
  * client config, which keeps runtime environment reads dynamic.
79
78
  */
80
79
  baseURL?: string;
80
+ /**
81
+ * Default error behavior for generated operations and their client. Per-call `throwOnError`
82
+ * takes precedence.
83
+ *
84
+ * @default true
85
+ */
86
+ throwOnErrorDefault?: boolean;
81
87
  /**
82
88
  * Validate request and response bodies with schemas from `@kubb/plugin-zod`.
83
89
  *
@@ -160,9 +166,10 @@ type ResolvedOptions = {
160
166
  output: Output;
161
167
  exclude: Array<Exclude>;
162
168
  include: Array<Include> | undefined;
163
- override: Array<Override<ResolvedOptions>>;
169
+ override: Array<Override<Omit<ResolvedOptions, 'throwOnErrorDefault'>>>;
164
170
  group: Group | null;
165
171
  baseURL: Options['baseURL'];
172
+ throwOnErrorDefault: boolean;
166
173
  validator: NonNullable<Options['validator']>;
167
174
  returnType: ReturnTypeOption;
168
175
  sdk: {
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { createFunctionParameter, createFunctionParameters, functionPrinter, plu
5
5
  import { File, Function, jsxRenderer } from "kubb/jsx";
6
6
  import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
7
7
  import { pluginZodName } from "@kubb/plugin-zod";
8
+ import { readFileSync } from "node:fs";
8
9
  import { fileURLToPath } from "node:url";
9
10
  //#region ../../internals/shared/src/params.ts
10
11
  /**
@@ -475,7 +476,7 @@ function buildResultType({ node, types, returnType }) {
475
476
  * `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
476
477
  * body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
477
478
  * 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.
479
+ * already does, using the plugin default when the call leaves `throwOnError` unset.
479
480
  *
480
481
  * Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
481
482
  * as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
@@ -485,10 +486,10 @@ function buildResultType({ node, types, returnType }) {
485
486
  * @example
486
487
  * `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
487
488
  * @example
488
- * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
489
+ * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
489
490
  */
490
- function buildReturnStatement({ node, types, callConfig, returnType }) {
491
- if (returnType === "data") return `return unwrapResult(request(${callConfig}), config.throwOnError) as ${buildResultType({
491
+ function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
492
+ if (returnType === "data") return `return unwrapResult(request(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
492
493
  node,
493
494
  types,
494
495
  returnType
@@ -578,7 +579,7 @@ const declarationPrinter = functionPrinter({ mode: "declaration" });
578
579
  * per-operation input type has to be emitted. Both names come from `types`, which is `plugin-ts` or
579
580
  * `plugin-zod`'s inferred types (see `resolveOperationTypes`).
580
581
  */
581
- function buildGroupedOptionsSignature({ node, types, returnType }) {
582
+ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault }) {
582
583
  const optionsName = types.response.options(node);
583
584
  const { isOptional } = getRequestGroupOptionality(node);
584
585
  return {
@@ -592,7 +593,7 @@ function buildGroupedOptionsSignature({ node, types, returnType }) {
592
593
  types,
593
594
  returnType
594
595
  }),
595
- generics: ["ThrowOnError extends boolean = true"]
596
+ generics: [`ThrowOnError extends boolean = ${throwOnErrorDefault}`]
596
597
  };
597
598
  }
598
599
  //#endregion
@@ -686,9 +687,10 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
686
687
  /**
687
688
  * Builds the call config literal forwarded to the contract client, mirroring the shared `Operation`
688
689
  * component: `{ method, url, security?, validator?, ...config }`. The `...config` spread carries every
689
- * per-call field (including `throwOnError`), so the method stays a thin wrapper over the contract.
690
+ * per-call field. The plugin's `throwOnError` fallback is applied after the spread so it also wins
691
+ * over a client instance's runtime config when the call omits an override.
690
692
  */
691
- function buildCallConfig({ node, validator, zodResolver, security }) {
693
+ function buildCallConfig({ node, validator, zodResolver, security, throwOnErrorDefault }) {
692
694
  const validators = buildValidatorHooks({
693
695
  node,
694
696
  validator,
@@ -702,7 +704,8 @@ function buildCallConfig({ node, validator, zodResolver, security }) {
702
704
  `url: '${node.path}'`,
703
705
  securityLiteral ? `security: ${securityLiteral}` : null,
704
706
  validatorLiteral,
705
- "...config"
707
+ "...config",
708
+ `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
706
709
  ].filter(Boolean).join(", ")} }`;
707
710
  }
708
711
  /**
@@ -712,12 +715,13 @@ function buildCallConfig({ node, validator, zodResolver, security }) {
712
715
  * instance client, so one operation can be routed to a different environment without a new
713
716
  * instance.
714
717
  */
715
- function buildSdkMethod({ node, name, types, zodResolver, validator, security, returnType }) {
718
+ function buildSdkMethod({ node, name, types, zodResolver, validator, security, returnType, throwOnErrorDefault }) {
716
719
  if (!ast.isHttpOperationNode(node)) return "";
717
720
  const signature = buildGroupedOptionsSignature({
718
721
  node,
719
722
  types,
720
- returnType
723
+ returnType,
724
+ throwOnErrorDefault
721
725
  });
722
726
  const returnStatement = buildReturnStatement({
723
727
  node,
@@ -726,9 +730,11 @@ function buildSdkMethod({ node, name, types, zodResolver, validator, security, r
726
730
  node,
727
731
  validator,
728
732
  zodResolver,
729
- security
733
+ security,
734
+ throwOnErrorDefault
730
735
  }),
731
- returnType
736
+ returnType,
737
+ throwOnErrorDefault
732
738
  });
733
739
  const generics = signature.generics.length ? `<${signature.generics.join(", ")}>` : "";
734
740
  const jsdoc = buildJSDoc(buildOperationComments(node, {
@@ -804,12 +810,13 @@ function buildStyles({ node }) {
804
810
  * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
805
811
  * the source.
806
812
  */
807
- function Operation({ name, node, types, zodResolver, validator, returnType, security, isExportable = true, isIndexable = true }) {
813
+ function Operation({ name, node, types, zodResolver, validator, returnType, throwOnErrorDefault, security, isExportable = true, isIndexable = true }) {
808
814
  if (!ast.isHttpOperationNode(node)) return null;
809
815
  const signature = buildGroupedOptionsSignature({
810
816
  node,
811
817
  types,
812
- returnType
818
+ returnType,
819
+ throwOnErrorDefault
813
820
  });
814
821
  const validators = buildValidatorHooks({
815
822
  node,
@@ -839,7 +846,8 @@ function Operation({ name, node, types, zodResolver, validator, returnType, secu
839
846
  validatorLiteral,
840
847
  contentTypeLiteral,
841
848
  responseTypeLiteral,
842
- "...config"
849
+ "...config",
850
+ `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
843
851
  ].filter(Boolean).join(", ")} }`;
844
852
  const eventType = `SuccessOf<${types.response.responses(node)}>`;
845
853
  const functionReturnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
@@ -847,7 +855,8 @@ function Operation({ name, node, types, zodResolver, validator, returnType, secu
847
855
  node,
848
856
  types,
849
857
  callConfig,
850
- returnType
858
+ returnType,
859
+ throwOnErrorDefault
851
860
  });
852
861
  return /* @__PURE__ */ jsx(File.Source, {
853
862
  name,
@@ -880,7 +889,7 @@ function Operation({ name, node, types, zodResolver, validator, returnType, secu
880
889
  * instance: `const api = new PetClient({ baseURL }); api.getPetById(...)`. A per-call `client` option
881
890
  * still overrides the instance client for a one-off call.
882
891
  */
883
- function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, returnType, children }) {
892
+ function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, returnType, throwOnErrorDefault, children }) {
884
893
  const methods = operations.map(({ node, name: methodName, types, zodResolver, security }) => buildSdkMethod({
885
894
  node,
886
895
  name: methodName,
@@ -888,7 +897,8 @@ function SdkClient({ name, isExportable = true, isIndexable = true, operations,
888
897
  zodResolver,
889
898
  validator,
890
899
  security,
891
- returnType
900
+ returnType,
901
+ throwOnErrorDefault
892
902
  }));
893
903
  const classCode = `export class ${name} {\n${[
894
904
  " private readonly client: ClientInstance",
@@ -968,7 +978,7 @@ function createClientGenerator(name) {
968
978
  operation(node, ctx) {
969
979
  if (!ast.isHttpOperationNode(node)) return null;
970
980
  const { config, driver, resolver, root } = ctx;
971
- const { output, validator, returnType, group } = ctx.options;
981
+ const { output, validator, returnType, throwOnErrorDefault, group } = ctx.options;
972
982
  const types = resolveOperationTypes(driver);
973
983
  if (!types) {
974
984
  ctx.warn(MISSING_OPERATION_TYPES_WARNING);
@@ -1071,6 +1081,7 @@ function createClientGenerator(name) {
1071
1081
  zodResolver,
1072
1082
  validator,
1073
1083
  returnType,
1084
+ throwOnErrorDefault,
1074
1085
  security
1075
1086
  })
1076
1087
  ]
@@ -1211,7 +1222,7 @@ function createSdkGenerator() {
1211
1222
  renderer: jsxRenderer,
1212
1223
  operations(nodes, ctx) {
1213
1224
  const { config, resolver, root } = ctx;
1214
- const { output, group, validator, returnType, sdk } = ctx.options;
1225
+ const { output, group, validator, returnType, throwOnErrorDefault, sdk } = ctx.options;
1215
1226
  if (!sdk) return null;
1216
1227
  const types = resolveOperationTypes(ctx.driver);
1217
1228
  if (!types) {
@@ -1297,7 +1308,8 @@ function createSdkGenerator() {
1297
1308
  name: className,
1298
1309
  operations: ops,
1299
1310
  validator,
1300
- returnType
1311
+ returnType,
1312
+ throwOnErrorDefault
1301
1313
  })
1302
1314
  ]
1303
1315
  }, file.path);
@@ -1383,6 +1395,27 @@ const resolverClient = createResolver({
1383
1395
  }
1384
1396
  });
1385
1397
  //#endregion
1398
+ //#region ../../internals/client/src/runtimeTemplate.ts
1399
+ /** Apply the active TypeScript parser's import extension to a copied client runtime. */
1400
+ function runtimeTemplate(path, config) {
1401
+ const parser = config.parsers.find((item) => item.extNames?.includes(".ts"));
1402
+ const probe = ast.factory.createFile({
1403
+ baseName: "probe.ts",
1404
+ path: "probe.ts",
1405
+ imports: [ast.factory.createImport({
1406
+ name: ["KubbRuntimeImport"],
1407
+ path: "/probeDependency.ts",
1408
+ root: "/"
1409
+ })],
1410
+ sources: [ast.factory.createSource({
1411
+ name: "probe",
1412
+ nodes: [ast.factory.createText("KubbRuntimeImport")]
1413
+ })]
1414
+ });
1415
+ const extension = parser?.parse(probe).match(/from ['"]\.\/probeDependency(\.[^'"]+)?['"]/)?.[1] ?? "";
1416
+ return readFileSync(path, "utf8").replace(/(from ['"]\.\/(?:serializers|standardSchema))(?:\.ts|\.js)?(['"])/g, `$1${extension}$2`);
1417
+ }
1418
+ //#endregion
1386
1419
  //#region src/generators/clientGenerator.tsx
1387
1420
  /**
1388
1421
  * Built-in operation generator for `@kubb/plugin-fetch`. Emits one async function per OpenAPI
@@ -1434,7 +1467,7 @@ const pluginFetch = definePlugin((options) => {
1434
1467
  const { output = {
1435
1468
  path: "clients",
1436
1469
  barrel: { type: "named" }
1437
- }, exclude = [], include, override = [], baseURL, validator = false, returnType = "full", group, sdk, resolver: userResolver } = options;
1470
+ }, exclude = [], include, override = [], baseURL, throwOnErrorDefault = true, validator = false, returnType = "full", group, sdk, resolver: userResolver } = options;
1438
1471
  const resolved = {
1439
1472
  output,
1440
1473
  exclude,
@@ -1442,6 +1475,7 @@ const pluginFetch = definePlugin((options) => {
1442
1475
  override,
1443
1476
  group: createGroupConfig(group),
1444
1477
  baseURL,
1478
+ throwOnErrorDefault,
1445
1479
  validator,
1446
1480
  returnType,
1447
1481
  sdk: sdk ? {
@@ -1470,7 +1504,7 @@ const pluginFetch = definePlugin((options) => {
1470
1504
  ctx.injectFile({
1471
1505
  baseName: "client.ts",
1472
1506
  path: path.resolve(root, ".kubb/client.ts"),
1473
- copy: fetchClientTemplatePath,
1507
+ banner: runtimeTemplate(fetchClientTemplatePath, ctx.config),
1474
1508
  footer: baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : void 0
1475
1509
  });
1476
1510
  ctx.injectFile({