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