rspack-plugin-mock 2.1.0 → 2.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.
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { attempt, attemptAsync, deepEqual, hasOwn, isArray, isBoolean, isEmptyObject, isFunction, isPlainObject, isString, kebabCase, objectKeys, partition, random, sleep, sortBy, timestamp, toArray, uniq } from "@pengzhanbo/utils";
2
+ import { attempt, attemptAsync, deepEqual, hasOwn, isArray, isBoolean, isEmptyObject, isFunction, isPlainObject, isString, kebabCase, objectKeys, omit, partition, random, sleep, sortBy, timestamp, toArray, uniq } from "@pengzhanbo/utils";
3
3
  import fs, { promises } from "node:fs";
4
4
  import ansis from "ansis";
5
5
  import picomatch from "picomatch";
@@ -7,20 +7,18 @@ import { loadPackageJSONSync } from "local-pkg";
7
7
  import { match, parse, pathToRegexp } from "path-to-regexp";
8
8
  import os from "node:os";
9
9
  import { fileURLToPath } from "node:url";
10
- import Debug from "debug";
11
10
  import { Volume, createFsFromVolume } from "memfs";
12
11
  import { parse as parse$1 } from "node:querystring";
13
- import crypto from "node:crypto";
14
12
  import cors from "cors";
15
13
  import bodyParser from "co-body";
16
14
  import formidable from "formidable";
17
- import http from "node:http";
15
+ import Cookies from "cookies";
18
16
  import { Buffer } from "node:buffer";
19
17
  import zlib from "node:zlib";
20
18
  import HTTP_STATUS from "http-status";
21
19
  import * as mime from "mime-types";
22
20
  import { WebSocketServer } from "ws";
23
- //#region src/utils/createMatcher.ts
21
+
24
22
  function createMatcher(include, exclude, defaultIgnore = true) {
25
23
  const pattern = [];
26
24
  const ignore = [...defaultIgnore ? ["**/node_modules/**"] : [], ...toArray(exclude)];
@@ -34,12 +32,12 @@ function createMatcher(include, exclude, defaultIgnore = true) {
34
32
  isMatch: picomatch(pattern, { ignore })
35
33
  };
36
34
  }
