whistle.mockbubu 1.0.0-dev.1

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.
@@ -0,0 +1,99 @@
1
+ const {
2
+ getApiList,
3
+ getProperty,
4
+ } = require('../utils')
5
+ const { RangeFilterMap, RuleFilterMap, LockedFilterMap } = require('../const')
6
+
7
+ // 统一错误响应格式
8
+ const createErrorResponse = (message, code = 500) => ({
9
+ code,
10
+ msg: message || '操作失败',
11
+ })
12
+
13
+ // 统一成功响应格式
14
+ const createSuccessResponse = (data = null, message = '操作成功') => ({
15
+ code: 200,
16
+ msg: message,
17
+ ...(data && { data }),
18
+ })
19
+
20
+ // 执行筛选
21
+ const execFilter = (result, filter) => {
22
+ const { key, value, method } = filter
23
+ const list = result.filter((item) => {
24
+ const type = Object.prototype.toString.call(value)
25
+
26
+ switch (method) {
27
+ case 'indexOf':
28
+ if (type === '[object String]') {
29
+ return ~item[key].indexOf(value)
30
+ }
31
+ break
32
+ case 'equal':
33
+ return item[key] === value
34
+ case 'unequal':
35
+ return item[key] !== value
36
+ }
37
+
38
+ return true
39
+ })
40
+
41
+ return list
42
+ }
43
+
44
+ // 处理筛选条件
45
+ const handleFilterList = (list, filterOptions) => {
46
+ if (!Array.isArray(list) || !filterOptions) return []
47
+
48
+ const filters = []
49
+ const { name, rule, range, ruleValue, locked } = filterOptions
50
+ if (name) {
51
+ filters.push({
52
+ key: 'name',
53
+ value: name.trim(),
54
+ method: 'indexOf',
55
+ })
56
+ }
57
+
58
+ if (rule) {
59
+ filters.push({
60
+ key: 'rule',
61
+ value: rule.trim(),
62
+ method: 'equal',
63
+ })
64
+ }
65
+
66
+ // 范围筛选
67
+ RangeFilterMap[range] && filters.push(RangeFilterMap[range])
68
+ // 规则值筛选
69
+ RuleFilterMap[ruleValue] && filters.push(RuleFilterMap[ruleValue])
70
+ // 锁定筛选
71
+ LockedFilterMap[locked] && filters.push(LockedFilterMap[locked])
72
+
73
+ // 执行筛选
74
+ const filteredList = filters.reduce((result, filter) => {
75
+ return execFilter(result, filter)
76
+ }, list)
77
+
78
+ return filteredList
79
+ }
80
+
81
+ // 获取文件列表(包含文件属性数据)
82
+ const getFullDataList = (localStorage) => {
83
+ // 获取接口列表
84
+ const list = getApiList(localStorage) || []
85
+
86
+ // 合并属性数据
87
+ list.forEach((item) => {
88
+ const props = getProperty(localStorage, item.name) || {}
89
+ delete item.data
90
+ Object.assign(item, props)
91
+ })
92
+ return list
93
+ }
94
+
95
+ exports.createErrorResponse = createErrorResponse
96
+ exports.createSuccessResponse = createSuccessResponse
97
+ exports.execFilter = execFilter
98
+ exports.handleFilterList = handleFilterList
99
+ exports.getFullDataList = getFullDataList
package/lib/utils.js ADDED
@@ -0,0 +1,287 @@
1
+ const zlib = require('zlib')
2
+ 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
+
18
+ const removePropertyAttr = (storage, property, attrName) => {
19
+ if (storage.hasProperty(property)) {
20
+ const oldVal = storage.getProperty(property)
21
+
22
+ if (typeof oldVal === 'object') {
23
+ delete oldVal[attrName]
24
+ storage.setProperty(property, oldVal)
25
+ }
26
+ }
27
+ }
28
+ const getPropertyAttr = (storage, property, attrName) => {
29
+ const props = storage.getProperty(property)
30
+
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
70
+ }
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
+ }
92
+
93
+ const updateFile = (storage, filename, body) => {
94
+ if (!filename) {
95
+ throw new Error('文件名不能为空')
96
+ }
97
+ // storage.writeFile(filename, JSON.stringify(body))
98
+
99
+ const sessionCache = storage.readFile(filename)
100
+ if (sessionCache) {
101
+ const session = JSON.parse(sessionCache)
102
+ session.res.body = JSON.stringify(body)
103
+ storage.writeFile(filename, JSON.stringify(session))
104
+ }
105
+ }
106
+
107
+ const readFile = (storage, filename) => {
108
+ // return storage.readFile(filename)
109
+
110
+ const sessionCache = storage.readFile(filename)
111
+ if (sessionCache) {
112
+ const resCache = JSON.parse(sessionCache)?.res
113
+ console.log(resCache)
114
+ return resCache.body
115
+ }
116
+ }
117
+
118
+ const removeFile = (storage, name) => {
119
+ storage.removeFile(name)
120
+ removeProperty(storage, name)
121
+ }
122
+ // 获取mock文件名
123
+ const getFilename = (originalReq) => {
124
+ const { url, ruleValue, pattern } = originalReq
125
+ const u = new URL(url)
126
+ let filename = url
127
+
128
+ switch (ruleValue) {
129
+ case RuleValueMap.href:
130
+ filename = u.href
131
+ break
132
+ case RuleValueMap.pathname:
133
+ filename = u.origin + u.pathname
134
+ break
135
+ case RuleValueMap.pattern:
136
+ filename = pattern
137
+ break
138
+ default:
139
+ filename = u.origin + u.pathname
140
+ }
141
+
142
+ return filename
143
+ }
144
+
145
+ const getApiListUpdated = (storage) => {
146
+ return !!storage.getProperty('api-list-updated')
147
+ }
148
+ const setApiListUpdated = (storage, updated) => {
149
+ return storage.setProperty('api-list-updated', updated)
150
+ }
151
+ const getRule = (originalReq) => {
152
+ const { pattern, ruleValue } = originalReq
153
+
154
+ return `${pattern} mockbubu://${ruleValue}`
155
+ }
156
+
157
+ const isJsonReq = (headers) => {
158
+ return (
159
+ (headers.accept && headers.accept.includes('application/json')) ||
160
+ headers['sec-fetch-dest'] === 'empty' ||
161
+ headers['Sec-Fetch-Dest'] === 'empty'
162
+ )
163
+ }
164
+
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)
200
+
201
+ addNewVersion(storage, filename, { versionName: newVersion, file })
202
+ deleteVersion({ storage, filename, versionName })
203
+ }
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
+
219
+ return list
220
+ }
221
+
222
+ const withTryCatch = (fn) => {
223
+ return async (...args) => {
224
+ try {
225
+ return await fn(...args)
226
+ } catch (e) {
227
+ console.error('全局捕获异常:', e)
228
+ }
229
+ }
230
+ }
231
+
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
245
+ exports.updateFile = updateFile
246
+ exports.readFile = readFile
247
+ exports.removeFile = removeFile
248
+ exports.getFilename = getFilename
249
+ exports.getApiListUpdated = getApiListUpdated
250
+ exports.setApiListUpdated = setApiListUpdated
251
+ exports.getRule = getRule
252
+ exports.isJsonReq = isJsonReq
253
+
254
+ exports.withTryCatch = withTryCatch
255
+
256
+ exports.handleBuffer2String = withTryCatch(({ body, encoding }) => {
257
+ switch (encoding) {
258
+ case 'gzip':
259
+ body = zlib.gunzipSync(body)
260
+ break
261
+ case 'deflate':
262
+ body = zlib.inflateSync(body)
263
+ break
264
+ case 'br':
265
+ body = zlib.brotliDecompressSync(body)
266
+ break
267
+ default:
268
+ break
269
+ }
270
+
271
+ const type = Object.prototype.toString.call(body)
272
+ let content
273
+ switch (type) {
274
+ case '[object String]':
275
+ content = body
276
+ break
277
+ case '[object Object]':
278
+ content = JSON.stringify(body)
279
+ break
280
+ case '[object Uint8Array]':
281
+ content = body.toString()
282
+ break
283
+ default:
284
+ content = body
285
+ }
286
+ return content
287
+ })
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "whistle.mockbubu",
3
+ "version": "1.0.0-dev.1",
4
+ "description": "mock response data",
5
+ "scripts": {
6
+ "lint": "eslint . --ext .js",
7
+ "lint:fix": "eslint . --ext .js --fix",
8
+ "lint:check": "eslint . --ext .js --max-warnings 0"
9
+ },
10
+ "dependencies": {
11
+ "koa": "^2.7.0",
12
+ "koa-bodyparser": "^4.2.1",
13
+ "koa-onerror": "^4.1.0",
14
+ "koa-router": "^7.4.0",
15
+ "koa-static": "^5.0.0",
16
+ "koa2-cors": "^2.0.6",
17
+ "qs": "^6.10.3",
18
+ "string_decoder": "^1.3.0"
19
+ },
20
+ "files": [
21
+ "lib/*",
22
+ "public/*",
23
+ "rules.txt",
24
+ ".editorconfig",
25
+ ".gitignore",
26
+ "index.js",
27
+ "package.json",
28
+ "README.md",
29
+ "step1.png",
30
+ "step2.png"
31
+ ],
32
+ "keywords": [
33
+ "proxy",
34
+ "mock",
35
+ "whistle"
36
+ ],
37
+ "devDependencies": {
38
+ "eslint": "^8.0.0",
39
+ "eslint-config-standard": "^17.0.0",
40
+ "eslint-plugin-import": "^2.27.0",
41
+ "eslint-plugin-node": "^11.1.0",
42
+ "eslint-plugin-promise": "^6.1.0"
43
+ }
44
+ }