@kubb/plugin-fetch 6.1.0-canary.20261109T175349 → 6.1.0-canary.20261109T191216

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 CHANGED
@@ -594,22 +594,19 @@ function resolveResponseValidator(validator) {
594
594
  return validator.response ?? null;
595
595
  }
596
596
  /**
597
- * Resolves the zod schema name a generated client validates an error body with on the non-throw path.
598
- * Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
599
- * operation documents no error responses with a schema.
597
+ * Builds the `validator.error` map of a call, one plugin-zod `<operation>StatusNNNSchema` per non-2xx response with a
598
+ * schema, such as `{ 404: getPetStatus404Schema, '5XX': getPetStatus5XXSchema }`. Returns `null` without one.
600
599
  */
601
600
  function buildZodErrorParse(node, zodResolver) {
602
- if (!getErrorStatusCodes(node).length) return null;
603
- return zodResolver.response.error?.(node) || null;
604
- }
605
- /** Prints the statuses `<operation>ErrorSchema` covers, such as `[404, '5XX']`, or `null` when a `default` response covers them all. */
606
- function buildErrorStatusLiteral(node) {
607
- const statusCodes = getErrorStatusCodes(node);
608
- if (!statusCodes.length || statusCodes.includes("default")) return null;
609
- return `[${statusCodes.map((statusCode) => /^\d+$/.test(statusCode) ? statusCode : stringify(statusCode)).join(", ")}]`;
610
- }
611
- function getErrorStatusCodes(node) {
612
- return node.responses.filter((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema)).map((res) => String(res.statusCode));
601
+ const entries = node.responses.filter((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema)).map((res) => ({
602
+ key: String(res.statusCode),
603
+ name: zodResolver.response.status(node, res.statusCode)
604
+ }));
605
+ if (!entries.length) return null;
606
+ return {
607
+ literal: `{ ${entries.map(({ key, name }) => `${/^\d+$/.test(key) ? key : stringify(key)}: ${name}`).join(", ")} }`,
608
+ names: entries.map(({ name }) => name)
609
+ };
613
610
  }
614
611
  // ../../internals/client/src/builders/generics.ts
615
612
  /**
@@ -804,6 +801,18 @@ function buildSecurityMetadata({ security }) {
804
801
  if (!security?.length) return null;
805
802
  return `[${security.map(serializeAuth).join(", ")}]`;
806
803
  }
804
+ // ../../internals/client/src/builders/server.ts
805
+ /**
806
+ * Returns the URL of the operation's own `servers`, else its path item's, picked with `server.index` and falling back to the
807
+ * first entry. Returns `undefined` without `servers`, for a relative URL such as `/v2`, which would drop the client's host, or
808
+ * for a URL with `{variable}` placeholders, which are not supported, so the client's `baseURL` applies.
809
+ */
810
+ function getOperationBaseURL({ document, method, path, server: { index = 0 } = {} }) {
811
+ const pathItem = document?.paths?.[path];
812
+ const servers = [(pathItem?.[method.toLowerCase()])?.servers, pathItem?.servers].find((list) => list?.length);
813
+ const url = (servers?.[index] ?? servers?.[0])?.url;
814
+ return url && /^[a-z][a-z\d+.-]*:\/\/[^{}]*$/i.test(url) ? url : void 0;
815
+ }
807
816
  // ../../internals/client/src/builders/validator.ts
808
817
  /**
809
818
  * Builds the validator-hook references for one operation. Request body and params validation run before the send.
@@ -814,8 +823,7 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
814
823
  const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
815
824
  const parsesResponse = zodResolver && resolveResponseValidator(validator) === "zod";
816
825
  const response = parsesResponse ? zodResolver.response.response(node) || null : null;
817
- const error = parsesResponse ? buildZodErrorParse(node, zodResolver) : null;
818
- const errorStatus = error ? buildErrorStatusLiteral(node) : null;
826
+ const errorParse = parsesResponse ? buildZodErrorParse(node, zodResolver) : null;
819
827
  const request = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) || null : null;
820
828
  const paramGroups = getOperationParameters(node);
821
829
  const resolveParamsName = (kind, params) => zodResolver && resolveParamsValidator(validator) === "zod" && params.length > 0 ? zodResolver.param[kind](node, params[0]) : null;
@@ -825,7 +833,7 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
825
833
  const cookies = resolveParamsName("cookies", paramGroups.cookie);
826
834
  const importedZodNames = [
827
835
  response,
828
- error,
836
+ ...errorParse?.names ?? [],
829
837
  request,
830
838
  path,
831
839
  query,
@@ -839,17 +847,17 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
839
847
  headers: headers ? `toHeadersStandardSchema(${headers}, ${headers}.shape)` : null,
840
848
  cookies,
841
849
  response,
842
- error,
843
- errorStatus,
850
+ error: errorParse?.literal ?? null,
844
851
  importedZodNames
845
852
  };
846
853
  }
847
854
  /**
848
855
  * Prints the `validator: { … }` entry of a call config from the hooks named in `keys`, or `null` when none is set.
856
+ * The entry spreads `config.validator` last, so a per-call hook overrides the generated one for its key only.
849
857
  */
