diff --git a/src/utils/versioning/version.ts b/src/utils/versioning/version.ts
index 0fdb07a..0349791 100644
--- a/src/utils/versioning/version.ts
+++ b/src/utils/versioning/version.ts
@@ -1,31 +1,165 @@
-export default class Version {
-    public readonly major: number;
-    public readonly minor: number;
-    public readonly build: number;
+import { SemVer, coerce, parse as parseSemVer } from "semver";
 
-    public constructor(major: number, minor: number, build: number);
+/**
+ * Represents a version number, which is a set of three non-negative integers: major, minor, and patch.
+ *
+ * This interface provides methods to compare versions and format them into a string representation.
+ */
+export interface Version {
+    /**
+     * The major version number.
+     */
+    get major(): number;
 
-    public constructor(version: string);
+    /**
+     * The minor version number.
+     */
+    get minor(): number;
 
-    public constructor(major: number | string, minor?: number, build?: number) {
-        if (typeof major === "string") {
-            [this.major, this.minor, this.build] = major.split(".").map(x => isNaN(+x) ? 0 : +x).concat(0, 0);
-        } else {
-            this.major = major || 0;
-            this.minor = minor || 0;
-            this.build = build || 0;
-        }
+    /**
+     * The patch version number.
+     */
+    get patch(): number;
+
+    /**
+     * Compares the current version to another one.
+     *
+     * @param other - The version to compare with.
+     *
+     * @returns A number indicating the comparison result:
+     *
+     * - 0 if both versions are equal.
+     * - A positive number if the current version is greater.
+     * - A negative number if the other version is greater.
+     */
+    compare(other?: string | Version): number;
+
+    /**
+     * Formats the version into a string representation.
+     *
+     * @returns The string representation of the version.
+     */
+    format(): string;
+
+    /**
+     * Returns the original string representation of the version.
+     *
+     * @returns The original string representation of the version.
+     */
+    toString(): string;
+}
+
+/**
+ * Parses a version string into a {@link Version} instance.
+ *
+ * @param version - The version string to parse.
+ *
+ * @returns A {@link Version} instance if parsing is successful, or `undefined` if it fails.
+ */
+export function parseVersion(version: string): Version | undefined {
+    return SemVerVersion.parse(version);
+}
+
+/**
+ * Regular expression for matching semver-like tags in version strings.
+ */
+const SEMVER_TAG_REGEX = /[a-z]{0,2}((\d+\.\d+)(\.\d+)?(.*))/i;
+
+/**
+ * Represents a version number compliant with the Semantic Versioning specification.
+ */
+class SemVerVersion implements Version {
+    /**
+     * The SemVer object representing the parsed semantic version.
+     */
+    private readonly _semver: SemVer;
+
+    /**
+     * The original string representation of the version.
+     */
+    private readonly _version: string;
+
+    /**
+     * Constructs a new {@link SemVerVersion} instance.
+     *
+     * @param semver - The SemVer object representing the parsed semantic version.
+     * @param version - The original string representation of the version.
+     */
+    constructor(semver: SemVer, version?: string) {
+        this._semver = semver;
+        this._version = version ?? semver.format();
     }
 
-    public equals(version: unknown): boolean {
-        if (version instanceof Version) {
-            return this.major === version.major && this.minor === version.minor && this.build === version.build;
+    /**
+     * Parses a version string into a {@link SemVerVersion} instance.
+     *
+     * @param version - The version string to parse.
+     *
+     * @returns A {@link SemVerVersion} instance if parsing is successful, or `undefined` if it fails.
+     */
+    static parse(version: string): SemVerVersion | undefined {
+        const semver = parseSemVer(version);
+        if (semver) {
+            return new SemVerVersion(semver, version);
         }
-        return typeof version === "string" && this.equals(new Version(version));
+
+        const match = version.match(SEMVER_TAG_REGEX);
+        if (match) {
+            const numericVersion = match[3] ? match[1] : `${match[2]}.0${match[4]}`;
+            const parsedSemVer = parseSemVer(numericVersion) || coerce(numericVersion);
+            return new SemVerVersion(parsedSemVer, match[0]);
+        }
+
+        return undefined;
     }
 
-    public static fromName(name: string): string {
-        const match = name.match(/[a-z]{0,2}\d+\.\d+.*/i);
-        return match ? match[0] : name;
+    /**
+     * @inheritdoc
+     */
+    get major(): number {
+        return this._semver.major;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    get minor(): number {
+        return this._semver.minor;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    get patch(): number {
+        return this._semver.patch;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    compare(other?: string | Version): number {
+        if (other === null || other === undefined) {
+            return 1;
+        }
+
+        if (typeof other === "string") {
+            other = SemVerVersion.parse(other);
+        }
+
+        return other instanceof SemVerVersion ? this._semver.compare(other._semver) : -other.compare(this);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    format(): string {
+        return this._semver.format();
+    }
+
+    /**
+     * @inheritdoc
+     */
+    toString(): string {
+        return this._version;
     }
 }
diff --git a/tests/unit/utils/versioning/version.spec.ts b/tests/unit/utils/versioning/version.spec.ts
new file mode 100644
index 0000000..1c5a6c5
--- /dev/null
+++ b/tests/unit/utils/versioning/version.spec.ts
@@ -0,0 +1,81 @@
+import { parseVersion } from "@/utils/versioning/version";
+
+describe("parseVersion", () => {
+    test("returns undefined when parsing invalid string", () => {
+        const version = parseVersion("abc");
+
+        expect(version).toBeUndefined();
+    });
+
+    test("parses classic semver format (major.minor.patch)", () => {
+        const version = parseVersion("1.2.3");
+
+        expect(version).toMatchObject({ major: 1, minor: 2, patch: 3 });
+    });
+
+    test("parses classic semver format with pre-release information", () => {
+        const version = parseVersion("1.2.3-alpha.1");
+
+        expect(version).toMatchObject({ major: 1, minor: 2, patch: 3 });
+        expect(version.toString()).toBe("1.2.3-alpha.1");
+    });
+
+    test("parses version strings with missing patch number (major.minor)", () => {
+        const version = parseVersion("1.2");
+
+        expect(version).toMatchObject({ major: 1, minor: 2, patch: 0 });
+        expect(version.format()).toBe("1.2.0");
+        expect(version.toString()).toBe("1.2");
+    });
+
+    test("parses version strings with missing patch number and pre-release information", () => {
+        const version = parseVersion("1.2-alpha.1");
+
+        expect(version).toMatchObject({ major: 1, minor: 2, patch: 0 });
+        expect(version.format()).toBe("1.2.0-alpha.1");
+        expect(version.toString()).toBe("1.2-alpha.1");
+    });
+
+    test("file version is correctly extracted from the filename", () => {
+        expect(String(parseVersion("sodium-fabric-mc1.17.1-0.3.2+build.7"))).toBe("mc1.17.1-0.3.2+build.7");
+        expect(String(parseVersion("fabric-api-0.40.1+1.18_experimental"))).toBe("0.40.1+1.18_experimental");
+        expect(String(parseVersion("TechReborn-5.0.8-beta+build.111"))).toBe("5.0.8-beta+build.111");
+        expect(String(parseVersion("TechReborn-1.17-5.0.1-beta+build.29"))).toBe("1.17-5.0.1-beta+build.29");
+        expect(String(parseVersion("Terra-forge-5.3.3-BETA+ec3b0e5d"))).toBe("5.3.3-BETA+ec3b0e5d");
+        expect(String(parseVersion("modmenu-2.0.12"))).toBe("2.0.12");
+        expect(String(parseVersion("enhancedblockentities-0.5+1.17"))).toBe("0.5+1.17");
+        expect(String(parseVersion("sync-mc1.17.x-1.2"))).toBe("mc1.17.x-1.2");
+    });
+});
+
+describe("Version", () => {
+    test("contains valid major, minor, and patch numbers", () => {
+        expect(parseVersion("1.2.3")).toMatchObject({ major: 1, minor: 2, patch: 3 });
+        expect(parseVersion("1.2.3-alpha.1")).toMatchObject({ major: 1, minor: 2, patch: 3 });
+        expect(parseVersion("1.2")).toMatchObject({ major: 1, minor: 2, patch: 0 });
+        expect(parseVersion("1.2-alpha.1")).toMatchObject({ major: 1, minor: 2, patch: 0 });
+    });
+
+    test("compares versions correctly", () => {
+        const version1 = parseVersion("1.2.3");
+        const version2 = parseVersion("2.3.4");
+
+        expect(version1.compare(version2)).toBeLessThan(0);
+        expect(version2.compare(version1)).toBeGreaterThan(0);
+        expect(version1.compare(version1)).toBe(0);
+    });
+
+    test("formats correctly", () => {
+        expect(parseVersion("1.0.0").format()).toEqual("1.0.0");
+        expect(parseVersion("1.0.0-alpha.1").format()).toEqual("1.0.0-alpha.1");
+        expect(parseVersion("1.0").format()).toEqual("1.0.0");
+        expect(parseVersion("1.0-alpha.1").format()).toEqual("1.0.0-alpha.1");
+    });
+
+    test("toString returns the original string representation", () => {
+        expect(parseVersion("1.0.0").toString()).toEqual("1.0.0");
+        expect(parseVersion("1.0").toString()).toEqual("1.0");
+        expect(parseVersion("1.0.0-alpha.1").toString()).toEqual("1.0.0-alpha.1");
+        expect(parseVersion("1.0-alpha.1").toString()).toEqual("1.0-alpha.1");
+    });
+});