import { ParsedVersion } from './types.js';
/**
* Pre-release patterns to exclude (case-insensitive):
* SNAPSHOT, alpha, beta, RC, M (milestone), CR, EA, preview, dev, incubating
*/
const UNSTABLE_PATTERNS = [
/snapshot/i,
/[-._]alpha/i,
/[-._]a\d+/i, // a1, a2, etc.
/[-._]beta/i,
/[-._]b\d+/i, // b1, b2, etc.
/[-._]rc\d*/i, // RC, RC1, RC2, etc.
/[-._]m\d+/i, // M1, M2, etc. (milestone)
/[-._]cr\d*/i, // CR, CR1, etc. (candidate release)
/[-._]ea\d*/i, // EA, EA1, etc. (early access)
/[-._]preview/i,
/[-._]dev/i,
/incubating/i,
];
export function isStableVersion(version: string): boolean {
for (const pattern of UNSTABLE_PATTERNS) {
if (pattern.test(version)) {
return false;
}
}
return true;
}
export function filterStableVersions(versions: string[]): string[] {
return versions.filter(isStableVersion);
}
export function parseVersion(version: string): ParsedVersion {
// Remove common prefixes like 'v' or 'release-'
const cleaned = version.replace(/^(v|release[-_]?)/i, '');
// Split on dots and hyphens
const parts = cleaned.split(/[.\-_]/);
return {
major: parseInt(parts[0], 10) || 0,
minor: parseInt(parts[1], 10) || 0,
patch: parseInt(parts[2], 10) || 0,
qualifier: parts.slice(3).join('.'),
raw: version,
};
}
export function compareVersions(v1: string, v2: string): number {
const parsed1 = parseVersion(v1);
const parsed2 = parseVersion(v2);
if (parsed1.major !== parsed2.major) {
return parsed1.major - parsed2.major;
}
if (parsed1.minor !== parsed2.minor) {
return parsed1.minor - parsed2.minor;
}
if (parsed1.patch !== parsed2.patch) {
return parsed1.patch - parsed2.patch;
}
// For same numeric versions, compare qualifiers lexicographically
return parsed1.qualifier.localeCompare(parsed2.qualifier);
}
export function determineUpgradeType(
current: string,
target: string
): 'major' | 'minor' | 'patch' | 'same' | 'downgrade' {
const comparison = compareVersions(target, current);
if (comparison === 0) {
return 'same';
}
if (comparison < 0) {
return 'downgrade';
}
const parsedCurrent = parseVersion(current);
const parsedTarget = parseVersion(target);
if (parsedTarget.major > parsedCurrent.major) {
return 'major';
}
if (parsedTarget.minor > parsedCurrent.minor) {
return 'minor';
}
return 'patch';
}