@kubb/plugin-msw 6.0.1 → 6.0.3

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
@@ -2,13 +2,12 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- //#endregion
6
5
  let kubb_kit = require("kubb/kit");
7
6
  let _kubb_plugin_faker = require("@kubb/plugin-faker");
8
7
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
9
8
  let kubb_jsx = require("kubb/jsx");
10
9
  let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
11
- //#region ../../internals/shared/src/operation.ts
10
+ // ../../internals/shared/src/operation.ts
12
11
  /**
13
12
  * Builds the `ResolverFileParams` every operation generator passes to
14
13
  * `resolver.file`: a file named `name`, tagged by the operation's first
@@ -78,46 +77,26 @@ function resolveResponseTypes(node, resolver) {
78
77
  }
79
78
  return types;
80
79
  }
81
- //#endregion
82
- //#region ../../internals/utils/src/casing.ts
80
+ // ../../internals/shared/src/resolver.ts
83
81
  /**
84
- * Shared implementation for camelCase and PascalCase conversion.
85
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
86
- * and capitalizes each word according to `pascal`.
82
+ * Builds the allocation key for a declaration an operation owns. `scope` names which declaration
83
+ * it is, so two of them never share an allocation even when a custom resolver gives both the same
84
+ * name. Leave `scope` empty for the operation's own declaration.
87
85
  *
88
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
89
- */
90
- function convert(text, pascal) {
91
- return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
92
- if (word.length > 1 && word === word.toUpperCase()) return word;
93
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
94
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
95
- }
96
- const cache = /* @__PURE__ */ new Map();
97
- const cacheLimit = 1e4;
98
- function toCamelOrPascal(text, pascal) {
99
- const key = `${pascal ? "P" : "c"}${text}`;
100
- const cached = cache.get(key);
101
- if (cached !== void 0) return cached;
102
- const result = convert(text, pascal);
103
- if (cache.size >= cacheLimit) cache.clear();
104
- cache.set(key, result);
105
- return result;
106
- }
107
- /**
108
- * Converts `text` to camelCase.
109
- *
110
- * @example Word boundaries
111
- * `camelCase('hello-world') // 'helloWorld'`
112
- *
113
- * @example With a prefix
114
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
86
+ * @example
87
+ * ```ts
88
+ * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
89
+ * ```
115
90
  */
116
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
117
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
91
+ function operationKey(node, ...scope) {
92
+ return [
93
+ "operation",
94
+ node.method,
95
+ node.path,
96
+ ...scope.map(String)
97
+ ];
118
98
  }
119
- //#endregion
120
- //#region ../../internals/shared/src/group.ts
99
+ // ../../internals/shared/src/group.ts
121
100
  /**
122
101
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
123
102
  * shared default naming so every plugin groups output consistently:
@@ -140,24 +119,21 @@ function createGroupConfig(group) {
140
119
  if (!group) return null;
141
120
  const defaultName = (ctx) => {
142
121
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
143
- return camelCase(ctx.group);
122
+ return (0, kubb_kit.camelCase)(ctx.group);
144
123
  };
145
124
  return {
146
125
  ...group,
147
126
  name: group.name ? group.name : defaultName
148
127
  };
149
128
  }
150
- //#endregion
151
- //#region kubb-extension.json
129
+ // kubb-extension.json
152
130
  var compatibility = {
153
- "kubb": ">=5.8.3",
131
+ "kubb": ">=5.14.6",
154
132
  "node": ">=22.22"
155
133
  };
156
- //#endregion
157
- //#region package.json
158
- var version = "6.0.1";
159
- //#endregion
160
- //#region src/generators/handlersGenerator.ts
134
+ // package.json
135
+ var version = "6.0.3";
136
+ // src/generators/handlersGenerator.ts
161
137
  /**
162
138
  * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
163
139
  * `handlers.ts` file that re-exports every generated handler in operation
@@ -170,7 +146,10 @@ const handlersGenerator = (0, kubb_kit.defineGenerator)({
170
146
  const { resolver, config, root } = ctx;
171
147
  const { output, group } = ctx.options;
172
148
  const handlersName = resolver.handler.listName();
173
- const createHandlersName = camelCase(handlersName, { prefix: "create" });
149
+ const createHandlersName = resolver.resolveName({
150
+ key: ["handlers", "create"],
151
+ name: (0, kubb_kit.camelCase)(handlersName, { prefix: "create" })
152
+ });
174
153
  const file = resolver.file({
175
154
  name: handlersName,
176
155
  extname: ".ts",
@@ -234,8 +213,7 @@ const handlersGenerator = (0, kubb_kit.defineGenerator)({
234
213
  })];
235
214
  }
236
215
  });
237
- //#endregion
238
- //#region src/utils.ts
216
+ // src/utils.ts
239
217
  /**
240
218
  * Returns the base URL as source code. A `${...}` value stays a template literal.
241
219
  */
@@ -291,8 +269,7 @@ function responseLiteral(body, status, contentType) {
291
269
  status: ${status},${headersBlock(contentType)}
292
270
  })`;
293
271
  }
294
- //#endregion
295
- //#region src/components/Mock.tsx
272
+ // src/components/Mock.tsx
296
273
  function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
297
274
  const method = getMswMethod(node);
298
275
  const methodGuard = node.method === "QUERY" ? `if (info.request.method !== 'QUERY') return\n\n ` : "";
@@ -301,8 +278,10 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
301
278
  const contentType = getContentType(successResponse);
302
279
  const url = kubb_kit.Url.toPath(node.path);
303
280
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
304
- const params = `data?: ${fakerName ? typeName : dataType} | ${requestTypeName ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}, options?: { baseURL?: string }`;
305
- const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`;
281
+ const paramType = fakerName ? typeName : dataType;
282
+ const requestBodyType = requestTypeName ? `${requestTypeName} extends DefaultBodyType ? ${requestTypeName} : DefaultBodyType` : null;
283
+ const params = `data?: ${paramType} | ${requestBodyType ? `HttpResponseResolver<Record<string, string>, ${requestBodyType}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}, options?: { baseURL?: string }`;
284
+ const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`;
306
285
  const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
307
286
  const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : "JSON.stringify(data)";
308
287
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
@@ -321,8 +300,7 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
321
300
  })
322
301
  });
323
302
  }
324
- //#endregion
325
- //#region src/components/Response.tsx
303
+ // src/components/Response.tsx
326
304
  function Response({ name, typeName, response }) {
327
305
  const statusCode = Number(response.statusCode);
328
306
  const contentType = getContentType(response);
@@ -341,8 +319,7 @@ function Response({ name, typeName, response }) {
341
319
  })
342
320
  });
343
321
  }
344
- //#endregion
345
- //#region src/generators/mswGenerator.tsx
322
+ // src/generators/mswGenerator.tsx
346
323
  /**
347
324
  * Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler
348
325
  * per OpenAPI operation. With `parser: 'faker'` the handler returns a value
@@ -446,6 +423,11 @@ const mswGenerator = (0, kubb_kit.defineGenerator)({
446
423
  isTypeOnly: true,
447
424
  path: "msw/http"
448
425
  }),
426
+ requestName && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
427
+ name: ["DefaultBodyType"],
428
+ isTypeOnly: true,
429
+ path: "msw"
430
+ }),
449
431
  /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
450
432
  name: Array.from(/* @__PURE__ */ new Set([
451
433
  ...hasResponseNameCollision ? [] : [type.responseName],
@@ -482,8 +464,7 @@ const mswGenerator = (0, kubb_kit.defineGenerator)({
482
464
  });
483
465
  }
484
466
  });
