@jonathanleelx/meddle 0.4.0-beta.6 → 0.4.0-beta.8

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.
@@ -69,8 +69,11 @@ async function resolveLatest({ force = false, notify = true, channel = 'stable'
69
69
  if (notify) {
70
70
  if (info.outdated) {
71
71
  output.info(`发现新版本 ${info.latest} (当前 ${info.current})`)
72
- } else {
72
+ } else if (info.latest === info.current) {
73
73
  output.success(`已是最新版本 (${info.current})`)
74
+ } else {
75
+ const channelLabel = info.channel === 'beta' ? 'beta' : '稳定'
76
+ output.info(`当前 ${info.current} 已超过${channelLabel}频道最新版本 ${info.latest}`)
74
77
  }
75
78
  }
76
79
  return info
@@ -265,8 +265,12 @@ function sha256Hex(buffer) {
265
265
  * the existing binary. The previous binary is kept as `<destFile>.bak`. When
266
266
  * the final replace fails the backup is restored and a descriptive error is
267
267
  * thrown (Windows commonly fails with EPERM on a running binary).
268
+ *
269
+ * The payload download is retried (default 3 attempts) because binaries are
270
+ * large (100MB+) and flaky connections drop mid-body.
268
271
  * @param {{ version: string, destFile: string, platform: string, arch: string,
269
272
  * baseUrl?: string, fetchImpl?: Function, timeoutMs?: number,
273
+ * retries?: number, retryDelayMs?: number,
270
274
  * fsImpl?: object }} opts
271
275
  * @returns {Promise<{ installed: string, backup: string, version: string }>}
272
276
  */
@@ -276,6 +280,8 @@ async function downloadBinaryAsset(opts) {
276
280
  const doFetch = opts.fetchImpl || fetch
277
281
  const fsImpl = opts.fsImpl || fs
278
282
  const payloadTimeout = opts.timeoutMs || DEFAULT_DOWNLOAD_TIMEOUT_MS
283
+ const maxRetries = opts.retries === undefined ? 3 : opts.retries
284
+ const retryDelayMs = opts.retryDelayMs === undefined ? 1000 : opts.retryDelayMs
279
285
 
280
286
  const asset = getAssetName({ platform, arch })
281
287
  const assetUrl = `${baseUrl}/v${version}/${asset}`
@@ -283,14 +289,27 @@ async function downloadBinaryAsset(opts) {
283
289
 
284
290
  // The binary is large (100MB+); the checksum sidecar is tiny. Use a long
285
291
  // timeout for the payload and the regular timeout for the metadata fetch.
286
- const [binaryResponse, shaResponse] = await Promise.all([
287
- doFetch(assetUrl, { signal: AbortSignal.timeout(payloadTimeout) }),
288
- doFetch(shaUrl, { signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS) }),
289
- ])
290
- if (!binaryResponse.ok) throw new Error(`download failed (${binaryResponse.status})`)
292
+ const fetchPayload = async () => {
293
+ let lastError = null
294
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
295
+ if (attempt > 0 && retryDelayMs > 0) {
296
+ await new Promise((r) => setTimeout(r, retryDelayMs * attempt))
297
+ }
298
+ try {
299
+ const response = await doFetch(assetUrl, { signal: AbortSignal.timeout(payloadTimeout) })
300
+ if (!response.ok) throw new Error(`download failed (${response.status})`)
301
+ return Buffer.from(await response.arrayBuffer())
302
+ } catch (err) {
303
+ lastError = err
304
+ }
305
+ }
306
+ throw lastError || new Error(`download failed for ${assetUrl}`)
307
+ }
308
+
309
+ const shaResponse = await doFetch(shaUrl, { signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS) })
291
310
  if (!shaResponse.ok) throw new Error(`checksum sidecar missing (${shaResponse.status})`)
292
311
 
293
- const payload = Buffer.from(await binaryResponse.arrayBuffer())
312
+ const payload = await fetchPayload()
294
313
  const shaText = await shaResponse.text()
295
314
  const expected = shaText.trim().split(/\s+/)[0]
296
315
  if (!/^[0-9a-f]{64}$/i.test(expected)) throw new Error(`invalid checksum sidecar`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonathanleelx/meddle",
3
- "version": "0.4.0-beta.6",
3
+ "version": "0.4.0-beta.8",
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",