@kubb/plugin-msw 5.2.0-canary.20261023T185431 → 5.2.1

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
@@ -155,7 +155,7 @@ var compatibility = {
155
155
  };
156
156
  //#endregion
157
157
  //#region package.json
158
- var version = "5.2.0-canary.20261023T185431";
158
+ var version = "5.2.1";
159
159
  //#endregion
160
160
  //#region src/generators/handlersGenerator.ts
161
161
  /**
@@ -170,6 +170,7 @@ const handlersGenerator = (0, kubb_kit.defineGenerator)({
170
170
  const { resolver, config, root } = ctx;
171
171
  const { output, group } = ctx.options;
172
172
  const handlersName = resolver.handler.listName();
173
+ const createHandlersName = camelCase(handlersName, { prefix: "create" });
173
174
  const file = resolver.file({
174
175
  name: handlersName,
175
176
  extname: ".ts",
@@ -195,7 +196,7 @@ const handlersGenerator = (0, kubb_kit.defineGenerator)({
195
196
  path: operationFile.path
196
197
  });
197
198
  });
198
- const handlers = handlerNames.map((name) => `${name}()`);
199
+ const handlers = handlerNames.map((name) => `${name}(undefined, options)`);
199
200
  return [kubb_kit.ast.factory.createFile({
200
201
  baseName: file.baseName,
201
202
  path: file.path,
@@ -218,10 +219,17 @@ const handlersGenerator = (0, kubb_kit.defineGenerator)({
218
219
  }),
219
220
  imports,
220
221
  sources: [kubb_kit.ast.factory.createSource({
222
+ name: createHandlersName,
223
+ isIndexable: true,
224
+ isExportable: true,
225
+ nodes: [kubb_kit.ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {
226
+ return [${handlers.join(", ")}] as const
227
+ }`)]
228
+ }), kubb_kit.ast.factory.createSource({
221
229
  name: handlersName,
222
230
  isIndexable: true,
223
231
  isExportable: true,
224
- nodes: [kubb_kit.ast.factory.createText(`export const ${handlersName} = ${JSON.stringify(handlers).replaceAll("\"", "")} as const`)]
232
+ nodes: [kubb_kit.ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)]
225
233
  })]
226
234
  })];
227
235
  }
@@ -229,6 +237,14 @@ const handlersGenerator = (0, kubb_kit.defineGenerator)({
229
237
  //#endregion
230
238
  //#region src/utils.ts
231
239
  /**
240
+ * Returns the base URL as source code. A `${...}` value stays a template literal.
241
+ */
242
+ function resolveDefaultBaseURL(baseURL) {
243
+ if (!baseURL) return "''";
244
+ if (baseURL.includes("${")) return `\`${baseURL.replaceAll("`", "\\`")}\``;
245
+ return JSON.stringify(baseURL);
246
+ }
247
+ /**
232
248
  * Gets the content type from a response, defaulting to 'application/json' if a schema exists.
233
249
  */
234
250
  function getContentType(response) {
@@ -285,10 +301,9 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
285
301
  const contentType = getContentType(successResponse);
286
302
  const url = kubb_kit.Url.toPath(node.path);
287
303
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
288
- const params = `data?: ${fakerName ? typeName : dataType} | ${requestTypeName ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}`;
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 }`;
289
305
  const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`;
290
- const requestUrl = `${baseURL}${url.replace(/([^/]):/g, "$1\\\\:")}`;
291
- const urlLiteral = fakerName ? `'${requestUrl}'` : `\`${requestUrl}\``;
306
+ const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
292
307
  const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : "JSON.stringify(data)";
293
308
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
294
309
  name,
@@ -547,12 +562,6 @@ const pluginMsw = (0, kubb_kit.defineExtension)({
547
562
  path: "handlers",
548
563
  barrel: { type: "named" }
549
564
  }, group, exclude = [], include, override = [], handlers = false, parser = "data", baseURL, resolver: userResolver, macros: userMacros } = options;
550
- (0, kubb_kit.assertPluginOption)({
551
- plugin: pluginMswName,
552
- option: "parser",
553
- value: parser,
554
- allowed: ["data", "faker"]
555
- });
556
565
  const resolver = userResolver ? kubb_kit.Resolver.merge(resolverMsw, userResolver) : resolverMsw;
557
566
  (0, kubb_kit.setResolver)(resolver);
558
567
  if (userMacros?.length) (0, kubb_kit.setMacros)(userMacros);
