@jonathanleelx/meddle 0.4.0-beta.3 → 0.4.0-beta.4

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.
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Usage:
5
5
  * meddle update check and upgrade to the latest version
6
+ * meddle update --beta check and upgrade to the latest beta version
6
7
  * meddle update --check only check, print the available version
7
8
  * meddle update --version <x.y.z> install a specific version
8
9
  * meddle update --auto on|off enable/disable auto-update (default off)
@@ -45,7 +46,9 @@ meddle update - 检查并升级 meddle
45
46
 
46
47
  用法:
47
48
  meddle update 检查并升级到最新版本
49
+ meddle update --beta 检查并升级到最新的 beta 测试版本
48
50
  meddle update --check 仅检查,提示可用版本 (有新版本时退出码为 2)
51
+ meddle update --check --beta 仅检查 beta 版本
49
52
  meddle update --version <x.y.z> 安装指定版本
50
53
  meddle update --auto on|off 启用/禁用自动更新 (默认关闭)
51
54
  meddle update status 查看安装方式、当前版本和自动更新开关
@@ -53,12 +56,13 @@ meddle update - 检查并升级 meddle
53
56
  `)
54
57
  }
55
58
 
56
- async function resolveLatest({ force = false, notify = true } = {}) {
59
+ async function resolveLatest({ force = false, notify = true, channel = 'stable' } = {}) {
57
60
  const info = await checkForUpdate({
58
61
  home,
59
62
  installMethod,
60
63
  current,
61
64
  force,
65
+ channel,
62
66
  })
63
67
  if (notify) {
64
68
  if (info.outdated) {
@@ -114,6 +118,7 @@ async function run() {
114
118
  const flag = args.find((a) => a === '--check' || a === 'status' || a === '--help' || a === '-h')
115
119
  const versionFlagIndex = args.indexOf('--version')
116
120
  const autoFlagIndex = args.indexOf('--auto')
121
+ const channel = args.includes('--beta') ? 'beta' : 'stable'
117
122
 
118
123
  if (flag === '--help' || flag === '-h') {
119
124
  showHelp()
@@ -121,9 +126,10 @@ async function run() {
121
126
  }
122
127
 
123
128
  if (flag === 'status') {
124
- const info = await resolveLatest({ notify: false })
129
+ const info = await resolveLatest({ notify: false, channel })
125
130
  output.header('Meddle Update Status')
126
131
  output.kv('Install method', installMethod)
132
+ output.kv('Channel', channel)
127
133
  output.kv('Current', info.current)
128
134
  output.kv('Latest', info.latest)
129
135
  output.kv('Auto-update', getAutoUpdate(home) ? 'on' : 'off')
@@ -170,13 +176,13 @@ async function run() {
170
176
  }
171
177
 
172
178
  if (flag === '--check') {
173
- const info = await resolveLatest()
179
+ const info = await resolveLatest({ channel })
174
180
  if (info.outdated) process.exit(2)
175
181
  return
176
182
  }
177
183
 
178
184
  // default: check + upgrade
179
- const info = await resolveLatest()
185
+ const info = await resolveLatest({ channel })
180
186
  if (!info.outdated) return
181
187
  await installVersion(info.latest)
182
188
  }
@@ -20,7 +20,9 @@ const { resolveMeddleHome } = require('./meddle-home')
20
20
  const PACKAGE_NAME = '@jonathanleelx/meddle'
21
21
  const REPO = 'JonathanLee-LX/meddle'
22
22
  const DEFAULT_NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`
23
+ const DEFAULT_NPM_BETA_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/beta`
23
24
  const DEFAULT_GITHUB_LATEST_URL = `https://github.com/${REPO}/releases/latest`
25
+ const DEFAULT_GITHUB_RELEASES_URL = `https://api.github.com/repos/${REPO}/releases`
24
26
  const DEFAULT_DOWNLOAD_BASE_URL = `https://github.com/${REPO}/releases/download`
25
27
  const DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000
26
28
  const DEFAULT_TIMEOUT_MS = 5000
