@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 +353 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +354 -211
- package/dist/index.js.map +1 -1
- package/docs/options.md +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +7 -7
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
|
-
*
|
|
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
|
|
83
|
-
|
|
84
|
-
return
|
|
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 =
|
|
323
|
+
const baseType = _kubb_kit.ast.mediaType.getBase(entry);
|
|
99
324
|
if (!baseType) return void 0;
|
|
100
|
-
if (
|
|
101
|
-
if (
|
|
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 =
|
|
119
|
-
const resolved = _kubb_kit.ast.
|
|
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) && !
|
|
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
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
344
|
-
|
|
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
|
|
348
|
-
const
|
|
349
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
375
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
398
|
-
const successResponse =
|
|
399
|
-
const statusCode = successResponse ?
|
|
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
|
|
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
|
-
${
|
|
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
|
|
427
|
-
const
|
|
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",
|
|
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.
|
|
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 =
|
|
496
|
-
const referencedNames =
|
|
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 =
|
|
508
|
-
|
|
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)
|
|
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
|
|
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.
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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}`, () =>
|
|
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:
|
|
823
|
+
group: group ?? null,
|
|
681
824
|
exclude,
|
|
682
825
|
include,
|
|
683
826
|
override,
|