whistle.mockbubu 1.0.0-dev.4 → 2.0.0-beta.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/lib/utils.js CHANGED
@@ -1,124 +1,98 @@
1
+ /**
2
+ * Whistle.mockbubu 工具函数集
3
+ * V3 架构:简化版本,移除废弃的版本管理函数
4
+ */
5
+
1
6
  const zlib = require('zlib')
2
7
  const { RuleValueMap } = require('./const')
3
- // const URL = require('url')
4
- const setProperty = (storage, property, val) => {
5
- if (storage.hasProperty(property)) {
6
- const oldVal = storage.getProperty(property)
7
-
8
- if (typeof oldVal === 'object') {
9
- storage.setProperty(property, Object.assign(oldVal, val))
10
- } else {
11
- storage.setProperty(property, val)
12
- }
13
- } else {
14
- storage.setProperty(property, val)
15
- }
16
- }
17
8
 
18
- const removePropertyAttr = (storage, property, attrName) => {
19
- if (storage.hasProperty(property)) {
20
- const oldVal = storage.getProperty(property)
9
+ // ========================================
10
+ // 文件操作函数
11
+ // ========================================
21
12
 
22
- if (typeof oldVal === 'object') {
23
- delete oldVal[attrName]
24
- storage.setProperty(property, oldVal)
25
- }
13
+ // 按组获取文件列表
14
+ const getGroupFileList = async (storage, groupId) => {
15
+ if (!groupId) {
16
+ console.warn('[mockbubu] getGroupFileList: groupId 为空,返回空数组')
17
+ return []
26
18
  }
27
- }
28
- const getPropertyAttr = (storage, property, attrName) => {
29
- const props = storage.getProperty(property)
30
19
 
31
- return props[attrName]
32
- }
33
- const getProperty = (storage, property) => {
34
- return storage.getProperty(property)
35
- }
36
- const removeProperty = (storage, property) => {
37
- return storage.removeProperty(property)
38
- }
39
-
40
- const writeFile = ({ storage, filename, body, properties, encoding }) => {
41
- switch (encoding) {
42
- case 'gzip':
43
- body = zlib.gunzipSync(body)
44
- break
45
- case 'deflate':
46
- body = zlib.inflateSync(body)
47
- break
48
- case 'br':
49
- body = zlib.brotliDecompressSync(body)
50
- break
51
- default:
52
- break
53
- }
54
- setProperty(storage, filename, properties || {})
55
-
56
- const type = Object.prototype.toString.call(body)
57
- let content
58
- switch (type) {
59
- case '[object String]':
60
- content = body
61
- break
62
- case '[object Object]':
63
- content = JSON.stringify(body)
64
- break
65
- case '[object Uint8Array]':
66
- content = body.toString()
67
- break
68
- default:
69
- content = body
20
+ try {
21
+ // 获取所有文件列表
22
+ // StorageAdapter 返回的格式已经是: groupId/filename (不需要解码)
23
+ const allFiles = await storage.getFileList() || []
24
+ const groupPrefix = `${groupId}/`
25
+ const result = []
26
+
27
+ // 过滤出属于该组的文件
28
+ allFiles.forEach((item) => {
29
+ try {
30
+ // StorageAdapter 返回的文件名格式: groupId/filename
31
+ // 例如: default/https://m.shein.com/us/bff-api/...
32
+ // 不需要任何解码,直接使用
33
+ const name = item.name
34
+
35
+ // 检查是否以 "groupId/" 开头
36
+ if (name.startsWith(groupPrefix)) {
37
+ // 提取纯文件名(去除 groupId/ 前缀)
38
+ const filename = name.substring(groupPrefix.length)
39
+
40
+ result.push({
41
+ name: filename, // 返回纯文件名,不包含 groupId 前缀
42
+ size: item.size || 0,
43
+ })
44
+ }
45
+ } catch (err) {
46
+ // 处理失败时跳过该文件
47
+ console.warn(`[mockbubu] 处理文件失败: ${item.name}`, err.message)
48
+ }
49
+ })
50
+
51
+ return result
52
+ } catch (error) {
53
+ console.error('[mockbubu] getGroupFileList 执行失败:', error.message)
54
+ console.error('[mockbubu] 错误堆栈:', error.stack)
55
+ return []
70
56
  }
71
- storage.writeFile(filename, content)
72
- setApiListUpdated(storage, true)
73
- console.log('%c [ content ]-58', 'font-size:13px; background:pink; color:#bf2c9f;', content)
74
- // switch (type) {
75
- // case '[object String]':
76
- // storage.writeFile(filename, body)
77
- // break
78
- // case '[object Object]':
79
- // storage.writeFile(filename, JSON.stringify(body))
80
- // break
81
- // case '[object Uint8Array]':
82
- // storage.writeFile(filename, body.toString())
83
- // break
84
- // default:
85
- // storage.writeFile(filename, body) // TODO:
86
- // }
87
- }
88
-
89
- const getApiList = (storage) => {
90
- return storage.getFileList()
91
57
  }
92
58
 
93
- const updateFile = (storage, filename, body) => {
59
+ const updateFile = (storage, filename, body, groupId) => {
94
60
  if (!filename) {
95
61
  throw new Error('文件名不能为空')
96
62
  }
97
- // storage.writeFile(filename, JSON.stringify(body))
98
63
 
99
- const sessionCache = storage.readFile(filename)
64
+ const filePath = groupId ? `${groupId}/${filename}` : filename
65
+ const sessionCache = storage.readFile(filePath)
100
66
  if (sessionCache) {
101
67
  const session = JSON.parse(sessionCache)
102
68
  session.res.body = JSON.stringify(body)
103
- storage.writeFile(filename, JSON.stringify(session))
69
+ storage.writeFile(filePath, JSON.stringify(session))
104
70
  }
105
71
  }
106
72
 
107
- const readFile = (storage, filename) => {
108
- // return storage.readFile(filename)
109
-
110
- const sessionCache = storage.readFile(filename)
73
+ const readFile = async (storage, filename, groupId) => {
74
+ const filePath = groupId ? `${groupId}/${filename}` : filename
75
+ const sessionCache = await storage.readFile(filePath)
111
76
  if (sessionCache) {
112
77
  const resCache = JSON.parse(sessionCache)?.res
113
- console.log(resCache)
114
78
  return resCache.body
115
79
  }
116
80
  }
117
81
 
118
- const removeFile = (storage, name) => {
119
- storage.removeFile(name)
120
- removeProperty(storage, name)
82
+ // 读取完整的 session 数据(包含 req res)
83
+ const readSession = async (storage, filename, groupId) => {
84
+ const filePath = groupId ? `${groupId}/${filename}` : filename
85
+ const sessionCache = await storage.readFile(filePath)
86
+ if (sessionCache) {
87
+ return JSON.parse(sessionCache)
88
+ }
89
+ return null
121
90
  }
91
+
92
+ // ========================================
93
+ // URL 和请求处理函数
94
+ // ========================================
95
+
122
96
  // 获取mock文件名
123
97
  const getFilename = (originalReq) => {
124
98
  const { url, ruleValue, pattern } = originalReq
@@ -142,11 +116,12 @@ const getFilename = (originalReq) => {
142
116
  return filename
143
117
  }
144
118
 
145
- const getApiListUpdated = (storage) => {
146
- return !!storage.getProperty('api-list-updated')
119
+ const getApiListUpdated = async (storage) => {
120
+ const value = await storage.getProperty('api-list-updated')
121
+ return !!value
147
122
  }
148
- const setApiListUpdated = (storage, updated) => {
149
- return storage.setProperty('api-list-updated', updated)
123
+ const setApiListUpdated = async (storage, updated) => {
124
+ return await storage.setProperty('api-list-updated', updated)
150
125
  }
151
126
  const getRule = (originalReq) => {
152
127
  const { pattern, ruleValue } = originalReq
@@ -155,102 +130,63 @@ const getRule = (originalReq) => {
155
130
  }
156
131
 
157
132
  const isJsonReq = (headers) => {
158
- return (
133
+ const result = (
159
134
  (headers.accept && headers.accept.includes('application/json')) ||
160
135
  headers['sec-fetch-dest'] === 'empty' ||
161
136
  headers['Sec-Fetch-Dest'] === 'empty'
162
137
  )
163
- }
164
138
 
165
- function getVersionName(name) {
166
- return 'version.' + name
167
- }
168
-
169
- /**
170
- * mock版本管理
171
- */
172
-
173
- // 新增版本
174
- const addNewVersion = ({ storage, filename, versionName, content }) => {
175
- const name = getVersionName(versionName)
176
-
177
- setProperty(storage, filename, { [name]: content })
178
- }
179
- // 更新版本文件内容
180
- const updateVersionContent = ({ storage, filename, versionName, content }) => {
181
- const name = getVersionName(versionName)
182
-
183
- setProperty(storage, filename, { [name]: content })
184
- }
185
- // 获取版本文件内容
186
- const getVersionContent = ({ storage, filename, versionName }) => {
187
- const name = getVersionName(versionName)
188
-
189
- return getPropertyAttr(storage, filename, name)
190
- }
191
- // 删除版本
192
- const deleteVersion = ({ storage, filename, versionName }) => {
193
- const name = getVersionName(versionName)
194
-
195
- removePropertyAttr(storage, filename, name)
196
- }
197
- // 更新版本名称
198
- const updateVersionName = ({ storage, filename, versionName, newVersion }) => {
199
- const file = getVersionContent(versionName)
139
+ // 调试:所有 m.shein.com 的请求都输出日志
140
+ if (headers.host && headers.host === 'm.shein.com') {
141
+ const fullUrl = headers['x-whistle-full-url'] || 'unknown'
142
+ console.log(`[isJsonReq] m.shein.com 请求: ${decodeURIComponent(fullUrl)}`)
143
+ console.log(' - Accept:', headers.accept)
144
+ console.log(' - sec-fetch-dest:', headers['sec-fetch-dest'])
145
+ console.log(' - Sec-Fetch-Dest:', headers['Sec-Fetch-Dest'])
146
+ console.log(' - 判断结果:', result)
147
+ }
200
148
 
201
- addNewVersion(storage, filename, { versionName: newVersion, file })
202
- deleteVersion({ storage, filename, versionName })
149
+ return result
203
150
  }
204
- const getVersions = ({ storage, filename }) => {
205
- const props = getProperty(storage, filename)
206
- const list = []
207
-
208
- Object.keys(props).forEach((key) => {
209
- const matchs = key.match(/^version\.{1}(.+)/)
210
-
211
- if (matchs && matchs[1]) {
212
- list.push({
213
- filename: matchs[1],
214
- content: props[key],
215
- })
216
- }
217
- })
218
151
 
219
- return list
220
- }
152
+ // ========================================
153
+ // 工具函数
154
+ // ========================================
221
155
 
222
156
  const withTryCatch = (fn) => {
223
157
  return async (...args) => {
224
158
  try {
225
159
  return await fn(...args)
226
160
  } catch (e) {
227
- console.error('全局捕获异常:', e)
161
+ // 仅在开发环境输出错误日志
162
+ if (process.env.NODE_ENV === 'development') {
163
+ console.error('[mockbubu] 全局捕获异常:', e)
164
+ }
165
+ throw e
228
166
  }
229
167
  }
230
168
  }
231
169
 
232
- exports.addNewVersion = addNewVersion
233
- exports.updateVersionContent = updateVersionContent
234
- exports.deleteVersion = deleteVersion
235
- exports.updateVersionName = updateVersionName
236
- exports.getVersions = getVersions
237
- exports.setProperty = setProperty
238
- exports.getProperty = getProperty
239
- exports.getPropertyAttr = getPropertyAttr
240
- exports.getVersionContent = getVersionContent
241
- exports.removeProperty = removeProperty
242
- exports.removePropertyAttr = removePropertyAttr
243
- exports.writeFile = writeFile
244
- exports.getApiList = getApiList
170
+ // ========================================
171
+ // 导出 - V3 架构使用的函数
172
+ // ========================================
173
+
174
+ // 文件操作
175
+ exports.getGroupFileList = getGroupFileList
245
176
  exports.updateFile = updateFile
246
177
  exports.readFile = readFile
247
- exports.removeFile = removeFile
178
+ exports.readSession = readSession
179
+
180
+ // URL 和请求处理
248
181
  exports.getFilename = getFilename
249
- exports.getApiListUpdated = getApiListUpdated
250
- exports.setApiListUpdated = setApiListUpdated
251
182
  exports.getRule = getRule
252
183
  exports.isJsonReq = isJsonReq
253
184
 
185
+ // API 列表更新标志
186
+ exports.getApiListUpdated = getApiListUpdated
187
+ exports.setApiListUpdated = setApiListUpdated
188
+
189
+ // 工具函数
254
190
  exports.withTryCatch = withTryCatch
255
191
 
256
192
  exports.handleBuffer2String = withTryCatch(({ body, encoding }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whistle.mockbubu",
3
- "version": "1.0.0-dev.4",
3
+ "version": "2.0.0-beta.0",
4
4
  "description": "mock response data",
5
5
  "scripts": {
6
6
  "lint": "eslint . --ext .js",