mirror of
https://github.com/oven-sh/bun
synced 2026-02-17 06:12:08 +00:00
Compare commits
13 Commits
claude/imp
...
cursor/pla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c37650c038 | ||
|
|
114d0068d0 | ||
|
|
80c74f0e9a | ||
|
|
30441585d6 | ||
|
|
753eeef322 | ||
|
|
19c5ab22e3 | ||
|
|
c7fde97e07 | ||
|
|
ae61cb6661 | ||
|
|
93cf328797 | ||
|
|
e7190c9a17 | ||
|
|
02ccb95520 | ||
|
|
9bb0ee6bb1 | ||
|
|
33fb280ac2 |
96
packages/bun-types/bun.d.ts
vendored
96
packages/bun-types/bun.d.ts
vendored
@@ -784,7 +784,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.
|
||||
*
|
||||
@@ -2175,8 +2174,7 @@ declare module "bun" {
|
||||
* ... on User {
|
||||
* id
|
||||
* }
|
||||
* }
|
||||
* }`;
|
||||
* }`;
|
||||
* ```
|
||||
*
|
||||
* Will be replaced with:
|
||||
@@ -2332,7 +2330,6 @@ declare module "bun" {
|
||||
path: string;
|
||||
kind: ImportKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [Bun.build API docs](https://bun.com/docs/bundler#api)
|
||||
*/
|
||||
@@ -3069,7 +3066,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.
|
||||
*
|
||||
@@ -3876,7 +3872,6 @@ declare module "bun" {
|
||||
errno?: number;
|
||||
syscall?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for TLS connections
|
||||
*/
|
||||
@@ -4659,7 +4654,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`
|
||||
*/
|
||||
@@ -5085,6 +5079,90 @@ 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;
|
||||
|
||||
/**
|
||||
* Returns true if the two version ranges intersect (have any versions in common).
|
||||
*/
|
||||
function intersects(range1: StringLike, range2: StringLike): boolean;
|
||||
|
||||
/**
|
||||
* Returns the highest version in the list that satisfies the range, or null if none of them do.
|
||||
*/
|
||||
function maxSatisfying(versions: StringLike[], range: StringLike): string | null;
|
||||
|
||||
/**
|
||||
* Returns the lowest version in the list that satisfies the range, or null if none of them do.
|
||||
*/
|
||||
function minSatisfying(versions: StringLike[], range: StringLike): string | null;
|
||||
|
||||
/**
|
||||
* Returns a simplified range expression that matches the same items in the versions list as the input range.
|
||||
*
|
||||
* @param versions Array of versions to match
|
||||
* @param range The range to simplify
|
||||
* @returns The simplified range, or the original if it can't be simplified
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* Bun.semver.simplifyRange(["1.0.0", "1.0.1", "1.0.2"], "1.0.0 || 1.0.1 || 1.0.2"); // "~1.0.0"
|
||||
* Bun.semver.simplifyRange(["1.0.0", "1.1.0", "1.2.0"], "1.0.0 || 1.1.0 || 1.2.0"); // "^1.0.0"
|
||||
* ```
|
||||
*/
|
||||
function simplifyRange(versions: StringLike[], range: StringLike): string | null;
|
||||
|
||||
/**
|
||||
* Returns the valid range string, or null if it's not valid.
|
||||
*/
|
||||
function validRange(range: StringLike): string | null;
|
||||
}
|
||||
|
||||
namespace unsafe {
|
||||
@@ -5399,7 +5477,6 @@ declare module "bun" {
|
||||
*/
|
||||
static readonly algorithms: SupportedCryptoAlgorithms[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a `Promise` after milliseconds. This is like
|
||||
* {@link setTimeout} except it returns a `Promise`.
|
||||
@@ -5444,7 +5521,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)
|
||||
*
|
||||
@@ -6129,7 +6205,6 @@ declare module "bun" {
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a TCP or TLS socket connection used for network communication.
|
||||
* This interface provides methods for reading, writing, managing the connection state,
|
||||
@@ -6871,7 +6946,6 @@ declare module "bun" {
|
||||
* @category HTTP & Networking
|
||||
*/
|
||||
function listen<Data = undefined>(options: UnixSocketOptions<Data>): UnixSocketListener<Data>;
|
||||
|
||||
/**
|
||||
* @category HTTP & Networking
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ pub const ExternalString = extern struct {
|
||||
pub inline fn from(in: string) ExternalString {
|
||||
return ExternalString{
|
||||
.value = String.init(in, in),
|
||||
.hash = bun.Wyhash.hash(0, in),
|
||||
.hash = bun.Wyhash11.hash(0, in),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const SemverObject = @This();
|
||||
|
||||
pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue {
|
||||
const object = jsc.JSValue.createEmptyObject(globalThis, 2);
|
||||
const object = jsc.JSValue.createEmptyObject(globalThis, 13);
|
||||
|
||||
object.put(
|
||||
globalThis,
|
||||
@@ -27,6 +27,138 @@ 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,
|
||||
),
|
||||
);
|
||||
|
||||
object.put(
|
||||
globalThis,
|
||||
jsc.ZigString.static("intersects"),
|
||||
jsc.host_fn.NewFunction(
|
||||
globalThis,
|
||||
jsc.ZigString.static("intersects"),
|
||||
2,
|
||||
SemverObject.intersects,
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
object.put(
|
||||
globalThis,
|
||||
jsc.ZigString.static("maxSatisfying"),
|
||||
jsc.host_fn.NewFunction(
|
||||
globalThis,
|
||||
jsc.ZigString.static("maxSatisfying"),
|
||||
2,
|
||||
SemverObject.maxSatisfying,
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
object.put(
|
||||
globalThis,
|
||||
jsc.ZigString.static("minSatisfying"),
|
||||
jsc.host_fn.NewFunction(
|
||||
globalThis,
|
||||
jsc.ZigString.static("minSatisfying"),
|
||||
2,
|
||||
SemverObject.minSatisfying,
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
object.put(
|
||||
globalThis,
|
||||
jsc.ZigString.static("simplifyRange"),
|
||||
jsc.host_fn.NewFunction(
|
||||
globalThis,
|
||||
jsc.ZigString.static("simplifyRange"),
|
||||
2,
|
||||
SemverObject.simplifyRange,
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
object.put(
|
||||
globalThis,
|
||||
jsc.ZigString.static("validRange"),
|
||||
jsc.host_fn.NewFunction(
|
||||
globalThis,
|
||||
jsc.ZigString.static("validRange"),
|
||||
1,
|
||||
SemverObject.validRange,
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
@@ -39,7 +171,7 @@ pub fn order(
|
||||
var stack_fallback = std.heap.stackFallback(512, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments_old(2).slice();
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 2) {
|
||||
return globalThis.throw("Expected two arguments", .{});
|
||||
}
|
||||
@@ -85,7 +217,7 @@ pub fn satisfies(globalThis: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) bun
|
||||
var stack_fallback = std.heap.stackFallback(512, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments_old(2).slice();
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 2) {
|
||||
return globalThis.throw("Expected two arguments", .{});
|
||||
}
|
||||
@@ -127,6 +259,567 @@ pub fn satisfies(globalThis: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) bun
|
||||
return .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();
|
||||
if (arguments.len < 1) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
// Check if the argument is a string
|
||||
if (!arguments[0].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
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.jsNull();
|
||||
|
||||
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
|
||||
if (!parse_result.valid) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const version = parse_result.version;
|
||||
return switch (component) {
|
||||
.major => jsc.JSValue.jsNumber(version.major orelse 0),
|
||||
.minor => jsc.JSValue.jsNumber(version.minor orelse 0),
|
||||
.patch => jsc.JSValue.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(512, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 1) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
// Check if the argument is a string
|
||||
if (!arguments[0].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
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.jsNull();
|
||||
|
||||
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
|
||||
if (!parse_result.valid) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const version = parse_result.version;
|
||||
if (!version.tag.hasPre()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
return try version.tag.toComponentsArray(true, globalThis, 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();
|
||||
if (arguments.len < 1) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
// Check if the argument is a string
|
||||
if (!arguments[0].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
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.jsNull();
|
||||
|
||||
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
|
||||
if (!parse_result.valid) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const version = parse_result.version;
|
||||
const obj = jsc.JSValue.createEmptyObject(globalThis, 7);
|
||||
|
||||
obj.put(globalThis, jsc.ZigString.static("major"), jsc.JSValue.jsNumber(version.major orelse 0));
|
||||
obj.put(globalThis, jsc.ZigString.static("minor"), jsc.JSValue.jsNumber(version.minor orelse 0));
|
||||
obj.put(globalThis, jsc.ZigString.static("patch"), jsc.JSValue.jsNumber(version.patch orelse 0));
|
||||
|
||||
// Handle prerelease
|
||||
if (version.tag.hasPre()) {
|
||||
obj.put(globalThis, jsc.ZigString.static("prerelease"), try version.tag.toComponentsArray(true, globalThis, version_slice.slice()));
|
||||
} else {
|
||||
obj.put(globalThis, jsc.ZigString.static("prerelease"), jsc.JSValue.jsNull());
|
||||
}
|
||||
|
||||
// Handle build
|
||||
if (version.tag.hasBuild()) {
|
||||
obj.put(globalThis, jsc.ZigString.static("build"), try version.tag.toComponentsArray(false, globalThis, version_slice.slice()));
|
||||
} else {
|
||||
obj.put(globalThis, jsc.ZigString.static("build"), jsc.JSValue.jsNull());
|
||||
}
|
||||
|
||||
// Format version string
|
||||
var version_str = std.ArrayList(u8).init(allocator);
|
||||
defer version_str.deinit();
|
||||
|
||||
try version_str.writer().print("{d}.{d}.{d}", .{ version.major orelse 0, version.minor orelse 0, version.patch orelse 0 });
|
||||
|
||||
if (version.tag.hasPre()) {
|
||||
try version_str.append('-');
|
||||
try version_str.appendSlice(version.tag.pre.slice(version_slice.slice()));
|
||||
}
|
||||
|
||||
if (version.tag.hasBuild()) {
|
||||
try version_str.append('+');
|
||||
try version_str.appendSlice(version.tag.build.slice(version_slice.slice()));
|
||||
}
|
||||
|
||||
obj.put(globalThis, jsc.ZigString.static("version"), try bun.String.createUTF8ForJS(globalThis, version_str.items));
|
||||
|
||||
// Store raw input
|
||||
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(2048, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 2) return jsc.JSValue.jsNull();
|
||||
|
||||
// Check if the first argument is a string
|
||||
if (!arguments[0].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
// Check if the second argument is a string
|
||||
if (!arguments[1].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const version_str = try arguments[0].toJSString(globalThis);
|
||||
const version_slice = version_str.toSlice(globalThis, allocator);
|
||||
defer version_slice.deinit();
|
||||
|
||||
const release_str = try arguments[1].toJSString(globalThis);
|
||||
const release_slice = release_str.toSlice(globalThis, allocator);
|
||||
defer release_slice.deinit();
|
||||
|
||||
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
|
||||
if (!parse_result.valid) return jsc.JSValue.jsNull();
|
||||
|
||||
const release_type = Version.ReleaseType.fromString(release_slice.slice()) orelse return jsc.JSValue.jsNull();
|
||||
|
||||
var identifier_slice: jsc.ZigString.Slice = .empty;
|
||||
defer identifier_slice.deinit();
|
||||
|
||||
if (arguments.len > 2 and !arguments[2].isUndefinedOrNull()) {
|
||||
const id_str = try arguments[2].toJSString(globalThis);
|
||||
identifier_slice = id_str.toSlice(globalThis, allocator);
|
||||
}
|
||||
|
||||
const new_version_str = parse_result.version.min().bump(allocator, release_type, identifier_slice.slice(), version_slice.slice()) catch return jsc.JSValue.jsNull();
|
||||
defer allocator.free(new_version_str);
|
||||
|
||||
return bun.String.createUTF8ForJS(globalThis, new_version_str);
|
||||
}
|
||||
|
||||
pub fn intersects(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(2048, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 2) return jsc.JSValue.jsBoolean(false);
|
||||
|
||||
// Check if both arguments are strings
|
||||
if (!arguments[0].isString() or !arguments[1].isString()) {
|
||||
return jsc.JSValue.jsBoolean(false);
|
||||
}
|
||||
|
||||
const r1_str = try arguments[0].toJSString(globalThis);
|
||||
const r1_slice = r1_str.toSlice(globalThis, allocator);
|
||||
defer r1_slice.deinit();
|
||||
|
||||
const r2_str = try arguments[1].toJSString(globalThis);
|
||||
const r2_slice = r2_str.toSlice(globalThis, allocator);
|
||||
defer r2_slice.deinit();
|
||||
|
||||
// Check for empty strings
|
||||
if (r1_slice.slice().len == 0 or r2_slice.slice().len == 0) {
|
||||
return jsc.JSValue.jsBoolean(false);
|
||||
}
|
||||
|
||||
const g1 = Query.parse(allocator, r1_slice.slice(), SlicedString.init(r1_slice.slice(), r1_slice.slice())) catch return jsc.JSValue.jsBoolean(false);
|
||||
defer g1.deinit();
|
||||
|
||||
const g2 = Query.parse(allocator, r2_slice.slice(), SlicedString.init(r2_slice.slice(), r2_slice.slice())) catch return jsc.JSValue.jsBoolean(false);
|
||||
defer g2.deinit();
|
||||
|
||||
return jsc.JSValue.jsBoolean(g1.intersects(&g2, r1_slice.slice(), r2_slice.slice()));
|
||||
}
|
||||
|
||||
fn findSatisfyingVersion(
|
||||
globalThis: *jsc.JSGlobalObject,
|
||||
versions_array: jsc.JSValue,
|
||||
range_str: []const u8,
|
||||
allocator: std.mem.Allocator,
|
||||
comptime find_max: bool,
|
||||
) bun.JSError!jsc.JSValue {
|
||||
const query = (Query.parse(allocator, range_str, SlicedString.init(range_str, range_str)) catch return jsc.JSValue.jsNull());
|
||||
defer query.deinit();
|
||||
|
||||
const length = versions_array.getLength(globalThis) catch return jsc.JSValue.jsNull();
|
||||
if (length == 0) return jsc.JSValue.jsNull();
|
||||
|
||||
var best: ?struct { version: Version, str: []const u8, index: u32 } = null;
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < length) : (i += 1) {
|
||||
const item = versions_array.getIndex(globalThis, i) catch continue;
|
||||
const version_string = try item.toJSString(globalThis);
|
||||
const version_slice = version_string.toSlice(globalThis, allocator);
|
||||
defer version_slice.deinit();
|
||||
|
||||
if (!strings.isAllASCII(version_slice.slice())) continue;
|
||||
|
||||
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
|
||||
if (!parse_result.valid) continue;
|
||||
|
||||
const version = parse_result.version.min();
|
||||
if (query.satisfies(version, range_str, version_slice.slice())) {
|
||||
if (best == null) {
|
||||
best = .{ .version = version, .str = version_slice.slice(), .index = i };
|
||||
} else {
|
||||
const comparison = best.?.version.orderWithoutBuild(version, best.?.str, version_slice.slice());
|
||||
if ((find_max and comparison == .lt) or (!find_max and comparison == .gt)) {
|
||||
best = .{ .version = version, .str = version_slice.slice(), .index = i };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (best) |b| (versions_array.getIndex(globalThis, b.index) catch jsc.JSValue.jsNull()) else jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
pub fn maxSatisfying(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(2048, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 1) return jsc.JSValue.jsNull();
|
||||
|
||||
const versions_array = arguments[0];
|
||||
if (!versions_array.isObject() or !(try versions_array.isIterable(globalThis))) {
|
||||
return globalThis.throw("First argument must be an array", .{});
|
||||
}
|
||||
|
||||
if (arguments.len < 2) return jsc.JSValue.jsNull();
|
||||
|
||||
// Check if the second argument is a string
|
||||
if (!arguments[1].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const range_str = try arguments[1].toJSString(globalThis);
|
||||
const range_slice = range_str.toSlice(globalThis, allocator);
|
||||
defer range_slice.deinit();
|
||||
|
||||
// Check for empty range
|
||||
if (range_slice.slice().len == 0) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
return findSatisfyingVersion(globalThis, versions_array, range_slice.slice(), allocator, true);
|
||||
}
|
||||
|
||||
pub fn minSatisfying(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(2048, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 1) return jsc.JSValue.jsNull();
|
||||
|
||||
const versions_array = arguments[0];
|
||||
if (!versions_array.isObject() or !(try versions_array.isIterable(globalThis))) {
|
||||
return globalThis.throw("First argument must be an array", .{});
|
||||
}
|
||||
|
||||
if (arguments.len < 2) return jsc.JSValue.jsNull();
|
||||
|
||||
// Check if the second argument is a string
|
||||
if (!arguments[1].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const range_str = try arguments[1].toJSString(globalThis);
|
||||
const range_slice = range_str.toSlice(globalThis, allocator);
|
||||
defer range_slice.deinit();
|
||||
|
||||
return findSatisfyingVersion(globalThis, versions_array, range_slice.slice(), allocator, false);
|
||||
}
|
||||
|
||||
pub fn simplifyRange(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(2048, arena.allocator());
|
||||
const allocator = stack_fallback.get();
|
||||
|
||||
const arguments = callFrame.arguments();
|
||||
if (arguments.len < 2) return jsc.JSValue.jsNull();
|
||||
|
||||
// First argument must be an array
|
||||
const versions_array = arguments[0];
|
||||
if (!versions_array.isObject() or !(try versions_array.isIterable(globalThis))) {
|
||||
return globalThis.throw("First argument must be an array", .{});
|
||||
}
|
||||
|
||||
// Second argument must be a string (the range)
|
||||
if (!arguments[1].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const range_str = try arguments[1].toJSString(globalThis);
|
||||
const range_slice = range_str.toSlice(globalThis, allocator);
|
||||
defer range_slice.deinit();
|
||||
|
||||
// Parse the range to validate it
|
||||
const query = Query.parse(allocator, range_slice.slice(), SlicedString.init(range_slice.slice(), range_slice.slice())) catch return arguments[1];
|
||||
defer query.deinit();
|
||||
|
||||
// Collect all versions that satisfy the range
|
||||
var satisfying_versions = std.ArrayList(Version).init(allocator);
|
||||
defer satisfying_versions.deinit();
|
||||
|
||||
const length = versions_array.getLength(globalThis) catch return arguments[1];
|
||||
if (length == 0) return arguments[1];
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < length) : (i += 1) {
|
||||
const item = versions_array.getIndex(globalThis, i) catch continue;
|
||||
if (!item.isString()) continue;
|
||||
|
||||
const version_string = try item.toJSString(globalThis);
|
||||
const version_slice = version_string.toSlice(globalThis, allocator);
|
||||
defer version_slice.deinit();
|
||||
|
||||
if (!strings.isAllASCII(version_slice.slice())) continue;
|
||||
|
||||
const parse_result = Version.parse(SlicedString.init(version_slice.slice(), version_slice.slice()));
|
||||
if (!parse_result.valid) continue;
|
||||
|
||||
const version = parse_result.version.min();
|
||||
if (query.satisfies(version, range_slice.slice(), version_slice.slice())) {
|
||||
satisfying_versions.append(version) catch continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (satisfying_versions.items.len == 0) return arguments[1];
|
||||
|
||||
// Sort versions
|
||||
std.sort.pdq(Version, satisfying_versions.items, {}, struct {
|
||||
fn lessThan(_: void, a: Version, b: Version) bool {
|
||||
return a.orderWithoutBuild(b, "", "") == .lt;
|
||||
}
|
||||
}.lessThan);
|
||||
|
||||
// Try to find a simpler range
|
||||
const simplified = try findSimplifiedRange(allocator, satisfying_versions.items, range_slice.slice());
|
||||
|
||||
// If the simplified range is shorter, return it
|
||||
if (simplified.len < range_slice.slice().len) {
|
||||
return bun.String.createUTF8ForJS(globalThis, simplified);
|
||||
}
|
||||
|
||||
// Otherwise return the original range
|
||||
return arguments[1];
|
||||
}
|
||||
|
||||
fn findSimplifiedRange(allocator: std.mem.Allocator, versions: []const Version, original_range: []const u8) ![]const u8 {
|
||||
if (versions.len == 0) return original_range;
|
||||
|
||||
const first = versions[0];
|
||||
const last = versions[versions.len - 1];
|
||||
|
||||
// Check if all versions are in the same major version
|
||||
var same_major = true;
|
||||
var same_minor = true;
|
||||
var same_patch = true;
|
||||
|
||||
for (versions) |v| {
|
||||
if (v.major != first.major) same_major = false;
|
||||
if (v.minor != first.minor) same_minor = false;
|
||||
if (v.patch != first.patch) same_patch = false;
|
||||
}
|
||||
|
||||
// If all versions are the same, return exact version only if it's shorter
|
||||
// AND we have multiple versions (not just one that happened to match)
|
||||
if (same_patch and same_minor and same_major and versions.len > 1) {
|
||||
const exact = try std.fmt.allocPrint(allocator, "{d}.{d}.{d}", .{ first.major, first.minor, first.patch });
|
||||
if (exact.len < original_range.len) {
|
||||
return exact;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for consecutive patch versions in same minor
|
||||
if (same_major and same_minor) {
|
||||
var consecutive = true;
|
||||
var expected_patch = first.patch;
|
||||
for (versions) |v| {
|
||||
if (v.patch != expected_patch) {
|
||||
consecutive = false;
|
||||
break;
|
||||
}
|
||||
expected_patch += 1;
|
||||
}
|
||||
|
||||
if (consecutive and versions.len >= 3) {
|
||||
// Use tilde range for consecutive patches
|
||||
return try std.fmt.allocPrint(allocator, "~{d}.{d}.{d}", .{ first.major, first.minor, first.patch });
|
||||
}
|
||||
}
|
||||
|
||||
// Check for consecutive minor versions in same major
|
||||
if (same_major) {
|
||||
// Check if we have all minors from first to last
|
||||
var expected_versions = std.ArrayList(Version).init(allocator);
|
||||
defer expected_versions.deinit();
|
||||
|
||||
var current_minor = first.minor;
|
||||
while (current_minor <= last.minor) : (current_minor += 1) {
|
||||
var current_patch: u32 = 0;
|
||||
while (current_patch <= 10) : (current_patch += 1) { // Reasonable limit
|
||||
for (versions) |v| {
|
||||
if (v.minor == current_minor and v.patch == current_patch) {
|
||||
expected_versions.append(v) catch break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have good coverage, use caret range
|
||||
if (expected_versions.items.len >= versions.len * 3 / 4) { // 75% coverage
|
||||
return try std.fmt.allocPrint(allocator, "^{d}.{d}.{d}", .{ first.major, first.minor, first.patch });
|
||||
}
|
||||
}
|
||||
|
||||
// Try range format only if it makes sense (versions are close together)
|
||||
if (versions.len > 1) {
|
||||
// Only use range format if versions are reasonably close
|
||||
const major_diff = last.major - first.major;
|
||||
const is_close = major_diff <= 1;
|
||||
|
||||
if (is_close) {
|
||||
const range_str = try std.fmt.allocPrint(allocator, ">={d}.{d}.{d} <={d}.{d}.{d}", .{
|
||||
first.major, first.minor, first.patch,
|
||||
last.major, last.minor, last.patch,
|
||||
});
|
||||
|
||||
if (range_str.len < original_range.len) {
|
||||
return range_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have just a few versions, try OR'ing them
|
||||
if (versions.len <= 3) {
|
||||
var result = std.ArrayList(u8).init(allocator);
|
||||
defer result.deinit();
|
||||
|
||||
for (versions, 0..) |v, idx| {
|
||||
if (idx > 0) {
|
||||
try result.appendSlice(" || ");
|
||||
}
|
||||
try result.writer().print("{d}.{d}.{d}", .{ v.major, v.minor, v.patch });
|
||||
}
|
||||
|
||||
if (result.items.len < original_range.len) {
|
||||
return try allocator.dupe(u8, result.items);
|
||||
}
|
||||
}
|
||||
|
||||
// Return original if we can't simplify
|
||||
return original_range;
|
||||
}
|
||||
|
||||
pub fn validRange(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();
|
||||
if (arguments.len < 1) return jsc.JSValue.jsNull();
|
||||
|
||||
// Check if the argument is a string
|
||||
if (!arguments[0].isString()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
const range_str = try arguments[0].toJSString(globalThis);
|
||||
const range_slice = range_str.toSlice(globalThis, allocator);
|
||||
defer range_slice.deinit();
|
||||
|
||||
// Try to parse the range
|
||||
const query = Query.parse(allocator, range_slice.slice(), SlicedString.init(range_slice.slice(), range_slice.slice())) catch return jsc.JSValue.jsNull();
|
||||
defer query.deinit();
|
||||
|
||||
// Check if the query has any meaningful content
|
||||
// If it's empty or has no valid range, it's invalid
|
||||
if (!query.head.head.range.hasLeft() and !query.head.head.range.hasRight()) {
|
||||
return jsc.JSValue.jsNull();
|
||||
}
|
||||
|
||||
// If it parses successfully and has content, return the normalized range string
|
||||
return arguments[0];
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const bun = @import("bun");
|
||||
|
||||
@@ -100,6 +100,25 @@ pub const List = struct {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn intersects(list1: *const List, list2: *const List, list1_buf: string, list2_buf: string) bool {
|
||||
// Two lists intersect if their ANDed queries can be satisfied by some version
|
||||
// This is an OR operation - if any of the ORed items intersect, the lists intersect
|
||||
|
||||
var l1 = list1;
|
||||
while (true) {
|
||||
var l2 = list2;
|
||||
while (true) {
|
||||
if (l1.head.intersects(&l2.head, list1_buf, list2_buf)) {
|
||||
return true;
|
||||
}
|
||||
l2 = l2.next orelse break;
|
||||
}
|
||||
l1 = l1.next orelse break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn eql(lhs: *const List, rhs: *const List) bool {
|
||||
if (!lhs.head.eql(&rhs.head)) return false;
|
||||
|
||||
@@ -299,6 +318,27 @@ pub const Group = struct {
|
||||
else
|
||||
group.head.satisfies(version, group_buf, version_buf);
|
||||
}
|
||||
|
||||
pub fn intersects(
|
||||
self: *const Group,
|
||||
other: *const Group,
|
||||
self_buf: string,
|
||||
other_buf: string,
|
||||
) bool {
|
||||
// Two groups intersect if any of their ORed lists intersect
|
||||
var list1 = &self.head;
|
||||
while (true) {
|
||||
var list2 = &other.head;
|
||||
while (true) {
|
||||
if (list1.intersects(list2, self_buf, other_buf)) {
|
||||
return true;
|
||||
}
|
||||
list2 = list2.next orelse break;
|
||||
}
|
||||
list1 = list1.next orelse break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn eql(lhs: *const Query, rhs: *const Query) bool {
|
||||
@@ -339,6 +379,113 @@ pub fn satisfiesPre(query: *const Query, version: Version, query_buf: string, ve
|
||||
);
|
||||
}
|
||||
|
||||
pub fn intersects(query1: *const Query, query2: *const Query, query1_buf: string, query2_buf: string) bool {
|
||||
// Two queries (ANDed ranges) intersect if there exists any version that satisfies both
|
||||
// We need to check if ALL constraints from both queries can be satisfied simultaneously
|
||||
|
||||
// Collect all ranges from both queries
|
||||
var all_ranges = std.ArrayList(Range).init(bun.default_allocator);
|
||||
defer all_ranges.deinit();
|
||||
|
||||
// Add all ranges from query1
|
||||
var q1: ?*const Query = query1;
|
||||
while (q1) |q| {
|
||||
all_ranges.append(q.range) catch return false;
|
||||
q1 = q.next;
|
||||
}
|
||||
|
||||
// Add all ranges from query2
|
||||
var q2: ?*const Query = query2;
|
||||
while (q2) |q| {
|
||||
all_ranges.append(q.range) catch return false;
|
||||
q2 = q.next;
|
||||
}
|
||||
|
||||
// Now check if all these ranges can be satisfied simultaneously
|
||||
// This means we need to find if there's a non-empty intersection of all ranges
|
||||
|
||||
// Find the effective lower and upper bounds from all ranges
|
||||
var has_lower = false;
|
||||
var lower_version: Version = undefined;
|
||||
var lower_inclusive = false;
|
||||
var has_upper = false;
|
||||
var upper_version: Version = undefined;
|
||||
var upper_inclusive = false;
|
||||
|
||||
for (all_ranges.items) |range| {
|
||||
// Process lower bounds
|
||||
if (range.hasLeft()) {
|
||||
if (range.left.op == .gte or range.left.op == .gt) {
|
||||
if (!has_lower) {
|
||||
has_lower = true;
|
||||
lower_version = range.left.version;
|
||||
lower_inclusive = (range.left.op == .gte);
|
||||
} else {
|
||||
// Take the maximum of the lower bounds
|
||||
const order = range.left.version.orderWithoutBuild(lower_version, "", "");
|
||||
if (order == .gt or (order == .eq and range.left.op == .gt and lower_inclusive)) {
|
||||
lower_version = range.left.version;
|
||||
lower_inclusive = (range.left.op == .gte);
|
||||
}
|
||||
}
|
||||
} else if (range.left.op == .eql and !range.hasRight()) {
|
||||
// Exact version constraint
|
||||
const exact_version = range.left.version;
|
||||
|
||||
// Check if this exact version satisfies all other constraints
|
||||
for (all_ranges.items) |other_range| {
|
||||
if (!other_range.satisfies(exact_version, query1_buf, query2_buf)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process upper bounds
|
||||
if (range.hasRight()) {
|
||||
if (range.right.op == .lte or range.right.op == .lt) {
|
||||
if (!has_upper) {
|
||||
has_upper = true;
|
||||
upper_version = range.right.version;
|
||||
upper_inclusive = (range.right.op == .lte);
|
||||
} else {
|
||||
// Take the minimum of the upper bounds
|
||||
const order = range.right.version.orderWithoutBuild(upper_version, "", "");
|
||||
if (order == .lt or (order == .eq and range.right.op == .lt and upper_inclusive)) {
|
||||
upper_version = range.right.version;
|
||||
upper_inclusive = (range.right.op == .lte);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (range.hasLeft() and (range.left.op == .lte or range.left.op == .lt)) {
|
||||
// Single upper bound constraint
|
||||
if (!has_upper) {
|
||||
has_upper = true;
|
||||
upper_version = range.left.version;
|
||||
upper_inclusive = (range.left.op == .lte);
|
||||
} else {
|
||||
// Take the minimum of the upper bounds
|
||||
const order = range.left.version.orderWithoutBuild(upper_version, "", "");
|
||||
if (order == .lt or (order == .eq and range.left.op == .lt and upper_inclusive)) {
|
||||
upper_version = range.left.version;
|
||||
upper_inclusive = (range.left.op == .lte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the effective range is valid
|
||||
if (has_lower and has_upper) {
|
||||
const order = lower_version.orderWithoutBuild(upper_version, "", "");
|
||||
if (order == .gt) return false;
|
||||
if (order == .eq and (!lower_inclusive or !upper_inclusive)) return false;
|
||||
}
|
||||
|
||||
// If we get here, the ranges can intersect
|
||||
return true;
|
||||
}
|
||||
|
||||
pub const Token = struct {
|
||||
tag: Tag = Tag.none,
|
||||
wildcard: Wildcard = Wildcard.none,
|
||||
|
||||
@@ -248,6 +248,130 @@ pub fn satisfiesPre(range: Range, version: Version, range_buf: string, version_b
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn intersects(range1: *const Range, range2: *const Range, range1_buf: string, range2_buf: string) bool {
|
||||
|
||||
// If either range has no constraints, they intersect
|
||||
if (!range1.hasLeft() or !range2.hasLeft()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special case: if either range accepts any version (>= 0.0.0), they intersect
|
||||
if (range1.anyRangeSatisfies() or range2.anyRangeSatisfies()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For two ranges to intersect, there must exist at least one version that satisfies both
|
||||
// We need to check if the ranges overlap by examining their bounds
|
||||
|
||||
// First, let's handle exact version matches (single comparator with op == eql)
|
||||
if (range1.left.op == .eql and !range1.hasRight() and range2.left.op == .eql and !range2.hasRight()) {
|
||||
// Both are exact versions, they only intersect if they're the same
|
||||
return range1.left.version.eql(range2.left.version);
|
||||
}
|
||||
|
||||
// If one is an exact version and the other is a range, check if the exact version satisfies the range
|
||||
if (range1.left.op == .eql and !range1.hasRight()) {
|
||||
// range1 is an exact version, check if it satisfies range2
|
||||
return range2.satisfies(range1.left.version, range2_buf, range1_buf);
|
||||
}
|
||||
if (range2.left.op == .eql and !range2.hasRight()) {
|
||||
// range2 is an exact version, check if it satisfies range1
|
||||
return range1.satisfies(range2.left.version, range1_buf, range2_buf);
|
||||
}
|
||||
|
||||
// Now handle general ranges
|
||||
// Two ranges intersect if their intervals overlap
|
||||
// We need to find the effective lower and upper bounds of each range
|
||||
|
||||
// For range1
|
||||
var r1_has_lower = false;
|
||||
var r1_lower_version: Version = undefined;
|
||||
var r1_lower_inclusive = false;
|
||||
var r1_has_upper = false;
|
||||
var r1_upper_version: Version = undefined;
|
||||
var r1_upper_inclusive = false;
|
||||
|
||||
if (range1.left.op == .gte or range1.left.op == .gt) {
|
||||
r1_has_lower = true;
|
||||
r1_lower_version = range1.left.version;
|
||||
r1_lower_inclusive = (range1.left.op == .gte);
|
||||
}
|
||||
|
||||
if (range1.hasRight()) {
|
||||
if (range1.right.op == .lte or range1.right.op == .lt) {
|
||||
r1_has_upper = true;
|
||||
r1_upper_version = range1.right.version;
|
||||
r1_upper_inclusive = (range1.right.op == .lte);
|
||||
}
|
||||
} else if (range1.left.op == .lte or range1.left.op == .lt) {
|
||||
// Single comparator with upper bound
|
||||
r1_has_upper = true;
|
||||
r1_upper_version = range1.left.version;
|
||||
r1_upper_inclusive = (range1.left.op == .lte);
|
||||
}
|
||||
|
||||
// For range2
|
||||
var r2_has_lower = false;
|
||||
var r2_lower_version: Version = undefined;
|
||||
var r2_lower_inclusive = false;
|
||||
var r2_has_upper = false;
|
||||
var r2_upper_version: Version = undefined;
|
||||
var r2_upper_inclusive = false;
|
||||
|
||||
if (range2.left.op == .gte or range2.left.op == .gt) {
|
||||
r2_has_lower = true;
|
||||
r2_lower_version = range2.left.version;
|
||||
r2_lower_inclusive = (range2.left.op == .gte);
|
||||
}
|
||||
|
||||
if (range2.hasRight()) {
|
||||
if (range2.right.op == .lte or range2.right.op == .lt) {
|
||||
r2_has_upper = true;
|
||||
r2_upper_version = range2.right.version;
|
||||
r2_upper_inclusive = (range2.right.op == .lte);
|
||||
}
|
||||
} else if (range2.left.op == .lte or range2.left.op == .lt) {
|
||||
// Single comparator with upper bound
|
||||
r2_has_upper = true;
|
||||
r2_upper_version = range2.left.version;
|
||||
r2_upper_inclusive = (range2.left.op == .lte);
|
||||
}
|
||||
|
||||
// Check if the ranges overlap
|
||||
// Case 1: Both have lower and upper bounds
|
||||
if (r1_has_lower and r1_has_upper and r2_has_lower and r2_has_upper) {
|
||||
// Check if r1's upper is below r2's lower
|
||||
const r1_upper_vs_r2_lower = r1_upper_version.orderWithoutBuild(r2_lower_version, "", "");
|
||||
if (r1_upper_vs_r2_lower == .lt) return false;
|
||||
if (r1_upper_vs_r2_lower == .eq and (!r1_upper_inclusive or !r2_lower_inclusive)) return false;
|
||||
|
||||
// Check if r2's upper is below r1's lower
|
||||
const r2_upper_vs_r1_lower = r2_upper_version.orderWithoutBuild(r1_lower_version, "", "");
|
||||
if (r2_upper_vs_r1_lower == .lt) return false;
|
||||
if (r2_upper_vs_r1_lower == .eq and (!r2_upper_inclusive or !r1_lower_inclusive)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Case 2: One or both ranges are unbounded on one side
|
||||
if (r1_has_lower and r2_has_upper) {
|
||||
// Check if r2's upper is below r1's lower
|
||||
const order = r2_upper_version.orderWithoutBuild(r1_lower_version, "", "");
|
||||
if (order == .lt) return false;
|
||||
if (order == .eq and (!r2_upper_inclusive or !r1_lower_inclusive)) return false;
|
||||
}
|
||||
|
||||
if (r2_has_lower and r1_has_upper) {
|
||||
// Check if r1's upper is below r2's lower
|
||||
const order = r1_upper_version.orderWithoutBuild(r2_lower_version, "", "");
|
||||
if (order == .lt) return false;
|
||||
if (order == .eq and (!r1_upper_inclusive or !r2_lower_inclusive)) return false;
|
||||
}
|
||||
|
||||
// If we get here, the ranges intersect
|
||||
return true;
|
||||
}
|
||||
|
||||
const string = []const u8;
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
@@ -277,6 +277,130 @@ 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;
|
||||
const id = if (identifier) |i| if (i.len > 0) i else "0" else "0";
|
||||
try pre_strings.writer().print("{s}.0", .{id});
|
||||
new_version.tag.pre = ExternalString.from(pre_strings.items);
|
||||
},
|
||||
.preminor => {
|
||||
new_version.minor +|= 1;
|
||||
new_version.patch = 0;
|
||||
const id = if (identifier) |i| if (i.len > 0) i else "0" else "0";
|
||||
try pre_strings.writer().print("{s}.0", .{id});
|
||||
new_version.tag.pre = ExternalString.from(pre_strings.items);
|
||||
},
|
||||
.prepatch => {
|
||||
new_version.patch +|= 1;
|
||||
const id = if (identifier) |i| if (i.len > 0) i else "0" else "0";
|
||||
try pre_strings.writer().print("{s}.0", .{id});
|
||||
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;
|
||||
const id = if (identifier) |i| if (i.len > 0) i else "0" else "0";
|
||||
try pre_strings.writer().print("{s}.0", .{id});
|
||||
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) catch null) |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) catch null) |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, // ~
|
||||
@@ -612,6 +736,33 @@ pub const Version = extern struct {
|
||||
return !this.build.isEmpty();
|
||||
}
|
||||
|
||||
pub fn toComponentsArray(
|
||||
self: Tag,
|
||||
is_pre: bool,
|
||||
globalThis: *jsc.JSGlobalObject,
|
||||
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.jsNull();
|
||||
}
|
||||
|
||||
const array = try jsc.JSValue.createEmptyArray(globalThis, 0);
|
||||
|
||||
var it = strings.split(tag_str, ".");
|
||||
var i: u32 = 0;
|
||||
while (it.next()) |part| {
|
||||
if (std.fmt.parseUnsigned(u64, part, 10) catch null) |num| {
|
||||
try array.putIndex(globalThis, @intCast(i), jsc.JSValue.jsNumber(@as(f64, @floatFromInt(num))));
|
||||
} else {
|
||||
try array.putIndex(globalThis, @intCast(i), try bun.String.createUTF8ForJS(globalThis, part));
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
pub fn eql(lhs: Tag, rhs: Tag) bool {
|
||||
return lhs.pre.hash == rhs.pre.hash;
|
||||
}
|
||||
@@ -994,6 +1145,7 @@ const Environment = bun.Environment;
|
||||
const Output = bun.Output;
|
||||
const assert = bun.assert;
|
||||
const strings = bun.strings;
|
||||
const jsc = bun.jsc;
|
||||
|
||||
const ExternalString = bun.Semver.ExternalString;
|
||||
const Query = bun.Semver.Query;
|
||||
|
||||
@@ -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,636 @@ 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);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bun.semver.intersects()", () => {
|
||||
test("returns true for overlapping ranges", () => {
|
||||
expect(Bun.semver.intersects("^1.0.0", "^1.2.0")).toBe(true);
|
||||
expect(Bun.semver.intersects(">=1.0.0", ">=1.5.0")).toBe(true);
|
||||
expect(Bun.semver.intersects("1.x", "1.2.x")).toBe(true);
|
||||
expect(Bun.semver.intersects("~1.2.3", "^1.0.0")).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for non-overlapping ranges", () => {
|
||||
expect(Bun.semver.intersects("^1.0.0", "^2.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("<1.0.0", ">=2.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("1.0.0", "2.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("~1.2.3", "~1.3.0")).toBe(false);
|
||||
});
|
||||
|
||||
test("returns true for exact version matches", () => {
|
||||
expect(Bun.semver.intersects("1.2.3", "1.2.3")).toBe(true);
|
||||
expect(Bun.semver.intersects("=1.2.3", "1.2.3")).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for exact version mismatches", () => {
|
||||
expect(Bun.semver.intersects("1.2.3", "1.2.4")).toBe(false);
|
||||
expect(Bun.semver.intersects("=1.2.3", "=1.2.4")).toBe(false);
|
||||
});
|
||||
|
||||
test("handles complex ranges", () => {
|
||||
expect(Bun.semver.intersects(">=1.2.3 <2.0.0", "^1.5.0")).toBe(true);
|
||||
expect(Bun.semver.intersects(">=1.0.0 <1.5.0", ">=1.4.0 <2.0.0")).toBe(true);
|
||||
expect(Bun.semver.intersects(">=1.0.0 <1.5.0", ">=1.5.0 <2.0.0")).toBe(false);
|
||||
});
|
||||
|
||||
test("handles OR'd ranges", () => {
|
||||
expect(Bun.semver.intersects("^1.0.0 || ^2.0.0", "^1.5.0")).toBe(true);
|
||||
expect(Bun.semver.intersects("^1.0.0 || ^2.0.0", "^2.5.0")).toBe(true);
|
||||
expect(Bun.semver.intersects("^1.0.0 || ^2.0.0", "^3.0.0")).toBe(false);
|
||||
});
|
||||
|
||||
test("handles wildcard ranges", () => {
|
||||
expect(Bun.semver.intersects("*", "1.2.3")).toBe(true);
|
||||
expect(Bun.semver.intersects("1.*", "1.2.3")).toBe(true);
|
||||
expect(Bun.semver.intersects("1.2.*", "1.2.3")).toBe(true);
|
||||
expect(Bun.semver.intersects("2.*", "1.2.3")).toBe(false);
|
||||
});
|
||||
|
||||
test("handles hyphen ranges", () => {
|
||||
expect(Bun.semver.intersects("1.0.0 - 2.0.0", "1.5.0")).toBe(true);
|
||||
expect(Bun.semver.intersects("1.0.0 - 2.0.0", "0.5.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("1.0.0 - 2.0.0", "2.5.0")).toBe(false);
|
||||
});
|
||||
|
||||
test("handles empty ranges", () => {
|
||||
expect(Bun.semver.intersects("", "")).toBe(false);
|
||||
expect(Bun.semver.intersects("", "1.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("1.0.0", "")).toBe(false);
|
||||
});
|
||||
|
||||
test("handles boundary cases", () => {
|
||||
expect(Bun.semver.intersects(">1.0.0", ">=1.0.0")).toBe(true);
|
||||
expect(Bun.semver.intersects(">1.0.0", "1.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects(">=1.0.0", "1.0.0")).toBe(true);
|
||||
expect(Bun.semver.intersects("<2.0.0", "<=2.0.0")).toBe(true);
|
||||
expect(Bun.semver.intersects("<2.0.0", "2.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("<=2.0.0", "2.0.0")).toBe(true);
|
||||
});
|
||||
|
||||
// Existing simplified test
|
||||
test("returns true for most cases (simplified)", () => {
|
||||
// Note: "not-a-range" is parsed as an exact version requirement
|
||||
expect(Bun.semver.intersects("^1.0.0", "not-a-range")).toBe(true);
|
||||
// Both arguments are parsed as exact version requirements
|
||||
expect(Bun.semver.intersects("not-a-range", "^1.0.0")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bun.semver.maxSatisfying()", () => {
|
||||
test("finds the highest satisfying version", () => {
|
||||
const versions = ["1.0.0", "1.2.0", "1.3.0", "2.0.0"];
|
||||
expect(Bun.semver.maxSatisfying(versions, "^1.0.0")).toBe("1.3.0");
|
||||
expect(Bun.semver.maxSatisfying(versions, "~1.2.0")).toBe("1.2.0");
|
||||
expect(Bun.semver.maxSatisfying(versions, ">=2.0.0")).toBe("2.0.0");
|
||||
});
|
||||
|
||||
test("returns null if no version satisfies", () => {
|
||||
const versions = ["1.0.0", "1.1.0", "1.2.0"];
|
||||
expect(Bun.semver.maxSatisfying(versions, "^2.0.0")).toBe(null);
|
||||
});
|
||||
|
||||
test("handles empty array", () => {
|
||||
expect(Bun.semver.maxSatisfying([], "^1.0.0")).toBe(null);
|
||||
});
|
||||
|
||||
test("skips invalid versions", () => {
|
||||
const versions = ["1.0.0", "invalid", "1.2.0"];
|
||||
expect(Bun.semver.maxSatisfying(versions, "^1.0.0")).toBe("1.2.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bun.semver.minSatisfying()", () => {
|
||||
test("finds the lowest satisfying version", () => {
|
||||
const versions = ["1.0.0", "1.2.0", "1.3.0", "2.0.0"];
|
||||
expect(Bun.semver.minSatisfying(versions, "^1.0.0")).toBe("1.0.0");
|
||||
expect(Bun.semver.minSatisfying(versions, ">=1.2.0")).toBe("1.2.0");
|
||||
});
|
||||
|
||||
test("returns null if no version satisfies", () => {
|
||||
const versions = ["1.0.0", "1.1.0", "1.2.0"];
|
||||
expect(Bun.semver.minSatisfying(versions, "^2.0.0")).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bun.semver.simplifyRange()", () => {
|
||||
test("simplifies OR'd exact versions to tilde range", () => {
|
||||
expect(Bun.semver.simplifyRange(["1.0.0", "1.0.1", "1.0.2"], "1.0.0 || 1.0.1 || 1.0.2")).toBe("~1.0.0");
|
||||
});
|
||||
|
||||
test("simplifies OR'd minor versions to caret range", () => {
|
||||
expect(Bun.semver.simplifyRange(["1.0.0", "1.1.0", "1.2.0"], "1.0.0 || 1.1.0 || 1.2.0")).toBe("^1.0.0");
|
||||
});
|
||||
|
||||
test("returns original range if can't simplify", () => {
|
||||
expect(Bun.semver.simplifyRange(["1.0.0", "2.0.0", "3.0.0"], "1.0.0 || 2.0.0 || 3.0.0")).toBe(
|
||||
"1.0.0 || 2.0.0 || 3.0.0",
|
||||
);
|
||||
});
|
||||
|
||||
test("returns original range if already simple", () => {
|
||||
expect(Bun.semver.simplifyRange(["1.0.0", "1.0.1", "1.0.2"], "^1.0.0")).toBe("^1.0.0");
|
||||
});
|
||||
|
||||
test("simplifies to range format when appropriate", () => {
|
||||
const versions = ["1.0.0", "1.0.1", "1.0.2", "1.0.3", "1.0.4"];
|
||||
expect(Bun.semver.simplifyRange(versions, "1.0.0 || 1.0.1 || 1.0.2 || 1.0.3 || 1.0.4")).toBe("~1.0.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bun.semver.validRange()", () => {
|
||||
test("returns range for valid ranges", () => {
|
||||
expect(Bun.semver.validRange("^1.0.0")).toBe("^1.0.0");
|
||||
expect(Bun.semver.validRange("~1.2.3")).toBe("~1.2.3");
|
||||
expect(Bun.semver.validRange(">=1.0.0 <2.0.0")).toBe(">=1.0.0 <2.0.0");
|
||||
});
|
||||
|
||||
test("returns null for invalid ranges", () => {
|
||||
expect(Bun.semver.validRange("not-a-range")).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
// Comprehensive negative tests
|
||||
describe("Bun.semver negative tests", () => {
|
||||
describe("major() negative tests", () => {
|
||||
test("returns null for non-string inputs", () => {
|
||||
expect(Bun.semver.major(123 as any)).toBe(null);
|
||||
expect(Bun.semver.major(null as any)).toBe(null);
|
||||
expect(Bun.semver.major(undefined as any)).toBe(null);
|
||||
expect(Bun.semver.major({} as any)).toBe(null);
|
||||
expect(Bun.semver.major([] as any)).toBe(null);
|
||||
expect(Bun.semver.major(true as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid version strings", () => {
|
||||
expect(Bun.semver.major("")).toBe(null);
|
||||
expect(Bun.semver.major("not-a-version")).toBe(null);
|
||||
expect(Bun.semver.major("1.2.3.4")).toBe(null);
|
||||
expect(Bun.semver.major("a.b.c")).toBe(null);
|
||||
expect(Bun.semver.major("-1.2.3")).toBe(null);
|
||||
expect(Bun.semver.major("1.-2.3")).toBe(null);
|
||||
// Parser stops at the negative sign
|
||||
expect(Bun.semver.major("1.2.-3")).toBe(1);
|
||||
expect(Bun.semver.major("@#$%")).toBe(null);
|
||||
expect(Bun.semver.major("v")).toBe(null);
|
||||
// Parser accepts "vv1.2.3" and parses the 1.2.3 part
|
||||
expect(Bun.semver.major("vv1.2.3")).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("minor() negative tests", () => {
|
||||
test("returns null for non-string inputs", () => {
|
||||
expect(Bun.semver.minor(123 as any)).toBe(null);
|
||||
expect(Bun.semver.minor(null as any)).toBe(null);
|
||||
expect(Bun.semver.minor(undefined as any)).toBe(null);
|
||||
expect(Bun.semver.minor({} as any)).toBe(null);
|
||||
expect(Bun.semver.minor([] as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid version strings", () => {
|
||||
expect(Bun.semver.minor("")).toBe(null);
|
||||
expect(Bun.semver.minor("not-a-version")).toBe(null);
|
||||
expect(Bun.semver.minor("1.2.3.4")).toBe(null);
|
||||
expect(Bun.semver.minor("a.b.c")).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("patch() negative tests", () => {
|
||||
test("returns null for non-string inputs", () => {
|
||||
expect(Bun.semver.patch(123 as any)).toBe(null);
|
||||
expect(Bun.semver.patch(null as any)).toBe(null);
|
||||
expect(Bun.semver.patch(undefined as any)).toBe(null);
|
||||
expect(Bun.semver.patch({} as any)).toBe(null);
|
||||
expect(Bun.semver.patch([] as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid version strings", () => {
|
||||
expect(Bun.semver.patch("")).toBe(null);
|
||||
expect(Bun.semver.patch("not-a-version")).toBe(null);
|
||||
expect(Bun.semver.patch("1.2.3.4")).toBe(null);
|
||||
expect(Bun.semver.patch("a.b.c")).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("prerelease() negative tests", () => {
|
||||
test("returns null for non-string inputs", () => {
|
||||
expect(Bun.semver.prerelease(123 as any)).toBe(null);
|
||||
expect(Bun.semver.prerelease(null as any)).toBe(null);
|
||||
expect(Bun.semver.prerelease(undefined as any)).toBe(null);
|
||||
expect(Bun.semver.prerelease({} as any)).toBe(null);
|
||||
expect(Bun.semver.prerelease([] as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid version strings", () => {
|
||||
expect(Bun.semver.prerelease("")).toBe(null);
|
||||
expect(Bun.semver.prerelease("not-a-version")).toBe(null);
|
||||
expect(Bun.semver.prerelease("1.2.3.4")).toBe(null);
|
||||
expect(Bun.semver.prerelease("a.b.c")).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parse() negative tests", () => {
|
||||
test("returns null for non-string inputs", () => {
|
||||
expect(Bun.semver.parse(123 as any)).toBe(null);
|
||||
expect(Bun.semver.parse(null as any)).toBe(null);
|
||||
expect(Bun.semver.parse(undefined as any)).toBe(null);
|
||||
expect(Bun.semver.parse({} as any)).toBe(null);
|
||||
expect(Bun.semver.parse([] as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid version strings", () => {
|
||||
expect(Bun.semver.parse("")).toBe(null);
|
||||
expect(Bun.semver.parse("not-a-version")).toBe(null);
|
||||
expect(Bun.semver.parse("1.2.3.4")).toBe(null);
|
||||
expect(Bun.semver.parse("a.b.c")).toBe(null);
|
||||
// Note: parser accepts trailing - and + as empty prerelease/build
|
||||
// expect(Bun.semver.parse("1.2.3-")).toBe(null);
|
||||
// expect(Bun.semver.parse("1.2.3+")).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("bump() negative tests", () => {
|
||||
test("returns null for non-string version inputs", () => {
|
||||
expect(Bun.semver.bump(123 as any, "major")).toBe(null);
|
||||
expect(Bun.semver.bump(null as any, "major")).toBe(null);
|
||||
expect(Bun.semver.bump(undefined as any, "major")).toBe(null);
|
||||
expect(Bun.semver.bump({} as any, "major")).toBe(null);
|
||||
expect(Bun.semver.bump([] as any, "major")).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid version strings", () => {
|
||||
expect(Bun.semver.bump("", "major")).toBe(null);
|
||||
expect(Bun.semver.bump("not-a-version", "major")).toBe(null);
|
||||
expect(Bun.semver.bump("1.2.3.4", "major")).toBe(null);
|
||||
expect(Bun.semver.bump("a.b.c", "major")).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid release types", () => {
|
||||
expect(Bun.semver.bump("1.2.3", "invalid" as any)).toBe(null);
|
||||
expect(Bun.semver.bump("1.2.3", "" as any)).toBe(null);
|
||||
expect(Bun.semver.bump("1.2.3", null as any)).toBe(null);
|
||||
expect(Bun.semver.bump("1.2.3", undefined as any)).toBe(null);
|
||||
expect(Bun.semver.bump("1.2.3", 123 as any)).toBe(null);
|
||||
expect(Bun.semver.bump("1.2.3", {} as any)).toBe(null);
|
||||
expect(Bun.semver.bump("1.2.3", [] as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("handles invalid identifier types", () => {
|
||||
// Note: non-string identifiers are converted to strings
|
||||
expect(Bun.semver.bump("1.2.3", "prerelease", 123 as any)).toBe("1.2.4-123.0");
|
||||
expect(Bun.semver.bump("1.2.3", "prerelease", {} as any)).toBe("1.2.4-[object Object].0");
|
||||
expect(Bun.semver.bump("1.2.3", "prerelease", [] as any)).toBe("1.2.4-0.0");
|
||||
expect(Bun.semver.bump("1.2.3", "prerelease", true as any)).toBe("1.2.4-true.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("intersects() negative tests", () => {
|
||||
test("returns false for non-string inputs", () => {
|
||||
expect(Bun.semver.intersects(123 as any, "^1.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("^1.0.0", 123 as any)).toBe(false);
|
||||
expect(Bun.semver.intersects(null as any, "^1.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("^1.0.0", null as any)).toBe(false);
|
||||
expect(Bun.semver.intersects(undefined as any, "^1.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("^1.0.0", undefined as any)).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false for invalid range strings", () => {
|
||||
expect(Bun.semver.intersects("", "^1.0.0")).toBe(false);
|
||||
expect(Bun.semver.intersects("^1.0.0", "")).toBe(false);
|
||||
// "not-a-range" is parsed as an exact version requirement
|
||||
expect(Bun.semver.intersects("not-a-range", "^1.0.0")).toBe(true);
|
||||
// Both arguments are parsed as exact version requirements
|
||||
expect(Bun.semver.intersects("^1.0.0", "not-a-range")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("maxSatisfying() negative tests", () => {
|
||||
test("throws for non-array first argument", () => {
|
||||
expect(() => Bun.semver.maxSatisfying("not-an-array" as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.maxSatisfying(123 as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.maxSatisfying(null as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.maxSatisfying(undefined as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.maxSatisfying({} as any, "^1.0.0")).toThrow();
|
||||
});
|
||||
|
||||
test("returns null for non-string range", () => {
|
||||
expect(Bun.semver.maxSatisfying(["1.0.0"], 123 as any)).toBe(null);
|
||||
expect(Bun.semver.maxSatisfying(["1.0.0"], null as any)).toBe(null);
|
||||
expect(Bun.semver.maxSatisfying(["1.0.0"], undefined as any)).toBe(null);
|
||||
expect(Bun.semver.maxSatisfying(["1.0.0"], {} as any)).toBe(null);
|
||||
expect(Bun.semver.maxSatisfying(["1.0.0"], [] as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("skips non-string versions in array", () => {
|
||||
expect(Bun.semver.maxSatisfying([123, "1.0.0", null, "2.0.0", undefined, {}] as any, "^1.0.0")).toBe("1.0.0");
|
||||
});
|
||||
|
||||
test("returns null for invalid range strings", () => {
|
||||
expect(Bun.semver.maxSatisfying(["1.0.0", "2.0.0"], "")).toBe(null);
|
||||
// "not-a-range" is parsed as an exact version requirement
|
||||
expect(Bun.semver.maxSatisfying(["1.0.0", "2.0.0"], "not-a-range")).toBe("2.0.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("minSatisfying() negative tests", () => {
|
||||
test("throws for non-array first argument", () => {
|
||||
expect(() => Bun.semver.minSatisfying("not-an-array" as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.minSatisfying(123 as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.minSatisfying(null as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.minSatisfying(undefined as any, "^1.0.0")).toThrow();
|
||||
});
|
||||
|
||||
test("returns null for non-string range", () => {
|
||||
expect(Bun.semver.minSatisfying(["1.0.0"], 123 as any)).toBe(null);
|
||||
expect(Bun.semver.minSatisfying(["1.0.0"], null as any)).toBe(null);
|
||||
expect(Bun.semver.minSatisfying(["1.0.0"], undefined as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("skips non-string versions in array", () => {
|
||||
expect(Bun.semver.minSatisfying([123, "2.0.0", null, "1.0.0", undefined, {}] as any, "^1.0.0")).toBe("1.0.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("simplifyRange() negative tests", () => {
|
||||
test("throws for non-array first argument", () => {
|
||||
expect(() => Bun.semver.simplifyRange("not-an-array" as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.simplifyRange(123 as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.simplifyRange(null as any, "^1.0.0")).toThrow();
|
||||
expect(() => Bun.semver.simplifyRange(undefined as any, "^1.0.0")).toThrow();
|
||||
});
|
||||
|
||||
test("returns null for non-string range", () => {
|
||||
expect(Bun.semver.simplifyRange(["1.0.0"], 123 as any)).toBe(null);
|
||||
expect(Bun.semver.simplifyRange(["1.0.0"], null as any)).toBe(null);
|
||||
expect(Bun.semver.simplifyRange(["1.0.0"], undefined as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns original range for empty version array", () => {
|
||||
expect(Bun.semver.simplifyRange([], "^1.0.0")).toBe("^1.0.0");
|
||||
});
|
||||
|
||||
test("handles invalid versions in array", () => {
|
||||
expect(Bun.semver.simplifyRange(["invalid", "1.0.0", "not-a-version"], "^1.0.0")).toBe("^1.0.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("validRange() negative tests", () => {
|
||||
test("returns null for non-string inputs", () => {
|
||||
expect(Bun.semver.validRange(123 as any)).toBe(null);
|
||||
expect(Bun.semver.validRange(null as any)).toBe(null);
|
||||
expect(Bun.semver.validRange(undefined as any)).toBe(null);
|
||||
expect(Bun.semver.validRange({} as any)).toBe(null);
|
||||
expect(Bun.semver.validRange([] as any)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for invalid range strings", () => {
|
||||
expect(Bun.semver.validRange("")).toBe(null);
|
||||
expect(Bun.semver.validRange("not-a-range")).toBe(null);
|
||||
expect(Bun.semver.validRange("@#$%")).toBe(null);
|
||||
expect(Bun.semver.validRange("!!!")).toBe(null);
|
||||
expect(Bun.semver.validRange("invalid range")).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases and boundary conditions", () => {
|
||||
test("handles very large version numbers", () => {
|
||||
const largeVersion = "999999999.999999999.999999999";
|
||||
expect(Bun.semver.major(largeVersion)).toBe(999999999);
|
||||
expect(Bun.semver.minor(largeVersion)).toBe(999999999);
|
||||
expect(Bun.semver.patch(largeVersion)).toBe(999999999);
|
||||
});
|
||||
|
||||
test("handles version numbers at max safe integer", () => {
|
||||
const maxVersion = `${Number.MAX_SAFE_INTEGER}.0.0`;
|
||||
// Note: Version numbers are parsed as u32, so MAX_SAFE_INTEGER overflows
|
||||
expect(Bun.semver.major(maxVersion)).toBe(0);
|
||||
});
|
||||
|
||||
test("handles empty arrays", () => {
|
||||
expect(Bun.semver.maxSatisfying([], "^1.0.0")).toBe(null);
|
||||
expect(Bun.semver.minSatisfying([], "^1.0.0")).toBe(null);
|
||||
// Note: simplifyRange now expects two arguments: versions array and range
|
||||
expect(Bun.semver.simplifyRange([], "^1.0.0")).toBe("^1.0.0");
|
||||
});
|
||||
|
||||
test("handles arrays with all invalid versions", () => {
|
||||
expect(Bun.semver.maxSatisfying(["not", "valid", "versions"], "^1.0.0")).toBe(null);
|
||||
expect(Bun.semver.minSatisfying(["not", "valid", "versions"], "^1.0.0")).toBe(null);
|
||||
});
|
||||
|
||||
test("handles very long prerelease identifiers", () => {
|
||||
const longPre = "1.2.3-" + "a".repeat(1000);
|
||||
const parsed = Bun.semver.parse(longPre);
|
||||
expect(parsed).not.toBe(null);
|
||||
expect(parsed.prerelease).toHaveLength(1);
|
||||
expect(parsed.prerelease[0]).toBe("a".repeat(1000));
|
||||
});
|
||||
|
||||
test("handles deeply nested prerelease identifiers", () => {
|
||||
const deepPre = "1.2.3-a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p";
|
||||
const parsed = Bun.semver.parse(deepPre);
|
||||
expect(parsed).not.toBe(null);
|
||||
expect(parsed.prerelease).toHaveLength(16);
|
||||
});
|
||||
|
||||
test("handles unicode in prerelease (should fail)", () => {
|
||||
expect(Bun.semver.parse("1.2.3-α")).toBe(null);
|
||||
expect(Bun.semver.parse("1.2.3-🚀")).toBe(null);
|
||||
expect(Bun.semver.parse("1.2.3-中文")).toBe(null);
|
||||
});
|
||||
|
||||
test("handles whitespace in various positions", () => {
|
||||
// Note: parser is lenient with leading/trailing whitespace
|
||||
// expect(Bun.semver.parse(" 1.2.3")).toBe(null);
|
||||
// expect(Bun.semver.parse("1.2.3 ")).toBe(null);
|
||||
// Parser stops at spaces
|
||||
expect(Bun.semver.parse("1. 2.3")).not.toBe(null);
|
||||
expect(Bun.semver.parse("1.2. 3")).not.toBe(null);
|
||||
expect(Bun.semver.parse("1.2.3- alpha")).not.toBe(null);
|
||||
});
|
||||
|
||||
test("handles missing arguments", () => {
|
||||
expect((Bun.semver.major as any)()).toBe(null);
|
||||
expect((Bun.semver.minor as any)()).toBe(null);
|
||||
expect((Bun.semver.patch as any)()).toBe(null);
|
||||
expect((Bun.semver.prerelease as any)()).toBe(null);
|
||||
expect((Bun.semver.parse as any)()).toBe(null);
|
||||
expect((Bun.semver.bump as any)()).toBe(null);
|
||||
expect((Bun.semver.bump as any)("1.2.3")).toBe(null);
|
||||
expect((Bun.semver.intersects as any)()).toBe(false);
|
||||
expect((Bun.semver.intersects as any)("^1.0.0")).toBe(false);
|
||||
// Returns null when called without arguments
|
||||
expect((Bun.semver.maxSatisfying as any)()).toBe(null);
|
||||
expect((Bun.semver.maxSatisfying as any)(["1.0.0"])).toBe(null);
|
||||
// Returns null when called without arguments
|
||||
expect((Bun.semver.minSatisfying as any)()).toBe(null);
|
||||
expect((Bun.semver.minSatisfying as any)(["1.0.0"])).toBe(null);
|
||||
expect((Bun.semver.simplifyRange as any)()).toBe(null);
|
||||
expect((Bun.semver.simplifyRange as any)(["1.0.0"])).toBe(null);
|
||||
expect((Bun.semver.validRange as any)()).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user