@kubb/plugin-client 5.0.0-beta.15 → 5.0.0-beta.25

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.js CHANGED
@@ -251,12 +251,12 @@ var URLPath = class {
251
251
  get object() {
252
252
  return this.toObject();
253
253
  }
254
- /** Returns a map of path parameter names, or `undefined` when the path has no parameters.
254
+ /** Returns a map of path parameter names, or `null` when the path has no parameters.
255
255
  *
256
256
  * @example
257
257
  * ```ts
258
258
  * new URLPath('/pet/{petId}').params // { petId: 'petId' }
259
- * new URLPath('/pet').params // undefined
259
+ * new URLPath('/pet').params // null
260
260
  * ```
261
261
  */
262
262
  get params() {
@@ -294,12 +294,13 @@ var URLPath = class {
294
294
  * @example
295
295
  * new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
296
296
  */
297
- toTemplateString({ prefix = "", replacer } = {}) {
298
- return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
297
+ toTemplateString({ prefix, replacer } = {}) {
298
+ const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
299
299
  if (i % 2 === 0) return part;
300
300
  const param = this.#transformParam(part);
301
301
  return `\${${replacer ? replacer(param) : param}}`;
302
- }).join("")}\``;
302
+ }).join("");
303
+ return `\`${prefix ?? ""}${result}\``;
303
304
  }
304
305
  /**
305
306
  * Extracts all `{param}` segments from the path and returns them as a key-value map.
@@ -318,7 +319,7 @@ var URLPath = class {
318
319
  const key = replacer ? replacer(param) : param;
319
320
  params[key] = key;
320
321
  });
321
- return Object.keys(params).length > 0 ? params : void 0;
322
+ return Object.keys(params).length > 0 ? params : null;
322
323
  }
323
324
  /** Converts the OpenAPI path to Express-style colon syntax.
324
325
  *
@@ -334,9 +335,9 @@ var URLPath = class {
334
335
  //#endregion
335
336
  //#region ../../internals/shared/src/operation.ts
336
337
  function getOperationLink(node, link) {
337
- if (!link) return;
338
- if (typeof link === "function") return link(node);
339
- if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : void 0;
338
+ if (!link) return null;
339
+ if (typeof link === "function") return link(node) ?? null;
340
+ if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : null;
340
341
  return `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}`;
341
342
  }
342
343
  function getContentTypeInfo(node) {
@@ -351,9 +352,9 @@ function getContentTypeInfo(node) {
351
352
  };
352
353
  }
353
354
  function buildRequestConfigType(node, resolver) {
354
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0;
355
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null;
355
356
  const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
356
- return `${requestName ? `Partial<RequestConfig<${requestName}>>` : "Partial<RequestConfig>"} & { ${["client?: Client", isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : void 0].filter(Boolean).join("; ")} }`;
357
+ return `${requestName ? `Partial<RequestConfig<${requestName}>>` : "Partial<RequestConfig>"} & { ${["client?: Client", isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null].filter(Boolean).join("; ")} }`;
357
358
  }
358
359
  function buildOperationComments(node, options = {}) {
359
360
  const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
@@ -383,11 +384,11 @@ function getOperationParameters(node, options = {}) {
383
384
  }
384
385
  function getStatusCodeNumber(statusCode) {
385
386
  const code = Number(statusCode);
386
- return Number.isNaN(code) ? void 0 : code;
387
+ return Number.isNaN(code) ? null : code;
387
388
  }
388
389
  function isErrorStatusCode(statusCode) {
389
390
  const code = getStatusCodeNumber(statusCode);
390
- return code !== void 0 && code >= 400;
391
+ return code !== null && code >= 400;
391
392
  }
392
393
  function resolveErrorNames(node, resolver) {
393
394
  return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
@@ -414,7 +415,7 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
414
415
  ...query.map((param) => resolver.resolveQueryParamsName(node, param)),
415
416
  ...header.map((param) => resolver.resolveHeaderParamsName(node, param))
416
417
  ];
417
- const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0, resolver.resolveResponseName(node)];
418
+ const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
418
419
  const result = (options.order === "body-response-first" ? [
419
420
  ...bodyAndResponseNames,
420
421
  ...paramNames,
@@ -437,7 +438,7 @@ function buildParamsMapping(originalParams, mappedParams) {
437
438
  mapping[param.name] = mappedName;
438
439
  if (param.name !== mappedName) hasChanged = true;
439
440
  });
440
- return hasChanged ? mapping : void 0;
441
+ return hasChanged ? mapping : null;
441
442
  }
442
443
  //#endregion
443
444
  //#region src/functionParams.ts
@@ -450,18 +451,18 @@ function createType(type) {
450
451
  return type ? ast.createParamsType({
451
452
  variant: "reference",
452
453
  name: type
453
- }) : void 0;
454
+ }) : null;
454
455
  }
455
456
  function createDeclarationLeaf(name, spec) {
456
457
  if (spec.default !== void 0) return ast.createFunctionParameter({
457
458
  name,
458
- type: createType(spec.type),
459
+ type: createType(spec.type) ?? void 0,
459
460
  default: spec.default,
460
461
  rest: spec.mode === "inlineSpread"
461
462
  });
462
463
  return ast.createFunctionParameter({
463
464
  name,
464
- type: createType(spec.type),
465
+ type: createType(spec.type) ?? void 0,
465
466
  optional: !!spec.optional,
466
467
  rest: spec.mode === "inlineSpread"
467
468
  });
@@ -470,14 +471,14 @@ function createDeclarationParam(name, spec) {
470
471
  if (isGroup(spec)) return ast.createParameterGroup({
471
472
  inline: spec.mode === "inlineSpread",
472
473
  default: spec.default,
473
- properties: Object.entries(spec.children).filter(([, child]) => child !== void 0).map(([childName, child]) => createDeclarationLeaf(childName, child))
474
+ properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => createDeclarationLeaf(childName, child))
474
475
  });
475
476
  return createDeclarationLeaf(name, spec);
476
477
  }
477
478
  function createCallParam(name, spec) {
478
479
  if (isGroup(spec)) return ast.createParameterGroup({
479
480
  inline: spec.mode === "inlineSpread",
480
- properties: Object.entries(spec.children).filter(([, child]) => child !== void 0).map(([childName, child]) => ast.createFunctionParameter({
481
+ properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => ast.createFunctionParameter({
481
482
  name: child?.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child?.value ? `${childName}: ${child.value}` : childName,
482
483
  rest: spec.mode === "inlineSpread" && child?.mode === "inlineSpread"
483
484
  }))
@@ -492,7 +493,7 @@ function createCallParam(name, spec) {
492
493
  * Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
493
494
  */
