@kubb/plugin-msw 6.2.0 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -13,7 +13,7 @@ var compatibility = {
13
13
  "node": ">=22.22"
14
14
  };
15
15
  // package.json
16
- var version = "6.2.0";
16
+ var version = "6.2.1";
17
17
  // src/generators/handlersGenerator.ts
18
18
  /**
19
19
  * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
@@ -452,6 +452,18 @@ function getTextContentType(response) {
452
452
  return !_kubb_kit.ast.mediaType.isJson(entry) && schema && _kubb_kit.ast.ref.getSchema(schema).primitive === "string";
453
453
  })?.contentType ?? null;
454
454
  }
455
+ const bigintReplacer = `(_key, value) => {
456
+ if (typeof value !== 'bigint') return value
457
+ if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) throw new TypeError(\`Cannot serialize \${value}n as JSON without losing precision.\`)
458
+ return Number(value)
459
+ }`;
460
+ /** The `JSON.stringify` call for `body`, with a replacer when the response holds an `int64`, which `JSON.stringify` throws on. */
461
+ function stringify(body, response) {
462
+ return (response?.content ?? []).some((entry) => entry.schema && _kubb_kit.ast.schema.find(entry.schema, {
463
+ predicate: (schema) => schema.type === "bigint",
464
+ followRefs: true
465
+ }) !== void 0) ? `JSON.stringify(${body}, ${bigintReplacer})` : `JSON.stringify(${body})`;
466
+ }
455
467
  /**
456
468
  * The statements that return a mocked `Response` for `body`. A string body for a response the client reads as text goes
457
469
  * out as raw text with that content type, so an XML mock is not sent as a quoted JSON string. Any other body is JSON.
@@ -465,27 +477,27 @@ function responseReturn(body, status, response) {
465
477
  const entry = getResponseEntry(response);
466
478
  const serialized = entry?.examples?.find((example) => example.serializedValue !== void 0)?.serializedValue;
467
479
  const fallback = serialized === void 0 ? "''" : toLiteral(serialized);
468
- if (entry?.stream?.shape !== "event") return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \`\${${eventDataLines(name)}}\\n\\n\``, status, contentType, headers)}`;
480
+ if (entry?.stream?.shape !== "event") return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \`\${${eventDataLines(name, response)}}\\n\\n\``, status, contentType, headers)}`;
469
481
  return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined
470
482
  const lines = [
471
483
  ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \`\${field}: \${event?.[field]}\`),
472
484
  // A client dispatches an event only once it has a \`data\` line, so an event without data still sends an empty one.
473
- event?.data === undefined ? 'data:' : ${eventDataLines("event.data")},
485
+ event?.data === undefined ? 'data:' : ${eventDataLines("event.data", response)},
474
486
  ]
475
487
 
476
488
  return ${responseLiteral(`event === undefined ? ${fallback} : \`\${lines.join('\\n')}\\n\\n\``, status, contentType, headers)}`;
477
489
  }
478
490
  const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null;
479
- if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType, headers)}`;
491
+ if (!textContentType) return `return ${responseLiteral(stringify(body, response), status, contentType, headers)}`;
480
492
  return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}
481
493
 
482
- return ${responseLiteral(`JSON.stringify(${name})`, status, contentType, headers)}`;
494
+ return ${responseLiteral(stringify(name, response), status, contentType, headers)}`;
483
495
  }
484
496
  /**
485
497
  * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.
486
498
  */