@@ -95,12 +97,14 @@ function compareVersions(a, b) {
95
97
 
96
98
  /**
97
99
  * Fetch the latest version from the npm registry.
98
- * @param {{ fetchImpl?: Function, registryUrl?: string, timeoutMs?: number }} opts
100
+ * @param {{ fetchImpl?: Function, registryUrl?: string, timeoutMs?: number, distTag?: string }} opts
99
101
  * @returns {Promise<string>}
100
102
  */
101
- async function getLatestVersionNpm({ fetchImpl, registryUrl, timeoutMs } = {}) {
103
+ async function getLatestVersionNpm({ fetchImpl, registryUrl, timeoutMs, distTag } = {}) {
102
104
  const doFetch = fetchImpl || fetch
103
- const url = registryUrl || process.env.MEDDLE_NPM_REGISTRY_URL || DEFAULT_NPM_REGISTRY_URL
105
+ const url = registryUrl
106
+ || process.env.MEDDLE_NPM_REGISTRY_URL
107
+ || (distTag === 'beta' ? DEFAULT_NPM_BETA_URL : DEFAULT_NPM_REGISTRY_URL)
104
108
  const response = await doFetch(url, { signal: AbortSignal.timeout(timeoutMs || DEFAULT_TIMEOUT_MS) })
105
109
  if (!response.ok) throw new Error(`npm registry responded ${response.status}`)
106
110
  const data = await response.json()
@@ -110,24 +114,42 @@ async function getLatestVersionNpm({ fetchImpl, registryUrl, timeoutMs } = {}) {
110
114
  }
111
115
 
112
116
  /**
113
- * Fetch the latest version from the GitHub releases/latest redirect.
114
- * @param {{ fetchImpl?: Function, latestUrl?: string, timeoutMs?: number }} opts
117
+ * Fetch the latest version from GitHub. For stable, follow the releases/latest
118
+ * redirect (prereleases excluded). For includePrerelease, list releases via the
119
+ * GitHub API and pick the highest version.
120
+ * @param {{ fetchImpl?: Function, latestUrl?: string, timeoutMs?: number, includePrerelease?: boolean, releasesUrl?: string }} opts
115
121
  * @returns {Promise<string>}
116
122
  */
117
- async function getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs } = {}) {
123
+ async function getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs, includePrerelease = false, releasesUrl } = {}) {
118
124
  const doFetch = fetchImpl || fetch
119
- const url = latestUrl || process.env.MEDDLE_GITHUB_LATEST_URL || DEFAULT_GITHUB_LATEST_URL
125
+ if (!includePrerelease) {
126
+ const url = latestUrl || process.env.MEDDLE_GITHUB_LATEST_URL || DEFAULT_GITHUB_LATEST_URL
127
+ const response = await doFetch(url, {
128
+ redirect: 'manual',
129
+ signal: AbortSignal.timeout(timeoutMs || DEFAULT_TIMEOUT_MS),
130
+ })
131
+ if (response.status < 300 || response.status >= 400) {
132
+ throw new Error(`GitHub latest release did not redirect (${response.status})`)
133
+ }
134
+ const location = response.headers.get('location') || ''
135
+ const match = /\/tag\/(v?[\d][\w.-]*)$/.exec(location)
136
+ if (!match) throw new Error(`Could not resolve latest release tag`)
137
+ return match[1].replace(/^v/, '')
138
+ }
139
+
140
+ const url = releasesUrl || process.env.MEDDLE_GITHUB_RELEASES_URL || DEFAULT_GITHUB_RELEASES_URL
120
141
  const response = await doFetch(url, {
121
- redirect: 'manual',
122
142
  signal: AbortSignal.timeout(timeoutMs || DEFAULT_TIMEOUT_MS),
123
143
  })
124
- if (response.status < 300 || response.status >= 400) {
125
- throw new Error(`GitHub latest release did not redirect (${response.status})`)
126
- }
127
- const location = response.headers.get('location') || ''
128
- const match = /\/tag\/(v?[\d][\w.-]*)$/.exec(location)
129
- if (!match) throw new Error(`Could not resolve latest release tag`)
130
- return match[1].replace(/^v/, '')
144
+ if (!response.ok) throw new Error(`GitHub releases API responded ${response.status}`)
145
+ const data = await response.json()
146
+ const versions = (Array.isArray(data) ? data : [])
147
+ .filter((r) => r && typeof r === 'object' && r.prerelease === true && typeof r.tag_name === 'string')
148
+ .map((r) => r.tag_name.replace(/^v/, ''))
149
+ .filter(isValidVersion)
150
+ if (versions.length === 0) throw new Error(`No prerelease found on GitHub releases`)
151
+ versions.sort((a, b) => compareVersions(b, a))
152
+ return versions[0]
131
153
  }
