@kubb/plugin-msw 6.2.0-canary.20261111T115959 → 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,42 +363,15 @@ 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
174
369
  * it is, so two of them never share an allocation even when a custom resolver gives both the same
175
370
  * name. Leave `scope` empty for the operation's own declaration.
176
371
  *
372
+ * The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro
373
+ * rewrites the method or path still resolves another plugin's names for the same operation.
374
+ *
177
375
  * @example
178
376
  * ```ts
179
377
  * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
@@ -182,8 +380,7 @@ function resolveResponseTypes(node, resolver) {
182
380
  function operationKey(node, ...scope) {
183
381
  return [
184
382
  "operation",
185
- node.method,
186
- node.path,
383
+ ...node.pointer ? [node.pointer] : [node.method, node.path],
187
384
  ...scope.map(String)
188
385
  ];
189
386
  }
@@ -193,124 +390,25 @@ function operationKey(node, ...scope) {
193
390
  function getEnumNames(ctx) {
194
391
  return ctx.memo("enumNames:set", () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => schema.type === "enum" && schema.name ? [schema.name] : [])));
195
392
  }
196
- // ../../internals/shared/src/group.ts
197
- /**
198
- * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
199
- * shared default naming so every plugin groups output consistently:
200
- *
201
- * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
202
- * - other groups use the camelCased group (`pet store` → `petStore`).
203
- *
204
- * A user-provided `group.name` always wins over the default namer, so callers stay in
205
- * control of their output folders. Returns `null` when grouping is disabled, matching the
206
- * per-plugin convention.
207
- *
208
- * @param group - The user-supplied group option, or `undefined` to disable grouping.
209
- *
210
- * @example
211
- * ```ts
212
- * createGroupConfig(group) // shared across every plugin
213
- * ```
214
- */
215
- function createGroupConfig(group) {
216
- if (!group) return null;
217
- const defaultName = (ctx) => {
218
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
219
- return (0, _kubb_kit.camelCase)(ctx.group);
220
- };
221
- return {
222
- ...group,
223
- name: group.name ? group.name : defaultName
224
- };
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);
225
398
  }
226
- // kubb-extension.json
227
- var compatibility = {
228
- "kubb": ">=5.16.0",
229
- "node": ">=22.22"
230
- };
231
- // package.json
232
- var version = "6.2.0-canary.20261111T115959";
233
- // src/generators/handlersGenerator.ts
234
399
  /**
235
- * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
236
- * `handlers.ts` file that re-exports every generated handler in operation
237
- * order, ready to spread into `setupServer(...handlers)` or
238
- * `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.
239
401
  */
