@kubb/plugin-client 6.1.0-canary.20261109T175349 → 6.1.0-canary.20261109T175913

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.
@@ -203,7 +203,7 @@ type RequestConfig<TBody = unknown, TRequest = NativeRequest, TResponse = Native
203
203
  transport?: Transport<TRequest, TResponse>;
204
204
  serializer?: Serializers;
205
205
  codecs?: Record<string, Codec>;
206
- /** `errorStatus` lists the statuses `error` covers, such as `[404, '5XX']`. Without it, `error` checks every non-2xx body. */
206
+ /** `error` takes one validator for every non-2xx body, or a map by status such as `{ 404: schema, '5XX': schema, default: schema }`. */
207
207
  validator?: {
208
208
  request?: Validator;
209
209
  path?: Validator;
@@ -211,8 +211,7 @@ type RequestConfig<TBody = unknown, TRequest = NativeRequest, TResponse = Native
211
211
  headers?: Validator;
212
212
  cookies?: Validator;
213
213
  response?: Validator;
214
- error?: Validator;
215
- errorStatus?: Array<number | string>;
214
+ error?: Validator | Record<string, Validator>;
216
215
  };
217
216
  onValidationError?: ValidationErrorHandler;
218
217
  validate?: ValidateOption;
package/dist/index.cjs CHANGED
@@ -547,7 +547,7 @@ var compatibility = {
547
547
  "node": ">=22.22"
548
548
  };
549
549
  // package.json
550
- var version = "6.1.0-canary.20261109T175349";
550
+ var version = "6.1.0-canary.20261109T175913";
551
551
  // ../../internals/client/src/builders/validatorOptions.ts
552
552
  /**
553
553
  * Returns `true` when any direction of the validator uses zod (used for dependency checks).
@@ -583,22 +583,19 @@ function resolveResponseValidator(validator) {
583
583
  return validator.response ?? null;
584
584
  }
585
585
  /**
586
- * Resolves the zod schema name a generated client validates an error body with on the non-throw path.
587
- * Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
588
- * operation documents no error responses with a schema.
586
+ * Builds the `validator.error` map of a call, one plugin-zod `<operation>StatusNNNSchema` per non-2xx response with a
587
+ * schema, such as `{ 404: getPetStatus404Schema, '5XX': getPetStatus5XXSchema }`. Returns `null` without one.
589
588
  */
590
589
  function buildZodErrorParse(node, zodResolver) {
591
- if (!getErrorStatusCodes(node).length) return null;
592
- return zodResolver.response.error?.(node) || null;
593
- }
594
- /** Prints the statuses `<operation>ErrorSchema` covers, such as `[404, '5XX']`, or `null` when a `default` response covers them all. */
595
- function buildErrorStatusLiteral(node) {
596
- const statusCodes = getErrorStatusCodes(node);
597
- if (!statusCodes.length || statusCodes.includes("default")) return null;
598
- return `[${statusCodes.map((statusCode) => /^\d+$/.test(statusCode) ? statusCode : stringify(statusCode)).join(", ")}]`;
599
- }
600
- function getErrorStatusCodes(node) {
601
- return node.responses.filter((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema)).map((res) => String(res.statusCode));
590
+ const entries = node.responses.filter((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema)).map((res) => ({
591
+ key: String(res.statusCode),
592
+ name: zodResolver.response.status(node, res.statusCode)
593
+ }));
594
+ if (!entries.length) return null;
595
+ return {
596
+ literal: `{ ${entries.map(({ key, name }) => `${/^\d+$/.test(key) ? key : stringify(key)}: ${name}`).join(", ")} }`,
597
+ names: entries.map(({ name }) => name)
598
+ };
602
599
  }
603
600
  // ../../internals/client/src/builders/generics.ts
