@kubb/plugin-msw 6.2.0-canary.20261111T122007 → 6.2.0

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.0";
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,7 +449,7 @@ 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
  }
360
455
  /**
@@ -363,40 +458,88 @@ function getTextContentType(response) {
363
458
  */
364
459
  function responseReturn(body, status, response) {
365
460
  const contentType = getContentType(response);
461
+ const headers = getMockHeaders(response);
462
+ const name = isValidVarName(body) ? body : "body";
463
+ const prelude = name === body ? "" : `const body = ${body}\n `;
464
+ if (contentType && _kubb_kit.ast.mediaType.getBase(contentType) === "text/event-stream") {
465
+ const entry = getResponseEntry(response);
466
+ const serialized = entry?.examples?.find((example) => example.serializedValue !== void 0)?.serializedValue;
467
+ 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)}`;
469
+ return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined
470
+ const lines = [
471
+ ...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \`\${field}: \${event?.[field]}\`),
472
+ // 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")},
474
+ ]
475
+
476
+ return ${responseLiteral(`event === undefined ? ${fallback} : \`\${lines.join('\\n')}\\n\\n\``, status, contentType, headers)}`;
477
+ }
366
478
  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)}
479
+ if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType, headers)}`;
480
+ return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}
370
481
 
371
- return ${responseLiteral(`JSON.stringify(${name})`, status, contentType)}`;
482
+ return ${responseLiteral(`JSON.stringify(${name})`, status, contentType, headers)}`;
483
+ }
484
+ /**
485
+ * The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.
486
+ */
487
+ function eventDataLines(body) {
488
+ return `(typeof ${body} === 'string' ? ${body} : JSON.stringify(${body})).split('\\n').map((line) => \`data: \${line}\`).join('\\n')`;
372
489
  }
373
490
  /**
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.
491
+ * The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.
492
+ */
493
+ function getExampleBody(response) {
494
+ const entry = getResponseEntry(response);
495
+ const example = entry?.examples?.find((item) => item.value !== void 0 || item.serializedValue !== void 0);
496
+ if (!entry || !example) return null;
497
+ if (example.value !== void 0) return toLiteral(example.value);
498
+ 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;
499
+ try {
500
+ return toLiteral(JSON.parse(example.serializedValue));
501
+ } catch {
502
+ return null;
503
+ }
504
+ }
505
+ const mswMethods = /* @__PURE__ */ new Set([
506
+ "GET",
507
+ "POST",
508
+ "PUT",
509
+ "PATCH",
510
+ "DELETE",
511
+ "HEAD",
512
+ "OPTIONS"
513
+ ]);
514
+ /**
515
+ * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no `http.query`, `http.trace` or
516
+ * handler for a custom `additionalOperations` method, so such an operation maps to `http.all` and its handler checks the
517
+ * method itself.
376
518
  */
377
519
  function getMswMethod(node) {
378
- return node.method === "QUERY" ? "all" : node.method.toLowerCase();
520
+ return mswMethods.has(node.method) ? node.method.toLowerCase() : "all";
379
521
  }
380
522
  /**
381
- * The `headers` entry of a mocked `Response` init, or an empty string when there is no content type.
523
+ * The `headers` entry of a mocked `Response` init, or an empty string when there is no header to send.
382
524
  */
383
- function headersBlock(contentType) {
384
- return contentType ? `\n headers: {\n 'Content-Type': '${contentType}'\n },` : "";
525
+ function headersBlock(contentType, headers) {
526
+ const entries = [...contentType ? [`'Content-Type': ${_kubb_kit.text.quote(contentType)}`] : [], ...headers.map(([name, value]) => `${_kubb_kit.text.quote(name)}: ${_kubb_kit.text.quote(value)}`)];
527
+ return entries.length ? `\n headers: {\n ${entries.join(",\n ")}\n },` : "";
385
528
  }
386
529
  /**
387
530
  * The `new HttpResponse<string>(...)` expression a mocked handler returns.
388
531
  */
389
- function responseLiteral(body, status, contentType) {
532
+ function responseLiteral(body, status, contentType, headers) {
390
533
  return `new HttpResponse<string>(${body}, {
391
- status: ${status},${headersBlock(contentType)}
534
+ status: ${status},${headersBlock(contentType, headers)}
392
535
  })`;
393
536
  }
394
537
  // src/components/Mock.tsx
395
- function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
538
+ function Mock({ baseURL = "", name, fakerName, responseName, typeName, requestTypeName, node }) {
396
539
  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;
540
+ const methodGuard = method === "all" ? `if (info.request.method !== ${_kubb_kit.text.quote(node.method)}) return\n\n ` : "";
541
+ const successResponse = _kubb_kit.ast.http.responses.findPrimary(node);
542
+ const statusCode = successResponse ? _kubb_kit.ast.http.status.toCode(successResponse) : 200;
400
543
  const url = _kubb_kit.Url.toPath(node.path);
401
544
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
402
545
  const paramType = fakerName ? typeName : dataType;
@@ -404,7 +547,10 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
404
547
  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
548
  const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`;
406
549
  const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
407
- const responseBody = fakerName ? `data ?? ${fakerName}(data)` : "data";
550
+ const example = fakerName ? null : getExampleBody(successResponse);
551
+ const exampleBody = example ? `data === undefined ? ${example} : data` : "data";
552
+ const responseBody = fakerName ? `data ?? ${fakerName}()` : exampleBody;
553
+ const responseStatements = responseName && !example ? `return ${responseName}(${responseBody})` : responseReturn(responseBody, statusCode, successResponse);
408
554
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
409
555
  name,
410
556
  isIndexable: true,
@@ -416,16 +562,15 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
416
562
  children: `return ${httpCall}(${urlLiteral}, function handler(info) {
417
563
  ${methodGuard}if(typeof data === 'function') return data(info)
418
564
 
419
- ${responseReturn(responseBody, statusCode, successResponse)}
565
+ ${responseStatements}
420
566
  })`
421
567
  })
422
568
  });
423
569
  }
424
570
  // src/components/Response.tsx
425
571
  function Response({ name, typeName, response }) {
426
- const statusCode = Number(response.statusCode);
427
- const params = `data${hasResponseSchema(response) ? "" : "?"}: ${typeName}`;
428
- const responseName = `${name}Response${statusCode}`;
572
+ const params = `data?: ${typeName}`;
573
+ const responseName = `${name}Response${response.statusCode}`;
429
574
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
430
575
  name: responseName,
431
576
  isIndexable: true,
@@ -435,7 +580,7 @@ function Response({ name, typeName, response }) {
435
580
  export: true,
436
581
  params,
437
582
  children: `
438
- ${responseReturn("data", statusCode, response)}`
583
+ ${responseReturn("data", _kubb_kit.ast.http.status.toCode(response), response)}`
439
584
  })
440
585
  });