485
- //#endregion
486
- //#region src/resolvers/resolverMsw.ts
467
+ // src/resolvers/resolverMsw.ts
487
468
  /**
488
469
  * Default resolver used by `@kubb/plugin-msw`. Decides the names and file
489
470
  * paths for every generated MSW handler. Function names get a `Handler`
@@ -502,19 +483,25 @@ const mswGenerator = (0, kubb_kit.defineGenerator)({
502
483
  const resolverMsw = (0, kubb_kit.createResolver)({
503
484
  pluginName: "plugin-msw",
504
485
  name(name) {
505
- return camelCase(name, { suffix: "handler" });
486
+ return this.memo(`name:${name}`, () => (0, kubb_kit.camelCase)(name, { suffix: "handler" }));
506
487
  },
507
488
  handler: {
508
489
  name(node) {
509
- return this.name(node.operationId);
490
+ const name = this.name(node.operationId);
491
+ return this.resolveName({
492
+ key: operationKey(node, "handler", "name"),
493
+ name
494
+ });
510
495
  },
511
496
  listName() {
512
- return "handlers";
497
+ return this.resolveName({
498
+ key: ["handlers", "list"],
499
+ name: "handlers"
500
+ });
513
501
  }
514
502
  }
515
503
  });
516
- //#endregion
517
- //#region src/plugin.ts
504
+ // src/plugin.ts
518
505
  /**
519
506
  * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and
520
507
  * cross-plugin dependency references.
@@ -579,7 +566,6 @@ const pluginMsw = (0, kubb_kit.defineExtension)({
579
566
  };
580
567
  }
581
568
  });
582
- //#endregion
583
569
  exports.default = pluginMsw;
584
570
  exports.pluginMsw = pluginMsw;
585
571
  exports.pluginMswName = pluginMswName;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["defineGenerator","ast","Url","File","Function","File","Function","defineGenerator","jsxRenderer","ast","pluginFakerName","pluginTsName","enumTypeName","File","createResolver","defineExtension","pluginTsName","pluginFakerName","Resolver"],"sources":["../../../internals/shared/src/operation.ts","../../../internals/utils/src/casing.ts","../../../internals/shared/src/group.ts","../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../src/utils.ts","../src/components/Mock.tsx","../src/components/Response.tsx","../src/generators/mswGenerator.tsx","../src/resolvers/resolverMsw.ts","../src/plugin.ts"],"sourcesContent":["import { ast, type Group, type NodeCache, type Output, type Resolver, type ResolverFileParams, Url } from 'kubb/kit'\n\n/**\n * Builds the `ResolverFileParams` every operation generator passes to\n * `resolver.file`: a file named `name`, tagged by the operation's first\n * tag (or `'default'`), at the operation's path. Centralizes the entry object\n * that was repeated at dozens of call sites across the client and query plugins.\n *\n * @example\n * ```ts\n * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })\n * ```\n */\nexport function operationFileEntry(node: ast.OperationNode, name: string, extname: ResolverFileParams['extname'] = '.ts'): ResolverFileParams {\n return {\n name,\n extname,\n tag: node.tags[0] ?? 'default',\n path: node.path,\n }\n}\n\n/**\n * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the\n * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the\n * same dependency for the same operation in one pass (a query plugin's several hook generators, the\n * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.\n *\n * @example Cache `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveDependencyOperationFile(options: {\n cache: NodeCache\n node: ast.OperationNode\n resolver: Pick<Resolver, 'file' | 'pluginName'>\n root: string\n output: Output\n group?: Group | null\n}): ast.FileNode {\n const { cache, node, resolver, root, output, group } = options\n\n return cache.ensureItem(`${resolver.pluginName}:operationFile`, () =>\n resolver.file({ ...operationFileEntry(node, node.operationId), root, output, group: group ?? undefined }),\n )\n}\n\nexport type ContentTypeInfo = {\n contentTypes: string[]\n isMultipleContentTypes: boolean\n contentTypeUnion: string\n defaultContentType: string\n hasFormData: boolean\n}\n\nexport type RequestConfigResolver = {\n response: {\n body(node: ast.OperationNode): string\n }\n}\n\nexport type ResponseStatusNameResolver = {\n response: {\n status(node: ast.OperationNode, statusCode: ast.StatusCode): string\n }\n}\n\nexport type ResponseNameResolver = ResponseStatusNameResolver & {\n response: {\n response(node: ast.OperationNode): string\n }\n}\n\nexport type OperationTypeNameResolver = RequestConfigResolver &\n ResponseNameResolver & {\n param: {\n path(node: ast.OperationNode, param: ast.ParameterNode): string\n query(node: ast.OperationNode, param: ast.ParameterNode): string\n headers(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type OperationCommentLink = 'pathTemplate' | 'urlPath' | false | ((node: ast.OperationNode) => string | undefined)\n\nexport type BuildOperationCommentsOptions = {\n link?: OperationCommentLink\n linkPosition?: 'beforeDeprecated' | 'afterDeprecated'\n splitLines?: boolean\n}\n\ntype ResponseLike = {\n statusCode: ast.StatusCode | number | string\n}\n\nexport type OperationParameterGroups = Record<ast.ParameterNode['in'], Array<ast.ParameterNode>>\n\nexport type ResolveOperationTypeNameOptions = {\n responseStatusNames?: boolean | 'error'\n exclude?: ReadonlyArray<string | undefined>\n order?: 'params-first' | 'body-response-first'\n /**\n * Include the individual `Path`/`Query`/`Headers` group type names. Set to `false` for clients\n * that reference the grouped `Options` type instead of the per-group types.\n */\n includeParams?: boolean\n}\n\nfunction getOperationLink(node: ast.OperationNode, link: OperationCommentLink): string | null {\n if (!link) {\n return null\n }\n\n if (typeof link === 'function') {\n return link(node) ?? null\n }\n\n return node.path ? `{@link ${Url.toPath(node.path)}}` : null\n}\n\n/**\n * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several\n * are present and the union, default, and form-data flags the client uses to pick one.\n */\nfunction buildContentTypeInfo(contentTypes: string[]): ContentTypeInfo {\n const isMultipleContentTypes = contentTypes.length > 1\n\n return {\n contentTypes,\n isMultipleContentTypes,\n contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(' | ') : '',\n defaultContentType: contentTypes[0] ?? 'application/json',\n hasFormData: contentTypes.some((ct) => ct === 'multipart/form-data'),\n }\n}\n\nexport function getContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? [])\n}\n\n/**\n * The request-body counterpart for the primary success response: the content types it documents and\n * whether several are present, so the client can let a caller pick which one to accept.\n */\nexport function getResponseContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\nexport type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n\n/**\n * Reads the single base content type of an operation's primary success response, lowercased and\n * stripped of any `; charset=...` suffix. Returns `undefined` when the response declares zero or\n * more than one content type, since neither case has a single type to act on.\n */\nfunction getPrimarySuccessContentType(node: ast.OperationNode): string | undefined {\n const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? []\n if (contentTypes.length !== 1) return undefined\n return contentTypes[0]!.split(';')[0]!.trim().toLowerCase()\n}\n\n/**\n * Whether an operation streams its primary success response as Server-Sent Events\n * (`text/event-stream`). The client generator uses this to return a typed event stream instead of a\n * one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getPrimarySuccessContentType(node) === 'text/event-stream'\n}\n\n/**\n * Derives the default `responseType` for an operation from its primary success response.\n *\n * Returns a value only when that response declares a single non-JSON content type. `text/event-stream`\n * and other binary types (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`,\n * `video/*`) map to a stream or `'blob'`, and other `text/*` maps to `'text'`. Otherwise `undefined`,\n * leaving the runtime client's `Content-Type` auto-detection in charge.\n */\nexport function getResponseType(node: ast.OperationNode): ResponseType | undefined {\n const baseType = getPrimarySuccessContentType(node)\n if (!baseType) return undefined\n\n if (baseType === 'application/json' || baseType.endsWith('+json') || baseType === 'text/json') return undefined\n if (baseType === 'text/event-stream') return 'stream'\n if (baseType.startsWith('text/')) return 'text'\n if (baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)) return 'blob'\n return undefined\n}\n\n/**\n * Maps a content type to the PascalCase suffix used to name per-content-type variants\n * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).\n */\nfunction getContentTypeSuffix(contentType: string): string {\n const baseType = contentType.split(';')[0]!.trim()\n if (baseType === 'application/json') return 'Json'\n if (baseType === 'multipart/form-data') return 'FormData'\n if (baseType === 'application/x-www-form-urlencoded') return 'FormUrlEncoded'\n const subtype = baseType.split('/').pop() ?? baseType\n const parts = subtype.split(/[^a-zA-Z0-9]+/).filter(Boolean)\n if (parts.length === 0) return 'Unknown'\n return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('')\n}\n\n/**\n * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last\n * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).\n */\nexport function getPerContentTypeName(baseName: string, suffix: string): string {\n if (baseName.endsWith('Data')) {\n return suffix.endsWith('Data') ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`\n }\n return baseName + suffix\n}\n\nexport type ContentVariantInput = { contentType: string; schema?: ast.SchemaNode | null; keysToOmit?: Array<string> | null }\nexport type ContentVariant = { name: string; suffix: string; schema: ast.SchemaNode; keysToOmit?: Array<string> | null; contentType: string }\n\n/**\n * Resolves per-content-type variant names for a set of content entries, deduplicating suffix\n * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is\n * the final (possibly counter-augmented) value, so callers can derive parallel names in another\n * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).\n */\nexport function resolveContentTypeVariants(entries: Array<ContentVariantInput>, baseName: string): Array<ContentVariant> {\n const usedNames = new Set<string>()\n return entries\n .filter((entry) => entry.schema)\n .map((entry) => {\n const baseSuffix = getContentTypeSuffix(entry.contentType)\n let suffix = baseSuffix\n let name = getPerContentTypeName(baseName, suffix)\n let counter = 2\n while (usedNames.has(name)) {\n suffix = `${baseSuffix}${counter++}`\n name = getPerContentTypeName(baseName, suffix)\n }\n usedNames.add(name)\n return { name, suffix, schema: entry.schema!, keysToOmit: entry.keysToOmit, contentType: entry.contentType }\n })\n}\n\n// The request groups come from the grouped params, so `config` drops the data-shape keys to stay\n// assignable to `Options`, which omits them from `RequestConfig`.\nconst requestConfigBaseType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`\n\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n\n // Only the ambiguous side is offered: a single-type side has nothing to pick, so it stays baked in\n // the generated call.\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n return members.length ? `${requestConfigBaseType} & { contentType?: { ${members.join('; ')} } }` : requestConfigBaseType\n}\n\n/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...). Unlike {@link buildRequestConfigType}, it never adds a\n * `contentType?:` member: query hooks wrap GET operations, which carry no request body to select a\n * content type for.\n */\nexport function buildClientOptionType(): string {\n return requestConfigBaseType\n}\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n}\n\n/**\n * Which of the grouped request options an operation carries.\n */\nexport function getRequestGroups(node: ast.OperationNode): RequestGroups {\n const { path, query, header } = getOperationParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(node.requestBody?.content?.[0]?.schema),\n headers: header.length > 0,\n }\n}\n\nexport type RequestGroupOptionality = {\n groups: RequestGroups\n hasRequiredPath: boolean\n hasRequiredQuery: boolean\n hasRequiredHeader: boolean\n /**\n * Whether the grouped request parameter can default to `{}`. True only when no group carries a\n * required member, so every member is safe to omit.\n */\n isOptional: boolean\n}\n\n/**\n * Resolves which grouped request options an operation carries together with whether each group\n * holds a required member. The grouped parameter stays optional only when nothing inside it is\n * required, matching the generated `RequestConfig` type.\n */\nexport function getRequestGroupOptionality(node: ast.OperationNode): RequestGroupOptionality {\n const groups = getRequestGroups(node)\n const { path, query, header } = getOperationParameters(node)\n const hasRequiredPath = path.some((param) => param.required)\n const hasRequiredQuery = query.some((param) => param.required)\n const hasRequiredHeader = header.some((param) => param.required)\n\n return {\n groups,\n hasRequiredPath,\n hasRequiredQuery,\n hasRequiredHeader,\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body,\n }\n}\n\nexport type RequestOptionsNameResolver = RequestConfigResolver & {\n response: {\n options(node: ast.OperationNode): string\n }\n}\n\n/**\n * Builds the grouped `{ path, query, body, headers }` parameter for a generated client\n * function, typed from the operation's `Options` (minus `url`). Only the groups the\n * operation actually has are destructured. The trailing `config` parameter carries the\n * runtime `RequestConfig` overrides plus `client`.\n */\nexport function buildRequestParamsSignature(\n node: ast.OperationNode,\n resolver: RequestOptionsNameResolver,\n options: { isConfigurable?: boolean } = {},\n): { signature: string; groups: RequestGroups } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = (['path', 'query', 'body', 'headers'] as const).filter((key) => groups[key])\n\n const firstParam = names.length > 0 ? `{ ${names.join(', ')} }: ${resolver.response.options(node)}${isOptional ? ' = {}' : ''}` : null\n const configParam = isConfigurable ? `config: ${buildRequestConfigType(node)} = {}` : null\n\n return {\n signature: [firstParam, configParam].filter(Boolean).join(', '),\n groups,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { link = 'pathTemplate', linkPosition = 'afterDeprecated', splitLines = false } = options\n const linkComment = getOperationLink(node, link)\n const comments =\n linkPosition === 'beforeDeprecated'\n ? [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, linkComment, node.deprecated && '@deprecated']\n : [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, node.deprecated && '@deprecated', linkComment]\n\n const filteredComments = comments.filter((comment): comment is string => Boolean(comment))\n\n if (!splitLines) {\n return filteredComments\n }\n\n return filteredComments.flatMap((text) => text.split(/\\r?\\n/).map((line) => line.trim())).filter((comment): comment is string => Boolean(comment))\n}\n\nconst operationParameterGroupsByNode = new WeakMap<ast.OperationNode, OperationParameterGroups>()\n\n/**\n * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each\n * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance\n * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the\n * same parameters once per plugin.\n */\nexport function getOperationParameters(node: ast.OperationNode): OperationParameterGroups {\n const cached = operationParameterGroupsByNode.get(node)\n if (cached) return cached\n\n // A malformed spec can declare the same name twice within one location. Both would resolve to the\n // same output property, so emitting both would yield an object type with a duplicate member,\n // which TypeScript rejects. Keep the first.\n const group = (location: ast.ParameterNode['in']) => {\n const seen = new Set<string>()\n\n return node.parameters.filter((param) => {\n if (param.in !== location || seen.has(param.name)) return false\n seen.add(param.name)\n return true\n })\n }\n\n const groups: OperationParameterGroups = { path: group('path'), query: group('query'), header: group('header'), cookie: group('cookie') }\n\n operationParameterGroupsByNode.set(node, groups)\n return groups\n}\n\n/**\n * Builds the combined `{ body, path, query, headers }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same\n * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod\n * printer to emit `z.object(…)` rather than a record.\n */\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver): ast.SchemaNode {\n const { path, query, header } = getOperationParameters(node)\n const hasBody = Boolean(node.requestBody?.content?.[0]?.schema)\n const createNever = () => ast.factory.createSchema({ type: 'never', primitive: undefined, optional: true })\n const groups = [\n { name: 'path', params: path, resolve: resolver.param.path },\n { name: 'query', params: query, resolve: resolver.param.query },\n { name: 'headers', params: header, resolve: resolver.param.headers },\n ] as const\n\n // NOTE(v5-stable): the fields were renamed from the legacy beta shape\n // (`data`/`pathParams`/`queryParams`/`headerParams`) to `body`/`path`/`query`/`headers` so the\n // type matches the runtime client. Drop this note once v5 leaves beta.\n return ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n deprecated: node.deprecated,\n properties: [\n ast.factory.createProperty({\n name: 'body',\n required: hasBody,\n schema: hasBody ? ast.factory.createSchema({ type: 'ref', name: resolver.response.body(node) }) : createNever(),\n }),\n ...groups.map(({ name, params, resolve }) => {\n const required = params.some((param) => param.required)\n\n return ast.factory.createProperty({\n name,\n required,\n schema:\n params.length > 0\n ? ast.factory.createSchema({ type: 'ref', name: resolve.call(resolver.param, node, params[0]!), optional: !required })\n : createNever(),\n })\n }),\n ],\n })\n}\n\n/**\n * The schema a status occupies in the `<Name>Responses` record. A status that documents several\n * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type\n * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the\n * query hooks and `result.data` use.\n */\nfunction buildResponseRecordEntry(node: ast.OperationNode, res: ast.ResponseNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n const statusName = resolver.response.status(node, res.statusCode)\n const variants = (res.content ?? []).filter((entry) => entry.schema)\n if (variants.length <= 1) {\n return ast.factory.createSchema({ type: 'ref', name: statusName })\n }\n\n return ast.factory.createSchema({\n type: 'union',\n members: resolveContentTypeVariants(variants, statusName).map((variant) =>\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [\n ast.factory.createProperty({\n name: 'contentType',\n required: true,\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema: ast.factory.createSchema({ type: 'ref', name: variant.name }),\n }),\n ],\n }),\n ),\n })\n}\n\n/**\n * Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved\n * `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s\n * inferred responses schema, so both emit the same shape from the same inputs.\n *\n * Always emits the keyed record, even when an operation declares no responses. An operation with no\n * responses renders as an empty object, which keeps every consumer's import (for example the axios\n * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.\n */\nexport function buildResponses(node: ast.OperationNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n return ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: node.responses.map((res) =>\n ast.factory.createProperty({\n name: String(res.statusCode),\n required: true,\n schema: buildResponseRecordEntry(node, res, resolver),\n }),\n ),\n })\n}\n\nexport function getStatusCodeNumber(statusCode: ast.StatusCode | number | string): number | null {\n const code = Number(statusCode)\n\n return Number.isNaN(code) ? null : code\n}\n\nexport function isSuccessStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 200 && code < 300\n}\n\nexport function isErrorStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 400\n}\n\nexport function getSuccessResponses<TResponse extends ResponseLike>(responses: ReadonlyArray<TResponse>): Array<TResponse> {\n return responses.filter((response) => isSuccessStatusCode(response.statusCode))\n}\n\nexport function getOperationSuccessResponses(node: ast.OperationNode): Array<ast.ResponseNode> {\n return getSuccessResponses(node.responses)\n}\n\nexport function getPrimarySuccessResponse(node: ast.OperationNode): ast.ResponseNode | null {\n return getOperationSuccessResponses(node)[0] ?? null\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\nconst typeNamesByResolver = new WeakMap<OperationTypeNameResolver, Map<string, string[]>>()\n\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const cacheKey = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n let byResolver = typeNamesByResolver.get(resolver)\n if (byResolver) {\n const cached = byResolver.get(cacheKey)\n if (cached) return cached\n } else {\n byResolver = new Map()\n typeNamesByResolver.set(resolver, byResolver)\n }\n\n const { path, query, header } = getOperationParameters(node)\n const responseStatusNames =\n options.responseStatusNames === 'error'\n ? resolveErrorNames(node, resolver)\n : options.responseStatusNames === false\n ? []\n : resolveStatusCodeNames(node, resolver)\n const exclude = new Set(options.exclude ?? [])\n const paramNames =\n options.includeParams === false\n ? []\n : [\n ...path.map((param) => resolver.param.path(node, param)),\n ...query.map((param) => resolver.param.query(node, param)),\n ...header.map((param) => resolver.param.headers(node, param)),\n ]\n const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n const result = names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n byResolver.set(cacheKey, result)\n return result\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = getStatusCodeNumber(response.statusCode)\n if (code === null) {\n continue\n }\n\n types.push([code, isSuccessStatusCode(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction convert(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n// Plugins resolve the same names over and over (about 47,000 calls for a 1,000 schema spec), so\n// results are kept. Clearing at the limit bounds the memory a long-running process spends on it.\nconst cache = new Map<string, string>()\nconst cacheLimit = 10_000\n\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n const key = `${pascal ? 'P' : 'c'}${text}`\n const cached = cache.get(key)\n if (cached !== undefined) return cached\n\n const result = convert(text, pascal)\n if (cache.size >= cacheLimit) cache.clear()\n cache.set(key, result)\n return result\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n\n/**\n * Uppercases only the first character of `text`, leaving the rest untouched.\n * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.\n *\n * @example\n * `capitalize('getPetById') // 'GetPetById'`\n */\nexport function capitalize(text: string): string {\n return `${text.charAt(0).toUpperCase()}${text.slice(1)}`\n}\n\n/**\n * Converts `text` to snake_case.\n *\n * @example From camelCase\n * `snakeCase('helloWorld') // 'hello_world'`\n *\n * @example From mixed separators\n * `snakeCase('Hello-World') // 'hello_world'`\n */\nexport function snakeCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n const processed = `${prefix} ${text} ${suffix}`.trim()\n return processed\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/[\\s\\-.]+/g, '_')\n .replace(/[^a-zA-Z0-9_]/g, '')\n .toLowerCase()\n .split('_')\n .filter(Boolean)\n .join('_')\n}\n\n/**\n * Converts `text` to SCREAMING_SNAKE_CASE.\n *\n * @example From camelCase\n * `screamingSnakeCase('helloWorld') // 'HELLO_WORLD'`\n */\nexport function screamingSnakeCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return snakeCase(text, { prefix, suffix }).toUpperCase()\n}\n","import { camelCase } from '@internals/utils'\nimport type { Group } from 'kubb/kit'\n\n/**\n * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the\n * shared default naming so every plugin groups output consistently:\n *\n * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).\n * - other groups use the camelCased group (`pet store` → `petStore`).\n *\n * A user-provided `group.name` always wins over the default namer, so callers stay in\n * control of their output folders. Returns `null` when grouping is disabled, matching the\n * per-plugin convention.\n *\n * @param group - The user-supplied group option, or `undefined` to disable grouping.\n *\n * @example\n * ```ts\n * createGroupConfig(group) // shared across every plugin\n * ```\n */\nexport function createGroupConfig(group: Group | undefined): Group | null {\n if (!group) {\n return null\n }\n\n const defaultName = (ctx: { group: string }): string => {\n if (group.type === 'path') {\n return `${ctx.group.split('/')[1]}`\n }\n\n return camelCase(ctx.group)\n }\n\n return {\n ...group,\n name: group.name ? group.name : defaultName,\n } satisfies Group\n}\n","","","import { camelCase } from '@internals/utils'\nimport { ast, defineGenerator } from 'kubb/kit'\nimport type { PluginMsw } from '../types'\n\n/**\n * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a\n * `handlers.ts` file that re-exports every generated handler in operation\n * order, ready to spread into `setupServer(...handlers)` or\n * `setupWorker(...handlers)`.\n */\nexport const handlersGenerator = defineGenerator<PluginMsw>({\n name: 'plugin-msw',\n operations(nodes, ctx) {\n const { resolver, config, root } = ctx\n const { output, group } = ctx.options\n\n const handlersName = resolver.handler.listName()\n const createHandlersName = camelCase(handlersName, { prefix: 'create' })\n const file = resolver.file({ name: handlersName, extname: '.ts', root, output, group: group ?? undefined })\n\n const handlerNames = nodes.map((node) => resolver.handler.name(node))\n\n const imports = nodes.map((node, index) => {\n const operationName = handlerNames[index]!\n const operationFile = resolver.file({\n name: resolver.name(node.operationId),\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output,\n group: group ?? undefined,\n })\n return ast.factory.createImport({ name: [operationName], root: file.path, path: operationFile.path })\n })\n\n const handlers = handlerNames.map((name) => `${name}(undefined, options)`)\n\n return [\n ast.factory.createFile({\n baseName: file.baseName,\n path: file.path,\n meta: file.meta,\n banner: resolver.default.banner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n footer: resolver.default.footer(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n imports,\n sources: [\n ast.factory.createSource({\n name: createHandlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [\n ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {\n return [${handlers.join(', ')}] as const\n}`),\n ],\n }),\n ast.factory.createSource({\n name: handlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)],\n }),\n ],\n }),\n ]\n },\n})\n","import type { ast } from 'kubb/kit'\n\n/**\n * Returns the base URL as source code. A `${...}` value stays a template literal.\n */\nexport function resolveDefaultBaseURL(baseURL: string | null | undefined): string {\n if (!baseURL) {\n return \"''\"\n }\n if (baseURL.includes('${')) {\n return `\\`${baseURL.replaceAll('`', '\\\\`')}\\``\n }\n return JSON.stringify(baseURL)\n}\n\n/**\n * Gets the content type from a response, defaulting to 'application/json' if a schema exists.\n */\nexport function getContentType(response: ast.ResponseNode | null | undefined): string | null {\n if (!hasResponseSchema(response)) {\n return null\n }\n\n return getResponseContentType(response) ?? 'application/json'\n}\n\n/**\n * Determines if a response has a usable body schema.\n */\nexport function hasResponseSchema(response: ast.ResponseNode | null | undefined): boolean {\n const schema = response?.content?.find((entry) => entry.schema)?.schema\n return !!schema && schema.type !== 'void' && schema.type !== 'any' && schema.type !== 'unknown'\n}\n\n/**\n * Picks the content type used for the mocked response header. When a response declares multiple\n * content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.\n */\nfunction getResponseContentType(response: ast.ResponseNode | null | undefined): string | null {\n const contents = response?.content ?? []\n const jsonEntry = contents.find((entry) => {\n const baseType = entry.contentType?.split(';')[0]?.trim().toLowerCase()\n return baseType === 'application/json' || baseType?.endsWith('+json')\n })\n const value = (jsonEntry ?? contents[0])?.contentType\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no\n * `http.query`, so a QUERY operation maps to `http.all` and its handler checks the method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return node.method === 'QUERY' ? 'all' : node.method.toLowerCase()\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no content type.\n */\nfunction headersBlock(contentType: string | null): string {\n return contentType ? `\\n headers: {\\n 'Content-Type': '${contentType}'\\n },` : ''\n}\n\n/**\n * The `new HttpResponse(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new HttpResponse(${body}, {\n status: ${status},${headersBlock(contentType)}\n })`\n}\n","import { getPrimarySuccessResponse } from '@internals/shared'\nimport type { ast } from 'kubb/kit'\nimport { Url } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseLiteral } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so the QUERY handler lets the others pass through.\n const methodGuard = node.method === 'QUERY' ? `if (info.request.method !== 'QUERY') return\\n\\n ` : ''\n const successResponse = getPrimarySuccessResponse(node)\n const statusCode = successResponse ? Number(successResponse.statusCode) : 200\n const contentType = getContentType(successResponse)\n const url = Url.toPath(node.path)\n\n const responseHasSchema = hasResponseSchema(successResponse)\n const dataType = responseHasSchema ? typeName : 'string | number | boolean | null | object'\n const paramType = fakerName ? typeName : dataType\n\n const callbackType = requestTypeName\n ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}>`\n : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`\n\n const params = `data?: ${paramType} | ${callbackType}, options?: { baseURL?: string }`\n\n const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`\n\n // Escape colons that do not start a param, such as the second one in `/pet/:petId:search`.\n const urlLiteral = `\\`\\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, '$1\\\\\\\\:')}\\``\n const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : 'JSON.stringify(data)'\n\n return (\n <File.Source name={name} isIndexable isExportable>\n <Function name={name} export params={params}>\n {`return ${httpCall}(${urlLiteral}, function handler(info) {\n ${methodGuard}if(typeof data === 'function') return data(info)\n\n return ${responseLiteral(responseBody, statusCode, contentType)}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import type { ast } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, hasResponseSchema, responseLiteral } from '../utils.ts'\n\ntype Props = {\n typeName: string\n name: string\n response: ast.ResponseNode\n}\n\nexport function Response({ name, typeName, response }: Props): KubbReactNode {\n const statusCode = Number(response.statusCode)\n const contentType = getContentType(response)\n\n const params = `data${hasResponseSchema(response) ? '' : '?'}: ${typeName}`\n\n const responseName = `${name}Response${statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n return ${responseLiteral('JSON.stringify(data)', statusCode, contentType)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getOperationSuccessResponses, resolveDependencyOperationFile, resolveResponseTypes } from '@internals/shared'\nimport { ast, defineGenerator } from 'kubb/kit'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { enumTypeName, pluginTsName } from '@kubb/plugin-ts'\nimport { File, jsxRenderer } from 'kubb/jsx'\nimport { Mock } from '../components/Mock.tsx'\nimport { Response } from '../components/Response.tsx'\nimport type { PluginMsw } from '../types'\nimport { hasResponseSchema } from '../utils.ts'\n\n/**\n * Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler\n * per OpenAPI operation. With `parser: 'faker'` the handler returns a value\n * from `@kubb/plugin-faker`; with `parser: 'data'` it returns a typed empty\n * payload for tests to fill in.\n */\nexport const mswGenerator = defineGenerator<PluginMsw>({\n name: 'msw',\n renderer: jsxRenderer,\n operation(node, ctx) {\n if (!ast.isHttpOperationNode(node)) return null\n const { driver, resolver, config, root } = ctx\n const { output, parser, baseURL, group } = ctx.options\n\n const fileName = resolver.name(node.operationId)\n const mock = {\n name: resolver.handler.name(node),\n file: resolver.file({ name: fileName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path, root, output, group: group ?? undefined }),\n }\n\n const fakerPlugin = parser === 'faker' ? ctx.requirePlugin(pluginFakerName) : null\n const fakerResolver = fakerPlugin ? driver.getResolver(pluginFakerName) : null\n const faker =\n fakerPlugin && fakerResolver\n ? {\n name: fakerResolver.response.response(node),\n file: fakerResolver.file({\n name: node.operationId,\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output: fakerPlugin.options?.output ?? output,\n group: fakerPlugin.options?.group ?? undefined,\n }),\n }\n : null\n\n const pluginTs = ctx.requirePlugin(pluginTsName)\n const tsResolver = driver.getResolver(pluginTsName)\n\n const type = {\n file: resolveDependencyOperationFile({\n cache: ctx.cache,\n node,\n resolver: tsResolver,\n root,\n output: pluginTs.options?.output ?? output,\n group: pluginTs.options?.group,\n }),\n responseName: tsResolver.response.response(node),\n }\n\n const successResponses = getOperationSuccessResponses(node)\n const referencedNames = node.responses.flatMap((response) =>\n (response.content ?? []).flatMap((entry) => (entry.schema ? ast.collectImportedRefNames(entry.schema) : [])),\n )\n const enumOptions = pluginTs.options?.enum\n const enumNames = new Set(ctx.meta.enumNames)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n const types = resolveResponseTypes(node, tsResolver).map(([code, typeName]) => {\n const successResponse = successResponses.find((response) => response.statusCode === String(code))\n return [code, hasResponseNameCollision && successResponse ? tsResolver.response.status(node, successResponse.statusCode) : typeName] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName).filter((name) => !hasResponseNameCollision || name !== type.responseName)\n const mockResponseName =\n hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName\n const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response))\n\n const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.response.body(node) : null\n\n return (\n <File\n baseName={mock.file.baseName}\n path={mock.file.path}\n meta={mock.file.meta}\n banner={resolver.default.banner(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n footer={resolver.default.footer(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n >\n <File.Import name={['http', 'HttpResponse']} path=\"msw/http\" />\n <File.Import name={['HttpResponseResolver']} isTypeOnly path=\"msw/http\" />\n <File.Import\n name={Array.from(new Set([...(hasResponseNameCollision ? [] : [type.responseName]), ...responseTypeNames, ...(requestName ? [requestName] : [])]))}\n path={type.file.path}\n root={mock.file.path}\n isTypeOnly\n />\n {faker && hasSuccessSchema && <File.Import name={[faker.name]} root={mock.file.path} path={faker.file.path} />}\n\n {types\n .filter(([code]) => code !== 'default')\n .map(([code, typeName]) => {\n const response = node.responses.find((item) => item.statusCode === String(code))\n if (!response) return null\n return <Response key={typeName} typeName={typeName} response={response} name={mock.name} />\n })}\n\n <Mock\n name={mock.name}\n typeName={mockResponseName}\n requestTypeName={requestName}\n fakerName={faker && hasSuccessSchema ? faker.name : undefined}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { camelCase } from '@internals/utils'\nimport { createResolver } from 'kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return camelCase(name, { suffix: 'handler' })\n },\n handler: {\n name(node) {\n return this.name(node.operationId)\n },\n listName() {\n return 'handlers'\n },\n },\n})\n","import { createGroupConfig } from '@internals/shared'\nimport { addGenerator, defineExtension, Resolver, setMacros, setResolver } from 'kubb/kit'\nimport { compatibility } from '../kubb-extension.json'\nimport { version } from '../package.json'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { pluginTsName } from '@kubb/plugin-ts'\nimport { handlersGenerator } from './generators/handlersGenerator.ts'\nimport { mswGenerator } from './generators/mswGenerator.tsx'\nimport { resolverMsw } from './resolvers/resolverMsw.ts'\nimport type { PluginMsw, ResolverMsw } from './types.ts'\n\n/**\n * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and\n * cross-plugin dependency references.\n */\nexport const pluginMswName = 'plugin-msw' satisfies PluginMsw['name']\n\n/**\n * Generates MSW request handlers from an OpenAPI spec. Drop them into your\n * test setup or service worker to mock the API end-to-end. Request path,\n * method, status, and response body all stay in sync with the spec. Combine\n * with `@kubb/plugin-faker` (via `parser: 'faker'`) to seed handlers with\n * realistic data.\n *\n * @example\n * ```ts\n * import { defineConfig } from 'kubb/config'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginMsw } from '@kubb/plugin-msw'\n *\n * export default defineConfig({\n * input: './petStore.yaml',\n * output: { path: './src/gen' },\n * plugins: [\n * pluginTs(),\n * pluginMsw({\n * output: { path: './handlers' },\n * handlers: true,\n * }),\n * ],\n * })\n * ```\n */\nexport const pluginMsw = defineExtension<PluginMsw>({\n kind: 'plugin',\n meta: { name: pluginMswName, version, compatibility },\n dependencies: (options) => ({ [pluginTsName]: {}, ...(options.parser === 'faker' ? { [pluginFakerName]: {} } : {}) }),\n setup(options) {\n const {\n output = { path: 'handlers', barrel: { type: 'named' } },\n group,\n exclude = [],\n include,\n override = [],\n handlers = false,\n parser = 'data',\n baseURL,\n resolver: userResolver,\n macros: userMacros,\n } = options\n\n const resolver = userResolver ? Resolver.merge<ResolverMsw>(resolverMsw, userResolver) : resolverMsw\n\n setResolver(resolver)\n if (userMacros?.length) {\n setMacros(userMacros)\n }\n\n addGenerator(mswGenerator)\n if (handlers) {\n addGenerator(handlersGenerator)\n }\n\n return {\n output,\n parser,\n baseURL,\n group: createGroupConfig(group),\n exclude,\n include,\n override,\n handlers,\n }\n },\n})\n\nexport default pluginMsw\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["camelCase","defineGenerator","camelCase","ast","Url","File","Function","File","Function","defineGenerator","jsxRenderer","ast","pluginFakerName","pluginTsName","enumTypeName","File","createResolver","camelCase","defineExtension","pluginTsName","pluginFakerName","Resolver"],"sources":["../../../internals/shared/src/operation.ts","../../../internals/shared/src/resolver.ts","../../../internals/shared/src/group.ts","../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../src/utils.ts","../src/components/Mock.tsx","../src/components/Response.tsx","../src/generators/mswGenerator.tsx","../src/resolvers/resolverMsw.ts","../src/plugin.ts"],"sourcesContent":["import { ast, type Group, type NodeCache, type Output, type Resolver, type ResolverFileParams, Url } from 'kubb/kit'\n\n/**\n * Builds the `ResolverFileParams` every operation generator passes to\n * `resolver.file`: a file named `name`, tagged by the operation's first\n * tag (or `'default'`), at the operation's path. Centralizes the entry object\n * that was repeated at dozens of call sites across the client and query plugins.\n *\n * @example\n * ```ts\n * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })\n * ```\n */\nexport function operationFileEntry(node: ast.OperationNode, name: string, extname: ResolverFileParams['extname'] = '.ts'): ResolverFileParams {\n return {\n name,\n extname,\n tag: node.tags[0] ?? 'default',\n path: node.path,\n }\n}\n\n/**\n * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the\n * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the\n * same dependency for the same operation in one pass (a query plugin's several hook generators, the\n * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.\n *\n * @example Cache `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveDependencyOperationFile(options: {\n cache: NodeCache\n node: ast.OperationNode\n resolver: Pick<Resolver, 'file' | 'pluginName'>\n root: string\n output: Output\n group?: Group | null\n}): ast.FileNode {\n const { cache, node, resolver, root, output, group } = options\n\n return cache.ensureItem(`${resolver.pluginName}:operationFile`, () =>\n resolver.file({ ...operationFileEntry(node, node.operationId), root, output, group: group ?? undefined }),\n )\n}\n\nexport type ContentTypeInfo = {\n contentTypes: string[]\n isMultipleContentTypes: boolean\n contentTypeUnion: string\n defaultContentType: string\n hasFormData: boolean\n}\n\nexport type RequestConfigResolver = {\n response: {\n body(node: ast.OperationNode): string\n }\n}\n\nexport type ResponseStatusNameResolver = {\n response: {\n status(node: ast.OperationNode, statusCode: ast.StatusCode): string\n }\n}\n\nexport type ResponseNameResolver = ResponseStatusNameResolver & {\n response: {\n response(node: ast.OperationNode): string\n }\n}\n\nexport type OperationTypeNameResolver = RequestConfigResolver &\n ResponseNameResolver & {\n param: {\n path(node: ast.OperationNode, param: ast.ParameterNode): string\n query(node: ast.OperationNode, param: ast.ParameterNode): string\n headers(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type OperationCommentLink = 'pathTemplate' | 'urlPath' | false | ((node: ast.OperationNode) => string | undefined)\n\nexport type BuildOperationCommentsOptions = {\n link?: OperationCommentLink\n linkPosition?: 'beforeDeprecated' | 'afterDeprecated'\n splitLines?: boolean\n}\n\ntype ResponseLike = {\n statusCode: ast.StatusCode | number | string\n}\n\nexport type OperationParameterGroups = Record<ast.ParameterNode['in'], Array<ast.ParameterNode>>\n\nexport type ResolveOperationTypeNameOptions = {\n responseStatusNames?: boolean | 'error'\n exclude?: ReadonlyArray<string | undefined>\n order?: 'params-first' | 'body-response-first'\n /**\n * Include the individual `Path`/`Query`/`Headers` group type names. Set to `false` for clients\n * that reference the grouped `Options` type instead of the per-group types.\n */\n includeParams?: boolean\n}\n\nfunction getOperationLink(node: ast.OperationNode, link: OperationCommentLink): string | null {\n if (!link) {\n return null\n }\n\n if (typeof link === 'function') {\n return link(node) ?? null\n }\n\n return node.path ? `{@link ${Url.toPath(node.path)}}` : null\n}\n\n/**\n * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several\n * are present and the union, default, and form-data flags the client uses to pick one.\n */\nfunction buildContentTypeInfo(contentTypes: string[]): ContentTypeInfo {\n const isMultipleContentTypes = contentTypes.length > 1\n\n return {\n contentTypes,\n isMultipleContentTypes,\n contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(' | ') : '',\n defaultContentType: contentTypes[0] ?? 'application/json',\n hasFormData: contentTypes.some((ct) => ct === 'multipart/form-data'),\n }\n}\n\nexport function getContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? [])\n}\n\n/**\n * The request-body counterpart for the primary success response: the content types it documents and\n * whether several are present, so the client can let a caller pick which one to accept.\n */\nexport function getResponseContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\nexport type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n\n/**\n * Reads the single base content type of an operation's primary success response, lowercased and\n * stripped of any `; charset=...` suffix. Returns `undefined` when the response declares zero or\n * more than one content type, since neither case has a single type to act on.\n */\nfunction getPrimarySuccessContentType(node: ast.OperationNode): string | undefined {\n const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? []\n if (contentTypes.length !== 1) return undefined\n return contentTypes[0]!.split(';')[0]!.trim().toLowerCase()\n}\n\n/**\n * Whether an operation streams its primary success response as Server-Sent Events\n * (`text/event-stream`). The client generator uses this to return a typed event stream instead of a\n * one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getPrimarySuccessContentType(node) === 'text/event-stream'\n}\n\n/**\n * Derives the default `responseType` for an operation from its primary success response.\n *\n * Returns a value only when that response declares a single non-JSON content type. `text/event-stream`\n * and other binary types (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`,\n * `video/*`) map to a stream or `'blob'`, and other `text/*` maps to `'text'`. Otherwise `undefined`,\n * leaving the runtime client's `Content-Type` auto-detection in charge.\n */\nexport function getResponseType(node: ast.OperationNode): ResponseType | undefined {\n const baseType = getPrimarySuccessContentType(node)\n if (!baseType) return undefined\n\n if (baseType === 'application/json' || baseType.endsWith('+json') || baseType === 'text/json') return undefined\n if (baseType === 'text/event-stream') return 'stream'\n if (baseType.startsWith('text/')) return 'text'\n if (baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)) return 'blob'\n return undefined\n}\n\n/**\n * Maps a content type to the PascalCase suffix used to name per-content-type variants\n * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).\n */\nfunction getContentTypeSuffix(contentType: string): string {\n const baseType = contentType.split(';')[0]!.trim()\n if (baseType === 'application/json') return 'Json'\n if (baseType === 'multipart/form-data') return 'FormData'\n if (baseType === 'application/x-www-form-urlencoded') return 'FormUrlEncoded'\n const subtype = baseType.split('/').pop() ?? baseType\n const parts = subtype.split(/[^a-zA-Z0-9]+/).filter(Boolean)\n if (parts.length === 0) return 'Unknown'\n return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('')\n}\n\n/**\n * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last\n * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).\n */\nexport function getPerContentTypeName(baseName: string, suffix: string): string {\n if (baseName.endsWith('Data')) {\n return suffix.endsWith('Data') ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`\n }\n return baseName + suffix\n}\n\nexport type ContentVariantInput = { contentType: string; schema?: ast.SchemaNode | null; keysToOmit?: Array<string> | null }\nexport type ContentVariant = { name: string; suffix: string; schema: ast.SchemaNode; keysToOmit?: Array<string> | null; contentType: string }\n\n/**\n * Resolves per-content-type variant names for a set of content entries, deduplicating suffix\n * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is\n * the final (possibly counter-augmented) value, so callers can derive parallel names in another\n * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).\n */\nexport function resolveContentTypeVariants(entries: Array<ContentVariantInput>, baseName: string): Array<ContentVariant> {\n const usedNames = new Set<string>()\n return entries\n .filter((entry) => entry.schema)\n .map((entry) => {\n const baseSuffix = getContentTypeSuffix(entry.contentType)\n let suffix = baseSuffix\n let name = getPerContentTypeName(baseName, suffix)\n let counter = 2\n while (usedNames.has(name)) {\n suffix = `${baseSuffix}${counter++}`\n name = getPerContentTypeName(baseName, suffix)\n }\n usedNames.add(name)\n return { name, suffix, schema: entry.schema!, keysToOmit: entry.keysToOmit, contentType: entry.contentType }\n })\n}\n\n// The request groups come from the grouped params, so `config` drops the data-shape keys to stay\n// assignable to `Options`, which omits them from `RequestConfig`.\nconst requestConfigBaseType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`\n\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n\n // Only the ambiguous side is offered: a single-type side has nothing to pick, so it stays baked in\n // the generated call.\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n return members.length ? `${requestConfigBaseType} & { contentType?: { ${members.join('; ')} } }` : requestConfigBaseType\n}\n\n/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...). Unlike {@link buildRequestConfigType}, it never adds a\n * `contentType?:` member: query hooks wrap GET operations, which carry no request body to select a\n * content type for.\n */\nexport function buildClientOptionType(): string {\n return requestConfigBaseType\n}\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n}\n\n/**\n * Which of the grouped request options an operation carries.\n */\nexport function getRequestGroups(node: ast.OperationNode): RequestGroups {\n const { path, query, header } = getOperationParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(node.requestBody?.content?.[0]?.schema),\n headers: header.length > 0,\n }\n}\n\nexport type RequestGroupOptionality = {\n groups: RequestGroups\n hasRequiredPath: boolean\n hasRequiredQuery: boolean\n hasRequiredHeader: boolean\n /**\n * Whether the grouped request parameter can default to `{}`. True only when no group carries a\n * required member, so every member is safe to omit.\n */\n isOptional: boolean\n}\n\n/**\n * Resolves which grouped request options an operation carries together with whether each group\n * holds a required member. The grouped parameter stays optional only when nothing inside it is\n * required, matching the generated `RequestConfig` type.\n */\nexport function getRequestGroupOptionality(node: ast.OperationNode): RequestGroupOptionality {\n const groups = getRequestGroups(node)\n const { path, query, header } = getOperationParameters(node)\n const hasRequiredPath = path.some((param) => param.required)\n const hasRequiredQuery = query.some((param) => param.required)\n const hasRequiredHeader = header.some((param) => param.required)\n\n return {\n groups,\n hasRequiredPath,\n hasRequiredQuery,\n hasRequiredHeader,\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body,\n }\n}\n\nexport type RequestOptionsNameResolver = RequestConfigResolver & {\n response: {\n options(node: ast.OperationNode): string\n }\n}\n\n/**\n * Builds the grouped `{ path, query, body, headers }` parameter for a generated client\n * function, typed from the operation's `Options` (minus `url`). Only the groups the\n * operation actually has are destructured. The trailing `config` parameter carries the\n * runtime `RequestConfig` overrides plus `client`.\n */\nexport function buildRequestParamsSignature(\n node: ast.OperationNode,\n resolver: RequestOptionsNameResolver,\n options: { isConfigurable?: boolean } = {},\n): { signature: string; groups: RequestGroups } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = (['path', 'query', 'body', 'headers'] as const).filter((key) => groups[key])\n\n // These callers never carry the `contentType` that several media types require.\n const optionsType = hasRequestContentTypeChoice(node)\n ? `Pick<${resolver.response.options(node)}, ${names.map((name) => `'${name}'`).join(' | ')}>`\n : resolver.response.options(node)\n const firstParam = names.length > 0 ? `{ ${names.join(', ')} }: ${optionsType}${isOptional ? ' = {}' : ''}` : null\n const configParam = isConfigurable ? `config: ${buildRequestConfigType(node)} = {}` : null\n\n return {\n signature: [firstParam, configParam].filter(Boolean).join(', '),\n groups,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { link = 'pathTemplate', linkPosition = 'afterDeprecated', splitLines = false } = options\n const linkComment = getOperationLink(node, link)\n const comments =\n linkPosition === 'beforeDeprecated'\n ? [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, linkComment, node.deprecated && '@deprecated']\n : [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, node.deprecated && '@deprecated', linkComment]\n\n const filteredComments = comments.filter((comment): comment is string => Boolean(comment))\n\n if (!splitLines) {\n return filteredComments\n }\n\n return filteredComments.flatMap((text) => text.split(/\\r?\\n/).map((line) => line.trim())).filter((comment): comment is string => Boolean(comment))\n}\n\nconst operationParameterGroupsByNode = new WeakMap<ast.OperationNode, OperationParameterGroups>()\n\n/**\n * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each\n * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance\n * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the\n * same parameters once per plugin.\n */\nexport function getOperationParameters(node: ast.OperationNode): OperationParameterGroups {\n const cached = operationParameterGroupsByNode.get(node)\n if (cached) return cached\n\n // A malformed spec can declare the same name twice within one location. Both would resolve to the\n // same output property, so emitting both would yield an object type with a duplicate member,\n // which TypeScript rejects. Keep the first.\n const group = (location: ast.ParameterNode['in']) => {\n const seen = new Set<string>()\n\n return node.parameters.filter((param) => {\n if (param.in !== location || seen.has(param.name)) return false\n seen.add(param.name)\n return true\n })\n }\n\n const groups: OperationParameterGroups = { path: group('path'), query: group('query'), header: group('header'), cookie: group('cookie') }\n\n operationParameterGroupsByNode.set(node, groups)\n return groups\n}\n\n/**\n * Builds the combined `{ body, path, query, headers }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same\n * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod\n * printer to emit `z.object(…)` rather than a record.\n */\nexport function hasRequestContentTypeChoice(node: ast.OperationNode): boolean {\n return (node.requestBody?.content ?? []).filter((entry) => entry.schema).length > 1\n}\n\n/**\n * Casts a call argument to the client's parameter type; a `'plain'` client is non-generic.\n */\nexport function castToClientOptions(callArg: string, clientName: string, returnType: 'full' | 'data' | 'plain' = 'full'): string {\n return `${callArg} as Parameters<typeof ${clientName}${returnType === 'plain' ? '' : '<true>'}>[0]`\n}\n\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver, { pairContentTypes = false } = {}): ast.SchemaNode {\n const { path, query, header } = getOperationParameters(node)\n const hasBody = Boolean(node.requestBody?.content?.[0]?.schema)\n const createNever = () => ast.factory.createSchema({ type: 'never', primitive: undefined, optional: true })\n const groups = [\n { name: 'path', params: path, resolve: resolver.param.path },\n { name: 'query', params: query, resolve: resolver.param.query },\n { name: 'headers', params: header, resolve: resolver.param.headers },\n ] as const\n\n const createOptions = (body: ast.SchemaNode, extra: Array<ast.PropertyNode> = []) =>\n // NOTE(v5-stable): the fields were renamed from the legacy beta shape\n // (`data`/`pathParams`/`queryParams`/`headerParams`) to `body`/`path`/`query`/`headers` so the\n // type matches the runtime client. Drop this note once v5 leaves beta.\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n deprecated: node.deprecated,\n properties: [\n ast.factory.createProperty({ name: 'body', required: hasBody, schema: body }),\n ...groups.map(({ name, params, resolve }) => {\n const required = params.some((param) => param.required)\n\n return ast.factory.createProperty({\n name,\n required,\n schema:\n params.length > 0\n ? ast.factory.createSchema({ type: 'ref', name: resolve.call(resolver.param, node, params[0]!), optional: !required })\n : createNever(),\n })\n }),\n ...extra,\n ],\n })\n\n const variants =\n pairContentTypes && hasRequestContentTypeChoice(node) ? resolveContentTypeVariants(node.requestBody?.content ?? [], resolver.response.body(node)) : []\n if (variants.length < 2) {\n return createOptions(hasBody ? ast.factory.createSchema({ type: 'ref', name: resolver.response.body(node) }) : createNever())\n }\n\n // The first media type is the runtime default, so only the others must pass `contentType`.\n return ast.factory.createSchema({\n type: 'union',\n members: variants.map((variant, index) =>\n createOptions(ast.factory.createSchema({ type: 'ref', name: variant.name }), [\n ast.factory.createProperty({\n name: 'contentType',\n required: index > 0,\n schema: ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [\n ast.factory.createProperty({\n name: 'request',\n required: true,\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] }),\n }),\n ],\n }),\n }),\n ]),\n ),\n })\n}\n\n/**\n * The schema a status occupies in the `<Name>Responses` record. A status that documents several\n * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type\n * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the\n * query hooks and `result.data` use.\n */\nfunction buildResponseRecordEntry(node: ast.OperationNode, res: ast.ResponseNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n const statusName = resolver.response.status(node, res.statusCode)\n const variants = (res.content ?? []).filter((entry) => entry.schema)\n if (variants.length <= 1) {\n return ast.factory.createSchema({ type: 'ref', name: statusName })\n }\n\n return ast.factory.createSchema({\n type: 'union',\n members: resolveContentTypeVariants(variants, statusName).map((variant) =>\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [\n ast.factory.createProperty({\n name: 'contentType',\n required: true,\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema: ast.factory.createSchema({ type: 'ref', name: variant.name }),\n }),\n ],\n }),\n ),\n })\n}\n\n/**\n * Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved\n * `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s\n * inferred responses schema, so both emit the same shape from the same inputs.\n *\n * Always emits the keyed record, even when an operation declares no responses. An operation with no\n * responses renders as an empty object, which keeps every consumer's import (for example the axios\n * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.\n */\nexport function buildResponses(node: ast.OperationNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n return ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: node.responses.map((res) =>\n ast.factory.createProperty({\n name: String(res.statusCode),\n required: true,\n schema: buildResponseRecordEntry(node, res, resolver),\n }),\n ),\n })\n}\n\nexport function getStatusCodeNumber(statusCode: ast.StatusCode | number | string): number | null {\n const code = Number(statusCode)\n\n return Number.isNaN(code) ? null : code\n}\n\nexport function isSuccessStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 200 && code < 300\n}\n\nexport function isErrorStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 400\n}\n\nexport function getSuccessResponses<TResponse extends ResponseLike>(responses: ReadonlyArray<TResponse>): Array<TResponse> {\n return responses.filter((response) => isSuccessStatusCode(response.statusCode))\n}\n\nexport function getOperationSuccessResponses(node: ast.OperationNode): Array<ast.ResponseNode> {\n return getSuccessResponses(node.responses)\n}\n\nexport function getPrimarySuccessResponse(node: ast.OperationNode): ast.ResponseNode | null {\n return getOperationSuccessResponses(node)[0] ?? null\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\n/**\n * Lists the type names an operation's types file declares, computed once per resolver and `options` so every\n * dependent reads the same list.\n */\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver & Pick<Resolver, 'memo'>,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const key = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n return resolver.memo(`operationTypeNames:${key}`, () => {\n const { path, query, header } = getOperationParameters(node)\n const responseStatusNames =\n options.responseStatusNames === 'error'\n ? resolveErrorNames(node, resolver)\n : options.responseStatusNames === false\n ? []\n : resolveStatusCodeNames(node, resolver)\n const exclude = new Set(options.exclude ?? [])\n const paramNames =\n options.includeParams === false\n ? []\n : [\n ...path.map((param) => resolver.param.path(node, param)),\n ...query.map((param) => resolver.param.query(node, param)),\n ...header.map((param) => resolver.param.headers(node, param)),\n ]\n const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n return names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n })\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = getStatusCodeNumber(response.statusCode)\n if (code === null) {\n continue\n }\n\n types.push([code, isSuccessStatusCode(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","import { ensureValidVarName, toFilePath } from '@internals/utils'\nimport { camelCase } from 'kubb/kit'\nimport type { ast, Resolver, ResolverFile } from 'kubb/kit'\n\n/**\n * The shared top-level `name` of the operation plugins, computed once per operation name and build.\n */\nexport function resolveOperationName(this: Resolver, name: string): string {\n return this.memo(`operation:${name}`, () => ensureValidVarName(camelCase(name)))\n}\n\n/**\n * Builds the allocation key for a declaration an operation owns. `scope` names which declaration\n * it is, so two of them never share an allocation even when a custom resolver gives both the same\n * name. Leave `scope` empty for the operation's own declaration.\n *\n * @example\n * ```ts\n * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })\n * ```\n */\nexport function operationKey(node: ast.OperationNode, ...scope: Array<string | number>): Array<string | undefined> {\n return ['operation', node.method, node.path, ...scope.map(String)]\n}\n\n/**\n * The `param` namespace shared by the schema-producing plugins (ts, zod, faker):\n * per-parameter names plus the grouped `Path`/`Query`/`Headers` names, all routed\n * through the plugin's top-level `name` casing.\n */\nexport type OperationParamResolver = {\n name(this: Resolver, node: ast.OperationNode, param: ast.ParameterNode): string\n path(this: Resolver, node: ast.OperationNode): string\n query(this: Resolver, node: ast.OperationNode): string\n headers(this: Resolver, node: ast.OperationNode): string\n}\n\n/**\n * The `response` namespace shared by the schema-producing plugins: per-status,\n * body, and combined response names, all routed through the plugin's top-level\n * `name` casing.\n */\nexport type OperationResponseResolver = {\n status(this: Resolver, node: ast.OperationNode, statusCode: ast.StatusCode): string\n body(this: Resolver, node: ast.OperationNode): string\n responses(this: Resolver, node: ast.OperationNode): string\n response(this: Resolver, node: ast.OperationNode): string\n}\n\n/**\n * Builds the shared `param` namespace. Spread the result into `createResolver`\n * and override individual methods next to it when a plugin deviates.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ param: createOperationParamResolver(), ... })\n * ```\n */\nexport function createOperationParamResolver(): OperationParamResolver {\n return {\n name(node, param) {\n const name = this.name(`${node.operationId} ${param.in} ${param.name}`)\n return this.resolveName({ key: operationKey(node, 'param', 'name', param.in, param.name), name })\n },\n path(node) {\n const name = this.name(`${node.operationId} Path`)\n return this.resolveName({ key: operationKey(node, 'param', 'path'), name })\n },\n query(node) {\n const name = this.name(`${node.operationId} Query`)\n return this.resolveName({ key: operationKey(node, 'param', 'query'), name })\n },\n headers(node) {\n const name = this.name(`${node.operationId} Headers`)\n return this.resolveName({ key: operationKey(node, 'param', 'headers'), name })\n },\n }\n}\n\n/**\n * Builds the shared `response` namespace. Spread the result into\n * `createResolver` and add plugin-specific methods (`options`, `error`) next\n * to it.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ response: { ...createOperationResponseResolver(), options(node) {...} }, ... })\n * ```\n */\nexport function createOperationResponseResolver(): OperationResponseResolver {\n return {\n status(node, statusCode) {\n const name = this.name(`${node.operationId} Status ${statusCode}`)\n return this.resolveName({ key: operationKey(node, 'response', 'status', statusCode), name })\n },\n body(node) {\n const name = this.name(`${node.operationId} Body`)\n return this.resolveName({ key: operationKey(node, 'response', 'body'), name })\n },\n responses(node) {\n const name = this.name(`${node.operationId} Responses`)\n return this.resolveName({ key: operationKey(node, 'response', 'responses'), name })\n },\n response(node) {\n const name = this.name(`${node.operationId} Response`)\n return this.resolveName({ key: operationKey(node, 'response', 'response'), name })\n },\n }\n}\n\n/**\n * Builds a resolver `file` override whose base name runs every path segment\n * through `toFilePath`, casing the final segment with `caseLast`.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })\n * ```\n */\nexport function createCasedFile(caseLast: (part: string) => string): ResolverFile {\n return {\n baseName({ name, extname }) {\n return this.memo(`file:${name}\\0${extname}`, (): ast.FileNode['baseName'] => `${toFilePath(name, caseLast)}${extname}`)\n },\n }\n}\n\ndeclare global {\n namespace Kubb {\n interface MemoRegistry {\n operation: string\n file: ast.FileNode['baseName']\n operationTypeNames: Array<string>\n }\n }\n}\n","import { camelCase, type Group } from 'kubb/kit'\n\n/**\n * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the\n * shared default naming so every plugin groups output consistently:\n *\n * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).\n * - other groups use the camelCased group (`pet store` → `petStore`).\n *\n * A user-provided `group.name` always wins over the default namer, so callers stay in\n * control of their output folders. Returns `null` when grouping is disabled, matching the\n * per-plugin convention.\n *\n * @param group - The user-supplied group option, or `undefined` to disable grouping.\n *\n * @example\n * ```ts\n * createGroupConfig(group) // shared across every plugin\n * ```\n */\nexport function createGroupConfig(group: Group | undefined): Group | null {\n if (!group) {\n return null\n }\n\n const defaultName = (ctx: { group: string }): string => {\n if (group.type === 'path') {\n return `${ctx.group.split('/')[1]}`\n }\n\n return camelCase(ctx.group)\n }\n\n return {\n ...group,\n name: group.name ? group.name : defaultName,\n } satisfies Group\n}\n","","","import { ast, camelCase, defineGenerator } from 'kubb/kit'\nimport type { PluginMsw } from '../types'\n\n/**\n * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a\n * `handlers.ts` file that re-exports every generated handler in operation\n * order, ready to spread into `setupServer(...handlers)` or\n * `setupWorker(...handlers)`.\n */\nexport const handlersGenerator = defineGenerator<PluginMsw>({\n name: 'plugin-msw',\n operations(nodes, ctx) {\n const { resolver, config, root } = ctx\n const { output, group } = ctx.options\n\n const handlersName = resolver.handler.listName()\n const createHandlersName = resolver.resolveName({ key: ['handlers', 'create'], name: camelCase(handlersName, { prefix: 'create' }) })\n const file = resolver.file({ name: handlersName, extname: '.ts', root, output, group: group ?? undefined })\n\n const handlerNames = nodes.map((node) => resolver.handler.name(node))\n\n const imports = nodes.map((node, index) => {\n const operationName = handlerNames[index]!\n const operationFile = resolver.file({\n name: resolver.name(node.operationId),\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output,\n group: group ?? undefined,\n })\n return ast.factory.createImport({ name: [operationName], root: file.path, path: operationFile.path })\n })\n\n const handlers = handlerNames.map((name) => `${name}(undefined, options)`)\n\n return [\n ast.factory.createFile({\n baseName: file.baseName,\n path: file.path,\n meta: file.meta,\n banner: resolver.default.banner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n footer: resolver.default.footer(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n imports,\n sources: [\n ast.factory.createSource({\n name: createHandlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [\n ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {\n return [${handlers.join(', ')}] as const\n}`),\n ],\n }),\n ast.factory.createSource({\n name: handlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)],\n }),\n ],\n }),\n ]\n },\n})\n","import type { ast } from 'kubb/kit'\n\n/**\n * Returns the base URL as source code. A `${...}` value stays a template literal.\n */\nexport function resolveDefaultBaseURL(baseURL: string | null | undefined): string {\n if (!baseURL) {\n return \"''\"\n }\n if (baseURL.includes('${')) {\n return `\\`${baseURL.replaceAll('`', '\\\\`')}\\``\n }\n return JSON.stringify(baseURL)\n}\n\n/**\n * Gets the content type from a response, defaulting to 'application/json' if a schema exists.\n */\nexport function getContentType(response: ast.ResponseNode | null | undefined): string | null {\n if (!hasResponseSchema(response)) {\n return null\n }\n\n return getResponseContentType(response) ?? 'application/json'\n}\n\n/**\n * Determines if a response has a usable body schema.\n */\nexport function hasResponseSchema(response: ast.ResponseNode | null | undefined): boolean {\n const schema = response?.content?.find((entry) => entry.schema)?.schema\n return !!schema && schema.type !== 'void' && schema.type !== 'any' && schema.type !== 'unknown'\n}\n\n/**\n * Picks the content type used for the mocked response header. When a response declares multiple\n * content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.\n */\nfunction getResponseContentType(response: ast.ResponseNode | null | undefined): string | null {\n const contents = response?.content ?? []\n const jsonEntry = contents.find((entry) => {\n const baseType = entry.contentType?.split(';')[0]?.trim().toLowerCase()\n return baseType === 'application/json' || baseType?.endsWith('+json')\n })\n const value = (jsonEntry ?? contents[0])?.contentType\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no\n * `http.query`, so a QUERY operation maps to `http.all` and its handler checks the method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return node.method === 'QUERY' ? 'all' : node.method.toLowerCase()\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no content type.\n */\nfunction headersBlock(contentType: string | null): string {\n return contentType ? `\\n headers: {\\n 'Content-Type': '${contentType}'\\n },` : ''\n}\n\n/**\n * The `new HttpResponse(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new HttpResponse(${body}, {\n status: ${status},${headersBlock(contentType)}\n })`\n}\n","import { getPrimarySuccessResponse } from '@internals/shared'\nimport type { ast } from 'kubb/kit'\nimport { Url } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseLiteral } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so the QUERY handler lets the others pass through.\n const methodGuard = node.method === 'QUERY' ? `if (info.request.method !== 'QUERY') return\\n\\n ` : ''\n const successResponse = getPrimarySuccessResponse(node)\n const statusCode = successResponse ? Number(successResponse.statusCode) : 200\n const contentType = getContentType(successResponse)\n const url = Url.toPath(node.path)\n\n const responseHasSchema = hasResponseSchema(successResponse)\n const dataType = responseHasSchema ? typeName : 'string | number | boolean | null | object'\n const paramType = fakerName ? typeName : dataType\n const requestBodyType = requestTypeName ? `${requestTypeName} extends DefaultBodyType ? ${requestTypeName} : DefaultBodyType` : null\n\n const callbackType = requestBodyType\n ? `HttpResponseResolver<Record<string, string>, ${requestBodyType}>`\n : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`\n\n const params = `data?: ${paramType} | ${callbackType}, options?: { baseURL?: string }`\n\n const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`\n\n // Escape colons that do not start a param, such as the second one in `/pet/:petId:search`.\n const urlLiteral = `\\`\\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, '$1\\\\\\\\:')}\\``\n const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : 'JSON.stringify(data)'\n\n return (\n <File.Source name={name} isIndexable isExportable>\n <Function name={name} export params={params}>\n {`return ${httpCall}(${urlLiteral}, function handler(info) {\n ${methodGuard}if(typeof data === 'function') return data(info)\n\n return ${responseLiteral(responseBody, statusCode, contentType)}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import type { ast } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, hasResponseSchema, responseLiteral } from '../utils.ts'\n\ntype Props = {\n typeName: string\n name: string\n response: ast.ResponseNode\n}\n\nexport function Response({ name, typeName, response }: Props): KubbReactNode {\n const statusCode = Number(response.statusCode)\n const contentType = getContentType(response)\n\n const params = `data${hasResponseSchema(response) ? '' : '?'}: ${typeName}`\n\n const responseName = `${name}Response${statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n return ${responseLiteral('JSON.stringify(data)', statusCode, contentType)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getOperationSuccessResponses, resolveDependencyOperationFile, resolveResponseTypes } from '@internals/shared'\nimport { ast, defineGenerator } from 'kubb/kit'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { enumTypeName, pluginTsName } from '@kubb/plugin-ts'\nimport { File, jsxRenderer } from 'kubb/jsx'\nimport { Mock } from '../components/Mock.tsx'\nimport { Response } from '../components/Response.tsx'\nimport type { PluginMsw } from '../types'\nimport { hasResponseSchema } from '../utils.ts'\n\n/**\n * Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler\n * per OpenAPI operation. With `parser: 'faker'` the handler returns a value\n * from `@kubb/plugin-faker`; with `parser: 'data'` it returns a typed empty\n * payload for tests to fill in.\n */\nexport const mswGenerator = defineGenerator<PluginMsw>({\n name: 'msw',\n renderer: jsxRenderer,\n operation(node, ctx) {\n if (!ast.isHttpOperationNode(node)) return null\n const { driver, resolver, config, root } = ctx\n const { output, parser, baseURL, group } = ctx.options\n\n const fileName = resolver.name(node.operationId)\n const mock = {\n name: resolver.handler.name(node),\n file: resolver.file({ name: fileName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path, root, output, group: group ?? undefined }),\n }\n\n const fakerPlugin = parser === 'faker' ? ctx.requirePlugin(pluginFakerName) : null\n const fakerResolver = fakerPlugin ? driver.getResolver(pluginFakerName) : null\n const faker =\n fakerPlugin && fakerResolver\n ? {\n name: fakerResolver.response.response(node),\n file: fakerResolver.file({\n name: node.operationId,\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output: fakerPlugin.options?.output ?? output,\n group: fakerPlugin.options?.group ?? undefined,\n }),\n }\n : null\n\n const pluginTs = ctx.requirePlugin(pluginTsName)\n const tsResolver = driver.getResolver(pluginTsName)\n\n const type = {\n file: resolveDependencyOperationFile({\n cache: ctx.cache,\n node,\n resolver: tsResolver,\n root,\n output: pluginTs.options?.output ?? output,\n group: pluginTs.options?.group,\n }),\n responseName: tsResolver.response.response(node),\n }\n\n const successResponses = getOperationSuccessResponses(node)\n const referencedNames = node.responses.flatMap((response) =>\n (response.content ?? []).flatMap((entry) => (entry.schema ? ast.collectImportedRefNames(entry.schema) : [])),\n )\n const enumOptions = pluginTs.options?.enum\n const enumNames = new Set(ctx.meta.enumNames)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n const types = resolveResponseTypes(node, tsResolver).map(([code, typeName]) => {\n const successResponse = successResponses.find((response) => response.statusCode === String(code))\n return [code, hasResponseNameCollision && successResponse ? tsResolver.response.status(node, successResponse.statusCode) : typeName] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName).filter((name) => !hasResponseNameCollision || name !== type.responseName)\n const mockResponseName =\n hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName\n const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response))\n\n const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.response.body(node) : null\n\n return (\n <File\n baseName={mock.file.baseName}\n path={mock.file.path}\n meta={mock.file.meta}\n banner={resolver.default.banner(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n footer={resolver.default.footer(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n >\n <File.Import name={['http', 'HttpResponse']} path=\"msw/http\" />\n <File.Import name={['HttpResponseResolver']} isTypeOnly path=\"msw/http\" />\n {requestName && <File.Import name={['DefaultBodyType']} isTypeOnly path=\"msw\" />}\n <File.Import\n name={Array.from(new Set([...(hasResponseNameCollision ? [] : [type.responseName]), ...responseTypeNames, ...(requestName ? [requestName] : [])]))}\n path={type.file.path}\n root={mock.file.path}\n isTypeOnly\n />\n {faker && hasSuccessSchema && <File.Import name={[faker.name]} root={mock.file.path} path={faker.file.path} />}\n\n {types\n .filter(([code]) => code !== 'default')\n .map(([code, typeName]) => {\n const response = node.responses.find((item) => item.statusCode === String(code))\n if (!response) return null\n return <Response key={typeName} typeName={typeName} response={response} name={mock.name} />\n })}\n\n <Mock\n name={mock.name}\n typeName={mockResponseName}\n requestTypeName={requestName}\n fakerName={faker && hasSuccessSchema ? faker.name : undefined}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { operationKey } from '@internals/shared'\nimport { camelCase, createResolver } from 'kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return this.memo(`name:${name}`, () => camelCase(name, { suffix: 'handler' }))\n },\n handler: {\n name(node) {\n const name = this.name(node.operationId)\n return this.resolveName({ key: operationKey(node, 'handler', 'name'), name })\n },\n listName() {\n return this.resolveName({ key: ['handlers', 'list'], name: 'handlers' })\n },\n },\n})\n","import { createGroupConfig } from '@internals/shared'\nimport { addGenerator, defineExtension, Resolver, setMacros, setResolver } from 'kubb/kit'\nimport { compatibility } from '../kubb-extension.json'\nimport { version } from '../package.json'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { pluginTsName } from '@kubb/plugin-ts'\nimport { handlersGenerator } from './generators/handlersGenerator.ts'\nimport { mswGenerator } from './generators/mswGenerator.tsx'\nimport { resolverMsw } from './resolvers/resolverMsw.ts'\nimport type { PluginMsw, ResolverMsw } from './types.ts'\n\n/**\n * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and\n * cross-plugin dependency references.\n */\nexport const pluginMswName = 'plugin-msw' satisfies PluginMsw['name']\n\n/**\n * Generates MSW request handlers from an OpenAPI spec. Drop them into your\n * test setup or service worker to mock the API end-to-end. Request path,\n * method, status, and response body all stay in sync with the spec. Combine\n * with `@kubb/plugin-faker` (via `parser: 'faker'`) to seed handlers with\n * realistic data.\n *\n * @example\n * ```ts\n * import { defineConfig } from 'kubb/config'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginMsw } from '@kubb/plugin-msw'\n *\n * export default defineConfig({\n * input: './petStore.yaml',\n * output: { path: './src/gen' },\n * plugins: [\n * pluginTs(),\n * pluginMsw({\n * output: { path: './handlers' },\n * handlers: true,\n * }),\n * ],\n * })\n * ```\n */\nexport const pluginMsw = defineExtension<PluginMsw>({\n kind: 'plugin',\n meta: { name: pluginMswName, version, compatibility },\n dependencies: (options) => ({ [pluginTsName]: {}, ...(options.parser === 'faker' ? { [pluginFakerName]: {} } : {}) }),\n setup(options) {\n const {\n output = { path: 'handlers', barrel: { type: 'named' } },\n group,\n exclude = [],\n include,\n override = [],\n handlers = false,\n parser = 'data',\n baseURL,\n resolver: userResolver,\n macros: userMacros,\n } = options\n\n const resolver = userResolver ? Resolver.merge<ResolverMsw>(resolverMsw, userResolver) : resolverMsw\n\n setResolver(resolver)\n if (userMacros?.length) {\n setMacros(userMacros)\n }\n\n addGenerator(mswGenerator)\n if (handlers) {\n addGenerator(handlersGenerator)\n }\n\n return {\n output,\n parser,\n baseURL,\n group: createGroupConfig(group),\n exclude,\n include,\n override,\n handlers,\n }\n },\n})\n\nexport default pluginMsw\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Exclude, Group, Include, Output, OutputOptions, Override, PluginType, Resolver, ResolverPatch, ast } from "kubb/kit";
2
- //#region src/types.d.ts
3
2
  /**
4
3
  * Resolver for MSW that provides naming methods for handler functions.
5
4
  */
@@ -84,8 +83,6 @@ declare global {
84
83
  }
85
84
  }
86
85
  }
