Checkpoint before follow-up message

This commit is contained in:
Cursor Agent
2025-06-23 04:19:15 +00:00
parent 33fb280ac2
commit 9bb0ee6bb1
4 changed files with 583 additions and 16 deletions

View File

@@ -783,7 +783,6 @@ declare module "bun" {
path?: string | undefined;
syscall?: string | undefined;
}
/**
* Concatenate an array of typed arrays into a single `ArrayBuffer`. This is a fast path.
*
@@ -1408,7 +1407,6 @@ declare module "bun" {
* @param sql Function to execute SQL queries within the savepoint
*/
type SQLSavepointContextCallback = (sql: SavepointSQL) => Promise<any> | Array<SQLQuery>;
/**
* Main SQL client interface providing connection and transaction management
*/
@@ -1961,8 +1959,7 @@ declare module "bun" {
* ... on User {
* id
* }
* }
* }`;
* }`;
* ```
*
* Will be replaced with:
@@ -2118,7 +2115,6 @@ declare module "bun" {
path: string;
kind: ImportKind;
}
/**
* @see [Bun.build API docs](https://bun.sh/docs/bundler#api)
*/
@@ -2842,7 +2838,6 @@ declare module "bun" {
* @link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
*/
type WebSocketReadyState = 0 | 1 | 2 | 3;
/**
* A fast WebSocket designed for servers.
*
@@ -3643,7 +3638,6 @@ declare module "bun" {
errno?: number;
syscall?: string;
}
/**
* Options for TLS connections
*/
@@ -4426,7 +4420,6 @@ declare module "bun" {
* This can be 3.5x faster than `new Uint8Array(size)`, but if you send uninitialized memory to your users (even unintentionally), it can potentially leak anything recently in memory.
*/
function allocUnsafe(size: number): Uint8Array;
/**
* Options for `Bun.inspect`
*/
@@ -4852,6 +4845,51 @@ declare module "bun" {
* Throws an error if either version is invalid.
*/
function order(v1: StringLike, v2: StringLike): -1 | 0 | 1;
/**
* Returns the major version number, or null if the version is invalid.
*/
function major(version: StringLike): number | null;
/**
* Returns the minor version number, or null if the version is invalid.
*/
function minor(version: StringLike): number | null;
/**
* Returns the patch version number, or null if the version is invalid.
*/
function patch(version: StringLike): number | null;
/**
* Returns an array of prerelease components, or null if the version doesn't have a prerelease or is invalid.
* Numeric components are parsed as numbers.
*/
function prerelease(version: StringLike): (string | number)[] | null;
/**
* Parses a version string into an object with its components.
* Returns null if the version is invalid.
*/
function parse(version: StringLike): {
major: number;
minor: number;
patch: number;
prerelease: (string | number)[] | null;
build: (string | number)[] | null;
version: string;
raw: string;
} | null;
/**
* Increments the version by the release type.
* Returns the new version string, or null if the version is invalid.
*
* @param version The version to increment
* @param releaseType The type of release: "major" | "premajor" | "minor" | "preminor" | "patch" | "prepatch" | "prerelease" | "release"
* @param identifier Optional identifier for pre-releases (e.g., "alpha", "beta")
*/
function bump(version: StringLike, releaseType: "major" | "premajor" | "minor" | "preminor" | "patch" | "prepatch" | "prerelease" | "release", identifier?: string): string | null;
}
namespace unsafe {
@@ -5166,7 +5204,6 @@ declare module "bun" {
*/
static readonly algorithms: SupportedCryptoAlgorithms[];
}
/**
* Resolve a `Promise` after milliseconds. This is like
* {@link setTimeout} except it returns a `Promise`.
@@ -5211,7 +5248,6 @@ declare module "bun" {
* Internally, it calls [nanosleep(2)](https://man7.org/linux/man-pages/man2/nanosleep.2.html)
*/
function sleepSync(ms: number): void;
/**
* Hash `input` using [SHA-2 512/256](https://en.wikipedia.org/wiki/SHA-2#Comparison_of_SHA_functions)
*
@@ -5872,7 +5908,6 @@ declare module "bun" {
interface HTMLBundle {
index: string;
}
/**
* Represents a TCP or TLS socket connection used for network communication.
* This interface provides methods for reading, writing, managing the connection state,
@@ -6614,7 +6649,6 @@ declare module "bun" {
* @category HTTP & Networking
*/
function listen<Data = undefined>(options: UnixSocketOptions<Data>): UnixSocketListener<Data>;
/**
* @category HTTP & Networking
*/
@@ -8031,4 +8065,4 @@ declare module "bun" {
*/
[Symbol.iterator](): IterableIterator<[string, string]>;
}
}
}

View File

@@ -1,5 +1,5 @@
pub fn create(globalThis: *JSC.JSGlobalObject) JSC.JSValue {
const object = JSC.JSValue.createEmptyObject(globalThis, 2);
const object = JSC.JSValue.createEmptyObject(globalThis, 8);
object.put(
globalThis,
@@ -25,6 +25,78 @@ pub fn create(globalThis: *JSC.JSGlobalObject) JSC.JSValue {
),
);
object.put(
globalThis,
JSC.ZigString.static("major"),
JSC.host_fn.NewFunction(
globalThis,
JSC.ZigString.static("major"),
1,
SemverObject.major,
false,
),
);
object.put(
globalThis,
JSC.ZigString.static("minor"),
JSC.host_fn.NewFunction(
globalThis,
JSC.ZigString.static("minor"),
1,
SemverObject.minor,
false,
),
);
object.put(
globalThis,
JSC.ZigString.static("patch"),
JSC.host_fn.NewFunction(
globalThis,
JSC.ZigString.static("patch"),
1,
SemverObject.patch,
false,
),
);
object.put(
globalThis,
JSC.ZigString.static("parse"),
JSC.host_fn.NewFunction(
globalThis,
JSC.ZigString.static("parse"),
1,
SemverObject.parse,
false,
),
);
object.put(
globalThis,
JSC.ZigString.static("prerelease"),
JSC.host_fn.NewFunction(
globalThis,
JSC.ZigString.static("prerelease"),
1,
SemverObject.prerelease,
false,
),
);
object.put(
globalThis,
JSC.ZigString.static("bump"),
JSC.host_fn.NewFunction(
globalThis,
JSC.ZigString.static("bump"),
3,
SemverObject.bump,
false,
),
);
return object;
}
@@ -125,6 +197,156 @@ pub fn satisfies(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun
return JSC.jsBoolean(right_group.satisfies(left_version, right.slice(), left.slice()));
}
// Add a helper to reduce boilerplate for major, minor, patch
fn getVersionComponent(
globalThis: *JSC.JSGlobalObject,
callFrame: *JSC.CallFrame,
comptime component: enum { major, minor, patch },
) bun.JSError!JSC.JSValue {
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
var stack_fallback = std.heap.stackFallback(512, arena.allocator());
const allocator = stack_fallback.get();
const arguments = callFrame.arguments_old(1).slice();
if (arguments.len < 1) {
return JSC.JSValue.null;
}
const arg_string = try arguments[0].toJSString(globalThis);
const version_slice = arg_string.toSlice(globalThis, allocator);
defer version_slice.deinit();
if (!strings.isAllASCII(version_slice.slice())) return JSC.JSValue.null;
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
if (!parse_result.valid) {
return JSC.JSValue.null;
}
const version = parse_result.version;
return switch (component) {
.major => JSC.jsNumber(version.major orelse 0),
.minor => JSC.jsNumber(version.minor orelse 0),
.patch => JSC.jsNumber(version.patch orelse 0),
};
}
pub fn major(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
return getVersionComponent(globalThis, callFrame, .major);
}
pub fn minor(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
return getVersionComponent(globalThis, callFrame, .minor);
}
pub fn patch(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
return getVersionComponent(globalThis, callFrame, .patch);
}
pub fn prerelease(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
var stack_fallback = std.heap.stackFallback(1024, arena.allocator());
const allocator = stack_fallback.get();
const arguments = callFrame.arguments_old(1).slice();
if (arguments.len < 1) return JSC.JSValue.null;
const arg_string = try arguments[0].toJSString(globalThis);
const version_slice = arg_string.toSlice(globalThis, allocator);
defer version_slice.deinit();
if (!strings.isAllASCII(version_slice.slice())) return JSC.JSValue.null;
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
if (!parse_result.valid or !parse_result.version.min().tag.hasPre()) {
return JSC.JSValue.null;
}
return parse_result.version.min().tag.toComponentsArray(true, globalThis, allocator, version_slice.slice());
}
pub fn parse(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
var stack_fallback = std.heap.stackFallback(1024, arena.allocator());
const allocator = stack_fallback.get();
const arguments = callFrame.arguments_old(1).slice();
if (arguments.len < 1) return JSC.JSValue.null;
const arg_string = try arguments[0].toJSString(globalThis);
const version_slice = arg_string.toSlice(globalThis, allocator);
defer version_slice.deinit();
if (!strings.isAllASCII(version_slice.slice())) return JSC.JSValue.null;
const sliced_string = SlicedString.init(version_slice.slice(), version_slice.slice());
const parse_result = Version.parse(sliced_string);
if (!parse_result.valid) {
return JSC.JSValue.null;
}
const version = parse_result.version.min();
const obj = JSC.JSValue.createEmptyObject(globalThis, 6);
obj.put(globalThis, JSC.ZigString.static("major"), JSC.jsNumber(version.major));
obj.put(globalThis, JSC.ZigString.static("minor"), JSC.jsNumber(version.minor));
obj.put(globalThis, JSC.ZigString.static("patch"), JSC.jsNumber(version.patch));
const pre_array = try version.tag.toComponentsArray(true, globalThis, allocator, version_slice.slice());
obj.put(globalThis, JSC.ZigString.static("prerelease"), pre_array);
const build_array = try version.tag.toComponentsArray(false, globalThis, allocator, version_slice.slice());
obj.put(globalThis, JSC.ZigString.static("build"), build_array);
const raw_version_string = try std.fmt.allocPrint(allocator, "{s}", .{version.fmt(version_slice.slice())});
obj.put(globalThis, JSC.ZigString.static("version"), bun.String.createUTF8ForJS(globalThis, raw_version_string));
obj.put(globalThis, JSC.ZigString.static("raw"), arguments[0]);
return obj;
}
pub fn bump(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue {
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
var stack_fallback = std.heap.stackFallback(1024, arena.allocator());
const allocator = stack_fallback.get();
const arguments = callFrame.arguments_old(3).slice(); // v, releaseType, identifier
if (arguments.len < 2) return JSC.JSValue.null;
// Arg 1: Version
const version_str = try arguments[0].toJSString(globalThis);
const version_slice = version_str.toSlice(globalThis, allocator);
defer version_slice.deinit();
if (!strings.isAllASCII(version_slice.slice())) return JSC.JSValue.null;
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
if (!parse_result.valid) return JSC.JSValue.null;
// Arg 2: Release Type
const release_type_str = try arguments[1].toJSString(globalThis);
const release_type_slice = release_type_str.toSlice(globalThis, allocator);
defer release_type_slice.deinit();
const release_type = Version.ReleaseType.fromString(release_type_slice.slice()) orelse return JSC.JSValue.null;
// Arg 3: Identifier (optional)
var identifier: ?[]const u8 = null;
if (arguments.len > 2 and arguments[2].isString()) {
const id_str = try arguments[2].toJSString(globalThis);
const id_slice = id_str.toSlice(globalThis, allocator);
defer id_slice.deinit();
identifier = id_slice.slice();
}
const new_version_str = (parse_result.version.min().bump(allocator, release_type, identifier) catch return JSC.JSValue.null);
defer allocator.free(new_version_str);
return bun.String.createUTF8ForJS(globalThis, new_version_str);
}
const std = @import("std");
const bun = @import("bun");
const strings = bun.strings;

View File

@@ -277,6 +277,126 @@ pub const Version = extern struct {
}
};
pub const ReleaseType = enum {
major,
premajor,
minor,
preminor,
patch,
prepatch,
prerelease,
release,
pub fn fromString(s: []const u8) ?ReleaseType {
return std.meta.stringToEnum(ReleaseType, s);
}
};
pub fn bump(
self: Version,
allocator: std.mem.Allocator,
release_type: ReleaseType,
identifier: ?[]const u8,
original_buf: []const u8,
) ![]const u8 {
var new_version = self;
new_version.tag.build = .{}; // Build metadata is always removed
// We'll need to allocate new strings for prerelease tags
var pre_strings = std.ArrayList(u8).init(allocator);
defer pre_strings.deinit();
switch (release_type) {
.major => {
new_version.major +|= 1;
new_version.minor = 0;
new_version.patch = 0;
new_version.tag.pre = .{};
},
.minor => {
new_version.minor +|= 1;
new_version.patch = 0;
new_version.tag.pre = .{};
},
.patch => {
new_version.patch +|= 1;
new_version.tag.pre = .{};
},
.premajor => {
new_version.major +|= 1;
new_version.minor = 0;
new_version.patch = 0;
try pre_strings.writer().print("{s}.0", .{identifier orelse "0"});
new_version.tag.pre = ExternalString.from(pre_strings.items);
},
.preminor => {
new_version.minor +|= 1;
new_version.patch = 0;
try pre_strings.writer().print("{s}.0", .{identifier orelse "0"});
new_version.tag.pre = ExternalString.from(pre_strings.items);
},
.prepatch => {
new_version.patch +|= 1;
try pre_strings.writer().print("{s}.0", .{identifier orelse "0"});
new_version.tag.pre = ExternalString.from(pre_strings.items);
},
.release => {
new_version.tag.pre = .{};
},
.prerelease => {
if (!new_version.tag.hasPre()) {
// Same as prepatch
new_version.patch +|= 1;
try pre_strings.writer().print("{s}.0", .{identifier orelse "0"});
new_version.tag.pre = ExternalString.from(pre_strings.items);
} else {
// Increment existing prerelease
const existing_pre = self.tag.pre.slice(original_buf);
// Find last numeric component
var last_dot: ?usize = null;
var i: usize = existing_pre.len;
while (i > 0) : (i -= 1) {
if (existing_pre[i - 1] == '.') {
last_dot = i - 1;
break;
}
}
if (last_dot) |dot_pos| {
const last_part = existing_pre[dot_pos + 1 ..];
if (std.fmt.parseUnsigned(u64, last_part, 10)) |num| {
try pre_strings.writer().print("{s}.{d}", .{ existing_pre[0..dot_pos], num + 1 });
} else {
try pre_strings.writer().print("{s}.0", .{existing_pre});
}
} else {
// No dots, check if the whole thing is numeric
if (std.fmt.parseUnsigned(u64, existing_pre, 10)) |num| {
try pre_strings.writer().print("{d}", .{num + 1});
} else {
try pre_strings.writer().print("{s}.0", .{existing_pre});
}
}
new_version.tag.pre = ExternalString.from(pre_strings.items);
}
},
}
// Build the final version string
var output = std.ArrayList(u8).init(allocator);
errdefer output.deinit();
try output.writer().print("{d}.{d}.{d}", .{ new_version.major, new_version.minor, new_version.patch });
if (new_version.tag.hasPre()) {
try output.append('-');
try output.appendSlice(new_version.tag.pre.slice(pre_strings.items));
}
return output.toOwnedSlice();
}
pub const PinnedVersion = enum {
major, // ^
minor, // ~
@@ -551,7 +671,7 @@ pub const Version = extern struct {
return lhs.orderPre(rhs, lhs_buf, rhs_buf);
}
const pre_order = lhs.pre.order(&rhs.pre, lhs_buf, rhs_buf);
const pre_order = lhs.orderPre(rhs, lhs_buf, rhs_buf);
if (pre_order != .eq) return pre_order;
return lhs.build.order(&rhs.build, lhs_buf, rhs_buf);
@@ -612,6 +732,33 @@ pub const Version = extern struct {
return !this.build.isEmpty();
}
pub fn toComponentsArray(
self: Tag,
is_pre: bool,
globalThis: *JSC.JSGlobalObject,
allocator: std.mem.Allocator,
buf: []const u8,
) bun.JSError!JSC.JSValue {
const tag_str = if (is_pre) self.pre.slice(buf) else self.build.slice(buf);
if (tag_str.len == 0) {
return JSC.JSValue.null;
}
var list = std.ArrayList(JSC.JSValue).init(allocator);
defer list.deinit();
var it = strings.split(tag_str, ".");
while (it.next()) |part| {
if (std.fmt.parseUnsigned(u64, part, 10) catch null) |num| {
try list.append(JSC.jsNumber(@floatFromInt(num)));
} else {
try list.append(bun.String.createUTF8ForJS(globalThis, part));
}
}
return JSC.JSValue.createArrayFromElements(globalThis, list.items);
}
pub fn eql(lhs: Tag, rhs: Tag) bool {
return lhs.pre.hash == rhs.pre.hash;
}
@@ -998,3 +1145,4 @@ const String = bun.Semver.String;
const Query = bun.Semver.Query;
const assert = bun.assert;
const JSC = bun.JSC;

View File

@@ -490,7 +490,6 @@ describe("Bun.semver.satisfies()", () => {
["1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre.2"],
["1.2.3-pre+asdf - 2.4.3-pre+asdf", "2.4.3-alpha"],
["1.2.3+asdf - 2.4.3+asdf", "1.2.3"],
["1.0.0", "1.0.0"],
[">=*", "0.2.4"],
["", "1.0.0"],
["*", "1.2.3"],
@@ -738,3 +737,167 @@ describe("Bun.semver.satisfies()", () => {
expect(unsortedPrereleases.sort(Bun.semver.order)).toMatchSnapshot();
});
});
describe("Bun.semver.major()", () => {
test("should return major version", () => {
expect(Bun.semver.major("1.2.3")).toBe(1);
expect(Bun.semver.major("v2.0.0-alpha.1")).toBe(2);
expect(Bun.semver.major("0.0.1")).toBe(0);
expect(Bun.semver.major("999.888.777")).toBe(999);
});
test("should return null for invalid versions", () => {
expect(Bun.semver.major("not-a-version")).toBe(null);
expect(Bun.semver.major("")).toBe(null);
expect(Bun.semver.major("v")).toBe(null);
});
});
describe("Bun.semver.minor()", () => {
test("should return minor version", () => {
expect(Bun.semver.minor("1.2.3")).toBe(2);
expect(Bun.semver.minor("v2.0.0-alpha.1")).toBe(0);
expect(Bun.semver.minor("0.999.1")).toBe(999);
});
test("should return null for invalid versions", () => {
expect(Bun.semver.minor("not-a-version")).toBe(null);
expect(Bun.semver.minor("")).toBe(null);
});
});
describe("Bun.semver.patch()", () => {
test("should return patch version", () => {
expect(Bun.semver.patch("1.2.3")).toBe(3);
expect(Bun.semver.patch("v2.0.0-alpha.1")).toBe(0);
expect(Bun.semver.patch("0.1.999")).toBe(999);
});
test("should return null for invalid versions", () => {
expect(Bun.semver.patch("not-a-version")).toBe(null);
expect(Bun.semver.patch("")).toBe(null);
});
});
describe("Bun.semver.prerelease()", () => {
test("should return prerelease components", () => {
expect(Bun.semver.prerelease("1.2.3-alpha.1")).toEqual(["alpha", 1]);
expect(Bun.semver.prerelease("1.0.0-rc.2.beta")).toEqual(["rc", 2, "beta"]);
expect(Bun.semver.prerelease("1.0.0-0")).toEqual([0]);
expect(Bun.semver.prerelease("1.0.0-x.7.z.92")).toEqual(["x", 7, "z", 92]);
});
test("should return null for non-prerelease versions", () => {
expect(Bun.semver.prerelease("1.2.3")).toBe(null);
expect(Bun.semver.prerelease("1.2.3+build")).toBe(null);
expect(Bun.semver.prerelease("invalid")).toBe(null);
});
});
describe("Bun.semver.parse()", () => {
test("should parse a version into an object", () => {
const v = "1.2.3-alpha.1+build.123";
const parsed = Bun.semver.parse(v);
expect(parsed).not.toBe(null);
expect(parsed.major).toBe(1);
expect(parsed.minor).toBe(2);
expect(parsed.patch).toBe(3);
expect(parsed.prerelease).toEqual(["alpha", 1]);
expect(parsed.build).toEqual(["build", 123]);
expect(parsed.version).toBe("1.2.3-alpha.1+build.123");
expect(parsed.raw).toBe(v);
});
test("should parse simple versions", () => {
const parsed = Bun.semver.parse("1.2.3");
expect(parsed).not.toBe(null);
expect(parsed.major).toBe(1);
expect(parsed.minor).toBe(2);
expect(parsed.patch).toBe(3);
expect(parsed.prerelease).toBe(null);
expect(parsed.build).toBe(null);
expect(parsed.version).toBe("1.2.3");
});
test("should return null for invalid versions", () => {
expect(Bun.semver.parse("not-a-version")).toBe(null);
expect(Bun.semver.parse("")).toBe(null);
expect(Bun.semver.parse("v")).toBe(null);
});
});
describe("Bun.semver.bump()", () => {
describe("major", () => {
test("increments major version", () => {
expect(Bun.semver.bump("1.2.3", "major")).toBe("2.0.0");
expect(Bun.semver.bump("0.0.1", "major")).toBe("1.0.0");
expect(Bun.semver.bump("1.2.3-alpha", "major")).toBe("2.0.0");
expect(Bun.semver.bump("1.2.3+build", "major")).toBe("2.0.0");
});
});
describe("minor", () => {
test("increments minor version", () => {
expect(Bun.semver.bump("1.2.3", "minor")).toBe("1.3.0");
expect(Bun.semver.bump("0.0.1", "minor")).toBe("0.1.0");
expect(Bun.semver.bump("1.2.3-alpha", "minor")).toBe("1.3.0");
});
});
describe("patch", () => {
test("increments patch version", () => {
expect(Bun.semver.bump("1.2.3", "patch")).toBe("1.2.4");
expect(Bun.semver.bump("0.0.1", "patch")).toBe("0.0.2");
expect(Bun.semver.bump("1.2.3-alpha", "patch")).toBe("1.2.4");
});
});
describe("premajor", () => {
test("increments major and adds prerelease", () => {
expect(Bun.semver.bump("1.2.3", "premajor")).toBe("2.0.0-0.0");
expect(Bun.semver.bump("1.2.3", "premajor", "alpha")).toBe("2.0.0-alpha.0");
expect(Bun.semver.bump("1.2.3", "premajor", "beta")).toBe("2.0.0-beta.0");
});
});
describe("preminor", () => {
test("increments minor and adds prerelease", () => {
expect(Bun.semver.bump("1.2.3", "preminor")).toBe("1.3.0-0.0");
expect(Bun.semver.bump("1.2.3", "preminor", "alpha")).toBe("1.3.0-alpha.0");
});
});
describe("prepatch", () => {
test("increments patch and adds prerelease", () => {
expect(Bun.semver.bump("1.2.3", "prepatch")).toBe("1.2.4-0.0");
expect(Bun.semver.bump("1.2.3", "prepatch", "alpha")).toBe("1.2.4-alpha.0");
});
});
describe("release", () => {
test("removes prerelease", () => {
expect(Bun.semver.bump("1.2.3-alpha.1", "release")).toBe("1.2.3");
expect(Bun.semver.bump("1.2.3-0", "release")).toBe("1.2.3");
expect(Bun.semver.bump("1.2.3", "release")).toBe("1.2.3");
});
});
describe("prerelease", () => {
test("increments prerelease version", () => {
expect(Bun.semver.bump("1.2.3-alpha.1", "prerelease")).toBe("1.2.3-alpha.2");
expect(Bun.semver.bump("1.2.3-0", "prerelease")).toBe("1.2.3-1");
expect(Bun.semver.bump("1.2.3-alpha", "prerelease")).toBe("1.2.3-alpha.0");
});
test("adds prerelease if none exists", () => {
expect(Bun.semver.bump("1.2.3", "prerelease")).toBe("1.2.4-0.0");
expect(Bun.semver.bump("1.2.3", "prerelease", "alpha")).toBe("1.2.4-alpha.0");
});
});
test("returns null for invalid versions", () => {
expect(Bun.semver.bump("not-a-version", "major")).toBe(null);
expect(Bun.semver.bump("", "major")).toBe(null);
expect(Bun.semver.bump("1.2.3", "invalid" as any)).toBe(null);
});
});