whistle.mockbubu 1.0.0-dev.4 → 2.0.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/.gitignore +4 -0
- package/CHANGELOG_GROUP_FEATURE.md +468 -0
- package/CHANGELOG_P0_FIXES.md +412 -0
- package/CHANGELOG_P1_OPTIMIZATIONS.md +292 -0
- package/CLAUDE.md +436 -0
- package/GROUP_FEATURE_DESIGN.md +520 -0
- package/lib/const.js +19 -0
- package/lib/group-manager.js +491 -0
- package/lib/server.js +173 -33
- package/lib/uiServer/index.js +46 -4
- package/lib/uiServer/router/group-router.js +218 -0
- package/lib/uiServer/router/index.js +1393 -38
- package/lib/uiServer/router/version-router.js +131 -53
- package/lib/uiServer/util.js +64 -10
- package/lib/uiServer/validator.js +105 -0
- package/lib/utils.js +149 -27
- package/package.json +1 -1
- package/public/js/app.js +4541 -747
- package/public/js/app.js.map +1 -1
- package/public/js/chunk-vendors.js +12194 -6869
- package/public/js/chunk-vendors.js.map +1 -1
package/lib/utils.js
CHANGED
|
@@ -28,7 +28,7 @@ const removePropertyAttr = (storage, property, attrName) => {
|
|
|
28
28
|
const getPropertyAttr = (storage, property, attrName) => {
|
|
29
29
|
const props = storage.getProperty(property)
|
|
30
30
|
|
|
31
|
-
return props[attrName]
|
|
31
|
+
return props ? props[attrName] : undefined
|
|
32
32
|
}
|
|
33
33
|
const getProperty = (storage, property) => {
|
|
34
34
|
return storage.getProperty(property)
|
|
@@ -37,7 +37,7 @@ const removeProperty = (storage, property) => {
|
|
|
37
37
|
return storage.removeProperty(property)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const writeFile = ({ storage, filename, body, properties, encoding }) => {
|
|
40
|
+
const writeFile = ({ storage, filename, body, properties, encoding, groupId }) => {
|
|
41
41
|
switch (encoding) {
|
|
42
42
|
case 'gzip':
|
|
43
43
|
body = zlib.gunzipSync(body)
|
|
@@ -68,9 +68,11 @@ const writeFile = ({ storage, filename, body, properties, encoding }) => {
|
|
|
68
68
|
default:
|
|
69
69
|
content = body
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
|
|
72
|
+
// 使用组目录结构: groupId/filename
|
|
73
|
+
const filePath = groupId ? `${groupId}/${filename}` : filename
|
|
74
|
+
storage.writeFile(filePath, content)
|
|
72
75
|
setApiListUpdated(storage, true)
|
|
73
|
-
console.log('%c [ content ]-58', 'font-size:13px; background:pink; color:#bf2c9f;', content)
|
|
74
76
|
// switch (type) {
|
|
75
77
|
// case '[object String]':
|
|
76
78
|
// storage.writeFile(filename, body)
|
|
@@ -86,38 +88,61 @@ const writeFile = ({ storage, filename, body, properties, encoding }) => {
|
|
|
86
88
|
// }
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
// ⚠️ 临时保留:为向后兼容暂时保留(将在 Phase 2 完成后移除)
|
|
89
92
|
const getApiList = (storage) => {
|
|
90
93
|
return storage.getFileList()
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
// ✅ 新增:按组获取文件列表
|
|
97
|
+
const getGroupFileList = (storage, groupId) => {
|
|
98
|
+
const allFiles = storage.getFileList()
|
|
99
|
+
const prefix = `${groupId}/`
|
|
100
|
+
return allFiles
|
|
101
|
+
.filter(item => item.name.startsWith(prefix))
|
|
102
|
+
.map(item => ({
|
|
103
|
+
...item,
|
|
104
|
+
name: item.name.replace(prefix, ''), // 移除前缀,返回原始文件名
|
|
105
|
+
}))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const updateFile = (storage, filename, body, groupId) => {
|
|
94
109
|
if (!filename) {
|
|
95
110
|
throw new Error('文件名不能为空')
|
|
96
111
|
}
|
|
97
|
-
// storage.writeFile(filename, JSON.stringify(body))
|
|
98
112
|
|
|
99
|
-
const
|
|
113
|
+
const filePath = groupId ? `${groupId}/${filename}` : filename
|
|
114
|
+
const sessionCache = storage.readFile(filePath)
|
|
100
115
|
if (sessionCache) {
|
|
101
116
|
const session = JSON.parse(sessionCache)
|
|
102
117
|
session.res.body = JSON.stringify(body)
|
|
103
|
-
storage.writeFile(
|
|
118
|
+
storage.writeFile(filePath, JSON.stringify(session))
|
|
104
119
|
}
|
|
105
120
|
}
|
|
106
121
|
|
|
107
|
-
const readFile = (storage, filename) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const sessionCache = storage.readFile(filename)
|
|
122
|
+
const readFile = (storage, filename, groupId) => {
|
|
123
|
+
const filePath = groupId ? `${groupId}/${filename}` : filename
|
|
124
|
+
const sessionCache = storage.readFile(filePath)
|
|
111
125
|
if (sessionCache) {
|
|
112
126
|
const resCache = JSON.parse(sessionCache)?.res
|
|
113
|
-
console.log(resCache)
|
|
114
127
|
return resCache.body
|
|
115
128
|
}
|
|
116
129
|
}
|
|
117
130
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
131
|
+
// 读取完整的 session 数据(包含 req 和 res)
|
|
132
|
+
const readSession = (storage, filename, groupId) => {
|
|
133
|
+
const filePath = groupId ? `${groupId}/${filename}` : filename
|
|
134
|
+
const sessionCache = storage.readFile(filePath)
|
|
135
|
+
if (sessionCache) {
|
|
136
|
+
return JSON.parse(sessionCache)
|
|
137
|
+
}
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const removeFile = (storage, name, groupId) => {
|
|
142
|
+
const filePath = groupId ? `${groupId}/${name}` : name
|
|
143
|
+
console.log(`[mockbubu] ❌ removeFile 被调用,彻底删除文件: ${filePath}`)
|
|
144
|
+
storage.removeFile(filePath)
|
|
145
|
+
// 注意:不再删除 properties,因为元数据现在由 groupManager 管理
|
|
121
146
|
}
|
|
122
147
|
// 获取mock文件名
|
|
123
148
|
const getFilename = (originalReq) => {
|
|
@@ -166,21 +191,52 @@ function getVersionName(name) {
|
|
|
166
191
|
return 'version.' + name
|
|
167
192
|
}
|
|
168
193
|
|
|
194
|
+
function getVersionMetaName(name) {
|
|
195
|
+
return 'versionMeta.' + name
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const MAX_VERSION_COUNT = 10 // 最多保留10个版本
|
|
199
|
+
|
|
169
200
|
/**
|
|
170
201
|
* mock版本管理
|
|
171
202
|
*/
|
|
172
203
|
|
|
173
204
|
// 新增版本
|
|
174
|
-
const addNewVersion = ({ storage, filename, versionName, content }) => {
|
|
205
|
+
const addNewVersion = ({ storage, filename, versionName, content, description = '' }) => {
|
|
175
206
|
const name = getVersionName(versionName)
|
|
207
|
+
const metaName = getVersionMetaName(versionName)
|
|
208
|
+
|
|
209
|
+
// 检查版本数量限制
|
|
210
|
+
const versions = getVersions({ storage, filename })
|
|
211
|
+
if (versions.length >= MAX_VERSION_COUNT) {
|
|
212
|
+
throw new Error(`版本数量已达上限(${MAX_VERSION_COUNT}个),请删除旧版本后再创建`)
|
|
213
|
+
}
|
|
176
214
|
|
|
177
|
-
|
|
215
|
+
// 保存版本内容和元数据
|
|
216
|
+
const now = Date.now()
|
|
217
|
+
setProperty(storage, filename, {
|
|
218
|
+
[name]: content,
|
|
219
|
+
[metaName]: {
|
|
220
|
+
description,
|
|
221
|
+
createTime: now,
|
|
222
|
+
updateTime: now,
|
|
223
|
+
},
|
|
224
|
+
})
|
|
178
225
|
}
|
|
179
226
|
// 更新版本文件内容
|
|
180
227
|
const updateVersionContent = ({ storage, filename, versionName, content }) => {
|
|
181
228
|
const name = getVersionName(versionName)
|
|
182
|
-
|
|
183
|
-
|
|
229
|
+
const metaName = getVersionMetaName(versionName)
|
|
230
|
+
|
|
231
|
+
// 更新内容和修改时间
|
|
232
|
+
const meta = getPropertyAttr(storage, filename, metaName) || {}
|
|
233
|
+
setProperty(storage, filename, {
|
|
234
|
+
[name]: content,
|
|
235
|
+
[metaName]: {
|
|
236
|
+
...meta,
|
|
237
|
+
updateTime: Date.now(),
|
|
238
|
+
},
|
|
239
|
+
})
|
|
184
240
|
}
|
|
185
241
|
// 获取版本文件内容
|
|
186
242
|
const getVersionContent = ({ storage, filename, versionName }) => {
|
|
@@ -191,32 +247,91 @@ const getVersionContent = ({ storage, filename, versionName }) => {
|
|
|
191
247
|
// 删除版本
|
|
192
248
|
const deleteVersion = ({ storage, filename, versionName }) => {
|
|
193
249
|
const name = getVersionName(versionName)
|
|
250
|
+
const metaName = getVersionMetaName(versionName)
|
|
194
251
|
|
|
195
252
|
removePropertyAttr(storage, filename, name)
|
|
253
|
+
removePropertyAttr(storage, filename, metaName)
|
|
196
254
|
}
|
|
197
255
|
// 更新版本名称
|
|
198
256
|
const updateVersionName = ({ storage, filename, versionName, newVersion }) => {
|
|
199
|
-
const
|
|
257
|
+
const content = getVersionContent({ storage, filename, versionName })
|
|
258
|
+
const oldMetaName = getVersionMetaName(versionName)
|
|
259
|
+
const meta = getPropertyAttr(storage, filename, oldMetaName) || {}
|
|
200
260
|
|
|
201
|
-
|
|
261
|
+
// 删除旧版本
|
|
202
262
|
deleteVersion({ storage, filename, versionName })
|
|
263
|
+
|
|
264
|
+
// 创建新版本(保留元数据,但不增加版本数量限制检查)
|
|
265
|
+
const name = getVersionName(newVersion)
|
|
266
|
+
const metaName = getVersionMetaName(newVersion)
|
|
267
|
+
setProperty(storage, filename, {
|
|
268
|
+
[name]: content,
|
|
269
|
+
[metaName]: {
|
|
270
|
+
...meta,
|
|
271
|
+
updateTime: Date.now(),
|
|
272
|
+
},
|
|
273
|
+
})
|
|
203
274
|
}
|
|
275
|
+
|
|
276
|
+
// 更新版本元信息(名称和描述)
|
|
277
|
+
const updateVersionMeta = ({ storage, filename, versionName, newVersionName, description }) => {
|
|
278
|
+
const content = getVersionContent({ storage, filename, versionName })
|
|
279
|
+
if (!content) {
|
|
280
|
+
throw new Error('版本不存在')
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const oldMetaName = getVersionMetaName(versionName)
|
|
284
|
+
const meta = getPropertyAttr(storage, filename, oldMetaName) || {}
|
|
285
|
+
|
|
286
|
+
// 如果版本名变了,删除旧版本
|
|
287
|
+
if (versionName !== newVersionName) {
|
|
288
|
+
deleteVersion({ storage, filename, versionName })
|
|
289
|
+
} else {
|
|
290
|
+
// 只删除旧的元数据
|
|
291
|
+
removePropertyAttr(storage, filename, oldMetaName)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 保存新的版本和元数据
|
|
295
|
+
const newName = getVersionName(newVersionName)
|
|
296
|
+
const newMetaName = getVersionMetaName(newVersionName)
|
|
297
|
+
setProperty(storage, filename, {
|
|
298
|
+
[newName]: content,
|
|
299
|
+
[newMetaName]: {
|
|
300
|
+
...meta,
|
|
301
|
+
description,
|
|
302
|
+
updateTime: Date.now(),
|
|
303
|
+
},
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
204
307
|
const getVersions = ({ storage, filename }) => {
|
|
205
308
|
const props = getProperty(storage, filename)
|
|
309
|
+
if (!props) {
|
|
310
|
+
return []
|
|
311
|
+
}
|
|
312
|
+
|
|
206
313
|
const list = []
|
|
207
314
|
|
|
208
315
|
Object.keys(props).forEach((key) => {
|
|
209
|
-
const matchs = key.match(/^version\.
|
|
316
|
+
const matchs = key.match(/^version\.(.+)/)
|
|
210
317
|
|
|
211
318
|
if (matchs && matchs[1]) {
|
|
319
|
+
const versionName = matchs[1]
|
|
320
|
+
const metaName = getVersionMetaName(versionName)
|
|
321
|
+
const meta = props[metaName] || {}
|
|
322
|
+
|
|
212
323
|
list.push({
|
|
213
|
-
filename:
|
|
324
|
+
filename: versionName,
|
|
214
325
|
content: props[key],
|
|
326
|
+
description: meta.description || '',
|
|
327
|
+
createTime: meta.createTime || 0,
|
|
328
|
+
updateTime: meta.updateTime || 0,
|
|
215
329
|
})
|
|
216
330
|
}
|
|
217
331
|
})
|
|
218
332
|
|
|
219
|
-
|
|
333
|
+
// 按修改时间倒序排列(最新修改的在前)
|
|
334
|
+
return list.sort((a, b) => b.updateTime - a.updateTime)
|
|
220
335
|
}
|
|
221
336
|
|
|
222
337
|
const withTryCatch = (fn) => {
|
|
@@ -224,7 +339,11 @@ const withTryCatch = (fn) => {
|
|
|
224
339
|
try {
|
|
225
340
|
return await fn(...args)
|
|
226
341
|
} catch (e) {
|
|
227
|
-
|
|
342
|
+
// 仅在开发环境输出错误日志
|
|
343
|
+
if (process.env.NODE_ENV === 'development') {
|
|
344
|
+
console.error('[mockbubu] 全局捕获异常:', e)
|
|
345
|
+
}
|
|
346
|
+
throw e
|
|
228
347
|
}
|
|
229
348
|
}
|
|
230
349
|
}
|
|
@@ -233,6 +352,7 @@ exports.addNewVersion = addNewVersion
|
|
|
233
352
|
exports.updateVersionContent = updateVersionContent
|
|
234
353
|
exports.deleteVersion = deleteVersion
|
|
235
354
|
exports.updateVersionName = updateVersionName
|
|
355
|
+
exports.updateVersionMeta = updateVersionMeta
|
|
236
356
|
exports.getVersions = getVersions
|
|
237
357
|
exports.setProperty = setProperty
|
|
238
358
|
exports.getProperty = getProperty
|
|
@@ -241,9 +361,11 @@ exports.getVersionContent = getVersionContent
|
|
|
241
361
|
exports.removeProperty = removeProperty
|
|
242
362
|
exports.removePropertyAttr = removePropertyAttr
|
|
243
363
|
exports.writeFile = writeFile
|
|
244
|
-
exports.getApiList = getApiList
|
|
364
|
+
exports.getApiList = getApiList // ⚠️ 临时保留用于向后兼容
|
|
365
|
+
exports.getGroupFileList = getGroupFileList // ✅ 新增:按组获取文件列表
|
|
245
366
|
exports.updateFile = updateFile
|
|
246
367
|
exports.readFile = readFile
|
|
368
|
+
exports.readSession = readSession
|
|
247
369
|
exports.removeFile = removeFile
|
|
248
370
|
exports.getFilename = getFilename
|
|
249
371
|
exports.getApiListUpdated = getApiListUpdated
|