37
- //#endregion
38
- //#region src/utils/doesProxyContextMatchUrl.ts
39
- const PATTERN_CACHE = /* @__PURE__ */ new Map();
35
+
36
+
37
+ const PATTERN_CACHE = new Map();
40
38
  function doesProxyContextMatchUrl(context, req) {
41
39
  const url = req.url;
42
- if (typeof context === "function") return context(url, req);
40
+ if (typeof context === "function") return !!context(url, req);
43
41
  if (context[0] === "^") {
44
42
  let pattern = PATTERN_CACHE.get(context);
45
43
  if (!pattern) PATTERN_CACHE.set(context, pattern = new RegExp(context));
@@ -47,8 +45,8 @@ function doesProxyContextMatchUrl(context, req) {
47
45
  }
48
46
  return url.startsWith(context);
49
47
  }
50
- //#endregion
51
- //#region src/utils/getDeps.ts
48
+
49
+
52
50
  function getPackageDeps(cwd) {
53
51
  const { dependencies, devDependencies, peerDependencies, optionalDependencies } = loadPackageJSONSync(cwd) || {};
54
52
  return {
@@ -59,22 +57,18 @@ function getPackageDeps(cwd) {
59
57
  };
60
58
  }
61
59
  function getPackageDepList(cwd) {
62
- return uniq(objectKeys(getPackageDeps(cwd)));
60
+ const deps = getPackageDeps(cwd);
61
+ return uniq(objectKeys(deps));
63
62
  }
64
- //#endregion
65
- //#region src/utils/is.ts
63
+
64
+
66
65
  function isStream(stream) {
67
66
  return stream !== null && typeof stream === "object" && typeof stream.pipe === "function";
68
67
  }
69
68
  function isReadableStream(stream) {
70
69
  return isStream(stream) && stream.readable !== false && typeof stream._read === "function" && typeof stream._readableState === "object";
71
70
  }
72
- /**
73
- * 判断内容类型是否为文本类型
74
- *
75
- * @param contentType 内容类型
76
- * @returns 是否为文本类型
77
- */
71
+
78
72
  function isTextContent(contentType) {
79
73
  return [
80
74
  "text",
@@ -82,15 +76,9 @@ function isTextContent(contentType) {
82
76
  "xml"
83
77
  ].some((type) => contentType.includes(type));
84
78
  }
85
- //#endregion
86
- //#region src/utils/isObjectSubset.ts
87
- /**
88
- * Checks if target object is a subset of source object.
89
- * That is, all properties and their corresponding values in target exist in source.
90
- *
91
- * 深度比较两个对象之间,target 是否属于 source 的子集,
92
- * 即 target 的所有属性和对应的值,都在 source 中,
93
- */
79
+
80
+
81
+
94
82
  function isObjectSubset(source, target) {
95
83
  if (!target) return true;
96
84
  for (const key in target) if (!isIncluded(source[key], target[key])) return false;
@@ -98,7 +86,7 @@ function isObjectSubset(source, target) {
98
86
  }
99
87
  function isIncluded(source, target) {
100
88
  if (isArray(source) && isArray(target)) {
101
- const seen = /* @__PURE__ */ new Set();
89
+ const seen = new Set();
102
90
  return target.every((ti) => source.some((si, i) => {
103
91
  if (seen.has(i)) return false;
104
92
  const included = isIncluded(si, ti);
@@ -109,12 +97,10 @@ function isIncluded(source, target) {
109
97
  if (isPlainObject(source) && isPlainObject(target)) return isObjectSubset(source, target);
110
98
  return Object.is(source, target);
111
99
  }
112
- //#endregion
113
- //#region src/utils/isPathMatch.ts
114
- const cache = /* @__PURE__ */ new Map();
115
- /**
116
- * 判断 path 是否匹配 pattern
117
- */
100
+
101
+
102
+ const cache = new Map();
103
+
118
104
  function isPathMatch(pattern, path) {
119
105
  let regexp = cache.get(pattern);
120
106
  if (!regexp) {
@@ -123,8 +109,8 @@ function isPathMatch(pattern, path) {
123
109
  }
124
110
  return regexp.test(path);
125
111
  }
126
- //#endregion
127
- //#region src/utils/matchScene.ts
112
+
113
+
128
114
  function matchScene(activeScene, mockScene) {
129
115
  if (!mockScene) return true;
130
116
  const scenes = toArray(mockScene);
@@ -137,7 +123,6 @@ const vfs = createFsFromVolume(new Volume());
137
123
  function getDirname(importMetaUrl) {
138
124
  return path.dirname(fileURLToPath(importMetaUrl));
139
125
  }
140
- Debug("vite:mock-dev-server");
141
126
  const windowsSlashRE = /\\/g;
142
127
  const isWindows = os.platform() === "win32";
143
128
  function slash(p) {
@@ -146,12 +131,9 @@ function slash(p) {
146
131
  function normalizePath(id) {
147
132
  return path.posix.normalize(isWindows ? slash(id) : id);
148
133
  }
149
- //#endregion
150
- //#region src/utils/urlParse.ts
151
- /**
152
- * nodejs 从 19.0.0 开始 弃用 url.parse,因此使用 url.parse 来解析 可能会报错,
153
- * 使用 URL 来解析
154
- */
134
+
135
+
136
+
155
137
  function urlParse(input) {
156
138
  const url = new URL(input, "http://example.com");
157
139
  return {
@@ -159,29 +141,21 @@ function urlParse(input) {
159
141
  query: parse$1(url.search.replace(/^\?/, ""))
160
142
  };
161
143
  }
162
- //#endregion
163
- //#region src/utils/waitingFor.ts
164
- function waitingFor(onSuccess, maxRetry = 5) {
165
- return function wait(getter, retry = 0) {
166
- const value = getter();
167
- if (value) onSuccess(value);
168
- else if (retry < maxRetry) setTimeout(() => wait(getter, retry + 1), 100);
169
- };
170
- }
171
- //#endregion
172
- //#region src/compiler/processData.ts
144
+
145
+
173
146
  function processRawData(rawData) {
174
147
  return rawData.filter((item) => item[0]).map(([raw, __filepath__]) => {
175
148
  let mockConfig;
176
- if (raw.default) if (isArray(raw.default)) mockConfig = raw.default.map((item) => ({
177
- ...item,
178
- __filepath__
179
- }));
180
- else mockConfig = {
181
- ...raw.default,
182
- __filepath__
183
- };
184
- else if ("url" in raw) mockConfig = {
149
+ if (raw.default) {
150
+ if (isArray(raw.default)) mockConfig = raw.default.map((item) => ({
151
+ ...item,
152
+ __filepath__
153
+ }));
154
+ else mockConfig = {
155
+ ...raw.default,
156
+ __filepath__
157
+ };
158
+ } else if ("url" in raw) mockConfig = {
185
159
  ...raw,
186
160
  __filepath__
187
161
  };
@@ -215,16 +189,18 @@ function processMockData(mockList) {
215
189
  };
216
190
  if (current.ws !== true) {
217
191
  const validator = current.validator;
218
- if (!isEmptyObject(query)) if (isFunction(validator)) current.validator = function(request) {
219
- return isObjectSubset(request.query, query) && validator(request);
220
- };
221
- else if (validator) {
222
- current.validator = { ...validator };
223
- current.validator.query = current.validator.query ? {
224
- ...query,
225
- ...current.validator.query
226
- } : query;
227
- } else current.validator = { query };
192
+ if (!isEmptyObject(query)) {
193
+ if (isFunction(validator)) current.validator = function(request) {
194
+ return isObjectSubset(request.query, query) && validator(request);
195
+ };
196
+ else if (validator) {
197
+ current.validator = { ...validator };
198
+ current.validator.query = current.validator.query ? {
199
+ ...query,
200
+ ...current.validator.query
201
+ } : query;
202
+ } else current.validator = { query };
203
+ }
228
204
  }
229
205
  list.push(current);
230
206
  });
@@ -246,35 +222,18 @@ function keysCount(obj) {
246
222
  if (!obj) return 0;
247
223
  return objectKeys(obj).length;
248
224
  }
249
- //#endregion
250
- //#region src/mockHttp/cors.ts
251
- /**
252
- * Create CORS middleware
253
- *
254
- * 创建 CORS 中间件
255
- *
256
- * @param corsOptions - CORS options / CORS 配置项
257
- * @returns CORS middleware function or undefined / CORS 中间件函数或未定义
258
- */
225
+
226
+
227
+
259
228
  function createCors(corsOptions) {
260
229
  const corsMiddleware = corsOptions ? cors(corsOptions) : void 0;
261
230
  return corsMiddleware ? (req, res) => new Promise((resolve, reject) => corsMiddleware(req, res, (err) => {
262
231
  err ? reject(err) : resolve();
263
232
  })) : void 0;
264
233
  }
265
- //#endregion
266
- //#region src/mockHttp/request.ts
267
- /**
268
- * Parse request body
269
- *
270
- * 解析请求体 request.body
271
- *
272
- * @param req - Incoming message object / 入站消息对象
273
- * @param logger - Logger instance / 日志实例
274
- * @param formidableOptions - Formidable options for multipart form data / 用于 multipart 表单数据的 Formidable 配置项
275
- * @param bodyParserOptions - Body parser options / 请求体解析配置项
276
- * @returns Parsed request body / 解析后的请求体
277
- */
234
+
235
+
236
+
278
237
  async function parseRequestBody(req, logger, formidableOptions, bodyParserOptions = {}) {
279
238
  const method = req.method.toUpperCase();
280
239
  if (["HEAD", "OPTIONS"].includes(method)) return void 0;
@@ -298,26 +257,14 @@ async function parseRequestBody(req, logger, formidableOptions, bodyParserOption
298
257
  logger.error(e);
299
258
  }
300
259
  }
301
- /**
302
- * Default formidable options
303
- *
304
- * 默认的 formidable 配置项
305
- */
260
+
306
261
  const DEFAULT_FORMIDABLE_OPTIONS = {
307
262
  keepExtensions: true,
308
263
  filename(name, ext, part) {
309
264
  return part?.originalFilename || `${name}.${Date.now()}${ext ? `.${ext}` : ""}`;
310
265
  }
311
266
  };
312
- /**
313
- * Parse request body with multipart form data
314
- *
315
- * 解析 request form multipart body
316
- *
317
- * @param req - Incoming message object / 入站消息对象
318
- * @param options - Formidable options / Formidable 配置项
319
- * @returns Parsed request body / 解析后的请求体
320
- */
267
+
321
268
  async function parseRequestBodyWithMultipart(req, options) {
322
269
  const form = formidable({
323
270
  ...DEFAULT_FORMIDABLE_OPTIONS,
@@ -336,21 +283,9 @@ async function parseRequestBodyWithMultipart(req, options) {
336
283
  });
337
284
  });
338
285
  }
339
- /**
340
- * Cache for path-to-regexp match functions
341
- *
342
- * path-to-regexp 匹配函数缓存
343
- */
344
- const matcherCache = /* @__PURE__ */ new Map();
345
- /**
346
- * Parse request URL dynamic parameters
347
- *
348
- * 解析请求 url 中的动态参数 params
349
- *
350
- * @param pattern - URL pattern / URL 模式
351
- * @param url - Request URL / 请求 URL
352
- * @returns Parsed parameters / 解析后的参数
353
- */
286
+
287
+ const matcherCache = new Map();
288
+
354
289
  function parseRequestParams(pattern, url) {
355
290
  let matcher = matcherCache.get(pattern);
356
291
  if (!matcher) {
@@ -360,40 +295,15 @@ function parseRequestParams(pattern, url) {
360
295
  const matched = matcher(url);
361
296
  return matched ? matched.params : {};
362
297
  }
363
- /**
364
- * Validate request against validator
365
- *
366
- * 验证请求是否符合 validator
367
- *
368
- * @param request - Request object / 请求对象
369
- * @param validator - Validator object / 验证器对象
370
- * @returns Whether the request is valid / 请求是否有效
371
- */
298
+
372
299
  function requestValidate(request, validator) {
373
300
  return isObjectSubset(request.headers, validator.headers) && isObjectSubset(request.body, validator.body) && isObjectSubset(request.params, validator.params) && isObjectSubset(request.query, validator.query) && isObjectSubset(request.refererQuery, validator.refererQuery);
374
301
  }
375
- /**
376
- * Format log data
377
- *
378
- * 格式化日志数据
379
- *
380
- * @param prefix - Log prefix / 日志前缀
381
- * @param data - Data to format / 要格式化的数据
382
- * @returns Formatted log string / 格式化后的日志字符串
383
- */
302
+
384
303
  function formatLog(prefix, data) {
385
304
  return !data || isEmptyObject(data) ? "" : ` ${ansis.gray(`${prefix}:`)}${JSON.stringify(data)}`;
386
305
  }
387
- /**
388
- * Generate request log
389
- *
390
- * 生成请求日志
391
- *
392
- * @param request - Request object / 请求对象
393
- * @param filepath - Mock file path / Mock 文件路径
394
- * @param shouldSimulateError - Whether to simulate error / 是否模拟错误
395
- * @returns Formatted log string / 格式化后的日志字符串
396
- */
306
+
397
307
  function requestLog(request, filepath, shouldSimulateError) {
398
308
  const { url, method, query, params, body } = request;
399
309
  let { pathname } = new URL(url, "http://example.com");
@@ -406,22 +316,9 @@ function requestLog(request, filepath, shouldSimulateError) {
406
316
  const file = ` ${ansis.dim.underline(`(${filepath})`)}`;
407
317
  return `${ms}${es} ${pathname}${qs}${ps}${bs}${file}`;
408
318
  }
409
- //#endregion
410
- //#region src/mockHttp/matcher.ts
411
- /**
412
- * Find matching mock data
413
- *
414
- * 查找匹配的 mock data
415
- *
416
- * @param mockList - Mock options list / Mock 配置列表
417
- * @param logger - Logger instance / 日志实例
418
- * @param options - Find options / 查找选项
419
- * @param options.pathname - Request pathname / 请求路径
420
- * @param options.method - HTTP method / HTTP 方法
421
- * @param options.request - Request object / 请求对象
422
- * @param options.activeScene - Active scene / 当前场景
423
- * @returns Matched mock HTTP item or undefined / 匹配的 Mock HTTP 项或未定义
424
- */
319
+
320
+
321
+
425
322
  function findMockData(mockList, logger, { pathname, method, request, activeScene }) {
426
323
  return mockList.find((mock) => {
427
324
  if (!pathname || !mock || !mock.url || mock.ws) return false;
@@ -450,212 +347,8 @@ function findMockData(mockList, logger, { pathname, method, request, activeScene
450
347
  return hasMock;
451
348
  });
452
349
  }
453
- //#endregion
454
- //#region src/cookies/constants.ts
455
- /**
456
- * RegExp to match field-content in RFC 7230 sec 3.2
457
- *
458
- * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
459
- * field-vchar = VCHAR / obs-text
460
- * obs-text = %x80-FF
461
- */
462
- const fieldContentRegExp = /^[\t\u0020-\u007E\u0080-\u00FF]+$/;
463
- /**
464
- * RegExp to match Priority cookie attribute value.
465
- */
466
- const PRIORITY_REGEXP = /^(?:low|medium|high)$/i;
467
- /**
468
- * Cache for generated name regular expressions.
469
- */
470
- const REGEXP_CACHE = Object.create(null);
471
- /**
472
- * RegExp to match all characters to escape in a RegExp.
473
- */
474
- const REGEXP_ESCAPE_CHARS_REGEXP = /[\^$\\.*+?()[\]{}|]/g;
475
- /**
476
- * RegExp to match basic restricted name characters for loose validation.
477
- */
478
- const RESTRICTED_NAME_CHARS_REGEXP = /[;=]/;
479
- /**
480
- * RegExp to match basic restricted value characters for loose validation.
481
- */
482
- const RESTRICTED_VALUE_CHARS_REGEXP = /;/;
483
- /**
484
- * RegExp to match Same-Site cookie attribute value.
485
- */
486
- const SAME_SITE_REGEXP = /^(?:lax|none|strict)$/i;
487
- //#endregion
488
- //#region src/cookies/Cookie.ts
489
- var Cookie = class {
490
- name;
491
- value;
492
- maxAge;
493
- expires;
494
- path = "/";
495
- domain;
496
- secure = false;
497
- httpOnly = true;
498
- sameSite = false;
499
- overwrite = false;
500
- priority;
501
- partitioned;
502
- constructor(name, value, options = {}) {
503
- if (!fieldContentRegExp.test(name) || RESTRICTED_NAME_CHARS_REGEXP.test(name)) throw new TypeError("argument name is invalid");
504
- if (value && (!fieldContentRegExp.test(value) || RESTRICTED_VALUE_CHARS_REGEXP.test(value))) throw new TypeError("argument value is invalid");
505
- this.name = name;
506
- this.value = value;
507
- Object.assign(this, options);
508
- if (!this.value) {
509
- this.expires = /* @__PURE__ */ new Date(0);
510
- this.maxAge = void 0;
511
- }
512
- if (this.path && !fieldContentRegExp.test(this.path)) throw new TypeError("[Cookie] option path is invalid");
513
- if (this.domain && !fieldContentRegExp.test(this.domain)) throw new TypeError("[Cookie] option domain is invalid");
514
- if (typeof this.maxAge === "number" ? Number.isNaN(this.maxAge) || !Number.isFinite(this.maxAge) : this.maxAge) throw new TypeError("[Cookie] option maxAge is invalid");
515
- if (this.priority && !PRIORITY_REGEXP.test(this.priority)) throw new TypeError("[Cookie] option priority is invalid");
516
- if (this.sameSite && this.sameSite !== true && !SAME_SITE_REGEXP.test(this.sameSite)) throw new TypeError("[Cookie] option sameSite is invalid");
517
- }
518
- toString() {
519
- return `${this.name}=${this.value}`;
520
- }
521
- toHeader() {
522
- let header = this.toString();
523
- if (this.maxAge) this.expires = new Date(Date.now() + this.maxAge);
524
- if (this.path) header += `; path=${this.path}`;
525
- if (this.expires) header += `; expires=${this.expires.toUTCString()}`;
526
- if (this.domain) header += `; domain=${this.domain}`;
527
- if (this.priority) header += `; priority=${this.priority.toLowerCase()}`;
528
- if (this.sameSite) header += `; samesite=${this.sameSite === true ? "strict" : this.sameSite.toLowerCase()}`;
529
- if (this.secure) header += "; secure";
530
- if (this.httpOnly) header += "; httponly";
531
- if (this.partitioned) header += "; partitioned";
532
- return header;
533
- }
534
- };
535
- //#endregion
536
- //#region src/cookies/timeSafeCompare.ts
537
- function bufferEqual(a, b) {
538
- if (a.length !== b.length) return false;
539
- if (crypto.timingSafeEqual) return crypto.timingSafeEqual(a, b);
540
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
541
- return true;
542
- }
543
- function createHmac(key, data) {
544
- return crypto.createHmac("sha256", key).update(data).digest();
545
- }
546
- function timeSafeCompare(a, b) {
547
- const sa = String(a);
548
- const sb = String(b);
549
- const key = crypto.randomBytes(32);
550
- return bufferEqual(createHmac(key, sa), createHmac(key, sb)) && a === b;
551
- }
552
- //#endregion
553
- //#region src/cookies/Keygrip.ts
554
- const SLASH_PATTERN = /[/+=]/g;
555
- const REPLACE_MAP = {
556
- "/": "_",
557
- "+": "-",
558
- "=": ""
559
- };
560
- var Keygrip = class {
561
- algorithm;
562
- encoding;
563
- keys = [];
564
- constructor(keys, algorithm, encoding) {
565
- this.keys = keys;
566
- this.algorithm = algorithm || "sha256";
567
- this.encoding = encoding || "base64";
568
- }
569
- sign(data, key = this.keys[0]) {
570
- return crypto.createHmac(this.algorithm, key).update(data).digest(this.encoding).replace(SLASH_PATTERN, (m) => REPLACE_MAP[m]);
571
- }
572
- index(data, digest) {
573
- for (let i = 0, l = this.keys.length; i < l; i++) if (timeSafeCompare(digest, this.sign(data, this.keys[i]))) return i;
574
- return -1;
575
- }
576
- verify(data, digest) {
577
- return this.index(data, digest) > -1;
578
- }
579
- };
580
- //#endregion
581
- //#region src/cookies/Cookies.ts
582
- var Cookies = class {
583
- request;
584
- response;
585
- secure;
586
- keys;
587
- constructor(req, res, options = {}) {
588
- this.request = req;
589
- this.response = res;
590
- this.secure = options.secure;
591
- if (options.keys instanceof Keygrip) this.keys = options.keys;
592
- else if (isArray(options.keys)) this.keys = new Keygrip(options.keys);
593
- }
594
- set(name, value, options) {
595
- const req = this.request;
596
- const res = this.response;
597
- const headers = toArray(res.getHeader("Set-Cookie"));
598
- const cookie = new Cookie(name, value, options);
599
- const signed = options?.signed ?? !!this.keys;
600
- const secure = this.secure === void 0 ? req.protocol === "https" || isRequestEncrypted(req) : Boolean(this.secure);
601
- if (!secure && options?.secure) throw new Error("Cannot send secure cookie over unencrypted connection");
602
- cookie.secure = options?.secure ?? secure;
603
- pushCookie(headers, cookie);
604
- if (signed && options) {
605
- if (!this.keys) throw new Error(".keys required for signed cookies");
606
- cookie.value = this.keys.sign(cookie.toString());
607
- cookie.name += ".sig";
608
- pushCookie(headers, cookie);
609
- }
610
- (res.set ? http.OutgoingMessage.prototype.setHeader : res.setHeader).call(res, "Set-Cookie", headers);
611
- return this;
612
- }
613
- get(name, options) {
614
- const signName = `${name}.sig`;
615
- const signed = options?.signed ?? !!this.keys;
616
- const header = this.request.headers.cookie;
617
- if (!header) return;
618
- const match = header.match(getPattern(name));
619
- if (!match) return;
620
- let value = match[1];
621
- if (value[0] === "\"") value = value.slice(1, -1);
622
- if (!options || !signed) return value;
623
- const remote = this.get(signName);
624
- if (!remote) return;
625
- const data = `${name}=${value}`;
626
- if (!this.keys) throw new Error(".keys required for signed cookies");
627
- const index = this.keys.index(data, remote);
628
- if (index < 0) this.set(signName, null, {
629
- path: "/",
630
- signed: false
631
- });
632
- else {
633
- index && this.set(signName, this.keys.sign(data), { signed: false });
634
- return value;
635
- }
636
- }
637
- };
638
- /**
639
- * Get the pattern to search for a cookie in a string.
640
- */
641
- function getPattern(name) {
642
- if (!REGEXP_CACHE[name]) REGEXP_CACHE[name] = new RegExp(`(?:^|;) *${name.replace(REGEXP_ESCAPE_CHARS_REGEXP, "\\$&")}=([^;]*)`);
643
- return REGEXP_CACHE[name];
644
- }
645
- /**
646
- * Get the encrypted status for a request.
647
- */
648
- function isRequestEncrypted(req) {
649
- return Boolean(req.socket ? req.socket.encrypted : req.connection.encrypted);
650
- }
651
- function pushCookie(headers, cookie) {
652
- if (cookie.overwrite) {
653
- for (let i = headers.length - 1; i >= 0; i--) if (headers[i].indexOf(`${cookie.name}=`) === 0) headers.splice(i, 1);
654
- }
655
- headers.push(cookie.toHeader());
656
- }
657
- //#endregion
658
- //#region src/recorder/constants.ts
350
+
351
+
659
352
  const FILTERED_RESPONSE_HEADERS = [
660
353
  "date",
661
354
  "expires",
@@ -696,17 +389,9 @@ const FILTERED_RESPONSE_HEADERS = [
696
389
  "server-timing",
697
390
  "x-dns-prefetch-control"
698
391
  ];
699
- //#endregion
700
- //#region src/recorder/decompress.ts
701
- /**
702
- * Decode response body according to encoding
703
- *
704
- * 根据编码解码响应体
705
- *
706
- * @param rawBody 原始响应体
707
- * @param encoding 编码
708
- * @returns 解码后的响应体
709
- */
392
+
393
+
394
+
710
395
  async function decompressBody(rawBody, encoding) {
711
396
  try {
712
397
  switch (encoding.toLowerCase()) {
@@ -736,16 +421,7 @@ async function decompressBody(rawBody, encoding) {
736
421
  };
737
422
  }
738
423
  let zstdStreaming = null;
739
- /**
740
- * Decompress zstd compressed data
741
- *
742
- * 解压缩 zstd 压缩数据
743
- *
744
- * zlib.zstdDecompress 从 v22.15.0 开始支持,对于旧版本 Node.js 可以使用 zstd-codec 库
745
- *
746
- * @param rawBody 压缩数据
747
- * @returns 解压缩后的数据
748
- */
424
+
749
425
  async function zstd(rawBody) {
750
426
  if (zlib.zstdDecompress) return new Promise((resolve, reject) => {
751
427
  zlib.zstdDecompress(rawBody, (err, data) => {
@@ -762,14 +438,7 @@ async function zstd(rawBody) {
762
438
  }
763
439
  return zstdStreaming.decompress(rawBody, rawBody.length);
764
440
  }
765
- /**
766
- * Decompress brotli compressed data
767
- *
768
- * 解压缩 brotli 压缩数据
769
- *
770
- * @param rawBody 压缩数据
771
- * @returns 解压缩后的数据
772
- */
441
+
773
442
  async function brotli(rawBody) {
774
443
  return new Promise((resolve, reject) => {
775
444
  zlib.brotliDecompress(rawBody, (err, data) => {
@@ -791,8 +460,8 @@ async function deflate(rawBody) {
791
460
  });
792
461
  });
793
462
  }
794
- //#endregion
795
- //#region src/recorder/helper.ts
463
+
464
+
796
465
  const timeFormatter = new Intl.DateTimeFormat("en-US", {
797
466
  year: "numeric",
798
467
  month: "numeric",
@@ -802,14 +471,7 @@ const timeFormatter = new Intl.DateTimeFormat("en-US", {
802
471
  second: "numeric",
803
472
  hour12: false
804
473
  });
805
- /**
806
- * 处理记录的请求
807
- *
808
- * @param req 原始请求对象
809
- * @param pathname 请求路径
810
- * @param body 请求体
811
- * @returns 处理后的请求记录
812
- */
474
+
813
475
  function processRecordReq(req, pathname, body) {
814
476
  const { query } = urlParse(req.url);
815
477
  const method = req.method.toUpperCase();
@@ -833,13 +495,7 @@ function processRecordReq(req, pathname, body) {
833
495
  body
834
496
  };
835
497
  }
836
- /**
837
- * 处理记录的响应
838
- *
839
- * @param res 原始响应对象
840
- * @param body 响应体
841
- * @returns 处理后的响应记录
842
- */
498
+
843
499
  async function processRecordRes(res, body) {
844
500
  const status = res.statusCode || 200;
845
501
  const statusText = res.statusMessage || "OK";
@@ -861,13 +517,7 @@ async function processRecordRes(res, body) {
861
517
  body: body.toString(isText ? "utf-8" : "base64")
862
518
  };
863
519
  }
864
- /**
865
- * 判断两个请求是否是同一个请求
866
- *
867
- * @param prev 上一个请求
868
- * @param current 当前请求
869
- * @returns 是否是同一个请求
870
- */
520
+
871
521
  function isSameRecord(prev, current) {
872
522
  if (prev.pathname !== current.pathname || prev.method !== current.method) return false;
873
523
  if (prev.bodyType !== current.bodyType) return false;
@@ -880,12 +530,7 @@ function isSameRecord(prev, current) {
880
530
  if (!deepEqual(prev.body, current.body)) return false;
881
531
  return true;
882
532
  }
883
- /**
884
- * 创建请求记录过滤器
885
- *
886
- * @param filter 记录过滤选项
887
- * @returns 请求匹配函数
888
- */
533
+
889
534
  function createRecordMatcher(filter) {
890
535
  if (isFunction(filter)) return filter;
891
536
  const { mode = "glob" } = filter;
@@ -899,25 +544,14 @@ function createRecordMatcher(filter) {
899
544
  return include.some((pattern) => isPathMatch(pattern, req.pathname)) && exclude.every((pattern) => !isPathMatch(pattern, req.pathname));
900
545
  };
901
546
  }
902
- /**
903
- * 生成记录文件路径
904
- *
905
- * @param pathname 请求路径
906
- * @param dir 记录目录
907
- * @returns 记录文件路径
908
- */
547
+
909
548
  function getFilepath(pathname, dir) {
910
549
  return path.join(dir, `${kebabCase(pathname)}.json`);
911
550
  }
912
- //#endregion
913
- //#region src/recorder/storage.ts
914
- const storage = /* @__PURE__ */ new Map();
915
- /**
916
- * 读取记录文件
917
- *
918
- * @param filepath 记录文件路径
919
- * @returns 记录的请求数组
920
- */
551
+
552
+
553
+ const storage = new Map();
554
+
921
555
  async function readRecordStorage(filepath) {
922
556
  if (storage.has(filepath)) return storage.get(filepath);
923
557
  try {
@@ -931,12 +565,7 @@ async function readRecordStorage(filepath) {
931
565
  return [];
932
566
  }
933
567
  }
934
- /**
935
- * 写入记录文件
936
- *
937
- * @param filepath 记录文件路径
938
- * @param records 记录的请求数组
939
- */
568
+
940
569
  async function writeRecordStorage(filepath, records) {
941
570
  try {
942
571
  storage.set(filepath, records);
@@ -946,16 +575,8 @@ async function writeRecordStorage(filepath, records) {
946
575
  console.error(`Error writing record file ${filepath}:`, error);
947
576
  }
948
577
  }
949
- const originalReqCache = /* @__PURE__ */ new WeakMap();
950
- /**
951
- * Record a request with the raw request object.
952
- *
953
- * 记录原始请求对象的请求
954
- *
955
- * @param req The original request object / 原始请求对象
956
- * @param pathname The request pathname / 请求路径名
957
- * @param body The request body / 请求体
958
- */
578
+ const originalReqCache = new WeakMap();
579
+
959
580
  function recordRequestWithRawReq(req, pathname, body) {
960
581
  originalReqCache.set(req, {
961
582
  body,
@@ -963,11 +584,9 @@ function recordRequestWithRawReq(req, pathname, body) {
963
584
  timestamp: Date.now()
964
585
  });
965
586
  }
966
- //#endregion
967
- //#region src/recorder/Recorder.ts
968
- /**
969
- * 请求记录器
970
- */
587
+
588
+
589
+
971
590
  var Recorder = class {
972
591
  options;
973
592
  filter;
@@ -1024,19 +643,9 @@ var Recorder = class {
1024
643
  if (!fs.existsSync(path.join(dirname, ".gitignore"))) await promises.writeFile(path.join(dirname, ".gitignore"), "*\n", "utf-8");
1025
644
  }
1026
645
  };
1027
- //#endregion
1028
- //#region src/recorder/replay.ts
1029
- /**
1030
- * Replay a recorded request.
1031
- *
1032
- * 重放已记录的请求
1033
- *
1034
- * @param rawReq The original request object / 原始请求对象
1035
- * @param pathname The request pathname / 请求路径名
1036
- * @param body The request body / 请求体
1037
- * @param options Record options / 录制配置项
1038
- * @returns The recorded request object if found, otherwise undefined / 如果找到记录的请求对象,则返回该对象,否则返回 undefined
1039
- */
646
+
647
+
648
+
1040
649
  async function replayRecordedRequest(rawReq, pathname, body, options) {
1041
650
  const req = processRecordReq(rawReq, pathname, body);
1042
651
  const filepath = path.join(options.cwd, getFilepath(req.pathname, options.dir));
@@ -1059,8 +668,8 @@ async function replayRecordedRequest(rawReq, pathname, body, options) {
1059
668
  };
1060
669
  }
1061
670
  }
1062
- //#endregion
1063
- //#region src/mockHttp/matchingWeight.ts
671
+
672
+
1064
673
  const tokensCache = {};
1065
674
  function getTokens(rule) {
1066
675
  if (tokensCache[rule]) return tokensCache[rule];
@@ -1112,16 +721,7 @@ function defaultPriority(rules) {
1112
721
  const highest = getHighest(rules);
1113
722
  return rules.sort((a, b) => computedWeight(a, highest) - computedWeight(b, highest));
1114
723
  }
1115
- /**
1116
- * Calculate matching weight for mock URLs
1117
- *
1118
- * 计算 Mock URL 的匹配权重
1119
- *
1120
- * @param rules - Array of URL patterns / URL 模式数组
1121
- * @param url - Request URL / 请求 URL
1122
- * @param priority - Priority configuration / 优先级配置
1123
- * @returns Sorted array of matched rules / 排序后的匹配规则数组
1124
- */
724
+
1125
725
  function matchingWeight(rules, url, priority) {
1126
726
  let matched = defaultPriority(rules.filter((rule) => isPathMatch(rule, url)));
1127
727
  const { global = [], special = {} } = priority;
@@ -1146,16 +746,10 @@ function matchingWeight(rules, url, priority) {
1146
746
  }
1147
747
  return matched;
1148
748
  }
1149
- //#endregion
1150
- //#region src/mockHttp/requestRecovery.ts
1151
- /**
1152
- * 请求复原
1153
- *
1154
- * 由于 parseReqBody 在解析请求时,会将请求流消费,
1155
- * 导致当接口不需要被 mock,继而由 vite http-proxy 转发时,请求流无法继续。
1156
- * 为此,我们在请求流中记录请求数据,当当前请求无法继续时,可以从备份中恢复请求流
1157
- */
1158
- const requestCollectCache = /* @__PURE__ */ new WeakMap();
749
+
750
+
751
+
752
+ const requestCollectCache = new WeakMap();
1159
753
  function collectRequest(req) {
1160
754
  const chunks = [];
1161
755
  req.on("data", (chunk) => {
@@ -1173,42 +767,18 @@ function rewriteRequest(proxyReq, req) {
1173
767
  if (!proxyReq.writableEnded) proxyReq.write(buffer);
1174
768
  }
1175
769
  }
1176
- //#endregion
1177
- //#region src/mockHttp/response.ts
1178
- /**
1179
- * Get HTTP status text by status code
1180
- *
1181
- * 根据状态码获取状态文本
1182
- *
1183
- * @param status - HTTP status code / HTTP 状态码
1184
- * @returns HTTP status text / HTTP 状态文本
1185
- */
770
+
771
+
772
+
1186
773
  function getHTTPStatusText(status) {
1187
774
  return HTTP_STATUS[status] || "Unknown";
1188
775
  }
1189
- /**
1190
- * Set response status
1191
- *
1192
- * 设置响应状态
1193
- *
1194
- * @param response - Response object / 响应对象
1195
- * @param status - HTTP status code / HTTP 状态码
1196
- * @param statusText - HTTP status text / HTTP 状态文本
1197
- */
776
+
1198
777
  function provideResponseStatus(response, status = 200, statusText) {
1199
778
  response.statusCode = status;
1200
779
  response.statusMessage = statusText || getHTTPStatusText(status);
1201
780
  }
1202
- /**
1203
- * Set response headers
1204
- *
1205
- * 设置响应头
1206
- *
1207
- * @param req - Request object / 请求对象
1208
- * @param res - Response object / 响应对象
1209
- * @param mock - Mock HTTP item / Mock HTTP 配置项
1210
- * @param logger - Logger instance / 日志实例
1211
- */
781
+
1212
782
  async function provideResponseHeaders(req, res, mock, logger) {
1213
783
  const { headers, type = "json" } = mock;
1214
784
  const filepath = mock.__filepath__;
@@ -1225,16 +795,7 @@ async function provideResponseHeaders(req, res, mock, logger) {
1225
795
  }
1226
796
  objectKeys(data).forEach((key) => res.setHeader(key, data[key]));
1227
797
  }
1228
- /**
1229
- * Set response cookies
1230
- *
1231
- * 设置响应cookie
1232
- *
1233
- * @param req - Request object / 请求对象
1234
- * @param res - Response object / 响应对象
1235
- * @param mock - Mock HTTP item / Mock HTTP 配置项
1236
- * @param logger - Logger instance / 日志实例
1237
- */
798
+
1238
799
  async function provideResponseCookies(req, res, mock, logger) {
1239
800
  const { cookies } = mock;
1240
801
  if (!cookies) return;
@@ -1250,15 +811,7 @@ async function provideResponseCookies(req, res, mock, logger) {
1250
811
  res.setCookie(key, value, options);
1251
812
  });
1252
813
  }
1253
- /**
1254
- * Send response data
1255
- *
1256
- * 设置响应数据
1257
- *
1258
- * @param res - Response object / 响应对象
1259
- * @param raw - Response body data / 响应体数据
1260
- * @param type - Response data type / 响应数据类型
1261
- */
814
+
1262
815
  function sendResponseData(res, raw, type) {
1263
816
  if (isReadableStream(raw)) raw.pipe(res);
1264
817
  else if (Buffer.isBuffer(raw)) res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
@@ -1267,14 +820,7 @@ function sendResponseData(res, raw, type) {
1267
820
  res.end(type === "buffer" ? Buffer.from(content) : content);
1268
821
  }
1269
822
  }
1270
- /**
1271
- * Apply real response delay
1272
- *
1273
- * 实际响应延迟
1274
- *
1275
- * @param startTime - Request start time / 请求开始时间
1276
- * @param delay - Delay configuration / 延迟配置
1277
- */
823
+
1278
824
  async function responseRealDelay(startTime, delay) {
1279
825
  if (!delay || typeof delay === "number" && delay <= 0 || isArray(delay) && delay.length !== 2) return;
1280
826
  let realDelay = 0;
@@ -1284,28 +830,9 @@ async function responseRealDelay(startTime, delay) {
1284
830
  } else realDelay = delay - (timestamp() - startTime);
1285
831
  if (realDelay > 0) await sleep(realDelay);
1286
832
  }
1287
- //#endregion
1288
- //#region src/mockHttp/middleware.ts
1289
- /**
1290
- * Create mock middleware
1291
- *
1292
- * 创建 Mock 中间件
1293
- *
1294
- * @param compiler - Compiler instance / 编译器实例
1295
- * @param options - Middleware options / 中间件配置项
1296
- * @param options.formidableOptions - Formidable options / Formidable 配置项
1297
- * @param options.bodyParserOptions - Body parser options / 请求体解析配置项
1298
- * @param options.proxies - Proxy paths / 代理路径
1299
- * @param options.cookiesOptions - Cookies options / Cookies 配置项
1300
- * @param options.logger - Logger instance / 日志实例
1301
- * @param options.priority - Path matching priority / 路径匹配优先级
1302
- * @param options.cors - CORS options / CORS 配置项
1303
- * @param options.record - Record options / 录制配置项
1304
- * @param options.replay - Replay options / 回放配置项
1305
- * @param options.activeScene - Active scene / 活动场景
1306
- *
1307
- * @returns Connect middleware function / Connect 中间件函数
1308
- */
833
+
834
+
835
+
1309
836
  function createMockMiddleware(compiler, { formidableOptions = {}, bodyParserOptions = {}, proxies, cookiesOptions, logger, priority = {}, cors: corsOptions, record, replay, activeScene }) {
1310
837
  const cors = createCors(corsOptions);
1311
838
  const [globFilter, contextFilter] = partition(proxies, (item) => isString(item) && item.includes("*"));
@@ -1361,7 +888,7 @@ function createMockMiddleware(compiler, { formidableOptions = {}, bodyParserOpti
1361
888
  }
1362
889
  const request = req;
1363
890
  const response = res;
1364
- Object.assign(request, extraReq);
891
+ Object.assign(request, omit(extraReq, ["headers"]));
1365
892
  request.params = parseRequestParams(mock.url, pathname);
1366
893
  response.setCookie = cookies.set.bind(cookies);
1367
894
  const { delay, type = "json", response: responseFn, log: logLevel, error: errorConfig, __filepath__: filepath } = mock;
@@ -1405,20 +932,20 @@ function createMockMiddleware(compiler, { formidableOptions = {}, bodyParserOpti
1405
932
  res.end("");
1406
933
  };
1407
934
  }
1408
- //#endregion
1409
- //#region src/mockWebsocket/server.ts
935
+
936
+
1410
937
  function mockWebSocket(compiler, httpServer, { wsProxies: proxies, cookiesOptions, logger }) {
1411
- const hmrMap = /* @__PURE__ */ new Map();
1412
- const poolMap = /* @__PURE__ */ new Map();
1413
- const wssContextMap = /* @__PURE__ */ new WeakMap();
938
+ const hmrMap = new Map();
939
+ const poolMap = new Map();
940
+ const wssContextMap = new WeakMap();
1414
941
  const getWssMap = (mockUrl) => {
1415
942
  let wssMap = poolMap.get(mockUrl);
1416
- if (!wssMap) poolMap.set(mockUrl, wssMap = /* @__PURE__ */ new Map());
943
+ if (!wssMap) poolMap.set(mockUrl, wssMap = new Map());
1417
944
  return wssMap;
1418
945
  };
1419
946
  const addHmr = (filepath, mockUrl) => {
1420
947
  let urlList = hmrMap.get(filepath);
1421
- if (!urlList) hmrMap.set(filepath, urlList = /* @__PURE__ */ new Set());
948
+ if (!urlList) hmrMap.set(filepath, urlList = new Set());
1422
949
  urlList.add(mockUrl);
1423
950
  };
1424
951
  const setupWss = (wssMap, wss, mock, context, pathname, filepath) => {
@@ -1524,8 +1051,8 @@ function cleanupRunner(cleanupList) {
1524
1051
  let cleanup;
1525
1052
  while (cleanup = cleanupList.shift()) cleanup?.();
1526
1053
  }
1527
- //#endregion
1528
- //#region src/core/logger.ts
1054
+
1055
+
1529
1056
  const logLevels = {
1530
1057
  silent: 0,
1531
1058
  error: 1,
@@ -1540,7 +1067,7 @@ function createLogger(prefix, defaultLevel = "info") {
1540
1067
  if (logLevels[level] >= logLevels[type]) {
1541
1068
  const method = type === "info" || type === "debug" ? "log" : type;
1542
1069
  const tag = type === "debug" ? ansis.magenta.bold(prefix) : type === "info" ? ansis.cyan.bold(prefix) : type === "warn" ? ansis.yellow.bold(prefix) : ansis.red.bold(prefix);
1543
- const format = `${ansis.dim((/* @__PURE__ */ new Date()).toLocaleTimeString())} ${tag} ${msg}`;
1070
+ const format = `${ansis.dim(( new Date()).toLocaleTimeString())} ${tag} ${msg}`;
1544
1071
  console[method](format);
1545
1072
  }
1546
1073
  }
@@ -1559,5 +1086,5 @@ function createLogger(prefix, defaultLevel = "info") {
1559
1086
  }
1560
1087
  };
1561
1088
  }
1562
- //#endregion
1563
- export { rewriteRequest as a, processRawData as c, normalizePath as d, vfs as f, createMatcher as h, createMockMiddleware as i, sortByValidator as l, getPackageDeps as m, logLevels as n, Recorder as o, getPackageDepList as p, mockWebSocket as r, processMockData as s, createLogger as t, waitingFor as u };
1089
+
1090
+ export { rewriteRequest as a, processRawData as c, vfs as d, getPackageDepList as f, createMockMiddleware as i, sortByValidator as l, createMatcher as m, logLevels as n, Recorder as o, getPackageDeps as p, mockWebSocket as r, processMockData as s, createLogger as t, normalizePath as u };