@@ -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 { 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 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}()`)\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: handlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [ast.factory.createText(`export const ${handlersName} = ${JSON.stringify(handlers).replaceAll('\"', '')} as const`)],\n }),\n ],\n }),\n ]\n },\n})\n","import type { ast } from 'kubb/kit'\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 Response(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new Response(${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, 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}`\n\n const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`\n\n const requestUrl = `${baseURL}${url.replace(/([^/]):/g, '$1\\\\\\\\:')}`\n const urlLiteral = fakerName ? `'${requestUrl}'` : `\\`${requestUrl}\\``\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']} path=\"msw\" />\n <File.Import name={['HttpResponseResolver']} 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 { 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, assertPluginOption, 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 assertPluginOption({\n plugin: pluginMswName,\n option: 'parser',\n value: parser,\n allowed: ['data', 'faker'] satisfies Array<PluginMsw['resolvedOptions']['parser']>,\n })\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":["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 Response(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new Response(${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']} path=\"msw\" />\n <File.Import name={['HttpResponseResolver']} 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 { 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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"}
package/dist/index.d.cts CHANGED
@@ -26,8 +26,10 @@ type ResolverMsw = Resolver & {
26
26
  */
27
27
  type Options = OutputOptions & {
28
28
  /**
29
- * Base URL prepended to every handler's request URL. When omitted, handlers match the
30
- * operation path only (for example `/pets/:petId`). The adapter's server URL is not used.
29
+ * Base URL prepended to every handler's request URL. A `${...}` expression runs at runtime.
30
+ * Pass `{ baseURL }` as a handler's second argument to override it per call.
31
+ *
32
+ * @note Leave off the trailing slash. Operation paths already start with `/`.
31
33
  */
32
34
  baseURL?: string;
33
35
  /**
@@ -51,10 +53,8 @@ type Options = OutputOptions & {
51
53
  */
52
54
  macros?: Array<ast.Macro>;
53
55
  /**
54
- * Emit a `handlers.ts` file that re-exports every handler in operation order.
55
- * Drop the file into `setupServer(...handlers)` or `setupWorker(...handlers)`.
56
- *
57
- * @default false
56
+ * Emit a `handlers.ts` file with every handler in operation order, as `handlers` and as
57
+ * `createHandlers({ baseURL })` for a runtime base URL.
58
58
  */
59
59
  handlers?: boolean;
60
60
  /**
package/dist/index.d.ts CHANGED
@@ -26,8 +26,10 @@ type ResolverMsw = Resolver & {
26
26
  */
27
27
  type Options = OutputOptions & {
28
28
  /**
29
- * Base URL prepended to every handler's request URL. When omitted, handlers match the
30
- * operation path only (for example `/pets/:petId`). The adapter's server URL is not used.
29
+ * Base URL prepended to every handler's request URL. A `${...}` expression runs at runtime.
30
+ * Pass `{ baseURL }` as a handler's second argument to override it per call.
31
+ *
32
+ * @note Leave off the trailing slash. Operation paths already start with `/`.
31
33
  */
32
34
  baseURL?: string;
33
35
  /**
@@ -51,10 +53,8 @@ type Options = OutputOptions & {
51
53
  */
52
54
  macros?: Array<ast.Macro>;
53
55
  /**
54
- * Emit a `handlers.ts` file that re-exports every handler in operation order.
55
- * Drop the file into `setupServer(...handlers)` or `setupWorker(...handlers)`.
56
- *
57
- * @default false
56
+ * Emit a `handlers.ts` file with every handler in operation order, as `handlers` and as
57
+ * `createHandlers({ baseURL })` for a runtime base URL.
58
58
  */
59
59
  handlers?: boolean;
60
60
  /**
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Resolver, Url, addGenerator, assertPluginOption, ast, createResolver, defineExtension, defineGenerator, setMacros, setResolver } from "kubb/kit";
1
+ import { Resolver, Url, addGenerator, ast, 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";
@@ -151,7 +151,7 @@ var compatibility = {
151
151
  };
152
152
  //#endregion
153
153
  //#region package.json
154
- var version = "5.2.0-canary.20261023T185431";
154
+ var version = "5.2.1";
155
155
  //#endregion
156
156
  //#region src/generators/handlersGenerator.ts
157
157
  /**
@@ -166,6 +166,7 @@ const handlersGenerator = defineGenerator({
166
166
  const { resolver, config, root } = ctx;
167
167
  const { output, group } = ctx.options;
168
168
  const handlersName = resolver.handler.listName();
169
+ const createHandlersName = camelCase(handlersName, { prefix: "create" });
169
170
  const file = resolver.file({
170
171
  name: handlersName,
171
172
  extname: ".ts",
@@ -191,7 +192,7 @@ const handlersGenerator = defineGenerator({
191
192
  path: operationFile.path
192
193
  });
193
194
  });
194
- const handlers = handlerNames.map((name) => `${name}()`);
195
+ const handlers = handlerNames.map((name) => `${name}(undefined, options)`);
195
196
  return [ast.factory.createFile({
196
197
  baseName: file.baseName,
197
198
  path: file.path,
@@ -214,10 +215,17 @@ const handlersGenerator = defineGenerator({
214
215
  }),
215
216
  imports,
216
217
  sources: [ast.factory.createSource({
218
+ name: createHandlersName,
219
+ isIndexable: true,
220
+ isExportable: true,
221
+ nodes: [ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {
222
+ return [${handlers.join(", ")}] as const
223
+ }`)]
224
+ }), ast.factory.createSource({
217
225
  name: handlersName,
218
226
  isIndexable: true,
219
227
  isExportable: true,
220
- nodes: [ast.factory.createText(`export const ${handlersName} = ${JSON.stringify(handlers).replaceAll("\"", "")} as const`)]
228
+ nodes: [ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)]
221
229
  })]
