openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
66 lines (65 loc) • 2.57 kB
JavaScript
//#region src/infra/semver-compare.ts
/**
* Converts legacy OpenClaw `1.2.3.beta.N` tags into semver-compatible `1.2.3-beta.N`.
*/
function normalizeLegacyDotBetaVersion(version) {
const trimmed = version.trim();
const dotBetaMatch = /^([vV]?[0-9]+\.[0-9]+\.[0-9]+)\.beta(?:\.([0-9A-Za-z.-]+))?$/.exec(trimmed);
if (!dotBetaMatch) return trimmed;
const base = dotBetaMatch[1];
const suffix = dotBetaMatch[2];
return suffix ? `${base}-beta.${suffix}` : `${base}-beta`;
}
/**
* Parses an exact semver-like version into fields used by update and plugin version ordering.
*/
function parseComparableSemver(version, options) {
if (!version) return null;
const normalized = options?.normalizeLegacyDotBeta ? normalizeLegacyDotBetaVersion(version) : version.trim();
const match = /^v?([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(normalized);
if (!match) return null;
const [, major, minor, patch, prereleaseRaw] = match;
if (!major || !minor || !patch) return null;
return {
major: Number.parseInt(major, 10),
minor: Number.parseInt(minor, 10),
patch: Number.parseInt(patch, 10),
prerelease: prereleaseRaw ? prereleaseRaw.split(".").filter(Boolean) : null
};
}
/**
* Compares semver prerelease identifiers using numeric-before-string semver precedence rules.
*/
function comparePrereleaseIdentifiers(a, b) {
if (!a?.length && !b?.length) return 0;
if (!a?.length) return 1;
if (!b?.length) return -1;
const max = Math.max(a.length, b.length);
for (let i = 0; i < max; i += 1) {
const ai = a[i];
const bi = b[i];
if (ai == null && bi == null) return 0;
if (ai == null) return -1;
if (bi == null) return 1;
if (ai === bi) continue;
const aiNumeric = /^[0-9]+$/.test(ai);
const biNumeric = /^[0-9]+$/.test(bi);
if (aiNumeric && biNumeric) return Number.parseInt(ai, 10) < Number.parseInt(bi, 10) ? -1 : 1;
if (aiNumeric && !biNumeric) return -1;
if (!aiNumeric && biNumeric) return 1;
return ai < bi ? -1 : 1;
}
return 0;
}
/**
* Compares parsed semver values, returning null when either side could not be parsed.
*/
function compareComparableSemver(a, b) {
if (!a || !b) return null;
if (a.major !== b.major) return a.major < b.major ? -1 : 1;
if (a.minor !== b.minor) return a.minor < b.minor ? -1 : 1;
if (a.patch !== b.patch) return a.patch < b.patch ? -1 : 1;
return comparePrereleaseIdentifiers(a.prerelease, b.prerelease);
}
//#endregion
export { parseComparableSemver as i, comparePrereleaseIdentifiers as n, normalizeLegacyDotBetaVersion as r, compareComparableSemver as t };