@kubb/plugin-fetch 5.0.0-beta.85 → 5.0.0-beta.87

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
@@ -1,5 +1,5 @@
1
1
  import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
- import { Exclude, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver, ast } from "kubb/kit";
2
+ import { Exclude, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver, ResolverPatch, ast } from "kubb/kit";
3
3
  //#region ../../internals/client/src/types.d.ts
4
4
  /**
5
5
  * Validator applied to request and response bodies using schemas from `@kubb/plugin-zod`.
@@ -21,39 +21,28 @@ type ValidatorOptions = false | 'zod' | {
21
21
  */
22
22
  type Mode = 'tag' | 'flat';
23
23
  /**
24
- * The resolver shared by the client plugins. Functions and files use camelCase; URL helpers get
25
- * a `get<Operation>Url` name.
24
+ * The resolver shared by the client plugins. Inherits the built-in camelCase `name` and `file`;
25
+ * classes and tag groups use PascalCase (with a `Client` suffix for groups).
26
26
  */
27
27
  type ResolverClient = Resolver & {
28
- /**
29
- * Resolves the function name for a raw operation name.
30
- *
31
- * @example
32
- * `resolver.resolveName('show pet by id') // -> 'showPetById'`
33
- */
34
- resolveName(this: ResolverClient, name: string): string;
35
- /**
36
- * Resolves the output file name for a generated client module.
37
- */
38
- resolvePathName(this: ResolverClient, name: string, type?: 'file' | 'function' | 'type' | 'const'): string;
39
28
  /**
40
29
  * Resolves the generated class name for class-based clients.
41
30
  */
42
- resolveClassName(this: ResolverClient, name: string): string;
31
+ className(this: ResolverClient, name: string): string;
43
32
  /**
44
33
  * Resolves the generated class name for a tag-based client group. The default appends a
45
34
  * `Client` suffix (tag `pet` becomes `PetClient`) so the class never collides with the schema
46
35
  * model of the same name in the barrel.
47
36
  *
48
37
  * @example
49
- * `resolver.resolveGroupName('pet') // -> 'PetClient'`
38
+ * `resolver.groupName('pet') // -> 'PetClient'`
50
39
  */
51
- resolveGroupName(this: ResolverClient, name: string): string;
40
+ groupName(this: ResolverClient, name: string): string;
52
41
  /**
53
42
  * Resolves the property name a tag client is exposed under on the composed root SDK
54
43
  * (`new PetStore(config).pet`).
55
44
  */
56
- resolveClientPropertyName(this: ResolverClient, name: string): string;
45
+ propertyName(this: ResolverClient, name: string): string;
57
46
  };
58
47
  /**
59
48
  * The shared options surface for the client plugins. Deliberately small: there is one
@@ -134,7 +123,7 @@ type Options = OutputOptions & {
134
123
  /**
135
124
  * Override how names and file paths are built. Methods you omit fall back to the default resolver.
136
125
  */
137
- resolver?: Partial<ResolverClient> & ThisType<ResolverClient>;
126
+ resolver?: ResolverPatch<ResolverClient>;
138
127
  /**
139
128
  * Macros applied to each operation node before code is printed.
140
129
  */
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "./rolldown-runtime-C0LytTxp.js";
2
2
  import path from "node:path";