132
154
 
133
155
  /**
@@ -152,8 +174,9 @@ function updateCachePath(home) {
152
174
  /**
153
175
  * @param {{ home: string, fetchImpl?: Function, installMethod: string, current: string,
154
176
  * now?: number, ttlMs?: number, force?: boolean,
155
- * registryUrl?: string, latestUrl?: string, timeoutMs?: number }} opts
156
- * @returns {Promise<{ current: string, latest: string, outdated: boolean, fromCache: boolean, checkedAt: number }>}
177
+ * registryUrl?: string, latestUrl?: string, timeoutMs?: number,
178
+ * channel?: string }} opts
179
+ * @returns {Promise<{ current: string, latest: string, outdated: boolean, fromCache: boolean, checkedAt: number, channel: string }>}
157
180
  */
158
181
  async function checkForUpdate(opts) {
159
182
  const {
@@ -167,6 +190,7 @@ async function checkForUpdate(opts) {
167
190
  registryUrl,
168
191
  latestUrl,
169
192
  timeoutMs,
193
+ channel = 'stable',
170
194
  } = opts
171
195
 
172
196
  const cacheFile = updateCachePath(home)
@@ -175,7 +199,8 @@ async function checkForUpdate(opts) {
175
199
  cached = JSON.parse(fs.readFileSync(cacheFile, 'utf8'))
176
200
  } catch (_) {}
177
201
 
178
- if (!force && cached && typeof cached.version === 'string' && typeof cached.checkedAt === 'number') {
202
+ if (!force && cached && typeof cached.version === 'string' && typeof cached.checkedAt === 'number'
203
+ && (cached.channel || 'stable') === channel) {
179
204
  if (now - cached.checkedAt < ttlMs) {
180
205
  return {
181
206
  current,
@@ -183,18 +208,19 @@ async function checkForUpdate(opts) {
183
208
  outdated: compareVersions(cached.version, current) > 0,
184
209
  fromCache: true,
185
210
  checkedAt: cached.checkedAt,
211
+ channel,
186
212
  }
187
213
  }
188
214
  }
189
215
 
190
216
  const latest = installMethod === 'binary'
191
- ? await getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs })
192
- : await getLatestVersionNpm({ fetchImpl, registryUrl, timeoutMs })
217
+ ? await getLatestVersionGithub({ fetchImpl, latestUrl, timeoutMs, includePrerelease: channel === 'beta' })
218
+ : await getLatestVersionNpm({ fetchImpl, registryUrl, timeoutMs, distTag: channel })
193
219
 
194
220
  const checkedAt = now
195
221
  try {
196
222
  fs.mkdirSync(path.dirname(cacheFile), { recursive: true })
197
- fs.writeFileSync(cacheFile, JSON.stringify({ version: latest, checkedAt }), 'utf8')
223
+ fs.writeFileSync(cacheFile, JSON.stringify({ version: latest, checkedAt, channel }), 'utf8')
198
224
  } catch (_) {}
199
225
 
200
226
  return {
@@ -203,6 +229,7 @@ async function checkForUpdate(opts) {
203
229
  outdated: compareVersions(latest, current) > 0,
204
230
  fromCache: false,
205
231
  checkedAt,
232
+ channel,
206
233
  }
207
234
  }
208
235
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonathanleelx/meddle",
3
- "version": "0.4.0-beta.3",
3
+ "version": "0.4.0-beta.4",
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",