import { DeepMap } from 'deep-equality-data-structures'; import * as P from './exver'; /** * Compile-time utility type that validates a version string literal conforms to semver format. * * Resolves to `unknown` if valid, `never` if invalid. Used with {@link testTypeVersion}. * * @example * ```ts * type Valid = ValidateVersion<"1.2.3"> // unknown (valid) * type Invalid = ValidateVersion<"-3"> // never (invalid) * ``` */ export type ValidateVersion = T extends `-${infer A}` ? never : T extends `${infer A}-${string}` ? ValidateVersion : T extends `${bigint}` ? unknown : T extends `${bigint}.${infer A}` ? ValidateVersion : never; /** * Compile-time utility type that validates an extended version string literal. * * Extended versions have the format `upstream:downstream` or `#flavor:upstream:downstream`. * * @example * ```ts * type Valid = ValidateExVer<"1.2.3:0"> // valid * type Flavored = ValidateExVer<"#bitcoin:1.0:0"> // valid * type Bad = ValidateExVer<"1.2-3"> // never (invalid) * ``` */ export type ValidateExVer = T extends `#${string}:${infer A}:${infer B}` ? ValidateVersion & ValidateVersion : T extends `${infer A}:${infer B}` ? ValidateVersion & ValidateVersion : never; /** * Validates a tuple of extended version string literals at compile time. * * @example * ```ts * type Valid = ValidateExVers<["1.0:0", "2.0:0"]> // valid * ``` */ export type ValidateExVers = T extends [] ? unknown[] : T extends [infer A, ...infer B] ? ValidateExVer & ValidateExVers : never[]; type Anchor = { type: 'Anchor'; operator: P.CmpOp; version: ExtendedVersion; }; type And = { type: 'And'; left: VersionRange; right: VersionRange; }; type Or = { type: 'Or'; left: VersionRange; right: VersionRange; }; type Not = { type: 'Not'; value: VersionRange; }; type Flavor = { type: 'Flavor'; flavor: string | null; }; type FlavorNot = { type: 'FlavorNot'; flavors: Set; }; type FlavorAtom = Flavor | FlavorNot; /** * Splits a number line of versions in half, so that every possible semver is either to the left or right. * The `side` field handles inclusively. * * # Example * Consider the version `1.2.3`. For side=-1 the version point is like `1.2.2.999*.999*.**` (that is, 1.2.3.0.0.** is greater) and * for side=+1 the point is like `1.2.3.0.0.**.1` (that is, 1.2.3.0.0.** is less). */ type VersionRangePoint = { upstream: Version; downstream: Version; side: -1 | 1; }; /** * Truth tables for version numbers and flavors. For each flavor we need a separate table, which * is quite straightforward. But in order to exhaustively enumerate the boolean values of every * combination of flavors and versions we also need tables for flavor negations. */ type VersionRangeTables = DeepMap | boolean; /** * A truth table for version numbers. This is easiest to picture as a number line, cut up into * ranges of versions between version points. */ declare class VersionRangeTable { protected points: Array; protected values: boolean[]; private constructor(); static zip(a: VersionRangeTable, b: VersionRangeTable, func: (a: boolean, b: boolean) => boolean): VersionRangeTable; /** * Creates a version table which is `true` for the given flavor, and `false` for any other flavor. */ static eqFlavor(flavor: string | null): VersionRangeTables; /** * Creates a version table with exactly two ranges (to the left and right of the given point) and with `false` for any other flavor. * This is easiest to understand by looking at `VersionRange.tables`. */ static cmpPoint(flavor: string | null, point: VersionRangePoint, left: boolean, right: boolean): VersionRangeTables; /** * Helper for `cmpPoint`. */ static cmp(version: ExtendedVersion, side: -1 | 1, left: boolean, right: boolean): VersionRangeTables; static not(tables: VersionRangeTables): boolean | DeepMap; static and(a_tables: VersionRangeTables, b_tables: VersionRangeTables): VersionRangeTables; static or(...in_tables: VersionRangeTables[]): VersionRangeTables; /** * If this is true for all versions or false for all versions, returen that value. Otherwise return null. */ static collapse(tables: VersionRangeTables): boolean | null; /** * Expresses this truth table as a series of version range operators. * https://en.wikipedia.org/wiki/Canonical_normal_form#Minterms */ static minterms(tables: VersionRangeTables): VersionRange; } /** * Represents a parsed version range expression used to match against {@link Version} or {@link ExtendedVersion} values. * * Version ranges support standard comparison operators (`=`, `>`, `<`, `>=`, `<=`, `!=`), * caret (`^`) and tilde (`~`) ranges, boolean logic (`&&`, `||`, `!`), and flavor matching (`#flavor`). * * @example * ```ts * const range = VersionRange.parse(">=1.0.0:0 && <2.0.0:0") * const version = ExtendedVersion.parse("1.5.0:0") * console.log(range.satisfiedBy(version)) // true * * // Combine ranges with boolean logic * const combined = VersionRange.and( * VersionRange.parse(">=1.0:0"), * VersionRange.parse("<3.0:0"), * ) * * // Match a specific flavor * const flavored = VersionRange.parse("#bitcoin") * ``` */ export declare class VersionRange { atom: Anchor | And | Or | Not | P.Any | P.None | Flavor; constructor(atom: Anchor | And | Or | Not | P.Any | P.None | Flavor); toStringParens(parent: 'And' | 'Or' | 'Not'): string; /** Serializes this version range back to its canonical string representation. */ toString(): string; private static parseAtom; private static parseRange; /** * Parses a version range string into a `VersionRange`. * * @param range - A version range expression, e.g. `">=1.0.0:0 && <2.0.0:0"`, `"^1.2:0"`, `"*"` * @returns The parsed `VersionRange` * @throws If the string is not a valid version range expression */ static parse(range: string): VersionRange; /** * Creates a version range from a comparison operator and an {@link ExtendedVersion}. * * @param operator - One of `"="`, `">"`, `"<"`, `">="`, `"<="`, `"!="`, `"^"`, `"~"` * @param version - The version to compare against */ static anchor(operator: P.CmpOp, version: ExtendedVersion): VersionRange; /** * Creates a version range that matches only versions with the specified flavor. * * @param flavor - The flavor string to match, or `null` for the default (unflavored) variant */ static flavor(flavor: string | null): VersionRange; /** * Parses a legacy "emver" format version range string. * * @param range - A version range in the legacy emver format * @returns The parsed `VersionRange` */ static parseEmver(range: string): VersionRange; /** Returns the intersection of this range with another (logical AND). */ and(right: VersionRange): VersionRange; /** Returns the union of this range with another (logical OR). */ or(right: VersionRange): VersionRange; /** Returns the negation of this range (logical NOT). */ not(): VersionRange; /** * Returns the logical AND (intersection) of multiple version ranges. * Short-circuits on `none()` and skips `any()`. */ static and(...xs: Array): VersionRange; /** * Returns the logical OR (union) of multiple version ranges. * Short-circuits on `any()` and skips `none()`. */ static or(...xs: Array): VersionRange; /** Returns a version range that matches all versions (wildcard `*`). */ static any(): VersionRange; /** Returns a version range that matches no versions (`!`). */ static none(): VersionRange; /** * Returns `true` if the given version satisfies this range. * * @param version - A {@link Version} or {@link ExtendedVersion} to test */ satisfiedBy(version: Version | ExtendedVersion): boolean; tables(): VersionRangeTables; /** Returns `true` if any version exists that could satisfy this range. */ satisfiable(): boolean; /** Returns `true` if this range and `other` share at least one satisfying version. */ intersects(other: VersionRange): boolean; /** * Returns a canonical (simplified) form of this range using minterm expansion. * Useful for normalizing complex boolean expressions into a minimal representation. */ normalize(): VersionRange; } /** * Represents a semantic version number with numeric segments and optional prerelease identifiers. * * Follows semver precedence rules: numeric segments are compared left-to-right, * and a version with prerelease identifiers has lower precedence than the same version without. * * @example * ```ts * const v = Version.parse("1.2.3") * console.log(v.toString()) // "1.2.3" * console.log(v.compare(Version.parse("1.3.0"))) // "less" * * const pre = Version.parse("2.0.0-beta.1") * console.log(pre.compare(Version.parse("2.0.0"))) // "less" (prerelease < release) * ``` */ export declare class Version { /** The numeric version segments (e.g. `[1, 2, 3]` for `"1.2.3"`). */ number: number[]; /** Optional prerelease identifiers (e.g. `["beta", 1]` for `"-beta.1"`). */ prerelease: (string | number)[]; constructor( /** The numeric version segments (e.g. `[1, 2, 3]` for `"1.2.3"`). */ number: number[], /** Optional prerelease identifiers (e.g. `["beta", 1]` for `"-beta.1"`). */ prerelease: (string | number)[]); /** Serializes this version to its string form (e.g. `"1.2.3"` or `"1.0.0-beta.1"`). */ toString(): string; /** * Compares this version against another using semver precedence rules. * * @param other - The version to compare against * @returns `'greater'`, `'equal'`, or `'less'` */ compare(other: Version): 'greater' | 'equal' | 'less'; /** * Compares two versions, returning a numeric value suitable for use with `Array.sort()`. * * @returns `-1` if less, `0` if equal, `1` if greater */ compareForSort(other: Version): -1 | 0 | 1; /** * Parses a version string into a `Version` instance. * * @param version - A semver-compatible string, e.g. `"1.2.3"` or `"1.0.0-beta.1"` * @throws If the string is not a valid version */ static parse(version: string): Version; /** * Returns `true` if this version satisfies the given {@link VersionRange}. * Internally treats this as an unflavored {@link ExtendedVersion} with downstream `0`. */ satisfies(versionRange: VersionRange): boolean; } /** * Represents an extended version with an optional flavor, an upstream version, and a downstream version. * * The format is `#flavor:upstream:downstream` (e.g. `#bitcoin:1.2.3:0`) or `upstream:downstream` * for unflavored versions. Flavors allow multiple variants of a package to coexist. * * - **flavor**: An optional string identifier for the variant (e.g. `"bitcoin"`, `"litecoin"`) * - **upstream**: The version of the upstream software being packaged * - **downstream**: The version of the StartOS packaging itself * * Versions with different flavors are incomparable (comparison returns `null`). * * @example * ```ts * const v = ExtendedVersion.parse("#bitcoin:1.2.3:0") * console.log(v.flavor) // "bitcoin" * console.log(v.upstream) // Version { number: [1, 2, 3] } * console.log(v.downstream) // Version { number: [0] } * console.log(v.toString()) // "#bitcoin:1.2.3:0" * * const range = VersionRange.parse(">=1.0.0:0") * console.log(v.satisfies(range)) // true * ``` */ export declare class ExtendedVersion { /** The flavor identifier (e.g. `"bitcoin"`), or `null` for unflavored versions. */ flavor: string | null; /** The upstream software version. */ upstream: Version; /** The downstream packaging version. */ downstream: Version; constructor( /** The flavor identifier (e.g. `"bitcoin"`), or `null` for unflavored versions. */ flavor: string | null, /** The upstream software version. */ upstream: Version, /** The downstream packaging version. */ downstream: Version); /** Serializes this extended version to its string form (e.g. `"#bitcoin:1.2.3:0"` or `"1.0.0:1"`). */ toString(): string; /** * Compares this extended version against another. * * @returns `'greater'`, `'equal'`, `'less'`, or `null` if the flavors differ (incomparable) */ compare(other: ExtendedVersion): 'greater' | 'equal' | 'less' | null; /** * Lexicographic comparison — compares flavors alphabetically first, then versions. * Unlike {@link compare}, this never returns `null`: different flavors are ordered alphabetically. */ compareLexicographic(other: ExtendedVersion): 'greater' | 'equal' | 'less'; /** * Returns a numeric comparison result suitable for use with `Array.sort()`. * Uses lexicographic ordering (flavors sorted alphabetically, then by version). */ compareForSort(other: ExtendedVersion): 1 | 0 | -1; /** Returns `true` if this version is strictly greater than `other`. Returns `false` if flavors differ. */ greaterThan(other: ExtendedVersion): boolean; /** Returns `true` if this version is greater than or equal to `other`. Returns `false` if flavors differ. */ greaterThanOrEqual(other: ExtendedVersion): boolean; /** Returns `true` if this version equals `other` (same flavor, upstream, and downstream). */ equals(other: ExtendedVersion): boolean; /** Returns `true` if this version is strictly less than `other`. Returns `false` if flavors differ. */ lessThan(other: ExtendedVersion): boolean; /** Returns `true` if this version is less than or equal to `other`. Returns `false` if flavors differ. */ lessThanOrEqual(other: ExtendedVersion): boolean; /** * Parses an extended version string into an `ExtendedVersion`. * * @param extendedVersion - A string like `"1.2.3:0"` or `"#bitcoin:1.0.0:0"` * @throws If the string is not a valid extended version */ static parse(extendedVersion: string): ExtendedVersion; /** * Parses a legacy "emver" format extended version string. * * @param extendedVersion - A version string in the legacy emver format * @throws If the string is not a valid emver version (error message includes the input string) */ static parseEmver(extendedVersion: string): ExtendedVersion; /** * Returns an ExtendedVersion with the Upstream major version version incremented by 1 * and sets subsequent digits to zero. * If no non-zero upstream digit can be found the last upstream digit will be incremented. */ incrementMajor(): ExtendedVersion; /** * Returns an ExtendedVersion with the Upstream minor version version incremented by 1 * also sets subsequent digits to zero. * If no non-zero upstream digit can be found the last digit will be incremented. */ incrementMinor(): ExtendedVersion; /** * Returns a boolean indicating whether a given version satisfies the VersionRange * !( >= 1:1 <= 2:2) || <=#bitcoin:1.2.0-alpha:0 */ satisfies(versionRange: VersionRange): boolean; } /** * Compile-time type-checking helper that validates an extended version string literal. * If the string is invalid, TypeScript will report a type error at the call site. * * @example * ```ts * testTypeExVer("1.2.3:0") // compiles * testTypeExVer("#bitcoin:1.0:0") // compiles * testTypeExVer("invalid") // type error * ``` */ export declare const testTypeExVer: (t: T & ValidateExVer) => T & ValidateExVer; /** * Compile-time type-checking helper that validates a version string literal. * If the string is invalid, TypeScript will report a type error at the call site. * * @example * ```ts * testTypeVersion("1.2.3") // compiles * testTypeVersion("-3") // type error * ``` */ export declare const testTypeVersion: (t: T & ValidateVersion) => T & ValidateVersion; export {};