@kubb/plugin-msw 6.2.0-canary.20261111T122007 → 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
@@ -7,6 +7,238 @@ let _kubb_plugin_faker = require("@kubb/plugin-faker");
7
7
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
8
8
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
9
9
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
10
+ // kubb-extension.json
11
+ var compatibility = {
12
+ "kubb": ">=5.18.4",
13
+ "node": ">=22.22"
14
+ };
15
+ // package.json
16
+ var version = "6.2.1";
17
+ // src/generators/handlersGenerator.ts
18
+ /**
19
+ * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
20
+ * `handlers.ts` file that re-exports every generated handler in operation
21
+ * order, ready to spread into `setupServer(...handlers)` or
22
+ * `setupWorker(...handlers)`.
23
+ */
24
+ const handlersGenerator = (0, _kubb_kit.defineGenerator)({
25
+ name: "plugin-msw",
26
+ operations(nodes, ctx) {
27
+ const { resolver, config, root } = ctx;
28
+ const { output, group } = ctx.options;
29
+ const handlersName = resolver.handler.listName();
30
+ const createHandlersName = resolver.resolveName({
31
+ key: ["handlers", "create"],
32
+ name: _kubb_kit.casing.camel(handlersName, { prefix: "create" })
33
+ });
34
+ const file = resolver.file({
35
+ name: handlersName,
36
+ extname: ".ts",
37
+ root,
38
+ output,
39
+ group: group ?? void 0
40
+ });
41
+ const handlerNames = nodes.map((node) => resolver.handler.name(node));
42
+ const imports = nodes.map((node, index) => {
43
+ const operationName = handlerNames[index];
44
+ const operationFile = resolver.file({
45
+ name: resolver.name(node.operationId),
46
+ extname: ".ts",
47
+ tag: node.tags[0] ?? "default",
48
+ path: node.path,
49
+ root,
50
+ output,
51
+ group: group ?? void 0
52
+ });
53
+ return _kubb_kit.ast.factory.createImport({
54
+ name: [operationName],
55
+ root: file.path,
56
+ path: operationFile.path
57
+ });
58
+ });
59
+ const handlers = handlerNames.map((name) => `${name}(undefined, options)`);
60
+ return [_kubb_kit.ast.factory.createFile({
61
+ baseName: file.baseName,
62
+ path: file.path,
63
+ meta: file.meta,
64
+ banner: resolver.default.banner(ctx.meta, {
65
+ output,
66
+ config,
67
+ file: {
68
+ path: file.path,
69
+ baseName: file.baseName
70
+ }
71
+ }),
72
+ footer: resolver.default.footer(ctx.meta, {
73
+ output,
74
+ config,
75
+ file: {
76
+ path: file.path,
77
+ baseName: file.baseName
78
+ }
79
+ }),
80
+ imports,
81
+ sources: [_kubb_kit.ast.factory.createSource({
82
+ name: createHandlersName,
83
+ isIndexable: true,
84
+ isExportable: true,
85
+ nodes: [_kubb_kit.ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {
86
+ return [${handlers.join(", ")}] as const
87
+ }`)]
88
+ }), _kubb_kit.ast.factory.createSource({
89
+ name: handlersName,
90
+ isIndexable: true,
91
+ isExportable: true,
92
+ nodes: [_kubb_kit.ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)]
93
+ })]
94
+ })];
95
+ }
96
+ });
97
+ // ../../internals/utils/src/reserved.ts
98
+ /**
99
+ * JavaScript and Java reserved words.
100
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
101
+ */
102
+ const reservedWords = /* @__PURE__ */ new Set([
103
+ "abstract",
104
+ "arguments",
105
+ "await",
106
+ "boolean",
107
+ "break",
108
+ "byte",
109
+ "case",
110
+ "catch",
111
+ "char",
112
+ "class",
113
+ "const",
114
+ "continue",
115
+ "debugger",
116
+ "default",
117
+ "delete",
118
+ "do",
119
+ "double",
120
+ "else",
121
+ "enum",
122
+ "eval",
123
+ "export",
124
+ "extends",
125
+ "false",
126
+ "final",
127
+ "finally",
128
+ "float",
129
+ "for",
130
+ "function",
131
+ "goto",
132
+ "if",
133
+ "implements",
134
+ "import",
135
+ "in",
136
+ "instanceof",
137
+ "int",
138
+ "interface",
139
+ "let",
140
+ "long",
141
+ "native",
142
+ "new",
143
+ "null",
144
+ "package",
145
+ "private",
146
+ "protected",
147
+ "public",
148
+ "return",
149
+ "short",
150
+ "static",
151
+ "super",
152
+ "switch",
153
+ "synchronized",
154
+ "this",
155
+ "throw",
156
+ "throws",
157
+ "transient",
158
+ "true",
159
+ "try",
160
+ "typeof",
161
+ "var",
162
+ "void",
163
+ "volatile",
164
+ "while",
165
+ "with",
166
+ "yield",
167
+ "Array",
168
+ "Date",
169
+ "hasOwnProperty",
170
+ "Infinity",
171
+ "isFinite",
172
+ "isNaN",
173
+ "isPrototypeOf",
174
+ "length",
175
+ "Math",
176
+ "name",
177
+ "NaN",
178
+ "Number",
179
+ "Object",
180
+ "prototype",
181
+ "String",
182
+ "toString",
183
+ "undefined",
184
+ "valueOf"
185
+ ]);
186
+ /**
187
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * isValidVarName('status') // true
192
+ * isValidVarName('class') // false (reserved word)
193
+ * isValidVarName('42foo') // false (starts with digit)
194
+ * ```
195
+ */
196
+ function isValidVarName(name) {
197
+ if (!name || reservedWords.has(name)) return false;
198
+ return isIdentifier(name);
199
+ }
200
+ /**
201
+ * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
202
+ *
203
+ * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
204
+ * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
205
+ * deciding whether an object key needs quoting.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * isIdentifier('name') // true
210
+ * isIdentifier('x-total')// false
211
+ * ```
212
+ */
213
+ function isIdentifier(name) {
214
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
215
+ }
216
+ // ../../internals/utils/src/codegen.ts
217
+ /**
218
+ * An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * objectKey('x-total') // "'x-total'"
223
+ * ```
224
+ */
225
+ function objectKey(name) {
226
+ return isIdentifier(name) ? name : _kubb_kit.text.quote(name);
227
+ }
228
+ /**
229
+ * A JSON value as a JavaScript literal, with single-quoted strings and identifier keys left unquoted.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * toLiteral({ id: 1, 'x-tag': ['a'] }) // "{ id: 1, 'x-tag': ['a'] }"
234
+ * ```
235
+ */
236
+ function toLiteral(value) {
237
+ if (typeof value === "string") return _kubb_kit.text.quote(value);
238
+ if (Array.isArray(value)) return `[${value.map(toLiteral).join(", ")}]`;
239
+ if (value && typeof value === "object") return `{ ${Object.entries(value).map(([key, item]) => `${objectKey(key)}: ${toLiteral(item)}`).join(", ")} }`;
240
+ return String(value);
241
+ }
10
242
  // ../../internals/shared/src/operation.ts
11
243
  /**
12
244
  * Builds the `ResolverFileParams` every operation generator passes to
@@ -72,19 +304,12 @@ function resolveOperationFile(options) {
72
304
  return file;
73
305
  });
74
306
  }
75
- function baseContentType(contentType) {
76
- return contentType.split(";")[0].trim().toLowerCase();
77
- }
78
307
  /**
79
- * Whether a content entry is a sequence of JSON values the adapter marks as a stream, such as `application/jsonl`,
80
- * `application/x-ndjson`, `application/json-seq`, or any media type with an OpenAPI 3.2 `itemSchema`.
308
+ * How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.
81
309
  */
82
- function isJsonLines(entry) {
83
- const baseType = baseContentType(entry.contentType);
84
- return Boolean(entry.stream) && baseType !== "text/event-stream" && !baseType.startsWith("multipart/");
85
- }
86
- function isJsonContentType(baseType) {
87
- return baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json";
310
+ function getStreamFraming(entry) {
311
+ if (_kubb_kit.ast.mediaType.getBase(entry) === "text/event-stream") return "events";
312
+ return entry.stream && _kubb_kit.ast.mediaType.isStream(entry) ? "lines" : void 0;
88
313
  }
89
314
  /**
90
315
  * Parser for one response content entry.
@@ -95,10 +320,10 @@ function isJsonContentType(baseType) {
95
320
  * `image/*`, `audio/*`, and `video/*` are blobs.
96
321
  */
97
322
  function responseTypeForContent(entry) {
98
- const baseType = baseContentType(entry.contentType);
323
+ const baseType = _kubb_kit.ast.mediaType.getBase(entry);
99
324
  if (!baseType) return void 0;
100
- if (baseType === "text/event-stream" || isJsonLines(entry)) return "stream";
101
- if (isJsonContentType(baseType)) return void 0;
325
+ if (getStreamFraming(entry)) return "stream";
326
+ if (_kubb_kit.ast.mediaType.isJson(baseType)) return void 0;
102
327
  if (entry.schema?.type === "blob") return "blob";
103
328
  if (baseType.startsWith("text/")) return "text";
104
329
  if (isBinaryContentType(baseType)) return "blob";
@@ -115,14 +340,14 @@ function isBinaryContentType(baseType) {
115
340
  function toResponseContent(entry, entries = [entry]) {
116
341
  const { contentType, schema } = entry;
117
342
  if (!schema || entry.stream) return entry;
118
- const baseType = baseContentType(contentType);
119
- const resolved = _kubb_kit.ast.resolveRef(schema);
343
+ const baseType = _kubb_kit.ast.mediaType.getBase(contentType);
344
+ const resolved = _kubb_kit.ast.ref.getSchema(schema);
120
345
  if (isBinaryContentType(baseType)) return resolved.type === "blob" ? entry : {
121
346
  ...entry,
122
347
  schema: _kubb_kit.ast.factory.createSchema({ type: "blob" }),
123
348
  keysToOmit: null
124
349
  };
125
- const isPlainMediaType = Boolean(baseType) && !isJsonContentType(baseType) && !baseType.includes("*");
350
+ const isPlainMediaType = Boolean(baseType) && !_kubb_kit.ast.mediaType.isJson(baseType) && !baseType.includes("*");
126
351
  if (resolved.type === "blob") return isPlainMediaType && !entries.every((item) => item.schema?.type === "blob") ? {
127
352
  ...entry,
128
353
  schema: _kubb_kit.ast.factory.createSchema({ type: "string" }),
@@ -138,36 +363,6 @@ function toResponseContent(entry, entries = [entry]) {
138
363
  keysToOmit: null
139
364
  } : entry;
140
365
  }
141
- function getStatusCodeNumber(statusCode) {
142
- const code = Number(statusCode);
143
- return Number.isNaN(code) ? null : code;
144
- }
145
- function isSuccessStatusCode(statusCode) {
146
- const code = getStatusCodeNumber(statusCode);
147
- return code !== null && code >= 200 && code < 300;
148
- }
149
- function getSuccessResponses(responses) {
150
- return responses.filter((response) => isSuccessStatusCode(response.statusCode));
151
- }
152
- function getOperationSuccessResponses(node) {
153
- return getSuccessResponses(node.responses);
154
- }
155
- function getPrimarySuccessResponse(node) {
156
- return getOperationSuccessResponses(node)[0] ?? null;
157
- }
158
- function resolveResponseTypes(node, resolver) {
159
- const types = [];
160
- for (const response of node.responses) {
161
- if (response.statusCode === "default") {
162
- types.push(["default", resolver.response.response(node)]);
163
- continue;
164
- }
165
- const code = getStatusCodeNumber(response.statusCode);
166
- if (code === null) continue;
167
- types.push([code, isSuccessStatusCode(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)]);
168
- }
169
- return types;
170
- }
171
366
  // ../../internals/shared/src/resolver.ts
172
367
  /**
173
368
  * Builds the allocation key for a declaration an operation owns. `scope` names which declaration
@@ -195,124 +390,25 @@ function operationKey(node, ...scope) {
195
390
  function getEnumNames(ctx) {
196
391
  return ctx.memo("enumNames:set", () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => schema.type === "enum" && schema.name ? [schema.name] : [])));
197
392
  }
198
- // ../../internals/shared/src/group.ts
199
- /**
200
- * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
201
- * shared default naming so every plugin groups output consistently:
202
- *
203
- * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
204
- * - other groups use the camelCased group (`pet store` → `petStore`).
205
- *
206
- * A user-provided `group.name` always wins over the default namer, so callers stay in
207
- * control of their output folders. Returns `null` when grouping is disabled, matching the
208
- * per-plugin convention.
209
- *
210
- * @param group - The user-supplied group option, or `undefined` to disable grouping.
211
- *
212
- * @example
213
- * ```ts
214
- * createGroupConfig(group) // shared across every plugin
215
- * ```
216
- */
217
- function createGroupConfig(group) {
218
- if (!group) return null;
219
- const defaultName = (ctx) => {
220
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
221
- return (0, _kubb_kit.camelCase)(ctx.group);
222
- };
223
- return {
224
- ...group,
225
- name: group.name ? group.name : defaultName
226
- };
393
+ // src/utils.ts
394
+ function toSimpleStyle(value) {
395
+ if (Array.isArray(value)) return value.join(",");
396
+ if (typeof value === "object" && value !== null) return Object.entries(value).flat().join(",");
397
+ return String(value);
227
398
  }
228
- // kubb-extension.json
229
- var compatibility = {
230
- "kubb": ">=5.16.0",
231
- "node": ">=22.22"
232
- };
233
- // package.json
234
- var version = "6.2.0-canary.20261111T122007";
235
- // src/generators/handlersGenerator.ts
236
399
  /**
237
- * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
238
- * `handlers.ts` file that re-exports every generated handler in operation
239
- * order, ready to spread into `setupServer(...handlers)` or
240
- * `setupWorker(...handlers)`.
400
+ * The declared headers a mock sends, each with its example's `serializedValue` or `value`, else its schema's `default` or first example.
241
401
  */
242
- const handlersGenerator = (0, _kubb_kit.defineGenerator)({
243
- name: "plugin-msw",
244
- operations(nodes, ctx) {
245
- const { resolver, config, root } = ctx;
246
- const { output, group } = ctx.options;
247
- const handlersName = resolver.handler.listName();
248
- const createHandlersName = resolver.resolveName({
249
- key: ["handlers", "create"],
250
- name: (0, _kubb_kit.camelCase)(handlersName, { prefix: "create" })
251
- });
252
- const file = resolver.file({
253
- name: handlersName,
254
- extname: ".ts",
255
- root,
256
- output,
257
- group: group ?? void 0
258
- });
259
- const handlerNames = nodes.map((node) => resolver.handler.name(node));
260
- const imports = nodes.map((node, index) => {
261
- const operationName = handlerNames[index];
262
- const operationFile = resolver.file({
263
- name: resolver.name(node.operationId),
264
- extname: ".ts",
265
- tag: node.tags[0] ?? "default",
266
- path: node.path,
267
- root,
268
- output,
269
- group: group ?? void 0
270
- });
271
- return _kubb_kit.ast.factory.createImport({
272
- name: [operationName],
273
- root: file.path,
274
- path: operationFile.path
275
- });
276
- });
277
- const handlers = handlerNames.map((name) => `${name}(undefined, options)`);
278
- return [_kubb_kit.ast.factory.createFile({
279
- baseName: file.baseName,
280
- path: file.path,
281
- meta: file.meta,
282
- banner: resolver.default.banner(ctx.meta, {
283
- output,
284
- config,
285
- file: {
286
- path: file.path,
287
- baseName: file.baseName
288
- }
289
- }),
290
- footer: resolver.default.footer(ctx.meta, {
291
- output,
292
- config,
293
- file: {
294
- path: file.path,
295
- baseName: file.baseName
296
- }
297
- }),
298
- imports,
299
- sources: [_kubb_kit.ast.factory.createSource({
300
- name: createHandlersName,
301
- isIndexable: true,
302
- isExportable: true,
303
- nodes: [_kubb_kit.ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {
304
- return [${handlers.join(", ")}] as const
305
- }`)]
306
- }), _kubb_kit.ast.factory.createSource({
307
- name: handlersName,
308
- isIndexable: true,
309
- isExportable: true,
310
- nodes: [_kubb_kit.ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)]
311
- })]
312
- })];
313
- }
314
- });
315
- // src/utils.ts
402
+ function getMockHeaders(response) {
403
+ return (response?.headers ?? []).flatMap((header) => {
404
+ if (header.name.toLowerCase() === "content-type") return [];
405
+ const example = header.examples?.find((entry) => entry.serializedValue !== void 0 || entry.value !== void 0);
406
+ const headerValue = example?.serializedValue ?? example?.value ?? header.schema.default ?? header.schema.examples?.[0];
407
+ if (headerValue === void 0 || headerValue === null) return [];
408
+ const headerText = toSimpleStyle(headerValue);
409
+ return /^[\t\x20-\x7E\x80-\xFF]*$/.test(headerText) ? [[header.name, headerText]] : [];
410
+ });
411
+ }
316
412
  /**
317
413
  * Returns the base URL as source code. A `${...}` value stays a template literal.
318
414
  */
@@ -340,13 +436,12 @@ function hasResponseSchema(response) {
340
436
  * content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.
341
437
  */
342
438
  function getResponseContentType(response) {
343
- const contents = response?.content ?? [];
344
- const value = (contents.find((entry) => isJson(entry.contentType)) ?? contents[0])?.contentType;
345
- return typeof value === "string" && value.length > 0 ? value : null;
439
+ const contentType = getResponseEntry(response)?.contentType;
440
+ return typeof contentType === "string" && contentType.length > 0 ? contentType : null;
346
441
  }
347
- function isJson(contentType) {
348
- const baseType = contentType?.split(";")[0]?.trim().toLowerCase();
349
- return baseType === "application/json" || Boolean(baseType?.endsWith("+json"));
442
+ function getResponseEntry(response) {
443
+ const contents = response?.content ?? [];
444
+ return contents.find((entry) => _kubb_kit.ast.mediaType.isJson(entry)) ?? contents[0];
350
445
  }
351
446
  /**
352
447
  * The first content type of a response that the client reads as text, such as `application/xml`, or `null` when none is.
@@ -354,49 +449,109 @@ function isJson(contentType) {
354
449
  function getTextContentType(response) {
355
450
  return (response?.content ?? []).find((entry) => {
356
451
  const schema = toResponseContent(entry, response?.content).schema;
357
- return !isJson(entry.contentType) && schema && _kubb_kit.ast.resolveRef(schema).primitive === "string";
452
+ return !_kubb_kit.ast.mediaType.isJson(entry) && schema && _kubb_kit.ast.ref.getSchema(schema).primitive === "string";
358
453
  })?.contentType ?? null;
359
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
+ }
360
467
  /**
361
468
  * The statements that return a mocked `Response` for `body`. A string body for a response the client reads as text goes
362
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.
363
470
  */
364
471
  function responseReturn(body, status, response) {
365
472
  const contentType = getContentType(response);
473
+ const headers = getMockHeaders(response);
474
+ const name = isValidVarName(body) ? body : "body";
475
+ const prelude = name === body ? "" : `const body = ${body}\n `;
476
+ if (contentType && _kubb_kit.ast.mediaType.getBase(contentType) === "text/event-stream") {
477
+ const entry = getResponseEntry(response);
478
+ const serialized = entry?.examples?.find((example) => example.serializedValue !== void 0)?.serializedValue;
479
+ const fallback = serialized === void 0 ? "''" : toLiteral(serialized);
480
+ if (entry?.stream?.shape !== "event") return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \`\${${eventDataLines(name, response)}}\\n\\n\``, status, contentType, headers)}`;
481
+ return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined
482
+ const lines = [
483
+ ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \`\${field}: \${event?.[field]}\`),
484
+ // A client dispatches an event only once it has a \`data\` line, so an event without data still sends an empty one.
485
+ event?.data === undefined ? 'data:' : ${eventDataLines("event.data", response)},
486
+ ]
487
+
488
+ return ${responseLiteral(`event === undefined ? ${fallback} : \`\${lines.join('\\n')}\\n\\n\``, status, contentType, headers)}`;
489
+ }
366
490
  const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null;
367
- if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType)}`;
368
- const name = /^\w+$/.test(body) ? body : "body";
369
- return `${name === body ? "" : `const body = ${body}\n `}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType)}
491
+ if (!textContentType) return `return ${responseLiteral(stringify(body, response), status, contentType, headers)}`;
492
+ return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}
370
493
 
371
- return ${responseLiteral(`JSON.stringify(${name})`, status, contentType)}`;
494
+ return ${responseLiteral(stringify(name, response), status, contentType, headers)}`;
495
+ }
496
+ /**
497
+ * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.
498
+ */
499
+ function eventDataLines(body, response) {
500
+ return `(typeof ${body} === 'string' ? ${body} : ${stringify(body, response)}).split('\\n').map((line) => \`data: \${line}\`).join('\\n')`;
372
501
  }
373
502
  /**
374
- * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no
375
- * `http.query`, so a QUERY operation maps to `http.all` and its handler checks the method itself.
503
+ * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.
504
+ */
505
+ function getExampleBody(response) {
506
+ const entry = getResponseEntry(response);
507
+ const example = entry?.examples?.find((item) => item.value !== void 0 || item.serializedValue !== void 0);
508
+ if (!entry || !example) return null;
509
+ if (example.value !== void 0) return toLiteral(example.value);
510
+ if (!_kubb_kit.ast.mediaType.isJson(entry)) return _kubb_kit.ast.mediaType.getBase(entry) !== "text/event-stream" && getTextContentType(response) === entry.contentType ? toLiteral(example.serializedValue) : null;
511
+ try {
512
+ return toLiteral(JSON.parse(example.serializedValue));
513
+ } catch {
514
+ return null;
515
+ }
516
+ }
517
+ const mswMethods = /* @__PURE__ */ new Set([
518
+ "GET",
519
+ "POST",
520
+ "PUT",
521
+ "PATCH",
522
+ "DELETE",
523
+ "HEAD",
524
+ "OPTIONS"
525
+ ]);
526
+ /**
527
+ * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no `http.query`, `http.trace` or
528
+ * handler for a custom `additionalOperations` method, so such an operation maps to `http.all` and its handler checks the
529
+ * method itself.
376
530
  */
377
531
  function getMswMethod(node) {
378
- return node.method === "QUERY" ? "all" : node.method.toLowerCase();
532
+ return mswMethods.has(node.method) ? node.method.toLowerCase() : "all";
379
533
  }
380
534
  /**
381
- * The `headers` entry of a mocked `Response` init, or an empty string when there is no content type.
535
+ * The `headers` entry of a mocked `Response` init, or an empty string when there is no header to send.
382
536
  */
383
- function headersBlock(contentType) {
384
- return contentType ? `\n headers: {\n 'Content-Type': '${contentType}'\n },` : "";
537
+ function headersBlock(contentType, headers) {
538
+ const entries = [...contentType ? [`'Content-Type': ${_kubb_kit.text.quote(contentType)}`] : [], ...headers.map(([name, value]) => `${_kubb_kit.text.quote(name)}: ${_kubb_kit.text.quote(value)}`)];
539
+ return entries.length ? `\n headers: {\n ${entries.join(",\n ")}\n },` : "";
385
540
  }
386
541
  /**
387
542
  * The `new HttpResponse<string>(...)` expression a mocked handler returns.
388
543
  */
389
- function responseLiteral(body, status, contentType) {
544
+ function responseLiteral(body, status, contentType, headers) {
390
545
  return `new HttpResponse<string>(${body}, {
391
- status: ${status},${headersBlock(contentType)}
546
+ status: ${status},${headersBlock(contentType, headers)}
392
547
  })`;
393
548
  }
394
549
  // src/components/Mock.tsx
395
- function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
550
+ function Mock({ baseURL = "", name, fakerName, responseName, typeName, requestTypeName, node }) {
396
551
  const method = getMswMethod(node);
397
- const methodGuard = node.method === "QUERY" ? `if (info.request.method !== 'QUERY') return\n\n ` : "";
398
- const successResponse = getPrimarySuccessResponse(node);
399
- const statusCode = successResponse ? Number(successResponse.statusCode) : 200;
552
+ const methodGuard = method === "all" ? `if (info.request.method !== ${_kubb_kit.text.quote(node.method)}) return\n\n ` : "";
553
+ const successResponse = _kubb_kit.ast.http.responses.findPrimary(node);
554
+ const statusCode = successResponse ? _kubb_kit.ast.http.status.toCode(successResponse) : 200;
400
555
  const url = _kubb_kit.Url.toPath(node.path);
401
556
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
402
557
  const paramType = fakerName ? typeName : dataType;
@@ -404,7 +559,10 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
404
559
  const params = `data?: ${paramType} | ${requestBodyType ? `HttpResponseResolver<Record<string, string>, ${requestBodyType}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}, options?: { baseURL?: string }`;
405
560
  const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`;
406
561
  const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
407
- const responseBody = fakerName ? `data ?? ${fakerName}(data)` : "data";
562
+ const example = fakerName ? null : getExampleBody(successResponse);
563
+ const exampleBody = example ? `data === undefined ? ${example} : data` : "data";
564
+ const responseBody = fakerName ? `data ?? ${fakerName}()` : exampleBody;
565
+ const responseStatements = responseName && !example ? `return ${responseName}(${responseBody})` : responseReturn(responseBody, statusCode, successResponse);
408
566
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
409
567
  name,
410
568
  isIndexable: true,
@@ -416,16 +574,15 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
416
574
  children: `return ${httpCall}(${urlLiteral}, function handler(info) {
417
575
  ${methodGuard}if(typeof data === 'function') return data(info)
418
576
 
419
- ${responseReturn(responseBody, statusCode, successResponse)}
577
+ ${responseStatements}
420
578
  })`
421
579
  })
422
580
  });
423
581
  }
424
582
  // src/components/Response.tsx
425
583
  function Response({ name, typeName, response }) {
426
- const statusCode = Number(response.statusCode);
427
- const params = `data${hasResponseSchema(response) ? "" : "?"}: ${typeName}`;
428
- const responseName = `${name}Response${statusCode}`;
584
+ const params = `data?: ${typeName}`;
585
+ const responseName = `${name}Response${response.statusCode}`;
429
586
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
430
587
  name: responseName,
431
588
  isIndexable: true,
@@ -435,7 +592,7 @@ function Response({ name, typeName, response }) {
435
592
  export: true,
436
593
  params,
437
594
  children: `
438
- ${responseReturn("data", statusCode, response)}`
595
+ ${responseReturn("data", _kubb_kit.ast.http.status.toCode(response), response)}`
439
596
  })
440
597
  });
441
598
  }
@@ -450,7 +607,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
450
607
  name: "msw",
451
608
  renderer: _kubb_renderer_jsx.jsxRenderer,
452
609
  operation(node, ctx) {
453
- if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
610
+ if (!_kubb_kit.ast.http.isOperation(node)) return null;
454
611
  const { driver, resolver, config, root } = ctx;
455
612
  const { output, parser, baseURL, group } = ctx.options;
456
613
  const fileName = resolver.name(node.operationId);
@@ -492,8 +649,8 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
492
649
  }),
493
650
  responseName: tsResolver.response.response(node)
494
651
  };
495
- const successResponses = getOperationSuccessResponses(node);
496
- const referencedNames = node.responses.flatMap((response) => (response.content ?? []).flatMap((entry) => entry.schema ? _kubb_kit.ast.collectImportedRefNames(entry.schema) : []));
652
+ const successResponses = _kubb_kit.ast.http.responses.filterSuccess(node);
653
+ const referencedNames = _kubb_kit.ast.http.responses.getImportedNames(node.responses);
497
654
  const enumOptions = pluginTs.options?.enum;
498
655
  const enumNames = getEnumNames(ctx);
499
656
  const hasResponseNameCollision = referencedNames.some((name) => {
@@ -504,14 +661,15 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
504
661
  isEnum: enumNames.has(name)
505
662
  }) === type.responseName;
506
663
  });
507
- const types = resolveResponseTypes(node, tsResolver).map(([code, typeName]) => {
508
- const successResponse = successResponses.find((response) => response.statusCode === String(code));
509
- return [code, hasResponseNameCollision && successResponse ? tsResolver.response.status(node, successResponse.statusCode) : typeName];
664
+ const types = node.responses.filter((response) => response.statusCode !== "default" && _kubb_kit.ast.http.status.getClass(response) !== 1).map((response) => {
665
+ return [response, _kubb_kit.ast.http.status.isSuccess(response) && !hasResponseNameCollision ? type.responseName : tsResolver.response.status(node, response.statusCode)];
510
666
  });
511
- const responseTypeNames = types.map(([, typeName]) => typeName).filter((name) => !hasResponseNameCollision || name !== type.responseName);
667
+ const responseTypeNames = types.map(([, typeName]) => typeName);
512
668
  const mockResponseName = hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName;
513
669
  const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response));
514
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.response.body(node) : null;
670
+ const primaryResponse = _kubb_kit.ast.http.responses.findPrimary(node);
671
+ const responseHelperName = primaryResponse && hasResponseSchema(primaryResponse) && types.some(([response, typeName]) => response.statusCode === primaryResponse.statusCode && typeName === mockResponseName) ? `${mock.name}Response${primaryResponse.statusCode}` : void 0;
672
+ const requestName = _kubb_kit.ast.http.requestBody.getSchema(node) ? tsResolver.response.body(node) : null;
515
673
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
516
674
  baseName: mock.file.baseName,
517
675
  path: mock.file.path,
@@ -562,20 +720,17 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
562
720
  root: mock.file.path,
563
721
  path: faker.file.path
564
722
  }),
565
- types.filter(([code]) => code !== "default").map(([code, typeName]) => {
566
- const response = node.responses.find((item) => item.statusCode === String(code));
567
- if (!response) return null;
568
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Response, {
569
- typeName,
570
- response,
571
- name: mock.name
572
- }, typeName);
573
- }),
723
+ types.map(([response, typeName]) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Response, {
724
+ typeName,
725
+ response,
726
+ name: mock.name
727
+ }, response.statusCode)),
574
728
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Mock, {
575
729
  name: mock.name,
576
730
  typeName: mockResponseName,
577
731
  requestTypeName: requestName,
578
732
  fakerName: faker && hasSuccessSchema ? faker.name : void 0,
733
+ responseName: responseHelperName,
579
734
  node,
580
735
  baseURL
581
736
  })
@@ -602,7 +757,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
602
757
  const resolverMsw = (0, _kubb_kit.createResolver)({
603
758
  pluginName: "plugin-msw",
604
759
  name(name) {
605
- return this.memo(`name:${name}`, () => (0, _kubb_kit.camelCase)(name, { suffix: "handler" }));
760
+ return this.memo(`name:${name}`, () => _kubb_kit.casing.camel(name, { suffix: "handler" }));
606
761
  },
607
762
  handler: {
608
763
  name(node) {
@@ -677,7 +832,7 @@ const pluginMsw = (0, _kubb_kit.defineExtension)({
677
832
  output,
678
833
  parser,
679
834
  baseURL,
680
- group: createGroupConfig(group),
835
+ group: group ?? null,
681
836
  exclude,
682
837
  include,
683
838
  override,