604
601
  /**
@@ -803,8 +800,7 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
803
800
  const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
804
801
  const parsesResponse = zodResolver && resolveResponseValidator(validator) === "zod";
805
802
  const response = parsesResponse ? zodResolver.response.response(node) || null : null;
806
- const error = parsesResponse ? buildZodErrorParse(node, zodResolver) : null;
807
- const errorStatus = error ? buildErrorStatusLiteral(node) : null;
803
+ const errorParse = parsesResponse ? buildZodErrorParse(node, zodResolver) : null;
808
804
  const request = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) || null : null;
809
805
  const paramGroups = getOperationParameters(node);
810
806
  const resolveParamsName = (kind, params) => zodResolver && resolveParamsValidator(validator) === "zod" && params.length > 0 ? zodResolver.param[kind](node, params[0]) : null;
@@ -814,7 +810,7 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
814
810
  const cookies = resolveParamsName("cookies", paramGroups.cookie);
815
811
  const importedZodNames = [
816
812
  response,
817
- error,
813
+ ...errorParse?.names ?? [],
818
814
  request,
819
815
  path,
820
816
  query,
@@ -828,17 +824,17 @@ function buildValidatorHooks({ node, validator, zodResolver }) {
828
824
  headers: headers ? `toHeadersStandardSchema(${headers}, ${headers}.shape)` : null,
829
825
  cookies,
830
826
  response,
831
- error,
832
- errorStatus,
827
+ error: errorParse?.literal ?? null,
833
828
  importedZodNames
834
829
  };
835
830
  }
836
831
  /**
837
832
  * Prints the `validator: { … }` entry of a call config from the hooks named in `keys`, or `null` when none is set.
833
+ * The entry spreads `config.validator` last, so a per-call hook overrides the generated one for its key only.
838
834
  */
839
835
  function buildValidatorLiteral(hooks, keys) {
840
836
  const entries = keys.flatMap((key) => hooks?.[key] ? [`${key}: ${hooks[key]}`] : []);
841
- return entries.length ? `validator: { ${entries.join(", ")} }` : null;
837
+ return entries.length ? `validator: { ${entries.join(", ")}, ...config.validator }` : null;
842
838
  }
843
839
  // ../../internals/client/src/builders/contentType.ts
844
840
  /**
@@ -891,13 +887,13 @@ function serializeParameter(parameter) {
891
887
  return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
892
888
  }
893
889
  /**
894
- * Serializes the `encoding` of the request body's default content type into a `{ property: { ... } }`
895
- * literal, or `null` when it carries none. Only an operation with a single request content type
890
+ * Serializes the `encoding` of the request body's default content type into `property: { ... }`
891
+ * entries, empty when it carries none. Only an operation with a single request content type
896
892
  * emits it, because the runtime applies `styles.body` to whichever type the caller selects.
897
893
  */
