fix(npm): correct pre-release version comparison

The isNewerOrEqual function was comparing pre-release strings
lexicographically (e.g., "beta.2" > "beta.10" due to string comparison),
causing incorrect downgrade detection. Now parses pre-release into
id and numeric parts for proper numeric comparison.

Fixes: beta version upgrade being treated as outdated and downgrading to stable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude committed 2026-03-20 08:31:46 +08:00
1 parent 6c82e54157
commit 6e1d6ff44f
2 files changed
+33 -11

No files matched your search

+17 -5
View File
@@ -122,12 +122,18 @@ function extractZip(buffer, destDir, binaryName) {
}
}
// parseVersion splits "1.2.3-beta.1" into { nums: [1,2,3], pre: "beta.1" }
// parseVersion splits "1.2.3-beta.1" into { nums: [1,2,3], preId: "beta", preNum: 1 }
function parseVersion(v) {
v = v.replace(/^v/, "").trim();
const [base, ...rest] = v.split("-");
const nums = base.split(".").map(Number);
return { nums, pre: rest.join("-") };
const preStr = rest.join("-");
// Split pre-release into id and numeric part: "beta.1" -> ["beta", 1], "rc.2" -> ["rc", 2]
const preMatch = preStr.match(/^([a-zA-Z]+)\.?(\d+)?$/);
if (preMatch) {
return { nums, preId: preMatch[1], preNum: preMatch[2] ? parseInt(preMatch[2], 10) : 0 };
}
return { nums, preId: preStr, preNum: 0 };
}
// isNewerOrEqual returns true if installed >= expected
@@ -141,9 +147,15 @@ function isNewerOrEqual(installed, expected) {
if (av > bv) return true;
if (av < bv) return false;
}
if (!a.pre && b.pre) return true;
if (a.pre && !b.pre) return false;
return a.pre >= b.pre;
// Same base: no pre-release >= any pre-release (1.2.3 >= 1.2.3-beta.1)
if (!a.preId && b.preId) return true;
if (a.preId && !b.preId) return false;
// Same pre-release type: compare numerically (beta.2 > beta.1)
if (a.preId === b.preId) {
return a.preNum >= b.preNum;
}
// Different pre-release types: compare lexicographically (rc > beta)
return a.preId >= b.preId;
}
async function main() {
+16 -6
View File
@@ -13,12 +13,18 @@ const binDir = path.join(__dirname, "bin");
const ext = process.platform === "win32" ? ".exe" : "";
const binaryPath = path.join(binDir, NAME + ext);
// parseVersion splits "1.2.3-beta.1" into { nums: [1,2,3], pre: "beta.1" }
// parseVersion splits "1.2.3-beta.1" into { nums: [1,2,3], preId: "beta", preNum: 1 }
function parseVersion(v) {
v = v.replace(/^v/, "").trim();
const [base, ...rest] = v.split("-");
const nums = base.split(".").map(Number);
return { nums, pre: rest.join("-") };
const preStr = rest.join("-");
// Split pre-release into id and numeric part: "beta.1" -> ["beta", 1], "rc.2" -> ["rc", 2]
const preMatch = preStr.match(/^([a-zA-Z]+)\.?(\d+)?$/);
if (preMatch) {
return { nums, preId: preMatch[1], preNum: preMatch[2] ? parseInt(preMatch[2], 10) : 0 };
}
return { nums, preId: preStr, preNum: 0 };
}
// isNewerOrEqual returns true if installed >= expected
@@ -33,10 +39,14 @@ function isNewerOrEqual(installed, expected) {
if (av < bv) return false;
}
// Same base: no pre-release >= any pre-release (1.2.3 >= 1.2.3-beta.1)
if (!a.pre && b.pre) return true;
if (a.pre && !b.pre) return false;
// Both have pre-release or both don't
return a.pre >= b.pre;
if (!a.preId && b.preId) return true;
if (a.preId && !b.preId) return false;
// Same pre-release type: compare numerically (beta.2 > beta.1)
if (a.preId === b.preId) {
return a.preNum >= b.preNum;
}
// Different pre-release types: compare lexicographically (rc > beta)
return a.preId >= b.preId;
}
function needsReinstall() {