87
- //#endregion
88
- //#region src/plugin.d.ts
89
86
  /**
90
87
  * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and
91
88
  * cross-plugin dependency references.
@@ -118,8 +115,6 @@ export declare const pluginMswName = "plugin-msw";
118
115
  * ```
119
116
  */
120
117
  export declare const pluginMsw: import("kubb/kit").PluginFactory<PluginMsw>;
121
- //#endregion
122
- //#region src/resolvers/resolverMsw.d.ts
123
118
  /**
124
119
  * Default resolver used by `@kubb/plugin-msw`. Decides the names and file
125
120
  * paths for every generated MSW handler. Function names get a `Handler`
@@ -136,6 +131,5 @@ export declare const pluginMsw: import("kubb/kit").PluginFactory<PluginMsw>;
136
131
  * ```
137
132
  */
138
133
  export declare const resolverMsw: ResolverMsw;
139
- //#endregion
140
134
  export { type PluginMsw, type ResolverMsw, pluginMsw as default };
141
135
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Exclude, Group, Include, Output, OutputOptions, Override, PluginType, Resolver, ResolverPatch, ast } from "kubb/kit";
2
- //#region src/types.d.ts
3
2
  /**
4
3
  * Resolver for MSW that provides naming methods for handler functions.
5
4
  */