898
894
  function serializeBodyEncoding(node) {
899
895
  const content = node.requestBody?.content;
900
- const entries = Object.entries(content?.length === 1 ? content[0]?.encoding ?? {} : {}).map(([property, { contentType, style, explode, allowReserved }]) => {
896
+ return Object.entries(content?.length === 1 ? content[0]?.encoding ?? {} : {}).map(([property, { contentType, style, explode, allowReserved }]) => {
901
897
  const parts = [
902
898
  style && `style: ${JSON.stringify(style)}`,
903
899
  explode !== void 0 && `explode: ${explode}`,
@@ -906,7 +902,6 @@ function serializeBodyEncoding(node) {
906
902
  ].filter(Boolean);
907
903
  return `${isValidVarName(property) ? property : JSON.stringify(property)}: { ${parts.join(", ")} }`;
908
904
  });
909
- return entries.length > 0 ? `body: { ${entries.join(", ")} }` : null;
910
905
  }
911
906
  /**
912
907
  * Builds the per-operation `styles` literal from the operation's parameters, grouped by location.
@@ -916,26 +911,31 @@ function serializeBodyEncoding(node) {
916
911
  * is unchanged. A form request body adds a `body` group keyed by property name from its `encoding`.
917
912
  * Returns `null` when nothing carries metadata.
918
913
  *
914
+ * With `merge`, the literal spreads `config.styles` per parameter, so a per-call entry overrides the generated one for
915
+ * that parameter and leaves the others in place.
916
+ *
919
917
  * @example
920
918
  * ```ts
921
919
  * // a path param with { style: 'matrix', explode: true } and a query param with { explode: false }
922
920
  * buildStyles({ node }) // "{ path: { id: { style: 'matrix', explode: true } }, query: { tags: { explode: false } } }"
923
921
  * ```
924
922
  */
925
- function buildStyles({ node }) {
923
+ function buildStyles({ node, merge }) {
926
924
  if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
927
925
  const groups = {
928
926
  path: [],
929
927
  query: [],
930
928
  header: [],
931
- cookie: []
929
+ cookie: [],
930
+ body: serializeBodyEncoding(node)
932
931
  };
933
932
  for (const [location, parameters] of Object.entries(getOperationParameters(node))) for (const parameter of parameters) {
934
933
  const literal = serializeParameter(parameter);
935
934
  if (literal) groups[location].push(`${toKey(parameter.name)}: ${literal}`);
936
935
  }
937
- const entries = [...Object.keys(groups).filter((location) => groups[location].length > 0).map((location) => `${location}: { ${groups[location].join(", ")} }`), serializeBodyEncoding(node)].filter(Boolean);
938
- return entries.length > 0 ? `{ ${entries.join(", ")} }` : null;
936
+ const entries = Object.entries(groups).filter(([, items]) => items.length > 0).map(([location, items]) => `${location}: { ${(merge ? [...items, `...config.styles?.${location}`] : items).join(", ")} }`);
937
+ if (entries.length === 0) return null;
938
+ return `{ ${(merge ? ["...config.styles", ...entries] : entries).join(", ")} }`;
939
939
  }
940
940
  // ../../internals/client/src/builders/callConfig.ts
941
941
  /**
@@ -951,7 +951,9 @@ function buildResponseTypeLiteral(node) {
951
951
  }
952
952
  /**
953
953
  * Builds the call config literal a generated operation forwards to the client, shared by the standalone
954
- * functions and the SDK methods. The `throwOnError` fallback is applied after the `...config` spread.
954
+ * functions and the SDK methods. The `validator` and `throwOnError` entries come after the `...config` spread, so a
955
+ * per-call `validator` is merged per key with the generated one instead of replacing it. `styles` is merged the same
956
+ * way, per parameter, unless `returnType` is `'plain'`.
955
957
  *
956
958
  * `mergeContentType` is `true` when the caller must destructure `contentType` from `options`, and
957
959
  * `eventStream` is `true` when the operation streams `text/event-stream` or JSON Lines. Unless `returnType` is `'plain'`, a
@@ -960,16 +962,20 @@ function buildResponseTypeLiteral(node) {
960
962
  function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault, returnType }) {
961
963
  const eventStream = isEventStream(node);
962
964
  const securityLiteral = buildSecurityMetadata({ security });
963
- const stylesLiteral = buildStyles({ node });
965
+ const ownRuntime = returnType !== "plain";
966
+ const stylesLiteral = buildStyles({
967
+ node,
968
+ merge: ownRuntime
969
+ });
970
+ const styles = stylesLiteral ? `styles: ${stylesLiteral}` : null;
964
971
  const validatorLiteral = buildValidatorLiteral(validatorHooks, [
965
972
  "request",
966
973
  "path",
967
974
  "query",
968
975
  "headers",
969
976
  "cookies",
970
- ...eventStream && returnType !== "plain" ? [] : ["response"],
971
- "error",
972
- "errorStatus"
977
+ ...eventStream && ownRuntime ? [] : ["response"],
978
+ "error"
973
979
  ]);
974
980
  const { literal: contentTypeLiteral, merge: mergeContentType } = buildContentType({ node });
975
981
  return {
@@ -977,11 +983,12 @@ function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault,
977
983
  `method: '${node.method.toUpperCase()}'`,
978
984
  `url: '${node.path}'`,
979
985
  securityLiteral ? `security: ${securityLiteral}` : null,
980
- stylesLiteral ? `styles: ${stylesLiteral}` : null,
981
- validatorLiteral,
986
+ ownRuntime ? null : styles,
982
987
  contentTypeLiteral,
983
988
  buildResponseTypeLiteral(node),
984
989
  "...config",
990
+ ownRuntime ? styles : null,
991
+ validatorLiteral,
985
992
  `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
986
993
  ].filter(Boolean).join(", ")} }`,
987
994
  mergeContentType,