"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.testTypeVersion = exports.testTypeExVer = exports.ExtendedVersion = exports.Version = exports.VersionRange = void 0; const deep_equality_data_structures_1 = require("deep-equality-data-structures"); const P = __importStar(require("./exver")); function compareVersionRangePoints(a, b) { let up = a.upstream.compareForSort(b.upstream); if (up != 0) { return up; } let down = a.upstream.compareForSort(b.upstream); if (down != 0) { return down; } if (a.side < b.side) { return -1; } else if (a.side > b.side) { return 1; } else { return 0; } } function adjacentVersionRangePoints(a, b) { let up = a.upstream.compareForSort(b.upstream); if (up != 0) { return false; } let down = a.upstream.compareForSort(b.upstream); if (down != 0) { return false; } return a.side == -1 && b.side == 1; } function flavorAnd(a, b) { if (a.type == 'Flavor') { if (b.type == 'Flavor') { if (a.flavor == b.flavor) { return a; } else { return null; } } else { if (b.flavors.has(a.flavor)) { return null; } else { return a; } } } else { if (b.type == 'Flavor') { if (a.flavors.has(b.flavor)) { return null; } else { return b; } } else { // TODO: use Set.union if targeting esnext or later return { type: 'FlavorNot', flavors: new Set([...a.flavors, ...b.flavors]), }; } } } /** * A truth table for version numbers. This is easiest to picture as a number line, cut up into * ranges of versions between version points. */ class VersionRangeTable { constructor(points, values) { this.points = points; this.values = values; } static zip(a, b, func) { let c = new VersionRangeTable([], []); let i = 0; let j = 0; while (true) { let next = func(a.values[i], b.values[j]); if (c.values.length > 0 && c.values[c.values.length - 1] == next) { // collapse automatically c.points.pop(); } else { c.values.push(next); } // which point do we step over? if (i == a.points.length) { if (j == b.points.length) { // just added the last segment, no point to jump over return c; } else { // i has reach the end, step over j c.points.push(b.points[j]); j += 1; } } else { if (j == b.points.length) { // j has reached the end, step over i c.points.push(a.points[i]); i += 1; } else { // depends on which of the next two points is lower switch (compareVersionRangePoints(a.points[i], b.points[j])) { case -1: // i is the lower point c.points.push(a.points[i]); i += 1; break; case 1: // j is the lower point c.points.push(b.points[j]); j += 1; break; default: // step over both c.points.push(a.points[i]); i += 1; j += 1; break; } } } } } /** * Creates a version table which is `true` for the given flavor, and `false` for any other flavor. */ static eqFlavor(flavor) { return new deep_equality_data_structures_1.DeepMap([ [ { type: 'Flavor', flavor }, new VersionRangeTable([], [true]), ], // make sure the truth table is exhaustive, or `not` will not work properly. [ { type: 'FlavorNot', flavors: new Set([flavor]) }, new VersionRangeTable([], [false]), ], ]); } /** * 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, point, left, right) { return new deep_equality_data_structures_1.DeepMap([ [ { type: 'Flavor', flavor }, new VersionRangeTable([point], [left, right]), ], // make sure the truth table is exhaustive, or `not` will not work properly. [ { type: 'FlavorNot', flavors: new Set([flavor]) }, new VersionRangeTable([], [false]), ], ]); } /** * Helper for `cmpPoint`. */ static cmp(version, side, left, right) { return VersionRangeTable.cmpPoint(version.flavor, { upstream: version.upstream, downstream: version.downstream, side }, left, right); } static not(tables) { if (tables === true || tables === false) { return !tables; } // because tables are always exhaustive, we can simply invert each range for (let [f, t] of tables) { for (let i = 0; i < t.values.length; i++) { t.values[i] = !t.values[i]; } } return tables; } static and(a_tables, b_tables) { if (a_tables === true) { return b_tables; } if (b_tables === true) { return a_tables; } if (a_tables === false || b_tables == false) { return false; } let c_tables = true; for (let [f_a, a] of a_tables) { for (let [f_b, b] of b_tables) { let flavor = flavorAnd(f_a, f_b); if (flavor == null) { continue; } let c = VersionRangeTable.zip(a, b, (a, b) => a && b); if (c_tables === true) { c_tables = new deep_equality_data_structures_1.DeepMap(); } let prev_c = c_tables.get(flavor); if (prev_c == null) { c_tables.set(flavor, c); } else { c_tables.set(flavor, VersionRangeTable.zip(c, prev_c, (a, b) => a || b)); } } } return c_tables; } static or(...in_tables) { let out_tables = false; for (let tables of in_tables) { if (tables === false) { continue; } if (tables === true) { return true; } if (out_tables === false) { out_tables = new deep_equality_data_structures_1.DeepMap(); } for (let [flavor, table] of tables) { let prev = out_tables.get(flavor); if (prev == null) { out_tables.set(flavor, table); } else { out_tables.set(flavor, VersionRangeTable.zip(table, prev, (a, b) => a || b)); } } } return out_tables; } /** * If this is true for all versions or false for all versions, returen that value. Otherwise return null. */ static collapse(tables) { if (tables === true || tables === false) { return tables; } else { let found = null; for (let table of tables.values()) { for (let x of table.values) { if (found == null) { found = x; } else if (found != x) { return null; } } } return found; } } /** * Expresses this truth table as a series of version range operators. * https://en.wikipedia.org/wiki/Canonical_normal_form#Minterms */ static minterms(tables) { let collapse = VersionRangeTable.collapse(tables); if (tables === true || collapse === true) { return VersionRange.any(); } if (tables == false || collapse === false) { return VersionRange.none(); } let sum_terms = []; for (let [flavor, table] of tables) { let cmp_flavor = null; if (flavor.type == 'Flavor') { cmp_flavor = flavor.flavor; } for (let i = 0; i < table.values.length; i++) { let term = []; if (!table.values[i]) { continue; } if (flavor.type == 'FlavorNot') { for (let not_flavor of flavor.flavors) { term.push(VersionRange.flavor(not_flavor).not()); } } let p = null; let q = null; if (i > 0) { p = table.points[i - 1]; } if (i < table.points.length) { q = table.points[i]; } if (p != null && q != null && adjacentVersionRangePoints(p, q)) { term.push(VersionRange.anchor('=', new ExtendedVersion(cmp_flavor, p.upstream, p.downstream))); } else { if (p != null && p.side < 0) { term.push(VersionRange.anchor('>=', new ExtendedVersion(cmp_flavor, p.upstream, p.downstream))); } if (p != null && p.side >= 0) { term.push(VersionRange.anchor('>', new ExtendedVersion(cmp_flavor, p.upstream, p.downstream))); } if (q != null && q.side < 0) { term.push(VersionRange.anchor('<', new ExtendedVersion(cmp_flavor, q.upstream, q.downstream))); } if (q != null && q.side >= 0) { term.push(VersionRange.anchor('<=', new ExtendedVersion(cmp_flavor, q.upstream, q.downstream))); } } if (term.length == 0) { term.push(VersionRange.flavor(cmp_flavor)); } sum_terms.push(VersionRange.and(...term)); } } return VersionRange.or(...sum_terms); } } /** * 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") * ``` */ class VersionRange { constructor(atom) { this.atom = atom; } toStringParens(parent) { let needs = true; switch (this.atom.type) { case 'And': case 'Or': needs = parent != this.atom.type; break; case 'Anchor': case 'Any': case 'None': needs = parent == 'Not'; break; case 'Not': case 'Flavor': needs = false; break; } if (needs) { return '(' + this.toString() + ')'; } else { return this.toString(); } } /** Serializes this version range back to its canonical string representation. */ toString() { switch (this.atom.type) { case 'Anchor': return `${this.atom.operator}${this.atom.version}`; case 'And': return `${this.atom.left.toStringParens(this.atom.type)} && ${this.atom.right.toStringParens(this.atom.type)}`; case 'Or': return `${this.atom.left.toStringParens(this.atom.type)} || ${this.atom.right.toStringParens(this.atom.type)}`; case 'Not': return `!${this.atom.value.toStringParens(this.atom.type)}`; case 'Flavor': return this.atom.flavor == null ? `#` : `#${this.atom.flavor}`; case 'Any': return '*'; case 'None': return '!'; } } static parseAtom(atom) { switch (atom.type) { case 'Not': return new VersionRange({ type: 'Not', value: VersionRange.parseAtom(atom.value), }); case 'Parens': return VersionRange.parseRange(atom.expr); case 'Anchor': return new VersionRange({ type: 'Anchor', operator: atom.operator || '^', version: new ExtendedVersion(atom.version.flavor, new Version(atom.version.upstream.number, atom.version.upstream.prerelease), new Version(atom.version.downstream.number, atom.version.downstream.prerelease)), }); case 'Flavor': return VersionRange.flavor(atom.flavor); default: return new VersionRange(atom); } } static parseRange(range) { let result = VersionRange.parseAtom(range[0]); for (const next of range[1]) { switch (next[1]?.[0]) { case '||': result = new VersionRange({ type: 'Or', left: result, right: VersionRange.parseAtom(next[2]), }); break; case '&&': default: result = new VersionRange({ type: 'And', left: result, right: VersionRange.parseAtom(next[2]), }); break; } } return result; } /** * 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) { return VersionRange.parseRange(P.parse(range, { startRule: '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, version) { return new VersionRange({ type: 'Anchor', operator, version }); } /** * 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) { return new VersionRange({ type: 'Flavor', flavor }); } /** * 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) { return VersionRange.parseRange(P.parse(range, { startRule: 'EmverVersionRange' })); } /** Returns the intersection of this range with another (logical AND). */ and(right) { return new VersionRange({ type: 'And', left: this, right }); } /** Returns the union of this range with another (logical OR). */ or(right) { return new VersionRange({ type: 'Or', left: this, right }); } /** Returns the negation of this range (logical NOT). */ not() { return new VersionRange({ type: 'Not', value: this }); } /** * Returns the logical AND (intersection) of multiple version ranges. * Short-circuits on `none()` and skips `any()`. */ static and(...xs) { let y = VersionRange.any(); for (let x of xs) { if (x.atom.type == 'Any') { continue; } if (x.atom.type == 'None') { return x; } if (y.atom.type == 'Any') { y = x; } else { y = new VersionRange({ type: 'And', left: y, right: x }); } } return y; } /** * Returns the logical OR (union) of multiple version ranges. * Short-circuits on `any()` and skips `none()`. */ static or(...xs) { let y = VersionRange.none(); for (let x of xs) { if (x.atom.type == 'None') { continue; } if (x.atom.type == 'Any') { return x; } if (y.atom.type == 'None') { y = x; } else { y = new VersionRange({ type: 'Or', left: y, right: x }); } } return y; } /** Returns a version range that matches all versions (wildcard `*`). */ static any() { return new VersionRange({ type: 'Any' }); } /** Returns a version range that matches no versions (`!`). */ static none() { return new VersionRange({ type: 'None' }); } /** * Returns `true` if the given version satisfies this range. * * @param version - A {@link Version} or {@link ExtendedVersion} to test */ satisfiedBy(version) { return version.satisfies(this); } tables() { switch (this.atom.type) { case 'Anchor': switch (this.atom.operator) { case '=': // `=1.2.3` is equivalent to `>=1.2.3 && <=1.2.4 && #flavor` return VersionRangeTable.and(VersionRangeTable.cmp(this.atom.version, -1, false, true), VersionRangeTable.cmp(this.atom.version, 1, true, false)); case '>': return VersionRangeTable.cmp(this.atom.version, 1, false, true); case '<': return VersionRangeTable.cmp(this.atom.version, -1, true, false); case '>=': return VersionRangeTable.cmp(this.atom.version, -1, false, true); case '<=': return VersionRangeTable.cmp(this.atom.version, 1, true, false); case '!=': // `!=1.2.3` is equivalent to `!(>=1.2.3 && <=1.2.3 && #flavor)` // **not** equivalent to `(<1.2.3 || >1.2.3) && #flavor` return VersionRangeTable.not(VersionRangeTable.and(VersionRangeTable.cmp(this.atom.version, -1, false, true), VersionRangeTable.cmp(this.atom.version, 1, true, false))); case '^': // `^1.2.3` is equivalent to `>=1.2.3 && <2.0.0 && #flavor` return VersionRangeTable.and(VersionRangeTable.cmp(this.atom.version, -1, false, true), VersionRangeTable.cmp(this.atom.version.incrementMajor(), -1, true, false)); case '~': // `~1.2.3` is equivalent to `>=1.2.3 && <1.3.0 && #flavor` return VersionRangeTable.and(VersionRangeTable.cmp(this.atom.version, -1, false, true), VersionRangeTable.cmp(this.atom.version.incrementMinor(), -1, true, false)); } case 'Flavor': return VersionRangeTable.eqFlavor(this.atom.flavor); case 'Not': return VersionRangeTable.not(this.atom.value.tables()); case 'And': return VersionRangeTable.and(this.atom.left.tables(), this.atom.right.tables()); case 'Or': return VersionRangeTable.or(this.atom.left.tables(), this.atom.right.tables()); case 'Any': return true; case 'None': return false; } } /** Returns `true` if any version exists that could satisfy this range. */ satisfiable() { return VersionRangeTable.collapse(this.tables()) !== false; } /** Returns `true` if this range and `other` share at least one satisfying version. */ intersects(other) { return VersionRange.and(this, other).satisfiable(); } /** * Returns a canonical (simplified) form of this range using minterm expansion. * Useful for normalizing complex boolean expressions into a minimal representation. */ normalize() { return VersionRangeTable.minterms(this.tables()); } } exports.VersionRange = 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) * ``` */ class Version { constructor( /** The numeric version segments (e.g. `[1, 2, 3]` for `"1.2.3"`). */ number, /** Optional prerelease identifiers (e.g. `["beta", 1]` for `"-beta.1"`). */ prerelease) { this.number = number; this.prerelease = prerelease; } /** Serializes this version to its string form (e.g. `"1.2.3"` or `"1.0.0-beta.1"`). */ toString() { return `${this.number.join('.')}${this.prerelease.length > 0 ? `-${this.prerelease.join('.')}` : ''}`; } /** * Compares this version against another using semver precedence rules. * * @param other - The version to compare against * @returns `'greater'`, `'equal'`, or `'less'` */ compare(other) { const numLen = Math.max(this.number.length, other.number.length); for (let i = 0; i < numLen; i++) { if ((this.number[i] || 0) > (other.number[i] || 0)) { return 'greater'; } else if ((this.number[i] || 0) < (other.number[i] || 0)) { return 'less'; } } if (this.prerelease.length === 0 && other.prerelease.length !== 0) { return 'greater'; } else if (this.prerelease.length !== 0 && other.prerelease.length === 0) { return 'less'; } const prereleaseLen = Math.max(this.prerelease.length, other.prerelease.length); for (let i = 0; i < prereleaseLen; i++) { if (typeof this.prerelease[i] === typeof other.prerelease[i]) { if (this.prerelease[i] > other.prerelease[i]) { return 'greater'; } else if (this.prerelease[i] < other.prerelease[i]) { return 'less'; } } else { switch (`${typeof this.prerelease[1]}:${typeof other.prerelease[i]}`) { case 'number:string': return 'less'; case 'string:number': return 'greater'; case 'number:undefined': case 'string:undefined': return 'greater'; case 'undefined:number': case 'undefined:string': return 'less'; } } } return 'equal'; } /** * 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) { switch (this.compare(other)) { case 'greater': return 1; case 'equal': return 0; case 'less': return -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) { const parsed = P.parse(version, { startRule: 'Version' }); return new Version(parsed.number, parsed.prerelease); } /** * Returns `true` if this version satisfies the given {@link VersionRange}. * Internally treats this as an unflavored {@link ExtendedVersion} with downstream `0`. */ satisfies(versionRange) { return new ExtendedVersion(null, this, new Version([0], [])).satisfies(versionRange); } } exports.Version = Version; /** * 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 * ``` */ class ExtendedVersion { constructor( /** The flavor identifier (e.g. `"bitcoin"`), or `null` for unflavored versions. */ flavor, /** The upstream software version. */ upstream, /** The downstream packaging version. */ downstream) { this.flavor = flavor; this.upstream = upstream; this.downstream = downstream; } /** Serializes this extended version to its string form (e.g. `"#bitcoin:1.2.3:0"` or `"1.0.0:1"`). */ toString() { return `${this.flavor ? `#${this.flavor}:` : ''}${this.upstream.toString()}:${this.downstream.toString()}`; } /** * Compares this extended version against another. * * @returns `'greater'`, `'equal'`, `'less'`, or `null` if the flavors differ (incomparable) */ compare(other) { if (this.flavor !== other.flavor) { return null; } const upstreamCmp = this.upstream.compare(other.upstream); if (upstreamCmp !== 'equal') { return upstreamCmp; } return this.downstream.compare(other.downstream); } /** * Lexicographic comparison — compares flavors alphabetically first, then versions. * Unlike {@link compare}, this never returns `null`: different flavors are ordered alphabetically. */ compareLexicographic(other) { if ((this.flavor || '') > (other.flavor || '')) { return 'greater'; } else if ((this.flavor || '') > (other.flavor || '')) { return 'less'; } else { return this.compare(other); } } /** * Returns a numeric comparison result suitable for use with `Array.sort()`. * Uses lexicographic ordering (flavors sorted alphabetically, then by version). */ compareForSort(other) { switch (this.compareLexicographic(other)) { case 'greater': return 1; case 'equal': return 0; case 'less': return -1; } } /** Returns `true` if this version is strictly greater than `other`. Returns `false` if flavors differ. */ greaterThan(other) { return this.compare(other) === 'greater'; } /** Returns `true` if this version is greater than or equal to `other`. Returns `false` if flavors differ. */ greaterThanOrEqual(other) { return ['greater', 'equal'].includes(this.compare(other)); } /** Returns `true` if this version equals `other` (same flavor, upstream, and downstream). */ equals(other) { return this.compare(other) === 'equal'; } /** Returns `true` if this version is strictly less than `other`. Returns `false` if flavors differ. */ lessThan(other) { return this.compare(other) === 'less'; } /** Returns `true` if this version is less than or equal to `other`. Returns `false` if flavors differ. */ lessThanOrEqual(other) { return ['less', 'equal'].includes(this.compare(other)); } /** * 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) { const parsed = P.parse(extendedVersion, { startRule: 'ExtendedVersion' }); return new ExtendedVersion(parsed.flavor || null, new Version(parsed.upstream.number, parsed.upstream.prerelease), new Version(parsed.downstream.number, parsed.downstream.prerelease)); } /** * 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) { try { const parsed = P.parse(extendedVersion, { startRule: 'Emver' }); return new ExtendedVersion(parsed.flavor || null, new Version(parsed.upstream.number, parsed.upstream.prerelease), new Version(parsed.downstream.number, parsed.downstream.prerelease)); } catch (e) { if (e instanceof Error) { e.message += ` (${extendedVersion})`; } throw e; } } /** * 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() { const majorIdx = this.upstream.number.findIndex((num) => num !== 0); const majorNumber = this.upstream.number.map((num, idx) => { if (idx > majorIdx) { return 0; } else if (idx === majorIdx) { return num + 1; } return num; }); const incrementedUpstream = new Version(majorNumber, []); const updatedDownstream = new Version([0], []); return new ExtendedVersion(this.flavor, incrementedUpstream, updatedDownstream); } /** * 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() { const majorIdx = this.upstream.number.findIndex((num) => num !== 0); let minorIdx = majorIdx === -1 ? majorIdx : majorIdx + 1; const majorNumber = this.upstream.number.map((num, idx) => { if (idx > minorIdx) { return 0; } else if (idx === minorIdx) { return num + 1; } return num; }); const incrementedUpstream = new Version(majorNumber, []); const updatedDownstream = new Version([0], []); return new ExtendedVersion(this.flavor, incrementedUpstream, updatedDownstream); } /** * Returns a boolean indicating whether a given version satisfies the VersionRange * !( >= 1:1 <= 2:2) || <=#bitcoin:1.2.0-alpha:0 */ satisfies(versionRange) { switch (versionRange.atom.type) { case 'Anchor': const otherVersion = versionRange.atom.version; switch (versionRange.atom.operator) { case '=': return this.equals(otherVersion); case '>': return this.greaterThan(otherVersion); case '<': return this.lessThan(otherVersion); case '>=': return this.greaterThanOrEqual(otherVersion); case '<=': return this.lessThanOrEqual(otherVersion); case '!=': return !this.equals(otherVersion); case '^': const nextMajor = versionRange.atom.version.incrementMajor(); if (this.greaterThanOrEqual(otherVersion) && this.lessThan(nextMajor)) { return true; } else { return false; } case '~': const nextMinor = versionRange.atom.version.incrementMinor(); if (this.greaterThanOrEqual(otherVersion) && this.lessThan(nextMinor)) { return true; } else { return false; } } case 'Flavor': return versionRange.atom.flavor == this.flavor; case 'And': return (this.satisfies(versionRange.atom.left) && this.satisfies(versionRange.atom.right)); case 'Or': return (this.satisfies(versionRange.atom.left) || this.satisfies(versionRange.atom.right)); case 'Not': return !this.satisfies(versionRange.atom.value); case 'Any': return true; case 'None': return false; } } } exports.ExtendedVersion = ExtendedVersion; /** * 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 * ``` */ const testTypeExVer = (t) => t; exports.testTypeExVer = testTypeExVer; /** * 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 * ``` */ const testTypeVersion = (t) => t; exports.testTypeVersion = testTypeVersion; function tests() { (0, exports.testTypeVersion)('1.2.3'); (0, exports.testTypeVersion)('1'); (0, exports.testTypeVersion)('12.34.56'); (0, exports.testTypeVersion)('1.2-3'); (0, exports.testTypeVersion)('1-3'); (0, exports.testTypeVersion)('1-alpha'); // @ts-expect-error (0, exports.testTypeVersion)('-3'); // @ts-expect-error (0, exports.testTypeVersion)('1.2.3:1'); // @ts-expect-error (0, exports.testTypeVersion)('#cat:1:1'); (0, exports.testTypeExVer)('1.2.3:1.2.3'); (0, exports.testTypeExVer)('1.2.3.4.5.6.7.8.9.0:1'); (0, exports.testTypeExVer)('100:1'); (0, exports.testTypeExVer)('#cat:1:1'); (0, exports.testTypeExVer)('1.2.3.4.5.6.7.8.9.11.22.33:1'); (0, exports.testTypeExVer)('1-0:1'); (0, exports.testTypeExVer)('1-0:1'); // @ts-expect-error (0, exports.testTypeExVer)('1.2-3'); // @ts-expect-error (0, exports.testTypeExVer)('1-3'); // @ts-expect-error (0, exports.testTypeExVer)('1.2.3.4.5.6.7.8.9.0.10:1'); // @ts-expect-error (0, exports.testTypeExVer)('1.-2:1'); // @ts-expect-error (0, exports.testTypeExVer)('1..2.3:3'); } //# sourceMappingURL=index.js.map