240
- const handlersGenerator = (0, _kubb_kit.defineGenerator)({
241
- name: "plugin-msw",
242
- operations(nodes, ctx) {
243
- const { resolver, config, root } = ctx;
244
- const { output, group } = ctx.options;
245
- const handlersName = resolver.handler.listName();
246
- const createHandlersName = resolver.resolveName({
247
- key: ["handlers", "create"],
248
- name: (0, _kubb_kit.camelCase)(handlersName, { prefix: "create" })
249
- });
250
- const file = resolver.file({
251
- name: handlersName,
252
- extname: ".ts",
253
- root,
254
- output,
255
- group: group ?? void 0
256
- });
257
- const handlerNames = nodes.map((node) => resolver.handler.name(node));
258
- const imports = nodes.map((node, index) => {
259
- const operationName = handlerNames[index];
260
- const operationFile = resolver.file({
261
- name: resolver.name(node.operationId),
262
- extname: ".ts",
263
- tag: node.tags[0] ?? "default",
264
- path: node.path,
265
- root,
266
- output,
267
- group: group ?? void 0
268
- });
269
- return _kubb_kit.ast.factory.createImport({
270
- name: [operationName],
271
- root: file.path,
272
- path: operationFile.path
273
- });
274
- });
275
- const handlers = handlerNames.map((name) => `${name}(undefined, options)`);
276
- return [_kubb_kit.ast.factory.createFile({
277
- baseName: file.baseName,
278
- path: file.path,
279
- meta: file.meta,
280
- banner: resolver.default.banner(ctx.meta, {
281
- output,
282
- config,
283
- file: {
284
- path: file.path,
285
- baseName: file.baseName
286
- }
287
- }),
288
- footer: resolver.default.footer(ctx.meta, {
289
- output,
290
- config,
291
- file: {
292
- path: file.path,
293
- baseName: file.baseName
294
- }
295
- }),
296
- imports,
297
- sources: [_kubb_kit.ast.factory.createSource({
298
- name: createHandlersName,
299
- isIndexable: true,
300
- isExportable: true,
301
- nodes: [_kubb_kit.ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {
302
- return [${handlers.join(", ")}] as const
303
- }`)]
304
- }), _kubb_kit.ast.factory.createSource({
305
- name: handlersName,
306
- isIndexable: true,
307
- isExportable: true,
308
- nodes: [_kubb_kit.ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)]
309
- })]
310
- })];
311
- }
312
- });
313
- // 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
+ }
314
412
  /**
315
413
  * Returns the base URL as source code. A `${...}` value stays a template literal.
316
414
  */
@@ -338,13 +436,12 @@ function hasResponseSchema(response) {
338
436
  * content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.
339
437
  */
340
438
  function getResponseContentType(response) {
341
- const contents = response?.content ?? [];
342
- const value = (contents.find((entry) => isJson(entry.contentType)) ?? contents[0])?.contentType;
343
- 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;
344
441
  }
345
- function isJson(contentType) {
346
- const baseType = contentType?.split(";")[0]?.trim().toLowerCase();
347
- 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];
348
445
  }
349
446
  /**
350
447
  * The first content type of a response that the client reads as text, such as `application/xml`, or `null` when none is.
@@ -352,7 +449,7 @@ function isJson(contentType) {
352
449
  function getTextContentType(response) {
353
450
  return (response?.content ?? []).find((entry) => {
354
451
  const schema = toResponseContent(entry, response?.content).schema;
355
- 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";
356
453
  })?.contentType ?? null;
357
454
  }
358
455
  /**
@@ -361,40 +458,88 @@ function getTextContentType(response) {
361
458
  */
362
459
  function responseReturn(body, status, response) {
363
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
+ }
364
478
  const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null;
365
- if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType)}`;
366
- const name = /^\w+$/.test(body) ? body : "body";
367
- 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)}
368
481
 
369
- 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')`;
370
489
  }
371
490
  /**
372
- * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no
373
- * `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.
374
518
  */
375
519
  function getMswMethod(node) {
376
- return node.method === "QUERY" ? "all" : node.method.toLowerCase();
520
+ return mswMethods.has(node.method) ? node.method.toLowerCase() : "all";
377
521
  }
378
522
  /**
379
- * 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.
380
524
  */
381
- function headersBlock(contentType) {
382
- 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 },` : "";
383
528
  }
384
529
  /**
385
530
  * The `new HttpResponse<string>(...)` expression a mocked handler returns.
386
531
  */
387
- function responseLiteral(body, status, contentType) {
532
+ function responseLiteral(body, status, contentType, headers) {
388
533
  return `new HttpResponse<string>(${body}, {
389
- status: ${status},${headersBlock(contentType)}
534
+ status: ${status},${headersBlock(contentType, headers)}
390
535
  })`;
391
536
  }
392
537
  // src/components/Mock.tsx
393
- function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
538
+ function Mock({ baseURL = "", name, fakerName, responseName, typeName, requestTypeName, node }) {
394
539
  const method = getMswMethod(node);
395
- const methodGuard = node.method === "QUERY" ? `if (info.request.method !== 'QUERY') return\n\n ` : "";
396
- const successResponse = getPrimarySuccessResponse(node);
397
- 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;
398
543
  const url = _kubb_kit.Url.toPath(node.path);
399
544
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
400
545
  const paramType = fakerName ? typeName : dataType;
@@ -402,7 +547,10 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
402
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 }`;
403
548
  const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`;
404
549
  const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
405
- 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);
406
554
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
407
555
  name,
408
556
  isIndexable: true,
@@ -414,16 +562,15 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
414
562
  children: `return ${httpCall}(${urlLiteral}, function handler(info) {
415
563
  ${methodGuard}if(typeof data === 'function') return data(info)
416
564
 
417
- ${responseReturn(responseBody, statusCode, successResponse)}
565
+ ${responseStatements}
418
566
  })`
419
567
  })
420
568
  });
421
569
  }
422
570
  // src/components/Response.tsx
423
571
  function Response({ name, typeName, response }) {
424
- const statusCode = Number(response.statusCode);
425
- const params = `data${hasResponseSchema(response) ? "" : "?"}: ${typeName}`;
426
- const responseName = `${name}Response${statusCode}`;
572
+ const params = `data?: ${typeName}`;
573
+ const responseName = `${name}Response${response.statusCode}`;
427
574
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
428
575
  name: responseName,
429
576
  isIndexable: true,
@@ -433,7 +580,7 @@ function Response({ name, typeName, response }) {
433
580
  export: true,
434
581
  params,
435
582
  children: `
436
- ${responseReturn("data", statusCode, response)}`
583
+ ${responseReturn("data", _kubb_kit.ast.http.status.toCode(response), response)}`
437
584
  })
438
585
  });
439
586
  }
@@ -448,7 +595,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
448
595
  name: "msw",
449
596
  renderer: _kubb_renderer_jsx.jsxRenderer,
450
597
  operation(node, ctx) {
451
- if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
598
+ if (!_kubb_kit.ast.http.isOperation(node)) return null;
452
599
  const { driver, resolver, config, root } = ctx;
453
600
  const { output, parser, baseURL, group } = ctx.options;
454
601
  const fileName = resolver.name(node.operationId);
@@ -490,8 +637,8 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
490
637
  }),
491
638
  responseName: tsResolver.response.response(node)
492
639
  };
