Files
2026-05-16 15:35:16 +02:00

73 lines
1.8 KiB
TypeScript

export interface ParsedVersion {
raw: string;
base: string;
parts: readonly number[];
commit: string | null;
}
export type StatusKind =
| 'unknown'
| 'outdated'
| 'ahead'
| 'commit-drift'
| 'current';
export function parseVersion(
raw: string | undefined | null
): ParsedVersion | null {
if (!raw) return null;
const trimmed = raw.trim().replace(/^v/i, '');
if (!trimmed) return null;
const [base, commitPart] = trimmed.split('+', 2);
const parts = (base ?? '')
.split('.')
.map((segment) => Number.parseInt(segment, 10))
.filter((value) => Number.isFinite(value));
if (parts.length === 0) return null;
return {
raw,
base: base ?? '',
parts,
commit: commitPart ?? null,
};
}
export function compareVersionParts(
a: readonly number[],
b: readonly number[]
): number {
const length = Math.max(a.length, b.length);
for (let i = 0; i < length; i += 1) {
const diff = (a[i] ?? 0) - (b[i] ?? 0);
if (diff !== 0) return diff;
}
return 0;
}
export function deriveStatus(
current: ParsedVersion | null,
latest: ParsedVersion | null
): StatusKind {
if (!current || !latest) return 'unknown';
const cmp = compareVersionParts(current.parts, latest.parts);
if (cmp < 0) return 'outdated';
if (cmp > 0) return 'ahead';
return current.commit ? 'commit-drift' : 'current';
}
export function formatReleaseDate(iso: string | undefined): string | null {
if (!iso) return null;
const date = new Date(iso);
if (Number.isNaN(date.getTime())) return null;
return date.toLocaleDateString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric',
});
}
export function formatVersionLabel(version: ParsedVersion | null): string {
if (!version) return 'unknown';
return version.raw.startsWith('v') ? version.raw : `v${version.raw}`;
}