@@ -84,8 +83,6 @@ declare global {
84
83
  }
85
84
  }
86
85
  }
87
- //#endregion
88
- //#region src/plugin.d.ts
89
86
  /**
90
87
  * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and
91
88
  * cross-plugin dependency references.
@@ -118,8 +115,6 @@ export declare const pluginMswName = "plugin-msw";
118
115
  * ```
119
116
  */
120
117
  export declare const pluginMsw: import("kubb/kit").PluginFactory<PluginMsw>;
121
- //#endregion
122
- //#region src/resolvers/resolverMsw.d.ts
123
118
  /**
124
119
  * Default resolver used by `@kubb/plugin-msw`. Decides the names and file
125
120
  * paths for every generated MSW handler. Function names get a `Handler`
@@ -136,6 +131,5 @@ export declare const pluginMsw: import("kubb/kit").PluginFactory<PluginMsw>;
136
131
  * ```
137
132
  */
138
133
  export declare const resolverMsw: ResolverMsw;
139
- //#endregion
140
134
  export { type PluginMsw, type ResolverMsw, pluginMsw as default };
141
135
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,10 +1,9 @@
1
- import { Resolver, Url, addGenerator, ast, createResolver, defineExtension, defineGenerator, setMacros, setResolver } from "kubb/kit";
1
+ import { Resolver, Url, addGenerator, ast, camelCase, createResolver, defineExtension, defineGenerator, setMacros, setResolver } from "kubb/kit";
2
2
  import { pluginFakerName } from "@kubb/plugin-faker";