850
858
  function buildValidatorLiteral(hooks, keys) {
851
859
  const entries = keys.flatMap((key) => hooks?.[key] ? [`${key}: ${hooks[key]}`] : []);
852
- return entries.length ? `validator: { ${entries.join(", ")} }` : null;
860
+ return entries.length ? `validator: { ${entries.join(", ")}, ...config.validator }` : null;
853
861
  }
854
862
  // ../../internals/client/src/builders/contentType.ts
855
863
  /**
@@ -902,13 +910,13 @@ function serializeParameter(parameter) {
902
910
  return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
903
911
  }
904
912
  /**
905
- * Serializes the `encoding` of the request body's default content type into a `{ property: { ... } }`
906
- * literal, or `null` when it carries none. Only an operation with a single request content type
913
+ * Serializes the `encoding` of the request body's default content type into `property: { ... }`
914
+ * entries, empty when it carries none. Only an operation with a single request content type
907
915
  * emits it, because the runtime applies `styles.body` to whichever type the caller selects.
908
916
  */
909
917
  function serializeBodyEncoding(node) {
910
918
  const content = node.requestBody?.content;
911
- const entries = Object.entries(content?.length === 1 ? content[0]?.encoding ?? {} : {}).map(([property, { contentType, style, explode, allowReserved }]) => {
919
+ return Object.entries(content?.length === 1 ? content[0]?.encoding ?? {} : {}).map(([property, { contentType, style, explode, allowReserved }]) => {
912
920
  const parts = [
913
921
  style && `style: ${JSON.stringify(style)}`,
914
922
  explode !== void 0 && `explode: ${explode}`,
@@ -917,7 +925,6 @@ function serializeBodyEncoding(node) {
917
925
  ].filter(Boolean);
918
926
  return `${isValidVarName(property) ? property : JSON.stringify(property)}: { ${parts.join(", ")} }`;
919
927
  });
920
- return entries.length > 0 ? `body: { ${entries.join(", ")} }` : null;
921
928
  }
922
929
  /**
923
930
  * Builds the per-operation `styles` literal from the operation's parameters, grouped by location.
@@ -927,26 +934,31 @@ function serializeBodyEncoding(node) {
927
934
  * is unchanged. A form request body adds a `body` group keyed by property name from its `encoding`.
928
935
  * Returns `null` when nothing carries metadata.
929
936
  *
937
+ * With `merge`, the literal spreads `config.styles` per parameter, so a per-call entry overrides the generated one for
938
+ * that parameter and leaves the others in place.
939
+ *
930
940
  * @example
931
941
  * ```ts
932
942
  * // a path param with { style: 'matrix', explode: true } and a query param with { explode: false }
933
943
  * buildStyles({ node }) // "{ path: { id: { style: 'matrix', explode: true } }, query: { tags: { explode: false } } }"
934
944
  * ```
935
945
  */
936
- function buildStyles({ node }) {
946
+ function buildStyles({ node, merge }) {
937
947
  if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
938
948
  const groups = {
939
949
  path: [],
940
950
  query: [],
941
951
  header: [],
942
- cookie: []
952
+ cookie: [],
953
+ body: serializeBodyEncoding(node)
943
954
  };
944
955
  for (const [location, parameters] of Object.entries(getOperationParameters(node))) for (const parameter of parameters) {
945
956
  const literal = serializeParameter(parameter);
946
957
  if (literal) groups[location].push(`${toKey(parameter.name)}: ${literal}`);
947
958
  }
948
- const entries = [...Object.keys(groups).filter((location) => groups[location].length > 0).map((location) => `${location}: { ${groups[location].join(", ")} }`), serializeBodyEncoding(node)].filter(Boolean);
949
- return entries.length > 0 ? `{ ${entries.join(", ")} }` : null;
959
+ const entries = Object.entries(groups).filter(([, items]) => items.length > 0).map(([location, items]) => `${location}: { ${(merge ? [...items, `...config.styles?.${location}`] : items).join(", ")} }`);
960
+ if (entries.length === 0) return null;
961
+ return `{ ${(merge ? ["...config.styles", ...entries] : entries).join(", ")} }`;
950
962
  }
951
963
  // ../../internals/client/src/builders/callConfig.ts
952
964
  /**
@@ -962,37 +974,45 @@ function buildResponseTypeLiteral(node) {
962
974
  }
963
975
  /**
964
976
  * Builds the call config literal a generated operation forwards to the client, shared by the standalone
965
- * functions and the SDK methods. The `throwOnError` fallback is applied after the `...config` spread.
977
+ * functions and the SDK methods. The operation's own `baseURL` comes before the `...config` spread, so a per-call one wins. The
978
+ * `validator` and `throwOnError` entries come after it, so a per-call `validator` is merged per key with the generated one instead
979
+ * of replacing it. `styles` is merged the same way, per parameter, unless `returnType` is `'plain'`.
966
980
  *
967
981
  * `mergeContentType` is `true` when the caller must destructure `contentType` from `options`, and
968
982
  * `eventStream` is `true` when the operation streams `text/event-stream` or JSON Lines. Unless `returnType` is `'plain'`, a
969
983
  * stream's literal leaves the `response` validator to `toEventStream`, which runs it per item.
970
984
  */
971
- function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault, returnType }) {
985
+ function buildCallConfig({ node, validatorHooks, security, baseURL, throwOnErrorDefault, returnType }) {
972
986
  const eventStream = isEventStream(node);
973
987
  const securityLiteral = buildSecurityMetadata({ security });
974
- const stylesLiteral = buildStyles({ node });
988
+ const ownRuntime = returnType !== "plain";
989
+ const stylesLiteral = buildStyles({
990
+ node,
991
+ merge: ownRuntime
992
+ });
993
+ const styles = stylesLiteral ? `styles: ${stylesLiteral}` : null;
975
994
  const validatorLiteral = buildValidatorLiteral(validatorHooks, [
976
995
  "request",
977
996
  "path",
978
997
  "query",
979
998
  "headers",
980
999
  "cookies",
981
- ...eventStream && returnType !== "plain" ? [] : ["response"],
982
- "error",
983
- "errorStatus"
1000
+ ...eventStream && ownRuntime ? [] : ["response"],
1001
+ "error"
984
1002
  ]);
985
1003
  const { literal: contentTypeLiteral, merge: mergeContentType } = buildContentType({ node });
986
1004
  return {
987
1005
  callConfig: `{ ${[
988
1006
  `method: '${node.method.toUpperCase()}'`,
989
1007
  `url: '${node.path}'`,
1008
+ baseURL ? `baseURL: ${JSON.stringify(baseURL)}` : null,
990
1009
  securityLiteral ? `security: ${securityLiteral}` : null,
991
- stylesLiteral ? `styles: ${stylesLiteral}` : null,
992
- validatorLiteral,
1010
+ ownRuntime ? null : styles,
993
1011
  contentTypeLiteral,
994
1012
  buildResponseTypeLiteral(node),
995
1013
  "...config",
1014
+ ownRuntime ? styles : null,
1015
+ validatorLiteral,
996
1016
  `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
997
1017
  ].filter(Boolean).join(", ")} }`,
998
1018
  mergeContentType,
@@ -1034,11 +1054,12 @@ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDef
1034
1054
  * body destructures `contentType` from `options`. Shared by the standalone functions and the SDK methods, so a
1035
1055
  * `text/event-stream` operation is wired to `toEventStream` in one place.
1036
1056
  */
1037
- function buildOperationBody({ node, types, validatorHooks, security, returnType, throwOnErrorDefault }) {
1057
+ function buildOperationBody({ node, types, validatorHooks, security, baseURL, returnType, throwOnErrorDefault }) {
1038
1058
  const { callConfig, mergeContentType, eventStream } = buildCallConfig({
1039
1059
  node,
1040
1060
  validatorHooks,
1041
1061
  security,
1062
+ baseURL,
1042
1063
  throwOnErrorDefault,
1043
1064
  returnType
1044
1065
  });
@@ -1078,13 +1099,14 @@ function buildOperationBody({ node, types, validatorHooks, security, returnType,
1078
1099
  * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
1079
1100
  * the source.
1080
1101
  */
1081
- function Operation({ name, node, types, validatorHooks, returnType, throwOnErrorDefault, security }) {
1102
+ function Operation({ name, node, types, validatorHooks, returnType, throwOnErrorDefault, security, baseURL }) {
1082
1103
  if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
1083
1104
  const body = buildOperationBody({
1084
1105
  node,
1085
1106
  types,
1086
1107
  validatorHooks,
1087
1108
  security,
1109
+ baseURL,
1088
1110
  returnType,
1089
1111
  throwOnErrorDefault
1090
1112
  });
@@ -1283,6 +1305,12 @@ function createClientGenerator(name, { sse } = {}) {
1283
1305
  method: node.method,
1284
1306
  path: node.path
1285
1307
  });
1308
+ const baseURL = getOperationBaseURL({
1309
+ document: ctx.adapter.document,
1310
+ method: node.method,
1311
+ path: node.path,
1312
+ server: ctx.adapter.options?.server
1313
+ });
1286
1314
  const clientPath = importPath ?? node_path.default.resolve(root, ".kubb/client.ts");
1287
1315
  const clientRoot = importPath === void 0 ? meta.file.path : void 0;
1288
1316
  const standardSchemaPath = importPath ?? node_path.default.resolve(root, ".kubb/standardSchema.ts");
@@ -1377,7 +1405,8 @@ function createClientGenerator(name, { sse } = {}) {
1377
1405
  validatorHooks,
1378
1406
  returnType,
1379
1407
  throwOnErrorDefault,
1380
- security
1408
+ security,
1409
+ baseURL
1381
1410
  })
1382
1411
  ]
1383
1412
  });
@@ -1388,13 +1417,14 @@ function createClientGenerator(name, { sse } = {}) {
1388
1417
  /**
1389
1418
  * Builds one instance method of a generated SDK class; a per-call `options.client` overrides `this.client`.
1390
1419
  */
1391
- function buildSdkMethod({ node, name, types, validatorHooks, security, returnType, throwOnErrorDefault }) {
1420
+ function buildSdkMethod({ node, name, types, validatorHooks, security, baseURL, returnType, throwOnErrorDefault }) {
1392
1421
  if (!_kubb_kit.ast.isHttpOperationNode(node)) return "";
1393
1422
  const body = buildOperationBody({
1394
1423
  node,
1395
1424
  types,
1396
1425
  validatorHooks,
1397
1426
  security,
1427
+ baseURL,
1398
1428
  returnType,
1399
1429
  throwOnErrorDefault
1400
1430
  });
@@ -1418,12 +1448,13 @@ function buildSdkMethod({ node, name, types, validatorHooks, security, returnTyp
1418
1448
  * still overrides the instance client for a one-off call.
1419
1449
  */
1420
1450
  function SdkClient({ name, operations, returnType, throwOnErrorDefault }) {
1421
- const methods = operations.map(({ node, name: methodName, types, validatorHooks, security }) => buildSdkMethod({
1451
+ const methods = operations.map(({ node, name: methodName, types, validatorHooks, security, baseURL }) => buildSdkMethod({
1422
1452
  node,
1423
1453
  name: methodName,
1424
1454
  types,
1425
1455
  validatorHooks,
1426
1456
  security,
1457
+ baseURL,
1427
1458
  returnType,
1428
1459
  throwOnErrorDefault
1429
1460
  }));
@@ -1478,6 +1509,7 @@ function buildControllers(nodes, ctx, types, validatorEnabled) {
1478
1509
  const pluginZod = validatorEnabled ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1479
1510
  const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1480
1511
  const document = ctx.adapter.document;
1512
+ const server = ctx.adapter.options?.server;
1481
1513
  function buildOperationData(node) {
1482
1514
  const typeFile = types.resolver.file({
1483
1515
  ...operationFileEntry(node, node.operationId),
@@ -1496,6 +1528,12 @@ function buildControllers(nodes, ctx, types, validatorEnabled) {
1496
1528
  method: node.method,
1497
1529
  path: node.path
1498
1530
  }) : void 0;
1531
+ const baseURL = _kubb_kit.ast.isHttpOperationNode(node) ? getOperationBaseURL({
1532
+ document,
1533
+ method: node.method,
1534
+ path: node.path,
1535
+ server
1536
+ }) : void 0;
1499
1537
  const validatorHooks = zodResolver ? buildValidatorHooks({
1500
1538
  node,
1501
1539
  validator,
@@ -1512,7 +1550,8 @@ function buildControllers(nodes, ctx, types, validatorEnabled) {
1512
1550
  typeFile,
1513
1551
  zodFile,
1514
1552
  validatorHooks,
1515
- security
1553
+ security,
1554
+ baseURL
1516
1555
  };
1517
1556
  }
1518
1557
  return nodes.reduce((acc, operationNode) => {
@@ -1801,7 +1840,7 @@ var compatibility = {
1801
1840
  "node": ">=22.22"
1802
1841
  };
1803
1842
  // package.json
1804
- var version = "6.1.0-canary.20261109T175349";
1843
+ var version = "6.1.0-canary.20261109T191216";
1805
1844
  // src/plugin.ts
1806
1845
  /**
1807
1846
  * Canonical plugin name for `@kubb/plugin-fetch`. Used for driver lookups and cross-plugin