222
230
  })];
223
231
  }
@@ -225,6 +233,14 @@ const handlersGenerator = defineGenerator({
225
233
  //#endregion
226
234
  //#region src/utils.ts
227
235
  /**
236
+ * Returns the base URL as source code. A `${...}` value stays a template literal.
237
+ */
238
+ function resolveDefaultBaseURL(baseURL) {
239
+ if (!baseURL) return "''";
240
+ if (baseURL.includes("${")) return `\`${baseURL.replaceAll("`", "\\`")}\``;
241
+ return JSON.stringify(baseURL);
242
+ }
243
+ /**
228
244
  * Gets the content type from a response, defaulting to 'application/json' if a schema exists.
229
245
  */
230
246
  function getContentType(response) {
@@ -281,10 +297,9 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
281
297
  const contentType = getContentType(successResponse);
282
298
  const url = Url.toPath(node.path);
283
299
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
284
- const params = `data?: ${fakerName ? typeName : dataType} | ${requestTypeName ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}`;
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 }`;
285
301
  const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`;
286
- const requestUrl = `${baseURL}${url.replace(/([^/]):/g, "$1\\\\:")}`;
287
- const urlLiteral = fakerName ? `'${requestUrl}'` : `\`${requestUrl}\``;
302
+ const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
288
303
  const responseBody = fakerName ? `JSON.stringify(data ?? ${fakerName}(data))` : "JSON.stringify(data)";
289
304
  return /* @__PURE__ */ jsx(File.Source, {
290
305
  name,
@@ -543,12 +558,6 @@ const pluginMsw = defineExtension({
543
558
  path: "handlers",
544
559
  barrel: { type: "named" }
545
560
  }, group, exclude = [], include, override = [], handlers = false, parser = "data", baseURL, resolver: userResolver, macros: userMacros } = options;
546
- assertPluginOption({
547
- plugin: pluginMswName,
548
- option: "parser",
549
- value: parser,
550
- allowed: ["data", "faker"]
551
- });
552
561
  const resolver = userResolver ? Resolver.merge(resolverMsw, userResolver) : resolverMsw;
553
562
  setResolver(resolver);
554
563
  if (userMacros?.length) setMacros(userMacros);
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 { 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 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}()`)\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: handlersName,\n isIndexable: true,\n isExportable: true,\n nodes: [ast.factory.createText(`export const ${handlersName} = ${JSON.stringify(handlers).replaceAll('\"', '')} as const`)],\n }),\n ],\n }),\n ]\n },\n})\n","import type { ast } from 'kubb/kit'\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 Response(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new Response(${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, 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}`\n\n const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`\n\n const requestUrl = `${baseURL}${url.replace(/([^/]):/g, '$1\\\\\\\\:')}`\n const urlLiteral = fakerName ? `'${requestUrl}'` : `\\`${requestUrl}\\``\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']} path=\"msw\" />\n <File.Import name={['HttpResponseResolver']} 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 { 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, assertPluginOption, 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 assertPluginOption({\n plugin: pluginMswName,\n option: 'parser',\n value: parser,\n allowed: ['data', 'faker'] satisfies Array<PluginMsw['resolvedOptions']['parser']>,\n })\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/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 Response(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null): string {\n return `new Response(${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']} path=\"msw\" />\n <File.Import name={['HttpResponseResolver']} 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 { 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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"}
@@ -83,3 +83,23 @@ export const worker = setupWorker(...handlers)
83
83
  ::
84
84
 
85
85
  Call `server.listen()` in your test setup, or `await worker.start()` in your browser entry.
86
+
87
+ ## Set the base URL at runtime
88
+
89
+ The `baseURL` option is fixed when Kubb generates the handlers. When the host is only known at runtime, such as from your API client's config, call `createHandlers` with it:
90
+
91
+ ```typescript [test-server.ts]
92
+ import { setupServer } from 'msw/node'
93
+ import { createHandlers } from './src/gen/handlers/handlers'
94
+
95
+ export const server = setupServer(...createHandlers({ baseURL: getBaseURL() }))
96
+ ```
97
+
98
+ Each handler also takes `{ baseURL }` as its second argument, which overrides the configured one for that handler. Pass `undefined` as the first argument to keep the default response. This lets you mock two services that share a path:
99
+
100
+ ```typescript [test-server.ts]
101
+ export const server = setupServer(
102
+ listPetsHandler(undefined, { baseURL: 'https://pets.example.com' }),
103
+ listPetsHandler(undefined, { baseURL: 'https://staging.example.com' }),
104
+ )
105
+ ```
package/docs/options.md CHANGED
@@ -21,7 +21,9 @@ Pass these options to `pluginMsw()`. Shared options link to [Shared plugin optio
21
21
 
22
22
  ### baseURL
23
23
 
24
- Base URL prepended to every handler's request. When omitted, no host is prepended and each handler matches the operation's relative path from the spec. Set it to point at a different environment than the spec.
24
+ Base URL prepended to every handler's request. When omitted, no host is prepended and each handler matches the operation's relative path from the spec. Set it to point at a different environment than the spec. Leave off the trailing slash, because operation paths already start with `/`.
25
+
26
+ A value with a template expression, such as `'${process.env.API_URL}'`, is evaluated when the handler is created. To set the base URL at runtime instead, pass `{ baseURL }` to a handler or to [`createHandlers`](#handlers).
25
27
 
26
28
  | | |
27
29
  | -------- | -------- |
@@ -30,7 +32,10 @@ Base URL prepended to every handler's request. When omitted, no host is prepende
30
32
 
31
33
  ### handlers
32
34
 
33
- Emits a `handlers.ts` file that re-exports every generated handler in operation order. Spread it into your MSW `setupServer(...handlers)` or `setupWorker(...handlers)` call.
35
+ Emits a `handlers.ts` file with every generated handler in operation order. It exports two things:
36
+
37
+ - `handlers`: the collection with the configured `baseURL`. Spread it into `setupServer(...handlers)` or `setupWorker(...handlers)`.
38
+ - `createHandlers(options?)`: builds the same collection with a `baseURL` you pass at runtime.
34
39
 
35
40
  | | |
36
41
  | -------- | --------- |
@@ -42,7 +47,11 @@ Emits a `handlers.ts` file that re-exports every generated handler in operation
42
47
  import { getPetHandler } from './getPetHandler'
43
48
  import { addPetHandler } from './addPetHandler'
44
49
 
45
- export const handlers = [getPetHandler(), addPetHandler()] as const
50
+ export function createHandlers(options?: { baseURL?: string }) {
51
+ return [getPetHandler(undefined, options), addPetHandler(undefined, options)] as const
52
+ }
53
+
54
+ export const handlers = createHandlers()
46
55
  ```
47
56
 
48
57
  ### parser
package/docs/overview.md CHANGED
@@ -13,7 +13,7 @@ Each handler returns the data you pass to it. With `parser: 'faker'`, a handler
13
13
  ## Options worth knowing
14
14
 
15
15
  - `parser` sets where the response body comes from: `data` (default) or `faker`.
16
- - `handlers` writes a `handlers.ts` file that exports every handler, ready to spread into `setupServer` or `setupWorker`.
16
+ - `handlers` writes a `handlers.ts` file that exports every handler, ready to spread into `setupServer` or `setupWorker`, plus `createHandlers({ baseURL })` to set the base URL at runtime.
17
17
  - `baseURL` is prepended to the path each handler matches.
18
18
 
19
19
  See [Options](https://kubb.dev/extensions/kubb-labs/plugin-msw/options) for every option and its default.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-msw",
3
- "version": "5.2.0-canary.20261023T185431",
3
+ "version": "5.2.1",
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,18 +85,18 @@
85
85
  "registry": "https://registry.npmjs.org/"
86
86
  },
87
87
  "dependencies": {
88
- "@kubb/plugin-faker": "^5.2.0-canary.20261023T185431",
89
- "@kubb/plugin-ts": "^5.1.0-canary.20261023T185431"
88
+ "@kubb/plugin-faker": "^5.1.11",
89
+ "@kubb/plugin-ts": "^5.0.16"
90
90
  },
91
91
  "devDependencies": {
92
- "@internals/shared": "0.1.0-canary.20261023T185431",
93
- "@internals/utils": "0.1.0-canary.20261023T185430",
92
+ "@internals/shared": "0.0.0",
93
+ "@internals/utils": "0.0.0",
94
94
  "@typescript/typescript6": "^6.0.2",
95
- "kubb": "^5.12.6",
95
+ "kubb": "^5.12.8",
96
96
  "msw": "^2.15.0"
97
97
  },
98
98
  "peerDependencies": {
99
- "kubb": "^5.12.6",
99
+ "kubb": "^5.12.8",
100
100
  "msw": "^2.0.0"
101
101
  },
102
102
  "peerDependenciesMeta": {