487
- function eventDataLines(body) {
488
- return `(typeof ${body} === 'string' ? ${body} : JSON.stringify(${body})).split('\\n').map((line) => \`data: \${line}\`).join('\\n')`;
499
+ function eventDataLines(body, response) {
500
+ return `(typeof ${body} === 'string' ? ${body} : ${stringify(body, response)}).split('\\n').map((line) => \`data: \${line}\`).join('\\n')`;
489
501
  }
490
502
  /**
491
503
  * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["defineGenerator","casing","ast","text","ast","ast","text","text","ast","Url","File","Function","File","Function","ast","defineGenerator","jsxRenderer","ast","pluginFakerName","pluginTsName","enumTypeName","File","createResolver","casing","defineExtension","pluginTsName","pluginFakerName","Resolver"],"sources":["../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../../../internals/utils/src/reserved.ts","../../../internals/utils/src/codegen.ts","../../../internals/shared/src/operation.ts","../../../internals/shared/src/resolver.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, casing, 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: casing.camel(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","/**\n * JavaScript and Java reserved words.\n * @link https://github.com/jonschlinkert/reserved/blob/master/index.js\n */\nconst reservedWords = new Set([\n 'abstract',\n 'arguments',\n 'await',\n 'boolean',\n 'break',\n 'byte',\n 'case',\n 'catch',\n 'char',\n 'class',\n 'const',\n 'continue',\n 'debugger',\n 'default',\n 'delete',\n 'do',\n 'double',\n 'else',\n 'enum',\n 'eval',\n 'export',\n 'extends',\n 'false',\n 'final',\n 'finally',\n 'float',\n 'for',\n 'function',\n 'goto',\n 'if',\n 'implements',\n 'import',\n 'in',\n 'instanceof',\n 'int',\n 'interface',\n 'let',\n 'long',\n 'native',\n 'new',\n 'null',\n 'package',\n 'private',\n 'protected',\n 'public',\n 'return',\n 'short',\n 'static',\n 'super',\n 'switch',\n 'synchronized',\n 'this',\n 'throw',\n 'throws',\n 'transient',\n 'true',\n 'try',\n 'typeof',\n 'var',\n 'void',\n 'volatile',\n 'while',\n 'with',\n 'yield',\n 'Array',\n 'Date',\n 'hasOwnProperty',\n 'Infinity',\n 'isFinite',\n 'isNaN',\n 'isPrototypeOf',\n 'length',\n 'Math',\n 'name',\n 'NaN',\n 'Number',\n 'Object',\n 'prototype',\n 'String',\n 'toString',\n 'undefined',\n 'valueOf',\n] as const)\n\n/**\n * Returns `true` when `name` is a syntactically valid JavaScript variable name.\n *\n * @example\n * ```ts\n * isValidVarName('status') // true\n * isValidVarName('class') // false (reserved word)\n * isValidVarName('42foo') // false (starts with digit)\n * ```\n */\nexport function isValidVarName(name: string): boolean {\n if (!name || reservedWords.has(name as 'valueOf')) {\n return false\n }\n return isIdentifier(name)\n}\n\n/**\n * Returns `name` when it's a syntactically valid JavaScript variable name,\n * otherwise prefixes it with `_` so the result is a valid identifier.\n *\n * Useful for sanitizing OpenAPI schema names or operation IDs that start with\n * a digit (e.g. `409`, `504AccountCancel`) before using them as exported\n * variable, type, or function names.\n *\n * @example\n * ```ts\n * ensureValidVarName('409') // '_409'\n * ensureValidVarName('504AccountCancel') // '_504AccountCancel'\n * ensureValidVarName('Pet') // 'Pet'\n * ensureValidVarName('class') // '_class'\n * ```\n */\nexport function ensureValidVarName(name: string): string {\n if (!name || isValidVarName(name)) {\n return name\n }\n return `_${name}`\n}\n\n/**\n * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.\n *\n * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys\n * even though they are not valid variable names, so use this (not {@link isValidVarName}) when\n * deciding whether an object key needs quoting.\n *\n * @example\n * ```ts\n * isIdentifier('name') // true\n * isIdentifier('x-total')// false\n * ```\n */\nexport function isIdentifier(name: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)\n}\n","import { text } from '@kubb/kit'\nimport { isIdentifier } from './reserved.ts'\n\n/**\n * Builds a JSDoc comment block from an array of lines. Returns `fallback` when there are no\n * comments.\n *\n * @example\n * ```ts\n * buildJSDoc(['@type string', '@example hello'])\n * // '/**\\n * @type string\\n * @example hello\\n *\\/\\n '\n * ```\n */\nexport function buildJSDoc(\n comments: Array<string>,\n options: {\n /**\n * String used to indent each comment line.\n * @default ' * '\n */\n indent?: string\n /**\n * String appended after the closing tag.\n * @default '\\n '\n */\n suffix?: string\n /**\n * Returned as-is when `comments` is empty.\n * @default ' '\n */\n fallback?: string\n } = {},\n): string {\n const { indent = ' * ', suffix = '\\n ', fallback = ' ' } = options\n\n if (comments.length === 0) return fallback\n\n return `/**\\n${comments.map((c) => `${indent}${c}`).join('\\n')}\\n */${suffix}`\n}\n\n/**\n * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one\n * level and closing the brace at column zero. Entries that are themselves multi-line objects indent\n * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.\n *\n * @example\n * ```ts\n * buildObject(['id: z.number()', 'name: z.string()'])\n * // '{\\n id: z.number(),\\n name: z.string(),\\n}'\n * ```\n */\nexport function buildObject(entries: Array<string>): string {\n if (entries.length === 0) return '{}'\n const body = entries.map((entry) => `${text.indent({ text: entry })},`).join('\\n')\n\n return `{\\n${body}\\n}`\n}\n\n/**\n * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on\n * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented\n * one level with a trailing comma and the closing bracket at column zero. Used for member lists such\n * as `z.union([…])` and `z.array([…])`.\n *\n * @example\n * ```ts\n * buildList(['z.string()', 'z.number()'])\n * // '[z.string(), z.number()]'\n * ```\n */\nexport function buildList(items: Array<string>, brackets: [open: string, close: string] = ['[', ']']): string {\n const [open, close] = brackets\n if (items.length === 0) return `${open}${close}`\n if (!items.some((item) => item.includes('\\n'))) return `${open}${items.join(', ')}${close}`\n const body = items.map((item) => `${text.indent({ text: item })},`).join('\\n')\n\n return `${open}\\n${body}\\n${close}`\n}\n\n/**\n * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key\n * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation\n * of a recursive schema until first access.\n *\n * @example\n * ```ts\n * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })\n * // \"get parent() { return z.lazy(() => Pet) }\"\n * ```\n */\nexport function lazyGetter({ name, body }: { name: string; body: string }): string {\n return `get ${objectKey(name)}() { return ${body} }`\n}\n\n/**\n * An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.\n *\n * @example\n * ```ts\n * objectKey('x-total') // \"'x-total'\"\n * ```\n */\nexport function objectKey(name: string): string {\n return isIdentifier(name) ? name : text.quote(name)\n}\n\n/**\n * A JSON value as a JavaScript literal, with single-quoted strings and identifier keys left unquoted.\n *\n * @example\n * ```ts\n * toLiteral({ id: 1, 'x-tag': ['a'] }) // \"{ id: 1, 'x-tag': ['a'] }\"\n * ```\n */\nexport function toLiteral(value: unknown): string {\n if (typeof value === 'string') return text.quote(value)\n if (Array.isArray(value)) return `[${value.map(toLiteral).join(', ')}]`\n if (value && typeof value === 'object') {\n return `{ ${Object.entries(value)\n .map(([key, item]) => `${objectKey(key)}: ${toLiteral(item)}`)\n .join(', ')} }`\n }\n return String(value)\n}\n","import { jsDocEscape } from '@internals/utils'\nimport { ast, type Group, type Memo, type Output, type Resolver, type ResolverFileParams } 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 the file a schema-emitting plugin (`plugin-ts`, `plugin-zod`, `plugin-faker`) writes\n * `node` to, cached in `ctx.memo` under the resolver's plugin name and the\n * operation. The plugin itself and every plugin that imports from it call this, so they all get\n * the same path.\n *\n * When that path matches the file of a component schema in `ctx.driver.inputNode`, ignoring case, the file name\n * gets an `Operation` suffix: operation `point` goes to `PointOperation.ts` next to schema `Point.ts`.\n * Otherwise the two merge into one file, or overwrite each other on a case-insensitive file system.\n * Only the file name changes. The declarations keep their names.\n *\n * @example Resolve `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveOperationFile({ ctx, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveOperationFile(options: {\n ctx: { memo: Memo; driver: { inputNode?: { schemas: ReadonlyArray<ast.SchemaNode> } | null } }\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 { ctx, node, resolver, root, output } = options\n const { memo } = ctx\n const location = { root, output, group: options.group ?? undefined }\n\n return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {\n const schemaPaths = memo(\n `${resolver.pluginName}:schemaFilePaths`,\n () =>\n new Set(\n (ctx.driver.inputNode?.schemas ?? []).flatMap((schema) =>\n schema.name ? [resolver.file({ name: schema.name, extname: '.ts', ...location }).path.toLowerCase()] : [],\n ),\n ),\n )\n let file = resolver.file({ ...operationFileEntry(node, node.operationId), ...location })\n\n for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += 'Operation') {\n const next = resolver.file({ ...operationFileEntry(node, name), ...location })\n // A single-file output holds every declaration on purpose, so a new name changes nothing.\n if (next.path === file.path) break\n file = next\n }\n\n return file\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 cookies(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type BuildOperationCommentsOptions = {\n splitLines?: boolean\n}\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\n/**\n * The operation's method and path as plain text, such as `` `GET /pet/{petId}` ``. A path is not a valid `{@link}` target.\n */\nfunction getOperationRequestLine(node: ast.OperationNode): string | null {\n return ast.http.isOperation(node) && node.path ? `\\`${node.method.toUpperCase()} ${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(ast.http.responses.findPrimary(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\ntype ResponseType = 'blob' | 'text' | 'stream'\n\ntype ResponseContentEntry = { contentType: string; schema?: ast.SchemaNode | null; stream?: ast.ContentNode['stream'] }\n\n/**\n * How an operation streams its primary success response item by item, when that response declares one content type:\n * - `'event'` for `text/event-stream` whose `itemSchema` types the whole event with its own `event` and `data`\n * - `'data'` for `text/event-stream` typed by its payload only\n * - `'line'` for JSON Lines, one parsed JSON value per line\n *\n * Returns `null` for any other response, and for a response that also declares another content type, which stays a normal\n * call with its query hooks.\n */\nexport function getEventStream(node: ast.OperationNode): 'event' | 'data' | 'line' | null {\n const content = ast.http.responses.findPrimary(node)?.content ?? []\n if (content.length !== 1) return null\n const entry = content[0]!\n const framing = getStreamFraming(entry)\n if (framing === 'events') return entry.stream?.shape ?? 'data'\n return framing === 'lines' ? 'line' : null\n}\n\n/**\n * How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.\n */\nexport function getStreamFraming(entry: Pick<ast.ContentNode, 'contentType' | 'stream'>): 'events' | 'lines' | undefined {\n if (ast.mediaType.getBase(entry) === 'text/event-stream') return 'events'\n // `multipart/mixed` with an `itemSchema` also has `stream`, but it is a stream of parts, not of JSON values.\n return entry.stream && ast.mediaType.isStream(entry) ? 'lines' : undefined\n}\n\n/**\n * Whether an operation streams its primary success response, as Server-Sent Events or JSON Lines. The client generator uses\n * this to return a typed stream of items instead of a one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getEventStream(node) !== null\n}\n\n// A stream without a schema is a `blob` with a string primitive, so it keeps the JSON-parse default.\nfunction isTextSchema(schema: ast.SchemaNode | null | undefined): boolean {\n if (!schema) return false\n const resolved = ast.ref.getSchema(schema)\n return resolved.type !== 'blob' && resolved.primitive === 'string'\n}\n\n/**\n * Whether an operation streams Server-Sent Events whose `data` the document types as a string, so the client keeps each\n * `data` as text instead of JSON-parsing it. With an event-shaped `itemSchema` where only some events type `data` as a\n * string, it returns the names of those events, or `false` when one of them has no fixed name.\n */\nexport function hasTextEventData(node: ast.OperationNode): boolean | Array<string> {\n const shape = getEventStream(node)\n const entry = ast.http.responses.findPrimary(node)?.content?.[0]\n if (!entry || (shape !== 'data' && shape !== 'event')) return false\n if (shape === 'data') return isTextSchema(entry.stream?.schema ?? entry.schema)\n\n const item = entry.stream?.schema ? ast.ref.getSchema(entry.stream.schema) : undefined\n if (!item) return false\n const events = item.type === 'union' ? (ast.schema.narrow(item, 'union')?.members ?? []) : [item]\n // A discriminator makes each event an intersection of its `$ref` and the pinned `event`, so every part is read.\n const propertySchemas = (event: ast.SchemaNode, propertyName: string) => ast.schema.findProperties({ node: event, propertyName }).map(({ schema }) => schema)\n const textEvents = events.filter((event) => {\n const dataSchemas = propertySchemas(event, 'data')\n return dataSchemas.length > 0 && dataSchemas.every(isTextSchema)\n })\n if (textEvents.length === events.length) return events.length > 0\n\n const eventNames = textEvents.map((event) => propertySchemas(event, 'event').flatMap((schema) => ast.schema.getLiterals(ast.ref.getSchema(schema))))\n return eventNames.length > 0 && eventNames.every((values) => values.length > 0) && [...new Set(eventNames.flat().map(String))]\n}\n\n/**\n * Whether the primary success response offers `text/event-stream`, alone or next to other content types. The client asks\n * for the stream by default and ships the `sse.ts` runtime, so a mixed response can be read as events too.\n */\nexport function hasEventStreamContent(node: ast.OperationNode): boolean {\n const entries = ast.http.responses.findPrimary(node)?.content ?? []\n // Next to another type, a stream without a schema gets no typed variant, so it keeps the plain call.\n return entries.some((entry) => getStreamFraming(entry) === 'events' && (entries.length === 1 || Boolean(entry.schema)))\n}\n\n/**\n * Whether a success response offers a stream media type next to other content types. The client returns that variant's\n * body as a `ReadableStream`, so the stream runtime is registered and a hook's data type includes the stream.\n */\nexport function hasStreamVariant(node: ast.OperationNode): boolean {\n return ast.http.responses.filterSuccess(node).some((response) => {\n const variants = (response.content ?? []).filter((entry) => entry.schema)\n return variants.length > 1 && variants.some((entry) => ast.mediaType.isStream(entry))\n })\n}\n\nconst formContentTypes = ['multipart/form-data', 'application/x-www-form-urlencoded'] as const\n\n/**\n * The form media types the operation's request body offers, `multipart/form-data` and `application/x-www-form-urlencoded`,\n * which the client needs a handler for.\n */\nexport function getFormContentTypes(node: ast.OperationNode): Array<(typeof formContentTypes)[number]> {\n const request = new Set((node.requestBody?.content ?? []).map((entry) => ast.mediaType.getBase(entry)))\n return formContentTypes.filter((contentType) => request.has(contentType))\n}\n\n/**\n * Parser for one response content entry.\n *\n * JSON stays unset so the runtime detects it. `text/event-stream` and JSON Lines are streams. A binary schema\n * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known\n * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,\n * `image/*`, `audio/*`, and `video/*` are blobs.\n */\nfunction responseTypeForContent(entry: ResponseContentEntry): ResponseType | undefined {\n const baseType = ast.mediaType.getBase(entry)\n if (!baseType) return undefined\n if (getStreamFraming(entry)) return 'stream'\n if (ast.mediaType.isJson(baseType)) return undefined\n if (entry.schema?.type === 'blob') return 'blob'\n if (baseType.startsWith('text/')) return 'text'\n if (isBinaryContentType(baseType)) return 'blob'\n return undefined\n}\n\nfunction isBinaryContentType(baseType: string): boolean {\n return baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)\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 // Keeps the original case, unlike ast.mediaType.getBase, so generated names stay stable.\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 * Returns the entry with the schema of the value the client returns: `string` for a body read as text, such as `Pet` on\n * `application/xml`, and `blob` for `image/*`, `audio/*`, `video/*`, PDF, and octet-stream. A binary schema elsewhere is a `Blob`\n * only through `parseAs: 'blob'`, which needs every entry in `entries` binary. JSON, vendor (`application/vnd.*`), stream,\n * schemaless, and wildcard entries keep their schema. `entries` is the response's full list of content types.\n */\nexport function toResponseContent<T extends ContentVariantInput & ResponseContentEntry>(entry: T, entries: ReadonlyArray<ResponseContentEntry> = [entry]): T {\n const { contentType, schema } = entry\n if (!schema || entry.stream) return entry\n\n const baseType = ast.mediaType.getBase(contentType)\n const resolved = ast.ref.getSchema(schema)\n if (isBinaryContentType(baseType)) {\n return resolved.type === 'blob' ? entry : { ...entry, schema: ast.factory.createSchema({ type: 'blob' }), keysToOmit: null }\n }\n const isPlainMediaType = Boolean(baseType) && !ast.mediaType.isJson(baseType) && !baseType.includes('*')\n if (resolved.type === 'blob') {\n // shortcut: an error response never gets `parseAs`, so a lone binary schema there reads as text too but stays `Blob`.\n const readsAsText = isPlainMediaType && !entries.every((item) => item.schema?.type === 'blob')\n return readsAsText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n }\n if (resolved.primitive === 'string') return entry\n\n const responseType = responseTypeForContent(entry)\n // A response without a body, such as a 405, has no schema to read. Only `text/*` is always text.\n if (responseType !== 'text' && (resolved.type === 'unknown' || resolved.type === 'any' || resolved.type === 'void')) return entry\n const isVendorJson = /^[^/]+\\/vnd\\./.test(baseType) && !/(xml|yaml|yml|csv)$/.test(baseType)\n const isText = responseType === 'text' || (!responseType && isPlainMediaType && !isVendorJson)\n\n return isText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n}\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/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...): `RequestConfig` minus what the\n * hook fills in, which is every request group and `throwOnError`. Unlike {@link buildRequestConfigType},\n * it never narrows `contentType`: query hooks wrap GET operations, which carry no request body to select\n * a content type for.\n */\nexport function buildClientOptionType(options: { omit?: Array<string> } = {}): string {\n const keys = [...requestGroupOrder, 'url', ...(options.omit ?? []), 'throwOnError']\n return `Partial<Omit<RequestConfig, ${keys.map((key) => `'${key}'`).join(' | ')}>>`\n}\n\n/**\n * The `client?:` option type of a mutation hook. When the operation has a request content type choice,\n * the mutation variables carry `contentType`, so the config leaves it out. Otherwise the ambiguous side is\n * narrowed to the operation's media types; a single-type side has nothing to pick, so it stays baked in the\n * generated call.\n */\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n if (hasRequestContentTypeChoice(node)) {\n return buildClientOptionType({ omit: ['contentType'] })\n }\n\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n const base = buildClientOptionType()\n return members.length ? `${base} & { contentType?: { ${members.join('; ')} } }` : base\n}\n\n/** The grouped request options, in the order generated signatures and calls list them. */\nexport const requestGroupOrder = ['path', 'query', 'body', 'headers', 'cookies'] as const\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n cookies: 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, cookie } = ast.http.groupParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(ast.http.requestBody.getSchema(node)),\n headers: header.length > 0,\n cookies: cookie.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 } = ast.http.groupParameters(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 // `cookies` never makes the parameter required: a browser sends its cookies itself and cannot set a `Cookie` header.\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !(groups.body && node.requestBody?.required === true),\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, cookies }` 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; names: Array<keyof RequestGroups> } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = requestGroupOrder.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 names,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { splitLines = false } = options\n // The request line leads: free text after a block tag would belong to that tag.\n const comments = [\n getOperationRequestLine(node),\n node.description && `@description ${node.description}`,\n node.summary && `@summary ${node.summary}`,\n node.deprecated && '@deprecated',\n node.externalDocs && `@see ${jsDocEscape([node.externalDocs.url, node.externalDocs.description].filter(Boolean).join(' '))}`,\n ]\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\n/**\n * Whether the operation declares more than one request media type with a schema, so a caller must pick one on `contentType`.\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\n/**\n * Builds the combined `{ body, path, query, headers, cookies }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. `cookies` appears only on an operation\n * with a cookie parameter, and stays optional because a browser sends its cookies itself. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same shape from the same inputs.\n * `primitive: 'object'` is a no-op for the TS printer and tells the Zod printer to emit `z.object(…)` rather\n * than a record.\n */\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver, { pairContentTypes = false } = {}): ast.SchemaNode {\n const { path, query, header, cookie } = ast.http.groupParameters(node)\n const hasBody = Boolean(ast.http.requestBody.getSchema(node))\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 { name: 'cookies', params: cookie, resolve: resolver.param.cookies },\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 && node.requestBody?.required === true, schema: body }),\n ...groups\n .filter(({ name, params }) => name !== 'cookies' || params.length > 0)\n .map(({ name, params, resolve }) => {\n const required = name !== 'cookies' && 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`. A bare string sets the\n // request content type at runtime, so it is accepted next to the `{ request }` form.\n return ast.factory.createSchema({\n type: 'union',\n members: variants.map((variant, index) => {\n const contentType = ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] })\n\n return 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: 'union',\n members: [\n contentType,\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [ast.factory.createProperty({ name: 'request', required: true, schema: 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. With `streamData`, the stream variant of a 2xx status holds that schema, since the\n * client returns its body unread.\n */\nfunction buildResponseRecordEntry(\n node: ast.OperationNode,\n res: ast.ResponseNode,\n resolver: ResponseStatusNameResolver,\n streamData: ast.SchemaNode | undefined,\n): 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 // The runtime reports the bare media type, without parameters such as `charset` or `profile`.\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [ast.mediaType.getBase(variant)] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema:\n streamData && ast.http.status.isSuccess(res) && ast.mediaType.isStream(variant)\n ? streamData\n : 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 *\n * `streamData` is the type of a stream variant's `data`, such as a `ReadableStream<Uint8Array>` reference.\n */\nexport function buildResponses(\n node: ast.OperationNode,\n resolver: ResponseStatusNameResolver,\n { streamData }: { streamData?: ast.SchemaNode } = {},\n): 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, streamData),\n }),\n ),\n })\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterError(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterSuccess(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\n/**\n * Lists the type names an operation's types file declares, computed once per resolver and `options` so every\n * dependent reads the same list.\n */\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver & Pick<Resolver, 'memo'>,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const key = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n return resolver.memo(`operationTypeNames:${key}`, () => {\n const { path, query, header, cookie } = ast.http.groupParameters(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 ...cookie.map((param) => resolver.param.cookies(node, param)),\n ]\n const bodyAndResponseNames = [ast.http.requestBody.getSchema(node) ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n return names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n })\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = Number(response.statusCode)\n if (Number.isNaN(code)) {\n continue\n }\n\n types.push([code, ast.http.status.isSuccess(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","import { ensureValidVarName, toFilePath } from '@internals/utils'\nimport { ast, casing } from '@kubb/kit'\nimport type { GeneratorContext, Resolver, ResolverFile } from '@kubb/kit'\n\n/**\n * The shared top-level `name` of the operation plugins, computed once per operation name and build.\n */\nexport function resolveOperationName(this: Resolver, name: string): string {\n return this.memo(`operation:${name}`, () => ensureValidVarName(casing.camel(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 * The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro\n * rewrites the method or path still resolves another plugin's names for the same operation.\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 const source = node.pointer ? [node.pointer] : [node.method, node.path]\n return ['operation', ...source, ...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`/`Cookies` 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 cookies(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 /**\n * Name of the headers a response declares, such as `ListPetsStatus200Headers`.\n */\n headers(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 cookies(node) {\n const name = this.name(`${node.operationId} Cookies`)\n return this.resolveName({ key: operationKey(node, 'param', 'cookies'), 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 headers(node, statusCode) {\n const name = this.name(`${node.operationId} Status ${statusCode} Headers`)\n return this.resolveName({ key: operationKey(node, 'response', 'headers', statusCode), name })\n },\n body(node) {\n const name = this.name(`${node.operationId} Body`)\n return this.resolveName({ key: operationKey(node, 'response', 'body'), name })\n },\n responses(node) {\n const name = this.name(`${node.operationId} Responses`)\n return this.resolveName({ key: operationKey(node, 'response', 'responses'), name })\n },\n response(node) {\n const name = this.name(`${node.operationId} Response`)\n return this.resolveName({ key: operationKey(node, 'response', 'response'), name })\n },\n }\n}\n\n/**\n * Builds a resolver `file` override whose base name runs every path segment\n * through `toFilePath`, casing the final segment with `caseLast`.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })\n * ```\n */\nexport function createCasedFile(caseLast: (part: string) => string): ResolverFile {\n return {\n baseName({ name, extname }) {\n return this.memo(`file:${name}\\0${extname}`, (): ast.FileNode['baseName'] => `${toFilePath(name, caseLast)}${extname}`)\n },\n }\n}\n\ntype SchemaIndexContext = Pick<GeneratorContext, 'memo' | 'driver'>\n\n/**\n * Names of the schemas in a circular reference chain, computed once per build.\n */\nexport function getCircularNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo('circularNames:set', () => ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []))\n}\n\n/**\n * Names of the schemas whose type is `enum`, computed once per build.\n */\nexport function getEnumNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo(\n 'enumNames:set',\n () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => (schema.type === 'enum' && schema.name ? [schema.name] : []))),\n )\n}\n\ndeclare global {\n namespace Kubb {\n interface MemoRegistry {\n operation: string\n file: ast.FileNode['baseName']\n operationTypeNames: Array<string>\n }\n }\n}\n","import { isValidVarName, toLiteral } from '@internals/utils'\nimport { toResponseContent } from '@internals/shared'\n\nimport { ast, text } from '@kubb/kit'\n\n// Headers use the `simple` style, so an array or object is sent comma-separated.\nfunction toSimpleStyle(value: unknown): string {\n if (Array.isArray(value)) return value.join(',')\n if (typeof value === 'object' && value !== null) return Object.entries(value).flat().join(',')\n return String(value)\n}\n\n/**\n * The declared headers a mock sends, each with its example's `serializedValue` or `value`, else its schema's `default` or first example.\n */\nexport function getMockHeaders(response: ast.ResponseNode | null | undefined): Array<[name: string, value: string]> {\n return (response?.headers ?? []).flatMap((header): Array<[string, string]> => {\n // OpenAPI ignores a `Content-Type` response header, and the mock already sends one.\n if (header.name.toLowerCase() === 'content-type') return []\n const example = header.examples?.find((entry) => entry.serializedValue !== undefined || entry.value !== undefined)\n const headerValue = example?.serializedValue ?? example?.value ?? header.schema.default ?? header.schema.examples?.[0]\n if (headerValue === undefined || headerValue === null) return []\n const headerText = toSimpleStyle(headerValue)\n // `Headers` throws on a line break or a character above U+00FF, so such an example is left out.\n return /^[\\t\\x20-\\x7E\\x80-\\xFF]*$/.test(headerText) ? [[header.name, headerText]] : []\n })\n}\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 contentType = getResponseEntry(response)?.contentType\n return typeof contentType === 'string' && contentType.length > 0 ? contentType : null\n}\n\nfunction getResponseEntry(response: ast.ResponseNode | null | undefined): ast.ContentNode | undefined {\n const contents = response?.content ?? []\n return contents.find((entry) => ast.mediaType.isJson(entry)) ?? contents[0]\n}\n\n/**\n * The first content type of a response that the client reads as text, such as `application/xml`, or `null` when none is.\n */\nfunction getTextContentType(response: ast.ResponseNode | null | undefined): string | null {\n const entry = (response?.content ?? []).find((entry) => {\n const schema = toResponseContent(entry, response?.content).schema\n return !ast.mediaType.isJson(entry) && schema && ast.ref.getSchema(schema).primitive === 'string'\n })\n return entry?.contentType ?? null\n}\n\n/**\n * The statements that return a mocked `Response` for `body`. A string body for a response the client reads as text goes\n * out as raw text with that content type, so an XML mock is not sent as a quoted JSON string. Any other body is JSON.\n */\nexport function responseReturn(body: string, status: number, response: ast.ResponseNode | null | undefined): string {\n const contentType = getContentType(response)\n const headers = getMockHeaders(response)\n // A plain variable such as `data` is checked as it is, an expression such as `data ?? createPet()` is read once.\n const name = isValidVarName(body) ? body : 'body'\n const prelude = name === body ? '' : `const body = ${body}\\n `\n\n // shortcut: one event per mock, upgrade when a mock needs to send several events.\n if (contentType && ast.mediaType.getBase(contentType) === 'text/event-stream') {\n const entry = getResponseEntry(response)\n // Without a body, a spec example already serialized as SSE text goes out as it is.\n const serialized = entry?.examples?.find((example) => example.serializedValue !== undefined)?.serializedValue\n const fallback = serialized === undefined ? \"''\" : toLiteral(serialized)\n if (entry?.stream?.shape !== 'event') {\n return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \\`\\${${eventDataLines(name)}}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n // The item is the whole event, so its fields become SSE lines and the client parses it back to the same object.\n return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined\n const lines = [\n ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \\`\\${field}: \\${event?.[field]}\\`),\n // A client dispatches an event only once it has a \\`data\\` line, so an event without data still sends an empty one.\n event?.data === undefined ? 'data:' : ${eventDataLines('event.data')},\n ]\n\n return ${responseLiteral(`event === undefined ? ${fallback} : \\`\\${lines.join('\\\\n')}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n\n const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null\n if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType, headers)}`\n\n return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}\n\n return ${responseLiteral(`JSON.stringify(${name})`, status, contentType, headers)}`\n}\n\n/**\n * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.\n */\nfunction eventDataLines(body: string): string {\n return `(typeof ${body} === 'string' ? ${body} : JSON.stringify(${body})).split('\\\\n').map((line) => \\`data: \\${line}\\`).join('\\\\n')`\n}\n\n/**\n * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.\n */\nexport function getExampleBody(response: ast.ResponseNode | null | undefined): string | null {\n const entry = getResponseEntry(response)\n const example = entry?.examples?.find((item) => item.value !== undefined || item.serializedValue !== undefined)\n if (!entry || !example) return null\n if (example.value !== undefined) return toLiteral(example.value)\n // `responseReturn` sends raw text only for a text response. For a binary one the example would go out JSON-encoded.\n // A serialized SSE example is already events, which `responseReturn` sends as is when the body is `undefined`.\n if (!ast.mediaType.isJson(entry)) {\n const isTextBody = ast.mediaType.getBase(entry) !== 'text/event-stream' && getTextContentType(response) === entry.contentType\n return isTextBody ? toLiteral(example.serializedValue) : null\n }\n // `responseReturn` encodes a JSON body itself, so a serialized JSON example is parsed first.\n try {\n return toLiteral(JSON.parse(example.serializedValue!))\n } catch {\n return null\n }\n}\n\nconst mswMethods = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no `http.query`, `http.trace` or\n * handler for a custom `additionalOperations` method, so such an operation maps to `http.all` and its handler checks the\n * method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return mswMethods.has(node.method) ? node.method.toLowerCase() : 'all'\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no header to send.\n */\nfunction headersBlock(contentType: string | null, headers: Array<[string, string]>): string {\n const entries = [\n ...(contentType ? [`'Content-Type': ${text.quote(contentType)}`] : []),\n ...headers.map(([name, value]) => `${text.quote(name)}: ${text.quote(value)}`),\n ]\n return entries.length ? `\\n headers: {\\n ${entries.join(',\\n ')}\\n },` : ''\n}\n\n/**\n * The `new HttpResponse<string>(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null, headers: Array<[string, string]>): string {\n return `new HttpResponse<string>(${body}, {\n status: ${status},${headersBlock(contentType, headers)}\n })`\n}\n","import { ast, text } from '@kubb/kit'\nimport { Url } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { getExampleBody, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseReturn } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n /**\n * The `...Response<status>` helper of the response this handler mocks, when its `data` has the handler's type.\n */\n responseName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, responseName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so a handler for a method MSW has no helper for lets the others pass through.\n const methodGuard = method === 'all' ? `if (info.request.method !== ${text.quote(node.method)}) return\\n\\n ` : ''\n const successResponse = ast.http.responses.findPrimary(node)\n const statusCode = successResponse ? ast.http.status.toCode(successResponse) : 200\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 // Without Faker, the spec's own response example is the default body. An explicit `null` stays a `null` body.\n const example = fakerName ? null : getExampleBody(successResponse)\n const exampleBody = example ? `data === undefined ? ${example} : data` : 'data'\n const responseBody = fakerName ? `data ?? ${fakerName}()` : exampleBody\n // A spec example is not checked against the response type, so it is not passed to the typed helper.\n const responseStatements = responseName && !example ? `return ${responseName}(${responseBody})` : responseReturn(responseBody, statusCode, successResponse)\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 ${responseStatements}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import { ast } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { responseReturn } 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 // Optional, since a handler passes on the `data` it got, which may be left out.\n const params = `data?: ${typeName}`\n\n const responseName = `${name}Response${response.statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n ${responseReturn('data', ast.http.status.toCode(response), response)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getEnumNames, resolveOperationFile } 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/renderer-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.http.isOperation(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: resolveOperationFile({\n ctx,\n node,\n resolver: fakerResolver,\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: resolveOperationFile({\n ctx,\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 = ast.http.responses.filterSuccess(node)\n const referencedNames = ast.http.responses.getImportedNames(node.responses)\n const enumOptions = pluginTs.options?.enum\n const enumNames = getEnumNames(ctx)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n // One helper per documented status, a range such as `4xx` included. `default` has no status to mock,\n // and the Fetch `Response` constructor rejects a `1xx` status.\n const types = node.responses\n .filter((response) => response.statusCode !== 'default' && ast.http.status.getClass(response) !== 1)\n .map((response) => {\n const isResponseType = ast.http.status.isSuccess(response) && !hasResponseNameCollision\n return [response, isResponseType ? type.responseName : tsResolver.response.status(node, response.statusCode)] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName)\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 // The handler calls the helper of the response it mocks when that helper takes the handler's own data type.\n const primaryResponse = ast.http.responses.findPrimary(node)\n const responseHelperName =\n primaryResponse &&\n hasResponseSchema(primaryResponse) &&\n types.some(([response, typeName]) => response.statusCode === primaryResponse.statusCode && typeName === mockResponseName)\n ? `${mock.name}Response${primaryResponse.statusCode}`\n : undefined\n\n const requestName = ast.http.requestBody.getSchema(node) ? 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.map(([response, typeName]) => (\n <Response key={response.statusCode} 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 responseName={responseHelperName}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { operationKey } from '@internals/shared'\nimport { casing, createResolver } from '@kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return this.memo(`name:${name}`, () => casing.camel(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 { 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: group ?? null,\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","casing","ast","text","ast","ast","text","text","ast","Url","File","Function","File","Function","ast","defineGenerator","jsxRenderer","ast","pluginFakerName","pluginTsName","enumTypeName","File","createResolver","casing","defineExtension","pluginTsName","pluginFakerName","Resolver"],"sources":["../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../../../internals/utils/src/reserved.ts","../../../internals/utils/src/codegen.ts","../../../internals/shared/src/operation.ts","../../../internals/shared/src/resolver.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, casing, 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: casing.camel(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","/**\n * JavaScript and Java reserved words.\n * @link https://github.com/jonschlinkert/reserved/blob/master/index.js\n */\nconst reservedWords = new Set([\n 'abstract',\n 'arguments',\n 'await',\n 'boolean',\n 'break',\n 'byte',\n 'case',\n 'catch',\n 'char',\n 'class',\n 'const',\n 'continue',\n 'debugger',\n 'default',\n 'delete',\n 'do',\n 'double',\n 'else',\n 'enum',\n 'eval',\n 'export',\n 'extends',\n 'false',\n 'final',\n 'finally',\n 'float',\n 'for',\n 'function',\n 'goto',\n 'if',\n 'implements',\n 'import',\n 'in',\n 'instanceof',\n 'int',\n 'interface',\n 'let',\n 'long',\n 'native',\n 'new',\n 'null',\n 'package',\n 'private',\n 'protected',\n 'public',\n 'return',\n 'short',\n 'static',\n 'super',\n 'switch',\n 'synchronized',\n 'this',\n 'throw',\n 'throws',\n 'transient',\n 'true',\n 'try',\n 'typeof',\n 'var',\n 'void',\n 'volatile',\n 'while',\n 'with',\n 'yield',\n 'Array',\n 'Date',\n 'hasOwnProperty',\n 'Infinity',\n 'isFinite',\n 'isNaN',\n 'isPrototypeOf',\n 'length',\n 'Math',\n 'name',\n 'NaN',\n 'Number',\n 'Object',\n 'prototype',\n 'String',\n 'toString',\n 'undefined',\n 'valueOf',\n] as const)\n\n/**\n * Returns `true` when `name` is a syntactically valid JavaScript variable name.\n *\n * @example\n * ```ts\n * isValidVarName('status') // true\n * isValidVarName('class') // false (reserved word)\n * isValidVarName('42foo') // false (starts with digit)\n * ```\n */\nexport function isValidVarName(name: string): boolean {\n if (!name || reservedWords.has(name as 'valueOf')) {\n return false\n }\n return isIdentifier(name)\n}\n\n/**\n * Returns `name` when it's a syntactically valid JavaScript variable name,\n * otherwise prefixes it with `_` so the result is a valid identifier.\n *\n * Useful for sanitizing OpenAPI schema names or operation IDs that start with\n * a digit (e.g. `409`, `504AccountCancel`) before using them as exported\n * variable, type, or function names.\n *\n * @example\n * ```ts\n * ensureValidVarName('409') // '_409'\n * ensureValidVarName('504AccountCancel') // '_504AccountCancel'\n * ensureValidVarName('Pet') // 'Pet'\n * ensureValidVarName('class') // '_class'\n * ```\n */\nexport function ensureValidVarName(name: string): string {\n if (!name || isValidVarName(name)) {\n return name\n }\n return `_${name}`\n}\n\n/**\n * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.\n *\n * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys\n * even though they are not valid variable names, so use this (not {@link isValidVarName}) when\n * deciding whether an object key needs quoting.\n *\n * @example\n * ```ts\n * isIdentifier('name') // true\n * isIdentifier('x-total')// false\n * ```\n */\nexport function isIdentifier(name: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)\n}\n","import { text } from '@kubb/kit'\nimport { isIdentifier } from './reserved.ts'\n\n/**\n * Builds a JSDoc comment block from an array of lines. Returns `fallback` when there are no\n * comments.\n *\n * @example\n * ```ts\n * buildJSDoc(['@type string', '@example hello'])\n * // '/**\\n * @type string\\n * @example hello\\n *\\/\\n '\n * ```\n */\nexport function buildJSDoc(\n comments: Array<string>,\n options: {\n /**\n * String used to indent each comment line.\n * @default ' * '\n */\n indent?: string\n /**\n * String appended after the closing tag.\n * @default '\\n '\n */\n suffix?: string\n /**\n * Returned as-is when `comments` is empty.\n * @default ' '\n */\n fallback?: string\n } = {},\n): string {\n const { indent = ' * ', suffix = '\\n ', fallback = ' ' } = options\n\n if (comments.length === 0) return fallback\n\n return `/**\\n${comments.map((c) => `${indent}${c}`).join('\\n')}\\n */${suffix}`\n}\n\n/**\n * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one\n * level and closing the brace at column zero. Entries that are themselves multi-line objects indent\n * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.\n *\n * @example\n * ```ts\n * buildObject(['id: z.number()', 'name: z.string()'])\n * // '{\\n id: z.number(),\\n name: z.string(),\\n}'\n * ```\n */\nexport function buildObject(entries: Array<string>): string {\n if (entries.length === 0) return '{}'\n const body = entries.map((entry) => `${text.indent({ text: entry })},`).join('\\n')\n\n return `{\\n${body}\\n}`\n}\n\n/**\n * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on\n * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented\n * one level with a trailing comma and the closing bracket at column zero. Used for member lists such\n * as `z.union([…])` and `z.array([…])`.\n *\n * @example\n * ```ts\n * buildList(['z.string()', 'z.number()'])\n * // '[z.string(), z.number()]'\n * ```\n */\nexport function buildList(items: Array<string>, brackets: [open: string, close: string] = ['[', ']']): string {\n const [open, close] = brackets\n if (items.length === 0) return `${open}${close}`\n if (!items.some((item) => item.includes('\\n'))) return `${open}${items.join(', ')}${close}`\n const body = items.map((item) => `${text.indent({ text: item })},`).join('\\n')\n\n return `${open}\\n${body}\\n${close}`\n}\n\n/**\n * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key\n * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation\n * of a recursive schema until first access.\n *\n * @example\n * ```ts\n * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })\n * // \"get parent() { return z.lazy(() => Pet) }\"\n * ```\n */\nexport function lazyGetter({ name, body }: { name: string; body: string }): string {\n return `get ${objectKey(name)}() { return ${body} }`\n}\n\n/**\n * An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.\n *\n * @example\n * ```ts\n * objectKey('x-total') // \"'x-total'\"\n * ```\n */\nexport function objectKey(name: string): string {\n return isIdentifier(name) ? name : text.quote(name)\n}\n\n/**\n * A JSON value as a JavaScript literal, with single-quoted strings and identifier keys left unquoted.\n *\n * @example\n * ```ts\n * toLiteral({ id: 1, 'x-tag': ['a'] }) // \"{ id: 1, 'x-tag': ['a'] }\"\n * ```\n */\nexport function toLiteral(value: unknown): string {\n if (typeof value === 'string') return text.quote(value)\n if (Array.isArray(value)) return `[${value.map(toLiteral).join(', ')}]`\n if (value && typeof value === 'object') {\n return `{ ${Object.entries(value)\n .map(([key, item]) => `${objectKey(key)}: ${toLiteral(item)}`)\n .join(', ')} }`\n }\n return String(value)\n}\n","import { jsDocEscape } from '@internals/utils'\nimport { ast, type Group, type Memo, type Output, type Resolver, type ResolverFileParams } 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 the file a schema-emitting plugin (`plugin-ts`, `plugin-zod`, `plugin-faker`) writes\n * `node` to, cached in `ctx.memo` under the resolver's plugin name and the\n * operation. The plugin itself and every plugin that imports from it call this, so they all get\n * the same path.\n *\n * When that path matches the file of a component schema in `ctx.driver.inputNode`, ignoring case, the file name\n * gets an `Operation` suffix: operation `point` goes to `PointOperation.ts` next to schema `Point.ts`.\n * Otherwise the two merge into one file, or overwrite each other on a case-insensitive file system.\n * Only the file name changes. The declarations keep their names.\n *\n * @example Resolve `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveOperationFile({ ctx, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveOperationFile(options: {\n ctx: { memo: Memo; driver: { inputNode?: { schemas: ReadonlyArray<ast.SchemaNode> } | null } }\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 { ctx, node, resolver, root, output } = options\n const { memo } = ctx\n const location = { root, output, group: options.group ?? undefined }\n\n return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {\n const schemaPaths = memo(\n `${resolver.pluginName}:schemaFilePaths`,\n () =>\n new Set(\n (ctx.driver.inputNode?.schemas ?? []).flatMap((schema) =>\n schema.name ? [resolver.file({ name: schema.name, extname: '.ts', ...location }).path.toLowerCase()] : [],\n ),\n ),\n )\n let file = resolver.file({ ...operationFileEntry(node, node.operationId), ...location })\n\n for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += 'Operation') {\n const next = resolver.file({ ...operationFileEntry(node, name), ...location })\n // A single-file output holds every declaration on purpose, so a new name changes nothing.\n if (next.path === file.path) break\n file = next\n }\n\n return file\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 cookies(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type BuildOperationCommentsOptions = {\n splitLines?: boolean\n}\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\n/**\n * The operation's method and path as plain text, such as `` `GET /pet/{petId}` ``. A path is not a valid `{@link}` target.\n */\nfunction getOperationRequestLine(node: ast.OperationNode): string | null {\n return ast.http.isOperation(node) && node.path ? `\\`${node.method.toUpperCase()} ${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(ast.http.responses.findPrimary(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\ntype ResponseType = 'blob' | 'text' | 'stream'\n\ntype ResponseContentEntry = { contentType: string; schema?: ast.SchemaNode | null; stream?: ast.ContentNode['stream'] }\n\n/**\n * How an operation streams its primary success response item by item, when that response declares one content type:\n * - `'event'` for `text/event-stream` whose `itemSchema` types the whole event with its own `event` and `data`\n * - `'data'` for `text/event-stream` typed by its payload only\n * - `'line'` for JSON Lines, one parsed JSON value per line\n *\n * Returns `null` for any other response, and for a response that also declares another content type, which stays a normal\n * call with its query hooks.\n */\nexport function getEventStream(node: ast.OperationNode): 'event' | 'data' | 'line' | null {\n const content = ast.http.responses.findPrimary(node)?.content ?? []\n if (content.length !== 1) return null\n const entry = content[0]!\n const framing = getStreamFraming(entry)\n if (framing === 'events') return entry.stream?.shape ?? 'data'\n return framing === 'lines' ? 'line' : null\n}\n\n/**\n * How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.\n */\nexport function getStreamFraming(entry: Pick<ast.ContentNode, 'contentType' | 'stream'>): 'events' | 'lines' | undefined {\n if (ast.mediaType.getBase(entry) === 'text/event-stream') return 'events'\n // `multipart/mixed` with an `itemSchema` also has `stream`, but it is a stream of parts, not of JSON values.\n return entry.stream && ast.mediaType.isStream(entry) ? 'lines' : undefined\n}\n\n/**\n * Whether an operation streams its primary success response, as Server-Sent Events or JSON Lines. The client generator uses\n * this to return a typed stream of items instead of a one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getEventStream(node) !== null\n}\n\n// A stream without a schema is a `blob` with a string primitive, so it keeps the JSON-parse default.\nfunction isTextSchema(schema: ast.SchemaNode | null | undefined): boolean {\n if (!schema) return false\n const resolved = ast.ref.getSchema(schema)\n return resolved.type !== 'blob' && resolved.primitive === 'string'\n}\n\n/**\n * Whether an operation streams Server-Sent Events whose `data` the document types as a string, so the client keeps each\n * `data` as text instead of JSON-parsing it. With an event-shaped `itemSchema` where only some events type `data` as a\n * string, it returns the names of those events, or `false` when one of them has no fixed name.\n */\nexport function hasTextEventData(node: ast.OperationNode): boolean | Array<string> {\n const shape = getEventStream(node)\n const entry = ast.http.responses.findPrimary(node)?.content?.[0]\n if (!entry || (shape !== 'data' && shape !== 'event')) return false\n if (shape === 'data') return isTextSchema(entry.stream?.schema ?? entry.schema)\n\n const item = entry.stream?.schema ? ast.ref.getSchema(entry.stream.schema) : undefined\n if (!item) return false\n const events = item.type === 'union' ? (ast.schema.narrow(item, 'union')?.members ?? []) : [item]\n // A discriminator makes each event an intersection of its `$ref` and the pinned `event`, so every part is read.\n const propertySchemas = (event: ast.SchemaNode, propertyName: string) => ast.schema.findProperties({ node: event, propertyName }).map(({ schema }) => schema)\n const textEvents = events.filter((event) => {\n const dataSchemas = propertySchemas(event, 'data')\n return dataSchemas.length > 0 && dataSchemas.every(isTextSchema)\n })\n if (textEvents.length === events.length) return events.length > 0\n\n const eventNames = textEvents.map((event) => propertySchemas(event, 'event').flatMap((schema) => ast.schema.getLiterals(ast.ref.getSchema(schema))))\n return eventNames.length > 0 && eventNames.every((values) => values.length > 0) && [...new Set(eventNames.flat().map(String))]\n}\n\n/**\n * Whether the primary success response offers `text/event-stream`, alone or next to other content types. The client asks\n * for the stream by default and ships the `sse.ts` runtime, so a mixed response can be read as events too.\n */\nexport function hasEventStreamContent(node: ast.OperationNode): boolean {\n const entries = ast.http.responses.findPrimary(node)?.content ?? []\n // Next to another type, a stream without a schema gets no typed variant, so it keeps the plain call.\n return entries.some((entry) => getStreamFraming(entry) === 'events' && (entries.length === 1 || Boolean(entry.schema)))\n}\n\n/**\n * Whether a success response offers a stream media type next to other content types. The client returns that variant's\n * body as a `ReadableStream`, so the stream runtime is registered and a hook's data type includes the stream.\n */\nexport function hasStreamVariant(node: ast.OperationNode): boolean {\n return ast.http.responses.filterSuccess(node).some((response) => {\n const variants = (response.content ?? []).filter((entry) => entry.schema)\n return variants.length > 1 && variants.some((entry) => ast.mediaType.isStream(entry))\n })\n}\n\nconst formContentTypes = ['multipart/form-data', 'application/x-www-form-urlencoded'] as const\n\n/**\n * The form media types the operation's request body offers, `multipart/form-data` and `application/x-www-form-urlencoded`,\n * which the client needs a handler for.\n */\nexport function getFormContentTypes(node: ast.OperationNode): Array<(typeof formContentTypes)[number]> {\n const request = new Set((node.requestBody?.content ?? []).map((entry) => ast.mediaType.getBase(entry)))\n return formContentTypes.filter((contentType) => request.has(contentType))\n}\n\n/**\n * Parser for one response content entry.\n *\n * JSON stays unset so the runtime detects it. `text/event-stream` and JSON Lines are streams. A binary schema\n * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known\n * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,\n * `image/*`, `audio/*`, and `video/*` are blobs.\n */\nfunction responseTypeForContent(entry: ResponseContentEntry): ResponseType | undefined {\n const baseType = ast.mediaType.getBase(entry)\n if (!baseType) return undefined\n if (getStreamFraming(entry)) return 'stream'\n if (ast.mediaType.isJson(baseType)) return undefined\n if (entry.schema?.type === 'blob') return 'blob'\n if (baseType.startsWith('text/')) return 'text'\n if (isBinaryContentType(baseType)) return 'blob'\n return undefined\n}\n\nfunction isBinaryContentType(baseType: string): boolean {\n return baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)\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 // Keeps the original case, unlike ast.mediaType.getBase, so generated names stay stable.\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 * Returns the entry with the schema of the value the client returns: `string` for a body read as text, such as `Pet` on\n * `application/xml`, and `blob` for `image/*`, `audio/*`, `video/*`, PDF, and octet-stream. A binary schema elsewhere is a `Blob`\n * only through `parseAs: 'blob'`, which needs every entry in `entries` binary. JSON, vendor (`application/vnd.*`), stream,\n * schemaless, and wildcard entries keep their schema. `entries` is the response's full list of content types.\n */\nexport function toResponseContent<T extends ContentVariantInput & ResponseContentEntry>(entry: T, entries: ReadonlyArray<ResponseContentEntry> = [entry]): T {\n const { contentType, schema } = entry\n if (!schema || entry.stream) return entry\n\n const baseType = ast.mediaType.getBase(contentType)\n const resolved = ast.ref.getSchema(schema)\n if (isBinaryContentType(baseType)) {\n return resolved.type === 'blob' ? entry : { ...entry, schema: ast.factory.createSchema({ type: 'blob' }), keysToOmit: null }\n }\n const isPlainMediaType = Boolean(baseType) && !ast.mediaType.isJson(baseType) && !baseType.includes('*')\n if (resolved.type === 'blob') {\n // shortcut: an error response never gets `parseAs`, so a lone binary schema there reads as text too but stays `Blob`.\n const readsAsText = isPlainMediaType && !entries.every((item) => item.schema?.type === 'blob')\n return readsAsText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n }\n if (resolved.primitive === 'string') return entry\n\n const responseType = responseTypeForContent(entry)\n // A response without a body, such as a 405, has no schema to read. Only `text/*` is always text.\n if (responseType !== 'text' && (resolved.type === 'unknown' || resolved.type === 'any' || resolved.type === 'void')) return entry\n const isVendorJson = /^[^/]+\\/vnd\\./.test(baseType) && !/(xml|yaml|yml|csv)$/.test(baseType)\n const isText = responseType === 'text' || (!responseType && isPlainMediaType && !isVendorJson)\n\n return isText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n}\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/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...): `RequestConfig` minus what the\n * hook fills in, which is every request group and `throwOnError`. Unlike {@link buildRequestConfigType},\n * it never narrows `contentType`: query hooks wrap GET operations, which carry no request body to select\n * a content type for.\n */\nexport function buildClientOptionType(options: { omit?: Array<string> } = {}): string {\n const keys = [...requestGroupOrder, 'url', ...(options.omit ?? []), 'throwOnError']\n return `Partial<Omit<RequestConfig, ${keys.map((key) => `'${key}'`).join(' | ')}>>`\n}\n\n/**\n * The `client?:` option type of a mutation hook. When the operation has a request content type choice,\n * the mutation variables carry `contentType`, so the config leaves it out. Otherwise the ambiguous side is\n * narrowed to the operation's media types; a single-type side has nothing to pick, so it stays baked in the\n * generated call.\n */\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n if (hasRequestContentTypeChoice(node)) {\n return buildClientOptionType({ omit: ['contentType'] })\n }\n\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n const base = buildClientOptionType()\n return members.length ? `${base} & { contentType?: { ${members.join('; ')} } }` : base\n}\n\n/** The grouped request options, in the order generated signatures and calls list them. */\nexport const requestGroupOrder = ['path', 'query', 'body', 'headers', 'cookies'] as const\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n cookies: 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, cookie } = ast.http.groupParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(ast.http.requestBody.getSchema(node)),\n headers: header.length > 0,\n cookies: cookie.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 } = ast.http.groupParameters(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 // `cookies` never makes the parameter required: a browser sends its cookies itself and cannot set a `Cookie` header.\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !(groups.body && node.requestBody?.required === true),\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, cookies }` 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; names: Array<keyof RequestGroups> } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = requestGroupOrder.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 names,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { splitLines = false } = options\n // The request line leads: free text after a block tag would belong to that tag.\n const comments = [\n getOperationRequestLine(node),\n node.description && `@description ${node.description}`,\n node.summary && `@summary ${node.summary}`,\n node.deprecated && '@deprecated',\n node.externalDocs && `@see ${jsDocEscape([node.externalDocs.url, node.externalDocs.description].filter(Boolean).join(' '))}`,\n ]\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\n/**\n * Whether the operation declares more than one request media type with a schema, so a caller must pick one on `contentType`.\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\n/**\n * Builds the combined `{ body, path, query, headers, cookies }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. `cookies` appears only on an operation\n * with a cookie parameter, and stays optional because a browser sends its cookies itself. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same shape from the same inputs.\n * `primitive: 'object'` is a no-op for the TS printer and tells the Zod printer to emit `z.object(…)` rather\n * than a record.\n */\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver, { pairContentTypes = false } = {}): ast.SchemaNode {\n const { path, query, header, cookie } = ast.http.groupParameters(node)\n const hasBody = Boolean(ast.http.requestBody.getSchema(node))\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 { name: 'cookies', params: cookie, resolve: resolver.param.cookies },\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 && node.requestBody?.required === true, schema: body }),\n ...groups\n .filter(({ name, params }) => name !== 'cookies' || params.length > 0)\n .map(({ name, params, resolve }) => {\n const required = name !== 'cookies' && 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`. A bare string sets the\n // request content type at runtime, so it is accepted next to the `{ request }` form.\n return ast.factory.createSchema({\n type: 'union',\n members: variants.map((variant, index) => {\n const contentType = ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] })\n\n return 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: 'union',\n members: [\n contentType,\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [ast.factory.createProperty({ name: 'request', required: true, schema: 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. With `streamData`, the stream variant of a 2xx status holds that schema, since the\n * client returns its body unread.\n */\nfunction buildResponseRecordEntry(\n node: ast.OperationNode,\n res: ast.ResponseNode,\n resolver: ResponseStatusNameResolver,\n streamData: ast.SchemaNode | undefined,\n): 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 // The runtime reports the bare media type, without parameters such as `charset` or `profile`.\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [ast.mediaType.getBase(variant)] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema:\n streamData && ast.http.status.isSuccess(res) && ast.mediaType.isStream(variant)\n ? streamData\n : 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 *\n * `streamData` is the type of a stream variant's `data`, such as a `ReadableStream<Uint8Array>` reference.\n */\nexport function buildResponses(\n node: ast.OperationNode,\n resolver: ResponseStatusNameResolver,\n { streamData }: { streamData?: ast.SchemaNode } = {},\n): 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, streamData),\n }),\n ),\n })\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterError(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterSuccess(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\n/**\n * Lists the type names an operation's types file declares, computed once per resolver and `options` so every\n * dependent reads the same list.\n */\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver & Pick<Resolver, 'memo'>,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const key = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n return resolver.memo(`operationTypeNames:${key}`, () => {\n const { path, query, header, cookie } = ast.http.groupParameters(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 ...cookie.map((param) => resolver.param.cookies(node, param)),\n ]\n const bodyAndResponseNames = [ast.http.requestBody.getSchema(node) ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n return names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n })\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = Number(response.statusCode)\n if (Number.isNaN(code)) {\n continue\n }\n\n types.push([code, ast.http.status.isSuccess(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","import { ensureValidVarName, toFilePath } from '@internals/utils'\nimport { ast, casing } from '@kubb/kit'\nimport type { GeneratorContext, Resolver, ResolverFile } from '@kubb/kit'\n\n/**\n * The shared top-level `name` of the operation plugins, computed once per operation name and build.\n */\nexport function resolveOperationName(this: Resolver, name: string): string {\n return this.memo(`operation:${name}`, () => ensureValidVarName(casing.camel(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 * The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro\n * rewrites the method or path still resolves another plugin's names for the same operation.\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 const source = node.pointer ? [node.pointer] : [node.method, node.path]\n return ['operation', ...source, ...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`/`Cookies` 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 cookies(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 /**\n * Name of the headers a response declares, such as `ListPetsStatus200Headers`.\n */\n headers(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 cookies(node) {\n const name = this.name(`${node.operationId} Cookies`)\n return this.resolveName({ key: operationKey(node, 'param', 'cookies'), 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 headers(node, statusCode) {\n const name = this.name(`${node.operationId} Status ${statusCode} Headers`)\n return this.resolveName({ key: operationKey(node, 'response', 'headers', statusCode), name })\n },\n body(node) {\n const name = this.name(`${node.operationId} Body`)\n return this.resolveName({ key: operationKey(node, 'response', 'body'), name })\n },\n responses(node) {\n const name = this.name(`${node.operationId} Responses`)\n return this.resolveName({ key: operationKey(node, 'response', 'responses'), name })\n },\n response(node) {\n const name = this.name(`${node.operationId} Response`)\n return this.resolveName({ key: operationKey(node, 'response', 'response'), name })\n },\n }\n}\n\n/**\n * Builds a resolver `file` override whose base name runs every path segment\n * through `toFilePath`, casing the final segment with `caseLast`.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })\n * ```\n */\nexport function createCasedFile(caseLast: (part: string) => string): ResolverFile {\n return {\n baseName({ name, extname }) {\n return this.memo(`file:${name}\\0${extname}`, (): ast.FileNode['baseName'] => `${toFilePath(name, caseLast)}${extname}`)\n },\n }\n}\n\ntype SchemaIndexContext = Pick<GeneratorContext, 'memo' | 'driver'>\n\n/**\n * Names of the schemas in a circular reference chain, computed once per build.\n */\nexport function getCircularNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo('circularNames:set', () => ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []))\n}\n\n/**\n * Names of the schemas whose type is `enum`, computed once per build.\n */\nexport function getEnumNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo(\n 'enumNames:set',\n () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => (schema.type === 'enum' && schema.name ? [schema.name] : []))),\n )\n}\n\ndeclare global {\n namespace Kubb {\n interface MemoRegistry {\n operation: string\n file: ast.FileNode['baseName']\n operationTypeNames: Array<string>\n }\n }\n}\n","import { isValidVarName, toLiteral } from '@internals/utils'\nimport { toResponseContent } from '@internals/shared'\n\nimport { ast, text } from '@kubb/kit'\n\n// Headers use the `simple` style, so an array or object is sent comma-separated.\nfunction toSimpleStyle(value: unknown): string {\n if (Array.isArray(value)) return value.join(',')\n if (typeof value === 'object' && value !== null) return Object.entries(value).flat().join(',')\n return String(value)\n}\n\n/**\n * The declared headers a mock sends, each with its example's `serializedValue` or `value`, else its schema's `default` or first example.\n */\nexport function getMockHeaders(response: ast.ResponseNode | null | undefined): Array<[name: string, value: string]> {\n return (response?.headers ?? []).flatMap((header): Array<[string, string]> => {\n // OpenAPI ignores a `Content-Type` response header, and the mock already sends one.\n if (header.name.toLowerCase() === 'content-type') return []\n const example = header.examples?.find((entry) => entry.serializedValue !== undefined || entry.value !== undefined)\n const headerValue = example?.serializedValue ?? example?.value ?? header.schema.default ?? header.schema.examples?.[0]\n if (headerValue === undefined || headerValue === null) return []\n const headerText = toSimpleStyle(headerValue)\n // `Headers` throws on a line break or a character above U+00FF, so such an example is left out.\n return /^[\\t\\x20-\\x7E\\x80-\\xFF]*$/.test(headerText) ? [[header.name, headerText]] : []\n })\n}\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 contentType = getResponseEntry(response)?.contentType\n return typeof contentType === 'string' && contentType.length > 0 ? contentType : null\n}\n\nfunction getResponseEntry(response: ast.ResponseNode | null | undefined): ast.ContentNode | undefined {\n const contents = response?.content ?? []\n return contents.find((entry) => ast.mediaType.isJson(entry)) ?? contents[0]\n}\n\n/**\n * The first content type of a response that the client reads as text, such as `application/xml`, or `null` when none is.\n */\nfunction getTextContentType(response: ast.ResponseNode | null | undefined): string | null {\n const entry = (response?.content ?? []).find((entry) => {\n const schema = toResponseContent(entry, response?.content).schema\n return !ast.mediaType.isJson(entry) && schema && ast.ref.getSchema(schema).primitive === 'string'\n })\n return entry?.contentType ?? null\n}\n\n// The client's `jsonReplacer`: a `bigint` goes out as a JSON number, and throws past the safe-integer range instead of losing precision.\nconst bigintReplacer = `(_key, value) => {\n if (typeof value !== 'bigint') return value\n if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) throw new TypeError(\\`Cannot serialize \\${value}n as JSON without losing precision.\\`)\n return Number(value)\n }`\n\n/** The `JSON.stringify` call for `body`, with a replacer when the response holds an `int64`, which `JSON.stringify` throws on. */\nfunction stringify(body: string, response: ast.ResponseNode | null | undefined): string {\n const hasBigint = (response?.content ?? []).some(\n (entry) => entry.schema && ast.schema.find(entry.schema, { predicate: (schema) => schema.type === 'bigint', followRefs: true }) !== undefined,\n )\n return hasBigint ? `JSON.stringify(${body}, ${bigintReplacer})` : `JSON.stringify(${body})`\n}\n\n/**\n * The statements that return a mocked `Response` for `body`. A string body for a response the client reads as text goes\n * out as raw text with that content type, so an XML mock is not sent as a quoted JSON string. Any other body is JSON.\n */\nexport function responseReturn(body: string, status: number, response: ast.ResponseNode | null | undefined): string {\n const contentType = getContentType(response)\n const headers = getMockHeaders(response)\n // A plain variable such as `data` is checked as it is, an expression such as `data ?? createPet()` is read once.\n const name = isValidVarName(body) ? body : 'body'\n const prelude = name === body ? '' : `const body = ${body}\\n `\n\n // shortcut: one event per mock, upgrade when a mock needs to send several events.\n if (contentType && ast.mediaType.getBase(contentType) === 'text/event-stream') {\n const entry = getResponseEntry(response)\n // Without a body, a spec example already serialized as SSE text goes out as it is.\n const serialized = entry?.examples?.find((example) => example.serializedValue !== undefined)?.serializedValue\n const fallback = serialized === undefined ? \"''\" : toLiteral(serialized)\n if (entry?.stream?.shape !== 'event') {\n return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \\`\\${${eventDataLines(name, response)}}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n // The item is the whole event, so its fields become SSE lines and the client parses it back to the same object.\n return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined\n const lines = [\n ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \\`\\${field}: \\${event?.[field]}\\`),\n // A client dispatches an event only once it has a \\`data\\` line, so an event without data still sends an empty one.\n event?.data === undefined ? 'data:' : ${eventDataLines('event.data', response)},\n ]\n\n return ${responseLiteral(`event === undefined ? ${fallback} : \\`\\${lines.join('\\\\n')}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n\n const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null\n if (!textContentType) return `return ${responseLiteral(stringify(body, response), status, contentType, headers)}`\n\n return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}\n\n return ${responseLiteral(stringify(name, response), status, contentType, headers)}`\n}\n\n/**\n * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.\n */\nfunction eventDataLines(body: string, response: ast.ResponseNode | null | undefined): string {\n return `(typeof ${body} === 'string' ? ${body} : ${stringify(body, response)}).split('\\\\n').map((line) => \\`data: \\${line}\\`).join('\\\\n')`\n}\n\n/**\n * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.\n */\nexport function getExampleBody(response: ast.ResponseNode | null | undefined): string | null {\n const entry = getResponseEntry(response)\n const example = entry?.examples?.find((item) => item.value !== undefined || item.serializedValue !== undefined)\n if (!entry || !example) return null\n if (example.value !== undefined) return toLiteral(example.value)\n // `responseReturn` sends raw text only for a text response. For a binary one the example would go out JSON-encoded.\n // A serialized SSE example is already events, which `responseReturn` sends as is when the body is `undefined`.\n if (!ast.mediaType.isJson(entry)) {\n const isTextBody = ast.mediaType.getBase(entry) !== 'text/event-stream' && getTextContentType(response) === entry.contentType\n return isTextBody ? toLiteral(example.serializedValue) : null\n }\n // `responseReturn` encodes a JSON body itself, so a serialized JSON example is parsed first.\n try {\n return toLiteral(JSON.parse(example.serializedValue!))\n } catch {\n return null\n }\n}\n\nconst mswMethods = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no `http.query`, `http.trace` or\n * handler for a custom `additionalOperations` method, so such an operation maps to `http.all` and its handler checks the\n * method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return mswMethods.has(node.method) ? node.method.toLowerCase() : 'all'\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no header to send.\n */\nfunction headersBlock(contentType: string | null, headers: Array<[string, string]>): string {\n const entries = [\n ...(contentType ? [`'Content-Type': ${text.quote(contentType)}`] : []),\n ...headers.map(([name, value]) => `${text.quote(name)}: ${text.quote(value)}`),\n ]\n return entries.length ? `\\n headers: {\\n ${entries.join(',\\n ')}\\n },` : ''\n}\n\n/**\n * The `new HttpResponse<string>(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null, headers: Array<[string, string]>): string {\n return `new HttpResponse<string>(${body}, {\n status: ${status},${headersBlock(contentType, headers)}\n })`\n}\n","import { ast, text } from '@kubb/kit'\nimport { Url } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { getExampleBody, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseReturn } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n /**\n * The `...Response<status>` helper of the response this handler mocks, when its `data` has the handler's type.\n */\n responseName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, responseName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so a handler for a method MSW has no helper for lets the others pass through.\n const methodGuard = method === 'all' ? `if (info.request.method !== ${text.quote(node.method)}) return\\n\\n ` : ''\n const successResponse = ast.http.responses.findPrimary(node)\n const statusCode = successResponse ? ast.http.status.toCode(successResponse) : 200\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 // Without Faker, the spec's own response example is the default body. An explicit `null` stays a `null` body.\n const example = fakerName ? null : getExampleBody(successResponse)\n const exampleBody = example ? `data === undefined ? ${example} : data` : 'data'\n const responseBody = fakerName ? `data ?? ${fakerName}()` : exampleBody\n // A spec example is not checked against the response type, so it is not passed to the typed helper.\n const responseStatements = responseName && !example ? `return ${responseName}(${responseBody})` : responseReturn(responseBody, statusCode, successResponse)\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 ${responseStatements}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import { ast } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { responseReturn } 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 // Optional, since a handler passes on the `data` it got, which may be left out.\n const params = `data?: ${typeName}`\n\n const responseName = `${name}Response${response.statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n ${responseReturn('data', ast.http.status.toCode(response), response)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getEnumNames, resolveOperationFile } 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/renderer-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.http.isOperation(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: resolveOperationFile({\n ctx,\n node,\n resolver: fakerResolver,\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: resolveOperationFile({\n ctx,\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 = ast.http.responses.filterSuccess(node)\n const referencedNames = ast.http.responses.getImportedNames(node.responses)\n const enumOptions = pluginTs.options?.enum\n const enumNames = getEnumNames(ctx)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n // One helper per documented status, a range such as `4xx` included. `default` has no status to mock,\n // and the Fetch `Response` constructor rejects a `1xx` status.\n const types = node.responses\n .filter((response) => response.statusCode !== 'default' && ast.http.status.getClass(response) !== 1)\n .map((response) => {\n const isResponseType = ast.http.status.isSuccess(response) && !hasResponseNameCollision\n return [response, isResponseType ? type.responseName : tsResolver.response.status(node, response.statusCode)] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName)\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 // The handler calls the helper of the response it mocks when that helper takes the handler's own data type.\n const primaryResponse = ast.http.responses.findPrimary(node)\n const responseHelperName =\n primaryResponse &&\n hasResponseSchema(primaryResponse) &&\n types.some(([response, typeName]) => response.statusCode === primaryResponse.statusCode && typeName === mockResponseName)\n ? `${mock.name}Response${primaryResponse.statusCode}`\n : undefined\n\n const requestName = ast.http.requestBody.getSchema(node) ? 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.map(([response, typeName]) => (\n <Response key={response.statusCode} 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 responseName={responseHelperName}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { operationKey } from '@internals/shared'\nimport { casing, createResolver } from '@kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return this.memo(`name:${name}`, () => casing.camel(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 { 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: group ?? null,\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
@@ -131,5 +131,4 @@ export declare const pluginMsw: import("@kubb/kit").PluginFactory<PluginMsw>;
131
131
  * ```
132
132
  */
133
133
  export declare const resolverMsw: ResolverMsw;
134
- export { type PluginMsw, type ResolverMsw, pluginMsw as default };
135
- //# sourceMappingURL=index.d.cts.map
134
+ export { type PluginMsw, type ResolverMsw, pluginMsw as default };
package/dist/index.d.ts CHANGED
@@ -131,5 +131,4 @@ export declare const pluginMsw: import("@kubb/kit").PluginFactory<PluginMsw>;
131
131
  * ```
132
132
  */
133
133
  export declare const resolverMsw: ResolverMsw;
134
- export { type PluginMsw, type ResolverMsw, pluginMsw as default };
135
- //# sourceMappingURL=index.d.ts.map
134
+ export { type PluginMsw, type ResolverMsw, pluginMsw as default };
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ var compatibility = {
9
9
  "node": ">=22.22"
10
10
  };
11
11
  // package.json
12
- var version = "6.2.0";
12
+ var version = "6.2.1";
13
13
  // src/generators/handlersGenerator.ts
14
14
  /**
15
15
  * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
@@ -448,6 +448,18 @@ function getTextContentType(response) {
448
448
  return !ast.mediaType.isJson(entry) && schema && ast.ref.getSchema(schema).primitive === "string";
449
449
  })?.contentType ?? null;
450
450
  }
451
+ const bigintReplacer = `(_key, value) => {
452
+ if (typeof value !== 'bigint') return value
453
+ if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) throw new TypeError(\`Cannot serialize \${value}n as JSON without losing precision.\`)
454
+ return Number(value)
455
+ }`;
456
+ /** The `JSON.stringify` call for `body`, with a replacer when the response holds an `int64`, which `JSON.stringify` throws on. */
457
+ function stringify(body, response) {
458
+ return (response?.content ?? []).some((entry) => entry.schema && ast.schema.find(entry.schema, {
459
+ predicate: (schema) => schema.type === "bigint",
460
+ followRefs: true
461
+ }) !== void 0) ? `JSON.stringify(${body}, ${bigintReplacer})` : `JSON.stringify(${body})`;
462
+ }
451
463
  /**
452
464
  * The statements that return a mocked `Response` for `body`. A string body for a response the client reads as text goes
453
465
  * out as raw text with that content type, so an XML mock is not sent as a quoted JSON string. Any other body is JSON.
@@ -461,27 +473,27 @@ function responseReturn(body, status, response) {
461
473
  const entry = getResponseEntry(response);
462
474
  const serialized = entry?.examples?.find((example) => example.serializedValue !== void 0)?.serializedValue;
463
475
  const fallback = serialized === void 0 ? "''" : toLiteral(serialized);
464
- if (entry?.stream?.shape !== "event") return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \`\${${eventDataLines(name)}}\\n\\n\``, status, contentType, headers)}`;
476
+ if (entry?.stream?.shape !== "event") return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \`\${${eventDataLines(name, response)}}\\n\\n\``, status, contentType, headers)}`;
465
477
  return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined
466
478
  const lines = [
467
479
  ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \`\${field}: \${event?.[field]}\`),
468
480
  // A client dispatches an event only once it has a \`data\` line, so an event without data still sends an empty one.
469
- event?.data === undefined ? 'data:' : ${eventDataLines("event.data")},
481
+ event?.data === undefined ? 'data:' : ${eventDataLines("event.data", response)},
470
482
  ]
471
483
 
472
484
  return ${responseLiteral(`event === undefined ? ${fallback} : \`\${lines.join('\\n')}\\n\\n\``, status, contentType, headers)}`;
473
485
  }
474
486
  const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null;
475
- if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType, headers)}`;
487
+ if (!textContentType) return `return ${responseLiteral(stringify(body, response), status, contentType, headers)}`;
476
488
  return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}
477
489
 
478
- return ${responseLiteral(`JSON.stringify(${name})`, status, contentType, headers)}`;
490
+ return ${responseLiteral(stringify(name, response), status, contentType, headers)}`;
479
491
  }
480
492
  /**
481
493
  * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.
482
494
  */
483
- function eventDataLines(body) {
484
- return `(typeof ${body} === 'string' ? ${body} : JSON.stringify(${body})).split('\\n').map((line) => \`data: \${line}\`).join('\\n')`;
495
+ function eventDataLines(body, response) {
496
+ return `(typeof ${body} === 'string' ? ${body} : ${stringify(body, response)}).split('\\n').map((line) => \`data: \${line}\`).join('\\n')`;
485
497
  }
486
498
  /**
487
499
  * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../../../internals/utils/src/reserved.ts","../../../internals/utils/src/codegen.ts","../../../internals/shared/src/operation.ts","../../../internals/shared/src/resolver.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, casing, 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: casing.camel(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","/**\n * JavaScript and Java reserved words.\n * @link https://github.com/jonschlinkert/reserved/blob/master/index.js\n */\nconst reservedWords = new Set([\n 'abstract',\n 'arguments',\n 'await',\n 'boolean',\n 'break',\n 'byte',\n 'case',\n 'catch',\n 'char',\n 'class',\n 'const',\n 'continue',\n 'debugger',\n 'default',\n 'delete',\n 'do',\n 'double',\n 'else',\n 'enum',\n 'eval',\n 'export',\n 'extends',\n 'false',\n 'final',\n 'finally',\n 'float',\n 'for',\n 'function',\n 'goto',\n 'if',\n 'implements',\n 'import',\n 'in',\n 'instanceof',\n 'int',\n 'interface',\n 'let',\n 'long',\n 'native',\n 'new',\n 'null',\n 'package',\n 'private',\n 'protected',\n 'public',\n 'return',\n 'short',\n 'static',\n 'super',\n 'switch',\n 'synchronized',\n 'this',\n 'throw',\n 'throws',\n 'transient',\n 'true',\n 'try',\n 'typeof',\n 'var',\n 'void',\n 'volatile',\n 'while',\n 'with',\n 'yield',\n 'Array',\n 'Date',\n 'hasOwnProperty',\n 'Infinity',\n 'isFinite',\n 'isNaN',\n 'isPrototypeOf',\n 'length',\n 'Math',\n 'name',\n 'NaN',\n 'Number',\n 'Object',\n 'prototype',\n 'String',\n 'toString',\n 'undefined',\n 'valueOf',\n] as const)\n\n/**\n * Returns `true` when `name` is a syntactically valid JavaScript variable name.\n *\n * @example\n * ```ts\n * isValidVarName('status') // true\n * isValidVarName('class') // false (reserved word)\n * isValidVarName('42foo') // false (starts with digit)\n * ```\n */\nexport function isValidVarName(name: string): boolean {\n if (!name || reservedWords.has(name as 'valueOf')) {\n return false\n }\n return isIdentifier(name)\n}\n\n/**\n * Returns `name` when it's a syntactically valid JavaScript variable name,\n * otherwise prefixes it with `_` so the result is a valid identifier.\n *\n * Useful for sanitizing OpenAPI schema names or operation IDs that start with\n * a digit (e.g. `409`, `504AccountCancel`) before using them as exported\n * variable, type, or function names.\n *\n * @example\n * ```ts\n * ensureValidVarName('409') // '_409'\n * ensureValidVarName('504AccountCancel') // '_504AccountCancel'\n * ensureValidVarName('Pet') // 'Pet'\n * ensureValidVarName('class') // '_class'\n * ```\n */\nexport function ensureValidVarName(name: string): string {\n if (!name || isValidVarName(name)) {\n return name\n }\n return `_${name}`\n}\n\n/**\n * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.\n *\n * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys\n * even though they are not valid variable names, so use this (not {@link isValidVarName}) when\n * deciding whether an object key needs quoting.\n *\n * @example\n * ```ts\n * isIdentifier('name') // true\n * isIdentifier('x-total')// false\n * ```\n */\nexport function isIdentifier(name: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)\n}\n","import { text } from '@kubb/kit'\nimport { isIdentifier } from './reserved.ts'\n\n/**\n * Builds a JSDoc comment block from an array of lines. Returns `fallback` when there are no\n * comments.\n *\n * @example\n * ```ts\n * buildJSDoc(['@type string', '@example hello'])\n * // '/**\\n * @type string\\n * @example hello\\n *\\/\\n '\n * ```\n */\nexport function buildJSDoc(\n comments: Array<string>,\n options: {\n /**\n * String used to indent each comment line.\n * @default ' * '\n */\n indent?: string\n /**\n * String appended after the closing tag.\n * @default '\\n '\n */\n suffix?: string\n /**\n * Returned as-is when `comments` is empty.\n * @default ' '\n */\n fallback?: string\n } = {},\n): string {\n const { indent = ' * ', suffix = '\\n ', fallback = ' ' } = options\n\n if (comments.length === 0) return fallback\n\n return `/**\\n${comments.map((c) => `${indent}${c}`).join('\\n')}\\n */${suffix}`\n}\n\n/**\n * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one\n * level and closing the brace at column zero. Entries that are themselves multi-line objects indent\n * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.\n *\n * @example\n * ```ts\n * buildObject(['id: z.number()', 'name: z.string()'])\n * // '{\\n id: z.number(),\\n name: z.string(),\\n}'\n * ```\n */\nexport function buildObject(entries: Array<string>): string {\n if (entries.length === 0) return '{}'\n const body = entries.map((entry) => `${text.indent({ text: entry })},`).join('\\n')\n\n return `{\\n${body}\\n}`\n}\n\n/**\n * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on\n * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented\n * one level with a trailing comma and the closing bracket at column zero. Used for member lists such\n * as `z.union([…])` and `z.array([…])`.\n *\n * @example\n * ```ts\n * buildList(['z.string()', 'z.number()'])\n * // '[z.string(), z.number()]'\n * ```\n */\nexport function buildList(items: Array<string>, brackets: [open: string, close: string] = ['[', ']']): string {\n const [open, close] = brackets\n if (items.length === 0) return `${open}${close}`\n if (!items.some((item) => item.includes('\\n'))) return `${open}${items.join(', ')}${close}`\n const body = items.map((item) => `${text.indent({ text: item })},`).join('\\n')\n\n return `${open}\\n${body}\\n${close}`\n}\n\n/**\n * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key\n * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation\n * of a recursive schema until first access.\n *\n * @example\n * ```ts\n * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })\n * // \"get parent() { return z.lazy(() => Pet) }\"\n * ```\n */\nexport function lazyGetter({ name, body }: { name: string; body: string }): string {\n return `get ${objectKey(name)}() { return ${body} }`\n}\n\n/**\n * An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.\n *\n * @example\n * ```ts\n * objectKey('x-total') // \"'x-total'\"\n * ```\n */\nexport function objectKey(name: string): string {\n return isIdentifier(name) ? name : text.quote(name)\n}\n\n/**\n * A JSON value as a JavaScript literal, with single-quoted strings and identifier keys left unquoted.\n *\n * @example\n * ```ts\n * toLiteral({ id: 1, 'x-tag': ['a'] }) // \"{ id: 1, 'x-tag': ['a'] }\"\n * ```\n */\nexport function toLiteral(value: unknown): string {\n if (typeof value === 'string') return text.quote(value)\n if (Array.isArray(value)) return `[${value.map(toLiteral).join(', ')}]`\n if (value && typeof value === 'object') {\n return `{ ${Object.entries(value)\n .map(([key, item]) => `${objectKey(key)}: ${toLiteral(item)}`)\n .join(', ')} }`\n }\n return String(value)\n}\n","import { jsDocEscape } from '@internals/utils'\nimport { ast, type Group, type Memo, type Output, type Resolver, type ResolverFileParams } 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 the file a schema-emitting plugin (`plugin-ts`, `plugin-zod`, `plugin-faker`) writes\n * `node` to, cached in `ctx.memo` under the resolver's plugin name and the\n * operation. The plugin itself and every plugin that imports from it call this, so they all get\n * the same path.\n *\n * When that path matches the file of a component schema in `ctx.driver.inputNode`, ignoring case, the file name\n * gets an `Operation` suffix: operation `point` goes to `PointOperation.ts` next to schema `Point.ts`.\n * Otherwise the two merge into one file, or overwrite each other on a case-insensitive file system.\n * Only the file name changes. The declarations keep their names.\n *\n * @example Resolve `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveOperationFile({ ctx, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveOperationFile(options: {\n ctx: { memo: Memo; driver: { inputNode?: { schemas: ReadonlyArray<ast.SchemaNode> } | null } }\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 { ctx, node, resolver, root, output } = options\n const { memo } = ctx\n const location = { root, output, group: options.group ?? undefined }\n\n return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {\n const schemaPaths = memo(\n `${resolver.pluginName}:schemaFilePaths`,\n () =>\n new Set(\n (ctx.driver.inputNode?.schemas ?? []).flatMap((schema) =>\n schema.name ? [resolver.file({ name: schema.name, extname: '.ts', ...location }).path.toLowerCase()] : [],\n ),\n ),\n )\n let file = resolver.file({ ...operationFileEntry(node, node.operationId), ...location })\n\n for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += 'Operation') {\n const next = resolver.file({ ...operationFileEntry(node, name), ...location })\n // A single-file output holds every declaration on purpose, so a new name changes nothing.\n if (next.path === file.path) break\n file = next\n }\n\n return file\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 cookies(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type BuildOperationCommentsOptions = {\n splitLines?: boolean\n}\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\n/**\n * The operation's method and path as plain text, such as `` `GET /pet/{petId}` ``. A path is not a valid `{@link}` target.\n */\nfunction getOperationRequestLine(node: ast.OperationNode): string | null {\n return ast.http.isOperation(node) && node.path ? `\\`${node.method.toUpperCase()} ${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(ast.http.responses.findPrimary(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\ntype ResponseType = 'blob' | 'text' | 'stream'\n\ntype ResponseContentEntry = { contentType: string; schema?: ast.SchemaNode | null; stream?: ast.ContentNode['stream'] }\n\n/**\n * How an operation streams its primary success response item by item, when that response declares one content type:\n * - `'event'` for `text/event-stream` whose `itemSchema` types the whole event with its own `event` and `data`\n * - `'data'` for `text/event-stream` typed by its payload only\n * - `'line'` for JSON Lines, one parsed JSON value per line\n *\n * Returns `null` for any other response, and for a response that also declares another content type, which stays a normal\n * call with its query hooks.\n */\nexport function getEventStream(node: ast.OperationNode): 'event' | 'data' | 'line' | null {\n const content = ast.http.responses.findPrimary(node)?.content ?? []\n if (content.length !== 1) return null\n const entry = content[0]!\n const framing = getStreamFraming(entry)\n if (framing === 'events') return entry.stream?.shape ?? 'data'\n return framing === 'lines' ? 'line' : null\n}\n\n/**\n * How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.\n */\nexport function getStreamFraming(entry: Pick<ast.ContentNode, 'contentType' | 'stream'>): 'events' | 'lines' | undefined {\n if (ast.mediaType.getBase(entry) === 'text/event-stream') return 'events'\n // `multipart/mixed` with an `itemSchema` also has `stream`, but it is a stream of parts, not of JSON values.\n return entry.stream && ast.mediaType.isStream(entry) ? 'lines' : undefined\n}\n\n/**\n * Whether an operation streams its primary success response, as Server-Sent Events or JSON Lines. The client generator uses\n * this to return a typed stream of items instead of a one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getEventStream(node) !== null\n}\n\n// A stream without a schema is a `blob` with a string primitive, so it keeps the JSON-parse default.\nfunction isTextSchema(schema: ast.SchemaNode | null | undefined): boolean {\n if (!schema) return false\n const resolved = ast.ref.getSchema(schema)\n return resolved.type !== 'blob' && resolved.primitive === 'string'\n}\n\n/**\n * Whether an operation streams Server-Sent Events whose `data` the document types as a string, so the client keeps each\n * `data` as text instead of JSON-parsing it. With an event-shaped `itemSchema` where only some events type `data` as a\n * string, it returns the names of those events, or `false` when one of them has no fixed name.\n */\nexport function hasTextEventData(node: ast.OperationNode): boolean | Array<string> {\n const shape = getEventStream(node)\n const entry = ast.http.responses.findPrimary(node)?.content?.[0]\n if (!entry || (shape !== 'data' && shape !== 'event')) return false\n if (shape === 'data') return isTextSchema(entry.stream?.schema ?? entry.schema)\n\n const item = entry.stream?.schema ? ast.ref.getSchema(entry.stream.schema) : undefined\n if (!item) return false\n const events = item.type === 'union' ? (ast.schema.narrow(item, 'union')?.members ?? []) : [item]\n // A discriminator makes each event an intersection of its `$ref` and the pinned `event`, so every part is read.\n const propertySchemas = (event: ast.SchemaNode, propertyName: string) => ast.schema.findProperties({ node: event, propertyName }).map(({ schema }) => schema)\n const textEvents = events.filter((event) => {\n const dataSchemas = propertySchemas(event, 'data')\n return dataSchemas.length > 0 && dataSchemas.every(isTextSchema)\n })\n if (textEvents.length === events.length) return events.length > 0\n\n const eventNames = textEvents.map((event) => propertySchemas(event, 'event').flatMap((schema) => ast.schema.getLiterals(ast.ref.getSchema(schema))))\n return eventNames.length > 0 && eventNames.every((values) => values.length > 0) && [...new Set(eventNames.flat().map(String))]\n}\n\n/**\n * Whether the primary success response offers `text/event-stream`, alone or next to other content types. The client asks\n * for the stream by default and ships the `sse.ts` runtime, so a mixed response can be read as events too.\n */\nexport function hasEventStreamContent(node: ast.OperationNode): boolean {\n const entries = ast.http.responses.findPrimary(node)?.content ?? []\n // Next to another type, a stream without a schema gets no typed variant, so it keeps the plain call.\n return entries.some((entry) => getStreamFraming(entry) === 'events' && (entries.length === 1 || Boolean(entry.schema)))\n}\n\n/**\n * Whether a success response offers a stream media type next to other content types. The client returns that variant's\n * body as a `ReadableStream`, so the stream runtime is registered and a hook's data type includes the stream.\n */\nexport function hasStreamVariant(node: ast.OperationNode): boolean {\n return ast.http.responses.filterSuccess(node).some((response) => {\n const variants = (response.content ?? []).filter((entry) => entry.schema)\n return variants.length > 1 && variants.some((entry) => ast.mediaType.isStream(entry))\n })\n}\n\nconst formContentTypes = ['multipart/form-data', 'application/x-www-form-urlencoded'] as const\n\n/**\n * The form media types the operation's request body offers, `multipart/form-data` and `application/x-www-form-urlencoded`,\n * which the client needs a handler for.\n */\nexport function getFormContentTypes(node: ast.OperationNode): Array<(typeof formContentTypes)[number]> {\n const request = new Set((node.requestBody?.content ?? []).map((entry) => ast.mediaType.getBase(entry)))\n return formContentTypes.filter((contentType) => request.has(contentType))\n}\n\n/**\n * Parser for one response content entry.\n *\n * JSON stays unset so the runtime detects it. `text/event-stream` and JSON Lines are streams. A binary schema\n * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known\n * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,\n * `image/*`, `audio/*`, and `video/*` are blobs.\n */\nfunction responseTypeForContent(entry: ResponseContentEntry): ResponseType | undefined {\n const baseType = ast.mediaType.getBase(entry)\n if (!baseType) return undefined\n if (getStreamFraming(entry)) return 'stream'\n if (ast.mediaType.isJson(baseType)) return undefined\n if (entry.schema?.type === 'blob') return 'blob'\n if (baseType.startsWith('text/')) return 'text'\n if (isBinaryContentType(baseType)) return 'blob'\n return undefined\n}\n\nfunction isBinaryContentType(baseType: string): boolean {\n return baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)\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 // Keeps the original case, unlike ast.mediaType.getBase, so generated names stay stable.\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 * Returns the entry with the schema of the value the client returns: `string` for a body read as text, such as `Pet` on\n * `application/xml`, and `blob` for `image/*`, `audio/*`, `video/*`, PDF, and octet-stream. A binary schema elsewhere is a `Blob`\n * only through `parseAs: 'blob'`, which needs every entry in `entries` binary. JSON, vendor (`application/vnd.*`), stream,\n * schemaless, and wildcard entries keep their schema. `entries` is the response's full list of content types.\n */\nexport function toResponseContent<T extends ContentVariantInput & ResponseContentEntry>(entry: T, entries: ReadonlyArray<ResponseContentEntry> = [entry]): T {\n const { contentType, schema } = entry\n if (!schema || entry.stream) return entry\n\n const baseType = ast.mediaType.getBase(contentType)\n const resolved = ast.ref.getSchema(schema)\n if (isBinaryContentType(baseType)) {\n return resolved.type === 'blob' ? entry : { ...entry, schema: ast.factory.createSchema({ type: 'blob' }), keysToOmit: null }\n }\n const isPlainMediaType = Boolean(baseType) && !ast.mediaType.isJson(baseType) && !baseType.includes('*')\n if (resolved.type === 'blob') {\n // shortcut: an error response never gets `parseAs`, so a lone binary schema there reads as text too but stays `Blob`.\n const readsAsText = isPlainMediaType && !entries.every((item) => item.schema?.type === 'blob')\n return readsAsText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n }\n if (resolved.primitive === 'string') return entry\n\n const responseType = responseTypeForContent(entry)\n // A response without a body, such as a 405, has no schema to read. Only `text/*` is always text.\n if (responseType !== 'text' && (resolved.type === 'unknown' || resolved.type === 'any' || resolved.type === 'void')) return entry\n const isVendorJson = /^[^/]+\\/vnd\\./.test(baseType) && !/(xml|yaml|yml|csv)$/.test(baseType)\n const isText = responseType === 'text' || (!responseType && isPlainMediaType && !isVendorJson)\n\n return isText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n}\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/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...): `RequestConfig` minus what the\n * hook fills in, which is every request group and `throwOnError`. Unlike {@link buildRequestConfigType},\n * it never narrows `contentType`: query hooks wrap GET operations, which carry no request body to select\n * a content type for.\n */\nexport function buildClientOptionType(options: { omit?: Array<string> } = {}): string {\n const keys = [...requestGroupOrder, 'url', ...(options.omit ?? []), 'throwOnError']\n return `Partial<Omit<RequestConfig, ${keys.map((key) => `'${key}'`).join(' | ')}>>`\n}\n\n/**\n * The `client?:` option type of a mutation hook. When the operation has a request content type choice,\n * the mutation variables carry `contentType`, so the config leaves it out. Otherwise the ambiguous side is\n * narrowed to the operation's media types; a single-type side has nothing to pick, so it stays baked in the\n * generated call.\n */\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n if (hasRequestContentTypeChoice(node)) {\n return buildClientOptionType({ omit: ['contentType'] })\n }\n\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n const base = buildClientOptionType()\n return members.length ? `${base} & { contentType?: { ${members.join('; ')} } }` : base\n}\n\n/** The grouped request options, in the order generated signatures and calls list them. */\nexport const requestGroupOrder = ['path', 'query', 'body', 'headers', 'cookies'] as const\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n cookies: 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, cookie } = ast.http.groupParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(ast.http.requestBody.getSchema(node)),\n headers: header.length > 0,\n cookies: cookie.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 } = ast.http.groupParameters(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 // `cookies` never makes the parameter required: a browser sends its cookies itself and cannot set a `Cookie` header.\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !(groups.body && node.requestBody?.required === true),\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, cookies }` 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; names: Array<keyof RequestGroups> } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = requestGroupOrder.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 names,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { splitLines = false } = options\n // The request line leads: free text after a block tag would belong to that tag.\n const comments = [\n getOperationRequestLine(node),\n node.description && `@description ${node.description}`,\n node.summary && `@summary ${node.summary}`,\n node.deprecated && '@deprecated',\n node.externalDocs && `@see ${jsDocEscape([node.externalDocs.url, node.externalDocs.description].filter(Boolean).join(' '))}`,\n ]\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\n/**\n * Whether the operation declares more than one request media type with a schema, so a caller must pick one on `contentType`.\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\n/**\n * Builds the combined `{ body, path, query, headers, cookies }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. `cookies` appears only on an operation\n * with a cookie parameter, and stays optional because a browser sends its cookies itself. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same shape from the same inputs.\n * `primitive: 'object'` is a no-op for the TS printer and tells the Zod printer to emit `z.object(…)` rather\n * than a record.\n */\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver, { pairContentTypes = false } = {}): ast.SchemaNode {\n const { path, query, header, cookie } = ast.http.groupParameters(node)\n const hasBody = Boolean(ast.http.requestBody.getSchema(node))\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 { name: 'cookies', params: cookie, resolve: resolver.param.cookies },\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 && node.requestBody?.required === true, schema: body }),\n ...groups\n .filter(({ name, params }) => name !== 'cookies' || params.length > 0)\n .map(({ name, params, resolve }) => {\n const required = name !== 'cookies' && 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`. A bare string sets the\n // request content type at runtime, so it is accepted next to the `{ request }` form.\n return ast.factory.createSchema({\n type: 'union',\n members: variants.map((variant, index) => {\n const contentType = ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] })\n\n return 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: 'union',\n members: [\n contentType,\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [ast.factory.createProperty({ name: 'request', required: true, schema: 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. With `streamData`, the stream variant of a 2xx status holds that schema, since the\n * client returns its body unread.\n */\nfunction buildResponseRecordEntry(\n node: ast.OperationNode,\n res: ast.ResponseNode,\n resolver: ResponseStatusNameResolver,\n streamData: ast.SchemaNode | undefined,\n): 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 // The runtime reports the bare media type, without parameters such as `charset` or `profile`.\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [ast.mediaType.getBase(variant)] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema:\n streamData && ast.http.status.isSuccess(res) && ast.mediaType.isStream(variant)\n ? streamData\n : 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 *\n * `streamData` is the type of a stream variant's `data`, such as a `ReadableStream<Uint8Array>` reference.\n */\nexport function buildResponses(\n node: ast.OperationNode,\n resolver: ResponseStatusNameResolver,\n { streamData }: { streamData?: ast.SchemaNode } = {},\n): 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, streamData),\n }),\n ),\n })\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterError(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterSuccess(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\n/**\n * Lists the type names an operation's types file declares, computed once per resolver and `options` so every\n * dependent reads the same list.\n */\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver & Pick<Resolver, 'memo'>,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const key = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n return resolver.memo(`operationTypeNames:${key}`, () => {\n const { path, query, header, cookie } = ast.http.groupParameters(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 ...cookie.map((param) => resolver.param.cookies(node, param)),\n ]\n const bodyAndResponseNames = [ast.http.requestBody.getSchema(node) ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n return names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n })\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = Number(response.statusCode)\n if (Number.isNaN(code)) {\n continue\n }\n\n types.push([code, ast.http.status.isSuccess(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","import { ensureValidVarName, toFilePath } from '@internals/utils'\nimport { ast, casing } from '@kubb/kit'\nimport type { GeneratorContext, Resolver, ResolverFile } from '@kubb/kit'\n\n/**\n * The shared top-level `name` of the operation plugins, computed once per operation name and build.\n */\nexport function resolveOperationName(this: Resolver, name: string): string {\n return this.memo(`operation:${name}`, () => ensureValidVarName(casing.camel(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 * The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro\n * rewrites the method or path still resolves another plugin's names for the same operation.\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 const source = node.pointer ? [node.pointer] : [node.method, node.path]\n return ['operation', ...source, ...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`/`Cookies` 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 cookies(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 /**\n * Name of the headers a response declares, such as `ListPetsStatus200Headers`.\n */\n headers(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 cookies(node) {\n const name = this.name(`${node.operationId} Cookies`)\n return this.resolveName({ key: operationKey(node, 'param', 'cookies'), 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 headers(node, statusCode) {\n const name = this.name(`${node.operationId} Status ${statusCode} Headers`)\n return this.resolveName({ key: operationKey(node, 'response', 'headers', statusCode), name })\n },\n body(node) {\n const name = this.name(`${node.operationId} Body`)\n return this.resolveName({ key: operationKey(node, 'response', 'body'), name })\n },\n responses(node) {\n const name = this.name(`${node.operationId} Responses`)\n return this.resolveName({ key: operationKey(node, 'response', 'responses'), name })\n },\n response(node) {\n const name = this.name(`${node.operationId} Response`)\n return this.resolveName({ key: operationKey(node, 'response', 'response'), name })\n },\n }\n}\n\n/**\n * Builds a resolver `file` override whose base name runs every path segment\n * through `toFilePath`, casing the final segment with `caseLast`.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })\n * ```\n */\nexport function createCasedFile(caseLast: (part: string) => string): ResolverFile {\n return {\n baseName({ name, extname }) {\n return this.memo(`file:${name}\\0${extname}`, (): ast.FileNode['baseName'] => `${toFilePath(name, caseLast)}${extname}`)\n },\n }\n}\n\ntype SchemaIndexContext = Pick<GeneratorContext, 'memo' | 'driver'>\n\n/**\n * Names of the schemas in a circular reference chain, computed once per build.\n */\nexport function getCircularNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo('circularNames:set', () => ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []))\n}\n\n/**\n * Names of the schemas whose type is `enum`, computed once per build.\n */\nexport function getEnumNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo(\n 'enumNames:set',\n () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => (schema.type === 'enum' && schema.name ? [schema.name] : []))),\n )\n}\n\ndeclare global {\n namespace Kubb {\n interface MemoRegistry {\n operation: string\n file: ast.FileNode['baseName']\n operationTypeNames: Array<string>\n }\n }\n}\n","import { isValidVarName, toLiteral } from '@internals/utils'\nimport { toResponseContent } from '@internals/shared'\n\nimport { ast, text } from '@kubb/kit'\n\n// Headers use the `simple` style, so an array or object is sent comma-separated.\nfunction toSimpleStyle(value: unknown): string {\n if (Array.isArray(value)) return value.join(',')\n if (typeof value === 'object' && value !== null) return Object.entries(value).flat().join(',')\n return String(value)\n}\n\n/**\n * The declared headers a mock sends, each with its example's `serializedValue` or `value`, else its schema's `default` or first example.\n */\nexport function getMockHeaders(response: ast.ResponseNode | null | undefined): Array<[name: string, value: string]> {\n return (response?.headers ?? []).flatMap((header): Array<[string, string]> => {\n // OpenAPI ignores a `Content-Type` response header, and the mock already sends one.\n if (header.name.toLowerCase() === 'content-type') return []\n const example = header.examples?.find((entry) => entry.serializedValue !== undefined || entry.value !== undefined)\n const headerValue = example?.serializedValue ?? example?.value ?? header.schema.default ?? header.schema.examples?.[0]\n if (headerValue === undefined || headerValue === null) return []\n const headerText = toSimpleStyle(headerValue)\n // `Headers` throws on a line break or a character above U+00FF, so such an example is left out.\n return /^[\\t\\x20-\\x7E\\x80-\\xFF]*$/.test(headerText) ? [[header.name, headerText]] : []\n })\n}\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 contentType = getResponseEntry(response)?.contentType\n return typeof contentType === 'string' && contentType.length > 0 ? contentType : null\n}\n\nfunction getResponseEntry(response: ast.ResponseNode | null | undefined): ast.ContentNode | undefined {\n const contents = response?.content ?? []\n return contents.find((entry) => ast.mediaType.isJson(entry)) ?? contents[0]\n}\n\n/**\n * The first content type of a response that the client reads as text, such as `application/xml`, or `null` when none is.\n */\nfunction getTextContentType(response: ast.ResponseNode | null | undefined): string | null {\n const entry = (response?.content ?? []).find((entry) => {\n const schema = toResponseContent(entry, response?.content).schema\n return !ast.mediaType.isJson(entry) && schema && ast.ref.getSchema(schema).primitive === 'string'\n })\n return entry?.contentType ?? null\n}\n\n/**\n * The statements that return a mocked `Response` for `body`. A string body for a response the client reads as text goes\n * out as raw text with that content type, so an XML mock is not sent as a quoted JSON string. Any other body is JSON.\n */\nexport function responseReturn(body: string, status: number, response: ast.ResponseNode | null | undefined): string {\n const contentType = getContentType(response)\n const headers = getMockHeaders(response)\n // A plain variable such as `data` is checked as it is, an expression such as `data ?? createPet()` is read once.\n const name = isValidVarName(body) ? body : 'body'\n const prelude = name === body ? '' : `const body = ${body}\\n `\n\n // shortcut: one event per mock, upgrade when a mock needs to send several events.\n if (contentType && ast.mediaType.getBase(contentType) === 'text/event-stream') {\n const entry = getResponseEntry(response)\n // Without a body, a spec example already serialized as SSE text goes out as it is.\n const serialized = entry?.examples?.find((example) => example.serializedValue !== undefined)?.serializedValue\n const fallback = serialized === undefined ? \"''\" : toLiteral(serialized)\n if (entry?.stream?.shape !== 'event') {\n return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \\`\\${${eventDataLines(name)}}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n // The item is the whole event, so its fields become SSE lines and the client parses it back to the same object.\n return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined\n const lines = [\n ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \\`\\${field}: \\${event?.[field]}\\`),\n // A client dispatches an event only once it has a \\`data\\` line, so an event without data still sends an empty one.\n event?.data === undefined ? 'data:' : ${eventDataLines('event.data')},\n ]\n\n return ${responseLiteral(`event === undefined ? ${fallback} : \\`\\${lines.join('\\\\n')}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n\n const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null\n if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType, headers)}`\n\n return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}\n\n return ${responseLiteral(`JSON.stringify(${name})`, status, contentType, headers)}`\n}\n\n/**\n * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.\n */\nfunction eventDataLines(body: string): string {\n return `(typeof ${body} === 'string' ? ${body} : JSON.stringify(${body})).split('\\\\n').map((line) => \\`data: \\${line}\\`).join('\\\\n')`\n}\n\n/**\n * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.\n */\nexport function getExampleBody(response: ast.ResponseNode | null | undefined): string | null {\n const entry = getResponseEntry(response)\n const example = entry?.examples?.find((item) => item.value !== undefined || item.serializedValue !== undefined)\n if (!entry || !example) return null\n if (example.value !== undefined) return toLiteral(example.value)\n // `responseReturn` sends raw text only for a text response. For a binary one the example would go out JSON-encoded.\n // A serialized SSE example is already events, which `responseReturn` sends as is when the body is `undefined`.\n if (!ast.mediaType.isJson(entry)) {\n const isTextBody = ast.mediaType.getBase(entry) !== 'text/event-stream' && getTextContentType(response) === entry.contentType\n return isTextBody ? toLiteral(example.serializedValue) : null\n }\n // `responseReturn` encodes a JSON body itself, so a serialized JSON example is parsed first.\n try {\n return toLiteral(JSON.parse(example.serializedValue!))\n } catch {\n return null\n }\n}\n\nconst mswMethods = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no `http.query`, `http.trace` or\n * handler for a custom `additionalOperations` method, so such an operation maps to `http.all` and its handler checks the\n * method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return mswMethods.has(node.method) ? node.method.toLowerCase() : 'all'\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no header to send.\n */\nfunction headersBlock(contentType: string | null, headers: Array<[string, string]>): string {\n const entries = [\n ...(contentType ? [`'Content-Type': ${text.quote(contentType)}`] : []),\n ...headers.map(([name, value]) => `${text.quote(name)}: ${text.quote(value)}`),\n ]\n return entries.length ? `\\n headers: {\\n ${entries.join(',\\n ')}\\n },` : ''\n}\n\n/**\n * The `new HttpResponse<string>(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null, headers: Array<[string, string]>): string {\n return `new HttpResponse<string>(${body}, {\n status: ${status},${headersBlock(contentType, headers)}\n })`\n}\n","import { ast, text } from '@kubb/kit'\nimport { Url } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { getExampleBody, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseReturn } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n /**\n * The `...Response<status>` helper of the response this handler mocks, when its `data` has the handler's type.\n */\n responseName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, responseName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so a handler for a method MSW has no helper for lets the others pass through.\n const methodGuard = method === 'all' ? `if (info.request.method !== ${text.quote(node.method)}) return\\n\\n ` : ''\n const successResponse = ast.http.responses.findPrimary(node)\n const statusCode = successResponse ? ast.http.status.toCode(successResponse) : 200\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 // Without Faker, the spec's own response example is the default body. An explicit `null` stays a `null` body.\n const example = fakerName ? null : getExampleBody(successResponse)\n const exampleBody = example ? `data === undefined ? ${example} : data` : 'data'\n const responseBody = fakerName ? `data ?? ${fakerName}()` : exampleBody\n // A spec example is not checked against the response type, so it is not passed to the typed helper.\n const responseStatements = responseName && !example ? `return ${responseName}(${responseBody})` : responseReturn(responseBody, statusCode, successResponse)\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 ${responseStatements}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import { ast } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { responseReturn } 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 // Optional, since a handler passes on the `data` it got, which may be left out.\n const params = `data?: ${typeName}`\n\n const responseName = `${name}Response${response.statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n ${responseReturn('data', ast.http.status.toCode(response), response)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getEnumNames, resolveOperationFile } 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/renderer-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.http.isOperation(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: resolveOperationFile({\n ctx,\n node,\n resolver: fakerResolver,\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: resolveOperationFile({\n ctx,\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 = ast.http.responses.filterSuccess(node)\n const referencedNames = ast.http.responses.getImportedNames(node.responses)\n const enumOptions = pluginTs.options?.enum\n const enumNames = getEnumNames(ctx)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n // One helper per documented status, a range such as `4xx` included. `default` has no status to mock,\n // and the Fetch `Response` constructor rejects a `1xx` status.\n const types = node.responses\n .filter((response) => response.statusCode !== 'default' && ast.http.status.getClass(response) !== 1)\n .map((response) => {\n const isResponseType = ast.http.status.isSuccess(response) && !hasResponseNameCollision\n return [response, isResponseType ? type.responseName : tsResolver.response.status(node, response.statusCode)] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName)\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 // The handler calls the helper of the response it mocks when that helper takes the handler's own data type.\n const primaryResponse = ast.http.responses.findPrimary(node)\n const responseHelperName =\n primaryResponse &&\n hasResponseSchema(primaryResponse) &&\n types.some(([response, typeName]) => response.statusCode === primaryResponse.statusCode && typeName === mockResponseName)\n ? `${mock.name}Response${primaryResponse.statusCode}`\n : undefined\n\n const requestName = ast.http.requestBody.getSchema(node) ? 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.map(([response, typeName]) => (\n <Response key={response.statusCode} 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 responseName={responseHelperName}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { operationKey } from '@internals/shared'\nimport { casing, createResolver } from '@kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return this.memo(`name:${name}`, () => casing.camel(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 { 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: group ?? null,\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":["../kubb-extension.json","../package.json","../src/generators/handlersGenerator.ts","../../../internals/utils/src/reserved.ts","../../../internals/utils/src/codegen.ts","../../../internals/shared/src/operation.ts","../../../internals/shared/src/resolver.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, casing, 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: casing.camel(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","/**\n * JavaScript and Java reserved words.\n * @link https://github.com/jonschlinkert/reserved/blob/master/index.js\n */\nconst reservedWords = new Set([\n 'abstract',\n 'arguments',\n 'await',\n 'boolean',\n 'break',\n 'byte',\n 'case',\n 'catch',\n 'char',\n 'class',\n 'const',\n 'continue',\n 'debugger',\n 'default',\n 'delete',\n 'do',\n 'double',\n 'else',\n 'enum',\n 'eval',\n 'export',\n 'extends',\n 'false',\n 'final',\n 'finally',\n 'float',\n 'for',\n 'function',\n 'goto',\n 'if',\n 'implements',\n 'import',\n 'in',\n 'instanceof',\n 'int',\n 'interface',\n 'let',\n 'long',\n 'native',\n 'new',\n 'null',\n 'package',\n 'private',\n 'protected',\n 'public',\n 'return',\n 'short',\n 'static',\n 'super',\n 'switch',\n 'synchronized',\n 'this',\n 'throw',\n 'throws',\n 'transient',\n 'true',\n 'try',\n 'typeof',\n 'var',\n 'void',\n 'volatile',\n 'while',\n 'with',\n 'yield',\n 'Array',\n 'Date',\n 'hasOwnProperty',\n 'Infinity',\n 'isFinite',\n 'isNaN',\n 'isPrototypeOf',\n 'length',\n 'Math',\n 'name',\n 'NaN',\n 'Number',\n 'Object',\n 'prototype',\n 'String',\n 'toString',\n 'undefined',\n 'valueOf',\n] as const)\n\n/**\n * Returns `true` when `name` is a syntactically valid JavaScript variable name.\n *\n * @example\n * ```ts\n * isValidVarName('status') // true\n * isValidVarName('class') // false (reserved word)\n * isValidVarName('42foo') // false (starts with digit)\n * ```\n */\nexport function isValidVarName(name: string): boolean {\n if (!name || reservedWords.has(name as 'valueOf')) {\n return false\n }\n return isIdentifier(name)\n}\n\n/**\n * Returns `name` when it's a syntactically valid JavaScript variable name,\n * otherwise prefixes it with `_` so the result is a valid identifier.\n *\n * Useful for sanitizing OpenAPI schema names or operation IDs that start with\n * a digit (e.g. `409`, `504AccountCancel`) before using them as exported\n * variable, type, or function names.\n *\n * @example\n * ```ts\n * ensureValidVarName('409') // '_409'\n * ensureValidVarName('504AccountCancel') // '_504AccountCancel'\n * ensureValidVarName('Pet') // 'Pet'\n * ensureValidVarName('class') // '_class'\n * ```\n */\nexport function ensureValidVarName(name: string): string {\n if (!name || isValidVarName(name)) {\n return name\n }\n return `_${name}`\n}\n\n/**\n * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.\n *\n * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys\n * even though they are not valid variable names, so use this (not {@link isValidVarName}) when\n * deciding whether an object key needs quoting.\n *\n * @example\n * ```ts\n * isIdentifier('name') // true\n * isIdentifier('x-total')// false\n * ```\n */\nexport function isIdentifier(name: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)\n}\n","import { text } from '@kubb/kit'\nimport { isIdentifier } from './reserved.ts'\n\n/**\n * Builds a JSDoc comment block from an array of lines. Returns `fallback` when there are no\n * comments.\n *\n * @example\n * ```ts\n * buildJSDoc(['@type string', '@example hello'])\n * // '/**\\n * @type string\\n * @example hello\\n *\\/\\n '\n * ```\n */\nexport function buildJSDoc(\n comments: Array<string>,\n options: {\n /**\n * String used to indent each comment line.\n * @default ' * '\n */\n indent?: string\n /**\n * String appended after the closing tag.\n * @default '\\n '\n */\n suffix?: string\n /**\n * Returned as-is when `comments` is empty.\n * @default ' '\n */\n fallback?: string\n } = {},\n): string {\n const { indent = ' * ', suffix = '\\n ', fallback = ' ' } = options\n\n if (comments.length === 0) return fallback\n\n return `/**\\n${comments.map((c) => `${indent}${c}`).join('\\n')}\\n */${suffix}`\n}\n\n/**\n * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one\n * level and closing the brace at column zero. Entries that are themselves multi-line objects indent\n * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.\n *\n * @example\n * ```ts\n * buildObject(['id: z.number()', 'name: z.string()'])\n * // '{\\n id: z.number(),\\n name: z.string(),\\n}'\n * ```\n */\nexport function buildObject(entries: Array<string>): string {\n if (entries.length === 0) return '{}'\n const body = entries.map((entry) => `${text.indent({ text: entry })},`).join('\\n')\n\n return `{\\n${body}\\n}`\n}\n\n/**\n * Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on\n * one line when no item spans multiple lines, and otherwise puts each item on its own line, indented\n * one level with a trailing comma and the closing bracket at column zero. Used for member lists such\n * as `z.union([…])` and `z.array([…])`.\n *\n * @example\n * ```ts\n * buildList(['z.string()', 'z.number()'])\n * // '[z.string(), z.number()]'\n * ```\n */\nexport function buildList(items: Array<string>, brackets: [open: string, close: string] = ['[', ']']): string {\n const [open, close] = brackets\n if (items.length === 0) return `${open}${close}`\n if (!items.some((item) => item.includes('\\n'))) return `${open}${items.join(', ')}${close}`\n const body = items.map((item) => `${text.indent({ text: item })},`).join('\\n')\n\n return `${open}\\n${body}\\n${close}`\n}\n\n/**\n * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key\n * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation\n * of a recursive schema until first access.\n *\n * @example\n * ```ts\n * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })\n * // \"get parent() { return z.lazy(() => Pet) }\"\n * ```\n */\nexport function lazyGetter({ name, body }: { name: string; body: string }): string {\n return `get ${objectKey(name)}() { return ${body} }`\n}\n\n/**\n * An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.\n *\n * @example\n * ```ts\n * objectKey('x-total') // \"'x-total'\"\n * ```\n */\nexport function objectKey(name: string): string {\n return isIdentifier(name) ? name : text.quote(name)\n}\n\n/**\n * A JSON value as a JavaScript literal, with single-quoted strings and identifier keys left unquoted.\n *\n * @example\n * ```ts\n * toLiteral({ id: 1, 'x-tag': ['a'] }) // \"{ id: 1, 'x-tag': ['a'] }\"\n * ```\n */\nexport function toLiteral(value: unknown): string {\n if (typeof value === 'string') return text.quote(value)\n if (Array.isArray(value)) return `[${value.map(toLiteral).join(', ')}]`\n if (value && typeof value === 'object') {\n return `{ ${Object.entries(value)\n .map(([key, item]) => `${objectKey(key)}: ${toLiteral(item)}`)\n .join(', ')} }`\n }\n return String(value)\n}\n","import { jsDocEscape } from '@internals/utils'\nimport { ast, type Group, type Memo, type Output, type Resolver, type ResolverFileParams } 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 the file a schema-emitting plugin (`plugin-ts`, `plugin-zod`, `plugin-faker`) writes\n * `node` to, cached in `ctx.memo` under the resolver's plugin name and the\n * operation. The plugin itself and every plugin that imports from it call this, so they all get\n * the same path.\n *\n * When that path matches the file of a component schema in `ctx.driver.inputNode`, ignoring case, the file name\n * gets an `Operation` suffix: operation `point` goes to `PointOperation.ts` next to schema `Point.ts`.\n * Otherwise the two merge into one file, or overwrite each other on a case-insensitive file system.\n * Only the file name changes. The declarations keep their names.\n *\n * @example Resolve `plugin-ts`'s file for the current operation\n * ```ts\n * const fileTs = resolveOperationFile({ ctx, node, resolver: tsResolver, root, output })\n * ```\n */\nexport function resolveOperationFile(options: {\n ctx: { memo: Memo; driver: { inputNode?: { schemas: ReadonlyArray<ast.SchemaNode> } | null } }\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 { ctx, node, resolver, root, output } = options\n const { memo } = ctx\n const location = { root, output, group: options.group ?? undefined }\n\n return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {\n const schemaPaths = memo(\n `${resolver.pluginName}:schemaFilePaths`,\n () =>\n new Set(\n (ctx.driver.inputNode?.schemas ?? []).flatMap((schema) =>\n schema.name ? [resolver.file({ name: schema.name, extname: '.ts', ...location }).path.toLowerCase()] : [],\n ),\n ),\n )\n let file = resolver.file({ ...operationFileEntry(node, node.operationId), ...location })\n\n for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += 'Operation') {\n const next = resolver.file({ ...operationFileEntry(node, name), ...location })\n // A single-file output holds every declaration on purpose, so a new name changes nothing.\n if (next.path === file.path) break\n file = next\n }\n\n return file\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 cookies(node: ast.OperationNode, param: ast.ParameterNode): string\n }\n }\n\nexport type BuildOperationCommentsOptions = {\n splitLines?: boolean\n}\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\n/**\n * The operation's method and path as plain text, such as `` `GET /pet/{petId}` ``. A path is not a valid `{@link}` target.\n */\nfunction getOperationRequestLine(node: ast.OperationNode): string | null {\n return ast.http.isOperation(node) && node.path ? `\\`${node.method.toUpperCase()} ${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(ast.http.responses.findPrimary(node)?.content?.map((e) => e.contentType) ?? [])\n}\n\ntype ResponseType = 'blob' | 'text' | 'stream'\n\ntype ResponseContentEntry = { contentType: string; schema?: ast.SchemaNode | null; stream?: ast.ContentNode['stream'] }\n\n/**\n * How an operation streams its primary success response item by item, when that response declares one content type:\n * - `'event'` for `text/event-stream` whose `itemSchema` types the whole event with its own `event` and `data`\n * - `'data'` for `text/event-stream` typed by its payload only\n * - `'line'` for JSON Lines, one parsed JSON value per line\n *\n * Returns `null` for any other response, and for a response that also declares another content type, which stays a normal\n * call with its query hooks.\n */\nexport function getEventStream(node: ast.OperationNode): 'event' | 'data' | 'line' | null {\n const content = ast.http.responses.findPrimary(node)?.content ?? []\n if (content.length !== 1) return null\n const entry = content[0]!\n const framing = getStreamFraming(entry)\n if (framing === 'events') return entry.stream?.shape ?? 'data'\n return framing === 'lines' ? 'line' : null\n}\n\n/**\n * How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.\n */\nexport function getStreamFraming(entry: Pick<ast.ContentNode, 'contentType' | 'stream'>): 'events' | 'lines' | undefined {\n if (ast.mediaType.getBase(entry) === 'text/event-stream') return 'events'\n // `multipart/mixed` with an `itemSchema` also has `stream`, but it is a stream of parts, not of JSON values.\n return entry.stream && ast.mediaType.isStream(entry) ? 'lines' : undefined\n}\n\n/**\n * Whether an operation streams its primary success response, as Server-Sent Events or JSON Lines. The client generator uses\n * this to return a typed stream of items instead of a one-shot `RequestResult`.\n */\nexport function isEventStream(node: ast.OperationNode): boolean {\n return getEventStream(node) !== null\n}\n\n// A stream without a schema is a `blob` with a string primitive, so it keeps the JSON-parse default.\nfunction isTextSchema(schema: ast.SchemaNode | null | undefined): boolean {\n if (!schema) return false\n const resolved = ast.ref.getSchema(schema)\n return resolved.type !== 'blob' && resolved.primitive === 'string'\n}\n\n/**\n * Whether an operation streams Server-Sent Events whose `data` the document types as a string, so the client keeps each\n * `data` as text instead of JSON-parsing it. With an event-shaped `itemSchema` where only some events type `data` as a\n * string, it returns the names of those events, or `false` when one of them has no fixed name.\n */\nexport function hasTextEventData(node: ast.OperationNode): boolean | Array<string> {\n const shape = getEventStream(node)\n const entry = ast.http.responses.findPrimary(node)?.content?.[0]\n if (!entry || (shape !== 'data' && shape !== 'event')) return false\n if (shape === 'data') return isTextSchema(entry.stream?.schema ?? entry.schema)\n\n const item = entry.stream?.schema ? ast.ref.getSchema(entry.stream.schema) : undefined\n if (!item) return false\n const events = item.type === 'union' ? (ast.schema.narrow(item, 'union')?.members ?? []) : [item]\n // A discriminator makes each event an intersection of its `$ref` and the pinned `event`, so every part is read.\n const propertySchemas = (event: ast.SchemaNode, propertyName: string) => ast.schema.findProperties({ node: event, propertyName }).map(({ schema }) => schema)\n const textEvents = events.filter((event) => {\n const dataSchemas = propertySchemas(event, 'data')\n return dataSchemas.length > 0 && dataSchemas.every(isTextSchema)\n })\n if (textEvents.length === events.length) return events.length > 0\n\n const eventNames = textEvents.map((event) => propertySchemas(event, 'event').flatMap((schema) => ast.schema.getLiterals(ast.ref.getSchema(schema))))\n return eventNames.length > 0 && eventNames.every((values) => values.length > 0) && [...new Set(eventNames.flat().map(String))]\n}\n\n/**\n * Whether the primary success response offers `text/event-stream`, alone or next to other content types. The client asks\n * for the stream by default and ships the `sse.ts` runtime, so a mixed response can be read as events too.\n */\nexport function hasEventStreamContent(node: ast.OperationNode): boolean {\n const entries = ast.http.responses.findPrimary(node)?.content ?? []\n // Next to another type, a stream without a schema gets no typed variant, so it keeps the plain call.\n return entries.some((entry) => getStreamFraming(entry) === 'events' && (entries.length === 1 || Boolean(entry.schema)))\n}\n\n/**\n * Whether a success response offers a stream media type next to other content types. The client returns that variant's\n * body as a `ReadableStream`, so the stream runtime is registered and a hook's data type includes the stream.\n */\nexport function hasStreamVariant(node: ast.OperationNode): boolean {\n return ast.http.responses.filterSuccess(node).some((response) => {\n const variants = (response.content ?? []).filter((entry) => entry.schema)\n return variants.length > 1 && variants.some((entry) => ast.mediaType.isStream(entry))\n })\n}\n\nconst formContentTypes = ['multipart/form-data', 'application/x-www-form-urlencoded'] as const\n\n/**\n * The form media types the operation's request body offers, `multipart/form-data` and `application/x-www-form-urlencoded`,\n * which the client needs a handler for.\n */\nexport function getFormContentTypes(node: ast.OperationNode): Array<(typeof formContentTypes)[number]> {\n const request = new Set((node.requestBody?.content ?? []).map((entry) => ast.mediaType.getBase(entry)))\n return formContentTypes.filter((contentType) => request.has(contentType))\n}\n\n/**\n * Parser for one response content entry.\n *\n * JSON stays unset so the runtime detects it. `text/event-stream` and JSON Lines are streams. A binary schema\n * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known\n * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,\n * `image/*`, `audio/*`, and `video/*` are blobs.\n */\nfunction responseTypeForContent(entry: ResponseContentEntry): ResponseType | undefined {\n const baseType = ast.mediaType.getBase(entry)\n if (!baseType) return undefined\n if (getStreamFraming(entry)) return 'stream'\n if (ast.mediaType.isJson(baseType)) return undefined\n if (entry.schema?.type === 'blob') return 'blob'\n if (baseType.startsWith('text/')) return 'text'\n if (isBinaryContentType(baseType)) return 'blob'\n return undefined\n}\n\nfunction isBinaryContentType(baseType: string): boolean {\n return baseType === 'application/octet-stream' || baseType === 'application/pdf' || /^(image|audio|video)\\//.test(baseType)\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 // Keeps the original case, unlike ast.mediaType.getBase, so generated names stay stable.\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 * Returns the entry with the schema of the value the client returns: `string` for a body read as text, such as `Pet` on\n * `application/xml`, and `blob` for `image/*`, `audio/*`, `video/*`, PDF, and octet-stream. A binary schema elsewhere is a `Blob`\n * only through `parseAs: 'blob'`, which needs every entry in `entries` binary. JSON, vendor (`application/vnd.*`), stream,\n * schemaless, and wildcard entries keep their schema. `entries` is the response's full list of content types.\n */\nexport function toResponseContent<T extends ContentVariantInput & ResponseContentEntry>(entry: T, entries: ReadonlyArray<ResponseContentEntry> = [entry]): T {\n const { contentType, schema } = entry\n if (!schema || entry.stream) return entry\n\n const baseType = ast.mediaType.getBase(contentType)\n const resolved = ast.ref.getSchema(schema)\n if (isBinaryContentType(baseType)) {\n return resolved.type === 'blob' ? entry : { ...entry, schema: ast.factory.createSchema({ type: 'blob' }), keysToOmit: null }\n }\n const isPlainMediaType = Boolean(baseType) && !ast.mediaType.isJson(baseType) && !baseType.includes('*')\n if (resolved.type === 'blob') {\n // shortcut: an error response never gets `parseAs`, so a lone binary schema there reads as text too but stays `Blob`.\n const readsAsText = isPlainMediaType && !entries.every((item) => item.schema?.type === 'blob')\n return readsAsText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n }\n if (resolved.primitive === 'string') return entry\n\n const responseType = responseTypeForContent(entry)\n // A response without a body, such as a 405, has no schema to read. Only `text/*` is always text.\n if (responseType !== 'text' && (resolved.type === 'unknown' || resolved.type === 'any' || resolved.type === 'void')) return entry\n const isVendorJson = /^[^/]+\\/vnd\\./.test(baseType) && !/(xml|yaml|yml|csv)$/.test(baseType)\n const isText = responseType === 'text' || (!responseType && isPlainMediaType && !isVendorJson)\n\n return isText ? { ...entry, schema: ast.factory.createSchema({ type: 'string' }), keysToOmit: null } : entry\n}\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/**\n * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,\n * `useInfiniteQuery`, `useSWR`, ...): `RequestConfig` minus what the\n * hook fills in, which is every request group and `throwOnError`. Unlike {@link buildRequestConfigType},\n * it never narrows `contentType`: query hooks wrap GET operations, which carry no request body to select\n * a content type for.\n */\nexport function buildClientOptionType(options: { omit?: Array<string> } = {}): string {\n const keys = [...requestGroupOrder, 'url', ...(options.omit ?? []), 'throwOnError']\n return `Partial<Omit<RequestConfig, ${keys.map((key) => `'${key}'`).join(' | ')}>>`\n}\n\n/**\n * The `client?:` option type of a mutation hook. When the operation has a request content type choice,\n * the mutation variables carry `contentType`, so the config leaves it out. Otherwise the ambiguous side is\n * narrowed to the operation's media types; a single-type side has nothing to pick, so it stays baked in the\n * generated call.\n */\nexport function buildRequestConfigType(node: ast.OperationNode): string {\n if (hasRequestContentTypeChoice(node)) {\n return buildClientOptionType({ omit: ['contentType'] })\n }\n\n const request = getContentTypeInfo(node)\n const response = getResponseContentTypeInfo(node)\n const members = [\n request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null,\n response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null,\n ].filter(Boolean)\n\n const base = buildClientOptionType()\n return members.length ? `${base} & { contentType?: { ${members.join('; ')} } }` : base\n}\n\n/** The grouped request options, in the order generated signatures and calls list them. */\nexport const requestGroupOrder = ['path', 'query', 'body', 'headers', 'cookies'] as const\n\nexport type RequestGroups = {\n path: boolean\n query: boolean\n body: boolean\n headers: boolean\n cookies: 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, cookie } = ast.http.groupParameters(node)\n return {\n path: path.length > 0,\n query: query.length > 0,\n body: Boolean(ast.http.requestBody.getSchema(node)),\n headers: header.length > 0,\n cookies: cookie.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 } = ast.http.groupParameters(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 // `cookies` never makes the parameter required: a browser sends its cookies itself and cannot set a `Cookie` header.\n isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !(groups.body && node.requestBody?.required === true),\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, cookies }` 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; names: Array<keyof RequestGroups> } {\n const { isConfigurable = true } = options\n const { groups, isOptional } = getRequestGroupOptionality(node)\n\n const names = requestGroupOrder.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 names,\n }\n}\n\nexport function buildOperationComments(node: ast.OperationNode, options: BuildOperationCommentsOptions = {}): Array<string> {\n const { splitLines = false } = options\n // The request line leads: free text after a block tag would belong to that tag.\n const comments = [\n getOperationRequestLine(node),\n node.description && `@description ${node.description}`,\n node.summary && `@summary ${node.summary}`,\n node.deprecated && '@deprecated',\n node.externalDocs && `@see ${jsDocEscape([node.externalDocs.url, node.externalDocs.description].filter(Boolean).join(' '))}`,\n ]\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\n/**\n * Whether the operation declares more than one request media type with a schema, so a caller must pick one on `contentType`.\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\n/**\n * Builds the combined `{ body, path, query, headers, cookies }` options object schema for an operation,\n * referencing the already-resolved body and grouped param names. `cookies` appears only on an operation\n * with a cookie parameter, and stays optional because a browser sends its cookies itself. Shared by `@kubb/plugin-ts`'s\n * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same shape from the same inputs.\n * `primitive: 'object'` is a no-op for the TS printer and tells the Zod printer to emit `z.object(…)` rather\n * than a record.\n */\nexport function buildOptionsSchema(node: ast.OperationNode, resolver: OperationTypeNameResolver, { pairContentTypes = false } = {}): ast.SchemaNode {\n const { path, query, header, cookie } = ast.http.groupParameters(node)\n const hasBody = Boolean(ast.http.requestBody.getSchema(node))\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 { name: 'cookies', params: cookie, resolve: resolver.param.cookies },\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 && node.requestBody?.required === true, schema: body }),\n ...groups\n .filter(({ name, params }) => name !== 'cookies' || params.length > 0)\n .map(({ name, params, resolve }) => {\n const required = name !== 'cookies' && 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`. A bare string sets the\n // request content type at runtime, so it is accepted next to the `{ request }` form.\n return ast.factory.createSchema({\n type: 'union',\n members: variants.map((variant, index) => {\n const contentType = ast.factory.createSchema({ type: 'enum', enumValues: [variant.contentType] })\n\n return 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: 'union',\n members: [\n contentType,\n ast.factory.createSchema({\n type: 'object',\n primitive: 'object',\n properties: [ast.factory.createProperty({ name: 'request', required: true, schema: 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. With `streamData`, the stream variant of a 2xx status holds that schema, since the\n * client returns its body unread.\n */\nfunction buildResponseRecordEntry(\n node: ast.OperationNode,\n res: ast.ResponseNode,\n resolver: ResponseStatusNameResolver,\n streamData: ast.SchemaNode | undefined,\n): 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 // The runtime reports the bare media type, without parameters such as `charset` or `profile`.\n schema: ast.factory.createSchema({ type: 'enum', enumValues: [ast.mediaType.getBase(variant)] }),\n }),\n ast.factory.createProperty({\n name: 'data',\n required: true,\n schema:\n streamData && ast.http.status.isSuccess(res) && ast.mediaType.isStream(variant)\n ? streamData\n : 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 *\n * `streamData` is the type of a stream variant's `data`, such as a `ReadableStream<Uint8Array>` reference.\n */\nexport function buildResponses(\n node: ast.OperationNode,\n resolver: ResponseStatusNameResolver,\n { streamData }: { streamData?: ast.SchemaNode } = {},\n): 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, streamData),\n }),\n ),\n })\n}\n\nexport function resolveErrorNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterError(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveSuccessNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return ast.http.responses.filterSuccess(node).map((response) => resolver.response.status(node, response.statusCode))\n}\n\nexport function resolveStatusCodeNames(node: ast.OperationNode, resolver: ResponseStatusNameResolver): string[] {\n return node.responses.map((response) => resolver.response.status(node, response.statusCode))\n}\n\n/**\n * Lists the type names an operation's types file declares, computed once per resolver and `options` so every\n * dependent reads the same list.\n */\nexport function resolveOperationTypeNames(\n node: ast.OperationNode,\n resolver: OperationTypeNameResolver & Pick<Resolver, 'memo'>,\n options: ResolveOperationTypeNameOptions = {},\n): string[] {\n const key = `${node.operationId}\\0${options.order ?? ''}\\0${options.responseStatusNames ?? ''}\\0${options.includeParams === false ? 'noparams' : ''}\\0${(options.exclude ?? []).join(',')}`\n return resolver.memo(`operationTypeNames:${key}`, () => {\n const { path, query, header, cookie } = ast.http.groupParameters(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 ...cookie.map((param) => resolver.param.cookies(node, param)),\n ]\n const bodyAndResponseNames = [ast.http.requestBody.getSchema(node) ? resolver.response.body(node) : null, resolver.response.response(node)]\n const names =\n options.order === 'body-response-first'\n ? [...bodyAndResponseNames, ...paramNames, ...responseStatusNames]\n : [...paramNames, ...bodyAndResponseNames, ...responseStatusNames]\n\n return names.filter((name): name is string => Boolean(name) && !exclude.has(name as string))\n })\n}\n\nexport function resolveResponseTypes(node: ast.OperationNode, resolver: ResponseNameResolver): Array<[statusCode: number | 'default', typeName: string]> {\n const types: Array<[number | 'default', string]> = []\n\n for (const response of node.responses) {\n if (response.statusCode === 'default') {\n types.push(['default', resolver.response.response(node)])\n continue\n }\n\n const code = Number(response.statusCode)\n if (Number.isNaN(code)) {\n continue\n }\n\n types.push([code, ast.http.status.isSuccess(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)])\n }\n\n return types\n}\n","import { ensureValidVarName, toFilePath } from '@internals/utils'\nimport { ast, casing } from '@kubb/kit'\nimport type { GeneratorContext, Resolver, ResolverFile } from '@kubb/kit'\n\n/**\n * The shared top-level `name` of the operation plugins, computed once per operation name and build.\n */\nexport function resolveOperationName(this: Resolver, name: string): string {\n return this.memo(`operation:${name}`, () => ensureValidVarName(casing.camel(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 * The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro\n * rewrites the method or path still resolves another plugin's names for the same operation.\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 const source = node.pointer ? [node.pointer] : [node.method, node.path]\n return ['operation', ...source, ...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`/`Cookies` 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 cookies(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 /**\n * Name of the headers a response declares, such as `ListPetsStatus200Headers`.\n */\n headers(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 cookies(node) {\n const name = this.name(`${node.operationId} Cookies`)\n return this.resolveName({ key: operationKey(node, 'param', 'cookies'), 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 headers(node, statusCode) {\n const name = this.name(`${node.operationId} Status ${statusCode} Headers`)\n return this.resolveName({ key: operationKey(node, 'response', 'headers', statusCode), name })\n },\n body(node) {\n const name = this.name(`${node.operationId} Body`)\n return this.resolveName({ key: operationKey(node, 'response', 'body'), name })\n },\n responses(node) {\n const name = this.name(`${node.operationId} Responses`)\n return this.resolveName({ key: operationKey(node, 'response', 'responses'), name })\n },\n response(node) {\n const name = this.name(`${node.operationId} Response`)\n return this.resolveName({ key: operationKey(node, 'response', 'response'), name })\n },\n }\n}\n\n/**\n * Builds a resolver `file` override whose base name runs every path segment\n * through `toFilePath`, casing the final segment with `caseLast`.\n *\n * @example\n * ```ts\n * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })\n * ```\n */\nexport function createCasedFile(caseLast: (part: string) => string): ResolverFile {\n return {\n baseName({ name, extname }) {\n return this.memo(`file:${name}\\0${extname}`, (): ast.FileNode['baseName'] => `${toFilePath(name, caseLast)}${extname}`)\n },\n }\n}\n\ntype SchemaIndexContext = Pick<GeneratorContext, 'memo' | 'driver'>\n\n/**\n * Names of the schemas in a circular reference chain, computed once per build.\n */\nexport function getCircularNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo('circularNames:set', () => ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []))\n}\n\n/**\n * Names of the schemas whose type is `enum`, computed once per build.\n */\nexport function getEnumNames(ctx: SchemaIndexContext): ReadonlySet<string> {\n return ctx.memo(\n 'enumNames:set',\n () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => (schema.type === 'enum' && schema.name ? [schema.name] : []))),\n )\n}\n\ndeclare global {\n namespace Kubb {\n interface MemoRegistry {\n operation: string\n file: ast.FileNode['baseName']\n operationTypeNames: Array<string>\n }\n }\n}\n","import { isValidVarName, toLiteral } from '@internals/utils'\nimport { toResponseContent } from '@internals/shared'\n\nimport { ast, text } from '@kubb/kit'\n\n// Headers use the `simple` style, so an array or object is sent comma-separated.\nfunction toSimpleStyle(value: unknown): string {\n if (Array.isArray(value)) return value.join(',')\n if (typeof value === 'object' && value !== null) return Object.entries(value).flat().join(',')\n return String(value)\n}\n\n/**\n * The declared headers a mock sends, each with its example's `serializedValue` or `value`, else its schema's `default` or first example.\n */\nexport function getMockHeaders(response: ast.ResponseNode | null | undefined): Array<[name: string, value: string]> {\n return (response?.headers ?? []).flatMap((header): Array<[string, string]> => {\n // OpenAPI ignores a `Content-Type` response header, and the mock already sends one.\n if (header.name.toLowerCase() === 'content-type') return []\n const example = header.examples?.find((entry) => entry.serializedValue !== undefined || entry.value !== undefined)\n const headerValue = example?.serializedValue ?? example?.value ?? header.schema.default ?? header.schema.examples?.[0]\n if (headerValue === undefined || headerValue === null) return []\n const headerText = toSimpleStyle(headerValue)\n // `Headers` throws on a line break or a character above U+00FF, so such an example is left out.\n return /^[\\t\\x20-\\x7E\\x80-\\xFF]*$/.test(headerText) ? [[header.name, headerText]] : []\n })\n}\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 contentType = getResponseEntry(response)?.contentType\n return typeof contentType === 'string' && contentType.length > 0 ? contentType : null\n}\n\nfunction getResponseEntry(response: ast.ResponseNode | null | undefined): ast.ContentNode | undefined {\n const contents = response?.content ?? []\n return contents.find((entry) => ast.mediaType.isJson(entry)) ?? contents[0]\n}\n\n/**\n * The first content type of a response that the client reads as text, such as `application/xml`, or `null` when none is.\n */\nfunction getTextContentType(response: ast.ResponseNode | null | undefined): string | null {\n const entry = (response?.content ?? []).find((entry) => {\n const schema = toResponseContent(entry, response?.content).schema\n return !ast.mediaType.isJson(entry) && schema && ast.ref.getSchema(schema).primitive === 'string'\n })\n return entry?.contentType ?? null\n}\n\n// The client's `jsonReplacer`: a `bigint` goes out as a JSON number, and throws past the safe-integer range instead of losing precision.\nconst bigintReplacer = `(_key, value) => {\n if (typeof value !== 'bigint') return value\n if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) throw new TypeError(\\`Cannot serialize \\${value}n as JSON without losing precision.\\`)\n return Number(value)\n }`\n\n/** The `JSON.stringify` call for `body`, with a replacer when the response holds an `int64`, which `JSON.stringify` throws on. */\nfunction stringify(body: string, response: ast.ResponseNode | null | undefined): string {\n const hasBigint = (response?.content ?? []).some(\n (entry) => entry.schema && ast.schema.find(entry.schema, { predicate: (schema) => schema.type === 'bigint', followRefs: true }) !== undefined,\n )\n return hasBigint ? `JSON.stringify(${body}, ${bigintReplacer})` : `JSON.stringify(${body})`\n}\n\n/**\n * The statements that return a mocked `Response` for `body`. A string body for a response the client reads as text goes\n * out as raw text with that content type, so an XML mock is not sent as a quoted JSON string. Any other body is JSON.\n */\nexport function responseReturn(body: string, status: number, response: ast.ResponseNode | null | undefined): string {\n const contentType = getContentType(response)\n const headers = getMockHeaders(response)\n // A plain variable such as `data` is checked as it is, an expression such as `data ?? createPet()` is read once.\n const name = isValidVarName(body) ? body : 'body'\n const prelude = name === body ? '' : `const body = ${body}\\n `\n\n // shortcut: one event per mock, upgrade when a mock needs to send several events.\n if (contentType && ast.mediaType.getBase(contentType) === 'text/event-stream') {\n const entry = getResponseEntry(response)\n // Without a body, a spec example already serialized as SSE text goes out as it is.\n const serialized = entry?.examples?.find((example) => example.serializedValue !== undefined)?.serializedValue\n const fallback = serialized === undefined ? \"''\" : toLiteral(serialized)\n if (entry?.stream?.shape !== 'event') {\n return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \\`\\${${eventDataLines(name, response)}}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n // The item is the whole event, so its fields become SSE lines and the client parses it back to the same object.\n return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined\n const lines = [\n ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \\`\\${field}: \\${event?.[field]}\\`),\n // A client dispatches an event only once it has a \\`data\\` line, so an event without data still sends an empty one.\n event?.data === undefined ? 'data:' : ${eventDataLines('event.data', response)},\n ]\n\n return ${responseLiteral(`event === undefined ? ${fallback} : \\`\\${lines.join('\\\\n')}\\\\n\\\\n\\``, status, contentType, headers)}`\n }\n\n const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null\n if (!textContentType) return `return ${responseLiteral(stringify(body, response), status, contentType, headers)}`\n\n return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}\n\n return ${responseLiteral(stringify(name, response), status, contentType, headers)}`\n}\n\n/**\n * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.\n */\nfunction eventDataLines(body: string, response: ast.ResponseNode | null | undefined): string {\n return `(typeof ${body} === 'string' ? ${body} : ${stringify(body, response)}).split('\\\\n').map((line) => \\`data: \\${line}\\`).join('\\\\n')`\n}\n\n/**\n * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.\n */\nexport function getExampleBody(response: ast.ResponseNode | null | undefined): string | null {\n const entry = getResponseEntry(response)\n const example = entry?.examples?.find((item) => item.value !== undefined || item.serializedValue !== undefined)\n if (!entry || !example) return null\n if (example.value !== undefined) return toLiteral(example.value)\n // `responseReturn` sends raw text only for a text response. For a binary one the example would go out JSON-encoded.\n // A serialized SSE example is already events, which `responseReturn` sends as is when the body is `undefined`.\n if (!ast.mediaType.isJson(entry)) {\n const isTextBody = ast.mediaType.getBase(entry) !== 'text/event-stream' && getTextContentType(response) === entry.contentType\n return isTextBody ? toLiteral(example.serializedValue) : null\n }\n // `responseReturn` encodes a JSON body itself, so a serialized JSON example is parsed first.\n try {\n return toLiteral(JSON.parse(example.serializedValue!))\n } catch {\n return null\n }\n}\n\nconst mswMethods = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n\n/**\n * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no `http.query`, `http.trace` or\n * handler for a custom `additionalOperations` method, so such an operation maps to `http.all` and its handler checks the\n * method itself.\n */\nexport function getMswMethod(node: ast.HttpOperationNode): string {\n return mswMethods.has(node.method) ? node.method.toLowerCase() : 'all'\n}\n\n/**\n * The `headers` entry of a mocked `Response` init, or an empty string when there is no header to send.\n */\nfunction headersBlock(contentType: string | null, headers: Array<[string, string]>): string {\n const entries = [\n ...(contentType ? [`'Content-Type': ${text.quote(contentType)}`] : []),\n ...headers.map(([name, value]) => `${text.quote(name)}: ${text.quote(value)}`),\n ]\n return entries.length ? `\\n headers: {\\n ${entries.join(',\\n ')}\\n },` : ''\n}\n\n/**\n * The `new HttpResponse<string>(...)` expression a mocked handler returns.\n */\nexport function responseLiteral(body: string, status: number, contentType: string | null, headers: Array<[string, string]>): string {\n return `new HttpResponse<string>(${body}, {\n status: ${status},${headersBlock(contentType, headers)}\n })`\n}\n","import { ast, text } from '@kubb/kit'\nimport { Url } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { getExampleBody, getMswMethod, hasResponseSchema, resolveDefaultBaseURL, responseReturn } from '../utils.ts'\n\ntype Props = {\n name: string\n typeName: string\n requestTypeName?: string | null\n fakerName?: string\n /**\n * The `...Response<status>` helper of the response this handler mocks, when its `data` has the handler's type.\n */\n responseName?: string\n baseURL: string | null | undefined\n node: ast.HttpOperationNode\n}\n\nexport function Mock({ baseURL = '', name, fakerName, responseName, typeName, requestTypeName, node }: Props): KubbReactNode {\n const method = getMswMethod(node)\n // `http.all` matches every method on the path, so a handler for a method MSW has no helper for lets the others pass through.\n const methodGuard = method === 'all' ? `if (info.request.method !== ${text.quote(node.method)}) return\\n\\n ` : ''\n const successResponse = ast.http.responses.findPrimary(node)\n const statusCode = successResponse ? ast.http.status.toCode(successResponse) : 200\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 // Without Faker, the spec's own response example is the default body. An explicit `null` stays a `null` body.\n const example = fakerName ? null : getExampleBody(successResponse)\n const exampleBody = example ? `data === undefined ? ${example} : data` : 'data'\n const responseBody = fakerName ? `data ?? ${fakerName}()` : exampleBody\n // A spec example is not checked against the response type, so it is not passed to the typed helper.\n const responseStatements = responseName && !example ? `return ${responseName}(${responseBody})` : responseReturn(responseBody, statusCode, successResponse)\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 ${responseStatements}\n })`}\n </Function>\n </File.Source>\n )\n}\n","import { ast } from '@kubb/kit'\nimport { File, Function } from '@kubb/renderer-jsx'\nimport type { KubbReactNode } from '@kubb/renderer-jsx'\nimport { responseReturn } 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 // Optional, since a handler passes on the `data` it got, which may be left out.\n const params = `data?: ${typeName}`\n\n const responseName = `${name}Response${response.statusCode}`\n\n return (\n <File.Source name={responseName} isIndexable isExportable>\n <Function name={responseName} export params={params}>\n {`\n ${responseReturn('data', ast.http.status.toCode(response), response)}`}\n </Function>\n </File.Source>\n )\n}\n","import { getEnumNames, resolveOperationFile } 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/renderer-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.http.isOperation(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: resolveOperationFile({\n ctx,\n node,\n resolver: fakerResolver,\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: resolveOperationFile({\n ctx,\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 = ast.http.responses.filterSuccess(node)\n const referencedNames = ast.http.responses.getImportedNames(node.responses)\n const enumOptions = pluginTs.options?.enum\n const enumNames = getEnumNames(ctx)\n const hasResponseNameCollision = referencedNames.some((name) => {\n return enumTypeName({ resolver: tsResolver, name, enumOptions, isEnum: enumNames.has(name) }) === type.responseName\n })\n // One helper per documented status, a range such as `4xx` included. `default` has no status to mock,\n // and the Fetch `Response` constructor rejects a `1xx` status.\n const types = node.responses\n .filter((response) => response.statusCode !== 'default' && ast.http.status.getClass(response) !== 1)\n .map((response) => {\n const isResponseType = ast.http.status.isSuccess(response) && !hasResponseNameCollision\n return [response, isResponseType ? type.responseName : tsResolver.response.status(node, response.statusCode)] as const\n })\n const responseTypeNames = types.map(([, typeName]) => typeName)\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 // The handler calls the helper of the response it mocks when that helper takes the handler's own data type.\n const primaryResponse = ast.http.responses.findPrimary(node)\n const responseHelperName =\n primaryResponse &&\n hasResponseSchema(primaryResponse) &&\n types.some(([response, typeName]) => response.statusCode === primaryResponse.statusCode && typeName === mockResponseName)\n ? `${mock.name}Response${primaryResponse.statusCode}`\n : undefined\n\n const requestName = ast.http.requestBody.getSchema(node) ? 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.map(([response, typeName]) => (\n <Response key={response.statusCode} 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 responseName={responseHelperName}\n node={node}\n baseURL={baseURL}\n />\n </File>\n )\n },\n})\n","import { operationKey } from '@internals/shared'\nimport { casing, createResolver } from '@kubb/kit'\nimport type { PluginMsw } from '../types.ts'\n\n/**\n * Default resolver used by `@kubb/plugin-msw`. Decides the names and file\n * paths for every generated MSW handler. Function names get a `Handler`\n * suffix; the aggregate export is always `handlers`.\n *\n * The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in\n * `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.\n *\n * @example Resolve a handler name\n * ```ts\n * import { resolverMsw } from '@kubb/plugin-msw'\n *\n * resolverMsw.name('addPet') // 'addPetHandler'\n * ```\n */\nexport const resolverMsw = createResolver<PluginMsw>({\n pluginName: 'plugin-msw',\n name(name) {\n return this.memo(`name:${name}`, () => casing.camel(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 { 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: group ?? null,\n exclude,\n include,\n override,\n handlers,\n }\n },\n})\n\nexport default pluginMsw\n"],"mappings":"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"}
@@ -39,26 +39,31 @@ export const server = setupServer(
39
39
 
40
40
  ## Generate mock data
41
41
 
42
- Add `pluginFaker` and change the MSW [`parser`](https://kubb.dev/extensions/kubb-labs/plugin-msw/options#parser) to `'faker'`.
42
+ Add `pluginFaker` and change the MSW [`parser`](https://kubb.dev/extensions/kubb-labs/plugin-msw/options#parser) to `'faker'`. A handler sends an `int64` field, which Faker generates as a `bigint`, as a JSON number. A value outside the safe-integer range throws instead of losing precision.
43
43
 
44
44
  ```typescript [kubb.config.ts]
45
+ import { defineConfig } from 'kubb/config'
45
46
  import { pluginTs } from '@kubb/plugin-ts'
46
47
  import { pluginFaker } from '@kubb/plugin-faker'
47
48
  import { pluginMsw } from '@kubb/plugin-msw'
48
49
 
49
- const plugins = [
50
- pluginTs(),
51
- pluginFaker(),
52
- pluginMsw({
53
- output: { path: 'handlers', mode: 'directory' },
54
- baseURL: 'http://localhost:3000',
55
- parser: 'faker',
56
- handlers: true,
57
- }),
58
- ]
50
+ export default defineConfig({
51
+ input: './petStore.yaml',
52
+ output: { path: './src/gen' },
53
+ plugins: [
54
+ pluginTs(),
55
+ pluginFaker(),
56
+ pluginMsw({
57
+ output: { path: 'handlers', mode: 'directory' },
58
+ baseURL: 'http://localhost:3000',
59
+ parser: 'faker',
60
+ handlers: true,
61
+ }),
62
+ ],
63
+ })
59
64
  ```
60
65
 
61
- Use this array as `defineConfig.plugins`. Calling `getPetByIdHandler()` without data now uses generated Faker values.
66
+ Calling `getPetByIdHandler()` without data now uses generated Faker values.
62
67
 
63
68
  ## Register the handler collection
64
69
 
package/docs/options.md CHANGED
@@ -35,7 +35,7 @@ A value with a template expression, such as `'${process.env.API_URL}'`, is evalu
35
35
  Emits a `handlers.ts` file with every generated handler in operation order. It exports two things:
36
36
 
37
37
  - `handlers`: the collection with the configured `baseURL`. Spread it into `setupServer(...handlers)` or `setupWorker(...handlers)`.
38
- - `createHandlers(options?)`: builds the same collection with a `baseURL` you pass at runtime.
38
+ - `createHandlers(options?)`: builds the same collection with a `baseURL` passed at runtime.
39
39
 
40
40
  | | |
41
41
  | -------- | --------- |
@@ -67,7 +67,7 @@ Source of the response body each handler returns.
67
67
  ::field-group
68
68
 
69
69
  :::field{name="'data'"}
70
- Default value. Returns the data you pass to the handler, typed with the `@kubb/plugin-ts` types. The handler uses `new HttpResponse(JSON.stringify(data), ...)`. Without `data`, it sends the success response's first example from the spec, when it has one: its `value`, else its `serializedValue`, parsed for a JSON response and sent as is for a text response. Pass `null` to send a `null` body.
70
+ Default value. Returns the data passed to the handler, typed with the `@kubb/plugin-ts` types. The handler uses `new HttpResponse(JSON.stringify(data), ...)`. Without `data`, the handler sends the first example of the success response, when the spec has one. It reads the example's `value`, else its `serializedValue`, which is parsed for a JSON response and sent as is for a text response. Pass `null` to send a `null` body.
71
71
  :::
72
72
 
73
73
  :::field{name="'faker'"}
@@ -77,18 +77,24 @@ Falls back to a value generated by `@kubb/plugin-faker` when `data` is `undefine
77
77
  ::
78
78
 
79
79
  ```typescript ['faker']
80
- export function getPetHandler(data?: GetPetQueryResponse | ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Response | Promise<Response>)) {
81
- return http.get('/pet/:petId', function handler(info) {
80
+ export function getPetHandler(
81
+ data?: GetPetResponse | ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Response | Promise<Response>),
82
+ options?: { baseURL?: string },
83
+ ) {
84
+ return http.get(`${options?.baseURL ?? ''}/pet/:petId`, function handler(info) {
82
85
  if (typeof data === 'function') return data(info)
83
86
 
84
- return new HttpResponse(JSON.stringify(data ?? createGetPetQueryResponse(data)), {
85
- status: 200,
86
- headers: { 'Content-Type': 'application/json' },
87
- })
87
+ return getPetHandlerResponse200(data ?? createGetPetResponse())
88
88
  })
89
89
  }
90
90
  ```
91
91
 
92
+ Each handler file also exports one response helper per status code in the spec, such as `getPetHandlerResponse200(data)` and `getPetHandlerResponse404(data)`. Return a helper from the callback to mock another status:
93
+
94
+ ```typescript [test-server.ts]
95
+ getPetHandler(() => getPetHandlerResponse404())
96
+ ```
97
+
92
98
  ### resolver
93
99
 
94
100
  Overrides generated file and symbol names. Omitted members keep `resolverMsw`. The shared members (`name`, `file`, `imports`) and the `this` context are described under [`resolver`](https://kubb.dev/docs/5.x/reference/plugin-options#resolver).
package/docs/overview.md CHANGED
@@ -8,7 +8,7 @@ Each handler returns the data you pass to it. With `parser: 'faker'`, a handler
8
8
 
9
9
  - Add `@kubb/plugin-ts` for the request and response types.
10
10
  - Add `@kubb/plugin-faker` when you set `parser: 'faker'`, and install `@faker-js/faker` v10. The default `data` parser does not need it.
11
- - Install `msw` v2 in the app that uses the generated code.
11
+ - Install `msw` v3 in the app that uses the generated code.
12
12
 
13
13
  ## Options worth knowing
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-msw",
3
- "version": "6.2.0",
3
+ "version": "6.2.1",
4
4
  "description": "Generate Mock Service Worker (MSW) request handlers with Kubb. Intercept HTTP requests in the browser or Node.js for frontend development and testing.",
5
5
  "keywords": [
6
6
  "api-mocking",
@@ -85,10 +85,10 @@
85
85
  "registry": "https://registry.npmjs.org/"
86
86
  },
87
87
  "dependencies": {
88
- "@kubb/kit": "^5.18.6",
89
- "@kubb/plugin-faker": "^5.4.0",
90
- "@kubb/plugin-ts": "^5.5.0",
91
- "@kubb/renderer-jsx": "^5.18.6"
88
+ "@kubb/kit": "^5.18.8",
89
+ "@kubb/plugin-faker": "^5.4.1",
90
+ "@kubb/plugin-ts": "^5.5.1",
91
+ "@kubb/renderer-jsx": "^5.18.8"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@internals/shared": "0.0.0",