@jonathanleelx/meddle 0.4.0 → 0.4.1-beta.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.
- package/README.md +1 -1
- package/bin/commands/update.js +2 -1
- package/bin/lib/update-check.js +40 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ irm https://raw.githubusercontent.com/JonathanLee-LX/meddle/main/scripts/install
|
|
|
34
34
|
| Windows x64 | `meddle-windows-x64.exe` |
|
|
35
35
|
| Windows ARM64 | `meddle-windows-arm64.exe` |
|
|
36
36
|
|
|
37
|
-
二进制产物随 [GitHub Release](https://github.com/JonathanLee-LX/meddle/releases) 发布,也可手动下载。安装目录默认 `~/.meddle/bin
|
|
37
|
+
二进制产物随 [GitHub Release](https://github.com/JonathanLee-LX/meddle/releases) 发布,也可手动下载。安装目录默认 `~/.local/bin`(已有 `~/.meddle/bin` 旧安装时原地升级),可通过 `MEDDLE_BIN_DIR` 环境变量自定义。`meddle update` 始终更新到当前运行二进制所在目录。
|
|
38
38
|
|
|
39
39
|
### 命令
|
|
40
40
|
|
package/bin/commands/update.js
CHANGED
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
getAutoUpdate,
|
|
26
26
|
setAutoUpdate,
|
|
27
27
|
inferChannel,
|
|
28
|
+
resolveUpdateBinDir,
|
|
28
29
|
} = require('../lib/update-check')
|
|
29
30
|
|
|
30
31
|
const PACKAGE_NAME = '@jonathanleelx/meddle'
|
|
@@ -103,7 +104,7 @@ async function resolveLatest({ force = false, notify = true, channel = 'stable',
|
|
|
103
104
|
|
|
104
105
|
async function installVersion(version) {
|
|
105
106
|
if (!isNpmInstall()) {
|
|
106
|
-
const binDir =
|
|
107
|
+
const binDir = resolveUpdateBinDir({ home })
|
|
107
108
|
const destFile = path.join(binDir, os.platform() === 'win32' ? 'meddle.exe' : 'meddle')
|
|
108
109
|
output.info(`下载 meddle ${version} (${os.platform()}/${os.arch()})...`)
|
|
109
110
|
let progressShown = false
|
package/bin/lib/update-check.js
CHANGED
|
@@ -25,7 +25,7 @@ const DEFAULT_GITHUB_LATEST_URL = `https://github.com/${REPO}/releases/latest`
|
|
|
25
25
|
const DEFAULT_DOWNLOAD_BASE_URL = `https://github.com/${REPO}/releases/download`
|
|
26
26
|
const DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000
|
|
27
27
|
const DEFAULT_TIMEOUT_MS = 5000
|
|
28
|
-
const DEFAULT_DOWNLOAD_TIMEOUT_MS =
|
|
28
|
+
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 120 * 1000
|
|
29
29
|
|
|
30
30
|
const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/
|
|
31
31
|
|
|
@@ -305,8 +305,10 @@ async function downloadBinaryAsset(opts) {
|
|
|
305
305
|
const assetUrl = `${baseUrl}/v${version}/${asset}`
|
|
306
306
|
const shaUrl = `${assetUrl}.sha256`
|
|
307
307
|
|
|
308
|
-
// The binary is large (100MB+); the checksum sidecar is tiny. Use
|
|
309
|
-
// timeout for the payload
|
|
308
|
+
// The binary is large (100MB+); the checksum sidecar is tiny. Use an IDLE
|
|
309
|
+
// timeout for the payload (reset on every received chunk — a slow-but-steady
|
|
310
|
+
// transfer is never killed by a total wall-time budget) and the regular
|
|
311
|
+
// timeout for the metadata fetch.
|
|
310
312
|
const fetchPayload = async () => {
|
|
311
313
|
let lastError = null
|
|
312
314
|
for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
|
|
@@ -314,8 +316,18 @@ async function downloadBinaryAsset(opts) {
|
|
|
314
316
|
await new Promise((r) => setTimeout(r, retryDelayMs * (attempt - 1)))
|
|
315
317
|
}
|
|
316
318
|
onAttempt(attempt, maxRetries + 1)
|
|
319
|
+
let idleTimer = null
|
|
320
|
+
const controller = new AbortController()
|
|
321
|
+
const resetIdle = () => {
|
|
322
|
+
if (idleTimer) clearTimeout(idleTimer)
|
|
323
|
+
idleTimer = setTimeout(() => {
|
|
324
|
+
controller.abort(new Error('download stalled (idle timeout)'))
|
|
325
|
+
}, payloadTimeout)
|
|
326
|
+
if (typeof idleTimer.unref === 'function') idleTimer.unref()
|
|
327
|
+
}
|
|
328
|
+
resetIdle()
|
|
317
329
|
try {
|
|
318
|
-
const response = await doFetch(assetUrl, { signal:
|
|
330
|
+
const response = await doFetch(assetUrl, { signal: controller.signal })
|
|
319
331
|
if (!response.ok) throw new Error(`download failed (${response.status})`)
|
|
320
332
|
const total = Number(response.headers.get('content-length')) || 0
|
|
321
333
|
const chunks = []
|
|
@@ -323,6 +335,7 @@ async function downloadBinaryAsset(opts) {
|
|
|
323
335
|
if (response.body && typeof response.body.getReader === 'function') {
|
|
324
336
|
const reader = response.body.getReader()
|
|
325
337
|
for (;;) {
|
|
338
|
+
resetIdle()
|
|
326
339
|
const { done, value } = await reader.read()
|
|
327
340
|
if (done) break
|
|
328
341
|
chunks.push(Buffer.from(value))
|
|
@@ -330,13 +343,16 @@ async function downloadBinaryAsset(opts) {
|
|
|
330
343
|
onProgress({ received, total })
|
|
331
344
|
}
|
|
332
345
|
} else {
|
|
346
|
+
resetIdle()
|
|
333
347
|
const buf = Buffer.from(await response.arrayBuffer())
|
|
334
348
|
chunks.push(buf)
|
|
335
349
|
received = buf.length
|
|
336
350
|
onProgress({ received, total })
|
|
337
351
|
}
|
|
352
|
+
if (idleTimer) clearTimeout(idleTimer)
|
|
338
353
|
return Buffer.concat(chunks)
|
|
339
354
|
} catch (err) {
|
|
355
|
+
if (idleTimer) clearTimeout(idleTimer)
|
|
340
356
|
lastError = err
|
|
341
357
|
if (attempt > maxRetries) break
|
|
342
358
|
}
|
|
@@ -433,6 +449,24 @@ function printUpdateNotice(info) {
|
|
|
433
449
|
)
|
|
434
450
|
}
|
|
435
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Resolve where `meddle update` should install the new binary.
|
|
454
|
+
* 1. MEDDLE_BIN_DIR env (explicit override)
|
|
455
|
+
* 2. the directory of the RUNNING binary — in-place updates, so installs on
|
|
456
|
+
* legacy (~/.meddle/bin) or custom paths stay where they are
|
|
457
|
+
* 3. <home>/bin fallback (npm installs / no running binary)
|
|
458
|
+
* @param {{ home: string, execPath?: string, platform?: string }} opts
|
|
459
|
+
* @returns {string}
|
|
460
|
+
*/
|
|
461
|
+
function resolveUpdateBinDir({ home, execPath = process.execPath, platform = os.platform() } = {}) {
|
|
462
|
+
if (process.env.MEDDLE_BIN_DIR) return process.env.MEDDLE_BIN_DIR
|
|
463
|
+
const p = platform === 'win32' ? path.win32 : path
|
|
464
|
+
const exeName = p.basename(execPath).toLowerCase()
|
|
465
|
+
const expected = platform === 'win32' ? 'meddle.exe' : 'meddle'
|
|
466
|
+
if (exeName === expected) return p.dirname(execPath)
|
|
467
|
+
return p.join(home, 'bin')
|
|
468
|
+
}
|
|
469
|
+
|
|
436
470
|
/**
|
|
437
471
|
* Fire-and-forget startup check. Never blocks, never throws, never keeps the
|
|
438
472
|
* process alive. When auto-update is enabled (binary installs) the new binary
|
|
@@ -455,7 +489,7 @@ function isSameBinary(a, b) {
|
|
|
455
489
|
function runAsyncUpdateCheck(opts) {
|
|
456
490
|
const home = opts.home || resolveMeddleHome()
|
|
457
491
|
const installMethod = opts.installMethod || getInstallMethod({ moduleDir: __dirname })
|
|
458
|
-
const binDir = opts.binDir ||
|
|
492
|
+
const binDir = opts.binDir || resolveUpdateBinDir({ home, execPath: opts.execPath || process.execPath })
|
|
459
493
|
const delayMs = opts.delayMs === undefined ? 1500 : opts.delayMs
|
|
460
494
|
const execPath = opts.execPath || process.execPath
|
|
461
495
|
const exeBase = path.basename(execPath).toLowerCase()
|
|
@@ -518,6 +552,7 @@ module.exports = {
|
|
|
518
552
|
downloadBinaryAsset,
|
|
519
553
|
getAutoUpdate,
|
|
520
554
|
setAutoUpdate,
|
|
555
|
+
resolveUpdateBinDir,
|
|
521
556
|
runAsyncUpdateCheck,
|
|
522
557
|
DEFAULT_DOWNLOAD_TIMEOUT_MS,
|
|
523
558
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jonathanleelx/meddle",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1-beta.1",
|
|
4
4
|
"description": "A simple development proxy server with custom routing rules, mock responses, HTTPS interception, and a plugin system",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "index.js",
|