3
- import { ast, defineGenerator, definePlugin, defineResolver } from "kubb/kit";
3
+ import { Resolver, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
4
4
  import { File, Function, jsxRenderer } from "kubb/jsx";
5
5
  import { createFunctionParameter, createFunctionParameters, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
6
6
  import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
@@ -45,31 +45,6 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
45
45
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
46
46
  }
47
47
  //#endregion
48
- //#region ../../internals/utils/src/fs.ts
49
- /**
50
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
51
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
52
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
53
- *
54
- * Empty segments are dropped before joining. They arise when the name starts with a dot
55
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
56
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
57
- * absolute path, letting generated files escape the configured output directory.
58
- *
59
- * @example Nested path from a dotted name
60
- * `toFilePath('pet.petId') // 'pet/petId'`
61
- *
62
- * @example PascalCase the final segment
63
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
64
- *
65
- * @example Suffix applied to the final segment only
66
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
67
- */
68
- function toFilePath(name, caseLast = camelCase) {
69
- const parts = name.split(/\.(?=[a-zA-Z])/);
70
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
71
- }
72
- //#endregion
73
48
  //#region ../../internals/utils/src/reserved.ts
74
49
  /**
75
50
  * JavaScript and Java reserved words.
@@ -170,7 +145,7 @@ const reservedWords = /* @__PURE__ */ new Set([
170
145
  */
171
146
  function isValidVarName(name) {
172
147
  if (!name || reservedWords.has(name)) return false;
173
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
148
+ return isIdentifier(name);
174
149
  }
175
150
  /**
176
151
  * Returns `name` when it's a syntactically valid JavaScript variable name,
@@ -192,6 +167,39 @@ function ensureValidVarName(name) {
192
167
  if (!name || isValidVarName(name)) return name;
193
168
  return `_${name}`;
194
169
  }
170
+ /**
171
+ * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
172
+ *
173
+ * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
174
+ * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
175
+ * deciding whether an object key needs quoting.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * isIdentifier('name') // true
180
+ * isIdentifier('x-total')// false
181
+ * ```
182
+ */
183
+ function isIdentifier(name) {
184
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
185
+ }
186
+ //#endregion
187
+ //#region ../../internals/utils/src/codegen.ts
188
+ /**
189
+ * Builds a JSDoc comment block from an array of lines. Returns `fallback` when there are no
190
+ * comments.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * buildJSDoc(['@type string', '@example hello'])
195
+ * // '/**\n * @type string\n * @example hello\n *\/\n '
196
+ * ```
197
+ */
198
+ function buildJSDoc(comments, options = {}) {
199
+ const { indent = " * ", suffix = "\n ", fallback = " " } = options;
200
+ if (comments.length === 0) return fallback;
201
+ return `/**\n${comments.map((c) => `${indent}${c}`).join("\n")}\n */${suffix}`;
202
+ }
195
203
  //#endregion
196
204
  //#region ../../internals/utils/src/url.ts
197
205
  function transformParam(raw, casing) {
@@ -373,13 +381,13 @@ function buildParamsRemapExpression({ source, mapping }) {
373
381
  //#region ../../internals/shared/src/operation.ts
374
382
  /**
375
383
  * Builds the `ResolverFileParams` every operation generator passes to
376
- * `resolver.resolveFile`: a file named `name`, tagged by the operation's first
384
+ * `resolver.file`: a file named `name`, tagged by the operation's first
377
385
  * tag (or `'default'`), at the operation's path. Centralizes the entry object
378
386
  * that was repeated at dozens of call sites across the client and query plugins.
379
387
  *
380
388
  * @example
381
389
  * ```ts
382
- * resolver.resolveFile(operationFileEntry(node, node.operationId), { root, output, group })
390
+ * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
383
391
  * ```
384
392
  */
385
393
  function operationFileEntry(node, name, extname = ".ts") {
@@ -396,8 +404,11 @@ function getOperationLink(node, link) {
396
404
  if (link === "urlPath") return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
397
405
  return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
398
406
  }
399
- function getContentTypeInfo(node) {
400
- const contentTypes = node.requestBody?.content?.map((e) => e.contentType) ?? [];
407
+ /**
408
+ * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
409
+ * are present and the union, default, and form-data flags the client uses to pick one.
410
+ */
411
+ function buildContentTypeInfo(contentTypes) {
401
412
  const isMultipleContentTypes = contentTypes.length > 1;
402
413
  return {
403
414
  contentTypes,
@@ -407,20 +418,15 @@ function getContentTypeInfo(node) {
407
418
  hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
408
419
  };
409
420
  }
421
+ function getContentTypeInfo(node) {
422
+ return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? []);
423
+ }
410
424
  /**
411
425
  * The request-body counterpart for the primary success response: the content types it documents and
412
426
  * whether several are present, so the client can let a caller pick which one to accept.
413
427
  */
414
428
  function getResponseContentTypeInfo(node) {
415
- const contentTypes = getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [];
416
- const isMultipleContentTypes = contentTypes.length > 1;
417
- return {
418
- contentTypes,
419
- isMultipleContentTypes,
420
- contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
421
- defaultContentType: contentTypes[0] ?? "application/json",
422
- hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
423
- };
429
+ return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
424
430
  }
425
431
  /**
426
432
  * Reads the single base content type of an operation's primary success response, lowercased and
@@ -602,7 +608,7 @@ function resolveResponseValidator(validator) {
602
608
  * `<operation>ResponseSchema` is used; error bodies are never zod-parsed.
603
609
  */
604
610
  function buildZodResponseParse(node, zodResolver) {
605
- const name = zodResolver.resolveResponseName?.(node);
611
+ const name = zodResolver.response.response(node);
606
612
  return name ? {
607
613
  expression: name,
608
614
  importNames: [name]
@@ -615,7 +621,7 @@ function buildZodResponseParse(node, zodResolver) {
615
621
  */
616
622
  function buildZodErrorParse(node, zodResolver) {
617
623
  if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
618
- const name = zodResolver.resolveErrorName?.(node);
624
+ const name = zodResolver.response.error?.(node);
619
625
  return name ? {
620
626
  expression: name,
621
627
  importNames: [name]
@@ -732,7 +738,7 @@ function buildParamsRemap({ node }) {
732
738
  * `buildRequestResultGenerics({ node, tsResolver }) // 'AddPetResponses, ThrowOnError'`
733
739
  */
734
740
  function buildRequestResultGenerics({ node, tsResolver }) {
735
- return `${tsResolver.resolveResponsesName(node)}, ThrowOnError`;
741
+ return `${tsResolver.response.responses(node)}, ThrowOnError`;
736
742
  }
737
743
  //#endregion
738
744
  //#region ../../internals/client/src/builders/returnStatement.ts
@@ -755,30 +761,30 @@ function buildReturnStatement({ node, tsResolver, callConfig }) {
755
761
  const declarationPrinter = functionPrinter({ mode: "declaration" });
756
762
  /**
757
763
  * Builds the grouped-options signature for one operation: a single `options` object whose `TData`
758
- * is the plugin-ts `<Name>RequestConfig` (carrying a literal `url`), and a `RequestResult` return type
764
+ * is the plugin-ts `<Name>Options` (carrying a literal `url`), and a `RequestResult` return type
759
765
  * keyed to the plugin-ts per-status responses record. There are no positional arguments.
760
766
  *
761
- * The generated file imports `<Name>RequestConfig` and `<Name>Responses` and uses them directly, so no
767
+ * The generated file imports `<Name>Options` and `<Name>Responses` and uses them directly, so no
762
768
  * per-operation input type has to be emitted.
763
769
  */
764
770
  function buildGroupedOptionsSignature({ node, tsResolver }) {
765
- const requestConfigName = tsResolver.resolveRequestConfigName(node);
766
- const responsesName = tsResolver.resolveResponsesName(node);
771
+ const optionsName = tsResolver.response.options(node);
772
+ const responsesName = tsResolver.response.responses(node);
767
773
  const resultGenerics = buildRequestResultGenerics({
768
774
  node,
769
775
  tsResolver
770
776
  });
771
777
  const { isOptional } = getRequestGroupOptionality(node);
772
778
  return {
773
- dataTypeName: requestConfigName,
779
+ dataTypeName: optionsName,
774
780
  paramsSignature: declarationPrinter.print(createFunctionParameters({ params: [createFunctionParameter({
775
781
  name: "options",
776
- type: `Options<${requestConfigName}, ThrowOnError>`,
782
+ type: `Options<${optionsName}, ThrowOnError>`,
777
783
  ...isOptional ? { default: "{}" } : {}
778
784
  })] })) ?? "",
779
785
  returnType: `Promise<RequestResult<${resultGenerics}>>`,
780
786
  generics: ["ThrowOnError extends boolean = true"],
781
- importedTypeNames: [requestConfigName, responsesName]
787
+ importedTypeNames: [optionsName, responsesName]
782
788
  };
783
789
  }
784
790
  //#endregion
@@ -844,7 +850,7 @@ function buildStyles({ node }) {
844
850
  function buildValidatorHooks({ node, validator, zodResolver }) {
845
851
  const importedZodNames = [];
846
852
  const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
847
- const zodRequestName = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.resolveDataName?.(node) : null;
853
+ const zodRequestName = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) : null;
848
854
  const request = zodRequestName ?? null;
849
855
  if (zodRequestName) importedZodNames.push(zodRequestName);
850
856
  const responseParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodResponseParse(node, zodResolver) : null;
@@ -904,7 +910,7 @@ function Operation({ name, node, tsResolver, zodResolver, validator, security, i
904
910
  "...config",
905
911
  ...buildParamsRemap({ node })
906
912
  ].filter(Boolean).join(", ")} }`;
907
- const eventType = `SuccessOf<${tsResolver.resolveResponsesName(node)}>`;
913
+ const eventType = `SuccessOf<${tsResolver.response.responses(node)}>`;
908
914
  const returnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
909
915
  const returnStatement = eventStream ? `return toEventStream<${eventType}>(request(${callConfig}))` : buildReturnStatement({
910
916
  node,
@@ -982,7 +988,7 @@ function buildSdkMethod({ node, name, tsResolver, zodResolver, validator, securi
982
988
  })
983
989
  });
984
990
  const generics = signature.generics.length ? `<${signature.generics.join(", ")}>` : "";
985
- const jsdoc = ast.buildJSDoc(buildOperationComments(node, {
991
+ const jsdoc = buildJSDoc(buildOperationComments(node, {
986
992
  link: "urlPath",
987
993
  linkPosition: "beforeDeprecated",
988
994
  splitLines: true
@@ -1052,15 +1058,15 @@ function SdkFacade({ name, isExportable = true, isIndexable = true, members, chi
1052
1058
  //#endregion
1053
1059
  //#region ../../internals/client/src/generators/sdkGenerator.tsx
1054
1060
  function resolveTypeImportNames(node, tsResolver) {
1055
- return [tsResolver.resolveRequestConfigName(node), tsResolver.resolveResponsesName(node)];
1061
+ return [tsResolver.response.options(node), tsResolver.response.responses(node)];
1056
1062
  }
1057
1063
  function resolveZodImportNames(node, zodResolver, validator) {
1058
1064
  const { query: queryParams } = getOperationParameters(node, { paramsCasing: "original" });
1059
1065
  return [
1060
- resolveResponseValidator(validator) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1066
+ resolveResponseValidator(validator) === "zod" ? zodResolver.response.response(node) : null,
1061
1067
  resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
1062
- resolveRequestValidator(validator) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
1063
- resolveQueryParamsValidator(validator) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
1068
+ resolveRequestValidator(validator) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.response.body(node) : null,
1069
+ resolveQueryParamsValidator(validator) === "zod" && queryParams.length > 0 ? zodResolver.param.query(node, queryParams[0]) : null
1064
1070
  ].filter((n) => Boolean(n));
1065
1071
  }
1066
1072
  /**
@@ -1077,12 +1083,14 @@ function buildControllers(nodes, ctx) {
1077
1083
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1078
1084
  const document = ctx.adapter.document;
1079
1085
  function buildOperationData(node) {
1080
- const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
1086
+ const typeFile = tsResolver.file({
1087
+ ...operationFileEntry(node, node.operationId),
1081
1088
  root,
1082
1089
  output: tsPluginOptions?.output ?? output,
1083
1090
  group: tsPluginOptions?.group
1084
1091
  });
1085
- const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1092
+ const zodFile = zodResolver && pluginZod?.options ? zodResolver.file({
1093
+ ...operationFileEntry(node, node.operationId),
1086
1094
  root,
1087
1095
  output: pluginZod.options?.output ?? output,
1088
1096
  group: pluginZod.options?.group ?? void 0
@@ -1094,7 +1102,7 @@ function buildControllers(nodes, ctx) {
1094
1102
  }) : void 0;
1095
1103
  return {
1096
1104
  node,
1097
- name: resolver.resolveName(node.operationId),
1105
+ name: resolver.name(node.operationId),
1098
1106
  tsResolver,
1099
1107
  zodResolver,
1100
1108
  typeFile,
@@ -1105,12 +1113,11 @@ function buildControllers(nodes, ctx) {
1105
1113
  return nodes.reduce((acc, operationNode) => {
1106
1114
  if (!ast.isHttpOperationNode(operationNode)) return acc;
1107
1115
  const tag = operationNode.tags[0];
1108
- const name = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("ApiClient");
1109
- const file = resolver.resolveFile({
1116
+ const name = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.groupName(tag) : resolver.className("ApiClient");
1117
+ const file = resolver.file({
1110
1118
  name,
1111
1119
  extname: ".ts",
1112
- tag
1113
- }, {
1120
+ tag,
1114
1121
  root,
1115
1122
  output,
1116
1123
  group: group ?? void 0
@@ -1164,7 +1171,7 @@ function createSdkGenerator() {
1164
1171
  if (!ctx.driver.getPlugin(pluginTsName) || !sdk) return null;
1165
1172
  const controllers = buildControllers(nodes, ctx);
1166
1173
  const clientPath = path.resolve(root, ".kubb/client.ts");
1167
- const banner = (file) => resolver.resolveBanner(ctx.meta, {
1174
+ const banner = (file) => resolver.default.banner(ctx.meta, {
1168
1175
  output,
1169
1176
  config,
1170
1177
  file: {
@@ -1172,7 +1179,7 @@ function createSdkGenerator() {
1172
1179
  baseName: file.baseName
1173
1180
  }
1174
1181
  });
1175
- const footer = (file) => resolver.resolveFooter(ctx.meta, {
1182
+ const footer = (file) => resolver.default.footer(ctx.meta, {
1176
1183
  output,
1177
1184
  config,
1178
1185
  file: {
@@ -1239,28 +1246,26 @@ function createSdkGenerator() {
1239
1246
  ]
1240
1247
  }, file.path);
1241
1248
  };
1242
- if (sdk.mode === "flat") return renderClassFile(resolver.resolveClassName(sdk.name ?? "sdk"), resolver.resolveFile({
1249
+ if (sdk.mode === "flat") return renderClassFile(resolver.className(sdk.name ?? "sdk"), resolver.file({
1243
1250
  name: sdk.name ?? "sdk",
1244
- extname: ".ts"
1245
- }, {
1251
+ extname: ".ts",
1246
1252
  root,
1247
1253
  output,
1248
1254
  group: group ?? void 0
1249
1255
  }), controllers.flatMap((controller) => controller.operations));
1250
1256
  const classFiles = controllers.map(({ name, file, operations: ops }) => renderClassFile(name, file, ops));
1251
1257
  if (!sdk.name) return /* @__PURE__ */ jsx(Fragment, { children: classFiles });
1252
- const sdkFile = resolver.resolveFile({
1258
+ const sdkFile = resolver.file({
1253
1259
  name: sdk.name,
1254
- extname: ".ts"
1255
- }, {
1260
+ extname: ".ts",
1256
1261
  root,
1257
1262
  output,
1258
1263
  group: group ?? void 0
1259
1264
  });
1260
- const facadeName = resolver.resolveClassName(sdk.name);
1265
+ const facadeName = resolver.className(sdk.name);
1261
1266
  const members = controllers.map(({ name, tag }) => ({
1262
1267
  className: name,
1263
- propName: resolver.resolveClientPropertyName(tag ?? name)
1268
+ propName: resolver.propertyName(tag ?? name)
1264
1269
  }));
1265
1270
  return /* @__PURE__ */ jsxs(Fragment, { children: [classFiles, /* @__PURE__ */ jsxs(File, {
1266
1271
  baseName: sdkFile.baseName,
@@ -1300,38 +1305,27 @@ const defaultMacros = [ast.macroSimplifyUnion];
1300
1305
  //#endregion
1301
1306
  //#region ../../internals/client/src/resolver.ts
1302
1307
  /**
1303
- * Default resolver shared by the client plugins. Functions and files use camelCase; classes and
1304
- * tag groups use PascalCase.
1308
+ * Default resolver shared by the client plugins. Functions and files inherit the built-in camelCase
1309
+ * `name` and `file`; classes and tag groups use PascalCase.
1305
1310
  *
1306
1311
  * @example
1307
1312
  * ```ts
1308
- * resolverClient.resolveName('show pet by id') // 'showPetById'
1309
- * resolverClient.resolveGroupName('pet') // 'PetClient'
1313
+ * resolverClient.name('show pet by id') // 'showPetById'
1314
+ * resolverClient.groupName('pet') // 'PetClient'
1310
1315
  * ```
1311
1316
  */
1312
- const resolverClient = defineResolver(() => ({
1313
- name: "default",
1317
+ const resolverClient = createResolver({
1314
1318
  pluginName: "plugin-contract-client",
1315
- default(name, type) {
1316
- if (type === "file") return toFilePath(name);
1317
- return ensureValidVarName(camelCase(name));
1318
- },
1319
- resolveName(name) {
1320
- return this.default(name, "function");
1321
- },
1322
- resolvePathName(name, type) {
1323
- return this.default(name, type);
1324
- },
1325
- resolveClassName(name) {
1319
+ className(name) {
1326
1320
  return ensureValidVarName(pascalCase(name));
1327
1321
  },
1328
- resolveGroupName(name) {
1322
+ groupName(name) {
1329
1323
  return ensureValidVarName(pascalCase(`${name} Client`));
1330
1324
  },
1331
- resolveClientPropertyName(name) {
1325
+ propertyName(name) {
1332
1326
  return ensureValidVarName(camelCase(name));
1333
1327
  }
1334
- }));
1328
+ });
1335
1329
  //#endregion
1336
1330
  //#region src/generators/clientGenerator.tsx
1337
1331
  /**
@@ -1352,25 +1346,28 @@ const clientGenerator = defineGenerator({
1352
1346
  const pluginZod = resolveResponseValidator(validator) === "zod" || resolveRequestValidator(validator) === "zod" ? driver.getPlugin(pluginZodName) : null;
1353
1347
  const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1354
1348
  const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
1355
- const importedTypeNames = [tsResolver.resolveRequestConfigName(node), tsResolver.resolveResponsesName(node)];
1349
+ const importedTypeNames = [tsResolver.response.options(node), tsResolver.response.responses(node)];
1356
1350
  const importedZodNames = zodResolver ? [
1357
- resolveResponseValidator(validator) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1351
+ resolveResponseValidator(validator) === "zod" ? zodResolver.response.response?.(node) : null,
1358
1352
  resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
1359
- resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.resolveDataName?.(node) : null
1353
+ resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body?.(node) : null
1360
1354
  ].filter((name) => Boolean(name)) : [];
1361
1355
  const meta = {
1362
- name: resolver.resolveName(node.operationId),
1363
- file: resolver.resolveFile(operationFileEntry(node, node.operationId), {
1356
+ name: resolver.name(node.operationId),
1357
+ file: resolver.file({
1358
+ ...operationFileEntry(node, node.operationId),
1364
1359
  root,
1365
1360
  output,
1366
1361
  group: group ?? void 0
1367
1362
  }),
1368
- fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
1363
+ fileTs: tsResolver.file({
1364
+ ...operationFileEntry(node, node.operationId),
1369
1365
  root,
1370
1366
  output: pluginTs.options?.output ?? output,
1371
1367
  group: pluginTs.options?.group ?? void 0
1372
1368
  }),
1373
- fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
1369
+ fileZod: zodResolver && pluginZod?.options ? zodResolver.file({
1370
+ ...operationFileEntry(node, node.operationId),
1374
1371
  root,
1375
1372
  output: pluginZod.options.output ?? output,
1376
1373
  group: pluginZod.options?.group ?? void 0
@@ -1387,7 +1384,7 @@ const clientGenerator = defineGenerator({
1387
1384
  baseName: meta.file.baseName,
1388
1385
  path: meta.file.path,
1389
1386
  meta: meta.file.meta,
1390
- banner: resolver.resolveBanner(ctx.meta, {
1387
+ banner: resolver.default.banner(ctx.meta, {
1391
1388
  output,
1392
1389
  config,
1393
1390
  file: {
@@ -1395,7 +1392,7 @@ const clientGenerator = defineGenerator({
1395
1392
  baseName: meta.file.baseName
1396
1393
  }
1397
1394
  }),
1398
- footer: resolver.resolveFooter(ctx.meta, {
1395
+ footer: resolver.default.footer(ctx.meta, {
1399
1396
  output,
1400
1397
  config,
1401
1398
  file: {
@@ -1499,10 +1496,7 @@ const pluginFetch = definePlugin((options) => {
1499
1496
  mode: sdk.mode ?? "tag",
1500
1497
  name: sdk.name
1501
1498
  } : void 0,
1502
- resolver: userResolver ? {
1503
- ...resolverClient,
1504
- ...userResolver
1505
- } : resolverClient
1499
+ resolver: userResolver ? Resolver.merge(resolverClient, userResolver) : resolverClient
1506
1500
  };
1507
1501
  const selectedGenerators = resolved.sdk ? [createSdkGenerator()] : [clientGenerator];
1508
1502
  return {