441
586
  }
@@ -450,7 +595,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
450
595
  name: "msw",
451
596
  renderer: _kubb_renderer_jsx.jsxRenderer,
452
597
  operation(node, ctx) {
453
- if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
598
+ if (!_kubb_kit.ast.http.isOperation(node)) return null;
454
599
  const { driver, resolver, config, root } = ctx;
455
600
  const { output, parser, baseURL, group } = ctx.options;
456
601
  const fileName = resolver.name(node.operationId);
@@ -492,8 +637,8 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
492
637
  }),
493
638
  responseName: tsResolver.response.response(node)
494
639
  };
495
- const successResponses = getOperationSuccessResponses(node);
496
- const referencedNames = node.responses.flatMap((response) => (response.content ?? []).flatMap((entry) => entry.schema ? _kubb_kit.ast.collectImportedRefNames(entry.schema) : []));
640
+ const successResponses = _kubb_kit.ast.http.responses.filterSuccess(node);
641
+ const referencedNames = _kubb_kit.ast.http.responses.getImportedNames(node.responses);
497
642
  const enumOptions = pluginTs.options?.enum;
498
643
  const enumNames = getEnumNames(ctx);
499
644
  const hasResponseNameCollision = referencedNames.some((name) => {
@@ -504,14 +649,15 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
504
649
  isEnum: enumNames.has(name)
505
650
  }) === type.responseName;
506
651
  });
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];
652
+ const types = node.responses.filter((response) => response.statusCode !== "default" && _kubb_kit.ast.http.status.getClass(response) !== 1).map((response) => {
653
+ return [response, _kubb_kit.ast.http.status.isSuccess(response) && !hasResponseNameCollision ? type.responseName : tsResolver.response.status(node, response.statusCode)];
510
654
  });
511
- const responseTypeNames = types.map(([, typeName]) => typeName).filter((name) => !hasResponseNameCollision || name !== type.responseName);
655
+ const responseTypeNames = types.map(([, typeName]) => typeName);
512
656
  const mockResponseName = hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName;
513
657
  const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response));
514
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.response.body(node) : null;
658
+ const primaryResponse = _kubb_kit.ast.http.responses.findPrimary(node);
659
+ const responseHelperName = primaryResponse && hasResponseSchema(primaryResponse) && types.some(([response, typeName]) => response.statusCode === primaryResponse.statusCode && typeName === mockResponseName) ? `${mock.name}Response${primaryResponse.statusCode}` : void 0;
660
+ const requestName = _kubb_kit.ast.http.requestBody.getSchema(node) ? tsResolver.response.body(node) : null;
515
661
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
516
662
  baseName: mock.file.baseName,
517
663
  path: mock.file.path,
@@ -562,20 +708,17 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
562
708
  root: mock.file.path,
563
709
  path: faker.file.path
564
710
  }),
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
- }),
711
+ types.map(([response, typeName]) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Response, {
712
+ typeName,
713
+ response,
714
+ name: mock.name
715
+ }, response.statusCode)),
574
716
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Mock, {
575
717
  name: mock.name,
576
718
  typeName: mockResponseName,
577
719
  requestTypeName: requestName,
578
720
  fakerName: faker && hasSuccessSchema ? faker.name : void 0,
721
+ responseName: responseHelperName,
579
722
  node,
580
723
  baseURL
581
724
  })
@@ -602,7 +745,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
602
745
  const resolverMsw = (0, _kubb_kit.createResolver)({
603
746
  pluginName: "plugin-msw",
604
747
  name(name) {
605
- return this.memo(`name:${name}`, () => (0, _kubb_kit.camelCase)(name, { suffix: "handler" }));
748
+ return this.memo(`name:${name}`, () => _kubb_kit.casing.camel(name, { suffix: "handler" }));
606
749
  },
607
750
  handler: {
608
751
  name(node) {
@@ -677,7 +820,7 @@ const pluginMsw = (0, _kubb_kit.defineExtension)({
677
820
  output,
678
821
  parser,
679
822
  baseURL,
680
- group: createGroupConfig(group),
823
+ group: group ?? null,
681
824
  exclude,
682
825
  include,
683
826
  override,