@kubb/plugin-fetch 5.5.0-canary.20260924T111237 → 5.5.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/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,7 +5,6 @@ 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";
9
8
  import { fileURLToPath } from "node:url";
10
9
  //#region ../../internals/shared/src/params.ts
11
10
  /**
@@ -476,7 +475,7 @@ function buildResultType({ node, types, returnType }) {
476
475
  * `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
477
476
  * body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
478
477
  * which narrows the resolved value down to the bare success body the same way `RequestResult`
479
- * already does, keeping the `throwOnError` default in one place instead of restating it per call.
478
+ * already does, using the plugin default when the call leaves `throwOnError` unset.
480
479
  *
481
480
  * Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
482
481
  * as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
@@ -486,10 +485,10 @@ function buildResultType({ node, types, returnType }) {
486
485
  * @example
487
486
  * `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
488
487
  * @example
489
- * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
488
+ * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
490
489
  */
491
- function buildReturnStatement({ node, types, callConfig, returnType }) {
492
- if (returnType === "data") return `return unwrapResult(request(${callConfig}), config.throwOnError) as ${buildResultType({
490
+ function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
491
+ if (returnType === "data") return `return unwrapResult(request(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
493
492
  node,
494
493
  types,
495
494
  returnType
@@ -579,7 +578,7 @@ const declarationPrinter = functionPrinter({ mode: "declaration" });
579
578
  * per-operation input type has to be emitted. Both names come from `types`, which is `plugin-ts` or
580
579
  * `plugin-zod`'s inferred types (see `resolveOperationTypes`).
581
580
  */
582
- function buildGroupedOptionsSignature({ node, types, returnType }) {
581
+ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault }) {
583
582
  const optionsName = types.response.options(node);
584
583
  const { isOptional } = getRequestGroupOptionality(node);
585
584
  return {
@@ -593,7 +592,7 @@ function buildGroupedOptionsSignature({ node, types, returnType }) {
593
592
  types,
594
593
  returnType
595
594
  }),
596
- generics: ["ThrowOnError extends boolean = true"]
595
+ generics: [`ThrowOnError extends boolean = ${throwOnErrorDefault}`]
597
596
  };
598
597
  }
599
598
  //#endregion
@@ -687,9 +686,10 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
687
686
  /**
688
687
  * Builds the call config literal forwarded to the contract client, mirroring the shared `Operation`
689
688
  * component: `{ method, url, security?, validator?, ...config }`. The `...config` spread carries every
690
- * per-call field (including `throwOnError`), so the method stays a thin wrapper over the contract.
689
+ * per-call field. The plugin's `throwOnError` fallback is applied after the spread so it also wins
690
+ * over a client instance's runtime config when the call omits an override.
691
691
  */
692
- function buildCallConfig({ node, validator, zodResolver, security }) {
692
+ function buildCallConfig({ node, validator, zodResolver, security, throwOnErrorDefault }) {
693
693
  const validators = buildValidatorHooks({
694
694
  node,
695
695
  validator,
@@ -703,7 +703,8 @@ function buildCallConfig({ node, validator, zodResolver, security }) {
703
703
  `url: '${node.path}'`,
704
704
  securityLiteral ? `security: ${securityLiteral}` : null,
705
705
  validatorLiteral,
706
- "...config"
706
+ "...config",
707
+ `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
707
708
  ].filter(Boolean).join(", ")} }`;
708
709
  }
709
710
  /**
@@ -713,12 +714,13 @@ function buildCallConfig({ node, validator, zodResolver, security }) {
713
714
  * instance client, so one operation can be routed to a different environment without a new
714
715
  * instance.
715
716
  */
716
- function buildSdkMethod({ node, name, types, zodResolver, validator, security, returnType }) {
717
+ function buildSdkMethod({ node, name, types, zodResolver, validator, security, returnType, throwOnErrorDefault }) {
717
718
  if (!ast.isHttpOperationNode(node)) return "";
718
719
  const signature = buildGroupedOptionsSignature({
719
720
  node,
720
721
  types,
721
- returnType
722
+ returnType,
723
+ throwOnErrorDefault
722
724
  });
723
725
  const returnStatement = buildReturnStatement({
724
726
  node,
@@ -727,9 +729,11 @@ function buildSdkMethod({ node, name, types, zodResolver, validator, security, r
727
729
  node,
728
730
  validator,
729
731
  zodResolver,
730
- security
732
+ security,
733
+ throwOnErrorDefault
731
734
  }),
732
- returnType
735
+ returnType,
736
+ throwOnErrorDefault
733
737
  });
734
738
  const generics = signature.generics.length ? `<${signature.generics.join(", ")}>` : "";
735
739
  const jsdoc = buildJSDoc(buildOperationComments(node, {
@@ -805,12 +809,13 @@ function buildStyles({ node }) {
805
809
  * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
806
810
  * the source.
807
811
  */
808
- function Operation({ name, node, types, zodResolver, validator, returnType, security, isExportable = true, isIndexable = true }) {
812
+ function Operation({ name, node, types, zodResolver, validator, returnType, throwOnErrorDefault, security, isExportable = true, isIndexable = true }) {
809
813
  if (!ast.isHttpOperationNode(node)) return null;
810
814
  const signature = buildGroupedOptionsSignature({
811
815
  node,
812
816
  types,
813
- returnType
817
+ returnType,
818
+ throwOnErrorDefault
814
819
  });
815
820
  const validators = buildValidatorHooks({
816
821
  node,
@@ -840,7 +845,8 @@ function Operation({ name, node, types, zodResolver, validator, returnType, secu
840
845
  validatorLiteral,
841
846
  contentTypeLiteral,
842
847
  responseTypeLiteral,
843
- "...config"
848
+ "...config",
849
+ `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
844
850
  ].filter(Boolean).join(", ")} }`;
845
851
  const eventType = `SuccessOf<${types.response.responses(node)}>`;
846
852
  const functionReturnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
@@ -848,7 +854,8 @@ function Operation({ name, node, types, zodResolver, validator, returnType, secu
848
854
  node,
849
855
  types,
850
856
  callConfig,
851
- returnType
857
+ returnType,
858
+ throwOnErrorDefault
852
859
  });
853
860
  return /* @__PURE__ */ jsx(File.Source, {
854
861
  name,
@@ -881,7 +888,7 @@ function Operation({ name, node, types, zodResolver, validator, returnType, secu
881
888
  * instance: `const api = new PetClient({ baseURL }); api.getPetById(...)`. A per-call `client` option
882
889
  * still overrides the instance client for a one-off call.
883
890
  */
884
- function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, returnType, children }) {
891
+ function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, returnType, throwOnErrorDefault, children }) {
885
892
  const methods = operations.map(({ node, name: methodName, types, zodResolver, security }) => buildSdkMethod({
886
893
  node,
887
894
  name: methodName,
@@ -889,7 +896,8 @@ function SdkClient({ name, isExportable = true, isIndexable = true, operations,
889
896
  zodResolver,
890
897
  validator,
891
898
  security,
892
- returnType
899
+ returnType,
900
+ throwOnErrorDefault
893
901
  }));
894
902
  const classCode = `export class ${name} {\n${[
895
903
  " private readonly client: ClientInstance",
@@ -969,7 +977,7 @@ function createClientGenerator(name) {
969
977
  operation(node, ctx) {
970
978
  if (!ast.isHttpOperationNode(node)) return null;
971
979
  const { config, driver, resolver, root } = ctx;
972
- const { output, validator, returnType, group } = ctx.options;
980
+ const { output, validator, returnType, throwOnErrorDefault, group } = ctx.options;
973
981
  const types = resolveOperationTypes(driver);
974
982
  if (!types) {
975
983
  ctx.warn(MISSING_OPERATION_TYPES_WARNING);
@@ -1072,6 +1080,7 @@ function createClientGenerator(name) {
1072
1080
  zodResolver,
1073
1081
  validator,
1074
1082
  returnType,
1083
+ throwOnErrorDefault,
1075
1084
  security
1076
1085
  })
1077
1086
  ]
@@ -1212,7 +1221,7 @@ function createSdkGenerator() {
1212
1221
  renderer: jsxRenderer,
1213
1222
  operations(nodes, ctx) {
1214
1223
  const { config, resolver, root } = ctx;
1215
- const { output, group, validator, returnType, sdk } = ctx.options;
1224
+ const { output, group, validator, returnType, throwOnErrorDefault, sdk } = ctx.options;
1216
1225
  if (!sdk) return null;
1217
1226
  const types = resolveOperationTypes(ctx.driver);
1218
1227
  if (!types) {
@@ -1298,7 +1307,8 @@ function createSdkGenerator() {
1298
1307
  name: className,
1299
1308
  operations: ops,
1300
1309
  validator,
1301
- returnType
1310
+ returnType,
1311
+ throwOnErrorDefault
1302
1312
  })
1303
1313
  ]
1304
1314
  }, file.path);
@@ -1435,7 +1445,7 @@ const pluginFetch = definePlugin((options) => {
1435
1445
  const { output = {
1436
1446
  path: "clients",
1437
1447
  barrel: { type: "named" }
1438
- }, exclude = [], include, override = [], baseURL, validator = false, returnType = "full", group, sdk, resolver: userResolver } = options;
1448
+ }, exclude = [], include, override = [], baseURL, throwOnErrorDefault = true, validator = false, returnType = "full", group, sdk, resolver: userResolver } = options;
1439
1449
  const resolved = {
1440
1450
  output,
1441
1451
  exclude,
@@ -1443,6 +1453,7 @@ const pluginFetch = definePlugin((options) => {
1443
1453
  override,
1444
1454
  group: createGroupConfig(group),
1445
1455
  baseURL,
1456
+ throwOnErrorDefault,
1446
1457
  validator,
1447
1458
  returnType,
1448
1459
  sdk: sdk ? {
@@ -1468,52 +1479,10 @@ const pluginFetch = definePlugin((options) => {
1468
1479
  path: path.resolve(root, ".kubb/serializers.ts"),
1469
1480
  copy: fetchSerializersTemplatePath
1470
1481
  });
1471
- const clientPath = path.resolve(root, ".kubb/client.ts");
1472
- const clientSource = readFileSync(fetchClientTemplatePath, "utf8");
1473
- const clientBody = clientSource.slice(clientSource.indexOf("\n\n") + 2);
1474
- const runtimeRoot = path.dirname(clientPath);
1475
1482
  ctx.injectFile({
1476
1483
  baseName: "client.ts",
1477
- path: clientPath,
1478
- imports: [
1479
- ast.factory.createImport({
1480
- name: [
1481
- "applyHeaderStyles",
1482
- "defaultBodySerializer",
1483
- "defaultPathSerializer",
1484
- "defaultQuerySerializer",
1485
- "isDefaultJsonBody",
1486
- "serializeCookies"
1487
- ],
1488
- path: path.resolve(root, ".kubb/serializers.ts"),
1489
- root: runtimeRoot
1490
- }),
1491
- ast.factory.createImport({
1492
- name: [
1493
- "HeadersInit",
1494
- "PathParamStyle",
1495
- "PathSerializer",
1496
- "RequestBody",
1497
- "Serializers",
1498
- "Styles"
1499
- ],
1500
- path: path.resolve(root, ".kubb/serializers.ts"),
1501
- root: runtimeRoot,
1502
- isTypeOnly: true
1503
- }),
1504
- ast.factory.createImport({
1505
- name: ["ParseError", "validateStandardSchema"],
1506
- path: path.resolve(root, ".kubb/standardSchema.ts"),
1507
- root: runtimeRoot
1508
- }),
1509
- ast.factory.createImport({
1510
- name: ["StandardSchemaValidator"],
1511
- path: path.resolve(root, ".kubb/standardSchema.ts"),
1512
- root: runtimeRoot,
1513
- isTypeOnly: true
1514
- })
1515
- ],
1516
- sources: [ast.factory.createSource({ nodes: [ast.factory.createText(clientBody)] })],
1484
+ path: path.resolve(root, ".kubb/client.ts"),
1485
+ copy: fetchClientTemplatePath,
1517
1486
  footer: baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : void 0
1518
1487
  });
1519
1488
  ctx.injectFile({