From a1bb6b791ce922714dda71f90fac18aaae44fa7b Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Tue, 3 Feb 2026 19:07:14 -0800 Subject: [PATCH] feat: no unstable versions --- src/services/npmService.ts | 7 ++++++ test/services/npmService.spec.ts | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/services/npmService.ts b/src/services/npmService.ts index 614b4b9..5780d20 100644 --- a/src/services/npmService.ts +++ b/src/services/npmService.ts @@ -6,6 +6,7 @@ import { Logger } from "@nhcarrigan/logger"; import axios, { isAxiosError, type AxiosInstance } from "axios"; +import semver from "semver"; import { config } from "../config.js"; import type { NpmPackageInfo } from "../types/package.types.js"; @@ -164,6 +165,12 @@ class NpmService { const versions = Object.keys(packageInfo.versions); const matureVersions = versions.filter((version) => { + // Skip prerelease versions (e.g., 1.0.0-rc.1, 1.0.0-alpha, 1.0.0-beta.2) + const prereleaseComponents = semver.prerelease(version); + if (prereleaseComponents !== null && prereleaseComponents.length > 0) { + return false; + } + const publishedAt = packageInfo.time[version]; if (publishedAt === undefined) { return false; diff --git a/test/services/npmService.spec.ts b/test/services/npmService.spec.ts index 3ec9dcc..e5e70c5 100644 --- a/test/services/npmService.spec.ts +++ b/test/services/npmService.spec.ts @@ -429,4 +429,41 @@ describe("npmService", () => { const resultCustom = NpmService.getLatestMatureVersion(packageInfo, 3); expect(resultCustom).toBe("2.0.0"); }); + + it("should skip prerelease versions like rc, alpha, beta", () => { + expect.assertions(1); + const oldDate = getDaysAgoIso(15); + const packageInfo = { + "dist-tags": { latest: "3.0.0-rc.1" }, + "name": "test-package", + "time": { + "2.0.0": oldDate, + "3.0.0-alpha": oldDate, + "3.0.0-beta.1": oldDate, + "3.0.0-rc.1": oldDate, + }, + "versions": { + "2.0.0": { version: "2.0.0" }, + "3.0.0-alpha": { version: "3.0.0-alpha" }, + "3.0.0-beta.1": { version: "3.0.0-beta.1" }, + "3.0.0-rc.1": { version: "3.0.0-rc.1" }, + }, + }; + // Should return 2.0.0 since all 3.0.0 versions are prereleases + const result = NpmService.getLatestMatureVersion(packageInfo); + expect(result).toBe("2.0.0"); + }); + + it("should return null when only prerelease versions exist", () => { + expect.assertions(1); + const oldDate = getDaysAgoIso(15); + const packageInfo = { + "dist-tags": { latest: "1.0.0-rc.1" }, + "name": "test-package", + "time": { "1.0.0-rc.1": oldDate }, + "versions": { "1.0.0-rc.1": { version: "1.0.0-rc.1" } }, + }; + const result = NpmService.getLatestMatureVersion(packageInfo); + expect(result).toBeNull(); + }); });