@jonathanleelx/meddle 0.5.0-beta.1 → 0.5.0-beta.11
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/README.md +2 -0
- package/bin/commands/mock/add.js +25 -3
- package/bin/commands/mock/index.js +8 -4
- package/bin/commands/mock/list.js +1 -0
- package/bin/commands/mock/update.js +23 -4
- package/bin/commands/update.js +4 -0
- package/bin/lib/update-check.js +67 -6
- package/dist/core/h2-pool.d.ts +1 -0
- package/dist/core/h2-pool.d.ts.map +1 -1
- package/dist/core/h2-pool.js +79 -23
- package/dist/core/h2-pool.js.map +1 -1
- package/dist/core/mock-handler.d.ts.map +1 -1
- package/dist/core/mock-handler.js +12 -14
- package/dist/core/mock-handler.js.map +1 -1
- package/dist/core/mock-utils.d.ts +30 -0
- package/dist/core/mock-utils.d.ts.map +1 -0
- package/dist/core/mock-utils.js +117 -0
- package/dist/core/mock-utils.js.map +1 -0
- package/dist/core/plugin-intercept.d.ts.map +1 -1
- package/dist/core/plugin-intercept.js +1 -0
- package/dist/core/plugin-intercept.js.map +1 -1
- package/dist/core/remote-access.d.ts +11 -0
- package/dist/core/remote-access.d.ts.map +1 -1
- package/dist/core/remote-access.js +32 -0
- package/dist/core/remote-access.js.map +1 -1
- package/dist/core/types.d.ts +4 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/helpers.d.ts +8 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +22 -5
- package/dist/helpers.js.map +1 -1
- package/dist/plugins/builtin/mock-plugin.d.ts.map +1 -1
- package/dist/plugins/builtin/mock-plugin.js +6 -5
- package/dist/plugins/builtin/mock-plugin.js.map +1 -1
- package/dist/server/agent/tools.d.ts.map +1 -1
- package/dist/server/agent/tools.js +4 -0
- package/dist/server/agent/tools.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mocks.d.ts.map +1 -1
- package/dist/server/mocks.js +1 -0
- package/dist/server/mocks.js.map +1 -1
- package/dist/server/rule-files.d.ts +44 -0
- package/dist/server/rule-files.d.ts.map +1 -1
- package/dist/server/rule-files.js +93 -0
- package/dist/server/rule-files.js.map +1 -1
- package/index.js +7 -8
- package/mcp-server.js +3 -0
- package/package.json +3 -2
- package/plugins/builtin/mock-plugin.ts +9 -5
- package/web/dist/assets/{checkbox-7NvTvLBs.js → checkbox-C5E3KITf.js} +1 -1
- package/web/dist/assets/{code-fixer-DoYxwBWb.js → code-fixer-Bbz_Rlzx.js} +2 -2
- package/web/dist/assets/detail-panel-SO3wZGmE.js +4 -0
- package/web/dist/assets/{dist-CxdY_A67.js → dist-C8RHq5At.js} +1 -1
- package/web/dist/assets/{dist-B8XUKgXe.js → dist-DoE7uybN.js} +1 -1
- package/web/dist/assets/{es2015-CfXu88Y7.js → es2015-DG46NGN7.js} +1 -1
- package/web/dist/assets/{global-panel-shell-Dq8vMdMI.js → global-panel-shell-czRfHl6X.js} +2 -2
- package/web/dist/assets/health-panel-B1nzvjHe.js +1 -0
- package/web/dist/assets/index-ChDOPlV_.js +18 -0
- package/web/dist/assets/index-bfV7sxx5.css +2 -0
- package/web/dist/assets/mobile-proxy-panel-By90SSuO.js +8 -0
- package/web/dist/assets/mock-config-8d2upIWY.js +1 -0
- package/web/dist/assets/mock-editor-panel-BzWl9IR3.js +1 -0
- package/web/dist/assets/{monaco-editor-CHNSCVe0.js → monaco-editor-BQUxbjIX.js} +1 -1
- package/web/dist/assets/plugin-code-editor-B38R8_va.js +2 -0
- package/web/dist/assets/plugin-config-BLTqZSgU.js +1 -0
- package/web/dist/assets/{plugin-generator-CojssHdE.js → plugin-generator-E5zcpf48.js} +2 -2
- package/web/dist/assets/plugin-test-dialog-BnYV2F3L.js +7 -0
- package/web/dist/assets/{route-preview-BecifYSe.js → route-preview-F0FExfHC.js} +1 -1
- package/web/dist/assets/{rule-ai-assistant-panel-LWDaNQKt.js → rule-ai-assistant-panel-D98DyECu.js} +2 -2
- package/web/dist/assets/rule-config-CKzf2Bdl.js +15 -0
- package/web/dist/assets/rule-overview-panel-8RHOtrPS.js +1 -0
- package/web/dist/assets/{select-C5thwxXQ.js → select-BavfGQkS.js} +1 -1
- package/web/dist/assets/settings-panel-BquHT2tt.js +1 -0
- package/web/dist/assets/{switch-C-Xk56TK.js → switch-Bes_YiJu.js} +1 -1
- package/web/dist/assets/table-2-BR5e3z8m.js +1 -0
- package/web/dist/index.html +10 -10
- package/web/dist/assets/detail-panel-vwvLpmzz.js +0 -4
- package/web/dist/assets/health-panel-CVQcuI1d.js +0 -1
- package/web/dist/assets/index-DJ5BnqEk.js +0 -18
- package/web/dist/assets/index-PcoCj3ST.css +0 -2
- package/web/dist/assets/mobile-proxy-panel-DMJRIDMK.js +0 -8
- package/web/dist/assets/mock-config-BTdHrudB.js +0 -1
- package/web/dist/assets/mock-editor-panel-ztb1w__0.js +0 -1
- package/web/dist/assets/plugin-code-editor-C_h_-lKr.js +0 -2
- package/web/dist/assets/plugin-config-B5tDGCq-.js +0 -1
- package/web/dist/assets/plugin-test-dialog-iWeHVCGW.js +0 -7
- package/web/dist/assets/rule-config-CLte-qNZ.js +0 -15
- package/web/dist/assets/settings-panel-Bx_qz1RQ.js +0 -1
- /package/web/dist/assets/{ai-config-store-DQXVJVn_.js → ai-config-store-C8D_PUGV.js} +0 -0
- /package/web/dist/assets/{alert-Bve02Ufr.js → alert-DCvlZAiv.js} +0 -0
- /package/web/dist/assets/{babel-DsprIpXZ.js → babel-DCr4VA1c.js} +0 -0
- /package/web/dist/assets/{body-diff-view-D7dCWajG.js → body-diff-view-B-4XWmL6.js} +0 -0
- /package/web/dist/assets/{clipboard-Dz6MgwV-.js → clipboard-VhPXNN5m.js} +0 -0
- /package/web/dist/assets/{dist-Cn0y0IRn.js → dist-Blyulj4_.js} +0 -0
- /package/web/dist/assets/{dist-DZJ9SMlJ.js → dist-Bsv9ad5F.js} +0 -0
- /package/web/dist/assets/{dist-Bb9TeEub.js → dist-CEd_wnRC.js} +0 -0
- /package/web/dist/assets/{dist-D5Dj17P8.js → dist-Ce4ujolc.js} +0 -0
- /package/web/dist/assets/{dist-C5nMErq8.js → dist-DWmDaXVN.js} +0 -0
- /package/web/dist/assets/{estree-BP7k0NIt.js → estree-qO_m2Qaw.js} +0 -0
- /package/web/dist/assets/{file-system-access-DK7FI6pN.js → file-system-access-_og-Tg9o.js} +0 -0
- /package/web/dist/assets/{html-6FIWbqxX.js → html-VDGahtyk.js} +0 -0
- /package/web/dist/assets/{postcss-Dnf30fQU.js → postcss-BUm8EKMw.js} +0 -0
- /package/web/dist/assets/{standalone-CjxtLKw-.js → standalone-Bj8SSd8Q.js} +0 -0
- /package/web/dist/assets/{syntax-highlight-BHsSDUha.js → syntax-highlight-Dk0-ewLI.js} +0 -0
- /package/web/dist/assets/{table-DymgLlcc.js → table-nW7uQVcl.js} +0 -0
- /package/web/dist/assets/{textarea-vuxJwU0x.js → textarea-BSI0rnW6.js} +0 -0
- /package/web/dist/assets/{wand-sparkles-wkul5vje.js → wand-sparkles-CJ7fSnom.js} +0 -0
- /package/web/dist/assets/{wifi-D9M35Ajj.js → wifi-CCLKyySQ.js} +0 -0
- /package/web/dist/assets/{zap-Djo4cgux.js → zap-DR-xiUMe.js} +0 -0
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ irm https://raw.githubusercontent.com/JonathanLee-LX/meddle/main/scripts/install
|
|
|
36
36
|
|
|
37
37
|
二进制产物随 [GitHub Release](https://github.com/JonathanLee-LX/meddle/releases) 发布,也可手动下载。安装目录默认 `~/.local/bin`(已有 `~/.meddle/bin` 旧安装时原地升级),可通过 `MEDDLE_BIN_DIR` 环境变量自定义。`meddle update` 始终更新到当前运行二进制所在目录。
|
|
38
38
|
|
|
39
|
+
**私有仓库:** `meddle update`(含 `meddle update --beta`)/ 安装脚本下载 Release 资产时会读取 `GH_TOKEN`(优先)或 `GITHUB_TOKEN`(`repo` 或 fine-grained `contents:read`)。私有仓安装后升级也需要设置 token;未设置时会表现为 404(checksum sidecar missing)。也可手动:`gh release download -R JonathanLee-LX/meddle -p 'meddle-*'`。
|
|
40
|
+
|
|
39
41
|
### 命令
|
|
40
42
|
|
|
41
43
|
```bash
|
package/bin/commands/mock/add.js
CHANGED
|
@@ -12,23 +12,40 @@ const args = process.argv.slice(3)
|
|
|
12
12
|
const jsonFlag = args.includes('--json')
|
|
13
13
|
output.setJsonMode(jsonFlag)
|
|
14
14
|
|
|
15
|
+
function parseCorsFlag(options) {
|
|
16
|
+
if (!options.cors) return
|
|
17
|
+
const corsHeaders = {
|
|
18
|
+
'Access-Control-Allow-Origin': '{origin}',
|
|
19
|
+
'Access-Control-Allow-Credentials': 'true',
|
|
20
|
+
'Access-Control-Allow-Methods': '*',
|
|
21
|
+
'Access-Control-Allow-Headers': '*',
|
|
22
|
+
}
|
|
23
|
+
options.headers = { ...corsHeaders, ...(options.headers || {}) }
|
|
24
|
+
delete options.cors
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
function parseArgs(args) {
|
|
16
28
|
const options = {}
|
|
17
29
|
for (let i = 0; i < args.length; i++) {
|
|
18
30
|
if (args[i] === '--name') options.name = args[++i]
|
|
19
31
|
else if (args[i] === '--pattern') options.urlPattern = args[++i]
|
|
32
|
+
else if (args[i] === '--query') options.query = args[++i]
|
|
20
33
|
else if (args[i] === '--method') options.method = args[++i]
|
|
21
34
|
else if (args[i] === '--status') options.statusCode = parseInt(args[++i], 10)
|
|
22
35
|
else if (args[i] === '--body') options.body = args[++i]
|
|
23
36
|
else if (args[i] === '--delay') options.delay = parseInt(args[++i], 10)
|
|
37
|
+
else if (args[i] === '--cors') options.cors = true
|
|
24
38
|
else if (args[i] === '--headers') {
|
|
39
|
+
const raw = args[++i]
|
|
25
40
|
try {
|
|
26
|
-
options.headers = JSON.parse(
|
|
41
|
+
options.headers = JSON.parse(raw)
|
|
27
42
|
} catch (_) {
|
|
28
|
-
|
|
43
|
+
output.error('Invalid --headers JSON: ' + raw)
|
|
44
|
+
process.exit(1)
|
|
29
45
|
}
|
|
30
46
|
}
|
|
31
47
|
}
|
|
48
|
+
parseCorsFlag(options)
|
|
32
49
|
return options
|
|
33
50
|
}
|
|
34
51
|
|
|
@@ -49,6 +66,7 @@ async function run() {
|
|
|
49
66
|
const rule = {
|
|
50
67
|
name: options.name,
|
|
51
68
|
urlPattern: options.urlPattern,
|
|
69
|
+
query: options.query || '',
|
|
52
70
|
method: options.method || '*',
|
|
53
71
|
statusCode: options.statusCode || 200,
|
|
54
72
|
delay: options.delay || 0,
|
|
@@ -81,12 +99,16 @@ async function run() {
|
|
|
81
99
|
output.header('Mock Rule Added')
|
|
82
100
|
output.success(`#${result.id}: ${result.name}`)
|
|
83
101
|
output.kv('Pattern', result.urlPattern)
|
|
102
|
+
if (result.query) output.kv('Query', result.query)
|
|
84
103
|
output.kv('Method', result.method)
|
|
85
104
|
output.kv('Status', result.statusCode)
|
|
86
105
|
output.kv('Delay', `${result.delay}ms`)
|
|
106
|
+
if (result.headers && Object.keys(result.headers).length > 0) {
|
|
107
|
+
output.kv('Headers', JSON.stringify(result.headers))
|
|
108
|
+
}
|
|
87
109
|
}
|
|
88
110
|
|
|
89
111
|
run().catch(e => {
|
|
90
112
|
output.error(e.message)
|
|
91
113
|
process.exit(1)
|
|
92
|
-
})
|
|
114
|
+
})
|
|
@@ -32,19 +32,23 @@ Mock Commands:
|
|
|
32
32
|
meddle mock enable <id> Enable a mock rule
|
|
33
33
|
meddle mock disable <id> Disable a mock rule
|
|
34
34
|
|
|
35
|
-
Add Options:
|
|
35
|
+
Add / Update Options:
|
|
36
36
|
--name <n> Rule name
|
|
37
|
-
--pattern <p> URL pattern (regex or string)
|
|
37
|
+
--pattern <p> URL pattern (regex or string; may include query)
|
|
38
|
+
--query <q> Query match (exact key=value params, e.g. window_key=A; not prefix)
|
|
38
39
|
--method <m> HTTP method (GET, POST, *, default: *)
|
|
39
40
|
--status <s> Response status code (default: 200)
|
|
40
41
|
--body <b> Response body content
|
|
41
42
|
--delay <d> Response delay in ms (default: 0)
|
|
43
|
+
--headers <json> Response headers JSON (values may use {origin})
|
|
44
|
+
--cors Shortcut: credentials-friendly CORS headers with {origin}
|
|
42
45
|
|
|
43
46
|
Examples:
|
|
44
47
|
meddle mock list
|
|
45
48
|
meddle mock add --name "API Mock" --pattern "example.com/api" --status 200
|
|
46
|
-
meddle mock
|
|
49
|
+
meddle mock add --name "Policy A" --pattern "/ops/.*/policy" --query "window_key=A" --body '{"key":"A"}' --cors
|
|
50
|
+
meddle mock update 1 --status 404 --headers '{"X-Debug":"1"}'
|
|
47
51
|
meddle mock delete 1
|
|
48
52
|
`)
|
|
49
53
|
process.exit(1)
|
|
50
|
-
}
|
|
54
|
+
}
|
|
@@ -48,6 +48,7 @@ async function run() {
|
|
|
48
48
|
output.section('Active Rules')
|
|
49
49
|
active.forEach(rule => {
|
|
50
50
|
output.success(`#${rule.id}: ${rule.name} (${rule.urlPattern})`)
|
|
51
|
+
if (rule.query) output.kv('Query', rule.query, 4)
|
|
51
52
|
output.kv('Status', rule.statusCode, 4)
|
|
52
53
|
output.kv('Method', rule.method || '*', 4)
|
|
53
54
|
output.kv('Delay', `${rule.delay || 0}ms`, 4)
|
|
@@ -5,13 +5,25 @@
|
|
|
5
5
|
const output = require('../../lib/output')
|
|
6
6
|
const { isProxyRunning } = require('../../lib/proxy-detect')
|
|
7
7
|
const { apiPut } = require('../../lib/api-client')
|
|
8
|
-
const { updateMockRule
|
|
8
|
+
const { updateMockRule } = require('../../lib/file-access')
|
|
9
9
|
|
|
10
10
|
// Parse arguments (skip 'mock' and 'update' from argv)
|
|
11
11
|
const args = process.argv.slice(4)
|
|
12
12
|
const jsonFlag = args.includes('--json')
|
|
13
13
|
output.setJsonMode(jsonFlag)
|
|
14
14
|
|
|
15
|
+
function parseCorsFlag(options) {
|
|
16
|
+
if (!options.cors) return
|
|
17
|
+
const corsHeaders = {
|
|
18
|
+
'Access-Control-Allow-Origin': '{origin}',
|
|
19
|
+
'Access-Control-Allow-Credentials': 'true',
|
|
20
|
+
'Access-Control-Allow-Methods': '*',
|
|
21
|
+
'Access-Control-Allow-Headers': '*',
|
|
22
|
+
}
|
|
23
|
+
options.headers = { ...corsHeaders, ...(options.headers || {}) }
|
|
24
|
+
delete options.cors
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
function parseArgs(args) {
|
|
16
28
|
// First positional arg is id
|
|
17
29
|
let id = null
|
|
@@ -20,15 +32,19 @@ function parseArgs(args) {
|
|
|
20
32
|
for (let i = 0; i < args.length; i++) {
|
|
21
33
|
if (args[i] === '--name') options.name = args[++i]
|
|
22
34
|
else if (args[i] === '--pattern') options.urlPattern = args[++i]
|
|
35
|
+
else if (args[i] === '--query') options.query = args[++i]
|
|
23
36
|
else if (args[i] === '--method') options.method = args[++i]
|
|
24
37
|
else if (args[i] === '--status') options.statusCode = parseInt(args[++i], 10)
|
|
25
38
|
else if (args[i] === '--body') options.body = args[++i]
|
|
26
39
|
else if (args[i] === '--delay') options.delay = parseInt(args[++i], 10)
|
|
40
|
+
else if (args[i] === '--cors') options.cors = true
|
|
27
41
|
else if (args[i] === '--headers') {
|
|
42
|
+
const raw = args[++i]
|
|
28
43
|
try {
|
|
29
|
-
options.headers = JSON.parse(
|
|
44
|
+
options.headers = JSON.parse(raw)
|
|
30
45
|
} catch (_) {
|
|
31
|
-
|
|
46
|
+
output.error('Invalid --headers JSON: ' + raw)
|
|
47
|
+
process.exit(1)
|
|
32
48
|
}
|
|
33
49
|
}
|
|
34
50
|
else if (!args[i].startsWith('--') && id === null) {
|
|
@@ -36,6 +52,7 @@ function parseArgs(args) {
|
|
|
36
52
|
}
|
|
37
53
|
}
|
|
38
54
|
|
|
55
|
+
parseCorsFlag(options)
|
|
39
56
|
return { id, options }
|
|
40
57
|
}
|
|
41
58
|
|
|
@@ -83,12 +100,14 @@ async function run() {
|
|
|
83
100
|
output.header('Mock Rule Updated')
|
|
84
101
|
output.success(`#${result.id}: ${result.name}`)
|
|
85
102
|
if (options.urlPattern) output.kv('Pattern', result.urlPattern)
|
|
103
|
+
if (options.query !== undefined) output.kv('Query', result.query || '(none)')
|
|
86
104
|
if (options.method) output.kv('Method', result.method)
|
|
87
105
|
if (options.statusCode) output.kv('Status', result.statusCode)
|
|
88
106
|
if (options.delay) output.kv('Delay', `${result.delay}ms`)
|
|
107
|
+
if (options.headers) output.kv('Headers', JSON.stringify(result.headers || {}))
|
|
89
108
|
}
|
|
90
109
|
|
|
91
110
|
run().catch(e => {
|
|
92
111
|
output.error(e.message)
|
|
93
112
|
process.exit(1)
|
|
94
|
-
})
|
|
113
|
+
})
|
package/bin/commands/update.js
CHANGED
|
@@ -77,6 +77,10 @@ meddle update - 检查并升级 meddle
|
|
|
77
77
|
meddle update --auto on|off 启用/禁用自动更新 (默认关闭)
|
|
78
78
|
meddle update status 查看安装方式、当前版本和自动更新开关
|
|
79
79
|
meddle update --help, -h 显示帮助
|
|
80
|
+
|
|
81
|
+
私有仓库二进制更新需要设置 GH_TOKEN 或 GITHUB_TOKEN(优先 GH_TOKEN;
|
|
82
|
+
权限:repo,或 fine-grained contents:read)。也可:
|
|
83
|
+
gh release download -R JonathanLee-LX/meddle -p 'meddle-*'
|
|
80
84
|
`)
|
|
81
85
|
}
|
|
82
86
|
|
package/bin/lib/update-check.js
CHANGED
|
@@ -27,6 +27,58 @@ const DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000
|
|
|
27
27
|
const DEFAULT_TIMEOUT_MS = 5000
|
|
28
28
|
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 120 * 1000
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a GitHub token for private release downloads / latest redirects.
|
|
32
|
+
* Precedence: explicit argument → GH_TOKEN → GITHUB_TOKEN.
|
|
33
|
+
* @param {string} [explicit]
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
function resolveGithubToken(explicit) {
|
|
37
|
+
if (typeof explicit === 'string' && explicit.trim()) return explicit.trim()
|
|
38
|
+
const gh = process.env.GH_TOKEN
|
|
39
|
+
if (typeof gh === 'string' && gh.trim()) return gh.trim()
|
|
40
|
+
const github = process.env.GITHUB_TOKEN
|
|
41
|
+
if (typeof github === 'string' && github.trim()) return github.trim()
|
|
42
|
+
return ''
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} token
|
|
47
|
+
* @returns {Record<string, string>}
|
|
48
|
+
*/
|
|
49
|
+
function githubAuthHeaders(token) {
|
|
50
|
+
if (!token) return {}
|
|
51
|
+
// GitHub accepts Bearer for PATs / fine-grained tokens on release asset URLs.
|
|
52
|
+
return { Authorization: `Bearer ${token}` }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function privateReleaseAuthHint() {
|
|
56
|
+
return (
|
|
57
|
+
'Private GitHub releases require GH_TOKEN or GITHUB_TOKEN '
|
|
58
|
+
+ '(scope: repo, or fine-grained contents:read). '
|
|
59
|
+
+ `Or: gh release download -R ${REPO} -p 'meddle-*'`
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {'checksum' | 'download'} kind
|
|
65
|
+
* @param {number} status
|
|
66
|
+
* @param {boolean} hadToken
|
|
67
|
+
* @returns {string}
|
|
68
|
+
*/
|
|
69
|
+
function formatDownloadStatusError(kind, status, hadToken) {
|
|
70
|
+
const base = kind === 'checksum'
|
|
71
|
+
? `checksum sidecar missing (${status})`
|
|
72
|
+
: `download failed (${status})`
|
|
73
|
+
if ((status === 404 || status === 401 || status === 403) && !hadToken) {
|
|
74
|
+
return `${base}. ${privateReleaseAuthHint()}`
|
|
75
|
+
}
|
|
76
|
+
if ((status === 401 || status === 403) && hadToken) {
|
|
77
|
+
return `${base}. Token may lack repo/contents:read access to ${REPO}`
|
|
78
|
+
}
|
|
79
|
+
return base
|
|
80
|
+
}
|
|
81
|
+
|
|
30
82
|
const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/
|
|
31
83
|
|
|
32
84
|
/**
|
|
@@ -120,12 +172,14 @@ async function getLatestVersionNpm({ fetchImpl, registryUrl, timeoutMs, distTag
|
|
|
120
172
|
* @param {{ fetchImpl?: Function, latestUrl?: string, timeoutMs?: number }} opts
|
|
121
173
|
* @returns {Promise<string>}
|
|
122
174
|
*/
|
|
123
|
-
async function getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs } = {}) {
|
|
175
|
+
async function getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs, githubToken } = {}) {
|
|
124
176
|
const doFetch = fetchImpl || fetch
|
|
125
177
|
const url = latestUrl || process.env.MEDDLE_GITHUB_LATEST_URL || DEFAULT_GITHUB_LATEST_URL
|
|
178
|
+
const token = resolveGithubToken(githubToken)
|
|
126
179
|
const response = await doFetch(url, {
|
|
127
180
|
redirect: 'manual',
|
|
128
181
|
signal: AbortSignal.timeout(timeoutMs || DEFAULT_TIMEOUT_MS),
|
|
182
|
+
headers: githubAuthHeaders(token),
|
|
129
183
|
})
|
|
130
184
|
if (response.status < 300 || response.status >= 400) {
|
|
131
185
|
throw new Error(`GitHub latest release did not redirect (${response.status})`)
|
|
@@ -287,7 +341,7 @@ function sha256Hex(buffer) {
|
|
|
287
341
|
* baseUrl?: string, fetchImpl?: Function, timeoutMs?: number,
|
|
288
342
|
* retries?: number, retryDelayMs?: number,
|
|
289
343
|
* onProgress?: Function, onAttempt?: Function,
|
|
290
|
-
* fsImpl?: object }} opts
|
|
344
|
+
* githubToken?: string, fsImpl?: object }} opts
|
|
291
345
|
* @returns {Promise<{ installed: string, backup: string, version: string }>}
|
|
292
346
|
*/
|
|
293
347
|
async function downloadBinaryAsset(opts) {
|
|
@@ -300,6 +354,8 @@ async function downloadBinaryAsset(opts) {
|
|
|
300
354
|
const retryDelayMs = opts.retryDelayMs === undefined ? 1000 : opts.retryDelayMs
|
|
301
355
|
const onProgress = typeof opts.onProgress === 'function' ? opts.onProgress : () => {}
|
|
302
356
|
const onAttempt = typeof opts.onAttempt === 'function' ? opts.onAttempt : () => {}
|
|
357
|
+
const token = resolveGithubToken(opts.githubToken)
|
|
358
|
+
const authHeaders = githubAuthHeaders(token)
|
|
303
359
|
|
|
304
360
|
const asset = getAssetName({ platform, arch })
|
|
305
361
|
const assetUrl = `${baseUrl}/v${version}/${asset}`
|
|
@@ -327,8 +383,8 @@ async function downloadBinaryAsset(opts) {
|
|
|
327
383
|
}
|
|
328
384
|
resetIdle()
|
|
329
385
|
try {
|
|
330
|
-
const response = await doFetch(assetUrl, { signal: controller.signal })
|
|
331
|
-
if (!response.ok) throw new Error(
|
|
386
|
+
const response = await doFetch(assetUrl, { signal: controller.signal, headers: authHeaders })
|
|
387
|
+
if (!response.ok) throw new Error(formatDownloadStatusError('download', response.status, Boolean(token)))
|
|
332
388
|
const total = Number(response.headers.get('content-length')) || 0
|
|
333
389
|
const chunks = []
|
|
334
390
|
let received = 0
|
|
@@ -365,8 +421,11 @@ async function downloadBinaryAsset(opts) {
|
|
|
365
421
|
// download — otherwise the sidecar body stream aborts before it is read
|
|
366
422
|
// and the whole install fails with "operation aborted" right after the
|
|
367
423
|
// payload reached 100%.
|
|
368
|
-
const shaResponse = await doFetch(shaUrl, {
|
|
369
|
-
|
|
424
|
+
const shaResponse = await doFetch(shaUrl, {
|
|
425
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
426
|
+
headers: authHeaders,
|
|
427
|
+
})
|
|
428
|
+
if (!shaResponse.ok) throw new Error(formatDownloadStatusError('checksum', shaResponse.status, Boolean(token)))
|
|
370
429
|
const shaText = await shaResponse.text()
|
|
371
430
|
|
|
372
431
|
const payload = await fetchPayload()
|
|
@@ -559,5 +618,7 @@ module.exports = {
|
|
|
559
618
|
setAutoUpdate,
|
|
560
619
|
resolveUpdateBinDir,
|
|
561
620
|
runAsyncUpdateCheck,
|
|
621
|
+
resolveGithubToken,
|
|
622
|
+
githubAuthHeaders,
|
|
562
623
|
DEFAULT_DOWNLOAD_TIMEOUT_MS,
|
|
563
624
|
}
|
package/dist/core/h2-pool.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ import type { ProxyResponse } from './types';
|
|
|
3
3
|
export declare const UPSTREAM_TIMEOUT_CODE = "UPSTREAM_TIMEOUT";
|
|
4
4
|
export declare function cleanHeadersForH2(headers: Record<string, any>): Record<string, any>;
|
|
5
5
|
export declare function __setH2SessionFactoryForTest(factory: ((origin: string, opts: http2.SecureClientSessionOptions) => http2.ClientHttp2Session) | null): void;
|
|
6
|
+
export declare function __setForceHttp1ForTest(value: boolean | null): void;
|
|
6
7
|
export declare function makeProxyRequest(target: string, method: string, headers: Record<string, any>, reqBody: Buffer): Promise<ProxyResponse>;
|
|
7
8
|
//# sourceMappingURL=h2-pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h2-pool.d.ts","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"h2-pool.d.ts","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAyB5C,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AA2BvD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASnF;AAKD,wBAAgB,4BAA4B,CACxC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,0BAA0B,KAAK,KAAK,CAAC,kBAAkB,CAAC,GAAG,IAAI,GACvG,IAAI,CAEN;AAKD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAElE;AAkKD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAU5I"}
|
package/dist/core/h2-pool.js
CHANGED
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.UPSTREAM_TIMEOUT_CODE = void 0;
|
|
40
40
|
exports.cleanHeadersForH2 = cleanHeadersForH2;
|
|
41
41
|
exports.__setH2SessionFactoryForTest = __setH2SessionFactoryForTest;
|
|
42
|
+
exports.__setForceHttp1ForTest = __setForceHttp1ForTest;
|
|
42
43
|
exports.makeProxyRequest = makeProxyRequest;
|
|
43
44
|
const http = __importStar(require("http"));
|
|
44
45
|
const https = __importStar(require("https"));
|
|
@@ -48,10 +49,19 @@ const proxyDebug = (0, debug_1.default)('proxy');
|
|
|
48
49
|
const h2SessionPool = new Map();
|
|
49
50
|
const MAX_H2_SESSIONS = process.env.MEDDLE_MAX_H2_SESSIONS ? parseInt(process.env.MEDDLE_MAX_H2_SESSIONS) : 32;
|
|
50
51
|
const DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS = 60000;
|
|
52
|
+
// Shorter than H1: deno's http2 client can hang on empty-body responses; fallback
|
|
53
|
+
// to H1 should happen before a CORS / browser request looks "stuck" (#72).
|
|
54
|
+
const DEFAULT_H2_REQUEST_TIMEOUT_MS = 8000;
|
|
55
|
+
function parsePositiveMs(raw, fallback) {
|
|
56
|
+
const parsed = raw ? parseInt(raw, 10) : NaN;
|
|
57
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
58
|
+
}
|
|
51
59
|
function upstreamTimeoutMs() {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
return parsePositiveMs(process.env.MEDDLE_UPSTREAM_TIMEOUT_MS, DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS);
|
|
61
|
+
}
|
|
62
|
+
function h2RequestTimeoutMs() {
|
|
63
|
+
const h2 = parsePositiveMs(process.env.MEDDLE_H2_REQUEST_TIMEOUT_MS, DEFAULT_H2_REQUEST_TIMEOUT_MS);
|
|
64
|
+
return Math.min(h2, upstreamTimeoutMs());
|
|
55
65
|
}
|
|
56
66
|
exports.UPSTREAM_TIMEOUT_CODE = 'UPSTREAM_TIMEOUT';
|
|
57
67
|
function upstreamTimeoutError(target, timeoutMs) {
|
|
@@ -99,6 +109,21 @@ let sessionFactoryOverride = null;
|
|
|
99
109
|
function __setH2SessionFactoryForTest(factory) {
|
|
100
110
|
sessionFactoryOverride = factory;
|
|
101
111
|
}
|
|
112
|
+
// Deno's node:http2 client hangs on empty-body responses (204/HEAD/OPTIONS)
|
|
113
|
+
// regardless of method (#72, #74 follow-up). Binary builds must not use it.
|
|
114
|
+
let forceHttp1Override = null;
|
|
115
|
+
function __setForceHttp1ForTest(value) {
|
|
116
|
+
forceHttp1Override = value;
|
|
117
|
+
}
|
|
118
|
+
function isDenoRuntime() {
|
|
119
|
+
return typeof globalThis.Deno !== 'undefined'
|
|
120
|
+
|| typeof process.versions.deno === 'string';
|
|
121
|
+
}
|
|
122
|
+
function shouldForceHttp1() {
|
|
123
|
+
if (forceHttp1Override !== null)
|
|
124
|
+
return forceHttp1Override;
|
|
125
|
+
return isDenoRuntime();
|
|
126
|
+
}
|
|
102
127
|
function createH2Session(origin, opts) {
|
|
103
128
|
return sessionFactoryOverride ? sessionFactoryOverride(origin, opts) : http2.connect(origin, opts);
|
|
104
129
|
}
|
|
@@ -151,6 +176,13 @@ function getOrCreateH2Session(origin, servername) {
|
|
|
151
176
|
});
|
|
152
177
|
});
|
|
153
178
|
}
|
|
179
|
+
/** CORS preflight (OPTIONS) and HEAD typically have empty bodies. Deno's
|
|
180
|
+
* node:http2 client never emits `response`/`end` for those, so they must
|
|
181
|
+
* not go through proxyViaH2 (#72). */
|
|
182
|
+
function shouldSkipHttp2(method) {
|
|
183
|
+
const m = String(method || '').toUpperCase();
|
|
184
|
+
return m === 'OPTIONS' || m === 'HEAD';
|
|
185
|
+
}
|
|
154
186
|
function proxyViaH2(target, method, headers, reqBody) {
|
|
155
187
|
const url = new URL(target);
|
|
156
188
|
const origin = url.origin;
|
|
@@ -159,30 +191,54 @@ function proxyViaH2(target, method, headers, reqBody) {
|
|
|
159
191
|
const servername = (url.hostname !== originalHostname) ? originalHostname : undefined;
|
|
160
192
|
return getOrCreateH2Session(origin, servername).then(session => {
|
|
161
193
|
return new Promise((resolve, reject) => {
|
|
194
|
+
const h2Headers = cleanHeadersForH2(headers);
|
|
195
|
+
h2Headers[':method'] = method;
|
|
196
|
+
h2Headers[':path'] = url.pathname + url.search;
|
|
197
|
+
h2Headers[':authority'] = originalHost;
|
|
198
|
+
h2Headers[':scheme'] = url.protocol.replace(':', '');
|
|
199
|
+
let h2Stream;
|
|
162
200
|
try {
|
|
163
|
-
|
|
164
|
-
h2Headers[':method'] = method;
|
|
165
|
-
h2Headers[':path'] = url.pathname + url.search;
|
|
166
|
-
h2Headers[':authority'] = originalHost;
|
|
167
|
-
h2Headers[':scheme'] = url.protocol.replace(':', '');
|
|
168
|
-
const h2Stream = session.request(h2Headers);
|
|
169
|
-
h2Stream.on('response', (resHeaders) => {
|
|
170
|
-
const statusCode = Number(resHeaders[':status']);
|
|
171
|
-
const clean = {};
|
|
172
|
-
for (const [k, v] of Object.entries(resHeaders)) {
|
|
173
|
-
if (!k.startsWith(':'))
|
|
174
|
-
clean[k] = v;
|
|
175
|
-
}
|
|
176
|
-
resolve({ statusCode, statusMessage: '', headers: clean, stream: h2Stream, protocol: 'h2' });
|
|
177
|
-
});
|
|
178
|
-
h2Stream.on('error', reject);
|
|
179
|
-
if (reqBody && reqBody.length > 0)
|
|
180
|
-
h2Stream.write(reqBody);
|
|
181
|
-
h2Stream.end();
|
|
201
|
+
h2Stream = session.request(h2Headers);
|
|
182
202
|
}
|
|
183
203
|
catch (err) {
|
|
184
204
|
reject(err);
|
|
205
|
+
return;
|
|
185
206
|
}
|
|
207
|
+
const timeoutMs = h2RequestTimeoutMs();
|
|
208
|
+
let settled = false;
|
|
209
|
+
const settle = (fn) => {
|
|
210
|
+
if (settled)
|
|
211
|
+
return;
|
|
212
|
+
settled = true;
|
|
213
|
+
clearTimeout(timer);
|
|
214
|
+
fn();
|
|
215
|
+
};
|
|
216
|
+
const timer = setTimeout(() => {
|
|
217
|
+
settle(() => {
|
|
218
|
+
try {
|
|
219
|
+
h2Stream.close();
|
|
220
|
+
}
|
|
221
|
+
catch (_) { /* ignore */ }
|
|
222
|
+
try {
|
|
223
|
+
h2Stream.destroy();
|
|
224
|
+
}
|
|
225
|
+
catch (_) { /* ignore */ }
|
|
226
|
+
reject(upstreamTimeoutError(target, timeoutMs));
|
|
227
|
+
});
|
|
228
|
+
}, timeoutMs);
|
|
229
|
+
h2Stream.on('response', (resHeaders) => {
|
|
230
|
+
const statusCode = Number(resHeaders[':status']);
|
|
231
|
+
const clean = {};
|
|
232
|
+
for (const [k, v] of Object.entries(resHeaders)) {
|
|
233
|
+
if (!k.startsWith(':'))
|
|
234
|
+
clean[k] = v;
|
|
235
|
+
}
|
|
236
|
+
settle(() => resolve({ statusCode, statusMessage: '', headers: clean, stream: h2Stream, protocol: 'h2' }));
|
|
237
|
+
});
|
|
238
|
+
h2Stream.on('error', (err) => settle(() => reject(err)));
|
|
239
|
+
if (reqBody && reqBody.length > 0)
|
|
240
|
+
h2Stream.write(reqBody);
|
|
241
|
+
h2Stream.end();
|
|
186
242
|
});
|
|
187
243
|
});
|
|
188
244
|
}
|
|
@@ -219,7 +275,7 @@ function proxyViaH1(target, method, headers, reqBody) {
|
|
|
219
275
|
});
|
|
220
276
|
}
|
|
221
277
|
async function makeProxyRequest(target, method, headers, reqBody) {
|
|
222
|
-
if (target.startsWith('https')) {
|
|
278
|
+
if (target.startsWith('https') && !shouldSkipHttp2(method) && !shouldForceHttp1()) {
|
|
223
279
|
try {
|
|
224
280
|
return await proxyViaH2(target, method, headers, reqBody);
|
|
225
281
|
}
|
package/dist/core/h2-pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h2-pool.js","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"h2-pool.js","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-handler.d.ts","sourceRoot":"","sources":["../../core/mock-handler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"mock-handler.d.ts","sourceRoot":"","sources":["../../core/mock-handler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAUvE,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAQ3E;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,WAAW,CAgQhE"}
|
|
@@ -45,6 +45,7 @@ const map_local_1 = require("./map-local");
|
|
|
45
45
|
const proxy_record_1 = require("./proxy-record");
|
|
46
46
|
const client_identity_1 = require("./client-identity");
|
|
47
47
|
const body_utils_1 = require("./body-utils");
|
|
48
|
+
const mock_utils_1 = require("./mock-utils");
|
|
48
49
|
const proxyDebug = (0, debug_1.default)('proxy');
|
|
49
50
|
/** 按 id 去重,后出现的条目覆盖先前的(与前端 use-mocks 行为一致) */
|
|
50
51
|
function dedupeMockRulesById(rules) {
|
|
@@ -117,25 +118,23 @@ function createMockHandler(ctx) {
|
|
|
117
118
|
return false;
|
|
118
119
|
if (rule.method && rule.method !== '*' && rule.method.toUpperCase() !== method.toUpperCase())
|
|
119
120
|
return false;
|
|
120
|
-
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
121
|
+
if (!(0, mock_utils_1.matchUrlPattern)(url, rule.urlPattern))
|
|
122
|
+
return false;
|
|
123
|
+
if (!(0, mock_utils_1.matchQueryCondition)(url, rule.query))
|
|
124
|
+
return false;
|
|
125
|
+
return true;
|
|
126
126
|
}) || null;
|
|
127
127
|
}
|
|
128
|
-
function buildMockResponseForTest(rule) {
|
|
128
|
+
function buildMockResponseForTest(rule, requestHeaders) {
|
|
129
129
|
const statusCode = rule.statusCode || 200;
|
|
130
130
|
const ruleHeaders = {};
|
|
131
131
|
for (const [key, value] of Object.entries(rule.headers || {})) {
|
|
132
132
|
ruleHeaders[key.toLowerCase()] = value;
|
|
133
133
|
}
|
|
134
|
-
const headers = {
|
|
134
|
+
const headers = (0, mock_utils_1.resolveHeaderPlaceholders)((0, mock_utils_1.mergeResponseHeaders)({
|
|
135
135
|
'x-mock-rule': encodeURIComponent(rule.name || rule.id?.toString() || rule.urlPattern || ''),
|
|
136
|
-
'content-type':
|
|
137
|
-
|
|
138
|
-
};
|
|
136
|
+
'content-type': 'application/json',
|
|
137
|
+
}, ruleHeaders), requestHeaders);
|
|
139
138
|
let body = '';
|
|
140
139
|
if (rule.bodyType === 'file' && rule.body) {
|
|
141
140
|
let filePath = rule.body.replace(/^file:\/\//, '').replace(/^\/[A-Za-z]:\//, '');
|
|
@@ -171,13 +170,12 @@ function createMockHandler(ctx) {
|
|
|
171
170
|
for (const [key, value] of Object.entries(rule.headers || {})) {
|
|
172
171
|
ruleHeaders[key.toLowerCase()] = value;
|
|
173
172
|
}
|
|
174
|
-
const responseHeaders = {
|
|
173
|
+
const responseHeaders = (0, mock_utils_1.resolveHeaderPlaceholders)((0, mock_utils_1.mergeResponseHeaders)({
|
|
175
174
|
'X-Mock-Rule': encodeURIComponent(mockRuleName),
|
|
176
175
|
'Access-Control-Allow-Origin': '*',
|
|
177
176
|
'Access-Control-Allow-Methods': '*',
|
|
178
177
|
'Access-Control-Allow-Headers': '*',
|
|
179
|
-
|
|
180
|
-
};
|
|
178
|
+
}, ruleHeaders), req.headers);
|
|
181
179
|
let finalStatusCode = statusCode;
|
|
182
180
|
let statusMessage = 'OK (Mock)';
|
|
183
181
|
if (isFileBody) {
|