493
- const successResponses = getOperationSuccessResponses(node);
494
- 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);
495
642
  const enumOptions = pluginTs.options?.enum;
496
643
  const enumNames = getEnumNames(ctx);
497
644
  const hasResponseNameCollision = referencedNames.some((name) => {
@@ -502,14 +649,15 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
502
649
  isEnum: enumNames.has(name)
503
650
  }) === type.responseName;
504
651
  });
505
- const types = resolveResponseTypes(node, tsResolver).map(([code, typeName]) => {
506
- const successResponse = successResponses.find((response) => response.statusCode === String(code));
507
- 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)];
508
654
  });
509
- const responseTypeNames = types.map(([, typeName]) => typeName).filter((name) => !hasResponseNameCollision || name !== type.responseName);
655
+ const responseTypeNames = types.map(([, typeName]) => typeName);
510
656
  const mockResponseName = hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName;
511
657
  const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response));
512
- 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;
513
661
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
514
662
  baseName: mock.file.baseName,
515
663
  path: mock.file.path,
@@ -560,20 +708,17 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
560
708
  root: mock.file.path,
561
709
  path: faker.file.path
562
710
  }),
563
- types.filter(([code]) => code !== "default").map(([code, typeName]) => {
564
- const response = node.responses.find((item) => item.statusCode === String(code));
565
- if (!response) return null;
566
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Response, {
567
- typeName,
568
- response,
569
- name: mock.name
570
- }, typeName);
571
- }),
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)),
572
716
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Mock, {
573
717
  name: mock.name,
574
718
  typeName: mockResponseName,
575
719
  requestTypeName: requestName,
576
720
  fakerName: faker && hasSuccessSchema ? faker.name : void 0,
721
+ responseName: responseHelperName,
577
722
  node,
578
723
  baseURL
579
724
  })
@@ -600,7 +745,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
600
745
  const resolverMsw = (0, _kubb_kit.createResolver)({
601
746
  pluginName: "plugin-msw",
602
747
  name(name) {
603
- 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" }));
604
749
  },
605
750
  handler: {
606
751
  name(node) {
@@ -675,7 +820,7 @@ const pluginMsw = (0, _kubb_kit.defineExtension)({
675
820
  output,
676
821
  parser,
677
822
  baseURL,
678
- group: createGroupConfig(group),
823
+ group: group ?? null,
679
824
  exclude,
680
825
  include,
681
826
  override,