3
3
  import { enumTypeName, pluginTsName } from "@kubb/plugin-ts";
4
4
  import { File, Function, jsxRenderer } from "kubb/jsx";
5
5
  import { jsx, jsxs } from "kubb/jsx/jsx-runtime";
6
- //#endregion
7
- //#region ../../internals/shared/src/operation.ts
6
+ // ../../internals/shared/src/operation.ts
8
7
  /**
9
8
  * Builds the `ResolverFileParams` every operation generator passes to
10
9
  * `resolver.file`: a file named `name`, tagged by the operation's first
@@ -74,46 +73,26 @@ function resolveResponseTypes(node, resolver) {
74
73
  }
75
74
  return types;
76
75
  }
77
- //#endregion
78
- //#region ../../internals/utils/src/casing.ts
76
+ // ../../internals/shared/src/resolver.ts
79
77
  /**
80
- * Shared implementation for camelCase and PascalCase conversion.
81
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
82
- * and capitalizes each word according to `pascal`.
78
+ * Builds the allocation key for a declaration an operation owns. `scope` names which declaration
79
+ * it is, so two of them never share an allocation even when a custom resolver gives both the same
80
+ * name. Leave `scope` empty for the operation's own declaration.
83
81
  *
84
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
85
- */
86
- function convert(text, pascal) {
87
- return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
88
- if (word.length > 1 && word === word.toUpperCase()) return word;
89
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
90
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
91
- }
92
- const cache = /* @__PURE__ */ new Map();
93
- const cacheLimit = 1e4;
94
- function toCamelOrPascal(text, pascal) {
95
- const key = `${pascal ? "P" : "c"}${text}`;
96
- const cached = cache.get(key);
97
- if (cached !== void 0) return cached;
98
- const result = convert(text, pascal);
99
- if (cache.size >= cacheLimit) cache.clear();
100
- cache.set(key, result);
101
- return result;
102
- }
103
- /**
104
- * Converts `text` to camelCase.
105
- *
106
- * @example Word boundaries
107
- * `camelCase('hello-world') // 'helloWorld'`
108
- *
109
- * @example With a prefix
110
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
82
+ * @example
83
+ * ```ts
84
+ * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
85
+ * ```
111
86
  */
112
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
113
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
87
+ function operationKey(node, ...scope) {
88
+ return [
89
+ "operation",
90
+ node.method,
91
+ node.path,
92
+ ...scope.map(String)
93
+ ];
114
94
  }
115
- //#endregion
116
- //#region ../../internals/shared/src/group.ts
95
+ // ../../internals/shared/src/group.ts
117
96
  /**
118
97
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
119
98
  * shared default naming so every plugin groups output consistently:
@@ -143,17 +122,14 @@ function createGroupConfig(group) {
143
122
  name: group.name ? group.name : defaultName
144
123
  };
145
124
  }
146
- //#endregion
147
- //#region kubb-extension.json
125
+ // kubb-extension.json
148
126
  var compatibility = {
149
- "kubb": ">=5.8.3",
127
+ "kubb": ">=5.14.6",
150
128
  "node": ">=22.22"
151
129
  };
152
- //#endregion
153
- //#region package.json
154
- var version = "6.0.1";
155
- //#endregion
156
- //#region src/generators/handlersGenerator.ts
130
+ // package.json
131
+ var version = "6.0.3";
132
+ // src/generators/handlersGenerator.ts
157
133
  /**
158
134
  * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
159
135
  * `handlers.ts` file that re-exports every generated handler in operation
@@ -166,7 +142,10 @@ const handlersGenerator = defineGenerator({
166
142
  const { resolver, config, root } = ctx;
167
143
  const { output, group } = ctx.options;
168
144
  const handlersName = resolver.handler.listName();
169
- const createHandlersName = camelCase(handlersName, { prefix: "create" });
145
+ const createHandlersName = resolver.resolveName({
146
+ key: ["handlers", "create"],
147
+ name: camelCase(handlersName, { prefix: "create" })
148
+ });
170
149
  const file = resolver.file({
171
150
  name: handlersName,
172
151
  extname: ".ts",
@@ -230,8 +209,7 @@ const handlersGenerator = defineGenerator({
230
209
  })];
231
210
  }
232
211
  });
233
- //#endregion
234
- //#region src/utils.ts
212
+ // src/utils.ts
235
213
  /**
236
214
  * Returns the base URL as source code. A `${...}` value stays a template literal.
237
215
  */
@@ -287,8 +265,7 @@ function responseLiteral(body, status, contentType) {
287
265
  status: ${status},${headersBlock(contentType)}
288
266
  })`;
289
267
  }
290
- //#endregion
291
- //#region src/components/Mock.tsx
268
+ // src/components/Mock.tsx
292
269
  function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
293
270
  const method = getMswMethod(node);
294
271
  const methodGuard = node.method === "QUERY" ? `if (info.request.method !== 'QUERY') return\n\n ` : "";
@@ -297,8 +274,10 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
297
274
  const contentType = getContentType(successResponse);
298
275
  const url = Url.toPath(node.path);
299
276
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
300
- const params = `data?: ${fakerName ? typeName : dataType} | ${requestTypeName ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}, options?: { baseURL?: string }`;
301
- const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`;
277
+ const paramType = fakerName ? typeName : dataType;
278
+ const requestBodyType = requestTypeName ? `${requestTypeName} extends DefaultBodyType ? ${requestTypeName} : DefaultBodyType` : null;
279
+ const params = `data?: ${paramType} | ${requestBodyType ? `HttpResponseResolver<Record<string, string>, ${requestBodyType}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}, options?: { baseURL?: string }`;
280
+ const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`;
302
281
  const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
303
282
  const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : "JSON.stringify(data)";
304
283
  return /* @__PURE__ */ jsx(File.Source, {
@@ -317,8 +296,7 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
317
296
  })
318
297
  });
319
298
  }
320
- //#endregion
321
- //#region src/components/Response.tsx
299
+ // src/components/Response.tsx
322
300
  function Response({ name, typeName, response }) {
323
301
  const statusCode = Number(response.statusCode);
324
302
  const contentType = getContentType(response);
@@ -337,8 +315,7 @@ function Response({ name, typeName, response }) {
337
315
  })
338
316
  });
339
317
  }