494
495
  function createFunctionParams(params) {
495
- const entries = Object.entries(params).filter(([, spec]) => spec !== void 0);
496
+ const entries = Object.entries(params).filter(([, spec]) => spec != null);
496
497
  return {
497
498
  toConstructor() {
498
499
  return declarationPrinter$4.print(ast.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
@@ -530,7 +531,7 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
530
531
  const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
531
532
  const { path: originalPathParams } = getOperationParameters(node);
532
533
  const { path: casedPathParams } = getOperationParameters(node, { paramsCasing });
533
- const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : void 0;
534
+ const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : null;
534
535
  return /* @__PURE__ */ jsx(File.Source, {
535
536
  name,
536
537
  isExportable,
@@ -577,19 +578,19 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
577
578
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
578
579
  const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node);
579
580
  const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing });
580
- const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : void 0;
581
- const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : void 0;
582
- const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : void 0;
583
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
581
+ const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : null;
582
+ const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : null;
583
+ const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : null;
584
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
584
585
  const responseName = tsResolver.resolveResponseName(node);
585
- const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : void 0;
586
- const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : void 0;
587
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
588
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
586
+ const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
587
+ const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
588
+ const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
589
+ const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
589
590
  const errorNames = node.responses.filter((r) => {
590
591
  return Number.parseInt(r.statusCode, 10) >= 400;
591
592
  }).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
592
- const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : void 0].filter(Boolean);
593
+ const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null].filter(Boolean);
593
594
  const generics = [
594
595
  responseName,
595
596
  `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
@@ -617,12 +618,12 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
617
618
  children: {
618
619
  method: { value: JSON.stringify(node.method.toUpperCase()) },
619
620
  url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path.template },
620
- baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : void 0,
621
- params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : void 0,
622
- data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : void 0,
623
- contentType: isConfigurable && isMultipleContentTypes ? {} : void 0,
624
- requestConfig: isConfigurable ? { mode: "inlineSpread" } : void 0,
625
- headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : void 0
621
+ baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
622
+ params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : null,
623
+ data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
624
+ contentType: isConfigurable && isMultipleContentTypes ? {} : null,
625
+ requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
626
+ headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
626
627
  }
627
628
  } });
628
629
  const childrenElement = children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -680,7 +681,7 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
680
681
  * Includes Content-Type (if not default) and spreads header parameters if present.
681
682
  */
682
683
  function buildHeaders(contentType, hasHeaderParams) {
683
- return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0, hasHeaderParams ? "...headers" : void 0].filter(Boolean);
684
+ return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
684
685
  }
685
686
  /**
686
687
  * Builds TypeScript generic parameters for a client method.
@@ -688,7 +689,7 @@ function buildHeaders(contentType, hasHeaderParams) {
688
689
  */
689
690
  function buildGenerics(node, tsResolver) {
690
691
  const responseName = tsResolver.resolveResponseName(node);
691
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
692
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
692
693
  const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
693
694
  return [
694
695
  responseName,
@@ -702,19 +703,19 @@ function buildGenerics(node, tsResolver) {
702
703
  */
703
704
  function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers }) {
704
705
  const { query: queryParams } = getOperationParameters(node);
705
- const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : void 0;
706
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
706
+ const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
707
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
707
708
  return createFunctionParams({ config: {
708
709
  mode: "object",
709
710
  children: {
710
711
  requestConfig: { mode: "inlineSpread" },
711
712
  method: { value: JSON.stringify(node.method.toUpperCase()) },
712
713
  url: { value: path.template },
713
- baseURL: baseURL ? { value: JSON.stringify(baseURL) } : void 0,
714
- params: queryParamsName ? {} : void 0,
715
- data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : void 0,
716
- contentType: isMultipleContentTypes ? {} : void 0,
717
- headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : void 0
714
+ baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
715
+ params: queryParamsName ? {} : null,
716
+ data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
717
+ contentType: isMultipleContentTypes ? {} : null,
718
+ headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
718
719
  }
719
720
  } });
720
721
  }
@@ -723,7 +724,7 @@ function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, i
723
724
  * Applies Zod validation if configured, otherwise uses data directly.
724
725
  */
725
726
  function buildRequestDataLine({ parser, node, zodResolver }) {
726
- const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
727
+ const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
727
728
  if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
728
729
  if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
729
730
  return "";
@@ -740,7 +741,7 @@ function buildFormDataLine(isFormData, hasRequest) {
740
741
  * Applies Zod validation to response data if configured, otherwise returns raw response.
741
742
  */
742
743
  function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
743
- const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
744
+ const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
744
745
  if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
745
746
  if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
746
747
  if (dataReturnType === "full" && parser === "client") return "return res";
@@ -754,7 +755,7 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
754
755
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
755
756
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
756
757
  const { header: headerParams } = getOperationParameters(node);
757
- const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : void 0;
758
+ const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
758
759
  const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
759
760
  const generics = buildGenerics(node, tsResolver);
760
761
  const paramsNode = buildClientParamsNode({
@@ -855,22 +856,27 @@ function resolveTypeImportNames$1(node, tsResolver) {
855
856
  }
856
857
  __name(resolveTypeImportNames$1, "resolveTypeImportNames");
857
858
  function resolveZodImportNames$1(node, zodResolver) {
858
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter((n) => Boolean(n));
859
+ return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((n) => Boolean(n));
859
860
  }
860
861
  __name(resolveZodImportNames$1, "resolveZodImportNames");
862
+ /**
863
+ * Built-in `operations` generator for `@kubb/plugin-client` when
864
+ * `clientType: 'class'`. Emits one class per tag, with one instance method
865
+ * per operation and a shared constructor for request configuration.
866
+ */
861
867
  const classClientGenerator = defineGenerator({
862
868
  name: "classClient",
863
869
  renderer: jsxRendererSync,
864
870
  operations(nodes, ctx) {
865
- const { config, driver, resolver, root, inputNode } = ctx;
871
+ const { config, driver, resolver, root } = ctx;
866
872
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
867
- const baseURL = ctx.options.baseURL ?? inputNode.meta?.baseURL;
873
+ const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
868
874
  const pluginTs = driver.getPlugin(pluginTsName);
869
875
  if (!pluginTs) return null;
870
876
  const tsResolver = driver.getResolver(pluginTsName);
871
877
  const tsPluginOptions = pluginTs.options;
872
- const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : void 0;
873
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
878
+ const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
879
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
874
880
  function buildOperationData(node) {
875
881
  const typeFile = tsResolver.resolveFile({
876
882
  name: node.operationId,
@@ -890,8 +896,8 @@ const classClientGenerator = defineGenerator({
890
896
  }, {
891
897
  root,
892
898
  output: pluginZod.options?.output ?? output,
893
- group: pluginZod.options?.group
894
- }) : void 0;
899
+ group: pluginZod.options?.group ?? void 0
900
+ }) : null;
895
901
  return {
896
902
  node,
897
903
  name: resolver.resolveName(node.operationId),
@@ -912,7 +918,7 @@ const classClientGenerator = defineGenerator({
912
918
  }, {
913
919
  root,
914
920
  output,
915
- group
921
+ group: group ?? void 0
916
922
  });
917
923
  const operationData = buildOperationData(operationNode);
918
924
  const previous = acc.find((item) => item.file.path === file.path);
@@ -923,7 +929,9 @@ const classClientGenerator = defineGenerator({
923
929
  file,
924
930
  operations: [operationData]
925
931
  });
926
- } else if (tag) {
932
+ return acc;
933
+ }
934
+ if (tag) {
927
935
  const name = groupName;
928
936
  const file = resolver.resolveFile({
929
937
  name,
@@ -932,7 +940,7 @@ const classClientGenerator = defineGenerator({
932
940
  }, {
933
941
  root,
934
942
  output,
935
- group
943
+ group: group ?? void 0
936
944
  });
937
945
  const operationData = buildOperationData(operationNode);
938
946
  const previous = acc.find((item) => item.file.path === file.path);
@@ -992,11 +1000,11 @@ const classClientGenerator = defineGenerator({
992
1000
  baseName: file.baseName,
993
1001
  path: file.path,
994
1002
  meta: file.meta,
995
- banner: resolver.resolveBanner(inputNode, {
1003
+ banner: resolver.resolveBanner(ctx.meta, {
996
1004
  output,
997
1005
  config
998
1006
  }),
999
- footer: resolver.resolveFooter(inputNode, {
1007
+ footer: resolver.resolveFooter(ctx.meta, {
1000
1008
  output,
1001
1009
  config
1002
1010
  }),
@@ -1089,17 +1097,17 @@ const classClientGenerator = defineGenerator({
1089
1097
  }, {
1090
1098
  root,
1091
1099
  output,
1092
- group
1100
+ group: group ?? void 0
1093
1101
  });
1094
1102
  files.push(/* @__PURE__ */ jsxs(File, {
1095
1103
  baseName: sdkFile.baseName,
1096
1104
  path: sdkFile.path,
1097
1105
  meta: sdkFile.meta,
1098
- banner: resolver.resolveBanner(inputNode, {
1106
+ banner: resolver.resolveBanner(ctx.meta, {
1099
1107
  output,
1100
1108
  config
1101
1109
  }),
1102
- footer: resolver.resolveFooter(inputNode, {
1110
+ footer: resolver.resolveFooter(ctx.meta, {
1103
1111
  output,
1104
1112
  config
1105
1113
  }),
@@ -1134,20 +1142,25 @@ const classClientGenerator = defineGenerator({
1134
1142
  });
1135
1143
  //#endregion
1136
1144
  //#region src/generators/clientGenerator.tsx
1145
+ /**
1146
+ * Built-in operation generator for `@kubb/plugin-client`. Emits one async
1147
+ * function per OpenAPI operation, plus the matching URL helper. Used when
1148
+ * `clientType: 'function'` (the default).
1149
+ */
1137
1150
  const clientGenerator = defineGenerator({
1138
1151
  name: "client",
1139
1152
  renderer: jsxRendererSync,
1140
1153
  operation(node, ctx) {
1141
- const { config, driver, resolver, root, inputNode } = ctx;
1154
+ const { config, driver, resolver, root } = ctx;
1142
1155
  const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options;
1143
- const baseURL = ctx.options.baseURL ?? inputNode.meta?.baseURL;
1156
+ const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
1144
1157
  const pluginTs = driver.getPlugin(pluginTsName);
1145
1158
  if (!pluginTs) return null;
1146
1159
  const tsResolver = driver.getResolver(pluginTsName);
1147
- const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : void 0;
1148
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
1160
+ const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
1161
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1149
1162
  const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
1150
- const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter((name) => Boolean(name)) : [];
1163
+ const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((name) => Boolean(name)) : [];
1151
1164
  const meta = {
1152
1165
  name: resolver.resolveName(node.operationId),
1153
1166
  urlName: resolver.resolveUrlName(node),
@@ -1159,7 +1172,7 @@ const clientGenerator = defineGenerator({
1159
1172
  }, {
1160
1173
  root,
1161
1174
  output,
1162
- group
1175
+ group: group ?? void 0
1163
1176
  }),
1164
1177
  fileTs: tsResolver.resolveFile({
1165
1178
  name: node.operationId,
@@ -1169,7 +1182,7 @@ const clientGenerator = defineGenerator({
1169
1182
  }, {
1170
1183
  root,
1171
1184
  output: pluginTs.options?.output ?? output,
1172
- group: pluginTs.options?.group
1185
+ group: pluginTs.options?.group ?? void 0
1173
1186
  }),
1174
1187
  fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile({
1175
1188
  name: node.operationId,
@@ -1179,19 +1192,19 @@ const clientGenerator = defineGenerator({
1179
1192
  }, {
1180
1193
  root,
1181
1194
  output: pluginZod.options.output ?? output,
1182
- group: pluginZod.options?.group
1183
- }) : void 0
1195
+ group: pluginZod.options?.group ?? void 0
1196
+ }) : null
1184
1197
  };
1185
1198
  const hasFormData = node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false;
1186
1199
  return /* @__PURE__ */ jsxs(File, {
1187
1200
  baseName: meta.file.baseName,
1188
1201
  path: meta.file.path,
1189
1202
  meta: meta.file.meta,
1190
- banner: resolver.resolveBanner(inputNode, {
1203
+ banner: resolver.resolveBanner(ctx.meta, {
1191
1204
  output,
1192
1205
  config
1193
1206
  }),
1194
- footer: resolver.resolveFooter(inputNode, {
1207
+ footer: resolver.resolveFooter(ctx.meta, {
1195
1208
  output,
1196
1209
  config
1197
1210
  }),
@@ -1267,16 +1280,22 @@ const clientGenerator = defineGenerator({
1267
1280
  });
1268
1281
  //#endregion
1269
1282
  //#region src/generators/groupedClientGenerator.tsx
1283
+ /**
1284
+ * Emits one aggregate file per tag/group when `group` is configured. Each
1285
+ * file re-exports every client function for that group, so callers can
1286
+ * `import { petController } from './gen/clients'` instead of importing
1287
+ * each operation individually.
1288
+ */
1270
1289
  const groupedClientGenerator = defineGenerator({
1271
1290
  name: "groupedClient",
1272
1291
  renderer: jsxRendererSync,
1273
1292
  operations(nodes, ctx) {
1274
- const { config, resolver, root, inputNode } = ctx;
1293
+ const { config, resolver, root } = ctx;
1275
1294
  const { output, group } = ctx.options;
1276
1295
  return /* @__PURE__ */ jsx(Fragment, { children: nodes.reduce((acc, operationNode) => {
1277
1296
  if (group?.type === "tag") {
1278
1297
  const tag = operationNode.tags[0];
1279
- const name = tag ? group?.name?.({ group: camelCase(tag) }) : void 0;
1298
+ const name = tag ? group?.name?.({ group: camelCase(tag) }) : null;
1280
1299
  if (!tag || !name) return acc;
1281
1300
  const file = resolver.resolveFile({
1282
1301
  name,
@@ -1285,7 +1304,7 @@ const groupedClientGenerator = defineGenerator({
1285
1304
  }, {
1286
1305
  root,
1287
1306
  output,
1288
- group
1307
+ group: group ?? void 0
1289
1308
  });
1290
1309
  const clientFile = resolver.resolveFile({
1291
1310
  name: operationNode.operationId,
@@ -1295,7 +1314,7 @@ const groupedClientGenerator = defineGenerator({
1295
1314
  }, {
1296
1315
  root,
1297
1316
  output,
1298
- group
1317
+ group: group ?? void 0
1299
1318
  });
1300
1319
  const client = {
1301
1320
  name: resolver.resolveName(operationNode.operationId),
@@ -1315,11 +1334,11 @@ const groupedClientGenerator = defineGenerator({
1315
1334
  baseName: file.baseName,
1316
1335
  path: file.path,
1317
1336
  meta: file.meta,
1318
- banner: resolver.resolveBanner(inputNode, {
1337
+ banner: resolver.resolveBanner(ctx.meta, {
1319
1338
  output,
1320
1339
  config
1321
1340
  }),
1322
- footer: resolver.resolveFooter(inputNode, {
1341
+ footer: resolver.resolveFooter(ctx.meta, {
1323
1342
  output,
1324
1343
  config
1325
1344
  }),
@@ -1364,11 +1383,17 @@ function Operations({ name, nodes }) {
1364
1383
  }
1365
1384
  //#endregion
1366
1385
  //#region src/generators/operationsGenerator.tsx
1386
+ /**
1387
+ * Generates an `operations.ts` file that re-exports every operation grouped
1388
+ * by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
1389
+ * for building meta-tooling on top of the generated client (route
1390
+ * registries, API explorers).
1391
+ */
1367
1392
  const operationsGenerator = defineGenerator({
1368
1393
  name: "client",
1369
1394
  renderer: jsxRendererSync,
1370
1395
  operations(nodes, ctx) {
1371
- const { config, resolver, root, inputNode } = ctx;
1396
+ const { config, resolver, root } = ctx;
1372
1397
  const { output, group } = ctx.options;
1373
1398
  const name = "operations";
1374
1399
  const file = resolver.resolveFile({
@@ -1377,17 +1402,17 @@ const operationsGenerator = defineGenerator({
1377
1402
  }, {
1378
1403
  root,
1379
1404
  output,
1380
- group
1405
+ group: group ?? void 0
1381
1406
  });
1382
1407
  return /* @__PURE__ */ jsx(File, {
1383
1408
  baseName: file.baseName,
1384
1409
  path: file.path,
1385
1410
  meta: file.meta,
1386
- banner: resolver.resolveBanner(inputNode, {
1411
+ banner: resolver.resolveBanner(ctx.meta, {
1387
1412
  output,
1388
1413
  config
1389
1414
  }),
1390
- footer: resolver.resolveFooter(inputNode, {
1415
+ footer: resolver.resolveFooter(ctx.meta, {
1391
1416
  output,
1392
1417
  config
1393
1418
  }),
@@ -1406,7 +1431,7 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
1406
1431
  const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
1407
1432
  const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
1408
1433
  const { header: headerParams } = getOperationParameters(node);
1409
- const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : void 0;
1434
+ const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
1410
1435
  const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
1411
1436
  const generics = buildGenerics(node, tsResolver);
1412
1437
  const paramsNode = buildClientParamsNode({
@@ -1480,21 +1505,27 @@ function resolveTypeImportNames(node, tsResolver) {
1480
1505
  return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
1481
1506
  }
1482
1507
  function resolveZodImportNames(node, zodResolver) {
1483
- return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter((n) => Boolean(n));
1508
+ return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((n) => Boolean(n));
1484
1509
  }
1510
+ /**
1511
+ * Built-in `operations` generator for `@kubb/plugin-client` when
1512
+ * `clientType: 'staticClass'`. Emits one class per tag, with a static method
1513
+ * per operation so callers can use `Pet.getPetById(...)` without
1514
+ * instantiating the class.
1515
+ */
1485
1516
  const staticClassClientGenerator = defineGenerator({
1486
1517
  name: "staticClassClient",
1487
1518
  renderer: jsxRendererSync,
1488
1519
  operations(nodes, ctx) {
1489
- const { config, driver, resolver, root, inputNode } = ctx;
1520
+ const { config, driver, resolver, root } = ctx;
1490
1521
  const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
1491
- const baseURL = ctx.options.baseURL ?? inputNode.meta?.baseURL;
1522
+ const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
1492
1523
  const pluginTs = driver.getPlugin(pluginTsName);
1493
1524
  if (!pluginTs) return null;
1494
1525
  const tsResolver = driver.getResolver(pluginTsName);
1495
1526
  const tsPluginOptions = pluginTs.options;
1496
- const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : void 0;
1497
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : void 0;
1527
+ const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
1528
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1498
1529
  function buildOperationData(node) {
1499
1530
  const typeFile = tsResolver.resolveFile({
1500
1531
  name: node.operationId,
@@ -1514,8 +1545,8 @@ const staticClassClientGenerator = defineGenerator({
1514
1545
  }, {
1515
1546
  root,
1516
1547
  output: pluginZod.options?.output ?? output,
1517
- group: pluginZod.options?.group
1518
- }) : void 0;
1548
+ group: pluginZod.options?.group ?? void 0
1549
+ }) : null;
1519
1550
  return {
1520
1551
  node,
1521
1552
  name: resolver.resolveName(node.operationId),
@@ -1536,7 +1567,7 @@ const staticClassClientGenerator = defineGenerator({
1536
1567
  }, {
1537
1568
  root,
1538
1569
  output,
1539
- group
1570
+ group: group ?? void 0
1540
1571
  });
1541
1572
  const operationData = buildOperationData(operationNode);
1542
1573
  const previous = acc.find((item) => item.file.path === file.path);
@@ -1546,7 +1577,9 @@ const staticClassClientGenerator = defineGenerator({
1546
1577
  file,
1547
1578
  operations: [operationData]
1548
1579
  });
1549
- } else if (tag) {
1580
+ return acc;
1581
+ }
1582
+ if (tag) {
1550
1583
  const name = groupName;
1551
1584
  const file = resolver.resolveFile({
1552
1585
  name,
@@ -1555,7 +1588,7 @@ const staticClassClientGenerator = defineGenerator({
1555
1588
  }, {
1556
1589
  root,
1557
1590
  output,
1558
- group
1591
+ group: group ?? void 0
1559
1592
  });
1560
1593
  const operationData = buildOperationData(operationNode);
1561
1594
  const previous = acc.find((item) => item.file.path === file.path);
@@ -1614,11 +1647,11 @@ const staticClassClientGenerator = defineGenerator({
1614
1647
  baseName: file.baseName,
1615
1648
  path: file.path,
1616
1649
  meta: file.meta,
1617
- banner: resolver.resolveBanner(inputNode, {
1650
+ banner: resolver.resolveBanner(ctx.meta, {
1618
1651
  output,
1619
1652
  config
1620
1653
  }),
1621
- footer: resolver.resolveFooter(inputNode, {
1654
+ footer: resolver.resolveFooter(ctx.meta, {
1622
1655
  output,
1623
1656
  config
1624
1657
  }),
@@ -1709,12 +1742,18 @@ const staticClassClientGenerator = defineGenerator({
1709
1742
  //#endregion
1710
1743
  //#region src/resolvers/resolverClient.ts
1711
1744
  /**
1712
- * Naming convention resolver for client plugin.
1745
+ * Default resolver used by `@kubb/plugin-client`. Decides the names and file
1746
+ * paths for every generated client function or class. Functions and files use
1747
+ * camelCase; classes and tag groups use PascalCase.
1713
1748
  *
1714
- * Provides default naming helpers using camelCase for functions and file paths.
1749
+ * @example Resolve client function and class names
1750
+ * ```ts
1751
+ * import { resolverClient } from '@kubb/plugin-client'
1715
1752
  *
1716
- * @example
1717
- * `resolverClient.default('list pets', 'function') // 'listPets'`
1753
+ * resolverClient.default('list pets', 'function') // 'listPets'
1754
+ * resolverClient.resolveClassName('pet') // 'Pet'
1755
+ * resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
1756
+ * ```
1718
1757
  */
1719
1758
  const resolverClient = defineResolver(() => ({
1720
1759
  name: "default",
@@ -1745,19 +1784,32 @@ const resolverClient = defineResolver(() => ({
1745
1784
  //#endregion
1746
1785
  //#region src/plugin.ts
1747
1786
  /**
1748
- * Canonical plugin name for `@kubb/plugin-client`, used in driver lookups and warnings.
1787
+ * Canonical plugin name for `@kubb/plugin-client`. Used for driver lookups and
1788
+ * cross-plugin dependency references.
1749
1789
  */
1750
1790
  const pluginClientName = "plugin-client";
1751
1791
  /**
1752
- * Generates type-safe HTTP client functions or classes from an OpenAPI specification.
1753
- * Creates client APIs by walking operations and delegating to generators.
1754
- * Writes barrel files based on the configured `barrelType`.
1792
+ * Generates one HTTP client function per OpenAPI operation. Each function has
1793
+ * typed path params, query params, body, and response, so callers use the API
1794
+ * like any other typed function. Ships with `axios` and `fetch` runtimes; bring
1795
+ * your own by setting `importPath`.
1755
1796
  *
1756
- * @example Client generator
1797
+ * @example
1757
1798
  * ```ts
1758
- * import pluginClient from '@kubb/plugin-client'
1799
+ * import { defineConfig } from 'kubb'
1800
+ * import { pluginTs } from '@kubb/plugin-ts'
1801
+ * import { pluginClient } from '@kubb/plugin-client'
1802
+ *
1759
1803
  * export default defineConfig({
1760
- * plugins: [pluginClient({ output: { path: 'clients' } })]
1804
+ * input: { path: './petStore.yaml' },
1805
+ * output: { path: './src/gen' },
1806
+ * plugins: [
1807
+ * pluginTs(),
1808
+ * pluginClient({
1809
+ * output: { path: './clients' },
1810
+ * client: 'fetch',
1811
+ * }),
1812
+ * ],
1761
1813
  * })
1762
1814
  * ```
1763
1815
  */
@@ -1769,8 +1821,8 @@ const pluginClient = definePlugin((options) => {
1769
1821
  const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
1770
1822
  const selectedGenerators = options.generators ?? [
1771
1823
  clientType === "staticClass" ? staticClassClientGenerator : clientType === "class" ? classClientGenerator : clientGenerator,
1772
- group && clientType === "function" ? groupedClientGenerator : void 0,
1773
- operations ? operationsGenerator : void 0
1824
+ group && clientType === "function" ? groupedClientGenerator : null,
1825
+ operations ? operationsGenerator : null
1774
1826
  ].filter((x) => Boolean(x));
1775
1827
  const groupConfig = group ? {
1776
1828
  ...group,
@@ -1778,11 +1830,11 @@ const pluginClient = definePlugin((options) => {
1778
1830
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1779
1831
  return `${camelCase(ctx.group)}Controller`;
1780
1832
  }
1781
- } : void 0;
1833
+ } : null;
1782
1834
  return {
1783
1835
  name: pluginClientName,
1784
1836
  options,
1785
- dependencies: [pluginTsName, parser === "zod" ? pluginZodName : void 0].filter((dependency) => Boolean(dependency)),
1837
+ dependencies: [pluginTsName, parser === "zod" ? pluginZodName : null].filter((dependency) => Boolean(dependency)),
1786
1838
  hooks: { "kubb:plugin:setup"(ctx) {
1787
1839
  const resolver = userResolver ? {
1788
1840
  ...resolverClient,