340
- //#endregion
341
- //#region src/generators/mswGenerator.tsx
318
+ // src/generators/mswGenerator.tsx
342
319
  /**
343
320
  * Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler
344
321
  * per OpenAPI operation. With `parser: 'faker'` the handler returns a value
@@ -442,6 +419,11 @@ const mswGenerator = defineGenerator({
442
419
  isTypeOnly: true,
443
420
  path: "msw/http"
444
421
  }),
422
+ requestName && /* @__PURE__ */ jsx(File.Import, {
423
+ name: ["DefaultBodyType"],
424
+ isTypeOnly: true,
425
+ path: "msw"
426
+ }),
445
427
  /* @__PURE__ */ jsx(File.Import, {
446
428
  name: Array.from(/* @__PURE__ */ new Set([
447
429
  ...hasResponseNameCollision ? [] : [type.responseName],
@@ -478,8 +460,7 @@ const mswGenerator = defineGenerator({
478
460
  });
479
461
  }
480
462
  });
481
- //#endregion
482
- //#region src/resolvers/resolverMsw.ts
463
+ // src/resolvers/resolverMsw.ts
483
464
  /**
484
465
  * Default resolver used by `@kubb/plugin-msw`. Decides the names and file
485
466
  * paths for every generated MSW handler. Function names get a `Handler`
@@ -498,19 +479,25 @@ const mswGenerator = defineGenerator({
498
479
  const resolverMsw = createResolver({
499
480
  pluginName: "plugin-msw",
500
481
  name(name) {
501
- return camelCase(name, { suffix: "handler" });
482
+ return this.memo(`name:${name}`, () => camelCase(name, { suffix: "handler" }));
502
483
  },
503
484
  handler: {
504
485
  name(node) {
505
- return this.name(node.operationId);
486
+ const name = this.name(node.operationId);
487
+ return this.resolveName({
488
+ key: operationKey(node, "handler", "name"),
489
+ name
490
+ });
506
491
  },
507
492
  listName() {
508
- return "handlers";
493
+ return this.resolveName({
494
+ key: ["handlers", "list"],
495
+ name: "handlers"
496
+ });
509
497
  }
510
498
  }
511
499
  });
512
- //#endregion
513
- //#region src/plugin.ts
500
+ // src/plugin.ts
514
501
  /**
515
502
  * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and
516
503
  * cross-plugin dependency references.
@@ -575,7 +562,6 @@ const pluginMsw = defineExtension({
575
562
  };
576
563
  }
577
564
  });
578
- //#endregion
579
565
  export { pluginMsw as default, pluginMsw, pluginMswName, resolverMsw };
580
566
 
581
567
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../internals/shared/src/operation.ts","../../../internals/utils/src/casing.ts","../../../internals/shared/src/group.ts","../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../src/utils.ts","../src/components/Mock.tsx","../src/components/Response.tsx","../src/generators/mswGenerator.tsx","../src/resolvers/resolverMsw.ts","../src/plugin.ts"],"sourcesContent":["import { ast, type Group, type NodeCache, type Output, type Resolver, type ResolverFileParams, Url } from 'kubb/kit'\n\n/**\n * Builds the `ResolverFileParams` every operation generator passes to\n * `resolver.file`: a file named `name`, tagged by the operation's first\n * tag (or `'default'`), at the operation's path. Centralizes the entry object\n * that was repeated at dozens of call sites across the client and query plugins.\n *\n * @example\n * ```ts\n * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })\n * ```\n */\nexport function operationFileEntry(node: ast.OperationNode, name: string, extname: ResolverFileParams['extname'] = '.ts'): ResolverFileParams {\n return {\n name,\n extname,\n tag: node.tags[0] ?? 'default',\n path: node.path,\n }\n}\n\n/**\n * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the\n * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the\n * same dependency for the same operation in one pass (a query plugin's several hook generators, the\n * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.\n *\n * @example Cache `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveDependencyOperationFile(options: {\n cache: NodeCache\n node: ast.OperationNode\n resolver: Pick<Resolver, 'file' | 'pluginName'>\n root: string\n output: Output\n group?: Group | null\n}): ast.FileNode {\n const { cache, node, resolver, root, output, group } = options\n\n return cache.ensureItem(`${resolver.pluginName}:operationFile`, () =>\n resolver.file({ ...operationFileEntry(node, node.operationId), root, output, group: group ?? undefined }),\n )\n}\n\nexport type ContentTypeInfo = {\n contentTypes: string[]\n isMultipleContentTypes: boolean\n contentTypeUnion: string\n defaultContentType: string\n hasFormData: boolean\n}\n\nexport type RequestConfigResolver = {\n response: {\n body(node: ast.OperationNode): string\n }\n}\n\nexport type ResponseStatusNameResolver = {\n response: {\n status(node: ast.OperationNode, statusCode: ast.StatusCode): string\n }\n}\n\nexport type ResponseNameResolver = ResponseStatusNameResolver & {\n response: {\n response(node: ast.OperationNode): string\n }\n}\n\nexport type OperationTypeNameResolver = RequestConfigResolver &\n ResponseNameResolver & {\n param: {\n path(node: ast.OperationNode, param: ast.ParameterNode): string\n query(node: ast.OperationNode, param: ast.ParameterNode): string\n headers(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type OperationCommentLink = 'pathTemplate' | 'urlPath' | false | ((node: ast.OperationNode) => string | undefined)\n\nexport type BuildOperationCommentsOptions = {\n link?: OperationCommentLink\n linkPosition?: 'beforeDeprecated' | 'afterDeprecated'\n splitLines?: boolean\n}\n\ntype ResponseLike = {\n statusCode: ast.StatusCode | number | string\n}\n\nexport type OperationParameterGroups = Record<ast.ParameterNode['in'], Array<ast.ParameterNode>>\n\nexport type ResolveOperationTypeNameOptions = {\n responseStatusNames?: boolean | 'error'\n exclude?: ReadonlyArray<string | undefined>\n order?: 'params-first' | 'body-response-first'\n /**\n * Include the individual `Path`/`Query`/`Headers` group type names. Set to `false` for clients\n * that reference the grouped `Options` type instead of the per-group types.\n */\n includeParams?: boolean\n}\n\nfunction getOperationLink(node: ast.OperationNode, link: OperationCommentLink): string | null {\n if (!link) {\n return null\n }\n\n if (typeof link === 'function') {\n return link(node) ?? null\n }\n\n return node.path ? `{@link ${Url.toPath(node.path)}}` : null\n}\n\n/**\n * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several\n * are present and the union, default, and form-data flags the client uses to pick one.\n */\nfunction buildContentTypeInfo(contentTypes: string[]): ContentTypeInfo {\n const isMultipleContentTypes = contentTypes.length > 1\n\n return {\n contentTypes,\n isMultipleContentTypes,\n contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(' | ') : '',\n defaultContentType: contentTypes[0] ?? 'application/json',\n hasFormData: contentTypes.some((ct) => ct === 'multipart/form-data'),\n }\n}\n\nexport function getContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? [])\n}\n\n/**\n * The request-body counterpart for the primary success response: the content types it documents and\n * whether several are present, so the client can let a caller pick which one to accept.\n */\nexport function getResponseContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\nexport type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n\n/**\n * Reads the single base content type of an operation's primary success response, lowercased and\n * stripped of any `; charset=...` suffix. Returns `undefined` when the response declares zero or\n * more than one content type, since neither case has a single type to act on.\n */\nfunction getPrimarySuccessContentType(node: ast.OperationNode): string | undefined {\n const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? []\n if (contentTypes.length !== 1) return undefined\n return contentTypes[0]!.split(';')[0]!.trim().toLowerCase()\n}\n\n/**\n * Whether an operation streams its primary success response as Server-Sent Events\n * (`text/event-stream`). The client generator uses this to return a typed event stream instead of a\n * one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getPrimarySuccessContentType(node) === 'text/event-stream'\n}\n\n/**\n * Derives the default `responseType` for an operation from its primary success response.\n *\n * Returns a value only when that response declares a single non-JSON content type. `text/event-stream`\n * and other binary types (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`,\n * `video/*`) map to a stream or `'blob'`, and other `text/*` maps to `'text'`. Otherwise `undefined`,\n * leaving the runtime client's `Content-Type` auto-detection in charge.\n */\nexport function getResponseType(node: ast.OperationNode): ResponseType | undefined {\n const baseType = getPrimarySuccessContentType(node)\n if (!baseType) return undefined\n\n if (baseType === 'application/json' || baseType.endsWith('+json') || baseType === 'text/json') return undefined\n if (baseType === 'text/event-stream') return 'stream'\n if (baseType.startsWith('text/')) return 'text'\n if (baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)) return 'blob'\n return undefined\n}\n\n/**\n * Maps a content type to the PascalCase suffix used to name per-content-type variants\n * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).\n */\nfunction getContentTypeSuffix(contentType: string): string {\n const baseType = contentType.split(';')[0]!.trim()\n if (baseType === 'application/json') return 'Json'\n if (baseType === 'multipart/form-data') return 'FormData'\n if (baseType === 'application/x-www-form-urlencoded') return 'FormUrlEncoded'\n const subtype = baseType.split('/').pop() ?? baseType\n const parts = subtype.split(/[^a-zA-Z0-9]+/).filter(Boolean)\n if (parts.length === 0) return 'Unknown'\n return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('')\n}\n\n/**\n * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last\n * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).\n */\nexport function getPerContentTypeName(baseName: string, suffix: string): string {\n if (baseName.endsWith('Data')) {\n return suffix.endsWith('Data') ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`\n }\n return baseName + suffix\n}\n\nexport type ContentVariantInput = { contentType: string; schema?: ast.SchemaNode | null; keysToOmit?: Array<string> | null }\nexport type ContentVariant = { name: string; suffix: string; schema: ast.SchemaNode; keysToOmit?: Array<string> | null; contentType: string }\n\n/**\n * Resolves per-content-type variant names for a set of content entries, deduplicating suffix\n * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is\n * the final (possibly counter-augmented) value, so callers can derive parallel names in another\n * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).\n */\nexport function resolveContentTypeVariants(entries: Array<ContentVariantInput>, baseName: string): Array<ContentVariant> {\n const usedNames = new Set<string>()\n return entries\n .filter((entry) => entry.schema)\n .map((entry) => {\n const baseSuffix = getContentTypeSuffix(entry.contentType)\n let suffix = baseSuffix\n let name = getPerContentTypeName(baseName, suffix)\n let counter = 2\n while (usedNames.has(name)) {\n suffix = `${baseSuffix}${counter++}`\n name = getPerContentTypeName(baseName, suffix)\n }\n usedNames.add(name)\n return { name, suffix, schema: entry.schema!, keysToOmit: entry.keysToOmit, contentType: entry.contentType }\n })\n}\n\n// The request groups come from the grouped params, so `config` drops the data-shape keys to stay\n// assignable to `Options`, which omits them from `RequestConfig`.\nconst requestConfigBaseType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`\n\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n\n // Only the ambiguous side is offered: a single-type side has nothing to pick, so it stays baked in\n // the generated call.\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n return members.length ? `${requestConfigBaseType} & { contentType?: { ${members.join('; ')} } }` : requestConfigBaseType\n}\n\n/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...). Unlike {@link buildRequestConfigType}, it never adds a\n * `contentType?:` member: query hooks wrap GET operations, which carry no request body to select a\n * content type for.\n */\nexport function buildClientOptionType(): string {\n return requestConfigBaseType\n}\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n}\n\n/**\n * Which of the grouped request options an operation carries.\n */\nexport function getRequestGroups(node: ast.OperationNode): RequestGroups {\n const { path, query, header } = getOperationParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(node.requestBody?.content?.[0]?.schema),\n headers: header.length > 0,\n }\n}\n\nexport type RequestGroupOptionality = {\n groups: RequestGroups\n hasRequiredPath: boolean\n hasRequiredQuery: boolean\n hasRequiredHeader: boolean\n /**\n * Whether the grouped request parameter can default to `{}`. True only when no group carries a\n * required member, so every member is safe to omit.\n */\n isOptional: boolean\n}\n\n/**\n * Resolves which grouped request options an operation carries together with whether each group\n * holds a required member. The grouped parameter stays optional only when nothing inside it is\n * required, matching the generated `RequestConfig` type.\n */\nexport function getRequestGroupOptionality(node: ast.OperationNode): RequestGroupOptionality {\n const groups = getRequestGroups(node)\n const { path, query, header } = getOperationParameters(node)\n const hasRequiredPath = path.some((param) => param.required)\n const hasRequiredQuery = query.some((param) => param.required)\n const hasRequiredHeader = header.some((param) => param.required)\n\n return {\n groups,\n hasRequiredPath,\n hasRequiredQuery,\n hasRequiredHeader,\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body,\n }\n}\n\nexport type RequestOptionsNameResolver = RequestConfigResolver & {\n response: {\n options(node: ast.OperationNode): string\n }\n}\n\n/**\n * Builds the grouped `{ path, query, body, headers }` parameter for a generated client\n * function, typed from the operation's `Options` (minus `url`). Only the groups the\n * operation actually has are destructured. The trailing `config` parameter carries the\n * runtime `RequestConfig` overrides plus `client`.\n */\nexport function buildRequestParamsSignature(\n node: ast.OperationNode,\n resolver: RequestOptionsNameResolver,\n options: { isConfigurable?: boolean } = {},\n): { signature: string; groups: RequestGroups } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = (['path', 'query', 'body', 'headers'] as const).filter((key) => groups[key])\n\n const firstParam = names.length > 0 ? `{ ${names.join(', ')} }: ${resolver.response.options(node)}${isOptional ? ' = {}' : ''}` : null\n const configParam = isConfigurable ? `config: ${buildRequestConfigType(node)} = {}` : null\n\n return {\n signature: [firstParam, configParam].filter(Boolean).join(', '),\n groups,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { link = 'pathTemplate', linkPosition = 'afterDeprecated', splitLines = false } = options\n const linkComment = getOperationLink(node, link)\n const comments =\n linkPosition === 'beforeDeprecated'\n ? [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, linkComment, node.deprecated && '@deprecated']\n : [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, node.deprecated && '@deprecated', linkComment]\n\n const filteredComments = comments.filter((comment): comment is string => Boolean(comment))\n\n if (!splitLines) {\n return filteredComments\n }\n\n return filteredComments.flatMap((text) => text.split(/\\r?\\n/).map((line) => line.trim())).filter((comment): comment is string => Boolean(comment))\n}\n\nconst operationParameterGroupsByNode = new WeakMap<ast.OperationNode, OperationParameterGroups>()\n\n/**\n * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each\n * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance\n * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the\n * same parameters once per plugin.\n */\nexport function getOperationParameters(node: ast.OperationNode): OperationParameterGroups {\n const cached = operationParameterGroupsByNode.get(node)\n if (cached) return cached\n\n // A malformed spec can declare the same name twice within one location. Both would resolve to the\n // same output property, so emitting both would yield an object type with a duplicate member,\n // which TypeScript rejects. Keep the first.\n const group = (location: ast.ParameterNode['in']) => {\n const seen = new Set<string>()\n\n return node.parameters.filter((param) => {\n if (param.in !== location || seen.has(param.name)) return false\n seen.add(param.name)\n return true\n })\n }\n\n const groups: OperationParameterGroups = { path: group('path'), query: group('query'), header: group('header'), cookie: group('cookie') }\n\n operationParameterGroupsByNode.set(node, groups)\n return groups\n}\n\n/**\n * Builds the combined `{ body, path, query, headers }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same\n * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod\n * printer to emit `z.object(…)` rather than a record.\n */\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver): ast.SchemaNode {\n const { path, query, header } = getOperationParameters(node)\n const hasBody = Boolean(node.requestBody?.content?.[0]?.schema)\n const createNever = () => ast.factory.createSchema({ type: 'never', primitive: undefined, optional: true })\n const groups = [\n { name: 'path', params: path, resolve: resolver.param.path },\n { name: 'query', params: query, resolve: resolver.param.query },\n { name: 'headers', params: header, resolve: resolver.param.headers },\n ] as const\n\n // NOTE(v5-stable): the fields were renamed from the legacy beta shape\n // (`data`/`pathParams`/`queryParams`/`headerParams`) to `body`/`path`/`query`/`headers` so the\n // type matches the runtime client. Drop this note once v5 leaves beta.\n return ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n deprecated: node.deprecated,\n properties: [\n ast.factory.createProperty({\n name: 'body',\n required: hasBody,\n schema: hasBody ? ast.factory.createSchema({ type: 'ref', name: resolver.response.body(node) }) : createNever(),\n }),\n ...groups.map(({ name, params, resolve }) => {\n const required = params.some((param) => param.required)\n\n return ast.factory.createProperty({\n name,\n required,\n schema:\n params.length > 0\n ? ast.factory.createSchema({ type: 'ref', name: resolve.call(resolver.param, node, params[0]!), optional: !required })\n : createNever(),\n })\n }),\n ],\n })\n}\n\n/**\n * The schema a status occupies in the `<Name>Responses` record. A status that documents several\n * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type\n * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the\n * query hooks and `result.data` use.\n */\nfunction buildResponseRecordEntry(node: ast.OperationNode, res: ast.ResponseNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n const statusName = resolver.response.status(node, res.statusCode)\n const variants = (res.content ?? []).filter((entry) => entry.schema)\n if (variants.length <= 1) {\n return ast.factory.createSchema({ type: 'ref', name: statusName })\n }\n\n return ast.factory.createSchema({\n type: 'union',\n members: resolveContentTypeVariants(variants, statusName).map((variant) =>\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [\n ast.factory.createProperty({\n name: 'contentType',\n required: true,\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema: ast.factory.createSchema({ type: 'ref', name: variant.name }),\n }),\n ],\n }),\n ),\n })\n}\n\n/**\n * Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved\n * `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s\n * inferred responses schema, so both emit the same shape from the same inputs.\n *\n * Always emits the keyed record, even when an operation declares no responses. An operation with no\n * responses renders as an empty object, which keeps every consumer's import (for example the axios\n * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.\n */\nexport function buildResponses(node: ast.OperationNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n return ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: node.responses.map((res) =>\n ast.factory.createProperty({\n name: String(res.statusCode),\n required: true,\n schema: buildResponseRecordEntry(node, res, resolver),\n }),\n ),\n })\n}\n\nexport function getStatusCodeNumber(statusCode: ast.StatusCode | number | string): number | null {\n const code = Number(statusCode)\n\n return Number.isNaN(code) ? null : code\n}\n\nexport function isSuccessStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 200 && code < 300\n}\n\nexport function isErrorStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 400\n}\n\nexport function getSuccessResponses<TResponse extends ResponseLike>(responses: ReadonlyArray<TResponse>): Array<TResponse> {\n return responses.filter((response) => isSuccessStatusCode(response.statusCode))\n}\n\nexport function getOperationSuccessResponses(node: ast.OperationNode): Array<ast.ResponseNode> {\n return getSuccessResponses(node.responses)\n}\n\nexport function getPrimarySuccessResponse(node: ast.OperationNode): ast.ResponseNode | null {\n return getOperationSuccessResponses(node)[0] ?? null\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\nconst typeNamesByResolver = new WeakMap<OperationTypeNameResolver, Map<string, string[]>>()\n\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const cacheKey = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n let byResolver = typeNamesByResolver.get(resolver)\n if (byResolver) {\n const cached = byResolver.get(cacheKey)\n if (cached) return cached\n } else {\n byResolver = new Map()\n typeNamesByResolver.set(resolver, byResolver)\n }\n\n const { path, query, header } = getOperationParameters(node)\n const responseStatusNames =\n options.responseStatusNames === 'error'\n ? resolveErrorNames(node, resolver)\n : options.responseStatusNames === false\n ? []\n : resolveStatusCodeNames(node, resolver)\n const exclude = new Set(options.exclude ?? [])\n const paramNames =\n options.includeParams === false\n ? []\n : [\n ...path.map((param) => resolver.param.path(node, param)),\n ...query.map((param) => resolver.param.query(node, param)),\n ...header.map((param) => resolver.param.headers(node, param)),\n ]\n const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n const result = names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n byResolver.set(cacheKey, result)\n return result\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = getStatusCodeNumber(response.statusCode)\n if (code === null) {\n continue\n }\n\n types.push([code, isSuccessStatusCode(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction convert(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n// Plugins resolve the same names over and over (about 47,000 calls for a 1,000 schema spec), so\n// results are kept. Clearing at the limit bounds the memory a long-running process spends on it.\nconst cache = new Map<string, string>()\nconst cacheLimit = 10_000\n\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n const key = `${pascal ? 'P' : 'c'}${text}`\n const cached = cache.get(key)\n if (cached !== undefined) return cached\n\n const result = convert(text, pascal)\n if (cache.size >= cacheLimit) cache.clear()\n cache.set(key, result)\n return result\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n\n/**\n * Uppercases only the first character of `text`, leaving the rest untouched.\n * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.\n *\n * @example\n * `capitalize('getPetById') // 'GetPetById'`\n */\nexport function capitalize(text: string): string {\n return `${text.charAt(0).toUpperCase()}${text.slice(1)}`\n}\n\n/**\n * Converts `text` to snake_case.\n *\n * @example From camelCase\n * `snakeCase('helloWorld') // 'hello_world'`\n *\n * @example From mixed separators\n * `snakeCase('Hello-World') // 'hello_world'`\n */\nexport function snakeCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n const processed = `${prefix} ${text} ${suffix}`.trim()\n return processed\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/[\\s\\-.]+/g, '_')\n .replace(/[^a-zA-Z0-9_]/g, '')\n .toLowerCase()\n .split('_')\n .filter(Boolean)\n .join('_')\n}\n\n/**\n * Converts `text` to SCREAMING_SNAKE_CASE.\n *\n * @example From camelCase\n * `screamingSnakeCase('helloWorld') // 'HELLO_WORLD'`\n */\nexport function screamingSnakeCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return snakeCase(text, { prefix, suffix }).toUpperCase()\n}\n","import { camelCase } from '@internals/utils'\nimport type { Group } from 'kubb/kit'\n\n/**\n * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the\n * shared default naming so every plugin groups output consistently:\n *\n * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).\n * - other groups use the camelCased group (`pet store` → `petStore`).\n *\n * A user-provided `group.name` always wins over the default namer, so callers stay in\n * control of their output folders. Returns `null` when grouping is disabled, matching the\n * per-plugin convention.\n *\n * @param group - The user-supplied group option, or `undefined` to disable grouping.\n *\n * @example\n * ```ts\n * createGroupConfig(group) // shared across every plugin\n * ```\n */\nexport function createGroupConfig(group: Group | undefined): Group | null {\n if (!group) {\n return null\n }\n\n const defaultName = (ctx: { group: string }): string => {\n if (group.type === 'path') {\n return `${ctx.group.split('/')[1]}`\n }\n\n return camelCase(ctx.group)\n }\n\n return {\n ...group,\n name: group.name ? group.name : defaultName,\n } satisfies Group\n}\n","","","import { camelCase } from '@internals/utils'\nimport { ast, defineGenerator } from 'kubb/kit'\nimport type { PluginMsw } from '../types'\n\n/**\n * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a\n * `handlers.ts` file that re-exports every generated handler in operation\n * order, ready to spread into `setupServer(...handlers)` or\n * `setupWorker(...handlers)`.\n */\nexport const handlersGenerator = defineGenerator<PluginMsw>({\n name: 'plugin-msw',\n operations(nodes, ctx) {\n const { resolver, config, root } = ctx\n const { output, group } = ctx.options\n\n const handlersName = resolver.handler.listName()\n const createHandlersName = camelCase(handlersName, { prefix: 'create' })\n const file = resolver.file({ name: handlersName, extname: '.ts', root, output, group: group ?? undefined })\n\n const handlerNames = nodes.map((node) => resolver.handler.name(node))\n\n const imports = nodes.map((node, index) => {\n const operationName = handlerNames[index]!\n const operationFile = resolver.file({\n name: resolver.name(node.operationId),\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output,\n group: group ?? undefined,\n })\n return ast.factory.createImport({ name: [operationName], root: file.path, path: operationFile.path })\n })\n\n const handlers = handlerNames.map((name) => `${name}(undefined, options)`)\n\n return [\n ast.factory.createFile({\n baseName: file.baseName,\n path: file.path,\n meta: file.meta,\n banner: resolver.default.banner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n footer: resolver.default.footer(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n imports,\n sources: [\n ast.factory.createSource({\n name: createHandlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [\n ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {\n return [${handlers.join(', ')}] as const\n}`),\n ],\n }),\n ast.factory.createSource({\n name: handlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)],\n }),\n ],\n }),\n ]\n },\n})\n","import type { ast } from 'kubb/kit'\n\n/**\n * Returns the base URL as source code. A `${...}` value stays a template literal.\n */\nexport function resolveDefaultBaseURL(baseURL: string | null | undefined): string {\n if (!baseURL) {\n return \"''\"\n }\n if (baseURL.includes('${')) {\n return `\\`${baseURL.replaceAll('`', '\\\\`')}\\``\n }\n return JSON.stringify(baseURL)\n}\n\n/**\n * Gets the content type from a response, defaulting to 'application/json' if a schema exists.\n */\nexport function getContentType(response: ast.ResponseNode | null | undefined): string | null {\n if (!hasResponseSchema(response)) {\n return null\n }\n\n return getResponseContentType(response) ?? 'application/json'\n}\n\n/**\n * Determines if a response has a usable body schema.\n */\nexport function hasResponseSchema(response: ast.ResponseNode | null | undefined): boolean {\n const schema = response?.content?.find((entry) => entry.schema)?.schema\n return !!schema && schema.type !== 'void' && schema.type !== 'any' && schema.type !== 'unknown'\n}\n\n/**\n * Picks the content type used for the mocked response header. When a response declares multiple\n * content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.\n */\nfunction getResponseContentType(response: ast.ResponseNode | null | undefined): string | null {\n const contents = response?.content ?? []\n const jsonEntry = contents.find((entry) => {\n const baseType = entry.contentType?.split(';')[0]?.trim().toLowerCase()\n return baseType === 'application/json' || baseType?.endsWith('+json')\n })\n const value = (jsonEntry ?? contents[0])?.contentType\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no\n * `http.query`, so a QUERY operation maps to `http.all` and its handler checks the method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return node.method === 'QUERY' ? 'all' : node.method.toLowerCase()\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no content type.\n */\nfunction headersBlock(contentType: string | null): string {\n return contentType ? `\\n headers: {\\n 'Content-Type': '${contentType}'\\n },` : ''\n}\n\n/**\n * The `new HttpResponse(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new HttpResponse(${body}, {\n status: ${status},${headersBlock(contentType)}\n })`\n}\n","import { getPrimarySuccessResponse } from '@internals/shared'\nimport type { ast } from 'kubb/kit'\nimport { Url } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseLiteral } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so the QUERY handler lets the others pass through.\n const methodGuard = node.method === 'QUERY' ? `if (info.request.method !== 'QUERY') return\\n\\n ` : ''\n const successResponse = getPrimarySuccessResponse(node)\n const statusCode = successResponse ? Number(successResponse.statusCode) : 200\n const contentType = getContentType(successResponse)\n const url = Url.toPath(node.path)\n\n const responseHasSchema = hasResponseSchema(successResponse)\n const dataType = responseHasSchema ? typeName : 'string | number | boolean | null | object'\n const paramType = fakerName ? typeName : dataType\n\n const callbackType = requestTypeName\n ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}>`\n : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`\n\n const params = `data?: ${paramType} | ${callbackType}, options?: { baseURL?: string }`\n\n const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`\n\n // Escape colons that do not start a param, such as the second one in `/pet/:petId:search`.\n const urlLiteral = `\\`\\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, '$1\\\\\\\\:')}\\``\n const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : 'JSON.stringify(data)'\n\n return (\n <File.Source name={name} isIndexable isExportable>\n <Function name={name} export params={params}>\n {`return ${httpCall}(${urlLiteral}, function handler(info) {\n ${methodGuard}if(typeof data === 'function') return data(info)\n\n return ${responseLiteral(responseBody, statusCode, contentType)}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import type { ast } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, hasResponseSchema, responseLiteral } from '../utils.ts'\n\ntype Props = {\n typeName: string\n name: string\n response: ast.ResponseNode\n}\n\nexport function Response({ name, typeName, response }: Props): KubbReactNode {\n const statusCode = Number(response.statusCode)\n const contentType = getContentType(response)\n\n const params = `data${hasResponseSchema(response) ? '' : '?'}: ${typeName}`\n\n const responseName = `${name}Response${statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n return ${responseLiteral('JSON.stringify(data)', statusCode, contentType)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getOperationSuccessResponses, resolveDependencyOperationFile, resolveResponseTypes } from '@internals/shared'\nimport { ast, defineGenerator } from 'kubb/kit'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { enumTypeName, pluginTsName } from '@kubb/plugin-ts'\nimport { File, jsxRenderer } from 'kubb/jsx'\nimport { Mock } from '../components/Mock.tsx'\nimport { Response } from '../components/Response.tsx'\nimport type { PluginMsw } from '../types'\nimport { hasResponseSchema } from '../utils.ts'\n\n/**\n * Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler\n * per OpenAPI operation. With `parser: 'faker'` the handler returns a value\n * from `@kubb/plugin-faker`; with `parser: 'data'` it returns a typed empty\n * payload for tests to fill in.\n */\nexport const mswGenerator = defineGenerator<PluginMsw>({\n name: 'msw',\n renderer: jsxRenderer,\n operation(node, ctx) {\n if (!ast.isHttpOperationNode(node)) return null\n const { driver, resolver, config, root } = ctx\n const { output, parser, baseURL, group } = ctx.options\n\n const fileName = resolver.name(node.operationId)\n const mock = {\n name: resolver.handler.name(node),\n file: resolver.file({ name: fileName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path, root, output, group: group ?? undefined }),\n }\n\n const fakerPlugin = parser === 'faker' ? ctx.requirePlugin(pluginFakerName) : null\n const fakerResolver = fakerPlugin ? driver.getResolver(pluginFakerName) : null\n const faker =\n fakerPlugin && fakerResolver\n ? {\n name: fakerResolver.response.response(node),\n file: fakerResolver.file({\n name: node.operationId,\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output: fakerPlugin.options?.output ?? output,\n group: fakerPlugin.options?.group ?? undefined,\n }),\n }\n : null\n\n const pluginTs = ctx.requirePlugin(pluginTsName)\n const tsResolver = driver.getResolver(pluginTsName)\n\n const type = {\n file: resolveDependencyOperationFile({\n cache: ctx.cache,\n node,\n resolver: tsResolver,\n root,\n output: pluginTs.options?.output ?? output,\n group: pluginTs.options?.group,\n }),\n responseName: tsResolver.response.response(node),\n }\n\n const successResponses = getOperationSuccessResponses(node)\n const referencedNames = node.responses.flatMap((response) =>\n (response.content ?? []).flatMap((entry) => (entry.schema ? ast.collectImportedRefNames(entry.schema) : [])),\n )\n const enumOptions = pluginTs.options?.enum\n const enumNames = new Set(ctx.meta.enumNames)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n const types = resolveResponseTypes(node, tsResolver).map(([code, typeName]) => {\n const successResponse = successResponses.find((response) => response.statusCode === String(code))\n return [code, hasResponseNameCollision && successResponse ? tsResolver.response.status(node, successResponse.statusCode) : typeName] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName).filter((name) => !hasResponseNameCollision || name !== type.responseName)\n const mockResponseName =\n hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName\n const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response))\n\n const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.response.body(node) : null\n\n return (\n <File\n baseName={mock.file.baseName}\n path={mock.file.path}\n meta={mock.file.meta}\n banner={resolver.default.banner(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n footer={resolver.default.footer(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n >\n <File.Import name={['http', 'HttpResponse']} path=\"msw/http\" />\n <File.Import name={['HttpResponseResolver']} isTypeOnly path=\"msw/http\" />\n <File.Import\n name={Array.from(new Set([...(hasResponseNameCollision ? [] : [type.responseName]), ...responseTypeNames, ...(requestName ? [requestName] : [])]))}\n path={type.file.path}\n root={mock.file.path}\n isTypeOnly\n />\n {faker && hasSuccessSchema && <File.Import name={[faker.name]} root={mock.file.path} path={faker.file.path} />}\n\n {types\n .filter(([code]) => code !== 'default')\n .map(([code, typeName]) => {\n const response = node.responses.find((item) => item.statusCode === String(code))\n if (!response) return null\n return <Response key={typeName} typeName={typeName} response={response} name={mock.name} />\n })}\n\n <Mock\n name={mock.name}\n typeName={mockResponseName}\n requestTypeName={requestName}\n fakerName={faker && hasSuccessSchema ? faker.name : undefined}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { camelCase } from '@internals/utils'\nimport { createResolver } from 'kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return camelCase(name, { suffix: 'handler' })\n },\n handler: {\n name(node) {\n return this.name(node.operationId)\n },\n listName() {\n return 'handlers'\n },\n },\n})\n","import { createGroupConfig } from '@internals/shared'\nimport { addGenerator, defineExtension, Resolver, setMacros, setResolver } from 'kubb/kit'\nimport { compatibility } from '../kubb-extension.json'\nimport { version } from '../package.json'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { pluginTsName } from '@kubb/plugin-ts'\nimport { handlersGenerator } from './generators/handlersGenerator.ts'\nimport { mswGenerator } from './generators/mswGenerator.tsx'\nimport { resolverMsw } from './resolvers/resolverMsw.ts'\nimport type { PluginMsw, ResolverMsw } from './types.ts'\n\n/**\n * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and\n * cross-plugin dependency references.\n */\nexport const pluginMswName = 'plugin-msw' satisfies PluginMsw['name']\n\n/**\n * Generates MSW request handlers from an OpenAPI spec. Drop them into your\n * test setup or service worker to mock the API end-to-end. Request path,\n * method, status, and response body all stay in sync with the spec. Combine\n * with `@kubb/plugin-faker` (via `parser: 'faker'`) to seed handlers with\n * realistic data.\n *\n * @example\n * ```ts\n * import { defineConfig } from 'kubb/config'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginMsw } from '@kubb/plugin-msw'\n *\n * export default defineConfig({\n * input: './petStore.yaml',\n * output: { path: './src/gen' },\n * plugins: [\n * pluginTs(),\n * pluginMsw({\n * output: { path: './handlers' },\n * handlers: true,\n * }),\n * ],\n * })\n * ```\n */\nexport const pluginMsw = defineExtension<PluginMsw>({\n kind: 'plugin',\n meta: { name: pluginMswName, version, compatibility },\n dependencies: (options) => ({ [pluginTsName]: {}, ...(options.parser === 'faker' ? { [pluginFakerName]: {} } : {}) }),\n setup(options) {\n const {\n output = { path: 'handlers', barrel: { type: 'named' } },\n group,\n exclude = [],\n include,\n override = [],\n handlers = false,\n parser = 'data',\n baseURL,\n resolver: userResolver,\n macros: userMacros,\n } = options\n\n const resolver = userResolver ? Resolver.merge<ResolverMsw>(resolverMsw, userResolver) : resolverMsw\n\n setResolver(resolver)\n if (userMacros?.length) {\n setMacros(userMacros)\n }\n\n addGenerator(mswGenerator)\n if (handlers) {\n addGenerator(handlersGenerator)\n }\n\n return {\n output,\n parser,\n baseURL,\n group: createGroupConfig(group),\n exclude,\n include,\n override,\n handlers,\n }\n },\n})\n\nexport default pluginMsw\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../internals/shared/src/operation.ts","../../../internals/shared/src/resolver.ts","../../../internals/shared/src/group.ts","../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../src/utils.ts","../src/components/Mock.tsx","../src/components/Response.tsx","../src/generators/mswGenerator.tsx","../src/resolvers/resolverMsw.ts","../src/plugin.ts"],"sourcesContent":["import { ast, type Group, type NodeCache, type Output, type Resolver, type ResolverFileParams, Url } from 'kubb/kit'\n\n/**\n * Builds the `ResolverFileParams` every operation generator passes to\n * `resolver.file`: a file named `name`, tagged by the operation's first\n * tag (or `'default'`), at the operation's path. Centralizes the entry object\n * that was repeated at dozens of call sites across the client and query plugins.\n *\n * @example\n * ```ts\n * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })\n * ```\n */\nexport function operationFileEntry(node: ast.OperationNode, name: string, extname: ResolverFileParams['extname'] = '.ts'): ResolverFileParams {\n return {\n name,\n extname,\n tag: node.tags[0] ?? 'default',\n path: node.path,\n }\n}\n\n/**\n * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the\n * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the\n * same dependency for the same operation in one pass (a query plugin's several hook generators, the\n * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.\n *\n * @example Cache `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveDependencyOperationFile(options: {\n cache: NodeCache\n node: ast.OperationNode\n resolver: Pick<Resolver, 'file' | 'pluginName'>\n root: string\n output: Output\n group?: Group | null\n}): ast.FileNode {\n const { cache, node, resolver, root, output, group } = options\n\n return cache.ensureItem(`${resolver.pluginName}:operationFile`, () =>\n resolver.file({ ...operationFileEntry(node, node.operationId), root, output, group: group ?? undefined }),\n )\n}\n\nexport type ContentTypeInfo = {\n contentTypes: string[]\n isMultipleContentTypes: boolean\n contentTypeUnion: string\n defaultContentType: string\n hasFormData: boolean\n}\n\nexport type RequestConfigResolver = {\n response: {\n body(node: ast.OperationNode): string\n }\n}\n\nexport type ResponseStatusNameResolver = {\n response: {\n status(node: ast.OperationNode, statusCode: ast.StatusCode): string\n }\n}\n\nexport type ResponseNameResolver = ResponseStatusNameResolver & {\n response: {\n response(node: ast.OperationNode): string\n }\n}\n\nexport type OperationTypeNameResolver = RequestConfigResolver &\n ResponseNameResolver & {\n param: {\n path(node: ast.OperationNode, param: ast.ParameterNode): string\n query(node: ast.OperationNode, param: ast.ParameterNode): string\n headers(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type OperationCommentLink = 'pathTemplate' | 'urlPath' | false | ((node: ast.OperationNode) => string | undefined)\n\nexport type BuildOperationCommentsOptions = {\n link?: OperationCommentLink\n linkPosition?: 'beforeDeprecated' | 'afterDeprecated'\n splitLines?: boolean\n}\n\ntype ResponseLike = {\n statusCode: ast.StatusCode | number | string\n}\n\nexport type OperationParameterGroups = Record<ast.ParameterNode['in'], Array<ast.ParameterNode>>\n\nexport type ResolveOperationTypeNameOptions = {\n responseStatusNames?: boolean | 'error'\n exclude?: ReadonlyArray<string | undefined>\n order?: 'params-first' | 'body-response-first'\n /**\n * Include the individual `Path`/`Query`/`Headers` group type names. Set to `false` for clients\n * that reference the grouped `Options` type instead of the per-group types.\n */\n includeParams?: boolean\n}\n\nfunction getOperationLink(node: ast.OperationNode, link: OperationCommentLink): string | null {\n if (!link) {\n return null\n }\n\n if (typeof link === 'function') {\n return link(node) ?? null\n }\n\n return node.path ? `{@link ${Url.toPath(node.path)}}` : null\n}\n\n/**\n * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several\n * are present and the union, default, and form-data flags the client uses to pick one.\n */\nfunction buildContentTypeInfo(contentTypes: string[]): ContentTypeInfo {\n const isMultipleContentTypes = contentTypes.length > 1\n\n return {\n contentTypes,\n isMultipleContentTypes,\n contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(' | ') : '',\n defaultContentType: contentTypes[0] ?? 'application/json',\n hasFormData: contentTypes.some((ct) => ct === 'multipart/form-data'),\n }\n}\n\nexport function getContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? [])\n}\n\n/**\n * The request-body counterpart for the primary success response: the content types it documents and\n * whether several are present, so the client can let a caller pick which one to accept.\n */\nexport function getResponseContentTypeInfo(node: ast.OperationNode): ContentTypeInfo {\n return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\nexport type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'\n\n/**\n * Reads the single base content type of an operation's primary success response, lowercased and\n * stripped of any `; charset=...` suffix. Returns `undefined` when the response declares zero or\n * more than one content type, since neither case has a single type to act on.\n */\nfunction getPrimarySuccessContentType(node: ast.OperationNode): string | undefined {\n const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? []\n if (contentTypes.length !== 1) return undefined\n return contentTypes[0]!.split(';')[0]!.trim().toLowerCase()\n}\n\n/**\n * Whether an operation streams its primary success response as Server-Sent Events\n * (`text/event-stream`). The client generator uses this to return a typed event stream instead of a\n * one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getPrimarySuccessContentType(node) === 'text/event-stream'\n}\n\n/**\n * Derives the default `responseType` for an operation from its primary success response.\n *\n * Returns a value only when that response declares a single non-JSON content type. `text/event-stream`\n * and other binary types (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`,\n * `video/*`) map to a stream or `'blob'`, and other `text/*` maps to `'text'`. Otherwise `undefined`,\n * leaving the runtime client's `Content-Type` auto-detection in charge.\n */\nexport function getResponseType(node: ast.OperationNode): ResponseType | undefined {\n const baseType = getPrimarySuccessContentType(node)\n if (!baseType) return undefined\n\n if (baseType === 'application/json' || baseType.endsWith('+json') || baseType === 'text/json') return undefined\n if (baseType === 'text/event-stream') return 'stream'\n if (baseType.startsWith('text/')) return 'text'\n if (baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)) return 'blob'\n return undefined\n}\n\n/**\n * Maps a content type to the PascalCase suffix used to name per-content-type variants\n * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).\n */\nfunction getContentTypeSuffix(contentType: string): string {\n const baseType = contentType.split(';')[0]!.trim()\n if (baseType === 'application/json') return 'Json'\n if (baseType === 'multipart/form-data') return 'FormData'\n if (baseType === 'application/x-www-form-urlencoded') return 'FormUrlEncoded'\n const subtype = baseType.split('/').pop() ?? baseType\n const parts = subtype.split(/[^a-zA-Z0-9]+/).filter(Boolean)\n if (parts.length === 0) return 'Unknown'\n return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('')\n}\n\n/**\n * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last\n * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).\n */\nexport function getPerContentTypeName(baseName: string, suffix: string): string {\n if (baseName.endsWith('Data')) {\n return suffix.endsWith('Data') ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`\n }\n return baseName + suffix\n}\n\nexport type ContentVariantInput = { contentType: string; schema?: ast.SchemaNode | null; keysToOmit?: Array<string> | null }\nexport type ContentVariant = { name: string; suffix: string; schema: ast.SchemaNode; keysToOmit?: Array<string> | null; contentType: string }\n\n/**\n * Resolves per-content-type variant names for a set of content entries, deduplicating suffix\n * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is\n * the final (possibly counter-augmented) value, so callers can derive parallel names in another\n * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).\n */\nexport function resolveContentTypeVariants(entries: Array<ContentVariantInput>, baseName: string): Array<ContentVariant> {\n const usedNames = new Set<string>()\n return entries\n .filter((entry) => entry.schema)\n .map((entry) => {\n const baseSuffix = getContentTypeSuffix(entry.contentType)\n let suffix = baseSuffix\n let name = getPerContentTypeName(baseName, suffix)\n let counter = 2\n while (usedNames.has(name)) {\n suffix = `${baseSuffix}${counter++}`\n name = getPerContentTypeName(baseName, suffix)\n }\n usedNames.add(name)\n return { name, suffix, schema: entry.schema!, keysToOmit: entry.keysToOmit, contentType: entry.contentType }\n })\n}\n\n// The request groups come from the grouped params, so `config` drops the data-shape keys to stay\n// assignable to `Options`, which omits them from `RequestConfig`.\nconst requestConfigBaseType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`\n\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n\n // Only the ambiguous side is offered: a single-type side has nothing to pick, so it stays baked in\n // the generated call.\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n return members.length ? `${requestConfigBaseType} & { contentType?: { ${members.join('; ')} } }` : requestConfigBaseType\n}\n\n/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...). Unlike {@link buildRequestConfigType}, it never adds a\n * `contentType?:` member: query hooks wrap GET operations, which carry no request body to select a\n * content type for.\n */\nexport function buildClientOptionType(): string {\n return requestConfigBaseType\n}\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n}\n\n/**\n * Which of the grouped request options an operation carries.\n */\nexport function getRequestGroups(node: ast.OperationNode): RequestGroups {\n const { path, query, header } = getOperationParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(node.requestBody?.content?.[0]?.schema),\n headers: header.length > 0,\n }\n}\n\nexport type RequestGroupOptionality = {\n groups: RequestGroups\n hasRequiredPath: boolean\n hasRequiredQuery: boolean\n hasRequiredHeader: boolean\n /**\n * Whether the grouped request parameter can default to `{}`. True only when no group carries a\n * required member, so every member is safe to omit.\n */\n isOptional: boolean\n}\n\n/**\n * Resolves which grouped request options an operation carries together with whether each group\n * holds a required member. The grouped parameter stays optional only when nothing inside it is\n * required, matching the generated `RequestConfig` type.\n */\nexport function getRequestGroupOptionality(node: ast.OperationNode): RequestGroupOptionality {\n const groups = getRequestGroups(node)\n const { path, query, header } = getOperationParameters(node)\n const hasRequiredPath = path.some((param) => param.required)\n const hasRequiredQuery = query.some((param) => param.required)\n const hasRequiredHeader = header.some((param) => param.required)\n\n return {\n groups,\n hasRequiredPath,\n hasRequiredQuery,\n hasRequiredHeader,\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body,\n }\n}\n\nexport type RequestOptionsNameResolver = RequestConfigResolver & {\n response: {\n options(node: ast.OperationNode): string\n }\n}\n\n/**\n * Builds the grouped `{ path, query, body, headers }` parameter for a generated client\n * function, typed from the operation's `Options` (minus `url`). Only the groups the\n * operation actually has are destructured. The trailing `config` parameter carries the\n * runtime `RequestConfig` overrides plus `client`.\n */\nexport function buildRequestParamsSignature(\n node: ast.OperationNode,\n resolver: RequestOptionsNameResolver,\n options: { isConfigurable?: boolean } = {},\n): { signature: string; groups: RequestGroups } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = (['path', 'query', 'body', 'headers'] as const).filter((key) => groups[key])\n\n // These callers never carry the `contentType` that several media types require.\n const optionsType = hasRequestContentTypeChoice(node)\n ? `Pick<${resolver.response.options(node)}, ${names.map((name) => `'${name}'`).join(' | ')}>`\n : resolver.response.options(node)\n const firstParam = names.length > 0 ? `{ ${names.join(', ')} }: ${optionsType}${isOptional ? ' = {}' : ''}` : null\n const configParam = isConfigurable ? `config: ${buildRequestConfigType(node)} = {}` : null\n\n return {\n signature: [firstParam, configParam].filter(Boolean).join(', '),\n groups,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { link = 'pathTemplate', linkPosition = 'afterDeprecated', splitLines = false } = options\n const linkComment = getOperationLink(node, link)\n const comments =\n linkPosition === 'beforeDeprecated'\n ? [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, linkComment, node.deprecated && '@deprecated']\n : [node.description && `@description ${node.description}`, node.summary && `@summary ${node.summary}`, node.deprecated && '@deprecated', linkComment]\n\n const filteredComments = comments.filter((comment): comment is string => Boolean(comment))\n\n if (!splitLines) {\n return filteredComments\n }\n\n return filteredComments.flatMap((text) => text.split(/\\r?\\n/).map((line) => line.trim())).filter((comment): comment is string => Boolean(comment))\n}\n\nconst operationParameterGroupsByNode = new WeakMap<ast.OperationNode, OperationParameterGroups>()\n\n/**\n * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each\n * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance\n * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the\n * same parameters once per plugin.\n */\nexport function getOperationParameters(node: ast.OperationNode): OperationParameterGroups {\n const cached = operationParameterGroupsByNode.get(node)\n if (cached) return cached\n\n // A malformed spec can declare the same name twice within one location. Both would resolve to the\n // same output property, so emitting both would yield an object type with a duplicate member,\n // which TypeScript rejects. Keep the first.\n const group = (location: ast.ParameterNode['in']) => {\n const seen = new Set<string>()\n\n return node.parameters.filter((param) => {\n if (param.in !== location || seen.has(param.name)) return false\n seen.add(param.name)\n return true\n })\n }\n\n const groups: OperationParameterGroups = { path: group('path'), query: group('query'), header: group('header'), cookie: group('cookie') }\n\n operationParameterGroupsByNode.set(node, groups)\n return groups\n}\n\n/**\n * Builds the combined `{ body, path, query, headers }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same\n * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod\n * printer to emit `z.object(…)` rather than a record.\n */\nexport function hasRequestContentTypeChoice(node: ast.OperationNode): boolean {\n return (node.requestBody?.content ?? []).filter((entry) => entry.schema).length > 1\n}\n\n/**\n * Casts a call argument to the client's parameter type; a `'plain'` client is non-generic.\n */\nexport function castToClientOptions(callArg: string, clientName: string, returnType: 'full' | 'data' | 'plain' = 'full'): string {\n return `${callArg} as Parameters<typeof ${clientName}${returnType === 'plain' ? '' : '<true>'}>[0]`\n}\n\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver, { pairContentTypes = false } = {}): ast.SchemaNode {\n const { path, query, header } = getOperationParameters(node)\n const hasBody = Boolean(node.requestBody?.content?.[0]?.schema)\n const createNever = () => ast.factory.createSchema({ type: 'never', primitive: undefined, optional: true })\n const groups = [\n { name: 'path', params: path, resolve: resolver.param.path },\n { name: 'query', params: query, resolve: resolver.param.query },\n { name: 'headers', params: header, resolve: resolver.param.headers },\n ] as const\n\n const createOptions = (body: ast.SchemaNode, extra: Array<ast.PropertyNode> = []) =>\n // NOTE(v5-stable): the fields were renamed from the legacy beta shape\n // (`data`/`pathParams`/`queryParams`/`headerParams`) to `body`/`path`/`query`/`headers` so the\n // type matches the runtime client. Drop this note once v5 leaves beta.\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n deprecated: node.deprecated,\n properties: [\n ast.factory.createProperty({ name: 'body', required: hasBody, schema: body }),\n ...groups.map(({ name, params, resolve }) => {\n const required = params.some((param) => param.required)\n\n return ast.factory.createProperty({\n name,\n required,\n schema:\n params.length > 0\n ? ast.factory.createSchema({ type: 'ref', name: resolve.call(resolver.param, node, params[0]!), optional: !required })\n : createNever(),\n })\n }),\n ...extra,\n ],\n })\n\n const variants =\n pairContentTypes && hasRequestContentTypeChoice(node) ? resolveContentTypeVariants(node.requestBody?.content ?? [], resolver.response.body(node)) : []\n if (variants.length < 2) {\n return createOptions(hasBody ? ast.factory.createSchema({ type: 'ref', name: resolver.response.body(node) }) : createNever())\n }\n\n // The first media type is the runtime default, so only the others must pass `contentType`.\n return ast.factory.createSchema({\n type: 'union',\n members: variants.map((variant, index) =>\n createOptions(ast.factory.createSchema({ type: 'ref', name: variant.name }), [\n ast.factory.createProperty({\n name: 'contentType',\n required: index > 0,\n schema: ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [\n ast.factory.createProperty({\n name: 'request',\n required: true,\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] }),\n }),\n ],\n }),\n }),\n ]),\n ),\n })\n}\n\n/**\n * The schema a status occupies in the `<Name>Responses` record. A status that documents several\n * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type\n * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the\n * query hooks and `result.data` use.\n */\nfunction buildResponseRecordEntry(node: ast.OperationNode, res: ast.ResponseNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n const statusName = resolver.response.status(node, res.statusCode)\n const variants = (res.content ?? []).filter((entry) => entry.schema)\n if (variants.length <= 1) {\n return ast.factory.createSchema({ type: 'ref', name: statusName })\n }\n\n return ast.factory.createSchema({\n type: 'union',\n members: resolveContentTypeVariants(variants, statusName).map((variant) =>\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [\n ast.factory.createProperty({\n name: 'contentType',\n required: true,\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema: ast.factory.createSchema({ type: 'ref', name: variant.name }),\n }),\n ],\n }),\n ),\n })\n}\n\n/**\n * Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved\n * `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s\n * inferred responses schema, so both emit the same shape from the same inputs.\n *\n * Always emits the keyed record, even when an operation declares no responses. An operation with no\n * responses renders as an empty object, which keeps every consumer's import (for example the axios\n * SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.\n */\nexport function buildResponses(node: ast.OperationNode, resolver: ResponseStatusNameResolver): ast.SchemaNode {\n return ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: node.responses.map((res) =>\n ast.factory.createProperty({\n name: String(res.statusCode),\n required: true,\n schema: buildResponseRecordEntry(node, res, resolver),\n }),\n ),\n })\n}\n\nexport function getStatusCodeNumber(statusCode: ast.StatusCode | number | string): number | null {\n const code = Number(statusCode)\n\n return Number.isNaN(code) ? null : code\n}\n\nexport function isSuccessStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 200 && code < 300\n}\n\nexport function isErrorStatusCode(statusCode: ast.StatusCode | number | string): boolean {\n const code = getStatusCodeNumber(statusCode)\n\n return code !== null && code >= 400\n}\n\nexport function getSuccessResponses<TResponse extends ResponseLike>(responses: ReadonlyArray<TResponse>): Array<TResponse> {\n return responses.filter((response) => isSuccessStatusCode(response.statusCode))\n}\n\nexport function getOperationSuccessResponses(node: ast.OperationNode): Array<ast.ResponseNode> {\n return getSuccessResponses(node.responses)\n}\n\nexport function getPrimarySuccessResponse(node: ast.OperationNode): ast.ResponseNode | null {\n return getOperationSuccessResponses(node)[0] ?? null\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\n/**\n * Lists the type names an operation's types file declares, computed once per resolver and `options` so every\n * dependent reads the same list.\n */\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver & Pick<Resolver, 'memo'>,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const key = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n return resolver.memo(`operationTypeNames:${key}`, () => {\n const { path, query, header } = getOperationParameters(node)\n const responseStatusNames =\n options.responseStatusNames === 'error'\n ? resolveErrorNames(node, resolver)\n : options.responseStatusNames === false\n ? []\n : resolveStatusCodeNames(node, resolver)\n const exclude = new Set(options.exclude ?? [])\n const paramNames =\n options.includeParams === false\n ? []\n : [\n ...path.map((param) => resolver.param.path(node, param)),\n ...query.map((param) => resolver.param.query(node, param)),\n ...header.map((param) => resolver.param.headers(node, param)),\n ]\n const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n return names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n })\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = getStatusCodeNumber(response.statusCode)\n if (code === null) {\n continue\n }\n\n types.push([code, isSuccessStatusCode(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","import { ensureValidVarName, toFilePath } from '@internals/utils'\nimport { camelCase } from 'kubb/kit'\nimport type { ast, Resolver, ResolverFile } from 'kubb/kit'\n\n/**\n * The shared top-level `name` of the operation plugins, computed once per operation name and build.\n */\nexport function resolveOperationName(this: Resolver, name: string): string {\n return this.memo(`operation:${name}`, () => ensureValidVarName(camelCase(name)))\n}\n\n/**\n * Builds the allocation key for a declaration an operation owns. `scope` names which declaration\n * it is, so two of them never share an allocation even when a custom resolver gives both the same\n * name. Leave `scope` empty for the operation's own declaration.\n *\n * @example\n * ```ts\n * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })\n * ```\n */\nexport function operationKey(node: ast.OperationNode, ...scope: Array<string | number>): Array<string | undefined> {\n return ['operation', node.method, node.path, ...scope.map(String)]\n}\n\n/**\n * The `param` namespace shared by the schema-producing plugins (ts, zod, faker):\n * per-parameter names plus the grouped `Path`/`Query`/`Headers` names, all routed\n * through the plugin's top-level `name` casing.\n */\nexport type OperationParamResolver = {\n name(this: Resolver, node: ast.OperationNode, param: ast.ParameterNode): string\n path(this: Resolver, node: ast.OperationNode): string\n query(this: Resolver, node: ast.OperationNode): string\n headers(this: Resolver, node: ast.OperationNode): string\n}\n\n/**\n * The `response` namespace shared by the schema-producing plugins: per-status,\n * body, and combined response names, all routed through the plugin's top-level\n * `name` casing.\n */\nexport type OperationResponseResolver = {\n status(this: Resolver, node: ast.OperationNode, statusCode: ast.StatusCode): string\n body(this: Resolver, node: ast.OperationNode): string\n responses(this: Resolver, node: ast.OperationNode): string\n response(this: Resolver, node: ast.OperationNode): string\n}\n\n/**\n * Builds the shared `param` namespace. Spread the result into `createResolver`\n * and override individual methods next to it when a plugin deviates.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ param: createOperationParamResolver(), ... })\n * ```\n */\nexport function createOperationParamResolver(): OperationParamResolver {\n return {\n name(node, param) {\n const name = this.name(`${node.operationId} ${param.in} ${param.name}`)\n return this.resolveName({ key: operationKey(node, 'param', 'name', param.in, param.name), name })\n },\n path(node) {\n const name = this.name(`${node.operationId} Path`)\n return this.resolveName({ key: operationKey(node, 'param', 'path'), name })\n },\n query(node) {\n const name = this.name(`${node.operationId} Query`)\n return this.resolveName({ key: operationKey(node, 'param', 'query'), name })\n },\n headers(node) {\n const name = this.name(`${node.operationId} Headers`)\n return this.resolveName({ key: operationKey(node, 'param', 'headers'), name })\n },\n }\n}\n\n/**\n * Builds the shared `response` namespace. Spread the result into\n * `createResolver` and add plugin-specific methods (`options`, `error`) next\n * to it.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ response: { ...createOperationResponseResolver(), options(node) {...} }, ... })\n * ```\n */\nexport function createOperationResponseResolver(): OperationResponseResolver {\n return {\n status(node, statusCode) {\n const name = this.name(`${node.operationId} Status ${statusCode}`)\n return this.resolveName({ key: operationKey(node, 'response', 'status', statusCode), name })\n },\n body(node) {\n const name = this.name(`${node.operationId} Body`)\n return this.resolveName({ key: operationKey(node, 'response', 'body'), name })\n },\n responses(node) {\n const name = this.name(`${node.operationId} Responses`)\n return this.resolveName({ key: operationKey(node, 'response', 'responses'), name })\n },\n response(node) {\n const name = this.name(`${node.operationId} Response`)\n return this.resolveName({ key: operationKey(node, 'response', 'response'), name })\n },\n }\n}\n\n/**\n * Builds a resolver `file` override whose base name runs every path segment\n * through `toFilePath`, casing the final segment with `caseLast`.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })\n * ```\n */\nexport function createCasedFile(caseLast: (part: string) => string): ResolverFile {\n return {\n baseName({ name, extname }) {\n return this.memo(`file:${name}\\0${extname}`, (): ast.FileNode['baseName'] => `${toFilePath(name, caseLast)}${extname}`)\n },\n }\n}\n\ndeclare global {\n namespace Kubb {\n interface MemoRegistry {\n operation: string\n file: ast.FileNode['baseName']\n operationTypeNames: Array<string>\n }\n }\n}\n","import { camelCase, type Group } from 'kubb/kit'\n\n/**\n * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the\n * shared default naming so every plugin groups output consistently:\n *\n * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).\n * - other groups use the camelCased group (`pet store` → `petStore`).\n *\n * A user-provided `group.name` always wins over the default namer, so callers stay in\n * control of their output folders. Returns `null` when grouping is disabled, matching the\n * per-plugin convention.\n *\n * @param group - The user-supplied group option, or `undefined` to disable grouping.\n *\n * @example\n * ```ts\n * createGroupConfig(group) // shared across every plugin\n * ```\n */\nexport function createGroupConfig(group: Group | undefined): Group | null {\n if (!group) {\n return null\n }\n\n const defaultName = (ctx: { group: string }): string => {\n if (group.type === 'path') {\n return `${ctx.group.split('/')[1]}`\n }\n\n return camelCase(ctx.group)\n }\n\n return {\n ...group,\n name: group.name ? group.name : defaultName,\n } satisfies Group\n}\n","","","import { ast, camelCase, defineGenerator } from 'kubb/kit'\nimport type { PluginMsw } from '../types'\n\n/**\n * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a\n * `handlers.ts` file that re-exports every generated handler in operation\n * order, ready to spread into `setupServer(...handlers)` or\n * `setupWorker(...handlers)`.\n */\nexport const handlersGenerator = defineGenerator<PluginMsw>({\n name: 'plugin-msw',\n operations(nodes, ctx) {\n const { resolver, config, root } = ctx\n const { output, group } = ctx.options\n\n const handlersName = resolver.handler.listName()\n const createHandlersName = resolver.resolveName({ key: ['handlers', 'create'], name: camelCase(handlersName, { prefix: 'create' }) })\n const file = resolver.file({ name: handlersName, extname: '.ts', root, output, group: group ?? undefined })\n\n const handlerNames = nodes.map((node) => resolver.handler.name(node))\n\n const imports = nodes.map((node, index) => {\n const operationName = handlerNames[index]!\n const operationFile = resolver.file({\n name: resolver.name(node.operationId),\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output,\n group: group ?? undefined,\n })\n return ast.factory.createImport({ name: [operationName], root: file.path, path: operationFile.path })\n })\n\n const handlers = handlerNames.map((name) => `${name}(undefined, options)`)\n\n return [\n ast.factory.createFile({\n baseName: file.baseName,\n path: file.path,\n meta: file.meta,\n banner: resolver.default.banner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n footer: resolver.default.footer(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),\n imports,\n sources: [\n ast.factory.createSource({\n name: createHandlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [\n ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {\n return [${handlers.join(', ')}] as const\n}`),\n ],\n }),\n ast.factory.createSource({\n name: handlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)],\n }),\n ],\n }),\n ]\n },\n})\n","import type { ast } from 'kubb/kit'\n\n/**\n * Returns the base URL as source code. A `${...}` value stays a template literal.\n */\nexport function resolveDefaultBaseURL(baseURL: string | null | undefined): string {\n if (!baseURL) {\n return \"''\"\n }\n if (baseURL.includes('${')) {\n return `\\`${baseURL.replaceAll('`', '\\\\`')}\\``\n }\n return JSON.stringify(baseURL)\n}\n\n/**\n * Gets the content type from a response, defaulting to 'application/json' if a schema exists.\n */\nexport function getContentType(response: ast.ResponseNode | null | undefined): string | null {\n if (!hasResponseSchema(response)) {\n return null\n }\n\n return getResponseContentType(response) ?? 'application/json'\n}\n\n/**\n * Determines if a response has a usable body schema.\n */\nexport function hasResponseSchema(response: ast.ResponseNode | null | undefined): boolean {\n const schema = response?.content?.find((entry) => entry.schema)?.schema\n return !!schema && schema.type !== 'void' && schema.type !== 'any' && schema.type !== 'unknown'\n}\n\n/**\n * Picks the content type used for the mocked response header. When a response declares multiple\n * content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.\n */\nfunction getResponseContentType(response: ast.ResponseNode | null | undefined): string | null {\n const contents = response?.content ?? []\n const jsonEntry = contents.find((entry) => {\n const baseType = entry.contentType?.split(';')[0]?.trim().toLowerCase()\n return baseType === 'application/json' || baseType?.endsWith('+json')\n })\n const value = (jsonEntry ?? contents[0])?.contentType\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no\n * `http.query`, so a QUERY operation maps to `http.all` and its handler checks the method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return node.method === 'QUERY' ? 'all' : node.method.toLowerCase()\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no content type.\n */\nfunction headersBlock(contentType: string | null): string {\n return contentType ? `\\n headers: {\\n 'Content-Type': '${contentType}'\\n },` : ''\n}\n\n/**\n * The `new HttpResponse(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new HttpResponse(${body}, {\n status: ${status},${headersBlock(contentType)}\n })`\n}\n","import { getPrimarySuccessResponse } from '@internals/shared'\nimport type { ast } from 'kubb/kit'\nimport { Url } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseLiteral } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so the QUERY handler lets the others pass through.\n const methodGuard = node.method === 'QUERY' ? `if (info.request.method !== 'QUERY') return\\n\\n ` : ''\n const successResponse = getPrimarySuccessResponse(node)\n const statusCode = successResponse ? Number(successResponse.statusCode) : 200\n const contentType = getContentType(successResponse)\n const url = Url.toPath(node.path)\n\n const responseHasSchema = hasResponseSchema(successResponse)\n const dataType = responseHasSchema ? typeName : 'string | number | boolean | null | object'\n const paramType = fakerName ? typeName : dataType\n const requestBodyType = requestTypeName ? `${requestTypeName} extends DefaultBodyType ? ${requestTypeName} : DefaultBodyType` : null\n\n const callbackType = requestBodyType\n ? `HttpResponseResolver<Record<string, string>, ${requestBodyType}>`\n : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`\n\n const params = `data?: ${paramType} | ${callbackType}, options?: { baseURL?: string }`\n\n const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`\n\n // Escape colons that do not start a param, such as the second one in `/pet/:petId:search`.\n const urlLiteral = `\\`\\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, '$1\\\\\\\\:')}\\``\n const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : 'JSON.stringify(data)'\n\n return (\n <File.Source name={name} isIndexable isExportable>\n <Function name={name} export params={params}>\n {`return ${httpCall}(${urlLiteral}, function handler(info) {\n ${methodGuard}if(typeof data === 'function') return data(info)\n\n return ${responseLiteral(responseBody, statusCode, contentType)}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import type { ast } from 'kubb/kit'\nimport { File, Function } from 'kubb/jsx'\nimport type { KubbReactNode } from 'kubb/jsx'\nimport { getContentType, hasResponseSchema, responseLiteral } from '../utils.ts'\n\ntype Props = {\n typeName: string\n name: string\n response: ast.ResponseNode\n}\n\nexport function Response({ name, typeName, response }: Props): KubbReactNode {\n const statusCode = Number(response.statusCode)\n const contentType = getContentType(response)\n\n const params = `data${hasResponseSchema(response) ? '' : '?'}: ${typeName}`\n\n const responseName = `${name}Response${statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n return ${responseLiteral('JSON.stringify(data)', statusCode, contentType)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getOperationSuccessResponses, resolveDependencyOperationFile, resolveResponseTypes } from '@internals/shared'\nimport { ast, defineGenerator } from 'kubb/kit'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { enumTypeName, pluginTsName } from '@kubb/plugin-ts'\nimport { File, jsxRenderer } from 'kubb/jsx'\nimport { Mock } from '../components/Mock.tsx'\nimport { Response } from '../components/Response.tsx'\nimport type { PluginMsw } from '../types'\nimport { hasResponseSchema } from '../utils.ts'\n\n/**\n * Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler\n * per OpenAPI operation. With `parser: 'faker'` the handler returns a value\n * from `@kubb/plugin-faker`; with `parser: 'data'` it returns a typed empty\n * payload for tests to fill in.\n */\nexport const mswGenerator = defineGenerator<PluginMsw>({\n name: 'msw',\n renderer: jsxRenderer,\n operation(node, ctx) {\n if (!ast.isHttpOperationNode(node)) return null\n const { driver, resolver, config, root } = ctx\n const { output, parser, baseURL, group } = ctx.options\n\n const fileName = resolver.name(node.operationId)\n const mock = {\n name: resolver.handler.name(node),\n file: resolver.file({ name: fileName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path, root, output, group: group ?? undefined }),\n }\n\n const fakerPlugin = parser === 'faker' ? ctx.requirePlugin(pluginFakerName) : null\n const fakerResolver = fakerPlugin ? driver.getResolver(pluginFakerName) : null\n const faker =\n fakerPlugin && fakerResolver\n ? {\n name: fakerResolver.response.response(node),\n file: fakerResolver.file({\n name: node.operationId,\n extname: '.ts',\n tag: node.tags[0] ?? 'default',\n path: node.path,\n root,\n output: fakerPlugin.options?.output ?? output,\n group: fakerPlugin.options?.group ?? undefined,\n }),\n }\n : null\n\n const pluginTs = ctx.requirePlugin(pluginTsName)\n const tsResolver = driver.getResolver(pluginTsName)\n\n const type = {\n file: resolveDependencyOperationFile({\n cache: ctx.cache,\n node,\n resolver: tsResolver,\n root,\n output: pluginTs.options?.output ?? output,\n group: pluginTs.options?.group,\n }),\n responseName: tsResolver.response.response(node),\n }\n\n const successResponses = getOperationSuccessResponses(node)\n const referencedNames = node.responses.flatMap((response) =>\n (response.content ?? []).flatMap((entry) => (entry.schema ? ast.collectImportedRefNames(entry.schema) : [])),\n )\n const enumOptions = pluginTs.options?.enum\n const enumNames = new Set(ctx.meta.enumNames)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n const types = resolveResponseTypes(node, tsResolver).map(([code, typeName]) => {\n const successResponse = successResponses.find((response) => response.statusCode === String(code))\n return [code, hasResponseNameCollision && successResponse ? tsResolver.response.status(node, successResponse.statusCode) : typeName] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName).filter((name) => !hasResponseNameCollision || name !== type.responseName)\n const mockResponseName =\n hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName\n const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response))\n\n const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.response.body(node) : null\n\n return (\n <File\n baseName={mock.file.baseName}\n path={mock.file.path}\n meta={mock.file.meta}\n banner={resolver.default.banner(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n footer={resolver.default.footer(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}\n >\n <File.Import name={['http', 'HttpResponse']} path=\"msw/http\" />\n <File.Import name={['HttpResponseResolver']} isTypeOnly path=\"msw/http\" />\n {requestName && <File.Import name={['DefaultBodyType']} isTypeOnly path=\"msw\" />}\n <File.Import\n name={Array.from(new Set([...(hasResponseNameCollision ? [] : [type.responseName]), ...responseTypeNames, ...(requestName ? [requestName] : [])]))}\n path={type.file.path}\n root={mock.file.path}\n isTypeOnly\n />\n {faker && hasSuccessSchema && <File.Import name={[faker.name]} root={mock.file.path} path={faker.file.path} />}\n\n {types\n .filter(([code]) => code !== 'default')\n .map(([code, typeName]) => {\n const response = node.responses.find((item) => item.statusCode === String(code))\n if (!response) return null\n return <Response key={typeName} typeName={typeName} response={response} name={mock.name} />\n })}\n\n <Mock\n name={mock.name}\n typeName={mockResponseName}\n requestTypeName={requestName}\n fakerName={faker && hasSuccessSchema ? faker.name : undefined}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { operationKey } from '@internals/shared'\nimport { camelCase, createResolver } from 'kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return this.memo(`name:${name}`, () => camelCase(name, { suffix: 'handler' }))\n },\n handler: {\n name(node) {\n const name = this.name(node.operationId)\n return this.resolveName({ key: operationKey(node, 'handler', 'name'), name })\n },\n listName() {\n return this.resolveName({ key: ['handlers', 'list'], name: 'handlers' })\n },\n },\n})\n","import { createGroupConfig } from '@internals/shared'\nimport { addGenerator, defineExtension, Resolver, setMacros, setResolver } from 'kubb/kit'\nimport { compatibility } from '../kubb-extension.json'\nimport { version } from '../package.json'\nimport { pluginFakerName } from '@kubb/plugin-faker'\nimport { pluginTsName } from '@kubb/plugin-ts'\nimport { handlersGenerator } from './generators/handlersGenerator.ts'\nimport { mswGenerator } from './generators/mswGenerator.tsx'\nimport { resolverMsw } from './resolvers/resolverMsw.ts'\nimport type { PluginMsw, ResolverMsw } from './types.ts'\n\n/**\n * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and\n * cross-plugin dependency references.\n */\nexport const pluginMswName = 'plugin-msw' satisfies PluginMsw['name']\n\n/**\n * Generates MSW request handlers from an OpenAPI spec. Drop them into your\n * test setup or service worker to mock the API end-to-end. Request path,\n * method, status, and response body all stay in sync with the spec. Combine\n * with `@kubb/plugin-faker` (via `parser: 'faker'`) to seed handlers with\n * realistic data.\n *\n * @example\n * ```ts\n * import { defineConfig } from 'kubb/config'\n * import { pluginTs } from '@kubb/plugin-ts'\n * import { pluginMsw } from '@kubb/plugin-msw'\n *\n * export default defineConfig({\n * input: './petStore.yaml',\n * output: { path: './src/gen' },\n * plugins: [\n * pluginTs(),\n * pluginMsw({\n * output: { path: './handlers' },\n * handlers: true,\n * }),\n * ],\n * })\n * ```\n */\nexport const pluginMsw = defineExtension<PluginMsw>({\n kind: 'plugin',\n meta: { name: pluginMswName, version, compatibility },\n dependencies: (options) => ({ [pluginTsName]: {}, ...(options.parser === 'faker' ? { [pluginFakerName]: {} } : {}) }),\n setup(options) {\n const {\n output = { path: 'handlers', barrel: { type: 'named' } },\n group,\n exclude = [],\n include,\n override = [],\n handlers = false,\n parser = 'data',\n baseURL,\n resolver: userResolver,\n macros: userMacros,\n } = options\n\n const resolver = userResolver ? Resolver.merge<ResolverMsw>(resolverMsw, userResolver) : resolverMsw\n\n setResolver(resolver)\n if (userMacros?.length) {\n setMacros(userMacros)\n }\n\n addGenerator(mswGenerator)\n if (handlers) {\n addGenerator(handlersGenerator)\n }\n\n return {\n output,\n parser,\n baseURL,\n group: createGroupConfig(group),\n exclude,\n include,\n override,\n handlers,\n }\n },\n})\n\nexport default pluginMsw\n"],"mappings":"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"}
@@ -20,7 +20,7 @@
20
20
  "dependencies": ["plugin-ts"],
21
21
  "optionalDependencies": ["plugin-faker"],
22
22
  "compatibility": {
23
- "kubb": ">=5.8.3",
23
+ "kubb": ">=5.14.6",
24
24
  "node": ">=22.22"
25
25
  },
26
26
  "options": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-msw",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
4
4
  "description": "Generate Mock Service Worker (MSW) request handlers with Kubb. Intercept HTTP requests in the browser or Node.js for frontend development and testing.",
5
5
  "keywords": [
6
6
  "api-mocking",
@@ -85,17 +85,17 @@
85
85
  "registry": "https://registry.npmjs.org/"
86
86
  },
87
87
  "dependencies": {
88
- "@kubb/plugin-faker": "^5.2.1",
89
- "@kubb/plugin-ts": "^5.2.0"
88
+ "@kubb/plugin-faker": "^5.2.3",
89
+ "@kubb/plugin-ts": "^5.3.3"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@internals/shared": "0.0.0",
93
93
  "@internals/utils": "0.0.0",
94
- "kubb": "^5.14.0",
94
+ "kubb": "^5.15.0",
95
95
  "msw": "^3.0.3"
96
96
  },
97
97
  "peerDependencies": {
98
- "kubb": "^5.14.0",
98
+ "kubb": "^5.15.0",
99
99
  "msw": "^3.0.0"
100
100
  },
101
101
  "peerDependenciesMeta": {